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