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*c6e80875Srab #include <zone.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; 889acbbeafSnn35248 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 1229acbbeafSnn35248 * basename appended to our cwd. If that also fails, and the process 1239acbbeafSnn35248 * 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) { 1279acbbeafSnn35248 char zpath[PATH_MAX]; 1289acbbeafSnn35248 const psinfo_t *pi = Ppsinfo(P); 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1317c478bd9Sstevel@tonic-gate goto found; 1327c478bd9Sstevel@tonic-gate 13368dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 13468dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1357c478bd9Sstevel@tonic-gate goto found; 1369acbbeafSnn35248 137*c6e80875Srab if (getzoneid() == GLOBAL_ZONEID && 138*c6e80875Srab pi->pr_zoneid != GLOBAL_ZONEID && 139*c6e80875Srab zone_getattr(pi->pr_zoneid, ZONE_ATTR_ROOT, zpath, 140*c6e80875Srab sizeof (zpath)) != -1) { 141*c6e80875Srab /* 142*c6e80875Srab * try_exec() only combines its cwd and path arguments 143*c6e80875Srab * if path is relative; but in our case even an absolute 144*c6e80875Srab * path inside a zone is a relative path from the global 145*c6e80875Srab * zone perspective. So we turn a non-global zone's 146*c6e80875Srab * absolute path into a relative path here before 147*c6e80875Srab * calling try_exec(). 148*c6e80875Srab */ 149*c6e80875Srab p = (path[0] == '/') ? path + 1 : path; 1509acbbeafSnn35248 if (try_exec(zpath, p, buf, isexec, isdata)) 1519acbbeafSnn35248 goto found; 1529acbbeafSnn35248 } 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate /* 1567c478bd9Sstevel@tonic-gate * Third try: try using the first whitespace-separated token 1577c478bd9Sstevel@tonic-gate * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). 1587c478bd9Sstevel@tonic-gate */ 1597c478bd9Sstevel@tonic-gate if (Ppsinfo(P) != NULL) { 1607c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 1617c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 1647c478bd9Sstevel@tonic-gate *p = '\0'; 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1677c478bd9Sstevel@tonic-gate goto found; 1687c478bd9Sstevel@tonic-gate 16968dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 17068dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1717c478bd9Sstevel@tonic-gate goto found; 1727c478bd9Sstevel@tonic-gate } 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate /* 1757c478bd9Sstevel@tonic-gate * Fourth try: read the string pointed to by argv[0] out of the 1767c478bd9Sstevel@tonic-gate * stack in the process's address space. 1777c478bd9Sstevel@tonic-gate */ 1787c478bd9Sstevel@tonic-gate if (P->psinfo.pr_argv != NULL && 1797c478bd9Sstevel@tonic-gate Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && 1807c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1837c478bd9Sstevel@tonic-gate goto found; 1847c478bd9Sstevel@tonic-gate 18568dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 18668dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1877c478bd9Sstevel@tonic-gate goto found; 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * Fifth try: read the process's $PATH environment variable and 1927c478bd9Sstevel@tonic-gate * search each directory named there for the name matching pr_fname. 1937c478bd9Sstevel@tonic-gate */ 1947c478bd9Sstevel@tonic-gate if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { 1957c478bd9Sstevel@tonic-gate /* 1967c478bd9Sstevel@tonic-gate * If the name from pr_psargs contains pr_fname as its 1977c478bd9Sstevel@tonic-gate * leading string, then accept the name from pr_psargs 1987c478bd9Sstevel@tonic-gate * because more bytes are saved there. Otherwise use 1997c478bd9Sstevel@tonic-gate * pr_fname because this gives us new information. 2007c478bd9Sstevel@tonic-gate */ 2017c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 2027c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 2037c478bd9Sstevel@tonic-gate 2047c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 2057c478bd9Sstevel@tonic-gate *p = '\0'; 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL || strncmp(path, 2087c478bd9Sstevel@tonic-gate P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) 2097c478bd9Sstevel@tonic-gate (void) strcpy(path, P->psinfo.pr_fname); 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate /* 2127c478bd9Sstevel@tonic-gate * Now iterate over the $PATH elements, trying to form 2137c478bd9Sstevel@tonic-gate * an executable pathname with each one. 2147c478bd9Sstevel@tonic-gate */ 2157c478bd9Sstevel@tonic-gate for (p = strtok_r(cwd, ":", &q); p != NULL; 2167c478bd9Sstevel@tonic-gate p = strtok_r(NULL, ":", &q)) { 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate if (*p != '/') 2197c478bd9Sstevel@tonic-gate continue; /* Ignore anything relative */ 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate if (try_exec(p, path, buf, isexec, isdata)) 2227c478bd9Sstevel@tonic-gate goto found; 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate errno = ENOENT; 2277c478bd9Sstevel@tonic-gate return (NULL); 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate found: 2307c478bd9Sstevel@tonic-gate if ((P->execname = strdup(buf)) == NULL) 2317c478bd9Sstevel@tonic-gate dprintf("failed to malloc; executable name is \"%s\"", buf); 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate return (P->execname); 2347c478bd9Sstevel@tonic-gate } 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate /* 2377c478bd9Sstevel@tonic-gate * Callback function for Pfindexec(). We return a match if we can stat the 2387c478bd9Sstevel@tonic-gate * suggested pathname and confirm its device and inode number match our 2397c478bd9Sstevel@tonic-gate * previous information about the /proc/<pid>/object/a.out file. 2407c478bd9Sstevel@tonic-gate */ 2417c478bd9Sstevel@tonic-gate static int 2427c478bd9Sstevel@tonic-gate stat_exec(const char *path, struct stat64 *stp) 2437c478bd9Sstevel@tonic-gate { 2447c478bd9Sstevel@tonic-gate struct stat64 st; 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && 2477c478bd9Sstevel@tonic-gate stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); 2487c478bd9Sstevel@tonic-gate } 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate /* 2517c478bd9Sstevel@tonic-gate * Return the full pathname for the executable file. If the process handle is 2527c478bd9Sstevel@tonic-gate * a core file, we've already tried our best to get the executable name. 2537c478bd9Sstevel@tonic-gate * Otherwise, we make an attempt using Pfindexec(). 2547c478bd9Sstevel@tonic-gate */ 2557c478bd9Sstevel@tonic-gate char * 2567c478bd9Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) 2577c478bd9Sstevel@tonic-gate { 2587c478bd9Sstevel@tonic-gate if (P->execname == NULL && P->state != PS_DEAD && P->state != PS_IDLE) { 2597c478bd9Sstevel@tonic-gate char exec_name[PATH_MAX]; 2607c478bd9Sstevel@tonic-gate char cwd[PATH_MAX]; 2617c478bd9Sstevel@tonic-gate char proc_cwd[64]; 2627c478bd9Sstevel@tonic-gate struct stat64 st; 2637c478bd9Sstevel@tonic-gate int ret; 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate /* 2667c478bd9Sstevel@tonic-gate * Try to get the path information first. 2677c478bd9Sstevel@tonic-gate */ 2687c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 2699acbbeafSnn35248 "%s/%d/path/a.out", procfs_path, (int)P->pid); 2707c478bd9Sstevel@tonic-gate if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { 2717c478bd9Sstevel@tonic-gate buf[ret] = '\0'; 2727c478bd9Sstevel@tonic-gate return (buf); 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate /* 2767c478bd9Sstevel@tonic-gate * Stat the executable file so we can compare Pfindexec's 2777c478bd9Sstevel@tonic-gate * suggestions to the actual device and inode number. 2787c478bd9Sstevel@tonic-gate */ 2797c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 2809acbbeafSnn35248 "%s/%d/object/a.out", procfs_path, (int)P->pid); 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) 2837c478bd9Sstevel@tonic-gate return (NULL); 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate /* 2867c478bd9Sstevel@tonic-gate * Attempt to figure out the current working directory of the 2877c478bd9Sstevel@tonic-gate * target process. This only works if the target process has 2887c478bd9Sstevel@tonic-gate * not changed its current directory since it was exec'd. 2897c478bd9Sstevel@tonic-gate */ 2907c478bd9Sstevel@tonic-gate (void) snprintf(proc_cwd, sizeof (proc_cwd), 2919acbbeafSnn35248 "%s/%d/path/cwd", procfs_path, (int)P->pid); 2927c478bd9Sstevel@tonic-gate 2937c478bd9Sstevel@tonic-gate if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) 2947c478bd9Sstevel@tonic-gate cwd[ret] = '\0'; 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate (void) Pfindexec(P, ret > 0 ? cwd : NULL, 2977c478bd9Sstevel@tonic-gate (int (*)(const char *, void *))stat_exec, &st); 2987c478bd9Sstevel@tonic-gate } 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate if (P->execname != NULL) { 3017c478bd9Sstevel@tonic-gate (void) strncpy(buf, P->execname, buflen); 3027c478bd9Sstevel@tonic-gate return (buf); 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate return (NULL); 3067c478bd9Sstevel@tonic-gate } 307