Improvements to Pipe backend: 51/73851/12
authorMichal Bloch <m.bloch@samsung.com>
Tue, 14 Jun 2016 15:58:43 +0000 (17:58 +0200)
committerMichal Bloch <m.bloch@samsung.com>
Wed, 29 Jun 2016 17:18:34 +0000 (19:18 +0200)
* each buffer now has its own socket, this way
  they can have separate level of access.
* dlogutil now sorts logs received from daemon.
  This is to guarantee time sequence.
* daemon output is now binary, which both
  increases performance and gives the user more
  control (for example, over output formatting).
* improve logger security. Clogged pipes cannot
  freeze the daemon.
* fix some cases of logs being dropped.
* performance tests now have better coverage.
* dlogctrl now provides documentation for config values.

Change-Id: I2718281828459045235360287471352662497757
Signed-off-by: Michal Bloch <m.bloch@samsung.com>
13 files changed:
configs/dlog.conf.pipe
include/logpipe.h
packaging/dlog.spec
src/libdlog/log_pipe.c
src/logctrl/logctrl.c
src/logctrl/logctrl_doc.h [new file with mode: 0644]
src/logger/logger_pipe.c
src/logutil/logutil_doc.h [new file with mode: 0644]
src/logutil/logutil_kmsg_logger.c
src/logutil/logutil_pipe.c
src/shared/logconfig.c
tests/performance_test.c
tests/test_pipe.sh

index dac7049..1606974 100644 (file)
@@ -1,15 +1,73 @@
+# Lines starting with # are comments and are ignored.
+# Empty lines are also ignored.
+# Every other line is a config value and has to be in key=value format
+
+##### Settings used by the logging library
+
+# Platform logging. Whether logging happens at all.
 plog=1
+
+# Limiter - whether it's enabled and the rules (check the documentation for how to create rules).
 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
+
+##### Settings used by the logging daemon
+
+# Sockets that all programs connect to to receive a logging pipe.
+main_write_sock=/run/dlog/main.wr
+apps_write_sock=/run/dlog/apps.wr
+system_write_sock=/run/dlog/system.wr
+radio_write_sock=/run/dlog/radio.wr
+
+# Sockets that administrative programs connect to to manipulate the daemon.
+main_ctl_sock=/run/dlog/main.ctl
+apps_ctl_sock=/run/dlog/apps.ctl
+system_ctl_sock=/run/dlog/system.ctl
+radio_ctl_sock=/run/dlog/radio.ctl
+
+# Permissions for the relevant sockets.
+main_write_sock_rights=0222
+apps_write_sock_rights=0222
+system_write_sock_rights=0222
+radio_write_sock_rights=0222
+
+main_ctl_sock_rights=0222
+apps_ctl_sock_rights=0222
+system_ctl_sock_rights=0222
+radio_ctl_sock_rights=0222
+
+main_write_sock_owner=log
+apps_write_sock_owner=log
+system_write_sock_owner=log
+radio_write_sock_owner=log
+
+main_ctl_sock_owner=log
+apps_ctl_sock_owner=log
+system_ctl_sock_owner=log
+radio_ctl_sock_owner=log
+
+main_write_sock_group=log
+apps_write_sock_group=log
+system_write_sock_group=log
+radio_write_sock_group=log
+
+main_ctl_sock_group=log
+apps_ctl_sock_group=log
+system_ctl_sock_group=log
+radio_ctl_sock_group=log
+
+# Size (in bytes) of internal cyclic buffers.
 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
+
+# Passive logging to file done by the daemon.
+dlog_logger_conf_0=dlogutil -b system -r 5120 -n 1 -f /var/log/dlog/system.raw *:I
+dlog_logger_conf_1=dlogutil -b main -r 3072 -n 1 -f /var/log/dlog/main.raw *:W
+dlog_logger_conf_2=dlogutil -b radio -r 1024 -n 1 -f /var/log/dlog/radio.raw
+
+##### Settings used by dlogutil
+
+# Sorting time window (in milliseconds). Proportional to sorting quality, but also delay.
+util_sorting_time_window=1000
index ba18f51..3c16418 100644 (file)
 
 #include <logcommon.h>
 
+#define PIPE_FILE_FORMAT_VERSION 1
+
 #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
 };
 
