chiark / gitweb /
Prep v234.3: Set defaults for the sleep config *after* loading the config file.
[elogind.git] / src / login / elogind.c
1 /***
2   This file is part of elogind.
3
4   Copyright 2017 Sven Eden
5
6   elogind is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   elogind is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with elogind; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20
21 #include "bus-util.h"
22 #include "cgroup.h"
23 #include "elogind.h"
24 #include "fd-util.h"
25 #include "mount-setup.h"
26 #include "socket-util.h"
27 #include "string-util.h"
28 #include "strv.h"
29 #include "umask-util.h"
30
31
32 #define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024)
33
34
35 static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
36         Manager *m = userdata;
37         char buf[PATH_MAX+1];
38         ssize_t n;
39
40         n = recv(fd, buf, sizeof(buf), 0);
41         if (n < 0)
42                 return log_error_errno(errno, "Failed to read cgroups agent message: %m");
43         if (n == 0) {
44                 log_error("Got zero-length cgroups agent message, ignoring.");
45                 return 0;
46         }
47         if ((size_t) n >= sizeof(buf)) {
48                 log_error("Got overly long cgroups agent message, ignoring.");
49                 return 0;
50         }
51
52         if (memchr(buf, 0, n)) {
53                 log_error("Got cgroups agent message with embedded NUL byte, ignoring.");
54                 return 0;
55         }
56         buf[n] = 0;
57
58         manager_notify_cgroup_empty(m, buf);
59
60         return 0;
61 }
62
63 /// Add-On for manager_connect_bus()
64 /// Original: src/core/manager.c:manager_setup_cgroups_agent()
65 int elogind_setup_cgroups_agent(Manager *m) {
66
67         static const union sockaddr_union sa = {
68                 .un.sun_family = AF_UNIX,
69                 .un.sun_path = "/run/systemd/cgroups-agent",
70         };
71         int r = 0;
72
73         /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering
74          * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and
75          * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on
76          * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number
77          * of D-Bus connections may be queued until the kernel will start dropping further incoming connections,
78          * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX
79          * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and
80          * we thus won't lose messages.
81          *
82          * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen
83          * to it. The system instance hence listens on this special socket, but the user instances listen on the system
84          * bus for these messages. */
85
86         if (m->test_run)
87                 return 0;
88
89         if (!MANAGER_IS_SYSTEM(m))
90                 return 0;
91
92         r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
93         if (r < 0)
94                 return log_error_errno(r, "Failed to determine whether unified cgroups hierarchy is used: %m");
95         if (r > 0) /* We don't need this anymore on the unified hierarchy */
96                 return 0;
97
98         if (m->cgroups_agent_fd < 0) {
99                 _cleanup_close_ int fd = -1;
100
101                 /* First free all secondary fields */
102                 m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source);
103
104                 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
105                 if (fd < 0)
106                         return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m");
107
108                 fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE);
109
110                 (void) unlink(sa.un.sun_path);
111
112                 /* Only allow root to connect to this socket */
113                 RUN_WITH_UMASK(0077)
114                         r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
115                 if (r < 0)
116                         return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
117
118                 m->cgroups_agent_fd = fd;
119                 fd = -1;
120         }
121
122         if (!m->cgroups_agent_event_source) {
123                 r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m);
124                 if (r < 0)
125                         return log_error_errno(r, "Failed to allocate cgroups agent event source: %m");
126
127                 /* Process cgroups notifications early, but after having processed service notification messages or
128                  * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification,
129                  * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of
130                  * cgroup inotify for the unified cgroup stuff. */
131                 r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5);
132                 if (r < 0)
133                         return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m");
134
135                 (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent");
136         }
137
138         return 0;
139 }
140
141 /// Add-On for manager_free()
142 void elogind_manager_free(Manager* m) {
143
144         manager_shutdown_cgroup(m, true);
145
146         sd_event_source_unref(m->cgroups_agent_event_source);
147
148         safe_close(m->cgroups_agent_fd);
149
150         strv_free(m->suspend_mode);
151         strv_free(m->suspend_state);
152         strv_free(m->hibernate_mode);
153         strv_free(m->hibernate_state);
154         strv_free(m->hybrid_sleep_mode);
155         strv_free(m->hybrid_sleep_state);
156 }
157
158 /// Add-On for manager_new()
159 int elogind_manager_new(Manager* m) {
160         int r = 0;
161
162         m->cgroups_agent_fd = -1;
163         m->pin_cgroupfs_fd  = -1;
164         m->test_run         = false;
165
166         /* Init sleep modes and states */
167         m->suspend_mode       = NULL;
168         m->suspend_state      = NULL;
169         m->hibernate_mode     = NULL;
170         m->hibernate_state    = NULL;
171         m->hybrid_sleep_mode  = NULL;
172         m->hybrid_sleep_state = NULL;
173
174         /* If elogind should be its own controller, mount its cgroup */
175         if (streq(SYSTEMD_CGROUP_CONTROLLER, "_elogind")) {
176                 m->is_system = true;
177                 r = mount_setup(true);
178         } else
179                 m->is_system = false;
180
181         /* Make cgroups */
182         if (r > -1)
183                 r = manager_setup_cgroup(m);
184
185         return r;
186 }
187
188 /// Add-On for manager_reset_config()
189 void elogind_manager_reset_config(Manager* m) {
190
191 #ifdef ENABLE_DEBUG_ELOGIND
192         int dbg_cnt;
193 #endif // ENABLE_DEBUG_ELOGIND
194
195         /* Set default Sleep config if not already set by logind.conf */
196         if (!m->suspend_state)
197                 m->suspend_state = strv_new("mem", "standby", "freeze", NULL);
198         if (!m->hibernate_mode)
199                 m->hibernate_mode = strv_new("platform", "shutdown", NULL);
200         if (!m->hibernate_state)
201                 m->hibernate_state = strv_new("disk", NULL);
202         if (!m->hybrid_sleep_mode)
203                 m->hybrid_sleep_mode = strv_new("suspend", "platform", "shutdown", NULL);
204         if (!m->hybrid_sleep_state)
205                 m->hybrid_sleep_state = strv_new("disk", NULL);
206
207 #ifdef ENABLE_DEBUG_ELOGIND
208         dbg_cnt = -1;
209         while (m->suspend_mode && m->suspend_mode[++dbg_cnt])
210                 log_debug_elogind("suspend_mode[%d] = %s",
211                                   dbg_cnt, m->suspend_mode[dbg_cnt]);
212         dbg_cnt = -1;
213         while (m->suspend_state[++dbg_cnt])
214                 log_debug_elogind("suspend_state[%d] = %s",
215                                   dbg_cnt, m->suspend_state[dbg_cnt]);
216         dbg_cnt = -1;
217         while (m->hibernate_mode[++dbg_cnt])
218                 log_debug_elogind("hibernate_mode[%d] = %s",
219                                   dbg_cnt, m->hibernate_mode[dbg_cnt]);
220         dbg_cnt = -1;
221         while (m->hibernate_state[++dbg_cnt])
222                 log_debug_elogind("hibernate_state[%d] = %s",
223                                   dbg_cnt, m->hibernate_state[dbg_cnt]);
224         dbg_cnt = -1;
225         while (m->hybrid_sleep_mode[++dbg_cnt])
226                 log_debug_elogind("hybrid_sleep_mode[%d] = %s",
227                                   dbg_cnt, m->hybrid_sleep_mode[dbg_cnt]);
228         dbg_cnt = -1;
229         while (m->hybrid_sleep_state[++dbg_cnt])
230                 log_debug_elogind("hybrid_sleep_state[%d] = %s",
231                                   dbg_cnt, m->hybrid_sleep_state[dbg_cnt]);
232 #endif // ENABLE_DEBUG_ELOGIND
233 }