xref: /openbsd/usr.sbin/amd/amd/xutil.c (revision c0c90351)
1 /*
2  * Copyright (c) 1990 Jan-Simon Pendry
3  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Jan-Simon Pendry at Imperial College, London.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	from: @(#)xutil.c	8.1 (Berkeley) 6/6/93
35  *	$Id: xutil.c,v 1.20 2023/07/05 18:45:14 guenther Exp $
36  */
37 
38 #include "am.h"
39 
40 #include <sys/stat.h>
41 #include <stdarg.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <time.h>
46 #include <unistd.h>
47 
48 FILE *logfp;
49 int syslogging;
50 int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS & ~XLOG_INFO;
51 int xlog_level_init = ~0;
52 
53 /*
54  * List of log options
55  */
56 struct opt_tab xlog_opt[] = {
57 	{ "all", XLOG_ALL },		/* All messages */
58 #ifdef DEBUG
59 	{ "debug", XLOG_DEBUG },	/* Debug messages */
60 #endif /* DEBUG */
61 	{ "error", XLOG_ERROR },	/* Non-fatal system errors */
62 	{ "fatal", XLOG_FATAL },	/* Fatal errors */
63 	{ "info", XLOG_INFO },		/* Information */
64 	{ "map", XLOG_MAP },		/* Map errors */
65 	{ "stats", XLOG_STATS },	/* Additional statistical information */
66 	{ "user", XLOG_USER },		/* Non-fatal user errors */
67 	{ "warn", XLOG_WARNING },	/* Warnings */
68 	{ "warning", XLOG_WARNING },	/* Warnings */
69 	{ 0, 0 }
70 };
71 
72 __dead void
xmallocfailure(void)73 xmallocfailure(void)
74 {
75 	plog(XLOG_FATAL, "Out of memory");
76 	going_down(1);
77 	abort();
78 }
79 
80 void *
xmalloc(size_t len)81 xmalloc(size_t len)
82 {
83 	void *p;
84 	int retries = 600;
85 
86 	do {
87 		if ((p = malloc(len)) != NULL)
88 			return p;
89 
90 		if (retries > 0) {
91 			plog(XLOG_ERROR, "Retrying memory allocation");
92 			sleep(1);
93 		}
94 	} while (--retries);
95 
96 	xmallocfailure();
97 }
98 
99 void *
xreallocarray(void * ptr,size_t nmemb,size_t size)100 xreallocarray(void *ptr, size_t nmemb, size_t size)
101 {
102 	ptr = reallocarray(ptr, nmemb, size);
103 
104 	if (ptr == NULL)
105 		xmallocfailure();
106 	return (ptr);
107 }
108 
109 
110 /*
111  * Take a log format string and expand occurrences of %m
112  * with the current error code taken from errno.  Make sure
113  * 'e' never gets longer than maxlen characters.
114  */
115 static void
expand_error(const char * f,char * e,int maxlen)116 expand_error(const char *f, char *e, int maxlen)
117 {
118 	const char *p;
119 	char *q;
120 	int error = errno;
121 	int len = 0;
122 
123 	for (p = f, q = e; (*q = *p) && len < maxlen; len++, q++, p++) {
124 		if (p[0] == '%' && p[1] == 'm') {
125 			char *errstr;
126 			errstr = strerror(error);
127 			if (errstr)
128 				strlcpy(q, errstr, maxlen - (q - e));
129 			else
130 				snprintf(q, maxlen - (q - e),
131 				    "Error %d", error);
132 			len += strlen(q) - 1;
133 			q += strlen(q) - 1;
134 			p++;
135 		}
136 	}
137 	e[maxlen-1] = '\0';		/* null terminate, to be sure */
138 }
139 
140 /*
141  * Output the time of day and hostname to the logfile
142  */
143 static void
show_time_host_and_name(int lvl)144 show_time_host_and_name(int lvl)
145 {
146 	static time_t last_t = 0;
147 	static char *last_ctime = 0;
148 	time_t t = clocktime();
149 	char *sev;
150 
151 #if defined(DEBUG) && defined(PARANOID)
152 extern char **gargv;
153 #endif /* defined(DEBUG) && defined(PARANOID) */
154 
155 	if (t != last_t) {
156 		last_ctime = ctime(&t);
157 		last_t = t;
158 	}
159 
160 	switch (lvl) {
161 	case XLOG_FATAL:	sev = "fatal:"; break;
162 	case XLOG_ERROR:	sev = "error:"; break;
163 	case XLOG_USER:		sev = "user: "; break;
164 	case XLOG_WARNING:	sev = "warn: "; break;
165 	case XLOG_INFO:		sev = "info: "; break;
166 	case XLOG_DEBUG:	sev = "debug:"; break;
167 	case XLOG_MAP:		sev = "map:  "; break;
168 	case XLOG_STATS:	sev = "stats:"; break;
169 	default:		sev = "hmm:  "; break;
170 	}
171 	fprintf(logfp, "%15.15s %s %s[%ld]/%s ",
172 		last_ctime+4, hostname,
173 #if defined(DEBUG) && defined(PARANOID)
174 		gargv[0],
175 #else
176 		__progname,
177 #endif /* defined(DEBUG) && defined(PARANOID) */
178 		(long)mypid,
179 		sev);
180 }
181 
182 void
plog(int lvl,const char * fmt,...)183 plog(int lvl, const char *fmt, ...)
184 {
185 	char efmt[1024];
186 	va_list ap;
187 
188 	if (!(xlog_level & lvl))
189 		return;
190 
191 
192 	if (syslogging) {
193 		switch(lvl) {	/* from mike <mcooper@usc.edu> */
194 		case XLOG_FATAL:	lvl = LOG_CRIT; break;
195 		case XLOG_ERROR:	lvl = LOG_ERR; break;
196 		case XLOG_USER:		lvl = LOG_WARNING; break;
197 		case XLOG_WARNING:	lvl = LOG_WARNING; break;
198 		case XLOG_INFO:		lvl = LOG_INFO; break;
199 		case XLOG_DEBUG:	lvl = LOG_DEBUG; break;
200 		case XLOG_MAP:		lvl = LOG_DEBUG; break;
201 		case XLOG_STATS:	lvl = LOG_INFO; break;
202 		default:		lvl = LOG_ERR; break;
203 		}
204 		va_start(ap, fmt);
205 		vsyslog(lvl, fmt, ap);
206 		va_end(ap);
207 		return;
208 	}
209 
210 	expand_error(fmt, efmt, sizeof(efmt));
211 
212 	/*
213 	 * Mimic syslog header
214 	 */
215 	show_time_host_and_name(lvl);
216 	va_start(ap, fmt);
217 	vfprintf(logfp, efmt, ap);
218 	va_end(ap);
219 	fputc('\n', logfp);
220 	fflush(logfp);
221 }
222 
223 void
show_opts(int ch,struct opt_tab * opts)224 show_opts(int ch, struct opt_tab *opts)
225 {
226 	/*
227 	 * Display current debug options
228 	 */
229 	int i;
230 	int s = '{';
231 
232 	fprintf(stderr, "\t[-%c {no}", ch);
233 	for (i = 0; opts[i].opt; i++) {
234 		fprintf(stderr, "%c%s", s, opts[i].opt);
235 		s = ',';
236 	}
237 	fputs("}]\n", stderr);
238 }
239 
240 int
cmdoption(char * s,struct opt_tab * optb,int * flags)241 cmdoption(char *s, struct opt_tab *optb, int *flags)
242 {
243 	char *p = s;
244 	int errs = 0;
245 
246 	while (p && *p) {
247 		int neg;
248 		char *opt;
249 		struct opt_tab *dp, *dpn = 0;
250 
251 		s = p;
252 		p = strchr(p, ',');
253 		if (p)
254 			*p = '\0';
255 
256 		if (s[0] == 'n' && s[1] == 'o') {
257 			opt = s + 2;
258 			neg = 1;
259 		} else {
260 			opt = s;
261 			neg = 0;
262 		}
263 
264 		/*
265 		 * Scan the array of debug options to find the
266 		 * corresponding flag value.  If it is found
267 		 * then set (or clear) the flag (depending on
268 		 * whether the option was prefixed with "no").
269 		 */
270 		for (dp = optb; dp->opt; dp++) {
271 			if (strcmp(opt, dp->opt) == 0)
272 				break;
273 			if (opt != s && !dpn && strcmp(s, dp->opt) == 0)
274 				dpn = dp;
275 		}
276 
277 		if (dp->opt || dpn) {
278 			if (!dp->opt) {
279 				dp = dpn;
280 				neg = !neg;
281 			}
282 			if (neg)
283 				*flags &= ~dp->flag;
284 			else
285 				*flags |= dp->flag;
286 		} else {
287 			/*
288 			 * This will log to stderr when parsing the command line
289 			 * since any -l option will not yet have taken effect.
290 			 */
291 			plog(XLOG_USER, "option \"%s\" not recognised", s);
292 			errs++;
293 		}
294 		/*
295 		 * Put the comma back
296 		 */
297 		if (p)
298 			*p++ = ',';
299 	}
300 
301 	return errs;
302 }
303 
304 /*
305  * Switch on/off logging options
306  */
307 int
switch_option(char * opt)308 switch_option(char *opt)
309 {
310 	int xl = xlog_level;
311 	int rc = cmdoption(opt, xlog_opt, &xl);
312 	if (rc) {
313 		rc = EINVAL;
314 	} else {
315 		/*
316 		 * Keep track of initial log level, and
317 		 * don't allow options to be turned off.
318 		 */
319 		if (xlog_level_init == ~0)
320 			xlog_level_init = xl;
321 		else
322 			xl |= xlog_level_init;
323 		xlog_level = xl;
324 	}
325 	return rc;
326 }
327 
328 /*
329  * Change current logfile
330  */
331 int
switch_to_logfile(char * logfile)332 switch_to_logfile(char *logfile)
333 {
334 	FILE *new_logfp = stderr;
335 
336 	if (logfile) {
337 		syslogging = 0;
338 		if (strcmp(logfile, "/dev/stderr") == 0)
339 			new_logfp = stderr;
340 		else if (strcmp(logfile, "syslog") == 0) {
341 			syslogging = 1;
342 			new_logfp = stderr;
343 #if defined(LOG_CONS) && defined(LOG_NOWAIT)
344 			openlog(__progname, LOG_PID|LOG_CONS|LOG_NOWAIT,
345 				LOG_DAEMON);
346 #else
347 			/* 4.2 compat mode - XXX */
348 			openlog(__progname, LOG_PID);
349 #endif /* LOG_CONS && LOG_NOWAIT */
350 		} else {
351 			(void) umask(orig_umask);
352 			new_logfp = fopen(logfile, "a");
353 			umask(0);
354 		}
355 	}
356 
357 	/*
358 	 * If we couldn't open a new file, then continue using the old.
359 	 */
360 	if (!new_logfp && logfile) {
361 		plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
362 		return 1;
363 	}
364 	/*
365 	 * Close the previous file
366 	 */
367 	if (logfp && logfp != stderr)
368 		(void) fclose(logfp);
369 	logfp = new_logfp;
370 	return 0;
371 }
372 
373 time_t clock_valid = 0;
374 time_t xclock_valid = 0;
375 #ifndef clocktime
376 time_t
clocktime(void)377 clocktime(void)
378 {
379 	time_t now = time(&clock_valid);
380 	if (xclock_valid > now) {
381 		/*
382 		 * Someone set the clock back!
383 		 */
384 		plog(XLOG_WARNING, "system clock reset");
385 		reschedule_timeouts(now, xclock_valid);
386 	}
387 	return xclock_valid = now;
388 }
389 #endif /* clocktime */
390