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