chiark / gitweb /
Merge commit 'b39a2770ba55637da80e2e389222c59dbea73507'
[elogind.git] / src / libsystemd / sd-bus / sd-memfd.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 <stdio.h>
23 #include <fcntl.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/prctl.h>
27
28 #include "util.h"
29 #include "kdbus.h"
30 #include "bus-label.h"
31
32 #include "sd-memfd.h"
33 #include "sd-bus.h"
34
35 struct sd_memfd {
36         int fd;
37         FILE *f;
38 };
39
40 _public_ int sd_memfd_new(sd_memfd **m, const char *name) {
41
42         struct kdbus_cmd_memfd_make *cmd;
43         struct kdbus_item *item;
44         _cleanup_close_ int kdbus = -1;
45         _cleanup_free_ char *g = NULL;
46         size_t sz, l;
47         sd_memfd *n;
48
49         assert_return(m, -EINVAL);
50
51         kdbus = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
52         if (kdbus < 0)
53                 return -errno;
54
55         if (name) {
56                 /* The kernel side is pretty picky about the character
57                  * set here, let's do the usual bus escaping to deal
58                  * with that. */
59
60                 g = bus_label_escape(name);
61                 if (!g)
62                         return -ENOMEM;
63
64                 name = g;
65
66         } else {
67                 char pr[17] = {};
68
69                 /* If no name is specified we generate one. We include
70                  * a hint indicating our library implementation, and
71                  * add the thread name to it */
72
73                 assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0);
74
75                 if (isempty(pr))
76                         name = "sd";
77                 else {
78                         _cleanup_free_ char *e = NULL;
79
80                         e = bus_label_escape(pr);
81                         if (!e)
82                                 return -ENOMEM;
83
84                         g = strappend("sd-", e);
85                         if (!g)
86                                 return -ENOMEM;
87
88                         name = g;
89                 }
90         }
91
92         l = strlen(name);
93         sz = ALIGN8(offsetof(struct kdbus_cmd_memfd_make, items)) +
94                 ALIGN8(offsetof(struct kdbus_item, str)) +
95                 l + 1;
96
97         cmd = alloca0(sz);
98         cmd->size = sz;
99
100         item = cmd->items;
101         item->size = ALIGN8(offsetof(struct kdbus_item, str)) + l + 1;
102         item->type = KDBUS_ITEM_MEMFD_NAME;
103         memcpy(item->str, name, l + 1);
104
105         if (ioctl(kdbus, KDBUS_CMD_MEMFD_NEW, cmd) < 0)
106                 return -errno;
107
108         n = new0(struct sd_memfd, 1);
109         if (!n) {
110                 safe_close(cmd->fd);
111                 return -ENOMEM;
112         }
113
114         n->fd = cmd->fd;
115         *m = n;
116         return 0;
117 }
118
119 _public_ int sd_memfd_new_from_fd(sd_memfd **m, int fd) {
120         sd_memfd *n;
121         uint64_t sz;
122
123         assert_return(m, -EINVAL);
124         assert_return(fd >= 0, -EINVAL);
125
126         /* Check if this is a valid memfd */
127         if (ioctl(fd, KDBUS_CMD_MEMFD_SIZE_GET, &sz) < 0)
128                 return -ENOTTY;
129
130         n = new0(struct sd_memfd, 1);
131         if (!n)
132                 return -ENOMEM;
133
134         n->fd = fd;
135         *m = n;
136
137         return 0;
138 }
139
140 _public_ void sd_memfd_free(sd_memfd *m) {
141         if (!m)
142                 return;
143
144         if (m->f)
145                 fclose(m->f);
146         else
147                 safe_close(m->fd);
148
149         free(m);
150 }
151
152 _public_ int sd_memfd_get_fd(sd_memfd *m) {
153         assert_return(m, -EINVAL);
154
155         return m->fd;
156 }
157
158 _public_ int sd_memfd_get_file(sd_memfd *m, FILE **f) {
159         assert_return(m, -EINVAL);
160         assert_return(f, -EINVAL);
161
162         if (!m->f) {
163                 m->f = fdopen(m->fd, "r+");
164                 if (!m->f)
165                         return -errno;
166         }
167
168         *f = m->f;
169         return 0;
170 }
171
172 _public_ int sd_memfd_dup_fd(sd_memfd *m) {
173         int fd;
174
175         assert_return(m, -EINVAL);
176
177         fd = fcntl(m->fd, F_DUPFD_CLOEXEC, 3);
178         if (fd < 0)
179                 return -errno;
180
181         return fd;
182 }
183
184 _public_ int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p) {
185         void *q;
186         int sealed;
187
188         assert_return(m, -EINVAL);
189         assert_return(size > 0, -EINVAL);
190         assert_return(p, -EINVAL);
191
192         sealed = sd_memfd_get_sealed(m);
193         if (sealed < 0)
194                 return sealed;
195
196         q = mmap(NULL, size, sealed ? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED, m->fd, offset);
197         if (q == MAP_FAILED)
198                 return -errno;
199
200         *p = q;
201         return 0;
202 }
203
204 _public_ int sd_memfd_set_sealed(sd_memfd *m, int b) {
205         int r;
206
207         assert_return(m, -EINVAL);
208
209         r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_SET, b);
210         if (r < 0)
211                 return -errno;
212
213         return 0;
214 }
215
216 _public_ int sd_memfd_get_sealed(sd_memfd *m) {
217         int r, b;
218
219         assert_return(m, -EINVAL);
220
221         r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_GET, &b);
222         if (r < 0)
223                 return -errno;
224
225         return !!b;
226 }
227
228 _public_ int sd_memfd_get_size(sd_memfd *m, uint64_t *sz) {
229         int r;
230
231         assert_return(m, -EINVAL);
232         assert_return(sz, -EINVAL);
233
234         r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_GET, sz);
235         if (r < 0)
236                 return -errno;
237
238         return r;
239 }
240
241 _public_ int sd_memfd_set_size(sd_memfd *m, uint64_t sz) {
242         int r;
243
244         assert_return(m, -EINVAL);
245
246         r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_SET, &sz);
247         if (r < 0)
248                 return -errno;
249
250         return r;
251 }
252
253 _public_ int sd_memfd_new_and_map(sd_memfd **m, const char *name, size_t sz, void **p) {
254         sd_memfd *n;
255         int r;
256
257         r = sd_memfd_new(&n, name);
258         if (r < 0)
259                 return r;
260
261         r = sd_memfd_set_size(n, sz);
262         if (r < 0) {
263                 sd_memfd_free(n);
264                 return r;
265         }
266
267         r = sd_memfd_map(n, 0, sz, p);
268         if (r < 0) {
269                 sd_memfd_free(n);
270                 return r;
271         }
272
273         *m = n;
274         return 0;
275 }
276
277 _public_ int sd_memfd_get_name(sd_memfd *m, char **name) {
278         char path[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int)], buf[FILENAME_MAX+1], *e;
279         const char *delim, *end;
280         _cleanup_free_ char *n = NULL;
281         ssize_t k;
282
283         assert_return(m, -EINVAL);
284         assert_return(name, -EINVAL);
285
286         sprintf(path, "/proc/self/fd/%i", m->fd);
287
288         k = readlink(path, buf, sizeof(buf));
289         if (k < 0)
290                 return -errno;
291
292         if ((size_t) k >= sizeof(buf))
293                 return -E2BIG;
294
295         buf[k] = 0;
296
297         delim = strstr(buf, ":[");
298         if (!delim)
299                 return -EIO;
300
301         delim = strchr(delim + 2, ':');
302         if (!delim)
303                 return -EIO;
304
305         delim++;
306
307         end = strchr(delim, ']');
308         if (!end)
309                 return -EIO;
310
311         n = strndup(delim, end - delim);
312         if (!n)
313                 return -ENOMEM;
314
315         e = bus_label_unescape(n);
316         if (!e)
317                 return -ENOMEM;
318
319         *name = e;
320
321         return 0;
322 }