Index: usr.bin/procstat/Makefile =================================================================== --- usr.bin/procstat/Makefile +++ usr.bin/procstat/Makefile @@ -13,15 +13,25 @@ procstat_cs.c \ procstat_files.c \ procstat_kstack.c \ + procstat_penv.c \ procstat_ptlwpinfo.c \ + procstat_pwdx.c \ procstat_rlimit.c \ procstat_rusage.c \ procstat_sigs.c \ procstat_threads.c \ procstat_vm.c +MLINKS+= procstat.1 pargs.1 +MLINKS+= procstat.1 penv.1 +MLINKS+= procstat.1 pwdx.1 + LIBADD+= procstat xo util sbuf +LINKS+= ${BINDIR}/procstat ${BINDIR}/pargs +LINKS+= ${BINDIR}/procstat ${BINDIR}/penv +LINKS+= ${BINDIR}/procstat ${BINDIR}/pwdx + HAS_TESTS= SUBDIR.${MK_TESTS}+= tests Index: usr.bin/procstat/procstat.h =================================================================== --- usr.bin/procstat/procstat.h +++ usr.bin/procstat/procstat.h @@ -42,7 +42,8 @@ PS_OPT_NOHEADER = 0x02, PS_OPT_PERTHREAD = 0x04, PS_OPT_SIGNUM = 0x08, - PS_OPT_VERBOSE = 0x10 + PS_OPT_VERBOSE = 0x10, + PS_MODE_COMPAT = 0x20, }; #define PS_SUBCOMMAND_OPTS \ @@ -64,7 +65,10 @@ void procstat_env(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_files(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_kstack(struct procstat *prstat, struct kinfo_proc *kipp); +void procstat_pargs(struct procstat *prstat, struct kinfo_proc *kipp); +void procstat_penv(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_ptlwpinfo(struct procstat *prstat, struct kinfo_proc *kipp); +void procstat_pwdx(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_rlimit(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_rusage(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_sigfastblock(struct procstat *procstat, Index: usr.bin/procstat/procstat.1 =================================================================== --- usr.bin/procstat/procstat.1 +++ usr.bin/procstat/procstat.1 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 4, 2019 +.Dd September 14, 2020 .Dt PROCSTAT 1 .Os .Sh NAME @@ -102,6 +102,15 @@ .Op Fl N Ar system .Op Fl w Ar interval .Ar core ... +.Nm pargs +.Op Fl -libxo +.Ar pid ... +.Nm penv +.Op Fl -libxo +.Ar pid ... +.Nm pwdx +.Op Fl -libxo +.Ar pid ... .Sh DESCRIPTION .Nm utility displays detailed information about the processes identified by the @@ -112,6 +121,17 @@ It can also display information extracted from a process core file, if the core file is specified as the argument. .Pp +The +.Nm pargs , +.Nm penv +and +.Nm pwdx +utilities display the arguments, environment, and current working directory, +respectively of the process specified by +.Ar pid +argument. +They mimic the behavior of Solaris utilities of the same names. +.Pp If the .Fl -libxo flag is specified the output is generated via @@ -121,7 +141,8 @@ .Xr xo_parse_args 3 for details on command line arguments. .Pp -The following commands are available: +The following commands are available for +.Nm : .Bl -tag -width indent .It Ar basic Print basic process statistics (this is the default). @@ -196,6 +217,12 @@ Display virtual memory mappings for the process. .It Ar auxv | Fl x Display ELF auxiliary vector for the process. +.It Ar pargs +Display arguments for the process. +.It Ar penv +Display environment variables for the process. +.It Ar pwdx +Display current working directory for the process. .El .Pp All options generate output in the format of a table, the first field of @@ -430,7 +457,7 @@ if signal is pending in the global process queue; - otherwise. .It I if signal delivery disposition is -.Dv SIG_IGN; +.Dv SIG_IGN ; - otherwise. .It C if the signal will be caught; - otherwise. @@ -711,6 +738,10 @@ .An -nosplit Allan Jude .Aq Mt allanjude@FreeBSD.org . +.br +.An Juraj Lutter +.Aq Mt juraj@lutter.sk +added the pargs, penv and pwdx functionality. .Sh BUGS The display of open file or memory mapping pathnames is implemented using the kernel's name cache. Index: usr.bin/procstat/procstat.c =================================================================== --- usr.bin/procstat/procstat.c +++ usr.bin/procstat/procstat.c @@ -68,6 +68,23 @@ static void cmdopt_files(int argc, char * const argv[]); static void cmdopt_cpuset(int argc, char * const argv[]); +static const char *progname; + +/* aliased program parameters and arguments + * - usage field is abused to hold the pointer to the function + * displaying program usage + */ +static const struct procstat_cmd pacmd_table[] = { + /* arguments are the same as for pwdx: pid or core file */ + { "pargs", "args", NULL, &procstat_pargs, &cmdopt_none, + PS_CMP_NORMAL | PS_MODE_COMPAT }, + { "penv", "env", NULL, &procstat_penv, &cmdopt_none, + PS_CMP_NORMAL | PS_MODE_COMPAT }, + { "pwdx", "pwd", NULL, &procstat_pwdx, &cmdopt_none, + PS_CMP_NORMAL | PS_MODE_COMPAT } +}; + +/* procstat parameters and arguments */ static const struct procstat_cmd cmd_table[] = { { "argument", "arguments", NULL, &procstat_args, &cmdopt_none, PS_CMP_PLURAL | PS_CMP_SUBSTR }, @@ -88,8 +105,14 @@ PS_CMP_PLURAL }, { "kstack", "kstack", "[-v]", &procstat_kstack, &cmdopt_verbose, PS_CMP_NORMAL }, + { "pargs", "args", NULL, &procstat_pargs, &cmdopt_none, + PS_CMP_NORMAL }, + { "penv", "env", NULL, &procstat_penv, &cmdopt_none, + PS_CMP_NORMAL }, { "ptlwpinfo", "ptlwpinfo", NULL, &procstat_ptlwpinfo, &cmdopt_none, PS_CMP_NORMAL }, + { "pwdx", "pwd", NULL, &procstat_pwdx, &cmdopt_none, + PS_CMP_NORMAL }, { "rlimit", "rlimit", NULL, &procstat_rlimit, &cmdopt_none, PS_CMP_NORMAL }, { "rusage", "rusage", "[-Ht]", &procstat_rusage, &cmdopt_rusage, @@ -106,43 +129,49 @@ }; static void -usage(void) +usage(const struct procstat_cmd *cmd) { size_t i, l; int multi; - xo_error("usage: procstat [--libxo] [-h] [-M core] [-N system]" - " [-w interval] command\n" - " [pid ... | core ...]\n" - " procstat [--libxo] -a [-h] [-M core] [-N system] " - " [-w interval] command\n" - " procstat [--libxo] [-h] [-M core] [-N system]" - " [-w interval]\n" - " [-S | -b | -c | -e | -f [-C] | -i [-n] | " - "-j [-n] | -k [-k] |\n" - " -l | -r [-H] | -s | -t | -v | -x] " - "[pid ... | core ...]\n" - " procstat [--libxo] -a [-h] [-M core] [-N system]" - " [-w interval]\n" - " [-S | -b | -c | -e | -f [-C] | -i [-n] | " - "-j [-n] | -k [-k] |\n" - " -l | -r [-H] | -s | -t | -v | -x]\n" - " procstat [--libxo] -L [-h] [-M core] [-N system] core ...\n" - "Available commands:\n"); - for (i = 0, l = nitems(cmd_table); i < l; i++) { - multi = i + 1 < l && cmd_table[i].cmd == cmd_table[i + 1].cmd; - xo_error(" %s%s%s", multi ? "[" : "", - cmd_table[i].command, (cmd_table[i].cmp & PS_CMP_PLURAL) ? - "(s)" : ""); - for (; i + 1 < l && cmd_table[i].cmd == cmd_table[i + 1].cmd; - i++) - xo_error(" | %s%s", cmd_table[i + 1].command, - (cmd_table[i].cmp & PS_CMP_PLURAL) ? "(s)" : ""); - if (multi) - xo_error("]"); - if (cmd_table[i].usage != NULL) - xo_error(" %s", cmd_table[i].usage); - xo_error("\n"); + if (cmd == NULL || (cmd->cmp & PS_MODE_COMPAT) == 0) { + xo_error("usage: procstat [--libxo] [-h] [-M core] [-N system]" + " [-w interval] command\n" + " [pid ... | core ...]\n" + " procstat [--libxo] -a [-h] [-M core] [-N system] " + " [-w interval] command\n" + " procstat [--libxo] [-h] [-M core] [-N system]" + " [-w interval]\n" + " [-S | -b | -c | -e | -f [-C] | -i [-n] | " + "-j [-n] | -k [-k] |\n" + " -l | -r [-H] | -s | -t | -v | -x] " + "[pid ... | core ...]\n" + " procstat [--libxo] -a [-h] [-M core] [-N system]" + " [-w interval]\n" + " [-S | -b | -c | -e | -f [-C] | -i [-n] | " + "-j [-n] | -k [-k] |\n" + " -l | -r [-H] | -s | -t | -v | -x]\n" + " procstat [--libxo] -L [-h] [-M core] [-N system] core ...\n" + "Available commands:\n"); + for (i = 0, l = nitems(cmd_table); i < l; i++) { + multi = i + 1 < l && cmd_table[i].cmd == + cmd_table[i + 1].cmd; + xo_error(" %s%s%s", multi ? "[" : "", + cmd_table[i].command, (cmd_table[i].cmp & + PS_CMP_PLURAL) ? "(s)" : ""); + for (; i + 1 < l && cmd_table[i].cmd == + cmd_table[i + 1].cmd; i++) + xo_error(" | %s%s", cmd_table[i + 1].command, + (cmd_table[i].cmp & PS_CMP_PLURAL) ? + "(s)" : ""); + if (multi) + xo_error("]"); + if (cmd_table[i].usage != NULL) + xo_error(" %s", cmd_table[i].usage); + xo_error("\n"); + } + } else { + xo_error("usage: %s [--libxo] pid ...\n", progname); } xo_finish(); exit(EX_USAGE); @@ -202,6 +231,25 @@ return (name); } +static const struct procstat_cmd * +getcmdbyprogname(const char *pprogname) +{ + const char *ca; + size_t i, len; + + if (pprogname == NULL) + return (NULL); + len = strlen(pprogname); + + for (i = 0; i < nitems(pacmd_table); i++) { + ca = pacmd_table[i].command; + if (ca != NULL && strcmp(ca, pprogname) == 0) + return (&pacmd_table[i]); + } + + return (NULL); +} + static const struct procstat_cmd * getcmd(const char *str) { @@ -237,17 +285,14 @@ int main(int argc, char *argv[]) { - int ch, interval; - int i; struct kinfo_proc *p; const struct procstat_cmd *cmd; struct procstat *prstat, *cprstat; + char *dummy, *nlistf, *memf; + const char *xocontainer; long l; pid_t pid; - char *dummy; - char *nlistf, *memf; - int aflag; - int cnt; + int aflag, ch, cnt, i, interval; interval = 0; cmd = NULL; @@ -255,6 +300,9 @@ aflag = 0; argc = xo_parse_args(argc, argv); + progname = getprogname(); + cmd = getcmdbyprogname(progname); + while ((ch = getopt(argc, argv, "abCcefHhijkLlM:N:nrSstvw:x")) != -1) { switch (ch) { case 'a': @@ -262,7 +310,7 @@ break; case 'b': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("binary"); break; case 'C': @@ -270,17 +318,17 @@ break; case 'c': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("arguments"); break; case 'e': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("environment"); break; case 'f': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("files"); break; case 'H': @@ -291,33 +339,33 @@ break; case 'i': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("signals"); break; case 'j': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("tsignals"); break; case 'k': if (cmd != NULL && cmd->cmd == procstat_kstack) { if ((procstat_opts & PS_OPT_VERBOSE) != 0) - usage(); + usage(cmd); procstat_opts |= PS_OPT_VERBOSE; } else { if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("kstack"); } break; case 'L': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("ptlwpinfo"); break; case 'l': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("rlimit"); break; case 'M': @@ -331,75 +379,79 @@ break; case 'r': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("rusage"); break; case 'S': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("cpuset"); break; case 's': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("credentials"); break; case 't': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("threads"); break; case 'v': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("vm"); break; case 'w': l = strtol(optarg, &dummy, 10); if (*dummy != '\0') - usage(); + usage(cmd); if (l < 1 || l > INT_MAX) - usage(); + usage(cmd); interval = l; break; case 'x': if (cmd != NULL) - usage(); + usage(cmd); cmd = getcmd("auxv"); break; case '?': default: - usage(); + usage(cmd); } } argc -= optind; argv += optind; - if (cmd == NULL && argv[0] != NULL && (cmd = getcmd(argv[0])) != NULL) { + if (cmd == NULL && argv[0] != NULL) + cmd = getcmd(argv[0]); + if (cmd != NULL) { if ((procstat_opts & PS_SUBCOMMAND_OPTS) != 0) - usage(); + usage(cmd); if (cmd->opt != NULL) { optreset = 1; optind = 1; cmd->opt(argc, argv); - argc -= optind; - argv += optind; + if ((cmd->cmp & PS_MODE_COMPAT) == 0) { + argc -= optind; + argv += optind; + } } else { argc -= 1; argv += 1; } } else { - if (cmd == NULL) - cmd = getcmd("basic"); - if (cmd->cmd != procstat_files && - (procstat_opts & PS_OPT_CAPABILITIES) != 0) - usage(); + cmd = getcmd("basic"); } + if (cmd->cmd != procstat_files && + (procstat_opts & PS_OPT_CAPABILITIES) != 0 && + (cmd->cmp & PS_MODE_COMPAT) == 0) + usage(cmd); /* Must specify either the -a flag or a list of pids. */ if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0)) - usage(); + usage(cmd); if (memf != NULL) prstat = procstat_open_kvm(nlistf, memf); @@ -408,9 +460,11 @@ if (prstat == NULL) xo_errx(1, "procstat_open()"); do { + xocontainer = cmd->xocontainer != NULL ? cmd->xocontainer : + cmd->command; xo_set_version(PROCSTAT_XO_VERSION); - xo_open_container("procstat"); - xo_open_container(cmd->xocontainer); + xo_open_container(progname); + xo_open_container(xocontainer); if (aflag) { p = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt); @@ -430,7 +484,7 @@ l = strtol(argv[i], &dummy, 10); if (*dummy == '\0') { if (l < 0) - usage(); + usage(cmd); pid = l; p = procstat_getprocs(prstat, KERN_PROC_PID, @@ -441,26 +495,34 @@ procstat(cmd, prstat, p); procstat_freeprocs(prstat, p); } else { - cprstat = procstat_open_core(argv[i]); - if (cprstat == NULL) { - warnx("procstat_open()"); - continue; + if ((cmd->cmp & PS_MODE_COMPAT) == 0) { + cprstat = procstat_open_core(argv[i]); + if (cprstat == NULL) { + warnx("procstat_open()"); + continue; + } + p = procstat_getprocs(cprstat, + KERN_PROC_PID, -1, &cnt); + if (p == NULL) { + xo_errx(1, + "procstat_getprocs()"); + } + if (cnt != 0) + procstat(cmd, cprstat, p); + procstat_freeprocs(cprstat, p); + procstat_close(cprstat); + } else { + usage(cmd); } - p = procstat_getprocs(cprstat, KERN_PROC_PID, - -1, &cnt); - if (p == NULL) - xo_errx(1, "procstat_getprocs()"); - if (cnt != 0) - procstat(cmd, cprstat, p); - procstat_freeprocs(cprstat, p); - procstat_close(cprstat); } - /* Suppress header after first process. */ - procstat_opts |= PS_OPT_NOHEADER; + if ((cmd->cmp & PS_MODE_COMPAT) == 0) { + /* Suppress header after first process. */ + procstat_opts |= PS_OPT_NOHEADER; + } } - xo_close_container(cmd->xocontainer); - xo_close_container("procstat"); + xo_close_container(xocontainer); + xo_close_container(progname); xo_finish(); if (interval) sleep(interval); @@ -480,7 +542,7 @@ switch (ch) { case '?': default: - usage(); + usage(NULL); } } } @@ -497,7 +559,7 @@ break; case '?': default: - usage(); + usage(NULL); } } } @@ -514,7 +576,7 @@ break; case '?': default: - usage(); + usage(NULL); } } } @@ -533,7 +595,7 @@ break; case '?': default: - usage(); + usage(NULL); } } } @@ -550,7 +612,7 @@ break; case '?': default: - usage(); + usage(NULL); } } } Index: usr.bin/procstat/procstat_kstack.c =================================================================== --- usr.bin/procstat/procstat_kstack.c +++ usr.bin/procstat/procstat_kstack.c @@ -68,7 +68,7 @@ return (TS_OFF); case TS_OFF: - return TS_FRAMENUM; + return (TS_FRAMENUM); default: errx(-1, "kstack_nextstate"); Index: usr.bin/procstat/procstat_penv.c =================================================================== --- /dev/null +++ usr.bin/procstat/procstat_penv.c @@ -0,0 +1,86 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Juraj Lutter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "procstat.h" + +void +procstat_pargs(struct procstat *procstat, struct kinfo_proc *kipp) +{ + int i; + char **args; + + args = procstat_getargv(procstat, kipp, 0); + + xo_emit("{k:process_id/%d}: {:command/%s/%s}\n", kipp->ki_pid, + kipp->ki_comm); + + if (args == NULL) { + xo_emit("{d:args/-}\n"); + } else { + for (i = 0; args[i] != NULL; i++) { + xo_emit("{Ld:argv[}{Ld:/%d}{Ldwc:]}{l:argv/%s}\n", + i, args[i]); + } + } +} + +void +procstat_penv(struct procstat *procstat, struct kinfo_proc *kipp) +{ + int i; + char **envs; + + envs = procstat_getenvv(procstat, kipp, 0); + + xo_emit("{k:process_id/%d}: {:command/%s/%s}\n", kipp->ki_pid, + kipp->ki_comm); + + if (envs == NULL) { + xo_emit("{d:env/-}\n"); + } else { + for (i = 0; envs[i] != NULL; i++) { + xo_emit("{Ld:envp[}{Ld:/%d}{Ldwc:]}{l:envp/%s}\n", + i, envs[i]); + } + } +} Index: usr.bin/procstat/procstat_pwdx.c =================================================================== --- /dev/null +++ usr.bin/procstat/procstat_pwdx.c @@ -0,0 +1,70 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Juraj Lutter + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "procstat.h" + +void +procstat_pwdx(struct procstat *procstat, struct kinfo_proc *kipp) +{ + struct filestat_list *head; + struct filestat *fst; + + head = procstat_getfiles(procstat, kipp, 0); + if (head == NULL) + return; + STAILQ_FOREACH(fst, head, next) { + if ((fst->fs_uflags & PS_FST_UFLAG_CDIR) && + (fst->fs_path != NULL)) { + xo_emit("{k:process_id/%d}{P:: }", kipp->ki_pid); + xo_emit("{:cwd/%s}", fst->fs_path); + xo_emit("\n"); + } + } + procstat_freefiles(procstat, head); +}