chiark / gitweb /
Set up cgroups when logind starts
[elogind.git] / src / login / logind-core.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/types.h>
23 #include <sys/ioctl.h>
24 #include <fcntl.h>
25 #include <pwd.h>
26 #include <linux/vt.h>
27
28 #include "strv.h"
29 #include "cgroup-util.h"
30 #include "bus-util.h"
31 #include "bus-error.h"
32 #include "udev-util.h"
33 #include "logind.h"
34
35 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
36         Device *d;
37
38         assert(m);
39         assert(sysfs);
40
41         d = hashmap_get(m->devices, sysfs);
42         if (d)
43                 /* we support adding master-flags, but not removing them */
44                 d->master = d->master || master;
45         else {
46                 d = device_new(m, sysfs, master);
47                 if (!d)
48                         return -ENOMEM;
49         }
50
51         if (_device)
52                 *_device = d;
53
54         return 0;
55 }
56
57 int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
58         Seat *s;
59
60         assert(m);
61         assert(id);
62
63         s = hashmap_get(m->seats, id);
64         if (!s) {
65                 s = seat_new(m, id);
66                 if (!s)
67                         return -ENOMEM;
68         }
69
70         if (_seat)
71                 *_seat = s;
72
73         return 0;
74 }
75
76 int manager_add_session(Manager *m, const char *id, Session **_session) {
77         Session *s;
78
79         assert(m);
80         assert(id);
81
82         s = hashmap_get(m->sessions, id);
83         if (!s) {
84                 s = session_new(m, id);
85                 if (!s)
86                         return -ENOMEM;
87         }
88
89         if (_session)
90                 *_session = s;
91
92         return 0;
93 }
94
95 int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
96         User *u;
97
98         assert(m);
99         assert(name);
100
101         u = hashmap_get(m->users, UID_TO_PTR(uid));
102         if (!u) {
103                 u = user_new(m, uid, gid, name);
104                 if (!u)
105                         return -ENOMEM;
106         }
107
108         if (_user)
109                 *_user = u;
110
111         return 0;
112 }
113
114 int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
115         uid_t uid;
116         gid_t gid;
117         int r;
118
119         assert(m);
120         assert(name);
121
122         r = get_user_creds(&name, &uid, &gid, NULL, NULL);
123         if (r < 0)
124                 return r;
125
126         return manager_add_user(m, uid, gid, name, _user);
127 }
128
129 int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
130         struct passwd *p;
131
132         assert(m);
133
134         errno = 0;
135         p = getpwuid(uid);
136         if (!p)
137                 return errno ? -errno : -ENOENT;
138
139         return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
140 }
141
142 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
143         Inhibitor *i;
144
145         assert(m);
146         assert(id);
147
148         i = hashmap_get(m->inhibitors, id);
149         if (i) {
150                 if (_inhibitor)
151                         *_inhibitor = i;
152
153                 return 0;
154         }
155
156         i = inhibitor_new(m, id);
157         if (!i)
158                 return -ENOMEM;
159
160         if (_inhibitor)
161                 *_inhibitor = i;
162
163         return 0;
164 }
165
166 int manager_add_button(Manager *m, const char *name, Button **_button) {
167         Button *b;
168
169         assert(m);
170         assert(name);
171
172         b = hashmap_get(m->buttons, name);
173         if (!b) {
174                 b = button_new(m, name);
175                 if (!b)
176                         return -ENOMEM;
177         }
178
179         if (_button)
180                 *_button = b;
181
182         return 0;
183 }
184
185 int manager_watch_busname(Manager *m, const char *name) {
186         char *n;
187         int r;
188
189         assert(m);
190         assert(name);
191
192         if (set_get(m->busnames, (char*) name))
193                 return 0;
194
195         n = strdup(name);
196         if (!n)
197                 return -ENOMEM;
198
199         r = set_put(m->busnames, n);
200         if (r < 0) {
201                 free(n);
202                 return r;
203         }
204
205         return 0;
206 }
207
208 void manager_drop_busname(Manager *m, const char *name) {
209         Session *session;
210         Iterator i;
211
212         assert(m);
213         assert(name);
214
215         /* keep it if the name still owns a controller */
216         HASHMAP_FOREACH(session, m->sessions, i)
217                 if (session_is_controller(session, name))
218                         return;
219
220         free(set_remove(m->busnames, (char*) name));
221 }
222
223 int manager_process_seat_device(Manager *m, struct udev_device *d) {
224         Device *device;
225         int r;
226
227         assert(m);
228
229         if (streq_ptr(udev_device_get_action(d), "remove")) {
230
231                 device = hashmap_get(m->devices, udev_device_get_syspath(d));
232                 if (!device)
233                         return 0;
234
235                 seat_add_to_gc_queue(device->seat);
236                 device_free(device);
237
238         } else {
239                 const char *sn;
240                 Seat *seat = NULL;
241                 bool master;
242
243                 sn = udev_device_get_property_value(d, "ID_SEAT");
244                 if (isempty(sn))
245                         sn = "seat0";
246
247                 if (!seat_name_is_valid(sn)) {
248                         log_warning("Device with invalid seat name %s found, ignoring.", sn);
249                         return 0;
250                 }
251
252                 seat = hashmap_get(m->seats, sn);
253                 master = udev_device_has_tag(d, "master-of-seat");
254
255                 /* Ignore non-master devices for unknown seats */
256                 if (!master && !seat)
257                         return 0;
258
259                 r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
260                 if (r < 0)
261                         return r;
262
263                 if (!seat) {
264                         r = manager_add_seat(m, sn, &seat);
265                         if (r < 0) {
266                                 if (!device->seat)
267                                         device_free(device);
268
269                                 return r;
270                         }
271                 }
272
273                 device_attach(device, seat);
274                 seat_start(seat);
275         }
276
277         return 0;
278 }
279
280 int manager_process_button_device(Manager *m, struct udev_device *d) {
281         Button *b;
282
283         int r;
284
285         assert(m);
286
287         if (streq_ptr(udev_device_get_action(d), "remove")) {
288
289                 b = hashmap_get(m->buttons, udev_device_get_sysname(d));
290                 if (!b)
291                         return 0;
292
293                 button_free(b);
294
295         } else {
296                 const char *sn;
297
298                 r = manager_add_button(m, udev_device_get_sysname(d), &b);
299                 if (r < 0)
300                         return r;
301
302                 sn = udev_device_get_property_value(d, "ID_SEAT");
303                 if (isempty(sn))
304                         sn = "seat0";
305
306                 button_set_seat(b, sn);
307                 button_open(b);
308         }
309
310         return 0;
311 }
312
313 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
314         assert(m);
315         assert(session);
316
317         /* Without cgroups, we have no way to map from pid to
318            session.  */
319         return 0;
320 }
321
322 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
323         assert(m);
324         assert(user);
325
326         if (pid < 1)
327                 return -EINVAL;
328
329         /* Without cgroups, we have no way to map from pid to
330            user.  */
331         return 0;
332 }
333
334 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
335         Session *s;
336         bool idle_hint;
337         dual_timestamp ts = { 0, 0 };
338         Iterator i;
339
340         assert(m);
341
342         idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
343
344         HASHMAP_FOREACH(s, m->sessions, i) {
345                 dual_timestamp k;
346                 int ih;
347
348                 ih = session_get_idle_hint(s, &k);
349                 if (ih < 0)
350                         return ih;
351
352                 if (!ih) {
353                         if (!idle_hint) {
354                                 if (k.monotonic < ts.monotonic)
355                                         ts = k;
356                         } else {
357                                 idle_hint = false;
358                                 ts = k;
359                         }
360                 } else if (idle_hint) {
361
362                         if (k.monotonic > ts.monotonic)
363                                 ts = k;
364                 }
365         }
366
367         if (t)
368                 *t = ts;
369
370         return idle_hint;
371 }
372
373 bool manager_shall_kill(Manager *m, const char *user) {
374         assert(m);
375         assert(user);
376
377         if (!m->kill_user_processes)
378                 return false;
379
380         if (strv_contains(m->kill_exclude_users, user))
381                 return false;
382
383         if (strv_isempty(m->kill_only_users))
384                 return true;
385
386         return strv_contains(m->kill_only_users, user);
387 }
388
389 static int vt_is_busy(unsigned int vtnr) {
390         struct vt_stat vt_stat;
391         int r = 0;
392         _cleanup_close_ int fd;
393
394         assert(vtnr >= 1);
395
396         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
397          * we'd open the latter we'd open the foreground tty which
398          * hence would be unconditionally busy. By opening /dev/tty1
399          * we avoid this. Since tty1 is special and needs to be an
400          * explicitly loaded getty or DM this is safe. */
401
402         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
403         if (fd < 0)
404                 return -errno;
405
406         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
407                 r = -errno;
408         else
409                 r = !!(vt_stat.v_state & (1 << vtnr));
410
411         return r;
412 }
413
414 bool manager_is_docked(Manager *m) {
415         Iterator i;
416         Button *b;
417
418         HASHMAP_FOREACH(b, m->buttons, i)
419                 if (b->docked)
420                         return true;
421
422         return false;
423 }
424
425 int manager_count_displays(Manager *m) {
426         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
427         struct udev_list_entry *item = NULL, *first = NULL;
428         int r;
429         int n = 0;
430
431         e = udev_enumerate_new(m->udev);
432         if (!e)
433                 return -ENOMEM;
434
435         r = udev_enumerate_add_match_subsystem(e, "drm");
436         if (r < 0)
437                 return r;
438
439         r = udev_enumerate_scan_devices(e);
440         if (r < 0)
441                 return r;
442
443         first = udev_enumerate_get_list_entry(e);
444         udev_list_entry_foreach(item, first) {
445                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
446                 struct udev_device *p;
447                 const char *status;
448
449                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
450                 if (!d)
451                         return -ENOMEM;
452
453                 p = udev_device_get_parent(d);
454                 if (!p)
455                         continue;
456
457                 /* If the parent shares the same subsystem as the
458                  * device we are looking at then it is a connector,
459                  * which is what we are interested in. */
460                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
461                         continue;
462
463                 /* We count any connector which is not explicitly
464                  * "disconnected" as connected. */
465                 status = udev_device_get_sysattr_value(d, "status");
466                 if (!streq_ptr(status, "disconnected"))
467                         n++;
468         }
469
470         return n;
471 }
472
473 bool manager_is_docked_or_multiple_displays(Manager *m) {
474         int n;
475
476         /* If we are docked don't react to lid closing */
477         if (manager_is_docked(m)) {
478                 log_debug("System is docked.");
479                 return true;
480         }
481
482         /* If we have more than one display connected,
483          * assume that we are docked. */
484         n = manager_count_displays(m);
485         if (n < 0)
486                 log_warning_errno(n, "Display counting failed: %m");
487         else if (n > 1) {
488                 log_debug("Multiple (%i) displays connected.", n);
489                 return true;
490         }
491
492         return false;
493 }