1 /* upsmon - monitor power status over the 'net (talks to upsd via TCP)
2
3 Copyright (C)
4 1998 Russell Kroll <rkroll@exploits.org>
5 2012 Arnaud Quette <arnaud.quette.free.fr>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include "common.h"
23
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <sys/socket.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29
30 #include "nut_stdint.h"
31 #include "upsclient.h"
32 #include "upsmon.h"
33 #include "parseconf.h"
34 #include "timehead.h"
35
36 #ifdef HAVE_STDARG_H
37 #include <stdarg.h>
38 #endif
39
40 static char *shutdowncmd = NULL, *notifycmd = NULL;
41 static char *powerdownflag = NULL, *configfile = NULL;
42
43 static unsigned int minsupplies = 1, sleepval = 5;
44
45 /* sum of all power values from config file */
46 static unsigned int totalpv = 0;
47
48 /* default TTL of a device gone AWOL, 3 x polling interval = 15 sec */
49 static int deadtime = 15;
50
51 /* default polling interval = 5 sec */
52 static unsigned int pollfreq = 5, pollfreqalert = 5;
53
54 /* secondary hosts are given 15 sec by default to logout from upsd */
55 static int hostsync = 15;
56
57 /* default replace battery warning interval (seconds) */
58 static int rbwarntime = 43200;
59
60 /* default "all communications down" warning interval (seconds) */
61 static int nocommwarntime = 300;
62
63 /* default interval between the shutdown warning and the shutdown */
64 static unsigned int finaldelay = 5;
65
66 /* set by SIGHUP handler, cleared after reload finishes */
67 static int reload_flag = 0;
68
69 /* set after SIGINT, SIGQUIT, or SIGTERM */
70 static int exit_flag = 0;
71
72 /* userid for unprivileged process when using fork mode */
73 static char *run_as_user = NULL;
74
75 /* SSL details - where to find certs, whether to use them */
76 static char *certpath = NULL;
77 static char *certname = NULL;
78 static char *certpasswd = NULL;
79 static int certverify = 0; /* don't verify by default */
80 static int forcessl = 0; /* don't require ssl by default */
81
82 static int userfsd = 0, use_pipe = 1, pipefd[2];
83
84 static utype_t *firstups = NULL;
85
86 static int opt_af = AF_UNSPEC;
87
88 /* signal handling things */
89 static struct sigaction sa;
90 static sigset_t nut_upsmon_sigmask;
91
setflag(int * val,int flag)92 static void setflag(int *val, int flag)
93 {
94 *val |= flag;
95 }
96
clearflag(int * val,int flag)97 static void clearflag(int *val, int flag)
98 {
99 *val ^= (*val & flag);
100 }
101
flag_isset(int num,int flag)102 static int flag_isset(int num, int flag)
103 {
104 return ((num & flag) == flag);
105 }
106
wall(const char * text)107 static void wall(const char *text)
108 {
109 FILE *wf;
110
111 wf = popen("wall", "w");
112
113 if (!wf) {
114 upslog_with_errno(LOG_NOTICE, "Can't invoke wall");
115 return;
116 }
117
118 fprintf(wf, "%s\n", text);
119 pclose(wf);
120 }
121
notify(const char * notice,int flags,const char * ntype,const char * upsname)122 static void notify(const char *notice, int flags, const char *ntype,
123 const char *upsname)
124 {
125 char exec[LARGEBUF];
126 int ret;
127
128 if (flag_isset(flags, NOTIFY_IGNORE))
129 return;
130
131 if (flag_isset(flags, NOTIFY_SYSLOG))
132 upslogx(LOG_NOTICE, "%s", notice);
133
134 /* fork here so upsmon doesn't get wedged if the notifier is slow */
135 ret = fork();
136
137 if (ret < 0) {
138 upslog_with_errno(LOG_ERR, "Can't fork to notify");
139 return;
140 }
141
142 if (ret != 0) /* parent */
143 return;
144
145 /* child continues and does all the work */
146
147 if (flag_isset(flags, NOTIFY_WALL))
148 wall(notice);
149
150 if (flag_isset(flags, NOTIFY_EXEC)) {
151 if (notifycmd != NULL) {
152 snprintf(exec, sizeof(exec), "%s \"%s\"", notifycmd, notice);
153
154 if (upsname)
155 setenv("UPSNAME", upsname, 1);
156 else
157 setenv("UPSNAME", "", 1);
158
159 setenv("NOTIFYTYPE", ntype, 1);
160 if (system(exec) == -1) {
161 upslog_with_errno(LOG_ERR, "%s", __func__);
162 }
163 }
164 }
165
166 exit(EXIT_SUCCESS);
167 }
168
do_notify(const utype_t * ups,int ntype)169 static void do_notify(const utype_t *ups, int ntype)
170 {
171 int i;
172 char msg[SMALLBUF], *upsname = NULL;
173
174 /* grab this for later */
175 if (ups)
176 upsname = ups->sys;
177
178 for (i = 0; notifylist[i].name != NULL; i++) {
179 if (notifylist[i].type == ntype) {
180 upsdebugx(2, "%s: ntype 0x%04x (%s)", __func__, ntype,
181 notifylist[i].name);
182 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
183 #pragma GCC diagnostic push
184 #endif
185 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
186 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
187 #endif
188 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
189 #pragma GCC diagnostic ignored "-Wformat-security"
190 #endif
191 snprintf(msg, sizeof(msg),
192 notifylist[i].msg ? notifylist[i].msg : notifylist[i].stockmsg,
193 ups ? ups->sys : "");
194 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
195 #pragma GCC diagnostic pop
196 #endif
197 notify(msg, notifylist[i].flags, notifylist[i].name,
198 upsname);
199 return;
200 }
201 }
202
203 /* not found ?! */
204 }
205
206 /* check if we need "primary" mode (managerial permissions)
207 * on the server for this ups, and apply for them then.
208 * Returns 0 in case of error, 1 otherwise (including when
209 * we do not need to try becoming a primary). This currently
210 * propagates further as the return value of do_upsd_auth().
211 */
apply_for_primary(utype_t * ups)212 static int apply_for_primary(utype_t *ups)
213 {
214 char buf[SMALLBUF];
215
216 /* don't bother if we're not configured as a primary for this ups */
217 if (!flag_isset(ups->status, ST_PRIMARY))
218 return 1;
219
220 /* this shouldn't happen (LOGIN checks it earlier) */
221 if ((ups->upsname == NULL) || (strlen(ups->upsname) == 0)) {
222 upslogx(LOG_ERR, "Set primary managerial mode on UPS [%s] failed: empty upsname",
223 ups->sys);
224 return 0;
225 }
226
227 /* TODO: Use PRIMARY first but if talking to older server, retry with MASTER */
228 snprintf(buf, sizeof(buf), "MASTER %s\n", ups->upsname);
229
230 if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) {
231 upslogx(LOG_ALERT, "Can't set primary managerial mode on UPS [%s] - %s",
232 ups->sys, upscli_strerror(&ups->conn));
233 return 0;
234 }
235
236 if (upscli_readline(&ups->conn, buf, sizeof(buf)) == 0) {
237 if (!strncmp(buf, "OK", 2))
238 return 1;
239
240 /* not ERR, but not caught by readline either? */
241
242 upslogx(LOG_ALERT, "Primary managerial privileges unavailable on UPS [%s]",
243 ups->sys);
244 upslogx(LOG_ALERT, "Response: [%s]", buf);
245 }
246 else { /* something caught by readraw's parsing call */
247 upslogx(LOG_ALERT, "Primary managerial privileges unavailable on UPS [%s]",
248 ups->sys);
249 upslogx(LOG_ALERT, "Reason: %s", upscli_strerror(&ups->conn));
250 }
251
252 return 0;
253 }
254
255 /* authenticate to upsd, plus do LOGIN and MASTER if applicable */
256 /* TODO: API change pending to replace deprecated MASTER with PRIMARY
257 * and SLAVE with SECONDARY (and backwards-compatible alias handling)
258 */
do_upsd_auth(utype_t * ups)259 static int do_upsd_auth(utype_t *ups)
260 {
261 char buf[SMALLBUF];
262
263 if (!ups->un) {
264 upslogx(LOG_ERR, "UPS [%s]: no username defined!", ups->sys);
265 return 0;
266 }
267
268 snprintf(buf, sizeof(buf), "USERNAME %s\n", ups->un);
269 if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) {
270 upslogx(LOG_ERR, "Can't set username on [%s]: %s",
271 ups->sys, upscli_strerror(&ups->conn));
272 return 0;
273 }
274
275 if (upscli_readline(&ups->conn, buf, sizeof(buf)) < 0) {
276 upslogx(LOG_ERR, "Set username on [%s] failed: %s",
277 ups->sys, upscli_strerror(&ups->conn));
278 return 0;
279 }
280
281 /* authenticate first */
282 snprintf(buf, sizeof(buf), "PASSWORD %s\n", ups->pw);
283
284 if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) {
285 upslogx(LOG_ERR, "Can't set password on [%s]: %s",
286 ups->sys, upscli_strerror(&ups->conn));
287 return 0;
288 }
289
290 if (upscli_readline(&ups->conn, buf, sizeof(buf)) < 0) {
291 upslogx(LOG_ERR, "Set password on [%s] failed: %s",
292 ups->sys, upscli_strerror(&ups->conn));
293 return 0;
294 }
295
296 /* catch insanity from the server - not ERR and not OK either */
297 if (strncmp(buf, "OK", 2) != 0) {
298 upslogx(LOG_ERR, "Set password on [%s] failed - got [%s]",
299 ups->sys, buf);
300 return 0;
301 }
302
303 /* we require a upsname now */
304 if ((ups->upsname == NULL) || (strlen(ups->upsname) == 0)) {
305 upslogx(LOG_ERR, "Login to UPS [%s] failed: empty upsname",
306 ups->sys);
307 return 0;
308 }
309
310 /* password is set, let's login */
311 snprintf(buf, sizeof(buf), "LOGIN %s\n", ups->upsname);
312
313 if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) {
314 upslogx(LOG_ERR, "Login to UPS [%s] failed: %s",
315 ups->sys, upscli_strerror(&ups->conn));
316 return 0;
317 }
318
319 if (upscli_readline(&ups->conn, buf, sizeof(buf)) < 0) {
320 upslogx(LOG_ERR, "Can't login to UPS [%s]: %s",
321 ups->sys, upscli_strerror(&ups->conn));
322 return 0;
323 }
324
325 /* catch insanity from the server - not ERR and not OK either */
326 if (strncmp(buf, "OK", 2) != 0) {
327 upslogx(LOG_ERR, "Login on UPS [%s] failed - got [%s]",
328 ups->sys, buf);
329 return 0;
330 }
331
332 /* finally - everything is OK */
333 upsdebugx(1, "Logged into UPS %s", ups->sys);
334 setflag(&ups->status, ST_LOGIN);
335
336 /* now see if we also need to test primary managerial-mode permissions */
337 return apply_for_primary(ups);
338 }
339
340 /* set flags and make announcements when a UPS has been checked successfully */
ups_is_alive(utype_t * ups)341 static void ups_is_alive(utype_t *ups)
342 {
343 time_t now;
344
345 time(&now);
346 ups->lastpoll = now;
347
348 if (ups->commstate == 1) /* already known */
349 return;
350
351 /* only notify for 0->1 transitions (to ignore the first connect) */
352 if (ups->commstate == 0)
353 do_notify(ups, NOTIFY_COMMOK);
354
355 ups->commstate = 1;
356 }
357
358 /* handle all the notifications for a missing UPS in one place */
ups_is_gone(utype_t * ups)359 static void ups_is_gone(utype_t *ups)
360 {
361 time_t now;
362
363 /* first time: clear the flag and throw the first notifier */
364 if (ups->commstate != 0) {
365 ups->commstate = 0;
366
367 /* COMMBAD is the initial loss of communications */
368 do_notify(ups, NOTIFY_COMMBAD);
369 return;
370 }
371
372 time(&now);
373
374 /* first only act if we're <nocommtime> seconds past the last poll */
375 if ((now - ups->lastpoll) < nocommwarntime)
376 return;
377
378 /* now only complain if we haven't lately */
379 if ((now - ups->lastncwarn) > nocommwarntime) {
380
381 /* NOCOMM indicates a persistent condition */
382 do_notify(ups, NOTIFY_NOCOMM);
383 ups->lastncwarn = now;
384 }
385 }
386
ups_on_batt(utype_t * ups)387 static void ups_on_batt(utype_t *ups)
388 {
389 if (flag_isset(ups->status, ST_ONBATT)) { /* no change */
390 upsdebugx(4, "%s: %s (no change)", __func__, ups->sys);
391 return;
392 }
393
394 sleepval = pollfreqalert; /* bump up polling frequency */
395
396 ups->linestate = 0;
397
398 upsdebugx(3, "%s: %s (first time)", __func__, ups->sys);
399
400 /* must have changed from OL to OB, so notify */
401
402 do_notify(ups, NOTIFY_ONBATT);
403 setflag(&ups->status, ST_ONBATT);
404 clearflag(&ups->status, ST_ONLINE);
405 }
406
ups_on_line(utype_t * ups)407 static void ups_on_line(utype_t *ups)
408 {
409 if (flag_isset(ups->status, ST_ONLINE)) { /* no change */
410 upsdebugx(4, "%s: %s (no change)", __func__, ups->sys);
411 return;
412 }
413
414 sleepval = pollfreq;
415
416 upsdebugx(3, "%s: %s (first time)", __func__, ups->sys);
417
418 /* ignore the first OL at startup, otherwise send the notifier */
419 if (ups->linestate != -1)
420 do_notify(ups, NOTIFY_ONLINE);
421
422 ups->linestate = 1;
423
424 setflag(&ups->status, ST_ONLINE);
425 clearflag(&ups->status, ST_ONBATT);
426 }
427
428 /* create the flag file if necessary */
set_pdflag(void)429 static void set_pdflag(void)
430 {
431 FILE *pdf;
432
433 if (!powerdownflag)
434 return;
435
436 pdf = fopen(powerdownflag, "w");
437 if (!pdf) {
438 upslogx(LOG_ERR, "Failed to create power down flag!");
439 return;
440 }
441
442 fprintf(pdf, "%s", SDMAGIC);
443 fclose(pdf);
444 }
445
446 /* the actual shutdown procedure */
447 static void doshutdown(void)
448 __attribute__((noreturn));
449
doshutdown(void)450 static void doshutdown(void)
451 {
452 /* this should probably go away at some point */
453 upslogx(LOG_CRIT, "Executing automatic power-fail shutdown");
454 wall("Executing automatic power-fail shutdown\n");
455
456 do_notify(NULL, NOTIFY_SHUTDOWN);
457
458 sleep(finaldelay);
459
460 /* in the pipe model, we let the parent do this for us */
461 if (use_pipe) {
462 char ch;
463 ssize_t wret;
464
465 ch = 1;
466 wret = write(pipefd[1], &ch, 1);
467
468 if (wret < 1)
469 upslogx(LOG_ERR, "Unable to call parent pipe for shutdown");
470 } else {
471 /* one process model = we do all the work here */
472 int sret;
473
474 if (geteuid() != 0)
475 upslogx(LOG_WARNING, "Not root, shutdown may fail");
476
477 set_pdflag();
478
479 sret = system(shutdowncmd);
480
481 if (sret != 0)
482 upslogx(LOG_ERR, "Unable to call shutdown command: %s",
483 shutdowncmd);
484 }
485
486 exit(EXIT_SUCCESS);
487 }
488
489 /* set forced shutdown flag so other upsmons know what's going on here */
setfsd(utype_t * ups)490 static void setfsd(utype_t *ups)
491 {
492 char buf[SMALLBUF];
493 ssize_t ret;
494
495 /* this shouldn't happen */
496 if (!ups->upsname) {
497 upslogx(LOG_ERR, "setfsd: programming error: no UPS name set [%s]",
498 ups->sys);
499 return;
500 }
501
502 upsdebugx(2, "Setting FSD on UPS %s", ups->sys);
503
504 snprintf(buf, sizeof(buf), "FSD %s\n", ups->upsname);
505
506 ret = upscli_sendline(&ups->conn, buf, strlen(buf));
507
508 if (ret < 0) {
509 upslogx(LOG_ERR, "FSD set on UPS %s failed: %s", ups->sys,
510 upscli_strerror(&ups->conn));
511 return;
512 }
513
514 ret = upscli_readline(&ups->conn, buf, sizeof(buf));
515
516 if (ret < 0) {
517 upslogx(LOG_ERR, "FSD set on UPS %s failed: %s", ups->sys,
518 upscli_strerror(&ups->conn));
519 return;
520 }
521
522 if (!strncmp(buf, "OK", 2))
523 return;
524
525 /* protocol error: upsd said something other than "OK" */
526 upslogx(LOG_ERR, "FSD set on UPS %s failed: %s", ups->sys, buf);
527 }
528
set_alarm(void)529 static void set_alarm(void)
530 {
531 alarm(NET_TIMEOUT);
532 }
533
clear_alarm(void)534 static void clear_alarm(void)
535 {
536 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES)
537 # pragma GCC diagnostic push
538 # pragma GCC diagnostic ignored "-Wstrict-prototypes"
539 #endif
540 signal(SIGALRM, SIG_IGN);
541 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES)
542 # pragma GCC diagnostic pop
543 #endif
544 alarm(0);
545 }
546
get_var(utype_t * ups,const char * var,char * buf,size_t bufsize)547 static int get_var(utype_t *ups, const char *var, char *buf, size_t bufsize)
548 {
549 int ret;
550 size_t numq, numa;
551 const char *query[4];
552 char **answer;
553
554 /* this shouldn't happen */
555 if (!ups->upsname) {
556 upslogx(LOG_ERR, "get_var: programming error: no UPS name set [%s]",
557 ups->sys);
558 return -1;
559 }
560
561 numq = 0;
562
563 if (!strcmp(var, "numlogins")) {
564 query[0] = "NUMLOGINS";
565 query[1] = ups->upsname;
566 numq = 2;
567 }
568
569 if (!strcmp(var, "status")) {
570 query[0] = "VAR";
571 query[1] = ups->upsname;
572 query[2] = "ups.status";
573 numq = 3;
574 }
575
576 if (numq == 0) {
577 upslogx(LOG_ERR, "get_var: programming error: var=%s", var);
578 return -1;
579 }
580
581 upsdebugx(3, "%s: %s / %s", __func__, ups->sys, var);
582
583 ret = upscli_get(&ups->conn, numq, query, &numa, &answer);
584
585 if (ret < 0) {
586
587 /* detect old upsd */
588 if (upscli_upserror(&ups->conn) == UPSCLI_ERR_UNKCOMMAND) {
589
590 upslogx(LOG_ERR, "UPS [%s]: Too old to monitor",
591 ups->sys);
592 return -1;
593 }
594
595 /* some other error */
596 return -1;
597 }
598
599 if (numa < numq) {
600 upslogx(LOG_ERR, "%s: Error: insufficient data "
601 "(got %zu args, need at least %zu)",
602 var, numa, numq);
603 return -1;
604 }
605
606 snprintf(buf, bufsize, "%s", answer[numq]);
607 return 0;
608 }
609
610 /* Called by upsmon which is the primary on some UPS(es) to wait
611 * until all secondaries log out from it on the shared upsd server
612 * or the HOSTSYNC timeout expires
613 */
sync_secondaries(void)614 static void sync_secondaries(void)
615 {
616 utype_t *ups;
617 char temp[SMALLBUF];
618 time_t start, now;
619 long maxlogins, logins;
620
621 time(&start);
622
623 for (;;) {
624 maxlogins = 0;
625
626 for (ups = firstups; ups != NULL; ups = ups->next) {
627
628 /* only check login count on devices we are the primary for */
629 if (!flag_isset(ups->status, ST_PRIMARY))
630 continue;
631
632 set_alarm();
633
634 if (get_var(ups, "numlogins", temp, sizeof(temp)) >= 0) {
635 logins = strtol(temp, (char **)NULL, 10);
636
637 if (logins > maxlogins)
638 maxlogins = logins;
639 }
640
641 clear_alarm();
642 }
643
644 /* if no UPS has more than 1 login (that would be us),
645 * then secondaries are all gone */
646 /* TO THINK: how about redundant setups with several primary-mode
647 * clients managing an UPS, or possibly differend UPSes, with the
648 * same upsd? */
649 if (maxlogins <= 1)
650 return;
651
652 /* after HOSTSYNC seconds, assume secondaries are stuck - and bail */
653 time(&now);
654
655 if ((now - start) > hostsync) {
656 upslogx(LOG_INFO, "Host sync timer expired, forcing shutdown");
657 return;
658 }
659
660 usleep(250000);
661 }
662 }
663
664 static void forceshutdown(void)
665 __attribute__((noreturn));
666
forceshutdown(void)667 static void forceshutdown(void)
668 {
669 utype_t *ups;
670 int isaprimary = 0;
671
672 upsdebugx(1, "Shutting down any UPSes in PRIMARY mode...");
673
674 /* set FSD on any "primary" UPS entries (forced shutdown in progress) */
675 for (ups = firstups; ups != NULL; ups = ups->next)
676 if (flag_isset(ups->status, ST_PRIMARY)) {
677 isaprimary = 1;
678 setfsd(ups);
679 }
680
681 /* if we're not a primary on anything, we should shut down now */
682 if (!isaprimary)
683 doshutdown();
684
685 /* we must be the primary now */
686 upsdebugx(1, "This system is a primary... waiting for secondaries to logout...");
687
688 /* wait up to HOSTSYNC seconds for secondaries to logout */
689 sync_secondaries();
690
691 /* time expired or all the secondaries are gone, so shutdown */
692 doshutdown();
693 }
694
is_ups_critical(utype_t * ups)695 static int is_ups_critical(utype_t *ups)
696 {
697 time_t now;
698
699 /* FSD = the primary is forcing a shutdown, or a driver forwarded the flag
700 * from a smarter UPS depending on vendor protocol, ability and settings
701 * (e.g. is charging but battery too low to guarantee safety to the load)
702 */
703 if (flag_isset(ups->status, ST_FSD))
704 return 1;
705
706 /* not OB or not LB = not critical yet */
707 if ((!flag_isset(ups->status, ST_ONBATT)) ||
708 (!flag_isset(ups->status, ST_LOWBATT)))
709 return 0;
710
711 /* must be OB+LB now */
712
713 /* if UPS is calibrating, don't declare it critical */
714 /* FIXME: Consider UPSes where we can know if they have other power
715 * circuits (bypass, etc.) and whether those do currently provide
716 * wall power to the host - and that we do not have both calibration
717 * and a real outage, when we still should shut down right now.
718 */
719 if (flag_isset(ups->status, ST_CAL)) {
720 upslogx(LOG_WARNING, "%s: seems that UPS [%s] is OB+LB now, but "
721 "it is also calibrating - not declaring a critical state",
722 __func__, ups->upsname);
723 return 0;
724 }
725
726 /* if we're a primary, declare it critical so we set FSD on it */
727 if (flag_isset(ups->status, ST_PRIMARY))
728 return 1;
729
730 /* must be a secondary now */
731
732 /* FSD isn't set, so the primary hasn't seen it yet */
733
734 time(&now);
735
736 /* give the primary up to HOSTSYNC seconds before shutting down */
737 if ((now - ups->lastnoncrit) > hostsync) {
738 upslogx(LOG_WARNING, "Giving up on the primary for UPS [%s]",
739 ups->sys);
740 return 1;
741 }
742
743 /* there's still time left, maybe OB+LB will go away next time we look? */
744 return 0;
745 }
746
747 /* recalculate the online power value and see if things are still OK */
recalc(void)748 static void recalc(void)
749 {
750 utype_t *ups;
751 unsigned int val_ol = 0;
752 time_t now;
753
754 time(&now);
755 ups = firstups;
756 while (ups != NULL) {
757 /* promote dead UPSes that were last known OB to OB+LB */
758 if ((now - ups->lastpoll) > deadtime)
759 if (flag_isset(ups->status, ST_ONBATT)) {
760 upsdebugx(1, "Promoting dead UPS: %s", ups->sys);
761 setflag(&ups->status, ST_LOWBATT);
762 }
763
764 /* note: we assume that a UPS that isn't critical must be OK *
765 * *
766 * this means a UPS we've never heard from is assumed OL *
767 * whether this is really the best thing to do is undecided */
768
769 /* crit = (FSD) || (OB & LB) > HOSTSYNC seconds */
770 if (is_ups_critical(ups))
771 upsdebugx(1, "Critical UPS: %s", ups->sys);
772 else
773 val_ol += ups->pv;
774
775 ups = ups->next;
776 }
777
778 upsdebugx(3, "Current power value: %u", val_ol);
779 upsdebugx(3, "Minimum power value: %u", minsupplies);
780
781 if (val_ol < minsupplies)
782 forceshutdown();
783 }
784
ups_low_batt(utype_t * ups)785 static void ups_low_batt(utype_t *ups)
786 {
787 if (flag_isset(ups->status, ST_LOWBATT)) { /* no change */
788 upsdebugx(4, "%s: %s (no change)", __func__, ups->sys);
789 return;
790 }
791
792 upsdebugx(3, "%s: %s (first time)", __func__, ups->sys);
793
794 /* must have changed from !LB to LB, so notify */
795
796 do_notify(ups, NOTIFY_LOWBATT);
797 setflag(&ups->status, ST_LOWBATT);
798 }
799
upsreplbatt(utype_t * ups)800 static void upsreplbatt(utype_t *ups)
801 {
802 time_t now;
803
804 time(&now);
805
806 if ((now - ups->lastrbwarn) > rbwarntime) {
807 do_notify(ups, NOTIFY_REPLBATT);
808 ups->lastrbwarn = now;
809 }
810 }
811
ups_cal(utype_t * ups)812 static void ups_cal(utype_t *ups)
813 {
814 if (flag_isset(ups->status, ST_CAL)) { /* no change */
815 upsdebugx(4, "%s: %s (no change)", __func__, ups->sys);
816 return;
817 }
818
819 upsdebugx(3, "%s: %s (first time)", __func__, ups->sys);
820
821 /* must have changed from !CAL to CAL, so notify */
822
823 do_notify(ups, NOTIFY_CAL);
824 setflag(&ups->status, ST_CAL);
825 }
826
ups_fsd(utype_t * ups)827 static void ups_fsd(utype_t *ups)
828 {
829 if (flag_isset(ups->status, ST_FSD)) { /* no change */
830 upsdebugx(4, "%s: %s (no change)", __func__, ups->sys);
831 return;
832 }
833
834 upsdebugx(3, "%s: %s (first time)", __func__, ups->sys);
835
836 /* must have changed from !FSD to FSD, so notify */
837
838 do_notify(ups, NOTIFY_FSD);
839 setflag(&ups->status, ST_FSD);
840 }
841
842 /* cleanly close the connection to a given UPS */
drop_connection(utype_t * ups)843 static void drop_connection(utype_t *ups)
844 {
845 upsdebugx(2, "Dropping connection to UPS [%s]", ups->sys);
846
847 ups->commstate = 0;
848 ups->linestate = 0;
849 clearflag(&ups->status, ST_LOGIN);
850 clearflag(&ups->status, ST_CONNECTED);
851
852 upscli_disconnect(&ups->conn);
853 }
854
855 /* change some UPS parameters during reloading */
redefine_ups(utype_t * ups,unsigned int pv,const char * un,const char * pw,const char * managerialOption)856 static void redefine_ups(utype_t *ups, unsigned int pv, const char *un,
857 const char *pw, const char *managerialOption)
858 {
859 ups->retain = 1;
860
861 if (ups->pv != pv) {
862 upslogx(LOG_INFO, "UPS [%s]: redefined power value to %d",
863 ups->sys, pv);
864 ups->pv = pv;
865 }
866
867 totalpv += ups->pv;
868
869 if (ups->un) {
870 if (strcmp(ups->un, un) != 0) {
871 upslogx(LOG_INFO, "UPS [%s]: redefined username",
872 ups->sys);
873
874 free(ups->un);
875
876 ups->un = xstrdup(un);
877
878 /*
879 * if not logged in force a reconnection since this
880 * may have been redefined to make a login work
881 */
882
883 if (!flag_isset(ups->status, ST_LOGIN)) {
884 upslogx(LOG_INFO, "UPS [%s]: retrying connection",
885 ups->sys);
886
887 drop_connection(ups);
888 }
889
890 } /* if (strcmp(ups->un, un) != 0) { */
891
892 } else {
893
894 /* adding a username? (going to new style MONITOR line) */
895
896 if (un) {
897 upslogx(LOG_INFO, "UPS [%s]: defined username",
898 ups->sys);
899
900 ups->un = xstrdup(un);
901
902 /* possibly force reconnection - see above */
903
904 if (!flag_isset(ups->status, ST_LOGIN)) {
905 upslogx(LOG_INFO, "UPS [%s]: retrying connection",
906 ups->sys);
907
908 drop_connection(ups);
909 }
910
911 } /* if (un) */
912 }
913
914 /* paranoia */
915 if (!ups->pw)
916 ups->pw = xstrdup(""); /* give it a bogus, but non-NULL one */
917
918 /* obviously don't put the new password in the syslog... */
919 if (strcmp(ups->pw, pw) != 0) {
920 upslogx(LOG_INFO, "UPS [%s]: redefined password", ups->sys);
921
922 free(ups->pw);
923 ups->pw = xstrdup(pw);
924
925 /* possibly force reconnection - see above */
926
927 if (!flag_isset(ups->status, ST_LOGIN)) {
928 upslogx(LOG_INFO, "UPS [%s]: retrying connection",
929 ups->sys);
930
931 drop_connection(ups);
932 }
933 }
934
935 /* secondary|slave -> primary|master */
936 if ( ( (!strcasecmp(managerialOption, "primary"))
937 || (!strcasecmp(managerialOption, "master")) )
938 && (!flag_isset(ups->status, ST_PRIMARY)) ) {
939 upslogx(LOG_INFO, "UPS [%s]: redefined as a primary", ups->sys);
940 setflag(&ups->status, ST_PRIMARY);
941
942 /* reset connection to ensure primary mode gets checked */
943 drop_connection(ups);
944 return;
945 }
946
947 /* primary|master -> secondary|slave */
948 if ( ( (!strcasecmp(managerialOption, "secondary"))
949 || (!strcasecmp(managerialOption, "slave")) )
950 && (flag_isset(ups->status, ST_PRIMARY)) ) {
951 upslogx(LOG_INFO, "UPS [%s]: redefined as a secondary", ups->sys);
952 clearflag(&ups->status, ST_PRIMARY);
953 return;
954 }
955 }
956
addups(int reloading,const char * sys,const char * pvs,const char * un,const char * pw,const char * managerialOption)957 static void addups(int reloading, const char *sys, const char *pvs,
958 const char *un, const char *pw, const char *managerialOption)
959 {
960 unsigned int pv;
961 utype_t *tmp, *last;
962
963 /* the username is now required - no more host-based auth */
964
965 if ((!sys) || (!pvs) || (!pw) || (!managerialOption) || (!un)) {
966 upslogx(LOG_WARNING, "Ignoring invalid MONITOR line in %s!", configfile);
967 upslogx(LOG_WARNING, "MONITOR configuration directives require five arguments.");
968 return;
969 }
970
971 long lpv = strtol(pvs, (char **) NULL, 10);
972
973 if (lpv < 0 || (sizeof(long) > sizeof(unsigned int) && lpv > (long)UINT_MAX)) {
974 upslogx(LOG_WARNING, "UPS [%s]: ignoring invalid power value [%s]",
975 sys, pvs);
976 return;
977 }
978 pv = (unsigned int)lpv;
979
980 last = tmp = firstups;
981
982 while (tmp) {
983 last = tmp;
984
985 /* check for duplicates */
986 if (!strcmp(tmp->sys, sys)) {
987 if (reloading)
988 redefine_ups(tmp, pv, un, pw, managerialOption);
989 else
990 upslogx(LOG_WARNING, "Warning: ignoring duplicate"
991 " UPS [%s]", sys);
992 return;
993 }
994
995 tmp = tmp->next;
996 }
997
998 tmp = xmalloc(sizeof(utype_t));
999 tmp->sys = xstrdup(sys);
1000 tmp->pv = pv;
1001
1002 /* build this up so the user doesn't run with bad settings */
1003 totalpv += tmp->pv;
1004
1005 tmp->un = xstrdup(un);
1006
1007 tmp->pw = xstrdup(pw);
1008 tmp->status = 0;
1009 tmp->retain = 1;
1010
1011 /* ignore initial COMMOK and ONLINE by default */
1012 tmp->commstate = -1;
1013 tmp->linestate = -1;
1014
1015 tmp->lastpoll = 0;
1016 tmp->lastnoncrit = 0;
1017 tmp->lastrbwarn = 0;
1018 tmp->lastncwarn = 0;
1019
1020 if ( (!strcasecmp(managerialOption, "primary"))
1021 || (!strcasecmp(managerialOption, "master")) ) {
1022 setflag(&tmp->status, ST_PRIMARY);
1023 }
1024
1025 tmp->next = NULL;
1026
1027 if (last)
1028 last->next = tmp;
1029 else
1030 firstups = tmp;
1031
1032 if (tmp->pv)
1033 upslogx(LOG_INFO, "UPS: %s (%s) (power value %d)", tmp->sys,
1034 flag_isset(tmp->status, ST_PRIMARY) ? "primary" : "secondary",
1035 tmp->pv);
1036 else
1037 upslogx(LOG_INFO, "UPS: %s (monitoring only)", tmp->sys);
1038
1039 tmp->upsname = tmp->hostname = NULL;
1040
1041 if (upscli_splitname(tmp->sys, &tmp->upsname, &tmp->hostname,
1042 &tmp->port) != 0) {
1043 upslogx(LOG_ERR, "Error: unable to split UPS name [%s]",
1044 tmp->sys);
1045 }
1046
1047 if (!tmp->upsname)
1048 upslogx(LOG_WARNING, "Warning: UPS [%s]: no upsname set!",
1049 tmp->sys);
1050 }
1051
set_notifymsg(const char * name,const char * msg)1052 static void set_notifymsg(const char *name, const char *msg)
1053 {
1054 int i;
1055
1056 for (i = 0; notifylist[i].name != NULL; i++) {
1057 if (!strcasecmp(notifylist[i].name, name)) {
1058 free(notifylist[i].msg);
1059 notifylist[i].msg = xstrdup(msg);
1060 return;
1061 }
1062 }
1063
1064 upslogx(LOG_WARNING, "'%s' is not a valid notify event name", name);
1065 }
1066
set_notifyflag(const char * ntype,char * flags)1067 static void set_notifyflag(const char *ntype, char *flags)
1068 {
1069 int i, pos;
1070 char *ptr, *tmp;
1071
1072 /* find ntype */
1073
1074 pos = -1;
1075 for (i = 0; notifylist[i].name != NULL; i++) {
1076 if (!strcasecmp(notifylist[i].name, ntype)) {
1077 pos = i;
1078 break;
1079 }
1080 }
1081
1082 if (pos == -1) {
1083 upslogx(LOG_WARNING, "Warning: invalid notify type [%s]", ntype);
1084 return;
1085 }
1086
1087 ptr = flags;
1088
1089 /* zero existing flags */
1090 notifylist[pos].flags = 0;
1091
1092 while (ptr) {
1093 int newflag;
1094
1095 tmp = strchr(ptr, '+');
1096 if (tmp)
1097 *tmp++ = '\0';
1098
1099 newflag = 0;
1100
1101 if (!strcmp(ptr, "SYSLOG"))
1102 newflag = NOTIFY_SYSLOG;
1103 if (!strcmp(ptr, "WALL"))
1104 newflag = NOTIFY_WALL;
1105 if (!strcmp(ptr, "EXEC"))
1106 newflag = NOTIFY_EXEC;
1107 if (!strcmp(ptr, "IGNORE"))
1108 newflag = NOTIFY_IGNORE;
1109
1110 if (newflag)
1111 notifylist[pos].flags |= newflag;
1112 else
1113 upslogx(LOG_WARNING, "Invalid notify flag: [%s]", ptr);
1114
1115 ptr = tmp;
1116 }
1117 }
1118
1119 /* in split mode, the parent doesn't hear about reloads */
checkmode(char * cfgentry,char * oldvalue,char * newvalue,int reloading)1120 static void checkmode(char *cfgentry, char *oldvalue, char *newvalue,
1121 int reloading)
1122 {
1123 /* nothing to do if in "all as root" mode */
1124 if (use_pipe == 0)
1125 return;
1126
1127 /* it's ok if we're not reloading yet */
1128 if (reloading == 0)
1129 return;
1130
1131 /* also nothing to do if it didn't change */
1132 if ((oldvalue) && (newvalue)) {
1133 if (!strcmp(oldvalue, newvalue))
1134 return;
1135 }
1136
1137 /* otherwise, yell at them */
1138 upslogx(LOG_WARNING, "Warning: %s redefined in split-process mode!",
1139 cfgentry);
1140 upslogx(LOG_WARNING, "You must restart upsmon for this change to work");
1141 }
1142
1143 /* returns 1 if used, 0 if not, so we can complain about bogus configs */
parse_conf_arg(size_t numargs,char ** arg)1144 static int parse_conf_arg(size_t numargs, char **arg)
1145 {
1146 /* using up to arg[1] below */
1147 if (numargs < 2)
1148 return 0;
1149
1150 /* SHUTDOWNCMD <cmd> */
1151 if (!strcmp(arg[0], "SHUTDOWNCMD")) {
1152 checkmode(arg[0], shutdowncmd, arg[1], reload_flag);
1153
1154 free(shutdowncmd);
1155 shutdowncmd = xstrdup(arg[1]);
1156 return 1;
1157 }
1158
1159 /* POWERDOWNFLAG <fn> */
1160 if (!strcmp(arg[0], "POWERDOWNFLAG")) {
1161 checkmode(arg[0], powerdownflag, arg[1], reload_flag);
1162
1163 free(powerdownflag);
1164 powerdownflag = xstrdup(arg[1]);
1165
1166 if (!reload_flag)
1167 upslogx(LOG_INFO, "Using power down flag file %s",
1168 arg[1]);
1169
1170 return 1;
1171 }
1172
1173 /* NOTIFYCMD <cmd> */
1174 if (!strcmp(arg[0], "NOTIFYCMD")) {
1175 free(notifycmd);
1176 notifycmd = xstrdup(arg[1]);
1177 return 1;
1178 }
1179
1180 /* POLLFREQ <num> */
1181 if (!strcmp(arg[0], "POLLFREQ")) {
1182 int ipollfreq = atoi(arg[1]);
1183 if (ipollfreq < 0) {
1184 upsdebugx(0, "Ignoring invalid POLLFREQ value: %d", ipollfreq);
1185 } else {
1186 pollfreq = (unsigned int)ipollfreq;
1187 }
1188 return 1;
1189 }
1190
1191 /* POLLFREQALERT <num> */
1192 if (!strcmp(arg[0], "POLLFREQALERT")) {
1193 int ipollfreqalert = atoi(arg[1]);
1194 if (ipollfreqalert < 0) {
1195 upsdebugx(0, "Ignoring invalid POLLFREQALERT value: %d", ipollfreqalert);
1196 } else {
1197 pollfreqalert = (unsigned int)ipollfreqalert;
1198 }
1199 return 1;
1200 }
1201
1202 /* HOSTSYNC <num> */
1203 if (!strcmp(arg[0], "HOSTSYNC")) {
1204 hostsync = atoi(arg[1]);
1205 return 1;
1206 }
1207
1208 /* DEADTIME <num> */
1209 if (!strcmp(arg[0], "DEADTIME")) {
1210 deadtime = atoi(arg[1]);
1211 return 1;
1212 }
1213
1214 /* MINSUPPLIES <num> */
1215 if (!strcmp(arg[0], "MINSUPPLIES")) {
1216 int iminsupplies = atoi(arg[1]);
1217 if (iminsupplies < 0) {
1218 upsdebugx(0, "Ignoring invalid MINSUPPLIES value: %d", iminsupplies);
1219 } else {
1220 minsupplies = (unsigned int)iminsupplies;
1221 }
1222 return 1;
1223 }
1224
1225 /* RBWARNTIME <num> */
1226 if (!strcmp(arg[0], "RBWARNTIME")) {
1227 rbwarntime = atoi(arg[1]);
1228 return 1;
1229 }
1230
1231 /* NOCOMMWARNTIME <num> */
1232 if (!strcmp(arg[0], "NOCOMMWARNTIME")) {
1233 nocommwarntime = atoi(arg[1]);
1234 return 1;
1235 }
1236
1237 /* FINALDELAY <num> */
1238 if (!strcmp(arg[0], "FINALDELAY")) {
1239 int ifinaldelay = atoi(arg[1]);
1240 if (ifinaldelay < 0) {
1241 upsdebugx(0, "Ignoring invalid FINALDELAY value: %d", ifinaldelay);
1242 } else {
1243 finaldelay = (unsigned int)ifinaldelay;
1244 }
1245 return 1;
1246 }
1247
1248 /* RUN_AS_USER <userid> */
1249 if (!strcmp(arg[0], "RUN_AS_USER")) {
1250 free(run_as_user);
1251 run_as_user = xstrdup(arg[1]);
1252 return 1;
1253 }
1254
1255 /* CERTPATH <path> */
1256 if (!strcmp(arg[0], "CERTPATH")) {
1257 free(certpath);
1258 certpath = xstrdup(arg[1]);
1259 return 1;
1260 }
1261
1262 /* CERTVERIFY (0|1) */
1263 if (!strcmp(arg[0], "CERTVERIFY")) {
1264 certverify = atoi(arg[1]);
1265 return 1;
1266 }
1267
1268 /* FORCESSL (0|1) */
1269 if (!strcmp(arg[0], "FORCESSL")) {
1270 forcessl = atoi(arg[1]);
1271 return 1;
1272 }
1273
1274 /* using up to arg[2] below */
1275 if (numargs < 3)
1276 return 0;
1277
1278 /* NOTIFYMSG <notify type> <replacement message> */
1279 if (!strcmp(arg[0], "NOTIFYMSG")) {
1280 set_notifymsg(arg[1], arg[2]);
1281 return 1;
1282 }
1283
1284 /* NOTIFYFLAG <notify type> <flags> */
1285 if (!strcmp(arg[0], "NOTIFYFLAG")) {
1286 set_notifyflag(arg[1], arg[2]);
1287 return 1;
1288 }
1289
1290 /* CERTIDENT <name> <passwd> */
1291 if (!strcmp(arg[0], "CERTIDENT")) {
1292 free(certname);
1293 certname = xstrdup(arg[1]);
1294 free(certpasswd);
1295 certpasswd = xstrdup(arg[2]);
1296 return 1;
1297 }
1298
1299 /* using up to arg[4] below */
1300 if (numargs < 5)
1301 return 0;
1302
1303 /* CERTHOST <hostname> <certname> (0|1) (0|1) */
1304 if (!strcmp(arg[0], "CERTHOST")) {
1305 upscli_add_host_cert(arg[1], arg[2], atoi(arg[3]), atoi(arg[4]));
1306 return 1;
1307 }
1308
1309 if (!strcmp(arg[0], "MONITOR")) {
1310
1311 /* original style: no username (only 5 args) */
1312 if (numargs == 5) {
1313 upslogx(LOG_ERR, "Unable to use old-style MONITOR line without a username");
1314 upslogx(LOG_ERR, "Convert it and add a username to upsd.users - see the documentation");
1315
1316 fatalx(EXIT_FAILURE, "Fatal error: unusable configuration");
1317 }
1318
1319 /* <sys> <pwrval> <user> <pw> ("primary"|"master" | "secondary"|"slave") */
1320 addups(reload_flag, arg[1], arg[2], arg[3], arg[4], arg[5]);
1321 return 1;
1322 }
1323
1324 /* didn't parse it at all */
1325 return 0;
1326 }
1327
1328 /* called for fatal errors in parseconf like malloc failures */
upsmon_err(const char * errmsg)1329 static void upsmon_err(const char *errmsg)
1330 {
1331 upslogx(LOG_ERR, "Fatal error in parseconf(%s): %s", configfile, errmsg);
1332 }
1333
loadconfig(void)1334 static void loadconfig(void)
1335 {
1336 PCONF_CTX_t ctx;
1337
1338 pconf_init(&ctx, upsmon_err);
1339
1340 if (!pconf_file_begin(&ctx, configfile)) {
1341 pconf_finish(&ctx);
1342
1343 if (reload_flag == 1) {
1344 upslog_with_errno(LOG_ERR, "Reload failed: %s", ctx.errmsg);
1345 return;
1346 }
1347
1348 fatalx(EXIT_FAILURE, "%s", ctx.errmsg);
1349 }
1350
1351 while (pconf_file_next(&ctx)) {
1352 if (pconf_parse_error(&ctx)) {
1353 upslogx(LOG_ERR, "Parse error: %s:%d: %s",
1354 configfile, ctx.linenum, ctx.errmsg);
1355 continue;
1356 }
1357
1358 if (ctx.numargs < 1)
1359 continue;
1360
1361 if (!parse_conf_arg(ctx.numargs, ctx.arglist)) {
1362 unsigned int i;
1363 char errmsg[SMALLBUF];
1364
1365 snprintf(errmsg, sizeof(errmsg),
1366 "%s line %d: invalid directive",
1367 configfile, ctx.linenum);
1368
1369 for (i = 0; i < ctx.numargs; i++)
1370 snprintfcat(errmsg, sizeof(errmsg), " %s",
1371 ctx.arglist[i]);
1372
1373 upslogx(LOG_WARNING, "%s", errmsg);
1374 }
1375 }
1376
1377 pconf_finish(&ctx);
1378 }
1379
1380 /* SIGPIPE handler */
sigpipe(int sig)1381 static void sigpipe(int sig)
1382 {
1383 upsdebugx(1, "SIGPIPE: dazed and confused, but continuing after signal %i...", sig);
1384 }
1385
1386 /* SIGQUIT, SIGTERM handler */
set_exit_flag(int sig)1387 static void set_exit_flag(int sig)
1388 {
1389 exit_flag = sig;
1390 }
1391
ups_free(utype_t * ups)1392 static void ups_free(utype_t *ups)
1393 {
1394 free(ups->sys);
1395 free(ups->upsname);
1396 free(ups->hostname);
1397 free(ups->un);
1398 free(ups->pw);
1399 free(ups);
1400 }
1401
upsmon_cleanup(void)1402 static void upsmon_cleanup(void)
1403 {
1404 int i;
1405 utype_t *utmp, *unext;
1406
1407 /* close all fds */
1408 utmp = firstups;
1409
1410 while (utmp) {
1411 unext = utmp->next;
1412
1413 drop_connection(utmp);
1414 ups_free(utmp);
1415
1416 utmp = unext;
1417 }
1418
1419 free(run_as_user);
1420 free(shutdowncmd);
1421 free(notifycmd);
1422 free(powerdownflag);
1423
1424 for (i = 0; notifylist[i].name != NULL; i++) {
1425 free(notifylist[i].msg);
1426 }
1427
1428 upscli_cleanup();
1429 }
1430
user_fsd(int sig)1431 static void user_fsd(int sig)
1432 {
1433 upslogx(LOG_INFO, "Signal %d: User requested FSD", sig);
1434 userfsd = 1;
1435 }
1436
set_reload_flag(int sig)1437 static void set_reload_flag(int sig)
1438 {
1439 NUT_UNUSED_VARIABLE(sig);
1440
1441 reload_flag = 1;
1442 }
1443
1444 /* handler for alarm when getupsvarfd times out */
read_timeout(int sig)1445 static void read_timeout(int sig)
1446 {
1447 NUT_UNUSED_VARIABLE(sig);
1448
1449 /* don't do anything here, just return */
1450 }
1451
1452 /* install handlers for a few signals */
setup_signals(void)1453 static void setup_signals(void)
1454 {
1455 sigemptyset(&nut_upsmon_sigmask);
1456 sa.sa_mask = nut_upsmon_sigmask;
1457 sa.sa_flags = 0;
1458
1459 sa.sa_handler = sigpipe;
1460 sigaction(SIGPIPE, &sa, NULL);
1461
1462 sa.sa_handler = set_exit_flag;
1463 sigaction(SIGINT, &sa, NULL);
1464 sigaction(SIGQUIT, &sa, NULL);
1465 sigaction(SIGTERM, &sa, NULL);
1466
1467 /* handle timeouts */
1468
1469 sa.sa_handler = read_timeout;
1470 sigaction(SIGALRM, &sa, NULL);
1471
1472 /* deal with the ones from userspace as well */
1473
1474 sa.sa_handler = user_fsd;
1475 sigaction(SIGCMD_FSD, &sa, NULL);
1476
1477 sa.sa_handler = set_reload_flag;
1478 sigaction(SIGCMD_RELOAD, &sa, NULL);
1479 }
1480
1481 /* remember the last time the ups was not critical (OB + LB) */
update_crittimer(utype_t * ups)1482 static void update_crittimer(utype_t *ups)
1483 {
1484 /* if !OB, !LB, or CAL, then it's not critical, so log the time */
1485 if ((!flag_isset(ups->status, ST_ONBATT)) ||
1486 (!flag_isset(ups->status, ST_LOWBATT)) ||
1487 (flag_isset(ups->status, ST_CAL))) {
1488
1489 time(&ups->lastnoncrit);
1490 return;
1491 }
1492
1493 /* fallthrough: let the timer age */
1494 }
1495
1496 /* handle connecting to upsd, plus get SSL going too if possible */
try_connect(utype_t * ups)1497 static int try_connect(utype_t *ups)
1498 {
1499 int flags = 0, ret;
1500
1501 upsdebugx(1, "Trying to connect to UPS [%s]", ups->sys);
1502
1503 clearflag(&ups->status, ST_CONNECTED);
1504
1505 /* force it if configured that way, just try it otherwise */
1506 if (forcessl == 1)
1507 flags |= UPSCLI_CONN_REQSSL;
1508 else
1509 flags |= UPSCLI_CONN_TRYSSL;
1510
1511 if (opt_af == AF_INET)
1512 flags |= UPSCLI_CONN_INET;
1513
1514 if (opt_af == AF_INET6)
1515 flags |= UPSCLI_CONN_INET6;
1516
1517 if (!certpath) {
1518 if (certverify == 1) {
1519 upslogx(LOG_ERR, "Configuration error: "
1520 "CERTVERIFY is set, but CERTPATH isn't");
1521 upslogx(LOG_ERR, "UPS [%s]: Connection impossible, "
1522 "dropping link", ups->sys);
1523
1524 ups_is_gone(ups);
1525 drop_connection(ups);
1526
1527 return 0; /* failed */
1528 }
1529 }
1530
1531 if (certverify == 1) {
1532 flags |= UPSCLI_CONN_CERTVERIF;
1533 }
1534
1535 ret = upscli_connect(&ups->conn, ups->hostname, ups->port, flags);
1536
1537 if (ret < 0) {
1538 upslogx(LOG_ERR, "UPS [%s]: connect failed: %s",
1539 ups->sys, upscli_strerror(&ups->conn));
1540 ups_is_gone(ups);
1541 return 0;
1542 }
1543
1544 /* we're definitely connected now */
1545 setflag(&ups->status, ST_CONNECTED);
1546
1547 /* prevent connection leaking to NOTIFYCMD */
1548 fcntl(upscli_fd(&ups->conn), F_SETFD, FD_CLOEXEC);
1549
1550 /* now try to authenticate to upsd */
1551
1552 ret = do_upsd_auth(ups);
1553
1554 if (ret == 1)
1555 return 1; /* everything is happy */
1556
1557 /* something failed in the auth so we may not be completely logged in */
1558
1559 /* FUTURE: do something beyond the error msgs from do_upsd_auth? */
1560
1561 return 0;
1562 }
1563
1564 /* deal with the contents of STATUS or ups.status for this ups */
parse_status(utype_t * ups,char * status)1565 static void parse_status(utype_t *ups, char *status)
1566 {
1567 char *statword, *ptr;
1568
1569 clear_alarm();
1570
1571 upsdebugx(2, "%s: [%s]", __func__, status);
1572
1573 /* empty response is the same as a dead ups */
1574 if (status == NULL || status[0] == '\0') {
1575 ups_is_gone(ups);
1576 return;
1577 }
1578
1579 ups_is_alive(ups);
1580
1581 /* clear these out early if they disappear */
1582 if (!strstr(status, "LB"))
1583 clearflag(&ups->status, ST_LOWBATT);
1584 if (!strstr(status, "FSD"))
1585 clearflag(&ups->status, ST_FSD);
1586
1587 statword = status;
1588
1589 /* split up the status words and parse each one separately */
1590 while (statword != NULL) {
1591 ptr = strchr(statword, ' ');
1592 if (ptr)
1593 *ptr++ = '\0';
1594
1595 upsdebugx(3, "parsing: [%s]", statword);
1596
1597 if (!strncasecmp(statword, "OL", 2))
1598 ups_on_line(ups);
1599 if (!strncasecmp(statword, "OB", 2))
1600 ups_on_batt(ups);
1601 if (!strncasecmp(statword, "LB", 2))
1602 ups_low_batt(ups);
1603 if (!strncasecmp(statword, "RB", 2))
1604 upsreplbatt(ups);
1605 if (!strncasecmp(statword, "CAL", 3))
1606 ups_cal(ups);
1607
1608 /* do it last to override any possible OL */
1609 if (!strncasecmp(statword, "FSD", 3))
1610 ups_fsd(ups);
1611
1612 update_crittimer(ups);
1613
1614 statword = ptr;
1615 }
1616 }
1617
1618 /* see what the status of the UPS is and handle any changes */
pollups(utype_t * ups)1619 static void pollups(utype_t *ups)
1620 {
1621 char status[SMALLBUF];
1622
1623 /* try a reconnect here */
1624 if (!flag_isset(ups->status, ST_CONNECTED))
1625 if (try_connect(ups) != 1)
1626 return;
1627
1628 if (upscli_ssl(&ups->conn) == 1)
1629 upsdebugx(2, "%s: %s [SSL]", __func__, ups->sys);
1630 else
1631 upsdebugx(2, "%s: %s", __func__, ups->sys);
1632
1633 set_alarm();
1634
1635 if (get_var(ups, "status", status, sizeof(status)) == 0) {
1636 clear_alarm();
1637 parse_status(ups, status);
1638 return;
1639 }
1640
1641 /* fallthrough: no communications */
1642 clear_alarm();
1643
1644 /* try to make some of these a little friendlier */
1645
1646 switch (upscli_upserror(&ups->conn)) {
1647
1648 case UPSCLI_ERR_UNKNOWNUPS:
1649 upslogx(LOG_ERR, "Poll UPS [%s] failed - [%s] "
1650 "does not exist on server %s",
1651 ups->sys, ups->upsname, ups->hostname);
1652
1653 break;
1654 default:
1655 upslogx(LOG_ERR, "Poll UPS [%s] failed - %s",
1656 ups->sys, upscli_strerror(&ups->conn));
1657 break;
1658 }
1659
1660 /* throw COMMBAD or NOCOMM as conditions may warrant */
1661 ups_is_gone(ups);
1662
1663 /* if upsclient lost the connection, clean up things on our side */
1664 if (upscli_fd(&ups->conn) == -1) {
1665 drop_connection(ups);
1666 return;
1667 }
1668 }
1669
1670 /* see if the powerdownflag file is there and proper */
pdflag_status(void)1671 static int pdflag_status(void)
1672 {
1673 FILE *pdf;
1674 char buf[SMALLBUF];
1675
1676 if (!powerdownflag)
1677 return 0; /* unusable */
1678
1679 pdf = fopen(powerdownflag, "r");
1680
1681 if (pdf == NULL)
1682 return 0; /* not there */
1683
1684 /* if it exists, see if it has the right text in it */
1685
1686 if (fgets(buf, sizeof(buf), pdf) == NULL) {
1687 upslog_with_errno(LOG_ERR, "'%s' exists, but we can't read from it", powerdownflag);
1688 }
1689 fclose(pdf);
1690
1691 /* reasoning: say upsmon.conf is world-writable (!) and some nasty
1692 * user puts something "important" as the power flag file. This
1693 * keeps upsmon from utterly trashing it when starting up or powering
1694 * down at the expense of not shutting down the UPS.
1695 *
1696 * solution: don't let mere mortals edit that configuration file.
1697 */
1698
1699 if (!strncmp(buf, SDMAGIC, strlen(SDMAGIC)))
1700 return 1; /* exists and looks good */
1701
1702 return -1; /* error: something else is in there */
1703 }
1704
1705 /* only remove the flag file if it's actually from us */
clear_pdflag(void)1706 static void clear_pdflag(void)
1707 {
1708 int ret;
1709
1710 ret = pdflag_status();
1711
1712 if (ret == -1) {
1713 upslogx(LOG_ERR, "POWERDOWNFLAG (%s) does not contain"
1714 "the upsmon magic string - disabling!", powerdownflag);
1715 powerdownflag = NULL;
1716 return;
1717 }
1718
1719 /* it's from us, so we can remove it */
1720 if (ret == 1)
1721 unlink(powerdownflag);
1722 }
1723
1724 /* exit with success only if it exists and is proper */
check_pdflag(void)1725 static int check_pdflag(void)
1726 {
1727 int ret;
1728
1729 ret = pdflag_status();
1730
1731 if (ret == -1) {
1732 upslogx(LOG_ERR, "POWERDOWNFLAG (%s) does not contain "
1733 "the upsmon magic string", powerdownflag);
1734 return EXIT_FAILURE;
1735 }
1736
1737 if (ret == 0) {
1738 /* not there - this is not a shutdown event */
1739 upslogx(LOG_ERR, "Power down flag is not set");
1740 return EXIT_FAILURE;
1741 }
1742
1743 if (ret != 1) {
1744 upslogx(LOG_ERR, "Programming error: pdflag_status returned %d",
1745 ret);
1746 return EXIT_FAILURE;
1747 }
1748
1749 /* only thing left - must be time for a shutdown */
1750 upslogx(LOG_INFO, "Power down flag is set");
1751 return EXIT_SUCCESS;
1752 }
1753
1754 static void help(const char *arg_progname)
1755 __attribute__((noreturn));
1756
help(const char * arg_progname)1757 static void help(const char *arg_progname)
1758 {
1759 printf("Monitors UPS servers and may initiate shutdown if necessary.\n\n");
1760
1761 printf("usage: %s [OPTIONS]\n\n", arg_progname);
1762 printf(" -c <cmd> send command to running process\n");
1763 printf(" commands:\n");
1764 printf(" - fsd: shutdown all primary-mode UPSes (use with caution)\n");
1765 printf(" - reload: reread configuration\n");
1766 printf(" - stop: stop monitoring and exit\n");
1767 printf(" -D raise debugging level\n");
1768 printf(" -h display this help\n");
1769 printf(" -K checks POWERDOWNFLAG, sets exit code to 0 if set\n");
1770 printf(" -p always run privileged (disable privileged parent)\n");
1771 printf(" -u <user> run child as user <user> (ignored when using -p)\n");
1772 printf(" -4 IPv4 only\n");
1773 printf(" -6 IPv6 only\n");
1774
1775 exit(EXIT_SUCCESS);
1776 }
1777
1778 static void runparent(int fd)
1779 __attribute__((noreturn));
1780
runparent(int fd)1781 static void runparent(int fd)
1782 {
1783 ssize_t ret;
1784 int sret;
1785 char ch;
1786
1787 /* handling signals is the child's job */
1788 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES)
1789 # pragma GCC diagnostic push
1790 # pragma GCC diagnostic ignored "-Wstrict-prototypes"
1791 #endif
1792 signal(SIGHUP, SIG_IGN);
1793 signal(SIGUSR1, SIG_IGN);
1794 signal(SIGUSR2, SIG_IGN);
1795 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES)
1796 # pragma GCC diagnostic pop
1797 #endif
1798
1799 ret = read(fd, &ch, 1);
1800
1801 if (ret < 1) {
1802 if (errno == ENOENT)
1803 fatalx(EXIT_FAILURE, "upsmon parent: exiting (child exited)");
1804
1805 fatal_with_errno(EXIT_FAILURE, "upsmon parent: read");
1806 }
1807
1808 if (ch != 1)
1809 fatalx(EXIT_FAILURE, "upsmon parent: got bogus pipe command %c", ch);
1810
1811 /* have to do this here - child is unprivileged */
1812 set_pdflag();
1813
1814 sret = system(shutdowncmd);
1815
1816 if (sret != 0)
1817 upslogx(LOG_ERR, "parent: Unable to call shutdown command: %s",
1818 shutdowncmd);
1819
1820 close(fd);
1821 exit(EXIT_SUCCESS);
1822 }
1823
1824 /* fire up the split parent/child scheme */
start_pipe(void)1825 static void start_pipe(void)
1826 {
1827 int ret;
1828
1829 ret = pipe(pipefd);
1830
1831 if (ret)
1832 fatal_with_errno(EXIT_FAILURE, "pipe creation failed");
1833
1834 ret = fork();
1835
1836 if (ret < 0)
1837 fatal_with_errno(EXIT_FAILURE, "fork failed");
1838
1839 /* start the privileged parent */
1840 if (ret != 0) {
1841 close(pipefd[1]);
1842 runparent(pipefd[0]);
1843
1844 #ifndef HAVE___ATTRIBUTE__NORETURN
1845 exit(EXIT_FAILURE); /* NOTREACHED */
1846 #endif
1847 }
1848
1849 close(pipefd[0]);
1850
1851 /* prevent pipe leaking to NOTIFYCMD */
1852 fcntl(pipefd[1], F_SETFD, FD_CLOEXEC);
1853 }
1854
delete_ups(utype_t * target)1855 static void delete_ups(utype_t *target)
1856 {
1857 utype_t *ptr, *last;
1858
1859 if (!target)
1860 return;
1861
1862 ptr = last = firstups;
1863
1864 while (ptr) {
1865 if (ptr == target) {
1866 upslogx(LOG_NOTICE, "No longer monitoring UPS [%s]",
1867 target->sys);
1868
1869 /* disconnect cleanly */
1870 drop_connection(ptr);
1871
1872 /* about to delete the first ups? */
1873 if (ptr == last)
1874 firstups = ptr->next;
1875 else
1876 last->next = ptr->next;
1877
1878 /* release memory */
1879
1880 ups_free(ptr);
1881
1882 return;
1883 }
1884
1885 last = ptr;
1886 ptr = ptr->next;
1887 }
1888
1889 /* shouldn't happen */
1890 upslogx(LOG_ERR, "delete_ups: UPS not found");
1891 }
1892
1893 /* see if we can open a file */
check_file(const char * fn)1894 static int check_file(const char *fn)
1895 {
1896 FILE *f;
1897
1898 f = fopen(fn, "r");
1899
1900 if (!f) {
1901 upslog_with_errno(LOG_ERR, "Reload failed: can't open %s", fn);
1902 return 0; /* failed */
1903 }
1904
1905 fclose(f);
1906 return 1; /* OK */
1907 }
1908
reload_conf(void)1909 static void reload_conf(void)
1910 {
1911 utype_t *tmp, *next;
1912
1913 upslogx(LOG_INFO, "Reloading configuration");
1914
1915 /* sanity check */
1916 if (!check_file(configfile)) {
1917 reload_flag = 0;
1918 return;
1919 }
1920
1921 /* flip through ups list, clear retain value */
1922 tmp = firstups;
1923
1924 while (tmp) {
1925 tmp->retain = 0;
1926 tmp = tmp->next;
1927 }
1928
1929 /* reset paranoia checker */
1930 totalpv = 0;
1931
1932 /* reread upsmon.conf */
1933 loadconfig();
1934
1935 /* go through the utype_t struct again */
1936 tmp = firstups;
1937
1938 while (tmp) {
1939 next = tmp->next;
1940
1941 /* !retain means it wasn't in the .conf this time around */
1942 if (tmp->retain == 0)
1943 delete_ups(tmp);
1944
1945 tmp = next;
1946 }
1947
1948 /* see if the user just blew off a foot */
1949 if (totalpv < minsupplies) {
1950 upslogx(LOG_CRIT, "Fatal error: total power value (%d) less "
1951 "than MINSUPPLIES (%d)", totalpv, minsupplies);
1952
1953 fatalx(EXIT_FAILURE, "Impossible power configuration, unable to continue");
1954 }
1955
1956 /* finally clear the flag */
1957 reload_flag = 0;
1958 }
1959
1960 /* make sure the parent is still alive */
check_parent(void)1961 static void check_parent(void)
1962 {
1963 int ret;
1964 fd_set rfds;
1965 struct timeval tv;
1966 time_t now;
1967 static time_t lastwarn = 0;
1968
1969 FD_ZERO(&rfds);
1970 FD_SET(pipefd[1], &rfds);
1971
1972 tv.tv_sec = 0;
1973 tv.tv_usec = 0;
1974
1975 ret = select(pipefd[1] + 1, &rfds, NULL, NULL, &tv);
1976
1977 if (ret == 0)
1978 return;
1979
1980 /* this should never happen, but we MUST KNOW if it ever does */
1981
1982 time(&now);
1983
1984 /* complain every 2 minutes */
1985 if ((now - lastwarn) < 120)
1986 return;
1987
1988 lastwarn = now;
1989 do_notify(NULL, NOTIFY_NOPARENT);
1990
1991 /* also do this in case the notifier isn't being effective */
1992 upslogx(LOG_ALERT, "Parent died - shutdown impossible");
1993 }
1994
main(int argc,char * argv[])1995 int main(int argc, char *argv[])
1996 {
1997 const char *prog = xbasename(argv[0]);
1998 int i, cmd = 0, checking_flag = 0;
1999
2000 printf("Network UPS Tools %s %s\n", prog, UPS_VERSION);
2001
2002 /* if no configuration file is specified on the command line, use default */
2003 configfile = xmalloc(SMALLBUF);
2004 snprintf(configfile, SMALLBUF, "%s/upsmon.conf", confpath());
2005 configfile = xrealloc(configfile, strlen(configfile) + 1);
2006
2007 run_as_user = xstrdup(RUN_AS_USER);
2008
2009 while ((i = getopt(argc, argv, "+Dhic:f:pu:VK46")) != -1) {
2010 switch (i) {
2011 case 'c':
2012 if (!strncmp(optarg, "fsd", strlen(optarg)))
2013 cmd = SIGCMD_FSD;
2014 if (!strncmp(optarg, "stop", strlen(optarg)))
2015 cmd = SIGCMD_STOP;
2016 if (!strncmp(optarg, "reload", strlen(optarg)))
2017 cmd = SIGCMD_RELOAD;
2018
2019 /* bad command name given */
2020 if (cmd == 0)
2021 help(argv[0]);
2022 break;
2023 case 'D':
2024 nut_debug_level++;
2025 break;
2026 case 'f':
2027 free(configfile);
2028 configfile = xstrdup(optarg);
2029 break;
2030 case 'h':
2031 help(argv[0]);
2032 #ifndef HAVE___ATTRIBUTE__NORETURN
2033 break;
2034 #endif
2035 case 'K':
2036 checking_flag = 1;
2037 break;
2038 case 'p':
2039 use_pipe = 0;
2040 break;
2041 case 'u':
2042 free(run_as_user);
2043 run_as_user = xstrdup(optarg);
2044 break;
2045 case 'V':
2046 /* just show the banner */
2047 exit(EXIT_SUCCESS);
2048 case '4':
2049 opt_af = AF_INET;
2050 break;
2051 case '6':
2052 opt_af = AF_INET6;
2053 break;
2054 default:
2055 help(argv[0]);
2056 #ifndef HAVE___ATTRIBUTE__NORETURN
2057 break;
2058 #endif
2059 }
2060 }
2061
2062 if (cmd) {
2063 sendsignal(prog, cmd);
2064 exit(EXIT_SUCCESS);
2065 }
2066
2067 /* otherwise, we are being asked to start.
2068 * so check if a previous instance is running by sending signal '0'
2069 * (Ie 'kill <pid> 0') */
2070 if (sendsignal(prog, 0) == 0) {
2071 printf("Fatal error: A previous upsmon instance is already running!\n");
2072 printf("Either stop the previous instance first, or use the 'reload' command.\n");
2073 exit(EXIT_FAILURE);
2074 }
2075
2076 argc -= optind;
2077 argv += optind;
2078
2079 open_syslog(prog);
2080
2081 loadconfig();
2082
2083 if (checking_flag)
2084 exit(check_pdflag());
2085
2086 if (shutdowncmd == NULL)
2087 printf("Warning: no shutdown command defined!\n");
2088
2089 /* we may need to get rid of a flag from a previous shutdown */
2090 if (powerdownflag != NULL)
2091 clear_pdflag();
2092 /* FIXME (else): POWERDOWNFLAG is not defined!!
2093 * => fallback to a default value */
2094
2095 if (totalpv < minsupplies) {
2096 printf("\nFatal error: insufficient power configured!\n\n");
2097
2098 printf("Sum of power values........: %d\n", totalpv);
2099 printf("Minimum value (MINSUPPLIES): %d\n", minsupplies);
2100
2101 printf("\nEdit your upsmon.conf and change the values.\n");
2102 exit(EXIT_FAILURE);
2103 }
2104
2105 if (nut_debug_level < 1) {
2106 background();
2107 } else {
2108 upsdebugx(1, "debug level is '%d'", nut_debug_level);
2109 }
2110
2111 /* only do the pipe stuff if the user hasn't disabled it */
2112 if (use_pipe) {
2113 struct passwd *new_uid = get_user_pwent(run_as_user);
2114
2115 /* === root parent and unprivileged child split here === */
2116 start_pipe();
2117
2118 /* write the pid file now, as we will soon lose root */
2119 writepid(prog);
2120
2121 become_user(new_uid);
2122 } else {
2123 upslogx(LOG_INFO, "Warning: running as one big root process by request (upsmon -p)");
2124
2125 writepid(prog);
2126 }
2127
2128 if (upscli_init(certverify, certpath, certname, certpasswd) < 0) {
2129 exit(EXIT_FAILURE);
2130 }
2131
2132 /* prep our signal handlers */
2133 setup_signals();
2134
2135 /* reopen the log for the child process */
2136 closelog();
2137 open_syslog(prog);
2138
2139 while (exit_flag == 0) {
2140 utype_t *ups;
2141
2142 /* check flags from signal handlers */
2143 if (userfsd)
2144 forceshutdown();
2145
2146 if (reload_flag)
2147 reload_conf();
2148
2149 for (ups = firstups; ups != NULL; ups = ups->next)
2150 pollups(ups);
2151
2152 recalc();
2153
2154 /* make sure the parent hasn't died */
2155 if (use_pipe)
2156 check_parent();
2157
2158 /* reap children that have exited */
2159 waitpid(-1, NULL, WNOHANG);
2160
2161 sleep(sleepval);
2162 }
2163
2164 upslogx(LOG_INFO, "Signal %d: exiting", exit_flag);
2165 upsmon_cleanup();
2166
2167 exit(EXIT_SUCCESS);
2168 }
2169