increase timeout to give sparc buildbot a chance
[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  * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
346  */
347 static void
348 start_process (struct ServiceList *sl,
349                const int *lsocks)
350 {
351   char *loprefix;
352   char *options;
353   char *optpos;
354   char *optend;
355   const char *next;
356   int use_debug;
357   char b;
358   char *val;
359
360   /* start service */
361   if (GNUNET_OK !=
362       GNUNET_CONFIGURATION_get_value_string (cfg,
363                                              sl->name, "PREFIX", &loprefix))
364     loprefix = GNUNET_strdup (prefix_command);
365   if (GNUNET_OK !=
366       GNUNET_CONFIGURATION_get_value_string (cfg,
367                                              sl->name, "OPTIONS", &options))
368     {      
369       options = GNUNET_strdup (final_option);
370       if (NULL == strstr (options, "%"))
371         {
372           /* replace '{}' with service name */
373           while (NULL != (optpos = strstr (options, "{}")))
374             {
375               optpos[0] = '%';
376               optpos[1] = 's';
377               GNUNET_asprintf (&optpos,
378                                options,
379                                sl->name);
380               GNUNET_free (options);
381               options = optpos;
382             }
383           /* replace '$PATH' with value associated with "PATH" */
384           while (NULL != (optpos = strstr (options, "$")))
385             {
386               optend = optpos + 1;
387               while (isupper ( (unsigned char) *optend)) optend++;            
388               b = *optend;
389               if ('\0' == b)
390                 next = "";
391               else
392                 next = optend+1;
393               *optend = '\0';
394               if (GNUNET_OK !=
395                   GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
396                                                          optpos+1,
397                                                          &val))
398                 val = GNUNET_strdup ("");
399               *optpos = '\0';
400               GNUNET_asprintf (&optpos,
401                                "%s%s%c%s",
402                                options,
403                                val,
404                                b,
405                                next);
406               GNUNET_free (options);
407               GNUNET_free (val);
408               options = optpos;
409             }
410         }
411     }
412   use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
413
414 #if DEBUG_ARM
415   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416               "Starting service `%s' using binary `%s' and configuration `%s'\n",
417               sl->name, sl->binary, sl->config);
418 #endif
419   if (GNUNET_YES == use_debug)
420     sl->pid = do_start_process (lsocks,
421                                 loprefix,                               
422                                 sl->binary,
423                                 "-c", sl->config,
424                                 "-L", "DEBUG",
425                                 options,
426                                 NULL);
427   else
428     sl->pid = do_start_process (lsocks,
429                                 loprefix,
430                                 sl->binary,
431                                 "-c", sl->config,
432                                 options,
433                                 NULL);
434   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
435               _("Starting service `%s' (PID: %d)\n"), 
436               sl->name,
437               (int) sl->pid);
438   GNUNET_free (loprefix);
439   GNUNET_free (options);
440   /* FIXME: should check sl->pid */
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  * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
450  * @return GNUNET_OK on success, GNUNET_SYSERR on error
451  */
452 int
453 start_service (struct GNUNET_SERVER_Client *client, 
454                const char *servicename,
455                const int *lsocks)
456 {
457   struct ServiceList *sl;
458   char *binary;
459   char *config;
460   struct stat sbuf;
461
462   if (GNUNET_YES == in_shutdown)
463     {
464       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
465                   _("ARM is shutting down, service `%s' not started.\n"),
466                   servicename);
467       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
468       return GNUNET_SYSERR;
469     }
470   sl = find_name (servicename);
471   if (sl != NULL)
472     {
473       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
474                   _("Service `%s' already running.\n"), servicename);
475       sl->next = running;
476       running = sl;
477       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
478       return GNUNET_SYSERR;
479     }
480   if (GNUNET_OK !=
481       GNUNET_CONFIGURATION_get_value_string (cfg,
482                                              servicename, "BINARY", &binary))
483     {
484       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
485                   _("Binary implementing service `%s' not known!\n"),
486                   servicename);
487       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
488       return GNUNET_SYSERR;
489     }
490   if ((GNUNET_OK !=
491        GNUNET_CONFIGURATION_get_value_filename (cfg,
492                                                 servicename,
493                                                 "CONFIG",
494                                                 &config)) ||
495       (0 != STAT (config, &sbuf)))
496     {
497       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
498                   _("Configuration file `%s' for service `%s' not known!\n"),
499                   config, servicename);
500       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
501       GNUNET_free (binary);
502       GNUNET_free_non_null (config);
503       return GNUNET_SYSERR;
504     }
505   (void) stop_listening (servicename);
506   sl = GNUNET_malloc (sizeof (struct ServiceList));
507   sl->name = GNUNET_strdup (servicename);
508   sl->next = running;
509   sl->binary = binary;
510   sl->config = config;
511   sl->mtime = sbuf.st_mtime;
512   sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
513   sl->restartAt = GNUNET_TIME_UNIT_FOREVER_ABS;
514
515   running = sl;
516   start_process (sl, lsocks);
517   if (NULL != client)
518     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
519   return GNUNET_OK;
520 }
521
522
523 /**
524  * Stop the specified service.
525  *
526  * @param client who is asking for this
527  * @param servicename name of the service to stop
528  */
529 static void
530 stop_service (struct GNUNET_SERVER_Client *client,
531               const char *servicename)
532 {
533   struct ServiceList *pos;
534
535   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
536               _("Preparing to stop `%s'\n"), servicename);
537   pos = find_name (servicename);
538   if (pos == NULL)
539     {
540       if (GNUNET_OK == stop_listening (servicename))
541         signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
542       else
543         signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN);
544       GNUNET_SERVER_receive_done (client, GNUNET_OK);
545       return;
546     }
547   if (pos->killing_client != NULL)
548     {
549       /* killing already in progress */
550 #if DEBUG_ARM
551       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552                   "Service `%s' is already down\n", servicename);
553 #endif
554       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
555       GNUNET_SERVER_receive_done (client, GNUNET_OK);
556       pos->next = running;
557       running = pos;
558       return;
559     }
560
561   if (GNUNET_YES == in_shutdown)
562     {
563 #if DEBUG_ARM
564       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565                   "Termination request already sent to `%s' (since ARM is in shutdown).\n",
566                   servicename);
567 #endif
568       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
569       GNUNET_SERVER_receive_done (client, GNUNET_OK);
570       pos->next = running;
571       running = pos;
572       return;
573     }
574   if (pos->pid == 0)
575     {
576       /* process is in delayed restart, simply remove it! */
577       free_entry (pos);
578       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
579       GNUNET_SERVER_receive_done (client, GNUNET_OK);
580       return;
581     }
582 #if DEBUG_ARM
583   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
584               "Sending kill signal to service `%s', waiting for process to die.\n",
585               servicename);
586 #endif
587   if (0 != PLIBC_KILL (pos->pid, SIGTERM))
588     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
589   pos->next = running;
590   running = pos;
591   pos->killing_client = client;
592   GNUNET_SERVER_client_keep (client);
593 }
594
595
596 /**
597  * Handle START-message.
598  *
599  * @param cls closure (always NULL)
600  * @param client identification of the client
601  * @param message the actual message
602  * @return GNUNET_OK to keep the connection open,
603  *         GNUNET_SYSERR to close it (signal serious error)
604  */
605 static void
606 handle_start (void *cls,
607               struct GNUNET_SERVER_Client *client,
608               const struct GNUNET_MessageHeader *message)
609 {
610   const char *servicename;
611   uint16_t size;
612
613   size = ntohs (message->size);
614   size -= sizeof (struct GNUNET_MessageHeader);
615   servicename = (const char *) &message[1];
616   if ((size == 0) || (servicename[size - 1] != '\0'))
617     {
618       GNUNET_break (0);
619       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
620       return;
621     }
622   start_service (client, servicename, NULL);
623   GNUNET_SERVER_receive_done (client, GNUNET_OK);
624 }
625
626
627 /**
628  * Handle STOP-message.
629  *
630  * @param cls closure (always NULL)
631  * @param client identification of the client
632  * @param message the actual message
633  * @return GNUNET_OK to keep the connection open,
634  *         GNUNET_SYSERR to close it (signal serious error)
635  */
636 static void
637 handle_stop (void *cls,
638              struct GNUNET_SERVER_Client *client,
639              const struct GNUNET_MessageHeader *message)
640 {
641   const char *servicename;
642   uint16_t size;
643
644   size = ntohs (message->size);
645   size -= sizeof (struct GNUNET_MessageHeader);
646   servicename = (const char *) &message[1];
647   if ((size == 0) || (servicename[size - 1] != '\0'))
648     {
649       GNUNET_break (0);
650       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
651       return;
652     }
653   stop_service (client, servicename);
654 }
655
656
657 /**
658  * Remove all entries for tasks that are not running
659  * (pid = 0) from the running list (they will no longer
660  * be restarted since we are shutting down).
661  */
662 static void
663 clean_up_running ()
664 {
665   struct ServiceList *pos;
666   struct ServiceList *next;
667   struct ServiceList *prev;
668  
669   pos = running;
670   prev = NULL;
671   while (NULL != pos)
672     {
673       next = pos->next;
674       if (pos->pid == 0)
675         {
676           if (prev == NULL)
677             running = next;
678           else
679             prev->next = next;
680           free_entry (pos);
681         }
682       else
683         prev = pos;
684       pos = next;
685     }
686 }
687
688
689 /**
690  * We are done with everything.  Stop remaining 
691  * tasks, signal handler and the server. 
692  */
693 static void
694 do_shutdown ()
695 {
696   GNUNET_SERVER_destroy (server);
697   server = NULL;
698   GNUNET_SIGNAL_handler_uninstall (shc_chld);
699   shc_chld = NULL;
700   GNUNET_SCHEDULER_cancel (sched, child_death_task);
701   child_death_task = GNUNET_SCHEDULER_NO_TASK;
702 }
703
704 #if DELAY_SHUTDOWN
705 /**
706  * Dummy task to delay arm shutdown.
707  */
708 void dummy_task (void *cls,
709                  const struct GNUNET_SCHEDULER_TaskContext * tc)
710 {
711   GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Dummy task executing\n");
712   return;
713 }
714 #endif
715
716 /**
717  * Task run for shutdown.
718  *
719  * @param cls closure, NULL if we need to self-restart
720  * @param tc context
721  */
722 static void
723 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
724 {
725   struct ServiceList *pos;
726
727 #if DEBUG_ARM
728   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n"));
729 #endif
730   stop_listening (NULL);
731   in_shutdown = GNUNET_YES;
732   pos = running;
733   while (NULL != pos)
734     {
735       if (pos->pid != 0)
736         {
737           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
738                       "Stopping service `%s' (PID: %d)\n",
739                       pos->name,
740                       pos->pid);
741           if (0 != PLIBC_KILL (pos->pid, SIGTERM))
742             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
743         }
744       pos = pos->next;
745     }
746 #if DELAY_SHUTDOWN
747   GNUNET_SCHEDULER_add_delayed(sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 2), &dummy_task, NULL);
748 #endif
749   if (running == NULL)
750     do_shutdown ();
751 }
752
753
754 /**
755  * Task run whenever it is time to restart a child that died.
756  *
757  * @param cls closure, always NULL
758  * @param tc context
759  */
760 static void
761 delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
762 {
763   struct ServiceList *pos;
764   struct GNUNET_TIME_Relative lowestRestartDelay;
765
766   child_restart_task = GNUNET_SCHEDULER_NO_TASK;
767   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
768     {
769       clean_up_running ();
770       if (NULL == running)
771         do_shutdown ();
772       return;
773     }
774   lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
775
776   /* check for services that need to be restarted due to
777      configuration changes or because the last restart failed */
778   pos = running;
779   while (pos != NULL)
780     {
781       if ( (pos->pid == 0) && 
782            (GNUNET_YES != in_shutdown) )
783         {
784           if (GNUNET_TIME_absolute_get_remaining (pos->restartAt).value == 0)
785             {
786               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
787                           _("Restarting service `%s'.\n"), pos->name);
788               start_process (pos, NULL);
789             }
790           else
791             {
792               lowestRestartDelay 
793                 = GNUNET_TIME_relative_min (lowestRestartDelay,
794                                             GNUNET_TIME_absolute_get_remaining
795                                             (pos->restartAt));
796             }
797         }
798       pos = pos->next;
799     }  
800   if (lowestRestartDelay.value != GNUNET_TIME_UNIT_FOREVER_REL.value)
801     {
802 #if DEBUG_ARM
803       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804                   "Will restart process in %llums\n",
805                   (unsigned long long) lowestRestartDelay.value);
806 #endif
807       child_restart_task
808         = GNUNET_SCHEDULER_add_delayed (sched,
809                                         lowestRestartDelay,
810                                         &delayed_restart_task,
811                                         NULL);
812     }
813 }
814
815
816 /**
817  * Task triggered whenever we receive a SIGCHLD (child
818  * process died).  
819  *
820  * @param cls closure, NULL if we need to self-restart
821  * @param tc context
822  */
823 static void
824 maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
825 {
826   struct ServiceList *pos;
827   struct ServiceList *prev;
828   struct ServiceList *next;
829   const char *statstr;
830   int statcode;
831   int ret;
832   char c[16];
833   enum GNUNET_OS_ProcessStatusType statusType;
834   unsigned long statusCode;
835
836   child_death_task = GNUNET_SCHEDULER_NO_TASK;
837   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
838     {
839       child_death_task =
840         GNUNET_SCHEDULER_add_read_file (sched, GNUNET_TIME_UNIT_FOREVER_REL, pr,
841                                         &maint_child_death, NULL);
842       return;    
843     }
844   /* consume the signal */
845   GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
846
847   /* check for services that died (WAITPID) */
848   prev = NULL;
849   next = running;
850   while (NULL != (pos = next))
851     {
852       next = pos->next;
853       if (pos->pid == 0) 
854         {
855           prev = pos;
856           continue;
857         }
858       if ((GNUNET_SYSERR == (ret = GNUNET_OS_process_status (pos->pid,
859                                                              &statusType,
860                                                              &statusCode))) ||
861           ( (ret == GNUNET_NO) ||
862             (statusType == GNUNET_OS_PROCESS_STOPPED) ||
863             (statusType == GNUNET_OS_PROCESS_RUNNING)) )
864         {
865           prev = pos;
866           continue;
867         }
868
869       if (statusType == GNUNET_OS_PROCESS_EXITED)
870         {
871           statstr = _( /* process termination method */ "exit");
872           statcode = statusCode;
873         }
874       else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
875         {
876           statstr = _( /* process termination method */ "signal");
877           statcode = statusCode;
878         }
879       else
880         {
881           statstr = _( /* process termination method */ "unknown");
882           statcode = 0;
883         }
884       pos->pid = 0;
885       if (NULL != pos->killing_client) 
886         {
887           if (prev == NULL)
888             running = next;
889           else
890             prev->next = next;
891           GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
892                       _("Service `%s' stopped\n"),
893                       pos->name);
894           signal_result (pos->killing_client, 
895                          pos->name, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
896           GNUNET_SERVER_receive_done (pos->killing_client, GNUNET_OK);
897           GNUNET_SERVER_client_drop (pos->killing_client);
898           free_entry (pos);
899           continue;
900         }
901       if (GNUNET_YES != in_shutdown)
902         {
903           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
904                       _
905                       ("Service `%s' terminated with status %s/%d, will try to restart it!\n"),
906                       pos->name, statstr, statcode);
907           /* schedule restart */
908           pos->restartAt
909             = GNUNET_TIME_relative_to_absolute (pos->backoff);
910           if (pos->backoff.value < EXPONENTIAL_BACKOFF_THRESHOLD)
911             pos->backoff 
912               = GNUNET_TIME_relative_multiply (pos->backoff, 2);
913           if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
914             GNUNET_SCHEDULER_cancel (sched, child_restart_task);
915           child_restart_task 
916             = GNUNET_SCHEDULER_add_with_priority (sched,
917                                                   GNUNET_SCHEDULER_PRIORITY_IDLE,
918                                                   &delayed_restart_task,
919                                                   NULL);
920         }
921 #if DEBUG_ARM
922       else
923         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
924                     "Service `%s' terminated with status %s/%d\n",
925                     pos->name, statstr, statcode);
926 #endif
927       prev = pos;
928     }
929   if (in_shutdown)
930     clean_up_running ();
931   if ( (running == NULL) &&
932        (in_shutdown) )
933     {
934       GNUNET_SERVER_destroy (server);
935       GNUNET_SIGNAL_handler_uninstall (shc_chld);
936       shc_chld = NULL;
937     }
938   else
939     {
940       child_death_task =
941         GNUNET_SCHEDULER_add_read_file (sched, GNUNET_TIME_UNIT_FOREVER_REL, pr,
942                                         &maint_child_death, NULL);
943     }
944 }
945
946
947 static size_t
948 transmit_shutdown_ack (void *cls, size_t size, void *buf)
949 {
950   struct GNUNET_SERVER_Client *client = cls;
951   struct GNUNET_MessageHeader *msg;
952
953   if (size < sizeof (struct GNUNET_MessageHeader))
954     {
955       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
956                   _("Failed to transmit shutdown ACK.\n"));
957       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
958       return 0;                 /* client disconnected */
959     }
960
961   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
962               _("Transmitting shutdown ACK.\n"));
963
964   msg = (struct GNUNET_MessageHeader *) buf;
965   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK);
966   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
967   GNUNET_SERVER_receive_done (client, GNUNET_OK);
968   GNUNET_SERVER_client_drop(client);
969   return sizeof (struct GNUNET_MessageHeader);
970 }
971
972 /**
973  * Handler for SHUTDOWN message.
974  *
975  * @param cls closure (refers to service)
976  * @param client identification of the client
977  * @param message the actual message
978  */
979 static void
980 handle_shutdown (void *cls,
981                  struct GNUNET_SERVER_Client *client,
982                  const struct GNUNET_MessageHeader *message)
983 {
984   GNUNET_SERVER_client_keep(client);
985   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
986               _("Initiating shutdown as requested by client.\n"));
987
988   GNUNET_SERVER_notify_transmit_ready (client,
989                                        sizeof(struct GNUNET_MessageHeader),
990                                        GNUNET_TIME_UNIT_FOREVER_REL,
991                                        &transmit_shutdown_ack, client);
992   GNUNET_SERVER_client_persist_ (client);
993   GNUNET_SCHEDULER_shutdown (sched);
994 }
995
996
997 /**
998  * Signal handler called for SIGCHLD.  Triggers the
999  * respective handler by writing to the trigger pipe.
1000  */
1001 static void
1002 sighandler_child_death ()
1003 {
1004   static char c;
1005   int old_errno = errno; /* back-up errno */
1006   GNUNET_break (1 == 
1007                 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
1008                                         (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
1009                                         sizeof (c)));
1010   errno = old_errno; /* restore errno */
1011 }
1012
1013
1014 /**
1015  * Process arm requests.
1016  *
1017  * @param cls closure
1018  * @param s scheduler to use
1019  * @param serv the initialized server
1020  * @param c configuration to use
1021  */
1022 static void
1023 run (void *cls,
1024      struct GNUNET_SCHEDULER_Handle *s,
1025      struct GNUNET_SERVER_Handle *serv,
1026      const struct GNUNET_CONFIGURATION_Handle *c)
1027 {
1028   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1029     {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
1030     {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
1031     {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
1032      sizeof (struct GNUNET_MessageHeader)},
1033     {NULL, NULL, 0, 0}
1034   };
1035   char *defaultservices;
1036   char *pos;
1037
1038   cfg = c;
1039   sched = s;
1040   server = serv;
1041   GNUNET_assert (serv != NULL);
1042   shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
1043   GNUNET_assert (sigpipe == NULL);
1044   sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO);
1045   GNUNET_assert (sigpipe != NULL);
1046   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
1047   GNUNET_assert (pr != NULL);
1048   GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES);
1049   GNUNET_SCHEDULER_add_delayed (sched,
1050                                 GNUNET_TIME_UNIT_FOREVER_REL,
1051                                 &shutdown_task,
1052                                 NULL);
1053   child_death_task =
1054     GNUNET_SCHEDULER_add_read_file (sched, GNUNET_TIME_UNIT_FOREVER_REL, pr,
1055                                     &maint_child_death, NULL);
1056
1057   if (GNUNET_OK !=
1058       GNUNET_CONFIGURATION_get_value_string (cfg,
1059                                              "ARM",
1060                                              "GLOBAL_PREFIX",
1061                                              &prefix_command))
1062     prefix_command = GNUNET_strdup ("");
1063   if (GNUNET_OK !=
1064       GNUNET_CONFIGURATION_get_value_string (cfg,
1065                                              "ARM",
1066                                              "GLOBAL_POSTFIX",
1067                                              &final_option))
1068     final_option = GNUNET_strdup ("");
1069   /* start default services... */
1070   if (GNUNET_OK ==
1071       GNUNET_CONFIGURATION_get_value_string (cfg,
1072                                              "ARM",
1073                                              "DEFAULTSERVICES",
1074                                              &defaultservices))
1075     {
1076 #if DEBUG_ARM
1077       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1078                   "Starting default services `%s'\n", defaultservices);
1079 #endif
1080       if (0 < strlen (defaultservices))
1081         {
1082           pos = strtok (defaultservices, " ");
1083           while (pos != NULL)
1084             {
1085               start_service (NULL, pos, NULL);
1086               pos = strtok (NULL, " ");
1087             }
1088         }
1089       GNUNET_free (defaultservices);
1090     }
1091   else
1092     {
1093 #if DEBUG_ARM
1094       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095                   "No default services configured.\n");
1096 #endif
1097     }
1098
1099   /* create listening sockets for future services*/
1100   prepareServices (cfg, sched);
1101   
1102   /* process client requests */
1103   GNUNET_SERVER_add_handlers (server, handlers);
1104
1105   /* manage services */
1106   GNUNET_SCHEDULER_add_with_priority (sched,
1107                                       GNUNET_SCHEDULER_PRIORITY_IDLE,
1108                                       &config_change_task, NULL);
1109 }
1110
1111
1112 /**
1113  * The main function for the arm service.
1114  *
1115  * @param argc number of arguments from the command line
1116  * @param argv command line arguments
1117  * @return 0 ok, 1 on error
1118  */
1119 int
1120 main (int argc, char *const *argv)
1121 {
1122   return (GNUNET_OK ==
1123           GNUNET_SERVICE_run (argc,
1124                               argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1;
1125 }
1126
1127 /* end of gnunet-service-arm.c */