From: Masatake YAMATO Date: Fri, 22 Oct 2021 03:48:47 +0000 (+0900) Subject: lsfd: introduce -p/--pid option, pids filter working in the early stage X-Git-Tag: v2.38-rc1~144^2~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4cfabab2a2cb8b1526cdfc247cd42d7acc5c41b6;p=thirdparty%2Futil-linux.git lsfd: introduce -p/--pid option, pids filter working in the early stage $ time sudo ./lsfd -Q '(PID == 1) or (PID == 2)' > /dev/null real 0m0.508s user 0m0.230s sys 0m0.267s $ time sudo ./lsfd -p 1,2 > /dev/null real 0m0.088s user 0m0.036s sys 0m0.033s $ [ $(./lsfd -p 1,2) = $(./lsfd -Q '(PID == 1) or (PID == 2)') ] $ echo $? 0 Signed-off-by: Masatake YAMATO --- diff --git a/misc-utils/lsfd.1.adoc b/misc-utils/lsfd.1.adoc index eaa6e2ef52..e6a72d302f 100644 --- a/misc-utils/lsfd.1.adoc +++ b/misc-utils/lsfd.1.adoc @@ -57,6 +57,16 @@ Use specified directory as system root. *--notruncate*:: Don't truncate text in columns. +*-p*, *--pid* _pids_:: +Collect information only specified processes. +_pids_ is a list of pids. A comma or whitespaces can be used as separators. +You can use this option with *pidof*(1). See "EXAMPLES". ++ +Both *-Q* option with an expression including PID, e.g. -Q (PID == 1), +and *-p* option, e.g. -p 1, may print the same output but using *-p* +option is a much more efficient because *-p* option works much earlier +stage of processing than *-Q* option. + *-Q*, *--filter* _expr_:: Print the files only satisfying with the condition represented by the _expr_. @@ -277,6 +287,18 @@ Do the same in an alternative way:: # lsfd -Q '(PID == 1) || (PID == 2)' +Do the same in a more efficient way:: + + # lsfd --pid 1,2 + +Whitescapes can be used instead of a comma:: + + # lsfd --pid '1 2' + +Utilize *pidof*(1) for list the files associated with "firefox":: + + # lsfd --pid "$(pidof firefox)" + List the 1st file descriptor opened by PID 1 process:: # lsfd -Q '(PID == 1) and (FD == 1)' @@ -337,6 +359,7 @@ mailto:kzak@redhat.com[Karel Zak] == SEE ALSO *lsof*(8) +*pidof*(1) *proc*(5) include::man-common/bugreports.adoc[] diff --git a/misc-utils/lsfd.c b/misc-utils/lsfd.c index 3d27466e78..8ab57a4280 100644 --- a/misc-utils/lsfd.c +++ b/misc-utils/lsfd.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -948,7 +949,57 @@ static void read_process(struct lsfd_control *ctl, struct path_cxt *pc, ul_path_close_dirfd(pc); } -static void collect_processes(struct lsfd_control *ctl) +static void parse_pids(const char *str, pid_t pids[], int *count, const int pids_size) +{ + long v; + char *next = NULL; + + if (!(*count < pids_size)) + err(EXIT_FAILURE, _("too many pids (more than %d) are specified"), pids_size); + if (*str == '\0') + return; + + errno = 0; + v = strtol(str, &next, 10); + if (errno) + err(EXIT_FAILURE, _("unexpected value for pid specification: %s"), str); + if (next == str) + errx(EXIT_FAILURE, _("garbage at the end of pid specification: %s"), str); + if (v < 0) + errx(EXIT_FAILURE, _("out of range value for pid specification: %ld"), v); + pids[(*count)++] = (pid_t)v; + + while (next && *next != '\0' + && (isspace((unsigned char)*next) || *next == ',')) + next++; + if (*next != '\0') + parse_pids(next, pids, count, pids_size); +} + +static int pidcmp(const void *a, const void *b) +{ + pid_t pa = *(pid_t *)a; + pid_t pb = *(pid_t *)b; + + if (pa < pb) + return -1; + else if (pa == pb) + return 0; + else + return 1; +} + +static void sort_pids(pid_t pids[], const int count) +{ + qsort(pids, count, sizeof(pid_t), pidcmp); +} + +static bool member_pids(const pid_t pid, const pid_t pids[], const int count) +{ + return bsearch(&pid, pids, count, sizeof(pid_t), pidcmp)? true: false; +} + +static void collect_processes(struct lsfd_control *ctl, const pid_t pids[], int n_pids) { DIR *dir; struct dirent *d; @@ -967,7 +1018,8 @@ static void collect_processes(struct lsfd_control *ctl) if (procfs_dirent_get_pid(d, &pid) != 0) continue; - read_process(ctl, pc, pid, 0); + if (n_pids == 0 || member_pids(pid, pids, n_pids)) + read_process(ctl, pc, pid, 0); } closedir(dir); @@ -990,6 +1042,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -r, --raw use raw output format\n"), out); fputs(_(" --sysroot use specified directory as system root\n"), out); fputs(_(" -u, --notruncate don't truncate text in columns\n"), out); + fputs(_(" -p, --pid collect information only specified processes\n"), out); fputs(_(" -Q, --filter apply display filter\n"), out); fputs(_(" --debug-filter dump the innternal data structure of filter and exit\n"), out); @@ -1051,6 +1104,9 @@ int main(int argc, char *argv[]) struct lsfd_control ctl = {}; char *filter_expr = NULL; bool debug_filter = false; +#define MAX_PIDS 128 + pid_t pids[MAX_PIDS] = {}; + int n_pids = 0; enum { OPT_SYSROOT = CHAR_MAX + 1, @@ -1066,6 +1122,7 @@ int main(int argc, char *argv[]) { "threads", no_argument, NULL, 'l' }, { "notruncate", no_argument, NULL, 'u' }, { "sysroot", required_argument, NULL, OPT_SYSROOT }, + { "pid", required_argument, NULL, 'p' }, { "filter", required_argument, NULL, 'Q' }, { "debug-filter",no_argument, NULL, OPT_DEBUG_FILTER }, { NULL, 0, NULL, 0 }, @@ -1076,7 +1133,7 @@ int main(int argc, char *argv[]) textdomain(PACKAGE); close_stdout_atexit(); - while ((c = getopt_long(argc, argv, "no:JrVhluQ:", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "no:JrVhluQ:p:", longopts, NULL)) != -1) { switch (c) { case 'n': ctl.noheadings = 1; @@ -1099,6 +1156,9 @@ int main(int argc, char *argv[]) case OPT_SYSROOT: ctl.sysroot = optarg; break; + case 'p': + parse_pids(optarg, pids, &n_pids, MAX_PIDS); + break; case 'Q': append_filter_expr(&filter_expr, optarg, true); break; @@ -1174,11 +1234,14 @@ int main(int argc, char *argv[]) free(filter_expr); } + if (n_pids > 0) + sort_pids(pids, n_pids); + /* collect data */ initialize_nodevs(); initialize_classes(); - collect_processes(&ctl); + collect_processes(&ctl, pids, n_pids); convert(&ctl.procs, &ctl); emit(&ctl); diff --git a/tests/expected/lsfd/option-pid b/tests/expected/lsfd/option-pid new file mode 100644 index 0000000000..f05aa57721 --- /dev/null +++ b/tests/expected/lsfd/option-pid @@ -0,0 +1,13 @@ +QOUT: 0 +POUT[--pid=1 PID]: 0 +EQ[--pid=1 PID]: 0 +POUT[--pid=PID 1]: 0 +EQ[--pid=PID 1]: 0 +POUT[--pid=1,PID]: 0 +EQ[--pid=1,PID]: 0 +POUT[--pid=PID,1]: 0 +EQ[--pid=PID,1]: 0 +POUT[-p 1 --pid=PID]: 0 +EQ[-p 1 --pid=PID]: 0 +POUT[-p PID --pid=1]: 0 +EQ[-p PID --pid=1]: 0 diff --git a/tests/ts/lsfd/option-pid b/tests/ts/lsfd/option-pid new file mode 100755 index 0000000000..7882dc1b0d --- /dev/null +++ b/tests/ts/lsfd/option-pid @@ -0,0 +1,81 @@ +#!/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="directory" + +. $TS_TOPDIR/functions.sh +ts_init "$*" + +ts_check_test_command "$TS_CMD_LSFD" +ts_check_test_command "$TS_HELPER_MKFDS" +ts_check_prog "ps" + +ts_cd "$TS_OUTDIR" + +ts_skip_nonroot + +[ "$(ps --no-headers -o comm 1)" = 'systemd' ] || ts_skip "pid 1 is not systemd" + +PID= +FD=3 +EXPR= +QOUT= +POUT= + +{ + coproc MKFDS { "$TS_HELPER_MKFDS" ro-regular-file $FD file=/etc/group; } + if read -u ${MKFDS[0]} PID; then + EXPR='((PID == '"${PID}"') or (PID == 1)) and (FD == '"$FD"')' + QOUT=$(${TS_CMD_LSFD} -n -o ASSOC,MODE,TYPE,FLAGS,NAME -Q "${EXPR}") + echo "QOUT:" $? + + POUT=$(${TS_CMD_LSFD} --pid="1 $PID" -n -o ASSOC,MODE,TYPE,FLAGS,NAME -Q "(FD == $FD)") + echo "POUT[--pid=1 PID]:" $? + [ "${QOUT}" = "${POUT}" ] + echo "EQ[--pid=1 PID]:" $? + + POUT=$(${TS_CMD_LSFD} --pid="$PID 1" -n -o ASSOC,MODE,TYPE,FLAGS,NAME -Q "(FD == $FD)") + echo "POUT[--pid=PID 1]:" $? + [ "${QOUT}" = "${POUT}" ] + echo "EQ[--pid=PID 1]:" $? + + POUT=$(${TS_CMD_LSFD} --pid="1,$PID" -n -o ASSOC,MODE,TYPE,FLAGS,NAME -Q "(FD == $FD)") + echo "POUT[--pid=1,PID]:" $? + [ "${QOUT}" = "${POUT}" ] + echo "EQ[--pid=1,PID]:" $? + + POUT=$(${TS_CMD_LSFD} --pid="$PID,1" -n -o ASSOC,MODE,TYPE,FLAGS,NAME -Q "(FD == $FD)") + echo "POUT[--pid=PID,1]:" $? + [ "${QOUT}" = "${POUT}" ] + echo "EQ[--pid=PID,1]:" $? + + POUT=$(${TS_CMD_LSFD} -p 1 --pid="$PID" -n -o ASSOC,MODE,TYPE,FLAGS,NAME -Q "(FD == $FD)") + echo "POUT[-p 1 --pid=PID]:" $? + [ "${QOUT}" = "${POUT}" ] + echo "EQ[-p 1 --pid=PID]:" $? + + POUT=$(${TS_CMD_LSFD} -p "$PID" --pid=1 -n -o ASSOC,MODE,TYPE,FLAGS,NAME -Q "(FD == $FD)") + echo "POUT[-p PID --pid=1]:" $? + [ "${QOUT}" = "${POUT}" ] + echo "EQ[-p PID --pid=1]:" $? + + kill -CONT ${PID} + wait ${MKFDS_PID} + fi +} > $TS_OUTPUT 2>&1 + +ts_finalize