Merge pull request #2680 from masterwishx/work2471-eco_addon
[networkupstools.git] / server / conf.c
blob3fa8393258c5212ef505a0356eb35500801eabd8
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
20 #include "upsd.h"
21 #include "conf.h"
22 #include "upsconf.h"
23 #include "sstate.h"
24 #include "user.h"
25 #include "netssl.h"
26 #include "nut_stdint.h"
27 #include <ctype.h>
29 static ups_t *upstable = NULL;
30 int num_ups = 0;
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)
47 upstype_t *temp;
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);
52 return;
56 /* grab some memory and add the info */
57 temp = xcalloc(1, sizeof(*temp));
58 temp->fn = xstrdup(fn);
59 temp->name = xstrdup(name);
61 if (desc) {
62 temp->desc = xstrdup(desc);
65 temp->stale = 1;
66 temp->retain = 1;
67 #ifdef WIN32
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 */
73 NULL /* no name */);
74 if(temp->read_overlapped.hEvent == NULL ) {
75 upslogx(LOG_ERR, "Can't create event for UPS [%s]",
76 name);
77 return;
79 #endif
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;
86 firstups = temp;
87 num_ups++;
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)
93 upstype_t *temp;
95 temp = get_ups_ptr(name);
97 if (!temp) {
98 upslogx(LOG_ERR, "UPS %s disappeared during reload", name);
99 return;
102 /* paranoia */
103 if (!temp->fn) {
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);
120 #ifndef WIN32
121 close(temp->sock_fd);
122 #else
123 CloseHandle(temp->sock_fd);
124 #endif
125 temp->sock_fd = ERROR_FD;
126 temp->dumpdone = 0;
128 /* now redefine the filename and wrap up */
129 free(temp->fn);
130 temp->fn = xstrdup(fn);
133 /* update the description */
135 free(temp->desc);
137 if (desc)
138 temp->desc = xstrdup(desc);
139 else
140 temp->desc = NULL;
142 /* always set this on reload */
143 temp->retain = 1;
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"))) {
152 *result = 1;
153 return 1;
155 if ( (!strcasecmp(arg, "false")) || (!strcasecmp(arg, "off")) || (!strcasecmp(arg, "no")) || (!strcasecmp(arg, "0"))) {
156 *result = 0;
157 return 1;
159 return 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] */
166 if (numargs < 2)
167 return 0;
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;
175 } else {
176 upslogx(LOG_INFO, "DEBUG_MIN has non numeric or negative value in upsd.conf");
178 return 1;
181 /* MAXAGE <seconds> */
182 if (!strcmp(arg[0], "MAXAGE")) {
183 if (isdigit((size_t)arg[1][0])) {
184 maxage = atoi(arg[1]);
185 return 1;
187 else {
188 upslogx(LOG_ERR, "MAXAGE has non numeric value (%s)!", arg[1]);
189 return 0;
193 /* TRACKINGDELAY <seconds> */
194 if (!strcmp(arg[0], "TRACKINGDELAY")) {
195 if (isdigit((size_t)arg[1][0])) {
196 tracking_delay = atoi(arg[1]);
197 return 1;
199 else {
200 upslogx(LOG_ERR, "TRACKINGDELAY has non numeric value (%s)!", arg[1]);
201 return 0;
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 */
209 return 1;
211 if (parse_boolean(arg[1], &allow_no_device))
212 return 1;
214 upslogx(LOG_ERR, "ALLOW_NO_DEVICE has non numeric and non boolean value (%s)!", arg[1]);
215 return 0;
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 */
222 return 1;
224 if (parse_boolean(arg[1], &allow_not_all_listeners))
225 return 1;
227 upslogx(LOG_ERR, "ALLOW_NOT_ALL_LISTENERS has non numeric and non boolean value (%s)!", arg[1]);
228 return 0;
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]);
236 return 1;
238 else {
239 upslogx(LOG_ERR, "MAXCONN has non numeric value (%s)!", arg[1]);
240 return 0;
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 */
249 upslogx(LOG_WARNING,
250 "Ignoring STATEPATH='%s' from configuration file, "
251 "in favor of NUT_STATEPATH='%s' environment variable",
252 NUT_STRARG(arg[1]), NUT_STRARG(sp));
254 free(statepath);
255 statepath = xstrdup(sp ? sp : arg[1]);
256 return 1;
259 /* DATAPATH <dir> */
260 if (!strcmp(arg[0], "DATAPATH")) {
261 free(datapath);
262 datapath = xstrdup(arg[1]);
263 return 1;
266 #ifdef WITH_OPENSSL
267 /* CERTFILE <dir> */
268 if (!strcmp(arg[0], "CERTFILE")) {
269 free(certfile);
270 certfile = xstrdup(arg[1]);
271 return 1;
273 #elif (defined WITH_NSS) /* WITH_OPENSSL */
274 /* CERTPATH <dir> */
275 if (!strcmp(arg[0], "CERTPATH")) {
276 free(certfile);
277 certfile = xstrdup(arg[1]);
278 return 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]);
285 return 1;
287 else {
288 upslogx(LOG_ERR, "CERTREQUEST has non numeric value (%s)!", arg[1]);
289 return 0;
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))
299 return 1;
301 upslogx(LOG_ERR, "DISABLE_WEAK_SSL has non boolean value (%s)!", arg[1]);
302 return 0;
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");
309 return 1;
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");
315 return 1;
318 /* LISTEN <address> [<port>] */
319 if (!strcmp(arg[0], "LISTEN")) {
320 if (numargs < 3)
321 listen_add(arg[1], string_const(PORT));
322 else
323 listen_add(arg[1], arg[2]);
324 return 1;
327 /* everything below here uses up through arg[2] */
328 if (numargs < 3)
329 return 0;
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");
334 return 1;
337 #ifdef WITH_NSS
338 /* CERTIDENT <name> <passwd> */
339 if (!strcmp(arg[0], "CERTIDENT")) {
340 free(certname);
341 certname = xstrdup(arg[1]);
342 free(certpasswd);
343 certpasswd = xstrdup(arg[2]);
344 return 1;
346 #endif /* WITH_NSS */
348 /* not recognized */
349 return 0;
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)
360 char fn[SMALLBUF];
361 PCONF_CTX_t ctx;
362 int numerrors = 0;
364 snprintf(fn, sizeof(fn), "%s/upsd.conf", confpath());
366 check_perms(fn);
368 pconf_init(&ctx, upsd_conf_err);
370 if (!pconf_file_begin(&ctx, fn)) {
371 pconf_finish(&ctx);
373 if (!reloading)
374 fatalx(EXIT_FAILURE, "%s", ctx.errmsg);
376 upslogx(LOG_ERR, "Reload failed: %s", ctx.errmsg);
377 return;
380 if (reloading) {
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);
391 numerrors++;
392 continue;
395 if (ctx.numargs < 1)
396 continue;
398 if (!parse_upsd_conf_args(ctx.numargs, ctx.arglist)) {
399 unsigned int i;
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",
407 ctx.arglist[i]);
409 numerrors++;
410 upslogx(LOG_WARNING, "%s", errmsg);
415 if (reloading) {
416 if (nut_debug_level_global > -1) {
417 upslogx(LOG_INFO,
418 "Applying DEBUG_MIN %d from upsd.conf",
419 nut_debug_level_global);
420 nut_debug_level = nut_debug_level_global;
421 } else {
422 /* DEBUG_MIN is absent or commented-away in ups.conf */
423 upslogx(LOG_INFO,
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?
434 if (numerrors) {
435 upslogx(LOG_ERR, "Encountered %d config errors, those entries were ignored", numerrors);
438 pconf_finish(&ctx);
441 /* callback during parsing of ups.conf */
442 void do_upsconf_args(char *upsname, char *var, char *val)
444 ups_t *temp;
446 /* no "global" stuff for us */
447 if (!upsname) {
448 return;
451 /* check if UPS is already listed */
452 for (temp = upstable; temp != NULL; temp = temp->next) {
453 if (!strcmp(temp->upsname, upsname)) {
454 break;
458 /* if not listed, create a new entry and prepend it to the list */
459 if (temp == NULL) {
460 temp = xcalloc(1, sizeof(*temp));
461 temp->upsname = xstrdup(upsname);
462 temp->next = upstable;
463 upstable = temp;
466 if (!strcmp(var, "driver")) {
467 free(temp->driver);
468 temp->driver = xstrdup(val);
469 } else if (!strcmp(var, "port")) {
470 free(temp->port);
471 temp->port = xstrdup(val);
472 } else if (!strcmp(var, "desc")) {
473 free(temp->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];
484 if (!tmp) {
485 upslogx(LOG_WARNING, "Warning: no UPS definitions in ups.conf");
486 return;
489 while (tmp) {
491 /* save for later, since we delete as we go along */
492 next = tmp->next;
494 /* this should always be set, but better safe than sorry */
495 if (!tmp->upsname) {
496 tmp = tmp->next;
497 continue;
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",
503 tmp->upsname);
504 } else {
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);
511 else
512 ups_create(statefn, tmp->upsname, tmp->desc);
515 /* free tmp's resources */
517 free(tmp->driver);
518 free(tmp->port);
519 free(tmp->desc);
520 free(tmp->upsname);
521 free(tmp);
523 tmp = next;
526 /* upstable should be completely gone by this point */
527 upstable = NULL;
530 /* remove a UPS from the linked list */
531 static void delete_ups(upstype_t *target)
533 upstype_t *ptr, *last;
535 if (!target)
536 return;
538 ptr = last = firstups;
540 while (ptr) {
541 if (ptr == target) {
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? */
548 if (ptr == last)
549 firstups = ptr->next;
550 else
551 last->next = ptr->next;
553 if (VALID_FD(ptr->sock_fd))
554 #ifndef WIN32
555 close(ptr->sock_fd);
556 #else
557 CloseHandle(ptr->sock_fd);
558 #endif
560 /* release memory */
561 sstate_infofree(ptr);
562 sstate_cmdfree(ptr);
563 pconf_finish(&ptr->sock_ctx);
565 free(ptr->fn);
566 free(ptr->name);
567 free(ptr->desc);
568 free(ptr);
570 return;
573 last = ptr;
574 ptr = ptr->next;
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];
585 FILE *f;
587 snprintf(chkfn, sizeof(chkfn), "%s/%s", confpath(), fn);
589 f = fopen(chkfn, "r");
591 if (!f) {
592 upslog_with_errno(LOG_ERR, "Reload failed: can't open %s", chkfn);
593 return 0; /* failed */
596 fclose(f);
597 return 1; /* OK */
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"))
609 return;
611 /* reset retain flags on all known UPS entries */
612 upstmp = firstups;
613 while (upstmp) {
614 upstmp->retain = 0;
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 */
627 upstmp = firstups;
629 while (upstmp) {
630 /* upstmp may be deleted during this pass */
631 upsnext = upstmp->next;
633 if (upstmp->retain == 0)
634 delete_ups(upstmp);
636 upstmp = upsnext;
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"))
645 return;
647 /* delete all users */
648 user_flush();
650 /* and finally reread from upsd.users */
651 user_load();