2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-09-25 17:04:41 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2011 Lennart Poettering
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <pwd.h>
|
2015-11-16 22:09:36 +01:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/types.h>
|
2013-09-25 17:04:41 +02:00
|
|
|
#include <linux/vt.h>
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2013-11-05 01:10:21 +01:00
|
|
|
#include "bus-error.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "bus-util.h"
|
|
|
|
#include "cgroup-util.h"
|
|
|
|
#include "fd-util.h"
|
2013-11-05 01:10:21 +01:00
|
|
|
#include "logind.h"
|
2017-08-30 17:06:12 +02:00
|
|
|
#include "parse-util.h"
|
2018-01-11 00:39:12 +01:00
|
|
|
#include "process-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "strv.h"
|
2015-04-10 23:15:59 +02:00
|
|
|
#include "terminal-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "udev-util.h"
|
2015-10-25 22:32:30 +01:00
|
|
|
#include "user-util.h"
|
2013-09-25 17:04:41 +02:00
|
|
|
|
|
|
|
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
|
|
|
|
Device *d;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(sysfs);
|
|
|
|
|
|
|
|
d = hashmap_get(m->devices, sysfs);
|
2014-02-03 05:58:16 +01:00
|
|
|
if (d)
|
2013-09-25 17:04:41 +02:00
|
|
|
/* we support adding master-flags, but not removing them */
|
|
|
|
d->master = d->master || master;
|
2014-02-03 05:58:16 +01:00
|
|
|
else {
|
|
|
|
d = device_new(m, sysfs, master);
|
|
|
|
if (!d)
|
|
|
|
return -ENOMEM;
|
2013-09-25 17:04:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_device)
|
|
|
|
*_device = d;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
|
|
|
|
Seat *s;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(id);
|
|
|
|
|
|
|
|
s = hashmap_get(m->seats, id);
|
2014-02-03 05:58:16 +01:00
|
|
|
if (!s) {
|
|
|
|
s = seat_new(m, id);
|
|
|
|
if (!s)
|
|
|
|
return -ENOMEM;
|
2013-09-25 17:04:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_seat)
|
|
|
|
*_seat = s;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int manager_add_session(Manager *m, const char *id, Session **_session) {
|
|
|
|
Session *s;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(id);
|
|
|
|
|
|
|
|
s = hashmap_get(m->sessions, id);
|
2014-02-03 05:58:16 +01:00
|
|
|
if (!s) {
|
|
|
|
s = session_new(m, id);
|
|
|
|
if (!s)
|
|
|
|
return -ENOMEM;
|
2013-09-25 17:04:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_session)
|
|
|
|
*_session = s;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
|
|
|
|
User *u;
|
2015-09-29 11:10:01 +02:00
|
|
|
int r;
|
2013-09-25 17:04:41 +02:00
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(name);
|
|
|
|
|
2015-01-09 16:25:47 +01:00
|
|
|
u = hashmap_get(m->users, UID_TO_PTR(uid));
|
2014-02-03 05:58:16 +01:00
|
|
|
if (!u) {
|
2015-09-29 11:10:01 +02:00
|
|
|
r = user_new(&u, m, uid, gid, name);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-09-25 17:04:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2016-01-11 18:47:14 +01:00
|
|
|
return errno > 0 ? -errno : -ENOENT;
|
2013-09-25 17:04:41 +02:00
|
|
|
|
|
|
|
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);
|
2014-02-03 05:58:16 +01:00
|
|
|
if (!b) {
|
|
|
|
b = button_new(m, name);
|
|
|
|
if (!b)
|
|
|
|
return -ENOMEM;
|
2013-09-25 17:04:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_button)
|
|
|
|
*_button = b;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-03-03 19:30:16 +01:00
|
|
|
seat = hashmap_get(m->seats, sn);
|
2013-09-25 17:04:41 +02:00
|
|
|
master = udev_device_has_tag(d, "master-of-seat");
|
2014-03-03 19:30:16 +01:00
|
|
|
|
|
|
|
/* Ignore non-master devices for unknown seats */
|
|
|
|
if (!master && !seat)
|
2013-09-25 17:04:41 +02:00
|
|
|
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);
|
2017-06-27 17:46:28 +02:00
|
|
|
|
|
|
|
r = button_open(b);
|
|
|
|
if (r < 0) /* event device doesn't have any keys or switches relevant to us? (or any other error
|
|
|
|
* opening the device?) let's close the button again. */
|
|
|
|
button_free(b);
|
2013-09-25 17:04:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2017-10-03 13:26:02 +02:00
|
|
|
if (!pid_is_valid(pid))
|
2013-09-25 17:04:41 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
r = cg_pid_get_unit(pid, &unit);
|
|
|
|
if (r < 0)
|
2013-11-06 02:03:04 +01:00
|
|
|
return 0;
|
2013-09-25 17:04:41 +02:00
|
|
|
|
|
|
|
s = hashmap_get(m->session_units, unit);
|
|
|
|
if (!s)
|
|
|
|
return 0;
|
|
|
|
|
2015-07-07 19:38:41 +02:00
|
|
|
if (session)
|
|
|
|
*session = s;
|
2013-09-25 17:04:41 +02:00
|
|
|
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);
|
|
|
|
|
2017-10-03 13:26:02 +02:00
|
|
|
if (!pid_is_valid(pid))
|
2013-09-25 17:04:41 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
r = cg_pid_get_slice(pid, &unit);
|
|
|
|
if (r < 0)
|
2013-11-06 02:03:04 +01:00
|
|
|
return 0;
|
2013-09-25 17:04:41 +02:00
|
|
|
|
|
|
|
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;
|
2015-06-16 01:08:12 +02:00
|
|
|
dual_timestamp ts = DUAL_TIMESTAMP_NULL;
|
2013-09-25 17:04:41 +02:00
|
|
|
Iterator i;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
2013-11-27 02:38:06 +01:00
|
|
|
idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
|
2013-09-25 17:04:41 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2016-04-09 21:03:19 +02:00
|
|
|
if (!m->kill_exclude_users && streq(user, "root"))
|
|
|
|
return false;
|
|
|
|
|
2013-09-25 17:04:41 +02:00
|
|
|
if (strv_contains(m->kill_exclude_users, user))
|
|
|
|
return false;
|
|
|
|
|
2016-04-13 05:52:41 +02:00
|
|
|
if (!strv_isempty(m->kill_only_users))
|
|
|
|
return strv_contains(m->kill_only_users, user);
|
2013-09-25 17:04:41 +02:00
|
|
|
|
2016-04-13 05:52:41 +02:00
|
|
|
return m->kill_user_processes;
|
2013-09-25 17:04:41 +02:00
|
|
|
}
|
|
|
|
|
2017-08-30 17:06:12 +02:00
|
|
|
int config_parse_n_autovts(
|
|
|
|
const char *unit,
|
|
|
|
const char *filename,
|
|
|
|
unsigned line,
|
|
|
|
const char *section,
|
|
|
|
unsigned section_line,
|
|
|
|
const char *lvalue,
|
|
|
|
int ltype,
|
|
|
|
const char *rvalue,
|
|
|
|
void *data,
|
|
|
|
void *userdata) {
|
|
|
|
|
|
|
|
unsigned *n = data;
|
|
|
|
unsigned o;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(filename);
|
|
|
|
assert(lvalue);
|
|
|
|
assert(rvalue);
|
|
|
|
assert(data);
|
|
|
|
|
|
|
|
r = safe_atou(rvalue, &o);
|
|
|
|
if (r < 0) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse number of autovts, ignoring: %s", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (o > 15) {
|
|
|
|
log_syntax(unit, LOG_ERR, filename, line, r, "A maximum of 15 autovts are supported, ignoring: %s", rvalue);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*n = o;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-28 17:05:34 +01:00
|
|
|
static int vt_is_busy(unsigned int vtnr) {
|
2013-09-25 17:04:41 +02:00
|
|
|
struct vt_stat vt_stat;
|
2014-02-03 05:58:16 +01:00
|
|
|
int r = 0;
|
|
|
|
_cleanup_close_ int fd;
|
2013-09-25 17:04:41 +02:00
|
|
|
|
|
|
|
assert(vtnr >= 1);
|
|
|
|
|
2017-08-30 17:06:12 +02:00
|
|
|
/* VT_GETSTATE "cannot return state for more than 16 VTs, since v_state is short" */
|
|
|
|
assert(vtnr <= 15);
|
|
|
|
|
2013-09-25 17:04:41 +02:00
|
|
|
/* 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));
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-11-28 17:05:34 +01:00
|
|
|
int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2014-02-03 05:58:16 +01:00
|
|
|
char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
|
2013-09-25 17:04:41 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(vtnr >= 1);
|
|
|
|
|
2013-11-28 17:05:34 +01:00
|
|
|
if (vtnr > m->n_autovts &&
|
|
|
|
vtnr != m->reserve_vt)
|
2013-09-25 17:04:41 +02:00
|
|
|
return 0;
|
|
|
|
|
2013-11-28 17:05:34 +01:00
|
|
|
if (vtnr != m->reserve_vt) {
|
2013-09-25 17:04:41 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2014-02-03 05:58:16 +01:00
|
|
|
snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
|
2013-11-05 01:10:21 +01:00
|
|
|
r = sd_bus_call_method(
|
2013-09-25 17:04:41 +02:00
|
|
|
m->bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"StartUnit",
|
2013-11-05 01:10:21 +01:00
|
|
|
&error,
|
2013-09-25 17:04:41 +02:00
|
|
|
NULL,
|
2013-11-05 01:10:21 +01:00
|
|
|
"ss", name, "fail");
|
|
|
|
if (r < 0)
|
|
|
|
log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
|
2013-09-25 17:04:41 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2014-02-24 16:22:23 +01:00
|
|
|
|
2015-06-17 00:24:05 +02:00
|
|
|
static bool manager_is_docked(Manager *m) {
|
2014-02-24 16:22:23 +01:00
|
|
|
Iterator i;
|
|
|
|
Button *b;
|
|
|
|
|
|
|
|
HASHMAP_FOREACH(b, m->buttons, i)
|
|
|
|
if (b->docked)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2014-03-03 19:30:16 +01:00
|
|
|
|
2015-06-17 00:24:05 +02:00
|
|
|
static int manager_count_external_displays(Manager *m) {
|
2014-03-03 19:30:16 +01:00
|
|
|
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
|
|
|
|
struct udev_list_entry *item = NULL, *first = NULL;
|
|
|
|
int r;
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
e = udev_enumerate_new(m->udev);
|
|
|
|
if (!e)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
r = udev_enumerate_add_match_subsystem(e, "drm");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = udev_enumerate_scan_devices(e);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
first = udev_enumerate_get_list_entry(e);
|
|
|
|
udev_list_entry_foreach(item, first) {
|
|
|
|
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
|
|
|
|
struct udev_device *p;
|
2015-06-17 00:24:05 +02:00
|
|
|
const char *status, *enabled, *dash, *nn, *i;
|
|
|
|
bool external = false;
|
2014-03-03 19:30:16 +01:00
|
|
|
|
|
|
|
d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
|
|
|
|
if (!d)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
p = udev_device_get_parent(d);
|
|
|
|
if (!p)
|
2014-03-11 16:49:00 +01:00
|
|
|
continue;
|
2014-03-03 19:30:16 +01:00
|
|
|
|
|
|
|
/* If the parent shares the same subsystem as the
|
|
|
|
* device we are looking at then it is a connector,
|
|
|
|
* which is what we are interested in. */
|
|
|
|
if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
|
|
|
|
continue;
|
|
|
|
|
2015-06-17 00:24:05 +02:00
|
|
|
nn = udev_device_get_sysname(d);
|
|
|
|
if (!nn)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Ignore internal displays: the type is encoded in
|
2016-05-12 11:23:35 +02:00
|
|
|
* the sysfs name, as the second dash separated item
|
2015-06-17 00:24:05 +02:00
|
|
|
* (the first is the card name, the last the connector
|
|
|
|
* number). We implement a whitelist of external
|
|
|
|
* displays here, rather than a whitelist, to ensure
|
|
|
|
* we don't block suspends too eagerly. */
|
|
|
|
dash = strchr(nn, '-');
|
|
|
|
if (!dash)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dash++;
|
|
|
|
FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
|
|
|
|
"Composite-", "SVIDEO-", "Component-",
|
|
|
|
"DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") {
|
|
|
|
|
|
|
|
if (startswith(dash, i)) {
|
|
|
|
external = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!external)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Ignore ports that are not enabled */
|
|
|
|
enabled = udev_device_get_sysattr_value(d, "enabled");
|
|
|
|
if (!enabled)
|
|
|
|
continue;
|
|
|
|
if (!streq_ptr(enabled, "enabled"))
|
|
|
|
continue;
|
|
|
|
|
2014-03-03 19:30:16 +01:00
|
|
|
/* We count any connector which is not explicitly
|
|
|
|
* "disconnected" as connected. */
|
|
|
|
status = udev_device_get_sysattr_value(d, "status");
|
|
|
|
if (!streq_ptr(status, "disconnected"))
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
2014-08-26 22:08:02 +02:00
|
|
|
|
2015-06-17 00:24:05 +02:00
|
|
|
bool manager_is_docked_or_external_displays(Manager *m) {
|
2014-08-26 22:08:02 +02:00
|
|
|
int n;
|
|
|
|
|
|
|
|
/* If we are docked don't react to lid closing */
|
|
|
|
if (manager_is_docked(m)) {
|
|
|
|
log_debug("System is docked.");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we have more than one display connected,
|
|
|
|
* assume that we are docked. */
|
2015-06-17 00:24:05 +02:00
|
|
|
n = manager_count_external_displays(m);
|
2014-08-26 22:08:02 +02:00
|
|
|
if (n < 0)
|
2014-11-28 13:19:16 +01:00
|
|
|
log_warning_errno(n, "Display counting failed: %m");
|
2015-06-17 00:24:05 +02:00
|
|
|
else if (n >= 1) {
|
|
|
|
log_debug("External (%i) displays connected.", n);
|
2014-08-26 22:08:02 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|