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 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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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 * Set if we are to shutdown all services (including ARM).
37 * Set if we are to start default services (including ARM).
42 * Set if we are to stop/start default services (including ARM).
47 * Set if we should delete configuration and temp directory on exit.
52 * Set if we should not print status messages.
57 * Monitor ARM activity.
62 * Set if we should print a list of currently running services.
67 * Set to the name of a service to start.
72 * Set to the name of a service to kill.
77 * Set to the name of the config file used.
79 static char *config_file;
82 * Set to the directory where runtime files are stored.
92 * Connection with ARM.
94 static struct GNUNET_ARM_Handle *h;
97 * Monitor connection with ARM.
99 static struct GNUNET_ARM_MonitorHandle *m;
104 static struct GNUNET_CONFIGURATION_Handle *cfg;
107 * Processing stage that we are in. Simple counter.
109 static unsigned int phase;
112 * User defined timestamp for completing operations.
114 static struct GNUNET_TIME_Relative timeout;
117 * Task to be run on timeout.
119 static struct GNUNET_SCHEDULER_Task *timeout_task;
122 * Do we want to give our stdout to gnunet-service-arm?
124 static int no_stdout;
127 * Do we want to give our stderr to gnunet-service-arm?
129 static int no_stderr;
132 * Handle for the task running the #action_loop().
134 static struct GNUNET_SCHEDULER_Task *al_task;
139 static struct GNUNET_ARM_Operation *op;
142 * Attempts to delete configuration file and GNUNET_HOME
143 * on ARM shutdown provided the end and delete options
144 * were specified when gnunet-arm was run.
150 GNUNET_ERROR_TYPE_DEBUG,
151 "Will attempt to remove configuration file %s and service directory %s\n",
154 if (0 != UNLINK (config_file))
156 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
157 _ ("Failed to remove configuration file %s\n"),
160 if (GNUNET_OK != GNUNET_DISK_directory_remove (dir))
162 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
163 _ ("Failed to remove servicehome directory %s\n"),
170 * Main continuation-passing-style loop. Runs the various
171 * jobs that we've been asked to do in order.
173 * @param cls closure, unused
176 shutdown_task (void *cls)
181 GNUNET_SCHEDULER_cancel (al_task);
186 GNUNET_ARM_operation_cancel (op);
191 GNUNET_ARM_disconnect (h);
196 GNUNET_ARM_monitor_stop (m);
199 if (NULL != timeout_task)
201 GNUNET_SCHEDULER_cancel (timeout_task);
204 if ((GNUNET_YES == end) && (GNUNET_YES == delete))
206 GNUNET_CONFIGURATION_destroy (cfg);
212 * Returns a string interpretation of @a rs
214 * @param rs the request status from ARM
215 * @return a string interpretation of the request status
218 req_string (enum GNUNET_ARM_RequestStatus rs)
222 case GNUNET_ARM_REQUEST_SENT_OK:
223 return _ ("Message was sent successfully");
224 case GNUNET_ARM_REQUEST_DISCONNECTED:
225 return _ ("We disconnected from ARM before we could send a request");
227 return _ ("Unknown request status");
232 * Returns a string interpretation of the @a result
234 * @param result the arm result
235 * @return a string interpretation
238 ret_string (enum GNUNET_ARM_Result result)
242 case GNUNET_ARM_RESULT_STOPPED:
243 return _ ("is stopped");
244 case GNUNET_ARM_RESULT_STARTING:
245 return _ ("is starting");
246 case GNUNET_ARM_RESULT_STOPPING:
247 return _ ("is stopping");
248 case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
249 return _ ("is starting already");
250 case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
251 return _ ("is stopping already");
252 case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
253 return _ ("is started already");
254 case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
255 return _ ("is stopped already");
256 case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
257 return _ ("service is not known to ARM");
258 case GNUNET_ARM_RESULT_START_FAILED:
259 return _ ("service failed to start");
260 case GNUNET_ARM_RESULT_IN_SHUTDOWN:
261 return _ ("service cannot be manipulated because ARM is shutting down");
263 return _ ("Unknown result code.");
268 * Main task that runs our various operations in order.
273 action_loop (void *cls);
277 * Function called whenever we connect to or disconnect from ARM.
278 * Termiantes the process if we fail to connect to the service on
282 * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
283 * #GNUNET_SYSERR on error.
286 conn_status (void *cls, int connected)
291 if ((GNUNET_SYSERR == connected) && (0 == once))
293 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
294 _ ("Fatal error initializing ARM API.\n"));
295 GNUNET_SCHEDULER_shutdown ();
303 * We have requested ARM to be started, this function
304 * is called with the result of the operation. Informs the
305 * use of the result; on success, we continue with the event
306 * loop, on failure we terminate the process.
308 * @param cls closure unused
309 * @param rs what happened to our request
310 * @param result if the request was processed, this is the result
314 start_callback (void *cls,
315 enum GNUNET_ARM_RequestStatus rs,
316 enum GNUNET_ARM_Result result)
320 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
323 _ ("Failed to start the ARM service: %s\n"),
325 GNUNET_SCHEDULER_shutdown ();
328 if ((GNUNET_ARM_RESULT_STARTING != result) &&
329 (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
332 _ ("Failed to start the ARM service: %s\n"),
333 ret_string (result));
334 GNUNET_SCHEDULER_shutdown ();
337 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM service [re]start successful\n");
339 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
344 * We have requested ARM to be stopped, this function
345 * is called with the result of the operation. Informs the
346 * use of the result; on success, we continue with the event
347 * loop, on failure we terminate the process.
349 * @param cls closure unused
350 * @param rs what happened to our request
351 * @param result if the request was processed, this is the result
355 stop_callback (void *cls,
356 enum GNUNET_ARM_RequestStatus rs,
357 enum GNUNET_ARM_Result result)
363 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
365 GNUNET_asprintf (&msg,
368 "Failed to send a stop request to the ARM service: %s\n"));
369 FPRINTF (stdout, msg, req_string (rs));
371 GNUNET_SCHEDULER_shutdown ();
374 if ((GNUNET_ARM_RESULT_STOPPING != result) &&
375 (GNUNET_ARM_RESULT_STOPPED != result) &&
376 (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result))
379 _ ("Failed to stop the ARM service: %s\n"),
380 ret_string (result));
381 GNUNET_SCHEDULER_shutdown ();
384 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM service shutdown successful\n");
390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initiating an ARM restart\n");
392 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
397 * We have requested a service to be started, this function
398 * is called with the result of the operation. Informs the
399 * use of the result; on success, we continue with the event
400 * loop, on failure we terminate the process.
402 * @param cls closure unused
403 * @param rs what happened to our request
404 * @param result if the request was processed, this is the result
408 init_callback (void *cls,
409 enum GNUNET_ARM_RequestStatus rs,
410 enum GNUNET_ARM_Result result)
414 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
417 _ ("Failed to send a request to start the `%s' service: %s\n"),
420 GNUNET_SCHEDULER_shutdown ();
423 if ((GNUNET_ARM_RESULT_STARTING != result) &&
424 (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
427 _ ("Failed to start the `%s' service: %s\n"),
429 ret_string (result));
430 GNUNET_SCHEDULER_shutdown ();
433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434 "Service %s [re]started successfully\n",
438 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
443 * We have requested a service to be stopped, this function
444 * is called with the result of the operation. Informs the
445 * use of the result; on success, we continue with the event
446 * loop, on failure we terminate the process.
448 * @param cls closure unused
449 * @param rs what happened to our request
450 * @param result if the request was processed, this is the result
454 term_callback (void *cls,
455 enum GNUNET_ARM_RequestStatus rs,
456 enum GNUNET_ARM_Result result)
462 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
464 GNUNET_asprintf (&msg,
466 "Failed to send a request to kill the `%s' service: %%s\n"),
468 FPRINTF (stdout, msg, req_string (rs));
470 GNUNET_SCHEDULER_shutdown ();
473 if ((GNUNET_ARM_RESULT_STOPPED != result) &&
474 (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY != result))
477 _ ("Failed to kill the `%s' service: %s\n"),
479 ret_string (result));
480 GNUNET_SCHEDULER_shutdown ();
484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485 "Service %s stopped successfully\n",
489 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
494 * Function called with the list of running services. Prints
495 * the list to stdout, then starts the event loop again.
496 * Prints an error message and terminates the process on errors.
498 * @param cls closure (unused)
499 * @param rs request status (success, failure, etc.)
500 * @param count number of services in the list
501 * @param list list of services that are running
504 list_callback (void *cls,
505 enum GNUNET_ARM_RequestStatus rs,
507 const char *const *list)
511 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
515 GNUNET_asprintf (&msg,
517 _ ("Failed to request a list of services: %s\n"));
518 FPRINTF (stdout, msg, req_string (rs));
521 GNUNET_SCHEDULER_shutdown ();
527 _ ("Error communicating with ARM. ARM not running?\n"));
528 GNUNET_SCHEDULER_shutdown ();
533 FPRINTF (stdout, "%s", _ ("Running services:\n"));
534 for (unsigned int i = 0; i < count; i++)
535 FPRINTF (stdout, "%s\n", list[i]);
536 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
541 * Main action loop. Runs the various jobs that we've been asked to
544 * @param cls closure, unused
547 action_loop (void *cls)
551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running requested actions\n");
559 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Termination action\n");
560 op = GNUNET_ARM_request_service_stop (h, term, &term_callback, NULL);
567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "End action\n");
568 op = GNUNET_ARM_request_service_stop (h, "arm", &stop_callback, NULL);
575 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start action\n");
577 GNUNET_ARM_request_service_start (h,
581 : GNUNET_OS_INHERIT_STD_OUT) |
584 : GNUNET_OS_INHERIT_STD_ERR),
593 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initialization action\n");
594 op = GNUNET_ARM_request_service_start (h,
596 GNUNET_OS_INHERIT_STD_NONE,
605 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606 "Going to list all running services controlled by ARM.\n");
607 op = GNUNET_ARM_request_service_list (h, &list_callback, &list);
615 fprintf (stderr, _ ("Now only monitoring, press CTRL-C to stop.\n"));
617 0; /* does not make sense to stay quiet in monitor mode at this time */
618 return; /* done with tasks, just monitor */
621 default: /* last phase */
622 GNUNET_SCHEDULER_shutdown ();
630 * Function called when a service starts or stops.
633 * @param service service name
634 * @param status status of the service
637 srv_status (void *cls,
639 enum GNUNET_ARM_ServiceStatus status)
646 case GNUNET_ARM_SERVICE_MONITORING_STARTED:
647 return; /* this should be done silently */
648 case GNUNET_ARM_SERVICE_STOPPED:
649 msg = _ ("Stopped %s.\n");
651 case GNUNET_ARM_SERVICE_STARTING:
652 msg = _ ("Starting %s...\n");
654 case GNUNET_ARM_SERVICE_STOPPING:
655 msg = _ ("Stopping %s...\n");
664 FPRINTF (stderr, msg, service);
667 _ ("Unknown status %u for service %s.\n"),
671 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
672 "Got service %s status %d\n",
679 * Task run on timeout (if -T is given).
682 timeout_task_cb (void *cls)
687 GNUNET_SCHEDULER_shutdown ();
692 * Main function that will be run by the scheduler.
695 * @param args remaining command-line arguments
696 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
697 * @param c configuration
703 const struct GNUNET_CONFIGURATION_Handle *c)
708 cfg = GNUNET_CONFIGURATION_dup (c);
710 GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "GNUNET_HOME", &dir))
712 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "PATHS", "GNUNET_HOME");
715 (void) GNUNET_CONFIGURATION_get_value_filename (cfg,
719 if (NULL == (h = GNUNET_ARM_connect (cfg, &conn_status, NULL)))
722 m = GNUNET_ARM_monitor_start (cfg, &srv_status, NULL);
723 al_task = GNUNET_SCHEDULER_add_now (&action_loop, NULL);
724 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
725 if (0 != timeout.rel_value_us)
727 GNUNET_SCHEDULER_add_delayed (timeout, &timeout_task_cb, NULL);
732 * The main function to obtain arm from gnunetd.
734 * @param argc number of arguments from the command line
735 * @param argv command line arguments
736 * @return 0 ok, 1 on error, 2 on timeout
739 main (int argc, char *const *argv)
741 struct GNUNET_GETOPT_CommandLineOption options[] =
742 {GNUNET_GETOPT_option_flag ('e',
744 gettext_noop ("stop all GNUnet services"),
746 GNUNET_GETOPT_option_string ('i',
749 gettext_noop ("start a particular service"),
751 GNUNET_GETOPT_option_string ('k',
754 gettext_noop ("stop a particular service"),
756 GNUNET_GETOPT_option_flag ('s',
759 "start all GNUnet default services"),
761 GNUNET_GETOPT_option_flag ('r',
764 "stop and start all GNUnet default services"),
766 GNUNET_GETOPT_option_flag ('d',
769 "delete config file and directory on exit"),
771 GNUNET_GETOPT_option_flag ('m',
773 gettext_noop ("monitor ARM activities"),
775 GNUNET_GETOPT_option_flag ('q',
777 gettext_noop ("don't print status messages"),
779 GNUNET_GETOPT_option_relative_time (
784 "exit with error status if operation does not finish after DELAY"),
786 GNUNET_GETOPT_option_flag ('I',
789 "list currently running services"),
791 GNUNET_GETOPT_option_flag (
794 gettext_noop ("don't let gnunet-service-arm inherit standard output"),
796 GNUNET_GETOPT_option_flag (
799 gettext_noop ("don't let gnunet-service-arm inherit standard error"),
801 GNUNET_GETOPT_OPTION_END};
804 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
807 (lret = GNUNET_PROGRAM_run (
812 "Control services and the Automated Restart Manager (ARM)"),
817 GNUNET_free ((void *) argv);
820 GNUNET_free ((void *) argv);
824 /* end of gnunet-arm.c */