diff options
author | Jonathan Maw <jonathan.maw@codethink.co.uk> | 2013-06-28 10:30:15 +0000 |
---|---|---|
committer | Jonathan Maw <jonathan.maw@codethink.co.uk> | 2013-06-28 10:30:15 +0000 |
commit | daba2f7416861898b2c01926ae6a2ef19fecfaab (patch) | |
tree | 6b159c5e7d612368e6bbb8e138ae3db15005c201 /src/journal | |
parent | 9a332ba261bea9e9f3c0915bc6f0c6a0d45a1d5d (diff) | |
parent | 606c24e3bd41207c395f24a56bcfcad791e265a5 (diff) | |
download | systemd-daba2f7416861898b2c01926ae6a2ef19fecfaab.tar.gz |
Merge tag 'v204' into new-systemd
systemd 204
Diffstat (limited to 'src/journal')
37 files changed, 1639 insertions, 852 deletions
diff --git a/src/journal/cat.c b/src/journal/cat.c index a95392ccb0..ea61578353 100644 --- a/src/journal/cat.c +++ b/src/journal/cat.c @@ -25,7 +25,7 @@ #include <unistd.h> #include <stdlib.h> #include <errno.h> -#include <sys/fcntl.h> +#include <fcntl.h> #include <systemd/sd-journal.h> diff --git a/src/journal/catalog.c b/src/journal/catalog.c index 3735ad9213..7738d243a5 100644 --- a/src/journal/catalog.c +++ b/src/journal/catalog.c @@ -26,6 +26,7 @@ #include <string.h> #include <sys/mman.h> #include <locale.h> +#include <libgen.h> #include "util.h" #include "log.h" @@ -34,11 +35,12 @@ #include "hashmap.h" #include "strv.h" #include "strbuf.h" +#include "strxcpyx.h" #include "conf-files.h" #include "mkdir.h" #include "catalog.h" -static const char * const conf_file_dirs[] = { +const char * const catalog_file_dirs[] = { "/usr/local/lib/systemd/catalog/", "/usr/lib/systemd/catalog/", NULL @@ -61,7 +63,7 @@ typedef struct CatalogItem { le64_t offset; } CatalogItem; -static unsigned catalog_hash_func(const void *p) { +unsigned catalog_hash_func(const void *p) { const CatalogItem *i = p; assert_cc(sizeof(unsigned) == sizeof(uint8_t)*4); @@ -85,7 +87,7 @@ static unsigned catalog_hash_func(const void *p) { string_hash_func(i->language); } -static int catalog_compare_func(const void *a, const void *b) { +int catalog_compare_func(const void *a, const void *b) { const CatalogItem *i = a, *j = b; unsigned k; @@ -96,7 +98,7 @@ static int catalog_compare_func(const void *a, const void *b) { return 1; } - return strncmp(i->language, j->language, sizeof(i->language)); + return strcmp(i->language, j->language); } static int finish_item( @@ -123,12 +125,13 @@ static int finish_item( return log_oom(); i->id = id; - strncpy(i->language, language, sizeof(i->language)); + strscpy(i->language, sizeof(i->language), language); i->offset = htole64((uint64_t) offset); r = hashmap_put(h, i, i); if (r == EEXIST) { - log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.", SD_ID128_FORMAT_VAL(id), language ? language : "C"); + log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.", + SD_ID128_FORMAT_VAL(id), language ? language : "C"); free(i); return 0; } @@ -136,7 +139,7 @@ static int finish_item( return 0; } -static int import_file(Hashmap *h, struct strbuf *sb, const char *path) { +int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *payload = NULL; unsigned n = 0; @@ -177,7 +180,7 @@ static int import_file(Hashmap *h, struct strbuf *sb, const char *path) { continue; } - if (strchr(COMMENTS, line[0])) + if (strchr(COMMENTS "\n", line[0])) continue; if (empty_line && @@ -185,15 +188,15 @@ static int import_file(Hashmap *h, struct strbuf *sb, const char *path) { line[0] == '-' && line[1] == '-' && line[2] == ' ' && - (line[2+1+32] == ' ' || line[2+1+32] == 0)) { + (line[2+1+32] == ' ' || line[2+1+32] == '\0')) { bool with_language; sd_id128_t jd; /* New entry */ - with_language = line[2+1+32] != 0; - line[2+1+32] = 0; + with_language = line[2+1+32] != '\0'; + line[2+1+32] = '\0'; if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) { @@ -211,21 +214,21 @@ static int import_file(Hashmap *h, struct strbuf *sb, const char *path) { log_error("[%s:%u] Language too short.", path, n); return -EINVAL; } - if (c > sizeof(language)) { + if (c > sizeof(language) - 1) { log_error("[%s:%u] language too long.", path, n); return -EINVAL; } - strncpy(language, t, sizeof(language)); + strscpy(language, sizeof(language), t); } else - zero(language); + language[0] = '\0'; got_id = true; empty_line = false; id = jd; if (payload) - payload[0] = 0; + payload[0] = '\0'; continue; } @@ -269,34 +272,99 @@ static int import_file(Hashmap *h, struct strbuf *sb, const char *path) { return 0; } -#define CATALOG_DATABASE CATALOG_PATH "/database" +static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb, + CatalogItem *items, size_t n) { + CatalogHeader header; + _cleanup_fclose_ FILE *w = NULL; + int r; + _cleanup_free_ char *d, *p = NULL; + size_t k; + + d = dirname_malloc(database); + if (!d) + return log_oom(); + + r = mkdir_p(d, 0775); + if (r < 0) { + log_error("Recursive mkdir %s: %s", d, strerror(-r)); + return r; + } + + r = fopen_temporary(database, &w, &p); + if (r < 0) { + log_error("Failed to open database for writing: %s: %s", + database, strerror(-r)); + return r; + } + + zero(header); + memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature)); + header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8)); + header.catalog_item_size = htole64(sizeof(CatalogItem)); + header.n_items = htole64(hashmap_size(h)); + + r = -EIO; -int catalog_update(void) { + k = fwrite(&header, 1, sizeof(header), w); + if (k != sizeof(header)) { + log_error("%s: failed to write header.", p); + goto error; + } + + k = fwrite(items, 1, n * sizeof(CatalogItem), w); + if (k != n * sizeof(CatalogItem)) { + log_error("%s: failed to write database.", p); + goto error; + } + + k = fwrite(sb->buf, 1, sb->len, w); + if (k != sb->len) { + log_error("%s: failed to write strings.", p); + goto error; + } + + fflush(w); + + if (ferror(w)) { + log_error("%s: failed to write database.", p); + goto error; + } + + fchmod(fileno(w), 0644); + + if (rename(p, database) < 0) { + log_error("rename (%s -> %s) failed: %m", p, database); + r = -errno; + goto error; + } + + return ftell(w); + +error: + unlink(p); + return r; +} + +int catalog_update(const char* database, const char* root, const char* const* dirs) { _cleanup_strv_free_ char **files = NULL; - _cleanup_fclose_ FILE *w = NULL; - _cleanup_free_ char *p = NULL; char **f; Hashmap *h; struct strbuf *sb = NULL; _cleanup_free_ CatalogItem *items = NULL; CatalogItem *i; - CatalogHeader header; - size_t k; Iterator j; unsigned n; - int r; + long r; h = hashmap_new(catalog_hash_func, catalog_compare_func); - if (!h) - return -ENOMEM; - sb = strbuf_new(); - if (!sb) { + + if (!h || !sb) { r = log_oom(); goto finish; } - r = conf_files_list_strv(&files, ".catalog", (const char **) conf_file_dirs); + r = conf_files_list_strv(&files, ".catalog", root, dirs); if (r < 0) { log_error("Failed to get catalog files: %s", strerror(-r)); goto finish; @@ -304,7 +372,7 @@ int catalog_update(void) { STRV_FOREACH(f, files) { log_debug("reading file '%s'", *f); - import_file(h, sb, *f); + catalog_import_file(h, sb, *f); } if (hashmap_size(h) <= 0) { @@ -324,86 +392,34 @@ int catalog_update(void) { n = 0; HASHMAP_FOREACH(i, h, j) { - log_debug("Found " SD_ID128_FORMAT_STR ", language %s", SD_ID128_FORMAT_VAL(i->id), isempty(i->language) ? "C" : i->language); + log_debug("Found " SD_ID128_FORMAT_STR ", language %s", + SD_ID128_FORMAT_VAL(i->id), + isempty(i->language) ? "C" : i->language); items[n++] = *i; } assert(n == hashmap_size(h)); qsort(items, n, sizeof(CatalogItem), catalog_compare_func); - r = mkdir_p(CATALOG_PATH, 0775); - if (r < 0) { - log_error("Recursive mkdir %s: %s", CATALOG_PATH, strerror(-r)); - goto finish; - } - - r = fopen_temporary(CATALOG_DATABASE, &w, &p); - if (r < 0) { - log_error("Failed to open database for writing: %s: %s", - CATALOG_DATABASE, strerror(-r)); - goto finish; - } - - zero(header); - memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature)); - header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8)); - header.catalog_item_size = htole64(sizeof(CatalogItem)); - header.n_items = htole64(hashmap_size(h)); - - k = fwrite(&header, 1, sizeof(header), w); - if (k != sizeof(header)) { - log_error("%s: failed to write header.", p); - goto finish; - } - - k = fwrite(items, 1, n * sizeof(CatalogItem), w); - if (k != n * sizeof(CatalogItem)) { - log_error("%s: failed to write database.", p); - goto finish; - } - - k = fwrite(sb->buf, 1, sb->len, w); - if (k != sb->len) { - log_error("%s: failed to write strings.", p); - goto finish; - } - - fflush(w); - - if (ferror(w)) { - log_error("%s: failed to write database.", p); - goto finish; - } - - fchmod(fileno(w), 0644); - - if (rename(p, CATALOG_DATABASE) < 0) { - log_error("rename (%s -> %s) failed: %m", p, CATALOG_DATABASE); - r = -errno; - goto finish; - } - - log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.", - CATALOG_DATABASE, n, sb->len, ftell(w)); - - free(p); - p = NULL; + r = write_catalog(database, h, sb, items, n); + if (r < 0) + log_error("Failed to write %s: %s", database, strerror(-r)); + else + log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.", + database, n, sb->len, r); r = 0; finish: - hashmap_free_free(h); - + if (h) + hashmap_free_free(h); if (sb) strbuf_cleanup(sb); - if (p) - unlink(p); - - return r; + return r < 0 ? r : 0; } -static int open_mmap(int *_fd, struct stat *_st, void **_p) { +static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) { const CatalogHeader *h; int fd; void *p; @@ -413,7 +429,7 @@ static int open_mmap(int *_fd, struct stat *_st, void **_p) { assert(_st); assert(_p); - fd = open(CATALOG_DATABASE, O_RDONLY|O_CLOEXEC); + fd = open(database, O_RDONLY|O_CLOEXEC); if (fd < 0) return -errno; @@ -491,7 +507,7 @@ static const char *find_id(void *p, sd_id128_t id) { le64toh(f->offset); } -int catalog_get(sd_id128_t id, char **_text) { +int catalog_get(const char* database, sd_id128_t id, char **_text) { _cleanup_close_ int fd = -1; void *p = NULL; struct stat st; @@ -501,7 +517,7 @@ int catalog_get(sd_id128_t id, char **_text) { assert(_text); - r = open_mmap(&fd, &st, &p); + r = open_mmap(database, &fd, &st, &p); if (r < 0) return r; @@ -551,7 +567,23 @@ static char *find_header(const char *s, const char *header) { } } -int catalog_list(FILE *f) { +static void dump_catalog_entry(FILE *f, sd_id128_t id, const char *s, bool oneline) { + if (oneline) { + _cleanup_free_ char *subject = NULL, *defined_by = NULL; + + subject = find_header(s, "Subject:"); + defined_by = find_header(s, "Defined-By:"); + + fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", + SD_ID128_FORMAT_VAL(id), + strna(defined_by), strna(subject)); + } else + fprintf(f, "-- " SD_ID128_FORMAT_STR "\n%s\n", + SD_ID128_FORMAT_VAL(id), s); +} + + +int catalog_list(FILE *f, const char *database, bool oneline) { _cleanup_close_ int fd = -1; void *p = NULL; struct stat st; @@ -562,7 +594,7 @@ int catalog_list(FILE *f) { sd_id128_t last_id; bool last_id_set = false; - r = open_mmap(&fd, &st, &p); + r = open_mmap(database, &fd, &st, &p); if (r < 0) return r; @@ -571,17 +603,13 @@ int catalog_list(FILE *f) { for (n = 0; n < le64toh(h->n_items); n++) { const char *s; - _cleanup_free_ char *subject = NULL, *defined_by = NULL; if (last_id_set && sd_id128_equal(last_id, items[n].id)) continue; assert_se(s = find_id(p, items[n].id)); - subject = find_header(s, "Subject:"); - defined_by = find_header(s, "Defined-By:"); - - fprintf(f, SD_ID128_FORMAT_STR " %s: %s\n", SD_ID128_FORMAT_VAL(items[n].id), strna(defined_by), strna(subject)); + dump_catalog_entry(f, items[n].id, s, oneline); last_id_set = true; last_id = items[n].id; @@ -591,3 +619,37 @@ int catalog_list(FILE *f) { return 0; } + +int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) { + char **item; + int r = 0; + + STRV_FOREACH(item, items) { + sd_id128_t id; + int k; + _cleanup_free_ char *msg = NULL; + + k = sd_id128_from_string(*item, &id); + if (k < 0) { + log_error("Failed to parse id128 '%s': %s", + *item, strerror(-k)); + if (r == 0) + r = k; + continue; + } + + k = catalog_get(database, id, &msg); + if (k < 0) { + log_full(k == -ENOENT ? LOG_NOTICE : LOG_ERR, + "Failed to retrieve catalog entry for '%s': %s", + *item, strerror(-k)); + if (r == 0) + r = k; + continue; + } + + dump_catalog_entry(f, id, msg, oneline); + } + + return r; +} diff --git a/src/journal/catalog.h b/src/journal/catalog.h index 9add773c95..24a2d0b553 100644 --- a/src/journal/catalog.h +++ b/src/journal/catalog.h @@ -21,8 +21,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "sd-id128.h" +#include <stdbool.h> -int catalog_update(void); -int catalog_get(sd_id128_t id, char **data); -int catalog_list(FILE *f); +#include "sd-id128.h" +#include "hashmap.h" +#include "strbuf.h" + +int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path); +unsigned catalog_hash_func(const void *p); +int catalog_compare_func(const void *a, const void *b) _pure_; +int catalog_update(const char* database, const char* root, const char* const* dirs); +int catalog_get(const char* database, sd_id128_t id, char **data); +int catalog_list(FILE *f, const char* database, bool oneline); +int catalog_list_items(FILE *f, const char* database, bool oneline, char **items); +extern const char * const catalog_file_dirs[]; diff --git a/src/journal/coredump.c b/src/journal/coredump.c index a507fc65f8..fd03e389bb 100644 --- a/src/journal/coredump.c +++ b/src/journal/coredump.c @@ -32,11 +32,16 @@ #include "log.h" #include "util.h" +#include "macro.h" #include "mkdir.h" #include "special.h" #include "cgroup-util.h" -#define COREDUMP_MAX (24*1024*1024) +/* Few programs have less than 3MiB resident */ +#define COREDUMP_MIN_START (3*1024*1024) +/* Make sure to not make this larger than the maximum journal entry + * size. See ENTRY_SIZE_MAX in journald-native.c. */ +#define COREDUMP_MAX (768*1024*1024) enum { ARG_PID = 1, @@ -49,8 +54,7 @@ enum { }; static int divert_coredump(void) { - FILE *f; - int r; + _cleanup_fclose_ FILE *f = NULL; log_info("Detected coredump of the journal daemon itself, diverting coredump to /var/lib/systemd/coredump/."); @@ -70,19 +74,16 @@ static int divert_coredump(void) { if (l <= 0) { if (ferror(f)) { log_error("Failed to read coredump: %m"); - r = -errno; - goto finish; + return -errno; } - r = 0; break; } q = fwrite(buffer, 1, l, f); if (q != l) { log_error("Failed to write coredump: %m"); - r = -errno; - goto finish; + return -errno; } } @@ -90,25 +91,24 @@ static int divert_coredump(void) { if (ferror(f)) { log_error("Failed to write coredump: %m"); - r = -errno; + return -errno; } -finish: - fclose(f); - return r; + return 0; } int main(int argc, char* argv[]) { int r, j = 0; - char *p = NULL; + char *t; ssize_t n; pid_t pid; uid_t uid; gid_t gid; struct iovec iovec[14]; - char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, + size_t coredump_bufsize, coredump_size; + _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL, - *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *t; + *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL; prctl(PR_SET_DUMPABLE, 0); @@ -143,11 +143,11 @@ int main(int argc, char* argv[]) { } core_unit = strappend("COREDUMP_UNIT=", t); - free(t); + } else if (cg_pid_get_user_unit(pid, &t) >= 0) + core_unit = strappend("COREDUMP_USER_UNIT=", t); - if (core_unit) - IOVEC_SET_STRING(iovec[j++], core_unit); - } + if (core_unit) + IOVEC_SET_STRING(iovec[j++], core_unit); /* OK, now we know it's not the journal, hence make use of * it */ @@ -205,7 +205,7 @@ int main(int argc, char* argv[]) { IOVEC_SET_STRING(iovec[j++], core_exe); } - if (get_process_cmdline(pid, LINE_MAX, false, &t) >= 0) { + if (get_process_cmdline(pid, 0, false, &t) >= 0) { core_cmdline = strappend("COREDUMP_CMDLINE=", t); free(t); @@ -237,23 +237,35 @@ int main(int argc, char* argv[]) { goto finish; } - p = malloc(9 + COREDUMP_MAX); - if (!p) { + coredump_bufsize = COREDUMP_MIN_START; + coredump_data = malloc(coredump_bufsize); + if (!coredump_data) { r = log_oom(); goto finish; } - memcpy(p, "COREDUMP=", 9); + memcpy(coredump_data, "COREDUMP=", 9); + coredump_size = 9; - n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false); - if (n < 0) { - log_error("Failed to read core dump data: %s", strerror(-n)); - r = (int) n; - goto finish; + for (;;) { + n = loop_read(STDIN_FILENO, coredump_data + coredump_size, + coredump_bufsize - coredump_size, false); + if (n < 0) { + log_error("Failed to read core dump data: %s", strerror(-n)); + r = (int) n; + goto finish; + } else if (n == 0) + break; + + coredump_size += n; + if (!GREEDY_REALLOC(coredump_data, coredump_bufsize, coredump_size + 1)) { + r = log_oom(); + goto finish; + } } - iovec[j].iov_base = p; - iovec[j].iov_len = 9 + n; + iovec[j].iov_base = coredump_data; + iovec[j].iov_len = coredump_size; j++; r = sd_journal_sendv(iovec, j); @@ -261,18 +273,5 @@ int main(int argc, char* argv[]) { log_error("Failed to send coredump: %s", strerror(-r)); finish: - free(p); - free(core_pid); - free(core_uid); - free(core_gid); - free(core_signal); - free(core_timestamp); - free(core_comm); - free(core_exe); - free(core_cmdline); - free(core_unit); - free(core_session); - free(core_message); - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c index b6e558186d..5652c2f91a 100644 --- a/src/journal/coredumpctl.c +++ b/src/journal/coredumpctl.c @@ -34,6 +34,8 @@ #include "log.h" #include "path-util.h" #include "pager.h" +#include "macro.h" +#include "journal-internal.h" static enum { ACTION_NONE, @@ -42,7 +44,6 @@ static enum { ACTION_GDB, } arg_action = ACTION_LIST; -static Set *matches = NULL; static FILE* output = NULL; static char* field = NULL; @@ -67,10 +68,9 @@ static Set *new_matches(void) { return NULL; } - r = set_put(set, tmp); + r = set_consume(set, tmp); if (r < 0) { log_error("failed to add to set: %s", strerror(-r)); - free(tmp); set_free(set); return NULL; } @@ -103,7 +103,7 @@ static int add_match(Set *set, const char *match) { unsigned pid; const char* prefix; char *pattern = NULL; - char _cleanup_free_ *p = NULL; + _cleanup_free_ char *p = NULL; if (strchr(match, '=')) prefix = ""; @@ -124,22 +124,21 @@ static int add_match(Set *set, const char *match) { if (!pattern) goto fail; - r = set_put(set, pattern); + log_debug("Adding pattern: %s", pattern); + r = set_consume(set, pattern); if (r < 0) { - log_error("failed to add pattern '%s': %s", + log_error("Failed to add pattern '%s': %s", pattern, strerror(-r)); goto fail; } - log_debug("Added pattern: %s", pattern); return 0; fail: - free(pattern); - log_error("failed to add match: %s", strerror(-r)); + log_error("Failed to add match: %s", strerror(-r)); return r; } -static int parse_argv(int argc, char *argv[]) { +static int parse_argv(int argc, char *argv[], Set *matches) { enum { ARG_VERSION = 0x100, ARG_NO_PAGER, @@ -268,7 +267,7 @@ static int retrieve(const void *data, } static void print_field(FILE* file, sd_journal *j) { - const char _cleanup_free_ *value = NULL; + _cleanup_free_ const char *value = NULL; const void *d; size_t l; @@ -281,7 +280,7 @@ static void print_field(FILE* file, sd_journal *j) { } static int print_entry(FILE* file, sd_journal *j, int had_legend) { - const char _cleanup_free_ + _cleanup_free_ const char *pid = NULL, *uid = NULL, *gid = NULL, *sgnl = NULL, *exe = NULL; const void *d; @@ -519,10 +518,11 @@ finish: } int main(int argc, char *argv[]) { - sd_journal *j = NULL; + _cleanup_journal_close_ sd_journal*j = NULL; const char* match; Iterator it; int r = 0; + _cleanup_set_free_free_ Set *matches = NULL; setlocale(LC_ALL, ""); log_parse_environment(); @@ -534,7 +534,7 @@ int main(int argc, char *argv[]) { goto end; } - r = parse_argv(argc, argv); + r = parse_argv(argc, argv, matches); if (r < 0) goto end; @@ -560,7 +560,7 @@ int main(int argc, char *argv[]) { case ACTION_LIST: if (!arg_no_pager) - pager_open(); + pager_open(false); r = dump_list(j); break; @@ -578,11 +578,6 @@ int main(int argc, char *argv[]) { } end: - if (j) - sd_journal_close(j); - - set_free_free(matches); - pager_close(); if (output) diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c index 2190b7c796..6817a629c8 100644 --- a/src/journal/fsprg.c +++ b/src/journal/fsprg.c @@ -74,7 +74,7 @@ static void uint64_export(void *buf, size_t buflen, uint64_t x) { ((uint8_t*) buf)[7] = (x >> 0) & 0xff; } -static uint64_t uint64_import(const void *buf, size_t buflen) { +_pure_ static uint64_t uint64_import(const void *buf, size_t buflen) { assert(buflen == 8); return (uint64_t)(((uint8_t*) buf)[0]) << 56 | diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h index 306ef18d73..150d034828 100644 --- a/src/journal/fsprg.h +++ b/src/journal/fsprg.h @@ -28,6 +28,8 @@ #include <sys/types.h> #include <inttypes.h> +#include "macro.h" + #ifdef __cplusplus extern "C" { #endif @@ -35,9 +37,9 @@ extern "C" { #define FSPRG_RECOMMENDED_SECPAR 1536 #define FSPRG_RECOMMENDED_SEEDLEN (96/8) -size_t FSPRG_mskinbytes(unsigned secpar); -size_t FSPRG_mpkinbytes(unsigned secpar); -size_t FSPRG_stateinbytes(unsigned secpar); +size_t FSPRG_mskinbytes(unsigned secpar) _const_; +size_t FSPRG_mpkinbytes(unsigned secpar) _const_; +size_t FSPRG_stateinbytes(unsigned secpar) _const_; /* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */ void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar); @@ -50,7 +52,7 @@ void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seed void FSPRG_Evolve(void *state); -uint64_t FSPRG_GetEpoch(const void *state); +uint64_t FSPRG_GetEpoch(const void *state) _pure_; /* Seek to any arbitrary state (by providing msk together with seed from GenState0). */ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen); diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 13fc8edea9..38499a6881 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -44,7 +44,7 @@ #define COMPRESSION_SIZE_THRESHOLD (512ULL) /* This is the minimum journal file size */ -#define JOURNAL_FILE_SIZE_MIN (64ULL*1024ULL) /* 64 KiB */ +#define JOURNAL_FILE_SIZE_MIN (4ULL*1024ULL*1024ULL) /* 4 MiB */ /* These are the lower and upper bounds if we deduce the max_use value * from the file system size */ @@ -68,6 +68,50 @@ /* How many entries to keep in the entry array chain cache at max */ #define CHAIN_CACHE_MAX 20 +int journal_file_set_online(JournalFile *f) { + assert(f); + + if (!f->writable) + return -EPERM; + + if (!(f->fd >= 0 && f->header)) + return -EINVAL; + + switch(f->header->state) { + case STATE_ONLINE: + return 0; + + case STATE_OFFLINE: + f->header->state = STATE_ONLINE; + fsync(f->fd); + return 0; + + default: + return -EINVAL; + } +} + +int journal_file_set_offline(JournalFile *f) { + assert(f); + + if (!f->writable) + return -EPERM; + + if (!(f->fd >= 0 && f->header)) + return -EINVAL; + + if (f->header->state != STATE_ONLINE) + return 0; + + fsync(f->fd); + + f->header->state = STATE_OFFLINE; + + fsync(f->fd); + + return 0; +} + void journal_file_close(JournalFile *f) { assert(f); @@ -81,16 +125,10 @@ void journal_file_close(JournalFile *f) { if (f->mmap && f->fd >= 0) mmap_cache_close_fd(f->mmap, f->fd); - if (f->writable && f->fd >= 0) - fdatasync(f->fd); - - if (f->header) { - /* Mark the file offline. Don't override the archived state if it already is set */ - if (f->writable && f->header->state == STATE_ONLINE) - f->header->state = STATE_OFFLINE; + journal_file_set_offline(f); + if (f->header) munmap(f->header, PAGE_ALIGN(sizeof(Header))); - } if (f->fd >= 0) close_nointr_nofail(f->fd); @@ -177,7 +215,7 @@ static int journal_file_refresh_header(JournalFile *f) { f->header->boot_id = boot_id; - f->header->state = STATE_ONLINE; + journal_file_set_online(f); /* Sync the online state to disk */ msync(f->header, PAGE_ALIGN(sizeof(Header)), MS_SYNC); @@ -457,6 +495,10 @@ int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object * assert(offset); assert(ret); + r = journal_file_set_online(f); + if (r < 0) + return r; + p = le64toh(f->header->tail_object_offset); if (p == 0) p = le64toh(f->header->header_size); @@ -1267,9 +1309,6 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st assert(f); assert(iovec || n_iovec == 0); - if (!f->writable) - return -EPERM; - if (!ts) { dual_timestamp_get(&_ts); ts = &_ts; @@ -1286,7 +1325,7 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st #endif /* alloca() can't take 0, hence let's allocate at least one */ - items = alloca(sizeof(EntryItem) * MAX(1, n_iovec)); + items = alloca(sizeof(EntryItem) * MAX(1u, n_iovec)); for (i = 0; i < n_iovec; i++) { uint64_t p; @@ -1673,7 +1712,7 @@ found: return 1; } -static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { +_pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { assert(f); assert(p > 0); @@ -1791,6 +1830,17 @@ static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) { return TEST_RIGHT; } +static inline int find_data_object_by_boot_id( + JournalFile *f, + sd_id128_t boot_id, + Object **o, + uint64_t *b) { + char t[sizeof("_BOOT_ID=")-1 + 32 + 1] = "_BOOT_ID="; + + sd_id128_to_string(boot_id, t + 9); + return journal_file_find_data_object(f, t, sizeof(t) - 1, o, b); +} + int journal_file_move_to_entry_by_monotonic( JournalFile *f, sd_id128_t boot_id, @@ -1799,14 +1849,12 @@ int journal_file_move_to_entry_by_monotonic( Object **ret, uint64_t *offset) { - char t[9+32+1] = "_BOOT_ID="; Object *o; int r; assert(f); - sd_id128_to_string(boot_id, t + 9); - r = journal_file_find_data_object(f, t, strlen(t), &o, NULL); + r = find_data_object_by_boot_id(f, boot_id, &o, NULL); if (r < 0) return r; if (r == 0) @@ -2020,7 +2068,6 @@ int journal_file_move_to_entry_by_monotonic_for_data( direction_t direction, Object **ret, uint64_t *offset) { - char t[9+32+1] = "_BOOT_ID="; Object *o, *d; int r; uint64_t b, z; @@ -2028,8 +2075,7 @@ int journal_file_move_to_entry_by_monotonic_for_data( assert(f); /* First, seek by time */ - sd_id128_to_string(boot_id, t + 9); - r = journal_file_find_data_object(f, t, strlen(t), &o, &b); + r = find_data_object_by_boot_id(f, boot_id, &o, &b); if (r < 0) return r; if (r == 0) @@ -2730,7 +2776,7 @@ void journal_default_metrics(JournalMetrics *m, int fd) { if (m->keep_free == (uint64_t) -1) { if (fs_size > 0) { - m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */ + m->keep_free = PAGE_ALIGN(fs_size * 3 / 20); /* 15% of file system size */ if (m->keep_free > DEFAULT_KEEP_FREE_UPPER) m->keep_free = DEFAULT_KEEP_FREE_UPPER; @@ -2768,7 +2814,6 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t * } int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) { - char t[9+32+1] = "_BOOT_ID="; Object *o; uint64_t p; int r; @@ -2776,9 +2821,7 @@ int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, u assert(f); assert(from || to); - sd_id128_to_string(boot_id, t + 9); - - r = journal_file_find_data_object(f, t, strlen(t), &o, &p); + r = find_data_object_by_boot_id(f, boot_id, &o, &p); if (r <= 0) return r; diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index cdbc8e41f6..7b1cd42854 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -106,6 +106,8 @@ int journal_file_open( JournalFile *template, JournalFile **ret); +int journal_file_set_offline(JournalFile *f); +int journal_file_set_online(JournalFile *f); void journal_file_close(JournalFile *j); int journal_file_open_reliably( @@ -148,9 +150,9 @@ static inline bool VALID_EPOCH(uint64_t u) { int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret); -uint64_t journal_file_entry_n_items(Object *o); -uint64_t journal_file_entry_array_n_items(Object *o); -uint64_t journal_file_hash_table_n_items(Object *o); +uint64_t journal_file_entry_n_items(Object *o) _pure_; +uint64_t journal_file_entry_array_n_items(Object *o) _pure_; +uint64_t journal_file_hash_table_n_items(Object *o) _pure_; int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset); int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); diff --git a/src/journal/journal-gatewayd.c b/src/journal/journal-gatewayd.c index 63d9744776..745f45f932 100644 --- a/src/journal/journal-gatewayd.c +++ b/src/journal/journal-gatewayd.c @@ -23,6 +23,7 @@ #include <string.h> #include <unistd.h> #include <fcntl.h> +#include <getopt.h> #include <microhttpd.h> @@ -30,8 +31,13 @@ #include "util.h" #include "sd-journal.h" #include "sd-daemon.h" +#include "sd-bus.h" +#include "bus-message.h" +#include "bus-internal.h" #include "logs-show.h" -#include "virt.h" +#include "microhttpd-util.h" +#include "build.h" +#include "fileio.h" typedef struct RequestMeta { sd_journal *journal; @@ -106,8 +112,7 @@ static int open_journal(RequestMeta *m) { return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY); } - -static int respond_oom(struct MHD_Connection *connection) { +static int respond_oom_internal(struct MHD_Connection *connection) { struct MHD_Response *response; const char m[] = "Out of memory.\n"; int ret; @@ -125,6 +130,8 @@ static int respond_oom(struct MHD_Connection *connection) { return ret; } +#define respond_oom(connection) log_oom(), respond_oom_internal(connection) + static int respond_error( struct MHD_Connection *connection, unsigned code, @@ -328,7 +335,7 @@ static int request_parse_range( colon2 = strchr(colon + 1, ':'); if (colon2) { - char _cleanup_free_ *t; + _cleanup_free_ char *t; t = strndup(colon + 1, colon2 - colon - 1); if (!t) @@ -477,18 +484,14 @@ static int request_parse_arguments( static int request_handler_entries( struct MHD_Connection *connection, - void **connection_cls) { + void *connection_cls) { struct MHD_Response *response; - RequestMeta *m; + RequestMeta *m = connection_cls; int r; assert(connection); - assert(connection_cls); - - m = request_meta(connection_cls); - if (!m) - return respond_oom(connection); + assert(m); r = open_journal(m); if (r < 0) @@ -646,15 +649,11 @@ static int request_handler_fields( void *connection_cls) { struct MHD_Response *response; - RequestMeta *m; + RequestMeta *m = connection_cls; int r; assert(connection); - assert(connection_cls); - - m = request_meta(connection_cls); - if (!m) - return respond_oom(connection); + assert(m); r = open_journal(m); if (r < 0) @@ -743,24 +742,63 @@ static int request_handler_file( return ret; } +static int get_virtualization(char **v) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_unref_ sd_bus *bus = NULL; + const char *t; + char *b; + int r; + + r = sd_bus_open_system(&bus); + if (r < 0) + return r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.DBus.Properties", + "Get", + NULL, + &reply, + "ss", + "org.freedesktop.systemd1.Manager", + "Virtualization"); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "v", "s", &t); + if (r < 0) + return r; + + if (isempty(t)) { + *v = NULL; + return 0; + } + + b = strdup(t); + if (!b) + return -ENOMEM; + + *v = b; + return 1; +} + static int request_handler_machine( struct MHD_Connection *connection, - void **connection_cls) { + void *connection_cls) { struct MHD_Response *response; - RequestMeta *m; + RequestMeta *m = connection_cls; int r; _cleanup_free_ char* hostname = NULL, *os_name = NULL; uint64_t cutoff_from, cutoff_to, usage; char *json; sd_id128_t mid, bid; - const char *v = "bare"; + _cleanup_free_ char *v = NULL; assert(connection); - - m = request_meta(connection_cls); - if (!m) - return respond_oom(connection); + assert(m); r = open_journal(m); if (r < 0) @@ -788,7 +826,7 @@ static int request_handler_machine( parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL); - detect_virtualization(&v); + get_virtualization(&v); r = asprintf(&json, "{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\"," @@ -801,9 +839,9 @@ static int request_handler_machine( "\"cutoff_to_realtime\" : \"%llu\" }\n", SD_ID128_FORMAT_VAL(mid), SD_ID128_FORMAT_VAL(bid), - hostname_cleanup(hostname), + hostname_cleanup(hostname, false), os_name ? os_name : "Linux", - v, + v ? v : "bare", (unsigned long long) usage, (unsigned long long) cutoff_from, (unsigned long long) cutoff_to); @@ -835,43 +873,146 @@ static int request_handler( void **connection_cls) { assert(connection); + assert(connection_cls); assert(url); assert(method); if (!streq(method, "GET")) - return MHD_NO; + return respond_error(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE, + "Unsupported method.\n"); + + + if (!*connection_cls) { + if (!request_meta(connection_cls)) + return respond_oom(connection); + return MHD_YES; + } if (streq(url, "/")) return request_handler_redirect(connection, "/browse"); if (streq(url, "/entries")) - return request_handler_entries(connection, connection_cls); + return request_handler_entries(connection, *connection_cls); if (startswith(url, "/fields/")) - return request_handler_fields(connection, url + 8, connection_cls); + return request_handler_fields(connection, url + 8, *connection_cls); if (streq(url, "/browse")) return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html"); if (streq(url, "/machine")) - return request_handler_machine(connection, connection_cls); + return request_handler_machine(connection, *connection_cls); return respond_error(connection, MHD_HTTP_NOT_FOUND, "Not found.\n"); } -int main(int argc, char *argv[]) { - struct MHD_Daemon *d = NULL; - int r = EXIT_FAILURE, n; +static int help(void) { + + printf("%s [OPTIONS...] ...\n\n" + "HTTP server for journal events.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --cert=CERT.PEM Specify server certificate in PEM format\n" + " --key=KEY.PEM Specify server key in PEM format\n", + program_invocation_short_name); + + return 0; +} - if (argc > 1) { +static char *key_pem = NULL; +static char *cert_pem = NULL; + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + ARG_KEY, + ARG_CERT, + }; + + int r, c; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "key", required_argument, NULL, ARG_KEY }, + { "cert", required_argument, NULL, ARG_CERT }, + { NULL, 0, NULL, 0 } + }; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + switch(c) { + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(SYSTEMD_FEATURES); + return 0; + + case 'h': + return help(); + + case ARG_KEY: + if (key_pem) { + log_error("Key file specified twice"); + return -EINVAL; + } + r = read_full_file(optarg, &key_pem, NULL); + if (r < 0) { + log_error("Failed to read key file: %s", strerror(-r)); + return r; + } + assert(key_pem); + break; + + case ARG_CERT: + if (cert_pem) { + log_error("Certificate file specified twice"); + return -EINVAL; + } + r = read_full_file(optarg, &cert_pem, NULL); + if (r < 0) { + log_error("Failed to read certificate file: %s", strerror(-r)); + return r; + } + assert(cert_pem); + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + + if (optind < argc) { log_error("This program does not take arguments."); - goto finish; + return -EINVAL; + } + + if (!!key_pem != !!cert_pem) { + log_error("Certificate and key files must be specified together"); + return -EINVAL; } + return 1; +} + +int main(int argc, char *argv[]) { + struct MHD_Daemon *d = NULL; + int r, n; + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); + r = parse_argv(argc, argv); + if (r < 0) + return EXIT_FAILURE; + if (r == 0) + return EXIT_SUCCESS; + n = sd_listen_fds(1); if (n < 0) { log_error("Failed to determine passed sockets: %s", strerror(-n)); @@ -879,23 +1020,36 @@ int main(int argc, char *argv[]) { } else if (n > 1) { log_error("Can't listen on more than one socket."); goto finish; - } else if (n > 0) { - d = MHD_start_daemon( - MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG, - 19531, - NULL, NULL, - request_handler, NULL, - MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START, - MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL, - MHD_OPTION_END); } else { - d = MHD_start_daemon( - MHD_USE_DEBUG|MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL, - 19531, - NULL, NULL, - request_handler, NULL, - MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL, - MHD_OPTION_END); + struct MHD_OptionItem opts[] = { + { MHD_OPTION_NOTIFY_COMPLETED, + (intptr_t) request_meta_free, NULL }, + { MHD_OPTION_EXTERNAL_LOGGER, + (intptr_t) microhttpd_logger, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL }}; + int opts_pos = 2; + int flags = MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG; + + if (n > 0) + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START}; + if (key_pem) { + assert(cert_pem); + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_HTTPS_MEM_KEY, 0, key_pem}; + opts[opts_pos++] = (struct MHD_OptionItem) + {MHD_OPTION_HTTPS_MEM_CERT, 0, cert_pem}; + flags |= MHD_USE_SSL; + } + + d = MHD_start_daemon(flags, 19531, + NULL, NULL, + request_handler, NULL, + MHD_OPTION_ARRAY, opts, + MHD_OPTION_END); } if (!d) { diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 97de0e75ff..c7e585d810 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -30,6 +30,7 @@ #include "journal-def.h" #include "list.h" #include "hashmap.h" +#include "set.h" #include "journal-file.h" typedef struct Match Match; @@ -73,19 +74,20 @@ typedef enum LocationType { struct Location { LocationType type; + bool seqnum_set; + bool realtime_set; + bool monotonic_set; + bool xor_hash_set; + uint64_t seqnum; sd_id128_t seqnum_id; - bool seqnum_set; uint64_t realtime; - bool realtime_set; uint64_t monotonic; sd_id128_t boot_id; - bool monotonic_set; uint64_t xor_hash; - bool xor_hash_set; }; struct Directory { @@ -112,7 +114,7 @@ struct sd_journal { int inotify_fd; - Match *level0, *level1; + Match *level0, *level1, *level2; unsigned current_invalidate_counter, last_invalidate_counter; @@ -123,7 +125,17 @@ struct sd_journal { bool on_network; size_t data_threshold; + + Set *errors; + + usec_t last_process_usec; }; char *journal_make_match_string(sd_journal *j); void journal_print_header(sd_journal *j); + +static inline void journal_closep(sd_journal **j) { + sd_journal_close(*j); +} + +#define _cleanup_journal_close_ _cleanup_(journal_closep) diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index d5ec73e371..14c437da78 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -111,13 +111,12 @@ _public_ int sd_journal_printv(int priority, const char *format, va_list ap) { return sd_journal_sendv(iov, 2); } -static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) { +_printf_attr_(1, 0) static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) { + PROTECT_ERRNO; int r, n = 0, i = 0, j; struct iovec *iov = NULL; - int saved_errno; assert(_iov); - saved_errno = errno; if (extra > 0) { n = MAX(extra * 2, extra + 4); @@ -163,7 +162,6 @@ static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct *_iov = iov; - errno = saved_errno; return i; fail: @@ -172,7 +170,6 @@ fail: free(iov); - errno = saved_errno; return r; } @@ -202,14 +199,14 @@ finish: } _public_ int sd_journal_sendv(const struct iovec *iov, int n) { + PROTECT_ERRNO; int fd, buffer_fd; struct iovec *w; uint64_t *l; - int r, i, j = 0; + int i, j = 0; struct msghdr mh; struct sockaddr_un sa; ssize_t k; - int saved_errno; union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; @@ -227,24 +224,18 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { if (_unlikely_(n <= 0)) return -EINVAL; - saved_errno = errno; - w = alloca(sizeof(struct iovec) * n * 5 + 3); l = alloca(sizeof(uint64_t) * n); for (i = 0; i < n; i++) { char *c, *nl; - if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) { - r = -EINVAL; - goto finish; - } + if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) + return -EINVAL; c = memchr(iov[i].iov_base, '=', iov[i].iov_len); - if (_unlikely_(!c || c == iov[i].iov_base)) { - r = -EINVAL; - goto finish; - } + if (_unlikely_(!c || c == iov[i].iov_base)) + return -EINVAL; have_syslog_identifier = have_syslog_identifier || (c == (char *) iov[i].iov_base + 17 && @@ -252,10 +243,8 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); if (nl) { - if (_unlikely_(nl < c)) { - r = -EINVAL; - goto finish; - } + if (_unlikely_(nl < c)) + return -EINVAL; /* Already includes a newline? Bummer, then * let's write the variable name, then a @@ -300,10 +289,8 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { } fd = journal_fd(); - if (_unlikely_(fd < 0)) { - r = fd; - goto finish; - } + if (_unlikely_(fd < 0)) + return fd; zero(sa); sa.sun_family = AF_UNIX; @@ -316,37 +303,29 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { mh.msg_iovlen = j; k = sendmsg(fd, &mh, MSG_NOSIGNAL); - if (k >= 0) { - r = 0; - goto finish; - } + if (k >= 0) + return 0; - if (errno != EMSGSIZE && errno != ENOBUFS) { - r = -errno; - goto finish; - } + if (errno != EMSGSIZE && errno != ENOBUFS) + return -errno; /* Message doesn't fit... Let's dump the data in a temporary * file and just pass a file descriptor of it to the other * side */ buffer_fd = mkostemp(path, O_CLOEXEC|O_RDWR); - if (buffer_fd < 0) { - r = -errno; - goto finish; - } + if (buffer_fd < 0) + return -errno; if (unlink(path) < 0) { close_nointr_nofail(buffer_fd); - r = -errno; - goto finish; + return -errno; } n = writev(buffer_fd, w, j); if (n < 0) { close_nointr_nofail(buffer_fd); - r = -errno; - goto finish; + return -errno; } mh.msg_iov = NULL; @@ -367,24 +346,15 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { k = sendmsg(fd, &mh, MSG_NOSIGNAL); close_nointr_nofail(buffer_fd); - if (k < 0) { - r = -errno; - goto finish; - } - - r = 0; - -finish: - errno = saved_errno; + if (k < 0) + return -errno; - return r; + return 0; } static int fill_iovec_perror_and_send(const char *message, int skip, struct iovec iov[]) { - size_t n, k, r; - int saved_errno; - - saved_errno = errno; + PROTECT_ERRNO; + size_t n, k; k = isempty(message) ? 0 : strlen(message) + 2; n = 8 + k + 256 + 1; @@ -394,7 +364,7 @@ static int fill_iovec_perror_and_send(const char *message, int skip, struct iove char* j; errno = 0; - j = strerror_r(saved_errno, buffer + 8 + k, n - 8 - k); + j = strerror_r(_saved_errno_, buffer + 8 + k, n - 8 - k); if (errno == 0) { char error[6 + 10 + 1]; /* for a 32bit value */ @@ -408,24 +378,18 @@ static int fill_iovec_perror_and_send(const char *message, int skip, struct iove memcpy(buffer + 8 + k - 2, ": ", 2); } - snprintf(error, sizeof(error), "ERRNO=%u", saved_errno); + snprintf(error, sizeof(error), "ERRNO=%u", _saved_errno_); char_array_0(error); IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3"); IOVEC_SET_STRING(iov[skip+1], buffer); IOVEC_SET_STRING(iov[skip+2], error); - r = sd_journal_sendv(iov, skip + 3); - - errno = saved_errno; - return r; + return sd_journal_sendv(iov, skip + 3); } - if (errno != ERANGE) { - r = -errno; - errno = saved_errno; - return r; - } + if (errno != ERANGE) + return -errno; n *= 2; } diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c index 731f6c770f..4a3a5a9e63 100644 --- a/src/journal/journal-vacuum.c +++ b/src/journal/journal-vacuum.c @@ -36,7 +36,7 @@ #include "util.h" struct vacuum_info { - off_t usage; + uint64_t usage; char *filename; uint64_t realtime; @@ -293,7 +293,7 @@ int journal_directory_vacuum( if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { log_debug("Deleted archived journal %s/%s.", directory, list[i].filename); - if ((uint64_t) list[i].usage > sum) + if (list[i].usage < sum) sum -= list[i].usage; else sum = 0; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index a74d43be7f..409f082276 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -27,7 +27,6 @@ #include <stdio.h> #include <unistd.h> #include <stdlib.h> -#include <sys/poll.h> #include <time.h> #include <getopt.h> #include <signal.h> @@ -35,9 +34,15 @@ #include <sys/ioctl.h> #include <linux/fs.h> +#ifdef HAVE_ACL +#include <sys/acl.h> +#include "acl-util.h" +#endif + #include <systemd/sd-journal.h> #include "log.h" +#include "logs-show.h" #include "util.h" #include "path-util.h" #include "build.h" @@ -56,11 +61,12 @@ #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE) static OutputMode arg_output = OUTPUT_SHORT; +static bool arg_pager_end = false; static bool arg_follow = false; static bool arg_full = false; static bool arg_all = false; static bool arg_no_pager = false; -static unsigned arg_lines = 0; +static int arg_lines = -1; static bool arg_no_tail = false; static bool arg_quiet = false; static bool arg_merge = false; @@ -74,9 +80,12 @@ static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; #endif static usec_t arg_since, arg_until; static bool arg_since_set = false, arg_until_set = false; -static const char *arg_unit = NULL; +static char **arg_system_units = NULL; +static char **arg_user_units = NULL; static const char *arg_field = NULL; static bool arg_catalog = false; +static bool arg_reverse = false; +static const char *arg_root = NULL; static enum { ACTION_SHOW, @@ -86,6 +95,7 @@ static enum { ACTION_VERIFY, ACTION_DISK_USAGE, ACTION_LIST_CATALOG, + ACTION_DUMP_CATALOG, ACTION_UPDATE_CATALOG } arg_action = ACTION_SHOW; @@ -99,10 +109,13 @@ static int help(void) { " -c --cursor=CURSOR Start showing entries from specified cursor\n" " -b --this-boot Show data only from current boot\n" " -u --unit=UNIT Show data only from the specified unit\n" + " --user-unit=UNIT Show data only from the specified user session unit\n" " -p --priority=RANGE Show only messages within the specified priority range\n" + " -e --pager-end Immediately jump to end of the journal in the pager\n" " -f --follow Follow journal\n" " -n --lines[=INTEGER] Number of journal entries to show\n" " --no-tail Show all lines, even in follow mode\n" + " -r --reverse Show the newest entries first\n" " -o --output=STRING Change journal output mode (short, short-monotonic,\n" " verbose, export, json, json-pretty, json-sse, cat)\n" " -x --catalog Add message explanations where available\n" @@ -112,6 +125,7 @@ static int help(void) { " --no-pager Do not pipe output into a pager\n" " -m --merge Show entries from all available journals\n" " -D --directory=PATH Show journal files from directory\n" + " --root=ROOT Operate on catalog files underneath the root ROOT\n" #ifdef HAVE_GCRYPT " --interval=TIME Time interval for changing the FSS sealing key\n" " --verify-key=KEY Specify FSS verification key\n" @@ -124,6 +138,7 @@ static int help(void) { " --disk-usage Show total disk usage\n" " -F --field=FIELD List all values a certain field takes\n" " --list-catalog Show message IDs of all entries in the message catalog\n" + " --dump-catalog Show entries in the message catalog\n" " --update-catalog Update the message catalog database\n" #ifdef HAVE_GCRYPT " --setup-keys Generate new FSS key pair\n" @@ -141,6 +156,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_PAGER, ARG_NO_TAIL, ARG_NEW_ID128, + ARG_ROOT, ARG_HEADER, ARG_FULL, ARG_SETUP_KEYS, @@ -150,7 +166,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_DISK_USAGE, ARG_SINCE, ARG_UNTIL, + ARG_USER_UNIT, ARG_LIST_CATALOG, + ARG_DUMP_CATALOG, ARG_UPDATE_CATALOG }; @@ -158,6 +176,7 @@ static int parse_argv(int argc, char *argv[]) { { "help", no_argument, NULL, 'h' }, { "version" , no_argument, NULL, ARG_VERSION }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "pager-end", no_argument, NULL, 'e' }, { "follow", no_argument, NULL, 'f' }, { "output", required_argument, NULL, 'o' }, { "all", no_argument, NULL, 'a' }, @@ -169,6 +188,7 @@ static int parse_argv(int argc, char *argv[]) { { "merge", no_argument, NULL, 'm' }, { "this-boot", no_argument, NULL, 'b' }, { "directory", required_argument, NULL, 'D' }, + { "root", required_argument, NULL, ARG_ROOT }, { "header", no_argument, NULL, ARG_HEADER }, { "priority", required_argument, NULL, 'p' }, { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, @@ -180,10 +200,13 @@ static int parse_argv(int argc, char *argv[]) { { "since", required_argument, NULL, ARG_SINCE }, { "until", required_argument, NULL, ARG_UNTIL }, { "unit", required_argument, NULL, 'u' }, + { "user-unit", required_argument, NULL, ARG_USER_UNIT }, { "field", required_argument, NULL, 'F' }, { "catalog", no_argument, NULL, 'x' }, { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG }, + { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, { "update-catalog",no_argument, NULL, ARG_UPDATE_CATALOG }, + { "reverse", no_argument, NULL, 'r' }, { NULL, 0, NULL, 0 } }; @@ -192,7 +215,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hfo:an::qmbD:p:c:u:F:x", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hefo:an::qmbD:p:c:u:F:xr", options, NULL)) >= 0) { switch (c) { @@ -209,6 +232,14 @@ static int parse_argv(int argc, char *argv[]) { arg_no_pager = true; break; + case 'e': + arg_pager_end = true; + + if (arg_lines < 0) + arg_lines = 1000; + + break; + case 'f': arg_follow = true; break; @@ -239,13 +270,30 @@ static int parse_argv(int argc, char *argv[]) { case 'n': if (optarg) { - r = safe_atou(optarg, &arg_lines); - if (r < 0 || arg_lines <= 0) { + r = safe_atoi(optarg, &arg_lines); + if (r < 0 || arg_lines < 0) { log_error("Failed to parse lines '%s'", optarg); return -EINVAL; } - } else - arg_lines = 10; + } else { + int n; + + /* Hmm, no argument? Maybe the next + * word on the command line is + * supposed to be the argument? Let's + * see if there is one, and is + * parsable as a positive + * integer... */ + + if (optind < argc && + safe_atoi(argv[optind], &n) >= 0 && + n >= 0) { + + arg_lines = n; + optind++; + } else + arg_lines = 10; + } break; @@ -273,6 +321,10 @@ static int parse_argv(int argc, char *argv[]) { arg_directory = optarg; break; + case ARG_ROOT: + arg_root = optarg; + break; + case 'c': arg_cursor = optarg; break; @@ -302,7 +354,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_INTERVAL: - r = parse_usec(optarg, &arg_interval); + r = parse_sec(optarg, &arg_interval); if (r < 0 || arg_interval <= 0) { log_error("Failed to parse sealing key change interval: %s", optarg); return -EINVAL; @@ -385,7 +437,15 @@ static int parse_argv(int argc, char *argv[]) { break; case 'u': - arg_unit = optarg; + r = strv_extend(&arg_system_units, optarg); + if (r < 0) + return log_oom(); + break; + + case ARG_USER_UNIT: + r = strv_extend(&arg_user_units, optarg); + if (r < 0) + return log_oom(); break; case '?': @@ -403,20 +463,28 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_LIST_CATALOG; break; + case ARG_DUMP_CATALOG: + arg_action = ACTION_DUMP_CATALOG; + break; + case ARG_UPDATE_CATALOG: arg_action = ACTION_UPDATE_CATALOG; break; + case 'r': + arg_reverse = true; + break; + default: log_error("Unknown option code %c", c); return -EINVAL; } } - if (arg_follow && !arg_no_tail && arg_lines <= 0) + if (arg_follow && !arg_no_tail && arg_lines < 0) arg_lines = 10; - if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) { + if (arg_since_set && arg_until_set && arg_since > arg_until) { log_error("--since= must be before --until=."); return -EINVAL; } @@ -426,6 +494,11 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_follow && arg_reverse) { + log_error("Please specify either --reverse= or --follow=, not both."); + return -EINVAL; + } + return 1; } @@ -445,30 +518,33 @@ static int generate_new_id128(void) { "As UUID:\n" "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n" "As macro:\n" - "#define MESSAGE_XYZ SD_ID128_MAKE(", + "#define MESSAGE_XYZ SD_ID128_MAKE(", SD_ID128_FORMAT_VAL(id), SD_ID128_FORMAT_VAL(id)); - for (i = 0; i < 16; i++) printf("%02x%s", id.bytes[i], i != 15 ? "," : ""); + fputs(")\n\n", stdout); - fputs(")\n", stdout); + printf("As Python constant:\n" + ">>> import uuid\n" + ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n", + SD_ID128_FORMAT_VAL(id)); return 0; } static int add_matches(sd_journal *j, char **args) { char **i; - int r; assert(j); STRV_FOREACH(i, args) { + int r; if (streq(*i, "+")) r = sd_journal_add_disjunction(j); else if (path_is_absolute(*i)) { - char *p, *t = NULL; + _cleanup_free_ char *p, *t = NULL; const char *path; struct stat st; @@ -476,7 +552,6 @@ static int add_matches(sd_journal *j, char **args) { path = p ? p : *i; if (stat(path, &st) < 0) { - free(p); log_error("Couldn't stat file: %m"); return -errno; } @@ -488,18 +563,14 @@ static int add_matches(sd_journal *j, char **args) { else if (S_ISBLK(st.st_mode)) asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev)); else { - free(p); - log_error("File is not a device node, regular file or is not executable: %s", *i); + log_error("File is neither a device node, nor regular file, nor executable: %s", *i); return -EINVAL; } - free(p); - if (!t) return log_oom(); r = sd_journal_add_match(j, t, 0); - free(t); } else r = sd_journal_add_match(j, *i, 0); @@ -535,39 +606,57 @@ static int add_this_boot(sd_journal *j) { return r; } + r = sd_journal_add_conjunction(j); + if (r < 0) + return r; + return 0; } -static int add_unit(sd_journal *j) { - _cleanup_free_ char *m = NULL, *u = NULL; +static int add_units(sd_journal *j) { + _cleanup_free_ char *u = NULL; int r; + char **i; assert(j); - if (isempty(arg_unit)) - return 0; + STRV_FOREACH(i, arg_system_units) { + u = unit_name_mangle(*i); + if (!u) + return log_oom(); + r = add_matches_for_unit(j, u); + if (r < 0) + return r; + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; + } - u = unit_name_mangle(arg_unit); - if (!u) - return log_oom(); + STRV_FOREACH(i, arg_user_units) { + u = unit_name_mangle(*i); + if (!u) + return log_oom(); - m = strappend("_SYSTEMD_UNIT=", u); - if (!m) - return log_oom(); + r = add_matches_for_user_unit(j, u, getuid()); + if (r < 0) + return r; + + r = sd_journal_add_disjunction(j); + if (r < 0) + return r; - r = sd_journal_add_match(j, m, strlen(m)); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); - return r; } + r = sd_journal_add_conjunction(j); + if (r < 0) + return r; + return 0; } static int add_priorities(sd_journal *j) { char match[] = "PRIORITY=0"; int i, r; - assert(j); if (arg_priorities == 0xFF) @@ -584,6 +673,10 @@ static int add_priorities(sd_journal *j) { } } + r = sd_journal_add_conjunction(j); + if (r < 0) + return r; + return 0; } @@ -737,12 +830,12 @@ static int setup_keys(void) { fprintf(stderr, ANSI_HIGHLIGHT_OFF "\n" "The sealing key is automatically changed every %s.\n", - format_timespan(tsb, sizeof(tsb), arg_interval)); + format_timespan(tsb, sizeof(tsb), arg_interval, 0)); hn = gethostname_malloc(); if (hn) { - hostname_cleanup(hn); + hostname_cleanup(hn, false); fprintf(stderr, "\nThe keys have been generated for host %s/" SD_ID128_FORMAT_STR ".\n", hn, SD_ID128_FORMAT_VAL(machine)); } else fprintf(stderr, "\nThe keys have been generated for host " SD_ID128_FORMAT_STR ".\n", SD_ID128_FORMAT_VAL(machine)); @@ -811,10 +904,10 @@ static int verify(sd_journal *j) { log_info("=> Validated from %s to %s, final %s entries not sealed.", format_timestamp(a, sizeof(a), first), format_timestamp(b, sizeof(b), validated), - format_timespan(c, sizeof(c), last > validated ? last - validated : 0)); + format_timespan(c, sizeof(c), last > validated ? last - validated : 0, 0)); } else if (last > 0) log_info("=> No sealing yet, %s of entries not sealed.", - format_timespan(c, sizeof(c), last - first)); + format_timespan(c, sizeof(c), last - first, 0)); else log_info("=> No sealing yet, no entries in file."); } @@ -824,33 +917,122 @@ static int verify(sd_journal *j) { return r; } -static int access_check(void) { - #ifdef HAVE_ACL - if (access("/var/log/journal", F_OK) < 0 && geteuid() != 0 && in_group("adm") <= 0) { - log_error("Unprivileged users can't see messages unless persistent log storage is enabled. Users in the group 'adm' can always see messages."); - return -EACCES; +static int access_check_var_log_journal(sd_journal *j) { + _cleanup_strv_free_ char **g = NULL; + bool have_access; + int r; + + assert(j); + + have_access = in_group("systemd-journal") > 0; + + if (!have_access) { + /* Let's enumerate all groups from the default ACL of + * the directory, which generally should allow access + * to most journal files too */ + r = search_acl_groups(&g, "/var/log/journal/", &have_access); + if (r < 0) + return r; } - if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0) - log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this notice off."); -#else - if (geteuid() != 0 && in_group("adm") <= 0) { - log_error("No access to messages. Only users in the group 'adm' can see messages."); - return -EACCES; + if (!have_access) { + + if (strv_isempty(g)) + log_notice("Hint: You are currently not seeing messages from other users and the system.\n" + " Users in the 'systemd-journal' group can see all messages. Pass -q to\n" + " turn off this notice."); + else { + _cleanup_free_ char *s = NULL; + + r = strv_extend(&g, "systemd-journal"); + if (r < 0) + return log_oom(); + + strv_sort(g); + strv_uniq(g); + + s = strv_join(g, "', '"); + if (!s) + return log_oom(); + + log_notice("Hint: You are currently not seeing messages from other users and the system.\n" + " Users in the groups '%s' can see all messages.\n" + " Pass -q to turn off this notice.", s); + } } -#endif return 0; } +#endif + +static int access_check(sd_journal *j) { + Iterator it; + void *code; + int r = 0; + + assert(j); + + if (set_isempty(j->errors)) { + if (hashmap_isempty(j->files)) + log_notice("No journal files were found."); + return 0; + } + + if (set_contains(j->errors, INT_TO_PTR(-EACCES))) { +#ifdef HAVE_ACL + /* If /var/log/journal doesn't even exist, + * unprivileged users have no access at all */ + if (access("/var/log/journal", F_OK) < 0 && + geteuid() != 0 && + in_group("systemd-journal") <= 0) { + log_error("Unprivileged users cannot access messages, unless persistent log storage is\n" + "enabled. Users in the 'systemd-journal' group may always access messages."); + return -EACCES; + } + + /* If /var/log/journal exists, try to pring a nice + notice if the user lacks access to it */ + if (!arg_quiet && geteuid() != 0) { + r = access_check_var_log_journal(j); + if (r < 0) + return r; + } +#else + if (geteuid() != 0 && in_group("systemd-journal") <= 0) { + log_error("Unprivileged users cannot access messages. Users in the 'systemd-journal' group\n" + "group may access messages."); + return -EACCES; + } +#endif + + if (hashmap_isempty(j->files)) { + log_error("No journal files were opened due to insufficient permissions."); + r = -EACCES; + } + } + + SET_FOREACH(code, j->errors, it) { + int err; + + err = -PTR_TO_INT(code); + assert(err > 0); + + if (err != EACCES) + log_warning("Error was encountered while opening journal files: %s", + strerror(err)); + } + + return r; +} int main(int argc, char *argv[]) { int r; - sd_journal *j = NULL; + _cleanup_journal_close_ sd_journal*j = NULL; bool need_seek = false; sd_id128_t previous_boot_id; - bool previous_boot_id_valid = false; - unsigned n_shown = 0; + bool previous_boot_id_valid = false, first_line = true; + int n_shown = 0; setlocale(LC_ALL, ""); log_parse_environment(); @@ -872,21 +1054,40 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_action == ACTION_LIST_CATALOG) { - r = catalog_list(stdout); - if (r < 0) - log_error("Failed to list catalog: %s", strerror(-r)); - goto finish; - } + if (arg_action == ACTION_UPDATE_CATALOG || + arg_action == ACTION_LIST_CATALOG || + arg_action == ACTION_DUMP_CATALOG) { - if (arg_action == ACTION_UPDATE_CATALOG) { - r = catalog_update(); - goto finish; - } + const char* database = CATALOG_DATABASE; + _cleanup_free_ char *copy = NULL; + if (arg_root) { + copy = strjoin(arg_root, "/", CATALOG_DATABASE, NULL); + if (!copy) { + r = log_oom(); + goto finish; + } + path_kill_slashes(copy); + database = copy; + } + + if (arg_action == ACTION_UPDATE_CATALOG) { + r = catalog_update(database, arg_root, catalog_file_dirs); + if (r < 0) + log_error("Failed to list catalog: %s", strerror(-r)); + } else { + bool oneline = arg_action == ACTION_LIST_CATALOG; + + if (optind < argc) + r = catalog_list_items(stdout, database, + oneline, argv + optind); + else + r = catalog_list(stdout, database, oneline); + if (r < 0) + log_error("Failed to list catalog: %s", strerror(-r)); + } - r = access_check(); - if (r < 0) goto finish; + } if (arg_directory) r = sd_journal_open_directory(&j, arg_directory, 0); @@ -894,9 +1095,13 @@ int main(int argc, char *argv[]) { r = sd_journal_open(&j, arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY); if (r < 0) { log_error("Failed to open journal: %s", strerror(-r)); - goto finish; + return EXIT_FAILURE; } + r = access_check(j); + if (r < 0) + return EXIT_FAILURE; + if (arg_action == ACTION_VERIFY) { r = verify(j); goto finish; @@ -904,8 +1109,7 @@ int main(int argc, char *argv[]) { if (arg_action == ACTION_PRINT_HEADER) { journal_print_header(j); - r = 0; - goto finish; + return EXIT_SUCCESS; } if (arg_action == ACTION_DISK_USAGE) { @@ -914,43 +1118,57 @@ int main(int argc, char *argv[]) { r = sd_journal_get_usage(j, &bytes); if (r < 0) - goto finish; + return EXIT_FAILURE; - printf("Journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes)); - r = 0; - goto finish; + printf("Journals take up %s on disk.\n", + format_bytes(sbytes, sizeof(sbytes), bytes)); + return EXIT_SUCCESS; } r = add_this_boot(j); if (r < 0) - goto finish; + return EXIT_FAILURE; + + r = add_units(j); + strv_free(arg_system_units); + strv_free(arg_user_units); - r = add_unit(j); if (r < 0) - goto finish; + return EXIT_FAILURE; + + r = add_priorities(j); + if (r < 0) + return EXIT_FAILURE; r = add_matches(j, argv + optind); if (r < 0) - goto finish; + return EXIT_FAILURE; - r = add_priorities(j); + /* Opening the fd now means the first sd_journal_wait() will actually wait */ + r = sd_journal_get_fd(j); if (r < 0) - goto finish; + return EXIT_FAILURE; if (arg_field) { const void *data; size_t size; + r = sd_journal_set_data_threshold(j, 0); + if (r < 0) { + log_error("Failed to unset data size threshold"); + return EXIT_FAILURE; + } + r = sd_journal_query_unique(j, arg_field); if (r < 0) { log_error("Failed to query unique data objects: %s", strerror(-r)); - goto finish; + return EXIT_FAILURE; } SD_JOURNAL_FOREACH_UNIQUE(j, data, size) { const void *eq; - if (arg_lines > 0 && n_shown >= arg_lines) + if (arg_lines >= 0 && n_shown >= arg_lines) break; eq = memchr(data, '=', size); @@ -962,41 +1180,59 @@ int main(int argc, char *argv[]) { n_shown ++; } - r = 0; - goto finish; + return EXIT_SUCCESS; } if (arg_cursor) { r = sd_journal_seek_cursor(j, arg_cursor); if (r < 0) { log_error("Failed to seek to cursor: %s", strerror(-r)); - goto finish; + return EXIT_FAILURE; } + if (!arg_reverse) + r = sd_journal_next(j); + else + r = sd_journal_previous(j); - r = sd_journal_next(j); - - } else if (arg_since_set) { + } else if (arg_since_set && !arg_reverse) { r = sd_journal_seek_realtime_usec(j, arg_since); if (r < 0) { log_error("Failed to seek to date: %s", strerror(-r)); - goto finish; + return EXIT_FAILURE; } r = sd_journal_next(j); - } else if (arg_lines > 0) { + } else if (arg_until_set && arg_reverse) { + r = sd_journal_seek_realtime_usec(j, arg_until); + if (r < 0) { + log_error("Failed to seek to date: %s", strerror(-r)); + return EXIT_FAILURE; + } + r = sd_journal_previous(j); + + } else if (arg_lines >= 0) { r = sd_journal_seek_tail(j); if (r < 0) { log_error("Failed to seek to tail: %s", strerror(-r)); - goto finish; + return EXIT_FAILURE; } r = sd_journal_previous_skip(j, arg_lines); + } else if (arg_reverse) { + r = sd_journal_seek_tail(j); + if (r < 0) { + log_error("Failed to seek to tail: %s", strerror(-r)); + return EXIT_FAILURE; + } + + r = sd_journal_previous(j); + } else { r = sd_journal_seek_head(j); if (r < 0) { log_error("Failed to seek to head: %s", strerror(-r)); - goto finish; + return EXIT_FAILURE; } r = sd_journal_next(j); @@ -1004,11 +1240,11 @@ int main(int argc, char *argv[]) { if (r < 0) { log_error("Failed to iterate through journal: %s", strerror(-r)); - goto finish; + return EXIT_FAILURE; } if (!arg_no_pager && !arg_follow) - pager_open(); + pager_open(arg_pager_end); if (!arg_quiet) { usec_t start, end; @@ -1032,11 +1268,14 @@ int main(int argc, char *argv[]) { } for (;;) { - while (arg_lines == 0 || arg_follow || n_shown < arg_lines) { + while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) { int flags; if (need_seek) { - r = sd_journal_next(j); + if (!arg_reverse) + r = sd_journal_next(j); + else + r = sd_journal_previous(j); if (r < 0) { log_error("Failed to iterate through journal: %s", strerror(-r)); goto finish; @@ -1046,7 +1285,7 @@ int main(int argc, char *argv[]) { if (r == 0) break; - if (arg_until_set) { + if (arg_until_set && !arg_reverse) { usec_t usec; r = sd_journal_get_realtime_usec(j, &usec); @@ -1054,6 +1293,20 @@ int main(int argc, char *argv[]) { log_error("Failed to determine timestamp: %s", strerror(-r)); goto finish; } + if (usec > arg_until) + goto finish; + } + + if (arg_since_set && arg_reverse) { + usec_t usec; + + r = sd_journal_get_realtime_usec(j, &usec); + if (r < 0) { + log_error("Failed to determine timestamp: %s", strerror(-r)); + goto finish; + } + if (usec < arg_since) + goto finish; } if (!arg_merge) { @@ -1077,7 +1330,7 @@ int main(int argc, char *argv[]) { arg_catalog * OUTPUT_CATALOG; r = output_journal(stdout, j, arg_output, 0, flags); - if (r < 0) + if (r < 0 || ferror(stdout)) goto finish; need_seek = true; @@ -1092,12 +1345,11 @@ int main(int argc, char *argv[]) { log_error("Couldn't wait for journal event: %s", strerror(-r)); goto finish; } + + first_line = false; } finish: - if (j) - sd_journal_close(j); - pager_close(); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf index 1baef1411c..2ecba3bd0e 100644 --- a/src/journal/journald-gperf.gperf +++ b/src/journal/journald-gperf.gperf @@ -18,7 +18,8 @@ struct ConfigPerfItem; Journal.Storage, config_parse_storage, 0, offsetof(Server, storage) Journal.Compress, config_parse_bool, 0, offsetof(Server, compress) Journal.Seal, config_parse_bool, 0, offsetof(Server, seal) -Journal.RateLimitInterval, config_parse_usec, 0, offsetof(Server, rate_limit_interval) +Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec) +Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval) Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst) Journal.SystemMaxUse, config_parse_bytes_off, 0, offsetof(Server, system_metrics.max_use) Journal.SystemMaxFileSize, config_parse_bytes_off, 0, offsetof(Server, system_metrics.max_size) @@ -26,8 +27,8 @@ Journal.SystemKeepFree, config_parse_bytes_off, 0, offsetof(Server, system_m Journal.RuntimeMaxUse, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.max_use) Journal.RuntimeMaxFileSize, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.max_size) Journal.RuntimeKeepFree, config_parse_bytes_off, 0, offsetof(Server, runtime_metrics.keep_free) -Journal.MaxRetentionSec, config_parse_usec, 0, offsetof(Server, max_retention_usec) -Journal.MaxFileSec, config_parse_usec, 0, offsetof(Server, max_file_usec) +Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Server, max_retention_usec) +Journal.MaxFileSec, config_parse_sec, 0, offsetof(Server, max_file_usec) Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Server, forward_to_syslog) Journal.ForwardToKMsg, config_parse_bool, 0, offsetof(Server, forward_to_kmsg) Journal.ForwardToConsole, config_parse_bool, 0, offsetof(Server, forward_to_console) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index b8198760d6..2f536320f8 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -250,10 +250,12 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) { break; g = udev_list_entry_get_name(ll); - b = strappend("_UDEV_DEVLINK=", g); if (g) { - IOVEC_SET_STRING(iovec[n++], b); - z++; + b = strappend("_UDEV_DEVLINK=", g); + if (b) { + IOVEC_SET_STRING(iovec[n++], b); + z++; + } } j++; diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 069114778b..f878dfc911 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -31,8 +31,10 @@ #include "journald-console.h" #include "journald-syslog.h" -#define ENTRY_SIZE_MAX (1024*1024*64) -#define DATA_SIZE_MAX (1024*1024*64) +/* Make sure not to make this smaller than the maximum coredump + * size. See COREDUMP_MAX in coredump.c */ +#define ENTRY_SIZE_MAX (1024*1024*768) +#define DATA_SIZE_MAX (1024*1024*768) static bool valid_user_field(const char *p, size_t l) { const char *a; @@ -121,11 +123,12 @@ void server_process_native_message( /* A property follows */ - if (n+N_IOVEC_META_FIELDS >= m) { + /* n received properties, +1 for _TRANSPORT */ + if (n + 1 + N_IOVEC_META_FIELDS >= m) { struct iovec *c; unsigned u; - u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U); + u = MAX((n + 1 + N_IOVEC_META_FIELDS) * 2U, 4U); c = realloc(iovec, u * sizeof(struct iovec)); if (!c) { log_oom(); diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index 8bd68476a3..32e35a926d 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -115,7 +115,7 @@ void journal_rate_limit_free(JournalRateLimit *r) { free(r); } -static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) { +_pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) { unsigned i; assert(g); @@ -170,21 +170,6 @@ fail: return NULL; } -static uint64_t u64log2(uint64_t n) { - unsigned r; - - if (n <= 1) - return 0; - - r = 0; - for (;;) { - n = n >> 1; - if (!n) - return r; - r++; - } -} - static unsigned burst_modulate(unsigned burst, uint64_t available) { unsigned k; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 43ffe75560..cc52b8a5c9 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -24,16 +24,14 @@ #include <linux/sockios.h> #include <sys/statvfs.h> #include <sys/mman.h> +#include <sys/timerfd.h> #include <libudev.h> #include <systemd/sd-journal.h> #include <systemd/sd-messages.h> #include <systemd/sd-daemon.h> -#ifdef HAVE_LOGIND -#include <systemd/sd-login.h> -#endif - +#include "fileio.h" #include "mkdir.h" #include "hashmap.h" #include "journal-file.h" @@ -66,6 +64,7 @@ #define USER_JOURNALS_MAX 1024 +#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE) #define DEFAULT_RATE_LIMIT_INTERVAL (10*USEC_PER_SEC) #define DEFAULT_RATE_LIMIT_BURST 200 @@ -91,13 +90,14 @@ DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting"); static uint64_t available_space(Server *s) { - char ids[33], *p; + char ids[33]; + _cleanup_free_ char *p = NULL; const char *f; sd_id128_t machine; struct statvfs ss; uint64_t sum = 0, avail = 0, ss_avail = 0; int r; - DIR *d; + _cleanup_closedir_ DIR *d = NULL; usec_t ts; JournalMetrics *m; @@ -125,13 +125,11 @@ static uint64_t available_space(Server *s) { return 0; d = opendir(p); - free(p); - if (!d) return 0; if (fstatvfs(dirfd(d), &ss) < 0) - goto finish; + return 0; for (;;) { struct stat st; @@ -170,14 +168,11 @@ static uint64_t available_space(Server *s) { s->cached_available_space = avail; s->cached_available_space_timestamp = ts; -finish: - closedir(d); - return avail; } static void server_read_file_gid(Server *s) { - const char *adm = "adm"; + const char *g = "systemd-journal"; int r; assert(s); @@ -185,9 +180,9 @@ static void server_read_file_gid(Server *s) { if (s->file_gid_valid) return; - r = get_group_creds(&adm, &s->file_gid); + r = get_group_creds(&g, &s->file_gid); if (r < 0) - log_warning("Failed to resolve 'adm' group: %s", strerror(-r)); + log_warning("Failed to resolve '%s' group: %s", g, strerror(-r)); /* if we couldn't read the gid, then it will be 0, but that's * fine and we shouldn't try to resolve the group again, so @@ -232,9 +227,9 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) { } } + /* We do not recalculate the mask here, so that the fchmod() mask above stays intact. */ if (acl_get_permset(entry, &permset) < 0 || - acl_add_perm(permset, ACL_READ) < 0 || - acl_calc_mask(&acl) < 0) { + acl_add_perm(permset, ACL_READ) < 0) { log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); goto finish; } @@ -347,6 +342,33 @@ void server_rotate(Server *s) { } } +void server_sync(Server *s) { + JournalFile *f; + void *k; + Iterator i; + int r; + + static const struct itimerspec sync_timer_disable = {}; + + if (s->system_journal) { + r = journal_file_set_offline(s->system_journal); + if (r < 0) + log_error("Failed to sync system journal: %s", strerror(-r)); + } + + HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) { + r = journal_file_set_offline(f); + if (r < 0) + log_error("Failed to sync user journal: %s", strerror(-r)); + } + + r = timerfd_settime(s->sync_timer_fd, 0, &sync_timer_disable, NULL); + if (r < 0) + log_error("Failed to disable max timer: %m"); + + s->sync_scheduled = false; +} + void server_vacuum(Server *s) { char *p; char ids[33]; @@ -394,48 +416,6 @@ void server_vacuum(Server *s) { 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; -} - bool shall_try_append_again(JournalFile *f, int r) { /* -E2BIG Hit configured limit @@ -490,8 +470,10 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned } r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL); - if (r >= 0) + if (r >= 0) { + server_schedule_sync(s); return; + } if (vacuumed || !shall_try_append_again(f, r)) { log_error("Failed to write entry, ignoring: %s", strerror(-r)); @@ -519,18 +501,26 @@ static void dispatch_message_real( const char *label, size_t label_len, const char *unit_id) { - 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, *session = NULL, - *owner_uid = NULL, *unit = NULL, *selinux_context = NULL; - - char idbuf[33]; + char pid[sizeof("_PID=") + DECIMAL_STR_MAX(pid_t)], + uid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)], + gid[sizeof("_GID=") + DECIMAL_STR_MAX(gid_t)], + owner_uid[sizeof("_SYSTEMD_OWNER_UID=") + DECIMAL_STR_MAX(uid_t)], + source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)], + boot_id[sizeof("_BOOT_ID=") + 32] = "_BOOT_ID=", + machine_id[sizeof("_MACHINE_ID=") + 32] = "_MACHINE_ID="; + char *comm, *exe, *cmdline, *cgroup, *session, *unit, *hostname; sd_id128_t id; int r; - char *t; - uid_t loginuid = 0, realuid = 0; + char *t, *c; + uid_t realuid = 0, owner = 0, journal_uid; + bool owner_valid = false; +#ifdef HAVE_AUDIT + char audit_session[sizeof("_AUDIT_SESSION=") + DECIMAL_STR_MAX(uint32_t)], + audit_loginuid[sizeof("_AUDIT_LOGINUID=") + DECIMAL_STR_MAX(uid_t)]; + + uint32_t audit; + uid_t loginuid; +#endif assert(s); assert(iovec); @@ -538,165 +528,154 @@ static void dispatch_message_real( assert(n + N_IOVEC_META_FIELDS <= m); if (ucred) { - uint32_t audit; -#ifdef HAVE_LOGIND - uid_t owner; -#endif - realuid = ucred->uid; - if (asprintf(&pid, "_PID=%lu", (unsigned long) ucred->pid) >= 0) - IOVEC_SET_STRING(iovec[n++], pid); + sprintf(pid, "_PID=%lu", (unsigned long) ucred->pid); + IOVEC_SET_STRING(iovec[n++], pid); - if (asprintf(&uid, "_UID=%lu", (unsigned long) ucred->uid) >= 0) - IOVEC_SET_STRING(iovec[n++], uid); + sprintf(uid, "_UID=%lu", (unsigned long) ucred->uid); + IOVEC_SET_STRING(iovec[n++], uid); - if (asprintf(&gid, "_GID=%lu", (unsigned long) ucred->gid) >= 0) - IOVEC_SET_STRING(iovec[n++], gid); + sprintf(gid, "_GID=%lu", (unsigned long) ucred->gid); + IOVEC_SET_STRING(iovec[n++], gid); r = get_process_comm(ucred->pid, &t); if (r >= 0) { - comm = strappend("_COMM=", t); + comm = strappenda("_COMM=", t); free(t); - - if (comm) - IOVEC_SET_STRING(iovec[n++], comm); + IOVEC_SET_STRING(iovec[n++], comm); } r = get_process_exe(ucred->pid, &t); if (r >= 0) { - exe = strappend("_EXE=", t); + exe = strappenda("_EXE=", t); free(t); - - if (exe) - IOVEC_SET_STRING(iovec[n++], exe); + IOVEC_SET_STRING(iovec[n++], exe); } - r = get_process_cmdline(ucred->pid, LINE_MAX, false, &t); + r = get_process_cmdline(ucred->pid, 0, false, &t); if (r >= 0) { - cmdline = strappend("_CMDLINE=", t); + cmdline = strappenda("_CMDLINE=", t); free(t); - - if (cmdline) - IOVEC_SET_STRING(iovec[n++], cmdline); + IOVEC_SET_STRING(iovec[n++], cmdline); } +#ifdef HAVE_AUDIT r = audit_session_from_pid(ucred->pid, &audit); - if (r >= 0) - if (asprintf(&audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit) >= 0) - IOVEC_SET_STRING(iovec[n++], audit_session); + if (r >= 0) { + sprintf(audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit); + IOVEC_SET_STRING(iovec[n++], audit_session); + } r = audit_loginuid_from_pid(ucred->pid, &loginuid); - if (r >= 0) - if (asprintf(&audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0) - IOVEC_SET_STRING(iovec[n++], audit_loginuid); - - t = shortened_cgroup_path(ucred->pid); - if (t) { - cgroup = strappend("_SYSTEMD_CGROUP=", t); - free(t); - - if (cgroup) - IOVEC_SET_STRING(iovec[n++], cgroup); + if (r >= 0) { + sprintf(audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid); + IOVEC_SET_STRING(iovec[n++], audit_loginuid); } +#endif -#ifdef HAVE_LOGIND - if (sd_pid_get_session(ucred->pid, &t) >= 0) { - session = strappend("_SYSTEMD_SESSION=", t); - free(t); + r = cg_pid_get_path_shifted(ucred->pid, NULL, &c); + if (r >= 0) { + cgroup = strappenda("_SYSTEMD_CGROUP=", c); + IOVEC_SET_STRING(iovec[n++], cgroup); - if (session) + r = cg_path_get_session(c, &t); + if (r >= 0) { + session = strappenda("_SYSTEMD_SESSION=", t); + free(t); IOVEC_SET_STRING(iovec[n++], session); - } + } - 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); -#endif + if (cg_path_get_owner_uid(c, &owner) >= 0) { + owner_valid = true; - if (cg_pid_get_unit(ucred->pid, &t) >= 0) { - unit = strappend("_SYSTEMD_UNIT=", t); - free(t); - } else if (unit_id) - unit = strappend("_SYSTEMD_UNIT=", unit_id); + sprintf(owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner); + IOVEC_SET_STRING(iovec[n++], owner_uid); + } - if (unit) - IOVEC_SET_STRING(iovec[n++], unit); + if (cg_path_get_unit(c, &t) >= 0) { + unit = strappenda("_SYSTEMD_UNIT=", t); + free(t); + } else if (cg_path_get_user_unit(c, &t) >= 0) { + unit = strappenda("_SYSTEMD_USER_UNIT=", t); + free(t); + } else if (unit_id) { + if (session) + unit = strappenda("_SYSTEMD_USER_UNIT=", unit_id); + else + unit = strappenda("_SYSTEMD_UNIT=", unit_id); + } else + unit = NULL; + + if (unit) + IOVEC_SET_STRING(iovec[n++], unit); + + free(c); + } #ifdef HAVE_SELINUX 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); - } + char *selinux_context = alloca(sizeof("_SELINUX_CONTEXT=") + label_len); + + *((char*) mempcpy(stpcpy(selinux_context, "_SELINUX_CONTEXT="), label, 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); + char *selinux_context = strappenda("_SELINUX_CONTEXT=", con); freecon(con); + IOVEC_SET_STRING(iovec[n++], selinux_context); } } #endif } if (tv) { - if (asprintf(&source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu", - (unsigned long long) timeval_load(tv)) >= 0) - IOVEC_SET_STRING(iovec[n++], source_time); + sprintf(source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu", (unsigned long long) timeval_load(tv)); + IOVEC_SET_STRING(iovec[n++], source_time); } /* Note that strictly speaking storing the boot id here is * redundant since the entry includes this in-line * anyway. However, we need this indexed, too. */ r = sd_id128_get_boot(&id); - if (r >= 0) - if (asprintf(&boot_id, "_BOOT_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) - IOVEC_SET_STRING(iovec[n++], boot_id); + if (r >= 0) { + sd_id128_to_string(id, boot_id + sizeof("_BOOT_ID=") - 1); + IOVEC_SET_STRING(iovec[n++], boot_id); + } r = sd_id128_get_machine(&id); - if (r >= 0) - if (asprintf(&machine_id, "_MACHINE_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) - IOVEC_SET_STRING(iovec[n++], machine_id); + if (r >= 0) { + sd_id128_to_string(id, machine_id + sizeof("_MACHINE_ID=") - 1); + IOVEC_SET_STRING(iovec[n++], machine_id); + } t = gethostname_malloc(); if (t) { - hostname = strappend("_HOSTNAME=", t); + hostname = strappenda("_HOSTNAME=", t); free(t); - if (hostname) - IOVEC_SET_STRING(iovec[n++], hostname); + IOVEC_SET_STRING(iovec[n++], hostname); } assert(n <= m); - write_to_journal(s, - s->split_mode == SPLIT_NONE ? 0 : - (s->split_mode == SPLIT_UID ? realuid : - (realuid == 0 ? 0 : loginuid)), iovec, n); - - free(pid); - free(uid); - free(gid); - free(comm); - free(exe); - free(cmdline); - free(source_time); - free(boot_id); - free(machine_id); - free(hostname); - free(audit_session); - free(audit_loginuid); - free(cgroup); - free(session); - free(owner_uid); - free(unit); - free(selinux_context); + if (s->split_mode == SPLIT_UID && realuid > 0) + /* Split up strictly by any UID */ + journal_uid = realuid; + else if (s->split_mode == SPLIT_LOGIN && realuid > 0 && owner_valid && owner > 0) + /* Split up by login UIDs, this avoids creation of + * individual journals for system UIDs. We do this + * only if the realuid is not root, in order not to + * accidentally leak privileged information to the + * user that is logged by a privileged process that is + * part of an unprivileged session.*/ + journal_uid = owner; + else + journal_uid = 0; + + write_to_journal(s, journal_uid, iovec, n); } void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) { @@ -705,7 +684,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format, struct iovec iovec[N_IOVEC_META_FIELDS + 4]; int n = 0; va_list ap; - struct ucred ucred; + struct ucred ucred = {}; assert(s); assert(format); @@ -726,7 +705,6 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format, IOVEC_SET_STRING(iovec[n++], mid); } - zero(ucred); ucred.pid = getpid(); ucred.uid = getuid(); ucred.gid = getgid(); @@ -743,8 +721,9 @@ void server_dispatch_message( const char *unit_id, int priority) { - int rl; - char *path = NULL, *c; + int rl, r; + _cleanup_free_ char *path = NULL; + char *c; assert(s); assert(iovec || n == 0); @@ -758,8 +737,8 @@ void server_dispatch_message( if (!ucred) goto finish; - path = shortened_cgroup_path(ucred->pid); - if (!path) + r = cg_pid_get_path_shifted(ucred->pid, NULL, &path); + if (r < 0) goto finish; /* example: /user/lennart/3/foobar @@ -778,18 +757,16 @@ void server_dispatch_message( } } - rl = journal_rate_limit_test(s->rate_limit, path, priority & LOG_PRIMASK, available_space(s)); + rl = journal_rate_limit_test(s->rate_limit, path, + priority & LOG_PRIMASK, available_space(s)); - if (rl == 0) { - free(path); + if (rl == 0) return; - } /* Write a suppression message if we suppressed something */ if (rl > 1) - server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED, "Suppressed %u messages from %s", rl - 1, path); - - free(path); + server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED, + "Suppressed %u messages from %s", rl - 1, path); finish: dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id); @@ -961,6 +938,12 @@ int server_flush_to_var(Server *s) { server_rotate(s); server_vacuum(s); + if (!s->system_journal) { + log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful."); + r = -EIO; + goto finish; + } + log_debug("Retrying write."); r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); if (r < 0) { @@ -978,8 +961,7 @@ finish: if (r >= 0) rm_rf("/run/log/journal", false, true, false); - if (j) - sd_journal_close(j); + sd_journal_close(j); return r; } @@ -1009,11 +991,10 @@ int process_event(Server *s, struct epoll_event *ev) { return -errno; } - log_info("Received SIG%s", signal_to_string(sfsi.ssi_signo)); - if (sfsi.ssi_signo == SIGUSR1) { touch("/run/systemd/journal/flushed"); server_flush_to_var(s); + server_sync(s); return 1; } @@ -1023,8 +1004,23 @@ int process_event(Server *s, struct epoll_event *ev) { return 1; } + log_info("Received SIG%s", signal_to_string(sfsi.ssi_signo)); + return 0; + } else if (ev->data.fd == s->sync_timer_fd) { + int r; + uint64_t t; + + log_debug("Got sync request from epoll."); + + r = read(ev->data.fd, (void *)&t, sizeof(t)); + if (r < 0) + return 0; + + server_sync(s); + return 1; + } else if (ev->data.fd == s->dev_kmsg_fd) { int r; @@ -1234,7 +1230,8 @@ static int open_signalfd(Server *s) { } static int server_parse_proc_cmdline(Server *s) { - char *line, *w, *state; + _cleanup_free_ char *line = NULL; + char *w, *state; int r; size_t l; @@ -1248,13 +1245,11 @@ static int server_parse_proc_cmdline(Server *s) { } FOREACH_WORD_QUOTED(w, l, line, state) { - char *word; + _cleanup_free_ char *word; word = strndup(w, l); - if (!word) { - r = -ENOMEM; - goto finish; - } + if (!word) + return -ENOMEM; if (startswith(word, "systemd.journald.forward_to_syslog=")) { r = parse_boolean(word + 35); @@ -1276,25 +1271,18 @@ static int server_parse_proc_cmdline(Server *s) { s->forward_to_console = r; } else if (startswith(word, "systemd.journald")) log_warning("Invalid systemd.journald parameter. Ignoring."); - - free(word); } - r = 0; - -finish: - free(line); - return r; + return 0; } static int server_parse_config_file(Server *s) { - FILE *f; - const char *fn; + static const char fn[] = "/etc/systemd/journald.conf"; + _cleanup_fclose_ FILE *f = NULL; int r; assert(s); - fn = "/etc/systemd/journald.conf"; f = fopen(fn, "re"); if (!f) { if (errno == ENOENT) @@ -1304,25 +1292,75 @@ static int server_parse_config_file(Server *s) { return -errno; } - r = config_parse(fn, f, "Journal\0", config_item_perf_lookup, (void*) journald_gperf_lookup, false, s); + r = config_parse(NULL, fn, f, "Journal\0", config_item_perf_lookup, + (void*) journald_gperf_lookup, false, false, s); if (r < 0) log_warning("Failed to parse configuration file: %s", strerror(-r)); - fclose(f); - return r; } +static int server_open_sync_timer(Server *s) { + int r; + struct epoll_event ev; + + assert(s); + + s->sync_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (s->sync_timer_fd < 0) + return -errno; + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->sync_timer_fd; + + r = epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->sync_timer_fd, &ev); + if (r < 0) { + log_error("Failed to add idle timer fd to epoll object: %m"); + return -errno; + } + + return 0; +} + +int server_schedule_sync(Server *s) { + int r; + + assert(s); + + if (s->sync_scheduled) + return 0; + + if (s->sync_interval_usec) { + struct itimerspec sync_timer_enable = { + .it_value.tv_sec = s->sync_interval_usec / USEC_PER_SEC, + .it_value.tv_nsec = s->sync_interval_usec % MSEC_PER_SEC, + }; + + r = timerfd_settime(s->sync_timer_fd, 0, &sync_timer_enable, NULL); + if (r < 0) + return -errno; + } + + s->sync_scheduled = true; + + return 0; +} + int server_init(Server *s) { int n, r, fd; assert(s); zero(*s); - s->syslog_fd = s->native_fd = s->stdout_fd = s->signal_fd = s->epoll_fd = s->dev_kmsg_fd = -1; + s->sync_timer_fd = s->syslog_fd = s->native_fd = s->stdout_fd = + s->signal_fd = s->epoll_fd = s->dev_kmsg_fd = -1; s->compress = true; s->seal = true; + s->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC; + s->sync_scheduled = false; + s->rate_limit_interval = DEFAULT_RATE_LIMIT_INTERVAL; s->rate_limit_burst = DEFAULT_RATE_LIMIT_BURST; @@ -1338,6 +1376,12 @@ int server_init(Server *s) { server_parse_config_file(s); server_parse_proc_cmdline(s); + if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) { + log_debug("Setting both rate limit interval and burst from %llu,%u to 0,0", + (long long unsigned) s->rate_limit_interval, + s->rate_limit_burst); + s->rate_limit_interval = s->rate_limit_burst = 0; + } mkdir_p("/run/systemd/journal", 0755); @@ -1416,6 +1460,10 @@ int server_init(Server *s) { if (r < 0) return r; + r = server_open_sync_timer(s); + if (r < 0) + return r; + r = open_signalfd(s); if (r < 0) return r; @@ -1424,7 +1472,8 @@ int server_init(Server *s) { if (!s->udev) return -ENOMEM; - s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst); + s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, + s->rate_limit_burst); if (!s->rate_limit) return -ENOMEM; @@ -1487,6 +1536,9 @@ void server_done(Server *s) { if (s->dev_kmsg_fd >= 0) close_nointr_nofail(s->dev_kmsg_fd); + if (s->sync_timer_fd >= 0) + close_nointr_nofail(s->sync_timer_fd); + if (s->rate_limit) journal_rate_limit_free(s->rate_limit); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 9f50a29e50..129f7e8ab4 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -71,6 +71,7 @@ typedef struct Server { size_t buffer_size; JournalRateLimit *rate_limit; + usec_t sync_interval_usec; usec_t rate_limit_interval; unsigned rate_limit_burst; @@ -119,6 +120,9 @@ typedef struct Server { uint64_t *kernel_seqnum; struct udev *udev; + + int sync_timer_fd; + bool sync_scheduled; } Server; #define N_IOVEC_META_FIELDS 17 @@ -126,27 +130,29 @@ typedef struct Server { #define N_IOVEC_UDEV_FIELDS 32 void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority); -void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...); +void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_attr_(3,4); /* gperf lookup function */ const struct ConfigPerfItem* journald_gperf_lookup(const char *key, unsigned length); -int config_parse_storage(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_storage(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -const char *storage_to_string(Storage s); -Storage storage_from_string(const char *s); +const char *storage_to_string(Storage s) _const_; +Storage storage_from_string(const char *s) _pure_; -int config_parse_split_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_split_mode(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -const char *split_mode_to_string(SplitMode s); -SplitMode split_mode_from_string(const char *s); +const char *split_mode_to_string(SplitMode s) _const_; +SplitMode split_mode_from_string(const char *s) _pure_; void server_fix_perms(Server *s, JournalFile *f, uid_t uid); bool shall_try_append_again(JournalFile *f, int r); int server_init(Server *s); void server_done(Server *s); +void server_sync(Server *s); void server_vacuum(Server *s); void server_rotate(Server *s); +int server_schedule_sync(Server *s); int server_flush_to_var(Server *s); int process_event(Server *s, struct epoll_event *ev); void server_maybe_append_tags(Server *s); diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 7b88f747db..6d51c29083 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -175,7 +175,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) { case STDOUT_STREAM_PRIORITY: r = safe_atoi(p, &s->priority); - if (r < 0 || s->priority <= 0 || s->priority >= 999) { + if (r < 0 || s->priority < 0 || s->priority > 999) { log_warning("Failed to parse log priority line."); return -EINVAL; } @@ -412,13 +412,16 @@ fail: } int server_open_stdout_socket(Server *s) { - union sockaddr_union sa; int r; struct epoll_event ev; assert(s); if (s->stdout_fd < 0) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->stdout_fd < 0) { @@ -426,10 +429,6 @@ int server_open_stdout_socket(Server *s) { return -errno; } - zero(sa); - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path)); - unlink(sa.un.sun_path); r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index afddca3630..000f5acc10 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -34,28 +34,28 @@ #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC) static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) { - struct msghdr msghdr; + + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/syslog", + }; + struct msghdr msghdr = { + .msg_iov = (struct iovec *) iovec, + .msg_iovlen = n_iovec, + .msg_name = &sa, + .msg_namelen = offsetof(union sockaddr_union, un.sun_path) + + sizeof("/run/systemd/journal/syslog") - 1, + }; struct cmsghdr *cmsg; union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; } control; - union sockaddr_union sa; assert(s); assert(iovec); assert(n_iovec > 0); - zero(msghdr); - msghdr.msg_iov = (struct iovec*) iovec; - msghdr.msg_iovlen = n_iovec; - - zero(sa); - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/run/systemd/journal/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); - if (ucred) { zero(control); msghdr.msg_control = &control; @@ -412,13 +412,16 @@ void server_process_syslog_message( } int server_open_syslog_socket(Server *s) { - union sockaddr_union sa; int one, r; struct epoll_event ev; assert(s); if (s->syslog_fd < 0) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/dev/log", + }; s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->syslog_fd < 0) { @@ -426,10 +429,6 @@ int server_open_syslog_socket(Server *s) { return -errno; } - zero(sa); - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); - unlink(sa.un.sun_path); r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); diff --git a/src/journal/journald-syslog.h b/src/journal/journald-syslog.h index 7ff215b524..324b70eef0 100644 --- a/src/journal/journald-syslog.h +++ b/src/journal/journald-syslog.h @@ -23,7 +23,7 @@ #include "journald-server.h" -int syslog_fixup_facility(int priority); +int syslog_fixup_facility(int priority) _const_; void syslog_parse_priority(char **p, int *priority); size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid); diff --git a/src/journal/journald.c b/src/journal/journald.c index d6b9be5974..5fb10b1d3f 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -88,7 +88,6 @@ int main(int argc, char *argv[]) { /* Calculate when to rotate the next time */ t = (int) ((server.oldest_file_usec + server.max_retention_usec - n + USEC_PER_MSEC - 1) / USEC_PER_MSEC); - log_info("Sleeping for %i ms", t); } #ifdef HAVE_GCRYPT diff --git a/src/journal/journald.conf b/src/journal/journald.conf index 948318bc62..5410477201 100644 --- a/src/journal/journald.conf +++ b/src/journal/journald.conf @@ -12,6 +12,7 @@ #Compress=yes #Seal=yes #SplitMode=login +#SyncIntervalSec=5m #RateLimitInterval=10s #RateLimitBurst=200 #SystemMaxUse= diff --git a/src/journal/libsystemd-journal.sym b/src/journal/libsystemd-journal.sym index 7b602f59cb..449f37c4da 100644 --- a/src/journal/libsystemd-journal.sym +++ b/src/journal/libsystemd-journal.sym @@ -83,9 +83,24 @@ global: LIBSYSTEMD_JOURNAL_196 { global: - sd_journal_fd_reliable; sd_journal_get_catalog; sd_journal_get_catalog_for_message_id; sd_journal_set_data_threshold; sd_journal_get_data_threshold; } LIBSYSTEMD_JOURNAL_195; + +LIBSYSTEMD_JOURNAL_198 { +global: + sd_journal_reliable_fd; +} LIBSYSTEMD_JOURNAL_196; + +LIBSYSTEMD_JOURNAL_201 { +global: + sd_journal_get_events; + sd_journal_get_timeout; +} LIBSYSTEMD_JOURNAL_198; + +LIBSYSTEMD_JOURNAL_202 { +global: + sd_journal_add_conjunction; +} LIBSYSTEMD_JOURNAL_201; diff --git a/src/journal/lookup3.h b/src/journal/lookup3.h index 502b42c209..3224473a6a 100644 --- a/src/journal/lookup3.h +++ b/src/journal/lookup3.h @@ -5,13 +5,15 @@ #include <inttypes.h> #include <sys/types.h> -uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval); +#include "macro.h" + +uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval) _pure_; void jenkins_hashword2(const uint32_t *k, size_t length, uint32_t *pc, uint32_t *pb); -uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval); +uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval) _pure_; void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); -uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval); +uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval) _pure_; static inline uint64_t hash64(const void *data, size_t length) { uint32_t a = 0, b = 0; diff --git a/src/journal/microhttpd-util.c b/src/journal/microhttpd-util.c new file mode 100644 index 0000000000..382087c790 --- /dev/null +++ b/src/journal/microhttpd-util.c @@ -0,0 +1,37 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stddef.h> +#include <stdio.h> + +#include "microhttpd-util.h" +#include "log.h" +#include "macro.h" +#include "util.h" + +void microhttpd_logger(void *arg, const char *fmt, va_list ap) { + _cleanup_free_ char *f; + if (asprintf(&f, "microhttpd: %s", fmt) <= 0) { + log_oom(); + return; + } + log_metav(LOG_INFO, NULL, 0, NULL, f, ap); +} diff --git a/src/journal/microhttpd-util.h b/src/journal/microhttpd-util.h new file mode 100644 index 0000000000..20ad76990c --- /dev/null +++ b/src/journal/microhttpd-util.h @@ -0,0 +1,28 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#pragma once + +#include <stdarg.h> + +#include "macro.h" + +void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_attr_(2, 0); diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index 251aefe121..767f555526 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -41,9 +41,9 @@ struct Window { bool keep_always; bool in_unused; + int prot; void *ptr; uint64_t offset; - int prot; size_t size; FileDescriptor *fd; @@ -70,12 +70,11 @@ struct FileDescriptor { struct MMapCache { int n_ref; + unsigned n_windows; Hashmap *fds; Hashmap *contexts; - unsigned n_windows; - LIST_HEAD(Window, unused); Window *last_unused; }; @@ -134,7 +133,7 @@ static void window_free(Window *w) { free(w); } -static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) { +_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) { assert(w); assert(fd >= 0); assert(size > 0); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 095fbb249c..c21712b7c4 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -49,6 +49,21 @@ #define DEFAULT_DATA_THRESHOLD (64*1024) +/* We return an error here only if we didn't manage to + memorize the real error. */ +static int set_put_error(sd_journal *j, int r) { + int k; + + if (r >= 0) + return r; + + k = set_ensure_allocated(&j->errors, trivial_hash_func, trivial_compare_func); + if (k < 0) + return k; + + return set_put(j->errors, INT_TO_PTR(r)); +} + static void detach_location(sd_journal *j) { Iterator i; JournalFile *f; @@ -94,6 +109,9 @@ static void set_location(sd_journal *j, LocationType type, JournalFile *f, Objec init_location(&j->current_location, type, f, o); + if (j->current_file) + j->current_file->current_offset = 0; + j->current_file = f; j->current_field = 0; @@ -188,7 +206,7 @@ static void match_free_if_empty(Match *m) { } _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { - Match *l2, *l3, *add_here = NULL, *m; + Match *l3, *l4, *add_here = NULL, *m; le64_t le_hash; if (!j) @@ -203,44 +221,52 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) if (!match_is_valid(data, size)) return -EINVAL; - /* level 0: OR term - * level 1: AND terms - * level 2: OR terms - * level 3: concrete matches */ + /* level 0: AND term + * level 1: OR terms + * level 2: AND terms + * level 3: OR terms + * level 4: concrete matches */ if (!j->level0) { - j->level0 = match_new(NULL, MATCH_OR_TERM); + j->level0 = match_new(NULL, MATCH_AND_TERM); if (!j->level0) return -ENOMEM; } if (!j->level1) { - j->level1 = match_new(j->level0, MATCH_AND_TERM); + j->level1 = match_new(j->level0, MATCH_OR_TERM); if (!j->level1) return -ENOMEM; } - assert(j->level0->type == MATCH_OR_TERM); - assert(j->level1->type == MATCH_AND_TERM); + if (!j->level2) { + j->level2 = match_new(j->level1, MATCH_AND_TERM); + if (!j->level2) + return -ENOMEM; + } + + assert(j->level0->type == MATCH_AND_TERM); + assert(j->level1->type == MATCH_OR_TERM); + assert(j->level2->type == MATCH_AND_TERM); le_hash = htole64(hash64(data, size)); - LIST_FOREACH(matches, l2, j->level1->matches) { - assert(l2->type == MATCH_OR_TERM); + LIST_FOREACH(matches, l3, j->level2->matches) { + assert(l3->type == MATCH_OR_TERM); - LIST_FOREACH(matches, l3, l2->matches) { - assert(l3->type == MATCH_DISCRETE); + LIST_FOREACH(matches, l4, l3->matches) { + assert(l4->type == MATCH_DISCRETE); /* Exactly the same match already? Then ignore * this addition */ - if (l3->le_hash == le_hash && - l3->size == size && - memcmp(l3->data, data, size) == 0) + if (l4->le_hash == le_hash && + l4->size == size && + memcmp(l4->data, data, size) == 0) return 0; /* Same field? Then let's add this to this OR term */ - if (same_field(data, size, l3->data, l3->size)) { - add_here = l2; + if (same_field(data, size, l4->data, l4->size)) { + add_here = l3; break; } } @@ -250,7 +276,7 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) } if (!add_here) { - add_here = match_new(j->level1, MATCH_OR_TERM); + add_here = match_new(j->level2, MATCH_OR_TERM); if (!add_here) goto fail; } @@ -273,6 +299,9 @@ fail: if (add_here) match_free_if_empty(add_here); + if (j->level2) + match_free_if_empty(j->level2); + if (j->level1) match_free_if_empty(j->level1); @@ -282,9 +311,7 @@ fail: return -ENOMEM; } -_public_ int sd_journal_add_disjunction(sd_journal *j) { - Match *m; - +_public_ int sd_journal_add_conjunction(sd_journal *j) { assert(j); if (!j->level0) @@ -296,11 +323,28 @@ _public_ int sd_journal_add_disjunction(sd_journal *j) { if (!j->level1->matches) return 0; - m = match_new(j->level0, MATCH_AND_TERM); - if (!m) - return -ENOMEM; + j->level1 = NULL; + j->level2 = NULL; + + return 0; +} + +_public_ int sd_journal_add_disjunction(sd_journal *j) { + assert(j); + + if (!j->level0) + return 0; + + if (!j->level1) + return 0; + + if (!j->level2) + return 0; - j->level1 = m; + if (!j->level2->matches) + return 0; + + j->level2 = NULL; return 0; } @@ -365,13 +409,13 @@ _public_ void sd_journal_flush_matches(sd_journal *j) { if (j->level0) match_free(j->level0); - j->level0 = j->level1 = NULL; + j->level0 = j->level1 = j->level2 = NULL; detach_location(j); } static int compare_entry_order(JournalFile *af, Object *_ao, - JournalFile *bf, uint64_t bp) { + JournalFile *bf, uint64_t bp) { uint64_t a, b; Object *ao, *bo; @@ -454,7 +498,7 @@ static int compare_entry_order(JournalFile *af, Object *_ao, return 0; } -static int compare_with_location(JournalFile *af, Object *ao, Location *l) { +_pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) { uint64_t a; assert(af); @@ -587,7 +631,7 @@ static int next_for_match( return r; if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) && - (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) { + (np == 0 || (direction == DIRECTION_DOWN ? cp > np : cp < np))) { np = cp; continue_looking = true; } @@ -958,7 +1002,7 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { (unsigned long long) le64toh(o->entry.xor_hash)) < 0) return -ENOMEM; - return 1; + return 0; } _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { @@ -1207,15 +1251,15 @@ static void check_network(sd_journal *j, int fd) { return; j->on_network = - (long)sfs.f_type == (long)CIFS_MAGIC_NUMBER || - sfs.f_type == CODA_SUPER_MAGIC || - sfs.f_type == NCP_SUPER_MAGIC || - sfs.f_type == NFS_SUPER_MAGIC || - sfs.f_type == SMB_SUPER_MAGIC; + F_TYPE_CMP(sfs.f_type, CIFS_MAGIC_NUMBER) || + F_TYPE_CMP(sfs.f_type, CODA_SUPER_MAGIC) || + F_TYPE_CMP(sfs.f_type, NCP_SUPER_MAGIC) || + F_TYPE_CMP(sfs.f_type, NFS_SUPER_MAGIC) || + F_TYPE_CMP(sfs.f_type, SMB_SUPER_MAGIC); } static int add_file(sd_journal *j, const char *prefix, const char *filename) { - char *path; + _cleanup_free_ char *path = NULL; int r; JournalFile *f; @@ -1234,20 +1278,15 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { if (!path) return -ENOMEM; - if (hashmap_get(j->files, path)) { - free(path); + if (hashmap_get(j->files, path)) return 0; - } if (hashmap_size(j->files) >= JOURNAL_FILES_MAX) { log_debug("Too many open journal files, not adding %s, ignoring.", path); - free(path); - return 0; + return set_put_error(j, -ETOOMANYREFS); } r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f); - free(path); - if (r < 0) { if (errno == ENOENT) return 0; @@ -1263,12 +1302,12 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { return r; } + log_debug("File %s got added.", f->path); + check_network(j, f->fd); j->current_invalidate_counter ++; - log_debug("File %s got added.", f->path); - return 0; } @@ -1311,9 +1350,9 @@ static int remove_file(sd_journal *j, const char *prefix, const char *filename) } static int add_directory(sd_journal *j, const char *prefix, const char *dirname) { - char *path; + _cleanup_free_ char *path = NULL; int r; - DIR *d; + _cleanup_closedir_ DIR *d = NULL; sd_id128_t id, mid; Directory *m; @@ -1321,10 +1360,12 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) assert(prefix); assert(dirname); + log_debug("Considering %s/%s.", prefix, dirname); + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && (sd_id128_from_string(dirname, &id) < 0 || sd_id128_get_machine(&mid) < 0 || - !sd_id128_equal(id, mid))) + !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run")))) return 0; path = strjoin(prefix, "/", dirname, NULL); @@ -1334,8 +1375,6 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) d = opendir(path); if (!d) { log_debug("Failed to open %s: %m", path); - free(path); - if (errno == ENOENT) return 0; return -errno; @@ -1344,32 +1383,24 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) m = hashmap_get(j->directories_by_path, path); if (!m) { m = new0(Directory, 1); - if (!m) { - closedir(d); - free(path); + if (!m) return -ENOMEM; - } m->is_root = false; m->path = path; if (hashmap_put(j->directories_by_path, m->path, m) < 0) { - closedir(d); - free(m->path); free(m); return -ENOMEM; } + path = NULL; /* avoid freeing in cleanup */ j->current_invalidate_counter ++; log_debug("Directory %s got added.", m->path); - } else if (m->is_root) { - free (path); - closedir(d); + } else if (m->is_root) return 0; - } else - free(path); if (m->wd <= 0 && j->inotify_fd >= 0) { @@ -1393,20 +1424,23 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) if (dirent_is_file_with_suffix(de, ".journal") || dirent_is_file_with_suffix(de, ".journal~")) { r = add_file(j, m->path, de->d_name); - if (r < 0) - log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r)); + if (r < 0) { + log_debug("Failed to add file %s/%s: %s", + m->path, de->d_name, strerror(-r)); + r = set_put_error(j, r); + if (r < 0) + return r; + } } } check_network(j, dirfd(d)); - closedir(d); - return 0; } static int add_root_directory(sd_journal *j, const char *p) { - DIR *d; + _cleanup_closedir_ DIR *d = NULL; Directory *m; int r; @@ -1424,21 +1458,17 @@ static int add_root_directory(sd_journal *j, const char *p) { m = hashmap_get(j->directories_by_path, p); if (!m) { m = new0(Directory, 1); - if (!m) { - closedir(d); + if (!m) return -ENOMEM; - } m->is_root = true; m->path = strdup(p); if (!m->path) { - closedir(d); free(m); return -ENOMEM; } if (hashmap_put(j->directories_by_path, m->path, m) < 0) { - closedir(d); free(m->path); free(m); return -ENOMEM; @@ -1448,10 +1478,8 @@ static int add_root_directory(sd_journal *j, const char *p) { log_debug("Root directory %s got added.", m->path); - } else if (!m->is_root) { - closedir(d); + } else if (!m->is_root) return 0; - } if (m->wd <= 0 && j->inotify_fd >= 0) { @@ -1475,9 +1503,13 @@ static int add_root_directory(sd_journal *j, const char *p) { if (dirent_is_file_with_suffix(de, ".journal") || dirent_is_file_with_suffix(de, ".journal~")) { r = add_file(j, m->path, de->d_name); - if (r < 0) - log_debug("Failed to add file %s/%s: %s", m->path, de->d_name, strerror(-r)); - + if (r < 0) { + log_debug("Failed to add file %s/%s: %s", + m->path, de->d_name, strerror(-r)); + r = set_put_error(j, r); + if (r < 0) + return r; + } } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) && sd_id128_from_string(de->d_name, &id) >= 0) { @@ -1489,8 +1521,6 @@ static int add_root_directory(sd_journal *j, const char *p) { check_network(j, dirfd(d)); - closedir(d); - return 0; } @@ -1518,7 +1548,7 @@ static int remove_directory(sd_journal *j, Directory *d) { } static int add_search_paths(sd_journal *j) { - + int r; const char search_paths[] = "/run/log/journal\0" "/var/log/journal\0"; @@ -1529,8 +1559,14 @@ static int add_search_paths(sd_journal *j) { /* We ignore most errors here, since the idea is to only open * what's actually accessible, and ignore the rest. */ - NULSTR_FOREACH(p, search_paths) - add_root_directory(j, p); + NULSTR_FOREACH(p, search_paths) { + r = add_root_directory(j, p); + if (r < 0 && r != -ENOENT) { + r = set_put_error(j, r); + if (r < 0) + return r; + } + } return 0; } @@ -1566,37 +1602,21 @@ static sd_journal *journal_new(int flags, const char *path) { if (path) { j->path = strdup(path); - if (!j->path) { - free(j); - return NULL; - } + if (!j->path) + goto fail; } j->files = hashmap_new(string_hash_func, string_compare_func); - if (!j->files) { - free(j->path); - free(j); - return NULL; - } - j->directories_by_path = hashmap_new(string_hash_func, string_compare_func); - if (!j->directories_by_path) { - hashmap_free(j->files); - free(j->path); - free(j); - return NULL; - } - j->mmap = mmap_cache_new(); - if (!j->mmap) { - hashmap_free(j->files); - hashmap_free(j->directories_by_path); - free(j->path); - free(j); - return NULL; - } + if (!j->files || !j->directories_by_path || !j->mmap) + goto fail; return j; + +fail: + sd_journal_close(j); + return NULL; } _public_ int sd_journal_open(sd_journal **ret, int flags) { @@ -1635,7 +1655,7 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f if (!ret) return -EINVAL; - if (!path || !path_is_absolute(path)) + if (!path) return -EINVAL; if (flags != 0) @@ -1646,8 +1666,10 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f return -ENOMEM; r = add_root_directory(j, path); - if (r < 0) + if (r < 0) { + set_put_error(j, r); goto fail; + } *ret = j; return 0; @@ -1665,6 +1687,8 @@ _public_ void sd_journal_close(sd_journal *j) { if (!j) return; + sd_journal_flush_matches(j); + while ((f = hashmap_steal_first(j->files))) journal_file_close(f); @@ -1682,13 +1706,12 @@ _public_ void sd_journal_close(sd_journal *j) { if (j->inotify_fd >= 0) close_nointr_nofail(j->inotify_fd); - sd_journal_flush_matches(j); - if (j->mmap) mmap_cache_unref(j->mmap); free(j->path); free(j->unique_field); + set_free(j->errors); free(j); } @@ -1866,7 +1889,7 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** *data = o->data.payload; *size = t; - return 1; + return 0; } r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); @@ -1987,6 +2010,43 @@ _public_ int sd_journal_get_fd(sd_journal *j) { return j->inotify_fd; } +_public_ int sd_journal_get_events(sd_journal *j) { + int fd; + + if (!j) + return -EINVAL; + + fd = sd_journal_get_fd(j); + if (fd < 0) + return fd; + + return POLLIN; +} + +_public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) { + int fd; + + if (!j) + return -EINVAL; + if (!timeout_usec) + return -EINVAL; + + fd = sd_journal_get_fd(j); + if (fd < 0) + return fd; + + if (!j->on_network) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + /* If we are on the network we need to regularly check for + * changes manually */ + + *timeout_usec = j->last_process_usec + JOURNAL_FILES_RECHECK_USEC; + return 1; +} + static void process_inotify_event(sd_journal *j, struct inotify_event *e) { Directory *d; int r; @@ -2007,8 +2067,11 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { r = add_file(j, d->path, e->name); - if (r < 0) - log_debug("Failed to add file %s/%s: %s", d->path, e->name, strerror(-r)); + if (r < 0) { + log_debug("Failed to add file %s/%s: %s", + d->path, e->name, strerror(-r)); + set_put_error(j, r); + } } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) { @@ -2066,6 +2129,8 @@ _public_ int sd_journal_process(sd_journal *j) { if (!j) return -EINVAL; + j->last_process_usec = now(CLOCK_MONOTONIC); + for (;;) { struct inotify_event *e; ssize_t l; @@ -2099,6 +2164,7 @@ _public_ int sd_journal_process(sd_journal *j) { _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { int r; + uint64_t t; assert(j); @@ -2117,12 +2183,18 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) { return determine_change(j); } - if (j->on_network) { - /* If we are on the network we need to regularly check - * for changes manually */ + r = sd_journal_get_timeout(j, &t); + if (r < 0) + return r; + + if (t != (uint64_t) -1) { + usec_t n; + + n = now(CLOCK_MONOTONIC); + t = t > n ? t - n : 0; - if (timeout_usec == (uint64_t) -1 || timeout_usec > JOURNAL_FILES_RECHECK_USEC) - timeout_usec = JOURNAL_FILES_RECHECK_USEC; + if (timeout_usec == (uint64_t) -1 || timeout_usec > t) + timeout_usec = t; } do { @@ -2442,7 +2514,7 @@ _public_ int sd_journal_get_catalog(sd_journal *j, char **ret) { if (r < 0) return r; - r = catalog_get(id, &text); + r = catalog_get(CATALOG_DATABASE, id, &text); if (r < 0) return r; @@ -2458,7 +2530,7 @@ _public_ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **ret) { if (!ret) return -EINVAL; - return catalog_get(id, ret); + return catalog_get(CATALOG_DATABASE, id, ret); } _public_ int sd_journal_set_data_threshold(sd_journal *j, size_t sz) { diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c index cec8a11c43..987867f0c8 100644 --- a/src/journal/test-catalog.c +++ b/src/journal/test-catalog.c @@ -4,6 +4,7 @@ This file is part of systemd. Copyright 2012 Lennart Poettering + Copyright 2013 Zbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -20,29 +21,114 @@ ***/ #include <locale.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> #include "util.h" #include "log.h" -#include "catalog.h" +#include "macro.h" #include "sd-messages.h" +#include "catalog.h" -int main(int argc, char *argv[]) { +static void test_import(Hashmap *h, struct strbuf *sb, + const char* contents, ssize_t size, int code) { + int r; + char name[] = "/tmp/test-catalog.XXXXXX"; + _cleanup_close_ int fd = mkstemp(name); + assert(fd >= 0); + assert_se(write(fd, contents, size) == size); + + r = catalog_import_file(h, sb, name); + assert(r == code); + + unlink(name); +} + +static void test_catalog_importing(void) { + Hashmap *h; + struct strbuf *sb; + + assert_se(h = hashmap_new(catalog_hash_func, catalog_compare_func)); + assert_se(sb = strbuf_new()); + +#define BUF "xxx" + test_import(h, sb, BUF, sizeof(BUF), -EINVAL); +#undef BUF + assert(hashmap_isempty(h)); + log_debug("----------------------------------------"); + +#define BUF \ +"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededede\n" \ +"Subject: message\n" \ +"\n" \ +"payload\n" + test_import(h, sb, BUF, sizeof(BUF), -EINVAL); +#undef BUF + + log_debug("----------------------------------------"); + +#define BUF \ +"-- 0027229ca0644181a76c4e92458afaff dededededededededededededededed\n" \ +"Subject: message\n" \ +"\n" \ +"payload\n" + test_import(h, sb, BUF, sizeof(BUF), 0); +#undef BUF + + assert(hashmap_size(h) == 1); + log_debug("----------------------------------------"); + + hashmap_free_free(h); + strbuf_cleanup(sb); +} + + +static const char* database = NULL; + +static void test_catalog_update(void) { + int r; + + static char name[] = "/tmp/test-catalog.XXXXXX"; + r = mkstemp(name); + assert(r >= 0); + + database = name; + + /* Test what happens if there are no files. */ + r = catalog_update(database, NULL, NULL); + assert(r >= 0); + + /* Note: this might actually not find anything, if systemd was + * not installed before. That should be fine too. */ + r = catalog_update(database, NULL, catalog_file_dirs); + assert(r >= 0); +} + +int main(int argc, char *argv[]) { _cleanup_free_ char *text = NULL; + int r; setlocale(LC_ALL, "de_DE.UTF-8"); log_set_max_level(LOG_DEBUG); - assert_se(catalog_update() >= 0); + test_catalog_importing(); + + test_catalog_update(); - assert_se(catalog_list(stdout) >= 0); + r = catalog_list(stdout, database, true); + assert_se(r >= 0); - assert_se(catalog_get(SD_MESSAGE_COREDUMP, &text) >= 0); + r = catalog_list(stdout, database, false); + assert_se(r >= 0); + assert_se(catalog_get(database, SD_MESSAGE_COREDUMP, &text) >= 0); printf(">>>%s<<<\n", text); - fflush(stdout); + if (database) + unlink(database); return 0; } diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c index 8a843ecdda..980244e016 100644 --- a/src/journal/test-journal-enum.c +++ b/src/journal/test-journal-enum.c @@ -23,10 +23,13 @@ #include "log.h" #include "sd-journal.h" +#include "macro.h" +#include "util.h" +#include "journal-internal.h" int main(int argc, char *argv[]) { unsigned n = 0; - sd_journal *j; + _cleanup_journal_close_ sd_journal*j = NULL; log_set_max_level(LOG_DEBUG); @@ -48,6 +51,5 @@ int main(int argc, char *argv[]) { break; } - sd_journal_close(j); return 0; } diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c index fa228144f5..37bffc1883 100644 --- a/src/journal/test-journal-match.c +++ b/src/journal/test-journal-match.c @@ -28,8 +28,8 @@ #include "log.h" int main(int argc, char *argv[]) { - sd_journal *j; - char *t; + _cleanup_journal_close_ sd_journal*j; + _cleanup_free_ char *t; log_set_max_level(LOG_DEBUG); @@ -54,14 +54,23 @@ int main(int argc, char *argv[]) { assert_se(sd_journal_add_match(j, "ONE=two", 0) >= 0); assert_se(sd_journal_add_match(j, "TWO=two", 0) >= 0); - assert_se(t = journal_make_match_string(j)); + assert_se(sd_journal_add_conjunction(j) >= 0); + + assert_se(sd_journal_add_match(j, "L4_1=yes", 0) >= 0); + assert_se(sd_journal_add_match(j, "L4_1=ok", 0) >= 0); + assert_se(sd_journal_add_match(j, "L4_2=yes", 0) >= 0); + assert_se(sd_journal_add_match(j, "L4_2=ok", 0) >= 0); + + assert_se(sd_journal_add_disjunction(j) >= 0); + + assert_se(sd_journal_add_match(j, "L3=yes", 0) >= 0); + assert_se(sd_journal_add_match(j, "L3=ok", 0) >= 0); - assert_se(streq(t, "((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO)))")); + assert_se(t = journal_make_match_string(j)); printf("resulting match expression is: %s\n", t); - free(t); - sd_journal_close(j); + assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO))))")); return 0; } diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c index b3e816db70..4aba7febc7 100644 --- a/src/journal/test-journal-stream.c +++ b/src/journal/test-journal-stream.c @@ -75,7 +75,7 @@ int main(int argc, char *argv[]) { JournalFile *one, *two, *three; char t[] = "/tmp/journal-stream-XXXXXX"; unsigned i; - sd_journal *j; + _cleanup_journal_close_ sd_journal*j = NULL; char *z; const void *data; size_t l; @@ -126,25 +126,23 @@ int main(int argc, char *argv[]) { assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); SD_JOURNAL_FOREACH_BACKWARDS(j) { - char *c; + _cleanup_free_ char *c; assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0); printf("\t%.*s\n", (int) l, (const char*) data); assert_se(sd_journal_get_cursor(j, &c) >= 0); assert_se(sd_journal_test_cursor(j, c) > 0); - free(c); } SD_JOURNAL_FOREACH(j) { - char *c; + _cleanup_free_ char *c; assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0); printf("\t%.*s\n", (int) l, (const char*) data); assert_se(sd_journal_get_cursor(j, &c) >= 0); assert_se(sd_journal_test_cursor(j, c) > 0); - free(c); } sd_journal_flush_matches(j); @@ -177,8 +175,6 @@ int main(int argc, char *argv[]) { SD_JOURNAL_FOREACH_UNIQUE(j, data, l) printf("%.*s\n", (int) l, (const char*) data); - sd_journal_close(j); - assert_se(rm_rf_dangerous(t, false, true, false) >= 0); return 0; diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c index 3ae8633f22..b9419372ad 100644 --- a/src/journal/test-journal-syslog.c +++ b/src/journal/test-journal-syslog.c @@ -25,14 +25,14 @@ static void test_syslog_parse_identifier(const char* str, const char *ident, const char*pid, int ret) { const char *buf = str; - char *ident2 = NULL, *pid2 = NULL; + _cleanup_free_ char *ident2 = NULL, *pid2 = NULL; int ret2; ret2 = syslog_parse_identifier(&buf, &ident2, &pid2); assert(ret == ret2); - assert(ident==ident2 || !strcmp(ident, ident2)); - assert(pid==pid2 || !strcmp(pid, pid2)); + assert(ident==ident2 || streq(ident, ident2)); + assert(pid==pid2 || streq(pid, pid2)); } int main(void) { diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index b6677215c0..ad2e2d4c3b 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) { log_info("=> Validated from %s to %s, %s missing", format_timestamp(a, sizeof(a), from), format_timestamp(b, sizeof(b), to), - format_timespan(c, sizeof(c), total > to ? total - to : 0)); + format_timespan(c, sizeof(c), total > to ? total - to : 0, 0)); } journal_file_close(f); |