fix
[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   if (NULL != server)
692     {
693       GNUNET_SERVER_destroy (server);
694       server = NULL;
695     }
696   if (GNUNET_SCHEDULER_NO_TASK != child_death_task)
697     {
698       GNUNET_SCHEDULER_cancel (child_death_task);
699       child_death_task = GNUNET_SCHEDULER_NO_TASK;
700     }
701 }
702
703 #if DELAY_SHUTDOWN
704 /**
705  * Dummy task to delay arm shutdown.
706  */
707 void dummy_task (void *cls,
708                  const struct GNUNET_SCHEDULER_TaskContext * tc)
709 {
710   GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Dummy task executing\n");
711   return;
712 }
713 #endif
714
715 /**
716  * Task run for shutdown.
717  *
718  * @param cls closure, NULL if we need to self-restart
719  * @param tc context
720  */
721 static void
722 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
723 {
724   struct ServiceList *pos;
725
726 #if DEBUG_ARM
727   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n"));
728 #endif
729   stop_listening (NULL);
730   in_shutdown = GNUNET_YES;
731   pos = running;
732   while (NULL != pos)
733     {
734       if (pos->proc != NULL)
735         {
736           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
737                       "Stopping service `%s'\n",
738                       pos->name);
739           if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
740             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
741         }
742       pos = pos->next;
743     }
744 #if DELAY_SHUTDOWN
745   GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 2), &dummy_task, NULL);
746 #endif
747   if (running == NULL)
748     do_shutdown ();
749 }
750
751
752 /**
753  * Task run whenever it is time to restart a child that died.
754  *
755  * @param cls closure, always NULL
756  * @param tc context
757  */
758 static void
759 delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
760 {
761   struct ServiceList *pos;
762   struct GNUNET_TIME_Relative lowestRestartDelay;
763
764   child_restart_task = GNUNET_SCHEDULER_NO_TASK;
765   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
766     {
767       clean_up_running ();
768       if (NULL == running)
769         do_shutdown ();
770       return;
771     }
772   lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
773
774   /* check for services that need to be restarted due to
775      configuration changes or because the last restart failed */
776   pos = running;
777   while (pos != NULL)
778     {
779       if ( (pos->proc == NULL) && 
780            (GNUNET_YES != in_shutdown) )
781         {
782           if (GNUNET_TIME_absolute_get_remaining (pos->restartAt).rel_value == 0)
783             {
784               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
785                           _("Restarting service `%s'.\n"), pos->name);
786               start_process (pos, NULL);
787             }
788           else
789             {
790               lowestRestartDelay 
791                 = GNUNET_TIME_relative_min (lowestRestartDelay,
792                                             GNUNET_TIME_absolute_get_remaining
793                                             (pos->restartAt));
794             }
795         }
796       pos = pos->next;
797     }  
798   if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
799     {
800 #if DEBUG_ARM
801       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802                   "Will restart process in %llums\n",
803                   (unsigned long long) lowestRestartDelay.rel_value);
804 #endif
805       child_restart_task
806         = GNUNET_SCHEDULER_add_delayed (lowestRestartDelay,
807                                         &delayed_restart_task,
808                                         NULL);
809     }
810 }
811
812
813 /**
814  * Task triggered whenever we receive a SIGCHLD (child
815  * process died).  
816  *
817  * @param cls closure, NULL if we need to self-restart
818  * @param tc context
819  */
820 static void
821 maint_child_death (void *cls,
822                    const struct GNUNET_SCHEDULER_TaskContext *tc)
823 {
824   struct ServiceList *pos;
825   struct ServiceList *prev;
826   struct ServiceList *next;
827   const char *statstr;
828   int statcode;
829   int ret;
830   char c[16];
831   enum GNUNET_OS_ProcessStatusType statusType;
832   unsigned long statusCode;
833
834   child_death_task = GNUNET_SCHEDULER_NO_TASK;
835   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
836     {
837       child_death_task =
838         GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
839                                         &maint_child_death, NULL);
840       return;    
841     }
842   /* consume the signal */
843   GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
844
845   /* check for services that died (WAITPID) */
846   prev = NULL;
847   next = running;
848   while (NULL != (pos = next))
849     {
850       next = pos->next;
851       if (pos->proc == NULL) 
852         {
853           prev = pos;
854           continue;
855         }
856       if ((GNUNET_SYSERR == (ret = GNUNET_OS_process_status (pos->proc,
857                                                              &statusType,
858                                                              &statusCode))) ||
859           ( (ret == GNUNET_NO) ||
860             (statusType == GNUNET_OS_PROCESS_STOPPED) ||
861             (statusType == GNUNET_OS_PROCESS_RUNNING)) )
862         {
863           prev = pos;
864           continue;
865         }
866
867       if (statusType == GNUNET_OS_PROCESS_EXITED)
868         {
869           statstr = _( /* process termination method */ "exit");
870           statcode = statusCode;
871         }
872       else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
873         {
874           statstr = _( /* process termination method */ "signal");
875           statcode = statusCode;
876         }
877       else
878         {
879           statstr = _( /* process termination method */ "unknown");
880           statcode = 0;
881         }
882       GNUNET_OS_process_close (pos->proc);
883       pos->proc = NULL;
884       if (NULL != pos->killing_client) 
885         {
886           if (prev == NULL)
887             running = next;
888           else
889             prev->next = next;
890           GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
891                       _("Service `%s' stopped\n"),
892                       pos->name);
893           signal_result (pos->killing_client, 
894                          pos->name, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
895           GNUNET_SERVER_receive_done (pos->killing_client, GNUNET_OK);
896           GNUNET_SERVER_client_drop (pos->killing_client);
897           free_entry (pos);
898           continue;
899         }
900       if ( (GNUNET_YES != in_shutdown) &&
901            (0 == (tc->reason & GNUNET_SCHEDULER_REASON_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.rel_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 (child_restart_task);
915           child_restart_task 
916             = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
917                                                   &delayed_restart_task,
918                                                   NULL);
919         }
920 #if DEBUG_ARM
921       else
922         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
923                     "Service `%s' terminated with status %s/%d\n",
924                     pos->name, statstr, statcode);
925 #endif
926       prev = pos;
927     }
928   if (in_shutdown)
929     clean_up_running ();
930   if ( (running == NULL) &&
931        (in_shutdown) )
932     {
933       GNUNET_SERVER_destroy (server);
934     }
935   else
936     {
937       child_death_task =
938         GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
939                                         &maint_child_death, NULL);
940     }
941 }
942
943
944 static size_t
945 transmit_shutdown_ack (void *cls, size_t size, void *buf)
946 {
947   struct GNUNET_SERVER_Client *client = cls;
948   struct GNUNET_MessageHeader *msg;
949
950   if (size < sizeof (struct GNUNET_MessageHeader))
951     {
952       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
953                   _("Failed to transmit shutdown ACK.\n"));
954       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
955       return 0;                 /* client disconnected */
956     }
957
958   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
959               _("Transmitting shutdown ACK.\n"));
960
961   msg = (struct GNUNET_MessageHeader *) buf;
962   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK);
963   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
964   GNUNET_SERVER_receive_done (client, GNUNET_OK);
965   GNUNET_SERVER_client_drop(client);
966   return sizeof (struct GNUNET_MessageHeader);
967 }
968
969 /**
970  * Handler for SHUTDOWN message.
971  *
972  * @param cls closure (refers to service)
973  * @param client identification of the client
974  * @param message the actual message
975  */
976 static void
977 handle_shutdown (void *cls,
978                  struct GNUNET_SERVER_Client *client,
979                  const struct GNUNET_MessageHeader *message)
980 {
981   GNUNET_SERVER_client_keep(client);
982   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
983               _("Initiating shutdown as requested by client.\n"));
984
985   GNUNET_SERVER_notify_transmit_ready (client,
986                                        sizeof(struct GNUNET_MessageHeader),
987                                        GNUNET_TIME_UNIT_FOREVER_REL,
988                                        &transmit_shutdown_ack, client);
989   GNUNET_SERVER_client_persist_ (client);
990   GNUNET_SCHEDULER_shutdown ();
991 }
992
993
994 /**
995  * Signal handler called for SIGCHLD.  Triggers the
996  * respective handler by writing to the trigger pipe.
997  */
998 static void
999 sighandler_child_death ()
1000 {
1001   static char c;
1002   int old_errno = errno; /* back-up errno */
1003   GNUNET_break (1 == 
1004                 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
1005                                         (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
1006                                         sizeof (c)));
1007   errno = old_errno; /* restore errno */
1008 }
1009
1010
1011 /**
1012  * Process arm requests.
1013  *
1014  * @param cls closure
1015  * @param serv the initialized server
1016  * @param c configuration to use
1017  */
1018 static void
1019 run (void *cls,
1020      struct GNUNET_SERVER_Handle *serv,
1021      const struct GNUNET_CONFIGURATION_Handle *c)
1022 {
1023   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1024     {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
1025     {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
1026     {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
1027      sizeof (struct GNUNET_MessageHeader)},
1028     {NULL, NULL, 0, 0}
1029   };
1030   char *defaultservices;
1031   char *pos;
1032
1033   cfg = c;
1034   server = serv;
1035   GNUNET_assert (serv != 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 (GNUNET_TIME_UNIT_FOREVER_REL,
1040                                 &shutdown_task,
1041                                 NULL);
1042   child_death_task =
1043     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
1044                                     &maint_child_death, NULL);
1045
1046   if (GNUNET_OK !=
1047       GNUNET_CONFIGURATION_get_value_string (cfg,
1048                                              "ARM",
1049                                              "GLOBAL_PREFIX",
1050                                              &prefix_command))
1051     prefix_command = GNUNET_strdup ("");
1052   if (GNUNET_OK !=
1053       GNUNET_CONFIGURATION_get_value_string (cfg,
1054                                              "ARM",
1055                                              "GLOBAL_POSTFIX",
1056                                              &final_option))
1057     final_option = GNUNET_strdup ("");
1058   /* start default services... */
1059   if (GNUNET_OK ==
1060       GNUNET_CONFIGURATION_get_value_string (cfg,
1061                                              "ARM",
1062                                              "DEFAULTSERVICES",
1063                                              &defaultservices))
1064     {
1065 #if DEBUG_ARM
1066       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1067                   "Starting default services `%s'\n", defaultservices);
1068 #endif
1069       if (0 < strlen (defaultservices))
1070         {
1071           pos = strtok (defaultservices, " ");
1072           while (pos != NULL)
1073             {
1074               start_service (NULL, pos, NULL);
1075               pos = strtok (NULL, " ");
1076             }
1077         }
1078       GNUNET_free (defaultservices);
1079     }
1080   else
1081     {
1082 #if DEBUG_ARM
1083       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1084                   "No default services configured.\n");
1085 #endif
1086     }
1087
1088   /* create listening sockets for future services*/
1089   prepareServices (cfg);
1090   
1091   /* process client requests */
1092   GNUNET_SERVER_add_handlers (server, handlers);
1093
1094   /* manage services */
1095   GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1096                                       &config_change_task, NULL);
1097 }
1098
1099
1100 /**
1101  * The main function for the arm service.
1102  *
1103  * @param argc number of arguments from the command line
1104  * @param argv command line arguments
1105  * @return 0 ok, 1 on error
1106  */
1107 int
1108 main (int argc, char *const *argv)
1109 {
1110   int ret;
1111   struct GNUNET_SIGNAL_Context *shc_chld;
1112
1113   sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO);
1114   GNUNET_assert (sigpipe != NULL);
1115   shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
1116   ret = (GNUNET_OK ==
1117          GNUNET_SERVICE_run (argc,
1118                              argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1;
1119   GNUNET_SIGNAL_handler_uninstall (shc_chld);
1120   shc_chld = NULL;
1121   GNUNET_DISK_pipe_close (sigpipe);
1122   sigpipe = NULL;
1123   return ret;
1124 }
1125
1126 #ifdef LINUX
1127 #include <malloc.h>
1128
1129 /**
1130  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1131  */
1132 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1133 {
1134   mallopt (M_TRIM_THRESHOLD, 4*1024);
1135   mallopt (M_TOP_PAD, 1*1024);
1136   malloc_trim (0);
1137 }
1138 #endif
1139
1140
1141 /* end of gnunet-service-arm.c */