summaryrefslogtreecommitdiffstats
path: root/tests/helpers/test_mkfds.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/helpers/test_mkfds.c')
-rw-r--r--tests/helpers/test_mkfds.c1900
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);
}