+# 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
#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
};
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
%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
%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
%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
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)
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
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;
}
#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;
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) {
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) {
}
for (;;) {
- int ret = getopt(argc, argv, "pt:k:gs:c");
+ int ret = getopt(argc, argv, "pt:k:gs:ci");
if (ret < 0)
break;
opt.should_set = 1;
snprintf(val, MAX_CONF_VAL_LEN, "%s", optarg);
break;
+ case 'i':
+ opt.help = 1;
+ break;
}
}
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;
--- /dev/null
+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
+ );
+}
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
#include <linux/limits.h>
#include <sys/epoll.h>
}
#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
struct writer* next;
struct writer* prev;
+ struct log_buffer* buf_ptr;
int readed;
char state;
int dumpcount;
struct reader* next;
struct reader* prev;
+ int partial_log_size;
+ char partial_log [LOG_MAX_SIZE];
};
struct log_buffer {
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;
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;
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;
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));
w->event.events = EPOLLIN;
w->socket_fd = fd;
w->_entity.type = ENTITY_WRITER;
+ w->rights = rights;
return 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)
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);
++ 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)
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) {
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;
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) {
}
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);
}
}
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:
* > 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)
{
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;
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;
}
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 (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)
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);
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)
}
}
-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;
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;
}
}
- 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);
}
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
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);
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);
--- /dev/null
+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);
+}
#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
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
#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)
{
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;
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;
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;
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;
return 1;
}
- handle_pipe (pipe_fd);
+ handle_pipe (pipe_fd, dump);
return 0;
}
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) {
-#define LOG_TAG "SRPOL_LOGGER"
+#define LOG_TAG "TEST"
#include <stdio.h>
#include <stdlib.h>
#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
{
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)
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);
TOTAL=0
fail(){
- ((ERRS++))
+ ((FAILS++))
((TOTAL++))
echo "#$TOTAL: FAILED"
}
# 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
# 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
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"