From: Masatake YAMATO Date: Sun, 10 Oct 2021 00:58:55 +0000 (+0900) Subject: lsfd: (test) add cases for displaying a regular file and pipe X-Git-Tag: v2.38-rc1~144^2~47 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fd81d3a5582e966e89b613d1636959a0d355c06a;p=thirdparty%2Futil-linux.git lsfd: (test) add cases for displaying a regular file and pipe Signed-off-by: Masatake YAMATO --- diff --git a/tests/commands.sh b/tests/commands.sh index 4b0e2f15a2..18467cb4ed 100644 --- a/tests/commands.sh +++ b/tests/commands.sh @@ -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 index 0000000000..272f0b7d85 --- /dev/null +++ b/tests/expected/lsfd/mkfds-pipe-no-fork @@ -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 index 0000000000..024e8060e9 --- /dev/null +++ b/tests/expected/lsfd/mkfds-ro-regular-file @@ -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 diff --git a/tests/helpers/Makemodule.am b/tests/helpers/Makemodule.am index a34cd8d2ad..a44e6902d3 100644 --- a/tests/helpers/Makemodule.am +++ b/tests/helpers/Makemodule.am @@ -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 index 0000000000..336ddc7d7e --- /dev/null +++ b/tests/helpers/test_mkfds.c @@ -0,0 +1,248 @@ +/* + * test_lsfd - make various file descriptors + * + * Written by Masatake YAMATO + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..d042501fc1 --- /dev/null +++ b/tests/ts/lsfd/mkfds-pipe-no-fork @@ -0,0 +1,73 @@ +#!/bin/bash +# +# Copyright (C) 2021 Masatake YAMATO +# +# 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 index 0000000000..5c4a843c5d --- /dev/null +++ b/tests/ts/lsfd/mkfds-ro-regular-file @@ -0,0 +1,67 @@ +#!/bin/bash +# +# Copyright (C) 2021 Masatake YAMATO +# +# 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