xref: /titanic_50/usr/src/lib/libproc/common/Pexecname.c (revision 9acbbeaf2a1ffe5c14b244867d427714fab43c5c)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
568dd05bfSrab  * Common Development and Distribution License (the "License").
668dd05bfSrab  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2268dd05bfSrab  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #define	__EXTENSIONS__
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #undef	__EXTENSIONS__
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <libgen.h>
337c478bd9Sstevel@tonic-gate #include <limits.h>
347c478bd9Sstevel@tonic-gate #include <stdio.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <unistd.h>
37*9acbbeafSnn35248 #include <libzonecfg.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #include "Pcontrol.h"
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /*
427c478bd9Sstevel@tonic-gate  * Pexecname.c - Way too much code to attempt to derive the full pathname of
437c478bd9Sstevel@tonic-gate  * the executable file from a process handle, be it dead or alive.
447c478bd9Sstevel@tonic-gate  */
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate /*
477c478bd9Sstevel@tonic-gate  * Once we've computed a cwd and a relative path, we use try_exec() to
487c478bd9Sstevel@tonic-gate  * form an absolute path, call resolvepath() on it, and then let the
497c478bd9Sstevel@tonic-gate  * caller's function do the final confirmation.
507c478bd9Sstevel@tonic-gate  */
517c478bd9Sstevel@tonic-gate static int
527c478bd9Sstevel@tonic-gate try_exec(const char *cwd, const char *path, char *buf,
537c478bd9Sstevel@tonic-gate     int (*isexec)(const char *, void *), void *isdata)
547c478bd9Sstevel@tonic-gate {
557c478bd9Sstevel@tonic-gate 	int i;
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate 	if (path[0] != '/')
587c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path);
597c478bd9Sstevel@tonic-gate 	else
607c478bd9Sstevel@tonic-gate 		(void) strcpy(buf, path);
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 	dprintf("try_exec \"%s\"\n", buf);
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	if ((i = resolvepath(buf, buf, PATH_MAX)) > 0) {
657c478bd9Sstevel@tonic-gate 		buf[i] = '\0';
667c478bd9Sstevel@tonic-gate 		return (isexec(buf, isdata));
677c478bd9Sstevel@tonic-gate 	}
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate 	return (0); /* resolvepath failed */
707c478bd9Sstevel@tonic-gate }
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate /*
737c478bd9Sstevel@tonic-gate  * The Pfindexec function contains the logic for the executable name dance.
747c478bd9Sstevel@tonic-gate  * The caller provides a possible executable name or likely directory (the
757c478bd9Sstevel@tonic-gate  * aout parameter), and a function which is responsible for doing any
767c478bd9Sstevel@tonic-gate  * final confirmation on the executable pathname once a possible full
777c478bd9Sstevel@tonic-gate  * pathname has been chosen.
787c478bd9Sstevel@tonic-gate  */
797c478bd9Sstevel@tonic-gate char *
807c478bd9Sstevel@tonic-gate Pfindexec(struct ps_prochandle *P, const char *aout,
817c478bd9Sstevel@tonic-gate     int (*isexec)(const char *, void *), void *isdata)
827c478bd9Sstevel@tonic-gate {
837c478bd9Sstevel@tonic-gate 	char cwd[PATH_MAX * 2];
847c478bd9Sstevel@tonic-gate 	char path[PATH_MAX];
857c478bd9Sstevel@tonic-gate 	char buf[PATH_MAX];
867c478bd9Sstevel@tonic-gate 	struct stat st;
877c478bd9Sstevel@tonic-gate 	uintptr_t addr;
88*9acbbeafSnn35248 	char *p = path, *q;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	if (P->execname)
917c478bd9Sstevel@tonic-gate 		return (P->execname); /* Already found */
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	errno = 0; /* Set to zero so we can tell if stat() failed */
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	/*
967c478bd9Sstevel@tonic-gate 	 * First try: use the provided default value, if it is not a directory.
977c478bd9Sstevel@tonic-gate 	 * If the aout parameter turns out to be a directory, this is
987c478bd9Sstevel@tonic-gate 	 * interpreted as the directory to use as an alternate cwd for
997c478bd9Sstevel@tonic-gate 	 * our subsequent attempts to locate the executable.
1007c478bd9Sstevel@tonic-gate 	 */
1017c478bd9Sstevel@tonic-gate 	if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) {
1027c478bd9Sstevel@tonic-gate 		if (try_exec(".", aout, buf, isexec, isdata))
1037c478bd9Sstevel@tonic-gate 			goto found;
1047c478bd9Sstevel@tonic-gate 		else
1057c478bd9Sstevel@tonic-gate 			aout = ".";
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	} else if (aout == NULL || errno != 0)
1087c478bd9Sstevel@tonic-gate 		aout = ".";
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	/*
1117c478bd9Sstevel@tonic-gate 	 * At this point 'aout' is either "." or an alternate cwd.  We use
1127c478bd9Sstevel@tonic-gate 	 * realpath(3c) to turn this into a full pathname free of ".", "..",
1137c478bd9Sstevel@tonic-gate 	 * and symlinks.  If this fails for some reason, fall back to "."
1147c478bd9Sstevel@tonic-gate 	 */
1157c478bd9Sstevel@tonic-gate 	if (realpath(aout, cwd) == NULL)
1167c478bd9Sstevel@tonic-gate 		(void) strcpy(cwd, ".");
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	/*
1197c478bd9Sstevel@tonic-gate 	 * Second try: read the string pointed to by the AT_SUN_EXECNAME
1207c478bd9Sstevel@tonic-gate 	 * auxv element, saved when the program was exec'd.  If the full
1217c478bd9Sstevel@tonic-gate 	 * pathname try_exec() forms fails, try again using just the
122*9acbbeafSnn35248 	 * basename appended to our cwd.  If that also fails, and the process
123*9acbbeafSnn35248 	 * is in a zone, try again with the zone path instead of our cwd.
1247c478bd9Sstevel@tonic-gate 	 */
1257c478bd9Sstevel@tonic-gate 	if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L &&
1267c478bd9Sstevel@tonic-gate 	    Pread_string(P, path, sizeof (path), (off_t)addr) > 0) {
127*9acbbeafSnn35248 		char		zname[ZONENAME_MAX];
128*9acbbeafSnn35248 		char		zpath[PATH_MAX];
129*9acbbeafSnn35248 		const psinfo_t	*pi = Ppsinfo(P);
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 		if (try_exec(cwd, path, buf, isexec, isdata))
1327c478bd9Sstevel@tonic-gate 			goto found;
1337c478bd9Sstevel@tonic-gate 
13468dd05bfSrab 		if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
13568dd05bfSrab 		    try_exec(cwd, p, buf, isexec, isdata))
1367c478bd9Sstevel@tonic-gate 			goto found;
137*9acbbeafSnn35248 
138*9acbbeafSnn35248 		if (getzonenamebyid(pi->pr_zoneid, zname,
139*9acbbeafSnn35248 		    sizeof (zname)) != -1 && strcmp(zname, "global") != 0 &&
140*9acbbeafSnn35248 		    zone_get_zonepath(zname, zpath, sizeof (zpath)) == Z_OK) {
141*9acbbeafSnn35248 			(void) strcat(zpath, "/root");
142*9acbbeafSnn35248 			if (try_exec(zpath, p, buf, isexec, isdata))
143*9acbbeafSnn35248 				goto found;
144*9acbbeafSnn35248 		}
1457c478bd9Sstevel@tonic-gate 	}
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	/*
1487c478bd9Sstevel@tonic-gate 	 * Third try: try using the first whitespace-separated token
1497c478bd9Sstevel@tonic-gate 	 * saved in the psinfo_t's pr_psargs (the initial value of argv[0]).
1507c478bd9Sstevel@tonic-gate 	 */
1517c478bd9Sstevel@tonic-gate 	if (Ppsinfo(P) != NULL) {
1527c478bd9Sstevel@tonic-gate 		(void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ);
1537c478bd9Sstevel@tonic-gate 		path[PRARGSZ] = '\0';
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 		if ((p = strchr(path, ' ')) != NULL)
1567c478bd9Sstevel@tonic-gate 			*p = '\0';
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 		if (try_exec(cwd, path, buf, isexec, isdata))
1597c478bd9Sstevel@tonic-gate 			goto found;
1607c478bd9Sstevel@tonic-gate 
16168dd05bfSrab 		if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
16268dd05bfSrab 		    try_exec(cwd, p, buf, isexec, isdata))
1637c478bd9Sstevel@tonic-gate 			goto found;
1647c478bd9Sstevel@tonic-gate 	}
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	/*
1677c478bd9Sstevel@tonic-gate 	 * Fourth try: read the string pointed to by argv[0] out of the
1687c478bd9Sstevel@tonic-gate 	 * stack in the process's address space.
1697c478bd9Sstevel@tonic-gate 	 */
1707c478bd9Sstevel@tonic-gate 	if (P->psinfo.pr_argv != NULL &&
1717c478bd9Sstevel@tonic-gate 	    Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 &&
1727c478bd9Sstevel@tonic-gate 	    Pread_string(P, path, sizeof (path), (off_t)addr) > 0) {
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 		if (try_exec(cwd, path, buf, isexec, isdata))
1757c478bd9Sstevel@tonic-gate 			goto found;
1767c478bd9Sstevel@tonic-gate 
17768dd05bfSrab 		if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
17868dd05bfSrab 		    try_exec(cwd, p, buf, isexec, isdata))
1797c478bd9Sstevel@tonic-gate 			goto found;
1807c478bd9Sstevel@tonic-gate 	}
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	/*
1837c478bd9Sstevel@tonic-gate 	 * Fifth try: read the process's $PATH environment variable and
1847c478bd9Sstevel@tonic-gate 	 * search each directory named there for the name matching pr_fname.
1857c478bd9Sstevel@tonic-gate 	 */
1867c478bd9Sstevel@tonic-gate 	if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) {
1877c478bd9Sstevel@tonic-gate 		/*
1887c478bd9Sstevel@tonic-gate 		 * If the name from pr_psargs contains pr_fname as its
1897c478bd9Sstevel@tonic-gate 		 * leading string, then accept the name from pr_psargs
1907c478bd9Sstevel@tonic-gate 		 * because more bytes are saved there.  Otherwise use
1917c478bd9Sstevel@tonic-gate 		 * pr_fname because this gives us new information.
1927c478bd9Sstevel@tonic-gate 		 */
1937c478bd9Sstevel@tonic-gate 		(void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ);
1947c478bd9Sstevel@tonic-gate 		path[PRARGSZ] = '\0';
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 		if ((p = strchr(path, ' ')) != NULL)
1977c478bd9Sstevel@tonic-gate 			*p = '\0';
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 		if (strchr(path, '/') != NULL || strncmp(path,
2007c478bd9Sstevel@tonic-gate 		    P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0)
2017c478bd9Sstevel@tonic-gate 			(void) strcpy(path, P->psinfo.pr_fname);
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 		/*
2047c478bd9Sstevel@tonic-gate 		 * Now iterate over the $PATH elements, trying to form
2057c478bd9Sstevel@tonic-gate 		 * an executable pathname with each one.
2067c478bd9Sstevel@tonic-gate 		 */
2077c478bd9Sstevel@tonic-gate 		for (p = strtok_r(cwd, ":", &q); p != NULL;
2087c478bd9Sstevel@tonic-gate 		    p = strtok_r(NULL, ":", &q)) {
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 			if (*p != '/')
2117c478bd9Sstevel@tonic-gate 				continue; /* Ignore anything relative */
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 			if (try_exec(p, path, buf, isexec, isdata))
2147c478bd9Sstevel@tonic-gate 				goto found;
2157c478bd9Sstevel@tonic-gate 		}
2167c478bd9Sstevel@tonic-gate 	}
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	errno = ENOENT;
2197c478bd9Sstevel@tonic-gate 	return (NULL);
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate found:
2227c478bd9Sstevel@tonic-gate 	if ((P->execname = strdup(buf)) == NULL)
2237c478bd9Sstevel@tonic-gate 		dprintf("failed to malloc; executable name is \"%s\"", buf);
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	return (P->execname);
2267c478bd9Sstevel@tonic-gate }
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate /*
2297c478bd9Sstevel@tonic-gate  * Callback function for Pfindexec().  We return a match if we can stat the
2307c478bd9Sstevel@tonic-gate  * suggested pathname and confirm its device and inode number match our
2317c478bd9Sstevel@tonic-gate  * previous information about the /proc/<pid>/object/a.out file.
2327c478bd9Sstevel@tonic-gate  */
2337c478bd9Sstevel@tonic-gate static int
2347c478bd9Sstevel@tonic-gate stat_exec(const char *path, struct stat64 *stp)
2357c478bd9Sstevel@tonic-gate {
2367c478bd9Sstevel@tonic-gate 	struct stat64 st;
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) &&
2397c478bd9Sstevel@tonic-gate 	    stp->st_dev == st.st_dev && stp->st_ino == st.st_ino);
2407c478bd9Sstevel@tonic-gate }
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate /*
2437c478bd9Sstevel@tonic-gate  * Return the full pathname for the executable file.  If the process handle is
2447c478bd9Sstevel@tonic-gate  * a core file, we've already tried our best to get the executable name.
2457c478bd9Sstevel@tonic-gate  * Otherwise, we make an attempt using Pfindexec().
2467c478bd9Sstevel@tonic-gate  */
2477c478bd9Sstevel@tonic-gate char *
2487c478bd9Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen)
2497c478bd9Sstevel@tonic-gate {
2507c478bd9Sstevel@tonic-gate 	if (P->execname == NULL && P->state != PS_DEAD && P->state != PS_IDLE) {
2517c478bd9Sstevel@tonic-gate 		char exec_name[PATH_MAX];
2527c478bd9Sstevel@tonic-gate 		char cwd[PATH_MAX];
2537c478bd9Sstevel@tonic-gate 		char proc_cwd[64];
2547c478bd9Sstevel@tonic-gate 		struct stat64 st;
2557c478bd9Sstevel@tonic-gate 		int ret;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 		/*
2587c478bd9Sstevel@tonic-gate 		 * Try to get the path information first.
2597c478bd9Sstevel@tonic-gate 		 */
2607c478bd9Sstevel@tonic-gate 		(void) snprintf(exec_name, sizeof (exec_name),
261*9acbbeafSnn35248 		    "%s/%d/path/a.out", procfs_path, (int)P->pid);
2627c478bd9Sstevel@tonic-gate 		if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) {
2637c478bd9Sstevel@tonic-gate 			buf[ret] = '\0';
2647c478bd9Sstevel@tonic-gate 			return (buf);
2657c478bd9Sstevel@tonic-gate 		}
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 		/*
2687c478bd9Sstevel@tonic-gate 		 * Stat the executable file so we can compare Pfindexec's
2697c478bd9Sstevel@tonic-gate 		 * suggestions to the actual device and inode number.
2707c478bd9Sstevel@tonic-gate 		 */
2717c478bd9Sstevel@tonic-gate 		(void) snprintf(exec_name, sizeof (exec_name),
272*9acbbeafSnn35248 		    "%s/%d/object/a.out", procfs_path, (int)P->pid);
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 		if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode))
2757c478bd9Sstevel@tonic-gate 			return (NULL);
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 		/*
2787c478bd9Sstevel@tonic-gate 		 * Attempt to figure out the current working directory of the
2797c478bd9Sstevel@tonic-gate 		 * target process.  This only works if the target process has
2807c478bd9Sstevel@tonic-gate 		 * not changed its current directory since it was exec'd.
2817c478bd9Sstevel@tonic-gate 		 */
2827c478bd9Sstevel@tonic-gate 		(void) snprintf(proc_cwd, sizeof (proc_cwd),
283*9acbbeafSnn35248 		    "%s/%d/path/cwd", procfs_path, (int)P->pid);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 		if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0)
2867c478bd9Sstevel@tonic-gate 			cwd[ret] = '\0';
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 		(void) Pfindexec(P, ret > 0 ? cwd : NULL,
2897c478bd9Sstevel@tonic-gate 		    (int (*)(const char *, void *))stat_exec, &st);
2907c478bd9Sstevel@tonic-gate 	}
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	if (P->execname != NULL) {
2937c478bd9Sstevel@tonic-gate 		(void) strncpy(buf, P->execname, buflen);
2947c478bd9Sstevel@tonic-gate 		return (buf);
2957c478bd9Sstevel@tonic-gate 	}
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	return (NULL);
2987c478bd9Sstevel@tonic-gate }
299