logind: optionally handle power, sleep and lid switch events

This takes handling of chassis power and sleep keys as well as the lid
switch over from acpid.

This logic is enabled by default for power and sleep keys, but not for
the lid switch.

If a graphical session is in the foreground no action is taken under the
assumption that the graphical session does this.
This commit is contained in:
Lennart Poettering 2012-05-30 15:01:51 +02:00
parent 939b8f14dc
commit 069cfc85f8
12 changed files with 324 additions and 29 deletions

View File

@ -2658,6 +2658,8 @@ systemd_logind_SOURCES = \
src/login/logind-dbus.c \
src/login/logind-device.c \
src/login/logind-device.h \
src/login/logind-button.c \
src/login/logind-button.h \
src/login/logind-seat.c \
src/login/logind-seat.h \
src/login/logind-session.c \
@ -2873,7 +2875,8 @@ rootlibexec_PROGRAMS += \
dist_udevrules_DATA += \
src/login/70-uaccess.rules \
src/login/71-seat.rules
src/login/71-seat.rules \
src/login/70-power-switch.rules
nodist_udevrules_DATA += \
src/login/73-seat-late.rules

3
TODO
View File

@ -31,6 +31,9 @@ Bugfixes:
* properly handle .mount unit state tracking when two mount points are stacked one on top of another on the exact same mount point.
Features:
* nspawn: make use of device cgroup contrller by default
* parse kernel cmdline option for capability bset
* logind: listen to power-button events

View File

@ -158,7 +158,45 @@
the operation executed
anyway. Defaults to
5s.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>HandlePowerKey=</varname></term>
<term><varname>HandleSleepKey=</varname></term>
<term><varname>HandleLidSwitch=</varname></term>
<listitem><para>Controls whether
logind shall handle the system power
and sleep keys and the lid switch to
trigger system power-off or
suspend. Can be one of
<literal>no</literal>,
<literal>yes</literal> and
<literal>always</literal>. If
<literal>no</literal> logind will
never handle these keys. If
<literal>yes</literal> logind will
handle these keys when no user is
logged in and no inhibitor lock is
taken, and trigger a warnig beep
otherwise. If set to
<literal>always</literal> logind will
handle these keys even if a user is
logged in or an inhibitor lock is
taken. In all cases logind will not
handle these keys if a graphical
session is in the foreground under the
assumption that the graphical session
will handle these keys
internally. Only input devices with
the <literal>power-switch</literal>
udev tag will be watched for key
events. <varname>HandlePowerKey=</varname>
and <varname>HandleSleepKey=</varname>
default to <literal>yes</literal>,
<varname>HandleLidSwitch=</varname>
defaults to
<literal>no</literal>.</para></listitem>
</varlistentry>
</variablelist>

View File

