mirror of
https://github.com/systemd/systemd.git
synced 2024-11-28 04:33:36 +08:00
Merge pull request #9824 from poettering/login-unit-fixes
many logind improvements
This commit is contained in:
commit
0919b554c6
37
TODO
37
TODO
@ -25,6 +25,12 @@ Features:
|
||||
|
||||
* set memory.oom.group in cgroupsv2 for all leaf cgroups
|
||||
|
||||
* drop umask() calls and suchlike from our generators, pid1 should set things up correctly anyway
|
||||
|
||||
* paranoia: whenever we process passwords, call mlock() on the memory
|
||||
first. i.e. look for all places we use string_erase()/string_free_erase() and
|
||||
augment them with mlock()
|
||||
|
||||
* whenever oom_kill memory.event event is triggered print a nice log message
|
||||
|
||||
* Move RestrictAddressFamily= to the new cgroup create socket
|
||||
@ -34,6 +40,14 @@ Features:
|
||||
|
||||
* chown() tty a service is attached to after the service goes down
|
||||
|
||||
* replace systemd-reboot.service's ExecStart= with a single SuccessAction=
|
||||
line, so that we don't need to fork() for executing the reboot
|
||||
service. Similar for other services like this, such as systemd-exit.service
|
||||
and so on. Of course, for this to work service units with no ExecYYZ= set but
|
||||
SuccessAction= set need to be acceptable.
|
||||
|
||||
* optionally: turn on cgroup delegation for per-session scope units
|
||||
|
||||
* optionally, if a per-partition GPT flag is set for the root/home/… partitions
|
||||
format the partition on next boot and unset the flag, in order to implement
|
||||
factory reset. also, add a second flag that simply indicates whether such a
|
||||
@ -41,20 +55,6 @@ Features:
|
||||
show state of these flags, and optionally trigger such a factory reset on
|
||||
next boot by setting the flag.
|
||||
|
||||
* logind: maybe watch utmp asynchronously using inotify, and populate our own
|
||||
tracked session metadata from the fields available therein. Why bother? Right
|
||||
now, all "ssh" sessions will be tracked without their TTY by logind (which is
|
||||
not just unfriendly to users as this means "loginctl session-status" shows
|
||||
less information than "who" in many cases, but also breaks the IdleAction
|
||||
logic, as we never can detect such sessions as idle, as we have no TTY to
|
||||
watch). ssh sets the PAM_TTY field on its PAM sessions to "ssh" rather than
|
||||
the actual pty, because the PAM session is opened early on for new
|
||||
connections, but the PTY only registered much later (if at all). ssh writes
|
||||
the utmp record only after a TTY is actually registered, hence we could use
|
||||
this data then, and use it if it is available. Using utmp for this is ugly of
|
||||
course, and watching things asynchronously even more so, but it should be
|
||||
good enough for the idle detection logic at least.
|
||||
|
||||
* maybe extend .path units to expose fanotify() per-mount change events
|
||||
|
||||
* Add a "systemctl list-units --by-slice" mode or so, which rearranges the
|
||||
@ -472,8 +472,6 @@ Features:
|
||||
|
||||
* maybe add support for specifier expansion in user.conf, specifically DefaultEnvironment=
|
||||
|
||||
* introduce systemd-timesync-wait.service or so to sync on an NTP fix?
|
||||
|
||||
* consider showing the unit names during boot up in the status output, not just the unit descriptions
|
||||
|
||||
* maybe allow timer units with an empty Units= setting, so that they
|
||||
@ -615,7 +613,6 @@ Features:
|
||||
- document chaining of signal handler for SIGCHLD and child handlers
|
||||
- define more intervals where we will shift wakeup intervals around in, 1h, 6h, 24h, ...
|
||||
- generate a failure of a default event loop is executed out-of-thread
|
||||
- maybe add support for inotify events (which we can do safely now, with O_PATH)
|
||||
|
||||
* investigate endianness issues of UUID vs. GUID
|
||||
|
||||
@ -674,11 +671,9 @@ Features:
|
||||
|
||||
* logind:
|
||||
- logind: optionally, ignore idle-hint logic for autosuspend, block suspend as long as a session is around
|
||||
- When we update the kernel all kind of hibernation should be prohibited until shutdown/reboot
|
||||
- logind: wakelock/opportunistic suspend support
|
||||
- Add pretty name for seats in logind
|
||||
- logind: allow showing logout dialog from system?
|
||||
- session scopes/user unit: add RequiresMountsFor for the home directory of the user
|
||||
- add Suspend() bus calls which take timestamps to fix double suspend issues when somebody hits suspend and closes laptop quickly.
|
||||
- if pam_systemd is invoked by su from a process that is outside of a
|
||||
any session we should probably just become a NOP, since that's
|
||||
@ -851,8 +846,6 @@ Features:
|
||||
"machinectl start" with a new --ephemeral switch
|
||||
- "machinectl status" should also show internal logs of the container in
|
||||
question
|
||||
- "machinectl list-images" should show os-release data, as well as
|
||||
machine-info data (including deployment level)
|
||||
- "machinectl history"
|
||||
- "machinectl diff"
|
||||
- "machinectl commit" that takes a writable snapshot of a tree, invokes a
|
||||
@ -1048,8 +1041,6 @@ External:
|
||||
|
||||
* kernel: add device_type = "fb", "fbcon" to class "graphics"
|
||||
|
||||
* drop accountsservice's StandardOutput=syslog and Type=dbus fields
|
||||
|
||||
* /usr/bin/service should actually show the new command line
|
||||
|
||||
* fedora: suggest auto-restart on failure, but not on success and not on coredump. also, ask people to think about changing the start limit logic. Also point people to RestartPreventExitStatus=, SuccessExitStatus=
|
||||
|
@ -184,6 +184,17 @@
|
||||
5.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>UserStopDelaySec=</varname></term>
|
||||
|
||||
<listitem><para>Specifies how long to keep the user record and per-user service
|
||||
<filename>user@.service</filename> around for a user after they logged out fully. If set to zero, the per-user
|
||||
service is terminated immediately when the last session of the user has ended. If this option is configured to
|
||||
non-zero rapid logout/login cycles are sped up, as the user's service manager is not constantly restarted. If
|
||||
set to <literal>infinity</literal> the per-user service for a user is never terminated again after first login,
|
||||
and continues to run until system shutdown. Defaults to 10s.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>HandlePowerKey=</varname></term>
|
||||
<term><varname>HandleSuspendKey=</varname></term>
|
||||
|
@ -180,6 +180,7 @@ manpages = [
|
||||
'sd_bus_error_get_errno',
|
||||
'sd_bus_error_has_name',
|
||||
'sd_bus_error_is_set',
|
||||
'sd_bus_error_move',
|
||||
'sd_bus_error_set',
|
||||
'sd_bus_error_set_const',
|
||||
'sd_bus_error_set_errno',
|
||||
|
@ -31,6 +31,7 @@
|
||||
<refname>sd_bus_error_set_errnofv</refname>
|
||||
<refname>sd_bus_error_get_errno</refname>
|
||||
<refname>sd_bus_error_copy</refname>
|
||||
<refname>sd_bus_error_move</refname>
|
||||
<refname>sd_bus_error_is_set</refname>
|
||||
<refname>sd_bus_error_has_name</refname>
|
||||
|
||||
@ -114,6 +115,12 @@
|
||||
<paramdef>const sd_bus_error *<parameter>e</parameter></paramdef>
|
||||
</funcprototype>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_bus_error_move</function></funcdef>
|
||||
<paramdef>sd_bus_error *<parameter>dst</parameter></paramdef>
|
||||
<paramdef>sd_bus_error *<parameter>e</parameter></paramdef>
|
||||
</funcprototype>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_bus_error_is_set</function></funcdef>
|
||||
<paramdef>const sd_bus_error *<parameter>e</parameter></paramdef>
|
||||
@ -148,7 +155,7 @@
|
||||
should have both fields initialized to NULL. Set an error
|
||||
structure to <constant>SD_BUS_ERROR_NULL</constant> in order to
|
||||
reset both fields to NULL. When no longer necessary, resources
|
||||
held by the <structname>sd_bus_error</structname>structure should
|
||||
held by the <structname>sd_bus_error</structname> structure should
|
||||
be destroyed with <function>sd_bus_error_free()</function>.</para>
|
||||
|
||||
<para><function>sd_bus_error_set()</function> sets an error
|
||||
@ -245,6 +252,14 @@
|
||||
Otherwise, they will be copied. Returns a converted
|
||||
<varname>errno</varname>-like, negative error code.</para>
|
||||
|
||||
<para><function>sd_bus_error_move()</function> is similar to <function>sd_bus_error_copy()</function>, but will
|
||||
move any error information from <parameter>e</parameter> into <parameter>dst</parameter>, resetting the
|
||||
former. This function cannot fail, as no new memory is allocated. Note that if <parameter>e</parameter> is not set
|
||||
(or <constant>NULL</constant>) <parameter>dst</parameter> is initializated to
|
||||
<constant>SD_BUS_ERROR_NULL</constant>. Moreover, if <parameter>dst</parameter> is <constant>NULL</constant> no
|
||||
operation is executed on it and and resources held by <parameter>e</parameter> are freed and reset. Returns a
|
||||
converted <varname>errno</varname>-like, negative error code.</para>
|
||||
|
||||
<para><function>sd_bus_error_is_set()</function> will return a
|
||||
non-zero value if <parameter>e</parameter> is
|
||||
non-<constant>NULL</constant> and an error has been set,
|
||||
@ -287,9 +302,8 @@
|
||||
<constant>NULL</constant>, and a positive errno value mapped from
|
||||
<parameter>e->name</parameter> otherwise.</para>
|
||||
|
||||
<para><function>sd_bus_error_copy()</function> returns 0 or a
|
||||
positive integer on success, and a negative error value converted
|
||||
from the error name otherwise.</para>
|
||||
<para><function>sd_bus_error_copy()</function> and <function>sd_bus_error_move()</function> return 0 or a positive
|
||||
integer on success, and a negative error value converted from the error name otherwise.</para>
|
||||
|
||||
<para><function>sd_bus_error_is_set()</function> returns a
|
||||
non-zero value when <parameter>e</parameter> and the
|
||||
|
@ -52,7 +52,7 @@
|
||||
<citerefentry project='die-net'><refentrytitle>socat</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
|
||||
The main differences for <command>systemd-socket-proxyd</command>
|
||||
are support for socket activation with
|
||||
<literal>Accept=false</literal> and an event-driven
|
||||
<literal>Accept=no</literal> and an event-driven
|
||||
design that scales better with the number of
|
||||
connections.</para>
|
||||
</refsect1>
|
||||
|
@ -1470,10 +1470,10 @@ Name=ipip-tun
|
||||
Kind=ipip
|
||||
|
||||
[Tunnel]
|
||||
Independent=true
|
||||
Independent=yes
|
||||
Local=10.65.208.212
|
||||
Remote=10.65.208.211
|
||||
FooOverUDP=true
|
||||
FooOverUDP=yes
|
||||
FOUDestinationPort=5555
|
||||
</programlisting>
|
||||
</example>
|
||||
@ -1484,8 +1484,8 @@ Name=tap-test
|
||||
Kind=tap
|
||||
|
||||
[Tap]
|
||||
MultiQueue=true
|
||||
PacketInfo=true</programlisting> </example>
|
||||
MultiQueue=yes
|
||||
PacketInfo=yes</programlisting> </example>
|
||||
|
||||
<example>
|
||||
<title>/etc/systemd/network/25-sit.netdev</title>
|
||||
|
@ -666,7 +666,7 @@
|
||||
<listitem><para>An IPv6 address, for which Neighbour Advertisement messages will be
|
||||
proxied. This option may be specified more than once. systemd-networkd will add the
|
||||
<option>IPv6ProxyNDPAddress=</option> entries to the kernel's IPv6 neighbor proxy table.
|
||||
This option implies <option>IPv6ProxyNDP=true</option> but has no effect if
|
||||
This option implies <option>IPv6ProxyNDP=yes</option> but has no effect if
|
||||
<option>IPv6ProxyNDP</option> has been set to false. Defaults to unset.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
@ -134,7 +134,7 @@
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The update service should declare <varname>DefaultDependencies=false</varname>,
|
||||
<para>The update service should declare <varname>DefaultDependencies=no</varname>,
|
||||
<varname>Requires=sysinit.target</varname>, <varname>After=sysinit.target</varname>,
|
||||
<varname>After=system-update-pre.target</varname>
|
||||
and explicitly pull in any other services it requires.</para>
|
||||
|
@ -465,7 +465,7 @@
|
||||
control group attribute, see <ulink
|
||||
url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
|
||||
|
||||
<para>Implies <literal>IOAccounting=true</literal>.</para>
|
||||
<para>Implies <literal>IOAccounting=yes</literal>.</para>
|
||||
|
||||
<para>These settings are supported only if the unified control group hierarchy is used.</para>
|
||||
</listitem>
|
||||
@ -760,7 +760,7 @@
|
||||
the startup phase. Using <varname>StartupCPUShares=</varname> allows prioritizing specific services at
|
||||
boot-up differently than during normal runtime.</para>
|
||||
|
||||
<para>Implies <literal>CPUAccounting=true</literal>.</para>
|
||||
<para>Implies <literal>CPUAccounting=yes</literal>.</para>
|
||||
|
||||
<para>These settings are deprecated. Use <varname>CPUWeight=</varname> and
|
||||
<varname>StartupCPUWeight=</varname> instead.</para>
|
||||
@ -781,7 +781,7 @@
|
||||
attribute, see <ulink
|
||||
url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>.</para>
|
||||
|
||||
<para>Implies <literal>MemoryAccounting=true</literal>.</para>
|
||||
<para>Implies <literal>MemoryAccounting=yes</literal>.</para>
|
||||
|
||||
<para>This setting is deprecated. Use <varname>MemoryMax=</varname> instead.</para>
|
||||
</listitem>
|
||||
@ -822,7 +822,7 @@
|
||||
boot-up differently than during runtime.</para>
|
||||
|
||||
<para>Implies
|
||||
<literal>BlockIOAccounting=true</literal>.</para>
|
||||
<literal>BlockIOAccounting=yes</literal>.</para>
|
||||
|
||||
<para>These settings are deprecated. Use <varname>IOWeight=</varname> and <varname>StartupIOWeight=</varname>
|
||||
instead.</para>
|
||||
@ -844,7 +844,7 @@
|
||||
url="https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt">blkio-controller.txt</ulink>.</para>
|
||||
|
||||
<para>Implies
|
||||
<literal>BlockIOAccounting=true</literal>.</para>
|
||||
<literal>BlockIOAccounting=yes</literal>.</para>
|
||||
|
||||
<para>This setting is deprecated. Use <varname>IODeviceWeight=</varname> instead.</para>
|
||||
</listitem>
|
||||
@ -869,7 +869,7 @@
|
||||
</para>
|
||||
|
||||
<para>Implies
|
||||
<literal>BlockIOAccounting=true</literal>.</para>
|
||||
<literal>BlockIOAccounting=yes</literal>.</para>
|
||||
|
||||
<para>These settings are deprecated. Use <varname>IOReadBandwidthMax=</varname> and
|
||||
<varname>IOWriteBandwidthMax=</varname> instead.</para>
|
||||
|
@ -68,8 +68,8 @@
|
||||
or it must be a template unit named the same way. Example: a
|
||||
socket file <filename>foo.socket</filename> needs a matching
|
||||
service <filename>foo.service</filename> if
|
||||
<option>Accept=false</option> is set. If
|
||||
<option>Accept=true</option> is set, a service template file
|
||||
<option>Accept=no</option> is set. If
|
||||
<option>Accept=yes</option> is set, a service template file
|
||||
<filename>foo@.service</filename> must exist from which services
|
||||
are instantiated for each incoming connection.</para>
|
||||
|
||||
@ -395,17 +395,17 @@
|
||||
incoming traffic. Defaults to <option>false</option>. For
|
||||
performance reasons, it is recommended to write new daemons
|
||||
only in a way that is suitable for
|
||||
<option>Accept=false</option>. A daemon listening on an
|
||||
<option>Accept=no</option>. A daemon listening on an
|
||||
<constant>AF_UNIX</constant> socket may, but does not need to,
|
||||
call
|
||||
<citerefentry><refentrytitle>close</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
on the received socket before exiting. However, it must not
|
||||
unlink the socket from a file system. It should not invoke
|
||||
<citerefentry><refentrytitle>shutdown</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
on sockets it got with <varname>Accept=false</varname>, but it
|
||||
on sockets it got with <varname>Accept=no</varname>, but it
|
||||
may do so for sockets it got with
|
||||
<varname>Accept=true</varname> set. Setting
|
||||
<varname>Accept=true</varname> is mostly useful to allow
|
||||
<varname>Accept=yes</varname> set. Setting
|
||||
<varname>Accept=yes</varname> is mostly useful to allow
|
||||
daemons designed for usage with
|
||||
<citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
to work unmodified with systemd socket
|
||||
@ -429,11 +429,11 @@
|
||||
<term><varname>MaxConnections=</varname></term>
|
||||
<listitem><para>The maximum number of connections to
|
||||
simultaneously run services instances for, when
|
||||
<option>Accept=true</option> is set. If more concurrent
|
||||
<option>Accept=yes</option> is set. If more concurrent
|
||||
connections are coming in, they will be refused until at least
|
||||
one existing connection is terminated. This setting has no
|
||||
effect on sockets configured with
|
||||
<option>Accept=false</option> or datagram sockets. Defaults to
|
||||
<option>Accept=no</option> or datagram sockets. Defaults to
|
||||
64.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
@ -1083,7 +1083,7 @@
|
||||
<term><varname>quiet</varname></term>
|
||||
|
||||
<listitem><para>Turn off status output at boot, much like
|
||||
<varname>systemd.show_status=false</varname> would. Note that
|
||||
<varname>systemd.show_status=no</varname> would. Note that
|
||||
this option is also read by the kernel itself and disables
|
||||
kernel log output. Passing this option hence turns off the
|
||||
usual output from both the system manager and the kernel.
|
||||
|
16
meson.build
16
meson.build
@ -1762,15 +1762,15 @@ if conf.get('ENABLE_LOGIND') == 1
|
||||
args : pam_systemd.full_path())
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
executable('systemd-user-runtime-dir',
|
||||
user_runtime_dir_sources,
|
||||
include_directories : includes,
|
||||
link_with : [libshared, liblogind_core],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
executable('systemd-user-runtime-dir',
|
||||
user_runtime_dir_sources,
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
endif
|
||||
|
||||
if conf.get('HAVE_PAM') == 1
|
||||
executable('systemd-user-sessions',
|
||||
|
@ -526,7 +526,9 @@ static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_erro
|
||||
if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
|
||||
job_type_is_conflicting(j->unit->job->type, j->type))
|
||||
return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
|
||||
"Transaction is destructive.");
|
||||
"Transaction for %s/%s is destructive (%s has '%s' job queued, but '%s' is included in transaction).",
|
||||
tr->anchor_job->unit->id, job_type_to_string(tr->anchor_job->type),
|
||||
j->unit->id, job_type_to_string(j->unit->job->type), job_type_to_string(j->type));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2000,7 +2000,7 @@ bool unit_is_unneeded(Unit *u) {
|
||||
* restart, then don't clean this one up. */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]], i) {
|
||||
if (u->job)
|
||||
if (other->job)
|
||||
return false;
|
||||
|
||||
if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
|
||||
|
@ -577,6 +577,8 @@ global:
|
||||
sd_bus_set_method_call_timeout;
|
||||
sd_bus_get_method_call_timeout;
|
||||
|
||||
sd_bus_error_move;
|
||||
|
||||
sd_device_ref;
|
||||
sd_device_unref;
|
||||
|
||||
|
@ -308,6 +308,28 @@ finish:
|
||||
return -bus_error_name_to_errno(e->name);
|
||||
}
|
||||
|
||||
_public_ int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e) {
|
||||
int r;
|
||||
|
||||
if (!sd_bus_error_is_set(e)) {
|
||||
|
||||
if (dest)
|
||||
*dest = SD_BUS_ERROR_NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = -bus_error_name_to_errno(e->name);
|
||||
|
||||
if (dest) {
|
||||
*dest = *e;
|
||||
*e = SD_BUS_ERROR_NULL;
|
||||
} else
|
||||
sd_bus_error_free(e);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
|
||||
if (!name)
|
||||
return 0;
|
||||
|
@ -5,6 +5,9 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/vt.h>
|
||||
#if ENABLE_UTMP
|
||||
#include <utmpx.h>
|
||||
#endif
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
@ -17,6 +20,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "logind.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
@ -29,6 +33,8 @@ void manager_reset_config(Manager *m) {
|
||||
m->reserve_vt = 6;
|
||||
m->remove_ipc = true;
|
||||
m->inhibit_delay_max = 5 * USEC_PER_SEC;
|
||||
m->user_stop_delay = 10 * USEC_PER_SEC;
|
||||
|
||||
m->handle_power_key = HANDLE_POWEROFF;
|
||||
m->handle_suspend_key = HANDLE_SUSPEND;
|
||||
m->handle_hibernate_key = HANDLE_HIBERNATE;
|
||||
@ -90,15 +96,16 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev
|
||||
|
||||
int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
|
||||
Seat *s;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(id);
|
||||
|
||||
s = hashmap_get(m->seats, id);
|
||||
if (!s) {
|
||||
s = seat_new(m, id);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
r = seat_new(&s, m, id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (_seat)
|
||||
@ -109,15 +116,16 @@ int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
|
||||
|
||||
int manager_add_session(Manager *m, const char *id, Session **_session) {
|
||||
Session *s;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(id);
|
||||
|
||||
s = hashmap_get(m->sessions, id);
|
||||
if (!s) {
|
||||
s = session_new(m, id);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
r = session_new(&s, m, id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (_session)
|
||||
@ -126,7 +134,14 @@ int manager_add_session(Manager *m, const char *id, Session **_session) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
|
||||
int manager_add_user(
|
||||
Manager *m,
|
||||
uid_t uid,
|
||||
gid_t gid,
|
||||
const char *name,
|
||||
const char *home,
|
||||
User **_user) {
|
||||
|
||||
User *u;
|
||||
int r;
|
||||
|
||||
@ -135,7 +150,7 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **
|
||||
|
||||
u = hashmap_get(m->users, UID_TO_PTR(uid));
|
||||
if (!u) {
|
||||
r = user_new(&u, m, uid, gid, name);
|
||||
r = user_new(&u, m, uid, gid, name, home);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -146,7 +161,12 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
|
||||
int manager_add_user_by_name(
|
||||
Manager *m,
|
||||
const char *name,
|
||||
User **_user) {
|
||||
|
||||
const char *home = NULL;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int r;
|
||||
@ -154,11 +174,11 @@ int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
|
||||
assert(m);
|
||||
assert(name);
|
||||
|
||||
r = get_user_creds(&name, &uid, &gid, NULL, NULL, 0);
|
||||
r = get_user_creds(&name, &uid, &gid, &home, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return manager_add_user(m, uid, gid, name, _user);
|
||||
return manager_add_user(m, uid, gid, name, home, _user);
|
||||
}
|
||||
|
||||
int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
|
||||
@ -171,7 +191,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
|
||||
if (!p)
|
||||
return errno > 0 ? -errno : -ENOENT;
|
||||
|
||||
return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
|
||||
return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user);
|
||||
}
|
||||
|
||||
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
|
||||
@ -335,13 +355,16 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **ret) {
|
||||
if (!pid_is_valid(pid))
|
||||
return -EINVAL;
|
||||
|
||||
r = cg_pid_get_unit(pid, &unit);
|
||||
if (r < 0)
|
||||
goto not_found;
|
||||
s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(pid));
|
||||
if (!s) {
|
||||
r = cg_pid_get_unit(pid, &unit);
|
||||
if (r < 0)
|
||||
goto not_found;
|
||||
|
||||
s = hashmap_get(m->session_units, unit);
|
||||
if (!s)
|
||||
goto not_found;
|
||||
s = hashmap_get(m->session_units, unit);
|
||||
if (!s)
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
*ret = s;
|
||||
@ -677,3 +700,142 @@ bool manager_all_buttons_ignored(Manager *m) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int manager_read_utmp(Manager *m) {
|
||||
#if ENABLE_UTMP
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (utmpxname(_PATH_UTMPX) < 0)
|
||||
return log_error_errno(errno, "Failed to set utmp path to " _PATH_UTMPX ": %m");
|
||||
|
||||
setutxent();
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
struct utmpx *u;
|
||||
const char *c;
|
||||
Session *s;
|
||||
|
||||
errno = 0;
|
||||
u = getutxent();
|
||||
if (!u) {
|
||||
if (errno != 0)
|
||||
log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m");
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (u->ut_type != USER_PROCESS)
|
||||
continue;
|
||||
|
||||
if (!pid_is_valid(u->ut_pid))
|
||||
continue;
|
||||
|
||||
t = strndup(u->ut_line, sizeof(u->ut_line));
|
||||
if (!t) {
|
||||
r = log_oom();
|
||||
break;
|
||||
}
|
||||
|
||||
c = path_startswith(t, "/dev/");
|
||||
if (c) {
|
||||
r = free_and_strdup(&t, c);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isempty(t))
|
||||
continue;
|
||||
|
||||
s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(u->ut_pid));
|
||||
if (!s)
|
||||
continue;
|
||||
|
||||
if (s->tty_validity == TTY_FROM_UTMP && !streq_ptr(s->tty, t)) {
|
||||
/* This may happen on multiplexed SSH connection (i.e. 'SSH connection sharing'). In
|
||||
* this case PAM and utmp sessions don't match. In such a case let's invalidate the TTY
|
||||
* information and never acquire it again. */
|
||||
|
||||
s->tty = mfree(s->tty);
|
||||
s->tty_validity = TTY_UTMP_INCONSISTENT;
|
||||
log_debug("Session '%s' has inconsistent TTY information, dropping TTY information.", s->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Never override what we figured out once */
|
||||
if (s->tty || s->tty_validity >= 0)
|
||||
continue;
|
||||
|
||||
s->tty = TAKE_PTR(t);
|
||||
s->tty_validity = TTY_FROM_UTMP;
|
||||
log_debug("Acquired TTY information '%s' from utmp for session '%s'.", s->tty, s->id);
|
||||
}
|
||||
|
||||
endutxent();
|
||||
return r;
|
||||
#else
|
||||
return 0
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_UTMP
|
||||
static int manager_dispatch_utmp(sd_event_source *s, const struct inotify_event *event, void *userdata) {
|
||||
Manager *m = userdata;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* If there's indication the file itself might have been removed or became otherwise unavailable, then let's
|
||||
* reestablish the watch on whatever there's now. */
|
||||
if ((event->mask & (IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF|IN_Q_OVERFLOW|IN_UNMOUNT)) != 0)
|
||||
manager_connect_utmp(m);
|
||||
|
||||
(void) manager_read_utmp(m);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void manager_connect_utmp(Manager *m) {
|
||||
#if ENABLE_UTMP
|
||||
sd_event_source *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Watch utmp for changes via inotify. We do this to deal with tools such as ssh, which will register the PAM
|
||||
* session early, and acquire a TTY only much later for the connection. Thus during PAM the TTY won't be known
|
||||
* yet. ssh will register itself with utmp when it finally acquired the TTY. Hence, let's make use of this, and
|
||||
* watch utmp for the TTY asynchronously. We use the PAM session's leader PID as key, to find the right entry.
|
||||
*
|
||||
* Yes, relying on utmp is pretty ugly, but it's good enough for informational purposes, as well as idle
|
||||
* detection (which, for tty sessions, relies on the TTY used) */
|
||||
|
||||
r = sd_event_add_inotify(m->event, &s, _PATH_UTMPX, IN_MODIFY|IN_MOVE_SELF|IN_DELETE_SELF|IN_ATTRIB, manager_dispatch_utmp, m);
|
||||
if (r < 0)
|
||||
log_full_errno(r == -ENOENT ? LOG_DEBUG: LOG_WARNING, r, "Failed to create inotify watch on " _PATH_UTMPX ", ignoring: %m");
|
||||
else {
|
||||
r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_IDLE);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to adjust utmp event source priority, ignoring: %m");
|
||||
|
||||
(void) sd_event_source_set_description(s, "utmp");
|
||||
}
|
||||
|
||||
sd_event_source_unref(m->utmp_event_source);
|
||||
m->utmp_event_source = s;
|
||||
#endif
|
||||
}
|
||||
|
||||
void manager_reconnect_utmp(Manager *m) {
|
||||
#if ENABLE_UTMP
|
||||
assert(m);
|
||||
|
||||
if (m->utmp_event_source)
|
||||
return;
|
||||
|
||||
manager_connect_utmp(m);
|
||||
#endif
|
||||
}
|
||||
|
@ -773,6 +773,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
|
||||
} while (hashmap_get(m->sessions, id));
|
||||
}
|
||||
|
||||
/* If we are not watching utmp aleady, try again */
|
||||
manager_reconnect_utmp(m);
|
||||
|
||||
r = manager_add_user_by_uid(m, uid, &user);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -782,9 +785,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
|
||||
goto fail;
|
||||
|
||||
session_set_user(session, user);
|
||||
session_set_leader(session, leader);
|
||||
|
||||
session->leader = leader;
|
||||
session->audit_id = audit_id;
|
||||
session->type = t;
|
||||
session->class = c;
|
||||
session->remote = remote;
|
||||
@ -796,6 +798,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
session->tty_validity = TTY_FROM_PAM;
|
||||
}
|
||||
|
||||
if (!isempty(display)) {
|
||||
@ -846,9 +850,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(sv)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto fail;
|
||||
|
||||
r = session_start(session, message);
|
||||
r = session_start(session, message, error);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -2648,6 +2652,7 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("HandleSuspendKey", "s", property_get_handle_action, offsetof(Manager, handle_suspend_key), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("HandleHibernateKey", "s", property_get_handle_action, offsetof(Manager, handle_hibernate_key), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -2728,24 +2733,20 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
};
|
||||
|
||||
static int session_jobs_reply(Session *s, const char *unit, const char *result) {
|
||||
int r = 0;
|
||||
|
||||
assert(s);
|
||||
assert(unit);
|
||||
|
||||
if (!s->started)
|
||||
return r;
|
||||
return 0;
|
||||
|
||||
if (streq(result, "done"))
|
||||
r = session_send_create_reply(s, NULL);
|
||||
else {
|
||||
if (result && !streq(result, "done")) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
|
||||
|
||||
sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
|
||||
r = session_send_create_reply(s, &e);
|
||||
sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit '%s' failed with '%s'", unit, result);
|
||||
return session_send_create_reply(s, &e);
|
||||
}
|
||||
|
||||
return r;
|
||||
return session_send_create_reply(s, NULL);
|
||||
}
|
||||
|
||||
int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
@ -2778,30 +2779,29 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
|
||||
}
|
||||
|
||||
session = hashmap_get(m->session_units, unit);
|
||||
if (session && streq_ptr(path, session->scope_job)) {
|
||||
session->scope_job = mfree(session->scope_job);
|
||||
session_jobs_reply(session, unit, result);
|
||||
if (session) {
|
||||
if (streq_ptr(path, session->scope_job)) {
|
||||
session->scope_job = mfree(session->scope_job);
|
||||
(void) session_jobs_reply(session, unit, result);
|
||||
|
||||
session_save(session);
|
||||
user_save(session->user);
|
||||
}
|
||||
|
||||
session_save(session);
|
||||
user_save(session->user);
|
||||
session_add_to_gc_queue(session);
|
||||
}
|
||||
|
||||
user = hashmap_get(m->user_units, unit);
|
||||
if (user &&
|
||||
(streq_ptr(path, user->service_job) ||
|
||||
streq_ptr(path, user->slice_job))) {
|
||||
|
||||
if (streq_ptr(path, user->service_job))
|
||||
if (user) {
|
||||
if (streq_ptr(path, user->service_job)) {
|
||||
user->service_job = mfree(user->service_job);
|
||||
|
||||
if (streq_ptr(path, user->slice_job))
|
||||
user->slice_job = mfree(user->slice_job);
|
||||
LIST_FOREACH(sessions_by_user, session, user->sessions)
|
||||
(void) session_jobs_reply(session, unit, NULL /* don't propagate user service failures to the client */);
|
||||
|
||||
LIST_FOREACH(sessions_by_user, session, user->sessions)
|
||||
session_jobs_reply(session, unit, result);
|
||||
user_save(user);
|
||||
}
|
||||
|
||||
user_save(user);
|
||||
user_add_to_gc_queue(user);
|
||||
}
|
||||
|
||||
@ -2933,13 +2933,15 @@ int manager_start_scope(
|
||||
pid_t pid,
|
||||
const char *slice,
|
||||
const char *description,
|
||||
const char *after,
|
||||
const char *after2,
|
||||
char **wants,
|
||||
char **after,
|
||||
const char *requires_mounts_for,
|
||||
sd_bus_message *more_properties,
|
||||
sd_bus_error *error,
|
||||
char **job) {
|
||||
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
@ -2977,14 +2979,20 @@ int manager_start_scope(
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!isempty(after)) {
|
||||
r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after);
|
||||
STRV_FOREACH(i, wants) {
|
||||
r = sd_bus_message_append(m, "(sv)", "Wants", "as", 1, *i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!isempty(after2)) {
|
||||
r = sd_bus_message_append(m, "(sv)", "After", "as", 1, after2);
|
||||
STRV_FOREACH(i, after) {
|
||||
r = sd_bus_message_append(m, "(sv)", "After", "as", 1, *i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!empty_or_root(requires_mounts_for)) {
|
||||
r = sd_bus_message_append(m, "(sv)", "RequiresMountsFor", "as", 1, requires_mounts_for);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -3081,7 +3089,8 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
|
||||
return strdup_job(reply, job);
|
||||
}
|
||||
|
||||
int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error) {
|
||||
int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *ret_error) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
int r;
|
||||
|
||||
@ -3098,17 +3107,16 @@ int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *err
|
||||
path,
|
||||
"org.freedesktop.systemd1.Scope",
|
||||
"Abandon",
|
||||
error,
|
||||
&error,
|
||||
NULL,
|
||||
NULL);
|
||||
if (r < 0) {
|
||||
if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
|
||||
sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED) ||
|
||||
sd_bus_error_has_name(error, BUS_ERROR_SCOPE_NOT_RUNNING)) {
|
||||
sd_bus_error_free(error);
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
|
||||
sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED) ||
|
||||
sd_bus_error_has_name(&error, BUS_ERROR_SCOPE_NOT_RUNNING))
|
||||
return 0;
|
||||
}
|
||||
|
||||
sd_bus_error_move(ret_error, &error);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -3130,7 +3138,7 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo
|
||||
"ssi", unit, who == KILL_LEADER ? "main" : "all", signo);
|
||||
}
|
||||
|
||||
int manager_unit_is_active(Manager *manager, const char *unit) {
|
||||
int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *ret_error) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
@ -3166,17 +3174,18 @@ int manager_unit_is_active(Manager *manager, const char *unit) {
|
||||
sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
|
||||
return false;
|
||||
|
||||
sd_bus_error_move(ret_error, &error);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_read(reply, "s", &state);
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
return r;
|
||||
|
||||
return !streq(state, "inactive") && !streq(state, "failed");
|
||||
return !STR_IN_SET(state, "inactive", "failed");
|
||||
}
|
||||
|
||||
int manager_job_is_active(Manager *manager, const char *path) {
|
||||
int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *ret_error) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
int r;
|
||||
@ -3201,6 +3210,7 @@ int manager_job_is_active(Manager *manager, const char *path) {
|
||||
if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
|
||||
return false;
|
||||
|
||||
sd_bus_error_move(ret_error, &error);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ Login.KillUserProcesses, config_parse_bool, 0, offse
|
||||
Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users)
|
||||
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)
|
||||
Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max)
|
||||
Login.UserStopDelaySec, config_parse_sec, 0, offsetof(Manager, user_stop_delay)
|
||||
Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key)
|
||||
Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key)
|
||||
Login.HandleHibernateKey, config_parse_handle_action, 0, offsetof(Manager, handle_hibernate_key)
|
||||
|
@ -21,33 +21,42 @@
|
||||
#include "terminal-util.h"
|
||||
#include "util.h"
|
||||
|
||||
Seat *seat_new(Manager *m, const char *id) {
|
||||
Seat *s;
|
||||
int seat_new(Seat** ret, Manager *m, const char *id) {
|
||||
_cleanup_(seat_freep) Seat *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(m);
|
||||
assert(id);
|
||||
|
||||
s = new0(Seat, 1);
|
||||
if (!seat_name_is_valid(id))
|
||||
return -EINVAL;
|
||||
|
||||
s = new(Seat, 1);
|
||||
if (!s)
|
||||
return NULL;
|
||||
return -ENOMEM;
|
||||
|
||||
*s = (Seat) {
|
||||
.manager = m,
|
||||
};
|
||||
|
||||
s->state_file = strappend("/run/systemd/seats/", id);
|
||||
if (!s->state_file)
|
||||
return mfree(s);
|
||||
return -ENOMEM;
|
||||
|
||||
s->id = basename(s->state_file);
|
||||
s->manager = m;
|
||||
|
||||
if (hashmap_put(m->seats, s->id, s) < 0) {
|
||||
free(s->state_file);
|
||||
return mfree(s);
|
||||
}
|
||||
r = hashmap_put(m->seats, s->id, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return s;
|
||||
*ret = TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void seat_free(Seat *s) {
|
||||
assert(s);
|
||||
Seat* seat_free(Seat *s) {
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
if (s->in_gc_queue)
|
||||
LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
|
||||
@ -64,7 +73,8 @@ void seat_free(Seat *s) {
|
||||
|
||||
free(s->positions);
|
||||
free(s->state_file);
|
||||
free(s);
|
||||
|
||||
return mfree(s);
|
||||
}
|
||||
|
||||
int seat_save(Seat *s) {
|
||||
@ -165,7 +175,7 @@ static int vt_allocate(unsigned int vtnr) {
|
||||
xsprintf(p, "/dev/tty%u", vtnr);
|
||||
fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
return fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -189,10 +199,8 @@ int seat_preallocate_vts(Seat *s) {
|
||||
int q;
|
||||
|
||||
q = vt_allocate(i);
|
||||
if (q < 0) {
|
||||
log_error_errno(q, "Failed to preallocate VT %u: %m", i);
|
||||
r = q;
|
||||
}
|
||||
if (q < 0)
|
||||
r = log_error_errno(q, "Failed to preallocate VT %u: %m", i);
|
||||
}
|
||||
|
||||
return r;
|
||||
@ -209,9 +217,9 @@ int seat_apply_acls(Seat *s, Session *old_active) {
|
||||
!!s->active, s->active ? s->active->user->uid : 0);
|
||||
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to apply ACLs: %m");
|
||||
return log_error_errno(r, "Failed to apply ACLs: %m");
|
||||
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int seat_set_active(Seat *s, Session *session) {
|
||||
@ -231,7 +239,7 @@ int seat_set_active(Seat *s, Session *session) {
|
||||
session_send_changed(old_active, "Active", NULL);
|
||||
}
|
||||
|
||||
seat_apply_acls(s, old_active);
|
||||
(void) seat_apply_acls(s, old_active);
|
||||
|
||||
if (session && session->started) {
|
||||
session_send_changed(session, "Active", NULL);
|
||||
@ -411,7 +419,7 @@ int seat_start(Seat *s) {
|
||||
}
|
||||
|
||||
int seat_stop(Seat *s, bool force) {
|
||||
int r = 0;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
@ -421,9 +429,9 @@ int seat_stop(Seat *s, bool force) {
|
||||
"SEAT_ID=%s", s->id,
|
||||
LOG_MESSAGE("Removed seat %s.", s->id));
|
||||
|
||||
seat_stop_sessions(s, force);
|
||||
r = seat_stop_sessions(s, force);
|
||||
|
||||
unlink(s->state_file);
|
||||
(void) unlink(s->state_file);
|
||||
seat_add_to_gc_queue(s);
|
||||
|
||||
if (s->started)
|
||||
|
@ -27,8 +27,10 @@ struct Seat {
|
||||
LIST_FIELDS(Seat, gc_queue);
|
||||
};
|
||||
|
||||
Seat *seat_new(Manager *m, const char *id);
|
||||
void seat_free(Seat *s);
|
||||
int seat_new(Seat **ret, Manager *m, const char *id);
|
||||
Seat* seat_free(Seat *s);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Seat *, seat_free);
|
||||
|
||||
int seat_save(Seat *s);
|
||||
int seat_load(Seat *s);
|
||||
|
@ -689,6 +689,15 @@ int session_send_lock_all(Manager *m, bool lock) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool session_ready(Session *s) {
|
||||
assert(s);
|
||||
|
||||
/* Returns true when the session is ready, i.e. all jobs we enqueued for it are done (regardless if successful or not) */
|
||||
|
||||
return !s->scope_job &&
|
||||
!s->user->service_job;
|
||||
}
|
||||
|
||||
int session_send_create_reply(Session *s, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
|
||||
_cleanup_close_ int fifo_fd = -1;
|
||||
@ -696,19 +705,16 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
|
||||
|
||||
assert(s);
|
||||
|
||||
/* This is called after the session scope and the user service
|
||||
* were successfully created, and finishes where
|
||||
/* This is called after the session scope and the user service were successfully created, and finishes where
|
||||
* bus_manager_create_session() left off. */
|
||||
|
||||
if (!s->create_message)
|
||||
return 0;
|
||||
|
||||
if (!sd_bus_error_is_set(error) && (s->scope_job || s->user->service_job))
|
||||
if (!sd_bus_error_is_set(error) && !session_ready(s))
|
||||
return 0;
|
||||
|
||||
c = s->create_message;
|
||||
s->create_message = NULL;
|
||||
|
||||
c = TAKE_PTR(s->create_message);
|
||||
if (error)
|
||||
return sd_bus_reply_method_error(c, error);
|
||||
|
||||
@ -716,8 +722,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
|
||||
if (fifo_fd < 0)
|
||||
return fifo_fd;
|
||||
|
||||
/* Update the session state file before we notify the client
|
||||
* about the result. */
|
||||
/* Update the session state file before we notify the client about the result. */
|
||||
session_save(s);
|
||||
|
||||
p = session_bus_path(s);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <linux/kd.h>
|
||||
#include <linux/vt.h>
|
||||
#include <signal.h>
|
||||
#include <stdio_ext.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
@ -24,57 +25,63 @@
|
||||
#include "mkdir.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "string-table.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
#include "process-util.h"
|
||||
|
||||
#define RELEASE_USEC (20*USEC_PER_SEC)
|
||||
|
||||
static void session_remove_fifo(Session *s);
|
||||
|
||||
Session* session_new(Manager *m, const char *id) {
|
||||
Session *s;
|
||||
int session_new(Session **ret, Manager *m, const char *id) {
|
||||
_cleanup_(session_freep) Session *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(m);
|
||||
assert(id);
|
||||
assert(session_id_valid(id));
|
||||
|
||||
s = new0(Session, 1);
|
||||
if (!session_id_valid(id))
|
||||
return -EINVAL;
|
||||
|
||||
s = new(Session, 1);
|
||||
if (!s)
|
||||
return NULL;
|
||||
return -ENOMEM;
|
||||
|
||||
*s = (Session) {
|
||||
.manager = m,
|
||||
.fifo_fd = -1,
|
||||
.vtfd = -1,
|
||||
.audit_id = AUDIT_SESSION_INVALID,
|
||||
.tty_validity = _TTY_VALIDITY_INVALID,
|
||||
};
|
||||
|
||||
s->state_file = strappend("/run/systemd/sessions/", id);
|
||||
if (!s->state_file)
|
||||
return mfree(s);
|
||||
|
||||
s->devices = hashmap_new(&devt_hash_ops);
|
||||
if (!s->devices) {
|
||||
free(s->state_file);
|
||||
return mfree(s);
|
||||
}
|
||||
return -ENOMEM;
|
||||
|
||||
s->id = basename(s->state_file);
|
||||
|
||||
if (hashmap_put(m->sessions, s->id, s) < 0) {
|
||||
hashmap_free(s->devices);
|
||||
free(s->state_file);
|
||||
return mfree(s);
|
||||
}
|
||||
s->devices = hashmap_new(&devt_hash_ops);
|
||||
if (!s->devices)
|
||||
return -ENOMEM;
|
||||
|
||||
s->manager = m;
|
||||
s->fifo_fd = -1;
|
||||
s->vtfd = -1;
|
||||
s->audit_id = AUDIT_SESSION_INVALID;
|
||||
r = hashmap_put(m->sessions, s->id, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return s;
|
||||
*ret = TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void session_free(Session *s) {
|
||||
Session* session_free(Session *s) {
|
||||
SessionDevice *sd;
|
||||
|
||||
assert(s);
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
if (s->in_gc_queue)
|
||||
LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s);
|
||||
@ -95,6 +102,8 @@ void session_free(Session *s) {
|
||||
|
||||
if (s->user->display == s)
|
||||
s->user->display = NULL;
|
||||
|
||||
user_update_last_session_timer(s->user);
|
||||
}
|
||||
|
||||
if (s->seat) {
|
||||
@ -112,6 +121,9 @@ void session_free(Session *s) {
|
||||
free(s->scope);
|
||||
}
|
||||
|
||||
if (pid_is_valid(s->leader))
|
||||
(void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
|
||||
|
||||
free(s->scope_job);
|
||||
|
||||
sd_bus_message_unref(s->create_message);
|
||||
@ -126,7 +138,8 @@ void session_free(Session *s) {
|
||||
hashmap_remove(s->manager->sessions, s->id);
|
||||
|
||||
free(s->state_file);
|
||||
free(s);
|
||||
|
||||
return mfree(s);
|
||||
}
|
||||
|
||||
void session_set_user(Session *s, User *u) {
|
||||
@ -135,6 +148,32 @@ void session_set_user(Session *s, User *u) {
|
||||
|
||||
s->user = u;
|
||||
LIST_PREPEND(sessions_by_user, u->sessions, s);
|
||||
|
||||
user_update_last_session_timer(u);
|
||||
}
|
||||
|
||||
int session_set_leader(Session *s, pid_t pid) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!pid_is_valid(pid))
|
||||
return -EINVAL;
|
||||
|
||||
if (s->leader == pid)
|
||||
return 0;
|
||||
|
||||
r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pid), s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (pid_is_valid(s->leader))
|
||||
(void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
|
||||
|
||||
s->leader = pid;
|
||||
(void) audit_session_from_pid(pid, &s->audit_id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void session_save_devices(Session *s, FILE *f) {
|
||||
@ -170,20 +209,21 @@ int session_save(Session *s) {
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
assert(s->user);
|
||||
|
||||
fchmod(fileno(f), 0644);
|
||||
(void) __fsetlocking(f, FSETLOCKING_BYCALLER);
|
||||
(void) fchmod(fileno(f), 0644);
|
||||
|
||||
fprintf(f,
|
||||
"# This is private data. Do not parse.\n"
|
||||
"UID="UID_FMT"\n"
|
||||
"USER=%s\n"
|
||||
"ACTIVE=%i\n"
|
||||
"IS_DISPLAY=%i\n"
|
||||
"STATE=%s\n"
|
||||
"REMOTE=%i\n",
|
||||
s->user->uid,
|
||||
s->user->name,
|
||||
session_is_active(s),
|
||||
s->user->display == s,
|
||||
session_state_to_string(session_get_state(s)),
|
||||
s->remote);
|
||||
|
||||
@ -207,6 +247,9 @@ int session_save(Session *s) {
|
||||
if (s->tty)
|
||||
fprintf(f, "TTY=%s\n", s->tty);
|
||||
|
||||
if (s->tty_validity >= 0)
|
||||
fprintf(f, "TTY_VALIDITY=%s\n", tty_validity_to_string(s->tty_validity));
|
||||
|
||||
if (s->display)
|
||||
fprintf(f, "DISPLAY=%s\n", s->display);
|
||||
|
||||
@ -343,6 +386,7 @@ static int session_load_devices(Session *s, const char *devices) {
|
||||
int session_load(Session *s) {
|
||||
_cleanup_free_ char *remote = NULL,
|
||||
*seat = NULL,
|
||||
*tty_validity = NULL,
|
||||
*vtnr = NULL,
|
||||
*state = NULL,
|
||||
*position = NULL,
|
||||
@ -354,7 +398,8 @@ int session_load(Session *s) {
|
||||
*monotonic = NULL,
|
||||
*controller = NULL,
|
||||
*active = NULL,
|
||||
*devices = NULL;
|
||||
*devices = NULL,
|
||||
*is_display = NULL;
|
||||
|
||||
int k, r;
|
||||
|
||||
@ -367,6 +412,7 @@ int session_load(Session *s) {
|
||||
"FIFO", &s->fifo_path,
|
||||
"SEAT", &seat,
|
||||
"TTY", &s->tty,
|
||||
"TTY_VALIDITY", &tty_validity,
|
||||
"DISPLAY", &s->display,
|
||||
"REMOTE_HOST", &s->remote_host,
|
||||
"REMOTE_USER", &s->remote_user,
|
||||
@ -384,6 +430,7 @@ int session_load(Session *s) {
|
||||
"CONTROLLER", &controller,
|
||||
"ACTIVE", &active,
|
||||
"DEVICES", &devices,
|
||||
"IS_DISPLAY", &is_display,
|
||||
NULL);
|
||||
|
||||
if (r < 0)
|
||||
@ -442,9 +489,27 @@ int session_load(Session *s) {
|
||||
seat_claim_position(s->seat, s, npos);
|
||||
}
|
||||
|
||||
if (tty_validity) {
|
||||
TTYValidity v;
|
||||
|
||||
v = tty_validity_from_string(tty_validity);
|
||||
if (v < 0)
|
||||
log_debug("Failed to parse TTY validity: %s", tty_validity);
|
||||
else
|
||||
s->tty_validity = v;
|
||||
}
|
||||
|
||||
if (leader) {
|
||||
if (parse_pid(leader, &s->leader) >= 0)
|
||||
(void) audit_session_from_pid(s->leader, &s->audit_id);
|
||||
pid_t pid;
|
||||
|
||||
r = parse_pid(leader, &pid);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse leader PID of session: %s", leader);
|
||||
else {
|
||||
r = session_set_leader(s, pid);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set session leader PID, ignoring: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (type) {
|
||||
@ -491,6 +556,18 @@ int session_load(Session *s) {
|
||||
s->was_active = k;
|
||||
}
|
||||
|
||||
if (is_display) {
|
||||
/* Note that when enumerating users are loaded before sessions, hence the display session to use is
|
||||
* something we have to store along with the session and not the user, as in that case we couldn't
|
||||
* apply it at the time we load the user. */
|
||||
|
||||
k = parse_boolean(is_display);
|
||||
if (k < 0)
|
||||
log_warning_errno(k, "Failed to parse IS_DISPLAY session property: %m");
|
||||
else if (k > 0)
|
||||
s->user->display = s;
|
||||
}
|
||||
|
||||
if (controller) {
|
||||
if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) {
|
||||
session_set_controller(s, controller, false, false);
|
||||
@ -516,7 +593,7 @@ int session_activate(Session *s) {
|
||||
|
||||
/* on seats with VTs, we let VTs manage session-switching */
|
||||
if (seat_has_vts(s->seat)) {
|
||||
if (!s->vtnr)
|
||||
if (s->vtnr == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return chvt(s->vtnr);
|
||||
@ -539,18 +616,18 @@ int session_activate(Session *s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_start_scope(Session *s, sd_bus_message *properties) {
|
||||
static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(s->user);
|
||||
|
||||
if (!s->scope) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_free_ char *scope = NULL;
|
||||
char *job = NULL;
|
||||
const char *description;
|
||||
|
||||
s->scope_job = mfree(s->scope_job);
|
||||
|
||||
scope = strjoin("session-", s->id, ".scope");
|
||||
if (!scope)
|
||||
return log_oom();
|
||||
@ -563,17 +640,16 @@ static int session_start_scope(Session *s, sd_bus_message *properties) {
|
||||
s->leader,
|
||||
s->user->slice,
|
||||
description,
|
||||
"systemd-logind.service",
|
||||
"systemd-user-sessions.service",
|
||||
STRV_MAKE(s->user->runtime_dir_service, s->user->service), /* These two have StopWhenUnneeded= set, hence add a dep towards them */
|
||||
STRV_MAKE("systemd-logind.service", "systemd-user-sessions.service", s->user->runtime_dir_service, s->user->service), /* And order us after some more */
|
||||
s->user->home,
|
||||
properties,
|
||||
&error,
|
||||
&job);
|
||||
error,
|
||||
&s->scope_job);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(&error, r));
|
||||
|
||||
return log_error_errno(r, "Failed to start session scope %s: %s", scope, bus_error_message(error, r));
|
||||
|
||||
s->scope = TAKE_PTR(scope);
|
||||
free_and_replace(s->scope_job, job);
|
||||
}
|
||||
|
||||
if (s->scope)
|
||||
@ -582,7 +658,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int session_start(Session *s, sd_bus_message *properties) {
|
||||
int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
@ -590,6 +666,9 @@ int session_start(Session *s, sd_bus_message *properties) {
|
||||
if (!s->user)
|
||||
return -ESTALE;
|
||||
|
||||
if (s->stopping)
|
||||
return -EINVAL;
|
||||
|
||||
if (s->started)
|
||||
return 0;
|
||||
|
||||
@ -597,8 +676,7 @@ int session_start(Session *s, sd_bus_message *properties) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Create cgroup */
|
||||
r = session_start_scope(s, properties);
|
||||
r = session_start_scope(s, properties, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -649,21 +727,24 @@ static int session_stop_scope(Session *s, bool force) {
|
||||
* that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log
|
||||
* when killing any processes left after this point. */
|
||||
r = manager_abandon_scope(s->manager, s->scope, &error);
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r));
|
||||
sd_bus_error_free(&error);
|
||||
}
|
||||
|
||||
s->scope_job = mfree(s->scope_job);
|
||||
|
||||
/* Optionally, let's kill everything that's left now. */
|
||||
if (force || manager_shall_kill(s->manager, s->user->name)) {
|
||||
char *job = NULL;
|
||||
|
||||
r = manager_stop_unit(s->manager, s->scope, &error, &job);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
|
||||
r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job);
|
||||
if (r < 0) {
|
||||
if (force)
|
||||
return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
|
||||
|
||||
free(s->scope_job);
|
||||
s->scope_job = job;
|
||||
log_warning_errno(r, "Failed to stop session scope, ignoring: %s", bus_error_message(&error, r));
|
||||
}
|
||||
} else {
|
||||
s->scope_job = mfree(s->scope_job);
|
||||
|
||||
/* With no killing, this session is allowed to persist in "closing" state indefinitely.
|
||||
* Therefore session stop and session removal may be two distinct events.
|
||||
@ -683,8 +764,17 @@ int session_stop(Session *s, bool force) {
|
||||
|
||||
assert(s);
|
||||
|
||||
/* This is called whenever we begin with tearing down a session record. It's called in four cases: explicit API
|
||||
* request via the bus (either directly for the session object or for the seat or user object this session
|
||||
* belongs to; 'force' is true), or due to automatic GC (i.e. scope vanished; 'force' is false), or because the
|
||||
* session FIFO saw an EOF ('force' is false), or because the release timer hit ('force' is false). */
|
||||
|
||||
if (!s->user)
|
||||
return -ESTALE;
|
||||
if (!s->started)
|
||||
return 0;
|
||||
if (s->stopping)
|
||||
return 0;
|
||||
|
||||
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
|
||||
|
||||
@ -776,7 +866,7 @@ int session_release(Session *s) {
|
||||
return sd_event_add_time(s->manager->event,
|
||||
&s->timer_event_source,
|
||||
CLOCK_MONOTONIC,
|
||||
now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
|
||||
usec_add(now(CLOCK_MONOTONIC), RELEASE_USEC), 0,
|
||||
release_timeout_callback, s);
|
||||
}
|
||||
|
||||
@ -855,7 +945,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
|
||||
|
||||
/* For sessions with a leader but no explicitly configured
|
||||
* tty, let's check the controlling tty of the leader */
|
||||
if (s->leader > 0) {
|
||||
if (pid_is_valid(s->leader)) {
|
||||
r = get_process_ctty_atime(s->leader, &atime);
|
||||
if (r >= 0)
|
||||
goto found_atime;
|
||||
@ -939,7 +1029,8 @@ int session_create_fifo(Session *s) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
|
||||
s->fifo_path = strjoin("/run/systemd/sessions/", s->id, ".ref");
|
||||
if (!s->fifo_path)
|
||||
return -ENOMEM;
|
||||
|
||||
if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
|
||||
@ -951,7 +1042,6 @@ int session_create_fifo(Session *s) {
|
||||
s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
|
||||
if (s->fifo_fd < 0)
|
||||
return -errno;
|
||||
|
||||
}
|
||||
|
||||
if (!s->fifo_event_source) {
|
||||
@ -981,12 +1071,14 @@ static void session_remove_fifo(Session *s) {
|
||||
s->fifo_fd = safe_close(s->fifo_fd);
|
||||
|
||||
if (s->fifo_path) {
|
||||
unlink(s->fifo_path);
|
||||
(void) unlink(s->fifo_path);
|
||||
s->fifo_path = mfree(s->fifo_path);
|
||||
}
|
||||
}
|
||||
|
||||
bool session_may_gc(Session *s, bool drop_not_started) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (drop_not_started && !s->started)
|
||||
@ -1000,11 +1092,25 @@ bool session_may_gc(Session *s, bool drop_not_started) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->scope_job && manager_job_is_active(s->manager, s->scope_job))
|
||||
return false;
|
||||
if (s->scope_job) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
if (s->scope && manager_unit_is_active(s->manager, s->scope))
|
||||
return false;
|
||||
r = manager_job_is_active(s->manager, s->scope_job, &error);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", s->scope_job, bus_error_message(&error, r));
|
||||
if (r != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->scope) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
r = manager_unit_is_active(s->manager, s->scope, &error);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", s->scope, bus_error_message(&error, r));
|
||||
if (r != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1309,3 +1415,11 @@ static const char* const kill_who_table[_KILL_WHO_MAX] = {
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
|
||||
|
||||
static const char* const tty_validity_table[_TTY_VALIDITY_MAX] = {
|
||||
[TTY_FROM_PAM] = "from-pam",
|
||||
[TTY_FROM_UTMP] = "from-utmp",
|
||||
[TTY_UTMP_INCONSISTENT] = "utmp-inconsistent",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(tty_validity, TTYValidity);
|
||||
|
@ -46,6 +46,14 @@ enum KillWho {
|
||||
_KILL_WHO_INVALID = -1
|
||||
};
|
||||
|
||||
typedef enum TTYValidity {
|
||||
TTY_FROM_PAM,
|
||||
TTY_FROM_UTMP,
|
||||
TTY_UTMP_INCONSISTENT, /* may happen on ssh sessions with multiplexed TTYs */
|
||||
_TTY_VALIDITY_MAX,
|
||||
_TTY_VALIDITY_INVALID = -1,
|
||||
} TTYValidity;
|
||||
|
||||
struct Session {
|
||||
Manager *manager;
|
||||
|
||||
@ -60,8 +68,9 @@ struct Session {
|
||||
|
||||
dual_timestamp timestamp;
|
||||
|
||||
char *tty;
|
||||
char *display;
|
||||
char *tty;
|
||||
TTYValidity tty_validity;
|
||||
|
||||
bool remote;
|
||||
char *remote_user;
|
||||
@ -97,6 +106,7 @@ struct Session {
|
||||
|
||||
sd_bus_message *create_message;
|
||||
|
||||
/* Set up when a client requested to release the session via the bus */
|
||||
sd_event_source *timer_event_source;
|
||||
|
||||
char *controller;
|
||||
@ -109,9 +119,13 @@ struct Session {
|
||||
LIST_FIELDS(Session, gc_queue);
|
||||
};
|
||||
|
||||
Session *session_new(Manager *m, const char *id);
|
||||
void session_free(Session *s);
|
||||
int session_new(Session **ret, Manager *m, const char *id);
|
||||
Session* session_free(Session *s);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Session *, session_free);
|
||||
|
||||
void session_set_user(Session *s, User *u);
|
||||
int session_set_leader(Session *s, pid_t pid);
|
||||
bool session_may_gc(Session *s, bool drop_not_started);
|
||||
void session_add_to_gc_queue(Session *s);
|
||||
int session_activate(Session *s);
|
||||
@ -121,7 +135,7 @@ void session_set_idle_hint(Session *s, bool b);
|
||||
int session_get_locked_hint(Session *s);
|
||||
void session_set_locked_hint(Session *s, bool b);
|
||||
int session_create_fifo(Session *s);
|
||||
int session_start(Session *s, sd_bus_message *properties);
|
||||
int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error);
|
||||
int session_stop(Session *s, bool force);
|
||||
int session_finalize(Session *s);
|
||||
int session_release(Session *s);
|
||||
@ -155,6 +169,9 @@ SessionClass session_class_from_string(const char *s) _pure_;
|
||||
const char *kill_who_to_string(KillWho k) _const_;
|
||||
KillWho kill_who_from_string(const char *s) _pure_;
|
||||
|
||||
const char* tty_validity_to_string(TTYValidity t) _const_;
|
||||
TTYValidity tty_validity_from_string(const char *s) _pure_;
|
||||
|
||||
int session_prepare_vt(Session *s);
|
||||
void session_restore_vt(Session *s);
|
||||
void session_leave_vt(Session *s);
|
||||
|
@ -109,7 +109,7 @@ static int property_get_idle_since_hint(
|
||||
assert(reply);
|
||||
assert(u);
|
||||
|
||||
user_get_idle_hint(u, &t);
|
||||
(void) user_get_idle_hint(u, &t);
|
||||
k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
|
||||
|
||||
return sd_bus_message_append(reply, "t", k);
|
||||
|
@ -26,38 +26,52 @@
|
||||
#include "special.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
#include "strv.h"
|
||||
#include "unit-name.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
|
||||
int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
|
||||
int user_new(User **ret,
|
||||
Manager *m,
|
||||
uid_t uid,
|
||||
gid_t gid,
|
||||
const char *name,
|
||||
const char *home) {
|
||||
|
||||
_cleanup_(user_freep) User *u = NULL;
|
||||
char lu[DECIMAL_STR_MAX(uid_t) + 1];
|
||||
int r;
|
||||
|
||||
assert(out);
|
||||
assert(ret);
|
||||
assert(m);
|
||||
assert(name);
|
||||
|
||||
u = new0(User, 1);
|
||||
u = new(User, 1);
|
||||
if (!u)
|
||||
return -ENOMEM;
|
||||
|
||||
u->manager = m;
|
||||
u->uid = uid;
|
||||
u->gid = gid;
|
||||
xsprintf(lu, UID_FMT, uid);
|
||||
*u = (User) {
|
||||
.manager = m,
|
||||
.uid = uid,
|
||||
.gid = gid,
|
||||
.last_session_timestamp = USEC_INFINITY,
|
||||
};
|
||||
|
||||
u->name = strdup(name);
|
||||
if (!u->name)
|
||||
return -ENOMEM;
|
||||
|
||||
u->home = strdup(home);
|
||||
if (!u->home)
|
||||
return -ENOMEM;
|
||||
|
||||
if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
xsprintf(lu, UID_FMT, uid);
|
||||
r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -66,6 +80,10 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_name_build("user-runtime-dir", lu, ".service", &u->runtime_dir_service);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_put(m->users, UID_TO_PTR(uid), u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -78,8 +96,11 @@ int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*out = TAKE_PTR(u);
|
||||
r = hashmap_put(m->user_units, u->runtime_dir_service, u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -96,19 +117,25 @@ User *user_free(User *u) {
|
||||
if (u->service)
|
||||
hashmap_remove_value(u->manager->user_units, u->service, u);
|
||||
|
||||
if (u->runtime_dir_service)
|
||||
hashmap_remove_value(u->manager->user_units, u->runtime_dir_service, u);
|
||||
|
||||
if (u->slice)
|
||||
hashmap_remove_value(u->manager->user_units, u->slice, u);
|
||||
|
||||
hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u);
|
||||
|
||||
u->slice_job = mfree(u->slice_job);
|
||||
(void) sd_event_source_unref(u->timer_event_source);
|
||||
|
||||
u->service_job = mfree(u->service_job);
|
||||
|
||||
u->service = mfree(u->service);
|
||||
u->runtime_dir_service = mfree(u->runtime_dir_service);
|
||||
u->slice = mfree(u->slice);
|
||||
u->runtime_path = mfree(u->runtime_path);
|
||||
u->state_file = mfree(u->state_file);
|
||||
u->name = mfree(u->name);
|
||||
u->home = mfree(u->home);
|
||||
|
||||
return mfree(u);
|
||||
}
|
||||
@ -135,9 +162,11 @@ static int user_save_internal(User *u) {
|
||||
fprintf(f,
|
||||
"# This is private data. Do not parse.\n"
|
||||
"NAME=%s\n"
|
||||
"STATE=%s\n",
|
||||
"STATE=%s\n" /* friendly user-facing state */
|
||||
"STOPPING=%s\n", /* low-level state */
|
||||
u->name,
|
||||
user_state_to_string(user_get_state(u)));
|
||||
user_state_to_string(user_get_state(u)),
|
||||
yes_no(u->stopping));
|
||||
|
||||
/* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */
|
||||
if (u->runtime_path)
|
||||
@ -146,9 +175,6 @@ static int user_save_internal(User *u) {
|
||||
if (u->service_job)
|
||||
fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
|
||||
|
||||
if (u->slice_job)
|
||||
fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
|
||||
|
||||
if (u->display)
|
||||
fprintf(f, "DISPLAY=%s\n", u->display->id);
|
||||
|
||||
@ -159,6 +185,10 @@ static int user_save_internal(User *u) {
|
||||
u->timestamp.realtime,
|
||||
u->timestamp.monotonic);
|
||||
|
||||
if (u->last_session_timestamp != USEC_INFINITY)
|
||||
fprintf(f, "LAST_SESSION_TIMESTAMP=" USEC_FMT "\n",
|
||||
u->last_session_timestamp);
|
||||
|
||||
if (u->sessions) {
|
||||
Session *i;
|
||||
bool first;
|
||||
@ -272,103 +302,83 @@ int user_save(User *u) {
|
||||
if (!u->started)
|
||||
return 0;
|
||||
|
||||
return user_save_internal (u);
|
||||
return user_save_internal(u);
|
||||
}
|
||||
|
||||
int user_load(User *u) {
|
||||
_cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
|
||||
Session *s = NULL;
|
||||
_cleanup_free_ char *realtime = NULL, *monotonic = NULL, *stopping = NULL, *last_session_timestamp = NULL;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
r = parse_env_file(NULL, u->state_file, NEWLINE,
|
||||
"SERVICE_JOB", &u->service_job,
|
||||
"SLICE_JOB", &u->slice_job,
|
||||
"DISPLAY", &display,
|
||||
"REALTIME", &realtime,
|
||||
"MONOTONIC", &monotonic,
|
||||
"SERVICE_JOB", &u->service_job,
|
||||
"STOPPING", &stopping,
|
||||
"REALTIME", &realtime,
|
||||
"MONOTONIC", &monotonic,
|
||||
"LAST_SESSION_TIMESTAMP", &last_session_timestamp,
|
||||
NULL);
|
||||
if (r < 0) {
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read %s: %m", u->state_file);
|
||||
|
||||
if (stopping) {
|
||||
r = parse_boolean(stopping);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse 'STOPPING' boolean: %s", stopping);
|
||||
else
|
||||
u->stopping = r;
|
||||
}
|
||||
|
||||
if (display)
|
||||
s = hashmap_get(u->manager->sessions, display);
|
||||
|
||||
if (s && s->display && display_is_local(s->display))
|
||||
u->display = s;
|
||||
|
||||
if (realtime)
|
||||
timestamp_deserialize(realtime, &u->timestamp.realtime);
|
||||
(void) timestamp_deserialize(realtime, &u->timestamp.realtime);
|
||||
if (monotonic)
|
||||
timestamp_deserialize(monotonic, &u->timestamp.monotonic);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int user_start_service(User *u) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
char *job;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
u->service_job = mfree(u->service_job);
|
||||
|
||||
r = manager_start_unit(
|
||||
u->manager,
|
||||
u->service,
|
||||
&error,
|
||||
&job);
|
||||
if (r < 0)
|
||||
/* we don't fail due to this, let's try to continue */
|
||||
log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r));
|
||||
else
|
||||
u->service_job = job;
|
||||
(void) timestamp_deserialize(monotonic, &u->timestamp.monotonic);
|
||||
if (last_session_timestamp)
|
||||
(void) timestamp_deserialize(last_session_timestamp, &u->last_session_timestamp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int user_start(User *u) {
|
||||
static void user_start_service(User *u) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
/* Start the service containing the "systemd --user" instance (user@.service). Note that we don't explicitly
|
||||
* start the per-user slice or the systemd-runtime-dir@.service instance, as those are pulled in both by
|
||||
* user@.service and the session scopes as dependencies. */
|
||||
|
||||
u->service_job = mfree(u->service_job);
|
||||
|
||||
r = manager_start_unit(u->manager, u->service, &error, &u->service_job);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
|
||||
}
|
||||
|
||||
int user_start(User *u) {
|
||||
assert(u);
|
||||
|
||||
if (u->started && !u->stopping)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If u->stopping is set, the user is marked for removal and the slice
|
||||
* and service stop-jobs are queued. We have to clear that flag before
|
||||
* queing the start-jobs again. If they succeed, the user object can be
|
||||
* re-used just fine (pid1 takes care of job-ordering and proper
|
||||
* restart), but if they fail, we want to force another user_stop() so
|
||||
* possibly pending units are stopped.
|
||||
* Note that we don't clear u->started, as we have no clue what state
|
||||
* the user is in on failure here. Hence, we pretend the user is
|
||||
* running so it will be properly taken down by GC. However, we clearly
|
||||
* return an error from user_start() in that case, so no further
|
||||
* reference to the user is taken.
|
||||
*/
|
||||
/* If u->stopping is set, the user is marked for removal and service stop-jobs are queued. We have to clear
|
||||
* that flag before queing the start-jobs again. If they succeed, the user object can be re-used just fine
|
||||
* (pid1 takes care of job-ordering and proper restart), but if they fail, we want to force another user_stop()
|
||||
* so possibly pending units are stopped. */
|
||||
u->stopping = false;
|
||||
|
||||
if (!u->started)
|
||||
log_debug("Starting services for new user %s.", u->name);
|
||||
|
||||
/* Save the user data so far, because pam_systemd will read the
|
||||
* XDG_RUNTIME_DIR out of it while starting up systemd --user.
|
||||
* We need to do user_save_internal() because we have not
|
||||
* "officially" started yet. */
|
||||
/* Save the user data so far, because pam_systemd will read the XDG_RUNTIME_DIR out of it while starting up
|
||||
* systemd --user. We need to do user_save_internal() because we have not "officially" started yet. */
|
||||
user_save_internal(u);
|
||||
|
||||
/* Spawn user systemd */
|
||||
r = user_start_service(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
/* Start user@UID.service */
|
||||
user_start_service(u);
|
||||
|
||||
if (!u->started) {
|
||||
if (!dual_timestamp_is_set(&u->timestamp))
|
||||
@ -383,60 +393,50 @@ int user_start(User *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int user_stop_slice(User *u) {
|
||||
static void user_stop_service(User *u) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
char *job;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(u->service);
|
||||
|
||||
r = manager_stop_unit(u->manager, u->slice, &error, &job);
|
||||
/* The reverse of user_start_service(). Note that we only stop user@UID.service here, and let StopWhenUnneeded=
|
||||
* deal with the slice and the user-runtime-dir@.service instance. */
|
||||
|
||||
u->service_job = mfree(u->service_job);
|
||||
|
||||
r = manager_stop_unit(u->manager, u->service, &error, &u->service_job);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to stop user slice: %s", bus_error_message(&error, r));
|
||||
|
||||
return free_and_replace(u->slice_job, job);
|
||||
}
|
||||
|
||||
static int user_stop_service(User *u) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
char *job;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
r = manager_stop_unit(u->manager, u->service, &error, &job);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to stop user service: %s", bus_error_message(&error, r));
|
||||
|
||||
return free_and_replace(u->service_job, job);
|
||||
log_warning_errno(r, "Failed to stop user service '%s', ignoring: %s", u->service, bus_error_message(&error, r));
|
||||
}
|
||||
|
||||
int user_stop(User *u, bool force) {
|
||||
Session *s;
|
||||
int r = 0, k;
|
||||
int r = 0;
|
||||
assert(u);
|
||||
|
||||
/* Stop jobs have already been queued */
|
||||
if (u->stopping) {
|
||||
/* This is called whenever we begin with tearing down a user record. It's called in two cases: explicit API
|
||||
* request to do so via the bus (in which case 'force' is true) and automatically due to GC, if there's no
|
||||
* session left pinning it (in which case 'force' is false). Note that this just initiates tearing down of the
|
||||
* user, the User object will remain in memory until user_finalize() is called, see below. */
|
||||
|
||||
if (!u->started)
|
||||
return 0;
|
||||
|
||||
if (u->stopping) { /* Stop jobs have already been queued */
|
||||
user_save(u);
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIST_FOREACH(sessions_by_user, s, u->sessions) {
|
||||
int k;
|
||||
|
||||
k = session_stop(s, force);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
/* Kill systemd */
|
||||
k = user_stop_service(u);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
/* Kill cgroup */
|
||||
k = user_stop_slice(u);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
user_stop_service(u);
|
||||
|
||||
u->stopping = true;
|
||||
|
||||
@ -451,6 +451,9 @@ int user_finalize(User *u) {
|
||||
|
||||
assert(u);
|
||||
|
||||
/* Called when the user is really ready to be freed, i.e. when all unit stop jobs and suchlike for it are
|
||||
* done. This is called as a result of an earlier user_done() when all jobs are completed. */
|
||||
|
||||
if (u->started)
|
||||
log_debug("User %s logged out.", u->name);
|
||||
|
||||
@ -472,7 +475,7 @@ int user_finalize(User *u) {
|
||||
r = k;
|
||||
}
|
||||
|
||||
unlink(u->state_file);
|
||||
(void) unlink(u->state_file);
|
||||
user_add_to_gc_queue(u);
|
||||
|
||||
if (u->started) {
|
||||
@ -528,11 +531,40 @@ int user_check_linger_file(User *u) {
|
||||
return -ENOMEM;
|
||||
|
||||
p = strjoina("/var/lib/systemd/linger/", cc);
|
||||
if (access(p, F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
return access(p, F_OK) >= 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool user_unit_active(User *u) {
|
||||
const char *i;
|
||||
int r;
|
||||
|
||||
assert(u->service);
|
||||
assert(u->runtime_dir_service);
|
||||
assert(u->slice);
|
||||
|
||||
FOREACH_STRING(i, u->service, u->runtime_dir_service, u->slice) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
r = manager_unit_is_active(u->manager, i, &error);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", u->service, bus_error_message(&error, r));
|
||||
if (r != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool user_may_gc(User *u, bool drop_not_started) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (drop_not_started && !u->started)
|
||||
@ -541,14 +573,35 @@ bool user_may_gc(User *u, bool drop_not_started) {
|
||||
if (u->sessions)
|
||||
return false;
|
||||
|
||||
if (user_check_linger_file(u) > 0)
|
||||
if (u->last_session_timestamp != USEC_INFINITY) {
|
||||
/* All sessions have been closed. Let's see if we shall leave the user record around for a bit */
|
||||
|
||||
if (u->manager->user_stop_delay == USEC_INFINITY)
|
||||
return false; /* Leave it around forever! */
|
||||
if (u->manager->user_stop_delay > 0 &&
|
||||
now(CLOCK_MONOTONIC) < usec_add(u->last_session_timestamp, u->manager->user_stop_delay))
|
||||
return false; /* Leave it around for a bit longer. */
|
||||
}
|
||||
|
||||
/* Is this a user that shall stay around forever ("linger")? Before we say "no" to GC'ing for lingering users, let's check
|
||||
* if any of the three units that we maintain for this user is still around. If none of them is,
|
||||
* there's no need to keep this user around even if lingering is enabled. */
|
||||
if (user_check_linger_file(u) > 0 && user_unit_active(u))
|
||||
return false;
|
||||
|
||||
if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
|
||||
return false;
|
||||
/* Check if our job is still pending */
|
||||
if (u->service_job) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
if (u->service_job && manager_job_is_active(u->manager, u->service_job))
|
||||
return false;
|
||||
r = manager_job_is_active(u->manager, u->service_job, &error);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", u->service_job, bus_error_message(&error, r));
|
||||
if (r != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Note that we don't care if the three units we manage for each user object are up or not, as we are managing
|
||||
* their state rather than tracking it. */
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -571,7 +624,7 @@ UserState user_get_state(User *u) {
|
||||
if (u->stopping)
|
||||
return USER_CLOSING;
|
||||
|
||||
if (!u->started || u->slice_job || u->service_job)
|
||||
if (!u->started || u->service_job)
|
||||
return USER_OPENING;
|
||||
|
||||
if (u->sessions) {
|
||||
@ -590,7 +643,7 @@ UserState user_get_state(User *u) {
|
||||
return all_closing ? USER_CLOSING : USER_ONLINE;
|
||||
}
|
||||
|
||||
if (user_check_linger_file(u) > 0)
|
||||
if (user_check_linger_file(u) > 0 && user_unit_active(u))
|
||||
return USER_LINGERING;
|
||||
|
||||
return USER_CLOSING;
|
||||
@ -603,11 +656,10 @@ int user_kill(User *u, int signo) {
|
||||
}
|
||||
|
||||
static bool elect_display_filter(Session *s) {
|
||||
/* Return true if the session is a candidate for the user’s ‘primary
|
||||
* session’ or ‘display’. */
|
||||
/* Return true if the session is a candidate for the user’s ‘primary session’ or ‘display’. */
|
||||
assert(s);
|
||||
|
||||
return (s->class == SESSION_USER && !s->stopping);
|
||||
return s->class == SESSION_USER && s->started && !s->stopping;
|
||||
}
|
||||
|
||||
static int elect_display_compare(Session *s1, Session *s2) {
|
||||
@ -653,9 +705,8 @@ void user_elect_display(User *u) {
|
||||
|
||||
assert(u);
|
||||
|
||||
/* This elects a primary session for each user, which we call
|
||||
* the "display". We try to keep the assignment stable, but we
|
||||
* "upgrade" to better choices. */
|
||||
/* This elects a primary session for each user, which we call the "display". We try to keep the assignment
|
||||
* stable, but we "upgrade" to better choices. */
|
||||
log_debug("Electing new display for user %s", u->name);
|
||||
|
||||
LIST_FOREACH(sessions_by_user, s, u->sessions) {
|
||||
@ -671,6 +722,59 @@ void user_elect_display(User *u) {
|
||||
}
|
||||
}
|
||||
|
||||
static int user_stop_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
|
||||
User *u = userdata;
|
||||
|
||||
assert(u);
|
||||
user_add_to_gc_queue(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void user_update_last_session_timer(User *u) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (u->sessions) {
|
||||
/* There are sessions, turn off the timer */
|
||||
u->last_session_timestamp = USEC_INFINITY;
|
||||
u->timer_event_source = sd_event_source_unref(u->timer_event_source);
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->last_session_timestamp != USEC_INFINITY)
|
||||
return; /* Timer already started */
|
||||
|
||||
u->last_session_timestamp = now(CLOCK_MONOTONIC);
|
||||
|
||||
assert(!u->timer_event_source);
|
||||
|
||||
if (u->manager->user_stop_delay == 0 || u->manager->user_stop_delay == USEC_INFINITY)
|
||||
return;
|
||||
|
||||
if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) {
|
||||
log_debug("Not allocating user stop timeout, since we are already exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
r = sd_event_add_time(u->manager->event,
|
||||
&u->timer_event_source,
|
||||
CLOCK_MONOTONIC,
|
||||
usec_add(u->last_session_timestamp, u->manager->user_stop_delay), 0,
|
||||
user_stop_timeout_callback, u);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to enqueue user stop event source, ignoring: %m");
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
char s[FORMAT_TIMESPAN_MAX];
|
||||
|
||||
log_debug("Last session of user '%s' logged out, terminating user context in %s.",
|
||||
u->name,
|
||||
format_timespan(s, sizeof(s), u->manager->user_stop_delay, USEC_PER_MSEC));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* const user_state_table[_USER_STATE_MAX] = {
|
||||
[USER_OFFLINE] = "offline",
|
||||
[USER_OPENING] = "opening",
|
||||
|
@ -23,27 +23,34 @@ struct User {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
char *name;
|
||||
char *home;
|
||||
char *state_file;
|
||||
char *runtime_path;
|
||||
char *slice;
|
||||
char *service;
|
||||
|
||||
char *slice; /* user-UID.slice */
|
||||
char *service; /* user@UID.service */
|
||||
char *runtime_dir_service; /* user-runtime-dir@UID.service */
|
||||
|
||||
char *service_job;
|
||||
char *slice_job;
|
||||
|
||||
Session *display;
|
||||
|
||||
dual_timestamp timestamp;
|
||||
dual_timestamp timestamp; /* When this User object was 'started' the first time */
|
||||
usec_t last_session_timestamp; /* When the number of sessions of this user went from 1 to 0 the last time */
|
||||
|
||||
/* Set up when the last session of the user logs out */
|
||||
sd_event_source *timer_event_source;
|
||||
|
||||
bool in_gc_queue:1;
|
||||
bool started:1;
|
||||
bool stopping:1;
|
||||
|
||||
bool started:1; /* Whenever the user being started, has been started or is being stopped again. */
|
||||
bool stopping:1; /* Whenever the user is being stopped or has been stopped. */
|
||||
|
||||
LIST_HEAD(Session, sessions);
|
||||
LIST_FIELDS(User, gc_queue);
|
||||
};
|
||||
|
||||
int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name);
|
||||
int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name, const char *home);
|
||||
User *user_free(User *u);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free);
|
||||
@ -60,6 +67,7 @@ int user_load(User *u);
|
||||
int user_kill(User *u, int signo);
|
||||
int user_check_linger_file(User *u);
|
||||
void user_elect_display(User *u);
|
||||
void user_update_last_session_timer(User *u);
|
||||
|
||||
extern const sd_bus_vtable user_vtable[];
|
||||
int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
|
||||
|
@ -35,18 +35,21 @@ static int manager_new(Manager **ret) {
|
||||
|
||||
assert(ret);
|
||||
|
||||
m = new0(Manager, 1);
|
||||
m = new(Manager, 1);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
m->console_active_fd = -1;
|
||||
m->reserve_vt_fd = -1;
|
||||
*m = (Manager) {
|
||||
.console_active_fd = -1,
|
||||
.reserve_vt_fd = -1,
|
||||
};
|
||||
|
||||
m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
|
||||
|
||||
m->devices = hashmap_new(&string_hash_ops);
|
||||
m->seats = hashmap_new(&string_hash_ops);
|
||||
m->sessions = hashmap_new(&string_hash_ops);
|
||||
m->sessions_by_leader = hashmap_new(NULL);
|
||||
m->users = hashmap_new(NULL);
|
||||
m->inhibitors = hashmap_new(&string_hash_ops);
|
||||
m->buttons = hashmap_new(&string_hash_ops);
|
||||
@ -54,7 +57,7 @@ static int manager_new(Manager **ret) {
|
||||
m->user_units = hashmap_new(&string_hash_ops);
|
||||
m->session_units = hashmap_new(&string_hash_ops);
|
||||
|
||||
if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
|
||||
if (!m->devices || !m->seats || !m->sessions || !m->sessions_by_leader || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
|
||||
return -ENOMEM;
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
@ -109,6 +112,7 @@ static Manager* manager_unref(Manager *m) {
|
||||
hashmap_free(m->devices);
|
||||
hashmap_free(m->seats);
|
||||
hashmap_free(m->sessions);
|
||||
hashmap_free(m->sessions_by_leader);
|
||||
hashmap_free(m->users);
|
||||
hashmap_free(m->inhibitors);
|
||||
hashmap_free(m->buttons);
|
||||
@ -129,6 +133,10 @@ static Manager* manager_unref(Manager *m) {
|
||||
sd_event_source_unref(m->udev_button_event_source);
|
||||
sd_event_source_unref(m->lid_switch_ignore_event_source);
|
||||
|
||||
#if ENABLE_UTMP
|
||||
sd_event_source_unref(m->utmp_event_source);
|
||||
#endif
|
||||
|
||||
safe_close(m->console_active_fd);
|
||||
|
||||
udev_monitor_unref(m->udev_seat_monitor);
|
||||
@ -787,28 +795,28 @@ static int manager_connect_console(Manager *m) {
|
||||
assert(m);
|
||||
assert(m->console_active_fd < 0);
|
||||
|
||||
/* On certain architectures (S390 and Xen, and containers),
|
||||
/dev/tty0 does not exist, so don't fail if we can't open
|
||||
it. */
|
||||
/* On certain systems (such as S390, Xen, and containers) /dev/tty0 does not exist (as there is no VC), so
|
||||
* don't fail if we can't open it. */
|
||||
|
||||
if (access("/dev/tty0", F_OK) < 0)
|
||||
return 0;
|
||||
|
||||
m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (m->console_active_fd < 0) {
|
||||
|
||||
/* On some systems the device node /dev/tty0 may exist
|
||||
* even though /sys/class/tty/tty0 does not. */
|
||||
if (errno == ENOENT)
|
||||
/* On some systems /dev/tty0 may exist even though /sys/class/tty/tty0 does not. These are broken, but
|
||||
* common. Let's complain but continue anyway. */
|
||||
if (errno == ENOENT) {
|
||||
log_warning_errno(errno, "System has /dev/tty0 but not /sys/class/tty/tty0/active which is broken, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Failed to open /sys/class/tty/tty0/active: %m");
|
||||
}
|
||||
|
||||
r = sd_event_add_io(m->event, &m->console_active_event_source, m->console_active_fd, 0, manager_dispatch_console, m);
|
||||
if (r < 0) {
|
||||
log_error("Failed to watch foreground console");
|
||||
return r;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to watch foreground console: %m");
|
||||
|
||||
/*
|
||||
* SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used
|
||||
@ -827,7 +835,7 @@ static int manager_connect_console(Manager *m) {
|
||||
|
||||
r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_error_errno(r, "Failed to subscribe to signal: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -951,13 +959,13 @@ static void manager_gc(Manager *m, bool drop_not_started) {
|
||||
/* First, if we are not closing yet, initiate stopping */
|
||||
if (session_may_gc(session, drop_not_started) &&
|
||||
session_get_state(session) != SESSION_CLOSING)
|
||||
session_stop(session, false);
|
||||
(void) session_stop(session, false);
|
||||
|
||||
/* Normally, this should make the session referenced
|
||||
* again, if it doesn't then let's get rid of it
|
||||
* immediately */
|
||||
if (session_may_gc(session, drop_not_started)) {
|
||||
session_finalize(session);
|
||||
(void) session_finalize(session);
|
||||
session_free(session);
|
||||
}
|
||||
}
|
||||
@ -968,11 +976,11 @@ static void manager_gc(Manager *m, bool drop_not_started) {
|
||||
|
||||
/* First step: queue stop jobs */
|
||||
if (user_may_gc(user, drop_not_started))
|
||||
user_stop(user, false);
|
||||
(void) user_stop(user, false);
|
||||
|
||||
/* Second step: finalize user */
|
||||
if (user_may_gc(user, drop_not_started)) {
|
||||
user_finalize(user);
|
||||
(void) user_finalize(user);
|
||||
user_free(user);
|
||||
}
|
||||
}
|
||||
@ -1067,6 +1075,9 @@ static int manager_startup(Manager *m) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to register SIGHUP handler: %m");
|
||||
|
||||
/* Connect to utmp */
|
||||
manager_connect_utmp(m);
|
||||
|
||||
/* Connect to console */
|
||||
r = manager_connect_console(m);
|
||||
if (r < 0)
|
||||
@ -1122,15 +1133,18 @@ static int manager_startup(Manager *m) {
|
||||
/* Reserve the special reserved VT */
|
||||
manager_reserve_vt(m);
|
||||
|
||||
/* Read in utmp if it exists */
|
||||
manager_read_utmp(m);
|
||||
|
||||
/* And start everything */
|
||||
HASHMAP_FOREACH(seat, m->seats, i)
|
||||
seat_start(seat);
|
||||
(void) seat_start(seat);
|
||||
|
||||
HASHMAP_FOREACH(user, m->users, i)
|
||||
user_start(user);
|
||||
(void) user_start(user);
|
||||
|
||||
HASHMAP_FOREACH(session, m->sessions, i)
|
||||
session_start(session, NULL);
|
||||
(void) session_start(session, NULL, NULL);
|
||||
|
||||
HASHMAP_FOREACH(inhibitor, m->inhibitors, i)
|
||||
inhibitor_start(inhibitor);
|
||||
|
@ -27,6 +27,7 @@ struct Manager {
|
||||
Hashmap *devices;
|
||||
Hashmap *seats;
|
||||
Hashmap *sessions;
|
||||
Hashmap *sessions_by_leader;
|
||||
Hashmap *users;
|
||||
Hashmap *inhibitors;
|
||||
Hashmap *buttons;
|
||||
@ -43,6 +44,10 @@ struct Manager {
|
||||
sd_event_source *udev_vcsa_event_source;
|
||||
sd_event_source *udev_button_event_source;
|
||||
|
||||
#if ENABLE_UTMP
|
||||
sd_event_source *utmp_event_source;
|
||||
#endif
|
||||
|
||||
int console_active_fd;
|
||||
|
||||
unsigned n_autovts;
|
||||
@ -62,6 +67,7 @@ struct Manager {
|
||||
Hashmap *user_units;
|
||||
|
||||
usec_t inhibit_delay_max;
|
||||
usec_t user_stop_delay;
|
||||
|
||||
/* If an action is currently being executed or is delayed,
|
||||
* this is != 0 and encodes what is being done */
|
||||
@ -128,7 +134,7 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev
|
||||
int manager_add_button(Manager *m, const char *name, Button **_button);
|
||||
int manager_add_seat(Manager *m, const char *id, Seat **_seat);
|
||||
int manager_add_session(Manager *m, const char *id, Session **_session);
|
||||
int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user);
|
||||
int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, const char *home, User **_user);
|
||||
int manager_add_user_by_name(Manager *m, const char *name, User **_user);
|
||||
int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user);
|
||||
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
|
||||
@ -149,6 +155,10 @@ bool manager_is_docked_or_external_displays(Manager *m);
|
||||
bool manager_is_on_external_power(void);
|
||||
bool manager_all_buttons_ignored(Manager *m);
|
||||
|
||||
int manager_read_utmp(Manager *m);
|
||||
void manager_connect_utmp(Manager *m);
|
||||
void manager_reconnect_utmp(Manager *m);
|
||||
|
||||
extern const sd_bus_vtable manager_vtable[];
|
||||
|
||||
int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
@ -161,13 +171,13 @@ int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name
|
||||
|
||||
int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
|
||||
|
||||
int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, const char *after, const char *after2, sd_bus_message *more_properties, sd_bus_error *error, char **job);
|
||||
int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, const char *requires_mounts_for, sd_bus_message *more_properties, sd_bus_error *error, char **job);
|
||||
int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
|
||||
int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
|
||||
int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error);
|
||||
int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error);
|
||||
int manager_unit_is_active(Manager *manager, const char *unit);
|
||||
int manager_job_is_active(Manager *manager, const char *path);
|
||||
int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error);
|
||||
int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error);
|
||||
|
||||
/* gperf lookup function */
|
||||
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
@ -58,7 +58,6 @@ loginctl_sources = files('''
|
||||
|
||||
user_runtime_dir_sources = files('''
|
||||
user-runtime-dir.c
|
||||
logind.h
|
||||
'''.split())
|
||||
|
||||
if conf.get('ENABLE_LOGIND') == 1
|
||||
|
@ -308,6 +308,36 @@ static int update_environment(pam_handle_t *handle, const char *key, const char
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool validate_runtime_directory(pam_handle_t *handle, const char *path, uid_t uid) {
|
||||
struct stat st;
|
||||
|
||||
assert(path);
|
||||
|
||||
/* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set
|
||||
* up properly for us. */
|
||||
|
||||
if (lstat(path, &st) < 0) {
|
||||
pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not actually a directory.", path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (st.st_uid != uid) {
|
||||
pam_syslog(handle, LOG_ERR, "Runtime directory '%s' is not owned by UID " UID_FMT ", as it should.", path, uid);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
pam_syslog(handle, LOG_WARNING, "Not setting $XDG_RUNTIME_DIR, as the directory is not in order.");
|
||||
return false;
|
||||
}
|
||||
|
||||
_public_ PAM_EXTERN int pam_sm_open_session(
|
||||
pam_handle_t *handle,
|
||||
int flags,
|
||||
@ -367,10 +397,12 @@ _public_ PAM_EXTERN int pam_sm_open_session(
|
||||
if (asprintf(&rt, "/run/user/"UID_FMT, pw->pw_uid) < 0)
|
||||
return PAM_BUF_ERR;
|
||||
|
||||
r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
|
||||
if (r != PAM_SUCCESS) {
|
||||
pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
|
||||
return r;
|
||||
if (validate_runtime_directory(handle, rt, pw->pw_uid)) {
|
||||
r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
|
||||
if (r != PAM_SUCCESS) {
|
||||
pam_syslog(handle, LOG_ERR, "Failed to set runtime dir.");
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return PAM_SUCCESS;
|
||||
@ -574,9 +606,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
|
||||
* in privileged apps clobbering the runtime directory
|
||||
* unnecessarily. */
|
||||
|
||||
r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
if (validate_runtime_directory(handle, runtime_path, pw->pw_uid)) {
|
||||
r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Most likely we got the session/type/class from environment variables, but might have gotten the data
|
||||
|
@ -3,9 +3,11 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "bus-error.h"
|
||||
#include "fs-util.h"
|
||||
#include "label.h"
|
||||
#include "logind.h"
|
||||
#include "mkdir.h"
|
||||
#include "mount-util.h"
|
||||
#include "path-util.h"
|
||||
@ -17,21 +19,28 @@
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
static int gather_configuration(size_t *runtime_dir_size) {
|
||||
Manager m = {};
|
||||
static int acquire_runtime_dir_size(uint64_t *ret) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
|
||||
int r;
|
||||
|
||||
manager_reset_config(&m);
|
||||
|
||||
r = manager_parse_config_file(&m);
|
||||
r = sd_bus_default_system(&bus);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse logind.conf: %m");
|
||||
return log_error_errno(r, "Failed to connect to system bus: %m");
|
||||
|
||||
r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', ret);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r));
|
||||
|
||||
*runtime_dir_size = m.runtime_dir_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gid, size_t runtime_dir_size) {
|
||||
static int user_mkdir_runtime_path(
|
||||
const char *runtime_path,
|
||||
uid_t uid,
|
||||
gid_t gid,
|
||||
uint64_t runtime_dir_size) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(runtime_path);
|
||||
@ -49,10 +58,10 @@ static int user_mkdir_runtime_path(const char *runtime_path, uid_t uid, gid_t gi
|
||||
char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*")
|
||||
+ DECIMAL_STR_MAX(uid_t)
|
||||
+ DECIMAL_STR_MAX(gid_t)
|
||||
+ DECIMAL_STR_MAX(size_t)];
|
||||
+ DECIMAL_STR_MAX(uint64_t)];
|
||||
|
||||
xsprintf(options,
|
||||
"mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s",
|
||||
"mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 "%s",
|
||||
uid, gid, runtime_dir_size,
|
||||
mac_smack_use() ? ",smackfsroot=*" : "");
|
||||
|
||||
@ -113,7 +122,7 @@ static int user_remove_runtime_path(const char *runtime_path) {
|
||||
|
||||
static int do_mount(const char *user) {
|
||||
char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
|
||||
size_t runtime_dir_size;
|
||||
uint64_t runtime_dir_size;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int r;
|
||||
@ -126,9 +135,11 @@ static int do_mount(const char *user) {
|
||||
: "Failed to look up user \"%s\": %m",
|
||||
user);
|
||||
|
||||
xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
|
||||
r = acquire_runtime_dir_size(&runtime_dir_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert_se(gather_configuration(&runtime_dir_size) == 0);
|
||||
xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
|
||||
|
||||
log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid);
|
||||
return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size);
|
||||
|
@ -422,6 +422,7 @@ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...)
|
||||
int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _sd_printf_(3,0);
|
||||
int sd_bus_error_get_errno(const sd_bus_error *e);
|
||||
int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e);
|
||||
int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e);
|
||||
int sd_bus_error_is_set(const sd_bus_error *e);
|
||||
int sd_bus_error_has_name(const sd_bus_error *e, const char *name);
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
Description=User Slice of UID %j
|
||||
Documentation=man:user@.service(5)
|
||||
After=systemd-user-sessions.service
|
||||
StopWhenUnneeded=yes
|
||||
|
||||
[Slice]
|
||||
TasksMax=33%
|
||||
|
@ -8,9 +8,9 @@
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=/run/user/%i mount wrapper
|
||||
Description=User Runtime Directory /run/user/%i
|
||||
Documentation=man:user@.service(5)
|
||||
After=systemd-user-sessions.service
|
||||
After=systemd-user-sessions.service dbus.service
|
||||
StopWhenUnneeded=yes
|
||||
IgnoreOnIsolate=yes
|
||||
|
||||
@ -18,5 +18,5 @@ IgnoreOnIsolate=yes
|
||||
ExecStart=@rootlibexecdir@/systemd-user-runtime-dir start %i
|
||||
ExecStop=@rootlibexecdir@/systemd-user-runtime-dir stop %i
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
RemainAfterExit=yes
|
||||
Slice=user-%i.slice
|
||||
|
Loading…
Reference in New Issue
Block a user