10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52232Srab * Common Development and Distribution License (the "License").
62232Srab * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*7675SEdward.Pilatowicz@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #define __EXTENSIONS__
270Sstevel@tonic-gate #include <string.h>
280Sstevel@tonic-gate #undef __EXTENSIONS__
290Sstevel@tonic-gate
300Sstevel@tonic-gate #include <libgen.h>
310Sstevel@tonic-gate #include <limits.h>
320Sstevel@tonic-gate #include <stdio.h>
330Sstevel@tonic-gate #include <errno.h>
340Sstevel@tonic-gate #include <unistd.h>
352823Srab #include <zone.h>
360Sstevel@tonic-gate
37*7675SEdward.Pilatowicz@Sun.COM #include "libproc.h"
380Sstevel@tonic-gate #include "Pcontrol.h"
390Sstevel@tonic-gate
400Sstevel@tonic-gate /*
410Sstevel@tonic-gate * Pexecname.c - Way too much code to attempt to derive the full pathname of
420Sstevel@tonic-gate * the executable file from a process handle, be it dead or alive.
430Sstevel@tonic-gate */
440Sstevel@tonic-gate
450Sstevel@tonic-gate /*
460Sstevel@tonic-gate * Once we've computed a cwd and a relative path, we use try_exec() to
470Sstevel@tonic-gate * form an absolute path, call resolvepath() on it, and then let the
480Sstevel@tonic-gate * caller's function do the final confirmation.
490Sstevel@tonic-gate */
500Sstevel@tonic-gate static int
try_exec(struct ps_prochandle * P,const char * cwd,const char * path,char * buf,int (* isexec)(const char *,void *),void * isdata)51*7675SEdward.Pilatowicz@Sun.COM try_exec(struct ps_prochandle *P, const char *cwd, const char *path, char *buf,
520Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata)
530Sstevel@tonic-gate {
540Sstevel@tonic-gate int i;
550Sstevel@tonic-gate
560Sstevel@tonic-gate if (path[0] != '/')
570Sstevel@tonic-gate (void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path);
580Sstevel@tonic-gate else
590Sstevel@tonic-gate (void) strcpy(buf, path);
600Sstevel@tonic-gate
610Sstevel@tonic-gate dprintf("try_exec \"%s\"\n", buf);
620Sstevel@tonic-gate
63*7675SEdward.Pilatowicz@Sun.COM (void) Pfindobj(P, buf, buf, PATH_MAX);
640Sstevel@tonic-gate if ((i = resolvepath(buf, buf, PATH_MAX)) > 0) {
650Sstevel@tonic-gate buf[i] = '\0';
660Sstevel@tonic-gate return (isexec(buf, isdata));
670Sstevel@tonic-gate }
680Sstevel@tonic-gate
690Sstevel@tonic-gate return (0); /* resolvepath failed */
700Sstevel@tonic-gate }
710Sstevel@tonic-gate
720Sstevel@tonic-gate /*
730Sstevel@tonic-gate * The Pfindexec function contains the logic for the executable name dance.
740Sstevel@tonic-gate * The caller provides a possible executable name or likely directory (the
750Sstevel@tonic-gate * aout parameter), and a function which is responsible for doing any
760Sstevel@tonic-gate * final confirmation on the executable pathname once a possible full
770Sstevel@tonic-gate * pathname has been chosen.
780Sstevel@tonic-gate */
790Sstevel@tonic-gate char *
Pfindexec(struct ps_prochandle * P,const char * aout,int (* isexec)(const char *,void *),void * isdata)800Sstevel@tonic-gate Pfindexec(struct ps_prochandle *P, const char *aout,
810Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata)
820Sstevel@tonic-gate {
830Sstevel@tonic-gate char cwd[PATH_MAX * 2];
840Sstevel@tonic-gate char path[PATH_MAX];
850Sstevel@tonic-gate char buf[PATH_MAX];
860Sstevel@tonic-gate struct stat st;
870Sstevel@tonic-gate uintptr_t addr;
882712Snn35248 char *p = path, *q;
890Sstevel@tonic-gate
90*7675SEdward.Pilatowicz@Sun.COM dprintf("Pfindexec '%s'\n", aout);
91*7675SEdward.Pilatowicz@Sun.COM
920Sstevel@tonic-gate if (P->execname)
930Sstevel@tonic-gate return (P->execname); /* Already found */
940Sstevel@tonic-gate
950Sstevel@tonic-gate errno = 0; /* Set to zero so we can tell if stat() failed */
960Sstevel@tonic-gate
970Sstevel@tonic-gate /*
980Sstevel@tonic-gate * First try: use the provided default value, if it is not a directory.
990Sstevel@tonic-gate * If the aout parameter turns out to be a directory, this is
1000Sstevel@tonic-gate * interpreted as the directory to use as an alternate cwd for
1010Sstevel@tonic-gate * our subsequent attempts to locate the executable.
1020Sstevel@tonic-gate */
1030Sstevel@tonic-gate if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) {
104*7675SEdward.Pilatowicz@Sun.COM if (try_exec(P, ".", aout, buf, isexec, isdata))
1050Sstevel@tonic-gate goto found;
1060Sstevel@tonic-gate else
1070Sstevel@tonic-gate aout = ".";
1080Sstevel@tonic-gate
1090Sstevel@tonic-gate } else if (aout == NULL || errno != 0)
1100Sstevel@tonic-gate aout = ".";
1110Sstevel@tonic-gate
1120Sstevel@tonic-gate /*
1130Sstevel@tonic-gate * At this point 'aout' is either "." or an alternate cwd. We use
1140Sstevel@tonic-gate * realpath(3c) to turn this into a full pathname free of ".", "..",
1150Sstevel@tonic-gate * and symlinks. If this fails for some reason, fall back to "."
1160Sstevel@tonic-gate */
1170Sstevel@tonic-gate if (realpath(aout, cwd) == NULL)
1180Sstevel@tonic-gate (void) strcpy(cwd, ".");
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate /*
1210Sstevel@tonic-gate * Second try: read the string pointed to by the AT_SUN_EXECNAME
1220Sstevel@tonic-gate * auxv element, saved when the program was exec'd. If the full
1230Sstevel@tonic-gate * pathname try_exec() forms fails, try again using just the
1242712Snn35248 * basename appended to our cwd. If that also fails, and the process
1252712Snn35248 * is in a zone, try again with the zone path instead of our cwd.
1260Sstevel@tonic-gate */
1270Sstevel@tonic-gate if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L &&
1280Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) {
1292712Snn35248 char zpath[PATH_MAX];
1302712Snn35248 const psinfo_t *pi = Ppsinfo(P);
1310Sstevel@tonic-gate
132*7675SEdward.Pilatowicz@Sun.COM if (try_exec(P, cwd, path, buf, isexec, isdata))
1330Sstevel@tonic-gate goto found;
1340Sstevel@tonic-gate
1352232Srab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
136*7675SEdward.Pilatowicz@Sun.COM try_exec(P, cwd, p, buf, isexec, isdata))
1370Sstevel@tonic-gate goto found;
1382712Snn35248
1392823Srab if (getzoneid() == GLOBAL_ZONEID &&
1402823Srab pi->pr_zoneid != GLOBAL_ZONEID &&
1412823Srab zone_getattr(pi->pr_zoneid, ZONE_ATTR_ROOT, zpath,
142*7675SEdward.Pilatowicz@Sun.COM sizeof (zpath)) != -1) {
1432823Srab /*
1442823Srab * try_exec() only combines its cwd and path arguments
1452823Srab * if path is relative; but in our case even an absolute
1462823Srab * path inside a zone is a relative path from the global
1472823Srab * zone perspective. So we turn a non-global zone's
1482823Srab * absolute path into a relative path here before
1492823Srab * calling try_exec().
1502823Srab */
1512823Srab p = (path[0] == '/') ? path + 1 : path;
152*7675SEdward.Pilatowicz@Sun.COM if (try_exec(P, zpath, p, buf, isexec, isdata))
1532712Snn35248 goto found;
1542712Snn35248 }
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate
1570Sstevel@tonic-gate /*
1580Sstevel@tonic-gate * Third try: try using the first whitespace-separated token
1590Sstevel@tonic-gate * saved in the psinfo_t's pr_psargs (the initial value of argv[0]).
1600Sstevel@tonic-gate */
1610Sstevel@tonic-gate if (Ppsinfo(P) != NULL) {
1620Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ);
1630Sstevel@tonic-gate path[PRARGSZ] = '\0';
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL)
1660Sstevel@tonic-gate *p = '\0';
1670Sstevel@tonic-gate
168*7675SEdward.Pilatowicz@Sun.COM if (try_exec(P, cwd, path, buf, isexec, isdata))
1690Sstevel@tonic-gate goto found;
1700Sstevel@tonic-gate
1712232Srab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
172*7675SEdward.Pilatowicz@Sun.COM try_exec(P, cwd, p, buf, isexec, isdata))
1730Sstevel@tonic-gate goto found;
1740Sstevel@tonic-gate }
1750Sstevel@tonic-gate
1760Sstevel@tonic-gate /*
1770Sstevel@tonic-gate * Fourth try: read the string pointed to by argv[0] out of the
1780Sstevel@tonic-gate * stack in the process's address space.
1790Sstevel@tonic-gate */
1800Sstevel@tonic-gate if (P->psinfo.pr_argv != NULL &&
1810Sstevel@tonic-gate Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 &&
1820Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) {
1830Sstevel@tonic-gate
184*7675SEdward.Pilatowicz@Sun.COM if (try_exec(P, cwd, path, buf, isexec, isdata))
1850Sstevel@tonic-gate goto found;
1860Sstevel@tonic-gate
1872232Srab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
188*7675SEdward.Pilatowicz@Sun.COM try_exec(P, cwd, p, buf, isexec, isdata))
1890Sstevel@tonic-gate goto found;
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate * Fifth try: read the process's $PATH environment variable and
1940Sstevel@tonic-gate * search each directory named there for the name matching pr_fname.
1950Sstevel@tonic-gate */
1960Sstevel@tonic-gate if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) {
1970Sstevel@tonic-gate /*
1980Sstevel@tonic-gate * If the name from pr_psargs contains pr_fname as its
1990Sstevel@tonic-gate * leading string, then accept the name from pr_psargs
2000Sstevel@tonic-gate * because more bytes are saved there. Otherwise use
2010Sstevel@tonic-gate * pr_fname because this gives us new information.
2020Sstevel@tonic-gate */
2030Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ);
2040Sstevel@tonic-gate path[PRARGSZ] = '\0';
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL)
2070Sstevel@tonic-gate *p = '\0';
2080Sstevel@tonic-gate
2090Sstevel@tonic-gate if (strchr(path, '/') != NULL || strncmp(path,
2100Sstevel@tonic-gate P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0)
2110Sstevel@tonic-gate (void) strcpy(path, P->psinfo.pr_fname);
2120Sstevel@tonic-gate
2130Sstevel@tonic-gate /*
2140Sstevel@tonic-gate * Now iterate over the $PATH elements, trying to form
2150Sstevel@tonic-gate * an executable pathname with each one.
2160Sstevel@tonic-gate */
2170Sstevel@tonic-gate for (p = strtok_r(cwd, ":", &q); p != NULL;
2180Sstevel@tonic-gate p = strtok_r(NULL, ":", &q)) {
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate if (*p != '/')
2210Sstevel@tonic-gate continue; /* Ignore anything relative */
2220Sstevel@tonic-gate
223*7675SEdward.Pilatowicz@Sun.COM if (try_exec(P, p, path, buf, isexec, isdata))
2240Sstevel@tonic-gate goto found;
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate errno = ENOENT;
2290Sstevel@tonic-gate return (NULL);
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate found:
2320Sstevel@tonic-gate if ((P->execname = strdup(buf)) == NULL)
2330Sstevel@tonic-gate dprintf("failed to malloc; executable name is \"%s\"", buf);
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate return (P->execname);
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate /*
2390Sstevel@tonic-gate * Callback function for Pfindexec(). We return a match if we can stat the
2400Sstevel@tonic-gate * suggested pathname and confirm its device and inode number match our
2410Sstevel@tonic-gate * previous information about the /proc/<pid>/object/a.out file.
2420Sstevel@tonic-gate */
2430Sstevel@tonic-gate static int
stat_exec(const char * path,struct stat64 * stp)2440Sstevel@tonic-gate stat_exec(const char *path, struct stat64 *stp)
2450Sstevel@tonic-gate {
2460Sstevel@tonic-gate struct stat64 st;
2470Sstevel@tonic-gate
2480Sstevel@tonic-gate return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) &&
2490Sstevel@tonic-gate stp->st_dev == st.st_dev && stp->st_ino == st.st_ino);
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate /*
2530Sstevel@tonic-gate * Return the full pathname for the executable file. If the process handle is
2540Sstevel@tonic-gate * a core file, we've already tried our best to get the executable name.
2550Sstevel@tonic-gate * Otherwise, we make an attempt using Pfindexec().
2560Sstevel@tonic-gate */
2570Sstevel@tonic-gate char *
Pexecname(struct ps_prochandle * P,char * buf,size_t buflen)2580Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen)
2590Sstevel@tonic-gate {
260*7675SEdward.Pilatowicz@Sun.COM if (P->execname != NULL) {
261*7675SEdward.Pilatowicz@Sun.COM (void) strncpy(buf, P->execname, buflen);
262*7675SEdward.Pilatowicz@Sun.COM return (buf);
263*7675SEdward.Pilatowicz@Sun.COM }
264*7675SEdward.Pilatowicz@Sun.COM
265*7675SEdward.Pilatowicz@Sun.COM if (P->state != PS_DEAD && P->state != PS_IDLE) {
2660Sstevel@tonic-gate char exec_name[PATH_MAX];
2670Sstevel@tonic-gate char cwd[PATH_MAX];
2680Sstevel@tonic-gate char proc_cwd[64];
2690Sstevel@tonic-gate struct stat64 st;
2700Sstevel@tonic-gate int ret;
2710Sstevel@tonic-gate
2720Sstevel@tonic-gate /*
2730Sstevel@tonic-gate * Try to get the path information first.
2740Sstevel@tonic-gate */
2750Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name),
2762712Snn35248 "%s/%d/path/a.out", procfs_path, (int)P->pid);
2770Sstevel@tonic-gate if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) {
2780Sstevel@tonic-gate buf[ret] = '\0';
279*7675SEdward.Pilatowicz@Sun.COM (void) Pfindobj(P, buf, buf, buflen);
2800Sstevel@tonic-gate return (buf);
2810Sstevel@tonic-gate }
2820Sstevel@tonic-gate
2830Sstevel@tonic-gate /*
2840Sstevel@tonic-gate * Stat the executable file so we can compare Pfindexec's
2850Sstevel@tonic-gate * suggestions to the actual device and inode number.
2860Sstevel@tonic-gate */
2870Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name),
2882712Snn35248 "%s/%d/object/a.out", procfs_path, (int)P->pid);
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode))
2910Sstevel@tonic-gate return (NULL);
2920Sstevel@tonic-gate
2930Sstevel@tonic-gate /*
2940Sstevel@tonic-gate * Attempt to figure out the current working directory of the
2950Sstevel@tonic-gate * target process. This only works if the target process has
2960Sstevel@tonic-gate * not changed its current directory since it was exec'd.
2970Sstevel@tonic-gate */
2980Sstevel@tonic-gate (void) snprintf(proc_cwd, sizeof (proc_cwd),
2992712Snn35248 "%s/%d/path/cwd", procfs_path, (int)P->pid);
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0)
3020Sstevel@tonic-gate cwd[ret] = '\0';
3030Sstevel@tonic-gate
3040Sstevel@tonic-gate (void) Pfindexec(P, ret > 0 ? cwd : NULL,
3050Sstevel@tonic-gate (int (*)(const char *, void *))stat_exec, &st);
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate
3080Sstevel@tonic-gate return (NULL);
3090Sstevel@tonic-gate }
310