glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / arm / gnunet-arm.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2012, 2013 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15
16 /**
17  * @file arm/gnunet-arm.c
18  * @brief arm for writing a tool
19  * @author Christian Grothoff
20  */
21 #include "platform.h"
22 #include "gnunet_arm_service.h"
23 #include "gnunet_constants.h"
24 #include "gnunet_util_lib.h"
25
26 /**
27  * Set if we are to shutdown all services (including ARM).
28  */
29 static int end;
30
31 /**
32  * Set if we are to start default services (including ARM).
33  */
34 static int start;
35
36 /**
37  * Set if we are to stop/start default services (including ARM).
38  */
39 static int restart;
40
41 /**
42  * Set if we should delete configuration and temp directory on exit.
43  */
44 static int delete;
45
46 /**
47  * Set if we should not print status messages.
48  */
49 static int quiet;
50
51 /**
52  * Monitor ARM activity.
53  */
54 static int monitor;
55
56 /**
57  * Set if we should print a list of currently running services.
58  */
59 static int list;
60
61 /**
62  * Set to the name of a service to start.
63  */
64 static char *init;
65
66 /**
67  * Set to the name of a service to kill.
68  */
69 static char *term;
70
71 /**
72  * Set to the name of the config file used.
73  */
74 static const char *config_file;
75
76 /**
77  * Set to the directory where runtime files are stored.
78  */
79 static char *dir;
80
81 /**
82  * Final status code.
83  */
84 static int ret;
85
86 /**
87  * Connection with ARM.
88  */
89 static struct GNUNET_ARM_Handle *h;
90
91 /**
92  * Monitor connection with ARM.
93  */
94 static struct GNUNET_ARM_MonitorHandle *m;
95
96 /**
97  * Our configuration.
98  */
99 static struct GNUNET_CONFIGURATION_Handle *cfg;
100
101 /**
102  * Processing stage that we are in.  Simple counter.
103  */
104 static unsigned int phase;
105
106 /**
107  * User defined timestamp for completing operations.
108  */
109 static struct GNUNET_TIME_Relative timeout;
110
111 /**
112  * Task to be run on timeout.
113  */
114 static struct GNUNET_SCHEDULER_Task *timeout_task;
115
116 /**
117  * Do we want to give our stdout to gnunet-service-arm?
118  */
119 static int no_stdout;
120
121 /**
122  * Do we want to give our stderr to gnunet-service-arm?
123  */
124 static int no_stderr;
125
126 /**
127  * Handle for the task running the #action_loop().
128  */
129 static struct GNUNET_SCHEDULER_Task *al_task;
130
131 /**
132  * Current operation.
133  */
134 static struct GNUNET_ARM_Operation *op;
135
136 /**
137  * Attempts to delete configuration file and GNUNET_HOME
138  * on ARM shutdown provided the end and delete options
139  * were specified when gnunet-arm was run.
140  */
141 static void
142 delete_files ()
143 {
144   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145               "Will attempt to remove configuration file %s and service directory %s\n",
146               config_file, dir);
147
148   if (0 != UNLINK (config_file))
149   {
150     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
151                 _("Failed to remove configuration file %s\n"),
152                 config_file);
153   }
154   if (GNUNET_OK != GNUNET_DISK_directory_remove (dir))
155   {
156     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
157                 _("Failed to remove servicehome directory %s\n"),
158                 dir);
159
160   }
161 }
162
163
164 /**
165  * Main continuation-passing-style loop.  Runs the various
166  * jobs that we've been asked to do in order.
167  *
168  * @param cls closure, unused
169  */
170 static void
171 shutdown_task (void *cls)
172 {
173   if (NULL != al_task)
174   {
175     GNUNET_SCHEDULER_cancel (al_task);
176     al_task = NULL;
177   }
178   if (NULL != op)
179   {
180     GNUNET_ARM_operation_cancel (op);
181     op = NULL;
182   }
183   if (NULL != h)
184   {
185     GNUNET_ARM_disconnect (h);
186     h = NULL;
187   }
188   if (NULL != m)
189   {
190     GNUNET_ARM_monitor_stop (m);
191     m = NULL;
192   }
193   if (NULL != timeout_task)
194   {
195     GNUNET_SCHEDULER_cancel (timeout_task);
196     timeout_task = NULL;
197   }
198   if ((GNUNET_YES == end) && (GNUNET_YES == delete))
199     delete_files ();
200   GNUNET_CONFIGURATION_destroy (cfg);
201   cfg = NULL;
202 }
203
204
205 /**
206  * Returns a string interpretation of 'rs'
207  *
208  * @param rs the request status from ARM
209  * @return a string interpretation of the request status
210  */
211 static const char *
212 req_string (enum GNUNET_ARM_RequestStatus rs)
213 {
214   switch (rs)
215   {
216   case GNUNET_ARM_REQUEST_SENT_OK:
217     return _("Message was sent successfully");
218   case GNUNET_ARM_REQUEST_DISCONNECTED:
219     return _("We disconnected from ARM before we could send a request");
220   }
221   return _("Unknown request status");
222 }
223
224
225 /**
226  * Returns a string interpretation of the 'result'
227  *
228  * @param result the arm result
229  * @return a string interpretation
230  */
231 static const char *
232 ret_string (enum GNUNET_ARM_Result result)
233 {
234   switch (result)
235   {
236   case GNUNET_ARM_RESULT_STOPPED:
237     return _("is stopped");
238   case GNUNET_ARM_RESULT_STARTING:
239     return _("is starting");
240   case GNUNET_ARM_RESULT_STOPPING:
241     return _("is stopping");
242   case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
243     return _("is starting already");
244   case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
245     return _("is stopping already");
246   case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
247     return _("is started already");
248   case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
249     return _("is stopped already");
250   case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
251     return _("service is not known to ARM");
252   case GNUNET_ARM_RESULT_START_FAILED:
253     return _("service failed to start");
254   case GNUNET_ARM_RESULT_IN_SHUTDOWN:
255     return _("service cannot be manipulated because ARM is shutting down");
256   }
257   return _("Unknown result code.");
258 }
259
260
261 /**
262  * Main task that runs our various operations in order.
263  *
264  * @param cls closure
265  */
266 static void
267 action_loop (void *cls);
268
269
270 /**
271  * Function called whenever we connect to or disconnect from ARM.
272  * Termiantes the process if we fail to connect to the service on
273  * our first attempt.
274  *
275  * @param cls closure
276  * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
277  *                  #GNUNET_SYSERR on error.
278  */
279 static void
280 conn_status (void *cls,
281              int connected)
282 {
283   static int once;
284
285   if ( (GNUNET_SYSERR == connected) &&
286        (0 == once) )
287   {
288     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
289                 _("Fatal error initializing ARM API.\n"));
290     GNUNET_SCHEDULER_shutdown ();
291     return;
292   }
293   once = 1;
294 }
295
296
297 /**
298  * We have requested ARM to be started, this function
299  * is called with the result of the operation.  Informs the
300  * use of the result; on success, we continue with the event
301  * loop, on failure we terminate the process.
302  *
303  * @param cls closure unused
304  * @param rs what happened to our request
305  * @param result if the request was processed, this is the result
306  *               according to ARM
307  */
308 static void
309 start_callback (void *cls,
310                 enum GNUNET_ARM_RequestStatus rs,
311                 enum GNUNET_ARM_Result result)
312 {
313   op = NULL;
314   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
315   {
316     FPRINTF (stdout,
317              _("Failed to start the ARM service: %s\n"),
318              req_string (rs));
319     GNUNET_SCHEDULER_shutdown ();
320     return;
321   }
322   if ( (GNUNET_ARM_RESULT_STARTING != result) &&
323        (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result) )
324   {
325     FPRINTF (stdout,
326              _("Failed to start the ARM service: %s\n"),
327              ret_string (result));
328     GNUNET_SCHEDULER_shutdown ();
329     return;
330   }
331   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
332               "ARM service [re]start successful\n");
333   start = 0;
334   al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
335 }
336
337
338 /**
339  * We have requested ARM to be stopped, this function
340  * is called with the result of the operation.  Informs the
341  * use of the result; on success, we continue with the event
342  * loop, on failure we terminate the process.
343  *
344  * @param cls closure unused
345  * @param rs what happened to our request
346  * @param result if the request was processed, this is the result
347  *               according to ARM
348  */
349 static void
350 stop_callback (void *cls,
351                enum GNUNET_ARM_RequestStatus rs,
352                enum GNUNET_ARM_Result result)
353 {
354   char *msg;
355
356   op = NULL;
357   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
358   {
359     GNUNET_asprintf (&msg, "%s",
360                      _("Failed to send a stop request to the ARM service: %s\n"));
361     FPRINTF (stdout, msg, req_string (rs));
362     GNUNET_free (msg);
363     GNUNET_SCHEDULER_shutdown ();
364     return;
365   }
366   if ((GNUNET_ARM_RESULT_STOPPING != result) &&
367       (GNUNET_ARM_RESULT_STOPPED != result) &&
368       (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result))
369   {
370     FPRINTF (stdout,
371              _("Failed to stop the ARM service: %s\n"),
372              ret_string (result));
373     GNUNET_SCHEDULER_shutdown ();
374     return;
375   }
376   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377               "ARM service shutdown successful\n");
378   end = 0;
379   if (restart)
380   {
381     restart = 0;
382     start = 1;
383     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
384                 "Initiating an ARM restart\n");
385   }
386   al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
387 }
388
389
390 /**
391  * We have requested a service to be started, this function
392  * is called with the result of the operation.  Informs the
393  * use of the result; on success, we continue with the event
394  * loop, on failure we terminate the process.
395  *
396  * @param cls closure unused
397  * @param rs what happened to our request
398  * @param result if the request was processed, this is the result
399  *               according to ARM
400  */
401 static void
402 init_callback (void *cls,
403                enum GNUNET_ARM_RequestStatus rs,
404                enum GNUNET_ARM_Result result)
405 {
406   op = NULL;
407   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
408   {
409     FPRINTF (stdout,
410              _("Failed to send a request to start the `%s' service: %s\n"),
411              init,
412              req_string (rs));
413     GNUNET_SCHEDULER_shutdown ();
414     return;
415   }
416   if ((GNUNET_ARM_RESULT_STARTING != result) &&
417       (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
418   {
419     FPRINTF (stdout,
420              _("Failed to start the `%s' service: %s\n"),
421              init,
422              ret_string (result));
423     GNUNET_SCHEDULER_shutdown ();
424     return;
425   }
426   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
427               "Service %s [re]started successfully\n",
428               init);
429   GNUNET_free (init);
430   init = NULL;
431   al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
432 }
433
434
435 /**
436  * We have requested a service to be stopped, this function
437  * is called with the result of the operation.  Informs the
438  * use of the result; on success, we continue with the event
439  * loop, on failure we terminate the process.
440  *
441  * @param cls closure unused
442  * @param rs what happened to our request
443  * @param result if the request was processed, this is the result
444  *               according to ARM
445  */
446 static void
447 term_callback (void *cls,
448                enum GNUNET_ARM_RequestStatus rs,
449                enum GNUNET_ARM_Result result)
450 {
451   char *msg;
452
453   op = NULL;
454   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
455   {
456     GNUNET_asprintf (&msg,
457                      _("Failed to send a request to kill the `%s' service: %%s\n"),
458                      term);
459     FPRINTF (stdout, msg, req_string (rs));
460     GNUNET_free (msg);
461     GNUNET_SCHEDULER_shutdown ();
462     return;
463   }
464   if ((GNUNET_ARM_RESULT_STOPPED != result) &&
465       (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result))
466   {
467     FPRINTF (stdout,
468              _("Failed to kill the `%s' service: %s\n"),
469              term,
470              ret_string (result));
471     GNUNET_SCHEDULER_shutdown ();
472     return;
473   }
474
475   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
476               "Service %s stopped successfully\n",
477               term);
478   GNUNET_free (term);
479   term = NULL;
480   al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
481 }
482
483
484 /**
485  * Function called with the list of running services. Prints
486  * the list to stdout, then starts the event loop again.
487  * Prints an error message and terminates the process on errors.
488  *
489  * @param cls closure (unused)
490  * @param rs request status (success, failure, etc.)
491  * @param count number of services in the list
492  * @param list list of services that are running
493  */
494 static void
495 list_callback (void *cls,
496                enum GNUNET_ARM_RequestStatus rs,
497                unsigned int count,
498                const char *const*list)
499 {
500   unsigned int i;
501
502   op = NULL;
503   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
504   {
505     char *msg;
506
507     GNUNET_asprintf (&msg, "%s",
508                      _("Failed to request a list of services: %s\n"));
509     FPRINTF (stdout, msg, req_string (rs));
510     GNUNET_free (msg);
511     ret = 3;
512     GNUNET_SCHEDULER_shutdown ();
513   }
514   if (NULL == list)
515   {
516     FPRINTF (stderr, "%s",
517              _("Error communicating with ARM. ARM not running?\n"));
518     GNUNET_SCHEDULER_shutdown ();
519     ret = 3;
520     return;
521   }
522   if (! quiet)
523     FPRINTF (stdout, "%s", _("Running services:\n"));
524   for (i = 0; i < count; i++)
525     FPRINTF (stdout, "%s\n", list[i]);
526   al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
527 }
528
529
530 /**
531  * Main action loop.  Runs the various jobs that we've been asked to
532  * do, in order.
533  *
534  * @param cls closure, unused
535  */
536 static void
537 action_loop (void *cls)
538 {
539   al_task = NULL;
540   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541               "Running requested actions\n");
542   while (1)
543   {
544     switch (phase++)
545     {
546     case 0:
547       if (NULL != term)
548       {
549         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
550                     "Termination action\n");
551         op = GNUNET_ARM_request_service_stop (h,
552                                               term,
553                                               &term_callback,
554                                               NULL);
555         return;
556       }
557       break;
558     case 1:
559       if (end || restart)
560       {
561         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562                     "End action\n");
563         op = GNUNET_ARM_request_service_stop (h,
564                                               "arm",
565                                               &stop_callback,
566                                               NULL);
567         return;
568       }
569       break;
570     case 2:
571       if (start)
572       {
573         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574                     "Start action\n");
575         op = GNUNET_ARM_request_service_start (h, "arm",
576                                                (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) |
577                                                (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR),
578                                                &start_callback,
579                                                NULL);
580         return;
581       }
582       break;
583     case 3:
584       if (NULL != init)
585       {
586         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
587                     "Initialization action\n");
588         op = GNUNET_ARM_request_service_start (h, init,
589                                                GNUNET_OS_INHERIT_STD_NONE,
590                                                &init_callback,
591                                                NULL);
592         return;
593       }
594       break;
595     case 4:
596       if (list)
597       {
598         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
599                     "Going to list all running services controlled by ARM.\n");
600         op = GNUNET_ARM_request_service_list (h,
601                                               &list_callback,
602                                               &list);
603         return;
604       }
605       break;
606     case 5:
607       if (monitor)
608         {
609           if (! quiet)
610             fprintf (stderr,
611                      _("Now only monitoring, press CTRL-C to stop.\n"));
612           quiet = 0; /* does not make sense to stay quiet in monitor mode at this time */
613           return; /* done with tasks, just monitor */
614         }
615       break;
616     default:            /* last phase */
617       GNUNET_SCHEDULER_shutdown ();
618       return;
619     }
620   }
621 }
622
623
624 /**
625  * Function called when a service starts or stops.
626  *
627  * @param cls closure
628  * @param service service name
629  * @param status status of the service
630  */
631 static void
632 srv_status (void *cls,
633             const char *service,
634             enum GNUNET_ARM_ServiceStatus status)
635 {
636   const char *msg;
637
638   switch (status)
639   {
640   case GNUNET_ARM_SERVICE_MONITORING_STARTED:
641     return; /* this should be done silently */
642   case GNUNET_ARM_SERVICE_STOPPED:
643     msg = _("Stopped %s.\n");
644     break;
645   case GNUNET_ARM_SERVICE_STARTING:
646     msg = _("Starting %s...\n");
647     break;
648   case GNUNET_ARM_SERVICE_STOPPING:
649     msg = _("Stopping %s...\n");
650     break;
651   default:
652     msg = NULL;
653     break;
654   }
655   if (! quiet)
656   {
657     if (NULL != msg)
658       FPRINTF (stderr,
659                msg,
660                service);
661     else
662       FPRINTF (stderr,
663                _("Unknown status %u for service %s.\n"),
664                status,
665                service);
666   }
667   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668               "Got service %s status %d\n",
669               service, (int)
670               status);
671 }
672
673
674 /**
675  * Task run on timeout (if -T is given).
676  */
677 static void
678 timeout_task_cb (void *cls)
679 {
680   timeout_task = NULL;
681   ret = 2;
682   GNUNET_SCHEDULER_shutdown ();
683 }
684
685
686 /**
687  * Main function that will be run by the scheduler.
688  *
689  * @param cls closure
690  * @param args remaining command-line arguments
691  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
692  * @param c configuration
693  */
694 static void
695 run (void *cls,
696      char *const *args,
697      const char *cfgfile,
698      const struct GNUNET_CONFIGURATION_Handle *c)
699 {
700   char *armconfig;
701
702   cfg = GNUNET_CONFIGURATION_dup (c);
703   config_file = cfgfile;
704   if (GNUNET_OK !=
705       GNUNET_CONFIGURATION_get_value_string (cfg,
706                                              "PATHS",
707                                              "GNUNET_HOME",
708                                              &dir))
709   {
710     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
711                                "PATHS",
712                                "GNUNET_HOME");
713     return;
714   }
715   if (NULL != cfgfile)
716   {
717     if (GNUNET_OK !=
718         GNUNET_CONFIGURATION_get_value_filename (cfg,
719                                                  "arm",
720                                                  "CONFIG",
721                                                  &armconfig))
722     {
723       GNUNET_CONFIGURATION_set_value_string (cfg,
724                                              "arm",
725                                              "CONFIG",
726                                              cfgfile);
727     }
728     else
729       GNUNET_free (armconfig);
730   }
731   if (NULL == (h = GNUNET_ARM_connect (cfg,
732                                        &conn_status,
733                                        NULL)))
734     return;
735   if (monitor)
736     m = GNUNET_ARM_monitor_start (cfg,
737                             &srv_status,
738                             NULL);
739   al_task = GNUNET_SCHEDULER_add_now (&action_loop,
740                                       NULL);
741   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
742                                  NULL);
743   if (0 != timeout.rel_value_us)
744     timeout_task = GNUNET_SCHEDULER_add_delayed (timeout,
745                                                  &timeout_task_cb,
746                                                  NULL);
747 }
748
749
750 /**
751  * The main function to obtain arm from gnunetd.
752  *
753  * @param argc number of arguments from the command line
754  * @param argv command line arguments
755  * @return 0 ok, 1 on error, 2 on timeout
756  */
757 int
758 main (int argc, char *const *argv)
759 {
760   struct GNUNET_GETOPT_CommandLineOption options[] = {
761
762     GNUNET_GETOPT_option_flag ('e',
763                                   "end",
764                                   gettext_noop ("stop all GNUnet services"),
765                                   &end),
766
767     GNUNET_GETOPT_option_string ('i',
768                                  "init",
769                                  "SERVICE",
770                                  gettext_noop ("start a particular service"),
771                                  &init),
772
773     GNUNET_GETOPT_option_string ('k',
774                                  "kill",
775                                  "SERVICE",
776                                  gettext_noop ("stop a particular service"),
777                                  &term),
778
779     GNUNET_GETOPT_option_flag ('s',
780                                   "start",
781                                   gettext_noop ("start all GNUnet default services"),
782                                   &start),
783
784     GNUNET_GETOPT_option_flag ('r',
785                                   "restart",
786                                   gettext_noop ("stop and start all GNUnet default services"),
787                                   &restart),
788     GNUNET_GETOPT_option_flag ('d',
789                                   "delete",
790                                   gettext_noop ("delete config file and directory on exit"),
791                                   &delete),
792
793     GNUNET_GETOPT_option_flag ('m',
794                                   "monitor",
795                                   gettext_noop ("monitor ARM activities"),
796                                   &monitor),
797
798     GNUNET_GETOPT_option_flag ('q',
799                                   "quiet",
800                                   gettext_noop ("don't print status messages"),
801                                   &quiet),
802
803     GNUNET_GETOPT_option_relative_time ('T',
804                                             "timeout",
805                                             "DELAY",
806                                             gettext_noop ("exit with error status if operation does not finish after DELAY"),
807                                             &timeout),
808
809     GNUNET_GETOPT_option_flag ('I',
810                                   "info",
811                                   gettext_noop ("list currently running services"),
812                                   &list), 
813
814     GNUNET_GETOPT_option_flag ('O',
815                                   "no-stdout",
816                                   gettext_noop ("don't let gnunet-service-arm inherit standard output"),
817                                   &no_stdout),
818
819     GNUNET_GETOPT_option_flag ('E',
820                                   "no-stderr",
821                                   gettext_noop ("don't let gnunet-service-arm inherit standard error"),
822                                   &no_stderr),
823
824     GNUNET_GETOPT_OPTION_END
825   };
826
827   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv,
828                                                  &argc, &argv))
829     return 2;
830
831   if (GNUNET_OK ==
832       GNUNET_PROGRAM_run (argc, argv, "gnunet-arm",
833                           gettext_noop
834                           ("Control services and the Automated Restart Manager (ARM)"),
835                           options, &run, NULL))
836   {
837     GNUNET_free ((void *) argv);
838     return ret;
839   }
840   GNUNET_free ((void*) argv);
841   return 1;
842 }
843
844 /* end of gnunet-arm.c */