pass only unsigned char to isspace and the like
[oweals/gnunet.git] / src / arm / gnunet-service-arm.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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  * - need to test auto-restart code on configuration changes;
28  * - should refine restart code to check if *relevant* parts of the
29  *   configuration were changed (anything in the section for the service)
30  * - should have a way to specify dependencies between services and
31  *   manage restarts of groups of services
32  */
33 #include "platform.h"
34 #include "gnunet_client_lib.h"
35 #include "gnunet_getopt_lib.h"
36 #include "gnunet_os_lib.h"
37 #include "gnunet_protocols.h"
38 #include "gnunet_service_lib.h"
39 #include "gnunet_signal_lib.h"
40 #include "gnunet-service-arm.h"
41 #include "arm.h"
42
43
44 /**
45  * Check for configuration file changes every 5s.
46  */
47 #define MAINT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
48
49 /**
50  * Threshold after which exponential backoff shouldn't increase (in ms); 30m
51  */
52 #define EXPONENTIAL_BACKOFF_THRESHOLD (1000 * 60 * 30)
53
54 #define DELAY_SHUTDOWN GNUNET_NO
55
56 /**
57  * List of our services.
58  */
59 struct ServiceList;
60
61
62 /**
63  * List of our services.
64  */
65 struct ServiceList
66 {
67   /**
68    * This is a linked list.
69    */
70   struct ServiceList *next;
71
72   /**
73    * Name of the service.
74    */
75   char *name;
76
77   /**
78    * Name of the binary used.
79    */
80   char *binary;
81
82   /**
83    * Name of the configuration file used.
84    */
85   char *config;
86
87   /**
88    * Client to notify upon kill completion (waitpid), NULL
89    * if we should simply restart the process.
90    */
91   struct GNUNET_SERVER_Client *killing_client;
92
93   /**
94    * Process ID of the child.
95    */
96   pid_t pid;
97
98   /**
99    * Last time the config of this service was
100    * modified.
101    */
102   time_t mtime;
103
104   /**
105    * Process exponential backoff time 
106    */
107   struct GNUNET_TIME_Relative backoff;
108
109   /**
110    * Absolute time at which the process is scheduled to restart in case of death 
111    */
112   struct GNUNET_TIME_Absolute restartAt;
113
114 };
115
116 /**
117  * List of running services.
118  */
119 static struct ServiceList *running;
120
121 /**
122  * Our configuration
123  */
124 static const struct GNUNET_CONFIGURATION_Handle *cfg;
125
126 /**
127  * Our scheduler.
128  */
129 static struct GNUNET_SCHEDULER_Handle *sched;
130
131 /**
132  * Command to prepend to each actual command.
133  */
134 static char *prefix_command;
135
136 /**
137  * Option to append to each actual command.
138  */
139 static char *final_option;
140
141 /**
142  * ID of task called whenever we get a SIGCHILD.
143  */
144 static GNUNET_SCHEDULER_TaskIdentifier child_death_task;
145
146 /**
147  * ID of task called whenever the timeout for restarting a child
148  * expires.
149  */
150 static GNUNET_SCHEDULER_TaskIdentifier child_restart_task;
151
152 /**
153  * Context for our SIGCHILD handler.
154  */
155 static struct GNUNET_SIGNAL_Context *shc_chld;
156
157 /**
158  * Pipe used to communicate shutdown via signal.
159  */
160 static struct GNUNET_DISK_PipeHandle *sigpipe;
161
162 /**
163  * Reading end of the signal pipe.
164  */
165 static const struct GNUNET_DISK_FileHandle *pr;
166
167 /**
168  * Are we in shutdown mode?
169  */
170 static int in_shutdown;
171
172
173 /**
174  * Handle to our server instance.  Our server is a bit special in that
175  * its service is not immediately stopped once we get a shutdown
176  * request (since we need to continue service until all of our child
177  * processes are dead).  This handle is used to shut down the server
178  * (and thus trigger process termination) once all child processes are
179  * also dead.  A special option in the ARM configuration modifies the
180  * behaviour of the service implementation to not do the shutdown
181  * immediately.
182  */
183 static struct GNUNET_SERVER_Handle *server;
184
185
186 /**
187  * If the configuration file changes, restart tasks that depended on that
188  * option.
189  *
190  * @param cls closure, NULL if we need to self-restart
191  * @param tc context
192  */
193 static void 
194 config_change_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
195 {
196   struct ServiceList *pos;
197   struct stat sbuf;
198
199   pos = running;
200   while (pos != NULL)
201     {
202       /* FIXME: this test for config change is a bit too coarse grained */
203       if ( (0 == STAT (pos->config, &sbuf)) && 
204            (pos->mtime < sbuf.st_mtime) &&
205            (pos->pid != 0) )
206         {
207           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
208                       _("Restarting service `%s' due to configuration file change.\n"));
209           if (0 != PLIBC_KILL (pos->pid, SIGTERM))
210             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
211           else
212             pos->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
213         }
214       pos = pos->next;
215     }
216 }
217
218
219
220 /**
221  * Transmit a status result message.
222  *
223  * @param cls pointer to "unit16_t*" with message type
224  * @param size number of bytes available in buf
225  * @param buf where to copy the message, NULL on error
226  * @return number of bytes copied to buf
227  */
228 static size_t
229 write_result (void *cls, size_t size, void *buf)
230 {
231   uint16_t *res = cls;
232   struct GNUNET_MessageHeader *msg;
233
234   if (buf == NULL)
235     {
236       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
237                   _("Could not send status result to client\n"));
238       return 0;                 /* error, not much we can do */
239     }
240 #if DEBUG_ARM
241   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242               "Sending status response %u to client\n",
243               (unsigned int) *res);
244 #endif
245   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
246   msg = buf;
247   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
248   msg->type = htons (*res);
249   GNUNET_free (res);
250   return sizeof (struct GNUNET_MessageHeader);
251 }
252
253
254
255 /**
256  * Signal our client that we will start or stop the
257  * service.
258  *
259  * @param client who is being signalled
260  * @param name name of the service
261  * @param result message type to send
262  * @return NULL if it was not found
263  */
264 static void
265 signal_result (struct GNUNET_SERVER_Client *client,
266                const char *name, uint16_t result)
267 {
268   uint16_t *res;
269
270   if (NULL == client)
271     {
272       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
273                   _
274                   ("Not sending status result to client: no client known\n"));
275       return;
276     }
277 #if DEBUG_ARM
278   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
279               "Telling client that service `%s' is now %s\n",
280               name,
281               result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up");
282 #endif
283   res = GNUNET_malloc (sizeof (uint16_t));
284   *res = result;
285   GNUNET_SERVER_notify_transmit_ready (client,
286                                        sizeof (struct GNUNET_MessageHeader),
287                                        GNUNET_TIME_UNIT_FOREVER_REL,
288                                        &write_result, res);
289 }
290
291
292 /**
293  * Find the process with the given service
294  * name in the given list, remove it and return it.
295  *
296  * @param name which service entry to look up
297  * @return NULL if it was not found
298  */
299 static struct ServiceList *
300 find_name (const char *name)
301 {
302   struct ServiceList *pos;
303   struct ServiceList *prev;
304
305   pos = running;
306   prev = NULL;
307   while (pos != NULL)
308     {
309       if (0 == strcmp (pos->name, name))
310         {
311           if (prev == NULL)
312             running = pos->next;
313           else
314             prev->next = pos->next;
315           pos->next = NULL;
316           return pos;
317         }
318       prev = pos;
319       pos = pos->next;
320     }
321   return NULL;
322 }
323
324
325 /**
326  * Free an entry in the service list.
327  *
328  * @param pos entry to free
329  */
330 static void
331 free_entry (struct ServiceList *pos)
332 {
333   GNUNET_free_non_null (pos->config);
334   GNUNET_free_non_null (pos->binary);
335   GNUNET_free (pos->name);
336   GNUNET_free (pos);
337 }
338
339 #include "do_start_process.c"
340
341 /**
342  * Actually start the process for the given service.
343  *
344  * @param sl identifies service to start
345  */
346 static void
347 start_process (struct ServiceList *sl)
348 {
349   char *loprefix;
350   char *options;
351   char *optpos;
352   char *optend;
353   const char *next;
354   int use_debug;
355   char b;
356   char *val;
357
358   /* start service */
359   if (GNUNET_OK !=
360       GNUNET_CONFIGURATION_get_value_string (cfg,
361                                              sl->name, "PREFIX", &loprefix))
362     loprefix = GNUNET_strdup (prefix_command);
363   if (GNUNET_OK !=
364       GNUNET_CONFIGURATION_get_value_string (cfg,
365                                              sl->name, "OPTIONS", &options))
366     {      
367       options = GNUNET_strdup (final_option);
368       if (NULL == strstr (options, "%"))
369         {
370           /* replace '{}' with service name */
371           while (NULL != (optpos = strstr (options, "{}")))
372             {
373               optpos[0] = '%';
374               optpos[1] = 's';
375               GNUNET_asprintf (&optpos,
376                                options,
377                                sl->name);
378               GNUNET_free (options);
379               options = optpos;
380             }
381           /* replace '$PATH' with value associated with "PATH" */
382           while (NULL != (optpos = strstr (options, "$")))
383             {
384               optend = optpos + 1;
385               while (isupper ( (unsigned char) *optend)) optend++;            
386               b = *optend;
387               if ('\0' == b)
388                 next = "";
389               else
390                 next = optend+1;
391               *optend = '\0';
392               if (GNUNET_OK !=
393                   GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
394                                                          optpos+1,
395                                                          &val))
396                 val = GNUNET_strdup ("");
397               *optpos = '\0';
398               GNUNET_asprintf (&optpos,
399                                "%s%s%c%s",
400                                options,
401                                val,
402                                b,
403                                next);
404               GNUNET_free (options);
405               GNUNET_free (val);
406               options = optpos;
407             }
408         }
409     }
410   use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
411
412 #if DEBUG_ARM
413   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
414               "Starting service `%s' using binary `%s' and configuration `%s'\n",
415               sl->name, sl->binary, sl->config);
416 #endif
417   if (GNUNET_YES == use_debug)
418     sl->pid = do_start_process (loprefix,
419                                 sl->binary,
420                                 "-c", sl->config,
421                                 "-L", "DEBUG",
422                                 options,
423                                 NULL);
424   else
425     sl->pid = do_start_process (loprefix,
426                                 sl->binary,
427                                 "-c", sl->config,
428                                 options,
429                                 NULL);
430   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
431               _("Starting service `%s' (PID: %d)\n"), 
432               sl->name,
433               (int) sl->pid);
434   GNUNET_free (loprefix);
435   GNUNET_free (options);
436   /* FIXME: should check sl->pid */
437 }
438
439
440 /**
441  * Start the specified service.
442  *
443  * @param client who is asking for this
444  * @param servicename name of the service to start
445  */
446 void
447 start_service (struct GNUNET_SERVER_Client *client, const char *servicename)
448 {
449   struct ServiceList *sl;
450   char *binary;
451   char *config;
452   struct stat sbuf;
453
454   if (GNUNET_YES == in_shutdown)
455     {
456       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
457                   _("ARM is shutting down, service `%s' not started.\n"),
458                   servicename);
459       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
460       return;
461     }
462   sl = find_name (servicename);
463   if (sl != NULL)
464     {
465       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466                   _("Service `%s' already running.\n"), servicename);
467       sl->next = running;
468       running = sl;
469       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
470       return;
471     }
472   if (GNUNET_OK !=
473       GNUNET_CONFIGURATION_get_value_string (cfg,
474                                              servicename, "BINARY", &binary))
475     {
476       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
477                   _("Binary implementing service `%s' not known!\n"),
478                   servicename);
479       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
480       return;
481     }
482   if ((GNUNET_OK !=
483        GNUNET_CONFIGURATION_get_value_filename (cfg,
484                                                 servicename,
485                                                 "CONFIG",
486                                                 &config)) ||
487       (0 != STAT (config, &sbuf)))
488     {
489       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
490                   _("Configuration file `%s' for service `%s' not known!\n"),
491                   config, servicename);
492       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
493       GNUNET_free (binary);
494       GNUNET_free_non_null (config);
495       return;
496     }
497   (void) stop_listening (servicename);
498   sl = GNUNET_malloc (sizeof (struct ServiceList));
499   sl->name = GNUNET_strdup (servicename);
500   sl->next = running;
501   sl->binary = binary;
502   sl->config = config;
503   sl->mtime = sbuf.st_mtime;
504   sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
505   sl->restartAt = GNUNET_TIME_UNIT_FOREVER_ABS;
506
507   running = sl;
508   start_process (sl);
509   if (NULL != client)
510     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
511 }
512
513
514 /**
515  * Stop the specified service.
516  *
517  * @param client who is asking for this
518  * @param servicename name of the service to stop
519  */
520 static void
521 stop_service (struct GNUNET_SERVER_Client *client,
522               const char *servicename)
523 {
524   struct ServiceList *pos;
525
526   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
527               _("Preparing to stop `%s'\n"), servicename);
528   pos = find_name (servicename);
529   if (pos == NULL)
530     {
531       if (GNUNET_OK == stop_listening (servicename))
532         signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
533       else
534         signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN);
535       GNUNET_SERVER_receive_done (client, GNUNET_OK);
536       return;
537     }
538   if (pos->killing_client != NULL)
539     {
540       /* killing already in progress */
541 #if DEBUG_ARM
542       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
543                   "Service `%s' is already down\n", servicename);
544 #endif
545       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
546       GNUNET_SERVER_receive_done (client, GNUNET_OK);
547       pos->next = running;
548       running = pos;
549       return;
550     }
551
552   if (GNUNET_YES == in_shutdown)
553     {
554 #if DEBUG_ARM
555       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
556                   "Termination request already sent to `%s' (since ARM is in shutdown).\n",
557                   servicename);
558 #endif
559       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
560       GNUNET_SERVER_receive_done (client, GNUNET_OK);
561       pos->next = running;
562       running = pos;
563       return;
564     }
565   if (pos->pid == 0)
566     {
567       /* process is in delayed restart, simply remove it! */
568       free_entry (pos);
569       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
570       GNUNET_SERVER_receive_done (client, GNUNET_OK);
571       return;
572     }
573 #if DEBUG_ARM
574   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575               "Sending kill signal to service `%s', waiting for process to die.\n",
576               servicename);
577 #endif
578   if (0 != PLIBC_KILL (pos->pid, SIGTERM))
579     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
580   pos->next = running;
581   running = pos;
582   pos->killing_client = client;
583   GNUNET_SERVER_client_keep (client);
584 }
585
586
587 /**
588  * Handle START-message.
589  *
590  * @param cls closure (always NULL)
591  * @param client identification of the client
592  * @param message the actual message
593  * @return GNUNET_OK to keep the connection open,
594  *         GNUNET_SYSERR to close it (signal serious error)
595  */
596 static void
597 handle_start (void *cls,
598               struct GNUNET_SERVER_Client *client,
599               const struct GNUNET_MessageHeader *message)
600 {
601   const char *servicename;
602   uint16_t size;
603
604   size = ntohs (message->size);
605   size -= sizeof (struct GNUNET_MessageHeader);
606   servicename = (const char *) &message[1];
607   if ((size == 0) || (servicename[size - 1] != '\0'))
608     {
609       GNUNET_break (0);
610       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
611       return;
612     }
613   start_service (client, servicename);
614   GNUNET_SERVER_receive_done (client, GNUNET_OK);
615 }
616
617
618 /**
619  * Handle STOP-message.
620  *
621  * @param cls closure (always NULL)
622  * @param client identification of the client
623  * @param message the actual message
624  * @return GNUNET_OK to keep the connection open,
625  *         GNUNET_SYSERR to close it (signal serious error)
626  */
627 static void
628 handle_stop (void *cls,
629              struct GNUNET_SERVER_Client *client,
630              const struct GNUNET_MessageHeader *message)
631 {
632   const char *servicename;
633   uint16_t size;
634
635   size = ntohs (message->size);
636   size -= sizeof (struct GNUNET_MessageHeader);
637   servicename = (const char *) &message[1];
638   if ((size == 0) || (servicename[size - 1] != '\0'))
639     {
640       GNUNET_break (0);
641       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
642       return;
643     }
644   stop_service (client, servicename);
645 }
646
647
648 /**
649  * Remove all entries for tasks that are not running
650  * (pid = 0) from the running list (they will no longer
651  * be restarted since we are shutting down).
652  */
653 static void
654 clean_up_running ()
655 {
656   struct ServiceList *pos;
657   struct ServiceList *next;
658   struct ServiceList *prev;
659  
660   pos = running;
661   prev = NULL;
662   while (NULL != pos)
663     {
664       next = pos->next;
665       if (pos->pid == 0)
666         {
667           if (prev == NULL)
668             running = next;
669           else
670             prev->next = next;
671           free_entry (pos);
672         }
673       else
674         prev = pos;
675       pos = next;
676     }
677 }
678
679
680 /**
681  * We are done with everything.  Stop remaining 
682  * tasks, signal handler and the server. 
683  */
684 static void
685 do_shutdown ()
686 {
687   GNUNET_SERVER_destroy (server);
688   server = NULL;
689   GNUNET_SIGNAL_handler_uninstall (shc_chld);
690   shc_chld = NULL;
691   GNUNET_SCHEDULER_cancel (sched, child_death_task);
692   child_death_task = GNUNET_SCHEDULER_NO_TASK;
693 }
694
695 #if DELAY_SHUTDOWN
696 /**
697  * Dummy task to delay arm shutdown.
698  */
699 void dummy_task (void *cls,
700                  const struct GNUNET_SCHEDULER_TaskContext * tc)
701 {
702   GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Dummy task executing\n");
703   return;
704 }
705 #endif
706
707 /**
708  * Task run for shutdown.
709  *
710  * @param cls closure, NULL if we need to self-restart
711  * @param tc context
712  */
713 static void
714 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
715 {
716   struct ServiceList *pos;
717
718 #if DEBUG_ARM
719   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n"));
720 #endif
721   stop_listening (NULL);
722   in_shutdown = GNUNET_YES;
723   pos = running;
724   while (NULL != pos)
725     {
726       if (pos->pid != 0)
727         {
728           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
729                       "Stopping service `%s' (PID: %d)\n",
730                       pos->name,
731                       pos->pid);
732           if (0 != PLIBC_KILL (pos->pid, SIGTERM))
733             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
734         }
735       pos = pos->next;
736     }
737 #if DELAY_SHUTDOWN
738   GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 2), &dummy_task, NULL);
739 #endif
740   if (running == NULL)
741     do_shutdown ();
742 }
743
744
745 /**
746  * Task run whenever it is time to restart a child that died.
747  *
748  * @param cls closure, always NULL
749  * @param tc context
750  */
751 static void
752 delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
753 {
754   struct ServiceList *pos;
755   struct GNUNET_TIME_Relative lowestRestartDelay;
756
757   child_restart_task = GNUNET_SCHEDULER_NO_TASK;
758   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
759     {
760       clean_up_running ();
761       if (NULL == running)
762         do_shutdown ();
763       return;
764     }
765   lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
766
767   /* check for services that need to be restarted due to
768      configuration changes or because the last restart failed */
769   pos = running;
770   while (pos != NULL)
771     {
772       if ( (pos->pid == 0) && 
773            (GNUNET_YES != in_shutdown) )
774         {
775           if (GNUNET_TIME_absolute_get_remaining (pos->restartAt).value == 0)
776             {
777               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
778                           _("Restarting service `%s'.\n"), pos->name);
779               start_process (pos);
780             }
781           else
782             {
783               lowestRestartDelay 
784                 = GNUNET_TIME_relative_min (lowestRestartDelay,
785                                             GNUNET_TIME_absolute_get_remaining
786                                             (pos->restartAt));
787             }
788         }
789       pos = pos->next;
790     }  
791   if (lowestRestartDelay.value != GNUNET_TIME_UNIT_FOREVER_REL.value)
792     {
793 #if DEBUG_ARM
794       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795                   "Will restart process in %llums\n",
796                   (unsigned long long) lowestRestartDelay.value);
797 #endif
798       child_restart_task
799         = GNUNET_SCHEDULER_add_delayed (sched,
800                                         lowestRestartDelay,
801                                         &delayed_restart_task,
802                                         NULL);
803     }
804 }
805
806
807 /**
808  * Task triggered whenever we receive a SIGCHLD (child
809  * process died).  
810  *
811  * @param cls closure, NULL if we need to self-restart
812  * @param tc context
813  */
814 static void
815 maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
816 {
817   struct ServiceList *pos;
818   struct ServiceList *prev;
819   struct ServiceList *next;
820   const char *statstr;
821   int statcode;
822   int ret;
823   char c[16];
824   enum GNUNET_OS_ProcessStatusType statusType;
825   unsigned long statusCode;
826
827   child_death_task = GNUNET_SCHEDULER_NO_TASK;
828   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
829     {
830       child_death_task =
831         GNUNET_SCHEDULER_add_read_file (sched, GNUNET_TIME_UNIT_FOREVER_REL, pr,
832                                         &maint_child_death, NULL);
833       return;    
834     }
835   /* consume the signal */
836   GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
837
838   /* check for services that died (WAITPID) */
839   prev = NULL;
840   next = running;
841   while (NULL != (pos = next))
842     {
843       next = pos->next;
844       if (pos->pid == 0) 
845         {
846           prev = pos;
847           continue;
848         }
849       if ((GNUNET_SYSERR == (ret = GNUNET_OS_process_status (pos->pid,
850                                                              &statusType,
851                                                              &statusCode))) ||
852           ( (ret == GNUNET_NO) ||
853             (statusType == GNUNET_OS_PROCESS_STOPPED) ||
854             (statusType == GNUNET_OS_PROCESS_RUNNING)) )
855         {
856           prev = pos;
857           continue;
858         }
859
860       if (statusType == GNUNET_OS_PROCESS_EXITED)
861         {
862           statstr = _( /* process termination method */ "exit");
863           statcode = statusCode;
864         }
865       else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
866         {
867           statstr = _( /* process termination method */ "signal");
868           statcode = statusCode;
869         }
870       else
871         {
872           statstr = _( /* process termination method */ "unknown");
873           statcode = 0;
874         }
875       pos->pid = 0;
876       if (NULL != pos->killing_client) 
877         {
878           if (prev == NULL)
879             running = next;
880           else
881             prev->next = next;
882           GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
883                       _("Service `%s' stopped\n"),
884                       pos->name);
885           signal_result (pos->killing_client, 
886                          pos->name, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
887           GNUNET_SERVER_receive_done (pos->killing_client, GNUNET_OK);
888           GNUNET_SERVER_client_drop (pos->killing_client);
889           free_entry (pos);
890           continue;
891         }
892       if (GNUNET_YES != in_shutdown)
893         {
894           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
895                       _
896                       ("Service `%s' terminated with status %s/%d, will try to restart it!\n"),
897                       pos->name, statstr, statcode);
898           /* schedule restart */
899           pos->restartAt
900             = GNUNET_TIME_relative_to_absolute (pos->backoff);
901           if (pos->backoff.value < EXPONENTIAL_BACKOFF_THRESHOLD)
902             pos->backoff 
903               = GNUNET_TIME_relative_multiply (pos->backoff, 2);
904           if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
905             GNUNET_SCHEDULER_cancel (sched, child_restart_task);
906           child_restart_task 
907             = GNUNET_SCHEDULER_add_with_priority (sched,
908                                                   GNUNET_SCHEDULER_PRIORITY_IDLE,
909                                                   &delayed_restart_task,
910                                                   NULL);
911         }
912 #if DEBUG_ARM
913       else
914         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
915                     "Service `%s' terminated with status %s/%d\n",
916                     pos->name, statstr, statcode);
917 #endif
918       prev = pos;
919     }
920   if (in_shutdown)
921     clean_up_running ();
922   if ( (running == NULL) &&
923        (in_shutdown) )
924     {
925       GNUNET_SERVER_destroy (server);
926       GNUNET_SIGNAL_handler_uninstall (shc_chld);
927       shc_chld = NULL;
928     }
929   else
930     {
931       child_death_task =
932         GNUNET_SCHEDULER_add_read_file (sched, GNUNET_TIME_UNIT_FOREVER_REL, pr,
933                                         &maint_child_death, NULL);
934     }
935 }
936
937
938 static size_t
939 transmit_shutdown_ack (void *cls, size_t size, void *buf)
940 {
941   struct GNUNET_SERVER_Client *client = cls;
942   struct GNUNET_MessageHeader *msg;
943
944   if (size < sizeof (struct GNUNET_MessageHeader))
945     {
946       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
947                   _("Failed to transmit shutdown ACK.\n"));
948       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
949       return 0;                 /* client disconnected */
950     }
951
952   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
953               _("Transmitting shutdown ACK.\n"));
954
955   msg = (struct GNUNET_MessageHeader *) buf;
956   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK);
957   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
958   GNUNET_SERVER_receive_done (client, GNUNET_OK);
959   GNUNET_SERVER_client_drop(client);
960   return sizeof (struct GNUNET_MessageHeader);
961 }
962
963 /**
964  * Handler for SHUTDOWN message.
965  *
966  * @param cls closure (refers to service)
967  * @param client identification of the client
968  * @param message the actual message
969  */
970 static void
971 handle_shutdown (void *cls,
972                  struct GNUNET_SERVER_Client *client,
973                  const struct GNUNET_MessageHeader *message)
974 {
975   GNUNET_SERVER_client_keep(client);
976   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
977               _("Initiating shutdown as requested by client.\n"));
978
979   GNUNET_SERVER_notify_transmit_ready (client,
980                                        sizeof(struct GNUNET_MessageHeader),
981                                        GNUNET_TIME_UNIT_FOREVER_REL,
982                                        &transmit_shutdown_ack, client);
983   GNUNET_SERVER_client_persist_ (client);
984   GNUNET_SCHEDULER_shutdown (sched);
985 }
986
987
988 /**
989  * List of handlers for the messages understood by this service.
990  */
991 static struct GNUNET_SERVER_MessageHandler handlers[] = {
992   {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
993   {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
994   {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
995    sizeof (struct GNUNET_MessageHeader)},
996   {NULL, NULL, 0, 0}
997 };
998
999 /**
1000  * Signal handler called for SIGCHLD.  Triggers the
1001  * respective handler by writing to the trigger pipe.
1002  */
1003 static void
1004 sighandler_child_death ()
1005 {
1006   static char c;
1007   GNUNET_break (1 == 
1008                 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
1009                                         (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
1010                                         sizeof (c)));
1011 }
1012
1013
1014 /**
1015  * Process arm requests.
1016  *
1017  * @param cls closure
1018  * @param s scheduler to use
1019  * @param serv the initialized server
1020  * @param c configuration to use
1021  */
1022 static void
1023 run (void *cls,
1024      struct GNUNET_SCHEDULER_Handle *s,
1025      struct GNUNET_SERVER_Handle *serv,
1026      const struct GNUNET_CONFIGURATION_Handle *c)
1027 {
1028   char *defaultservices;
1029   char *pos;
1030
1031   cfg = c;
1032   sched = s;
1033   server = serv;
1034   GNUNET_assert (serv != NULL);
1035   shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
1036   GNUNET_assert (sigpipe == NULL);
1037   sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
1038   GNUNET_assert (sigpipe != NULL);
1039   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
1040   GNUNET_assert (pr != NULL);
1041   GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES);
1042   GNUNET_SCHEDULER_add_delayed (sched,
1043                                 GNUNET_TIME_UNIT_FOREVER_REL,
1044                                 &shutdown_task,
1045                                 NULL);
1046   child_death_task =
1047     GNUNET_SCHEDULER_add_read_file (sched, GNUNET_TIME_UNIT_FOREVER_REL, pr,
1048                                     &maint_child_death, NULL);
1049
1050   if (GNUNET_OK !=
1051       GNUNET_CONFIGURATION_get_value_string (cfg,
1052                                              "ARM",
1053                                              "GLOBAL_PREFIX",
1054                                              &prefix_command))
1055     prefix_command = GNUNET_strdup ("");
1056   if (GNUNET_OK !=
1057       GNUNET_CONFIGURATION_get_value_string (cfg,
1058                                              "ARM",
1059                                              "GLOBAL_POSTFIX",
1060                                              &final_option))
1061     final_option = GNUNET_strdup ("");
1062   /* start default services... */
1063   if (GNUNET_OK ==
1064       GNUNET_CONFIGURATION_get_value_string (cfg,
1065                                              "ARM",
1066                                              "DEFAULTSERVICES",
1067                                              &defaultservices))
1068     {
1069 #if DEBUG_ARM
1070       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1071                   "Starting default services `%s'\n", defaultservices);
1072 #endif
1073       if (0 < strlen (defaultservices))
1074         {
1075           pos = strtok (defaultservices, " ");
1076           while (pos != NULL)
1077             {
1078               start_service (NULL, pos);
1079               pos = strtok (NULL, " ");
1080             }
1081         }
1082       GNUNET_free (defaultservices);
1083     }
1084   else
1085     {
1086 #if DEBUG_ARM
1087       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1088                   "No default services configured.\n");
1089 #endif
1090     }
1091
1092   /* create listening sockets for future services*/
1093   prepareServices (cfg, sched);
1094   
1095   /* process client requests */
1096   GNUNET_SERVER_add_handlers (server, handlers);
1097
1098   /* manage services */
1099   GNUNET_SCHEDULER_add_with_priority (sched,
1100                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
1101                                       &config_change_task, NULL);
1102 }
1103
1104
1105 /**
1106  * The main function for the arm service.
1107  *
1108  * @param argc number of arguments from the command line
1109  * @param argv command line arguments
1110  * @return 0 ok, 1 on error
1111  */
1112 int
1113 main (int argc, char *const *argv)
1114 {
1115   return (GNUNET_OK ==
1116           GNUNET_SERVICE_run (argc,
1117                               argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1;
1118 }
1119
1120 /* end of gnunet-service-arm.c */