diff options
Diffstat (limited to 'src')
29 files changed, 697 insertions, 174 deletions
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 51268828af..e24e7036f7 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -38,6 +38,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" @@ -493,3 +494,17 @@ int get_files_in_directory(const char *path, char ***list) { return n; } + +int inotify_add_watch_fd(int fd, int what, uint32_t mask) { + char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + int r; + + /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ + xsprintf(path, "/proc/self/fd/%i", what); + + r = inotify_add_watch(fd, path, mask); + if (r < 0) + return -errno; + + return r; +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 0d23f8635f..517b599d6f 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -72,3 +72,5 @@ union inotify_event_buffer { struct inotify_event ev; uint8_t raw[INOTIFY_EVENT_MAX]; }; + +int inotify_add_watch_fd(int fd, int what, uint32_t mask); diff --git a/src/core/service.h b/src/core/service.h index a5ced215e4..cd9e41646e 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -80,7 +80,7 @@ typedef enum NotifyState { typedef enum ServiceResult { SERVICE_SUCCESS, - SERVICE_FAILURE_RESOURCES, + SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */ SERVICE_FAILURE_TIMEOUT, SERVICE_FAILURE_EXIT_CODE, SERVICE_FAILURE_SIGNAL, diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index bed825cdc3..ff01e5aa94 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -37,6 +37,7 @@ #include "journal-file.h" #include "lookup3.h" #include "parse-util.h" +#include "path-util.h" #include "random-util.h" #include "sd-event.h" #include "set.h" @@ -217,8 +218,10 @@ int journal_file_set_offline(JournalFile *f, bool wait) { if (!(f->fd >= 0 && f->header)) return -EINVAL; - if (f->header->state != STATE_ONLINE) - return 0; + /* An offlining journal is implicitly online and may modify f->header->state, + * we must also join any potentially lingering offline thread when not online. */ + if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE) + return journal_file_set_offline_thread_join(f); /* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */ restarted = journal_file_set_offline_try_restart(f); @@ -362,7 +365,8 @@ JournalFile* journal_file_close(JournalFile *f) { (void) btrfs_defrag_fd(f->fd); } - safe_close(f->fd); + if (f->close_fd) + safe_close(f->fd); free(f->path); mmap_cache_unref(f->mmap); @@ -703,7 +707,11 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset /* Objects may only be located at multiple of 64 bit */ if (!VALID64(offset)) - return -EFAULT; + return -EBADMSG; + + /* Object may not be located in the file header */ + if (offset < le64toh(f->header->header_size)) + return -EBADMSG; r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t); if (r < 0) @@ -1976,9 +1984,14 @@ static int generic_array_bisect( i = right - 1; lp = p = le64toh(array->entry_array.items[i]); if (p <= 0) - return -EBADMSG; - - r = test_object(f, p, needle); + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)"); + n = i; + continue; + } if (r < 0) return r; @@ -2054,9 +2067,14 @@ static int generic_array_bisect( p = le64toh(array->entry_array.items[i]); if (p <= 0) - return -EBADMSG; - - r = test_object(f, p, needle); + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)"); + right = n = i; + continue; + } if (r < 0) return r; @@ -2461,13 +2479,18 @@ int journal_file_next_entry( le64toh(f->header->entry_array_offset), i, ret, &ofs); + if (r == -EBADMSG && direction == DIRECTION_DOWN) { + /* Special case: when we iterate throught the journal file linearly, and hit an entry we can't read, + * consider this the end of the journal file. */ + log_debug_errno(r, "Encountered entry we can't read while iterating through journal file. Considering this the end of the file."); + return 0; + } if (r <= 0) return r; if (p > 0 && (direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) { - log_debug("%s: entry array corrupted at entry %"PRIu64, - f->path, i); + log_debug("%s: entry array corrupted at entry %" PRIu64, f->path, i); return -EBADMSG; } @@ -2806,11 +2829,11 @@ void journal_file_print_header(JournalFile *f) { "Data Hash Table Size: %"PRIu64"\n" "Field Hash Table Size: %"PRIu64"\n" "Rotate Suggested: %s\n" - "Head Sequential Number: %"PRIu64"\n" - "Tail Sequential Number: %"PRIu64"\n" - "Head Realtime Timestamp: %s\n" - "Tail Realtime Timestamp: %s\n" - "Tail Monotonic Timestamp: %s\n" + "Head Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Tail Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Head Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Monotonic Timestamp: %s (%"PRIx64")\n" "Objects: %"PRIu64"\n" "Entry Objects: %"PRIu64"\n", f->path, @@ -2831,11 +2854,11 @@ void journal_file_print_header(JournalFile *f) { le64toh(f->header->data_hash_table_size) / sizeof(HashItem), le64toh(f->header->field_hash_table_size) / sizeof(HashItem), yes_no(journal_file_rotate_suggested(f, 0)), - le64toh(f->header->head_entry_seqnum), - le64toh(f->header->tail_entry_seqnum), - format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), - format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), - format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), + le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum), + le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum), + format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime), + format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime), + format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic), le64toh(f->header->n_objects), le64toh(f->header->n_entries)); @@ -2898,6 +2921,7 @@ static int journal_file_warn_btrfs(JournalFile *f) { } int journal_file_open( + int fd, const char *fname, int flags, mode_t mode, @@ -2914,22 +2938,24 @@ int journal_file_open( void *h; int r; - assert(fname); assert(ret); + assert(fd >= 0 || fname); if ((flags & O_ACCMODE) != O_RDONLY && (flags & O_ACCMODE) != O_RDWR) return -EINVAL; - if (!endswith(fname, ".journal") && - !endswith(fname, ".journal~")) - return -EINVAL; + if (fname) { + if (!endswith(fname, ".journal") && + !endswith(fname, ".journal~")) + return -EINVAL; + } f = new0(JournalFile, 1); if (!f) return -ENOMEM; - f->fd = -1; + f->fd = fd; f->mode = mode; f->flags = flags; @@ -2954,7 +2980,10 @@ int journal_file_open( } } - f->path = strdup(fname); + if (fname) + f->path = strdup(fname); + else /* If we don't know the path, fill in something explanatory and vaguely useful */ + asprintf(&f->path, "/proc/self/%i", fd); if (!f->path) { r = -ENOMEM; goto fail; @@ -2966,10 +2995,15 @@ int journal_file_open( goto fail; } - f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); if (f->fd < 0) { - r = -errno; - goto fail; + f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); + if (f->fd < 0) { + r = -errno; + goto fail; + } + + /* fds we opened here by us should also be closed by us. */ + f->close_fd = true; } r = journal_file_fstat(f); @@ -3090,6 +3124,9 @@ int journal_file_open( goto fail; } + /* The file is opened now successfully, thus we take possesion of any passed in fd. */ + f->close_fd = true; + *ret = f; return 0; @@ -3116,6 +3153,11 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred if (!old_file->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")) + return -EINVAL; + if (!endswith(old_file->path, ".journal")) return -EINVAL; @@ -3142,7 +3184,7 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred * we archive them */ old_file->defrag_on_close = true; - r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file); + r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file); if (deferred_closes && set_put(deferred_closes, old_file) >= 0) @@ -3170,7 +3212,7 @@ int journal_file_open_reliably( size_t l; _cleanup_free_ char *p = NULL; - r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); + r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); if (!IN_SET(r, -EBADMSG, /* corrupted */ -ENODATA, /* truncated */ @@ -3211,7 +3253,7 @@ int journal_file_open_reliably( log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname); - return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); + return journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); } int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) { diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 9ad6013359..a0ec8c284b 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -85,6 +85,7 @@ typedef struct JournalFile { bool compress_lz4:1; bool seal:1; bool defrag_on_close:1; + bool close_fd:1; bool tail_entry_monotonic_valid:1; @@ -142,6 +143,7 @@ typedef struct JournalFile { } JournalFile; int journal_file_open( + int fd, const char *fname, int flags, mode_t mode, diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 7639325acf..34a48141f5 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -82,6 +82,8 @@ struct Directory { }; struct sd_journal { + int toplevel_fd; + char *path; char *prefix; @@ -117,6 +119,7 @@ struct sd_journal { bool on_network:1; bool no_new_files:1; + bool no_inotify:1; bool unique_file_lost:1; /* File we were iterating over got removed, and there were no more files, so sd_j_enumerate_unique diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index c9a2c3812d..f67c556783 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -101,6 +101,7 @@ static const char *arg_after_cursor = NULL; static bool arg_show_cursor = false; static const char *arg_directory = NULL; static char **arg_file = NULL; +static bool arg_file_stdin = false; static int arg_priorities = 0xFF; static const char *arg_verify_key = NULL; #ifdef HAVE_GCRYPT @@ -592,9 +593,17 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_FILE: - r = glob_extend(&arg_file, optarg); - if (r < 0) - return log_error_errno(r, "Failed to add paths: %m"); + if (streq(optarg, "-")) + /* An undocumented feature: we can read journal files from STDIN. We don't document + * this though, since after all we only support this for mmap-able, seekable files, and + * not for example pipes which are probably the primary usecase for reading things from + * STDIN. To avoid confusion we hence don't document this feature. */ + arg_file_stdin = true; + else { + r = glob_extend(&arg_file, optarg); + if (r < 0) + return log_error_errno(r, "Failed to add paths: %m"); + } break; case ARG_ROOT: @@ -864,6 +873,18 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (!strv_isempty(arg_system_units) && (arg_journal_type == SD_JOURNAL_CURRENT_USER)) { + + /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user + * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice + * to users, and automatically turn --unit= into --user-unit= if combined with --user. */ + r = strv_extend_strv(&arg_user_units, arg_system_units, true); + if (r < 0) + return -ENOMEM; + + arg_system_units = strv_free(arg_system_units); + } + return 1; } @@ -988,18 +1009,18 @@ static void boot_id_free_all(BootId *l) { } } -static int discover_next_boot( - sd_journal *j, - BootId **boot, +static int discover_next_boot(sd_journal *j, + sd_id128_t previous_boot_id, bool advance_older, - bool read_realtime) { + BootId **ret) { - int r; - char match[9+32+1] = "_BOOT_ID="; _cleanup_free_ BootId *next_boot = NULL; + char match[9+32+1] = "_BOOT_ID="; + sd_id128_t boot_id; + int r; assert(j); - assert(boot); + assert(ret); /* We expect the journal to be on the last position of a boot * (in relation to the direction we are going), so that the next @@ -1012,29 +1033,40 @@ static int discover_next_boot( * we can actually advance to a *different* boot. */ sd_journal_flush_matches(j); - if (advance_older) - r = sd_journal_previous(j); - else - r = sd_journal_next(j); - if (r < 0) - return r; - else if (r == 0) - return 0; /* End of journal, yay. */ + do { + if (advance_older) + r = sd_journal_previous(j); + else + r = sd_journal_next(j); + if (r < 0) + return r; + else if (r == 0) + return 0; /* End of journal, yay. */ + + r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); + if (r < 0) + return r; + + /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that + * normally, this will only require a single iteration, as we seeked to the last entry of the previous + * boot entry already. However, it might happen that the per-journal-field entry arrays are less + * complete than the main entry array, and hence might reference an entry that's not actually the last + * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to + * speed things up, but let's not trust that it is complete, and hence, manually advance as + * necessary. */ + + } while (sd_id128_equal(boot_id, previous_boot_id)); next_boot = new0(BootId, 1); if (!next_boot) return -ENOMEM; - r = sd_journal_get_monotonic_usec(j, NULL, &next_boot->id); + next_boot->id = boot_id; + + r = sd_journal_get_realtime_usec(j, &next_boot->first); if (r < 0) return r; - if (read_realtime) { - r = sd_journal_get_realtime_usec(j, &next_boot->first); - if (r < 0) - return r; - } - /* Now seek to the last occurrence of this boot ID. */ sd_id128_to_string(next_boot->id, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); @@ -1057,13 +1089,11 @@ static int discover_next_boot( else if (r == 0) return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */ - if (read_realtime) { - r = sd_journal_get_realtime_usec(j, &next_boot->last); - if (r < 0) - return r; - } + r = sd_journal_get_realtime_usec(j, &next_boot->last); + if (r < 0) + return r; - *boot = next_boot; + *ret = next_boot; next_boot = NULL; return 0; @@ -1072,47 +1102,48 @@ static int discover_next_boot( static int get_boots( sd_journal *j, BootId **boots, - BootId *query_ref_boot, + sd_id128_t *query_ref_boot, int ref_boot_offset) { bool skip_once; int r, count = 0; BootId *head = NULL, *tail = NULL; const bool advance_older = query_ref_boot && ref_boot_offset <= 0; + sd_id128_t previous_boot_id; assert(j); /* Adjust for the asymmetry that offset 0 is * the last (and current) boot, while 1 is considered the * (chronological) first boot in the journal. */ - skip_once = query_ref_boot && sd_id128_is_null(query_ref_boot->id) && ref_boot_offset < 0; + skip_once = query_ref_boot && sd_id128_is_null(*query_ref_boot) && ref_boot_offset < 0; /* Advance to the earliest/latest occurrence of our reference * boot ID (taking our lookup direction into account), so that * discover_next_boot() can do its job. * If no reference is given, the journal head/tail will do, * they're "virtual" boots after all. */ - if (query_ref_boot && !sd_id128_is_null(query_ref_boot->id)) { + if (query_ref_boot && !sd_id128_is_null(*query_ref_boot)) { char match[9+32+1] = "_BOOT_ID="; sd_journal_flush_matches(j); - sd_id128_to_string(query_ref_boot->id, match + 9); + sd_id128_to_string(*query_ref_boot, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); if (r < 0) return r; if (advance_older) - r = sd_journal_seek_head(j); + r = sd_journal_seek_head(j); /* seek to oldest */ else - r = sd_journal_seek_tail(j); + r = sd_journal_seek_tail(j); /* seek to newest */ if (r < 0) return r; if (advance_older) - r = sd_journal_next(j); + r = sd_journal_next(j); /* read the oldest entry */ else - r = sd_journal_previous(j); + r = sd_journal_previous(j); /* read the most recently added entry */ if (r < 0) return r; else if (r == 0) @@ -1121,21 +1152,31 @@ static int get_boots( count = 1; goto finish; } + + /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot + * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at + * the following entry, which must then have an older/newer boot ID */ } else { + if (advance_older) - r = sd_journal_seek_tail(j); + r = sd_journal_seek_tail(j); /* seek to newest */ else - r = sd_journal_seek_head(j); + r = sd_journal_seek_head(j); /* seek to oldest */ if (r < 0) return r; - /* No sd_journal_next/previous here. */ + /* No sd_journal_next()/_previous() here. + * + * At this point the read pointer is positioned after the newest/before the oldest entry in the whole + * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest + * entry we have. */ } + previous_boot_id = SD_ID128_NULL; for (;;) { _cleanup_free_ BootId *current = NULL; - r = discover_next_boot(j, ¤t, advance_older, !query_ref_boot); + r = discover_next_boot(j, previous_boot_id, advance_older, ¤t); if (r < 0) { boot_id_free_all(head); return r; @@ -1144,6 +1185,8 @@ static int get_boots( if (!current) break; + previous_boot_id = current->id; + if (query_ref_boot) { if (!skip_once) ref_boot_offset += advance_older ? 1 : -1; @@ -1151,7 +1194,7 @@ static int get_boots( if (ref_boot_offset == 0) { count = 1; - query_ref_boot->id = current->id; + *query_ref_boot = current->id; break; } } else { @@ -1207,8 +1250,8 @@ static int list_boots(sd_journal *j) { static int add_boot(sd_journal *j) { char match[9+32+1] = "_BOOT_ID="; + sd_id128_t ref_boot_id; int r; - BootId ref_boot_id = {}; assert(j); @@ -1218,7 +1261,7 @@ static int add_boot(sd_journal *j) { if (arg_boot_offset == 0 && sd_id128_equal(arg_boot_id, SD_ID128_NULL)) return add_match_this_boot(j, arg_machine); - ref_boot_id.id = arg_boot_id; + ref_boot_id = arg_boot_id; r = get_boots(j, NULL, &ref_boot_id, arg_boot_offset); assert(r <= 1); if (r <= 0) { @@ -1234,7 +1277,7 @@ static int add_boot(sd_journal *j) { return r == 0 ? -ENODATA : r; } - sd_id128_to_string(ref_boot_id.id, match + 9); + sd_id128_to_string(ref_boot_id, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); if (r < 0) @@ -1847,7 +1890,7 @@ static int access_check(sd_journal *j) { break; default: - log_warning_errno(err, "An error was encountered while opening journal file %s, ignoring file.", path); + log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path); break; } } @@ -2103,11 +2146,61 @@ int main(int argc, char *argv[]) { if (arg_directory) r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); - else if (arg_file) + else if (arg_file_stdin) { + int ifd = STDIN_FILENO; + r = sd_journal_open_files_fd(&j, &ifd, 1, 0); + } else if (arg_file) r = sd_journal_open_files(&j, (const char**) arg_file, 0); - else if (arg_machine) - r = sd_journal_open_container(&j, arg_machine, 0); - else + else if (arg_machine) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int fd; + + if (geteuid() != 0) { + /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of + * the container, thus we need root privileges to override them. */ + log_error("Using the --machine= switch requires root privileges."); + r = -EPERM; + goto finish; + } + + r = sd_bus_open_system(&bus); + if (r < 0) { + log_error_errno(r, "Failed to open system bus: %m"); + goto finish; + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineRootDirectory", + &error, + &reply, + "s", arg_machine); + if (r < 0) { + log_error_errno(r, "Failed to open root directory: %s", bus_error_message(&error, r)); + goto finish; + } + + r = sd_bus_message_read(reply, "h", &fd); + if (r < 0) { + bus_log_parse_error(r); + goto finish; + } + + fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (fd < 0) { + r = log_error_errno(errno, "Failed to duplicate file descriptor: %m"); + goto finish; + } + + r = sd_journal_open_directory_fd(&j, fd, SD_JOURNAL_OS_ROOT); + if (r < 0) + safe_close(fd); + } else r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) { log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal"); @@ -2283,6 +2376,10 @@ 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 == -EMEDIUMTYPE) { + log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN."); + goto finish; + } if (r < 0) { log_error_errno(r, "Failed to get journal fd: %m"); goto finish; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 8089bb5883..e14d0ad980 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -253,7 +253,7 @@ static int open_journal( if (reliably) r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); else - r = journal_file_open(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); + r = journal_file_open(-1, fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); if (r < 0) return r; diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 3c21d4129e..27c1dd346f 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -1233,14 +1233,37 @@ static bool file_type_wanted(int flags, const char *filename) { return false; } -static int add_any_file(sd_journal *j, const char *path) { +static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) { + assert(j); + assert(path); + assert(prefix); + + if (j->toplevel_fd >= 0) + return false; + + return path_startswith(path, prefix); +} + +static const char *skip_slash(const char *p) { + + if (!p) + return NULL; + + while (*p == '/') + p++; + + return p; +} + +static int add_any_file(sd_journal *j, int fd, const char *path) { JournalFile *f = NULL; + bool close_fd = false; int r, k; assert(j); - assert(path); + assert(fd >= 0 || path); - if (ordered_hashmap_get(j->files, path)) + if (path && ordered_hashmap_get(j->files, path)) return 0; if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) { @@ -1249,8 +1272,24 @@ static int add_any_file(sd_journal *j, const char *path) { goto fail; } - r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f); + if (fd < 0 && j->toplevel_fd >= 0) { + + /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative, + * explicitly, since otherwise openat() ignores the first argument.) */ + + fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC); + if (fd < 0) { + r = log_debug_errno(errno, "Failed to open journal file %s: %m", path); + goto fail; + } + + close_fd = true; + } + + r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f); if (r < 0) { + if (close_fd) + safe_close(fd); log_debug_errno(r, "Failed to open journal file %s: %m", path); goto fail; } @@ -1259,10 +1298,16 @@ static int add_any_file(sd_journal *j, const char *path) { r = ordered_hashmap_put(j->files, f->path, f); if (r < 0) { + f->close_fd = close_fd; (void) journal_file_close(f); goto fail; } + if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run")) + j->has_runtime_files = true; + else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var")) + j->has_persistent_files = true; + log_debug("File %s added.", f->path); check_network(j, f->fd); @@ -1286,18 +1331,14 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { assert(prefix); assert(filename); - if (j->no_new_files || - !file_type_wanted(j->flags, filename)) + if (j->no_new_files) + return 0; + + if (!file_type_wanted(j->flags, filename)) return 0; path = strjoina(prefix, "/", filename); - - if (!j->has_runtime_files && path_startswith(path, "/run/log/journal")) - j->has_runtime_files = true; - else if (!j->has_persistent_files && path_startswith(path, "/var/log/journal")) - j->has_persistent_files = true; - - return add_any_file(j, path); + return add_any_file(j, -1, path); } static void remove_file(sd_journal *j, const char *prefix, const char *filename) { @@ -1373,21 +1414,33 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) assert(j); assert(prefix); - assert(dirname); - - log_debug("Considering %s/%s.", prefix, dirname); - if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && - !(dirname_is_machine_id(dirname) > 0 || path_startswith(prefix, "/run"))) - return 0; + /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch + * and reenumerates directory contents */ - path = strjoin(prefix, "/", dirname, NULL); + if (dirname) + path = strjoin(prefix, "/", dirname, NULL); + else + path = strdup(prefix); if (!path) { r = -ENOMEM; goto fail; } - d = opendir(path); + log_debug("Considering directory %s.", path); + + /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */ + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && + !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run"))) + return 0; + + + if (j->toplevel_fd < 0) + d = opendir(path); + else + /* Open the specified directory relative to the the toplevel fd. Enforce that the path specified is + * relative, by dropping the initial slash */ + d = xopendirat(j->toplevel_fd, skip_slash(path), 0); if (!d) { r = log_debug_errno(errno, "Failed to open directory %s: %m", path); goto fail; @@ -1419,17 +1472,18 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) return 0; if (m->wd <= 0 && j->inotify_fd >= 0) { + /* Watch this directory, if it not being watched yet. */ - m->wd = inotify_add_watch(j->inotify_fd, m->path, - IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| - IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| - IN_ONLYDIR); + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| + IN_ONLYDIR); if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) inotify_rm_watch(j->inotify_fd, m->wd); } - FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) { + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { if (dirent_is_file_with_suffix(de, ".journal") || dirent_is_file_with_suffix(de, ".journal~")) @@ -1441,7 +1495,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) return 0; fail: - k = journal_put_error(j, r, path ?: dirname); + k = journal_put_error(j, r, path ?: prefix); if (k < 0) return k; @@ -1449,28 +1503,62 @@ fail: } static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { + _cleanup_closedir_ DIR *d = NULL; struct dirent *de; Directory *m; int r, k; assert(j); - assert(p); - if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && - !path_startswith(p, "/run")) - return -EINVAL; + /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we + * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially + * populate the set, as well as to update it later. */ - if (j->prefix) - p = strjoina(j->prefix, p); + if (p) { + /* If there's a path specified, use it. */ - d = opendir(p); - if (!d) { - if (errno == ENOENT && missing_ok) - return 0; + if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && + !path_has_prefix(j, p, "/run")) + return -EINVAL; - r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); - goto fail; + if (j->prefix) + p = strjoina(j->prefix, p); + + if (j->toplevel_fd < 0) + d = opendir(p); + else + d = xopendirat(j->toplevel_fd, skip_slash(p), 0); + + if (!d) { + if (errno == ENOENT && missing_ok) + return 0; + + r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); + goto fail; + } + } else { + int dfd; + + /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since + * opendir() will take possession of the fd, and close it, which we don't want. */ + + p = "."; /* store this as "." in the directories hashmap */ + + dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3); + if (dfd < 0) { + r = -errno; + goto fail; + } + + d = fdopendir(dfd); + if (!d) { + r = -errno; + safe_close(dfd); + goto fail; + } + + rewinddir(d); } m = hashmap_get(j->directories_by_path, p); @@ -1482,6 +1570,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { } m->is_root = true; + m->path = strdup(p); if (!m->path) { free(m); @@ -1505,7 +1594,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { if (m->wd <= 0 && j->inotify_fd >= 0) { - m->wd = inotify_add_watch(j->inotify_fd, m->path, + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| IN_ONLYDIR); @@ -1516,7 +1605,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { if (j->no_new_files) return 0; - FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) { + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { sd_id128_t id; if (dirent_is_file_with_suffix(de, ".journal") || @@ -1585,8 +1674,7 @@ static int add_current_paths(sd_journal *j) { assert(j); assert(j->no_new_files); - /* Simply adds all directories for files we have open as - * "root" directories. We don't expect errors here, so we + /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we * treat them as fatal. */ ORDERED_HASHMAP_FOREACH(f, j->files, i) { @@ -1597,7 +1685,7 @@ static int add_current_paths(sd_journal *j) { if (!dir) return -ENOMEM; - r = add_root_directory(j, dir, true); + r = add_directory(j, dir, NULL); if (r < 0) return r; } @@ -1614,13 +1702,7 @@ static int allocate_inotify(sd_journal *j) { return -errno; } - if (!j->directories_by_wd) { - j->directories_by_wd = hashmap_new(NULL); - if (!j->directories_by_wd) - return -ENOMEM; - } - - return 0; + return hashmap_ensure_allocated(&j->directories_by_wd, NULL); } static sd_journal *journal_new(int flags, const char *path) { @@ -1631,6 +1713,7 @@ static sd_journal *journal_new(int flags, const char *path) { return NULL; j->original_pid = getpid(); + j->toplevel_fd = -1; j->inotify_fd = -1; j->flags = flags; j->data_threshold = DEFAULT_DATA_THRESHOLD; @@ -1684,6 +1767,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in char *p; int r; + /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in + * combination with sd_journal_open_directory_fd(). */ + assert_return(machine, -EINVAL); assert_return(ret, -EINVAL); assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); @@ -1726,13 +1812,16 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f assert_return(ret, -EINVAL); assert_return(path, -EINVAL); - assert_return(flags == 0, -EINVAL); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); j = journal_new(flags, path); if (!j) return -ENOMEM; - r = add_root_directory(j, path, false); + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, path, false); if (r < 0) goto fail; @@ -1741,7 +1830,6 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f fail: sd_journal_close(j); - return r; } @@ -1758,7 +1846,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla return -ENOMEM; STRV_FOREACH(path, paths) { - r = add_any_file(j, *path); + r = add_any_file(j, -1, *path); if (r < 0) goto fail; } @@ -1770,7 +1858,96 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla fail: sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { + sd_journal *j; + struct stat st; + int r; + + assert_return(ret, -EINVAL); + assert_return(fd >= 0, -EBADF); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -EBADFD; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + j->toplevel_fd = fd; + + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, NULL, false); + if (r < 0) + goto fail; + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) { + Iterator iterator; + JournalFile *f; + sd_journal *j; + unsigned i; + int r; + + assert_return(ret, -EINVAL); + assert_return(n_fds > 0, -EBADF); + assert_return(flags == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + for (i = 0; i < n_fds; i++) { + struct stat st; + + if (fds[i] < 0) { + r = -EBADF; + goto fail; + } + + if (fstat(fds[i], &st) < 0) { + r = -errno; + goto fail; + } + + if (!S_ISREG(st.st_mode)) { + r = -EBADFD; + goto fail; + } + + r = add_any_file(j, fds[i], NULL); + if (r < 0) + goto fail; + } + + j->no_new_files = true; + j->no_inotify = true; + + *ret = j; + return 0; + +fail: + /* If we fail, make sure we don't take possession of the files we managed to make use of successfuly, and they + * remain open */ + ORDERED_HASHMAP_FOREACH(f, j->files, iterator) + f->close_fd = false; + + sd_journal_close(j); return r; } @@ -2097,6 +2274,9 @@ _public_ int sd_journal_get_fd(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); + if (j->no_inotify) + return -EMEDIUMTYPE; + if (j->inotify_fd >= 0) return j->inotify_fd; @@ -2104,10 +2284,14 @@ _public_ int sd_journal_get_fd(sd_journal *j) { if (r < 0) return r; + log_debug("Reiterating files to get inotify watches established"); + /* Iterate through all dirs again, to add them to the * inotify */ if (j->no_new_files) r = add_current_paths(j); + else if (j->toplevel_fd >= 0) + r = add_root_directory(j, NULL, false); else if (j->path) r = add_root_directory(j, j->path, true); else diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c index 93dc0e0d81..ba8b20b228 100644 --- a/src/journal/test-journal-flush.c +++ b/src/journal/test-journal-flush.c @@ -38,7 +38,7 @@ int main(int argc, char *argv[]) { assert_se(mkdtemp(dn)); fn = strappend(dn, "/test.journal"); - r = journal_file_open(fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal); + r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal); assert_se(r >= 0); r = sd_journal_open(&j, 0); diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index f887f43f0d..5e063f4d04 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -52,7 +52,7 @@ noreturn static void log_assert_errno(const char *text, int eno, const char *fil static JournalFile *test_open(const char *name) { JournalFile *f; - assert_ret(journal_file_open(name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f)); + assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f)); return f; } @@ -216,7 +216,7 @@ static void test_sequence_numbers(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0644, + assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &one) == 0); append_number(one, 1, &seqnum); @@ -233,7 +233,7 @@ static void test_sequence_numbers(void) { memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t)); - assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0644, + assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, one, &two) == 0); assert_se(two->header->state == STATE_ONLINE); @@ -264,7 +264,7 @@ static void test_sequence_numbers(void) { /* restart server */ seqnum = 0; - assert_se(journal_file_open("two.journal", O_RDWR, 0, + assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0, true, false, NULL, NULL, NULL, NULL, &two) == 0); assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id)); diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c index 839ea5a9a5..7e5a980719 100644 --- a/src/journal/test-journal-stream.c +++ b/src/journal/test-journal-stream.c @@ -92,9 +92,9 @@ int main(int argc, char *argv[]) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0); - assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0); - assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0); + assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0); + assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0); + assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0); for (i = 0; i < N_ENTRIES; i++) { char *p, *q; diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index 6b4643cd25..3d2312fc55 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -55,7 +55,7 @@ static int raw_verify(const char *fn, const char *verification_key) { JournalFile *f; int r; - r = journal_file_open(fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f); + r = journal_file_open(-1, fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f); if (r < 0) return r; @@ -88,7 +88,7 @@ int main(int argc, char *argv[]) { log_info("Generating..."); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); for (n = 0; n < N_ENTRIES; n++) { struct iovec iovec; @@ -111,7 +111,7 @@ int main(int argc, char *argv[]) { log_info("Verifying..."); - assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); /* journal_file_print_header(f); */ journal_file_dump(f); diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index ea685af782..2543d64b5b 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -42,7 +42,7 @@ static void test_non_empty(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0); dual_timestamp_get(&ts); @@ -131,13 +131,13 @@ static void test_empty(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0); - assert_se(journal_file_open("test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0); + assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0); - assert_se(journal_file_open("test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0); + assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0); - assert_se(journal_file_open("test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0); + assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0); journal_file_print_header(f1); puts(""); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 4ab637b686..0b3a1708dc 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -489,3 +489,9 @@ global: sd_journal_enumerate_fields; sd_journal_restart_fields; } LIBSYSTEMD_227; + +LIBSYSTEMD_230 { +global: + sd_journal_open_directory_fd; + sd_journal_open_files_fd; +} LIBSYSTEMD_229; diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index 255526bf32..f251536a89 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -402,7 +402,6 @@ int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret, int r; assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL); - assert_return(nlmsg_type != RTM_DELLINK || index > 0, -EINVAL); assert_return(ret, -EINVAL); r = message_new(rtnl, ret, nlmsg_type); diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4 index 7933508f2b..f188a8e548 100644 --- a/src/login/systemd-user.m4 +++ b/src/login/systemd-user.m4 @@ -8,4 +8,5 @@ m4_ifdef(`HAVE_SELINUX', session required pam_selinux.so close session required pam_selinux.so nottys open )m4_dnl +session required pam_loginuid.so session include system-auth diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 73f5112c4d..b764bc43a0 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -137,6 +137,8 @@ int bus_image_method_clone( return 1; /* Will call us back */ r = image_clone(image, new_name, read_only); + if (r == -EOPNOTSUPP) + return sd_bus_reply_method_errnof(message, r, "Image cloning is currently only supported on btrfs file systems."); if (r < 0) return r; diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index ab54d9e934..5121bfdd18 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -1274,6 +1274,97 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro return 1; } +int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_close_ int fd = -1; + Machine *m = userdata; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + switch (m->class) { + + case MACHINE_HOST: + fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return -errno; + + break; + + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + siginfo_t si; + pid_t child; + + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int dfd = -1; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + _exit(EXIT_FAILURE); + + r = send_one_fd(pair[1], dfd, 0); + dfd = safe_close(dfd); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + + fd = receive_one_fd(pair[0], MSG_DONTWAIT); + if (fd < 0) + return fd; + + break; + } + + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines."); + } + + return sd_bus_reply_method_return(message, "h", fd); +} + const sd_bus_vtable machine_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1297,6 +1388,7 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index 3a8162b171..241b23c7ec 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -38,6 +38,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error); int machine_send_signal(Machine *m, bool new_machine); int machine_send_create_reply(Machine *m, sd_bus_error *error); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index c9639c3cf2..31efa3695b 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -706,6 +706,26 @@ static int method_copy_machine(sd_bus_message *message, void *userdata, sd_bus_e return bus_machine_method_copy(message, machine, error); } +static int method_open_machine_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_root_directory(message, machine, error); +} + static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(image_unrefp) Image* i = NULL; const char *name; @@ -1225,6 +1245,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 7f9a7268cc..429319da6b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -67,16 +67,15 @@ int address_new_static(Network *network, unsigned section, Address **ret) { if (r < 0) return r; - address->network = network; - - LIST_APPEND(addresses, network->static_addresses, address); - if (section) { address->section = section; hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address); } + address->network = network; + LIST_APPEND(addresses, network->static_addresses, address); + *ret = address; address = NULL; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index ab9b777d9a..bda2707e6d 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -68,15 +68,15 @@ int route_new_static(Network *network, unsigned section, Route **ret) { route->protocol = RTPROT_STATIC; if (section) { + route->section = section; + r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route); if (r < 0) return r; - - route->section = section; } - LIST_PREPEND(routes, network->static_routes, route); route->network = network; + LIST_PREPEND(routes, network->static_routes, route); *ret = route; route = NULL; diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index 74a0ae865b..f2b7e4dd79 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -538,3 +538,50 @@ int veth_extra_parse(char ***l, const char *p) { a = b = NULL; return 0; } + +static int remove_one_veth_link(sd_netlink *rtnl, const char *name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + if (isempty(name)) + return 0; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, name); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r == -ENODEV) /* Already gone */ + return 0; + if (r < 0) + return log_error_errno(r, "Failed to remove veth interface %s: %m", name); + + return 1; +} + +int remove_veth_links(const char *primary, char **pairs) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + char **a, **b; + int r; + + /* In some cases the kernel might pin the veth links between host and container even after the namespace + * died. Hence, let's better remove them explicitly too. */ + + if (isempty(primary) && strv_isempty(pairs)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + remove_one_veth_link(rtnl, primary); + + STRV_FOREACH_PAIR(a, b, pairs) + remove_one_veth_link(rtnl, *a); + + return 0; +} diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h index 9ab1606d1c..c5036ab470 100644 --- a/src/nspawn/nspawn-network.h +++ b/src/nspawn/nspawn-network.h @@ -34,3 +34,5 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces); int move_network_interfaces(pid_t pid, char **ifaces); int veth_extra_parse(char ***l, const char *p); + +int remove_veth_links(const char *primary, char **pairs); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index c8a7ec71a3..5d39c9d7c3 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3981,6 +3981,7 @@ int main(int argc, char *argv[]) { } expose_port_flush(arg_expose_ports, &exposed); + (void) remove_veth_links(veth_name, arg_network_veth_extra); } finish: @@ -4013,6 +4014,7 @@ finish: } expose_port_flush(arg_expose_ports, &exposed); + (void) remove_veth_links(veth_name, arg_network_veth_extra); free(arg_directory); free(arg_template); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index da479aec8d..2b755cea28 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -684,7 +684,7 @@ static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { static const struct { const char *result, *explanation; } explanations [] = { - { "resources", "a configured resource limit was exceeded" }, + { "resources", "of unavailable resources or another system error" }, { "timeout", "a timeout was exceeded" }, { "exit-code", "the control process exited with error code" }, { "signal", "a fatal signal was delivered to the control process" }, diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index cd3d53669c..9351b85eed 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -287,7 +287,10 @@ static int output_short( if (r < 0) return r; } - + if (r == -EBADMSG) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } if (r < 0) return log_error_errno(r, "Failed to get journal fields: %m"); diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index d4c6f409cd..9c36b27157 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -67,10 +67,11 @@ typedef struct sd_journal sd_journal; /* Open flags */ enum { - SD_JOURNAL_LOCAL_ONLY = 1, - SD_JOURNAL_RUNTIME_ONLY = 2, - SD_JOURNAL_SYSTEM = 4, - SD_JOURNAL_CURRENT_USER = 8, + SD_JOURNAL_LOCAL_ONLY = 1 << 0, + SD_JOURNAL_RUNTIME_ONLY = 1 << 1, + SD_JOURNAL_SYSTEM = 1 << 2, + SD_JOURNAL_CURRENT_USER = 1 << 3, + SD_JOURNAL_OS_ROOT = 1 << 4, SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */ }; @@ -84,8 +85,10 @@ enum { int sd_journal_open(sd_journal **ret, int flags); int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); +int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); -int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); +int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */ void sd_journal_close(sd_journal *j); int sd_journal_previous(sd_journal *j); |