f74da8c7f5dba0ea2803a704668a1fe4d63b937f
[oweals/gnunet.git] / src / util / program.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 util/program.c
23  * @brief standard code for GNUnet startup and shutdown
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_configuration_lib.h"
30 #include "gnunet_crypto_lib.h"
31 #include "gnunet_directories.h"
32 #include "gnunet_getopt_lib.h"
33 #include "gnunet_os_lib.h"
34 #include "gnunet_program_lib.h"
35 #include "gnunet_scheduler_lib.h"
36 #include <gcrypt.h>
37
38 #if HAVE_ARGZ_H
39 #include <argz.h>
40 #else
41 #include "program_lib_argz.c"
42 #endif
43
44 /**
45  * Context for the command.
46  */
47 struct CommandContext
48 {
49   /**
50    * Argv argument.
51    */
52   char *const *args;
53
54   /**
55    * Name of the configuration file used, can be NULL!
56    */
57   char *cfgfile;
58
59   /**
60    * Main function to run.
61    */
62   GNUNET_PROGRAM_Main task;
63
64   /**
65    * Closure for task.
66    */
67   void *task_cls;
68
69   /**
70    * Configuration to use.
71    */
72   const struct GNUNET_CONFIGURATION_Handle *cfg;
73
74 };
75
76
77 /**
78  * Initial task called by the scheduler for each
79  * program.  Runs the program-specific main task.
80  */
81 static void
82 program_main (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
83 {
84   struct CommandContext *cc = cls;
85
86   cc->task (cc->task_cls, tc->sched, cc->args, cc->cfgfile, cc->cfg);
87 }
88
89
90 /**
91  * Compare function for 'qsort' to sort command-line arguments by the
92  * short option.
93  *
94  * @param a1 first command line option
95  * @param a2 second command line option
96  */
97 static int
98 cmd_sorter (__const void *a1, __const void *a2)
99 {
100   __const struct GNUNET_GETOPT_CommandLineOption *c1 = a1;
101   __const struct GNUNET_GETOPT_CommandLineOption *c2 = a2;
102   if (toupper (c1->shortName) > toupper (c2->shortName))
103     return 1;
104   if (toupper (c1->shortName) < toupper (c2->shortName))
105     return -1;
106   if (c1->shortName > c2->shortName)
107     return 1;
108   if (c1->shortName < c2->shortName)
109     return -1;
110   return 0;
111 }
112
113
114 /**
115  * Run a standard GNUnet command startup sequence (initialize loggers
116  * and configuration, parse options).
117  *
118  * @param argc number of command line arguments
119  * @param argv command line arguments
120  * @param binaryName our expected name
121  * @param binaryHelp help text for the program
122  * @param options command line options
123  * @param task main function to run
124  * @param task_cls closure for task
125  * @return GNUNET_SYSERR on error, GNUNET_OK on success
126  */
127 int
128 GNUNET_PROGRAM_run (int argc,
129                     char *const *argv,
130                     const char *binaryName,
131                     const char *binaryHelp,
132                     const struct GNUNET_GETOPT_CommandLineOption *options,
133                     GNUNET_PROGRAM_Main task, void *task_cls)
134 {
135   struct CommandContext cc;
136   char *path;
137   char *loglev;
138   int ret;
139   unsigned int cnt;
140   struct GNUNET_CONFIGURATION_Handle *cfg;
141   struct GNUNET_GETOPT_CommandLineOption defoptions[] = {
142     GNUNET_GETOPT_OPTION_CFG_FILE (&cc.cfgfile),
143     GNUNET_GETOPT_OPTION_HELP (binaryHelp),
144     GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev),
145     GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION)
146   };
147   struct GNUNET_GETOPT_CommandLineOption *allopts;
148   const char *gargs;
149
150   gargs = getenv ("GNUNET_ARGS");
151   if (gargs != NULL)
152     {
153       char *gargz;
154       size_t gargl;
155       char **gargv;
156       int i;
157
158       argz_create_sep (gargs, ' ', &gargz, &gargl);
159       for (i=0;i<argc;i++)
160         argz_insert (&gargz, &gargl, gargz, argv[i]);
161       gargv = GNUNET_malloc (sizeof (char*) * (gargl+1));
162       argz_extract (gargz, gargl, gargv);
163       argc = argz_count (gargz, gargl);
164       free (gargz);
165       argv = (char *const *) gargv;
166     }
167   memset (&cc, 0, sizeof (cc));
168   loglev = NULL;
169   cc.task = task;
170   cc.task_cls = task_cls;
171   cc.cfg = cfg = GNUNET_CONFIGURATION_create ();
172
173   /* prepare */
174 #if ENABLE_NLS
175   setlocale (LC_ALL, "");
176   path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR);
177   if (path != NULL)
178     {
179       BINDTEXTDOMAIN ("GNUnet", path);
180       GNUNET_free (path);
181     }
182   textdomain ("GNUnet");
183 #endif
184   cnt = 0;
185   while (options[cnt].name != NULL)
186     cnt++;
187   allopts =
188     GNUNET_malloc ((cnt +
189                     1) * sizeof (struct GNUNET_GETOPT_CommandLineOption) +
190                    sizeof (defoptions));
191   memcpy (allopts, defoptions, sizeof (defoptions));
192   memcpy (&allopts
193           [sizeof (defoptions) /
194            sizeof (struct GNUNET_GETOPT_CommandLineOption)], options,
195           (cnt + 1) * sizeof (struct GNUNET_GETOPT_CommandLineOption));
196   cnt +=
197     sizeof (defoptions) / sizeof (struct GNUNET_GETOPT_CommandLineOption);
198   qsort (allopts, cnt, sizeof (struct GNUNET_GETOPT_CommandLineOption),
199          &cmd_sorter);
200   loglev = GNUNET_strdup ("WARNING");
201   if ((-1 == (ret = GNUNET_GETOPT_run (binaryName,
202                                        allopts,
203                                        (unsigned int) argc, argv))) ||
204       ((GNUNET_OK !=
205         GNUNET_log_setup (binaryName,
206                           loglev,
207                           NULL)) ||
208        (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cc.cfgfile))))
209
210     {
211       GNUNET_free_non_null (cc.cfgfile);
212       GNUNET_free (loglev);
213       GNUNET_free (allopts);
214       return GNUNET_SYSERR;
215     }
216   GNUNET_free (allopts);
217
218   /* run */
219   cc.args = &argv[ret];
220   GNUNET_SCHEDULER_run (&program_main, &cc);
221
222   /* clean up */
223   GNUNET_CONFIGURATION_destroy (cfg);
224   GNUNET_free_non_null (cc.cfgfile);
225   GNUNET_free (loglev);
226   return GNUNET_OK;
227 }
228
229
230 /* end of program.c */