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 * Copyright (c) 2013 by Delphix. All rights reserved. 27 */ 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 #include <zone.h> 39 40 #include "libproc.h" 41 #include "Pcontrol.h" 42 43 /* 44 * Pexecname.c - Way too much code to attempt to derive the full pathname of 45 * the executable file from a process handle, be it dead or alive. 46 */ 47 48 /* 49 * Once we've computed a cwd and a relative path, we use try_exec() to 50 * form an absolute path, call resolvepath() on it, and then let the 51 * caller's function do the final confirmation. 52 */ 53 static int 54 try_exec(struct ps_prochandle *P, const char *cwd, const char *path, char *buf, 55 int (*isexec)(const char *, void *), void *isdata) 56 { 57 int i; 58 59 if (path[0] != '/') 60 (void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path); 61 else 62 (void) strcpy(buf, path); 63 64 dprintf("try_exec \"%s\"\n", buf); 65 66 (void) Pfindobj(P, buf, buf, PATH_MAX); 67 if ((i = resolvepath(buf, buf, PATH_MAX)) > 0) { 68 buf[i] = '\0'; 69 return (isexec(buf, isdata)); 70 } 71 72 return (0); /* resolvepath failed */ 73 } 74 75 /* 76 * The Pfindexec function contains the logic for the executable name dance. 77 * The caller provides a possible executable name or likely directory (the 78 * aout parameter), and a function which is responsible for doing any 79 * final confirmation on the executable pathname once a possible full 80 * pathname has been chosen. 81 */ 82 char * 83 Pfindexec(struct ps_prochandle *P, const char *aout, 84 int (*isexec)(const char *, void *), void *isdata) 85 { 86 char cwd[PATH_MAX * 2]; 87 char path[PATH_MAX]; 88 char buf[PATH_MAX]; 89 struct stat st; 90 uintptr_t addr; 91 char *p = path, *q; 92 93 dprintf("Pfindexec '%s'\n", aout); 94 95 if (P->execname) 96 return (P->execname); /* Already found */ 97 98 errno = 0; /* Set to zero so we can tell if stat() failed */ 99 100 /* 101 * First try: use the provided default value, if it is not a directory. 102 * If the aout parameter turns out to be a directory, this is 103 * interpreted as the directory to use as an alternate cwd for 104 * our subsequent attempts to locate the executable. 105 */ 106 if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) { 107 if (try_exec(P, ".", aout, buf, isexec, isdata)) 108 goto found; 109 else 110 aout = "."; 111 112 } else if (aout == NULL || errno != 0) 113 aout = "."; 114 115 /* 116 * At this point 'aout' is either "." or an alternate cwd. We use 117 * realpath(3c) to turn this into a full pathname free of ".", "..", 118 * and symlinks. If this fails for some reason, fall back to "." 119 */ 120 if (realpath(aout, cwd) == NULL) 121 (void) strcpy(cwd, "."); 122 123 /* 124 * Second try: read the string pointed to by the AT_SUN_EXECNAME 125 * auxv element, saved when the program was exec'd. If the full 126 * pathname try_exec() forms fails, try again using just the 127 * basename appended to our cwd. If that also fails, and the process 128 * is in a zone, try again with the zone path instead of our cwd. 129 */ 130 if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L && 131 Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 132 char zpath[PATH_MAX]; 133 const psinfo_t *pi = Ppsinfo(P); 134 135 if (try_exec(P, cwd, path, buf, isexec, isdata)) 136 goto found; 137 138 if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 139 try_exec(P, cwd, p, buf, isexec, isdata)) 140 goto found; 141 142 if (getzoneid() == GLOBAL_ZONEID && 143 pi->pr_zoneid != GLOBAL_ZONEID && 144 zone_getattr(pi->pr_zoneid, ZONE_ATTR_ROOT, zpath, 145 sizeof (zpath)) != -1) { 146 /* 147 * try_exec() only combines its cwd and path arguments 148 * if path is relative; but in our case even an absolute 149 * path inside a zone is a relative path from the global 150 * zone perspective. So we turn a non-global zone's 151 * absolute path into a relative path here before 152 * calling try_exec(). 153 */ 154 p = (path[0] == '/') ? path + 1 : path; 155 if (try_exec(P, zpath, p, buf, isexec, isdata)) 156 goto found; 157 } 158 } 159 160 /* 161 * Third try: try using the first whitespace-separated token 162 * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). 163 */ 164 if (Ppsinfo(P) != NULL) { 165 (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 166 path[PRARGSZ] = '\0'; 167 168 if ((p = strchr(path, ' ')) != NULL) 169 *p = '\0'; 170 171 if (try_exec(P, cwd, path, buf, isexec, isdata)) 172 goto found; 173 174 if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 175 try_exec(P, cwd, p, buf, isexec, isdata)) 176 goto found; 177 } 178 179 /* 180 * Fourth try: read the string pointed to by argv[0] out of the 181 * stack in the process's address space. 182 */ 183 if (P->psinfo.pr_argv != NULL && 184 Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && 185 Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 186 187 if (try_exec(P, cwd, path, buf, isexec, isdata)) 188 goto found; 189 190 if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 191 try_exec(P, cwd, p, buf, isexec, isdata)) 192 goto found; 193 } 194 195 /* 196 * Fifth try: read the process's $PATH environment variable and 197 * search each directory named there for the name matching pr_fname. 198 */ 199 if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { 200 /* 201 * If the name from pr_psargs contains pr_fname as its 202 * leading string, then accept the name from pr_psargs 203 * because more bytes are saved there. Otherwise use 204 * pr_fname because this gives us new information. 205 */ 206 (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 207 path[PRARGSZ] = '\0'; 208 209 if ((p = strchr(path, ' ')) != NULL) 210 *p = '\0'; 211 212 if (strchr(path, '/') != NULL || strncmp(path, 213 P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) 214 (void) strcpy(path, P->psinfo.pr_fname); 215 216 /* 217 * Now iterate over the $PATH elements, trying to form 218 * an executable pathname with each one. 219 */ 220 for (p = strtok_r(cwd, ":", &q); p != NULL; 221 p = strtok_r(NULL, ":", &q)) { 222 223 if (*p != '/') 224 continue; /* Ignore anything relative */ 225 226 if (try_exec(P, p, path, buf, isexec, isdata)) 227 goto found; 228 } 229 } 230 231 errno = ENOENT; 232 return (NULL); 233 234 found: 235 if ((P->execname = strdup(buf)) == NULL) 236 dprintf("failed to malloc; executable name is \"%s\"", buf); 237 238 return (P->execname); 239 } 240 241 /* 242 * Return the full pathname for the executable file. 243 */ 244 char * 245 Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) 246 { 247 if (P->execname != NULL) { 248 (void) strncpy(buf, P->execname, buflen); 249 return (buf); 250 } 251 252 return (P->ops.pop_execname(P, buf, buflen, P->data)); 253 } 254