]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
tests: (mkfds) add mqueue factory
authorMasatake YAMATO <yamato@redhat.com>
Sat, 11 Mar 2023 15:24:02 +0000 (00:24 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Mon, 22 May 2023 14:29:19 +0000 (23:29 +0900)
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
configure.ac
meson.build
tests/helpers/Makemodule.am
tests/helpers/test_mkfds.c

index 2066997cb38f11f1977b989ddc338b219fe1397e..dfdcdbdce8471498bb3d046e5778d460549f04ba 100644 (file)
@@ -729,6 +729,9 @@ AS_IF([test x"$have_dirfd" = xno], [
 
 AM_CONDITIONAL([HAVE_DIRFD], [test "x$have_dirfd" = xyes || test "x$have_ddfd" = xyes])
 
+MQ_LIBS=
+AC_CHECK_LIB([rt], [mq_open], [MQ_LIBS="-lrt"])
+AC_SUBST([MQ_LIBS])
 
 AC_MSG_CHECKING([whether program_invocation_short_name is defined])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
index e1099cf5471031ba105e64617b419924726d63b9..2d0500cb35ee584373b9a84536cc098643b58cfa 100644 (file)
@@ -3257,11 +3257,14 @@ exe = executable(
   include_directories : includes)
 exes += exe
 
+mq_libs = []
+mq_libs += cc.find_library('rt', required : true)
 # XXX: LINUX
 exe = executable(
   'test_mkfds',
   'tests/helpers/test_mkfds.c',
-  include_directories : includes)
+  include_directories : includes,
+  dependencies : mq_libs)
 exes += exe
 
 exe = executable(
index 2b1df3c6baba01fd641f0aac1a24df70a6fd29ba..6f0075f8388457979a8fb525ac85ed35ed62e681 100644 (file)
@@ -34,6 +34,7 @@ test_uuid_namespace_SOURCES = tests/helpers/test_uuid_namespace.c \
 if LINUX
 check_PROGRAMS += test_mkfds
 test_mkfds_SOURCES = tests/helpers/test_mkfds.c
+test_mkfds_LDADD = $(LDADD) $(MQ_LIBS)
 
 check_PROGRAMS += test_enosys
 test_enosys_SOURCES = tests/helpers/test_enosys.c
index 4cd29f26dcf1d4e1cc3be66da501c6194e83e7b3..85e739219cf220d00d893cddf71d0a7c89e3b2af 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/if_packet.h>
 #include <linux/netlink.h>
 #include <linux/sockios.h>  /* SIOCGSKNS */
+#include <mqueue.h>
 #include <net/if.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
@@ -2012,6 +2013,179 @@ static void free_eventfd(const struct factory * factory _U_, void *data)
        }
 }
 
+struct mqueue_data {
+       pid_t pid;
+       const char *path;
+       bool created;
+};
+
+static void mqueue_data_free(struct mqueue_data *data)
+{
+       if (data->created)
+               mq_unlink(data->path);
+       free((void *)data->path);
+       free(data);
+}
+
+static void report_mqueue(const struct factory *factory _U_,
+                         int nth, void *data, FILE *fp)
+{
+       if (nth == 0) {
+               fprintf(fp, "%d", ((struct mqueue_data *)data)->pid);
+       }
+}
+
+static void close_mqueue(int fd, void *data _U_)
+{
+       mq_close(fd);
+}
+
+static void free_mqueue(const struct factory * factory _U_, void *data)
+{
+       struct mqueue_data *mqueue_data = data;
+       pid_t child = mqueue_data->pid;
+       int wstatus;
+
+       mqueue_data_free(mqueue_data);
+
+       kill(child, SIGCONT);
+       if (waitpid(child, &wstatus, 0) < 0)
+               err(EXIT_FAILURE, "failed in waitpid()");
+
+       if (WIFEXITED(wstatus)) {
+               int s = WEXITSTATUS(wstatus);
+               if (s != 0)
+                       err(EXIT_FAILURE, "the child process got an error: %d", s);
+       } else if (WIFSIGNALED(wstatus)) {
+               int s = WTERMSIG(wstatus);
+               if (WTERMSIG(wstatus) != 0)
+                       err(EXIT_FAILURE, "the child process got a signal: %d", s);
+       }
+}
+
+static void *make_mqueue(const struct factory *factory _U_, struct fdesc fdescs[],
+                        int argc _U_, char ** argv _U_)
+{
+       struct mqueue_data *mqueue_data;
+       struct arg path = decode_arg("path", factory->params, argc, argv);
+       const char *spath = ARG_STRING(path);
+
+       struct mq_attr attr = {
+               .mq_maxmsg = 1,
+               .mq_msgsize = 1,
+       };
+
+       int fd;
+
+       if (spath[0] != '/')
+               errx(EXIT_FAILURE, "the path for mqueue must start with '/': %s", spath);
+
+       if (spath[0] == '\0')
+               err(EXIT_FAILURE, "the path should not be empty");
+
+       if (fdescs[0].fd == fdescs[1].fd)
+               errx(EXIT_FAILURE, "specify three different numbers as file descriptors");
+
+       mqueue_data = xmalloc(sizeof(*mqueue_data));
+       mqueue_data->pid = 0;
+       mqueue_data->path = xstrdup(spath);
+       mqueue_data->created = false;
+
+       free_arg(&path);
+
+       fd = mq_open(mqueue_data->path, O_CREAT|O_EXCL | O_RDONLY, S_IRUSR | S_IWUSR, &attr);
+       if (fd < 0) {
+               mqueue_data_free(mqueue_data);
+               err(EXIT_FAILURE, "failed in mq_open(3) for reading");
+       }
+
+       mqueue_data->created = true;
+       if (fd != fdescs[0].fd) {
+               if (dup2(fd, fdescs[0].fd) < 0) {
+                       int e = errno;
+                       mq_close(fd);
+                       mqueue_data_free(mqueue_data);
+                       errno = e;
+                       err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
+               }
+               mq_close(fd);
+       }
+
+       fdescs[0] = (struct fdesc){
+               .fd    = fdescs[0].fd,
+               .close = close_mqueue,
+               .data  = NULL
+       };
+
+       fd = mq_open(mqueue_data->path, O_WRONLY, S_IRUSR | S_IWUSR, NULL);
+       if (fd < 0) {
+               int e = errno;
+               mq_close(fdescs[0].fd);
+               mqueue_data_free(mqueue_data);
+               errno = e;
+               err(EXIT_FAILURE, "failed in mq_open(3) for writing");
+       }
+
+       if (fd != fdescs[1].fd) {
+               if (dup2(fd, fdescs[1].fd) < 0) {
+                       int e = errno;
+                       mq_close(fd);
+                       mq_close(fdescs[0].fd);
+                       errno = e;
+                       err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[1].fd);
+               }
+               mq_close(fd);
+       }
+       fdescs[1] = (struct fdesc){
+               .fd    = fdescs[1].fd,
+               .close = close_mqueue,
+               .data  = NULL
+       };
+
+       signal(SIGCHLD, abort_with_child_death_message);
+       mqueue_data->pid = fork();
+       if (mqueue_data->pid < -1) {
+               int e = errno;
+               mq_close(fdescs[0].fd);
+               mq_close(fdescs[1].fd);
+               mqueue_data_free(mqueue_data);
+               errno = e;
+               err(EXIT_FAILURE, "failed in fork()");
+       } else if (mqueue_data->pid == 0) {
+               mqueue_data->created = false;
+               mqueue_data_free(mqueue_data);
+               mq_close(fdescs[0].fd);
+
+               signal(SIGCONT, do_nothing);
+               /* Notify the parent that I'm ready. */
+               if (mq_send(fdescs[1].fd, "", 0, 0) < 0)
+                       err(EXIT_FAILURE,
+                           "failed in mq_send() to notify the readiness to the prent");
+               /* Wait till the parent lets me go. */
+               pause();
+
+               mq_close(fdescs[1].fd);
+               exit(0);
+       } else {
+               char c;
+
+               /* The child owns fdescs[1]. */
+               mq_close(fdescs[1].fd);
+               fdescs[1].fd = -1;
+
+               /* Wait till the child is ready. */
+               if (mq_receive(fdescs[0].fd, &c, 1, NULL) < 0) {
+                       mq_close(fdescs[0].fd);
+                       mqueue_data_free(mqueue_data);
+                       err(EXIT_FAILURE,
+                           "failed in mq_receive() the readiness notification from the child");
+               }
+               signal(SIGCHLD, SIG_DFL);
+       }
+
+       return mqueue_data;
+}
+
 #define PARAM_END { .name = NULL, }
 static const struct factory factories[] = {
        {
@@ -2589,6 +2763,26 @@ static const struct factory factories[] = {
                        PARAM_END
                }
        },
+       {
+               .name = "mqueue",
+               .desc = "make a mqueue connecting two processes",
+               .priv = false,
+               .N    = 2,
+               .EX_N = 0,
+               .EX_R = 1,
+               .make = make_mqueue,
+               .report = report_mqueue,
+               .free = free_mqueue,
+               .params = (struct parameter []) {
+                       {
+                               .name = "path",
+                               .type = PTYPE_STRING,
+                               .desc = "path for mqueue",
+                               .defv.string = "/test_mkfds-mqueue",
+                       },
+                       PARAM_END
+               }
+       },
 };
 
 static int count_parameters(const struct factory *factory)