#include <err.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stdbool.h>
#include <libproc.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
#define EXIT_USAGE 2
typedef enum {
PWDX_CWD = 1 << 0,
PWDX_MOUNT = 1 << 1,
PWDX_REST = 1 << 2,
PWDX_QUIET = 1 << 3
} pwdx_output_t;
static void
pwdx_usage(const char *fmt, ...)
{
if (fmt != NULL) {
va_list ap;
va_start(ap, fmt);
vwarnx(fmt, ap);
va_end(ap);
}
(void) fprintf(stderr, "Usage: pwdx [-m] [-q | -v] { pid | core } "
"...\n");
(void) fprintf(stderr, "Print process and core current working "
"directory\n"
"\t-m\t\tshow mountpoint path\n"
"\t-q\t\tonly output paths\n"
"\t-v\t\toutput verbose mount information\n");
}
static void
pwdx_escape(char c)
{
(void) printf("\\%c%c%c", '0' + ((c >> 6) & 07), '0' + ((c >> 3) & 07),
'0' + (c & 07));
}
static void
pwdx_puts(const char *str, bool newline)
{
size_t slen = strlen(str), off = 0;
while (off < slen) {
wchar_t wc;
int ret = mbtowc(&wc, str + off, slen - off);
if (ret < 0) {
pwdx_escape(str[off]);
off++;
continue;
} else if (ret == 0) {
break;
}
if (iswprint(wc)) {
(void) putwchar(wc);
} else {
for (int i = 0; i < ret; i++) {
pwdx_escape(str[off + i]);
}
}
off += ret;
}
if (newline) {
(void) putchar('\n');
}
}
static bool
pwdx(const char *str, pwdx_output_t output)
{
int err;
bool ret = false;
struct ps_prochandle *P;
prcwd_t *cwd = NULL;
const psinfo_t *info;
P = proc_arg_grab(str, PR_ARG_ANY, PGRAB_RDONLY, &err);
if (P == NULL) {
warnx("failed to open %s: %s", str, Pgrab_error(err));
return (false);
}
info = Ppsinfo(P);
if (info == NULL) {
warn("failed to get psinfo from %s", str);
goto out;
}
if (Pcwd(P, &cwd) != 0) {
warn("failed to read cwd of %s", str);
goto out;
}
if ((output & PWDX_QUIET) == 0) {
if (Pstate(P) == PS_DEAD) {
(void) printf("core '%s' of ", str);
}
(void) printf("%" _PRIdID ":\t", info->pr_pid);
}
if ((output & PWDX_CWD) != 0) {
pwdx_puts(cwd->prcwd_cwd, true);
} else {
if (cwd->prcwd_mntpt[0] != '\0') {
pwdx_puts(cwd->prcwd_mntpt, true);
} else {
(void) puts("<unknown>");
}
}
if (output & PWDX_REST) {
const char *spec, *point;
if (cwd->prcwd_mntspec[0] == '\0') {
spec = "unknown";
} else {
spec = cwd->prcwd_mntspec;
}
if (cwd->prcwd_mntpt[0] == '\0') {
point = "unknown";
} else {
point = cwd->prcwd_mntpt;
}
(void) printf("\tMountpoint ");
pwdx_puts(point, false);
(void) printf(" on ");
pwdx_puts(spec, true);
(void) printf("\tFilesystem %s (ID: 0x%" PRIx64 ")\n",
cwd->prcwd_fsname, cwd->prcwd_fsid);
}
ret = true;
out:
Pcwd_free(cwd);
Pfree(P);
return (ret);
}
int
main(int argc, char *argv[])
{
int ret = EXIT_SUCCESS;
pwdx_output_t output = PWDX_CWD;
int c;
(void) setlocale(LC_ALL, "");
while ((c = getopt(argc, argv, ":mqv")) != -1) {
switch (c) {
case 'm':
output |= PWDX_MOUNT;
output &= ~PWDX_CWD;
break;
case 'q':
if (output & PWDX_REST) {
errx(EXIT_USAGE, "only one of -q and -v may be "
"specified");
}
output |= PWDX_QUIET;
break;
case 'v':
if (output & PWDX_QUIET) {
errx(EXIT_USAGE, "only one of -q and -v may be "
"specified");
}
output |= PWDX_CWD | PWDX_MOUNT | PWDX_REST;
break;
case ':':
pwdx_usage("option -%c requires an "
"argument", optopt);
exit(EXIT_USAGE);
case '?':
pwdx_usage("unknown option: -%c", optopt);
exit(EXIT_USAGE);
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
errx(EXIT_FAILURE, "at least one process must be specified");
}
for (int i = 0; i < argc; i++) {
if (!pwdx(argv[i], output))
ret = EXIT_FAILURE;
}
return (ret);
}