Features:
* add a new ExecStart= flag that inserts the configured user's shell as first
- word in the comand line. (maybe use character '.'). Usecase: tool such as
+ word in the command line. (maybe use character '.'). Usecase: tool such as
uid0 can use that to spawn the target user's default shell.
* varlink: figure out how to do docs for our varlink interfaces. Idea: install
'systemd-makefs',
'systemd-mkswap@.service'],
''],
- ['systemd-measure', '1', [], 'ENABLE_BOOTLOADER'],
+ ['systemd-measure', '1', [], 'HAVE_TPM2'],
['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
['systemd-mount', '1', ['systemd-umount'], ''],
['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
'systemd-pcrlock-make-policy.service',
'systemd-pcrlock-secureboot-authority.service',
'systemd-pcrlock-secureboot-policy.service'],
- 'ENABLE_BOOTLOADER'],
+ 'HAVE_TPM2'],
['systemd-pcrphase.service',
'8',
['systemd-pcrextend',
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd-measure" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='ENABLE_BOOTLOADER'>
+<refentry id="systemd-measure" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='HAVE_TPM2'>
<refentryinfo>
<title>systemd-measure</title>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd-pcrlock" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='ENABLE_BOOTLOADER'>
+<refentry id="systemd-pcrlock" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='HAVE_TPM2'>
<refentryinfo>
<title>systemd-pcrlock</title>
#include "escape.h"
#include "fileio.h"
#include "main-func.h"
-#include "mkdir.h"
#include "parse-util.h"
#include "percent-util.h"
#include "pretty-print.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
+#include "verbs.h"
#define PCI_CLASS_GRAPHICS_CARD 0x30000
return true;
}
-static int get_max_brightness(sd_device *device, unsigned *ret) {
+static int read_max_brightness(sd_device *device, unsigned *ret) {
+ unsigned max_brightness;
const char *s;
int r;
if (r < 0)
return log_device_warning_errno(device, r, "Failed to read 'max_brightness' attribute: %m");
- r = safe_atou(s, ret);
+ r = safe_atou(s, &max_brightness);
if (r < 0)
return log_device_warning_errno(device, r, "Failed to parse 'max_brightness' \"%s\": %m", s);
- return 0;
+ /* If max_brightness is 0, then there is no actual backlight device. This happens on desktops
+ * with Asus mainboards that load the eeepc-wmi module. */
+ if (max_brightness == 0) {
+ log_device_warning(device, "Maximum brightness is 0, ignoring device.");
+ *ret = 0;
+ return 0;
+ }
+
+ log_device_debug(device, "Maximum brightness is %u", max_brightness);
+
+ *ret = max_brightness;
+ return 1; /* valid max brightness */
}
static int clamp_brightness(
return 0;
}
-static int run(int argc, char *argv[]) {
- _cleanup_(sd_device_unrefp) sd_device *device = NULL;
- _cleanup_free_ char *escaped_ss = NULL, *escaped_sysname = NULL, *escaped_path_id = NULL;
- const char *sysname, *path_id, *ss, *saved;
- unsigned max_brightness, brightness;
+static int build_save_file_path(sd_device *device, char **ret) {
+ _cleanup_free_ char *escaped_subsystem = NULL, *escaped_sysname = NULL, *path = NULL;
+ const char *s;
int r;
- log_setup();
+ assert(device);
+ assert(ret);
- if (argv_looks_like_help(argc, argv))
- return help();
+ r = sd_device_get_subsystem(device, &s);
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to get subsystem: %m");
- if (argc != 3)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires two arguments.");
+ escaped_subsystem = cescape(s);
+ if (!escaped_subsystem)
+ return log_oom();
- if (!STR_IN_SET(argv[1], "load", "save"))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", argv[1]);
+ r = sd_device_get_sysname(device, &s);
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to get sysname: %m");
- umask(0022);
+ escaped_sysname = cescape(s);
+ if (!escaped_sysname)
+ return log_oom();
- r = mkdir_p("/var/lib/systemd/backlight", 0755);
- if (r < 0)
- return log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m");
+ if (sd_device_get_property_value(device, "ID_PATH", &s) >= 0) {
+ _cleanup_free_ char *escaped_path_id = cescape(s);
+ if (!escaped_path_id)
+ return log_oom();
- sysname = strchr(argv[2], ':');
- if (!sysname)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Requires a subsystem and sysname pair specifying a backlight device.");
+ path = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_subsystem, ":", escaped_sysname);
+ } else
+ path = strjoin("/var/lib/systemd/backlight/", escaped_subsystem, ":", escaped_sysname);
+ if (!path)
+ return log_oom();
- ss = strndupa_safe(argv[2], sysname - argv[2]);
+ *ret = TAKE_PTR(path);
+ return 0;
+}
- sysname++;
+static int read_saved_brightness(sd_device *device, unsigned *ret) {
+ _cleanup_free_ char *path = NULL, *value = NULL;
+ int r;
- if (!STR_IN_SET(ss, "backlight", "leds"))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a backlight or LED device: '%s:%s'", ss, sysname);
+ assert(device);
+ assert(ret);
- r = sd_device_new_from_subsystem_sysname(&device, ss, sysname);
+ r = build_save_file_path(device, &path);
+ if (r < 0)
+ return r;
+
+ r = read_one_line_file(path, &value);
if (r < 0) {
- bool ignore = r == -ENODEV;
+ if (r != -ENOENT)
+ log_device_error_errno(device, r, "Failed to read %s: %m", path);
+ return r;
+ }
- /* Some drivers, e.g. for AMD GPU, removes acpi backlight device soon after it is added.
- * See issue #21997. */
- log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
- "Failed to get backlight or LED device '%s:%s'%s: %m",
- ss, sysname, ignore ? ", ignoring" : "");
- return ignore ? 0 : r;
+ r = safe_atou(value, ret);
+ if (r < 0) {
+ log_device_warning_errno(device, r,
+ "Failed to parse saved brightness '%s', removing %s.",
+ value, path);
+ (void) unlink(path);
+ return r;
}
- /* If max_brightness is 0, then there is no actual backlight device. This happens on desktops
- * with Asus mainboards that load the eeepc-wmi module. */
- if (get_max_brightness(device, &max_brightness) < 0)
- return 0;
+ log_device_debug(device, "Using saved brightness %u.", *ret);
+ return 0;
+}
- if (max_brightness == 0) {
- log_device_warning(device, "Maximum brightness is 0, ignoring device.");
- return 0;
- }
+static int device_new_from_arg(const char *s, sd_device **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ _cleanup_free_ char *subsystem = NULL;
+ const char *sysname;
+ int r;
- log_device_debug(device, "Maximum brightness is %u", max_brightness);
+ assert(s);
+ assert(ret);
- escaped_ss = cescape(ss);
- if (!escaped_ss)
- return log_oom();
+ sysname = strchr(s, ':');
+ if (!sysname)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Requires a subsystem and sysname pair specifying a backlight or LED device.");
- escaped_sysname = cescape(sysname);
- if (!escaped_sysname)
+ subsystem = strndup(s, sysname - s);
+ if (!subsystem)
return log_oom();
- if (sd_device_get_property_value(device, "ID_PATH", &path_id) >= 0) {
- escaped_path_id = cescape(path_id);
- if (!escaped_path_id)
- return log_oom();
+ sysname++;
- saved = strjoina("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname);
- } else
- saved = strjoina("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname);
+ if (!STR_IN_SET(subsystem, "backlight", "leds"))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Not a backlight or LED device: '%s:%s'",
+ subsystem, sysname);
- /* If there are multiple conflicting backlight devices, then their probing at boot-time might
- * happen in any order. This means the validity checking of the device then is not reliable,
- * since it might not see other devices conflicting with a specific backlight. To deal with
- * this, we will actively delete backlight state files at shutdown (where device probing should
- * be complete), so that the validity check at boot time doesn't have to be reliable. */
+ r = sd_device_new_from_subsystem_sysname(&device, subsystem, sysname);
+ if (r == -ENODEV) {
+ /* Some drivers, e.g. for AMD GPU, removes acpi backlight device soon after it is added.
+ * See issue #21997. */
+ log_debug_errno(r, "Failed to get backlight or LED device '%s:%s', ignoring: %m", subsystem, sysname);
+ *ret = NULL;
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get backlight or LED device '%s:%s': %m", subsystem, sysname);
- if (streq(argv[1], "load")) {
- _cleanup_free_ char *value = NULL;
- unsigned percent;
- bool clamp;
+ *ret = TAKE_PTR(device);
+ return 1; /* Found. */
+}
- if (!shall_restore_state())
- return 0;
+static int verb_load(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ unsigned max_brightness, brightness, percent;
+ bool clamp;
+ int r;
- if (validate_device(device) == 0)
- return 0;
+ assert(argc == 2);
- clamp = shall_clamp(device, &percent);
+ if (!shall_restore_state())
+ return 0;
- r = read_one_line_file(saved, &value);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to read %s: %m", saved);
- if (r > 0) {
- r = safe_atou(value, &brightness);
- if (r < 0) {
- log_warning_errno(r, "Failed to parse saved brightness '%s', removing %s.",
- value, saved);
- (void) unlink(saved);
- } else {
- log_debug("Using saved brightness %u.", brightness);
- if (clamp)
- (void) clamp_brightness(device, percent, /* saved = */ true, max_brightness, &brightness);
-
- /* Do not fall back to read current brightness below. */
- r = 1;
- }
- }
- if (r <= 0) {
- /* Fallback to clamping current brightness or exit early if clamping is not
- * supported/enabled. */
- if (!clamp)
- return 0;
+ r = device_new_from_arg(argv[1], &device);
+ if (r <= 0)
+ return r;
- r = read_brightness(device, max_brightness, &brightness);
- if (r < 0)
- return log_device_error_errno(device, r, "Failed to read current brightness: %m");
+ r = read_max_brightness(device, &max_brightness);
+ if (r <= 0)
+ return r;
- (void) clamp_brightness(device, percent, /* saved = */ false, max_brightness, &brightness);
- }
+ /* Ignore any errors in validation, and use the device as is. */
+ if (validate_device(device) == 0)
+ return 0;
- r = sd_device_set_sysattr_valuef(device, "brightness", "%u", brightness);
- if (r < 0)
- return log_device_error_errno(device, r, "Failed to write system 'brightness' attribute: %m");
+ clamp = shall_clamp(device, &percent);
- } else if (streq(argv[1], "save")) {
- if (validate_device(device) == 0) {
- (void) unlink(saved);
+ r = read_saved_brightness(device, &brightness);
+ if (r < 0) {
+ /* Fallback to clamping current brightness or exit early if clamping is not
+ * supported/enabled. */
+ if (!clamp)
return 0;
- }
r = read_brightness(device, max_brightness, &brightness);
if (r < 0)
return log_device_error_errno(device, r, "Failed to read current brightness: %m");
- r = write_string_filef(saved, WRITE_STRING_FILE_CREATE, "%u", brightness);
- if (r < 0)
- return log_device_error_errno(device, r, "Failed to write %s: %m", saved);
+ (void) clamp_brightness(device, percent, /* saved = */ false, max_brightness, &brightness);
+ } else if (clamp)
+ (void) clamp_brightness(device, percent, /* saved = */ true, max_brightness, &brightness);
- } else
- assert_not_reached();
+ r = sd_device_set_sysattr_valuef(device, "brightness", "%u", brightness);
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to write system 'brightness' attribute: %m");
return 0;
}
+static int verb_save(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ _cleanup_free_ char *path = NULL;
+ unsigned max_brightness, brightness;
+ int r;
+
+ assert(argc == 2);
+
+ r = device_new_from_arg(argv[1], &device);
+ if (r <= 0)
+ return r;
+
+ r = read_max_brightness(device, &max_brightness);
+ if (r <= 0)
+ return r;
+
+ r = build_save_file_path(device, &path);
+ if (r < 0)
+ return r;
+
+ /* If there are multiple conflicting backlight devices, then their probing at boot-time might
+ * happen in any order. This means the validity checking of the device then is not reliable,
+ * since it might not see other devices conflicting with a specific backlight. To deal with
+ * this, we will actively delete backlight state files at shutdown (where device probing should
+ * be complete), so that the validity check at boot time doesn't have to be reliable. */
+ if (validate_device(device) == 0) {
+ (void) unlink(path);
+ return 0;
+ }
+
+ r = read_brightness(device, max_brightness, &brightness);
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to read current brightness: %m");
+
+ r = write_string_filef(path, WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_MKDIR_0755, "%u", brightness);
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to write %s: %m", path);
+
+ return 0;
+}
+
+static int run(int argc, char *argv[]) {
+ static const Verb verbs[] = {
+ { "load", 2, 2, VERB_ONLINE_ONLY, verb_load },
+ { "save", 2, 2, VERB_ONLINE_ONLY, verb_save },
+ {}
+ };
+
+ log_setup();
+
+ if (argv_looks_like_help(argc, argv))
+ return help();
+
+ umask(0022);
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
DEFINE_MAIN_FUNCTION(run);
#include "stdio-util.h"
#include "string-util.h"
#include "sync-util.h"
+#include "terminal-util.h"
#include "tmpfile-util.h"
/* The maximum size of the file we'll read in one go in read_full_file() (64M). */
* and don't call isatty() on an invalid fd */
flags |= READ_LINE_NOT_A_TTY;
else
- flags |= isatty(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
+ flags |= isatty_safe(fd) ? READ_LINE_IS_A_TTY : READ_LINE_NOT_A_TTY;
}
if (FLAGS_SET(flags, READ_LINE_IS_A_TTY))
break;
return false;
if (console_fd_is_tty < 0)
- console_fd_is_tty = isatty(console_fd) > 0;
+ console_fd_is_tty = isatty_safe(console_fd);
return console_fd_is_tty;
}
static volatile int cached_color_mode = _COLOR_INVALID;
static volatile int cached_underline_enabled = -1;
+bool isatty_safe(int fd) {
+ assert(fd >= 0);
+
+ if (isatty(fd))
+ return true;
+
+ /* Be resilient if we're working on stdio, since they're set up by parent process. */
+ assert(errno != EBADF || IN_SET(fd, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO));
+
+ return false;
+}
+
int chvt(int vt) {
_cleanup_close_ int fd = -EBADF;
assert(fd >= 0);
- if (isatty(fd) < 1)
+ if (!isatty_safe(fd))
return log_debug_errno(errno, "Asked to reset a terminal that actually isn't a terminal: %m");
/* We leave locked terminal attributes untouched, so that Plymouth may set whatever it wants to set,
c++;
}
- if (isatty(fd) < 1)
+ if (!isatty_safe(fd))
return negative_errno();
return TAKE_FD(fd);
}
int vt_restore(int fd) {
+
static const struct vt_mode mode = {
.mode = VT_AUTO,
};
- int r, q = 0;
- if (isatty(fd) < 1)
+ int r, ret = 0;
+
+ assert(fd >= 0);
+
+ if (!isatty_safe(fd))
return log_debug_errno(errno, "Asked to restore the VT for an fd that does not refer to a terminal: %m");
if (ioctl(fd, KDSETMODE, KD_TEXT) < 0)
- q = log_debug_errno(errno, "Failed to set VT in text mode, ignoring: %m");
+ RET_GATHER(ret, log_debug_errno(errno, "Failed to set VT to text mode, ignoring: %m"));
r = vt_reset_keyboard(fd);
- if (r < 0) {
- log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m");
- if (q >= 0)
- q = r;
- }
+ if (r < 0)
+ RET_GATHER(ret, log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m"));
- if (ioctl(fd, VT_SETMODE, &mode) < 0) {
- log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m");
- if (q >= 0)
- q = -errno;
- }
+ if (ioctl(fd, VT_SETMODE, &mode) < 0)
+ RET_GATHER(ret, log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m"));
r = fchmod_and_chown(fd, TTY_MODE, 0, GID_INVALID);
- if (r < 0) {
- log_debug_errno(r, "Failed to chmod()/chown() VT, ignoring: %m");
- if (q >= 0)
- q = r;
- }
+ if (r < 0)
+ RET_GATHER(ret, log_debug_errno(r, "Failed to chmod()/chown() VT, ignoring: %m"));
- return q;
+ return ret;
}
int vt_release(int fd, bool restore) {
* sent by the kernel and optionally reset the VT in text and auto
* VT-switching modes. */
- if (isatty(fd) < 1)
+ if (!isatty_safe(fd))
return log_debug_errno(errno, "Asked to release the VT for an fd that does not refer to a terminal: %m");
if (ioctl(fd, VT_RELDISP, 1) < 0)
if (!colors_enabled())
return -EOPNOTSUPP;
- if (isatty(STDOUT_FILENO) < 1 || isatty(STDIN_FILENO) < 1)
+ if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))
return -EOPNOTSUPP;
if (streq_ptr(getenv("TERM"), "linux")) {
/* Set cursor to top left corner and clear screen */
#define ANSI_HOME_CLEAR "\x1B[H\x1B[2J"
+bool isatty_safe(int fd);
+
int reset_terminal_fd(int fd, bool switch_to_text);
int reset_terminal(const char *name);
int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column);
"busctl (systemd) " STRINGIFY(PROJECT_VERSION) " (Git " GIT_VERSION ")";
int r;
- if (isatty(fileno(stdout)) > 0)
+ if (isatty(STDOUT_FILENO))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Refusing to write message data to console, please redirect output to a file.");
assert(fd >= 0);
/* Before we chown/chmod the TTY, let's ensure this is actually a tty */
- if (isatty(fd) < 1) {
- if (IN_SET(errno, EINVAL, ENOTTY))
- return 0; /* not a tty */
-
- return -errno;
- }
+ if (!isatty_safe(fd))
+ return 0;
/* This might fail. What matters are the results. */
r = fchmod_and_chown(fd, TTY_MODE, uid, GID_INVALID);
const char *path = exec_context_tty_path(context);
- if (p && p->stdin_fd >= 0 && isatty(p->stdin_fd))
+ if (p && p->stdin_fd >= 0 && isatty_safe(p->stdin_fd))
fd = p->stdin_fd;
else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
/* Don't bother unless this is a tty */
fd = fileno(f);
- if (fd >= 0 && isatty(fd) <= 0)
+ if (fd >= 0 && !isatty_safe(fd))
return 0;
if (fputc('\n', f) != '\n')
if (fd < 0)
return -errno;
- errno = 0;
- if (isatty(fd) <= 0)
- return errno_or_else(EIO);
+ if (!isatty_safe(fd))
+ return -errno;
return 0;
}
#include "main-func.h"
#include "process-util.h"
#include "sigbus.h"
+#include "terminal-util.h"
static int run(int argc, char *argv[]) {
_cleanup_(server_freep) Server *s = NULL;
* daemon when it comes to logging hence LOG_TARGET_AUTO won't do the right thing for
* us. Hence explicitly log to the console if we're started from a console or to kmsg
* otherwise. */
- log_target = isatty(STDERR_FILENO) > 0 ? LOG_TARGET_CONSOLE : LOG_TARGET_KMSG;
+ log_target = isatty(STDERR_FILENO) ? LOG_TARGET_CONSOLE : LOG_TARGET_KMSG;
log_set_prohibit_ipc(true); /* better safe than sorry */
log_set_target(log_target);
#include "terminal-util.h"
#include "udev-util.h"
#include "unit-def.h"
+#include "varlink.h"
#include "verbs.h"
#include "wifi-util.h"
STATIC_DESTRUCTOR_REGISTER(arg_drop_in, freep);
-static int check_netns_match(sd_bus *bus) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+static int check_netns_match(void) {
struct stat st;
uint64_t id;
+ JsonVariant *reply = NULL;
+ _cleanup_(varlink_unrefp) Varlink *vl = NULL;
int r;
- assert(bus);
+ r = varlink_connect_address(&vl, "/run/systemd/netif/io.systemd.Network");
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to network service /run/systemd/netif/io.systemd.Network: %m");
+
+ r = varlink_call(vl, "io.systemd.Network.GetNamespaceId", NULL, &reply, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue GetNamespaceId() varlink call: %m");
+
+ static const JsonDispatch dispatch_table[] = {
+ { "NamespaceId", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, 0, JSON_MANDATORY },
+ {},
+ };
+
+ r = json_dispatch(reply, dispatch_table, JSON_LOG, &id);
+ if (r < 0)
+ return r;
- r = bus_get_property_trivial(bus, bus_network_mgr, "NamespaceId", &error, 't', &id);
- if (r < 0) {
- log_debug_errno(r, "Failed to query network namespace of networkd, ignoring: %s", bus_error_message(&error, r));
- return 0;
- }
if (id == 0) {
log_debug("systemd-networkd.service not running in a network namespace (?), skipping netns check.");
return 0;
return log_error_errno(r, "Failed to connect to system bus: %m");
if (networkd_is_running()) {
- r = check_netns_match(bus);
+ r = check_netns_match();
if (r < 0)
return r;
} else
m->routes = set_free(m->routes);
m->nexthops_by_id = hashmap_free(m->nexthops_by_id);
+ m->nexthop_ids = set_free(m->nexthop_ids);
sd_event_source_unref(m->speed_meter_event_source);
sd_event_unref(m->event);
if (r < 0)
return r;
- return manager_build_dhcp_pd_subnet_ids(m);
+ r = manager_build_dhcp_pd_subnet_ids(m);
+ if (r < 0)
+ return r;
+
+ r = manager_build_nexthop_ids(m);
+ if (r < 0)
+ return r;
+
+ return 0;
}
int manager_enumerate_internal(
/* Manage nexthops by id. */
Hashmap *nexthops_by_id;
+ Set *nexthop_ids; /* requested IDs in .network files */
/* Manager stores routes without RTA_OIF attribute. */
unsigned route_remove_messages;
if (r < 0)
return r; /* network_drop_invalid_addresses() logs internally. */
network_drop_invalid_routes(network);
- network_drop_invalid_nexthops(network);
+ r = network_drop_invalid_nexthops(network);
+ if (r < 0)
+ return r;
network_drop_invalid_bridge_fdb_entries(network);
network_drop_invalid_bridge_mdb_entries(network);
r = network_drop_invalid_neighbors(network);
ordered_hashmap_free_with_destructor(manager->networks, network_unref);
manager->networks = new_networks;
- return manager_build_dhcp_pd_subnet_ids(manager);
+ r = manager_build_dhcp_pd_subnet_ids(manager);
+ if (r < 0)
+ return r;
+
+ r = manager_build_nexthop_ids(manager);
+ if (r < 0)
+ return r;
+
+ return 0;
failure:
ordered_hashmap_free_with_destructor(new_networks, network_unref);
set_free_free(network->ipv6_proxy_ndp_addresses);
ordered_hashmap_free_with_destructor(network->addresses_by_section, address_free);
hashmap_free_with_destructor(network->routes_by_section, route_free);
- hashmap_free_with_destructor(network->nexthops_by_section, nexthop_free);
+ ordered_hashmap_free_with_destructor(network->nexthops_by_section, nexthop_free);
hashmap_free_with_destructor(network->bridge_fdb_entries_by_section, bridge_fdb_free);
hashmap_free_with_destructor(network->bridge_mdb_entries_by_section, bridge_mdb_free);
ordered_hashmap_free_with_destructor(network->neighbors_by_section, neighbor_free);
OrderedHashmap *addresses_by_section;
Hashmap *routes_by_section;
- Hashmap *nexthops_by_section;
+ OrderedHashmap *nexthops_by_section;
Hashmap *bridge_fdb_entries_by_section;
Hashmap *bridge_mdb_entries_by_section;
OrderedHashmap *neighbors_by_section;
if (nexthop->network) {
assert(nexthop->section);
- hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
+ ordered_hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
}
config_section_free(nexthop->section);
if (r < 0)
return r;
- nexthop = hashmap_get(network->nexthops_by_section, n);
+ nexthop = ordered_hashmap_get(network->nexthops_by_section, n);
if (nexthop) {
*ret = TAKE_PTR(nexthop);
return 0;
nexthop->section = TAKE_PTR(n);
nexthop->source = NETWORK_CONFIG_SOURCE_STATIC;
- r = hashmap_ensure_put(&network->nexthops_by_section, &config_section_hash_ops, nexthop->section, nexthop);
+ r = ordered_hashmap_ensure_put(&network->nexthops_by_section, &config_section_hash_ops, nexthop->section, nexthop);
if (r < 0)
return r;
if (in->id > 0)
return nexthop_get_by_id(link->manager, in->id, ret);
+ /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
+ * nexthop_section_verify(). */
+ assert(link->manager->manage_foreign_nexthops);
+
ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
if (nexthop_compare_full(nexthop, in) != 0)
continue;
+ /* Even if the configuration matches, it may be configured with another [NextHop] section
+ * that has an explicit ID. If so, the assigned nexthop is not the one we are looking for. */
+ if (set_contains(link->manager->nexthop_ids, UINT32_TO_PTR(nexthop->id)))
+ continue;
+
if (ret)
*ret = nexthop;
return 0;
if (in->id > 0)
return nexthop_get_request_by_id(link->manager, in->id, ret);
+ /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
+ * nexthop_section_verify(). */
+ assert(link->manager->manage_foreign_nexthops);
+
ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
ORDERED_SET_FOREACH(req, link->manager->request_queue) {
if (nexthop_compare_full(nexthop, in) != 0)
continue;
+ /* Even if the configuration matches, it may be requested by another [NextHop] section
+ * that has an explicit ID. If so, the request is not the one we are looking for. */
+ if (set_contains(link->manager->nexthop_ids, UINT32_TO_PTR(nexthop->id)))
+ continue;
+
if (ret)
*ret = req;
return 0;
}
static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) {
- _cleanup_set_free_ Set *ids = NULL;
- Network *network;
- int r;
-
assert(manager);
assert(nexthop);
/* Find the lowest unused ID. */
- ORDERED_HASHMAP_FOREACH(network, manager->networks) {
- NextHop *tmp;
-
- HASHMAP_FOREACH(tmp, network->nexthops_by_section) {
- if (tmp->id == 0)
- continue;
-
- r = set_ensure_put(&ids, NULL, UINT32_TO_PTR(tmp->id));
- if (r < 0)
- return r;
- }
- }
-
for (uint32_t id = 1; id < UINT32_MAX; id++) {
if (nexthop_get_by_id(manager, id, NULL) >= 0)
continue;
if (nexthop_get_request_by_id(manager, id, NULL) >= 0)
continue;
- if (set_contains(ids, UINT32_TO_PTR(id)))
+ if (set_contains(manager->nexthop_ids, UINT32_TO_PTR(id)))
continue;
nexthop->id = id;
Request *req;
int r;
- manager = ASSERT_PTR(ASSERT_PTR(nexthop)->manager);
+ assert(nexthop);
+ assert(nexthop->id > 0);
+
+ manager = ASSERT_PTR(nexthop->manager);
/* link may be NULL. */
(void) link_get_by_index(manager, nexthop->ifindex, &link);
- if (nexthop->id == 0) {
- log_link_debug(link, "Cannot remove nexthop without valid ID, ignoring.");
- return 0;
- }
-
log_nexthop_debug(nexthop, "Removing", manager);
r = sd_rtnl_message_new_nexthop(manager->rtnl, &m, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC);
assert(link);
assert(nexthop);
+ assert(nexthop->id > 0);
if (!link_is_ready_to_configure(link, false))
return false;
return false;
}
- if (nexthop->id == 0) {
- Request *req;
-
- ORDERED_SET_FOREACH(req, link->manager->request_queue) {
- if (req->type != REQUEST_TYPE_NEXTHOP)
- continue;
- if (((NextHop*) req->userdata)->id != 0)
- return false; /* first configure nexthop with id. */
- }
- }
-
return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw);
}
link->static_nexthops_configured = false;
- HASHMAP_FOREACH(nh, link->network->nexthops_by_section) {
+ ORDERED_HASHMAP_FOREACH(nh, link->network->nexthops_by_section) {
if (only_ipv4 && nh->family != AF_INET)
continue;
if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
continue;
- HASHMAP_FOREACH(nexthop, other->network->nexthops_by_section) {
+ ORDERED_HASHMAP_FOREACH(nexthop, other->network->nexthops_by_section) {
NextHop *existing;
if (nexthop_get(other, nexthop, &existing) < 0)
"Ignoring [NextHop] section from line %u.",
nh->section->filename, nh->section->line);
- if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw))
+ if (nh->blackhole)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: nexthop group cannot be a blackhole. "
"Ignoring [NextHop] section from line %u.",
nh->section->filename, nh->section->line);
+
+ if (nh->onlink > 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: nexthop group cannot have on-link flag. "
+ "Ignoring [NextHop] section from line %u.",
+ nh->section->filename, nh->section->line);
} else if (nh->family == AF_UNSPEC)
/* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */
nh->family = AF_INET;
- if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw))
- return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
- "%s: blackhole nexthop cannot have gateway address. "
- "Ignoring [NextHop] section from line %u.",
- nh->section->filename, nh->section->line);
+ if (nh->blackhole) {
+ if (in_addr_is_set(nh->family, &nh->gw))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: blackhole nexthop cannot have gateway address. "
+ "Ignoring [NextHop] section from line %u.",
+ nh->section->filename, nh->section->line);
+
+ if (nh->onlink > 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: blackhole nexthop cannot have on-link flag. "
+ "Ignoring [NextHop] section from line %u.",
+ nh->section->filename, nh->section->line);
+ }
if (nh->onlink < 0 && in_addr_is_set(nh->family, &nh->gw) &&
ordered_hashmap_isempty(nh->network->addresses_by_section)) {
return 0;
}
-void network_drop_invalid_nexthops(Network *network) {
+int network_drop_invalid_nexthops(Network *network) {
+ _cleanup_hashmap_free_ Hashmap *nexthops = NULL;
NextHop *nh;
+ int r;
assert(network);
- HASHMAP_FOREACH(nh, network->nexthops_by_section)
- if (nexthop_section_verify(nh) < 0)
+ ORDERED_HASHMAP_FOREACH(nh, network->nexthops_by_section) {
+ if (nexthop_section_verify(nh) < 0) {
nexthop_free(nh);
+ continue;
+ }
+
+ if (nh->id == 0)
+ continue;
+
+ /* Always use the setting specified later. So, remove the previously assigned setting. */
+ NextHop *dup = hashmap_remove(nexthops, UINT32_TO_PTR(nh->id));
+ if (dup) {
+ log_warning("%s: Duplicated nexthop settings for ID %"PRIu32" is specified at line %u and %u, "
+ "dropping the nexthop setting specified at line %u.",
+ dup->section->filename,
+ nh->id, nh->section->line,
+ dup->section->line, dup->section->line);
+ /* nexthop_free() will drop the nexthop from nexthops_by_section. */
+ nexthop_free(dup);
+ }
+
+ r = hashmap_ensure_put(&nexthops, NULL, UINT32_TO_PTR(nh->id), nh);
+ if (r < 0)
+ return log_oom();
+ assert(r > 0);
+ }
+
+ return 0;
+}
+
+int manager_build_nexthop_ids(Manager *manager) {
+ Network *network;
+ int r;
+
+ assert(manager);
+
+ if (!manager->manage_foreign_nexthops)
+ return 0;
+
+ manager->nexthop_ids = set_free(manager->nexthop_ids);
+
+ ORDERED_HASHMAP_FOREACH(network, manager->networks) {
+ NextHop *nh;
+
+ ORDERED_HASHMAP_FOREACH(nh, network->nexthops_by_section) {
+ if (nh->id == 0)
+ continue;
+
+ r = set_ensure_put(&manager->nexthop_ids, NULL, UINT32_TO_PTR(nh->id));
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
}
int config_parse_nexthop_id(
NextHop *nexthop_free(NextHop *nexthop);
-void network_drop_invalid_nexthops(Network *network);
+int network_drop_invalid_nexthops(Network *network);
int link_drop_nexthops(Link *link, bool foreign);
static inline int link_drop_foreign_nexthops(Link *link) {
int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret);
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
+int manager_build_nexthop_ids(Manager *manager);
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(NextHop, nexthop);
else if (streq(arg, "passive"))
arg_console_mode = CONSOLE_PASSIVE;
else if (streq(arg, "pipe")) {
- if (isatty(STDIN_FILENO) > 0 && isatty(STDOUT_FILENO) > 0)
+ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
log_full(arg_quiet ? LOG_DEBUG : LOG_NOTICE,
"Console mode 'pipe' selected, but standard input/output are connected to an interactive TTY. "
"Most likely you want to use 'interactive' console mode for proper interactivity and shell job control. "
arg_console_mode = CONSOLE_PIPE;
} else if (streq(arg, "autopipe")) {
- if (isatty(STDIN_FILENO) > 0 && isatty(STDOUT_FILENO) > 0)
+ if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
arg_console_mode = CONSOLE_INTERACTIVE;
else
arg_console_mode = CONSOLE_PIPE;
goto finish;
if (arg_console_mode < 0)
- arg_console_mode =
- isatty(STDIN_FILENO) > 0 &&
- isatty(STDOUT_FILENO) > 0 ? CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
+ arg_console_mode = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) ?
+ CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY;
if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */
arg_quiet = true;
fd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return -errno;
- if (!isatty(fd))
- return -ENOTTY;
+
+ if (!isatty_safe(fd))
+ return -errno;
return loop_write_full(fd, message, SIZE_MAX, TIMEOUT_USEC);
}
c->display_refresh_scheduled = false;
- if (isatty(STDERR_FILENO) > 0)
+ if (isatty(STDERR_FILENO))
fputs(ANSI_HOME_CLEAR, stderr);
/* If we have both IPv4 and IPv6, we display IPv4 info via Plymouth, since it doesn't have much
if (r < 0)
return log_error_errno(r, "Failed to subscribe to RTM_DELADDR events: %m");
- if (isatty(0) > 0)
+ if (isatty(STDIN_FILENO))
log_info("Hit Ctrl-C to exit target mode.");
_unused_ _cleanup_(notify_on_cleanup) const char *notify_message =
test(m, "exec-privatedevices-yes.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
- if (access("/dev/kvm", F_OK) >= 0)
+ if (access("/dev/kmsg", F_OK) >= 0)
test(m, "exec-privatedevices-bind.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-privatedevices-disabled-by-prefix.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
udev_watch_restore(manager->inotify_fd);
- /* block and listen to all signals on signalfd */
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, SIGRTMIN+18, -1) >= 0);
+ /* block SIGCHLD for listening child events. */
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
r = sd_event_default(&manager->event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
- r = sd_event_add_signal(manager->event, NULL, SIGINT, on_sigterm, manager);
+ r = sd_event_add_signal(manager->event, NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, on_sigterm, manager);
if (r < 0)
return log_error_errno(r, "Failed to create SIGINT event source: %m");
- r = sd_event_add_signal(manager->event, NULL, SIGTERM, on_sigterm, manager);
+ r = sd_event_add_signal(manager->event, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_sigterm, manager);
if (r < 0)
return log_error_errno(r, "Failed to create SIGTERM event source: %m");
- r = sd_event_add_signal(manager->event, NULL, SIGHUP, on_sighup, manager);
+ r = sd_event_add_signal(manager->event, NULL, SIGHUP | SD_EVENT_SIGNAL_PROCMASK, on_sighup, manager);
if (r < 0)
return log_error_errno(r, "Failed to create SIGHUP event source: %m");
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r) || (r == -EHOSTDOWN) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to allocate memory pressure watch, ignoring: %m");
- r = sd_event_add_signal(manager->event, &manager->memory_pressure_event_source, SIGRTMIN+18, sigrtmin18_handler, NULL);
+ r = sd_event_add_signal(manager->event, &manager->memory_pressure_event_source,
+ (SIGRTMIN+18) | SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate SIGRTMIN+18 event source, ignoring: %m");
DEVICE_TRACE_POINT(worker_spawned, dev, getpid_cached());
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, -1) >= 0);
-
/* Reset OOM score, we only protect the main daemon. */
r = set_oom_score_adjust(0);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
- r = sd_event_add_signal(worker->event, NULL, SIGTERM, NULL, NULL);
+ r = sd_event_add_signal(worker->event, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to set SIGTERM event: %m");