cleaner handling of clients and service processes, ignore shutdown
[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  * Are we in shutdown mode?
157  */
158 static int in_shutdown;
159
160 /**
161  * Handle to our server instance.  Our server is a bit special in that
162  * its service is not immediately stopped once we get a shutdown
163  * request (since we need to continue service until all of our child
164  * processes are dead).  This handle is used to shut down the server
165  * (and thus trigger process termination) once all child processes are
166  * also dead.  A special option in the ARM configuration modifies the
167  * behaviour of the service implementation to not do the shutdown
168  * immediately.
169  */
170 static struct GNUNET_SERVER_Handle *server;
171
172 /**
173  * Background task doing maintenance.
174  *
175  * @param cls closure, NULL if we need to self-restart
176  * @param tc context
177  */
178 static void
179 maint (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
180
181
182 /**
183  * Transmit a status result message.
184  *
185  * @param cls pointer to "unit16_t*" with message type
186  * @param size number of bytes available in buf
187  * @param buf where to copy the message, NULL on error
188  * @return number of bytes copied to buf
189  */
190 static size_t
191 write_result (void *cls, size_t size, void *buf)
192 {
193   uint16_t *res = cls;
194   struct GNUNET_MessageHeader *msg;
195
196   if (buf == NULL)
197     {
198       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
199                   _("Could not send status result to client\n"));
200       return 0;                   /* error, not much we can do */
201     }
202   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
203   msg = buf;
204   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
205   msg->type = htons (*res);
206   GNUNET_free (res);
207   return sizeof (struct GNUNET_MessageHeader);
208 }
209
210
211
212 /**
213  * Signal our client that we will start or stop the
214  * service.
215  *
216  * @param client who is being signalled
217  * @param name name of the service
218  * @param result message type to send
219  * @return NULL if it was not found
220  */
221 static void
222 signal_result (struct GNUNET_SERVER_Client *client,
223                const char *name, uint16_t result)
224 {
225   uint16_t *res;
226
227   if (NULL == client)
228     {
229       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
230                   _("Not sending status result to client: no client known\n"));
231       return;
232     }
233 #if DEBUG_ARM
234   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
235               "Telling client that service `%s' is now %s\n",
236               name,
237               result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up");
238 #endif
239   res = GNUNET_malloc (sizeof (uint16_t));
240   *res = result;
241   GNUNET_SERVER_notify_transmit_ready (client,
242                                        sizeof (struct GNUNET_MessageHeader),
243                                        GNUNET_TIME_UNIT_FOREVER_REL,
244                                        &write_result, res);
245 }
246
247
248 /**
249  * Find the process with the given service
250  * name in the given list, remove it and return it.
251  *
252  * @param name which service entry to look up
253  * @return NULL if it was not found
254  */
255 static struct ServiceList *
256 find_name (const char *name)
257 {
258   struct ServiceList *pos;
259   struct ServiceList *prev;
260
261   pos = running;
262   prev = NULL;
263   while (pos != NULL)
264     {
265       if (0 == strcmp (pos->name, name))
266         {
267           if (prev == NULL)
268             running = pos->next;
269           else
270             prev->next = pos->next;
271           pos->next = NULL;
272           return pos;
273         }
274       prev = pos;
275       pos = pos->next;
276     }
277   return NULL;
278 }
279
280
281 /**
282  * Free an entry in the service list.
283  *
284  * @param pos entry to free
285  */
286 static void
287 free_entry (struct ServiceList *pos)
288 {
289   GNUNET_free_non_null (pos->config);
290   GNUNET_free_non_null (pos->binary);
291   GNUNET_free (pos->name);
292   GNUNET_free (pos);
293 }
294
295
296 /**
297  * Actually start the process for the given service.
298  *
299  * @param sl identifies service to start
300  */
301 static void
302 start_process (struct ServiceList *sl)
303 {
304   char *loprefix;
305   char *options;
306   char **argv;
307   unsigned int argv_size;
308   char *lopos;
309   char *optpos;
310   const char *firstarg;
311   int use_debug;
312
313   /* start service */
314   if (GNUNET_OK !=
315       GNUNET_CONFIGURATION_get_value_string (cfg,
316                                              sl->name, "PREFIX", &loprefix))
317     loprefix = GNUNET_strdup (prefix_command);
318   if (GNUNET_OK !=
319       GNUNET_CONFIGURATION_get_value_string (cfg,
320                                              sl->name, "OPTIONS", &options))
321     options = GNUNET_strdup ("");
322   use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
323
324   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name);
325 #if DEBUG_ARM
326   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327               "Starting service `%s' using binary `%s' and configuration `%s'\n",
328               sl->name, sl->binary, sl->config);
329 #endif
330   argv_size = 6;
331   if (use_debug)
332     argv_size += 2;
333   lopos = loprefix;
334   while ('\0' != *lopos)
335     {
336       if (*lopos == ' ')
337         argv_size++;
338       lopos++;
339     }
340   optpos = options;
341   while ('\0' != *optpos)
342     {
343       if (*optpos == ' ')
344         argv_size++;
345       optpos++;
346     }
347   firstarg = NULL;
348   argv = GNUNET_malloc (argv_size * sizeof (char *));
349   argv_size = 0;
350   lopos = loprefix;
351
352   while ('\0' != *lopos)
353     {
354       while (*lopos == ' ')
355         lopos++;
356       if (*lopos == '\0')
357         continue;
358       if (argv_size == 0)
359         firstarg = lopos;
360       argv[argv_size++] = lopos;
361       while (('\0' != *lopos) && (' ' != *lopos))
362         lopos++;
363       if ('\0' == *lopos)
364         continue;
365       *lopos = '\0';
366       lopos++;
367     }
368   if (argv_size == 0)
369     firstarg = sl->binary;
370   argv[argv_size++] = sl->binary;
371   argv[argv_size++] = "-c";
372   argv[argv_size++] = sl->config;
373   if (GNUNET_YES == use_debug)
374     {
375       argv[argv_size++] = "-L";
376       argv[argv_size++] = "DEBUG";
377     }
378   optpos = options;
379   while ('\0' != *optpos)
380     {
381       while (*optpos == ' ')
382         optpos++;
383       if (*optpos == '\0')
384         continue;
385       argv[argv_size++] = optpos;
386       while (('\0' != *optpos) && (' ' != *optpos))
387         optpos++;
388       if ('\0' == *optpos)
389         continue;
390       *optpos = '\0';
391       optpos++;
392     }
393   argv[argv_size++] = NULL;
394   sl->pid = GNUNET_OS_start_process_v (firstarg, argv);
395   GNUNET_free (argv);
396   GNUNET_free (loprefix);
397   GNUNET_free (options);
398 }
399
400
401 /**
402  * Start the specified service.
403  *
404  * @param client who is asking for this
405  * @param servicename name of the service to start
406  */
407 static void
408 start_service (struct GNUNET_SERVER_Client *client, const char *servicename)
409 {
410   struct ServiceList *sl;
411   char *binary;
412   char *config;
413   struct stat sbuf;
414
415   if (GNUNET_YES == in_shutdown)
416     {
417       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
418                   _("ARM is shutting down, service `%s' not started.\n"), servicename);
419       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
420       return;
421     }
422   sl = find_name (servicename);
423   if (sl != NULL)
424     {
425       /* already running, just increment RC */
426       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
427                   _("Service `%s' already running.\n"), servicename);
428       sl->rc++;
429       sl->next = running;
430       running = sl;
431       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
432       return;
433     }
434   if (GNUNET_OK !=
435       GNUNET_CONFIGURATION_get_value_string (cfg,
436                                              servicename, "BINARY", &binary))
437     {
438       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
439                   _("Binary implementing service `%s' not known!\n"),
440                   servicename);
441       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
442       return;
443     }
444   if ((GNUNET_OK !=
445        GNUNET_CONFIGURATION_get_value_filename (cfg,
446                                                 servicename,
447                                                 "CONFIG",
448                                                 &config)) ||
449       (0 != STAT (config, &sbuf)))
450     {
451       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
452                   _("Configuration file `%s' for service `%s' not known!\n"),
453                   config, servicename);
454       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
455       GNUNET_free (binary);
456       GNUNET_free_non_null (config);
457       return;
458     }
459   sl = GNUNET_malloc (sizeof (struct ServiceList));
460   sl->name = GNUNET_strdup (servicename);
461   sl->next = running;
462   sl->rc = 1;
463   sl->binary = binary;
464   sl->config = config;
465   sl->mtime = sbuf.st_mtime;
466   running = sl;
467   start_process (sl);
468   if (NULL != client)
469     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
470 }
471
472
473 /**
474  * Free the given entry in the service list and signal
475  * the given client that the service is now down.
476  *
477  * @param cls pointer to the client ("struct GNUNET_SERVER_Client*")
478  * @param pos entry for the service
479  */
480 static void
481 free_and_signal (void *cls, struct ServiceList *pos)
482 {
483   struct GNUNET_SERVER_Client *client = cls;
484   /* find_name will remove "pos" from the list! */
485   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Service `%s' stopped\n", pos->name);
486   signal_result (client, pos->name, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
487   GNUNET_SERVER_receive_done (client, GNUNET_OK);
488   GNUNET_SERVER_client_drop (client);
489   free_entry (pos);
490 }
491
492
493 /**
494  * Stop the specified service.
495  *
496  * @param client who is asking for this
497  * @param servicename name of the service to stop
498  */
499 static void
500 stop_service (struct GNUNET_SERVER_Client *client,
501               const char *servicename)
502 {
503   struct ServiceList *pos;
504
505   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
506               _("Preparing to stop `%s'\n"), servicename);
507   pos = find_name (servicename);
508   if (pos == NULL)
509     {
510       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN);
511       GNUNET_SERVER_receive_done (client, GNUNET_OK);
512       return;
513     }
514   if (pos->rc > 1)
515     {
516       /* RC>1, just decrement RC */
517       pos->rc--;
518       pos->next = running;
519       running = pos;
520 #if DEBUG_ARM
521       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
522                   "Service `%s' still used by %u clients, will keep it running!\n",
523                   servicename,
524                   pos->rc);
525 #endif
526       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
527       GNUNET_SERVER_receive_done (client, GNUNET_OK);
528       return;
529     }
530   if (pos->rc == 1)
531     pos->rc--; /* decrement RC to zero */
532   if (pos->kill_continuation != NULL)
533     {
534       /* killing already in progress */
535 #if DEBUG_ARM
536       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537                   "Service `%s' is already down\n", servicename);
538 #endif
539       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
540       GNUNET_SERVER_receive_done (client, GNUNET_OK);
541       return;
542     }
543
544   if (GNUNET_YES == in_shutdown)
545     {
546 #if DEBUG_ARM
547       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
548                   "Termination request already sent to `%s' (since ARM is in shutdown).\n",
549                   servicename);
550 #endif
551       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
552       GNUNET_SERVER_receive_done (client, GNUNET_OK);
553       return;
554     }
555
556
557 #if DEBUG_ARM
558   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
559               "Sending kill signal to service `%s', waiting for process to die.\n",
560               servicename);
561 #endif
562   if (0 != PLIBC_KILL (pos->pid, SIGTERM))
563     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
564   pos->next = running;
565   running = pos;
566   pos->kill_continuation = &free_and_signal;
567   pos->kill_continuation_cls = client;
568   GNUNET_SERVER_client_keep (client);
569   GNUNET_SCHEDULER_add_delayed (sched,
570                                 MAINT_FAST_FREQUENCY, &maint, "non-null");
571 }
572
573
574 /**
575  * Handle START-message.
576  *
577  * @param cls closure (always NULL)
578  * @param client identification of the client
579  * @param message the actual message
580  * @return GNUNET_OK to keep the connection open,
581  *         GNUNET_SYSERR to close it (signal serious error)
582  */
583 static void
584 handle_start (void *cls,
585               struct GNUNET_SERVER_Client *client,
586               const struct GNUNET_MessageHeader *message)
587 {
588   const char *servicename;
589   uint16_t size;
590
591   size = ntohs (message->size);
592   size -= sizeof (struct GNUNET_MessageHeader);
593   servicename = (const char *) &message[1];
594   if ((size == 0) || (servicename[size - 1] != '\0'))
595     {
596       GNUNET_break (0);
597       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
598       return;
599     }
600   start_service (client, servicename);
601   GNUNET_SERVER_receive_done (client, GNUNET_OK);
602 }
603
604
605 /**
606  * Handle STOP-message.
607  *
608  * @param cls closure (always NULL)
609  * @param client identification of the client
610  * @param message the actual message
611  * @return GNUNET_OK to keep the connection open,
612  *         GNUNET_SYSERR to close it (signal serious error)
613  */
614 static void
615 handle_stop (void *cls,
616              struct GNUNET_SERVER_Client *client,
617              const struct GNUNET_MessageHeader *message)
618 {
619   const char *servicename;
620   uint16_t size;
621
622   size = ntohs (message->size);
623   size -= sizeof (struct GNUNET_MessageHeader);
624   servicename = (const char *) &message[1];
625   if ((size == 0) || (servicename[size - 1] != '\0'))
626     {
627       GNUNET_break (0);
628       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
629       return;
630     }
631   stop_service (client, servicename);
632 }
633
634
635 /**
636  * Background task doing maintenance.
637  *
638  * @param cls closure, NULL if we need to self-restart
639  * @param tc context
640  */
641 static void
642 maint (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
643 {
644   struct ServiceList *pos;
645   struct ServiceList *prev;
646   struct ServiceList *next;
647   const char *statstr;
648   int statcode;
649   struct stat sbuf;
650   int ret;
651
652   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
653     {
654       GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Stopping all services\n"));
655       in_shutdown = GNUNET_YES;
656       pos = running;
657       while (NULL != pos)
658         {
659           if (pos->pid != 0) 
660             {
661 #if DEBUG_ARM
662               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
663                           "Sending SIGTERM to `%s'\n",
664                           pos->name);
665 #endif
666               if (0 != PLIBC_KILL (pos->pid, SIGTERM))
667                 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");            
668             }
669           pos = pos->next;
670         }
671     }
672   if (cls == NULL)
673     {
674       if ( (in_shutdown == GNUNET_YES) &&
675            (running == NULL) )
676         {
677 #if DEBUG_ARM
678           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
679                       "ARM service terminates.\n");
680 #endif
681           GNUNET_assert (server != NULL);
682           GNUNET_SERVER_destroy (server);
683           server = NULL;
684           return; /* we are done! */      
685         }
686       GNUNET_SCHEDULER_add_delayed (tc->sched,
687                                     (in_shutdown == GNUNET_YES)
688                                     ? MAINT_FAST_FREQUENCY
689                                     : MAINT_FREQUENCY, 
690                                     &maint, NULL);
691     }
692
693   /* check for services that died (WAITPID) */
694   prev = NULL;
695   next = running;
696   while (NULL != (pos = next))
697     {
698       enum GNUNET_OS_ProcessStatusType statusType;
699       unsigned long statusCode;
700      
701       next = pos->next;
702       if ( (NULL != pos->kill_continuation) ||
703            ( (GNUNET_YES == in_shutdown) &&
704              (pos->pid == 0) ) )
705         {
706           if (prev == NULL)
707             running = next;
708           else
709             prev->next = next;
710           if (NULL != pos->kill_continuation)
711             pos->kill_continuation (pos->kill_continuation_cls, pos);
712           else
713             free_entry (pos);
714           continue;
715         }
716       if ( (GNUNET_SYSERR == (ret = GNUNET_OS_process_status(pos->pid, 
717                                                              &statusType,
718                                                              &statusCode))) ||
719            ( (ret == GNUNET_NO) ||
720              (statusType == GNUNET_OS_PROCESS_STOPPED) || 
721              (statusType == GNUNET_OS_PROCESS_RUNNING) ) )
722         {
723           prev = pos;
724           continue;
725         }
726       if (statusType == GNUNET_OS_PROCESS_EXITED)
727         {
728           statstr = _( /* process termination method */ "exit");
729           statcode = statusCode;
730         }
731       else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
732         {
733           statstr = _( /* process termination method */ "signal");
734           statcode = statusCode;
735         }
736       else
737         {
738           statstr = _( /* process termination method */ "unknown");
739           statcode = 0;
740         }    
741       if (GNUNET_YES != in_shutdown)
742         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
743                     _("Service `%s' terminated with status %s/%d, will try to restart it!\n"),
744                     pos->name, statstr, statcode);
745 #if DEBUG_ARM
746       else
747         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
748                     "Service `%s' terminated with status %s/%d\n",
749                     pos->name, statstr, statcode);
750 #endif  
751       /* schedule restart */
752       pos->pid = 0;
753       prev = pos;
754     }
755
756   /* check for services that need to be restarted due to
757      configuration changes or because the last restart failed */
758   pos = running;
759   while (pos != NULL)
760     {
761       if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime))
762         {
763           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
764                       _("Restarting service `%s' due to configuration file change.\n"));
765           if (0 != PLIBC_KILL (pos->pid, SIGTERM))
766             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
767         }
768       if ( (pos->pid == 0) &&
769            (GNUNET_YES != in_shutdown) )
770         {
771           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
772                       _("Restarting service `%s'.\n"), pos->name);
773           /* FIXME: should have some exponentially
774              increasing timer to avoid tight restart loops */
775           start_process (pos);
776         }
777       pos = pos->next;
778     }
779 }
780
781
782 /**
783  * List of handlers for the messages understood by this service.
784  */
785 static struct GNUNET_SERVER_MessageHandler handlers[] = {
786   {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
787   {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
788   {NULL, NULL, 0, 0}
789 };
790
791
792 /**
793  * Process arm requests.
794  *
795  * @param cls closure
796  * @param s scheduler to use
797  * @param serv the initialized server
798  * @param c configuration to use
799  */
800 static void
801 run (void *cls,
802      struct GNUNET_SCHEDULER_Handle *s,
803      struct GNUNET_SERVER_Handle *serv,
804      const struct GNUNET_CONFIGURATION_Handle *c)
805 {
806   char *defaultservices;
807   char *pos;
808
809   GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES);
810   GNUNET_assert (serv != NULL);
811   cfg = c;
812   sched = s;
813   server = serv;
814   if (GNUNET_OK !=
815       GNUNET_CONFIGURATION_get_value_string (cfg,
816                                              "ARM",
817                                              "GLOBAL_PREFIX",
818                                              &prefix_command))
819     prefix_command = GNUNET_strdup ("");
820   /* start default services... */
821   if (GNUNET_OK ==
822       GNUNET_CONFIGURATION_get_value_string (cfg,
823                                              "ARM",
824                                              "DEFAULTSERVICES",
825                                              &defaultservices))
826     {
827 #if DEBUG_ARM
828       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
829                   "Starting default services `%s'\n", defaultservices);
830 #endif
831       pos = strtok (defaultservices, " ");
832       while (pos != NULL)
833         {
834           start_service (NULL, pos);
835           pos = strtok (NULL, " ");
836         }
837       GNUNET_free (defaultservices);
838     }
839   else
840     {
841 #if DEBUG_ARM
842       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
843                   "No default services configured.\n");
844 #endif
845     }
846
847   /* process client requests */
848   GNUNET_SERVER_add_handlers (server, handlers);
849
850   /* manage services */
851   GNUNET_SCHEDULER_add_with_priority (sched,
852                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
853                                       &maint, NULL);
854 }
855
856
857 /**
858  * The main function for the arm service.
859  *
860  * @param argc number of arguments from the command line
861  * @param argv command line arguments
862  * @return 0 ok, 1 on error
863  */
864 int
865 main (int argc, char *const *argv)
866 {
867   return (GNUNET_OK ==
868           GNUNET_SERVICE_run (argc,
869                               argv, "arm",
870                               GNUNET_YES,
871                               &run, NULL)) ? 0 : 1;
872 }
873
874 /* end of gnunet-service-arm.c */