Add a pipe-based userspace logging backend. 38/67438/26
authorKazimierz Krosman <k.krosman@samsung.com>
Mon, 30 May 2016 15:05:54 +0000 (17:05 +0200)
committerMichal Bloch <m.bloch@samsung.com>
Wed, 1 Jun 2016 20:13:35 +0000 (22:13 +0200)
Change-Id: I1dca48f71ecf637c71f651f511ceca8d99ca16d1
Signed-off-by: Kazimierz Krosman <k.krosman@samsung.com>
Signed-off-by: Michal Bloch <m.bloch@samsung.com>
13 files changed:
Makefile.am
configs/dlog-pipe.conf [new file with mode: 0644]
configs/dlog.conf.pipe [new file with mode: 0644]
configure.ac
include/logcommon.h
include/logpipe.h [new file with mode: 0644]
packaging/dlog.spec
packaging/dlog_logger.path.pipe [new file with mode: 0644]
src/libdlog/log_pipe.c [new file with mode: 0644]
src/logger/logger_pipe.c [new file with mode: 0644]
src/logutil/logutil_pipe.c [new file with mode: 0644]
src/shared/logcommon.c
src/shared/logconfig.c

index 5b85867..7c9a423 100755 (executable)
@@ -26,6 +26,11 @@ libdlog_la_SOURCES =  \
        src/libdlog/loglimiter.c \
        include/loglimiter.h
 
+if WITH_PIPE
+libdlog_la_SOURCES += \
+       src/libdlog/log_pipe.c
+endif
+
 if WITH_ANDROID_LOGGER
 libdlog_la_SOURCES += \
        src/libdlog/log_android.c
@@ -89,6 +94,10 @@ if WITH_SYSTEMD_JOURNAL
 dlogutil_SOURCES += src/logutil/logutil_journal.c
 endif
 
+if WITH_PIPE
+dlogutil_SOURCES += src/logutil/logutil_pipe.c
+endif
+
 if !WITH_SYSTEMD_JOURNAL
 
 bin_PROGRAMS += dlog_logger
@@ -106,7 +115,6 @@ dlog_logger_SOURCES = \
        src/shared/logcommon.c \
        src/shared/logconfig.c \
        src/shared/logprint.c \
-       src/logger/logger.c \
        src/shared/queued_entry.c \
        src/shared/log_file.c \
        include/dlog_ioctl.h \
@@ -115,6 +123,12 @@ dlog_logger_SOURCES = \
        include/log_file.h \
        include/logprint.h
 
+if WITH_PIPE
+dlog_logger_SOURCES += src/logger/logger_pipe.c
+else
+dlog_logger_SOURCES += src/logger/logger.c
+endif
+
 endif
 
 if WITH_KMSG
@@ -167,9 +181,18 @@ if WITH_SYSTEMD_JOURNAL
 dlog.conf: configs/dlog.conf.journal
        cp configs/dlog.conf.journal dlog.conf
 endif
+if WITH_PIPE
+dlog.conf: configs/dlog.conf.pipe
+       cp configs/dlog.conf.pipe dlog.conf
+endif
 
 optetcdir = /opt/etc
 optetc_DATA = dlog.conf
 
