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

Reply via email to