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