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