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