1/*-
2 * Copyright (c) 2008 John Birrell (jb@freebsd.org)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <sys/types.h>
30#include <sys/sysctl.h>
31#include <sys/wait.h>
32
33#include <err.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <limits.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#include "_libproc.h"
42
43static int	proc_init(pid_t, int, int, struct proc_handle *);
44
45static int
46proc_init(pid_t pid, int flags, int status, struct proc_handle *phdl)
47{
48	int mib[4], error;
49	size_t len;
50
51	memset(phdl, 0, sizeof(*phdl));
52	phdl->pid = pid;
53	phdl->flags = flags;
54	phdl->status = status;
55
56	mib[0] = CTL_KERN;
57	mib[1] = KERN_PROC;
58	mib[2] = KERN_PROC_PATHNAME;
59	mib[3] = pid;
60	len = sizeof(phdl->execname);
61	if (sysctl(mib, 4, phdl->execname, &len, NULL, 0) != 0) {
62		error = errno;
63		DPRINTF("ERROR: cannot get pathname for child process %d", pid);
64		return (error);
65	}
66	if (len == 0)
67		phdl->execname[0] = '\0';
68
69	return (0);
70}
71
72int
73proc_attach(pid_t pid, int flags, struct proc_handle **pphdl)
74{
75	struct proc_handle *phdl;
76	int error = 0;
77	int status;
78
79	if (pid == 0 || pid == getpid())
80		return (EINVAL);
81
82	/*
83	 * Allocate memory for the process handle, a structure containing
84	 * all things related to the process.
85	 */
86	if ((phdl = malloc(sizeof(struct proc_handle))) == NULL)
87		return (ENOMEM);
88
89	elf_version(EV_CURRENT);
90
91	error = proc_init(pid, flags, PS_RUN, phdl);
92	if (error != 0)
93		goto out;
94
95	if (ptrace(PT_ATTACH, phdl->pid, 0, 0) != 0) {
96		error = errno;
97		DPRINTF("ERROR: cannot ptrace child process %d", pid);
98		goto out;
99	}
100
101	/* Wait for the child process to stop. */
102	if (waitpid(pid, &status, WUNTRACED) == -1) {
103		error = errno;
104		DPRINTF("ERROR: child process %d didn't stop as expected", pid);
105		goto out;
106	}
107
108	/* Check for an unexpected status. */
109	if (WIFSTOPPED(status) == 0)
110		DPRINTFX("ERROR: child process %d status 0x%x", pid, status);
111	else
112		phdl->status = PS_STOP;
113
114out:
115	if (error)
116		proc_free(phdl);
117	else
118		*pphdl = phdl;
119	return (error);
120}
121
122int
123proc_create(const char *file, char * const *argv, proc_child_func *pcf,
124    void *child_arg, struct proc_handle **pphdl)
125{
126	struct proc_handle *phdl;
127	int error = 0;
128	int status;
129	pid_t pid;
130
131	/*
132	 * Allocate memory for the process handle, a structure containing
133	 * all things related to the process.
134	 */
135	if ((phdl = malloc(sizeof(struct proc_handle))) == NULL)
136		return (ENOMEM);
137
138	elf_version(EV_CURRENT);
139
140	/* Fork a new process. */
141	if ((pid = vfork()) == -1)
142		error = errno;
143	else if (pid == 0) {
144		/* The child expects to be traced. */
145		if (ptrace(PT_TRACE_ME, 0, 0, 0) != 0)
146			_exit(1);
147
148		if (pcf != NULL)
149			(*pcf)(child_arg);
150
151		/* Execute the specified file: */
152		execvp(file, argv);
153
154		/* Couldn't execute the file. */
155		_exit(2);
156	} else {
157		/* The parent owns the process handle. */
158		error = proc_init(pid, 0, PS_IDLE, phdl);
159		if (error != 0)
160			goto bad;
161
162		/* Wait for the child process to stop. */
163		if (waitpid(pid, &status, WUNTRACED) == -1) {
164			error = errno;
165			DPRINTF("ERROR: child process %d didn't stop as expected", pid);
166			goto bad;
167		}
168
169		/* Check for an unexpected status. */
170		if (WIFSTOPPED(status) == 0) {
171			error = errno;
172			DPRINTFX("ERROR: child process %d status 0x%x", pid, status);
173			goto bad;
174		} else
175			phdl->status = PS_STOP;
176	}
177bad:
178	if (error)
179		proc_free(phdl);
180	else
181		*pphdl = phdl;
182	return (error);
183}
184
185void
186proc_free(struct proc_handle *phdl)
187{
188	free(phdl);
189}
190