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