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