make sure to update the package in question to provide proper, native systemd
unit files. Contact vendor if necessary. Compatibility support for System V
services is deprecated and will be removed soon.
+
+-- 187c62eb1e7f463bb530394f52cb090f
+Subject: A Portable Service has been attached
+Defined-By: systemd
+Support: %SUPPORT_URL%
+Documentation: https://systemd.io/PORTABLE_SERVICES/
+
+A new Portable Service @PORTABLE_ROOT@ (with extensions: @PORTABLE_EXTENSION@) has
+been attached to the system and is now available for use. The list of attached
+Portable Services can be queried with 'portablectl list'.
+
+-- 76c5c754d628490d8ecba4c9d042112b
+Subject: A Portable Service has been detached
+Defined-By: systemd
+Support: %SUPPORT_URL%
+Documentation: https://systemd.io/PORTABLE_SERVICES/
+
+A Portable Service @PORTABLE_ROOT@ (with extensions: @PORTABLE_EXTENSION@) has been
+detached from the system and is no longer available for use. The list of attached
+Portable Services can be queried with 'portablectl list'.
<para>The <function>AttachImageWithExtensions()</function>,
<function>DetachImageWithExtensions()</function> and
<function>ReattachImageWithExtensions()</function> methods take in options as flags instead of
- booleans to allow for extendability. <varname>SD_SYSTEMD_PORTABLE_FORCE_ATTACH</varname> will cause
- safety checks that ensure the units are not running while the new image is attached or detached
- to be skipped. <varname>SD_SYSTEMD_PORTABLE_FORCE_EXTENSION</varname> will cause the check that the
+ booleans to allow for extendability. <varname>SD_SYSTEMD_PORTABLE_FORCE_ATTACH</varname> will bypass
+ the safety checks that ensure the units are not running while the image is attached or detached.
+ <varname>SD_SYSTEMD_PORTABLE_FORCE_EXTENSION</varname> will bypass the check that ensures the
<filename>extension-release.<replaceable>NAME</replaceable></filename> file in the extension image
- matches the image name to be skipped. They are defined as follows:</para>
+ matches the image name. They are defined as follows:</para>
<programlisting>
#define SD_SYSTEMD_PORTABLE_RUNTIME (UINT64_C(1) << 0)
#define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path)
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
- _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, netnsfd = -EBADF, usernsfd = -EBADF;
- int rfd = -EBADF;
+int namespace_open(
+ pid_t pid,
+ int *ret_pidns_fd,
+ int *ret_mntns_fd,
+ int *ret_netns_fd,
+ int *ret_userns_fd,
+ int *ret_root_fd) {
+
+ _cleanup_close_ int pidns_fd = -EBADF, mntns_fd = -EBADF, netns_fd = -EBADF,
+ userns_fd = -EBADF, root_fd = -EBADF;
assert(pid >= 0);
- if (mntns_fd) {
- const char *mntns;
+ if (ret_pidns_fd) {
+ const char *pidns;
- mntns = pid_namespace_path(pid, NAMESPACE_MOUNT);
- mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (mntnsfd < 0)
+ pidns = pid_namespace_path(pid, NAMESPACE_PID);
+ pidns_fd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (pidns_fd < 0)
return -errno;
}
- if (pidns_fd) {
- const char *pidns;
+ if (ret_mntns_fd) {
+ const char *mntns;
- pidns = pid_namespace_path(pid, NAMESPACE_PID);
- pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (pidnsfd < 0)
+ mntns = pid_namespace_path(pid, NAMESPACE_MOUNT);
+ mntns_fd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (mntns_fd < 0)
return -errno;
}
- if (netns_fd) {
+ if (ret_netns_fd) {
const char *netns;
netns = pid_namespace_path(pid, NAMESPACE_NET);
- netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (netnsfd < 0)
+ netns_fd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (netns_fd < 0)
return -errno;
}
- if (userns_fd) {
+ if (ret_userns_fd) {
const char *userns;
userns = pid_namespace_path(pid, NAMESPACE_USER);
- usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (usernsfd < 0 && errno != ENOENT)
+ userns_fd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (userns_fd < 0 && errno != ENOENT)
return -errno;
}
- if (root_fd) {
+ if (ret_root_fd) {
const char *root;
root = procfs_file_alloca(pid, "root");
- rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
- if (rfd < 0)
+ root_fd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+ if (root_fd < 0)
return -errno;
}
- if (pidns_fd)
- *pidns_fd = TAKE_FD(pidnsfd);
+ if (ret_pidns_fd)
+ *ret_pidns_fd = TAKE_FD(pidns_fd);
- if (mntns_fd)
- *mntns_fd = TAKE_FD(mntnsfd);
+ if (ret_mntns_fd)
+ *ret_mntns_fd = TAKE_FD(mntns_fd);
- if (netns_fd)
- *netns_fd = TAKE_FD(netnsfd);
+ if (ret_netns_fd)
+ *ret_netns_fd = TAKE_FD(netns_fd);
- if (userns_fd)
- *userns_fd = TAKE_FD(usernsfd);
+ if (ret_userns_fd)
+ *ret_userns_fd = TAKE_FD(userns_fd);
- if (root_fd)
- *root_fd = TAKE_FD(rfd);
+ if (ret_root_fd)
+ *ret_root_fd = TAKE_FD(root_fd);
return 0;
}
r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to fork process (sd-mkuserns): %m");
if (r == 0)
/* Child. We do nothing here, just freeze until somebody kills us. */
freeze();
xsprintf(path, "/proc/" PID_FMT "/uid_map", pid);
r = write_string_file(path, uid_map, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return log_error_errno(r, "Failed to write UID map: %m");
+ return log_debug_errno(r, "Failed to write UID map: %m");
xsprintf(path, "/proc/" PID_FMT "/gid_map", pid);
r = write_string_file(path, gid_map, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
- return log_error_errno(r, "Failed to write GID map: %m");
-
- r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL);
+ return log_debug_errno(r, "Failed to write GID map: %m");
+
+ r = namespace_open(pid,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ /* ret_netns_fd = */ NULL,
+ &userns_fd,
+ /* ret_root_fd = */ NULL);
if (r < 0)
- return log_error_errno(r, "Failed to open userns fd: %m");
+ return log_debug_errno(r, "Failed to open userns fd: %m");
return TAKE_FD(userns_fd);
+}
+
+int netns_acquire(void) {
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ _cleanup_close_ int netns_fd = -EBADF;
+ int r;
+
+ /* Forks off a process in a new network namespace, acquires a network namespace fd, and then kills
+ * the process again. This way we have a netns fd that is not bound to any process. */
+
+ r = safe_fork("(sd-mknetns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_NETNS, &pid);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to fork process (sd-mknetns): %m");
+ if (r == 0)
+ /* Child. We do nothing here, just freeze until somebody kills us. */
+ freeze();
+
+ r = namespace_open(pid,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ &netns_fd,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open netns fd: %m");
+ return TAKE_FD(netns_fd);
}
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
unsigned int clone_flag;
} namespace_info[_NAMESPACE_TYPE_MAX + 1];
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd);
+int namespace_open(
+ pid_t pid,
+ int *ret_pidns_fd,
+ int *ret_mntns_fd,
+ int *ret_netns_fd,
+ int *ret_userns_fd,
+ int *ret_root_fd);
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
int fd_is_ns(int fd, unsigned long nsflag);
}
int userns_acquire(const char *uid_map, const char *gid_map);
+int netns_acquire(void);
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
}
}
- if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0)
+ if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS|FORK_NEW_NETNS)) != 0)
pid = raw_clone(SIGCHLD|
(FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) |
- (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0));
+ (FLAGS_SET(flags, FORK_NEW_USERNS) ? CLONE_NEWUSER : 0) |
+ (FLAGS_SET(flags, FORK_NEW_NETNS) ? CLONE_NEWNET : 0));
else
pid = fork();
if (pid < 0)
pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata);
-/* 💣 Note that FORK_NEW_USERNS + FORK_NEW_MOUNTNS should not be called in threaded programs, because they
- * cause us to use raw_clone() which does not synchronize the glibc malloc() locks, and thus will cause
- * deadlocks if the parent uses threads and the child does memory allocations. Hence: if the parent is
- * threaded these flags may not be used. These flags cannot be used if the parent uses threads or the child
- * uses malloc(). 💣 */
+/* 💣 Note that FORK_NEW_USERNS, FORK_NEW_MOUNTNS, or FORK_NEW_NETNS should not be called in threaded
+ * programs, because they cause us to use raw_clone() which does not synchronize the glibc malloc() locks,
+ * and thus will cause deadlocks if the parent uses threads and the child does memory allocations. Hence: if
+ * the parent is threaded these flags may not be used. These flags cannot be used if the parent uses threads
+ * or the child uses malloc(). 💣 */
typedef enum ForkFlags {
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */
FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */
FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */
+ FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
} ForkFlags;
int safe_fork_full(
assert(pid > 0);
- r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return r;
pid_t child;
int r;
- r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return r;
assert(state);
err = BS->LocateProtocol(MAKE_GUID_PTR(EFI_DT_FIXUP_PROTOCOL), NULL, (void **) &fixup);
+ /* Skip fixup if we cannot locate device tree fixup protocol */
if (err != EFI_SUCCESS)
- return log_error_status(EFI_SUCCESS, "Could not locate device tree fixup protocol, skipping.");
+ return EFI_SUCCESS;
size = devicetree_allocated(state);
err = fixup->Fixup(fixup, PHYSICAL_ADDRESS_TO_POINTER(state->addr), &size,
return r;
}
-static int simple_varlink_call(const char *option, const char *method) {
- _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
- const char *fn;
+static int varlink_connect_journal(Varlink **ret_link) {
+ const char *address;
int r;
- if (arg_machine)
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "%s is not supported in conjunction with --machine=.", option);
-
- fn = arg_namespace ?
- strjoina("/run/systemd/journal.", arg_namespace, "/io.systemd.journal") :
- "/run/systemd/journal/io.systemd.journal";
+ address = arg_namespace ?
+ strjoina("/run/systemd/journal.", arg_namespace, "/io.systemd.journal") :
+ "/run/systemd/journal/io.systemd.journal";
- r = varlink_connect_address(&link, fn);
+ r = varlink_connect_address(ret_link, address);
if (r < 0)
- return log_error_errno(r, "Failed to connect to %s: %m", fn);
+ return r;
- (void) varlink_set_description(link, "journal");
- (void) varlink_set_relative_timeout(link, USEC_INFINITY);
+ (void) varlink_set_description(*ret_link, "journal");
+ (void) varlink_set_relative_timeout(*ret_link, USEC_INFINITY);
- return varlink_call_and_log(link, method, /* parameters= */ NULL, /* ret_parameters= */ NULL);
+ return 0;
}
static int flush_to_var(void) {
+ _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
+ int r;
+
+ if (arg_machine || arg_namespace)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "--flush is not supported in conjunction with %s.",
+ arg_machine ? "--machine=" : "--namespace=");
+
if (access("/run/systemd/journal/flushed", F_OK) >= 0)
return 0; /* Already flushed, no need to contact journald */
if (errno != ENOENT)
return log_error_errno(errno, "Unable to check for existence of /run/systemd/journal/flushed: %m");
- return simple_varlink_call("--flush", "io.systemd.Journal.FlushToVar");
+ r = varlink_connect_journal(&link);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to Varlink socket: %m");
+
+ return varlink_call_and_log(link, "io.systemd.Journal.FlushToVar", /* parameters= */ NULL, /* ret_parameters= */ NULL);
}
static int relinquish_var(void) {
- return simple_varlink_call("--relinquish-var/--smart-relinquish-var", "io.systemd.Journal.RelinquishVar");
+ _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
+ int r;
+
+ if (arg_machine || arg_namespace)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "--(smart-)relinquish-var is not supported in conjunction with %s.",
+ arg_machine ? "--machine=" : "--namespace=");
+
+ r = varlink_connect_journal(&link);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to Varlink socket: %m");
+
+ return varlink_call_and_log(link, "io.systemd.Journal.RelinquishVar", /* parameters= */ NULL, /* ret_parameters= */ NULL);
}
static int rotate(void) {
- return simple_varlink_call("--rotate", "io.systemd.Journal.Rotate");
+ _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
+ int r;
+
+ if (arg_machine)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "--rotate is not supported in conjunction with --machine=.");
+
+ r = varlink_connect_journal(&link);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to Varlink socket: %m");
+
+ return varlink_call_and_log(link, "io.systemd.Journal.Rotate", /* parameters= */ NULL, /* ret_parameters= */ NULL);
}
static int sync_journal(void) {
- return simple_varlink_call("--sync", "io.systemd.Journal.Synchronize");
+ _cleanup_(varlink_flush_close_unrefp) Varlink *link = NULL;
+ int r;
+
+ if (arg_machine)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "--sync is not supported in conjunction with --machine=.");
+
+ r = varlink_connect_journal(&link);
+ if (ERRNO_IS_NEG_DISCONNECT(r) && arg_namespace)
+ /* If the namespaced sd-journald instance was shut down due to inactivity, it should already
+ * be synchronized */
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to Varlink socket: %m");
+
+ return varlink_call_and_log(link, "io.systemd.Journal.Synchronize", /* parameters= */ NULL, /* ret_parameters= */ NULL);
}
static int action_list_fields(sd_journal *j) {
log_debug("sd-bus: connecting bus%s%s to namespace of PID "PID_FMT"...",
b->description ? " " : "", strempty(b->description), b->nspid);
- r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+ r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return log_debug_errno(r, "Failed to open namespace of PID "PID_FMT": %m", b->nspid);
int device_get_property_int(sd_device *device, const char *key, int *ret);
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
+int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value);
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *ret);
return v > 0;
}
+int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value) {
+ const char *value;
+ int r;
+
+ r = sd_device_get_sysattr_value(device, sysattr, &value);
+ if (r < 0)
+ return r;
+
+ uint32_t v;
+ r = safe_atou32(value, &v);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
+
+ if (ret_value)
+ *ret_value = v;
+ /* We return "true" if the value is positive. */
+ return v > 0;
+}
+
int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
const char *value;
int r;
[NL80211_ATTR_SSID] = BUILD_POLICY_WITH_SIZE(BINARY, IEEE80211_MAX_SSID_LEN),
[NL80211_ATTR_STATUS_CODE] = BUILD_POLICY(U16),
[NL80211_ATTR_4ADDR] = BUILD_POLICY(U8),
+ [NL80211_ATTR_NETNS_FD] = BUILD_POLICY(U32),
};
/***************** genl wireguard type systems *****************/
assert(name);
/* Assign the requested name. */
+
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
if (r < 0)
return r;
return sd_netlink_call(*rtnl, message, 0, NULL);
}
+int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
+ int r, ifindex;
+
+ assert(orig_name);
+ assert(new_name);
+
+ /* This does not check alternative names. Callers must check the requested name is not used as an
+ * alternative name. */
+
+ if (streq(orig_name, new_name))
+ return 0;
+
+ if (!ifname_valid(new_name))
+ return -EINVAL;
+
+ if (!rtnl)
+ rtnl = &our_rtnl;
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
+ ifindex = rtnl_resolve_ifname(rtnl, orig_name);
+ if (ifindex < 0)
+ return ifindex;
+
+ return set_link_name(rtnl, ifindex, new_name);
+}
+
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const *alternative_names) {
_cleanup_strv_free_ char **original_altnames = NULL, **new_altnames = NULL;
bool altname_deleted = false;
int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret);
+int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name);
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const* alternative_names);
static inline int rtnl_append_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) {
return rtnl_set_link_name(rtnl, ifindex, NULL, alternative_names);
_cleanup_free_ char *resolved = NULL;
assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved) == ifindex);
assert_se(streq_ptr(resolved, "test-shortname"));
+ resolved = mfree(resolved);
+
+ assert_se(rtnl_rename_link(&rtnl, "test-shortname", "test-shortname") >= 0);
+ assert_se(rtnl_rename_link(&rtnl, "test-shortname", "test-shortname2") >= 0);
+ assert_se(rtnl_rename_link(NULL, "test-shortname2", "test-shortname3") >= 0);
+
+ assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved) == ifindex);
+ assert_se(streq_ptr(resolved, "test-shortname3"));
+ resolved = mfree(resolved);
+
+ assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-shortname3", &resolved) == ifindex);
+ assert_se(streq_ptr(resolved, "test-shortname3"));
+ resolved = mfree(resolved);
}
DEFINE_TEST_MAIN(LOG_DEBUG);
assert(m);
assert(ret);
- if (SEAT_IS_SELF(name)) /* the caller's own session */
+ if (SESSION_IS_SELF(name)) /* the caller's own session */
return get_sender_session(m, message, false, error, ret);
- if (SEAT_IS_AUTO(name)) /* The caller's own session if they have one, otherwise their user's display session */
+ if (SESSION_IS_AUTO(name)) /* The caller's own session if they have one, otherwise their user's display session */
return get_sender_session(m, message, true, error, ret);
session = hashmap_get(m->sessions, name);
return 0;
}
- LIST_FOREACH(sessions_by_user, s, u->sessions) {
- int k;
-
- k = session_stop(s, force);
- if (k < 0)
- r = k;
- }
+ LIST_FOREACH(sessions_by_user, s, u->sessions)
+ RET_GATHER(r, session_stop(s, force));
user_stop_service(u, force);
}
int user_finalize(User *u) {
- int r = 0, k;
+ int r = 0;
assert(u);
if (u->started)
log_debug("User %s logged out.", u->user_record->user_name);
- LIST_FOREACH(sessions_by_user, s, u->sessions) {
- k = session_finalize(s);
- if (k < 0)
- r = k;
- }
+ LIST_FOREACH(sessions_by_user, s, u->sessions)
+ RET_GATHER(r, session_finalize(s));
/* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
* are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
* cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
* a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
* and do it only for normal users. */
- if (u->manager->remove_ipc && !uid_is_system(u->user_record->uid)) {
- k = clean_ipc_by_uid(u->user_record->uid);
- if (k < 0)
- r = k;
- }
+ if (u->manager->remove_ipc && !uid_is_system(u->user_record->uid))
+ RET_GATHER(r, clean_ipc_by_uid(u->user_record->uid));
(void) unlink(u->state_file);
user_add_to_gc_queue(u);
const char *runtime_max_sec;
} SessionContext;
-static int create_session_message(sd_bus *bus, pam_handle_t *handle, const SessionContext *context, bool avoid_pidfd, sd_bus_message **ret) {
+static int create_session_message(
+ sd_bus *bus,
+ pam_handle_t *handle,
+ const SessionContext *context,
+ bool avoid_pidfd,
+ sd_bus_message **ret) {
+
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
- int r, pidfd = -EBADFD;
+ _cleanup_close_ int pidfd = -EBADF;
+ int r;
assert(bus);
assert(handle);
assert(context);
+ assert(ret);
if (!avoid_pidfd) {
pidfd = pidfd_open(getpid_cached(), 0);
r = create_session_message(bus,
handle,
&context,
- false /* avoid_pidfd = */,
+ /* avoid_pidfd = */ false,
&m);
if (r < 0)
return pam_bus_log_create_error(handle, r);
r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+ if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+ sd_bus_error_free(&error);
+ pam_debug_syslog(handle, debug,
+ "CreateSessionWithPIDFD() API is not available, retrying with CreateSession().");
+
+ m = sd_bus_message_unref(m);
+ r = create_session_message(bus,
+ handle,
+ &context,
+ /* avoid_pidfd = */ true,
+ &m);
+ if (r < 0)
+ return pam_bus_log_create_error(handle, r);
+
+ r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
+ }
if (r < 0) {
if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
+ /* We are already in a session, don't do anything */
pam_debug_syslog(handle, debug,
"Not creating session: %s", bus_error_message(&error, r));
- /* We are already in a session, don't do anything */
goto success;
- } else if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
- pam_debug_syslog(handle, debug,
- "CreateSessionWithPIDFD() API is not available, retrying with CreateSession().");
-
- m = sd_bus_message_unref(m);
- r = create_session_message(bus,
- handle,
- &context,
- true /* avoid_pidfd = */,
- &m);
- if (r < 0)
- return pam_bus_log_create_error(handle, r);
-
- sd_bus_error_free(&error);
- r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
- }
- if (r < 0) {
- pam_syslog(handle, LOG_ERR,
- "Failed to create session: %s", bus_error_message(&error, r));
- return PAM_SESSION_ERR;
}
+
+ pam_syslog(handle, LOG_ERR,
+ "Failed to create session: %s", bus_error_message(&error, r));
+ return PAM_SESSION_ERR;
}
r = sd_bus_message_read(reply,
if (streq(us, them))
return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
- r = namespace_open(m->leader.pid, NULL, NULL, &netns_fd, NULL, NULL);
+ r = namespace_open(m->leader.pid,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ &netns_fd,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
if (r < 0)
return r;
_cleanup_fclose_ FILE *f = NULL;
pid_t child;
- r = namespace_open(m->leader.pid, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
+ r = namespace_open(m->leader.pid,
+ &pidns_fd,
+ &mntns_fd,
+ /* ret_netns_fd = */ NULL,
+ /* ret_userns_fd = */ NULL,
+ &root_fd);
if (r < 0)
return r;
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
pid_t child;
- r = namespace_open(m->leader.pid, NULL, &mntns_fd, NULL, NULL, &root_fd);
+ r = namespace_open(m->leader.pid,
+ /* ret_pidns_fd = */ NULL,
+ &mntns_fd,
+ /* ret_netns_fd = */ NULL,
+ /* ret_userns_fd = */ NULL,
+ &root_fd);
if (r < 0)
return r;
else
route_unmark(existing);
- r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp_pd_messages,
- dhcp_pd_route_handler, NULL);
+ r = link_request_route(link, route, &link->dhcp_pd_messages, dhcp_pd_route_handler);
if (r < 0)
return log_link_error_errno(link, r, "Failed to request DHCP-PD prefix route: %m");
else
route_unmark(existing);
- r = link_request_route(link, TAKE_PTR(route), true, counter, callback, NULL);
+ r = link_request_route(link, route, counter, callback);
if (r < 0)
return log_link_error_errno(link, r, "Failed to request unreachable route for DHCP delegated prefix %s: %m",
IN6_ADDR_PREFIX_TO_STRING(addr, prefixlen));
else
route_unmark(existing);
- r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp_pd_messages,
- dhcp_pd_route_handler, NULL);
+ r = link_request_route(link, route, &link->dhcp_pd_messages, dhcp_pd_route_handler);
if (r < 0)
return log_link_debug_errno(link, r, "Failed to request default gateway for DHCP delegated prefix: %m");
return 1;
}
-static int dhcp4_request_route(Route *in, Link *link) {
- _cleanup_(route_freep) Route *route = in;
+static int dhcp4_request_route(Route *route, Link *link) {
struct in_addr server;
Route *existing;
int r;
else
route_unmark(existing);
- return link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
- dhcp4_route_handler, NULL);
+ return link_request_route(link, route, &link->dhcp4_messages, dhcp4_route_handler);
}
static bool link_prefixroute(Link *link) {
if (r < 0)
return r;
- return dhcp4_request_route(TAKE_PTR(route), link);
+ return dhcp4_request_route(route, link);
}
static int dhcp4_request_route_to_gateway(Link *link, const struct in_addr *gw) {
route->prefsrc.in = address;
route->scope = RT_SCOPE_LINK;
- return dhcp4_request_route(TAKE_PTR(route), link);
+ return dhcp4_request_route(route, link);
}
static int dhcp4_request_route_auto(
- Route *in,
+ Route *route,
Link *link,
const struct in_addr *gw) {
- _cleanup_(route_freep) Route *route = in;
struct in_addr address;
int r;
route->prefsrc.in = address;
}
- return dhcp4_request_route(TAKE_PTR(route), link);
+ return dhcp4_request_route(route, link);
}
static int dhcp4_request_classless_static_or_static_routes(Link *link) {
if (r < 0)
return r;
- r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw);
+ r = dhcp4_request_route_auto(route, link, &gw);
if (r < 0)
return r;
}
route->nexthop.gw.in = router;
route->prefsrc.in = address;
- return dhcp4_request_route(TAKE_PTR(route), link);
+ return dhcp4_request_route(route, link);
}
static int dhcp4_request_semi_static_routes(Link *link) {
route->nexthop.gw.in = gw;
- r = dhcp4_request_route(TAKE_PTR(route), link);
+ r = dhcp4_request_route(route, link);
if (r < 0)
return r;
}
route->dst.in = *dst;
route->dst_prefixlen = 32;
- r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw);
+ r = dhcp4_request_route_auto(route, link, &gw);
if (r < 0)
return r;
}
}
}
-static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
- _cleanup_(route_freep) Route *route = in;
+static int ndisc_request_route(Route *route, Link *link, sd_ndisc_router *rt) {
struct in6_addr router;
uint8_t hop_limit = 0;
uint32_t mtu = 0;
is_new = route_get(NULL, link, route, NULL) < 0;
- r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages,
- ndisc_route_handler, NULL);
+ r = link_request_route(link, route, &link->ndisc_messages, ndisc_route_handler);
if (r < 0)
return r;
if (r > 0 && is_new)
route->nexthop.gw.in6 = gateway;
route->lifetime_usec = lifetime_usec;
- r = ndisc_request_route(TAKE_PTR(route), link, rt);
+ r = ndisc_request_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request default route: %m");
}
route->pref = preference;
route->lifetime_usec = lifetime_usec;
- r = ndisc_request_route(TAKE_PTR(route), link, rt);
+ r = ndisc_request_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request gateway: %m");
}
route->pref = preference;
route->lifetime_usec = lifetime_usec;
- r = ndisc_request_route(TAKE_PTR(route), link, rt);
+ r = ndisc_request_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request prefix route: %m");
route->dst_prefixlen = prefixlen;
route->lifetime_usec = lifetime_usec;
- r = ndisc_request_route(TAKE_PTR(route), link, rt);
+ r = ndisc_request_route(route, link, rt);
if (r < 0)
return log_link_warning_errno(link, r, "Could not request additional route: %m");
siphash24_compress_typesafe(req->type, state);
- if (req->type != REQUEST_TYPE_NEXTHOP) {
+ if (!IN_SET(req->type, REQUEST_TYPE_NEXTHOP, REQUEST_TYPE_ROUTE)) {
siphash24_compress_boolean(req->link, state);
if (req->link)
siphash24_compress_typesafe(req->link->ifindex, state);
if (r != 0)
return r;
- if (a->type != REQUEST_TYPE_NEXTHOP) {
+ if (!IN_SET(a->type, REQUEST_TYPE_NEXTHOP, REQUEST_TYPE_ROUTE)) {
r = CMP(!!a->link, !!b->link);
if (r != 0)
return r;
ordered_set_free(route->nexthops);
}
-static void route_nexthop_hash_func_full(const RouteNextHop *nh, struct siphash *state, bool hash_all_parameters) {
+static void route_nexthop_hash_func_full(const RouteNextHop *nh, struct siphash *state, bool with_weight) {
assert(nh);
assert(state);
return;
in_addr_hash_func(&nh->gw, nh->family, state);
- if (!hash_all_parameters)
- return;
- siphash24_compress_typesafe(nh->weight, state);
+ if (with_weight)
+ siphash24_compress_typesafe(nh->weight, state);
siphash24_compress_typesafe(nh->ifindex, state);
if (nh->ifindex == 0)
siphash24_compress_string(nh->ifname, state); /* For Network or Request object. */
}
-static int route_nexthop_compare_func_full(const RouteNextHop *a, const RouteNextHop *b, bool hash_all_parameters) {
+static int route_nexthop_compare_func_full(const RouteNextHop *a, const RouteNextHop *b, bool with_weight) {
int r;
assert(a);
if (r != 0)
return r;
- if (!hash_all_parameters)
- return 0;
-
- r = CMP(a->weight, b->weight);
- if (r != 0)
- return r;
+ if (with_weight) {
+ r = CMP(a->weight, b->weight);
+ if (r != 0)
+ return r;
+ }
r = CMP(a->ifindex, b->ifindex);
if (r != 0)
}
static void route_nexthop_hash_func(const RouteNextHop *nh, struct siphash *state) {
- route_nexthop_hash_func_full(nh, state, /* hash_all_parameters = */ true);
+ route_nexthop_hash_func_full(nh, state, /* with_weight = */ true);
}
static int route_nexthop_compare_func(const RouteNextHop *a, const RouteNextHop *b) {
- return route_nexthop_compare_func_full(a, b, /* hash_all_parameters = */ true);
+ return route_nexthop_compare_func_full(a, b, /* with_weight = */ true);
}
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
route_nexthop_free);
static size_t route_n_nexthops(const Route *route) {
- assert(route);
-
if (route->nexthop_id != 0 || route_type_is_reject(route))
return 0;
return;
case 1:
- route_nexthop_hash_func_full(&route->nexthop, state, /* hash_all_parameters = */ false);
+ route_nexthop_hash_func_full(&route->nexthop, state, /* with_weight = */ false);
return;
default: {
return CMP(a->nexthop_id, b->nexthop_id);
case 1:
- return route_nexthop_compare_func_full(&a->nexthop, &b->nexthop, /* hash_all_parameters = */ false);
+ return route_nexthop_compare_func_full(&a->nexthop, &b->nexthop, /* with_weight = */ false);
default: {
RouteNextHop *nh;
/* unset pointer copied in the above. */
dest->ifname = NULL;
- return strdup_or_null(src->ifindex == 0 ? NULL : src->ifname, &dest->ifname);
+ return strdup_or_null(src->ifindex > 0 ? NULL : src->ifname, &dest->ifname);
+}
+
+static int route_nexthop_dup(const RouteNextHop *src, RouteNextHop **ret) {
+ _cleanup_(route_nexthop_freep) RouteNextHop *dest = NULL;
+ int r;
+
+ assert(src);
+ assert(ret);
+
+ dest = new(RouteNextHop, 1);
+ if (!dest)
+ return -ENOMEM;
+
+ r = route_nexthop_copy(src, dest);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(dest);
+ return 0;
}
int route_nexthops_copy(const Route *src, const RouteNextHop *nh, Route *dest) {
+ int r;
+
assert(src);
assert(dest);
if (ordered_set_isempty(src->nexthops))
return route_nexthop_copy(&src->nexthop, &dest->nexthop);
- /* Currently, this does not copy multipath routes. */
+ ORDERED_SET_FOREACH(nh, src->nexthops) {
+ _cleanup_(route_nexthop_freep) RouteNextHop *nh_dup = NULL;
+
+ r = route_nexthop_dup(nh, &nh_dup);
+ if (r < 0)
+ return r;
+
+ r = ordered_set_ensure_put(&dest->nexthops, &route_nexthop_hash_ops, nh_dup);
+ if (r < 0)
+ return r;
+ assert(r > 0);
+
+ TAKE_PTR(nh_dup);
+ }
return 0;
}
return true; /* updated */
}
-int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret) {
+int route_nexthop_get_link(Manager *manager, const RouteNextHop *nh, Link **ret) {
assert(manager);
assert(nh);
if (nh->ifname)
return link_get_by_name(manager, nh->ifname, ret);
- if (link) {
- if (ret)
- *ret = link;
- return 0;
- }
-
return -ENOENT;
}
-static bool route_nexthop_is_ready_to_configure(const RouteNextHop *nh, Link *link, bool onlink) {
+static bool route_nexthop_is_ready_to_configure(const RouteNextHop *nh, Manager *manager, bool onlink) {
+ Link *link;
+
assert(nh);
- assert(link);
+ assert(manager);
- if (route_nexthop_get_link(link->manager, link, nh, &link))
+ if (route_nexthop_get_link(manager, nh, &link) < 0)
return false;
if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ true))
return gateway_is_ready(link, onlink, nh->family, &nh->gw);
}
-int route_nexthops_is_ready_to_configure(const Route *route, Link *link) {
+int route_nexthops_is_ready_to_configure(const Route *route, Manager *manager) {
int r;
assert(route);
- assert(link);
-
- Manager *manager = ASSERT_PTR(link->manager);
+ assert(manager);
if (route->nexthop_id != 0) {
struct nexthop_grp *nhg;
return true;
if (ordered_set_isempty(route->nexthops))
- return route_nexthop_is_ready_to_configure(&route->nexthop, link, FLAGS_SET(route->flags, RTNH_F_ONLINK));
+ return route_nexthop_is_ready_to_configure(&route->nexthop, manager, FLAGS_SET(route->flags, RTNH_F_ONLINK));
RouteNextHop *nh;
ORDERED_SET_FOREACH(nh, route->nexthops)
- if (!route_nexthop_is_ready_to_configure(nh, link, FLAGS_SET(route->flags, RTNH_F_ONLINK)))
+ if (!route_nexthop_is_ready_to_configure(nh, manager, FLAGS_SET(route->flags, RTNH_F_ONLINK)))
return false;
return true;
return 0;
}
-static int append_nexthop_one(Link *link, const Route *route, const RouteNextHop *nh, struct rtattr **rta, size_t offset) {
+static int append_nexthop_one(const Route *route, const RouteNextHop *nh, struct rtattr **rta, size_t offset) {
struct rtnexthop *rtnh;
struct rtattr *new_rta;
int r;
assert(rta);
assert(*rta);
- if (nh->ifindex <= 0) {
- assert(link);
- assert(link->manager);
-
- r = route_nexthop_get_link(link->manager, link, nh, &link);
- if (r < 0)
- return r;
- }
-
new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
if (!new_rta)
return -ENOMEM;
rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
*rtnh = (struct rtnexthop) {
.rtnh_len = sizeof(*rtnh),
- .rtnh_ifindex = nh->ifindex > 0 ? nh->ifindex : link->ifindex,
+ .rtnh_ifindex = nh->ifindex,
.rtnh_hops = nh->weight,
};
return r;
}
-static int netlink_message_append_multipath_route(Link *link, const Route *route, sd_netlink_message *message) {
+static int netlink_message_append_multipath_route(const Route *route, sd_netlink_message *message) {
_cleanup_free_ struct rtattr *rta = NULL;
size_t offset;
int r;
offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
if (ordered_set_isempty(route->nexthops)) {
- r = append_nexthop_one(link, route, &route->nexthop, &rta, offset);
+ r = append_nexthop_one(route, &route->nexthop, &rta, offset);
if (r < 0)
return r;
ORDERED_SET_FOREACH(nh, route->nexthops) {
struct rtnexthop *rtnh;
- r = append_nexthop_one(link, route, nh, &rta, offset);
+ r = append_nexthop_one(route, nh, &rta, offset);
if (r < 0)
return r;
return sd_netlink_message_append_data(message, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
}
-int route_nexthops_set_netlink_message(Link *link, const Route *route, sd_netlink_message *message) {
+int route_nexthops_set_netlink_message(const Route *route, sd_netlink_message *message) {
int r;
assert(route);
if (route_type_is_reject(route))
return 0;
- /* We request IPv6 multipath routes separatedly. Even though, if weight is non-zero, we need to use
+ /* We request IPv6 multipath routes separately. Even though, if weight is non-zero, we need to use
* RTA_MULTIPATH, as we have no way to specify the weight of the nexthop. */
if (ordered_set_isempty(route->nexthops) && route->nexthop.weight == 0) {
return r;
}
- return sd_netlink_message_append_u32(message, RTA_OIF, route->nexthop.ifindex > 0 ? route->nexthop.ifindex : ASSERT_PTR(link)->ifindex);
+ assert(route->nexthop.ifindex > 0);
+ return sd_netlink_message_append_u32(message, RTA_OIF, route->nexthop.ifindex);
}
- return netlink_message_append_multipath_route(link, route, message);
+ return netlink_message_append_multipath_route(route, message);
}
static int route_parse_nexthops(Route *route, const struct rtnexthop *rtnh, size_t size) {
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
+ if (ordered_set_size(route->nexthops) == 1) {
+ _cleanup_(route_nexthop_freep) RouteNextHop *nh = ordered_set_steal_first(route->nexthops);
+
+ route_nexthop_done(&route->nexthop);
+ route->nexthop = TAKE_STRUCT(*nh);
+
+ assert(ordered_set_isempty(route->nexthops));
+ route->nexthops = ordered_set_free(route->nexthops);
+ }
+
return 0;
}
bool route_nexthops_needs_adjust(const Route *route);
int route_adjust_nexthops(Route *route, Link *link);
-int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret);
-int route_nexthops_is_ready_to_configure(const Route *route, Link *link);
+int route_nexthop_get_link(Manager *manager, const RouteNextHop *nh, Link **ret);
+int route_nexthops_is_ready_to_configure(const Route *route, Manager *manager);
int route_nexthops_to_string(const Route *route, char **ret);
-int route_nexthops_set_netlink_message(Link *link, const Route *route, sd_netlink_message *message);
+int route_nexthops_set_netlink_message(const Route *route, sd_netlink_message *message);
int route_nexthops_read_netlink_message(Route *route, sd_netlink_message *message);
int route_section_verify_nexthops(Route *route);
return 0;
}
-static int route_add(Manager *manager, Link *link, Route *route) {
+static int route_add(Manager *manager, Route *route) {
int r;
+ assert(manager);
assert(route);
+ assert(!route->network);
+ assert(!route->wireguard);
- if (route_type_is_reject(route)) {
- assert(manager);
-
- r = set_ensure_put(&manager->routes, &route_hash_ops, route);
- if (r < 0)
- return r;
- if (r == 0)
- return -EEXIST;
-
- route->manager = manager;
- } else {
- assert(link);
-
+ Link *link;
+ if (route_nexthop_get_link(manager, &route->nexthop, &link) >= 0) {
r = set_ensure_put(&link->routes, &route_hash_ops, route);
if (r < 0)
return r;
return -EEXIST;
route->link = link;
+ return 0;
}
+ r = set_ensure_put(&manager->routes, &route_hash_ops, route);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
+
+ route->manager = manager;
return 0;
}
-int route_get(Manager *manager, Link *link, const Route *in, Route **ret) {
- Route *route;
-
- assert(in);
+int route_get(Manager *manager, Link *link, const Route *route, Route **ret) {
+ Route *existing;
- if (route_type_is_reject(in)) {
- if (!manager)
- return -ENOENT;
-
- route = set_get(manager->routes, in);
- } else {
- if (!link)
- return -ENOENT;
+ if (!manager)
+ manager = ASSERT_PTR(ASSERT_PTR(link)->manager);
+ assert(route);
- route = set_get(link->routes, in);
- }
- if (!route)
+ if (route_nexthop_get_link(manager, &route->nexthop, &link) >= 0)
+ existing = set_get(link->routes, route);
+ else
+ existing = set_get(manager->routes, route);
+ if (!existing)
return -ENOENT;
if (ret)
- *ret = route;
+ *ret = existing;
return 0;
}
return link_get_by_index(manager, nh->ifindex, ret);
}
- return route_nexthop_get_link(manager, NULL, &route->nexthop, ret);
+ return route_nexthop_get_link(manager, &route->nexthop, ret);
}
-static int route_get_request(Link *link, const Route *route, Request **ret) {
+static int route_get_request(Manager *manager, const Route *route, Request **ret) {
Request *req;
- assert(link);
- assert(link->manager);
+ assert(manager);
assert(route);
- req = ordered_set_get(link->manager->request_queue,
+ req = ordered_set_get(manager->request_queue,
&(const Request) {
- .link = link,
.type = REQUEST_TYPE_ROUTE,
.userdata = (void*) route,
.hash_func = (hash_func_t) route_hash_func,
return 0;
}
-static void route_apply_nexthop(Route *route, const NextHop *nh, uint8_t nh_weight) {
- assert(route);
- assert(nh);
- assert(hashmap_isempty(nh->group));
-
- route->nexthop.family = nh->family;
- route->nexthop.gw = nh->gw;
-
- if (nh_weight != UINT8_MAX)
- route->nexthop.weight = nh_weight;
-
- if (nh->blackhole)
- route->type = RTN_BLACKHOLE;
-}
-
-static void route_apply_route_nexthop(Route *route, const RouteNextHop *nh) {
- assert(route);
- assert(nh);
-
- route->nexthop.family = nh->family;
- route->nexthop.gw = nh->gw;
- route->nexthop.weight = nh->weight;
-}
-
-typedef struct ConvertedRoutes {
- size_t n;
- Route **routes;
- Link **links;
-} ConvertedRoutes;
-
-static ConvertedRoutes *converted_routes_free(ConvertedRoutes *c) {
- if (!c)
- return NULL;
-
- for (size_t i = 0; i < c->n; i++)
- route_free(c->routes[i]);
-
- free(c->routes);
- free(c->links);
-
- return mfree(c);
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(ConvertedRoutes*, converted_routes_free);
-
-static int converted_routes_new(size_t n, ConvertedRoutes **ret) {
- _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
- _cleanup_free_ Route **routes = NULL;
- _cleanup_free_ Link **links = NULL;
-
- assert(n > 0);
- assert(ret);
-
- routes = new0(Route*, n);
- if (!routes)
- return -ENOMEM;
-
- links = new0(Link*, n);
- if (!links)
- return -ENOMEM;
-
- c = new(ConvertedRoutes, 1);
- if (!c)
- return -ENOMEM;
-
- *c = (ConvertedRoutes) {
- .n = n,
- .routes = TAKE_PTR(routes),
- .links = TAKE_PTR(links),
- };
-
- *ret = TAKE_PTR(c);
- return 0;
-}
-
-static bool route_needs_convert(const Route *route) {
- assert(route);
-
- return route->nexthop_id > 0 || !ordered_set_isempty(route->nexthops);
-}
-
-static int route_convert(Manager *manager, Link *link, const Route *route, ConvertedRoutes **ret) {
- _cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
- int r;
-
- assert(manager);
- assert(route);
- assert(ret);
-
- /* link may be NULL */
-
- if (!route_needs_convert(route)) {
- *ret = NULL;
- return 0;
- }
-
- if (route->nexthop_id > 0) {
- struct nexthop_grp *nhg;
- NextHop *nh;
-
- r = nexthop_get_by_id(manager, route->nexthop_id, &nh);
- if (r < 0)
- return r;
-
- if (hashmap_isempty(nh->group)) {
- r = converted_routes_new(1, &c);
- if (r < 0)
- return r;
-
- r = route_dup(route, NULL, &c->routes[0]);
- if (r < 0)
- return r;
-
- route_apply_nexthop(c->routes[0], nh, UINT8_MAX);
- (void) link_get_by_index(manager, nh->ifindex, c->links);
-
- *ret = TAKE_PTR(c);
- return 1;
- }
-
- r = converted_routes_new(hashmap_size(nh->group), &c);
- if (r < 0)
- return r;
-
- size_t i = 0;
- HASHMAP_FOREACH(nhg, nh->group) {
- NextHop *h;
-
- r = nexthop_get_by_id(manager, nhg->id, &h);
- if (r < 0)
- return r;
-
- r = route_dup(route, NULL, &c->routes[i]);
- if (r < 0)
- return r;
-
- route_apply_nexthop(c->routes[i], h, nhg->weight);
- (void) link_get_by_index(manager, h->ifindex, c->links + i);
-
- i++;
- }
-
- *ret = TAKE_PTR(c);
- return 1;
-
- }
-
- assert(!ordered_set_isempty(route->nexthops));
-
- r = converted_routes_new(ordered_set_size(route->nexthops), &c);
- if (r < 0)
- return r;
-
- size_t i = 0;
- RouteNextHop *nh;
- ORDERED_SET_FOREACH(nh, route->nexthops) {
- r = route_dup(route, NULL, &c->routes[i]);
- if (r < 0)
- return r;
-
- route_apply_route_nexthop(c->routes[i], nh);
-
- r = route_nexthop_get_link(manager, link, nh, &c->links[i]);
- if (r < 0)
- return r;
-
- i++;
- }
-
- *ret = TAKE_PTR(c);
- return 1;
-}
-
void link_mark_routes(Link *link, NetworkConfigSource source) {
Route *route;
strna(proto), strna(scope), strna(route_type_to_string(route->type)), strna(flags));
}
-static int route_set_netlink_message(const Route *route, sd_netlink_message *m, Link *link) {
+static int route_set_netlink_message(const Route *route, sd_netlink_message *m) {
int r;
assert(route);
assert(m);
- /* link may be NULL */
-
/* rtmsg header (and relevant attributes) */
if (route->dst_prefixlen > 0) {
r = netlink_message_append_in_addr_union(m, RTA_DST, route->family, &route->dst);
return r;
/* nexthops */
- r = route_nexthops_set_netlink_message(link, route, m);
+ r = route_nexthops_set_netlink_message(route, m);
if (r < 0)
return r;
int route_remove(Route *route) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- unsigned char type;
Manager *manager;
Link *link;
int r;
assert(route);
assert(route->manager || (route->link && route->link->manager));
- assert(IN_SET(route->family, AF_INET, AF_INET6));
link = route->link;
manager = route->manager ?: link->manager;
if (r < 0)
return log_link_warning_errno(link, r, "Could not create netlink message: %m");
- r = route_set_netlink_message(route, m, link);
+ r = route_set_netlink_message(route, m);
if (r < 0)
return log_link_warning_errno(link, r, "Could not fill netlink message: %m");
- if (route->family == AF_INET && route->nexthop_id > 0 && route->type == RTN_BLACKHOLE)
- /* When IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel
- * sends RTM_NEWROUTE netlink message with blackhole type, kernel's internal route type
- * fib_rt_info::type may not be blackhole. Thus, we cannot know the internal value.
- * Moreover, on route removal, the matching is done with the hidden value if we set
- * non-zero type in RTM_DELROUTE message. Note, sd_rtnl_message_new_route() sets
- * RTN_UNICAST by default. So, we need to clear the type here. */
- type = RTN_UNSPEC;
- else
- type = route->type;
-
- r = sd_rtnl_message_route_set_type(m, type);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set route type: %m");
-
r = netlink_call_async(manager->rtnl, NULL, m, route_remove_handler,
link ? link_netlink_destroy_callback : NULL, link);
if (r < 0)
return 0;
}
+static int link_unmark_route(Link *link, const Route *route, const RouteNextHop *nh) {
+ _cleanup_(route_freep) Route *tmp = NULL;
+ Route *existing;
+ int r;
+
+ assert(link);
+ assert(route);
+
+ r = route_dup(route, nh, &tmp);
+ if (r < 0)
+ return r;
+
+ r = route_adjust_nexthops(tmp, link);
+ if (r < 0)
+ return r;
+
+ if (route_get(link->manager, link, tmp, &existing) < 0)
+ return 0;
+
+ route_unmark(existing);
+ return 1;
+}
+
static void manager_mark_routes(Manager *manager, bool foreign, const Link *except) {
Route *route;
Link *link;
- int r;
assert(manager);
continue;
HASHMAP_FOREACH(route, link->network->routes_by_section) {
- _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
- Route *existing;
-
- r = route_convert(manager, link, route, &converted);
- if (r < 0)
- continue;
- if (r == 0) {
- if (route_get(manager, NULL, route, &existing) >= 0)
- route_unmark(existing);
- continue;
- }
+ if (route->family == AF_INET || ordered_set_isempty(route->nexthops))
+ (void) link_unmark_route(link, route, NULL);
- for (size_t i = 0; i < converted->n; i++)
- if (route_get(manager, NULL, converted->routes[i], &existing) >= 0)
- route_unmark(existing);
+ else {
+ RouteNextHop *nh;
+ ORDERED_SET_FOREACH(nh, route->nexthops)
+ (void) link_unmark_route(link, route, nh);
+ }
}
}
}
if (!link->netdev || link->netdev->kind != NETDEV_KIND_WIREGUARD)
return;
- Route *route, *existing;
+ Route *route;
Wireguard *w = WIREGUARD(link->netdev);
SET_FOREACH(route, w->routes)
- if (route_get(NULL, link, route, &existing) >= 0)
- route_unmark(existing);
+ (void) link_unmark_route(link, route, NULL);
}
int link_drop_foreign_routes(Link *link) {
}
HASHMAP_FOREACH(route, link->network->routes_by_section) {
- _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
- Route *existing;
+ if (route->family == AF_INET || ordered_set_isempty(route->nexthops))
+ (void) link_unmark_route(link, route, NULL);
- r = route_convert(link->manager, link, route, &converted);
- if (r < 0)
- continue;
- if (r == 0) {
- if (route_get(NULL, link, route, &existing) >= 0)
- route_unmark(existing);
- continue;
+ else {
+ RouteNextHop *nh;
+ ORDERED_SET_FOREACH(nh, route->nexthops)
+ (void) link_unmark_route(link, route, nh);
}
-
- for (size_t i = 0; i < converted->n; i++)
- if (route_get(NULL, link, converted->routes[i], &existing) >= 0)
- route_unmark(existing);
}
link_unmark_wireguard_routes(link);
if (r < 0)
return r;
- r = route_set_netlink_message(route, m, link);
+ r = route_set_netlink_message(route, m);
if (r < 0)
return r;
return request_call_netlink_async(link->manager->rtnl, m, req);
}
+static int route_requeue_request(Request *req, Link *link, const Route *route) {
+ _unused_ _cleanup_(request_unrefp) Request *req_unref = NULL;
+ _cleanup_(route_freep) Route *tmp = NULL;
+ int r;
+
+ assert(req);
+ assert(link);
+ assert(link->manager);
+ assert(route);
+
+ /* It is not possible to adjust the Route object owned by Request, as it is used as a key to manage
+ * Request objects in the queue. Hence, we need to re-request with the updated Route object. */
+
+ if (!route_nexthops_needs_adjust(route))
+ return 0; /* The Route object does not need the adjustment. Continue with it. */
+
+ r = route_dup(route, NULL, &tmp);
+ if (r < 0)
+ return r;
+
+ r = route_adjust_nexthops(tmp, link);
+ if (r <= 0)
+ return r;
+
+ if (route_compare_func(route, tmp) == 0 && route->type == tmp->type)
+ return 0; /* No effective change?? That's OK. */
+
+ /* Avoid the request to be freed by request_detach(). */
+ req_unref = request_ref(req);
+
+ /* Detach the request from the queue, to make not the new request is deduped.
+ * Why this is necessary? IPv6 routes with different type may be handled as the same,
+ * As commented in route_adjust_nexthops(), we need to configure the adjusted type,
+ * otherwise we cannot remove the route on reconfigure or so. If we request the new Route object
+ * without detaching the current request, the new request is deduped, and the route is configured
+ * with unmodified type. */
+ request_detach(req);
+
+ /* Request the route with the adjusted Route object combined with the same other parameters. */
+ r = link_queue_request_full(link,
+ req->type,
+ tmp,
+ req->free_func,
+ req->hash_func,
+ req->compare_func,
+ req->process,
+ req->counter,
+ req->netlink_handler,
+ NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* Already queued?? That's OK. Maybe, [Route] section is effectively duplicated. */
+
+ TAKE_PTR(tmp);
+ return 1; /* New request is queued. Finish to process this request. */
+}
+
static int route_is_ready_to_configure(const Route *route, Link *link) {
int r;
return r;
}
- return route_nexthops_is_ready_to_configure(route, link);
+ return route_nexthops_is_ready_to_configure(route, link->manager);
}
static int route_process_request(Request *req, Link *link, Route *route) {
- _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
+ Route *existing;
int r;
assert(req);
if (r == 0)
return 0;
- if (route_needs_convert(route)) {
- r = route_convert(link->manager, link, route, &converted);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to convert route: %m");
-
- assert(r > 0);
- assert(converted);
-
- for (size_t i = 0; i < converted->n; i++) {
- Route *existing;
-
- if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) {
- _cleanup_(route_freep) Route *tmp = NULL;
-
- r = route_dup(converted->routes[i], NULL, &tmp);
- if (r < 0)
- return log_oom();
-
- r = route_add(link->manager, converted->links[i] ?: link, tmp);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to add route: %m");
-
- TAKE_PTR(tmp);
- } else {
- existing->source = converted->routes[i]->source;
- existing->provider = converted->routes[i]->provider;
- }
- }
- }
-
usec_t now_usec;
assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec);
log_link_debug(link, "Refuse to configure %s route with zero lifetime.",
network_config_source_to_string(route->source));
- if (converted)
- for (size_t i = 0; i < converted->n; i++) {
- Route *existing;
-
- assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0);
- route_cancel_requesting(existing);
- }
- else
- route_cancel_requesting(route);
-
+ route_cancel_requesting(route);
+ if (route_get(link->manager, link, route, &existing) >= 0)
+ route_cancel_requesting(existing);
return 1;
}
+ r = route_requeue_request(req, link, route);
+ if (r != 0)
+ return r;
+
r = route_configure(route, sec, link, req);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to configure route: %m");
- if (converted)
- for (size_t i = 0; i < converted->n; i++) {
- Route *existing;
-
- assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0);
- route_enter_configuring(existing);
- }
- else
- route_enter_configuring(route);
-
+ route_enter_configuring(route);
+ if (route_get(link->manager, link, route, &existing) >= 0)
+ route_enter_configuring(existing);
return 1;
}
-int link_request_route(
+static int link_request_route_one(
Link *link,
- Route *route,
- bool consume_object,
+ const Route *route,
+ const RouteNextHop *nh,
unsigned *message_counter,
- route_netlink_handler_t netlink_handler,
- Request **ret) {
+ route_netlink_handler_t netlink_handler) {
+ _cleanup_(route_freep) Route *tmp = NULL;
Route *existing = NULL;
int r;
assert(link);
assert(link->manager);
assert(route);
- assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
- assert(!route_needs_convert(route));
-
- (void) route_get(link->manager, link, route, &existing);
-
- if (route->lifetime_usec == 0) {
- if (consume_object)
- route_free(route);
- /* The requested route is outdated. Let's remove it. */
- return route_remove_and_drop(existing);
- }
-
- if (!existing) {
- _cleanup_(route_freep) Route *tmp = NULL;
-
- if (consume_object)
- tmp = route;
- else {
- r = route_dup(route, NULL, &tmp);
- if (r < 0)
- return r;
- }
+ r = route_dup(route, nh, &tmp);
+ if (r < 0)
+ return r;
- r = route_add(link->manager, link, tmp);
- if (r < 0)
- return r;
+ r = route_adjust_nexthops(tmp, link);
+ if (r < 0)
+ return r;
- existing = TAKE_PTR(tmp);
- } else {
- existing->source = route->source;
- existing->provider = route->provider;
- existing->lifetime_usec = route->lifetime_usec;
- if (consume_object)
- route_free(route);
- }
+ if (route_get(link->manager, link, tmp, &existing) >= 0)
+ /* Copy state for logging below. */
+ tmp->state = existing->state;
- log_route_debug(existing, "Requesting", link->manager);
+ log_route_debug(tmp, "Requesting", link->manager);
r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
- existing, NULL,
+ tmp,
+ route_free,
route_hash_func,
route_compare_func,
route_process_request,
- message_counter, netlink_handler, ret);
+ message_counter,
+ netlink_handler,
+ NULL);
if (r <= 0)
return r;
- route_enter_requesting(existing);
+ route_enter_requesting(tmp);
+ if (existing)
+ route_enter_requesting(existing);
+
+ TAKE_PTR(tmp);
return 1;
}
+int link_request_route(
+ Link *link,
+ const Route *route,
+ unsigned *message_counter,
+ route_netlink_handler_t netlink_handler) {
+
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(route);
+ assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
+
+ if (route->family == AF_INET || route_type_is_reject(route) || ordered_set_isempty(route->nexthops))
+ return link_request_route_one(link, route, NULL, message_counter, netlink_handler);
+
+ RouteNextHop *nh;
+ ORDERED_SET_FOREACH(nh, route->nexthops) {
+ r = link_request_route_one(link, route, nh, message_counter, netlink_handler);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
int r;
return 1;
}
-static int link_request_static_route(Link *link, Route *route) {
- assert(link);
- assert(link->manager);
- assert(route);
-
- if (!route_needs_convert(route))
- return link_request_route(link, route, false, &link->static_route_messages,
- static_route_handler, NULL);
-
- log_route_debug(route, "Requesting", link->manager);
- return link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
- route, NULL, route_hash_func, route_compare_func,
- route_process_request,
- &link->static_route_messages, static_route_handler, NULL);
-}
-
static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
NetDev *netdev;
Route *route;
if (only_ipv4 && route->family != AF_INET)
continue;
- r = link_request_static_route(link, route);
+ r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
if (r < 0)
return r;
}
if (only_ipv4 && route->family != AF_INET)
continue;
- r = link_request_static_route(link, route);
+ r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
if (r < 0)
return r;
}
void route_cancel_request(Route *route, Link *link) {
assert(route);
-
- link = ASSERT_PTR(route->link ?: link);
+ Manager *manager = ASSERT_PTR(route->manager ?:
+ route->link ? route->link->manager :
+ ASSERT_PTR(link)->manager);
if (!route_is_requesting(route))
return;
Request *req;
- if (route_get_request(link, route, &req) >= 0)
+ if (route_get_request(manager, route, &req) >= 0)
request_detach(req);
route_cancel_requesting(route);
static int process_route_one(
Manager *manager,
- Link *link,
uint16_t type,
Route *in,
const struct rta_cacheinfo *cacheinfo) {
_cleanup_(route_freep) Route *tmp = in;
+ Request *req = NULL;
Route *route = NULL;
+ Link *link = NULL;
bool is_new = false, update_dhcp4;
int r;
assert(tmp);
assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
- /* link may be NULL. This consumes 'in'. */
+ (void) route_get(manager, NULL, tmp, &route);
+ (void) route_get_request(manager, tmp, &req);
+ (void) route_get_link(manager, tmp, &link);
update_dhcp4 = link && tmp->family == AF_INET6 && tmp->dst_prefixlen == 0;
- (void) route_get(manager, link, tmp, &route);
-
switch (type) {
case RTM_NEWROUTE:
if (!route) {
- if (!manager->manage_foreign_routes) {
+ if (!manager->manage_foreign_routes && !(req && req->waiting_reply)) {
route_enter_configured(tmp);
log_route_debug(tmp, "Ignoring received", manager);
return 0;
}
/* If we do not know the route, then save it. */
- r = route_add(manager, link, tmp);
+ r = route_add(manager, tmp);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
return 0;
} else
/* Update remembered route with the received notification. */
- route->flags = tmp->flags;
+ route->nexthop.weight = tmp->nexthop.weight;
+
+ /* Also update information that cannot be obtained through netlink notification. */
+ if (req && req->waiting_reply) {
+ Route *rt = ASSERT_PTR(req->userdata);
+
+ route->source = rt->source;
+ route->provider = rt->provider;
+ route->lifetime_usec = rt->lifetime_usec;
+ }
route_enter_configured(route);
log_route_debug(route, is_new ? "Received new" : "Received remembered", manager);
case RTM_DELROUTE:
if (route) {
route_enter_removed(route);
- if (route->state == 0) {
- log_route_debug(route, "Forgetting", manager);
- route_free(route);
- } else
- log_route_debug(route, "Removed", manager);
+ log_route_debug(route, "Forgetting removed", manager);
+ route_free(route);
} else
log_route_debug(tmp,
manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
manager);
+ if (req)
+ route_enter_removed(req->userdata);
+
break;
default:
}
has_cacheinfo = r >= 0;
- Link *link = NULL;
- if (tmp->nexthop.ifindex > 0) {
- r = link_get_by_index(m, tmp->nexthop.ifindex, &link);
- if (r < 0) {
- /* when enumerating we might be out of sync, but we will
- * get the route again, so just ignore it */
- if (!m->enumerating)
- log_warning("rtnl: received route message for link (%i) we do not know about, ignoring", tmp->nexthop.ifindex);
- return 0;
- }
- }
-
- if (!route_needs_convert(tmp))
- return process_route_one(m, link, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
+ if (tmp->family == AF_INET || ordered_set_isempty(tmp->nexthops))
+ return process_route_one(m, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
- _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL;
- r = route_convert(m, link, tmp, &converted);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: failed to convert received route, ignoring: %m");
- return 0;
- }
+ RouteNextHop *nh;
+ ORDERED_SET_FOREACH(nh, tmp->nexthops) {
+ _cleanup_(route_freep) Route *dup = NULL;
- assert(r > 0);
- assert(converted);
+ r = route_dup(tmp, nh, &dup);
+ if (r < 0)
+ return log_oom();
- for (size_t i = 0; i < converted->n; i++)
- (void) process_route_one(m,
- converted->links[i] ?: link,
- type,
- TAKE_PTR(converted->routes[i]),
- has_cacheinfo ? &cacheinfo : NULL);
+ r = process_route_one(m, type, TAKE_PTR(dup), has_cacheinfo ? &cacheinfo : NULL);
+ if (r < 0)
+ return r;
+ }
return 1;
}
void route_cancel_request(Route *route, Link *link);
int link_request_route(
Link *link,
- Route *route,
- bool consume_object,
+ const Route *route,
unsigned *message_counter,
- route_netlink_handler_t netlink_handler,
- Request **ret);
+ route_netlink_handler_t netlink_handler);
int link_request_static_routes(Link *link, bool only_ipv4);
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
_cleanup_close_ int orig_mntns_fd = -EBADF;
int r, rr;
- r = namespace_open(0, NULL, &orig_mntns_fd, NULL, NULL, NULL);
+ r = namespace_open(0,
+ /* ret_pidns_fd = */ NULL,
+ &orig_mntns_fd,
+ /* ret_netns_fd = */ NULL,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to pin originating mount namespace: %m");
- r = namespace_enter(-EBADF, mntns_fd, -EBADF, -EBADF, -EBADF);
+ r = namespace_enter(/* pidns_fd = */ -EBADF,
+ mntns_fd,
+ /* netns_fd = */ -EBADF,
+ /* userns_fd = */ -EBADF,
+ /* root_fd = */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to enter mount namespace: %m");
rr = do_wipe_fully_visible_fs();
- r = namespace_enter(-EBADF, orig_mntns_fd, -EBADF, -EBADF, -EBADF);
+ r = namespace_enter(/* pidns_fd = */ -EBADF,
+ orig_mntns_fd,
+ /* netns_fd = */ -EBADF,
+ /* userns_fd = */ -EBADF,
+ /* root_fd = */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to enter original mount namespace: %m");
#include <net/if.h>
#include <linux/if.h>
+#include <linux/nl80211.h>
#include <linux/veth.h>
#include <sys/file.h>
+#include <sys/mount.h>
#include "sd-device.h"
#include "sd-id128.h"
#include "sd-netlink.h"
#include "alloc-util.h"
+#include "device-private.h"
+#include "device-util.h"
#include "ether-addr-util.h"
+#include "fd-util.h"
#include "hexdecoct.h"
#include "lock-util.h"
#include "missing_network.h"
+#include "mkdir.h"
+#include "mount-util.h"
+#include "namespace-util.h"
#include "netif-naming-scheme.h"
#include "netlink-util.h"
#include "nspawn-network.h"
#include "parse-util.h"
+#include "process-util.h"
#include "siphash24.h"
#include "socket-netlink.h"
#include "socket-util.h"
return 0;
}
-int move_network_interfaces(int netns_fd, char **iface_pairs) {
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+static int netns_child_begin(int netns_fd, int *ret_original_netns_fd) {
+ _cleanup_close_ int original_netns_fd = -EBADF;
int r;
- if (strv_isempty(iface_pairs))
+ assert(netns_fd >= 0);
+
+ if (ret_original_netns_fd) {
+ r = namespace_open(0,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ &original_netns_fd,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open original network namespace: %m");
+ }
+
+ r = namespace_enter(/* pidns_fd = */ -EBADF,
+ /* mntns_fd = */ -EBADF,
+ netns_fd,
+ /* userns_fd = */ -EBADF,
+ /* root_fd = */ -EBADF);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enter child network namespace: %m");
+
+ r = umount_recursive("/sys/", /* flags = */ 0);
+ if (r < 0)
+ log_debug_errno(r, "Failed to unmount directories below /sys/, ignoring: %m");
+
+ (void) mkdir_p("/sys/", 0755);
+
+ /* Populate new sysfs instance associated with the client netns, to make sd_device usable. */
+ r = mount_nofollow_verbose(LOG_ERR, "sysfs", "/sys/", "sysfs",
+ MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, /* opts = */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mount sysfs on /sys/: %m");
+
+ /* udev_avaliable() might be called previously and the result may be cached.
+ * Now, we (re-)mount sysfs. Hence, we need to reset the cache. */
+ reset_cached_udev_availability();
+
+ if (ret_original_netns_fd)
+ *ret_original_netns_fd = TAKE_FD(original_netns_fd);
+
+ return 0;
+}
+
+static int netns_fork_and_wait(int netns_fd, int *ret_original_netns_fd) {
+ int r;
+
+ assert(netns_fd >= 0);
+
+ r = safe_fork("(sd-netns)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to fork process (sd-netns): %m");
+ if (r == 0) {
+ if (netns_child_begin(netns_fd, ret_original_netns_fd) < 0)
+ _exit(EXIT_FAILURE);
+
return 0;
+ }
- r = sd_netlink_open(&rtnl);
+ if (ret_original_netns_fd)
+ *ret_original_netns_fd = -EBADF;
+
+ return 1;
+}
+
+static int needs_rename(sd_netlink **rtnl, sd_device *dev, const char *name) {
+ int r;
+
+ assert(rtnl);
+ assert(dev);
+ assert(name);
+
+ const char *ifname;
+ r = sd_device_get_sysname(dev, &ifname);
if (r < 0)
- return log_error_errno(r, "Failed to connect to netlink: %m");
+ return r;
- STRV_FOREACH_PAIR(i, b, iface_pairs) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- int ifi;
+ if (streq(name, ifname))
+ return false;
- ifi = rtnl_resolve_interface_or_warn(&rtnl, *i);
- if (ifi < 0)
- return ifi;
+ int ifindex;
+ r = sd_device_get_ifindex(dev, &ifindex);
+ if (r < 0)
+ return r;
+
+ _cleanup_strv_free_ char **altnames = NULL;
+ r = rtnl_get_link_alternative_names(rtnl, ifindex, &altnames);
+ if (r == -EOPNOTSUPP)
+ return true; /* alternative interface name is not supported, hence the name is not
+ * assigned to the interface. */
+ if (r < 0)
+ return r;
- r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi);
+ return !strv_contains(altnames, name);
+}
+
+static int move_wlan_interface_impl(sd_netlink **genl, int netns_fd, sd_device *dev) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *our_genl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
+
+ assert(netns_fd >= 0);
+ assert(dev);
+
+ if (!genl)
+ genl = &our_genl;
+ if (!*genl) {
+ r = sd_genl_socket_open(genl);
if (r < 0)
- return log_error_errno(r, "Failed to allocate netlink message: %m");
+ return log_error_errno(r, "Failed to connect to generic netlink: %m");
+ }
+
+ r = sd_genl_message_new(*genl, NL80211_GENL_NAME, NL80211_CMD_SET_WIPHY_NETNS, &m);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to allocate netlink message: %m");
+
+ uint32_t phy_index;
+ r = device_get_sysattr_u32(dev, "phy80211/index", &phy_index);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get phy index: %m");
+
+ r = sd_netlink_message_append_u32(m, NL80211_ATTR_WIPHY, phy_index);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to append phy index to netlink message: %m");
+
+ r = sd_netlink_message_append_u32(m, NL80211_ATTR_NETNS_FD, netns_fd);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to append namespace fd to netlink message: %m");
+
+ r = sd_netlink_call(*genl, m, 0, NULL);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to move interface to namespace: %m");
+
+ return 0;
+}
+
+static int move_wlan_interface_one(
+ sd_netlink **rtnl,
+ sd_netlink **genl,
+ int *temp_netns_fd,
+ int netns_fd,
+ sd_device *dev,
+ const char *name) {
+
+ int r;
- r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd);
+ assert(rtnl);
+ assert(genl);
+ assert(temp_netns_fd);
+ assert(netns_fd >= 0);
+ assert(dev);
+ assert(name);
+
+ r = needs_rename(rtnl, dev, name);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to determine if the interface should be renamed to '%s': %m", name);
+ if (r == 0)
+ return move_wlan_interface_impl(genl, netns_fd, dev);
+
+ /* The command NL80211_CMD_SET_WIPHY_NETNS takes phy instead of network interface, and does not take
+ * an interface name in the passed network namespace. Hence, we need to move the phy and interface to
+ * a temporary network namespace, rename the interface in it, and move them to the requested netns. */
+
+ if (*temp_netns_fd < 0) {
+ r = netns_acquire();
if (r < 0)
- return log_error_errno(r, "Failed to append namespace fd to netlink message: %m");
+ return log_error_errno(r, "Failed to acquire new network namespace: %m");
+ *temp_netns_fd = r;
+ }
+
+ r = move_wlan_interface_impl(genl, *temp_netns_fd, dev);
+ if (r < 0)
+ return r;
+
+ const char *sysname;
+ r = sd_device_get_sysname(dev, &sysname);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get interface name: %m");
- if (!streq(*b, *i)) {
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, *b);
- if (r < 0)
- return log_error_errno(r, "Failed to add netlink interface name: %m");
+ r = netns_fork_and_wait(*temp_netns_fd, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to fork process (nspawn-rename-wlan): %m");
+ if (r == 0) {
+ _cleanup_(sd_device_unrefp) sd_device *temp_dev = NULL;
+
+ r = rtnl_rename_link(NULL, sysname, name);
+ if (r < 0) {
+ log_error_errno(r, "Failed to rename network interface '%s' to '%s': %m", sysname, name);
+ goto finalize;
}
- r = sd_netlink_call(rtnl, m, 0, NULL);
+ r = sd_device_new_from_ifname(&temp_dev, name);
+ if (r < 0) {
+ log_error_errno(r, "Failed to acquire device '%s': %m", name);
+ goto finalize;
+ }
+
+ r = move_wlan_interface_impl(NULL, netns_fd, temp_dev);
+
+ finalize:
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+
+ return 0;
+}
+
+static int move_network_interface_one(sd_netlink **rtnl, int netns_fd, sd_device *dev, const char *name) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ int r;
+
+ assert(rtnl);
+ assert(netns_fd >= 0);
+ assert(dev);
+ assert(name);
+
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to rtnetlink: %m");
+ }
+
+ int ifindex;
+ r = sd_device_get_ifindex(dev, &ifindex);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to get ifindex: %m");
+
+ r = sd_rtnl_message_new_link(*rtnl, &m, RTM_SETLINK, ifindex);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to allocate netlink message: %m");
+
+ r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to append namespace fd to netlink message: %m");
+
+ r = needs_rename(rtnl, dev, name);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to determine if the interface should be renamed to '%s': %m", name);
+ if (r > 0) {
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, name);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to add netlink interface name: %m");
+ }
+
+ r = sd_netlink_call(*rtnl, m, 0, NULL);
+ if (r < 0)
+ return log_device_error_errno(dev, r, "Failed to move interface to namespace: %m");
+
+ return 0;
+}
+
+int move_network_interfaces(int netns_fd, char **iface_pairs) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL, *genl = NULL;
+ _cleanup_close_ int temp_netns_fd = -EBADF;
+ int r;
+
+ assert(netns_fd >= 0);
+
+ if (strv_isempty(iface_pairs))
+ return 0;
+
+ STRV_FOREACH_PAIR(from, to, iface_pairs) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+
+ r = sd_device_new_from_ifname(&dev, *from);
if (r < 0)
- return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i);
+ return log_error_errno(r, "Unknown interface name %s: %m", *from);
+
+ if (device_is_devtype(dev, "wlan"))
+ r = move_wlan_interface_one(&rtnl, &genl, &temp_netns_fd, netns_fd, dev, *to);
+ else
+ r = move_network_interface_one(&rtnl, netns_fd, dev, *to);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int move_back_network_interfaces(int child_netns_fd, char **interface_pairs) {
+ _cleanup_close_ int parent_netns_fd = -EBADF;
+ int r;
+
+ assert(child_netns_fd >= 0);
+
+ if (strv_isempty(interface_pairs))
+ return 0;
+
+ r = netns_fork_and_wait(child_netns_fd, &parent_netns_fd);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Reverse network interfaces pair list so that interfaces get their initial name back.
+ * This is about ensuring interfaces get their old name back when being moved back. */
+ interface_pairs = strv_reverse(interface_pairs);
+
+ r = move_network_interfaces(parent_netns_fd, interface_pairs);
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
return 0;
int setup_ipvlan(const char *machine_name, pid_t pid, char **iface_pairs);
int move_network_interfaces(int netns_fd, char **iface_pairs);
+int move_back_network_interfaces(int child_netns_fd, char **interface_pairs);
int veth_extra_parse(char ***l, const char *p);
return r;
if (arg_userns_mode != USER_NAMESPACE_NO) {
- r = namespace_open(0, NULL, &mntns_fd, NULL, NULL, NULL);
+ r = namespace_open(0,
+ /* ret_pidns_fd = */ NULL,
+ &mntns_fd,
+ /* ret_netns_fd = */ NULL,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to pin outer mount namespace: %m");
* user if user namespaces are turned on. */
if (arg_network_namespace_path) {
- r = namespace_enter(-1, -1, netns_fd, -1, -1);
+ r = namespace_enter(/* pidns_fd = */ -EBADF,
+ /* mntns_fd = */ -EBADF,
+ netns_fd,
+ /* userns_fd = */ -EBADF,
+ /* root_fd = */ -EBADF);
if (r < 0)
return log_error_errno(r, "Failed to join network namespace: %m");
}
if (child_netns_fd < 0) {
/* Make sure we have an open file descriptor to the child's network
* namespace so it stays alive even if the child exits. */
- r = namespace_open(*pid, NULL, NULL, &child_netns_fd, NULL, NULL);
+ r = namespace_open(*pid,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
+ &child_netns_fd,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to open child network namespace: %m");
}
fd_kmsg_fifo = safe_close(fd_kmsg_fifo);
if (arg_private_network) {
- /* Move network interfaces back to the parent network namespace. We use `safe_fork`
- * to avoid having to move the parent to the child network namespace. */
- r = safe_fork(NULL, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL);
+ r = move_back_network_interfaces(child_netns_fd, arg_network_interfaces);
if (r < 0)
return r;
-
- if (r == 0) {
- _cleanup_close_ int parent_netns_fd = -EBADF;
-
- r = namespace_open(getpid_cached(), NULL, NULL, &parent_netns_fd, NULL, NULL);
- if (r < 0) {
- log_error_errno(r, "Failed to open parent network namespace: %m");
- _exit(EXIT_FAILURE);
- }
-
- r = namespace_enter(-1, -1, child_netns_fd, -1, -1);
- if (r < 0) {
- log_error_errno(r, "Failed to enter child network namespace: %m");
- _exit(EXIT_FAILURE);
- }
-
- /* Reverse network interfaces pair list so that interfaces get their initial name back.
- * This is about ensuring interfaces get their old name back when being moved back. */
- arg_network_interfaces = strv_reverse(arg_network_interfaces);
-
- r = move_network_interfaces(parent_netns_fd, arg_network_interfaces);
- if (r < 0)
- log_error_errno(r, "Failed to move network interfaces back to parent network namespace: %m");
-
- _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
- }
}
r = wait_for_container(TAKE_PID(*pid), &container_status);
#include <linux/loop.h>
+#include "sd-messages.h"
+
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-locator.h"
return true;
}
+static void log_portable_verb(
+ const char *verb,
+ const char *message_id,
+ const char *image_path,
+ OrderedHashmap *extension_images,
+ char **extension_image_paths,
+ PortableFlags flags) {
+
+ _cleanup_free_ char *root_base_name = NULL, *extensions_joined = NULL;
+ _cleanup_strv_free_ char **extension_base_names = NULL;
+ Image *ext;
+ int r;
+
+ assert(verb);
+ assert(message_id);
+ assert(image_path);
+ assert(!extension_images || !extension_image_paths);
+
+ /* Use the same structured metadata as it is attached to units via LogExtraFields=. The main image
+ * is logged as PORTABLE_ROOT= and extensions, if any, as individual PORTABLE_EXTENSION= fields. */
+
+ r = path_extract_filename(image_path, &root_base_name);
+ if (r < 0)
+ log_debug_errno(r, "Failed to extract basename from '%s', ignoring: %m", image_path);
+
+ ORDERED_HASHMAP_FOREACH(ext, extension_images) {
+ _cleanup_free_ char *extension_base_name = NULL;
+
+ r = path_extract_filename(ext->path, &extension_base_name);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to extract basename from '%s', ignoring: %m", ext->path);
+ continue;
+ }
+
+ r = strv_extendf(&extension_base_names, "PORTABLE_EXTENSION=%s", extension_base_name);
+ if (r < 0)
+ log_oom_debug();
+
+ if (!strextend_with_separator(&extensions_joined, ", ", ext->path))
+ log_oom_debug();
+ }
+
+ STRV_FOREACH(e, extension_image_paths) {
+ _cleanup_free_ char *extension_base_name = NULL;
+
+ r = path_extract_filename(*e, &extension_base_name);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to extract basename from '%s', ignoring: %m", *e);
+ continue;
+ }
+
+ r = strv_extendf(&extension_base_names, "PORTABLE_EXTENSION=%s", extension_base_name);
+ if (r < 0)
+ log_oom_debug();
+
+ if (!strextend_with_separator(&extensions_joined, ", ", *e))
+ log_oom_debug();
+ }
+
+ LOG_CONTEXT_PUSH_STRV(extension_base_names);
+
+ log_struct(LOG_INFO,
+ LOG_MESSAGE("Successfully %s%s '%s%s%s'",
+ verb,
+ FLAGS_SET(flags, PORTABLE_RUNTIME) ? " ephemeral" : "",
+ image_path,
+ isempty(extensions_joined) ? "" : "' and its extension(s) '",
+ strempty(extensions_joined)),
+ message_id,
+ "PORTABLE_ROOT=%s", strna(root_base_name));
+}
+
int portable_attach(
sd_bus *bus,
const char *name_or_path,
* operation otherwise. */
(void) install_image_and_extensions_symlinks(image, extension_images, flags, changes, n_changes);
+ log_portable_verb(
+ "attached",
+ "MESSAGE_ID=" SD_MESSAGE_PORTABLE_ATTACHED_STR,
+ image->path,
+ extension_images,
+ /* extension_image_paths= */ NULL,
+ flags);
+
return 0;
}
if (rmdir(where) >= 0)
portable_changes_add(changes, n_changes, PORTABLE_UNLINK, where, NULL);
+ log_portable_verb(
+ "detached",
+ "MESSAGE_ID=" SD_MESSAGE_PORTABLE_DETACHED_STR,
+ name_or_path,
+ /* extension_images= */ NULL,
+ extension_image_paths,
+ flags);
+
return ret;
not_found:
if (r < 0)
return r;
- r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, NULL, &rootfd);
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
if (r < 0)
return r;
fd = safe_close(fd);
/* Store current mount namespace */
- r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
+ r = namespace_open(0,
+ /* ret_pidns_fd = */ NULL,
+ &initial_mntns_fd,
+ /* ret_netns_fd = */ NULL,
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
if (r < 0)
return log_error_errno(r, "Can't fetch current mount namespace: %m");
return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
/* Return to initial namespace and proceed a lazy tmpfs unmount */
- r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
+ r = namespace_enter(/* pidns_fd = */ -EBADF,
+ initial_mntns_fd,
+ /* netns_fd = */ -EBADF,
+ /* userns_fd = */ -EBADF,
+ /* root_fd = */ -EBADF);
if (r < 0)
return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id);
if (!pidref_is_set(target))
return -ESRCH;
- r = namespace_open(target->pid, &pidns_fd, &mntns_fd, NULL, NULL, &root_fd);
+ r = namespace_open(target->pid, &pidns_fd, &mntns_fd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &root_fd);
if (r < 0)
return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m");
if (netnsfd < 0) {
r = namespace_open(
0,
- /* pidns_fd= */ NULL,
- /* mntns_fd= */ NULL,
+ /* ret_pidns_fd = */ NULL,
+ /* ret_mntns_fd = */ NULL,
&_netns_fd,
- /* userns_fd= */ NULL,
- /* root_fd= */ NULL);
+ /* ret_userns_fd = */ NULL,
+ /* ret_root_fd = */ NULL);
if (r < 0)
return r;
#include "id128-util.h"
#include "log.h"
#include "macro.h"
+#include "missing_threads.h"
#include "parse-util.h"
#include "path-util.h"
#include "signal-util.h"
(errno == ENOENT ? true : -errno) : false;
}
-bool udev_available(void) {
- static int cache = -1;
+static int cached_udev_availability = -1;
+
+void reset_cached_udev_availability(void) {
+ cached_udev_availability = -1;
+}
+bool udev_available(void) {
/* The service systemd-udevd is started only when /sys is read write.
* See systemd-udevd.service: ConditionPathIsReadWrite=/sys
* Also, our container interface (http://systemd.io/CONTAINER_INTERFACE/) states that /sys must
* be mounted in read-only mode in containers. */
- if (cache >= 0)
- return cache;
+ if (cached_udev_availability >= 0)
+ return cached_udev_availability;
- return (cache = (path_is_read_only_fs("/sys/") <= 0));
+ return (cached_udev_availability = (path_is_read_only_fs("/sys/") <= 0));
}
int device_get_vendor_string(sd_device *device, const char **ret) {
int udev_queue_is_empty(void);
+void reset_cached_udev_availability(void);
bool udev_available(void);
int device_get_vendor_string(sd_device *device, const char **ret);
#define SD_MESSAGE_SYSV_GENERATOR_DEPRECATED SD_ID128_MAKE(a8,fa,8d,ac,db,1d,44,3e,95,03,b8,be,36,7a,6a,db)
#define SD_MESSAGE_SYSV_GENERATOR_DEPRECATED_STR SD_ID128_MAKE_STR(a8,fa,8d,ac,db,1d,44,3e,95,03,b8,be,36,7a,6a,db)
+#define SD_MESSAGE_PORTABLE_ATTACHED SD_ID128_MAKE(18,7c,62,eb,1e,7f,46,3b,b5,30,39,4f,52,cb,09,0f)
+#define SD_MESSAGE_PORTABLE_ATTACHED_STR SD_ID128_MAKE_STR(18,7c,62,eb,1e,7f,46,3b,b5,30,39,4f,52,cb,09,0f)
+#define SD_MESSAGE_PORTABLE_DETACHED SD_ID128_MAKE(76,c5,c7,54,d6,28,49,0d,8e,cb,a4,c9,d0,42,11,2b)
+#define SD_MESSAGE_PORTABLE_DETACHED_STR SD_ID128_MAKE_STR(76,c5,c7,54,d6,28,49,0d,8e,cb,a4,c9,d0,42,11,2b)
+
_SD_END_DECLARATIONS;
#endif
local workspace="${1:?}"
local container="$workspace/testsuite-13-container-template"
+ # For virtual wlan interface.
+ instmods mac80211_hwsim
+ generate_module_dependencies
+
# Create a dummy container "template" with a minimal toolset, which we can
# then use as a base for our nspawn/machinectl tests
initdir="$container" setup_basic_dirs
}
nspawn_settings_cleanup() {
- for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-ipvlan{1,2}; do
+ for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-ipvlan{1,2}; do
ip link del "$dev" || :
done
}
testcase_nspawn_settings() {
- local root container dev private_users
+ local root container dev private_users wlan_names='' wlan_checks=''
mkdir -p /run/systemd/nspawn
root="$(mktemp -d /var/lib/machines/testsuite-13.nspawn-settings.XXX)"
rm -f "/etc/systemd/nspawn/$container.nspawn"
mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
- for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
+ # add virtual wlan interfaces
+ if modprobe mac80211_hwsim radios=2; then
+ wlan_names='wlan0 wlan1:wl-renamed1'
+ wlan_checks='ip link | grep wlan0\nip link | grep wl-renamed1'
+ fi
+
+ for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
ip link add "$dev" type dummy
done
udevadm settle
+ ip link property add dev sd-shared3 altname sd-altname3 altname sd-altname-tooooooooooooo-long
ip link
trap nspawn_settings_cleanup RETURN
VirtualEthernet=yes
VirtualEthernetExtra=my-fancy-veth1
VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
-Interface=sd-shared1 sd-shared2:sd-shared2
+Interface=sd-shared1 sd-shared2:sd-renamed2 sd-shared3:sd-altname3 ${wlan_names}
MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
Zone=sd-zone0
ip link | grep my-fancy-veth1@
ip link | grep my-fancy-veth2@
ip link | grep sd-shared1
-ip link | grep sd-shared2
+ip link | grep sd-renamed2
+ip link | grep sd-shared3
+ip link | grep sd-altname3
+ip link | grep sd-altname-tooooooooooooo-long
ip link | grep mv-sd-macvlan1@
ip link | grep my-macvlan2@
ip link | grep iv-sd-ipvlan1@
ip link | grep my-ipvlan2@
EOF
+ echo -e "$wlan_checks" >>"$root/entrypoint.sh"
+
timeout 30 systemd-nspawn --directory="$root"
# And now for stuff that needs to run separately