swayaway
a drop-in replacement for swayidle(1)
logind.c
5023 bytes raw
1/* this is deliberately the ONLY thing swayaway does on the system bus:
2 * it listens for org.freedesktop.login1.Manager.PrepareForSleep so that
3 * `before-sleep` and `after-resume` commands can be run around suspend
4 * it is implemented with plain libdbus-1 method calls and signal matches,
5 * so there is no elogind, no libsystemd, and no dependency on logind
6 * actually running. (if there is no logind, these events simply never fire) */
7
8#include "swayaway.h"
9
10#include <errno.h>
11#include <fcntl.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <unistd.h>
16#include <dbus/dbus.h>
17
18#define LOGIND_SERVICE "org.freedesktop.login1"
19#define LOGIND_PATH "/org/freedesktop/login1"
20#define LOGIND_MANAGER "org.freedesktop.login1.Manager"
21
22static void handle_prepare_for_sleep(DBusConnection *conn, DBusMessage *msg)
23{
24 (void)conn;
25 dbus_bool_t going_down = TRUE;
26 DBusError err;
27 dbus_error_init(&err);
28
29 if (!dbus_message_get_args(msg, &err, DBUS_TYPE_BOOLEAN, &going_down,
30 DBUS_TYPE_INVALID)) {
31 sway_log_debug("bad PrepareForSleep: %s",
32 err.message ? err.message : "unknown error");
33 dbus_error_free(&err);
34 return;
35 }
36 sway_log_debug("PrepareForSleep %s", going_down ? "going down" : "resumed");
37
38 if (going_down) {
39 if (state.before_sleep_cmd) {
40 cmd_exec(state.before_sleep_cmd);
41 }
42 // releasing the delay lock lets logind actually suspend now
43 logind_release_sleep_lock();
44 } else {
45 if (state.after_resume_cmd) {
46 cmd_exec(state.after_resume_cmd);
47 }
48 logind_acquire_sleep_lock();
49 }
50}
51
52static DBusHandlerResult filter(DBusConnection *conn, DBusMessage *msg,
53 void *ud)
54{
55 (void)ud;
56 if (dbus_message_is_signal(msg, LOGIND_MANAGER, "PrepareForSleep")) {
57 handle_prepare_for_sleep(conn, msg);
58 return DBUS_HANDLER_RESULT_HANDLED;
59 }
60 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
61}
62
63void logind_init(void)
64{
65 DBusError err;
66 dbus_error_init(&err);
67
68 state.system_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
69 if (state.system_conn == NULL) {
70 sway_log("could not connect to the system bus: %s",
71 err.message ? err.message : "unknown error");
72 dbus_error_free(&err);
73 return;
74 }
75 dbus_error_free(&err);
76 dbus_connection_set_exit_on_disconnect(state.system_conn, FALSE);
77 dbus_connection_get_unix_fd(state.system_conn, &state.system_fd);
78
79 dbus_connection_add_filter(state.system_conn, filter, NULL, NULL);
80
81 DBusError match_err;
82 dbus_error_init(&match_err);
83 dbus_bus_add_match(state.system_conn,
84 "type='signal',interface='org.freedesktop.login1.Manager',"
85 "member='PrepareForSleep',path='/org/freedesktop/login1'",
86 &match_err);
87 if (dbus_error_is_set(&match_err)) {
88 sway_log("could not add PrepareForSleep match: %s", match_err.message);
89 }
90 dbus_error_free(&match_err);
91
92 logind_acquire_sleep_lock();
93}
94
95/* hold a logind "sleep" delay inhibitor so logind waits for the before-sleep
96 * command to finish
97 *
98 * inhibit returns a unix fd that keeps the lock alive until closed
99 * without logind this fails harmlessly and sleep events simply won't fire. */
100void logind_acquire_sleep_lock(void)
101{
102 if (state.system_conn == NULL || state.sleep_lock_fd >= 0) {
103 return;
104 }
105 if (!state.before_sleep_cmd && !state.after_resume_cmd) {
106 return;
107 }
108
109 DBusMessage *call = dbus_message_new_method_call(LOGIND_SERVICE,
110 LOGIND_PATH, LOGIND_MANAGER, "Inhibit");
111 if (!call) {
112 return;
113 }
114 const char *type = "sleep";
115 const char *who = "swayaway";
116 const char *why = "Swayaway is preventing sleep";
117 const char *mode = "delay";
118 dbus_message_append_args(call, DBUS_TYPE_STRING, &type,
119 DBUS_TYPE_STRING, &who, DBUS_TYPE_STRING, &why,
120 DBUS_TYPE_STRING, &mode, DBUS_TYPE_INVALID);
121
122 DBusError err;
123 dbus_error_init(&err);
124 DBusMessage *reply = dbus_connection_send_with_reply_and_block(
125 state.system_conn, call, -1, &err);
126 dbus_message_unref(call);
127 if (reply == NULL) {
128 sway_log_debug("logind Inhibit failed: %s",
129 err.message ? err.message : "unknown error");
130 dbus_error_free(&err);
131 return;
132 }
133 dbus_error_free(&err);
134
135 int fd = -1;
136 if (!dbus_message_get_args(reply, &err, DBUS_TYPE_UNIX_FD, &fd,
137 DBUS_TYPE_INVALID)) {
138 sway_log_debug("logind Inhibit reply malformed: %s",
139 err.message ? err.message : "unknown error");
140 dbus_error_free(&err);
141 dbus_message_unref(reply);
142 return;
143 }
144 dbus_message_unref(reply);
145
146 int dup_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
147 close(fd);
148 if (dup_fd >= 0) {
149 state.sleep_lock_fd = dup_fd;
150 sway_log_debug("holding logind sleep delay lock (fd %d)", dup_fd);
151 } else {
152 sway_log("could not duplicate sleep lock fd: %s", strerror(errno));
153 }
154}
155
156void logind_release_sleep_lock(void)
157{
158 if (state.sleep_lock_fd >= 0) {
159 sway_log_debug("releasing logind sleep delay lock");
160 close(state.sleep_lock_fd);
161 state.sleep_lock_fd = -1;
162 }
163}