chiark / gitweb /
2b4d6ff0b37b74eb44da7ddc28b4a19e3ae7750f
[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 = { 0, 0 };
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 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 int manager_count_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;
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                 /* We count any connector which is not explicitly
527                  * "disconnected" as connected. */
528                 status = udev_device_get_sysattr_value(d, "status");
529                 if (!streq_ptr(status, "disconnected"))
530                         n++;
531         }
532
533         return n;
534 }
535
536 bool manager_is_docked_or_multiple_displays(Manager *m) {
537         int n;
538
539         /* If we are docked don't react to lid closing */
540         if (manager_is_docked(m)) {
541                 log_debug("System is docked.");
542                 return true;
543         }
544
545         /* If we have more than one display connected,
546          * assume that we are docked. */
547         n = manager_count_displays(m);
548         if (n < 0)
549                 log_warning_errno(n, "Display counting failed: %m");
550         else if (n > 1) {
551                 log_debug("Multiple (%i) displays connected.", n);
552                 return true;
553         }
554
555         return false;
556 }