Print this page
3946 ::gcore
Reviewed by: Adam Leventhal <ahl@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libproc/common/Pexecname.c
+++ new/usr/src/lib/libproc/common/Pexecname.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
↓ open down ↓ |
14 lines elided |
↑ open up ↑ |
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 +/*
26 + * Copyright (c) 2013 by Delphix. All rights reserved.
27 + */
25 28
26 29 #define __EXTENSIONS__
27 30 #include <string.h>
28 31 #undef __EXTENSIONS__
29 32
30 33 #include <libgen.h>
31 34 #include <limits.h>
32 35 #include <stdio.h>
33 36 #include <errno.h>
34 37 #include <unistd.h>
35 38 #include <zone.h>
36 39
37 40 #include "libproc.h"
38 41 #include "Pcontrol.h"
39 42
40 43 /*
41 44 * Pexecname.c - Way too much code to attempt to derive the full pathname of
42 45 * the executable file from a process handle, be it dead or alive.
43 46 */
44 47
45 48 /*
46 49 * Once we've computed a cwd and a relative path, we use try_exec() to
47 50 * form an absolute path, call resolvepath() on it, and then let the
48 51 * caller's function do the final confirmation.
49 52 */
50 53 static int
51 54 try_exec(struct ps_prochandle *P, const char *cwd, const char *path, char *buf,
52 55 int (*isexec)(const char *, void *), void *isdata)
53 56 {
54 57 int i;
55 58
56 59 if (path[0] != '/')
57 60 (void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path);
58 61 else
59 62 (void) strcpy(buf, path);
60 63
61 64 dprintf("try_exec \"%s\"\n", buf);
62 65
63 66 (void) Pfindobj(P, buf, buf, PATH_MAX);
64 67 if ((i = resolvepath(buf, buf, PATH_MAX)) > 0) {
65 68 buf[i] = '\0';
66 69 return (isexec(buf, isdata));
67 70 }
68 71
69 72 return (0); /* resolvepath failed */
70 73 }
71 74
72 75 /*
73 76 * The Pfindexec function contains the logic for the executable name dance.
74 77 * The caller provides a possible executable name or likely directory (the
75 78 * aout parameter), and a function which is responsible for doing any
76 79 * final confirmation on the executable pathname once a possible full
77 80 * pathname has been chosen.
78 81 */
79 82 char *
80 83 Pfindexec(struct ps_prochandle *P, const char *aout,
81 84 int (*isexec)(const char *, void *), void *isdata)
82 85 {
83 86 char cwd[PATH_MAX * 2];
84 87 char path[PATH_MAX];
85 88 char buf[PATH_MAX];
86 89 struct stat st;
87 90 uintptr_t addr;
88 91 char *p = path, *q;
89 92
90 93 dprintf("Pfindexec '%s'\n", aout);
91 94
92 95 if (P->execname)
93 96 return (P->execname); /* Already found */
94 97
95 98 errno = 0; /* Set to zero so we can tell if stat() failed */
96 99
97 100 /*
98 101 * First try: use the provided default value, if it is not a directory.
99 102 * If the aout parameter turns out to be a directory, this is
100 103 * interpreted as the directory to use as an alternate cwd for
101 104 * our subsequent attempts to locate the executable.
102 105 */
103 106 if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) {
104 107 if (try_exec(P, ".", aout, buf, isexec, isdata))
105 108 goto found;
106 109 else
107 110 aout = ".";
108 111
109 112 } else if (aout == NULL || errno != 0)
110 113 aout = ".";
111 114
112 115 /*
113 116 * At this point 'aout' is either "." or an alternate cwd. We use
114 117 * realpath(3c) to turn this into a full pathname free of ".", "..",
115 118 * and symlinks. If this fails for some reason, fall back to "."
116 119 */
117 120 if (realpath(aout, cwd) == NULL)
118 121 (void) strcpy(cwd, ".");
119 122
120 123 /*
121 124 * Second try: read the string pointed to by the AT_SUN_EXECNAME
122 125 * auxv element, saved when the program was exec'd. If the full
123 126 * pathname try_exec() forms fails, try again using just the
124 127 * basename appended to our cwd. If that also fails, and the process
125 128 * is in a zone, try again with the zone path instead of our cwd.
126 129 */
127 130 if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L &&
128 131 Pread_string(P, path, sizeof (path), (off_t)addr) > 0) {
129 132 char zpath[PATH_MAX];
130 133 const psinfo_t *pi = Ppsinfo(P);
131 134
132 135 if (try_exec(P, cwd, path, buf, isexec, isdata))
133 136 goto found;
134 137
135 138 if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
136 139 try_exec(P, cwd, p, buf, isexec, isdata))
137 140 goto found;
138 141
139 142 if (getzoneid() == GLOBAL_ZONEID &&
140 143 pi->pr_zoneid != GLOBAL_ZONEID &&
141 144 zone_getattr(pi->pr_zoneid, ZONE_ATTR_ROOT, zpath,
142 145 sizeof (zpath)) != -1) {
143 146 /*
144 147 * try_exec() only combines its cwd and path arguments
145 148 * if path is relative; but in our case even an absolute
146 149 * path inside a zone is a relative path from the global
147 150 * zone perspective. So we turn a non-global zone's
148 151 * absolute path into a relative path here before
149 152 * calling try_exec().
150 153 */
151 154 p = (path[0] == '/') ? path + 1 : path;
152 155 if (try_exec(P, zpath, p, buf, isexec, isdata))
153 156 goto found;
154 157 }
155 158 }
156 159
157 160 /*
158 161 * Third try: try using the first whitespace-separated token
159 162 * saved in the psinfo_t's pr_psargs (the initial value of argv[0]).
160 163 */
161 164 if (Ppsinfo(P) != NULL) {
162 165 (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ);
163 166 path[PRARGSZ] = '\0';
164 167
165 168 if ((p = strchr(path, ' ')) != NULL)
166 169 *p = '\0';
167 170
168 171 if (try_exec(P, cwd, path, buf, isexec, isdata))
169 172 goto found;
170 173
171 174 if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
172 175 try_exec(P, cwd, p, buf, isexec, isdata))
173 176 goto found;
174 177 }
175 178
176 179 /*
177 180 * Fourth try: read the string pointed to by argv[0] out of the
178 181 * stack in the process's address space.
179 182 */
180 183 if (P->psinfo.pr_argv != NULL &&
181 184 Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 &&
182 185 Pread_string(P, path, sizeof (path), (off_t)addr) > 0) {
183 186
184 187 if (try_exec(P, cwd, path, buf, isexec, isdata))
185 188 goto found;
186 189
187 190 if (strchr(path, '/') != NULL && (p = basename(path)) != NULL &&
188 191 try_exec(P, cwd, p, buf, isexec, isdata))
189 192 goto found;
190 193 }
191 194
192 195 /*
193 196 * Fifth try: read the process's $PATH environment variable and
194 197 * search each directory named there for the name matching pr_fname.
195 198 */
196 199 if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) {
197 200 /*
198 201 * If the name from pr_psargs contains pr_fname as its
199 202 * leading string, then accept the name from pr_psargs
200 203 * because more bytes are saved there. Otherwise use
201 204 * pr_fname because this gives us new information.
202 205 */
203 206 (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ);
204 207 path[PRARGSZ] = '\0';
205 208
206 209 if ((p = strchr(path, ' ')) != NULL)
207 210 *p = '\0';
208 211
209 212 if (strchr(path, '/') != NULL || strncmp(path,
210 213 P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0)
211 214 (void) strcpy(path, P->psinfo.pr_fname);
212 215
213 216 /*
214 217 * Now iterate over the $PATH elements, trying to form
215 218 * an executable pathname with each one.
216 219 */
217 220 for (p = strtok_r(cwd, ":", &q); p != NULL;
218 221 p = strtok_r(NULL, ":", &q)) {
219 222
220 223 if (*p != '/')
221 224 continue; /* Ignore anything relative */
222 225
223 226 if (try_exec(P, p, path, buf, isexec, isdata))
224 227 goto found;
225 228 }
226 229 }
227 230
228 231 errno = ENOENT;
↓ open down ↓ |
194 lines elided |
↑ open up ↑ |
229 232 return (NULL);
230 233
231 234 found:
232 235 if ((P->execname = strdup(buf)) == NULL)
233 236 dprintf("failed to malloc; executable name is \"%s\"", buf);
234 237
235 238 return (P->execname);
236 239 }
237 240
238 241 /*
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 + * Return the full pathname for the executable file.
242 243 */
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 244 char *
258 245 Pexecname(struct ps_prochandle *P, char *buf, size_t buflen)
259 246 {
260 247 if (P->execname != NULL) {
261 248 (void) strncpy(buf, P->execname, buflen);
262 249 return (buf);
263 250 }
264 251
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);
252 + return (P->ops.pop_execname(P, buf, buflen, P->data));
309 253 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX