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