1 /*
2 * option.c - getopt function implementation
3 *
4 * Copyright (C) 2011-2013 Thien-Thi Nguyen
5 * Copyright (C) 2000, 2001, 2003 Stefan Jahn <stefan@lkcc.org>
6 * Copyright (C) 2000 Raimund Jacob <raimi@lkcc.org>
7 *
8 * This is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3, or (at your option)
11 * any later version.
12 *
13 * This software is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this package. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #ifdef HAVE_CRYPT_H
29 # include <crypt.h>
30 #endif
31
32 #include "networking-headers.h"
33 #include "cfgfile.h"
34 #include "libserveez.h"
35 #include "option.h"
36 #include "unused.h"
37
38 #ifndef HAVE_GETOPT
39 /*
40 * Lousy implementation of @code{getopt}.
41 * Only good for parsing simple short option command lines on
42 * stupid systems like Win32. No error checking !
43 */
44 char *optarg = NULL;
45 int optind = 1;
46 int opterr = 1;
47 int optopt = 0;
48
49 int
getopt(int argc,char * const argv[],const char * optstring)50 getopt (int argc, char * const argv[], const char *optstring)
51 {
52 static int current_arg = 1, current_opt = 0, current_idx = 1;
53 int n;
54 char *prog = argv[0] + strlen (argv[0]);
55
56 /* parse the programs name */
57 while (prog > argv[0] && (*prog != '/' || *prog != '\\'))
58 prog--;
59 if (*prog == '/' || *prog == '\\')
60 prog++;
61
62 while (current_arg < argc)
63 {
64 if (argv[current_arg][0] == '-')
65 {
66 if (current_opt == 0)
67 current_opt = 1;
68 while (argv[current_arg][current_opt] != '\0')
69 {
70 n = 0;
71 /* go through all option characters */
72 while (optstring[n])
73 {
74 if (optstring[n] == argv[current_arg][current_opt])
75 {
76 current_opt++;
77 if (optstring[n + 1] == ':')
78 {
79 optarg = argv[current_arg + current_idx];
80 current_idx++;
81 if (opterr && optarg == NULL)
82 fprintf (stderr,
83 "%s: option requires an argument -- %c\n",
84 prog, optstring[n]);
85 }
86 else
87 optarg = NULL;
88 if (argv[current_arg][current_opt] == '\0')
89 {
90 current_arg += current_idx;
91 current_opt = 0;
92 current_idx = 1;
93 }
94 optind = current_arg + current_idx - 1;
95 return optstring[n];
96 }
97 n++;
98 }
99 optopt = argv[current_arg][current_opt];
100 if (opterr)
101 fprintf (stderr, "%s: invalid option -- %c\n", prog, optopt);
102 return '?';
103 }
104 current_opt++;
105 }
106 current_arg++;
107 current_idx = 1;
108 }
109
110 current_arg = 1;
111 current_opt = 0;
112 current_idx = 1;
113 return EOF;
114 }
115 #endif /* not HAVE_GETOPT */
116
117 /*
118 * Print program version.
119 */
120 static void
version(void)121 version (void)
122 {
123 fprintf (stdout, "serveez (%s) %s\n"
124 "Copyright (C) 2011-2013 Thien-Thi Nguyen\n"
125 "Copyright (C) 2000-2009 Martin Grabmueller,"
126 " Stefan Jahn, Raimund Jacob, Andreas Rottmann\n"
127 "License GPLv3+: GNU GPL version 3 or later"
128 " <http://gnu.org/licenses/gpl.html>\n"
129 "This is free software: you are free"
130 " to change and redistribute it.\n"
131 "There is NO WARRANTY, to the extent permitted by law.\n",
132 PACKAGE_STRING, PROGRAM_VERSION);
133 }
134
135 #ifdef HAVE_GETOPT_LONG
136 #define LONGOPTS 1
137
138 static struct option serveez_options[];
139
140 static const char *
longname(char letter)141 longname (char letter)
142 {
143 struct option *o = serveez_options;
144
145 while (o->val != letter)
146 o++;
147
148 return o->name;
149 }
150
151 #else /* !defined HAVE_GETOPT_LONG */
152 #define LONGOPTS 0
153 #endif /* !deifned HAVE_GETOPT_LONG */
154
155 #define AB(blurb,uri) blurb ": <" uri ">\n"
156 #define GNU(blurb,rest) AB (blurb, "http://www.gnu.org/" rest)
157
158 #define BUGME \
159 ("\n" \
160 AB ("Report bugs to", PACKAGE_BUGREPORT) \
161 GNU ("Serveez home page", "software/serveez/") \
162 GNU ("General help using GNU software", "gethelp/"))
163
164 /*
165 * Display program command line options.
166 * Then @code{exit} with @var{exitval}.
167 */
168 static void
usage(int exitval)169 usage (int exitval)
170 {
171 struct human {
172 char letter;
173 char *arg;
174 char *description;
175 } all[] = {
176 {'h', NULL, "display this help and exit"},
177 {'V', NULL, "display version information and exit"},
178 {'L', NULL, "display builtin servers and exit"},
179 {'i', NULL, "list local network interfaces and exit"},
180 {'f', "FILENAME", "file to use as configuration file (serveez.cfg)"},
181 {'v', "LEVEL", "set level of verbosity"},
182 {'l', "FILENAME", "use FILENAME for logging (default is stderr)"},
183 #if ENABLE_CONTROL_PROTO
184 {'P', "STRING", "set the password for control connections"},
185 #endif
186 {'m', "COUNT", "set the max. number of socket descriptors"},
187 {'d', NULL, "start as daemon in background"},
188 {'c', NULL, "use standard input as configuration file"},
189 {'s', NULL, "don't start any coservers"}
190 };
191 int const count = sizeof (all) / sizeof (struct human);
192 int width = 0;
193 int one;
194 struct human *h;
195
196 for (h = all; h < all + count; h++)
197 {
198 one = (LONGOPTS ? strlen (longname (h->letter)) : 0)
199 + (h->arg ? strlen (h->arg) : 0);
200 if (width < one)
201 width = one;
202 }
203 width += 3;
204
205 printf ("Usage: serveez [OPTION...]\n\n");
206 for (h = all; h < all + count; h++)
207 {
208 one = 0;
209 printf (" -%c", h->letter);
210 if (LONGOPTS)
211 {
212 const char *name = longname (h->letter);
213
214 printf (", --%s", name);
215 one += strlen (name);
216 }
217 if (h->arg)
218 {
219 /* NB: This doesn't handle the ‘optional_argument’ case,
220 which should be displayed as "[=ARG]" or " [ARG]". */
221 printf ("%c%s",
222 LONGOPTS ? '=' : ' ',
223 h->arg);
224 one += 1 + strlen (h->arg);
225 }
226 printf ("%*s%s\n", width - one, "", h->description);
227 }
228 fprintf (stdout, "%s", BUGME);
229
230 exit (exitval);
231 }
232
233 #if HAVE_GETOPT_LONG
234 /*
235 * Argument array for ‘getopt_long’ system call.
236 */
237 static struct option serveez_options[] = {
238 {"help", no_argument, NULL, 'h'},
239 {"version", no_argument, NULL, 'V'},
240 {"list-servers", no_argument, NULL, 'L'},
241 {"iflist", no_argument, NULL, 'i'},
242 {"daemon", no_argument, NULL, 'd'},
243 {"stdin", no_argument, NULL, 'c'},
244 {"verbose", required_argument, NULL, 'v'},
245 {"cfg-file", required_argument, NULL, 'f'},
246 {"log-file", required_argument, NULL, 'l'},
247 #if ENABLE_CONTROL_PROTO
248 {"password", required_argument, NULL, 'P'},
249 #endif
250 {"max-sockets", required_argument, NULL, 'm'},
251 {"solitary", no_argument, NULL, 's'},
252 {NULL, 0, NULL, 0}
253 };
254 #endif /* HAVE_GETOPT_LONG */
255
256 #if ENABLE_CONTROL_PROTO
257 #define SERVEEZ_OPTIONS "l:hVLiv:f:P:m:dcs"
258 #else
259 #define SERVEEZ_OPTIONS "l:hVLiv:f:m:dcs"
260 #endif
261
262 static int
display_ifc(const svz_interface_t * ifc,UNUSED void * closure)263 display_ifc (const svz_interface_t *ifc, UNUSED void *closure)
264 {
265 char addr[64];
266
267 SVZ_PP_ADDR (addr, ifc->addr);
268 if (ifc->description)
269 /* interface with description */
270 printf ("%40s: %s\n",
271 ifc->description, addr);
272 else
273 /* interface with interface # only */
274 printf ("%31s%09zu: %s\n",
275 "interface # ", ifc->index, addr);
276 return 0;
277 }
278
279 /*
280 * Parse the command line options. If these have been correct the function
281 * either terminates the program successfully or returns an option
282 * structure containing information about the command line arguments or it
283 * exits the program failurefully if the command line has been wrong.
284 */
285 option_t *
handle_options(int argc,char ** argv)286 handle_options (int argc, char **argv)
287 {
288 static option_t options;
289 static char *cfgfile = "serveez.cfg";
290 int arg;
291 #if HAVE_GETOPT_LONG
292 int index;
293 #endif
294
295 /* initialize command line options */
296 options.logfile = NULL;
297 options.cfgfile = cfgfile;
298 options.verbosity = -1;
299 options.sockets = -1;
300 #if ENABLE_CONTROL_PROTO
301 options.pass = NULL;
302 #endif
303 options.daemon = 0;
304 options.loghandle = NULL;
305 options.coservers = 1;
306
307 /* go through the command line itself */
308 #if HAVE_GETOPT_LONG
309 while ((arg = getopt_long (argc, argv, SERVEEZ_OPTIONS, serveez_options,
310 &index)) != EOF)
311 #else
312 while ((arg = getopt (argc, argv, SERVEEZ_OPTIONS)) != EOF)
313 #endif
314 {
315 switch (arg)
316 {
317 case 'h':
318 usage (EXIT_SUCCESS);
319 break;
320
321 case 'V':
322 version ();
323 exit (EXIT_SUCCESS);
324 break;
325
326 case 'L':
327 print_available_servers ();
328 exit (EXIT_SUCCESS);
329 break;
330
331 case 'i':
332 printf ("--- list of local interfaces"
333 " you can start ip services on ---\n");
334 svz_foreach_interface (display_ifc, NULL);
335 exit (EXIT_SUCCESS);
336 break;
337
338 case 'c':
339 if (options.cfgfile != cfgfile)
340 usage (EXIT_FAILURE);
341 options.cfgfile = NULL;
342 break;
343
344 case 'f':
345 if (!optarg || options.cfgfile == NULL)
346 usage (EXIT_FAILURE);
347 options.cfgfile = optarg;
348 break;
349
350 case 'v':
351 if (optarg)
352 {
353 options.verbosity = atoi (optarg);
354 if (options.verbosity < SVZ_LOG_FATAL)
355 options.verbosity = SVZ_LOG_FATAL;
356 else if (options.verbosity > SVZ_LOG_DEBUG)
357 options.verbosity = SVZ_LOG_DEBUG;
358 }
359 else
360 options.verbosity = SVZ_LOG_DEBUG;
361 break;
362
363 case 'l':
364 if (!optarg)
365 usage (EXIT_FAILURE);
366 options.logfile = optarg;
367 break;
368
369 #if ENABLE_CONTROL_PROTO
370 case 'P':
371 if (!optarg || strlen (optarg) < 2)
372 usage (EXIT_FAILURE);
373 #if defined HAVE_CRYPT
374 options.pass = svz_strdup (crypt (optarg, optarg));
375 #else
376 options.pass = svz_strdup (optarg);
377 #endif
378 break;
379 #endif /* ENABLE_CONTROL_PROTO */
380
381 case 'm':
382 if (!optarg)
383 usage (EXIT_FAILURE);
384 options.sockets = atoi (optarg);
385 break;
386
387 case 'd':
388 options.daemon = 1;
389 break;
390
391 case 's':
392 options.coservers = -1;
393 break;
394
395 default:
396 usage (EXIT_FAILURE);
397 }
398 }
399
400 return &options;
401 }
402