chiark / gitweb /
bus: parse and expose kdbus message timestamps
[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         uint64_t nsec = 0;
229         int r;
230
231         assert(bus);
232         assert(k);
233         assert(ret);
234
235         if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
236                 return 0;
237
238         KDBUS_MSG_FOREACH_DATA(d, k) {
239                 size_t l;
240
241                 l = d->size - offsetof(struct kdbus_msg_data, data);
242
243                 if (d->type == KDBUS_MSG_PAYLOAD) {
244
245                         if (!h) {
246                                 if (l < sizeof(struct bus_header))
247                                         return -EBADMSG;
248
249                                 h = (struct bus_header*) d->data;
250                         }
251
252                         n_payload++;
253                         n_bytes += l;
254
255                 } else if (d->type == KDBUS_MSG_UNIX_FDS) {
256                         int *f;
257                         unsigned j;
258
259                         j = l / sizeof(int);
260                         f = realloc(fds, sizeof(int) * (n_fds + j));
261                         if (!f)
262                                 return -ENOMEM;
263
264                         fds = f;
265                         memcpy(fds + n_fds, d->fds, j);
266                         n_fds += j;
267
268                 } else if (d->type == KDBUS_MSG_SRC_CREDS)
269                         creds = &d->creds;
270                 else if (d->type == KDBUS_MSG_TIMESTAMP)
271                         nsec = d->ts_ns;
272         }
273
274         if (!h)
275                 return -EBADMSG;
276
277         r = bus_header_size(h, &total);
278         if (r < 0)
279                 return r;
280
281         if (n_bytes != total)
282                 return -EBADMSG;
283
284         r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, NULL, 0, &m);
285         if (r < 0)
286                 return r;
287
288         KDBUS_MSG_FOREACH_DATA(d, k) {
289                 size_t l;
290
291                 if (d->type != KDBUS_MSG_PAYLOAD)
292                         continue;
293
294                 l = d->size - offsetof(struct kdbus_msg_data, data);
295                 if (idx == sizeof(struct bus_header) &&
296                     l == BUS_MESSAGE_FIELDS_SIZE(m))
297                         m->fields = d->data;
298                 else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
299                          l == BUS_MESSAGE_BODY_SIZE(m))
300                         m->body = d->data;
301                 else if (!(idx == 0 && l == sizeof(struct bus_header)) &&
302                          !(idx == sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m))) {
303                         sd_bus_message_unref(m);
304                         return -EBADMSG;
305                 }
306
307                 idx += l;
308         }
309
310         if (creds) {
311                 m->pid_starttime = creds->starttime / NSEC_PER_USEC;
312                 m->uid = creds->uid;
313                 m->gid = creds->gid;
314                 m->pid = creds->pid;
315                 m->tid = creds->tid;
316                 m->uid_valid = m->gid_valid = true;
317         }
318
319         m->timestamp = nsec / NSEC_PER_USEC;
320
321         r = bus_message_parse_fields(m);
322         if (r < 0) {
323                 sd_bus_message_unref(m);
324                 return r;
325         }
326
327         /* We take possession of the kmsg struct now */
328         m->kdbus = k;
329         m->free_kdbus = true;
330         m->free_fds = true;
331
332         fds = NULL;
333
334         *ret = m;
335         return 1;
336 }
337
338 int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
339         struct kdbus_msg *k;
340         size_t sz = 128;
341         int r;
342
343         assert(bus);
344         assert(m);
345
346         for (;;) {
347                 void *q;
348
349                 q = realloc(bus->rbuffer, sz);
350                 if (!q)
351                         return -errno;
352
353                 k = bus->rbuffer = q;
354                 k->size = sz;
355
356                 r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
357                 if (r >= 0)
358                         break;
359
360                 if (errno == EAGAIN)
361                         return 0;
362
363                 if (errno != ENOBUFS)
364                         return -errno;
365
366                 sz *= 2;
367         }
368
369         r = bus_kernel_make_message(bus, k, m);
370         if (r > 0)
371                 bus->rbuffer = NULL;
372         else
373                 close_kdbus_msg(k);
374
375         return r;
376 }
377
378 int bus_kernel_create(const char *name, char **s) {
379         struct kdbus_cmd_fname *fname;
380         size_t l;
381         int fd;
382         char *p;
383
384         assert(name);
385         assert(s);
386
387         fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
388         if (fd < 0)
389                 return -errno;
390
391         l = strlen(name);
392         fname = alloca(offsetof(struct kdbus_cmd_fname, name) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
393         sprintf(fname->name, "%lu-%s", (unsigned long) getuid(), name);
394         fname->size = offsetof(struct kdbus_cmd_fname, name) + strlen(fname->name) + 1;
395         fname->kernel_flags = KDBUS_CMD_FNAME_ACCESS_WORLD;
396         fname->user_flags = 0;
397
398         p = strjoin("/dev/kdbus/", fname->name, "/bus", NULL);
399         if (!p)
400                 return -ENOMEM;
401
402         if (ioctl(fd, KDBUS_CMD_BUS_MAKE, fname) < 0) {
403                 close_nointr_nofail(fd);
404                 free(p);
405                 return -errno;
406         }
407
408         if (s)
409                 *s = p;
410
411         return fd;
412 }