From: Daniel Wagner <[email protected]>
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
[email protected]
http://lists.connman.net/listinfo/connman