The Pedigree Project 0.1
uts-namespace-contract-test/fixture.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <poll.h>
4#include <signal.h>
5#include <string.h>
6#include <time.h>
7#include <unistd.h>
8
9#include "contract.h"
10#include <sys/socket.h>
11#include <sys/stat.h>
12#include <sys/utsname.h>
13#include <sys/wait.h>
14
15size_t ns_page;
16
17int64_t ns_now(void) {
18 struct timespec value;
19 return clock_gettime(CLOCK_MONOTONIC, &value)
20 ? -1
21 : (int64_t)value.tv_sec * 1000000000 + value.tv_nsec;
22}
23
24int ns_reap(pid_t pid, int milliseconds) {
25 int64_t start = ns_now(), now, end = start + (int64_t)milliseconds * 1000000;
26 while (start >= 0 && (now = ns_now()) >= 0 && now < end) {
27 int status;
28 pid_t result = waitpid(pid, &status, WNOHANG);
29 if (result == pid)
30 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
31 if (result < 0 && errno != EINTR)
32 return -1;
33 struct timespec pause = {0, 5000000};
34 nanosleep(&pause, NULL);
35 }
36 kill(pid, SIGKILL);
37 while (waitpid(pid, NULL, 0) < 0 && errno == EINTR) {
38 }
39 return -1;
40}
41
42int ns_read(int fd, void* buffer, size_t length) {
43 unsigned char* bytes = buffer;
44 while (length) {
45 struct pollfd ready = {.fd = fd, .events = POLLIN};
46 int result;
47 do
48 result = poll(&ready, 1, 10000);
49 while (result < 0 && errno == EINTR);
50 if (result <= 0)
51 return -1;
52 ssize_t count = read(fd, bytes, length);
53 if (count < 0 && errno == EINTR)
54 continue;
55 if (count <= 0)
56 return -1;
57 bytes += count;
58 length -= count;
59 }
60 return 0;
61}
62
63int ns_write(int fd, const void* buffer, size_t length) {
64 const unsigned char* bytes = buffer;
65 while (length) {
66 ssize_t count = write(fd, bytes, length);
67 if (count < 0 && errno == EINTR)
68 continue;
69 if (count <= 0)
70 return -1;
71 bytes += count;
72 length -= count;
73 }
74 return 0;
75}
76int ns_send(int fd, char value) {
77 return ns_write(fd, &value, 1);
78}
79int ns_receive(int fd, char value) {
80 char actual;
81 return ns_read(fd, &actual, 1) || actual != value ? -1 : 0;
82}
83
84int ns_spawn(struct ns_peer* peer, int (*body)(int, int, void*), void* argument) {
85 int command[2], report[2];
86 if (pipe(command))
87 return -1;
88 if (pipe(report)) {
89 close(command[0]);
90 close(command[1]);
91 return -1;
92 }
93 pid_t pid = fork();
94 if (!pid) {
95 close(command[1]);
96 close(report[0]);
97 alarm(30);
98 int result = body(command[0], report[1], argument);
99 fflush(stdout);
100 fflush(stderr);
101 _exit(result ? 1 : 0);
102 }
103 close(command[0]);
104 close(report[1]);
105 if (pid < 0) {
106 close(command[1]);
107 close(report[0]);
108 return -1;
109 }
110 *peer = (struct ns_peer){pid, command[1], report[0]};
111 return 0;
112}
113
114int ns_join(struct ns_peer* peer) {
115 int result = ns_reap(peer->pid, 10000);
116 if (result)
117 fprintf(stderr, "namespace child pid=%d status=%d\n", peer->pid, result);
118 peer->pid = -1;
119 ns_cleanup(peer);
120 return result;
121}
122void ns_cleanup(struct ns_peer* peer) {
123 if (peer->pid > 0) {
124 kill(peer->pid, SIGKILL);
125 ns_reap(peer->pid, 1000);
126 }
127 if (peer->command >= 0)
128 close(peer->command);
129 if (peer->report >= 0)
130 close(peer->report);
131 *peer = (struct ns_peer)NS_PEER_INITIALIZER;
132}
133
134int ns_fd_identity(int fd, struct ns_identity* identity) {
135 struct stat status;
136 if (fstat(fd, &status) || !status.st_ino)
137 return -1;
138 *identity = (struct ns_identity){status.st_dev, status.st_ino};
139 return 0;
140}
141int ns_path_identity(const char* path, struct ns_identity* identity) {
142 int fd = open(path, O_RDONLY | O_CLOEXEC);
143 if (fd < 0)
144 return -1;
145 int result = ns_fd_identity(fd, identity);
146 close(fd);
147 return result;
148}
149int ns_same(struct ns_identity left, struct ns_identity right) {
150 return left.device == right.device && left.inode == right.inode;
151}
152int ns_set(const char* host, const char* domain) {
153 return sethostname(host, strlen(host)) || setdomainname(domain, strlen(domain));
154}
155int ns_expect(const char* host, const char* domain) {
156 struct utsname value;
157 if (uname(&value))
158 return -1;
159 if (!strcmp(value.nodename, host) && !strcmp(value.domainname, domain))
160 return 0;
161 fprintf(stderr, "namespace names expected=%s/%s actual=%s/%s\n", host, domain, value.nodename,
162 value.domainname);
163 return -1;
164}
165
166int ns_send_fd(int socket, int fd) {
167 char byte = 'N';
168 struct iovec vector = {&byte, 1};
169 union {
170 struct cmsghdr align;
171 char bytes[CMSG_SPACE(sizeof(int))];
172 } control = {0};
173 struct msghdr message = {.msg_iov = &vector,
174 .msg_iovlen = 1,
175 .msg_control = control.bytes,
176 .msg_controllen = sizeof(control)};
177 struct cmsghdr* header = CMSG_FIRSTHDR(&message);
178 header->cmsg_level = SOL_SOCKET;
179 header->cmsg_type = SCM_RIGHTS;
180 header->cmsg_len = CMSG_LEN(sizeof(int));
181 memcpy(CMSG_DATA(header), &fd, sizeof(fd));
182 return sendmsg(socket, &message, 0) == 1 ? 0 : -1;
183}
184int ns_receive_fd(int socket) {
185 char byte;
186 struct iovec vector = {&byte, 1};
187 union {
188 struct cmsghdr align;
189 char bytes[CMSG_SPACE(sizeof(int))];
190 } control = {0};
191 struct msghdr message = {.msg_iov = &vector,
192 .msg_iovlen = 1,
193 .msg_control = control.bytes,
194 .msg_controllen = sizeof(control)};
195 if (recvmsg(socket, &message, 0) != 1 || byte != 'N' || (message.msg_flags & MSG_CTRUNC))
196 return -1;
197 struct cmsghdr* header = CMSG_FIRSTHDR(&message);
198 if (!header || header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS ||
199 header->cmsg_len != CMSG_LEN(sizeof(int)))
200 return -1;
201 int fd;
202 memcpy(&fd, CMSG_DATA(header), sizeof(fd));
203 return fd;
204}