Update to dhcpcd-9.2.0 with the following changes:
[dragonfly.git] / contrib / dhcpcd / src / logerr.c
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         char             log_buf[BUFSIZ];
54         unsigned int     log_opts;
55 #ifndef SMALL
56         FILE            *log_file;
57 #ifdef LOGERR_TAG
58         const char      *log_tag;
59 #endif
60 #endif
61 };
62
63 static struct logctx _logctx = {
64         /* syslog style, but without the hostname or tag. */
65         .log_opts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID,
66 };
67
68 #if defined(LOGERR_TAG) && defined(__linux__)
69 /* Poor man's getprogname(3). */
70 static char *_logprog;
71 static const char *
72 getprogname(void)
73 {
74         const char *p;
75
76         /* Use PATH_MAX + 1 to avoid truncation. */
77         if (_logprog == NULL) {
78                 /* readlink(2) does not append a NULL byte,
79                  * so zero the buffer. */
80                 if ((_logprog = calloc(1, PATH_MAX + 1)) == NULL)
81                         return NULL;
82         }
83         if (readlink("/proc/self/exe", _logprog, PATH_MAX + 1) == -1)
84                 return NULL;
85         if (_logprog[0] == '[')
86                 return NULL;
87         p = strrchr(_logprog, '/');
88         if (p == NULL)
89                 return _logprog;
90         return p + 1;
91 }
92 #endif
93
94 #ifndef SMALL
95 /* Write the time, syslog style. month day time - */
96 static int
97 logprintdate(FILE *stream)
98 {
99         struct timeval tv;
100         time_t now;
101         struct tm tmnow;
102         char buf[32];
103
104         if (gettimeofday(&tv, NULL) == -1)
105                 return -1;
106
107         now = tv.tv_sec;
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         errno = _errno;
242 }
243
244 __printflike(2, 3) void
245 logerrmessage(int pri, const char *fmt, ...)
246 {
247         va_list args;
248
249         va_start(args, fmt);
250         vlogerrmessage(pri, fmt, args);
251         va_end(args);
252 }
253
254 void
255 log_debug(const char *fmt, ...)
256 {
257         va_list args;
258
259         va_start(args, fmt);
260         vlogerrmessage(LOG_DEBUG, fmt, args);
261         va_end(args);
262 }
263
264 void
265 log_debugx(const char *fmt, ...)
266 {
267         va_list args;
268
269         va_start(args, fmt);
270         vlogmessage(LOG_DEBUG, fmt, args);
271         va_end(args);
272 }
273
274 void
275 log_info(const char *fmt, ...)
276 {
277         va_list args;
278
279         va_start(args, fmt);
280         vlogerrmessage(LOG_INFO, fmt, args);
281         va_end(args);
282 }
283
284 void
285 log_infox(const char *fmt, ...)
286 {
287         va_list args;
288
289         va_start(args, fmt);
290         vlogmessage(LOG_INFO, fmt, args);
291         va_end(args);
292 }
293
294 void
295 log_warn(const char *fmt, ...)
296 {
297         va_list args;
298
299         va_start(args, fmt);
300         vlogerrmessage(LOG_WARNING, fmt, args);
301         va_end(args);
302 }
303
304 void
305 log_warnx(const char *fmt, ...)
306 {
307         va_list args;
308
309         va_start(args, fmt);
310         vlogmessage(LOG_WARNING, fmt, args);
311         va_end(args);
312 }
313
314 void
315 log_err(const char *fmt, ...)
316 {
317         va_list args;
318
319         va_start(args, fmt);
320         vlogerrmessage(LOG_ERR, fmt, args);
321         va_end(args);
322 }
323
324 void
325 log_errx(const char *fmt, ...)
326 {
327         va_list args;
328
329         va_start(args, fmt);
330         vlogmessage(LOG_ERR, fmt, args);
331         va_end(args);
332 }
333
334 unsigned int
335 loggetopts(void)
336 {
337         struct logctx *ctx = &_logctx;
338
339         return ctx->log_opts;
340 }
341
342 void
343 logsetopts(unsigned int opts)
344 {
345         struct logctx *ctx = &_logctx;
346
347         ctx->log_opts = opts;
348         setlogmask(LOG_UPTO(opts & LOGERR_DEBUG ? LOG_DEBUG : LOG_INFO));
349 }
350
351 #ifdef LOGERR_TAG
352 void
353 logsettag(const char *tag)
354 {
355 #if !defined(SMALL)
356         struct logctx *ctx = &_logctx;
357
358         ctx->log_tag = tag;
359 #else
360         UNUSED(tag);
361 #endif
362 }
363 #endif
364
365 int
366 logopen(const char *path)
367 {
368         struct logctx *ctx = &_logctx;
369
370         /* Cache timezone */
371         tzset();
372
373         (void)setvbuf(stderr, ctx->log_buf, _IOLBF, sizeof(ctx->log_buf));
374
375         if (path == NULL) {
376                 int opts = 0;
377
378                 if (ctx->log_opts & LOGERR_LOG_PID)
379                         opts |= LOG_PID;
380                 openlog(NULL, opts, LOGERR_SYSLOG_FACILITY);
381                 return 1;
382         }
383
384 #ifndef SMALL
385         if ((ctx->log_file = fopen(path, "a")) == NULL)
386                 return -1;
387         setlinebuf(ctx->log_file);
388         return fileno(ctx->log_file);
389 #else
390         errno = ENOTSUP;
391         return -1;
392 #endif
393 }
394
395 void
396 logclose(void)
397 {
398 #ifndef SMALL
399         struct logctx *ctx = &_logctx;
400 #endif
401
402         closelog();
403 #ifndef SMALL
404         if (ctx->log_file == NULL)
405                 return;
406         fclose(ctx->log_file);
407         ctx->log_file = NULL;
408 #endif
409 #if defined(LOGERR_TAG) && defined(__linux__)
410         free(_logprog);
411 #endif
412 }