diff options
Diffstat (limited to 'tests/helpers/test_mkfds.c')
-rw-r--r-- | tests/helpers/test_mkfds.c | 1900 |
1 files changed, 1858 insertions, 42 deletions
diff --git a/tests/helpers/test_mkfds.c b/tests/helpers/test_mkfds.c index 194c175..39427ba 100644 --- a/tests/helpers/test_mkfds.c +++ b/tests/helpers/test_mkfds.c @@ -17,49 +17,66 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "c.h" +#include "xalloc.h" +#include "test_mkfds.h" +#include "exitcodes.h" + #include <arpa/inet.h> #include <ctype.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <sys/file.h> #include <getopt.h> +#include <linux/bpf.h> #include <linux/if_ether.h> #include <linux/if_packet.h> +#include <linux/if_tun.h> #include <linux/netlink.h> +#include <linux/sock_diag.h> +# include <linux/unix_diag.h> /* for UNIX domain sockets */ #include <linux/sockios.h> /* SIOCGSKNS */ +#include <mqueue.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/tcp.h> +#include <poll.h> #include <sched.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> #include <sys/inotify.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/prctl.h> #include <sys/select.h> +#include <sys/signalfd.h> #include <sys/socket.h> +#include <sys/shm.h> #include <sys/syscall.h> +#include <sys/timerfd.h> #include <sys/types.h> #include <sys/un.h> #include <sys/user.h> +#include <sys/wait.h> +#include <time.h> #include <unistd.h> -#include "c.h" -#include "xalloc.h" - -#define EXIT_ENOSYS 17 #define EXIT_EPERM 18 #define EXIT_ENOPROTOOPT 19 #define EXIT_EPROTONOSUPPORT 20 -#define EXIT_EACCESS 21 +#define EXIT_EACCES 21 +#define EXIT_ENOENT 22 #define _U_ __attribute__((__unused__)) static int pidfd_open(pid_t pid, unsigned int flags); +static void do_nothing(int signum _U_); static void __attribute__((__noreturn__)) usage(FILE *out, int status) { @@ -67,11 +84,15 @@ static void __attribute__((__noreturn__)) usage(FILE *out, int status) fprintf(out, " %s [options] FACTORY FD... [PARAM=VAL...]\n", program_invocation_short_name); fputs("\nOptions:\n", out); + fputs(" -a, --is-available <factory> exit 0 if the factory is available\n", out); fputs(" -l, --list list available file descriptor factories and exit\n", out); fputs(" -I, --parameters <factory> list parameters the factory takes\n", out); fputs(" -r, --comm <name> rename self\n", out); fputs(" -q, --quiet don't print pid(s)\n", out); + fputs(" -X, --dont-monitor-stdin don't monitor stdin when pausing\n", out); fputs(" -c, --dont-pause don't pause after making fd(s)\n", out); + fputs(" -w, --wait-with <multiplexer> use MULTIPLEXER for waiting events\n", out); + fputs(" -W, --multiplexers list multiplexers\n", out); fputs("\n", out); fputs("Examples:\n", out); @@ -289,7 +310,7 @@ static struct arg decode_arg(const char *pname, v = NULL; } } - arg.v = ptype_classes [p->type].read (v, &p->defv); + arg.v = ptype_classes [p->type].read(v, &p->defv); arg.free = ptype_classes [p->type].free; return arg; } @@ -299,22 +320,17 @@ static void free_arg(struct arg *arg) arg->free(arg->v); } -struct fdesc { - int fd; - void (*close)(int, void *); - void *data; -}; - struct factory { const char *name; /* [-a-zA-Z0-9_]+ */ const char *desc; bool priv; /* the root privilege is needed to make fd(s) */ -#define MAX_N 5 +#define MAX_N 13 int N; /* the number of fds this factory makes */ int EX_N; /* fds made optionally */ + int EX_R; /* the number of extra words printed to stdout. */ void *(*make)(const struct factory *, struct fdesc[], int, char **); void (*free)(const struct factory *, void *); - void (*report)(const struct factory *, void *, FILE *); + void (*report)(const struct factory *, int, void *, FILE *); const struct parameter * params; }; @@ -323,11 +339,20 @@ static void close_fdesc(int fd, void *data _U_) close(fd); } +volatile ssize_t unused_result_ok; +static void abort_with_child_death_message(int signum _U_) +{ + const char msg[] = "the child process exits unexpectedly"; + unused_result_ok = write(2, msg, sizeof(msg)); + _exit(EXIT_FAILURE); +} + static void *open_ro_regular_file(const struct factory *factory, struct fdesc fdescs[], int argc, char ** argv) { struct arg file = decode_arg("file", factory->params, argc, argv); struct arg offset = decode_arg("offset", factory->params, argc, argv); + struct arg lease_r = decode_arg("read-lease", factory->params, argc, argv); int fd = open(ARG_STRING(file), O_RDONLY); if (fd < 0) @@ -344,6 +369,16 @@ static void *open_ro_regular_file(const struct factory *factory, struct fdesc fd } free_arg(&offset); + if (ARG_BOOLEAN(lease_r)) { + if (fcntl(fd, F_SETLEASE, F_RDLCK) < 0) { + int e = errno; + close(fd); + errno = e; + err(EXIT_FAILURE, "failed to take out a read lease"); + } + } + free_arg(&lease_r); + if (fd != fdescs[0].fd) { if (dup2(fd, fdescs[0].fd) < 0) { int e = errno; @@ -363,6 +398,365 @@ static void *open_ro_regular_file(const struct factory *factory, struct fdesc fd return NULL; } +static void unlink_and_close_fdesc(int fd, void *data) +{ + char *fname = data; + + unlink(fname); + close(fd); +} + +typedef void (*lockFn)(int fd, const char *fname, int dupfd); + +static void lock_fn_none(int fd _U_, const char *fname _U_, int dupfd _U_) +{ + /* Do nothing */ +} + +static void lock_fn_flock_sh(int fd, const char *fname, int dupfd) +{ + if (flock(fd, LOCK_SH) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock"); + } +} + +static void lock_fn_flock_ex(int fd, const char *fname, int dupfd) +{ + if (flock(fd, LOCK_EX) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock"); + } +} + +static void lock_fn_posix_r_(int fd, const char *fname, int dupfd) +{ + struct flock r = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1, + }; + if (fcntl(fd, F_SETLK, &r) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock"); + } +} + +static void lock_fn_posix__w(int fd, const char *fname, int dupfd) +{ + struct flock w = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1, + }; + if (fcntl(fd, F_SETLK, &w) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock"); + } +} + +static void lock_fn_posix_rw(int fd, const char *fname, int dupfd) +{ + struct flock r = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1, + }; + struct flock w = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_start = 2, + .l_len = 1, + }; + if (fcntl(fd, F_SETLK, &r) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock(read)"); + } + if (fcntl(fd, F_SETLK, &w) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock(write)"); + } +} + +#ifdef F_OFD_SETLK +static void lock_fn_ofd_r_(int fd, const char *fname, int dupfd) +{ + struct flock r = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1, + .l_pid = 0, + }; + if (fcntl(fd, F_OFD_SETLK, &r) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock"); + } +} + +static void lock_fn_ofd__w(int fd, const char *fname, int dupfd) +{ + struct flock w = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1, + .l_pid = 0, + }; + if (fcntl(fd, F_OFD_SETLK, &w) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock"); + } +} + +static void lock_fn_ofd_rw(int fd, const char *fname, int dupfd) +{ + struct flock r = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1, + .l_pid = 0, + }; + struct flock w = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_start = 2, + .l_len = 1, + .l_pid = 0, + }; + if (fcntl(fd, F_OFD_SETLK, &r) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock(read)"); + } + if (fcntl(fd, F_OFD_SETLK, &w) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to lock(write)"); + } +} +#endif /* F_OFD_SETLK */ + +static void lock_fn_lease_w(int fd, const char *fname, int dupfd) +{ + if (fcntl(fd, F_SETLEASE, F_WRLCK) < 0) { + int e = errno; + close(fd); + close(dupfd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to take out a write lease"); + } +} + + +static void *make_w_regular_file(const struct factory *factory, struct fdesc fdescs[], + int argc, char ** argv) +{ + int fd; + + struct arg file = decode_arg("file", factory->params, argc, argv); + char *fname = xstrdup(ARG_STRING(file)); + + struct arg delete = decode_arg("delete", factory->params, argc, argv); + bool bDelete = ARG_BOOLEAN(delete); + + struct arg write_bytes = decode_arg("write-bytes", factory->params, argc, argv); + int iWrite_bytes = ARG_INTEGER(write_bytes); + + struct arg readable = decode_arg("readable", factory->params, argc, argv); + bool bReadable = ARG_BOOLEAN(readable); + + struct arg lock = decode_arg("lock", factory->params, argc, argv); + const char *sLock = ARG_STRING(lock); + lockFn lock_fn; + + struct arg dupfd = decode_arg("dupfd", factory->params, argc, argv); + int iDupfd = ARG_INTEGER(dupfd); + + void *data = NULL; + + if (iWrite_bytes < 0) + errx(EXIT_FAILURE, "write-bytes must be a positive number or zero."); + + if (strcmp(sLock, "none") == 0) + lock_fn = lock_fn_none; + else if (strcmp(sLock, "flock-sh") == 0) + lock_fn = lock_fn_flock_sh; + else if (strcmp(sLock, "flock-ex") == 0) + lock_fn = lock_fn_flock_ex; + else if (strcmp(sLock, "posix-r-") == 0) { + bReadable = true; + if (iWrite_bytes < 1) + iWrite_bytes = 1; + lock_fn = lock_fn_posix_r_; + } else if (strcmp(sLock, "posix--w") == 0) { + if (iWrite_bytes < 1) + iWrite_bytes = 1; + lock_fn = lock_fn_posix__w; + } else if (strcmp(sLock, "posix-rw") == 0) { + bReadable = true; + if (iWrite_bytes < 3) + iWrite_bytes = 3; + lock_fn = lock_fn_posix_rw; +#ifdef F_OFD_SETLK + } else if (strcmp(sLock, "ofd-r-") == 0) { + bReadable = true; + if (iWrite_bytes < 1) + iWrite_bytes = 1; + lock_fn = lock_fn_ofd_r_; + } else if (strcmp(sLock, "ofd--w") == 0) { + if (iWrite_bytes < 1) + iWrite_bytes = 1; + lock_fn = lock_fn_ofd__w; + } else if (strcmp(sLock, "ofd-rw") == 0) { + bReadable = true; + if (iWrite_bytes < 3) + iWrite_bytes = 3; + lock_fn = lock_fn_ofd_rw; +#else + } else if (strcmp(sLock, "ofd-r-") == 0 + || strcmp(sLock, "ofd--w") == 0 + || strcmp(sLock, "ofd-rw") == 0) { + errx(EXIT_ENOSYS, "no availability for ofd lock"); +#endif /* F_OFD_SETLK */ + } else if (strcmp(sLock, "lease-w") == 0) + lock_fn = lock_fn_lease_w; + else + errx(EXIT_FAILURE, "unexpected value for lock parameter: %s", sLock); + + free_arg(&dupfd); + free_arg(&lock); + free_arg(&readable); + free_arg(&write_bytes); + free_arg(&delete); + free_arg(&file); + + fd = open(fname, O_CREAT|O_EXCL|(bReadable? O_RDWR: O_WRONLY), S_IWUSR); + if (fd < 0) + err(EXIT_FAILURE, "failed to make: %s", fname); + + if (fd != fdescs[0].fd) { + if (dup2(fd, fdescs[0].fd) < 0) { + int e = errno; + close(fd); + unlink(fname); + free(fname); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd); + } + close(fd); + fd = fdescs[0].fd; + } + + if (bDelete) { + if (unlink(fname) < 0) { + int e = errno; + close(fd); + errno = e; + err(EXIT_FAILURE, "failed to unlink %s", fname); + } + free(fname); + fname = NULL; + } + + for (int i = 0; i < iWrite_bytes; i++) { + if (write(fd, "z", 1) != 1) { + int e = errno; + close(fd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed to write"); + } + } + + if (iDupfd >= 0) { + if (dup2(fd, iDupfd) < 0) { + int e = errno; + close(fd); + if (fname) + unlink(fname); + errno = e; + err(EXIT_FAILURE, "failed in dup2"); + } + data = xmalloc(sizeof(iDupfd)); + *((int *)data) = iDupfd; + } + + lock_fn(fd, fname, iDupfd); + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = bDelete? close_fdesc: unlink_and_close_fdesc, + .data = fname, + }; + + return data; +} + +static void free_after_closing_duplicated_fd(const struct factory * factory _U_, void *data) +{ + if (data) { + int *fdp = data; + close(*fdp); + free(data); + } +} + static void *make_pipe(const struct factory *factory, struct fdesc fdescs[], int argc, char ** argv) { @@ -552,6 +946,12 @@ static void *make_socketpair(const struct factory *factory, struct fdesc fdescs[ int sd[2]; struct arg socktype = decode_arg("socktype", factory->params, argc, argv); int isocktype; + struct arg halfclose = decode_arg("halfclose", factory->params, argc, argv); + bool bhalfclose; + + bhalfclose = ARG_BOOLEAN(halfclose); + free_arg(&halfclose); + if (strcmp(ARG_STRING(socktype), "STREAM") == 0) isocktype = SOCK_STREAM; else if (strcmp(ARG_STRING(socktype), "DGRAM") == 0) @@ -567,6 +967,15 @@ static void *make_socketpair(const struct factory *factory, struct fdesc fdescs[ if (socketpair(AF_UNIX, isocktype, 0, sd) < 0) err(EXIT_FAILURE, "failed to make socket pair"); + if (bhalfclose) { + if (shutdown(sd[0], SHUT_RD) < 0) + err(EXIT_FAILURE, + "failed to shutdown the read end of the 1st socket"); + if (shutdown(sd[1], SHUT_WR) < 0) + err(EXIT_FAILURE, + "failed to shutdown the write end of the 2nd socket"); + } + for (int i = 0; i < 2; i++) { if (sd[i] != fdescs[i].fd) { if (dup2(sd[i], fdescs[i].fd) < 0) { @@ -735,7 +1144,7 @@ static void *make_mmapped_packet_socket(const struct factory *factory, struct fd "failed to specify a buffer spec to a packet socket"); } - munmap_data = xmalloc(sizeof (*munmap_data)); + munmap_data = xmalloc(sizeof(*munmap_data)); munmap_data->len = (size_t) req.tp_block_size * req.tp_block_nr; munmap_data->ptr = mmap(NULL, munmap_data->len, PROT_WRITE, MAP_SHARED, sd, 0); if (munmap_data->ptr == MAP_FAILED) { @@ -775,8 +1184,7 @@ static void *make_pidfd(const struct factory *factory, struct fdesc fdescs[], int fd = pidfd_open(pid, 0); if (fd < 0) - err((errno == ENOSYS? EXIT_ENOSYS: EXIT_FAILURE), - "failed in pidfd_open(%d)", (int)pid); + err_nosys(EXIT_FAILURE, "failed in pidfd_open(%d)", (int)pid); free_arg(&target_pid); if (fd != fdescs[0].fd) { @@ -801,10 +1209,31 @@ static void *make_pidfd(const struct factory *factory, struct fdesc fdescs[], static void *make_inotify_fd(const struct factory *factory _U_, struct fdesc fdescs[], int argc _U_, char ** argv _U_) { + struct arg dir = decode_arg("dir", factory->params, argc, argv); + const char *sdir = ARG_STRING(dir); + struct arg file = decode_arg("file", factory->params, argc, argv); + const char *sfile = ARG_STRING(file); + int fd = inotify_init(); if (fd < 0) err(EXIT_FAILURE, "failed in inotify_init()"); + if (inotify_add_watch(fd, sdir, IN_DELETE) < 0) { + int e = errno; + close(fd); + errno = e; + err(EXIT_FAILURE, "failed in inotify_add_watch(\"%s\")", sdir); + } + free_arg(&dir); + + if (inotify_add_watch(fd, sfile, IN_DELETE) < 0) { + int e = errno; + close(fd); + errno = e; + err(EXIT_FAILURE, "failed in inotify_add_watch(\"%s\")", sfile); + } + free_arg(&file); + if (fd != fdescs[0].fd) { if (dup2(fd, fdescs[0].fd) < 0) { int e = errno; @@ -1670,7 +2099,7 @@ static void *make_ping_common(const struct factory *factory, struct fdesc fdescs sd = socket(family, SOCK_DGRAM, protocol); if (sd < 0) - err((errno == EACCES? EXIT_EACCESS: EXIT_FAILURE), + err((errno == EACCES? EXIT_EACCES: EXIT_FAILURE), "failed to make an icmp socket"); if (sd != fdescs[0].fd) { @@ -1690,7 +2119,7 @@ static void *make_ping_common(const struct factory *factory, struct fdesc fdescs int e = errno; close(sd); errno = e; - err((errno == EACCES? EXIT_EACCESS: EXIT_FAILURE), + err((errno == EACCES? EXIT_EACCES: EXIT_FAILURE), "failed in bind(2)"); } } @@ -1809,6 +2238,7 @@ static void *make_ping6(const struct factory *factory, struct fdesc fdescs[], (struct sockaddr *)&in6); } +#ifdef SIOCGSKNS static void *make_netns(const struct factory *factory _U_, struct fdesc fdescs[], int argc _U_, char ** argv _U_) { @@ -1818,8 +2248,7 @@ static void *make_netns(const struct factory *factory _U_, struct fdesc fdescs[] int ns = ioctl(sd, SIOCGSKNS); if (ns < 0) - err((errno == ENOSYS? EXIT_ENOSYS: EXIT_FAILURE), - "failed in ioctl(SIOCGSKNS)"); + err_nosys(EXIT_FAILURE, "failed in ioctl(SIOCGSKNS)"); close(sd); if (ns != fdescs[0].fd) { @@ -1840,6 +2269,7 @@ static void *make_netns(const struct factory *factory _U_, struct fdesc fdescs[] return NULL; } +#endif /* SIOCGSKNS */ static void *make_netlink(const struct factory *factory, struct fdesc fdescs[], int argc, char ** argv) @@ -1887,6 +2317,952 @@ static void *make_netlink(const struct factory *factory, struct fdesc fdescs[], return NULL; } +static void *make_eventfd(const struct factory *factory _U_, struct fdesc fdescs[], + int argc _U_, char ** argv _U_) +{ + int fd; + pid_t *pid = xcalloc(1, sizeof(*pid)); + + if (fdescs[0].fd == fdescs[1].fd) + errx(EXIT_FAILURE, "specify three different numbers as file descriptors"); + + fd = eventfd(0, 0); + if (fd < 0) + err(EXIT_FAILURE, "failed in eventfd(2)"); + + if (fd != fdescs[0].fd) { + if (dup2(fd, fdescs[0].fd) < 0) { + int e = errno; + close(fd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd); + } + close(fd); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + if (dup2(fdescs[0].fd, fdescs[1].fd) < 0) { + int e = errno; + close(fdescs[0].fd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", fdescs[0].fd, fdescs[1].fd); + } + + signal(SIGCHLD, abort_with_child_death_message); + *pid = fork(); + if (*pid < -1) { + int e = errno; + close(fdescs[0].fd); + close(fdescs[1].fd); + errno = e; + err(EXIT_FAILURE, "failed in fork()"); + } else if (*pid == 0) { + uint64_t v = 1; + + free(pid); + close(fdescs[0].fd); + + signal(SIGCONT, do_nothing); + /* Notify the parent that I'm ready. */ + if (write(fdescs[1].fd, &v, sizeof(v)) != sizeof(v)) { + close(fdescs[1].fd); + err(EXIT_FAILURE, + "failed in write() to notify the readiness to the prent"); + } + /* Wait till the parent lets me go. */ + pause(); + + close(fdescs[1].fd); + exit(0); + } else { + uint64_t v; + + /* The child owns fdescs[1]. */ + close(fdescs[1].fd); + fdescs[1].fd = -1; + + /* Wait till the child is ready. */ + if (read(fdescs[0].fd, &v, sizeof(uint64_t)) != sizeof(v)) { + free(pid); + close(fdescs[0].fd); + err(EXIT_FAILURE, + "failed in read() the readiness notification from the child"); + } + signal(SIGCHLD, SIG_DFL); + } + + return pid; +} + +static void report_eventfd(const struct factory *factory _U_, + int nth, void *data, FILE *fp) +{ + if (nth == 0) { + pid_t *child = data; + fprintf(fp, "%d", *child); + } +} + +static void free_eventfd(const struct factory * factory _U_, void *data) +{ + pid_t child = *(pid_t *)data; + int wstatus; + + free(data); + + kill(child, SIGCONT); + if (waitpid(child, &wstatus, 0) < 0) + err(EXIT_FAILURE, "failed in waitpid()"); + + if (WIFEXITED(wstatus)) { + int s = WEXITSTATUS(wstatus); + if (s != 0) + err(EXIT_FAILURE, "the child process got an error: %d", s); + } else if (WIFSIGNALED(wstatus)) { + int s = WTERMSIG(wstatus); + if (WTERMSIG(wstatus) != 0) + err(EXIT_FAILURE, "the child process got a signal: %d", s); + } +} + +struct mqueue_data { + pid_t pid; + const char *path; + bool created; +}; + +static void mqueue_data_free(struct mqueue_data *data) +{ + if (data->created) + mq_unlink(data->path); + free((void *)data->path); + free(data); +} + +static void report_mqueue(const struct factory *factory _U_, + int nth, void *data, FILE *fp) +{ + if (nth == 0) { + fprintf(fp, "%d", ((struct mqueue_data *)data)->pid); + } +} + +static void close_mqueue(int fd, void *data _U_) +{ + mq_close(fd); +} + +static void free_mqueue(const struct factory * factory _U_, void *data) +{ + struct mqueue_data *mqueue_data = data; + pid_t child = mqueue_data->pid; + int wstatus; + + mqueue_data_free(mqueue_data); + + kill(child, SIGCONT); + if (waitpid(child, &wstatus, 0) < 0) + err(EXIT_FAILURE, "failed in waitpid()"); + + if (WIFEXITED(wstatus)) { + int s = WEXITSTATUS(wstatus); + if (s != 0) + err(EXIT_FAILURE, "the child process got an error: %d", s); + } else if (WIFSIGNALED(wstatus)) { + int s = WTERMSIG(wstatus); + if (WTERMSIG(wstatus) != 0) + err(EXIT_FAILURE, "the child process got a signal: %d", s); + } +} + +static void *make_mqueue(const struct factory *factory, struct fdesc fdescs[], + int argc, char ** argv) +{ + struct mqueue_data *mqueue_data; + struct arg path = decode_arg("path", factory->params, argc, argv); + const char *spath = ARG_STRING(path); + + struct mq_attr attr = { + .mq_maxmsg = 1, + .mq_msgsize = 1, + }; + + int fd; + + if (spath[0] != '/') + errx(EXIT_FAILURE, "the path for mqueue must start with '/': %s", spath); + + if (spath[0] == '\0') + err(EXIT_FAILURE, "the path should not be empty"); + + if (fdescs[0].fd == fdescs[1].fd) + errx(EXIT_FAILURE, "specify three different numbers as file descriptors"); + + mqueue_data = xmalloc(sizeof(*mqueue_data)); + mqueue_data->pid = 0; + mqueue_data->path = xstrdup(spath); + mqueue_data->created = false; + + free_arg(&path); + + fd = mq_open(mqueue_data->path, O_CREAT|O_EXCL | O_RDONLY, S_IRUSR | S_IWUSR, &attr); + if (fd < 0) { + mqueue_data_free(mqueue_data); + err(EXIT_FAILURE, "failed in mq_open(3) for reading"); + } + + mqueue_data->created = true; + if (fd != fdescs[0].fd) { + if (dup2(fd, fdescs[0].fd) < 0) { + int e = errno; + mq_close(fd); + mqueue_data_free(mqueue_data); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd); + } + mq_close(fd); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_mqueue, + .data = NULL + }; + + fd = mq_open(mqueue_data->path, O_WRONLY, S_IRUSR | S_IWUSR, NULL); + if (fd < 0) { + int e = errno; + mq_close(fdescs[0].fd); + mqueue_data_free(mqueue_data); + errno = e; + err(EXIT_FAILURE, "failed in mq_open(3) for writing"); + } + + if (fd != fdescs[1].fd) { + if (dup2(fd, fdescs[1].fd) < 0) { + int e = errno; + mq_close(fd); + mq_close(fdescs[0].fd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[1].fd); + } + mq_close(fd); + } + fdescs[1] = (struct fdesc){ + .fd = fdescs[1].fd, + .close = close_mqueue, + .data = NULL + }; + + signal(SIGCHLD, abort_with_child_death_message); + mqueue_data->pid = fork(); + if (mqueue_data->pid < -1) { + int e = errno; + mq_close(fdescs[0].fd); + mq_close(fdescs[1].fd); + mqueue_data_free(mqueue_data); + errno = e; + err(EXIT_FAILURE, "failed in fork()"); + } else if (mqueue_data->pid == 0) { + mqueue_data->created = false; + mqueue_data_free(mqueue_data); + mq_close(fdescs[0].fd); + + signal(SIGCONT, do_nothing); + /* Notify the parent that I'm ready. */ + if (mq_send(fdescs[1].fd, "", 0, 0) < 0) + err(EXIT_FAILURE, + "failed in mq_send() to notify the readiness to the prent"); + /* Wait till the parent lets me go. */ + pause(); + + mq_close(fdescs[1].fd); + exit(0); + } else { + char c; + + /* The child owns fdescs[1]. */ + mq_close(fdescs[1].fd); + fdescs[1].fd = -1; + + /* Wait till the child is ready. */ + if (mq_receive(fdescs[0].fd, &c, 1, NULL) < 0) { + mq_close(fdescs[0].fd); + mqueue_data_free(mqueue_data); + err(EXIT_FAILURE, + "failed in mq_receive() the readiness notification from the child"); + } + signal(SIGCHLD, SIG_DFL); + } + + return mqueue_data; +} +struct sysvshm_data { + void *addr; + int id; +}; + +static void *make_sysvshm(const struct factory *factory _U_, struct fdesc fdescs[] _U_, + int argc _U_, char ** argv _U_) +{ + size_t pagesize = getpagesize(); + struct sysvshm_data *sysvshm_data; + int id = shmget(IPC_PRIVATE, pagesize, IPC_CREAT | 0600); + void *start; + + if (id == -1) + err(EXIT_FAILURE, "failed to do shmget(.., %zu, ...)", + pagesize); + + start = shmat(id, NULL, SHM_RDONLY); + if (start == (void *) -1) { + int e = errno; + shmctl(id, IPC_RMID, NULL); + errno = e; + err(EXIT_FAILURE, "failed to do shmat(%d,...)", id); + } + + sysvshm_data = xmalloc(sizeof(*sysvshm_data)); + sysvshm_data->addr = start; + sysvshm_data->id = id; + return sysvshm_data; +} + +static void free_sysvshm(const struct factory *factory _U_, void *data) +{ + struct sysvshm_data *sysvshm_data = data; + + shmdt(sysvshm_data->addr); + shmctl(sysvshm_data->id, IPC_RMID, NULL); +} + +static void *make_eventpoll(const struct factory *factory _U_, struct fdesc fdescs[], + int argc _U_, char ** argv _U_) +{ + int efd; + struct spec { + const char *file; + int flag; + uint32_t events; + } specs [] = { + { + .file = "DUMMY, DONT'USE THIS" + }, { + .file = "/dev/random", + .flag = O_RDONLY, + .events = EPOLLIN, + }, { + .file = "/dev/random", + .flag = O_WRONLY, + .events = EPOLLOUT, + }, + }; + + efd = epoll_create(1); + if (efd < 0) + err(EXIT_FAILURE, "failed in epoll_create(2)"); + if (efd != fdescs[0].fd) { + if (dup2(efd, fdescs[0].fd) < 0) { + int e = errno; + close(efd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", efd, fdescs[0].fd); + } + close(efd); + efd = fdescs[0].fd; + } + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + for (size_t i = 1; i < ARRAY_SIZE(specs); i++) { + int fd = open(specs[i].file, specs[i].flag); + if (fd < 0) { + int e = errno; + close(efd); + for (size_t j = i - 1; j > 0; j--) + close(fdescs[j].fd); + errno = e; + err(EXIT_FAILURE, "failed in open(\"%s\",...)", + specs[i].file); + } + if (fd != fdescs[i].fd) { + if (dup2(fd, fdescs[i].fd) < 0) { + int e = errno; + close(efd); + for (size_t j = i - 1; j > 0; j--) + close(fdescs[j].fd); + close(fd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", + fd, fdescs[i].fd); + } + close(fd); + } + fdescs[i] = (struct fdesc) { + .fd = fdescs[i].fd, + .close = close_fdesc, + .data = NULL + }; + if (epoll_ctl(efd, EPOLL_CTL_ADD, fdescs[i].fd, + &(struct epoll_event) { + .events = specs[i].events, + .data = {.ptr = NULL,} + }) < 0) { + int e = errno; + close(efd); + for (size_t j = i; j > 0; j--) + close(fdescs[j].fd); + errno = e; + err(EXIT_FAILURE, + "failed to add fd %d to the eventpoll fd with epoll_ctl", + fdescs[i].fd); + } + } + + return NULL; +} + +static bool decode_clockid(const char *sclockid, clockid_t *clockid) +{ + if (sclockid == NULL) + return false; + if (sclockid[0] == '\0') + return false; + + if (strcmp(sclockid, "realtime") == 0) + *clockid = CLOCK_REALTIME; + else if (strcmp(sclockid, "monotonic") == 0) + *clockid = CLOCK_MONOTONIC; + else if (strcmp(sclockid, "boottime") == 0) + *clockid = CLOCK_BOOTTIME; + else if (strcmp(sclockid, "realtime-alarm") == 0) + *clockid = CLOCK_REALTIME_ALARM; + else if (strcmp(sclockid, "boottime-alarm") == 0) + *clockid = CLOCK_BOOTTIME_ALARM; + else + return false; + return true; +} + +static void *make_timerfd(const struct factory *factory, struct fdesc fdescs[], + int argc, char ** argv) +{ + int tfd; + struct timespec now; + struct itimerspec tspec; + + struct arg abstime = decode_arg("abstime", factory->params, argc, argv); + bool babstime = ARG_BOOLEAN(abstime); + + struct arg remaining = decode_arg("remaining", factory->params, argc, argv); + unsigned int uremaining = ARG_UINTEGER(remaining); + + struct arg interval = decode_arg("interval", factory->params, argc, argv); + unsigned int uinterval = ARG_UINTEGER(interval); + + struct arg interval_frac = decode_arg("interval-nanofrac", factory->params, argc, argv); + unsigned int uinterval_frac = ARG_UINTEGER(interval_frac); + + struct arg clockid_ = decode_arg("clockid", factory->params, argc, argv); + const char *sclockid = ARG_STRING(clockid_); + clockid_t clockid; + + if (decode_clockid (sclockid, &clockid) == false) + err(EXIT_FAILURE, "unknown clockid: %s", sclockid); + + free_arg(&clockid_); + free_arg(&interval_frac); + free_arg(&interval); + free_arg(&remaining); + free_arg(&abstime); + + if (babstime) { + int r = clock_gettime(clockid, &now); + if (r == -1) + err(EXIT_FAILURE, "failed in clock_gettime(2)"); + } + + tfd = timerfd_create(clockid, 0); + if (tfd < 0) + err(EXIT_FAILURE, "failed in timerfd_create(2)"); + + tspec.it_value.tv_sec = (babstime? now.tv_sec: 0) + uremaining; + tspec.it_value.tv_nsec = (babstime? now.tv_nsec: 0); + + tspec.it_interval.tv_sec = uinterval; + tspec.it_interval.tv_nsec = uinterval_frac; + + if (timerfd_settime(tfd, babstime? TFD_TIMER_ABSTIME: 0, &tspec, NULL) < 0) { + int e = errno; + close(tfd); + errno = e; + err(EXIT_FAILURE, "failed in timerfd_settime(2)"); + } + + if (tfd != fdescs[0].fd) { + if (dup2(tfd, fdescs[0].fd) < 0) { + int e = errno; + close(tfd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", tfd, fdescs[0].fd); + } + close(tfd); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + return NULL; +} + +static void *make_signalfd(const struct factory *factory _U_, struct fdesc fdescs[], + int argc _U_, char ** argv _U_) +{ + sigset_t mask; + int numsig = 42; + + if (sigemptyset(&mask) < 0) + err(EXIT_FAILURE, "failed in sigemptyset()"); + if (sigaddset(&mask, SIGFPE) < 0) + err(EXIT_FAILURE, "failed in sigaddset(FPE)"); + if (sigaddset(&mask, SIGUSR1) < 0) + err(EXIT_FAILURE, "failed in sigaddset(USR1)"); + if (sigaddset(&mask, numsig) < 0) + err(EXIT_FAILURE, "failed in sigaddset(%d)", numsig); + + int sfd= signalfd(-1, &mask, 0); + if (sfd < 0) + err(EXIT_FAILURE, "failed in signalfd(2)"); + + if (sfd != fdescs[0].fd) { + if (dup2(sfd, fdescs[0].fd) < 0) { + int e = errno; + close(sfd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", sfd, fdescs[0].fd); + } + close(sfd); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + return NULL; +} + + +/* ref. linux/Documentation/networking/tuntap.rst */ +static void *make_cdev_tun(const struct factory *factory _U_, struct fdesc fdescs[], + int argc _U_, char ** argv _U_) +{ + int tfd = open("/dev/net/tun", O_RDWR); + struct ifreq ifr; + + if (tfd < 0) + err(EXIT_FAILURE, "failed in opening /dev/net/tun"); + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = IFF_TUN; + strcpy(ifr.ifr_name, "mkfds%d"); + + if (ioctl(tfd, TUNSETIFF, (void *) &ifr) < 0) { + int e = errno; + close(tfd); + errno = e; + err(EXIT_FAILURE, "failed in setting \"lo\" to the tun device"); + } + + if (tfd != fdescs[0].fd) { + if (dup2(tfd, fdescs[0].fd) < 0) { + int e = errno; + close(tfd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", tfd, fdescs[0].fd); + } + close(tfd); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + return xstrdup(ifr.ifr_name); +} + +static void report_cdev_tun(const struct factory *factory _U_, + int nth, void *data, FILE *fp) +{ + if (nth == 0) { + char *devname = data; + fprintf(fp, "%s", devname); + } +} + +static void free_cdev_tun(const struct factory * factory _U_, void *data) +{ + free(data); +} + +static void *make_bpf_prog(const struct factory *factory, struct fdesc fdescs[], + int argc, char ** argv) +{ + struct arg prog_type_id = decode_arg("prog-type-id", factory->params, argc, argv); + int iprog_type_id = ARG_INTEGER(prog_type_id); + + struct arg name = decode_arg("name", factory->params, argc, argv); + const char *sname = ARG_STRING(name); + + int bfd; + union bpf_attr attr; + /* Just doing exit with 0. */ + struct bpf_insn insns[] = { + [0] = { + .code = BPF_ALU64 | BPF_MOV | BPF_K, + .dst_reg = BPF_REG_0, .src_reg = 0, .off = 0, .imm = 0 + }, + [1] = { + .code = BPF_JMP | BPF_EXIT, + .dst_reg = 0, .src_reg = 0, .off = 0, .imm = 0 + }, + }; + + memset(&attr, 0, sizeof(attr)); + attr.prog_type = iprog_type_id; + attr.insns = (uint64_t)(unsigned long)insns; + attr.insn_cnt = ARRAY_SIZE(insns); + attr.license = (int64_t)(unsigned long)"GPL"; + strncpy(attr.prog_name, sname, sizeof(attr.prog_name) - 1); + + free_arg(&name); + free_arg(&prog_type_id); + + bfd = syscall(SYS_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); + if (bfd < 0) + err_nosys(EXIT_FAILURE, "failed in bpf(BPF_PROG_LOAD)"); + + if (bfd != fdescs[0].fd) { + if (dup2(bfd, fdescs[0].fd) < 0) { + int e = errno; + close(bfd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", bfd, fdescs[0].fd); + } + close(bfd); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + return NULL; +} + +static void *make_some_pipes(const struct factory *factory _U_, struct fdesc fdescs[], + int argc _U_, char ** argv _U_) +{ + /* Reserver fds before making pipes */ + for (int i = 0; i < factory->N; i++) { + close(fdescs[i].fd); + if (dup2(0, fdescs[0].fd) < 0) + err(EXIT_FAILURE, "failed to reserve fd %d with dup2", fdescs[0].fd); + } + + for (int i = 0; i < (factory->N) / 2; i++) { + int pd[2]; + unsigned int mode; + int r = 0, w = 1; + + mode = 1 << (i % 3); + if (mode == MX_WRITE) { + r = 1; + w = 0; + } + + if (pipe(pd) < 0) + err(EXIT_FAILURE, "failed to make pipe"); + + if (dup2(pd[0], fdescs[2 * i + r].fd) < 0) + err(EXIT_FAILURE, "failed to dup %d -> %d", pd[0], fdescs[2 * i + r].fd); + close(pd[0]); + fdescs[2 * 1 + r].close = close_fdesc; + + if (dup2(pd[1], fdescs[2 * i + w].fd) < 0) + err(EXIT_FAILURE, "failed to dup %d -> %d", pd[1], fdescs[2 * i + 2].fd); + close(pd[1]); + fdescs[2 * 1 + w].close = close_fdesc; + + fdescs[2 * i].mx_modes |= mode; + + /* Make the pipe for writing full. */ + if (fdescs[2 * i].mx_modes & MX_WRITE) { + int n = fcntl(fdescs[2 * i].fd, F_GETPIPE_SZ); + char *buf; + + if (n < 0) + err(EXIT_FAILURE, "failed to get PIPE BUFFER SIZE from %d", fdescs[2 * i].fd); + + buf = xmalloc(n); + if (write(fdescs[2 * i].fd, buf, n) != n) + err(EXIT_FAILURE, "failed to fill the pipe buffer specified with %d", + fdescs[2 * i].fd); + free(buf); + } + + } + + return NULL; +} + +static void *make_bpf_map(const struct factory *factory, struct fdesc fdescs[], + int argc, char ** argv) +{ + struct arg map_type_id = decode_arg("map-type-id", factory->params, argc, argv); + int imap_type_id = ARG_INTEGER(map_type_id); + + struct arg name = decode_arg("name", factory->params, argc, argv); + const char *sname = ARG_STRING(name); + + int bfd; + union bpf_attr attr = { + .map_type = imap_type_id, + .key_size = 4, + .value_size = 4, + .max_entries = 10, + }; + + strncpy(attr.map_name, sname, sizeof(attr.map_name) - 1); + + free_arg(&name); + free_arg(&map_type_id); + + bfd = syscall(SYS_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); + if (bfd < 0) + err_nosys(EXIT_FAILURE, "failed in bpf(BPF_MAP_CREATE)"); + + if (bfd != fdescs[0].fd) { + if (dup2(bfd, fdescs[0].fd) < 0) { + int e = errno; + close(bfd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", bfd, fdescs[0].fd); + } + close(bfd); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + return NULL; +} + +static void *make_pty(const struct factory *factory _U_, struct fdesc fdescs[], + int argc _U_, char ** argv _U_) +{ + int index, *indexp; + char *pts; + int pts_fd; + int ptmx_fd = posix_openpt(O_RDWR); + if (ptmx_fd < 0) + err(EXIT_FAILURE, "failed in opening /dev/ptmx"); + + if (unlockpt(ptmx_fd) < 0) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed in unlockpt()"); + } + + if (ioctl(ptmx_fd, TIOCGPTN, &index) < 0) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed in ioctl(TIOCGPTN)"); + } + + pts = ptsname(ptmx_fd); + if (pts == NULL) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed in ptsname()"); + } + + if (ptmx_fd != fdescs[0].fd) { + if (dup2(ptmx_fd, fdescs[0].fd) < 0) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", ptmx_fd, fdescs[0].fd); + } + close(ptmx_fd); + ptmx_fd = fdescs[0].fd; + } + + pts_fd = open(pts, O_RDONLY); + if (pts_fd < 0) { + int e = errno; + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed in opening %s", pts); + } + + if (pts_fd != fdescs[1].fd) { + if (dup2(pts_fd, fdescs[1].fd) < 0) { + int e = errno; + close(pts_fd); + close(ptmx_fd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", pts_fd, fdescs[1].fd); + } + close(pts_fd); + pts_fd = fdescs[1].fd; + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + fdescs[1] = (struct fdesc){ + .fd = fdescs[1].fd, + .close = close_fdesc, + .data = NULL + }; + + indexp = xmalloc(sizeof(index)); + *indexp = index; + return indexp; +} + +static void report_pty(const struct factory *factory _U_, + int nth, void *data, FILE *fp) +{ + if (nth == 0) { + int *index = data; + fprintf(fp, "%d", *index); + } +} + +static void free_pty(const struct factory * factory _U_, void *data) +{ + free(data); +} + +static int send_diag_request(int diagsd, void *req, size_t req_size) +{ + struct sockaddr_nl nladdr = { + .nl_family = AF_NETLINK, + }; + + struct nlmsghdr nlh = { + .nlmsg_len = sizeof(nlh) + req_size, + .nlmsg_type = SOCK_DIAG_BY_FAMILY, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + }; + + struct iovec iovecs[] = { + { &nlh, sizeof(nlh) }, + { req, req_size }, + }; + + const struct msghdr mhd = { + .msg_namelen = sizeof(nladdr), + .msg_name = &nladdr, + .msg_iovlen = ARRAY_SIZE(iovecs), + .msg_iov = iovecs, + }; + + if (sendmsg(diagsd, &mhd, 0) < 0) + return errno; + + return 0; +} + +static void *make_sockdiag(const struct factory *factory, struct fdesc fdescs[], + int argc, char ** argv) +{ + struct arg family = decode_arg("family", factory->params, argc, argv); + const char *sfamily = ARG_STRING(family); + int ifamily; + + if (strcmp(sfamily, "unix") == 0) + ifamily = AF_UNIX; + else + errx(EXIT_FAILURE, "unknown/unsupported family: %s", sfamily); + free_arg(&family); + + int diagsd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_SOCK_DIAG); + if (diagsd < 0) + err(errno == EPROTONOSUPPORT? EXIT_EPROTONOSUPPORT: EXIT_FAILURE, + "failed in sendmsg()"); + + void *req = NULL; + size_t reqlen = 0; + + struct unix_diag_req udr; + if (ifamily == AF_UNIX) { + udr = (struct unix_diag_req) { + .sdiag_family = AF_UNIX, + .udiag_states = -1, /* set the all bits. */ + .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UNIX_DIAG_SHUTDOWN, + }; + req = &udr; + reqlen = sizeof(udr); + } + + int e = send_diag_request(diagsd, req, reqlen); + if (e) { + close (diagsd); + errno = e; + if (errno == EACCES) + err(EXIT_EACCES, "failed in sendmsg()"); + if (errno == ENOENT) + err(EXIT_ENOENT, "failed in sendmsg()"); + err(EXIT_FAILURE, "failed in sendmsg()"); + } + + + if (diagsd != fdescs[0].fd) { + if (dup2(diagsd, fdescs[0].fd) < 0) { + int e = errno; + close(diagsd); + errno = e; + err(EXIT_FAILURE, "failed to dup %d -> %d", diagsd, fdescs[0].fd); + } + close(diagsd); + } + + fdescs[0] = (struct fdesc){ + .fd = fdescs[0].fd, + .close = close_fdesc, + .data = NULL + }; + + return NULL; +} + #define PARAM_END { .name = NULL, } static const struct factory factories[] = { { @@ -1909,6 +3285,60 @@ static const struct factory factories[] = { .desc = "seek bytes after open with SEEK_CUR", .defv.integer = 0, }, + { + .name = "read-lease", + .type = PTYPE_BOOLEAN, + .desc = "taking out read lease for the file", + .defv.boolean = false, + }, + PARAM_END + }, + }, + { + .name = "make-regular-file", + .desc = "regular file for writing", + .priv = false, + .N = 1, + .EX_N = 0, + .make = make_w_regular_file, + .free = free_after_closing_duplicated_fd, + .params = (struct parameter []) { + { + .name = "file", + .type = PTYPE_STRING, + .desc = "file to be made", + .defv.string = "./test_mkfds_make_regular_file", + }, + { + .name = "delete", + .type = PTYPE_BOOLEAN, + .desc = "delete the file just after making it", + .defv.boolean = false, + }, + { + .name = "write-bytes", + .type = PTYPE_INTEGER, + .desc = "write something (> 0)", + .defv.integer = 0, + }, + { + .name = "readable", + .type = PTYPE_BOOLEAN, + .desc = "open the new file readable way", + .defv.string = false, + }, + { + .name = "lock", + .type = PTYPE_STRING, + .desc = "the way for file locking: [none]|flock-sh|flock-ex|posix-r-|posix--w|posix-rw|ofd-r-|ofd--w|ofd-rw|lease-w", + .defv.string = "none", + }, + { + .name = "dupfd", + .type = PTYPE_INTEGER, + .desc = "the number for the fd duplicated from the original fd", + .defv.integer = -1, + }, PARAM_END }, }, @@ -1995,6 +3425,12 @@ static const struct factory factories[] = { .desc = "STREAM, DGRAM, or SEQPACKET", .defv.string = "STREAM", }, + { + .name = "halfclose", + .type = PTYPE_BOOLEAN, + .desc = "Shutdown the read end of the 1st socket, the write end of the 2nd socket", + .defv.boolean = false, + }, PARAM_END }, }, @@ -2080,6 +3516,18 @@ static const struct factory factories[] = { .EX_N = 0, .make = make_inotify_fd, .params = (struct parameter []) { + { + .name = "dir", + .type = PTYPE_STRING, + .desc = "the directory that the inotify monitors", + .defv.string = "/", + }, + { + .name = "file", + .type = PTYPE_STRING, + .desc = "the file that the inotify monitors", + .defv.string = "/etc/fstab", + }, PARAM_END }, }, @@ -2416,6 +3864,7 @@ static const struct factory factories[] = { PARAM_END } }, +#ifdef SIOCGSKNS { .name = "netns", .desc = "open a file specifying a netns", @@ -2427,6 +3876,7 @@ static const struct factory factories[] = { PARAM_END } }, +#endif { .name = "netlink", .desc = "AF_NETLINK sockets", @@ -2450,6 +3900,219 @@ static const struct factory factories[] = { PARAM_END } }, + { + .name = "eventfd", + .desc = "make an eventfd connecting two processes", + .priv = false, + .N = 2, + .EX_N = 0, + .EX_R = 1, + .make = make_eventfd, + .report = report_eventfd, + .free = free_eventfd, + .params = (struct parameter []) { + PARAM_END + } + }, + { + .name = "mqueue", + .desc = "make a mqueue connecting two processes", + .priv = false, + .N = 2, + .EX_N = 0, + .EX_R = 1, + .make = make_mqueue, + .report = report_mqueue, + .free = free_mqueue, + .params = (struct parameter []) { + { + .name = "path", + .type = PTYPE_STRING, + .desc = "path for mqueue", + .defv.string = "/test_mkfds-mqueue", + }, + PARAM_END + } + }, + { + .name = "sysvshm", + .desc = "shared memory mapped with SYSVIPC shmem syscalls", + .priv = false, + .N = 0, + .EX_N = 0, + .make = make_sysvshm, + .free = free_sysvshm, + .params = (struct parameter []) { + PARAM_END + }, + }, + { + .name = "eventpoll", + .desc = "make eventpoll (epoll) file", + .priv = false, + .N = 3, + .EX_N = 0, + .make = make_eventpoll, + .params = (struct parameter []) { + PARAM_END + } + }, + { + .name = "timerfd", + .desc = "make timerfd", + .priv = false, + .N = 1, + .EX_N = 0, + .make = make_timerfd, + .params = (struct parameter []) { + { + .name = "clockid", + .type = PTYPE_STRING, + .desc = "ID: realtime, monotonic, boottime, realtime-alarm, or boottime-alarm", + .defv.string = "realtime", + }, + { + .name = "abstime", + .type = PTYPE_BOOLEAN, + .desc = "use TFD_TIMER_ABSTIME flag", + .defv.boolean = false, + }, + { + .name = "remaining", + .type = PTYPE_UINTEGER, + .desc = "remaining seconds for expiration", + .defv.uinteger = 99, + }, + { + .name = "interval", + .type = PTYPE_UINTEGER, + .desc = "inteval in seconds", + .defv.uinteger = 10, + }, + { + .name = "interval-nanofrac", + .type = PTYPE_UINTEGER, + .desc = "nsec part of inteval", + .defv.uinteger = 0, + }, + + PARAM_END + } + }, + { + .name = "signalfd", + .desc = "make signalfd", + .priv = false, + .N = 1, + .EX_N = 0, + .make = make_signalfd, + .params = (struct parameter []) { + PARAM_END + } + }, + { + .name = "cdev-tun", + .desc = "open /dev/net/tun", + .priv = true, + .N = 1, + .EX_N = 0, + .EX_R = 1, + .make = make_cdev_tun, + .report = report_cdev_tun, + .free = free_cdev_tun, + .params = (struct parameter []) { + PARAM_END + } + }, + { + .name = "bpf-prog", + .desc = "make bpf-prog", + .priv = true, + .N = 1, + .EX_N = 0, + .make = make_bpf_prog, + .params = (struct parameter []) { + { + .name = "prog-type-id", + .type = PTYPE_INTEGER, + .desc = "program type by id", + .defv.integer = 1, + }, + { + .name = "name", + .type = PTYPE_STRING, + .desc = "name assigned to bpf prog object", + .defv.string = "mkfds_bpf_prog", + }, + PARAM_END + } + }, + { + .name = "multiplexing", + .desc = "making pipes monitored by multiplexers", + .priv = false, + .N = 12, + .EX_N = 0, + .make = make_some_pipes, + .params = (struct parameter []) { + PARAM_END + } + }, + { + .name = "bpf-map", + .desc = "make bpf-map", + .priv = true, + .N = 1, + .EX_N = 0, + .make = make_bpf_map, + .params = (struct parameter []) { + { + .name = "map-type-id", + .type = PTYPE_INTEGER, + .desc = "map type by id", + .defv.integer = 1, + }, + { + .name = "name", + .type = PTYPE_STRING, + .desc = "name assigned to the bpf map object", + .defv.string = "mkfds_bpf_map", + }, + PARAM_END + } + }, + { + .name = "pty", + .desc = "make a pair of ptmx and pts", + .priv = false, + .N = 2, + .EX_N = 0, + .EX_R = 1, + .make = make_pty, + .report = report_pty, + .free = free_pty, + .params = (struct parameter []) { + PARAM_END + } + }, + { + .name = "sockdiag", + .desc = "make a sockdiag netlink socket", + .priv = false, + .N = 1, + .EX_N = 0, + .make = make_sockdiag, + .params = (struct parameter []) { + { + .name = "family", + .type = PTYPE_STRING, + /* TODO: inet, inet6 */ + .desc = "name of a protocol family ([unix])", + .defv.string = "unix", + }, + PARAM_END + } + }, }; static int count_parameters(const struct factory *factory) @@ -2465,17 +4128,18 @@ static int count_parameters(const struct factory *factory) static void print_factory(const struct factory *factory) { - printf("%-20s %4s %5d %6d %s\n", + printf("%-20s %4s %5d %7d %6d %s\n", factory->name, factory->priv? "yes": "no", factory->N, + factory->EX_R + 1, count_parameters(factory), factory->desc); } static void list_factories(void) { - printf("%-20s PRIV COUNT NPARAM DESCRIPTION\n", "FACTORY"); + printf("%-20s PRIV COUNT NRETURN NPARAM DESCRIPTION\n", "FACTORY"); for (size_t i = 0; i < ARRAY_SIZE(factories); i++) print_factory(factories + i); } @@ -2533,25 +4197,146 @@ pidfd_open(pid_t pid _U_, unsigned int flags _U_) } #endif -static void wait_event(void) +/* + * Multiplexers + */ +struct multiplexer { + const char *name; + void (*fn)(bool, struct fdesc *fdescs, size_t n_fdescs); +}; + +#if defined(__NR_select) || defined(__NR_poll) +static void sighandler_nop(int si _U_) { - fd_set readfds; - sigset_t sigset; - int n = 0; + /* Do nothing */ +} +#endif - FD_ZERO(&readfds); - /* Monitor the standard input only when the process - * is in foreground. */ - if (tcgetpgrp(STDIN_FILENO) == getpgrp()) { - n = 1; - FD_SET(0, &readfds); +#define DEFUN_WAIT_EVENT_SELECT(NAME,SYSCALL,XDECLS,SETUP_SIG_HANDLER,SYSCALL_INVOCATION) \ + static void wait_event_##NAME(bool add_stdin, struct fdesc *fdescs, size_t n_fdescs) \ + { \ + fd_set readfds; \ + fd_set writefds; \ + fd_set exceptfds; \ + XDECLS \ + int n = 0; \ + \ + FD_ZERO(&readfds); \ + FD_ZERO(&writefds); \ + FD_ZERO(&exceptfds); \ + /* Monitor the standard input only when the process \ + * is in foreground. */ \ + if (add_stdin) { \ + n = 1; \ + FD_SET(0, &readfds); \ + } \ + \ + for (size_t i = 0; i < n_fdescs; i++) { \ + if (fdescs[i].mx_modes & MX_READ) { \ + n = max(n, fdescs[i].fd + 1); \ + FD_SET(fdescs[i].fd, &readfds); \ + } \ + if (fdescs[i].mx_modes & MX_WRITE) { \ + n = max(n, fdescs[i].fd + 1); \ + FD_SET(fdescs[i].fd, &writefds); \ + } \ + if (fdescs[i].mx_modes & MX_EXCEPT) { \ + n = max(n, fdescs[i].fd + 1); \ + FD_SET(fdescs[i].fd, &exceptfds); \ + } \ + } \ + \ + SETUP_SIG_HANDLER \ + \ + if (SYSCALL_INVOCATION < 0 \ + && errno != EINTR) \ + err(EXIT_FAILURE, "failed in " SYSCALL); \ } - sigemptyset(&sigset); +DEFUN_WAIT_EVENT_SELECT(default, + "pselect", + sigset_t sigset;, + sigemptyset(&sigset);, + pselect(n, &readfds, &writefds, &exceptfds, NULL, &sigset)) + +#ifdef __NR_pselect6 +DEFUN_WAIT_EVENT_SELECT(pselect6, + "pselect6", + sigset_t sigset;, + sigemptyset(&sigset);, + syscall(__NR_pselect6, n, &readfds, &writefds, &exceptfds, NULL, &sigset)) +#endif - if (pselect(n, &readfds, NULL, NULL, NULL, &sigset) < 0 - && errno != EINTR) - errx(EXIT_FAILURE, "failed in pselect"); +#ifdef __NR_select +DEFUN_WAIT_EVENT_SELECT(select, + "select", + , + signal(SIGCONT,sighandler_nop);, + syscall(__NR_select, n, &readfds, &writefds, &exceptfds, NULL)) +#endif + +#ifdef __NR_poll +static DEFUN_WAIT_EVENT_POLL(poll, + "poll", + , + signal(SIGCONT,sighandler_nop);, + syscall(__NR_poll, pfds, n, -1)) +#endif + +#define DEFAULT_MULTIPLEXER 0 +static struct multiplexer multiplexers [] = { + { + .name = "default", + .fn = wait_event_default, + }, +#ifdef __NR_pselect6 + { + .name = "pselect6", + .fn = wait_event_pselect6, + }, +#endif +#ifdef __NR_select + { + .name = "select", + .fn = wait_event_select, + }, +#endif +#ifdef __NR_poll + { + .name = "poll", + .fn = wait_event_poll, + }, +#endif +#ifdef __NR_ppoll + { + .name = "ppoll", + .fn = wait_event_ppoll, + }, +#endif +}; + +static struct multiplexer *lookup_multiplexer(const char *name) +{ + for (size_t i = 0; i < ARRAY_SIZE(multiplexers); i++) + if (strcmp(name, multiplexers[i].name) == 0) + return multiplexers + i; + return NULL; +} + +static void list_multiplexers(void) +{ + puts("NAME"); + for (size_t i = 0; i < ARRAY_SIZE(multiplexers); i++) + puts(multiplexers[i].name); +} + +static bool is_available(const char *factory) +{ + for (size_t i = 0; i < ARRAY_SIZE(factories); i++) + if (strcmp(factories[i].name, factory) == 0) + return true; + + return false; } int main(int argc, char **argv) @@ -2562,21 +4347,30 @@ int main(int argc, char **argv) bool quiet = false; bool cont = false; void *data; + bool monitor_stdin = true; + + struct multiplexer *wait_event = NULL; static const struct option longopts[] = { + { "is-available",required_argument,NULL, 'a' }, { "list", no_argument, NULL, 'l' }, { "parameters", required_argument, NULL, 'I' }, { "comm", required_argument, NULL, 'r' }, { "quiet", no_argument, NULL, 'q' }, + { "dont-monitor-stdin", no_argument, NULL, 'X' }, { "dont-puase", no_argument, NULL, 'c' }, + { "wait-with", required_argument, NULL, 'w' }, + { "multiplexers",no_argument,NULL, 'W' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0 }, }; - while ((c = getopt_long(argc, argv, "lhqcI:r:", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "a:lhqcI:r:w:WX", longopts, NULL)) != -1) { switch (c) { case 'h': usage(stdout, EXIT_SUCCESS); + case 'a': + exit(is_available(optarg)? 0: 1); case 'l': list_factories(); exit(EXIT_SUCCESS); @@ -2589,9 +4383,20 @@ int main(int argc, char **argv) case 'c': cont = true; break; + case 'w': + wait_event = lookup_multiplexer(optarg); + if (wait_event == NULL) + errx(EXIT_FAILURE, "unknown multiplexer: %s", optarg); + break; + case 'W': + list_multiplexers(); + exit(EXIT_SUCCESS); case 'r': rename_self(optarg); break; + case 'X': + monitor_stdin = false; + break; default: usage(stderr, EXIT_FAILURE); } @@ -2600,6 +4405,11 @@ int main(int argc, char **argv) if (optind == argc) errx(EXIT_FAILURE, "no file descriptor specification given"); + if (cont && wait_event) + errx(EXIT_FAILURE, "don't specify both -c/--dont-puase and -w/--wait-with options"); + if (wait_event == NULL) + wait_event = multiplexers + DEFAULT_MULTIPLEXER; + factory = find_factory(argv[optind]); if (!factory) errx(EXIT_FAILURE, "no such factory: %s", argv[optind]); @@ -2615,6 +4425,7 @@ int main(int argc, char **argv) for (int i = 0; i < MAX_N; i++) { fdescs[i].fd = -1; + fdescs[i].mx_modes = 0; fdescs[i].close = NULL; } @@ -2645,21 +4456,26 @@ int main(int argc, char **argv) if (!quiet) { printf("%d", getpid()); + if (factory->report) { + for (int i = 0; i < factory->EX_R; i++) { + putchar(' '); + factory->report(factory, i, data, stdout); + } + } putchar('\n'); - if (factory->report) - factory->report(factory, data, stdout); fflush(stdout); } if (!cont) - wait_event(); + wait_event->fn(monitor_stdin, + fdescs, factory->N + factory->EX_N); for (int i = 0; i < factory->N + factory->EX_N; i++) if (fdescs[i].fd >= 0 && fdescs[i].close) fdescs[i].close(fdescs[i].fd, fdescs[i].data); if (factory->free) - factory->free (factory, data); + factory->free(factory, data); exit(EXIT_SUCCESS); } |