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 } 410