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*9acbbeafSnn35248 #include <libzonecfg.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; 88*9acbbeafSnn35248 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 122*9acbbeafSnn35248 * basename appended to our cwd. If that also fails, and the process 123*9acbbeafSnn35248 * 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) { 127*9acbbeafSnn35248 char zname[ZONENAME_MAX]; 128*9acbbeafSnn35248 char zpath[PATH_MAX]; 129*9acbbeafSnn35248 const psinfo_t *pi = Ppsinfo(P); 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1327c478bd9Sstevel@tonic-gate goto found; 1337c478bd9Sstevel@tonic-gate 13468dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 13568dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1367c478bd9Sstevel@tonic-gate goto found; 137*9acbbeafSnn35248 138*9acbbeafSnn35248 if (getzonenamebyid(pi->pr_zoneid, zname, 139*9acbbeafSnn35248 sizeof (zname)) != -1 && strcmp(zname, "global") != 0 && 140*9acbbeafSnn35248 zone_get_zonepath(zname, zpath, sizeof (zpath)) == Z_OK) { 141*9acbbeafSnn35248 (void) strcat(zpath, "/root"); 142*9acbbeafSnn35248 if (try_exec(zpath, p, buf, isexec, isdata)) 143*9acbbeafSnn35248 goto found; 144*9acbbeafSnn35248 } 1457c478bd9Sstevel@tonic-gate } 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate /* 1487c478bd9Sstevel@tonic-gate * Third try: try using the first whitespace-separated token 1497c478bd9Sstevel@tonic-gate * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). 1507c478bd9Sstevel@tonic-gate */ 1517c478bd9Sstevel@tonic-gate if (Ppsinfo(P) != NULL) { 1527c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 1537c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 1567c478bd9Sstevel@tonic-gate *p = '\0'; 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1597c478bd9Sstevel@tonic-gate goto found; 1607c478bd9Sstevel@tonic-gate 16168dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 16268dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1637c478bd9Sstevel@tonic-gate goto found; 1647c478bd9Sstevel@tonic-gate } 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate /* 1677c478bd9Sstevel@tonic-gate * Fourth try: read the string pointed to by argv[0] out of the 1687c478bd9Sstevel@tonic-gate * stack in the process's address space. 1697c478bd9Sstevel@tonic-gate */ 1707c478bd9Sstevel@tonic-gate if (P->psinfo.pr_argv != NULL && 1717c478bd9Sstevel@tonic-gate Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && 1727c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1757c478bd9Sstevel@tonic-gate goto found; 1767c478bd9Sstevel@tonic-gate 17768dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 17868dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1797c478bd9Sstevel@tonic-gate goto found; 1807c478bd9Sstevel@tonic-gate } 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate /* 1837c478bd9Sstevel@tonic-gate * Fifth try: read the process's $PATH environment variable and 1847c478bd9Sstevel@tonic-gate * search each directory named there for the name matching pr_fname. 1857c478bd9Sstevel@tonic-gate */ 1867c478bd9Sstevel@tonic-gate if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { 1877c478bd9Sstevel@tonic-gate /* 1887c478bd9Sstevel@tonic-gate * If the name from pr_psargs contains pr_fname as its 1897c478bd9Sstevel@tonic-gate * leading string, then accept the name from pr_psargs 1907c478bd9Sstevel@tonic-gate * because more bytes are saved there. Otherwise use 1917c478bd9Sstevel@tonic-gate * pr_fname because this gives us new information. 1927c478bd9Sstevel@tonic-gate */ 1937c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 1947c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 1977c478bd9Sstevel@tonic-gate *p = '\0'; 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL || strncmp(path, 2007c478bd9Sstevel@tonic-gate P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) 2017c478bd9Sstevel@tonic-gate (void) strcpy(path, P->psinfo.pr_fname); 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate /* 2047c478bd9Sstevel@tonic-gate * Now iterate over the $PATH elements, trying to form 2057c478bd9Sstevel@tonic-gate * an executable pathname with each one. 2067c478bd9Sstevel@tonic-gate */ 2077c478bd9Sstevel@tonic-gate for (p = strtok_r(cwd, ":", &q); p != NULL; 2087c478bd9Sstevel@tonic-gate p = strtok_r(NULL, ":", &q)) { 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate if (*p != '/') 2117c478bd9Sstevel@tonic-gate continue; /* Ignore anything relative */ 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate if (try_exec(p, path, buf, isexec, isdata)) 2147c478bd9Sstevel@tonic-gate goto found; 2157c478bd9Sstevel@tonic-gate } 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate errno = ENOENT; 2197c478bd9Sstevel@tonic-gate return (NULL); 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate found: 2227c478bd9Sstevel@tonic-gate if ((P->execname = strdup(buf)) == NULL) 2237c478bd9Sstevel@tonic-gate dprintf("failed to malloc; executable name is \"%s\"", buf); 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate return (P->execname); 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate /* 2297c478bd9Sstevel@tonic-gate * Callback function for Pfindexec(). We return a match if we can stat the 2307c478bd9Sstevel@tonic-gate * suggested pathname and confirm its device and inode number match our 2317c478bd9Sstevel@tonic-gate * previous information about the /proc/<pid>/object/a.out file. 2327c478bd9Sstevel@tonic-gate */ 2337c478bd9Sstevel@tonic-gate static int 2347c478bd9Sstevel@tonic-gate stat_exec(const char *path, struct stat64 *stp) 2357c478bd9Sstevel@tonic-gate { 2367c478bd9Sstevel@tonic-gate struct stat64 st; 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && 2397c478bd9Sstevel@tonic-gate stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); 2407c478bd9Sstevel@tonic-gate } 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate /* 2437c478bd9Sstevel@tonic-gate * Return the full pathname for the executable file. If the process handle is 2447c478bd9Sstevel@tonic-gate * a core file, we've already tried our best to get the executable name. 2457c478bd9Sstevel@tonic-gate * Otherwise, we make an attempt using Pfindexec(). 2467c478bd9Sstevel@tonic-gate */ 2477c478bd9Sstevel@tonic-gate char * 2487c478bd9Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) 2497c478bd9Sstevel@tonic-gate { 2507c478bd9Sstevel@tonic-gate if (P->execname == NULL && P->state != PS_DEAD && P->state != PS_IDLE) { 2517c478bd9Sstevel@tonic-gate char exec_name[PATH_MAX]; 2527c478bd9Sstevel@tonic-gate char cwd[PATH_MAX]; 2537c478bd9Sstevel@tonic-gate char proc_cwd[64]; 2547c478bd9Sstevel@tonic-gate struct stat64 st; 2557c478bd9Sstevel@tonic-gate int ret; 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate /* 2587c478bd9Sstevel@tonic-gate * Try to get the path information first. 2597c478bd9Sstevel@tonic-gate */ 2607c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 261*9acbbeafSnn35248 "%s/%d/path/a.out", procfs_path, (int)P->pid); 2627c478bd9Sstevel@tonic-gate if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { 2637c478bd9Sstevel@tonic-gate buf[ret] = '\0'; 2647c478bd9Sstevel@tonic-gate return (buf); 2657c478bd9Sstevel@tonic-gate } 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate /* 2687c478bd9Sstevel@tonic-gate * Stat the executable file so we can compare Pfindexec's 2697c478bd9Sstevel@tonic-gate * suggestions to the actual device and inode number. 2707c478bd9Sstevel@tonic-gate */ 2717c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 272*9acbbeafSnn35248 "%s/%d/object/a.out", procfs_path, (int)P->pid); 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) 2757c478bd9Sstevel@tonic-gate return (NULL); 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate /* 2787c478bd9Sstevel@tonic-gate * Attempt to figure out the current working directory of the 2797c478bd9Sstevel@tonic-gate * target process. This only works if the target process has 2807c478bd9Sstevel@tonic-gate * not changed its current directory since it was exec'd. 2817c478bd9Sstevel@tonic-gate */ 2827c478bd9Sstevel@tonic-gate (void) snprintf(proc_cwd, sizeof (proc_cwd), 283*9acbbeafSnn35248 "%s/%d/path/cwd", procfs_path, (int)P->pid); 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) 2867c478bd9Sstevel@tonic-gate cwd[ret] = '\0'; 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate (void) Pfindexec(P, ret > 0 ? cwd : NULL, 2897c478bd9Sstevel@tonic-gate (int (*)(const char *, void *))stat_exec, &st); 2907c478bd9Sstevel@tonic-gate } 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate if (P->execname != NULL) { 2937c478bd9Sstevel@tonic-gate (void) strncpy(buf, P->execname, buflen); 2947c478bd9Sstevel@tonic-gate return (buf); 2957c478bd9Sstevel@tonic-gate } 2967c478bd9Sstevel@tonic-gate 2977c478bd9Sstevel@tonic-gate return (NULL); 2987c478bd9Sstevel@tonic-gate } 299