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 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 * 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 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 * 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