X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fjournald.c;h=47fe6edb0cad2ac85ca42f972837551937c41595;hb=e91af489a25e8bb65016a63f533ae28a505119ef;hp=5d3956ea460157c58ea97ab25f4fddba3cea697e;hpb=fe6521272ba203ec8f0d5a94f0729960b3f90525;p=elogind.git diff --git a/src/journal/journald.c b/src/journal/journald.c index 5d3956ea4..47fe6edb0 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "hashmap.h" #include "journal-file.h" @@ -38,10 +39,23 @@ #include "acl-util.h" #include "cgroup-util.h" #include "list.h" +#include "journal-rate-limit.h" +#include "sd-journal.h" +#include "sd-login.h" +#include "journal-internal.h" #define USER_JOURNALS_MAX 1024 #define STDOUT_STREAMS_MAX 4096 +#define DEFAULT_RATE_LIMIT_INTERVAL (10*USEC_PER_SEC) +#define DEFAULT_RATE_LIMIT_BURST 200 + +#define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC) + +#define RECHECK_VAR_AVAILABLE_USEC (30*USEC_PER_SEC) + +#define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC) + typedef struct StdoutStream StdoutStream; typedef struct Server { @@ -60,10 +74,18 @@ typedef struct Server { char *buffer; size_t buffer_size; - JournalMetrics metrics; - uint64_t max_use; + JournalRateLimit *rate_limit; + + JournalMetrics runtime_metrics; + JournalMetrics system_metrics; + bool compress; + uint64_t cached_available_space; + usec_t cached_available_space_timestamp; + + uint64_t var_available_timestamp; + LIST_HEAD(StdoutStream, stdout_streams); unsigned n_stdout_streams; } Server; @@ -95,6 +117,92 @@ struct StdoutStream { LIST_FIELDS(StdoutStream, stdout_stream); }; +static int server_flush_to_var(Server *s); + +static uint64_t available_space(Server *s) { + char ids[33], *p; + const char *f; + sd_id128_t machine; + struct statvfs ss; + uint64_t sum = 0, avail = 0, ss_avail = 0; + int r; + DIR *d; + usec_t ts; + JournalMetrics *m; + + ts = now(CLOCK_MONOTONIC); + + if (s->cached_available_space_timestamp + RECHECK_AVAILABLE_SPACE_USEC > ts) + return s->cached_available_space; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return 0; + + if (s->system_journal) { + f = "/var/log/journal/"; + m = &s->system_metrics; + } else { + f = "/run/log/journal/"; + m = &s->runtime_metrics; + } + + assert(m); + + p = strappend(f, sd_id128_to_string(machine, ids)); + if (!p) + return 0; + + d = opendir(p); + free(p); + + if (!d) + return 0; + + if (fstatvfs(dirfd(d), &ss) < 0) + goto finish; + + for (;;) { + struct stat st; + struct dirent buf, *de; + int k; + + k = readdir_r(d, &buf, &de); + if (k != 0) { + r = -k; + goto finish; + } + + if (!de) + break; + + if (!dirent_is_file_with_suffix(de, ".journal")) + continue; + + if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + continue; + + sum += (uint64_t) st.st_blocks * (uint64_t) st.st_blksize; + } + + avail = sum >= m->max_use ? 0 : m->max_use - sum; + + ss_avail = ss.f_bsize * ss.f_bavail; + + ss_avail = ss_avail < m->keep_free ? 0 : ss_avail - m->keep_free; + + if (ss_avail < avail) + avail = ss_avail; + + s->cached_available_space = avail; + s->cached_available_space_timestamp = ts; + +finish: + closedir(d); + + return avail; +} + static void fix_perms(JournalFile *f, uid_t uid) { acl_t acl; acl_entry_t entry; @@ -150,8 +258,12 @@ static JournalFile* find_journal(Server *s, uid_t uid) { assert(s); - /* We split up user logs only on /var, not on /run */ - if (!s->system_journal) + /* We split up user logs only on /var, not on /run. If the + * runtime file is open, we write to it exclusively, in order + * to guarantee proper order as soon as we flush /run to + * /var and close the runtime file. */ + + if (s->runtime_journal) return s->runtime_journal; if (uid <= 0) @@ -182,7 +294,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { return s->system_journal; fix_perms(f, uid); - f->metrics = s->metrics; + f->metrics = s->system_metrics; f->compress = s->compress; r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f); @@ -194,14 +306,11 @@ static JournalFile* find_journal(Server *s, uid_t uid) { return f; } -static void server_vacuum(Server *s) { - Iterator i; +static void server_rotate(Server *s) { + JournalFile *f; void *k; - char *p; - char ids[33]; - sd_id128_t machine; + Iterator i; int r; - JournalFile *f; log_info("Rotating..."); @@ -224,6 +333,13 @@ static void server_vacuum(Server *s) { else hashmap_replace(s->user_journals, k, f); } +} + +static void server_vacuum(Server *s) { + char *p; + char ids[33]; + sd_id128_t machine; + int r; log_info("Vacuuming..."); @@ -233,33 +349,89 @@ static void server_vacuum(Server *s) { return; } - if (asprintf(&p, "/var/log/journal/%s", sd_id128_to_string(machine, ids)) < 0) { - log_error("Out of memory."); - return; + sd_id128_to_string(machine, ids); + + if (s->system_journal) { + if (asprintf(&p, "/var/log/journal/%s", ids) < 0) { + log_error("Out of memory."); + return; + } + + r = journal_directory_vacuum(p, s->system_metrics.max_use, s->system_metrics.keep_free); + if (r < 0 && r != -ENOENT) + log_error("Failed to vacuum %s: %s", p, strerror(-r)); + free(p); } - r = journal_directory_vacuum(p, s->max_use, s->metrics.keep_free); - if (r < 0 && r != -ENOENT) - log_error("Failed to vacuum %s: %s", p, strerror(-r)); - free(p); - if (asprintf(&p, "/run/log/journal/%s", ids) < 0) { - log_error("Out of memory."); - return; + if (s->runtime_journal) { + if (asprintf(&p, "/run/log/journal/%s", ids) < 0) { + log_error("Out of memory."); + return; + } + + r = journal_directory_vacuum(p, s->runtime_metrics.max_use, s->runtime_metrics.keep_free); + if (r < 0 && r != -ENOENT) + log_error("Failed to vacuum %s: %s", p, strerror(-r)); + free(p); } - r = journal_directory_vacuum(p, s->max_use, s->metrics.keep_free); - if (r < 0 && r != -ENOENT) - log_error("Failed to vacuum %s: %s", p, strerror(-r)); - free(p); + s->cached_available_space_timestamp = 0; +} + +static char *shortened_cgroup_path(pid_t pid) { + int r; + char *process_path, *init_path, *path; + + assert(pid > 0); + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &process_path); + if (r < 0) + return NULL; + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &init_path); + if (r < 0) { + free(process_path); + return NULL; + } + + if (endswith(init_path, "/system")) + init_path[strlen(init_path) - 7] = 0; + else if (streq(init_path, "/")) + init_path[0] = 0; + + if (startswith(process_path, init_path)) { + char *p; + + p = strdup(process_path + strlen(init_path)); + if (!p) { + free(process_path); + free(init_path); + return NULL; + } + path = p; + } else { + path = process_path; + process_path = NULL; + } + + free(process_path); + free(init_path); + + return path; } -static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv) { +static void dispatch_message_real(Server *s, + struct iovec *iovec, unsigned n, unsigned m, + struct ucred *ucred, + struct timeval *tv) { + char *pid = NULL, *uid = NULL, *gid = NULL, *source_time = NULL, *boot_id = NULL, *machine_id = NULL, *comm = NULL, *cmdline = NULL, *hostname = NULL, *audit_session = NULL, *audit_loginuid = NULL, - *exe = NULL, *cgroup = NULL; + *exe = NULL, *cgroup = NULL, *session = NULL, + *owner_uid = NULL, *service = NULL; char idbuf[33]; sd_id128_t id; @@ -270,16 +442,13 @@ static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne bool vacuumed = false; assert(s); - assert(iovec || n == 0); - - if (n == 0) - return; - - assert(n + 13 <= m); + assert(iovec); + assert(n > 0); + assert(n + 16 <= m); if (ucred) { - uint32_t session; - char *path; + uint32_t audit; + uid_t owner; realuid = ucred->uid; @@ -295,30 +464,33 @@ static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne r = get_process_comm(ucred->pid, &t); if (r >= 0) { comm = strappend("_COMM=", t); + free(t); + if (comm) IOVEC_SET_STRING(iovec[n++], comm); - free(t); } r = get_process_exe(ucred->pid, &t); if (r >= 0) { exe = strappend("_EXE=", t); + free(t); + if (comm) IOVEC_SET_STRING(iovec[n++], exe); - free(t); } r = get_process_cmdline(ucred->pid, LINE_MAX, false, &t); if (r >= 0) { cmdline = strappend("_CMDLINE=", t); + free(t); + if (cmdline) IOVEC_SET_STRING(iovec[n++], cmdline); - free(t); } - r = audit_session_from_pid(ucred->pid, &session); + r = audit_session_from_pid(ucred->pid, &audit); if (r >= 0) - if (asprintf(&audit_session, "_AUDIT_SESSION=%lu", (unsigned long) session) >= 0) + if (asprintf(&audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit) >= 0) IOVEC_SET_STRING(iovec[n++], audit_session); r = audit_loginuid_from_pid(ucred->pid, &loginuid); @@ -326,13 +498,34 @@ static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne if (asprintf(&audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0) IOVEC_SET_STRING(iovec[n++], audit_loginuid); - r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, ucred->pid, &path); - if (r >= 0) { - cgroup = strappend("_SYSTEMD_CGROUP=", path); + t = shortened_cgroup_path(ucred->pid); + if (t) { + cgroup = strappend("_SYSTEMD_CGROUP=", t); + free(t); + if (cgroup) IOVEC_SET_STRING(iovec[n++], cgroup); - free(path); } + + if (sd_pid_get_session(ucred->pid, &t) >= 0) { + session = strappend("_SYSTEMD_SESSION=", t); + free(t); + + if (session) + IOVEC_SET_STRING(iovec[n++], session); + } + + if (sd_pid_get_service(ucred->pid, &t) >= 0) { + service = strappend("_SYSTEMD_SERVICE=", t); + free(t); + + if (service) + IOVEC_SET_STRING(iovec[n++], service); + } + + if (sd_pid_get_owner_uid(ucred->uid, &owner) >= 0) + if (asprintf(&owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner) >= 0) + IOVEC_SET_STRING(iovec[n++], owner_uid); } if (tv) { @@ -357,13 +550,15 @@ static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne t = gethostname_malloc(); if (t) { hostname = strappend("_HOSTNAME=", t); + free(t); if (hostname) IOVEC_SET_STRING(iovec[n++], hostname); - free(t); } assert(n <= m); + server_flush_to_var(s); + retry: f = find_journal(s, realuid == 0 ? 0 : loginuid); if (!f) @@ -374,6 +569,7 @@ retry: if (r == -E2BIG && !vacuumed) { log_info("Allocation limit reached."); + server_rotate(s); server_vacuum(s); vacuumed = true; @@ -398,11 +594,80 @@ retry: free(audit_session); free(audit_loginuid); free(cgroup); + free(session); + free(owner_uid); + free(service); +} + +static void dispatch_message(Server *s, + struct iovec *iovec, unsigned n, unsigned m, + struct ucred *ucred, + struct timeval *tv, + int priority) { + int rl; + char *path = NULL, *c; + + assert(s); + assert(iovec || n == 0); + + if (n == 0) + return; + + if (!ucred) + goto finish; + + path = shortened_cgroup_path(ucred->pid); + if (!path) + goto finish; + + /* example: /user/lennart/3/foobar + * /system/dbus.service/foobar + * + * So let's cut of everything past the third /, since that is + * wher user directories start */ + + c = strchr(path, '/'); + if (c) { + c = strchr(c+1, '/'); + if (c) { + c = strchr(c+1, '/'); + if (c) + *c = 0; + } + } + + rl = journal_rate_limit_test(s->rate_limit, path, priority, available_space(s)); + + if (rl == 0) { + free(path); + return; + } + + if (rl > 1) { + int j = 0; + char suppress_message[LINE_MAX]; + struct iovec suppress_iovec[18]; + + /* Write a suppression message if we suppressed something */ + + snprintf(suppress_message, sizeof(suppress_message), "MESSAGE=Suppressed %u messages from %s", rl - 1, path); + char_array_0(suppress_message); + + IOVEC_SET_STRING(suppress_iovec[j++], "PRIORITY=5"); + IOVEC_SET_STRING(suppress_iovec[j++], suppress_message); + + dispatch_message_real(s, suppress_iovec, j, ELEMENTSOF(suppress_iovec), NULL, NULL); + } + + free(path); + +finish: + dispatch_message_real(s, iovec, n, m, ucred, tv); } static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL; - struct iovec iovec[16]; + struct iovec iovec[19]; unsigned n = 0; int priority = LOG_USER | LOG_INFO; @@ -422,7 +687,7 @@ static void process_syslog_message(Server *s, const char *buf, struct ucred *ucr if (message) IOVEC_SET_STRING(iovec[n++], message); - dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv); + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, priority & LOG_PRIMASK); free(message); free(syslog_facility); @@ -469,6 +734,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ unsigned n = 0, m = 0, j; const char *p; size_t remaining; + int priority = LOG_INFO; assert(s); assert(buffer || n == 0); @@ -489,8 +755,9 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ if (e == p) { /* Entry separator */ - dispatch_message(s, iovec, n, m, ucred, tv); + dispatch_message(s, iovec, n, m, ucred, tv, priority); n = 0; + priority = LOG_INFO; p++; remaining--; @@ -507,11 +774,11 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ /* A property follows */ - if (n+13 >= m) { + if (n+16 >= m) { struct iovec *c; unsigned u; - u = MAX((n+13U) * 2U, 4U); + u = MAX((n+16U) * 2U, 4U); c = realloc(iovec, u * sizeof(struct iovec)); if (!c) { log_error("Out of memory"); @@ -532,6 +799,15 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ iovec[n].iov_base = (char*) p; iovec[n].iov_len = e - p; n++; + + /* We need to determine the priority + * of this entry for the rate limiting + * logic */ + if (e - p == 10 && + memcmp(p, "PRIORITY=", 10) == 0 && + p[10] >= '0' && + p[10] <= '9') + priority = p[10] - '0'; } remaining -= (e - p) + 1; @@ -577,7 +853,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ } } - dispatch_message(s, iovec, n, m, ucred, tv); + dispatch_message(s, iovec, n, m, ucred, tv, priority); for (j = 0; j < n; j++) if (iovec[j].iov_base < buffer || @@ -586,7 +862,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ } static int stdout_stream_log(StdoutStream *s, const char *p, size_t l) { - struct iovec iovec[15]; + struct iovec iovec[18]; char *message = NULL, *syslog_priority = NULL; unsigned n = 0; size_t tag_len; @@ -630,7 +906,7 @@ static int stdout_stream_log(StdoutStream *s, const char *p, size_t l) { n++; } - dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL); + dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, priority); if (s->tee_console) { int console; @@ -889,6 +1165,243 @@ fail: return r; } +static int system_journal_open(Server *s) { + int r; + char *fn; + sd_id128_t machine; + char ids[33]; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + sd_id128_to_string(machine, ids); + + if (!s->system_journal) { + + /* First try to create the machine path, but not the prefix */ + fn = strappend("/var/log/journal/", ids); + if (!fn) + return -ENOMEM; + (void) mkdir(fn, 0755); + free(fn); + + /* The create the system journal file */ + fn = join("/var/log/journal/", ids, "/system.journal", NULL); + if (!fn) + return -ENOMEM; + + r = journal_file_open(fn, O_RDWR|O_CREAT, 0640, NULL, &s->system_journal); + free(fn); + + if (r >= 0) { + journal_default_metrics(&s->system_metrics, s->system_journal->fd); + + s->system_journal->metrics = s->system_metrics; + s->system_journal->compress = s->compress; + + fix_perms(s->system_journal, 0); + } else if (r < 0) { + + if (r != -ENOENT && r != -EROFS) + log_warning("Failed to open system journal: %s", strerror(-r)); + + r = 0; + } + } + + if (!s->runtime_journal) { + + fn = join("/run/log/journal/", ids, "/system.journal", NULL); + if (!fn) + return -ENOMEM; + + if (s->system_journal) { + + /* Try to open the runtime journal, but only + * if it already exists, so that we can flush + * it into the system journal */ + + r = journal_file_open(fn, O_RDWR, 0640, NULL, &s->runtime_journal); + free(fn); + + if (r < 0) { + if (r != -ENOENT) + log_warning("Failed to open runtime journal: %s", strerror(-r)); + + r = 0; + } + + } else { + + /* OK, we really need the runtime journal, so create + * it if necessary. */ + + (void) mkdir_parents(fn, 0755); + r = journal_file_open(fn, O_RDWR|O_CREAT, 0640, NULL, &s->runtime_journal); + free(fn); + + if (r < 0) { + log_error("Failed to open runtime journal: %s", strerror(-r)); + return r; + } + } + + if (s->runtime_journal) { + journal_default_metrics(&s->runtime_metrics, s->runtime_journal->fd); + + s->runtime_journal->metrics = s->runtime_metrics; + s->runtime_journal->compress = s->compress; + + fix_perms(s->runtime_journal, 0); + } + } + + return r; +} + +static int server_flush_to_var(Server *s) { + char path[] = "/run/log/journal/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + Object *o = NULL; + int r; + sd_id128_t machine; + sd_journal *j; + usec_t ts; + + assert(s); + + if (!s->runtime_journal) + return 0; + + ts = now(CLOCK_MONOTONIC); + if (s->var_available_timestamp + RECHECK_VAR_AVAILABLE_USEC > ts) + return 0; + + s->var_available_timestamp = ts; + + system_journal_open(s); + + if (!s->system_journal) + return 0; + + r = sd_id128_get_machine(&machine); + if (r < 0) { + log_error("Failed to get machine id: %s", strerror(-r)); + return r; + } + + r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY); + if (r < 0) { + log_error("Failed to read runtime journal: %s", strerror(-r)); + return r; + } + + SD_JOURNAL_FOREACH(j) { + JournalFile *f; + + f = j->current_file; + assert(f && f->current_offset > 0); + + r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); + if (r < 0) { + log_error("Can't read entry: %s", strerror(-r)); + goto finish; + } + + r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); + if (r == -E2BIG) { + log_info("Allocation limit reached."); + + journal_file_post_change(s->system_journal); + server_rotate(s); + server_vacuum(s); + + r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); + } + + if (r < 0) { + log_error("Can't write entry: %s", strerror(-r)); + goto finish; + } + } + +finish: + journal_file_post_change(s->system_journal); + + journal_file_close(s->runtime_journal); + s->runtime_journal = NULL; + + if (r >= 0) { + sd_id128_to_string(machine, path + 17); + rm_rf(path, false, true, false); + } + + return r; +} + +static void forward_syslog(Server *s, const void *buffer, size_t length, struct ucred *ucred, struct timeval *tv) { + struct msghdr msghdr; + struct iovec iovec; + struct cmsghdr *cmsg; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(struct timeval))]; + } control; + union sockaddr_union sa; + + assert(s); + + zero(msghdr); + + zero(iovec); + iovec.iov_base = (void*) buffer; + iovec.iov_len = length; + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path)); + msghdr.msg_name = &sa; + msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path); + + zero(control); + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred)); + msghdr.msg_controllen = cmsg->cmsg_len; + + /* Forward the syslog message we received via /dev/log to + * /run/systemd/syslog. Unfortunately we currently can't set + * the SO_TIMESTAMP auxiliary data, and hence we don't. */ + + if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) + return; + + if (errno == ESRCH) { + struct ucred u; + + /* Hmm, presumably the sender process vanished + * by now, so let's fix it as good as we + * can, and retry */ + + u = *ucred; + u.pid = getpid(); + memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred)); + + if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) + return; + } + + log_debug("Failed to forward syslog message: %m"); +} + static int process_event(Server *s, struct epoll_event *ev) { assert(s); @@ -913,6 +1426,11 @@ static int process_event(Server *s, struct epoll_event *ev) { return -errno; } + if (sfsi.ssi_signo == SIGUSR1) { + server_flush_to_var(s); + return 0; + } + log_debug("Received SIG%s", signal_to_string(sfsi.ssi_signo)); return 0; @@ -1004,6 +1522,7 @@ static int process_event(Server *s, struct epoll_event *ev) { else s->buffer[n] = 0; + forward_syslog(s, s->buffer, n, ucred, tv); process_syslog_message(s, strstrip(s->buffer), ucred, tv); } else process_native_message(s, s->buffer, n, ucred, tv); @@ -1048,70 +1567,11 @@ static int process_event(Server *s, struct epoll_event *ev) { return 0; } -static int system_journal_open(Server *s) { - int r; - char *fn; - sd_id128_t machine; - char ids[33]; - - r = sd_id128_get_machine(&machine); - if (r < 0) - return r; - - /* First try to create the machine path, but not the prefix */ - fn = strappend("/var/log/journal/", sd_id128_to_string(machine, ids)); - if (!fn) - return -ENOMEM; - (void) mkdir(fn, 0755); - free(fn); - - /* The create the system journal file */ - fn = join("/var/log/journal/", ids, "/system.journal", NULL); - if (!fn) - return -ENOMEM; - - r = journal_file_open(fn, O_RDWR|O_CREAT, 0640, NULL, &s->system_journal); - free(fn); - - if (r >= 0) { - s->system_journal->metrics = s->metrics; - s->system_journal->compress = s->compress; - - fix_perms(s->system_journal, 0); - return r; - } - - if (r < 0 && r != -ENOENT) { - log_error("Failed to open system journal: %s", strerror(-r)); - return r; - } - - /* /var didn't work, so try /run, but this time we - * create the prefix too */ - fn = join("/run/log/journal/", ids, "/system.journal", NULL); - if (!fn) - return -ENOMEM; - - (void) mkdir_parents(fn, 0755); - r = journal_file_open(fn, O_RDWR|O_CREAT, 0640, NULL, &s->runtime_journal); - free(fn); - - if (r < 0) { - log_error("Failed to open runtime journal: %s", strerror(-r)); - return r; - } - - s->runtime_journal->metrics = s->metrics; - s->runtime_journal->compress = s->compress; - - fix_perms(s->runtime_journal, 0); - return r; -} - static int open_syslog_socket(Server *s) { union sockaddr_union sa; int one, r; struct epoll_event ev; + struct timeval tv; assert(s); @@ -1125,7 +1585,7 @@ static int open_syslog_socket(Server *s) { zero(sa); sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path)); + strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); unlink(sa.un.sun_path); @@ -1152,6 +1612,15 @@ static int open_syslog_socket(Server *s) { return -errno; } + /* Since we use the same socket for forwarding this to some + * other syslog implementation, make sure we don't hang + * forever */ + timeval_store(&tv, SYSLOG_TIMEOUT_USEC); + if (setsockopt(s->syslog_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { + log_error("SO_SNDTIMEO failed: %m"); + return -errno; + } + zero(ev); ev.events = EPOLLIN; ev.data.fd = s->syslog_fd; @@ -1271,7 +1740,7 @@ static int open_signalfd(Server *s) { assert(s); assert_se(sigemptyset(&mask) == 0); - sigset_add_many(&mask, SIGINT, SIGTERM, -1); + sigset_add_many(&mask, SIGINT, SIGTERM, SIGUSR1, -1); assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); @@ -1299,12 +1768,11 @@ static int server_init(Server *s) { zero(*s); s->syslog_fd = s->native_fd = s->stdout_fd = s->signal_fd = s->epoll_fd = -1; - s->metrics.max_size = DEFAULT_MAX_SIZE; - s->metrics.min_size = DEFAULT_MIN_SIZE; - s->metrics.keep_free = DEFAULT_KEEP_FREE; - s->max_use = DEFAULT_MAX_USE; s->compress = true; + memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics)); + memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics)); + s->user_journals = hashmap_new(trivial_hash_func, trivial_compare_func); if (!s->user_journals) { log_error("Out of memory."); @@ -1378,6 +1846,10 @@ static int server_init(Server *s) { if (r < 0) return r; + s->rate_limit = journal_rate_limit_new(DEFAULT_RATE_LIMIT_INTERVAL, DEFAULT_RATE_LIMIT_BURST); + if (!s->rate_limit) + return -ENOMEM; + return 0; } @@ -1413,6 +1885,11 @@ static void server_done(Server *s) { if (s->stdout_fd >= 0) close_nointr_nofail(s->stdout_fd); + + if (s->rate_limit) + journal_rate_limit_free(s->rate_limit); + + free(s->buffer); } int main(int argc, char *argv[]) { @@ -1430,7 +1907,6 @@ int main(int argc, char *argv[]) { } log_set_target(LOG_TARGET_CONSOLE); - log_set_max_level(LOG_DEBUG); log_parse_environment(); log_open(); @@ -1446,6 +1922,9 @@ int main(int argc, char *argv[]) { "READY=1\n" "STATUS=Processing requests..."); + server_vacuum(&server); + server_flush_to_var(&server); + for (;;) { struct epoll_event event;