]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: (test) add cases for displaying a regular file and pipe
authorMasatake YAMATO <yamato@redhat.com>
Sun, 10 Oct 2021 00:58:55 +0000 (09:58 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Mon, 11 Oct 2021 22:25:14 +0000 (07:25 +0900)
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
tests/commands.sh
tests/expected/lsfd/mkfds-pipe-no-fork [new file with mode: 0644]
tests/expected/lsfd/mkfds-ro-regular-file [new file with mode: 0644]
tests/helpers/Makemodule.am
tests/helpers/test_mkfds.c [new file with mode: 0644]
tests/ts/lsfd/mkfds-pipe-no-fork [new file with mode: 0755]
tests/ts/lsfd/mkfds-ro-regular-file [new file with mode: 0755]

index 4b0e2f15a2aae58be4891cf73fe2e28997443565..18467cb4ed833777ed91e1ffe08090a85f6fa318 100644 (file)
@@ -44,6 +44,7 @@ TS_HELPER_UUID_NAMESPACE="${ts_helpersdir}test_uuid_namespace"
 TS_HELPER_MBSENCODE="${ts_helpersdir}test_mbsencode"
 TS_HELPER_CAL="${ts_helpersdir}test_cal"
 TS_HELPER_LAST_FUZZ="${ts_helpersdir}test_last_fuzz"
+TS_HELPER_MKFDS="${ts_helpersdir}test_mkfds"
 
 # paths to commands
 TS_CMD_ADDPART=${TS_CMD_ADDPART:-"${ts_commandsdir}addpart"}
@@ -80,6 +81,7 @@ TS_CMD_LOOK=${TS_CMD_LOOK-"${ts_commandsdir}look"}
 TS_CMD_LOSETUP=${TS_CMD_LOSETUP:-"${ts_commandsdir}losetup"}
 TS_CMD_LSBLK=${TS_CMD_LSBLK-"${ts_commandsdir}lsblk"}
 TS_CMD_LSCPU=${TS_CMD_LSCPU-"${ts_commandsdir}lscpu"}
+TS_CMD_LSFD=${TS_CMD_LSFD-"${ts_commandsdir}lsfd"}
 TS_CMD_LSMEM=${TS_CMD_LSMEM-"${ts_commandsdir}lsmem"}
 TS_CMD_LSNS=${TS_CMD_LSNS-"${ts_commandsdir}lsns"}
 TS_CMD_MCOOKIE=${TS_CMD_MCOOKIE-"${ts_commandsdir}mcookie"}
diff --git a/tests/expected/lsfd/mkfds-pipe-no-fork b/tests/expected/lsfd/mkfds-pipe-no-fork
new file mode 100644 (file)
index 0000000..272f0b7
--- /dev/null
@@ -0,0 +1,9 @@
+    3  r-- FIFO pipefs
+    4  -w- FIFO pipefs
+ASSOC,MODE,TYPE,SOURCE: 0
+PID[RUN]: 0
+PID[STR]: 0
+NAMES[RUN]: 0
+NAMES[STR]: 0
+INODES[RUN]: 0
+INODES[STR]: 0
diff --git a/tests/expected/lsfd/mkfds-ro-regular-file b/tests/expected/lsfd/mkfds-ro-regular-file
new file mode 100644 (file)
index 0000000..024e806
--- /dev/null
@@ -0,0 +1,10 @@
+    3  r--  REG /etc/passwd
+ASSOC,MODE,TYPE,NAME: 0
+PID[RUN]: 0
+PID[STR]: 0
+INODE[RUN]: 0
+INODE[STR]: 0
+UID[RUN]: 0
+UID[STR]: 0
+USER[RUN]: 0
+USER[STR]: 0
index a34cd8d2ad998f10e073e88b212d88760303f419..a44e6902d3ba055b75ae7640b8e3464ee965d23f 100644 (file)
@@ -31,3 +31,5 @@ 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
 
+check_PROGRAMS += test_mkfds
+test_mkfds_SOURCES = tests/helpers/test_mkfds.c
diff --git a/tests/helpers/test_mkfds.c b/tests/helpers/test_mkfds.c
new file mode 100644 (file)
index 0000000..336ddc7
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * test_lsfd - 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 <getopt.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+
+#define _U_ __attribute__((__unused__))
+
+static void __attribute__((__noreturn__)) usage(FILE *out, int status)
+{
+       fputs(USAGE_HEADER, out);
+       fprintf(out, _(" %s [options] FACTORY FD...\n"), program_invocation_short_name);
+
+       fputs(USAGE_OPTIONS, out);
+       fputs(_(" -l, --list           list available file descriptor factories and exit\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(USAGE_SEPARATOR, out);
+       fputs(_("Examples:\n"), out);
+       fprintf(out, _(" %s ro-regular-file 3     using 3, open an regular file\n"),
+               program_invocation_short_name);
+       fprintf(out, _(" %s pipe-no-fork 3 4      using 3 and 4, make a pair\n"),
+               program_invocation_short_name);
+
+       exit(status);
+}
+
+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 3
+       int  N;                 /* the number of fds this factory makes */
+       bool fork;              /* whether this factory make a child process or not */
+       void (*make)(struct factory *, struct fdesc[], pid_t *);
+};
+
+static void close_fdesc(int fd, void *data _U_)
+{
+       close(fd);
+}
+
+static void open_ro_regular_file(struct factory *factory _U_, struct fdesc fdescs[], pid_t * child _U_)
+{
+       const char *file = "/etc/passwd";
+
+       int fd = open(file, O_RDONLY);
+       if (fd < 0)
+               err(EXIT_FAILURE, "failed to open: %s", file);
+
+       if (dup2(fd, fdescs[0].fd) < 0) {
+               close(fd);
+               err(EXIT_FAILURE, "failed to dup %d -> %d", fd, fdescs[0].fd);
+       }
+
+       fdescs[0] = (struct fdesc){
+               .fd    = fdescs[0].fd,
+               .close = close_fdesc,
+               .data  = NULL
+       };
+}
+
+static void make_pipe(struct factory *factory _U_, struct fdesc fdescs[], pid_t * child _U_)
+{
+       int pd[2];
+       if (pipe(pd) < 0)
+               err(EXIT_FAILURE, "failed to make pipe");
+
+       for (int i = 0; i < 2; i++) {
+               if (dup2(pd[i], fdescs[i].fd) < 0) {
+                       close(pd[0]);
+                       close(pd[1]);
+                       err(EXIT_FAILURE, "failed to dup %d -> %d",
+                           pd[i], fdescs[i].fd);
+               }
+               fdescs[i] = (struct fdesc){
+                       .fd    = fdescs[i].fd,
+                       .close = close_fdesc,
+                       .data  = NULL
+               };
+       }
+}
+
+static struct factory factories[] = {
+       {
+               .name = "ro-regular-file",
+               .desc = "read-only regular file (FILE=/etc/passwd)",
+               .priv = false,
+               .N    = 1,
+               .fork = false,
+               .make = open_ro_regular_file,
+       },
+       {
+               .name = "pipe-no-fork",
+               .desc = "making pair of fds with pipe(2)",
+               .priv = false,
+               .N    = 2,
+               .fork = false,
+               .make = make_pipe,
+       },
+};
+
+static void print_factory(struct factory *factory)
+{
+       printf("%-20s %4s %5d %4s %s\n",
+              factory->name,
+              factory->priv? "yes": "no",
+              factory->N,
+              factory->fork? "yes": "no",
+              factory->desc);
+}
+
+static void list_factories(void)
+{
+       printf("%-20s PRIV COUNT FORK DESCRIPTION\n", "FACTORY");
+       for (size_t i = 0; i < ARRAY_SIZE(factories); i++)
+               print_factory(factories + i);
+}
+
+static 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 do_nothing(int signum _U_)
+{
+}
+
+int main(int argc, char **argv)
+{
+       int c;
+       pid_t pid[2];
+       struct factory *factory;
+       struct fdesc fdescs[MAX_N];
+       bool quiet = false;
+       bool cont  = false;
+
+       pid[0] = getpid();
+       pid[1] = -1;
+
+       static const struct option longopts[] = {
+               { "list",       no_argument, NULL, 'l' },
+               { "quiet",      no_argument, NULL, 'q' },
+               { "dont-puase", no_argument, NULL, 'c' },
+               { "help",       no_argument, NULL, 'h' },
+       };
+
+       while ((c = getopt_long(argc, argv, "lhqc", longopts, NULL)) != -1) {
+               switch (c) {
+               case 'h':
+                       usage(stdout, EXIT_SUCCESS);
+               case 'l':
+                       list_factories();
+                       exit(EXIT_SUCCESS);
+               case 'q':
+                       quiet = true;
+                       break;
+               case 'c':
+                       cont = true;
+                       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 < MAX_N);
+       optind++;
+
+       if ((optind + factory->N) > argc)
+               errx(EXIT_FAILURE, _("not enough file descriptors given for %s"),
+                    factory->name);
+       for (int i = 0; i < factory->N; i++) {
+               char *str = argv[optind + i];
+               long fd;
+
+               errno  = 0;
+               fd = strtol(str, NULL, 10);
+               if (errno)
+                       err(EXIT_FAILURE, "failed to convert fd 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;
+
+       factory->make(factory, fdescs, pid + 1);
+
+       signal(SIGCONT, do_nothing);
+
+       if (!quiet) {
+               printf("%d", pid[0]);
+               if (pid[1] != -1)
+                       printf(" %d", pid[1]);
+               putchar('\n');
+               fflush(stdout);
+       }
+
+       if (!cont)
+               pause();
+
+       for (int i = 0; i < factory->N; i++)
+               fdescs[i].close(fdescs[i].fd, fdescs[i].data);
+
+       exit(EXIT_SUCCESS);
+}
diff --git a/tests/ts/lsfd/mkfds-pipe-no-fork b/tests/ts/lsfd/mkfds-pipe-no-fork
new file mode 100755 (executable)
index 0000000..d042501
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 Masatake YAMATO <yamato@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.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="pipe, no fork"
+
+. $TS_TOPDIR/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_HELPER_MKFDS"
+
+ts_check_prog "stat"
+ts_check_prog "readlink"
+
+ts_cd "$TS_OUTDIR"
+
+pipe_name()
+{
+    readlink /proc/$1/fd/$2
+}
+
+pipe_inode()
+{
+    stat -L -c %i /proc/$1/fd/$2
+}
+
+PID=
+FD0=3
+FD1=4
+EXPR=
+
+{
+    coproc MKFDS { "$TS_HELPER_MKFDS" pipe-no-fork $FD0 $FD1; }
+    if read -u ${MKFDS[0]} PID; then
+       EXPR='(PID == '"${PID}"') and ((FD == '"$FD0"') or (FD =='"$FD1"'))'
+       ${TS_CMD_LSFD} -n -o ASSOC,MODE,TYPE,SOURCE -Q "${EXPR}"
+       echo 'ASSOC,MODE,TYPE,SOURCE': $?
+
+       LSFD_PIDS=$(${TS_CMD_LSFD} --raw -n -o PID -Q "${EXPR}")
+       echo 'PID[RUN]:' $?
+       [ "${LSFD_PIDS}" == "$(printf '%d\n%d' ${PID} ${PID})" ]
+       echo 'PID[STR]:' $?
+
+       LSFD_NAMES=$(${TS_CMD_LSFD} --raw -n -o NAME -Q "${EXPR}")
+       echo 'NAMES[RUN]:' $?
+       [ "${LSFD_NAMES}" == "$(printf '%s\n%s' $(pipe_name $PID $FD0) $(pipe_name $PID $FD1))" ]
+       echo 'NAMES[STR]:' $?
+
+       LSFD_INODES=$(${TS_CMD_LSFD} --raw -n -o INODE -Q "${EXPR}")
+       echo 'INODES[RUN]:' $?
+       [ "${LSFD_INODES}" == "$(printf '%d\n%d' $(pipe_inode $PID $FD0) $(pipe_inode $PID $FD1))" ]
+       echo 'INODES[STR]:' $?
+
+       kill -CONT ${PID}
+       wait ${MKFDS_PID}
+    fi
+} > $TS_OUTPUT 2>&1
+
+ts_finalize
diff --git a/tests/ts/lsfd/mkfds-ro-regular-file b/tests/ts/lsfd/mkfds-ro-regular-file
new file mode 100755 (executable)
index 0000000..5c4a843
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 Masatake YAMATO <yamato@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.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="read-only regular file"
+
+. $TS_TOPDIR/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_HELPER_MKFDS"
+
+ts_check_prog "stat"
+ts_check_prog "id"
+
+ts_cd "$TS_OUTDIR"
+
+PID=
+FD=3
+EXPR=
+
+{
+    coproc MKFDS { "$TS_HELPER_MKFDS" ro-regular-file $FD; }
+    if read -u ${MKFDS[0]} PID; then
+       EXPR='(PID == '"${PID}"') and (FD == '"$FD"')'
+       ${TS_CMD_LSFD} -n -o ASSOC,MODE,TYPE,NAME -Q "${EXPR}"
+       echo 'ASSOC,MODE,TYPE,NAME': $?
+
+       LSFD_PID=$(${TS_CMD_LSFD} --raw -n -o PID -Q "${EXPR}")
+       echo 'PID[RUN]:' $?
+       [ "${LSFD_PID}" == "${PID}" ]
+       echo 'PID[STR]:' $?
+
+       LSFD_INODE=$(${TS_CMD_LSFD} --raw -n -o INODE -Q "${EXPR}")
+       echo 'INODE[RUN]:' $?
+       [ "${LSFD_INODE}"  == "$(stat -c %i /etc/passwd)" ]
+       echo 'INODE[STR]:' $?
+
+       LSFD_UID=$(${TS_CMD_LSFD} --raw -n -o UID -Q "${EXPR}")
+       echo 'UID[RUN]:' $?
+       [ "${LSFD_UID}" == $(id -u) ]
+       echo 'UID[STR]:' $?
+
+       LSFD_USER=$(${TS_CMD_LSFD} --raw -n -o USER -Q "${EXPR}")
+       echo 'USER[RUN]:' $?
+       [ "${LSFD_USER}" == $(id -u -n) ]
+       echo 'USER[STR]:' $?
+
+       kill -CONT ${PID}
+       wait ${MKFDS_PID}
+    fi
+} > $TS_OUTPUT 2>&1
+
+ts_finalize