xref: /dragonfly/contrib/dhcpcd/src/logerr.c (revision 9f020288)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * logerr: errx with logging
4  * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
5  * All rights reserved
6 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/time.h>
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <syslog.h>
37 #include <time.h>
38 #include <unistd.h>
39 
40 #include "logerr.h"
41 
42 #ifndef	LOGERR_SYSLOG_FACILITY
43 #define	LOGERR_SYSLOG_FACILITY	LOG_DAEMON
44 #endif
45 
46 #ifdef SMALL
47 #undef LOGERR_TAG
48 #endif
49 
50 #define UNUSED(a)		(void)(a)
51 
52 struct logctx {
53 	unsigned int	 log_opts;
54 #ifndef SMALL
55 	FILE		*log_file;
56 #ifdef LOGERR_TAG
57 	const char	*log_tag;
58 #endif
59 #endif
60 };
61 
62 static struct logctx _logctx = {
63 	/* syslog style, but without the hostname or tag. */
64 	.log_opts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID,
65 };
66 
67 #if defined(LOGERR_TAG) && defined(__linux__)
68 /* Poor man's getprogname(3). */
69 static char *_logprog;
70 static const char *
71 getprogname(void)
72 {
73 	const char *p;
74 
75 	/* Use PATH_MAX + 1 to avoid truncation. */
76 	if (_logprog == NULL) {
77 		/* readlink(2) does not append a NULL byte,
78 		 * so zero the buffer. */
79 		if ((_logprog = calloc(1, PATH_MAX + 1)) == NULL)
80 			return NULL;
81 	}
82 	if (readlink("/proc/self/exe", _logprog, PATH_MAX + 1) == -1)
83 		return NULL;
84 	if (_logprog[0] == '[')
85 		return NULL;
86 	p = strrchr(_logprog, '/');
87 	if (p == NULL)
88 		return _logprog;
89 	return p + 1;
90 }
91 #endif
92 
93 #ifndef SMALL
94 /* Write the time, syslog style. month day time - */
95 static int
96 logprintdate(FILE *stream)
97 {
98 	struct timeval tv;
99 	time_t now;
100 	struct tm tmnow;
101 	char buf[32];
102 
103 	if (gettimeofday(&tv, NULL) == -1)
104 		return -1;
105 
106 	now = tv.tv_sec;
107 	tzset();
108 	if (localtime_r(&now, &tmnow) == NULL)
109 		return -1;
110 	if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
111 		return -1;
112 	return fprintf(stream, "%s", buf);
113 }
114 #endif
115 
116 __printflike(3, 0) static int
117 vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
118 {
119 	int len = 0, e;
120 	va_list a;
121 #ifndef SMALL
122 	bool log_pid;
123 #ifdef LOGERR_TAG
124 	bool log_tag;
125 #endif
126 
127 	if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||
128 	    (stream != stderr && ctx->log_opts & LOGERR_LOG_DATE))
129 	{
130 		if ((e = logprintdate(stream)) == -1)
131 			return -1;
132 		len += e;
133 	}
134 
135 #ifdef LOGERR_TAG
136 	log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) ||
137 	    (stream != stderr && ctx->log_opts & LOGERR_LOG_TAG));
138 	if (log_tag) {
139 		if (ctx->log_tag == NULL)
140 			ctx->log_tag = getprogname();
141 		if ((e = fprintf(stream, "%s", ctx->log_tag)) == -1)
142 			return -1;
143 		len += e;
144 	}
145 #endif
146 
147 	log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||
148 	    (stream != stderr && ctx->log_opts & LOGERR_LOG_PID));
149 	if (log_pid) {
150 		if ((e = fprintf(stream, "[%d]", getpid())) == -1)
151 			return -1;
152 		len += e;
153 	}
154 
155 #ifdef LOGERR_TAG
156 	if (log_tag || log_pid)
157 #else
158 	if (log_pid)
159 #endif
160 	{
161 		if ((e = fprintf(stream, ": ")) == -1)
162 			return -1;
163 		len += e;
164 	}
165 #else
166 	UNUSED(ctx);
167 #endif
168 
169 	va_copy(a, args);
170 	e = vfprintf(stream, fmt, a);
171 	if (fputc('\n', stream) == EOF)
172 		e = -1;
173 	else if (e != -1)
174 		e++;
175 	va_end(a);
176 
177 	return e == -1 ? -1 : len + e;
178 }
179 
180 /*
181  * NetBSD's gcc has been modified to check for the non standard %m in printf
182  * like functions and warn noisily about it that they should be marked as
183  * syslog like instead.
184  * This is all well and good, but our logger also goes via vfprintf and
185  * when marked as a sysloglike funcion, gcc will then warn us that the
186  * function should be printflike instead!
187  * This creates an infinte loop of gcc warnings.
188  * Until NetBSD solves this issue, we have to disable a gcc diagnostic
189  * for our fully standards compliant code in the logger function.
190  */
191 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
192 #pragma GCC diagnostic push
193 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
194 #endif
195 __printflike(2, 0) static int
196 vlogmessage(int pri, const char *fmt, va_list args)
197 {
198 	struct logctx *ctx = &_logctx;
199 	int len = 0;
200 
201 	if (ctx->log_opts & LOGERR_ERR &&
202 	    (pri <= LOG_ERR ||
203 	    (!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) ||
204 	    (ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG)))
205 		len = vlogprintf_r(ctx, stderr, fmt, args);
206 
207 	if (!(ctx->log_opts & LOGERR_LOG))
208 		return len;
209 
210 #ifndef SMALL
211 	if (ctx->log_file != NULL &&
212 	    (pri != LOG_DEBUG || (ctx->log_opts & LOGERR_DEBUG)))
213 		len = vlogprintf_r(ctx, ctx->log_file, fmt, args);
214 #endif
215 
216 	vsyslog(pri, fmt, args);
217 	return len;
218 }
219 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
220 #pragma GCC diagnostic pop
221 #endif
222 
223 __printflike(2, 3) void
224 logmessage(int pri, const char *fmt, ...)
225 {
226 	va_list args;
227 
228 	va_start(args, fmt);
229 	vlogmessage(pri, fmt, args);
230 	va_end(args);
231 }
232 
233 __printflike(2, 0) static void
234 vlogerrmessage(int pri, const char *fmt, va_list args)
235 {
236 	int _errno = errno;
237 	char buf[1024];
238 
239 	vsnprintf(buf, sizeof(buf), fmt, args);
240 	logmessage(pri, "%s: %s", buf, strerror(_errno));
241 }
242 
243 __printflike(2, 3) void
244 logerrmessage(int pri, const char *fmt, ...)
245 {
246 	va_list args;
247 
248 	va_start(args, fmt);
249 	vlogerrmessage(pri, fmt, args);
250 	va_end(args);
251 }
252 
253 void
254 log_debug(const char *fmt, ...)
255 {
256 	va_list args;
257 
258 	va_start(args, fmt);
259 	vlogerrmessage(LOG_DEBUG, fmt, args);
260 	va_end(args);
261 }
262 
263 void
264 log_debugx(const char *fmt, ...)
265 {
266 	va_list args;
267 
268 	va_start(args, fmt);
269 	vlogmessage(LOG_DEBUG, fmt, args);
270 	va_end(args);
271 }
272 
273 void
274 log_info(const char *fmt, ...)
275 {
276 	va_list args;
277 
278 	va_start(args, fmt);
279 	vlogerrmessage(LOG_INFO, fmt, args);
280 	va_end(args);
281 }
282 
283 void
284 log_infox(const char *fmt, ...)
285 {
286 	va_list args;
287 
288 	va_start(args, fmt);
289 	vlogmessage(LOG_INFO, fmt, args);
290 	va_end(args);
291 }
292 
293 void
294 log_warn(const char *fmt, ...)
295 {
296 	va_list args;
297 
298 	va_start(args, fmt);
299 	vlogerrmessage(LOG_WARNING, fmt, args);
300 	va_end(args);
301 }
302 
303 void
304 log_warnx(const char *fmt, ...)
305 {
306 	va_list args;
307 
308 	va_start(args, fmt);
309 	vlogmessage(LOG_WARNING, fmt, args);
310 	va_end(args);
311 }
312 
313 void
314 log_err(const char *fmt, ...)
315 {
316 	va_list args;
317 
318 	va_start(args, fmt);
319 	vlogerrmessage(LOG_ERR, fmt, args);
320 	va_end(args);
321 }
322 
323 void
324 log_errx(const char *fmt, ...)
325 {
326 	va_list args;
327 
328 	va_start(args, fmt);
329 	vlogmessage(LOG_ERR, fmt, args);
330 	va_end(args);
331 }
332 
333 unsigned int
334 loggetopts(void)
335 {
336 	struct logctx *ctx = &_logctx;
337 
338 	return ctx->log_opts;
339 }
340 
341 void
342 logsetopts(unsigned int opts)
343 {
344 	struct logctx *ctx = &_logctx;
345 
346 	ctx->log_opts = opts;
347 	setlogmask(LOG_UPTO(opts & LOGERR_DEBUG ? LOG_DEBUG : LOG_INFO));
348 }
349 
350 #ifdef LOGERR_TAG
351 void
352 logsettag(const char *tag)
353 {
354 #if !defined(SMALL)
355 	struct logctx *ctx = &_logctx;
356 
357 	ctx->log_tag = tag;
358 #else
359 	UNUSED(tag);
360 #endif
361 }
362 #endif
363 
364 int
365 logopen(const char *path)
366 {
367 	struct logctx *ctx = &_logctx;
368 
369 	if (path == NULL) {
370 		int opts = 0;
371 
372 		if (ctx->log_opts & LOGERR_LOG_PID)
373 			opts |= LOG_PID;
374 		openlog(NULL, opts, LOGERR_SYSLOG_FACILITY);
375 		return 1;
376 	}
377 
378 #ifndef SMALL
379 	if ((ctx->log_file = fopen(path, "a")) == NULL)
380 		return -1;
381 	setlinebuf(ctx->log_file);
382 	return fileno(ctx->log_file);
383 #else
384 	errno = ENOTSUP;
385 	return -1;
386 #endif
387 }
388 
389 void
390 logclose(void)
391 {
392 #ifndef SMALL
393 	struct logctx *ctx = &_logctx;
394 #endif
395 
396 	closelog();
397 #ifndef SMALL
398 	if (ctx->log_file == NULL)
399 		return;
400 	fclose(ctx->log_file);
401 	ctx->log_file = NULL;
402 #endif
403 #if defined(LOGERR_TAG) && defined(__linux__)
404 	free(_logprog);
405 #endif
406 }
407