chiark / gitweb /
3d26f16cda7866f3149d973e62bf94a0b55b6cad
[elogind.git] / src / libsystemd-bus / bus-kernel.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 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 <fcntl.h>
23
24 #include "util.h"
25
26 #include "bus-internal.h"
27 #include "bus-message.h"
28 #include "bus-kernel.h"
29
30 #define KDBUS_MSG_FOREACH_DATA(d, k)                                    \
31         for ((d) = (k)->data;                                           \
32              (uint8_t*) (d) < (uint8_t*) (k) + (k)->size;               \
33              (d) = (struct kdbus_msg_data*) ((uint8_t*) (d) + ALIGN8((d)->size)))
34
35
36
37 static int parse_unique_name(const char *s, uint64_t *id) {
38         int r;
39
40         assert(s);
41         assert(id);
42
43         if (!startswith(s, ":1."))
44                 return 0;
45
46         r = safe_atou64(s + 3, id);
47         if (r < 0)
48                 return r;
49
50         return 1;
51 }
52
53 static void append_payload_vec(struct kdbus_msg_data **d, const void *p, size_t sz) {
54         assert(d);
55         assert(p);
56         assert(sz > 0);
57
58         (*d)->size = offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec);
59         (*d)->type = KDBUS_MSG_PAYLOAD_VEC;
60         (*d)->vec.address = (uint64_t) p;
61         (*d)->vec.size = sz;
62
63         *d = (struct kdbus_msg_data*) ((uint8_t*) *d + ALIGN8((*d)->size));
64 }
65
66 static void append_destination(struct kdbus_msg_data **d, const char *s, size_t length) {
67         assert(d);
68         assert(d);
69
70         (*d)->size = offsetof(struct kdbus_msg_data, data) + length + 1;
71         (*d)->type = KDBUS_MSG_DST_NAME;
72         memcpy((*d)->data, s, length + 1);
73
74         *d = (struct kdbus_msg_data*) ((uint8_t*) *d + ALIGN8((*d)->size));
75 }
76
77 static int bus_message_setup_kmsg(sd_bus_message *m) {
78         struct kdbus_msg_data *d;
79         bool well_known;
80         uint64_t unique;
81         size_t sz, dl;
82         int r;
83
84         assert(m);
85         assert(m->sealed);
86
87         if (m->kdbus)
88                 return 0;
89
90         if (m->destination) {
91                 r = parse_unique_name(m->destination, &unique);
92                 if (r < 0)
93                         return r;
94
95                 well_known = r == 0;
96         } else
97                 well_known = false;
98
99         sz = offsetof(struct kdbus_msg, data);
100
101         /* Add in fixed header, fields header, fields header padding and payload */
102         sz += 4 * ALIGN8(offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec));
103
104         /* Add in well-known destination header */
105         if (well_known) {
106                 dl = strlen(m->destination);
107                 sz += ALIGN8(offsetof(struct kdbus_msg, data) + dl + 1);
108         }
109
110         m->kdbus = malloc0(sz);
111         if (!m->kdbus)
112                 return -ENOMEM;
113
114         m->kdbus->flags =
115                 ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
116                 ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
117         m->kdbus->dst_id =
118                 well_known ? 0 :
119                 m->destination ? unique : (uint64_t) -1;
120         m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
121         m->kdbus->cookie = m->header->serial;
122
123         m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
124
125         d = m->kdbus->data;
126
127         if (well_known)
128                 append_destination(&d, m->destination, dl);
129
130         append_payload_vec(&d, m->header, sizeof(*m->header));
131
132         if (m->fields) {
133                 append_payload_vec(&d, m->fields, m->header->fields_size);
134
135                 if (m->header->fields_size % 8 != 0) {
136                         static const uint8_t padding[7] = {};
137
138                         append_payload_vec(&d, padding, 8 - (m->header->fields_size % 8));
139                 }
140         }
141
142         if (m->body)
143                 append_payload_vec(&d, m->body, m->header->body_size);
144
145         m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
146         assert(m->kdbus->size <= sz);
147
148         m->free_kdbus = true;
149
150         return 0;
151 }
152
153 int bus_kernel_take_fd(sd_bus *b) {
154         struct kdbus_cmd_hello hello = {};
155         int r;
156
157         assert(b);
158
159         r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
160         if (r < 0)
161                 return -errno;
162
163         if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0)
164                 return -ENOMEM;
165
166         b->is_kernel = true;
167
168         r = bus_start_running(b);
169         if (r < 0)
170                 return r;
171
172         return 1;
173 }
174
175 int bus_kernel_connect(sd_bus *b) {
176         assert(b);
177         assert(b->input_fd < 0);
178         assert(b->output_fd < 0);
179         assert(b->kernel);
180
181         b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
182         if (b->input_fd < 0)
183                 return -errno;
184
185         b->output_fd = b->input_fd;
186
187         return bus_kernel_take_fd(b);
188 }
189
190 int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
191         int r;
192
193         assert(bus);
194         assert(m);
195         assert(bus->state == BUS_RUNNING);
196
197         r = bus_message_setup_kmsg(m);
198         if (r < 0)
199                 return r;
200
201         r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
202         if (r < 0)
203                 return errno == EAGAIN ? 0 : -errno;
204
205         return 0;
206 }
207
208 static void close_kdbus_msg(struct kdbus_msg *k) {
209         struct kdbus_msg_data *d;
210
211         KDBUS_MSG_FOREACH_DATA(d, k) {
212
213                 if (d->type != KDBUS_MSG_UNIX_FDS)
214                         continue;
215
216                 close_many(d->fds, (d->size - offsetof(struct kdbus_msg_data, fds)) / sizeof(int));
217         }
218 }
219
220 static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
221         sd_bus_message *m = NULL;
222         struct kdbus_msg_data *d;
223         unsigned n_payload = 0, n_fds = 0;
224         _cleanup_free_ int *fds = NULL;
225         struct bus_header *h = NULL;
226         size_t total, n_bytes = 0, idx = 0;
227         struct kdbus_creds *creds = NULL;
228         int r;
229
230         assert(bus);
231         assert(k);
232         assert(ret);
233
234         if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
235                 return 0;
236
237         KDBUS_MSG_FOREACH_DATA(d, k) {
238                 size_t l;
239
240                 l = d->size - offsetof(struct kdbus_msg_data, data);
241
242                 if (d->type == KDBUS_MSG_PAYLOAD) {
243
244                         if (!h) {
245                                 if (l < sizeof(struct bus_header))
246                                         return -EBADMSG;
247
248                                 h = (struct bus_header*) d->data;
249                         }
250
251                         n_payload++;
252                         n_bytes += l;
253
254                 } else if (d->type == KDBUS_MSG_UNIX_FDS) {
255                         int *f;
256                         unsigned j;
257
258                         j = l / sizeof(int);
259                         f = realloc(fds, sizeof(int) * (n_fds + j));
260                         if (!f)
261                                 return -ENOMEM;
262
263                         fds = f;
264                         memcpy(fds + n_fds, d->fds, j);
265                         n_fds += j;
266
267                 } else if (d->type == KDBUS_MSG_SRC_CREDS)
268                         creds = &d->creds;
269         }
270
271         if (!h)
272                 return -EBADMSG;
273
274         r = bus_header_size(h, &total);
275         if (r < 0)
276                 return r;
277
278         if (n_bytes != total)
279                 return -EBADMSG;
280
281         r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, NULL, 0, &m);
282         if (r < 0)
283                 return r;
284
285         KDBUS_MSG_FOREACH_DATA(d, k) {
286                 size_t l;
287
288                 if (d->type != KDBUS_MSG_PAYLOAD)
289                         continue;
290
291                 l = d->size - offsetof(struct kdbus_msg_data, data);
292                 if (idx == sizeof(struct bus_header) &&
293                     l == BUS_MESSAGE_FIELDS_SIZE(m))
294                         m->fields = d->data;
295                 else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
296                          l == BUS_MESSAGE_BODY_SIZE(m))
297                         m->body = d->data;
298                 else if (!(idx == 0 && l == sizeof(struct bus_header)) &&
299                          !(idx == sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m))) {
300                         sd_bus_message_unref(m);
301                         return -EBADMSG;
302                 }
303
304                 idx += l;
305         }
306
307         if (creds) {
308                 m->pid_starttime = creds->starttime / NSEC_PER_USEC;
309                 m->uid = creds->uid;
310                 m->gid = creds->gid;
311                 m->pid = creds->pid;
312                 m->tid = creds->tid;
313                 m->uid_valid = m->gid_valid = true;
314         }
315
316         r = bus_message_parse_fields(m);
317         if (r < 0) {
318                 sd_bus_message_unref(m);
319                 return r;
320         }
321
322         /* We take possession of the kmsg struct now */
323         m->kdbus = k;
324         m->free_kdbus = true;
325         m->free_fds = true;
326
327         fds = NULL;
328
329         *ret = m;
330         return 1;
331 }
332
333 int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
334         struct kdbus_msg *k;
335         size_t sz = 128;
336         int r;
337
338         assert(bus);
339         assert(m);
340
341         for (;;) {
342                 void *q;
343
344                 q = realloc(bus->rbuffer, sz);
345                 if (!q)
346                         return -errno;
347
348                 k = bus->rbuffer = q;
349                 k->size = sz;
350
351                 r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
352                 if (r >= 0)
353                         break;
354
355                 if (errno == EAGAIN)
356                         return 0;
357
358                 if (errno != ENOBUFS)
359                         return -errno;
360
361                 sz *= 2;
362         }
363
364         r = bus_kernel_make_message(bus, k, m);
365         if (r > 0)
366                 bus->rbuffer = NULL;
367         else
368                 close_kdbus_msg(k);
369
370         return r;
371 }
372
373 int bus_kernel_create(const char *name, char **s) {
374         struct kdbus_cmd_fname *fname;
375         size_t l;
376         int fd;
377         char *p;
378
379         assert(name);
380         assert(s);
381
382         fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
383         if (fd < 0)
384                 return -errno;
385
386         l = strlen(name);
387         fname = alloca(offsetof(struct kdbus_cmd_fname, name) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
388         sprintf(fname->name, "%lu-%s", (unsigned long) getuid(), name);
389         fname->size = offsetof(struct kdbus_cmd_fname, name) + strlen(fname->name) + 1;
390         fname->kernel_flags = KDBUS_CMD_FNAME_ACCESS_WORLD;
391         fname->user_flags = 0;
392
393         p = strjoin("/dev/kdbus/", fname->name, "/bus", NULL);
394         if (!p)
395                 return -ENOMEM;
396
397         if (ioctl(fd, KDBUS_CMD_BUS_MAKE, fname) < 0) {
398                 close_nointr_nofail(fd);
399                 free(p);
400                 return -errno;
401         }
402
403         if (s)
404                 *s = p;
405
406         return fd;
407 }