chiark / gitweb /
e8da59a18252861127bd548b0d152588ddea5919
[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
35 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
36         Device *d;
37
38         assert(m);
39         assert(sysfs);
40
41         d = hashmap_get(m->devices, sysfs);
42         if (d)
43                 /* we support adding master-flags, but not removing them */
44                 d->master = d->master || master;
45         else {
46                 d = device_new(m, sysfs, master);
47                 if (!d)
48                         return -ENOMEM;
49         }
50
51         if (_device)
52                 *_device = d;
53
54         return 0;
55 }
56
57 int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
58         Seat *s;
59
60         assert(m);
61         assert(id);
62
63         s = hashmap_get(m->seats, id);
64         if (!s) {
65                 s = seat_new(m, id);
66                 if (!s)
67                         return -ENOMEM;
68         }
69
70         if (_seat)
71                 *_seat = s;
72
73         return 0;
74 }
75
76 int manager_add_session(Manager *m, const char *id, Session **_session) {
77         Session *s;
78
79         assert(m);
80         assert(id);
81
82         s = hashmap_get(m->sessions, id);
83         if (!s) {
84                 s = session_new(m, id);
85                 if (!s)
86                         return -ENOMEM;
87         }
88
89         if (_session)
90                 *_session = s;
91
92         return 0;
93 }
94
95 int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
96         User *u;
97
98         assert(m);
99         assert(name);
100
101         u = hashmap_get(m->users, UID_TO_PTR(uid));
102         if (!u) {
103                 u = user_new(m, uid, gid, name);
104                 if (!u)
105                         return -ENOMEM;
106         }
107
108         if (_user)
109                 *_user = u;
110
111         return 0;
112 }
113
114 int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
115         uid_t uid;
116         gid_t gid;
117         int r;
118
119         assert(m);
120         assert(name);
121
122         r = get_user_creds(&name, &uid, &gid, NULL, NULL);
123         if (r < 0)
124                 return r;
125
126         return manager_add_user(m, uid, gid, name, _user);
127 }
128
129 int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
130         struct passwd *p;
131
132         assert(m);
133
134         errno = 0;
135         p = getpwuid(uid);
136         if (!p)
137                 return errno ? -errno : -ENOENT;
138
139         return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
140 }
141
142 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
143         Inhibitor *i;
144
145         assert(m);
146         assert(id);
147
148         i = hashmap_get(m->inhibitors, id);
149         if (i) {
150                 if (_inhibitor)
151                         *_inhibitor = i;
152
153                 return 0;
154         }
155
156         i = inhibitor_new(m, id);
157         if (!i)
158                 return -ENOMEM;
159
160         if (_inhibitor)
161                 *_inhibitor = i;
162
163         return 0;
164 }
165
166 int manager_add_button(Manager *m, const char *name, Button **_button) {
167         Button *b;
168
169         assert(m);
170         assert(name);
171
172         b = hashmap_get(m->buttons, name);
173         if (!b) {
174                 b = button_new(m, name);
175                 if (!b)
176                         return -ENOMEM;
177         }
178
179         if (_button)
180                 *_button = b;
181
182         return 0;
183 }
184
185 int manager_watch_busname(Manager *m, const char *name) {
186         char *n;
187         int r;
188
189         assert(m);
190         assert(name);
191
192         if (set_get(m->busnames, (char*) name))
193                 return 0;
194
195         n = strdup(name);
196         if (!n)
197                 return -ENOMEM;
198
199         r = set_put(m->busnames, n);
200         if (r < 0) {
201                 free(n);
202                 return r;
203         }
204
205         return 0;
206 }
207
208 void manager_drop_busname(Manager *m, const char *name) {
209         Session *session;
210         Iterator i;
211
212         assert(m);
213         assert(name);
214
215         /* keep it if the name still owns a controller */
216         HASHMAP_FOREACH(session, m->sessions, i)
217                 if (session_is_controller(session, name))
218                         return;
219
220         free(set_remove(m->busnames, (char*) name));
221 }
222
223 int manager_process_seat_device(Manager *m, struct udev_device *d) {
224         Device *device;
225         int r;
226
227         assert(m);
228
229         if (streq_ptr(udev_device_get_action(d), "remove")) {
230
231                 device = hashmap_get(m->devices, udev_device_get_syspath(d));
232                 if (!device)
233                         return 0;
234
235                 seat_add_to_gc_queue(device->seat);
236                 device_free(device);
237
238         } else {
239                 const char *sn;
240                 Seat *seat = NULL;
241                 bool master;
242
243                 sn = udev_device_get_property_value(d, "ID_SEAT");
244                 if (isempty(sn))
245                         sn = "seat0";
246
247                 if (!seat_name_is_valid(sn)) {
248                         log_warning("Device with invalid seat name %s found, ignoring.", sn);
249                         return 0;
250                 }
251
252                 seat = hashmap_get(m->seats, sn);
253                 master = udev_device_has_tag(d, "master-of-seat");
254
255                 /* Ignore non-master devices for unknown seats */
256                 if (!master && !seat)
257                         return 0;
258
259                 r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
260                 if (r < 0)
261                         return r;
262
263                 if (!seat) {
264                         r = manager_add_seat(m, sn, &seat);
265                         if (r < 0) {
266                                 if (!device->seat)
267                                         device_free(device);
268
269                                 return r;
270                         }
271                 }
272
273                 device_attach(device, seat);
274                 seat_start(seat);
275         }
276
277         return 0;
278 }
279
280 int manager_process_button_device(Manager *m, struct udev_device *d) {
281         Button *b;
282
283         int r;
284
285         assert(m);
286
287         if (streq_ptr(udev_device_get_action(d), "remove")) {
288
289                 b = hashmap_get(m->buttons, udev_device_get_sysname(d));
290                 if (!b)
291                         return 0;
292
293                 button_free(b);
294
295         } else {
296                 const char *sn;
297
298                 r = manager_add_button(m, udev_device_get_sysname(d), &b);
299                 if (r < 0)
300                         return r;
301
302                 sn = udev_device_get_property_value(d, "ID_SEAT");
303                 if (isempty(sn))
304                         sn = "seat0";
305
306                 button_set_seat(b, sn);
307                 button_open(b);
308         }
309
310         return 0;
311 }
312
313 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
314         _cleanup_free_ char *unit = NULL;
315         Session *s;
316         int r;
317
318         assert(m);
319         assert(session);
320
321         if (pid < 1)
322                 return -EINVAL;
323
324         r = cg_pid_get_unit(pid, &unit);
325         if (r < 0)
326                 return 0;
327
328         s = hashmap_get(m->session_units, unit);
329         if (!s)
330                 return 0;
331
332         *session = s;
333         return 1;
334 }
335
336 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
337         _cleanup_free_ char *unit = NULL;
338         User *u;
339         int r;
340
341         assert(m);
342         assert(user);
343
344         if (pid < 1)
345                 return -EINVAL;
346
347         r = cg_pid_get_slice(pid, &unit);
348         if (r < 0)
349                 return 0;
350
351         u = hashmap_get(m->user_units, unit);
352         if (!u)
353                 return 0;
354
355         *user = u;
356         return 1;
357 }
358
359 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
360         Session *s;
361         bool idle_hint;
362         dual_timestamp ts = { 0, 0 };
363         Iterator i;
364
365         assert(m);
366
367         idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
368
369         HASHMAP_FOREACH(s, m->sessions, i) {
370                 dual_timestamp k;
371                 int ih;
372
373                 ih = session_get_idle_hint(s, &k);
374                 if (ih < 0)
375                         return ih;
376
377                 if (!ih) {
378                         if (!idle_hint) {
379                                 if (k.monotonic < ts.monotonic)
380                                         ts = k;
381                         } else {
382                                 idle_hint = false;
383                                 ts = k;
384                         }
385                 } else if (idle_hint) {
386
387                         if (k.monotonic > ts.monotonic)
388                                 ts = k;
389                 }
390         }
391
392         if (t)
393                 *t = ts;
394
395         return idle_hint;
396 }
397
398 bool manager_shall_kill(Manager *m, const char *user) {
399         assert(m);
400         assert(user);
401
402         if (!m->kill_user_processes)
403                 return false;
404
405         if (strv_contains(m->kill_exclude_users, user))
406                 return false;
407
408         if (strv_isempty(m->kill_only_users))
409                 return true;
410
411         return strv_contains(m->kill_only_users, user);
412 }
413
414 static int vt_is_busy(unsigned int vtnr) {
415         struct vt_stat vt_stat;
416         int r = 0;
417         _cleanup_close_ int fd;
418
419         assert(vtnr >= 1);
420
421         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
422          * we'd open the latter we'd open the foreground tty which
423          * hence would be unconditionally busy. By opening /dev/tty1
424          * we avoid this. Since tty1 is special and needs to be an
425          * explicitly loaded getty or DM this is safe. */
426
427         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
428         if (fd < 0)
429                 return -errno;
430
431         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
432                 r = -errno;
433         else
434                 r = !!(vt_stat.v_state & (1 << vtnr));
435
436         return r;
437 }
438
439 int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
440         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
441         char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
442         int r;
443
444         assert(m);
445         assert(vtnr >= 1);
446
447         if (vtnr > m->n_autovts &&
448             vtnr != m->reserve_vt)
449                 return 0;
450
451         if (vtnr != m->reserve_vt) {
452                 /* If this is the reserved TTY, we'll start the getty
453                  * on it in any case, but otherwise only if it is not
454                  * busy. */
455
456                 r = vt_is_busy(vtnr);
457                 if (r < 0)
458                         return r;
459                 else if (r > 0)
460                         return -EBUSY;
461         }
462
463         snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
464         r = sd_bus_call_method(
465                         m->bus,
466                         "org.freedesktop.systemd1",
467                         "/org/freedesktop/systemd1",
468                         "org.freedesktop.systemd1.Manager",
469                         "StartUnit",
470                         &error,
471                         NULL,
472                         "ss", name, "fail");
473         if (r < 0)
474                 log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
475
476         return r;
477 }
478
479 bool manager_is_docked(Manager *m) {
480         Iterator i;
481         Button *b;
482
483         HASHMAP_FOREACH(b, m->buttons, i)
484                 if (b->docked)
485                         return true;
486
487         return false;
488 }
489
490 int manager_count_displays(Manager *m) {
491         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
492         struct udev_list_entry *item = NULL, *first = NULL;
493         int r;
494         int n = 0;
495
496         e = udev_enumerate_new(m->udev);
497         if (!e)
498                 return -ENOMEM;
499
500         r = udev_enumerate_add_match_subsystem(e, "drm");
501         if (r < 0)
502                 return r;
503
504         r = udev_enumerate_scan_devices(e);
505         if (r < 0)
506                 return r;
507
508         first = udev_enumerate_get_list_entry(e);
509         udev_list_entry_foreach(item, first) {
510                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
511                 struct udev_device *p;
512                 const char *status;
513
514                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
515                 if (!d)
516                         return -ENOMEM;
517
518                 p = udev_device_get_parent(d);
519                 if (!p)
520                         continue;
521
522                 /* If the parent shares the same subsystem as the
523                  * device we are looking at then it is a connector,
524                  * which is what we are interested in. */
525                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
526                         continue;
527
528                 /* We count any connector which is not explicitly
529                  * "disconnected" as connected. */
530                 status = udev_device_get_sysattr_value(d, "status");
531                 if (!streq_ptr(status, "disconnected"))
532                         n++;
533         }
534
535         return n;
536 }
537
538 bool manager_is_docked_or_multiple_displays(Manager *m) {
539         int n;
540
541         /* If we are docked don't react to lid closing */
542         if (manager_is_docked(m)) {
543                 log_debug("System is docked.");
544                 return true;
545         }
546
547         /* If we have more than one display connected,
548          * assume that we are docked. */
549         n = manager_count_displays(m);
550         if (n < 0)
551                 log_warning_errno(n, "Display counting failed: %m");
552         else if (n > 1) {
553                 log_debug("Multiple (%i) displays connected.", n);
554                 return true;
555         }
556
557         return false;
558 }