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