chiark / gitweb /
login: support more than just power-gpio-key
[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/ioctl.h>
24 #include <fcntl.h>
25 #include <pwd.h>
26 #include <linux/vt.h>
27
28 #include "strv.h"
29 #include "cgroup-util.h"
30 #include "bus-util.h"
31 #include "bus-error.h"
32 #include "udev-util.h"
33 #include "logind.h"
34 #include "terminal-util.h"
35
36 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
37         Device *d;
38
39         assert(m);
40         assert(sysfs);
41
42         d = hashmap_get(m->devices, sysfs);
43         if (d)
44                 /* we support adding master-flags, but not removing them */
45                 d->master = d->master || master;
46         else {
47                 d = device_new(m, sysfs, master);
48                 if (!d)
49                         return -ENOMEM;
50         }
51
52         if (_device)
53                 *_device = d;
54
55         return 0;
56 }
57
58 int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
59         Seat *s;
60
61         assert(m);
62         assert(id);
63
64         s = hashmap_get(m->seats, id);
65         if (!s) {
66                 s = seat_new(m, id);
67                 if (!s)
68                         return -ENOMEM;
69         }
70
71         if (_seat)
72                 *_seat = s;
73
74         return 0;
75 }
76
77 int manager_add_session(Manager *m, const char *id, Session **_session) {
78         Session *s;
79
80         assert(m);
81         assert(id);
82
83         s = hashmap_get(m->sessions, id);
84         if (!s) {
85                 s = session_new(m, id);
86                 if (!s)
87                         return -ENOMEM;
88         }
89
90         if (_session)
91                 *_session = s;
92
93         return 0;
94 }
95
96 int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
97         User *u;
98
99         assert(m);
100         assert(name);
101
102         u = hashmap_get(m->users, UID_TO_PTR(uid));
103         if (!u) {
104                 u = user_new(m, uid, gid, name);
105                 if (!u)
106                         return -ENOMEM;
107         }
108
109         if (_user)
110                 *_user = u;
111
112         return 0;
113 }
114
115 int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
116         uid_t uid;
117         gid_t gid;
118         int r;
119
120         assert(m);
121         assert(name);
122
123         r = get_user_creds(&name, &uid, &gid, NULL, NULL);
124         if (r < 0)
125                 return r;
126
127         return manager_add_user(m, uid, gid, name, _user);
128 }
129
130 int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
131         struct passwd *p;
132
133         assert(m);
134
135         errno = 0;
136         p = getpwuid(uid);
137         if (!p)
138                 return errno ? -errno : -ENOENT;
139
140         return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
141 }
142
143 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
144         Inhibitor *i;
145
146         assert(m);
147         assert(id);
148
149         i = hashmap_get(m->inhibitors, id);
150         if (i) {
151                 if (_inhibitor)
152                         *_inhibitor = i;
153
154                 return 0;
155         }
156
157         i = inhibitor_new(m, id);
158         if (!i)
159                 return -ENOMEM;
160
161         if (_inhibitor)
162                 *_inhibitor = i;
163
164         return 0;
165 }
166
167 int manager_add_button(Manager *m, const char *name, Button **_button) {
168         Button *b;
169
170         assert(m);
171         assert(name);
172
173         b = hashmap_get(m->buttons, name);
174         if (!b) {
175                 b = button_new(m, name);
176                 if (!b)
177                         return -ENOMEM;
178         }
179
180         if (_button)
181                 *_button = b;
182
183         return 0;
184 }
185
186 int manager_process_seat_device(Manager *m, struct udev_device *d) {
187         Device *device;
188         int r;
189
190         assert(m);
191
192         if (streq_ptr(udev_device_get_action(d), "remove")) {
193
194                 device = hashmap_get(m->devices, udev_device_get_syspath(d));
195                 if (!device)
196                         return 0;
197
198                 seat_add_to_gc_queue(device->seat);
199                 device_free(device);
200
201         } else {
202                 const char *sn;
203                 Seat *seat = NULL;
204                 bool master;
205
206                 sn = udev_device_get_property_value(d, "ID_SEAT");
207                 if (isempty(sn))
208                         sn = "seat0";
209
210                 if (!seat_name_is_valid(sn)) {
211                         log_warning("Device with invalid seat name %s found, ignoring.", sn);
212                         return 0;
213                 }
214
215                 seat = hashmap_get(m->seats, sn);
216                 master = udev_device_has_tag(d, "master-of-seat");
217
218                 /* Ignore non-master devices for unknown seats */
219                 if (!master && !seat)
220                         return 0;
221
222                 r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
223                 if (r < 0)
224                         return r;
225
226                 if (!seat) {
227                         r = manager_add_seat(m, sn, &seat);
228                         if (r < 0) {
229                                 if (!device->seat)
230                                         device_free(device);
231
232                                 return r;
233                         }
234                 }
235
236                 device_attach(device, seat);
237                 seat_start(seat);
238         }
239
240         return 0;
241 }
242
243 int manager_process_button_device(Manager *m, struct udev_device *d) {
244         Button *b;
245
246         int r;
247
248         assert(m);
249
250         if (streq_ptr(udev_device_get_action(d), "remove")) {
251
252                 b = hashmap_get(m->buttons, udev_device_get_sysname(d));
253                 if (!b)
254                         return 0;
255
256                 button_free(b);
257
258         } else {
259                 const char *sn;
260
261                 r = manager_add_button(m, udev_device_get_sysname(d), &b);
262                 if (r < 0)
263                         return r;
264
265                 sn = udev_device_get_property_value(d, "ID_SEAT");
266                 if (isempty(sn))
267                         sn = "seat0";
268
269                 button_set_seat(b, sn);
270                 button_open(b);
271         }
272
273         return 0;
274 }
275
276 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
277 /// elogind does not support systemd units, but its own session system
278 #if 0
279         _cleanup_free_ char *unit = NULL;
280 #else
281         _cleanup_free_ char *session_name = NULL;
282 #endif // 0
283         Session *s;
284         int r;
285
286         assert(m);
287
288         if (pid < 1)
289                 return -EINVAL;
290
291 /// elogind does not support systemd units, but its own session system
292 #if 0
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 /// elogind does not support systemd units, but its own session system
318 #if 0
319         _cleanup_free_ char *unit = NULL;
320         User *u;
321 #else
322         Session *s;
323 #endif // 0
324         int r;
325
326         assert(m);
327         assert(user);
328
329         if (pid < 1)
330                 return -EINVAL;
331
332 /// elogind does not support systemd units, but its own session system
333 #if 0
334         r = cg_pid_get_slice(pid, &unit);
335         if (r < 0)
336                 return 0;
337
338         u = hashmap_get(m->user_units, unit);
339         if (!u)
340                 return 0;
341
342         *user = u;
343 #else
344         r = manager_get_session_by_pid (m, pid, &s);
345         if (r <= 0)
346                 return r;
347
348         *user = s->user;
349 #endif // 0
350
351         return 1;
352 }
353
354 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
355         Session *s;
356         bool idle_hint;
357         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
358         Iterator i;
359
360         assert(m);
361
362         idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
363
364         HASHMAP_FOREACH(s, m->sessions, i) {
365                 dual_timestamp k;
366                 int ih;
367
368                 ih = session_get_idle_hint(s, &k);
369                 if (ih < 0)
370                         return ih;
371
372                 if (!ih) {
373                         if (!idle_hint) {
374                                 if (k.monotonic < ts.monotonic)
375                                         ts = k;
376                         } else {
377                                 idle_hint = false;
378                                 ts = k;
379                         }
380                 } else if (idle_hint) {
381
382                         if (k.monotonic > ts.monotonic)
383                                 ts = k;
384                 }
385         }
386
387         if (t)
388                 *t = ts;
389
390         return idle_hint;
391 }
392
393 bool manager_shall_kill(Manager *m, const char *user) {
394         assert(m);
395         assert(user);
396
397         if (!m->kill_user_processes)
398                 return false;
399
400         if (strv_contains(m->kill_exclude_users, user))
401                 return false;
402
403         if (strv_isempty(m->kill_only_users))
404                 return true;
405
406         return strv_contains(m->kill_only_users, user);
407 }
408
409 /// UNNEEDED by elogind
410 #if 0
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_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 seperated 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 }