chiark / gitweb /
logind: implement ACL management
[elogind.git] / src / logind-seat.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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/ioctl.h>
27 #include <linux/vt.h>
28 #include <string.h>
29
30 #include "logind-seat.h"
31 #include "logind-acl.h"
32 #include "util.h"
33
34 Seat *seat_new(Manager *m, const char *id) {
35         Seat *s;
36
37         assert(m);
38         assert(id);
39
40         s = new0(Seat, 1);
41         if (!s)
42                 return NULL;
43
44         s->state_file = strappend("/run/systemd/seat/", id);
45         if (!s->state_file) {
46                 free(s);
47                 return NULL;
48         }
49
50         s->id = file_name_from_path(s->state_file);
51
52         if (hashmap_put(m->seats, s->id, s) < 0) {
53                 free(s->id);
54                 free(s);
55                 return NULL;
56         }
57
58         s->manager = m;
59
60         return s;
61 }
62
63 void seat_free(Seat *s) {
64         assert(s);
65
66         while (s->sessions)
67                 session_free(s->sessions);
68
69         assert(!s->active);
70
71         while (s->devices)
72                 device_free(s->devices);
73
74         hashmap_remove(s->manager->seats, s->id);
75
76         free(s->state_file);
77         free(s);
78 }
79
80 int seat_save(Seat *s) {
81         FILE *f;
82         int r;
83
84         assert(s);
85
86         r = safe_mkdir("/run/systemd/seat", 0755, 0, 0);
87         if (r < 0)
88                 return r;
89
90         f = fopen(s->state_file, "we");
91         if (!f)
92                 return -errno;
93
94         fprintf(f,
95                 "IS_VTCONSOLE=%i\n",
96                 s->manager->vtconsole == s);
97
98         if (s->active) {
99                 assert(s->active->user);
100
101                 fprintf(f,
102                         "ACTIVE=%s\n"
103                         "ACTIVE_UID=%lu\n",
104                         s->active->id,
105                         (unsigned long) s->active->user->uid);
106         }
107
108         if (s->sessions) {
109                 Session *i;
110                 fputs("OTHER_UIDS=", f);
111
112                 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
113                         assert(i->user);
114
115                         if (i == s->active)
116                                 continue;
117
118                         fprintf(f,
119                                 "%s%lu",
120                                 i == s->sessions ? "" : " ",
121                                 (unsigned long) i->user->uid);
122                 }
123         }
124
125         fflush(f);
126         if (ferror(f)) {
127                 r = -errno;
128                 unlink(s->state_file);
129         }
130
131         fclose(f);
132         return r;
133 }
134
135 int seat_load(Seat *s) {
136         assert(s);
137
138         return 0;
139 }
140
141 static int vt_allocate(int vtnr) {
142         int fd, r;
143         char *p;
144
145         assert(vtnr >= 1);
146
147         if (asprintf(&p, "/dev/tty%i", vtnr) < 0)
148                 return -ENOMEM;
149
150         fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
151         free(p);
152
153         r = fd < 0 ? -errno : 0;
154
155         if (fd >= 0)
156                 close_nointr_nofail(fd);
157
158         return r;
159 }
160
161 int seat_preallocate_vts(Seat *s) {
162         int i, r = 0;
163
164         assert(s);
165         assert(s->manager);
166
167         if (s->manager->n_autovts <= 0)
168                 return 0;
169
170         if (s->manager->vtconsole != s)
171                 return 0;
172
173         for (i = 1; i < s->manager->n_autovts; i++) {
174                 int q;
175
176                 q = vt_allocate(i);
177                 if (r >= 0 && q < 0)
178                         r = q;
179         }
180
181         return r;
182 }
183
184 int seat_apply_acls(Seat *s, Session *old_active) {
185         int r;
186
187         assert(s);
188
189         r = devnode_acl_all(s->manager->udev,
190                             s->id,
191                             false,
192                             !!old_active, old_active ? old_active->user->uid : 0,
193                             !!s->active, s->active ? s->active->user->uid : 0);
194
195         if (r < 0)
196                 log_error("Failed to apply ACLs: %s", strerror(-r));
197
198         return r;
199 }
200
201 int seat_active_vt_changed(Seat *s, int vtnr) {
202         Session *i;
203         Session *old_active;
204
205         assert(s);
206         assert(vtnr >= 1);
207         assert(s->manager->vtconsole == s);
208
209         old_active = s->active;
210         s->active = NULL;
211
212         LIST_FOREACH(sessions_by_seat, i, s->sessions)
213                 if (i->vtnr == vtnr) {
214                         s->active = i;
215                         break;
216                 }
217
218         if (old_active == s->active)
219                 return 0;
220
221         seat_apply_acls(s, old_active);
222         manager_spawn_autovt(s->manager, vtnr);
223
224         return 0;
225 }
226
227 int seat_stop(Seat *s) {
228         Session *session;
229         int r = 0;
230
231         assert(s);
232
233         LIST_FOREACH(sessions_by_seat, session, s->sessions) {
234                 int k;
235
236                 k = session_stop(session);
237                 if (k < 0)
238                         r = k;
239         }
240
241         return r;
242 }