X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fjournal%2Fjournald.c;h=c429896ac40cbd4aa9cb8530da247fe853743fbb;hp=39263bfe4c41530e9431396f91886b4f68d95cba;hb=a6903061530cac5fbaa99a080a93221c02c349f9;hpb=5c3759bf8a3d418fa877e6a278f3150f404745b2 diff --git a/src/journal/journald.c b/src/journal/journald.c index 39263bfe4..c429896ac 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -6,16 +6,16 @@ Copyright 2011 Lennart Poettering systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. systemd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ @@ -35,6 +35,7 @@ #include #include +#include "mkdir.h" #include "hashmap.h" #include "journal-file.h" #include "socket-util.h" @@ -45,6 +46,7 @@ #include "conf-parser.h" #include "journald.h" #include "virt.h" +#include "missing.h" #ifdef HAVE_ACL #include @@ -66,8 +68,6 @@ #define RECHECK_VAR_AVAILABLE_USEC (30*USEC_PER_SEC) -#define SYSLOG_TIMEOUT_USEC (250*USEC_PER_MSEC) - #define N_IOVEC_META_FIELDS 17 #define ENTRY_SIZE_MAX (1024*1024*32) @@ -89,6 +89,9 @@ struct StdoutStream { int fd; struct ucred ucred; +#ifdef HAVE_SELINUX + security_context_t security_context; +#endif char *identifier; int priority; @@ -151,24 +154,25 @@ static uint64_t available_space(Server *s) { for (;;) { struct stat st; struct dirent buf, *de; - int k; - k = readdir_r(d, &buf, &de); - if (k != 0) { - r = -k; - goto finish; - } + r = readdir_r(d, &buf, &de); + if (r != 0) + break; if (!de) break; - if (!dirent_is_file_with_suffix(de, ".journal")) + if (!endswith(de->d_name, ".journal") && + !endswith(de->d_name, ".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; + if (!S_ISREG(st.st_mode)) + continue; + + sum += (uint64_t) st.st_blocks * 512UL; } avail = sum >= m->max_use ? 0 : m->max_use - sum; @@ -298,15 +302,13 @@ static JournalFile* find_journal(Server *s, uid_t uid) { journal_file_close(f); } - r = journal_file_open(p, O_RDWR|O_CREAT, 0640, s->system_journal, &f); + r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, s->system_journal, &f); free(p); if (r < 0) return s->system_journal; server_fix_perms(s, f, uid); - f->metrics = s->system_metrics; - f->compress = s->compress; r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f); if (r < 0) { @@ -328,21 +330,37 @@ static void server_rotate(Server *s) { if (s->runtime_journal) { r = journal_file_rotate(&s->runtime_journal); if (r < 0) - log_error("Failed to rotate %s: %s", s->runtime_journal->path, strerror(-r)); + if (s->runtime_journal) + log_error("Failed to rotate %s: %s", s->runtime_journal->path, strerror(-r)); + else + log_error("Failed to create new runtime journal: %s", strerror(-r)); + else + server_fix_perms(s, s->runtime_journal, 0); } if (s->system_journal) { r = journal_file_rotate(&s->system_journal); if (r < 0) - log_error("Failed to rotate %s: %s", s->system_journal->path, strerror(-r)); + if (s->system_journal) + log_error("Failed to rotate %s: %s", s->system_journal->path, strerror(-r)); + else + log_error("Failed to create new system journal: %s", strerror(-r)); + + else + server_fix_perms(s, s->system_journal, 0); } HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) { r = journal_file_rotate(&f); if (r < 0) - log_error("Failed to rotate %s: %s", f->path, strerror(-r)); - else + if (f->path) + log_error("Failed to rotate %s: %s", f->path, strerror(-r)); + else + log_error("Failed to create user journal: %s", strerror(-r)); + else { hashmap_replace(s->user_journals, k, f); + server_fix_perms(s, s->system_journal, PTR_TO_UINT32(k)); + } } } @@ -432,10 +450,12 @@ static char *shortened_cgroup_path(pid_t pid) { return path; } -static void dispatch_message_real(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, + const char *label, size_t label_len) { char *pid = NULL, *uid = NULL, *gid = NULL, *source_time = NULL, *boot_id = NULL, *machine_id = NULL, @@ -460,9 +480,6 @@ static void dispatch_message_real(Server *s, if (ucred) { uint32_t audit; uid_t owner; -#ifdef HAVE_SELINUX - security_context_t con; -#endif realuid = ucred->uid; @@ -542,12 +559,24 @@ static void dispatch_message_real(Server *s, IOVEC_SET_STRING(iovec[n++], owner_uid); #ifdef HAVE_SELINUX - if (getpidcon(ucred->pid, &con) >= 0) { - selinux_context = strappend("_SELINUX_CONTEXT=", con); - if (selinux_context) + if (label) { + selinux_context = malloc(sizeof("_SELINUX_CONTEXT=") + label_len); + if (selinux_context) { + memcpy(selinux_context, "_SELINUX_CONTEXT=", sizeof("_SELINUX_CONTEXT=")-1); + memcpy(selinux_context+sizeof("_SELINUX_CONTEXT=")-1, label, label_len); + selinux_context[sizeof("_SELINUX_CONTEXT=")-1+label_len] = 0; IOVEC_SET_STRING(iovec[n++], selinux_context); + } + } else { + security_context_t con; + + if (getpidcon(ucred->pid, &con) >= 0) { + selinux_context = strappend("_SELINUX_CONTEXT=", con); + if (selinux_context) + IOVEC_SET_STRING(iovec[n++], selinux_context); - freecon(con); + freecon(con); + } } #endif } @@ -590,8 +619,20 @@ retry: else { r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL); - if (r == -E2BIG && !vacuumed) { - log_info("Allocation limit reached."); + if ((r == -E2BIG || /* hit limit */ + r == -EFBIG || /* hit fs limit */ + r == -EDQUOT || /* quota hit */ + r == -ENOSPC || /* disk full */ + r == -EBADMSG || /* corrupted */ + r == -ENODATA || /* truncated */ + r == -EHOSTDOWN || /* other machine */ + r == -EPROTONOSUPPORT) && /* unsupported feature */ + !vacuumed) { + + if (r == -E2BIG) + log_info("Allocation limit reached, rotating."); + else + log_warning("Journal file corrupted, rotating."); server_rotate(s); server_vacuum(s); @@ -654,13 +695,14 @@ static void driver_message(Server *s, sd_id128_t message_id, const char *format, ucred.uid = getuid(); ucred.gid = getgid(); - dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL); + dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0); } static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv, + const char *label, size_t label_len, int priority) { int rl; char *path = NULL, *c; @@ -708,7 +750,7 @@ static void dispatch_message(Server *s, free(path); finish: - dispatch_message_real(s, iovec, n, m, ucred, tv); + dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len); } static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) { @@ -1008,7 +1050,7 @@ static void read_identifier(const char **buf, char **identifier, char **pid) { *buf += strspn(*buf, WHITESPACE); } -static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { +static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv, const char *label, size_t label_len) { char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL; struct iovec iovec[N_IOVEC_META_FIELDS + 6]; unsigned n = 0; @@ -1056,7 +1098,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, priority); + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, priority); free(message); free(identifier); @@ -1101,7 +1143,13 @@ static bool valid_user_field(const char *p, size_t l) { return true; } -static void process_native_message(Server *s, const void *buffer, size_t buffer_size, struct ucred *ucred, struct timeval *tv) { +static void process_native_message( + Server *s, + const void *buffer, size_t buffer_size, + struct ucred *ucred, + struct timeval *tv, + const char *label, size_t label_len) { + struct iovec *iovec = NULL; unsigned n = 0, m = 0, j, tn = (unsigned) -1; const char *p; @@ -1110,7 +1158,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ char *identifier = NULL, *message = NULL; assert(s); - assert(buffer || n == 0); + assert(buffer || buffer_size == 0); p = buffer; remaining = buffer_size; @@ -1128,7 +1176,7 @@ 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, priority); + dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, priority); n = 0; priority = LOG_INFO; @@ -1196,11 +1244,11 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ p[17] >= '0' && p[17] <= '9') priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3); - else if (l >= 12 && - memcmp(p, "SYSLOG_IDENTIFIER=", 11) == 0) { + else if (l >= 19 && + memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) { char *t; - t = strndup(p + 11, l - 11); + t = strndup(p + 18, l - 18); if (t) { free(identifier); identifier = t; @@ -1221,6 +1269,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ p = e + 1; continue; } else { + le64_t l_le; uint64_t l; char *k; @@ -1229,8 +1278,8 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ break; } - memcpy(&l, e + 1, sizeof(uint64_t)); - l = le64toh(l); + memcpy(&l_le, e + 1, sizeof(uint64_t)); + l = le64toh(l_le); if (remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || e[1+sizeof(uint64_t)+l] != '\n') { @@ -1277,7 +1326,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ forward_console(s, identifier, message, ucred); } - dispatch_message(s, iovec, n, m, ucred, tv, priority); + dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, priority); finish: for (j = 0; j < n; j++) { @@ -1289,11 +1338,18 @@ finish: free(iovec[j].iov_base); } + free(iovec); free(identifier); free(message); } -static void process_native_file(Server *s, int fd, struct ucred *ucred, struct timeval *tv) { +static void process_native_file( + Server *s, + int fd, + struct ucred *ucred, + struct timeval *tv, + const char *label, size_t label_len) { + struct stat st; void *p; ssize_t n; @@ -1334,7 +1390,7 @@ static void process_native_file(Server *s, int fd, struct ucred *ucred, struct t if (n < 0) log_error("Failed to read file, ignoring: %s", strerror(-n)); else if (n > 0) - process_native_message(s, p, n, ucred, tv); + process_native_message(s, p, n, ucred, tv, label, label_len); free(p); } @@ -1344,6 +1400,8 @@ static int stdout_stream_log(StdoutStream *s, const char *p) { char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL; unsigned n = 0; int priority; + char *label = NULL; + size_t label_len = 0; assert(s); assert(p); @@ -1384,7 +1442,14 @@ static int stdout_stream_log(StdoutStream *s, const char *p) { if (message) IOVEC_SET_STRING(iovec[n++], message); - dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, priority); +#ifdef HAVE_SELINUX + if (s->security_context) { + label = (char*) s->security_context; + label_len = strlen((char*) s->security_context); + } +#endif + + dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, label, label_len, priority); free(message); free(syslog_priority); @@ -1405,10 +1470,14 @@ static int stdout_stream_line(StdoutStream *s, char *p) { switch (s->state) { case STDOUT_STREAM_IDENTIFIER: - s->identifier = strdup(p); - if (!s->identifier) { - log_error("Out of memory"); - return -ENOMEM; + if (isempty(p)) + s->identifier = NULL; + else { + s->identifier = strdup(p); + if (!s->identifier) { + log_error("Out of memory"); + return -ENOMEM; + } } s->state = STDOUT_STREAM_PRIORITY; @@ -1574,6 +1643,11 @@ static void stdout_stream_free(StdoutStream *s) { close_nointr_nofail(s->fd); } +#ifdef HAVE_SELINUX + if (s->security_context) + freecon(s->security_context); +#endif + free(s->identifier); free(s); } @@ -1617,6 +1691,11 @@ static int stdout_stream_new(Server *s) { goto fail; } +#ifdef HAVE_SELINUX + if (getpeercon(fd, &stream->security_context) < 0) + log_error("Failed to determine peer security context."); +#endif + if (shutdown(fd, SHUT_WR) < 0) { log_error("Failed to shutdown writing side of socket: %m"); r = -errno; @@ -1751,7 +1830,7 @@ static void proc_kmsg_line(Server *s, const char *p) { if (message) IOVEC_SET_STRING(iovec[n++], message); - dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, priority); + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, priority); free(message); free(syslog_priority); @@ -1824,7 +1903,7 @@ static int system_journal_open(Server *s) { if (!fn) return -ENOMEM; - r = journal_file_open(fn, O_RDWR|O_CREAT, 0640, NULL, &s->system_journal); + r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, NULL, &s->system_journal); free(fn); if (r >= 0) { @@ -1871,7 +1950,7 @@ static int system_journal_open(Server *s) { * it if necessary. */ (void) mkdir_parents(fn, 0755); - r = journal_file_open(fn, O_RDWR|O_CREAT, 0640, NULL, &s->runtime_journal); + r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, NULL, &s->runtime_journal); free(fn); if (r < 0) { @@ -2077,11 +2156,24 @@ static int process_event(Server *s, struct epoll_event *ev) { struct ucred *ucred = NULL; struct timeval *tv = NULL; struct cmsghdr *cmsg; + char *label = NULL; + size_t label_len = 0; union { struct cmsghdr cmsghdr; + + /* We use NAME_MAX space for the + * SELinux label here. The kernel + * currently enforces no limit, but + * according to suggestions from the + * SELinux people this will change and + * it will probably be identical to + * NAME_MAX. For now we use that, but + * this should be updated one day when + * the final limit is known.*/ uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(struct timeval)) + - CMSG_SPACE(sizeof(int))]; + CMSG_SPACE(sizeof(int)) + /* fd */ + CMSG_SPACE(NAME_MAX)]; /* selinux label */ } control; ssize_t n; int v; @@ -2137,6 +2229,10 @@ static int process_event(Server *s, struct epoll_event *ev) { cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) ucred = (struct ucred*) CMSG_DATA(cmsg); else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_SECURITY) { + label = (char*) CMSG_DATA(cmsg); + label_len = cmsg->cmsg_len - CMSG_LEN(0); + } else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP && cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) tv = (struct timeval*) CMSG_DATA(cmsg); @@ -2157,15 +2253,15 @@ static int process_event(Server *s, struct epoll_event *ev) { else s->buffer[n] = 0; - process_syslog_message(s, strstrip(s->buffer), ucred, tv); + process_syslog_message(s, strstrip(s->buffer), ucred, tv, label, label_len); } else if (n_fds > 0) log_warning("Got file descriptors via syslog socket. Ignoring."); } else { if (n > 0 && n_fds == 0) - process_native_message(s, s->buffer, n, ucred, tv); + process_native_message(s, s->buffer, n, ucred, tv, label, label_len); else if (n == 0 && n_fds == 1) - process_native_file(s, fds[0], ucred, tv); + process_native_file(s, fds[0], ucred, tv, label, label_len); else if (n_fds > 0) log_warning("Got too many file descriptors via native socket. Ignoring."); } @@ -2216,7 +2312,6 @@ static int open_syslog_socket(Server *s) { union sockaddr_union sa; int one, r; struct epoll_event ev; - struct timeval tv; assert(s); @@ -2241,7 +2336,8 @@ static int open_syslog_socket(Server *s) { } chmod(sa.un.sun_path, 0666); - } + } else + fd_nonblock(s->syslog_fd, 1); one = 1; r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); @@ -2250,6 +2346,13 @@ static int open_syslog_socket(Server *s) { return -errno; } +#ifdef HAVE_SELINUX + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); + if (r < 0) + log_warning("SO_PASSSEC failed: %m"); +#endif + one = 1; r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); if (r < 0) { @@ -2257,15 +2360,6 @@ 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; @@ -2305,7 +2399,8 @@ static int open_native_socket(Server*s) { } chmod(sa.un.sun_path, 0666); - } + } else + fd_nonblock(s->native_fd, 1); one = 1; r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); @@ -2314,6 +2409,13 @@ static int open_native_socket(Server*s) { return -errno; } +#ifdef HAVE_SELINUX + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); + if (r < 0) + log_warning("SO_PASSSEC failed: %m"); +#endif + one = 1; r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); if (r < 0) { @@ -2365,7 +2467,8 @@ static int open_stdout_socket(Server *s) { log_error("liste() failed: %m"); return -errno; } - } + } else + fd_nonblock(s->stdout_fd, 1); zero(ev); ev.events = EPOLLIN; @@ -2386,8 +2489,7 @@ static int open_proc_kmsg(Server *s) { if (!s->import_proc_kmsg) return 0; - - s->proc_kmsg_fd = open("/proc/kmsg", O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + s->proc_kmsg_fd = open("/proc/kmsg", O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (s->proc_kmsg_fd < 0) { log_warning("Failed to open /proc/kmsg, ignoring: %m"); return 0; @@ -2492,7 +2594,7 @@ static int server_parse_config_file(Server *s) { assert(s); - fn = "/etc/systemd/systemd-journald.conf"; + fn = "/etc/systemd/journald.conf"; f = fopen(fn, "re"); if (!f) { if (errno == ENOENT) @@ -2601,10 +2703,6 @@ static int server_init(Server *s) { if (r < 0) return r; - r = system_journal_open(s); - if (r < 0) - return r; - r = open_signalfd(s); if (r < 0) return r; @@ -2613,6 +2711,10 @@ static int server_init(Server *s) { if (!s->rate_limit) return -ENOMEM; + r = system_journal_open(s); + if (r < 0) + return r; + return 0; } @@ -2672,7 +2774,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_CONSOLE); + log_set_target(LOG_TARGET_SAFE); + log_set_facility(LOG_SYSLOG); log_parse_environment(); log_open();