chiark / gitweb /
logind: when we wake up from suspend and the lid is still closed, go to sleep immedia...
authorLennart Poettering <lennart@poettering.net>
Fri, 21 Feb 2014 20:10:00 +0000 (21:10 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Feb 2014 20:13:53 +0000 (21:13 +0100)
This is quite useful on laptops such as the Lenovo Yoga, where the power
button is placed on the front side of the laptop and can be pressed by
accident even if the lid is closed.

This reworks a bit of the logind logic to repeatedly try to suspend the
system as long as a lid is closed. We use the new "post" event source
for this, so that we don't keep things busy.

This also adds some code to check the lid status on boot, so that a
powered-off machine that is accidentaly powered on goes into suspend
immediately.

Yay! From now on I can put my Yoga safely in my backpack without fearing
that it might turn itself on and drain the battery.

src/login/logind-action.c
src/login/logind-button.c
src/login/logind-button.h
src/login/logind.c

index 7744addf69211982eb519a30e9715b7c1d672173..3bad92271349bbfc072ba1940d3d53b571ab2fbd 100644 (file)
@@ -80,6 +80,10 @@ int manager_handle_action(
 
         /* Locking is handled differently from the rest. */
         if (handle == HANDLE_LOCK) {
 
         /* Locking is handled differently from the rest. */
         if (handle == HANDLE_LOCK) {
+
+                if (!is_edge)
+                        return 0;
+
                 log_info("Locking sessions...");
                 session_send_lock_all(m, true);
                 return 1;
                 log_info("Locking sessions...");
                 session_send_lock_all(m, true);
                 return 1;
index 80236c40c1e8bd36f04cac5b04f366414d34da81..720071a2a846faeb8cd9b0b9e51ba0c32bdc8a62 100644 (file)
@@ -66,7 +66,8 @@ void button_free(Button *b) {
 
         hashmap_remove(b->manager->buttons, b->name);
 
 
         hashmap_remove(b->manager->buttons, b->name);
 
-        sd_event_source_unref(b->event_source);
+        sd_event_source_unref(b->io_event_source);
+        sd_event_source_unref(b->check_event_source);
 
         if (b->fd >= 0) {
                 /* If the device has been unplugged close() returns
 
         if (b->fd >= 0) {
                 /* If the device has been unplugged close() returns
@@ -96,24 +97,30 @@ int button_set_seat(Button *b, const char *sn) {
         return 0;
 }
 
         return 0;
 }
 
-static int button_handle(
-                Button *b,
-                InhibitWhat inhibit_key,
-                HandleAction handle,
-                bool ignore_inhibited,
-                bool is_edge) {
+static int button_recheck(sd_event_source *e, void *userdata) {
+        Button *b = userdata;
 
 
-        int r;
+        assert(b);
+        assert(b->lid_closed);
+
+        manager_handle_action(b->manager, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
+        return 1;
+}
 
 
+static int button_install_check_event_source(Button *b) {
+        int r;
         assert(b);
 
         assert(b);
 
-        r = manager_handle_action(b->manager, inhibit_key, handle, ignore_inhibited, is_edge);
-        if (r > 0)
-                /* We are executing the operation, so make sure we don't
-                 * execute another one until the lid is opened/closed again */
-                b->lid_close_queued = false;
+        /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
 
 
-        return 0;
+        if (b->check_event_source)
+                return 0;
+
+        r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b);
+        if (r < 0)
+                return r;
+
+        return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1);
 }
 
 static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
 }
 
 static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@@ -142,7 +149,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
                                    MESSAGE_ID(SD_MESSAGE_POWER_KEY),
                                    NULL);
 
                                    MESSAGE_ID(SD_MESSAGE_POWER_KEY),
                                    NULL);
 
