Print this page
OS-3463 expose process argv through procfs
OS-3207 in lx zone, 'ps auxww' does not show full cmdline for processes
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
OS-3383 lx brand: node.js test test-setproctitle.js fails
OS-15 add procfs equivalent to prctl(PR_SET_NAME)
@@ -19,11 +19,11 @@
* CDDL HEADER END
*/
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
@@ -94,10 +94,15 @@
};
#define PRSDSIZE (sizeof (struct prdirect))
/*
+ * Maximum length of the /proc/$$/argv file:
+ */
+int prmaxargvlen = 4096;
+
+/*
* Directory characteristics.
*/
typedef struct prdirent {
ino64_t d_ino; /* "inode number" of entry */
off64_t d_off; /* offset of disk directory entry */
@@ -164,10 +169,12 @@
"contracts" },
#if defined(__x86)
{ PR_LDT, 27 * sizeof (prdirent_t), sizeof (prdirent_t),
"ldt" },
#endif
+ { PR_ARGV, 28 * sizeof (prdirent_t), sizeof (prdirent_t),
+ "argv" },
};
#define NPIDDIRFILES (sizeof (piddir) / sizeof (piddir[0]) - 2)
/*
@@ -580,10 +587,11 @@
pr_read_map(), pr_read_rmap(), pr_read_xmap(),
pr_read_cred(), pr_read_sigact(), pr_read_auxv(),
#if defined(__x86)
pr_read_ldt(),
#endif
+ pr_read_argv(),
pr_read_usage(), pr_read_lusage(), pr_read_pagedata(),
pr_read_watch(), pr_read_lwpstatus(), pr_read_lwpsinfo(),
pr_read_lwpusage(), pr_read_xregs(), pr_read_priv(),
pr_read_spymaster(),
#if defined(__sparc)
@@ -608,10 +616,11 @@
pr_read_sigact, /* /proc/<pid>/sigact */
pr_read_auxv, /* /proc/<pid>/auxv */
#if defined(__x86)
pr_read_ldt, /* /proc/<pid>/ldt */
#endif
+ pr_read_argv, /* /proc/<pid>/argv */
pr_read_usage, /* /proc/<pid>/usage */
pr_read_lusage, /* /proc/<pid>/lusage */
pr_read_pagedata, /* /proc/<pid>/pagedata */
pr_read_watch, /* /proc/<pid>/watch */
pr_read_inval, /* /proc/<pid>/cwd */
@@ -670,10 +679,45 @@
return (error);
}
static int
+pr_read_argv(prnode_t *pnp, uio_t *uiop)
+{
+ char *args;
+ int error;
+ size_t asz = prmaxargvlen, sz;
+
+ /*
+ * Allocate a scratch buffer for collection of the process arguments.
+ */
+ args = kmem_alloc(asz, KM_SLEEP);
+
+ ASSERT(pnp->pr_type == PR_ARGV);
+
+ if ((error = prlock(pnp, ZNO)) != 0) {
+ kmem_free(args, asz);
+ return (error);
+ }
+
+ if ((error = prreadargv(pnp->pr_common->prc_proc, args, asz,
+ &sz)) != 0) {
+ prunlock(pnp);
+ kmem_free(args, asz);
+ return (error);
+ }
+
+ prunlock(pnp);
+
+ error = pr_uioread(args, sz, uiop);
+
+ kmem_free(args, asz);
+
+ return (error);
+}
+
+static int
pr_read_as(prnode_t *pnp, uio_t *uiop)
{
int error;
ASSERT(pnp->pr_type == PR_AS);
@@ -1765,10 +1809,11 @@
pr_read_sigact_32, /* /proc/<pid>/sigact */
pr_read_auxv_32, /* /proc/<pid>/auxv */
#if defined(__x86)
pr_read_ldt, /* /proc/<pid>/ldt */
#endif
+ pr_read_argv, /* /proc/<pid>/argv */
pr_read_usage_32, /* /proc/<pid>/usage */
pr_read_lusage_32, /* /proc/<pid>/lusage */
pr_read_pagedata_32, /* /proc/<pid>/pagedata */
pr_read_watch_32, /* /proc/<pid>/watch */
pr_read_inval, /* /proc/<pid>/cwd */
@@ -2684,10 +2729,107 @@
#else
return (pr_read_function[pnp->pr_type](pnp, uiop));
#endif
}
+/*
+ * We make pr_write_psinfo_fname() somewhat simpler by asserting at compile
+ * time that PRFNSZ has the same definition as MAXCOMLEN.
+ */
+#if PRFNSZ != MAXCOMLEN
+#error PRFNSZ/MAXCOMLEN mismatch
+#endif
+
+static int
+pr_write_psinfo_fname(prnode_t *pnp, uio_t *uiop)
+{
+ char fname[PRFNSZ];
+ int offset = offsetof(psinfo_t, pr_fname), error;
+
+#ifdef _SYSCALL32_IMPL
+ if (curproc->p_model != DATAMODEL_LP64)
+ offset = offsetof(psinfo32_t, pr_fname);
+#endif
+
+ /*
+ * If this isn't a write to pr_fname (or if the size doesn't match
+ * PRFNSZ) return.
+ */
+ if (uiop->uio_offset != offset || uiop->uio_resid != PRFNSZ)
+ return (0);
+
+ if ((error = uiomove(fname, PRFNSZ, UIO_WRITE, uiop)) != 0)
+ return (error);
+
+ fname[PRFNSZ - 1] = '\0';
+
+ if ((error = prlock(pnp, ZNO)) != 0)
+ return (error);
+
+ bcopy(fname, pnp->pr_common->prc_proc->p_user.u_comm, PRFNSZ);
+
+ prunlock(pnp);
+
+ return (0);
+}
+
+/*
+ * We make pr_write_psinfo_psargs() somewhat simpler by asserting at compile
+ * time that PRARGSZ has the same definition as PSARGSZ.
+ */
+#if PRARGSZ != PSARGSZ
+#error PRARGSZ/PSARGSZ mismatch
+#endif
+
+static int
+pr_write_psinfo_psargs(prnode_t *pnp, uio_t *uiop)
+{
+ char psargs[PRARGSZ];
+ int offset = offsetof(psinfo_t, pr_psargs), error;
+
+#ifdef _SYSCALL32_IMPL
+ if (curproc->p_model != DATAMODEL_LP64)
+ offset = offsetof(psinfo32_t, pr_psargs);
+#endif
+
+ /*
+ * If this isn't a write to pr_psargs (or if the size doesn't match
+ * PRARGSZ) return.
+ */
+ if (uiop->uio_offset != offset || uiop->uio_resid != PRARGSZ)
+ return (0);
+
+ if ((error = uiomove(psargs, PRARGSZ, UIO_WRITE, uiop)) != 0)
+ return (error);
+
+ psargs[PRARGSZ - 1] = '\0';
+
+ if ((error = prlock(pnp, ZNO)) != 0)
+ return (error);
+
+ bcopy(psargs, pnp->pr_common->prc_proc->p_user.u_psargs, PRARGSZ);
+
+ prunlock(pnp);
+
+ return (0);
+}
+
+int
+pr_write_psinfo(prnode_t *pnp, uio_t *uiop)
+{
+ int error;
+
+ if ((error = pr_write_psinfo_fname(pnp, uiop)) != 0)
+ return (error);
+
+ if ((error = pr_write_psinfo_psargs(pnp, uiop)) != 0)
+ return (error);
+
+ return (0);
+}
+
+
/* ARGSUSED */
static int
prwrite(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct)
{
prnode_t *pnp = VTOP(vp);
@@ -2762,10 +2904,13 @@
*/
if (error == EINTR)
uiop->uio_resid = resid;
return (error);
+ case PR_PSINFO:
+ return (pr_write_psinfo(pnp, uiop));
+
default:
return ((vp->v_type == VDIR)? EISDIR : EBADF);
}
/* NOTREACHED */
}
@@ -3045,10 +3190,17 @@
PR_OBJSIZE(struct sigaction32, struct sigaction);
break;
case PR_AUXV:
vap->va_size = __KERN_NAUXV_IMPL * PR_OBJSIZE(auxv32_t, auxv_t);
break;
+ case PR_ARGV:
+ if ((p->p_flag & SSYS) || p->p_as == &kas) {
+ vap->va_size = PSARGSZ;
+ } else {
+ vap->va_size = prmaxargvlen;
+ }
+ break;
#if defined(__x86)
case PR_LDT:
mutex_exit(&p->p_lock);
mutex_enter(&p->p_ldtlock);
vap->va_size = prnldt(p) * sizeof (struct ssd);
@@ -3220,10 +3372,11 @@
case PR_LWPDIR:
case PR_LWPIDDIR:
case PR_USAGE:
case PR_LUSAGE:
case PR_LWPUSAGE:
+ case PR_ARGV:
p = pr_p_lock(pnp);
mutex_exit(&pr_pidlock);
if (p == NULL)
return (ENOENT);
prunlock(pnp);
@@ -3305,10 +3458,11 @@
pr_lookup_notdir, /* /proc/<pid>/sigact */
pr_lookup_notdir, /* /proc/<pid>/auxv */
#if defined(__x86)
pr_lookup_notdir, /* /proc/<pid>/ldt */
#endif
+ pr_lookup_notdir, /* /proc/<pid>/argv */
pr_lookup_notdir, /* /proc/<pid>/usage */
pr_lookup_notdir, /* /proc/<pid>/lusage */
pr_lookup_notdir, /* /proc/<pid>/pagedata */
pr_lookup_notdir, /* /proc/<pid>/watch */
pr_lookup_notdir, /* /proc/<pid>/cwd */
@@ -4544,15 +4698,19 @@
case PR_LWPIDFILE:
pnp->pr_mode = 0600; /* read-write by owner only */
break;
case PR_PSINFO:
+ pnp->pr_mode = 0644; /* readable by all + owner can write */
+ break;
+
case PR_LPSINFO:
case PR_LWPSINFO:
case PR_USAGE:
case PR_LUSAGE:
case PR_LWPUSAGE:
+ case PR_ARGV:
pnp->pr_mode = 0444; /* read-only by all */
break;
default:
pnp->pr_mode = 0400; /* read-only by owner only */
@@ -4654,10 +4812,11 @@
pr_readdir_notdir, /* /proc/<pid>/sigact */
pr_readdir_notdir, /* /proc/<pid>/auxv */
#if defined(__x86)
pr_readdir_notdir, /* /proc/<pid>/ldt */
#endif
+ pr_readdir_notdir, /* /proc/<pid>/argv */
pr_readdir_notdir, /* /proc/<pid>/usage */
pr_readdir_notdir, /* /proc/<pid>/lusage */
pr_readdir_notdir, /* /proc/<pid>/pagedata */
pr_readdir_notdir, /* /proc/<pid>/watch */
pr_readdir_notdir, /* /proc/<pid>/cwd */
@@ -4803,10 +4962,11 @@
switch (dirp->d_ino) {
case PR_PIDDIR:
case PR_PROCDIR:
case PR_PSINFO:
case PR_USAGE:
+ case PR_ARGV:
break;
default:
continue;
}
}