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