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,
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
;
50 /* ---------------------------------------------------------------------- */
52 static const GENINFO onoroff
[] = {
53 { "off", "off", FALSE
},
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
},
118 const GENINFO
*values
;
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},
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"},
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
);
209 fprintf(stderr
, "error ignored.\n");
216 #ifdef UNSUPPORTED_CODE
217 static int start_ups(UPSINFO
*ups
, int offset
, const GENINFO
* size
, const char *v
)
221 if (!sscanf(v
, "%s", x
))
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
);
232 /* Here start the definition of a new UPS to add to the linked list. */
236 Error_abort1("%s: not enough memory.\n", argvalue
);
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;
250 static int end_ups(UPSINFO
*ups
, int offset
, const GENINFO
* size
, const char *v
)
254 if (!sscanf(v
, "%s", x
))
258 fprintf(stderr
, "%s: upsname [%s] mismatch in config file.\n", argvalue
, x
);
262 if (strncmp(x
, ups
->upsname
, UPSNAMELEN
) != 0) {
263 fprintf(stderr
, "%s: upsname [%s] mismatch in config file.\n",
264 argvalue
, ups
->upsname
);
275 static int match_int(UPSINFO
*ups
, int offset
, const GENINFO
* junk
, const char *v
)
279 if (sscanf(v
, "%d", &x
)) {
280 *(int *)AT(ups
, offset
) = x
;
286 static int match_range(UPSINFO
*ups
, int offset
, const GENINFO
* vs
, const char *v
)
292 /* Shouldn't ever happen so abort if it ever does. */
293 Error_abort1("%s: Bogus configuration table! Fix and recompile.\n",
297 if (!sscanf(v
, "%s", x
))
300 for (; vs
->name
; vs
++)
301 if (!strcasecmp(x
, vs
->name
))
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
);
315 "%s: Bogus configuration value (%s)\n", argvalue
, vs
->long_name
);
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
327 astrncpy(t
->name
, vs
->name
, sizeof(t
->name
));
328 astrncpy(t
->long_name
, vs
->long_name
, sizeof(t
->long_name
));
333 * Similar to match range except that it only returns the index of the
336 static int match_index(UPSINFO
*ups
, int offset
, const GENINFO
* vs
, const char *v
)
341 /* Shouldn't ever happen so abort if it ever does. */
342 Error_abort1("%s: Bogus configuration table! Fix and recompile.\n",
346 if (!sscanf(v
, "%s", x
))
349 for (; vs
->name
; vs
++)
350 if (!strcasecmp(x
, vs
->name
))
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
);
361 "%s: Bogus configuration value (%s)\n", argvalue
, vs
->long_name
);
365 /* put it in ups buffer */
366 *(int *)AT(ups
, offset
) = vs
->type
;
372 * We accept strings containing internal whitespace (in order to support
373 * paths with spaces in them) but truncate after '#' since we assume it
376 static int match_str(UPSINFO
*ups
, int offset
, const GENINFO
* gen
, const char *v
)
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
, '#');
389 /* Remove any trailing whitespace */
390 ptr
= x
+ strlen(x
) - 1;
391 while (ptr
>= x
&& isspace(*ptr
))
394 astrncpy((char *)AT(ups
, offset
), x
, (int)size
);
398 static int match_facility(UPSINFO
*ups
, int offset
,
399 const GENINFO
*junk
, const char *v
)
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
},
425 if (!sscanf(v
, "%s", x
))
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
) {
435 openlog("apcupsd", LOG_CONS
| LOG_PID
, ups
->sysfac
);
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
)
457 ** Hopefully my notation is obvious enough:
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)
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)
483 /* catch EOF (might actually be only an empty line) and comments */
484 if (!*key
|| (*key
== '#'))
488 ** key = (KEY (WS+ VALUE)* WS* (WS+ COMMENT)? (EOL|EOF)
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
++)
505 ** value = (VALUE (WS+ VALUE)* WS* (WS+ COMMENT)? (EOL|EOF))
506 ** | (COMMENT (EOL|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())
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.
535 return (p
->handler(ups
, p
->offset
, p
->values
, value
));
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
)
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;
567 ups
->nologin_time
= 0;
568 ups
->nologin_file
= FALSE
;
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 */
585 astrncpy(ups
->selftest
, "-1", sizeof(ups
->selftest
)); /* no value */
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 */
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 */
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
)
664 char line
[MAXSTRING
];
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",
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
) {
707 if (ParseConfig(ups
, line
)) {
709 Dmsg1(100, "%s\n", line
);
710 Dmsg2(100, "Parsing error at line %d of config file %s.\n", erpos
, cfgfile
);
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.
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
)
733 if (ups
->sharenet
.type
== SHAREMASTER
) {
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
)
747 match_range(ups
, WHERE(cable
), cables
, "usb");
753 match_range(ups
, WHERE(cable
), cables
, "ether");
756 match_range(ups
, WHERE(cable
), cables
, "smart");
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");
764 // Allow anything in test mode
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.
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
));
794 ups
->lockpath
[0] = 0;
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
) {
807 if (ups
->maxtime
!= 0)
808 ups
->nologin_time
= (int)(ups
->maxtime
* 0.9);
811 ups
->nologin_time
= (int)(ups
->percent
* 1.1);
812 if (ups
->nologin_time
== ups
->percent
)
816 ups
->nologin_time
= (int)(ups
->runtime
* 1.1);
817 if (ups
->nologin_time
== ups
->runtime
)
826 error_exit("Terminating due to configuration file errors.\n");