1 /* 2 * logerr: errx with logging 3 * Copyright (c) 2006-2018 Roy Marples <roy@marples.name> 4 * All rights reserved 5 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/time.h> 29 #include <errno.h> 30 #include <stdbool.h> 31 #include <stdarg.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <syslog.h> 36 #include <time.h> 37 #include <unistd.h> 38 39 #include "logerr.h" 40 41 #ifndef LOGERR_SYSLOG_FACILITY 42 #define LOGERR_SYSLOG_FACILITY LOG_DAEMON 43 #endif 44 45 #ifdef SMALL 46 #undef LOGERR_TAG 47 #endif 48 49 #define UNUSED(a) (void)(a) 50 51 struct logctx { 52 unsigned int log_opts; 53 #ifndef SMALL 54 FILE *log_file; 55 #ifdef LOGERR_TAG 56 const char *log_tag; 57 #endif 58 #endif 59 }; 60 61 static struct logctx _logctx = { 62 /* syslog style, but without the hostname or tag. */ 63 .log_opts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID, 64 }; 65 66 #if defined(LOGERR_TAG) && defined(__linux__) 67 /* Poor man's getprogname(3). */ 68 static char *_logprog; 69 static const char * 70 getprogname(void) 71 { 72 const char *p; 73 74 /* Use PATH_MAX + 1 to avoid truncation. */ 75 if (_logprog == NULL) { 76 /* readlink(2) does not append a NULL byte, 77 * so zero the buffer. */ 78 if ((_logprog = calloc(1, PATH_MAX + 1)) == NULL) 79 return NULL; 80 } 81 if (readlink("/proc/self/exe", _logprog, PATH_MAX + 1) == -1) 82 return NULL; 83 if (_logprog[0] == '[') 84 return NULL; 85 p = strrchr(_logprog, '/'); 86 if (p == NULL) 87 return _logprog; 88 return p + 1; 89 } 90 #endif 91 92 #ifndef SMALL 93 /* Write the time, syslog style. month day time - */ 94 static void 95 logprintdate(FILE *stream) 96 { 97 struct timeval tv; 98 time_t now; 99 struct tm tmnow; 100 char buf[32]; 101 102 if (gettimeofday(&tv, NULL) == -1) 103 return; 104 105 now = tv.tv_sec; 106 tzset(); 107 localtime_r(&now, &tmnow); 108 strftime(buf, sizeof(buf), "%b %d %T ", &tmnow); 109 fprintf(stream, "%s", buf); 110 } 111 #endif 112 113 __printflike(3, 0) static void 114 vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args) 115 { 116 va_list a; 117 #ifndef SMALL 118 bool log_pid; 119 #ifdef LOGERR_TAG 120 bool log_tag; 121 #endif 122 123 if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) || 124 (stream != stderr && ctx->log_opts & LOGERR_LOG_DATE)) 125 logprintdate(stream); 126 127 #ifdef LOGERR_TAG 128 log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) || 129 (stream != stderr && ctx->log_opts & LOGERR_LOG_TAG)); 130 if (log_tag) { 131 if (ctx->log_tag == NULL) 132 ctx->log_tag = getprogname(); 133 fprintf(stream, "%s", ctx->log_tag); 134 } 135 #endif 136 137 log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) || 138 (stream != stderr && ctx->log_opts & LOGERR_LOG_PID)); 139 if (log_pid) 140 fprintf(stream, "[%d]", getpid()); 141 142 #ifdef LOGERR_TAG 143 if (log_tag || log_pid) 144 #else 145 if (log_pid) 146 #endif 147 fprintf(stream, ": "); 148 #else 149 UNUSED(ctx); 150 #endif 151 152 va_copy(a, args); 153 vfprintf(stream, fmt, a); 154 fputc('\n', stream); 155 va_end(a); 156 } 157 158 /* 159 * NetBSD's gcc has been modified to check for the non standard %m in printf 160 * like functions and warn noisily about it that they should be marked as 161 * syslog like instead. 162 * This is all well and good, but our logger also goes via vfprintf and 163 * when marked as a sysloglike funcion, gcc will then warn us that the 164 * function should be printflike instead! 165 * This creates an infinte loop of gcc warnings. 166 * Until NetBSD solves this issue, we have to disable a gcc diagnostic 167 * for our fully standards compliant code in the logger function. 168 */ 169 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)) 170 #pragma GCC diagnostic push 171 #pragma GCC diagnostic ignored "-Wmissing-format-attribute" 172 #endif 173 __printflike(2, 0) static void 174 vlogmessage(int pri, const char *fmt, va_list args) 175 { 176 struct logctx *ctx = &_logctx; 177 178 if (ctx->log_opts & LOGERR_ERR && 179 (pri <= LOG_ERR || 180 (!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) || 181 (ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG))) 182 vlogprintf_r(ctx, stderr, fmt, args); 183 184 if (!(ctx->log_opts & LOGERR_LOG)) 185 return; 186 187 #ifdef SMALL 188 vsyslog(pri, fmt, args); 189 #else 190 if (ctx->log_file == NULL) { 191 vsyslog(pri, fmt, args); 192 return; 193 } 194 if (pri == LOG_DEBUG && !(ctx->log_opts & LOGERR_DEBUG)) 195 return; 196 vlogprintf_r(ctx, ctx->log_file, fmt, args); 197 #endif 198 } 199 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5)) 200 #pragma GCC diagnostic pop 201 #endif 202 203 __printflike(2, 3) static void 204 logmessage(int pri, const char *fmt, ...) 205 { 206 va_list args; 207 208 va_start(args, fmt); 209 vlogmessage(pri, fmt, args); 210 va_end(args); 211 } 212 213 __printflike(2, 0) static void 214 vlogerrmessage(int pri, const char *fmt, va_list args) 215 { 216 int _errno = errno; 217 char buf[1024]; 218 219 vsnprintf(buf, sizeof(buf), fmt, args); 220 logmessage(pri, "%s: %s", buf, strerror(_errno)); 221 } 222 223 void 224 logdebug(const char *fmt, ...) 225 { 226 va_list args; 227 228 va_start(args, fmt); 229 vlogerrmessage(LOG_DEBUG, fmt, args); 230 va_end(args); 231 } 232 233 void 234 logdebugx(const char *fmt, ...) 235 { 236 va_list args; 237 238 va_start(args, fmt); 239 vlogmessage(LOG_DEBUG, fmt, args); 240 va_end(args); 241 } 242 243 void 244 loginfo(const char *fmt, ...) 245 { 246 va_list args; 247 248 va_start(args, fmt); 249 vlogerrmessage(LOG_INFO, fmt, args); 250 va_end(args); 251 } 252 253 void 254 loginfox(const char *fmt, ...) 255 { 256 va_list args; 257 258 va_start(args, fmt); 259 vlogmessage(LOG_INFO, fmt, args); 260 va_end(args); 261 } 262 263 void 264 logwarn(const char *fmt, ...) 265 { 266 va_list args; 267 268 va_start(args, fmt); 269 vlogerrmessage(LOG_WARNING, fmt, args); 270 va_end(args); 271 } 272 273 void 274 logwarnx(const char *fmt, ...) 275 { 276 va_list args; 277 278 va_start(args, fmt); 279 vlogmessage(LOG_WARNING, fmt, args); 280 va_end(args); 281 } 282 283 void 284 logerr(const char *fmt, ...) 285 { 286 va_list args; 287 288 va_start(args, fmt); 289 vlogerrmessage(LOG_ERR, fmt, args); 290 va_end(args); 291 } 292 293 void 294 logerrx(const char *fmt, ...) 295 { 296 va_list args; 297 298 va_start(args, fmt); 299 vlogmessage(LOG_ERR, fmt, args); 300 va_end(args); 301 } 302 303 void 304 logsetopts(unsigned int opts) 305 { 306 struct logctx *ctx = &_logctx; 307 308 ctx->log_opts = opts; 309 setlogmask(LOG_UPTO(opts & LOGERR_DEBUG ? LOG_DEBUG : LOG_INFO)); 310 } 311 312 #ifdef LOGERR_TAG 313 void 314 logsettag(const char *tag) 315 { 316 #if !defined(SMALL) 317 struct logctx *ctx = &_logctx; 318 319 ctx->log_tag = tag; 320 #else 321 UNUSED(tag); 322 #endif 323 } 324 #endif 325 326 int 327 logopen(const char *path) 328 { 329 struct logctx *ctx = &_logctx; 330 331 if (path == NULL) { 332 int opts = 0; 333 334 if (ctx->log_opts & LOGERR_LOG_PID) 335 opts |= LOG_PID; 336 openlog(NULL, opts, LOGERR_SYSLOG_FACILITY); 337 return 1; 338 } 339 340 #ifndef SMALL 341 if ((ctx->log_file = fopen(path, "a")) == NULL) 342 return -1; 343 setlinebuf(ctx->log_file); 344 return fileno(ctx->log_file); 345 #else 346 errno = ENOTSUP; 347 return -1; 348 #endif 349 } 350 351 void 352 logclose(void) 353 { 354 #ifndef SMALL 355 struct logctx *ctx = &_logctx; 356 #endif 357 358 closelog(); 359 #ifndef SMALL 360 if (ctx->log_file == NULL) 361 return; 362 fclose(ctx->log_file); 363 ctx->log_file = NULL; 364 #endif 365 #if defined(LOGERR_TAG) && defined(__linux__) 366 free(_logprog); 367 #endif 368 } 369