diff options
Diffstat (limited to 'src/journal')
47 files changed, 1244 insertions, 950 deletions
diff --git a/src/journal/audit-type.c b/src/journal/audit-type.c index b30963da66..7b3dc1e9ab 100644 --- a/src/journal/audit-type.c +++ b/src/journal/audit-type.c @@ -1,12 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include <stdio.h> -#include <linux/audit.h> -#if HAVE_AUDIT -# include <libaudit.h> -#endif - -#include "missing.h" #include "audit-type.h" +#include "missing_audit.h" + #include "audit_type-to-name.h" -#include "macro.h" diff --git a/src/journal/audit-type.h b/src/journal/audit-type.h index 965b044cbf..069a883490 100644 --- a/src/journal/audit-type.h +++ b/src/journal/audit-type.h @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <alloca.h> +#include <stdio.h> + #include "macro.h" const char *audit_type_to_string(int type); diff --git a/src/journal/cat.c b/src/journal/cat.c index 8a11c8c3d1..a84350fbc9 100644 --- a/src/journal/cat.c +++ b/src/journal/cat.c @@ -9,8 +9,11 @@ #include "sd-journal.h" +#include "alloc-util.h" #include "fd-util.h" +#include "main-func.h" #include "parse-util.h" +#include "pretty-print.h" #include "string-util.h" #include "syslog-util.h" #include "util.h" @@ -19,7 +22,14 @@ static const char *arg_identifier = NULL; static int arg_priority = LOG_INFO; static bool arg_level_prefix = true; -static void help(void) { +static int help(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("systemd-cat", "1", &link); + if (r < 0) + return log_oom(); + printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Execute process with stdout/stderr connected to the journal.\n\n" " -h --help Show this help\n" @@ -27,7 +37,12 @@ static void help(void) { " -t --identifier=STRING Set syslog identifier\n" " -p --priority=PRIORITY Set priority value (0..7)\n" " --level-prefix=BOOL Control whether level prefix shall be parsed\n" - , program_invocation_short_name); + "\nSee the %s for details.\n" + , program_invocation_short_name + , link + ); + + return 0; } static int parse_argv(int argc, char *argv[]) { @@ -71,10 +86,9 @@ static int parse_argv(int argc, char *argv[]) { case 'p': arg_priority = log_level_from_string(optarg); - if (arg_priority < 0) { - log_error("Failed to parse priority value."); - return -EINVAL; - } + if (arg_priority < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse priority value."); break; case ARG_LEVEL_PREFIX: { @@ -98,7 +112,7 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { _cleanup_close_ int fd = -1, saved_stderr = -1; int r; @@ -107,22 +121,18 @@ int main(int argc, char *argv[]) { r = parse_argv(argc, argv); if (r <= 0) - goto finish; + return r; fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix); - if (fd < 0) { - r = log_error_errno(fd, "Failed to create stream fd: %m"); - goto finish; - } + if (fd < 0) + return log_error_errno(fd, "Failed to create stream fd: %m"); saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); r = rearrange_stdio(STDIN_FILENO, fd, fd); /* Invalidates fd on succcess + error! */ - fd = -1; - if (r < 0) { - log_error_errno(r, "Failed to rearrange stdout/stderr: %m"); - goto finish; - } + TAKE_FD(fd); + if (r < 0) + return log_error_errno(r, "Failed to rearrange stdout/stderr: %m"); if (argc <= optind) (void) execl("/bin/cat", "/bin/cat", NULL); @@ -134,8 +144,7 @@ int main(int argc, char *argv[]) { if (saved_stderr >= 0) (void) dup3(saved_stderr, STDERR_FILENO, 0); - log_error_errno(r, "Failed to execute process: %m"); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + return log_error_errno(r, "Failed to execute process: %m"); } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/journal/catalog.c b/src/journal/catalog.c index f9118f0b62..3556a101bf 100644 --- a/src/journal/catalog.c +++ b/src/journal/catalog.c @@ -24,6 +24,7 @@ #include "strbuf.h" #include "string-util.h" #include "strv.h" +#include "tmpfile-util.h" #include "util.h" const char * const catalog_file_dirs[] = { @@ -49,31 +50,25 @@ typedef struct CatalogItem { le64_t offset; } CatalogItem; -static void catalog_hash_func(const void *p, struct siphash *state) { - const CatalogItem *i = p; - +static void catalog_hash_func(const CatalogItem *i, struct siphash *state) { siphash24_compress(&i->id, sizeof(i->id), state); siphash24_compress(i->language, strlen(i->language), state); } -static int catalog_compare_func(const void *a, const void *b) { - const CatalogItem *i = a, *j = b; +static int catalog_compare_func(const CatalogItem *a, const CatalogItem *b) { unsigned k; + int r; - for (k = 0; k < ELEMENTSOF(j->id.bytes); k++) { - if (i->id.bytes[k] < j->id.bytes[k]) - return -1; - if (i->id.bytes[k] > j->id.bytes[k]) - return 1; + for (k = 0; k < ELEMENTSOF(b->id.bytes); k++) { + r = CMP(a->id.bytes[k], b->id.bytes[k]); + if (r != 0) + return r; } - return strcmp(i->language, j->language); + return strcmp(a->language, b->language); } -const struct hash_ops catalog_hash_ops = { - .hash = catalog_hash_func, - .compare = catalog_compare_func -}; +DEFINE_HASH_OPS(catalog_hash_ops, CatalogItem, catalog_hash_func, catalog_compare_func); static bool next_header(const char **s) { const char *e; @@ -218,14 +213,14 @@ static int catalog_entry_lang(const char* filename, int line, size_t c; c = strlen(t); - if (c == 0) { - log_error("[%s:%u] Language too short.", filename, line); - return -EINVAL; - } - if (c > 31) { - log_error("[%s:%u] language too long.", filename, line); - return -EINVAL; - } + if (c < 2) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Language too short.", + filename, line); + if (c > 31) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] language too long.", filename, + line); if (deflang) { if (streq(t, deflang)) { @@ -268,26 +263,23 @@ int catalog_import_file(Hashmap *h, const char *path) { log_debug("File %s has language %s.", path, deflang); for (;;) { - char line[LINE_MAX]; + _cleanup_free_ char *line = NULL; size_t line_len; - if (!fgets(line, sizeof(line), f)) { - if (feof(f)) - break; - - return log_error_errno(errno, "Failed to read file %s: %m", path); - } + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return log_error_errno(r, "Failed to read file %s: %m", path); + if (r == 0) + break; n++; - truncate_nl(line); - - if (line[0] == 0) { + if (isempty(line)) { empty_line = true; continue; } - if (strchr(COMMENTS "\n", line[0])) + if (strchr(COMMENTS, line[0])) continue; if (empty_line && @@ -308,10 +300,11 @@ int catalog_import_file(Hashmap *h, const char *path) { if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) { if (got_id) { - if (payload_size == 0) { - log_error("[%s:%u] No payload text.", path, n); - return -EINVAL; - } + if (payload_size == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] No payload text.", + path, + n); r = finish_item(h, id, lang ?: deflang, payload, payload_size); if (r < 0) @@ -339,10 +332,10 @@ int catalog_import_file(Hashmap *h, const char *path) { } /* Payload */ - if (!got_id) { - log_error("[%s:%u] Got payload before ID.", path, n); - return -EINVAL; - } + if (!got_id) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Got payload before ID.", + path, n); line_len = strlen(line); if (!GREEDY_REALLOC(payload, payload_allocated, @@ -360,10 +353,10 @@ int catalog_import_file(Hashmap *h, const char *path) { } if (got_id) { - if (payload_size == 0) { - log_error("[%s:%u] No payload text.", path, n); - return -EINVAL; - } + if (payload_size == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] No payload text.", + path, n); r = finish_item(h, id, lang ?: deflang, payload, payload_size); if (r < 0) @@ -495,7 +488,7 @@ int catalog_update(const char* database, const char* root, const char* const* di } assert(n == hashmap_size(h)); - qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func); + typesafe_qsort(items, n, catalog_compare_func); strbuf_complete(sb); @@ -567,21 +560,21 @@ static const char *find_id(void *p, sd_id128_t id) { strncpy(key.language, loc, sizeof(key.language)); key.language[strcspn(key.language, ".@")] = 0; - f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func); if (!f) { char *e; e = strchr(key.language, '_'); if (e) { *e = 0; - f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func); } } } if (!f) { zero(key.language); - f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), catalog_compare_func); + f = bsearch(&key, (const uint8_t*) p + le64toh(h->header_size), le64toh(h->n_items), le64toh(h->catalog_item_size), (comparison_fn_t) catalog_compare_func); } if (!f) diff --git a/src/journal/compress.c b/src/journal/compress.c index 6baf15c8ff..e95ce2bcaa 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -95,11 +95,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size, if (src_size < 9) return -ENOBUFS; -#if LZ4_VERSION_NUMBER >= 10700 r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); -#else - r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); -#endif if (r <= 0) return -ENOBUFS; @@ -294,7 +290,6 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size, * prefix */ int r; - size_t size; assert(src); assert(src_size > 0); @@ -311,23 +306,37 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size, r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8, prefix_len + 1, *buffer_size); - if (r >= 0) - size = (unsigned) r; - else { - /* lz4 always tries to decode full "sequence", so in - * pathological cases might need to decompress the - * full field. */ + /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where + * just a part of the buffer is decompressed. But if we get a smaller + * amount of bytes than requested, we don't know whether there isn't enough + * data to fill the requested size or whether we just got a partial answer. + */ + if (r < 0 || (size_t) r < prefix_len + 1) { + size_t size; + + if (LZ4_versionNumber() >= 10803) + /* We trust that the newer lz4 decompresses the number of bytes we + * requested if available in the compressed string. */ + return 0; + + if (r > 0) + /* Compare what we have first, in case of mismatch we can + * shortcut the full comparison. */ + if (memcmp(*buffer, prefix, r) != 0) + return 0; + + /* Before version 1.8.3, lz4 always tries to decode full a "sequence", + * so in pathological cases might need to decompress the full field. */ r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0); if (r < 0) return r; - } - if (size >= prefix_len + 1) - return memcmp(*buffer, prefix, prefix_len) == 0 && - ((const uint8_t*) *buffer)[prefix_len] == extra; - else - return 0; + if (size < prefix_len + 1) + return 0; + } + return memcmp(*buffer, prefix, prefix_len) == 0 && + ((const uint8_t*) *buffer)[prefix_len] == extra; #else return -EPROTONOSUPPORT; #endif diff --git a/src/journal/fsprg.h b/src/journal/fsprg.h index bf78c3e9c3..33412675b8 100644 --- a/src/journal/fsprg.h +++ b/src/journal/fsprg.h @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ - -#ifndef __fsprgh__ -#define __fsprgh__ +#pragma once /* * fsprg v0.1 - (seekable) forward-secure pseudorandom generator @@ -22,7 +20,6 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA - * */ #include <inttypes.h> @@ -63,5 +60,3 @@ void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx); #ifdef __cplusplus } #endif - -#endif diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h index 43f70c861a..e48260206f 100644 --- a/src/journal/journal-def.h +++ b/src/journal/journal-def.h @@ -10,7 +10,6 @@ * If you change this file you probably should also change its documentation: * * http://www.freedesktop.org/wiki/Software/systemd/journal-files - * */ typedef struct Header Header; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 62e7f68a13..56827f9f36 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -10,6 +10,8 @@ #include <sys/uio.h> #include <unistd.h> +#include "sd-event.h" + #include "alloc-util.h" #include "btrfs-util.h" #include "chattr-util.h" @@ -23,7 +25,6 @@ #include "parse-util.h" #include "path-util.h" #include "random-util.h" -#include "sd-event.h" #include "set.h" #include "stat-util.h" #include "string-util.h" @@ -235,8 +236,7 @@ int journal_file_set_offline(JournalFile *f, bool wait) { sigset_t ss, saved_ss; int k; - if (sigfillset(&ss) < 0) - return -errno; + assert_se(sigfillset(&ss) >= 0); r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); if (r > 0) @@ -349,11 +349,8 @@ JournalFile* journal_file_close(JournalFile *f) { #endif if (f->post_change_timer) { - int enabled; - - if (sd_event_source_get_enabled(f->post_change_timer, &enabled) >= 0) - if (enabled == SD_EVENT_ONESHOT) - journal_file_post_change(f); + if (sd_event_source_get_enabled(f->post_change_timer, NULL) > 0) + journal_file_post_change(f); (void) sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_OFF); sd_event_source_unref(f->post_change_timer); @@ -372,7 +369,7 @@ JournalFile* journal_file_close(JournalFile *f) { * reenable all the good bits COW usually provides * (such as data checksumming). */ - (void) chattr_fd(f->fd, 0, FS_NOCOW_FL); + (void) chattr_fd(f->fd, 0, FS_NOCOW_FL, NULL); (void) btrfs_defrag_fd(f->fd); } @@ -568,13 +565,14 @@ static int journal_file_verify_header(JournalFile *f) { if (state == STATE_ARCHIVED) return -ESHUTDOWN; /* Already archived */ - else if (state == STATE_ONLINE) { - log_debug("Journal file %s is already online. Assuming unclean closing.", f->path); - return -EBUSY; - } else if (state != STATE_OFFLINE) { - log_debug("Journal file %s has unknown state %i.", f->path, state); - return -EBUSY; - } + else if (state == STATE_ONLINE) + return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), + "Journal file %s is already online. Assuming unclean closing.", + f->path); + else if (state != STATE_OFFLINE) + return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), + "Journal file %s has unknown state %i.", + f->path, state); if (f->header->field_hash_table_size == 0 || f->header->data_hash_table_size == 0) return -EBADMSG; @@ -582,10 +580,10 @@ static int journal_file_verify_header(JournalFile *f) { /* Don't permit appending to files from the future. Because otherwise the realtime timestamps wouldn't * be strictly ordered in the entries in the file anymore, and we can't have that since it breaks * bisection. */ - if (le64toh(f->header->tail_entry_realtime) > now(CLOCK_REALTIME)) { - log_debug("Journal file %s is from the future, refusing to append new data to it that'd be older.", f->path); - return -ETXTBSY; - } + if (le64toh(f->header->tail_entry_realtime) > now(CLOCK_REALTIME)) + return log_debug_errno(SYNTHETIC_ERRNO(ETXTBSY), + "Journal file %s is from the future, refusing to append new data to it that'd be older.", + f->path); } f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header); @@ -749,153 +747,124 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) switch (o->object.type) { case OBJECT_DATA: { - if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) { - log_debug("Bad n_entries: %"PRIu64": %"PRIu64, - le64toh(o->data.n_entries), offset); - return -EBADMSG; - } - - if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) { - log_debug("Bad object size (<= %zu): %"PRIu64": %"PRIu64, - offsetof(DataObject, payload), - le64toh(o->object.size), - offset); - return -EBADMSG; - } + if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Bad n_entries: %" PRIu64 ": %" PRIu64, + le64toh(o->data.n_entries), + offset); + + if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64, + offsetof(DataObject, payload), + le64toh(o->object.size), + offset); if (!VALID64(le64toh(o->data.next_hash_offset)) || !VALID64(le64toh(o->data.next_field_offset)) || !VALID64(le64toh(o->data.entry_offset)) || - !VALID64(le64toh(o->data.entry_array_offset))) { - log_debug("Invalid offset, next_hash_offset="OFSfmt", next_field_offset="OFSfmt - ", entry_offset="OFSfmt", entry_array_offset="OFSfmt": %"PRIu64, - le64toh(o->data.next_hash_offset), - le64toh(o->data.next_field_offset), - le64toh(o->data.entry_offset), - le64toh(o->data.entry_array_offset), - offset); - return -EBADMSG; - } + !VALID64(le64toh(o->data.entry_array_offset))) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid offset, next_hash_offset=" OFSfmt ", next_field_offset=" OFSfmt ", entry_offset=" OFSfmt ", entry_array_offset=" OFSfmt ": %" PRIu64, + le64toh(o->data.next_hash_offset), + le64toh(o->data.next_field_offset), + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + offset); break; } case OBJECT_FIELD: - if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) { - log_debug( - "Bad field size (<= %zu): %"PRIu64": %"PRIu64, - offsetof(FieldObject, payload), - le64toh(o->object.size), - offset); - return -EBADMSG; - } + if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64, + offsetof(FieldObject, payload), + le64toh(o->object.size), + offset); if (!VALID64(le64toh(o->field.next_hash_offset)) || - !VALID64(le64toh(o->field.head_data_offset))) { - log_debug( - "Invalid offset, next_hash_offset="OFSfmt - ", head_data_offset="OFSfmt": %"PRIu64, - le64toh(o->field.next_hash_offset), - le64toh(o->field.head_data_offset), - offset); - return -EBADMSG; - } + !VALID64(le64toh(o->field.head_data_offset))) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid offset, next_hash_offset=" OFSfmt ", head_data_offset=" OFSfmt ": %" PRIu64, + le64toh(o->field.next_hash_offset), + le64toh(o->field.head_data_offset), + offset); break; case OBJECT_ENTRY: - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) { - log_debug( - "Bad entry size (<= %zu): %"PRIu64": %"PRIu64, - offsetof(EntryObject, items), - le64toh(o->object.size), - offset); - return -EBADMSG; - } - - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) { - log_debug( - "Invalid number items in entry: %"PRIu64": %"PRIu64, - (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem), - offset); - return -EBADMSG; - } - - if (le64toh(o->entry.seqnum) <= 0) { - log_debug( - "Invalid entry seqnum: %"PRIx64": %"PRIu64, - le64toh(o->entry.seqnum), - offset); - return -EBADMSG; - } - - if (!VALID_REALTIME(le64toh(o->entry.realtime))) { - log_debug( - "Invalid entry realtime timestamp: %"PRIu64": %"PRIu64, - le64toh(o->entry.realtime), - offset); - return -EBADMSG; - } - - if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) { - log_debug( - "Invalid entry monotonic timestamp: %"PRIu64": %"PRIu64, - le64toh(o->entry.monotonic), - offset); - return -EBADMSG; - } + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64, + offsetof(EntryObject, items), + le64toh(o->object.size), + offset); + + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid number items in entry: %" PRIu64 ": %" PRIu64, + (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem), + offset); + + if (le64toh(o->entry.seqnum) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid entry seqnum: %" PRIx64 ": %" PRIu64, + le64toh(o->entry.seqnum), + offset); + + if (!VALID_REALTIME(le64toh(o->entry.realtime))) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid entry realtime timestamp: %" PRIu64 ": %" PRIu64, + le64toh(o->entry.realtime), + offset); + + if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid entry monotonic timestamp: %" PRIu64 ": %" PRIu64, + le64toh(o->entry.monotonic), + offset); break; case OBJECT_DATA_HASH_TABLE: case OBJECT_FIELD_HASH_TABLE: if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 || - (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) { - log_debug( - "Invalid %s hash table size: %"PRIu64": %"PRIu64, - o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", - le64toh(o->object.size), - offset); - return -EBADMSG; - } + (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid %s hash table size: %" PRIu64 ": %" PRIu64, + o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", + le64toh(o->object.size), + offset); break; case OBJECT_ENTRY_ARRAY: if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 || - (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) { - log_debug( - "Invalid object entry array size: %"PRIu64": %"PRIu64, - le64toh(o->object.size), - offset); - return -EBADMSG; - } - - if (!VALID64(le64toh(o->entry_array.next_entry_array_offset))) { - log_debug( - "Invalid object entry array next_entry_array_offset: "OFSfmt": %"PRIu64, - le64toh(o->entry_array.next_entry_array_offset), - offset); - return -EBADMSG; - } + (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid object entry array size: %" PRIu64 ": %" PRIu64, + le64toh(o->object.size), + offset); + + if (!VALID64(le64toh(o->entry_array.next_entry_array_offset))) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid object entry array next_entry_array_offset: " OFSfmt ": %" PRIu64, + le64toh(o->entry_array.next_entry_array_offset), + offset); break; case OBJECT_TAG: - if (le64toh(o->object.size) != sizeof(TagObject)) { - log_debug( - "Invalid object tag size: %"PRIu64": %"PRIu64, - le64toh(o->object.size), - offset); - return -EBADMSG; - } + if (le64toh(o->object.size) != sizeof(TagObject)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid object tag size: %" PRIu64 ": %" PRIu64, + le64toh(o->object.size), + offset); - if (!VALID_EPOCH(le64toh(o->tag.epoch))) { - log_debug( - "Invalid object tag epoch: %"PRIu64": %"PRIu64, - le64toh(o->tag.epoch), - offset); - return -EBADMSG; - } + if (!VALID_EPOCH(le64toh(o->tag.epoch))) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid object tag epoch: %" PRIu64 ": %" PRIu64, + le64toh(o->tag.epoch), offset); break; } @@ -914,16 +883,16 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset assert(ret); /* Objects may only be located at multiple of 64 bit */ - if (!VALID64(offset)) { - log_debug("Attempt to move to object at non-64bit boundary: %" PRIu64, offset); - return -EBADMSG; - } + if (!VALID64(offset)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Attempt to move to object at non-64bit boundary: %" PRIu64, + offset); /* Object may not be located in the file header */ - if (offset < le64toh(f->header->header_size)) { - log_debug("Attempt to move to object located in file header: %" PRIu64, offset); - return -EBADMSG; - } + if (offset < le64toh(f->header->header_size)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Attempt to move to object located in file header: %" PRIu64, + offset); r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t, &tsize); if (r < 0) @@ -932,29 +901,29 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset o = (Object*) t; s = le64toh(o->object.size); - if (s == 0) { - log_debug("Attempt to move to uninitialized object: %" PRIu64, offset); - return -EBADMSG; - } - if (s < sizeof(ObjectHeader)) { - log_debug("Attempt to move to overly short object: %" PRIu64, offset); - return -EBADMSG; - } - - if (o->object.type <= OBJECT_UNUSED) { - log_debug("Attempt to move to object with invalid type: %" PRIu64, offset); - return -EBADMSG; - } - - if (s < minimum_header_size(o)) { - log_debug("Attempt to move to truncated object: %" PRIu64, offset); - return -EBADMSG; - } - - if (type > OBJECT_UNUSED && o->object.type != type) { - log_debug("Attempt to move to object of unexpected type: %" PRIu64, offset); - return -EBADMSG; - } + if (s == 0) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Attempt to move to uninitialized object: %" PRIu64, + offset); + if (s < sizeof(ObjectHeader)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Attempt to move to overly short object: %" PRIu64, + offset); + + if (o->object.type <= OBJECT_UNUSED) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Attempt to move to object with invalid type: %" PRIu64, + offset); + + if (s < minimum_header_size(o)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Attempt to move to truncated object: %" PRIu64, + offset); + + if (type > OBJECT_UNUSED && o->object.type != type) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Attempt to move to object of unexpected type: %" PRIu64, + offset); if (s > tsize) { r = journal_file_move_to(f, type, false, offset, s, &t, NULL); @@ -1846,6 +1815,9 @@ static int journal_file_append_entry_internal( void journal_file_post_change(JournalFile *f) { assert(f); + if (f->fd < 0) + return; + /* inotify() does not receive IN_MODIFY events from file * accesses done via mmap(). After each access we hence * trigger IN_MODIFY by truncating the journal file to its @@ -1866,37 +1838,33 @@ static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userda } static void schedule_post_change(JournalFile *f) { - sd_event_source *timer; - int enabled, r; uint64_t now; + int r; assert(f); assert(f->post_change_timer); - timer = f->post_change_timer; - - r = sd_event_source_get_enabled(timer, &enabled); + r = sd_event_source_get_enabled(f->post_change_timer, NULL); if (r < 0) { log_debug_errno(r, "Failed to get ftruncate timer state: %m"); goto fail; } - - if (enabled == SD_EVENT_ONESHOT) + if (r > 0) return; - r = sd_event_now(sd_event_source_get_event(timer), CLOCK_MONOTONIC, &now); + r = sd_event_now(sd_event_source_get_event(f->post_change_timer), CLOCK_MONOTONIC, &now); if (r < 0) { log_debug_errno(r, "Failed to get clock's now for scheduling ftruncate: %m"); goto fail; } - r = sd_event_source_set_time(timer, now+f->post_change_timer_period); + r = sd_event_source_set_time(f->post_change_timer, now + f->post_change_timer_period); if (r < 0) { log_debug_errno(r, "Failed to set time for scheduling ftruncate: %m"); goto fail; } - r = sd_event_source_set_enabled(timer, SD_EVENT_ONESHOT); + r = sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_ONESHOT); if (r < 0) { log_debug_errno(r, "Failed to enable scheduled ftruncate: %m"); goto fail; @@ -1933,14 +1901,8 @@ int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t) return r; } -static int entry_item_cmp(const void *_a, const void *_b) { - const EntryItem *a = _a, *b = _b; - - if (le64toh(a->object_offset) < le64toh(b->object_offset)) - return -1; - if (le64toh(a->object_offset) > le64toh(b->object_offset)) - return 1; - return 0; +static int entry_item_cmp(const EntryItem *a, const EntryItem *b) { + return CMP(le64toh(a->object_offset), le64toh(b->object_offset)); } int journal_file_append_entry( @@ -1962,14 +1924,14 @@ int journal_file_append_entry( assert(iovec || n_iovec == 0); if (ts) { - if (!VALID_REALTIME(ts->realtime)) { - log_debug("Invalid realtime timestamp %"PRIu64", refusing entry.", ts->realtime); - return -EBADMSG; - } - if (!VALID_MONOTONIC(ts->monotonic)) { - log_debug("Invalid monotomic timestamp %"PRIu64", refusing entry.", ts->monotonic); - return -EBADMSG; - } + if (!VALID_REALTIME(ts->realtime)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid realtime timestamp %" PRIu64 ", refusing entry.", + ts->realtime); + if (!VALID_MONOTONIC(ts->monotonic)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "Invalid monotomic timestamp %" PRIu64 ", refusing entry.", + ts->monotonic); } else { dual_timestamp_get(&_ts); ts = &_ts; @@ -1999,7 +1961,7 @@ int journal_file_append_entry( /* Order by the position on disk, in order to improve seek * times for rotating media. */ - qsort_safe(items, n_iovec, sizeof(EntryItem), entry_item_cmp); + typesafe_qsort(items, n_iovec, entry_item_cmp); r = journal_file_append_entry_internal(f, ts, boot_id, xor_hash, items, n_iovec, seqnum, ret, offset); @@ -2619,6 +2581,8 @@ void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) { } int journal_file_compare_locations(JournalFile *af, JournalFile *bf) { + int r; + assert(af); assert(af->header); assert(bf); @@ -2638,10 +2602,9 @@ int journal_file_compare_locations(JournalFile *af, JournalFile *bf) { /* If this is from the same seqnum source, compare * seqnums */ - if (af->current_seqnum < bf->current_seqnum) - return -1; - if (af->current_seqnum > bf->current_seqnum) - return 1; + r = CMP(af->current_seqnum, bf->current_seqnum); + if (r != 0) + return r; /* Wow! This is weird, different data but the same * seqnums? Something is borked, but let's make the @@ -2651,25 +2614,18 @@ int journal_file_compare_locations(JournalFile *af, JournalFile *bf) { if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) { /* If the boot id matches, compare monotonic time */ - if (af->current_monotonic < bf->current_monotonic) - return -1; - if (af->current_monotonic > bf->current_monotonic) - return 1; + r = CMP(af->current_monotonic, bf->current_monotonic); + if (r != 0) + return r; } /* Otherwise, compare UTC time */ - if (af->current_realtime < bf->current_realtime) - return -1; - if (af->current_realtime > bf->current_realtime) - return 1; + r = CMP(af->current_realtime, bf->current_realtime); + if (r != 0) + return r; /* Finally, compare by contents */ - if (af->current_xor_hash < bf->current_xor_hash) - return -1; - if (af->current_xor_hash > bf->current_xor_hash) - return 1; - - return 0; + return CMP(af->current_xor_hash, bf->current_xor_hash); } static int bump_array_index(uint64_t *i, direction_t direction, uint64_t n) { @@ -2759,10 +2715,10 @@ int journal_file_next_entry( } /* Ensure our array is properly ordered. */ - if (p > 0 && !check_properly_ordered(ofs, p, direction)) { - log_debug("%s: entry array not properly ordered at entry %" PRIu64, f->path, i); - return -EBADMSG; - } + if (p > 0 && !check_properly_ordered(ofs, p, direction)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "%s: entry array not properly ordered at entry %" PRIu64, + f->path, i); if (offset) *offset = ofs; @@ -2835,10 +2791,10 @@ int journal_file_next_entry_for_data( } /* Ensure our array is properly ordered. */ - if (p > 0 && check_properly_ordered(ofs, p, direction)) { - log_debug("%s data entry array not properly ordered at entry %" PRIu64, f->path, i); - return -EBADMSG; - } + if (p > 0 && check_properly_ordered(ofs, p, direction)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "%s data entry array not properly ordered at entry %" PRIu64, + f->path, i); if (offset) *offset = ofs; @@ -3233,30 +3189,30 @@ int journal_file_open( if (fname && (flags & O_CREAT) && !endswith(fname, ".journal")) return -EINVAL; - f = new0(JournalFile, 1); + f = new(JournalFile, 1); if (!f) return -ENOMEM; - f->fd = fd; - f->mode = mode; + *f = (JournalFile) { + .fd = fd, + .mode = mode, + + .flags = flags, + .prot = prot_from_flags(flags), + .writable = (flags & O_ACCMODE) != O_RDONLY, - f->flags = flags; - f->prot = prot_from_flags(flags); - f->writable = (flags & O_ACCMODE) != O_RDONLY; #if HAVE_LZ4 - f->compress_lz4 = compress; + .compress_lz4 = compress, #elif HAVE_XZ - f->compress_xz = compress; + .compress_xz = compress, #endif - - if (compress_threshold_bytes == (uint64_t) -1) - f->compress_threshold_bytes = DEFAULT_COMPRESS_THRESHOLD; - else - f->compress_threshold_bytes = MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes); - + .compress_threshold_bytes = compress_threshold_bytes == (uint64_t) -1 ? + DEFAULT_COMPRESS_THRESHOLD : + MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes), #if HAVE_GCRYPT - f->seal = seal; + .seal = seal, #endif + }; log_debug("Journal effective settings seal=%s compress=%s compress_threshold_bytes=%s", yes_no(f->seal), yes_no(JOURNAL_FILE_COMPRESS(f)), @@ -3446,74 +3402,145 @@ fail: return r; } -int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes) { +int journal_file_archive(JournalFile *f) { _cleanup_free_ char *p = NULL; - size_t l; - JournalFile *old_file, *new_file = NULL; - int r; assert(f); - assert(*f); - old_file = *f; - - if (!old_file->writable) + if (!f->writable) return -EINVAL; /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse * rotation, since we don't know the actual path, and couldn't rename the file hence. */ - if (path_startswith(old_file->path, "/proc/self/fd")) + if (path_startswith(f->path, "/proc/self/fd")) return -EINVAL; - if (!endswith(old_file->path, ".journal")) + if (!endswith(f->path, ".journal")) return -EINVAL; - l = strlen(old_file->path); - r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal", - (int) l - 8, old_file->path, - SD_ID128_FORMAT_VAL(old_file->header->seqnum_id), - le64toh((*f)->header->head_entry_seqnum), - le64toh((*f)->header->head_entry_realtime)); - if (r < 0) + if (asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal", + (int) strlen(f->path) - 8, f->path, + SD_ID128_FORMAT_VAL(f->header->seqnum_id), + le64toh(f->header->head_entry_seqnum), + le64toh(f->header->head_entry_realtime)) < 0) return -ENOMEM; - /* Try to rename the file to the archived version. If the file - * already was deleted, we'll get ENOENT, let's ignore that - * case. */ - r = rename(old_file->path, p); - if (r < 0 && errno != ENOENT) + /* Try to rename the file to the archived version. If the file already was deleted, we'll get ENOENT, let's + * ignore that case. */ + if (rename(f->path, p) < 0 && errno != ENOENT) return -errno; /* Sync the rename to disk */ - (void) fsync_directory_of_file(old_file->fd); - - /* Set as archive so offlining commits w/state=STATE_ARCHIVED. - * Previously we would set old_file->header->state to STATE_ARCHIVED directly here, - * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which - * would result in the rotated journal never getting fsync() called before closing. - * Now we simply queue the archive state by setting an archive bit, leaving the state - * as STATE_ONLINE so proper offlining occurs. */ - old_file->archive = true; - - /* Currently, btrfs is not very good with out write patterns - * and fragments heavily. Let's defrag our journal files when - * we archive them */ - old_file->defrag_on_close = true; - - r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, - compress_threshold_bytes, seal, NULL, old_file->mmap, deferred_closes, - old_file, &new_file); - - if (deferred_closes && - set_put(deferred_closes, old_file) >= 0) - (void) journal_file_set_offline(old_file, false); - else - (void) journal_file_close(old_file); + (void) fsync_directory_of_file(f->fd); + + /* Set as archive so offlining commits w/state=STATE_ARCHIVED. Previously we would set old_file->header->state + * to STATE_ARCHIVED directly here, but journal_file_set_offline() short-circuits when state != STATE_ONLINE, + * which would result in the rotated journal never getting fsync() called before closing. Now we simply queue + * the archive state by setting an archive bit, leaving the state as STATE_ONLINE so proper offlining + * occurs. */ + f->archive = true; + + /* Currently, btrfs is not very good with out write patterns and fragments heavily. Let's defrag our journal + * files when we archive them */ + f->defrag_on_close = true; + + return 0; +} + +JournalFile* journal_initiate_close( + JournalFile *f, + Set *deferred_closes) { + + int r; + + assert(f); + + if (deferred_closes) { + + r = set_put(deferred_closes, f); + if (r < 0) + log_debug_errno(r, "Failed to add file to deferred close set, closing immediately."); + else { + (void) journal_file_set_offline(f, false); + return NULL; + } + } + + return journal_file_close(f); +} + +int journal_file_rotate( + JournalFile **f, + bool compress, + uint64_t compress_threshold_bytes, + bool seal, + Set *deferred_closes) { + JournalFile *new_file = NULL; + int r; + + assert(f); + assert(*f); + + r = journal_file_archive(*f); + if (r < 0) + return r; + + r = journal_file_open( + -1, + (*f)->path, + (*f)->flags, + (*f)->mode, + compress, + compress_threshold_bytes, + seal, + NULL, /* metrics */ + (*f)->mmap, + deferred_closes, + *f, /* template */ + &new_file); + + journal_initiate_close(*f, deferred_closes); *f = new_file; + return r; } +int journal_file_dispose(int dir_fd, const char *fname) { + _cleanup_free_ char *p = NULL; + _cleanup_close_ int fd = -1; + + assert(fname); + + /* Renames a journal file to *.journal~, i.e. to mark it as corruped or otherwise uncleanly shutdown. Note that + * this is done without looking into the file or changing any of its contents. The idea is that this is called + * whenever something is suspicious and we want to move the file away and make clear that it is not accessed + * for writing anymore. */ + + if (!endswith(fname, ".journal")) + return -EINVAL; + + if (asprintf(&p, "%.*s@%016" PRIx64 "-%016" PRIx64 ".journal~", + (int) strlen(fname) - 8, fname, + now(CLOCK_REALTIME), + random_u64()) < 0) + return -ENOMEM; + + if (renameat(dir_fd, fname, dir_fd, p) < 0) + return -errno; + + /* btrfs doesn't cope well with our write pattern and fragments heavily. Let's defrag all files we rotate */ + fd = openat(dir_fd, p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); + if (fd < 0) + log_debug_errno(errno, "Failed to open file for defragmentation/FS_NOCOW_FL, ignoring: %m"); + else { + (void) chattr_fd(fd, 0, FS_NOCOW_FL, NULL); + (void) btrfs_defrag_fd(fd); + } + + return 0; +} + int journal_file_open_reliably( const char *fname, int flags, @@ -3528,8 +3555,6 @@ int journal_file_open_reliably( JournalFile **ret) { int r; - size_t l; - _cleanup_free_ char *p = NULL; r = journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache, deferred_closes, template, ret); @@ -3555,25 +3580,12 @@ int journal_file_open_reliably( return r; /* The file is corrupted. Rotate it away and try it again (but only once) */ - - l = strlen(fname); - if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~", - (int) l - 8, fname, - now(CLOCK_REALTIME), - random_u64()) < 0) - return -ENOMEM; - - if (rename(fname, p) < 0) - return -errno; - - /* btrfs doesn't cope well with our write pattern and - * fragments heavily. Let's defrag all files we rotate */ - - (void) chattr_path(p, 0, FS_NOCOW_FL); - (void) btrfs_defrag(p); - log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname); + r = journal_file_dispose(AT_FDCWD, fname); + if (r < 0) + return r; + return journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache, deferred_closes, template, ret); } diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index c8114ee2d0..29e324d8cf 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -4,16 +4,16 @@ #include <inttypes.h> #if HAVE_GCRYPT -#include <gcrypt.h> +# include <gcrypt.h> #endif +#include "sd-event.h" #include "sd-id128.h" #include "hashmap.h" #include "journal-def.h" #include "macro.h" #include "mmap-cache.h" -#include "sd-event.h" #include "sparse-endian.h" typedef struct JournalMetrics { @@ -235,8 +235,12 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 void journal_file_dump(JournalFile *f); void journal_file_print_header(JournalFile *f); +int journal_file_archive(JournalFile *f); +JournalFile* journal_initiate_close(JournalFile *f, Set *deferred_closes); int journal_file_rotate(JournalFile **f, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes); +int journal_file_dispose(int dir_fd, const char *fname); + void journal_file_post_change(JournalFile *f); int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t); diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index a0621524a9..87056435fc 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -14,12 +14,12 @@ #include "alloc-util.h" #include "fd-util.h" -#include "fileio.h" #include "io-util.h" #include "memfd-util.h" #include "socket-util.h" #include "stdio-util.h" #include "string-util.h" +#include "tmpfile-util.h" #include "util.h" #define SNDBUF_SIZE (8*1024*1024) diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c index 8d3ae71440..2778ce40c5 100644 --- a/src/journal/journal-vacuum.c +++ b/src/journal/journal-vacuum.c @@ -15,6 +15,7 @@ #include "journal-vacuum.h" #include "parse-util.h" #include "string-util.h" +#include "time-util.h" #include "util.h" #include "xattr-util.h" @@ -29,30 +30,21 @@ struct vacuum_info { bool have_seqnum; }; -static int vacuum_compare(const void *_a, const void *_b) { - const struct vacuum_info *a, *b; - - a = _a; - b = _b; +static int vacuum_compare(const struct vacuum_info *a, const struct vacuum_info *b) { + int r; if (a->have_seqnum && b->have_seqnum && - sd_id128_equal(a->seqnum_id, b->seqnum_id)) { - if (a->seqnum < b->seqnum) - return -1; - else if (a->seqnum > b->seqnum) - return 1; - else - return 0; - } + sd_id128_equal(a->seqnum_id, b->seqnum_id)) + return CMP(a->seqnum, b->seqnum); - if (a->realtime < b->realtime) - return -1; - else if (a->realtime > b->realtime) - return 1; - else if (a->have_seqnum && b->have_seqnum) + r = CMP(a->realtime, b->realtime); + if (r != 0) + return r; + + if (a->have_seqnum && b->have_seqnum) return memcmp(&a->seqnum_id, &b->seqnum_id, 16); - else - return strcmp(a->filename, b->filename); + + return strcmp(a->filename, b->filename); } static void patch_realtime( @@ -134,11 +126,10 @@ int journal_directory_vacuum( usec_t *oldest_usec, bool verbose) { + uint64_t sum = 0, freed = 0, n_active_files = 0; + size_t n_list = 0, n_allocated = 0, i; _cleanup_closedir_ DIR *d = NULL; struct vacuum_info *list = NULL; - unsigned n_list = 0, i, n_active_files = 0; - size_t n_allocated = 0; - uint64_t sum = 0, freed = 0; usec_t retention_limit = 0; char sbytes[FORMAT_BYTES_MAX]; struct dirent *de; @@ -149,13 +140,8 @@ int journal_directory_vacuum( if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0) return 0; - if (max_retention_usec > 0) { - retention_limit = now(CLOCK_REALTIME); - if (retention_limit > max_retention_usec) - retention_limit -= max_retention_usec; - else - max_retention_usec = retention_limit = 0; - } + if (max_retention_usec > 0) + retention_limit = usec_sub_unsigned(now(CLOCK_REALTIME), max_retention_usec); d = opendir(directory); if (!d) @@ -281,21 +267,22 @@ int journal_directory_vacuum( goto finish; } - list[n_list].filename = TAKE_PTR(p); - list[n_list].usage = size; - list[n_list].seqnum = seqnum; - list[n_list].realtime = realtime; - list[n_list].seqnum_id = seqnum_id; - list[n_list].have_seqnum = have_seqnum; - n_list++; + list[n_list++] = (struct vacuum_info) { + .filename = TAKE_PTR(p), + .usage = size, + .seqnum = seqnum, + .realtime = realtime, + .seqnum_id = seqnum_id, + .have_seqnum = have_seqnum, + }; sum += size; } - qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare); + typesafe_qsort(list, n_list, vacuum_compare); for (i = 0; i < n_list; i++) { - unsigned left; + uint64_t left; left = n_active_files + n_list - i; diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index c2f0467c6e..5eff80a99f 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -17,6 +17,7 @@ #include "lookup3.h" #include "macro.h" #include "terminal-util.h" +#include "tmpfile-util.h" #include "util.h" static void draw_progress(uint64_t p, usec_t *last_usec) { @@ -60,10 +61,11 @@ static void draw_progress(uint64_t p, usec_t *last_usec) { } static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) { + /* Calculates scale * p / m, but handles m == 0 safely, and saturates. + * Currently all callers use m >= 1, but we keep the check to be defensive. + */ - /* Calculates scale * p / m, but handles m == 0 safely, and saturates */ - - if (p >= m || m == 0) + if (p >= m || m == 0) /* lgtm [cpp/constant-comparison] */ return scale; return scale * p / m; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 56b1be530d..14a02eda74 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -22,6 +22,7 @@ #endif #include "sd-bus.h" +#include "sd-device.h" #include "sd-journal.h" #include "acl-util.h" @@ -30,12 +31,15 @@ #include "bus-util.h" #include "catalog.h" #include "chattr-util.h" +#include "def.h" +#include "device-private.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "fsprg.h" #include "glob-util.h" #include "hostname-util.h" +#include "id128-print.h" #include "io-util.h" #include "journal-def.h" #include "journal-internal.h" @@ -50,6 +54,7 @@ #include "pager.h" #include "parse-util.h" #include "path-util.h" +#include "pretty-print.h" #include "rlimit-util.h" #include "set.h" #include "sigbus.h" @@ -57,8 +62,7 @@ #include "strv.h" #include "syslog-util.h" #include "terminal-util.h" -#include "udev-util.h" -#include "udev.h" +#include "tmpfile-util.h" #include "unit-name.h" #include "user-util.h" @@ -82,10 +86,9 @@ static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out r = pcre2_get_error_message(errorcode, buf, sizeof buf); - log_error("Bad pattern \"%s\": %s", - pattern, - r < 0 ? "unknown error" : (char*) buf); - return -EINVAL; + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Bad pattern \"%s\": %s", pattern, + r < 0 ? "unknown error" : (char *)buf); } *out = p; @@ -102,11 +105,10 @@ enum { static OutputMode arg_output = OUTPUT_SHORT; static bool arg_utc = false; -static bool arg_pager_end = false; static bool arg_follow = false; static bool arg_full = true; static bool arg_all = false; -static bool arg_no_pager = false; +static PagerFlags arg_pager_flags = 0; static int arg_lines = ARG_LINES_DEFAULT; static bool arg_no_tail = false; static bool arg_quiet = false; @@ -165,6 +167,7 @@ static enum { ACTION_SYNC, ACTION_ROTATE, ACTION_VACUUM, + ACTION_ROTATE_AND_VACUUM, ACTION_LIST_FIELDS, ACTION_LIST_FIELD_NAMES, } arg_action = ACTION_SHOW; @@ -177,9 +180,8 @@ typedef struct BootId { } BootId; static int add_matches_for_device(sd_journal *j, const char *devpath) { - _cleanup_(udev_unrefp) struct udev *udev = NULL; - _cleanup_(udev_device_unrefp) struct udev_device *device = NULL; - struct udev_device *d = NULL; + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + sd_device *d = NULL; struct stat st; int r; @@ -191,33 +193,25 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) { return -EINVAL; } - udev = udev_new(); - if (!udev) - return log_oom(); - if (stat(devpath, &st) < 0) return log_error_errno(errno, "Couldn't stat file: %m"); - r = udev_device_new_from_stat_rdev(udev, &st, &device); + r = device_new_from_stat_rdev(&device, &st); if (r < 0) - return log_error_errno(r, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev)); + return log_error_errno(r, "Failed to get device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev)); - d = device; - while (d) { + for (d = device; d; ) { _cleanup_free_ char *match = NULL; const char *subsys, *sysname, *devnode; + sd_device *parent; - subsys = udev_device_get_subsystem(d); - if (!subsys) { - d = udev_device_get_parent(d); - continue; - } + r = sd_device_get_subsystem(d, &subsys); + if (r < 0) + goto get_parent; - sysname = udev_device_get_sysname(d); - if (!sysname) { - d = udev_device_get_parent(d); - continue; - } + r = sd_device_get_sysname(d, &sysname); + if (r < 0) + goto get_parent; match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname); if (!match) @@ -227,8 +221,7 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) { if (r < 0) return log_error_errno(r, "Failed to add match: %m"); - devnode = udev_device_get_devnode(d); - if (devnode) { + if (sd_device_get_devname(d, &devnode) >= 0) { _cleanup_free_ char *match1 = NULL; r = stat(devnode, &st); @@ -244,7 +237,11 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) { return log_error_errno(r, "Failed to add match: %m"); } - d = udev_device_get_parent(d); +get_parent: + if (sd_device_get_parent(d, &parent) < 0) + break; + + d = parent; } r = add_match_this_boot(j, arg_machine); @@ -297,9 +294,15 @@ static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset return 0; } -static void help(void) { +static int help(void) { + _cleanup_free_ char *link = NULL; + int r; - (void) pager_open(arg_no_pager, arg_pager_end); + (void) pager_open(arg_pager_flags); + + r = terminal_urlify_man("journalctl", "1", &link); + if (r < 0) + return log_oom(); printf("%s [OPTIONS...] [MATCHES...]\n\n" "Query the journal.\n\n" @@ -329,7 +332,8 @@ static void help(void) { " -o --output=STRING Change journal output mode (short, short-precise,\n" " short-iso, short-iso-precise, short-full,\n" " short-monotonic, short-unix, verbose, export,\n" - " json, json-pretty, json-sse, cat, with-unit)\n" + " json, json-pretty, json-sse, json-seq, cat,\n" + " with-unit)\n" " --output-fields=LIST Select fields to print in verbose/export/json modes\n" " --utc Express time in Coordinated Universal Time (UTC)\n" " -x --catalog Add message explanations where available\n" @@ -342,11 +346,9 @@ static void help(void) { " -D --directory=PATH Show journal files from directory\n" " --file=PATH Show journal file\n" " --root=ROOT Operate on files below a root directory\n" -#if HAVE_GCRYPT " --interval=TIME Time interval for changing the FSS sealing key\n" " --verify-key=KEY Specify FSS verification key\n" " --force Override of the FSS key pair with --setup-keys\n" -#endif "\nCommands:\n" " -h --help Show this help text\n" " --version Show package version\n" @@ -364,11 +366,13 @@ static void help(void) { " --list-catalog Show all message IDs in the catalog\n" " --dump-catalog Show entries in the message catalog\n" " --update-catalog Update the message catalog database\n" - " --new-id128 Generate a new 128-bit ID\n" -#if HAVE_GCRYPT " --setup-keys Generate a new FSS key pair\n" -#endif - , program_invocation_short_name); + "\nSee the %s for details.\n" + , program_invocation_short_name + , link + ); + + return 0; } static int parse_argv(int argc, char *argv[]) { @@ -423,7 +427,7 @@ static int parse_argv(int argc, char *argv[]) { { "no-full", no_argument, NULL, ARG_NO_FULL }, { "lines", optional_argument, NULL, 'n' }, { "no-tail", no_argument, NULL, ARG_NO_TAIL }, - { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, + { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */ { "quiet", no_argument, NULL, 'q' }, { "merge", no_argument, NULL, 'm' }, { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */ @@ -482,18 +486,17 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { case 'h': - help(); - return 0; + return help(); case ARG_VERSION: return version(); case ARG_NO_PAGER: - arg_no_pager = true; + arg_pager_flags |= PAGER_DISABLE; break; case 'e': - arg_pager_end = true; + arg_pager_flags |= PAGER_JUMP_TO_END; if (arg_lines == ARG_LINES_DEFAULT) arg_lines = 1000; @@ -516,7 +519,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_CAT)) + if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT)) arg_quiet = true; break; @@ -685,7 +688,7 @@ static int parse_argv(int argc, char *argv[]) { return r; } - arg_action = ACTION_VACUUM; + arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM; break; case ARG_VACUUM_FILES: @@ -695,7 +698,7 @@ static int parse_argv(int argc, char *argv[]) { return r; } - arg_action = ACTION_VACUUM; + arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM; break; case ARG_VACUUM_TIME: @@ -705,7 +708,7 @@ static int parse_argv(int argc, char *argv[]) { return r; } - arg_action = ACTION_VACUUM; + arg_action = arg_action == ACTION_ROTATE ? ACTION_ROTATE_AND_VACUUM : ACTION_VACUUM; break; #if HAVE_GCRYPT @@ -741,7 +744,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERIFY_KEY: case ARG_INTERVAL: case ARG_FORCE: - log_error("Forward-secure sealing not available."); + log_error("Compiled without forward-secure sealing support."); return -EOPNOTSUPP; #endif @@ -894,7 +897,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_ROTATE: - arg_action = ACTION_ROTATE; + arg_action = arg_action == ACTION_VACUUM ? ACTION_ROTATE_AND_VACUUM : ACTION_ROTATE; break; case ARG_SYNC: @@ -1007,35 +1010,6 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int generate_new_id128(void) { - sd_id128_t id; - int r; - unsigned i; - - r = sd_id128_randomize(&id); - if (r < 0) - return log_error_errno(r, "Failed to generate ID: %m"); - - printf("As string:\n" - SD_ID128_FORMAT_STR "\n\n" - "As UUID:\n" - "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n" - "As man:sd-id128(3) macro:\n" - "#define MESSAGE_XYZ SD_ID128_MAKE(", - SD_ID128_FORMAT_VAL(id), - SD_ID128_FORMAT_VAL(id)); - for (i = 0; i < 16; i++) - printf("%02x%s", id.bytes[i], i != 15 ? "," : ""); - fputs(")\n\n", stdout); - - printf("As Python constant:\n" - ">>> import uuid\n" - ">>> MESSAGE_XYZ = uuid.UUID('" SD_ID128_FORMAT_STR "')\n", - SD_ID128_FORMAT_VAL(id)); - - return 0; -} - static int add_matches(sd_journal *j, char **args) { char **i; bool have_term = false; @@ -1096,10 +1070,10 @@ static int add_matches(sd_journal *j, char **args) { r = add_matches_for_device(j, p); if (r < 0) return r; - } else { - log_error("File is neither a device node, nor regular file, nor executable: %s", *i); - return -EINVAL; - } + } else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "File is neither a device node, nor regular file, nor executable: %s", + *i); have_term = true; } else { @@ -1111,10 +1085,9 @@ static int add_matches(sd_journal *j, char **args) { return log_error_errno(r, "Failed to add match '%s': %m", *i); } - if (!strv_isempty(args) && !have_term) { - log_error("\"+\" can only be used between terms"); - return -EINVAL; - } + if (!strv_isempty(args) && !have_term) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "\"+\" can only be used between terms"); return 0; } @@ -1205,10 +1178,9 @@ static int discover_next_boot(sd_journal *j, r = sd_journal_previous(j); if (r < 0) return r; - else if (r == 0) { - log_debug("Whoopsie! We found a boot ID but can't read its last entry."); - return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */ - } + else if (r == 0) + return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), + "Whoopsie! We found a boot ID but can't read its last entry."); /* This shouldn't happen. We just came from this very boot ID. */ r = sd_journal_get_realtime_usec(j, &next_boot->last); if (r < 0) @@ -1352,7 +1324,7 @@ static int list_boots(sd_journal *j) { if (count == 0) return count; - (void) pager_open(arg_no_pager, arg_pager_end); + (void) pager_open(arg_pager_flags); /* numbers are one less, but we need an extra char for the sign */ w = DECIMAL_STR_WIDTH(count - 1) + 1; @@ -1761,7 +1733,7 @@ static int setup_keys(void) { /* Enable secure remove, exclusion from dump, synchronous * writing and in-place updating */ - r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL); + r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, NULL); if (r < 0) log_warning_errno(r, "Failed to set file attributes: %m"); @@ -1859,8 +1831,8 @@ finish: return r; #else - log_error("Forward-secure sealing not available."); - return -EOPNOTSUPP; + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Forward-secure sealing not available."); #endif } @@ -1997,7 +1969,7 @@ static int send_signal_and_wait(int sig, const char *watch_path) { /* See if a sync happened by now. */ r = read_timestamp_file(watch_path, &tstamp); if (r < 0 && r != -ENOENT) - return log_error_errno(errno, "Failed to read %s: %m", watch_path); + return log_error_errno(r, "Failed to read %s: %m", watch_path); if (r >= 0 && tstamp >= start) return 0; @@ -2064,19 +2036,59 @@ static int sync_journal(void) { return send_signal_and_wait(SIGRTMIN+1, "/run/systemd/journal/synced"); } -int main(int argc, char *argv[]) { +static int wait_for_change(sd_journal *j, int poll_fd) { + struct pollfd pollfds[] = { + { .fd = poll_fd, .events = POLLIN }, + { .fd = STDOUT_FILENO }, + }; + + struct timespec ts; + usec_t timeout; int r; + + assert(j); + assert(poll_fd >= 0); + + /* Much like sd_journal_wait() but also keeps an eye on STDOUT, and exits as soon as we see a POLLHUP on that, + * i.e. when it is closed. */ + + r = sd_journal_get_timeout(j, &timeout); + if (r < 0) + return log_error_errno(r, "Failed to determine journal waiting time: %m"); + + if (ppoll(pollfds, ELEMENTSOF(pollfds), + timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL) < 0) { + if (errno == EINTR) + return 0; + + return log_error_errno(errno, "Couldn't wait for journal event: %m"); + } + + if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */ + return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED), + "Standard output has been closed."); + + r = sd_journal_process(j); + if (r < 0) + return log_error_errno(r, "Failed to process journal events: %m"); + + return 0; +} + +int main(int argc, char *argv[]) { + bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false; _cleanup_(sd_journal_closep) sd_journal *j = NULL; - bool need_seek = false; sd_id128_t previous_boot_id; - bool previous_boot_id_valid = false, first_line = true; - int n_shown = 0; - bool ellipsized = false; + int n_shown = 0, r, poll_fd = -1; setlocale(LC_ALL, ""); log_parse_environment(); log_open(); + /* Increase max number of open files if we can, we might needs this when browsing journal files, which might be + * split up into many files. */ + (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE); + r = parse_argv(argc, argv); if (r <= 0) goto finish; @@ -2084,15 +2096,10 @@ int main(int argc, char *argv[]) { signal(SIGWINCH, columns_lines_cache_reset); sigbus_install(); - /* Increase max number of open files to 16K if we can, we - * might needs this when browsing journal files, which might - * be split up into many files. */ - setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(16384)); - switch (arg_action) { case ACTION_NEW_ID128: - r = generate_new_id128(); + r = id128_print_new(true); goto finish; case ACTION_SETUP_KEYS: @@ -2104,7 +2111,7 @@ int main(int argc, char *argv[]) { case ACTION_UPDATE_CATALOG: { _cleanup_free_ char *database; - database = path_join(arg_root, CATALOG_DATABASE, NULL); + database = path_join(arg_root, CATALOG_DATABASE); if (!database) { r = log_oom(); goto finish; @@ -2117,7 +2124,7 @@ int main(int argc, char *argv[]) { } else { bool oneline = arg_action == ACTION_LIST_CATALOG; - (void) pager_open(arg_no_pager, arg_pager_end); + (void) pager_open(arg_pager_flags); if (optind < argc) r = catalog_list_items(stdout, database, oneline, argv + optind); @@ -2148,6 +2155,7 @@ int main(int argc, char *argv[]) { case ACTION_DISK_USAGE: case ACTION_LIST_BOOTS: case ACTION_VACUUM: + case ACTION_ROTATE_AND_VACUUM: case ACTION_LIST_FIELDS: case ACTION_LIST_FIELD_NAMES: /* These ones require access to the journal files, continue below. */ @@ -2265,6 +2273,14 @@ int main(int argc, char *argv[]) { r = list_boots(j); goto finish; + case ACTION_ROTATE_AND_VACUUM: + + r = rotate(); + if (r < 0) + goto finish; + + _fallthrough_; + case ACTION_VACUUM: { Directory *d; Iterator i; @@ -2391,15 +2407,15 @@ int main(int argc, char *argv[]) { /* Opening the fd now means the first sd_journal_wait() will actually wait */ if (arg_follow) { - r = sd_journal_get_fd(j); - if (r == -EMFILE) { - log_warning("Insufficent watch descriptors available. Reverting to -n."); + poll_fd = sd_journal_get_fd(j); + if (poll_fd == -EMFILE) { + log_warning_errno(poll_fd, "Insufficent watch descriptors available. Reverting to -n."); arg_follow = false; - } else if (r == -EMEDIUMTYPE) { - log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN."); + } else if (poll_fd == -EMEDIUMTYPE) { + log_error_errno(poll_fd, "The --follow switch is not supported in conjunction with reading from STDIN."); goto finish; - } else if (r < 0) { - log_error_errno(r, "Failed to get journal fd: %m"); + } else if (poll_fd < 0) { + log_error_errno(poll_fd, "Failed to get journal fd: %m"); goto finish; } } @@ -2476,7 +2492,7 @@ int main(int argc, char *argv[]) { need_seek = true; if (!arg_follow) - (void) pager_open(arg_no_pager, arg_pager_end); + (void) pager_open(arg_pager_flags); if (!arg_quiet && (arg_lines != 0 || arg_follow)) { usec_t start, end; @@ -2621,7 +2637,7 @@ int main(int argc, char *argv[]) { need_seek = true; if (r == -EADDRNOTAVAIL) break; - else if (r < 0 || ferror(stdout)) + else if (r < 0) goto finish; n_shown++; @@ -2659,11 +2675,10 @@ int main(int argc, char *argv[]) { } fflush(stdout); - r = sd_journal_wait(j, (uint64_t) -1); - if (r < 0) { - log_error_errno(r, "Couldn't wait for journal event: %m"); + + r = wait_for_change(j, poll_fd); + if (r < 0) goto finish; - } first_line = false; } diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index 87726684af..345e43ef44 100644 --- a/src/journal/journald-audit.c +++ b/src/journal/journald-audit.c @@ -313,7 +313,7 @@ static int map_all_fields( } } -static void process_audit_string(Server *s, int type, const char *data, size_t size) { +void process_audit_string(Server *s, int type, const char *data, size_t size) { size_t n_iov_allocated = 0, n_iov = 0, z; _cleanup_free_ struct iovec *iov = NULL; uint64_t seconds, msec, id; @@ -341,11 +341,12 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s if (!p) return; + k = 0; if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n", &seconds, &msec, &id, - &k) != 3) + &k) != 3 || k == 0) return; p += k; @@ -497,7 +498,6 @@ static int enable_audit(int fd, bool b) { } int server_open_audit(Server *s) { - static const int one = 1; int r; if (s->audit_fd < 0) { @@ -526,11 +526,11 @@ int server_open_audit(Server *s) { return 0; } } else - fd_nonblock(s->audit_fd, 1); + (void) fd_nonblock(s->audit_fd, true); - r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + r = setsockopt_int(s->audit_fd, SOL_SOCKET, SO_PASSCRED, true); if (r < 0) - return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m"); + return log_error_errno(r, "Failed to set SO_PASSCRED on audit socket: %m"); r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s); if (r < 0) diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h index 57bb1711c9..df41f81435 100644 --- a/src/journal/journald-audit.h +++ b/src/journal/journald-audit.h @@ -6,4 +6,6 @@ void server_process_audit_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const union sockaddr_union *sa, socklen_t salen); -int server_open_audit(Server*s); +void process_audit_string(Server *s, int type, const char *data, size_t size); + +int server_open_audit(Server *s); diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index ce07de1bfb..8253a45128 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -13,6 +13,8 @@ #include "io-util.h" #include "journal-util.h" #include "journald-context.h" +#include "parse-util.h" +#include "path-util.h" #include "process-util.h" #include "string-util.h" #include "syslog-util.h" @@ -62,18 +64,13 @@ static int client_context_compare(const void *a, const void *b) { const ClientContext *x = a, *y = b; + int r; - if (x->timestamp < y->timestamp) - return -1; - if (x->timestamp > y->timestamp) - return 1; - - if (x->pid < y->pid) - return -1; - if (x->pid > y->pid) - return 1; + r = CMP(x->timestamp, y->timestamp); + if (r != 0) + return r; - return 0; + return CMP(x->pid, y->pid); } static int client_context_new(Server *s, pid_t pid, ClientContext **ret) { @@ -107,6 +104,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) { c->timestamp = USEC_INFINITY; c->extra_fields_mtime = NSEC_INFINITY; c->log_level_max = -1; + c->log_rate_limit_interval = s->rate_limit_interval; + c->log_rate_limit_burst = s->rate_limit_burst; r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c); if (r < 0) { @@ -118,7 +117,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) { return 0; } -static void client_context_reset(ClientContext *c) { +static void client_context_reset(Server *s, ClientContext *c) { + assert(s); assert(c); c->timestamp = USEC_INFINITY; @@ -153,6 +153,9 @@ static void client_context_reset(ClientContext *c) { c->extra_fields_mtime = NSEC_INFINITY; c->log_level_max = -1; + + c->log_rate_limit_interval = s->rate_limit_interval; + c->log_rate_limit_burst = s->rate_limit_burst; } static ClientContext* client_context_free(Server *s, ClientContext *c) { @@ -166,7 +169,7 @@ static ClientContext* client_context_free(Server *s, ClientContext *c) { if (c->in_lru) assert_se(prioq_remove(s->client_contexts_lru, c, &c->lru_index) >= 0); - client_context_reset(c); + client_context_reset(s, c); return mfree(c); } @@ -250,9 +253,11 @@ static int client_context_read_cgroup(Server *s, ClientContext *c, const char *u /* Try to acquire the current cgroup path */ r = cg_pid_get_path_shifted(c->pid, s->cgroup_root, &t); - if (r < 0) { + if (r < 0 || empty_or_root(t)) { - /* If that didn't work, we use the unit ID passed in as fallback, if we have nothing cached yet */ + /* We use the unit ID passed in as fallback if we have nothing cached yet and cg_pid_get_path_shifted() + * failed or process is running in a root cgroup. Zombie processes are automatically migrated to root cgroup + * on cgroupsv1 and we want to be able to map log messages from them too. */ if (unit_id && !c->unit) { c->unit = strdup(unit_id); if (c->unit) @@ -429,6 +434,42 @@ static int client_context_read_extra_fields( return 0; } +static int client_context_read_log_rate_limit_interval(ClientContext *c) { + _cleanup_free_ char *value = NULL; + const char *p; + int r; + + assert(c); + + if (!c->unit) + return 0; + + p = strjoina("/run/systemd/units/log-rate-limit-interval:", c->unit); + r = readlink_malloc(p, &value); + if (r < 0) + return r; + + return safe_atou64(value, &c->log_rate_limit_interval); +} + +static int client_context_read_log_rate_limit_burst(ClientContext *c) { + _cleanup_free_ char *value = NULL; + const char *p; + int r; + + assert(c); + + if (!c->unit) + return 0; + + p = strjoina("/run/systemd/units/log-rate-limit-burst:", c->unit); + r = readlink_malloc(p, &value); + if (r < 0) + return r; + + return safe_atou(value, &c->log_rate_limit_burst); +} + static void client_context_really_refresh( Server *s, ClientContext *c, @@ -455,6 +496,8 @@ static void client_context_really_refresh( (void) client_context_read_invocation_id(s, c); (void) client_context_read_log_level_max(s, c); (void) client_context_read_extra_fields(s, c); + (void) client_context_read_log_rate_limit_interval(c); + (void) client_context_read_log_rate_limit_burst(c); c->timestamp = timestamp; @@ -485,7 +528,7 @@ void client_context_maybe_refresh( /* If the data isn't pinned and if the cashed data is older than the upper limit, we flush it out * entirely. This follows the logic that as long as an entry is pinned the PID reuse is unlikely. */ if (c->n_ref == 0 && c->timestamp + MAX_USEC < timestamp) { - client_context_reset(c); + client_context_reset(s, c); goto refresh; } diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h index 9df3a38eff..5e19c71f14 100644 --- a/src/journal/journald-context.h +++ b/src/journal/journald-context.h @@ -49,6 +49,9 @@ struct ClientContext { size_t extra_fields_n_iovec; void *extra_fields_data; nsec_t extra_fields_mtime; + + usec_t log_rate_limit_interval; + unsigned log_rate_limit_burst; }; int client_context_get( diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index e9aff13168..ce82102eed 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -6,10 +6,11 @@ #include <sys/socket.h> #include <unistd.h> -#include "libudev.h" +#include "sd-device.h" #include "sd-messages.h" #include "alloc-util.h" +#include "device-util.h" #include "escape.h" #include "fd-util.h" #include "format-util.h" @@ -93,7 +94,7 @@ static bool is_us(const char *identifier, const char *pid) { streq(identifier, program_invocation_short_name); } -static void dev_kmsg_record(Server *s, const char *p, size_t l) { +void dev_kmsg_record(Server *s, char *p, size_t l) { _cleanup_free_ char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL, *identifier = NULL, *pid = NULL; struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; @@ -191,7 +192,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) { e = memchr(k, '\n', l); if (!e) - return; + goto finish; *e = 0; @@ -209,16 +210,13 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) { } if (kernel_device) { - struct udev_device *ud; + _cleanup_(sd_device_unrefp) sd_device *d = NULL; - ud = udev_device_new_from_device_id(s->udev, kernel_device); - if (ud) { + if (sd_device_new_from_device_id(&d, kernel_device) >= 0) { const char *g; - struct udev_list_entry *ll; char *b; - g = udev_device_get_devnode(ud); - if (g) { + if (sd_device_get_devname(d, &g) >= 0) { b = strappend("_UDEV_DEVNODE=", g); if (b) { iovec[n++] = IOVEC_MAKE_STRING(b); @@ -226,8 +224,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) { } } - g = udev_device_get_sysname(ud); - if (g) { + if (sd_device_get_sysname(d, &g) >= 0) { b = strappend("_UDEV_SYSNAME=", g); if (b) { iovec[n++] = IOVEC_MAKE_STRING(b); @@ -236,25 +233,19 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) { } j = 0; - ll = udev_device_get_devlinks_list_entry(ud); - udev_list_entry_foreach(ll, ll) { + FOREACH_DEVICE_DEVLINK(d, g) { - if (j > N_IOVEC_UDEV_FIELDS) + if (j >= N_IOVEC_UDEV_FIELDS) break; - g = udev_list_entry_get_name(ll); - if (g) { - b = strappend("_UDEV_DEVLINK=", g); - if (b) { - iovec[n++] = IOVEC_MAKE_STRING(b); - z++; - } + b = strappend("_UDEV_DEVLINK=", g); + if (b) { + iovec[n++] = IOVEC_MAKE_STRING(b); + z++; } j++; } - - udev_device_unref(ud); } } diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h index bff24ac310..2326bc8c93 100644 --- a/src/journal/journald-kmsg.h +++ b/src/journal/journald-kmsg.h @@ -9,3 +9,5 @@ int server_flush_dev_kmsg(Server *s); void server_forward_kmsg(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); int server_open_kernel_seqnum(Server *s); + +void dev_kmsg_record(Server *s, char *p, size_t l); diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 5ff22a10af..e86178ed77 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -25,6 +25,7 @@ #include "selinux-util.h" #include "socket-util.h" #include "string-util.h" +#include "strv.h" #include "unaligned.h" static bool allow_object_pid(const struct ucred *ucred) { @@ -205,8 +206,7 @@ static int server_process_entry( memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l); if (journal_field_valid(p, e - p, false)) { - iovec[n].iov_base = k; - iovec[n].iov_len = (e - p) + 1 + l; + iovec[n] = IOVEC_MAKE(k, (e - p) + 1 + l); entry_size += iovec[n].iov_len; n++; @@ -277,7 +277,7 @@ finish: void server_process_native_message( Server *s, - const void *buffer, size_t buffer_size, + const char *buffer, size_t buffer_size, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) { @@ -337,11 +337,7 @@ void server_process_native_file( return; } - e = path_startswith(k, "/dev/shm/"); - if (!e) - e = path_startswith(k, "/tmp/"); - if (!e) - e = path_startswith(k, "/var/tmp/"); + e = PATH_STARTSWITH_SET(k, "/dev/shm/", "/tmp/", "/var/tmp/"); if (!e) { log_error("Received file outside of allowed directories. Refusing."); return; @@ -437,13 +433,12 @@ void server_process_native_file( } } -int server_open_native_socket(Server*s) { +int server_open_native_socket(Server *s) { static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/journal/socket", }; - static const int one = 1; int r; assert(s); @@ -453,7 +448,7 @@ int server_open_native_socket(Server*s) { if (s->native_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - (void) unlink(sa.un.sun_path); + (void) sockaddr_un_unlink(&sa.un); r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) @@ -461,23 +456,23 @@ int server_open_native_socket(Server*s) { (void) chmod(sa.un.sun_path, 0666); } else - fd_nonblock(s->native_fd, 1); + (void) fd_nonblock(s->native_fd, true); - r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_PASSCRED, true); if (r < 0) - return log_error_errno(errno, "SO_PASSCRED failed: %m"); + return log_error_errno(r, "SO_PASSCRED failed: %m"); #if HAVE_SELINUX if (mac_selinux_use()) { - r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); + r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_PASSSEC, true); if (r < 0) - log_warning_errno(errno, "SO_PASSSEC failed: %m"); + log_warning_errno(r, "SO_PASSSEC failed: %m"); } #endif - r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + r = setsockopt_int(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, true); if (r < 0) - return log_error_errno(errno, "SO_TIMESTAMP failed: %m"); + return log_error_errno(r, "SO_TIMESTAMP failed: %m"); r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, server_process_datagram, s); if (r < 0) diff --git a/src/journal/journald-native.h b/src/journal/journald-native.h index 7211d4fab4..2a33ef74c5 100644 --- a/src/journal/journald-native.h +++ b/src/journal/journald-native.h @@ -5,7 +5,7 @@ void server_process_native_message( Server *s, - const void *buffer, + const char *buffer, size_t buffer_size, const struct ucred *ucred, const struct timeval *tv, diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index 6a8a36a736..0b42d53760 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -39,6 +39,10 @@ struct JournalRateLimitGroup { JournalRateLimit *parent; char *id; + + /* Interval is stored to keep track of when the group expires */ + usec_t interval; + JournalRateLimitPool pools[POOLS_MAX]; uint64_t hash; @@ -47,8 +51,6 @@ struct JournalRateLimitGroup { }; struct JournalRateLimit { - usec_t interval; - unsigned burst; JournalRateLimitGroup* buckets[BUCKETS_MAX]; JournalRateLimitGroup *lru, *lru_tail; @@ -58,18 +60,13 @@ struct JournalRateLimit { uint8_t hash_key[16]; }; -JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) { +JournalRateLimit *journal_rate_limit_new(void) { JournalRateLimit *r; - assert(interval > 0 || burst == 0); - r = new0(JournalRateLimit, 1); if (!r) return NULL; - r->interval = interval; - r->burst = burst; - random_bytes(r->hash_key, sizeof(r->hash_key)); return r; @@ -109,7 +106,7 @@ _pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, us assert(g); for (i = 0; i < POOLS_MAX; i++) - if (g->pools[i].begin + g->parent->interval >= ts) + if (g->pools[i].begin + g->interval >= ts) return false; return true; @@ -126,9 +123,8 @@ static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) { journal_rate_limit_group_free(r->lru_tail); } -static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) { +static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t interval, usec_t ts) { JournalRateLimitGroup *g; - struct siphash state; assert(r); assert(id); @@ -141,9 +137,9 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, if (!g->id) goto fail; - siphash24_init(&state, r->hash_key); - string_hash_func(g->id, &state); - g->hash = siphash24_finalize(&state); + g->hash = siphash24_string(g->id, r->hash_key); + + g->interval = interval; journal_rate_limit_vacuum(r, ts); @@ -189,11 +185,10 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) { return burst; } -int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) { +int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) { uint64_t h; JournalRateLimitGroup *g; JournalRateLimitPool *p; - struct siphash state; unsigned burst; usec_t ts; @@ -209,16 +204,9 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u if (!r) return 1; - if (r->interval == 0 || r->burst == 0) - return 1; - - burst = burst_modulate(r->burst, available); - ts = now(CLOCK_MONOTONIC); - siphash24_init(&state, r->hash_key); - string_hash_func(id, &state); - h = siphash24_finalize(&state); + h = siphash24_string(id, r->hash_key); g = r->buckets[h % BUCKETS_MAX]; LIST_FOREACH(bucket, g, g) @@ -226,10 +214,16 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u break; if (!g) { - g = journal_rate_limit_group_new(r, id, ts); + g = journal_rate_limit_group_new(r, id, rl_interval, ts); if (!g) return -ENOMEM; - } + } else + g->interval = rl_interval; + + if (rl_interval == 0 || rl_burst == 0) + return 1; + + burst = burst_modulate(rl_burst, available); p = &g->pools[priority_map[priority]]; @@ -240,7 +234,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u return 1; } - if (p->begin + r->interval < ts) { + if (p->begin + rl_interval < ts) { unsigned s; s = p->suppressed; diff --git a/src/journal/journald-rate-limit.h b/src/journal/journald-rate-limit.h index 3a7f106de0..a2992800fe 100644 --- a/src/journal/journald-rate-limit.h +++ b/src/journal/journald-rate-limit.h @@ -5,6 +5,6 @@ typedef struct JournalRateLimit JournalRateLimit; -JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst); +JournalRateLimit *journal_rate_limit_new(void); void journal_rate_limit_free(JournalRateLimit *r); -int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available); +int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 4f1550ec5b..434325c179 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -9,7 +9,6 @@ #include <sys/statvfs.h> #include <linux/sockios.h> -#include "libudev.h" #include "sd-daemon.h" #include "sd-journal.h" #include "sd-messages.h" @@ -75,6 +74,8 @@ * for a bit of additional metadata. */ #define DEFAULT_LINE_MAX (48*1024) +#define DEFERRED_CLOSES_MAX (4096) + static int determine_path_usage(Server *s, const char *path, uint64_t *ret_used, uint64_t *ret_free) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -253,8 +254,9 @@ static int open_journal( bool seal, JournalMetrics *metrics, JournalFile **ret) { - int r; + JournalFile *f; + int r; assert(s); assert(fname); @@ -309,7 +311,7 @@ static int system_journal_open(Server *s, bool flush_requested) { server_add_acls(s->system_journal, 0); (void) cache_space_refresh(s, &s->system_storage); patch_min_use(&s->system_storage); - } else if (r < 0) { + } else { if (!IN_SET(r, -ENOENT, -EROFS)) log_warning_errno(r, "Failed to open system journal: %m"); @@ -400,17 +402,21 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (uid_for_system_journal(uid)) return s->system_journal; - r = sd_id128_get_machine(&machine); - if (r < 0) - return s->system_journal; - f = ordered_hashmap_get(s->user_journals, UID_TO_PTR(uid)); if (f) return f; + r = sd_id128_get_machine(&machine); + if (r < 0) { + log_debug_errno(r, "Failed to determine machine ID, using system log: %m"); + return s->system_journal; + } + if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-"UID_FMT".journal", - SD_ID128_FORMAT_VAL(machine), uid) < 0) + SD_ID128_FORMAT_VAL(machine), uid) < 0) { + log_oom(); return s->system_journal; + } while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) { /* Too many open? Then let's close one */ @@ -460,17 +466,81 @@ static int do_rotate( return r; } +static void server_process_deferred_closes(Server *s) { + JournalFile *f; + Iterator i; + + /* Perform any deferred closes which aren't still offlining. */ + SET_FOREACH(f, s->deferred_closes, i) { + if (journal_file_is_offlining(f)) + continue; + + (void) set_remove(s->deferred_closes, f); + (void) journal_file_close(f); + } +} + +static void server_vacuum_deferred_closes(Server *s) { + assert(s); + + /* Make some room in the deferred closes list, so that it doesn't grow without bounds */ + if (set_size(s->deferred_closes) < DEFERRED_CLOSES_MAX) + return; + + /* Let's first remove all journal files that might already have completed closing */ + server_process_deferred_closes(s); + + /* And now, let's close some more until we reach the limit again. */ + while (set_size(s->deferred_closes) >= DEFERRED_CLOSES_MAX) { + JournalFile *f; + + assert_se(f = set_steal_first(s->deferred_closes)); + journal_file_close(f); + } +} + +static int open_user_journal_directory(Server *s, DIR **ret_dir, char **ret_path) { + _cleanup_closedir_ DIR *dir = NULL; + _cleanup_free_ char *path = NULL; + sd_id128_t machine; + int r; + + assert(s); + + r = sd_id128_get_machine(&machine); + if (r < 0) + return log_error_errno(r, "Failed to determine machine ID, ignoring: %m"); + + if (asprintf(&path, "/var/log/journal/" SD_ID128_FORMAT_STR "/", SD_ID128_FORMAT_VAL(machine)) < 0) + return log_oom(); + + dir = opendir(path); + if (!dir) + return log_error_errno(errno, "Failed to open user journal directory '%s': %m", path); + + if (ret_dir) + *ret_dir = TAKE_PTR(dir); + if (ret_path) + *ret_path = TAKE_PTR(path); + + return 0; +} + void server_rotate(Server *s) { + _cleanup_free_ char *path = NULL; + _cleanup_closedir_ DIR *d = NULL; JournalFile *f; - void *k; Iterator i; + void *k; int r; log_debug("Rotating..."); + /* First, rotate the system journal (either in its runtime flavour or in its runtime flavour) */ (void) do_rotate(s, &s->runtime_journal, "runtime", false, 0); (void) do_rotate(s, &s->system_journal, "system", s->seal, 0); + /* Then, rotate all user journals we have open (keeping them open) */ ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) { r = do_rotate(s, &f, "user", s->seal, PTR_TO_UID(k)); if (r >= 0) @@ -480,12 +550,94 @@ void server_rotate(Server *s) { ordered_hashmap_remove(s->user_journals, k); } - /* Perform any deferred closes which aren't still offlining. */ - SET_FOREACH(f, s->deferred_closes, i) - if (!journal_file_is_offlining(f)) { - (void) set_remove(s->deferred_closes, f); - (void) journal_file_close(f); + /* Finally, also rotate all user journals we currently do not have open. (But do so only if we actually have + * access to /var, i.e. are not in the log-to-runtime-journal mode). */ + if (!s->runtime_journal && + open_user_journal_directory(s, &d, &path) >= 0) { + + struct dirent *de; + + FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate %s, ignoring: %m", path)) { + _cleanup_free_ char *u = NULL, *full = NULL; + _cleanup_close_ int fd = -1; + const char *a, *b; + uid_t uid; + + a = startswith(de->d_name, "user-"); + if (!a) + continue; + b = endswith(de->d_name, ".journal"); + if (!b) + continue; + + u = strndup(a, b-a); + if (!u) { + log_oom(); + break; + } + + r = parse_uid(u, &uid); + if (r < 0) { + log_debug_errno(r, "Failed to parse UID from file name '%s', ignoring: %m", de->d_name); + continue; + } + + /* Already rotated in the above loop? i.e. is it an open user journal? */ + if (ordered_hashmap_contains(s->user_journals, UID_TO_PTR(uid))) + continue; + + full = strjoin(path, de->d_name); + if (!full) { + log_oom(); + break; + } + + fd = openat(dirfd(d), de->d_name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK); + if (fd < 0) { + log_full_errno(IN_SET(errno, ELOOP, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to open journal file '%s' for rotation: %m", full); + continue; + } + + /* Make some room in the set of deferred close()s */ + server_vacuum_deferred_closes(s); + + /* Open the file briefly, so that we can archive it */ + r = journal_file_open(fd, + full, + O_RDWR, + 0640, + s->compress.enabled, + s->compress.threshold_bytes, + s->seal, + &s->system_storage.metrics, + s->mmap, + s->deferred_closes, + NULL, + &f); + if (r < 0) { + log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full); + + r = journal_file_dispose(dirfd(d), de->d_name); + if (r < 0) + log_warning_errno(r, "Failed to move %s out of the way, ignoring: %m", full); + else + log_debug("Successfully moved %s out of the way.", full); + + continue; + } + + TAKE_FD(fd); /* Donated to journal_file_open() */ + + r = journal_file_archive(f); + if (r < 0) + log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full); + + f = journal_initiate_close(f, s->deferred_closes); } + } + + server_process_deferred_closes(s); } void server_sync(Server *s) { @@ -938,7 +1090,7 @@ void server_dispatch_message( if (c && c->unit) { (void) determine_space(s, &available, NULL); - rl = journal_rate_limit_test(s->rate_limit, c->unit, priority & LOG_PRIMASK, available); + rl = journal_rate_limit_test(s->rate_limit, c->unit, c->log_rate_limit_interval, c->log_rate_limit_burst, priority & LOG_PRIMASK, available); if (rl == 0) return; @@ -1036,7 +1188,8 @@ int server_flush_to_var(Server *s, bool require_flag_file) { r = 0; finish: - journal_file_post_change(s->system_journal); + if (s->system_journal) + journal_file_post_change(s->system_journal); s->runtime_journal = journal_file_close(s->runtime_journal); @@ -1096,10 +1249,10 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void assert(s); assert(fd == s->native_fd || fd == s->syslog_fd || fd == s->audit_fd); - if (revents != EPOLLIN) { - log_error("Got invalid event from epoll for datagram fd: %"PRIx32, revents); - return -EIO; - } + if (revents != EPOLLIN) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Got invalid event from epoll for datagram fd: %" PRIx32, + revents); /* Try to get the right size, if we can. (Not all sockets support SIOCINQ, hence we just try, but don't rely on * it.) */ @@ -1113,8 +1266,7 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void if (!GREEDY_REALLOC(s->buffer, s->buffer_size, m)) return log_oom(); - iovec.iov_base = s->buffer; - iovec.iov_len = s->buffer_size - 1; /* Leave room for trailing NUL we add later */ + iovec = IOVEC_MAKE(s->buffer, s->buffer_size - 1); /* Leave room for trailing NUL we add later */ n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); if (n < 0) { @@ -1595,11 +1747,9 @@ static int dispatch_watchdog(sd_event_source *es, uint64_t usec, void *userdata) } static int server_connect_notify(Server *s) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - }; + union sockaddr_union sa = {}; const char *e; - int r; + int r, salen; assert(s); assert(s->notify_fd < 0); @@ -1628,15 +1778,9 @@ static int server_connect_notify(Server *s) { if (!e) return 0; - if (!IN_SET(e[0], '@', '/') || e[1] == 0) { - log_error("NOTIFY_SOCKET set to an invalid value: %s", e); - return -EINVAL; - } - - if (strlen(e) > sizeof(sa.un.sun_path)) { - log_error("NOTIFY_SOCKET path too long: %s", e); - return -EINVAL; - } + salen = sockaddr_un_set_path(&sa.un, e); + if (salen < 0) + return log_error_errno(salen, "NOTIFY_SOCKET set to invalid value '%s': %m", e); s->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->notify_fd < 0) @@ -1644,11 +1788,7 @@ static int server_connect_notify(Server *s) { (void) fd_inc_sndbuf(s->notify_fd, NOTIFY_SNDBUF_SIZE); - strncpy(sa.un.sun_path, e, sizeof(sa.un.sun_path)); - if (sa.un.sun_path[0] == '@') - sa.un.sun_path[0] = 0; - - r = connect(s->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + r = connect(s->notify_fd, &sa.sa, salen); if (r < 0) return log_error_errno(errno, "Failed to connect to notify socket: %m"); @@ -1745,38 +1885,34 @@ int server_init(Server *s) { if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) { - if (s->native_fd >= 0) { - log_error("Too many native sockets passed."); - return -EINVAL; - } + if (s->native_fd >= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too many native sockets passed."); s->native_fd = fd; } else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) { - if (s->stdout_fd >= 0) { - log_error("Too many stdout sockets passed."); - return -EINVAL; - } + if (s->stdout_fd >= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too many stdout sockets passed."); s->stdout_fd = fd; } else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0 || sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/dev-log", 0) > 0) { - if (s->syslog_fd >= 0) { - log_error("Too many /dev/log sockets passed."); - return -EINVAL; - } + if (s->syslog_fd >= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too many /dev/log sockets passed."); s->syslog_fd = fd; } else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { - if (s->audit_fd >= 0) { - log_error("Too many audit sockets passed."); - return -EINVAL; - } + if (s->audit_fd >= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too many audit sockets passed."); s->audit_fd = fd; @@ -1845,11 +1981,7 @@ int server_init(Server *s) { if (r < 0) return r; - s->udev = udev_new(); - if (!s->udev) - return -ENOMEM; - - s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst); + s->rate_limit = journal_rate_limit_new(); if (!s->rate_limit) return -ENOMEM; @@ -1949,8 +2081,6 @@ void server_done(Server *s) { if (s->mmap) mmap_cache_unref(s->mmap); - - udev_unref(s->udev); } static const char* const storage_table[_STORAGE_MAX] = { diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 983be8bb89..6d4847b0cd 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -137,8 +137,6 @@ struct Server { Set *deferred_closes; - struct udev *udev; - uint64_t *kernel_seqnum; bool dev_kmsg_readable:1; diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index dbf3503a82..137c8f0446 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -12,6 +12,7 @@ #include "alloc-util.h" #include "dirent-util.h" +#include "env-file.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -31,6 +32,7 @@ #include "stdio-util.h" #include "string-util.h" #include "syslog-util.h" +#include "tmpfile-util.h" #include "unit-name.h" #define STDOUT_STREAMS_MAX 4096 @@ -125,7 +127,7 @@ void stdout_stream_free(StdoutStream *s) { DEFINE_TRIVIAL_CLEANUP_FUNC(StdoutStream*, stdout_stream_free); -static void stdout_stream_destroy(StdoutStream *s) { +void stdout_stream_destroy(StdoutStream *s) { if (!s) return; @@ -534,7 +536,7 @@ terminate: return 0; } -static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { +int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { _cleanup_(stdout_stream_freep) StdoutStream *stream = NULL; sd_id128_t id; int r; @@ -596,10 +598,10 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent assert(s); - if (revents != EPOLLIN) { - log_error("Got invalid event from epoll for stdout server fd: %"PRIx32, revents); - return -EIO; - } + if (revents != EPOLLIN) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Got invalid event from epoll for stdout server fd: %" PRIx32, + revents); fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (fd < 0) { @@ -610,7 +612,17 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent } if (s->n_stdout_streams >= STDOUT_STREAMS_MAX) { - log_warning("Too many stdout streams, refusing connection."); + struct ucred u; + + r = getpeercred(fd, &u); + + /* By closing fd here we make sure that the client won't wait too long for journald to + * gather all the data it adds to the error message to find out that the connection has + * just been refused. + */ + fd = safe_close(fd); + + server_driver_message(s, r < 0 ? 0 : u.pid, NULL, LOG_MESSAGE("Too many stdout streams, refusing connection."), NULL); return 0; } @@ -641,7 +653,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) { return log_oom(); } - r = parse_env_file(NULL, stream->state_file, NEWLINE, + r = parse_env_file(NULL, stream->state_file, "PRIORITY", &priority, "LEVEL_PREFIX", &level_prefix, "FORWARD_TO_SYSLOG", &forward_to_syslog, @@ -649,8 +661,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) { "FORWARD_TO_CONSOLE", &forward_to_console, "IDENTIFIER", &stream->identifier, "UNIT", &stream->unit_id, - "STREAM_ID", &stream_id, - NULL); + "STREAM_ID", &stream_id); if (r < 0) return log_error_errno(r, "Failed to read: %s", stream->state_file); @@ -793,7 +804,7 @@ int server_open_stdout_socket(Server *s) { if (s->stdout_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - (void) unlink(sa.un.sun_path); + (void) sockaddr_un_unlink(&sa.un); r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) @@ -804,7 +815,7 @@ int server_open_stdout_socket(Server *s) { if (listen(s->stdout_fd, SOMAXCONN) < 0) return log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path); } else - fd_nonblock(s->stdout_fd, 1); + (void) fd_nonblock(s->stdout_fd, true); r = sd_event_add_io(s->event, &s->stdout_event_source, s->stdout_fd, EPOLLIN, stdout_stream_new, s); if (r < 0) diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h index bc5622ab3b..487376e763 100644 --- a/src/journal/journald-stream.h +++ b/src/journal/journald-stream.h @@ -10,4 +10,6 @@ int server_open_stdout_socket(Server *s); int server_restore_streams(Server *s, FDSet *fds); void stdout_stream_free(StdoutStream *s); +int stdout_stream_install(Server *s, int fd, StdoutStream **ret); +void stdout_stream_destroy(StdoutStream *s); void stdout_stream_send_notify(StdoutStream *s); diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 9dea116722..a60a259bc4 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -117,7 +117,7 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons header_pid[STRLEN("[]: ") + DECIMAL_STR_MAX(pid_t) + 1]; int n = 0; time_t t; - struct tm *tm; + struct tm tm; _cleanup_free_ char *ident_buf = NULL; assert(s); @@ -134,10 +134,9 @@ void server_forward_syslog(Server *s, int priority, const char *identifier, cons /* Second: timestamp */ t = tv ? tv->tv_sec : ((time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC)); - tm = localtime(&t); - if (!tm) + if (!localtime_r(&t, &tm)) return; - if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) + if (strftime(header_time, sizeof(header_time), "%h %e %T ", &tm) <= 0) return; iovec[n++] = IOVEC_MAKE_STRING(header_time); @@ -194,7 +193,7 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) e = l; l--; - if (p[l-1] == ']') { + if (l > 0 && p[l-1] == ']') { size_t k = l-1; for (;;) { @@ -219,13 +218,16 @@ size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) if (t) *identifier = t; - if (strchr(WHITESPACE, p[e])) + /* Single space is used as separator */ + if (p[e] != '\0' && strchr(WHITESPACE, p[e])) e++; + + l = (p - *buf) + e; *buf = p + e; - return e; + return l; } -static void syslog_skip_date(char **buf) { +static int syslog_skip_timestamp(const char **buf) { enum { LETTER, SPACE, @@ -245,24 +247,21 @@ static void syslog_skip_date(char **buf) { SPACE }; - char *p; + const char *p, *t; unsigned i; assert(buf); assert(*buf); - p = *buf; - - for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { - + for (i = 0, p = *buf; i < ELEMENTSOF(sequence); i++, p++) { if (!*p) - return; + return 0; switch (sequence[i]) { case SPACE: if (*p != ' ') - return; + return 0; break; case SPACE_OR_NUMBER: @@ -272,48 +271,57 @@ static void syslog_skip_date(char **buf) { _fallthrough_; case NUMBER: if (*p < '0' || *p > '9') - return; + return 0; break; case LETTER: if (!(*p >= 'A' && *p <= 'Z') && !(*p >= 'a' && *p <= 'z')) - return; + return 0; break; case COLON: if (*p != ':') - return; + return 0; break; } } + t = *buf; *buf = p; + return p - t; } void server_process_syslog_message( Server *s, const char *buf, - size_t buf_len, + size_t raw_len, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) { - char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)], - syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)], *msg; - const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL; - _cleanup_free_ char *identifier = NULL, *pid = NULL; + char *t, syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)], + syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)]; + const char *msg, *syslog_ts, *a; + _cleanup_free_ char *identifier = NULL, *pid = NULL, + *dummy = NULL, *msg_msg = NULL, *msg_raw = NULL; int priority = LOG_USER | LOG_INFO, r; ClientContext *context = NULL; struct iovec *iovec; - size_t n = 0, m, i; + size_t n = 0, m, i, leading_ws, syslog_ts_len; + bool store_raw; assert(s); assert(buf); + /* The message cannot be empty. */ + assert(raw_len > 0); + /* The buffer NUL-terminated and can be used a string. raw_len is the length + * without the terminating NUL byte, the buffer is actually one bigger. */ + assert(buf[raw_len] == '\0'); if (ucred && pid_is_valid(ucred->pid)) { r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context); @@ -321,26 +329,50 @@ void server_process_syslog_message( log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid); } - /* We are creating copy of the message because we want to forward original message verbatim to the legacy - syslog implementation */ - for (i = buf_len; i > 0; i--) + /* We are creating a copy of the message because we want to forward the original message + verbatim to the legacy syslog implementation */ + for (i = raw_len; i > 0; i--) if (!strchr(WHITESPACE, buf[i-1])) break; - msg = newa(char, i + 1); - *((char *) mempcpy(msg, buf, i)) = 0; - msg = skip_leading_chars(msg, WHITESPACE); + leading_ws = strspn(buf, WHITESPACE); + + if (i == 0) + /* The message contains only whitespaces */ + msg = buf + raw_len; + else if (i == raw_len) + /* Nice! No need to strip anything on the end, let's optimize this a bit */ + msg = buf + leading_ws; + else { + msg = dummy = new(char, i - leading_ws + 1); + if (!dummy) { + log_oom(); + return; + } - syslog_parse_priority((const char **)&msg, &priority, true); + memcpy(dummy, buf + leading_ws, i - leading_ws); + dummy[i - leading_ws] = 0; + } + + /* We will add the SYSLOG_RAW= field when we stripped anything + * _or_ if the input message contained NUL bytes. */ + store_raw = msg != buf || strlen(msg) != raw_len; + + syslog_parse_priority(&msg, &priority, true); if (!client_context_test_priority(context, priority)) return; - if (s->forward_to_syslog) - forward_syslog_raw(s, priority, buf, buf_len, ucred, tv); + syslog_ts = msg; + syslog_ts_len = syslog_skip_timestamp(&msg); + if (syslog_ts_len == 0) + /* We failed to parse the full timestamp, store the raw message too */ + store_raw = true; + + syslog_parse_identifier(&msg, &identifier, &pid); - syslog_skip_date(&msg); - syslog_parse_identifier((const char**)&msg, &identifier, &pid); + if (s->forward_to_syslog) + forward_syslog_raw(s, priority, buf, raw_len, ucred, tv); if (s->forward_to_kmsg) server_forward_kmsg(s, priority, identifier, msg, ucred); @@ -351,7 +383,7 @@ void server_process_syslog_message( if (s->forward_to_wall) server_forward_wall(s, priority, identifier, msg, ucred); - m = N_IOVEC_META_FIELDS + 6 + client_context_extra_fields_n_iovec(context); + m = N_IOVEC_META_FIELDS + 8 + client_context_extra_fields_n_iovec(context); iovec = newa(struct iovec, m); iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=syslog"); @@ -365,18 +397,46 @@ void server_process_syslog_message( } if (identifier) { - syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier); - iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier); + a = strjoina("SYSLOG_IDENTIFIER=", identifier); + iovec[n++] = IOVEC_MAKE_STRING(a); } if (pid) { - syslog_pid = strjoina("SYSLOG_PID=", pid); - iovec[n++] = IOVEC_MAKE_STRING(syslog_pid); + a = strjoina("SYSLOG_PID=", pid); + iovec[n++] = IOVEC_MAKE_STRING(a); } - message = strjoina("MESSAGE=", msg); - if (message) - iovec[n++] = IOVEC_MAKE_STRING(message); + if (syslog_ts_len > 0) { + const size_t hlen = STRLEN("SYSLOG_TIMESTAMP="); + + t = newa(char, hlen + syslog_ts_len); + memcpy(t, "SYSLOG_TIMESTAMP=", hlen); + memcpy(t + hlen, syslog_ts, syslog_ts_len); + + iovec[n++] = IOVEC_MAKE(t, hlen + syslog_ts_len); + } + + msg_msg = strjoin("MESSAGE=", msg); + if (!msg_msg) { + log_oom(); + return; + } + iovec[n++] = IOVEC_MAKE_STRING(msg_msg); + + if (store_raw) { + const size_t hlen = STRLEN("SYSLOG_RAW="); + + msg_raw = new(char, hlen + raw_len); + if (!msg_raw) { + log_oom(); + return; + } + + memcpy(msg_raw, "SYSLOG_RAW=", hlen); + memcpy(msg_raw + hlen, buf, raw_len); + + iovec[n++] = IOVEC_MAKE(msg_raw, hlen + raw_len); + } server_dispatch_message(s, iovec, n, m, context, tv, priority, 0); } @@ -387,7 +447,6 @@ int server_open_syslog_socket(Server *s) { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/journal/dev-log", }; - static const int one = 1; int r; assert(s); @@ -397,7 +456,7 @@ int server_open_syslog_socket(Server *s) { if (s->syslog_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - (void) unlink(sa.un.sun_path); + (void) sockaddr_un_unlink(&sa.un); r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) @@ -405,23 +464,23 @@ int server_open_syslog_socket(Server *s) { (void) chmod(sa.un.sun_path, 0666); } else - fd_nonblock(s->syslog_fd, 1); + (void) fd_nonblock(s->syslog_fd, true); - r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, true); if (r < 0) - return log_error_errno(errno, "SO_PASSCRED failed: %m"); + return log_error_errno(r, "SO_PASSCRED failed: %m"); #if HAVE_SELINUX if (mac_selinux_use()) { - r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); + r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, true); if (r < 0) - log_warning_errno(errno, "SO_PASSSEC failed: %m"); + log_warning_errno(r, "SO_PASSSEC failed: %m"); } #endif - r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + r = setsockopt_int(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, true); if (r < 0) - return log_error_errno(errno, "SO_TIMESTAMP failed: %m"); + return log_error_errno(r, "SO_TIMESTAMP failed: %m"); r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, server_process_datagram, s); if (r < 0) diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c index 75328aa94b..370c9b32e2 100644 --- a/src/journal/journald-wall.c +++ b/src/journal/journald-wall.c @@ -1,7 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - Copyright © 2014 Sebastian Thorarensen -***/ #include "alloc-util.h" #include "format-util.h" diff --git a/src/journal/journald-wall.h b/src/journal/journald-wall.h index be27370d6b..d081c8254c 100644 --- a/src/journal/journald-wall.h +++ b/src/journal/journald-wall.h @@ -1,9 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - Copyright © 2014 Sebastian Thorarensen -***/ #include "journald-server.h" diff --git a/src/journal/journald.conf b/src/journal/journald.conf index 94d5c678aa..2f1c661153 100644 --- a/src/journal/journald.conf +++ b/src/journal/journald.conf @@ -40,3 +40,4 @@ #MaxLevelConsole=info #MaxLevelWall=emerg #LineMax=48K +#ReadKMsg=yes diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c index ff194dd951..6c61f17c7d 100644 --- a/src/journal/lookup3.c +++ b/src/journal/lookup3.c @@ -319,7 +319,7 @@ uint32_t jenkins_hashlittle( const void *key, size_t length, uint32_t initval) * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#if !VALGRIND && !defined(__SANITIZE_ADDRESS__) +#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER switch(length) { @@ -504,7 +504,7 @@ void jenkins_hashlittle2( * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#if !VALGRIND && !defined(__SANITIZE_ADDRESS__) +#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER switch(length) { @@ -680,7 +680,7 @@ uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval) * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#if !VALGRIND && !defined(__SANITIZE_ADDRESS__) +#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER switch(length) { diff --git a/src/journal/meson.build b/src/journal/meson.build index 807dece545..e03d6dc232 100644 --- a/src/journal/meson.build +++ b/src/journal/meson.build @@ -34,7 +34,7 @@ endif ############################################################ audit_type_includes = [config_h, - missing_h, + missing_audit_h, 'linux/audit.h'] if conf.get('HAVE_AUDIT') == 1 audit_type_includes += 'libaudit.h' diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index 2366055f0a..90549f1c9f 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -53,7 +53,7 @@ struct MMapFileDescriptor { }; struct MMapCache { - int n_ref; + unsigned n_ref; unsigned n_windows; unsigned n_hit, n_missed; @@ -85,14 +85,6 @@ MMapCache* mmap_cache_new(void) { return m; } -MMapCache* mmap_cache_ref(MMapCache *m) { - assert(m); - assert(m->n_ref > 0); - - m->n_ref++; - return m; -} - static void window_unlink(Window *w) { Context *c; @@ -278,7 +270,7 @@ static void context_free(Context *c) { free(c); } -static void mmap_cache_free(MMapCache *m) { +static MMapCache *mmap_cache_free(MMapCache *m) { int i; assert(m); @@ -292,22 +284,10 @@ static void mmap_cache_free(MMapCache *m) { while (m->unused) window_free(m->unused); - free(m); + return mfree(m); } -MMapCache* mmap_cache_unref(MMapCache *m) { - - if (!m) - return NULL; - - assert(m->n_ref > 0); - - m->n_ref--; - if (m->n_ref == 0) - mmap_cache_free(m); - - return NULL; -} +DEFINE_TRIVIAL_REF_UNREF_FUNC(MMapCache, mmap_cache, mmap_cache_free); static int make_room(MMapCache *m) { assert(m); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 83abd82d1c..b5ff5b64f3 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -16,6 +16,8 @@ #include "catalog.h" #include "compress.h" #include "dirent-util.h" +#include "env-file.h" +#include "escape.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" @@ -381,7 +383,7 @@ static char *match_make_string(Match *m) { return strdup("none"); if (m->type == MATCH_DISCRETE) - return strndup(m->data, m->size); + return cescape_length(m->data, m->size); LIST_FOREACH(matches, i, m->matches) { char *t, *k; @@ -433,6 +435,8 @@ _public_ void sd_journal_flush_matches(sd_journal *j) { } _pure_ static int compare_with_location(JournalFile *f, Location *l) { + int r; + assert(f); assert(l); assert(f->location_type == LOCATION_SEEK); @@ -449,35 +453,31 @@ _pure_ static int compare_with_location(JournalFile *f, Location *l) { if (l->seqnum_set && sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) { - if (f->current_seqnum < l->seqnum) - return -1; - if (f->current_seqnum > l->seqnum) - return 1; + r = CMP(f->current_seqnum, l->seqnum); + if (r != 0) + return r; } if (l->monotonic_set && sd_id128_equal(f->current_boot_id, l->boot_id)) { - if (f->current_monotonic < l->monotonic) - return -1; - if (f->current_monotonic > l->monotonic) - return 1; + r = CMP(f->current_monotonic, l->monotonic); + if (r != 0) + return r; } if (l->realtime_set) { - if (f->current_realtime < l->realtime) - return -1; - if (f->current_realtime > l->realtime) - return 1; + r = CMP(f->current_realtime, l->realtime); + if (r != 0) + return r; } if (l->xor_hash_set) { - if (f->current_xor_hash < l->xor_hash) - return -1; - if (f->current_xor_hash > l->xor_hash) - return 1; + r = CMP(f->current_xor_hash, l->xor_hash); + if (r != 0) + return r; } return 0; @@ -1184,8 +1184,7 @@ static bool file_has_type_prefix(const char *prefix, const char *filename) { tilded = strjoina(full, "~"); atted = strjoina(prefix, "@"); - return streq(filename, full) || - streq(filename, tilded) || + return STR_IN_SET(filename, full, tilded) || startswith(filename, atted); } @@ -1889,7 +1888,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in assert_return(machine_name_is_valid(machine), -EINVAL); p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(NULL, p, NEWLINE, "ROOT", &root, "CLASS", &class, NULL); + r = parse_env_file(NULL, p, + "ROOT", &root, + "CLASS", &class); if (r == -ENOENT) return -EHOSTDOWN; if (r < 0) @@ -2824,31 +2825,30 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_ return r; /* Let's do the type check by hand, since we used 0 context above. */ - if (o->object.type != OBJECT_DATA) { - log_debug("%s:offset " OFSfmt ": object has type %d, expected %d", - j->unique_file->path, j->unique_offset, - o->object.type, OBJECT_DATA); - return -EBADMSG; - } + if (o->object.type != OBJECT_DATA) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "%s:offset " OFSfmt ": object has type %d, expected %d", + j->unique_file->path, + j->unique_offset, + o->object.type, OBJECT_DATA); r = return_data(j, j->unique_file, o, &odata, &ol); if (r < 0) return r; /* Check if we have at least the field name and "=". */ - if (ol <= k) { - log_debug("%s:offset " OFSfmt ": object has size %zu, expected at least %zu", - j->unique_file->path, j->unique_offset, - ol, k + 1); - return -EBADMSG; - } - - if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') { - log_debug("%s:offset " OFSfmt ": object does not start with \"%s=\"", - j->unique_file->path, j->unique_offset, - j->unique_field); - return -EBADMSG; - } + if (ol <= k) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "%s:offset " OFSfmt ": object has size %zu, expected at least %zu", + j->unique_file->path, + j->unique_offset, ol, k + 1); + + if (memcmp(odata, j->unique_field, k) || ((const char*) odata)[k] != '=') + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "%s:offset " OFSfmt ": object does not start with \"%s=\"", + j->unique_file->path, + j->unique_offset, + j->unique_field); /* OK, now let's see if we already returned this data * object by checking if it exists in the earlier @@ -2979,10 +2979,11 @@ _public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) { return r; /* Because we used OBJECT_UNUSED above, we need to do our type check manually */ - if (o->object.type != OBJECT_FIELD) { - log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD); - return -EBADMSG; - } + if (o->object.type != OBJECT_FIELD) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), + "%s:offset " OFSfmt ": object has type %i, expected %i", + f->path, j->fields_offset, + o->object.type, OBJECT_FIELD); sz = le64toh(o->object.size) - offsetof(Object, field.payload); diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c index 8eae993780..192bb0cb07 100644 --- a/src/journal/test-catalog.c +++ b/src/journal/test-catalog.c @@ -11,17 +11,16 @@ #include "catalog.h" #include "fd-util.h" #include "fs-util.h" -#include "fileio.h" #include "log.h" #include "macro.h" +#include "path-util.h" #include "string-util.h" +#include "strv.h" +#include "tests.h" +#include "tmpfile-util.h" #include "util.h" -static const char *catalog_dirs[] = { - CATALOG_DIR, - NULL, -}; - +static char** catalog_dirs = NULL; static const char *no_catalog_dirs[] = { "/bin/hopefully/with/no/catalog", NULL @@ -167,8 +166,8 @@ static void test_catalog_update(const char *database) { assert_se(r == 0); /* Make sure that we at least have some files loaded or the - catalog_list below will fail. */ - r = catalog_update(database, NULL, catalog_dirs); + * catalog_list below will fail. */ + r = catalog_update(database, NULL, (const char * const *) catalog_dirs); assert_se(r == 0); } @@ -207,8 +206,14 @@ int main(int argc, char *argv[]) { setlocale(LC_ALL, "de_DE.UTF-8"); - log_parse_environment(); - log_open(); + test_setup_logging(LOG_DEBUG); + + /* If test-catalog is located at the build directory, then use catalogs in that. + * If it is not, e.g. installed by systemd-tests package, then use installed catalogs. */ + catalog_dirs = STRV_MAKE(get_catalog_dir()); + + assert_se(access(catalog_dirs[0], F_OK) >= 0); + log_notice("Using catalog directory '%s'", catalog_dirs[0]); test_catalog_file_lang(); diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c index 411df3fa7a..7f13b611e6 100644 --- a/src/journal/test-compress-benchmark.c +++ b/src/journal/test-compress-benchmark.c @@ -8,6 +8,7 @@ #include "process-util.h" #include "random-util.h" #include "string-util.h" +#include "tests.h" #include "util.h" typedef int (compress_t)(const void *src, uint64_t src_size, void *dst, @@ -142,30 +143,23 @@ static void test_compress_decompress(const char* label, const char* type, int main(int argc, char *argv[]) { #if HAVE_XZ || HAVE_LZ4 - const char *i; - int r; - - log_set_max_level(LOG_INFO); + test_setup_logging(LOG_INFO); if (argc >= 2) { unsigned x; assert_se(safe_atou(argv[1], &x) >= 0); arg_duration = x * USEC_PER_SEC; - } else { - bool slow; - - r = getenv_bool("SYSTEMD_SLOW_TESTS"); - slow = r >= 0 ? r : SYSTEMD_SLOW_TESTS_DEFAULT; - - arg_duration = slow ? 2 * USEC_PER_SEC : USEC_PER_SEC / 50; - } + } else + arg_duration = slow_tests_enabled() ? + 2 * USEC_PER_SEC : USEC_PER_SEC / 50; if (argc == 3) (void) safe_atozu(argv[2], &arg_start); else arg_start = getpid_cached(); + const char *i; NULSTR_FOREACH(i, "zeros\0simple\0random\0") { #if HAVE_XZ test_compress_decompress("XZ", i, compress_blob_xz, decompress_blob_xz); @@ -176,6 +170,6 @@ int main(int argc, char *argv[]) { } return 0; #else - return EXIT_TEST_SKIP; + return log_tests_skipped("No compression feature is enabled"); #endif } diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index 791c6fdffb..1b050b7052 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -7,11 +7,12 @@ #include "alloc-util.h" #include "compress.h" #include "fd-util.h" -#include "fileio.h" #include "fs-util.h" #include "macro.h" #include "path-util.h" #include "random-util.h" +#include "tests.h" +#include "tmpfile-util.h" #include "util.h" #if HAVE_XZ @@ -131,6 +132,32 @@ static void test_decompress_startswith(int compression, assert_se(r > 0); } +static void test_decompress_startswith_short(int compression, + compress_blob_t compress, + decompress_sw_t decompress_sw) { + +#define TEXT "HUGE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + + char buf[1024]; + size_t i, csize; + int r; + + log_info("/* %s with %s */", __func__, object_compressed_to_string(compression)); + + r = compress(TEXT, sizeof TEXT, buf, sizeof buf, &csize); + assert_se(r == 0); + + for (i = 1; i < strlen(TEXT); i++) { + size_t alloc_size = i; + _cleanup_free_ void *buf2 = NULL; + + assert_se(buf2 = malloc(i)); + + assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, TEXT[i]) == 1); + assert_se(decompress_sw(buf, csize, &buf2, &alloc_size, TEXT, i, 'y') == 0); + } +} + static void test_compress_stream(int compression, const char* cat, compress_stream_t compress, @@ -197,21 +224,17 @@ static void test_compress_stream(int compression, #if HAVE_LZ4 static void test_lz4_decompress_partial(void) { - char buf[20000]; + char buf[20000], buf2[100]; size_t buf_size = sizeof(buf), compressed; int r; _cleanup_free_ char *huge = NULL; #define HUGE_SIZE (4096*1024) - huge = malloc(HUGE_SIZE); + assert_se(huge = malloc(HUGE_SIZE)); memset(huge, 'x', HUGE_SIZE); memcpy(huge, "HUGE=", 5); -#if LZ4_VERSION_NUMBER >= 10700 r = LZ4_compress_default(huge, buf, HUGE_SIZE, buf_size); -#else - r = LZ4_compress_limitedOutput(huge, buf, HUGE_SIZE, buf_size); -#endif assert_se(r >= 0); compressed = r; log_info("Compressed %i → %zu", HUGE_SIZE, compressed); @@ -226,14 +249,15 @@ static void test_lz4_decompress_partial(void) { assert_se(r >= 0); log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE, r); - /* We expect this to fail, because that's how current lz4 works. If this - * call succeeds, then lz4 has been fixed, and we need to change our code. - */ - r = LZ4_decompress_safe_partial(buf, huge, - compressed, - 12, HUGE_SIZE-1); - assert_se(r < 0); - log_info("Decompressed partial %i/%i → %i", 12, HUGE_SIZE-1, r); + for (size_t size = 1; size < sizeof(buf2); size++) { + /* This failed in older lz4s but works in newer ones. */ + r = LZ4_decompress_safe_partial(buf, buf2, compressed, size, size); + log_info("Decompressed partial %zu/%zu → %i (%s)", size, size, r, + r < 0 ? "bad" : "good"); + if (r >= 0 && LZ4_versionNumber() >= 10803) + /* lz4 <= 1.8.2 should fail that test, let's only check for newer ones */ + assert_se(memcmp(buf2, huge, r) == 0); + } } #endif @@ -253,7 +277,7 @@ int main(int argc, char *argv[]) { memcpy(huge, "HUGE=", 5); char_array_0(huge); - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); random_bytes(data + 7, sizeof(data) - 7); @@ -275,6 +299,9 @@ int main(int argc, char *argv[]) { test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat", compress_stream_xz, decompress_stream_xz, srcfile); + + test_decompress_startswith_short(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_startswith_xz); + #else log_info("/* XZ test skipped */"); #endif @@ -299,12 +326,16 @@ int main(int argc, char *argv[]) { compress_stream_lz4, decompress_stream_lz4, srcfile); test_lz4_decompress_partial(); + + test_decompress_startswith_short(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_startswith_lz4); + #else log_info("/* LZ4 test skipped */"); #endif return 0; #else + log_info("/* XZ and LZ4 tests skipped */"); return EXIT_TEST_SKIP; #endif } diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c index b25a983498..8e839920b9 100644 --- a/src/journal/test-journal-enum.c +++ b/src/journal/test-journal-enum.c @@ -7,12 +7,13 @@ #include "journal-internal.h" #include "log.h" #include "macro.h" +#include "tests.h" int main(int argc, char *argv[]) { unsigned n = 0; - _cleanup_(sd_journal_closep) sd_journal*j = NULL; + _cleanup_(sd_journal_closep) sd_journal *j = NULL; - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0); diff --git a/src/journal/test-journal-init.c b/src/journal/test-journal-init.c index a43672b6e1..860baca383 100644 --- a/src/journal/test-journal-init.c +++ b/src/journal/test-journal-init.c @@ -5,6 +5,7 @@ #include "log.h" #include "parse-util.h" #include "rm-rf.h" +#include "tests.h" #include "util.h" int main(int argc, char *argv[]) { @@ -12,7 +13,7 @@ int main(int argc, char *argv[]) { int r, i, I = 100; char t[] = "/tmp/journal-stream-XXXXXX"; - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); if (argc >= 2) { r = safe_atoi(argv[1], &I); diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index 1f0c9f8f2a..cf0561df61 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -1,7 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - Copyright © 2013 Marius Vollmer -***/ #include <fcntl.h> #include <unistd.h> @@ -9,15 +6,16 @@ #include "sd-journal.h" #include "alloc-util.h" +#include "io-util.h" #include "journal-file.h" #include "journal-vacuum.h" #include "log.h" #include "parse-util.h" #include "rm-rf.h" +#include "tests.h" #include "util.h" -/* This program tests skipping around in a multi-file journal. - */ +/* This program tests skipping around in a multi-file journal. */ static bool arg_keep = false; @@ -61,8 +59,7 @@ static void append_number(JournalFile *f, int n, uint64_t *seqnum) { previous_ts = ts; assert_se(asprintf(&p, "NUMBER=%d", n) >= 0); - iovec[0].iov_base = p; - iovec[0].iov_len = strlen(p); + iovec[0] = IOVEC_MAKE_STRING(p); assert_ret(journal_file_append_entry(f, &ts, NULL, iovec, 1, seqnum, NULL, NULL)); free(p); } @@ -274,11 +271,11 @@ static void test_sequence_numbers(void) { } int main(int argc, char *argv[]) { - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); /* journal_file_open requires a valid machine id */ if (access("/etc/machine-id", F_OK) != 0) - return EXIT_TEST_SKIP; + return log_tests_skipped("/etc/machine-id not found"); arg_keep = argc > 1; diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c index 4e5ad1791a..b17527916c 100644 --- a/src/journal/test-journal-match.c +++ b/src/journal/test-journal-match.c @@ -8,13 +8,14 @@ #include "journal-internal.h" #include "log.h" #include "string-util.h" +#include "tests.h" #include "util.h" int main(int argc, char *argv[]) { - _cleanup_(sd_journal_closep) sd_journal*j = NULL; + _cleanup_(sd_journal_closep) sd_journal *j = NULL; _cleanup_free_ char *t; - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); assert_se(sd_journal_open(&j, 0) >= 0); @@ -23,6 +24,8 @@ int main(int argc, char *argv[]) { assert_se(sd_journal_add_match(j, "", 0) < 0); assert_se(sd_journal_add_match(j, "=", 0) < 0); assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0); + assert_se(sd_journal_add_match(j, (uint8_t[4]){'A', '=', '\1', '\2'}, 4) >= 0); + assert_se(sd_journal_add_match(j, (uint8_t[5]){'B', '=', 'C', '\0', 'D'}, 5) >= 0); assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0); assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0); assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0); @@ -53,7 +56,7 @@ int main(int argc, char *argv[]) { printf("resulting match expression is: %s\n", t); - assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO))))")); + assert_se(streq(t, "(((L3=ok OR L3=yes) OR ((L4_2=ok OR L4_2=yes) AND (L4_1=ok OR L4_1=yes))) AND ((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO) AND B=C\\000D AND A=\\001\\002)))")); return 0; } diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c index ae35c91eff..226c30f80a 100644 --- a/src/journal/test-journal-stream.c +++ b/src/journal/test-journal-stream.c @@ -12,6 +12,7 @@ #include "macro.h" #include "parse-util.h" #include "rm-rf.h" +#include "tests.h" #include "util.h" #define N_ENTRIES 200 @@ -68,9 +69,9 @@ int main(int argc, char *argv[]) { /* journal_file_open requires a valid machine id */ if (access("/etc/machine-id", F_OK) != 0) - return EXIT_TEST_SKIP; + return log_tests_skipped("/etc/machine-id not found"); - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c index 9ba86f6c8a..45be7e5bc4 100644 --- a/src/journal/test-journal-syslog.c +++ b/src/journal/test-journal-syslog.c @@ -4,9 +4,10 @@ #include "journald-syslog.h" #include "macro.h" #include "string-util.h" +#include "syslog-util.h" -static void test_syslog_parse_identifier(const char* str, - const char *ident, const char*pid, int ret) { +static void test_syslog_parse_identifier(const char *str, + const char *ident, const char *pid, const char *rest, int ret) { const char *buf = str; _cleanup_free_ char *ident2 = NULL, *pid2 = NULL; int ret2; @@ -16,12 +17,43 @@ static void test_syslog_parse_identifier(const char* str, assert_se(ret == ret2); assert_se(ident == ident2 || streq_ptr(ident, ident2)); assert_se(pid == pid2 || streq_ptr(pid, pid2)); + assert_se(streq(buf, rest)); +} + +static void test_syslog_parse_priority(const char *str, int priority, int ret) { + const char *buf = str; + int priority2 = 0, ret2; + + ret2 = syslog_parse_priority(&buf, &priority2, false); + + assert_se(ret == ret2); + if (ret2 == 1) + assert_se(priority == priority2); } int main(void) { - test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11); - test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6); - test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0); + test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", "xxx", 11); + test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, "xxx", 6); + test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, " xxx", 6); + test_syslog_parse_identifier("pidu xxx", NULL, NULL, "pidu xxx", 0); + test_syslog_parse_identifier(" pidu xxx", NULL, NULL, " pidu xxx", 0); + test_syslog_parse_identifier("", NULL, NULL, "", 0); + test_syslog_parse_identifier(" ", NULL, NULL, " ", 0); + test_syslog_parse_identifier(":", "", NULL, "", 1); + test_syslog_parse_identifier(": ", "", NULL, " ", 2); + test_syslog_parse_identifier(" :", "", NULL, "", 2); + test_syslog_parse_identifier(" pidu:", "pidu", NULL, "", 8); + test_syslog_parse_identifier("pidu:", "pidu", NULL, "", 5); + test_syslog_parse_identifier("pidu: ", "pidu", NULL, "", 6); + test_syslog_parse_identifier("pidu : ", NULL, NULL, "pidu : ", 0); + + test_syslog_parse_priority("<>", 0, 0); + test_syslog_parse_priority("<>aaa", 0, 0); + test_syslog_parse_priority("<aaaa>", 0, 0); + test_syslog_parse_priority("<aaaa>aaa", 0, 0); + test_syslog_parse_priority(" <aaaa>", 0, 0); + test_syslog_parse_priority(" <aaaa>aaa", 0, 0); + /* TODO: add test cases of valid priorities */ return 0; } diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index c1c3a82c42..c4fa41e076 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -5,11 +5,13 @@ #include <unistd.h> #include "fd-util.h" +#include "io-util.h" #include "journal-file.h" #include "journal-verify.h" #include "log.h" #include "rm-rf.h" #include "terminal-util.h" +#include "tests.h" #include "util.h" #define N_ENTRIES 6000 @@ -62,9 +64,9 @@ int main(int argc, char *argv[]) { /* journal_file_open requires a valid machine id */ if (access("/etc/machine-id", F_OK) != 0) - return EXIT_TEST_SKIP; + return log_tests_skipped("/etc/machine-id not found"); - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); @@ -82,8 +84,7 @@ int main(int argc, char *argv[]) { assert_se(asprintf(&test, "RANDOM=%lu", random() % RANDOM_RANGE)); - iovec.iov_base = (void*) test; - iovec.iov_len = strlen(test); + iovec = IOVEC_MAKE_STRING(test); assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0); diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 69bdff6760..0795e0da0a 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -3,11 +3,13 @@ #include <fcntl.h> #include <unistd.h> +#include "io-util.h" #include "journal-authenticate.h" #include "journal-file.h" #include "journal-vacuum.h" #include "log.h" #include "rm-rf.h" +#include "tests.h" static bool arg_keep = false; @@ -21,7 +23,7 @@ static void test_non_empty(void) { sd_id128_t fake_boot_id; char t[] = "/tmp/journal-XXXXXX"; - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); @@ -31,16 +33,13 @@ static void test_non_empty(void) { assert_se(dual_timestamp_get(&ts)); assert_se(sd_id128_randomize(&fake_boot_id) == 0); - iovec.iov_base = (void*) test; - iovec.iov_len = strlen(test); + iovec = IOVEC_MAKE_STRING(test); assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0); - iovec.iov_base = (void*) test2; - iovec.iov_len = strlen(test2); + iovec = IOVEC_MAKE_STRING(test2); assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0); - iovec.iov_base = (void*) test; - iovec.iov_len = strlen(test); + iovec = IOVEC_MAKE_STRING(test); assert_se(journal_file_append_entry(f, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL) == 0); #if HAVE_GCRYPT @@ -112,7 +111,7 @@ static void test_empty(void) { JournalFile *f1, *f2, *f3, *f4; char t[] = "/tmp/journal-XXXXXX"; - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); @@ -164,7 +163,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { assert_se(data_size <= sizeof(data)); - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); @@ -173,8 +172,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { dual_timestamp_get(&ts); - iovec.iov_base = (void*) data; - iovec.iov_len = data_size; + iovec = IOVEC_MAKE(data, data_size); assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL) == 0); #if HAVE_GCRYPT @@ -238,9 +236,11 @@ static void test_min_compress_size(void) { int main(int argc, char *argv[]) { arg_keep = argc > 1; + test_setup_logging(LOG_INFO); + /* journal_file_open requires a valid machine id */ if (access("/etc/machine-id", F_OK) != 0) - return EXIT_TEST_SKIP; + return log_tests_skipped("/etc/machine-id not found"); test_non_empty(); test_empty(); diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c index 5c55b35f57..8f755efdde 100644 --- a/src/journal/test-mmap-cache.c +++ b/src/journal/test-mmap-cache.c @@ -6,9 +6,9 @@ #include <unistd.h> #include "fd-util.h" -#include "fileio.h" #include "macro.h" #include "mmap-cache.h" +#include "tmpfile-util.h" #include "util.h" int main(int argc, char *argv[]) { |