logind-core.c revision 2d62c530d2b4c2730abff715b7342f1402114513
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen/***
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen This file is part of systemd.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Copyright 2011 Lennart Poettering
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen systemd is free software; you can redistribute it and/or modify it
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen under the terms of the GNU Lesser General Public License as published by
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen (at your option) any later version.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen systemd is distributed in the hope that it will be useful, but
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Lesser General Public License for more details.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen You should have received a copy of the GNU Lesser General Public License
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen***/
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen#include <sys/types.h>
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen#include <sys/stat.h>
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen#include <sys/ioctl.h>
f60e98b33646e7a3317553e13bac591a98ce41c0Lukasz Skalski#include <fcntl.h>
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen#include <pwd.h>
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen#include <unistd.h>
39d8db043b599a7382f94bfc904d5e108af438bdLennart Poettering#include <linux/vt.h>
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering#include "strv.h"
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen#include "cgroup-util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "audit.h"
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen#include "bus-util.h"
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen#include "bus-error.h"
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen#include "logind.h"
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersenint manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen Device *d;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen assert(m);
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen assert(sysfs);
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen d = hashmap_get(m->devices, sysfs);
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (d) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (_device)
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen *_device = d;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek /* we support adding master-flags, but not removing them */
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek d->master = d->master || master;
cc56fafeebf814ef035e549115cf1850e6473fa5WaLyong Cho
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek return 0;
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt }
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek d = device_new(m, sysfs, master);
a5a807e63a50314e190e9166d8a453cd8dd258e3Zbigniew Jędrzejewski-Szmek if (!d)
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen return -ENOMEM;
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt if (_device)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering *_device = d;
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen return 0;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen}
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersenint manager_add_seat(Manager *m, const char *id, Seat **_seat) {
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt Seat *s;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen assert(m);
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen assert(id);
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen
682265d5e2157882861b0091c6b81fa92699b72aTom Gundersen s = hashmap_get(m->seats, id);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (s) {
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (_seat)
b9e7a9d870ac41d4db954edd52a1f5dd7d153389Lennart Poettering *_seat = s;
b9e7a9d870ac41d4db954edd52a1f5dd7d153389Lennart Poettering
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen return 0;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen }
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering s = seat_new(m, id);
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (!s)
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen return -ENOMEM;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (_seat)
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt *_seat = s;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering return 0;
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering}
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poetteringint manager_add_session(Manager *m, const char *id, Session **_session) {
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering Session *s;
edc501d4674dadc304d45a7e1c5b69e207eb8cd4Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering assert(m);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering assert(id);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
4e945a6f7971fd7d1f6b2c62ee3afdaff3c95ce4Lennart Poettering s = hashmap_get(m->sessions, id);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt if (s) {
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (_session)
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen *_session = s;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen return 0;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen }
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen s = session_new(m, id);
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt if (!s)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return -ENOMEM;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen if (_session)
96e6e394431dcc1db52847be311e2c8e61d7a9d6Lennart Poettering *_session = s;
96e6e394431dcc1db52847be311e2c8e61d7a9d6Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return 0;
af4ec4309e8f82aad87a8d574785c12f8763d5f8Lennart Poettering}
b37d45c9ab5f645502695e47d268af1a54216e0eTom Gundersen
af4ec4309e8f82aad87a8d574785c12f8763d5f8Lennart Poetteringint manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen User *u;
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen
091a364c802e34a58f3260c9cb5db9b75c62215cTom Gundersen assert(m);
assert(name);
u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
if (u) {
if (_user)
*_user = u;
return 0;
}
u = user_new(m, uid, gid, name);
if (!u)
return -ENOMEM;
if (_user)
*_user = u;
return 0;
}
int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
uid_t uid;
gid_t gid;
int r;
assert(m);
assert(name);
r = get_user_creds(&name, &uid, &gid, NULL, NULL);
if (r < 0)
return r;
return manager_add_user(m, uid, gid, name, _user);
}
int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
struct passwd *p;
assert(m);
errno = 0;
p = getpwuid(uid);
if (!p)
return errno ? -errno : -ENOENT;
return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
}
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
Inhibitor *i;
assert(m);
assert(id);
i = hashmap_get(m->inhibitors, id);
if (i) {
if (_inhibitor)
*_inhibitor = i;
return 0;
}
i = inhibitor_new(m, id);
if (!i)
return -ENOMEM;
if (_inhibitor)
*_inhibitor = i;
return 0;
}
int manager_add_button(Manager *m, const char *name, Button **_button) {
Button *b;
assert(m);
assert(name);
b = hashmap_get(m->buttons, name);
if (b) {
if (_button)
*_button = b;
return 0;
}
b = button_new(m, name);
if (!b)
return -ENOMEM;
if (_button)
*_button = b;
return 0;
}
int manager_watch_busname(Manager *m, const char *name) {
char *n;
int r;
assert(m);
assert(name);
if (set_get(m->busnames, (char*) name))
return 0;
n = strdup(name);
if (!n)
return -ENOMEM;
r = set_put(m->busnames, n);
if (r < 0) {
free(n);
return r;
}
return 0;
}
void manager_drop_busname(Manager *m, const char *name) {
Session *session;
Iterator i;
assert(m);
assert(name);
/* keep it if the name still owns a controller */
HASHMAP_FOREACH(session, m->sessions, i)
if (session_is_controller(session, name))
return;
free(set_remove(m->busnames, (char*) name));
}
int manager_process_seat_device(Manager *m, struct udev_device *d) {
Device *device;
int r;
assert(m);
if (streq_ptr(udev_device_get_action(d), "remove")) {
device = hashmap_get(m->devices, udev_device_get_syspath(d));
if (!device)
return 0;
seat_add_to_gc_queue(device->seat);
device_free(device);
} else {
const char *sn;
Seat *seat = NULL;
bool master;
sn = udev_device_get_property_value(d, "ID_SEAT");
if (isempty(sn))
sn = "seat0";
if (!seat_name_is_valid(sn)) {
log_warning("Device with invalid seat name %s found, ignoring.", sn);
return 0;
}
/* ignore non-master devices for unknown seats */
master = udev_device_has_tag(d, "master-of-seat");
if (!master && !(seat = hashmap_get(m->seats, sn)))
return 0;
r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
if (r < 0)
return r;
if (!seat) {
r = manager_add_seat(m, sn, &seat);
if (r < 0) {
if (!device->seat)
device_free(device);
return r;
}
}
device_attach(device, seat);
seat_start(seat);
}
return 0;
}
int manager_process_button_device(Manager *m, struct udev_device *d) {
Button *b;
int r;
assert(m);
if (streq_ptr(udev_device_get_action(d), "remove")) {
b = hashmap_get(m->buttons, udev_device_get_sysname(d));
if (!b)
return 0;
button_free(b);
} else {
const char *sn;
r = manager_add_button(m, udev_device_get_sysname(d), &b);
if (r < 0)
return r;
sn = udev_device_get_property_value(d, "ID_SEAT");
if (isempty(sn))
sn = "seat0";
button_set_seat(b, sn);
button_open(b);
}
return 0;
}
int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
_cleanup_free_ char *unit = NULL;
Session *s;
int r;
assert(m);
assert(session);
if (pid < 1)
return -EINVAL;
r = cg_pid_get_unit(pid, &unit);
if (r < 0)
return 0;
s = hashmap_get(m->session_units, unit);
if (!s)
return 0;
*session = s;
return 1;
}
int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
_cleanup_free_ char *unit = NULL;
User *u;
int r;
assert(m);
assert(user);
if (pid < 1)
return -EINVAL;
r = cg_pid_get_slice(pid, &unit);
if (r < 0)
return 0;
u = hashmap_get(m->user_units, unit);
if (!u)
return 0;
*user = u;
return 1;
}
int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
Session *s;
bool idle_hint;
dual_timestamp ts = { 0, 0 };
Iterator i;
assert(m);
idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
HASHMAP_FOREACH(s, m->sessions, i) {
dual_timestamp k;
int ih;
ih = session_get_idle_hint(s, &k);
if (ih < 0)
return ih;
if (!ih) {
if (!idle_hint) {
if (k.monotonic < ts.monotonic)
ts = k;
} else {
idle_hint = false;
ts = k;
}
} else if (idle_hint) {
if (k.monotonic > ts.monotonic)
ts = k;
}
}
if (t)
*t = ts;
return idle_hint;
}
bool manager_shall_kill(Manager *m, const char *user) {
assert(m);
assert(user);
if (!m->kill_user_processes)
return false;
if (strv_contains(m->kill_exclude_users, user))
return false;
if (strv_isempty(m->kill_only_users))
return true;
return strv_contains(m->kill_only_users, user);
}
static int vt_is_busy(unsigned int vtnr) {
struct vt_stat vt_stat;
int r = 0, fd;
assert(vtnr >= 1);
/* We explicitly open /dev/tty1 here instead of /dev/tty0. If
* we'd open the latter we'd open the foreground tty which
* hence would be unconditionally busy. By opening /dev/tty1
* we avoid this. Since tty1 is special and needs to be an
* explicitly loaded getty or DM this is safe. */
fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return -errno;
if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
r = -errno;
else
r = !!(vt_stat.v_state & (1 << vtnr));
close_nointr_nofail(fd);
return r;
}
int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *name = NULL;
int r;
assert(m);
assert(vtnr >= 1);
if (vtnr > m->n_autovts &&
vtnr != m->reserve_vt)
return 0;
if (vtnr != m->reserve_vt) {
/* If this is the reserved TTY, we'll start the getty
* on it in any case, but otherwise only if it is not
* busy. */
r = vt_is_busy(vtnr);
if (r < 0)
return r;
else if (r > 0)
return -EBUSY;
}
if (asprintf(&name, "autovt@tty%u.service", vtnr) < 0)
return log_oom();
r = sd_bus_call_method(
m->bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
&error,
NULL,
"ss", name, "fail");
if (r < 0)
log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
return r;
}
bool manager_is_docked(Manager *m) {
Iterator i;
Button *b;
HASHMAP_FOREACH(b, m->buttons, i)
if (b->docked)
return true;
return false;
}