Author: trociny
Date: Sat Apr 20 08:07:04 2013
New Revision: 249679
URL: http://svnweb.freebsd.org/changeset/base/249679

Log:
  Extend libprocstat with functions to retrieve process command line
  arguments and environment variables.
  
  Suggested by: stas
  Reviewed by:  jhb and stas (initial version)
  MFC after:    1 month

Modified:
  head/lib/libprocstat/Symbol.map
  head/lib/libprocstat/core.c
  head/lib/libprocstat/core.h
  head/lib/libprocstat/libprocstat.3
  head/lib/libprocstat/libprocstat.c
  head/lib/libprocstat/libprocstat.h
  head/lib/libprocstat/libprocstat_internal.h

Modified: head/lib/libprocstat/Symbol.map
==============================================================================
--- head/lib/libprocstat/Symbol.map     Sat Apr 20 08:05:04 2013        
(r249678)
+++ head/lib/libprocstat/Symbol.map     Sat Apr 20 08:07:04 2013        
(r249679)
@@ -16,9 +16,13 @@ FBSD_1.2 {
 };
 
 FBSD_1.3 {
+       procstat_freeargv;
+       procstat_freeenvv;
        procstat_freegroups;
        procstat_freevmmap;
        procstat_get_shm_info;
+       procstat_getargv;
+       procstat_getenvv;
        procstat_getgroups;
        procstat_getosrel;
        procstat_getpathname;

Modified: head/lib/libprocstat/core.c
==============================================================================
--- head/lib/libprocstat/core.c Sat Apr 20 08:05:04 2013        (r249678)
+++ head/lib/libprocstat/core.c Sat Apr 20 08:07:04 2013        (r249679)
@@ -28,6 +28,7 @@
 
 #include <sys/param.h>
 #include <sys/elf.h>
+#include <sys/exec.h>
 #include <sys/user.h>
 
 #include <assert.h>
@@ -56,6 +57,10 @@ struct procstat_core
 
 static bool    core_offset(struct procstat_core *core, off_t offset);
 static bool    core_read(struct procstat_core *core, void *buf, size_t len);
+static ssize_t core_read_mem(struct procstat_core *core, void *buf,
+    size_t len, vm_offset_t addr, bool readall);
+static void    *get_args(struct procstat_core *core, vm_offset_t psstrings,
+    enum psc_type type, void *buf, size_t *lenp);
 
 struct procstat_core *
 procstat_core_open(const char *filename)
@@ -146,6 +151,7 @@ procstat_core_get(struct procstat_core *
 {
        Elf_Note nhdr;
        off_t offset, eoffset;
+       vm_offset_t psstrings;
        void *freebuf;
        size_t len;
        u_int32_t n_type;
@@ -183,6 +189,12 @@ procstat_core_get(struct procstat_core *
                n_type = NT_PROCSTAT_OSREL;
                structsize = sizeof(int);
                break;
+       case PSC_TYPE_PSSTRINGS:
+       case PSC_TYPE_ARGV:
+       case PSC_TYPE_ENVV:
+               n_type = NT_PROCSTAT_PSSTRINGS;
+               structsize = sizeof(vm_offset_t);
+               break;
        default:
                warnx("unknown core stat type: %d", type);
                return (NULL);
@@ -238,6 +250,19 @@ procstat_core_get(struct procstat_core *
                        free(freebuf);
                        return (NULL);
                }
+               if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) {
+                       if (len < sizeof(psstrings)) {
+                               free(freebuf);
+                               return (NULL);
+                       }
+                       psstrings = *(vm_offset_t *)buf;
+                       if (freebuf == NULL)
+                               len = *lenp;
+                       else
+                               buf = NULL;
+                       free(freebuf);
+                       buf = get_args(core, psstrings, type, buf, &len);
+               }
                *lenp = len;
                return (buf);
         }
@@ -276,3 +301,128 @@ core_read(struct procstat_core *core, vo
        }
        return (true);
 }
+
+static ssize_t
+core_read_mem(struct procstat_core *core, void *buf, size_t len,
+    vm_offset_t addr, bool readall)
+{
+       GElf_Phdr phdr;
+       off_t offset;
+       int i;
+
+       assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
+
+       for (i = 0; i < core->pc_ehdr.e_phnum; i++) {
+               if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) {
+                       warnx("gelf_getphdr: %s", elf_errmsg(-1));
+                       return (-1);
+               }
+               if (phdr.p_type != PT_LOAD)
+                       continue;
+               if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz)
+                       continue;
+               offset = phdr.p_offset + (addr - phdr.p_vaddr);
+               if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) {
+                       if (readall) {
+                               warnx("format error: "
+                                   "attempt to read out of segment");
+                               return (-1);
+                       }
+                       len = (phdr.p_vaddr + phdr.p_memsz) - addr;
+               }
+               if (!core_offset(core, offset))
+                       return (-1);
+               if (!core_read(core, buf, len))
+                       return (-1);
+               return (len);
+       }
+       warnx("format error: address %ju not found", (uintmax_t)addr);
+       return (-1);
+}
+
+#define ARGS_CHUNK_SZ  256     /* Chunk size (bytes) for get_args operations. 
*/
+
+static void *
+get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type,
+     void *args, size_t *lenp)
+{
+       struct ps_strings pss;
+       void *freeargs;
+       vm_offset_t addr;
+       char **argv, *p;
+       size_t chunksz, done, len, nchr, size;
+       ssize_t n;
+       u_int i, nstr;
+
+       assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV);
+
+       if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1)
+               return (NULL);
+       if (type == PSC_TYPE_ARGV) {
+               addr = (vm_offset_t)pss.ps_argvstr;
+               nstr = pss.ps_nargvstr;
+       } else /* type == PSC_TYPE_ENVV */ {
+               addr = (vm_offset_t)pss.ps_envstr;
+               nstr = pss.ps_nenvstr;
+       }
+       if (addr == 0 || nstr == 0)
+               return (NULL);
+       if (nstr > ARG_MAX) {
+               warnx("format error");
+               return (NULL);
+       }
+       size = nstr * sizeof(char *);
+       argv = malloc(size);
+       if (argv == NULL) {
+               warn("malloc(%zu)", size);
+               return (NULL);
+       }
+       done = 0;
+       freeargs = NULL;
+       if (core_read_mem(core, argv, size, addr, true) == -1)
+               goto fail;
+       if (args != NULL) {
+               nchr = MIN(ARG_MAX, *lenp);
+       } else {
+               nchr = ARG_MAX;
+               freeargs = args = malloc(nchr);
+               if (args == NULL) {
+                       warn("malloc(%zu)", nchr);
+                       goto fail;
+               }
+       }
+       p = args;
+       for (i = 0; ; i++) {
+               if (i == nstr)
+                       goto done;
+               /*
+                * The program may have scribbled into its argv array, e.g. to
+                * remove some arguments.  If that has happened, break out
+                * before trying to read from NULL.
+                */
+               if (argv[i] == NULL)
+                       goto done;
+               for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) {
+                       chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done);
+                       if (chunksz <= 0)
+                               goto done;
+                       n = core_read_mem(core, p, chunksz, addr, false);
+                       if (n == -1)
+                               goto fail;
+                       len = strnlen(p, chunksz);
+                       p += len;
+                       done += len;
+                       if (len != chunksz)
+                               break;
+               }
+               *p++ = '\0';
+               done++;
+       }
+fail:
+       free(freeargs);
+       args = NULL;
+done:
+       *lenp = done;
+       free(argv);
+       return (args);
+}

