summaryrefslogtreecommitdiffstats
path: root/tests/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'tests/helpers')
-rw-r--r--tests/helpers/Makemodule.am40
-rw-r--r--tests/helpers/test_byteswap.c113
-rw-r--r--tests/helpers/test_enosys.c126
-rw-r--r--tests/helpers/test_mbsencode.c43
-rw-r--r--tests/helpers/test_md5.c37
-rw-r--r--tests/helpers/test_mkfds.c2665
-rw-r--r--tests/helpers/test_pathnames.c86
-rw-r--r--tests/helpers/test_sha1.c37
-rw-r--r--tests/helpers/test_sigreceive.c170
-rw-r--r--tests/helpers/test_strerror.c44
-rw-r--r--tests/helpers/test_sysinfo.c176
-rw-r--r--tests/helpers/test_tiocsti.c21
-rw-r--r--tests/helpers/test_uuid_namespace.c42
13 files changed, 3600 insertions, 0 deletions
diff --git a/tests/helpers/Makemodule.am b/tests/helpers/Makemodule.am
new file mode 100644
index 0000000..2b1df3c
--- /dev/null
+++ b/tests/helpers/Makemodule.am
@@ -0,0 +1,40 @@
+check_PROGRAMS += test_mbsencode
+test_mbsencode_SOURCES = tests/helpers/test_mbsencode.c
+test_mbsencode_LDADD = $(LDADD) libcommon.la
+
+check_PROGRAMS += test_byteswap
+test_byteswap_SOURCES = tests/helpers/test_byteswap.c
+
+check_PROGRAMS += test_md5
+test_md5_SOURCES = tests/helpers/test_md5.c lib/md5.c
+
+check_PROGRAMS += test_sha1
+test_sha1_SOURCES = tests/helpers/test_sha1.c lib/sha1.c
+
+check_PROGRAMS += test_pathnames
+test_pathnames_SOURCES = tests/helpers/test_pathnames.c
+
+check_PROGRAMS += test_strerror
+test_strerror_SOURCES = tests/helpers/test_strerror.c
+
+check_PROGRAMS += test_sysinfo
+test_sysinfo_SOURCES = tests/helpers/test_sysinfo.c
+
+check_PROGRAMS += test_sigreceive
+test_sigreceive_SOURCES = tests/helpers/test_sigreceive.c
+test_sigreceive_LDADD = $(LDADD) libcommon.la
+
+check_PROGRAMS += test_tiocsti
+test_tiocsti_SOURCES = tests/helpers/test_tiocsti.c
+
+check_PROGRAMS += test_uuid_namespace
+test_uuid_namespace_SOURCES = tests/helpers/test_uuid_namespace.c \
+ libuuid/src/predefined.c libuuid/src/unpack.c libuuid/src/unparse.c
+
+if LINUX
+check_PROGRAMS += test_mkfds
+test_mkfds_SOURCES = tests/helpers/test_mkfds.c
+
+check_PROGRAMS += test_enosys
+test_enosys_SOURCES = tests/helpers/test_enosys.c
+endif
diff --git a/tests/helpers/test_byteswap.c b/tests/helpers/test_byteswap.c
new file mode 100644
index 0000000..d8a7b2f
--- /dev/null
+++ b/tests/helpers/test_byteswap.c
@@ -0,0 +1,113 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This testing program makes sure the byteswap functions work
+ *
+ * Copyright (C) 2000 by Theodore Ts'o.
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "bitops.h"
+
+static uint16_t ary16[] = {
+ 0x0001, 0x0100,
+ 0x1234, 0x3412,
+ 0xff00, 0x00ff,
+ 0x4000, 0x0040,
+ 0xfeff, 0xfffe,
+ 0x0000, 0x0000
+ };
+
+static uint32_t ary32[] = {
+ 0x00000001, 0x01000000,
+ 0x80000000, 0x00000080,
+ 0x12345678, 0x78563412,
+ 0xffff0000, 0x0000ffff,
+ 0x00ff0000, 0x0000ff00,
+ 0xff000000, 0x000000ff,
+ 0x00000000, 0x00000000
+ };
+
+static uint64_t ary64[] = {
+ 0x0000000000000001, 0x0100000000000000,
+ 0x8000000000000000, 0x0000000000000080,
+ 0x1234567812345678, 0x7856341278563412,
+ 0xffffffff00000000, 0x00000000ffffffff,
+ 0x00ff000000000000, 0x000000000000ff00,
+ 0xff00000000000000, 0x00000000000000ff,
+ 0x0000000000000000, 0x0000000000000000
+ };
+
+int main(void)
+{
+ int i;
+ int errors = 0;
+
+ printf("Testing swab16\n");
+ i=0;
+ do {
+ printf("swab16(0x%04"PRIx16") = 0x%04"PRIx16"\n",
+ ary16[i], swab16(ary16[i]));
+ if (swab16(ary16[i]) != ary16[i+1]) {
+ printf("Error!!! %04"PRIx16" != %04"PRIx16"\n",
+ swab16(ary16[i]), ary16[i+1]);
+ errors++;
+ }
+ if (swab16(ary16[i+1]) != ary16[i]) {
+ printf("Error!!! %04"PRIx16" != %04"PRIx16"\n",
+ swab16(ary16[i+1]), ary16[i]);
+ errors++;
+ }
+ i += 2;
+ } while (ary16[i] != 0);
+
+ printf("Testing swab32\n");
+ i = 0;
+ do {
+ printf("swab32(0x%08"PRIx32") = 0x%08"PRIx32"\n",
+ ary32[i], swab32(ary32[i]));
+ if (swab32(ary32[i]) != ary32[i+1]) {
+ printf("Error!!! %04"PRIx32" != %04"PRIx32"\n",
+ swab32(ary32[i]), ary32[i+1]);
+ errors++;
+ }
+ if (swab32(ary32[i+1]) != ary32[i]) {
+ printf("Error!!! %04"PRIx32" != %04"PRIx32"\n",
+ swab32(ary32[i+1]), ary32[i]);
+ errors++;
+ }
+ i += 2;
+ } while (ary32[i] != 0);
+
+ printf("Testing swab64\n");
+ i = 0;
+ do {
+ printf("swab64(0x%016"PRIx64") = 0x%016"PRIx64"\n",
+ ary64[i], swab64(ary64[i]));
+ if (swab64(ary64[i]) != ary64[i+1]) {
+ printf("Error!!! %016"PRIx64" != %016"PRIx64"\n",
+ swab64(ary64[i]), ary64[i+1]);
+ errors++;
+ }
+ if (swab64(ary64[i+1]) != ary64[i]) {
+ printf("Error!!! %016"PRIx64" != %016"PRIx64"\n",
+ swab64(ary64[i+1]), ary64[i]);
+ errors++;
+ }
+ i += 2;
+ } while (ary64[i] != 0);
+
+ if (!errors)
+ printf("No errors found in the byteswap implementation\n");
+
+ return errors;
+}
diff --git a/tests/helpers/test_enosys.c b/tests/helpers/test_enosys.c
new file mode 100644
index 0000000..9e93cc2
--- /dev/null
+++ b/tests/helpers/test_enosys.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <getopt.h>
+
+#include <linux/unistd.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/audit.h>
+#include <sys/prctl.h>
+
+#include "c.h"
+#include "audit-arch.h"
+#include "exitcodes.h"
+
+#define syscall_nr (offsetof(struct seccomp_data, nr))
+
+struct syscall {
+ const char *const name;
+ int number;
+};
+
+const struct syscall syscalls[] = {
+#ifdef __NR_move_mount
+ { "move_mount", __NR_move_mount },
+#endif
+#ifdef __NR_open_tree
+ { "open_tree", __NR_open_tree },
+#endif
+#ifdef __NR_fsopen
+ { "fsopen", __NR_fsopen },
+#endif
+#ifdef __NR_mount_setattr
+ { "mount_setattr", __NR_mount_setattr },
+#endif
+
+};
+
+int main(int argc, char **argv)
+{
+ int c;
+ size_t i;
+ bool found;
+ static const struct option longopts[] = {
+ { "syscall", required_argument, NULL, 's' },
+ { 0 }
+ };
+
+ bool blocked_syscalls[ARRAY_SIZE(syscalls)] = {};
+
+ while ((c = getopt_long (argc, argv, "s:", longopts, NULL)) != -1) {
+ switch (c) {
+ case 's':
+ found = 0;
+ for (i = 0; i < ARRAY_SIZE(syscalls); i++) {
+ if (strcmp(optarg, syscalls[i].name) == 0) {
+ blocked_syscalls[i] = true;
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ errx(EXIT_FAILURE, "Unknown syscall '%s'", optarg);
+ break;
+ default:
+ errx(EXIT_FAILURE, "Unknown option");
+ }
+ }
+
+ if (optind >= argc)
+ errx(EXIT_FAILURE, "No executable specified");
+
+#define N_FILTERS (ARRAY_SIZE(syscalls) + 3)
+
+ struct sock_filter filter[N_FILTERS] = {
+ [0] = BPF_STMT(BPF_LD | BPF_W | BPF_ABS, syscall_nr),
+
+ [N_FILTERS - 2] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ [N_FILTERS - 1] = BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | ENOSYS),
+ };
+
+ const struct sock_filter nop = BPF_JUMP(BPF_JMP | BPF_JA, 0, 0, 0);
+
+ for (i = 0; i < ARRAY_SIZE(syscalls); i++) {
+ if (blocked_syscalls[i]) {
+ const struct sock_filter block = BPF_JUMP(
+ BPF_JMP | BPF_JEQ | BPF_K,
+ syscalls[i].number,
+ N_FILTERS - 3 - i, 0);
+ filter[i + 1] = block;
+ } else {
+ filter[i + 1] = nop;
+ }
+ }
+
+ struct sock_fprog prog = {
+ .len = ARRAY_SIZE(filter),
+ .filter = filter,
+ };
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
+ err(EXIT_NOTSUPP, "prctl(PR_SET_NO_NEW_PRIVS)");
+
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
+ err(EXIT_NOTSUPP, "prctl(PR_SET_SECCOMP)");
+
+ if (execvp(argv[optind], argv + optind))
+ err(EXIT_NOTSUPP, "Could not exec");
+}
diff --git a/tests/helpers/test_mbsencode.c b/tests/helpers/test_mbsencode.c
new file mode 100644
index 0000000..44d88f9
--- /dev/null
+++ b/tests/helpers/test_mbsencode.c
@@ -0,0 +1,43 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (C) 2018 Vaclav Dolezal <vdolezal@redhat.com>
+ *
+ * This file is part of util-linux.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#include "mbsalign.h"
+
+int main(int argc, char **argv)
+{
+ int i = 1;
+ char *(*encode_fn)(const char *, size_t *) = mbs_safe_encode;
+
+ setlocale(LC_ALL, "");
+
+ if (i < argc) {
+ if (!strcmp(argv[i], "--safe")) {
+ i++;
+ encode_fn = mbs_safe_encode;
+ } else if (!strcmp(argv[i], "--invalid")) {
+ i++;
+ encode_fn = mbs_invalid_encode;
+ } else if (!strcmp(argv[i], "--")) {
+ i++;
+ }
+ }
+
+ for (; i < argc; i++) {
+ size_t width;
+ char *res;
+ res = encode_fn(argv[i], &width);
+ printf("%zi %s\n", width, res);
+ free(res);
+ }
+
+ return 0;
+}
diff --git a/tests/helpers/test_md5.c b/tests/helpers/test_md5.c
new file mode 100644
index 0000000..dc49d21
--- /dev/null
+++ b/tests/helpers/test_md5.c
@@ -0,0 +1,37 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <err.h>
+
+#include "md5.h"
+
+int main(void)
+{
+ int i, ret;
+ struct UL_MD5Context ctx;
+ unsigned char digest[UL_MD5LENGTH];
+ unsigned char buf[BUFSIZ];
+
+ ul_MD5Init( &ctx );
+
+ while(!feof(stdin) && !ferror(stdin)) {
+ ret = fread(buf, 1, sizeof(buf), stdin);
+ if (ret)
+ ul_MD5Update( &ctx, buf, ret );
+ }
+
+ if(freopen ("/dev/null", "r", stdin) == NULL)
+ err(EXIT_FAILURE, "stdin->null failed!");
+
+ ul_MD5Final( digest, &ctx );
+
+ for (i = 0; i < UL_MD5LENGTH; i++)
+ printf( "%02x", digest[i] );
+ printf("\n");
+ return 0;
+}
diff --git a/tests/helpers/test_mkfds.c b/tests/helpers/test_mkfds.c
new file mode 100644
index 0000000..194c175
--- /dev/null
+++ b/tests/helpers/test_mkfds.c
@@ -0,0 +1,2665 @@
+/*
+ * test_mkfds - make various file descriptors
+ *
+ * Written by Masatake YAMATO <yamato@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/netlink.h>
+#include <linux/sockios.h> /* SIOCGSKNS */
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/user.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 _U_ __attribute__((__unused__))
+
+static int pidfd_open(pid_t pid, unsigned int flags);
+
+static void __attribute__((__noreturn__)) usage(FILE *out, int status)
+{
+ fputs("\nUsage:\n", out);
+ fprintf(out, " %s [options] FACTORY FD... [PARAM=VAL...]\n", program_invocation_short_name);
+
+ fputs("\nOptions:\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(" -c, --dont-pause don't pause after making fd(s)\n", out);
+
+ fputs("\n", out);
+ fputs("Examples:\n", out);
+ fprintf(out, "Using 3, open /etc/group:\n\n $ %s ro-regular-file 3 file=/etc/group\n\n",
+ program_invocation_short_name);
+ fprintf(out, "Using 3 and 4, make a pipe:\n\n $ %s pipe-no-fork 3 4\n\n",
+ program_invocation_short_name);
+
+ exit(status);
+}
+
+union value {
+ const char *string;
+ long integer;
+ unsigned long uinteger;
+ bool boolean;
+};
+
+enum ptype {
+ PTYPE_STRING,
+ PTYPE_INTEGER,
+ PTYPE_UINTEGER,
+ PTYPE_BOOLEAN,
+};
+
+struct ptype_class {
+ const char *name;
+
+ /* Covert to a string representation.
+ * A caller must free the returned value with free(3) after using. */
+ char *(*sprint)(const union value *value);
+
+ /* Convert from a string. If ARG is NULL, use DEFV instead.
+ * A caller must free the returned value with the free method
+ * after using. */
+ union value (*read)(const char *arg, const union value *defv);
+
+ /* Free the value returned from the read method. */
+ void (*free)(union value value);
+};
+
+#define ARG_STRING(A) (A.v.string)
+#define ARG_INTEGER(A) (A.v.integer)
+#define ARG_UINTEGER(A) (A.v.uinteger)
+#define ARG_BOOLEAN(A) (A.v.boolean)
+struct arg {
+ union value v;
+ void (*free)(union value value);
+};
+
+struct parameter {
+ const char *name;
+ const enum ptype type;
+ const char *desc;
+ union value defv; /* Default value */
+};
+
+static char *string_sprint(const union value *value)
+{
+ return xstrdup(value->string);
+}
+
+static union value string_read(const char *arg, const union value *defv)
+{
+ return (union value){ .string = xstrdup(arg?: defv->string) };
+}
+
+static void string_free(union value value)
+{
+ free((void *)value.string);
+}
+
+static char *integer_sprint(const union value *value)
+{
+ char *str = NULL;
+ xasprintf(&str, "%ld", value->integer);
+ return str;
+}
+
+static union value integer_read(const char *arg, const union value *defv)
+{
+ char *ep;
+ union value r;
+
+ if (!arg)
+ return *defv;
+
+ errno = 0;
+ r.integer = strtol(arg, &ep, 10);
+ if (errno)
+ err(EXIT_FAILURE, "fail to make a number from %s", arg);
+ else if (*ep != '\0')
+ errx(EXIT_FAILURE, "garbage at the end of number: %s", arg);
+ return r;
+}
+
+static void integer_free(union value value _U_)
+{
+ /* Do nothing */
+}
+
+static char *uinteger_sprint(const union value *value)
+{
+ char *str = NULL;
+ xasprintf(&str, "%lu", value->uinteger);
+ return str;
+}
+
+static union value uinteger_read(const char *arg, const union value *defv)
+{
+ char *ep;
+ union value r;
+
+ if (!arg)
+ return *defv;
+
+ errno = 0;
+ r.uinteger = strtoul(arg, &ep, 10);
+ if (errno)
+ err(EXIT_FAILURE, "fail to make a number from %s", arg);
+ else if (*ep != '\0')
+ errx(EXIT_FAILURE, "garbage at the end of number: %s", arg);
+ return r;
+}
+
+static void uinteger_free(union value value _U_)
+{
+ /* Do nothing */
+}
+
+static char *boolean_sprint(const union value *value)
+{
+ return xstrdup(value->boolean? "true": "false");
+}
+
+static union value boolean_read(const char *arg, const union value *defv)
+{
+ union value r;
+
+ if (!arg)
+ return *defv;
+
+ if (strcasecmp(arg, "true") == 0
+ || strcmp(arg, "1") == 0
+ || strcasecmp(arg, "yes") == 0
+ || strcasecmp(arg, "y") == 0)
+ r.boolean = true;
+ else
+ r.boolean = false;
+ return r;
+}
+
+static void boolean_free(union value value _U_)
+{
+ /* Do nothing */
+}
+
+struct ptype_class ptype_classes [] = {
+ [PTYPE_STRING] = {
+ .name = "string",
+ .sprint = string_sprint,
+ .read = string_read,
+ .free = string_free,
+ },
+ [PTYPE_INTEGER] = {
+ .name = "integer",
+ .sprint = integer_sprint,
+ .read = integer_read,
+ .free = integer_free,
+ },
+ [PTYPE_UINTEGER] = {
+ .name = "uinteger",
+ .sprint = uinteger_sprint,
+ .read = uinteger_read,
+ .free = uinteger_free,
+ },
+ [PTYPE_BOOLEAN] = {
+ .name = "boolean",
+ .sprint = boolean_sprint,
+ .read = boolean_read,
+ .free = boolean_free,
+ },
+};
+
+static struct arg decode_arg(const char *pname,
+ const struct parameter *parameters,
+ int argc, char **argv)
+{
+ char *v = NULL;
+ size_t len = strlen(pname);
+ const struct parameter *p = NULL;
+ struct arg arg;
+
+ while (parameters->name) {
+ if (strcmp(pname, parameters->name) == 0) {
+ p = parameters;
+ break;
+ }
+ parameters++;
+ }
+ if (p == NULL)
+ errx(EXIT_FAILURE, "no such parameter: %s", pname);
+
+ for (int i = 0; i < argc; i++) {
+ if (strncmp(pname, argv[i], len) == 0) {
+ v = argv[i] + len;
+ if (*v == '=') {
+ v++;
+ break;
+ } else if (*v == '\0')
+ errx(EXIT_FAILURE,
+ "no value given for \"%s\" parameter",
+ pname);
+ else
+ v = NULL;
+ }
+ }
+ arg.v = ptype_classes [p->type].read (v, &p->defv);
+ arg.free = ptype_classes [p->type].free;
+ return arg;
+}
+
+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
+ int N; /* the number of fds this factory makes */
+ int EX_N; /* fds made optionally */
+ void *(*make)(const struct factory *, struct fdesc[], int, char **);
+ void (*free)(const struct factory *, void *);
+ void (*report)(const struct factory *, void *, FILE *);
+ const struct parameter * params;
+};
+
+static void close_fdesc(int fd, void *data _U_)
+{
+ close(fd);
+}
+
+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);
+
+ int fd = open(ARG_STRING(file), O_RDONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, "failed to open: %s", ARG_STRING(file));
+ free_arg(&file);
+
+ if (ARG_INTEGER(offset) != 0) {
+ if (lseek(fd, (off_t)ARG_INTEGER(offset), SEEK_CUR) < 0) {
+ int e = errno;
+ close(fd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to seek 0 -> %ld", ARG_INTEGER(offset));
+ }
+ }
+ free_arg(&offset);
+
+ 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
+ };
+
+ return NULL;
+}
+
+static void *make_pipe(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ int pd[2];
+ int nonblock_flags[2] = {0, 0};
+ struct arg nonblock = decode_arg("nonblock", factory->params, argc, argv);
+ if (strlen(ARG_STRING(nonblock)) != 2) {
+ errx(EXIT_FAILURE, "string value for %s has unexpected length: %s",
+ "nonblock", ARG_STRING(nonblock));
+ }
+
+ /* Make extra pipe descriptors for making pipe objects connected
+ * with fds more than 2.
+ * See https://github.com/util-linux/util-linux/pull/1622
+ * about the background of the requirement. */
+ struct arg rdup = decode_arg("rdup", factory->params, argc, argv);
+ struct arg wdup = decode_arg("wdup", factory->params, argc, argv);
+ int xpd[2];
+ xpd [0] = ARG_INTEGER(rdup);
+ xpd [1] = ARG_INTEGER(wdup);
+
+ for (int i = 0; i < 2; i++) {
+ if (ARG_STRING(nonblock)[i] == '-')
+ continue;
+ if ((i == 0 && ARG_STRING(nonblock)[i] == 'r')
+ || (i == 1 && ARG_STRING(nonblock)[i] == 'w'))
+ nonblock_flags[i] = 1;
+ else
+ errx(EXIT_FAILURE, "unexpected value %c for the %s fd of %s",
+ ARG_STRING(nonblock)[i],
+ (i == 0)? "read": "write",
+ "nonblock");
+ }
+ free_arg(&nonblock);
+
+ if (pipe(pd) < 0)
+ err(EXIT_FAILURE, "failed to make pipe");
+
+ for (int i = 0; i < 2; i++) {
+ if (nonblock_flags[i]) {
+ int flags = fcntl(pd[i], F_GETFL);
+ if (fcntl(pd[i], F_SETFL, flags|O_NONBLOCK) < 0) {
+ int e = errno;
+ close(pd[0]);
+ close(pd[1]);
+ errno = e;
+ errx(EXIT_FAILURE, "failed to set NONBLOCK flag to the %s fd",
+ (i == 0)? "read": "write");
+ }
+ }
+ }
+
+ for (int i = 0; i < 2; i++) {
+ if (pd[i] != fdescs[i].fd) {
+ if (dup2(pd[i], fdescs[i].fd) < 0) {
+ int e = errno;
+ close(pd[0]);
+ close(pd[1]);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d",
+ pd[i], fdescs[i].fd);
+ }
+ close(pd[i]);
+ }
+ fdescs[i] = (struct fdesc){
+ .fd = fdescs[i].fd,
+ .close = close_fdesc,
+ .data = NULL
+ };
+ }
+
+ /* Make extra pipe descriptors. */
+ for (int i = 0; i < 2; i++) {
+ if (xpd[i] >= 0) {
+ if (dup2(fdescs[i].fd, xpd[i]) < 0) {
+ int e = errno;
+ close(fdescs[0].fd);
+ close(fdescs[1].fd);
+ if (i > 0 && xpd[0] >= 0)
+ close(xpd[0]);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d",
+ fdescs[i].fd, xpd[i]);
+ }
+ fdescs[i + 2] = (struct fdesc){
+ .fd = xpd[i],
+ .close = close_fdesc,
+ .data = NULL
+ };
+ }
+ }
+
+ return NULL;
+}
+
+static void close_dir(int fd, void *data)
+{
+ DIR *dp = data;
+ if (dp)
+ closedir(dp);
+ else
+ close_fdesc(fd, NULL);
+}
+
+static void *open_directory(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg dir = decode_arg("dir", factory->params, argc, argv);
+ struct arg dentries = decode_arg("dentries", factory->params, argc, argv);
+ DIR *dp = NULL;
+
+ int fd = open(ARG_STRING(dir), O_RDONLY|O_DIRECTORY);
+ if (fd < 0)
+ err(EXIT_FAILURE, "failed to open: %s", ARG_STRING(dir));
+ free_arg(&dir);
+
+ 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);
+ }
+
+ if (ARG_INTEGER(dentries) > 0) {
+ dp = fdopendir(fdescs[0].fd);
+ if (dp == NULL) {
+ int e = errno;
+ close(fdescs[0].fd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to make DIR* from fd: %s", ARG_STRING(dir));
+ }
+ for (int i = 0; i < ARG_INTEGER(dentries); i++) {
+ struct dirent *d = readdir(dp);
+ if (!d) {
+ int e = errno;
+ closedir(dp);
+ errno = e;
+ err(EXIT_FAILURE, "failed in readdir(3)");
+ }
+ }
+ }
+ free_arg(&dentries);
+
+ fdescs[0] = (struct fdesc){
+ .fd = fdescs[0].fd,
+ .close = close_dir,
+ .data = dp
+ };
+
+ return NULL;
+}
+
+static void *open_rw_chrdev(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg chrdev = decode_arg("chrdev", factory->params, argc, argv);
+ int fd = open(ARG_STRING(chrdev), O_RDWR);
+ if (fd < 0)
+ err(EXIT_FAILURE, "failed to open: %s", ARG_STRING(chrdev));
+ free_arg(&chrdev);
+
+ 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
+ };
+
+ return NULL;
+}
+
+static void *make_socketpair(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ int sd[2];
+ struct arg socktype = decode_arg("socktype", factory->params, argc, argv);
+ int isocktype;
+ if (strcmp(ARG_STRING(socktype), "STREAM") == 0)
+ isocktype = SOCK_STREAM;
+ else if (strcmp(ARG_STRING(socktype), "DGRAM") == 0)
+ isocktype = SOCK_DGRAM;
+ else if (strcmp(ARG_STRING(socktype), "SEQPACKET") == 0)
+ isocktype = SOCK_SEQPACKET;
+ else
+ errx(EXIT_FAILURE,
+ "unknown socket type for socketpair(AF_UNIX,...): %s",
+ ARG_STRING(socktype));
+ free_arg(&socktype);
+
+ if (socketpair(AF_UNIX, isocktype, 0, sd) < 0)
+ err(EXIT_FAILURE, "failed to make socket pair");
+
+ for (int i = 0; i < 2; i++) {
+ if (sd[i] != fdescs[i].fd) {
+ if (dup2(sd[i], fdescs[i].fd) < 0) {
+ int e = errno;
+ close(sd[0]);
+ close(sd[1]);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d",
+ sd[i], fdescs[i].fd);
+ }
+ close(sd[i]);
+ }
+ fdescs[i] = (struct fdesc){
+ .fd = fdescs[i].fd,
+ .close = close_fdesc,
+ .data = NULL
+ };
+ }
+
+ return NULL;
+}
+
+static void *open_with_opath(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg path = decode_arg("path", factory->params, argc, argv);
+ int fd = open(ARG_STRING(path), O_PATH|O_NOFOLLOW);
+ if (fd < 0)
+ err(EXIT_FAILURE, "failed to open with O_PATH: %s", ARG_STRING(path));
+ free_arg(&path);
+
+ 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
+ };
+
+ return NULL;
+}
+
+static void *open_ro_blkdev(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg blkdev = decode_arg("blkdev", factory->params, argc, argv);
+ int fd = open(ARG_STRING(blkdev), O_RDONLY);
+ if (fd < 0)
+ err(EXIT_FAILURE, "failed to open: %s", ARG_STRING(blkdev));
+ free_arg(&blkdev);
+
+ 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,
+ };
+
+ return NULL;
+}
+
+static int make_packet_socket(int socktype, const char *interface)
+{
+ int sd;
+ struct sockaddr_ll addr;
+
+ sd = socket(AF_PACKET, socktype, htons(ETH_P_ALL));
+ if (sd < 0)
+ err(EXIT_FAILURE, "failed to make a socket with AF_PACKET");
+
+ if (interface == NULL)
+ return sd; /* Just making a socket */
+
+ memset(&addr, 0, sizeof(struct sockaddr_ll));
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = if_nametoindex(interface);
+ if (addr.sll_ifindex == 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE,
+ "failed to get the interface index for %s", interface);
+ }
+ if (bind(sd, (struct sockaddr *)&addr, sizeof(struct sockaddr_ll)) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE,
+ "failed to get the interface index for %s", interface);
+ }
+
+ return sd;
+}
+
+struct munmap_data {
+ void *ptr;
+ size_t len;
+};
+
+static void close_fdesc_after_munmap(int fd, void *data)
+{
+ struct munmap_data *munmap_data = data;
+ munmap(munmap_data->ptr, munmap_data->len);
+ free(data);
+ close(fd);
+}
+
+static void *make_mmapped_packet_socket(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ int sd;
+ struct arg socktype = decode_arg("socktype", factory->params, argc, argv);
+ struct arg interface = decode_arg("interface", factory->params, argc, argv);
+
+ int isocktype;
+ const char *sinterface;
+ struct tpacket_req req;
+ struct munmap_data *munmap_data;
+
+ if (strcmp(ARG_STRING(socktype), "DGRAM") == 0)
+ isocktype = SOCK_DGRAM;
+ else if (strcmp(ARG_STRING(socktype), "RAW") == 0)
+ isocktype = SOCK_RAW;
+ else
+ errx(EXIT_FAILURE,
+ "unknown socket type for socket(AF_PACKET,...): %s",
+ ARG_STRING(socktype));
+ free_arg(&socktype);
+
+ sinterface = ARG_STRING(interface);
+ sd = make_packet_socket(isocktype, sinterface);
+ free_arg(&interface);
+
+ /* Specify the spec of ring buffers.
+ *
+ * ref.
+ * - linux/Documentation/networking/packet_mmap.rst
+ * - https://sites.google.com/site/packetmmap/home
+ */
+ req.tp_block_size = getpagesize();
+ req.tp_frame_size = getpagesize();
+ req.tp_block_nr = 1;
+ req.tp_frame_nr = 1;
+ if (setsockopt(sd, SOL_PACKET, PACKET_TX_RING, (char *)&req, sizeof(req)) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err((errno == ENOPROTOOPT? EXIT_ENOPROTOOPT: EXIT_FAILURE),
+ "failed to specify a buffer spec to a packet socket");
+ }
+
+ 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) {
+ int e = errno;
+ close(sd);
+ free(munmap_data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to do mmap a packet socket");
+ }
+
+ if (sd != fdescs[0].fd) {
+ if (dup2(sd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(sd);
+ munmap(munmap_data->ptr, munmap_data->len);
+ free(munmap_data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[0].fd);
+ }
+ close(sd);
+ }
+
+ fdescs[0] = (struct fdesc){
+ .fd = fdescs[0].fd,
+ .close = close_fdesc_after_munmap,
+ .data = munmap_data,
+ };
+
+ return NULL;
+}
+
+static void *make_pidfd(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg target_pid = decode_arg("target-pid", factory->params, argc, argv);
+ pid_t pid = ARG_INTEGER(target_pid);
+
+ int fd = pidfd_open(pid, 0);
+ if (fd < 0)
+ err((errno == ENOSYS? EXIT_ENOSYS: EXIT_FAILURE),
+ "failed in pidfd_open(%d)", (int)pid);
+ free_arg(&target_pid);
+
+ 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
+ };
+
+ return NULL;
+}
+
+static void *make_inotify_fd(const struct factory *factory _U_, struct fdesc fdescs[],
+ int argc _U_, char ** argv _U_)
+{
+ int fd = inotify_init();
+ if (fd < 0)
+ err(EXIT_FAILURE, "failed in inotify_init()");
+
+ 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
+ };
+
+ return NULL;
+}
+
+static void close_unix_socket(int fd, void *data)
+{
+ char *path = data;
+ close(fd);
+ if (path) {
+ unlink(path);
+ free(path);
+ }
+}
+
+static void *make_unix_stream_core(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv, int type, const char *typestr)
+{
+ struct arg path = decode_arg("path", factory->params, argc, argv);
+ const char *spath = ARG_STRING(path);
+
+ struct arg backlog = decode_arg("backlog", factory->params, argc, argv);
+ int ibacklog = ARG_INTEGER(path);
+
+ struct arg abstract = decode_arg("abstract", factory->params, argc, argv);
+ bool babstract = ARG_BOOLEAN(abstract);
+
+ struct arg server_shutdown = decode_arg("server-shutdown", factory->params, argc, argv);
+ int iserver_shutdown = ARG_INTEGER(server_shutdown);
+ struct arg client_shutdown = decode_arg("client-shutdown", factory->params, argc, argv);
+ int iclient_shutdown = ARG_INTEGER(client_shutdown);
+
+ int ssd, csd, asd; /* server, client, and accepted socket descriptors */
+ struct sockaddr_un un;
+ size_t un_len = sizeof(un);
+
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ if (babstract) {
+ strncpy(un.sun_path + 1, spath, sizeof(un.sun_path) - 1 - 1);
+ size_t pathlen = strlen(spath);
+ if (sizeof(un.sun_path) - 1 > pathlen)
+ un_len = sizeof(un) - sizeof(un.sun_path) + 1 + pathlen;
+ } else
+ strncpy(un.sun_path, spath, sizeof(un.sun_path) - 1 );
+
+ free_arg(&client_shutdown);
+ free_arg(&server_shutdown);
+ free_arg(&abstract);
+ free_arg(&backlog);
+ free_arg(&path);
+
+ if (iserver_shutdown < 0 || iserver_shutdown > 3)
+ errx(EXIT_FAILURE, "the server shudown specification in unexpected range");
+ if (iclient_shutdown < 0 || iclient_shutdown > 3)
+ errx(EXIT_FAILURE, "the client shudown specification in unexpected range");
+
+ ssd = socket(AF_UNIX, type, 0);
+ if (ssd < 0)
+ err(EXIT_FAILURE,
+ "failed to make a socket with AF_UNIX + SOCK_%s (server side)", typestr);
+ if (ssd != fdescs[0].fd) {
+ if (dup2(ssd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
+ }
+ close(ssd);
+ ssd = fdescs[0].fd;
+ }
+
+ fdescs[0] = (struct fdesc){
+ .fd = fdescs[0].fd,
+ .close = close_unix_socket,
+ .data = NULL,
+ };
+
+ if (!babstract)
+ unlink(un.sun_path);
+ if (bind(ssd, (const struct sockaddr *)&un, un_len) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to bind a socket for listening");
+ }
+
+ if (!babstract)
+ fdescs[0].data = xstrdup(un.sun_path);
+ if (listen(ssd, ibacklog) < 0) {
+ int e = errno;
+ close_unix_socket(ssd, fdescs[0].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to listen a socket");
+ }
+
+ csd = socket(AF_UNIX, type, 0);
+ if (csd < 0)
+ err(EXIT_FAILURE,
+ "failed to make a socket with AF_UNIX + SOCK_%s (client side)", typestr);
+ if (csd != fdescs[1].fd) {
+ if (dup2(csd, fdescs[1].fd) < 0) {
+ int e = errno;
+ close(csd);
+ close_unix_socket(ssd, fdescs[0].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", csd, fdescs[1].fd);
+ }
+ close(csd);
+ csd = fdescs[1].fd;
+ }
+
+ fdescs[1] = (struct fdesc){
+ .fd = fdescs[1].fd,
+ .close = close_fdesc,
+ .data = NULL,
+ };
+
+ if (connect(csd, (const struct sockaddr *)&un, un_len) < 0) {
+ int e = errno;
+ close_fdesc(csd, NULL);
+ close_unix_socket(ssd, fdescs[0].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to connect a socket to the listening socket");
+ }
+
+ if (!babstract)
+ unlink(un.sun_path);
+
+ asd = accept(ssd, NULL, NULL);
+ if (asd < 0) {
+ int e = errno;
+ close_fdesc(csd, NULL);
+ close_unix_socket(ssd, fdescs[0].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to accept a socket from the listening socket");
+ }
+ if (asd != fdescs[2].fd) {
+ if (dup2(asd, fdescs[2].fd) < 0) {
+ int e = errno;
+ close(asd);
+ close_fdesc(csd, NULL);
+ close_unix_socket(ssd, fdescs[0].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", asd, fdescs[2].fd);
+ }
+ close(asd);
+ asd = fdescs[2].fd;
+ }
+
+ if (iserver_shutdown & (1 << 0))
+ shutdown(asd, SHUT_RD);
+ if (iserver_shutdown & (1 << 1))
+ shutdown(asd, SHUT_WR);
+ if (iclient_shutdown & (1 << 0))
+ shutdown(csd, SHUT_RD);
+ if (iclient_shutdown & (1 << 1))
+ shutdown(csd, SHUT_WR);
+
+ return NULL;
+}
+
+static void *make_unix_stream(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg type = decode_arg("type", factory->params, argc, argv);
+ const char *stype = ARG_STRING(type);
+
+ int typesym;
+ const char *typestr;
+
+ if (strcmp(stype, "stream") == 0) {
+ typesym = SOCK_STREAM;
+ typestr = "STREAM";
+ } else if (strcmp(stype, "seqpacket") == 0) {
+ typesym = SOCK_SEQPACKET;
+ typestr = "SEQPACKET";
+ } else
+ errx(EXIT_FAILURE, "unknown unix socket type: %s", stype);
+
+ free_arg(&type);
+
+ return make_unix_stream_core(factory, fdescs, argc, argv, typesym, typestr);
+}
+
+static void *make_unix_dgram(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg path = decode_arg("path", factory->params, argc, argv);
+ const char *spath = ARG_STRING(path);
+
+ struct arg abstract = decode_arg("abstract", factory->params, argc, argv);
+ bool babstract = ARG_BOOLEAN(abstract);
+
+ int ssd, csd; /* server and client socket descriptors */
+
+ struct sockaddr_un un;
+ size_t un_len = sizeof(un);
+
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ if (babstract) {
+ strncpy(un.sun_path + 1, spath, sizeof(un.sun_path) - 1 - 1);
+ size_t pathlen = strlen(spath);
+ if (sizeof(un.sun_path) - 1 > pathlen)
+ un_len = sizeof(un) - sizeof(un.sun_path) + 1 + pathlen;
+ } else
+ strncpy(un.sun_path, spath, sizeof(un.sun_path) - 1 );
+
+ free_arg(&abstract);
+ free_arg(&path);
+
+ ssd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (ssd < 0)
+ err(EXIT_FAILURE,
+ "failed to make a socket with AF_UNIX + SOCK_DGRAM (server side)");
+ if (ssd != fdescs[0].fd) {
+ if (dup2(ssd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
+ }
+ close(ssd);
+ ssd = fdescs[0].fd;
+ }
+
+ fdescs[0] = (struct fdesc){
+ .fd = fdescs[0].fd,
+ .close = close_unix_socket,
+ .data = NULL,
+ };
+
+ if (!babstract)
+ unlink(un.sun_path);
+ if (bind(ssd, (const struct sockaddr *)&un, un_len) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to bind a socket for server");
+ }
+
+ if (!babstract)
+ fdescs[0].data = xstrdup(un.sun_path);
+ csd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (csd < 0)
+ err(EXIT_FAILURE,
+ "failed to make a socket with AF_UNIX + SOCK_DGRAM (client side)");
+ if (csd != fdescs[1].fd) {
+ if (dup2(csd, fdescs[1].fd) < 0) {
+ int e = errno;
+ close(csd);
+ close_unix_socket(ssd, fdescs[0].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", csd, fdescs[1].fd);
+ }
+ close(csd);
+ csd = fdescs[1].fd;
+ }
+
+ fdescs[1] = (struct fdesc){
+ .fd = fdescs[1].fd,
+ .close = close_fdesc,
+ .data = NULL,
+ };
+
+ if (connect(csd, (const struct sockaddr *)&un, un_len) < 0) {
+ int e = errno;
+ close_fdesc(csd, NULL);
+ close_unix_socket(ssd, fdescs[0].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to connect a socket to the server socket");
+ }
+
+ if (!babstract)
+ unlink(un.sun_path);
+
+ return NULL;
+}
+
+static void *make_unix_in_new_netns(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg type = decode_arg("type", factory->params, argc, argv);
+ const char *stype = ARG_STRING(type);
+
+ struct arg path = decode_arg("path", factory->params, argc, argv);
+ const char *spath = ARG_STRING(path);
+
+ struct arg abstract = decode_arg("abstract", factory->params, argc, argv);
+ bool babstract = ARG_BOOLEAN(abstract);
+
+ int typesym;
+ const char *typestr;
+
+ struct sockaddr_un un;
+ size_t un_len = sizeof(un);
+
+ int self_netns, tmp_netns, sd;
+
+ if (strcmp(stype, "stream") == 0) {
+ typesym = SOCK_STREAM;
+ typestr = "STREAM";
+ } else if (strcmp(stype, "seqpacket") == 0) {
+ typesym = SOCK_SEQPACKET;
+ typestr = "SEQPACKET";
+ } else if (strcmp(stype, "dgram") == 0) {
+ typesym = SOCK_DGRAM;
+ typestr = "DGRAM";
+ } else {
+ free_arg(&abstract);
+ free_arg(&path);
+ free_arg(&type);
+ errx(EXIT_FAILURE, "unknown unix socket type: %s", stype);
+ }
+
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ if (babstract) {
+ strncpy(un.sun_path + 1, spath, sizeof(un.sun_path) - 1 - 1);
+ size_t pathlen = strlen(spath);
+ if (sizeof(un.sun_path) - 1 > pathlen)
+ un_len = sizeof(un) - sizeof(un.sun_path) + 1 + pathlen;
+ } else
+ strncpy(un.sun_path, spath, sizeof(un.sun_path) - 1 );
+
+ free_arg(&abstract);
+ free_arg(&path);
+ free_arg(&type);
+
+ self_netns = open("/proc/self/ns/net", O_RDONLY);
+ if (self_netns < 0)
+ err(EXIT_FAILURE, "failed to open /proc/self/ns/net");
+ if (self_netns != fdescs[0].fd) {
+ if (dup2(self_netns, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(self_netns);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", self_netns, fdescs[0].fd);
+ }
+ close(self_netns);
+ self_netns = fdescs[0].fd;
+ }
+
+ fdescs[0] = (struct fdesc){
+ .fd = fdescs[0].fd,
+ .close = close_fdesc,
+ .data = NULL,
+ };
+
+ if (unshare(CLONE_NEWNET) < 0) {
+ int e = errno;
+ close_fdesc(self_netns, NULL);
+ errno = e;
+ err((errno == EPERM? EXIT_EPERM: EXIT_FAILURE),
+ "failed in unshare");
+ }
+
+ tmp_netns = open("/proc/self/ns/net", O_RDONLY);
+ if (tmp_netns < 0) {
+ int e = errno;
+ close_fdesc(self_netns, NULL);
+ errno = e;
+ err(EXIT_FAILURE, "failed to open /proc/self/ns/net for the new netns");
+ }
+ if (tmp_netns != fdescs[1].fd) {
+ if (dup2(tmp_netns, fdescs[1].fd) < 0) {
+ int e = errno;
+ close_fdesc(self_netns, NULL);
+ close(tmp_netns);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", tmp_netns, fdescs[1].fd);
+ }
+ close(tmp_netns);
+ tmp_netns = fdescs[1].fd;
+ }
+
+ fdescs[1] = (struct fdesc){
+ .fd = fdescs[1].fd,
+ .close = close_fdesc,
+ .data = NULL,
+ };
+
+ sd = socket(AF_UNIX, typesym, 0);
+ if (sd < 0) {
+ int e = errno;
+ close_fdesc(self_netns, NULL);
+ close_fdesc(tmp_netns, NULL);
+ errno = e;
+ err(EXIT_FAILURE,
+ "failed to make a socket with AF_UNIX + SOCK_%s",
+ typestr);
+ }
+
+ if (sd != fdescs[2].fd) {
+ if (dup2(sd, fdescs[2].fd) < 0) {
+ int e = errno;
+ close_fdesc(self_netns, NULL);
+ close_fdesc(tmp_netns, NULL);
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[2].fd);
+ }
+ close(sd);
+ sd = fdescs[2].fd;
+ }
+
+ fdescs[2] = (struct fdesc){
+ .fd = fdescs[2].fd,
+ .close = close_unix_socket,
+ .data = NULL,
+ };
+
+ if (!babstract)
+ unlink(un.sun_path);
+ if (bind(sd, (const struct sockaddr *)&un, un_len) < 0) {
+ int e = errno;
+ close_fdesc(self_netns, NULL);
+ close_fdesc(tmp_netns, NULL);
+ close_unix_socket(sd, NULL);
+ errno = e;
+ err(EXIT_FAILURE, "failed to bind a socket");
+ }
+
+ if (!babstract)
+ fdescs[2].data = xstrdup(un.sun_path);
+
+ if (typesym != SOCK_DGRAM) {
+ if (listen(sd, 1) < 0) {
+ int e = errno;
+ close_fdesc(self_netns, NULL);
+ close_fdesc(tmp_netns, NULL);
+ close_unix_socket(sd, fdescs[2].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to listen a socket");
+ }
+ }
+
+ if (setns(self_netns, CLONE_NEWNET) < 0) {
+ int e = errno;
+ close_fdesc(self_netns, NULL);
+ close_fdesc(tmp_netns, NULL);
+ close_unix_socket(sd, fdescs[2].data);
+ errno = e;
+ err(EXIT_FAILURE, "failed to swich back to the original net namespace");
+ }
+
+ return NULL;
+}
+
+static void *make_tcp_common(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv,
+ int family,
+ void (*init_addr)(struct sockaddr *, unsigned short),
+ size_t addr_size,
+ struct sockaddr * sin, struct sockaddr * cin)
+{
+ struct arg server_port = decode_arg("server-port", factory->params, argc, argv);
+ unsigned short iserver_port = (unsigned short)ARG_INTEGER(server_port);
+ struct arg client_port = decode_arg("client-port", factory->params, argc, argv);
+ unsigned short iclient_port = (unsigned short)ARG_INTEGER(client_port);
+
+ int ssd, csd, asd;
+
+ const int y = 1;
+
+ free_arg(&server_port);
+ free_arg(&client_port);
+
+ ssd = socket(family, SOCK_STREAM, 0);
+ if (ssd < 0)
+ err(EXIT_FAILURE,
+ "failed to make a tcp socket for listening");
+
+ if (setsockopt(ssd, SOL_SOCKET,
+ SO_REUSEADDR, (const char *)&y, sizeof(y)) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to setsockopt(SO_REUSEADDR)");
+ }
+
+ if (ssd != fdescs[0].fd) {
+ if (dup2(ssd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
+ }
+ close(ssd);
+ ssd = fdescs[0].fd;
+ }
+
+ init_addr(sin, iserver_port);
+ if (bind(ssd, sin, addr_size) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to bind a listening socket");
+ }
+
+ if (listen(ssd, 1) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to listen a socket");
+ }
+
+ csd = socket(family, SOCK_STREAM, 0);
+ if (csd < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE,
+ "failed to make a tcp client socket");
+ }
+
+ if (setsockopt(csd, SOL_SOCKET,
+ SO_REUSEADDR, (const char *)&y, sizeof(y)) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to setsockopt(SO_REUSEADDR)");
+ }
+
+ if (csd != fdescs[1].fd) {
+ if (dup2(csd, fdescs[1].fd) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", csd, fdescs[1].fd);
+ }
+ close(csd);
+ csd = fdescs[1].fd;
+ }
+
+ init_addr(cin, iclient_port);
+ if (bind(csd, cin, addr_size) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to bind a client socket");
+ }
+
+ if (connect(csd, sin, addr_size) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to connect a client socket to the server socket");
+ }
+
+ asd = accept(ssd, NULL, NULL);
+ if (asd < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to accept a socket from the listening socket");
+ }
+ if (asd != fdescs[2].fd) {
+ if (dup2(asd, fdescs[2].fd) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", asd, fdescs[2].fd);
+ }
+ close(asd);
+ asd = fdescs[2].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,
+ };
+ fdescs[2] = (struct fdesc) {
+ .fd = fdescs[2].fd,
+ .close = close_fdesc,
+ .data = NULL,
+ };
+
+ return NULL;
+}
+
+static void tcp_init_addr(struct sockaddr *addr, unsigned short port)
+{
+ struct sockaddr_in *in = (struct sockaddr_in *)addr;
+ memset(in, 0, sizeof(*in));
+ in->sin_family = AF_INET;
+ in->sin_port = htons(port);
+ in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+}
+
+static void *make_tcp(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in sin, cin;
+ return make_tcp_common(factory, fdescs, argc, argv,
+ AF_INET,
+ tcp_init_addr, sizeof(sin),
+ (struct sockaddr *)&sin, (struct sockaddr *)&cin);
+}
+
+static void *make_udp_common(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv,
+ int family,
+ void (*init_addr)(struct sockaddr *, unsigned short),
+ size_t addr_size,
+ struct sockaddr * sin, struct sockaddr * cin)
+{
+ struct arg lite = decode_arg("lite", factory->params, argc, argv);
+ bool blite = ARG_BOOLEAN(lite);
+
+ struct arg server_port = decode_arg("server-port", factory->params, argc, argv);
+ unsigned short iserver_port = (unsigned short)ARG_INTEGER(server_port);
+ struct arg client_port = decode_arg("client-port", factory->params, argc, argv);
+ unsigned short iclient_port = (unsigned short)ARG_INTEGER(client_port);
+
+ struct arg server_do_bind = decode_arg("server-do-bind", factory->params, argc, argv);
+ bool bserver_do_bind = ARG_BOOLEAN(server_do_bind);
+ struct arg client_do_bind = decode_arg("client-do-bind", factory->params, argc, argv);
+ bool bclient_do_bind = ARG_BOOLEAN(client_do_bind);
+ struct arg client_do_connect = decode_arg("client-do-connect", factory->params, argc, argv);
+ bool bclient_do_connect = ARG_BOOLEAN(client_do_connect);
+
+ int ssd, csd;
+
+ const int y = 1;
+
+ free_arg(&client_do_connect);
+ free_arg(&client_do_bind);
+ free_arg(&server_do_bind);
+ free_arg(&server_port);
+ free_arg(&client_port);
+ free_arg(&lite);
+
+ ssd = socket(family, SOCK_DGRAM, blite? IPPROTO_UDPLITE: 0);
+ if (ssd < 0)
+ err(EXIT_FAILURE,
+ "failed to make a udp socket for server");
+
+ if (setsockopt(ssd, SOL_SOCKET,
+ SO_REUSEADDR, (const char *)&y, sizeof(y)) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to setsockopt(SO_REUSEADDR)");
+ }
+
+ if (ssd != fdescs[0].fd) {
+ if (dup2(ssd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
+ }
+ close(ssd);
+ ssd = fdescs[0].fd;
+ }
+
+ init_addr(sin, iserver_port);
+ if (bserver_do_bind) {
+ if (bind(ssd, sin, addr_size) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to bind a server socket");
+ }
+ }
+
+ csd = socket(family, SOCK_DGRAM, blite? IPPROTO_UDPLITE: 0);
+ if (csd < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE,
+ "failed to make a udp client socket");
+ }
+
+ if (setsockopt(csd, SOL_SOCKET,
+ SO_REUSEADDR, (const char *)&y, sizeof(y)) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to setsockopt(SO_REUSEADDR)");
+ }
+
+ if (csd != fdescs[1].fd) {
+ if (dup2(csd, fdescs[1].fd) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", csd, fdescs[1].fd);
+ }
+ close(csd);
+ csd = fdescs[1].fd;
+ }
+
+ if (bclient_do_bind) {
+ init_addr(cin, iclient_port);
+ if (bind(csd, cin, addr_size) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to bind a client socket");
+ }
+ }
+
+ if (bclient_do_connect) {
+ if (connect(csd, sin, addr_size) < 0) {
+ int e = errno;
+ close(ssd);
+ close(csd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to connect a client socket to the server socket");
+ }
+ }
+
+ 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,
+ };
+
+ return NULL;
+}
+
+static void *make_udp(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in sin, cin;
+ return make_udp_common(factory, fdescs, argc, argv,
+ AF_INET,
+ tcp_init_addr, sizeof(sin),
+ (struct sockaddr *)&sin, (struct sockaddr *)&cin);
+}
+
+static void *make_raw_common(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv,
+ int family,
+ void (*init_addr)(struct sockaddr *, bool),
+ size_t addr_size,
+ struct sockaddr * sin)
+{
+ struct arg protocol = decode_arg("protocol", factory->params, argc, argv);
+ int iprotocol = ARG_INTEGER(protocol);
+ int ssd;
+
+ free_arg(&protocol);
+
+ ssd = socket(family, SOCK_RAW, iprotocol);
+ if (ssd < 0)
+ err(EXIT_FAILURE,
+ "failed to make a udp socket for server");
+
+ if (ssd != fdescs[0].fd) {
+ if (dup2(ssd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", ssd, fdescs[0].fd);
+ }
+ close(ssd);
+ ssd = fdescs[0].fd;
+ }
+
+ init_addr(sin, false);
+ if (bind(ssd, sin, addr_size) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed in bind(2)");
+ }
+
+ init_addr(sin, true);
+ if (connect(ssd, sin, addr_size) < 0) {
+ int e = errno;
+ close(ssd);
+ errno = e;
+ err(EXIT_FAILURE, "failed in connect(2)");
+ }
+
+ fdescs[0] = (struct fdesc) {
+ .fd = fdescs[0].fd,
+ .close = close_fdesc,
+ .data = NULL,
+ };
+
+ return NULL;
+}
+
+static void raw_init_addr(struct sockaddr * addr, bool remote_addr)
+{
+ struct sockaddr_in *in = (struct sockaddr_in *)addr;
+ memset(in, 0, sizeof(*in));
+ in->sin_family = AF_INET;
+ in->sin_addr.s_addr = htonl(INADDR_LOOPBACK + (remote_addr? 1: 0));
+}
+
+static void *make_raw(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in sin;
+ return make_raw_common(factory, fdescs, argc, argv,
+ AF_INET,
+ raw_init_addr, sizeof(sin),
+ (struct sockaddr *)&sin);
+}
+
+static void *make_ping_common(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv,
+ int family, int protocol,
+ void (*init_addr)(struct sockaddr *, unsigned short),
+ size_t addr_size,
+ struct sockaddr *sin)
+{
+ struct arg connect_ = decode_arg("connect", factory->params, argc, argv);
+ bool bconnect = ARG_BOOLEAN(connect_);
+
+ struct arg bind_ = decode_arg("bind", factory->params, argc, argv);
+ bool bbind = ARG_BOOLEAN(bind_);
+
+ struct arg id = decode_arg("id", factory->params, argc, argv);
+ unsigned short iid = (unsigned short)ARG_INTEGER(id);
+
+ int sd;
+
+ free_arg(&id);
+ free_arg(&bind_);
+ free_arg(&connect_);
+
+ sd = socket(family, SOCK_DGRAM, protocol);
+ if (sd < 0)
+ err((errno == EACCES? EXIT_EACCESS: EXIT_FAILURE),
+ "failed to make an icmp socket");
+
+ if (sd != fdescs[0].fd) {
+ if (dup2(sd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[0].fd);
+ }
+ close(sd);
+ sd = fdescs[0].fd;
+ }
+
+ if (bbind) {
+ init_addr(sin, iid);
+ if (bind(sd, sin, addr_size) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err((errno == EACCES? EXIT_EACCESS: EXIT_FAILURE),
+ "failed in bind(2)");
+ }
+ }
+
+ if (bconnect) {
+ init_addr(sin, 0);
+ if (connect(sd, sin, addr_size) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE, "failed in connect(2)");
+ }
+ }
+
+ fdescs[0] = (struct fdesc) {
+ .fd = fdescs[0].fd,
+ .close = close_fdesc,
+ .data = NULL,
+ };
+
+ return NULL;
+}
+
+static void ping_init_addr(struct sockaddr *addr, unsigned short id)
+{
+ struct sockaddr_in *in = (struct sockaddr_in *)addr;
+ memset(in, 0, sizeof(*in));
+ in->sin_family = AF_INET;
+ in->sin_port = htons(id);
+ in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+}
+
+static void *make_ping(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in in;
+ return make_ping_common(factory, fdescs, argc, argv,
+ AF_INET, IPPROTO_ICMP,
+ ping_init_addr,
+ sizeof(in),
+ (struct sockaddr *)&in);
+}
+
+static void tcp6_init_addr(struct sockaddr *addr, unsigned short port)
+{
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;
+ memset(in6, 0, sizeof(*in6));
+ in6->sin6_family = AF_INET6;
+ in6->sin6_flowinfo = 0;
+ in6->sin6_port = htons(port);
+ in6->sin6_addr = in6addr_loopback;
+}
+
+static void *make_tcp6(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in6 sin, cin;
+ return make_tcp_common(factory, fdescs, argc, argv,
+ AF_INET6,
+ tcp6_init_addr, sizeof(sin),
+ (struct sockaddr *)&sin, (struct sockaddr *)&cin);
+}
+
+static void *make_udp6(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in6 sin, cin;
+ return make_udp_common(factory, fdescs, argc, argv,
+ AF_INET6,
+ tcp6_init_addr, sizeof(sin),
+ (struct sockaddr *)&sin, (struct sockaddr *)&cin);
+}
+
+static void raw6_init_addr(struct sockaddr *addr, bool remote_addr)
+{
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;
+ memset(in6, 0, sizeof(*in6));
+ in6->sin6_family = AF_INET6;
+ in6->sin6_flowinfo = 0;
+
+ if (remote_addr) {
+ /* ::ffff:127.0.0.1 */
+ in6->sin6_addr.s6_addr16[5] = 0xffff;
+ in6->sin6_addr.s6_addr32[3] = htonl(INADDR_LOOPBACK);
+ } else
+ in6->sin6_addr = in6addr_loopback;
+}
+
+static void *make_raw6(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in6 sin;
+ return make_raw_common(factory, fdescs, argc, argv,
+ AF_INET6,
+ raw6_init_addr, sizeof(sin),
+ (struct sockaddr *)&sin);
+}
+
+static void ping6_init_addr(struct sockaddr *addr, unsigned short id)
+{
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;
+ memset(in6, 0, sizeof(*in6));
+ in6->sin6_family = AF_INET6;
+ in6->sin6_port = htons(id);
+ in6->sin6_addr = in6addr_loopback;
+}
+
+static void *make_ping6(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct sockaddr_in6 in6;
+ return make_ping_common(factory, fdescs, argc, argv,
+ AF_INET6, IPPROTO_ICMPV6,
+ ping6_init_addr,
+ sizeof(in6),
+ (struct sockaddr *)&in6);
+}
+
+static void *make_netns(const struct factory *factory _U_, struct fdesc fdescs[],
+ int argc _U_, char ** argv _U_)
+{
+ int sd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sd < 0)
+ err(EXIT_FAILURE, "failed in socket()");
+
+ int ns = ioctl(sd, SIOCGSKNS);
+ if (ns < 0)
+ err((errno == ENOSYS? EXIT_ENOSYS: EXIT_FAILURE),
+ "failed in ioctl(SIOCGSKNS)");
+ close(sd);
+
+ if (ns != fdescs[0].fd) {
+ if (dup2(ns, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(ns);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", ns, fdescs[0].fd);
+ }
+ close(ns);
+ }
+
+ fdescs[0] = (struct fdesc){
+ .fd = fdescs[0].fd,
+ .close = close_fdesc,
+ .data = NULL
+ };
+
+ return NULL;
+}
+
+static void *make_netlink(const struct factory *factory, struct fdesc fdescs[],
+ int argc, char ** argv)
+{
+ struct arg protocol = decode_arg("protocol", factory->params, argc, argv);
+ int iprotocol = ARG_INTEGER(protocol);
+ struct arg groups = decode_arg("groups", factory->params, argc, argv);
+ unsigned int ugroups = ARG_UINTEGER(groups);
+ int sd;
+
+ free_arg(&protocol);
+
+ sd = socket(AF_NETLINK, SOCK_RAW, iprotocol);
+ if (sd < 0)
+ err((errno == EPROTONOSUPPORT)? EXIT_EPROTONOSUPPORT: EXIT_FAILURE,
+ "failed in socket()");
+
+ if (sd != fdescs[0].fd) {
+ if (dup2(sd, fdescs[0].fd) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE, "failed to dup %d -> %d", sd, fdescs[0].fd);
+ }
+ close(sd);
+ }
+
+ struct sockaddr_nl nl;
+ memset(&nl, 0, sizeof(nl));
+ nl.nl_family = AF_NETLINK;
+ nl.nl_groups = ugroups;
+ if (bind(sd, (struct sockaddr*)&nl, sizeof(nl)) < 0) {
+ int e = errno;
+ close(sd);
+ errno = e;
+ err(EXIT_FAILURE, "failed in bind(2)");
+ }
+
+ 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[] = {
+ {
+ .name = "ro-regular-file",
+ .desc = "read-only regular file",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = open_ro_regular_file,
+ .params = (struct parameter []) {
+ {
+ .name = "file",
+ .type = PTYPE_STRING,
+ .desc = "file to be opened",
+ .defv.string = "/etc/passwd",
+ },
+ {
+ .name = "offset",
+ .type = PTYPE_INTEGER,
+ .desc = "seek bytes after open with SEEK_CUR",
+ .defv.integer = 0,
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "pipe-no-fork",
+ .desc = "making pair of fds with pipe(2)",
+ .priv = false,
+ .N = 2,
+ .EX_N = 2,
+ .make = make_pipe,
+ .params = (struct parameter []) {
+ {
+ .name = "nonblock",
+ .type = PTYPE_STRING,
+ .desc = "set nonblock flag (\"--\", \"r-\", \"-w\", or \"rw\")",
+ .defv.string = "--",
+ },
+ {
+ .name = "rdup",
+ .type = PTYPE_INTEGER,
+ .desc = "file descriptor for duplicating the pipe input",
+ .defv.integer = -1,
+ },
+ {
+ .name = "wdup",
+ .type = PTYPE_INTEGER,
+ .desc = "file descriptor for duplicating the pipe output",
+ .defv.integer = -1,
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "directory",
+ .desc = "directory",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = open_directory,
+ .params = (struct parameter []) {
+ {
+ .name = "dir",
+ .type = PTYPE_STRING,
+ .desc = "directory to be opened",
+ .defv.string = "/",
+ },
+ {
+ .name = "dentries",
+ .type = PTYPE_INTEGER,
+ .desc = "read the number of dentries after open with readdir(3)",
+ .defv.integer = 0,
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "rw-character-device",
+ .desc = "character device with O_RDWR flag",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = open_rw_chrdev,
+ .params = (struct parameter []) {
+ {
+ .name = "chrdev",
+ .type = PTYPE_STRING,
+ .desc = "character device node to be opened",
+ .defv.string = "/dev/zero",
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "socketpair",
+ .desc = "AF_UNIX socket pair created with socketpair(2)",
+ .priv = false,
+ .N = 2,
+ .EX_N = 0,
+ .make = make_socketpair,
+ .params = (struct parameter []) {
+ {
+ .name = "socktype",
+ .type = PTYPE_STRING,
+ .desc = "STREAM, DGRAM, or SEQPACKET",
+ .defv.string = "STREAM",
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "symlink",
+ .desc = "symbolic link itself opened with O_PATH",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = open_with_opath,
+ .params = (struct parameter []) {
+ {
+ .name = "path",
+ .type = PTYPE_STRING,
+ .desc = "path to a symbolic link",
+ .defv.string = "/dev/stdin",
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "ro-block-device",
+ .desc = "block device with O_RDONLY flag",
+ .priv = true,
+ .N = 1,
+ .EX_N = 0,
+ .make = open_ro_blkdev,
+ .params = (struct parameter []) {
+ {
+ .name = "blkdev",
+ .type = PTYPE_STRING,
+ .desc = "block device node to be opened",
+ .defv.string = "/dev/nullb0",
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "mapped-packet-socket",
+ .desc = "mmap'ed AF_PACKET socket",
+ .priv = true,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_mmapped_packet_socket,
+ .params = (struct parameter []) {
+ {
+ .name = "socktype",
+ .type = PTYPE_STRING,
+ .desc = "DGRAM or RAW",
+ .defv.string = "RAW",
+ },
+ {
+ .name = "interface",
+ .type = PTYPE_STRING,
+ .desc = "a name of network interface like eth0 or lo",
+ .defv.string = "lo",
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "pidfd",
+ .desc = "pidfd returned from pidfd_open(2)",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_pidfd,
+ .params = (struct parameter []) {
+ {
+ .name = "target-pid",
+ .type = PTYPE_INTEGER,
+ .desc = "the pid of the target process",
+ .defv.integer = 1,
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "inotify",
+ .desc = "inotify fd returned from inotify_init(2)",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_inotify_fd,
+ .params = (struct parameter []) {
+ PARAM_END
+ },
+ },
+ {
+ .name = "unix-stream",
+ .desc = "AF_UNIX+SOCK_STREAM sockets",
+ .priv = false,
+ .N = 3,
+ .EX_N = 0,
+ .make = make_unix_stream,
+ .params = (struct parameter []) {
+ {
+ .name = "path",
+ .type = PTYPE_STRING,
+ .desc = "path for listening-socket bound to",
+ .defv.string = "/tmp/test_mkfds-unix-stream",
+ },
+ {
+ .name = "backlog",
+ .type = PTYPE_INTEGER,
+ .desc = "backlog passed to listen(2)",
+ .defv.integer = 5,
+ },
+ {
+ .name = "abstract",
+ .type = PTYPE_BOOLEAN,
+ .desc = "use PATH as an abstract socket address",
+ .defv.boolean = false,
+ },
+ {
+ .name = "server-shutdown",
+ .type = PTYPE_INTEGER,
+ .desc = "shutdown the accepted socket; 1: R, 2: W, 3: RW",
+ .defv.integer = 0,
+ },
+ {
+ .name = "client-shutdown",
+ .type = PTYPE_INTEGER,
+ .desc = "shutdown the client socket; 1: R, 2: W, 3: RW",
+ .defv.integer = 0,
+ },
+ {
+ .name = "type",
+ .type = PTYPE_STRING,
+ .desc = "stream or seqpacket",
+ .defv.string = "stream",
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "unix-dgram",
+ .desc = "AF_UNIX+SOCK_DGRAM sockets",
+ .priv = false,
+ .N = 2,
+ .EX_N = 0,
+ .make = make_unix_dgram,
+ .params = (struct parameter []) {
+ {
+ .name = "path",
+ .type = PTYPE_STRING,
+ .desc = "path for unix non-stream bound to",
+ .defv.string = "/tmp/test_mkfds-unix-dgram",
+ },
+ {
+ .name = "abstract",
+ .type = PTYPE_BOOLEAN,
+ .desc = "use PATH as an abstract socket address",
+ .defv.boolean = false,
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "unix-in-netns",
+ .desc = "make a unix socket in a new network namespace",
+ .priv = true,
+ .N = 3,
+ .EX_N = 0,
+ .make = make_unix_in_new_netns,
+ .params = (struct parameter []) {
+ {
+ .name = "type",
+ .type = PTYPE_STRING,
+ .desc = "dgram, stream, or seqpacket",
+ .defv.string = "stream",
+ },
+ {
+ .name = "path",
+ .type = PTYPE_STRING,
+ .desc = "path for unix non-stream bound to",
+ .defv.string = "/tmp/test_mkfds-unix-in-netns",
+ },
+ {
+ .name = "abstract",
+ .type = PTYPE_BOOLEAN,
+ .desc = "use PATH as an abstract socket address",
+ .defv.boolean = false,
+ },
+ PARAM_END
+ },
+ },
+ {
+ .name = "tcp",
+ .desc = "AF_INET+SOCK_STREAM sockets",
+ .priv = false,
+ .N = 3,
+ .EX_N = 0,
+ .make = make_tcp,
+ .params = (struct parameter []) {
+ {
+ .name = "server-port",
+ .type = PTYPE_INTEGER,
+ .desc = "TCP port the server may listen",
+ .defv.integer = 12345,
+ },
+ {
+ .name = "client-port",
+ .type = PTYPE_INTEGER,
+ .desc = "TCP port the client may bind",
+ .defv.integer = 23456,
+ },
+ PARAM_END
+ }
+ },
+ {
+ .name = "udp",
+ .desc = "AF_INET+SOCK_DGRAM sockets",
+ .priv = false,
+ .N = 2,
+ .EX_N = 0,
+ .make = make_udp,
+ .params = (struct parameter []) {
+ {
+ .name = "lite",
+ .type = PTYPE_BOOLEAN,
+ .desc = "Use UDPLITE instead of UDP",
+ .defv.boolean = false,
+ },
+ {
+ .name = "server-port",
+ .type = PTYPE_INTEGER,
+ .desc = "UDP port the server may listen",
+ .defv.integer = 12345,
+ },
+ {
+ .name = "client-port",
+ .type = PTYPE_INTEGER,
+ .desc = "UDP port the client may bind",
+ .defv.integer = 23456,
+ },
+ {
+ .name = "server-do-bind",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call bind with the server socket",
+ .defv.boolean = true,
+ },
+ {
+ .name = "client-do-bind",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call bind with the client socket",
+ .defv.boolean = true,
+ },
+ {
+ .name = "client-do-connect",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call connect with the client socket",
+ .defv.boolean = true,
+ },
+ PARAM_END
+ }
+ },
+ {
+ .name = "raw",
+ .desc = "AF_INET+SOCK_RAW sockets",
+ .priv = true,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_raw,
+ .params = (struct parameter []) {
+ {
+ .name = "protocol",
+ .type = PTYPE_INTEGER,
+ .desc = "protocol passed to socket(AF_INET, SOCK_RAW, protocol)",
+ .defv.integer = IPPROTO_IPIP,
+ },
+ PARAM_END
+ }
+
+ },
+ {
+ .name = "ping",
+ .desc = "AF_INET+SOCK_DGRAM+IPPROTO_ICMP sockets",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_ping,
+ .params = (struct parameter []) {
+ {
+ .name = "connect",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call connect(2) with the socket",
+ .defv.boolean = true,
+ },
+ {
+ .name = "bind",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call bind(2) with the socket",
+ .defv.boolean = true,
+ },
+ {
+ .name = "id",
+ .type = PTYPE_INTEGER,
+ .desc = "ICMP echo request id",
+ .defv.integer = 0,
+ },
+ PARAM_END
+ }
+ },
+ {
+ .name = "tcp6",
+ .desc = "AF_INET6+SOCK_STREAM sockets",
+ .priv = false,
+ .N = 3,
+ .EX_N = 0,
+ .make = make_tcp6,
+ .params = (struct parameter []) {
+ {
+ .name = "server-port",
+ .type = PTYPE_INTEGER,
+ .desc = "TCP port the server may listen",
+ .defv.integer = 12345,
+ },
+ {
+ .name = "client-port",
+ .type = PTYPE_INTEGER,
+ .desc = "TCP port the client may bind",
+ .defv.integer = 23456,
+ },
+ PARAM_END
+ }
+ },
+ {
+ .name = "udp6",
+ .desc = "AF_INET6+SOCK_DGRAM sockets",
+ .priv = false,
+ .N = 2,
+ .EX_N = 0,
+ .make = make_udp6,
+ .params = (struct parameter []) {
+ {
+ .name = "lite",
+ .type = PTYPE_BOOLEAN,
+ .desc = "Use UDPLITE instead of UDP",
+ .defv.boolean = false,
+ },
+ {
+ .name = "server-port",
+ .type = PTYPE_INTEGER,
+ .desc = "UDP port the server may listen",
+ .defv.integer = 12345,
+ },
+ {
+ .name = "client-port",
+ .type = PTYPE_INTEGER,
+ .desc = "UDP port the client may bind",
+ .defv.integer = 23456,
+ },
+ {
+ .name = "server-do-bind",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call bind with the server socket",
+ .defv.boolean = true,
+ },
+ {
+ .name = "client-do-bind",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call bind with the client socket",
+ .defv.boolean = true,
+ },
+ {
+ .name = "client-do-connect",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call connect with the client socket",
+ .defv.boolean = true,
+ },
+ PARAM_END
+ }
+ },
+ {
+ .name = "raw6",
+ .desc = "AF_INET6+SOCK_RAW sockets",
+ .priv = true,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_raw6,
+ .params = (struct parameter []) {
+ {
+ .name = "protocol",
+ .type = PTYPE_INTEGER,
+ .desc = "protocol passed to socket(AF_INET6, SOCK_RAW, protocol)",
+ .defv.integer = IPPROTO_IPIP,
+ },
+ PARAM_END
+ }
+
+ },
+ {
+ .name = "ping6",
+ .desc = "AF_INET6+SOCK_DGRAM+IPPROTO_ICMPV6 sockets",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_ping6,
+ .params = (struct parameter []) {
+ {
+ .name = "connect",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call connect(2) with the socket",
+ .defv.boolean = true,
+ },
+ {
+ .name = "bind",
+ .type = PTYPE_BOOLEAN,
+ .desc = "call bind(2) with the socket",
+ .defv.boolean = true,
+ },
+ {
+ .name = "id",
+ .type = PTYPE_INTEGER,
+ .desc = "ICMP echo request id",
+ .defv.integer = 0,
+ },
+ PARAM_END
+ }
+ },
+ {
+ .name = "netns",
+ .desc = "open a file specifying a netns",
+ .priv = true,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_netns,
+ .params = (struct parameter []) {
+ PARAM_END
+ }
+ },
+ {
+ .name = "netlink",
+ .desc = "AF_NETLINK sockets",
+ .priv = false,
+ .N = 1,
+ .EX_N = 0,
+ .make = make_netlink,
+ .params = (struct parameter []) {
+ {
+ .name = "protocol",
+ .type = PTYPE_INTEGER,
+ .desc = "protocol passed to socket(AF_NETLINK, SOCK_RAW, protocol)",
+ .defv.integer = NETLINK_USERSOCK,
+ },
+ {
+ .name = "groups",
+ .type = PTYPE_UINTEGER,
+ .desc = "multicast groups of netlink communication (requires CAP_NET_ADMIN)",
+ .defv.uinteger = 0,
+ },
+ PARAM_END
+ }
+ },
+};
+
+static int count_parameters(const struct factory *factory)
+{
+
+ const struct parameter *p = factory->params;
+ if (!p)
+ return 0;
+ while (p->name)
+ p++;
+ return p - factory->params;
+}
+
+static void print_factory(const struct factory *factory)
+{
+ printf("%-20s %4s %5d %6d %s\n",
+ factory->name,
+ factory->priv? "yes": "no",
+ factory->N,
+ count_parameters(factory),
+ factory->desc);
+}
+
+static void list_factories(void)
+{
+ printf("%-20s PRIV COUNT NPARAM DESCRIPTION\n", "FACTORY");
+ for (size_t i = 0; i < ARRAY_SIZE(factories); i++)
+ print_factory(factories + i);
+}
+
+static const struct factory *find_factory(const char *name)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(factories); i++)
+ if (strcmp(factories[i].name, name) == 0)
+ return factories + i;
+ return NULL;
+}
+
+static void list_parameters(const char *factory_name)
+{
+ const struct factory *factory = find_factory(factory_name);
+ const char *fmt = "%-15s %-8s %15s %s\n";
+
+ if (!factory)
+ errx(EXIT_FAILURE, "no such factory: %s", factory_name);
+
+ if (!factory->params)
+ return;
+
+ printf(fmt, "PARAMETER", "TYPE", "DEFAULT_VALUE", "DESCRIPTION");
+ for (const struct parameter *p = factory->params; p->name != NULL; p++) {
+ char *defv = ptype_classes[p->type].sprint(&p->defv);
+ printf(fmt, p->name, ptype_classes[p->type].name, defv, p->desc);
+ free(defv);
+ }
+}
+
+static void rename_self(const char *comm)
+{
+ if (prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0) < 0)
+ err(EXIT_FAILURE, "failed to rename self via prctl: %s", comm);
+}
+
+static void do_nothing(int signum _U_)
+{
+}
+
+#ifdef __NR_pidfd_open
+
+static int
+pidfd_open(pid_t pid, unsigned int flags)
+{
+ return syscall(__NR_pidfd_open, pid, flags);
+}
+#else
+static int
+pidfd_open(pid_t pid _U_, unsigned int flags _U_)
+{
+ errno = ENOSYS;
+ return -1;
+}
+#endif
+
+static void wait_event(void)
+{
+ fd_set readfds;
+ sigset_t sigset;
+ int n = 0;
+
+ 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);
+ }
+
+ sigemptyset(&sigset);
+
+ if (pselect(n, &readfds, NULL, NULL, NULL, &sigset) < 0
+ && errno != EINTR)
+ errx(EXIT_FAILURE, "failed in pselect");
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+ const struct factory *factory;
+ struct fdesc fdescs[MAX_N];
+ bool quiet = false;
+ bool cont = false;
+ void *data;
+
+ static const struct option longopts[] = {
+ { "list", no_argument, NULL, 'l' },
+ { "parameters", required_argument, NULL, 'I' },
+ { "comm", required_argument, NULL, 'r' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "dont-puase", no_argument, NULL, 'c' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+
+ while ((c = getopt_long(argc, argv, "lhqcI:r:", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ usage(stdout, EXIT_SUCCESS);
+ case 'l':
+ list_factories();
+ exit(EXIT_SUCCESS);
+ case 'I':
+ list_parameters(optarg);
+ exit(EXIT_SUCCESS);
+ case 'q':
+ quiet = true;
+ break;
+ case 'c':
+ cont = true;
+ break;
+ case 'r':
+ rename_self(optarg);
+ break;
+ default:
+ usage(stderr, EXIT_FAILURE);
+ }
+ }
+
+ if (optind == argc)
+ errx(EXIT_FAILURE, "no file descriptor specification given");
+
+ factory = find_factory(argv[optind]);
+ if (!factory)
+ errx(EXIT_FAILURE, "no such factory: %s", argv[optind]);
+ assert(factory->N + factory->EX_N < MAX_N);
+ optind++;
+
+ if ((optind + factory->N) > argc)
+ errx(EXIT_FAILURE, "not enough file descriptors given for %s",
+ factory->name);
+
+ if (factory->priv && getuid() != 0)
+ errx(EXIT_FAILURE, "%s factory requires root privilege", factory->name);
+
+ for (int i = 0; i < MAX_N; i++) {
+ fdescs[i].fd = -1;
+ fdescs[i].close = NULL;
+ }
+
+ for (int i = 0; i < factory->N; i++) {
+ char *str = argv[optind + i];
+ long fd;
+ char *ep;
+
+ errno = 0;
+ fd = strtol(str, &ep, 10);
+ if (errno)
+ err(EXIT_FAILURE, "failed to convert fd number: %s", str);
+ if (ep == str)
+ errx(EXIT_FAILURE, "failed to convert fd number: %s", str);
+ if (*ep != '\0')
+ errx(EXIT_FAILURE, "garbage at the end of number: %s", str);
+ if (fd < 0)
+ errx(EXIT_FAILURE, "fd number should not be negative: %s", str);
+ if (fd < 3)
+ errx(EXIT_FAILURE, "fd 0, 1, 2 are reserved: %s", str);
+ fdescs[i].fd = fd;
+ }
+ optind += factory->N;
+
+ data = factory->make(factory, fdescs, argc - optind, argv + optind);
+
+ signal(SIGCONT, do_nothing);
+
+ if (!quiet) {
+ printf("%d", getpid());
+ putchar('\n');
+ if (factory->report)
+ factory->report(factory, data, stdout);
+ fflush(stdout);
+ }
+
+ if (!cont)
+ wait_event();
+
+ 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);
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/tests/helpers/test_pathnames.c b/tests/helpers/test_pathnames.c
new file mode 100644
index 0000000..2368641
--- /dev/null
+++ b/tests/helpers/test_pathnames.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2007 Karel Zak <kzak@redhat.com>
+ *
+ * This file is part of util-linux.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+struct hlpPath
+{
+ const char *name;
+ const char *path;
+};
+
+#define DEF_HLPPATH(_p) { #_p, _p }
+
+static struct hlpPath paths[] =
+{
+ DEF_HLPPATH(_PATH_DEFPATH),
+ DEF_HLPPATH(_PATH_DEFPATH_ROOT),
+ DEF_HLPPATH(_PATH_DEV_LOOP),
+ DEF_HLPPATH(_PATH_HUSHLOGIN),
+ DEF_HLPPATH(_PATH_MAILDIR),
+ DEF_HLPPATH(_PATH_MOTDFILE),
+ DEF_HLPPATH(_PATH_NOLOGIN),
+ DEF_HLPPATH(_PATH_LOGIN),
+ DEF_HLPPATH(_PATH_PASSWD),
+ DEF_HLPPATH(_PATH_GSHADOW),
+ DEF_HLPPATH(_PATH_GROUP),
+ DEF_HLPPATH(_PATH_SHADOW_PASSWD),
+ DEF_HLPPATH(_PATH_WORDS),
+ DEF_HLPPATH(_PATH_WORDS_ALT),
+ DEF_HLPPATH(_PATH_FILESYSTEMS),
+ DEF_HLPPATH(_PATH_PROC_SWAPS),
+ DEF_HLPPATH(_PATH_PROC_FILESYSTEMS),
+ DEF_HLPPATH(_PATH_MOUNTED),
+ DEF_HLPPATH(_PATH_MNTTAB),
+ DEF_HLPPATH(_PATH_DEV_BYLABEL),
+ DEF_HLPPATH(_PATH_DEV_BYUUID),
+ { NULL, NULL }
+};
+
+int
+main(int argc, char **argv)
+{
+ struct hlpPath *p;
+
+ if (argc == 1) {
+ for (p = paths; p->name; p++)
+ printf("%20s %s\n", p->name, p->path);
+ exit(EXIT_SUCCESS);
+ } else {
+ if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
+ printf("%s <option>\n", argv[0]);
+ fputs("options:\n", stdout);
+ for (p = paths; p->name; p++)
+ printf("\t%s\n", p->name);
+ exit(EXIT_SUCCESS);
+ }
+
+ for (p = paths; p->name; p++) {
+ if (strcmp(p->name, argv[1]) == 0) {
+ printf("%s\n", p->path);
+ exit(EXIT_SUCCESS);
+ }
+ }
+ }
+
+ exit(EXIT_FAILURE);
+}
+
diff --git a/tests/helpers/test_sha1.c b/tests/helpers/test_sha1.c
new file mode 100644
index 0000000..7cb107f
--- /dev/null
+++ b/tests/helpers/test_sha1.c
@@ -0,0 +1,37 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (C) 2017 Philip Prindeville <philipp@redfish-solutions.com>
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <err.h>
+#include <stdlib.h>
+
+#include "sha1.h"
+
+int main(void)
+{
+ int i, ret;
+ UL_SHA1_CTX ctx;
+ unsigned char digest[UL_SHA1LENGTH];
+ unsigned char buf[BUFSIZ];
+
+ ul_SHA1Init( &ctx );
+
+ while(!feof(stdin) && !ferror(stdin)) {
+ ret = fread(buf, 1, sizeof(buf), stdin);
+ if (ret)
+ ul_SHA1Update( &ctx, buf, ret );
+ }
+
+ if(freopen ("/dev/null", "r", stdin) == NULL)
+ err(EXIT_FAILURE, "stdin->null failed!");
+
+ ul_SHA1Final( digest, &ctx );
+
+ for (i = 0; i < UL_SHA1LENGTH; i++)
+ printf( "%02x", digest[i] );
+ printf("\n");
+ return 0;
+}
diff --git a/tests/helpers/test_sigreceive.c b/tests/helpers/test_sigreceive.c
new file mode 100644
index 0000000..8f83e05
--- /dev/null
+++ b/tests/helpers/test_sigreceive.c
@@ -0,0 +1,170 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * test_sigreceive - wait for signal and exit with value of it
+ *
+ * Written by Sami Kerola <kerolasa@iki.fi>
+ */
+
+#include <err.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "strutils.h"
+
+#define TEST_SIGRECEIVE_FAILURE 0
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+ fputs("Usage: test_sigreceive [-s|--setuid <login|uid>]\n", out);
+ exit(TEST_SIGRECEIVE_FAILURE);
+}
+
+static __attribute__((__noreturn__))
+void exiter(int signo __attribute__((__unused__)),
+ siginfo_t *info,
+ void *context __attribute__((__unused__)))
+{
+ int ret = info->si_signo;
+
+ if (info->si_code == SI_QUEUE && info->si_value.sival_int != 0)
+ ret = info->si_value.sival_int;
+ _exit(ret);
+}
+
+int main(int argc, char **argv)
+{
+ struct sigaction sigact;
+ fd_set rfds;
+ struct timeval timeout;
+ char *user = NULL;
+ int c;
+
+ static const struct option longopts[] = {
+ {"setuid", required_argument, NULL, 's'},
+ {NULL, 0, NULL, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "s:h", longopts, NULL)) != -1)
+ switch (c) {
+ case 's':
+ user = optarg;
+ break;
+ case 'h':
+ usage(stdout);
+ default:
+ usage(stderr);
+ }
+
+ if (user) {
+ struct passwd *pw;
+ uid_t uid;
+
+ pw = getpwnam(user);
+ if (pw)
+ uid = pw->pw_uid;
+ else
+ uid = strtou32_or_err(user, "failed to parse uid");
+ if (setuid(uid) < 0)
+ err(TEST_SIGRECEIVE_FAILURE, "setuid failed");
+ }
+
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = SA_SIGINFO;
+ sigact.sa_sigaction = exiter;
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+
+ sigaction(SIGINT, &sigact, NULL);
+ sigaction(SIGQUIT, &sigact, NULL);
+ sigaction(SIGILL, &sigact, NULL);
+#ifdef SIGTRAP
+ sigaction(SIGTRAP, &sigact, NULL);
+#endif
+ sigaction(SIGABRT, &sigact, NULL);
+#ifdef SIGIOT
+ sigaction(SIGIOT, &sigact, NULL);
+#endif
+#ifdef SIGEMT
+ sigaction(SIGEMT, &sigact, NULL);
+#endif
+#ifdef SIGBUS
+ sigaction(SIGBUS, &sigact, NULL);
+#endif
+ sigaction(SIGFPE, &sigact, NULL);
+ sigaction(SIGUSR1, &sigact, NULL);
+ sigaction(SIGSEGV, &sigact, NULL);
+ sigaction(SIGUSR2, &sigact, NULL);
+ sigaction(SIGPIPE, &sigact, NULL);
+ sigaction(SIGALRM, &sigact, NULL);
+ sigaction(SIGTERM, &sigact, NULL);
+#ifdef SIGSTKFLT
+ sigaction(SIGSTKFLT, &sigact, NULL);
+#endif
+ sigaction(SIGCHLD, &sigact, NULL);
+#ifdef SIGCLD
+ sigaction(SIGCLD, &sigact, NULL);
+#endif
+ sigaction(SIGCONT, &sigact, NULL);
+ sigaction(SIGTSTP, &sigact, NULL);
+ sigaction(SIGTTIN, &sigact, NULL);
+ sigaction(SIGTTOU, &sigact, NULL);
+#ifdef SIGURG
+ sigaction(SIGURG, &sigact, NULL);
+#endif
+#ifdef SIGXCPU
+ sigaction(SIGXCPU, &sigact, NULL);
+#endif
+#ifdef SIGXFSZ
+ sigaction(SIGXFSZ, &sigact, NULL);
+#endif
+#ifdef SIGVTALRM
+ sigaction(SIGVTALRM, &sigact, NULL);
+#endif
+#ifdef SIGPROF
+ sigaction(SIGPROF, &sigact, NULL);
+#endif
+#ifdef SIGWINCH
+ sigaction(SIGWINCH, &sigact, NULL);
+#endif
+#ifdef SIGIO
+ sigaction(SIGIO, &sigact, NULL);
+#endif
+#ifdef SIGPOLL
+ sigaction(SIGPOLL, &sigact, NULL);
+#endif
+#ifdef SIGINFO
+ sigaction(SIGINFO, &sigact, NULL);
+#endif
+#ifdef SIGLOST
+ sigaction(SIGLOST, &sigact, NULL);
+#endif
+#ifdef SIGPWR
+ sigaction(SIGPWR, &sigact, NULL);
+#endif
+#ifdef SIGUNUSED
+ sigaction(SIGUNUSED, &sigact, NULL);
+#endif
+#ifdef SIGSYS
+ sigaction(SIGSYS, &sigact, NULL);
+#endif
+#ifdef SIGRTMIN
+ sigaction(SIGRTMIN, &sigact, NULL);
+ sigaction(SIGRTMAX, &sigact, NULL);
+#endif
+ /* Keep SIGHUP last, the bit it flips tells to check script the
+ * helper is ready to be killed. */
+ sigaction(SIGHUP, &sigact, NULL);
+
+ FD_ZERO(&rfds);
+ FD_SET(STDIN_FILENO, &rfds);
+ select(0, &rfds, NULL, NULL, &timeout);
+
+ exit(TEST_SIGRECEIVE_FAILURE);
+}
diff --git a/tests/helpers/test_strerror.c b/tests/helpers/test_strerror.c
new file mode 100644
index 0000000..a574c6e
--- /dev/null
+++ b/tests/helpers/test_strerror.c
@@ -0,0 +1,44 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This test program prints errno messages to allow for portable
+ * verification of error messages.
+ *
+ * Copyright (C) 2019 Patrick Steinhardt <ps@pks.im
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define E(x) { #x, x }
+static struct {
+ const char *str;
+ int error;
+} errors[] = {
+ E(ENOENT),
+ E(ENOTTY),
+ E(EILSEQ),
+ E(EINVAL),
+};
+
+int main(int argc, const char *argv[])
+{
+ size_t i;
+
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s <errno>\n", argv[0]);
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(errors)/sizeof(*errors); i++) {
+ if (strcmp(errors[i].str, argv[1]) != 0)
+ continue;
+ puts(strerror(errors[i].error));
+ return 0;
+ }
+
+ fprintf(stderr, "Invalid errno: %s\n", argv[1]);
+ return -1;
+}
diff --git a/tests/helpers/test_sysinfo.c b/tests/helpers/test_sysinfo.c
new file mode 100644
index 0000000..81fbdd7
--- /dev/null
+++ b/tests/helpers/test_sysinfo.c
@@ -0,0 +1,176 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (C) 2007 Karel Zak <kzak@redhat.com>
+ *
+ * This file is part of util-linux.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <wchar.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+
+#include "mount-api-utils.h"
+
+typedef struct {
+ const char *name;
+ int (*fnc)(void);
+} mntHlpfnc;
+
+static int hlp_wordsize(void)
+{
+ printf("%zu\n", CHAR_BIT * sizeof(void*));
+ return 0;
+}
+
+static int hlp_endianness(void)
+{
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ printf("LE\n");
+#else
+ printf("BE\n");
+#endif
+ return 0;
+}
+
+
+static int hlp_pagesize(void)
+{
+ printf("%d\n", getpagesize());
+ return 0;
+}
+
+static int hlp_int_max(void)
+{
+ printf("%d\n", INT_MAX);
+ return 0;
+}
+
+static int hlp_uint_max(void)
+{
+ printf("%u\n", UINT_MAX);
+ return 0;
+}
+
+static int hlp_long_max(void)
+{
+ printf("%ld\n", LONG_MAX);
+ return 0;
+}
+
+static int hlp_ulong_max(void)
+{
+ printf("%lu\n", ULONG_MAX);
+ return 0;
+}
+
+static int hlp_u64_max(void)
+{
+ printf("%" PRIu64 "\n", UINT64_MAX);
+ return 0;
+}
+
+static int hlp_ulong_max32(void)
+{
+#if __WORDSIZE == 64
+ printf("%lu\n", ULONG_MAX >> 32);
+#else
+ printf("%lu\n", ULONG_MAX);
+#endif
+ return 0;
+}
+
+static int hlp_wcsspn_ok(void)
+{
+ printf("%d\n", wcsspn(L"FOO", L"F") == 1);
+ return 0;
+}
+
+static int hlp_enotty_ok(void)
+{
+ errno = 0;
+ ioctl(STDOUT_FILENO, 0);
+
+ printf("%d\n", errno != ENOSYS);
+ return 0;
+}
+
+static int hlp_fsopen_ok(void)
+{
+#ifdef FSOPEN_CLOEXEC
+ errno = 0;
+ fsopen(NULL, FSOPEN_CLOEXEC);
+#else
+ errno = ENOSYS;
+#endif
+ printf("%d\n", errno != ENOSYS);
+ return 0;
+}
+
+static mntHlpfnc hlps[] =
+{
+ { "WORDSIZE", hlp_wordsize },
+ { "pagesize", hlp_pagesize },
+ { "INT_MAX", hlp_int_max },
+ { "UINT_MAX", hlp_uint_max },
+ { "LONG_MAX", hlp_long_max },
+ { "ULONG_MAX", hlp_ulong_max },
+ { "ULONG_MAX32",hlp_ulong_max32 },
+ { "UINT64_MAX", hlp_u64_max },
+ { "byte-order", hlp_endianness },
+ { "wcsspn-ok", hlp_wcsspn_ok },
+ { "enotty-ok", hlp_enotty_ok },
+ { "fsopen-ok", hlp_fsopen_ok },
+ { NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+ int re = 0;
+ mntHlpfnc *fn;
+
+ if (argc == 1) {
+ for (fn = hlps; fn->name; fn++) {
+ printf("%15s: ", fn->name);
+ re += fn->fnc();
+ }
+ } else {
+ int i;
+
+ if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
+ printf("%s <option>\n", argv[0]);
+ fputs("options:\n", stdout);
+ for (fn = hlps; fn->name; fn++)
+ printf("\t%s\n", fn->name);
+ exit(EXIT_SUCCESS);
+ }
+
+ for (i=1; i < argc; i++) {
+ for (fn = hlps; fn->name; fn++) {
+ if (strcmp(fn->name, argv[i]) == 0)
+ re += fn->fnc();
+ }
+ }
+ }
+
+ exit(re ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
diff --git a/tests/helpers/test_tiocsti.c b/tests/helpers/test_tiocsti.c
new file mode 100644
index 0000000..c92645a
--- /dev/null
+++ b/tests/helpers/test_tiocsti.c
@@ -0,0 +1,21 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * test_tiocsti - test security of TIOCSTI
+ *
+ * Written by Federico Bento <up201407890@alunos.dcc.fc.up.pt>
+ */
+
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+int main(void)
+{
+ int rc = 0;
+ char *cmd = "id -u -n\n";
+
+ while(*cmd)
+ rc += ioctl(0, TIOCSTI, cmd++);
+
+ exit(rc ? EXIT_FAILURE : EXIT_SUCCESS);
+}
diff --git a/tests/helpers/test_uuid_namespace.c b/tests/helpers/test_uuid_namespace.c
new file mode 100644
index 0000000..3ee6aca
--- /dev/null
+++ b/tests/helpers/test_uuid_namespace.c
@@ -0,0 +1,42 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (C) 2017 Philip Prindeville <philipp@redfish-solutions.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../libuuid/src/uuid.h"
+
+static void get_template(const char *ns)
+{
+ const uuid_t *uuidptr;
+ char buf[UUID_STR_LEN];
+
+ uuidptr = uuid_get_template(ns);
+ if (uuidptr == NULL)
+ strcpy(buf, "NULL");
+ else
+ uuid_unparse_lower(*uuidptr, buf);
+
+ printf("uuid_get_template %s returns %s\n", (ns ? ns : "NULL"), buf);
+}
+
+int main(void)
+{
+ get_template("dns");
+
+ get_template("url");
+
+ get_template("oid");
+
+ get_template("x500");
+
+ get_template(NULL);
+ get_template("");
+ get_template("unknown");
+
+ exit(0);
+}
+