chiark / gitweb /
Prep v222: Update build system:
[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_watch_busname(Manager *m, const char *name) {
187         char *n;
188         int r;
189
190         assert(m);
191         assert(name);
192
193         if (set_get(m->busnames, (char*) name))
194                 return 0;
195
196         n = strdup(name);
197         if (!n)
198                 return -ENOMEM;
199
200         r = set_put(m->busnames, n);
201         if (r < 0) {
202                 free(n);
203                 return r;
204         }
205
206         return 0;
207 }
208
209 void manager_drop_busname(Manager *m, const char *name) {
210         Session *session;
211         Iterator i;
212
213         assert(m);
214         assert(name);
215
216         /* keep it if the name still owns a controller */
217         HASHMAP_FOREACH(session, m->sessions, i)
218                 if (session_is_controller(session, name))
219                         return;
220
221         free(set_remove(m->busnames, (char*) name));
222 }
223
224 int manager_process_seat_device(Manager *m, struct udev_device *d) {
225         Device *device;
226         int r;
227
228         assert(m);
229
230         if (streq_ptr(udev_device_get_action(d), "remove")) {
231
232                 device = hashmap_get(m->devices, udev_device_get_syspath(d));
233                 if (!device)
234                         return 0;
235
236                 seat_add_to_gc_queue(device->seat);
237                 device_free(device);
238
239         } else {
240                 const char *sn;
241                 Seat *seat = NULL;
242                 bool master;
243
244                 sn = udev_device_get_property_value(d, "ID_SEAT");
245                 if (isempty(sn))
246                         sn = "seat0";
247
248                 if (!seat_name_is_valid(sn)) {
249                         log_warning("Device with invalid seat name %s found, ignoring.", sn);
250                         return 0;
251                 }
252
253                 seat = hashmap_get(m->seats, sn);
254                 master = udev_device_has_tag(d, "master-of-seat");
255
256                 /* Ignore non-master devices for unknown seats */
257                 if (!master && !seat)
258                         return 0;
259
260                 r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
261                 if (r < 0)
262                         return r;
263
264                 if (!seat) {
265                         r = manager_add_seat(m, sn, &seat);
266                         if (r < 0) {
267                                 if (!device->seat)
268                                         device_free(device);
269
270                                 return r;
271                         }
272                 }
273
274                 device_attach(device, seat);
275                 seat_start(seat);
276         }
277
278         return 0;
279 }
280
281 int manager_process_button_device(Manager *m, struct udev_device *d) {
282         Button *b;
283
284         int r;
285
286         assert(m);
287
288         if (streq_ptr(udev_device_get_action(d), "remove")) {
289
290                 b = hashmap_get(m->buttons, udev_device_get_sysname(d));
291                 if (!b)
292                         return 0;
293
294                 button_free(b);
295
296         } else {
297                 const char *sn;
298
299                 r = manager_add_button(m, udev_device_get_sysname(d), &b);
300                 if (r < 0)
301                         return r;
302
303                 sn = udev_device_get_property_value(d, "ID_SEAT");
304                 if (isempty(sn))
305                         sn = "seat0";
306
307                 button_set_seat(b, sn);
308                 button_open(b);
309         }
310
311         return 0;
312 }
313
314 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
315         _cleanup_free_ char *unit = NULL;
316         Session *s;
317         int r;
318
319         assert(m);
320         assert(session);
321
322         if (pid < 1)
323                 return -EINVAL;
324
325         r = cg_pid_get_unit(pid, &unit);
326         if (r < 0)
327                 return 0;
328
329         s = hashmap_get(m->session_units, unit);
330         if (!s)
331                 return 0;
332
333         *session = s;
334         return 1;
335 }
336
337 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
338         _cleanup_free_ char *unit = NULL;
339         User *u;
340         int r;
341
342         assert(m);
343         assert(user);
344
345         if (pid < 1)
346                 return -EINVAL;
347
348         r = cg_pid_get_slice(pid, &unit);
349         if (r < 0)
350                 return 0;
351
352         u = hashmap_get(m->user_units, unit);
353         if (!u)
354                 return 0;
355
356         *user = u;
357         return 1;
358 }
359
360 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
361         Session *s;
362         bool idle_hint;
363         dual_timestamp ts = { 0, 0 };
364         Iterator i;
365
366         assert(m);
367
368         idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
369
370         HASHMAP_FOREACH(s, m->sessions, i) {
371                 dual_timestamp k;
372                 int ih;
373
374                 ih = session_get_idle_hint(s, &k);
375                 if (ih < 0)
376                         return ih;
377
378                 if (!ih) {
379                         if (!idle_hint) {
380                                 if (k.monotonic < ts.monotonic)
381                                         ts = k;
382                         } else {
383                                 idle_hint = false;
384                                 ts = k;
385                         }
386                 } else if (idle_hint) {
387
388                         if (k.monotonic > ts.monotonic)
389                                 ts = k;
390                 }
391         }
392
393         if (t)
394                 *t = ts;
395
396         return idle_hint;
397 }
398
399 bool manager_shall_kill(Manager *m, const char *user) {
400         assert(m);
401         assert(user);
402
403         if (!m->kill_user_processes)
404                 return false;
405
406         if (strv_contains(m->kill_exclude_users, user))
407                 return false;
408
409         if (strv_isempty(m->kill_only_users))
410                 return true;
411
412         return strv_contains(m->kill_only_users, user);
413 }
414
415 static int vt_is_busy(unsigned int vtnr) {
416         struct vt_stat vt_stat;
417         int r = 0;
418         _cleanup_close_ int fd;
419
420         assert(vtnr >= 1);
421
422         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
423          * we'd open the latter we'd open the foreground tty which
424          * hence would be unconditionally busy. By opening /dev/tty1
425          * we avoid this. Since tty1 is special and needs to be an
426          * explicitly loaded getty or DM this is safe. */
427
428         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
429         if (fd < 0)
430                 return -errno;
431
432         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
433                 r = -errno;
434         else
435                 r = !!(vt_stat.v_state & (1 << vtnr));
436
437         return r;
438 }
439
440 int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
441         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
442         char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
443         int r;
444
445         assert(m);
446         assert(vtnr >= 1);
447
448         if (vtnr > m->n_autovts &&
449             vtnr != m->reserve_vt)
450                 return 0;
451
452         if (vtnr != m->reserve_vt) {
453                 /* If this is the reserved TTY, we'll start the getty
454                  * on it in any case, but otherwise only if it is not
455                  * busy. */
456
457                 r = vt_is_busy(vtnr);
458                 if (r < 0)
459                         return r;
460                 else if (r > 0)
461                         return -EBUSY;
462         }
463
464         snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
465         r = sd_bus_call_method(
466                         m->bus,
467                         "org.freedesktop.systemd1",
468                         "/org/freedesktop/systemd1",
469                         "org.freedesktop.systemd1.Manager",
470                         "StartUnit",
471                         &error,
472                         NULL,
473                         "ss", name, "fail");
474         if (r < 0)
475                 log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
476
477         return r;
478 }
479
480 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 int manager_count_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;
514
515                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
516                 if (!d)
517                         return -ENOMEM;
518
519                 p = udev_device_get_parent(d);
520                 if (!p)
521                         continue;
522
523                 /* If the parent shares the same subsystem as the
524                  * device we are looking at then it is a connector,
525                  * which is what we are interested in. */
526                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
527                         continue;
528
529                 /* We count any connector which is not explicitly
530                  * "disconnected" as connected. */
531                 status = udev_device_get_sysattr_value(d, "status");
532                 if (!streq_ptr(status, "disconnected"))
533                         n++;
534         }
535
536         return n;
537 }
538
539 bool manager_is_docked_or_multiple_displays(Manager *m) {
540         int n;
541
542         /* If we are docked don't react to lid closing */
543         if (manager_is_docked(m)) {
544                 log_debug("System is docked.");
545                 return true;
546         }
547
548         /* If we have more than one display connected,
549          * assume that we are docked. */
550         n = manager_count_displays(m);
551         if (n < 0)
552                 log_warning_errno(n, "Display counting failed: %m");
553         else if (n > 1) {
554                 log_debug("Multiple (%i) displays connected.", n);
555                 return true;
556         }
557
558         return false;
559 }