Modified: head/lib/libprocstat/core.h
==============================================================================
--- head/lib/libprocstat/core.h Sat Apr 20 08:05:04 2013        (r249678)
+++ head/lib/libprocstat/core.h Sat Apr 20 08:07:04 2013        (r249679)
@@ -37,6 +37,9 @@ enum psc_type {
        PSC_TYPE_UMASK,
        PSC_TYPE_RLIMIT,
        PSC_TYPE_OSREL,
+       PSC_TYPE_PSSTRINGS,
+       PSC_TYPE_ARGV,
+       PSC_TYPE_ENVV,
 };
 
 struct procstat_core;

Modified: head/lib/libprocstat/libprocstat.3
==============================================================================
--- head/lib/libprocstat/libprocstat.3  Sat Apr 20 08:05:04 2013        
(r249678)
+++ head/lib/libprocstat/libprocstat.3  Sat Apr 20 08:07:04 2013        
(r249679)
@@ -32,6 +32,8 @@
 .Nm procstat_open_kvm ,
 .Nm procstat_open_sysctl ,
 .Nm procstat_close ,
+.Nm procstat_getargv ,
+.Nm procstat_getenvv ,
 .Nm procstat_getfiles ,
 .Nm procstat_getgroups ,
 .Nm procstat_getosrel ,
