/* log.c - logging funtions Copyright (C) 2002, 2003, 2008, 2010, 2011, 2012, 2013 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #define MAX_REQUESTID_LENGTH 40 #ifdef TLS /* the session id that is set for this thread */ static TLS char *sessionid = NULL; /* the request identifier that is set for this thread */ static TLS char *requestid = NULL; #else /* no TLS, use pthreads */ static pthread_once_t tls_init_once = PTHREAD_ONCE_INIT; static pthread_key_t sessionid_key; static pthread_key_t requestid_key; static void tls_init_keys(void) { pthread_key_create(&sessionid_key, NULL); pthread_key_create(&requestid_key, NULL); } #endif /* no TLS, use pthreads */ static char *loglevel2sd(int pri) { switch(pri) { case LOG_EMERG: return SD_EMERG; case LOG_ALERT: return SD_ALERT; case LOG_CRIT: return SD_CRIT; case LOG_ERR: return SD_ERR; case LOG_WARNING: return SD_WARNING; case LOG_NOTICE: return SD_NOTICE; case LOG_INFO: return SD_INFO; case LOG_DEBUG: return SD_DEBUG; } return ""; } /* indicate that we should clear any session identifiers set by log_newsession */ void log_clearsession(void) { #ifndef TLS char *sessionid, *requestid; pthread_once(&tls_init_once, tls_init_keys); sessionid = pthread_getspecific(sessionid_key); requestid = pthread_getspecific(requestid_key); #endif /* no TLS */ /* set the session id to empty */ if (sessionid != NULL) sessionid[0] = '\0'; /* set the request id to empty */ if (requestid != NULL) requestid[0] = '\0'; } /* indicate that a session id should be included in the output and set it to a new value */ void log_newsession(void) { #ifndef TLS char *sessionid, *requestid; pthread_once(&tls_init_once, tls_init_keys); sessionid = pthread_getspecific(sessionid_key); requestid = pthread_getspecific(requestid_key); #endif /* no TLS */ /* ensure that sessionid can hold a string */ if (sessionid == NULL) { sessionid = (char *)malloc(7); if (sessionid == NULL) { fprintf(stderr, SD_ERR "malloc() failed: %s", strerror(errno)); return; /* silently fail */ } #ifndef TLS pthread_setspecific(sessionid_key, sessionid); #endif /* no TLS */ } sprintf(sessionid, "%06x", (int)(rand() & 0xffffff)); /* set the request id to empty */ if (requestid != NULL) requestid[0] = '\0'; } /* indicate that a request identifier should be included in the output from this point on, until log_newsession() is called */ void log_setrequest(const char *format, ...) { va_list ap; #ifndef TLS char *requestid; pthread_once(&tls_init_once, tls_init_keys); requestid = pthread_getspecific(requestid_key); #endif /* no TLS */ /* ensure that requestid can hold a string */ if (requestid == NULL) { requestid = (char *)malloc(MAX_REQUESTID_LENGTH); if (requestid == NULL) { fprintf(stderr, SD_ERR "malloc() failed: %s", strerror(errno)); return; /* silently fail */ } #ifndef TLS pthread_setspecific(requestid_key, requestid); #endif /* no TLS */ } /* make the message */ va_start(ap, format); vsnprintf(requestid, MAX_REQUESTID_LENGTH, format, ap); requestid[MAX_REQUESTID_LENGTH - 1] = '\0'; va_end(ap); } /* log the given message using the configured logging method */ void log_log(int pri, const char *format, ...) { char *msg = NULL; char *tmp = NULL; va_list ap; #ifndef TLS char *sessionid, *requestid; pthread_once(&tls_init_once, tls_init_keys); sessionid = pthread_getspecific(sessionid_key); requestid = pthread_getspecific(requestid_key); #endif /* no TLS */ /* make the message */ va_start(ap, format); if (vasprintf(&msg, format, ap) < 0) { fprintf(stderr, SD_ERR "vasprintf() failed: %s", strerror(errno)); return; } va_end(ap); if (errno != 0) { if (asprintf(&tmp, "%s: %s", msg, strerror(errno)) < 0) { fprintf(stderr, SD_ERR "asprintf() failed: %s", strerror(errno)); } else { free(msg); msg = tmp; errno = 0; } } /* do the logging */ if ((requestid != NULL) && (requestid[0] != '\0')) fprintf(stderr, "%s[sess:%s] %s\n", loglevel2sd(pri), sessionid, requestid, msg); else if ((sessionid != NULL) && (sessionid[0] != '\0')) fprintf(stderr, "%s[sess:%s] %s\n", loglevel2sd(pri), sessionid, msg); else fprintf(stderr, "%s%s\n", loglevel2sd(pri), msg); fflush(stderr); free(msg); }