7ad3e48e7a0a607af04b73cc6eda6cfe00c31aff
[oweals/gnunet.git] / src / arm / gnunet-arm.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 Christian Grothoff (and other contributing authors)
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 2, 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, 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_client_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_program_lib.h"
31 #include "gnunet_time_lib.h"
32
33 /**
34  * Timeout for stopping services.  Long to give some services a real chance.
35  */
36 #define STOP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 360)
37
38 /**
39  * Timeout for starting services, very short because of the strange way start works
40  * (by checking if running before starting, so really this time is always waited on
41  * startup (annoying)).
42  */
43 #define START_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 50)
44
45 /**
46  * Set if we are to shutdown all services (including ARM).
47  */
48 static int end;
49
50 /**
51  * Set if we are to start default services (including ARM).
52  */
53 static int start;
54
55 /**
56  * Set if we are to stop/start default services (including ARM).
57  */
58 static int restart;
59
60 /**
61  * Set if we should delete configuration and temp directory on exit.
62  */
63 static int delete;
64
65 /**
66  * Set if we should not print status messages.
67  */
68 static int quiet;
69
70 /**
71  * Set to the name of a service to start.
72  */
73 static char *init;
74
75 /**
76  * Set to the name of a service to kill.
77  */
78 static char *term;
79
80 /**
81  * Set to the name of a service to test.
82  */
83 static char *test;
84
85 /**
86  * Set to the name of the config file used.
87  */
88 static const char *config_file;
89
90 /**
91  * Set to the directory where runtime files are stored.
92  */
93 static char *dir;
94
95 /**
96  * Final status code.
97  */
98 static int ret;
99
100 /**
101  * Connection with ARM.
102  */
103 static struct GNUNET_ARM_Handle *h;
104
105 /**
106  * Our scheduler.
107  */
108 static struct GNUNET_SCHEDULER_Handle *sched;
109
110 /**
111  * Our configuration.
112  */
113 const struct GNUNET_CONFIGURATION_Handle *cfg;
114
115 /**
116  * Processing stage that we are in.  Simple counter.
117  */
118 static unsigned int phase;
119
120
121 /**
122  * Main continuation-passing-style loop.  Runs the various
123  * jobs that we've been asked to do in order.
124  *
125  * @param cls closure, unused
126  * @param tc context, unused
127  */
128 static void
129 cps_loop (void *cls,
130           const struct GNUNET_SCHEDULER_TaskContext *tc);
131
132
133 /**
134  * Callback invoked with the status of the last operation.  Reports to the
135  * user and then runs the next phase in the FSM.
136  *
137  * @param cls pointer to "const char*" identifying service that was manipulated
138  * @param success GNUNET_OK if service is now running, GNUNET_NO if not, GNUNET_SYSERR on error
139  */
140 static void
141 confirm_cb (void *cls, int success)
142 {
143   const char *service = cls;
144   switch (success)
145     {
146     case GNUNET_OK:
147       if (quiet != GNUNET_YES)
148         fprintf(stdout, _("Service `%s' is now running.\n"), service);
149       if ((phase - 1 != 2) && (phase - 1 != 3))
150         {
151           if (quiet != GNUNET_YES)
152             fprintf(stdout, _("Failed to stop service `%s'!\n"), service);
153           ret = 1;
154         }
155       break;
156     case GNUNET_NO:
157       if (quiet != GNUNET_YES)
158         fprintf(stdout, _("Service `%s' is not running.\n"), service);
159       if ((phase - 1 != 0) && (phase - 1 != 1))
160         {
161           if (quiet != GNUNET_YES)
162             fprintf(stdout, _("Failed to start service `%s'!\n"), service);
163           ret = 1;
164         }
165       break;
166     case GNUNET_SYSERR:
167       if (quiet != GNUNET_YES)
168         fprintf(stdout,
169                 _("Some error communicating with service `%s'.\n"), service);
170       ret = 1;
171       break;
172     }
173
174   GNUNET_SCHEDULER_add_continuation (sched,
175                                      &cps_loop,
176                                      NULL,
177                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
178 }
179
180
181 /**
182  * Function called to confirm that a service is running (or that
183  * it is not running).
184  *
185  * @param cls pointer to "const char*" identifying service that was manipulated
186  * @param tc reason determines if service is now running
187  */
188 static void
189 confirm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
190 {
191   const char *service = cls;
192
193   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE))
194     {
195       if (quiet != GNUNET_YES)
196         fprintf(stdout, _("Service `%s' is running.\n"), service);
197     }
198   else
199     {
200       if (quiet != GNUNET_YES)
201         fprintf(stdout, _("Service `%s' is not running.\n"), service);
202     }
203   GNUNET_SCHEDULER_add_continuation (sched,
204                                      &cps_loop,
205                                      NULL,
206                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
207 }
208
209
210 /**
211  * Main function that will be run by the scheduler.
212  *
213  * @param cls closure
214  * @param s the scheduler to use
215  * @param args remaining command-line arguments
216  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
217  * @param c configuration
218  */
219 static void
220 run (void *cls,
221      struct GNUNET_SCHEDULER_Handle *s,
222      char *const *args,
223      const char *cfgfile,
224      const struct GNUNET_CONFIGURATION_Handle *c)
225 {
226   sched = s;
227   cfg = c;
228   config_file = cfgfile;
229   if (GNUNET_CONFIGURATION_get_value_string(cfg, "PATHS", "SERVICEHOME", &dir) != GNUNET_OK)
230   {
231     dir = NULL;
232   }
233
234   h = GNUNET_ARM_connect (cfg, sched, NULL);
235   if (h == NULL)
236     {
237       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
238                _("Fatal error initializing ARM API.\n"));
239       ret = 1;
240       return;
241     }
242   GNUNET_SCHEDULER_add_continuation (sched,
243                                      &cps_loop,
244                                      NULL,
245                                      GNUNET_SCHEDULER_REASON_PREREQ_DONE);
246 }
247
248 /**
249  * Attempts to delete configuration file and SERVICEHOME
250  * on arm shutdown provided the end and delete options
251  * were specified when gnunet-arm was run.
252  */
253 static void delete_files()
254 {
255   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will attempt to remove configuration file %s and service directory %s\n", config_file, dir);
256
257   if (UNLINK(config_file) != 0)
258   {
259     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
260            _("Failed to remove configuration file %s\n"), config_file);
261   }
262
263   if (GNUNET_DISK_directory_remove(dir) != GNUNET_OK)
264   {
265     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
266         _("Failed to remove servicehome directory %s\n"), dir);
267
268   }
269 }
270
271 /**
272  * Main continuation-passing-style loop.  Runs the various
273  * jobs that we've been asked to do in order.
274  *
275  * @param cls closure, unused
276  * @param tc context, unused
277  */
278 static void
279 cps_loop (void *cls,
280           const struct GNUNET_SCHEDULER_TaskContext *tc)
281 {
282   while (1)
283     {
284       switch (phase++)
285         {
286         case 0:
287           if (term != NULL)
288             {
289               GNUNET_ARM_stop_service (h, term, STOP_TIMEOUT, &confirm_cb, term);
290               return;
291             }
292           break;
293         case 1:
294           if ((end) || (restart))
295             {
296               GNUNET_ARM_stop_service (h, "arm", STOP_TIMEOUT, &confirm_cb, "arm");
297               return;
298             }
299           break;
300         case 2:
301           if (start)
302             {
303               GNUNET_ARM_start_service (h, "arm", START_TIMEOUT, &confirm_cb, "arm");
304               return;
305             }
306           break;
307         case 3:
308           if (init != NULL)
309             {
310               GNUNET_ARM_start_service (h, init, START_TIMEOUT, &confirm_cb, init);
311               return;
312             }
313           break;
314         case 4:
315           if (test != NULL)
316             {
317               GNUNET_CLIENT_service_test (sched, test, cfg, START_TIMEOUT, &confirm_task, test);
318               return;
319             }
320           break;
321         case 5:
322           if (restart)
323             {
324               GNUNET_ARM_disconnect (h);
325               phase = 0;
326               end = 0;
327               start = 1;
328               restart = 0;
329               h = GNUNET_ARM_connect (cfg, sched, NULL);
330               if (h == NULL)
331                 {
332                   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333                            _("Fatal error initializing ARM API.\n"));
334                   ret = 1;
335                   return;
336                 }
337               GNUNET_SCHEDULER_add_now(sched, &cps_loop, NULL);
338               return;
339             }
340           /* Fall through */
341         default: /* last phase */
342           GNUNET_ARM_disconnect (h);
343           if ((end == GNUNET_YES) && (delete == GNUNET_YES))
344             delete_files();
345           return;
346         }
347     }
348 }
349
350
351 /**
352  * gnunet-arm command line options
353  */
354 static struct GNUNET_GETOPT_CommandLineOption options[] = {
355   {'e', "end", NULL, gettext_noop ("stop all GNUnet services"),
356    GNUNET_NO, &GNUNET_GETOPT_set_one, &end},
357   {'i', "init", "SERVICE", gettext_noop ("start a particular service"),
358    GNUNET_YES, &GNUNET_GETOPT_set_string, &init},
359   {'k', "kill", "SERVICE", gettext_noop ("stop a particular service"),
360    GNUNET_YES, &GNUNET_GETOPT_set_string, &term},
361   {'s', "start", NULL, gettext_noop ("start all GNUnet default services"),
362    GNUNET_NO, &GNUNET_GETOPT_set_one, &start},
363   {'r', "restart", NULL, gettext_noop ("stop and start all GNUnet default services"),
364     GNUNET_NO, &GNUNET_GETOPT_set_one, &restart},
365   {'t', "test", "SERVICE",
366    gettext_noop ("test if a particular service is running"),
367    GNUNET_YES, &GNUNET_GETOPT_set_string, &test},
368   {'d', "delete", NULL, gettext_noop ("delete config file and directory on exit"),
369    GNUNET_NO, &GNUNET_GETOPT_set_one, &delete},
370   {'q', "quiet", NULL, gettext_noop ("don't print status messages"),
371    GNUNET_NO, &GNUNET_GETOPT_set_one, &quiet},
372   GNUNET_GETOPT_OPTION_END
373 };
374
375
376 /**
377  * The main function to obtain arm from gnunetd.
378  *
379  * @param argc number of arguments from the command line
380  * @param argv command line arguments
381  * @return 0 ok, 1 on error
382  */
383 int
384 main (int argc, char *const *argv)
385 {
386   return (GNUNET_OK ==
387           GNUNET_PROGRAM_run (argc,
388                               argv,
389                               "gnunet-arm",
390                               gettext_noop
391                               ("Control services and the Automated Restart Manager (ARM)"),
392                               options, &run, NULL)) ? ret : 1;
393 }
394
395 /* end of gnunet-arm.c */