check for NULL
[oweals/gnunet.git] / src / arm / gnunet-service-arm.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file arm/gnunet-service-arm.c
23  * @brief the automated restart manager service
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - multiple start-stop requests with RC>1 can result
28  *   in UP/DOWN signals based on "pending" that are inaccurate...
29  *   => have list of clients waiting for a resolution instead of
30  *      giving instant (but incorrect) replies
31  * - code could go into restart-loop for a service
32  *   if service crashes instantly -- need exponential back-off
33  * - need to test auto-restart code on configuration changes;
34  * - should refine restart code to check if *relevant* parts of the
35  *   configuration were changed (anything in the section for the service)
36  * - should have a way to specify dependencies between services and
37  *   manage restarts of groups of services
38  */
39 #include "platform.h"
40 #include "gnunet_client_lib.h"
41 #include "gnunet_getopt_lib.h"
42 #include "gnunet_os_lib.h"
43 #include "gnunet_protocols.h"
44 #include "gnunet_service_lib.h"
45 #include "arm.h"
46
47
48 /**
49  * Run normal maintenance every 2s.
50  */
51 #define MAINT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
52
53 /**
54  * Run fast maintenance after 100ms.  This is used for an extra-job
55  * that is run to check for a process that we just killed.
56  */
57 #define MAINT_FAST_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100)
58
59 /**
60  * How long do we wait until we decide that a service
61  * did not start?
62  */
63 #define CHECK_TIMEOUT GNUNET_TIME_UNIT_MINUTES
64
65 /**
66  * List of our services.
67  */
68 struct ServiceList;
69
70 /**
71  * Function to call if waitpid informs us that
72  * a process has died.
73  *
74  * @param cls closure
75  * @param pos entry in the service list of the process that died
76  */
77 typedef void (*CleanCallback) (void *cls, struct ServiceList * pos);
78
79 /**
80  * List of our services.
81  */
82 struct ServiceList
83 {
84   /**
85    * This is a linked list.
86    */
87   struct ServiceList *next;
88
89   /**
90    * Name of the service.
91    */
92   char *name;
93
94   /**
95    * Name of the binary used.
96    */
97   char *binary;
98
99   /**
100    * Name of the configuration file used.
101    */
102   char *config;
103
104   /**
105    * Function to call upon kill completion (waitpid), NULL
106    * if we should simply restart the process.
107    */
108   CleanCallback kill_continuation;
109
110   /**
111    * Closure for kill_continuation.
112    */
113   void *kill_continuation_cls;
114
115   /**
116    * Process ID of the child.
117    */
118   pid_t pid;
119
120   /**
121    * Last time the config of this service was
122    * modified.
123    */
124   time_t mtime;
125
126   /**
127    * Reference counter (counts how many times we've been
128    * asked to start the service).  We only actually stop
129    * it once rc hits zero.
130    */
131   unsigned int rc;
132
133 };
134
135 /**
136  * List of running services.
137  */
138 static struct ServiceList *running;
139
140 /**
141  * Our configuration
142  */
143 static const struct GNUNET_CONFIGURATION_Handle *cfg;
144
145 /**
146  * Our scheduler.
147  */
148 static struct GNUNET_SCHEDULER_Handle *sched;
149
150 /**
151  * Command to prepend to each actual command.
152  */
153 static char *prefix_command;
154
155
156 /**
157  * Background task doing maintenance.
158  *
159  * @param cls closure, NULL if we need to self-restart
160  * @param tc context
161  */
162 static void
163 maint (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
164
165
166 /**
167  * Transmit a status result message.
168  *
169  * @param cls pointer to "unit16_t*" with message type
170  * @param size number of bytes available in buf
171  * @param buf where to copy the message, NULL on error
172  * @return number of bytes copied to buf
173  */
174 static size_t
175 write_result (void *cls, size_t size, void *buf)
176 {
177   uint16_t *res = cls;
178   struct GNUNET_MessageHeader *msg;
179
180   if (buf == NULL)
181     return 0;                   /* error, not much we can do */
182   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
183   msg = buf;
184   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
185   msg->type = htons (*res);
186   GNUNET_free (res);
187   return sizeof (struct GNUNET_MessageHeader);
188 }
189
190
191
192 /**
193  * Signal our client that we will start or stop the
194  * service.
195  *
196  * @param client who is being signalled
197  * @param name name of the service
198  * @param result message type to send
199  * @return NULL if it was not found
200  */
201 static void
202 signal_result (struct GNUNET_SERVER_Client *client,
203                const char *name, uint16_t result)
204 {
205   uint16_t *res;
206
207   if (NULL == client)
208     return;
209 #if DEBUG_ARM
210   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211               "Telling client that service `%s' is now %s\n",
212               name,
213               result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up");
214 #endif
215   res = GNUNET_malloc (sizeof (uint16_t));
216   *res = result;
217   GNUNET_SERVER_notify_transmit_ready (client,
218                                        sizeof (struct GNUNET_MessageHeader),
219                                        GNUNET_TIME_UNIT_FOREVER_REL,
220                                        &write_result, res);
221 }
222
223
224 /**
225  * Find the process with the given service
226  * name in the given list, remove it and return it.
227  *
228  * @param name which service entry to look up
229  * @return NULL if it was not found
230  */
231 static struct ServiceList *
232 find_name (const char *name)
233 {
234   struct ServiceList *pos;
235   struct ServiceList *prev;
236
237   pos = running;
238   prev = NULL;
239   while (pos != NULL)
240     {
241       if (0 == strcmp (pos->name, name))
242         {
243           if (prev == NULL)
244             running = pos->next;
245           else
246             prev->next = pos->next;
247           pos->next = NULL;
248           return pos;
249         }
250       prev = pos;
251       pos = pos->next;
252     }
253   return NULL;
254 }
255
256
257 /**
258  * Free an entry in the service list.
259  *
260  * @param pos entry to free
261  */
262 static void
263 free_entry (struct ServiceList *pos)
264 {
265   GNUNET_free_non_null (pos->config);
266   GNUNET_free_non_null (pos->binary);
267   GNUNET_free (pos->name);
268   GNUNET_free (pos);
269 }
270
271
272 /**
273  * Actually start the process for the given service.
274  *
275  * @param sl identifies service to start
276  */
277 static void
278 start_process (struct ServiceList *sl)
279 {
280   char *loprefix;
281   char *options;
282   char **argv;
283   unsigned int argv_size;
284   char *lopos;
285   char *optpos;
286   const char *firstarg;
287   int use_debug;
288
289   /* start service */
290   if (GNUNET_OK !=
291       GNUNET_CONFIGURATION_get_value_string (cfg,
292                                              sl->name, "PREFIX", &loprefix))
293     loprefix = GNUNET_strdup (prefix_command);
294   if (GNUNET_OK !=
295       GNUNET_CONFIGURATION_get_value_string (cfg,
296                                              sl->name, "OPTIONS", &options))
297     options = GNUNET_strdup ("");
298   use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
299
300   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name);
301 #if DEBUG_ARM
302   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303               "Starting service `%s' using binary `%s' and configuration `%s'\n",
304               sl->name, sl->binary, sl->config);
305 #endif
306   argv_size = 6;
307   if (use_debug)
308     argv_size += 2;
309   lopos = loprefix;
310   while ('\0' != *lopos)
311     {
312       if (*lopos == ' ')
313         argv_size++;
314       lopos++;
315     }
316   optpos = options;
317   while ('\0' != *optpos)
318     {
319       if (*optpos == ' ')
320         argv_size++;
321       optpos++;
322     }
323   firstarg = NULL;
324   argv = GNUNET_malloc (argv_size * sizeof (char *));
325   argv_size = 0;
326   lopos = loprefix;
327
328   while ('\0' != *lopos)
329     {
330       while (*lopos == ' ')
331         lopos++;
332       if (*lopos == '\0')
333         continue;
334       if (argv_size == 0)
335         firstarg = lopos;
336       argv[argv_size++] = lopos;
337       while (('\0' != *lopos) && (' ' != *lopos))
338         lopos++;
339       if ('\0' == *lopos)
340         continue;
341       *lopos = '\0';
342       lopos++;
343     }
344   if (argv_size == 0)
345     firstarg = sl->binary;
346   argv[argv_size++] = sl->binary;
347   argv[argv_size++] = "-c";
348   argv[argv_size++] = sl->config;
349   if (GNUNET_YES == use_debug)
350     {
351       argv[argv_size++] = "-L";
352       argv[argv_size++] = "DEBUG";
353     }
354   optpos = options;
355   while ('\0' != *optpos)
356     {
357       while (*optpos == ' ')
358         optpos++;
359       if (*optpos == '\0')
360         continue;
361       argv[argv_size++] = optpos;
362       while (('\0' != *optpos) && (' ' != *optpos))
363         optpos++;
364       if ('\0' == *optpos)
365         continue;
366       *optpos = '\0';
367       optpos++;
368     }
369   argv[argv_size++] = NULL;
370   sl->pid = GNUNET_OS_start_process_v (firstarg, argv);
371   GNUNET_free (argv);
372   GNUNET_free (loprefix);
373   GNUNET_free (options);
374 }
375
376
377 /**
378  * Start the specified service.
379  *
380  * @param client who is asking for this
381  * @param servicename name of the service to start
382  */
383 static void
384 start_service (struct GNUNET_SERVER_Client *client, const char *servicename)
385 {
386   struct ServiceList *sl;
387   char *binary;
388   char *config;
389   struct stat sbuf;
390   sl = find_name (servicename);
391   if (sl != NULL)
392     {
393       /* already running, just increment RC */
394       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
395                   _("Service `%s' already running.\n"), servicename);
396       sl->rc++;
397       sl->next = running;
398       running = sl;
399       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
400       return;
401     }
402   if (GNUNET_OK !=
403       GNUNET_CONFIGURATION_get_value_string (cfg,
404                                              servicename, "BINARY", &binary))
405     {
406       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
407                   _("Binary implementing service `%s' not known!\n"),
408                   servicename);
409       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
410       return;
411     }
412   if ((GNUNET_OK !=
413        GNUNET_CONFIGURATION_get_value_filename (cfg,
414                                                 servicename,
415                                                 "CONFIG",
416                                                 &config)) ||
417       (0 != STAT (config, &sbuf)))
418     {
419       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
420                   _("Configuration file `%s' for service `%s' not known!\n"),
421                   config, servicename);
422       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
423       GNUNET_free (binary);
424       GNUNET_free_non_null (config);
425       return;
426     }
427   sl = GNUNET_malloc (sizeof (struct ServiceList));
428   sl->name = GNUNET_strdup (servicename);
429   sl->next = running;
430   sl->rc = 1;
431   sl->binary = binary;
432   sl->config = config;
433   sl->mtime = sbuf.st_mtime;
434   running = sl;
435   start_process (sl);
436   if (NULL != client)
437     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
438 }
439
440
441 /**
442  * Free the given entry in the service list and signal
443  * the given client that the service is now down.
444  *
445  * @param cls pointer to the client ("struct GNUNET_SERVER_Client*")
446  * @param pos entry for the service
447  */
448 static void
449 free_and_signal (void *cls, struct ServiceList *pos)
450 {
451   struct GNUNET_SERVER_Client *client = cls;
452   /* find_name will remove "pos" from the list! */
453   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Service `%s' stopped\n", pos->name);
454   signal_result (client, pos->name, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
455   GNUNET_SERVER_receive_done (client, GNUNET_OK);
456   GNUNET_SERVER_client_drop (client);
457   free_entry (pos);
458 }
459
460
461 /**
462  * Stop the specified service.
463  *
464  * @param client who is asking for this
465  * @param servicename name of the service to stop
466  */
467 static void
468 stop_service (struct GNUNET_SERVER_Client *client,
469               const char *servicename)
470 {
471   struct ServiceList *pos;
472   struct GNUNET_CLIENT_Connection *sc;
473   unsigned long long port;
474
475   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
476               _("Preparing to stop `%s'\n"), servicename);
477   pos = find_name (servicename);
478   if ((pos != NULL) && (pos->kill_continuation != NULL))
479     {
480       /* killing already in progress */
481 #if DEBUG_ARM
482       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483                   "Service `%s' is already down\n", servicename);
484 #endif
485       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
486       return;
487     }
488   if ((pos != NULL) && (pos->rc > 1))
489     {
490       /* RC>1, just decrement RC */
491       pos->rc--;
492       pos->next = running;
493       running = pos;
494 #if DEBUG_ARM
495       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
496                   "Service `%s' still used by %u clients, will keep it running!\n",
497                   servicename,
498                   pos->rc);
499 #endif
500       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
501       GNUNET_SERVER_receive_done (client, GNUNET_OK);
502       return;
503     }
504   if (pos != NULL)
505     {
506 #if DEBUG_ARM
507       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
508                   "Sending kill signal to service `%s', waiting for process to die.\n",
509                   servicename);
510 #endif
511       if (0 != PLIBC_KILL (pos->pid, SIGTERM))
512         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
513       pos->next = running;
514       running = pos;
515       pos->kill_continuation = &free_and_signal;
516       pos->kill_continuation_cls = client;
517       GNUNET_SERVER_client_keep (client);
518       GNUNET_SCHEDULER_add_delayed (sched,
519                                     GNUNET_YES,
520                                     GNUNET_SCHEDULER_PRIORITY_IDLE,
521                                     GNUNET_SCHEDULER_NO_TASK,
522                                     MAINT_FAST_FREQUENCY, &maint, NULL);
523     }
524   else
525     {
526 #if DEBUG_ARM
527       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
528                   "Sending termination request to service `%s'.\n",
529                   servicename);
530 #endif
531       if ( (GNUNET_OK ==
532             GNUNET_CONFIGURATION_get_value_number (cfg,
533                                                    servicename,
534                                                    "PORT",
535                                                    &port)) &&
536            (NULL != (sc = GNUNET_CLIENT_connect (sched, servicename, cfg))) )
537         {
538           GNUNET_CLIENT_service_shutdown (sc);
539           signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
540         }
541       else
542         {
543           signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN);
544         }
545       GNUNET_SERVER_receive_done (client, GNUNET_OK);
546     }
547 }
548
549
550 /**
551  * Handle START-message.
552  *
553  * @param cls closure (always NULL)
554  * @param client identification of the client
555  * @param message the actual message
556  * @return GNUNET_OK to keep the connection open,
557  *         GNUNET_SYSERR to close it (signal serious error)
558  */
559 static void
560 handle_start (void *cls,
561               struct GNUNET_SERVER_Client *client,
562               const struct GNUNET_MessageHeader *message)
563 {
564   const char *servicename;
565   uint16_t size;
566
567   size = ntohs (message->size);
568   size -= sizeof (struct GNUNET_MessageHeader);
569   servicename = (const char *) &message[1];
570   if ((size == 0) || (servicename[size - 1] != '\0'))
571     {
572       GNUNET_break (0);
573       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
574       return;
575     }
576   start_service (client, servicename);
577   GNUNET_SERVER_receive_done (client, GNUNET_OK);
578 }
579
580
581 /**
582  * Handle STOP-message.
583  *
584  * @param cls closure (always NULL)
585  * @param client identification of the client
586  * @param message the actual message
587  * @return GNUNET_OK to keep the connection open,
588  *         GNUNET_SYSERR to close it (signal serious error)
589  */
590 static void
591 handle_stop (void *cls,
592              struct GNUNET_SERVER_Client *client,
593              const struct GNUNET_MessageHeader *message)
594 {
595   const char *servicename;
596   uint16_t size;
597
598   size = ntohs (message->size);
599   size -= sizeof (struct GNUNET_MessageHeader);
600   servicename = (const char *) &message[1];
601   if ((size == 0) || (servicename[size - 1] != '\0'))
602     {
603       GNUNET_break (0);
604       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
605       return;
606     }
607   stop_service (client, servicename);
608 }
609
610
611 /**
612  * Background task doing maintenance.
613  *
614  * @param cls closure, NULL if we need to self-restart
615  * @param tc context
616  */
617 static void
618 maint (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
619 {
620   struct ServiceList *pos;
621   struct ServiceList *prev;
622   struct ServiceList *next;
623   const char *statstr;
624   int statcode;
625   struct stat sbuf;
626   int ret;
627
628   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
629     {
630       GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopping all services\n"));
631       while (NULL != (pos = running))
632         {
633           running = pos->next;
634           if (0 != PLIBC_KILL (pos->pid, SIGTERM))
635             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
636           if (GNUNET_OK != GNUNET_OS_process_wait(pos->pid))
637             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
638           free_entry (pos);
639         }
640       return;
641     }
642   if (cls == NULL)
643     GNUNET_SCHEDULER_add_delayed (tc->sched,
644                                   GNUNET_YES,
645                                   GNUNET_SCHEDULER_PRIORITY_IDLE,
646                                   GNUNET_SCHEDULER_NO_TASK,
647                                   MAINT_FREQUENCY, &maint, NULL);
648
649   /* check for services that died (WAITPID) */
650   prev = NULL;
651   next = running;
652   while (NULL != (pos = next))
653     {
654       enum GNUNET_OS_ProcessStatusType statusType;
655       unsigned long statusCode;
656      
657       next = pos->next;
658       if (pos->pid == 0)
659         {
660           if (NULL != pos->kill_continuation)
661             {
662               if (prev == NULL)
663                 running = next;
664               else
665                 prev->next = next;
666               pos->kill_continuation (pos->kill_continuation_cls, pos);     
667             }
668           continue;
669         }
670       if ( (GNUNET_SYSERR == (ret = GNUNET_OS_process_status(pos->pid, 
671                                                              &statusType,
672                                                              &statusCode))) ||
673            ( (ret == GNUNET_NO) ||
674              (statusType == GNUNET_OS_PROCESS_STOPPED) || 
675              (statusType == GNUNET_OS_PROCESS_RUNNING) ) )
676         {
677           prev = pos;
678           continue;
679         }
680       if (statusType == GNUNET_OS_PROCESS_EXITED)
681         {
682           statstr = _( /* process termination method */ "exit");
683           statcode = statusCode;
684         }
685       else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
686         {
687           statstr = _( /* process termination method */ "signal");
688           statcode = statusCode;
689         }
690       else
691         {
692           statstr = _( /* process termination method */ "unknown");
693           statcode = 0;
694         }    
695       if (NULL != pos->kill_continuation)
696         {
697           if (prev == NULL)
698             running = next;
699           else
700             prev->next = next;
701           pos->kill_continuation (pos->kill_continuation_cls, pos);
702           continue;
703         }
704       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
705                   _("Service `%s' terminated with status %s/%d, will try to restart it!\n"),
706                   pos->name, statstr, statcode);
707       /* schedule restart */
708       pos->pid = 0;
709       prev = pos;
710     }
711
712   /* check for services that need to be restarted due to
713      configuration changes or because the last restart failed */
714   pos = running;
715   while (pos != NULL)
716     {
717       if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime))
718         {
719           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
720                       _("Restarting service `%s' due to configuration file change.\n"));
721           if (0 != PLIBC_KILL (pos->pid, SIGTERM))
722             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
723         }
724       if (pos->pid == 0)
725         {
726           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
727                       _("Restarting service `%s'.\n"), pos->name);
728           /* FIXME: should have some exponentially
729              increasing timer to avoid tight restart loops */
730           start_process (pos);
731         }
732       pos = pos->next;
733     }
734 }
735
736
737 /**
738  * List of handlers for the messages understood by this service.
739  */
740 static struct GNUNET_SERVER_MessageHandler handlers[] = {
741   {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
742   {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
743   {NULL, NULL, 0, 0}
744 };
745
746
747 /**
748  * Process arm requests.
749  *
750  * @param cls closure
751  * @param s scheduler to use
752  * @param server the initialized server
753  * @param c configuration to use
754  */
755 static void
756 run (void *cls,
757      struct GNUNET_SCHEDULER_Handle *s,
758      struct GNUNET_SERVER_Handle *server,
759      const struct GNUNET_CONFIGURATION_Handle *c)
760 {
761   char *defaultservices;
762   char *pos;
763
764   cfg = c;
765   sched = s;
766   if (GNUNET_OK !=
767       GNUNET_CONFIGURATION_get_value_string (cfg,
768                                              "ARM",
769                                              "GLOBAL_PREFIX",
770                                              &prefix_command))
771     prefix_command = GNUNET_strdup ("");
772   /* start default services... */
773   if (GNUNET_OK ==
774       GNUNET_CONFIGURATION_get_value_string (cfg,
775                                              "ARM",
776                                              "DEFAULTSERVICES",
777                                              &defaultservices))
778     {
779 #if DEBUG_ARM
780       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
781                   "Starting default services `%s'\n", defaultservices);
782 #endif
783       pos = strtok (defaultservices, " ");
784       while (pos != NULL)
785         {
786           start_service (NULL, pos);
787           pos = strtok (NULL, " ");
788         }
789       GNUNET_free (defaultservices);
790     }
791   else
792     {
793 #if DEBUG_ARM
794       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795                   "No default services configured.\n");
796 #endif
797     }
798
799   /* process client requests */
800   GNUNET_SERVER_add_handlers (server, handlers);
801
802   /* manage services */
803   GNUNET_SCHEDULER_add_delayed (sched,
804                                 GNUNET_YES,
805                                 GNUNET_SCHEDULER_PRIORITY_IDLE,
806                                 GNUNET_SCHEDULER_NO_TASK,
807                                 MAINT_FREQUENCY, &maint, NULL);
808 }
809
810
811 /**
812  * The main function for the arm service.
813  *
814  * @param argc number of arguments from the command line
815  * @param argv command line arguments
816  * @return 0 ok, 1 on error
817  */
818 int
819 main (int argc, char *const *argv)
820 {
821   return (GNUNET_OK ==
822           GNUNET_SERVICE_run (argc,
823                               argv, "arm", &run, NULL, NULL, NULL)) ? 0 : 1;
824 }
825
826 /* end of gnunet-service-arm.c */