chiark / gitweb /
c7d7cedf79ffc918620659a3bd15485adf8c5f28
[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 #if 0 /// UNNEEDED by elogind
414 static int vt_is_busy(unsigned int vtnr) {
415         struct vt_stat vt_stat;
416         int r = 0;
417         _cleanup_close_ int fd;
418
419         assert(vtnr >= 1);
420
421         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
422          * we'd open the latter we'd open the foreground tty which
423          * hence would be unconditionally busy. By opening /dev/tty1
424          * we avoid this. Since tty1 is special and needs to be an
425          * explicitly loaded getty or DM this is safe. */
426
427         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
428         if (fd < 0)
429                 return -errno;
430
431         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
432                 r = -errno;
433         else
434                 r = !!(vt_stat.v_state & (1 << vtnr));
435
436         return r;
437 }
438
439 int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
440         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
441         char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
442         int r;
443
444         assert(m);
445         assert(vtnr >= 1);
446
447         if (vtnr > m->n_autovts &&
448             vtnr != m->reserve_vt)
449                 return 0;
450
451         if (vtnr != m->reserve_vt) {
452                 /* If this is the reserved TTY, we'll start the getty
453                  * on it in any case, but otherwise only if it is not
454                  * busy. */
455
456                 r = vt_is_busy(vtnr);
457                 if (r < 0)
458                         return r;
459                 else if (r > 0)
460                         return -EBUSY;
461         }
462
463         snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
464         r = sd_bus_call_method(
465                         m->bus,
466                         "org.freedesktop.systemd1",
467                         "/org/freedesktop/systemd1",
468                         "org.freedesktop.systemd1.Manager",
469                         "StartUnit",
470                         &error,
471                         NULL,
472                         "ss", name, "fail");
473         if (r < 0)
474                 log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
475
476         return r;
477 }
478 #endif // 0
479
480 static bool manager_is_docked(Manager *m) {
481         Iterator i;
482         Button *b;
483
484         HASHMAP_FOREACH(b, m->buttons, i)
485                 if (b->docked)
486                         return true;
487
488         return false;
489 }
490
491 static int manager_count_external_displays(Manager *m) {
492         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
493         struct udev_list_entry *item = NULL, *first = NULL;
494         int r;
495         int n = 0;
496
497         e = udev_enumerate_new(m->udev);
498         if (!e)
499                 return -ENOMEM;
500
501         r = udev_enumerate_add_match_subsystem(e, "drm");
502         if (r < 0)
503                 return r;
504
505         r = udev_enumerate_scan_devices(e);
506         if (r < 0)
507                 return r;
508
509         first = udev_enumerate_get_list_entry(e);
510         udev_list_entry_foreach(item, first) {
511                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
512                 struct udev_device *p;
513                 const char *status, *enabled, *dash, *nn, *i;
514                 bool external = false;
515
516                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
517                 if (!d)
518                         return -ENOMEM;
519
520                 p = udev_device_get_parent(d);
521                 if (!p)
522                         continue;
523
524                 /* If the parent shares the same subsystem as the
525                  * device we are looking at then it is a connector,
526                  * which is what we are interested in. */
527                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
528                         continue;
529
530                 nn = udev_device_get_sysname(d);
531                 if (!nn)
532                         continue;
533
534                 /* Ignore internal displays: the type is encoded in
535                  * the sysfs name, as the second dash seperated item
536                  * (the first is the card name, the last the connector
537                  * number). We implement a whitelist of external
538                  * displays here, rather than a whitelist, to ensure
539                  * we don't block suspends too eagerly. */
540                 dash = strchr(nn, '-');
541                 if (!dash)
542                         continue;
543
544                 dash++;
545                 FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
546                                "Composite-", "SVIDEO-", "Component-",
547                                "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") {
548
549                         if (startswith(dash, i)) {
550                                 external = true;
551                                 break;
552                         }
553                 }
554                 if (!external)
555                         continue;
556
557                 /* Ignore ports that are not enabled */
558                 enabled = udev_device_get_sysattr_value(d, "enabled");
559                 if (!enabled)
560                         continue;
561                 if (!streq_ptr(enabled, "enabled"))
562                         continue;
563
564                 /* We count any connector which is not explicitly
565                  * "disconnected" as connected. */
566                 status = udev_device_get_sysattr_value(d, "status");
567                 if (!streq_ptr(status, "disconnected"))
568                         n++;
569         }
570
571         return n;
572 }
573
574 bool manager_is_docked_or_external_displays(Manager *m) {
575         int n;
576
577         /* If we are docked don't react to lid closing */
578         if (manager_is_docked(m)) {
579                 log_debug("System is docked.");
580                 return true;
581         }
582
583         /* If we have more than one display connected,
584          * assume that we are docked. */
585         n = manager_count_external_displays(m);
586         if (n < 0)
587                 log_warning_errno(n, "Display counting failed: %m");
588         else if (n >= 1) {
589                 log_debug("External (%i) displays connected.", n);
590                 return true;
591         }
592
593         return false;
594 }