1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * logerr: errx with logging
4 * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
42 #ifndef LOGERR_SYSLOG_FACILITY
43 #define LOGERR_SYSLOG_FACILITY LOG_DAEMON
50 #define UNUSED(a) (void)(a)
53 unsigned int log_opts;
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;
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)
82 if (readlink("/proc/self/exe", _logprog, PATH_MAX + 1) == -1)
84 if (_logprog[0] == '[')
86 p = strrchr(_logprog, '/');
94 /* Write the time, syslog style. month day time - */
96 logprintdate(FILE *stream)
103 if (gettimeofday(&tv, NULL) == -1)
107 if (localtime_r(&now, &tmnow) == NULL)
109 if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
111 return fprintf(stream, "%s", buf);
115 __printflike(3, 0) static int
116 vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
126 if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||
127 (stream != stderr && ctx->log_opts & LOGERR_LOG_DATE))
129 if ((e = logprintdate(stream)) == -1)
135 log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) ||
136 (stream != stderr && ctx->log_opts & LOGERR_LOG_TAG));
138 if (ctx->log_tag == NULL)
139 ctx->log_tag = getprogname();
140 if ((e = fprintf(stream, "%s", ctx->log_tag)) == -1)
146 log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||
147 (stream != stderr && ctx->log_opts & LOGERR_LOG_PID));
149 if ((e = fprintf(stream, "[%d]", getpid())) == -1)
155 if (log_tag || log_pid)
160 if ((e = fprintf(stream, ": ")) == -1)
169 e = vfprintf(stream, fmt, a);
170 if (fputc('\n', stream) == EOF)
176 return e == -1 ? -1 : len + e;
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.
190 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
191 #pragma GCC diagnostic push
192 #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
194 __printflike(2, 0) static int
195 vlogmessage(int pri, const char *fmt, va_list args)
197 struct logctx *ctx = &_logctx;
200 if (ctx->log_opts & LOGERR_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);
206 if (!(ctx->log_opts & LOGERR_LOG))
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);
215 vsyslog(pri, fmt, args);
218 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
219 #pragma GCC diagnostic pop
222 __printflike(2, 3) void
223 logmessage(int pri, const char *fmt, ...)
228 vlogmessage(pri, fmt, args);
232 __printflike(2, 0) static void
233 vlogerrmessage(int pri, const char *fmt, va_list args)
238 vsnprintf(buf, sizeof(buf), fmt, args);
239 logmessage(pri, "%s: %s", buf, strerror(_errno));
243 __printflike(2, 3) void
244 logerrmessage(int pri, const char *fmt, ...)
249 vlogerrmessage(pri, fmt, args);
254 log_debug(const char *fmt, ...)
259 vlogerrmessage(LOG_DEBUG, fmt, args);
264 log_debugx(const char *fmt, ...)
269 vlogmessage(LOG_DEBUG, fmt, args);
274 log_info(const char *fmt, ...)
279 vlogerrmessage(LOG_INFO, fmt, args);
284 log_infox(const char *fmt, ...)
289 vlogmessage(LOG_INFO, fmt, args);
294 log_warn(const char *fmt, ...)
299 vlogerrmessage(LOG_WARNING, fmt, args);
304 log_warnx(const char *fmt, ...)
309 vlogmessage(LOG_WARNING, fmt, args);
314 log_err(const char *fmt, ...)
319 vlogerrmessage(LOG_ERR, fmt, args);
324 log_errx(const char *fmt, ...)
329 vlogmessage(LOG_ERR, fmt, args);
336 struct logctx *ctx = &_logctx;
338 return ctx->log_opts;
342 logsetopts(unsigned int opts)
344 struct logctx *ctx = &_logctx;
346 ctx->log_opts = opts;
347 setlogmask(LOG_UPTO(opts & LOGERR_DEBUG ? LOG_DEBUG : LOG_INFO));
352 logsettag(const char *tag)
355 struct logctx *ctx = &_logctx;
365 logopen(const char *path)
367 struct logctx *ctx = &_logctx;
375 if (ctx->log_opts & LOGERR_LOG_PID)
377 openlog(NULL, opts, LOGERR_SYSLOG_FACILITY);
382 if ((ctx->log_file = fopen(path, "a")) == NULL)
384 setlinebuf(ctx->log_file);
385 return fileno(ctx->log_file);
396 struct logctx *ctx = &_logctx;
401 if (ctx->log_file == NULL)
403 fclose(ctx->log_file);
404 ctx->log_file = NULL;
406 #if defined(LOGERR_TAG) && defined(__linux__)