# Temporarily add libarchive-devel build dep and libarchive runtime dep
# until the change propagates to Rawhide's specfile
- "sed -ri '0,/^BuildRequires: .+$/s//&\\nBuildRequires: libarchive-devel\\nRequires: libarchive/' .packit_rpm/systemd.spec"
+ # FIXME: temporarily build without BPF stuff, since there's currently no
+ # bpftool package in Rawhide (at least not until [0] lands)
+ # [0] https://bodhi.fedoraproject.org/updates/FEDORA-2024-bb73636f1d
+ - "sed -nri '1N;2N;/\\nBuildRequires:\\s+bpftool$/{N;N;d};P;N;D' .packit_rpm/systemd.spec"
jobs:
- job: copr_build
sudo apt-get install -y -t "$UBUNTU_RELEASE-backports" lxc
sudo apt-get install -y python3-debian git dpkg-dev fakeroot python3-jinja2
- [ -d "$AUTOPKGTEST_DIR" ] || git clone --quiet --depth=1 https://salsa.debian.org/ci-team/autopkgtest.git "$AUTOPKGTEST_DIR"
+ [ -d "$AUTOPKGTEST_DIR" ] || git clone --quiet --branch=debian/5.32 --depth=1 https://salsa.debian.org/ci-team/autopkgtest.git "$AUTOPKGTEST_DIR"
create_container
;;
[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/systemd.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#systemd)<br/>
[![CIFuzz](https://github.com/systemd/systemd/workflows/CIFuzz/badge.svg)](https://github.com/systemd/systemd/actions)<br/>
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)<br/>
-[![CentOS CI - CentOS 8](https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/buildStatus/icon?subject=CentOS%20CI%20-%20CentOS%208&job=upstream-centos8)](https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/job/upstream-centos8/)<br/>
+[![CentOS CI - CentOS 9](https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/buildStatus/icon?subject=CentOS%20CI%20-%20CentOS%209&job=upstream-centos9s)](https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/job/upstream-centos9s/)<br/>
[![CentOS CI - Arch](https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/buildStatus/icon?subject=CentOS%20CI%20-%20Arch&job=upstream-vagrant-archlinux)](https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/job/upstream-vagrant-archlinux/)<br/>
[![CentOS CI - Arch (sanitizers)](https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/buildStatus/icon?subject=CentOS%20CI%20-%20Arch%20(sanitizers)&job=upstream-vagrant-archlinux-sanitizers)](https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/job/upstream-vagrant-archlinux-sanitizers/)<br/>
[![Fossies codespell report](https://fossies.org/linux/test/systemd-main.tar.gz/codespell.svg)](https://fossies.org/linux/test/systemd-main.tar.gz/codespell.html)</br>
<term><varname>systemd.mask=</varname></term>
<term><varname>systemd.wants=</varname></term>
<term><varname>systemd.debug_shell</varname></term>
+ <term><varname>systemd.default_debug_tty=</varname></term>
<listitem>
<para>Additional parameters understood by
<citerefentry><refentrytitle>systemd-debug-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<varlistentry>
<term><option>--unlock-tpm2-device=</option><replaceable>PATH</replaceable></term>
- <listitem><para>Use a TPM2 device insteaad of a password/passhprase read from stdin to unlock the
+ <listitem><para>Use a TPM2 device instead of a password/passhprase read from stdin to unlock the
volume. Expects a device node path referring to the TPM2 chip (e.g. <filename>/dev/tpmrm0</filename>).
Alternatively the special value <literal>auto</literal> may be specified, in order to automatically
determine the device node of a currently discovered TPM2 device (of which there must be exactly one).
RAM disk (initrd) while <option>systemd.wants=</option> is
honored only in the main system.</para>
- <para>If the <option>systemd.debug_shell</option> or
- <option>rd.systemd.debug_shell</option> option is
- specified, the debug shell service
- <literal>debug-shell.service</literal> is pulled into the boot
- transaction and a debug shell will be spawned during early boot.
- By default, <filename>&DEBUGTTY;</filename> is used, but a specific tty can also be set,
- either with or without the <filename>/dev/</filename> prefix.
- Note that the shell may also be turned on persistently by enabling it with
- <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
- <command>enable</command> command.
- <option>rd.systemd.debug_shell=</option> is honored only by initial
- RAM disk (initrd) while <option>systemd.debug_shell</option> is
- honored only in the main system.</para>
+ <para>If the <option>systemd.debug_shell</option> or <option>rd.systemd.debug_shell</option> option is
+ specified, the debug shell service <literal>debug-shell.service</literal> is pulled into the boot
+ transaction and a debug shell will be spawned during early boot. By default,
+ <filename>&DEBUGTTY;</filename> is used, but a specific tty can also be specified, either with or without
+ the <filename>/dev/</filename> prefix. To set the tty to use without enabling the debug shell, the
+ <option>systemd.default_debug_tty=</option> option can be used which also takes a tty with or without the
+ <filename>/dev/</filename> prefix. Note that the shell may also be turned on persistently by enabling it
+ with <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <command>enable</command> command. <option>rd.systemd.debug_shell=</option> is honored only by initial
+ RAM disk (initrd) while <option>systemd.debug_shell</option> is honored only in the main system.</para>
<para><filename>systemd-debug-generator</filename> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
are implied and these settings in the .network file are silently ignored. Also,
<varname>Hostname=</varname>,
<varname>MUDURL=</varname>,
- <varname>RequestAddress</varname>,
+ <varname>RequestAddress=</varname>,
<varname>RequestOptions=</varname>,
<varname>SendOption=</varname>,
<varname>SendVendorOption=</varname>,
man-db
openbsd-netcat
openssh
+ pacman
polkit
python-pefile
python-psutil
if (current < 0)
return current;
- if (((unsigned long) current & 0xffff) == PER_LINUX32)
+ if (((unsigned long) current & OPINIONATED_PERSONALITY_MASK) == PER_LINUX32)
*ret = PER_LINUX32;
else
*ret = PER_LINUX;
bool oom_score_adjust_is_valid(int oa);
#ifndef PERSONALITY_INVALID
-/* personality(7) documents that 0xffffffffUL is used for querying the
+/* personality(2) documents that 0xFFFFFFFFUL is used for querying the
* current personality, hence let's use that here as error
* indicator. */
-#define PERSONALITY_INVALID 0xffffffffLU
+#define PERSONALITY_INVALID 0xFFFFFFFFUL
#endif
+/* The personality() syscall returns a 32-bit value where the top three bytes are reserved for flags that
+ * emulate historical or architectural quirks, and only the least significant byte reflects the actual
+ * personality we're interested in. */
+#define OPINIONATED_PERSONALITY_MASK 0xFFUL
+
unsigned long personality_from_string(const char *p);
const char *personality_to_string(unsigned long);
static char *arg_default_unit = NULL;
static char **arg_mask = NULL;
static char **arg_wants = NULL;
-static char *arg_debug_shell = NULL;
+static bool arg_debug_shell = false;
+static char *arg_debug_tty = NULL;
+static char *arg_default_debug_tty = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_default_unit, freep);
STATIC_DESTRUCTOR_REGISTER(arg_mask, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_wants, strv_freep);
-STATIC_DESTRUCTOR_REGISTER(arg_debug_shell, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_debug_tty, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_default_debug_tty, freep);
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
return log_oom();
} else if (proc_cmdline_key_streq(key, "systemd.debug_shell")) {
- const char *t = NULL;
-
r = value ? parse_boolean(value) : 1;
- if (r < 0)
- t = skip_dev_prefix(value);
- else if (r > 0)
- t = skip_dev_prefix(DEBUGTTY);
+ arg_debug_shell = r != 0;
+ if (r >= 0)
+ return 0;
+
+ return free_and_strdup_warn(&arg_debug_tty, skip_dev_prefix(value));
+
+ } else if (proc_cmdline_key_streq(key, "systemd.default_debug_tty")) {
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
- return free_and_strdup_warn(&arg_debug_shell, t);
+ return free_and_strdup_warn(&arg_default_debug_tty, skip_dev_prefix(value));
} else if (streq(key, "systemd.unit")) {
}
static void install_debug_shell_dropin(const char *dir) {
+ const char *tty = arg_debug_tty ?: arg_default_debug_tty;
int r;
- if (streq(arg_debug_shell, skip_dev_prefix(DEBUGTTY)))
+ if (!tty || path_equal(tty, skip_dev_prefix(DEBUGTTY)))
return;
r = write_drop_in_format(dir, "debug-shell.service", 50, "tty",
"ConditionPathExists=\n"
"[Service]\n"
"TTYPath=/dev/%s",
- arg_debug_shell, arg_debug_shell);
+ tty, tty);
if (r < 0)
log_warning_errno(r, "Failed to write drop-in for debug-shell.service, ignoring: %m");
}
SD_BUS_ERROR_MAP(BUS_ERROR_SPEED_METER_INACTIVE, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_UNMANAGED_INTERFACE, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_ALREADY_RELOADING, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_HOME, EEXIST),
SD_BUS_ERROR_MAP(BUS_ERROR_UID_IN_USE, EEXIST),
#define BUS_ERROR_SPEED_METER_INACTIVE "org.freedesktop.network1.SpeedMeterInactive"
#define BUS_ERROR_UNMANAGED_INTERFACE "org.freedesktop.network1.UnmanagedInterface"
+#define BUS_ERROR_NETWORK_ALREADY_RELOADING "org.freedesktop.network1.AlreadyReloading"
#define BUS_ERROR_NO_SUCH_HOME "org.freedesktop.home1.NoSuchHome"
#define BUS_ERROR_UID_IN_USE "org.freedesktop.home1.UIDInUse"
return 1; /* 1 means the interface will be reconfigured. */
}
+typedef struct ReconfigureData {
+ Link *link;
+ Manager *manager;
+ sd_bus_message *message;
+} ReconfigureData;
+
+static void reconfigure_data_destroy_callback(ReconfigureData *data) {
+ int r;
+
+ assert(data);
+ assert(data->link);
+ assert(data->manager);
+ assert(data->manager->reloading > 0);
+ assert(data->message);
+
+ link_unref(data->link);
+
+ data->manager->reloading--;
+ if (data->manager->reloading <= 0) {
+ r = sd_bus_reply_method_return(data->message, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to send reply for 'Reload' DBus method, ignoring: %m");
+ }
+
+ sd_bus_message_unref(data->message);
+ free(data);
+}
+
+static int reconfigure_handler_on_bus_method_reload(sd_netlink *rtnl, sd_netlink_message *m, ReconfigureData *data) {
+ assert(data);
+ assert(data->link);
+ return link_reconfigure_handler_internal(rtnl, m, data->link, /* force = */ false);
+}
+
+int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ _cleanup_free_ ReconfigureData *data = NULL;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+ assert(message);
+
+ /* See comments in link_reconfigure() above. */
+ if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_LINGER))
+ return 0;
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_GETLINK, link->ifindex);
+ if (r < 0)
+ return r;
+
+ data = new(ReconfigureData, 1);
+ if (!data)
+ return -ENOMEM;
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req,
+ reconfigure_handler_on_bus_method_reload,
+ reconfigure_data_destroy_callback, data);
+ if (r < 0)
+ return r;
+
+ *data = (ReconfigureData) {
+ .link = link_ref(link),
+ .manager = link->manager,
+ .message = sd_bus_message_ref(message),
+ };
+
+ link->manager->reloading++;
+
+ TAKE_PTR(data);
+ return 0;
+}
+
static int link_initialized_and_synced(Link *link) {
int r;
int link_reconfigure_impl(Link *link, bool force);
int link_reconfigure(Link *link, bool force);
+int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message);
int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action);
int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
}
static int bus_method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *manager = userdata;
+ Manager *manager = ASSERT_PTR(userdata);
int r;
+ if (manager->reloading > 0)
+ return sd_bus_error_set(error, BUS_ERROR_NETWORK_ALREADY_RELOADING, "Already reloading.");
+
r = bus_verify_polkit_async(
message,
"org.freedesktop.network1.reload",
if (r == 0)
return 1; /* Polkit will call us back */
- r = manager_reload(manager);
+ r = manager_reload(manager, message);
if (r < 0)
return r;
+ if (manager->reloading > 0)
+ return 1; /* Will reply later. */
+
return sd_bus_reply_method_return(message, NULL);
}
static int signal_reload_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
- manager_reload(m);
+ (void) manager_reload(m, /* message = */ NULL);
return 0;
}
return 0;
}
-int manager_reload(Manager *m) {
+int manager_reload(Manager *m, sd_bus_message *message) {
Link *link;
int r;
goto finish;
HASHMAP_FOREACH(link, m->links_by_index) {
- r = link_reconfigure(link, /* force = */ false);
- if (r < 0)
- goto finish;
+ if (message)
+ r = link_reconfigure_on_bus_method_reload(link, message);
+ else
+ r = link_reconfigure(link, /* force = */ false);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to reconfigure the interface: %m");
+ link_enter_failed(link);
+ }
}
r = 0;
OrderedSet *remove_request_queue;
Hashmap *tuntap_fds_by_name;
+
+ unsigned reloading;
};
int manager_new(Manager **ret, bool test_mode);
int manager_set_hostname(Manager *m, const char *hostname);
int manager_set_timezone(Manager *m, const char *timezone);
-int manager_reload(Manager *m);
+int manager_reload(Manager *m, sd_bus_message *message);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
return link->bridge_vlan_set;
case REQUEST_TYPE_SET_LINK_CAN:
- /* Do not check link->set_flgas_messages here, as it is ok even if link->flags
+ /* Do not check link->set_flags_messages here, as it is ok even if link->flags
* is outdated, and checking the counter causes a deadlock. */
if (FLAGS_SET(link->flags, IFF_UP)) {
/* The CAN interface must be down to configure bitrate, etc... */
if (!netdev_is_ready(link->network->bond))
return false;
m = link->network->bond->ifindex;
-
- /* Do not check link->set_flgas_messages here, as it is ok even if link->flags
- * is outdated, and checking the counter causes a deadlock. */
- if (FLAGS_SET(link->flags, IFF_UP)) {
- /* link must be down when joining to bond master. */
- r = link_down_now(link);
- if (r < 0)
- return r;
- }
} else if (link->network->bridge) {
if (ordered_set_contains(link->manager->request_queue, &req_mac))
return false;
return -EALREADY; /* indicate to cancel the request. */
}
+ /* Do not check link->set_flags_messages here, as it is ok even if link->flags is outdated,
+ * and checking the counter causes a deadlock. */
+ if (link->network->bond && FLAGS_SET(link->flags, IFF_UP)) {
+ /* link must be down when joining to bond master. */
+ r = link_down_now(link);
+ if (r < 0)
+ return r;
+ }
+
req->userdata = UINT32_TO_PTR(m);
break;
}
assert(link->manager->rtnl);
assert(req);
+ /* The log message is checked in the test. Please also update test_bond_active_slave() in
+ * test/test-network/systemd-networkd-tests.py. when the log message below is modified. */
log_link_debug(link, "Bringing link %s", up_or_down(up));
r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex);
/* Helper struct for naming simplicity and reusability */
static const struct {
- const char *dot_directory_name;
- const char *directory_name;
+ const char *full_identifier;
const char *short_identifier;
const char *short_identifier_plural;
+ const char *blurb;
+ const char *dot_directory_name;
+ const char *directory_name;
const char *level_env;
const char *scope_env;
const char *name_env;
unsigned long default_mount_flags;
} image_class_info[_IMAGE_CLASS_MAX] = {
[IMAGE_SYSEXT] = {
- .dot_directory_name = ".systemd-sysext",
- .directory_name = "systemd-sysext",
+ .full_identifier = "systemd-sysext",
.short_identifier = "sysext",
.short_identifier_plural = "extensions",
+ .blurb = "Merge system extension images into /usr/ and /opt/.",
+ .dot_directory_name = ".systemd-sysext",
.level_env = "SYSEXT_LEVEL",
.scope_env = "SYSEXT_SCOPE",
.name_env = "SYSTEMD_SYSEXT_HIERARCHIES",
.default_mount_flags = MS_RDONLY|MS_NODEV,
},
[IMAGE_CONFEXT] = {
- .dot_directory_name = ".systemd-confext",
- .directory_name = "systemd-confext",
+ .full_identifier = "systemd-confext",
.short_identifier = "confext",
.short_identifier_plural = "confexts",
+ .blurb = "Merge configuration extension images into /etc/.",
+ .dot_directory_name = ".systemd-confext",
.level_env = "CONFEXT_LEVEL",
.scope_env = "CONFEXT_SCOPE",
.name_env = "SYSTEMD_CONFEXT_HIERARCHIES",
_cleanup_free_ char *link = NULL;
int r;
- r = terminal_urlify_man("systemd-sysext", "8", &link);
+ r = terminal_urlify_man(image_class_info[arg_image_class].full_identifier, "8", &link);
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] COMMAND\n"
- "\n%5$sMerge extension images into /usr/ and /opt/ hierarchies for\n"
- " sysext and into the /etc/ hierarchy for confext.%6$s\n"
+ "\n%5$s%7$s%6$s\n"
+ "\n%3$sCommands:%4$s\n"
" status Show current merge status (default)\n"
" merge Merge extensions into relevant hierarchies\n"
" unmerge Unmerge extensions from relevant hierarchies\n"
ansi_underline(),
ansi_normal(),
ansi_highlight(),
- ansi_normal());
+ ansi_normal(),
+ image_class_info[arg_image_class].blurb);
return 0;
}
service_state_to_string(service->state),
service_result_to_string(service->result));
- if (service->state == SERVICE_FAILED && service->main_exec_status.status == EXIT_CGROUP) {
+ if (service->state == SERVICE_FAILED &&
+ (service->main_exec_status.status == EXIT_CGROUP || service->result == SERVICE_FAILURE_RESOURCES)) {
const char *ci = ci_environment();
/* On a general purpose system we may fail to start the service for reasons which are
}
TEST(lock_personality) {
- unsigned long current;
+ unsigned long current_opinionated;
pid_t pid;
if (!is_seccomp_available()) {
return;
}
- assert_se(opinionated_personality(¤t) >= 0);
- /* On ppc64le sanitizers disable ASLR (i.e. by setting ADDR_NO_RANDOMIZE),
- * which opinionated_personality() doesn't return. Let's tweak the current
- * personality ourselves in such cases.
- * See: https://github.com/llvm/llvm-project/commit/78f7a6eaa601bfdd6ae70ffd3da2254c21ff77f9
- */
- if (FLAGS_SET(safe_personality(PERSONALITY_INVALID), ADDR_NO_RANDOMIZE))
- current |= ADDR_NO_RANDOMIZE;
+ assert_se(opinionated_personality(¤t_opinionated) >= 0);
- log_info("current personality=0x%lX", current);
+ log_info("current personality=0x%lX", (unsigned long) safe_personality(PERSONALITY_INVALID));
+ log_info("current opinionated personality=0x%lX", current_opinionated);
pid = fork();
assert_se(pid >= 0);
if (pid == 0) {
- assert_se(seccomp_lock_personality(current) >= 0);
+ unsigned long current;
- assert_se((unsigned long) safe_personality(current) == current);
+ assert_se(seccomp_lock_personality(current_opinionated) >= 0);
+
+ current = safe_personality(current_opinionated);
+ assert_se((current & OPINIONATED_PERSONALITY_MASK) == current_opinionated);
/* Note, we also test that safe_personality() works correctly, by checking whether errno is properly
* set, in addition to the return value */
assert_se(safe_personality(PER_LINUX_32BIT) == -EPERM);
assert_se(safe_personality(PER_SVR4) == -EPERM);
assert_se(safe_personality(PER_BSD) == -EPERM);
- assert_se(safe_personality(current == PER_LINUX ? PER_LINUX32 : PER_LINUX) == -EPERM);
+ assert_se(safe_personality(current_opinionated == PER_LINUX ? PER_LINUX32 : PER_LINUX) == -EPERM);
assert_se(safe_personality(PER_LINUX32_3GB) == -EPERM);
assert_se(safe_personality(PER_UW7) == -EPERM);
assert_se(safe_personality(0x42) == -EPERM);
assert_se(safe_personality(PERSONALITY_INVALID) == -EPERM); /* maybe remove this later */
- assert_se((unsigned long) personality(current) == current);
+ current = safe_personality(current_opinionated);
+ assert_se((current & OPINIONATED_PERSONALITY_MASK) == current_opinionated);
_exit(EXIT_SUCCESS);
}
def networkctl_reconfigure(*links):
networkctl('reconfigure', *links)
-def networkctl_reload(sleep_time=1):
+def networkctl_reload():
networkctl('reload')
- # 'networkctl reload' asynchronously reconfigure links.
- # Hence, we need to wait for a short time for link to be in configuring state.
- if sleep_time > 0:
- time.sleep(sleep_time)
def resolvectl(*args):
return check_output(*(resolvectl_cmd + list(args)), env=env)
print(output)
self.assertIn('active_slave dummy98', output)
+ # test case for issue #31165.
+ since = datetime.datetime.now()
+ networkctl_reconfigure('dummy98')
+ self.wait_online(['dummy98:enslaved', 'bond199:degraded'])
+ self.assertNotIn('dummy98: Bringing link down', read_networkd_log(since=since))
+
def test_bond_primary_slave(self):
copy_network_unit('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
start_networkd()
EDITOR='cp' script -ec 'networkctl edit @test2 --drop-in test2.conf' /dev/null
cmp "+4" "/etc/systemd/network/${NETWORK_NAME}.d/test2.conf"
-sleep 1
(! EDITOR='true' script -ec 'networkctl edit @test2 --runtime --drop-in test2.conf' /dev/null)
ip_link="$(ip link show test2)"
link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
+# Same thing, but with custom tty using systemd.default_debug_tty
+: "debug-shell: regular + systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
+CMDLINE="$CMDLINE systemd.default_debug_tty=/dev/tty666 systemd.debug_shell=yes"
+SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
+link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
+grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
+
# Now override the default target via systemd.unit=
: "debug-shell: regular + systemd.unit="
CMDLINE="$CMDLINE systemd.unit=my-fancy.target"