chiark / gitweb /
913be2086ac79461593e9174c0700c743dc117cb
[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(const char *name, sd_memfd **m) {
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         cmd = alloca0(sz);
96         cmd->size = sz;
97         item = cmd->items;
98         item->size = ALIGN8(offsetof(struct kdbus_item, str)) + l + 1;
99         item->type = KDBUS_ITEM_MEMFD_NAME;
100         memcpy(item->str, name, l + 1);
101
102         if (ioctl(kdbus, KDBUS_CMD_MEMFD_NEW, cmd) < 0)
103                 return -errno;
104
105         n = new0(struct sd_memfd, 1);
106         if (!n) {
107                 close_nointr_nofail(cmd->fd);
108                 return -ENOMEM;
109         }
110
111         n->fd = cmd->fd;
112         *m = n;
113         return 0;
114 }
115
116 _public_ int sd_memfd_make(int fd, sd_memfd **m) {
117         sd_memfd *n;
118         uint64_t sz;
119
120         assert_return(m, -EINVAL);
121         assert_return(fd >= 0, -EINVAL);
122
123         /* Check if this is a valid memfd */
124         if (ioctl(fd, KDBUS_CMD_MEMFD_SIZE_GET, &sz) < 0)
125                 return -ENOTTY;
126
127         n = new0(struct sd_memfd, 1);
128         if (!n)
129                 return -ENOMEM;
130
131         n->fd = fd;
132         *m = n;
133
134         return 0;
135 }
136
137 _public_ void sd_memfd_free(sd_memfd *m) {
138         if (!m)
139                 return;
140
141         if (m->f)
142                 fclose(m->f);
143         else
144                 close_nointr_nofail(m->fd);
145
146         free(m);
147 }
148
149 _public_ int sd_memfd_get_fd(sd_memfd *m) {
150         assert_return(m, -EINVAL);
151
152         return m->fd;
153 }
154
155 _public_ int sd_memfd_get_file(sd_memfd *m, FILE **f) {
156         assert_return(m, -EINVAL);
157         assert_return(f, -EINVAL);
158
159         if (!m->f) {
160                 m->f = fdopen(m->fd, "r+");
161                 if (!m->f)
162                         return -errno;
163         }
164
165         *f = m->f;
166         return 0;
167 }
168
169 _public_ int sd_memfd_dup_fd(sd_memfd *m) {
170         int fd;
171
172         assert_return(m, -EINVAL);
173
174         fd = fcntl(m->fd, F_DUPFD_CLOEXEC, 3);
175         if (fd < 0)
176                 return -errno;
177
178         return fd;
179 }
180
181 _public_ int sd_memfd_map(sd_memfd *m, uint64_t offset, size_t size, void **p) {
182         void *q;
183         int sealed;
184
185         assert_return(m, -EINVAL);
186         assert_return(size > 0, -EINVAL);
187         assert_return(p, -EINVAL);
188
189         sealed = sd_memfd_get_sealed(m);
190         if (sealed < 0)
191                 return sealed;
192
193         q = mmap(NULL, size, sealed ? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED, m->fd, offset);
194         if (q == MAP_FAILED)
195                 return -errno;
196
197         *p = q;
198         return 0;
199 }
200
201 _public_ int sd_memfd_set_sealed(sd_memfd *m, int b) {
202         int r;
203
204         assert_return(m, -EINVAL);
205
206         r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_SET, b);
207         if (r < 0)
208                 return -errno;
209
210         return 0;
211 }
212
213 _public_ int sd_memfd_get_sealed(sd_memfd *m) {
214         int r, b;
215
216         assert_return(m, -EINVAL);
217
218         r = ioctl(m->fd, KDBUS_CMD_MEMFD_SEAL_GET, &b);
219         if (r < 0)
220                 return -errno;
221
222         return !!b;
223 }
224
225 _public_ int sd_memfd_get_size(sd_memfd *m, uint64_t *sz) {
226         int r;
227
228         assert_return(m, -EINVAL);
229         assert_return(sz, -EINVAL);
230
231         r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_GET, sz);
232         if (r < 0)
233                 return -errno;
234
235         return r;
236 }
237
238 _public_ int sd_memfd_set_size(sd_memfd *m, uint64_t sz) {
239         int r;
240
241         assert_return(m, -EINVAL);
242
243         r = ioctl(m->fd, KDBUS_CMD_MEMFD_SIZE_SET, &sz);
244         if (r < 0)
245                 return -errno;
246
247         return r;
248 }
249
250 _public_ int sd_memfd_new_and_map(const char *name, sd_memfd **m, size_t sz, void **p) {
251         sd_memfd *n;
252         int r;
253
254         r = sd_memfd_new(name, &n);
255         if (r < 0)
256                 return r;
257
258         r = sd_memfd_set_size(n, sz);
259         if (r < 0) {
260                 sd_memfd_free(n);
261                 return r;
262         }
263
264         r = sd_memfd_map(n, 0, sz, p);
265         if (r < 0) {
266                 sd_memfd_free(n);
267                 return r;
268         }
269
270         *m = n;
271         return 0;
272 }
273
274 _public_ int sd_memfd_get_name(sd_memfd *m, char **name) {
275         char path[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int)], buf[FILENAME_MAX+1], *e;
276         const char *delim, *end;
277         _cleanup_free_ char *n = NULL;
278         ssize_t k;
279
280         assert_return(m, -EINVAL);
281         assert_return(name, -EINVAL);
282
283         sprintf(path, "/proc/self/fd/%i", m->fd);
284
285         k = readlink(path, buf, sizeof(buf));
286         if (k < 0)
287                 return -errno;
288
289         if ((size_t) k >= sizeof(buf))
290                 return -E2BIG;
291
292         buf[k] = 0;
293
294         delim = strstr(buf, ":[");
295         if (!delim)
296                 return -EIO;
297
298         delim = strchr(delim + 2, ':');
299         if (!delim)
300                 return -EIO;
301
302         delim++;
303
304         end = strchr(delim, ']');
305         if (!end)
306                 return -EIO;
307
308         n = strndup(delim, end - delim);
309         if (!n)
310                 return -ENOMEM;
311
312         e = sd_bus_label_unescape(n);
313         if (!e)
314                 return -ENOMEM;
315
316         *name = e;
317
318         return 0;
319 }