1139749Simp/*-
265942Sgibbs * Copyright (c) 2008 John Birrell (jb@freebsd.org)
339220Sgibbs * All rights reserved.
471717Sgibbs *
595378Sgibbs * Redistribution and use in source and binary forms, with or without
639220Sgibbs * modification, are permitted provided that the following conditions
739220Sgibbs * are met:
839220Sgibbs * 1. Redistributions of source code must retain the above copyright
939220Sgibbs *    notice, this list of conditions and the following disclaimer.
1039220Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1139220Sgibbs *    notice, this list of conditions and the following disclaimer in the
1239220Sgibbs *    documentation and/or other materials provided with the distribution.
1354211Sgibbs *
1495378Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1595378Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1695378Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1795378Sgibbs * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1895378Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1995378Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2095378Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2195378Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2239220Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2354211Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2495378Sgibbs * SUCH DAMAGE.
2595378Sgibbs *
2639220Sgibbs * $FreeBSD$
2795378Sgibbs */
2895378Sgibbs
2995378Sgibbs#include <sys/types.h>
3095378Sgibbs#include <sys/sysctl.h>
3195378Sgibbs#include <sys/wait.h>
3295378Sgibbs
3339220Sgibbs#include <err.h>
3439220Sgibbs#include <errno.h>
3595378Sgibbs#include <fcntl.h>
3695378Sgibbs#include <limits.h>
3795378Sgibbs#include <stdlib.h>
3895378Sgibbs#include <string.h>
3939220Sgibbs#include <unistd.h>
40123579Sgibbs
4165942Sgibbs#include "_libproc.h"
4250477Speter
4339220Sgibbsstatic int	proc_init(pid_t, int, int, struct proc_handle *);
4439220Sgibbs
4539220Sgibbsstatic int
4639220Sgibbsproc_init(pid_t pid, int flags, int status, struct proc_handle *phdl)
4739220Sgibbs{
4865942Sgibbs	int mib[4], error;
4963457Sgibbs	size_t len;
5039220Sgibbs
5165942Sgibbs	memset(phdl, 0, sizeof(*phdl));
5265942Sgibbs	phdl->pid = pid;
5365942Sgibbs	phdl->flags = flags;
5495378Sgibbs	phdl->status = status;
5539220Sgibbs
5665942Sgibbs	mib[0] = CTL_KERN;
5745965Sgibbs	mib[1] = KERN_PROC;
5845965Sgibbs	mib[2] = KERN_PROC_PATHNAME;
5945965Sgibbs	mib[3] = pid;
6045965Sgibbs	len = sizeof(phdl->execname);
6145965Sgibbs	if (sysctl(mib, 4, phdl->execname, &len, NULL, 0) != 0) {
6245965Sgibbs		error = errno;
6345965Sgibbs		DPRINTF("ERROR: cannot get pathname for child process %d", pid);
6445965Sgibbs		return (error);
6565942Sgibbs	}
6665942Sgibbs	if (len == 0)
6765942Sgibbs		phdl->execname[0] = '\0';
6865942Sgibbs
6965942Sgibbs	return (0);
7065942Sgibbs}
7165942Sgibbs
7265942Sgibbsint
7365942Sgibbsproc_attach(pid_t pid, int flags, struct proc_handle **pphdl)
7465942Sgibbs{
7565942Sgibbs	struct proc_handle *phdl;
7665942Sgibbs	int error = 0;
7765942Sgibbs	int status;
7865942Sgibbs
7965942Sgibbs	if (pid == 0 || pid == getpid())
8065942Sgibbs		return (EINVAL);
8165942Sgibbs
8265942Sgibbs	/*
8365942Sgibbs	 * Allocate memory for the process handle, a structure containing
8465942Sgibbs	 * all things related to the process.
8565942Sgibbs	 */
8665942Sgibbs	if ((phdl = malloc(sizeof(struct proc_handle))) == NULL)
8765942Sgibbs		return (ENOMEM);
8865942Sgibbs
8965942Sgibbs	elf_version(EV_CURRENT);
9065942Sgibbs
9165942Sgibbs	error = proc_init(pid, flags, PS_RUN, phdl);
9265942Sgibbs	if (error != 0)
9365942Sgibbs		goto out;
9465942Sgibbs
9565942Sgibbs	if (ptrace(PT_ATTACH, phdl->pid, 0, 0) != 0) {
96115333Sgibbs		error = errno;
9765942Sgibbs		DPRINTF("ERROR: cannot ptrace child process %d", pid);
9865942Sgibbs		goto out;
9965942Sgibbs	}
10065942Sgibbs
101109590Sgibbs	/* Wait for the child process to stop. */
102109590Sgibbs	if (waitpid(pid, &status, WUNTRACED) == -1) {
103109590Sgibbs		error = errno;
104109590Sgibbs		DPRINTF("ERROR: child process %d didn't stop as expected", pid);
105109590Sgibbs		goto out;
106109590Sgibbs	}
107109590Sgibbs
108109590Sgibbs	/* Check for an unexpected status. */
10965942Sgibbs	if (WIFSTOPPED(status) == 0)
11065942Sgibbs		DPRINTFX("ERROR: child process %d status 0x%x", pid, status);
11165942Sgibbs	else
11265942Sgibbs		phdl->status = PS_STOP;
11365942Sgibbs
11465942Sgibbsout:
11565942Sgibbs	if (error)
11668579Sgibbs		proc_free(phdl);
11768579Sgibbs	else
11868579Sgibbs		*pphdl = phdl;
11968579Sgibbs	return (error);
12068579Sgibbs}
12165942Sgibbs
12245965Sgibbsint
12363457Sgibbsproc_create(const char *file, char * const *argv, proc_child_func *pcf,
12463457Sgibbs    void *child_arg, struct proc_handle **pphdl)
12563457Sgibbs{
12663457Sgibbs	struct proc_handle *phdl;
12763457Sgibbs	int error = 0;
12863457Sgibbs	int status;
12970204Sgibbs	pid_t pid;
13070204Sgibbs
13170204Sgibbs	/*
13263457Sgibbs	 * Allocate memory for the process handle, a structure containing
13370204Sgibbs	 * all things related to the process.
13463457Sgibbs	 */
13563457Sgibbs	if ((phdl = malloc(sizeof(struct proc_handle))) == NULL)
13645965Sgibbs		return (ENOMEM);
13745965Sgibbs
13839220Sgibbs	elf_version(EV_CURRENT);
13939220Sgibbs
14045965Sgibbs	/* Fork a new process. */
14171390Sgibbs	if ((pid = vfork()) == -1)
14271390Sgibbs		error = errno;
14371390Sgibbs	else if (pid == 0) {
14471390Sgibbs		/* The child expects to be traced. */
14571390Sgibbs		if (ptrace(PT_TRACE_ME, 0, 0, 0) != 0)
14671390Sgibbs			_exit(1);
14771390Sgibbs
14865942Sgibbs		if (pcf != NULL)
14965942Sgibbs			(*pcf)(child_arg);
15065942Sgibbs
15165942Sgibbs		/* Execute the specified file: */
15271390Sgibbs		execvp(file, argv);
15371390Sgibbs
15471390Sgibbs		/* Couldn't execute the file. */
15571390Sgibbs		_exit(2);
15671390Sgibbs	} else {
15771390Sgibbs		/* The parent owns the process handle. */
15871390Sgibbs		error = proc_init(pid, 0, PS_IDLE, phdl);
15971390Sgibbs		if (error != 0)
16071390Sgibbs			goto bad;
16171390Sgibbs
16271390Sgibbs		/* Wait for the child process to stop. */
16372640Sasmodai		if (waitpid(pid, &status, WUNTRACED) == -1) {
16445965Sgibbs			error = errno;
16571390Sgibbs			DPRINTF("ERROR: child process %d didn't stop as expected", pid);
16645965Sgibbs			goto bad;
16765942Sgibbs		}
16895378Sgibbs
16995378Sgibbs		/* Check for an unexpected status. */
17095378Sgibbs		if (WIFSTOPPED(status) == 0) {
17195378Sgibbs			error = errno;
17295378Sgibbs			DPRINTFX("ERROR: child process %d status 0x%x", pid, status);
17395378Sgibbs			goto bad;
17495378Sgibbs		} else
17565942Sgibbs			phdl->status = PS_STOP;
17665942Sgibbs	}
17765942Sgibbsbad:
17865942Sgibbs	if (error)
17965942Sgibbs		proc_free(phdl);
18039220Sgibbs	else
18165942Sgibbs		*pphdl = phdl;
182102671Sgibbs	return (error);
18339220Sgibbs}
184136711Sgibbs
185136711Sgibbsvoid
186136711Sgibbsproc_free(struct proc_handle *phdl)
187136711Sgibbs{
18865942Sgibbs	free(phdl);
18963457Sgibbs}
19065942Sgibbs