2 This file is part of GNUnet.
3 Copyright (C) 2009, 2012, 2013 GNUnet e.V.
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.
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.
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.
22 * @file arm/gnunet-arm.c
23 * @brief arm for writing a tool
24 * @author Christian Grothoff
27 #include "gnunet_arm_service.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_util_lib.h"
32 * Timeout for stopping services. Long to give some services a real chance.
34 #define STOP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 1)
37 * Timeout for stopping ARM. Extra-long since ARM needs to stop everyone else.
39 #define STOP_TIMEOUT_ARM GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
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)).
46 #define START_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
49 * Timeout for listing all running services.
51 #define LIST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
54 * Set if we are to shutdown all services (including ARM).
59 * Set if we are to start default services (including ARM).
64 * Set if we are to stop/start default services (including ARM).
69 * Set if we should delete configuration and temp directory on exit.
74 * Set if we should not print status messages.
79 * Monitor ARM activity.
84 * Set if we should print a list of currently running services.
89 * Set to the name of a service to start.
94 * Set to the name of a service to kill.
99 * Set to the name of the config file used.
101 static const char *config_file;
104 * Set to the directory where runtime files are stored.
114 * Connection with ARM.
116 static struct GNUNET_ARM_Handle *h;
119 * Monitor connection with ARM.
121 static struct GNUNET_ARM_MonitorHandle *m;
126 static struct GNUNET_CONFIGURATION_Handle *cfg;
129 * Processing stage that we are in. Simple counter.
131 static unsigned int phase;
134 * User defined timestamp for completing operations.
136 static struct GNUNET_TIME_Relative timeout;
139 * Do we want to give our stdout to gnunet-service-arm?
141 static unsigned int no_stdout;
144 * Do we want to give our stderr to gnunet-service-arm?
146 static unsigned int no_stderr;
149 * Handle for the task running the #action_loop().
151 static struct GNUNET_SCHEDULER_Task *al_task;
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.
162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163 "Will attempt to remove configuration file %s and service directory %s\n",
166 if (0 != UNLINK (config_file))
168 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
169 _("Failed to remove configuration file %s\n"),
172 if (GNUNET_OK != GNUNET_DISK_directory_remove (dir))
174 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
175 _("Failed to remove servicehome directory %s\n"),
183 * Main continuation-passing-style loop. Runs the various
184 * jobs that we've been asked to do in order.
186 * @param cls closure, unused
189 shutdown_task (void *cls)
193 GNUNET_SCHEDULER_cancel (al_task);
198 GNUNET_ARM_disconnect_and_free (h);
203 GNUNET_ARM_monitor_disconnect_and_free (m);
206 if ((GNUNET_YES == end) && (GNUNET_YES == delete))
208 GNUNET_CONFIGURATION_destroy (cfg);
214 * Returns a string interpretation of 'rs'
216 * @param rs the request status from ARM
217 * @return a string interpretation of the request status
220 req_string (enum GNUNET_ARM_RequestStatus rs)
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");
237 return _("Unknown request status");
242 * Returns a string interpretation of the 'result'
244 * @param result the arm result
245 * @return a string interpretation
248 ret_string (enum GNUNET_ARM_Result result)
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");
273 return _("%.s Unknown result code.");
278 * Main task that runs our various operations in order.
283 action_loop (void *cls);
287 * Function called whenever we connect to or disconnect from ARM.
288 * Termiantes the process if we fail to connect to the service on
292 * @param connected GNUNET_YES if connected, GNUNET_NO if disconnected,
293 * GNUNET_SYSERR on error.
296 conn_status (void *cls,
301 if ( (GNUNET_SYSERR == connected) &&
304 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
305 _("Fatal error initializing ARM API.\n"));
306 GNUNET_SCHEDULER_shutdown ();
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.
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
326 start_callback (void *cls,
327 enum GNUNET_ARM_RequestStatus rs,
329 enum GNUNET_ARM_Result result)
333 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
335 GNUNET_asprintf (&msg, "%s", _("Failed to start the ARM service: %s\n"));
336 FPRINTF (stdout, msg, req_string (rs));
338 GNUNET_SCHEDULER_shutdown ();
341 if ( (GNUNET_ARM_RESULT_STARTING != result) &&
342 (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result) )
344 GNUNET_asprintf (&msg, "%s", _("Failed to start the ARM service: %s\n"));
345 FPRINTF (stdout, msg, ret_string (result));
347 GNUNET_SCHEDULER_shutdown ();
350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
351 "ARM service [re]start successful\n");
353 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
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.
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
370 stop_callback (void *cls,
371 enum GNUNET_ARM_RequestStatus rs,
373 enum GNUNET_ARM_Result result)
377 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
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));
383 GNUNET_SCHEDULER_shutdown ();
386 if ((GNUNET_ARM_RESULT_STOPPING != result) &&
387 (GNUNET_ARM_RESULT_STOPPED != result) &&
388 (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result))
390 GNUNET_asprintf (&msg, "%s",
391 _("Failed to stop the ARM service: %s\n"));
392 FPRINTF (stdout, msg, ret_string (result));
394 GNUNET_SCHEDULER_shutdown ();
397 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
398 "ARM service shutdown successful\n");
404 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405 "Initiating an ARM restart\n");
407 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
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.
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
424 init_callback (void *cls,
425 enum GNUNET_ARM_RequestStatus rs,
427 enum GNUNET_ARM_Result result)
431 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
433 GNUNET_asprintf (&msg,
434 _("Failed to send a request to start the `%s' service: %%s\n"),
436 FPRINTF (stdout, msg, req_string (rs));
438 GNUNET_SCHEDULER_shutdown ();
441 if ((GNUNET_ARM_RESULT_STARTING != result) &&
442 (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
444 GNUNET_asprintf (&msg, _("Failed to start the `%s' service: %s\n"),
445 init, ret_string (result));
446 FPRINTF (stdout, msg, service);
448 GNUNET_SCHEDULER_shutdown ();
451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452 "Service %s [re]started successfully\n",
456 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
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.
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
473 term_callback (void *cls,
474 enum GNUNET_ARM_RequestStatus rs,
476 enum GNUNET_ARM_Result result)
479 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
481 GNUNET_asprintf (&msg,
482 _("Failed to send a request to kill the `%s' service: %%s\n"),
484 FPRINTF (stdout, msg, req_string (rs));
486 GNUNET_SCHEDULER_shutdown ();
489 if ((GNUNET_ARM_RESULT_STOPPED != result) &&
490 (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result))
492 GNUNET_asprintf (&msg,
493 _("Failed to kill the `%s' service: %s\n"),
494 term, ret_string (result));
495 FPRINTF (stdout, msg, service);
497 GNUNET_SCHEDULER_shutdown ();
501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
502 "Service %s stopped successfully\n",
506 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
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.
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
521 list_callback (void *cls,
522 enum GNUNET_ARM_RequestStatus rs,
524 const char *const*list)
528 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
532 GNUNET_asprintf (&msg, "%s",
533 _("Failed to request a list of services: %s\n"));
534 FPRINTF (stdout, msg, req_string (rs));
537 GNUNET_SCHEDULER_shutdown ();
541 FPRINTF (stderr, "%s",
542 _("Error communicating with ARM. ARM not running?\n"));
543 GNUNET_SCHEDULER_shutdown ();
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);
555 * Main action loop. Runs the various jobs that we've been asked to
558 * @param cls closure, unused
561 action_loop (void *cls)
564 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565 "Running requested actions\n");
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);
584 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
586 GNUNET_ARM_request_service_stop (h, "arm",
587 (0 == timeout.rel_value_us) ? STOP_TIMEOUT_ARM : timeout,
588 &stop_callback, NULL);
595 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
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);
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);
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);
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 */
638 default: /* last phase */
639 GNUNET_SCHEDULER_shutdown ();
647 * Function called when a service starts or stops.
650 * @param service service name
651 * @param status status of the service
654 srv_status (void *cls,
656 enum GNUNET_ARM_ServiceStatus status)
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");
667 case GNUNET_ARM_SERVICE_STARTING:
668 msg = _("Starting %s...\n");
670 case GNUNET_ARM_SERVICE_STOPPING:
671 msg = _("Stopping %s...\n");
680 FPRINTF (stderr, msg, service);
682 FPRINTF (stderr, _("Unknown status %u for service %s.\n"), status, service);
684 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got service %s status %d\n", service, (int) status);
689 * Main function that will be run by the scheduler.
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
700 const struct GNUNET_CONFIGURATION_Handle *c)
704 cfg = GNUNET_CONFIGURATION_dup (c);
705 config_file = cfgfile;
707 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "GNUNET_HOME", &dir))
709 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
710 "PATHS", "GNUNET_HOME");
716 GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG",
719 GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG",
723 GNUNET_free (armconfig);
725 if (NULL == (h = GNUNET_ARM_connect (cfg, &conn_status, NULL)))
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);
735 * The main function to obtain arm from gnunetd.
737 * @param argc number of arguments from the command line
738 * @param argv command line arguments
739 * @return 0 ok, 1 on error
742 main (int argc, char *const *argv)
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
776 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
780 GNUNET_PROGRAM_run (argc, argv, "gnunet-arm",
782 ("Control services and the Automated Restart Manager (ARM)"),
783 options, &run, NULL))
785 GNUNET_free ((void *) argv);
788 GNUNET_free ((void*) argv);
792 /* end of gnunet-arm.c */