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