|  | /* | 
|  | * Check that fault injection works properly. | 
|  | * | 
|  | * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org> | 
|  | * Copyright (c) 2016-2018 The strace developers. | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. The name of the author may not be used to endorse or promote products | 
|  | *    derived from this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include "tests.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <limits.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/uio.h> | 
|  | #include <sys/wait.h> | 
|  |  | 
|  | static int exp_fd; | 
|  | static int got_fd; | 
|  | static int out_fd; | 
|  |  | 
|  | #define DEFAULT_ERRNO ENOSYS | 
|  |  | 
|  | static const char *errstr; | 
|  | static int is_raw, err, first, step, iter, try; | 
|  |  | 
|  | static void | 
|  | invoke(int fail) | 
|  | { | 
|  | static char buf[sizeof(int) * 3 + 3]; | 
|  | const struct iovec io = { | 
|  | .iov_base = buf, | 
|  | .iov_len = sprintf(buf, "%d.", ++try) | 
|  | }; | 
|  | int rc; | 
|  |  | 
|  | if (!fail) { | 
|  | rc = write(exp_fd, io.iov_base, io.iov_len); | 
|  | if (rc != (int) io.iov_len) | 
|  | perror_msg_and_fail("write"); | 
|  | } | 
|  |  | 
|  | errno = 0; | 
|  | rc = writev(got_fd, &io, 1); | 
|  |  | 
|  | if (fail) { | 
|  | if (!(rc == -1 && errno == err)) | 
|  | perror_msg_and_fail("expected errno %d" | 
|  | ", got rc == %d, errno == %d", | 
|  | err, rc, errno); | 
|  |  | 
|  | if (is_raw) | 
|  | tprintf("writev(%#x, %p, 0x1)" | 
|  | " = -1 %s (%m) (INJECTED)\n", | 
|  | got_fd, &io, errstr); | 
|  | else | 
|  | tprintf("writev(%d, [{iov_base=\"%s\", iov_len=%d}], 1)" | 
|  | " = -1 %s (%m) (INJECTED)\n", | 
|  | got_fd, buf, (int) io.iov_len, errstr); | 
|  | } else { | 
|  | if (rc != (int) io.iov_len) | 
|  | perror_msg_and_fail("expected %d" | 
|  | ", got rc == %d, errno == %d", | 
|  | (int) io.iov_len, rc, errno); | 
|  |  | 
|  | if (is_raw) | 
|  | tprintf("writev(%#x, %p, 0x1) = %#x\n", | 
|  | got_fd, &io, rc); | 
|  | else | 
|  | tprintf("writev(%d, [{iov_base=\"%s\", iov_len=%d}], 1)" | 
|  | " = %d\n", | 
|  | got_fd, buf, (int) io.iov_len, | 
|  | (int) io.iov_len); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | open_file(const char *prefix, int proc) | 
|  | { | 
|  | static const int open_flags = O_WRONLY | O_TRUNC | O_CREAT; | 
|  | static char path[PATH_MAX + 1]; | 
|  |  | 
|  | snprintf(path, sizeof(path), "%s.%d", prefix, proc); | 
|  |  | 
|  | int fd = open(path, open_flags, 0600); | 
|  | if (fd < 0) | 
|  | perror_msg_and_fail("open: %s", path); | 
|  |  | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | int | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | assert(argc == 11); | 
|  |  | 
|  | is_raw = !strcmp("raw", argv[1]); | 
|  |  | 
|  | errstr = argv[2]; | 
|  | err = atoi(errstr); | 
|  | assert(err >= 0); | 
|  |  | 
|  | if (!err) { | 
|  | if (!*errstr) | 
|  | err = DEFAULT_ERRNO; | 
|  | else if (!strcasecmp(errstr, "EINVAL")) | 
|  | err = EINVAL; | 
|  | else | 
|  | err = ENOSYS; | 
|  | } | 
|  |  | 
|  | errno = err; | 
|  | errstr = errno2name(); | 
|  |  | 
|  | first = atoi(argv[3]); | 
|  | step = atoi(argv[4]); | 
|  | iter = atoi(argv[5]); | 
|  | int num_procs = atoi(argv[6]); | 
|  | char *exp_prefix = argv[7]; | 
|  | char *got_prefix = argv[8]; | 
|  | char *out_prefix = argv[9]; | 
|  | char *pid_prefix = argv[10]; | 
|  |  | 
|  | assert(first > 0); | 
|  | assert(step >= 0); | 
|  | assert(num_procs > 0); | 
|  |  | 
|  | int proc; | 
|  | for (proc = 0; proc < num_procs; ++proc) { | 
|  | int ret = fork(); | 
|  |  | 
|  | if (ret < 0) | 
|  | perror_msg_and_fail("fork"); | 
|  |  | 
|  | if (ret > 0) { | 
|  | int pidfd = open_file(pid_prefix, proc); | 
|  |  | 
|  | char pidstr[sizeof(ret) * 3]; | 
|  | int len = snprintf(pidstr, sizeof(pidstr), "%d", ret); | 
|  | assert(len > 0 && len < (int) sizeof(pidstr)); | 
|  | assert(write(pidfd, pidstr, len) == len); | 
|  |  | 
|  | close(pidfd); | 
|  |  | 
|  | continue; | 
|  | } | 
|  |  | 
|  | tprintf("%s", ""); | 
|  |  | 
|  | exp_fd = open_file(exp_prefix, proc); | 
|  | got_fd = open_file(got_prefix, proc); | 
|  | out_fd = open_file(out_prefix, proc); | 
|  |  | 
|  | /* This magic forces tprintf to write where we want it. */ | 
|  | dup2(out_fd, 3); | 
|  |  | 
|  | int i; | 
|  | for (i = 1; i <= iter; ++i) { | 
|  | int fail = 0; | 
|  | if (first > 0) { | 
|  | --first; | 
|  | if (first == 0) { | 
|  | fail = 1; | 
|  | first = step; | 
|  | } | 
|  | } | 
|  | invoke(fail); | 
|  | } | 
|  |  | 
|  | tprintf("%s\n", "+++ exited with 0 +++"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | for (proc = 0; proc < num_procs; ++proc) { | 
|  | int status; | 
|  | int ret = wait(&status); | 
|  | if (ret <= 0) | 
|  | perror_msg_and_fail("wait %d", proc); | 
|  | if (status) | 
|  | error_msg_and_fail("wait: pid=%d status=%d", | 
|  | ret, status); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |