missing function reference
[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 (pos->pid == 0)
581     {
582       /* process is in delayed restart, simply remove it! */
583       free_entry (pos);
584       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
585       GNUNET_SERVER_receive_done (client, GNUNET_OK);
586       return;
587     }
588 #if DEBUG_ARM
589   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
590               "Sending kill signal to service `%s', waiting for process to die.\n",
591               servicename);
592 #endif
593   if (0 != PLIBC_KILL (pos->pid, SIGTERM))
594     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
595   pos->next = running;
596   running = pos;
597   pos->killing_client = client;
598   GNUNET_SERVER_client_keep (client);
599 }
600
601
602 /**
603  * Handle START-message.
604  *
605  * @param cls closure (always NULL)
606  * @param client identification of the client
607  * @param message the actual message
608  * @return GNUNET_OK to keep the connection open,
609  *         GNUNET_SYSERR to close it (signal serious error)
610  */
611 static void
612 handle_start (void *cls,
613               struct GNUNET_SERVER_Client *client,
614               const struct GNUNET_MessageHeader *message)
615 {
616   const char *servicename;
617   uint16_t size;
618
619   size = ntohs (message->size);
620   size -= sizeof (struct GNUNET_MessageHeader);
621   servicename = (const char *) &message[1];
622   if ((size == 0) || (servicename[size - 1] != '\0'))
623     {
624       GNUNET_break (0);
625       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
626       return;
627     }
628   start_service (client, servicename);
629   GNUNET_SERVER_receive_done (client, GNUNET_OK);
630 }
631
632
633 /**
634  * Handle STOP-message.
635  *
636  * @param cls closure (always NULL)
637  * @param client identification of the client
638  * @param message the actual message
639  * @return GNUNET_OK to keep the connection open,
640  *         GNUNET_SYSERR to close it (signal serious error)
641  */
642 static void
643 handle_stop (void *cls,
644              struct GNUNET_SERVER_Client *client,
645              const struct GNUNET_MessageHeader *message)
646 {
647   const char *servicename;
648   uint16_t size;
649
650   size = ntohs (message->size);
651   size -= sizeof (struct GNUNET_MessageHeader);
652   servicename = (const char *) &message[1];
653   if ((size == 0) || (servicename[size - 1] != '\0'))
654     {
655       GNUNET_break (0);
656       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
657       return;
658     }
659   stop_service (client, servicename);
660 }
661
662
663 /**
664  * Remove all entries for tasks that are not running
665  * (pid = 0) from the running list (they will no longer
666  * be restarted since we are shutting down).
667  */
668 static void
669 clean_up_running ()
670 {
671   struct ServiceList *pos;
672   struct ServiceList *next;
673   struct ServiceList *prev;
674  
675   pos = running;
676   prev = NULL;
677   while (NULL != pos)
678     {
679       next = pos->next;
680       if (pos->pid == 0)
681         {
682           if (prev == NULL)
683             running = next;
684           else
685             prev->next = next;
686           free_entry (pos);
687         }
688       else
689         prev = pos;
690       pos = next;
691     }
692 }
693
694
695 /**
696  * We are done with everything.  Stop remaining 
697  * tasks, signal handler and the server. 
698  */
699 static void
700 do_shutdown ()
701 {
702   GNUNET_SERVER_destroy (server);
703   server = NULL;
704   GNUNET_SIGNAL_handler_uninstall (shc_chld);
705   shc_chld = NULL;
706   GNUNET_SCHEDULER_cancel (sched, child_death_task);
707   child_death_task = GNUNET_SCHEDULER_NO_TASK;
708 }
709
710
711 /**
712  * Task run for shutdown.
713  *
714  * @param cls closure, NULL if we need to self-restart
715  * @param tc context
716  */
717 static void
718 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
719 {
720   struct ServiceList *pos;
721  
722 #if DEBUG_ARM
723   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n"));
724 #endif
725   in_shutdown = GNUNET_YES;
726   pos = running;
727   while (NULL != pos)
728     {
729       if (pos->pid != 0)
730         {
731 #if DEBUG_ARM
732           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733                       "Sending SIGTERM to `%s'\n", pos->name);
734 #endif
735           if (0 != PLIBC_KILL (pos->pid, SIGTERM))
736             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
737         }
738       pos = pos->next;
739     }
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       if (statusType == GNUNET_OS_PROCESS_EXITED)
860         {
861           statstr = _( /* process termination method */ "exit");
862           statcode = statusCode;
863         }
864       else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
865         {
866           statstr = _( /* process termination method */ "signal");
867           statcode = statusCode;
868         }
869       else
870         {
871           statstr = _( /* process termination method */ "unknown");
872           statcode = 0;
873         }
874       pos->pid = 0;
875       if (NULL != pos->killing_client) 
876         {
877           if (prev == NULL)
878             running = next;
879           else
880             prev->next = next;
881           GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
882                       _("Service `%s' stopped\n"),
883                       pos->name);
884           signal_result (pos->killing_client, 
885                          pos->name, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
886           GNUNET_SERVER_receive_done (pos->killing_client, GNUNET_OK);
887           GNUNET_SERVER_client_drop (pos->killing_client);
888           free_entry (pos);
889           continue;
890         }
891       if (GNUNET_YES != in_shutdown)
892         {
893           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
894                       _
895                       ("Service `%s' terminated with status %s/%d, will try to restart it!\n"),
896                       pos->name, statstr, statcode);
897           /* schedule restart */
898           pos->restartAt
899             = GNUNET_TIME_relative_to_absolute (pos->backoff);
900           if (pos->backoff.value < EXPONENTIAL_BACKOFF_THRESHOLD)
901             pos->backoff 
902               = GNUNET_TIME_relative_multiply (pos->backoff, 2);
903           if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
904             GNUNET_SCHEDULER_cancel (sched, child_restart_task);
905           child_restart_task 
906             = GNUNET_SCHEDULER_add_with_priority (sched,
907                                                   GNUNET_SCHEDULER_PRIORITY_IDLE,
908                                                   &delayed_restart_task,
909                                                   NULL);
910         }
911 #if DEBUG_ARM
912       else
913         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
914                     "Service `%s' terminated with status %s/%d\n",
915                     pos->name, statstr, statcode);
916 #endif
917       prev = pos;
918     }
919   if (in_shutdown)
920     clean_up_running ();
921   if ( (running == NULL) &&
922        (in_shutdown) )
923     {
924       GNUNET_SERVER_destroy (server);
925       GNUNET_SIGNAL_handler_uninstall (shc_chld);
926       shc_chld = NULL;
927     }
928   else
929     {
930       child_death_task =
931         GNUNET_SCHEDULER_add_read_file (sched, GNUNET_TIME_UNIT_FOREVER_REL, pr,
932                                         &maint_child_death, NULL);
933     }
934 }
935
936
937 /**
938  * List of handlers for the messages understood by this service.
939  */
940 static struct GNUNET_SERVER_MessageHandler handlers[] = {
941   {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
942   {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
943   {NULL, NULL, 0, 0}
944 };
945
946 /**
947  * Signal handler called for SIGCHLD.  Triggers the
948  * respective handler by writing to the trigger pipe.
949  */
950 static void
951 sighandler_child_death ()
952 {
953   static char c;
954
955   GNUNET_break (1 == 
956                 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
957                                         (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
958                                         sizeof (c)));
959 }
960
961
962 /**
963  * Process arm requests.
964  *
965  * @param cls closure
966  * @param s scheduler to use
967  * @param serv the initialized server
968  * @param c configuration to use
969  */
970 static void
971 run (void *cls,
972      struct GNUNET_SCHEDULER_Handle *s,
973      struct GNUNET_SERVER_Handle *serv,
974      const struct GNUNET_CONFIGURATION_Handle *c)
975 {
976   char *defaultservices;
977   char *pos;
978
979   cfg = c;
980   sched = s;
981   server = serv;
982   GNUNET_assert (serv != NULL);
983   shc_chld = GNUNET_SIGNAL_handler_install (SIGCHLD, &sighandler_child_death);
984   GNUNET_assert (sigpipe == NULL);
985   sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
986   GNUNET_assert (sigpipe != NULL);
987   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
988   GNUNET_assert (pr != NULL);
989   GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES);
990   GNUNET_SCHEDULER_add_delayed (sched,
991                                 GNUNET_TIME_UNIT_FOREVER_REL,
992                                 &shutdown_task,
993                                 NULL);
994   child_death_task =
995     GNUNET_SCHEDULER_add_read_file (sched, GNUNET_TIME_UNIT_FOREVER_REL, pr,
996                                     &maint_child_death, NULL);
997
998   if (GNUNET_OK !=
999       GNUNET_CONFIGURATION_get_value_string (cfg,
1000                                              "ARM",
1001                                              "GLOBAL_PREFIX",
1002                                              &prefix_command))
1003     prefix_command = GNUNET_strdup ("");
1004   /* start default services... */
1005   if (GNUNET_OK ==
1006       GNUNET_CONFIGURATION_get_value_string (cfg,
1007                                              "ARM",
1008                                              "DEFAULTSERVICES",
1009                                              &defaultservices))
1010     {
1011 #if DEBUG_ARM
1012       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1013                   "Starting default services `%s'\n", defaultservices);
1014 #endif
1015       pos = strtok (defaultservices, " ");
1016       while (pos != NULL)
1017         {
1018           start_service (NULL, pos);
1019           pos = strtok (NULL, " ");
1020         }
1021       GNUNET_free (defaultservices);
1022     }
1023   else
1024     {
1025 #if DEBUG_ARM
1026       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1027                   "No default services configured.\n");
1028 #endif
1029     }
1030
1031   /* process client requests */
1032   GNUNET_SERVER_add_handlers (server, handlers);
1033
1034   /* manage services */
1035   GNUNET_SCHEDULER_add_with_priority (sched,
1036                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
1037                                       &config_change_task, NULL);
1038 }
1039
1040
1041 /**
1042  * The main function for the arm service.
1043  *
1044  * @param argc number of arguments from the command line
1045  * @param argv command line arguments
1046  * @return 0 ok, 1 on error
1047  */
1048 int
1049 main (int argc, char *const *argv)
1050 {
1051   return (GNUNET_OK ==
1052           GNUNET_SERVICE_run (argc,
1053                               argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1;
1054 }
1055
1056 /* end of gnunet-service-arm.c */