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 5*68dd05bfSrab * Common Development and Distribution License (the "License"). 6*68dd05bfSrab * 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*68dd05bfSrab * 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> 377c478bd9Sstevel@tonic-gate 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 517c478bd9Sstevel@tonic-gate try_exec(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 637c478bd9Sstevel@tonic-gate if ((i = resolvepath(buf, buf, PATH_MAX)) > 0) { 647c478bd9Sstevel@tonic-gate buf[i] = '\0'; 657c478bd9Sstevel@tonic-gate return (isexec(buf, isdata)); 667c478bd9Sstevel@tonic-gate } 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate return (0); /* resolvepath failed */ 697c478bd9Sstevel@tonic-gate } 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* 727c478bd9Sstevel@tonic-gate * The Pfindexec function contains the logic for the executable name dance. 737c478bd9Sstevel@tonic-gate * The caller provides a possible executable name or likely directory (the 747c478bd9Sstevel@tonic-gate * aout parameter), and a function which is responsible for doing any 757c478bd9Sstevel@tonic-gate * final confirmation on the executable pathname once a possible full 767c478bd9Sstevel@tonic-gate * pathname has been chosen. 777c478bd9Sstevel@tonic-gate */ 787c478bd9Sstevel@tonic-gate char * 797c478bd9Sstevel@tonic-gate Pfindexec(struct ps_prochandle *P, const char *aout, 807c478bd9Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata) 817c478bd9Sstevel@tonic-gate { 827c478bd9Sstevel@tonic-gate char cwd[PATH_MAX * 2]; 837c478bd9Sstevel@tonic-gate char path[PATH_MAX]; 847c478bd9Sstevel@tonic-gate char buf[PATH_MAX]; 857c478bd9Sstevel@tonic-gate struct stat st; 867c478bd9Sstevel@tonic-gate uintptr_t addr; 877c478bd9Sstevel@tonic-gate char *p, *q; 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate if (P->execname) 907c478bd9Sstevel@tonic-gate return (P->execname); /* Already found */ 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate errno = 0; /* Set to zero so we can tell if stat() failed */ 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate /* 957c478bd9Sstevel@tonic-gate * First try: use the provided default value, if it is not a directory. 967c478bd9Sstevel@tonic-gate * If the aout parameter turns out to be a directory, this is 977c478bd9Sstevel@tonic-gate * interpreted as the directory to use as an alternate cwd for 987c478bd9Sstevel@tonic-gate * our subsequent attempts to locate the executable. 997c478bd9Sstevel@tonic-gate */ 1007c478bd9Sstevel@tonic-gate if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) { 1017c478bd9Sstevel@tonic-gate if (try_exec(".", aout, buf, isexec, isdata)) 1027c478bd9Sstevel@tonic-gate goto found; 1037c478bd9Sstevel@tonic-gate else 1047c478bd9Sstevel@tonic-gate aout = "."; 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate } else if (aout == NULL || errno != 0) 1077c478bd9Sstevel@tonic-gate aout = "."; 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate /* 1107c478bd9Sstevel@tonic-gate * At this point 'aout' is either "." or an alternate cwd. We use 1117c478bd9Sstevel@tonic-gate * realpath(3c) to turn this into a full pathname free of ".", "..", 1127c478bd9Sstevel@tonic-gate * and symlinks. If this fails for some reason, fall back to "." 1137c478bd9Sstevel@tonic-gate */ 1147c478bd9Sstevel@tonic-gate if (realpath(aout, cwd) == NULL) 1157c478bd9Sstevel@tonic-gate (void) strcpy(cwd, "."); 1167c478bd9Sstevel@tonic-gate 1177c478bd9Sstevel@tonic-gate /* 1187c478bd9Sstevel@tonic-gate * Second try: read the string pointed to by the AT_SUN_EXECNAME 1197c478bd9Sstevel@tonic-gate * auxv element, saved when the program was exec'd. If the full 1207c478bd9Sstevel@tonic-gate * pathname try_exec() forms fails, try again using just the 1217c478bd9Sstevel@tonic-gate * basename appended to our cwd. 1227c478bd9Sstevel@tonic-gate */ 1237c478bd9Sstevel@tonic-gate if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L && 1247c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1277c478bd9Sstevel@tonic-gate goto found; 1287c478bd9Sstevel@tonic-gate 129*68dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 130*68dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1317c478bd9Sstevel@tonic-gate goto found; 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate /* 1357c478bd9Sstevel@tonic-gate * Third try: try using the first whitespace-separated token 1367c478bd9Sstevel@tonic-gate * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). 1377c478bd9Sstevel@tonic-gate */ 1387c478bd9Sstevel@tonic-gate if (Ppsinfo(P) != NULL) { 1397c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 1407c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 1437c478bd9Sstevel@tonic-gate *p = '\0'; 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1467c478bd9Sstevel@tonic-gate goto found; 1477c478bd9Sstevel@tonic-gate 148*68dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 149*68dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1507c478bd9Sstevel@tonic-gate goto found; 1517c478bd9Sstevel@tonic-gate } 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate /* 1547c478bd9Sstevel@tonic-gate * Fourth try: read the string pointed to by argv[0] out of the 1557c478bd9Sstevel@tonic-gate * stack in the process's address space. 1567c478bd9Sstevel@tonic-gate */ 1577c478bd9Sstevel@tonic-gate if (P->psinfo.pr_argv != NULL && 1587c478bd9Sstevel@tonic-gate Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && 1597c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 1627c478bd9Sstevel@tonic-gate goto found; 1637c478bd9Sstevel@tonic-gate 164*68dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 165*68dd05bfSrab try_exec(cwd, p, buf, isexec, isdata)) 1667c478bd9Sstevel@tonic-gate goto found; 1677c478bd9Sstevel@tonic-gate } 1687c478bd9Sstevel@tonic-gate 1697c478bd9Sstevel@tonic-gate /* 1707c478bd9Sstevel@tonic-gate * Fifth try: read the process's $PATH environment variable and 1717c478bd9Sstevel@tonic-gate * search each directory named there for the name matching pr_fname. 1727c478bd9Sstevel@tonic-gate */ 1737c478bd9Sstevel@tonic-gate if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { 1747c478bd9Sstevel@tonic-gate /* 1757c478bd9Sstevel@tonic-gate * If the name from pr_psargs contains pr_fname as its 1767c478bd9Sstevel@tonic-gate * leading string, then accept the name from pr_psargs 1777c478bd9Sstevel@tonic-gate * because more bytes are saved there. Otherwise use 1787c478bd9Sstevel@tonic-gate * pr_fname because this gives us new information. 1797c478bd9Sstevel@tonic-gate */ 1807c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 1817c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 1847c478bd9Sstevel@tonic-gate *p = '\0'; 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL || strncmp(path, 1877c478bd9Sstevel@tonic-gate P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) 1887c478bd9Sstevel@tonic-gate (void) strcpy(path, P->psinfo.pr_fname); 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * Now iterate over the $PATH elements, trying to form 1927c478bd9Sstevel@tonic-gate * an executable pathname with each one. 1937c478bd9Sstevel@tonic-gate */ 1947c478bd9Sstevel@tonic-gate for (p = strtok_r(cwd, ":", &q); p != NULL; 1957c478bd9Sstevel@tonic-gate p = strtok_r(NULL, ":", &q)) { 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate if (*p != '/') 1987c478bd9Sstevel@tonic-gate continue; /* Ignore anything relative */ 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate if (try_exec(p, path, buf, isexec, isdata)) 2017c478bd9Sstevel@tonic-gate goto found; 2027c478bd9Sstevel@tonic-gate } 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate errno = ENOENT; 2067c478bd9Sstevel@tonic-gate return (NULL); 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate found: 2097c478bd9Sstevel@tonic-gate if ((P->execname = strdup(buf)) == NULL) 2107c478bd9Sstevel@tonic-gate dprintf("failed to malloc; executable name is \"%s\"", buf); 2117c478bd9Sstevel@tonic-gate 2127c478bd9Sstevel@tonic-gate return (P->execname); 2137c478bd9Sstevel@tonic-gate } 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate /* 2167c478bd9Sstevel@tonic-gate * Callback function for Pfindexec(). We return a match if we can stat the 2177c478bd9Sstevel@tonic-gate * suggested pathname and confirm its device and inode number match our 2187c478bd9Sstevel@tonic-gate * previous information about the /proc/<pid>/object/a.out file. 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate static int 2217c478bd9Sstevel@tonic-gate stat_exec(const char *path, struct stat64 *stp) 2227c478bd9Sstevel@tonic-gate { 2237c478bd9Sstevel@tonic-gate struct stat64 st; 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && 2267c478bd9Sstevel@tonic-gate stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); 2277c478bd9Sstevel@tonic-gate } 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate /* 2307c478bd9Sstevel@tonic-gate * Return the full pathname for the executable file. If the process handle is 2317c478bd9Sstevel@tonic-gate * a core file, we've already tried our best to get the executable name. 2327c478bd9Sstevel@tonic-gate * Otherwise, we make an attempt using Pfindexec(). 2337c478bd9Sstevel@tonic-gate */ 2347c478bd9Sstevel@tonic-gate char * 2357c478bd9Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) 2367c478bd9Sstevel@tonic-gate { 2377c478bd9Sstevel@tonic-gate if (P->execname == NULL && P->state != PS_DEAD && P->state != PS_IDLE) { 2387c478bd9Sstevel@tonic-gate char exec_name[PATH_MAX]; 2397c478bd9Sstevel@tonic-gate char cwd[PATH_MAX]; 2407c478bd9Sstevel@tonic-gate char proc_cwd[64]; 2417c478bd9Sstevel@tonic-gate struct stat64 st; 2427c478bd9Sstevel@tonic-gate int ret; 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate /* 2457c478bd9Sstevel@tonic-gate * Try to get the path information first. 2467c478bd9Sstevel@tonic-gate */ 2477c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 2487c478bd9Sstevel@tonic-gate "/proc/%d/path/a.out", (int)P->pid); 2497c478bd9Sstevel@tonic-gate if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { 2507c478bd9Sstevel@tonic-gate buf[ret] = '\0'; 2517c478bd9Sstevel@tonic-gate return (buf); 2527c478bd9Sstevel@tonic-gate } 2537c478bd9Sstevel@tonic-gate 2547c478bd9Sstevel@tonic-gate /* 2557c478bd9Sstevel@tonic-gate * Stat the executable file so we can compare Pfindexec's 2567c478bd9Sstevel@tonic-gate * suggestions to the actual device and inode number. 2577c478bd9Sstevel@tonic-gate */ 2587c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 2597c478bd9Sstevel@tonic-gate "/proc/%d/object/a.out", (int)P->pid); 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) 2627c478bd9Sstevel@tonic-gate return (NULL); 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate /* 2657c478bd9Sstevel@tonic-gate * Attempt to figure out the current working directory of the 2667c478bd9Sstevel@tonic-gate * target process. This only works if the target process has 2677c478bd9Sstevel@tonic-gate * not changed its current directory since it was exec'd. 2687c478bd9Sstevel@tonic-gate */ 2697c478bd9Sstevel@tonic-gate (void) snprintf(proc_cwd, sizeof (proc_cwd), 2707c478bd9Sstevel@tonic-gate "/proc/%d/path/cwd", (int)P->pid); 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) 2737c478bd9Sstevel@tonic-gate cwd[ret] = '\0'; 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate (void) Pfindexec(P, ret > 0 ? cwd : NULL, 2767c478bd9Sstevel@tonic-gate (int (*)(const char *, void *))stat_exec, &st); 2777c478bd9Sstevel@tonic-gate } 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate if (P->execname != NULL) { 2807c478bd9Sstevel@tonic-gate (void) strncpy(buf, P->execname, buflen); 2817c478bd9Sstevel@tonic-gate return (buf); 2827c478bd9Sstevel@tonic-gate } 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate return (NULL); 2857c478bd9Sstevel@tonic-gate } 286