10 #include <sys/types.h>
14 #include <ccan/read_write_all/read_write_all.h>
15 #include <ccan/failtest/failtest_proto.h>
16 #include <ccan/failtest/failtest.h>
18 bool (*failtest_hook)(struct failtest_call *history, unsigned num)
19 = failtest_default_hook;
21 unsigned int failtest_timeout_ms = 20000;
32 struct write_info_hdr {
46 struct write_info_hdr hdr;
52 bool (*failtest_exit_check)(struct failtest_call *history, unsigned num);
54 static struct failtest_call *history = NULL;
55 static unsigned int history_num = 0;
56 static int control_fd = -1;
58 static struct write_info *writes = NULL;
59 static unsigned int writes_num = 0;
61 static struct write_info *child_writes = NULL;
62 static unsigned int child_writes_num = 0;
64 static struct fd_orig *fd_orig = NULL;
65 static unsigned int fd_orig_num = 0;
67 static const char info_to_arg[] = "mceoprw";
69 /* Dummy call used for failtest_undo wrappers. */
70 static struct failtest_call unrecorded_call;
72 static struct failtest_call *add_history_(enum failtest_call_type type,
78 /* NULL file is how we suppress failure. */
80 return &unrecorded_call;
82 history = realloc(history, (history_num + 1) * sizeof(*history));
83 history[history_num].type = type;
84 history[history_num].file = file;
85 history[history_num].line = line;
86 memcpy(&history[history_num].u, elem, elem_size);
87 return &history[history_num++];
90 #define add_history(type, file, line, elem) \
91 add_history_((type), (file), (line), (elem), sizeof(*(elem)))
93 static void save_fd_orig(int fd)
97 for (i = 0; i < fd_orig_num; i++)
98 if (fd_orig[i].fd == fd)
101 fd_orig = realloc(fd_orig, (fd_orig_num + 1) * sizeof(*fd_orig));
102 fd_orig[fd_orig_num].fd = fd;
103 fd_orig[fd_orig_num].dupped = false;
104 fd_orig[fd_orig_num].offset = lseek(fd, 0, SEEK_CUR);
105 fd_orig[fd_orig_num].size = lseek(fd, 0, SEEK_END);
106 lseek(fd, fd_orig[fd_orig_num].offset, SEEK_SET);
110 bool failtest_default_hook(struct failtest_call *history, unsigned num)
115 static bool read_write_info(int fd)
117 struct write_info_hdr hdr;
119 if (!read_all(fd, &hdr, sizeof(hdr)))
122 child_writes = realloc(child_writes,
123 (child_writes_num+1) * sizeof(child_writes[0]));
124 child_writes[child_writes_num].hdr = hdr;
125 child_writes[child_writes_num].data = malloc(hdr.len);
126 if (!read_all(fd, child_writes[child_writes_num].data, hdr.len))
133 static void print_reproduce(void)
137 printf("To reproduce: --failpath=");
138 for (i = 0; i < history_num; i++) {
140 printf("%c", toupper(info_to_arg[history[i].type]));
142 printf("%c", info_to_arg[history[i].type]);
147 static void tell_parent(enum info_type type)
149 if (control_fd != -1)
150 write_all(control_fd, &type, sizeof(type));
153 static void child_fail(const char *out, size_t outlen, const char *fmt, ...)
158 vfprintf(stderr, fmt, ap);
161 fprintf(stderr, "%.*s", (int)outlen, out);
163 tell_parent(FAILURE);
169 static void hand_down(int signal)
174 static bool should_fail(struct failtest_call *call)
177 int control[2], output[2];
178 enum info_type type = UNEXPECTED;
182 if (call == &unrecorded_call)
186 if (tolower(*failpath) != info_to_arg[call->type])
187 errx(1, "Failpath expected '%c' got '%c'\n",
188 info_to_arg[call->type], *failpath);
189 return isupper(*(failpath++));
192 if (!failtest_hook(history, history_num)) {
197 /* We're going to fail in the child. */
199 if (pipe(control) != 0 || pipe(output) != 0)
200 err(1, "opening pipe");
202 /* Prevent double-printing (in child and parent) */
206 err(1, "forking failed");
211 dup2(output[1], STDOUT_FILENO);
212 dup2(output[1], STDERR_FILENO);
213 if (output[1] != STDOUT_FILENO && output[1] != STDERR_FILENO)
215 control_fd = control[1];
219 signal(SIGUSR1, hand_down);
224 /* We grab output so we can display it; we grab writes so we
227 struct pollfd pfd[2];
230 pfd[0].fd = output[0];
231 pfd[0].events = POLLIN|POLLHUP;
232 pfd[1].fd = control[0];
233 pfd[1].events = POLLIN|POLLHUP;
236 ret = poll(pfd, 1, failtest_timeout_ms);
238 ret = poll(pfd, 2, failtest_timeout_ms);
243 if (pfd[0].revents & POLLIN) {
246 out = realloc(out, outlen + 8192);
247 len = read(output[0], out + outlen, 8192);
249 } else if (type != SUCCESS && (pfd[1].revents & POLLIN)) {
250 if (read_all(control[0], &type, sizeof(type))) {
252 if (!read_write_info(control[0]))
256 } else if (pfd[0].revents & POLLHUP) {
259 } while (type != FAILURE);
263 waitpid(child, &status, 0);
264 if (!WIFEXITED(status))
265 child_fail(out, outlen, "Killed by signal %u: ",
267 /* Child printed failure already, just pass up exit code. */
268 if (type == FAILURE) {
269 fprintf(stderr, "%.*s", (int)outlen, out);
271 exit(WEXITSTATUS(status) ? WEXITSTATUS(status) : 1);
273 if (WEXITSTATUS(status) != 0)
274 child_fail(out, outlen, "Exited with status %i: ",
275 WEXITSTATUS(status));
278 signal(SIGUSR1, SIG_DFL);
280 /* We continue onwards without failing. */
285 void *failtest_calloc(size_t nmemb, size_t size,
286 const char *file, unsigned line)
288 struct failtest_call *p;
289 struct calloc_call call;
292 p = add_history(FAILTEST_CALLOC, file, line, &call);
294 if (should_fail(p)) {
295 p->u.calloc.ret = NULL;
298 p->u.calloc.ret = calloc(nmemb, size);
301 return p->u.calloc.ret;
304 void *failtest_malloc(size_t size, const char *file, unsigned line)
306 struct failtest_call *p;
307 struct malloc_call call;
310 p = add_history(FAILTEST_MALLOC, file, line, &call);
311 if (should_fail(p)) {
312 p->u.calloc.ret = NULL;
315 p->u.calloc.ret = malloc(size);
318 return p->u.calloc.ret;
321 void *failtest_realloc(void *ptr, size_t size, const char *file, unsigned line)
323 struct failtest_call *p;
324 struct realloc_call call;
326 p = add_history(FAILTEST_REALLOC, file, line, &call);
328 /* FIXME: Try one child moving allocation, one not. */
329 if (should_fail(p)) {
330 p->u.realloc.ret = NULL;
333 p->u.realloc.ret = realloc(ptr, size);
336 return p->u.realloc.ret;
339 int failtest_open(const char *pathname, int flags,
340 const char *file, unsigned line, ...)
342 struct failtest_call *p;
343 struct open_call call;
345 call.pathname = strdup(pathname);
347 if (flags & O_CREAT) {
350 call.mode = va_arg(ap, mode_t);
353 p = add_history(FAILTEST_OPEN, file, line, &call);
354 /* Avoid memory leak! */
355 if (p == &unrecorded_call)
356 free((char *)call.pathname);
357 if (should_fail(p)) {
359 /* FIXME: Play with error codes? */
362 p->u.open.ret = open(pathname, flags, call.mode);
365 return p->u.open.ret;
368 int failtest_pipe(int pipefd[2], const char *file, unsigned line)
370 struct failtest_call *p;
371 struct pipe_call call;
373 p = add_history(FAILTEST_PIPE, file, line, &call);
374 if (should_fail(p)) {
376 /* FIXME: Play with error codes? */
379 p->u.pipe.ret = pipe(p->u.pipe.fds);
381 /* This causes valgrind to notice if they use pipefd[] after failure */
382 memcpy(pipefd, p->u.pipe.fds, sizeof(p->u.pipe.fds));
384 return p->u.pipe.ret;
387 ssize_t failtest_read(int fd, void *buf, size_t count,
388 const char *file, unsigned line)
390 struct failtest_call *p;
391 struct read_call call;
395 p = add_history(FAILTEST_READ, file, line, &call);
397 /* This is going to change seek offset, so save it. */
398 if (control_fd != -1)
401 /* FIXME: Try partial read returns. */
402 if (should_fail(p)) {
406 p->u.read.ret = read(fd, buf, count);
409 return p->u.read.ret;
412 static struct write_info *new_write(void)
414 writes = realloc(writes, (writes_num + 1) * sizeof(*writes));
415 return &writes[writes_num++];
418 ssize_t failtest_write(int fd, const void *buf, size_t count,
419 const char *file, unsigned line)
421 struct failtest_call *p;
422 struct write_call call;
428 p = add_history(FAILTEST_WRITE, file, line, &call);
430 offset = lseek(fd, 0, SEEK_CUR);
432 /* If we're a child, save contents and tell parent about write. */
433 if (control_fd != -1) {
434 struct write_info *winfo = new_write();
435 enum info_type type = WRITE;
439 winfo->hdr.len = count;
441 winfo->data = malloc(count);
442 memcpy(winfo->data, buf, count);
443 winfo->hdr.offset = offset;
444 if (winfo->hdr.offset != (off_t)-1) {
445 lseek(fd, offset, SEEK_SET);
446 winfo->olddata = malloc(count);
447 winfo->oldlen = read(fd, winfo->olddata, count);
448 if (winfo->oldlen == -1)
451 write_all(control_fd, &type, sizeof(type));
452 write_all(control_fd, &winfo->hdr, sizeof(winfo->hdr));
453 write_all(control_fd, winfo->data, count);
456 /* FIXME: Try partial write returns. */
457 if (should_fail(p)) {
461 /* FIXME: We assume same write order in parent and child */
462 if (child_writes_num != 0) {
463 if (child_writes[0].hdr.fd != fd)
464 errx(1, "Child wrote to fd %u, not %u?",
465 child_writes[0].hdr.fd, fd);
466 if (child_writes[0].hdr.offset != offset)
467 errx(1, "Child wrote to offset %zu, not %zu?",
468 (size_t)child_writes[0].hdr.offset,
470 if (child_writes[0].hdr.len != count)
471 errx(1, "Child wrote length %zu, not %zu?",
472 child_writes[0].hdr.len, count);
473 if (memcmp(child_writes[0].data, buf, count)) {
475 "Child wrote differently to"
476 " fd %u than we did!\n", fd);
478 free(child_writes[0].data);
480 memmove(&child_writes[0], &child_writes[1],
481 sizeof(child_writes[0]) * child_writes_num);
483 /* Is this is a socket or pipe, child wrote it
485 if (offset == (off_t)-1) {
486 p->u.write.ret = count;
488 return p->u.write.ret;
491 p->u.write.ret = write(fd, buf, count);
494 return p->u.write.ret;
497 /* We only trap this so we can dup fds in case we need to restore. */
498 int failtest_close(int fd)
503 for (i = 0; i < fd_orig_num; i++) {
504 if (fd_orig[i].fd == fd) {
505 fd_orig[i].fd = newfd = dup(fd);
506 fd_orig[i].dupped = true;
510 for (i = 0; i < writes_num; i++) {
511 if (writes[i].hdr.fd == fd)
512 writes[i].hdr.fd = newfd;
517 void failtest_init(int argc, char *argv[])
520 && strncmp(argv[1], "--failpath=", strlen("--failpath=")) == 0) {
521 failpath = argv[1] + strlen("--failpath=");
525 /* Free up memory, so valgrind doesn't report leaks. */
526 static void free_everything(void)
530 for (i = 0; i < writes_num; i++) {
531 free(writes[i].data);
532 if (writes[i].hdr.offset != (off_t)-1)
533 free(writes[i].olddata);
537 for (i = 0; i < history_num; i++) {
538 if (history[i].type == FAILTEST_OPEN)
539 free((char *)history[i].u.open.pathname);
544 void failtest_exit(int status)
548 if (control_fd == -1) {
553 if (failtest_exit_check) {
554 if (!failtest_exit_check(history, history_num))
555 child_fail(NULL, 0, "failtest_exit_check failed\n");
558 /* Restore any stuff we overwrote. */
559 for (i = 0; i < writes_num; i++) {
560 if (writes[i].hdr.offset == (off_t)-1)
562 if (writes[i].oldlen != 0) {
563 lseek(writes[i].hdr.fd, writes[i].hdr.offset,
565 write(writes[i].hdr.fd, writes[i].olddata,
570 /* Fix up fd offsets, restore sizes. */
571 for (i = 0; i < fd_orig_num; i++) {
572 lseek(fd_orig[i].fd, fd_orig[i].offset, SEEK_SET);
573 ftruncate(fd_orig[i].fd, fd_orig[i].size);
574 /* Free up any file descriptors we dup'ed. */
575 if (fd_orig[i].dupped)
576 close(fd_orig[i].fd);
580 tell_parent(SUCCESS);