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