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