+if WITH_PIPE
+usrlibtmpfilesddir = /usr/lib/tmpfiles.d
+usrlibtmpfilesd_DATA = configs/dlog-pipe.conf
+endif
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = dlog.pc
diff --git a/configs/dlog-pipe.conf b/configs/dlog-pipe.conf
new file mode 100644 (file)
index 0000000..4a9a528
--- /dev/null
@@ -0,0 +1 @@
+d      /run/dlog       0755    log     log     -       -
diff --git a/configs/dlog.conf.pipe b/configs/dlog.conf.pipe
new file mode 100644 (file)
index 0000000..dac7049
--- /dev/null
@@ -0,0 +1,15 @@
+plog=1
+limiter=0
+limiter|*|*=allow
+pipe_control_socket=/run/dlog/control.sock
+main=/run/dlog/log_main.sock
+apps=/run/dlog/log_apps.sock
+system=/run/dlog/log_system.sock
+radio=/run/dlog/log_radio.sock
+main_size=1048576
+apps_size=1048576
+system_size=1048576
+radio_size=1048576
+dlog_logger_conf_0=dlogutil -b system -r 5120 -n 1 -f /var/log/dlog/system -v kerneltime *:I
+dlog_logger_conf_1=dlogutil -b main -r 3072 -n 1 -f /var/log/dlog/main -v kerneltime *:W
+dlog_logger_conf_2=dlogutil -b radio -r 1024 -n 1 -f /var/log/dlog/radio -v kerneltime
index 921d80b..a515659 100755 (executable)
@@ -21,6 +21,10 @@ AC_ARG_ENABLE(journal, AS_HELP_STRING([--enable-journal], [enable systemd journa
     with_systemd_journal=no)
 
 #check for backends
+AC_ARG_ENABLE(pipe, AS_HELP_STRING([--enable-pipe], [enable pipe]),
+    [with_pipe=yes],
+    with_pipe=no)
+
 AC_ARG_ENABLE(kmsg, AS_HELP_STRING([--enable-kmsg], [enable kmsg]),
     [with_kmsg=yes],
     with_kmsg=no)
@@ -40,6 +44,7 @@ if test "x$with_systemd_journal" != "xno"; then
 fi
 AM_CONDITIONAL(HAVE_SYSTEMD_JOURNAL, [test "x$have_systemd_journal" = "xyes"])
 AM_CONDITIONAL(WITH_SYSTEMD_JOURNAL, [test "x$with_systemd_journal" = "xyes"])
+AM_CONDITIONAL(WITH_PIPE, [test "x$with_pipe" = "xyes"])
 AM_CONDITIONAL(WITH_ANDROID_LOGGER, [test "x$with_android_logger" = "xyes"])
 AM_CONDITIONAL(WITH_KMSG, [test "x$with_kmsg" = "xyes"])
 
index a80166f..21d66e3 100644 (file)
@@ -44,5 +44,7 @@
 
 char * log_name_by_id (log_id_t id);
 log_id_t log_id_by_name (const char *name);
+
 void syslog_critical_failure (const char * message);
+int recv_file_descriptor(int socket);
 #endif /* _LOGCOMMON_H */
diff --git a/include/logpipe.h b/include/logpipe.h
new file mode 100644 (file)
index 0000000..ba18f51
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * DLOG
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGPIPE_H
+#define _LOGPIPE_H
+
+#include <logcommon.h>
+
+#define DLOG_CTRL_REQ_PIPE {sizeof(struct dlog_control_msg), DLOG_REQ_PIPE, 0}
+
+enum {
+       DLOG_REQ_PIPE=1,
+       DLOG_REQ_BUFFER_SIZE,
+       DLOG_REQ_CLEAR,
+       DLOG_REQ_HANDLE_LOGUTIL,
+       DLOG_REQ_REMOVE_WRITER,
+       DLOG_REQ_MAX
+};
+
+enum {
+       DLOG_FLAG_ANSW  = 0x01,
+       DLOG_FLAG_WRITE = 0x02,
+       DLOG_FLAG_READ  = 0x04
+};
+
+struct dlog_control_msg {
+       char length;
+       char request;
+       char flags;
+       char data[0];
+};
+
+struct log_buffer_desc {
+       const char* path;
+       const int   size;
+       const int   flag;
+};
+
+struct dlog_crtl_file {
+       unsigned char  buf_id;
+       unsigned short rot_size;
+       unsigned short max_size;
+       char           path[0];
+} __attribute__ ((packed));
+
+
+#endif
index b844ecc..3f388f8 100755 (executable)
@@ -10,27 +10,31 @@ Source102:  packaging/libdlog.manifest
 Source301:  packaging/dlog_logger.service
 Source302:  packaging/dlog_logger.path.kmsg
 Source303:  packaging/dlog_logger.path.logger
+Source304:  packaging/dlog_logger.path.pipe
 Source401:  packaging/dloginit.service
 Source501:  packaging/01-dlog.rules.kmsg
 Source502:  packaging/01-dlog.rules.logger
 
 # Choose dlog backend log device
-# Warning : MUST be only one "ON" in below three switches
+# Warning : MUST be only one "ON" in below four switches
 %define backend_journal        ON
 %define backend_kmsg   OFF
 %define backend_logger OFF
+%define backend_pipe   OFF
 
 # Do NOT touch switches below
 %if "%{?tizen_target_name}" == "TM1" || "%{?tizen_target_name}" == "hawkp"
 %define backend_journal        OFF
 %define backend_kmsg   ON
 %define backend_logger OFF
+%define backend_pipe   OFF
 %endif
 
 %if "%{?profile}" == "wearable" || "%{?_with_emulator}" == "1"
 %define backend_journal        OFF
 %define backend_kmsg   OFF
 %define backend_logger ON
+%define backend_pipe   OFF
 %endif
 
 BuildRequires: autoconf
@@ -89,6 +93,9 @@ cp %{SOURCE102} .
                %if %{?backend_journal} == ON
                        --enable-journal \
                %endif
+               %if %{?backend_pipe} == ON
+                       --enable-pipe \
+               %endif
                %if %{?backend_kmsg} == ON
                        --enable-kmsg \
                %endif
@@ -108,6 +115,9 @@ make %{?jobs:-j%jobs} \
 %if %{?backend_logger} == ON
        CFLAGS+=-DDLOG_BACKEND_LOGGER
 %endif
+%if %{?backend_pipe} == ON
+       CFLAGS+=-DDLOG_BACKEND_PIPE
+%endif
 
 
 %install
@@ -125,11 +135,16 @@ install -m 0644 %SOURCE301 %{buildroot}%{_unitdir}
 
 %if %{?backend_kmsg} == ON
 install -m 0644 %SOURCE302 %{buildroot}%{_unitdir}/dlog_logger.path
-%else
-install -m 0644 %SOURCE303 %{buildroot}%{_unitdir}/dlog_logger.path
+ln -s ../dlog_logger.path %{buildroot}%{_unitdir}/multi-user.target.wants/dlog_logger.path
 %endif
-
+%if %{?backend_logger} == ON
+install -m 0644 %SOURCE303 %{buildroot}%{_unitdir}/dlog_logger.path
 ln -s ../dlog_logger.path %{buildroot}%{_unitdir}/multi-user.target.wants/dlog_logger.path
+%endif
+%if %{?backend_pipe} == ON
+install -m 0644 %SOURCE304 %{buildroot}%{_unitdir}/dlog_logger.path
+ln -s ../dlog_logger.service %{buildroot}%{_unitdir}/multi-user.target.wants/dlog_logger.service
+%endif
 
 %endif
 
@@ -178,12 +193,19 @@ systemctl daemon-reload
 %attr(755,log,log) %{_bindir}/dlogctrl
 %attr(755,log,log) /var/log/dlog
 %if %{?backend_journal} == OFF
-%{_udevrulesdir}/01-dlog.rules
 %attr(750,log,log) %{_bindir}/dlog_logger
 %{_unitdir}/dlog_logger.service
 %{_unitdir}/dlog_logger.path
+%if  %{?backend_pipe} == OFF
+%{_udevrulesdir}/01-dlog.rules
 %{_unitdir}/multi-user.target.wants/dlog_logger.path
 %endif
+%endif
+
+%if %{?backend_pipe} == ON
+%{_unitdir}/multi-user.target.wants/dlog_logger.service
+%endif
+
 
 %files  -n libdlog
 %manifest libdlog.manifest
@@ -197,6 +219,9 @@ systemctl daemon-reload
 %attr(-,log,log) %{_unitdir}/dloginit.service
 %{_unitdir}/sysinit.target.wants/dloginit.service
 %endif
+%if %{?backend_pipe} == ON
+%attr(664,log,log) /usr/lib/tmpfiles.d/dlog-pipe.conf
+%endif
 
 %files -n libdlog-devel
 %{_includedir}/dlog/dlog.h
diff --git a/packaging/dlog_logger.path.pipe b/packaging/dlog_logger.path.pipe
new file mode 100644 (file)
index 0000000..9585fc9
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=Path activation for dlog_logger
+
+[Path]
+PathExists=/opt/etc/dlog.conf
diff --git a/src/libdlog/log_pipe.c b/src/libdlog/log_pipe.c
new file mode 100644 (file)
index 0000000..954920f
--- /dev/null
@@ -0,0 +1,141 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <dlog.h>
+#include <logpipe.h>
+#include <logprint.h>
+#include <logconfig.h>
+#include <logcommon.h>
+
+extern int (*write_to_log)(log_id_t, log_priority, const char *tag, const char *msg);
+extern pthread_mutex_t log_init_lock;
+static int pipe_fd = -1;
+static char log_pipe_path [PATH_MAX];
+
+
+static int connect_pipe(const char * path)
+{
+       int r;
+       int fd;
+       struct sockaddr_un sa = { .sun_family = AF_UNIX };
+       struct dlog_control_msg ctrl_msg = DLOG_CTRL_REQ_PIPE;
+
+       fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               return -errno;
+
+       strncpy(sa.sun_path, path, sizeof (sa.sun_path));
+
+       r = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
+       if (r < 0) {
+               close (fd);
+               return -errno;
+       }
+
+       r = write (fd, &ctrl_msg, ctrl_msg.length);
+       if (r < 0) {
+               close (fd);
+               return -errno;
+       }
+       fd = recv_file_descriptor(fd);
+       return fd;
+}
+
+
+static int __write_to_log_pipe(log_id_t log_id, log_priority prio, const char *tag, const char *msg)
+{
+       ssize_t ret;
+       char buf [LOG_MAX_SIZE];
+       struct logger_entry* le = (struct logger_entry*)buf;
+       struct timespec ts;
+       int len;
+       int tag_l;
+       int msg_l;
+
+       if (log_id >= LOG_ID_MAX
+       || prio < DLOG_VERBOSE
+       || prio >= DLOG_PRIO_MAX
+       || !msg)
+               return DLOG_ERROR_INVALID_PARAMETER;
+
+       if (!tag)
+               tag = "";
+
+       tag_l = strlen(tag) + 1;
+       msg_l = strlen(msg) + 1;
+       len = 1 + msg_l + tag_l + sizeof(struct logger_entry);
+       if (len > LOG_MAX_SIZE)
+               return DLOG_ERROR_INVALID_PARAMETER;
+
+       clock_gettime (CLOCK_MONOTONIC, &ts);
+       le->sec = ts.tv_sec;
+       le->nsec = ts.tv_nsec;
+       le->buf_id = log_id;
+       le->len = len;
+       le->pid = getpid();
+       le->tid = gettid();
+       le->msg[0] = prio;
+
+       memcpy(le->msg + 1, tag, tag_l);
+       memcpy(le->msg + 1 + tag_l, msg, msg_l);
+
+       if (le->len % 2)
+               le->len += 1;
+       ret = write (pipe_fd, buf, le->len);
+       if (ret < 0 && errno == EPIPE) {
+               pthread_mutex_lock(&log_init_lock);
+               close (pipe_fd);
+               pipe_fd = connect_pipe(log_pipe_path);
+               pthread_mutex_unlock(&log_init_lock);
+               ret = write (pipe_fd, buf, le->len);
+       }
+       return ret;
+}
+
+void __dlog_init_backend()
+{
+       const char * pipe_path_temp;
+       struct log_config conf;
+
+       /*
+        * We mask SIGPIPE signal because most applications do not install their
+        * own SIGPIPE handler. Default behaviour in SIGPIPE case is to abort the
+        * process. SIGPIPE occurs when e.g. dlog daemon closes read pipe endpoint
+        */
+       signal(SIGPIPE, SIG_IGN);
+
+       log_config_read (&conf);
+       pipe_path_temp = log_config_get(&conf, "pipe_control_socket");
+       if (!pipe_path_temp) {
+               syslog_critical_failure ("THERE IS NO PATH TO pipe_control_socket IN THE CONFIG!");
+               return;
+       }
+       sprintf(log_pipe_path, "%s", pipe_path_temp);
+       log_config_free (&conf);
+
+       pipe_fd = connect_pipe(log_pipe_path);
+       write_to_log = __write_to_log_pipe;
+}
diff --git a/src/logger/logger_pipe.c b/src/logger/logger_pipe.c
new file mode 100644 (file)
index 0000000..681aae0
--- /dev/null
@@ -0,0 +1,1203 @@
+/*
+ * Copyright (c) 2016, Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <linux/limits.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <logpipe.h>
+#include <logprint.h>
+#include <log_file.h>
+#include <logconfig.h>
+
+#define LIST_FOREACH(head, i)                                  \
+       for((i) = (head); i; (i) = (i)->next)
+
+#define LIST_REMOVE(head, i, type)                                     \
+       {                                                                                               \
+       struct type* tmp;                                                               \
+       tmp = i->prev;                                                                  \
+       if (!tmp)                                                                               \
+               head = i->next;                                                         \
+       else                                                                                    \
+               tmp->next = i->next;                                            \
+                                                                                                       \
+       if ((i)->next)                                                                  \
+               (i)->next->prev = tmp;                                          \
+       }
+
+#define LIST_ADD(head, i)                                              \
+       {                                                                                       \
+               if (!(head))                                                    \
+               head = i;                                                               \
+       else {                                                                                  \
+               (i)->next = head;                                               \
+               head->prev = i;                                                 \
+               head = i;                                                               \
+       }                                                                                       \
+       }
+
+
+#define LIST_ITEM_FREE(i, type)                                                \
+       {                                                                                               \
+               struct type* tmp = i->next;                                     \
+               type##_free(i);                                                         \
+               i = tmp;                                                                        \
+       }
+
+#define PIPE_REQUESTED_SIZE (256*4096)
+#define LOG_CONTROL_SOCKET "/var/log/control.sock"
+#define FILE_PATH_SIZE (256)
+#define INTERVAL_MAX 3600
+#define BUFFER_MAX 65535
+#define MAX_CONNECTION_Q 100
+
+#define DELIMITER " "
+
+enum reader_type {
+       READER_SOCKET=1,
+       READER_FILE
+};
+
+enum entity_type {
+       ENTITY_WRITER = 1,
+       ENTITY_BUFFER,
+       ENTITY_CONTROL
+};
+
+enum writer_state {
+       WRITER_SOCKET = 0,
+       WRITER_PIPE
+};
+
+struct fd_entity {
+       int type;
+};
+
+struct writer;
+
+struct writer {
+       struct fd_entity   _entity;
+       int                socket_fd;
+       int                pipe_fd[2];
+       struct epoll_event event;
+       int                rights;
+
+       struct writer*     next;
+       struct writer*     prev;
+
+       int                readed;
+       char               state;
+       char               buffer[LOG_MAX_SIZE];
+};
+
+struct reader;
+
+struct reader {
+       enum reader_type   type;
+       struct log_file    file;
+       int                current;
+       int                buf_id;
+       int                dumpcount;
+       struct reader*     next;
+       struct reader*     prev;
+};
+
+struct log_buffer {
+       struct fd_entity   _entity;
+       int                listen_fd;
+       struct epoll_event event;
+
+       int                id;
+       int                size;
+       int                lines;
+       int                head;
+       int                tail;
+       int                not_empty;
+       int                buffered_len;
+       int                elapsed;
+       char               buffer[0];
+};
+
+struct control {
+       struct fd_entity   _entity;
+       int                fd;
+       struct epoll_event event;
+       char               path [PATH_MAX];
+};
+
+struct logger {
+       int                 epollfd;
+       struct control      control;
+       struct writer*      writers;
+       struct reader**     readers;
+       struct log_buffer** buffers;
+       int                 max_buffered_bytes;
+       int                 max_buffered_time;
+       int                 should_timeout;
+       log_format*         default_format;
+};
+
+static int listen_fd_create(const char* path)
+{
+       struct sockaddr_un server_addr;
+       int sd;
+
+       sd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (sd == -1)
+               return -errno;
+
+       memset(&server_addr, 0, sizeof(server_addr));
+       server_addr.sun_family = AF_UNIX;
+       strncpy(server_addr.sun_path, path, sizeof(server_addr.sun_path)-1);
+       unlink(server_addr.sun_path);
+
+       if (bind(sd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1)
+               goto failure;
+
+       if (listen(sd, MAX_CONNECTION_Q) < 0)
+               goto failure;
+
+       return sd;
+
+failure:
+       close (sd);
+       return -errno;
+}
+
+static int add_fd_loop(int epollfd, int fd, struct epoll_event* event)
+{
+       return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, event);
+}
+
+static int remove_fd_loop(int epollfd, int fd)
+{
+       return epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
+}
+
+static struct writer* writer_create(int fd)
+{
+       struct writer* w = calloc(1, sizeof(struct writer));
+
+       if (!w)
+               return w;
+
+       w->event.data.ptr = w;
+       w->event.events = EPOLLIN;
+       w->socket_fd = fd;
+       w->_entity.type = ENTITY_WRITER;
+       return w;
+}
+
+static void writer_free(struct writer* w)
+{
+       switch (w->state) {
+       case WRITER_SOCKET:
+               close (w->socket_fd);
+               break;
+       case WRITER_PIPE:
+               close (w->pipe_fd[0]);
+               break;
+       default:
+               break;
+       }
+       free(w);
+}
+
+static struct log_buffer* buffer_create(int buf_id, int size, const char * path)
+{
+       struct log_buffer* lb = calloc(1, sizeof(struct log_buffer) + size);
+
+       if (!lb)
+               return NULL;
+
+       if ((lb->listen_fd = listen_fd_create(path)) < 0)
+               goto err;
+
+       lb->event.data.ptr = lb;
+       lb->event.events = EPOLLIN;
+       lb->_entity.type = ENTITY_BUFFER;
+       lb->id = buf_id;
+       lb->size = size;
+
+       return lb;
+err:
+       free(lb);
+       return NULL;
+}
+
+static void buffer_free(struct log_buffer** buffer)
+{
+       close((*buffer)->listen_fd);
+       free(*buffer);
+       *buffer = NULL;
+}
+
+static void copy_from_buffer(void* dst, int from, int count, struct log_buffer* b)
+{
+       int size = b->size;
+       int s1 = (from + count) > size ? size - from : count;
+       int s2 = count - s1;
+
+       memcpy(dst, b->buffer + from, s1);
+       memcpy((char*)dst + s1, b->buffer , s2);
+}
+
+static void copy_to_buffer(const void* src, int from, int count, struct log_buffer* b)
+{
+       int size = b->size;
+       int s1 = (from + count) > size ? size - from : count;
+       int s2 = count - s1;
+
+       memcpy(b->buffer + from, src, s1);
+       memcpy(b->buffer, (char*)src + s1, s2);
+}
+
+static int buffer_free_space(struct log_buffer* b)
+{
+       int head = b->head;
+       int tail = b->tail;
+       int free_space = b->size;
+       if (head == tail && b->not_empty)
+               free_space = 0;
+       else if (head < tail)
+               free_space = b->size - (tail-head);
+       else if (head > tail)
+               free_space = head - tail;
+
+       return free_space;
+}
+
+static void buffer_append(const struct logger_entry* entry, struct log_buffer* b)
+{
+       while (buffer_free_space(b) < entry->len) {
+               struct logger_entry* t = (struct logger_entry*)(b->buffer + b->head);
+               b->head += t->len;
+               b->head %= b->size;
+               -- b->lines;
+       }
+
+       copy_to_buffer(entry, b->tail, entry->len, b);
+       b->tail += entry->len;
+       b->tail %= b->size;
+       b->buffered_len += entry->len;
+       b->not_empty = 1;
+       ++ b->lines;
+}
+
+static int print_out_logs(struct log_file* file, struct log_buffer* buffer, int from, int dumpcount)
+{
+       int r;
+       int written;
+       log_entry entry;
+       struct logger_entry* ple;
+       char tmp[LOG_MAX_SIZE];
+       int skip = (dumpcount > 0) * (buffer->lines - dumpcount);
+
+       while (from != buffer->tail) {
+               ple = (struct logger_entry*)(buffer->buffer + from);
+               if (ple->len + from >= buffer->size) {
+                       copy_from_buffer(tmp, from, ple->len, buffer);
+                       ple = (struct logger_entry*)tmp;
+               }
+
+               from += ple->len;
+               from %= buffer->size;
+               r = log_process_log_buffer(ple, &entry);
+               if (r)
+                       return r;
+
+               if (skip --> 0)
+                       continue;
+
+               if (!log_should_print_line(file->format, entry.tag, entry.priority))
+                       continue;
+
+               written = log_print_log_line(file->format, file->fd, &entry);
+               if (written < 0)
+                       return 1;
+
+               file->size += written;
+
+               if ((file->rotate_size_kbytes > 0) && ((file->size / 1024) >= file->rotate_size_kbytes))
+                       rotate_logs(file);
+       }
+       return 0;
+}
+
+static int send_pipe(int socket, int wr_pipe, int type)
+{
+       int r;
+       struct dlog_control_msg tmp =
+               {sizeof(struct dlog_control_msg), DLOG_REQ_PIPE, DLOG_FLAG_ANSW | type};
+       struct msghdr msg = { 0 };
+       char buf[CMSG_SPACE(sizeof(wr_pipe))];
+       memset(buf, '\0', sizeof(buf));
+       struct iovec io = { .iov_base = &tmp, .iov_len = sizeof(struct dlog_control_msg) };
+
+       msg.msg_iov = &io;
+       msg.msg_iovlen = 1;
+       msg.msg_control = buf;
+       msg.msg_controllen = sizeof(buf);
+
+       struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_RIGHTS;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(wr_pipe));
+
+       memcpy(CMSG_DATA(cmsg), &wr_pipe, sizeof(wr_pipe));
+
+       msg.msg_controllen = cmsg->cmsg_len;
+
+       if ((r = sendmsg(socket, &msg, 0)) < 0)
+               return r;
+
+       close (wr_pipe);
+       return 0;
+}
+
+static int send_data(int socket, struct dlog_control_msg* m, const void*  data, int len)
+{
+       int r;
+       struct msghdr msg = { 0 };
+
+       struct iovec io[2] = {
+               (struct iovec){
+                       .iov_base = m,
+                       .iov_len = sizeof(struct dlog_control_msg)
+               },
+               (struct iovec){
+                       .iov_base = (void*)data,
+                       .iov_len = len
+               }
+       };
+
+       msg.msg_iov = io;
+       msg.msg_iovlen = 2;
+
+       if ((r = sendmsg(socket, &msg, 0)) < 0)
+               return r;
+
+       return 0;
+}
+
+static void writer_close_fd(struct logger* server, struct writer* wr)
+{
+       switch (wr->state) {
+       case WRITER_SOCKET:
+               remove_fd_loop(server->epollfd, wr->socket_fd);
+               close (wr->socket_fd);
+               break;
+       case WRITER_PIPE:
+               remove_fd_loop(server->epollfd, wr->pipe_fd[0]);
+               close (wr->pipe_fd[0]);
+               break;
+       default:
+               break;
+       }
+}
+
+static void reader_free(struct reader* reader)
+{
+       if (reader->file.path)
+               free(reader->file.path);
+
+       close(reader->file.fd);
+       log_format_free(reader->file.format);
+       free(reader);
+}
+
+static int service_reader(struct logger* server, struct reader* reader)
+{
+       int buf_id = reader->buf_id;
+       struct log_buffer* buffer = server->buffers[buf_id];
+       int r = 0;
+
+       r = print_out_logs(&reader->file, buffer, reader->current, reader->dumpcount);
+       reader->current = buffer->tail;
+       return r;
+}
+
+static int parse_command_line(const char* cmdl, struct logger* server, int wr_socket_fd)
+{
+       char cmdline[512];
+       int option, argc;
+       char *argv [ARG_MAX];
+       char *tok;
+       char *tok_sv;
+       int silent = 0;
+       int retval = 0;
+       struct reader * reader;
+
+       if (!server || !cmdl) return EINVAL;
+
+       strncpy (cmdline, cmdl, 512);
+
+       reader = calloc(1, sizeof(struct reader));
+       if (!reader) return ENOMEM;
+
+       tok = strtok_r(cmdline, DELIMITER, &tok_sv);
+       if (!tok || strcmp(tok, "dlogutil")) return -1;
+
+       argc = 0;
+       while (tok && (argc < ARG_MAX)) {
+               argv[argc++] = strdup(tok);
+               tok = strtok_r(NULL, DELIMITER, &tok_sv);
+       }
+
+       reader->type = READER_FILE;
+       reader->file.fd = -1;
+       reader->file.path = NULL;
+       reader->file.format = log_format_from_format(server->default_format);
+       reader->file.rotate_size_kbytes = 0;
+       reader->file.max_rotated = 0;
+       reader->file.size = 0;
+       reader->buf_id = 0;
+       reader->dumpcount = 0;
+
+       while ((option = getopt(argc, argv, "cgsdt:f:r:n:v:b:")) != -1) {
+               switch (option) {
+               case 'c':
+               case 'g':
+                       // already handled in dlogutil
+                       break;
+               case 't':
+                       if (!optarg) {
+                               retval = -1;
+                               goto cleanup;
+                       }
+                       reader->dumpcount = atoi(optarg);
+                       break;
+               case 'd':
+                       if (!reader->dumpcount) reader->dumpcount = -1;
+                       break;
+               case 's':
+                       silent = 1;
+                       break;
+               case 'f':
+                       if (!optarg) {
+                               retval = -1;
+                               goto cleanup;
+                       }
+                       reader->file.path = strdup(optarg);
+                       break;
+               case 'b':
+                       if (!optarg) {
+                               retval = -1;
+                               goto cleanup;
+                       }
+                       reader->buf_id = log_id_by_name (optarg);
+                       break;
+               case 'r':
+                       if (!optarg || !isdigit(optarg[0])) {
+                               retval = -1;
+                               goto cleanup;
+                       }
+                       reader->file.rotate_size_kbytes = atoi(optarg);
+                       break;
+               case 'n':
+                       if (!optarg || !isdigit(optarg[0])) {
+                               retval = -1;
+                               goto cleanup;
+                       }
+                       reader->file.max_rotated = atoi(optarg);
+                       break;
+               case 'v':
+                       if (!optarg) {
+                               retval = -1;
+                               goto cleanup;
+                       } else {
+                               log_print_format print_format;
+                               print_format = log_format_from_string(optarg);
+                               if (print_format == FORMAT_OFF)
+                                       break;
+
+                               log_set_print_format(reader->file.format, print_format);
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       if ((reader->buf_id >= LOG_ID_MAX) || (reader->buf_id < 0)) {
+               retval = -1;
+               goto cleanup;
+       }
+
+       if (!silent)
+               if (argc != optind)
+                       while (optind < argc)
+                               log_add_filter_string(reader->file.format, argv[optind++]);
+               else
+                       log_add_filter_string(reader->file.format, "*:d");
+       else
+               log_add_filter_string(reader->file.format, "*:s");
+
+       if (reader->file.path != NULL) {
+               reader->file.fd = open (reader->file.path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+       } else {
+               reader->file.rotate_size_kbytes = 0;
+               reader->file.max_rotated = 0;
+               int fds[2];
+               if (!pipe2(fds, O_CLOEXEC)) {
+                       reader->file.fd = fds[1];
+                       send_pipe(wr_socket_fd, fds[0], DLOG_FLAG_READ);
+               }
+       }
+       if (reader->file.fd < 0) {
+               retval = -1;
+               goto cleanup;
+       }
+       reader->current = server->buffers[reader->buf_id]->head;
+       server->should_timeout |= (1 << reader->buf_id);
+       LIST_ADD(server->readers[reader->buf_id], reader);
+
+cleanup:
+
+       while (argc--)
+               free(argv[argc]);
+
+       /* recycle for further usage */
+       optarg = NULL;
+       optind = 0;
+       optopt = 0;
+
+       if (retval) reader_free (reader);
+
+       return retval;
+}
+
+static void fd_change_flag(int fd, int flag, int set)
+{
+       int flags = fcntl(fd, F_GETFL);
+
+       if (set)
+               flags |= flag;
+       else
+               flags &= ~flag;
+
+       fcntl(fd, F_SETFL, flags);
+}
+
+/*
+       The following handlers use the following return value convention:
+       * = 0 - success, but the reader connection needs to be closed (for example as a part of transition to another state).
+       * = 1 - success, connection needs to be kept.
+       * > 1 - failure of this particular request (value is +errno). Reader connection to be kept.
+       * < 0 - failure of connection (value is -errno). Reader needs to be closed.
+*/
+static int service_writer_handle_req_remove(struct logger* server, struct writer* wr, struct dlog_control_msg* msg)
+{
+       char * str = msg->data;
+       int buf_id;
+       struct reader * reader;
+
+       if (msg->length < (sizeof(struct dlog_control_msg)))
+               return EINVAL;
+
+       msg->data[msg->length - sizeof(struct dlog_control_msg) -1] = 0;
+       buf_id = log_id_by_name(str);
+       if (buf_id < 0 || buf_id >= LOG_ID_MAX)
+               return EINVAL;
+
+       str = msg->data + strlen(str) + 1;
+       LIST_FOREACH(server->readers[buf_id], reader) {
+               if (!str ? !reader->file.path : !strcmp(str, reader->file.path)) {
+                       LIST_REMOVE(server->readers[buf_id], reader, reader);
+                       LIST_ITEM_FREE(reader, reader);
+                       break;
+               }
+       }
+
+       if (wr->readed > msg->length) {
+               wr->readed -= msg->length;
+               memcpy(wr->buffer, wr->buffer + msg->length, wr->readed);
+       } else
+               wr->readed = 0;
+
+       return 1;
+}
+
+static int service_writer_handle_req_util(struct logger* server, struct writer* wr, struct dlog_control_msg* msg)
+{
+       int r;
+       if (msg->length <= sizeof(struct dlog_control_msg)
+               || msg->length >= LOG_MAX_SIZE-1)
+               return EINVAL;
+
+       msg->data[msg->length - sizeof(struct dlog_control_msg) -1] = 0;
+       r = parse_command_line(msg->data, server, wr->socket_fd);
+       if (r < 0)
+               return r;
+
+       if (wr->readed > msg->length) {
+               wr->readed -= msg->length;
+               memcpy(wr->buffer, wr->buffer + msg->length, wr->readed);
+       } else
+               wr->readed = 0;
+
+       return 1;
+}
+
+static int service_writer_handle_req_clear(struct logger* server, struct writer* wr, struct dlog_control_msg* msg)
+{
+       int buf_id;
+       struct reader* reader;
+       struct log_buffer* lb;
+
+       if (msg->length != (sizeof(struct dlog_control_msg) + 1))
+               return EINVAL;
+
+       buf_id = msg->data[0];
+       if (buf_id < 0 || buf_id >= LOG_ID_MAX)
+               return EINVAL;
+
+       lb = server->buffers[buf_id];
+       lb->head = lb->tail = lb->not_empty = 0;
+       lb->elapsed = lb->buffered_len = lb->lines = 0;
+
+       LIST_FOREACH(server->readers[buf_id], reader) {
+               reader->current = 0;
+       }
+
+       if (wr->readed > msg->length) {
+               wr->readed -= msg->length;
+               memcpy(wr->buffer, wr->buffer + msg->length, wr->readed);
+       } else
+               wr->readed = 0;
+
+       return 1;
+}
+
+static int service_writer_handle_req_size(struct logger* server, struct writer* wr, struct dlog_control_msg* msg)
+{
+       int r;
+       int buf_id;
+       if (msg->length != (sizeof(struct dlog_control_msg) + 1))
+               return EINVAL;
+
+       buf_id = msg->data[0];
+       if (buf_id < 0 || buf_id >= LOG_ID_MAX)
+               return EINVAL;
+
+       msg->flags |= DLOG_FLAG_ANSW;
+       r = send_data (wr->socket_fd,
+                                  msg,
+                                  &server->buffers[buf_id]->size,
+                                  sizeof(server->buffers[buf_id]->size));
+
+       if (r)
+               return r;
+
+       if (wr->readed > msg->length) {
+               wr->readed -= msg->length;
+               memcpy(wr->buffer, wr->buffer + msg->length, wr->readed);
+       } else
+               wr->readed = 0;
+
+       return 1;
+}
+
+static int service_writer_handle_req_pipe(struct logger* server, struct writer* wr, struct dlog_control_msg* msg)
+{
+       int r;
+       if (msg->length != sizeof(struct dlog_control_msg))
+               return EINVAL;
+
+       if (pipe2(wr->pipe_fd, O_CLOEXEC | O_NONBLOCK) < 0)
+               return -errno;
+
+       wr->event.data.ptr = wr;
+       wr->event.events = EPOLLIN;
+       fd_change_flag(wr->pipe_fd[1], O_NONBLOCK, 0);
+       r = send_pipe(wr->socket_fd, wr->pipe_fd[1], DLOG_FLAG_WRITE);
+       if (r)
+               return r;
+
+       fcntl(wr->pipe_fd[1], F_SETPIPE_SZ, PIPE_REQUESTED_SIZE);
+       add_fd_loop(server->epollfd, wr->pipe_fd[0], &wr->event);
+       writer_close_fd(server, wr);
+       wr->state = WRITER_PIPE;
+       wr->readed = 0;
+       return 0;
+}
+
+static int service_writer_socket(struct logger* server, struct writer* wr, struct epoll_event* event)
+{
+       int r = 0;
+       struct dlog_control_msg* msg;
+
+       if (event->events & EPOLLIN)
+               r = read(wr->socket_fd, wr->buffer + wr->readed, LOG_MAX_SIZE - wr->readed);
+
+       if ((r == 0 || r == -1) && event->events & EPOLLHUP)
+               return EINVAL;
+
+       do {
+               if (r > 0)
+                       wr->readed += r;
+
+               if (wr->readed < sizeof(msg->length))
+                       continue;
+
+               msg = (struct dlog_control_msg*) wr->buffer;
+
+               if (msg->length >= LOG_MAX_SIZE)
+                       return EINVAL;
+
+               if (wr->readed < msg->length)
+                       continue;
+
+               switch (msg->request) {
+               case DLOG_REQ_PIPE :
+                       r = service_writer_handle_req_pipe(server, wr, msg);
+                       break;
+               case DLOG_REQ_BUFFER_SIZE :
+                       r = service_writer_handle_req_size(server, wr, msg);
+                       break;
+               case DLOG_REQ_CLEAR:
+                       r = service_writer_handle_req_clear(server, wr, msg);
+                       break;
+               case DLOG_REQ_HANDLE_LOGUTIL:
+                       r = service_writer_handle_req_util(server, wr, msg);
+                       break;
+               case DLOG_REQ_REMOVE_WRITER:
+                       r = service_writer_handle_req_remove(server, wr, msg);
+                       break;
+               default:
+                       return EINVAL;
+               }
+
+               if (r <= 0)
+                       return r;
+
+               r = read(wr->socket_fd, wr->buffer + wr->readed, LOG_MAX_SIZE - wr->readed);
+       } while (r > 0 || ((wr->readed >= sizeof(msg->length) && wr->readed >= msg->length)) );
+
+       return (r >= 0 || (r < 0 && errno == EAGAIN))  ? 0 : r;
+}
+
+static int service_writer_pipe(struct logger* server, struct writer* wr, struct epoll_event* event)
+{
+       struct logger_entry* entry;
+       int r = 0;
+
+       if (event->events & EPOLLIN) {
+               r = read (wr->pipe_fd[0], wr->buffer + wr->readed, LOG_MAX_SIZE - wr->readed);
+
+               if (r == -1 && errno == EAGAIN)
+                       return 0;
+               else if ((r == 0 || r== -1) && event->events & EPOLLHUP)
+                       return EINVAL;
+               else if (r == 0)
+                       return -EBADF;
+
+               wr->readed += r;
+
+               entry = (struct logger_entry*)wr->buffer;
+               while ((wr->readed >= sizeof(entry->len)) && (entry->len <= wr->readed)) {
+                       buffer_append(entry, server->buffers[entry->buf_id]);
+                       wr->readed -= entry->len;
+                       server->should_timeout |= (1<<entry->buf_id);
+                       memmove(wr->buffer, wr->buffer + entry->len, LOG_MAX_SIZE - entry->len);
+               }
+       } else if (event->events & EPOLLHUP)
+               return -EBADF;
+
+       return 0;
+}
+
+static int service_writer(struct logger* server, struct writer* wr, struct epoll_event* event)
+{
+       switch (wr->state) {
+       case WRITER_SOCKET:
+               return service_writer_socket(server, wr, event);
+       case WRITER_PIPE:
+               return service_writer_pipe(server, wr, event);
+       }
+
+       return 0;
+}
+
+static void service_all_readers(struct logger* server, int time_elapsed)
+{
+       int i = 0;
+       int r = 0;
+       struct log_buffer** buffers = server->buffers;
+       struct reader* reader = NULL;
+
+       for (i = 0; i < LOG_ID_MAX; i++) {
+               buffers[i]->elapsed += time_elapsed;
+               if (buffers[i]->buffered_len >= server->max_buffered_bytes ||
+                       buffers[i]->elapsed >= server->max_buffered_time) {
+                       LIST_FOREACH(server->readers[i], reader) {
+                               r = service_reader(server, reader);
+                               if (r || reader->dumpcount) {
+                                       LIST_REMOVE(server->readers[i], reader, reader);
+                                       LIST_ITEM_FREE(reader, reader);
+                                       if (!reader)
+                                               break;
+                               }
+                       }
+
+                       buffers[i]->buffered_len = 0;
+                       buffers[i]->elapsed = 0;
+                       server->should_timeout &= ~(1<<i);
+               }
+       }
+}
+
+static struct logger* logger_create(char* path, struct log_config *conf)
+{
+       struct logger* l = calloc(1, sizeof(struct logger));
+       int i = 0;
+       int j;
+       int size = 0;
+       char conf_line [MAX_CONF_KEY_LEN];
+       const char *conf_value;
+
+       if (!l)
+               goto err1;
+
+       l->epollfd = epoll_create1(0);
+       if (l->epollfd == -1)
+               goto err1;
+
+       strncpy(l->control.path, path, sizeof(l->control.path));
+
+       if ((l->control.fd = listen_fd_create(l->control.path)) < 0)
+               goto err1;
+
+       l->control._entity.type = ENTITY_CONTROL;
+       l->control.event.data.ptr = &l->control;
+       l->control.event.events = EPOLLIN;
+
+       if (!(l->default_format = log_format_new()))
+               goto err2;
+       log_add_filter_string(l->default_format, "*:d");
+
+       if (!(l->buffers = calloc(LOG_ID_MAX, sizeof(struct log_buffer*))))
+               goto err2;
+
+       if (!(l->readers = calloc(LOG_ID_MAX, sizeof(struct reader*))))
+               goto err3;
+
+       add_fd_loop(l->epollfd, l->control.fd, &l->control.event);
+
+       for (i = 0; i < LOG_ID_MAX; i++) {
+               snprintf(conf_line, MAX_CONF_KEY_LEN, "%s_size", log_name_by_id(i));
+               conf_value = log_config_get (conf, conf_line);
+               if (!conf_value)
+                       goto err4;
+
+               size = atoi (conf_value);
+
+               conf_value = log_config_get(conf, log_name_by_id(i));
+               if (!conf_value)
+                       goto err4;
+
+               if (!(l->buffers[i] = buffer_create(i, size, conf_value)))
+                       goto err4;
+       }
+
+       return l;
+
+err4:
+       for (j = 0; j < i; j++)
+               buffer_free(&l->buffers[j]);
+err3:
+       free(l->buffers);
+err2:
+       close(l->control.fd);
+err1:
+       free(l);
+       return NULL;
+}
+
+static void logger_free(struct logger* l)
+{
+       int j;
+
+       if (!l) return;
+
+       log_format_free(l->default_format);
+
+       for (j = 0; j < LOG_ID_MAX; j++) {
+               struct writer* i;
+               while((i = l->writers)) {
+                       LIST_REMOVE(l->writers, i, writer);
+                       writer_free(i);
+               }
+       }
+
+       for (j = 0; j < LOG_ID_MAX; j++) {
+               struct reader* reader;
+               while((reader = l->readers[j])) {
+                       LIST_REMOVE(l->readers[j], reader, reader);
+                       reader_free(reader);
+               }
+       }
+
+       for (j = 0; j < LOG_ID_MAX; j++)
+               buffer_free(&l->buffers[j]);
+
+       free(l->buffers);
+       close(l->control.fd);
+       free(l);
+}
+
+static int add_reader(struct logger* server, int buf_id, int fd, enum reader_type type, const char* path, int rot_size, int max_rot)
+{
+       struct reader* r = calloc(1, sizeof(struct reader));
+
+       if (!r)
+               return ENOMEM;
+
+       r->type = type;
+       r->file.fd = fd;
+       r->buf_id = buf_id;
+       if (path != NULL) {
+               r->file.path = calloc(1, strlen(path));
+               strcpy(r->file.path, path);
+       }
+       r->file.format = log_format_from_format(server->default_format);
+       r->file.rotate_size_kbytes = rot_size;
+       r->file.max_rotated = max_rot;
+       r->file.size = 0;
+
+       LIST_ADD(server->readers[buf_id], r);
+
+       return 0;
+}
+
+static int dispatch_event(struct logger* server, struct fd_entity* entity, struct epoll_event* event)
+{
+       int r = 1;
+       switch(entity->type) {
+       case ENTITY_WRITER:
+               r = service_writer(server, (struct writer*)entity, event);
+               if (r) {
+                       LIST_REMOVE (server->writers, ((struct writer*)entity), writer);
+                       writer_free((struct writer*)entity);
+               }
+               break;
+       case ENTITY_CONTROL:
+       {
+               int sock_pipe = accept4(((struct control*)event->data.ptr)->fd, NULL, NULL, SOCK_NONBLOCK);
+               if (sock_pipe >= 0) {
+                       struct writer* writer = writer_create(sock_pipe);
+
+                       if (writer) {
+                               LIST_ADD(server->writers, writer);
+                               add_fd_loop(server->epollfd, writer->socket_fd, &writer->event);
+                       } else
+                               close (sock_pipe);
+               }
+               break;
+       }
+       case ENTITY_BUFFER:
+       {
+               struct log_buffer* buffer = (struct log_buffer*)event->data.ptr;
+               int sock = accept4(buffer->listen_fd, NULL, NULL, SOCK_NONBLOCK);
+               if (sock >= 0)
+                       r = add_reader(server, buffer->id, sock, READER_SOCKET, NULL, 0, 0);
+
+               if (r)
+                       close(sock);
+               break;
+       }
+       }
+       return 0;
+}
+
+static int logger_get_timeout(struct logger* server)
+{
+       int timeout = -1;
+       int i = 0;
+
+       if (!server->should_timeout)
+               return timeout;
+
+       for (i = 0; i < LOG_ID_MAX; i++) {
+               int diff = server->max_buffered_time - server->buffers[i]->elapsed;
+               if (diff >= 0 && (diff < timeout || timeout == -1))
+                       timeout = diff;
+       }
+       return timeout;
+}
+
+static int do_logger(struct logger* server)
+{
+       int nfds, i;
+       int time_left = 0;
+       struct timeval tv1;
+       struct timeval tv2;
+
+       const int max_events = 1024;
+       struct epoll_event events[1024];
+
+       for (i = 0; i < LOG_ID_MAX; i++)
+               add_fd_loop (server->epollfd, server->buffers[i]->listen_fd, &server->buffers[i]->event);
+
+       for(;;) {
+               gettimeofday(&tv1, NULL);
+               time_left = logger_get_timeout(server);
+               nfds = epoll_wait(server->epollfd, events, max_events, time_left);
+
+               if (nfds < 0 && errno == EINTR) {
+                       gettimeofday(&tv2, NULL);
+                       time_left = (tv2.tv_sec - tv1.tv_sec)*1000 + (tv2.tv_usec - tv1.tv_usec)/1000;
+               } else if (nfds < 0)
+                       goto err;
+
+               for (i = 0; i < nfds; i++) {
+                       struct fd_entity* entity = (struct fd_entity*) events[i].data.ptr;
+                       dispatch_event(server, entity, &events[i]);
+               }
+               service_all_readers(server, time_left);
+       }
+       return 0;
+
+err:
+       logger_free(server);
+       return errno;
+}
+
+static int parse_configs(struct logger** server)
+{
+       struct log_config conf;
+       const char * tmp;
+       char tmp2 [64];
+       int i;
+       char path_buffer[1024];
+       path_buffer[0] = 0;
+       if (*server)
+               logger_free(*server);
+
+       log_config_read (&conf);
+       tmp = log_config_get(&conf, "pipe_control_socket");
+
+       if (!tmp)
+               sprintf(path_buffer, LOG_CONTROL_SOCKET);
+       else
+               sprintf(path_buffer, "%s", tmp);
+
+       *server = logger_create(path_buffer, &conf);
+       if (!(*server))
+               return EINVAL;
+       i = 0;
+
+       while (1) {
+               sprintf(tmp2, "dlog_logger_conf_%d", i);
+               tmp = log_config_get(&conf, tmp2);
+               if (!tmp) break;
+               parse_command_line (tmp, *server, -1);
+               ++i;
+       }
+
+       log_config_free (&conf);
+       return 0;
+}
+
+static void help () {
+       printf
+               ( "Usage: %s [options]\n"
+                 "\t-h    Show this help\n"
+                 "\t-b N  Set the size of the log buffer (in bytes)\n"
+                 "\t-t N  Set time between writes to file (in seconds)\n"
+               , program_invocation_short_name
+       );
+}
+
+static int parse_args (int argc, char ** argv, struct logger * server)
+{
+       int temp, option;
+       while ((option = getopt(argc, argv, "hb:t:")) != -1) {
+               switch (option) {
+               case 't':
+                       if (!isdigit(optarg[0])) {
+                               help();
+                               return EINVAL;
+                       }
+
+                       temp = atoi(optarg);
+                       if (temp < 0) temp = 0;
+                       if (temp > INTERVAL_MAX) temp = INTERVAL_MAX;
+                       server->max_buffered_time = temp;
+
+                       break;
+               case 'b':
+                       if (!isdigit(optarg[0])) {
+                               help();
+                               return EINVAL;
+                       }
+
+                       temp = atoi(optarg);
+                       if (temp < 0) temp = 0;
+                       if (temp > BUFFER_MAX) temp = BUFFER_MAX;
+                       server->max_buffered_bytes = temp;
+
+                       break;
+               case 'h':
+                       help();
+                       return EINVAL;
+               default:
+                       return EINVAL;
+               }
+       }
+       optind = 0;
+       optopt = 0;
+       optarg = NULL;
+       return 0;
+}
+
+int main(int argc, char** argv)
+{
+       int r;
+       struct logger* server = NULL;
+       signal(SIGPIPE, SIG_IGN);
+
+       r = parse_configs(&server);
+       if (r) {
+               logger_free (server);
+               return r;
+       }
+
+       r = parse_args (argc, argv, server);
+       if (r) {
+               logger_free (server);
+               return r;
+       }
+
+       return do_logger(server);
+}
diff --git a/src/logutil/logutil_pipe.c b/src/logutil/logutil_pipe.c
new file mode 100644 (file)
index 0000000..c77dc50
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2016, Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <linux/limits.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <logpipe.h>
+#include <logprint.h>
+#include <log_file.h>
+#include <logconfig.h>
+
+/* should fit a whole command line with concatenated arguments (reasonably) */
+#define MAX_LOGGER_REQUEST_LEN 2048
+
+int buf_id = 0;
+int sock_fd = -1;
+
+static int connect_sock(const char * path)
+{
+       int r;
+       int fd;
+       struct sockaddr_un sa = { .sun_family = AF_UNIX };
+       fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+
+       if (fd < 0)
+               return -errno;
+
+       strncpy(sa.sun_path, path, sizeof (sa.sun_path));
+
+       r = connect(fd, (struct sockaddr *) &sa, sizeof(sa));
+       if (r < 0) {
+               close (fd);
+               return -errno;
+       }
+
+       return fd;
+}
+
+static int do_clear ()
+{
+       const int size = sizeof(struct dlog_control_msg) + 1;
+       struct dlog_control_msg * const msg = calloc (1, size);
+
+       msg->length = size;
+       msg->request = DLOG_REQ_CLEAR;
+       msg->flags = 0;
+       msg->data[0] = buf_id;
+       if (write (sock_fd, msg, size) < 0) {
+               printf("Error: could not send a CLEAR request to logger; socket write failed\n");
+               return 1;
+       }
+       return 0;
+}
+
+static int do_getsize (struct log_config * conf)
+{
+       char conf_key [MAX_CONF_KEY_LEN];
+       const char * conf_value;
+
+       sprintf(conf_key, "%s_size", log_name_by_id (buf_id));
+       conf_value = log_config_get (conf, conf_key);
+       if (!conf_value) {
+               printf ("Error: could not get size of buffer #%d (%s); it has no config entry\n", buf_id, log_name_by_id(buf_id));
+               return 1;
+       }
+
+       printf ("Buffer #%d (%s) has size %d KB\n", buf_id, log_name_by_id (buf_id), atoi (conf_value) / 1024);
+       return 0;
+}
+
+static int send_logger_request (int argc, char ** argv)
+{
+       char logger_request [MAX_LOGGER_REQUEST_LEN];
+       int logger_request_len = snprintf (logger_request, MAX_LOGGER_REQUEST_LEN, "dlogutil");
+       struct dlog_control_msg * msg;
+       int i;
+
+       for (i = 1; i < argc; ++i)
+        {
+                logger_request_len += snprintf (logger_request + logger_request_len, MAX_LOGGER_REQUEST_LEN - logger_request_len, " %s", argv[i]);
+        }
+
+       logger_request_len += sizeof(struct dlog_control_msg) + 1;
+
+        msg = calloc(1, logger_request_len);
+        msg->length = logger_request_len;
+        msg->request = DLOG_REQ_HANDLE_LOGUTIL;
+        msg->flags = 0;
+        memcpy(msg->data, logger_request, logger_request_len - sizeof(struct dlog_control_msg));
+        if (write (sock_fd, msg, logger_request_len) < 0) {
+                printf("Error: could not send a logger request; socket write failed\n");
+                return 0;
+        }
+
+        free (msg);
+       return 1;
+}
+
+static void handle_pipe (int pipe_fd)
+{
+       char buff [LOG_MAX_SIZE];
+       int r;
+       for (;;) {
+               r = read(pipe_fd, buff, LOG_MAX_SIZE);
+               if (r <= 0 && errno != EAGAIN)
+                       return;
+               buff[r] = '\0';
+               printf ("%s", buff);
+       }
+}
+
+int main (int argc, char ** argv)
+{
+       char buffer_name [MAX_CONF_VAL_LEN] = "";
+       const char * sock_path;
+       int pipe_fd;
+       struct log_config conf;
+       int should_clear = 0;
+       int should_getsize = 0;
+       int into_file = 0;
+
+       while (1) {
+               int option = getopt(argc, argv, "cdt:gsf:r:n:v:b:");
+
+               if (option < 0)
+                       break;
+
+               switch (option) {
+               case 'c':
+                       should_clear = 1;
+                       break;
+               case 'g':
+                       should_getsize = 1;
+                       break;
+               case 'b':
+                       strncpy (buffer_name, optarg, MAX_CONF_VAL_LEN);
+                       break;
+               case 'f':
+                       into_file = 1;
+                       break;
+               default:
+                       // everything else gets relegated to dlog_logger
+                       break;
+               }
+       }
+
+       if (strlen(buffer_name) && (((buf_id = log_id_by_name (buffer_name)) < 0) || (buf_id >= LOG_ID_MAX))) {
+               printf ("There is no buffer \"%s\"\n", buffer_name);
+               return 1;
+       }
+
+       log_config_read (&conf);
+
+       if (should_getsize)
+               return do_getsize (&conf);
+
+       if ((sock_path = log_config_get(&conf, "pipe_control_socket")) == NULL) {
+               printf("Error: dlog config is broken, lacks the pipe_control_socket entry\n");
+               return 1;
+       }
+
+       log_config_free (&conf);
+
+       if ((sock_fd = connect_sock (sock_path)) < 0) {
+               printf("Error: socket connection failed\n");
+               return 1;
+       }
+
+       if (should_clear)
+               return do_clear ();
+
+       if (!send_logger_request (argc, argv)) {
+               printf("Error: could not send request to logger daemon\n");
+               return 1;
+       }
+
+       if (into_file) {
+               close (sock_fd);
+               return 0;
+       }
+
+       if ((pipe_fd = recv_file_descriptor (sock_fd)) < 0) {
+               printf("Error: failed to receive a logging pipe\n");
+               return 1;
+       }
+
+       handle_pipe (pipe_fd);
+
+       return 0;
+}
index 6d27af5..f543461 100644 (file)
 #include <string.h>
 #include <syslog.h>
 
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
 #include <logcommon.h>
 
 #define MAX_PREFIX_SIZE 32
@@ -63,3 +67,37 @@ void syslog_critical_failure (const char * message)
        syslog (LOG_CRIT, "DLOG CRITIAL FAILURE: %s", message);
        closelog ();
 }
