sbin/hammer2/cmd_debug.c: Clear errno
[dragonfly.git] / contrib / dhcpcd / src / logerr.c
blob319d6f48e6ce2cdb8f505061cfac27e0d1a3539d
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * logerr: errx with logging
4 * Copyright (c) 2006-2023 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 /* syslog protocol is 1k message max, RFC 3164 section 4.1 */
51 #define LOGERR_SYSLOGBUF 1024 + sizeof(int) + sizeof(pid_t)
53 #define UNUSED(a) (void)(a)
55 struct logctx {
56 char log_buf[BUFSIZ];
57 unsigned int log_opts;
58 int log_fd;
59 pid_t log_pid;
60 #ifndef SMALL
61 FILE *log_file;
62 #ifdef LOGERR_TAG
63 const char *log_tag;
64 #endif
65 #endif
68 static struct logctx _logctx = {
69 /* syslog style, but without the hostname or tag. */
70 .log_opts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID,
71 .log_fd = -1,
72 .log_pid = 0,
75 #if defined(__linux__)
76 /* Poor man's getprogname(3). */
77 static char *_logprog;
78 static const char *
79 getprogname(void)
81 const char *p;
83 /* Use PATH_MAX + 1 to avoid truncation. */
84 if (_logprog == NULL) {
85 /* readlink(2) does not append a NULL byte,
86 * so zero the buffer. */
87 if ((_logprog = calloc(1, PATH_MAX + 1)) == NULL)
88 return NULL;
89 if (readlink("/proc/self/exe", _logprog, PATH_MAX + 1) == -1) {
90 free(_logprog);
91 _logprog = NULL;
92 return NULL;
95 if (_logprog[0] == '[')
96 return NULL;
97 p = strrchr(_logprog, '/');
98 if (p == NULL)
99 return _logprog;
100 return p + 1;
102 #endif
104 #ifndef SMALL
105 /* Write the time, syslog style. month day time - */
106 static int
107 logprintdate(FILE *stream)
109 struct timeval tv;
110 time_t now;
111 struct tm tmnow;
112 char buf[32];
114 if (gettimeofday(&tv, NULL) == -1)
115 return -1;
117 now = tv.tv_sec;
118 if (localtime_r(&now, &tmnow) == NULL)
119 return -1;
120 if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
121 return -1;
122 return fprintf(stream, "%s", buf);
124 #endif
126 __printflike(3, 0) static int
127 vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
129 int len = 0, e;
130 va_list a;
131 #ifndef SMALL
132 bool log_pid;
133 #ifdef LOGERR_TAG
134 bool log_tag;
135 #endif
137 if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||
138 (stream != stderr && ctx->log_opts & LOGERR_LOG_DATE))
140 if ((e = logprintdate(stream)) == -1)
141 return -1;
142 len += e;
145 #ifdef LOGERR_TAG
146 log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) ||
147 (stream != stderr && ctx->log_opts & LOGERR_LOG_TAG));
148 if (log_tag) {
149 if (ctx->log_tag == NULL)
150 ctx->log_tag = getprogname();
151 if ((e = fprintf(stream, "%s", ctx->log_tag)) == -1)
152 return -1;
153 len += e;
155 #endif
157 log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||
158 (stream != stderr && ctx->log_opts & LOGERR_LOG_PID));
159 if (log_pid) {
160 pid_t pid;
162 if (ctx->log_pid == 0)
163 pid = getpid();
164 else
165 pid = ctx->log_pid;
166 if ((e = fprintf(stream, "[%d]", pid)) == -1)
167 return -1;
168 len += e;
171 #ifdef LOGERR_TAG
172 if (log_tag || log_pid)
173 #else
174 if (log_pid)
175 #endif
177 if ((e = fprintf(stream, ": ")) == -1)
178 return -1;
179 len += e;
181 #else
182 UNUSED(ctx);
183 #endif
185 va_copy(a, args);
186 e = vfprintf(stream, fmt, a);
187 if (fputc('\n', stream) == EOF)
188 e = -1;
189 else if (e != -1)
190 e++;
191 va_end(a);
193 return e == -1 ? -1 : len + e;
197 * NetBSD's gcc has been modified to check for the non standard %m in printf
198 * like functions and warn noisily about it that they should be marked as
199 * syslog like instead.
200 * This is all well and good, but our logger also goes via vfprintf and
201 * when marked as a sysloglike funcion, gcc will then warn us that the
202 * function should be printflike instead!
203 * This creates an infinte loop of gcc warnings.
204 * Until NetBSD solves this issue, we have to disable a gcc diagnostic
205 * for our fully standards compliant code in the logger function.
207 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
208 #pragma GCC diagnostic push
209 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
210 #endif
211 __printflike(2, 0) static int
212 vlogmessage(int pri, const char *fmt, va_list args)
214 struct logctx *ctx = &_logctx;
215 int len = 0;
217 if (ctx->log_fd != -1) {
218 char buf[LOGERR_SYSLOGBUF];
219 pid_t pid;
221 memcpy(buf, &pri, sizeof(pri));
222 pid = getpid();
223 memcpy(buf + sizeof(pri), &pid, sizeof(pid));
224 len = vsnprintf(buf + sizeof(pri) + sizeof(pid),
225 sizeof(buf) - sizeof(pri) - sizeof(pid),
226 fmt, args);
227 if (len != -1)
228 len = (int)write(ctx->log_fd, buf,
229 ((size_t)++len) + sizeof(pri) + sizeof(pid));
230 return len;
233 if (ctx->log_opts & LOGERR_ERR &&
234 (pri <= LOG_ERR ||
235 (!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) ||
236 (ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG)))
237 len = vlogprintf_r(ctx, stderr, fmt, args);
239 #ifndef SMALL
240 if (ctx->log_file != NULL &&
241 (pri != LOG_DEBUG || (ctx->log_opts & LOGERR_DEBUG)))
242 len = vlogprintf_r(ctx, ctx->log_file, fmt, args);
243 #endif
245 if (ctx->log_opts & LOGERR_LOG)
246 vsyslog(pri, fmt, args);
248 return len;
250 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
251 #pragma GCC diagnostic pop
252 #endif
254 __printflike(2, 3) void
255 logmessage(int pri, const char *fmt, ...)
257 va_list args;
259 va_start(args, fmt);
260 vlogmessage(pri, fmt, args);
261 va_end(args);
264 __printflike(2, 0) static void
265 vlogerrmessage(int pri, const char *fmt, va_list args)
267 int _errno = errno;
268 char buf[1024];
270 vsnprintf(buf, sizeof(buf), fmt, args);
271 logmessage(pri, "%s: %s", buf, strerror(_errno));
272 errno = _errno;
275 __printflike(2, 3) void
276 logerrmessage(int pri, const char *fmt, ...)
278 va_list args;
280 va_start(args, fmt);
281 vlogerrmessage(pri, fmt, args);
282 va_end(args);
285 void
286 log_debug(const char *fmt, ...)
288 va_list args;
290 va_start(args, fmt);
291 vlogerrmessage(LOG_DEBUG, fmt, args);
292 va_end(args);
295 void
296 log_debugx(const char *fmt, ...)
298 va_list args;
300 va_start(args, fmt);
301 vlogmessage(LOG_DEBUG, fmt, args);
302 va_end(args);
305 void
306 log_info(const char *fmt, ...)
308 va_list args;
310 va_start(args, fmt);
311 vlogerrmessage(LOG_INFO, fmt, args);
312 va_end(args);
315 void
316 log_infox(const char *fmt, ...)
318 va_list args;
320 va_start(args, fmt);
321 vlogmessage(LOG_INFO, fmt, args);
322 va_end(args);
325 void
326 log_warn(const char *fmt, ...)
328 va_list args;
330 va_start(args, fmt);
331 vlogerrmessage(LOG_WARNING, fmt, args);
332 va_end(args);
335 void
336 log_warnx(const char *fmt, ...)
338 va_list args;
340 va_start(args, fmt);
341 vlogmessage(LOG_WARNING, fmt, args);
342 va_end(args);
345 void
346 log_err(const char *fmt, ...)
348 va_list args;
350 va_start(args, fmt);
351 vlogerrmessage(LOG_ERR, fmt, args);
352 va_end(args);
355 void
356 log_errx(const char *fmt, ...)
358 va_list args;
360 va_start(args, fmt);
361 vlogmessage(LOG_ERR, fmt, args);
362 va_end(args);
366 loggetfd(void)
368 struct logctx *ctx = &_logctx;
370 return ctx->log_fd;
373 void
374 logsetfd(int fd)
376 struct logctx *ctx = &_logctx;
378 ctx->log_fd = fd;
379 if (fd != -1)
380 closelog();
381 #ifndef SMALL
382 if (fd != -1 && ctx->log_file != NULL) {
383 fclose(ctx->log_file);
384 ctx->log_file = NULL;
386 #endif
390 logreadfd(int fd)
392 struct logctx *ctx = &_logctx;
393 char buf[LOGERR_SYSLOGBUF];
394 int len, pri;
396 len = (int)read(fd, buf, sizeof(buf));
397 if (len == -1)
398 return -1;
400 /* Ensure we have pri, pid and a terminator */
401 if (len < (int)(sizeof(pri) + sizeof(pid_t) + 1) ||
402 buf[len - 1] != '\0')
404 errno = EINVAL;
405 return -1;
408 memcpy(&pri, buf, sizeof(pri));
409 memcpy(&ctx->log_pid, buf + sizeof(pri), sizeof(ctx->log_pid));
410 logmessage(pri, "%s", buf + sizeof(pri) + sizeof(ctx->log_pid));
411 ctx->log_pid = 0;
412 return len;
415 unsigned int
416 loggetopts(void)
418 struct logctx *ctx = &_logctx;
420 return ctx->log_opts;
423 void
424 logsetopts(unsigned int opts)
426 struct logctx *ctx = &_logctx;
428 ctx->log_opts = opts;
429 setlogmask(LOG_UPTO(opts & LOGERR_DEBUG ? LOG_DEBUG : LOG_INFO));
432 #ifdef LOGERR_TAG
433 void
434 logsettag(const char *tag)
436 #if !defined(SMALL)
437 struct logctx *ctx = &_logctx;
439 ctx->log_tag = tag;
440 #else
441 UNUSED(tag);
442 #endif
444 #endif
447 logopen(const char *path)
449 struct logctx *ctx = &_logctx;
450 int opts = 0;
452 /* Cache timezone */
453 tzset();
455 (void)setvbuf(stderr, ctx->log_buf, _IOLBF, sizeof(ctx->log_buf));
457 #ifndef SMALL
458 if (ctx->log_file != NULL) {
459 fclose(ctx->log_file);
460 ctx->log_file = NULL;
462 #endif
464 if (ctx->log_opts & LOGERR_LOG_PID)
465 opts |= LOG_PID;
466 openlog(getprogname(), opts, LOGERR_SYSLOG_FACILITY);
467 if (path == NULL)
468 return 1;
470 #ifndef SMALL
471 if ((ctx->log_file = fopen(path, "ae")) == NULL)
472 return -1;
473 setlinebuf(ctx->log_file);
474 return fileno(ctx->log_file);
475 #else
476 errno = ENOTSUP;
477 return -1;
478 #endif
481 void
482 logclose(void)
484 #ifndef SMALL
485 struct logctx *ctx = &_logctx;
486 #endif
488 closelog();
489 #if defined(__linux__)
490 free(_logprog);
491 _logprog = NULL;
492 #endif
493 #ifndef SMALL
494 if (ctx->log_file == NULL)
495 return;
496 fclose(ctx->log_file);
497 ctx->log_file = NULL;
498 #endif