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