]> git.ozlabs.org Git - ccan/blob - ccan/antithread/antithread.c
Fix new glibc warnings about warn_unused_result.
[ccan] / ccan / antithread / antithread.c
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <stdbool.h>
5 #include <string.h>
6 #include <sys/mman.h>
7 #include <sys/types.h>
8 #include <sys/wait.h>
9 #include <errno.h>
10 #include <err.h>
11 #include "antithread.h"
12 #include <ccan/noerr/noerr.h>
13 #include <ccan/talloc/talloc.h>
14 #include <ccan/read_write_all/read_write_all.h>
15 #include <ccan/alloc/alloc.h>
16
17 /* FIXME: Valgrind support should be possible for some cases.  Tricky
18  * case is where another process allocates for you, but at worst we
19  * could reset what is valid and what isn't on every entry into the
20  * library or something. */
21
22 struct at_pool
23 {
24         const void *ctx;
25         void *pool;
26         unsigned long poolsize;
27         int fd;
28         int parent_rfd, parent_wfd;
29 };
30
31 struct athread
32 {
33         pid_t pid;
34         int rfd, wfd;
35 };
36
37 /* FIXME: Better locking through futexes. */
38 static void lock(int fd, unsigned long off)
39 {
40         struct flock fl;
41
42         fl.l_type = F_WRLCK;
43         fl.l_whence = SEEK_SET;
44         fl.l_start = off;
45         fl.l_len = 1;
46
47         while (fcntl(fd, F_SETLKW, &fl) < 0) {
48                 if (errno != EINTR)
49                         err(1, "Failure locking antithread file");
50         }
51 }
52
53 static void unlock(int fd, unsigned long off)
54 {
55         struct flock fl;
56         int serrno = errno;
57
58         fl.l_type = F_UNLCK;
59         fl.l_whence = SEEK_SET;
60         fl.l_start = off;
61         fl.l_len = 1;
62
63         fcntl(fd, F_SETLK, &fl);
64         errno = serrno;
65 }
66
67 static void *at_realloc(const void *parent, void *ptr, size_t size)
68 {
69         struct at_pool *p = talloc_find_parent_bytype(parent, struct at_pool);
70         /* FIXME: realloc in ccan/alloc? */
71         void *new;
72
73         lock(p->fd, 0);
74         if (size == 0) {
75                 alloc_free(p->pool, p->poolsize, ptr);
76                 new = NULL;
77         } else if (ptr == NULL) {
78                 /* FIXME: Alignment */
79                 new = alloc_get(p->pool, p->poolsize, size, 16);
80         } else {
81                 if (size <= alloc_size(p->pool, p->poolsize, ptr))
82                         new = ptr;
83                 else {
84                         new = alloc_get(p->pool, p->poolsize, size, 16);
85                         if (new) {
86                                 memcpy(new, ptr,
87                                        alloc_size(p->pool, p->poolsize, ptr));
88                                 alloc_free(p->pool, p->poolsize, ptr);
89                         }
90                 }
91         }
92         unlock(p->fd, 0);
93         return new;
94 }
95
96 /* We add 16MB to size.  This compensates for address randomization. */
97 #define PADDING (16 * 1024 * 1024)
98
99 /* Create a new sharable pool. */
100 struct at_pool *at_pool(unsigned long size)
101 {
102         int fd;
103         struct at_pool *p;
104         FILE *f;
105
106         /* FIXME: How much should we actually add for overhead?. */
107         size += 32 * getpagesize();
108
109         /* Round up to whole pages. */
110         size = (size + getpagesize()-1) & ~(getpagesize()-1);
111
112         f = tmpfile();
113         if (!f)
114                 return NULL;
115
116         fd = dup(fileno(f));
117         fclose_noerr(f);
118
119         if (fd < 0)
120                 return NULL;
121
122         if (ftruncate(fd, size + PADDING) != 0)
123                 goto fail_close;
124
125         p = talloc(NULL, struct at_pool);
126         if (!p)
127                 goto fail_close;
128
129         /* First map gets a nice big area. */
130         p->pool = mmap(NULL, size+PADDING, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
131                        0);
132         if (p->pool == MAP_FAILED)
133                 goto fail_free;
134
135         /* Then we remap into the middle of it. */
136         munmap(p->pool, size+PADDING);
137         p->pool = mmap(p->pool + PADDING/2, size, PROT_READ|PROT_WRITE,
138                        MAP_SHARED, fd, 0);
139         if (p->pool == MAP_FAILED)
140                 goto fail_free;
141
142         /* FIXME: Destructor? */
143         p->fd = fd;
144         p->poolsize = size;
145         p->parent_rfd = p->parent_wfd = -1;
146         alloc_init(p->pool, p->poolsize);
147
148         p->ctx = talloc_add_external(p, at_realloc);
149         if (!p->ctx)
150                 goto fail_unmap;
151
152         return p;
153
154 fail_unmap:
155         munmap(p->pool, size);
156 fail_free:
157         talloc_free(p);
158 fail_close:
159         close_noerr(fd);
160         return NULL;
161 }
162
163 /* Talloc off this to allocate from within the pool. */
164 const void *at_pool_ctx(struct at_pool *atp)
165 {
166         return atp->ctx;
167 }
168
169 static int cant_destroy_self(struct athread *at)
170 {
171         /* Perhaps this means we want to detach, but it doesn't really
172          * make sense. */
173         abort();
174         return 0;
175 }
176
177 static int destroy_at(struct athread *at)
178 {
179         /* If it is already a zombie, this is harmless. */
180         kill(at->pid, SIGTERM);
181
182         close(at->rfd);
183         close(at->wfd);
184
185         /* FIXME: Should we do SIGKILL if process doesn't exit soon? */
186         if (waitpid(at->pid, NULL, 0) != at->pid)
187                 err(1, "Waiting for athread %p (pid %u)", at, at->pid);
188
189         return 0;
190 }
191
192 /* Sets up thread and forks it.  NULL on error. */
193 static struct athread *fork_thread(struct at_pool *pool)
194 {
195         int p2c[2], c2p[2];
196         struct athread *at;
197
198         /* You can't already be a child of this pool. */
199         if (pool->parent_rfd != -1)
200                 errx(1, "Can't create antithread on this pool: we're one");
201
202         /* We don't want this allocated *in* the pool. */
203         at = talloc_steal(pool, talloc(NULL, struct athread));
204
205         if (pipe(p2c) != 0)
206                 goto free;
207
208         if (pipe(c2p) != 0)
209                 goto close_p2c;
210
211         at->pid = fork();
212         if (at->pid == -1)
213                 goto close_c2p;
214
215         if (at->pid == 0) {
216                 /* Child */
217                 close(c2p[0]);
218                 close(p2c[1]);
219                 pool->parent_rfd = p2c[0];
220                 pool->parent_wfd = c2p[1];
221                 talloc_set_destructor(at, cant_destroy_self);
222         } else {
223                 /* Parent */
224                 close(c2p[1]);
225                 close(p2c[0]);
226                 at->rfd = c2p[0];
227                 at->wfd = p2c[1];
228                 talloc_set_destructor(at, destroy_at);
229         }
230
231         return at;
232 close_c2p:
233         close_noerr(c2p[0]);
234         close_noerr(c2p[1]);
235 close_p2c:
236         close_noerr(p2c[0]);
237         close_noerr(p2c[1]);
238 free:
239         talloc_free(at);
240         return NULL;
241 }
242
243 /* Creating an antithread via fork() */
244 struct athread *_at_run(struct at_pool *pool,
245                         void *(*fn)(struct at_pool *, void *),
246                         void *obj)
247 {
248         struct athread *at;
249
250         at = fork_thread(pool);
251         if (!at)
252                 return NULL;
253
254         if (at->pid == 0) {
255                 /* Child */
256                 at_tell_parent(pool, fn(pool, obj));
257                 exit(0);
258         }
259         /* Parent */
260         return at;
261 }
262
263 static unsigned int num_args(char *const argv[])
264 {
265         unsigned int i;
266
267         for (i = 0; argv[i]; i++);
268         return i;
269 }
270
271 /* Fork and execvp, with added arguments for child to grab. */
272 struct athread *at_spawn(struct at_pool *pool, void *arg, char *cmdline[])
273 {
274         struct athread *at;
275         int err;
276
277         at = fork_thread(pool);
278         if (!at)
279                 return NULL;
280
281         if (at->pid == 0) {
282                 /* child */
283                 char *argv[num_args(cmdline) + 2];
284                 argv[0] = cmdline[0];
285                 argv[1] = talloc_asprintf(NULL, "AT:%p/%lu/%i/%i/%i/%p",
286                                           pool->pool, pool->poolsize,
287                                           pool->fd, pool->parent_rfd,
288                                           pool->parent_wfd, arg);
289                 /* Copy including NULL terminator. */
290                 memcpy(&argv[2], &cmdline[1], num_args(cmdline)*sizeof(char *));
291                 execvp(argv[0], argv);
292
293                 err = errno;
294                 write_all(pool->parent_wfd, &err, sizeof(err));
295                 exit(1);
296         }
297
298         /* Child should always write an error code (or 0). */
299         if (read(at->rfd, &err, sizeof(err)) != sizeof(err)) {
300                 errno = ECHILD;
301                 talloc_free(at);
302                 return NULL;
303         }
304         if (err != 0) {
305                 errno = err;
306                 talloc_free(at);
307                 return NULL;
308         }
309         return at;
310 }
311
312 /* The fd to poll on */
313 int at_fd(struct athread *at)
314 {
315         return at->rfd;
316 }
317
318 /* What's the antithread saying?  Blocks if fd not ready. */
319 void *at_read(struct athread *at)
320 {
321         void *ret;
322
323         switch (read(at->rfd, &ret, sizeof(ret))) {
324         case -1:
325                 err(1, "Reading from athread %p (pid %u)", at, at->pid);
326         case 0:
327                 /* Thread died. */
328                 return NULL;
329         case sizeof(ret):
330                 return ret;
331         default:
332                 /* Should never happen. */
333                 err(1, "Short read from athread %p (pid %u)", at, at->pid);
334         }
335 }
336
337 /* Say something to a child. */
338 void at_tell(struct athread *at, const void *status)
339 {
340         if (write(at->wfd, &status, sizeof(status)) != sizeof(status))
341                 err(1, "Failure writing to athread %p (pid %u)", at, at->pid);
342 }
343
344 /* For child to grab arguments from command line (removes them) */
345 struct at_pool *at_get_pool(int *argc, char *argv[], void **arg)
346 {
347         struct at_pool *p = talloc(NULL, struct at_pool);
348         void *map;
349         int err;
350
351         if (!argv[1]) {
352                 errno = EINVAL;
353                 goto fail;
354         }
355
356         /* If they don't care, use dummy value. */
357         if (arg == NULL)
358                 arg = &map;
359
360         if (sscanf(argv[1], "AT:%p/%lu/%i/%i/%i/%p", 
361                    &p->pool, &p->poolsize, &p->fd,
362                    &p->parent_rfd, &p->parent_wfd, arg) != 6) {
363                 errno = EINVAL;
364                 goto fail;
365         }
366
367         /* FIXME: To try to adjust for address space randomization, we
368          * could re-exec a few times. */
369         map = mmap(p->pool, p->poolsize, PROT_READ|PROT_WRITE, MAP_SHARED,
370                    p->fd, 0);
371         if (map != p->pool) {
372                 fprintf(stderr, "Mapping %lu bytes @%p gave %p\n",
373                         p->poolsize, p->pool, map);
374                 errno = ENOMEM;
375                 goto fail;
376         }
377
378         p->ctx = talloc_add_external(p, at_realloc);
379         if (!p->ctx)
380                 goto fail;
381
382         /* Tell parent we're good. */
383         err = 0;
384         if (write(p->parent_wfd, &err, sizeof(err)) != sizeof(err)) {
385                 errno = EBADF;
386                 goto fail;
387         }
388
389         /* Delete AT arg. */
390         memmove(&argv[1], &argv[2], --(*argc));
391         return p;
392
393 fail:
394         /* FIXME: cleanup properly. */
395         talloc_free(p);
396         return NULL;
397 }
398
399 /* Say something to our parent (async). */
400 void at_tell_parent(struct at_pool *pool, const void *status)
401 {
402         if (pool->parent_wfd == -1)
403                 errx(1, "This process is not an antithread of this pool");
404
405         if (write(pool->parent_wfd, &status, sizeof(status)) != sizeof(status))
406                 err(1, "Failure writing to parent");
407 }
408
409 /* What's the parent saying?  Blocks if fd not ready. */
410 void *at_read_parent(struct at_pool *pool)
411 {
412         void *ret;
413
414         if (pool->parent_rfd == -1)
415                 errx(1, "This process is not an antithread of this pool");
416
417         switch (read(pool->parent_rfd, &ret, sizeof(ret))) {
418         case -1:
419                 err(1, "Reading from parent");
420         case 0:
421                 /* Parent died. */
422                 return NULL;
423         case sizeof(ret):
424                 return ret;
425         default:
426                 /* Should never happen. */
427                 err(1, "Short read from parent");
428         }
429 }
430
431 /* The fd to poll on */
432 int at_parent_fd(struct at_pool *pool)
433 {
434         if (pool->parent_rfd == -1)
435                 errx(1, "This process is not an antithread of this pool");
436
437         return pool->parent_rfd;
438 }
439
440 /* FIXME: Futexme. */
441 void at_lock(void *obj)
442 {
443         struct at_pool *p = talloc_find_parent_bytype(obj, struct at_pool);
444 #if 0
445         unsigned int *l;
446
447         /* This isn't required yet, but ensures it's a talloc ptr */
448         l = talloc_lock_ptr(obj);
449 #endif
450
451         lock(p->fd, (char *)obj - (char *)p->pool);
452
453 #if 0
454         if (*l)
455                 errx(1, "Object %p was already locked (something died?)", obj);
456         *l = 1;
457 #endif
458 }
459
460 void at_unlock(void *obj)
461 {
462         struct at_pool *p = talloc_find_parent_bytype(obj, struct at_pool);
463 #if 0
464         unsigned int *l;
465
466         l = talloc_lock_ptr(obj);
467         if (!*l)
468                 errx(1, "Object %p was already unlocked", obj);
469         *l = 0;
470 #endif
471         unlock(p->fd, (char *)obj - (char *)p->pool);
472 }
473
474 void at_lock_all(struct at_pool *p)
475 {
476         lock(p->fd, 0);
477 }
478         
479 void at_unlock_all(struct at_pool *p)
480 {
481         unlock(p->fd, 0);
482 }