Update to dhcpcd-9.1.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         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         if (localtime_r(&now, &tmnow) == NULL)
108                 return -1;
109         if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
110                 return -1;
111         return fprintf(stream, "%s", buf);
112 }
113 #endif
114
115 __printflike(3, 0) static int
116 vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
117 {
118         int len = 0, e;
119         va_list a;
120 #ifndef SMALL
121         bool log_pid;
122 #ifdef LOGERR_TAG
123         bool log_tag;
124 #endif
125
126         if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||
127             (stream != stderr && ctx->log_opts & LOGERR_LOG_DATE))
128         {
129                 if ((e = logprintdate(stream)) == -1)
130                         return -1;
131                 len += e;
132         }
133
134 #ifdef LOGERR_TAG
135         log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) ||
136             (stream != stderr && ctx->log_opts & LOGERR_LOG_TAG));
137         if (log_tag) {
138                 if (ctx->log_tag == NULL)
139                         ctx->log_tag = getprogname();
140                 if ((e = fprintf(stream, "%s", ctx->log_tag)) == -1)
141                         return -1;
142                 len += e;
143         }
144 #endif
145
146         log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||
147             (stream != stderr && ctx->log_opts & LOGERR_LOG_PID));
148         if (log_pid) {
149                 if ((e = fprintf(stream, "[%d]", getpid())) == -1)
150                         return -1;
151                 len += e;
152         }
153
154 #ifdef LOGERR_TAG
155         if (log_tag || log_pid)
156 #else
157         if (log_pid)
158 #endif
159         {
160                 if ((e = fprintf(stream, ": ")) == -1)
161                         return -1;
162                 len += e;
163         }
164 #else
165         UNUSED(ctx);
166 #endif
167
168         va_copy(a, args);
169         e = vfprintf(stream, fmt, a);
170         if (fputc('\n', stream) == EOF)
171                 e = -1;
172         else if (e != -1)
173                 e++;
174         va_end(a);
175
176         return e == -1 ? -1 : len + e;
177 }
178
179 /*
180  * NetBSD's gcc has been modified to check for the non standard %m in printf
181  * like functions and warn noisily about it that they should be marked as
182  * syslog like instead.
183  * This is all well and good, but our logger also goes via vfprintf and
184  * when marked as a sysloglike funcion, gcc will then warn us that the
185  * function should be printflike instead!
186  * This creates an infinte loop of gcc warnings.
187  * Until NetBSD solves this issue, we have to disable a gcc diagnostic
188  * for our fully standards compliant code in the logger function.
189  */
190 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
191 #pragma GCC diagnostic push
192 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
193 #endif
194 __printflike(2, 0) static int
195 vlogmessage(int pri, const char *fmt, va_list args)
196 {
197         struct logctx *ctx = &_logctx;
198         int len = 0;
199
200         if (ctx->log_opts & LOGERR_ERR &&
201             (pri <= LOG_ERR ||
202             (!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) ||
203             (ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG)))
204                 len = vlogprintf_r(ctx, stderr, fmt, args);
205
206         if (!(ctx->log_opts & LOGERR_LOG))
207                 return len;
208
209 #ifndef SMALL
210         if (ctx->log_file != NULL &&
211             (pri != LOG_DEBUG || (ctx->log_opts & LOGERR_DEBUG)))
212                 len = vlogprintf_r(ctx, ctx->log_file, fmt, args);
213 #endif
214
215         vsyslog(pri, fmt, args);
216         return len;
217 }
218 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
219 #pragma GCC diagnostic pop
220 #endif
221
222 __printflike(2, 3) void
223 logmessage(int pri, const char *fmt, ...)
224 {
225         va_list args;
226
227         va_start(args, fmt);
228         vlogmessage(pri, fmt, args);
229         va_end(args);
230 }
231
232 __printflike(2, 0) static void
233 vlogerrmessage(int pri, const char *fmt, va_list args)
234 {
235         int _errno = errno;
236         char buf[1024];
237
238         vsnprintf(buf, sizeof(buf), fmt, args);
239         logmessage(pri, "%s: %s", buf, strerror(_errno));
240         errno = _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         /* Cache timezone */
370         tzset();
371
372         if (path == NULL) {
373                 int opts = 0;
374
375                 if (ctx->log_opts & LOGERR_LOG_PID)
376                         opts |= LOG_PID;
377                 openlog(NULL, opts, LOGERR_SYSLOG_FACILITY);
378                 return 1;
379         }
380
381 #ifndef SMALL
382         if ((ctx->log_file = fopen(path, "a")) == NULL)
383                 return -1;
384         setlinebuf(ctx->log_file);
385         return fileno(ctx->log_file);
386 #else
387         errno = ENOTSUP;
388         return -1;
389 #endif
390 }
391
392 void
393 logclose(void)
394 {
395 #ifndef SMALL
396         struct logctx *ctx = &_logctx;
397 #endif
398
399         closelog();
400 #ifndef SMALL
401         if (ctx->log_file == NULL)
402                 return;
403         fclose(ctx->log_file);
404         ctx->log_file = NULL;
405 #endif
406 #if defined(LOGERR_TAG) && defined(__linux__)
407         free(_logprog);
408 #endif
409 }