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
59acbbeafSnn  * Common Development and Distribution License (the "License").
69acbbeafSnn  * 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  */
227c478bd9Sstevel@tonic-gate /*
238fd04b83SRoger A. Faulkner  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25cd11e192Sjhaslam  *
26cd11e192Sjhaslam  * Portions Copyright 2007 Chad Mynhier
2734bdffbfSGarrett D'Amore  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
282a12f85aSJeremy Jones  * Copyright (c) 2013 by Delphix. All rights reserved.
2943051d27SRobert Mustacchi  * Copyright 2015, Joyent, Inc.
30a02120c4SAndy Fiddaman  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
3150d4d24eSRobert Mustacchi  * Copyright 2021 Oxide Computer Company
327c478bd9Sstevel@tonic-gate  */
34d9452f23SEdward Pilatowicz #include <assert.h>
357c478bd9Sstevel@tonic-gate #include <stdio.h>
367c478bd9Sstevel@tonic-gate #include <stdlib.h>
377c478bd9Sstevel@tonic-gate #include <unistd.h>
387c478bd9Sstevel@tonic-gate #include <ctype.h>
397c478bd9Sstevel@tonic-gate #include <fcntl.h>
407c478bd9Sstevel@tonic-gate #include <string.h>
419acbbeafSnn #include <strings.h>
427c478bd9Sstevel@tonic-gate #include <memory.h>
437c478bd9Sstevel@tonic-gate #include <errno.h>
447c478bd9Sstevel@tonic-gate #include <dirent.h>
457c478bd9Sstevel@tonic-gate #include <limits.h>
467c478bd9Sstevel@tonic-gate #include <signal.h>
47cb620785Sraf #include <atomic.h>
482a12f85aSJeremy Jones #include <zone.h>
497c478bd9Sstevel@tonic-gate #include <sys/types.h>
507c478bd9Sstevel@tonic-gate #include <sys/uio.h>
517c478bd9Sstevel@tonic-gate #include <sys/stat.h>
527c478bd9Sstevel@tonic-gate #include <sys/resource.h>
537c478bd9Sstevel@tonic-gate #include <sys/param.h>
547c478bd9Sstevel@tonic-gate #include <sys/stack.h>
557c478bd9Sstevel@tonic-gate #include <sys/fault.h>
567c478bd9Sstevel@tonic-gate #include <sys/syscall.h>
577c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
582a12f85aSJeremy Jones #include <sys/systeminfo.h>
59d2a70789SRichard Lowe #include <sys/secflags.h>
617c478bd9Sstevel@tonic-gate #include "libproc.h"
627c478bd9Sstevel@tonic-gate #include "Pcontrol.h"
637c478bd9Sstevel@tonic-gate #include "Putil.h"
647c478bd9Sstevel@tonic-gate #include "P32ton.h"
667c478bd9Sstevel@tonic-gate int	_libproc_debug;		/* set non-zero to enable debugging printfs */
67d7755b5aSrh int	_libproc_no_qsort;	/* set non-zero to inhibit sorting */
68d7755b5aSrh 				/* of symbol tables */
69d9452f23SEdward Pilatowicz int	_libproc_incore_elf;	/* only use in-core elf data */
717c478bd9Sstevel@tonic-gate sigset_t blockable_sigs;	/* signals to block when we need to be safe */
727c478bd9Sstevel@tonic-gate static	int	minfd;	/* minimum file descriptor returned by dupfd(fd, 0) */
739acbbeafSnn char	procfs_path[PATH_MAX] = "/proc";
757c478bd9Sstevel@tonic-gate /*
767c478bd9Sstevel@tonic-gate  * Function prototypes for static routines in this module.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate static	void	deadcheck(struct ps_prochandle *);
797c478bd9Sstevel@tonic-gate static	void	restore_tracing_flags(struct ps_prochandle *);
807c478bd9Sstevel@tonic-gate static	void	Lfree_internal(struct ps_prochandle *, struct ps_lwphandle *);
812a12f85aSJeremy Jones static  prheader_t *read_lfile(struct ps_prochandle *, const char *);
837c478bd9Sstevel@tonic-gate /*
842a12f85aSJeremy Jones  * Ops vector functions for live processes.
857c478bd9Sstevel@tonic-gate  */
872a12f85aSJeremy Jones /*ARGSUSED*/
887c478bd9Sstevel@tonic-gate static ssize_t
Pread_live(struct ps_prochandle * P,void * buf,size_t n,uintptr_t addr,void * data)892a12f85aSJeremy Jones Pread_live(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr,
902a12f85aSJeremy Jones     void *data)
917c478bd9Sstevel@tonic-gate {
927c478bd9Sstevel@tonic-gate 	return (pread(P->asfd, buf, n, (off_t)addr));
937c478bd9Sstevel@tonic-gate }
952a12f85aSJeremy Jones /*ARGSUSED*/
967c478bd9Sstevel@tonic-gate static ssize_t
Pwrite_live(struct ps_prochandle * P,const void * buf,size_t n,uintptr_t addr,void * data)972a12f85aSJeremy Jones Pwrite_live(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr,
982a12f85aSJeremy Jones     void *data)
997c478bd9Sstevel@tonic-gate {
1007c478bd9Sstevel@tonic-gate 	return (pwrite(P->asfd, buf, n, (off_t)addr));
1017c478bd9Sstevel@tonic-gate }
1032a12f85aSJeremy Jones /*ARGSUSED*/
1042a12f85aSJeremy Jones static int
Pread_maps_live(struct ps_prochandle * P,prmap_t ** Pmapp,ssize_t * nmapp,void * data)1052a12f85aSJeremy Jones Pread_maps_live(struct ps_prochandle *P, prmap_t **Pmapp, ssize_t *nmapp,
1062a12f85aSJeremy Jones     void *data)
1072a12f85aSJeremy Jones {
1082a12f85aSJeremy Jones 	char mapfile[PATH_MAX];
1092a12f85aSJeremy Jones 	int mapfd;
1102a12f85aSJeremy Jones 	struct stat statb;
1112a12f85aSJeremy Jones 	ssize_t nmap;
1122a12f85aSJeremy Jones 	prmap_t *Pmap = NULL;
1132a12f85aSJeremy Jones 
1142a12f85aSJeremy Jones 	(void) snprintf(mapfile, sizeof (mapfile), "%s/%d/map",
1152a12f85aSJeremy Jones 	    procfs_path, (int)P->pid);
1162a12f85aSJeremy Jones 	if ((mapfd = open(mapfile, O_RDONLY)) < 0 ||
1172a12f85aSJeremy Jones 	    fstat(mapfd, &statb) != 0 ||
1182a12f85aSJeremy Jones 	    statb.st_size < sizeof (prmap_t) ||
1192a12f85aSJeremy Jones 	    (Pmap = malloc(statb.st_size)) == NULL ||
1202a12f85aSJeremy Jones 	    (nmap = pread(mapfd, Pmap, statb.st_size, 0L)) <= 0 ||
1212a12f85aSJeremy Jones 	    (nmap /= sizeof (prmap_t)) == 0) {
1222a12f85aSJeremy Jones 		if (Pmap != NULL)
1232a12f85aSJeremy Jones 			free(Pmap);
1242a12f85aSJeremy Jones 		if (mapfd >= 0)
1252a12f85aSJeremy Jones 			(void) close(mapfd);
1262a12f85aSJeremy Jones 		Preset_maps(P); /* utter failure; destroy tables */
1272a12f85aSJeremy Jones 		return (-1);
1282a12f85aSJeremy Jones 	}
1292a12f85aSJeremy Jones 	(void) close(mapfd);
1302a12f85aSJeremy Jones 
1312a12f85aSJeremy Jones 	*Pmapp = Pmap;
1322a12f85aSJeremy Jones 	*nmapp = nmap;
1332a12f85aSJeremy Jones 
1342a12f85aSJeremy Jones 	return (0);
1352a12f85aSJeremy Jones }
1362a12f85aSJeremy Jones 
1372a12f85aSJeremy Jones /*ARGSUSED*/
1382a12f85aSJeremy Jones static void
Pread_aux_live(struct ps_prochandle * P,auxv_t ** auxvp,int * nauxp,void * data)1392a12f85aSJeremy Jones Pread_aux_live(struct ps_prochandle *P, auxv_t **auxvp, int *nauxp, void *data)
1402a12f85aSJeremy Jones {
1412a12f85aSJeremy Jones 	char auxfile[64];
1422a12f85aSJeremy Jones 	int fd;
1432a12f85aSJeremy Jones 	struct stat statb;
1442a12f85aSJeremy Jones 	auxv_t *auxv;
1452a12f85aSJeremy Jones 	ssize_t naux;
1462a12f85aSJeremy Jones 
1472a12f85aSJeremy Jones 	(void) snprintf(auxfile, sizeof (auxfile), "%s/%d/auxv",
1482a12f85aSJeremy Jones 	    procfs_path, (int)P->pid);
1492a12f85aSJeremy Jones 	if ((fd = open(auxfile, O_RDONLY)) < 0) {
1502a12f85aSJeremy Jones 		dprintf("%s: failed to open %s: %s\n",
1512a12f85aSJeremy Jones 		    __func__, auxfile, strerror(errno));
1522a12f85aSJeremy Jones 		return;
1532a12f85aSJeremy Jones 	}
1542a12f85aSJeremy Jones 
1552a12f85aSJeremy Jones 	if (fstat(fd, &statb) == 0 &&
1562a12f85aSJeremy Jones 	    statb.st_size >= sizeof (auxv_t) &&
1572a12f85aSJeremy Jones 	    (auxv = malloc(statb.st_size + sizeof (auxv_t))) != NULL) {
1582a12f85aSJeremy Jones 		if ((naux = read(fd, auxv, statb.st_size)) < 0 ||
1592a12f85aSJeremy Jones 		    (naux /= sizeof (auxv_t)) < 1) {
1602a12f85aSJeremy Jones 			dprintf("%s: read failed: %s\n",
1612a12f85aSJeremy Jones 			    __func__, strerror(errno));
1622a12f85aSJeremy Jones 			free(auxv);
1632a12f85aSJeremy Jones 		} else {
1642a12f85aSJeremy Jones 			auxv[naux].a_type = AT_NULL;
1652a12f85aSJeremy Jones 			auxv[naux].a_un.a_val = 0L;
1662a12f85aSJeremy Jones 
1672a12f85aSJeremy Jones 			*auxvp = auxv;
1682a12f85aSJeremy Jones 			*nauxp = (int)naux;
1692a12f85aSJeremy Jones 		}
1702a12f85aSJeremy Jones 	}
1712a12f85aSJeremy Jones 
1722a12f85aSJeremy Jones 	(void) close(fd);
1732a12f85aSJeremy Jones }
1742a12f85aSJeremy Jones 
1752a12f85aSJeremy Jones /*ARGSUSED*/
1762a12f85aSJeremy Jones static int
Pcred_live(struct ps_prochandle * P,prcred_t * pcrp,int ngroups,void * data)1772a12f85aSJeremy Jones Pcred_live(struct ps_prochandle *P, prcred_t *pcrp, int ngroups, void *data)
1782a12f85aSJeremy Jones {
1792a12f85aSJeremy Jones 	return (proc_get_cred(P->pid, pcrp, ngroups));
1802a12f85aSJeremy Jones }
1812a12f85aSJeremy Jones 
182d2a70789SRichard Lowe /* ARGSUSED */
183d2a70789SRichard Lowe static int
Psecflags_live(struct ps_prochandle * P,prsecflags_t ** psf,void * data)184d2a70789SRichard Lowe Psecflags_live(struct ps_prochandle *P, prsecflags_t **psf, void *data)
185d2a70789SRichard Lowe {
186d2a70789SRichard Lowe 	return (proc_get_secflags(P->pid, psf));
187d2a70789SRichard Lowe }
188d2a70789SRichard Lowe 
1892a12f85aSJeremy Jones /*ARGSUSED*/
1902a12f85aSJeremy Jones static int
Ppriv_live(struct ps_prochandle * P,prpriv_t ** pprv,void * data)1912a12f85aSJeremy Jones Ppriv_live(struct ps_prochandle *P, prpriv_t **pprv, void *data)
1922a12f85aSJeremy Jones {
1932a12f85aSJeremy Jones 	prpriv_t *pp;
1942a12f85aSJeremy Jones 
1952a12f85aSJeremy Jones 	pp = proc_get_priv(P->pid);
1962a12f85aSJeremy Jones 	if (pp == NULL) {
1972a12f85aSJeremy Jones 		return (-1);
1982a12f85aSJeremy Jones 	}
1992a12f85aSJeremy Jones 
2002a12f85aSJeremy Jones 	*pprv = pp;
2012a12f85aSJeremy Jones 	return (0);
2022a12f85aSJeremy Jones }
2032a12f85aSJeremy Jones 
2042a12f85aSJeremy Jones /*ARGSUSED*/
2052a12f85aSJeremy Jones static const psinfo_t *
Ppsinfo_live(struct ps_prochandle * P,psinfo_t * psinfo,void * data)2062a12f85aSJeremy Jones Ppsinfo_live(struct ps_prochandle *P, psinfo_t *psinfo, void *data)
2072a12f85aSJeremy Jones {
2082a12f85aSJeremy Jones 	if (proc_get_psinfo(P->pid, psinfo) == -1)
2092a12f85aSJeremy Jones 		return (NULL);
2102a12f85aSJeremy Jones 
2112a12f85aSJeremy Jones 	return (psinfo);
2122a12f85aSJeremy Jones }
2132a12f85aSJeremy Jones 
2142a12f85aSJeremy Jones /*ARGSUSED*/
2152a12f85aSJeremy Jones static prheader_t *
Plstatus_live(struct ps_prochandle * P,void * data)2162a12f85aSJeremy Jones Plstatus_live(struct ps_prochandle *P, void *data)
2172a12f85aSJeremy Jones {
2182a12f85aSJeremy Jones 	return (read_lfile(P, "lstatus"));
2192a12f85aSJeremy Jones }
2202a12f85aSJeremy Jones 
2212a12f85aSJeremy Jones /*ARGSUSED*/
2222a12f85aSJeremy Jones static prheader_t *
Plpsinfo_live(struct ps_prochandle * P,void * data)2232a12f85aSJeremy Jones Plpsinfo_live(struct ps_prochandle *P, void *data)
2242a12f85aSJeremy Jones {
2252a12f85aSJeremy Jones 	return (read_lfile(P, "lpsinfo"));
2262a12f85aSJeremy Jones }
2272a12f85aSJeremy Jones 
2282a12f85aSJeremy Jones /*ARGSUSED*/
2292a12f85aSJeremy Jones static char *
Pplatform_live(struct ps_prochandle * P,char * s,size_t n,void * data)2302a12f85aSJeremy Jones Pplatform_live(struct ps_prochandle *P, char *s, size_t n, void *data)
2312a12f85aSJeremy Jones {
2322a12f85aSJeremy Jones 	if (sysinfo(SI_PLATFORM, s, n) == -1)
2332a12f85aSJeremy Jones 		return (NULL);
2342a12f85aSJeremy Jones 	return (s);
2352a12f85aSJeremy Jones }
2362a12f85aSJeremy Jones 
2372a12f85aSJeremy Jones /*ARGSUSED*/
2382a12f85aSJeremy Jones static int
Puname_live(struct ps_prochandle * P,struct utsname * u,void * data)2392a12f85aSJeremy Jones Puname_live(struct ps_prochandle *P, struct utsname *u, void *data)
2402a12f85aSJeremy Jones {
2412a12f85aSJeremy Jones 	return (uname(u));
2422a12f85aSJeremy Jones }
2432a12f85aSJeremy Jones 
2442a12f85aSJeremy Jones /*ARGSUSED*/
2452a12f85aSJeremy Jones static char *
Pzonename_live(struct ps_prochandle * P,char * s,size_t n,void * data)2462a12f85aSJeremy Jones Pzonename_live(struct ps_prochandle *P, char *s, size_t n, void *data)
2472a12f85aSJeremy Jones {
2482a12f85aSJeremy Jones 	if (getzonenamebyid(P->status.pr_zoneid, s, n) < 0)
2492a12f85aSJeremy Jones 		return (NULL);
2502a12f85aSJeremy Jones 	s[n - 1] = '\0';
2512a12f85aSJeremy Jones 	return (s);
2522a12f85aSJeremy Jones }
2532a12f85aSJeremy Jones 
2542a12f85aSJeremy Jones /*
2552a12f85aSJeremy Jones  * Callback function for Pfindexec().  We return a match if we can stat the
2562a12f85aSJeremy Jones  * suggested pathname and confirm its device and inode number match our
2572a12f85aSJeremy Jones  * previous information about the /proc/<pid>/object/a.out file.
2582a12f85aSJeremy Jones  */
2592a12f85aSJeremy Jones static int
stat_exec(const char * path,void * arg)2602a12f85aSJeremy Jones stat_exec(const char *path, void *arg)
2612a12f85aSJeremy Jones {
2622a12f85aSJeremy Jones 	struct stat64 *stp = arg;
2632a12f85aSJeremy Jones 	struct stat64 st;
2642a12f85aSJeremy Jones 
2652a12f85aSJeremy Jones 	return (stat64(path, &st) == 0 && S_ISREG(st.st_mode) &&
2662a12f85aSJeremy Jones 	    stp->st_dev == st.st_dev && stp->st_ino == st.st_ino);
2672a12f85aSJeremy Jones }
2682a12f85aSJeremy Jones 
2692a12f85aSJeremy Jones /*ARGSUSED*/
2702a12f85aSJeremy Jones static char *
Pexecname_live(struct ps_prochandle * P,char * buf,size_t buflen,void * data)2712a12f85aSJeremy Jones Pexecname_live(struct ps_prochandle *P, char *buf, size_t buflen, void *data)
2722a12f85aSJeremy Jones {
2732a12f85aSJeremy Jones 	char exec_name[PATH_MAX];
2742a12f85aSJeremy Jones 	char cwd[PATH_MAX];
2752a12f85aSJeremy Jones 	char proc_cwd[64];
2762a12f85aSJeremy Jones 	struct stat64 st;
2772a12f85aSJeremy Jones 	int ret;
2782a12f85aSJeremy Jones 
2792a12f85aSJeremy Jones 	/*
2802a12f85aSJeremy Jones 	 * Try to get the path information first.
2812a12f85aSJeremy Jones 	 */
2822a12f85aSJeremy Jones 	(void) snprintf(exec_name, sizeof (exec_name),
2832a12f85aSJeremy Jones 	    "%s/%d/path/a.out", procfs_path, (int)P->pid);
2842a12f85aSJeremy Jones 	if ((ret = readlink(exec_name, buf, buflen - 1)) > 0) {
2852a12f85aSJeremy Jones 		buf[ret] = '\0';
2862a12f85aSJeremy Jones 		(void) Pfindobj(P, buf, buf, buflen);
2872a12f85aSJeremy Jones 		return (buf);
2882a12f85aSJeremy Jones 	}
2892a12f85aSJeremy Jones 
2902a12f85aSJeremy Jones 	/*
2912a12f85aSJeremy Jones 	 * Stat the executable file so we can compare Pfindexec's
2922a12f85aSJeremy Jones 	 * suggestions to the actual device and inode number.
2932a12f85aSJeremy Jones 	 */
2942a12f85aSJeremy Jones 	(void) snprintf(exec_name, sizeof (exec_name),
2952a12f85aSJeremy Jones 	    "%s/%d/object/a.out", procfs_path, (int)P->pid);
2962a12f85aSJeremy Jones 
2972a12f85aSJeremy Jones 	if (stat64(exec_name, &st) != 0 || !S_ISREG(st.st_mode))
2982a12f85aSJeremy Jones 		return (NULL);
2992a12f85aSJeremy Jones 
3002a12f85aSJeremy Jones 	/*
3012a12f85aSJeremy Jones 	 * Attempt to figure out the current working directory of the
3022a12f85aSJeremy Jones 	 * target process.  This only works if the target process has
3032a12f85aSJeremy Jones 	 * not changed its current directory since it was exec'd.
3042a12f85aSJeremy Jones 	 */
3052a12f85aSJeremy Jones 	(void) snprintf(proc_cwd, sizeof (proc_cwd),
3062a12f85aSJeremy Jones 	    "%s/%d/path/cwd", procfs_path, (int)P->pid);
3072a12f85aSJeremy Jones 
3082a12f85aSJeremy Jones 	if ((ret = readlink(proc_cwd, cwd, PATH_MAX - 1)) > 0)
3092a12f85aSJeremy Jones 		cwd[ret] = '\0';
3102a12f85aSJeremy Jones 
3112a12f85aSJeremy Jones 	(void) Pfindexec(P, ret > 0 ? cwd : NULL, stat_exec, &st);
3122a12f85aSJeremy Jones 
3132a12f85aSJeremy Jones 	return (NULL);
3142a12f85aSJeremy Jones }
3152a12f85aSJeremy Jones 
3162a12f85aSJeremy Jones #if defined(__i386) || defined(__amd64)
3172a12f85aSJeremy Jones /*ARGSUSED*/
3182a12f85aSJeremy Jones static int
Pldt_live(struct ps_prochandle * P,struct ssd * pldt,int nldt,void * data)3192a12f85aSJeremy Jones Pldt_live(struct ps_prochandle *P, struct ssd *pldt, int nldt, void *data)
3202a12f85aSJeremy Jones {
3212a12f85aSJeremy Jones 	return (proc_get_ldt(P->pid, pldt, nldt));
3222a12f85aSJeremy Jones }
3232a12f85aSJeremy Jones #endif
3242a12f85aSJeremy Jones 
3252a12f85aSJeremy Jones static const ps_ops_t P_live_ops = {
3262a12f85aSJeremy Jones 	.pop_pread	= Pread_live,
3272a12f85aSJeremy Jones 	.pop_pwrite	= Pwrite_live,
3282a12f85aSJeremy Jones 	.pop_read_maps	= Pread_maps_live,
3292a12f85aSJeremy Jones 	.pop_read_aux	= Pread_aux_live,
3302a12f85aSJeremy Jones 	.pop_cred	= Pcred_live,
3312a12f85aSJeremy Jones 	.pop_priv	= Ppriv_live,
3322a12f85aSJeremy Jones 	.pop_psinfo	= Ppsinfo_live,
3332a12f85aSJeremy Jones 	.pop_lstatus	= Plstatus_live,
3342a12f85aSJeremy Jones 	.pop_lpsinfo	= Plpsinfo_live,
3352a12f85aSJeremy Jones 	.pop_platform	= Pplatform_live,
3362a12f85aSJeremy Jones 	.pop_uname	= Puname_live,
3372a12f85aSJeremy Jones 	.pop_zonename	= Pzonename_live,
3382a12f85aSJeremy Jones 	.pop_execname	= Pexecname_live,
339d2a70789SRichard Lowe 	.pop_secflags	= Psecflags_live,
3402a12f85aSJeremy Jones #if defined(__i386) || defined(__amd64)
3412a12f85aSJeremy Jones 	.pop_ldt	= Pldt_live
3422a12f85aSJeremy Jones #endif
3432a12f85aSJeremy Jones };
3457c478bd9Sstevel@tonic-gate /*
3467c478bd9Sstevel@tonic-gate  * This is the library's .init handler.
3477c478bd9Sstevel@tonic-gate  */
3487c478bd9Sstevel@tonic-gate #pragma init(_libproc_init)
3497c478bd9Sstevel@tonic-gate void
_libproc_init(void)3507c478bd9Sstevel@tonic-gate _libproc_init(void)
3517c478bd9Sstevel@tonic-gate {
3527c478bd9Sstevel@tonic-gate 	_libproc_debug = getenv("LIBPROC_DEBUG") != NULL;
353d7755b5aSrh 	_libproc_no_qsort = getenv("LIBPROC_NO_QSORT") != NULL;
354d9452f23SEdward Pilatowicz 	_libproc_incore_elf = getenv("LIBPROC_INCORE_ELF") != NULL;
3567c478bd9Sstevel@tonic-gate 	(void) sigfillset(&blockable_sigs);
3577c478bd9Sstevel@tonic-gate 	(void) sigdelset(&blockable_sigs, SIGKILL);
3587c478bd9Sstevel@tonic-gate 	(void) sigdelset(&blockable_sigs, SIGSTOP);
3597c478bd9Sstevel@tonic-gate }
3619acbbeafSnn void
Pset_procfs_path(const char * path)3629acbbeafSnn Pset_procfs_path(const char *path)
3639acbbeafSnn {
3649acbbeafSnn 	(void) snprintf(procfs_path, sizeof (procfs_path), "%s", path);
3659acbbeafSnn }
3677c478bd9Sstevel@tonic-gate /*
3687c478bd9Sstevel@tonic-gate  * Call set_minfd() once before calling dupfd() several times.
3697c478bd9Sstevel@tonic-gate  * We assume that the application will not reduce its current file
3707c478bd9Sstevel@tonic-gate  * descriptor limit lower than 512 once it has set at least that value.
3717c478bd9Sstevel@tonic-gate  */
3727c478bd9Sstevel@tonic-gate int
set_minfd(void)3737c478bd9Sstevel@tonic-gate set_minfd(void)
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate 	static mutex_t minfd_lock = DEFAULTMUTEX;
3767c478bd9Sstevel@tonic-gate 	struct rlimit rlim;
3777c478bd9Sstevel@tonic-gate 	int fd;
3797c478bd9Sstevel@tonic-gate 	if ((fd = minfd) < 256) {
3807c478bd9Sstevel@tonic-gate 		(void) mutex_lock(&minfd_lock);
3817c478bd9Sstevel@tonic-gate 		if ((fd = minfd) < 256) {
3827c478bd9Sstevel@tonic-gate 			if (getrlimit(RLIMIT_NOFILE, &rlim) != 0)
3837c478bd9Sstevel@tonic-gate 				rlim.rlim_cur = rlim.rlim_max = 0;
3847c478bd9Sstevel@tonic-gate 			if (rlim.rlim_cur >= 512)
3857c478bd9Sstevel@tonic-gate 				fd = 256;
3867c478bd9Sstevel@tonic-gate 			else if ((fd = rlim.rlim_cur / 2) < 3)
3877c478bd9Sstevel@tonic-gate 				fd = 3;
388cb620785Sraf 			membar_producer();
3897c478bd9Sstevel@tonic-gate 			minfd = fd;
3907c478bd9Sstevel@tonic-gate 		}
3917c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(&minfd_lock);
3927c478bd9Sstevel@tonic-gate 	}
3937c478bd9Sstevel@tonic-gate 	return (fd);
3947c478bd9Sstevel@tonic-gate }
3967c478bd9Sstevel@tonic-gate int
dupfd(int fd,int dfd)3977c478bd9Sstevel@tonic-gate dupfd(int fd, int dfd)
3987c478bd9Sstevel@tonic-gate {
3997c478bd9Sstevel@tonic-gate 	int mfd;
4017c478bd9Sstevel@tonic-gate 	/*
4027c478bd9Sstevel@tonic-gate 	 * Make fd be greater than 255 (the 32-bit stdio limit),
4037c478bd9Sstevel@tonic-gate 	 * or at least make it greater than 2 so that the
404*bbf21555SRichard Lowe 	 * program will work when spawned by init(8).
4057c478bd9Sstevel@tonic-gate 	 * Also, if dfd is non-zero, dup the fd to be dfd.
4067c478bd9Sstevel@tonic-gate 	 */
4077c478bd9Sstevel@tonic-gate 	if ((mfd = minfd) == 0)
4087c478bd9Sstevel@tonic-gate 		mfd = set_minfd();
4097c478bd9Sstevel@tonic-gate 	if (dfd > 0 || (0 <= fd && fd < mfd)) {
4107c478bd9Sstevel@tonic-gate 		if (dfd <= 0)
4117c478bd9Sstevel@tonic-gate 			dfd = mfd;
4127c478bd9Sstevel@tonic-gate 		dfd = fcntl(fd, F_DUPFD, dfd);
4137c478bd9Sstevel@tonic-gate 		(void) close(fd);
4147c478bd9Sstevel@tonic-gate 		fd = dfd;
4157c478bd9Sstevel@tonic-gate 	}
4167c478bd9Sstevel@tonic-gate 	/*
4177c478bd9Sstevel@tonic-gate 	 * Mark it close-on-exec so any created process doesn't inherit it.
4187c478bd9Sstevel@tonic-gate 	 */
4197c478bd9Sstevel@tonic-gate 	if (fd >= 0)
4207c478bd9Sstevel@tonic-gate 		(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
4217c478bd9Sstevel@tonic-gate 	return (fd);
4227c478bd9Sstevel@tonic-gate }
4247c478bd9Sstevel@tonic-gate /*
4257c478bd9Sstevel@tonic-gate  * Create a new controlled process.
4267c478bd9Sstevel@tonic-gate  * Leave it stopped on successful exit from exec() or execve().
4277c478bd9Sstevel@tonic-gate  * Return an opaque pointer to its process control structure.
4287c478bd9Sstevel@tonic-gate  * Return NULL if process cannot be created (fork()/exec() not successful).
4297c478bd9Sstevel@tonic-gate  */
4307c478bd9Sstevel@tonic-gate struct ps_prochandle *
Pxcreate(const char * file,char * const * argv,char * const * envp,int * perr,char * path,size_t len)4317c478bd9Sstevel@tonic-gate Pxcreate(const char *file,	/* executable file name */
432d2a70789SRichard Lowe     char *const *argv,		/* argument vector */
433d2a70789SRichard Lowe     char *const *envp,		/* environment */
434d2a70789SRichard Lowe     int *perr,			/* pointer to error return code */
435d2a70789SRichard Lowe     char *path,		/* if non-null, holds exec path name on return */
436d2a70789SRichard Lowe     size_t len)			/* size of the path buffer */
4377c478bd9Sstevel@tonic-gate {
4387c478bd9Sstevel@tonic-gate 	char execpath[PATH_MAX];
4399acbbeafSnn 	char procname[PATH_MAX];
4407c478bd9Sstevel@tonic-gate 	struct ps_prochandle *P;
4417c478bd9Sstevel@tonic-gate 	pid_t pid;
4427c478bd9Sstevel@tonic-gate 	int fd;
4437c478bd9Sstevel@tonic-gate 	char *fname;
4447c478bd9Sstevel@tonic-gate 	int rc;
4457c478bd9Sstevel@tonic-gate 	int lasterrno = 0;
4477c478bd9Sstevel@tonic-gate 	if (len == 0)	/* zero length, no path */
4487c478bd9Sstevel@tonic-gate 		path = NULL;
4497c478bd9Sstevel@tonic-gate 	if (path != NULL)
4507c478bd9Sstevel@tonic-gate 		*path = '\0';
4527c478bd9Sstevel@tonic-gate 	if ((P = malloc(sizeof (struct ps_prochandle))) == NULL) {
4537c478bd9Sstevel@tonic-gate 		*perr = C_STRANGE;
4547c478bd9Sstevel@tonic-gate 		return (NULL);
4557c478bd9Sstevel@tonic-gate 	}
4577c478bd9Sstevel@tonic-gate 	if ((pid = fork1()) == -1) {
4587c478bd9Sstevel@tonic-gate 		free(P);
4597c478bd9Sstevel@tonic-gate 		*perr = C_FORK;
4607c478bd9Sstevel@tonic-gate 		return (NULL);
4617c478bd9Sstevel@tonic-gate 	}
4637c478bd9Sstevel@tonic-gate 	if (pid == 0) {			/* child process */
4647c478bd9Sstevel@tonic-gate 		id_t id;
4657c478bd9Sstevel@tonic-gate 		extern char **environ;
4677c478bd9Sstevel@tonic-gate 		/*
4687c478bd9Sstevel@tonic-gate 		 * If running setuid or setgid, reset credentials to normal.
4697c478bd9Sstevel@tonic-gate 		 */
4707c478bd9Sstevel@tonic-gate 		if ((id = getgid()) != getegid())
4717c478bd9Sstevel@tonic-gate 			(void) setgid(id);
4727c478bd9Sstevel@tonic-gate 		if ((id = getuid()) != geteuid())
4737c478bd9Sstevel@tonic-gate 			(void) setuid(id);
4757c478bd9Sstevel@tonic-gate 		Pcreate_callback(P);	/* execute callback (see below) */
4767c478bd9Sstevel@tonic-gate 		(void) pause();		/* wait for PRSABORT from parent */
4787c478bd9Sstevel@tonic-gate 		/*
4797c478bd9Sstevel@tonic-gate 		 * This is ugly.  There is no execvep() function that takes a
4807c478bd9Sstevel@tonic-gate 		 * path and an environment.  We cheat here by replacing the
4817c478bd9Sstevel@tonic-gate 		 * global 'environ' variable right before we call this.
4827c478bd9Sstevel@tonic-gate 		 */
4837c478bd9Sstevel@tonic-gate 		if (envp)
4847c478bd9Sstevel@tonic-gate 			environ = (char **)envp;
4867c478bd9Sstevel@tonic-gate 		(void) execvp(file, argv);  /* execute the program */
4877c478bd9Sstevel@tonic-gate 		_exit(127);
4887c478bd9Sstevel@tonic-gate 	}
4907c478bd9Sstevel@tonic-gate 	/*
4917c478bd9Sstevel@tonic-gate 	 * Initialize the process structure.
4927c478bd9Sstevel@tonic-gate 	 */
4937c478bd9Sstevel@tonic-gate 	(void) memset(P, 0, sizeof (*P));
4947c478bd9Sstevel@tonic-gate 	(void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL);
4957c478bd9Sstevel@tonic-gate 	P->flags |= CREATED;
4967c478bd9Sstevel@tonic-gate 	P->state = PS_RUN;
4977c478bd9Sstevel@tonic-gate 	P->pid = pid;
4987c478bd9Sstevel@tonic-gate 	P->asfd = -1;
4997c478bd9Sstevel@tonic-gate 	P->ctlfd = -1;
5007c478bd9Sstevel@tonic-gate 	P->statfd = -1;
5017c478bd9Sstevel@tonic-gate 	P->agentctlfd = -1;
5027c478bd9Sstevel@tonic-gate 	P->agentstatfd = -1;
5032a12f85aSJeremy Jones 	Pinit_ops(&P->ops, &P_live_ops);
5047c478bd9Sstevel@tonic-gate 	Pinitsym(P);
50550d4d24eSRobert Mustacchi 	Pinitfd(P);
5077c478bd9Sstevel@tonic-gate 	/*
5087c478bd9Sstevel@tonic-gate 	 * Open the /proc/pid files.
5097c478bd9Sstevel@tonic-gate 	 */
5109acbbeafSnn 	(void) snprintf(procname, sizeof (procname), "%s/%d/",
5119acbbeafSnn 	    procfs_path, (int)pid);
5127c478bd9Sstevel@tonic-gate 	fname = procname + strlen(procname);
5137c478bd9Sstevel@tonic-gate 	(void) set_minfd();
5157c478bd9Sstevel@tonic-gate 	/*
5167c478bd9Sstevel@tonic-gate 	 * Exclusive write open advises others not to interfere.
5177c478bd9Sstevel@tonic-gate 	 * There is no reason for any of these open()s to fail.
5187c478bd9Sstevel@tonic-gate 	 */
5197c478bd9Sstevel@tonic-gate 	(void) strcpy(fname, "as");
5207c478bd9Sstevel@tonic-gate 	if ((fd = open(procname, (O_RDWR|O_EXCL))) < 0 ||
5217c478bd9Sstevel@tonic-gate 	    (fd = dupfd(fd, 0)) < 0) {
5227c478bd9Sstevel@tonic-gate 		dprintf("Pcreate: failed to open %s: %s\n",
5237c478bd9Sstevel@tonic-gate 		    procname, strerror(errno));
5247c478bd9Sstevel@tonic-gate 		rc = C_STRANGE;
5257c478bd9Sstevel@tonic-gate 		goto bad;
5267c478bd9Sstevel@tonic-gate 	}
5277c478bd9Sstevel@tonic-gate 	P->asfd = fd;
5297c478bd9Sstevel@tonic-gate 	(void) strcpy(fname, "status");
5307c478bd9Sstevel@tonic-gate 	if ((fd = open(procname, O_RDONLY)) < 0 ||
5317c478bd9Sstevel@tonic-gate 	    (fd = dupfd(fd, 0)) < 0) {
5327c478bd9Sstevel@tonic-gate 		dprintf("Pcreate: failed to open %s: %s\n",
5337c478bd9Sstevel@tonic-gate 		    procname, strerror(errno));
5347c478bd9Sstevel@tonic-gate 		rc = C_STRANGE;
5357c478bd9Sstevel@tonic-gate 		goto bad;
5367c478bd9Sstevel@tonic-gate 	}
5377c478bd9Sstevel@tonic-gate 	P->statfd = fd;
5397c478bd9Sstevel@tonic-gate 	(void) strcpy(fname, "ctl");
5407c478bd9Sstevel@tonic-gate 	if ((fd = open(procname, O_WRONLY)) < 0 ||
5417c478bd9Sstevel@tonic-gate 	    (fd = dupfd(fd, 0)) < 0) {
5427c478bd9Sstevel@tonic-gate 		dprintf("Pcreate: failed to open %s: %s\n",
5437c478bd9Sstevel@tonic-gate 		    procname, strerror(errno));
5447c478bd9Sstevel@tonic-gate 		rc = C_STRANGE;
5457c478bd9Sstevel@tonic-gate 		goto bad;
5467c478bd9Sstevel@tonic-gate 	}
5477c478bd9Sstevel@tonic-gate 	P->ctlfd = fd;
5497c478bd9Sstevel@tonic-gate 	(void) Pstop(P, 0);	/* stop the controlled process */
5517c478bd9Sstevel@tonic-gate 	/*
5527c478bd9Sstevel@tonic-gate 	 * Wait for process to sleep in pause().
5537c478bd9Sstevel@tonic-gate 	 * If the process has already called pause(), then it should be
5547c478bd9Sstevel@tonic-gate 	 * stopped (PR_REQUESTED) while asleep in pause and we are done.
5557c478bd9Sstevel@tonic-gate 	 * Else we set up to catch entry/exit to pause() and set the process
5567c478bd9Sstevel@tonic-gate 	 * running again, expecting it to stop when it reaches pause().
5577c478bd9Sstevel@tonic-gate 	 * There is no reason for this to fail other than an interrupt.
5587c478bd9Sstevel@tonic-gate 	 */
5597c478bd9Sstevel@tonic-gate 	(void) Psysentry(P, SYS_pause, 1);
5607c478bd9Sstevel@tonic-gate 	(void) Psysexit(P, SYS_pause, 1);
5617c478bd9Sstevel@tonic-gate 	for (;;) {
5627c478bd9Sstevel@tonic-gate 		if (P->state == PS_STOP &&
5637c478bd9Sstevel@tonic-gate 		    P->status.pr_lwp.pr_syscall == SYS_pause &&
5647c478bd9Sstevel@tonic-gate 		    (P->status.pr_lwp.pr_why == PR_REQUESTED ||
5657c478bd9Sstevel@tonic-gate 		    P->status.pr_lwp.pr_why == PR_SYSENTRY ||
5667c478bd9Sstevel@tonic-gate 		    P->status.pr_lwp.pr_why == PR_SYSEXIT))
5677c478bd9Sstevel@tonic-gate 			break;
5697c478bd9Sstevel@tonic-gate 		if (P->state != PS_STOP ||	/* interrupt or process died */
5707c478bd9Sstevel@tonic-gate 		    Psetrun(P, 0, 0) != 0) {	/* can't restart */
5717c478bd9Sstevel@tonic-gate 			if (errno == EINTR || errno == ERESTART)
5727c478bd9Sstevel@tonic-gate 				rc = C_INTR;
5737c478bd9Sstevel@tonic-gate 			else {
5747c478bd9Sstevel@tonic-gate 				dprintf("Pcreate: Psetrun failed: %s\n",
5757c478bd9Sstevel@tonic-gate 				    strerror(errno));
5767c478bd9Sstevel@tonic-gate 				rc = C_STRANGE;
5777c478bd9Sstevel@tonic-gate 			}
5787c478bd9Sstevel@tonic-gate 			goto bad;
5797c478bd9Sstevel@tonic-gate 		}
5817c478bd9Sstevel@tonic-gate 		(void) Pwait(P, 0);
5827c478bd9Sstevel@tonic-gate 	}
5837c478bd9Sstevel@tonic-gate 	(void) Psysentry(P, SYS_pause, 0);
5847c478bd9Sstevel@tonic-gate 	(void) Psysexit(P, SYS_pause, 0);
5867c478bd9Sstevel@tonic-gate 	/*
5877c478bd9Sstevel@tonic-gate 	 * Kick the process off the pause() and catch
5887c478bd9Sstevel@tonic-gate 	 * it again on entry to exec() or exit().
5897c478bd9Sstevel@tonic-gate 	 */
5907c478bd9Sstevel@tonic-gate 	(void) Psysentry(P, SYS_exit, 1);
5917c478bd9Sstevel@tonic-gate 	(void) Psysentry(P, SYS_execve, 1);
5927c478bd9Sstevel@tonic-gate 	if (Psetrun(P, 0, PRSABORT) == -1) {
5937c478bd9Sstevel@tonic-gate 		dprintf("Pcreate: Psetrun failed: %s\n", strerror(errno));
5947c478bd9Sstevel@tonic-gate 		rc = C_STRANGE;
5957c478bd9Sstevel@tonic-gate 		goto bad;
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 	(void) Pwait(P, 0);
5987c478bd9Sstevel@tonic-gate 	if (P->state != PS_STOP) {
5997c478bd9Sstevel@tonic-gate 		dprintf("Pcreate: Pwait failed: %s\n", strerror(errno));
6007c478bd9Sstevel@tonic-gate 		rc = C_STRANGE;
6017c478bd9Sstevel@tonic-gate 		goto bad;
6027c478bd9Sstevel@tonic-gate 	}
6047c478bd9Sstevel@tonic-gate 	/*
6057c478bd9Sstevel@tonic-gate 	 * Move the process through instances of failed exec()s
6067c478bd9Sstevel@tonic-gate 	 * to reach the point of stopped on successful exec().
6077c478bd9Sstevel@tonic-gate 	 */
6087c478bd9Sstevel@tonic-gate 	(void) Psysexit(P, SYS_execve, TRUE);
6107c478bd9Sstevel@tonic-gate 	while (P->state == PS_STOP &&
6117c478bd9Sstevel@tonic-gate 	    P->status.pr_lwp.pr_why == PR_SYSENTRY &&
6128fd04b83SRoger A. Faulkner 	    P->status.pr_lwp.pr_what == SYS_execve) {
6137c478bd9Sstevel@tonic-gate 		/*
6147c478bd9Sstevel@tonic-gate 		 * Fetch the exec path name now, before we complete
6157c478bd9Sstevel@tonic-gate 		 * the exec().  We may lose the process and be unable
6167c478bd9Sstevel@tonic-gate 		 * to get the information later.
6177c478bd9Sstevel@tonic-gate 		 */
6187c478bd9Sstevel@tonic-gate 		(void) Pread_string(P, execpath, sizeof (execpath),
619d7755b5aSrh 		    (off_t)P->status.pr_lwp.pr_sysarg[0]);
6207c478bd9Sstevel@tonic-gate 		if (path != NULL)
6217c478bd9Sstevel@tonic-gate 			(void) strncpy(path, execpath, len);
6227c478bd9Sstevel@tonic-gate 		/*
6237c478bd9Sstevel@tonic-gate 		 * Set the process running and wait for
6247c478bd9Sstevel@tonic-gate 		 * it to stop on exit from the exec().
6257c478bd9Sstevel@tonic-gate 		 */
6267c478bd9Sstevel@tonic-gate 		(void) Psetrun(P, 0, 0);
6277c478bd9Sstevel@tonic-gate 		(void) Pwait(P, 0);
6297c478bd9Sstevel@tonic-gate 		if (P->state == PS_LOST &&		/* we lost control */
6307c478bd9Sstevel@tonic-gate 		    Preopen(P) != 0) {		/* and we can't get it back */
6317c478bd9Sstevel@tonic-gate 			rc = C_PERM;
6327c478bd9Sstevel@tonic-gate 			goto bad;
6337c478bd9Sstevel@tonic-gate 		}
6357c478bd9Sstevel@tonic-gate 		/*
6367c478bd9Sstevel@tonic-gate 		 * If the exec() failed, continue the loop, expecting
6377c478bd9Sstevel@tonic-gate 		 * there to be more attempts to exec(), based on PATH.
6387c478bd9Sstevel@tonic-gate 		 */
6397c478bd9Sstevel@tonic-gate 		if (P->state == PS_STOP &&
6407c478bd9Sstevel@tonic-gate 		    P->status.pr_lwp.pr_why == PR_SYSEXIT &&
6418fd04b83SRoger A. Faulkner 		    P->status.pr_lwp.pr_what == SYS_execve &&
6427c478bd9Sstevel@tonic-gate 		    (lasterrno = P->status.pr_lwp.pr_errno) != 0) {
6437c478bd9Sstevel@tonic-gate 			/*
6447c478bd9Sstevel@tonic-gate 			 * The exec() failed.  Set the process running and
6457c478bd9Sstevel@tonic-gate 			 * wait for it to stop on entry to the next exec().
6467c478bd9Sstevel@tonic-gate 			 */
6477c478bd9Sstevel@tonic-gate 			(void) Psetrun(P, 0, 0);
6487c478bd9Sstevel@tonic-gate 			(void) Pwait(P, 0);
6507c478bd9Sstevel@tonic-gate 			continue;
6517c478bd9Sstevel@tonic-gate 		}
6527c478bd9Sstevel@tonic-gate 		break;
6537c478bd9Sstevel@tonic-gate 	}
6557c478bd9Sstevel@tonic-gate 	if (P->state == PS_STOP &&
6567c478bd9Sstevel@tonic-gate 	    P->status.pr_lwp.pr_why == PR_SYSEXIT &&
6578fd04b83SRoger A. Faulkner 	    P->status.pr_lwp.pr_what == SYS_execve &&
6587c478bd9Sstevel@tonic-gate 	    P->status.pr_lwp.pr_errno == 0) {
6597c478bd9Sstevel@tonic-gate 		/*
6607c478bd9Sstevel@tonic-gate 		 * The process is stopped on successful exec() or execve().
6617c478bd9Sstevel@tonic-gate 		 * Turn off all tracing flags and return success.
6627c478bd9Sstevel@tonic-gate 		 */
6637c478bd9Sstevel@tonic-gate 		restore_tracing_flags(P);
6647c478bd9Sstevel@tonic-gate #ifndef _LP64
6657c478bd9Sstevel@tonic-gate 		/* We must be a 64-bit process to deal with a 64-bit process */
6667c478bd9Sstevel@tonic-gate 		if (P->status.pr_dmodel == PR_MODEL_LP64) {
6677c478bd9Sstevel@tonic-gate 			rc = C_LP64;
6687c478bd9Sstevel@tonic-gate 			goto bad;
6697c478bd9Sstevel@tonic-gate 		}
6707c478bd9Sstevel@tonic-gate #endif
6717c478bd9Sstevel@tonic-gate 		/*
6727c478bd9Sstevel@tonic-gate 		 * Set run-on-last-close so the controlled process
6737c478bd9Sstevel@tonic-gate 		 * runs even if we die on a signal.
6747c478bd9Sstevel@tonic-gate 		 */
6757c478bd9Sstevel@tonic-gate 		(void) Psetflags(P, PR_RLC);
6767c478bd9Sstevel@tonic-gate 		*perr = 0;
6777c478bd9Sstevel@tonic-gate 		return (P);
6787c478bd9Sstevel@tonic-gate 	}
6807c478bd9Sstevel@tonic-gate 	rc = lasterrno == ENOENT ? C_NOENT : C_NOEXEC;
6827c478bd9Sstevel@tonic-gate bad:
6837c478bd9Sstevel@tonic-gate 	(void) kill(pid, SIGKILL);
6847c478bd9Sstevel@tonic-gate 	if (path != NULL && rc != C_PERM && rc != C_LP64)
6857c478bd9Sstevel@tonic-gate 		*path = '\0';
6867c478bd9Sstevel@tonic-gate 	Pfree(P);
6877c478bd9Sstevel@tonic-gate 	*perr = rc;
6887c478bd9Sstevel@tonic-gate 	return (NULL);
6897c478bd9Sstevel@tonic-gate }
6917c478bd9Sstevel@tonic-gate struct ps_prochandle *
Pcreate(const char * file,char * const * argv,int * perr,char * path,size_t len)6927c478bd9Sstevel@tonic-gate Pcreate(
6937c478bd9Sstevel@tonic-gate 	const char *file,	/* executable file name */
6947c478bd9Sstevel@tonic-gate 	char *const *argv,	/* argument vector */
6957c478bd9Sstevel@tonic-gate 	int *perr,	/* pointer to error return code */
6967c478bd9Sstevel@tonic-gate 	char *path,	/* if non-null, holds exec path name on return */
6977c478bd9Sstevel@tonic-gate 	size_t len)	/* size of the path buffer */
6987c478bd9Sstevel@tonic-gate {
6997c478bd9Sstevel@tonic-gate 	return (Pxcreate(file, argv, NULL, perr, path, len));
7007c478bd9Sstevel@tonic-gate }
7027c478bd9Sstevel@tonic-gate /*
7037c478bd9Sstevel@tonic-gate  * Return a printable string corresponding to a Pcreate() error return.
7047c478bd9Sstevel@tonic-gate  */
7057c478bd9Sstevel@tonic-gate const char *
Pcreate_error(int error)7067c478bd9Sstevel@tonic-gate Pcreate_error(int error)
7077c478bd9Sstevel@tonic-gate {
7087c478bd9Sstevel@tonic-gate 	const char *str;
7107c478bd9Sstevel@tonic-gate 	switch (error) {
7117c478bd9Sstevel@tonic-gate 	case C_FORK:
7127c478bd9Sstevel@tonic-gate 		str = "cannot fork";
7137c478bd9Sstevel@tonic-gate 		break;
7147c478bd9Sstevel@tonic-gate 	case C_PERM:
7157c478bd9Sstevel@tonic-gate 		str = "file is set-id or unreadable";
7167c478bd9Sstevel@tonic-gate 		break;
7177c478bd9Sstevel@tonic-gate 	case C_NOEXEC:
7187c478bd9Sstevel@tonic-gate 		str = "cannot execute file";
7197c478bd9Sstevel@tonic-gate 		break;
7207c478bd9Sstevel@tonic-gate 	case C_INTR:
7217c478bd9Sstevel@tonic-gate 		str = "operation interrupted";
7227c478bd9Sstevel@tonic-gate 		break;
7237c478bd9Sstevel@tonic-gate 	case C_LP64:
7247c478bd9Sstevel@tonic-gate 		str = "program is _LP64, self is not";
7257c478bd9Sstevel@tonic-gate 		break;
7267c478bd9Sstevel@tonic-gate 	case C_STRANGE:
7277c478bd9Sstevel@tonic-gate 		str = "unanticipated system error";
7287c478bd9Sstevel@tonic-gate 		break;
7297c478bd9Sstevel@tonic-gate 	case C_NOENT:
7307c478bd9Sstevel@tonic-gate 		str = "cannot find executable file";
7317c478bd9Sstevel@tonic-gate 		break;
7327c478bd9Sstevel@tonic-gate 	default:
7337c478bd9Sstevel@tonic-gate 		str = "unknown error";
7347c478bd9Sstevel@tonic-gate 		break;
7357c478bd9Sstevel@tonic-gate 	}
7377c478bd9Sstevel@tonic-gate 	return (str);
7387c478bd9Sstevel@tonic-gate }
7407c478bd9Sstevel@tonic-gate /*
7417c478bd9Sstevel@tonic-gate  * Callback to execute in each child process created with Pcreate() after fork
7427c478bd9Sstevel@tonic-gate  * but before it execs the new process image.  By default, we do nothing, but
7437c478bd9Sstevel@tonic-gate  * by calling this function we allow the client program to define its own
7447c478bd9Sstevel@tonic-gate  * version of the function which will interpose on our empty default.  This
7457c478bd9Sstevel@tonic-gate  * may be useful for clients that need to modify signal dispositions, terminal
7467c478bd9Sstevel@tonic-gate  * attributes, or process group and session properties for each new victim.
7477c478bd9Sstevel@tonic-gate  */
7487c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7497c478bd9Sstevel@tonic-gate void
Pcreate_callback(struct ps_prochandle * P)7507c478bd9Sstevel@tonic-gate Pcreate_callback(struct ps_prochandle *P)
7517c478bd9Sstevel@tonic-gate {
7527c478bd9Sstevel@tonic-gate 	/* nothing to do here */
7537c478bd9Sstevel@tonic-gate }
7557c478bd9Sstevel@tonic-gate /*
7567c478bd9Sstevel@tonic-gate  * Grab an existing process.
7577c478bd9Sstevel@tonic-gate  * Return an opaque pointer to its process control structure.
7587c478bd9Sstevel@tonic-gate  *
7597c478bd9Sstevel@tonic-gate  * pid:		UNIX process ID.
7607c478bd9Sstevel@tonic-gate  * flags:
7617c478bd9Sstevel@tonic-gate  *	PGRAB_RETAIN	Retain tracing flags (default clears all tracing flags).
7627c478bd9Sstevel@tonic-gate  *	PGRAB_FORCE	Grab regardless of whether process is already traced.
7637c478bd9Sstevel@tonic-gate  *	PGRAB_RDONLY	Open the address space file O_RDONLY instead of O_RDWR,
7647c478bd9Sstevel@tonic-gate  *                      and do not open the process control file.
7657c478bd9Sstevel@tonic-gate  *	PGRAB_NOSTOP	Open the process but do not force it to stop.
7667c478bd9Sstevel@tonic-gate  * perr:	pointer to error return code.
7677c478bd9Sstevel@tonic-gate  */
7687c478bd9Sstevel@tonic-gate struct ps_prochandle *
Pgrab(pid_t pid,int flags,int * perr)7697c478bd9Sstevel@tonic-gate Pgrab(pid_t pid, int flags, int *perr)
7707c478bd9Sstevel@tonic-gate {
7717c478bd9Sstevel@tonic-gate 	struct ps_prochandle *P;
7727c478bd9Sstevel@tonic-gate 	int fd, omode;
7739acbbeafSnn 	char procname[PATH_MAX];
7747c478bd9Sstevel@tonic-gate 	char *fname;
7757c478bd9Sstevel@tonic-gate 	int rc = 0;
7777c478bd9Sstevel@tonic-gate 	/*
7787c478bd9Sstevel@tonic-gate 	 * PGRAB_RDONLY means that we do not open the /proc/<pid>/control file,
7797c478bd9Sstevel@tonic-gate 	 * and so it implies RETAIN and NOSTOP since both require control.
7807c478bd9Sstevel@tonic-gate 	 */
7817c478bd9Sstevel@tonic-gate 	if (flags & PGRAB_RDONLY)
7827c478bd9Sstevel@tonic-gate 		flags |= PGRAB_RETAIN | PGRAB_NOSTOP;
7847c478bd9Sstevel@tonic-gate 	if ((P = malloc(sizeof (struct ps_prochandle))) == NULL) {
7857c478bd9Sstevel@tonic-gate 		*perr = G_STRANGE;
7867c478bd9Sstevel@tonic-gate 		return (NULL);
7877c478bd9Sstevel@tonic-gate 	}
7897c478bd9Sstevel@tonic-gate 	P->asfd = -1;
7907c478bd9Sstevel@tonic-gate 	P->ctlfd = -1;
7917c478bd9Sstevel@tonic-gate 	P->statfd = -1;
7937c478bd9Sstevel@tonic-gate again:	/* Come back here if we lose it in the Window of Vulnerability */
7947c478bd9Sstevel@tonic-gate 	if (P->ctlfd >= 0)
7957c478bd9Sstevel@tonic-gate 		(void) close(P->ctlfd);
7967c478bd9Sstevel@tonic-gate 	if (P->asfd >= 0)
7977c478bd9Sstevel@tonic-gate 		(void) close(P->asfd);
7987c478bd9Sstevel@tonic-gate 	if (P->statfd >= 0)
7997c478bd9Sstevel@tonic-gate 		(void) close(P->statfd);
8007c478bd9Sstevel@tonic-gate 	(void) memset(P, 0, sizeof (*P));
8017c478bd9Sstevel@tonic-gate 	(void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL);
8027c478bd9Sstevel@tonic-gate 	P->ctlfd = -1;
8037c478bd9Sstevel@tonic-gate 	P->asfd = -1;
8047c478bd9Sstevel@tonic-gate 	P->statfd = -1;
8057c478bd9Sstevel@tonic-gate 	P->agentctlfd = -1;
8067c478bd9Sstevel@tonic-gate 	P->agentstatfd = -1;
8072a12f85aSJeremy Jones 	Pinit_ops(&P->ops, &P_live_ops);
8087c478bd9Sstevel@tonic-gate 	Pinitsym(P);
80950d4d24eSRobert Mustacchi 	Pinitfd(P);
8117c478bd9Sstevel@tonic-gate 	/*
8127c478bd9Sstevel@tonic-gate 	 * Open the /proc/pid files
8137c478bd9Sstevel@tonic-gate 	 */
8149acbbeafSnn 	(void) snprintf(procname, sizeof (procname), "%s/%d/",
8159acbbeafSnn 	    procfs_path, (int)pid);
8167c478bd9Sstevel@tonic-gate 	fname = procname + strlen(procname);
8177c478bd9Sstevel@tonic-gate 	(void) set_minfd();
8197c478bd9Sstevel@tonic-gate 	/*
8207c478bd9Sstevel@tonic-gate 	 * Request exclusive open to avoid grabbing someone else's
8217c478bd9Sstevel@tonic-gate 	 * process and to prevent others from interfering afterwards.
8227c478bd9Sstevel@tonic-gate 	 * If this fails and the 'PGRAB_FORCE' flag is set, attempt to
8237c478bd9Sstevel@tonic-gate 	 * open non-exclusively.
8247c478bd9Sstevel@tonic-gate 	 */
8257c478bd9Sstevel@tonic-gate 	(void) strcpy(fname, "as");
8267c478bd9Sstevel@tonic-gate 	omode = (flags & PGRAB_RDONLY) ? O_RDONLY : O_RDWR;
8287c478bd9Sstevel@tonic-gate 	if (((fd = open(procname, omode | O_EXCL)) < 0 &&
8297c478bd9Sstevel@tonic-gate 	    (fd = ((flags & PGRAB_FORCE)? open(procname, omode) : -1)) < 0) ||
8307c478bd9Sstevel@tonic-gate 	    (fd = dupfd(fd, 0)) < 0) {
8317c478bd9Sstevel@tonic-gate 		switch (errno) {
8327c478bd9Sstevel@tonic-gate 		case ENOENT:
8337c478bd9Sstevel@tonic-gate 			rc = G_NOPROC;
8347c478bd9Sstevel@tonic-gate 			break;
8357c478bd9Sstevel@tonic-gate 		case EACCES:
8367c478bd9Sstevel@tonic-gate 		case EPERM:
8377c478bd9Sstevel@tonic-gate 			rc = G_PERM;
8387c478bd9Sstevel@tonic-gate 			break;
839cd11e192Sjhaslam 		case EMFILE:
840cd11e192Sjhaslam 			rc = G_NOFD;
841cd11e192Sjhaslam 			break;
8427c478bd9Sstevel@tonic-gate 		case EBUSY:
8437c478bd9Sstevel@tonic-gate 			if (!(flags & PGRAB_FORCE) || geteuid() != 0) {
8447c478bd9Sstevel@tonic-gate 				rc = G_BUSY;
8457c478bd9Sstevel@tonic-gate 				break;
8467c478bd9Sstevel@tonic-gate 			}
8477c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
8487c478bd9Sstevel@tonic-gate 		default:
8497c478bd9Sstevel@tonic-gate 			dprintf("Pgrab: failed to open %s: %s\n",
8507c478bd9Sstevel@tonic-gate 			    procname, strerror(errno));
8517c478bd9Sstevel@tonic-gate 			rc = G_STRANGE;
8527c478bd9Sstevel@tonic-gate 			break;
8537c478bd9Sstevel@tonic-gate 		}
8547c478bd9Sstevel@tonic-gate 		goto err;
8557c478bd9Sstevel@tonic-gate 	}
8567c478bd9Sstevel@tonic-gate 	P->asfd = fd;
8587c478bd9Sstevel@tonic-gate 	(void) strcpy(fname, "status");
8597c478bd9Sstevel@tonic-gate 	if ((fd = open(procname, O_RDONLY)) < 0 ||
8607c478bd9Sstevel@tonic-gate 	    (fd = dupfd(fd, 0)) < 0) {
8617c478bd9Sstevel@tonic-gate 		switch (errno) {
8627c478bd9Sstevel@tonic-gate 		case ENOENT:
8637c478bd9Sstevel@tonic-gate 			rc = G_NOPROC;
8647c478bd9Sstevel@tonic-gate 			break;
865cd11e192Sjhaslam 		case EMFILE:
866cd11e192Sjhaslam 			rc = G_NOFD;
867cd11e192Sjhaslam 			break;
8687c478bd9Sstevel@tonic-gate 		default:
8697c478bd9Sstevel@tonic-gate 			dprintf("Pgrab: failed to open %s: %s\n",
8707c478bd9Sstevel@tonic-gate 			    procname, strerror(errno));
8717c478bd9Sstevel@tonic-gate 			rc = G_STRANGE;
8727c478bd9Sstevel@tonic-gate 			break;
8737c478bd9Sstevel@tonic-gate 		}
8747c478bd9Sstevel@tonic-gate 		goto err;
8757c478bd9Sstevel@tonic-gate 	}
8767c478bd9Sstevel@tonic-gate 	P->statfd = fd;
8787c478bd9Sstevel@tonic-gate 	if (!(flags & PGRAB_RDONLY)) {
8797c478bd9Sstevel@tonic-gate 		(void) strcpy(fname, "ctl");
8807c478bd9Sstevel@tonic-gate 		if ((fd = open(procname, O_WRONLY)) < 0 ||
8817c478bd9Sstevel@tonic-gate 		    (fd = dupfd(fd, 0)) < 0) {
8827c478bd9Sstevel@tonic-gate 			switch (errno) {
8837c478bd9Sstevel@tonic-gate 			case ENOENT:
8847c478bd9Sstevel@tonic-gate 				rc = G_NOPROC;
8857c478bd9Sstevel@tonic-gate 				break;
886cd11e192Sjhaslam 			case EMFILE:
887cd11e192Sjhaslam 				rc = G_NOFD;
888cd11e192Sjhaslam 				break;
8897c478bd9Sstevel@tonic-gate 			default:
8907c478bd9Sstevel@tonic-gate 				dprintf("Pgrab: failed to open %s: %s\n",
8917c478bd9Sstevel@tonic-gate 				    procname, strerror(errno));
8927c478bd9Sstevel@tonic-gate 				rc = G_STRANGE;
8937c478bd9Sstevel@tonic-gate 				break;
8947c478bd9Sstevel@tonic-gate 			}
8957c478bd9Sstevel@tonic-gate 			goto err;
8967c478bd9Sstevel@tonic-gate 		}
8977c478bd9Sstevel@tonic-gate 		P->ctlfd = fd;
8987c478bd9Sstevel@tonic-gate 	}
9007c478bd9Sstevel@tonic-gate 	P->state = PS_RUN;
9017c478bd9Sstevel@tonic-gate 	P->pid = pid;
9037c478bd9Sstevel@tonic-gate 	/*
9047c478bd9Sstevel@tonic-gate 	 * We are now in the Window of Vulnerability (WoV).  The process may
9057c478bd9Sstevel@tonic-gate 	 * exec() a setuid/setgid or unreadable object file between the open()
9067c478bd9Sstevel@tonic-gate 	 * and the PCSTOP.  We will get EAGAIN in this case and must start over.
9077c478bd9Sstevel@tonic-gate 	 * As Pstopstatus will trigger the first read() from a /proc file,
9087c478bd9Sstevel@tonic-gate 	 * we also need to handle EOVERFLOW here when 32-bit as an indicator
9097c478bd9Sstevel@tonic-gate 	 * that this process is 64-bit.  Finally, if the process has become
9107c478bd9Sstevel@tonic-gate 	 * a zombie (PS_UNDEAD) while we were trying to grab it, just remain
9117c478bd9Sstevel@tonic-gate 	 * silent about this and pretend there was no process.
9127c478bd9Sstevel@tonic-gate 	 */
9137c478bd9Sstevel@tonic-gate 	if (Pstopstatus(P, PCNULL, 0) != 0) {
9147c478bd9Sstevel@tonic-gate #ifndef _LP64
9157c478bd9Sstevel@tonic-gate 		if (errno == EOVERFLOW) {
9167c478bd9Sstevel@tonic-gate 			rc = G_LP64;
9177c478bd9Sstevel@tonic-gate 			goto err;
9187c478bd9Sstevel@tonic-gate 		}
9197c478bd9Sstevel@tonic-gate #endif
9207c478bd9Sstevel@tonic-gate 		if (P->state == PS_LOST) {	/* WoV */
9217c478bd9Sstevel@tonic-gate 			(void) mutex_destroy(&P->proc_lock);
9227c478bd9Sstevel@tonic-gate 			goto again;
9237c478bd9Sstevel@tonic-gate 		}
9257c478bd9Sstevel@tonic-gate 		if (P->state == PS_UNDEAD)
9267c478bd9Sstevel@tonic-gate 			rc = G_NOPROC;
9277c478bd9Sstevel@tonic-gate 		else
9287c478bd9Sstevel@tonic-gate 			rc = G_STRANGE;
9307c478bd9Sstevel@tonic-gate 		goto err;
9317c478bd9Sstevel@tonic-gate 	}
9337c478bd9Sstevel@tonic-gate 	/*
9347c478bd9Sstevel@tonic-gate 	 * If the process is a system process, we can't control it even as root
9357c478bd9Sstevel@tonic-gate 	 */
9367c478bd9Sstevel@tonic-gate 	if (P->status.pr_flags & PR_ISSYS) {
9377c478bd9Sstevel@tonic-gate 		rc = G_SYS;
9387c478bd9Sstevel@tonic-gate 		goto err;
9397c478bd9Sstevel@tonic-gate 	}
9407c478bd9Sstevel@tonic-gate #ifndef _LP64
9417c478bd9Sstevel@tonic-gate 	/*
9427c478bd9Sstevel@tonic-gate 	 * We must be a 64-bit process to deal with a 64-bit process
9437c478bd9Sstevel@tonic-gate 	 */
9447c478bd9Sstevel@tonic-gate 	if (P->status.pr_dmodel == PR_MODEL_LP64) {
9457c478bd9Sstevel@tonic-gate 		rc = G_LP64;
9467c478bd9Sstevel@tonic-gate 		goto err;
9477c478bd9Sstevel@tonic-gate 	}
9487c478bd9Sstevel@tonic-gate #endif
9507c478bd9Sstevel@tonic-gate 	/*
9517c478bd9Sstevel@tonic-gate 	 * Remember the status for use by Prelease().
9527c478bd9Sstevel@tonic-gate 	 */
9537c478bd9Sstevel@tonic-gate 	P->orig_status = P->status;	/* structure copy */
9557c478bd9Sstevel@tonic-gate 	/*
9567c478bd9Sstevel@tonic-gate 	 * Before stopping the process, make sure we are not grabbing ourselves.
9577c478bd9Sstevel@tonic-gate 	 * If we are, make sure we are doing it PGRAB_RDONLY.
9587c478bd9Sstevel@tonic-gate 	 */
9597c478bd9Sstevel@tonic-gate 	if (pid == getpid()) {
9607c478bd9Sstevel@tonic-gate 		/*
9617c478bd9Sstevel@tonic-gate 		 * Verify that the process is really ourself:
9627c478bd9Sstevel@tonic-gate 		 * Set a magic number, read it through the
9637c478bd9Sstevel@tonic-gate 		 * /proc file and see if the results match.
9647c478bd9Sstevel@tonic-gate 		 */
9657c478bd9Sstevel@tonic-gate 		uint32_t magic1 = 0;
9667c478bd9Sstevel@tonic-gate 		uint32_t magic2 = 2;
9687c478bd9Sstevel@tonic-gate 		errno = 0;
9707c478bd9Sstevel@tonic-gate 		if (Pread(P, &magic2, sizeof (magic2), (uintptr_t)&magic1)
9717c478bd9Sstevel@tonic-gate 		    == sizeof (magic2) &&
9727c478bd9Sstevel@tonic-gate 		    magic2 == 0 &&
9737c478bd9Sstevel@tonic-gate 		    (magic1 = 0xfeedbeef) &&
9747c478bd9Sstevel@tonic-gate 		    Pread(P, &magic2, sizeof (magic2), (uintptr_t)&magic1)
9757c478bd9Sstevel@tonic-gate 		    == sizeof (magic2) &&
9767c478bd9Sstevel@tonic-gate 		    magic2 == 0xfeedbeef &&
9777c478bd9Sstevel@tonic-gate 		    !(flags & PGRAB_RDONLY)) {
9787c478bd9Sstevel@tonic-gate 			rc = G_SELF;
9797c478bd9Sstevel@tonic-gate 			goto err;
9807c478bd9Sstevel@tonic-gate 		}
9817c478bd9Sstevel@tonic-gate 	}
9837c478bd9Sstevel@tonic-gate 	/*
9847c478bd9Sstevel@tonic-gate 	 * If the process is already stopped or has been directed
9857c478bd9Sstevel@tonic-gate 	 * to stop via /proc, do not set run-on-last-close.
9867c478bd9Sstevel@tonic-gate 	 */
9877c478bd9Sstevel@tonic-gate 	if (!(P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) &&
9887c478bd9Sstevel@tonic-gate 	    !(flags & PGRAB_RDONLY)) {
9897c478bd9Sstevel@tonic-gate 		/*
9907c478bd9Sstevel@tonic-gate 		 * Mark the process run-on-last-close so
9917c478bd9Sstevel@tonic-gate 		 * it runs even if we die from SIGKILL.
9927c478bd9Sstevel@tonic-gate 		 */
9937c478bd9Sstevel@tonic-gate 		if (Psetflags(P, PR_RLC) != 0) {
9947c478bd9Sstevel@tonic-gate 			if (errno == EAGAIN) {	/* WoV */
9957c478bd9Sstevel@tonic-gate 				(void) mutex_destroy(&P->proc_lock);
9967c478bd9Sstevel@tonic-gate 				goto again;
9977c478bd9Sstevel@tonic-gate 			}
9987c478bd9Sstevel@tonic-gate 			if (errno == ENOENT)	/* No complaint about zombies */
9997c478bd9Sstevel@tonic-gate 				rc = G_ZOMB;
10007c478bd9Sstevel@tonic-gate 			else {
10017c478bd9Sstevel@tonic-gate 				dprintf("Pgrab: failed to set RLC\n");
10027c478bd9Sstevel@tonic-gate 				rc = G_STRANGE;
10037c478bd9Sstevel@tonic-gate 			}
10047c478bd9Sstevel@tonic-gate 			goto err;
10057c478bd9Sstevel@tonic-gate 		}
10067c478bd9Sstevel@tonic-gate 	}
10087c478bd9Sstevel@tonic-gate 	/*
10097c478bd9Sstevel@tonic-gate 	 * If a stop directive is pending and the process has not yet stopped,
10107c478bd9Sstevel@tonic-gate 	 * then synchronously wait for the stop directive to take effect.
10117c478bd9Sstevel@tonic-gate 	 * Limit the time spent waiting for the process to stop by iterating
10127c478bd9Sstevel@tonic-gate 	 * at most 10 times. The time-out of 20 ms corresponds to the time
10137c478bd9Sstevel@tonic-gate 	 * between sending the stop directive and the process actually stopped
10147c478bd9Sstevel@tonic-gate 	 * as measured by DTrace on a slow, busy system. If the process doesn't
10157c478bd9Sstevel@tonic-gate 	 * stop voluntarily, clear the PR_DSTOP flag so that the code below
10167c478bd9Sstevel@tonic-gate 	 * forces the process to stop.
10177c478bd9Sstevel@tonic-gate 	 */
10187c478bd9Sstevel@tonic-gate 	if (!(flags & PGRAB_RDONLY)) {
10197c478bd9Sstevel@tonic-gate 		int niter = 0;
10207c478bd9Sstevel@tonic-gate 		while ((P->status.pr_lwp.pr_flags & (PR_STOPPED|PR_DSTOP)) ==
10217c478bd9Sstevel@tonic-gate 		    PR_DSTOP && niter < 10 &&
10227c478bd9Sstevel@tonic-gate 		    Pstopstatus(P, PCTWSTOP, 20) != 0) {
10237c478bd9Sstevel@tonic-gate 			niter++;
10247c478bd9Sstevel@tonic-gate 			if (flags & PGRAB_NOSTOP)
10257c478bd9Sstevel@tonic-gate 				break;
10267c478bd9Sstevel@tonic-gate 		}
10277c478bd9Sstevel@tonic-gate 		if (niter == 10 && !(flags & PGRAB_NOSTOP)) {
10287c478bd9Sstevel@tonic-gate 			/* Try it harder down below */
10297c478bd9Sstevel@tonic-gate 			P->status.pr_lwp.pr_flags &= ~PR_DSTOP;
10307c478bd9Sstevel@tonic-gate 		}
10317c478bd9Sstevel@tonic-gate 	}
10337c478bd9Sstevel@tonic-gate 	/*
10347c478bd9Sstevel@tonic-gate 	 * If the process is not already stopped or directed to stop
10357c478bd9Sstevel@tonic-gate 	 * and PGRAB_NOSTOP was not specified, stop the process now.
10367c478bd9Sstevel@tonic-gate 	 */
10377c478bd9Sstevel@tonic-gate 	if (!(P->status.pr_lwp.pr_flags & (PR_ISTOP|PR_DSTOP)) &&
10387c478bd9Sstevel@tonic-gate 	    !(flags & PGRAB_NOSTOP)) {
10397c478bd9Sstevel@tonic-gate 		/*
10407c478bd9Sstevel@tonic-gate 		 * Stop the process, get its status and signal/syscall masks.
10417c478bd9Sstevel@tonic-gate 		 */
10427c478bd9Sstevel@tonic-gate 		if (((P->status.pr_lwp.pr_flags & PR_STOPPED) &&