@@ -39,6 +41,8 @@
 .Nm procstat_getprocs ,
 .Nm procstat_getumask ,
 .Nm procstat_getvmmap ,
+.Nm procstat_freeargv ,
+.Nm procstat_freeenvv ,
 .Nm procstat_freefiles ,
 .Nm procstat_freegroups ,
 .Nm procstat_freeprocs ,
@@ -59,6 +63,14 @@
 .Fn procstat_close "struct procstat *procstat"
 .Fc
 .Ft void
+.Fo procstat_freeargv
+.Fa "struct procstat *procstat"
+.Fc
+.Ft void
+.Fo procstat_freeenvv
+.Fa "struct procstat *procstat"
+.Fc
+.Ft void
 .Fo procstat_freefiles
 .Fa "struct procstat *procstat"
 .Fa "struct filestat_list *head"
@@ -110,6 +122,20 @@
 .Fa "struct vnstat *vn"
 .Fa "char *errbuf"
 .Fc
+.Ft "char **"
+.Fo procstat_getargv
+.Fa "struct procstat *procstat"
+.Fa "const struct kinfo_proc *kp"
+.Fa "size_t nchr"
+.Fa "char *errbuf"
+.Fc
+.Ft "char **"
+.Fo procstat_getenvv
+.Fa "struct procstat *procstat"
+.Fa "const struct kinfo_proc *kp"
+.Fa "size_t nchr"
+.Fa "char *errbuf"
+.Fc
 .Ft "struct filestat_list *"
 .Fo procstat_getfiles
 .Fa "struct procstat *procstat"
@@ -251,6 +277,50 @@ The caller is responsible to free the al
 function call.
 .Pp
 The
+.Fn procstat_getargv
+function gets a pointer to the
+.Vt procstat
+structure from one of the
+.Fn procstat_open_*
+functions, a pointer to
+.Vt kinfo_proc
+structure from the array obtained from the
+.Fn kvm_getprocs
+function, and returns a null-terminated argument vector that corresponds to
+the command line arguments passed to the process.
+The
+.Fa nchr
+argument indicates the maximum number of characters, including null bytes,
+to use in building the strings.
+If this amount is exceeded, the string causing the overflow is truncated and
+the partial result is returned.
+This is handy for programs that print only a one line summary of a
+command and should not copy out large amounts of text only to ignore it.
+If
+.Fa nchr
+is zero, no limit is imposed and all argument strings are returned.
+The values of the returned argument vector refer the strings stored
+in the
+.Vt procstat
+internal buffer.
+A subsequent call of the function with the same
+.Vt procstat
+argument will reuse the buffer.
+To free the allocated memory
+.Fn procstat_freeargv
+function call can be used, or it will be released on
+.Fn procstat_close .
+.Pp
+The
+.Fn procstat_getenvv
+function is similar to
+.Fn procstat_getargv
+but returns the vector of environment strings.
+The caller may free the allocated memory with a subsequent
+.Fn procstat_freeenv
+function call.
+.Pp
+The
 .Fn procstat_getfiles
 function gets a pointer to the
 .Vt procstat

