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