17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 568dd05bfSrab * Common Development and Distribution License (the "License"). 668dd05bfSrab * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22186f7fbfSEdward Pilatowicz * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 25*2a12f85aSJeremy Jones /* 26*2a12f85aSJeremy Jones * Copyright (c) 2013 by Delphix. All rights reserved. 27*2a12f85aSJeremy Jones */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #define __EXTENSIONS__ 307c478bd9Sstevel@tonic-gate #include <string.h> 317c478bd9Sstevel@tonic-gate #undef __EXTENSIONS__ 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate #include <libgen.h> 347c478bd9Sstevel@tonic-gate #include <limits.h> 357c478bd9Sstevel@tonic-gate #include <stdio.h> 367c478bd9Sstevel@tonic-gate #include <errno.h> 377c478bd9Sstevel@tonic-gate #include <unistd.h> 38c6e80875Srab #include <zone.h> 397c478bd9Sstevel@tonic-gate 40186f7fbfSEdward Pilatowicz #include "libproc.h" 417c478bd9Sstevel@tonic-gate #include "Pcontrol.h" 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate /* 447c478bd9Sstevel@tonic-gate * Pexecname.c - Way too much code to attempt to derive the full pathname of 457c478bd9Sstevel@tonic-gate * the executable file from a process handle, be it dead or alive. 467c478bd9Sstevel@tonic-gate */ 477c478bd9Sstevel@tonic-gate 487c478bd9Sstevel@tonic-gate /* 497c478bd9Sstevel@tonic-gate * Once we've computed a cwd and a relative path, we use try_exec() to 507c478bd9Sstevel@tonic-gate * form an absolute path, call resolvepath() on it, and then let the 517c478bd9Sstevel@tonic-gate * caller's function do the final confirmation. 527c478bd9Sstevel@tonic-gate */ 537c478bd9Sstevel@tonic-gate static int 54186f7fbfSEdward Pilatowicz try_exec(struct ps_prochandle *P, const char *cwd, const char *path, char *buf, 557c478bd9Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata) 567c478bd9Sstevel@tonic-gate { 577c478bd9Sstevel@tonic-gate int i; 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate if (path[0] != '/') 607c478bd9Sstevel@tonic-gate (void) snprintf(buf, PATH_MAX, "%s/%s", cwd, path); 617c478bd9Sstevel@tonic-gate else 627c478bd9Sstevel@tonic-gate (void) strcpy(buf, path); 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate dprintf("try_exec \"%s\"\n", buf); 657c478bd9Sstevel@tonic-gate 66186f7fbfSEdward Pilatowicz (void) Pfindobj(P, buf, buf, PATH_MAX); 677c478bd9Sstevel@tonic-gate if ((i = resolvepath(buf, buf, PATH_MAX)) > 0) { 687c478bd9Sstevel@tonic-gate buf[i] = '\0'; 697c478bd9Sstevel@tonic-gate return (isexec(buf, isdata)); 707c478bd9Sstevel@tonic-gate } 717c478bd9Sstevel@tonic-gate 727c478bd9Sstevel@tonic-gate return (0); /* resolvepath failed */ 737c478bd9Sstevel@tonic-gate } 747c478bd9Sstevel@tonic-gate 757c478bd9Sstevel@tonic-gate /* 767c478bd9Sstevel@tonic-gate * The Pfindexec function contains the logic for the executable name dance. 777c478bd9Sstevel@tonic-gate * The caller provides a possible executable name or likely directory (the 787c478bd9Sstevel@tonic-gate * aout parameter), and a function which is responsible for doing any 797c478bd9Sstevel@tonic-gate * final confirmation on the executable pathname once a possible full 807c478bd9Sstevel@tonic-gate * pathname has been chosen. 817c478bd9Sstevel@tonic-gate */ 827c478bd9Sstevel@tonic-gate char * 837c478bd9Sstevel@tonic-gate Pfindexec(struct ps_prochandle *P, const char *aout, 847c478bd9Sstevel@tonic-gate int (*isexec)(const char *, void *), void *isdata) 857c478bd9Sstevel@tonic-gate { 867c478bd9Sstevel@tonic-gate char cwd[PATH_MAX * 2]; 877c478bd9Sstevel@tonic-gate char path[PATH_MAX]; 887c478bd9Sstevel@tonic-gate char buf[PATH_MAX]; 897c478bd9Sstevel@tonic-gate struct stat st; 907c478bd9Sstevel@tonic-gate uintptr_t addr; 919acbbeafSnn35248 char *p = path, *q; 927c478bd9Sstevel@tonic-gate 93186f7fbfSEdward Pilatowicz dprintf("Pfindexec '%s'\n", aout); 94186f7fbfSEdward Pilatowicz 957c478bd9Sstevel@tonic-gate if (P->execname) 967c478bd9Sstevel@tonic-gate return (P->execname); /* Already found */ 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate errno = 0; /* Set to zero so we can tell if stat() failed */ 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate /* 1017c478bd9Sstevel@tonic-gate * First try: use the provided default value, if it is not a directory. 1027c478bd9Sstevel@tonic-gate * If the aout parameter turns out to be a directory, this is 1037c478bd9Sstevel@tonic-gate * interpreted as the directory to use as an alternate cwd for 1047c478bd9Sstevel@tonic-gate * our subsequent attempts to locate the executable. 1057c478bd9Sstevel@tonic-gate */ 1067c478bd9Sstevel@tonic-gate if (aout != NULL && stat(aout, &st) == 0 && !S_ISDIR(st.st_mode)) { 107186f7fbfSEdward Pilatowicz if (try_exec(P, ".", aout, buf, isexec, isdata)) 1087c478bd9Sstevel@tonic-gate goto found; 1097c478bd9Sstevel@tonic-gate else 1107c478bd9Sstevel@tonic-gate aout = "."; 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate } else if (aout == NULL || errno != 0) 1137c478bd9Sstevel@tonic-gate aout = "."; 1147c478bd9Sstevel@tonic-gate 1157c478bd9Sstevel@tonic-gate /* 1167c478bd9Sstevel@tonic-gate * At this point 'aout' is either "." or an alternate cwd. We use 1177c478bd9Sstevel@tonic-gate * realpath(3c) to turn this into a full pathname free of ".", "..", 1187c478bd9Sstevel@tonic-gate * and symlinks. If this fails for some reason, fall back to "." 1197c478bd9Sstevel@tonic-gate */ 1207c478bd9Sstevel@tonic-gate if (realpath(aout, cwd) == NULL) 1217c478bd9Sstevel@tonic-gate (void) strcpy(cwd, "."); 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate /* 1247c478bd9Sstevel@tonic-gate * Second try: read the string pointed to by the AT_SUN_EXECNAME 1257c478bd9Sstevel@tonic-gate * auxv element, saved when the program was exec'd. If the full 1267c478bd9Sstevel@tonic-gate * pathname try_exec() forms fails, try again using just the 1279acbbeafSnn35248 * basename appended to our cwd. If that also fails, and the process 1289acbbeafSnn35248 * is in a zone, try again with the zone path instead of our cwd. 1297c478bd9Sstevel@tonic-gate */ 1307c478bd9Sstevel@tonic-gate if ((addr = Pgetauxval(P, AT_SUN_EXECNAME)) != (uintptr_t)-1L && 1317c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1329acbbeafSnn35248 char zpath[PATH_MAX]; 1339acbbeafSnn35248 const psinfo_t *pi = Ppsinfo(P); 1347c478bd9Sstevel@tonic-gate 135186f7fbfSEdward Pilatowicz if (try_exec(P, cwd, path, buf, isexec, isdata)) 1367c478bd9Sstevel@tonic-gate goto found; 1377c478bd9Sstevel@tonic-gate 13868dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 139186f7fbfSEdward Pilatowicz try_exec(P, cwd, p, buf, isexec, isdata)) 1407c478bd9Sstevel@tonic-gate goto found; 1419acbbeafSnn35248 142c6e80875Srab if (getzoneid() == GLOBAL_ZONEID && 143c6e80875Srab pi->pr_zoneid != GLOBAL_ZONEID && 144c6e80875Srab zone_getattr(pi->pr_zoneid, ZONE_ATTR_ROOT, zpath, 145c6e80875Srab sizeof (zpath)) != -1) { 146c6e80875Srab /* 147c6e80875Srab * try_exec() only combines its cwd and path arguments 148c6e80875Srab * if path is relative; but in our case even an absolute 149c6e80875Srab * path inside a zone is a relative path from the global 150c6e80875Srab * zone perspective. So we turn a non-global zone's 151c6e80875Srab * absolute path into a relative path here before 152c6e80875Srab * calling try_exec(). 153c6e80875Srab */ 154c6e80875Srab p = (path[0] == '/') ? path + 1 : path; 155186f7fbfSEdward Pilatowicz if (try_exec(P, zpath, p, buf, isexec, isdata)) 1569acbbeafSnn35248 goto found; 1579acbbeafSnn35248 } 1587c478bd9Sstevel@tonic-gate } 1597c478bd9Sstevel@tonic-gate 1607c478bd9Sstevel@tonic-gate /* 1617c478bd9Sstevel@tonic-gate * Third try: try using the first whitespace-separated token 1627c478bd9Sstevel@tonic-gate * saved in the psinfo_t's pr_psargs (the initial value of argv[0]). 1637c478bd9Sstevel@tonic-gate */ 1647c478bd9Sstevel@tonic-gate if (Ppsinfo(P) != NULL) { 1657c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 1667c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 1697c478bd9Sstevel@tonic-gate *p = '\0'; 1707c478bd9Sstevel@tonic-gate 171186f7fbfSEdward Pilatowicz if (try_exec(P, cwd, path, buf, isexec, isdata)) 1727c478bd9Sstevel@tonic-gate goto found; 1737c478bd9Sstevel@tonic-gate 17468dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 175186f7fbfSEdward Pilatowicz try_exec(P, cwd, p, buf, isexec, isdata)) 1767c478bd9Sstevel@tonic-gate goto found; 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate /* 1807c478bd9Sstevel@tonic-gate * Fourth try: read the string pointed to by argv[0] out of the 1817c478bd9Sstevel@tonic-gate * stack in the process's address space. 1827c478bd9Sstevel@tonic-gate */ 1837c478bd9Sstevel@tonic-gate if (P->psinfo.pr_argv != NULL && 1847c478bd9Sstevel@tonic-gate Pread(P, &addr, sizeof (addr), P->psinfo.pr_argv) != -1 && 1857c478bd9Sstevel@tonic-gate Pread_string(P, path, sizeof (path), (off_t)addr) > 0) { 1867c478bd9Sstevel@tonic-gate 187186f7fbfSEdward Pilatowicz if (try_exec(P, cwd, path, buf, isexec, isdata)) 1887c478bd9Sstevel@tonic-gate goto found; 1897c478bd9Sstevel@tonic-gate 19068dd05bfSrab if (strchr(path, '/') != NULL && (p = basename(path)) != NULL && 191186f7fbfSEdward Pilatowicz try_exec(P, cwd, p, buf, isexec, isdata)) 1927c478bd9Sstevel@tonic-gate goto found; 1937c478bd9Sstevel@tonic-gate } 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate /* 1967c478bd9Sstevel@tonic-gate * Fifth try: read the process's $PATH environment variable and 1977c478bd9Sstevel@tonic-gate * search each directory named there for the name matching pr_fname. 1987c478bd9Sstevel@tonic-gate */ 1997c478bd9Sstevel@tonic-gate if (Pgetenv(P, "PATH", cwd, sizeof (cwd)) != NULL) { 2007c478bd9Sstevel@tonic-gate /* 2017c478bd9Sstevel@tonic-gate * If the name from pr_psargs contains pr_fname as its 2027c478bd9Sstevel@tonic-gate * leading string, then accept the name from pr_psargs 2037c478bd9Sstevel@tonic-gate * because more bytes are saved there. Otherwise use 2047c478bd9Sstevel@tonic-gate * pr_fname because this gives us new information. 2057c478bd9Sstevel@tonic-gate */ 2067c478bd9Sstevel@tonic-gate (void) strncpy(path, P->psinfo.pr_psargs, PRARGSZ); 2077c478bd9Sstevel@tonic-gate path[PRARGSZ] = '\0'; 2087c478bd9Sstevel@tonic-gate 2097c478bd9Sstevel@tonic-gate if ((p = strchr(path, ' ')) != NULL) 2107c478bd9Sstevel@tonic-gate *p = '\0'; 2117c478bd9Sstevel@tonic-gate 2127c478bd9Sstevel@tonic-gate if (strchr(path, '/') != NULL || strncmp(path, 2137c478bd9Sstevel@tonic-gate P->psinfo.pr_fname, strlen(P->psinfo.pr_fname)) != 0) 2147c478bd9Sstevel@tonic-gate (void) strcpy(path, P->psinfo.pr_fname); 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate /* 2177c478bd9Sstevel@tonic-gate * Now iterate over the $PATH elements, trying to form 2187c478bd9Sstevel@tonic-gate * an executable pathname with each one. 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate for (p = strtok_r(cwd, ":", &q); p != NULL; 2217c478bd9Sstevel@tonic-gate p = strtok_r(NULL, ":", &q)) { 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate if (*p != '/') 2247c478bd9Sstevel@tonic-gate continue; /* Ignore anything relative */ 2257c478bd9Sstevel@tonic-gate 226186f7fbfSEdward Pilatowicz if (try_exec(P, p, path, buf, isexec, isdata)) 2277c478bd9Sstevel@tonic-gate goto found; 2287c478bd9Sstevel@tonic-gate } 2297c478bd9Sstevel@tonic-gate } 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate errno = ENOENT; 2327c478bd9Sstevel@tonic-gate return (NULL); 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate found: 2357c478bd9Sstevel@tonic-gate if ((P->execname = strdup(buf)) == NULL) 2367c478bd9Sstevel@tonic-gate dprintf("failed to malloc; executable name is \"%s\"", buf); 2377c478bd9Sstevel@tonic-gate 2387c478bd9Sstevel@tonic-gate return (P->execname); 2397c478bd9Sstevel@tonic-gate } 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate /* 242*2a12f85aSJeremy Jones * Return the full pathname for the executable file. 2437c478bd9Sstevel@tonic-gate */ 2447c478bd9Sstevel@tonic-gate char * 2457c478bd9Sstevel@tonic-gate Pexecname(struct ps_prochandle *P, char *buf, size_t buflen) 2467c478bd9Sstevel@tonic-gate { 247186f7fbfSEdward Pilatowicz if (P->execname != NULL) { 248186f7fbfSEdward Pilatowicz (void) strncpy(buf, P->execname, buflen); 249186f7fbfSEdward Pilatowicz return (buf); 250186f7fbfSEdward Pilatowicz } 251186f7fbfSEdward Pilatowicz 252*2a12f85aSJeremy Jones return (P->ops.pop_execname(P, buf, buflen, P->data)); 2537c478bd9Sstevel@tonic-gate } 254