1 /* conf.c - configuration handlers for upsd
3 Copyright (C) 2001 Russell Kroll <rkroll@exploits.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "nut_stdint.h"
29 static ups_t
*upstable
= NULL
;
32 /* Users can pass a -D[...] option to enable debugging.
33 * For the service tracing purposes, also the upsd.conf
34 * can define a debug_min value in the global section,
35 * to set the minimal debug level (CLI provided value less
36 * than that would not have effect, can only have more).
38 int nut_debug_level_global
= -1;
39 /* Debug level specified via command line - we revert to
40 * it when reloading if there was no DEBUG_MIN in upsd.conf
42 int nut_debug_level_args
= 0;
44 /* add another UPS for monitoring from ups.conf */
45 static void ups_create(const char *fn
, const char *name
, const char *desc
)
49 for (temp
= firstups
; temp
!= NULL
; temp
= temp
->next
) {
50 if (!strcasecmp(temp
->name
, name
)) {
51 upslogx(LOG_ERR
, "UPS name [%s] is already in use!", name
);
56 /* grab some memory and add the info */
57 temp
= xcalloc(1, sizeof(*temp
));
58 temp
->fn
= xstrdup(fn
);
59 temp
->name
= xstrdup(name
);
62 temp
->desc
= xstrdup(desc
);
68 memset(&temp
->read_overlapped
,0,sizeof(temp
->read_overlapped
));
69 memset(temp
->buf
,0,sizeof(temp
->buf
));
70 temp
->read_overlapped
.hEvent
= CreateEvent(NULL
, /* Security */
71 FALSE
, /* auto-reset*/
72 FALSE
, /* initial state = non signaled */
74 if(temp
->read_overlapped
.hEvent
== NULL
) {
75 upslogx(LOG_ERR
, "Can't create event for UPS [%s]",
80 temp
->sock_fd
= sstate_connect(temp
);
82 /* preload this to the current time to avoid false staleness */
83 time(&temp
->last_heard
);
85 temp
->next
= firstups
;
90 /* change the configuration of an existing UPS (used during reloads) */
91 static void ups_update(const char *fn
, const char *name
, const char *desc
)
95 temp
= get_ups_ptr(name
);
98 upslogx(LOG_ERR
, "UPS %s disappeared during reload", name
);
104 upslogx(LOG_ERR
, "UPS %s had a NULL filename!", name
);
106 /* let's give it something quick to use later */
107 temp
->fn
= xstrdup("");
110 /* when the filename changes, force a reconnect */
111 if (strcmp(temp
->fn
, fn
) != 0) {
113 upslogx(LOG_NOTICE
, "Redefined UPS [%s]", name
);
115 /* release all data */
116 sstate_infofree(temp
);
117 sstate_cmdfree(temp
);
118 pconf_finish(&temp
->sock_ctx
);
121 close(temp
->sock_fd
);
123 CloseHandle(temp
->sock_fd
);
125 temp
->sock_fd
= ERROR_FD
;
128 /* now redefine the filename and wrap up */
130 temp
->fn
= xstrdup(fn
);
133 /* update the description */
138 temp
->desc
= xstrdup(desc
);
142 /* always set this on reload */
146 /* returns 1 if "arg" was usable as a boolean value, 0 if not
147 * saves converted meaning of "arg" into referenced "result"
149 static int parse_boolean(char *arg
, int *result
)
151 if ( (!strcasecmp(arg
, "true")) || (!strcasecmp(arg
, "on")) || (!strcasecmp(arg
, "yes")) || (!strcasecmp(arg
, "1"))) {
155 if ( (!strcasecmp(arg
, "false")) || (!strcasecmp(arg
, "off")) || (!strcasecmp(arg
, "no")) || (!strcasecmp(arg
, "0"))) {
162 /* return 1 if usable, 0 if not */
163 static int parse_upsd_conf_args(size_t numargs
, char **arg
)
165 /* everything below here uses up through arg[1] */
169 /* DEBUG_MIN (NUM) */
170 /* debug_min (NUM) also acceptable, to be on par with ups.conf */
171 if (!strcasecmp(arg
[0], "DEBUG_MIN")) {
172 int lvl
= -1; /* typeof common/common.c: int nut_debug_level */
173 if ( str_to_int (arg
[1], &lvl
, 10) && lvl
>= 0 ) {
174 nut_debug_level_global
= lvl
;
176 upslogx(LOG_INFO
, "DEBUG_MIN has non numeric or negative value in upsd.conf");
181 /* MAXAGE <seconds> */
182 if (!strcmp(arg
[0], "MAXAGE")) {
183 if (isdigit((size_t)arg
[1][0])) {
184 maxage
= atoi(arg
[1]);
188 upslogx(LOG_ERR
, "MAXAGE has non numeric value (%s)!", arg
[1]);
193 /* TRACKINGDELAY <seconds> */
194 if (!strcmp(arg
[0], "TRACKINGDELAY")) {
195 if (isdigit((size_t)arg
[1][0])) {
196 tracking_delay
= atoi(arg
[1]);
200 upslogx(LOG_ERR
, "TRACKINGDELAY has non numeric value (%s)!", arg
[1]);
205 /* ALLOW_NO_DEVICE <bool> */
206 if (!strcmp(arg
[0], "ALLOW_NO_DEVICE")) {
207 if (isdigit((size_t)arg
[1][0])) {
208 allow_no_device
= (atoi(arg
[1]) != 0); /* non-zero arg is true here */
211 if (parse_boolean(arg
[1], &allow_no_device
))
214 upslogx(LOG_ERR
, "ALLOW_NO_DEVICE has non numeric and non boolean value (%s)!", arg
[1]);
218 /* ALLOW_NOT_ALL_LISTENERS <bool> */
219 if (!strcmp(arg
[0], "ALLOW_NOT_ALL_LISTENERS")) {
220 if (isdigit((size_t)arg
[1][0])) {
221 allow_not_all_listeners
= (atoi(arg
[1]) != 0); /* non-zero arg is true here */
224 if (parse_boolean(arg
[1], &allow_not_all_listeners
))
227 upslogx(LOG_ERR
, "ALLOW_NOT_ALL_LISTENERS has non numeric and non boolean value (%s)!", arg
[1]);
231 /* MAXCONN <connections> */
232 if (!strcmp(arg
[0], "MAXCONN")) {
233 if (isdigit((size_t)arg
[1][0])) {
234 /* FIXME: Check for overflows (and int size of nfds_t vs. long) - see get_max_pid_t() for example */
235 maxconn
= (nfds_t
)atol(arg
[1]);
239 upslogx(LOG_ERR
, "MAXCONN has non numeric value (%s)!", arg
[1]);
244 /* STATEPATH <dir> */
245 if (!strcmp(arg
[0], "STATEPATH")) {
246 const char *sp
= getenv("NUT_STATEPATH");
247 if (sp
&& strcmp(sp
, arg
[1])) {
248 /* Only warn if the two strings are not equal */
250 "Ignoring STATEPATH='%s' from configuration file, "
251 "in favor of NUT_STATEPATH='%s' environment variable",
252 NUT_STRARG(arg
[1]), NUT_STRARG(sp
));
255 statepath
= xstrdup(sp
? sp
: arg
[1]);
260 if (!strcmp(arg
[0], "DATAPATH")) {
262 datapath
= xstrdup(arg
[1]);
268 if (!strcmp(arg
[0], "CERTFILE")) {
270 certfile
= xstrdup(arg
[1]);
273 #elif (defined WITH_NSS) /* WITH_OPENSSL */
275 if (!strcmp(arg
[0], "CERTPATH")) {
277 certfile
= xstrdup(arg
[1]);
280 #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION
281 /* CERTREQUEST (0 | 1 | 2) */
282 if (!strcmp(arg
[0], "CERTREQUEST")) {
283 if (isdigit((size_t)arg
[1][0])) {
284 certrequest
= atoi(arg
[1]);
288 upslogx(LOG_ERR
, "CERTREQUEST has non numeric value (%s)!", arg
[1]);
292 #endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */
293 #endif /* WITH_OPENSSL | WITH_NSS */
295 #if defined(WITH_OPENSSL) || defined(WITH_NSS)
296 /* DISABLE_WEAK_SSL <bool> */
297 if (!strcmp(arg
[0], "DISABLE_WEAK_SSL")) {
298 if (parse_boolean(arg
[1], &disable_weak_ssl
))
301 upslogx(LOG_ERR
, "DISABLE_WEAK_SSL has non boolean value (%s)!", arg
[1]);
304 #endif /* WITH_OPENSSL | WITH_NSS */
306 /* ACCEPT <aclname> [<aclname>...] */
307 if (!strcmp(arg
[0], "ACCEPT")) {
308 upslogx(LOG_WARNING
, "ACCEPT in upsd.conf is no longer supported - switch to LISTEN");
312 /* REJECT <aclname> [<aclname>...] */
313 if (!strcmp(arg
[0], "REJECT")) {
314 upslogx(LOG_WARNING
, "REJECT in upsd.conf is no longer supported - switch to LISTEN");
318 /* LISTEN <address> [<port>] */
319 if (!strcmp(arg
[0], "LISTEN")) {
321 listen_add(arg
[1], string_const(PORT
));
323 listen_add(arg
[1], arg
[2]);
327 /* everything below here uses up through arg[2] */
331 /* ACL <aclname> <ip block> */
332 if (!strcmp(arg
[0], "ACL")) {
333 upslogx(LOG_WARNING
, "ACL in upsd.conf is no longer supported - switch to LISTEN");
338 /* CERTIDENT <name> <passwd> */
339 if (!strcmp(arg
[0], "CERTIDENT")) {
341 certname
= xstrdup(arg
[1]);
343 certpasswd
= xstrdup(arg
[2]);
346 #endif /* WITH_NSS */
352 /* called for fatal errors in parseconf like malloc failures */
353 static void upsd_conf_err(const char *errmsg
)
355 upslogx(LOG_ERR
, "Fatal error in parseconf (upsd.conf): %s", errmsg
);
358 void load_upsdconf(int reloading
)
364 snprintf(fn
, sizeof(fn
), "%s/upsd.conf", confpath());
368 pconf_init(&ctx
, upsd_conf_err
);
370 if (!pconf_file_begin(&ctx
, fn
)) {
374 fatalx(EXIT_FAILURE
, "%s", ctx
.errmsg
);
376 upslogx(LOG_ERR
, "Reload failed: %s", ctx
.errmsg
);
381 /* if upsd.conf added or changed
382 * (or commented away) the debug_min
383 * setting, detect that */
384 nut_debug_level_global
= -1;
387 while (pconf_file_next(&ctx
)) {
388 if (pconf_parse_error(&ctx
)) {
389 upslogx(LOG_ERR
, "Parse error: %s:%d: %s",
390 fn
, ctx
.linenum
, ctx
.errmsg
);
398 if (!parse_upsd_conf_args(ctx
.numargs
, ctx
.arglist
)) {
400 char errmsg
[SMALLBUF
];
402 snprintf(errmsg
, sizeof(errmsg
),
403 "upsd.conf: invalid directive");
405 for (i
= 0; i
< ctx
.numargs
; i
++)
406 snprintfcat(errmsg
, sizeof(errmsg
), " %s",
410 upslogx(LOG_WARNING
, "%s", errmsg
);
416 if (nut_debug_level_global
> -1) {
418 "Applying DEBUG_MIN %d from upsd.conf",
419 nut_debug_level_global
);
420 nut_debug_level
= nut_debug_level_global
;
422 /* DEBUG_MIN is absent or commented-away in ups.conf */
424 "Applying debug level %d from "
425 "original command line arguments",
426 nut_debug_level_args
);
427 nut_debug_level
= nut_debug_level_args
;
431 /* FIXME: Per legacy behavior, we silently went on.
432 * Maybe should abort on unusable configs?
435 upslogx(LOG_ERR
, "Encountered %d config errors, those entries were ignored", numerrors
);
441 /* callback during parsing of ups.conf */
442 void do_upsconf_args(char *upsname
, char *var
, char *val
)
446 /* no "global" stuff for us */
451 /* check if UPS is already listed */
452 for (temp
= upstable
; temp
!= NULL
; temp
= temp
->next
) {
453 if (!strcmp(temp
->upsname
, upsname
)) {
458 /* if not listed, create a new entry and prepend it to the list */
460 temp
= xcalloc(1, sizeof(*temp
));
461 temp
->upsname
= xstrdup(upsname
);
462 temp
->next
= upstable
;
466 if (!strcmp(var
, "driver")) {
468 temp
->driver
= xstrdup(val
);
469 } else if (!strcmp(var
, "port")) {
471 temp
->port
= xstrdup(val
);
472 } else if (!strcmp(var
, "desc")) {
474 temp
->desc
= xstrdup(val
);
478 /* add valid UPSes from ups.conf to the internal structures */
479 void upsconf_add(int reloading
)
481 ups_t
*tmp
= upstable
, *next
;
482 char statefn
[SMALLBUF
];
485 upslogx(LOG_WARNING
, "Warning: no UPS definitions in ups.conf");
491 /* save for later, since we delete as we go along */
494 /* this should always be set, but better safe than sorry */
500 /* don't accept an entry that's missing items */
501 if ((!tmp
->driver
) || (!tmp
->port
)) {
502 upslogx(LOG_WARNING
, "Warning: ignoring incomplete configuration for UPS [%s]\n",
505 snprintf(statefn
, sizeof(statefn
), "%s-%s",
506 tmp
->driver
, tmp
->upsname
);
508 /* if a UPS exists, update it, else add it as new */
509 if ((reloading
) && (get_ups_ptr(tmp
->upsname
) != NULL
))
510 ups_update(statefn
, tmp
->upsname
, tmp
->desc
);
512 ups_create(statefn
, tmp
->upsname
, tmp
->desc
);
515 /* free tmp's resources */
526 /* upstable should be completely gone by this point */
530 /* remove a UPS from the linked list */
531 static void delete_ups(upstype_t
*target
)
533 upstype_t
*ptr
, *last
;
538 ptr
= last
= firstups
;
542 upslogx(LOG_NOTICE
, "Deleting UPS [%s]", target
->name
);
544 /* make sure nobody stays logged into this thing */
545 kick_login_clients(target
->name
);
547 /* about to delete the first ups? */
549 firstups
= ptr
->next
;
551 last
->next
= ptr
->next
;
553 if (VALID_FD(ptr
->sock_fd
))
557 CloseHandle(ptr
->sock_fd
);
561 sstate_infofree(ptr
);
563 pconf_finish(&ptr
->sock_ctx
);
577 /* shouldn't happen */
578 upslogx(LOG_ERR
, "delete_ups: UPS not found");
581 /* see if we can open a file */
582 static int check_file(const char *fn
)
584 char chkfn
[SMALLBUF
];
587 snprintf(chkfn
, sizeof(chkfn
), "%s/%s", confpath(), fn
);
589 f
= fopen(chkfn
, "r");
592 upslog_with_errno(LOG_ERR
, "Reload failed: can't open %s", chkfn
);
593 return 0; /* failed */
600 /* called after SIGHUP */
601 void conf_reload(void)
603 upstype_t
*upstmp
, *upsnext
;
605 upslogx(LOG_INFO
, "SIGHUP: reloading configuration");
607 /* see if we can access upsd.conf before blowing away the config */
608 if (!check_file("upsd.conf"))
611 /* reset retain flags on all known UPS entries */
615 upstmp
= upstmp
->next
;
618 /* reload from ups.conf */
619 read_upsconf(1); /* 1 = may abort upon fundamental errors */
620 upsconf_add(1); /* 1 = reloading */
622 /* now reread upsd.conf */
623 load_upsdconf(1); /* 1 = reloading */
625 /* now delete all UPS entries that didn't get reloaded */
630 /* upstmp may be deleted during this pass */
631 upsnext
= upstmp
->next
;
633 if (upstmp
->retain
== 0)
639 /* did they actually delete the last UPS? */
640 if (firstups
== NULL
)
641 upslogx(LOG_WARNING
, "Warning: no UPSes currently defined!");
643 /* and also make sure upsd.users can be read... */
644 if (!check_file("upsd.users"))
647 /* delete all users */
650 /* and finally reread from upsd.users */