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