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 /* 222232Srab * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #define __EXTENSIONS__ 290Sstevel@tonic-gate #include <string.h> 300Sstevel@tonic-gate #undef __EXTENSIONS__ 310Sstevel@tonic-gate 320Sstevel@tonic-gate #include <libgen.h> 330Sstevel@tonic-gate #include <limits.h> 340Sstevel@tonic-gate #include <stdio.h> 350Sstevel@tonic-gate #include <errno.h> 360Sstevel@tonic-gate #include <unistd.h> 37*2823Srab #include <zone.h> 380Sstevel@tonic-gate 390Sstevel@tonic-gate #include "Pcontrol.h" 400Sstevel@tonic-gate 410Sstevel@tonic-gate /* 420Sstevel@tonic-gate * Pexecname.c - Way too much code to attempt to derive the full pathname of 430Sstevel@tonic-gate * the executable file from a process handle, be it dead or alive. 440Sstevel@tonic-gate */ 450Sstevel@tonic-gate 460Sstevel@tonic-gate /* 470Sstevel@tonic-gate * Once we've computed a cwd and a relative path, we use try_exec() to 480Sstevel@tonic-gate * form an absolute path, call resolvepath() on it, and then let the 490Sstevel@tonic-gate * caller's function do the final confirmation. 500Sstevel@tonic-gate */ 510Sstevel@tonic-gate static int 520Sstevel@tonic-gate try_exec(const char *cwd, const char *path, char *buf, 530Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata) 540Sstevel@tonic-gate { 550Sstevel@tonic-gate int i; 560Sstevel@tonic-gate 570Sstevel@tonic-gate if (path[0] != '/') 580Sstevel@tonic-gate (void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path); 590Sstevel@tonic-gate else 600Sstevel@tonic-gate (void) strcpy(buf, path); 610Sstevel@tonic-gate 620Sstevel@tonic-gate dprintf("try_exec \"%s\"\n", buf); 630Sstevel@tonic-gate 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 900Sstevel@tonic-gate if (P->execname) 910Sstevel@tonic-gate return (P->execname); /* Already found */ 920Sstevel@tonic-gate 930Sstevel@tonic-gate errno = 0; /* Set to zero so we can tell if stat() failed */ 940Sstevel@tonic-gate 950Sstevel@tonic-gate /* 960Sstevel@tonic-gate * First try: use the provided default value, if it is not a directory. 970Sstevel@tonic-gate * If the aout parameter turns out to be a directory, this is 980Sstevel@tonic-gate * interpreted as the directory to use as an alternate cwd for 990Sstevel@tonic-gate * our subsequent attempts to locate the executable. 1000Sstevel@tonic-gate */ 1010Sstevel@tonic-gate if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) { 1020Sstevel@tonic-gate if (try_exec(".", aout, buf, isexec, isdata)) 1030Sstevel@tonic-gate goto found; 1040Sstevel@tonic-gate else 1050Sstevel@tonic-gate aout = "."; 1060Sstevel@tonic-gate 1070Sstevel@tonic-gate } else if (aout == NULL || errno != 0) 1080Sstevel@tonic-gate aout = "."; 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate /* 1110Sstevel@tonic-gate * At this point 'aout' is either "." or an alternate cwd. We use 1120Sstevel@tonic-gate * realpath(3c) to turn this into a full pathname free of ".", "..", 1130Sstevel@tonic-gate * and symlinks. If this fails for some reason, fall back to "." 1140Sstevel@tonic-gate */ 1150Sstevel@tonic-gate if (realpath(aout, cwd) == NULL) 1160Sstevel@tonic-gate (void) strcpy(cwd, "."); 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate /* 1190Sstevel@tonic-gate * Second try: read the string pointed to by the AT_SUN_EXECNAME 1200Sstevel@tonic-gate * auxv element, saved when the program was exec'd. If the full 1210Sstevel@tonic-gate * pathname try_exec() forms fails, try again using just the 1222712Snn35248 * basename appended to our cwd. If that also fails, and the process 1232712Snn35248 * is in a zone, try again with the zone path instead of our cwd. 1240Sstevel@tonic-gate */ 1250Sstevel@tonic-gate if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L && 1260Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1272712Snn35248 char zpath[PATH_MAX]; 1282712Snn35248 const psinfo_t *pi = Ppsinfo(P); 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1310Sstevel@tonic-gate goto found; 1320Sstevel@tonic-gate 1332232Srab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 1342232Srab try_exec(cwd, p, buf, isexec, isdata)) 1350Sstevel@tonic-gate goto found; 1362712Snn35248 137*2823Srab if (getzoneid() == GLOBAL_ZONEID && 138*2823Srab pi->pr_zoneid != GLOBAL_ZONEID && 139*2823Srab zone_getattr(pi->pr_zoneid, ZONE_ATTR_ROOT, zpath, 140*2823Srab sizeof (zpath)) != -1) { 141*2823Srab /* 142*2823Srab * try_exec() only combines its cwd and path arguments 143*2823Srab * if path is relative; but in our case even an absolute 144*2823Srab * path inside a zone is a relative path from the global 145*2823Srab * zone perspective. So we turn a non-global zone's 146*2823Srab * absolute path into a relative path here before 147*2823Srab * calling try_exec(). 148*2823Srab */ 149*2823Srab p = (path[0] == '/') ? path + 1 : path; 1502712Snn35248 if (try_exec(zpath, p, buf, isexec, isdata)) 1512712Snn35248 goto found; 1522712Snn35248 } 1530Sstevel@tonic-gate } 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate /* 1560Sstevel@tonic-gate * Third try: try using the first whitespace-separated token 1570Sstevel@tonic-gate * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). 1580Sstevel@tonic-gate */ 1590Sstevel@tonic-gate if (Ppsinfo(P) != NULL) { 1600Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 1610Sstevel@tonic-gate path[PRARGSZ] = '\0'; 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 1640Sstevel@tonic-gate *p = '\0'; 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1670Sstevel@tonic-gate goto found; 1680Sstevel@tonic-gate 1692232Srab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 1702232Srab try_exec(cwd, p, buf, isexec, isdata)) 1710Sstevel@tonic-gate goto found; 1720Sstevel@tonic-gate } 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate /* 1750Sstevel@tonic-gate * Fourth try: read the string pointed to by argv[0] out of the 1760Sstevel@tonic-gate * stack in the process's address space. 1770Sstevel@tonic-gate */ 1780Sstevel@tonic-gate if (P->psinfo.pr_argv != NULL && 1790Sstevel@tonic-gate Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && 1800Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1830Sstevel@tonic-gate goto found; 1840Sstevel@tonic-gate 1852232Srab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 1862232Srab try_exec(cwd, p, buf, isexec, isdata)) 1870Sstevel@tonic-gate goto found; 1880Sstevel@tonic-gate } 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate /* 1910Sstevel@tonic-gate * Fifth try: read the process's $PATH environment variable and 1920Sstevel@tonic-gate * search each directory named there for the name matching pr_fname. 1930Sstevel@tonic-gate */ 1940Sstevel@tonic-gate if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { 1950Sstevel@tonic-gate /* 1960Sstevel@tonic-gate * If the name from pr_psargs contains pr_fname as its 1970Sstevel@tonic-gate * leading string, then accept the name from pr_psargs 1980Sstevel@tonic-gate * because more bytes are saved there. Otherwise use 1990Sstevel@tonic-gate * pr_fname because this gives us new information. 2000Sstevel@tonic-gate */ 2010Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 2020Sstevel@tonic-gate path[PRARGSZ] = '\0'; 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 2050Sstevel@tonic-gate *p = '\0'; 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate if (strchr(path, '/') != NULL || strncmp(path, 2080Sstevel@tonic-gate P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) 2090Sstevel@tonic-gate (void) strcpy(path, P->psinfo.pr_fname); 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate /* 2120Sstevel@tonic-gate * Now iterate over the $PATH elements, trying to form 2130Sstevel@tonic-gate * an executable pathname with each one. 2140Sstevel@tonic-gate */ 2150Sstevel@tonic-gate for (p = strtok_r(cwd, ":", &q); p != NULL; 2160Sstevel@tonic-gate p = strtok_r(NULL, ":", &q)) { 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate if (*p != '/') 2190Sstevel@tonic-gate continue; /* Ignore anything relative */ 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate if (try_exec(p, path, buf, isexec, isdata)) 2220Sstevel@tonic-gate goto found; 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate } 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate errno = ENOENT; 2270Sstevel@tonic-gate return (NULL); 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate found: 2300Sstevel@tonic-gate if ((P->execname = strdup(buf)) == NULL) 2310Sstevel@tonic-gate dprintf("failed to malloc; executable name is \"%s\"", buf); 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate return (P->execname); 2340Sstevel@tonic-gate } 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate /* 2370Sstevel@tonic-gate * Callback function for Pfindexec(). We return a match if we can stat the 2380Sstevel@tonic-gate * suggested pathname and confirm its device and inode number match our 2390Sstevel@tonic-gate * previous information about the /proc/<pid>/object/a.out file. 2400Sstevel@tonic-gate */ 2410Sstevel@tonic-gate static int 2420Sstevel@tonic-gate stat_exec(const char *path, struct stat64 *stp) 2430Sstevel@tonic-gate { 2440Sstevel@tonic-gate struct stat64 st; 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && 2470Sstevel@tonic-gate stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); 2480Sstevel@tonic-gate } 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate /* 2510Sstevel@tonic-gate * Return the full pathname for the executable file. If the process handle is 2520Sstevel@tonic-gate * a core file, we've already tried our best to get the executable name. 2530Sstevel@tonic-gate * Otherwise, we make an attempt using Pfindexec(). 2540Sstevel@tonic-gate */ 2550Sstevel@tonic-gate char * 2560Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) 2570Sstevel@tonic-gate { 2580Sstevel@tonic-gate if (P->execname == NULL && P->state != PS_DEAD && P->state != PS_IDLE) { 2590Sstevel@tonic-gate char exec_name[PATH_MAX]; 2600Sstevel@tonic-gate char cwd[PATH_MAX]; 2610Sstevel@tonic-gate char proc_cwd[64]; 2620Sstevel@tonic-gate struct stat64 st; 2630Sstevel@tonic-gate int ret; 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate /* 2660Sstevel@tonic-gate * Try to get the path information first. 2670Sstevel@tonic-gate */ 2680Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 2692712Snn35248 "%s/%d/path/a.out", procfs_path, (int)P->pid); 2700Sstevel@tonic-gate if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { 2710Sstevel@tonic-gate buf[ret] = '\0'; 2720Sstevel@tonic-gate return (buf); 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * Stat the executable file so we can compare Pfindexec's 2770Sstevel@tonic-gate * suggestions to the actual device and inode number. 2780Sstevel@tonic-gate */ 2790Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 2802712Snn35248 "%s/%d/object/a.out", procfs_path, (int)P->pid); 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) 2830Sstevel@tonic-gate return (NULL); 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate /* 2860Sstevel@tonic-gate * Attempt to figure out the current working directory of the 2870Sstevel@tonic-gate * target process. This only works if the target process has 2880Sstevel@tonic-gate * not changed its current directory since it was exec'd. 2890Sstevel@tonic-gate */ 2900Sstevel@tonic-gate (void) snprintf(proc_cwd, sizeof (proc_cwd), 2912712Snn35248 "%s/%d/path/cwd", procfs_path, (int)P->pid); 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) 2940Sstevel@tonic-gate cwd[ret] = '\0'; 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate (void) Pfindexec(P, ret > 0 ? cwd : NULL, 2970Sstevel@tonic-gate (int (*)(const char *, void *))stat_exec, &st); 2980Sstevel@tonic-gate } 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate if (P->execname != NULL) { 3010Sstevel@tonic-gate (void) strncpy(buf, P->execname, buflen); 3020Sstevel@tonic-gate return (buf); 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate return (NULL); 3060Sstevel@tonic-gate } 307