From 87d2c1ff6a7375f03476767e6f59454bcc5cd04b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Oct 2011 21:06:39 +0200 Subject: journal: add preliminary incomplete implementation --- Makefile.am | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 5 deletions(-) (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index f4a17aa7f5..9bf92ad7ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -134,7 +134,8 @@ rootbin_PROGRAMS = \ systemd-ask-password \ systemd-tty-ask-password-agent \ systemd-tmpfiles \ - systemd-machine-id-setup + systemd-machine-id-setup \ + systemd-journalctl bin_PROGRAMS = \ systemd-cgls \ @@ -173,7 +174,8 @@ rootlibexec_PROGRAMS = \ systemd-detect-virt \ systemd-sysctl \ systemd-logind \ - systemd-uaccess + systemd-uaccess \ + systemd-journald if ENABLE_BINFMT rootlibexec_PROGRAMS += \ @@ -225,7 +227,9 @@ noinst_PROGRAMS = \ test-env-replace \ test-strv \ test-login \ - test-install + test-install \ + test-id128 \ + test-journal if HAVE_PAM pamlib_LTLIBRARIES = \ @@ -685,7 +689,8 @@ libsystemd_core_la_SOURCES = \ src/dbus-common.c \ src/sd-daemon.c \ src/install.c \ - src/cgroup-attr.c + src/cgroup-attr.c \ + src/sd-id128.c nodist_libsystemd_core_la_SOURCES = \ src/load-fragment-gperf.c \ @@ -947,6 +952,53 @@ test_install_CFLAGS = \ test_install_LDADD = \ libsystemd-basic.la +test_id128_SOURCES = \ + src/test-id128.c \ + src/sd-id128.c + +test_id128_CFLAGS = \ + $(AM_CFLAGS) + +test_id128_LDADD = \ + libsystemd-basic.la + +test_journal_SOURCES = \ + src/journal/test-journal.c \ + src/journal/sd-journal.c \ + src/journal/lookup3.c \ + src/sd-id128.c + +test_journal_CFLAGS = \ + $(AM_CFLAGS) + +test_journal_LDADD = \ + libsystemd-basic.la + +systemd_journald_SOURCES = \ + src/journal/journald.c \ + src/journal/sd-journal.c \ + src/journal/lookup3.c \ + src/sd-id128.c + +systemd_journald_CFLAGS = \ + $(AM_CFLAGS) + +systemd_journald_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la + +systemd_journalctl_SOURCES = \ + src/journal/journalctl.c \ + src/journal/sd-journal.c \ + src/journal/lookup3.c \ + src/sd-id128.c + +systemd_journalctl_CFLAGS = \ + $(AM_CFLAGS) + +systemd_journalctl_LDADD = \ + libsystemd-basic.la + systemd_stdout_syslog_bridge_SOURCES = \ src/stdout-syslog-bridge.c \ src/tcpwrap.c @@ -1142,7 +1194,8 @@ systemd_tmpfiles_LDADD = \ systemd_machine_id_setup_SOURCES = \ src/machine-id-setup.c \ - src/machine-id-main.c + src/machine-id-main.c \ + src/sd-id128.c systemd_machine_id_setup_CFLAGS = \ $(AM_CFLAGS) -- cgit v1.2.3-54-g00ecf From f4b4781191e8edfb5690e4447166e3ba7bcb48f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Oct 2011 23:03:07 +0200 Subject: journal: split user logs into their own journal files --- Makefile.am | 15 +++-- src/acl-util.c | 68 +++++++++++++++++++++ src/acl-util.h | 27 +++++++++ src/journal/journal-private.h | 27 ++++++++- src/journal/journald.c | 138 ++++++++++++++++++++++++++++++++++-------- src/journal/sd-journal.c | 27 --------- src/logind-acl.c | 45 +------------- src/util.c | 16 +++++ src/util.h | 1 + 9 files changed, 264 insertions(+), 100 deletions(-) create mode 100644 src/acl-util.c create mode 100644 src/acl-util.h (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index 9bf92ad7ac..d43da3c47b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -978,14 +978,17 @@ systemd_journald_SOURCES = \ src/journal/journald.c \ src/journal/sd-journal.c \ src/journal/lookup3.c \ - src/sd-id128.c + src/sd-id128.c \ + src/acl-util.c systemd_journald_CFLAGS = \ - $(AM_CFLAGS) + $(AM_CFLAGS) \ + $(ACL_CFLAGS) systemd_journald_LDADD = \ libsystemd-basic.la \ - libsystemd-daemon.la + libsystemd-daemon.la \ + $(ACL_LIBS) systemd_journalctl_SOURCES = \ src/journal/journalctl.c \ @@ -1143,10 +1146,12 @@ systemd_uaccess_SOURCES = \ if HAVE_ACL systemd_logind_SOURCES += \ - src/logind-acl.c + src/logind-acl.c \ + src/acl-util.c systemd_uaccess_SOURCES += \ - src/logind-acl.c + src/logind-acl.c \ + src/acl-util.c endif systemd_uaccess_CFLAGS = \ diff --git a/src/acl-util.c b/src/acl-util.c new file mode 100644 index 0000000000..a2a9f9a22b --- /dev/null +++ b/src/acl-util.c @@ -0,0 +1,68 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "acl-util.h" + +int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) { + acl_entry_t i; + int found; + + assert(acl); + assert(entry); + + for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + found > 0; + found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + + acl_tag_t tag; + uid_t *u; + bool b; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + u = acl_get_qualifier(i); + if (!u) + return -errno; + + b = *u == uid; + acl_free(u); + + if (b) { + *entry = i; + return 1; + } + } + + if (found < 0) + return -errno; + + return 0; +} diff --git a/src/acl-util.h b/src/acl-util.h new file mode 100644 index 0000000000..798ce43364 --- /dev/null +++ b/src/acl-util.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooaclutilhfoo +#define fooaclutilhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry); + +#endif diff --git a/src/journal/journal-private.h b/src/journal/journal-private.h index 914b73a40b..3277d29542 100644 --- a/src/journal/journal-private.h +++ b/src/journal/journal-private.h @@ -29,6 +29,31 @@ #include "util.h" #include "sd-id128.h" +typedef struct JournalFile { + int fd; + char *path; + struct stat last_stat; + int prot; + bool writable; + + Header *header; + + HashItem *hash_table; + void *hash_table_window; + uint64_t hash_table_window_size; + + uint64_t *bisect_table; + void *bisect_table_window; + uint64_t bisect_table_window_size; + + void *window; + uint64_t window_offset; + uint64_t window_size; + + Object *current; + uint64_t current_offset; +} JournalFile; + typedef struct JournalCoursor { sd_id128_t file_id; sd_id128_t boot_id; @@ -38,8 +63,6 @@ typedef struct JournalCoursor { uint64_t xor_hash; } JournalCoursor; -typedef struct JournalFile JournalFile; - int journal_file_open(const char *fname, int flags, mode_t mode, JournalFile **ret); void journal_file_close(JournalFile *j); diff --git a/src/journal/journald.c b/src/journal/journald.c index 818e146f94..e9ac3a832e 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -25,21 +25,109 @@ #include #include #include +#include +#include #include "hashmap.h" #include "journal-private.h" #include "sd-daemon.h" #include "socket-util.h" +#include "acl-util.h" typedef struct Server { int syslog_fd; int epoll_fd; int signal_fd; + JournalFile *runtime_journal; JournalFile *system_journal; Hashmap *user_journals; } Server; +static void fix_perms(JournalFile *f, uid_t uid) { + acl_t acl; + acl_entry_t entry; + acl_permset_t permset; + int r; + + assert(f); + + r = fchmod_and_fchown(f->fd, 0640, 0, 0); + if (r < 0) + log_warning("Failed to fix access mode/rights on %s, ignoring: %s", f->path, strerror(-r)); + + if (uid <= 0) + return; + + acl = acl_get_fd(f->fd); + if (!acl) { + log_warning("Failed to read ACL on %s, ignoring: %m", f->path); + return; + } + + r = acl_find_uid(acl, uid, &entry); + if (r <= 0) { + + if (acl_create_entry(&acl, &entry) < 0 || + acl_set_tag_type(entry, ACL_USER) < 0 || + acl_set_qualifier(entry, &uid) < 0) { + log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); + goto finish; + } + } + + if (acl_get_permset(entry, &permset) < 0 || + acl_add_perm(permset, ACL_READ) < 0 || + acl_calc_mask(&acl) < 0) { + log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); + goto finish; + } + + if (acl_set_fd(f->fd, acl) < 0) + log_warning("Failed to set ACL on %s, ignoring: %m", f->path); + +finish: + acl_free(acl); +} + +static JournalFile* find_journal(Server *s, uid_t uid) { + char *p; + int r; + JournalFile *f; + + assert(s); + + /* We split up user logs only on /var, not on /run */ + if (!s->system_journal) + return s->runtime_journal; + + if (uid <= 0) + return s->system_journal; + + f = hashmap_get(s->user_journals, UINT32_TO_PTR(uid)); + if (f) + return f; + + if (asprintf(&p, "/var/log/journal/%lu.journal", (unsigned long) uid) < 0) + return s->system_journal; + + r = journal_file_open(p, O_RDWR|O_CREAT, 0640, &f); + free(p); + + if (r < 0) + return s->system_journal; + + fix_perms(f, uid); + + r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f); + if (r < 0) { + journal_file_close(f); + return s->system_journal; + } + + return f; +} + static void process_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { char *message = NULL, *pid = NULL, *uid = NULL, *gid = NULL, *source_time = NULL, *boot_id = NULL, *machine_id = NULL, @@ -47,7 +135,6 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str *audit_session = NULL, *audit_loginuid = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *exe = NULL; - dual_timestamp ts; struct iovec iovec[15]; unsigned n = 0; char idbuf[33]; @@ -55,8 +142,8 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str int r; char *t; int priority = LOG_USER | LOG_INFO; - - dual_timestamp_get(&ts); + uid_t loginuid = 0; + JournalFile *f; parse_syslog_priority((char**) &buf, &priority); skip_syslog_date((char**) &buf); @@ -73,7 +160,6 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str if (ucred) { uint32_t session; - uid_t loginuid; if (asprintf(&pid, "PID=%lu", (unsigned long) ucred->pid) >= 0) IOVEC_SET_STRING(iovec[n++], pid); @@ -143,10 +229,15 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str free(t); } - r = journal_file_append_entry(s->system_journal, &ts, iovec, n, NULL, NULL); - if (r < 0) - log_error("Failed to write entry: %s", strerror(-r)); + f = find_journal(s, loginuid); + if (!f) + log_warning("Dropping message, as we can't find a place to store the data."); + else { + r = journal_file_append_entry(f, NULL, iovec, n, NULL, NULL); + if (r < 0) + log_error("Failed to write entry, ignoring: %s", strerror(-r)); + } free(message); free(pid); @@ -253,20 +344,6 @@ static int process_event(Server *s, struct epoll_event *ev) { return 1; } - -static int open_system_journal(JournalFile **f) { - int r; - - r = journal_file_open("/var/log/journal/system.journal", O_RDWR|O_CREAT, 0644, f); - if (r == -ENOENT) { - mkdir_p("/run/log/journal", 0755); - - r = journal_file_open("/run/log/journal/system.journal", O_RDWR|O_CREAT, 0644, f); - } - - return r; -} - static int server_init(Server *s) { int n, one, r; struct epoll_event ev; @@ -348,8 +425,18 @@ static int server_init(Server *s) { return -ENOMEM; } - r = open_system_journal(&s->system_journal); - if (r < 0) { + r = journal_file_open("/var/log/journal/system.journal", O_RDWR|O_CREAT, 0640, &s->system_journal); + if (r >= 0) + fix_perms(s->system_journal, 0); + else if (r == -ENOENT) { + mkdir_p("/run/log/journal", 0755); + + r = journal_file_open("/run/log/journal/system.journal", O_RDWR|O_CREAT, 0640, &s->runtime_journal); + if (r >= 0) + fix_perms(s->runtime_journal, 0); + } + + if (r < 0 && r != -ENOENT) { log_error("Failed to open journal: %s", strerror(-r)); return r; } @@ -383,6 +470,9 @@ static void server_done(Server *s) { if (s->system_journal) journal_file_close(s->system_journal); + if (s->runtime_journal) + journal_file_close(s->runtime_journal); + while ((f = hashmap_steal_first(s->user_journals))) journal_file_close(f); @@ -412,7 +502,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_AUTO); + log_set_target(LOG_TARGET_CONSOLE); log_parse_environment(); log_open(); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 8bca300f93..89bf545837 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -43,33 +43,6 @@ #define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL) -struct JournalFile { - int fd; - char *path; - struct stat last_stat; - int prot; - bool writable; - - Header *header; - - HashItem *hash_table; - void *hash_table_window; - uint64_t hash_table_window_size; - - uint64_t *bisect_table; - void *bisect_table_window; - uint64_t bisect_table_window_size; - - void *window; - uint64_t window_offset; - uint64_t window_size; - - Object *current; - uint64_t current_offset; - - LIST_FIELDS(JournalFile, files); -}; - struct sd_journal { Hashmap *files; }; diff --git a/src/logind-acl.c b/src/logind-acl.c index 7a06b501d4..eb8a48d191 100644 --- a/src/logind-acl.c +++ b/src/logind-acl.c @@ -27,46 +27,7 @@ #include "logind-acl.h" #include "util.h" - -static int find_acl(acl_t acl, uid_t uid, acl_entry_t *entry) { - acl_entry_t i; - int found; - - assert(acl); - assert(entry); - - for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); - found > 0; - found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { - - acl_tag_t tag; - uid_t *u; - bool b; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (tag != ACL_USER) - continue; - - u = acl_get_qualifier(i); - if (!u) - return -errno; - - b = *u == uid; - acl_free(u); - - if (b) { - *entry = i; - return 1; - } - } - - if (found < 0) - return -errno; - - return 0; -} +#include "acl-util.h" static int flush_acl(acl_t acl) { acl_entry_t i; @@ -125,7 +86,7 @@ int devnode_acl(const char *path, } else if (del && old_uid > 0) { acl_entry_t entry; - r = find_acl(acl, old_uid, &entry); + r = acl_find_uid(acl, old_uid, &entry); if (r < 0) goto finish; @@ -144,7 +105,7 @@ int devnode_acl(const char *path, acl_permset_t permset; int rd, wt; - r = find_acl(acl, new_uid, &entry); + r = acl_find_uid(acl, new_uid, &entry); if (r < 0) goto finish; diff --git a/src/util.c b/src/util.c index 99737e4e63..a3cfe864b6 100644 --- a/src/util.c +++ b/src/util.c @@ -3529,6 +3529,22 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { return 0; } +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { + assert(fd >= 0); + + /* Under the assumption that we are running privileged we + * first change the access mode and only then hand out + * ownership to avoid a window where access is too open. */ + + if (fchmod(fd, mode) < 0) + return -errno; + + if (fchown(fd, uid, gid) < 0) + return -errno; + + return 0; +} + cpu_set_t* cpu_set_malloc(unsigned *ncpus) { cpu_set_t *r; unsigned n = 1024; diff --git a/src/util.h b/src/util.h index 1a2dd5825d..89a7bec612 100644 --- a/src/util.h +++ b/src/util.h @@ -366,6 +366,7 @@ int get_ctty_devnr(pid_t pid, dev_t *d); int get_ctty(pid_t, dev_t *_devnr, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); -- cgit v1.2.3-54-g00ecf From cec736d21ff86c4ac81b4d306ddba2120333818c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 8 Oct 2011 02:20:44 +0200 Subject: journal: implement parallel traversal in client --- Makefile.am | 3 + src/journal/journal-def.h | 5 +- src/journal/journal-file.c | 1191 +++++++++++++++++++++++++++++++++++++++ src/journal/journal-file.h | 86 +++ src/journal/journal-private.h | 86 --- src/journal/journalctl.c | 11 +- src/journal/journald.c | 11 +- src/journal/sd-journal.c | 1241 +++++------------------------------------ src/journal/sd-journal.h | 27 +- src/journal/test-journal.c | 2 +- 10 files changed, 1434 insertions(+), 1229 deletions(-) create mode 100644 src/journal/journal-file.c create mode 100644 src/journal/journal-file.h delete mode 100644 src/journal/journal-private.h (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index d43da3c47b..892072318f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -965,6 +965,7 @@ test_id128_LDADD = \ test_journal_SOURCES = \ src/journal/test-journal.c \ src/journal/sd-journal.c \ + src/journal/journal-file.c \ src/journal/lookup3.c \ src/sd-id128.c @@ -977,6 +978,7 @@ test_journal_LDADD = \ systemd_journald_SOURCES = \ src/journal/journald.c \ src/journal/sd-journal.c \ + src/journal/journal-file.c \ src/journal/lookup3.c \ src/sd-id128.c \ src/acl-util.c @@ -993,6 +995,7 @@ systemd_journald_LDADD = \ systemd_journalctl_SOURCES = \ src/journal/journalctl.c \ src/journal/sd-journal.c \ + src/journal/journal-file.c \ src/journal/lookup3.c \ src/sd-id128.c diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index 2a519fe0db..b3fa1e524f 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -48,7 +48,7 @@ enum { _packed_ struct ObjectHeader { uint8_t type; - uint8_t reserved[3]; + uint8_t reserved[7]; uint64_t size; uint8_t payload[]; }; @@ -74,6 +74,7 @@ _packed_ struct EntryObject { uint64_t seqnum; uint64_t realtime; uint64_t monotonic; + sd_id128_t boot_id; uint64_t xor_hash; uint64_t prev_entry_offset; uint64_t next_entry_offset; @@ -118,6 +119,7 @@ _packed_ struct Header { sd_id128_t file_id; sd_id128_t machine_id; sd_id128_t boot_id; + sd_id128_t seqnum_id; uint64_t arena_offset; uint64_t arena_size; uint64_t arena_max_size; @@ -133,7 +135,6 @@ _packed_ struct Header { uint64_t tail_entry_offset; uint64_t last_bisect_offset; uint64_t n_objects; - uint64_t seqnum_base; uint64_t seqnum; }; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c new file mode 100644 index 0000000000..37e2e37eb1 --- /dev/null +++ b/src/journal/journal-file.c @@ -0,0 +1,1191 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "journal-def.h" +#include "journal-file.h" +#include "lookup3.h" + +#define DEFAULT_ARENA_MAX_SIZE (16ULL*1024ULL*1024ULL*1024ULL) +#define DEFAULT_ARENA_MIN_SIZE (256ULL*1024ULL) +#define DEFAULT_ARENA_KEEP_FREE (1ULL*1024ULL*1024ULL) + +#define DEFAULT_HASH_TABLE_SIZE (2047ULL*16ULL) +#define DEFAULT_BISECT_TABLE_SIZE ((DEFAULT_ARENA_MAX_SIZE/(64ULL*1024ULL))*8ULL) + +#define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL) + +static const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }; + +#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) + +void journal_file_close(JournalFile *f) { + assert(f); + + if (f->fd >= 0) + close_nointr_nofail(f->fd); + + if (f->header) + munmap(f->header, PAGE_ALIGN(sizeof(Header))); + + if (f->hash_table_window) + munmap(f->hash_table_window, f->hash_table_window_size); + + if (f->bisect_table_window) + munmap(f->bisect_table_window, f->bisect_table_window_size); + + if (f->window) + munmap(f->window, f->window_size); + + free(f->path); + free(f); +} + +static int journal_file_init_header(JournalFile *f) { + Header h; + ssize_t k; + int r; + + assert(f); + + zero(h); + memcpy(h.signature, signature, 8); + h.arena_offset = htole64(ALIGN64(sizeof(h))); + h.arena_max_size = htole64(DEFAULT_ARENA_MAX_SIZE); + h.arena_min_size = htole64(DEFAULT_ARENA_MIN_SIZE); + h.arena_keep_free = htole64(DEFAULT_ARENA_KEEP_FREE); + + r = sd_id128_randomize(&h.file_id); + if (r < 0) + return r; + + h.seqnum_id = h.file_id; + + k = pwrite(f->fd, &h, sizeof(h), 0); + if (k < 0) + return -errno; + + if (k != sizeof(h)) + return -EIO; + + return 0; +} + +static int journal_file_refresh_header(JournalFile *f) { + int r; + + assert(f); + + r = sd_id128_get_machine(&f->header->machine_id); + if (r < 0) + return r; + + r = sd_id128_get_boot(&f->header->boot_id); + if (r < 0) + return r; + + f->header->state = htole32(STATE_ONLINE); + return 0; +} + +static int journal_file_verify_header(JournalFile *f) { + assert(f); + + if (memcmp(f->header, signature, 8)) + return -EBADMSG; + + if (f->header->incompatible_flags != 0) + return -EPROTONOSUPPORT; + + if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->arena_offset) + le64toh(f->header->arena_size))) + return -ENODATA; + + if (f->writable) { + uint32_t state; + sd_id128_t machine_id; + int r; + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + if (!sd_id128_equal(machine_id, f->header->machine_id)) + return -EHOSTDOWN; + + state = le32toh(f->header->state); + + if (state == STATE_ONLINE) + log_debug("Journal file %s is already online. Assuming unclean closing. Ignoring.", f->path); + else if (state == STATE_ARCHIVED) + return -ESHUTDOWN; + else if (state != STATE_OFFLINE) + log_debug("Journal file %s has unknown state %u. Ignoring.", f->path, state); + } + + return 0; +} + +static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { + uint64_t asize; + uint64_t old_size, new_size; + + assert(f); + + if (offset < le64toh(f->header->arena_offset)) + return -EINVAL; + + new_size = PAGE_ALIGN(offset + size); + + /* We assume that this file is not sparse, and we know that + * for sure, since we alway call posix_fallocate() + * ourselves */ + + old_size = + le64toh(f->header->arena_offset) + + le64toh(f->header->arena_size); + + if (old_size >= new_size) + return 0; + + asize = new_size - le64toh(f->header->arena_offset); + + if (asize > le64toh(f->header->arena_min_size)) { + struct statvfs svfs; + + if (fstatvfs(f->fd, &svfs) >= 0) { + uint64_t available; + + available = svfs.f_bfree * svfs.f_bsize; + + if (available >= f->header->arena_keep_free) + available -= f->header->arena_keep_free; + else + available = 0; + + if (new_size - old_size > available) + return -E2BIG; + } + } + + if (asize > le64toh(f->header->arena_max_size)) + return -E2BIG; + + if (posix_fallocate(f->fd, 0, new_size) < 0) + return -errno; + + if (fstat(f->fd, &f->last_stat) < 0) + return -errno; + + f->header->arena_size = htole64(asize); + + return 0; +} + +static int journal_file_map( + JournalFile *f, + uint64_t offset, + uint64_t size, + void **_window, + uint64_t *_woffset, + uint64_t *_wsize, + void **ret) { + + uint64_t woffset, wsize; + void *window; + + assert(f); + assert(size > 0); + assert(ret); + + woffset = offset & ~((uint64_t) page_size() - 1ULL); + wsize = size + (offset - woffset); + wsize = PAGE_ALIGN(wsize); + + window = mmap(NULL, wsize, f->prot, MAP_SHARED, f->fd, woffset); + if (window == MAP_FAILED) + return -errno; + + if (_window) + *_window = window; + + if (_woffset) + *_woffset = woffset; + + if (_wsize) + *_wsize = wsize; + + *ret = (uint8_t*) window + (offset - woffset); + + return 0; +} + +static int journal_file_move_to(JournalFile *f, uint64_t offset, uint64_t size, void **ret) { + void *p; + uint64_t delta; + int r; + + assert(f); + assert(ret); + + if (_likely_(f->window && + f->window_offset <= offset && + f->window_offset+f->window_size >= offset + size)) { + + *ret = (uint8_t*) f->window + (offset - f->window_offset); + return 0; + } + + if (f->window) { + if (munmap(f->window, f->window_size) < 0) + return -errno; + + f->window = NULL; + f->window_size = f->window_offset = 0; + } + + if (size < DEFAULT_WINDOW_SIZE) { + /* If the default window size is larger then what was + * asked for extend the mapping a bit in the hope to + * minimize needed remappings later on. We add half + * the window space before and half behind the + * requested mapping */ + + delta = PAGE_ALIGN((DEFAULT_WINDOW_SIZE - size) / 2); + + if (offset < delta) + delta = offset; + + offset -= delta; + size += (DEFAULT_WINDOW_SIZE - delta); + } else + delta = 0; + + r = journal_file_map(f, + offset, size, + &f->window, &f->window_offset, &f->window_size, + & p); + + if (r < 0) + return r; + + *ret = (uint8_t*) p + delta; + return 0; +} + +static bool verify_hash(Object *o) { + uint64_t t; + + assert(o); + + t = le64toh(o->object.type); + if (t == OBJECT_DATA) { + uint64_t s, h1, h2; + + s = le64toh(o->object.size); + + h1 = le64toh(o->data.hash); + h2 = hash64(o->data.payload, s - offsetof(Object, data.payload)); + + return h1 == h2; + } + + return true; +} + +int journal_file_move_to_object(JournalFile *f, uint64_t offset, int type, Object **ret) { + int r; + void *t; + Object *o; + uint64_t s; + + assert(f); + assert(ret); + + r = journal_file_move_to(f, offset, sizeof(ObjectHeader), &t); + if (r < 0) + return r; + + o = (Object*) t; + s = le64toh(o->object.size); + + if (s < sizeof(ObjectHeader)) + return -EBADMSG; + + if (type >= 0 && le64toh(o->object.type) != type) + return -EBADMSG; + + if (s > sizeof(ObjectHeader)) { + r = journal_file_move_to(f, offset, s, &t); + if (r < 0) + return r; + + o = (Object*) t; + } + + if (!verify_hash(o)) + return -EBADMSG; + + *ret = o; + return 0; +} + +static uint64_t journal_file_seqnum(JournalFile *f) { + uint64_t r; + + assert(f); + + r = le64toh(f->header->seqnum) + 1; + f->header->seqnum = htole64(r); + + return r; +} + +static int journal_file_append_object(JournalFile *f, uint64_t size, Object **ret, uint64_t *offset) { + int r; + uint64_t p; + Object *tail, *o; + void *t; + + assert(f); + assert(size >= sizeof(ObjectHeader)); + assert(offset); + assert(ret); + + p = le64toh(f->header->tail_object_offset); + + if (p == 0) + p = le64toh(f->header->arena_offset); + else { + r = journal_file_move_to_object(f, p, -1, &tail); + if (r < 0) + return r; + + p += ALIGN64(le64toh(tail->object.size)); + } + + r = journal_file_allocate(f, p, size); + if (r < 0) + return r; + + r = journal_file_move_to(f, p, size, &t); + if (r < 0) + return r; + + o = (Object*) t; + + zero(o->object); + o->object.type = htole64(OBJECT_UNUSED); + zero(o->object.reserved); + o->object.size = htole64(size); + + f->header->tail_object_offset = htole64(p); + if (f->header->head_object_offset == 0) + f->header->head_object_offset = htole64(p); + + f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); + + *ret = o; + *offset = p; + + return 0; +} + +static int journal_file_setup_hash_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + + s = DEFAULT_HASH_TABLE_SIZE; + r = journal_file_append_object(f, offsetof(Object, hash_table.table) + s, &o, &p); + if (r < 0) + return r; + + o->object.type = htole64(OBJECT_HASH_TABLE); + memset(o->hash_table.table, 0, s); + + f->header->hash_table_offset = htole64(p + offsetof(Object, hash_table.table)); + f->header->hash_table_size = htole64(s); + + return 0; +} + +static int journal_file_setup_bisect_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + + s = DEFAULT_BISECT_TABLE_SIZE; + r = journal_file_append_object(f, offsetof(Object, bisect_table.table) + s, &o, &p); + if (r < 0) + return r; + + o->object.type = htole64(OBJECT_BISECT_TABLE); + memset(o->bisect_table.table, 0, s); + + f->header->bisect_table_offset = htole64(p + offsetof(Object, bisect_table.table)); + f->header->bisect_table_size = htole64(s); + + return 0; +} + +static int journal_file_map_hash_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + + p = le64toh(f->header->hash_table_offset); + s = le64toh(f->header->hash_table_size); + + r = journal_file_map(f, + p, s, + &f->hash_table_window, NULL, &f->hash_table_window_size, + &t); + if (r < 0) + return r; + + f->hash_table = t; + return 0; +} + +static int journal_file_map_bisect_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + + p = le64toh(f->header->bisect_table_offset); + s = le64toh(f->header->bisect_table_size); + + r = journal_file_map(f, + p, s, + &f->bisect_table_window, NULL, &f->bisect_table_window_size, + &t); + + if (r < 0) + return r; + + f->bisect_table = t; + return 0; +} + +static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash_index) { + uint64_t p; + int r; + + assert(f); + assert(o); + assert(offset > 0); + assert(o->object.type == htole64(OBJECT_DATA)); + + o->data.head_entry_offset = o->data.tail_entry_offset = 0; + o->data.next_hash_offset = 0; + + p = le64toh(f->hash_table[hash_index].tail_hash_offset); + if (p == 0) { + /* Only entry in the hash table is easy */ + + o->data.prev_hash_offset = 0; + f->hash_table[hash_index].head_hash_offset = htole64(offset); + } else { + o->data.prev_hash_offset = htole64(p); + + /* Temporarily move back to the previous data object, + * to patch in pointer */ + + r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + if (r < 0) + return r; + + o->data.next_hash_offset = offset; + + r = journal_file_move_to_object(f, offset, OBJECT_DATA, &o); + if (r < 0) + return r; + } + + f->hash_table[hash_index].tail_hash_offset = htole64(offset); + + return 0; +} + +static int journal_file_append_data(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { + uint64_t hash, h, p, np; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(data || size == 0); + + osize = offsetof(Object, data.payload) + size; + + hash = hash64(data, size); + h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); + p = le64toh(f->hash_table[h].head_hash_offset); + + while (p != 0) { + /* Look for this data object in the hash table */ + + r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + if (r < 0) + return r; + + if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (le64toh(o->data.hash) != hash) + return -EBADMSG; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; + } + + p = le64toh(o->data.next_hash_offset); + } + + r = journal_file_append_object(f, osize, &o, &np); + if (r < 0) + return r; + + o->object.type = htole64(OBJECT_DATA); + o->data.hash = htole64(hash); + memcpy(o->data.payload, data, size); + + r = journal_file_link_data(f, o, np, h); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 0; +} + +uint64_t journal_file_entry_n_items(Object *o) { + assert(o); + assert(o->object.type == htole64(OBJECT_ENTRY)); + + return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); +} + +static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { + uint64_t p, q; + int r; + assert(f); + assert(o); + assert(offset > 0); + + p = le64toh(o->entry.items[i].object_offset); + if (p == 0) + return -EINVAL; + + o->entry.items[i].next_entry_offset = 0; + + /* Move to the data object */ + r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + if (r < 0) + return r; + + q = le64toh(o->data.tail_entry_offset); + o->data.tail_entry_offset = htole64(offset); + + if (q == 0) + o->data.head_entry_offset = htole64(offset); + else { + uint64_t n, j; + + /* Move to previous entry */ + r = journal_file_move_to_object(f, q, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + n = journal_file_entry_n_items(o); + for (j = 0; j < n; j++) + if (le64toh(o->entry.items[j].object_offset) == p) + break; + + if (j >= n) + return -EBADMSG; + + o->entry.items[j].next_entry_offset = offset; + } + + /* Move back to original entry */ + r = journal_file_move_to_object(f, offset, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + o->entry.items[i].prev_entry_offset = q; + return 0; +} + +static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { + uint64_t p, i, n, k, a, b; + int r; + + assert(f); + assert(o); + assert(offset > 0); + assert(o->object.type == htole64(OBJECT_ENTRY)); + + /* Link up the entry itself */ + p = le64toh(f->header->tail_entry_offset); + + o->entry.prev_entry_offset = f->header->tail_entry_offset; + o->entry.next_entry_offset = 0; + + if (p == 0) + f->header->head_entry_offset = htole64(offset); + else { + /* Temporarily move back to the previous entry, to + * patch in pointer */ + + r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + o->entry.next_entry_offset = htole64(offset); + + r = journal_file_move_to_object(f, offset, OBJECT_ENTRY, &o); + if (r < 0) + return r; + } + + f->header->tail_entry_offset = htole64(offset); + + /* Link up the items */ + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + r = journal_file_link_entry_item(f, o, offset, i); + if (r < 0) + return r; + } + + /* Link up the entry in the bisect table */ + n = le64toh(f->header->bisect_table_size) / sizeof(uint64_t); + k = le64toh(f->header->arena_max_size) / n; + + a = (le64toh(f->header->last_bisect_offset) + k - 1) / k; + b = offset / k; + + for (; a <= b; a++) + f->bisect_table[a] = htole64(offset); + + f->header->last_bisect_offset = htole64(offset + le64toh(o->object.size)); + + return 0; +} + +static int journal_file_append_entry_internal( + JournalFile *f, + const dual_timestamp *ts, + uint64_t xor_hash, + const EntryItem items[], unsigned n_items, + Object **ret, uint64_t *offset) { + uint64_t np; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(items || n_items == 0); + + osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem)); + + r = journal_file_append_object(f, osize, &o, &np); + if (r < 0) + return r; + + o->object.type = htole64(OBJECT_ENTRY); + o->entry.seqnum = htole64(journal_file_seqnum(f)); + memcpy(o->entry.items, items, n_items * sizeof(EntryItem)); + o->entry.realtime = ts ? htole64(ts->realtime) : 0; + o->entry.monotonic = ts ? htole64(ts->monotonic) : 0; + o->entry.xor_hash = htole64(xor_hash); + o->entry.boot_id = f->header->boot_id; + + r = journal_file_link_entry(f, o, np); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 0; +} + +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, Object **ret, uint64_t *offset) { + unsigned i; + EntryItem *items; + int r; + uint64_t xor_hash = 0; + + assert(f); + assert(iovec || n_iovec == 0); + + items = new(EntryItem, n_iovec); + if (!items) + return -ENOMEM; + + for (i = 0; i < n_iovec; i++) { + uint64_t p; + Object *o; + + r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p); + if (r < 0) + goto finish; + + xor_hash ^= le64toh(o->data.hash); + items[i].object_offset = htole64(p); + } + + r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, ret, offset); + +finish: + free(items); + + return r; +} + +int journal_file_move_to_entry(JournalFile *f, uint64_t seqnum, Object **ret, uint64_t *offset) { + Object *o; + uint64_t lower, upper, p, n, k; + int r; + + assert(f); + + n = le64toh(f->header->bisect_table_size) / sizeof(uint64_t); + k = le64toh(f->header->arena_max_size) / n; + + lower = 0; + upper = le64toh(f->header->last_bisect_offset)/k+1; + + while (lower < upper) { + k = (upper + lower) / 2; + p = le64toh(f->bisect_table[k]); + + if (p == 0) { + upper = k; + continue; + } + + r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + if (o->entry.seqnum == seqnum) { + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } else if (seqnum < o->entry.seqnum) + upper = k; + else if (seqnum > o->entry.seqnum) + lower = k+1; + } + + assert(lower == upper); + + if (lower <= 0) + return 0; + + /* The object we are looking for is between + * bisect_table[lower-1] and bisect_table[lower] */ + + p = le64toh(f->bisect_table[lower-1]); + + for (;;) { + r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + if (o->entry.seqnum == seqnum) { + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + + } if (seqnum < o->entry.seqnum) + return 0; + + if (o->entry.next_entry_offset == 0) + return 0; + + p = le64toh(o->entry.next_entry_offset); + } + + return 0; +} + +int journal_file_next_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset) { + uint64_t np; + int r; + + assert(f); + + if (!o) + np = le64toh(f->header->head_entry_offset); + else { + if (le64toh(o->object.type) != OBJECT_ENTRY) + return -EINVAL; + + np = le64toh(o->entry.next_entry_offset); + } + + if (np == 0) + return 0; + + r = journal_file_move_to_object(f, np, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 1; +} + +int journal_file_prev_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset) { + uint64_t np; + int r; + + assert(f); + + if (!o) + np = le64toh(f->header->tail_entry_offset); + else { + if (le64toh(o->object.type) != OBJECT_ENTRY) + return -EINVAL; + + np = le64toh(o->entry.prev_entry_offset); + } + + if (np == 0) + return 0; + + r = journal_file_move_to_object(f, np, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 1; +} + +int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { + uint64_t p, osize, hash, h; + int r; + + assert(f); + assert(data || size == 0); + + osize = offsetof(Object, data.payload) + size; + + hash = hash64(data, size); + h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); + p = le64toh(f->hash_table[h].head_hash_offset); + + while (p != 0) { + Object *o; + + r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + if (r < 0) + return r; + + if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (le64toh(o->data.hash) != hash) + return -EBADMSG; + + if (o->data.head_entry_offset == 0) + return 0; + + p = le64toh(o->data.head_entry_offset); + r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } + + p = le64toh(o->data.next_hash_offset); + } + + return 0; +} + +int journal_file_find_last_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { + uint64_t p, osize, hash, h; + int r; + + assert(f); + assert(data || size == 0); + + osize = offsetof(Object, data.payload) + size; + + hash = hash64(data, size); + h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); + p = le64toh(f->hash_table[h].tail_hash_offset); + + while (p != 0) { + Object *o; + + r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); + if (r < 0) + return r; + + if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (le64toh(o->data.hash) != hash) + return -EBADMSG; + + if (o->data.tail_entry_offset == 0) + return 0; + + p = le64toh(o->data.tail_entry_offset); + r = journal_file_move_to_object(f, p, OBJECT_ENTRY, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } + + p = le64toh(o->data.prev_hash_offset); + } + + return 0; +} + +void journal_file_dump(JournalFile *f) { + char a[33], b[33], c[33]; + Object *o; + int r; + uint64_t p; + + assert(f); + + printf("File ID: %s\n" + "Machine ID: %s\n" + "Boot ID: %s\n" + "Arena size: %llu\n", + sd_id128_to_string(f->header->file_id, a), + sd_id128_to_string(f->header->machine_id, b), + sd_id128_to_string(f->header->boot_id, c), + (unsigned long long) le64toh(f->header->arena_size)); + + p = le64toh(f->header->head_object_offset); + while (p != 0) { + r = journal_file_move_to_object(f, p, -1, &o); + if (r < 0) + goto fail; + + switch (o->object.type) { + + case OBJECT_UNUSED: + printf("Type: OBJECT_UNUSED\n"); + break; + + case OBJECT_DATA: + printf("Type: OBJECT_DATA\n"); + break; + + case OBJECT_ENTRY: + printf("Type: OBJECT_ENTRY %llu\n", (unsigned long long) le64toh(o->entry.seqnum)); + break; + + case OBJECT_HASH_TABLE: + printf("Type: OBJECT_HASH_TABLE\n"); + break; + + case OBJECT_BISECT_TABLE: + printf("Type: OBJECT_BISECT_TABLE\n"); + break; + } + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + return; +fail: + log_error("File corrupt"); +} + +int journal_file_open( + const char *fname, + int flags, + mode_t mode, + JournalFile **ret) { + + JournalFile *f; + int r; + bool newly_created = false; + + assert(fname); + + if ((flags & O_ACCMODE) != O_RDONLY && + (flags & O_ACCMODE) != O_RDWR) + return -EINVAL; + + f = new0(JournalFile, 1); + if (!f) + return -ENOMEM; + + f->writable = (flags & O_ACCMODE) != O_RDONLY; + f->prot = prot_from_flags(flags); + + f->fd = open(fname, flags|O_CLOEXEC, mode); + if (f->fd < 0) { + r = -errno; + goto fail; + } + + f->path = strdup(fname); + if (!f->path) { + r = -ENOMEM; + goto fail; + } + + if (fstat(f->fd, &f->last_stat) < 0) { + r = -errno; + goto fail; + } + + if (f->last_stat.st_size == 0 && f->writable) { + newly_created = true; + + r = journal_file_init_header(f); + if (r < 0) + goto fail; + + if (fstat(f->fd, &f->last_stat) < 0) { + r = -errno; + goto fail; + } + } + + if (f->last_stat.st_size < (off_t) sizeof(Header)) { + r = -EIO; + goto fail; + } + + f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0); + if (f->header == MAP_FAILED) { + f->header = NULL; + r = -errno; + goto fail; + } + + if (!newly_created) { + r = journal_file_verify_header(f); + if (r < 0) + goto fail; + } + + if (f->writable) { + r = journal_file_refresh_header(f); + if (r < 0) + goto fail; + } + + if (newly_created) { + + r = journal_file_setup_hash_table(f); + if (r < 0) + goto fail; + + r = journal_file_setup_bisect_table(f); + if (r < 0) + goto fail; + } + + r = journal_file_map_hash_table(f); + if (r < 0) + goto fail; + + r = journal_file_map_bisect_table(f); + if (r < 0) + goto fail; + + if (ret) + *ret = f; + + return 0; + +fail: + journal_file_close(f); + + return r; +} diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h new file mode 100644 index 0000000000..55cc7153af --- /dev/null +++ b/src/journal/journal-file.h @@ -0,0 +1,86 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalfilehfoo +#define foojournalfilehfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "journal-def.h" +#include "util.h" +#include "sd-id128.h" + +typedef struct JournalFile { + int fd; + char *path; + struct stat last_stat; + int prot; + bool writable; + + Header *header; + + HashItem *hash_table; + void *hash_table_window; + uint64_t hash_table_window_size; + + uint64_t *bisect_table; + void *bisect_table_window; + uint64_t bisect_table_window_size; + + void *window; + uint64_t window_offset; + uint64_t window_size; + + uint64_t current_offset; +} JournalFile; + +typedef struct JournalCursor { + uint8_t version; + uint8_t reserved[7]; + uint64_t seqnum; + sd_id128_t seqnum_id; + sd_id128_t boot_id; + uint64_t monotonic; + uint64_t realtime; + uint64_t xor_hash; +} JournalCursor; + +int journal_file_open(const char *fname, int flags, mode_t mode, JournalFile **ret); + +void journal_file_close(JournalFile *j); + +int journal_file_move_to_object(JournalFile *f, uint64_t offset, int type, Object **ret); + +uint64_t journal_file_entry_n_items(Object *o); + +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry(JournalFile *f, uint64_t seqnum, Object **ret, uint64_t *offset); + +int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_find_last_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); + +int journal_file_next_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset); +int journal_file_prev_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset); + +void journal_file_dump(JournalFile *f); + +#endif diff --git a/src/journal/journal-private.h b/src/journal/journal-private.h deleted file mode 100644 index 3277d29542..0000000000 --- a/src/journal/journal-private.h +++ /dev/null @@ -1,86 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foojournalprivatehfoo -#define foojournalprivatehfoo - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with systemd; If not, see . -***/ - -#include - -#include "sd-journal.h" -#include "journal-def.h" -#include "util.h" -#include "sd-id128.h" - -typedef struct JournalFile { - int fd; - char *path; - struct stat last_stat; - int prot; - bool writable; - - Header *header; - - HashItem *hash_table; - void *hash_table_window; - uint64_t hash_table_window_size; - - uint64_t *bisect_table; - void *bisect_table_window; - uint64_t bisect_table_window_size; - - void *window; - uint64_t window_offset; - uint64_t window_size; - - Object *current; - uint64_t current_offset; -} JournalFile; - -typedef struct JournalCoursor { - sd_id128_t file_id; - sd_id128_t boot_id; - uint64_t seqnum; - uint64_t monotonic; - uint64_t realtime; - uint64_t xor_hash; -} JournalCoursor; - -int journal_file_open(const char *fname, int flags, mode_t mode, JournalFile **ret); - -void journal_file_close(JournalFile *j); - -int journal_file_move_to_object(JournalFile *f, uint64_t offset, Object **ret); - -uint64_t journal_file_entry_n_items(Object *o); - -int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, Object **ret, uint64_t *offset); - -int journal_file_move_to_entry(JournalFile *f, uint64_t seqnum, Object **ret, uint64_t *offset); - -int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); -int journal_file_find_last_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); - -int journal_file_next_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset); -int journal_file_prev_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset); - -void journal_file_dump(JournalFile *f); - -#endif diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 7bcd842f6d..5f17f45cac 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -23,7 +23,7 @@ #include #include -#include "journal-private.h" +#include "journal-file.h" int main(int argc, char *argv[]) { int r; @@ -62,21 +62,16 @@ int main(int argc, char *argv[]) { uint64_t p, l; p = le64toh(o->entry.items[i].object_offset); - r = journal_file_move_to_object(f, p, &o); + r = journal_file_move_to_object(f, p, OBJECT_DATA, &o); if (r < 0) { log_error("Failed to move to data: %s", strerror(-r)); goto finish; } - if (le64toh(o->object.type) != OBJECT_DATA) { - log_error("Invalid file"); - goto finish; - } - l = o->object.size - offsetof(Object, data.payload); printf("\t[%.*s]\n", (int) l, o->data.payload); - r = journal_file_move_to_object(f, offset, &o); + r = journal_file_move_to_object(f, offset, OBJECT_ENTRY, &o); if (r < 0) { log_error("Failed to move back to entry: %s", strerror(-r)); goto finish; diff --git a/src/journal/journald.c b/src/journal/journald.c index e9ac3a832e..d65451df58 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -29,7 +29,7 @@ #include #include "hashmap.h" -#include "journal-private.h" +#include "journal-file.h" #include "sd-daemon.h" #include "socket-util.h" #include "acl-util.h" @@ -282,7 +282,9 @@ static int process_event(Server *s, struct epoll_event *ev) { log_debug("Received SIG%s", signal_to_string(sfsi.ssi_signo)); return 0; - } else { + } + + if (ev->data.fd == s->syslog_fd) { for (;;) { char buf[LINE_MAX+1]; struct msghdr msghdr; @@ -339,9 +341,12 @@ static int process_event(Server *s, struct epoll_event *ev) { process_message(s, strstrip(buf), ucred, tv); } + + return 1; } - return 1; + log_error("Unknown event."); + return 0; } static int server_init(Server *s) { diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 89bf545837..8426b3bf9e 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -19,1206 +19,223 @@ along with systemd; If not, see . ***/ -#include #include -#include -#include -#include #include -#include #include "sd-journal.h" #include "journal-def.h" -#include "journal-private.h" -#include "lookup3.h" -#include "list.h" +#include "journal-file.h" #include "hashmap.h" +#include "list.h" -#define DEFAULT_ARENA_MAX_SIZE (16ULL*1024ULL*1024ULL*1024ULL) -#define DEFAULT_ARENA_MIN_SIZE (256ULL*1024ULL) -#define DEFAULT_ARENA_KEEP_FREE (1ULL*1024ULL*1024ULL) +typedef struct Match Match; -#define DEFAULT_HASH_TABLE_SIZE (2047ULL*16ULL) -#define DEFAULT_BISECT_TABLE_SIZE ((DEFAULT_ARENA_MAX_SIZE/(64ULL*1024ULL))*8ULL) +struct Match { + char *data; + size_t size; + uint64_t hash; -#define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL) + LIST_FIELDS(Match, matches); +}; struct sd_journal { Hashmap *files; -}; - -static const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }; - -#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) - -void journal_file_close(JournalFile *f) { - assert(f); - - if (f->fd >= 0) - close_nointr_nofail(f->fd); - - if (f->header) - munmap(f->header, PAGE_ALIGN(sizeof(Header))); - - if (f->hash_table_window) - munmap(f->hash_table_window, f->hash_table_window_size); - - if (f->bisect_table_window) - munmap(f->bisect_table_window, f->bisect_table_window_size); - - if (f->window) - munmap(f->window, f->window_size); - - free(f->path); - free(f); -} - -static int journal_file_init_header(JournalFile *f) { - Header h; - ssize_t k; - int r; - - assert(f); - - zero(h); - memcpy(h.signature, signature, 8); - h.arena_offset = htole64(ALIGN64(sizeof(h))); - h.arena_max_size = htole64(DEFAULT_ARENA_MAX_SIZE); - h.arena_min_size = htole64(DEFAULT_ARENA_MIN_SIZE); - h.arena_keep_free = htole64(DEFAULT_ARENA_KEEP_FREE); - - r = sd_id128_randomize(&h.file_id); - if (r < 0) - return r; - - k = pwrite(f->fd, &h, sizeof(h), 0); - if (k < 0) - return -errno; - - if (k != sizeof(h)) - return -EIO; - - return 0; -} - -static int journal_file_refresh_header(JournalFile *f) { - int r; - - assert(f); - - r = sd_id128_get_machine(&f->header->machine_id); - if (r < 0) - return r; - - r = sd_id128_get_boot(&f->header->boot_id); - if (r < 0) - return r; - - f->header->state = htole32(STATE_ONLINE); - return 0; -} - -static int journal_file_verify_header(JournalFile *f) { - assert(f); - - if (memcmp(f->header, signature, 8)) - return -EBADMSG; - - if (f->header->incompatible_flags != 0) - return -EPROTONOSUPPORT; - - if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->arena_offset) + le64toh(f->header->arena_size))) - return -ENODATA; - - if (f->writable) { - uint32_t state; - sd_id128_t machine_id; - int r; - - r = sd_id128_get_machine(&machine_id); - if (r < 0) - return r; - - if (!sd_id128_equal(machine_id, f->header->machine_id)) - return -EHOSTDOWN; - - state = le32toh(f->header->state); - - if (state == STATE_ONLINE) - log_debug("Journal file %s is already online. Assuming unclean closing. Ignoring.", f->path); - else if (state == STATE_ARCHIVED) - return -ESHUTDOWN; - else if (state != STATE_OFFLINE) - log_debug("Journal file %s has unknown state %u. Ignoring.", f->path, state); - } - - return 0; -} - -static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { - uint64_t asize; - uint64_t old_size, new_size; - - assert(f); - - if (offset < le64toh(f->header->arena_offset)) - return -EINVAL; - - new_size = PAGE_ALIGN(offset + size); - - /* We assume that this file is not sparse, and we know that - * for sure, since we alway call posix_fallocate() - * ourselves */ - - old_size = - le64toh(f->header->arena_offset) + - le64toh(f->header->arena_size); - - if (old_size >= new_size) - return 0; - - asize = new_size - le64toh(f->header->arena_offset); - if (asize > le64toh(f->header->arena_min_size)) { - struct statvfs svfs; - - if (fstatvfs(f->fd, &svfs) >= 0) { - uint64_t available; - - available = svfs.f_bfree * svfs.f_bsize; - - if (available >= f->header->arena_keep_free) - available -= f->header->arena_keep_free; - else - available = 0; - - if (new_size - old_size > available) - return -E2BIG; - } - } - - if (asize > le64toh(f->header->arena_max_size)) - return -E2BIG; - - if (posix_fallocate(f->fd, 0, new_size) < 0) - return -errno; - - if (fstat(f->fd, &f->last_stat) < 0) - return -errno; - - f->header->arena_size = htole64(asize); - - return 0; -} - -static int journal_file_map( - JournalFile *f, - uint64_t offset, - uint64_t size, - void **_window, - uint64_t *_woffset, - uint64_t *_wsize, - void **ret) { - - uint64_t woffset, wsize; - void *window; - - assert(f); - assert(size > 0); - assert(ret); - - woffset = offset & ~((uint64_t) page_size() - 1ULL); - wsize = size + (offset - woffset); - wsize = PAGE_ALIGN(wsize); - - window = mmap(NULL, wsize, f->prot, MAP_SHARED, f->fd, woffset); - if (window == MAP_FAILED) - return -errno; - - if (_window) - *_window = window; - - if (_woffset) - *_woffset = woffset; - - if (_wsize) - *_wsize = wsize; - - *ret = (uint8_t*) window + (offset - woffset); - - return 0; -} - -static int journal_file_move_to(JournalFile *f, uint64_t offset, uint64_t size, void **ret) { - void *p; - uint64_t delta; - int r; - - assert(f); - assert(ret); - - if (_likely_(f->window && - f->window_offset <= offset && - f->window_offset+f->window_size >= offset + size)) { - - *ret = (uint8_t*) f->window + (offset - f->window_offset); - return 0; - } - - if (f->window) { - if (munmap(f->window, f->window_size) < 0) - return -errno; - - f->window = NULL; - f->window_size = f->window_offset = 0; - } - - if (size < DEFAULT_WINDOW_SIZE) { - /* If the default window size is larger then what was - * asked for extend the mapping a bit in the hope to - * minimize needed remappings later on. We add half - * the window space before and half behind the - * requested mapping */ - - delta = PAGE_ALIGN((DEFAULT_WINDOW_SIZE - size) / 2); - - if (offset < delta) - delta = offset; - - offset -= delta; - size += (DEFAULT_WINDOW_SIZE - delta); - } else - delta = 0; - - r = journal_file_map(f, - offset, size, - &f->window, &f->window_offset, &f->window_size, - & p); - - if (r < 0) - return r; - - *ret = (uint8_t*) p + delta; - return 0; -} - -static bool verify_hash(Object *o) { - uint64_t t; - - assert(o); - - t = le64toh(o->object.type); - if (t == OBJECT_DATA) { - uint64_t s, h1, h2; - - s = le64toh(o->object.size); - - h1 = le64toh(o->data.hash); - h2 = hash64(o->data.payload, s - offsetof(Object, data.payload)); - - return h1 == h2; - } - - return true; -} - -int journal_file_move_to_object(JournalFile *f, uint64_t offset, Object **ret) { - int r; - void *t; - Object *o; - uint64_t s; - - assert(f); - assert(ret); + JournalFile *current_file; - r = journal_file_move_to(f, offset, sizeof(ObjectHeader), &t); - if (r < 0) - return r; + LIST_HEAD(Match, matches); +}; - o = (Object*) t; - s = le64toh(o->object.size); +int sd_journal_add_match(sd_journal *j, const char *field, const void *data, size_t size) { + Match *m; + char *e; - if (s < sizeof(ObjectHeader)) - return -EBADMSG; + assert(j); + assert(field); + assert(data || size == 0); - if (s > sizeof(ObjectHeader)) { - r = journal_file_move_to(f, offset, s, &t); - if (r < 0) - return r; + m = new0(Match, 1); + if (!m) + return -ENOMEM; - o = (Object*) t; + m->size = strlen(field) + 1 + size; + m->data = malloc(m->size); + if (!m->data) { + free(m); + return -ENOMEM; } - if (!verify_hash(o)) - return -EBADMSG; + e = stpcpy(m->data, field); + *(e++) = '='; + memcpy(e, data, size); - *ret = o; + LIST_PREPEND(Match, matches, j->matches, m); return 0; } -static uint64_t journal_file_seqnum(JournalFile *f) { - uint64_t r; - - assert(f); - - r = le64toh(f->header->seqnum) + 1; - f->header->seqnum = htole64(r); - - return r; -} - -static int journal_file_append_object(JournalFile *f, uint64_t size, Object **ret, uint64_t *offset) { - int r; - uint64_t p; - Object *tail, *o; - void *t; - - assert(f); - assert(size >= sizeof(ObjectHeader)); - assert(offset); - assert(ret); - - p = le64toh(f->header->tail_object_offset); +void sd_journal_flush_matches(sd_journal *j) { + assert(j); - if (p == 0) - p = le64toh(f->header->arena_offset); - else { - r = journal_file_move_to_object(f, p, &tail); - if (r < 0) - return r; + while (j->matches) { + Match *m = j->matches; - p += ALIGN64(le64toh(tail->object.size)); + LIST_REMOVE(Match, matches, j->matches, m); + free(m->data); + free(m); } - - r = journal_file_allocate(f, p, size); - if (r < 0) - return r; - - r = journal_file_move_to(f, p, size, &t); - if (r < 0) - return r; - - o = (Object*) t; - - zero(o->object); - o->object.type = htole64(OBJECT_UNUSED); - zero(o->object.reserved); - o->object.size = htole64(size); - - f->header->tail_object_offset = htole64(p); - if (f->header->head_object_offset == 0) - f->header->head_object_offset = htole64(p); - - f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); - - *ret = o; - *offset = p; - - return 0; -} - -static int journal_file_setup_hash_table(JournalFile *f) { - uint64_t s, p; - Object *o; - int r; - - assert(f); - - s = DEFAULT_HASH_TABLE_SIZE; - r = journal_file_append_object(f, offsetof(Object, hash_table.table) + s, &o, &p); - if (r < 0) - return r; - - o->object.type = htole64(OBJECT_HASH_TABLE); - memset(o->hash_table.table, 0, s); - - f->header->hash_table_offset = htole64(p + offsetof(Object, hash_table.table)); - f->header->hash_table_size = htole64(s); - - return 0; -} - -static int journal_file_setup_bisect_table(JournalFile *f) { - uint64_t s, p; - Object *o; - int r; - - assert(f); - - s = DEFAULT_BISECT_TABLE_SIZE; - r = journal_file_append_object(f, offsetof(Object, bisect_table.table) + s, &o, &p); - if (r < 0) - return r; - - o->object.type = htole64(OBJECT_BISECT_TABLE); - memset(o->bisect_table.table, 0, s); - - f->header->bisect_table_offset = htole64(p + offsetof(Object, bisect_table.table)); - f->header->bisect_table_size = htole64(s); - - return 0; -} - -static int journal_file_map_hash_table(JournalFile *f) { - uint64_t s, p; - void *t; - int r; - - assert(f); - - p = le64toh(f->header->hash_table_offset); - s = le64toh(f->header->hash_table_size); - - r = journal_file_map(f, - p, s, - &f->hash_table_window, NULL, &f->hash_table_window_size, - &t); - if (r < 0) - return r; - - f->hash_table = t; - return 0; } -static int journal_file_map_bisect_table(JournalFile *f) { - uint64_t s, p; - void *t; - int r; - - assert(f); - - p = le64toh(f->header->bisect_table_offset); - s = le64toh(f->header->bisect_table_size); +static int compare_order(JournalFile *af, Object *ao, uint64_t ap, + JournalFile *bf, Object *bo, uint64_t bp) { - r = journal_file_map(f, - p, s, - &f->bisect_table_window, NULL, &f->bisect_table_window_size, - &t); - - if (r < 0) - return r; + uint64_t a, b; - f->bisect_table = t; - return 0; -} + if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) { -static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash_index) { - uint64_t p; - int r; + /* If this is from the same seqnum source, compare + * seqnums */ + a = le64toh(ao->entry.seqnum); + b = le64toh(bo->entry.seqnum); - assert(f); - assert(o); - assert(offset > 0); - assert(o->object.type == htole64(OBJECT_DATA)); - o->data.head_entry_offset = o->data.tail_entry_offset = 0; - o->data.next_hash_offset = 0; + } else if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) { - p = le64toh(f->hash_table[hash_index].tail_hash_offset); - if (p == 0) { - /* Only entry in the hash table is easy */ + /* If the boot id matches compare monotonic time */ + a = le64toh(ao->entry.monotonic); + b = le64toh(bo->entry.monotonic); - o->data.prev_hash_offset = 0; - f->hash_table[hash_index].head_hash_offset = htole64(offset); } else { - o->data.prev_hash_offset = htole64(p); - /* Temporarily move back to the previous data object, - * to patch in pointer */ - - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - return r; - - o->data.next_hash_offset = offset; - - r = journal_file_move_to_object(f, offset, &o); - if (r < 0) - return r; + /* Otherwise compare UTC time */ + a = le64toh(ao->entry.realtime); + b = le64toh(ao->entry.realtime); } - f->hash_table[hash_index].tail_hash_offset = htole64(offset); - - return 0; + return + a < b ? -1 : + a > b ? +1 : 0; } -static int journal_file_append_data(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { - uint64_t hash, h, p, np; - uint64_t osize; - Object *o; +int sd_journal_next(sd_journal *j) { + JournalFile *f, *new_current = NULL; + Iterator i; int r; + uint64_t new_offset = 0; + Object *new_entry = NULL; - assert(f); - assert(data || size == 0); - - osize = offsetof(Object, data.payload) + size; + assert(j); - hash = hash64(data, size); - h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); - p = le64toh(f->hash_table[h].head_hash_offset); + HASHMAP_FOREACH(f, j->files, i) { + Object *o; + uint64_t p; - while (p != 0) { - /* Look for this data object in the hash table */ + if (f->current_offset > 0) { + r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o); + if (r < 0) + return r; + } else + o = NULL; - r = journal_file_move_to_object(f, p, &o); + r = journal_file_next_entry(f, o, &o, &p); if (r < 0) return r; + else if (r == 0) + continue; - if (le64toh(o->object.type) != OBJECT_DATA) - return -EBADMSG; - - if (le64toh(o->object.size) == osize && - memcmp(o->data.payload, data, size) == 0) { - - if (le64toh(o->data.hash) != hash) - return -EBADMSG; - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 0; + if (!new_current || compare_order(new_current, new_entry, new_offset, f, o, p) > 0) { + new_current = f; + new_entry = o; + new_offset = p; } - - p = le64toh(o->data.next_hash_offset); } - r = journal_file_append_object(f, osize, &o, &np); - if (r < 0) - return r; - - o->object.type = htole64(OBJECT_DATA); - o->data.hash = htole64(hash); - memcpy(o->data.payload, data, size); - - r = journal_file_link_data(f, o, np, h); - if (r < 0) - return r; - - if (ret) - *ret = o; - - if (offset) - *offset = np; - - return 0; -} - -uint64_t journal_file_entry_n_items(Object *o) { - assert(o); - assert(o->object.type == htole64(OBJECT_ENTRY)); - - return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); -} - -static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { - uint64_t p, q; - int r; - assert(f); - assert(o); - assert(offset > 0); - - p = le64toh(o->entry.items[i].object_offset); - if (p == 0) - return -EINVAL; - - o->entry.items[i].next_entry_offset = 0; - - /* Move to the data object */ - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - return r; - - if (o->object.type != htole64(OBJECT_DATA)) - return -EBADMSG; - - q = le64toh(o->data.tail_entry_offset); - o->data.tail_entry_offset = htole64(offset); - - if (q == 0) - o->data.head_entry_offset = htole64(offset); - else { - uint64_t n, j; - - /* Move to previous entry */ - r = journal_file_move_to_object(f, q, &o); - if (r < 0) - return r; - - if (o->object.type != htole64(OBJECT_ENTRY)) - return -EBADMSG; - - n = journal_file_entry_n_items(o); - for (j = 0; j < n; j++) - if (le64toh(o->entry.items[j].object_offset) == p) - break; - - if (j >= n) - return -EBADMSG; - - o->entry.items[j].next_entry_offset = offset; - } - - /* Move back to original entry */ - r = journal_file_move_to_object(f, offset, &o); - if (r < 0) - return r; - - o->entry.items[i].prev_entry_offset = q; - return 0; -} - -static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { - uint64_t p, i, n, k, a, b; - int r; - - assert(f); - assert(o); - assert(offset > 0); - assert(o->object.type == htole64(OBJECT_ENTRY)); - - /* Link up the entry itself */ - p = le64toh(f->header->tail_entry_offset); - - o->entry.prev_entry_offset = f->header->tail_entry_offset; - o->entry.next_entry_offset = 0; - - if (p == 0) - f->header->head_entry_offset = htole64(offset); - else { - /* Temporarily move back to the previous entry, to - * patch in pointer */ - - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - return r; - - o->entry.next_entry_offset = htole64(offset); - - r = journal_file_move_to_object(f, offset, &o); - if (r < 0) - return r; - } - - f->header->tail_entry_offset = htole64(offset); - - /* Link up the items */ - n = journal_file_entry_n_items(o); - for (i = 0; i < n; i++) { - r = journal_file_link_entry_item(f, o, offset, i); - if (r < 0) - return r; + if (new_current) { + j->current_file = new_current; + f->current_offset = new_offset; + return 1; } - /* Link up the entry in the bisect table */ - n = le64toh(f->header->bisect_table_size) / sizeof(uint64_t); - k = le64toh(f->header->arena_max_size) / n; - - a = (le64toh(f->header->last_bisect_offset) + k - 1) / k; - b = offset / k; - - for (; a <= b; a++) - f->bisect_table[a] = htole64(offset); - - f->header->last_bisect_offset = htole64(offset + le64toh(o->object.size)); - return 0; } -static int journal_file_append_entry_internal( - JournalFile *f, - const dual_timestamp *ts, - uint64_t xor_hash, - const EntryItem items[], unsigned n_items, - Object **ret, uint64_t *offset) { - uint64_t np; - uint64_t osize; - Object *o; +int sd_journal_previous(sd_journal *j) { + JournalFile *f, *new_current = NULL; + Iterator i; int r; + uint64_t new_offset = 0; + Object *new_entry = NULL; - assert(f); - assert(items || n_items == 0); - - osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem)); - - r = journal_file_append_object(f, osize, &o, &np); - if (r < 0) - return r; - - o->object.type = htole64(OBJECT_ENTRY); - o->entry.seqnum = htole64(journal_file_seqnum(f)); - memcpy(o->entry.items, items, n_items * sizeof(EntryItem)); - o->entry.realtime = ts ? htole64(ts->realtime) : 0; - o->entry.monotonic = ts ? htole64(ts->monotonic) : 0; - o->entry.xor_hash = htole64(xor_hash); - - r = journal_file_link_entry(f, o, np); - if (r < 0) - return r; - - if (ret) - *ret = o; - - if (offset) - *offset = np; - - return 0; -} - -int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, Object **ret, uint64_t *offset) { - unsigned i; - EntryItem *items; - int r; - uint64_t xor_hash = 0; - - assert(f); - assert(iovec || n_iovec == 0); - - items = new(EntryItem, n_iovec); - if (!items) - return -ENOMEM; + assert(j); - for (i = 0; i < n_iovec; i++) { - uint64_t p; + HASHMAP_FOREACH(f, j->files, i) { Object *o; + uint64_t p; - r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p); - if (r < 0) - goto finish; - - xor_hash ^= le64toh(o->data.hash); - items[i].object_offset = htole64(p); - } - - r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, ret, offset); - -finish: - free(items); - - return r; -} - -int journal_file_move_to_entry(JournalFile *f, uint64_t seqnum, Object **ret, uint64_t *offset) { - Object *o; - uint64_t lower, upper, p, n, k; - int r; - - assert(f); - - n = le64toh(f->header->bisect_table_size) / sizeof(uint64_t); - k = le64toh(f->header->arena_max_size) / n; - - lower = 0; - upper = le64toh(f->header->last_bisect_offset)/k+1; - - while (lower < upper) { - k = (upper + lower) / 2; - p = le64toh(f->bisect_table[k]); - - if (p == 0) { - upper = k; - continue; - } + if (f->current_offset > 0) { + r = journal_file_move_to_object(f, f->current_offset, OBJECT_ENTRY, &o); + if (r < 0) + return r; + } else + o = NULL; - r = journal_file_move_to_object(f, p, &o); + r = journal_file_prev_entry(f, o, &o, &p); if (r < 0) return r; + else if (r == 0) + continue; - if (o->object.type != htole64(OBJECT_ENTRY)) - return -EBADMSG; - - if (o->entry.seqnum == seqnum) { - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; - } else if (seqnum < o->entry.seqnum) - upper = k; - else if (seqnum > o->entry.seqnum) - lower = k+1; + if (!new_current || compare_order(new_current, new_entry, new_offset, f, o, p) > 0) { + new_current = f; + new_entry = o; + new_offset = p; + } } - assert(lower == upper); - - if (lower <= 0) - return 0; - - /* The object we are looking for is between - * bisect_table[lower-1] and bisect_table[lower] */ - - p = le64toh(f->bisect_table[lower-1]); - - for (;;) { - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - return r; - - if (o->entry.seqnum == seqnum) { - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; - - } if (seqnum < o->entry.seqnum) - return 0; - - if (o->entry.next_entry_offset == 0) - return 0; - - p = le64toh(o->entry.next_entry_offset); + if (new_current) { + j->current_file = new_current; + f->current_offset = new_offset; + return 1; } return 0; } -int journal_file_next_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset) { - uint64_t np; - int r; - - assert(f); - - if (!o) - np = le64toh(f->header->head_entry_offset); - else { - if (le64toh(o->object.type) != OBJECT_ENTRY) - return -EINVAL; - - np = le64toh(o->entry.next_entry_offset); - } - - if (np == 0) - return 0; - - r = journal_file_move_to_object(f, np, &o); - if (r < 0) - return r; - - if (le64toh(o->object.type) != OBJECT_ENTRY) - return -EBADMSG; - - if (ret) - *ret = o; - - if (offset) - *offset = np; - - return 1; -} - -int journal_file_prev_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset) { - uint64_t np; +int sd_journal_get_cursor(sd_journal *j, void **cursor, size_t *size) { + JournalCursor *c; + Object *o; int r; - assert(f); - - if (!o) - np = le64toh(f->header->tail_entry_offset); - else { - if (le64toh(o->object.type) != OBJECT_ENTRY) - return -EINVAL; - - np = le64toh(o->entry.prev_entry_offset); - } + assert(j); + assert(cursor); + assert(size); - if (np == 0) + if (!j->current_file || !j->current_file->current_offset <= 0) return 0; - r = journal_file_move_to_object(f, np, &o); + r = journal_file_move_to_object(j->current_file, j->current_file->current_offset, OBJECT_ENTRY, &o); if (r < 0) return r; - if (le64toh(o->object.type) != OBJECT_ENTRY) - return -EBADMSG; + c = new0(JournalCursor, 1); + if (!c) + return -ENOMEM; - if (ret) - *ret = o; + c->version = 1; + c->seqnum = o->entry.seqnum; + c->seqnum_id = j->current_file->header->seqnum_id; + c->boot_id = o->entry.boot_id; + c->monotonic = o->entry.monotonic; + c->realtime = o->entry.realtime; + c->xor_hash = o->entry.xor_hash; - if (offset) - *offset = np; + *cursor = c; + *size = sizeof(JournalCursor); return 1; } -int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { - uint64_t p, osize, hash, h; - int r; - - assert(f); - assert(data || size == 0); - - osize = offsetof(Object, data.payload) + size; - - hash = hash64(data, size); - h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); - p = le64toh(f->hash_table[h].head_hash_offset); - - while (p != 0) { - Object *o; - - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - return r; - - if (le64toh(o->object.type) != OBJECT_DATA) - return -EBADMSG; - - if (le64toh(o->object.size) == osize && - memcmp(o->data.payload, data, size) == 0) { - - if (le64toh(o->data.hash) != hash) - return -EBADMSG; - - if (o->data.head_entry_offset == 0) - return 0; - - p = le64toh(o->data.head_entry_offset); - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - return r; - - if (le64toh(o->object.type) != OBJECT_ENTRY) - return -EBADMSG; - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; - } - - p = le64toh(o->data.next_hash_offset); - } - - return 0; -} - -int journal_file_find_last_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { - uint64_t p, osize, hash, h; - int r; - - assert(f); - assert(data || size == 0); - - osize = offsetof(Object, data.payload) + size; - - hash = hash64(data, size); - h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); - p = le64toh(f->hash_table[h].tail_hash_offset); - - while (p != 0) { - Object *o; - - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - return r; - - if (le64toh(o->object.type) != OBJECT_DATA) - return -EBADMSG; - - if (le64toh(o->object.size) == osize && - memcmp(o->data.payload, data, size) == 0) { - - if (le64toh(o->data.hash) != hash) - return -EBADMSG; - - if (o->data.tail_entry_offset == 0) - return 0; - - p = le64toh(o->data.tail_entry_offset); - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - return r; - - if (le64toh(o->object.type) != OBJECT_ENTRY) - return -EBADMSG; - - if (ret) - *ret = o; - - if (offset) - *offset = p; - - return 1; - } - - p = le64toh(o->data.prev_hash_offset); - } - - return 0; -} - -void journal_file_dump(JournalFile *f) { - char a[33], b[33], c[33]; - Object *o; - int r; - uint64_t p; - - assert(f); - - printf("File ID: %s\n" - "Machine ID: %s\n" - "Boot ID: %s\n" - "Arena size: %llu\n", - sd_id128_to_string(f->header->file_id, a), - sd_id128_to_string(f->header->machine_id, b), - sd_id128_to_string(f->header->boot_id, c), - (unsigned long long) le64toh(f->header->arena_size)); - - p = le64toh(f->header->head_object_offset); - while (p != 0) { - r = journal_file_move_to_object(f, p, &o); - if (r < 0) - goto fail; - - switch (o->object.type) { - - case OBJECT_UNUSED: - printf("Type: OBJECT_UNUSED\n"); - break; - - case OBJECT_DATA: - printf("Type: OBJECT_DATA\n"); - break; - - case OBJECT_ENTRY: - printf("Type: OBJECT_ENTRY %llu\n", (unsigned long long) le64toh(o->entry.seqnum)); - break; - - case OBJECT_HASH_TABLE: - printf("Type: OBJECT_HASH_TABLE\n"); - break; - - case OBJECT_BISECT_TABLE: - printf("Type: OBJECT_BISECT_TABLE\n"); - break; - } - - if (p == le64toh(f->header->tail_object_offset)) - p = 0; - else - p = p + ALIGN64(le64toh(o->object.size)); - } - - return; -fail: - log_error("File corrupt"); -} - -int journal_file_open( - const char *fname, - int flags, - mode_t mode, - JournalFile **ret) { - - JournalFile *f; - int r; - bool newly_created = false; - - assert(fname); - - if ((flags & O_ACCMODE) != O_RDONLY && - (flags & O_ACCMODE) != O_RDWR) - return -EINVAL; - - f = new0(JournalFile, 1); - if (!f) - return -ENOMEM; - - f->writable = (flags & O_ACCMODE) != O_RDONLY; - f->prot = prot_from_flags(flags); - - f->fd = open(fname, flags|O_CLOEXEC, mode); - if (f->fd < 0) { - r = -errno; - goto fail; - } - - f->path = strdup(fname); - if (!f->path) { - r = -ENOMEM; - goto fail; - } - - if (fstat(f->fd, &f->last_stat) < 0) { - r = -errno; - goto fail; - } - - if (f->last_stat.st_size == 0 && f->writable) { - newly_created = true; - - r = journal_file_init_header(f); - if (r < 0) - goto fail; - - if (fstat(f->fd, &f->last_stat) < 0) { - r = -errno; - goto fail; - } - } - - if (f->last_stat.st_size < (off_t) sizeof(Header)) { - r = -EIO; - goto fail; - } - - f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0); - if (f->header == MAP_FAILED) { - f->header = NULL; - r = -errno; - goto fail; - } - - if (!newly_created) { - r = journal_file_verify_header(f); - if (r < 0) - goto fail; - } - - if (f->writable) { - r = journal_file_refresh_header(f); - if (r < 0) - goto fail; - } - - if (newly_created) { - - r = journal_file_setup_hash_table(f); - if (r < 0) - goto fail; - - r = journal_file_setup_bisect_table(f); - if (r < 0) - goto fail; - } - - r = journal_file_map_hash_table(f); - if (r < 0) - goto fail; - - r = journal_file_map_bisect_table(f); - if (r < 0) - goto fail; - - if (ret) - *ret = f; - - return 0; - -fail: - journal_file_close(f); - - return r; +int sd_journal_set_cursor(sd_journal *j, const void *cursor, size_t size) { + return -EINVAL; } int sd_journal_open(sd_journal **ret) { diff --git a/src/journal/sd-journal.h b/src/journal/sd-journal.h index 8170dea87c..55e58601fe 100644 --- a/src/journal/sd-journal.h +++ b/src/journal/sd-journal.h @@ -25,13 +25,12 @@ #include #include -#include "sd-id128.h" - /* TODO: * * - implement rotation * - check LE/BE conversion for 8bit, 16bit, 32bit values * - implement parallel traversal + * - implement inotify usage on client * - implement audit gateway * - implement native gateway * - extend hash table/bisect table as we go @@ -45,12 +44,13 @@ void sd_journal_close(sd_journal *j); int sd_journal_previous(sd_journal *j); int sd_journal_next(sd_journal *j); -void* sd_journal_get(sd_journal *j, const char *field, size_t *size); -uint64_t sd_journal_get_seqnum(sd_journal *j); -uint64_t sd_journal_get_realtime_usec(sd_journal *j); -uint64_t sd_journal_get_monotonic_usec(sd_journal *j); +int sd_journal_get(sd_journal *j, const char *field, const void **data, size_t *size); +int sd_journal_get_seqnum(sd_journal *j, uint64_t *ret); +int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret); +int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret); -int sd_journal_add_match(sd_journal *j, const char *item, size_t *size); +int sd_journal_add_match(sd_journal *j, const char *field, const void *data, size_t size); +void sd_journal_flush_matches(sd_journal *j); int sd_journal_seek_head(sd_journal *j); int sd_journal_seek_tail(sd_journal *j); @@ -59,16 +59,9 @@ int sd_journal_seek_seqnum(sd_journal *j, uint64_t seqnum); int sd_journal_seek_monotonic_usec(sd_journal *j, uint64_t usec); int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); -uint64_t sd_journal_get_max_size(sd_journal *j); -uint64_t sd_journal_get_min_size(sd_journal *j); -uint64_t sd_journal_get_keep_free(sd_journal *j); - -int sd_journal_set_max_size(sd_journal *j, uint64_t size); -int sd_journal_set_min_size(sd_journal *j, uint64_t size); -int sd_journal_set_keep_free(sd_journal *j, uint64_t size); +int sd_journal_get_cursor(sd_journal *j, void **cursor, size_t *size); +int sd_journal_set_cursor(sd_journal *j, const void *cursor, size_t size); -sd_id128_t sd_journal_get_file_id(sd_journal *j); -sd_id128_t sd_journal_get_machine_id(sd_journal *j); -sd_id128_t sd_journal_get_boot_id(sd_journal *j); +int sd_journal_get_fd(sd_journal *j); #endif diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index e0aedc7b83..7028f11f7c 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -21,7 +21,7 @@ #include -#include "journal-private.h" +#include "journal-file.h" #include "log.h" int main(int argc, char *argv[]) { -- cgit v1.2.3-54-g00ecf From 69e5d42db09dfb638bc74055c33bb2645f81563d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 12 Oct 2011 05:28:39 +0200 Subject: journal: add cgroup path to entries --- Makefile.am | 3 ++- src/journal/journald.c | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index b26c613d4f..e08785a8a3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -983,7 +983,8 @@ systemd_journald_SOURCES = \ src/journal/journal-file.c \ src/journal/lookup3.c \ src/sd-id128.c \ - src/acl-util.c + src/acl-util.c \ + src/cgroup-util.c systemd_journald_CFLAGS = \ $(AM_CFLAGS) \ diff --git a/src/journal/journald.c b/src/journal/journald.c index d6db9b1c55..b8a9fc3adf 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -33,6 +33,7 @@ #include "sd-daemon.h" #include "socket-util.h" #include "acl-util.h" +#include "cgroup-util.h" typedef struct Server { int syslog_fd; @@ -134,8 +135,8 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str *comm = NULL, *cmdline = NULL, *hostname = NULL, *audit_session = NULL, *audit_loginuid = NULL, *syslog_priority = NULL, *syslog_facility = NULL, - *exe = NULL; - struct iovec iovec[15]; + *exe = NULL, *cgroup = NULL; + struct iovec iovec[16]; unsigned n = 0; char idbuf[33]; sd_id128_t id; @@ -160,6 +161,7 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str if (ucred) { uint32_t session; + char *path; if (asprintf(&pid, "PID=%lu", (unsigned long) ucred->pid) >= 0) IOVEC_SET_STRING(iovec[n++], pid); @@ -203,6 +205,14 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str if (r >= 0) if (asprintf(&audit_loginuid, "AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0) IOVEC_SET_STRING(iovec[n++], audit_loginuid); + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, ucred->pid, &path); + if (r >= 0) { + cgroup = strappend("SYSTEMD_CGROUP=", path); + if (cgroup) + IOVEC_SET_STRING(iovec[n++], cgroup); + free(path); + } } if (tv) { @@ -247,6 +257,7 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str free(uid); free(gid); free(comm); + free(exe); free(cmdline); free(source_time); free(boot_id); @@ -256,6 +267,7 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str free(audit_loginuid); free(syslog_facility); free(syslog_priority); + free(cgroup); } static int process_event(Server *s, struct epoll_event *ev) { -- cgit v1.2.3-54-g00ecf From 7f3e62571a63ac90de6ac5eefeeb8d3e9aa6f49e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 17 Dec 2011 00:56:34 +0100 Subject: journal: add native protocol to journald, and client side API to send journal messages --- Makefile.am | 1 + TODO | 4 + src/journal/journal-send.c | 196 +++++++++++++++++++++++ src/journal/journald.c | 375 ++++++++++++++++++++++++++++++++++++++------- src/journal/sd-journal.h | 12 ++ src/journal/test-journal.c | 1 + 6 files changed, 531 insertions(+), 58 deletions(-) create mode 100644 src/journal/journal-send.c (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index 58b3a63517..7dee4cf0a4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -983,6 +983,7 @@ test_journal_SOURCES = \ src/journal/sd-journal.c \ src/journal/journal-file.c \ src/journal/lookup3.c \ + src/journal/journal-send.c \ src/sd-id128.c test_journal_CFLAGS = \ diff --git a/TODO b/TODO index 8c3034e079..5b64f048e5 100644 --- a/TODO +++ b/TODO @@ -21,6 +21,10 @@ Bugfixes: Features: +* logind: allow showing logout dialog from system + +* document that %% can be used to write % in a string that is specifier extended + * check utf8 everywhere * when an instanced service exits, remove its parent cgroup too if possible. diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c new file mode 100644 index 0000000000..e2575a9805 --- /dev/null +++ b/src/journal/journal-send.c @@ -0,0 +1,196 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "sd-journal.h" +#include "util.h" + +/* We open a single fd, and we'll share it with the current process, + * all its threads, and all its subprocesses. This means we need to + * initialize it atomically, and need to operate on it atomically + * never assuming we are the only user */ + +static int journal_fd(void) { + int fd; + static int fd_plus_one = 0; + +retry: + if (fd_plus_one > 0) + return fd_plus_one - 1; + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) { + close_nointr_nofail(fd); + goto retry; + } + + return fd; +} + +int sd_journal_print(const char *format, ...) { + int r; + va_list ap; + + va_start(ap, format); + r = sd_journal_printv(format, ap); + va_end(ap); + + return r; +} + +int sd_journal_printv(const char *format, va_list ap) { + char buffer[8 + LINE_MAX]; + struct iovec iov; + + memcpy(buffer, "MESSAGE=", 8); + vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + + char_array_0(buffer); + + zero(iov); + IOVEC_SET_STRING(iov, buffer); + + return sd_journal_sendv(&iov, 1); +} + +int sd_journal_send(const char *format, ...) { + int r, n = 0, i = 0, j; + va_list ap; + struct iovec *iov = NULL; + + va_start(ap, format); + while (format) { + struct iovec *c; + char *buffer; + + if (i >= n) { + n = MAX(i*2, 4); + c = realloc(iov, n * sizeof(struct iovec)); + if (!c) { + r = -ENOMEM; + goto fail; + } + + iov = c; + } + + if (vasprintf(&buffer, format, ap) < 0) { + r = -ENOMEM; + goto fail; + } + + IOVEC_SET_STRING(iov[i++], buffer); + + format = va_arg(ap, char *); + } + va_end(ap); + + r = sd_journal_sendv(iov, i); + +fail: + for (j = 0; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +int sd_journal_sendv(const struct iovec *iov, int n) { + int fd; + struct iovec *w; + uint64_t *l; + int i, j = 0; + struct msghdr mh; + struct sockaddr_un sa; + + if (!iov || n <= 0) + return -EINVAL; + + w = alloca(sizeof(struct iovec) * n * 5); + l = alloca(sizeof(uint64_t) * n); + + for (i = 0; i < n; i++) { + char *c, *nl; + + c = memchr(iov[i].iov_base, '=', iov[i].iov_len); + if (!c) + return -EINVAL; + + nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); + if (nl) { + if (nl < c) + return -EINVAL; + + /* Already includes a newline? Bummer, then + * let's write the variable name, then a + * newline, then the size (64bit LE), followed + * by the data and a final newline */ + + w[j].iov_base = iov[i].iov_base; + w[j].iov_len = c - (char*) iov[i].iov_base; + j++; + + IOVEC_SET_STRING(w[j++], "\n"); + + l[i] = htole64(iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1); + w[j].iov_base = &l[i]; + w[j].iov_len = sizeof(uint64_t); + j++; + + w[j].iov_base = c + 1; + w[j].iov_len = iov[i].iov_len - (c - (char*) iov[i].iov_base) - 1; + j++; + + } else + /* Nothing special? Then just add the line and + * append a newline */ + w[j++] = iov[i]; + + IOVEC_SET_STRING(w[j++], "\n"); + } + + fd = journal_fd(); + if (fd < 0) + return fd; + + zero(sa); + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path,"/run/systemd/journal", sizeof(sa.sun_path)); + + zero(mh); + mh.msg_name = &sa; + mh.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path); + mh.msg_iov = w; + mh.msg_iovlen = j; + + if (sendmsg(fd, &mh, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; +} diff --git a/src/journal/journald.c b/src/journal/journald.c index 89d8bee2a2..453495a964 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #include "hashmap.h" #include "journal-file.h" @@ -36,15 +39,19 @@ #include "cgroup-util.h" typedef struct Server { - int syslog_fd; int epoll_fd; int signal_fd; + int syslog_fd; + int native_fd; JournalFile *runtime_journal; JournalFile *system_journal; Hashmap *user_journals; uint64_t seqnum; + + char *buffer; + size_t buffer_size; } Server; static void fix_perms(JournalFile *f, uid_t uid) { @@ -137,35 +144,27 @@ static JournalFile* find_journal(Server *s, uid_t uid) { return f; } -static void process_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { - char *message = NULL, *pid = NULL, *uid = NULL, *gid = NULL, +static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv) { + char *pid = NULL, *uid = NULL, *gid = NULL, *source_time = NULL, *boot_id = NULL, *machine_id = NULL, *comm = NULL, *cmdline = NULL, *hostname = NULL, *audit_session = NULL, *audit_loginuid = NULL, - *syslog_priority = NULL, *syslog_facility = NULL, *exe = NULL, *cgroup = NULL; - struct iovec iovec[17]; - unsigned n = 0; + char idbuf[33]; sd_id128_t id; int r; char *t; - int priority = LOG_USER | LOG_INFO; uid_t loginuid = 0, realuid = 0; JournalFile *f; - parse_syslog_priority((char**) &buf, &priority); - skip_syslog_date((char**) &buf); - - if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) - IOVEC_SET_STRING(iovec[n++], syslog_priority); + assert(s); + assert(iovec || n == 0); - if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) - IOVEC_SET_STRING(iovec[n++], syslog_facility); + if (n == 0) + return; - message = strappend("MESSAGE=", buf); - if (message) - IOVEC_SET_STRING(iovec[n++], message); + assert(n + 13 <= m); if (ucred) { uint32_t session; @@ -252,6 +251,8 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str free(t); } + assert(n <= m); + f = find_journal(s, realuid == 0 ? 0 : loginuid); if (!f) log_warning("Dropping message, as we can't find a place to store the data."); @@ -262,7 +263,6 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str log_error("Failed to write entry, ignoring: %s", strerror(-r)); } - free(message); free(pid); free(uid); free(gid); @@ -275,9 +275,148 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str free(hostname); free(audit_session); free(audit_loginuid); + free(cgroup); + +} + +static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { + char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL; + struct iovec iovec[16]; + unsigned n = 0; + int priority = LOG_USER | LOG_INFO; + + assert(s); + assert(buf); + + parse_syslog_priority((char**) &buf, &priority); + skip_syslog_date((char**) &buf); + + if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_priority); + + if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_facility); + + message = strappend("MESSAGE=", buf); + if (message) + IOVEC_SET_STRING(iovec[n++], message); + + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv); + + free(message); free(syslog_facility); free(syslog_priority); - free(cgroup); +} + +static void process_native_message(Server *s, const void *buffer, size_t buffer_size, struct ucred *ucred, struct timeval *tv) { + struct iovec *iovec = NULL; + unsigned n = 0, m = 0, j; + const char *p; + size_t remaining; + + assert(s); + assert(buffer || n == 0); + + p = buffer; + remaining = buffer_size; + + while (remaining > 0) { + const char *e, *q; + + e = memchr(p, '\n', remaining); + + if (!e) { + /* Trailing noise, let's ignore it, and flush what we collected */ + log_debug("Received message with trailing noise, ignoring."); + break; + } + + if (e == p) { + /* Entry separator */ + dispatch_message(s, iovec, n, m, ucred, tv); + n = 0; + + p++; + remaining--; + continue; + } + + if (*p == '.') { + /* Control command, ignore for now */ + remaining -= (e - p) + 1; + p = e + 1; + continue; + } + + /* A property follows */ + + if (n+13 >= m) { + struct iovec *c; + unsigned u; + + u = MAX((n+13U) * 2U, 4U); + c = realloc(iovec, u * sizeof(struct iovec)); + if (!c) { + log_error("Out of memory"); + break; + } + + iovec = c; + m = u; + } + + q = memchr(p, '=', e - p); + if (q) { + iovec[n].iov_base = (char*) p; + iovec[n].iov_len = e - p; + n++; + + remaining -= (e - p) + 1; + p = e + 1; + continue; + } else { + uint64_t l; + char *k; + + if (remaining < e - p + 1 + sizeof(uint64_t) + 1) { + log_debug("Failed to parse message, ignoring."); + break; + } + + memcpy(&l, e + 1, sizeof(uint64_t)); + l = le64toh(l); + + if (remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || + e[1+sizeof(uint64_t)+l] != '\n') { + log_debug("Failed to parse message, ignoring."); + break; + } + + k = malloc((e - p) + 1 + l); + if (!k) { + log_error("Out of memory"); + break; + } + + memcpy(k, p, e - p); + k[e - p] = '='; + memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l); + + iovec[n].iov_base = k; + iovec[n].iov_len = (e - p) + 1 + l; + n++; + + remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1; + p = e + 1 + sizeof(uint64_t) + l + 1; + } + } + + dispatch_message(s, iovec, n, m, ucred, tv); + + for (j = 0; j < n; j++) + if (iovec[j].iov_base < buffer || + (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size) + free(iovec[j].iov_base); } static int process_event(Server *s, struct epoll_event *ev) { @@ -309,9 +448,9 @@ static int process_event(Server *s, struct epoll_event *ev) { } - if (ev->data.fd == s->syslog_fd) { + if (ev->data.fd == s->native_fd || + ev->data.fd == s->syslog_fd) { for (;;) { - char buf[LINE_MAX+1]; struct msghdr msghdr; struct iovec iovec; struct ucred *ucred = NULL; @@ -323,11 +462,35 @@ static int process_event(Server *s, struct epoll_event *ev) { CMSG_SPACE(sizeof(struct timeval))]; } control; ssize_t n; - char *e; + int v; + + if (ioctl(ev->data.fd, SIOCINQ, &v) < 0) { + log_error("SIOCINQ failed: %m"); + return -errno; + } + + if (v <= 0) + return 1; + + if (s->buffer_size < (size_t) v) { + void *b; + size_t l; + + l = MAX(LINE_MAX + (size_t) v, s->buffer_size * 2); + b = realloc(s->buffer, l+1); + + if (!b) { + log_error("Couldn't increase buffer."); + return -ENOMEM; + } + + s->buffer_size = l; + s->buffer = b; + } zero(iovec); - iovec.iov_base = buf; - iovec.iov_len = sizeof(buf)-1; + iovec.iov_base = s->buffer; + iovec.iov_len = s->buffer_size; zero(control); zero(msghdr); @@ -358,13 +521,18 @@ static int process_event(Server *s, struct epoll_event *ev) { tv = (struct timeval*) CMSG_DATA(cmsg); } - e = memchr(buf, '\n', n); - if (e) - *e = 0; - else - buf[n] = 0; + if (ev->data.fd == s->syslog_fd) { + char *e; - process_message(s, strstrip(buf), ucred, tv); + e = memchr(s->buffer, '\n', n); + if (e) + *e = 0; + else + s->buffer[n] = 0; + + process_syslog_message(s, strstrip(s->buffer), ucred, tv); + } else + process_native_message(s, s->buffer, n, ucred, tv); } return 1; @@ -428,51 +596,73 @@ static int system_journal_open(Server *s) { return r; } -static int server_init(Server *s) { - int n, one, r; - struct epoll_event ev; - sigset_t mask; +static int open_syslog_socket(Server *s) { + union sockaddr_union sa; + int one, r; assert(s); - zero(*s); - s->syslog_fd = s->signal_fd = -1; + if (s->syslog_fd < 0) { - s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); - if (s->epoll_fd < 0) { - log_error("Failed to create epoll object: %m"); - return -errno; + s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (s->syslog_fd < 0) { + log_error("socket() failed: %m"); + return -errno; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/syslog", 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)); + if (r < 0) { + log_error("bind() failed: %m"); + return -errno; + } + + chmod(sa.un.sun_path, 0666); } - n = sd_listen_fds(true); - if (n < 0) { - log_error("Failed to read listening file descriptors from environment: %s", strerror(-n)); - return n; + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) { + log_error("SO_PASSCRED failed: %m"); + return -errno; } - if (n > 1) { - log_error("Too many file descriptors passed."); - return -EINVAL; + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + if (r < 0) { + log_error("SO_TIMESTAMP failed: %m"); + return -errno; } - if (n == 1) - s->syslog_fd = SD_LISTEN_FDS_START; - else { - union sockaddr_union sa; + return 0; +} - s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (s->syslog_fd < 0) { +static int open_native_socket(Server*s) { + union sockaddr_union sa; + int one, r; + + assert(s); + + if (s->native_fd < 0) { + + s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (s->native_fd < 0) { log_error("socket() failed: %m"); return -errno; } zero(sa); sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path)); + strncpy(sa.un.sun_path, "/run/systemd/journal", sizeof(sa.un.sun_path)); unlink(sa.un.sun_path); - r = bind(s->syslog_fd, &sa.sa, sizeof(sa.un)); + r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); if (r < 0) { log_error("bind() failed: %m"); return -errno; @@ -482,24 +672,90 @@ static int server_init(Server *s) { } one = 1; - r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); if (r < 0) { log_error("SO_PASSCRED failed: %m"); return -errno; } one = 1; - r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); if (r < 0) { log_error("SO_TIMESTAMP failed: %m"); return -errno; } + return 0; +} + +static int server_init(Server *s) { + int n, r, fd; + struct epoll_event ev; + sigset_t mask; + + assert(s); + + zero(*s); + s->syslog_fd = s->native_fd = s->signal_fd = -1; + + s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (s->epoll_fd < 0) { + log_error("Failed to create epoll object: %m"); + return -errno; + } + + n = sd_listen_fds(true); + if (n < 0) { + log_error("Failed to read listening file descriptors from environment: %s", strerror(-n)); + return n; + } + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { + + if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0) { + + if (s->syslog_fd >= 0) { + log_error("Too many /dev/log sockets passed."); + return -EINVAL; + } + + s->syslog_fd = fd; + + } else if (sd_is_socket(fd, AF_UNIX, SOCK_DGRAM, -1) > 0) { + + if (s->native_fd >= 0) { + log_error("Too many native sockets passed."); + return -EINVAL; + } + + s->native_fd = fd; + } else { + log_error("Unknown socket passed."); + return -EINVAL; + } + } + + r = open_syslog_socket(s); + if (r < 0) + return r; + zero(ev); ev.events = EPOLLIN; ev.data.fd = s->syslog_fd; if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) { - log_error("Failed to add server fd to epoll object: %m"); + log_error("Failed to add syslog server fd to epoll object: %m"); + return -errno; + } + + r = open_native_socket(s); + if (r < 0) + return r; + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->native_fd; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) { + log_error("Failed to add native server fd to epoll object: %m"); return -errno; } @@ -558,6 +814,9 @@ static void server_done(Server *s) { if (s->syslog_fd >= 0) close_nointr_nofail(s->syslog_fd); + + if (s->native_fd >= 0) + close_nointr_nofail(s->native_fd); } int main(int argc, char *argv[]) { diff --git a/src/journal/sd-journal.h b/src/journal/sd-journal.h index 8d7e314223..e42293ffe7 100644 --- a/src/journal/sd-journal.h +++ b/src/journal/sd-journal.h @@ -24,6 +24,8 @@ #include #include +#include +#include #include "sd-id128.h" @@ -43,6 +45,16 @@ * - comm, argv can be manipulated, should it be _COMM=, _CMDLINE= or COMM=, CMDLINE=? */ +/* Write to daemon */ + +int sd_journal_print(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +int sd_journal_printv(const char *format, va_list ap); + +int sd_journal_send(const char *format, ...) __attribute__((sentinel)); +int sd_journal_sendv(const struct iovec *iov, int n); + +/* Browse journal stream */ + typedef struct sd_journal sd_journal; int sd_journal_open(sd_journal **ret); diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 45ced12b46..a9bd6cb2cf 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -22,6 +22,7 @@ #include #include +#include "sd-journal.h" #include "journal-file.h" #include "log.h" -- cgit v1.2.3-54-g00ecf From f0d2e205a28e37528ef791cc2913e6664d0dde7f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Dec 2011 13:12:36 +0100 Subject: man: build new man pages --- Makefile.am | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index 7dee4cf0a4..248204b45c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -831,6 +831,12 @@ MANPAGES_ALIAS = \ man/sd_is_socket_unix.3 \ man/sd_is_socket_inet.3 \ man/sd_notifyf.3 \ + man/sd_pid_get_session.3 \ + man/sd_uid_get_state.3 \ + man/sd_session_is_active.3 \ + man/sd_seat_get_active.3 \ + man/sd_get_seats.3 \ + man/sd_login_monitor_new.3 \ man/init.1 man/reboot.8: man/halt.8 -- cgit v1.2.3-54-g00ecf From c10eb7b02eb048eb23f0c9f239bfe1f9e7bc8e4a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Dec 2011 13:25:00 +0100 Subject: build-sys: add rules for man page aliases --- Makefile.am | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index 248204b45c..907ecb4f7c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -844,8 +844,22 @@ man/poweroff.8: man/halt.8 man/sd_is_socket.3: man/sd_is_fifo.3 man/sd_is_socket_unix.3: man/sd_is_fifo.3 man/sd_is_socket_inet.3: man/sd_is_fifo.3 +man/sd_is_mq.3: man/sd_is_fifo.3 man/sd_notifyf.3: man/sd_notify.3 man/init.1: man/systemd.1 +man/sd_session_get_uid.3: man/sd_session_is_active.3 +man/sd_session_get_seat.3: man/sd_session_is_active.3 +man/sd_pid_get_owner_uid.3: man/sd_pid_get_session.3 +man/sd_uid_is_on_seat.3: man/sd_uid_get_state.3 +man/sd_uid_get_sessions.3: man/sd_uid_get_state.3 +man/sd_uid_get_seats.3: man/sd_uid_get_state.3 +man/sd_seat_get_sessions.3: man/sd_seat_get_active.3 +man/sd_seat_can_multi_session.3: man/sd_seat_get_active.3 +man/sd_get_sessions.3: man/sd_get_seats.3 +man/sd_get_uids.3: man/sd_get_seats.3 +man/sd_login_monitor_unref.3: man/sd_login_monitor_new.3 +man/sd_login_monitor_flush.3: man/sd_login_monitor_new.3 +man/sd_login_monitor_get_fd.3: man/sd_login_monitor_new.3 dist_man_MANS = \ $(MANPAGES) \ -- cgit v1.2.3-54-g00ecf From 01448ff92d9549785242ffab453bf5bcde348c61 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Dec 2011 13:57:07 +0100 Subject: man: add sd-login(7) page --- Makefile.am | 31 ++++++++---- man/sd-login.xml | 117 +++++++++++++++++++++++++++++++++++++++++++++ man/sd_pid_get_session.xml | 19 ++++---- 3 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 man/sd-login.xml (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index 907ecb4f7c..e689355cee 100644 --- a/Makefile.am +++ b/Makefile.am @@ -799,6 +799,7 @@ MANPAGES = \ man/daemon.7 \ man/sd-daemon.7 \ man/sd-readahead.7 \ + man/sd-login.7 \ man/runlevel.8 \ man/telinit.8 \ man/halt.8 \ @@ -817,7 +818,13 @@ MANPAGES = \ man/modules-load.d.5 \ man/sysctl.d.5 \ man/systemd-ask-password.1 \ - man/systemd-loginctl.1 + man/systemd-loginctl.1 \ + man/sd_pid_get_session.3 \ + man/sd_uid_get_state.3 \ + man/sd_session_is_active.3 \ + man/sd_seat_get_active.3 \ + man/sd_get_seats.3 \ + man/sd_login_monitor_new.3 if ENABLE_BINFMT MANPAGES += \ @@ -830,14 +837,22 @@ MANPAGES_ALIAS = \ man/sd_is_socket.3 \ man/sd_is_socket_unix.3 \ man/sd_is_socket_inet.3 \ + man/sd_is_mq.3 \ man/sd_notifyf.3 \ - man/sd_pid_get_session.3 \ - man/sd_uid_get_state.3 \ - man/sd_session_is_active.3 \ - man/sd_seat_get_active.3 \ - man/sd_get_seats.3 \ - man/sd_login_monitor_new.3 \ - man/init.1 + man/init.1 \ + man/sd_session_get_uid.3 \ + man/sd_session_get_seat.3 \ + man/sd_pid_get_owner_uid.3 \ + man/sd_uid_is_on_seat.3 \ + man/sd_uid_get_sessions.3 \ + man/sd_uid_get_seats.3 \ + man/sd_seat_get_sessions.3 \ + man/sd_seat_can_multi_session.3 \ + man/sd_get_sessions.3 \ + man/sd_get_uids.3 \ + man/sd_login_monitor_unref.3 \ + man/sd_login_monitor_flush.3 \ + man/sd_login_monitor_get_fd.3 man/reboot.8: man/halt.8 man/poweroff.8: man/halt.8 diff --git a/man/sd-login.xml b/man/sd-login.xml new file mode 100644 index 0000000000..62ec6ffefd --- /dev/null +++ b/man/sd-login.xml @@ -0,0 +1,117 @@ + + + + + + + + + sd-login + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd-login + 7 + + + + sd-login + APIs for + tracking logins + + + + + #include <systemd/sd-login.h> + + + + pkg-config --cflags --libs libsystemd-login + + + + + Description + + sd-login.h provides APIs to + introspect and monitor seat, login session and user + status information on the local system. + + See Multi-Seat + on Linux for an introduction into multi-seat + support on Linux, the background for this set of APIs. + + Note that these APIs only allow purely passive access + and monitoring of seats, sessions and users. To + actively make changes to the seat configuration, + terminate login sessions, or switch session on a seat + you need to utilize the D-Bus API of + systemd-logind. + + See + sd_pid_get_session3, + sd_uid_get_state3, + sd_session_is_active3, + sd_seat_get_active3, + sd_get_seats3, + sd_login_monitor_new3 + for more information about the functions + implemented. + + + + Notes + + These APIs are implemented as shared library, + which can be compiled and linked to with the + libsystemd-login + pkg-config1 + file. + + + + See Also + + systemd1, + sd_pid_get_session3, + sd_uid_get_state3, + sd_session_is_active3, + sd_seat_get_active3, + sd_get_seats3, + sd_login_monitor_new3, + sd-daemon7, + sd-readahead7, + pkg-config1 + + + + diff --git a/man/sd_pid_get_session.xml b/man/sd_pid_get_session.xml index 9176433c3d..24e468015f 100644 --- a/man/sd_pid_get_session.xml +++ b/man/sd_pid_get_session.xml @@ -71,16 +71,15 @@ sd_pid_get_session() may be used to determine the login session identifier of a - process identified by the specified process identifier. The session - identifier is a short string (up to 64 characters), - consisting only of the characters a-zA-Z0-9 as well as - '-' and '_'. It is suitable for usage in file system - paths. Note that not all processes are part of a login - session (e.g. system service processes and user - processes that are shared between multiple sessions of - the same user). For processes not being part of a - login session this function will fail. The returned - string needs to be freed with the libc + process identified by the specified process + identifier. The session identifier is a short string, + suitable for usage in file system paths. Note that not + all processes are part of a login session (e.g. system + service processes and user processes that are shared + between multiple sessions of the same user). For + processes not being part of a login session this + function will fail. The returned string needs to be + freed with the libc free3 call after use. -- cgit v1.2.3-54-g00ecf From 38c67e2a442d875c1de6f5aae46647a195230b05 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Dec 2011 19:55:54 +0100 Subject: man: generate HTML instead of XHTML with XSL docbook to work around 'fsfunc' noise --- Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index e689355cee..656909c6c7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1771,11 +1771,11 @@ XSLTPROC_PROCESS_MAN_IN = \ XSLTPROC_PROCESS_HTML = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/xhtml-1_1/docbook.xsl $< + $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< XSLTPROC_PROCESS_HTML_IN = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(XSLTPROC) -o ${@:.in=} $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/xhtml-1_1/docbook.xsl $< && \ + $(XSLTPROC) -o ${@:.in=} $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< && \ mv ${@:.in=} $@ man/%.1: man/%.xml -- cgit v1.2.3-54-g00ecf From 76318284fc970b30e9dc4c079960807345331dad Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 Dec 2011 20:25:52 +0100 Subject: man: switch to UTF-8 output, to work around charset issues --- Makefile.am | 9 +++++---- man/custom-html.xsl | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 man/custom-html.xsl (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index 656909c6c7..f9093b6d8e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -506,7 +506,8 @@ EXTRA_DIST = \ src/libsystemd-login.sym \ introspect.awk \ src/73-seat-late.rules.in \ - src/99-systemd.rules.in + src/99-systemd.rules.in \ + man/custom-html.xsl if ENABLE_BINFMT EXTRA_DIST += \ @@ -1758,7 +1759,7 @@ endif if HAVE_XSLTPROC XSLTPROC_FLAGS = \ --nonet \ - --param funcsynopsis.style "'ansi'" + --stringparam funcsynopsis.style ansi XSLTPROC_PROCESS_MAN = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ @@ -1771,11 +1772,11 @@ XSLTPROC_PROCESS_MAN_IN = \ XSLTPROC_PROCESS_HTML = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< + $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) man/custom-html.xsl $< XSLTPROC_PROCESS_HTML_IN = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(XSLTPROC) -o ${@:.in=} $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< && \ + $(XSLTPROC) -o ${@:.in=} $(XSLTPROC_FLAGS) man/custom-html.xsl $< && \ mv ${@:.in=} $@ man/%.1: man/%.xml diff --git a/man/custom-html.xsl b/man/custom-html.xsl new file mode 100644 index 0000000000..2d2f458793 --- /dev/null +++ b/man/custom-html.xsl @@ -0,0 +1,29 @@ + + + + + + + + + + + + -- cgit v1.2.3-54-g00ecf From 807e17f05e217b474af39503efb9503d81b12596 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Dec 2011 02:40:59 +0100 Subject: journal: add inline compression support with XZ --- Makefile.am | 27 +++++++++++++++++ TODO | 2 ++ configure.ac | 14 +++++++++ src/journal/journal-def.h | 13 +++++++- src/journal/journal-file.c | 75 ++++++++++++++++++++++++++++++++++++++++++---- src/journal/journal-file.h | 7 +++++ src/journal/journald.c | 11 +++++++ src/journal/sd-journal.c | 49 ++++++++++++++++++++++++++---- src/journal/sd-journal.h | 2 +- 9 files changed, 187 insertions(+), 13 deletions(-) (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index f9093b6d8e..7d551a9dcf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1028,6 +1028,15 @@ test_journal_CFLAGS = \ test_journal_LDADD = \ libsystemd-basic.la +if HAVE_XZ +test_journal_SOURCES += \ + src/journal/compress.c +test_journal_CFLAGS += \ + $(XZ_CFLAGS) +test_journal_LDADD += \ + $(XZ_LIBS) +endif + systemd_journald_SOURCES = \ src/journal/journald.c \ src/journal/sd-journal.c \ @@ -1046,6 +1055,15 @@ systemd_journald_LDADD = \ libsystemd-daemon.la \ $(ACL_LIBS) +if HAVE_XZ +systemd_journald_SOURCES += \ + src/journal/compress.c +systemd_journald_CFLAGS += \ + $(XZ_CFLAGS) +systemd_journald_LDADD += \ + $(XZ_LIBS) +endif + systemd_journalctl_SOURCES = \ src/journal/journalctl.c \ src/journal/sd-journal.c \ @@ -1059,6 +1077,15 @@ systemd_journalctl_CFLAGS = \ systemd_journalctl_LDADD = \ libsystemd-basic.la +if HAVE_XZ +systemd_journalctl_SOURCES += \ + src/journal/compress.c +systemd_journalctl_CFLAGS += \ + $(XZ_CFLAGS) +systemd_journalctl_LDADD += \ + $(XZ_LIBS) +endif + systemd_stdout_syslog_bridge_SOURCES = \ src/stdout-syslog-bridge.c \ src/tcpwrap.c diff --git a/TODO b/TODO index 5b64f048e5..8daf79a7f0 100644 --- a/TODO +++ b/TODO @@ -21,6 +21,8 @@ Bugfixes: Features: +* logind: sends SessionNew on Lock()? + * logind: allow showing logout dialog from system * document that %% can be used to write % in a string that is specifier extended diff --git a/configure.ac b/configure.ac index 596a32ac23..a2e9c7d65c 100644 --- a/configure.ac +++ b/configure.ac @@ -149,6 +149,19 @@ if test "x$enable_selinux" != "xno"; then fi AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"]) +have_xz=no +AC_ARG_ENABLE(xz, AS_HELP_STRING([--disable-xz], [Disable optional XZ support])) +if test "x$enable_xz" != "xno"; then + PKG_CHECK_MODULES(XZ, [ liblzma ], + [AC_DEFINE(HAVE_XZ, 1, [Define if XZ is available]) have_xz=yes], have_xz=no) + AC_SUBST(XZ_CFLAGS) + AC_SUBST(XZ_LIBS) + if test "x$have_xz" = xno -a "x$enable_xz" = xyes; then + AC_MSG_ERROR([*** Xz support requested but libraries not found]) + fi +fi +AM_CONDITIONAL(HAVE_XZ, [test "$have_xz" = "yes"]) + AC_ARG_ENABLE([tcpwrap], AS_HELP_STRING([--disable-tcpwrap],[Disable optional TCP wrappers support]), [case "${enableval}" in @@ -591,6 +604,7 @@ AC_MSG_RESULT([ PAM: ${have_pam} AUDIT: ${have_audit} SELinux: ${have_selinux} + XZ: ${have_xz} ACL: ${have_acl} binfmt: ${have_binfmt} hostnamed: ${have_hostnamed} diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index 5f026ee0f6..ef0cb6dae6 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -50,9 +50,15 @@ enum { _OBJECT_TYPE_MAX }; +/* Object flags */ +enum { + OBJECT_COMPRESSED = 1 +}; + _packed_ struct ObjectHeader { uint8_t type; - uint8_t reserved[7]; + uint8_t flags; + uint8_t reserved[6]; uint64_t size; uint8_t payload[]; }; @@ -123,6 +129,11 @@ enum { STATE_ARCHIVED }; +/* Header flags */ +enum { + HEADER_INCOMPATIBLE_COMPRESSED = 1 +}; + _packed_ struct Header { uint8_t signature[8]; /* "LPKSHHRH" */ uint32_t compatible_flags; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 8f9b61bc2f..a0c479fc67 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -30,12 +30,15 @@ #include "journal-def.h" #include "journal-file.h" #include "lookup3.h" +#include "compress.h" #define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*16ULL) #define DEFAULT_FIELD_HASH_TABLE_SIZE (2047ULL*16ULL) #define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL) +#define COMPRESSION_SIZE_THRESHOLD (64ULL) + static const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }; #define ALIGN64(x) (((x) + 7ULL) & ~7ULL) @@ -57,6 +60,11 @@ void journal_file_close(JournalFile *f) { close_nointr_nofail(f->fd); free(f->path); + +#ifdef HAVE_XZ + free(f->compress_buffer); +#endif + free(f); } @@ -120,8 +128,13 @@ static int journal_file_verify_header(JournalFile *f) { if (memcmp(f->header, signature, 8)) return -EBADMSG; +#ifdef HAVE_XZ + if ((le64toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0) + return -EPROTONOSUPPORT; +#else if (f->header->incompatible_flags != 0) return -EPROTONOSUPPORT; +#endif if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->arena_offset) + le64toh(f->header->arena_size))) return -ENODATA; @@ -309,7 +322,7 @@ static bool verify_hash(Object *o) { assert(o); - if (o->object.type == OBJECT_DATA) { + if (o->object.type == OBJECT_DATA && !(o->object.flags & OBJECT_COMPRESSED)) { h1 = le64toh(o->data.hash); h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); } else if (o->object.type == OBJECT_FIELD) { @@ -581,12 +594,40 @@ int journal_file_find_data_object_with_hash( if (r < 0) return r; - if (le64toh(o->object.size) == osize && - memcmp(o->data.payload, data, size) == 0) { + if (le64toh(o->data.hash) != hash) + return -EBADMSG; + + if (o->object.flags & OBJECT_COMPRESSED) { +#ifdef HAVE_XZ + uint64_t l, rsize; - if (le64toh(o->data.hash) != hash) + l = le64toh(o->object.size); + if (l <= offsetof(Object, data.payload)) return -EBADMSG; + l -= offsetof(Object, data.payload); + + if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize)) + return -EBADMSG; + + if (rsize == size && + memcmp(f->compress_buffer, data, size) == 0) { + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } +#else + return -EPROTONOSUPPORT; +#endif + + } else if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + if (ret) *ret = o; @@ -624,6 +665,7 @@ static int journal_file_append_data(JournalFile *f, const void *data, uint64_t s uint64_t osize; Object *o; int r; + bool compressed = false; assert(f); assert(data || size == 0); @@ -650,7 +692,27 @@ static int journal_file_append_data(JournalFile *f, const void *data, uint64_t s return r; o->data.hash = htole64(hash); - memcpy(o->data.payload, data, size); + +#ifdef HAVE_XZ + if (f->compress && + size >= COMPRESSION_SIZE_THRESHOLD) { + uint64_t rsize; + + compressed = compress_blob(data, size, o->data.payload, &rsize); + + if (compressed) { + o->object.size = htole64(offsetof(Object, data.payload) + rsize); + o->object.flags |= OBJECT_COMPRESSED; + + f->header->incompatible_flags = htole32(le32toh(f->header->incompatible_flags) | HEADER_INCOMPATIBLE_COMPRESSED); + + log_debug("Compressed data object %lu -> %lu", (unsigned long) size, (unsigned long) rsize); + } + } +#endif + + if (!compressed) + memcpy(o->data.payload, data, size); r = journal_file_link_data(f, o, p, hash); if (r < 0) @@ -1585,6 +1647,9 @@ void journal_file_dump(JournalFile *f) { break; } + if (o->object.flags & OBJECT_COMPRESSED) + printf("Flags: COMPRESSED\n"); + if (p == le64toh(f->header->tail_object_offset)) p = 0; else diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 20712b5dcc..421dfa6766 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -75,6 +75,13 @@ typedef struct JournalFile { uint64_t current_offset; JournalMetrics metrics; + + bool compress; + +#ifdef HAVE_XZ + void *compress_buffer; + size_t compress_buffer_size; +#endif } JournalFile; typedef enum direction { diff --git a/src/journal/journald.c b/src/journal/journald.c index 37f8f16754..ca274ee44a 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -57,6 +57,7 @@ typedef struct Server { JournalMetrics metrics; uint64_t max_use; + bool compress; } Server; static void fix_perms(JournalFile *f, uid_t uid) { @@ -146,6 +147,8 @@ static JournalFile* find_journal(Server *s, uid_t uid) { return s->system_journal; fix_perms(f, uid); + f->metrics = s->metrics; + f->compress = s->compress; r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f); if (r < 0) { @@ -661,6 +664,9 @@ static int system_journal_open(Server *s) { free(fn); if (r >= 0) { + s->system_journal->metrics = s->metrics; + s->system_journal->compress = s->compress; + fix_perms(s->system_journal, 0); return r; } @@ -685,6 +691,9 @@ static int system_journal_open(Server *s) { return r; } + s->runtime_journal->metrics = s->metrics; + s->runtime_journal->compress = s->compress; + fix_perms(s->runtime_journal, 0); return r; } @@ -794,6 +803,7 @@ static int server_init(Server *s) { s->metrics.min_size = DEFAULT_MIN_SIZE; s->metrics.keep_free = DEFAULT_KEEP_FREE; s->max_use = DEFAULT_MAX_USE; + s->compress = true; s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (s->epoll_fd < 0) { @@ -931,6 +941,7 @@ int main(int argc, char *argv[]) { } log_set_target(LOG_TARGET_CONSOLE); + log_set_max_level(LOG_DEBUG); log_parse_environment(); log_open(); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 9dff72429b..bc575b43ef 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -31,6 +31,7 @@ #include "hashmap.h" #include "list.h" #include "lookup3.h" +#include "compress.h" #define JOURNAL_FILES_MAX 1024 @@ -1344,7 +1345,7 @@ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, siz size_t t; p = le64toh(o->entry.items[i].object_offset); - le_hash = o->entry.items[j->current_field].hash; + le_hash = o->entry.items[i].hash; r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (r < 0) return r; @@ -1354,9 +1355,31 @@ int sd_journal_get_data(sd_journal *j, const char *field, const void **data, siz l = le64toh(o->object.size) - offsetof(Object, data.payload); - if (l >= field_length+1 && - memcmp(o->data.payload, field, field_length) == 0 && - o->data.payload[field_length] == '=') { + if (o->object.flags & OBJECT_COMPRESSED) { + +#ifdef HAVE_XZ + if (uncompress_startswith(o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, + field, field_length, '=')) { + + uint64_t rsize; + + if (!uncompress_blob(o->data.payload, l, + &f->compress_buffer, &f->compress_buffer_size, &rsize)) + return -EBADMSG; + + *data = f->compress_buffer; + *size = (size_t) rsize; + + return 0; + } +#else + return -EPROTONOSUPPORT; +#endif + + } else if (l >= field_length+1 && + memcmp(o->data.payload, field, field_length) == 0 && + o->data.payload[field_length] == '=') { t = (size_t) l; @@ -1419,8 +1442,22 @@ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *size) { if ((uint64_t) t != l) return -E2BIG; - *data = o->data.payload; - *size = t; + if (o->object.flags & OBJECT_COMPRESSED) { +#ifdef HAVE_XZ + uint64_t rsize; + + if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize)) + return -EBADMSG; + + *data = f->compress_buffer; + *size = (size_t) rsize; +#else + return -EPROTONOSUPPORT; +#endif + } else { + *data = o->data.payload; + *size = t; + } j->current_field ++; diff --git a/src/journal/sd-journal.h b/src/journal/sd-journal.h index ee9813f28c..b29680b3a2 100644 --- a/src/journal/sd-journal.h +++ b/src/journal/sd-journal.h @@ -38,7 +38,7 @@ * - accelerate looking for "all hostnames" and suchlike. * - throttling * - cryptographic hash - * - compression + * - never access beyond fle size check */ /* Write to daemon */ -- cgit v1.2.3-54-g00ecf From 0d43c6944bbca30d5692d4b02885f007a0c630c8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Dec 2011 18:59:56 +0100 Subject: journalctl: add command line parsing --- Makefile.am | 3 +- src/journal/journalctl.c | 125 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 5 deletions(-) (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index 7d551a9dcf..81b5c50199 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1069,7 +1069,8 @@ systemd_journalctl_SOURCES = \ src/journal/sd-journal.c \ src/journal/journal-file.c \ src/journal/lookup3.c \ - src/sd-id128.c + src/sd-id128.c \ + src/pager.c systemd_journalctl_CFLAGS = \ $(AM_CFLAGS) diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 6f4342e597..5a1cb6e88a 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -28,10 +28,13 @@ #include #include #include +#include #include "sd-journal.h" #include "log.h" #include "util.h" +#include "build.h" +#include "pager.h" #define PRINT_THRESHOLD 128 @@ -41,10 +44,11 @@ static enum { OUTPUT_EXPORT, OUTPUT_JSON, _OUTPUT_MAX -} arg_output = OUTPUT_JSON; +} arg_output = OUTPUT_SHORT; static bool arg_follow = false; static bool arg_show_all = false; +static bool arg_no_pager = false; static bool contains_unprintable(const void *p, size_t l) { const char *j; @@ -345,6 +349,96 @@ static int (*output_funcs[_OUTPUT_MAX])(sd_journal*j, unsigned line) = { [OUTPUT_JSON] = output_json }; +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Send control commands to or query the login manager.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " -a --all Show all properties, including long and unprintable\n" + " -f --follow Follow journal\n" + " -o --output=STRING Change output mode (short, verbose, export, json)\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "follow", no_argument, NULL, 'f' }, + { "output", required_argument, NULL, 'o' }, + { "all", no_argument, NULL, 'a' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hfo:a", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case 'f': + arg_follow = true; + break; + + case 'o': + if (streq(optarg, "short")) + arg_output = OUTPUT_SHORT; + else if (streq(optarg, "verbose")) + arg_output = OUTPUT_VERBOSE; + else if (streq(optarg, "export")) + arg_output = OUTPUT_EXPORT; + else if (streq(optarg, "json")) + arg_output = OUTPUT_JSON; + else { + log_error("Unknown output '%s'.", optarg); + return -EINVAL; + } + break; + + case 'a': + arg_show_all = true; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + int main(int argc, char *argv[]) { int r, i, fd; sd_journal *j = NULL; @@ -356,13 +450,17 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + r = sd_journal_open(&j); if (r < 0) { log_error("Failed to open journal: %s", strerror(-r)); goto finish; } - for (i = 1; i < argc; i++) { + for (i = optind; i < argc; i++) { r = sd_journal_add_match(j, argv[i], strlen(argv[i])); if (r < 0) { log_error("Failed to add match: %s", strerror(-r)); @@ -382,13 +480,30 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_output == OUTPUT_JSON) + if (!arg_no_pager && !arg_follow) { + columns(); + pager_open(); + } + + if (arg_output == OUTPUT_JSON) { fputc('[', stdout); + fflush(stdout); + } for (;;) { struct pollfd pollfd; - while (sd_journal_next(j) > 0) { + for (;;) { + r = sd_journal_next(j); + + if (r < 0) { + log_error("Failed to iterate through journal: %s", strerror(-r)); + goto finish; + } + + if (r == 0) + break; + line ++; r = output_funcs[arg_output](j, line); @@ -426,5 +541,7 @@ finish: if (j) sd_journal_close(j); + pager_close(); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } -- cgit v1.2.3-54-g00ecf From 6e409ce10d134625626d1eddfd6152755ef1908d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 27 Dec 2011 22:51:46 +0100 Subject: journald: implement sophisticated rate limiting --- Makefile.am | 1 + TODO | 2 + src/journal/journal-rate-limit.c | 273 +++++++++++++++++++++++++++++++++++++++ src/journal/journal-rate-limit.h | 34 +++++ src/journal/journald.c | 213 ++++++++++++++++++++++++++++-- src/journal/sd-journal.h | 1 + 6 files changed, 512 insertions(+), 12 deletions(-) create mode 100644 src/journal/journal-rate-limit.c create mode 100644 src/journal/journal-rate-limit.h (limited to 'Makefile.am') diff --git a/Makefile.am b/Makefile.am index 81b5c50199..5fe67fd1c7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1042,6 +1042,7 @@ systemd_journald_SOURCES = \ src/journal/sd-journal.c \ src/journal/journal-file.c \ src/journal/lookup3.c \ + src/journal/journal-rate-limit.c \ src/sd-id128.c \ src/acl-util.c \ src/cgroup-util.c diff --git a/TODO b/TODO index 8daf79a7f0..02688ad9a8 100644 --- a/TODO +++ b/TODO @@ -21,6 +21,8 @@ Bugfixes: Features: +* logind: selinux is borked... + * logind: sends SessionNew on Lock()? * logind: allow showing logout dialog from system diff --git a/src/journal/journal-rate-limit.c b/src/journal/journal-rate-limit.c new file mode 100644 index 0000000000..f69ab2770f --- /dev/null +++ b/src/journal/journal-rate-limit.c @@ -0,0 +1,273 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "journal-rate-limit.h" +#include "list.h" +#include "util.h" +#include "hashmap.h" + +#define POOLS_MAX 5 +#define BUCKETS_MAX 127 +#define GROUPS_MAX 2047 + +static const int priority_map[] = { + [LOG_EMERG] = 0, + [LOG_ALERT] = 0, + [LOG_CRIT] = 0, + [LOG_ERR] = 1, + [LOG_WARNING] = 2, + [LOG_NOTICE] = 3, + [LOG_INFO] = 3, + [LOG_DEBUG] = 4 +}; + +typedef struct JournalRateLimitPool JournalRateLimitPool; +typedef struct JournalRateLimitGroup JournalRateLimitGroup; + +struct JournalRateLimitPool { + usec_t begin; + unsigned num; + unsigned suppressed; +}; + +struct JournalRateLimitGroup { + JournalRateLimit *parent; + + char *id; + JournalRateLimitPool pools[POOLS_MAX]; + unsigned hash; + + LIST_FIELDS(JournalRateLimitGroup, bucket); + LIST_FIELDS(JournalRateLimitGroup, lru); +}; + +struct JournalRateLimit { + usec_t interval; + unsigned burst; + + JournalRateLimitGroup* buckets[BUCKETS_MAX]; + JournalRateLimitGroup *lru, *lru_tail; + + unsigned n_groups; +}; + +JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) { + JournalRateLimit *r; + + assert(interval > 0 || burst == 0); + + r = new0(JournalRateLimit, 1); + if (!r) + return NULL; + + r->interval = interval; + r->burst = burst; + + return r; +} + +static void journal_rate_limit_group_free(JournalRateLimitGroup *g) { + assert(g); + + if (g->parent) { + assert(g->parent->n_groups > 0); + + if (g->parent->lru_tail == g) + g->parent->lru_tail = g->lru_prev; + + LIST_REMOVE(JournalRateLimitGroup, lru, g->parent->lru, g); + LIST_REMOVE(JournalRateLimitGroup, bucket, g->parent->buckets[g->hash % BUCKETS_MAX], g); + + g->parent->n_groups --; + } + + free(g->id); + free(g); +} + +void journal_rate_limit_free(JournalRateLimit *r) { + assert(r); + + while (r->lru) + journal_rate_limit_group_free(r->lru); +} + +static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) { + unsigned i; + + assert(g); + + for (i = 0; i < POOLS_MAX; i++) + if (g->pools[i].begin + g->parent->interval >= ts) + return false; + + return true; +} + +static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) { + assert(r); + + /* Makes room for at least one new item, but drop all + * expored items too. */ + + while (r->n_groups >= GROUPS_MAX || + (r->lru_tail && journal_rate_limit_group_expired(r->lru_tail, ts))) + journal_rate_limit_group_free(r->lru_tail); +} + +static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) { + JournalRateLimitGroup *g; + + assert(r); + assert(id); + + g = new0(JournalRateLimitGroup, 1); + if (!g) + return NULL; + + g->id = strdup(id); + if (!g->id) + goto fail; + + g->hash = string_hash_func(g->id); + + journal_rate_limit_vacuum(r, ts); + + LIST_PREPEND(JournalRateLimitGroup, bucket, r->buckets[g->hash % BUCKETS_MAX], g); + LIST_PREPEND(JournalRateLimitGroup, lru, r->lru, g); + if (!g->lru_next) + r->lru_tail = g; + r->n_groups ++; + + g->parent = r; + return g; + +fail: + journal_rate_limit_group_free(g); + 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; + + /* Modulates the burst rate a bit with the amount of available + * disk space */ + + k = u64log2(available); + + /* 1MB */ + if (k <= 20) + return burst; + + burst = (burst * (k-20)) / 4; + + /* + * Example: + * + * <= 1MB = rate * 1 + * 16MB = rate * 2 + * 256MB = rate * 3 + * 4GB = rate * 4 + * 64GB = rate * 5 + * 1TB = rate * 6 + */ + + return burst; +} + +int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) { + unsigned h; + JournalRateLimitGroup *g; + JournalRateLimitPool *p; + unsigned burst; + usec_t ts; + + assert(id); + + if (!r) + return 1; + + if (r->interval == 0 || r->burst == 0) + return 1; + + burst = burst_modulate(r->burst, available); + + ts = now(CLOCK_MONOTONIC); + + h = string_hash_func(id); + g = r->buckets[h % BUCKETS_MAX]; + + LIST_FOREACH(bucket, g, g) + if (streq(g->id, id)) + break; + + if (!g) { + g = journal_rate_limit_group_new(r, id, ts); + if (!g) + return -ENOMEM; + } + + p = &g->pools[priority_map[priority]]; + + if (p->begin <= 0) { + p->suppressed = 0; + p->num = 1; + p->begin = ts; + return 1; + } + + if (p->begin + r->interval < ts) { + unsigned s; + + s = p->suppressed; + p->suppressed = 0; + p->num = 1; + p->begin = ts; + + return 1 + s; + } + + if (p->num <= burst) { + p->num++; + return 1; + } + + p->suppressed++; + return 0; +} diff --git a/src/journal/journal-rate-limit.h b/src/journal/journal-rate-limit.h new file mode 100644 index 0000000000..2bbdd5f9fe --- /dev/null +++ b/src/journal/journal-rate-limit.h @@ -0,0 +1,34 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalratelimithfoo +#define foojournalratelimithfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include "macro.h" +#include "util.h" + +typedef struct JournalRateLimit JournalRateLimit; + +JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst); +void journal_rate_limit_free(JournalRateLimit *r); +int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available); + +#endif diff --git a/src/journal/journald.c b/src/journal/journald.c index 5d3956ea46..9f753013a0 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "hashmap.h" #include "journal-file.h" @@ -38,6 +39,7 @@ #include "acl-util.h" #include "cgroup-util.h" #include "list.h" +#include "journal-rate-limit.h" #define USER_JOURNALS_MAX 1024 #define STDOUT_STREAMS_MAX 4096 @@ -60,6 +62,8 @@ typedef struct Server { char *buffer; size_t buffer_size; + JournalRateLimit *rate_limit; + JournalMetrics metrics; uint64_t max_use; bool compress; @@ -95,6 +99,76 @@ struct StdoutStream { LIST_FIELDS(StdoutStream, stdout_stream); }; +static uint64_t available_space(Server *s) { + char ids[33]; + sd_id128_t machine; + char *p; + const char *f; + struct statvfs ss; + uint64_t sum = 0, avail = 0, ss_avail = 0; + int r; + DIR *d; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return 0; + + if (s->system_journal) + f = "/var/log/journal/"; + else + f = "/run/log/journal/"; + + p = strappend(f, sd_id128_to_string(machine, ids)); + if (!p) + return 0; + + d = opendir(p); + free(p); + + if (!d) + return 0; + + if (fstatvfs(dirfd(d), &ss) < 0) + goto finish; + + for (;;) { + struct stat st; + struct dirent buf, *de; + int k; + + k = readdir_r(d, &buf, &de); + if (k != 0) { + r = -k; + goto finish; + } + + if (!de) + break; + + if (!dirent_is_file_with_suffix(de, ".journal")) + continue; + + if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + continue; + + sum += (uint64_t) st.st_blocks * (uint64_t) st.st_blksize; + } + + avail = sum >= s->max_use ? 0 : s->max_use - sum; + + ss_avail = ss.f_bsize * ss.f_bavail; + + ss_avail = ss_avail < s->metrics.keep_free ? 0 : ss_avail - s->metrics.keep_free; + + if (ss_avail < avail) + avail = ss_avail; + +finish: + closedir(d); + + return avail; +} + static void fix_perms(JournalFile *f, uid_t uid) { acl_t acl; acl_entry_t entry; @@ -254,7 +328,40 @@ static void server_vacuum(Server *s) { free(p); } -static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv) { +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 (streq(init_path, "/")) + init_path[0] = 0; + + if (startswith(process_path, init_path)) + path = process_path + strlen(init_path); + else + path = process_path; + + free(init_path); + + return path; +} + +static void dispatch_message_real(Server *s, + struct iovec *iovec, unsigned n, unsigned m, + struct ucred *ucred, + struct timeval *tv) { + char *pid = NULL, *uid = NULL, *gid = NULL, *source_time = NULL, *boot_id = NULL, *machine_id = NULL, *comm = NULL, *cmdline = NULL, *hostname = NULL, @@ -270,11 +377,8 @@ static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne bool vacuumed = false; assert(s); - assert(iovec || n == 0); - - if (n == 0) - return; - + assert(iovec); + assert(n > 0); assert(n + 13 <= m); if (ucred) { @@ -326,11 +430,12 @@ static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne if (asprintf(&audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0) IOVEC_SET_STRING(iovec[n++], audit_loginuid); - r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, ucred->pid, &path); - if (r >= 0) { + path = shortened_cgroup_path(ucred->pid); + if (path) { cgroup = strappend("_SYSTEMD_CGROUP=", path); if (cgroup) IOVEC_SET_STRING(iovec[n++], cgroup); + free(path); } } @@ -400,6 +505,72 @@ retry: free(cgroup); } +static void dispatch_message(Server *s, + struct iovec *iovec, unsigned n, unsigned m, + struct ucred *ucred, + struct timeval *tv, + int priority) { + int rl; + char *path, *c; + + assert(s); + assert(iovec || n == 0); + + if (n == 0) + return; + + if (!ucred) + goto finish; + + path = shortened_cgroup_path(ucred->pid); + if (!path) + goto finish; + + /* example: /user/lennart/3/foobar + * /system/dbus.service/foobar + * + * So let's cut of everything past the third /, since that is + * wher user directories start */ + + c = strchr(path, '/'); + if (c) { + c = strchr(c+1, '/'); + if (c) { + c = strchr(c+1, '/'); + if (c) + *c = 0; + } + } + + rl = journal_rate_limit_test(s->rate_limit, path, priority, available_space(s)); + + if (rl == 0) { + free(path); + return; + } + + if (rl > 1) { + int j = 0; + char suppress_message[LINE_MAX]; + struct iovec suppress_iovec[15]; + + /* Write a suppression message if we suppressed something */ + + snprintf(suppress_message, sizeof(suppress_message), "MESSAGE=Suppressed %u messages from %s", rl - 1, path); + char_array_0(suppress_message); + + IOVEC_SET_STRING(suppress_iovec[j++], "PRIORITY=5"); + IOVEC_SET_STRING(suppress_iovec[j++], suppress_message); + + dispatch_message_real(s, suppress_iovec, j, ELEMENTSOF(suppress_iovec), NULL, NULL); + } + + free(path); + +finish: + dispatch_message_real(s, iovec, n, m, ucred, tv); +} + static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL; struct iovec iovec[16]; @@ -422,7 +593,7 @@ static void process_syslog_message(Server *s, const char *buf, struct ucred *ucr if (message) IOVEC_SET_STRING(iovec[n++], message); - dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv); + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, priority & LOG_PRIMASK); free(message); free(syslog_facility); @@ -469,6 +640,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ unsigned n = 0, m = 0, j; const char *p; size_t remaining; + int priority = LOG_INFO; assert(s); assert(buffer || n == 0); @@ -489,8 +661,9 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ if (e == p) { /* Entry separator */ - dispatch_message(s, iovec, n, m, ucred, tv); + dispatch_message(s, iovec, n, m, ucred, tv, priority); n = 0; + priority = LOG_INFO; p++; remaining--; @@ -532,6 +705,15 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ iovec[n].iov_base = (char*) p; iovec[n].iov_len = e - p; n++; + + /* We need to determine the priority + * of this entry for the rate limiting + * logic */ + if (e - p == 10 && + memcmp(p, "PRIORITY=", 10) == 0 && + p[10] >= '0' && + p[10] <= '9') + priority = p[10] - '0'; } remaining -= (e - p) + 1; @@ -577,7 +759,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ } } - dispatch_message(s, iovec, n, m, ucred, tv); + dispatch_message(s, iovec, n, m, ucred, tv, priority); for (j = 0; j < n; j++) if (iovec[j].iov_base < buffer || @@ -630,7 +812,7 @@ static int stdout_stream_log(StdoutStream *s, const char *p, size_t l) { n++; } - dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL); + dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, priority); if (s->tee_console) { int console; @@ -1378,6 +1560,10 @@ static int server_init(Server *s) { if (r < 0) return r; + s->rate_limit = journal_rate_limit_new(10*USEC_PER_SEC, 2); + if (!s->rate_limit) + return -ENOMEM; + return 0; } @@ -1413,6 +1599,9 @@ static void server_done(Server *s) { if (s->stdout_fd >= 0) close_nointr_nofail(s->stdout_fd); + + if (s->rate_limit) + journal_rate_limit_free(s->rate_limit); } int main(int argc, char *argv[]) { diff --git a/src/journal/sd-journal.h b/src/journal/sd-journal.h index 0333db4a4d..7e2ef15327 100644 --- a/src/journal/sd-journal.h +++ b/src/journal/sd-journal.h @@ -38,6 +38,7 @@ * - throttling * - cryptographic hash * - never access beyond fle size check + * - OR of matches is borked... */ /* Write to daemon */ -- cgit v1.2.3-54-g00ecf From 4b2d99d9f4258a29f0bf8b1a78d17836e75bc378 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 30 Dec 2011 17:50:37 +0100 Subject: journal: add unit files and shared library glue --- .gitignore | 2 + Makefile.am | 108 +++++++++++++++++++++++++++++++++++--- libsystemd-id128.pc.in | 18 +++++++ libsystemd-journal.pc.in | 19 +++++++ src/libsystemd-id128.sym | 22 ++++++++ src/libsystemd-journal.sym | 45 ++++++++++++++++ units/.gitignore | 1 + units/systemd-journald.service.in | 24 +++++++++ units/systemd-journald.socket | 24 +++++++++ 9 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 libsystemd-id128.pc.in create mode 100644 libsystemd-journal.pc.in create mode 100644 src/libsystemd-id128.sym create mode 100644 src/libsystemd-journal.sym create mode 100644 units/systemd-journald.service.in create mode 100644 units/systemd-journald.socket (limited to 'Makefile.am') diff --git a/.gitignore b/.gitignore index 265801ff5f..28b40de7db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/libsystemd-journal.pc +/libsystemd-id128.pc systemd-journalctl systemd-journald test-id128 diff --git a/Makefile.am b/Makefile.am index 5fe67fd1c7..fa0a217813 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,14 @@ LIBSYSTEMD_DAEMON_CURRENT=0 LIBSYSTEMD_DAEMON_REVISION=0 LIBSYSTEMD_DAEMON_AGE=0 +LIBSYSTEMD_ID128_CURRENT=0 +LIBSYSTEMD_ID128_REVISION=0 +LIBSYSTEMD_ID128_AGE=0 + +LIBSYSTEMD_JOURNAL_CURRENT=0 +LIBSYSTEMD_JOURNAL_REVISION=0 +LIBSYSTEMD_JOURNAL_AGE=0 + # Dirs of external packages dbuspolicydir=@dbuspolicydir@ dbussessionservicedir=@dbussessionservicedir@ @@ -217,11 +225,15 @@ endif lib_LTLIBRARIES = \ libsystemd-daemon.la \ - libsystemd-login.la + libsystemd-login.la \ + libsystemd-id128.la \ + libsystemd-journal.la pkginclude_HEADERS = \ src/sd-daemon.h \ - src/sd-login.h + src/sd-login.h \ + src/sd-id128.h \ + src/journal/sd-journal.h noinst_PROGRAMS = \ test-engine \ @@ -389,7 +401,8 @@ dist_systemunit_DATA = \ units/quotaon.service \ units/systemd-ask-password-wall.path \ units/systemd-ask-password-console.path \ - units/syslog.target + units/syslog.target \ + units/systemd-journald.socket if HAVE_SYSV_COMPAT dist_systemunit_DATA += \ @@ -410,6 +423,7 @@ nodist_systemunit_DATA = \ units/systemd-stdout-syslog-bridge.service \ units/systemd-shutdownd.service \ units/systemd-logind.service \ + units/systemd-journald.service \ units/systemd-kmsg-syslogd.service \ units/systemd-modules-load.service \ units/systemd-vconsole-setup.service \ @@ -473,7 +487,8 @@ EXTRA_DIST = \ units/systemd-stdout-syslog-bridge.service.in \ units/systemd-shutdownd.service.in \ units/systemd-logind.service.in \ - units/systemd-kmsg-syslogd.service.in \ + units/systemd-journald.service.in \ + units/systemd-kmsg-syslogd.service.in \ units/systemd-modules-load.service.in \ units/systemd-vconsole-setup.service.in \ units/systemd-remount-api-vfs.service.in \ @@ -502,8 +517,12 @@ EXTRA_DIST = \ systemd.pc.in \ libsystemd-daemon.pc.in \ libsystemd-login.pc.in \ + libsystemd-id128.pc.in \ + libsystemd-journal.pc.in \ src/libsystemd-daemon.sym \ src/libsystemd-login.sym \ + src/libsystemd-id128.sym \ + src/libsystemd-journal.sym \ introspect.awk \ src/73-seat-late.rules.in \ src/99-systemd.rules.in \ @@ -594,7 +613,9 @@ pkgconfigdata_DATA = \ pkgconfiglib_DATA = \ libsystemd-daemon.pc \ - libsystemd-login.pc + libsystemd-login.pc \ + libsystemd-id128.pc \ + libsystemd-journal.pc # Passed through intltool only polkitpolicy_in_files = \ @@ -770,7 +791,14 @@ EXTRA_DIST += \ src/logind-user.h \ src/logind-acl.h \ src/dbus-loop.h \ - src/spawn-agent.h + src/spawn-agent.h \ + src/journal/journal-def.h \ + src/journal/journal-internal.h \ + src/journal/journal-file.h \ + src/journal/lookup3.h \ + src/journal/compress.h \ + src/journal/journal-rate-limit.h \ + src/acl-util.h MANPAGES = \ man/systemd.1 \ @@ -1703,6 +1731,69 @@ libsystemd-login-install-hook: libsystemd-login-uninstall-hook: rm -f $(DESTDIR)$(rootlibdir)/libsystemd-login.so* +libsystemd_id128_la_SOURCES = \ + src/sd-id128.c + +libsystemd_id128_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=hidden + +libsystemd_id128_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_ID128_CURRENT):$(LIBSYSTEMD_ID128_REVISION):$(LIBSYSTEMD_ID128_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/libsystemd-id128.sym + +libsystemd_id128_la_LIBADD = \ + libsystemd-basic.la + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libsystemd-id128-install-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libsystemd-id128.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libsystemd-id128.so && \ + mv $(DESTDIR)$(libdir)/libsystemd-id128.so.* $(DESTDIR)$(rootlibdir); \ + fi + +libsystemd-id128-uninstall-hook: + rm -f $(DESTDIR)$(rootlibdir)/libsystemd-id128.so* + +libsystemd_journal_la_SOURCES = \ + src/journal/sd-journal.c \ + src/journal/journal-file.c \ + src/journal/compress.c \ + src/journal/lookup3.c \ + src/journal/journal-send.c + +libsystemd_journal_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(XZ_CFLAGS) \ + -fvisibility=hidden + +libsystemd_journal_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_JOURNAL_CURRENT):$(LIBSYSTEMD_JOURNAL_REVISION):$(LIBSYSTEMD_JOURNAL_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/libsystemd-journal.sym + +libsystemd_journal_la_LIBADD = \ + libsystemd-basic.la \ + libsystemd-id128.la \ + $(XZ_LIBS) + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libsystemd-journal-install-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libsystemd-journal.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libsystemd-journal.so && \ + mv $(DESTDIR)$(libdir)/libsystemd-journal.so.* $(DESTDIR)$(rootlibdir); \ + fi + +libsystemd-journal-uninstall-hook: + rm -f $(DESTDIR)$(rootlibdir)/libsystemd-journal.so* + SED_PROCESS = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ $(SED) -e 's,@rootlibexecdir\@,$(rootlibexecdir),g' \ @@ -1919,11 +2010,12 @@ endif rm -f user && \ $(LN_S) $(pkgsysconfdir)/user user ) ( cd $(DESTDIR)$(systemunitdir)/sockets.target.wants && \ - rm -f systemd-initctl.socket systemd-stdout-syslog-bridge.socket systemd-shutdownd.socket syslog.socket && \ + rm -f systemd-initctl.socket systemd-stdout-syslog-bridge.socket systemd-shutdownd.socket syslog.socket systemd-journald.socket && \ $(LN_S) ../systemd-stdout-syslog-bridge.socket systemd-stdout-syslog-bridge.socket && \ $(LN_S) ../systemd-initctl.socket systemd-initctl.socket && \ $(LN_S) ../systemd-shutdownd.socket systemd-shutdownd.socket && \ - $(LN_S) ../syslog.socket syslog.socket ) + $(LN_S) ../syslog.socket syslog.socket && \ + $(LN_S) ../systemd-journald.socket ) ( cd $(DESTDIR)$(systemunitdir)/runlevel1.target.wants && \ rm -f systemd-update-utmp-runlevel.service && \ $(LN_S) ../systemd-update-utmp-runlevel.service systemd-update-utmp-runlevel.service ) diff --git a/libsystemd-id128.pc.in b/libsystemd-id128.pc.in new file mode 100644 index 0000000000..4d984fdff5 --- /dev/null +++ b/libsystemd-id128.pc.in @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: systemd +Description: systemd 128 Bit ID Utility Library +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lsystemd-id128 +Cflags: -I${includedir} diff --git a/libsystemd-journal.pc.in b/libsystemd-journal.pc.in new file mode 100644 index 0000000000..13cc8208df --- /dev/null +++ b/libsystemd-journal.pc.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: systemd +Description: systemd Journal Utility Library +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Requires: libsystemd-id128 = @PACKAGE_VERSION@ +Libs: -L${libdir} -lsystemd-journal +Cflags: -I${includedir} diff --git a/src/libsystemd-id128.sym b/src/libsystemd-id128.sym new file mode 100644 index 0000000000..c4d1cf5d48 --- /dev/null +++ b/src/libsystemd-id128.sym @@ -0,0 +1,22 @@ +/*** + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +***/ + +/* Original symbols from systemd v38 */ + +LIBSYSTEMD_ID128_38 { +global: + sd_id128_to_string; + sd_id128_from_string; + sd_id128_randomize; + sd_id128_make_v4_uuid; + sd_id128_get_machine; + sd_id128_get_boot; +local: + *; +}; diff --git a/src/libsystemd-journal.sym b/src/libsystemd-journal.sym new file mode 100644 index 0000000000..7653880e8f --- /dev/null +++ b/src/libsystemd-journal.sym @@ -0,0 +1,45 @@ +/*** + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +***/ + +/* Original symbols from systemd v38 */ + +LIBSYSTEMD_JOURNAL_38 { +global: + sd_journal_print; + sd_journal_printv; + sd_journal_send; + sd_journal_sendv; + sd_journal_stream_fd; + sd_journal_open; + sd_journal_close; + sd_journal_previous; + sd_journal_next; + sd_journal_previous_skip; + sd_journal_next_skip; + sd_journal_get_realtime_usec; + sd_journal_get_monotonic_usec; + sd_journal_get_data; + sd_journal_enumerate_data; + sd_journal_restart_data; + sd_journal_add_match; + sd_journal_flush_matches; + sd_journal_seek_head; + sd_journal_seek_tail; + sd_journal_seek_monotonic_usec; + sd_journal_seek_realtime_usec; + sd_journal_seek_cursor; + sd_journal_get_cursor; + sd_journal_query_unique; + sd_journal_enumerate_unique; + sd_journal_restart_unique; + sd_journal_get_fd; + sd_journal_process; +local: + *; +}; diff --git a/units/.gitignore b/units/.gitignore index cc92c73022..94412d52e7 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +/systemd-journald.service user@.service systemd-logind.service systemd-localed.service diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in new file mode 100644 index 0000000000..2cfc68482e --- /dev/null +++ b/units/systemd-journald.service.in @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Journal Service +DefaultDependencies=no +Requires=systemd-journald.socket +After=systemd-journald.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-journald +NotifyAccess=all +StandardOutput=null +#CapabilityBoundingSet=CAP_SYS_ADMIN CAP_SETUID CAP_SETGID + +# Increase the default a bit in order to allow many simultaneous +# services being run since we keep one fd open per service. +LimitNOFILE=16384 diff --git a/units/systemd-journald.socket b/units/systemd-journald.socket new file mode 100644 index 0000000000..b439bc1fdc --- /dev/null +++ b/units/systemd-journald.socket @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Journal Socket +DefaultDependencies=no +Before=sockets.target syslog.target + +# Mount and swap units need this. If this socket unit is removed by an +# isolate request the mount and and swap units would be removed too, +# hence let's exclude this from isolate requests. +IgnoreOnIsolate=yes + +[Socket] +ListenStream=/run/systemd/stdout +ListenDatagram=/run/systemd/native +ListenDatagram=/dev/log +SocketMode=0666 -- cgit v1.2.3-54-g00ecf