remove 'illegal' (non-reentrant) log logic from signal handler
[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      SPDX-License-Identifier: AGPL3.0-or-later
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  * Set if we should print all services, including stopped ones.
58  */
59 static int show_all;
60
61 /**
62  * Monitor ARM activity.
63  */
64 static int monitor;
65
66 /**
67  * Set if we should print a list of currently running services.
68  */
69 static int list;
70
71 /**
72  * Set to the name of a service to start.
73  */
74 static char *init;
75
76 /**
77  * Set to the name of a service to kill.
78  */
79 static char *term;
80
81 /**
82  * Set to the name of the config file used.
83  */
84 static char *config_file;
85
86 /**
87  * Set to the directory where runtime files are stored.
88  */
89 static char *dir;
90
91 /**
92  * Final status code.
93  */
94 static int ret;
95
96 /**
97  * Connection with ARM.
98  */
99 static struct GNUNET_ARM_Handle *h;
100
101 /**
102  * Monitor connection with ARM.
103  */
104 static struct GNUNET_ARM_MonitorHandle *m;
105
106 /**
107  * Our configuration.
108  */
109 static struct GNUNET_CONFIGURATION_Handle *cfg;
110
111 /**
112  * Processing stage that we are in.  Simple counter.
113  */
114 static unsigned int phase;
115
116 /**
117  * User defined timestamp for completing operations.
118  */
119 static struct GNUNET_TIME_Relative timeout;
120
121 /**
122  * Task to be run on timeout.
123  */
124 static struct GNUNET_SCHEDULER_Task *timeout_task;
125
126 /**
127  * Do we want to give our stdout to gnunet-service-arm?
128  */
129 static int no_stdout;
130
131 /**
132  * Do we want to give our stderr to gnunet-service-arm?
133  */
134 static int no_stderr;
135
136 /**
137  * Handle for the task running the #action_loop().
138  */
139 static struct GNUNET_SCHEDULER_Task *al_task;
140
141 /**
142  * Current operation.
143  */
144 static struct GNUNET_ARM_Operation *op;
145
146 /**
147  * Attempts to delete configuration file and GNUNET_HOME
148  * on ARM shutdown provided the end and delete options
149  * were specified when gnunet-arm was run.
150  */
151 static void
152 delete_files ()
153 {
154   GNUNET_log (
155     GNUNET_ERROR_TYPE_DEBUG,
156     "Will attempt to remove configuration file %s and service directory %s\n",
157     config_file,
158     dir);
159   if (0 != unlink (config_file))
160   {
161     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
162                 _ ("Failed to remove configuration file %s\n"),
163                 config_file);
164   }
165   if (GNUNET_OK != GNUNET_DISK_directory_remove (dir))
166   {
167     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
168                 _ ("Failed to remove servicehome directory %s\n"),
169                 dir);
170   }
171 }
172
173
174 /**
175  * Main continuation-passing-style loop.  Runs the various
176  * jobs that we've been asked to do in order.
177  *
178  * @param cls closure, unused
179  */
180 static void
181 shutdown_task (void *cls)
182 {
183   (void) cls;
184   if (NULL != al_task)
185   {
186     GNUNET_SCHEDULER_cancel (al_task);
187     al_task = NULL;
188   }
189   if (NULL != op)
190   {
191     GNUNET_ARM_operation_cancel (op);
192     op = NULL;
193   }
194   if (NULL != h)
195   {
196     GNUNET_ARM_disconnect (h);
197     h = NULL;
198   }
199   if (NULL != m)
200   {
201     GNUNET_ARM_monitor_stop (m);
202     m = NULL;
203   }
204   if (NULL != timeout_task)
205   {
206     GNUNET_SCHEDULER_cancel (timeout_task);
207     timeout_task = NULL;
208   }
209   if ( (GNUNET_YES == end) &&
210        (GNUNET_YES == delete) )
211     delete_files ();
212   GNUNET_CONFIGURATION_destroy (cfg);
213   cfg = NULL;
214 }
215
216
217 /**
218  * Returns a string interpretation of @a rs
219  *
220  * @param rs the request status from ARM
221  * @return a string interpretation of the request status
222  */
223 static const char *
224 req_string (enum GNUNET_ARM_RequestStatus rs)
225 {
226   switch (rs)
227   {
228   case GNUNET_ARM_REQUEST_SENT_OK:
229     return _ ("Message was sent successfully");
230
231   case GNUNET_ARM_REQUEST_DISCONNECTED:
232     return _ ("We disconnected from ARM before we could send a request");
233   }
234   return _ ("Unknown request status");
235 }
236
237
238 /**
239  * Returns a string interpretation of the @a result
240  *
241  * @param result the arm result
242  * @return a string interpretation
243  */
244 static const char *
245 ret_string (enum GNUNET_ARM_Result result)
246 {
247   switch (result)
248   {
249   case GNUNET_ARM_RESULT_STOPPED:
250     return _ ("is stopped");
251
252   case GNUNET_ARM_RESULT_STARTING:
253     return _ ("is starting");
254
255   case GNUNET_ARM_RESULT_STOPPING:
256     return _ ("is stopping");
257
258   case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
259     return _ ("is starting already");
260
261   case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
262     return _ ("is stopping already");
263
264   case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
265     return _ ("is started already");
266
267   case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
268     return _ ("is stopped already");
269
270   case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
271     return _ ("service is not known to ARM");
272
273   case GNUNET_ARM_RESULT_START_FAILED:
274     return _ ("service failed to start");
275
276   case GNUNET_ARM_RESULT_IN_SHUTDOWN:
277     return _ ("service cannot be manipulated because ARM is shutting down");
278   }
279   return _ ("Unknown result code.");
280 }
281
282
283 /**
284  * Main task that runs our various operations in order.
285  *
286  * @param cls closure
287  */
288 static void
289 action_loop (void *cls);
290
291
292 /**
293  * Function called whenever we connect to or disconnect from ARM.
294  * Termiantes the process if we fail to connect to the service on
295  * our first attempt.
296  *
297  * @param cls closure
298  * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
299  *                  #GNUNET_SYSERR on error.
300  */
301 static void
302 conn_status (void *cls,
303              int connected)
304 {
305   static int once;
306
307   (void) cls;
308   if ( (GNUNET_SYSERR == connected) &&
309        (0 == once) )
310   {
311     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
312                 _ ("Fatal error initializing ARM API.\n"));
313     GNUNET_SCHEDULER_shutdown ();
314     return;
315   }
316   once = 1;
317 }
318
319
320 /**
321  * We have requested ARM to be started, this function
322  * is called with the result of the operation.  Informs the
323  * use of the result; on success, we continue with the event
324  * loop, on failure we terminate the process.
325  *
326  * @param cls closure unused
327  * @param rs what happened to our request
328  * @param result if the request was processed, this is the result
329  *               according to ARM
330  */
331 static void
332 start_callback (void *cls,
333                 enum GNUNET_ARM_RequestStatus rs,
334                 enum GNUNET_ARM_Result result)
335 {
336   (void) cls;
337   op = NULL;
338   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
339   {
340     fprintf (stdout,
341              _ ("Failed to start the ARM service: %s\n"),
342              req_string (rs));
343     GNUNET_SCHEDULER_shutdown ();
344     return;
345   }
346   if ((GNUNET_ARM_RESULT_STARTING != result) &&
347       (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
348   {
349     fprintf (stdout,
350              _ ("Failed to start the ARM service: %s\n"),
351              ret_string (result));
352     GNUNET_SCHEDULER_shutdown ();
353     return;
354   }
355   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356               "ARM service [re]start successful\n");
357   start = 0;
358   al_task = GNUNET_SCHEDULER_add_now (&action_loop,
359                                       NULL);
360 }
361
362
363 /**
364  * We have requested ARM to be stopped, this function
365  * is called with the result of the operation.  Informs the
366  * use of the result; on success, we continue with the event
367  * loop, on failure we terminate the process.
368  *
369  * @param cls closure unused
370  * @param rs what happened to our request
371  * @param result if the request was processed, this is the result
372  *               according to ARM
373  */
374 static void
375 stop_callback (void *cls,
376                enum GNUNET_ARM_RequestStatus rs,
377                enum GNUNET_ARM_Result result)
378 {
379   char *msg;
380
381   (void) cls;
382   op = NULL;
383   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
384   {
385     GNUNET_asprintf (&msg,
386                      "%s",
387                      _ (
388                        "Failed to send a stop request to the ARM service: %s\n"));
389     fprintf (stdout, msg, req_string (rs));
390     GNUNET_free (msg);
391     GNUNET_SCHEDULER_shutdown ();
392     return;
393   }
394   if ( (GNUNET_ARM_RESULT_STOPPING != result) &&
395        (GNUNET_ARM_RESULT_STOPPED != result) &&
396        (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result) )
397   {
398     fprintf (stdout,
399              _ ("Failed to stop the ARM service: %s\n"),
400              ret_string (result));
401     GNUNET_SCHEDULER_shutdown ();
402     return;
403   }
404   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405               "ARM service shutdown successful\n");
406   end = 0;
407   if (restart)
408   {
409     restart = 0;
410     start = 1;
411     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412                 "Initiating an ARM restart\n");
413   }
414   al_task = GNUNET_SCHEDULER_add_now (&action_loop,
415                                       NULL);
416 }
417
418
419 /**
420  * We have requested a service to be started, this function
421  * is called with the result of the operation.  Informs the
422  * use of the result; on success, we continue with the event
423  * loop, on failure we terminate the process.
424  *
425  * @param cls closure unused
426  * @param rs what happened to our request
427  * @param result if the request was processed, this is the result
428  *               according to ARM
429  */
430 static void
431 init_callback (void *cls,
432                enum GNUNET_ARM_RequestStatus rs,
433                enum GNUNET_ARM_Result result)
434 {
435   (void) cls;
436   op = NULL;
437   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
438   {
439     fprintf (stdout,
440              _ ("Failed to send a request to start the `%s' service: %s\n"),
441              init,
442              req_string (rs));
443     GNUNET_SCHEDULER_shutdown ();
444     return;
445   }
446   if ((GNUNET_ARM_RESULT_STARTING != result) &&
447       (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
448   {
449     fprintf (stdout,
450              _ ("Failed to start the `%s' service: %s\n"),
451              init,
452              ret_string (result));
453     GNUNET_SCHEDULER_shutdown ();
454     return;
455   }
456   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
457               "Service %s [re]started successfully\n",
458               init);
459   GNUNET_free (init);
460   init = NULL;
461   al_task = GNUNET_SCHEDULER_add_now (&action_loop,
462                                       NULL);
463 }
464
465
466 /**
467  * We have requested a service to be stopped, this function
468  * is called with the result of the operation.  Informs the
469  * use of the result; on success, we continue with the event
470  * loop, on failure we terminate the process.
471  *
472  * @param cls closure unused
473  * @param rs what happened to our request
474  * @param result if the request was processed, this is the result
475  *               according to ARM
476  */
477 static void
478 term_callback (void *cls,
479                enum GNUNET_ARM_RequestStatus rs,
480                enum GNUNET_ARM_Result result)
481 {
482   char *msg;
483
484   (void) cls;
485   op = NULL;
486   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
487   {
488     GNUNET_asprintf (&msg,
489                      _ (
490                        "Failed to send a request to kill the `%s' service: %%s\n"),
491                      term);
492     fprintf (stdout,
493              msg,
494              req_string (rs));
495     GNUNET_free (msg);
496     GNUNET_SCHEDULER_shutdown ();
497     return;
498   }
499   if ( (GNUNET_ARM_RESULT_STOPPED != result) &&
500        (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result) )
501   {
502     fprintf (stdout,
503              _ ("Failed to kill the `%s' service: %s\n"),
504              term,
505              ret_string (result));
506     GNUNET_SCHEDULER_shutdown ();
507     return;
508   }
509
510   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
511               "Service %s stopped successfully\n",
512               term);
513   GNUNET_free (term);
514   term = NULL;
515   al_task = GNUNET_SCHEDULER_add_now (&action_loop,
516                                       NULL);
517 }
518
519
520 /**
521  * Function called with the list of running services. Prints
522  * the list to stdout, then starts the event loop again.
523  * Prints an error message and terminates the process on errors.
524  *
525  * @param cls closure (unused)
526  * @param rs request status (success, failure, etc.)
527  * @param count number of services in the list
528  * @param list list of services managed by arm
529  */
530 static void
531 list_callback (void *cls,
532                enum GNUNET_ARM_RequestStatus rs,
533                unsigned int count,
534                const struct GNUNET_ARM_ServiceInfo *list)
535 {
536   unsigned int num_stopped = 0;
537   unsigned int num_started = 0;
538   unsigned int num_stopping = 0;
539   unsigned int num_failed = 0;
540   unsigned int num_finished = 0;
541   (void) cls;
542   op = NULL;
543   if (GNUNET_ARM_REQUEST_SENT_OK != rs)
544   {
545     char *msg;
546
547     GNUNET_asprintf (&msg,
548                      "%s",
549                      _ ("Failed to request a list of services: %s\n"));
550     fprintf (stdout,
551              msg,
552              req_string (rs));
553     GNUNET_free (msg);
554     ret = 3;
555     GNUNET_SCHEDULER_shutdown ();
556   }
557   if (NULL == list)
558   {
559     fprintf (stderr,
560              "%s",
561              _ ("Error communicating with ARM. ARM not running?\n"));
562     GNUNET_SCHEDULER_shutdown ();
563     ret = 3;
564     return;
565   }
566   for (unsigned int i = 0; i < count; i++)
567   {
568     switch (list[i].status)
569     {
570     case GNUNET_ARM_SERVICE_STATUS_STOPPED:
571       num_stopped++;
572       break;
573     case GNUNET_ARM_SERVICE_STATUS_FAILED:
574       num_failed++;
575       break;
576     case GNUNET_ARM_SERVICE_STATUS_FINISHED:
577       num_finished++;
578       break;
579     case GNUNET_ARM_SERVICE_STATUS_STARTED:
580       num_started++;
581       break;
582     case GNUNET_ARM_SERVICE_STATUS_STOPPING:
583       num_stopping++;
584       fprintf (stdout,
585                "%s (binary='%s', status=stopping)\n",
586                list[i].name,
587                list[i].binary);
588       break;
589     default:
590       GNUNET_break_op (0);
591       fprintf (stdout,
592                "%s (binary='%s', status=unknown)\n",
593                list[i].name,
594                list[i].binary);
595       break;
596     }
597   }
598   if (! quiet)
599   {
600     if (show_all)
601       fprintf (stdout,
602                "%s",
603                _ ("All services:\n"));
604     else
605       fprintf (stdout,
606                "%s",
607                _ ("Services (excluding stopped services):\n"));
608     if (num_stopped || num_failed || num_finished || num_stopping ||
609         num_started)
610     {
611       int sep = 0;
612       fprintf (stdout, "(");
613       if (0 != num_started)
614       {
615         if (sep)
616           fprintf (stdout, " / ");
617         fprintf (stdout,
618                  "started: %u",
619                  num_started);
620         sep = 1;
621       }
622       if (0 != num_failed)
623       {
624         if (sep)
625           fprintf (stdout, " / ");
626         fprintf (stdout,
627                  "failed: %u",
628                  num_failed);
629         sep = 1;
630       }
631       if (0 != num_stopping)
632       {
633         if (sep)
634           fprintf (stdout, " / ");
635         fprintf (stdout,
636                  "stopping: %u",
637                  num_stopping);
638         sep = 1;
639       }
640       if (0 != num_stopped)
641       {
642         if (sep)
643           fprintf (stdout, " / ");
644         fprintf (stdout,
645                  "stopped: %u",
646                  num_stopped);
647         sep = 1;
648       }
649       if (0 != num_finished)
650       {
651         if (sep)
652           fprintf (stdout, " / ");
653         fprintf (stdout,
654                  "finished: %u",
655                  num_finished);
656         sep = 1;
657       }
658       fprintf (stdout, ")\n");
659     }
660     else
661     {
662       fprintf (stdout,
663                "%s",
664                _ ("(No services configured.)\n"));
665     }
666   }
667   for (unsigned int i = 0; i < count; i++)
668   {
669     struct GNUNET_TIME_Relative restart_in;
670     switch (list[i].status)
671     {
672     case GNUNET_ARM_SERVICE_STATUS_STOPPED:
673       if (show_all)
674         fprintf (stdout,
675                  "%s (binary='%s', status=stopped)\n",
676                  list[i].name,
677                  list[i].binary);
678       break;
679     case GNUNET_ARM_SERVICE_STATUS_FAILED:
680       restart_in = GNUNET_TIME_absolute_get_remaining (list[i].restart_at);
681       fprintf (stdout,
682                "%s (binary='%s', status=failed, exit_status=%d, restart_delay='%s')\n",
683                list[i].name,
684                list[i].binary,
685                list[i].last_exit_status,
686                GNUNET_STRINGS_relative_time_to_string (restart_in,
687                                                        GNUNET_YES));
688       break;
689     case GNUNET_ARM_SERVICE_STATUS_FINISHED:
690       fprintf (stdout,
691                "%s (binary='%s', status=finished)\n",
692                list[i].name,
693                list[i].binary);
694       break;
695     case GNUNET_ARM_SERVICE_STATUS_STARTED:
696       fprintf (stdout,
697                "%s (binary='%s', status=started)\n",
698                list[i].name,
699                list[i].binary);
700       break;
701     case GNUNET_ARM_SERVICE_STATUS_STOPPING:
702       fprintf (stdout,
703                "%s (binary='%s', status=stopping)\n",
704                list[i].name,
705                list[i].binary);
706       break;
707     default:
708       GNUNET_break_op (0);
709       fprintf (stdout,
710                "%s (binary='%s', status=unknown)\n",
711                list[i].name,
712                list[i].binary);
713       break;
714     }
715   }
716   al_task = GNUNET_SCHEDULER_add_now (&action_loop,
717                                       NULL);
718 }
719
720
721 /**
722  * Main action loop.  Runs the various jobs that we've been asked to
723  * do, in order.
724  *
725  * @param cls closure, unused
726  */
727 static void
728 action_loop (void *cls)
729 {
730   (void) cls;
731   al_task = NULL;
732   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733               "Running requested actions\n");
734   while (1)
735   {
736     switch (phase++)
737     {
738     case 0:
739       if (NULL != term)
740       {
741         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
742                     "Termination action\n");
743         op = GNUNET_ARM_request_service_stop (h,
744                                               term,
745                                               &term_callback,
746                                               NULL);
747         return;
748       }
749       break;
750
751     case 1:
752       if (end || restart)
753       {
754         if (GNUNET_YES !=
755             GNUNET_CLIENT_test (cfg,
756                                 "arm"))
757         {
758           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
759                       "GNUnet not running, cannot stop the peer\n");
760         }
761         else
762         {
763           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764                       "End action\n");
765           op = GNUNET_ARM_request_service_stop (h,
766                                                 "arm",
767                                                 &stop_callback,
768                                                 NULL);
769           return;
770         }
771       }
772       break;
773
774     case 2:
775       if (start)
776       {
777         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
778                     "Start action\n");
779         op =
780           GNUNET_ARM_request_service_start (h,
781                                             "arm",
782                                             (no_stdout
783                                              ? 0
784                                              : GNUNET_OS_INHERIT_STD_OUT)
785                                             | (no_stderr
786                                                ? 0
787                                                : GNUNET_OS_INHERIT_STD_ERR),
788                                             &start_callback,
789                                             NULL);
790         return;
791       }
792       break;
793
794     case 3:
795       if (NULL != init)
796       {
797         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798                     "Initialization action\n");
799         op = GNUNET_ARM_request_service_start (h,
800                                                init,
801                                                GNUNET_OS_INHERIT_STD_NONE,
802                                                &init_callback,
803                                                NULL);
804         return;
805       }
806       break;
807
808     case 4:
809       if (list)
810       {
811         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812                     "Going to list all running services controlled by ARM.\n");
813         op = GNUNET_ARM_request_service_list (h,
814                                               &list_callback,
815                                               &list);
816         return;
817       }
818       break;
819
820     case 5:
821       if (monitor)
822       {
823         if (! quiet)
824           fprintf (stderr,
825                    _ ("Now only monitoring, press CTRL-C to stop.\n"));
826         quiet =
827           0;       /* does not make sense to stay quiet in monitor mode at this time */
828         return;       /* done with tasks, just monitor */
829       }
830       break;
831
832     default:     /* last phase */
833       GNUNET_SCHEDULER_shutdown ();
834       return;
835     }
836   }
837 }
838
839
840 /**
841  * Function called when a service starts or stops.
842  *
843  * @param cls closure
844  * @param service service name
845  * @param status status of the service
846  */
847 static void
848 srv_status (void *cls,
849             const char *service,
850             enum GNUNET_ARM_ServiceMonitorStatus status)
851 {
852   const char *msg;
853
854   (void) cls;
855   switch (status)
856   {
857   case GNUNET_ARM_SERVICE_MONITORING_STARTED:
858     return;   /* this should be done silently */
859
860   case GNUNET_ARM_SERVICE_STOPPED:
861     msg = _ ("Stopped %s.\n");
862     break;
863
864   case GNUNET_ARM_SERVICE_STARTING:
865     msg = _ ("Starting %s...\n");
866     break;
867
868   case GNUNET_ARM_SERVICE_STOPPING:
869     msg = _ ("Stopping %s...\n");
870     break;
871
872   default:
873     msg = NULL;
874     break;
875   }
876   if (! quiet)
877   {
878     if (NULL != msg)
879       fprintf (stderr,
880                msg,
881                service);
882     else
883       fprintf (stderr,
884                _ ("Unknown status %u for service %s.\n"),
885                status,
886                service);
887   }
888   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889               "Got service %s status %d\n",
890               service,
891               (int) status);
892 }
893
894
895 /**
896  * Task run on timeout (if -T is given).
897  */
898 static void
899 timeout_task_cb (void *cls)
900 {
901   (void) cls;
902   timeout_task = NULL;
903   ret = 2;
904   GNUNET_SCHEDULER_shutdown ();
905 }
906
907
908 /**
909  * Main function that will be run by the scheduler.
910  *
911  * @param cls closure
912  * @param args remaining command-line arguments
913  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
914  * @param c configuration
915  */
916 static void
917 run (void *cls,
918      char *const *args,
919      const char *cfgfile,
920      const struct GNUNET_CONFIGURATION_Handle *c)
921 {
922   (void) cls;
923   (void) args;
924   (void) cfgfile;
925   cfg = GNUNET_CONFIGURATION_dup (c);
926   if (GNUNET_OK !=
927       GNUNET_CONFIGURATION_get_value_string (cfg,
928                                              "PATHS",
929                                              "GNUNET_HOME",
930                                              &dir))
931   {
932     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
933                                "PATHS",
934                                "GNUNET_HOME");
935     return;
936   }
937   (void) GNUNET_CONFIGURATION_get_value_filename (cfg,
938                                                   "arm",
939                                                   "CONFIG",
940                                                   &config_file);
941   if (NULL == (h = GNUNET_ARM_connect (cfg,
942                                        &conn_status,
943                                        NULL)))
944     return;
945   if (monitor)
946     m = GNUNET_ARM_monitor_start (cfg,
947                                   &srv_status,
948                                   NULL);
949   al_task = GNUNET_SCHEDULER_add_now (&action_loop,
950                                       NULL);
951   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
952                                  NULL);
953   if (0 != timeout.rel_value_us)
954     timeout_task =
955       GNUNET_SCHEDULER_add_delayed (timeout,
956                                     &timeout_task_cb,
957                                     NULL);
958 }
959
960
961 /**
962  * The main function to obtain arm from gnunetd.
963  *
964  * @param argc number of arguments from the command line
965  * @param argv command line arguments
966  * @return 0 ok, 1 on error, 2 on timeout
967  */
968 int
969 main (int argc, char *const *argv)
970 {
971   struct GNUNET_GETOPT_CommandLineOption options[] = {
972     GNUNET_GETOPT_option_flag ('e',
973                                "end",
974                                gettext_noop ("stop all GNUnet services"),
975                                &end),
976     GNUNET_GETOPT_option_string ('i',
977                                  "init",
978                                  "SERVICE",
979                                  gettext_noop ("start a particular service"),
980                                  &init),
981     GNUNET_GETOPT_option_string ('k',
982                                  "kill",
983                                  "SERVICE",
984                                  gettext_noop ("stop a particular service"),
985                                  &term),
986     GNUNET_GETOPT_option_flag ('a',
987                                "all",
988                                gettext_noop (
989                                  "also show stopped services (used with -I)"),
990                                &show_all),
991     GNUNET_GETOPT_option_flag ('s',
992                                "start",
993                                gettext_noop (
994                                  "start all GNUnet default services"),
995                                &start),
996     GNUNET_GETOPT_option_flag ('r',
997                                "restart",
998                                gettext_noop (
999                                  "stop and start all GNUnet default services"),
1000                                &restart),
1001     GNUNET_GETOPT_option_flag ('d',
1002                                "delete",
1003                                gettext_noop (
1004                                  "delete config file and directory on exit"),
1005                                &delete),
1006     GNUNET_GETOPT_option_flag ('m',
1007                                "monitor",
1008                                gettext_noop ("monitor ARM activities"),
1009                                &monitor),
1010     GNUNET_GETOPT_option_flag ('q',
1011                                "quiet",
1012                                gettext_noop ("don't print status messages"),
1013                                &quiet),
1014     GNUNET_GETOPT_option_relative_time (
1015       'T',
1016       "timeout",
1017       "DELAY",
1018       gettext_noop (
1019         "exit with error status if operation does not finish after DELAY"),
1020       &timeout),
1021     GNUNET_GETOPT_option_flag ('I',
1022                                "info",
1023                                gettext_noop (
1024                                  "list currently running services"),
1025                                &list),
1026     GNUNET_GETOPT_option_flag (
1027       'O',
1028       "no-stdout",
1029       gettext_noop ("don't let gnunet-service-arm inherit standard output"),
1030       &no_stdout),
1031     GNUNET_GETOPT_option_flag (
1032       'E',
1033       "no-stderr",
1034       gettext_noop ("don't let gnunet-service-arm inherit standard error"),
1035       &no_stderr),
1036     GNUNET_GETOPT_OPTION_END
1037   };
1038   int lret;
1039
1040   if (GNUNET_OK !=
1041       GNUNET_STRINGS_get_utf8_args (argc,
1042                                     argv,
1043                                     &argc,
1044                                     &argv))
1045     return 2;
1046   if (GNUNET_OK ==
1047       (lret = GNUNET_PROGRAM_run (
1048          argc,
1049          argv,
1050          "gnunet-arm",
1051          gettext_noop (
1052            "Control services and the Automated Restart Manager (ARM)"),
1053          options,
1054          &run,
1055          NULL)))
1056   {
1057     GNUNET_free ((void *) argv);
1058     return ret;
1059   }
1060   GNUNET_free ((void *) argv);
1061   return lret;
1062 }
1063
1064
1065 /* end of gnunet-arm.c */