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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright (c) 2013 by Delphix. All rights reserved.
29 #define __EXTENSIONS__
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.
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.
54 try_exec(struct ps_prochandle
*P
, const char *cwd
, const char *path
, char *buf
,
55 int (*isexec
)(const char *, void *), void *isdata
)
60 (void) snprintf(buf
, PATH_MAX
, "%s/%s", cwd
, path
);
62 (void) strcpy(buf
, path
);
64 dprintf("try_exec \"%s\"\n", buf
);
66 (void) Pfindobj(P
, buf
, buf
, PATH_MAX
);
67 if ((i
= resolvepath(buf
, buf
, PATH_MAX
)) > 0) {
69 return (isexec(buf
, isdata
));
72 return (0); /* resolvepath failed */
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.
83 Pfindexec(struct ps_prochandle
*P
, const char *aout
,
84 int (*isexec
)(const char *, void *), void *isdata
)
86 char cwd
[PATH_MAX
* 2];
93 dprintf("Pfindexec '%s'\n", aout
);
96 return (P
->execname
); /* Already found */
98 errno
= 0; /* Set to zero so we can tell if stat() failed */
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.
106 if (aout
!= NULL
&& stat(aout
, &st
) == 0 && !S_ISDIR(st
.st_mode
)) {
107 if (try_exec(P
, ".", aout
, buf
, isexec
, isdata
))
112 } else if (aout
== NULL
|| errno
!= 0)
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 "."
120 if (realpath(aout
, cwd
) == NULL
)
121 (void) strcpy(cwd
, ".");
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.
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
);
135 if (try_exec(P
, cwd
, path
, buf
, isexec
, isdata
))
138 if (strchr(path
, '/') != NULL
&& (p
= basename(path
)) != NULL
&&
139 try_exec(P
, cwd
, p
, buf
, isexec
, isdata
))
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) {
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().
154 p
= (path
[0] == '/') ? path
+ 1 : path
;
155 if (try_exec(P
, zpath
, p
, buf
, isexec
, isdata
))
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]).
164 if (Ppsinfo(P
) != NULL
) {
165 (void) strncpy(path
, P
->psinfo
.pr_psargs
, PRARGSZ
);
166 path
[PRARGSZ
] = '\0';
168 if ((p
= strchr(path
, ' ')) != NULL
)
171 if (try_exec(P
, cwd
, path
, buf
, isexec
, isdata
))
174 if (strchr(path
, '/') != NULL
&& (p
= basename(path
)) != NULL
&&
175 try_exec(P
, cwd
, p
, buf
, isexec
, isdata
))
180 * Fourth try: read the string pointed to by argv[0] out of the
181 * stack in the process's address space.
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) {
187 if (try_exec(P
, cwd
, path
, buf
, isexec
, isdata
))
190 if (strchr(path
, '/') != NULL
&& (p
= basename(path
)) != NULL
&&
191 try_exec(P
, cwd
, p
, buf
, isexec
, isdata
))
196 * Fifth try: read the process's $PATH environment variable and
197 * search each directory named there for the name matching pr_fname.
199 if (Pgetenv(P
, "PATH", cwd
, sizeof (cwd
)) != NULL
) {
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.
206 (void) strncpy(path
, P
->psinfo
.pr_psargs
, PRARGSZ
);
207 path
[PRARGSZ
] = '\0';
209 if ((p
= strchr(path
, ' ')) != NULL
)
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
);
217 * Now iterate over the $PATH elements, trying to form
218 * an executable pathname with each one.
220 for (p
= strtok_r(cwd
, ":", &q
); p
!= NULL
;
221 p
= strtok_r(NULL
, ":", &q
)) {
224 continue; /* Ignore anything relative */
226 if (try_exec(P
, p
, path
, buf
, isexec
, isdata
))
235 if ((P
->execname
= strdup(buf
)) == NULL
)
236 dprintf("failed to malloc; executable name is \"%s\"", buf
);
238 return (P
->execname
);
242 * Return the full pathname for the executable file.
245 Pexecname(struct ps_prochandle
*P
, char *buf
, size_t buflen
)
247 if (P
->execname
!= NULL
) {
248 (void) strncpy(buf
, P
->execname
, buflen
);
252 return (P
->ops
.pop_execname(P
, buf
, buflen
, P
->data
));