Modified: head/lib/libprocstat/libprocstat.c
==============================================================================
--- head/lib/libprocstat/libprocstat.c  Sat Apr 20 08:05:04 2013        
(r249678)
+++ head/lib/libprocstat/libprocstat.c  Sat Apr 20 08:07:04 2013        
(r249679)
@@ -105,6 +105,8 @@ int     statfs(const char *, struct stat
 #define        PROCSTAT_SYSCTL 2
 #define        PROCSTAT_CORE   3
 
+static char    **getargv(struct procstat *procstat, struct kinfo_proc *kp,
+    size_t nchr, int env);
 static char    *getmnton(kvm_t *kd, struct mount *m);
 static struct kinfo_vmentry *  kinfo_getvmmap_core(struct procstat_core *core,
     int *cntp);
@@ -158,6 +160,8 @@ procstat_close(struct procstat *procstat
                kvm_close(procstat->kd);
        else if (procstat->type == PROCSTAT_CORE)
                procstat_core_close(procstat->core);
+       procstat_freeargv(procstat);
+       procstat_freeenvv(procstat);
        free(procstat);
 }
 
@@ -1524,6 +1528,180 @@ getmnton(kvm_t *kd, struct mount *m)
        return (mt->mntonname);
 }
 
+/*
+ * Auxiliary structures and functions to get process environment or
+ * command line arguments.
+ */
+struct argvec {
+       char    *buf;
+       size_t  bufsize;
+       char    **argv;
+       size_t  argc;
+};
+
+static struct argvec *
+argvec_alloc(size_t bufsize)
+{
+       struct argvec *av;
+
+       av = malloc(sizeof(*av));
+       if (av == NULL)
+               return (NULL);
+       av->bufsize = bufsize;
+       av->buf = malloc(av->bufsize);
+       if (av->buf == NULL) {
+               free(av);
+               return (NULL);
+       }
+       av->argc = 32;
+       av->argv = malloc(sizeof(char *) * av->argc);
+       if (av->argv == NULL) {
+               free(av->buf);
+               free(av);
+               return (NULL);
+       }
+       return av;
+}
+
+static void
+argvec_free(struct argvec * av)
+{
+
+       free(av->argv);
+       free(av->buf);
+       free(av);
+}
+
+static char **
+getargv(struct procstat *procstat, struct kinfo_proc *kp, size_t nchr, int env)
+{
+       int error, name[4], argc, i;
+       struct argvec *av, **avp;
+       enum psc_type type;
+       size_t len;
+       char *p, **argv;
+
+       assert(procstat);
+       assert(kp);
+       if (procstat->type == PROCSTAT_KVM) {
+               warnx("can't use kvm access method");
+               return (NULL);
+       }
+       if (procstat->type != PROCSTAT_SYSCTL &&
+           procstat->type != PROCSTAT_CORE) {
+               warnx("unknown access method: %d", procstat->type);
+               return (NULL);
+       }
+
+       if (nchr == 0 || nchr > ARG_MAX)
+               nchr = ARG_MAX;
+
+       avp = (struct argvec **)(env ? &procstat->argv : &procstat->envv);
+       av = *avp;
+
+       if (av == NULL)
+       {
+               av = argvec_alloc(nchr);
+               if (av == NULL)
+               {
+                       warn("malloc(%zu)", nchr);
+                       return (NULL);
+               }
+               *avp = av;
+       } else if (av->bufsize < nchr) {
+               av->buf = reallocf(av->buf, nchr);
+               if (av->buf == NULL) {
+                       warn("malloc(%zu)", nchr);
+                       return (NULL);
+               }
+       }
+       if (procstat->type == PROCSTAT_SYSCTL) {
+               name[0] = CTL_KERN;
+               name[1] = KERN_PROC;
+               name[2] = env ? KERN_PROC_ENV : KERN_PROC_ARGS;
+               name[3] = kp->ki_pid;
+               len = nchr;
+               error = sysctl(name, 4, av->buf, &len, NULL, 0);
+               if (error != 0 && errno != ESRCH && errno != EPERM)
+                       warn("sysctl(kern.proc.%s)", env ? "env" : "args");
+               if (error != 0 || len == 0)
+                       return (NULL);
+       } else /* procstat->type == PROCSTAT_CORE */ {
+               type = env ? PSC_TYPE_ENVV : PSC_TYPE_ARGV;
+               len = nchr;
+               if (procstat_core_get(procstat->core, type, av->buf, &len)
+                   == NULL) {
+                       return (NULL);
+               }
+       }
+
+       argv = av->argv;
+       argc = av->argc;
+       i = 0;
+       for (p = av->buf; p < av->buf + len; p += strlen(p) + 1) {
+               argv[i++] = p;
+               if (i < argc)
+                       continue;
+               /* Grow argv. */
+               argc += argc;
+               argv = realloc(argv, sizeof(char *) * argc);
+               if (argv == NULL) {
+                       warn("malloc(%zu)", sizeof(char *) * argc);
+                       return (NULL);
+               }
+               av->argv = argv;
+               av->argc = argc;
+       }
+       argv[i] = NULL;
+
+       return (argv);
+}
+
+/*
+ * Return process command line arguments.
+ */
+char **
+procstat_getargv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr)
+{
+
+       return (getargv(procstat, p, nchr, 0));
+}
+
+/*
+ * Free the buffer allocated by procstat_getargv().
+ */
+void
+procstat_freeargv(struct procstat *procstat)
+{
+
+       if (procstat->argv != NULL) {
+               argvec_free(procstat->argv);
+               procstat->argv = NULL;
+       }
+}
+
+/*
+ * Return process environment.
+ */
+char **
+procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr)
+{
+
+       return (getargv(procstat, p, nchr, 1));
+}
+
+/*
+ * Free the buffer allocated by procstat_getenvv().
+ */
+void
+procstat_freeenvv(struct procstat *procstat)
+{
+       if (procstat->envv != NULL) {
+               argvec_free(procstat->envv);
+               procstat->envv = NULL;
+       }
+}
+
 static struct kinfo_vmentry *
 kinfo_getvmmap_core(struct procstat_core *core, int *cntp)
 {

Modified: head/lib/libprocstat/libprocstat.h
==============================================================================
--- head/lib/libprocstat/libprocstat.h  Sat Apr 20 08:05:04 2013        
(r249678)
+++ head/lib/libprocstat/libprocstat.h  Sat Apr 20 08:07:04 2013        
(r249679)
@@ -147,6 +147,8 @@ STAILQ_HEAD(filestat_list, filestat);
 
 __BEGIN_DECLS
 void   procstat_close(struct procstat *procstat);
+void   procstat_freeargv(struct procstat *procstat);
+void   procstat_freeenvv(struct procstat *procstat);
 void   procstat_freegroups(struct procstat *procstat, gid_t *groups);
 void   procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p);
 void   procstat_freefiles(struct procstat *procstat,
@@ -167,6 +169,10 @@ int        procstat_get_socket_info(struct proc
     struct sockstat *sock, char *errbuf);
 int    procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst,
     struct vnstat *vn, char *errbuf);
+char   **procstat_getargv(struct procstat *procstat, struct kinfo_proc *p,
+    size_t nchr);
+char   **procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p,
+    size_t nchr);
 gid_t  *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp,
     unsigned int *count);
 int    procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp,

Modified: head/lib/libprocstat/libprocstat_internal.h
==============================================================================
--- head/lib/libprocstat/libprocstat_internal.h Sat Apr 20 08:05:04 2013        
(r249678)
+++ head/lib/libprocstat/libprocstat_internal.h Sat Apr 20 08:07:04 2013        
(r249679)
@@ -34,6 +34,8 @@ struct procstat {
        kvm_t   *kd;
        void    *vmentries;
        void    *files;
+       void    *argv;
+       void    *envv;
        struct procstat_core *core;
 };
 
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to