Subject: code to store the path of the executable in struct proc...
To: None <tech-kern@netbsd.org>
From: Christos Zoulas <christos@zoulas.com>
List: tech-kern
Date: 09/20/2007 17:01:03
In order to properly implement ld.elf_so $ORIGIN and the procfs
"exe" symlink we need to know the path to an executable. The
following code does this, but it is not particularly efficient and
its use of vnode_to_path() is questionable.

Comments?

Index: kern/exec_elf32.c
===================================================================
RCS file: /cvsroot/src/sys/kern/exec_elf32.c,v
retrieving revision 1.124
diff -u -u -r1.124 exec_elf32.c
--- kern/exec_elf32.c	24 Jun 2007 20:35:37 -0000	1.124
+++ kern/exec_elf32.c	20 Sep 2007 20:52:43 -0000
@@ -131,7 +131,7 @@
     struct ps_strings *arginfo, char **stackp, void *argp)
 {
 	size_t len;
-	AuxInfo ai[ELF_AUX_ENTRIES], *a;
+	AuxInfo ai[ELF_AUX_ENTRIES], *a, *execname;
 	struct elf_args *ap;
 	int error;
 
@@ -139,6 +139,7 @@
 		return error;
 
 	a = ai;
+	execname = NULL;
 
 	/*
 	 * Push extra arguments on the stack needed by dynamically
@@ -197,6 +198,12 @@
 		a->a_v = kauth_cred_getgid(l->l_cred);
 		a++;
 
+		if (l->l_proc->p_path) {
+			execname = a;
+			a->a_type = AT_SUN_EXECNAME;
+			a++;
+		}
+
 		free(ap, M_TEMP);
 		pack->ep_emul_arg = NULL;
 	}
@@ -210,6 +217,15 @@
 		return error;
 	*stackp += len;
 
+	if (execname) {
+		char *path = l->l_proc->p_path;
+		execname->a_v = (intptr_t)*stackp;
+		len = strlen(path) + 1;
+		if ((error = copyout(path, *stackp, len)) != 0)
+			return error;
+		*stackp += ALIGN(len);
+	}
+
 	return 0;
 }
 
Index: kern/init_main.c
===================================================================
RCS file: /cvsroot/src/sys/kern/init_main.c,v
retrieving revision 1.314
diff -u -u -r1.314 init_main.c
--- kern/init_main.c	7 Sep 2007 18:56:08 -0000	1.314
+++ kern/init_main.c	20 Sep 2007 20:52:43 -0000
@@ -718,6 +718,7 @@
 	 * Now in process 1.
 	 */
 	strncpy(p->p_comm, "init", MAXCOMLEN);
+	p->p_path = NULL;
 
 	/*
 	 * Wait for main() to tell us that it's safe to exec.
Index: kern/kern_exec.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_exec.c,v
retrieving revision 1.248
diff -u -u -r1.248 kern_exec.c
--- kern/kern_exec.c	20 Sep 2007 20:51:38 -0000	1.248
+++ kern/kern_exec.c	20 Sep 2007 20:52:43 -0000
@@ -708,6 +708,24 @@
 	arginfo.ps_nargvstr = argc;
 	arginfo.ps_nenvstr = envc;
 
+	/* set command name & other accounting info */
+	i = min(nid.ni_cnd.cn_namelen, MAXCOMLEN);
+	(void)memcpy(p->p_comm, nid.ni_cnd.cn_nameptr, i);
+	p->p_comm[i] = '\0';
+
+	if (p->p_path == NULL)
+		p->p_path = PNBUF_GET();
+
+	if ((error = vnode_to_path(p->p_path, MAXPATHLEN, p->p_textvp, l, p))
+	    != 0) {
+#ifdef DIAGNOSTIC
+		printf("Cannot get path for pid %d [%s] (error %d)",
+		    (int)p->p_pid, p->p_comm, error);
+#endif
+		PNBUF_PUT(p->p_path);
+		p->p_path = NULL;
+	}
+
 	stack = (char *)STACK_ALLOC(STACK_GROW(vm->vm_minsaddr,
 		STACK_PTHREADSPACE + sizeof(struct ps_strings) + szsigcode),
 		len - (sizeof(struct ps_strings) + szsigcode));
@@ -769,12 +787,8 @@
 
 	l->l_ctxlink = NULL;	/* reset ucontext link */
 
-	/* set command name & other accounting info */
-	i = min(nid.ni_cnd.cn_namelen, MAXCOMLEN);
-	memcpy(p->p_comm, nid.ni_cnd.cn_nameptr, i);
-	p->p_comm[i] = '\0';
-	p->p_acflag &= ~AFORK;
 
+	p->p_acflag &= ~AFORK;
 	p->p_flag |= PK_EXEC;
 
 	/*
Index: kern/kern_exit.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_exit.c,v
retrieving revision 1.187
diff -u -u -r1.187 kern_exit.c
--- kern/kern_exit.c	6 Sep 2007 23:58:56 -0000	1.187
+++ kern/kern_exit.c	20 Sep 2007 20:52:43 -0000
@@ -500,6 +500,11 @@
 	 */
 	p->p_stat = SDEAD;
 
+	/* Free the path */
+	if (p->p_path)
+		PNBUF_PUT(p->p_path);
+	p->p_path = NULL;
+
 	/* Put in front of parent's sibling list for parent to collect it */
 	q = p->p_pptr;
 	q->p_nstopchild++;
Index: kern/kern_fork.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_fork.c,v
retrieving revision 1.142
diff -u -u -r1.142 kern_fork.c
--- kern/kern_fork.c	15 Aug 2007 12:07:33 -0000	1.142
+++ kern/kern_fork.c	20 Sep 2007 20:52:43 -0000
@@ -307,6 +307,11 @@
 	p2->p_flag = p1->p_flag & (PK_SUGID | PK_NOCLDWAIT | PK_CLDSIGIGN);
 	p2->p_emul = p1->p_emul;
 	p2->p_execsw = p1->p_execsw;
+	if (p1->p_path) {
+		p2->p_path = PNBUF_GET();
+		(void)strcpy(p2->p_path, p1->p_path);
+	} else
+		p2->p_path = NULL;
 
 	if (flags & FORK_SYSTEM) {
 		/*
Index: kern/kern_proc.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_proc.c,v
retrieving revision 1.115
diff -u -u -r1.115 kern_proc.c
--- kern/kern_proc.c	6 Sep 2007 23:58:57 -0000	1.115
+++ kern/kern_proc.c	20 Sep 2007 20:52:43 -0000
@@ -357,6 +357,7 @@
 	(*p->p_emul->e_syscall_intern)(p);
 #endif
 	strlcpy(p->p_comm, "system", sizeof(p->p_comm));
+	p->p_path = NULL;
 
 	l->l_flag = LW_INMEM | LW_SYSTEM;
 	l->l_stat = LSONPROC;
Index: kern/vfs_getcwd.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_getcwd.c,v
retrieving revision 1.35
diff -u -u -r1.35 vfs_getcwd.c
--- kern/vfs_getcwd.c	9 Feb 2007 21:55:32 -0000	1.35
+++ kern/vfs_getcwd.c	20 Sep 2007 20:52:44 -0000
@@ -558,3 +558,54 @@
 	free(path, M_TEMP);
 	return error;
 }
+
+/*
+ * Try to find a pathname for a vnode. Since there is no mapping
+ * vnode -> parent directory, this needs the NAMECACHE_ENTER_REVERSE
+ * option to work (to make cache_revlookup succeed).
+ */
+int
+vnode_to_path(char *path, size_t len, struct vnode *vp, struct lwp *curl,
+    struct proc *p)
+{
+	struct proc *curp = curl->l_proc;
+	int error, lenused, elen;
+	char *bp, *bend;
+	struct vnode *dvp;
+
+	bp = bend = &path[len];
+	*(--bp) = '\0';
+
+	error = vget(vp, LK_EXCLUSIVE | LK_RETRY);
+	if (error != 0)
+		return error;
+	error = cache_revlookup(vp, &dvp, &bp, path);
+	vput(vp);
+	if (error != 0)
+		return (error == -1 ? ENOENT : error);
+
+	error = vget(dvp, 0);
+	if (error != 0)
+		return error;
+	*(--bp) = '/';
+	/* XXX GETCWD_CHECK_ACCESS == 0x0001 */
+	error = getcwd_common(dvp, NULL, &bp, path, len / 2, 1, curl);
+
+	/*
+	 * Strip off emulation path for emulated processes looking at
+	 * the maps file of a process of the same emulation. (Won't
+	 * work if /emul/xxx is a symlink..)
+	 */
+	if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) {
+		elen = strlen(curp->p_emul->e_path);
+		if (!strncmp(bp, curp->p_emul->e_path, elen))
+			bp = &bp[elen];
+	}
+
+	lenused = bend - bp;
+
+	memcpy(path, bp, lenused);
+	path[lenused] = 0;
+
+	return 0;
+}
Index: sys/exec_elf.h
===================================================================
RCS file: /cvsroot/src/sys/sys/exec_elf.h,v
retrieving revision 1.91
diff -u -u -r1.91 exec_elf.h
--- sys/exec_elf.h	19 Aug 2007 03:38:52 -0000	1.91
+++ sys/exec_elf.h	20 Sep 2007 20:52:44 -0000
@@ -806,7 +806,7 @@
 
 #ifdef _KERNEL
 
-#define ELF_AUX_ENTRIES	12		/* Size of aux array passed to loader */
+#define ELF_AUX_ENTRIES	14	/* Max size of aux array passed to loader */
 #define ELF32_NO_ADDR	(~(Elf32_Addr)0) /* Indicates addr. not yet filled in */
 #define ELF32_LINK_ADDR	((Elf32_Addr)-2) /* advises to use link address */
 #define ELF64_NO_ADDR	(~(Elf64_Addr)0) /* Indicates addr. not yet filled in */
Index: sys/filedesc.h
===================================================================
RCS file: /cvsroot/src/sys/sys/filedesc.h,v
retrieving revision 1.40
diff -u -u -r1.40 filedesc.h
--- sys/filedesc.h	9 Jul 2007 21:11:32 -0000	1.40
+++ sys/filedesc.h	20 Sep 2007 20:52:44 -0000
@@ -156,6 +156,8 @@
 #define GETCWD_CHECK_ACCESS 0x0001
 int	getcwd_common(struct vnode *, struct vnode *, char **, char *, int,
     int, struct lwp *);
+int	vnode_to_path(char *, size_t, struct vnode *, struct lwp *,
+    struct proc *);
 
 int	closef(struct file *, struct lwp *);
 int	getsock(struct filedesc *, int, struct file **);
Index: sys/proc.h
===================================================================
RCS file: /cvsroot/src/sys/sys/proc.h,v
retrieving revision 1.254
diff -u -u -r1.254 proc.h
--- sys/proc.h	7 Sep 2007 18:56:12 -0000	1.254
+++ sys/proc.h	20 Sep 2007 20:52:44 -0000
@@ -249,6 +249,7 @@
 	char		p_pad1[3];
 
 	pid_t		p_pid;		/* (: Process identifier. */
+	char		*p_path;	/* (: absolute of executable */
 	LIST_ENTRY(proc) p_pglist;	/* l: List of processes in pgrp. */
 	struct proc 	*p_pptr;	/* l: Pointer to parent process. */
 	LIST_ENTRY(proc) p_sibling;	/* l: List of sibling processes. */
Index: miscfs/procfs/procfs_map.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/procfs/procfs_map.c,v
retrieving revision 1.32
diff -u -u -r1.32 procfs_map.c
--- miscfs/procfs/procfs_map.c	21 Jul 2007 22:47:36 -0000	1.32
+++ miscfs/procfs/procfs_map.c	20 Sep 2007 20:52:44 -0000
@@ -93,9 +93,6 @@
 
 #define BUFFERSIZE (64 * 1024)
 
-static int procfs_vnode_to_path(struct vnode *vp, char *path, int len,
-				struct lwp *curl, struct proc *p);
-
 /*
  * The map entries can *almost* be read with programs like cat.  However,
  * large maps need special programs to read.  It is not easy to implement
@@ -167,8 +164,8 @@
 				if (error == 0 && vp != pfs->pfs_vnode) {
 					fileid = va.va_fileid;
 					dev = va.va_fsid;
-					error = procfs_vnode_to_path(vp, path,
-					    MAXPATHLEN * 4, curl, p);
+					error = vnode_to_path(path,
+					    MAXPATHLEN * 4, vp, curl, p);
 				}
 			}
 			pos += snprintf(buffer + pos, BUFFERSIZE - pos,
@@ -219,53 +216,3 @@
 {
 	return ((l->l_flag & LW_SYSTEM) == 0);
 }
-
-/*
- * Try to find a pathname for a vnode. Since there is no mapping
- * vnode -> parent directory, this needs the NAMECACHE_ENTER_REVERSE
- * option to work (to make cache_revlookup succeed).
- */
-static int procfs_vnode_to_path(struct vnode *vp, char *path, int len,
-				struct lwp *curl, struct proc *p)
-{
-	struct proc *curp = curl->l_proc;
-	int error, lenused, elen;
-	char *bp, *bend;
-	struct vnode *dvp;
-
-	bp = bend = &path[len];
-	*(--bp) = '\0';
-
-	error = vget(vp, LK_EXCLUSIVE | LK_RETRY);
-	if (error != 0)
-		return error;
-	error = cache_revlookup(vp, &dvp, &bp, path);
-	vput(vp);
-	if (error != 0)
-		return (error == -1 ? ENOENT : error);
-
-	error = vget(dvp, 0);
-	if (error != 0)
-		return error;
-	*(--bp) = '/';
-	/* XXX GETCWD_CHECK_ACCESS == 0x0001 */
-	error = getcwd_common(dvp, NULL, &bp, path, len / 2, 1, curl);
-
-	/*
-	 * Strip off emulation path for emulated processes looking at
-	 * the maps file of a process of the same emulation. (Won't
-	 * work if /emul/xxx is a symlink..)
-	 */
-	if (curp->p_emul == p->p_emul && curp->p_emul->e_path != NULL) {
-		elen = strlen(curp->p_emul->e_path);
-		if (!strncmp(bp, curp->p_emul->e_path, elen))
-			bp = &bp[elen];
-	}
-
-	lenused = bend - bp;
-
-	memcpy(path, bp, lenused);
-	path[lenused] = 0;
-
-	return 0;
-}
Index: miscfs/procfs/procfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/procfs/procfs_vnops.c,v
retrieving revision 1.158
diff -u -u -r1.158 procfs_vnops.c
--- miscfs/procfs/procfs_vnops.c	22 Jul 2007 13:37:13 -0000	1.158
+++ miscfs/procfs/procfs_vnops.c	20 Sep 2007 20:52:44 -0000
@@ -146,7 +146,7 @@
 static int procfs_validfile_linux(struct lwp *, struct mount *);
 static int procfs_root_readdir_callback(struct proc *, void *);
 static struct vnode *procfs_dir(pfstype, struct lwp *, struct proc *,
-				char **, char *, int);
+				char **, char *, size_t);
 
 /*
  * This is a list of the valid names in the
@@ -561,7 +561,7 @@
  */
 static struct vnode *
 procfs_dir(pfstype t, struct lwp *caller, struct proc *target,
-	   char **bpp, char *path, int len)
+	   char **bpp, char *path, size_t len)
 {
 	struct vnode *vp, *rvp = caller->l_proc->p_cwdi->cwdi_rdir;
 	char *bp;
@@ -579,6 +579,12 @@
 		break;
 	case PFSexe:
 		vp = target->p_textvp;
+		if (target->p_path) {
+			(void)strlcpy(path, target->p_path, len);
+			if (bpp)
+				*bpp = path;
+			return vp;
+		}
 		break;
 	default:
 		return (NULL);