166550Snyan/*-
266550Snyan * Copyright 1997 Sean Eric Fagan
366550Snyan *
466550Snyan * Redistribution and use in source and binary forms, with or without
5139749Simp * modification, are permitted provided that the following conditions
666550Snyan * are met:
766550Snyan * 1. Redistributions of source code must retain the above copyright
866550Snyan *    notice, this list of conditions and the following disclaimer.
9139749Simp * 2. Redistributions in binary form must reproduce the above copyright
1066550Snyan *    notice, this list of conditions and the following disclaimer in the
1166550Snyan *    documentation and/or other materials provided with the distribution.
1266550Snyan * 3. All advertising materials mentioning features or use of this software
1366550Snyan *    must display the following acknowledgement:
1466550Snyan *	This product includes software developed by Sean Eric Fagan
1566550Snyan * 4. Neither the name of the author may be used to endorse or promote
1666550Snyan *    products derived from this software without specific prior written
1766550Snyan *    permission.
1866550Snyan *
1966550Snyan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2066550Snyan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2166550Snyan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2266550Snyan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2366550Snyan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2466550Snyan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2566550Snyan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2666550Snyan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2766550Snyan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2866550Snyan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2966550Snyan * SUCH DAMAGE.
3066550Snyan */
3166550Snyan
3266550Snyan#include <sys/cdefs.h>
3366550Snyan__FBSDID("$FreeBSD$");
3466550Snyan
3566550Snyan/*
3666550Snyan * Various setup functions for truss.  Not the cleanest-written code,
3766550Snyan * I'm afraid.
3866550Snyan */
3966550Snyan
4066550Snyan#include <sys/param.h>
4166550Snyan#include <sys/types.h>
4266550Snyan#include <sys/ptrace.h>
4366550Snyan#include <sys/wait.h>
4466550Snyan
4566550Snyan#include <err.h>
4666550Snyan#include <errno.h>
4766550Snyan#include <fcntl.h>
4866550Snyan#include <signal.h>
4966550Snyan#include <stdio.h>
5066550Snyan#include <stdlib.h>
5166550Snyan#include <string.h>
5266550Snyan#include <time.h>
5366550Snyan#include <unistd.h>
5466550Snyan
5566550Snyan#include <machine/reg.h>
5666550Snyan
5766550Snyan#include "truss.h"
5866550Snyan#include "extern.h"
5966550Snyan
6066550Snyanstatic sig_atomic_t detaching;
6166550Snyan
6266550Snyan/*
6366550Snyan * setup_and_wait() is called to start a process.  All it really does
6466550Snyan * is fork(), set itself up to stop on exec or exit, and then exec
6566550Snyan * the given command.  At that point, the child process stops, and
6666550Snyan * the parent can wake up and deal with it.
6766550Snyan */
6866550Snyan
6966550Snyanint
7066550Snyansetup_and_wait(char *command[])
7166550Snyan{
7266550Snyan	pid_t pid;
7366550Snyan
7466550Snyan	pid = vfork();
7566550Snyan	if (pid == -1)
7666550Snyan		err(1, "fork failed");
7766550Snyan	if (pid == 0) {	/* Child */
7866550Snyan		ptrace(PT_TRACE_ME, 0, 0, 0);
7966550Snyan		execvp(command[0], command);
8066550Snyan		err(1, "execvp %s", command[0]);
8166550Snyan	}
8266550Snyan
8366550Snyan	/* Only in the parent here */
8466550Snyan	if (waitpid(pid, NULL, 0) < 0)
8566550Snyan		err(1, "unexpect stop in waitpid");
8666550Snyan
8766550Snyan	return (pid);
8866550Snyan}
8966550Snyan
9066550Snyan/*
9166550Snyan * start_tracing picks up where setup_and_wait() dropped off -- namely,
9266550Snyan * it sets the event mask for the given process id.  Called for both
9366550Snyan * monitoring an existing process and when we create our own.
9466550Snyan */
9566550Snyan
9666550Snyanint
9766550Snyanstart_tracing(pid_t pid)
9866550Snyan{
9966550Snyan	int ret, retry;
10066550Snyan
10166550Snyan	retry = 10;
10266550Snyan	do {
10366550Snyan		ret = ptrace(PT_ATTACH, pid, NULL, 0);
10466550Snyan		usleep(200);
10566550Snyan	} while (ret && retry-- > 0);
10666550Snyan	if (ret)
10766550Snyan		err(1, "can not attach to target process");
108147256Sbrooks
10966550Snyan	if (waitpid(pid, NULL, 0) < 0)
11066550Snyan		err(1, "Unexpect stop in waitpid");
11166550Snyan
11266550Snyan	return (0);
11366550Snyan}
11466550Snyan
11566550Snyan/*
11666550Snyan * Restore a process back to it's pre-truss state.
11766550Snyan * Called for SIGINT, SIGTERM, SIGQUIT.  This only
11866550Snyan * applies if truss was told to monitor an already-existing
11966550Snyan * process.
12066550Snyan */
12166550Snyan
12266550Snyanvoid
12366550Snyanrestore_proc(int signo __unused)
12466550Snyan{
12566550Snyan
12666550Snyan	detaching = 1;
12766550Snyan}
12866550Snyan
12966550Snyanstatic int
13066550Snyandetach_proc(pid_t pid)
13166550Snyan{
13266550Snyan	int waitval;
13366550Snyan
13466550Snyan	/* stop the child so that we can detach */
13566550Snyan	kill(pid, SIGSTOP);
13666550Snyan	if (waitpid(pid, &waitval, 0) < 0)
13766550Snyan		err(1, "Unexpected stop in waitpid");
13866550Snyan
13966550Snyan	if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0)
14066550Snyan		err(1, "Can not detach the process");
14166550Snyan
14266550Snyan	kill(pid, SIGCONT);
14366550Snyan
14466550Snyan	return (waitval);
14566550Snyan}
14666550Snyan
14766550Snyan/*
14866550Snyan * Change curthread member based on lwpid.
14966550Snyan * If it is a new thread, create a threadinfo structure
15066550Snyan */
15166550Snyanstatic void
15266550Snyanfind_thread(struct trussinfo *info, lwpid_t lwpid)
15366550Snyan{
15466550Snyan	struct threadinfo *np;
15566550Snyan
15666550Snyan	info->curthread = NULL;
15766550Snyan	SLIST_FOREACH(np, &info->threadlist, entries) {
15866550Snyan		if (np->tid == lwpid) {
15966550Snyan			info->curthread = np;
16066550Snyan			return;
16166550Snyan		}
16292739Salfred	}
16366550Snyan
16492739Salfred	np = (struct threadinfo *)calloc(1, sizeof(struct threadinfo));
16566550Snyan	if (np == NULL)
16666550Snyan		err(1, "calloc() failed");
16766550Snyan	np->tid = lwpid;
16866550Snyan	SLIST_INSERT_HEAD(&info->threadlist, np, entries);
16966550Snyan	info->curthread = np;
17066550Snyan}
17166550Snyan
17266550Snyan/*
17366550Snyan * Start the traced process and wait until it stoped.
17492739Salfred * Fill trussinfo structure.
17566550Snyan * When this even returns, the traced process is in stop state.
17692739Salfred */
17766550Snyanvoid
17892739Salfredwaitevent(struct trussinfo *info)
17966550Snyan{
18092739Salfred	struct ptrace_lwpinfo lwpinfo;
18166550Snyan	static int pending_signal = 0;
18292739Salfred	int waitval;
18366550Snyan
18466550Snyan	ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
18566550Snyan	pending_signal = 0;
18666550Snyan
18766550Snyandetach:
18866550Snyan	if (detaching) {
18966550Snyan		waitval = detach_proc(info->pid);
19066550Snyan		info->pr_why = S_DETACHED;
19192739Salfred		info->pr_data = WEXITSTATUS(waitval);
19292739Salfred		return;
19392739Salfred	}
19492739Salfred
19566550Snyan	if (waitpid(info->pid, &waitval, 0) == -1) {
19666550Snyan		if (errno == EINTR)
19766550Snyan			goto detach;
19892739Salfred		err(1, "Unexpected stop in waitpid");
19992739Salfred	}
20066550Snyan
20166550Snyan	if (WIFCONTINUED(waitval)) {
20266550Snyan		info->pr_why = S_NONE;
203181298Sjhb		return;
204181298Sjhb	}
205181298Sjhb	if (WIFEXITED(waitval)) {
20666550Snyan		info->pr_why = S_EXIT;
20766550Snyan		info->pr_data = WEXITSTATUS(waitval);
208181298Sjhb		return;
209181298Sjhb	}
210181298Sjhb	if (WIFSTOPPED(waitval)) {
211181298Sjhb		ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo,
21266550Snyan		    sizeof(lwpinfo));
21366550Snyan		find_thread(info, lwpinfo.pl_lwpid);
21466550Snyan		switch (WSTOPSIG(waitval)) {
21566550Snyan		case SIGTRAP:
21666550Snyan			if (lwpinfo.pl_flags & PL_FLAG_SCE) {
21766550Snyan				info->pr_why = S_SCE;
21866550Snyan				info->curthread->in_syscall = 1;
21966550Snyan				break;
22066550Snyan			} else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
22166550Snyan				info->pr_why = S_SCX;
22266550Snyan				info->curthread->in_syscall = 0;
22366550Snyan				break;
22466550Snyan			} else {
22566550Snyan				errx(1,
22666550Snyan		   "pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX",
22766550Snyan				    lwpinfo.pl_flags);
22866550Snyan			}
22966550Snyan		default:
23066550Snyan			info->pr_why = S_SIG;
23166550Snyan			info->pr_data = WSTOPSIG(waitval);
23266550Snyan			pending_signal = info->pr_data;
23366550Snyan			break;
23466550Snyan		}
23566550Snyan	}
23666550Snyan	if (WIFSIGNALED(waitval)) {
23766550Snyan		info->pr_why = S_EXIT;
23866550Snyan		info->pr_data = 0;
23966550Snyan		return;
24066550Snyan	}
24166550Snyan}
24266550Snyan