74bbd76c2b55e5865d1f55a5561645c00171cdb2
[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 "arm.h"
39
40
41 /**
42  * Check for configuration file changes every 5s.
43  */
44 #define MAINT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
45
46 /**
47  * Threshold after which exponential backoff shouldn't increase (in ms); 30m
48  */
49 #define EXPONENTIAL_BACKOFF_THRESHOLD (1000 * 60 * 30)
50
51 /**
52  * List of our services.
53  */
54 struct ServiceList;
55
56
57 /**
58  * List of our services.
59  */
60 struct ServiceList
61 {
62   /**
63    * This is a doubly-linked list.
64    */
65   struct ServiceList *next;
66
67   /**
68    * This is a doubly-linked list.
69    */
70   struct ServiceList *prev;
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_head;
120
121 /**
122  * List of running services.
123  */
124 static struct ServiceList *running_tail;
125
126 /**
127  * Our configuration
128  */
129 static const struct GNUNET_CONFIGURATION_Handle *cfg;
130
131 /**
132  * Command to prepend to each actual command.
133  */
134 static char *prefix_command;
135
136 /**
137  * Option to append to each actual command.
138  */
139 static char *final_option;
140
141 /**
142  * ID of task called whenever we get a SIGCHILD.
143  */
144 static GNUNET_SCHEDULER_TaskIdentifier child_death_task;
145
146 /**
147  * ID of task called whenever the timeout for restarting a child
148  * expires.
149  */
150 static GNUNET_SCHEDULER_TaskIdentifier child_restart_task;
151
152 /**
153  *
154  */
155 struct ServiceListeningInfo
156 {
157   /**
158    * This is a linked list.
159    */
160   struct ServiceListeningInfo *next;
161
162   /**
163    * This is a linked list.
164    */
165   struct ServiceListeningInfo *prev;
166
167   /**
168    * Name of the service being forwarded.
169    */
170   char *serviceName;
171
172   /**
173    *
174    */
175   struct sockaddr *service_addr;
176
177   /**
178    *
179    */
180   socklen_t service_addr_len;
181
182   /**
183    * Our listening socket.
184    */
185   struct GNUNET_NETWORK_Handle *listeningSocket;
186
187   /**
188    * Task doing the accepting.
189    */
190   GNUNET_SCHEDULER_TaskIdentifier acceptTask;
191 };
192
193
194 /**
195  * Array with the names of the services started by default.
196  */
197 static char **defaultServicesList;
198
199 /**
200  * Size of the defaultServicesList array.
201  */
202 static unsigned int numDefaultServices;
203
204 /**
205  *
206  */
207 static struct ServiceListeningInfo *serviceListeningInfoList_head;
208
209 /**
210  *
211  */
212 static struct ServiceListeningInfo *serviceListeningInfoList_tail;
213
214
215 /**
216  * Pipe used to communicate shutdown via signal.
217  */
218 static struct GNUNET_DISK_PipeHandle *sigpipe;
219
220 /**
221  * Reading end of the signal pipe.
222  */
223 static const struct GNUNET_DISK_FileHandle *pr;
224
225 /**
226  * Are we in shutdown mode?
227  */
228 static int in_shutdown;
229
230
231 /**
232  * Handle to our server instance.  Our server is a bit special in that
233  * its service is not immediately stopped once we get a shutdown
234  * request (since we need to continue service until all of our child
235  * processes are dead).  This handle is used to shut down the server
236  * (and thus trigger process termination) once all child processes are
237  * also dead.  A special option in the ARM configuration modifies the
238  * behaviour of the service implementation to not do the shutdown
239  * immediately.
240  */
241 static struct GNUNET_SERVER_Handle *server;
242
243
244 #include "do_start_process.c"
245
246
247 /**
248  * Actually start the process for the given service.
249  *
250  * @param sl identifies service to start
251  * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
252  */
253 static void
254 start_process (struct ServiceList *sl, const SOCKTYPE *lsocks)
255 {
256   char *loprefix;
257   char *options;
258   char *optpos;
259   char *optend;
260   const char *next;
261   int use_debug;
262   char b;
263   char *val;
264
265   /* start service */
266   if (GNUNET_OK !=
267       GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX",
268                                              &loprefix))
269     loprefix = GNUNET_strdup (prefix_command);
270   if (GNUNET_OK !=
271       GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS",
272                                              &options))
273   {
274     options = GNUNET_strdup (final_option);
275     if (NULL == strstr (options, "%"))
276     {
277       /* replace '{}' with service name */
278       while (NULL != (optpos = strstr (options, "{}")))
279       {
280         optpos[0] = '%';
281         optpos[1] = 's';
282         GNUNET_asprintf (&optpos, options, sl->name);
283         GNUNET_free (options);
284         options = optpos;
285       }
286       /* replace '$PATH' with value associated with "PATH" */
287       while (NULL != (optpos = strstr (options, "$")))
288       {
289         optend = optpos + 1;
290         while (isupper ((unsigned char) *optend))
291           optend++;
292         b = *optend;
293         if ('\0' == b)
294           next = "";
295         else
296           next = optend + 1;
297         *optend = '\0';
298         if (GNUNET_OK !=
299             GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", optpos + 1,
300                                                    &val))
301           val = GNUNET_strdup ("");
302         *optpos = '\0';
303         GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next);
304         GNUNET_free (options);
305         GNUNET_free (val);
306         options = optpos;
307       }
308     }
309   }
310   use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG");
311
312 #if DEBUG_ARM
313   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314               "Starting service `%s' using binary `%s' and configuration `%s'\n",
315               sl->name, sl->binary, sl->config);
316 #endif
317   if (GNUNET_YES == use_debug)
318     sl->proc =
319         do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L",
320                           "DEBUG", options, NULL);
321   else
322     sl->proc =
323         do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config,
324                           options, NULL);
325   if (sl->proc == NULL)
326     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
327                 sl->name);
328   else
329     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name);
330   GNUNET_free (loprefix);
331   GNUNET_free (options);
332 }
333
334
335 /**
336  * Put the default services represented by a space separated string into an array of strings
337  *
338  * @param services space separated string of default services
339  */
340 static void
341 addDefaultServicesToList (const char *services)
342 {
343   unsigned int i;
344   const char *token;
345   char *s;
346
347   if (strlen (services) == 0)
348     return;
349   s = GNUNET_strdup (services);
350   token = strtok (s, " ");
351   while (NULL != token)
352   {
353     numDefaultServices++;
354     token = strtok (NULL, " ");
355   }
356   GNUNET_free (s);
357
358   defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *));
359   i = 0;
360   s = GNUNET_strdup (services);
361   token = strtok (s, " ");
362   while (NULL != token)
363   {
364     defaultServicesList[i++] = GNUNET_strdup (token);
365     token = strtok (NULL, " ");
366   }
367   GNUNET_free (s);
368   GNUNET_assert (i == numDefaultServices);
369 }
370
371
372 /**
373  * Checks whether the serviceName is in the list of default services
374  *
375  * @param serviceName string to check its existance in the list
376  * @return GNUNET_YES if the service is started by default
377  */
378 static int
379 isInDefaultList (const char *serviceName)
380 {
381   unsigned int i;
382
383   for (i = 0; i < numDefaultServices; i++)
384     if (strcmp (serviceName, defaultServicesList[i]) == 0)
385       return GNUNET_YES;
386   return GNUNET_NO;
387 }
388
389
390 /**
391  *
392  */
393 static int
394 stop_listening (const char *serviceName)
395 {
396   struct ServiceListeningInfo *pos;
397   struct ServiceListeningInfo *next;
398   int ret;
399
400   ret = GNUNET_NO;
401   next = serviceListeningInfoList_head;
402   while (NULL != (pos = next))
403   {
404     next = pos->next;
405     if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0))
406       continue;
407     if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK)
408       GNUNET_SCHEDULER_cancel (pos->acceptTask);
409     GNUNET_break (GNUNET_OK ==
410                   GNUNET_NETWORK_socket_close (pos->listeningSocket));
411     GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
412                                  serviceListeningInfoList_tail, pos);
413     GNUNET_free (pos->serviceName);
414     GNUNET_free (pos->service_addr);
415     GNUNET_free (pos);
416     ret = GNUNET_OK;
417   }
418   return ret;
419 }
420
421
422 /**
423  * Transmit a status result message.
424  *
425  * @param cls pointer to "unit16_t*" with message type
426  * @param size number of bytes available in buf
427  * @param buf where to copy the message, NULL on error
428  * @return number of bytes copied to buf
429  */
430 static size_t
431 write_result (void *cls, size_t size, void *buf)
432 {
433   uint16_t *res = cls;
434   struct GNUNET_MessageHeader *msg;
435
436   if (buf == NULL)
437   {
438     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
439                 _("Could not send status result to client\n"));
440     return 0;                   /* error, not much we can do */
441   }
442 #if DEBUG_ARM
443   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending status response %u to client\n",
444               (unsigned int) *res);
445 #endif
446   GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
447   msg = buf;
448   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
449   msg->type = htons (*res);
450   GNUNET_free (res);
451   return sizeof (struct GNUNET_MessageHeader);
452 }
453
454
455 /**
456  * Signal our client that we will start or stop the
457  * service.
458  *
459  * @param client who is being signalled
460  * @param name name of the service
461  * @param result message type to send
462  * @return NULL if it was not found
463  */
464 static void
465 signal_result (struct GNUNET_SERVER_Client *client, const char *name,
466                uint16_t result)
467 {
468   uint16_t *res;
469
470   if (NULL == client)
471   {
472     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
473                 _("Not sending status result to client: no client known\n"));
474     return;
475   }
476 #if DEBUG_ARM
477   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
478               "Telling client that service `%s' is now %s\n", name,
479               result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up");
480 #endif
481   res = GNUNET_malloc (sizeof (uint16_t));
482   *res = result;
483   GNUNET_SERVER_notify_transmit_ready (client,
484                                        sizeof (struct GNUNET_MessageHeader),
485                                        GNUNET_TIME_UNIT_FOREVER_REL,
486                                        &write_result, res);
487 }
488
489
490 /**
491  * Find the process with the given service
492  * name in the given list and return it.
493  *
494  * @param name which service entry to look up
495  * @return NULL if it was not found
496  */
497 static struct ServiceList *
498 find_service (const char *name)
499 {
500   struct ServiceList *pos;
501
502   pos = running_head;
503   while (pos != NULL)
504   {
505     if (0 == strcmp (pos->name, name))
506       return pos;
507     pos = pos->next;
508   }
509   return NULL;
510 }
511
512
513 /**
514  * Start the specified service.
515  *
516  * @param client who is asking for this
517  * @param servicename name of the service to start
518  * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL
519  * @return GNUNET_OK on success, GNUNET_SYSERR on error
520  */
521 static int
522 start_service (struct GNUNET_SERVER_Client *client, const char *servicename,
523                const SOCKTYPE *lsocks)
524 {
525   struct ServiceList *sl;
526   char *binary;
527   char *config;
528   struct stat sbuf;
529
530   if (GNUNET_YES == in_shutdown)
531   {
532     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
533                 _("ARM is shutting down, service `%s' not started.\n"),
534                 servicename);
535     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
536     return GNUNET_SYSERR;
537   }
538   sl = find_service (servicename);
539   if (sl != NULL)
540   {
541     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Service `%s' already running.\n"),
542                 servicename);
543     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
544     return GNUNET_SYSERR;
545   }
546   if (GNUNET_OK !=
547       GNUNET_CONFIGURATION_get_value_string (cfg, servicename, "BINARY",
548                                              &binary))
549   {
550     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
551                 _("Binary implementing service `%s' not known!\n"),
552                 servicename);
553     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
554     return GNUNET_SYSERR;
555   }
556   if ((GNUNET_OK !=
557        GNUNET_CONFIGURATION_get_value_filename (cfg, servicename, "CONFIG",
558                                                 &config)) ||
559       (0 != STAT (config, &sbuf)))
560   {
561     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
562                 _("Configuration file `%s' for service `%s' not known!\n"),
563                 config, servicename);
564     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
565     GNUNET_free (binary);
566     GNUNET_free_non_null (config);
567     return GNUNET_SYSERR;
568   }
569   (void) stop_listening (servicename);
570   sl = GNUNET_malloc (sizeof (struct ServiceList));
571   sl->name = GNUNET_strdup (servicename);
572   sl->binary = binary;
573   sl->config = config;
574   sl->mtime = sbuf.st_mtime;
575   sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
576   sl->restartAt = GNUNET_TIME_UNIT_FOREVER_ABS;
577   GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl);
578   start_process (sl, lsocks);
579   if (NULL != client)
580     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
581   return GNUNET_OK;
582 }
583
584
585 /**
586  * First connection has come to the listening socket associated with the service,
587  * create the service in order to relay the incoming connection to it
588  *
589  * @param cls callback data, struct ServiceListeningInfo describing a listen socket
590  * @param tc context
591  */
592 static void
593 acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
594 {
595   struct ServiceListeningInfo *sli = cls;
596   struct ServiceListeningInfo *pos;
597   struct ServiceListeningInfo *next;
598   SOCKTYPE *lsocks;
599   unsigned int ls;
600
601   sli->acceptTask = GNUNET_SCHEDULER_NO_TASK;
602   if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
603     return;
604   GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
605                                serviceListeningInfoList_tail, sli);
606   lsocks = NULL;
607   ls = 0;
608   next = serviceListeningInfoList_head;
609   while (NULL != (pos = next))
610   {
611     next = pos->next;
612     if (0 == strcmp (pos->serviceName, sli->serviceName))
613     {
614       GNUNET_array_append (lsocks, ls,
615                            GNUNET_NETWORK_get_fd (pos->listeningSocket));
616       GNUNET_free (pos->listeningSocket);       /* deliberately no closing! */
617       GNUNET_free (pos->service_addr);
618       GNUNET_free (pos->serviceName);
619       GNUNET_SCHEDULER_cancel (pos->acceptTask);
620       GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head,
621                                    serviceListeningInfoList_tail, pos);
622       GNUNET_free (pos);
623     }
624   }
625   GNUNET_array_append (lsocks, ls,
626                        GNUNET_NETWORK_get_fd (sli->listeningSocket));
627   GNUNET_free (sli->listeningSocket);   /* deliberately no closing! */
628   GNUNET_free (sli->service_addr);
629 #if WINDOWS
630   GNUNET_array_append (lsocks, ls, INVALID_SOCKET);
631 #else
632   GNUNET_array_append (lsocks, ls, -1);
633 #endif
634   start_service (NULL, sli->serviceName, lsocks);
635   ls = 0;
636   while (lsocks[ls] != -1)
637 #if WINDOWS
638     GNUNET_break (0 == closesocket (lsocks[ls++]));
639 #else
640     GNUNET_break (0 == close (lsocks[ls++]));
641 #endif
642   GNUNET_array_grow (lsocks, ls, 0);
643   GNUNET_free (sli->serviceName);
644   GNUNET_free (sli);
645 }
646
647
648 /**
649  * Creating a listening socket for each of the service's addresses and
650  * wait for the first incoming connection to it
651  *
652  * @param sa address associated with the service
653  * @param addr_len length of sa
654  * @param serviceName the name of the service in question
655  */
656 static void
657 createListeningSocket (struct sockaddr *sa, socklen_t addr_len,
658                        const char *serviceName)
659 {
660   const static int on = 1;
661   struct GNUNET_NETWORK_Handle *sock;
662   struct ServiceListeningInfo *serviceListeningInfo;
663
664   switch (sa->sa_family)
665   {
666   case AF_INET:
667     sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0);
668     break;
669   case AF_INET6:
670     sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
671     break;
672   case AF_UNIX:
673     if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0)   /* Do not bind to blank UNIX path! */
674       return;
675     sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
676     break;
677   default:
678     GNUNET_break (0);
679     sock = NULL;
680     errno = EAFNOSUPPORT;
681     break;
682   }
683   if (NULL == sock)
684   {
685     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
686                 _("Unable to create socket for service `%s': %s\n"),
687                 serviceName, STRERROR (errno));
688     GNUNET_free (sa);
689     return;
690   }
691   if (GNUNET_NETWORK_socket_setsockopt
692       (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
693     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
694                          "setsockopt");
695 #ifdef IPV6_V6ONLY
696   if ((sa->sa_family == AF_INET6) &&
697       (GNUNET_NETWORK_socket_setsockopt
698        (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
699     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
700                          "setsockopt");
701 #endif
702
703   if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len)
704       != GNUNET_OK)
705   {
706     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
707                 _
708                 ("Unable to bind listening socket for service `%s' to address `%s': %s\n"),
709                 serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno));
710     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
711     GNUNET_free (sa);
712     return;
713   }
714   if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK)
715   {
716     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
717     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
718     GNUNET_free (sa);
719     return;
720   }
721   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
722               _("ARM now monitors connections to service `%s' at `%s'\n"),
723               serviceName, GNUNET_a2s (sa, addr_len));
724   serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo));
725   serviceListeningInfo->serviceName = GNUNET_strdup (serviceName);
726   serviceListeningInfo->service_addr = sa;
727   serviceListeningInfo->service_addr_len = addr_len;
728   serviceListeningInfo->listeningSocket = sock;
729   serviceListeningInfo->acceptTask =
730       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock,
731                                      &acceptConnection, serviceListeningInfo);
732   GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head,
733                                serviceListeningInfoList_tail,
734                                serviceListeningInfo);
735 }
736
737
738 /**
739  * Callback function, checks whether the current tokens are representing a service,
740  * gets its addresses and create listening socket for it.
741  *
742  * @param cls callback data, not used
743  * @param section configuration section
744  * @param option configuration option
745  * @param value the option's value
746  */
747 static void
748 checkPortNumberCB (void *cls, const char *section, const char *option,
749                    const char *value)
750 {
751   struct sockaddr **addrs;
752   socklen_t *addr_lens;
753   int ret;
754   unsigned int i;
755
756   if ((strcasecmp (section, "arm") == 0) ||
757       (strcasecmp (option, "AUTOSTART") != 0) ||
758       (strcasecmp (value, "YES") != 0) ||
759       (isInDefaultList (section) == GNUNET_YES))
760     return;
761   if (0 >=
762       (ret =
763        GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, &addr_lens)))
764     return;
765   /* this will free (or capture) addrs[i] */
766   for (i = 0; i < ret; i++)
767     createListeningSocket (addrs[i], addr_lens[i], section);
768   GNUNET_free (addrs);
769   GNUNET_free (addr_lens);
770 }
771
772
773 /**
774  * Entry point to the Service Manager
775  *
776  * @param configurationHandle configuration to use to get services
777  */
778 static void
779 prepare_services (const struct GNUNET_CONFIGURATION_Handle *configurationHandle)
780 {
781   char *defaultServicesString;
782
783   cfg = configurationHandle;
784   /* Split the default services into a list */
785   if (GNUNET_OK ==
786       GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES",
787                                              &defaultServicesString))
788   {
789     addDefaultServicesToList (defaultServicesString);
790     GNUNET_free (defaultServicesString);
791   }
792   /* Spot the services from the configuration and create a listening
793    * socket for each */
794   GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL);
795 }
796
797
798
799 /**
800  * If the configuration file changes, restart tasks that depended on that
801  * option.
802  *
803  * @param cls closure, NULL if we need to self-restart
804  * @param tc context
805  */
806 static void
807 config_change_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
808 {
809   struct ServiceList *pos;
810   struct stat sbuf;
811
812   pos = running_head;
813   while (pos != NULL)
814   {
815     /* FIXME: this test for config change may be a bit too coarse grained */
816     if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime) &&
817         (pos->proc != NULL))
818     {
819       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
820                   _
821                   ("Restarting service `%s' due to configuration file change.\n"));
822       if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
823         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
824       else
825         pos->backoff = GNUNET_TIME_UNIT_MILLISECONDS;
826     }
827     pos = pos->next;
828   }
829 }
830
831
832 /**
833  * Remove and free an entry in the service list.
834  *
835  * @param pos entry to free
836  */
837 static void
838 free_service (struct ServiceList *pos)
839 {
840   GNUNET_CONTAINER_DLL_remove (running_head, running_tail, pos);
841   GNUNET_free_non_null (pos->config);
842   GNUNET_free_non_null (pos->binary);
843   GNUNET_free (pos->name);
844   GNUNET_free (pos);
845 }
846
847
848 /**
849  * Stop the specified service.
850  *
851  * @param client who is asking for this
852  * @param servicename name of the service to stop
853  */
854 static void
855 stop_service (struct GNUNET_SERVER_Client *client, const char *servicename)
856 {
857   struct ServiceList *pos;
858
859   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Preparing to stop `%s'\n"),
860               servicename);
861   pos = find_service (servicename);
862   if (pos == NULL)
863   {
864     if (GNUNET_OK == stop_listening (servicename))
865       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
866     else
867       signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN);
868     GNUNET_SERVER_receive_done (client, GNUNET_OK);
869     return;
870   }
871   if (pos->killing_client != NULL)
872   {
873     /* killing already in progress */
874 #if DEBUG_ARM
875     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' is already down\n",
876                 servicename);
877 #endif
878     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
879     GNUNET_SERVER_receive_done (client, GNUNET_OK);
880     return;
881   }
882
883   if (GNUNET_YES == in_shutdown)
884   {
885 #if DEBUG_ARM
886     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
887                 "Termination request already sent to `%s' (since ARM is in shutdown).\n",
888                 servicename);
889 #endif
890     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
891     GNUNET_SERVER_receive_done (client, GNUNET_OK);
892     return;
893   }
894   if (pos->proc == NULL)
895   {
896     /* process is in delayed restart, simply remove it! */
897     free_service (pos);
898     signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
899     GNUNET_SERVER_receive_done (client, GNUNET_OK);
900     return;
901   }
902 #if DEBUG_ARM
903   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
904               "Sending kill signal to service `%s', waiting for process to die.\n",
905               servicename);
906 #endif
907   if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
908     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
909   pos->killing_client = client;
910   GNUNET_SERVER_client_keep (client);
911 }
912
913
914 /**
915  * Handle START-message.
916  *
917  * @param cls closure (always NULL)
918  * @param client identification of the client
919  * @param message the actual message
920  * @return GNUNET_OK to keep the connection open,
921  *         GNUNET_SYSERR to close it (signal serious error)
922  */
923 static void
924 handle_start (void *cls, struct GNUNET_SERVER_Client *client,
925               const struct GNUNET_MessageHeader *message)
926 {
927   const char *servicename;
928   uint16_t size;
929
930   size = ntohs (message->size);
931   size -= sizeof (struct GNUNET_MessageHeader);
932   servicename = (const char *) &message[1];
933   if ((size == 0) || (servicename[size - 1] != '\0'))
934   {
935     GNUNET_break (0);
936     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
937     return;
938   }
939   start_service (client, servicename, NULL);
940   GNUNET_SERVER_receive_done (client, GNUNET_OK);
941 }
942
943
944 /**
945  * Handle STOP-message.
946  *
947  * @param cls closure (always NULL)
948  * @param client identification of the client
949  * @param message the actual message
950  * @return GNUNET_OK to keep the connection open,
951  *         GNUNET_SYSERR to close it (signal serious error)
952  */
953 static void
954 handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
955              const struct GNUNET_MessageHeader *message)
956 {
957   const char *servicename;
958   uint16_t size;
959
960   size = ntohs (message->size);
961   size -= sizeof (struct GNUNET_MessageHeader);
962   servicename = (const char *) &message[1];
963   if ((size == 0) || (servicename[size - 1] != '\0'))
964   {
965     GNUNET_break (0);
966     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
967     return;
968   }
969   stop_service (client, servicename);
970 }
971
972
973 /**
974  * Remove all entries for tasks that are not running
975  * (proc = NULL) from the running list (they will no longer
976  * be restarted since we are shutting down).
977  */
978 static void
979 clean_up_running ()
980 {
981   struct ServiceList *pos;
982   struct ServiceList *next;
983
984   next = running_head;
985   while (NULL != (pos = next))
986   {
987     next = pos->next;
988     if (pos->proc == NULL)
989       free_service (pos);
990   }
991 }
992
993
994 /**
995  * We are done with everything.  Stop remaining
996  * tasks, signal handler and the server.
997  */
998 static void
999 do_shutdown ()
1000 {
1001   if (NULL != server)
1002   {
1003     GNUNET_SERVER_destroy (server);
1004     server = NULL;
1005   }
1006   if (GNUNET_SCHEDULER_NO_TASK != child_death_task)
1007   {
1008     GNUNET_SCHEDULER_cancel (child_death_task);
1009     child_death_task = GNUNET_SCHEDULER_NO_TASK;
1010   }
1011 }
1012
1013
1014 /**
1015  * Task run for shutdown.
1016  *
1017  * @param cls closure, NULL if we need to self-restart
1018  * @param tc context
1019  */
1020 static void
1021 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1022 {
1023   struct ServiceList *pos;
1024   struct ServiceList *nxt;
1025
1026 #if DEBUG_ARM
1027   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n"));
1028 #endif
1029   if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
1030   {
1031     GNUNET_SCHEDULER_cancel (child_restart_task);
1032     child_restart_task = GNUNET_SCHEDULER_NO_TASK;
1033   }
1034   in_shutdown = GNUNET_YES;
1035   stop_listening (NULL);
1036   pos = running_head;
1037   while (NULL != pos)
1038   {
1039     nxt = pos->next;
1040     if (pos->proc != NULL)
1041     {
1042       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", pos->name);
1043       if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM))
1044         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
1045     }
1046     else
1047     {
1048       free_service (pos);
1049     }
1050     pos = nxt;
1051   }
1052   if (running_head == NULL)
1053     do_shutdown ();
1054 }
1055
1056
1057 /**
1058  * Task run whenever it is time to restart a child that died.
1059  *
1060  * @param cls closure, always NULL
1061  * @param tc context
1062  */
1063 static void
1064 delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1065 {
1066   struct ServiceList *pos;
1067   struct GNUNET_TIME_Relative lowestRestartDelay;
1068
1069   child_restart_task = GNUNET_SCHEDULER_NO_TASK;
1070   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1071     return;
1072   GNUNET_assert (GNUNET_NO == in_shutdown);
1073   lowestRestartDelay = GNUNET_TIME_UNIT_FOREVER_REL;
1074
1075   /* check for services that need to be restarted due to
1076    * configuration changes or because the last restart failed */
1077   pos = running_head;
1078   while (pos != NULL)
1079   {
1080     if (pos->proc == NULL)
1081     {
1082       if (GNUNET_TIME_absolute_get_remaining (pos->restartAt).rel_value == 0)
1083       {
1084         GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Restarting service `%s'.\n"),
1085                     pos->name);
1086         start_process (pos, NULL);
1087       }
1088       else
1089       {
1090         lowestRestartDelay =
1091             GNUNET_TIME_relative_min (lowestRestartDelay,
1092                                       GNUNET_TIME_absolute_get_remaining
1093                                       (pos->restartAt));
1094       }
1095     }
1096     pos = pos->next;
1097   }
1098   if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
1099   {
1100 #if DEBUG_ARM
1101     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %llums\n",
1102                 (unsigned long long) lowestRestartDelay.rel_value);
1103 #endif
1104     child_restart_task =
1105         GNUNET_SCHEDULER_add_delayed (lowestRestartDelay, &delayed_restart_task,
1106                                       NULL);
1107   }
1108 }
1109
1110
1111 /**
1112  * Task triggered whenever we receive a SIGCHLD (child
1113  * process died).
1114  *
1115  * @param cls closure, NULL if we need to self-restart
1116  * @param tc context
1117  */
1118 static void
1119 maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1120 {
1121   struct ServiceList *pos;
1122   struct ServiceList *next;
1123   const char *statstr;
1124   int statcode;
1125   int ret;
1126   char c[16];
1127   enum GNUNET_OS_ProcessStatusType statusType;
1128   unsigned long statusCode;
1129
1130   child_death_task = GNUNET_SCHEDULER_NO_TASK;
1131   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
1132   {
1133     /* shutdown scheduled us, ignore! */
1134     child_death_task =
1135         GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
1136                                         &maint_child_death, NULL);
1137     return;
1138   }
1139   /* consume the signal */
1140   GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
1141
1142   /* check for services that died (WAITPID) */
1143   next = running_head;
1144   while (NULL != (pos = next))
1145   {
1146     next = pos->next;
1147     if (pos->proc == NULL)
1148       continue;
1149     if ((GNUNET_SYSERR ==
1150          (ret = GNUNET_OS_process_status (pos->proc, &statusType, &statusCode)))
1151         || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) ||
1152             (statusType == GNUNET_OS_PROCESS_RUNNING)))
1153       continue;
1154
1155     if (statusType == GNUNET_OS_PROCESS_EXITED)
1156     {
1157       statstr = _( /* process termination method */ "exit");
1158       statcode = statusCode;
1159     }
1160     else if (statusType == GNUNET_OS_PROCESS_SIGNALED)
1161     {
1162       statstr = _( /* process termination method */ "signal");
1163       statcode = statusCode;
1164     }
1165     else
1166     {
1167       statstr = _( /* process termination method */ "unknown");
1168       statcode = 0;
1169     }
1170     GNUNET_OS_process_close (pos->proc);
1171     pos->proc = NULL;
1172     if (NULL != pos->killing_client)
1173     {
1174       GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' stopped\n"),
1175                   pos->name);
1176       signal_result (pos->killing_client, pos->name,
1177                      GNUNET_MESSAGE_TYPE_ARM_IS_DOWN);
1178       GNUNET_SERVER_receive_done (pos->killing_client, GNUNET_OK);
1179       GNUNET_SERVER_client_drop (pos->killing_client);
1180       free_service (pos);
1181       continue;
1182     }
1183     if (GNUNET_YES != in_shutdown)
1184     {
1185       if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1186         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1187                     _
1188                     ("Service `%s' terminated with status %s/%d, will try to restart it!\n"),
1189                     pos->name, statstr, statcode);
1190       /* schedule restart */
1191       pos->restartAt = GNUNET_TIME_relative_to_absolute (pos->backoff);
1192       if (pos->backoff.rel_value < EXPONENTIAL_BACKOFF_THRESHOLD)
1193         pos->backoff = GNUNET_TIME_relative_multiply (pos->backoff, 2);
1194       if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
1195         GNUNET_SCHEDULER_cancel (child_restart_task);
1196       child_restart_task =
1197           GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1198                                               &delayed_restart_task, NULL);
1199     }
1200 #if DEBUG_ARM
1201     else
1202       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1203                   "Service `%s' terminated with status %s/%d\n", pos->name,
1204                   statstr, statcode);
1205 #endif
1206   }
1207   child_death_task =
1208       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
1209                                       &maint_child_death, NULL);
1210   if (GNUNET_YES == in_shutdown)
1211     clean_up_running ();
1212   if ((NULL == running_head) && (GNUNET_YES == in_shutdown))
1213     do_shutdown ();
1214 }
1215
1216
1217 static size_t
1218 transmit_shutdown_ack (void *cls, size_t size, void *buf)
1219 {
1220   struct GNUNET_SERVER_Client *client = cls;
1221   struct GNUNET_MessageHeader *msg;
1222
1223   if (size < sizeof (struct GNUNET_MessageHeader))
1224   {
1225     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1226                 _("Failed to transmit shutdown ACK.\n"));
1227     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
1228     return 0;                   /* client disconnected */
1229   }
1230
1231   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting shutdown ACK.\n"));
1232
1233   /* Make the connection flushing for the purpose of ACK transmitting,
1234    * needed on W32 to ensure that the message is even received, harmless
1235    * on other platforms... */
1236   GNUNET_break (GNUNET_OK == GNUNET_SERVER_client_disable_corking (client));
1237   msg = (struct GNUNET_MessageHeader *) buf;
1238   msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK);
1239   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
1240   GNUNET_SERVER_receive_done (client, GNUNET_OK);
1241   GNUNET_SERVER_client_drop (client);
1242   return sizeof (struct GNUNET_MessageHeader);
1243 }
1244
1245
1246 /**
1247  * Handler for SHUTDOWN message.
1248  *
1249  * @param cls closure (refers to service)
1250  * @param client identification of the client
1251  * @param message the actual message
1252  */
1253 static void
1254 handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client,
1255                  const struct GNUNET_MessageHeader *message)
1256 {
1257   GNUNET_SERVER_client_keep (client);
1258   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1259               _("Initiating shutdown as requested by client.\n"));
1260   GNUNET_SERVER_notify_transmit_ready (client,
1261                                        sizeof (struct GNUNET_MessageHeader),
1262                                        GNUNET_TIME_UNIT_FOREVER_REL,
1263                                        &transmit_shutdown_ack, client);
1264   GNUNET_SERVER_client_persist_ (client);
1265   GNUNET_SCHEDULER_shutdown ();
1266 }
1267
1268
1269 /**
1270  * Signal handler called for SIGCHLD.  Triggers the
1271  * respective handler by writing to the trigger pipe.
1272  */
1273 static void
1274 sighandler_child_death ()
1275 {
1276   static char c;
1277   int old_errno = errno;        /* back-up errno */
1278
1279   GNUNET_break (1 ==
1280                 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
1281                                         (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
1282                                         &c, sizeof (c)));
1283   errno = old_errno;            /* restore errno */
1284 }
1285
1286
1287 /**
1288  * Process arm requests.
1289  *
1290  * @param cls closure
1291  * @param serv the initialized server
1292  * @param c configuration to use
1293  */
1294 static void
1295 run (void *cls, struct GNUNET_SERVER_Handle *serv,
1296      const struct GNUNET_CONFIGURATION_Handle *c)
1297 {
1298   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1299     {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
1300     {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
1301     {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN,
1302      sizeof (struct GNUNET_MessageHeader)},
1303     {NULL, NULL, 0, 0}
1304   };
1305   char *defaultservices;
1306   char *pos;
1307
1308   cfg = c;
1309   server = serv;
1310   GNUNET_assert (serv != NULL);
1311   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
1312   GNUNET_assert (pr != NULL);
1313   GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES);
1314   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
1315                                 NULL);
1316   child_death_task =
1317       GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr,
1318                                       &maint_child_death, NULL);
1319
1320   if (GNUNET_OK !=
1321       GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_PREFIX",
1322                                              &prefix_command))
1323     prefix_command = GNUNET_strdup ("");
1324   if (GNUNET_OK !=
1325       GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_POSTFIX",
1326                                              &final_option))
1327     final_option = GNUNET_strdup ("");
1328   /* start default services... */
1329   if (GNUNET_OK ==
1330       GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "DEFAULTSERVICES",
1331                                              &defaultservices))
1332   {
1333 #if DEBUG_ARM
1334     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting default services `%s'\n",
1335                 defaultservices);
1336 #endif
1337     if (0 < strlen (defaultservices))
1338     {
1339       pos = strtok (defaultservices, " ");
1340       while (pos != NULL)
1341       {
1342         start_service (NULL, pos, NULL);
1343         pos = strtok (NULL, " ");
1344       }
1345     }
1346     GNUNET_free (defaultservices);
1347   }
1348   else
1349   {
1350 #if DEBUG_ARM
1351     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No default services configured.\n");
1352 #endif
1353   }
1354
1355   /* create listening sockets for future services */
1356   prepare_services (cfg);
1357
1358   /* process client requests */
1359   GNUNET_SERVER_add_handlers (server, handlers);
1360
1361   /* manage services */
1362   GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
1363                                       &config_change_task, NULL);
1364 }
1365
1366
1367 /**
1368  * The main function for the arm service.
1369  *
1370  * @param argc number of arguments from the command line
1371  * @param argv command line arguments
1372  * @return 0 ok, 1 on error
1373  */
1374 int
1375 main (int argc, char *const *argv)
1376 {
1377   int ret;
1378   struct GNUNET_SIGNAL_Context *shc_chld;
1379
1380   sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO);
1381   GNUNET_assert (sigpipe != NULL);
1382   shc_chld =
1383       GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
1384   ret =
1385       (GNUNET_OK ==
1386        GNUNET_SERVICE_run (argc, argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1;
1387   GNUNET_SIGNAL_handler_uninstall (shc_chld);
1388   shc_chld = NULL;
1389   GNUNET_DISK_pipe_close (sigpipe);
1390   sigpipe = NULL;
1391   return ret;
1392 }
1393
1394 #ifdef LINUX
1395 #include <malloc.h>
1396
1397 /**
1398  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1399  */
1400 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1401 {
1402   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1403   mallopt (M_TOP_PAD, 1 * 1024);
1404   malloc_trim (0);
1405 }
1406 #endif
1407
1408
1409 /* end of gnunet-service-arm.c */