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
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 *session_name = 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_session(pid, &session_name);
325         if (r < 0)
326                 return 0;
327
328         s = hashmap_get(m->sessions, session_name);
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         Session *s;
338         int r;
339
340         assert(m);
341         assert(user);
342
343         r = manager_get_session_by_pid (m, pid, &s);
344         if (r <= 0)
345                 return r;
346
347         *user = s->user;
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 static int vt_is_busy(unsigned int vtnr) {
407         struct vt_stat vt_stat;
408         int r = 0;
409         _cleanup_close_ int fd;
410
411         assert(vtnr >= 1);
412
413         /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
414          * we'd open the latter we'd open the foreground tty which
415          * hence would be unconditionally busy. By opening /dev/tty1
416          * we avoid this. Since tty1 is special and needs to be an
417          * explicitly loaded getty or DM this is safe. */
418
419         fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
420         if (fd < 0)
421                 return -errno;
422
423         if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
424                 r = -errno;
425         else
426                 r = !!(vt_stat.v_state & (1 << vtnr));
427
428         return r;
429 }
430
431 bool manager_is_docked(Manager *m) {
432         Iterator i;
433         Button *b;
434
435         HASHMAP_FOREACH(b, m->buttons, i)
436                 if (b->docked)
437                         return true;
438
439         return false;
440 }
441
442 int manager_count_displays(Manager *m) {
443         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
444         struct udev_list_entry *item = NULL, *first = NULL;
445         int r;
446         int n = 0;
447
448         e = udev_enumerate_new(m->udev);
449         if (!e)
450                 return -ENOMEM;
451
452         r = udev_enumerate_add_match_subsystem(e, "drm");
453         if (r < 0)
454                 return r;
455
456         r = udev_enumerate_scan_devices(e);
457         if (r < 0)
458                 return r;
459
460         first = udev_enumerate_get_list_entry(e);
461         udev_list_entry_foreach(item, first) {
462                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
463                 struct udev_device *p;
464                 const char *status;
465
466                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
467                 if (!d)
468                         return -ENOMEM;
469
470                 p = udev_device_get_parent(d);
471                 if (!p)
472                         continue;
473
474                 /* If the parent shares the same subsystem as the
475                  * device we are looking at then it is a connector,
476                  * which is what we are interested in. */
477                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
478                         continue;
479
480                 /* We count any connector which is not explicitly
481                  * "disconnected" as connected. */
482                 status = udev_device_get_sysattr_value(d, "status");
483                 if (!streq_ptr(status, "disconnected"))
484                         n++;
485         }
486
487         return n;
488 }
489
490 bool manager_is_docked_or_multiple_displays(Manager *m) {
491         int n;
492
493         /* If we are docked don't react to lid closing */
494         if (manager_is_docked(m)) {
495                 log_debug("System is docked.");
496                 return true;
497         }
498
499         /* If we have more than one display connected,
500          * assume that we are docked. */
501         n = manager_count_displays(m);
502         if (n < 0)
503                 log_warning_errno(n, "Display counting failed: %m");
504         else if (n > 1) {
505                 log_debug("Multiple (%i) displays connected.", n);
506                 return true;
507         }
508
509         return false;
510 }