⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcontrol.c

📁 Sun Solaris 10 中的 DTrace 组件的源代码。请参看: http://www.sun.com/software/solaris/observability.jsp
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * Copyright 2005 Sun Microsystems, Inc.  All rights reserved. * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only. * See the file usr/src/LICENSING.NOTICE in this distribution or * http://www.opensolaris.org/license/ for details. */#pragma ident	"@(#)Pcontrol.c	1.37	04/11/01 SMI"#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <ctype.h>#include <fcntl.h>#include <string.h>#include <memory.h>#include <errno.h>#include <dirent.h>#include <limits.h>#include <signal.h>#include <sys/types.h>#include <sys/uio.h>#include <sys/stat.h>#include <sys/resource.h>#include <sys/param.h>#include <sys/stack.h>#include <sys/fault.h>#include <sys/syscall.h>#include <sys/sysmacros.h>#include "libproc.h"#include "Pcontrol.h"#include "Putil.h"#include "P32ton.h"int	_libproc_debug;		/* set non-zero to enable debugging printfs */sigset_t blockable_sigs;	/* signals to block when we need to be safe */static	int	minfd;	/* minimum file descriptor returned by dupfd(fd, 0) *//* * Function prototypes for static routines in this module. */static	void	deadcheck(struct ps_prochandle *);static	void	restore_tracing_flags(struct ps_prochandle *);static	void	Lfree_internal(struct ps_prochandle *, struct ps_lwphandle *);/* * Read/write interface for live processes: just pread/pwrite the * /proc/<pid>/as file: */static ssize_tPread_live(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr){	return (pread(P->asfd, buf, n, (off_t)addr));}static ssize_tPwrite_live(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr){	return (pwrite(P->asfd, buf, n, (off_t)addr));}static const ps_rwops_t P_live_ops = { Pread_live, Pwrite_live };/* * This is the library's .init handler. */#pragma init(_libproc_init)void_libproc_init(void){	_libproc_debug = getenv("LIBPROC_DEBUG") != NULL;	(void) sigfillset(&blockable_sigs);	(void) sigdelset(&blockable_sigs, SIGKILL);	(void) sigdelset(&blockable_sigs, SIGSTOP);}/* * Call set_minfd() once before calling dupfd() several times. * We assume that the application will not reduce its current file * descriptor limit lower than 512 once it has set at least that value. */intset_minfd(void){	static mutex_t minfd_lock = DEFAULTMUTEX;	struct rlimit rlim;	int fd;	if ((fd = minfd) < 256) {		(void) mutex_lock(&minfd_lock);		if ((fd = minfd) < 256) {			if (getrlimit(RLIMIT_NOFILE, &rlim) != 0)				rlim.rlim_cur = rlim.rlim_max = 0;			if (rlim.rlim_cur >= 512)				fd = 256;			else if ((fd = rlim.rlim_cur / 2) < 3)				fd = 3;			minfd = fd;		}		(void) mutex_unlock(&minfd_lock);	}	return (fd);}intdupfd(int fd, int dfd){	int mfd;	/*	 * Make fd be greater than 255 (the 32-bit stdio limit),	 * or at least make it greater than 2 so that the	 * program will work when spawned by init(1m).	 * Also, if dfd is non-zero, dup the fd to be dfd.	 */	if ((mfd = minfd) == 0)		mfd = set_minfd();	if (dfd > 0 || (0 <= fd && fd < mfd)) {		if (dfd <= 0)			dfd = mfd;		dfd = fcntl(fd, F_DUPFD, dfd);		(void) close(fd);		fd = dfd;	}	/*	 * Mark it close-on-exec so any created process doesn't inherit it.	 */	if (fd >= 0)		(void) fcntl(fd, F_SETFD, FD_CLOEXEC);	return (fd);}/* * Create a new controlled process. * Leave it stopped on successful exit from exec() or execve(). * Return an opaque pointer to its process control structure. * Return NULL if process cannot be created (fork()/exec() not successful). */struct ps_prochandle *Pxcreate(const char *file,	/* executable file name */	char *const *argv,	/* argument vector */	char *const *envp,	/* environment */	int *perr,	/* pointer to error return code */	char *path,	/* if non-null, holds exec path name on return */	size_t len)	/* size of the path buffer */{	char execpath[PATH_MAX];	char procname[100];	struct ps_prochandle *P;	pid_t pid;	int fd;	char *fname;	int rc;	int lasterrno = 0;	if (len == 0)	/* zero length, no path */		path = NULL;	if (path != NULL)		*path = '\0';	if ((P = malloc(sizeof (struct ps_prochandle))) == NULL) {		*perr = C_STRANGE;		return (NULL);	}	if ((pid = fork1()) == -1) {		free(P);		*perr = C_FORK;		return (NULL);	}	if (pid == 0) {			/* child process */		id_t id;		extern char **environ;		/*		 * If running setuid or setgid, reset credentials to normal.		 */		if ((id = getgid()) != getegid())			(void) setgid(id);		if ((id = getuid()) != geteuid())			(void) setuid(id);		Pcreate_callback(P);	/* execute callback (see below) */		(void) pause();		/* wait for PRSABORT from parent */		/*		 * This is ugly.  There is no execvep() function that takes a		 * path and an environment.  We cheat here by replacing the		 * global 'environ' variable right before we call this.		 */		if (envp)			environ = (char **)envp;		(void) execvp(file, argv);  /* execute the program */		_exit(127);	}	/*	 * Initialize the process structure.	 */	(void) memset(P, 0, sizeof (*P));	(void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL);	P->flags |= CREATED;	P->state = PS_RUN;	P->pid = pid;	P->asfd = -1;	P->ctlfd = -1;	P->statfd = -1;	P->agentctlfd = -1;	P->agentstatfd = -1;	P->ops = &P_live_ops;	Pinitsym(P);	/*	 * Open the /proc/pid files.	 */	(void) sprintf(procname, "/proc/%d/", (int)pid);	fname = procname + strlen(procname);	(void) set_minfd();	/*	 * Exclusive write open advises others not to interfere.	 * There is no reason for any of these open()s to fail.	 */	(void) strcpy(fname, "as");	if ((fd = open(procname, (O_RDWR|O_EXCL))) < 0 ||	    (fd = dupfd(fd, 0)) < 0) {		dprintf("Pcreate: failed to open %s: %s\n",		    procname, strerror(errno));		rc = C_STRANGE;		goto bad;	}	P->asfd = fd;	(void) strcpy(fname, "status");	if ((fd = open(procname, O_RDONLY)) < 0 ||	    (fd = dupfd(fd, 0)) < 0) {		dprintf("Pcreate: failed to open %s: %s\n",		    procname, strerror(errno));		rc = C_STRANGE;		goto bad;	}	P->statfd = fd;	(void) strcpy(fname, "ctl");	if ((fd = open(procname, O_WRONLY)) < 0 ||	    (fd = dupfd(fd, 0)) < 0) {		dprintf("Pcreate: failed to open %s: %s\n",		    procname, strerror(errno));		rc = C_STRANGE;		goto bad;	}	P->ctlfd = fd;	(void) Pstop(P, 0);	/* stop the controlled process */	/*	 * Wait for process to sleep in pause().	 * If the process has already called pause(), then it should be	 * stopped (PR_REQUESTED) while asleep in pause and we are done.	 * Else we set up to catch entry/exit to pause() and set the process	 * running again, expecting it to stop when it reaches pause().	 * There is no reason for this to fail other than an interrupt.	 */	(void) Psysentry(P, SYS_pause, 1);	(void) Psysexit(P, SYS_pause, 1);	for (;;) {		if (P->state == PS_STOP &&		    P->status.pr_lwp.pr_syscall == SYS_pause &&		    (P->status.pr_lwp.pr_why == PR_REQUESTED ||		    P->status.pr_lwp.pr_why == PR_SYSENTRY ||		    P->status.pr_lwp.pr_why == PR_SYSEXIT))			break;		if (P->state != PS_STOP ||	/* interrupt or process died */		    Psetrun(P, 0, 0) != 0) {	/* can't restart */			if (errno == EINTR || errno == ERESTART)				rc = C_INTR;			else {				dprintf("Pcreate: Psetrun failed: %s\n",				    strerror(errno));				rc = C_STRANGE;			}			goto bad;		}		(void) Pwait(P, 0);	}	(void) Psysentry(P, SYS_pause, 0);	(void) Psysexit(P, SYS_pause, 0);	/*	 * Kick the process off the pause() and catch	 * it again on entry to exec() or exit().	 */	(void) Psysentry(P, SYS_exit, 1);	(void) Psysentry(P, SYS_exec, 1);	(void) Psysentry(P, SYS_execve, 1);	if (Psetrun(P, 0, PRSABORT) == -1) {		dprintf("Pcreate: Psetrun failed: %s\n", strerror(errno));		rc = C_STRANGE;		goto bad;	}	(void) Pwait(P, 0);	if (P->state != PS_STOP) {		dprintf("Pcreate: Pwait failed: %s\n", strerror(errno));		rc = C_STRANGE;		goto bad;	}	/*	 * Move the process through instances of failed exec()s	 * to reach the point of stopped on successful exec().	 */	(void) Psysexit(P, SYS_exec, TRUE);	(void) Psysexit(P, SYS_execve, TRUE);	while (P->state == PS_STOP &&	    P->status.pr_lwp.pr_why == PR_SYSENTRY &&	    (P->status.pr_lwp.pr_what == SYS_execve ||	    P->status.pr_lwp.pr_what == SYS_exec)) {		/*		 * Fetch the exec path name now, before we complete		 * the exec().  We may lose the process and be unable		 * to get the information later.		 */		(void) Pread_string(P, execpath, sizeof (execpath),			(off_t)P->status.pr_lwp.pr_sysarg[0]);		if (path != NULL)			(void) strncpy(path, execpath, len);		/*		 * Set the process running and wait for		 * it to stop on exit from the exec().		 */		(void) Psetrun(P, 0, 0);		(void) Pwait(P, 0);		if (P->state == PS_LOST &&		/* we lost control */		    Preopen(P) != 0) {		/* and we can't get it back */			rc = C_PERM;			goto bad;		}		/*		 * If the exec() failed, continue the loop, expecting		 * there to be more attempts to exec(), based on PATH.		 */		if (P->state == PS_STOP &&		    P->status.pr_lwp.pr_why == PR_SYSEXIT &&		    (P->status.pr_lwp.pr_what == SYS_execve ||		    P->status.pr_lwp.pr_what == SYS_exec) &&		    (lasterrno = P->status.pr_lwp.pr_errno) != 0) {			/*			 * The exec() failed.  Set the process running and			 * wait for it to stop on entry to the next exec().			 */			(void) Psetrun(P, 0, 0);			(void) Pwait(P, 0);			continue;		}		break;	}	if (P->state == PS_STOP &&	    P->status.pr_lwp.pr_why == PR_SYSEXIT &&	    (P->status.pr_lwp.pr_what == SYS_execve ||	    P->status.pr_lwp.pr_what == SYS_exec) &&	    P->status.pr_lwp.pr_errno == 0) {		/*		 * The process is stopped on successful exec() or execve().		 * Turn off all tracing flags and return success.		 */		restore_tracing_flags(P);#ifndef _LP64		/* We must be a 64-bit process to deal with a 64-bit process */		if (P->status.pr_dmodel == PR_MODEL_LP64) {			rc = C_LP64;			goto bad;		}#endif		/*		 * Set run-on-last-close so the controlled process		 * runs even if we die on a signal.		 */		(void) Psetflags(P, PR_RLC);		*perr = 0;		return (P);	}	rc = lasterrno == ENOENT ? C_NOENT : C_NOEXEC;bad:	(void) kill(pid, SIGKILL);	if (path != NULL && rc != C_PERM && rc != C_LP64)		*path = '\0';	Pfree(P);	*perr = rc;	return (NULL);}struct ps_prochandle *Pcreate(	const char *file,	/* executable file name */	char *const *argv,	/* argument vector */	int *perr,	/* pointer to error return code */	char *path,	/* if non-null, holds exec path name on return */	size_t len)	/* size of the path buffer */{	return (Pxcreate(file, argv, NULL, perr, path, len));}/* * Return a printable string corresponding to a Pcreate() error return. */const char *Pcreate_error(int error){	const char *str;	switch (error) {	case C_FORK:		str = "cannot fork";		break;	case C_PERM:		str = "file is set-id or unreadable";		break;	case C_NOEXEC:		str = "cannot execute file";		break;	case C_INTR:		str = "operation interrupted";		break;	case C_LP64:		str = "program is _LP64, self is not";		break;	case C_STRANGE:		str = "unanticipated system error";		break;	case C_NOENT:		str = "cannot find executable file";		break;	default:		str = "unknown error";		break;	}	return (str);}/* * Callback to execute in each child process created with Pcreate() after fork * but before it execs the new process image.  By default, we do nothing, but * by calling this function we allow the client program to define its own * version of the function which will interpose on our empty default.  This * may be useful for clients that need to modify signal dispositions, terminal * attributes, or process group and session properties for each new victim. *//*ARGSUSED*/voidPcreate_callback(struct ps_prochandle *P){	/* nothing to do here */}/* * Grab an existing process. * Return an opaque pointer to its process control structure. * * pid:		UNIX process ID. * flags: *	PGRAB_RETAIN	Retain tracing flags (default clears all tracing flags). *	PGRAB_FORCE	Grab regardless of whether process is already traced. *	PGRAB_RDONLY	Open the address space file O_RDONLY instead of O_RDWR, *                      and do not open the process control file. *	PGRAB_NOSTOP	Open the process but do not force it to stop. * perr:	pointer to error return code. */struct ps_prochandle *Pgrab(pid_t pid, int flags, int *perr){	struct ps_prochandle *P;	int fd, omode;	char procname[100];	char *fname;	int rc = 0;	/*	 * PGRAB_RDONLY means that we do not open the /proc/<pid>/control file,	 * and so it implies RETAIN and NOSTOP since both require control.	 */	if (flags & PGRAB_RDONLY)		flags |= PGRAB_RETAIN | PGRAB_NOSTOP;	if ((P = malloc(sizeof (struct ps_prochandle))) == NULL) {		*perr = G_STRANGE;		return (NULL);	}	P->asfd = -1;	P->ctlfd = -1;	P->statfd = -1;again:	/* Come back here if we lose it in the Window of Vulnerability */	if (P->ctlfd >= 0)		(void) close(P->ctlfd);	if (P->asfd >= 0)		(void) close(P->asfd);	if (P->statfd >= 0)		(void) close(P->statfd);	(void) memset(P, 0, sizeof (*P));	(void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL);	P->ctlfd = -1;	P->asfd = -1;	P->statfd = -1;	P->agentctlfd = -1;	P->agentstatfd = -1;	P->ops = &P_live_ops;	Pinitsym(P);	/*	 * Open the /proc/pid files	 */	(void) sprintf(procname, "/proc/%d/", (int)pid);	fname = procname + strlen(procname);	(void) set_minfd();	/*	 * Request exclusive open to avoid grabbing someone else's	 * process and to prevent others from interfering afterwards.	 * If this fails and the 'PGRAB_FORCE' flag is set, attempt to	 * open non-exclusively.	 */	(void) strcpy(fname, "as");	omode = (flags & PGRAB_RDONLY) ? O_RDONLY : O_RDWR;	if (((fd = open(procname, omode | O_EXCL)) < 0 &&	    (fd = ((flags & PGRAB_FORCE)? open(procname, omode) : -1)) < 0) ||	    (fd = dupfd(fd, 0)) < 0) {		switch (errno) {		case ENOENT:			rc = G_NOPROC;			break;		case EACCES:		case EPERM:			rc = G_PERM;			break;		case EBUSY:			if (!(flags & PGRAB_FORCE) || geteuid() != 0) {				rc = G_BUSY;				break;			}			/* FALLTHROUGH */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -