1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * logerr: errx with logging
4 * Copyright (c) 2006-2019 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)
108 if (localtime_r(&now
, &tmnow
) == NULL
)
110 if (strftime(buf
, sizeof(buf
), "%b %d %T ", &tmnow
) == 0)
112 return fprintf(stream
, "%s", buf
);
116 __printflike(3, 0) static int
117 vlogprintf_r(struct logctx
*ctx
, FILE *stream
, const char *fmt
, va_list args
)
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)
136 log_tag
= ((stream
== stderr
&& ctx
->log_opts
& LOGERR_ERR_TAG
) ||
137 (stream
!= stderr
&& ctx
->log_opts
& LOGERR_LOG_TAG
));
139 if (ctx
->log_tag
== NULL
)
140 ctx
->log_tag
= getprogname();
141 if ((e
= fprintf(stream
, "%s", ctx
->log_tag
)) == -1)
147 log_pid
= ((stream
== stderr
&& ctx
->log_opts
& LOGERR_ERR_PID
) ||
148 (stream
!= stderr
&& ctx
->log_opts
& LOGERR_LOG_PID
));
150 if ((e
= fprintf(stream
, "[%d]", getpid())) == -1)
156 if (log_tag
|| log_pid
)
161 if ((e
= fprintf(stream
, ": ")) == -1)
170 e
= vfprintf(stream
, fmt
, a
);
171 if (fputc('\n', stream
) == EOF
)
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"
195 __printflike(2, 0) static int
196 vlogmessage(int pri
, const char *fmt
, va_list args
)
198 struct logctx
*ctx
= &_logctx
;
201 if (ctx
->log_opts
& LOGERR_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
))
211 vsyslog(pri
, fmt
, args
);
214 if (ctx
->log_file
== NULL
) {
215 vsyslog(pri
, fmt
, args
);
218 if (pri
== LOG_DEBUG
&& !(ctx
->log_opts
& LOGERR_DEBUG
))
220 return vlogprintf_r(ctx
, ctx
->log_file
, fmt
, args
);
223 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
224 #pragma GCC diagnostic pop
227 __printflike(2, 3) static void
228 logmessage(int pri
, const char *fmt
, ...)
233 vlogmessage(pri
, fmt
, args
);
237 __printflike(2, 0) static void
238 vlogerrmessage(int pri
, const char *fmt
, va_list args
)
243 vsnprintf(buf
, sizeof(buf
), fmt
, args
);
244 logmessage(pri
, "%s: %s", buf
, strerror(_errno
));
248 logdebug(const char *fmt
, ...)
253 vlogerrmessage(LOG_DEBUG
, fmt
, args
);
258 logdebugx(const char *fmt
, ...)
263 vlogmessage(LOG_DEBUG
, fmt
, args
);
268 loginfo(const char *fmt
, ...)
273 vlogerrmessage(LOG_INFO
, fmt
, args
);
278 loginfox(const char *fmt
, ...)
283 vlogmessage(LOG_INFO
, fmt
, args
);
288 logwarn(const char *fmt
, ...)
293 vlogerrmessage(LOG_WARNING
, fmt
, args
);
298 logwarnx(const char *fmt
, ...)
303 vlogmessage(LOG_WARNING
, fmt
, args
);
308 logerr(const char *fmt
, ...)
313 vlogerrmessage(LOG_ERR
, fmt
, args
);
318 logerrx(const char *fmt
, ...)
323 vlogmessage(LOG_ERR
, fmt
, args
);
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
));
338 logsettag(const char *tag
)
341 struct logctx
*ctx
= &_logctx
;
351 logopen(const char *path
)
353 struct logctx
*ctx
= &_logctx
;
358 if (ctx
->log_opts
& LOGERR_LOG_PID
)
360 openlog(NULL
, opts
, LOGERR_SYSLOG_FACILITY
);
365 if ((ctx
->log_file
= fopen(path
, "a")) == NULL
)
367 setlinebuf(ctx
->log_file
);
368 return fileno(ctx
->log_file
);
379 struct logctx
*ctx
= &_logctx
;
384 if (ctx
->log_file
== NULL
)
386 fclose(ctx
->log_file
);
387 ctx
->log_file
= NULL
;
389 #if defined(LOGERR_TAG) && defined(__linux__)