1 | #include <mqueue.h>
|
---|
2 | #include <pthread.h>
|
---|
3 | #include <errno.h>
|
---|
4 | #include <sys/socket.h>
|
---|
5 | #include <signal.h>
|
---|
6 | #include <unistd.h>
|
---|
7 | #include "syscall.h"
|
---|
8 |
|
---|
9 | struct args {
|
---|
10 | pthread_barrier_t barrier;
|
---|
11 | int sock;
|
---|
12 | const struct sigevent *sev;
|
---|
13 | };
|
---|
14 |
|
---|
15 | static void *start(void *p)
|
---|
16 | {
|
---|
17 | struct args *args = p;
|
---|
18 | char buf[32];
|
---|
19 | ssize_t n;
|
---|
20 | int s = args->sock;
|
---|
21 | void (*func)(union sigval) = args->sev->sigev_notify_function;
|
---|
22 | union sigval val = args->sev->sigev_value;
|
---|
23 |
|
---|
24 | pthread_barrier_wait(&args->barrier);
|
---|
25 | n = recv(s, buf, sizeof(buf), MSG_NOSIGNAL|MSG_WAITALL);
|
---|
26 | close(s);
|
---|
27 | if (n==sizeof buf && buf[sizeof buf - 1] == 1)
|
---|
28 | func(val);
|
---|
29 | return 0;
|
---|
30 | }
|
---|
31 |
|
---|
32 | int mq_notify(mqd_t mqd, const struct sigevent *sev)
|
---|
33 | {
|
---|
34 | struct args args = { .sev = sev };
|
---|
35 | pthread_attr_t attr;
|
---|
36 | pthread_t td;
|
---|
37 | int s;
|
---|
38 | struct sigevent sev2;
|
---|
39 | static const char zeros[32];
|
---|
40 |
|
---|
41 | if (!sev || sev->sigev_notify != SIGEV_THREAD)
|
---|
42 | return syscall(SYS_mq_notify, mqd, sev);
|
---|
43 |
|
---|
44 | s = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, 0);
|
---|
45 | if (s < 0) return -1;
|
---|
46 | args.sock = s;
|
---|
47 |
|
---|
48 | if (sev->sigev_notify_attributes) attr = *sev->sigev_notify_attributes;
|
---|
49 | else pthread_attr_init(&attr);
|
---|
50 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
---|
51 | pthread_barrier_init(&args.barrier, 0, 2);
|
---|
52 |
|
---|
53 | if (pthread_create(&td, &attr, start, &args)) {
|
---|
54 | __syscall(SYS_close, s);
|
---|
55 | errno = EAGAIN;
|
---|
56 | return -1;
|
---|
57 | }
|
---|
58 |
|
---|
59 | pthread_barrier_wait(&args.barrier);
|
---|
60 | pthread_barrier_destroy(&args.barrier);
|
---|
61 |
|
---|
62 | sev2.sigev_notify = SIGEV_THREAD;
|
---|
63 | sev2.sigev_signo = s;
|
---|
64 | sev2.sigev_value.sival_ptr = (void *)&zeros;
|
---|
65 |
|
---|
66 | if (syscall(SYS_mq_notify, mqd, &sev2) < 0) {
|
---|
67 | pthread_cancel(td);
|
---|
68 | __syscall(SYS_close, s);
|
---|
69 | return -1;
|
---|
70 | }
|
---|
71 |
|
---|
72 | return 0;
|
---|
73 | }
|
---|