diff options
Diffstat (limited to 'src/shared')
110 files changed, 2683 insertions, 571 deletions
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index 79a3b9591d..889a971d88 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -225,7 +226,7 @@ int acl_search_groups(const char *path, char ***ret_groups) { } int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) { - _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not be freed */ + _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */ _cleanup_strv_free_ char **split; char **entry; int r = -EINVAL; diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h index a0e31d8e29..6b581cbc42 100644 --- a/src/shared/acl-util.h +++ b/src/shared/acl-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c index 6779691c28..1a640f4f1b 100644 --- a/src/shared/acpi-fpdt.c +++ b/src/shared/acpi-fpdt.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/acpi-fpdt.h b/src/shared/acpi-fpdt.h index fc28175d0a..4521a1e686 100644 --- a/src/shared/acpi-fpdt.h +++ b/src/shared/acpi-fpdt.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/apparmor-util.c b/src/shared/apparmor-util.c index edd695fd23..e9c4081892 100644 --- a/src/shared/apparmor-util.c +++ b/src/shared/apparmor-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/apparmor-util.h b/src/shared/apparmor-util.h index 524f740152..33ebd4d612 100644 --- a/src/shared/apparmor-util.h +++ b/src/shared/apparmor-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index e33d8b11cf..17928a9732 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -251,11 +252,13 @@ int ask_password_tty( } if (colors_enabled()) - loop_write(ttyfd, ANSI_HIGHLIGHT, strlen(ANSI_HIGHLIGHT), false); + loop_write(ttyfd, ANSI_HIGHLIGHT, + STRLEN(ANSI_HIGHLIGHT), false); loop_write(ttyfd, message, strlen(message), false); loop_write(ttyfd, " ", 1, false); if (colors_enabled()) - loop_write(ttyfd, ANSI_NORMAL, strlen(ANSI_NORMAL), false); + loop_write(ttyfd, ANSI_NORMAL, STRLEN(ANSI_NORMAL), + false); new_termios = old_termios; new_termios.c_lflag &= ~(ICANON|ECHO); diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index 9d7f65130c..f3ca6743a9 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index 903a187861..3c25aa534c 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/base-filesystem.h b/src/shared/base-filesystem.h index 49599f0a60..5d134b4eb9 100644 --- a/src/shared/base-filesystem.h +++ b/src/shared/base-filesystem.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c index 7e0152761c..543e01a364 100644 --- a/src/shared/boot-timestamps.c +++ b/src/shared/boot-timestamps.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/boot-timestamps.h b/src/shared/boot-timestamps.h index 6f691026be..8c67d302b4 100644 --- a/src/shared/boot-timestamps.h +++ b/src/shared/boot-timestamps.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c new file mode 100644 index 0000000000..c0a10417d8 --- /dev/null +++ b/src/shared/bootspec.c @@ -0,0 +1,654 @@ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew JÄ™drzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdio.h> +#include <linux/magic.h> + +#include "alloc-util.h" +#include "blkid-util.h" +#include "bootspec.h" +#include "conf-files.h" +#include "def.h" +#include "device-nodes.h" +#include "efivars.h" +#include "fd-util.h" +#include "fileio.h" +#include "parse-util.h" +#include "stat-util.h" +#include "string-util.h" +#include "strv.h" +#include "virt.h" + +void boot_entry_free(BootEntry *entry) { + assert(entry); + + free(entry->filename); + free(entry->title); + free(entry->show_title); + free(entry->version); + free(entry->machine_id); + free(entry->architecture); + strv_free(entry->options); + free(entry->kernel); + free(entry->efi); + strv_free(entry->initrd); + free(entry->device_tree); +} + +int boot_entry_load(const char *path, BootEntry *entry) { + _cleanup_fclose_ FILE *f = NULL; + unsigned line = 1; + _cleanup_(boot_entry_free) BootEntry tmp = {}; + int r; + + assert(path); + assert(entry); + + f = fopen(path, "re"); + if (!f) + return log_error_errno(errno, "Failed to open \"%s\": %m", path); + + tmp.filename = strdup(basename(path)); + if (!tmp.filename) + return log_oom(); + + for (;;) { + _cleanup_free_ char *buf = NULL; + char *p; + + r = read_line(f, LONG_LINE_MAX, &buf); + if (r == 0) + break; + if (r == -ENOBUFS) + return log_error_errno(r, "%s:%u: Line too long", path, line); + if (r < 0) + return log_error_errno(r, "%s:%u: Error while reading: %m", path, line); + + line++; + + if (IN_SET(*strstrip(buf), '#', '\0')) + continue; + + p = strchr(buf, ' '); + if (!p) { + log_warning("%s:%u: Bad syntax", path, line); + continue; + } + *p = '\0'; + p = strstrip(p + 1); + + if (streq(buf, "title")) + r = free_and_strdup(&tmp.title, p); + else if (streq(buf, "version")) + r = free_and_strdup(&tmp.version, p); + else if (streq(buf, "machine-id")) + r = free_and_strdup(&tmp.machine_id, p); + else if (streq(buf, "architecture")) + r = free_and_strdup(&tmp.architecture, p); + else if (streq(buf, "options")) + r = strv_extend(&tmp.options, p); + else if (streq(buf, "linux")) + r = free_and_strdup(&tmp.kernel, p); + else if (streq(buf, "efi")) + r = free_and_strdup(&tmp.efi, p); + else if (streq(buf, "initrd")) + r = strv_extend(&tmp.initrd, p); + else if (streq(buf, "devicetree")) + r = free_and_strdup(&tmp.device_tree, p); + else { + log_notice("%s:%u: Unknown line \"%s\"", path, line, buf); + continue; + } + if (r < 0) + return log_error_errno(r, "%s:%u: Error while reading: %m", path, line); + } + + *entry = tmp; + tmp = (BootEntry) {}; + return 0; +} + +void boot_config_free(BootConfig *config) { + unsigned i; + + assert(config); + + free(config->default_pattern); + free(config->timeout); + free(config->editor); + + free(config->entry_oneshot); + free(config->entry_default); + + for (i = 0; i < config->n_entries; i++) + boot_entry_free(config->entries + i); + free(config->entries); +} + +int boot_loader_read_conf(const char *path, BootConfig *config) { + _cleanup_fclose_ FILE *f = NULL; + unsigned line = 1; + int r; + + assert(path); + assert(config); + + f = fopen(path, "re"); + if (!f) + return log_error_errno(errno, "Failed to open \"%s\": %m", path); + + for (;;) { + _cleanup_free_ char *buf = NULL; + char *p; + + r = read_line(f, LONG_LINE_MAX, &buf); + if (r == 0) + break; + if (r == -ENOBUFS) + return log_error_errno(r, "%s:%u: Line too long", path, line); + if (r < 0) + return log_error_errno(r, "%s:%u: Error while reading: %m", path, line); + + line++; + + if (IN_SET(*strstrip(buf), '#', '\0')) + continue; + + p = strchr(buf, ' '); + if (!p) { + log_warning("%s:%u: Bad syntax", path, line); + continue; + } + *p = '\0'; + p = strstrip(p + 1); + + if (streq(buf, "default")) + r = free_and_strdup(&config->default_pattern, p); + else if (streq(buf, "timeout")) + r = free_and_strdup(&config->timeout, p); + else if (streq(buf, "editor")) + r = free_and_strdup(&config->editor, p); + else { + log_notice("%s:%u: Unknown line \"%s\"", path, line, buf); + continue; + } + if (r < 0) + return log_error_errno(r, "%s:%u: Error while reading: %m", path, line); + } + + return 0; +} + +/* This is a direct translation of str_verscmp from boot.c */ +static bool is_digit(int c) { + return c >= '0' && c <= '9'; +} + +static int c_order(int c) { + if (c == '\0') + return 0; + if (is_digit(c)) + return 0; + else if ((c >= 'a') && (c <= 'z')) + return c; + else + return c + 0x10000; +} + +static int str_verscmp(const char *s1, const char *s2) { + const char *os1 = s1; + const char *os2 = s2; + + while (*s1 || *s2) { + int first; + + while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { + int order; + + order = c_order(*s1) - c_order(*s2); + if (order) + return order; + s1++; + s2++; + } + + while (*s1 == '0') + s1++; + while (*s2 == '0') + s2++; + + first = 0; + while (is_digit(*s1) && is_digit(*s2)) { + if (first == 0) + first = *s1 - *s2; + s1++; + s2++; + } + + if (is_digit(*s1)) + return 1; + if (is_digit(*s2)) + return -1; + + if (first != 0) + return first; + } + + return strcmp(os1, os2); +} + +static int boot_entry_compare(const void *a, const void *b) { + const BootEntry *aa = a; + const BootEntry *bb = b; + + return str_verscmp(aa->filename, bb->filename); +} + +int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) { + _cleanup_strv_free_ char **files = NULL; + char **f; + int r; + BootEntry *array = NULL; + size_t n_allocated = 0, n = 0; + + assert(dir); + assert(ret_entries); + assert(ret_n_entries); + + r = conf_files_list(&files, ".conf", NULL, 0, dir, NULL); + if (r < 0) + return log_error_errno(r, "Failed to list files in \"%s\": %m", dir); + + STRV_FOREACH(f, files) { + if (!GREEDY_REALLOC0(array, n_allocated, n + 1)) + return log_oom(); + + r = boot_entry_load(*f, array + n); + if (r < 0) + continue; + + n++; + } + + qsort_safe(array, n, sizeof(BootEntry), boot_entry_compare); + + *ret_entries = array; + *ret_n_entries = n; + + return 0; +} + +static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) { + unsigned i, j; + bool non_unique = false; + + assert(entries || n_entries == 0); + assert(arr || n_entries == 0); + + for (i = 0; i < n_entries; i++) + arr[i] = false; + + for (i = 0; i < n_entries; i++) + for (j = 0; j < n_entries; j++) + if (i != j && streq(boot_entry_title(entries + i), + boot_entry_title(entries + j))) + non_unique = arr[i] = arr[j] = true; + + return non_unique; +} + +static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) { + char *s; + unsigned i; + int r; + bool arr[n_entries]; + + assert(entries || n_entries == 0); + + /* Find _all_ non-unique titles */ + if (!find_nonunique(entries, n_entries, arr)) + return 0; + + /* Add version to non-unique titles */ + for (i = 0; i < n_entries; i++) + if (arr[i] && entries[i].version) { + r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version); + if (r < 0) + return -ENOMEM; + + free_and_replace(entries[i].show_title, s); + } + + if (!find_nonunique(entries, n_entries, arr)) + return 0; + + /* Add machine-id to non-unique titles */ + for (i = 0; i < n_entries; i++) + if (arr[i] && entries[i].machine_id) { + r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id); + if (r < 0) + return -ENOMEM; + + free_and_replace(entries[i].show_title, s); + } + + if (!find_nonunique(entries, n_entries, arr)) + return 0; + + /* Add file name to non-unique titles */ + for (i = 0; i < n_entries; i++) + if (arr[i]) { + r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].filename); + if (r < 0) + return -ENOMEM; + + free_and_replace(entries[i].show_title, s); + } + + return 0; +} + +static int boot_entries_select_default(const BootConfig *config) { + int i; + + assert(config); + + if (config->entry_oneshot) + for (i = config->n_entries - 1; i >= 0; i--) + if (streq(config->entry_oneshot, config->entries[i].filename)) { + log_debug("Found default: filename \"%s\" is matched by LoaderEntryOneShot", + config->entries[i].filename); + return i; + } + + if (config->entry_default) + for (i = config->n_entries - 1; i >= 0; i--) + if (streq(config->entry_default, config->entries[i].filename)) { + log_debug("Found default: filename \"%s\" is matched by LoaderEntryDefault", + config->entries[i].filename); + return i; + } + + if (config->default_pattern) + for (i = config->n_entries - 1; i >= 0; i--) + if (fnmatch(config->default_pattern, config->entries[i].filename, FNM_CASEFOLD) == 0) { + log_debug("Found default: filename \"%s\" is matched by pattern \"%s\"", + config->entries[i].filename, config->default_pattern); + return i; + } + + if (config->n_entries > 0) + log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].filename); + else + log_debug("Found no default boot entry :("); + + return config->n_entries - 1; /* -1 means "no default" */ +} + +int boot_entries_load_config(const char *esp_path, BootConfig *config) { + const char *p; + int r; + + assert(esp_path); + assert(config); + + p = strjoina(esp_path, "/loader/loader.conf"); + r = boot_loader_read_conf(p, config); + if (r < 0) + return log_error_errno(r, "Failed to read boot config from \"%s\": %m", p); + + p = strjoina(esp_path, "/loader/entries"); + r = boot_entries_find(p, &config->entries, &config->n_entries); + if (r < 0) + return log_error_errno(r, "Failed to read boot entries from \"%s\": %m", p); + + r = boot_entries_uniquify(config->entries, config->n_entries); + if (r < 0) + return log_error_errno(r, "Failed to uniquify boot entries: %m"); + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m"); + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m"); + + config->default_entry = boot_entries_select_default(config); + return 0; +} + +/********************************************************************************/ + +static int verify_esp( + const char *p, + bool searching, + bool unprivileged_mode, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { +#if HAVE_BLKID + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + char t[DEV_NUM_PATH_MAX]; + const char *v; +#endif + uint64_t pstart = 0, psize = 0; + struct stat st, st2; + const char *t2; + struct statfs sfs; + sd_id128_t uuid = SD_ID128_NULL; + uint32_t part = 0; + int r; + + assert(p); + + /* Non-root user can only check the status, so if an error occured in the following, it does not cause any + * issues. Let's also, silence the error messages. */ + + if (statfs(p, &sfs) < 0) { + /* If we are searching for the mount point, don't generate a log message if we can't find the path */ + if (errno == ENOENT && searching) + return -ENOENT; + + return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + "Failed to check file system type of \"%s\": %m", p); + } + + if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) { + if (searching) + return -EADDRNOTAVAIL; + + log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); + return -ENODEV; + } + + if (stat(p, &st) < 0) + return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of \"%s\": %m", p); + + if (major(st.st_dev) == 0) { + log_error("Block device node of %p is invalid.", p); + return -ENODEV; + } + + t2 = strjoina(p, "/.."); + r = stat(t2, &st2); + if (r < 0) + return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of parent of \"%s\": %m", p); + + if (st.st_dev == st2.st_dev) { + log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); + return -ENODEV; + } + + /* In a container we don't have access to block devices, skip this part of the verification, we trust the + * container manager set everything up correctly on its own. Also skip the following verification for non-root user. */ + if (detect_container() > 0 || unprivileged_mode) + goto finish; + +#if HAVE_BLKID + xsprintf_dev_num_path(t, "block", st.st_dev); + errno = 0; + b = blkid_new_probe_from_filename(t); + if (!b) + return log_error_errno(errno ?: ENOMEM, "Failed to open file system \"%s\": %m", p); + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) { + log_error("File system \"%s\" is ambiguous.", p); + return -ENODEV; + } else if (r == 1) { + log_error("File system \"%s\" does not contain a label.", p); + return -ENODEV; + } else if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe file system \"%s\": %m", p); + + errno = 0; + r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe file system type \"%s\": %m", p); + if (!streq(v, "vfat")) { + log_error("File system \"%s\" is not FAT.", p); + return -ENODEV; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition scheme \"%s\": %m", p); + if (!streq(v, "gpt")) { + log_error("File system \"%s\" is not on a GPT partition table.", p); + return -ENODEV; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID \"%s\": %m", p); + if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) { + log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); + return -ENODEV; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition entry UUID \"%s\": %m", p); + r = sd_id128_from_string(v, &uuid); + if (r < 0) { + log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v); + return -EIO; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition number \"%s\": m", p); + r = safe_atou32(v, &part); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition offset \"%s\": %m", p); + r = safe_atou64(v, &pstart); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition size \"%s\": %m", p); + r = safe_atou64(v, &psize); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); +#endif + +finish: + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +int find_esp_and_warn( + const char *path, + bool unprivileged_mode, + char **ret_path, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { + + int r; + + /* This logs about all errors except: + * + * -ENOKEY → when we can't find the partition + * -EACCESS → when unprivileged_mode is true, and we can't access something + */ + + if (path) { + r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid); + if (r < 0) + return r; + + goto found; + } + + FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") { + + r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid); + if (r >= 0) + goto found; + if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ + return r; + } + + /* No logging here */ + return -ENOKEY; + +found: + if (ret_path) { + char *c; + + c = strdup(path); + if (!c) + return log_oom(); + + *ret_path = c; + } + + return 0; +} diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h new file mode 100644 index 0000000000..d9c641bf08 --- /dev/null +++ b/src/shared/bootspec.h @@ -0,0 +1,64 @@ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew JÄ™drzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#pragma once + +#include <stdlib.h> + +typedef struct BootEntry { + char *filename; + + char *title; + char *show_title; + char *version; + char *machine_id; + char *architecture; + char **options; + char *kernel; /* linux is #defined to 1, yikes! */ + char *efi; + char **initrd; + char *device_tree; +} BootEntry; + +typedef struct BootConfig { + char *default_pattern; + char *timeout; + char *editor; + + char *entry_oneshot; + char *entry_default; + + BootEntry *entries; + size_t n_entries; + ssize_t default_entry; +} BootConfig; + +void boot_entry_free(BootEntry *entry); +int boot_entry_load(const char *path, BootEntry *entry); +int boot_entries_find(const char *dir, BootEntry **entries, size_t *n_entries); + +int boot_loader_read_conf(const char *path, BootConfig *config); +void boot_config_free(BootConfig *config); +int boot_entries_load_config(const char *esp_path, BootConfig *config); + +static inline const char* boot_entry_title(const BootEntry *entry) { + return entry->show_title ?: entry->title ?: entry->filename; +} + +int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index e191f8c93e..b58abed2b5 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -28,6 +29,7 @@ #include "errno-list.h" #include "escape.h" #include "hashmap.h" +#include "hexdecoct.h" #include "hostname-util.h" #include "in-addr-util.h" #include "list.h" @@ -132,9 +134,12 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else if (streq(field, "EnvironmentFile")) { - r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, - eq[0] == '-' ? eq + 1 : eq, - eq[0] == '-'); + if (isempty(eq)) + r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 0); + else + r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, + eq[0] == '-' ? eq + 1 : eq, + eq[0] == '-'); goto finish; } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { @@ -156,7 +161,32 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "sv", n, "t", t); goto finish; - } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) { + } else if (streq(field, "LogExtraFields")) { + + r = sd_bus_message_append(m, "s", "LogExtraFields"); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(m, 'v', "aay"); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(m, 'a', "ay"); + if (r < 0) + goto finish; + + r = sd_bus_message_append_array(m, 'y', eq, strlen(eq)); + if (r < 0) + goto finish; + + r = sd_bus_message_close_container(m); + if (r < 0) + goto finish; + + r = sd_bus_message_close_container(m); + goto finish; + + } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit")) { uint64_t bytes; if (isempty(eq) || streq(eq, "infinity")) @@ -183,6 +213,51 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "sv", field, "t", bytes); goto finish; + + } else if (streq(field, "Delegate")) { + + r = parse_boolean(eq); + if (r < 0) { + const char *p = eq; + + r = sd_bus_message_append(m, "s", "DelegateControllers"); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + goto finish; + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + goto finish; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Invalid syntax: %s", eq); + + r = sd_bus_message_append(m, "s", word); + if (r < 0) + goto finish; + } + + r = sd_bus_message_close_container(m); + if (r < 0) + goto finish; + + r = sd_bus_message_close_container(m); + } else + r = sd_bus_message_append(m, "sv", "Delegate", "b", r); + + goto finish; + } else if (streq(field, "TasksMax")) { uint64_t t; @@ -203,6 +278,50 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "sv", "TasksMax", "t", t); goto finish; + + } else if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) { + const char *n, *appended; + + n = startswith(eq, "fd:"); + if (n) { + appended = strjoina(field, "FileDescriptorName"); + r = sd_bus_message_append(m, "sv", appended, "s", n); + + } else if ((n = startswith(eq, "file:"))) { + appended = strjoina(field, "File"); + r = sd_bus_message_append(m, "sv", appended, "s", n); + } else + r = sd_bus_message_append(m, "sv", field, "s", eq); + + goto finish; + + } else if (streq(field, "StandardInputText")) { + _cleanup_free_ char *unescaped = NULL; + + r = cunescape(eq, 0, &unescaped); + if (r < 0) + return log_error_errno(r, "Failed to unescape text '%s': %m", eq); + + if (!strextend(&unescaped, "\n", NULL)) + return log_oom(); + + /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic + * interface anyway */ + + r = sd_bus_message_append(m, "s", "StandardInputData"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "ay"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'y', unescaped, strlen(unescaped)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + goto finish; } r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); @@ -238,7 +357,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "TasksAccounting", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", - "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", + "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse", "Persistent", "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality")) { @@ -253,10 +372,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen uint64_t u; r = cg_weight_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); r = sd_bus_message_append(m, "v", "t", u); @@ -264,10 +381,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen uint64_t u; r = cg_cpu_shares_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); r = sd_bus_message_append(m, "v", "t", u); @@ -275,10 +390,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen uint64_t u; r = cg_weight_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); r = sd_bus_message_append(m, "v", "t", u); @@ -286,25 +399,42 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen uint64_t u; r = cg_blkio_weight_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); r = sd_bus_message_append(m, "v", "t", u); } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode", "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", - "StandardInput", "StandardOutput", "StandardError", "Description", "Slice", "Type", "WorkingDirectory", "RootDirectory", "SyslogIdentifier", "ProtectSystem", "ProtectHome", "SELinuxContext", "Restart", "RootImage", "NotifyAccess", "RuntimeDirectoryPreserve", "Personality", - "KeyringMode")) + "KeyringMode", "CollectMode", "FailureAction", "SuccessAction", + "OnCalendar")) + r = sd_bus_message_append(m, "v", "s", eq); - else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { + else if (streq(field, "StandardInputData")) { + _cleanup_free_ void *decoded = NULL; + size_t sz; + + r = unbase64mem(eq, (size_t) -1, &decoded, &sz); + if (r < 0) + return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq); + + r = sd_bus_message_open_container(m, 'v', "ay"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'y', decoded, sz); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { bool ignore; const char *s; @@ -318,7 +448,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "(bs)", ignore, s); - } else if (streq(field, "SyslogLevel")) { + } else if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) { int level; level = log_level_from_string(eq); @@ -343,10 +473,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else if (streq(field, "SecureBits")) { r = secure_bits_from_string(eq); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); r = sd_bus_message_append(m, "v", "i", r); @@ -362,10 +490,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } r = capability_set_from_string(p, &sum); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); sum = invert ? ~sum : sum; @@ -421,10 +547,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen bytes = CGROUP_LIMIT_MAX; } else { r = parse_size(bandwidth, 1000, &bytes); - if (r < 0) { - log_error("Failed to parse byte value %s.", bandwidth); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth); } r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); @@ -453,10 +577,9 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } r = safe_atou64(weight, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, weight); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight); + r = sd_bus_message_append(m, "v", "a(st)", 1, path, u); } @@ -511,7 +634,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); prefix.in6 = (struct in6_addr) { - .__in6_u.__u6_addr32[0] = htobe32(0xfe800000) + .s6_addr32[0] = htobe32(0xfe800000) }; r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64); if (r < 0) @@ -527,7 +650,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); prefix.in6 = (struct in6_addr) { - .__in6_u.__u6_addr32[0] = htobe32(0xff000000) + .s6_addr32[0] = htobe32(0xff000000) }; r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8); if (r < 0) @@ -584,8 +707,9 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r < 0) return bus_log_create_error(r); - if (cpuset) - sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus)); + r = sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus)); + if (r < 0) + return bus_log_create_error(r); r = sd_bus_message_close_container(m); @@ -598,16 +722,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "i", (int32_t) n); -#if HAVE_SECCOMP - } else if (streq(field, "SystemCallFilter")) { int whitelist; + _cleanup_strv_free_ char **l = NULL; const char *p; - r = sd_bus_message_open_container(m, 'v', "bas"); - if (r < 0) - return bus_log_create_error(r); - p = eq; if (*p == '~') { whitelist = 0; @@ -615,18 +734,10 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else whitelist = 1; - r = sd_bus_message_append_basic(m, 'b', &whitelist); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - if (whitelist != 0) { - r = sd_bus_message_append_basic(m, 's', "@default"); + r = strv_extend(&l, "@default"); if (r < 0) - return bus_log_create_error(r); + return log_oom(); } for (;;) { @@ -638,16 +749,34 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r == 0) break; - r = sd_bus_message_append_basic(m, 's', word); + r = strv_extend(&l, word); if (r < 0) - return bus_log_create_error(r); + return log_oom(); } + r = sd_bus_message_open_container(m, 'v', "(bas)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'r', "bas"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 'b', &whitelist); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, l); + if (r < 0) + return bus_log_create_error(r); + r = sd_bus_message_close_container(m); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); } else if (streq(field, "SystemCallArchitectures")) { const char *p; @@ -683,35 +812,23 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else if (streq(field, "SystemCallErrorNumber")) { int n; - n = errno_from_name(eq); - if (n < 0) + n = parse_errno(eq); + if (n <= 0) return log_error_errno(r, "Failed to parse %s value: %s", field, eq); r = sd_bus_message_append(m, "v", "i", (int32_t) n); } else if (streq(field, "RestrictAddressFamilies")) { int whitelist; - const char *p; - - r = sd_bus_message_open_container(m, 'v', "bas"); - if (r < 0) - return bus_log_create_error(r); + _cleanup_strv_free_ char **l = NULL; + const char *p = eq; - p = eq; if (*p == '~') { whitelist = 0; p++; } else whitelist = 1; - r = sd_bus_message_append_basic(m, 'b', &whitelist); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - for (;;) { _cleanup_free_ char *word = NULL; @@ -721,18 +838,34 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r == 0) break; - r = sd_bus_message_append_basic(m, 's', word); + r = strv_extend(&l, word); if (r < 0) - return bus_log_create_error(r); + return log_oom(); } - r = sd_bus_message_close_container(m); + r = sd_bus_message_open_container(m, 'v', "(bas)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'r', "bas"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 'b', &whitelist); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, l); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); -#endif + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); } else if (streq(field, "FileDescriptorStoreMax")) { unsigned u; @@ -776,10 +909,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) { - log_error("Failed to parse Environment value %s", eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse Environment value %s: %m", eq); if (r == 0) break; @@ -826,20 +957,16 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen nsec_t n; r = parse_nsec(eq, &n); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); r = sd_bus_message_append(m, "v", "t", n); } else if (streq(field, "OOMScoreAdjust")) { int oa; r = safe_atoi(eq, &oa); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); if (!oom_score_adjust_is_valid(oa)) { log_error("OOM score adjust value out of range"); @@ -864,10 +991,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen size_t offset; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); if (r == 0) break; @@ -912,10 +1037,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); if (r == 0) break; @@ -1081,8 +1204,107 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return r; r = sd_bus_message_close_container(m); + + } else if (STR_IN_SET(field, "ExecStartPre", "ExecStart", "ExecStartPost", + "ExecReload", "ExecStop", "ExecStopPost")) { + + bool ignore_failure = false, explicit_path = false, done = false; + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *path = NULL; + + do { + switch (*eq) { + + case '-': + if (ignore_failure) + done = true; + else { + ignore_failure = true; + eq++; + } + break; + + case '@': + if (explicit_path) + done = true; + else { + explicit_path = true; + eq++; + } + break; + + case '+': + case '!': + /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */ + log_error("Sorry, but +, ! and !! are currently not supported for transient services."); + return -EOPNOTSUPP; + + default: + done = true; + break; + } + } while (!done); + + if (explicit_path) { + r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse path: %m"); + } + + r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse command line: %m"); + + r = sd_bus_message_open_container(m, 'v', "a(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sasb)"); + if (r < 0) + return r; + + if (strv_length(l) > 0) { + + r = sd_bus_message_open_container(m, 'r', "sasb"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", path ?: l[0]); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, l); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "b", ignore_failure); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + + } else if (STR_IN_SET(field, + "OnActiveSec", "OnBootSec", "OnStartupSec", + "OnUnitActiveSec","OnUnitInactiveSec")) { + usec_t t; + + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + + r = sd_bus_message_append(m, "v", "t", t); + } else { - log_error("Unknown assignment %s.", assignment); + log_error("Unknown assignment: %s", assignment); return -EINVAL; } @@ -1290,7 +1512,7 @@ static void log_job_error_with_service_result(const char* service, const char *r service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH); - if (extra_args) { + if (!strv_isempty((char**) extra_args)) { _cleanup_free_ char *t; t = strv_join((char**) extra_args, " "); @@ -1471,7 +1693,7 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, Un if (r < 0) return bus_log_parse_error(r); - unit_file_dump_changes(0, NULL, *changes, *n_changes, false); + unit_file_dump_changes(0, NULL, *changes, *n_changes, quiet); return 0; } diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index d102ea180e..1a137e8b84 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 7609d9c522..7a185461a3 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -553,12 +554,7 @@ int bus_verify_polkit_async( void bus_verify_polkit_async_registry_free(Hashmap *registry) { #if ENABLE_POLKIT - AsyncPolkitQuery *q; - - while ((q = hashmap_steal_first(registry))) - async_polkit_query_free(q); - - hashmap_free(registry); + hashmap_free_with_destructor(registry, async_polkit_query_free); #endif } @@ -664,7 +660,7 @@ int bus_connect_user_systemd(sd_bus **_bus) { printf(fmt "\n", __VA_ARGS__); \ else \ printf("%s=" fmt "\n", name, __VA_ARGS__); \ - } while(0) + } while (0) int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all) { char type; diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index d9ce4263bb..a9f4969d7d 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 436130edea..0ddae95434 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index 1764f76744..efa597aad0 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c index 64f81bb5d3..7e2ef4a8eb 100644 --- a/src/shared/clean-ipc.c +++ b/src/shared/clean-ipc.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/clean-ipc.h b/src/shared/clean-ipc.h index c04ed3596b..0ade561b3f 100644 --- a/src/shared/clean-ipc.h +++ b/src/shared/clean-ipc.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/condition.c b/src/shared/condition.c index 74d5e854e1..3f32dfb7b6 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -54,6 +55,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "tomoyo-util.h" #include "user-util.h" #include "util.h" #include "virt.h" @@ -76,8 +78,7 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger r = free_and_strdup(&c->parameter, parameter); if (r < 0) { - free(c); - return NULL; + return mfree(c); } return c; @@ -156,7 +157,7 @@ static int condition_test_user(Condition *c) { return id == getuid() || id == geteuid(); if (streq("@system", c->parameter)) - return getuid() <= SYSTEM_UID_MAX || geteuid() <= SYSTEM_UID_MAX; + return uid_is_system(getuid()) || uid_is_system(geteuid()); username = getusername_malloc(); if (!username) @@ -301,6 +302,8 @@ static int condition_test_security(Condition *c) { return use_audit(); if (streq(c->parameter, "ima")) return use_ima(); + if (streq(c->parameter, "tomoyo")) + return mac_tomoyo_use(); return false; } diff --git a/src/shared/condition.h b/src/shared/condition.h index d0b592bc43..534906b6d6 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index c304ae3334..daddb7cf20 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -121,17 +122,18 @@ int config_item_perf_lookup( } /* Run the user supplied parser for an assignment */ -static int next_assignment(const char *unit, - const char *filename, - unsigned line, - ConfigItemLookup lookup, - const void *table, - const char *section, - unsigned section_line, - const char *lvalue, - const char *rvalue, - bool relaxed, - void *userdata) { +static int next_assignment( + const char *unit, + const char *filename, + unsigned line, + ConfigItemLookup lookup, + const void *table, + const char *section, + unsigned section_line, + const char *lvalue, + const char *rvalue, + ConfigParseFlags flags, + void *userdata) { ConfigParserCallback func = NULL; int ltype = 0; @@ -157,26 +159,26 @@ static int next_assignment(const char *unit, } /* Warn about unknown non-extension fields. */ - if (!relaxed && !startswith(lvalue, "X-")) + if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-")) log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section); return 0; } /* Parse a variable assignment line */ -static int parse_line(const char* unit, - const char *filename, - unsigned line, - const char *sections, - ConfigItemLookup lookup, - const void *table, - bool relaxed, - bool allow_include, - char **section, - unsigned *section_line, - bool *section_ignored, - char *l, - void *userdata) { +static int parse_line( + const char* unit, + const char *filename, + unsigned line, + const char *sections, + ConfigItemLookup lookup, + const void *table, + ConfigParseFlags flags, + char **section, + unsigned *section_line, + bool *section_ignored, + char *l, + void *userdata) { char *e; @@ -186,7 +188,6 @@ static int parse_line(const char* unit, assert(l); l = strstrip(l); - if (!*l) return 0; @@ -205,7 +206,7 @@ static int parse_line(const char* unit, * * Support for them should be eventually removed. */ - if (!allow_include) { + if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) { log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring."); return 0; } @@ -214,7 +215,7 @@ static int parse_line(const char* unit, if (!fn) return -ENOMEM; - return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata); + return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata); } if (*l == '[') { @@ -235,7 +236,7 @@ static int parse_line(const char* unit, if (sections && !nulstr_contains(sections, n)) { - if (!relaxed && !startswith(n, "X-")) + if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-")) log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); free(n); @@ -254,7 +255,7 @@ static int parse_line(const char* unit, if (sections && !*section) { - if (!relaxed && !*section_ignored) + if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored) log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring."); return 0; @@ -278,7 +279,7 @@ static int parse_line(const char* unit, *section_line, strstrip(l), strstrip(e), - relaxed, + flags, userdata); } @@ -289,15 +290,13 @@ int config_parse(const char *unit, const char *sections, ConfigItemLookup lookup, const void *table, - bool relaxed, - bool allow_include, - bool warn, + ConfigParseFlags flags, void *userdata) { _cleanup_free_ char *section = NULL, *continuation = NULL; _cleanup_fclose_ FILE *ours = NULL; unsigned line = 0, section_line = 0; - bool section_ignored = false, allow_bom = true; + bool section_ignored = false; int r; assert(filename); @@ -308,7 +307,7 @@ int config_parse(const char *unit, if (!f) { /* Only log on request, except for ENOENT, * since we return 0 to the caller. */ - if (warn || errno == ENOENT) + if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT) log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR, "Failed to open configuration file '%s': %m", filename); return errno == ENOENT ? 0 : -errno; @@ -319,52 +318,50 @@ int config_parse(const char *unit, for (;;) { _cleanup_free_ char *buf = NULL; - char *l, *p, *c = NULL, *e; bool escaped = false; + char *l, *p, *e; r = read_line(f, LONG_LINE_MAX, &buf); if (r == 0) break; if (r == -ENOBUFS) { - if (warn) + if (flags & CONFIG_PARSE_WARN) log_error_errno(r, "%s:%u: Line too long", filename, line); return r; } if (r < 0) { - if (warn) + if (CONFIG_PARSE_WARN) log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line); return r; } l = buf; - if (allow_bom) { + if (!(flags & CONFIG_PARSE_REFUSE_BOM)) { char *q; q = startswith(buf, UTF8_BYTE_ORDER_MARK); if (q) { l = q; - allow_bom = false; + flags |= CONFIG_PARSE_REFUSE_BOM; } } if (continuation) { if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) { - if (warn) + if (flags & CONFIG_PARSE_WARN) log_error("%s:%u: Continuation line too long", filename, line); return -ENOBUFS; } - c = strappend(continuation, l); - if (!c) { - if (warn) + if (!strextend(&continuation, l, NULL)) { + if (flags & CONFIG_PARSE_WARN) log_oom(); return -ENOMEM; } - continuation = mfree(continuation); - p = c; + p = continuation; } else p = l; @@ -378,12 +375,10 @@ int config_parse(const char *unit, if (escaped) { *(e-1) = ' '; - if (c) - continuation = c; - else { + if (!continuation) { continuation = strdup(l); if (!continuation) { - if (warn) + if (flags & CONFIG_PARSE_WARN) log_oom(); return -ENOMEM; } @@ -398,20 +393,20 @@ int config_parse(const char *unit, sections, lookup, table, - relaxed, - allow_include, + flags, §ion, §ion_line, §ion_ignored, p, userdata); - free(c); - if (r < 0) { - if (warn) + if (flags & CONFIG_PARSE_WARN) log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line); return r; + } + + continuation = mfree(continuation); } return 0; @@ -423,20 +418,20 @@ static int config_parse_many_files( const char *sections, ConfigItemLookup lookup, const void *table, - bool relaxed, + ConfigParseFlags flags, void *userdata) { char **fn; int r; if (conf_file) { - r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata); + r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata); if (r < 0) return r; } STRV_FOREACH(fn, files) { - r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata); + r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata); if (r < 0) return r; } @@ -451,7 +446,7 @@ int config_parse_many_nulstr( const char *sections, ConfigItemLookup lookup, const void *table, - bool relaxed, + ConfigParseFlags flags, void *userdata) { _cleanup_strv_free_ char **files = NULL; @@ -461,8 +456,7 @@ int config_parse_many_nulstr( if (r < 0) return r; - return config_parse_many_files(conf_file, files, - sections, lookup, table, relaxed, userdata); + return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata); } /* Parse each config file in the directories specified as strv. */ @@ -473,7 +467,7 @@ int config_parse_many( const char *sections, ConfigItemLookup lookup, const void *table, - bool relaxed, + ConfigParseFlags flags, void *userdata) { _cleanup_strv_free_ char **dropin_dirs = NULL; @@ -490,8 +484,7 @@ int config_parse_many( if (r < 0) return r; - return config_parse_many_files(conf_file, files, - sections, lookup, table, relaxed, userdata); + return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata); } #define DEFINE_PARSER(type, vartype, conv_func) \ @@ -567,16 +560,17 @@ int config_parse_iec_size(const char* unit, return 0; } -int config_parse_si_size(const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_si_size( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { size_t *sz = data; uint64_t v; @@ -597,16 +591,17 @@ int config_parse_si_size(const char* unit, return 0; } -int config_parse_iec_uint64(const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_iec_uint64( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { uint64_t *bytes = data; int r; @@ -840,7 +835,6 @@ int config_parse_log_facility( void *data, void *userdata) { - int *o = data, x; assert(filename); @@ -871,7 +865,6 @@ int config_parse_log_level( void *data, void *userdata) { - int *o = data, x; assert(filename); @@ -885,7 +878,11 @@ int config_parse_log_level( return 0; } - *o = (*o & LOG_FACMASK) | x; + if (*o < 0) /* if it wasn't initialized so far, assume zero facility */ + *o = x; + else + *o = (*o & LOG_FACMASK) | x; + return 0; } diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index ce1113485d..2fd135baa0 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -29,8 +30,14 @@ #include "log.h" #include "macro.h" -/* An abstract parser for simple, line based, shallow configuration - * files consisting of variable assignments only. */ +/* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */ + +typedef enum ConfigParseFlags { + CONFIG_PARSE_RELAXED = 1U << 0, + CONFIG_PARSE_ALLOW_INCLUDE = 1U << 1, + CONFIG_PARSE_WARN = 1U << 2, + CONFIG_PARSE_REFUSE_BOM = 1U << 3, +} ConfigParseFlags; /* Prototype for a parser for a specific configuration setting */ typedef int (*ConfigParserCallback)(const char *unit, @@ -91,9 +98,7 @@ int config_parse( const char *sections, /* nulstr */ ConfigItemLookup lookup, const void *table, - bool relaxed, - bool allow_include, - bool warn, + ConfigParseFlags flags, void *userdata); int config_parse_many_nulstr( @@ -102,7 +107,7 @@ int config_parse_many_nulstr( const char *sections, /* nulstr */ ConfigItemLookup lookup, const void *table, - bool relaxed, + ConfigParseFlags flags, void *userdata); int config_parse_many( @@ -112,7 +117,7 @@ int config_parse_many( const char *sections, /* nulstr */ ConfigItemLookup lookup, const void *table, - bool relaxed, + ConfigParseFlags flags, void *userdata); /* Generic parsers */ diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c index b2d464c117..6d2cc685fa 100644 --- a/src/shared/dev-setup.c +++ b/src/shared/dev-setup.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/dev-setup.h b/src/shared/dev-setup.h index 5766a62060..4dd591de0a 100644 --- a/src/shared/dev-setup.h +++ b/src/shared/dev-setup.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 4b59f2ca7d..e8d7db8f0c 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -17,50 +18,68 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#if HAVE_LIBCRYPTSETUP -#include <libcryptsetup.h> -#endif #include <sys/mount.h> +#include <sys/prctl.h> +#include <sys/wait.h> #include "architecture.h" #include "ask-password-api.h" #include "blkid-util.h" +#include "copy.h" +#include "crypt-util.h" +#include "def.h" +#include "device-nodes.h" #include "dissect-image.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "gpt.h" #include "hexdecoct.h" +#include "hostname-util.h" +#include "id128-util.h" #include "linux-3.13/dm-ioctl.h" #include "mount-util.h" #include "path-util.h" +#include "process-util.h" +#include "raw-clone.h" +#include "signal-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" #include "udev-util.h" +#include "user-util.h" #include "xattr-util.h" -_unused_ static int probe_filesystem(const char *node, char **ret_fstype) { +int probe_filesystem(const char *node, char **ret_fstype) { + /* Try to find device content type and return it in *ret_fstype. If nothing is found, + * 0/NULL will be returned. -EUCLEAN will be returned for ambigous results, and an + * different error otherwise. */ + #if HAVE_BLKID _cleanup_blkid_free_probe_ blkid_probe b = NULL; const char *fstype; int r; + errno = 0; b = blkid_new_probe_from_filename(node); if (!b) - return -ENOMEM; + return -errno ?: -ENOMEM; blkid_probe_enable_superblocks(b, 1); blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); errno = 0; r = blkid_do_safeprobe(b); - if (IN_SET(r, -2, 1)) { - log_debug("Failed to identify any partition type on partition %s", node); + if (r == 1) { + log_debug("No type detected on partition %s", node); goto not_found; } + if (r == -2) { + log_debug("Results ambiguous for partition %s", node); + return -EUCLEAN; + } if (r != 0) return -errno ?: -EIO; @@ -266,18 +285,34 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI return -EIO; } if (n < z + 1) { - unsigned j; + unsigned j = 0; /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a * synchronous call that waits until probing is complete. */ - for (j = 0; j < 20; j++) { + for (;;) { + if (j++ > 20) + return -EBUSY; - r = ioctl(fd, BLKRRPART, 0); - if (r < 0) + if (ioctl(fd, BLKRRPART, 0) < 0) { r = -errno; - if (r >= 0 || r != -EBUSY) + + if (r == -EINVAL) { + struct loop_info64 info; + + /* If we are running on a loop device that has partition scanning off, + * return an explicit recognizable error about this, so that callers + * can generate a proper message explaining the situation. */ + + if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) { + log_debug("Device is loop device and partition scanning is off!"); + return -EPROTONOSUPPORT; + } + } + if (r != -EBUSY) + return r; + } else break; /* If something else has the device open, such as an udev rule, the ioctl will return @@ -286,11 +321,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI * * This is really something they should fix in the kernel! */ - usleep(50 * USEC_PER_MSEC); + (void) usleep(50 * USEC_PER_MSEC); } - - if (r < 0) - return r; } e = udev_enumerate_unref(e); @@ -585,7 +617,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI if (!p->fstype && p->node) { r = probe_filesystem(p->node, &p->fstype); - if (r < 0) + if (r < 0 && r != -EUCLEAN) return r; } @@ -618,12 +650,15 @@ DissectedImage* dissected_image_unref(DissectedImage *m) { free(m->partitions[i].decrypted_node); } - free(m); - return NULL; + free(m->hostname); + strv_free(m->machine_info); + strv_free(m->os_release); + + return mfree(m); } static int is_loop_device(const char *path) { - char s[strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen("/../loop/")]; + char s[SYS_BLOCK_PATH_MAX("/../loop/")]; struct stat st; assert(path); @@ -634,13 +669,13 @@ static int is_loop_device(const char *path) { if (!S_ISBLK(st.st_mode)) return -ENOTBLK; - xsprintf(s, "/sys/dev/block/%u:%u/loop/", major(st.st_rdev), minor(st.st_rdev)); + xsprintf_sys_block_path(s, "/loop/", st.st_dev); if (access(s, F_OK) < 0) { if (errno != ENOENT) return -errno; /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */ - xsprintf(s, "/sys/dev/block/%u:%u/../loop/", major(st.st_rdev), minor(st.st_rdev)); + xsprintf_sys_block_path(s, "/../loop/", st.st_dev); if (access(s, F_OK) < 0) return errno == ENOENT ? false : -errno; } @@ -652,10 +687,11 @@ static int mount_partition( DissectedPartition *m, const char *where, const char *directory, + uid_t uid_shift, DissectImageFlags flags) { - const char *p, *options = NULL, *node, *fstype; - _cleanup_free_ char *chased = NULL; + _cleanup_free_ char *chased = NULL, *options = NULL; + const char *p, *node, *fstype; bool rw; int r; @@ -686,13 +722,26 @@ static int mount_partition( /* If requested, turn on discard support. */ if (fstype_can_discard(fstype) && ((flags & DISSECT_IMAGE_DISCARD) || - ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node)))) - options = "discard"; + ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node)))) { + options = strdup("discard"); + if (!options) + return -ENOMEM; + } + + if (uid_is_valid(uid_shift) && uid_shift != 0 && fstype_can_uid_gid(fstype)) { + _cleanup_free_ char *uid_option = NULL; + + if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0) + return -ENOMEM; + + if (!strextend_with_separator(&options, ",", uid_option, NULL)) + return -ENOMEM; + } return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options); } -int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) { +int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) { int r; assert(m); @@ -701,15 +750,20 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag if (!m->partitions[PARTITION_ROOT].found) return -ENXIO; - r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, flags); - if (r < 0) - return r; + if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) { + r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags); + if (r < 0) + return r; + } + + if ((flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)) + return 0; - r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", flags); + r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags); if (r < 0) return r; - r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", flags); + r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", uid_shift, flags); if (r < 0) return r; @@ -727,7 +781,7 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag r = dir_is_empty(p); if (r > 0) { - r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags); + r = mount_partition(m->partitions + PARTITION_ESP, where, mp, uid_shift, flags); if (r < 0) return r; } @@ -820,7 +874,7 @@ static int decrypt_partition( DecryptedImage *d) { _cleanup_free_ char *node = NULL, *name = NULL; - struct crypt_device *cd; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; assert(m); @@ -832,6 +886,9 @@ static int decrypt_partition( if (!streq(m->fstype, "crypto_LUKS")) return 0; + if (!passphrase) + return -ENOKEY; + r = make_dm_name_and_node(m->node, "-decrypted", &name, &node); if (r < 0) return r; @@ -843,38 +900,29 @@ static int decrypt_partition( if (r < 0) return log_debug_errno(r, "Failed to initialize dm-crypt: %m"); - r = crypt_load(cd, CRYPT_LUKS1, NULL); - if (r < 0) { - log_debug_errno(r, "Failed to load LUKS metadata: %m"); - goto fail; - } + r = crypt_load(cd, CRYPT_LUKS, NULL); + if (r < 0) + return log_debug_errno(r, "Failed to load LUKS metadata: %m"); r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) | ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0)); - if (r < 0) + if (r < 0) { log_debug_errno(r, "Failed to activate LUKS device: %m"); - if (r == -EPERM) { - r = -EKEYREJECTED; - goto fail; + return r == -EPERM ? -EKEYREJECTED : r; } - if (r < 0) - goto fail; d->decrypted[d->n_decrypted].name = name; name = NULL; d->decrypted[d->n_decrypted].device = cd; + cd = NULL; d->n_decrypted++; m->decrypted_node = node; node = NULL; return 0; - -fail: - crypt_free(cd); - return r; } static int verity_partition( @@ -886,7 +934,7 @@ static int verity_partition( DecryptedImage *d) { _cleanup_free_ char *node = NULL, *name = NULL; - struct crypt_device *cd; + _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; assert(m); @@ -916,30 +964,27 @@ static int verity_partition( r = crypt_load(cd, CRYPT_VERITY, NULL); if (r < 0) - goto fail; + return r; r = crypt_set_data_device(cd, m->node); if (r < 0) - goto fail; + return r; r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY); if (r < 0) - goto fail; + return r; d->decrypted[d->n_decrypted].name = name; name = NULL; d->decrypted[d->n_decrypted].device = cd; + cd = NULL; d->n_decrypted++; m->decrypted_node = node; node = NULL; return 0; - -fail: - crypt_free(cd); - return r; } #endif @@ -951,8 +996,8 @@ int dissected_image_decrypt( DissectImageFlags flags, DecryptedImage **ret) { - _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL; #if HAVE_LIBCRYPTSETUP + _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL; unsigned i; int r; #endif @@ -977,9 +1022,6 @@ int dissected_image_decrypt( } #if HAVE_LIBCRYPTSETUP - if (m->encrypted && !passphrase) - return -ENOKEY; - d = new0(DecryptedImage, 1); if (!d) return -ENOMEM; @@ -1004,7 +1046,7 @@ int dissected_image_decrypt( if (!p->decrypted_fstype && p->decrypted_node) { r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype); - if (r < 0) + if (r < 0 && r != -EUCLEAN) return r; } } @@ -1144,7 +1186,7 @@ int root_hash_load(const char *image, void **ret, size_t *ret_size) { if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT)) return r; - fn = newa(char, strlen(image) + strlen(".roothash") + 1); + fn = newa(char, strlen(image) + STRLEN(".roothash") + 1); n = stpcpy(fn, image); e = endswith(fn, ".raw"); if (e) @@ -1176,6 +1218,174 @@ int root_hash_load(const char *image, void **ret, size_t *ret_size) { return 1; } +int dissected_image_acquire_metadata(DissectedImage *m) { + + enum { + META_HOSTNAME, + META_MACHINE_ID, + META_MACHINE_INFO, + META_OS_RELEASE, + _META_MAX, + }; + + static const char *const paths[_META_MAX] = { + [META_HOSTNAME] = "/etc/hostname\0", + [META_MACHINE_ID] = "/etc/machine-id\0", + [META_MACHINE_INFO] = "/etc/machine-info\0", + [META_OS_RELEASE] = "/etc/os-release\0/usr/lib/os-release\0", + }; + + _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL; + _cleanup_(rmdir_and_freep) char *t = NULL; + _cleanup_(sigkill_waitp) pid_t child = 0; + sd_id128_t machine_id = SD_ID128_NULL; + _cleanup_free_ char *hostname = NULL; + unsigned n_meta_initialized = 0, k; + int fds[2 * _META_MAX], r; + siginfo_t si; + + BLOCK_SIGNALS(SIGCHLD); + + assert(m); + + for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) + if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) { + r = -errno; + goto finish; + } + + r = mkdtemp_malloc("/tmp/dissect-XXXXXX", &t); + if (r < 0) + goto finish; + + child = raw_clone(SIGCHLD|CLONE_NEWNS); + if (child < 0) { + r = -errno; + goto finish; + } + + if (child == 0) { + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + /* Make sure we never propagate to the host */ + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) + _exit(EXIT_FAILURE); + + r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY); + if (r < 0) + _exit(EXIT_FAILURE); + + for (k = 0; k < _META_MAX; k++) { + _cleanup_close_ int fd = -1; + const char *p; + + fds[2*k] = safe_close(fds[2*k]); + + NULSTR_FOREACH(p, paths[k]) { + _cleanup_free_ char *q = NULL; + + r = chase_symlinks(p, t, CHASE_PREFIX_ROOT, &q); + if (r < 0) + continue; + + fd = open(q, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd >= 0) + break; + } + if (fd < 0) + continue; + + r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0); + if (r < 0) + _exit(EXIT_FAILURE); + + fds[2*k+1] = safe_close(fds[2*k+1]); + } + + _exit(EXIT_SUCCESS); + } + + for (k = 0; k < _META_MAX; k++) { + _cleanup_fclose_ FILE *f = NULL; + + fds[2*k+1] = safe_close(fds[2*k+1]); + + f = fdopen(fds[2*k], "re"); + if (!f) { + r = -errno; + goto finish; + } + + fds[2*k] = -1; + + switch (k) { + + case META_HOSTNAME: + r = read_etc_hostname_stream(f, &hostname); + if (r < 0) + log_debug_errno(r, "Failed to read /etc/hostname: %m"); + + break; + + case META_MACHINE_ID: { + _cleanup_free_ char *line = NULL; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + log_debug_errno(r, "Failed to read /etc/machine-id: %m"); + else if (r == 33) { + r = sd_id128_from_string(line, &machine_id); + if (r < 0) + log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line); + } else if (r == 0) + log_debug("/etc/machine-id file is empty."); + else + log_debug("/etc/machine-id has unexpected length %i.", r); + + break; + } + + case META_MACHINE_INFO: + r = load_env_file_pairs(f, "machine-info", NULL, &machine_info); + if (r < 0) + log_debug_errno(r, "Failed to read /etc/machine-info: %m"); + + break; + + case META_OS_RELEASE: + r = load_env_file_pairs(f, "os-release", NULL, &os_release); + if (r < 0) + log_debug_errno(r, "Failed to read OS release file: %m"); + + break; + } + } + + r = wait_for_terminate(child, &si); + if (r < 0) + goto finish; + child = 0; + + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) { + r = -EPROTO; + goto finish; + } + + free_and_replace(m->hostname, hostname); + m->machine_id = machine_id; + strv_free_and_replace(m->machine_info, machine_info); + strv_free_and_replace(m->os_release, os_release); + +finish: + for (k = 0; k < n_meta_initialized; k++) + safe_close_pair(fds + 2*k); + + return r; +} + static const char *const partition_designator_table[] = { [PARTITION_ROOT] = "root", [PARTITION_ROOT_SECONDARY] = "root-secondary", diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index cdb083be6f..53a1554a28 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -61,24 +62,33 @@ static inline int PARTITION_VERITY_OF(int p) { } typedef enum DissectImageFlags { - DISSECT_IMAGE_READ_ONLY = 1, - DISSECT_IMAGE_DISCARD_ON_LOOP = 2, /* Turn on "discard" if on a loop device and file system supports it */ - DISSECT_IMAGE_DISCARD = 4, /* Turn on "discard" if file system supports it, on all block devices */ - DISSECT_IMAGE_DISCARD_ON_CRYPTO = 8, /* Turn on "discard" also on crypto devices */ + DISSECT_IMAGE_READ_ONLY = 1 << 0, + DISSECT_IMAGE_DISCARD_ON_LOOP = 1 << 1, /* Turn on "discard" if on a loop device and file system supports it */ + DISSECT_IMAGE_DISCARD = 1 << 2, /* Turn on "discard" if file system supports it, on all block devices */ + DISSECT_IMAGE_DISCARD_ON_CRYPTO = 1 << 3, /* Turn on "discard" also on crypto devices */ DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD | DISSECT_IMAGE_DISCARD_ON_CRYPTO, - DISSECT_IMAGE_GPT_ONLY = 16, /* Only recognize images with GPT partition tables */ - DISSECT_IMAGE_REQUIRE_ROOT = 32, /* Don't accept disks without root partition */ + DISSECT_IMAGE_GPT_ONLY = 1 << 4, /* Only recognize images with GPT partition tables */ + DISSECT_IMAGE_REQUIRE_ROOT = 1 << 5, /* Don't accept disks without root partition */ + DISSECT_IMAGE_MOUNT_ROOT_ONLY = 1 << 6, /* Mount only the root partition */ + DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7, /* Mount only non-root partitions */ } DissectImageFlags; struct DissectedImage { bool encrypted:1; bool verity:1; /* verity available and usable */ bool can_verity:1; /* verity available, but not necessarily used */ + DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX]; + + char *hostname; + sd_id128_t machine_id; + char **machine_info; + char **os_release; }; +int probe_filesystem(const char *node, char **ret_fstype); int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret); DissectedImage* dissected_image_unref(DissectedImage *m); @@ -86,7 +96,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref); int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret); int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret); -int dissected_image_mount(DissectedImage *m, const char *dest, DissectImageFlags flags); +int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags); + +int dissected_image_acquire_metadata(DissectedImage *m); DecryptedImage* decrypted_image_unref(DecryptedImage *p); DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 4739cc880b..8c807e0e23 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -692,23 +693,26 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char } int dns_name_between(const char *a, const char *b, const char *c) { - int n; - /* Determine if b is strictly greater than a and strictly smaller than c. We consider the order of names to be circular, so that if a is strictly greater than c, we consider b to be between them if it is either greater than a or smaller than c. This is how the canonical DNS name order used in NSEC records work. */ - n = dns_name_compare_func(a, c); - if (n == 0) - return -EINVAL; - else if (n < 0) - /* a<---b--->c */ + if (dns_name_compare_func(a, c) < 0) + /* + a and c are properly ordered: + a<---b--->c + */ return dns_name_compare_func(a, b) < 0 && dns_name_compare_func(b, c) < 0; else - /* <--b--c a--b--> */ + /* + a and c are equal or 'reversed': + <--b--c a-----> + or: + <-----c a--b--> + */ return dns_name_compare_func(b, c) < 0 || dns_name_compare_func(a, b) < 0; } @@ -958,6 +962,12 @@ bool dns_srv_type_is_valid(const char *name) { return c == 2; /* exactly two labels */ } +bool dnssd_srv_type_is_valid(const char *name) { + return dns_srv_type_is_valid(name) && + ((dns_name_endswith(name, "_tcp") > 0) || + (dns_name_endswith(name, "_udp") > 0)); /* Specific to DNS-SD. RFC 6763, Section 7 */ +} + bool dns_service_name_is_valid(const char *name) { size_t l; diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index a44d9d48d4..cd3dbb7a89 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -95,6 +96,7 @@ bool dns_name_is_single_label(const char *name); int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical); bool dns_srv_type_is_valid(const char *name); +bool dnssd_srv_type_is_valid(const char *name); bool dns_service_name_is_valid(const char *name); int dns_service_join(const char *name, const char *type, const char *domain, char **ret); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 059b50dbd0..f0966874ee 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -127,7 +128,11 @@ static int unit_file_find_dir( assert(path); r = chase_symlinks(path, original_root, 0, &chased); - if (r == -ENOENT) /* Ignore -ENOENT, after all most units won't have a drop-in dir */ + /* Ignore -ENOENT, after all most units won't have a drop-in dir. + * Also ignore -ENAMETOOLONG, users are not even able to create + * the drop-in dir in such case. This mostly happens for device units with long /sys path. + * */ + if (IN_SET(r, -ENOENT, -ENAMETOOLONG)) return 0; if (r < 0) return log_full_errno(LOG_WARNING, r, "Failed to canonicalize path %s: %m", path); diff --git a/src/shared/dropin.h b/src/shared/dropin.h index a2b8cdce61..102fc9403c 100644 --- a/src/shared/dropin.h +++ b/src/shared/dropin.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/efivars.c b/src/shared/efivars.c index a3850bede2..9ca51cf750 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -84,10 +85,10 @@ bool is_efi_boot(void) { } static int read_flag(const char *varname) { - int r; _cleanup_free_ void *v = NULL; - size_t s; uint8_t b; + size_t s; + int r; r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s); if (r < 0) @@ -97,8 +98,7 @@ static int read_flag(const char *varname) { return -EINVAL; b = *(uint8_t *)v; - r = b > 0; - return r; + return b > 0; } bool is_efi_secure_boot(void) { @@ -110,29 +110,33 @@ bool is_efi_secure_boot_setup_mode(void) { } int efi_reboot_to_firmware_supported(void) { - int r; - size_t s; - uint64_t b; _cleanup_free_ void *v = NULL; + uint64_t b; + size_t s; + int r; if (!is_efi_boot() || detect_container() > 0) return -EOPNOTSUPP; r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s); + if (r == -ENOENT) /* variable doesn't exist? it's not supported then */ + return -EOPNOTSUPP; if (r < 0) return r; - else if (s != sizeof(uint64_t)) + if (s != sizeof(uint64_t)) return -EINVAL; - b = *(uint64_t *)v; - b &= EFI_OS_INDICATIONS_BOOT_TO_FW_UI; - return b > 0 ? 0 : -EOPNOTSUPP; + b = *(uint64_t*) v; + if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) + return -EOPNOTSUPP; /* bit unset? it's not supported then */ + + return 0; } static int get_os_indications(uint64_t *os_indication) { - int r; - size_t s; _cleanup_free_ void *v = NULL; + size_t s; + int r; r = efi_reboot_to_firmware_supported(); if (r < 0) diff --git a/src/shared/efivars.h b/src/shared/efivars.h index 72bace0d07..9a4880de7d 100644 --- a/src/shared/efivars.h +++ b/src/shared/efivars.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/fdset.c b/src/shared/fdset.c index 090f3fdcdd..9ce1295223 100644 --- a/src/shared/fdset.c +++ b/src/shared/fdset.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/fdset.h b/src/shared/fdset.h index 16efe5bdf2..864ab676f7 100644 --- a/src/shared/fdset.h +++ b/src/shared/fdset.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 6d295ea65b..a6859462ac 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -77,7 +78,8 @@ static int entry_fill_basics( if (out_interface) { size_t l = strlen(out_interface); - assert(l < sizeof entry->ip.outiface && l < sizeof entry->ip.outiface_mask); + assert(l < sizeof entry->ip.outiface); + assert(l < sizeof entry->ip.outiface_mask); strcpy(entry->ip.outiface, out_interface); memset(entry->ip.outiface_mask, 0xFF, l + 1); diff --git a/src/shared/firewall-util.h b/src/shared/firewall-util.h index 5915266b4b..fd7e3b456e 100644 --- a/src/shared/firewall-util.h +++ b/src/shared/firewall-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index ec2e868ca8..bcd7b43084 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h index bbf0441351..87f82dcfb4 100644 --- a/src/shared/fstab-util.h +++ b/src/shared/fstab-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/gcrypt-util.c b/src/shared/gcrypt-util.c index e10a38dcfc..1bfb776725 100644 --- a/src/shared/gcrypt-util.c +++ b/src/shared/gcrypt-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** diff --git a/src/shared/gcrypt-util.h b/src/shared/gcrypt-util.h index f08ed6052c..69faf08e56 100644 --- a/src/shared/gcrypt-util.h +++ b/src/shared/gcrypt-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** diff --git a/src/shared/generator.c b/src/shared/generator.c index 18fc469098..3495a7ef7d 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -32,6 +33,7 @@ #include "mkdir.h" #include "path-util.h" #include "special.h" +#include "specifier.h" #include "string-util.h" #include "time-util.h" #include "unit-name.h" @@ -54,15 +56,19 @@ int generator_add_symlink(const char *root, const char *dst, const char *dep_typ } static int write_fsck_sysroot_service(const char *dir, const char *what) { - _cleanup_free_ char *device = NULL, *escaped = NULL; + _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL; _cleanup_fclose_ FILE *f = NULL; const char *unit; int r; - escaped = cescape(what); + escaped = specifier_escape(what); if (!escaped) return log_oom(); + escaped2 = cescape(escaped); + if (!escaped2) + return log_oom(); + unit = strjoina(dir, "/systemd-fsck-root.service"); log_debug("Creating %s", unit); @@ -77,8 +83,8 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { fprintf(f, "# Automatically generated by %1$s\n\n" "[Unit]\n" - "Documentation=man:systemd-fsck-root.service(8)\n" "Description=File System Check on %2$s\n" + "Documentation=man:systemd-fsck-root.service(8)\n" "DefaultDependencies=no\n" "BindsTo=%3$s\n" "After=initrd-root-device.target local-fs-pre.target %3$s\n" @@ -90,9 +96,9 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { "ExecStart=" SYSTEMD_FSCK_PATH " %4$s\n" "TimeoutSec=0\n", program_invocation_short_name, - what, + escaped, device, - escaped); + escaped2); r = fflush_and_check(f); if (r < 0) @@ -242,7 +248,8 @@ int generator_write_device_deps( r = unit_name_from_path(node, ".device", &unit); if (r < 0) - return log_error_errno(r, "Failed to make unit name from path: %m"); + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + node); /* See mount_add_default_dependencies for explanation why we create such * dependencies. */ @@ -260,7 +267,8 @@ int generator_write_initrd_root_device_deps(const char *dir, const char *what) { r = unit_name_from_path(what, ".device", &unit); if (r < 0) - return log_error_errno(r, "Failed to make unit name from path: %m"); + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + what); return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device", "# Automatically generated by %s\n\n" @@ -271,3 +279,206 @@ int generator_write_initrd_root_device_deps(const char *dir, const char *what) { unit, unit); } + +int generator_hook_up_mkswap( + const char *dir, + const char *what) { + + _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *unit_file; + int r; + + node = fstab_node_to_udev_node(what); + if (!node) + return log_oom(); + + /* Nothing to work on. */ + if (!is_device_path(node)) { + log_error("Cannot format something that is not a device node: %s", node); + return -EINVAL; + } + + r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", + node); + + unit_file = strjoina(dir, "/", unit); + log_debug("Creating %s", unit_file); + + escaped = cescape(node); + if (!escaped) + return log_oom(); + + r = unit_name_from_path(what, ".swap", &where_unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + what); + + f = fopen(unit_file, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", + unit_file); + + fprintf(f, + "# Automatically generated by %s\n\n" + "[Unit]\n" + "Description=Make Swap on %%f\n" + "Documentation=man:systemd-mkswap@.service(8)\n" + "DefaultDependencies=no\n" + "BindsTo=%%i.device\n" + "After=%%i.device\n" + "Before=%s\n" + "Before=shutdown.target\n" + "\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n" + "TimeoutSec=0\n", + program_invocation_short_name, + where_unit, + escaped); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit_file); + + return generator_add_symlink(dir, where_unit, "requires", unit); +} + +int generator_hook_up_mkfs( + const char *dir, + const char *what, + const char *where, + const char *type) { + + _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *unit_file; + int r; + + node = fstab_node_to_udev_node(what); + if (!node) + return log_oom(); + + /* Nothing to work on. */ + if (!is_device_path(node)) { + log_error("Cannot format something that is not a device node: %s", node); + return -EINVAL; + } + + if (!type || streq(type, "auto")) { + log_error("Cannot format partition %s, filesystem type is not specified", node); + return -EINVAL; + } + + r = unit_name_from_path_instance("systemd-mkfs", node, ".service", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", + node); + + unit_file = strjoina(dir, "/", unit); + log_debug("Creating %s", unit_file); + + escaped = cescape(node); + if (!escaped) + return log_oom(); + + r = unit_name_from_path(where, ".mount", &where_unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + where); + + f = fopen(unit_file, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", + unit_file); + + fprintf(f, + "# Automatically generated by %s\n\n" + "[Unit]\n" + "Description=Make File System on %%f\n" + "Documentation=man:systemd-mkfs@.service(8)\n" + "DefaultDependencies=no\n" + "BindsTo=%%i.device\n" + "After=%%i.device\n" + /* fsck might or might not be used, so let's be safe and order + * ourselves before both systemd-fsck@.service and the mount unit. */ + "Before=systemd-fsck@%%i.service\n" + "Before=%s\n" + "Before=shutdown.target\n" + "\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n" + "TimeoutSec=0\n", + program_invocation_short_name, + where_unit, + type, + escaped); + // XXX: what about local-fs-pre.target? + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit_file); + + return generator_add_symlink(dir, where_unit, "requires", unit); +} + +int generator_hook_up_growfs( + const char *dir, + const char *where, + const char *target) { + + _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *unit_file; + int r; + + escaped = cescape(where); + if (!escaped) + return log_oom(); + + r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", + where); + + r = unit_name_from_path(where, ".mount", &where_unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", + where); + + unit_file = strjoina(dir, "/", unit); + log_debug("Creating %s", unit_file); + + f = fopen(unit_file, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", + unit_file); + + fprintf(f, + "# Automatically generated by %s\n\n" + "[Unit]\n" + "Description=Grow File System on %%f\n" + "Documentation=man:systemd-growfs@.service(8)\n" + "DefaultDependencies=no\n" + "BindsTo=%%i.mount\n" + "After=%%i.mount\n" + "Before=shutdown.target\n" + "Before=%s\n" + "\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart="SYSTEMD_GROWFS_PATH " %s\n" + "TimeoutSec=0\n", + program_invocation_short_name, + target, + escaped); + + return generator_add_symlink(dir, where_unit, "wants", unit); +} diff --git a/src/shared/generator.h b/src/shared/generator.h index e70016839f..32d1ad021c 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -38,11 +39,24 @@ int generator_write_timeouts( char **filtered); int generator_write_device_deps( - const char *dir, - const char *what, - const char *where, - const char *opts); + const char *dir, + const char *what, + const char *where, + const char *opts); int generator_write_initrd_root_device_deps( const char *dir, const char *what); + +int generator_hook_up_mkswap( + const char *dir, + const char *what); +int generator_hook_up_mkfs( + const char *dir, + const char *what, + const char *where, + const char *type); +int generator_hook_up_growfs( + const char *dir, + const char *where, + const char *target); diff --git a/src/shared/gpt.h b/src/shared/gpt.h index cc752006fa..7589f6fb7a 100644 --- a/src/shared/gpt.h +++ b/src/shared/gpt.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/ima-util.c b/src/shared/ima-util.c index 789064d653..064f38be6f 100644 --- a/src/shared/ima-util.c +++ b/src/shared/ima-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/ima-util.h b/src/shared/ima-util.h index 5be94761fd..5633c78be9 100644 --- a/src/shared/ima-util.h +++ b/src/shared/ima-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/import-util.c b/src/shared/import-util.c index ab701ad8b2..07ba216e93 100644 --- a/src/shared/import-util.c +++ b/src/shared/import-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/import-util.h b/src/shared/import-util.h index 77b17d91f3..583845b1a4 100644 --- a/src/shared/import-util.h +++ b/src/shared/import-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index c10ed3d311..aaab2e6665 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -93,7 +94,7 @@ static int specifier_instance(char specifier, void *data, void *userdata, char * return r; if (isempty(instance)) { - r = free_and_strdup(&instance, i->default_instance ?: ""); + r = free_and_strdup(&instance, strempty(i->default_instance)); if (r < 0) return r; } @@ -102,34 +103,6 @@ static int specifier_instance(char specifier, void *data, void *userdata, char * return 0; } -static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - char *t; - - /* If we are UID 0 (root), this will not result in NSS, - * otherwise it might. This is good, as we want to be able to - * run this in PID 1, where our user ID is 0, but where NSS - * lookups are not allowed. - - * We don't user getusername_malloc() here, because we don't want to look - * at $USER, to remain consistent with specifer_user_id() below. - */ - - t = uid_to_name(getuid()); - if (!t) - return -ENOMEM; - - *ret = t; - return 0; -} - -static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) { - - if (asprintf(ret, UID_FMT, getuid()) < 0) - return -ENOMEM; - - return 0; -} - int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) { /* This is similar to unit_full_printf() but does not support diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h index 8a570fc265..d868f65cfa 100644 --- a/src/shared/install-printf.h +++ b/src/shared/install-printf.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/install.c b/src/shared/install.c index 7598bf6a23..05ccc586a9 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -991,23 +992,11 @@ static void install_info_free(UnitFileInstallInfo *i) { free(i); } -static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) { - UnitFileInstallInfo *i; - - if (!m) - return NULL; - - while ((i = ordered_hashmap_steal_first(m))) - install_info_free(i); - - return ordered_hashmap_free(m); -} - static void install_context_done(InstallContext *c) { assert(c); - c->will_process = install_info_hashmap_free(c->will_process); - c->have_processed = install_info_hashmap_free(c->have_processed); + c->will_process = ordered_hashmap_free_with_destructor(c->will_process, install_info_free); + c->have_processed = ordered_hashmap_free_with_destructor(c->have_processed, install_info_free); } static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) { @@ -1128,15 +1117,14 @@ static int config_parse_alias( void *data, void *userdata) { - const char *name; UnitType type; + assert(unit); assert(filename); assert(lvalue); assert(rvalue); - name = basename(filename); - type = unit_name_to_type(name); + type = unit_name_to_type(unit); if (!unit_type_may_alias(type)) return log_syntax(unit, LOG_WARNING, filename, line, 0, "Alias= is not allowed for %s units, ignoring.", @@ -1162,6 +1150,7 @@ static int config_parse_also( InstallContext *c = data; int r; + assert(unit); assert(filename); assert(lvalue); assert(rvalue); @@ -1209,20 +1198,19 @@ static int config_parse_default_instance( void *userdata) { UnitFileInstallInfo *i = data; - const char *name; _cleanup_free_ char *printed = NULL; int r; + assert(unit); assert(filename); assert(lvalue); assert(rvalue); - name = basename(filename); - if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) + if (unit_name_is_valid(unit, UNIT_NAME_INSTANCE)) /* When enabling an instance, we might be using a template unit file, * but we should ignore DefaultInstance silently. */ return 0; - if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) + if (!unit_name_is_valid(unit, UNIT_NAME_TEMPLATE)) return log_syntax(unit, LOG_WARNING, filename, line, 0, "DefaultInstance= only makes sense for template units, ignoring."); @@ -1251,7 +1239,6 @@ static int unit_file_load( {} }; - const char *name; UnitType type; _cleanup_fclose_ FILE *f = NULL; _cleanup_close_ int fd = -1; @@ -1261,9 +1248,8 @@ static int unit_file_load( assert(info); assert(path); - name = basename(path); - type = unit_name_to_type(name); - if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && + type = unit_name_to_type(info->name); + if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type)) return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type)); @@ -1308,10 +1294,10 @@ static int unit_file_load( return -errno; fd = -1; - r = config_parse(NULL, path, f, + r = config_parse(info->name, path, f, NULL, config_item_table_lookup, items, - true, true, false, info); + CONFIG_PARSE_RELAXED|CONFIG_PARSE_ALLOW_INCLUDE, info); if (r < 0) return log_debug_errno(r, "Failed to parse %s: %m", info->name); @@ -1398,8 +1384,15 @@ static int unit_file_search( SearchFlags flags) { _cleanup_free_ char *template = NULL; + _cleanup_strv_free_ char **dirs = NULL; + _cleanup_strv_free_ char **files = NULL; + const char *dropin_dir_name = NULL; + const char *dropin_template_dir_name = NULL; + char **p; int r; + int result; + bool found_unit = false; assert(info); assert(paths); @@ -1413,6 +1406,12 @@ static int unit_file_search( assert(info->name); + if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { + r = unit_name_template(info->name, &template); + if (r < 0) + return r; + } + STRV_FOREACH(p, paths->search_path) { _cleanup_free_ char *path = NULL; @@ -1421,23 +1420,23 @@ static int unit_file_search( return -ENOMEM; r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); + if (r >= 0) { info->path = path; path = NULL; - return r; + result = r; + found_unit = true; + break; } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES)) return r; } - if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { + if (!found_unit && template) { + /* Unit file doesn't exist, however instance * enablement was requested. We will check if it is * possible to load template unit file. */ - r = unit_name_template(info->name, &template); - if (r < 0) - return r; - STRV_FOREACH(p, paths->search_path) { _cleanup_free_ char *path = NULL; @@ -1449,14 +1448,62 @@ static int unit_file_search( if (r >= 0) { info->path = path; path = NULL; - return r; + result = r; + found_unit = true; + break; } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES)) return r; } } - log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template)); - return -ENOENT; + if (!found_unit) { + log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template)); + return -ENOENT; + } + + /* Search for drop-in directories */ + + dropin_dir_name = strjoina(info->name, ".d"); + STRV_FOREACH(p, paths->search_path) { + char *path; + + path = path_join(NULL, *p, dropin_dir_name); + if (!path) + return -ENOMEM; + + r = strv_consume(&dirs, path); + if (r < 0) + return r; + } + + if (template) { + dropin_template_dir_name = strjoina(template, ".d"); + STRV_FOREACH(p, paths->search_path) { + char *path; + + path = path_join(NULL, *p, dropin_template_dir_name); + if (!path) + return -ENOMEM; + + r = strv_consume(&dirs, path); + if (r < 0) + return r; + } + } + + /* Load drop-in conf files */ + + r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) dirs); + if (r < 0) + return log_debug_errno(r, "Failed to get list of conf files: %m"); + + STRV_FOREACH(p, files) { + r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags); + if (r < 0) + return log_debug_errno(r, "Failed to load conf file %s: %m", *p); + } + + return result; } static int install_info_follow( @@ -2937,7 +2984,7 @@ static int preset_prepare_one( if (r < 0) return r; if (!streq(name, i->name)) { - log_debug("Skipping %s because is an alias for %s", name, i->name); + log_debug("Skipping %s because it is an alias for %s.", name, i->name); return 0; } @@ -3063,6 +3110,8 @@ int unit_file_preset_all( else if (r == -ENOLINK) r = unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, de->d_name, NULL); + else if (r == -EADDRNOTAVAIL) /* Ignore generated/transient units when applying preset */ + continue; if (r < 0) return r; } @@ -3080,12 +3129,7 @@ static void unit_file_list_free_one(UnitFileList *f) { } Hashmap* unit_file_list_free(Hashmap *h) { - UnitFileList *i; - - while ((i = hashmap_steal_first(h))) - unit_file_list_free_one(i); - - return hashmap_free(h); + return hashmap_free_with_destructor(h, unit_file_list_free_one); } DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); diff --git a/src/shared/install.h b/src/shared/install.h index c1fcbe96ed..6d7518d72a 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/journal-util.c b/src/shared/journal-util.c index fff3dfc9d1..eb7a75295f 100644 --- a/src/shared/journal-util.c +++ b/src/shared/journal-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -149,3 +150,41 @@ int journal_access_check_and_warn(sd_journal *j, bool quiet) { return r; } + +bool journal_field_valid(const char *p, size_t l, bool allow_protected) { + const char *a; + + /* We kinda enforce POSIX syntax recommendations for + environment variables here, but make a couple of additional + requirements. + + http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */ + + if (l == (size_t) -1) + l = strlen(p); + + /* No empty field names */ + if (l <= 0) + return false; + + /* Don't allow names longer than 64 chars */ + if (l > 64) + return false; + + /* Variables starting with an underscore are protected */ + if (!allow_protected && p[0] == '_') + return false; + + /* Don't allow digits as first character */ + if (p[0] >= '0' && p[0] <= '9') + return false; + + /* Only allow A-Z0-9 and '_' */ + for (a = p; a < p + l; a++) + if ((*a < 'A' || *a > 'Z') && + (*a < '0' || *a > '9') && + *a != '_') + return false; + + return true; +} diff --git a/src/shared/journal-util.h b/src/shared/journal-util.h index 499e6c62ec..ef5e314d37 100644 --- a/src/shared/journal-util.h +++ b/src/shared/journal-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -19,7 +20,10 @@ ***/ #include <stdbool.h> +#include <sys/types.h> #include "sd-journal.h" +bool journal_field_valid(const char *p, size_t l, bool allow_protected); + int journal_access_check_and_warn(sd_journal *j, bool quiet); diff --git a/src/shared/linux/auto_dev-ioctl.h b/src/shared/linux/auto_dev-ioctl.h index aeaeb3ea7a..b3555fb2c8 100644 --- a/src/shared/linux/auto_dev-ioctl.h +++ b/src/shared/linux/auto_dev-ioctl.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright 2008 Red Hat, Inc. All rights reserved. * Copyright 2008 Ian Kent <raven@themaw.net> diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 0626d80b19..afc3dcd219 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -48,6 +49,7 @@ #include "stdio-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "terminal-util.h" #include "time-util.h" #include "utf8.h" @@ -79,37 +81,76 @@ static int print_catalog(FILE *f, sd_journal *j) { return 0; } -static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) { - size_t fl, nl; +static int parse_field(const void *data, size_t length, const char *field, size_t field_len, char **target, size_t *target_len) { + size_t nl; char *buf; assert(data); assert(field); assert(target); - fl = strlen(field); - if (length < fl) + if (length < field_len) return 0; - if (memcmp(data, field, fl)) + if (memcmp(data, field, field_len)) return 0; - nl = length - fl; + nl = length - field_len; - buf = newdup_suffix0(char, (const char*) data + fl, nl); + buf = newdup_suffix0(char, (const char*) data + field_len, nl); if (!buf) return log_oom(); free(*target); *target = buf; - if (target_size) - *target_size = nl; + if (target_len) + *target_len = nl; return 1; } +typedef struct ParseFieldVec { + const char *field; + size_t field_len; + char **target; + size_t *target_len; +} ParseFieldVec; + +#define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) \ + { .field = _field, .field_len = strlen(_field), .target = _target, .target_len = _target_len } + +static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fields, unsigned n_fields) { + unsigned i; + + for (i = 0; i < n_fields; i++) { + const ParseFieldVec *f = &fields[i]; + int r; + + r = parse_field(data, length, f->field, f->field_len, f->target, f->target_len); + if (r < 0) + return r; + else if (r > 0) + break; + } + + return 0; +} + +static int field_set_test(Set *fields, const char *name, size_t n) { + char *s = NULL; + + if (!fields) + return 1; + + s = strndupa(name, n); + if (!s) + return log_oom(); + + return set_get(fields, s) ? 1 : 0; +} + static bool shall_print(const char *p, size_t l, OutputFlags flags) { assert(p); @@ -327,7 +368,8 @@ static int output_short( sd_journal *j, OutputMode mode, unsigned n_columns, - OutputFlags flags) { + OutputFlags flags, + Set *output_fields) { int r; const void *data; @@ -337,6 +379,17 @@ static int output_short( size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0; int p = LOG_INFO; bool ellipsized = false; + const ParseFieldVec fields[] = { + PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len), + PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len), + PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len), + PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len), + PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len), + PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len), + PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len), + PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len), + PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len), + }; assert(f); assert(j); @@ -351,55 +404,7 @@ static int output_short( JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { - r = parse_field(data, length, "PRIORITY=", &priority, &priority_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_COMM=", &comm, &comm_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_PID=", &pid, &pid_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len); - if (r < 0) - return r; - else if (r > 0) - continue; - - r = parse_field(data, length, "MESSAGE=", &message, &message_len); + r = parse_fieldv(data, length, fields, ELEMENTSOF(fields)); if (r < 0) return r; } @@ -477,7 +482,8 @@ static int output_verbose( sd_journal *j, OutputMode mode, unsigned n_columns, - OutputFlags flags) { + OutputFlags flags, + Set *output_fields) { const void *data; size_t length; @@ -500,7 +506,9 @@ static int output_verbose( else { _cleanup_free_ char *value = NULL; - r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, NULL); + r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", + STRLEN("_SOURCE_REALTIME_TIMESTAMP="), &value, + NULL); if (r < 0) return r; assert(r > 0); @@ -538,6 +546,12 @@ static int output_verbose( } fieldlen = c - (const char*) data; + r = field_set_test(output_fields, data, fieldlen); + if (r < 0) + return r; + if (!r) + continue; + if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) { on = ANSI_HIGHLIGHT; off = ANSI_NORMAL; @@ -575,7 +589,8 @@ static int output_export( sd_journal *j, OutputMode mode, unsigned n_columns, - OutputFlags flags) { + OutputFlags flags, + Set *output_fields) { sd_id128_t boot_id; char sid[33]; @@ -612,6 +627,7 @@ static int output_export( sd_id128_to_string(boot_id, sid)); JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { + const char *c; /* We already printed the boot id, from the data in * the header, hence let's suppress it here */ @@ -619,18 +635,23 @@ static int output_export( startswith(data, "_BOOT_ID=")) continue; + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + r = field_set_test(output_fields, data, c - (const char *) data); + if (r < 0) + return r; + if (!r) + continue; + if (utf8_is_printable_newline(data, length, false)) fwrite(data, length, 1, f); else { - const char *c; uint64_t le64; - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - fwrite(data, c - (const char*) data, 1, f); fputc('\n', f); le64 = htole64(length - (c - (const char*) data) - 1); @@ -706,7 +727,8 @@ static int output_json( sd_journal *j, OutputMode mode, unsigned n_columns, - OutputFlags flags) { + OutputFlags flags, + Set *output_fields) { uint64_t realtime, monotonic; _cleanup_free_ char *cursor = NULL; @@ -825,13 +847,6 @@ static int output_json( if (!eq) continue; - if (separator) { - if (mode == OUTPUT_JSON_PRETTY) - fputs(",\n\t", f); - else - fputs(", ", f); - } - m = eq - (const char*) data; n = strndup(data, m); @@ -840,6 +855,18 @@ static int output_json( goto finish; } + if (output_fields && !set_get(output_fields, n)) { + free(n); + continue; + } + + if (separator) { + if (mode == OUTPUT_JSON_PRETTY) + fputs(",\n\t", f); + else + fputs(", ", f); + } + u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk)); if (u == 0) { /* We already printed this, let's jump to the next */ @@ -924,7 +951,8 @@ static int output_cat( sd_journal *j, OutputMode mode, unsigned n_columns, - OutputFlags flags) { + OutputFlags flags, + Set *output_fields) { const void *data; size_t l; @@ -957,7 +985,8 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])( sd_journal*j, OutputMode mode, unsigned n_columns, - OutputFlags flags) = { + OutputFlags flags, + Set *output_fields) = { [OUTPUT_SHORT] = output_short, [OUTPUT_SHORT_ISO] = output_short, @@ -980,16 +1009,28 @@ int output_journal( OutputMode mode, unsigned n_columns, OutputFlags flags, + char **output_fields, bool *ellipsized) { int ret; + _cleanup_set_free_free_ Set *fields = NULL; assert(mode >= 0); assert(mode < _OUTPUT_MODE_MAX); if (n_columns <= 0) n_columns = columns(); - ret = output_funcs[mode](f, j, mode, n_columns, flags); + if (output_fields) { + fields = set_new(&string_hash_ops); + if (!fields) + return log_oom(); + + ret = set_put_strdupv(fields, output_fields); + if (ret < 0) + return ret; + } + + ret = output_funcs[mode](f, j, mode, n_columns, flags, fields); if (ellipsized && ret > 0) *ellipsized = true; @@ -1071,7 +1112,7 @@ static int show_journal(FILE *f, line++; maybe_print_begin_newline(f, &flags); - r = output_journal(f, j, mode, n_columns, flags, ellipsized); + r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized); if (r < 0) return r; } diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 6643440881..eaa69b6e90 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -37,6 +38,7 @@ int output_journal( OutputMode mode, unsigned n_columns, OutputFlags flags, + char **output_fields, bool *ellipsized); int add_match_this_boot(sd_journal *j, const char *machine); diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index 047e213634..097de690e5 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -151,9 +152,7 @@ LoopDevice* loop_device_unref(LoopDevice *d) { } free(d->node); - free(d); - - return NULL; + return mfree(d); } void loop_device_relinquish(LoopDevice *d) { diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h index 45fead5f18..7e18e5779d 100644 --- a/src/shared/loop-util.h +++ b/src/shared/loop-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 859e5ffc1a..66eefb3036 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -33,12 +34,17 @@ #include "chattr-util.h" #include "copy.h" #include "dirent-util.h" +#include "dissect-image.h" #include "env-util.h" #include "fd-util.h" +#include "fileio.h" #include "fs-util.h" #include "hashmap.h" +#include "hostname-util.h" +#include "id128-util.h" #include "lockfile-util.h" #include "log.h" +#include "loop-util.h" #include "machine-image.h" #include "macro.h" #include "mkdir.h" @@ -64,6 +70,11 @@ Image *image_unref(Image *i) { free(i->name); free(i->path); + + free(i->hostname); + strv_free(i->machine_info); + strv_free(i->os_release); + return mfree(i); } @@ -171,9 +182,8 @@ static int image_make( assert(filename); - /* We explicitly *do* follow symlinks here, since we want to - * allow symlinking trees into /var/lib/machines/, and treat - * them normally. */ + /* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block + * devices into /var/lib/machines/, and treat them normally. */ if (fstatat(dfd, filename, &st, 0) < 0) return -errno; @@ -286,6 +296,58 @@ static int image_make( (*ret)->limit = (*ret)->limit_exclusive = st.st_size; return 1; + + } else if (S_ISBLK(st.st_mode)) { + _cleanup_close_ int block_fd = -1; + uint64_t size = UINT64_MAX; + + /* A block device */ + + if (!ret) + return 1; + + if (!pretty) + pretty = filename; + + block_fd = openat(dfd, filename, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (block_fd < 0) + log_debug_errno(errno, "Failed to open block device %s/%s, ignoring: %m", path, filename); + else { + if (fstat(block_fd, &st) < 0) + return -errno; + if (!S_ISBLK(st.st_mode)) /* Verify that what we opened is actually what we think it is */ + return -ENOTTY; + + if (!read_only) { + int state = 0; + + if (ioctl(block_fd, BLKROGET, &state) < 0) + log_debug_errno(errno, "Failed to issue BLKROGET on device %s/%s, ignoring: %m", path, filename); + else if (state) + read_only = true; + } + + if (ioctl(block_fd, BLKGETSIZE64, &size) < 0) + log_debug_errno(errno, "Failed to issue BLKFLSBUF on device %s/%s, ignoring: %m", path, filename); + + block_fd = safe_close(block_fd); + } + + r = image_new(IMAGE_BLOCK, + pretty, + path, + filename, + !(st.st_mode & 0222) || read_only, + 0, + 0, + ret); + if (r < 0) + return r; + + if (size != 0 && size != UINT64_MAX) + (*ret)->usage = (*ret)->usage_exclusive = (*ret)->limit = (*ret)->limit_exclusive = size; + + return 1; } return 0; @@ -395,15 +457,6 @@ int image_discover(Hashmap *h) { return 0; } -void image_hashmap_free(Hashmap *map) { - Image *i; - - while ((i = hashmap_steal_first(map))) - image_unref(i); - - hashmap_free(map); -} - int image_remove(Image *i) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; _cleanup_strv_free_ char **settings = NULL; @@ -432,9 +485,15 @@ int image_remove(Image *i) { switch (i->type) { case IMAGE_SUBVOLUME: - r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); - if (r < 0) - return r; + + /* Let's unlink first, maybe it is a symlink? If that works we are happy. Otherwise, let's get out the + * big guns */ + if (unlink(i->path) < 0) { + r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + if (r < 0) + return r; + } + break; case IMAGE_DIRECTORY: @@ -446,6 +505,16 @@ int image_remove(Image *i) { break; + case IMAGE_BLOCK: + + /* If this is inside of /dev, then it's a real block device, hence let's not touch the device node + * itself (but let's remove the stuff stored alongside it). If it's anywhere else, let's try to unlink + * the thing (it's most likely a symlink after all). */ + + if (path_startswith(i->path, "/dev")) + break; + + _fallthrough_; case IMAGE_RAW: if (unlink(i->path) < 0) return -errno; @@ -530,12 +599,20 @@ int image_rename(Image *i, const char *new_name) { if (file_attr & FS_IMMUTABLE_FL) (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); - /* fall through */ - + _fallthrough_; case IMAGE_SUBVOLUME: new_path = file_in_same_dir(i->path, new_name); break; + case IMAGE_BLOCK: + + /* Refuse renaming raw block devices in /dev, the names are picked by udev after all. */ + if (path_startswith(i->path, "/dev")) + return -EROFS; + + new_path = file_in_same_dir(i->path, new_name); + break; + case IMAGE_RAW: { const char *fn; @@ -563,13 +640,8 @@ int image_rename(Image *i, const char *new_name) { if (file_attr & FS_IMMUTABLE_FL) (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL); - free(i->path); - i->path = new_path; - new_path = NULL; - - free(i->name); - i->name = nn; - nn = NULL; + free_and_replace(i->path, new_path); + free_and_replace(i->name, nn); STRV_FOREACH(j, settings) { r = rename_auxiliary_file(*j, new_name, ".nspawn"); @@ -659,6 +731,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) { r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, COPY_REFLINK); break; + case IMAGE_BLOCK: default: return -EOPNOTSUPP; } @@ -682,6 +755,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) { int image_read_only(Image *i, bool b) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; int r; + assert(i); if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) @@ -737,6 +811,26 @@ int image_read_only(Image *i, bool b) { break; } + case IMAGE_BLOCK: { + _cleanup_close_ int fd = -1; + struct stat st; + int state = b; + + fd = open(i->path, O_CLOEXEC|O_RDONLY|O_NONBLOCK|O_NOCTTY); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + if (!S_ISBLK(st.st_mode)) + return -ENOTTY; + + if (ioctl(fd, BLKROSET, &state) < 0) + return -errno; + + break; + } + default: return -EOPNOTSUPP; } @@ -772,13 +866,24 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile return -EBUSY; if (stat(path, &st) >= 0) { - if (asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino) < 0) + if (S_ISBLK(st.st_mode)) + r = asprintf(&p, "/run/systemd/nspawn/locks/block-%u:%u", major(st.st_rdev), minor(st.st_rdev)); + else if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) + r = asprintf(&p, "/run/systemd/nspawn/locks/inode-%lu:%lu", (unsigned long) st.st_dev, (unsigned long) st.st_ino); + else + return -ENOTTY; + + if (r < 0) return -ENOMEM; } - r = make_lock_file_for(path, operation, &t); - if (r < 0) - return r; + /* For block devices we don't need the "local" lock, as the major/minor lock above should be sufficient, since + * block devices are device local anyway. */ + if (!path_startswith(path, "/dev")) { + r = make_lock_file_for(path, operation, &t); + if (r < 0) + return r; + } if (p) { mkdir_p("/run/systemd/nspawn/locks", 0700); @@ -814,6 +919,118 @@ int image_set_limit(Image *i, uint64_t referenced_max) { return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max); } +int image_read_metadata(Image *i) { + _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; + int r; + + assert(i); + + r = image_path_lock(i->path, LOCK_SH|LOCK_NB, &global_lock, &local_lock); + if (r < 0) + return r; + + switch (i->type) { + + case IMAGE_SUBVOLUME: + case IMAGE_DIRECTORY: { + _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL; + sd_id128_t machine_id = SD_ID128_NULL; + _cleanup_free_ char *hostname = NULL; + _cleanup_free_ char *path = NULL; + + r = chase_symlinks("/etc/hostname", i->path, CHASE_PREFIX_ROOT, &path); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to chase /etc/hostname in image %s: %m", i->name); + else if (r >= 0) { + r = read_etc_hostname(path, &hostname); + if (r < 0) + log_debug_errno(errno, "Failed to read /etc/hostname of image %s: %m", i->name); + } + + path = mfree(path); + + r = chase_symlinks("/etc/machine-id", i->path, CHASE_PREFIX_ROOT, &path); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to chase /etc/machine-id in image %s: %m", i->name); + else if (r >= 0) { + _cleanup_close_ int fd = -1; + + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + log_debug_errno(errno, "Failed to open %s: %m", path); + else { + r = id128_read_fd(fd, ID128_PLAIN, &machine_id); + if (r < 0) + log_debug_errno(r, "Image %s contains invalid machine ID.", i->name); + } + } + + path = mfree(path); + + r = chase_symlinks("/etc/machine-info", i->path, CHASE_PREFIX_ROOT, &path); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to chase /etc/machine-info in image %s: %m", i->name); + else if (r >= 0) { + r = load_env_file_pairs(NULL, path, NULL, &machine_info); + if (r < 0) + log_debug_errno(r, "Failed to parse machine-info data of %s: %m", i->name); + } + + path = mfree(path); + + r = chase_symlinks("/etc/os-release", i->path, CHASE_PREFIX_ROOT, &path); + if (r == -ENOENT) + r = chase_symlinks("/usr/lib/os-release", i->path, CHASE_PREFIX_ROOT, &path); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to chase os-release in image: %m"); + else if (r >= 0) { + r = load_env_file_pairs(NULL, path, NULL, &os_release); + if (r < 0) + log_debug_errno(r, "Failed to parse os-release data of %s: %m", i->name); + } + + free_and_replace(i->hostname, hostname); + i->machine_id = machine_id; + strv_free_and_replace(i->machine_info, machine_info); + strv_free_and_replace(i->os_release, os_release); + + break; + } + + case IMAGE_RAW: + case IMAGE_BLOCK: { + _cleanup_(loop_device_unrefp) LoopDevice *d = NULL; + _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; + + r = loop_device_make_by_path(i->path, O_RDONLY, &d); + if (r < 0) + return r; + + r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m); + if (r < 0) + return r; + + r = dissected_image_acquire_metadata(m); + if (r < 0) + return r; + + free_and_replace(i->hostname, m->hostname); + i->machine_id = m->machine_id; + strv_free_and_replace(i->machine_info, m->machine_info); + strv_free_and_replace(i->os_release, m->os_release); + + break; + } + + default: + return -EOPNOTSUPP; + } + + i->metadata_valid = true; + + return 0; +} + int image_name_lock(const char *name, int operation, LockFile *ret) { const char *p; @@ -860,6 +1077,7 @@ static const char* const image_type_table[_IMAGE_TYPE_MAX] = { [IMAGE_DIRECTORY] = "directory", [IMAGE_SUBVOLUME] = "subvolume", [IMAGE_RAW] = "raw", + [IMAGE_BLOCK] = "block", }; DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType); diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index 7410168c4f..3df9a29a24 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -33,6 +34,7 @@ typedef enum ImageType { IMAGE_DIRECTORY, IMAGE_SUBVOLUME, IMAGE_RAW, + IMAGE_BLOCK, _IMAGE_TYPE_MAX, _IMAGE_TYPE_INVALID = -1 } ImageType; @@ -51,11 +53,20 @@ typedef struct Image { uint64_t limit; uint64_t limit_exclusive; + char *hostname; + sd_id128_t machine_id; + char **machine_info; + char **os_release; + + bool metadata_valid; + void *userdata; } Image; Image *image_unref(Image *i); -void image_hashmap_free(Hashmap *map); +static inline Hashmap* image_hashmap_free(Hashmap *map) { + return hashmap_free_with_destructor(map, image_unref); +} DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free); @@ -78,6 +89,8 @@ int image_name_lock(const char *name, int operation, LockFile *ret); int image_set_limit(Image *i, uint64_t referenced_max); +int image_read_metadata(Image *i); + static inline bool IMAGE_IS_HIDDEN(const struct Image *i) { assert(i); diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index c581bdeb79..167bcfad36 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/machine-pool.h b/src/shared/machine-pool.h index 40fe5ecb3a..6e390521e8 100644 --- a/src/shared/machine-pool.h +++ b/src/shared/machine-pool.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/meson.build b/src/shared/meson.build index 883821352e..06a944c49d 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -1,3 +1,20 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# Copyright 2017 Zbigniew JÄ™drzejewski-Szmek +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see <http://www.gnu.org/licenses/>. + shared_sources = ''' acl-util.h acpi-fpdt.c @@ -10,6 +27,8 @@ shared_sources = ''' base-filesystem.h boot-timestamps.c boot-timestamps.h + bootspec.c + bootspec.h bus-unit-util.c bus-unit-util.h bus-util.c @@ -88,6 +107,8 @@ shared_sources = ''' sysctl-util.h tests.c tests.h + tomoyo-util.c + tomoyo-util.h udev-util.h udev-util.c uid-range.c diff --git a/src/shared/nsflags.c b/src/shared/nsflags.c index aeb79b131e..05ec9feb8d 100644 --- a/src/shared/nsflags.c +++ b/src/shared/nsflags.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/nsflags.h b/src/shared/nsflags.h index 152ab8b936..dcac6cd0b2 100644 --- a/src/shared/nsflags.h +++ b/src/shared/nsflags.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c index 29dcba9f6b..5256e917a3 100644 --- a/src/shared/output-mode.c +++ b/src/shared/output-mode.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index 2a1bfd98d0..747f7eb1b9 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/pager.c b/src/shared/pager.c index 0661ff0bb9..39997278f1 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -80,9 +81,10 @@ int pager_open(bool no_pager, bool jump_to_end) { if (pager && STR_IN_SET(pager, "", "cat")) return 0; - /* Determine and cache number of columns before we spawn the - * pager so that we get the value from the actual tty */ + /* Determine and cache number of columns/lines before we spawn the pager so that we get the value from the + * actual tty */ (void) columns(); + (void) lines(); if (pipe2(fd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); diff --git a/src/shared/pager.h b/src/shared/pager.h index 893e1d2bb6..99716f8747 100644 --- a/src/shared/pager.h +++ b/src/shared/pager.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 802e4b9c8d..d57c78a8b1 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -38,7 +39,7 @@ #include "user-util.h" #include "util.h" -static int user_runtime_dir(char **ret, const char *suffix) { +int xdg_user_runtime_dir(char **ret, const char *suffix) { const char *e; char *j; @@ -57,7 +58,7 @@ static int user_runtime_dir(char **ret, const char *suffix) { return 0; } -static int user_config_dir(char **ret, const char *suffix) { +int xdg_user_config_dir(char **ret, const char *suffix) { const char *e; char *j; int r; @@ -84,7 +85,7 @@ static int user_config_dir(char **ret, const char *suffix) { return 0; } -static int user_data_dir(char **ret, const char *suffix) { +int xdg_user_data_dir(char **ret, const char *suffix) { const char *e; char *j; int r; @@ -130,23 +131,7 @@ static const char* const user_config_unit_paths[] = { NULL }; -static char** user_dirs( - const char *persistent_config, - const char *runtime_config, - const char *generator, - const char *generator_early, - const char *generator_late, - const char *transient, - const char *persistent_control, - const char *runtime_control) { - - _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; - _cleanup_free_ char *data_home = NULL; - _cleanup_strv_free_ char **res = NULL; - const char *e; - char **tmp; - int r; - +int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) { /* Implement the mechanisms defined in * * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html @@ -155,18 +140,16 @@ static char** user_dirs( * want to encourage that distributors ship their unit files * as data, and allow overriding as configuration. */ + const char *e; + _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; e = getenv("XDG_CONFIG_DIRS"); if (e) { config_dirs = strv_split(e, ":"); if (!config_dirs) - return NULL; + return -ENOMEM; } - r = user_data_dir(&data_home, "/systemd/user"); - if (r < 0 && r != -ENXIO) - return NULL; - e = getenv("XDG_DATA_DIRS"); if (e) data_dirs = strv_split(e, ":"); @@ -175,6 +158,36 @@ static char** user_dirs( "/usr/share", NULL); if (!data_dirs) + return -ENOMEM; + + *ret_config_dirs = config_dirs; + *ret_data_dirs = data_dirs; + config_dirs = data_dirs = NULL; + return 0; +} + +static char** user_dirs( + const char *persistent_config, + const char *runtime_config, + const char *generator, + const char *generator_early, + const char *generator_late, + const char *transient, + const char *persistent_control, + const char *runtime_control) { + + _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; + _cleanup_free_ char *data_home = NULL; + _cleanup_strv_free_ char **res = NULL; + char **tmp; + int r; + + r = xdg_user_dirs(&config_dirs, &data_dirs); + if (r < 0) + return NULL; + + r = xdg_user_data_dir(&data_home, "/systemd/user"); + if (r < 0 && r != -ENXIO) return NULL; /* Now merge everything we found. */ @@ -245,7 +258,6 @@ static int acquire_generator_dirs( char **generator_early, char **generator_late) { - _cleanup_(rmdir_and_freep) char *t = NULL; _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL; const char *prefix; @@ -311,7 +323,7 @@ static int acquire_transient_dir( else if (scope == UNIT_FILE_SYSTEM) transient = strdup("/run/systemd/transient"); else - return user_runtime_dir(ret, "/systemd/transient"); + return xdg_user_runtime_dir(ret, "/systemd/transient"); if (!transient) return -ENOMEM; @@ -339,11 +351,11 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru break; case UNIT_FILE_USER: - r = user_config_dir(&a, "/systemd/user"); + r = xdg_user_config_dir(&a, "/systemd/user"); if (r < 0 && r != -ENXIO) return r; - r = user_runtime_dir(runtime, "/systemd/user"); + r = xdg_user_runtime_dir(runtime, "/systemd/user"); if (r < 0) { if (r != -ENXIO) return r; @@ -399,11 +411,11 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r } case UNIT_FILE_USER: - r = user_config_dir(&a, "/systemd/system.control"); + r = xdg_user_config_dir(&a, "/systemd/system.control"); if (r < 0 && r != -ENXIO) return r; - r = user_runtime_dir(runtime, "/systemd/system.control"); + r = xdg_user_runtime_dir(runtime, "/systemd/system.control"); if (r < 0) { if (r != -ENXIO) return r; @@ -736,6 +748,14 @@ int lookup_paths_reduce(LookupPaths *p) { struct stat st; unsigned k; + /* Never strip the transient and control directories from the path */ + if (path_equal_ptr(p->search_path[c], p->transient) || + path_equal_ptr(p->search_path[c], p->persistent_control) || + path_equal_ptr(p->search_path[c], p->runtime_control)) { + c++; + continue; + } + if (p->root_dir) r = lstat(p->search_path[c], &st); else diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h index fc8b8ed8c7..42a870aa3e 100644 --- a/src/shared/path-lookup.h +++ b/src/shared/path-lookup.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -27,8 +28,8 @@ typedef struct LookupPaths LookupPaths; #include "macro.h" typedef enum LookupPathsFlags { - LOOKUP_PATHS_EXCLUDE_GENERATED = 1, - LOOKUP_PATHS_TEMPORARY_GENERATED, + LOOKUP_PATHS_EXCLUDE_GENERATED = 1 << 0, + LOOKUP_PATHS_TEMPORARY_GENERATED = 1 << 1, } LookupPathsFlags; struct LookupPaths { @@ -67,6 +68,10 @@ struct LookupPaths { }; int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); +int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs); +int xdg_user_runtime_dir(char **ret, const char *suffix); +int xdg_user_config_dir(char **ret, const char *suffix); +int xdg_user_data_dir(char **ret, const char *suffix); bool path_is_user_data_dir(const char *path); bool path_is_user_config_dir(const char *path); diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 0c92184ba5..94a4dd513f 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -170,6 +171,30 @@ static bool ignore_vhangup(PTYForward *f) { return false; } +static bool drained(PTYForward *f) { + int q = 0; + + assert(f); + + if (f->out_buffer_full > 0) + return false; + + if (f->master_readable) + return false; + + if (ioctl(f->master, TIOCINQ, &q) < 0) + log_debug_errno(errno, "TIOCINQ failed on master: %m"); + else if (q > 0) + return false; + + if (ioctl(f->master, TIOCOUTQ, &q) < 0) + log_debug_errno(errno, "TIOCOUTQ failed on master: %m"); + else if (q > 0) + return false; + + return true; +} + static int shovel(PTYForward *f) { ssize_t k; @@ -305,7 +330,7 @@ static int shovel(PTYForward *f) { /* If we were asked to drain, and there's nothing more to handle from the master, then call the callback * too. */ - if (f->drain && f->out_buffer_full == 0 && !f->master_readable) + if (f->drain && drained(f)) return pty_forward_done(f, 0); return 0; @@ -546,6 +571,28 @@ bool pty_forward_drain(PTYForward *f) { */ f->drain = true; + return drained(f); +} + +int pty_forward_set_priority(PTYForward *f, int64_t priority) { + int r; + assert(f); - return f->out_buffer_full == 0 && !f->master_readable; + r = sd_event_source_set_priority(f->stdin_event_source, priority); + if (r < 0) + return r; + + r = sd_event_source_set_priority(f->stdout_event_source, priority); + if (r < 0) + return r; + + r = sd_event_source_set_priority(f->master_event_source, priority); + if (r < 0) + return r; + + r = sd_event_source_set_priority(f->sigwinch_event_source, priority); + if (r < 0) + return r; + + return 0; } diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h index 3fad1d3b26..6a0e0c6a2b 100644 --- a/src/shared/ptyfwd.h +++ b/src/shared/ptyfwd.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -53,4 +54,6 @@ void pty_forward_set_handler(PTYForward *f, PTYForwardHandler handler, void *use bool pty_forward_drain(PTYForward *f); +int pty_forward_set_priority(PTYForward *f, int64_t priority); + DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free); diff --git a/src/shared/resolve-util.c b/src/shared/resolve-util.c index e2da81bab7..edcb8e05e7 100644 --- a/src/shared/resolve-util.c +++ b/src/shared/resolve-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h index 8636a6c134..975156ca96 100644 --- a/src/shared/resolve-util.h +++ b/src/shared/resolve-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 14a75bfffe..62742858c7 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -313,6 +314,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "set_robust_list\0" "set_thread_area\0" "set_tid_address\0" + "set_tls\0" "sigreturn\0" "time\0" "ugetrlimit\0" @@ -465,7 +467,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "stat64\0" "statfs\0" "statfs64\0" -#ifdef __PNR_statx +#ifdef __NR_statx "statx\0" #endif "symlink\0" @@ -900,20 +902,20 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter return 0; } -int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint32_t action) { +int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action) { uint32_t arch; int r; /* Similar to seccomp_load_syscall_filter_set(), but takes a raw Set* of syscalls, instead of a * SyscallFilterSet* table. */ - if (set_isempty(set) && default_action == SCMP_ACT_ALLOW) + if (hashmap_isempty(set) && default_action == SCMP_ACT_ALLOW) return 0; SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; Iterator i; - void *id; + void *id, *val; log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); @@ -921,8 +923,14 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint3 if (r < 0) return r; - SET_FOREACH(id, set, i) { - r = seccomp_rule_add_exact(seccomp, action, PTR_TO_INT(id) - 1, 0); + HASHMAP_FOREACH_KEY(val, id, set, i) { + uint32_t a = action; + int e = PTR_TO_INT(val); + + if (action != SCMP_ACT_ALLOW && e >= 0) + a = SCMP_ACT_ERRNO(e); + + r = seccomp_rule_add_exact(seccomp, a, PTR_TO_INT(id) - 1, 0); if (r < 0) { /* If the system call is not known on this architecture, then that's fine, let's ignore it */ _cleanup_free_ char *n = NULL; @@ -1433,6 +1441,14 @@ int seccomp_memory_deny_write_execute(void) { if (r < 0) continue; +#ifdef __NR_pkey_mprotect + r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(pkey_mprotect), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC)); + if (r < 0) + continue; +#endif + if (shmat_syscall != 0) { r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(shmat), 1, @@ -1515,7 +1531,7 @@ int parse_syscall_archs(char **l, Set **archs) { return 0; } -int seccomp_filter_set_add(Set *filter, bool add, const SyscallFilterSet *set) { +int seccomp_filter_set_add(Hashmap *filter, bool add, const SyscallFilterSet *set) { const char *i; int r; @@ -1543,11 +1559,11 @@ int seccomp_filter_set_add(Set *filter, bool add, const SyscallFilterSet *set) { } if (add) { - r = set_put(filter, INT_TO_PTR(id + 1)); + r = hashmap_put(filter, INT_TO_PTR(id + 1), INT_TO_PTR(-1)); if (r < 0) return r; } else - (void) set_remove(filter, INT_TO_PTR(id + 1)); + (void) hashmap_remove(filter, INT_TO_PTR(id + 1)); } } diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index 6dfa465ef3..ad2ab7f6b5 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -73,12 +74,12 @@ extern const SyscallFilterSet syscall_filter_sets[]; const SyscallFilterSet *syscall_filter_set_find(const char *name); -int seccomp_filter_set_add(Set *s, bool b, const SyscallFilterSet *set); +int seccomp_filter_set_add(Hashmap *s, bool b, const SyscallFilterSet *set); int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint32_t action, char **exclude); int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); -int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint32_t action); +int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action); int seccomp_restrict_archs(Set *archs); int seccomp_restrict_namespaces(unsigned long retain); diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 8c1624ff46..ecac98e0ab 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -58,10 +59,10 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) { {} }; - config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf", - CONF_PATHS_NULSTR("systemd/sleep.conf.d"), - "Sleep\0", config_item_table_lookup, items, - false, NULL); + (void) config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf", + CONF_PATHS_NULSTR("systemd/sleep.conf.d"), + "Sleep\0", config_item_table_lookup, items, + CONFIG_PARSE_WARN, NULL); if (streq(verb, "suspend")) { /* empty by default */ diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index ad10039ff4..fc5a81d954 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c index a46b7525f0..9af5faa3dd 100644 --- a/src/shared/spawn-ask-password-agent.c +++ b/src/shared/spawn-ask-password-agent.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/spawn-ask-password-agent.h b/src/shared/spawn-ask-password-agent.h index fb0749b13f..158f8839ab 100644 --- a/src/shared/spawn-ask-password-agent.h +++ b/src/shared/spawn-ask-password-agent.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c index 9a40147662..423069fb0e 100644 --- a/src/shared/spawn-polkit-agent.c +++ b/src/shared/spawn-polkit-agent.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/spawn-polkit-agent.h b/src/shared/spawn-polkit-agent.h index 42b2989ded..9f26fa10a7 100644 --- a/src/shared/spawn-polkit-agent.h +++ b/src/shared/spawn-polkit-agent.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -19,5 +20,22 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "bus-util.h" + int polkit_agent_open(void); void polkit_agent_close(void); + +static inline void polkit_agent_open_if_enabled( + BusTransport transport, + bool ask_password) { + + /* Open the polkit agent as a child process if necessary */ + + if (transport != BUS_TRANSPORT_LOCAL) + return; + + if (!ask_password) + return; + + polkit_agent_open(); +} diff --git a/src/shared/specifier.c b/src/shared/specifier.c index 81379041cc..23aaa88c4b 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -31,6 +32,8 @@ #include "macro.h" #include "specifier.h" #include "string-util.h" +#include "strv.h" +#include "user-util.h" /* * Generic infrastructure for replacing %x style specifiers in @@ -38,11 +41,16 @@ * */ +/* Any ASCII character or digit: our pool of potential specifiers, + * and "%" used for escaping. */ +#define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%" + int specifier_printf(const char *text, const Specifier table[], void *userdata, char **_ret) { - char *ret, *t; + size_t l; + _cleanup_free_ char *ret = NULL; + char *t; const char *f; bool percent = false; - size_t l; int r; assert(text); @@ -73,28 +81,25 @@ int specifier_printf(const char *text, const Specifier table[], void *userdata, size_t k, j; r = i->lookup(i->specifier, i->data, userdata, &w); - if (r < 0) { - free(ret); + if (r < 0) return r; - } j = t - ret; k = strlen(w); n = new(char, j + k + l + 1); - if (!n) { - free(ret); + if (!n) return -ENOMEM; - } memcpy(n, ret, j); memcpy(n + j, w, k); - free(ret); - - ret = n; - t = n + j + k; - } else { + free_and_replace(ret, n); + t = ret + j + k; + } else if (strchr(POSSIBLE_SPECIFIERS, *f)) + /* Oops, an unknown specifier. */ + return -EBADSLT; + else { *(t++) = '%'; *(t++) = *f; } @@ -113,6 +118,7 @@ int specifier_printf(const char *text, const Specifier table[], void *userdata, *t = 0; *_ret = ret; + ret = NULL; return 0; } @@ -190,3 +196,74 @@ int specifier_kernel_release(char specifier, void *data, void *userdata, char ** *ret = n; return 0; } + +int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { + char *t; + + /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able + * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed. + + * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with + * specifer_user_id() below. + */ + + t = uid_to_name(getuid()); + if (!t) + return -ENOMEM; + + *ret = t; + return 0; +} + +int specifier_user_id(char specifier, void *data, void *userdata, char **ret) { + + if (asprintf(ret, UID_FMT, getuid()) < 0) + return -ENOMEM; + + return 0; +} + +int specifier_user_home(char specifier, void *data, void *userdata, char **ret) { + + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ + + return get_home_dir(ret); +} + +int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) { + + /* On PID 1 (which runs as root) this will not result in NSS, + * which is good. See above */ + + return get_shell(ret); +} + +int specifier_escape_strv(char **l, char ***ret) { + char **z, **p, **q; + + assert(ret); + + if (strv_isempty(l)) { + *ret = NULL; + return 0; + } + + z = new(char*, strv_length(l)+1); + if (!z) + return -ENOMEM; + + for (p = l, q = z; *p; p++, q++) { + + *q = specifier_escape(*p); + if (!*q) { + strv_free(z); + return -ENOMEM; + } + } + + *q = NULL; + *ret = z; + + return 0; +} diff --git a/src/shared/specifier.h b/src/shared/specifier.h index 6b1623ee61..bd6bc55577 100644 --- a/src/shared/specifier.h +++ b/src/shared/specifier.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -19,6 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "string-util.h" + typedef int (*SpecifierCallback)(char specifier, void *data, void *userdata, char **ret); typedef struct Specifier { @@ -35,3 +38,14 @@ int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) int specifier_boot_id(char specifier, void *data, void *userdata, char **ret); int specifier_host_name(char specifier, void *data, void *userdata, char **ret); int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret); + +int specifier_user_name(char specifier, void *data, void *userdata, char **ret); +int specifier_user_id(char specifier, void *data, void *userdata, char **ret); +int specifier_user_home(char specifier, void *data, void *userdata, char **ret); +int specifier_user_shell(char specifier, void *data, void *userdata, char **ret); + +static inline char* specifier_escape(const char *string) { + return strreplace(string, "%", "%%"); +} + +int specifier_escape_strv(char **l, char ***ret); diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index afdf1ab5ad..3c51fa36f3 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/switch-root.h b/src/shared/switch-root.h index a7a080b3e8..abcdc1c65f 100644 --- a/src/shared/switch-root.h +++ b/src/shared/switch-root.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once #include <stdbool.h> diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c index e1ccb3294c..189580e3ed 100644 --- a/src/shared/sysctl-util.c +++ b/src/shared/sysctl-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -59,7 +60,7 @@ int sysctl_write(const char *property, const char *value) { log_debug("Setting '%s' to '%s'", property, value); p = strjoina("/proc/sys/", property); - return write_string_file(p, value, 0); + return write_string_file(p, value, WRITE_STRING_FILE_DISABLE_BUFFER); } int sysctl_read(const char *property, char **content) { diff --git a/src/shared/sysctl-util.h b/src/shared/sysctl-util.h index 2decb39f58..446aa6f384 100644 --- a/src/shared/sysctl-util.h +++ b/src/shared/sysctl-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/test-tables.h b/src/shared/test-tables.h index 228e510104..6b223b1ee5 100644 --- a/src/shared/test-tables.h +++ b/src/shared/test-tables.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd diff --git a/src/shared/tests.c b/src/shared/tests.c index f300bbc66f..d78ab7b069 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/tests.h b/src/shared/tests.h index 7055124990..b070f386e3 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/tomoyo-util.c b/src/shared/tomoyo-util.c new file mode 100644 index 0000000000..390fff6152 --- /dev/null +++ b/src/shared/tomoyo-util.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + This file is part of systemd. + + Copyright 2017 Shawn Landden + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <unistd.h> + +#include "tomoyo-util.h" + +bool mac_tomoyo_use(void) { + static int cached_use = -1; + + if (cached_use < 0) + cached_use = (access("/sys/kernel/security/tomoyo/version", + F_OK) == 0); + + return cached_use; +} diff --git a/src/shared/tomoyo-util.h b/src/shared/tomoyo-util.h new file mode 100644 index 0000000000..4fb309fd54 --- /dev/null +++ b/src/shared/tomoyo-util.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Shawn Landden + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.h> + +bool mac_tomoyo_use(void); diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index f708dcfa14..65a09e9c2e 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index a415be249e..c5e4197dff 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c index b6ec474390..c38b7cc984 100644 --- a/src/shared/uid-range.c +++ b/src/shared/uid-range.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/uid-range.h b/src/shared/uid-range.h index 4044eb4c9c..882f6624cd 100644 --- a/src/shared/uid-range.h +++ b/src/shared/uid-range.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index fc8548c5b3..1715c0fb24 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -424,7 +425,7 @@ int utmp_wall( if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) continue; - /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */ + /* this access is fine, because STRLEN("/dev/") << 32 (UT_LINESIZE) */ if (path_startswith(u->ut_line, "/dev/")) path = u->ut_line; else { diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index 8f4fbcdeff..2c75d4097e 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/vlan-util.c b/src/shared/vlan-util.c index 1edd96fbe7..fa270164bb 100644 --- a/src/shared/vlan-util.c +++ b/src/shared/vlan-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/vlan-util.h b/src/shared/vlan-util.h index 365ed14d88..6d287fd32a 100644 --- a/src/shared/vlan-util.h +++ b/src/shared/vlan-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/volatile-util.c b/src/shared/volatile-util.c index e7e9721411..85512d00af 100644 --- a/src/shared/volatile-util.c +++ b/src/shared/volatile-util.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. diff --git a/src/shared/volatile-util.h b/src/shared/volatile-util.h index 17930ba6ae..3ad037af8b 100644 --- a/src/shared/volatile-util.h +++ b/src/shared/volatile-util.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index 4f3e0125f3..b0a422da84 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -26,10 +27,12 @@ #include "fd-util.h" #include "log.h" +#include "string-util.h" #include "time-util.h" #include "watchdog.h" static int watchdog_fd = -1; +static char *watchdog_device = NULL; static usec_t watchdog_timeout = USEC_INFINITY; static int update_timeout(void) { @@ -83,7 +86,8 @@ static int open_watchdog(void) { if (watchdog_fd >= 0) return 0; - watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); + watchdog_fd = open(watchdog_device ?: "/dev/watchdog", + O_WRONLY|O_CLOEXEC); if (watchdog_fd < 0) return -errno; @@ -95,6 +99,10 @@ static int open_watchdog(void) { return update_timeout(); } +int watchdog_set_device(char *path) { + return free_and_strdup(&watchdog_device, path); +} + int watchdog_set_timeout(usec_t *usec) { int r; diff --git a/src/shared/watchdog.h b/src/shared/watchdog.h index f6ec178ea1..5694338db3 100644 --- a/src/shared/watchdog.h +++ b/src/shared/watchdog.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once /*** @@ -24,6 +25,11 @@ #include "time-util.h" #include "util.h" +int watchdog_set_device(char *path); int watchdog_set_timeout(usec_t *usec); int watchdog_ping(void); void watchdog_close(bool disarm); + +static inline void watchdog_free_device(void) { + (void) watchdog_set_device(NULL); +} |