From: Daniel Wagner <daniel.wag...@bmw-carit.de> When using syslog() directly we are in danger to deadlock when syslog() is already been called by normal code and the signal kicks in. With using a socket to "/dev/log" we are safe even in the signal handler to log messages. --- src/log.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 143 insertions(+), 10 deletions(-)
diff --git a/src/log.c b/src/log.c index 04e61f3..63f1df3 100644 --- a/src/log.c +++ b/src/log.c @@ -32,12 +32,92 @@ #include <syslog.h> #include <execinfo.h> #include <dlfcn.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> #include "connman.h" static const char *program_exec; static const char *program_path; +static int log_option; +static int syslog_fd; + +static char *program_str; +static char *pid_str; +static const char * const level_str[] = { "<0>", "<1>", "<2>", "<3>", + "<4>", "<5>", "<6>", "<7>" }; + +#define IOVEC_SET_STRING(i, s) \ + do { \ + struct iovec *_i = &(i); \ + char *_s = (char *)(s); \ + _i->iov_base = _s; \ + _i->iov_len = strlen(_s); \ + } while (0); + +static int write_to_syslog(unsigned int level, const char *buffer) +{ + struct iovec iovec[4]; + struct msghdr msghdr; + + if (level > LOG_DEBUG) + level = LOG_DEBUG; + + memset(iovec, 0, sizeof(iovec)); + IOVEC_SET_STRING(iovec[0], level_str[level]); + IOVEC_SET_STRING(iovec[1], program_str); + IOVEC_SET_STRING(iovec[2], pid_str); + IOVEC_SET_STRING(iovec[3], buffer); + + memset(&msghdr, 0, sizeof(msghdr)); + msghdr.msg_iov = iovec; + msghdr.msg_iovlen = 4; + + if (writev(syslog_fd, iovec, 4) < 0) + return -errno; + + return 0; +} + +static int write_to_console(const char *buffer) +{ + struct iovec iovec[4]; + struct msghdr msghdr; + + memset(iovec, 0, sizeof(iovec)); + IOVEC_SET_STRING(iovec[0], program_str); + IOVEC_SET_STRING(iovec[1], pid_str); + IOVEC_SET_STRING(iovec[2], buffer); + IOVEC_SET_STRING(iovec[3], "\n"); + + memset(&msghdr, 0, sizeof(msghdr)); + msghdr.msg_iov = iovec; + msghdr.msg_iovlen = 4; + + if (writev(STDERR_FILENO, iovec, 4) < 0) + return -errno; + + return 0; +} + +static void log_dispatch(unsigned int level, const char *format, va_list ap) +{ + char name[16], pid[16]; + char *buffer; + int len; + + len = vasprintf(&buffer, format, ap); + + write_to_syslog(level, buffer); + + if ((log_option & LOG_PERROR) == LOG_PERROR) + write_to_console(buffer); + + free(buffer); +} + /** * connman_info: * @format: format string @@ -51,7 +131,7 @@ void connman_info(const char *format, ...) va_start(ap, format); - vsyslog(LOG_INFO, format, ap); + log_dispatch(LOG_INFO, format, ap); va_end(ap); } @@ -69,7 +149,7 @@ void connman_warn(const char *format, ...) va_start(ap, format); - vsyslog(LOG_WARNING, format, ap); + log_dispatch(LOG_WARNING, format, ap); va_end(ap); } @@ -87,7 +167,7 @@ void connman_error(const char *format, ...) va_start(ap, format); - vsyslog(LOG_ERR, format, ap); + log_dispatch(LOG_ERR, format, ap); va_end(ap); } @@ -105,7 +185,7 @@ void connman_debug(const char *format, ...) va_start(ap, format); - vsyslog(LOG_DEBUG, format, ap); + log_dispatch(LOG_DEBUG, format, ap); va_end(ap); } @@ -238,6 +318,37 @@ static void signal_setup(sighandler_t handler) sigaction(SIGPIPE, &sa, NULL); } +static int open_syslog(void) +{ + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + + memset(&sa, 0, sizeof(sa)); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); + + syslog_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (syslog_fd < 0) + return -errno; + + if (connect(syslog_fd, &sa.sa, sizeof(sa)) < 0) { + close(syslog_fd); + return -errno; + } + + return 0; +} + +static void close_syslog(void) +{ + if (syslog_fd <= 0) + return; + + close(syslog_fd); +} + extern struct connman_debug_desc __start___debug[]; extern struct connman_debug_desc __stop___debug[]; @@ -320,6 +431,7 @@ int __connman_log_init(const char *program, const char *debug, { static char path[PATH_MAX]; int option = LOG_NDELAY | LOG_PID; + int err; program_exec = program; program_path = getcwd(path, sizeof(path)); @@ -329,25 +441,46 @@ int __connman_log_init(const char *program, const char *debug, __connman_log_enable(__start___debug, __stop___debug); + if (asprintf(&program_str, "%s", basename(program_exec)) < 0) { + err = -errno; + goto err; + } + + if (asprintf(&pid_str, "[%lu]: ", (unsigned long)getpid()) < 0) { + err = -errno; + goto err; + } + + err = open_syslog(); + if (err < 0) + goto err; + if (detach == FALSE) - option |= LOG_PERROR; + log_option |= LOG_PERROR; signal_setup(signal_handler); - openlog(basename(program), option, LOG_DAEMON); - - syslog(LOG_INFO, "Connection Manager version %s", VERSION); + connman_info("Connection Manager version %s", VERSION); return 0; + +err: + g_free(program_str); + g_free(pid_str); + + return err; } void __connman_log_cleanup(void) { - syslog(LOG_INFO, "Exit"); + connman_info("Exit"); - closelog(); + close_syslog(); signal_setup(SIG_DFL); + g_free(program_str); + g_free(pid_str); + g_strfreev(enabled); } -- 1.7.7 _______________________________________________ connman mailing list connman@connman.net http://lists.connman.net/listinfo/connman