chiark / gitweb /
Classify processes from sessions into cgroups
[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
283         Session *s;
284         int r;
285
286         assert(m);
287         assert(session);
288
289         if (pid < 1)
290                 return -EINVAL;
291
292 /// elogind does not support systemd units, but its own session system
293 #if 0
294         r = cg_pid_get_unit(pid, &unit);
295         if (r < 0)
296                 return 0;
297
298         s = hashmap_get(m->session_units, unit);
299 #else
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 #endif
306         if (!s)
307                 return 0;
308
309         *session = s;
310         return 1;
311 }
312
313 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
314 /// elogind does not support systemd units, but its own session system
315 #if 0
316         _cleanup_free_ char *unit = NULL;
317         User *u;
318 #else
319         Session *s;
320 #endif
321         int r;
322
323         assert(m);
324         assert(user);
325
326         if (pid < 1)
327                 return -EINVAL;
328
329 /// elogind does not support systemd units, but its own session system
330 #if 0
331         r = cg_pid_get_slice(pid, &unit);
332         if (r < 0)
333                 return 0;
334
335         u = hashmap_get(m->user_units, unit);
336         if (!u)
337                 return 0;
338
339         *user = u;
340 #else
341         r = manager_get_session_by_pid (m, pid, &s);
342         if (r <= 0)
343                 return r;
344
345         *user = s->user;
346 #endif // 0
347
348         return 1;
349 }
350
351 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
352         Session *s;
353         bool idle_hint;
354         dual_timestamp ts = { 0, 0 };
355         Iterator i;
356
357         assert(m);
358
359         idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
360
361         HASHMAP_FOREACH(s, m->sessions, i) {
362                 dual_timestamp k;
363                 int ih;
364
365                 ih = session_get_idle_hint(s, &k);
366                 if (ih < 0)
367                         return ih;
368
369                 if (!ih) {
370                         if (!idle_hint) {
371                                 if (k.monotonic < ts.monotonic)
372                                         ts = k;
373                         } else {
374                                 idle_hint = false;
375                                 ts = k;
376                         }
377                 } else if (idle_hint) {
378
379                         if (k.monotonic > ts.monotonic)
380                                 ts = k;
381                 }
382         }
383
384         if (t)
385                 *t = ts;
386
387         return idle_hint;
388 }
389
390 bool manager_shall_kill(Manager *m, const char *user) {
391         assert(m);
392         assert(user);
393
394         if (!m->kill_user_processes)
395                 return false;
396
397         if (strv_contains(m->kill_exclude_users, user))
398                 return false;
399
400         if (strv_isempty(m->kill_only_users))
401                 return true;
402
403         return strv_contains(m->kill_only_users, user);
404 }
405
406 /// UNNEEDED by elogind
407 #if 0
408 static int vt_is_busy(unsigned int vtnr) {
409         struct vt_stat vt_stat;
410         int r = 0;
411         _cleanup_close_ int fd;
412
413         assert(vtnr >= 1);
414
415         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
416          * we'd open the latter we'd open the foreground tty which
417          * hence would be unconditionally busy. By opening /dev/tty1
418          * we avoid this. Since tty1 is special and needs to be an
419          * explicitly loaded getty or DM this is safe. */
420
421         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
422         if (fd < 0)
423                 return -errno;
424
425         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
426                 r = -errno;
427         else
428                 r = !!(vt_stat.v_state & (1 << vtnr));
429
430         return r;
431 }
432
433 int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
434         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
435         char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
436         int r;
437
438         assert(m);
439         assert(vtnr >= 1);
440
441         if (vtnr > m->n_autovts &&
442             vtnr != m->reserve_vt)
443                 return 0;
444
445         if (vtnr != m->reserve_vt) {
446                 /* If this is the reserved TTY, we'll start the getty
447                  * on it in any case, but otherwise only if it is not
448                  * busy. */
449
450                 r = vt_is_busy(vtnr);
451                 if (r < 0)
452                         return r;
453                 else if (r > 0)
454                         return -EBUSY;
455         }
456
457         snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
458         r = sd_bus_call_method(
459                         m->bus,
460                         "org.freedesktop.systemd1",
461                         "/org/freedesktop/systemd1",
462                         "org.freedesktop.systemd1.Manager",
463                         "StartUnit",
464                         &error,
465                         NULL,
466                         "ss", name, "fail");
467         if (r < 0)
468                 log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
469
470         return r;
471 }
472 #endif // 0
473
474 bool manager_is_docked(Manager *m) {
475         Iterator i;
476         Button *b;
477
478         HASHMAP_FOREACH(b, m->buttons, i)
479                 if (b->docked)
480                         return true;
481
482         return false;
483 }
484
485 int manager_count_displays(Manager *m) {
486         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
487         struct udev_list_entry *item = NULL, *first = NULL;
488         int r;
489         int n = 0;
490
491         e = udev_enumerate_new(m->udev);
492         if (!e)
493                 return -ENOMEM;
494
495         r = udev_enumerate_add_match_subsystem(e, "drm");
496         if (r < 0)
497                 return r;
498
499         r = udev_enumerate_scan_devices(e);
500         if (r < 0)
501                 return r;
502
503         first = udev_enumerate_get_list_entry(e);
504         udev_list_entry_foreach(item, first) {
505                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
506                 struct udev_device *p;
507                 const char *status;
508
509                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
510                 if (!d)
511                         return -ENOMEM;
512
513                 p = udev_device_get_parent(d);
514                 if (!p)
515                         continue;
516
517                 /* If the parent shares the same subsystem as the
518                  * device we are looking at then it is a connector,
519                  * which is what we are interested in. */
520                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
521                         continue;
522
523                 /* We count any connector which is not explicitly
524                  * "disconnected" as connected. */
525                 status = udev_device_get_sysattr_value(d, "status");
526                 if (!streq_ptr(status, "disconnected"))
527                         n++;
528         }
529
530         return n;
531 }
532
533 bool manager_is_docked_or_multiple_displays(Manager *m) {
534         int n;
535
536         /* If we are docked don't react to lid closing */
537         if (manager_is_docked(m)) {
538                 log_debug("System is docked.");
539                 return true;
540         }
541
542         /* If we have more than one display connected,
543          * assume that we are docked. */
544         n = manager_count_displays(m);
545         if (n < 0)
546                 log_warning_errno(n, "Display counting failed: %m");
547         else if (n > 1) {
548                 log_debug("Multiple (%i) displays connected.", n);
549                 return true;
550         }
551
552         return false;
553 }