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