+
+int recv_file_descriptor(int socket)
+{
+       struct msghdr message;
+       struct iovec iov[1];
+       struct cmsghdr *control_message = NULL;
+       char ctrl_buf[CMSG_SPACE(sizeof(int))];
+       char data[1];
+       int res;
+
+       memset(&message, 0, sizeof(struct msghdr));
+       memset(ctrl_buf, 0, CMSG_SPACE(sizeof(int)));
+
+       /* For the dummy data */
+       iov[0].iov_base = data;
+       iov[0].iov_len = sizeof(data);
+
+       message.msg_name = NULL;
+       message.msg_namelen = 0;
+       message.msg_control = ctrl_buf;
+       message.msg_controllen = CMSG_SPACE(sizeof(int));
+       message.msg_iov = iov;
+       message.msg_iovlen = 1;
+
+       if((res = recvmsg(socket, &message, 0)) <= 0)
+       return res;
+
+       /* Iterate through header to find if there is a file descriptor */
+       for(control_message = CMSG_FIRSTHDR(&message); control_message != NULL; control_message = CMSG_NXTHDR(&message, control_message)) {
+               if( (control_message->cmsg_level == SOL_SOCKET) && (control_message->cmsg_type == SCM_RIGHTS) )
+                       return *(CMSG_DATA(control_message));
+       }
+       return -1;
+}
index 1fbfe86..2e2c65f 100644 (file)
@@ -62,7 +62,6 @@ int log_config_set (struct log_config* c, const char* key, const char* value)
 
 int log_config_read (struct log_config* config)
 {
-       int i;
        int ret = 0;
        char const * override;