chiark / gitweb /
logind: minor cleanup and use IN_SET() in manager_handle_action()
[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                 button_open(b);
273         }
274
275         return 0;
276 }
277
278 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
279 #if 0 /// elogind does not support systemd units, but its own session system
280         _cleanup_free_ char *unit = NULL;
281 #else
282         _cleanup_free_ char *session_name = NULL;
283 #endif // 0
284         Session *s;
285         int r;
286
287         assert(m);
288
289         if (pid < 1)
290                 return -EINVAL;
291
292 #if 0 /// elogind does not support systemd units, but its own session system
293         r = cg_pid_get_unit(pid, &unit);
294         if (r < 0)
295                 return 0;
296
297         s = hashmap_get(m->session_units, unit);
298 #else
299         log_debug_elogind("Searching session for PID %u", pid);
300         r = cg_pid_get_session(pid, &session_name);
301         if (r < 0)
302                 return 0;
303
304         s = hashmap_get(m->sessions, session_name);
305         log_debug_elogind("Session Name \"%s\" -> Session \"%s\"",
306                           session_name, s && s->id ? s->id : "NULL");
307 #endif // 0
308         if (!s)
309                 return 0;
310
311         if (session)
312                 *session = s;
313         return 1;
314 }
315
316 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
317 #if 0 /// elogind does not support systemd units, but its own session system
318         _cleanup_free_ char *unit = NULL;
319         User *u;
320 #else
321         Session *s;
322 #endif // 0
323         int r;
324
325         assert(m);
326         assert(user);
327
328         if (pid < 1)
329                 return -EINVAL;
330
331 #if 0 /// elogind does not support systemd units, but its own session system
332         r = cg_pid_get_slice(pid, &unit);
333         if (r < 0)
334                 return 0;
335
336         u = hashmap_get(m->user_units, unit);
337         if (!u)
338                 return 0;
339
340         *user = u;
341 #else
342         r = manager_get_session_by_pid (m, pid, &s);
343         if (r <= 0)
344                 return r;
345
346         *user = s->user;
347 #endif // 0
348         return 1;
349 }
350
351 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
352         Session *s;
353         bool idle_hint;
354         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
355         Iterator i;
356
357         assert(m);
358
359         idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
360
361         HASHMAP_FOREACH(s, m->sessions, i) {
362                 dual_timestamp k;
363                 int ih;
364
365                 ih = session_get_idle_hint(s, &k);
366                 if (ih < 0)
367                         return ih;
368
369                 if (!ih) {
370                         if (!idle_hint) {
371                                 if (k.monotonic < ts.monotonic)
372                                         ts = k;
373                         } else {
374                                 idle_hint = false;
375                                 ts = k;
376                         }
377                 } else if (idle_hint) {
378
379                         if (k.monotonic > ts.monotonic)
380                                 ts = k;
381                 }
382         }
383
384         if (t)
385                 *t = ts;
386
387         return idle_hint;
388 }
389
390 bool manager_shall_kill(Manager *m, const char *user) {
391         assert(m);
392         assert(user);
393
394         if (!m->kill_exclude_users && streq(user, "root"))
395                 return false;
396
397         if (strv_contains(m->kill_exclude_users, user))
398                 return false;
399
400         if (!strv_isempty(m->kill_only_users))
401                 return strv_contains(m->kill_only_users, user);
402
403         return m->kill_user_processes;
404 }
405
406 #if 0 /// UNNEEDED by elogind
407 static int vt_is_busy(unsigned int vtnr) {
408         struct vt_stat vt_stat;
409         int r = 0;
410         _cleanup_close_ int fd;
411
412         assert(vtnr >= 1);
413
414         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
415          * we'd open the latter we'd open the foreground tty which
416          * hence would be unconditionally busy. By opening /dev/tty1
417          * we avoid this. Since tty1 is special and needs to be an
418          * explicitly loaded getty or DM this is safe. */
419
420         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
421         if (fd < 0)
422                 return -errno;
423
424         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
425                 r = -errno;
426         else
427                 r = !!(vt_stat.v_state & (1 << vtnr));
428
429         return r;
430 }
431
432 int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
433         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
434         char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
435         int r;
436
437         assert(m);
438         assert(vtnr >= 1);
439
440         if (vtnr > m->n_autovts &&
441             vtnr != m->reserve_vt)
442                 return 0;
443
444         if (vtnr != m->reserve_vt) {
445                 /* If this is the reserved TTY, we'll start the getty
446                  * on it in any case, but otherwise only if it is not
447                  * busy. */
448
449                 r = vt_is_busy(vtnr);
450                 if (r < 0)
451                         return r;
452                 else if (r > 0)
453                         return -EBUSY;
454         }
455
456         snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
457         r = sd_bus_call_method(
458                         m->bus,
459                         "org.freedesktop.systemd1",
460                         "/org/freedesktop/systemd1",
461                         "org.freedesktop.systemd.Manager",
462                         "StartUnit",
463                         &error,
464                         NULL,
465                         "ss", name, "fail");
466         if (r < 0)
467                 log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
468
469         return r;
470 }
471 #endif // 0
472
473 static bool manager_is_docked(Manager *m) {
474         Iterator i;
475         Button *b;
476
477         HASHMAP_FOREACH(b, m->buttons, i)
478                 if (b->docked)
479                         return true;
480
481         return false;
482 }
483
484 static int manager_count_external_displays(Manager *m) {
485         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
486         struct udev_list_entry *item = NULL, *first = NULL;
487         int r;
488         int n = 0;
489
490         e = udev_enumerate_new(m->udev);
491         if (!e)
492                 return -ENOMEM;
493
494         r = udev_enumerate_add_match_subsystem(e, "drm");
495         if (r < 0)
496                 return r;
497
498         r = udev_enumerate_scan_devices(e);
499         if (r < 0)
500                 return r;
501
502         first = udev_enumerate_get_list_entry(e);
503         udev_list_entry_foreach(item, first) {
504                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
505                 struct udev_device *p;
506                 const char *status, *enabled, *dash, *nn, *i;
507                 bool external = false;
508
509                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
510                 if (!d)
511                         return -ENOMEM;
512
513                 p = udev_device_get_parent(d);
514                 if (!p)
515                         continue;
516
517                 /* If the parent shares the same subsystem as the
518                  * device we are looking at then it is a connector,
519                  * which is what we are interested in. */
520                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
521                         continue;
522
523                 nn = udev_device_get_sysname(d);
524                 if (!nn)
525                         continue;
526
527                 /* Ignore internal displays: the type is encoded in
528                  * the sysfs name, as the second dash separated item
529                  * (the first is the card name, the last the connector
530                  * number). We implement a whitelist of external
531                  * displays here, rather than a whitelist, to ensure
532                  * we don't block suspends too eagerly. */
533                 dash = strchr(nn, '-');
534                 if (!dash)
535                         continue;
536
537                 dash++;
538                 FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
539                                "Composite-", "SVIDEO-", "Component-",
540                                "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") {
541
542                         if (startswith(dash, i)) {
543                                 external = true;
544                                 break;
545                         }
546                 }
547                 if (!external)
548                         continue;
549
550                 /* Ignore ports that are not enabled */
551                 enabled = udev_device_get_sysattr_value(d, "enabled");
552                 if (!enabled)
553                         continue;
554                 if (!streq_ptr(enabled, "enabled"))
555                         continue;
556
557                 /* We count any connector which is not explicitly
558                  * "disconnected" as connected. */
559                 status = udev_device_get_sysattr_value(d, "status");
560                 if (!streq_ptr(status, "disconnected"))
561                         n++;
562         }
563
564         return n;
565 }
566
567 bool manager_is_docked_or_external_displays(Manager *m) {
568         int n;
569
570         /* If we are docked don't react to lid closing */
571         if (manager_is_docked(m)) {
572                 log_debug("System is docked.");
573                 return true;
574         }
575
576         /* If we have more than one display connected,
577          * assume that we are docked. */
578         n = manager_count_external_displays(m);
579         if (n < 0)
580                 log_warning_errno(n, "Display counting failed: %m");
581         else if (n >= 1) {
582                 log_debug("External (%i) displays connected.", n);
583                 return true;
584         }
585
586         return false;
587 }