@ -206,6 +206,9 @@
" <property name=\"BlockInhibited\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DelayInhibited\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"InhibitDelayMaxUSec\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"HandlePowerKey\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"HandleSleepKey\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"HandleLidSwitch\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
#define INTROSPECTION_BEGIN \
@ -1136,6 +1139,36 @@ finish:
return 0;
}
int bus_manager_shutdown_or_sleep_now_or_later(
Manager *m,
const char *unit_name,
InhibitWhat w,
DBusError *error) {
bool delayed;
int r;
assert(m);
assert(unit_name);
assert(w >= 0);
assert(w <= _INHIBIT_WHAT_MAX);
delayed =
m->inhibit_delay_max > 0 &&
manager_is_inhibited(m, w, INHIBIT_DELAY, NULL);
if (delayed)
/* Shutdown is delayed, keep in mind what we
* want to do, and start a timeout */
r = delay_shutdown_or_sleep(m, w, unit_name);
else
/* Shutdown is not delayed, execute it
* immediately */
r = send_start_unit(m->bus, unit_name, error);
return r;
}
static int bus_manager_do_shutdown_or_sleep(
Manager *m,
DBusConnection *connection,
@ -1150,7 +1183,7 @@ static int bus_manager_do_shutdown_or_sleep(
DBusMessage **_reply) {
dbus_bool_t interactive;
bool multiple_sessions, blocked, delayed;
bool multiple_sessions, blocked;
DBusMessage *reply = NULL;
int r;
@ -1207,19 +1240,7 @@ static int bus_manager_do_shutdown_or_sleep(
return r;
}
delayed =
m->inhibit_delay_max > 0 &&
manager_is_inhibited(m, w, INHIBIT_DELAY, NULL);
if (delayed) {
/* Shutdown is delayed, keep in mind what we
* want to do, and start a timeout */
r = delay_shutdown_or_sleep(m, w, unit_name);
} else
/* Shutdown is not delayed, execute it
* immediately */
r = send_start_unit(connection, unit_name, error);
r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
if (r < 0)
return r;
@ -1231,6 +1252,8 @@ static int bus_manager_do_shutdown_or_sleep(
return 0;
}
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_button, handle_button, HandleButton);
static const BusProperty bus_login_manager_properties[] = {
{ "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true },
{ "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true },
@ -1245,6 +1268,9 @@ static const BusProperty bus_login_manager_properties[] = {
{ "BlockInhibited", bus_manager_append_inhibited, "s", 0 },
{ "DelayInhibited", bus_manager_append_inhibited, "s", 0 },
{ "InhibitDelayMaxUSec", bus_property_append_usec, "t", offsetof(Manager, inhibit_delay_max) },
{ "HandlePowerKey", bus_manager_append_handle_button, "s", offsetof(Manager, handle_power_key) },
{ "HandleSleepKey", bus_manager_append_handle_button, "s", offsetof(Manager, handle_sleep_key) },
{ "HandleLidSwitch", bus_manager_append_handle_button, "s", offsetof(Manager, handle_lid_switch) },
{ NULL, }
};

View File

@ -21,3 +21,6 @@ Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclud
Login.Controllers, config_parse_strv, 0, offsetof(Manager, controllers)
Login.ResetControllers, config_parse_strv, 0, offsetof(Manager, reset_controllers)
Login.InhibitDelayMaxSec,config_parse_usec, 0, offsetof(Manager, inhibit_delay_max)
Login.HandlePowerKey, config_parse_handle_button, 0, offsetof(Manager, handle_power_key)
Login.HandleSleepKey, config_parse_handle_button, 0, offsetof(Manager, handle_sleep_key)
Login.HandleLidSwitch, config_parse_handle_button, 0, offsetof(Manager, handle_lid_switch)

View File

@ -297,7 +297,7 @@ int inhibitor_create_fifo(Inhibitor *i) {
zero(ev);
ev.events = 0;
ev.data.u32 = FD_FIFO_BASE + i->fifo_fd;
ev.data.u32 = FD_OTHER_BASE + i->fifo_fd;
if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0)
return -errno;

View File

@ -841,7 +841,7 @@ int session_create_fifo(Session *s) {
zero(ev);
ev.events = 0;
ev.data.u32 = FD_FIFO_BASE + s->fifo_fd;
ev.data.u32 = FD_OTHER_BASE + s->fifo_fd;
if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
return -errno;

View File

@ -48,22 +48,29 @@ Manager *manager_new(void) {
m->bus_fd = -1;
m->udev_seat_fd = -1;
m->udev_vcsa_fd = -1;
m->udev_button_fd = -1;
m->epoll_fd = -1;
m->n_autovts = 6;
m->inhibit_delay_max = 5 * USEC_PER_SEC;
m->handle_power_key = HANDLE_YES;
m->handle_sleep_key = HANDLE_YES;
m->handle_lid_switch = HANDLE_NO;
m->devices = hashmap_new(string_hash_func, string_compare_func);
m->seats = hashmap_new(string_hash_func, string_compare_func);
m->sessions = hashmap_new(string_hash_func, string_compare_func);
m->users = hashmap_new(trivial_hash_func, trivial_compare_func);
m->inhibitors = hashmap_new(string_hash_func, string_compare_func);
m->buttons = hashmap_new(string_hash_func, string_compare_func);
m->cgroups = hashmap_new(string_hash_func, string_compare_func);
m->session_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
m->inhibitor_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
m->button_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors ||
!m->cgroups || !m->session_fds || !m->inhibitor_fds) {
if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons ||
!m->cgroups || !m->session_fds || !m->inhibitor_fds || !m->button_fds) {
manager_free(m);
return NULL;
}
@ -95,6 +102,7 @@ void manager_free(Manager *m) {
Device *d;
Seat *s;
Inhibitor *i;
Button *b;
assert(m);
@ -113,24 +121,30 @@ void manager_free(Manager *m) {
while ((i = hashmap_first(m->inhibitors)))
inhibitor_free(i);
while ((b = hashmap_first(m->buttons)))
button_free(b);
hashmap_free(m->devices);
hashmap_free(m->seats);
hashmap_free(m->sessions);
hashmap_free(m->users);
hashmap_free(m->inhibitors);
hashmap_free(m->buttons);
hashmap_free(m->cgroups);
hashmap_free(m->session_fds);
hashmap_free(m->inhibitor_fds);
hashmap_free(m->button_fds);
if (m->console_active_fd >= 0)
close_nointr_nofail(m->console_active_fd);
if (m->udev_seat_monitor)
udev_monitor_unref(m->udev_seat_monitor);
if (m->udev_vcsa_monitor)
udev_monitor_unref(m->udev_vcsa_monitor);
if (m->udev_button_monitor)
udev_monitor_unref(m->udev_button_monitor);
if (m->udev)
udev_unref(m->udev);
@ -304,6 +318,30 @@ int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
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_process_seat_device(Manager *m, struct udev_device *d) {
Device *device;
int r;
@ -351,6 +389,39 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) {
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_enumerate_devices(Manager *m) {
struct udev_list_entry *item = NULL, *first = NULL;
struct udev_enumerate *e;
@ -404,6 +475,58 @@ finish:
return r;
}
int manager_enumerate_buttons(Manager *m) {
struct udev_list_entry *item = NULL, *first = NULL;
struct udev_enumerate *e;
int r;
assert(m);
/* Loads buttons from udev */
e = udev_enumerate_new(m->udev);
if (!e) {
r = -ENOMEM;
goto finish;
}
r = udev_enumerate_add_match_subsystem(e, "input");
if (r < 0)
goto finish;
r = udev_enumerate_add_match_tag(e, "power-switch");
if (r < 0)
goto finish;
r = udev_enumerate_scan_devices(e);
if (r < 0)
goto finish;
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
struct udev_device *d;
int k;
d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
if (!d) {
r = -ENOMEM;
goto finish;
}
k = manager_process_button_device(m, d);
udev_device_unref(d);
if (k < 0)
r = k;
}
finish:
if (e)
udev_enumerate_unref(e);
return r;
}
int manager_enumerate_seats(Manager *m) {
DIR *d;
struct dirent *de;
@ -756,6 +879,22 @@ int manager_dispatch_vcsa_udev(Manager *m) {
return r;
}
int manager_dispatch_button_udev(Manager *m) {
struct udev_device *d;
int r;
assert(m);
d = udev_monitor_receive_device(m->udev_button_monitor);
if (!d)
return -ENOMEM;
r = manager_process_button_device(m, d);
udev_device_unref(d);
return r;
}
int manager_dispatch_console(Manager *m) {
assert(m);
@ -926,9 +1065,10 @@ void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
session_add_to_gc_queue(s);
}
static void manager_pipe_notify_eof(Manager *m, int fd) {
static void manager_dispatch_other(Manager *m, int fd) {
Session *s;
Inhibitor *i;
Button *b;
assert_se(m);
assert_se(fd >= 0);
@ -949,7 +1089,14 @@ static void manager_pipe_notify_eof(Manager *m, int fd) {
return;
}
assert_not_reached("Got EOF on unknown pipe");
b = hashmap_get(m->button_fds, INT_TO_PTR(fd + 1));
if (b) {
assert(b->fd == fd);
button_process(b);
return;
}
assert_not_reached("Got event for unknown fd");
}
static int manager_connect_bus(Manager *m) {
@ -1064,6 +1211,7 @@ static int manager_connect_udev(Manager *m) {
assert(m);
assert(!m->udev_seat_monitor);
assert(!m->udev_vcsa_monitor);
assert(!m->udev_button_monitor);
m->udev_seat_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
if (!m->udev_seat_monitor)
@ -1087,13 +1235,38 @@ static int manager_connect_udev(Manager *m) {
ev.events = EPOLLIN;
ev.data.u32 = FD_SEAT_UDEV;
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_seat_fd, &ev) < 0)
return -errno;
m->udev_button_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
if (!m->udev_button_monitor)
return -ENOMEM;
r = udev_monitor_filter_add_match_tag(m->udev_button_monitor, "power-switch");
if (r < 0)
return r;
r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_button_monitor, "input", NULL);
if (r < 0)
return r;
r = udev_monitor_enable_receiving(m->udev_button_monitor);
if (r < 0)
return r;
m->udev_button_fd = udev_monitor_get_fd(m->udev_button_monitor);
zero(ev);
ev.events = EPOLLIN;
ev.data.u32 = FD_BUTTON_UDEV;
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_button_fd, &ev) < 0)
return -errno;
/* Don't bother watching VCSA devices, if nobody cares */
if (m->n_autovts <= 0 || m->console_active_fd < 0)
return 0;
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_seat_fd, &ev) < 0)
return -errno;
m->udev_vcsa_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
if (!m->udev_vcsa_monitor)
return -ENOMEM;
@ -1239,6 +1412,7 @@ int manager_startup(Manager *m) {
manager_enumerate_users(m);
manager_enumerate_sessions(m);
manager_enumerate_inhibitors(m);
manager_enumerate_buttons(m);
/* Remove stale objects before we start them */
manager_gc(m, false);
@ -1308,6 +1482,10 @@ int manager_run(Manager *m) {
manager_dispatch_vcsa_udev(m);
break;
case FD_BUTTON_UDEV:
manager_dispatch_button_udev(m);
break;
case FD_CONSOLE:
manager_dispatch_console(m);
break;
@ -1317,8 +1495,8 @@ int manager_run(Manager *m) {
break;
default:
if (event.data.u32 >= FD_FIFO_BASE)
manager_pipe_notify_eof(m, event.data.u32 - FD_FIFO_BASE);
if (event.data.u32 >= FD_OTHER_BASE)
manager_dispatch_other(m, event.data.u32 - FD_OTHER_BASE);
}
}

View File

@ -15,3 +15,6 @@
#Controllers=
#ResetControllers=cpu
#InhibitDelayMaxSec=5
#HandlePowerKey=yes
#HandleSleepKey=yes
#HandleLidSwitch=no

View File

@ -40,6 +40,7 @@ typedef struct Manager Manager;
#include "logind-session.h"
#include "logind-user.h"
#include "logind-inhibit.h"
#include "logind-button.h"
struct Manager {
DBusConnection *bus;
@ -49,16 +50,18 @@ struct Manager {
Hashmap *sessions;
Hashmap *users;
Hashmap *inhibitors;
Hashmap *buttons;
LIST_HEAD(Seat, seat_gc_queue);
LIST_HEAD(Session, session_gc_queue);
LIST_HEAD(User, user_gc_queue);
struct udev *udev;
struct udev_monitor *udev_seat_monitor, *udev_vcsa_monitor;
struct udev_monitor *udev_seat_monitor, *udev_vcsa_monitor, *udev_button_monitor;
int udev_seat_fd;
int udev_vcsa_fd;
int udev_button_fd;
int console_active_fd;
int bus_fd;
@ -81,6 +84,7 @@ struct Manager {
Hashmap *cgroups;
Hashmap *session_fds;
Hashmap *inhibitor_fds;
Hashmap *button_fds;
/* If a shutdown was delayed due to a inhibitor this contains
the unit name we are supposed to start after the delay is
@ -90,20 +94,26 @@ struct Manager {
usec_t delayed_timestamp;
usec_t inhibit_delay_max;
HandleButton handle_power_key;
HandleButton handle_sleep_key;
HandleButton handle_lid_switch;
};
enum {
FD_SEAT_UDEV,
FD_VCSA_UDEV,
FD_BUTTON_UDEV,
FD_CONSOLE,
FD_BUS,
FD_FIFO_BASE
FD_OTHER_BASE
};
Manager *manager_new(void);
void manager_free(Manager *m);
int manager_add_device(Manager *m, const char *sysfs, Device **_device);
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, User *u, const char *id, Session **_session);
int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user);
@ -112,11 +122,15 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user);
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
int manager_process_seat_device(Manager *m, struct udev_device *d);
int manager_process_button_device(Manager *m, struct udev_device *d);
int manager_dispatch_seat_udev(Manager *m);
int manager_dispatch_vcsa_udev(Manager *m);
int manager_dispatch_button_udev(Manager *m);
int manager_dispatch_console(Manager *m);
int manager_enumerate_devices(Manager *m);
int manager_enumerate_buttons(Manager *m);
int manager_enumerate_seats(Manager *m);
int manager_enumerate_sessions(Manager *m);
int manager_enumerate_users(Manager *m);
@ -139,6 +153,8 @@ extern const DBusObjectPathVTable bus_manager_vtable;
DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, void *userdata);
int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, DBusError *error);
int manager_send_changed(Manager *manager, const char *properties);
int manager_dispatch_delayed(Manager *manager);

View File

@ -5639,3 +5639,25 @@ bool in_initrd(void) {
return saved;
}
void warn_melody(void) {
int fd;
fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return;
/* Yeah, this is synchronous. Kinda sucks. Bute well... */
ioctl(fd, KIOCSOUND, (int)(1193180/440));
usleep(125*USEC_PER_MSEC);
ioctl(fd, KIOCSOUND, (int)(1193180/220));
usleep(125*USEC_PER_MSEC);
ioctl(fd, KIOCSOUND, (int)(1193180/220));
usleep(125*USEC_PER_MSEC);
ioctl(fd, KIOCSOUND, 0);
close_nointr_nofail(fd);
}

View File

@ -513,4 +513,7 @@ int can_sleep(const char *type);
bool is_valid_documentation_url(const char *url);
bool in_initrd(void);
void warn_melody(void);
#endif