aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2020-09-18 12:59:27 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2020-09-18 12:59:27 +0000
commit085e04945cc3cb3a725a2b0cd992ad0358e49f7e (patch)
tree365987687b1458022eee70728746e98daf3eaf07
parentd99cb9802bf2a6d1c921dc1b3eea0b6e8e15f7fb (diff)
downloadsrc-085e04945cc3cb3a725a2b0cd992ad0358e49f7e.tar.gz
src-085e04945cc3cb3a725a2b0cd992ad0358e49f7e.zip
Add pargs, penv, pwdx commands and aliases to procstat(1).
Intent is to mimic Solaris commands with the same names. Submitted by: Juraj Lutter <juraj@lutter.sk> MFC after: 1 week Differential revision: https://reviews.freebsd.org/D26310
Notes
Notes: svn path=/head/; revision=365880
-rw-r--r--usr.bin/procstat/Makefile10
-rw-r--r--usr.bin/procstat/procstat.137
-rw-r--r--usr.bin/procstat/procstat.c246
-rw-r--r--usr.bin/procstat/procstat.h6
-rw-r--r--usr.bin/procstat/procstat_kstack.c2
-rw-r--r--usr.bin/procstat/procstat_penv.c86
-rw-r--r--usr.bin/procstat/procstat_pwdx.c70
7 files changed, 360 insertions, 97 deletions
diff --git a/usr.bin/procstat/Makefile b/usr.bin/procstat/Makefile
index 03b6cdf933ba..0b88f2f4634a 100644
--- a/usr.bin/procstat/Makefile
+++ b/usr.bin/procstat/Makefile
@@ -13,15 +13,25 @@ SRCS= procstat.c \
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
diff --git a/usr.bin/procstat/procstat.1 b/usr.bin/procstat/procstat.1
index f0a9c6242dab..17da8648b714 100644
--- a/usr.bin/procstat/procstat.1
+++ b/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 @@ flag is used, all processes.
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 @@ See
.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 thread information for the process.
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 @@ process signal disposition details, three symbols
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 @@ support was added by
.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.
diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c
index 72456fa9815b..91cab9be1028 100644
--- a/usr.bin/procstat/procstat.c
+++ b/usr.bin/procstat/procstat.c
@@ -68,6 +68,23 @@ static void cmdopt_rusage(int argc, char * const argv[]);
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 @@ static const struct procstat_cmd cmd_table[] = {
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 const struct procstat_cmd cmd_table[] = {
};
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);
@@ -203,6 +232,25 @@ kinfo_proc_thread_name(const struct kinfo_proc *kipp)
}
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)
{
const struct procstat_cmd *cmd;
@@ -237,17 +285,14 @@ getcmd(const char *str)
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
break;
case 'b':
if (cmd != NULL)
- usage();
+ usage(cmd);
cmd = getcmd("binary");
break;
case 'C':
@@ -270,17 +318,17 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ main(int argc, char *argv[])
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 @@ cmdopt_none(int argc, char * const argv[])
switch (ch) {
case '?':
default:
- usage();
+ usage(NULL);
}
}
}
@@ -497,7 +559,7 @@ cmdopt_verbose(int argc, char * const argv[])
break;
case '?':
default:
- usage();
+ usage(NULL);
}
}
}
@@ -514,7 +576,7 @@ cmdopt_signals(int argc, char * const argv[])
break;
case '?':
default:
- usage();
+ usage(NULL);
}
}
}
@@ -533,7 +595,7 @@ cmdopt_rusage(int argc, char * const argv[])
break;
case '?':
default:
- usage();
+ usage(NULL);
}
}
}
@@ -550,7 +612,7 @@ cmdopt_files(int argc, char * const argv[])
break;
case '?':
default:
- usage();
+ usage(NULL);
}
}
}
diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h
index a839949b5a5d..5a0bc193ab45 100644
--- a/usr.bin/procstat/procstat.h
+++ b/usr.bin/procstat/procstat.h
@@ -42,7 +42,8 @@ enum {
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_cs(struct procstat *prstat, struct kinfo_proc *kipp);
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,
diff --git a/usr.bin/procstat/procstat_kstack.c b/usr.bin/procstat/procstat_kstack.c
index 8992292d4c5e..e633f40bad55 100644
--- a/usr.bin/procstat/procstat_kstack.c
+++ b/usr.bin/procstat/procstat_kstack.c
@@ -68,7 +68,7 @@ kstack_nextstate(enum trace_state ts)
return (TS_OFF);
case TS_OFF:
- return TS_FRAMENUM;
+ return (TS_FRAMENUM);
default:
errx(-1, "kstack_nextstate");
diff --git a/usr.bin/procstat/procstat_penv.c b/usr.bin/procstat/procstat_penv.c
new file mode 100644
index 000000000000..d7e51d2fb04c
--- /dev/null
+++ b/usr.bin/procstat/procstat_penv.c
@@ -0,0 +1,86 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Juraj Lutter <juraj@lutter.sk>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <err.h>
+#include <errno.h>
+#include <libprocstat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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]);
+ }
+ }
+}
diff --git a/usr.bin/procstat/procstat_pwdx.c b/usr.bin/procstat/procstat_pwdx.c
new file mode 100644
index 000000000000..22550ec8233c
--- /dev/null
+++ b/usr.bin/procstat/procstat_pwdx.c
@@ -0,0 +1,70 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Juraj Lutter <juraj@lutter.sk>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+#include <sys/user.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <libprocstat.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+}