Import dhcpcd-8.0.4 to vendor branch.
[dragonfly.git] / contrib / dhcpcd / src / logerr.c
blob59f8cb611241be5788fa1c670dc811a039570ca5
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * logerr: errx with logging
4 * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
5 * All rights reserved
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.
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.
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>
40 #include "logerr.h"
42 #ifndef LOGERR_SYSLOG_FACILITY
43 #define LOGERR_SYSLOG_FACILITY LOG_DAEMON
44 #endif
46 #ifdef SMALL
47 #undef LOGERR_TAG
48 #endif
50 #define UNUSED(a) (void)(a)
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
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,
67 #if defined(LOGERR_TAG) && defined(__linux__)
68 /* Poor man's getprogname(3). */
69 static char *_logprog;
70 static const char *
71 getprogname(void)
73 const char *p;
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;
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;
91 #endif
93 #ifndef SMALL
94 /* Write the time, syslog style. month day time - */
95 static int
96 logprintdate(FILE *stream)
98 struct timeval tv;
99 time_t now;
100 struct tm tmnow;
101 char buf[32];
103 if (gettimeofday(&tv, NULL) == -1)
104 return -1;
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);
114 #endif
116 __printflike(3, 0) static int
117 vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
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
127 if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||
128 (stream != stderr && ctx->log_opts & LOGERR_LOG_DATE))
130 if ((e = logprintdate(stream)) == -1)
131 return -1;
132 len += e;
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;
145 #endif
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;
155 #ifdef LOGERR_TAG
156 if (log_tag || log_pid)
157 #else
158 if (log_pid)
159 #endif
161 if ((e = fprintf(stream, ": ")) == -1)
162 return -1;
163 len += e;
165 #else
166 UNUSED(ctx);
167 #endif
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);
177 return e == -1 ? -1 : len + e;
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.
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)
198 struct logctx *ctx = &_logctx;
199 int len = 0;
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);
207 if (!(ctx->log_opts & LOGERR_LOG))
208 return len;
210 #ifdef SMALL
211 vsyslog(pri, fmt, args);
212 return len;
213 #else
214 if (ctx->log_file == NULL) {
215 vsyslog(pri, fmt, args);
216 return len;
218 if (pri == LOG_DEBUG && !(ctx->log_opts & LOGERR_DEBUG))
219 return len;
220 return vlogprintf_r(ctx, ctx->log_file, fmt, args);
221 #endif
223 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
224 #pragma GCC diagnostic pop
225 #endif
227 __printflike(2, 3) static void
228 logmessage(int pri, const char *fmt, ...)
230 va_list args;
232 va_start(args, fmt);
233 vlogmessage(pri, fmt, args);
234 va_end(args);
237 __printflike(2, 0) static void
238 vlogerrmessage(int pri, const char *fmt, va_list args)
240 int _errno = errno;
241 char buf[1024];
243 vsnprintf(buf, sizeof(buf), fmt, args);
244 logmessage(pri, "%s: %s", buf, strerror(_errno));
247 void
248 logdebug(const char *fmt, ...)
250 va_list args;
252 va_start(args, fmt);
253 vlogerrmessage(LOG_DEBUG, fmt, args);
254 va_end(args);
257 void
258 logdebugx(const char *fmt, ...)
260 va_list args;
262 va_start(args, fmt);
263 vlogmessage(LOG_DEBUG, fmt, args);
264 va_end(args);
267 void
268 loginfo(const char *fmt, ...)
270 va_list args;
272 va_start(args, fmt);
273 vlogerrmessage(LOG_INFO, fmt, args);
274 va_end(args);
277 void
278 loginfox(const char *fmt, ...)
280 va_list args;
282 va_start(args, fmt);
283 vlogmessage(LOG_INFO, fmt, args);
284 va_end(args);
287 void
288 logwarn(const char *fmt, ...)
290 va_list args;
292 va_start(args, fmt);
293 vlogerrmessage(LOG_WARNING, fmt, args);
294 va_end(args);
297 void
298 logwarnx(const char *fmt, ...)
300 va_list args;
302 va_start(args, fmt);
303 vlogmessage(LOG_WARNING, fmt, args);
304 va_end(args);
307 void
308 logerr(const char *fmt, ...)
310 va_list args;
312 va_start(args, fmt);
313 vlogerrmessage(LOG_ERR, fmt, args);
314 va_end(args);
317 void
318 logerrx(const char *fmt, ...)
320 va_list args;
322 va_start(args, fmt);
323 vlogmessage(LOG_ERR, fmt, args);
324 va_end(args);
327 void
328 logsetopts(unsigned int opts)
330 struct logctx *ctx = &_logctx;
332 ctx->log_opts = opts;
333 setlogmask(LOG_UPTO(opts & LOGERR_DEBUG ? LOG_DEBUG : LOG_INFO));
336 #ifdef LOGERR_TAG
337 void
338 logsettag(const char *tag)
340 #if !defined(SMALL)
341 struct logctx *ctx = &_logctx;
343 ctx->log_tag = tag;
344 #else
345 UNUSED(tag);
346 #endif
348 #endif
351 logopen(const char *path)
353 struct logctx *ctx = &_logctx;
355 if (path == NULL) {
356 int opts = 0;
358 if (ctx->log_opts & LOGERR_LOG_PID)
359 opts |= LOG_PID;
360 openlog(NULL, opts, LOGERR_SYSLOG_FACILITY);
361 return 1;
364 #ifndef SMALL
365 if ((ctx->log_file = fopen(path, "a")) == NULL)
366 return -1;
367 setlinebuf(ctx->log_file);
368 return fileno(ctx->log_file);
369 #else
370 errno = ENOTSUP;
371 return -1;
372 #endif
375 void
376 logclose(void)
378 #ifndef SMALL
379 struct logctx *ctx = &_logctx;
380 #endif
382 closelog();
383 #ifndef SMALL
384 if (ctx->log_file == NULL)
385 return;
386 fclose(ctx->log_file);
387 ctx->log_file = NULL;
388 #endif
389 #if defined(LOGERR_TAG) && defined(__linux__)
390 free(_logprog);
391 #endif