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