chiark / gitweb /
f6714945e06ff6e63ee0ba9dd8570a3a4179e9df
[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         assert(m);
316         assert(session);
317
318         /* Without cgroups, we have no way to map from pid to
319            session.  */
320         return 0;
321 }
322
323 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
324         assert(m);
325         assert(user);
326
327         if (pid < 1)
328                 return -EINVAL;
329
330         /* Without cgroups, we have no way to map from pid to
331            user.  */
332         return 0;
333 }
334
335 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
336         Session *s;
337         bool idle_hint;
338         dual_timestamp ts = { 0, 0 };
339         Iterator i;
340
341         assert(m);
342
343         idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
344
345         HASHMAP_FOREACH(s, m->sessions, i) {
346                 dual_timestamp k;
347                 int ih;
348
349                 ih = session_get_idle_hint(s, &k);
350                 if (ih < 0)
351                         return ih;
352
353                 if (!ih) {
354                         if (!idle_hint) {
355                                 if (k.monotonic < ts.monotonic)
356                                         ts = k;
357                         } else {
358                                 idle_hint = false;
359                                 ts = k;
360                         }
361                 } else if (idle_hint) {
362
363                         if (k.monotonic > ts.monotonic)
364                                 ts = k;
365                 }
366         }
367
368         if (t)
369                 *t = ts;
370
371         return idle_hint;
372 }
373
374 bool manager_shall_kill(Manager *m, const char *user) {
375         assert(m);
376         assert(user);
377
378         if (!m->kill_user_processes)
379                 return false;
380
381         if (strv_contains(m->kill_exclude_users, user))
382                 return false;
383
384         if (strv_isempty(m->kill_only_users))
385                 return true;
386
387         return strv_contains(m->kill_only_users, user);
388 }
389
390 static int vt_is_busy(unsigned int vtnr) {
391         struct vt_stat vt_stat;
392         int r = 0;
393         _cleanup_close_ int fd;
394
395         assert(vtnr >= 1);
396
397         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
398          * we'd open the latter we'd open the foreground tty which
399          * hence would be unconditionally busy. By opening /dev/tty1
400          * we avoid this. Since tty1 is special and needs to be an
401          * explicitly loaded getty or DM this is safe. */
402
403         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
404         if (fd < 0)
405                 return -errno;
406
407         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
408                 r = -errno;
409         else
410                 r = !!(vt_stat.v_state & (1 << vtnr));
411
412         return r;
413 }
414
415 bool manager_is_docked(Manager *m) {
416         Iterator i;
417         Button *b;
418
419         HASHMAP_FOREACH(b, m->buttons, i)
420                 if (b->docked)
421                         return true;
422
423         return false;
424 }
425
426 int manager_count_displays(Manager *m) {
427         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
428         struct udev_list_entry *item = NULL, *first = NULL;
429         int r;
430         int n = 0;
431
432         e = udev_enumerate_new(m->udev);
433         if (!e)
434                 return -ENOMEM;
435
436         r = udev_enumerate_add_match_subsystem(e, "drm");
437         if (r < 0)
438                 return r;
439
440         r = udev_enumerate_scan_devices(e);
441         if (r < 0)
442                 return r;
443
444         first = udev_enumerate_get_list_entry(e);
445         udev_list_entry_foreach(item, first) {
446                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
447                 struct udev_device *p;
448                 const char *status;
449
450                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
451                 if (!d)
452                         return -ENOMEM;
453
454                 p = udev_device_get_parent(d);
455                 if (!p)
456                         continue;
457
458                 /* If the parent shares the same subsystem as the
459                  * device we are looking at then it is a connector,
460                  * which is what we are interested in. */
461                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
462                         continue;
463
464                 /* We count any connector which is not explicitly
465                  * "disconnected" as connected. */
466                 status = udev_device_get_sysattr_value(d, "status");
467                 if (!streq_ptr(status, "disconnected"))
468                         n++;
469         }
470
471         return n;
472 }
473
474 bool manager_is_docked_or_multiple_displays(Manager *m) {
475         int n;
476
477         /* If we are docked don't react to lid closing */
478         if (manager_is_docked(m)) {
479                 log_debug("System is docked.");
480                 return true;
481         }
482
483         /* If we have more than one display connected,
484          * assume that we are docked. */
485         n = manager_count_displays(m);
486         if (n < 0)
487                 log_warning_errno(n, "Display counting failed: %m");
488         else if (n > 1) {
489                 log_debug("Multiple (%i) displays connected.", n);
490                 return true;
491         }
492
493         return false;
494 }