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