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