<varlistentry>
<term>
<command>delete</command>
+ <replaceable>DEVICE…</replaceable>
</term>
<listitem><para>Deletes virtual netdevs. Takes interface name or index number.</para></listitem>
</varlistentry>
<varlistentry>
<term>
<command>up</command>
+ <replaceable>DEVICE…</replaceable>
</term>
<listitem><para>Bring devices up. Takes interface name or index number.</para></listitem>
</varlistentry>
<varlistentry>
<term>
<command>down</command>
+ <replaceable>DEVICE…</replaceable>
</term>
<listitem><para>Bring devices down. Takes interface name or index number.</para></listitem>
</varlistentry>
<varlistentry>
<term>
<command>renew</command>
+ <replaceable>DEVICE…</replaceable>
</term>
<listitem><para>Renew dynamic configurations e.g. addresses received from DHCP server.
Takes interface name or index number.</para></listitem>
<varlistentry>
<term>
<command>forcerenew</command>
+ <replaceable>DEVICE…</replaceable>
</term>
<listitem><para>Send a FORCERENEW message to all connected clients, triggering DHCP reconfiguration.
Takes interface name or index number.</para></listitem>
<varlistentry>
<term>
<command>reconfigure</command>
+ <replaceable>DEVICE…</replaceable>
</term>
<listitem><para>Reconfigure network interfaces. Takes interface name or index number.</para></listitem>
</varlistentry>
<!--property RestartUSec is not documented!-->
- <!--property TimeoutStartUSec is not documented!-->
-
- <!--property TimeoutStopUSec is not documented!-->
-
- <!--property TimeoutAbortUSec is not documented!-->
-
<!--property TimeoutStartFailureMode is not documented!-->
<!--property TimeoutStopFailureMode is not documented!-->
<para>Most properties of the Service interface map directly to the corresponding settings in service
unit files. For the sake of brevity, here's a list of all exceptions only:</para>
+ <para><varname>TimeoutStartUSec</varname>, <varname>TimeoutStopUSec</varname> and
+ <varname>TimeoutAbortUSec</varname> contain the start, stop and abort timeouts, in microseconds. Note
+ the slight difference in naming when compared to the matching unit file settings (see
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>7</manvolnum></citerefentry>):
+ these bus properties strictly use microseconds (and thus are suffixed <varname>…USec</varname>) while
+ the unit file settings default to a time unit of seconds (and thus are suffixed
+ <varname>…Sec</varname>), unless a different unit is explicitly specified. This reflects that fact that
+ internally the service manager deals in microsecond units only, and the bus properties are a relatively
+ low-level (binary) concept exposing this. The unit file settings on the other hand are relatively
+ high-level (string-based) concepts and thus support more user friendly time specifications which
+ default to second time units but allow other units too, if specified.</para>
+
<para><varname>WatchdogTimestamp</varname> and <varname>WatchdogTimestampMonotonic</varname> contain
<constant>CLOCK_REALTIME</constant>/<constant>CLOCK_MONOTONIC</constant> microsecond timestamps of the
last watchdog ping received from the service, or 0 if none was ever received.</para>
<!--method AttachProcesses is not documented!-->
- <!--property TimeoutStopUSec is not documented!-->
-
<!--property RuntimeMaxUSec is not documented!-->
<!--property Slice is not documented!-->
</tgroup>
</table>
- <para>For types "s" and "g" (unicode string or signature), the pointer may be
- <constant>NULL</constant>, which is equivalent to an empty string. See
+ <para>For types <literal>s</literal> and <literal>g</literal> (unicode string or signature), the pointer
+ may be <constant>NULL</constant>, which is equivalent to an empty string. For <literal>h</literal> (UNIX
+ file descriptor), the descriptor is duplicated by this call and the passed descriptor stays in possession
+ of the caller. See
<citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for the precise interpretation of those and other types.</para>
</refsect1>
should be read. See the table below for a complete list of allowed arguments and their types. Note that,
if the basic type is a pointer (e.g., <type>const char *</type> in the case of a string), the argument is
a pointer to a pointer, and also the pointer value that is written is only borrowed and the contents must
- be copied if they are to be used after the end of the messages lifetime.</para>
+ be copied if they are to be used after the end of the messages lifetime. If the type is
+ <literal>h</literal> (UNIX file descriptor), the descriptor is not duplicated by this call and the
+ returned descriptor remains in possession of the message object, and needs to be duplicated by the caller
+ in order to keep an open reference to it after the message object is freed.</para>
<para>Each argument may also be <constant>NULL</constant>, in which case the value is read and ignored.
</para>
sd_bus_message_read(m, "a{is}", 3, &i, &s, &j, &t, &k, &u);
</programlisting>
+
+ <para>Read a single file descriptor, and duplicate it in order to keep it open after the message is
+ freed.</para>
+
+ <programlisting>sd_bus_message *m;
+int fd, fd_copy;
+
+sd_bus_message_read(m, "h", &fd);
+fd_copy = fcntl(fd, FD_DUPFD_CLOEXEC, 3);</programlisting>
</refsect1>
<refsect1>
</para>
<para>
- If <parameter>p</parameter> is not <constant>NULL</constant>, it should contain
- a pointer to an appropriate object. For example, if <parameter>type</parameter>
- is <constant>'y'</constant>, the object passed in <parameter>p</parameter>
- should have type <type>uint8_t *</type>. If <parameter>type</parameter> is
- <constant>'s'</constant>, the object passed in <parameter>p</parameter> should
- have type <type>const char **</type>. Note that, if the basic type is a pointer
- (e.g., <type>const char *</type> in the case of a string), the pointer is only
- borrowed and the contents must be copied if they are to be used after the end
- of the messages lifetime. Similarly, during the lifetime of such a pointer, the
- message must not be modified. See the table below for a complete list of allowed
- types.
+ If <parameter>p</parameter> is not <constant>NULL</constant>, it should contain a pointer to an
+ appropriate object. For example, if <parameter>type</parameter> is <constant>'y'</constant>, the object
+ passed in <parameter>p</parameter> should have type <type>uint8_t *</type>. If
+ <parameter>type</parameter> is <constant>'s'</constant>, the object passed in <parameter>p</parameter>
+ should have type <type>const char **</type>. Note that, if the basic type is a pointer (e.g.,
+ <type>const char *</type> in the case of a string), the pointer is only borrowed and the contents must
+ be copied if they are to be used after the end of the messages lifetime. Similarly, during the lifetime
+ of such a pointer, the message must not be modified. If <parameter>type</parameter> is
+ <constant>'h'</constant> (UNIX file descriptor), the descriptor is not duplicated by this call and the
+ returned descriptor remains in possession of the message object, and needs to be duplicated by the caller
+ in order to keep an open reference to it after the message object is freed (for example by calling
+ <literal>fcntl(fd, FD_DUPFD_CLOEXEC, 3)</literal>). See the table below for a complete list of
+ allowed types.
</para>
<table id='format-specifiers'>
(i.e. <constant>SD_LISTEN_FDS_START</constant>), the remaining
descriptors follow at 4, 5, 6, …, if any.</para>
- <para>If a daemon receives more than one file descriptor, they
- will be passed in the same order as configured in the systemd
- socket unit file (see
- <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details). Nonetheless, it is recommended to verify the correct
- socket types before using them. To simplify this checking, the
- functions
+ <para>If a daemon receives more than one file descriptor, they will be passed in the same order as
+ configured in the systemd socket unit file (see
+ <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details) — if there's only one such file (see below). Nonetheless, it is recommended to verify the
+ correct socket types before using them. To simplify this checking, the functions
<citerefentry><refentrytitle>sd_is_fifo</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_is_socket</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_is_socket_inet</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sd_is_socket_unix</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- are provided. In order to maximize flexibility, it is recommended
- to make these checks as loose as possible without allowing
- incorrect setups. i.e. often, the actual port number a socket is
- bound to matters little for the service to work, hence it should
- not be verified. On the other hand, whether a socket is a datagram
- or stream socket matters a lot for the most common program logics
- and should be checked.</para>
+ <citerefentry><refentrytitle>sd_is_socket_unix</refentrytitle><manvolnum>3</manvolnum></citerefentry> are
+ provided. In order to maximize flexibility, it is recommended to make these checks as loose as possible
+ without allowing incorrect setups. i.e. often, the actual port number a socket is bound to matters little
+ for the service to work, hence it should not be verified. On the other hand, whether a socket is a
+ datagram or stream socket matters a lot for the most common program logics and should be checked.</para>
<para>This function call will set the FD_CLOEXEC flag for all
passed file descriptors to avoid further inheritance to children
current main process identifier as <literal>MainPID</literal> (which is runtime state), and time settings
are always exposed as properties ending in the <literal>…USec</literal> suffix even if a matching
configuration options end in <literal>…Sec</literal>, because microseconds is the normalized time unit used
- by the system and service manager.</para>
+ internally by the system and service manager.</para>
+
+ <para>For details about many of these properties, see the documentation of the D-Bus interface
+ backing these properties, see
+ <citerefentry><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>FileDescriptorStoreMax=</varname></term>
- <listitem><para>Configure how many file descriptors may be stored in the service manager for the service using
+ <listitem><para>Configure how many file descriptors may be stored in the service manager for the
+ service using
<citerefentry><refentrytitle>sd_pid_notify_with_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>'s
- <literal>FDSTORE=1</literal> messages. This is useful for implementing services that can restart after an
- explicit request or a crash without losing state. Any open sockets and other file descriptors which should not
- be closed during the restart may be stored this way. Application state can either be serialized to a file in
- <filename>/run</filename>, or better, stored in a
- <citerefentry><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry> memory file
- descriptor. Defaults to 0, i.e. no file descriptors may be stored in the service manager. All file descriptors
- passed to the service manager from a specific service are passed back to the service's main process on the next
- service restart. Any file descriptors passed to the service manager are automatically closed when
- <constant>POLLHUP</constant> or <constant>POLLERR</constant> is seen on them, or when the service is fully
- stopped and no job is queued or being executed for it. If this option is used, <varname>NotifyAccess=</varname>
- (see above) should be set to open access to the notification socket provided by systemd. If
- <varname>NotifyAccess=</varname> is not set, it will be implicitly set to
+ <literal>FDSTORE=1</literal> messages. This is useful for implementing services that can restart
+ after an explicit request or a crash without losing state. Any open sockets and other file
+ descriptors which should not be closed during the restart may be stored this way. Application state
+ can either be serialized to a file in <filename>/run</filename>, or better, stored in a
+ <citerefentry><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ memory file descriptor. Defaults to 0, i.e. no file descriptors may be stored in the service
+ manager. All file descriptors passed to the service manager from a specific service are passed back
+ to the service's main process on the next service restart (see
+ <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+ details about the precise protocol used and the order in which the file descriptors are passed). Any
+ file descriptors passed to the service manager are automatically closed when
+ <constant>POLLHUP</constant> or <constant>POLLERR</constant> is seen on them, or when the service is
+ fully stopped and no job is queued or being executed for it. If this option is used,
+ <varname>NotifyAccess=</varname> (see above) should be set to open access to the notification socket
+ provided by systemd. If <varname>NotifyAccess=</varname> is not set, it will be implicitly set to
<option>main</option>.</para></listitem>
</varlistentry>
services, as well as parallelized starting of services. See the
blog stories linked at the end for an introduction.</para>
- <para>Note that the daemon software configured for socket
- activation with socket units needs to be able to accept sockets
- from systemd, either via systemd's native socket passing interface
- (see
- <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for details) or via the traditional
- <citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>-style
- socket passing (i.e. sockets passed in via standard input and
- output, using <varname>StandardInput=socket</varname> in the
- service file).</para>
+ <para>Note that the daemon software configured for socket activation with socket units needs to be able
+ to accept sockets from systemd, either via systemd's native socket passing interface (see
+ <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+ details about the precise protocol used and the order in which the file descriptors are passed) or via
+ traditional <citerefentry
+ project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>-style
+ socket passing (i.e. sockets passed in via standard input and output, using
+ <varname>StandardInput=socket</varname> in the service file).</para>
<para>All network sockets allocated through <filename>.socket</filename> units are allocated in the host's network
namespace (see <citerefentry
<para>One can use the <command>timespan</command> command of
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
to normalise a textual time span for testing and validation purposes.</para>
+
+ <para>Internally, systemd generally operates with microsecond time granularity, while the default time
+ unit in user-configurable time spans is usually seconds (see above). This disparity becomes visible when
+ comparing the same settings in the (high-level) unit file syntax with the matching (more low-level) D-Bus
+ properties (which are what
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <command>show</command> command displays). The former typically are suffixed with <literal>…Sec</literal>
+ to indicate the default unit of seconds, the latter are typically suffixed with <literal>…USec</literal>
+ to indicate the underlying low-level time unit, even if they both encapsulate the very same
+ settings.</para>
</refsect1>
<refsect1>
local -a _networkctl_cmds
_networkctl_cmds=(
'list:List existing links'
- 'status:Show information about the specified links'
- 'lldp:Show Link Layer Discovery Protocol status'
- 'label:Show address labels'
+ 'status:Show information about the specified links'
+ 'lldp:Show Link Layer Discovery Protocol status'
+ 'label:Show address labels'
+ 'delete:Delete virtual netdevs'
+ 'up:Bring devices up'
+ 'down:Bring devices down'
+ 'renew:Renew dynamic configurations'
+ 'forcerenew:Trigger DHCP reconfiguration of all connected clients'
+ 'reconfigure:Reconfigure interfaces'
+ 'reload:Reload .network and .netdev files'
)
if (( CURRENT == 1 )); then
_describe -t commands 'networkctl command' _networkctl_cmds
local curcontext="$curcontext"
local -a _links
cmd="${${_networkctl_cmds[(r)$words[1]:*]%%:*}}"
- if [ $cmd = "status" ]; then
- _links=( "${(foa)$(networkctl list --no-legend | awk 'BEGIN{OFS=":"} {sub(/[[ \t]+/, ""); print $2,$0}' 2>/dev/null)}" )
- if [[ -n "$_links" ]]; then
- _describe -t links 'links' _links
- else
- _message "no links"
- fi
- else
- _message "no more options"
- fi
+ case $cmd in
+ (list|status|up|down|lldp|delete|renew|forcerenew|reconfigure)
+ _links=( "${(foa)$(networkctl list --no-legend | awk 'BEGIN{OFS=":"} {sub(/[[ \t]+/, ""); print $2,$0}' 2>/dev/null)}" )
+ if [[ -n "$_links" ]]; then
+ _describe -t links 'links' _links
+ else
+ _message "no links"
+ fi
+ ;;
+ *)
+ _message "no more options"
+ ;;
+ esac
fi
}
status = exit_status_from_string(argv[i]);
if (status < 0)
- return log_error_errno(r, "Invalid exit status \"%s\": %m", argv[i]);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid exit status \"%s\".", argv[i]);
assert(status >= 0 && (size_t) status < ELEMENTSOF(exit_status_mappings));
r = table_add_many(table,
#undef CAP_LAST_CAP
#define CAP_LAST_CAP CAP_BPF
#endif
+
+/* 124ea650d3072b005457faed69909221c2905a1f (5.9) */
+#ifndef CAP_CHECKPOINT_RESTORE
+#define CAP_CHECKPOINT_RESTORE 40
+
+#undef CAP_LAST_CAP
+#define CAP_LAST_CAP CAP_CHECKPOINT_RESTORE
+#endif
if (!j)
return -ENOMEM;
- if (is_dir(j, true))
- continue;
-
if (access(j, X_OK) >= 0) {
- /* Found it! */
+ _cleanup_free_ char *with_dash;
- if (ret)
- *ret = path_simplify(TAKE_PTR(j), false);
+ with_dash = strjoin(j, "/");
+ if (!with_dash)
+ return -ENOMEM;
- return 0;
+ /* If this passes, it must be a directory, and so should be skipped. */
+ if (access(with_dash, X_OK) >= 0)
+ continue;
+
+ /**
+ * We can't just `continue` inverting this case, since we need to update last_error.
+ */
+ if (errno == ENOTDIR) {
+ /* Found it! */
+ if (ret)
+ *ret = path_simplify(TAKE_PTR(j), false);
+
+ return 0;
+ }
}
/* PATH entries which we don't have access to are ignored, as per tradition. */
#include <string.h>
#include <sys/types.h>
+#include "time-util.h"
+
struct siphash {
uint64_t v0;
uint64_t v1;
siphash24_compress(&i, sizeof i, state);
}
+static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) {
+ siphash24_compress(&in, sizeof in, state);
+}
+
static inline void siphash24_compress_string(const char *in, struct siphash *state) {
if (!in)
return;
/* Possibly rebuild the fragment map to catch new units */
r = unit_file_build_name_map(&u->manager->lookup_paths,
- &u->manager->unit_cache_mtime,
+ &u->manager->unit_cache_timestamp_hash,
&u->manager->unit_id_map,
&u->manager->unit_name_map,
&u->manager->unit_path_cache);
#include "dbus-manager.h"
#include "dbus.h"
#include "def.h"
+#include "dev-setup.h"
#include "efi-random.h"
#include "efivars.h"
#include "emergency-action.h"
#include "loopback-setup.h"
#include "machine-id-setup.h"
#include "manager.h"
+#include "mkdir.h"
#include "mount-setup.h"
#include "os-util.h"
#include "pager.h"
if (r < 0)
log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device);
}
+ } else {
+ _cleanup_free_ char *p = NULL;
+
+ /* Create the runtime directory and place the inaccessible device nodes there, if we run in
+ * user mode. In system mode mount_setup() already did that. */
+
+ r = xdg_user_runtime_dir(&p, "/systemd");
+ if (r < 0) {
+ *ret_error_message = "$XDG_RUNTIME_DIR is not set";
+ return log_emergency_errno(r, "Failed to determine $XDG_RUNTIME_DIR path: %m");
+ }
+
+ (void) mkdir_p(p, 0755);
+ (void) make_inaccessible_nodes(p, UID_INVALID, GID_INVALID);
}
if (arg_timer_slack_nsec != NSEC_INFINITY)
m->unit_id_map = hashmap_free(m->unit_id_map);
m->unit_name_map = hashmap_free(m->unit_name_map);
m->unit_path_cache = set_free(m->unit_path_cache);
- m->unit_cache_mtime = 0;
+ m->unit_cache_timestamp_hash = 0;
}
static int manager_setup_run_queue(Manager *m) {
return n;
}
-bool manager_unit_file_maybe_loadable_from_cache(Unit *u) {
+bool manager_unit_cache_should_retry_load(Unit *u) {
assert(u);
+ /* Automatic reloading from disk only applies to units which were not found sometime in the past, and
+ * the not-found stub is kept pinned in the unit graph by dependencies. For units that were
+ * previously loaded, we don't do automatic reloading, and daemon-reload is necessary to update. */
if (u->load_state != UNIT_NOT_FOUND)
return false;
- if (u->manager->unit_cache_mtime == 0)
- return false;
-
- if (u->manager->unit_cache_mtime > u->fragment_loadtime)
+ /* The cache has been updated since the last time we tried to load the unit. There might be new
+ * fragment paths to read. */
+ if (u->manager->unit_cache_timestamp_hash != u->fragment_not_found_timestamp_hash)
return true;
- return !lookup_paths_mtime_good(&u->manager->lookup_paths, u->manager->unit_cache_mtime);
+ /* The cache needs to be updated because there are modifications on disk. */
+ return !lookup_paths_timestamp_hash_same(&u->manager->lookup_paths, u->manager->unit_cache_timestamp_hash, NULL);
}
int manager_load_unit_prepare(
* first if anything in the usual paths was modified since the last time
* the cache was loaded. Also check if the last time an attempt to load the
* unit was made was before the most recent cache refresh, so that we know
- * we need to try again - even if the cache is current, it might have been
+ * we need to try again — even if the cache is current, it might have been
* updated in a different context before we had a chance to retry loading
* this particular unit. */
- if (manager_unit_file_maybe_loadable_from_cache(ret))
+ if (manager_unit_cache_should_retry_load(ret))
ret->load_state = UNIT_STUB;
else {
*_ret = ret;
Hashmap *unit_id_map;
Hashmap *unit_name_map;
Set *unit_path_cache;
- usec_t unit_cache_mtime;
+ uint64_t unit_cache_timestamp_hash;
char **transient_environment; /* The environment, as determined from config files, kernel cmdline and environment generators */
char **client_environment; /* Environment variables created by clients through the bus API */
int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
-bool manager_unit_file_maybe_loadable_from_cache(Unit *u);
+bool manager_unit_cache_should_retry_load(Unit *u);
int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
int manager_load_startable_unit_or_warn(Manager *m, const char *name, const char *path, Unit **ret);
(void) mkdir_label("/run/systemd/system", 0755);
/* Also create /run/systemd/inaccessible nodes, so that we always have something to mount
- * inaccessible nodes from. */
- (void) make_inaccessible_nodes(NULL, UID_INVALID, GID_INVALID);
+ * inaccessible nodes from. If we run in a container the host might have created these for us already
+ * in /run/host/inaccessible/. Use those if we can, since tht way we likely get access to block/char
+ * device nodes that are inaccessible, and if userns is used to nodes that are on mounts owned by a
+ * userns outside the container and thus nicely read-only and not remountable. */
+ if (access("/run/host/inaccessible/", F_OK) < 0) {
+ if (errno != ENOENT)
+ log_debug_errno(errno, "Failed to check if /run/host/inaccessible exists, ignoring: %m");
+
+ (void) make_inaccessible_nodes("/run/systemd", UID_INVALID, GID_INVALID);
+ } else
+ (void) symlink("../host/inaccessible", "/run/systemd/inaccessible");
return 0;
}
* first if anything in the usual paths was modified since the last time
* the cache was loaded. Also check if the last time an attempt to load the
* unit was made was before the most recent cache refresh, so that we know
- * we need to try again - even if the cache is current, it might have been
+ * we need to try again — even if the cache is current, it might have been
* updated in a different context before we had a chance to retry loading
* this particular unit.
+ *
* Given building up the transaction is a synchronous operation, attempt
* to load the unit immediately. */
- if (r < 0 && manager_unit_file_maybe_loadable_from_cache(unit)) {
+ if (r < 0 && manager_unit_cache_should_retry_load(unit)) {
sd_bus_error_free(e);
unit->load_state = UNIT_STUB;
r = unit_load(unit);
return 0;
fail:
- /* We convert ENOEXEC errors to the UNIT_BAD_SETTING load state here. Configuration parsing code should hence
- * return ENOEXEC to ensure units are placed in this state after loading */
+ /* We convert ENOEXEC errors to the UNIT_BAD_SETTING load state here. Configuration parsing code
+ * should hence return ENOEXEC to ensure units are placed in this state after loading. */
u->load_state = u->load_state == UNIT_STUB ? UNIT_NOT_FOUND :
r == -ENOEXEC ? UNIT_BAD_SETTING :
UNIT_ERROR;
u->load_error = r;
- /* Record the last time we tried to load the unit, so that if the cache gets updated between now
- * and the next time an attempt is made to load this unit, we know we need to check again */
+ /* Record the timestamp on the cache, so that if the cache gets updated between now and the next time
+ * an attempt is made to load this unit, we know we need to check again. */
if (u->load_state == UNIT_NOT_FOUND)
- u->fragment_loadtime = now(CLOCK_REALTIME);
+ u->fragment_not_found_timestamp_hash = u->manager->unit_cache_timestamp_hash;
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
char *source_path; /* if converted, the source file */
char **dropin_paths;
- usec_t fragment_loadtime;
+ usec_t fragment_not_found_timestamp_hash;
usec_t fragment_mtime;
usec_t source_mtime;
usec_t dropin_mtime;
META_ARGV_UID, /* %u: as seen in the initial user namespace */
META_ARGV_GID, /* %g: as seen in the initial user namespace */
META_ARGV_SIGNAL, /* %s: number of signal causing dump */
- META_ARGV_TIMESTAMP, /* %t: time of dump, expressed as seconds since the Epoch */
+ META_ARGV_TIMESTAMP, /* %t: time of dump, expressed as seconds since the Epoch (we expand this to µs granularity) */
META_ARGV_RLIMIT, /* %c: core file size soft resource limit */
META_ARGV_HOSTNAME, /* %h: hostname */
_META_ARGV_MAX,
return -ENOMEM;
if (asprintf(ret,
- "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s000000",
+ "/var/lib/systemd/coredump/core.%s.%s." SD_ID128_FORMAT_STR ".%s.%s",
c,
u,
SD_ID128_FORMAT_VAL(boot),
return 0;
}
-static int gather_pid_metadata_from_argv(struct iovec_wrapper *iovw, Context *context,
- int argc, char **argv) {
+static int gather_pid_metadata_from_argv(
+ struct iovec_wrapper *iovw,
+ Context *context,
+ int argc, char **argv) {
+
_cleanup_free_ char *free_timestamp = NULL;
int i, r, signo;
char *t;
t = argv[i];
switch (i) {
+
case META_ARGV_TIMESTAMP:
/* The journal fields contain the timestamp padded with six
* zeroes, so that the kernel-supplied 1s granularity timestamps
if (!t)
return log_oom();
break;
+
case META_ARGV_SIGNAL:
/* For signal, record its pretty name too */
if (safe_atoi(argv[i], &signo) >= 0 && SIGNAL_VALID(signo))
(void) iovw_put_string_field(iovw, "COREDUMP_SIGNAL_NAME=SIG",
signal_to_string(signo));
break;
+
default:
break;
}
static void print_welcome(void) {
_cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL;
static bool done = false;
- const char *pn;
+ const char *pn, *ac;
int r;
if (!arg_welcome)
"Failed to read os-release file, ignoring: %m");
pn = isempty(pretty_name) ? "Linux" : pretty_name;
+ ac = isempty(ansi_color) ? "0" : ansi_color;
if (colors_enabled())
- printf("\nWelcome to your new installation of \x1B[%sm%s\x1B[0m!\n", ansi_color, pn);
+ printf("\nWelcome to your new installation of \x1B[%sm%s\x1B[0m!\n", ac, pn);
else
printf("\nWelcome to your new installation of %s!\n", pn);
homework = getenv("SYSTEMD_HOMEWORK_PATH") ?: SYSTEMD_HOMEWORK_PATH;
execl(homework, homework, verb, NULL);
- log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m");
+ log_error_errno(errno, "Failed to invoke %s: %m", homework);
_exit(EXIT_FAILURE);
}
#pragma once
#include <stdbool.h>
+#include <sys/stat.h>
#include "sd-bus.h"
#include "sd-device.h"
goto fail;
}
- log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s.\n"
+ log_debug_errno(errno,
+ "Failed to mount per-user tmpfs directory %s.\n"
"Assuming containerized execution, ignoring: %m", runtime_path);
r = chmod_and_chown(runtime_path, 0700, uid, gid);
log_warning_errno(r, "Failed to fix label of \"%s\", ignoring: %m", runtime_path);
}
- /* Set up inaccessible nodes now so they're available if we decide to use them with user namespaces. */
- (void) make_inaccessible_nodes(runtime_path, uid, gid);
return 0;
fail:
{ "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links },
{ "status", VERB_ANY, VERB_ANY, 0, link_status },
{ "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status },
- { "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
+ { "label", 1, 1, 0, list_address_labels },
{ "delete", 2, VERB_ANY, 0, link_delete },
{ "up", 2, VERB_ANY, 0, link_up_down },
{ "down", 2, VERB_ANY, 0, link_up_down },
addr->cinfo.ifa_valid = lifetime_valid;
(void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
- log_link_info(link, "DHCPv6 address %s/%u timeout preferred %d valid %d",
+ log_link_full(link, set_contains(link->dhcp6_addresses, addr) ? LOG_DEBUG : LOG_INFO, 0,
+ "DHCPv6 address %s/%u timeout preferred %d valid %d",
strna(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
r = address_configure(addr, link, dhcp6_address_handler, true, &ret);
(void) dev_setup(directory, arg_uid_shift, arg_uid_shift);
- p = prefix_roota(directory, "/run");
+ p = prefix_roota(directory, "/run/host");
(void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift);
r = setup_pts(directory);
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <fcntl.h>
+#include <sys/file.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#!/usr/bin/env bash
set -ex
+[[ -f /dev/loop-control ]] || exit 77
+
repart=$1
test -x $repart
dns_transaction_gc(t);
}
+static void dns_transaction_complete_errno(DnsTransaction *t, int error) {
+ assert(t);
+ assert(error != 0);
+
+ t->answer_errno = abs(error);
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+}
+
static int dns_transaction_pick_server(DnsTransaction *t) {
DnsServer *server;
dns_scope_next_dns_server(t->scope);
r = dns_transaction_go(t);
- if (r < 0) {
- t->answer_errno = -r;
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
- }
+ if (r < 0)
+ dns_transaction_complete_errno(t, r);
}
static int dns_transaction_maybe_restart(DnsTransaction *t) {
dns_transaction_retry(t, true);
return;
}
- if (error != 0) {
- t->answer_errno = error;
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
- }
+ if (error != 0)
+ dns_transaction_complete_errno(t, error);
}
static int dns_transaction_on_stream_packet(DnsTransaction *t, DnsPacket *p) {
return;
fail:
- t->answer_errno = -r;
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+ dns_transaction_complete_errno(t, r);
}
static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
return;
fail:
- t->answer_errno = -r;
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+ dns_transaction_complete_errno(t, r);
}
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
return 0;
}
if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
- t->answer_errno = -r;
+ dns_transaction_complete_errno(t, r);
return 0;
}
if (r == 0)
assert(ret);
+ if (!message)
+ message = "Password:";
+
if (flag_file) {
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (notify < 0)
}
int make_inaccessible_nodes(
- const char *runtime_dir,
+ const char *parent_dir,
uid_t uid,
gid_t gid) {
const char *name;
mode_t mode;
} table[] = {
- { "/systemd", S_IFDIR | 0755 },
- { "/systemd/inaccessible", S_IFDIR | 0000 },
- { "/systemd/inaccessible/reg", S_IFREG | 0000 },
- { "/systemd/inaccessible/dir", S_IFDIR | 0000 },
- { "/systemd/inaccessible/fifo", S_IFIFO | 0000 },
- { "/systemd/inaccessible/sock", S_IFSOCK | 0000 },
+ { "inaccessible", S_IFDIR | 0755 },
+ { "inaccessible/reg", S_IFREG | 0000 },
+ { "inaccessible/dir", S_IFDIR | 0000 },
+ { "inaccessible/fifo", S_IFIFO | 0000 },
+ { "inaccessible/sock", S_IFSOCK | 0000 },
/* The following two are likely to fail if we lack the privs for it (for example in an userns
* environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0
* device nodes to be created). But that's entirely fine. Consumers of these files should carry
* fallback to use a different node then, for example <root>/inaccessible/sock, which is close
* enough in behaviour and semantics for most uses. */
- { "/systemd/inaccessible/chr", S_IFCHR | 0000 },
- { "/systemd/inaccessible/blk", S_IFBLK | 0000 },
+ { "inaccessible/chr", S_IFCHR | 0000 },
+ { "inaccessible/blk", S_IFBLK | 0000 },
};
_cleanup_umask_ mode_t u;
- size_t i;
int r;
- if (!runtime_dir)
- runtime_dir = "/run";
+ if (!parent_dir)
+ parent_dir = "/run/systemd";
u = umask(0000);
* to lock down these nodes as much as we can, but otherwise try to match them as closely as possible with the
* underlying file, i.e. in the best case we offer the same node type as the underlying node. */
- for (i = 0; i < ELEMENTSOF(table); i++) {
+ for (size_t i = 0; i < ELEMENTSOF(table); i++) {
_cleanup_free_ char *path = NULL;
- path = path_join(runtime_dir, table[i].name);
+ path = path_join(parent_dir, table[i].name);
if (!path)
return log_oom();
else
r = mknod_label(path, table[i].mode, makedev(0, 0));
if (r < 0) {
- if (r != -EEXIST)
- log_debug_errno(r, "Failed to create '%s', ignoring: %m", path);
+ log_debug_errno(r, "Failed to create '%s', ignoring: %m", path);
continue;
}
int dev_setup(const char *prefix, uid_t uid, gid_t gid);
-int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid);
+int make_inaccessible_nodes(const char *parent_dir, uid_t uid, gid_t gid);
#include "user-util.h"
#include "xattr-util.h"
+/* how many times to wait for the device nodes to appear */
+#define N_DEVICE_NODE_LIST_ATTEMPTS 10
+
int probe_filesystem(const char *node, char **ret_fstype) {
/* Try to find device content type and return it in *ret_fstype. If nothing is found,
* 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an
return 0;
}
-/* how many times to wait for the device nodes to appear */
-#define N_DEVICE_NODE_LIST_ATTEMPTS 10
-
static int wait_for_partitions_to_appear(
int fd,
sd_device *d,
g->gid = grp->gr_gid;
if (sgrp) {
- if (hashed_password_valid(sgrp->sg_passwd)) {
+ if (looks_like_hashed_password(sgrp->sg_passwd)) {
g->hashed_password = strv_new(sgrp->sg_passwd);
if (!g->hashed_password)
return -ENOMEM;
#endif
}
-bool hashed_password_valid(const char *s) {
-
- /* Returns true if the specified string is a 'valid' hashed UNIX password, i.e. if starts with '$' or
- * with '!$' (the latter being a valid, yet locked password). */
-
- if (isempty(s))
+bool looks_like_hashed_password(const char *s) {
+ /* Returns false if the specified string is certainly not a hashed UNIX password. crypt(5) lists
+ * various hashing methods. We only reject (return false) strings which are documented to have
+ * different meanings.
+ *
+ * In particular, we allow locked passwords, i.e. strings starting with "!", including just "!",
+ * i.e. the locked empty password. See also fc58c0c7bf7e4f525b916e3e5be0de2307fef04e.
+ */
+ if (!s)
return false;
- return STARTSWITH_SET(s, "$", "!$");
+ s += strspn(s, "!"); /* Skip (possibly duplicated) locking prefix */
+
+ return !STR_IN_SET(s, "x", "*");
}
int make_salt(char **ret);
-bool hashed_password_valid(const char *s);
+bool looks_like_hashed_password(const char *s);
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include "sd-id128.h"
+
#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
streq_ptr(path, lp->runtime_control);
}
-bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime) {
- char **dir;
+#define HASH_KEY SD_ID128_MAKE(4e,86,1b,e3,39,b3,40,46,98,5d,b8,11,34,8f,c3,c1)
+
+bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new) {
+ struct siphash state;
+ siphash24_init(&state, HASH_KEY.bytes);
+
+ char **dir;
STRV_FOREACH(dir, (char**) lp->search_path) {
struct stat st;
continue;
}
- if (timespec_load(&st.st_mtim) > mtime) {
- log_debug_errno(errno, "Unit dir %s has changed, need to update cache.", *dir);
- return false;
- }
+ siphash24_compress_usec_t(timespec_load(&st.st_mtim), &state);
}
- return true;
+ uint64_t updated = siphash24_finalize(&state);
+ if (ret_new)
+ *ret_new = updated;
+ if (updated != timestamp_hash)
+ log_debug("Modification times have changed, need to update cache.");
+ return updated == timestamp_hash;
}
int unit_file_build_name_map(
const LookupPaths *lp,
- usec_t *cache_mtime,
+ uint64_t *cache_timestamp_hash,
Hashmap **unit_ids_map,
Hashmap **unit_names_map,
Set **path_cache) {
_cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL;
_cleanup_set_free_free_ Set *paths = NULL;
+ uint64_t timestamp_hash;
char **dir;
int r;
- usec_t mtime = 0;
- /* Before doing anything, check if the mtime that was passed is still valid. If
- * yes, do nothing. If *cache_time == 0, always build the cache. */
- if (cache_mtime && *cache_mtime > 0 && lookup_paths_mtime_good(lp, *cache_mtime))
- return 0;
+ /* Before doing anything, check if the timestamp hash that was passed is still valid.
+ * If yes, do nothing. */
+ if (cache_timestamp_hash &&
+ lookup_paths_timestamp_hash_same(lp, *cache_timestamp_hash, ×tamp_hash))
+ return 0;
+
+ /* The timestamp hash is now set based on the mtimes from before when we start reading files.
+ * If anything is modified concurrently, we'll consider the cache outdated. */
if (path_cache) {
paths = set_new(&path_hash_ops_free);
STRV_FOREACH(dir, (char**) lp->search_path) {
struct dirent *de;
_cleanup_closedir_ DIR *d = NULL;
- struct stat st;
d = opendir(*dir);
if (!d) {
continue;
}
- /* Determine the latest lookup path modification time */
- if (fstat(dirfd(d), &st) < 0)
- return log_error_errno(errno, "Failed to fstat %s: %m", *dir);
-
- if (!lookup_paths_mtime_exclude(lp, *dir))
- mtime = MAX(mtime, timespec_load(&st.st_mtim));
-
FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) {
char *filename;
_cleanup_free_ char *_filename_free = NULL, *simplified = NULL;
basename(dst), src);
}
- if (cache_mtime)
- *cache_mtime = mtime;
+ if (cache_timestamp_hash)
+ *cache_timestamp_hash = timestamp_hash;
hashmap_free_and_replace(*unit_ids_map, ids);
hashmap_free_and_replace(*unit_names_map, names);
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target);
-bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime);
+bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new);
int unit_file_build_name_map(
const LookupPaths *lp,
- usec_t *ret_time,
- Hashmap **ret_unit_ids_map,
- Hashmap **ret_unit_names_map,
- Set **ret_path_cache);
+ uint64_t *cache_timestamp_hash,
+ Hashmap **unit_ids_map,
+ Hashmap **unit_names_map,
+ Set **path_cache);
int unit_file_find_fragment(
Hashmap *unit_ids_map,
Hashmap *unit_name_map,
const char *unit_name,
const char **ret_fragment_path,
- Set **names);
+ Set **ret_names);
const char* runlevel_to_target(const char *rl);
hr->uid = pwd->pw_uid;
hr->gid = pwd->pw_gid;
- if (spwd && hashed_password_valid(spwd->sp_pwdp)) {
+ if (spwd && looks_like_hashed_password(spwd->sp_pwdp)) {
strv_free_erase(hr->hashed_password);
hr->hashed_password = strv_new(spwd->sp_pwdp);
if (!hr->hashed_password)
sz = e - begin + 1;
- varlink_log(v, "New incoming message: %s", begin);
+ varlink_log(v, "New incoming message: %s", begin); /* FIXME: should we output the whole message here before validation?
+ * This may produce a non-printable journal entry if the message
+ * is invalid. We may also expose privileged information. */
r = json_parse(begin, 0, &v->current, NULL, NULL);
- if (r < 0)
- return r;
+ if (r < 0) {
+ /* If we encounter a parse failure flush all data. We cannot possibly recover from this,
+ * hence drop all buffered data now. */
+ v->input_buffer_index = v->input_buffer_size = v->input_buffer_unscanned = 0;
+ return varlink_log_errno(v, r, "Failed to parse JSON: %m");
+ }
v->input_buffer_size -= sz;
#include "capability-util.h"
#include "dev-setup.h"
#include "fs-util.h"
+#include "mkdir.h"
#include "path-util.h"
#include "rm-rf.h"
#include "tmpfile-util.h"
assert_se(mkdtemp_malloc("/tmp/test-dev-setupXXXXXX", &p) >= 0);
- f = prefix_roota(p, "/run");
- assert_se(mkdir(f, 0755) >= 0);
+ f = prefix_roota(p, "/run/systemd");
+ assert_se(mkdir_p(f, 0755) >= 0);
assert_se(make_inaccessible_nodes(f, 1, 1) >= 0);
ACTION_LIST,
ACTION_QUERY,
ACTION_WATCH,
- ACTION_WALL
+ ACTION_WALL,
} arg_action = ACTION_QUERY;
static bool arg_plymouth = false;
const char *flag_file,
char ***ret) {
- int tty_fd = -1;
- int r;
+ int tty_fd = -1, r;
if (arg_console) {
const char *con = arg_device ?: "/dev/console";
release_terminal();
}
- return 0;
+ return r;
}
static int process_one_password_file(const char *filename) {
switch (arg_action) {
case ACTION_LIST:
- printf("'%s' (PID %u)\n", message, pid);
+ printf("'%s' (PID %u)\n", strna(message), pid);
return 0;
case ACTION_WALL: {
if (asprintf(&wall,
"Password entry required for \'%s\' (PID %u).\r\n"
"Please enter password with the systemd-tty-ask-password-agent tool.",
- message,
+ strna(message),
pid) < 0)
return log_oom();
if (access(socket_name, W_OK) < 0) {
if (arg_action == ACTION_QUERY)
- log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
+ log_info("Not querying '%s' (PID %u), lacking privileges.", strna(message), pid);
return 0;
}
r = ask_password_plymouth(message, not_after, flags, filename, &passwords);
else
r = agent_ask_password_tty(message, not_after, flags, filename, &passwords);
-
if (r < 0) {
/* If the query went away, that's OK */
if (IN_SET(r, -ETIME, -ENOENT))
if (r < 0)
return log_error_errno(r, "Failed to send: %m");
break;
- }
- }
+ }}
return 0;
}
(void) release_terminal();
}
- return process_and_watch_password_files(arg_action != ACTION_QUERY);
+ return process_and_watch_password_files(!IN_SET(arg_action, ACTION_QUERY, ACTION_LIST));
}
DEFINE_MAIN_FUNCTION(run);
else if (streq(optarg, "help")) {
puts("classic\n"
"friendly\n"
- "json");
+ "json\n"
+ "table");
return 0;
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --output= mode: %s", optarg);
['tmp.mount', '',
'local-fs.target.wants/'],
['umount.target', ''],
+ ['usb-gadget.target', ''],
['user.slice', ''],
['var-lib-machines.mount', 'ENABLE_MACHINED',
'remote-fs.target.wants/ machines.target.wants/'],