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 /* 22*186f7fbfSEdward Pilatowicz * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #define __EXTENSIONS__ 277c478bd9Sstevel@tonic-gate #include <string.h> 287c478bd9Sstevel@tonic-gate #undef __EXTENSIONS__ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #include <libgen.h> 317c478bd9Sstevel@tonic-gate #include <limits.h> 327c478bd9Sstevel@tonic-gate #include <stdio.h> 337c478bd9Sstevel@tonic-gate #include <errno.h> 347c478bd9Sstevel@tonic-gate #include <unistd.h> 35c6e80875Srab #include <zone.h> 367c478bd9Sstevel@tonic-gate 37*186f7fbfSEdward Pilatowicz #include "libproc.h" 387c478bd9Sstevel@tonic-gate #include "Pcontrol.h" 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate /* 417c478bd9Sstevel@tonic-gate * Pexecname.c - Way too much code to attempt to derive the full pathname of 427c478bd9Sstevel@tonic-gate * the executable file from a process handle, be it dead or alive. 437c478bd9Sstevel@tonic-gate */ 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate /* 467c478bd9Sstevel@tonic-gate * Once we've computed a cwd and a relative path, we use try_exec() to 477c478bd9Sstevel@tonic-gate * form an absolute path, call resolvepath() on it, and then let the 487c478bd9Sstevel@tonic-gate * caller's function do the final confirmation. 497c478bd9Sstevel@tonic-gate */ 507c478bd9Sstevel@tonic-gate static int 51*186f7fbfSEdward Pilatowicz try_exec(struct ps_prochandle *P, const char *cwd, const char *path, char *buf, 527c478bd9Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata) 537c478bd9Sstevel@tonic-gate { 547c478bd9Sstevel@tonic-gate int i; 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate if (path[0] != '/') 577c478bd9Sstevel@tonic-gate (void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path); 587c478bd9Sstevel@tonic-gate else 597c478bd9Sstevel@tonic-gate (void) strcpy(buf, path); 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate dprintf("try_exec \"%s\"\n", buf); 627c478bd9Sstevel@tonic-gate 63*186f7fbfSEdward Pilatowicz (void) Pfindobj(P, buf, buf, PATH_MAX); 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; 889acbbeafSnn35248 char *p = path, *q; 897c478bd9Sstevel@tonic-gate 90*186f7fbfSEdward Pilatowicz dprintf("Pfindexec '%s'\n", aout); 91*186f7fbfSEdward Pilatowicz 927c478bd9Sstevel@tonic-gate if (P->execname) 937c478bd9Sstevel@tonic-gate return (P->execname); /* Already found */ 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate errno = 0; /* Set to zero so we can tell if stat() failed */ 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate /* 987c478bd9Sstevel@tonic-gate * First try: use the provided default value, if it is not a directory. 997c478bd9Sstevel@tonic-gate * If the aout parameter turns out to be a directory, this is 1007c478bd9Sstevel@tonic-gate * interpreted as the directory to use as an alternate cwd for 1017c478bd9Sstevel@tonic-gate * our subsequent attempts to locate the executable. 1027c478bd9Sstevel@tonic-gate */ 1037c478bd9Sstevel@tonic-gate if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) { 104*186f7fbfSEdward Pilatowicz if (try_exec(P, ".", aout, buf, isexec, isdata)) 1057c478bd9Sstevel@tonic-gate goto found; 1067c478bd9Sstevel@tonic-gate else 1077c478bd9Sstevel@tonic-gate aout = "."; 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate } else if (aout == NULL || errno != 0) 1107c478bd9Sstevel@tonic-gate aout = "."; 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate /* 1137c478bd9Sstevel@tonic-gate * At this point 'aout' is either "." or an alternate cwd. We use 1147c478bd9Sstevel@tonic-gate * realpath(3c) to turn this into a full pathname free of ".", "..", 1157c478bd9Sstevel@tonic-gate * and symlinks. If this fails for some reason, fall back to "." 1167c478bd9Sstevel@tonic-gate */ 1177c478bd9Sstevel@tonic-gate if (realpath(aout, cwd) == NULL) 1187c478bd9Sstevel@tonic-gate (void) strcpy(cwd, "."); 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate /* 1217c478bd9Sstevel@tonic-gate * Second try: read the string pointed to by the AT_SUN_EXECNAME 1227c478bd9Sstevel@tonic-gate * auxv element, saved when the program was exec'd. If the full 1237c478bd9Sstevel@tonic-gate * pathname try_exec() forms fails, try again using just the 1249acbbeafSnn35248 * basename appended to our cwd. If that also fails, and the process 1259acbbeafSnn35248 * is in a zone, try again with the zone path instead of our cwd. 1267c478bd9Sstevel@tonic-gate */ 1277c478bd9Sstevel@tonic-gate if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L && 1287c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1299acbbeafSnn35248 char zpath[PATH_MAX]; 1309acbbeafSnn35248 const psinfo_t *pi = Ppsinfo(P); 1317c478bd9Sstevel@tonic-gate 132*186f7fbfSEdward Pilatowicz if (try_exec(P, cwd, path, buf, isexec, isdata)) 1337c478bd9Sstevel@tonic-gate goto found; 1347c478bd9Sstevel@tonic-gate 13568dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 136*186f7fbfSEdward Pilatowicz try_exec(P, cwd, p, buf, isexec, isdata)) 1377c478bd9Sstevel@tonic-gate goto found; 1389acbbeafSnn35248 139c6e80875Srab if (getzoneid() == GLOBAL_ZONEID && 140c6e80875Srab pi->pr_zoneid != GLOBAL_ZONEID && 141c6e80875Srab zone_getattr(pi->pr_zoneid, ZONE_ATTR_ROOT, zpath, 142c6e80875Srab sizeof (zpath)) != -1) { 143c6e80875Srab /* 144c6e80875Srab * try_exec() only combines its cwd and path arguments 145c6e80875Srab * if path is relative; but in our case even an absolute 146c6e80875Srab * path inside a zone is a relative path from the global 147c6e80875Srab * zone perspective. So we turn a non-global zone's 148c6e80875Srab * absolute path into a relative path here before 149c6e80875Srab * calling try_exec(). 150c6e80875Srab */ 151c6e80875Srab p = (path[0] == '/') ? path + 1 : path; 152*186f7fbfSEdward Pilatowicz if (try_exec(P, zpath, p, buf, isexec, isdata)) 1539acbbeafSnn35248 goto found; 1549acbbeafSnn35248 } 1557c478bd9Sstevel@tonic-gate } 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate /* 1587c478bd9Sstevel@tonic-gate * Third try: try using the first whitespace-separated token 1597c478bd9Sstevel@tonic-gate * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). 1607c478bd9Sstevel@tonic-gate */ 1617c478bd9Sstevel@tonic-gate if (Ppsinfo(P) != NULL) { 1627c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 1637c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 1667c478bd9Sstevel@tonic-gate *p = '\0'; 1677c478bd9Sstevel@tonic-gate 168*186f7fbfSEdward Pilatowicz if (try_exec(P, cwd, path, buf, isexec, isdata)) 1697c478bd9Sstevel@tonic-gate goto found; 1707c478bd9Sstevel@tonic-gate 17168dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 172*186f7fbfSEdward Pilatowicz try_exec(P, cwd, p, buf, isexec, isdata)) 1737c478bd9Sstevel@tonic-gate goto found; 1747c478bd9Sstevel@tonic-gate } 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate /* 1777c478bd9Sstevel@tonic-gate * Fourth try: read the string pointed to by argv[0] out of the 1787c478bd9Sstevel@tonic-gate * stack in the process's address space. 1797c478bd9Sstevel@tonic-gate */ 1807c478bd9Sstevel@tonic-gate if (P->psinfo.pr_argv != NULL && 1817c478bd9Sstevel@tonic-gate Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && 1827c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1837c478bd9Sstevel@tonic-gate 184*186f7fbfSEdward Pilatowicz if (try_exec(P, cwd, path, buf, isexec, isdata)) 1857c478bd9Sstevel@tonic-gate goto found; 1867c478bd9Sstevel@tonic-gate 18768dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 188*186f7fbfSEdward Pilatowicz try_exec(P, cwd, p, buf, isexec, isdata)) 1897c478bd9Sstevel@tonic-gate goto found; 1907c478bd9Sstevel@tonic-gate } 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate /* 1937c478bd9Sstevel@tonic-gate * Fifth try: read the process's $PATH environment variable and 1947c478bd9Sstevel@tonic-gate * search each directory named there for the name matching pr_fname. 1957c478bd9Sstevel@tonic-gate */ 1967c478bd9Sstevel@tonic-gate if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { 1977c478bd9Sstevel@tonic-gate /* 1987c478bd9Sstevel@tonic-gate * If the name from pr_psargs contains pr_fname as its 1997c478bd9Sstevel@tonic-gate * leading string, then accept the name from pr_psargs 2007c478bd9Sstevel@tonic-gate * because more bytes are saved there. Otherwise use 2017c478bd9Sstevel@tonic-gate * pr_fname because this gives us new information. 2027c478bd9Sstevel@tonic-gate */ 2037c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 2047c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 2077c478bd9Sstevel@tonic-gate *p = '\0'; 2087c478bd9Sstevel@tonic-gate 2097c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL || strncmp(path, 2107c478bd9Sstevel@tonic-gate P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) 2117c478bd9Sstevel@tonic-gate (void) strcpy(path, P->psinfo.pr_fname); 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate /* 2147c478bd9Sstevel@tonic-gate * Now iterate over the $PATH elements, trying to form 2157c478bd9Sstevel@tonic-gate * an executable pathname with each one. 2167c478bd9Sstevel@tonic-gate */ 2177c478bd9Sstevel@tonic-gate for (p = strtok_r(cwd, ":", &q); p != NULL; 2187c478bd9Sstevel@tonic-gate p = strtok_r(NULL, ":", &q)) { 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate if (*p != '/') 2217c478bd9Sstevel@tonic-gate continue; /* Ignore anything relative */ 2227c478bd9Sstevel@tonic-gate 223*186f7fbfSEdward Pilatowicz if (try_exec(P, p, path, buf, isexec, isdata)) 2247c478bd9Sstevel@tonic-gate goto found; 2257c478bd9Sstevel@tonic-gate } 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate errno = ENOENT; 2297c478bd9Sstevel@tonic-gate return (NULL); 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate found: 2327c478bd9Sstevel@tonic-gate if ((P->execname = strdup(buf)) == NULL) 2337c478bd9Sstevel@tonic-gate dprintf("failed to malloc; executable name is \"%s\"", buf); 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate return (P->execname); 2367c478bd9Sstevel@tonic-gate } 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate /* 2397c478bd9Sstevel@tonic-gate * Callback function for Pfindexec(). We return a match if we can stat the 2407c478bd9Sstevel@tonic-gate * suggested pathname and confirm its device and inode number match our 2417c478bd9Sstevel@tonic-gate * previous information about the /proc/<pid>/object/a.out file. 2427c478bd9Sstevel@tonic-gate */ 2437c478bd9Sstevel@tonic-gate static int 2447c478bd9Sstevel@tonic-gate stat_exec(const char *path, struct stat64 *stp) 2457c478bd9Sstevel@tonic-gate { 2467c478bd9Sstevel@tonic-gate struct stat64 st; 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && 2497c478bd9Sstevel@tonic-gate stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); 2507c478bd9Sstevel@tonic-gate } 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate /* 2537c478bd9Sstevel@tonic-gate * Return the full pathname for the executable file. If the process handle is 2547c478bd9Sstevel@tonic-gate * a core file, we've already tried our best to get the executable name. 2557c478bd9Sstevel@tonic-gate * Otherwise, we make an attempt using Pfindexec(). 2567c478bd9Sstevel@tonic-gate */ 2577c478bd9Sstevel@tonic-gate char * 2587c478bd9Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) 2597c478bd9Sstevel@tonic-gate { 260*186f7fbfSEdward Pilatowicz if (P->execname != NULL) { 261*186f7fbfSEdward Pilatowicz (void) strncpy(buf, P->execname, buflen); 262*186f7fbfSEdward Pilatowicz return (buf); 263*186f7fbfSEdward Pilatowicz } 264*186f7fbfSEdward Pilatowicz 265*186f7fbfSEdward Pilatowicz if (P->state != PS_DEAD && P->state != PS_IDLE) { 2667c478bd9Sstevel@tonic-gate char exec_name[PATH_MAX]; 2677c478bd9Sstevel@tonic-gate char cwd[PATH_MAX]; 2687c478bd9Sstevel@tonic-gate char proc_cwd[64]; 2697c478bd9Sstevel@tonic-gate struct stat64 st; 2707c478bd9Sstevel@tonic-gate int ret; 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate /* 2737c478bd9Sstevel@tonic-gate * Try to get the path information first. 2747c478bd9Sstevel@tonic-gate */ 2757c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 2769acbbeafSnn35248 "%s/%d/path/a.out", procfs_path, (int)P->pid); 2777c478bd9Sstevel@tonic-gate if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { 2787c478bd9Sstevel@tonic-gate buf[ret] = '\0'; 279*186f7fbfSEdward Pilatowicz (void) Pfindobj(P, buf, buf, buflen); 2807c478bd9Sstevel@tonic-gate return (buf); 2817c478bd9Sstevel@tonic-gate } 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate /* 2847c478bd9Sstevel@tonic-gate * Stat the executable file so we can compare Pfindexec's 2857c478bd9Sstevel@tonic-gate * suggestions to the actual device and inode number. 2867c478bd9Sstevel@tonic-gate */ 2877c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 2889acbbeafSnn35248 "%s/%d/object/a.out", procfs_path, (int)P->pid); 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) 2917c478bd9Sstevel@tonic-gate return (NULL); 2927c478bd9Sstevel@tonic-gate 2937c478bd9Sstevel@tonic-gate /* 2947c478bd9Sstevel@tonic-gate * Attempt to figure out the current working directory of the 2957c478bd9Sstevel@tonic-gate * target process. This only works if the target process has 2967c478bd9Sstevel@tonic-gate * not changed its current directory since it was exec'd. 2977c478bd9Sstevel@tonic-gate */ 2987c478bd9Sstevel@tonic-gate (void) snprintf(proc_cwd, sizeof (proc_cwd), 2999acbbeafSnn35248 "%s/%d/path/cwd", procfs_path, (int)P->pid); 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) 3027c478bd9Sstevel@tonic-gate cwd[ret] = '\0'; 3037c478bd9Sstevel@tonic-gate 3047c478bd9Sstevel@tonic-gate (void) Pfindexec(P, ret > 0 ? cwd : NULL, 3057c478bd9Sstevel@tonic-gate (int (*)(const char *, void *))stat_exec, &st); 3067c478bd9Sstevel@tonic-gate } 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate return (NULL); 3097c478bd9Sstevel@tonic-gate } 310