]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
tests: (mkfds) add eventfd factory
authorMasatake YAMATO <yamato@redhat.com>
Sun, 5 Mar 2023 18:04:13 +0000 (03:04 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Thu, 18 May 2023 18:27:27 +0000 (03:27 +0900)
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
tests/helpers/test_mkfds.c

index 2aad1dacf6145da4d7edcab7722d04a8f34943ea..540fc50240c49375780906376aae3cbea02aafac 100644 (file)
@@ -36,6 +36,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/eventfd.h>
 #include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
@@ -46,6 +47,7 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/user.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #include "c.h"
@@ -60,6 +62,7 @@
 #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)
 {
@@ -324,6 +327,13 @@ static void close_fdesc(int fd, void *data _U_)
        close(fd);
 }
 
+static void abort_with_child_death_message(int signum _U_)
+{
+       const char msg[] = "the child process exits unexpectedly";
+       (void)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)
 {
@@ -1888,6 +1898,119 @@ 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);
+       }
+}
+
 #define PARAM_END { .name = NULL, }
 static const struct factory factories[] = {
        {
@@ -2451,6 +2574,20 @@ 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
+               }
+       },
 };
 
 static int count_parameters(const struct factory *factory)