diff --git a/sys/fs/procfs/procfs.c b/sys/fs/procfs/procfs.c --- a/sys/fs/procfs/procfs.c +++ b/sys/fs/procfs/procfs.c @@ -69,22 +69,17 @@ int procfs_doprocfile(PFS_FILL_ARGS) { - char *fullpath; - char *freepath; - struct vnode *textvp; + char *fullpath, *freepath, *binpath; int error; freepath = NULL; + binpath = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); PROC_LOCK(p); - textvp = p->p_textvp; - vhold(textvp); - PROC_UNLOCK(p); - error = vn_fullpath(textvp, &fullpath, &freepath); - vdrop(textvp); + error = proc_get_binpath(p, binpath, &fullpath, &freepath); if (error == 0) - sbuf_printf(sb, "%s", fullpath); - if (freepath != NULL) - free(freepath, M_TEMP); + sbuf_printf(sb, "%s", fullpath == NULL ? "" : fullpath); + free(binpath, M_TEMP); + free(freepath, M_TEMP); return (error); } diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -2226,41 +2226,34 @@ } /* - * This sysctl allows a process to retrieve the path of the executable for - * itself or another process. + * Look up the canonical executable path running in the specified process. + * It tries to return the same hardlink name as was used for execve(2). + * This allows the programs that modify its behavior based on the progname, + * to operate correctly. + * + * Result is returned in retbuf, it must not be freed, similar to vn_fullpath() + * calling conventions. + * binname is a pointer to temporary string buffer of length MAXPATHLEN, + * allocated and freed by caller. + * freebuf should be freed by caller, from the M_TEMP malloc type. */ -static int -sysctl_kern_proc_pathname(SYSCTL_HANDLER_ARGS) +int +proc_get_binpath(struct proc *p, char *binname, char **retbuf, + char **freebuf) { - pid_t *pidp = (pid_t *)arg1; - unsigned int arglen = arg2; - struct proc *p; - struct vnode *vp, *dvp; - char *retbuf, *freebuf, *binname; struct nameidata nd; + struct vnode *vp, *dvp; size_t freepath_size; int error; bool do_fullpath; - if (arglen != 1) - return (EINVAL); - binname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); - binname[0] = '\0'; - if (*pidp == -1) { /* -1 means this process */ - p = req->td->td_proc; - } else { - error = pget(*pidp, PGET_CANSEE, &p); - if (error != 0) { - free(binname, M_TEMP); - return (error); - } - } + PROC_LOCK_ASSERT(p, MA_OWNED); vp = p->p_textvp; if (vp == NULL) { - if (*pidp != -1) - PROC_UNLOCK(p); - free(binname, M_TEMP); + PROC_UNLOCK(p); + *retbuf = NULL; + *freebuf = NULL; return (0); } vref(vp); @@ -2269,20 +2262,20 @@ vref(dvp); if (p->p_binname != NULL) strlcpy(binname, p->p_binname, MAXPATHLEN); - if (*pidp != -1) - PROC_UNLOCK(p); + PROC_UNLOCK(p); + do_fullpath = true; - freebuf = NULL; + *freebuf = NULL; if (dvp != NULL && binname[0] != '\0') { freepath_size = MAXPATHLEN; if (vn_fullpath_hardlink(vp, dvp, binname, strlen(binname), - &retbuf, &freebuf, &freepath_size) == 0) { + retbuf, freebuf, &freepath_size) == 0) { /* * Recheck the looked up path. The binary * might have been renamed or replaced, in * which case we should not report old name. */ - NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, retbuf, + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, *retbuf, req->td); error = namei(&nd); if (error == 0) { @@ -2294,13 +2287,43 @@ } } if (do_fullpath) { - free(freebuf, M_TEMP); - freebuf = NULL; - error = vn_fullpath(vp, &retbuf, &freebuf); + free(*freebuf, M_TEMP); + *freebuf = NULL; + error = vn_fullpath(vp, retbuf, freebuf); } vrele(vp); if (dvp != NULL) vrele(dvp); + return (error); +} + +/* + * This sysctl allows a process to retrieve the path of the executable for + * itself or another process. + */ +static int +sysctl_kern_proc_pathname(SYSCTL_HANDLER_ARGS) +{ + pid_t *pidp = (pid_t *)arg1; + unsigned int arglen = arg2; + struct proc *p; + char *retbuf, *freebuf, *binname; + int error; + + if (arglen != 1) + return (EINVAL); + binname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); + binname[0] = '\0'; + if (*pidp == -1) { /* -1 means this process */ + error = 0; + p = req->td->td_proc; + PROC_LOCK(p); + } else { + error = pget(*pidp, PGET_CANSEE, &p); + } + + if (error == 0) + error = proc_get_binpath(p, binname, &retbuf, &freebuf); free(binname, M_TEMP); if (error != 0) return (error); diff --git a/sys/sys/proc.h b/sys/sys/proc.h --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1127,6 +1127,9 @@ struct pargs *pargs_alloc(int len); void pargs_drop(struct pargs *pa); void pargs_hold(struct pargs *pa); +void proc_add_orphan(struct proc *child, struct proc *parent); +int proc_get_binpath(struct proc *p, char *binname, char **fullpath, + char **freepath); int proc_getargv(struct thread *td, struct proc *p, struct sbuf *sb); int proc_getauxv(struct thread *td, struct proc *p, struct sbuf *sb); int proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb); @@ -1137,7 +1140,6 @@ struct proc *proc_realparent(struct proc *child); void proc_reap(struct thread *td, struct proc *p, int *status, int options); void proc_reparent(struct proc *child, struct proc *newparent, bool set_oppid); -void proc_add_orphan(struct proc *child, struct proc *parent); void proc_set_traced(struct proc *p, bool stop); void proc_wkilled(struct proc *p); struct pstats *pstats_alloc(void);