Features:
+* if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it
+
* build short web pages out of each catalog entry, build them along with man
pages, and include hyperlinks to them in the journal output
* nspawn: support time namespaces
+* add ConditionSecurity=tpm2
+
* pid1: Move to tracking of main pid/control pid of units per pidfd
* pid1: support new clone3() fork-into-cgroup feature
6. If this boot also fails, on the next boot the boot loader will see the
tag `+0-3`, i.e. the counter reached zero. At this point the entry will be
- considered "bad", and ordered to the end of the list of entries. The next
- newest boot entry is now tried, i.e. the system automatically reverted back
- to an earlier version.
+ considered "bad", and ordered to the beginning of the list of entries. The
+ next newest boot entry is now tried, i.e. the system automatically reverted
+ back to an earlier version.
The above describes the walkthrough when the selected boot entry continuously
fails. Let's have a look at an alternative ending to this walkthrough. In this
</varlistentry>
<varlistentry>
- <term><option>tmp</option></term>
+ <term><option>tmp=</option></term>
- <listitem><para>The encrypted block device will be prepared
- for using it as <filename>/tmp</filename>; it will be
- formatted using
- <citerefentry project='man-pages'><refentrytitle>mke2fs</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
- This option implies <option>plain</option>.</para>
+ <listitem><para>The encrypted block device will be prepared for using it as
+ <filename>/tmp/</filename>; it will be formatted using <citerefentry
+ project='man-pages'><refentrytitle>mkfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>. Takes
+ a file system type as argument, such as <literal>ext4</literal>, <literal>xfs</literal> or
+ <literal>btrfs</literal>. If no argument is specified defaults to <literal>ext4</literal>. This
+ option implies <option>plain</option>.</para>
- <para>WARNING: Using the <option>tmp</option> option will
- destroy the contents of the named partition during every boot,
- so make sure the underlying block device is specified
- correctly.</para></listitem>
+ <para>WARNING: Using the <option>tmp</option> option will destroy the contents of the named partition
+ during every boot, so make sure the underlying block device is specified correctly.</para></listitem>
</varlistentry>
<varlistentry>
considered 'good' from then on.</para>
<para>The boot menu takes the 'tries left' counter into account when sorting the menu entries: entries in 'bad'
- state are ordered at the end of the list, and entries in 'good' or 'indeterminate' at the beginning. The user can
+ state are ordered at the beginning of the list, and entries in 'good' or 'indeterminate' at the end. The user can
freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry to boot is
- automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the top item of
- the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or
+ automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the bottom
+ item of the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or
'indeterminate' entries left.</para>
<para>The <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry> kernel
<varlistentry>
<term><varname>KillMode=</varname></term>
- <listitem><para>Specifies how processes of this unit shall be
- killed. One of
- <option>control-group</option>,
- <option>process</option>,
- <option>mixed</option>,
+ <listitem><para>Specifies how processes of this unit shall be killed. One of
+ <option>control-group</option>, <option>mixed</option>, <option>process</option>,
<option>none</option>.</para>
- <para>If set to <option>control-group</option>, all remaining
- processes in the control group of this unit will be killed on
- unit stop (for services: after the stop command is executed,
- as configured with <varname>ExecStop=</varname>). If set to
- <option>process</option>, only the main process itself is
- killed. If set to <option>mixed</option>, the
- <constant>SIGTERM</constant> signal (see below) is sent to the
- main process while the subsequent <constant>SIGKILL</constant>
- signal (see below) is sent to all remaining processes of the
- unit's control group. If set to <option>none</option>, no
- process is killed. In this case, only the stop command will be
- executed on unit stop, but no process will be killed otherwise.
- Processes remaining alive after stop are left in their control
- group and the control group continues to exist after stop
- unless it is empty.</para>
+ <para>If set to <option>control-group</option>, all remaining processes in the control group of this
+ unit will be killed on unit stop (for services: after the stop command is executed, as configured
+ with <varname>ExecStop=</varname>). If set to <option>mixed</option>, the
+ <constant>SIGTERM</constant> signal (see below) is sent to the main process while the subsequent
+ <constant>SIGKILL</constant> signal (see below) is sent to all remaining processes of the unit's
+ control group. If set to <option>process</option>, only the main process itself is killed (not
+ recommended!). If set to <option>none</option>, no process is killed (strongly recommended
+ against!). In this case, only the stop command will be executed on unit stop, but no process will be
+ killed otherwise. Processes remaining alive after stop are left in their control group and the
+ control group continues to exist after stop unless empty.</para>
+
+ <para>Note that it is not recommended to set <varname>KillMode=</varname> to
+ <constant>process</constant> or even <constant>none</constant>, as this allows processes to escape
+ the service manager's lifecycle and resource management, and to remain running even while their
+ service is considered stopped and is assumed to not consume any resources.</para>
<para>Processes will first be terminated via <constant>SIGTERM</constant> (unless the signal to send
is changed via <varname>KillSignal=</varname> or <varname>RestartKillSignal=</varname>). Optionally,
already existing public-key cryptography, for post-quantum
resistance.
Note that because this information is secret, you may want to set
- the permissions of the .netdev file to be owned by <literal>root:systemd-networkd</literal>
+ the permissions of the .netdev file to be owned by <literal>root:systemd-network</literal>
with a <literal>0640</literal> file mode.</para>
</listitem>
</varlistentry>
disable systemd-journal-gatewayd.*
disable systemd-journal-remote.*
disable systemd-journal-upload.*
-
-# Passive targets: always off by default, since they should only be pulled in
-# by dependent units.
-
-disable cryptsetup-pre.target
-disable getty-pre.target
-disable local-fs-pre.target
-disable network.target
-disable network-pre.target
-disable nss-lookup.target
-disable nss-user-lookup.target
-disable remote-fs-pre.target
-disable rpcbind.target
-disable time-set.target
-disable time-sync.target
enable systemd-tmpfiles-setup.service
enable systemd-tmpfiles-clean.timer
-
-# Passive targets: always off by default, since they should only be pulled in
-# by dependent units.
-
-disable graphical-session-pre.target
-disable graphical-session.target
for (c = 0;; c++) {
if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) {
- /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */
- if (errno == EINVAL)
- return -ENOPROTOOPT;
+ /* On old kernels if quota is not enabled, we get EINVAL. On newer kernels we get
+ * ENOTCONN. Let's always convert this to ENOTCONN to make this recognizable
+ * everywhere the same way. */
+
+ if (IN_SET(errno, EINVAL, ENOTCONN))
+ return -ENOTCONN;
if (errno == EBUSY && c < 10) {
(void) btrfs_quota_scan_wait(fd);
_cleanup_free_ char *p = NULL;
_cleanup_free_ void *buf = NULL;
struct stat st;
+ usec_t begin;
uint32_t a;
ssize_t n;
return 0;
}
+ if (DEBUG_LOGGING)
+ begin = now(CLOCK_MONOTONIC);
+
fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
/* Assume that the reported size is accurate */
n = st.st_size - 4;
+ if (DEBUG_LOGGING) {
+ char ts[FORMAT_TIMESPAN_MAX];
+ usec_t end;
+
+ end = now(CLOCK_MONOTONIC);
+ if (end > begin + EFI_RETRY_DELAY)
+ log_debug("Detected slow EFI variable read access on " SD_ID128_FORMAT_STR "-%s: %s",
+ SD_ID128_FORMAT_VAL(vendor), name, format_timespan(ts, sizeof(ts), end - begin, 1));
+ }
+
/* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
* with a smaller value. */
static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
INTN r;
- /* Order entries that have no tries left to the end of the list */
+ /* Order entries that have no tries left to the beginning of the list */
if (a->tries_left != 0 && b->tries_left == 0)
- return -1;
- if (a->tries_left == 0 && b->tries_left != 0)
return 1;
+ if (a->tries_left == 0 && b->tries_left != 0)
+ return -1;
r = str_verscmp(a->id, b->id);
if (r != 0)
b->tries_left == (UINTN) -1)
return 0;
- /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
+ /* If both items have boot counting, and otherwise are identical, put the entry with more tries left last */
if (a->tries_left > b->tries_left)
- return -1;
- if (a->tries_left < b->tries_left)
return 1;
+ if (a->tries_left < b->tries_left)
+ return -1;
/* If they have the same number of tries left, then let the one win which was tried fewer times so far */
if (a->tries_done < b->tries_done)
- return -1;
- if (a->tries_done > b->tries_done)
return 1;
+ if (a->tries_done > b->tries_done)
+ return -1;
return 0;
}
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
static BUS_DEFINE_PROPERTY_GET(property_get_timeout_abort_usec, "t", Service, service_timeout_abort_usec);
+static BUS_DEFINE_PROPERTY_GET(property_get_watchdog_usec, "t", Service, service_get_watchdog_usec);
static int property_get_exit_status_set(
sd_bus *bus,
SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutAbortUSec", "t", property_get_timeout_abort_usec, 0, 0),
SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("WatchdogUSec", "t", property_get_watchdog_usec, 0, 0),
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* 😷 deprecated */
SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value");
DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
return 0;
}
+int config_parse_kill_mode(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ KillMode *k = data, m;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ *k = KILL_CONTROL_GROUP;
+ return 0;
+ }
+
+ m = kill_mode_from_string(rvalue);
+ if (m < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to parse kill mode specification, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (m == KILL_NONE)
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Unit configured to use KillMode=none. "
+ "This is unsafe, as it disables systemd's process life-cycle management for the service. "
+ "Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
+ "Support for KillMode=none is deprecated and will eventually be removed.");
+
+ *k = m;
+ return 0;
+}
+
int config_parse_exec(
const char *unit,
const char *filename,
m->result = f;
unit_log_result(UNIT(m), m->result == MOUNT_SUCCESS, mount_result_to_string(m->result));
+ unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_stop);
+
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
m->exec_runtime = exec_runtime_unref(m->exec_runtime, true);
(void) mkdir_p_label(m->where, m->directory_mode);
unit_warn_if_dir_nonempty(UNIT(m), m->where);
- unit_warn_leftover_processes(UNIT(m));
+ unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_start);
m->control_command_id = MOUNT_EXEC_MOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_MOUNT;
s->watchdog_timestamp = DUAL_TIMESTAMP_NULL;
}
-static usec_t service_get_watchdog_usec(Service *s) {
- assert(s);
-
- if (s->watchdog_override_enable)
- return s->watchdog_override_usec;
-
- return s->watchdog_original_usec;
-}
-
static void service_start_watchdog(Service *s) {
usec_t watchdog_usec;
int r;
}
}
- fs = new0(ServiceFDStore, 1);
+ fs = new(ServiceFDStore, 1);
if (!fs)
return -ENOMEM;
- fs->fd = fd;
- fs->service = s;
- fs->do_poll = do_poll;
- fs->fdname = strdup(name ?: "stored");
+ *fs = (ServiceFDStore) {
+ .fd = fd,
+ .service = s,
+ .do_poll = do_poll,
+ .fdname = strdup(name ?: "stored"),
+ };
+
if (!fs->fdname) {
free(fs);
return -ENOMEM;
unit_log_failure(UNIT(s), service_result_to_string(s->result));
end_state = SERVICE_FAILED;
}
+ unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
if (!allow_restart)
log_unit_debug(UNIT(s), "Service restart not allowed.");
assert(s);
/* KillMode=mixed and control group are used to indicate that all process should be killed off.
- * SendSIGKILL is used for services that require a clean shutdown. These are typically database
- * service where a SigKilled process would result in a lengthy recovery and who's shutdown or
- * startup time is quite variable (so Timeout settings aren't of use).
+ * SendSIGKILL= is used for services that require a clean shutdown. These are typically database
+ * service where a SigKilled process would result in a lengthy recovery and who's shutdown or startup
+ * time is quite variable (so Timeout settings aren't of use).
*
* Here we take these two factors and refuse to start a service if there are existing processes
* within a control group. Databases, while generally having some protection against multiple
- * instances running, lets not stress the rigor of these. Also ExecStartPre parts of the service
+ * instances running, lets not stress the rigor of these. Also ExecStartPre= parts of the service
* aren't as rigoriously written to protect aganst against multiple use. */
- if (unit_warn_leftover_processes(UNIT(s)) &&
+
+ if (unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start) > 0 &&
IN_SET(s->kill_context.kill_mode, KILL_MIXED, KILL_CONTROL_GROUP) &&
!s->kill_context.send_sigkill)
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(EBUSY),
return s->timeout_abort_set ? s->timeout_abort_usec : s->timeout_stop_usec;
}
+static inline usec_t service_get_watchdog_usec(Service *s) {
+ assert(s);
+ return s->watchdog_override_enable ? s->watchdog_override_usec : s->watchdog_original_usec;
+}
+
extern const UnitVTable service_vtable;
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net);
else
unit_log_failure(UNIT(s), socket_result_to_string(s->result));
+ unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
+
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
socket_unwatch_control_pid(s);
- unit_warn_leftover_processes(UNIT(s));
+ unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start);
s->control_command_id = SOCKET_EXEC_START_PRE;
s->control_command = s->exec_command[SOCKET_EXEC_START_PRE];
s->result = f;
unit_log_result(UNIT(s), s->result == SWAP_SUCCESS, swap_result_to_string(s->result));
+ unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
assert(s);
- unit_warn_leftover_processes(UNIT(s));
+ unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start);
s->control_command_id = SWAP_EXEC_ACTIVATE;
s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE;
if (!pid_set)
return -ENOMEM;
- cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
- SIGHUP,
- CGROUP_IGNORE_SELF,
- pid_set,
- NULL, NULL);
+ (void) cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
+ SIGHUP,
+ CGROUP_IGNORE_SELF,
+ pid_set,
+ NULL, NULL);
}
}
}
return 0;
}
-static int log_leftover(pid_t pid, int sig, void *userdata) {
+static bool ignore_leftover_process(const char *comm) {
+ return comm && comm[0] == '('; /* Most likely our own helper process (PAM?), ignore */
+}
+
+int unit_log_leftover_process_start(pid_t pid, int sig, void *userdata) {
_cleanup_free_ char *comm = NULL;
(void) get_process_comm(pid, &comm);
- if (comm && comm[0] == '(') /* Most likely our own helper process (PAM?), ignore */
+ if (ignore_leftover_process(comm))
return 0;
+ /* During start we print a warning */
+
log_unit_warning(userdata,
"Found left-over process " PID_FMT " (%s) in control group while starting unit. Ignoring.\n"
"This usually indicates unclean termination of a previous run, or service implementation deficiencies.",
return 1;
}
-int unit_warn_leftover_processes(Unit *u) {
+int unit_log_leftover_process_stop(pid_t pid, int sig, void *userdata) {
+ _cleanup_free_ char *comm = NULL;
+
+ (void) get_process_comm(pid, &comm);
+
+ if (ignore_leftover_process(comm))
+ return 0;
+
+ /* During stop we only print an informational message */
+
+ log_unit_info(userdata,
+ "Unit process " PID_FMT " (%s) remains running after unit stopped.",
+ pid, strna(comm));
+
+ return 1;
+}
+
+int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func) {
assert(u);
(void) unit_pick_cgroup_path(u);
if (!u->cgroup_path)
return 0;
- return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u);
+ return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_func, u);
}
bool unit_needs_console(Unit *u) {
int unit_prepare_exec(Unit *u);
-int unit_warn_leftover_processes(Unit *u);
+int unit_log_leftover_process_start(pid_t pid, int sig, void *userdata);
+int unit_log_leftover_process_stop(pid_t pid, int sig, void *userdata);
+int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func);
bool unit_needs_console(Unit *u);
_cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
*keydev_mount = NULL, *keyfile_timeout_value = NULL,
- *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL;
+ *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL,
+ *tmp_fstype = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *dmname;
- bool noauto, nofail, tmp, swap, netdev, attach_in_initrd;
- int r, detached_header, keyfile_can_timeout;
+ bool noauto, nofail, swap, netdev, attach_in_initrd;
+ int r, detached_header, keyfile_can_timeout, tmp;
assert(name);
assert(device);
noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
- tmp = fstab_test_option(options, "tmp\0");
swap = fstab_test_option(options, "swap\0");
netdev = fstab_test_option(options, "_netdev\0");
attach_in_initrd = fstab_test_option(options, "x-initrd.attach\0");
if (detached_header < 0)
return log_error_errno(detached_header, "Failed to parse header= option value: %m");
+ tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL);
+ if (tmp < 0)
+ return log_error_errno(tmp, "Failed to parse tmp= option value: %m");
+
if (tmp && swap)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.",
if (r < 0)
return r;
- if (tmp)
+ if (tmp) {
+ _cleanup_free_ char *tmp_fstype_escaped = NULL;
+
+ if (tmp_fstype) {
+ tmp_fstype_escaped = specifier_escape(tmp_fstype);
+ if (!tmp_fstype_escaped)
+ return log_oom();
+ }
+
fprintf(f,
- "ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs ext2 '/dev/mapper/%s'\n",
- name_escaped);
+ "ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs '%s' '/dev/mapper/%s'\n",
+ tmp_fstype_escaped ?: "ext4", name_escaped);
+ }
if (swap)
fprintf(f,
loud
quiet
keyscript=
- tmp= (the version without argument is supported)
initramfs
*/
} else if (STR_IN_SET(option, "tcrypt-veracrypt", "veracrypt")) {
arg_type = CRYPT_TCRYPT;
arg_tcrypt_veracrypt = true;
- } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
+ } else if (STR_IN_SET(option, "plain", "swap", "tmp") ||
+ startswith(option, "tmp="))
arg_type = CRYPT_PLAIN;
else if ((val = startswith(option, "timeout="))) {
return state;
}
-static bool dns_query_candidate_is_routable(DnsQueryCandidate *c, uint16_t type) {
- int family;
-
- assert(c);
-
- /* Checks whether the specified RR type matches an address family that is routable on the link(s) the scope of
- * this candidate belongs to. Specifically, whether there's a routable IPv4 address on it if we query an A RR,
- * or a routable IPv6 address if we query an AAAA RR. */
-
- if (!c->query->suppress_unroutable_family)
- return true;
-
- if (c->scope->protocol != DNS_PROTOCOL_DNS)
- return true;
-
- family = dns_type_to_af(type);
- if (family < 0)
- return true;
-
- if (c->scope->link)
- return link_relevant(c->scope->link, family, false);
- else
- return manager_routable(c->scope->manager, family);
-}
-
static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
DnsQuestion *question;
DnsResourceKey *key;
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
DnsResourceKey *qkey;
- if (!dns_query_candidate_is_routable(c, key->type))
- continue;
-
if (c->search_domain) {
r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
if (r < 0)
PTYForward *forward;
sd_bus_slot *match;
- /* The exit data of the unit */
+ /* Current state of the unit */
char *active_state;
+ bool has_job;
+
+ /* The exit data of the unit */
uint64_t inactive_exit_usec;
uint64_t inactive_enter_usec;
char *result;
assert(c);
if (c->match)
- done = STRPTR_IN_SET(c->active_state, "inactive", "failed");
+ done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
else
done = true;
sd_event_exit(c->event, EXIT_SUCCESS);
}
+static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+ bool *b = userdata;
+ const char *job;
+ uint32_t id;
+ int r;
+
+ r = sd_bus_message_read(m, "(uo)", &id, &job);
+ if (r < 0)
+ return r;
+
+ *b = id != 0 || !streq(job, "/");
+ return 0;
+}
+
static int run_context_update(RunContext *c, const char *path) {
static const struct bus_properties_map map[] = {
- { "ActiveState", "s", NULL, offsetof(RunContext, active_state) },
- { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_exit_usec) },
- { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_enter_usec) },
- { "Result", "s", NULL, offsetof(RunContext, result) },
- { "ExecMainCode", "i", NULL, offsetof(RunContext, exit_code) },
- { "ExecMainStatus", "i", NULL, offsetof(RunContext, exit_status) },
- { "CPUUsageNSec", "t", NULL, offsetof(RunContext, cpu_usage_nsec) },
- { "IPIngressBytes", "t", NULL, offsetof(RunContext, ip_ingress_bytes) },
- { "IPEgressBytes", "t", NULL, offsetof(RunContext, ip_egress_bytes) },
- { "IOReadBytes", "t", NULL, offsetof(RunContext, io_read_bytes) },
- { "IOWriteBytes", "t", NULL, offsetof(RunContext, io_write_bytes) },
+ { "ActiveState", "s", NULL, offsetof(RunContext, active_state) },
+ { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_exit_usec) },
+ { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(RunContext, inactive_enter_usec) },
+ { "Result", "s", NULL, offsetof(RunContext, result) },
+ { "ExecMainCode", "i", NULL, offsetof(RunContext, exit_code) },
+ { "ExecMainStatus", "i", NULL, offsetof(RunContext, exit_status) },
+ { "CPUUsageNSec", "t", NULL, offsetof(RunContext, cpu_usage_nsec) },
+ { "IPIngressBytes", "t", NULL, offsetof(RunContext, ip_ingress_bytes) },
+ { "IPEgressBytes", "t", NULL, offsetof(RunContext, ip_egress_bytes) },
+ { "IOReadBytes", "t", NULL, offsetof(RunContext, io_read_bytes) },
+ { "IOWriteBytes", "t", NULL, offsetof(RunContext, io_write_bytes) },
+ { "Job", "(uo)", map_job, offsetof(RunContext, has_job) },
{}
};
}
int fstab_filter_options(const char *opts, const char *names,
- const char **namefound, char **value, char **filtered) {
+ const char **ret_namefound, char **ret_value, char **ret_filtered) {
const char *name, *n = NULL, *x;
_cleanup_strv_free_ char **stor = NULL;
_cleanup_free_ char *v = NULL, **strv = NULL;
/* If !value and !filtered, this function is not allowed to fail. */
- if (!filtered) {
+ if (!ret_filtered) {
const char *word, *state;
size_t l;
x = word + strlen(name);
if (IN_SET(*x, '\0', '=', ',')) {
n = name;
- if (value) {
+ if (ret_value) {
free(v);
if (IN_SET(*x, '\0', ','))
v = NULL;
found:
/* Keep the last occurrence found */
n = name;
- if (value) {
+ if (ret_value) {
free(v);
if (*x == '\0')
v = NULL;
}
answer:
- if (namefound)
- *namefound = n;
- if (filtered) {
+ if (ret_namefound)
+ *ret_namefound = n;
+ if (ret_filtered) {
char *f;
f = strv_join(strv, ",");
if (!f)
return -ENOMEM;
- *filtered = f;
+ *ret_filtered = f;
}
- if (value)
- *value = TAKE_PTR(v);
+ if (ret_value)
+ *ret_value = TAKE_PTR(v);
return !!n;
}
{ GPT_SRV, "srv" },
{ GPT_VAR, "var" },
{ GPT_TMP, "tmp" },
- { GPT_LINUX_GENERIC, "linux-generic", },
+ { GPT_USER_HOME, "user-home" },
+ { GPT_LINUX_GENERIC, "linux-generic" },
{}
};
if (!fpath)
return log_oom();
- service = new0(SysvStub, 1);
+ log_warning("SysV service '%s' lacks a native systemd unit file. "
+ "Automatically generating a unit file for compatibility. "
+ "Please update package to include a native systemd unit file, in order to make it more safe and robust.", fpath);
+
+ service = new(SysvStub, 1);
if (!service)
return log_oom();
- service->sysv_start_priority = -1;
- service->name = TAKE_PTR(name);
- service->path = TAKE_PTR(fpath);
+ *service = (SysvStub) {
+ .sysv_start_priority = -1,
+ .name = TAKE_PTR(name),
+ .path = TAKE_PTR(fpath),
+ };
r = hashmap_put(all_services, service->name, service);
if (r < 0)
return log_oom();
- service = NULL;
+ TAKE_PTR(service);
}
}
static int write_one_file(Item *i, const char *path) {
_cleanup_close_ int fd = -1, dir_fd = -1;
char *bn;
- int flags, r;
+ int r;
assert(i);
assert(path);
bn = basename(path);
- flags = O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY;
-
/* Follows symlinks */
- fd = openat(dir_fd, bn, i->append_or_force ? flags|O_APPEND : flags, i->mode);
+ fd = openat(dir_fd, bn,
+ O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY|(i->append_or_force ? O_APPEND : 0),
+ i->mode);
if (fd < 0) {
if (errno == ENOENT) {
log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
return 0;
}
+
+ if (i->allow_failure)
+ return log_debug_errno(errno, "Failed to open file \"%s\", ignoring: %m", path);
+
return log_error_errno(errno, "Failed to open file \"%s\": %m", path);
}
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
else if (r == -EROFS)
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
- else if (r == -ENOPROTOOPT)
+ else if (r == -ENOTCONN)
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
else if (r < 0)
q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
# SPDX-License-Identifier: LGPL-2.1+
+# The 'optimization' option was introduced in meson 0.48.0, so let's keep
+# the code compatible with older versions as well
+if meson.version().version_compare('>=0.48.0')
+ optimization = '--optimization=@0@'.format(get_option('optimization'))
+else
+ optimization = ''
+endif
+
sanitize_address_undefined = custom_target(
'sanitize-address-undefined-fuzzers',
output : 'sanitize-address-undefined-fuzzers',
project_source_root,
'@OUTPUT@',
'fuzzers',
- '-Db_lundef=false -Db_sanitize=address,undefined ' +
- '--optimization=@0@'.format(get_option('optimization')),
+ '-Db_lundef=false -Db_sanitize=address,undefined @0@'.format(optimization),
' '.join(cc.cmd_array()),
cxx_cmd])
if [ -n "${LOOPDEV}" ]; then
ddebug "losetup -d $LOOPDEV"
losetup -d "${LOOPDEV}"
+ unset LOOPDEV
fi
}
rm -f "$IMAGE_PRIVATE" "$IMAGE_PUBLIC"
# Create the blank file to use as a root filesystem
- truncate -s "${_size}M" "$IMAGE_PRIVATE"
- ln -vs "$(realpath $IMAGE_PRIVATE)" "$IMAGE_PUBLIC"
+ truncate -s "${_size}M" "$IMAGE_PUBLIC"
LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC")
[ -b "$LOOPDEV" ] || return 1
if [ -e "$IMAGE_PRIVATE" ]; then
echo "Reusing existing image $IMAGE_PRIVATE → $(realpath $IMAGE_PRIVATE)"
mount_initdir
- elif [ -e "$IMAGE_PUBLIC" ]; then
+ else
+ if [ ! -e "$IMAGE_PUBLIC" ]; then
+ # Create the backing public image, but then completely unmount
+ # it and drop the loopback device responsible for it, since we're
+ # going to symlink/copy the image and mount it again from
+ # elsewhere.
+ test_create_image
+ test_setup_cleanup
+ umount_loopback
+ cleanup_loopdev
+ fi
+
echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath $IMAGE_PUBLIC)"
if [ ${TEST_PARALLELIZE} -ne 0 ]; then
cp -v "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
else
ln -sv "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
fi
+
mount_initdir
- else
- test_create_image
fi
setup_nspawn_root
su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@"
}
-runas testuser systemd-run --user --unit=test-private-users \
+runas testuser systemd-run --wait --user --unit=test-private-users \
-p PrivateUsers=yes -P echo hello
-runas testuser systemd-run --user --unit=test-private-tmp-innerfile \
+runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \
-p PrivateUsers=yes -p PrivateTmp=yes \
-P touch /tmp/innerfile.txt
# File should not exist outside the job's tmp directory.
touch /tmp/outerfile.txt
# File should not appear in unit's private tmp.
-runas testuser systemd-run --user --unit=test-private-tmp-outerfile \
+runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \
-p PrivateUsers=yes -p PrivateTmp=yes \
-P test ! -e /tmp/outerfile.txt
# Confirm that creating a file in home works
-runas testuser systemd-run --user --unit=test-unprotected-home \
+runas testuser systemd-run --wait --user --unit=test-unprotected-home \
-P touch /home/testuser/works.txt
test -e /home/testuser/works.txt
# Confirm that creating a file in home is blocked under read-only
-runas testuser systemd-run --user --unit=test-protect-home-read-only \
+runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \
-p PrivateUsers=yes -p ProtectHome=read-only \
-P bash -c '
test -e /home/testuser/works.txt
test ! -e /home/testuser/blocked.txt
# Check that tmpfs hides the whole directory
-runas testuser systemd-run --user --unit=test-protect-home-tmpfs \
+runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \
-p PrivateUsers=yes -p ProtectHome=tmpfs \
-P test ! -e /home/testuser
# Confirm that home, /root, and /run/user are inaccessible under "yes"
-runas testuser systemd-run --user --unit=test-protect-home-yes \
+runas testuser systemd-run --wait --user --unit=test-protect-home-yes \
-p PrivateUsers=yes -p ProtectHome=yes \
-P bash -c '
test "$(stat -c %a /home)" = "0"
# namespace (no CAP_SETGID in the parent namespace to write the additional
# mapping of the user supplied group and thus cannot change groups to an
# unmapped group ID)
-! runas testuser systemd-run --user --unit=test-group-fail \
+! runas testuser systemd-run --wait --user --unit=test-group-fail \
-p PrivateUsers=yes -p Group=daemon \
-P true
# These changes are automatically applied on future re-boots.
d /var/lib/systemd/pstore 0755 root root 14d
-#w /sys/module/printk/parameters/always_kmsg_dump - - - - Y
-w /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y
+#w- /sys/module/printk/parameters/always_kmsg_dump - - - - Y
+w- /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y
cd $REPO_ROOT
export PATH="$HOME/.local/bin/:$PATH"
-# We use a subset of https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks instead of "undefined"
-# because our fuzzers crash with "float-cast-overflow":
-# https://github.com/systemd/systemd/pull/12812#issuecomment-502780455
# TODO: figure out what to do about unsigned-integer-overflow: https://github.com/google/oss-fuzz/issues/910
-export SANITIZER="address -fsanitize=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,unsigned-integer-overflow,vla-bound,vptr -fno-sanitize-recover=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr"
+export SANITIZER="address -fsanitize=undefined,unsigned-integer-overflow"
tools/oss-fuzz.sh
FUZZING_TYPE=${1:-regression}
TTYPath=/dev/console
TTYReset=yes
TTYVHangup=yes
+m4_ifdef(`ENABLE_LOGIND',,
KillMode=process
+)m4_dnl
IgnoreSIGPIPE=no
SendSIGHUP=yes
TTYPath=/dev/pts/%I
TTYReset=yes
TTYVHangup=yes
+m4_ifdef(`ENABLE_LOGIND',,
KillMode=process
+)m4_dnl
IgnoreSIGPIPE=no
SendSIGHUP=yes
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
+m4_ifdef(`ENABLE_LOGIND',,
KillMode=process
+)m4_dnl
IgnoreSIGPIPE=no
SendSIGHUP=yes
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
+m4_ifdef(`ENABLE_LOGIND',,
KillMode=process
+)m4_dnl
IgnoreSIGPIPE=no
SendSIGHUP=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
-PrivateNetwork=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_ALG
RestrictNamespaces=mnt
RestrictRealtime=yes