@@ -44,18 +44,4 @@ struct dlog_control_msg {
        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 410b208..8883875 100755 (executable)
@@ -131,6 +131,7 @@ ln -s ../dlog_logger.path %{buildroot}%{_unitdir}/multi-user.target.wants/dlog_l
 %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
+sed -e '/^Nice=/ d' -i %{buildroot}%{_unitdir}/dlog_logger.service
 %endif
 
 %endif
@@ -192,11 +193,6 @@ chsmack -a System /var/log/dlog
 %endif
 %endif
 
-%if %{?backend_pipe} == ON
-%{_unitdir}/multi-user.target.wants/dlog_logger.service
-%endif
-
-
 %files  -n libdlog
 %manifest libdlog.manifest
 /usr/share/license/libdlog
@@ -212,6 +208,7 @@ chsmack -a System /var/log/dlog
 %if %{?backend_pipe} == ON
 %attr(755,log,log) /var/log/dlog
 %attr(750,log,log) %{_bindir}/dlog_logger
+%{_unitdir}/multi-user.target.wants/dlog_logger.service
 %{_unitdir}/dlog_logger.service
 %{_unitdir}/dlog_logger.path
 %attr(664,log,log) /usr/lib/tmpfiles.d/dlog-pipe.conf
index 954920f..840a6a3 100644 (file)
@@ -32,8 +32,8 @@
 
 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 pipe_fd [LOG_ID_MAX];
+static char log_pipe_path [LOG_ID_MAX] [PATH_MAX];
 
 
 static int connect_pipe(const char * path)
@@ -104,21 +104,23 @@ static int __write_to_log_pipe(log_id_t log_id, log_priority prio, const char *t
 
        if (le->len % 2)
                le->len += 1;
-       ret = write (pipe_fd, buf, le->len);
+       ret = write (pipe_fd[log_id], 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);
+               pthread_mutex_lock (&log_init_lock);
+               close (pipe_fd [log_id]);
+               pipe_fd [log_id] = connect_pipe (log_pipe_path [log_id]);
+               pthread_mutex_unlock (&log_init_lock);
+               ret = write (pipe_fd [log_id], buf, le->len);
        }
        return ret;
 }
 
 void __dlog_init_backend()
 {
-       const char * pipe_path_temp;
+       const char * conf_val;
+       char conf_key [MAX_CONF_KEY_LEN];
        struct log_config conf;
+       int i;
 
        /*
         * We mask SIGPIPE signal because most applications do not install their
@@ -128,14 +130,18 @@ void __dlog_init_backend()
        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;
+
+       for (i = 0; i < LOG_ID_MAX; ++i) {
+               snprintf (conf_key, sizeof (conf_key), "%s_write_sock", log_name_by_id (i));
+               conf_val = log_config_get (&conf, conf_key);
+               if (!conf_val) {
+                       syslog_critical_failure ("DLog config lacks the \"%s\" entry!");
+                       return;
+               }
+               snprintf (log_pipe_path[i], PATH_MAX, "%s", conf_val);
+               pipe_fd[i] = connect_pipe (log_pipe_path[i]);
        }
-       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;
 }
index 9e6a76a..e50790d 100644 (file)
@@ -4,22 +4,12 @@
 #include <unistd.h>
 
 #include <logconfig.h>
-
-void print_help ()
-{
-       printf ("dlogctrl - provides control over dlog configuration. Options:\n"
-               "\t-p        Precludes -k. Prints all entries in the config.\n"
-               "\t-k key    Precludes -p and requires one of -gs.  Specifies a config key.\n"
-               "\t-g        Requires -k, precludes -cs. Gets and prints the value of the entry assigned to the key.\n"
-               "\t-s value  Requires -k, precludes -cg. Sets the value of the entry associated with the key.\n"
-               "\t-c        Requires -k, precludes -gs. Clears the entry.\n"
-               "Having either -p or -k is mandatory.\n"
-       );
-}
+#include "logctrl_doc.h"
 
 struct options {
        int print_all;
        int has_key;
+       int help;
        int should_get;
        int should_set;
        int should_clear;
@@ -28,16 +18,16 @@ struct options {
 int validate (struct options o) {
        int valid = 1;
 
-       if (!o.has_key && !o.print_all) {
-               printf ("Having either -p or -k is mandatory!\n");
+       if (!o.has_key && !o.print_all && !o.help) {
+               printf ("Having either -i, -p, or -k is mandatory!\n");
                valid = 0;
        }
        if (o.has_key && !(o.should_get || o.should_set || o.should_clear)) {
                printf ("-k requires either -c, -g, or -s!\n");
                valid = 0;
        }
-       if (o.has_key && o.print_all) {
-               printf ("-p and -k preclude each other!\n");
+       if ((o.has_key + o.print_all + o.help) > 1) {
+               printf ("-i, -p and -k preclude each other!\n");
                valid = 0;
        }
        if (o.should_set + o.should_get + o.should_clear > 1) {
@@ -57,7 +47,7 @@ int main (int argc, char ** argv)
        struct log_config conf;
        char key [MAX_CONF_KEY_LEN];
        char val [MAX_CONF_VAL_LEN];
-       struct options opt = {0,0,0,0,0};
+       struct options opt = {0,0,0,0,0,0};
        const char * filename = getenv ("DLOG_CONFIG_PATH") ?: get_config_filename (CONFIG_TYPE_COMMON);
 
        if (argc == 1) {
@@ -66,7 +56,7 @@ int main (int argc, char ** argv)
        }
 
        for (;;) {
-               int ret = getopt(argc, argv, "pt:k:gs:c");
+               int ret = getopt(argc, argv, "pt:k:gs:ci");
 
                if (ret < 0)
                        break;
@@ -89,6 +79,9 @@ int main (int argc, char ** argv)
                        opt.should_set = 1;
                        snprintf(val, MAX_CONF_VAL_LEN, "%s", optarg);
                        break;
+               case 'i':
+                       opt.help = 1;
+                       break;
                }
        }
 
@@ -97,6 +90,11 @@ int main (int argc, char ** argv)
                return 1;
        }
 
+       if (opt.help) {
+               print_extended_help ();
+               return 1;
+       }
+
        if (!log_config_read (&conf)) {
                printf ("Error: cannot open the config file!\n");
                return 1;
diff --git a/src/logctrl/logctrl_doc.h b/src/logctrl/logctrl_doc.h
new file mode 100644 (file)
index 0000000..1d5eb7a
--- /dev/null
@@ -0,0 +1,76 @@
+void print_help ()
+{
+       printf ("dlogctrl - provides control over dlog configuration. Options:\n"
+               "\t-p        Precludes -kh. Prints all entries in the config.\n"
+               "\t-k key    Precludes -ph and requires one of -gs.  Specifies a config key.\n"
+               "\t-g        Requires -k, precludes -cs. Gets and prints the value of the entry assigned to the key.\n"
+               "\t-s value  Requires -k, precludes -cg. Sets the value of the entry associated with the key.\n"
+               "\t-c        Requires -k, precludes -gs. Clears the entry.\n"
+               "\t-i        Precludes -pk. Shows information for important config values.\n"
+               "Having either -i, -p or -k is mandatory.\n"
+       );
+}
+
+void print_extended_help ()
+{
+       printf ("Important entries:\n"
+               "\tplog: enable platform logging, ie. whether any logging happens at all. Values are 0 or 1.\n"
+               "\tlimiter: enable log limiting. Values are 0 or 1.\n\n"
+               "Limiter rules:\n"
+               "\tlimiter entry keys are of the form \"limiter|TAG|LEVEL\",\n"
+               "\twhere TAG is the arbitrary string which defines the user application,\n"
+               "\tand LEVEL is logging level which is one of:\n"
+               "\t\tV or 1: verbose\n"
+               "\t\tD or 2: debug\n"
+               "\t\tI or 3: info\n"
+               "\t\tW or 4: warning\n"
+               "\t\tE or 5: error\n"
+               "\t\tF or 6: fatal\n"
+               "\tA rule fits messages of given tag and level. Alternatively,\n"
+               "\tan asterisk (*) can also be used in place of either, as a wild card.\n"
+               "\tExplicit rules have priority over wild-card ones.\n"
+               "\tTwo asterisks can be used to signify a backup rule for those messages\n"
+               "\twhich did not fit any other rule. Some example rule keys:\n"
+               "\tExample rules:\n"
+               "\t\tlimiter|SOME_APP|W\n"
+               "\t\tlimiter|SOME_APP|* (affects all SOME_APP logs except warnings, which already have an explicit rule)\n"
+               "\t\tlimiter|*|W (affects all warnings except the ones from SOME_APP)\n"
+               "\t\tlimiter|*|* (affects everything else)\n"
+               "\tLimiter entries can have one of three value types:\n"
+               "\t\tstring \"allow\" or integers < 0: lets all logs of given type through.\n"
+               "\t\tstring \"deny\" or integers > 10000: blocks all logs of given type.\n"
+               "\t\tintegers from the <1, 10000> range: let through that many logs per minute.\n\n"
+#ifndef DLOG_BACKEND_JOURNAL
+               "Buffer size entries:\n"
+               "\tkeys are of the form <BUFFER>_size, for example main_size;\n"
+               "\tvalues are size in bytes, for example 1048576.\n"
+               "\tThese entries control the size of the logging buffers.\n\n"
+               "Passive logging to file (dlog_logger):\n"
+               "\tkeys are of the format dlog_logger_conf_#\n"
+               "\twhere # are consecutive integers from 0.\n"
+               "\tValues are the same as when calling dlogutil (including \"dlogutil\" at start), for example\n"
+               "\t\tdlogutil -b system -r 5120 -n 1 -f /var/log/dlog/system.raw *:I\n\n"
+#ifndef DLOG_BACKEND_PIPE
+               "Buffer device names:\n"
+               "\tkeys are simply buffer names, for example main;\n"
+               "\tvalues are paths to the devices.\n"
+               "\tNote: these values are read-only!\n"
+               "\tChanging them does nothing and can potentially break things.\n\n"
+#endif
+#endif
+#ifdef DLOG_BACKEND_PIPE
+               "DLogUtil sorting:\n"
+               "\tkey is util_sorting_time_window;\n"
+               "\tvalue is an integer representing the size\n"
+               "\tof the sorting window in milliseconds. This\n"
+               "\taffects quality of the sort, but also delay.\n\n"
+               "Socket control entries:\n"
+               "\tkeys are of the form <BUFFER>_{ctl,write}_sock_{path,rights,group,owner}\n"
+               "\tFor example: system_write_sock_group, radio_ctl_sock_path\n"
+               "\tCTL sockets are for administrative purposes, WRITE are for programs making logs.\n"
+               "\tFor PATH, values signify the path to the appropriate socket.\n"
+               "\tFor RIGHTS, the value is file permissions in octal (for example 0664).\n"
+               "\tFor GROUP and OWNER the values are group/owner of the file (for example root).\n\n"
+#endif
+       );
+}
index 681aae0..02979cc 100644 (file)
@@ -26,6 +26,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include <linux/limits.h>
 #include <sys/epoll.h>
@@ -75,7 +77,6 @@
        }
 
 #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
@@ -114,6 +115,7 @@ struct writer {
 
        struct writer*     next;
        struct writer*     prev;
+       struct log_buffer* buf_ptr;
 
        int                readed;
        char               state;
@@ -130,6 +132,8 @@ struct reader {
        int                dumpcount;
        struct reader*     next;
        struct reader*     prev;
+       int                partial_log_size;
+       char               partial_log [LOG_MAX_SIZE];
 };
 
 struct log_buffer {
@@ -152,12 +156,15 @@ struct control {
        struct fd_entity   _entity;
        int                fd;
        struct epoll_event event;
+       struct log_buffer* buf_ptr;
+       int                is_control;
        char               path [PATH_MAX];
 };
 
 struct logger {
        int                 epollfd;
-       struct control      control;
+       struct control      socket_wr  [LOG_ID_MAX];
+       struct control      socket_ctl [LOG_ID_MAX];
        struct writer*      writers;
        struct reader**     readers;
        struct log_buffer** buffers;
@@ -167,7 +174,60 @@ struct logger {
        log_format*         default_format;
 };
 
-static int listen_fd_create(const char* path)
+static int parse_permissions (const char * str)
+{
+       int ret, parsed;
+       char * parse_safety;
+
+       if (!str)
+               return S_IWUSR | S_IWGRP | S_IWOTH; // 0222: everyone can write
+
+       parsed = strtol (str, & parse_safety, 8); // note, rights are octal
+       if (parse_safety != (str + strlen (str)))
+               return 0;
+
+       ret = 0;
+
+       // note that R and X are pretty useless, only W makes sense
+       if (parsed & 00001) ret |= S_IXOTH;
+       if (parsed & 00002) ret |= S_IWOTH;
+       if (parsed & 00004) ret |= S_IROTH;
+       if (parsed & 00010) ret |= S_IXGRP;
+       if (parsed & 00020) ret |= S_IWGRP;
+       if (parsed & 00040) ret |= S_IWGRP;
+       if (parsed & 00100) ret |= S_IWUSR;
+       if (parsed & 00200) ret |= S_IWUSR;
+       if (parsed & 00400) ret |= S_IWUSR;
+       if (parsed & 01000) ret |= S_ISVTX;
+       if (parsed & 02000) ret |= S_ISGID;
+       if (parsed & 04000) ret |= S_ISUID;
+
+       return ret;
+}
+
+static int change_owners (const char * file, const char * user, const char * group)
+{
+       uid_t uid = -1;
+       gid_t gid = -1;
+       struct passwd * pwd = NULL;
+       struct group  * grp = NULL;
+
+       if (user)
+               pwd = getpwnam (user);
+
+       if (pwd)
+               uid = pwd->pw_uid;
+
+       if (group)
+               grp = getgrnam (group);
+
+       if (grp)
+               gid = grp->gr_gid;
+
+       return! chown (file, uid, gid); // ideally would be fchown, but that is broken
+}
+
+static int listen_fd_create (const char* path, int permissions)
 {
        struct sockaddr_un server_addr;
        int sd;
@@ -184,6 +244,10 @@ static int listen_fd_create(const char* path)
        if (bind(sd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1)
                goto failure;
 
+       if (permissions)
+               if (chmod (path, permissions) < 0) // ideally, fchmod would be used, but that does not work
+                       goto failure;
+
        if (listen(sd, MAX_CONNECTION_Q) < 0)
                goto failure;
 
@@ -204,7 +268,7 @@ 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)
+static struct writer* writer_create(int fd, int rights)
 {
        struct writer* w = calloc(1, sizeof(struct writer));
 
@@ -215,6 +279,7 @@ static struct writer* writer_create(int fd)
        w->event.events = EPOLLIN;
        w->socket_fd = fd;
        w->_entity.type = ENTITY_WRITER;
+       w->rights = rights;
        return w;
 }
 
@@ -233,26 +298,17 @@ static void writer_free(struct writer* w)
        free(w);
 }
 
-static struct log_buffer* buffer_create(int buf_id, int size, const char * path)
+static struct log_buffer* buffer_create(int buf_id, int size)
 {
        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)
@@ -297,13 +353,18 @@ static int buffer_free_space(struct log_buffer* b)
        return free_space;
 }
 
-static void buffer_append(const struct logger_entry* entry, struct log_buffer* b)
+static void buffer_append(const struct logger_entry* entry, struct log_buffer* b, struct reader* reader_head)
 {
-       while (buffer_free_space(b) < entry->len) {
+       while (buffer_free_space(b) <= entry->len) { // note that we use <= instead of < to make sure there's always some extra empty space. This guarantees that head != tail, which serves to disambiguate readers living there
+               int old_head = b->head;
+               struct reader * reader;
                struct logger_entry* t = (struct logger_entry*)(b->buffer + b->head);
                b->head += t->len;
                b->head %= b->size;
                -- b->lines;
+               LIST_FOREACH(reader_head, reader)
+                       if (reader->current == old_head)
+                               reader->current = b->head;
        }
 
        copy_to_buffer(entry, b->tail, entry->len, b);
@@ -314,44 +375,104 @@ static void buffer_append(const struct logger_entry* entry, struct log_buffer* b
        ++ b->lines;
 }
 
-static int print_out_logs(struct log_file* file, struct log_buffer* buffer, int from, int dumpcount)
+static void add_misc_file_info (int fd)
 {
+       const int32_t version = PIPE_FILE_FORMAT_VERSION;
+       const int32_t endian = 0x12345678;
        int r;
-       int written;
-       log_entry entry;
+
+       r = write (fd, &endian, 4);
+       if (r <= 0)
+               return;
+
+       r = write (fd, &version, 4);
+       if (r <= 0)
+               return;
+}
+
+static int print_out_logs(struct reader* reader, struct log_buffer* buffer)
+{
+       int r, ret = 0;
        struct logger_entry* ple;
-       char tmp[LOG_MAX_SIZE];
-       int skip = (dumpcount > 0) * (buffer->lines - dumpcount);
+       char tmp [LOG_MAX_SIZE];
+       int priority;
+       char * tag;
+       struct epoll_event ev = { .events = EPOLLOUT, .data.fd = reader->file.fd };
+       int epoll_fd;
+       int from = reader->current;
+       int is_file = 0;
+
+       epoll_fd = epoll_create1 (0);
+       r = epoll_ctl (epoll_fd, EPOLL_CTL_ADD, reader->file.fd, &ev);
+       if (r == -1 && errno == EPERM)
+               is_file = 1;
+
+       if (reader->partial_log_size) {
+               if (!is_file && epoll_wait (epoll_fd, &ev, 1, 0) < 1)
+                       goto cleanup;
+
+               do {
+                       r = write (reader->file.fd, reader->partial_log, reader->partial_log_size);
+               } while (r < 0 && errno == EINTR);
+
+               if (r <= 0)
+                       goto cleanup;
+
+               if (r < reader->partial_log_size) {
+                       reader->partial_log_size -= r;
+                       memmove (reader->partial_log, reader->partial_log + r, reader->partial_log_size);
+                       goto cleanup;
+               }
+
+               reader->partial_log_size = 0;
+       }
 
        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;
-               }
+               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)
+               priority = ple->msg[0];
+               if (priority < 0 || priority > DLOG_SILENT)
+                       continue;
+
+               tag = ple->msg + 1;
+               if (!strlen (tag))
                        continue;
 
-               if (!log_should_print_line(file->format, entry.tag, entry.priority))
+               if (!log_should_print_line(reader->file.format, tag, priority))
                        continue;
 
-               written = log_print_log_line(file->format, file->fd, &entry);
-               if (written < 0)
-                       return 1;
+               if (!is_file && epoll_wait (epoll_fd, &ev, 1, 0) < 1)
+                       goto cleanup;
 
-               file->size += written;
+               do {
+                       r = write (reader->file.fd, ple, ple->len);
+               } while (r < 0 && errno == EINTR);
 
-               if ((file->rotate_size_kbytes > 0) && ((file->size / 1024) >= file->rotate_size_kbytes))
-                       rotate_logs(file);
+               if (r > 0)
+                       reader->file.size += r;
+
+               if (r < ple->len) {
+                       reader->partial_log_size = ple->len - r;
+                       memcpy (reader->partial_log, ple + r, reader->partial_log_size);
+                       goto cleanup;
+               } else if ((reader->file.rotate_size_kbytes > 0) && ((reader->file.size / 1024) >= reader->file.rotate_size_kbytes)) {
+                       rotate_logs(&reader->file);
+                       add_misc_file_info (reader->file.fd);
+               }
        }
-       return 0;
+
+       if (reader->dumpcount)
+               ret = 1;
+
+cleanup:
+       close (epoll_fd);
+       reader->current = from;
+       return ret;
 }
 
 static int send_pipe(int socket, int wr_pipe, int type)
@@ -385,31 +506,6 @@ static int send_pipe(int socket, int wr_pipe, int type)
        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) {
@@ -442,20 +538,19 @@ static int service_reader(struct logger* server, struct reader* reader)
        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;
+       r = print_out_logs(reader, buffer);
        return r;
 }
 
-static int parse_command_line(const char* cmdl, struct logger* server, int wr_socket_fd)
+static int parse_command_line(const char* cmdl, struct logger* server, struct writer* wr)
 {
        char cmdline[512];
        int option, argc;
        char *argv [ARG_MAX];
        char *tok;
        char *tok_sv;
-       int silent = 0;
        int retval = 0;
+       int wr_socket_fd = wr ? wr->socket_fd : -1;
        struct reader * reader;
 
        if (!server || !cmdl) return EINVAL;
@@ -483,25 +578,14 @@ static int parse_command_line(const char* cmdl, struct logger* server, int wr_so
        reader->file.size = 0;
        reader->buf_id = 0;
        reader->dumpcount = 0;
+       reader->partial_log_size = 0;
 
-       while ((option = getopt(argc, argv, "cgsdt:f:r:n:v:b:")) != -1) {
+       while ((option = getopt(argc, argv, "cdt:gsf: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;
+               case 't':
+                       reader->dumpcount = -1;
                        break;
                case 'f':
                        if (!optarg) {
@@ -531,45 +615,28 @@ static int parse_command_line(const char* cmdl, struct logger* server, int wr_so
                        }
                        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:
+                       // everything else gets handled in util directly
                        break;
                }
        }
 
+       if (wr && wr->buf_ptr)
+               reader->buf_id = wr->buf_ptr->id;
+
        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);
+               add_misc_file_info (reader->file.fd);
        } else {
                reader->file.rotate_size_kbytes = 0;
                reader->file.max_rotated = 0;
                int fds[2];
-               if (!pipe2(fds, O_CLOEXEC)) {
+               if (!pipe2(fds, O_CLOEXEC | O_NONBLOCK)) {
                        reader->file.fd = fds[1];
                        send_pipe(wr_socket_fd, fds[0], DLOG_FLAG_READ);
                }
@@ -580,6 +647,7 @@ static int parse_command_line(const char* cmdl, struct logger* server, int wr_so
        }
        reader->current = server->buffers[reader->buf_id]->head;
        server->should_timeout |= (1 << reader->buf_id);
+       server->buffers[reader->buf_id]->elapsed = server->max_buffered_time;
        LIST_ADD(server->readers[reader->buf_id], reader);
 
 cleanup:
@@ -616,37 +684,6 @@ static void fd_change_flag(int fd, int flag, int set)
        * > 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)
 {
@@ -656,7 +693,8 @@ static int service_writer_handle_req_util(struct logger* server, struct writer*
                return EINVAL;
 
        msg->data[msg->length - sizeof(struct dlog_control_msg) -1] = 0;
-       r = parse_command_line(msg->data, server, wr->socket_fd);
+       r = parse_command_line(msg->data, server, wr);
+
        if (r < 0)
                return r;
 
@@ -671,22 +709,18 @@ static int service_writer_handle_req_util(struct logger* server, struct writer*
 
 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))
+       if (msg->length != (sizeof(struct dlog_control_msg)))
                return EINVAL;
 
-       buf_id = msg->data[0];
-       if (buf_id < 0 || buf_id >= LOG_ID_MAX)
+       if (!wr || !wr->buf_ptr)
                return EINVAL;
 
-       lb = server->buffers[buf_id];
-       lb->head = lb->tail = lb->not_empty = 0;
-       lb->elapsed = lb->buffered_len = lb->lines = 0;
+       wr->buf_ptr->head = wr->buf_ptr->tail = wr->buf_ptr->not_empty = 0;
+       wr->buf_ptr->elapsed = wr->buf_ptr->buffered_len = wr->buf_ptr->lines = 0;
 
-       LIST_FOREACH(server->readers[buf_id], reader) {
+       LIST_FOREACH(server->readers[wr->buf_ptr->id], reader) {
                reader->current = 0;
        }
 
@@ -699,35 +733,6 @@ static int service_writer_handle_req_clear(struct logger* server, struct writer*
        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;
@@ -778,24 +783,25 @@ static int service_writer_socket(struct logger* server, struct writer* wr, struc
                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:
+               if (wr->rights) {
+                       switch (msg->request) {
+                       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;
+                               break;
+                       case DLOG_REQ_HANDLE_LOGUTIL:
+                               r = service_writer_handle_req_util(server, wr, msg);
+                               break;
+                       default:
+                               return EINVAL;
+                       }
+               } else {
+                       switch (msg->request) {
+                       case DLOG_REQ_PIPE:
+                               r = service_writer_handle_req_pipe(server, wr, msg);
+                               break;
+                       default:
+                               return EINVAL;
+                       }
                }
 
                if (r <= 0)
@@ -826,7 +832,7 @@ static int service_writer_pipe(struct logger* server, struct writer* wr, struct
 
                entry = (struct logger_entry*)wr->buffer;
                while ((wr->readed >= sizeof(entry->len)) && (entry->len <= wr->readed)) {
-                       buffer_append(entry, server->buffers[entry->buf_id]);
+                       buffer_append(entry, server->buffers[entry->buf_id], server->readers[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);
@@ -862,7 +868,7 @@ static void service_all_readers(struct logger* server, int time_elapsed)
                        buffers[i]->elapsed >= server->max_buffered_time) {
                        LIST_FOREACH(server->readers[i], reader) {
                                r = service_reader(server, reader);
-                               if (r || reader->dumpcount) {
+                               if (r) {
                                        LIST_REMOVE(server->readers[i], reader, reader);
                                        LIST_ITEM_FREE(reader, reader);
                                        if (!reader)
@@ -877,14 +883,14 @@ static void service_all_readers(struct logger* server, int time_elapsed)
        }
 }
 
-static struct logger* logger_create(char* path, struct log_config *conf)
+static struct logger* logger_create (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;
+       char conf_key [MAX_CONF_KEY_LEN];
+       const char *conf_value, *conf_value2;
+       int permissions;
 
        if (!l)
                goto err1;
@@ -893,52 +899,113 @@ static struct logger* logger_create(char* path, struct log_config *conf)
        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);
+       if (!(l->readers = calloc(LOG_ID_MAX, sizeof(struct reader*))))
+               goto err4;
 
        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);
+
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_ctl_sock", log_name_by_id (i));
+               conf_value = log_config_get (conf, conf_key);
                if (!conf_value)
-                       goto err4;
+                       goto err5;
+               strncpy (l->socket_ctl[i].path, conf_value, sizeof(l->socket_ctl[i].path));
 
-               size = atoi (conf_value);
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_ctl_sock_rights", log_name_by_id (i));
+               conf_value = log_config_get (conf, conf_key);
 
-               conf_value = log_config_get(conf, log_name_by_id(i));
+               permissions = parse_permissions (conf_value);
+               if (!permissions)
+                       goto err5;
+
+               if ((l->socket_ctl[i].fd = listen_fd_create(l->socket_ctl[i].path, permissions)) < 0)
+                       goto err5;
+
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_ctl_sock_owner", log_name_by_id (i));
+               conf_value  = log_config_get (conf, conf_key);
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_ctl_sock_group", log_name_by_id (i));
+               conf_value2 = log_config_get (conf, conf_key);
+
+               if (!change_owners (l->socket_ctl[i].path, conf_value, conf_value2))
+                       goto err6;
+
+               l->socket_ctl[i]._entity.type = ENTITY_CONTROL;
+               l->socket_ctl[i].event.data.ptr = &l->socket_ctl[i];
+               l->socket_ctl[i].event.events = EPOLLIN;
+               l->socket_ctl[i].is_control = 1;
+
+               add_fd_loop (l->epollfd, l->socket_ctl[i].fd, &l->socket_ctl[i].event);
+
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_write_sock", log_name_by_id (i));
+               conf_value = log_config_get (conf, conf_key);
                if (!conf_value)
-                       goto err4;
+                       goto err6;
+               strncpy(l->socket_wr[i].path, conf_value, sizeof(l->socket_wr[i].path));
+
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_write_sock_rights", log_name_by_id (i));
+               conf_value = log_config_get (conf, conf_key);
+
+               permissions = parse_permissions (conf_value);
+               if (!permissions)
+                       goto err6;
+
+               if ((l->socket_wr[i].fd = listen_fd_create(l->socket_wr[i].path, permissions)) < 0)
+                       goto err6;
+
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_write_sock_owner", log_name_by_id (i));
+               conf_value  = log_config_get (conf, conf_key);
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_write_sock_group", log_name_by_id (i));
+               conf_value2 = log_config_get (conf, conf_key);
 
-               if (!(l->buffers[i] = buffer_create(i, size, conf_value)))
-                       goto err4;
+               if (!change_owners (l->socket_ctl[i].path, conf_value, conf_value2))
+                       goto err7;
+
+               l->socket_wr[i]._entity.type = ENTITY_CONTROL;
+               l->socket_wr[i].event.data.ptr = &l->socket_wr[i];
+               l->socket_wr[i].event.events = EPOLLIN;
+               l->socket_wr[i].is_control = 0;
+
+               add_fd_loop (l->epollfd, l->socket_wr[i].fd, &l->socket_wr[i].event);
+
+               snprintf (conf_key, MAX_CONF_KEY_LEN, "%s_size", log_name_by_id(i));
+               conf_value = log_config_get (conf, conf_key);
+               if (!conf_value)
+                       goto err7;
+
+               size = atoi (conf_value);
+
+               if (!(l->buffers[i] = buffer_create(i, size)))
+                       goto err7;
+
+               l->socket_wr[i].buf_ptr = l->buffers[i];
+               l->socket_ctl[i].buf_ptr = l->buffers[i];
        }
 
        return l;
 
+err7:
+       close (l->socket_wr[i].fd);
+err6:
+       close (l->socket_ctl[i].fd);
+err5:
+       while (i--) {
+               close (l->socket_ctl[i].fd);
+               close (l->socket_wr[i].fd);
+               buffer_free (&l->buffers[i]);
+       }
+       free (l->readers);
 err4:
-       for (j = 0; j < i; j++)
-               buffer_free(&l->buffers[j]);
+       free (l->buffers);
 err3:
-       free(l->buffers);
+       log_format_free (l->default_format);
 err2:
-       close(l->control.fd);
+       close (l->epollfd);
 err1:
        free(l);
        return NULL;
@@ -968,11 +1035,13 @@ static void logger_free(struct logger* l)
                }
        }
 
-       for (j = 0; j < LOG_ID_MAX; j++)
+       for (j = 0; j < LOG_ID_MAX; j++) {
+               close (l->socket_ctl[j].fd);
+               close (l->socket_wr[j].fd);
                buffer_free(&l->buffers[j]);
+       }
 
        free(l->buffers);
-       close(l->control.fd);
        free(l);
 }
 
@@ -1011,13 +1080,14 @@ static int dispatch_event(struct logger* server, struct fd_entity* entity, struc
                        writer_free((struct writer*)entity);
                }
                break;
-       case ENTITY_CONTROL:
-       {
-               int sock_pipe = accept4(((struct control*)event->data.ptr)->fd, NULL, NULL, SOCK_NONBLOCK);
+       case ENTITY_CONTROL: {
+               struct control* ctrl = (struct control*) event->data.ptr;
+               int sock_pipe = accept4(ctrl->fd, NULL, NULL, SOCK_NONBLOCK);
                if (sock_pipe >= 0) {
-                       struct writer* writer = writer_create(sock_pipe);
+                       struct writer* writer = writer_create(sock_pipe, ctrl->is_control);
 
                        if (writer) {
+                               writer->buf_ptr = ((struct control*)event->data.ptr)->buf_ptr;
                                LIST_ADD(server->writers, writer);
                                add_fd_loop(server->epollfd, writer->socket_fd, &writer->event);
                        } else
@@ -1066,9 +1136,6 @@ static int do_logger(struct logger* server)
        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);
@@ -1096,33 +1163,24 @@ err:
 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;
+       const char * conf_val;
+       char conf_key [MAX_CONF_KEY_LEN];
+       int i = 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);
+       *server = logger_create (&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;
+               sprintf (conf_key, "dlog_logger_conf_%d", i++);
+               conf_val = log_config_get (&conf, conf_key);
+               if (!conf_val)
+                       break;
+               parse_command_line (conf_val, *server, NULL);
        }
 
        log_config_free (&conf);
diff --git a/src/logutil/logutil_doc.h b/src/logutil/logutil_doc.h
new file mode 100644 (file)
index 0000000..710b509
--- /dev/null
@@ -0,0 +1,39 @@
+static void show_help(const char *cmd)
+{
+       fprintf ( stderr,
+               "Usage: %s [options] [filterspecs]"
+#ifdef DLOG_BACKEND_PIPE
+               " [ < filename.raw ]"
+#endif
+               "\noptions include:\n"
+               "  -s              Set default filter to silent.\n"
+               "                  Like specifying filterspec '*:s'\n"
+               "  -f <filename>   Log to file. Default to stdout\n"
+               "  -r [<kbytes>]   Rotate log every kbytes. (16 if unspecified). Requires -f\n"
+               "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
+               "  -v <format>     Sets the log print format, where <format> is one of:\n\n"
+               "                  brief(by default) process tag thread raw time threadtime long\n\n"
+               "  -c              clear (flush) the entire log and exit, conflicts with '-g'\n"
+               "  -d              dump the log and then exit (don't block)\n"
+               "  -t <count>      print only the most recent <count> lines (implies -d)\n"
+               "  -g              get the size of the log's ring buffer and exit, conflicts with '-c'\n"
+               "  -b <buffer>     request alternate ring buffer\n"
+               "                  ('main' (default), 'radio', 'system')\n"
+               "filterspecs are a series of\n<tag>[:priority]\n"
+               "where <tag> is a log component tag (or * for all) and priority is:\n"
+               "  V    Verbose\n"
+               "  D    Debug\n"
+               "  I    Info\n"
+               "  W    Warn\n"
+               "  E    Error\n"
+               "  F    Fatal\n"
+               "  S    Silent (supress all output)\n"
+               "\n'*' means '*:D' and <tag> by itself means <tag>:V\n"
+               "If no filterspec is found, filter defaults to '*:I'\n\n"
+
+#ifdef DLOG_BACKEND_PIPE
+               "You can also redirect .raw files generated by daemon to stdin to read them.\n\n"
+#endif
+
+       , cmd);
+}
index 71d6d57..0f3ac51 100755 (executable)
@@ -35,6 +35,8 @@
 #include <dlog_ioctl.h>
 #include <logconfig.h>
 
+#include "logutil_doc.h"
+
 #define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
 #define DEFAULT_MAX_ROTATED_LOGS 4
 #define MAX_QUEUED 4096
@@ -306,41 +308,6 @@ static int set_log_format(const char * formatString)
        return 0;
 }
 
-static void show_help(const char *cmd)
-{
-       fprintf(stderr, "Usage: %s [options] [filterspecs]\n", cmd);
-
-       fprintf(stderr, "options include:\n"
-                       "  -s              Set default filter to silent.\n"
-                       "                  Like specifying filterspec '*:s'\n"
-                       "  -f <filename>   Log to file. Default to stdout\n"
-                       "  -r [<kbytes>]   Rotate log every kbytes. (16 if unspecified). Requires -f\n"
-                       "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
-                       "  -v <format>     Sets the log print format, where <format> is one of:\n\n"
-                       "                  brief(by default) process tag thread raw time threadtime long\n\n"
-                       "  -c              clear (flush) the entire log and exit, conflicts with '-g'\n"
-                       "  -d              dump the log and then exit (don't block)\n"
-                       "  -t <count>      print only the most recent <count> lines (implies -d)\n"
-                       "  -g              get the size of the log's ring buffer and exit, conflicts with '-c'\n"
-                       "  -b <buffer>     request alternate ring buffer\n"
-                       "                  ('main' (default), 'radio', 'system')");
-
-
-       fprintf(stderr, "\nfilterspecs are a series of \n"
-                       "  <tag>[:priority]\n\n"
-                       "where <tag> is a log component tag (or * for all) and priority is:\n"
-                       "  V    Verbose\n"
-                       "  D    Debug\n"
-                       "  I    Info\n"
-                       "  W    Warn\n"
-                       "  E    Error\n"
-                       "  F    Fatal\n"
-                       "  S    Silent (supress all output)\n"
-                       "\n'*' means '*:D' and <tag> by itself means <tag>:V\n"
-                       "If no filterspec is found, filter defaults to '*:I'\n\n");
-}
-
-
 /*
  * free one log_device_t and it doesn't take care of chain so it
  * may break the chain list
index c77dc50..58b987a 100644 (file)
 #include <logprint.h>
 #include <log_file.h>
 #include <logconfig.h>
+#include <queued_entry.h>
+
+#include "logutil_doc.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;
+/* The size (in entries) of the sorting buffer. */
+#define SORT_BUFFER_SIZE 16384
+
+/* How large (in bytes) the pipe receiving buffer is. */
+#define RECEIVE_BUFFER_SIZE 16384
+
+static int buf_id = 0;
+static int sock_fd = -1;
+static int sort_timeout = 1000; // ms
+static log_format * log_fmt;
+
+static struct sorting_vector {
+       struct logger_entry * data [SORT_BUFFER_SIZE];
+       int size;
+       int last_processed;
+} logs;
+
+static void push_log (struct logger_entry * p)
+{
+       int i;
+       log_entry entry;
+
+       log_process_log_buffer (p, & entry);
+       if (!log_should_print_line (log_fmt, entry.tag, entry.priority))
+               return;
+
+       if ((logs.size + 1) % SORT_BUFFER_SIZE == logs.last_processed) {
+               free (logs.data [logs.last_processed]);
+               logs.last_processed = (logs.last_processed + 1) % SORT_BUFFER_SIZE;
+       }
+
+       for (i = logs.size; i > logs.last_processed || (logs.size < logs.last_processed && i <= logs.size); --i) {
+               struct logger_entry * e = logs.data[(i ?: SORT_BUFFER_SIZE)-1];
+               if (e->sec < p->sec || (e->sec == p->sec && e->nsec < p->nsec)) {
+                       logs.data[i] = p;
+                       logs.size = (logs.size + 1) % SORT_BUFFER_SIZE;
+                       return;
+               } else {
+                       logs.data[i] = e;
+               }
+               if (!i)
+                       i = SORT_BUFFER_SIZE;
+       }
+
+       logs.data[i] = p;
+       logs.size = (logs.size + 1) % SORT_BUFFER_SIZE;
+}
 
 static int connect_sock(const char * path)
 {
@@ -64,13 +112,12 @@ static int connect_sock(const char * path)
 
 static int do_clear ()
 {
-       const int size = sizeof(struct dlog_control_msg) + 1;
+       const int size = sizeof(struct dlog_control_msg);
        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;
@@ -102,56 +149,199 @@ static int send_logger_request (int argc, char ** argv)
        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 += 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);
+       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)
+static int process_log (struct logger_entry * e, const struct timespec * now)
+{
+       int s = now->tv_sec - e->sec;
+       int ns = now->tv_nsec - e->nsec;
+
+       if (ns < 0) {
+               ns += 1000000000;
+               -- s;
+       }
+
+       if (sort_timeout < (s*1000 + ns/1000000)) {
+               log_entry entry;
+               log_process_log_buffer (e, & entry);
+               log_print_log_line (log_fmt, 1, & entry);
+               return 1;
+       } else
+               return 0;
+}
+
+static void handle_pipe (int pipe_fd, int dump)
 {
-       char buff [LOG_MAX_SIZE];
+       char buff [RECEIVE_BUFFER_SIZE];
+       int index = 0;
+       int filled = 1;
        int r;
+       int accepting_logs = 1;
+       struct timespec start_time;
+
+       int epollfd;
+       struct epoll_event ev = { .events = EPOLLIN, .data.fd = pipe_fd };
+
+       epollfd = epoll_create1(0);
+       epoll_ctl (epollfd, EPOLL_CTL_ADD, pipe_fd, &ev);
+
+       clock_gettime (CLOCK_MONOTONIC, &start_time);
+
+       fcntl (pipe_fd, F_SETFL, fcntl (pipe_fd, F_GETFL, 0) | O_NONBLOCK);
+
        for (;;) {
-               r = read(pipe_fd, buff, LOG_MAX_SIZE);
-               if (r <= 0 && errno != EAGAIN)
-                       return;
-               buff[r] = '\0';
-               printf ("%s", buff);
+               struct logger_entry * e;
+               struct timespec now;
+               clock_gettime (CLOCK_MONOTONIC, & now);
+
+               if (dump > 0) {
+                       if (!accepting_logs) {
+                               int available = logs.size - logs.last_processed;
+                               if (available < 0)
+                                       available += SORT_BUFFER_SIZE;
+                               if (dump < available)
+                                       available = dump;
+                               logs.last_processed = (logs.size - available);
+                               if (logs.last_processed < 0)
+                                       logs.last_processed += SORT_BUFFER_SIZE;
+                               dump = 0;
+                       }
+               } else
+                       while (!filled && logs.last_processed != logs.size)
+                               if (process_log (logs.data [logs.last_processed], & now)) {
+                                       free (logs.data [logs.last_processed]);
+                                       logs.last_processed = (logs.last_processed + 1) % SORT_BUFFER_SIZE;
+                               } else
+                                       break;
+
+               if (!accepting_logs) {
+                       if (logs.last_processed == logs.size)
+                               break;
+                       else
+                               continue;
+               }
+
+               r = epoll_wait (epollfd, &ev, 1, 100);
+               if (r < 1) {
+                       filled = 0;
+                       continue;
+               }
+
+               r = read (pipe_fd, buff + index, RECEIVE_BUFFER_SIZE - index);
+
+               if (r < 0) {
+                       if (errno != EAGAIN)
+                               return;
+                       else {
+                               filled = 0;
+                               continue;
+                       }
+               }
+
+               if (r == 0) {
+                       filled = 0;
+                       accepting_logs = 0;
+                       continue;
+               }
+
+               filled = 1;
+               index += r;
+
+               e = (struct logger_entry *) buff;
+               while (index > 2 && index >= e->len) {
+                       struct logger_entry * temp = malloc (e->len);
+                       memcpy (temp, buff, e->len);
+                       index -= e->len;
+                       memmove (buff, buff + e->len, RECEIVE_BUFFER_SIZE - e->len);
+                       push_log (temp);
+               }
        }
 }
 
+int handle_stdin (int dump)
+{
+       fd_set readfds;
+       int endian, version;
+       int r;
+       FD_ZERO (&readfds);
+       FD_SET (STDIN_FILENO, &readfds);
+
+       struct timeval timeout;
+       timeout.tv_sec = 0;
+       timeout.tv_usec = 0;
+
+       /* Check whether stdin contains anything.
+       read() would be blocking if nothing was redirected into stdin
+       ergo we need to know about that beforehand. */
+       if (!select (1, &readfds, NULL, NULL, &timeout))
+               return 0;
+
+       r = read (STDIN_FILENO, &endian, 4);
+       if (r <= 0)
+               return 1;
+
+       r = read (STDIN_FILENO, &version, 4);
+       if (r <= 0)
+               return 1;
+
+       handle_pipe (STDIN_FILENO, dump);
+       return 1;
+}
+
 int main (int argc, char ** argv)
 {
        char buffer_name [MAX_CONF_VAL_LEN] = "";
        const char * sock_path;
+       const char * conf_value;
        int pipe_fd;
        struct log_config conf;
        int should_clear = 0;
        int should_getsize = 0;
+       int dump = 0;
        int into_file = 0;
+       int silence = 0;
+       char conf_key [MAX_CONF_KEY_LEN];
+
+       if (argc == 2 && !strcmp ("--help", argv[1])) {
+               show_help (argv[0]);
+               return 1;
+       }
+
+       log_fmt = log_format_new ();
+       log_set_print_format (log_fmt, FORMAT_KERNELTIME);
 
        while (1) {
-               int option = getopt(argc, argv, "cdt:gsf:r:n:v:b:");
+               int option = getopt(argc, argv, "cdt:gsf:r:n:v:b:h");
 
                if (option < 0)
                        break;
 
                switch (option) {
+               case 'h':
+                       show_help (argv[0]);
+                       return 1;
+               case 'd':
+                       dump = -1;
+                       break;
+               case 't':
+                       dump = atoi (optarg);
+                       break;
                case 'c':
                        should_clear = 1;
                        break;
@@ -164,12 +354,30 @@ int main (int argc, char ** argv)
                case 'f':
                        into_file = 1;
                        break;
+               case 'v':
+                       log_set_print_format (log_fmt, log_format_from_string (optarg));
+                       break;
+               case 's':
+                       silence = 1;
+                       break;
                default:
                        // everything else gets relegated to dlog_logger
                        break;
                }
        }
 
+       if (!silence)
+               if (optind < argc)
+                       while (optind < argc)
+                               log_add_filter_string(log_fmt, argv[optind++]);
+               else
+                       log_add_filter_string(log_fmt, "*:D");
+       else
+               log_add_filter_string(log_fmt, "*:s");
+
+       logs.size = 0;
+       logs.last_processed = 0;
+
        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;
@@ -177,16 +385,27 @@ int main (int argc, char ** argv)
 
        log_config_read (&conf);
 
+       conf_value = log_config_get (&conf, "util_sorting_time_window");
+       if (conf_value)
+               sort_timeout = strtol (conf_value, NULL, 10);
+       if (sort_timeout <= 0)
+               sort_timeout = 1000;
+
        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");
+       snprintf (conf_key, sizeof(conf_key), "%s_ctl_sock", log_name_by_id (buf_id));
+
+       if ((sock_path = log_config_get(&conf, conf_key)) == NULL) {
+               printf("Error: dlog config is broken, lacks the \"%s\" entry\n", conf_key);
                return 1;
        }
 
        log_config_free (&conf);
 
+       if (handle_stdin (dump))
+               return 0;
+
        if ((sock_fd = connect_sock (sock_path)) < 0) {
                printf("Error: socket connection failed\n");
                return 1;
@@ -210,7 +429,7 @@ int main (int argc, char ** argv)
                return 1;
        }
 
-       handle_pipe (pipe_fd);
+       handle_pipe (pipe_fd, dump);
 
        return 0;
 }
index f1a578f..7debda4 100644 (file)
@@ -144,6 +144,8 @@ int log_config_write (struct log_config* config, char const * filename)
 
        e = config->begin;
 
+       fprintf (file, "# This file has been autogenerated by dlogctrl\n");
+
        while (e) {
                r = fprintf (file, "%s=%s\n", e->key, e->value);
                if (r < 0) {
index c21faff..d5273cb 100644 (file)
@@ -1,4 +1,4 @@
-#define LOG_TAG "SRPOL_LOGGER"
+#define LOG_TAG "TEST"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -6,10 +6,10 @@
 #include <pthread.h>
 #include <time.h>
 
-#include <dlog.h>
+#include <dlog/dlog.h>
 
 #define THREADS 50
-#define LOGS_PER_THREAD 10000
+#define LOGS_PER_THREAD 1000
 #define TOTAL_LOGS (THREADS * LOGS_PER_THREAD)
 
 #define SECOND_TO_MICROSECOND 1000000
@@ -18,8 +18,12 @@ void * func (void * data)
 {
        int i;
 
-       for (i = 0; i < LOGS_PER_THREAD; ++i)
-               LOGE ("Logging test %d", i);
+       for (i = 0; i < (LOGS_PER_THREAD / 4); ++i) {
+                LOGE ("test");
+               SLOGE ("test");
+               RLOGE ("test");
+               ALOGE ("test");
+       }
 }
 
 int main (int argc, char **argv)
@@ -27,13 +31,13 @@ int main (int argc, char **argv)
        int i = 0;
        struct timespec start, end;
        double elapsed;
-       pthread_t threads [50];
+       pthread_t threads [THREADS];
 
        clock_gettime (CLOCK_MONOTONIC, &start);
 
-       for (i = 0; i < 50; ++i)
+       for (i = 0; i < THREADS; ++i)
                pthread_create(threads + i, NULL, func, NULL);
-       for (i = 0; i < 50; ++i)
+       for (i = 0; i < THREADS; ++i)
                pthread_join (threads[i], NULL);
 
        clock_gettime (CLOCK_MONOTONIC, &end);
index 26acfea..795a614 100755 (executable)
@@ -6,7 +6,7 @@ OKS=0
 TOTAL=0
 
 fail(){
-       ((ERRS++))
+       ((FAILS++))
        ((TOTAL++))
        echo "#$TOTAL: FAILED"
 }
@@ -42,7 +42,9 @@ dlogctrl -k some_test_key -g                         &> /dev/null && fail || ok
 # Start the daemon, add some logs
 dlog_logger -b 99 -t 600 &
 LOGGER=$!
+sleep 1
 test_libdlog 100
+sleep 1
 
 # 19-22: test -d and -t
 dlogutil -d &> /dev/null && ok || fail
@@ -68,7 +70,7 @@ if [ $(dlogutil -s -d | wc -l) -eq 0 ]; then ok; else fail; fi
 # 29: test -g
 dlogutil -g &> /dev/null && ok || fail
 
-# 30-38: test -f, -r and -n
+# 30-37: test -f, -r and -n
 mkdir /tmp/dlog_tests
 dlogutil -f /tmp/dlog_tests/dlog_test_file -d &> /dev/null && ok || fail
 dlogutil -f /tmp/dlog_tests/dlog_rotating_file -r 12 -n 3 && ok || fail # 3 files at 12 KB each
@@ -78,21 +80,16 @@ if [ -e /tmp/dlog_tests/dlog_rotating_file.1 ]; then ok; else fail; fi
 if [ -e /tmp/dlog_tests/dlog_rotating_file.2 ]; then ok; else fail; fi
 if [ -e /tmp/dlog_tests/dlog_rotating_file.3 ]; then ok; else fail; fi
 if [ -e /tmp/dlog_tests/dlog_rotating_file.4 ]; then fail; else ok; fi
-if [ $(wc -l < /tmp/dlog_tests/dlog_test_file) -eq 10 ]; then ok; else fail; fi
 if [ $(du /tmp/dlog_tests/dlog_rotating_file.3 | sed 's/\t\/tmp\/dlog_tests\/dlog_rotating_file\.3//g') -eq 16 ]; then ok; else fail; fi # the actual size is one sector more (so 12 -> 16) because the limit is checked after reaching it, not before
 
 # Test -v
 # TODO
 
-# 39: test library
+# 38: test library
 dlogutil -f /tmp/dlog_tests/dlog_mt_test
 MT_TEST=$!
 test_libdlog && ok || fail
 
-# 40: test multithreading
-sleep 1
-if [ $(grep "Multithreading test 9999" < /tmp/dlog_tests/dlog_mt_test | wc -l) -eq 50 ]; then ok; else fail; fi
-
 # show results and clean up
 
 echo "$OKS / $TOTAL tests passed"