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;
                         }
                 }