implementing new scheduler shutdown semantics
[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   FPRINTF (stdout, "%s", _("Running services:\n"));
548   for (i = 0; i < count; i++)
549     FPRINTF (stdout, "%s\n", list[i]);
550   al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
551 }
552
553
554 /**
555  * Main action loop.  Runs the various jobs that we've been asked to
556  * do, in order.
557  *
558  * @param cls closure, unused
559  */
560 static void
561 action_loop (void *cls)
562 {
563   al_task = NULL;
564   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565               "Running requested actions\n");
566   while (1)
567   {
568     switch (phase++)
569     {
570     case 0:
571       if (NULL != term)
572       {
573         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574                     "Termination action\n");
575         GNUNET_ARM_request_service_stop (h, term,
576                                          (0 == timeout.rel_value_us) ? STOP_TIMEOUT : timeout,
577                                          &term_callback, NULL);
578         return;
579       }
580       break;
581     case 1:
582       if (end || restart)
583       {
584         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
585                     "End action\n");
586         GNUNET_ARM_request_service_stop (h, "arm",
587                                          (0 == timeout.rel_value_us) ? STOP_TIMEOUT_ARM : timeout,
588                                          &stop_callback, NULL);
589         return;
590       }
591       break;
592     case 2:
593       if (start)
594       {
595         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
596                     "Start action\n");
597         GNUNET_ARM_request_service_start (h, "arm",
598             (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) |
599             (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR),
600             (0 == timeout.rel_value_us) ? START_TIMEOUT: timeout,
601             start_callback, NULL);
602         return;
603       }
604       break;
605     case 3:
606       if (NULL != init)
607       {
608         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
609                     "Initialization action\n");
610         GNUNET_ARM_request_service_start (h, init,
611                                           GNUNET_OS_INHERIT_STD_NONE,
612                                           (0 == timeout.rel_value_us) ? STOP_TIMEOUT : timeout,
613                                           &init_callback, NULL);
614         return;
615       }
616       break;
617     case 4:
618       if (list)
619       {
620         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
621                     "Going to list all running services controlled by ARM.\n");
622         GNUNET_ARM_request_service_list (h,
623                                          (0 == timeout.rel_value_us) ? LIST_TIMEOUT : timeout,
624                                          &list_callback, &list);
625         return;
626       }
627       break;
628     case 5:
629       if (monitor)
630         {
631           if (! quiet)
632             fprintf (stderr,
633                      _("Now only monitoring, press CTRL-C to stop.\n"));
634           quiet = 0; /* does not make sense to stay quiet in monitor mode at this time */
635           return; /* done with tasks, just monitor */
636         }
637       break;
638     default:            /* last phase */
639       GNUNET_SCHEDULER_shutdown ();
640       return;
641     }
642   }
643 }
644
645
646 /**
647  * Function called when a service starts or stops.
648  *
649  * @param cls closure
650  * @param service service name
651  * @param status status of the service
652  */
653 static void
654 srv_status (void *cls,
655             const char *service,
656             enum GNUNET_ARM_ServiceStatus status)
657 {
658   const char *msg;
659
660   switch (status)
661   {
662   case GNUNET_ARM_SERVICE_MONITORING_STARTED:
663     return; /* this should be done silently */
664   case GNUNET_ARM_SERVICE_STOPPED:
665     msg = _("Stopped %s.\n");
666     break;
667   case GNUNET_ARM_SERVICE_STARTING:
668     msg = _("Starting %s...\n");
669     break;
670   case GNUNET_ARM_SERVICE_STOPPING:
671     msg = _("Stopping %s...\n");
672     break;
673   default:
674     msg = NULL;
675     break;
676   }
677   if (! quiet)
678     {
679       if (NULL != msg)
680         FPRINTF (stderr, msg, service);
681       else
682         FPRINTF (stderr, _("Unknown status %u for service %s.\n"), status, service);
683     }
684   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got service %s status %d\n", service, (int) status);
685 }
686
687
688 /**
689  * Main function that will be run by the scheduler.
690  *
691  * @param cls closure
692  * @param args remaining command-line arguments
693  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
694  * @param c configuration
695  */
696 static void
697 run (void *cls,
698      char *const *args,
699      const char *cfgfile,
700      const struct GNUNET_CONFIGURATION_Handle *c)
701 {
702   char *armconfig;
703
704   cfg = GNUNET_CONFIGURATION_dup (c);
705   config_file = cfgfile;
706   if (GNUNET_OK !=
707       GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "GNUNET_HOME", &dir))
708   {
709     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
710                                "PATHS", "GNUNET_HOME");
711     return;
712   }
713   if (NULL != cfgfile)
714   {
715     if (GNUNET_OK !=
716         GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG",
717                                                  &armconfig))
718     {
719       GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG",
720                                              cfgfile);
721     }
722     else
723       GNUNET_free (armconfig);
724   }
725   if (NULL == (h = GNUNET_ARM_connect (cfg, &conn_status, NULL)))
726     return;
727   if (monitor)
728     m = GNUNET_ARM_monitor (cfg, &srv_status, NULL);
729   al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
730   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
731 }
732
733
734 /**
735  * The main function to obtain arm from gnunetd.
736  *
737  * @param argc number of arguments from the command line
738  * @param argv command line arguments
739  * @return 0 ok, 1 on error
740  */
741 int
742 main (int argc, char *const *argv)
743 {
744   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
745     {'e', "end", NULL, gettext_noop ("stop all GNUnet services"),
746      GNUNET_NO, &GNUNET_GETOPT_set_one, &end},
747     {'i', "init", "SERVICE", gettext_noop ("start a particular service"),
748      GNUNET_YES, &GNUNET_GETOPT_set_string, &init},
749     {'k', "kill", "SERVICE", gettext_noop ("stop a particular service"),
750      GNUNET_YES, &GNUNET_GETOPT_set_string, &term},
751     {'s', "start", NULL, gettext_noop ("start all GNUnet default services"),
752      GNUNET_NO, &GNUNET_GETOPT_set_one, &start},
753     {'r', "restart", NULL,
754      gettext_noop ("stop and start all GNUnet default services"),
755      GNUNET_NO, &GNUNET_GETOPT_set_one, &restart},
756     {'d', "delete", NULL,
757      gettext_noop ("delete config file and directory on exit"),
758      GNUNET_NO, &GNUNET_GETOPT_set_one, &delete},
759     {'m', "monitor", NULL,
760      gettext_noop ("monitor ARM activities"),
761      GNUNET_NO, &GNUNET_GETOPT_set_one, &monitor},
762     {'q', "quiet", NULL, gettext_noop ("don't print status messages"),
763      GNUNET_NO, &GNUNET_GETOPT_set_one, &quiet},
764     {'T', "timeout", "DELAY",
765      gettext_noop ("exit with error status if operation does not finish after DELAY"),
766      GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &timeout},
767     {'I', "info", NULL, gettext_noop ("list currently running services"),
768      GNUNET_NO, &GNUNET_GETOPT_set_one, &list},
769     {'O', "no-stdout", NULL, gettext_noop ("don't let gnunet-service-arm inherit standard output"),
770      GNUNET_NO, &GNUNET_GETOPT_set_one, &no_stdout},
771     {'E', "no-stderr", NULL, gettext_noop ("don't let gnunet-service-arm inherit standard error"),
772      GNUNET_NO, &GNUNET_GETOPT_set_one, &no_stderr},
773     GNUNET_GETOPT_OPTION_END
774   };
775
776   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
777     return 2;
778
779   if (GNUNET_OK ==
780       GNUNET_PROGRAM_run (argc, argv, "gnunet-arm",
781                           gettext_noop
782                           ("Control services and the Automated Restart Manager (ARM)"),
783                           options, &run, NULL))
784     {
785       GNUNET_free ((void *) argv);
786       return ret;
787     }
788   GNUNET_free ((void*) argv);
789   return 1;
790 }
791
792 /* end of gnunet-arm.c */