aboutsummaryrefslogtreecommitdiff
path: root/src/lib/util/program.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/program.c')
-rw-r--r--src/lib/util/program.c454
1 files changed, 454 insertions, 0 deletions
diff --git a/src/lib/util/program.c b/src/lib/util/program.c
new file mode 100644
index 000000000..2801b7650
--- /dev/null
+++ b/src/lib/util/program.c
@@ -0,0 +1,454 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-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 PURPROSE. 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 util/program.c
23 * @brief standard code for GNUnet startup and shutdown
24 * @author Christian Grothoff
25 */
26
27
28#include "platform.h"
29#include "gnunet_util_lib.h"
30#include "gnunet_resolver_service.h"
31#include "gnunet_constants.h"
32#include "speedup.h"
33#include <gcrypt.h>
34
35#define LOG(kind, ...) GNUNET_log_from (kind, "util-program", __VA_ARGS__)
36
37#define LOG_STRERROR_FILE(kind, syscall, filename) \
38 GNUNET_log_from_strerror_file (kind, "util-program", syscall, filename)
39
40/**
41 * Context for the command.
42 */
43struct CommandContext
44{
45 /**
46 * Argv argument.
47 */
48 char *const *args;
49
50 /**
51 * Name of the configuration file used, can be NULL!
52 */
53 char *cfgfile;
54
55 /**
56 * Main function to run.
57 */
58 GNUNET_PROGRAM_Main task;
59
60 /**
61 * Closure for @e task.
62 */
63 void *task_cls;
64
65 /**
66 * Configuration to use.
67 */
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
69};
70
71
72/**
73 * task run when the scheduler shuts down
74 */
75static void
76shutdown_task (void *cls)
77{
78 (void) cls;
79 GNUNET_SPEEDUP_stop_ ();
80}
81
82
83/**
84 * Initial task called by the scheduler for each
85 * program. Runs the program-specific main task.
86 */
87static void
88program_main (void *cls)
89{
90 struct CommandContext *cc = cls;
91
92 GNUNET_SPEEDUP_start_ (cc->cfg);
93 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
94 NULL);
95 GNUNET_RESOLVER_connect (cc->cfg);
96 cc->task (cc->task_cls,
97 cc->args,
98 cc->cfgfile,
99 cc->cfg);
100}
101
102
103/**
104 * Compare function for 'qsort' to sort command-line arguments by the
105 * short option.
106 *
107 * @param a1 first command line option
108 * @param a2 second command line option
109 */
110static int
111cmd_sorter (const void *a1,
112 const void *a2)
113{
114 const struct GNUNET_GETOPT_CommandLineOption *c1 = a1;
115 const struct GNUNET_GETOPT_CommandLineOption *c2 = a2;
116
117 if (toupper ((unsigned char) c1->shortName) >
118 toupper ((unsigned char) c2->shortName))
119 return 1;
120 if (toupper ((unsigned char) c1->shortName) <
121 toupper ((unsigned char) c2->shortName))
122 return -1;
123 if (c1->shortName > c2->shortName)
124 return 1;
125 if (c1->shortName < c2->shortName)
126 return -1;
127 return 0;
128}
129
130
131enum GNUNET_GenericReturnValue
132GNUNET_PROGRAM_run2 (int argc,
133 char *const *argv,
134 const char *binaryName,
135 const char *binaryHelp,
136 const struct GNUNET_GETOPT_CommandLineOption *options,
137 GNUNET_PROGRAM_Main task,
138 void *task_cls,
139 int run_without_scheduler)
140{
141 struct CommandContext cc;
142
143#if ENABLE_NLS
144 char *path;
145#endif
146 char *loglev;
147 char *logfile;
148 char *cfg_fn;
149 enum GNUNET_GenericReturnValue ret;
150 int iret;
151 unsigned int cnt;
152 unsigned long long skew_offset;
153 unsigned long long skew_variance;
154 long long clock_offset;
155 struct GNUNET_CONFIGURATION_Handle *cfg;
156 const struct GNUNET_OS_ProjectData *pd = GNUNET_OS_project_data_get ();
157 const struct GNUNET_GETOPT_CommandLineOption defoptions[] = {
158 GNUNET_GETOPT_option_cfgfile (&cc.cfgfile),
159 GNUNET_GETOPT_option_help (binaryHelp),
160 GNUNET_GETOPT_option_loglevel (&loglev),
161 GNUNET_GETOPT_option_logfile (&logfile),
162 GNUNET_GETOPT_option_version (pd->version)
163 };
164 unsigned int deflen = sizeof(defoptions) / sizeof(defoptions[0]);
165 struct GNUNET_GETOPT_CommandLineOption *allopts;
166 const char *gargs;
167 char *lpfx;
168 char *spc;
169
170 logfile = NULL;
171 gargs = getenv ("GNUNET_ARGS");
172 if (NULL != gargs)
173 {
174 char **gargv;
175 unsigned int gargc;
176 char *cargs;
177
178 gargv = NULL;
179 gargc = 0;
180 for (int i = 0; i < argc; i++)
181 GNUNET_array_append (gargv,
182 gargc,
183 GNUNET_strdup (argv[i]));
184 cargs = GNUNET_strdup (gargs);
185 for (char *tok = strtok (cargs, " ");
186 NULL != tok;
187 tok = strtok (NULL, " "))
188 GNUNET_array_append (gargv, gargc, GNUNET_strdup (tok));
189 GNUNET_free (cargs);
190 GNUNET_array_append (gargv, gargc, NULL);
191 argv = (char *const *) gargv;
192 argc = gargc - 1;
193 }
194 memset (&cc, 0, sizeof(cc));
195 loglev = NULL;
196 cc.task = task;
197 cc.task_cls = task_cls;
198 cc.cfg = cfg = GNUNET_CONFIGURATION_create ();
199 /* prepare */
200#if ENABLE_NLS
201 if (NULL != pd->gettext_domain)
202 {
203 setlocale (LC_ALL, "");
204 path = (NULL == pd->gettext_path)
205 ? GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR)
206 : GNUNET_strdup (pd->gettext_path);
207 if (NULL != path)
208 {
209 bindtextdomain (pd->gettext_domain, path);
210 GNUNET_free (path);
211 }
212 textdomain (pd->gettext_domain);
213 }
214#endif
215 cnt = 0;
216 while (NULL != options[cnt].name)
217 cnt++;
218 allopts = GNUNET_new_array (cnt + deflen + 1,
219 struct GNUNET_GETOPT_CommandLineOption);
220 GNUNET_memcpy (allopts,
221 options,
222 cnt * sizeof(struct GNUNET_GETOPT_CommandLineOption));
223 {
224 unsigned int xtra = 0;
225
226 for (unsigned int i = 0;
227 i<sizeof (defoptions) / sizeof(struct GNUNET_GETOPT_CommandLineOption);
228 i++)
229 {
230 bool found = false;
231
232 for (unsigned int j = 0; j<cnt; j++)
233 {
234 found |= ( (options[j].shortName == defoptions[i].shortName) &&
235 (0 != options[j].shortName) );
236 found |= ( (NULL != options[j].name) &&
237 (NULL != defoptions[i].name) &&
238 (0 == strcmp (options[j].name,
239 defoptions[i].name)) );
240 if (found)
241 break;
242 }
243 if (found)
244 continue;
245 GNUNET_memcpy (&allopts[cnt + xtra],
246 &defoptions[i],
247 sizeof (struct GNUNET_GETOPT_CommandLineOption));
248 xtra++;
249 }
250 cnt += xtra;
251 }
252 qsort (allopts,
253 cnt,
254 sizeof(struct GNUNET_GETOPT_CommandLineOption),
255 &cmd_sorter);
256 loglev = NULL;
257 if ((NULL != pd->config_file) && (NULL != pd->user_config_file))
258 cfg_fn = GNUNET_CONFIGURATION_default_filename ();
259 else
260 cfg_fn = NULL;
261 lpfx = GNUNET_strdup (binaryName);
262 if (NULL != (spc = strstr (lpfx, " ")))
263 *spc = '\0';
264 iret = GNUNET_GETOPT_run (binaryName,
265 allopts,
266 (unsigned int) argc,
267 argv);
268 if ((GNUNET_OK > iret) ||
269 (GNUNET_OK != GNUNET_log_setup (lpfx,
270 loglev,
271 logfile)))
272 {
273 GNUNET_free (allopts);
274 GNUNET_free (lpfx);
275 ret = (enum GNUNET_GenericReturnValue) iret;
276 goto cleanup;
277 }
278 if (NULL != cc.cfgfile)
279 {
280 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
281 "Loading configuration from entry point specified as option (%s)\n",
282 cc.cfgfile);
283 if (GNUNET_YES !=
284 GNUNET_DISK_file_test (cc.cfgfile))
285 {
286 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
287 _ ("Unreadable configuration file `%s', exiting ...\n"),
288 cc.cfgfile);
289 ret = GNUNET_SYSERR;
290 GNUNET_free (allopts);
291 GNUNET_free (lpfx);
292 goto cleanup;
293 }
294 if (GNUNET_SYSERR ==
295 GNUNET_CONFIGURATION_load (cfg,
296 cc.cfgfile))
297 {
298 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
299 _ ("Malformed configuration file `%s', exiting ...\n"),
300 cc.cfgfile);
301 ret = GNUNET_SYSERR;
302 GNUNET_free (allopts);
303 GNUNET_free (lpfx);
304 goto cleanup;
305 }
306 }
307 else
308 {
309 if ( (NULL != cfg_fn) &&
310 (GNUNET_YES !=
311 GNUNET_DISK_file_test (cfg_fn)) )
312 {
313 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
314 _ ("Unreadable configuration file `%s'. Exiting ...\n"),
315 cfg_fn);
316 ret = GNUNET_SYSERR;
317 GNUNET_free (allopts);
318 GNUNET_free (lpfx);
319 goto cleanup;
320 }
321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
322 "Loading configuration from entry point `%s'\n",
323 cc.cfgfile);
324 if (GNUNET_SYSERR ==
325 GNUNET_CONFIGURATION_load (cfg,
326 cfg_fn))
327 {
328 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
329 _ ("Malformed configuration. Exiting ...\n"));
330 ret = GNUNET_SYSERR;
331 GNUNET_free (allopts);
332 GNUNET_free (lpfx);
333 goto cleanup;
334 }
335 }
336 GNUNET_free (allopts);
337 GNUNET_free (lpfx);
338 if ((GNUNET_OK ==
339 GNUNET_CONFIGURATION_get_value_number (cc.cfg,
340 "testing",
341 "skew_offset",
342 &skew_offset)) &&
343 (GNUNET_OK ==
344 GNUNET_CONFIGURATION_get_value_number (cc.cfg,
345 "testing",
346 "skew_variance",
347 &skew_variance)))
348 {
349 clock_offset = skew_offset - skew_variance;
350 GNUNET_TIME_set_offset (clock_offset);
351 }
352 /* ARM needs to know which configuration file to use when starting
353 services. If we got a command-line option *and* if nothing is
354 specified in the configuration, remember the command-line option
355 in "cfg". This is typically really only having an effect if we
356 are running code in src/arm/, as obviously the rest of the code
357 has little business with ARM-specific options. */
358 if (GNUNET_YES !=
359 GNUNET_CONFIGURATION_have_value (cfg,
360 "arm",
361 "CONFIG"))
362 {
363 if (NULL != cc.cfgfile)
364 GNUNET_CONFIGURATION_set_value_string (cfg,
365 "arm",
366 "CONFIG",
367 cc.cfgfile);
368 else if (NULL != cfg_fn)
369 GNUNET_CONFIGURATION_set_value_string (cfg,
370 "arm",
371 "CONFIG",
372 cfg_fn);
373 }
374
375 /* run */
376 cc.args = &argv[iret];
377 if ((NULL == cc.cfgfile) && (NULL != cfg_fn))
378 cc.cfgfile = GNUNET_strdup (cfg_fn);
379 if (GNUNET_NO == run_without_scheduler)
380 {
381 GNUNET_SCHEDULER_run (&program_main, &cc);
382 }
383 else
384 {
385 GNUNET_RESOLVER_connect (cc.cfg);
386 cc.task (cc.task_cls, cc.args, cc.cfgfile, cc.cfg);
387 }
388 ret = GNUNET_OK;
389cleanup:
390 GNUNET_CONFIGURATION_destroy (cfg);
391 GNUNET_free (cc.cfgfile);
392 GNUNET_free (cfg_fn);
393 GNUNET_free (loglev);
394 GNUNET_free (logfile);
395 return ret;
396}
397
398
399enum GNUNET_GenericReturnValue
400GNUNET_PROGRAM_run (int argc,
401 char *const *argv,
402 const char *binaryName,
403 const char *binaryHelp,
404 const struct GNUNET_GETOPT_CommandLineOption *options,
405 GNUNET_PROGRAM_Main task,
406 void *task_cls)
407{
408 return GNUNET_PROGRAM_run2 (argc,
409 argv,
410 binaryName,
411 binaryHelp,
412 options,
413 task,
414 task_cls,
415 GNUNET_NO);
416}
417
418
419/* A list of daemons to be launched when GNUNET_main()
420 * is called
421 */
422struct DaemonHandleList
423{
424 /* DLL */
425 struct DaemonHandleList *prev;
426
427 /* DLL */
428 struct DaemonHandleList *next;
429
430 /* Program to launch */
431 GNUNET_PROGRAM_Main d;
432};
433
434/* The daemon list */
435static struct DaemonHandleList *hll_head = NULL;
436
437/* The daemon list */
438static struct DaemonHandleList *hll_tail = NULL;
439
440enum GNUNET_GenericReturnValue
441GNUNET_DAEMON_register (const char *daemon_name,
442 const char *daemon_help,
443 GNUNET_PROGRAM_Main task)
444{
445 struct DaemonHandleList *hle;
446
447 hle = GNUNET_new (struct DaemonHandleList);
448 hle->d = task;
449 GNUNET_CONTAINER_DLL_insert (hll_head, hll_tail, hle);
450 return GNUNET_OK;
451}
452
453
454/* end of program.c */