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