1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #define __EXTENSIONS__ 30*7c478bd9Sstevel@tonic-gate #include <string.h> 31*7c478bd9Sstevel@tonic-gate #undef __EXTENSIONS__ 32*7c478bd9Sstevel@tonic-gate 33*7c478bd9Sstevel@tonic-gate #include <libgen.h> 34*7c478bd9Sstevel@tonic-gate #include <limits.h> 35*7c478bd9Sstevel@tonic-gate #include <stdio.h> 36*7c478bd9Sstevel@tonic-gate #include <errno.h> 37*7c478bd9Sstevel@tonic-gate #include <unistd.h> 38*7c478bd9Sstevel@tonic-gate 39*7c478bd9Sstevel@tonic-gate #include "Pcontrol.h" 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate /* 42*7c478bd9Sstevel@tonic-gate * Pexecname.c - Way too much code to attempt to derive the full pathname of 43*7c478bd9Sstevel@tonic-gate * the executable file from a process handle, be it dead or alive. 44*7c478bd9Sstevel@tonic-gate */ 45*7c478bd9Sstevel@tonic-gate 46*7c478bd9Sstevel@tonic-gate /* 47*7c478bd9Sstevel@tonic-gate * Once we've computed a cwd and a relative path, we use try_exec() to 48*7c478bd9Sstevel@tonic-gate * form an absolute path, call resolvepath() on it, and then let the 49*7c478bd9Sstevel@tonic-gate * caller's function do the final confirmation. 50*7c478bd9Sstevel@tonic-gate */ 51*7c478bd9Sstevel@tonic-gate static int 52*7c478bd9Sstevel@tonic-gate try_exec(const char *cwd, const char *path, char *buf, 53*7c478bd9Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata) 54*7c478bd9Sstevel@tonic-gate { 55*7c478bd9Sstevel@tonic-gate int i; 56*7c478bd9Sstevel@tonic-gate 57*7c478bd9Sstevel@tonic-gate if (path[0] != '/') 58*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path); 59*7c478bd9Sstevel@tonic-gate else 60*7c478bd9Sstevel@tonic-gate (void) strcpy(buf, path); 61*7c478bd9Sstevel@tonic-gate 62*7c478bd9Sstevel@tonic-gate dprintf("try_exec \"%s\"\n", buf); 63*7c478bd9Sstevel@tonic-gate 64*7c478bd9Sstevel@tonic-gate if ((i = resolvepath(buf, buf, PATH_MAX)) > 0) { 65*7c478bd9Sstevel@tonic-gate buf[i] = '\0'; 66*7c478bd9Sstevel@tonic-gate return (isexec(buf, isdata)); 67*7c478bd9Sstevel@tonic-gate } 68*7c478bd9Sstevel@tonic-gate 69*7c478bd9Sstevel@tonic-gate return (0); /* resolvepath failed */ 70*7c478bd9Sstevel@tonic-gate } 71*7c478bd9Sstevel@tonic-gate 72*7c478bd9Sstevel@tonic-gate /* 73*7c478bd9Sstevel@tonic-gate * The Pfindexec function contains the logic for the executable name dance. 74*7c478bd9Sstevel@tonic-gate * The caller provides a possible executable name or likely directory (the 75*7c478bd9Sstevel@tonic-gate * aout parameter), and a function which is responsible for doing any 76*7c478bd9Sstevel@tonic-gate * final confirmation on the executable pathname once a possible full 77*7c478bd9Sstevel@tonic-gate * pathname has been chosen. 78*7c478bd9Sstevel@tonic-gate */ 79*7c478bd9Sstevel@tonic-gate char * 80*7c478bd9Sstevel@tonic-gate Pfindexec(struct ps_prochandle *P, const char *aout, 81*7c478bd9Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata) 82*7c478bd9Sstevel@tonic-gate { 83*7c478bd9Sstevel@tonic-gate char cwd[PATH_MAX * 2]; 84*7c478bd9Sstevel@tonic-gate char path[PATH_MAX]; 85*7c478bd9Sstevel@tonic-gate char buf[PATH_MAX]; 86*7c478bd9Sstevel@tonic-gate struct stat st; 87*7c478bd9Sstevel@tonic-gate uintptr_t addr; 88*7c478bd9Sstevel@tonic-gate char *p, *q; 89*7c478bd9Sstevel@tonic-gate 90*7c478bd9Sstevel@tonic-gate if (P->execname) 91*7c478bd9Sstevel@tonic-gate return (P->execname); /* Already found */ 92*7c478bd9Sstevel@tonic-gate 93*7c478bd9Sstevel@tonic-gate errno = 0; /* Set to zero so we can tell if stat() failed */ 94*7c478bd9Sstevel@tonic-gate 95*7c478bd9Sstevel@tonic-gate /* 96*7c478bd9Sstevel@tonic-gate * First try: use the provided default value, if it is not a directory. 97*7c478bd9Sstevel@tonic-gate * If the aout parameter turns out to be a directory, this is 98*7c478bd9Sstevel@tonic-gate * interpreted as the directory to use as an alternate cwd for 99*7c478bd9Sstevel@tonic-gate * our subsequent attempts to locate the executable. 100*7c478bd9Sstevel@tonic-gate */ 101*7c478bd9Sstevel@tonic-gate if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) { 102*7c478bd9Sstevel@tonic-gate if (try_exec(".", aout, buf, isexec, isdata)) 103*7c478bd9Sstevel@tonic-gate goto found; 104*7c478bd9Sstevel@tonic-gate else 105*7c478bd9Sstevel@tonic-gate aout = "."; 106*7c478bd9Sstevel@tonic-gate 107*7c478bd9Sstevel@tonic-gate } else if (aout == NULL || errno != 0) 108*7c478bd9Sstevel@tonic-gate aout = "."; 109*7c478bd9Sstevel@tonic-gate 110*7c478bd9Sstevel@tonic-gate /* 111*7c478bd9Sstevel@tonic-gate * At this point 'aout' is either "." or an alternate cwd. We use 112*7c478bd9Sstevel@tonic-gate * realpath(3c) to turn this into a full pathname free of ".", "..", 113*7c478bd9Sstevel@tonic-gate * and symlinks. If this fails for some reason, fall back to "." 114*7c478bd9Sstevel@tonic-gate */ 115*7c478bd9Sstevel@tonic-gate if (realpath(aout, cwd) == NULL) 116*7c478bd9Sstevel@tonic-gate (void) strcpy(cwd, "."); 117*7c478bd9Sstevel@tonic-gate 118*7c478bd9Sstevel@tonic-gate /* 119*7c478bd9Sstevel@tonic-gate * Second try: read the string pointed to by the AT_SUN_EXECNAME 120*7c478bd9Sstevel@tonic-gate * auxv element, saved when the program was exec'd. If the full 121*7c478bd9Sstevel@tonic-gate * pathname try_exec() forms fails, try again using just the 122*7c478bd9Sstevel@tonic-gate * basename appended to our cwd. 123*7c478bd9Sstevel@tonic-gate */ 124*7c478bd9Sstevel@tonic-gate if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L && 125*7c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 126*7c478bd9Sstevel@tonic-gate 127*7c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 128*7c478bd9Sstevel@tonic-gate goto found; 129*7c478bd9Sstevel@tonic-gate 130*7c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL && basename(path) != NULL && 131*7c478bd9Sstevel@tonic-gate try_exec(cwd, path, buf, isexec, isdata)) 132*7c478bd9Sstevel@tonic-gate goto found; 133*7c478bd9Sstevel@tonic-gate } 134*7c478bd9Sstevel@tonic-gate 135*7c478bd9Sstevel@tonic-gate /* 136*7c478bd9Sstevel@tonic-gate * Third try: try using the first whitespace-separated token 137*7c478bd9Sstevel@tonic-gate * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). 138*7c478bd9Sstevel@tonic-gate */ 139*7c478bd9Sstevel@tonic-gate if (Ppsinfo(P) != NULL) { 140*7c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 141*7c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 142*7c478bd9Sstevel@tonic-gate 143*7c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 144*7c478bd9Sstevel@tonic-gate *p = '\0'; 145*7c478bd9Sstevel@tonic-gate 146*7c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 147*7c478bd9Sstevel@tonic-gate goto found; 148*7c478bd9Sstevel@tonic-gate 149*7c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL && basename(path) != NULL && 150*7c478bd9Sstevel@tonic-gate try_exec(cwd, path, buf, isexec, isdata)) 151*7c478bd9Sstevel@tonic-gate goto found; 152*7c478bd9Sstevel@tonic-gate } 153*7c478bd9Sstevel@tonic-gate 154*7c478bd9Sstevel@tonic-gate /* 155*7c478bd9Sstevel@tonic-gate * Fourth try: read the string pointed to by argv[0] out of the 156*7c478bd9Sstevel@tonic-gate * stack in the process's address space. 157*7c478bd9Sstevel@tonic-gate */ 158*7c478bd9Sstevel@tonic-gate if (P->psinfo.pr_argv != NULL && 159*7c478bd9Sstevel@tonic-gate Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && 160*7c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 161*7c478bd9Sstevel@tonic-gate 162*7c478bd9Sstevel@tonic-gate if (try_exec(cwd, path, buf, isexec, isdata)) 163*7c478bd9Sstevel@tonic-gate goto found; 164*7c478bd9Sstevel@tonic-gate 165*7c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL && basename(path) != NULL && 166*7c478bd9Sstevel@tonic-gate try_exec(cwd, path, buf, isexec, isdata)) 167*7c478bd9Sstevel@tonic-gate goto found; 168*7c478bd9Sstevel@tonic-gate } 169*7c478bd9Sstevel@tonic-gate 170*7c478bd9Sstevel@tonic-gate /* 171*7c478bd9Sstevel@tonic-gate * Fifth try: read the process's $PATH environment variable and 172*7c478bd9Sstevel@tonic-gate * search each directory named there for the name matching pr_fname. 173*7c478bd9Sstevel@tonic-gate */ 174*7c478bd9Sstevel@tonic-gate if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { 175*7c478bd9Sstevel@tonic-gate /* 176*7c478bd9Sstevel@tonic-gate * If the name from pr_psargs contains pr_fname as its 177*7c478bd9Sstevel@tonic-gate * leading string, then accept the name from pr_psargs 178*7c478bd9Sstevel@tonic-gate * because more bytes are saved there. Otherwise use 179*7c478bd9Sstevel@tonic-gate * pr_fname because this gives us new information. 180*7c478bd9Sstevel@tonic-gate */ 181*7c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 182*7c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 183*7c478bd9Sstevel@tonic-gate 184*7c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 185*7c478bd9Sstevel@tonic-gate *p = '\0'; 186*7c478bd9Sstevel@tonic-gate 187*7c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL || strncmp(path, 188*7c478bd9Sstevel@tonic-gate P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) 189*7c478bd9Sstevel@tonic-gate (void) strcpy(path, P->psinfo.pr_fname); 190*7c478bd9Sstevel@tonic-gate 191*7c478bd9Sstevel@tonic-gate /* 192*7c478bd9Sstevel@tonic-gate * Now iterate over the $PATH elements, trying to form 193*7c478bd9Sstevel@tonic-gate * an executable pathname with each one. 194*7c478bd9Sstevel@tonic-gate */ 195*7c478bd9Sstevel@tonic-gate for (p = strtok_r(cwd, ":", &q); p != NULL; 196*7c478bd9Sstevel@tonic-gate p = strtok_r(NULL, ":", &q)) { 197*7c478bd9Sstevel@tonic-gate 198*7c478bd9Sstevel@tonic-gate if (*p != '/') 199*7c478bd9Sstevel@tonic-gate continue; /* Ignore anything relative */ 200*7c478bd9Sstevel@tonic-gate 201*7c478bd9Sstevel@tonic-gate if (try_exec(p, path, buf, isexec, isdata)) 202*7c478bd9Sstevel@tonic-gate goto found; 203*7c478bd9Sstevel@tonic-gate } 204*7c478bd9Sstevel@tonic-gate } 205*7c478bd9Sstevel@tonic-gate 206*7c478bd9Sstevel@tonic-gate errno = ENOENT; 207*7c478bd9Sstevel@tonic-gate return (NULL); 208*7c478bd9Sstevel@tonic-gate 209*7c478bd9Sstevel@tonic-gate found: 210*7c478bd9Sstevel@tonic-gate if ((P->execname = strdup(buf)) == NULL) 211*7c478bd9Sstevel@tonic-gate dprintf("failed to malloc; executable name is \"%s\"", buf); 212*7c478bd9Sstevel@tonic-gate 213*7c478bd9Sstevel@tonic-gate return (P->execname); 214*7c478bd9Sstevel@tonic-gate } 215*7c478bd9Sstevel@tonic-gate 216*7c478bd9Sstevel@tonic-gate /* 217*7c478bd9Sstevel@tonic-gate * Callback function for Pfindexec(). We return a match if we can stat the 218*7c478bd9Sstevel@tonic-gate * suggested pathname and confirm its device and inode number match our 219*7c478bd9Sstevel@tonic-gate * previous information about the /proc/<pid>/object/a.out file. 220*7c478bd9Sstevel@tonic-gate */ 221*7c478bd9Sstevel@tonic-gate static int 222*7c478bd9Sstevel@tonic-gate stat_exec(const char *path, struct stat64 *stp) 223*7c478bd9Sstevel@tonic-gate { 224*7c478bd9Sstevel@tonic-gate struct stat64 st; 225*7c478bd9Sstevel@tonic-gate 226*7c478bd9Sstevel@tonic-gate return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) && 227*7c478bd9Sstevel@tonic-gate stp->st_dev == st.st_dev && stp->st_ino == st.st_ino); 228*7c478bd9Sstevel@tonic-gate } 229*7c478bd9Sstevel@tonic-gate 230*7c478bd9Sstevel@tonic-gate /* 231*7c478bd9Sstevel@tonic-gate * Return the full pathname for the executable file. If the process handle is 232*7c478bd9Sstevel@tonic-gate * a core file, we've already tried our best to get the executable name. 233*7c478bd9Sstevel@tonic-gate * Otherwise, we make an attempt using Pfindexec(). 234*7c478bd9Sstevel@tonic-gate */ 235*7c478bd9Sstevel@tonic-gate char * 236*7c478bd9Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) 237*7c478bd9Sstevel@tonic-gate { 238*7c478bd9Sstevel@tonic-gate if (P->execname == NULL && P->state != PS_DEAD && P->state != PS_IDLE) { 239*7c478bd9Sstevel@tonic-gate char exec_name[PATH_MAX]; 240*7c478bd9Sstevel@tonic-gate char cwd[PATH_MAX]; 241*7c478bd9Sstevel@tonic-gate char proc_cwd[64]; 242*7c478bd9Sstevel@tonic-gate struct stat64 st; 243*7c478bd9Sstevel@tonic-gate int ret; 244*7c478bd9Sstevel@tonic-gate 245*7c478bd9Sstevel@tonic-gate /* 246*7c478bd9Sstevel@tonic-gate * Try to get the path information first. 247*7c478bd9Sstevel@tonic-gate */ 248*7c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 249*7c478bd9Sstevel@tonic-gate "/proc/%d/path/a.out", (int)P->pid); 250*7c478bd9Sstevel@tonic-gate if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) { 251*7c478bd9Sstevel@tonic-gate buf[ret] = '\0'; 252*7c478bd9Sstevel@tonic-gate return (buf); 253*7c478bd9Sstevel@tonic-gate } 254*7c478bd9Sstevel@tonic-gate 255*7c478bd9Sstevel@tonic-gate /* 256*7c478bd9Sstevel@tonic-gate * Stat the executable file so we can compare Pfindexec's 257*7c478bd9Sstevel@tonic-gate * suggestions to the actual device and inode number. 258*7c478bd9Sstevel@tonic-gate */ 259*7c478bd9Sstevel@tonic-gate (void) snprintf(exec_name, sizeof (exec_name), 260*7c478bd9Sstevel@tonic-gate "/proc/%d/object/a.out", (int)P->pid); 261*7c478bd9Sstevel@tonic-gate 262*7c478bd9Sstevel@tonic-gate if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode)) 263*7c478bd9Sstevel@tonic-gate return (NULL); 264*7c478bd9Sstevel@tonic-gate 265*7c478bd9Sstevel@tonic-gate /* 266*7c478bd9Sstevel@tonic-gate * Attempt to figure out the current working directory of the 267*7c478bd9Sstevel@tonic-gate * target process. This only works if the target process has 268*7c478bd9Sstevel@tonic-gate * not changed its current directory since it was exec'd. 269*7c478bd9Sstevel@tonic-gate */ 270*7c478bd9Sstevel@tonic-gate (void) snprintf(proc_cwd, sizeof (proc_cwd), 271*7c478bd9Sstevel@tonic-gate "/proc/%d/path/cwd", (int)P->pid); 272*7c478bd9Sstevel@tonic-gate 273*7c478bd9Sstevel@tonic-gate if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0) 274*7c478bd9Sstevel@tonic-gate cwd[ret] = '\0'; 275*7c478bd9Sstevel@tonic-gate 276*7c478bd9Sstevel@tonic-gate (void) Pfindexec(P, ret > 0 ? cwd : NULL, 277*7c478bd9Sstevel@tonic-gate (int (*)(const char *, void *))stat_exec, &st); 278*7c478bd9Sstevel@tonic-gate } 279*7c478bd9Sstevel@tonic-gate 280*7c478bd9Sstevel@tonic-gate if (P->execname != NULL) { 281*7c478bd9Sstevel@tonic-gate (void) strncpy(buf, P->execname, buflen); 282*7c478bd9Sstevel@tonic-gate return (buf); 283*7c478bd9Sstevel@tonic-gate } 284*7c478bd9Sstevel@tonic-gate 285*7c478bd9Sstevel@tonic-gate return (NULL); 286*7c478bd9Sstevel@tonic-gate } 287