1 /* common.c - common useful functions
2
3 Copyright (C) 2000 Russell Kroll <rkroll@exploits.org>
4 Copyright (C) 2021 Jim Klimov <jimklimov+nut@gmail.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "common.h"
22
23 #include <ctype.h>
24 #include <syslog.h>
25 #include <errno.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <dirent.h>
29
30 /* the reason we define UPS_VERSION as a static string, rather than a
31 macro, is to make dependency tracking easier (only common.o depends
32 on nut_version_macro.h), and also to prevent all sources from
33 having to be recompiled each time the version changes (they only
34 need to be re-linked). */
35 #include "nut_version.h"
36 const char *UPS_VERSION = NUT_VERSION_MACRO;
37
38 #include <stdio.h>
39
40 /* Know which bitness we were built for,
41 * to adjust the search paths for get_libname() */
42 #include "nut_stdint.h"
43 #if UINTPTR_MAX == 0xffffffffffffffffULL
44 # define BUILD_64 1
45 #else
46 # ifdef BUILD_64
47 # undef BUILD_64
48 # endif
49 #endif
50
51 // https://stackoverflow.com/a/12844426/4715872
52 #include <sys/types.h>
53 #include <limits.h>
54 #include <stdlib.h>
get_max_pid_t()55 pid_t get_max_pid_t()
56 {
57 if (sizeof(pid_t) == sizeof(short)) return (pid_t)SHRT_MAX;
58 if (sizeof(pid_t) == sizeof(int)) return (pid_t)INT_MAX;
59 if (sizeof(pid_t) == sizeof(long)) return (pid_t)LONG_MAX;
60 #if defined(LLONG_MAX) // C99
61 if (sizeof(pid_t) == sizeof(long long)) return (pid_t)LLONG_MAX;
62 #endif
63 abort();
64 }
65
66 int nut_debug_level = 0;
67 int nut_log_level = 0;
68 static int upslog_flags = UPSLOG_STDERR;
69
xbit_set(int * val,int flag)70 static void xbit_set(int *val, int flag)
71 {
72 *val |= flag;
73 }
74
xbit_clear(int * val,int flag)75 static void xbit_clear(int *val, int flag)
76 {
77 *val ^= (*val & flag);
78 }
79
xbit_test(int val,int flag)80 static int xbit_test(int val, int flag)
81 {
82 return ((val & flag) == flag);
83 }
84
85 /* enable writing upslog_with_errno() and upslogx() type messages to
86 the syslog */
syslogbit_set(void)87 void syslogbit_set(void)
88 {
89 xbit_set(&upslog_flags, UPSLOG_SYSLOG);
90 }
91
92 /* get the syslog ready for us */
open_syslog(const char * progname)93 void open_syslog(const char *progname)
94 {
95 int opt;
96
97 opt = LOG_PID;
98
99 /* we need this to grab /dev/log before chroot */
100 #ifdef LOG_NDELAY
101 opt |= LOG_NDELAY;
102 #endif
103
104 openlog(progname, opt, LOG_FACILITY);
105
106 switch (nut_log_level)
107 {
108 #if HAVE_SETLOGMASK && HAVE_DECL_LOG_UPTO
109 case 7:
110 setlogmask(LOG_UPTO(LOG_EMERG)); /* system is unusable */
111 break;
112 case 6:
113 setlogmask(LOG_UPTO(LOG_ALERT)); /* action must be taken immediately */
114 break;
115 case 5:
116 setlogmask(LOG_UPTO(LOG_CRIT)); /* critical conditions */
117 break;
118 case 4:
119 setlogmask(LOG_UPTO(LOG_ERR)); /* error conditions */
120 break;
121 case 3:
122 setlogmask(LOG_UPTO(LOG_WARNING)); /* warning conditions */
123 break;
124 case 2:
125 setlogmask(LOG_UPTO(LOG_NOTICE)); /* normal but significant condition */
126 break;
127 case 1:
128 setlogmask(LOG_UPTO(LOG_INFO)); /* informational */
129 break;
130 case 0:
131 setlogmask(LOG_UPTO(LOG_DEBUG)); /* debug-level messages */
132 break;
133 default:
134 fatalx(EXIT_FAILURE, "Invalid log level threshold");
135 #else
136 case 0:
137 break;
138 default:
139 upslogx(LOG_INFO, "Changing log level threshold not possible");
140 break;
141 #endif
142 }
143 }
144
145 /* close ttys and become a daemon */
background(void)146 void background(void)
147 {
148 int pid;
149
150 if ((pid = fork()) < 0)
151 fatal_with_errno(EXIT_FAILURE, "Unable to enter background");
152
153 xbit_set(&upslog_flags, UPSLOG_SYSLOG);
154 xbit_clear(&upslog_flags, UPSLOG_STDERR);
155
156 close(0);
157 close(1);
158 close(2);
159
160 if (pid != 0)
161 _exit(EXIT_SUCCESS); /* parent */
162
163 /* child */
164
165 /* make fds 0-2 point somewhere defined */
166 if (open("/dev/null", O_RDWR) != 0)
167 fatal_with_errno(EXIT_FAILURE, "open /dev/null");
168
169 if (dup(0) == -1)
170 fatal_with_errno(EXIT_FAILURE, "dup");
171
172 if (dup(0) == -1)
173 fatal_with_errno(EXIT_FAILURE, "dup");
174
175 #ifdef HAVE_SETSID
176 setsid(); /* make a new session to dodge signals */
177 #endif
178
179 upslogx(LOG_INFO, "Startup successful");
180 }
181
182 /* do this here to keep pwd/grp stuff out of the main files */
get_user_pwent(const char * name)183 struct passwd *get_user_pwent(const char *name)
184 {
185 struct passwd *r;
186 errno = 0;
187 if ((r = getpwnam(name)))
188 return r;
189
190 /* POSIX does not specify that "user not found" is an error, so
191 some implementations of getpwnam() do not set errno when this
192 happens. */
193 if (errno == 0)
194 fatalx(EXIT_FAILURE, "user %s not found", name);
195 else
196 fatal_with_errno(EXIT_FAILURE, "getpwnam(%s)", name);
197
198 return NULL; /* to make the compiler happy */
199 }
200
201 /* change to the user defined in the struct */
become_user(struct passwd * pw)202 void become_user(struct passwd *pw)
203 {
204 /* if we can't switch users, then don't even try */
205 if ((geteuid() != 0) && (getuid() != 0))
206 return;
207
208 if (getuid() == 0)
209 if (seteuid(0))
210 fatal_with_errno(EXIT_FAILURE, "getuid gave 0, but seteuid(0) failed");
211
212 if (initgroups(pw->pw_name, pw->pw_gid) == -1)
213 fatal_with_errno(EXIT_FAILURE, "initgroups");
214
215 if (setgid(pw->pw_gid) == -1)
216 fatal_with_errno(EXIT_FAILURE, "setgid");
217
218 if (setuid(pw->pw_uid) == -1)
219 fatal_with_errno(EXIT_FAILURE, "setuid");
220 }
221
222 /* drop down into a directory and throw away pointers to the old path */
chroot_start(const char * path)223 void chroot_start(const char *path)
224 {
225 if (chdir(path))
226 fatal_with_errno(EXIT_FAILURE, "chdir(%s)", path);
227
228 if (chroot(path))
229 fatal_with_errno(EXIT_FAILURE, "chroot(%s)", path);
230
231 if (chdir("/"))
232 fatal_with_errno(EXIT_FAILURE, "chdir(/)");
233
234 upsdebugx(1, "chrooted into %s", path);
235 }
236
237 /* drop off a pidfile for this process */
writepid(const char * name)238 void writepid(const char *name)
239 {
240 char fn[SMALLBUF];
241 FILE *pidf;
242 mode_t mask;
243
244 /* use full path if present, else build filename in PIDPATH */
245 if (*name == '/')
246 snprintf(fn, sizeof(fn), "%s", name);
247 else
248 snprintf(fn, sizeof(fn), "%s/%s.pid", PIDPATH, name);
249
250 mask = umask(022);
251 pidf = fopen(fn, "w");
252
253 if (pidf) {
254 fprintf(pidf, "%d\n", (int) getpid());
255 fclose(pidf);
256 } else {
257 upslog_with_errno(LOG_NOTICE, "writepid: fopen %s", fn);
258 }
259
260 umask(mask);
261 }
262
263 /* open pidfn, get the pid, then send it sig */
sendsignalfn(const char * pidfn,int sig)264 int sendsignalfn(const char *pidfn, int sig)
265 {
266 char buf[SMALLBUF];
267 FILE *pidf;
268 pid_t pid = -1;
269 int ret;
270
271 pidf = fopen(pidfn, "r");
272 if (!pidf) {
273 upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn);
274 return -1;
275 }
276
277 if (fgets(buf, sizeof(buf), pidf) == NULL) {
278 upslogx(LOG_NOTICE, "Failed to read pid from %s", pidfn);
279 fclose(pidf);
280 return -1;
281 }
282
283 { // scoping
284 intmax_t _pid = strtol(buf, (char **)NULL, 10); // assuming 10 digits for a long
285 if (_pid <= get_max_pid_t()) {
286 pid = (pid_t)_pid;
287 } else {
288 upslogx(LOG_NOTICE, "Received a pid number too big for a pid_t: %" PRIdMAX, _pid);
289 }
290 }
291
292 if (pid < 2) {
293 upslogx(LOG_NOTICE, "Ignoring invalid pid number %" PRIdMAX, (intmax_t) pid);
294 fclose(pidf);
295 return -1;
296 }
297
298 /* see if this is going to work first */
299 ret = kill(pid, 0);
300
301 if (ret < 0) {
302 perror("kill");
303 fclose(pidf);
304 return -1;
305 }
306
307 /* now actually send it */
308 ret = kill(pid, sig);
309
310 if (ret < 0) {
311 perror("kill");
312 fclose(pidf);
313 return -1;
314 }
315
316 fclose(pidf);
317 return 0;
318 }
319
snprintfcat(char * dst,size_t size,const char * fmt,...)320 int snprintfcat(char *dst, size_t size, const char *fmt, ...)
321 {
322 va_list ap;
323 size_t len = strlen(dst);
324 int ret;
325
326 size--;
327 if (len > size) {
328 /* Do not truncate existing string */
329 errno = ERANGE;
330 return -1;
331 }
332
333 va_start(ap, fmt);
334 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
335 #pragma GCC diagnostic push
336 #endif
337 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
338 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
339 #endif
340 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
341 #pragma GCC diagnostic ignored "-Wformat-security"
342 #endif
343 /* Note: this code intentionally uses a caller-provided format string */
344 ret = vsnprintf(dst + len, size - len, fmt, ap);
345 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
346 #pragma GCC diagnostic pop
347 #endif
348 va_end(ap);
349
350 dst[size] = '\0';
351
352 /* Note: there is a standards loophole here: strlen() must return size_t
353 * and printf() family returns a signed int with negatives for errors.
354 * In theory it can overflow a 64-vs-32 bit range, or signed-vs-unsigned.
355 * In practice we hope to not have gigabytes-long config strings.
356 */
357 if (ret < 0) {
358 return ret;
359 }
360 #ifdef INT_MAX
361 if ( ( (unsigned long long)len + (unsigned long long)ret ) >= (unsigned long long)INT_MAX ) {
362 errno = ERANGE;
363 return -1;
364 }
365 #endif
366 return (int)len + ret;
367 }
368
369 /* lazy way to send a signal if the program uses the PIDPATH */
sendsignal(const char * progname,int sig)370 int sendsignal(const char *progname, int sig)
371 {
372 char fn[SMALLBUF];
373
374 snprintf(fn, sizeof(fn), "%s/%s.pid", PIDPATH, progname);
375
376 return sendsignalfn(fn, sig);
377 }
378
xbasename(const char * file)379 const char *xbasename(const char *file)
380 {
381 const char *p = strrchr(file, '/');
382
383 if (p == NULL)
384 return file;
385 return p + 1;
386 }
387
vupslog(int priority,const char * fmt,va_list va,int use_strerror)388 static void vupslog(int priority, const char *fmt, va_list va, int use_strerror)
389 {
390 int ret;
391 char buf[LARGEBUF];
392
393 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
394 #pragma GCC diagnostic push
395 #endif
396 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
397 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
398 #endif
399 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
400 #pragma GCC diagnostic ignored "-Wformat-security"
401 #endif
402 ret = vsnprintf(buf, sizeof(buf), fmt, va);
403 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
404 #pragma GCC diagnostic pop
405 #endif
406
407 if ((ret < 0) || (ret >= (int) sizeof(buf)))
408 syslog(LOG_WARNING, "vupslog: vsnprintf needed more than %d bytes",
409 LARGEBUF);
410
411 if (use_strerror)
412 snprintfcat(buf, sizeof(buf), ": %s", strerror(errno));
413
414 if (nut_debug_level > 0) {
415 static struct timeval start = { 0, 0 };
416 struct timeval now;
417
418 gettimeofday(&now, NULL);
419
420 if (start.tv_sec == 0) {
421 start = now;
422 }
423
424 if (start.tv_usec > now.tv_usec) {
425 now.tv_usec += 1000000;
426 now.tv_sec -= 1;
427 }
428
429 fprintf(stderr, "%4.0f.%06ld\t", difftime(now.tv_sec, start.tv_sec), (long)(now.tv_usec - start.tv_usec));
430 }
431
432 if (xbit_test(upslog_flags, UPSLOG_STDERR))
433 fprintf(stderr, "%s\n", buf);
434 if (xbit_test(upslog_flags, UPSLOG_SYSLOG))
435 syslog(priority, "%s", buf);
436 }
437
438 /* Return the default path for the directory containing configuration files */
confpath(void)439 const char * confpath(void)
440 {
441 const char * path;
442
443 if ((path = getenv("NUT_CONFPATH")) == NULL)
444 path = CONFPATH;
445
446 return path;
447 }
448
449 /* Return the default path for the directory containing state files */
dflt_statepath(void)450 const char * dflt_statepath(void)
451 {
452 const char * path;
453
454 path = getenv("NUT_STATEPATH");
455 if ( (path == NULL) || (*path == '\0') )
456 path = STATEPATH;
457
458 return path;
459 }
460
461 /* Return the alternate path for pid files, for processes running as non-root
462 * Per documentation and configure script, the fallback value is the
463 * state-file path as the daemon and drivers can write there too.
464 * Note that this differs from PIDPATH that higher-privileged daemons, such
465 * as upsmon, tend to use.
466 */
altpidpath(void)467 const char * altpidpath(void)
468 {
469 const char * path;
470
471 path = getenv("NUT_ALTPIDPATH");
472 if ( (path == NULL) || (*path == '\0') )
473 path = getenv("NUT_STATEPATH");
474
475 if ( (path != NULL) && (*path != '\0') )
476 return path;
477
478 #ifdef ALTPIDPATH
479 return ALTPIDPATH;
480 #else
481 /* We assume, here and elsewhere, that at least STATEPATH is always defined */
482 return STATEPATH;
483 #endif
484 }
485
486 /* logs the formatted string to any configured logging devices + the output of strerror(errno) */
upslog_with_errno(int priority,const char * fmt,...)487 void upslog_with_errno(int priority, const char *fmt, ...)
488 {
489 va_list va;
490
491 va_start(va, fmt);
492 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
493 #pragma GCC diagnostic push
494 #endif
495 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
496 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
497 #endif
498 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
499 #pragma GCC diagnostic ignored "-Wformat-security"
500 #endif
501 vupslog(priority, fmt, va, 1);
502 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
503 #pragma GCC diagnostic pop
504 #endif
505 va_end(va);
506 }
507
508 /* logs the formatted string to any configured logging devices */
upslogx(int priority,const char * fmt,...)509 void upslogx(int priority, const char *fmt, ...)
510 {
511 va_list va;
512
513 va_start(va, fmt);
514 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
515 #pragma GCC diagnostic push
516 #endif
517 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
518 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
519 #endif
520 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
521 #pragma GCC diagnostic ignored "-Wformat-security"
522 #endif
523 vupslog(priority, fmt, va, 0);
524 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
525 #pragma GCC diagnostic pop
526 #endif
527 va_end(va);
528 }
529
s_upsdebug_with_errno(int level,const char * fmt,...)530 void s_upsdebug_with_errno(int level, const char *fmt, ...)
531 {
532 va_list va;
533
534 /* Note: Thanks to macro wrapping, we do not quite need this
535 * test now, but we still need the "level" value to report
536 * below - when it is not zero.
537 */
538 if (nut_debug_level < level)
539 return;
540
541 /* For debugging output, we want to prepend the debug level so the user can
542 * e.g. lower the level (less -D's on command line) to retain just the amount
543 * of logging info he needs to see at the moment. Using '-DDDDD' all the time
544 * is too brutal and needed high-level overview can be lost. This [D#] prefix
545 * can help limit this debug stream quicker, than experimentally picking ;) */
546 char fmt2[LARGEBUF];
547 if (level > 0) {
548 int ret;
549 ret = snprintf(fmt2, sizeof(fmt2), "[D%d] %s", level, fmt);
550 if ((ret < 0) || (ret >= (int) sizeof(fmt2))) {
551 syslog(LOG_WARNING, "upsdebug_with_errno: snprintf needed more than %d bytes",
552 LARGEBUF);
553 } else {
554 fmt = (const char *)fmt2;
555 }
556 }
557
558 va_start(va, fmt);
559 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
560 #pragma GCC diagnostic push
561 #endif
562 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
563 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
564 #endif
565 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
566 #pragma GCC diagnostic ignored "-Wformat-security"
567 #endif
568 vupslog(LOG_DEBUG, fmt, va, 1);
569 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
570 #pragma GCC diagnostic pop
571 #endif
572 va_end(va);
573 }
574
s_upsdebugx(int level,const char * fmt,...)575 void s_upsdebugx(int level, const char *fmt, ...)
576 {
577 va_list va;
578
579 if (nut_debug_level < level)
580 return;
581
582 /* See comments above in upsdebug_with_errno() - they apply here too. */
583 char fmt2[LARGEBUF];
584 if (level > 0) {
585 int ret;
586 ret = snprintf(fmt2, sizeof(fmt2), "[D%d] %s", level, fmt);
587 if ((ret < 0) || (ret >= (int) sizeof(fmt2))) {
588 syslog(LOG_WARNING, "upsdebugx: snprintf needed more than %d bytes",
589 LARGEBUF);
590 } else {
591 fmt = (const char *)fmt2;
592 }
593 }
594
595 va_start(va, fmt);
596 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
597 #pragma GCC diagnostic push
598 #endif
599 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
600 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
601 #endif
602 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
603 #pragma GCC diagnostic ignored "-Wformat-security"
604 #endif
605 vupslog(LOG_DEBUG, fmt, va, 0);
606 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
607 #pragma GCC diagnostic pop
608 #endif
609 va_end(va);
610 }
611
612 /* dump message msg and len bytes from buf to upsdebugx(level) in
613 hexadecimal. (This function replaces Philippe Marzouk's original
614 dump_hex() function) */
s_upsdebug_hex(int level,const char * msg,const void * buf,size_t len)615 void s_upsdebug_hex(int level, const char *msg, const void *buf, size_t len)
616 {
617 char line[100];
618 int n; /* number of characters currently in line */
619 size_t i; /* number of bytes output from buffer */
620
621 n = snprintf(line, sizeof(line), "%s: (%zu bytes) =>", msg, len);
622 if (n < 0) goto failed;
623
624 for (i = 0; i < len; i++) {
625
626 if (n > 72) {
627 upsdebugx(level, "%s", line);
628 line[0] = 0;
629 }
630
631 n = snprintfcat(line, sizeof(line), n ? " %02x" : "%02x",
632 ((const unsigned char *)buf)[i]);
633
634 if (n < 0) goto failed;
635 }
636
637 s_upsdebugx(level, "%s", line);
638 return;
639
640 failed:
641 s_upsdebugx(level, "%s", "Failed to print a hex dump for debug");
642 }
643
644 /* taken from www.asciitable.com */
645 static const char* ascii_symb[] = {
646 "NUL", /* 0x00 */
647 "SOH", /* 0x01 */
648 "STX", /* 0x02 */
649 "ETX", /* 0x03 */
650 "EOT", /* 0x04 */
651 "ENQ", /* 0x05 */
652 "ACK", /* 0x06 */
653 "BEL", /* 0x07 */
654 "BS", /* 0x08 */
655 "TAB", /* 0x09 */
656 "LF", /* 0x0A */
657 "VT", /* 0x0B */
658 "FF", /* 0x0C */
659 "CR", /* 0x0D */
660 "SO", /* 0x0E */
661 "SI", /* 0x0F */
662 "DLE", /* 0x10 */
663 "DC1", /* 0x11 */
664 "DC2", /* 0x12 */
665 "DC3", /* 0x13 */
666 "DC4", /* 0x14 */
667 "NAK", /* 0x15 */
668 "SYN", /* 0x16 */
669 "ETB", /* 0x17 */
670 "CAN", /* 0x18 */
671 "EM", /* 0x19 */
672 "SUB", /* 0x1A */
673 "ESC", /* 0x1B */
674 "FS", /* 0x1C */
675 "GS", /* 0x1D */
676 "RS", /* 0x1E */
677 "US" /* 0x1F */
678 };
679
680 /* dump message msg and len bytes from buf to upsdebugx(level) in ascii. */
s_upsdebug_ascii(int level,const char * msg,const void * buf,size_t len)681 void s_upsdebug_ascii(int level, const char *msg, const void *buf, size_t len)
682 {
683 char line[256];
684 int n; /* number of characters currently in line */
685 size_t i; /* number of bytes output from buffer */
686 unsigned char ch;
687
688 if (nut_debug_level < level)
689 return; /* save cpu cycles */
690
691 n = snprintf(line, sizeof(line), "%s", msg);
692 if (n < 0) goto failed;
693
694 for (i=0; i<len; ++i) {
695 ch = ((const unsigned char *)buf)[i];
696
697 if (ch < 0x20)
698 n = snprintfcat(line, sizeof(line), "%3s ", ascii_symb[ch]);
699 else if (ch >= 0x80)
700 n = snprintfcat(line, sizeof(line), "%02Xh ", ch);
701 else
702 n = snprintfcat(line, sizeof(line), "'%c' ", ch);
703
704 if (n < 0) goto failed;
705 }
706
707 s_upsdebugx(level, "%s", line);
708 return;
709
710 failed:
711 s_upsdebugx(level, "%s", "Failed to print an ASCII data dump for debug");
712 }
713
vfatal(const char * fmt,va_list va,int use_strerror)714 static void vfatal(const char *fmt, va_list va, int use_strerror)
715 {
716 if (xbit_test(upslog_flags, UPSLOG_STDERR_ON_FATAL))
717 xbit_set(&upslog_flags, UPSLOG_STDERR);
718 if (xbit_test(upslog_flags, UPSLOG_SYSLOG_ON_FATAL))
719 xbit_set(&upslog_flags, UPSLOG_SYSLOG);
720
721 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
722 #pragma GCC diagnostic push
723 #endif
724 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
725 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
726 #endif
727 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
728 #pragma GCC diagnostic ignored "-Wformat-security"
729 #endif
730 vupslog(LOG_ERR, fmt, va, use_strerror);
731 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
732 #pragma GCC diagnostic pop
733 #endif
734 }
735
fatal_with_errno(int status,const char * fmt,...)736 void fatal_with_errno(int status, const char *fmt, ...)
737 {
738 va_list va;
739
740 va_start(va, fmt);
741 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
742 #pragma GCC diagnostic push
743 #endif
744 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
745 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
746 #endif
747 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
748 #pragma GCC diagnostic ignored "-Wformat-security"
749 #endif
750 vfatal(fmt, va, (errno > 0) ? 1 : 0);
751 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
752 #pragma GCC diagnostic pop
753 #endif
754 va_end(va);
755
756 exit(status);
757 }
758
fatalx(int status,const char * fmt,...)759 void fatalx(int status, const char *fmt, ...)
760 {
761 va_list va;
762
763 va_start(va, fmt);
764 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
765 #pragma GCC diagnostic push
766 #endif
767 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
768 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
769 #endif
770 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
771 #pragma GCC diagnostic ignored "-Wformat-security"
772 #endif
773 vfatal(fmt, va, 0);
774 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
775 #pragma GCC diagnostic pop
776 #endif
777 va_end(va);
778
779 exit(status);
780 }
781
782 static const char *oom_msg = "Out of memory";
783
xmalloc(size_t size)784 void *xmalloc(size_t size)
785 {
786 void *p = malloc(size);
787
788 if (p == NULL)
789 fatal_with_errno(EXIT_FAILURE, "%s", oom_msg);
790 return p;
791 }
792
xcalloc(size_t number,size_t size)793 void *xcalloc(size_t number, size_t size)
794 {
795 void *p = calloc(number, size);
796
797 if (p == NULL)
798 fatal_with_errno(EXIT_FAILURE, "%s", oom_msg);
799 return p;
800 }
801
xrealloc(void * ptr,size_t size)802 void *xrealloc(void *ptr, size_t size)
803 {
804 void *p = realloc(ptr, size);
805
806 if (p == NULL)
807 fatal_with_errno(EXIT_FAILURE, "%s", oom_msg);
808 return p;
809 }
810
xstrdup(const char * string)811 char *xstrdup(const char *string)
812 {
813 char *p = strdup(string);
814
815 if (p == NULL)
816 fatal_with_errno(EXIT_FAILURE, "%s", oom_msg);
817 return p;
818 }
819
820 /* Read up to buflen bytes from fd and return the number of bytes
821 read. If no data is available within d_sec + d_usec, return 0.
822 On error, a value < 0 is returned (errno indicates error). */
select_read(const int fd,void * buf,const size_t buflen,const time_t d_sec,const suseconds_t d_usec)823 ssize_t select_read(const int fd, void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec)
824 {
825 int ret;
826 fd_set fds;
827 struct timeval tv;
828
829 FD_ZERO(&fds);
830 FD_SET(fd, &fds);
831
832 tv.tv_sec = d_sec;
833 tv.tv_usec = d_usec;
834
835 ret = select(fd + 1, &fds, NULL, NULL, &tv);
836
837 if (ret < 1) {
838 return ret;
839 }
840
841 return read(fd, buf, buflen);
842 }
843
844 /* Write up to buflen bytes to fd and return the number of bytes
845 written. If no data is available within d_sec + d_usec, return 0.
846 On error, a value < 0 is returned (errno indicates error). */
select_write(const int fd,const void * buf,const size_t buflen,const time_t d_sec,const suseconds_t d_usec)847 ssize_t select_write(const int fd, const void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec)
848 {
849 int ret;
850 fd_set fds;
851 struct timeval tv;
852
853 FD_ZERO(&fds);
854 FD_SET(fd, &fds);
855
856 tv.tv_sec = d_sec;
857 tv.tv_usec = d_usec;
858
859 ret = select(fd + 1, NULL, &fds, NULL, &tv);
860
861 if (ret < 1) {
862 return ret;
863 }
864
865 return write(fd, buf, buflen);
866 }
867
868
869 /* FIXME: would be good to get more from /etc/ld.so.conf[.d] and/or
870 * LD_LIBRARY_PATH and a smarter dependency on build bitness; also
871 * note that different OSes can have their pathnames set up differently
872 * with regard to default/preferred bitness (maybe a "32" in the name
873 * should also be searched explicitly - again, IFF our build is 32-bit).
874 *
875 * General premise for this solution is that some parts of NUT (e.g. the
876 * nut-scanner tool, or DMF feature code) must be pre-built and distributed
877 * in binary packages, but only at run-time it gets to know which third-party
878 * libraries it should use for particular operations. This differs from e.g.
879 * distribution packages which group NUT driver binaries explicitly dynamically
880 * linked against certain OS-provided libraries for accessing this or that
881 * communications media and/or vendor protocol.
882 */
883 static const char * search_paths[] = {
884 /* Use the library path (and bitness) provided during ./configure first */
885 LIBDIR,
886 "/usr"LIBDIR,
887 "/usr/local"LIBDIR,
888 #ifdef BUILD_64
889 /* Fall back to explicit preference of 64-bit paths as named on some OSes */
890 "/usr/lib/64",
891 "/usr/lib64",
892 #endif
893 "/usr/lib",
894 #ifdef BUILD_64
895 "/lib/64",
896 "/lib64",
897 #endif
898 "/lib",
899 #ifdef BUILD_64
900 "/usr/local/lib/64",
901 "/usr/local/lib64",
902 #endif
903 "/usr/local/lib",
904 #ifdef AUTOTOOLS_TARGET_SHORT_ALIAS
905 "/usr/lib/" AUTOTOOLS_TARGET_SHORT_ALIAS,
906 "/usr/lib/gcc/" AUTOTOOLS_TARGET_SHORT_ALIAS,
907 #else
908 # ifdef AUTOTOOLS_HOST_SHORT_ALIAS
909 "/usr/lib/" AUTOTOOLS_HOST_SHORT_ALIAS,
910 "/usr/lib/gcc/" AUTOTOOLS_HOST_SHORT_ALIAS,
911 # else
912 # ifdef AUTOTOOLS_BUILD_SHORT_ALIAS
913 "/usr/lib/" AUTOTOOLS_BUILD_SHORT_ALIAS,
914 "/usr/lib/gcc/" AUTOTOOLS_BUILD_SHORT_ALIAS,
915 # endif
916 # endif
917 #endif
918 #ifdef AUTOTOOLS_TARGET_ALIAS
919 "/usr/lib/" AUTOTOOLS_TARGET_ALIAS,
920 "/usr/lib/gcc/" AUTOTOOLS_TARGET_ALIAS,
921 #else
922 # ifdef AUTOTOOLS_HOST_ALIAS
923 "/usr/lib/" AUTOTOOLS_HOST_ALIAS,
924 "/usr/lib/gcc/" AUTOTOOLS_HOST_ALIAS,
925 # else
926 # ifdef AUTOTOOLS_BUILD_ALIAS
927 "/usr/lib/" AUTOTOOLS_BUILD_ALIAS,
928 "/usr/lib/gcc/" AUTOTOOLS_BUILD_ALIAS,
929 # endif
930 # endif
931 #endif
932 NULL
933 };
934
get_libname(const char * base_libname)935 char * get_libname(const char* base_libname)
936 {
937 DIR *dp;
938 struct dirent *dirp;
939 int index = 0;
940 char *libname_path = NULL;
941 char current_test_path[LARGEBUF];
942 size_t base_libname_length = strlen(base_libname);
943
944 for(index = 0 ; (search_paths[index] != NULL) && (libname_path == NULL) ; index++)
945 {
946 memset(current_test_path, 0, LARGEBUF);
947
948 if ((dp = opendir(search_paths[index])) == NULL)
949 continue;
950
951 upsdebugx(2,"Looking for lib %s in directory #%d : %s", base_libname, index, search_paths[index]);
952 while ((dirp = readdir(dp)) != NULL)
953 {
954 upsdebugx(5,"Comparing lib %s with dirpath %s", base_libname, dirp->d_name);
955 int compres = strncmp(dirp->d_name, base_libname, base_libname_length);
956 if(compres == 0) {
957 snprintf(current_test_path, LARGEBUF, "%s/%s", search_paths[index], dirp->d_name);
958 libname_path = realpath(current_test_path, NULL);
959 upsdebugx(2,"Candidate path for lib %s is %s (realpath %s)", base_libname, current_test_path, (libname_path!=NULL)?libname_path:"NULL");
960 if (libname_path != NULL)
961 break;
962 }
963 }
964 closedir(dp);
965 }
966
967 upsdebugx(1,"Looking for lib %s, found %s", base_libname, (libname_path!=NULL)?libname_path:"NULL");
968 return libname_path;
969 }
970