Index: usr.bin/procstat/Makefile =================================================================== --- usr.bin/procstat/Makefile +++ usr.bin/procstat/Makefile @@ -9,6 +9,8 @@ procstat_auxv.c \ procstat_basic.c \ procstat_bin.c \ + procstat_compat_penv.c \ + procstat_compat_pwdx.c \ procstat_cred.c \ procstat_cs.c \ procstat_files.c \ @@ -20,8 +22,16 @@ 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 @@ -4,6 +4,7 @@ * Copyright (c) 2007 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * Copyright (c) 2017 Dell EMC + * Copyright (c) 2020 Juraj Lutter * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,14 +43,23 @@ PS_OPT_NOHEADER = 0x02, PS_OPT_PERTHREAD = 0x04, PS_OPT_SIGNUM = 0x08, - PS_OPT_VERBOSE = 0x10 + PS_OPT_VERBOSE = 0x10, + PS_OPT_NOPIDSTR = 0x20, + PS_OPT_LISTOPENED = 0x40 }; +enum { + PS_MODE_NORMAL = 0x00, + PS_MODE_COMPAT = 0x01 +}; + #define PS_SUBCOMMAND_OPTS \ (PS_OPT_CAPABILITIES | PS_OPT_SIGNUM | \ PS_OPT_PERTHREAD | PS_OPT_VERBOSE) extern int procstat_opts; +extern int procstat_mode; +extern const char * xocontainer; struct kinfo_proc; void kinfo_proc_sort(struct kinfo_proc *kipp, int count); @@ -74,4 +84,8 @@ void procstat_threads_sigs(struct procstat *prstat, struct kinfo_proc *kipp); void procstat_vm(struct procstat *prstat, struct kinfo_proc *kipp); +void procstat_compat_pwdx(struct procstat *prstat, struct kinfo_proc *kipp); +void procstat_compat_penv(struct procstat *prstat, struct kinfo_proc *kipp); +void procstat_compat_pargs(struct procstat *prstat, struct kinfo_proc *kipp); + #endif /* !PROCSTAT_H */ 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,9 @@ .Op Fl N Ar system .Op Fl w Ar interval .Ar core ... +.Nm pargs | penv | pwdx +.Op Fl -libxo +.Ar pid ... .Sh DESCRIPTION .Nm utility displays detailed information about the processes identified by the @@ -112,6 +115,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 mimics the Solaris utilities of the same names. +They display the arguments, environment and current working directory, +respectively of the process specified by +.Ar pid +argument. +.Pp If the .Fl -libxo flag is specified the output is generated via @@ -121,7 +135,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). @@ -430,7 +445,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 +726,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 @@ -4,6 +4,7 @@ * Copyright (c) 2007, 2011 Robert N. M. Watson * Copyright (c) 2015 Allan Jude * Copyright (c) 2017 Dell EMC + * Copyright (c) 2020 Juraj Lutter * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,9 +58,12 @@ void (*cmd)(struct procstat *, struct kinfo_proc *); void (*opt)(int, char * const *); int cmp; + int mode; }; int procstat_opts = 0; +int procstat_mode = PS_MODE_NORMAL; +const char * xocontainer; static void cmdopt_none(int argc, char * const argv[]); static void cmdopt_verbose(int argc, char * const argv[]); @@ -68,45 +72,72 @@ static void cmdopt_files(int argc, char * const argv[]); static void cmdopt_cpuset(int argc, char * const argv[]); +static void usage_procstat(void); +static void usage_compat(void); + +/* pointer to actual usage function */ +static void (*usage_func)(void); + +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[] = { + { "pwdx", "pwd", (const char *)&usage_compat, &procstat_compat_pwdx, &cmdopt_none, + PS_CMP_NORMAL, PS_MODE_COMPAT }, + /* arguments are the same as for pwdx: pid or core file */ + { "penv", "env", (const char *)&usage_compat, &procstat_compat_penv, &cmdopt_none, + PS_CMP_NORMAL, PS_MODE_COMPAT }, + { "pargs", "args", (const char *)&usage_compat, &procstat_compat_pargs, &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 }, - { "auxv", "auxv", NULL, &procstat_auxv, &cmdopt_none, PS_CMP_NORMAL }, + PS_CMP_PLURAL | PS_CMP_SUBSTR, PS_MODE_NORMAL }, + { "auxv", "auxv", NULL, &procstat_auxv, &cmdopt_none, PS_CMP_NORMAL, + PS_MODE_NORMAL }, { "basic", "basic", NULL, &procstat_basic, &cmdopt_none, - PS_CMP_NORMAL }, + PS_CMP_NORMAL, PS_MODE_NORMAL }, { "binary", "binary", NULL, &procstat_bin, &cmdopt_none, - PS_CMP_SUBSTR }, - { "cpuset", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL }, - { "cs", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL }, + PS_CMP_SUBSTR, PS_MODE_NORMAL }, + { "cpuset", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL, + PS_MODE_NORMAL }, + { "cs", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL, + PS_MODE_NORMAL }, { "credential", "credentials", NULL, &procstat_cred, &cmdopt_none, - PS_CMP_PLURAL | PS_CMP_SUBSTR }, + PS_CMP_PLURAL | PS_CMP_SUBSTR, PS_MODE_NORMAL }, { "environment", "environment", NULL, &procstat_env, &cmdopt_none, - PS_CMP_SUBSTR }, + PS_CMP_SUBSTR, PS_MODE_NORMAL }, { "fd", "files", "[-C]", &procstat_files, &cmdopt_files, - PS_CMP_PLURAL }, + PS_CMP_PLURAL, PS_MODE_NORMAL }, { "file", "files", "[-C]", &procstat_files, &cmdopt_files, - PS_CMP_PLURAL }, + PS_CMP_PLURAL, PS_MODE_NORMAL }, { "kstack", "kstack", "[-v]", &procstat_kstack, &cmdopt_verbose, - PS_CMP_NORMAL }, + PS_CMP_NORMAL, PS_MODE_NORMAL }, { "ptlwpinfo", "ptlwpinfo", NULL, &procstat_ptlwpinfo, &cmdopt_none, - PS_CMP_NORMAL }, + PS_CMP_NORMAL, PS_MODE_NORMAL }, { "rlimit", "rlimit", NULL, &procstat_rlimit, &cmdopt_none, - PS_CMP_NORMAL }, + PS_CMP_NORMAL, PS_MODE_NORMAL }, { "rusage", "rusage", "[-Ht]", &procstat_rusage, &cmdopt_rusage, - PS_CMP_NORMAL }, + PS_CMP_NORMAL, PS_MODE_NORMAL }, { "sigfastblock", "sigfastblock", NULL, &procstat_sigfastblock, - &cmdopt_none, PS_CMP_NORMAL }, + &cmdopt_none, PS_CMP_NORMAL, PS_MODE_NORMAL }, { "signal", "signals", "[-n]", &procstat_sigs, &cmdopt_signals, - PS_CMP_PLURAL | PS_CMP_SUBSTR }, + PS_CMP_PLURAL | PS_CMP_SUBSTR, PS_MODE_NORMAL }, { "thread", "threads", NULL, &procstat_threads, &cmdopt_none, - PS_CMP_PLURAL }, + PS_CMP_PLURAL, PS_MODE_NORMAL }, { "tsignal", "thread_signals", "[-n]", &procstat_threads_sigs, - &cmdopt_signals, PS_CMP_PLURAL | PS_CMP_SUBSTR }, - { "vm", "vm", NULL, &procstat_vm, &cmdopt_none, PS_CMP_NORMAL } + &cmdopt_signals, PS_CMP_PLURAL | PS_CMP_SUBSTR, PS_MODE_NORMAL }, + { "vm", "vm", NULL, &procstat_vm, &cmdopt_none, PS_CMP_NORMAL, + PS_MODE_NORMAL } }; static void -usage(void) +usage_procstat(void) { size_t i, l; int multi; @@ -149,18 +180,39 @@ } static void +usage_compat(void) +{ + xo_error("usage: %s [--libxo] pid ...\n", progname); + xo_finish(); + exit(EX_USAGE); +} + +static void +usage(void) +{ + if (usage_func != NULL) + usage_func(); + else + usage_procstat(); +} + +static void procstat(const struct procstat_cmd *cmd, struct procstat *prstat, struct kinfo_proc *kipp) { char *pidstr = NULL; - asprintf(&pidstr, "%d", kipp->ki_pid); - if (pidstr == NULL) - xo_errc(1, ENOMEM, "Failed to allocate memory in procstat()"); - xo_open_container(pidstr); + if ((procstat_opts & PS_OPT_NOPIDSTR) == 0) { + asprintf(&pidstr, "%d", kipp->ki_pid); + if (pidstr == NULL) + xo_errc(1, ENOMEM, "Failed to allocate memory in procstat()"); + xo_open_container(pidstr); + } cmd->cmd(prstat, kipp); - xo_close_container(pidstr); - free(pidstr); + if ((procstat_opts & PS_OPT_NOPIDSTR) == 0) { + xo_close_container(pidstr); + free(pidstr); + } } /* @@ -203,6 +255,28 @@ } static const struct procstat_cmd * +getcmdbyprogname(const char *pprogname) +{ + size_t i, len; + const char *ca; + + if (pprogname == NULL) + return NULL; + len = strlen(pprogname); + + for (i = 0; i < nitems(pacmd_table); i++) { + ca = pacmd_table[i].command; + if (ca != NULL && + strlen(ca) == len && + strncmp(pprogname + len - strlen(ca), ca, strlen(ca)) == 0) { + return &pacmd_table[i]; + } + } + + return NULL; +} + +static const struct procstat_cmd * getcmd(const char *str) { const struct procstat_cmd *cmd; @@ -210,7 +284,7 @@ int cmp, s; if (str == NULL) - return (NULL); + return NULL; cmd = NULL; if ((l = strlen(str)) == 0) return (getcmd("basic")); @@ -252,9 +326,16 @@ interval = 0; cmd = NULL; memf = nlistf = NULL; + progname = NULL; aflag = 0; argc = xo_parse_args(argc, argv); + progname = getprogname(); + cmd = getcmdbyprogname(progname); + if ((cmd != NULL) && (cmd->usage != NULL)) { + usage_func = (const void*)(cmd->usage); + } + while ((ch = getopt(argc, argv, "abCcefHhijkLlM:N:nrSstvw:x")) != -1) { switch (ch) { case 'a': @@ -376,26 +457,31 @@ 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(); + procstat_mode = cmd->mode; if (cmd->opt != NULL) { optreset = 1; optind = 1; cmd->opt(argc, argv); - argc -= optind; - argv += optind; + if (procstat_mode == PS_MODE_NORMAL) { + 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 && + procstat_mode == 0) + usage(); /* Must specify either the -a flag or a list of pids. */ if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0)) @@ -408,9 +494,10 @@ 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); @@ -441,26 +528,32 @@ procstat(cmd, prstat, p); procstat_freeprocs(prstat, p); } else { - cprstat = procstat_open_core(argv[i]); - if (cprstat == NULL) { - warnx("procstat_open()"); - continue; + if (procstat_mode == PS_MODE_NORMAL) { + 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(); } - 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 (procstat_mode == PS_MODE_NORMAL) { + /* 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); Index: usr.bin/procstat/procstat_compat_penv.c =================================================================== --- usr.bin/procstat/procstat_compat_penv.c +++ usr.bin/procstat/procstat_compat_penv.c @@ -0,0 +1,83 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2007 Robert N. M. Watson + * Copyright (c) 2015 Allan Jude + * 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. + * + * $FreeBSD$ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "procstat.h" + +void +procstat_compat_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:args[}{Ld:/%d}{Ldwc:]}{l:args/%s}\n", i, args[i]); + } +} + +void +procstat_compat_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_compat_pwdx.c =================================================================== --- usr.bin/procstat/procstat_compat_pwdx.c +++ usr.bin/procstat/procstat_compat_pwdx.c @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2007-2011 Robert N. M. Watson + * Copyright (c) 2015 Allan Jude + * 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "procstat.h" + +void +procstat_compat_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); +}