diff --git a/Makefile.am b/Makefile.am index dad97ca45be..2d3378e309c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/TODO b/TODO index 3b3c451c762..f4d3c63464b 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 166038b3839..09db6001433 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -158,7 +158,45 @@ the operation executed anyway. Defaults to 5s. + + + HandlePowerKey= + HandleSleepKey= + HandleLidSwitch= + + 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 + no, + yes and + always. If + no logind will + never handle these keys. If + yes 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 + always 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 power-switch + udev tag will be watched for key + events. HandlePowerKey= + and HandleSleepKey= + default to yes, + HandleLidSwitch= + defaults to + no. diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index e089eebed96..5cdd0890e39 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -206,6 +206,9 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \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, } }; diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index d8ef92a0160..72fdad739b9 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -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) diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index 512fc0716bb..2d25b79c251 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -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; diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 1f3a7f37551..dd0de7805b4 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -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; diff --git a/src/login/logind.c b/src/login/logind.c index 8d4f7333c95..0382972e8bf 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -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); } } diff --git a/src/login/logind.conf b/src/login/logind.conf index c0779a7685c..4f012e5d89b 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf @@ -15,3 +15,6 @@ #Controllers= #ResetControllers=cpu #InhibitDelayMaxSec=5 +#HandlePowerKey=yes +#HandleSleepKey=yes +#HandleLidSwitch=no diff --git a/src/login/logind.h b/src/login/logind.h index 24332210c2d..6fcef6f310d 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -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); diff --git a/src/shared/util.c b/src/shared/util.c index 7d98dc6e4fb..70b159f8c31 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -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); +} diff --git a/src/shared/util.h b/src/shared/util.h index 2d890fa6a37..35ff2e3547f 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -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