chiark / gitweb /
logind: ignore lid switch if more than 1 display is connected
[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/stat.h>
24 #include <sys/ioctl.h>
25 #include <fcntl.h>
26 #include <pwd.h>
27 #include <unistd.h>
28 #include <linux/vt.h>
29
30 #include "strv.h"
31 #include "cgroup-util.h"
32 #include "audit.h"
33 #include "bus-util.h"
34 #include "bus-error.h"
35 #include "udev-util.h"
36 #include "logind.h"
37
38 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
39         Device *d;
40
41         assert(m);
42         assert(sysfs);
43
44         d = hashmap_get(m->devices, sysfs);
45         if (d) {
46                 if (_device)
47                         *_device = d;
48
49                 /* we support adding master-flags, but not removing them */
50                 d->master = d->master || master;
51
52                 return 0;
53         }
54
55         d = device_new(m, sysfs, master);
56         if (!d)
57                 return -ENOMEM;
58
59         if (_device)
60                 *_device = d;
61
62         return 0;
63 }
64
65 int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
66         Seat *s;
67
68         assert(m);
69         assert(id);
70
71         s = hashmap_get(m->seats, id);
72         if (s) {
73                 if (_seat)
74                         *_seat = s;
75
76                 return 0;
77         }
78
79         s = seat_new(m, id);
80         if (!s)
81                 return -ENOMEM;
82
83         if (_seat)
84                 *_seat = s;
85
86         return 0;
87 }
88
89 int manager_add_session(Manager *m, const char *id, Session **_session) {
90         Session *s;
91
92         assert(m);
93         assert(id);
94
95         s = hashmap_get(m->sessions, id);
96         if (s) {
97                 if (_session)
98                         *_session = s;
99
100                 return 0;
101         }
102
103         s = session_new(m, id);
104         if (!s)
105                 return -ENOMEM;
106
107         if (_session)
108                 *_session = s;
109
110         return 0;
111 }
112
113 int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
114         User *u;
115
116         assert(m);
117         assert(name);
118
119         u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
120         if (u) {
121                 if (_user)
122                         *_user = u;
123
124                 return 0;
125         }
126
127         u = user_new(m, uid, gid, name);
128         if (!u)
129                 return -ENOMEM;
130
131         if (_user)
132                 *_user = u;
133
134         return 0;
135 }
136
137 int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
138         uid_t uid;
139         gid_t gid;
140         int r;
141
142         assert(m);
143         assert(name);
144
145         r = get_user_creds(&name, &uid, &gid, NULL, NULL);
146         if (r < 0)
147                 return r;
148
149         return manager_add_user(m, uid, gid, name, _user);
150 }
151
152 int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
153         struct passwd *p;
154
155         assert(m);
156
157         errno = 0;
158         p = getpwuid(uid);
159         if (!p)
160                 return errno ? -errno : -ENOENT;
161
162         return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
163 }
164
165 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
166         Inhibitor *i;
167
168         assert(m);
169         assert(id);
170
171         i = hashmap_get(m->inhibitors, id);
172         if (i) {
173                 if (_inhibitor)
174                         *_inhibitor = i;
175
176                 return 0;
177         }
178
179         i = inhibitor_new(m, id);
180         if (!i)
181                 return -ENOMEM;
182
183         if (_inhibitor)
184                 *_inhibitor = i;
185
186         return 0;
187 }
188
189 int manager_add_button(Manager *m, const char *name, Button **_button) {
190         Button *b;
191
192         assert(m);
193         assert(name);
194
195         b = hashmap_get(m->buttons, name);
196         if (b) {
197                 if (_button)
198                         *_button = b;
199
200                 return 0;
201         }
202
203         b = button_new(m, name);
204         if (!b)
205                 return -ENOMEM;
206
207         if (_button)
208                 *_button = b;
209
210         return 0;
211 }
212
213 int manager_watch_busname(Manager *m, const char *name) {
214         char *n;
215         int r;
216
217         assert(m);
218         assert(name);
219
220         if (set_get(m->busnames, (char*) name))
221                 return 0;
222
223         n = strdup(name);
224         if (!n)
225                 return -ENOMEM;
226
227         r = set_put(m->busnames, n);
228         if (r < 0) {
229                 free(n);
230                 return r;
231         }
232
233         return 0;
234 }
235
236 void manager_drop_busname(Manager *m, const char *name) {
237         Session *session;
238         Iterator i;
239
240         assert(m);
241         assert(name);
242
243         /* keep it if the name still owns a controller */
244         HASHMAP_FOREACH(session, m->sessions, i)
245                 if (session_is_controller(session, name))
246                         return;
247
248         free(set_remove(m->busnames, (char*) name));
249 }
250
251 int manager_process_seat_device(Manager *m, struct udev_device *d) {
252         Device *device;
253         int r;
254
255         assert(m);
256
257         if (streq_ptr(udev_device_get_action(d), "remove")) {
258
259                 device = hashmap_get(m->devices, udev_device_get_syspath(d));
260                 if (!device)
261                         return 0;
262
263                 seat_add_to_gc_queue(device->seat);
264                 device_free(device);
265
266         } else {
267                 const char *sn;
268                 Seat *seat = NULL;
269                 bool master;
270
271                 sn = udev_device_get_property_value(d, "ID_SEAT");
272                 if (isempty(sn))
273                         sn = "seat0";
274
275                 if (!seat_name_is_valid(sn)) {
276                         log_warning("Device with invalid seat name %s found, ignoring.", sn);
277                         return 0;
278                 }
279
280                 seat = hashmap_get(m->seats, sn);
281                 master = udev_device_has_tag(d, "master-of-seat");
282
283                 /* Ignore non-master devices for unknown seats */
284                 if (!master && !seat)
285                         return 0;
286
287                 r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
288                 if (r < 0)
289                         return r;
290
291                 if (!seat) {
292                         r = manager_add_seat(m, sn, &seat);
293                         if (r < 0) {
294                                 if (!device->seat)
295                                         device_free(device);
296
297                                 return r;
298                         }
299                 }
300
301                 device_attach(device, seat);
302                 seat_start(seat);
303         }
304
305         return 0;
306 }
307
308 int manager_process_button_device(Manager *m, struct udev_device *d) {
309         Button *b;
310
311         int r;
312
313         assert(m);
314
315         if (streq_ptr(udev_device_get_action(d), "remove")) {
316
317                 b = hashmap_get(m->buttons, udev_device_get_sysname(d));
318                 if (!b)
319                         return 0;
320
321                 button_free(b);
322
323         } else {
324                 const char *sn;
325
326                 r = manager_add_button(m, udev_device_get_sysname(d), &b);
327                 if (r < 0)
328                         return r;
329
330                 sn = udev_device_get_property_value(d, "ID_SEAT");
331                 if (isempty(sn))
332                         sn = "seat0";
333
334                 button_set_seat(b, sn);
335                 button_open(b);
336         }
337
338         return 0;
339 }
340
341 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
342         _cleanup_free_ char *unit = NULL;
343         Session *s;
344         int r;
345
346         assert(m);
347         assert(session);
348
349         if (pid < 1)
350                 return -EINVAL;
351
352         r = cg_pid_get_unit(pid, &unit);
353         if (r < 0)
354                 return 0;
355
356         s = hashmap_get(m->session_units, unit);
357         if (!s)
358                 return 0;
359
360         *session = s;
361         return 1;
362 }
363
364 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
365         _cleanup_free_ char *unit = NULL;
366         User *u;
367         int r;
368
369         assert(m);
370         assert(user);
371
372         if (pid < 1)
373                 return -EINVAL;
374
375         r = cg_pid_get_slice(pid, &unit);
376         if (r < 0)
377                 return 0;
378
379         u = hashmap_get(m->user_units, unit);
380         if (!u)
381                 return 0;
382
383         *user = u;
384         return 1;
385 }
386
387 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
388         Session *s;
389         bool idle_hint;
390         dual_timestamp ts = { 0, 0 };
391         Iterator i;
392
393         assert(m);
394
395         idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
396
397         HASHMAP_FOREACH(s, m->sessions, i) {
398                 dual_timestamp k;
399                 int ih;
400
401                 ih = session_get_idle_hint(s, &k);
402                 if (ih < 0)
403                         return ih;
404
405                 if (!ih) {
406                         if (!idle_hint) {
407                                 if (k.monotonic < ts.monotonic)
408                                         ts = k;
409                         } else {
410                                 idle_hint = false;
411                                 ts = k;
412                         }
413                 } else if (idle_hint) {
414
415                         if (k.monotonic > ts.monotonic)
416                                 ts = k;
417                 }
418         }
419
420         if (t)
421                 *t = ts;
422
423         return idle_hint;
424 }
425
426 bool manager_shall_kill(Manager *m, const char *user) {
427         assert(m);
428         assert(user);
429
430         if (!m->kill_user_processes)
431                 return false;
432
433         if (strv_contains(m->kill_exclude_users, user))
434                 return false;
435
436         if (strv_isempty(m->kill_only_users))
437                 return true;
438
439         return strv_contains(m->kill_only_users, user);
440 }
441
442 static int vt_is_busy(unsigned int vtnr) {
443         struct vt_stat vt_stat;
444         int r = 0, fd;
445
446         assert(vtnr >= 1);
447
448         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
449          * we'd open the latter we'd open the foreground tty which
450          * hence would be unconditionally busy. By opening /dev/tty1
451          * we avoid this. Since tty1 is special and needs to be an
452          * explicitly loaded getty or DM this is safe. */
453
454         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
455         if (fd < 0)
456                 return -errno;
457
458         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
459                 r = -errno;
460         else
461                 r = !!(vt_stat.v_state & (1 << vtnr));
462
463         close_nointr_nofail(fd);
464
465         return r;
466 }
467
468 int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
469         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
470         _cleanup_free_ char *name = NULL;
471         int r;
472
473         assert(m);
474         assert(vtnr >= 1);
475
476         if (vtnr > m->n_autovts &&
477             vtnr != m->reserve_vt)
478                 return 0;
479
480         if (vtnr != m->reserve_vt) {
481                 /* If this is the reserved TTY, we'll start the getty
482                  * on it in any case, but otherwise only if it is not
483                  * busy. */
484
485                 r = vt_is_busy(vtnr);
486                 if (r < 0)
487                         return r;
488                 else if (r > 0)
489                         return -EBUSY;
490         }
491
492         if (asprintf(&name, "autovt@tty%u.service", vtnr) < 0)
493                 return log_oom();
494
495         r = sd_bus_call_method(
496                         m->bus,
497                         "org.freedesktop.systemd1",
498                         "/org/freedesktop/systemd1",
499                         "org.freedesktop.systemd1.Manager",
500                         "StartUnit",
501                         &error,
502                         NULL,
503                         "ss", name, "fail");
504         if (r < 0)
505                 log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
506
507         return r;
508 }
509
510 bool manager_is_docked(Manager *m) {
511         Iterator i;
512         Button *b;
513
514         HASHMAP_FOREACH(b, m->buttons, i)
515                 if (b->docked)
516                         return true;
517
518         return false;
519 }
520
521 int manager_count_displays(Manager *m) {
522         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
523         struct udev_list_entry *item = NULL, *first = NULL;
524         int r;
525         int n = 0;
526
527         e = udev_enumerate_new(m->udev);
528         if (!e)
529                 return -ENOMEM;
530
531         r = udev_enumerate_add_match_subsystem(e, "drm");
532         if (r < 0)
533                 return r;
534
535         r = udev_enumerate_scan_devices(e);
536         if (r < 0)
537                 return r;
538
539         first = udev_enumerate_get_list_entry(e);
540         udev_list_entry_foreach(item, first) {
541                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
542                 struct udev_device *p;
543                 const char *status;
544
545                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
546                 if (!d)
547                         return -ENOMEM;
548
549                 p = udev_device_get_parent(d);
550                 if (!p)
551                         return -ENOMEM;
552
553                 /* If the parent shares the same subsystem as the
554                  * device we are looking at then it is a connector,
555                  * which is what we are interested in. */
556                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
557                         continue;
558
559                 /* We count any connector which is not explicitly
560                  * "disconnected" as connected. */
561                 status = udev_device_get_sysattr_value(d, "status");
562                 if (!streq_ptr(status, "disconnected"))
563                         n++;
564         }
565
566         return n;
567 }