-                        button_handle(b, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
+                        manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
                         break;
 
                 /* The kernel is a bit confused here:
                         break;
 
                 /* The kernel is a bit confused here:
@@ -157,7 +164,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
                                    MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
                                    NULL);
 
                                    MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
                                    NULL);
 
-                        button_handle(b, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
+                        manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
                         break;
 
                 case KEY_SUSPEND:
                         break;
 
                 case KEY_SUSPEND:
@@ -166,7 +173,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
                                    MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
                                    NULL);
 
                                    MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
                                    NULL);
 
-                        button_handle(b, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
+                        manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
                         break;
                 }
 
                         break;
                 }
 
@@ -178,8 +185,9 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
                                    MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
                                    NULL);
 
                                    MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
                                    NULL);
 
-                        b->lid_close_queued = true;
-                        button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
+                        b->lid_closed = true;
+                        manager_handle_action(b->manager, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
+                        button_install_check_event_source(b);
                 }
 
         } else if (ev.type == EV_SW && ev.value == 0) {
                 }
 
         } else if (ev.type == EV_SW && ev.value == 0) {
@@ -190,7 +198,8 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
                                    MESSAGE_ID(SD_MESSAGE_LID_OPENED),
                                    NULL);
 
                                    MESSAGE_ID(SD_MESSAGE_LID_OPENED),
                                    NULL);
 
-                        b->lid_close_queued = false;
+                        b->lid_closed = false;
+                        b->check_event_source = sd_event_source_unref(b->check_event_source);
                 }
         }
 
                 }
         }
 
@@ -222,7 +231,7 @@ int button_open(Button *b) {
                 goto fail;
         }
 
                 goto fail;
         }
 
-        r = sd_event_add_io(b->manager->event, &b->event_source, b->fd, EPOLLIN, button_dispatch, b);
+        r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
         if (r < 0) {
                 log_error("Failed to add button event: %s", strerror(-r));
                 goto fail;
         if (r < 0) {
                 log_error("Failed to add button event: %s", strerror(-r));
                 goto fail;
@@ -238,11 +247,22 @@ fail:
         return r;
 }
 
         return r;
 }
 
-int button_recheck(Button *b) {
+int button_check_lid(Button *b) {
+        uint8_t switches[SW_MAX/8+1] = {};
         assert(b);
 
         assert(b);
 
-        if (!b->lid_close_queued)
-                return 0;
+        if (b->fd < 0)
+                return -EINVAL;
+
+        if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
+                return -errno;
 
 
-        return button_handle(b, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, false);
+        b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
+
+        if (b->lid_closed) {
+                manager_handle_action(b->manager, INHIBIT_HANDLE_LID_SWITCH, b->manager->handle_lid_switch, b->manager->lid_switch_ignore_inhibited, true);
+                button_install_check_event_source(b);
+        }
+
+        return 0;
 }
 }
index 824106cd24e5d3e8278ec666f5190ac57c1513e2..e85aa81d0a8e75b5fc6ad310111a43c8918e5343 100644 (file)
@@ -30,17 +30,18 @@ typedef struct Button Button;
 struct Button {
         Manager *manager;
 
 struct Button {
         Manager *manager;
 
-        sd_event_source *event_source;
+        sd_event_source *io_event_source;
+        sd_event_source *check_event_source;
 
         char *name;
         char *seat;
         int fd;
 
 
         char *name;
         char *seat;
         int fd;
 
-        bool lid_close_queued;
+        bool lid_closed;
 };
 
 Button* button_new(Manager *m, const char *name);
 void button_free(Button*b);
 int button_open(Button *b);
 };
 
 Button* button_new(Manager *m, const char *name);
 void button_free(Button*b);
 int button_open(Button *b);
-int button_recheck(Button *b);
 int button_set_seat(Button *b, const char *sn);
 int button_set_seat(Button *b, const char *sn);
+int button_check_lid(Button *b);
index 28d7058fe28b3793a922e8fefec70117f690bcbf..9cbd9e817fc2c8028e68104a1e6021cbf828e860 100644 (file)
@@ -73,32 +73,28 @@ Manager *manager_new(void) {
         m->busnames = set_new(string_hash_func, string_compare_func);
 
         if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->busnames ||
         m->busnames = set_new(string_hash_func, string_compare_func);
 
         if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->busnames ||
-            !m->user_units || !m->session_units) {
-                manager_free(m);
-                return NULL;
-        }
+            !m->user_units || !m->session_units)
+                goto fail;
 
         m->kill_exclude_users = strv_new("root", NULL);
 
         m->kill_exclude_users = strv_new("root", NULL);
-        if (!m->kill_exclude_users) {
-                manager_free(m);
-                return NULL;
-        }
+        if (!m->kill_exclude_users)
+                goto fail;
 
         m->udev = udev_new();
 
         m->udev = udev_new();
-        if (!m->udev) {
-                manager_free(m);
-                return NULL;
-        }
+        if (!m->udev)
+                goto fail;
 
         r = sd_event_default(&m->event);
 
         r = sd_event_default(&m->event);
-        if (r < 0) {
-                manager_free(m);
-                return NULL;
-        }
+        if (r < 0)
+                goto fail;
 
         sd_event_set_watchdog(m->event, true);
 
         return m;
 
         sd_event_set_watchdog(m->event, true);
 
         return m;
+
+fail:
+        manager_free(m);
+        return NULL;
 }
 
 void manager_free(Manager *m) {
 }
 
 void manager_free(Manager *m) {
@@ -968,6 +964,7 @@ int manager_startup(Manager *m) {
         Seat *seat;
         Session *session;
         User *user;
         Seat *seat;
         Session *session;
         User *user;
+        Button *button;
         Inhibitor *inhibitor;
         Iterator i;
 
         Inhibitor *inhibitor;
         Iterator i;
 
@@ -1041,31 +1038,14 @@ int manager_startup(Manager *m) {
         HASHMAP_FOREACH(inhibitor, m->inhibitors, i)
                 inhibitor_start(inhibitor);
 
         HASHMAP_FOREACH(inhibitor, m->inhibitors, i)
                 inhibitor_start(inhibitor);
 
+        HASHMAP_FOREACH(button, m->buttons, i)
+                button_check_lid(button);
+
         manager_dispatch_idle_action(NULL, 0, m);
 
         return 0;
 }
 
         manager_dispatch_idle_action(NULL, 0, m);
 
         return 0;
 }
 
-static int manager_recheck_buttons(Manager *m) {
-        Iterator i;
-        Button *b;
-        int r = 0;
-
-        assert(m);
-
-        HASHMAP_FOREACH(b, m->buttons, i) {
-                int q;
-
-                q = button_recheck(b);
-                if (q > 0)
-                        return 1;
-                if (q < 0)
-                        r = q;
-        }
-
-        return r;
-}
-
 int manager_run(Manager *m) {
         int r;
 
 int manager_run(Manager *m) {
         int r;
 
@@ -1085,9 +1065,6 @@ int manager_run(Manager *m) {
                 if (manager_dispatch_delayed(m) > 0)
                         continue;
 
                 if (manager_dispatch_delayed(m) > 0)
                         continue;
 
-                if (manager_recheck_buttons(m) > 0)
-                        continue;
-
                 if (m->action_what != 0 && !m->action_job) {
                         usec_t x, y;
 
                 if (m->action_what != 0 && !m->action_job) {
                         usec_t x, y;