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