UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / lib / apcconfig.c
blob0035986c47262746f0d24595a9c52ade3acdede0
1 /*
2 * apcconfig.c
4 * Config file parser.
5 */
7 /*
8 * Copyright (C) 2000-2004 Kern Sibbald
9 * Copyright (C) Riccardo Facchetti <riccardo@master.oasi.gpa.it>
10 * Copyright (C) Jonathan H N Chin <jc254@newton.cam.ac.uk>
11 * Copyright (C) 1996-99 Andre M. Hedrick <andre@suse.com>
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of version 2 of the GNU General
15 * Public License as published by the Free Software Foundation.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public
23 * License along with this program; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
28 #include "apc.h"
30 char argvalue[MAXSTRING];
33 * We use more complicated defaults for these constants in some cases,
34 * so stick them in arrays that can be modified at runtime.
36 char APCCONF[APC_FILENAME_MAX] = SYSCONFDIR APCCONF_FILE;
38 /* ---------------------------------------------------------------------- */
40 typedef int (HANDLER) (UPSINFO *, int, const GENINFO *, const char *);
42 static HANDLER match_int, match_range, match_str;
43 static HANDLER match_facility, match_index;
44 static HANDLER obsolete;
46 #ifdef UNSUPPORTED_CODE
47 static HANDLER start_ups, end_ups;
48 #endif
50 /* ---------------------------------------------------------------------- */
52 static const GENINFO onoroff[] = {
53 { "off", "off", FALSE },
54 { "on", "on", TRUE },
55 { NULL, "value must be ON or OFF", FALSE },
58 static const GENINFO cables[] = {
59 { "simple", "Custom Cable Simple", CUSTOM_SIMPLE },
60 { "smart", "Custom Cable Smart", CABLE_SMART },
61 { "ether", "Ethernet Link", APC_NET },
62 { "940-0119A", "APC Cable 940-0119A", APC_940_0119A },
63 { "940-0127A", "APC Cable 940-0127A", APC_940_0127A },
64 { "940-0128A", "APC Cable 940-0128A", APC_940_0128A },
65 { "940-0020B", "APC Cable 940-0020B", APC_940_0020B },
66 { "940-0020C", "APC Cable 940-0020C", APC_940_0020C },
67 { "940-0023A", "APC Cable 940-0023A", APC_940_0023A },
68 { "940-0024B", "APC Cable 940-0024B", CABLE_SMART },
69 { "940-0024C", "APC Cable 940-0024C", CABLE_SMART },
70 { "940-1524C", "APC Cable 940-1524C", CABLE_SMART },
71 { "940-0024G", "APC Cable 940-0024G", CABLE_SMART },
72 { "940-0095A", "APC Cable 940-0095A", APC_940_0095A },
73 { "940-0095B", "APC Cable 940-0095B", APC_940_0095B },
74 { "940-0095C", "APC Cable 940-0095C", APC_940_0095C },
75 { "MAM-04-02-2000", "MAM Cable 04-02-2000", MAM_CABLE },
76 { "usb", "USB Cable", USB_CABLE },
77 { NULL, "*invalid-cable*", NO_CABLE },
80 static const GENINFO upsclasses[] = {
81 { "standalone", "Stand Alone", STANDALONE },
82 { "shareslave", "ShareUPS Slave", SHARESLAVE },
83 { "sharemaster", "ShareUPS Master", SHAREMASTER },
84 { NULL, "*invalid-ups-class*", NO_CLASS },
87 static const GENINFO logins[] = {
88 { "always", "always", ALWAYS }, /* must come first */
89 { "disable", "disable", NEVER },
90 { "timeout", "timeout", TIMEOUT },
91 { "percent", "percent", PERCENT },
92 { "minutes", "minutes", MINUTES },
93 { NULL, "*invalid-login-mode*", NO_LOGON },
96 static const GENINFO modes[] = {
97 { "disable", "Network & ShareUPS Disabled", DISABLE },
98 { "share", "ShareUPS", SHARE },
99 { NULL, "*invalid-ups-mode*", NO_SHARE_NET },
102 static const GENINFO types[] = {
103 { "dumb", "DUMB UPS Driver", DUMB_UPS },
104 { "apcsmart", "APC Smart UPS (any)", APCSMART_UPS },
105 { "usb", "USB UPS Driver", USB_UPS },
106 { "snmp", "SNMP UPS Driver", SNMPLITE_UPS },
107 { "net", "NETWORK UPS Driver", NETWORK_UPS },
108 { "test", "TEST UPS Driver", TEST_UPS },
109 { "pcnet", "PCNET UPS Driver", PCNET_UPS },
110 { "netsnmp", "NET-SNMP UPS Driver", SNMP_UPS },
111 { NULL, "*invalid-ups-type*", NO_UPS },
114 typedef struct {
115 const char *key;
116 HANDLER *handler;
117 size_t offset;
118 const GENINFO *values;
119 } PAIRS;
121 static const PAIRS table[] = {
123 /* General parameters */
125 {"UPSNAME", match_str, WHERE(upsname), SIZE(upsname)},
126 {"UPSCABLE", match_range, WHERE(cable), cables},
127 {"UPSTYPE", match_range, WHERE(mode), types},
128 {"DEVICE", match_str, WHERE(device), SIZE(device)},
129 {"POLLTIME", match_int, WHERE(polltime), 0},
131 /* Paths */
132 {"LOCKFILE", match_str, WHERE(lockpath), SIZE(lockpath)},
133 {"SCRIPTDIR", match_str, WHERE(scriptdir), SIZE(scriptdir)},
134 {"PWRFAILDIR", match_str, WHERE(pwrfailpath), SIZE(pwrfailpath)},
135 {"NOLOGINDIR", match_str, WHERE(nologinpath), SIZE(nologinpath)},
137 /* Configuration parameters used during power failures */
138 {"ANNOY", match_int, WHERE(annoy), 0},
139 {"ANNOYDELAY", match_int, WHERE(annoydelay), 0},
140 {"ONBATTERYDELAY", match_int, WHERE(onbattdelay), 0},
141 {"TIMEOUT", match_int, WHERE(maxtime), 0},
142 {"NOLOGON", match_range, WHERE(nologin), logins},
143 {"BATTERYLEVEL", match_int, WHERE(percent), 0},
144 {"MINUTES", match_int, WHERE(runtime), 0},
145 {"KILLDELAY", match_int, WHERE(killdelay), 0},
147 /* Configuration parmeters for network information server */
148 {"NETSERVER", match_index, WHERE(netstats), onoroff},
149 {"NISIP", match_str, WHERE(nisip), SIZE(nisip)},
150 {"NISPORT", match_int, WHERE(statusport), 0},
152 /* Configuration parameters for event logging */
153 {"EVENTSFILE", match_str, WHERE(eventfile), SIZE(eventfile)},
154 {"EVENTSFILEMAX", match_int, WHERE(eventfilemax), 0},
156 /* Configuration parameters to control system logging */
157 {"FACILITY", match_facility, 0, 0},
158 {"STATFILE", match_str, WHERE(statfile), SIZE(statfile)},
159 {"LOGSTATS", match_index, WHERE(logstats), onoroff},
160 {"STATTIME", match_int, WHERE(stattime), 0},
161 {"DATATIME", match_int, WHERE(datatime), 0},
163 /* Values used to set UPS EPROM for --configure */
164 {"SELFTEST", match_str, WHERE(selftest), SIZE(selftest)},
165 {"HITRANSFER", match_int, WHERE(hitrans), 0},
166 {"LOTRANSFER", match_int, WHERE(lotrans), 0},
167 {"LOWBATT", match_int, WHERE(dlowbatt), 0},
168 {"WAKEUP", match_int, WHERE(dwake), 0},
169 {"RETURNCHARGE", match_int, WHERE(rtnpct), 0},
170 {"OUTPUTVOLTS", match_int, WHERE(NomOutputVoltage), 0},
171 {"SLEEP", match_int, WHERE(dshutd), 0},
172 {"BEEPSTATE", match_str, WHERE(beepstate), SIZE(beepstate)},
173 {"BATTDATE", match_str, WHERE(battdat), SIZE(battdat)},
174 {"SENSITIVITY", match_str, WHERE(sensitivity), SIZE(sensitivity)},
176 /* Configuration statements for network sharing of the UPS */
177 {"UPSCLASS", match_range, WHERE(upsclass), upsclasses},
178 {"UPSMODE", match_range, WHERE(sharenet), modes },
179 {"NETTIME", match_int, WHERE(polltime), 0 },
182 * Obsolete configuration options: To be removed in the future.
183 * The warning string is passed in the GENINFO* field since it is
184 * not used any more for obsoleted options: we are only interested in
185 * printing the message.
186 * There is a new meaning for offset field, too. If TRUE will bail out,
187 * if FALSE it will continue to run apcupsd. This way we can bail out
188 * if an obsolete option is too important to continue running apcupsd.
190 {"CONTROL", obsolete, TRUE, (GENINFO *)"CONTROL config directive is obsolete" },
191 {"NETACCESS", obsolete, TRUE, (GENINFO *)"NETACCESS config directive is obsolete" },
192 {"MASTER", obsolete, TRUE, (GENINFO *)"MASTER config directive is obsolete" },
193 {"USERMAGIC", obsolete, FALSE, (GENINFO *)"USERMAGIC config directive is obsolete" },
194 {"SLAVE", obsolete, FALSE, (GENINFO *)"SLAVE config directive is obsolete" },
195 {"NETPORT", obsolete, FALSE, (GENINFO *)"NETPORT config directive is obsolete" },
196 {"NETSTATUS", obsolete, FALSE, (GENINFO *)"NETSTATUS config directive is obsolete" },
197 {"SERVERPORT", obsolete, FALSE, (GENINFO *)"SERVERPORT config directive is obsolete"},
199 /* must be last */
200 {NULL, 0, 0, 0}
203 static int obsolete(UPSINFO *ups, int offset, const GENINFO * junk, const char *v)
205 char *msg = (char *)junk;
207 fprintf(stderr, "%s\n", msg);
208 if (!offset) {
209 fprintf(stderr, "error ignored.\n");
210 return SUCCESS;
213 return FAILURE;
216 #ifdef UNSUPPORTED_CODE
217 static int start_ups(UPSINFO *ups, int offset, const GENINFO * size, const char *v)
219 char x[MAXSTRING];
221 if (!sscanf(v, "%s", x))
222 return FAILURE;
224 /* Verify that we don't already have an UPS with the same name. */
225 for (ups = NULL; (ups = getNextUps(ups)) != NULL;)
226 if (strncmp(x, ups->upsname, UPSNAMELEN) == 0) {
227 fprintf(stderr, "%s: duplicate upsname [%s] in config file.\n",
228 argvalue, ups->upsname);
229 return FAILURE;
232 /* Here start the definition of a new UPS to add to the linked list. */
233 ups = new_ups();
235 if (ups == NULL) {
236 Error_abort1("%s: not enough memory.\n", argvalue);
237 return FAILURE;
240 init_ups_struct(ups);
243 astrncpy((char *)AT(ups, offset), x, (int)size);
245 /* Terminate string */
246 *((char *)AT(ups, (offset + (int)size) - 1)) = 0;
247 return SUCCESS;
250 static int end_ups(UPSINFO *ups, int offset, const GENINFO * size, const char *v)
252 char x[MAXSTRING];
254 if (!sscanf(v, "%s", x))
255 return FAILURE;
257 if (ups == NULL) {
258 fprintf(stderr, "%s: upsname [%s] mismatch in config file.\n", argvalue, x);
259 return FAILURE;
262 if (strncmp(x, ups->upsname, UPSNAMELEN) != 0) {
263 fprintf(stderr, "%s: upsname [%s] mismatch in config file.\n",
264 argvalue, ups->upsname);
265 return FAILURE;
268 insertUps(ups);
270 return SUCCESS;
272 #endif
275 static int match_int(UPSINFO *ups, int offset, const GENINFO * junk, const char *v)
277 int x;
279 if (sscanf(v, "%d", &x)) {
280 *(int *)AT(ups, offset) = x;
281 return SUCCESS;
283 return FAILURE;
286 static int match_range(UPSINFO *ups, int offset, const GENINFO * vs, const char *v)
288 char x[MAXSTRING];
289 INTERNALGENINFO *t;
291 if (!vs) {
292 /* Shouldn't ever happen so abort if it ever does. */
293 Error_abort1("%s: Bogus configuration table! Fix and recompile.\n",
294 argvalue);
297 if (!sscanf(v, "%s", x))
298 return FAILURE;
300 for (; vs->name; vs++)
301 if (!strcasecmp(x, vs->name))
302 break;
304 t = (INTERNALGENINFO *) AT(ups, offset);
306 /* Copy the structure */
307 if (vs->name == NULL) {
309 * A NULL here means that the config value is bogus, so
310 * print error message (and log it).
312 log_event(ups, LOG_WARNING,
313 "%s: Bogus configuration value (%s)\n", argvalue, vs->long_name);
314 fprintf(stderr,
315 "%s: Bogus configuration value (%s)\n", argvalue, vs->long_name);
316 return FAILURE;
321 * THIS IS VERY UGLY. If some idiot like myself doesn't
322 * know enough to define all the appropriate variables
323 * with all the correct ordering and sizes, this will
324 * overwrite memory.
326 t->type = vs->type;
327 astrncpy(t->name, vs->name, sizeof(t->name));
328 astrncpy(t->long_name, vs->long_name, sizeof(t->long_name));
329 return SUCCESS;
333 * Similar to match range except that it only returns the index of the
334 * item.
336 static int match_index(UPSINFO *ups, int offset, const GENINFO * vs, const char *v)
338 char x[MAXSTRING];
340 if (!vs) {
341 /* Shouldn't ever happen so abort if it ever does. */
342 Error_abort1("%s: Bogus configuration table! Fix and recompile.\n",
343 argvalue);
346 if (!sscanf(v, "%s", x))
347 return FAILURE;
349 for (; vs->name; vs++)
350 if (!strcasecmp(x, vs->name))
351 break;
353 if (vs->name == NULL) {
355 * A NULL here means that the config value is bogus, so
356 * print error message (and log it).
358 log_event(ups, LOG_WARNING,
359 "%s: Bogus configuration value (%s)\n", argvalue, vs->long_name);
360 fprintf(stderr,
361 "%s: Bogus configuration value (%s)\n", argvalue, vs->long_name);
362 return FAILURE;
365 /* put it in ups buffer */
366 *(int *)AT(ups, offset) = vs->type;
367 return SUCCESS;
372 * We accept strings containing internal whitespace (in order to support
373 * paths with spaces in them) but truncate after '#' since we assume it
374 * marks a comment.
376 static int match_str(UPSINFO *ups, int offset, const GENINFO * gen, const char *v)
378 char x[MAXSTRING];
379 long size = (long)gen;
381 /* Copy the string so we can edit it in place */
382 astrncpy(x, v, sizeof(x));
384 /* Remove trailing comment, if there is one */
385 char *ptr = strchr(x, '#');
386 if (ptr)
387 *ptr = '\0';
389 /* Remove any trailing whitespace */
390 ptr = x + strlen(x) - 1;
391 while (ptr >= x && isspace(*ptr))
392 *ptr-- = '\0';
394 astrncpy((char *)AT(ups, offset), x, (int)size);
395 return SUCCESS;
398 static int match_facility(UPSINFO *ups, int offset,
399 const GENINFO *junk, const char *v)
401 const struct {
402 const char *fn;
403 int fi;
404 } facnames[] = {
405 {"daemon", LOG_DAEMON},
406 {"local0", LOG_LOCAL0},
407 {"local1", LOG_LOCAL1},
408 {"local2", LOG_LOCAL2},
409 {"local3", LOG_LOCAL3},
410 {"local4", LOG_LOCAL4},
411 {"local5", LOG_LOCAL5},
412 {"local6", LOG_LOCAL6},
413 {"local7", LOG_LOCAL7},
414 {"lpr", LOG_LPR},
415 {"mail", LOG_MAIL},
416 {"news", LOG_NEWS},
417 {"uucp", LOG_UUCP},
418 {"user", LOG_USER},
419 {NULL, -1}
421 int i;
422 char x[MAXSTRING];
423 int oldfac;
425 if (!sscanf(v, "%s", x))
426 return FAILURE;
428 oldfac = ups->sysfac;
429 for (i = 0; facnames[i].fn; i++) {
430 if (!(strcasecmp(facnames[i].fn, x))) {
431 ups->sysfac = facnames[i].fi;
432 /* if it changed, close log file and reopen it */
433 if (ups->sysfac != oldfac) {
434 closelog();
435 openlog("apcupsd", LOG_CONS | LOG_PID, ups->sysfac);
437 return SUCCESS;
441 return FAILURE;
444 /* ---------------------------------------------------------------------- */
447 ** This function has been so buggy, I thought commentary was needed.
448 ** Be careful about changing anything. Make sure you understand what is
449 ** going on first. Even the people who wrote it kept messing it up... 8^)
451 static int ParseConfig(UPSINFO *ups, char *line)
453 const PAIRS *p;
454 char *key, *value;
457 ** Hopefully my notation is obvious enough:
458 ** (a) : a
459 ** a|b : a or b
460 ** a? : zero or one a
461 ** a* : zero or more a
462 ** a+ : one or more a
464 ** On entry, situation is expected to be:
466 ** line = WS* ((KEY (WS+ VALUE)* WS* (WS+ COMMENT)?) | COMMENT?) (EOL|EOF)
467 ** key = undef
468 ** value = undef
470 ** if line is not of this form we will eventually return an error
471 ** line, key, value point to null-terminated strings
472 ** EOF may be end of file, or if it occurs alone signifies null string
475 /* skip initial whitespace */
476 for (key = line; *key && isspace(*key); key++)
480 ** key = ((KEY (WS+ VALUE)* WS* (WS+ COMMENT)?) | COMMENT?) (EOL|EOF)
481 ** value = undef
483 /* catch EOF (might actually be only an empty line) and comments */
484 if (!*key || (*key == '#'))
485 return (SUCCESS);
488 ** key = (KEY (WS+ VALUE)* WS* (WS+ COMMENT)? (EOL|EOF)
489 ** value = undef
491 /* convert key to UPPERCASE */
492 for (value = key; *value && !isspace(*value); value++)
493 *value = toupper(*value);
496 ** key = KEY (WS+ VALUE)* WS* (WS+ COMMENT)? (EOL|EOF)
497 ** value = (WS+ VALUE)* WS* (WS+ COMMENT)? (EOL|EOF)
499 /* null out whitespace in the string */
500 for (; *value && isspace(*value); value++)
501 *value = '\0';
504 ** key = KEY
505 ** value = (VALUE (WS+ VALUE)* WS* (WS+ COMMENT)? (EOL|EOF))
506 ** | (COMMENT (EOL|EOF))
507 ** | EOF
509 ** key is now fully `normalised' (no leading or trailing garbage)
510 ** value contains zero or more whitespace delimited elements and there
511 ** may be a trailing comment.
513 /* look for key in table */
514 for (p = table; p->key && strcmp(p->key, key); p++)
518 ** It is *not* the responsibility of a dispatcher (ie. ParseConfig())
519 ** to parse `value'.
520 ** Parsing `value' is the responsibility of the handler (ie. p->handler()).
521 ** (Although one could conceive of the case where a ParseConfig()-alike
522 ** function were invoked recursively as the handler...!)
524 ** Currently all the handler functions (match_int(), match_str(), etc)
525 ** just use sscanf() to pull out the bits they want out of `value'.
527 ** In particular, if "%s" is used, leading/trailing whitespace
528 ** is automatically removed.
530 ** In addition, unused bits of value are simply discarded if the handler
531 ** does not use them. So trailing garbage on lines is unimportant.
533 if (p->key) {
534 if (p->handler)
535 return (p->handler(ups, p->offset, p->values, value));
536 else
537 return (SUCCESS);
538 } else {
539 return (FAILURE);
543 /* ---------------------------------------------------------------------- */
545 #define BUF_SIZE 1000
548 * Setup general defaults for the ups structure.
549 * N.B. Do not zero the structure because it already has
550 * pthreads structures initialized.
552 void init_ups_struct(UPSINFO *ups)
554 ups->fd = -1;
556 ups->set_plugged();
558 astrncpy(ups->nologin.name, logins[0].name, sizeof(ups->nologin.name));
559 astrncpy(ups->nologin.long_name, logins[0].long_name,
560 sizeof(ups->nologin.long_name));
561 ups->nologin.type = logins[0].type;
563 ups->annoy = 5 * 60; /* annoy every 5 mins */
564 ups->annoydelay = 60; /* must be > than annoy to work, why???? */
565 ups->onbattdelay = 6;
566 ups->maxtime = 0;
567 ups->nologin_time = 0;
568 ups->nologin_file = FALSE;
570 ups->stattime = 0;
571 ups->datatime = 0;
572 ups->polltime = 60;
573 ups->percent = 10;
574 ups->runtime = 5;
575 ups->netstats = TRUE;
576 ups->statusport = NISPORT;
577 ups->upsmodel[0] = 0; /* end of string */
580 /* EPROM values that can be changed with config directives */
582 astrncpy(ups->sensitivity, "-1", sizeof(ups->sensitivity)); /* no value */
583 ups->dwake = -1;
584 ups->dshutd = -1;
585 astrncpy(ups->selftest, "-1", sizeof(ups->selftest)); /* no value */
586 ups->lotrans = -1;
587 ups->hitrans = -1;
588 ups->rtnpct = -1;
589 ups->dlowbatt = -1;
590 ups->NomOutputVoltage = -1;
591 astrncpy(ups->beepstate, "-1", sizeof(ups->beepstate)); /* no value */
593 ups->nisip[0] = 0; /* no nis IP file as default */
595 ups->lockfile = -1;
597 ups->clear_shut_load();
598 ups->clear_shut_btime();
599 ups->clear_shut_ltime();
600 ups->clear_shut_emerg();
601 ups->clear_shut_remote();
603 ups->sysfac = LOG_DAEMON;
605 ups->statfile[0] = 0; /* no stats file default */
606 ups->eventfile[0] = 0; /* no events file as default */
607 ups->eventfilemax = 10; /* trim the events file at 10K as default */
608 ups->event_fd = -1; /* no file open */
610 /* Default paths */
611 astrncpy(ups->scriptdir, SYSCONFDIR, sizeof(ups->scriptdir));
612 astrncpy(ups->pwrfailpath, PWRFAILDIR, sizeof(ups->pwrfailpath));
613 astrncpy(ups->nologinpath, NOLOGDIR, sizeof(ups->nologinpath));
615 /* Initialize UPS function codes */
616 ups->UPS_Cmd[CI_STATUS] = APC_CMD_STATUS;
617 ups->UPS_Cmd[CI_LQUAL] = APC_CMD_LQUAL;
618 ups->UPS_Cmd[CI_WHY_BATT] = APC_CMD_WHY_BATT;
619 ups->UPS_Cmd[CI_ST_STAT] = APC_CMD_ST_STAT;
620 ups->UPS_Cmd[CI_VLINE] = APC_CMD_VLINE;
621 ups->UPS_Cmd[CI_VMAX] = APC_CMD_VMAX;
622 ups->UPS_Cmd[CI_VMIN] = APC_CMD_VMIN;
623 ups->UPS_Cmd[CI_VOUT] = APC_CMD_VOUT;
624 ups->UPS_Cmd[CI_BATTLEV] = APC_CMD_BATTLEV;
625 ups->UPS_Cmd[CI_VBATT] = APC_CMD_VBATT;
626 ups->UPS_Cmd[CI_LOAD] = APC_CMD_LOAD;
627 ups->UPS_Cmd[CI_FREQ] = APC_CMD_FREQ;
628 ups->UPS_Cmd[CI_RUNTIM] = APC_CMD_RUNTIM;
629 ups->UPS_Cmd[CI_ITEMP] = APC_CMD_ITEMP;
630 ups->UPS_Cmd[CI_DIPSW] = APC_CMD_DIPSW;
631 ups->UPS_Cmd[CI_SENS] = APC_CMD_SENS;
632 ups->UPS_Cmd[CI_DWAKE] = APC_CMD_DWAKE;
633 ups->UPS_Cmd[CI_DSHUTD] = APC_CMD_DSHUTD;
634 ups->UPS_Cmd[CI_LTRANS] = APC_CMD_LTRANS;
635 ups->UPS_Cmd[CI_HTRANS] = APC_CMD_HTRANS;
636 ups->UPS_Cmd[CI_RETPCT] = APC_CMD_RETPCT;
637 ups->UPS_Cmd[CI_DALARM] = APC_CMD_DALARM;
638 ups->UPS_Cmd[CI_DLBATT] = APC_CMD_DLBATT;
639 ups->UPS_Cmd[CI_IDEN] = APC_CMD_IDEN;
640 ups->UPS_Cmd[CI_STESTI] = APC_CMD_STESTI;
641 ups->UPS_Cmd[CI_MANDAT] = APC_CMD_MANDAT;
642 ups->UPS_Cmd[CI_SERNO] = APC_CMD_SERNO;
643 ups->UPS_Cmd[CI_BATTDAT] = APC_CMD_BATTDAT;
644 ups->UPS_Cmd[CI_NOMBATTV] = APC_CMD_NOMBATTV;
645 ups->UPS_Cmd[CI_HUMID] = APC_CMD_HUMID;
646 ups->UPS_Cmd[CI_REVNO] = APC_CMD_REVNO;
647 ups->UPS_Cmd[CI_REG1] = APC_CMD_REG1;
648 ups->UPS_Cmd[CI_REG2] = APC_CMD_REG2;
649 ups->UPS_Cmd[CI_REG3] = APC_CMD_REG3;
650 ups->UPS_Cmd[CI_EXTBATTS] = APC_CMD_EXTBATTS;
651 ups->UPS_Cmd[CI_ATEMP] = APC_CMD_ATEMP;
652 ups->UPS_Cmd[CI_UPSMODEL] = APC_CMD_UPSMODEL;
653 ups->UPS_Cmd[CI_NOMOUTV] = APC_CMD_NOMOUTV;
654 ups->UPS_Cmd[CI_BADBATTS] = APC_CMD_BADBATTS;
655 ups->UPS_Cmd[CI_EPROM] = APC_CMD_EPROM;
656 ups->UPS_Cmd[CI_ST_TIME] = APC_CMD_ST_TIME;
657 ups->UPS_Cmd[CI_CYCLE_EPROM] = APC_CMD_CYCLE_EPROM;
658 ups->UPS_Cmd[CI_UPS_CAPS] = APC_CMD_UPS_CAPS;
661 void check_for_config(UPSINFO *ups, char *cfgfile)
663 FILE *apcconf;
664 char line[MAXSTRING];
665 int errors = 0;
666 int erpos = 0;
668 if ((apcconf = fopen(cfgfile, "r")) == NULL) {
669 Error_abort2("Error opening configuration file (%s): %s\n",
670 cfgfile, strerror(errno));
672 astrncpy(ups->configfile, cfgfile, sizeof(ups->configfile));
674 /* Check configuration file format is a suitable version */
675 if (fgets(line, sizeof(line), apcconf) != NULL) {
677 * The -1 in sizeof is there because of the last character
678 * to be checked. In line is '\n' and in APC... is '\0'.
679 * They never match so don't even compare them.
681 if (strncmp(line, APC_CONFIG_MAGIC, sizeof(APC_CONFIG_MAGIC) - 1) != 0) {
682 fprintf(stderr, "%s: Warning: old configuration file found.\n\n"
683 "%s: Expected: \"%s\"\n"
684 "%s: Found: \"%s\"\n\n"
685 "%s: Please check new file format and\n"
686 "%s: modify accordingly the first line\n"
687 "%s: of config file.\n\n"
688 "%s: Processing config file anyway.\n",
689 argvalue,
690 argvalue, APC_CONFIG_MAGIC,
691 argvalue, line, argvalue, argvalue, argvalue, argvalue);
695 * Here we have read alredy the first line of configuration
696 * so there may be the case where the first line is a config
697 * parameter and we must not discard it reading another line.
698 * Jump into the reading configuration loop.
700 goto jump_into_the_loop;
703 while (fgets(line, sizeof(line), apcconf) != NULL) {
704 jump_into_the_loop:
705 erpos++;
707 if (ParseConfig(ups, line)) {
708 errors++;
709 Dmsg1(100, "%s\n", line);
710 Dmsg2(100, "Parsing error at line %d of config file %s.\n", erpos, cfgfile);
714 fclose(apcconf);
717 * The next step will need a good ups struct.
718 * Of course if here we have errors, the apc struct is not good
719 * so don't bother to post-process it.
721 if (errors)
722 goto bail_out;
724 /* post-process the configuration stored in the ups structure */
727 * If annoy time is greater than initial delay, don't bother about
728 * initial delay and set it to 0.
730 if (ups->annoy >= ups->annoydelay)
731 ups->annoydelay = 0;
733 if (ups->sharenet.type == SHAREMASTER) {
734 ups->maxtime = 0;
735 ups->percent = 10;
736 ups->runtime = 5;
739 // Sanitize cable type & UPS mode. Since UPSTYPE (aka mode) and UPSCABLE
740 // can contradict eachother, the rule is that UPSTYPE wins. In all cases
741 // except Dumb we can determine cable type from mode so we ignore user's
742 // configured UPSCABLE and force it ourselves. In the case of Dumb UPSes we
743 // just ensure they picked a simple cable type.
744 switch (ups->mode.type)
746 case USB_UPS:
747 match_range(ups, WHERE(cable), cables, "usb");
748 break;
749 case SNMPLITE_UPS:
750 case SNMP_UPS:
751 case PCNET_UPS:
752 case NETWORK_UPS:
753 match_range(ups, WHERE(cable), cables, "ether");
754 break;
755 case APCSMART_UPS:
756 match_range(ups, WHERE(cable), cables, "smart");
757 break;
758 case DUMB_UPS:
759 // Abort if user specified dumb UPS type with smart cable
760 if (ups->cable.type >= CABLE_SMART)
761 Error_abort0("Invalid cable specified for Dumb UPS\n");
762 break;
763 case TEST_UPS:
764 // Allow anything in test mode
765 break;
769 * apcupsd _must_ have a lock file, mainly for security reasons.
770 * If apcupsd is running and a lock is not there the admin could
771 * mistakenly open a serial device with minicom for using it as a
772 * modem or other device. Think about the implications of sending
773 * extraneous characters to the UPS: a wrong char and the machine
774 * can be powered down.
776 * No thanks.
778 * On the other hand if a lock file is there, minicom (like any
779 * other serious serial program) will refuse to open the device.
781 * About "/var/lock//LCK..." nicety ... the right word IMHO is
782 * simplify, so don't bother.
784 if (ups->lockpath[0] == '\0')
785 astrncpy(ups->lockpath, LOCK_DEFAULT, sizeof(ups->lockpath));
787 /* If APC_NET, the lockfile is not needed. */
788 if (ups->cable.type != APC_NET) {
789 char *dev = strrchr(ups->device, '/');
791 astrncat(ups->lockpath, APC_LOCK_PREFIX, sizeof(ups->lockpath));
792 astrncat(ups->lockpath, dev ? ++dev : ups->device, sizeof(ups->lockpath));
793 } else {
794 ups->lockpath[0] = 0;
795 ups->lockfile = -1;
798 /* Append filenames to paths */
799 Dmsg1(200, "After config scriptdir: \"%s\"\n", ups->scriptdir);
800 Dmsg1(200, "After config pwrfailpath: \"%s\"\n", ups->pwrfailpath);
801 Dmsg1(200, "After config nologinpath: \"%s\"\n", ups->nologinpath);
802 astrncat(ups->nologinpath, NOLOGIN_FILE, sizeof(ups->nologinpath));
803 astrncat(ups->pwrfailpath, PWRFAIL_FILE, sizeof(ups->pwrfailpath));
805 switch (ups->nologin.type) {
806 case TIMEOUT:
807 if (ups->maxtime != 0)
808 ups->nologin_time = (int)(ups->maxtime * 0.9);
809 break;
810 case PERCENT:
811 ups->nologin_time = (int)(ups->percent * 1.1);
812 if (ups->nologin_time == ups->percent)
813 ups->nologin_time++;
814 break;
815 case MINUTES:
816 ups->nologin_time = (int)(ups->runtime * 1.1);
817 if (ups->nologin_time == ups->runtime)
818 ups->nologin_time++;
819 break;
820 default:
821 break;
824 bail_out:
825 if (errors)
826 error_exit("Terminating due to configuration file errors.\n");
828 return;