1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * logerr: errx with logging
4 * Copyright (c) 2006-2023 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 /* 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)
57 unsigned int log_opts
;
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
,
75 #if defined(__linux__)
76 /* Poor man's getprogname(3). */
77 static char *_logprog
;
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
)
89 if (readlink("/proc/self/exe", _logprog
, PATH_MAX
+ 1) == -1) {
95 if (_logprog
[0] == '[')
97 p
= strrchr(_logprog
, '/');
105 /* Write the time, syslog style. month day time - */
107 logprintdate(FILE *stream
)
114 if (gettimeofday(&tv
, NULL
) == -1)
118 if (localtime_r(&now
, &tmnow
) == NULL
)
120 if (strftime(buf
, sizeof(buf
), "%b %d %T ", &tmnow
) == 0)
122 return fprintf(stream
, "%s", buf
);
126 __printflike(3, 0) static int
127 vlogprintf_r(struct logctx
*ctx
, FILE *stream
, const char *fmt
, va_list args
)
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)
146 log_tag
= ((stream
== stderr
&& ctx
->log_opts
& LOGERR_ERR_TAG
) ||
147 (stream
!= stderr
&& ctx
->log_opts
& LOGERR_LOG_TAG
));
149 if (ctx
->log_tag
== NULL
)
150 ctx
->log_tag
= getprogname();
151 if ((e
= fprintf(stream
, "%s", ctx
->log_tag
)) == -1)
157 log_pid
= ((stream
== stderr
&& ctx
->log_opts
& LOGERR_ERR_PID
) ||
158 (stream
!= stderr
&& ctx
->log_opts
& LOGERR_LOG_PID
));
162 if (ctx
->log_pid
== 0)
166 if ((e
= fprintf(stream
, "[%d]", pid
)) == -1)
172 if (log_tag
|| log_pid
)
177 if ((e
= fprintf(stream
, ": ")) == -1)
186 e
= vfprintf(stream
, fmt
, a
);
187 if (fputc('\n', stream
) == EOF
)
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"
211 __printflike(2, 0) static int
212 vlogmessage(int pri
, const char *fmt
, va_list args
)
214 struct logctx
*ctx
= &_logctx
;
217 if (ctx
->log_fd
!= -1) {
218 char buf
[LOGERR_SYSLOGBUF
];
221 memcpy(buf
, &pri
, sizeof(pri
));
223 memcpy(buf
+ sizeof(pri
), &pid
, sizeof(pid
));
224 len
= vsnprintf(buf
+ sizeof(pri
) + sizeof(pid
),
225 sizeof(buf
) - sizeof(pri
) - sizeof(pid
),
228 len
= (int)write(ctx
->log_fd
, buf
,
229 ((size_t)++len
) + sizeof(pri
) + sizeof(pid
));
233 if (ctx
->log_opts
& LOGERR_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
);
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
);
245 if (ctx
->log_opts
& LOGERR_LOG
)
246 vsyslog(pri
, fmt
, args
);
250 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
251 #pragma GCC diagnostic pop
254 __printflike(2, 3) void
255 logmessage(int pri
, const char *fmt
, ...)
260 vlogmessage(pri
, fmt
, args
);
264 __printflike(2, 0) static void
265 vlogerrmessage(int pri
, const char *fmt
, va_list args
)
270 vsnprintf(buf
, sizeof(buf
), fmt
, args
);
271 logmessage(pri
, "%s: %s", buf
, strerror(_errno
));
275 __printflike(2, 3) void
276 logerrmessage(int pri
, const char *fmt
, ...)
281 vlogerrmessage(pri
, fmt
, args
);
286 log_debug(const char *fmt
, ...)
291 vlogerrmessage(LOG_DEBUG
, fmt
, args
);
296 log_debugx(const char *fmt
, ...)
301 vlogmessage(LOG_DEBUG
, fmt
, args
);
306 log_info(const char *fmt
, ...)
311 vlogerrmessage(LOG_INFO
, fmt
, args
);
316 log_infox(const char *fmt
, ...)
321 vlogmessage(LOG_INFO
, fmt
, args
);
326 log_warn(const char *fmt
, ...)
331 vlogerrmessage(LOG_WARNING
, fmt
, args
);
336 log_warnx(const char *fmt
, ...)
341 vlogmessage(LOG_WARNING
, fmt
, args
);
346 log_err(const char *fmt
, ...)
351 vlogerrmessage(LOG_ERR
, fmt
, args
);
356 log_errx(const char *fmt
, ...)
361 vlogmessage(LOG_ERR
, fmt
, args
);
368 struct logctx
*ctx
= &_logctx
;
376 struct logctx
*ctx
= &_logctx
;
382 if (fd
!= -1 && ctx
->log_file
!= NULL
) {
383 fclose(ctx
->log_file
);
384 ctx
->log_file
= NULL
;
392 struct logctx
*ctx
= &_logctx
;
393 char buf
[LOGERR_SYSLOGBUF
];
396 len
= (int)read(fd
, buf
, sizeof(buf
));
400 /* Ensure we have pri, pid and a terminator */
401 if (len
< (int)(sizeof(pri
) + sizeof(pid_t
) + 1) ||
402 buf
[len
- 1] != '\0')
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
));
418 struct logctx
*ctx
= &_logctx
;
420 return ctx
->log_opts
;
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
));
434 logsettag(const char *tag
)
437 struct logctx
*ctx
= &_logctx
;
447 logopen(const char *path
)
449 struct logctx
*ctx
= &_logctx
;
455 (void)setvbuf(stderr
, ctx
->log_buf
, _IOLBF
, sizeof(ctx
->log_buf
));
458 if (ctx
->log_file
!= NULL
) {
459 fclose(ctx
->log_file
);
460 ctx
->log_file
= NULL
;
464 if (ctx
->log_opts
& LOGERR_LOG_PID
)
466 openlog(getprogname(), opts
, LOGERR_SYSLOG_FACILITY
);
471 if ((ctx
->log_file
= fopen(path
, "ae")) == NULL
)
473 setlinebuf(ctx
->log_file
);
474 return fileno(ctx
->log_file
);
485 struct logctx
*ctx
= &_logctx
;
489 #if defined(__linux__)
494 if (ctx
->log_file
== NULL
)
496 fclose(ctx
->log_file
);
497 ctx
->log_file
= NULL
;