-docu
[oweals/gnunet.git] / src / arm / gnunet-service-arm.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011 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 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_arm_service.h"
29 #include "gnunet_protocols.h"
30 #include "arm.h"
31
32 /**
33  * Threshold after which exponential backoff shouldn't increase (in ms); 30m
34  */
35 #define EXPONENTIAL_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
36
37
38 /**
39  * List of our services.
40  */
41 struct ServiceList;
42
43
44 /**
45  * Record with information about a listen socket we have open.
46  */
47 struct ServiceListeningInfo
48 {
49   /**
50    * This is a linked list.
51    */
52   struct ServiceListeningInfo *next;
53
54   /**
55    * This is a linked list.
56    */
57   struct ServiceListeningInfo *prev;
58
59   /**
60    * Address this socket is listening on.
61    */
62   struct sockaddr *service_addr;
63
64   /**
65    * Service this listen socket is for.
66    */
67   struct ServiceList *sl;
68
69   /**
70    * Number of bytes in 'service_addr'
71    */
72   socklen_t service_addr_len;
73
74   /**
75    * Our listening socket.
76    */
77   struct GNUNET_NETWORK_Handle *listen_socket;
78
79   /**
80    * Task doing the accepting.
81    */
82   GNUNET_SCHEDULER_TaskIdentifier accept_task;
83
84 };
85
86
87 /**
88  * List of our services.
89  */
90 struct ServiceList
91 {
92   /**
93    * This is a doubly-linked list.
94    */
95   struct ServiceList *next;
96
97   /**
98    * This is a doubly-linked list.
99    */
100   struct ServiceList *prev;
101
102   /**
103    * Linked list of listen sockets associated with this service.
104    */
105   struct ServiceListeningInfo *listen_head;
106
107   /**
108    * Linked list of listen sockets associated with this service.
109    */
110   struct ServiceListeningInfo *listen_tail;
111
112   /**
113    * Name of the service.
114    */
115   char *name;
116
117   /**
118    * Name of the binary used.
119    */
120   char *binary;
121
122   /**
123    * Name of the configuration file used.
124    */
125   char *config;
126
127   /**
128    * Client to notify upon kill completion (waitpid), NULL
129    * if we should simply restart the process.
130    */
131   struct GNUNET_SERVER_Client *killing_client;
132
133   /**
134    * Process structure pointer of the child.
135    */
136   struct GNUNET_OS_Process *proc;
137
138   /**
139    * Process exponential backoff time
140    */
141   struct GNUNET_TIME_Relative backoff;
142
143   /**
144    * Absolute time at which the process is scheduled to restart in case of death
145    */
146   struct GNUNET_TIME_Absolute restart_at;
147
148   /**
149    * Is this service to be started by default (or did a client tell us explicitly
150    * to start it)?  GNUNET_NO if the service is started only upon 'accept' on a
151    * listen socket or possibly explicitly by a client changing the value.
152    */
153   int is_default;
154
155 };
156
157 /**
158  * List of running services.
159  */
160 static struct ServiceList *running_head;
161
162 /**
163  * List of running services.
164  */
165 static struct ServiceList *running_tail;
166
167 /**
168  * Our configuration
169  */
170 static const struct GNUNET_CONFIGURATION_Handle *cfg;
171
172 /**
173  * Command to prepend to each actual command.
174  */
175 static char *prefix_command;
176
177 /**
178  * Option to append to each actual command.
179  */
180 static char *final_option;
181
182 /**
183  * ID of task called whenever we get a SIGCHILD.
184  */
185 static GNUNET_SCHEDULER_TaskIdentifier child_death_task;
186
187 /**
188  * ID of task called whenever the timeout for restarting a child
189  * expires.
190  */
191 static GNUNET_SCHEDULER_TaskIdentifier child_restart_task;
192
193 /**
194  * Pipe used to communicate shutdown via signal.
195  */
196 static struct GNUNET_DISK_PipeHandle *sigpipe;
197
198 /**
199  * Are we in shutdown mode?
200  */
201 static int in_shutdown;
202
203 /**
204  * Handle to our server instance.  Our server is a bit special in that
205  * its service is not immediately stopped once we get a shutdown
206  * request (since we need to continue service until all of our child
207  * processes are dead).  This handle is used to shut down the server
208  * (and thus trigger process termination) once all child processes are
209  * also dead.  A special option in the ARM configuration modifies the
210  * behaviour of the service implementation to not do the shutdown
211  * immediately.
212  */
213 static struct GNUNET_SERVER_Handle *server;
214
215
216 #include "do_start_process.c"
217
218
219 /**
220  * Actually start the process for the given service.
221  *
222  * @param sl identifies service to start
223  */
224 static void
225 start_process (struct ServiceList *sl)
226 {
227   char *loprefix;
228   char *options;
229   char *optpos;
230   char *optend;
231   const char *next;
232   int use_debug;
233   char b;
234   char *val;
235   struct ServiceListeningInfo *sli;
236   SOCKTYPE *lsocks;
237   unsigned int ls;
238
239   /* calculate listen socket list */
240   lsocks = NULL;
241   ls = 0;
242   for (sli = sl->listen_head; NULL != sli; sli = sli->next)
243     {
244       GNUNET_array_append (lsocks, ls,
245                            GNUNET_NETWORK_get_fd (sli->listen_socket));
246       if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK)
247         {
248           GNUNET_SCHEDULER_cancel (sli->accept_task);
249           sli->accept_task = GNUNET_SCHEDULER_NO_TASK;
250         }
251     }
252 #if WINDOWS
253   GNUNET_array_append (lsocks, ls, INVALID_SOCKET);
254 #else
255   GNUNET_array_append (lsocks, ls, -1);
256 #endif
257
258   /* obtain configuration */
259   if (GNUNET_OK !=
260       GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX",
261                                              &loprefix))
262     loprefix = GNUNET_strdup (prefix_command);
263   if (GNUNET_OK !=
264       GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS",
265                                              &options))
266     {
267       options = GNUNET_strdup (final_option);
268       if (NULL == strstr (options, "%"))
269         {
270           /* replace '{}' with service name */
271           while (NULL != (optpos = strstr (options, "{}")))
272             {
273               optpos[0] = '%';
274               optpos[1] = 's';
275               GNUNET_asprintf (&optpos, options, sl->name);
276               GNUNET_free (options);
277               options = optpos;
278             }
279           /* replace '$PATH' with value associated with "PATH" */
280           while (NULL != (optpos = strstr (options, "$")))
281             {
282               optend = optpos + 1;
283               while (isupper ((unsigned char) *optend))
284                 optend++;
285               b = *optend;
286               if ('\0' == b)
287                 next = "";
288               else
289                 next = optend + 1;
290               *optend = '\0';
291               if (GNUNET_OK !=
292                   GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS",
293                                                          optpos + 1, &val))
294                 val = GNUNET_strdup ("");
295               *optpos = '\0';
296               GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next);
297               GNUNET_free (options);
298               GNUNET_free (val);
299               options = optpos;
300             }
301         }
302     }
303   use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
304
305   /* actually start process */
306 #if DEBUG_ARM
307   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308               "Starting service `%s' using binary `%s' and configuration `%s'\n",
309               sl->name, sl->binary, sl->config);
310 #endif
311   GNUNET_assert (NULL == sl->proc);
312   if (GNUNET_YES == use_debug)
313     sl->proc =
314       do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
315                         "DEBUG", options, NULL);
316   else
317     sl->proc =
318       do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config,
319                         options, NULL);
320   if (sl->proc == NULL)
321     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
322                 sl->name);
323   else
324     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"),
325                 sl->name);
326   /* clean up */
327   GNUNET_free (loprefix);
328   GNUNET_free (options);
329   GNUNET_array_grow (lsocks, ls, 0);
330 }
331
332
333 /**
334  * Transmit a status result message.
335  *
336  * @param cls pointer to "unit16_t*" with message type
337  * @param size number of bytes available in buf
338  * @param buf where to copy the message, NULL on error
339  * @return number of bytes copied to buf
340  */
341 static size_t
342 write_result (void *cls, size_t size, void *buf)
343 {
344   enum GNUNET_ARM_ProcessStatus *res = cls;
345   struct GNUNET_ARM_ResultMessage *msg;
346
347   if (buf == NULL)
348   {
349     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
350                 _("Could not send status result to client\n"));
351     return 0;                   /* error, not much we can do */
352   }
353 #if DEBUG_ARM
354   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355               "Sending status response %u to client\n", (unsigned int) *res);
356 #endif
357   GNUNET_assert (size >= sizeof (struct GNUNET_ARM_ResultMessage));
358   msg = buf;
359   msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage));
360   msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT);
361   msg->status = htonl ((uint32_t) (*res));
362   GNUNET_free (res);
363   return sizeof (struct GNUNET_ARM_ResultMessage);
364 }
365
366
367 /**
368  * Signal our client that we will start or stop the
369  * service.
370  *
371  * @param client who is being signalled
372  * @param name name of the service
373  * @param result message type to send
374  * @return NULL if it was not found
375  */
376 static void
377 signal_result (struct GNUNET_SERVER_Client *client, const char *name,
378                enum GNUNET_ARM_ProcessStatus result)
379 {
380   enum GNUNET_ARM_ProcessStatus *res;
381
382   if (NULL == client)
383     return;
384   /* FIXME: this is not super-clean yet... */
385   res = GNUNET_malloc (sizeof (enum GNUNET_ARM_ProcessStatus));
386   *res = result;
387   GNUNET_SERVER_notify_transmit_ready (client,
388                                        sizeof (struct
389                                                GNUNET_ARM_ResultMessage),
390                                        GNUNET_TIME_UNIT_FOREVER_REL,
391                                        &write_result, res);
392   GNUNET_SERVER_receive_done (client, GNUNET_OK);
393 }
394
395
396 /**
397  * Find the process with the given service
398  * name in the given list and return it.
399  *
400  * @param name which service entry to look up
401  * @return NULL if it was not found
402  */
403 static struct ServiceList *
404 find_service (const char *name)
405 {
406   struct ServiceList *sl;
407
408   sl = running_head;
409   while (sl != NULL)
410     {
411       if (0 == strcasecmp (sl->name, name))
412         return sl;
413       sl = sl->next;
414     }
415   return NULL;
416 }
417
418
419 /**
420  * First connection has come to the listening socket associated with the service,
421  * create the service in order to relay the incoming connection to it
422  *
423  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
424  * @param tc context
425  */
426 static void
427 accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
428 {
429   struct ServiceListeningInfo *sli = cls;
430   struct ServiceList *sl = sli->sl;
431
432   sli->accept_task = GNUNET_SCHEDULER_NO_TASK;
433   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
434     return;
435   start_process (sl);
436 }
437
438
439 /**
440  * Creating a listening socket for each of the service's addresses and
441  * wait for the first incoming connection to it
442  *
443  * @param sa address associated with the service
444  * @param addr_len length of sa
445  * @param sl service entry for the service in question
446  */
447 static void
448 create_listen_socket (struct sockaddr *sa, socklen_t addr_len,
449                       struct ServiceList *sl)
450 {
451   const static int on = 1;
452   struct GNUNET_NETWORK_Handle *sock;
453   struct ServiceListeningInfo *sli;
454
455   switch (sa->sa_family)
456     {
457     case AF_INET:
458       sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
459       break;
460     case AF_INET6:
461       sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
462       break;
463     case AF_UNIX:
464       if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */
465         return;
466       sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
467       break;
468     default:
469       GNUNET_break (0);
470       sock = NULL;
471       errno = EAFNOSUPPORT;
472       break;
473     }
474   if (NULL == sock)
475     {
476       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
477                   _("Unable to create socket for service `%s': %s\n"),
478                   sl->name, STRERROR (errno));
479       GNUNET_free (sa);
480       return;
481     }
482   if (GNUNET_NETWORK_socket_setsockopt
483       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
484     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
485                          "setsockopt");
486 #ifdef IPV6_V6ONLY
487   if ((sa->sa_family == AF_INET6) &&
488       (GNUNET_NETWORK_socket_setsockopt
489        (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
490     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
491                          "setsockopt");
492 #endif
493
494   if (GNUNET_NETWORK_socket_bind
495       (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK)
496     {
497       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
498                   _
499                   ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
500                   sl->name, GNUNET_a2s (sa, addr_len), STRERROR (errno));
501       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
502       GNUNET_free (sa);
503       return;
504     }
505   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
506     {
507       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
508       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
509       GNUNET_free (sa);
510       return;
511     }
512   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
513               _("ARM now monitors connections to service `%s' at `%s'\n"),
514               sl->name, GNUNET_a2s (sa, addr_len));
515   sli = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
516   sli->service_addr = sa;
517   sli->service_addr_len = addr_len;
518   sli->listen_socket = sock;
519   sli->sl = sl;
520   sli->accept_task =
521     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
522                                    &accept_connection, sli);
523   GNUNET_CONTAINER_DLL_insert (sl->listen_head, sl->listen_tail, sli);
524 }
525
526
527 /**
528  * Remove and free an entry in the service list.  Listen sockets
529  * must have already been cleaned up.  Only to be called during shutdown.
530  *
531  * @param sl entry to free
532  */
533 static void
534 free_service (struct ServiceList *sl)
535 {
536   GNUNET_assert (GNUNET_YES == in_shutdown);
537   GNUNET_CONTAINER_DLL_remove (running_head, running_tail, sl);
538   GNUNET_assert (NULL == sl->listen_head);
539   GNUNET_free_non_null (sl->config);
540   GNUNET_free_non_null (sl->binary);
541   GNUNET_free (sl->name);
542   GNUNET_free (sl);
543 }
544
545
546 /**
547  * Handle START-message.
548  *
549  * @param cls closure (always NULL)
550  * @param client identification of the client
551  * @param message the actual message
552  * @return GNUNET_OK to keep the connection open,
553  *         GNUNET_SYSERR to close it (signal serious error)
554  */
555 static void
556 handle_start (void *cls, struct GNUNET_SERVER_Client *client,
557               const struct GNUNET_MessageHeader *message)
558 {
559   const char *servicename;
560   struct ServiceList *sl;
561   uint16_t size;
562
563   size = ntohs (message->size);
564   size -= sizeof (struct GNUNET_MessageHeader);
565   servicename = (const char *) &message[1];
566   if ((size == 0) || (servicename[size - 1] != '\0'))
567     {
568       GNUNET_break (0);
569       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
570       return;
571     }
572   if (GNUNET_YES == in_shutdown)
573     {
574       signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN);
575       return;
576     }
577   sl = find_service (servicename);
578   if (NULL == sl)
579     {
580       signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN);
581       return;
582     }
583   sl->is_default = GNUNET_YES;
584   if (sl->proc != NULL)
585     {
586       signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_RUNNING);
587       return;
588     }
589   start_process (sl);
590   signal_result (client, servicename, GNUNET_ARM_PROCESS_STARTING);
591 }
592
593
594 /**
595  * Handle STOP-message.
596  *
597  * @param cls closure (always NULL)
598  * @param client identification of the client
599  * @param message the actual message
600  * @return GNUNET_OK to keep the connection open,
601  *         GNUNET_SYSERR to close it (signal serious error)
602  */
603 static void
604 handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
605              const struct GNUNET_MessageHeader *message)
606 {
607   struct ServiceList *sl;
608   const char *servicename;
609   uint16_t size;
610
611   size = ntohs (message->size);
612   size -= sizeof (struct GNUNET_MessageHeader);
613   servicename = (const char *) &message[1];
614   if ((size == 0) || (servicename[size - 1] != '\0'))
615     {
616       GNUNET_break (0);
617       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
618       return;
619     }
620   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
621               _("Preparing to stop `%s'\n"), servicename);
622   sl = find_service (servicename);
623   if (sl == NULL)
624     {
625       signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN);
626       return;
627     }
628   sl->is_default = GNUNET_NO;
629   if (GNUNET_YES == in_shutdown)
630     {
631       /* shutdown in progress */
632       signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN);
633       return;
634     }
635   if (sl->killing_client != NULL)
636     {
637       /* killing already in progress */
638       signal_result (client, servicename,
639                      GNUNET_ARM_PROCESS_ALREADY_STOPPING);
640       return;
641     }
642   if (sl->proc == NULL)
643     {
644       /* process is down */
645       signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_DOWN);
646       return;
647     }
648 #if DEBUG_ARM
649   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
650               "Sending kill signal to service `%s', waiting for process to die.\n",
651               servicename);
652 #endif
653   if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM))
654     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
655   sl->killing_client = client;
656   GNUNET_SERVER_client_keep (client);
657 }
658
659
660 /**
661  * We are done with everything.  Stop remaining
662  * tasks, signal handler and the server.
663  */
664 static void
665 do_shutdown ()
666 {
667   if (NULL != server)
668     {
669       GNUNET_SERVER_destroy (server);
670       server = NULL;
671     }
672   if (GNUNET_SCHEDULER_NO_TASK != child_death_task)
673     {
674       GNUNET_SCHEDULER_cancel (child_death_task);
675       child_death_task = GNUNET_SCHEDULER_NO_TASK;
676     }
677 }
678
679
680 /**
681  * Task run for shutdown.
682  *
683  * @param cls closure, NULL if we need to self-restart
684  * @param tc context
685  */
686 static void
687 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
688 {
689   struct ServiceList *pos;
690   struct ServiceList *nxt;
691   struct ServiceListeningInfo *sli;
692
693   if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
694     {
695       GNUNET_SCHEDULER_cancel (child_restart_task);
696       child_restart_task = GNUNET_SCHEDULER_NO_TASK;
697     }
698   in_shutdown = GNUNET_YES;
699   /* first, stop listening */
700   for (pos = running_head; NULL != pos; pos = pos->next)
701     {
702       while (NULL != (sli = pos->listen_head))
703         {
704           GNUNET_CONTAINER_DLL_remove (pos->listen_head,
705                                        pos->listen_tail, sli);
706           if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK)
707             {
708               GNUNET_SCHEDULER_cancel (sli->accept_task);
709               sli->accept_task = GNUNET_SCHEDULER_NO_TASK;
710             }
711           GNUNET_break (GNUNET_OK ==
712                         GNUNET_NETWORK_socket_close (sli->listen_socket));
713           GNUNET_free (sli->service_addr);
714           GNUNET_free (sli);
715         }
716     }
717   /* then, shutdown all existing service processes */
718   nxt = running_head;
719   while (NULL != (pos = nxt))
720     {
721       nxt = pos->next;
722       if (pos->proc != NULL)
723         {
724           GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n",
725                       pos->name);
726           if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
727             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
728         }
729       else
730         {
731           free_service (pos);
732         }
733     }
734   /* finally, should all service processes be already gone, terminate for real */
735   if (running_head == NULL)
736     do_shutdown ();
737 }
738
739
740 /**
741  * Task run whenever it is time to restart a child that died.
742  *
743  * @param cls closure, always NULL
744  * @param tc context
745  */
746 static void
747 delayed_restart_task (void *cls,
748                       const struct GNUNET_SCHEDULER_TaskContext *tc)
749 {
750   struct ServiceList *sl;
751   struct GNUNET_TIME_Relative lowestRestartDelay;
752   struct ServiceListeningInfo *sli;
753
754   child_restart_task = GNUNET_SCHEDULER_NO_TASK;
755   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
756     return;
757   GNUNET_assert (GNUNET_NO == in_shutdown);
758   lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
759
760   /* check for services that need to be restarted due to
761    * configuration changes or because the last restart failed */
762   for (sl = running_head; NULL != sl; sl = sl->next)
763     {
764       if (sl->proc == NULL)
765         {
766           /* service is currently not running */
767           if (GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value ==
768               0)
769             {
770               /* restart is now allowed */
771               if (sl->is_default)
772                 {
773                   /* process should run by default, start immediately */
774                   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
775                               _("Restarting service `%s'.\n"), sl->name);
776                   start_process (sl);
777                 }
778               else
779                 {
780                   /* process is run on-demand, ensure it is re-started if there is demand */
781                   for (sli = sl->listen_head; NULL != sli; sli = sli->next)
782                     if (GNUNET_SCHEDULER_NO_TASK == sli->accept_task)
783                       {
784                         /* accept was actually paused, so start it again */
785                         sli->accept_task =
786                           GNUNET_SCHEDULER_add_read_net
787                           (GNUNET_TIME_UNIT_FOREVER_REL, sli->listen_socket,
788                            &accept_connection, sli);
789                       }
790                 }
791             }
792           else
793             {
794               /* update calculation for earliest time to reactivate a service */
795               lowestRestartDelay =
796                 GNUNET_TIME_relative_min (lowestRestartDelay,
797                                           GNUNET_TIME_absolute_get_remaining
798                                           (sl->restart_at));
799             }
800         }
801     }
802   if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
803     {
804 #if DEBUG_ARM
805       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %llums\n",
806                   (unsigned long long) lowestRestartDelay.rel_value);
807 #endif
808       child_restart_task =
809         GNUNET_SCHEDULER_add_delayed_with_priority (lowestRestartDelay,
810                                                     GNUNET_SCHEDULER_PRIORITY_IDLE, 
811                                                     &delayed_restart_task, NULL);
812     }
813 }
814
815
816 /**
817  * Task triggered whenever we receive a SIGCHLD (child
818  * process died).
819  *
820  * @param cls closure, NULL if we need to self-restart
821  * @param tc context
822  */
823 static void
824 maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
825 {
826   struct ServiceList *pos;
827   struct ServiceList *next;
828   struct ServiceListeningInfo *sli;
829   const char *statstr;
830   int statcode;
831   int ret;
832   char c[16];
833   enum GNUNET_OS_ProcessStatusType statusType;
834   unsigned long statusCode;
835   const struct GNUNET_DISK_FileHandle *pr;
836
837   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
838   child_death_task = GNUNET_SCHEDULER_NO_TASK;
839   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
840     {
841       /* shutdown scheduled us, ignore! */
842       child_death_task =
843         GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
844                                         pr, &maint_child_death, NULL);
845       return;
846     }
847   /* consume the signal */
848   GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
849
850   /* check for services that died (WAITPID) */
851   next = running_head;
852   while (NULL != (pos = next))
853     {
854       next = pos->next;
855
856       if (pos->proc == NULL)
857         {
858           if (GNUNET_YES == in_shutdown)
859             free_service (pos);
860           continue;
861         }
862       if ((GNUNET_SYSERR ==
863            (ret =
864             GNUNET_OS_process_status (pos->proc, &statusType, &statusCode)))
865           || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED)
866               || (statusType == GNUNET_OS_PROCESS_RUNNING)))
867         continue;
868       if (statusType == GNUNET_OS_PROCESS_EXITED)
869         {
870           statstr = _( /* process termination method */ "exit");
871           statcode = statusCode;
872         }
873       else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
874         {
875           statstr = _( /* process termination method */ "signal");
876           statcode = statusCode;
877         }
878       else
879         {
880           statstr = _( /* process termination method */ "unknown");
881           statcode = 0;
882         }
883       GNUNET_OS_process_close (pos->proc);
884       pos->proc = NULL;
885       if (NULL != pos->killing_client)
886         {
887           signal_result (pos->killing_client, pos->name,
888                          GNUNET_ARM_PROCESS_DOWN);
889           GNUNET_SERVER_client_drop (pos->killing_client);
890           pos->killing_client = NULL;
891           /* process can still be re-started on-demand, ensure it is re-started if there is demand */
892           for (sli = pos->listen_head; NULL != sli; sli = sli->next)
893             {
894               GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task);
895               sli->accept_task =
896                 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
897                                                sli->listen_socket,
898                                                &accept_connection, sli);
899             }
900           continue;
901         }
902       if (GNUNET_YES != in_shutdown)
903         {
904           if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0))
905             {
906               /* process terminated normally, allow restart at any time */
907               pos->restart_at.abs_value = 0;
908             }
909           else
910             {
911               if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
912                 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
913                             _
914                             ("Service `%s' terminated with status %s/%d, will restart in %llu ms\n"),
915                             pos->name, statstr, statcode, pos->backoff.rel_value);
916               /* schedule restart */
917               pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
918               pos->backoff =
919                 GNUNET_TIME_relative_min (EXPONENTIAL_BACKOFF_THRESHOLD,
920                                           GNUNET_TIME_relative_multiply
921                                           (pos->backoff, 2));
922             }
923           if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
924             GNUNET_SCHEDULER_cancel (child_restart_task);
925           child_restart_task =
926             GNUNET_SCHEDULER_add_with_priority
927             (GNUNET_SCHEDULER_PRIORITY_IDLE, 
928              &delayed_restart_task, NULL);
929         }
930       else
931         {
932           free_service (pos);
933         }
934     }
935   child_death_task =
936     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
937                                     pr, &maint_child_death, NULL);
938   if ((NULL == running_head) && (GNUNET_YES == in_shutdown))
939     do_shutdown ();
940 }
941
942
943 /**
944  * Transmit our shutdown acknowledgement to the client.
945  *
946  * @param cls the 'struct GNUNET_SERVER_Client'
947  * @param size number of bytes available in buf
948  * @param buf where to write the message
949  * @return number of bytes written
950  */
951 static size_t
952 transmit_shutdown_ack (void *cls, size_t size, void *buf)
953 {
954   struct GNUNET_SERVER_Client *client = cls;
955   struct GNUNET_ARM_ResultMessage *msg;
956
957   if (size < sizeof (struct GNUNET_ARM_ResultMessage))
958     {
959       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
960                   _("Failed to transmit shutdown ACK.\n"));
961       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
962       return 0;                 /* client disconnected */
963     }
964   /* Make the connection flushing for the purpose of ACK transmitting,
965    * needed on W32 to ensure that the message is even received, harmless
966    * on other platforms... */
967   GNUNET_break (GNUNET_OK == GNUNET_SERVER_client_disable_corking (client));
968   msg = (struct GNUNET_ARM_ResultMessage *) buf;
969   msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT);
970   msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage));
971   msg->status = htonl ((uint32_t) GNUNET_ARM_PROCESS_SHUTDOWN);
972   GNUNET_SERVER_receive_done (client, GNUNET_OK);
973   GNUNET_SERVER_client_drop (client);
974   return sizeof (struct GNUNET_ARM_ResultMessage);
975 }
976
977
978 /**
979  * Handler for SHUTDOWN message.
980  *
981  * @param cls closure (refers to service)
982  * @param client identification of the client
983  * @param message the actual message
984  */
985 static void
986 handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client,
987                  const struct GNUNET_MessageHeader *message)
988 {
989   GNUNET_SCHEDULER_shutdown ();
990   GNUNET_SERVER_client_keep (client);
991   GNUNET_SERVER_notify_transmit_ready (client,
992                                        sizeof (struct GNUNET_ARM_ResultMessage),
993                                        GNUNET_TIME_UNIT_FOREVER_REL,
994                                        &transmit_shutdown_ack, client);
995   GNUNET_SERVER_client_persist_ (client);
996 }
997
998
999 /**
1000  * Signal handler called for SIGCHLD.  Triggers the
1001  * respective handler by writing to the trigger pipe.
1002  */
1003 static void
1004 sighandler_child_death ()
1005 {
1006   static char c;
1007   int old_errno = errno;        /* back-up errno */
1008
1009   GNUNET_break (1 ==
1010                 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
1011                                         (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
1012                                         &c, sizeof (c)));
1013   errno = old_errno;            /* restore errno */
1014 }
1015
1016
1017 /**
1018  * Setup our service record for the given section in the configuration file
1019  * (assuming the section is for a service).
1020  *
1021  * @param cls unused
1022  * @param section a section in the configuration file
1023  * @return GNUNET_OK (continue)
1024  */
1025 static void
1026 setup_service (void *cls, const char *section)
1027 {
1028   struct ServiceList *sl;
1029   char *binary;
1030   char *config;
1031   struct stat sbuf;
1032   struct sockaddr **addrs;
1033   socklen_t *addr_lens;
1034   int ret;
1035   unsigned int i;
1036
1037   if (strcasecmp (section, "arm") == 0)
1038     return;
1039   if (GNUNET_OK !=
1040       GNUNET_CONFIGURATION_get_value_string (cfg, section, "BINARY", &binary))
1041     {
1042       /* not a service section */
1043       return;
1044     }
1045   sl = find_service (section);
1046   if (NULL != sl)
1047   {
1048     /* got the same section twice!? */
1049     GNUNET_break (0);
1050     return;
1051   }
1052   config = NULL;
1053   if ((GNUNET_OK !=
1054        GNUNET_CONFIGURATION_get_value_filename (cfg, section, "CONFIG",
1055                                                 &config)) ||
1056       (0 != STAT (config, &sbuf)))
1057     {
1058       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1059                   _
1060                   ("Configuration file `%s' for service `%s' not valid: %s\n"),
1061                   config, section,
1062                   (config == NULL) ? _("option missing") : STRERROR (errno));
1063       GNUNET_free (binary);
1064       GNUNET_free_non_null (config);
1065       return;
1066     }
1067   sl = GNUNET_malloc (sizeof (struct ServiceList));
1068   sl->name = GNUNET_strdup (section);
1069   sl->binary = binary;
1070   sl->config = config;
1071   sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
1072   sl->restart_at = GNUNET_TIME_UNIT_FOREVER_ABS;
1073   GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl);
1074   if (GNUNET_YES !=
1075       GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "AUTOSTART"))
1076     return;
1077   if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg,
1078                                                        &addrs, &addr_lens)))
1079     return;
1080   /* this will free (or capture) addrs[i] */
1081   for (i = 0; i < ret; i++)
1082     create_listen_socket (addrs[i], addr_lens[i], sl);
1083   GNUNET_free (addrs);
1084   GNUNET_free (addr_lens);
1085 }
1086
1087
1088 /**
1089  * Process arm requests.
1090  *
1091  * @param cls closure
1092  * @param serv the initialized server
1093  * @param c configuration to use
1094  */
1095 static void
1096 run (void *cls, struct GNUNET_SERVER_Handle *serv,
1097      const struct GNUNET_CONFIGURATION_Handle *c)
1098 {
1099   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1100     {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
1101     {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
1102     {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
1103      sizeof (struct GNUNET_MessageHeader)},
1104     {NULL, NULL, 0, 0}
1105   };
1106   char *defaultservices;
1107   const char *pos;
1108   struct ServiceList *sl;
1109
1110   cfg = c;
1111   server = serv;
1112   GNUNET_assert (serv != NULL);
1113   GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES);
1114   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1115                                 NULL);
1116   child_death_task =
1117     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1118                                     GNUNET_DISK_pipe_handle (sigpipe,
1119                                                              GNUNET_DISK_PIPE_END_READ),
1120                                     &maint_child_death, NULL);
1121
1122   if (GNUNET_OK !=
1123       GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_PREFIX",
1124                                              &prefix_command))
1125     prefix_command = GNUNET_strdup ("");
1126   if (GNUNET_OK !=
1127       GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_POSTFIX",
1128                                              &final_option))
1129     final_option = GNUNET_strdup ("");
1130
1131   GNUNET_CONFIGURATION_iterate_sections (cfg, &setup_service, NULL);
1132
1133   /* start default services... */
1134   if (GNUNET_OK ==
1135       GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "DEFAULTSERVICES",
1136                                              &defaultservices))
1137     {
1138       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1139                   _("Starting default services `%s'\n"), defaultservices);
1140       if (0 < strlen (defaultservices))
1141         {
1142           for (pos = strtok (defaultservices, " "); NULL != pos;
1143                pos = strtok (NULL, " "))
1144             {
1145               sl = find_service (pos);
1146               if (NULL == sl)
1147                 {
1148                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1149                               _
1150                               ("Default service `%s' not configured correctly!\n"),
1151                               pos);
1152                   continue;
1153                 }
1154               sl->is_default = GNUNET_YES;
1155               start_process (sl);
1156             }
1157         }
1158       GNUNET_free (defaultservices);
1159     }
1160   else
1161     {
1162       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1163                   _
1164                   ("No default services configured, GNUnet will not really start right now.\n"));
1165     }
1166
1167   /* process client requests */
1168   GNUNET_SERVER_add_handlers (server, handlers);
1169 }
1170
1171
1172 /**
1173  * The main function for the arm service.
1174  *
1175  * @param argc number of arguments from the command line
1176  * @param argv command line arguments
1177  * @return 0 ok, 1 on error
1178  */
1179 int
1180 main (int argc, char *const *argv)
1181 {
1182   int ret;
1183   struct GNUNET_SIGNAL_Context *shc_chld;
1184
1185   sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
1186   GNUNET_assert (sigpipe != NULL);
1187   shc_chld =
1188     GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
1189   ret =
1190     (GNUNET_OK ==
1191      GNUNET_SERVICE_run (argc, argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1;
1192   GNUNET_SIGNAL_handler_uninstall (shc_chld);
1193   shc_chld = NULL;
1194   GNUNET_DISK_pipe_close (sigpipe);
1195   sigpipe = NULL;
1196   return ret;
1197 }
1198
1199
1200 #ifdef LINUX
1201 #include <malloc.h>
1202
1203 /**
1204  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1205  */
1206 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1207 {
1208   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1209   mallopt (M_TOP_PAD, 1 * 1024);
1210   malloc_trim (0);
1211 }
1212 #endif
1213
1214
1215 /* end of gnunet-service-arm.c */