1 /*@unused@*/ static const char rcsid[] =
2     "@(#)$Id: options.c,v 1.34 2003/05/26 19:54:04 carstenklapp Exp $";
3 
4 /*
5  Uptime Client v5.0 beta
6 
7  $Id: options.c,v 1.34 2003/05/26 19:54:04 carstenklapp Exp $
8 
9  Logs system uptime and statistics with Uptimes Project servers
10 
11  Copyright (C) 1999-2002 Martijn Broenland, Alex C. de Haas, Carsten Klapp
12 
13  This program is free software; you can redistribute it and/or modify
14  it under the terms of the GNU General Public License as published by
15  the Free Software Foundation; either version 2 of the License, or
16  (at your option) any later version.
17 
18  This program is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  GNU General Public License for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with this program; if not, write to the Free Software
25  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 
27  Carsten Klapp <carstenklapp@users.sourceforge.net>
28  Alex C. de Haas <alex@uptimes.net>
29  Martijn Broenland <tgm@uptimes.net>
30  */
31 
32 /**
33  * @filename    options.c
34  *
35  * @desc        Options file parsing
36  */
37 
38 /* System includes */
39 #include <ctype.h>
40 #include <errno.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 /* My includes */
46 #include "locale.h"     /* gettext */
47 #include "options.h"
48 #include "uplog.h"      /* wrapper for <syslog.h> */
49 
50 /* Include if not compiling on WinNT or BeOS */
51 #if !defined PLATFORM_WINNT && !defined PLATFORM_BEOS
52 #   include <sysexits.h>
53 #elif
54 #   include "compat/sysexits.h"
55 #endif
56 
57 /**
58  * Magic Numbers
59  * Timer interval limits in seconds.
60  * Minimum value is enforced. A warning is logged if greater than upper limit.
61  */
62 #define MINIMUM_INTERVAL  30    /* enforced */
63 #define DEFAULT_INTERVAL 550
64 #define UPPER_INTERVAL   600    /* warning if exceeded */
65 
66 /* Macros */
67 #define MAX_LINE_LEN  1024
68 #define MAX_KEY_LEN     64
69 #define MAX_VAL_LEN    256
70 
71 #define LINE_ERR        0
72 #define LINE_EMPTY      1
73 #define LINE_COMMENT    2
74 #define LINE_OPTION     3
75 
76 /**
77  * Global variables, very ugly, watch through sunglasses!
78  *
79  * These are the configuration options available.
80  */
81 char   cfg_authkey[AUTHKEY_REQUIRED_LEN + 1] = "";
82 char   cfg_upserver[UPSERVER_MAXLEN + 1] = "uptimes.hostingwired.com";
83 long int cfg_interval = DEFAULT_INTERVAL;
84 char   cfg_pidfile[PIDFILE_MAXLEN + 1] = PIDFILE;
85 char   cfg_proxyserver[PROXYSERVER_MAXLEN + 1] = "";
86 in_port_t cfg_udpport = 49153;
87 in_port_t cfg_proxyport = 8080;
88 char   cfg_proxyuser[PROXYUSER_MAXLEN + 1] = "";
89 char   cfg_proxypass[PROXYPASS_MAXLEN + 1] = "";
90 
91 #if defined PLATFORM_SOLARIS
92 unsigned short int cfg_SendIdle = 0;
93 #else
94 unsigned short int cfg_SendIdle = 1;
95 #endif /* PLATFORM_SOLARIS */
96 #if defined PLATFORM_UNIXWARE
97 unsigned short int cfg_SendUsage = 0;
98 #else
99 unsigned short int cfg_SendUsage = 1;
100 #endif /* PLATFORM_UNIXWARE */
101 unsigned short int cfg_sendosname = 1;
102 unsigned short int cfg_sendosversion = 1;
103 unsigned short int cfg_sendcpu = 1;
104 unsigned short int cfg_sendcpudetail = 1;
105 unsigned short int cfg_sendloadavg = 0; /* unimplemented */
106 
107 /**
108  * Global variables, very ugly, watch through sunglasses!
109  *
110  * These are some booleans set to reflect the configuration
111  * options.
112  */
113 unsigned short int have_proxyserver = 0;
114 unsigned short int have_proxyport = 0;
115 unsigned short int have_proxyuser = 0;
116 unsigned short int have_proxypass = 0;
117 
118 unsigned short int verbose = 0;
119 
120 #if defined DEBUG
121 /**
122  * @desc    Print options
123  */
124 static void
print_options(void)125 print_options(void)
126 {
127     uplog(LOG_DEBUG, "cfg_authkey:         [%s]", cfg_authkey);
128     uplog(LOG_DEBUG, "cfg_interval:        [%d]", cfg_interval);
129     uplog(LOG_DEBUG, "cfg_upserver:        [%s]", cfg_upserver);
130     uplog(LOG_DEBUG, "cfg_proxyserver:     [%s]", cfg_proxyserver);
131     uplog(LOG_DEBUG, "cfg_proxyport:       [%d]", cfg_proxyport);
132     uplog(LOG_DEBUG, "cfg_udpport:         [%d]", cfg_udpport);
133     uplog(LOG_DEBUG, "cfg_proxyuser:       [%s]", cfg_proxyuser);
134     uplog(LOG_DEBUG, "cfg_proxypass:       [%s]", cfg_proxypass);
135     uplog(LOG_DEBUG, "cfg_pidfile:         [%s]", cfg_pidfile);
136     uplog(LOG_DEBUG, "cfg_SendIdle: [%d]", cfg_SendIdle);
137     uplog(LOG_DEBUG, "cfg_SendUsage: [%d]", cfg_SendUsage);
138     uplog(LOG_DEBUG, "cfg_sendosname:      [%d]", cfg_sendosname);
139     uplog(LOG_DEBUG, "cfg_sendosversion:   [%d]", cfg_sendosversion);
140     uplog(LOG_DEBUG, "cfg_sendcpu:         [%d]", cfg_sendcpu);
141     uplog(LOG_DEBUG, "cfg_sendcpudetail:   [%d]", cfg_sendcpudetail);
142     uplog(LOG_DEBUG, "cfg_sendloadavg:     [%d]", cfg_sendloadavg);
143 }
144 #endif /* DEBUG */
145 
146 /**
147  * @desc    Process key/value pair
148  */
149 static int
process_option(const char * key,const char * value)150 process_option(const char *key, const char *value)
151 {
152     if (strcmp(key, "AuthKey") == 0) {
153         if (strlen(value) == AUTHKEY_REQUIRED_LEN) {
154             strncpy(cfg_authkey, value, AUTHKEY_REQUIRED_LEN);
155             cfg_authkey[AUTHKEY_REQUIRED_LEN] = 0;
156         }
157         else {
158             uplog(LOG_ERR, _("%s AuthKey is not %d bytes long! (%s)\n"),
159                   _("FATAL ERROR:"), AUTHKEY_REQUIRED_LEN, CONFIGFILE);
160             printf(_("%s AuthKey is not %d bytes long! (%s)\n"),
161                    _("FATAL ERROR:"), AUTHKEY_REQUIRED_LEN, CONFIGFILE);
162 #if !defined PARANOID
163             exit(EX_DATAERR);
164 #else /* PARANOID */
165             uplog(LOG_ERR, "%s %s\n", _("FATAL ERROR:"),
166                   _("Ignoring due to Paranoid Mode Enabled."));
167             printf("%s %s\n", _("FATAL ERROR:"),
168                    _("Ignoring due to Paranoid Mode Enabled."));
169 #endif /* PARANOID */
170         }
171     }
172     else if (strcmp(key, "Interval") == 0) {
173 
174         /**
175          * Send the uptime at most once every 30 seconds
176          * Send the uptime at least once every 10 minutes
177          */
178         if (atol(value) < MINIMUM_INTERVAL) {
179 #if !defined PARANOID
180            /* is there an existing value? (i.e. we are here because of an HUP) */
181             if (cfg_interval > (MINIMUM_INTERVAL - 1)) {
182                 uplog(LOG_NOTICE, _("%s %s Using previous value %d (%s).\n"),
183                       _("NOTE:"),
184                       _("Uptime reporting interval is less than 30 seconds."),
185                       cfg_interval, CONFIGFILE);
186             }
187             else {
188                 cfg_interval = DEFAULT_INTERVAL;
189                 uplog(LOG_NOTICE, _("%s %s Using default value of %d (%s).\n"),
190                       _("NOTE:"),
191                       _("Uptime reporting interval is less than 30 seconds."),
192                       cfg_interval, CONFIGFILE);
193                 printf(_("%s %s Using default value of %ld (%s).\n"),
194                        _("NOTE:"),
195                        _("Uptime reporting interval is less than 30 seconds."),
196                        cfg_interval, CONFIGFILE);
197             }
198            /* exit(EX_USAGE); */
199 #else /* PARANOID */
200            /* use it anyway because we are paranoid */
201             cfg_interval = atol(value);
202             uplog(LOG_NOTICE, "%s %s %s\n", _("NOTE:"),
203                   _("Uptime reporting interval is less than 30 seconds."),
204                   _("Ignoring due to Paranoid Mode Enabled."));
205             printf("%s %s %s\n", _("NOTE:"),
206                    _("Uptime reporting interval is less than 30 seconds."),
207                    _("Ignoring due to Paranoid Mode Enabled."));
208 #endif /* PARANOID */
209         }
210         if (atol(value) > UPPER_INTERVAL) {
211             uplog(LOG_WARNING, "%s %s %s (%s)\n", _("WARNING:"),
212                   _("Uptime reporting interval is larger than 10 minutes."),
213                   _
214                   ("An interval between 30 seconds and 10 minutes is recommended."),
215                   CONFIGFILE);
216             printf("%s %s %s (%s)\n", _("WARNING:"),
217                    _("Uptime reporting interval is larger than 10 minutes."),
218                    _
219                    ("An interval between 30 seconds and 10 minutes is recommended."),
220                    CONFIGFILE);
221             cfg_interval = atol(value);
222         }
223         else {
224            /* OK to go. */
225             if (atol(value) > (MINIMUM_INTERVAL - 1)) {
226                 cfg_interval = atol(value);
227             }   /* else just keep the old value. */
228         }
229     }
230     else if (strcmp(key, "UptimeServer") == 0) {
231         strncpy(cfg_upserver, value, UPSERVER_MAXLEN);
232         cfg_upserver[UPSERVER_MAXLEN] = 0;
233     }
234     else if (strcmp(key, "ProxyServer") == 0) {
235         strncpy(cfg_proxyserver, value, PROXYSERVER_MAXLEN);
236         cfg_proxyserver[PROXYSERVER_MAXLEN] = 0;
237         have_proxyserver = 1;
238     }
239     else if (strcmp(key, "ProxyPort") == 0) {
240         cfg_proxyport = (in_port_t) atol(value);
241         if (!(cfg_proxyport >= 1 && cfg_proxyport <= 65535)) {
242             uplog(LOG_ERR, "%s ProxyPort %s (%s)\n", _("FATAL ERROR:"),
243                   _("must be in the <1 - 65535> range."), CONFIGFILE);
244             printf("%s ProxyPort %s (%s)\n", _("FATAL ERROR:"),
245                    _("must be in the <1 - 65535> range."), CONFIGFILE);
246             exit(EX_USAGE);
247         }
248         have_proxyserver = 1;
249     }
250     else if (strcmp(key, "ProxyUsername") == 0) {
251         strncpy(cfg_proxyuser, value, PROXYUSER_MAXLEN);
252         cfg_proxyuser[PROXYUSER_MAXLEN] = 0;
253         have_proxyserver = 1;
254     }
255     else if (strcmp(key, "ProxyPassword") == 0) {
256         strncpy(cfg_proxypass, value, PROXYPASS_MAXLEN);
257         cfg_proxypass[PROXYPASS_MAXLEN] = 0;
258         have_proxyserver = 1;
259     }
260     else if (strcmp(key, "PidFile") == 0) {
261         strncpy(cfg_pidfile, value, PIDFILE_MAXLEN);
262         cfg_pidfile[PIDFILE_MAXLEN] = 0;
263 
264     }
265     else if (strcmp(key, "SendIdle") == 0) {
266         cfg_SendIdle = (unsigned short int)atol(value);
267     }
268     else if (strcmp(key, "SendUsage") == 0) {
269         cfg_SendUsage = (unsigned short int)atol(value);
270     }
271     else if (strcmp(key, "SendLoadAvg") == 0) {
272         cfg_sendloadavg = (unsigned short int)atol(value);
273     }
274     else if (strcmp(key, "SendOSName") == 0) {
275         cfg_sendosname = (unsigned short int)atol(value);
276     }
277     else if (strcmp(key, "SendOSVersion") == 0) {
278         cfg_sendosversion = (unsigned short int)atol(value);
279     }
280     else if (strcmp(key, "SendCPU") == 0) {
281         cfg_sendcpu = (unsigned short int)atol(value);
282     }
283     else if (strcmp(key, "SendCPUDetail") == 0) {
284         cfg_sendcpudetail = (unsigned short int)atol(value);
285         if (!(cfg_sendcpudetail == 0 || cfg_sendcpudetail == 1)) {
286             uplog(LOG_ERR, "%s SendCPUDetail %s (%s)\n", _("FATAL ERROR:"),
287                   _("must be in the <0 - 1> range."), CONFIGFILE);
288             printf("%s SendCPUDetail %s (%s)\n", _("FATAL ERROR:"),
289                    _("must be in the <0 - 1> range."), CONFIGFILE);
290             exit(EX_USAGE);
291         }
292     }
293     else if (key[0] != '#') {
294         if (verbose > 2) {
295             fprintf(stderr, "upclient: %s config '%s' = '%s'\n",
296                     _("verbose 3:"), key, value);
297             uplog(LOG_INFO, "%s config '%s' = '%s'\n", _("verbose 3:"), key,
298                   value);
299         }
300         return 0;
301     }
302     if (verbose > 2) {
303         fprintf(stderr, "upclient: %s config '%s' = '%s'\n", _("verbose 3:"),
304                 key, value);
305         uplog(LOG_INFO, "%s config '%s' = '%s'\n", _("verbose 3:"), key, value);
306     }
307     return 1;
308 }
309 
310 /**
311  * @desc    isblank() for Solaris
312  */
313 #if defined PLATFORM_SOLARIS
314 static int
isblank(const char chr)315 isblank(const char chr)
316 {
317     return (chr == ' ' || chr == '\t');
318 }
319 #endif /* PLATFORM_SOLARIS */
320 
321 /**
322  * @desc    Skip white space
323  */
324 static int
skipwhite(const char * line,int pos)325 skipwhite(const char *line, int pos)
326 {
327     while (isblank((unsigned char)line[pos]))
328         pos++;
329     return pos;
330 }
331 
332 /**
333  * @desc    Is it an empty line?
334  */
335 static int
isemptyline(const char * line)336 isemptyline(const char *line)
337 {
338     return (line[skipwhite(line, 0)] == '\n');
339 }
340 
341 /**
342  * @desc    Is it a comment line?
343  */
344 static int
iscommentline(const char * line)345 iscommentline(const char *line)
346 {
347     return (line[skipwhite(line, 0)] == '#');
348 }
349 
350 /**
351  * @desc    Is it a valid end of line?
352  */
353 static int
isvalideol(const char * line,int pos)354 isvalideol(const char *line, int pos)
355 {
356     pos = skipwhite(line, pos);
357 
358     if (line[pos] == '\n')
359         return 1;
360 
361     if (line[pos] == '#' && line[strlen(line) - 1] == '\n')
362         return 1;
363 
364     return 0;
365 }
366 
367 /**
368  * @desc    Get key
369  */
370 static int
getkey(const char * line,int pos,char * key)371 getkey(const char *line, int pos, char *key)
372 {
373     int    i = 0;
374 
375     pos = skipwhite(line, pos);
376 
377    /* cast to unsigned for Solaris */
378     while (isalnum((unsigned char)line[pos]) && i < MAX_KEY_LEN) {
379         key[i] = line[pos];
380         i++;
381         pos++;
382     }
383     key[i] = '\0';
384 
385     if (isblank((unsigned char)line[pos]) || (unsigned char)line[pos] == '=')
386         return pos;
387     else
388         return 0;
389 }
390 
391 /**
392  * @desc    Get sign
393  */
394 static int
getsign(const char * line,int pos,int sign)395 getsign(const char *line, int pos, int sign)
396 {       /* const char or int for arg 3? */
397     pos = skipwhite(line, pos);
398     if (line[pos] == sign)
399         return pos + 1;
400     return 0;
401 }
402 
403 /**
404  * @desc    Get value of the key
405  */
406 static int
getval(const char * line,int pos,char * value)407 getval(const char *line, int pos, char *value)
408 {
409     int    i = 0;
410 
411     pos = skipwhite(line, pos);
412 
413    /* cast to unsigned for Solaris */
414     while (isgraph((unsigned char)line[pos]) && line[pos] != '#' &&
415            i < MAX_VAL_LEN) {
416         value[i] = line[pos];
417         i++;
418         pos++;
419     }
420     value[i] = '\0';
421 
422     if (isblank((unsigned char)line[pos]) || (unsigned char)line[pos] == '#' ||
423         (unsigned char)line[pos] == '\n')
424         return pos;
425     else
426         return 0;
427 }
428 
429 /**
430  * @desc    Parse config file line
431  */
432 static int
parseline(const char * line,char * key,char * value)433 parseline(const char *line, char *key, char *value)
434 {
435     int    linepos = 0;
436 
437     if (isemptyline(line))
438         return LINE_EMPTY;
439 
440     if (iscommentline(line))
441         return LINE_COMMENT;
442 
443     linepos = getkey(line, 0, key);
444     linepos = getsign(line, linepos, '=');
445    /* options.c:410: warning: passing arg 3 of `getsign' with different width
446       due to prototype */
447     linepos = getval(line, linepos, value);
448 
449     if (!isvalideol(line, linepos))
450         return LINE_ERR;
451 
452     return LINE_OPTION;
453 }
454 
455 /**
456  * @desc    Read and parse config file, activate options
457  *
458  * @caller  main()
459  *
460  * @param   none
461  *
462  * @return  true
463  */
464 extern void
read_config(void)465 read_config(void)
466 {
467     FILE  *fp;
468     char   line[MAX_LINE_LEN];
469     char   key[MAX_KEY_LEN];
470     char   value[MAX_VAL_LEN];
471     int    linenr = 1;
472     int    linetype;
473 
474     if (verbose > 2) {
475         uplog(LOG_INFO, "%s %s %s", _("verbose 3:"), _("Reading configfile:"),
476               CONFIGFILE);
477         fprintf(stderr, "upclient: %s %s %s\n", _("verbose 3:"),
478                 _("Reading configfile:"), CONFIGFILE);
479     }
480 
481    /* Try to open config file as specified during compile */
482     if (!(fp = fopen(CONFIGFILE, "r"))) {
483         fprintf(stderr, "upclient: %s %s\n",
484                 _("Could not open config file for reading:"), CONFIGFILE);
485         uplog(LOG_ERR, "%s %s", _("Could not open config file for reading:"),
486               CONFIGFILE);
487 
488        /* Try to open a config file in current directory */
489         if (fp = fopen("upclient.conf", "r")) {
490             printf("upclient: %s %s\n", _("Using config file found in:"),
491                    _("current directory"));
492             uplog(LOG_INFO, "%s %s", _("Using config file found in:"),
493                   _("current directory"));
494         }
495         else if (fp = fopen("/etc/upclient.conf", "r")) {
496            /* Failed, now try to open a config file in /etc */
497             printf("upclient: %s %s\n", _("Using config file found in:"),
498                    "/etc");
499             uplog(LOG_INFO, "%s %s", _("Using config file found in:"), "/etc");
500         }
501         else if (fp = fopen("/usr/local/etc/upclient.conf", "r")) {
502            /* Failed, now try to open a config file in /usr/local/etc */
503             printf("upclient: %s %s\n", _("Using config file found in:"),
504                    "/usr/local/etc");
505             uplog(LOG_INFO, "%s %s\n", _("Using config file found in:"),
506                   "/usr/local/etc");
507         }
508         else if (fp = fopen("/usr/pkg/etc/upclient.conf", "r")) {
509            /* Failed, now try to open a config file for NetBSD */
510             printf("upclient: %s %s\n", _("Using config file found in:"),
511                    "/usr/pkg/etc");
512             uplog(LOG_INFO, "%s %s\n", _("Using config file found in:"),
513                   "/usr/pkg/etc");
514         }
515         else {
516 
517             printf("%s %s \n- %s %s", _("FATAL ERROR:"),
518                    _("Could not find config file in:"), _("current directory"),
519                    "\n- /etc\n- /usr/local/etc\n- /usr/pkg/etc\n");
520             uplog(LOG_ERR, "%s %s \n- %s %s", _("FATAL ERROR:"),
521                   _("Could not find config file in:"), _("current directory"),
522                   "\n- /etc\n- /usr/local/etc\n- /usr/pkg/etc\n");
523             exit(EX_OSFILE);
524         }
525     }
526 
527    /* Read & parse config file */
528     while (!feof(fp)) {
529        /* Read one line */
530         fgets(line, MAX_LINE_LEN, fp);
531 
532        /* We got a problem Houston */
533         if (line[strlen(line) - 1] != '\n') {
534             printf(_("upclient: %s Config file line %d too long.\n"),
535                    _("FATAL ERROR:"), linenr);
536             uplog(LOG_ERR, _("%s Config file line %d too long."),
537                   _("FATAL ERROR:"), linenr);
538             exit(EX_DATAERR);
539         }
540 
541        /* Parse rule */
542         linetype = parseline(line, key, value);
543 
544         if (linetype == LINE_ERR) {
545             uplog(LOG_WARNING, _("%s Error in %s line %d:\n  content: %s"),
546                   _("WARNING:"), CONFIGFILE, linenr, line);
547             printf(_("upclient: %s Error in %s line %d:\n  content: %s\n"),
548                    _("WARNING:"), CONFIGFILE, linenr, line);
549         }
550 
551        /* If the line is an option, process it */
552         if (linetype == LINE_OPTION) {
553             if (!process_option(key, value)) {
554                 uplog(LOG_WARNING,
555                       _("%s Error in %s line %d:\n  key: %s, value: %s"),
556                       _("WARNING:"), CONFIGFILE, linenr, key, value);
557                 printf(_
558                        ("upclient: %s Error in %s line %d:\n  key: %s, value: %s\n"),
559                        _("WARNING:"), CONFIGFILE, linenr, key, value);
560             }
561         }
562 
563        /* The core functionality of this algorithm */
564         linenr++;
565     }
566 
567 #if defined DEBUG
568     print_options();
569 #endif /* DEBUG */
570 
571    /* Clean up the mess we made */
572     if (fclose(fp))
573         uplog(LOG_ERR, "%s", strerror(errno));
574 
575    /* Life is giving and taking */
576    /* return 1; */
577 }
578