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