xref: /netbsd-src/usr.bin/make/filemon/filemon_ktrace.c (revision cc821a91f9f2abce8a2e014e69b7998124cedcd1)
1*cc821a91Srillig /*	$NetBSD: filemon_ktrace.c,v 1.15 2021/07/31 09:30:17 rillig Exp $	*/
2bea0f8c1Sriastradh 
385aee7a6Srillig /*
4bea0f8c1Sriastradh  * Copyright (c) 2019 The NetBSD Foundation, Inc.
5bea0f8c1Sriastradh  * All rights reserved.
6bea0f8c1Sriastradh  *
7bea0f8c1Sriastradh  * This code is derived from software contributed to The NetBSD Foundation
8bea0f8c1Sriastradh  * by Taylor R. Campbell.
9bea0f8c1Sriastradh  *
10bea0f8c1Sriastradh  * Redistribution and use in source and binary forms, with or without
11bea0f8c1Sriastradh  * modification, are permitted provided that the following conditions
12bea0f8c1Sriastradh  * are met:
13bea0f8c1Sriastradh  * 1. Redistributions of source code must retain the above copyright
14bea0f8c1Sriastradh  *    notice, this list of conditions and the following disclaimer.
15bea0f8c1Sriastradh  * 2. Redistributions in binary form must reproduce the above copyright
16bea0f8c1Sriastradh  *    notice, this list of conditions and the following disclaimer in the
17bea0f8c1Sriastradh  *    documentation and/or other materials provided with the distribution.
18bea0f8c1Sriastradh  *
19bea0f8c1Sriastradh  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20bea0f8c1Sriastradh  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21bea0f8c1Sriastradh  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22bea0f8c1Sriastradh  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23bea0f8c1Sriastradh  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24bea0f8c1Sriastradh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25bea0f8c1Sriastradh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26bea0f8c1Sriastradh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27bea0f8c1Sriastradh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28bea0f8c1Sriastradh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29bea0f8c1Sriastradh  * POSSIBILITY OF SUCH DAMAGE.
30bea0f8c1Sriastradh  */
31bea0f8c1Sriastradh 
32b8bc1cd7Sriastradh #define _KERNTYPES		/* register_t */
33b8bc1cd7Sriastradh 
34bea0f8c1Sriastradh #include "filemon.h"
35bea0f8c1Sriastradh 
36bea0f8c1Sriastradh #include <sys/param.h>
37bea0f8c1Sriastradh #include <sys/types.h>
38bea0f8c1Sriastradh #include <sys/rbtree.h>
39bea0f8c1Sriastradh #include <sys/syscall.h>
40bea0f8c1Sriastradh #include <sys/time.h>
41bea0f8c1Sriastradh #include <sys/uio.h>
42bea0f8c1Sriastradh #include <sys/wait.h>
43bea0f8c1Sriastradh 
44bea0f8c1Sriastradh #include <sys/ktrace.h>
45bea0f8c1Sriastradh 
46bea0f8c1Sriastradh #include <assert.h>
47bea0f8c1Sriastradh #include <err.h>
48bea0f8c1Sriastradh #include <errno.h>
49bea0f8c1Sriastradh #include <fcntl.h>
50bea0f8c1Sriastradh #include <stdbool.h>
51bea0f8c1Sriastradh #include <stddef.h>
52bea0f8c1Sriastradh #include <stdio.h>
53bea0f8c1Sriastradh #include <stdlib.h>
54bea0f8c1Sriastradh #include <string.h>
55bea0f8c1Sriastradh #include <unistd.h>
56bea0f8c1Sriastradh 
57bea0f8c1Sriastradh #ifndef AT_CWD
58bea0f8c1Sriastradh #define AT_CWD -1
59bea0f8c1Sriastradh #endif
60bea0f8c1Sriastradh 
61bea0f8c1Sriastradh struct filemon;
62bea0f8c1Sriastradh struct filemon_key;
63bea0f8c1Sriastradh struct filemon_state;
64bea0f8c1Sriastradh 
65bea0f8c1Sriastradh typedef struct filemon_state *filemon_syscall_t(struct filemon *,
66bea0f8c1Sriastradh     const struct filemon_key *, const struct ktr_syscall *);
67bea0f8c1Sriastradh 
68bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_chdir;
69bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_execve;
70bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_exit;
71bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_fork;
72bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_link;
73bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_open;
74bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_openat;
75bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_symlink;
76bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_unlink;
77bea0f8c1Sriastradh static filemon_syscall_t filemon_sys_rename;
78bea0f8c1Sriastradh 
79bea0f8c1Sriastradh static filemon_syscall_t *const filemon_syscalls[] = {
80bea0f8c1Sriastradh 	[SYS_chdir] = &filemon_sys_chdir,
81bea0f8c1Sriastradh 	[SYS_execve] = &filemon_sys_execve,
82bea0f8c1Sriastradh 	[SYS_exit] = &filemon_sys_exit,
83bea0f8c1Sriastradh 	[SYS_fork] = &filemon_sys_fork,
84bea0f8c1Sriastradh 	[SYS_link] = &filemon_sys_link,
85bea0f8c1Sriastradh 	[SYS_open] = &filemon_sys_open,
86bea0f8c1Sriastradh 	[SYS_openat] = &filemon_sys_openat,
87bea0f8c1Sriastradh 	[SYS_symlink] = &filemon_sys_symlink,
88bea0f8c1Sriastradh 	[SYS_unlink] = &filemon_sys_unlink,
89bea0f8c1Sriastradh 	[SYS_rename] = &filemon_sys_rename,
90bea0f8c1Sriastradh };
91bea0f8c1Sriastradh 
92bea0f8c1Sriastradh struct filemon {
93bea0f8c1Sriastradh 	int			ktrfd;	/* kernel writes ktrace events here */
94bea0f8c1Sriastradh 	FILE			*in;	/* we read ktrace events from here */
95bea0f8c1Sriastradh 	FILE			*out;	/* we write filemon events to here */
96bea0f8c1Sriastradh 	rb_tree_t		active;
97bea0f8c1Sriastradh 	pid_t			child;
98bea0f8c1Sriastradh 
99bea0f8c1Sriastradh 	/* I/O state machine.  */
100bea0f8c1Sriastradh 	enum {
101bea0f8c1Sriastradh 		FILEMON_START = 0,
102bea0f8c1Sriastradh 		FILEMON_HEADER,
103bea0f8c1Sriastradh 		FILEMON_PAYLOAD,
104bea0f8c1Sriastradh 		FILEMON_ERROR,
105bea0f8c1Sriastradh 	}			state;
106bea0f8c1Sriastradh 	unsigned char		*p;
107bea0f8c1Sriastradh 	size_t			resid;
108bea0f8c1Sriastradh 
109bea0f8c1Sriastradh 	/* I/O buffer.  */
110bea0f8c1Sriastradh 	struct ktr_header	hdr;
111bea0f8c1Sriastradh 	union {
112bea0f8c1Sriastradh 		struct ktr_syscall	syscall;
113bea0f8c1Sriastradh 		struct ktr_sysret	sysret;
114bea0f8c1Sriastradh 		char			namei[PATH_MAX];
115bea0f8c1Sriastradh 		unsigned char		buf[4096];
116bea0f8c1Sriastradh 	}			payload;
117bea0f8c1Sriastradh };
118bea0f8c1Sriastradh 
119bea0f8c1Sriastradh struct filemon_state {
120bea0f8c1Sriastradh 	struct filemon_key {
121bea0f8c1Sriastradh 		pid_t		pid;
122bea0f8c1Sriastradh 		lwpid_t		lid;
123bea0f8c1Sriastradh 	}		key;
124bea0f8c1Sriastradh 	struct rb_node	node;
125bea0f8c1Sriastradh 	int		syscode;
126bea0f8c1Sriastradh 	void		(*show)(struct filemon *, const struct filemon_state *,
127bea0f8c1Sriastradh 			    const struct ktr_sysret *);
128bea0f8c1Sriastradh 	unsigned	i;
129bea0f8c1Sriastradh 	unsigned	npath;
130bea0f8c1Sriastradh 	char		*path[/*npath*/];
131bea0f8c1Sriastradh };
132bea0f8c1Sriastradh 
133462cbdfaSrillig /*ARGSUSED*/
134bea0f8c1Sriastradh static int
compare_filemon_states(void * cookie,const void * na,const void * nb)135bea0f8c1Sriastradh compare_filemon_states(void *cookie, const void *na, const void *nb)
136bea0f8c1Sriastradh {
137bea0f8c1Sriastradh 	const struct filemon_state *Sa = na;
138bea0f8c1Sriastradh 	const struct filemon_state *Sb = nb;
139bea0f8c1Sriastradh 
140bea0f8c1Sriastradh 	if (Sa->key.pid < Sb->key.pid)
141bea0f8c1Sriastradh 		return -1;
142bea0f8c1Sriastradh 	if (Sa->key.pid > Sb->key.pid)
143bea0f8c1Sriastradh 		return +1;
144bea0f8c1Sriastradh 	if (Sa->key.lid < Sb->key.lid)
145bea0f8c1Sriastradh 		return -1;
146bea0f8c1Sriastradh 	if (Sa->key.lid > Sb->key.lid)
147bea0f8c1Sriastradh 		return +1;
148bea0f8c1Sriastradh 	return 0;
149bea0f8c1Sriastradh }
150bea0f8c1Sriastradh 
151462cbdfaSrillig /*ARGSUSED*/
152bea0f8c1Sriastradh static int
compare_filemon_key(void * cookie,const void * n,const void * k)153bea0f8c1Sriastradh compare_filemon_key(void *cookie, const void *n, const void *k)
154bea0f8c1Sriastradh {
155bea0f8c1Sriastradh 	const struct filemon_state *S = n;
156bea0f8c1Sriastradh 	const struct filemon_key *key = k;
157bea0f8c1Sriastradh 
158bea0f8c1Sriastradh 	if (S->key.pid < key->pid)
159bea0f8c1Sriastradh 		return -1;
160bea0f8c1Sriastradh 	if (S->key.pid > key->pid)
161bea0f8c1Sriastradh 		return +1;
162bea0f8c1Sriastradh 	if (S->key.lid < key->lid)
163bea0f8c1Sriastradh 		return -1;
164bea0f8c1Sriastradh 	if (S->key.lid > key->lid)
165bea0f8c1Sriastradh 		return +1;
166bea0f8c1Sriastradh 	return 0;
167bea0f8c1Sriastradh }
168bea0f8c1Sriastradh 
169bea0f8c1Sriastradh static const rb_tree_ops_t filemon_rb_ops = {
170bea0f8c1Sriastradh 	.rbto_compare_nodes = &compare_filemon_states,
171bea0f8c1Sriastradh 	.rbto_compare_key = &compare_filemon_key,
172bea0f8c1Sriastradh 	.rbto_node_offset = offsetof(struct filemon_state, node),
173bea0f8c1Sriastradh 	.rbto_context = NULL,
174bea0f8c1Sriastradh };
175bea0f8c1Sriastradh 
176bea0f8c1Sriastradh /*
177bea0f8c1Sriastradh  * filemon_path()
178bea0f8c1Sriastradh  *
179bea0f8c1Sriastradh  *	Return a pointer to a constant string denoting the `path' of
180bea0f8c1Sriastradh  *	the filemon.
181bea0f8c1Sriastradh  */
182bea0f8c1Sriastradh const char *
filemon_path(void)183bea0f8c1Sriastradh filemon_path(void)
184bea0f8c1Sriastradh {
185bea0f8c1Sriastradh 
186bea0f8c1Sriastradh 	return "ktrace";
187bea0f8c1Sriastradh }
188bea0f8c1Sriastradh 
189bea0f8c1Sriastradh /*
190bea0f8c1Sriastradh  * filemon_open()
191bea0f8c1Sriastradh  *
192bea0f8c1Sriastradh  *	Allocate a filemon descriptor.  Returns NULL and sets errno on
193bea0f8c1Sriastradh  *	failure.
194bea0f8c1Sriastradh  */
195bea0f8c1Sriastradh struct filemon *
filemon_open(void)196bea0f8c1Sriastradh filemon_open(void)
197bea0f8c1Sriastradh {
198bea0f8c1Sriastradh 	struct filemon *F;
199bea0f8c1Sriastradh 	int ktrpipe[2];
200bea0f8c1Sriastradh 	int error;
201bea0f8c1Sriastradh 
202bea0f8c1Sriastradh 	/* Allocate and zero a struct filemon object.  */
203143a3267Srillig 	F = calloc(1, sizeof *F);
204bea0f8c1Sriastradh 	if (F == NULL)
205bea0f8c1Sriastradh 		return NULL;
206bea0f8c1Sriastradh 
207bea0f8c1Sriastradh 	/* Create a pipe for ktrace events.  */
208bea0f8c1Sriastradh 	if (pipe2(ktrpipe, O_CLOEXEC|O_NONBLOCK) == -1) {
209bea0f8c1Sriastradh 		error = errno;
210bea0f8c1Sriastradh 		goto fail0;
211bea0f8c1Sriastradh 	}
212bea0f8c1Sriastradh 
213bea0f8c1Sriastradh 	/* Create a file stream for reading the ktrace events.  */
214bea0f8c1Sriastradh 	if ((F->in = fdopen(ktrpipe[0], "r")) == NULL) {
215bea0f8c1Sriastradh 		error = errno;
216bea0f8c1Sriastradh 		goto fail1;
217bea0f8c1Sriastradh 	}
218bea0f8c1Sriastradh 	ktrpipe[0] = -1;	/* claimed by fdopen */
219bea0f8c1Sriastradh 
220bea0f8c1Sriastradh 	/*
221bea0f8c1Sriastradh 	 * Set the fd for writing ktrace events and initialize the
222bea0f8c1Sriastradh 	 * rbtree.  The rest can be safely initialized to zero.
223bea0f8c1Sriastradh 	 */
224bea0f8c1Sriastradh 	F->ktrfd = ktrpipe[1];
225bea0f8c1Sriastradh 	rb_tree_init(&F->active, &filemon_rb_ops);
226bea0f8c1Sriastradh 
227bea0f8c1Sriastradh 	/* Success!  */
228bea0f8c1Sriastradh 	return F;
229bea0f8c1Sriastradh 
230bea0f8c1Sriastradh fail1:	(void)close(ktrpipe[0]);
231bea0f8c1Sriastradh 	(void)close(ktrpipe[1]);
232bea0f8c1Sriastradh fail0:	free(F);
233bea0f8c1Sriastradh 	errno = error;
234bea0f8c1Sriastradh 	return NULL;
235bea0f8c1Sriastradh }
236bea0f8c1Sriastradh 
237bea0f8c1Sriastradh /*
238bea0f8c1Sriastradh  * filemon_closefd(F)
239bea0f8c1Sriastradh  *
240bea0f8c1Sriastradh  *	Internal subroutine to try to flush and close the output file.
241bea0f8c1Sriastradh  *	If F is not open for output, do nothing.  Never leaves F open
242bea0f8c1Sriastradh  *	for output even on failure.  Returns 0 on success; sets errno
243bea0f8c1Sriastradh  *	and return -1 on failure.
244bea0f8c1Sriastradh  */
245bea0f8c1Sriastradh static int
filemon_closefd(struct filemon * F)246bea0f8c1Sriastradh filemon_closefd(struct filemon *F)
247bea0f8c1Sriastradh {
248bea0f8c1Sriastradh 	int error = 0;
249bea0f8c1Sriastradh 
250bea0f8c1Sriastradh 	/* If we're not open, nothing to do.  */
251bea0f8c1Sriastradh 	if (F->out == NULL)
252bea0f8c1Sriastradh 		return 0;
253bea0f8c1Sriastradh 
254bea0f8c1Sriastradh 	/*
255bea0f8c1Sriastradh 	 * Flush it, close it, and null it unconditionally, but be
256bea0f8c1Sriastradh 	 * careful to return the earliest error in errno.
257bea0f8c1Sriastradh 	 */
258bea0f8c1Sriastradh 	if (fflush(F->out) == EOF && error == 0)
259bea0f8c1Sriastradh 		error = errno;
260bea0f8c1Sriastradh 	if (fclose(F->out) == EOF && error == 0)
261bea0f8c1Sriastradh 		error = errno;
262bea0f8c1Sriastradh 	F->out = NULL;
263bea0f8c1Sriastradh 
264bea0f8c1Sriastradh 	/* Set errno and return -1 if anything went wrong.  */
2654adbce36Srillig 	if (error != 0) {
266bea0f8c1Sriastradh 		errno = error;
267bea0f8c1Sriastradh 		return -1;
268bea0f8c1Sriastradh 	}
269bea0f8c1Sriastradh 
270bea0f8c1Sriastradh 	/* Success!  */
271bea0f8c1Sriastradh 	return 0;
272bea0f8c1Sriastradh }
273bea0f8c1Sriastradh 
274bea0f8c1Sriastradh /*
275bea0f8c1Sriastradh  * filemon_setfd(F, fd)
276bea0f8c1Sriastradh  *
277bea0f8c1Sriastradh  *	Cause filemon activity on F to be sent to fd.  Claims ownership
278bea0f8c1Sriastradh  *	of fd; caller should not use fd afterward, and any duplicates
279bea0f8c1Sriastradh  *	of fd may see their file positions changed.
280bea0f8c1Sriastradh  */
281bea0f8c1Sriastradh int
filemon_setfd(struct filemon * F,int fd)282bea0f8c1Sriastradh filemon_setfd(struct filemon *F, int fd)
283bea0f8c1Sriastradh {
284bea0f8c1Sriastradh 
285bea0f8c1Sriastradh 	/*
286bea0f8c1Sriastradh 	 * Close an existing output file if done.  Fail now if there's
287bea0f8c1Sriastradh 	 * an error closing.
288bea0f8c1Sriastradh 	 */
289bea0f8c1Sriastradh 	if ((filemon_closefd(F)) == -1)
290bea0f8c1Sriastradh 		return -1;
291bea0f8c1Sriastradh 	assert(F->out == NULL);
292bea0f8c1Sriastradh 
293bea0f8c1Sriastradh 	/* Open a file stream and claim ownership of the fd.  */
294bea0f8c1Sriastradh 	if ((F->out = fdopen(fd, "a")) == NULL)
295bea0f8c1Sriastradh 		return -1;
296bea0f8c1Sriastradh 
297bea0f8c1Sriastradh 	/*
298bea0f8c1Sriastradh 	 * Print the opening output.  Any failure will be deferred
299bea0f8c1Sriastradh 	 * until closing.  For hysterical raisins, we show the parent
300bea0f8c1Sriastradh 	 * pid, not the child pid.
301bea0f8c1Sriastradh 	 */
302bea0f8c1Sriastradh 	fprintf(F->out, "# filemon version 4\n");
303bea0f8c1Sriastradh 	fprintf(F->out, "# Target pid %jd\n", (intmax_t)getpid());
304bea0f8c1Sriastradh 	fprintf(F->out, "V 4\n");
305bea0f8c1Sriastradh 
306bea0f8c1Sriastradh 	/* Success!  */
307bea0f8c1Sriastradh 	return 0;
308bea0f8c1Sriastradh }
309bea0f8c1Sriastradh 
310bea0f8c1Sriastradh /*
311bea0f8c1Sriastradh  * filemon_setpid_parent(F, pid)
312bea0f8c1Sriastradh  *
313bea0f8c1Sriastradh  *	Set the traced pid, from the parent.  Never fails.
314bea0f8c1Sriastradh  */
315bea0f8c1Sriastradh void
filemon_setpid_parent(struct filemon * F,pid_t pid)316bea0f8c1Sriastradh filemon_setpid_parent(struct filemon *F, pid_t pid)
317bea0f8c1Sriastradh {
318bea0f8c1Sriastradh 
319bea0f8c1Sriastradh 	F->child = pid;
320bea0f8c1Sriastradh }
321bea0f8c1Sriastradh 
322bea0f8c1Sriastradh /*
323bea0f8c1Sriastradh  * filemon_setpid_child(F, pid)
324bea0f8c1Sriastradh  *
325bea0f8c1Sriastradh  *	Set the traced pid, from the child.  Returns 0 on success; sets
326bea0f8c1Sriastradh  *	errno and returns -1 on failure.
327bea0f8c1Sriastradh  */
328bea0f8c1Sriastradh int
filemon_setpid_child(const struct filemon * F,pid_t pid)329bea0f8c1Sriastradh filemon_setpid_child(const struct filemon *F, pid_t pid)
330bea0f8c1Sriastradh {
331bea0f8c1Sriastradh 	int ops, trpoints;
332bea0f8c1Sriastradh 
333bea0f8c1Sriastradh 	ops = KTROP_SET|KTRFLAG_DESCEND;
334bea0f8c1Sriastradh 	trpoints = KTRFACv2;
335bea0f8c1Sriastradh 	trpoints |= KTRFAC_SYSCALL|KTRFAC_NAMEI|KTRFAC_SYSRET;
336bea0f8c1Sriastradh 	trpoints |= KTRFAC_INHERIT;
337bea0f8c1Sriastradh 	if (fktrace(F->ktrfd, ops, trpoints, pid) == -1)
338bea0f8c1Sriastradh 		return -1;
339bea0f8c1Sriastradh 
340bea0f8c1Sriastradh 	return 0;
341bea0f8c1Sriastradh }
342bea0f8c1Sriastradh 
343bea0f8c1Sriastradh /*
344bea0f8c1Sriastradh  * filemon_close(F)
345bea0f8c1Sriastradh  *
346bea0f8c1Sriastradh  *	Close F for output if necessary, and free a filemon descriptor.
347bea0f8c1Sriastradh  *	Returns 0 on success; sets errno and returns -1 on failure, but
348bea0f8c1Sriastradh  *	frees the filemon descriptor either way;
349bea0f8c1Sriastradh  */
350bea0f8c1Sriastradh int
filemon_close(struct filemon * F)351bea0f8c1Sriastradh filemon_close(struct filemon *F)
352bea0f8c1Sriastradh {
353bea0f8c1Sriastradh 	struct filemon_state *S;
354bea0f8c1Sriastradh 	int error = 0;
355bea0f8c1Sriastradh 
356bea0f8c1Sriastradh 	/* Close for output.  */
357bea0f8c1Sriastradh 	if (filemon_closefd(F) == -1 && error == 0)
358bea0f8c1Sriastradh 		error = errno;
359bea0f8c1Sriastradh 
360bea0f8c1Sriastradh 	/* Close the ktrace pipe.  */
361bea0f8c1Sriastradh 	if (fclose(F->in) == EOF && error == 0)
362bea0f8c1Sriastradh 		error = errno;
363bea0f8c1Sriastradh 	if (close(F->ktrfd) == -1 && error == 0)
364bea0f8c1Sriastradh 		error = errno;
365bea0f8c1Sriastradh 
366bea0f8c1Sriastradh 	/* Free any active records.  */
367bea0f8c1Sriastradh 	while ((S = RB_TREE_MIN(&F->active)) != NULL) {
368bea0f8c1Sriastradh 		rb_tree_remove_node(&F->active, S);
369bea0f8c1Sriastradh 		free(S);
370bea0f8c1Sriastradh 	}
371bea0f8c1Sriastradh 
372bea0f8c1Sriastradh 	/* Free the filemon descriptor.  */
373bea0f8c1Sriastradh 	free(F);
374bea0f8c1Sriastradh 
375bea0f8c1Sriastradh 	/* Set errno and return -1 if anything went wrong.  */
3764adbce36Srillig 	if (error != 0) {
377bea0f8c1Sriastradh 		errno = error;
378bea0f8c1Sriastradh 		return -1;
379bea0f8c1Sriastradh 	}
380bea0f8c1Sriastradh 
381bea0f8c1Sriastradh 	/* Success!  */
382bea0f8c1Sriastradh 	return 0;
383bea0f8c1Sriastradh }
384bea0f8c1Sriastradh 
385bea0f8c1Sriastradh /*
386bea0f8c1Sriastradh  * filemon_readfd(F)
387bea0f8c1Sriastradh  *
388bea0f8c1Sriastradh  *	Returns a file descriptor which will select/poll ready for read
389bea0f8c1Sriastradh  *	when there are filemon events to be processed by
390bea0f8c1Sriastradh  *	filemon_process, or -1 if anything has gone wrong.
391bea0f8c1Sriastradh  */
392bea0f8c1Sriastradh int
filemon_readfd(const struct filemon * F)393bea0f8c1Sriastradh filemon_readfd(const struct filemon *F)
394bea0f8c1Sriastradh {
395bea0f8c1Sriastradh 
396bea0f8c1Sriastradh 	if (F->state == FILEMON_ERROR)
397bea0f8c1Sriastradh 		return -1;
398bea0f8c1Sriastradh 	return fileno(F->in);
399bea0f8c1Sriastradh }
400bea0f8c1Sriastradh 
401bea0f8c1Sriastradh /*
402bea0f8c1Sriastradh  * filemon_dispatch(F)
403bea0f8c1Sriastradh  *
404bea0f8c1Sriastradh  *	Internal subroutine to dispatch a filemon ktrace event.
405bea0f8c1Sriastradh  *	Silently ignore events that we don't recognize.
406bea0f8c1Sriastradh  */
407bea0f8c1Sriastradh static void
filemon_dispatch(struct filemon * F)408bea0f8c1Sriastradh filemon_dispatch(struct filemon *F)
409bea0f8c1Sriastradh {
410bea0f8c1Sriastradh 	const struct filemon_key key = {
411bea0f8c1Sriastradh 		.pid = F->hdr.ktr_pid,
412bea0f8c1Sriastradh 		.lid = F->hdr.ktr_lid,
413bea0f8c1Sriastradh 	};
414bea0f8c1Sriastradh 	struct filemon_state *S;
415bea0f8c1Sriastradh 
416bea0f8c1Sriastradh 	switch (F->hdr.ktr_type) {
417bea0f8c1Sriastradh 	case KTR_SYSCALL: {
418bea0f8c1Sriastradh 		struct ktr_syscall *call = &F->payload.syscall;
419bea0f8c1Sriastradh 		struct filemon_state *S1;
420bea0f8c1Sriastradh 
421bea0f8c1Sriastradh 		/* Validate the syscall code.  */
422bea0f8c1Sriastradh 		if (call->ktr_code < 0 ||
423bea0f8c1Sriastradh 		    (size_t)call->ktr_code >= __arraycount(filemon_syscalls) ||
424bea0f8c1Sriastradh 		    filemon_syscalls[call->ktr_code] == NULL)
425bea0f8c1Sriastradh 			break;
426bea0f8c1Sriastradh 
427bea0f8c1Sriastradh 		/*
428bea0f8c1Sriastradh 		 * Invoke the syscall-specific logic to create a new
429bea0f8c1Sriastradh 		 * active state.
430bea0f8c1Sriastradh 		 */
431bea0f8c1Sriastradh 		S = (*filemon_syscalls[call->ktr_code])(F, &key, call);
432bea0f8c1Sriastradh 		if (S == NULL)
433bea0f8c1Sriastradh 			break;
434bea0f8c1Sriastradh 
435bea0f8c1Sriastradh 		/*
436bea0f8c1Sriastradh 		 * Insert the active state, or ignore it if there
437bea0f8c1Sriastradh 		 * already is one.
438bea0f8c1Sriastradh 		 *
439bea0f8c1Sriastradh 		 * Collisions shouldn't happen because the states are
440bea0f8c1Sriastradh 		 * keyed by <pid,lid>, in which syscalls should happen
441bea0f8c1Sriastradh 		 * sequentially in CALL/RET pairs, but let's be
442bea0f8c1Sriastradh 		 * defensive.
443bea0f8c1Sriastradh 		 */
444bea0f8c1Sriastradh 		S1 = rb_tree_insert_node(&F->active, S);
445bea0f8c1Sriastradh 		if (S1 != S) {
446bea0f8c1Sriastradh 			/* XXX Which one to drop?  */
447bea0f8c1Sriastradh 			free(S);
448bea0f8c1Sriastradh 			break;
449bea0f8c1Sriastradh 		}
450bea0f8c1Sriastradh 		break;
451bea0f8c1Sriastradh 	}
452bea0f8c1Sriastradh 	case KTR_NAMEI:
453bea0f8c1Sriastradh 		/* Find an active syscall state, or drop it.  */
454bea0f8c1Sriastradh 		S = rb_tree_find_node(&F->active, &key);
455bea0f8c1Sriastradh 		if (S == NULL)
456bea0f8c1Sriastradh 			break;
457bea0f8c1Sriastradh 		/* Find the position of the next path, or drop it.  */
458bea0f8c1Sriastradh 		if (S->i >= S->npath)
459bea0f8c1Sriastradh 			break;
460bea0f8c1Sriastradh 		/* Record the path.  */
461bea0f8c1Sriastradh 		S->path[S->i++] = strndup(F->payload.namei,
462bea0f8c1Sriastradh 		    sizeof F->payload.namei);
463bea0f8c1Sriastradh 		break;
464bea0f8c1Sriastradh 	case KTR_SYSRET: {
465bea0f8c1Sriastradh 		struct ktr_sysret *ret = &F->payload.sysret;
466bea0f8c1Sriastradh 		unsigned i;
467bea0f8c1Sriastradh 
468bea0f8c1Sriastradh 		/* Find and remove an active syscall state, or drop it.  */
469bea0f8c1Sriastradh 		S = rb_tree_find_node(&F->active, &key);
470bea0f8c1Sriastradh 		if (S == NULL)
471bea0f8c1Sriastradh 			break;
472bea0f8c1Sriastradh 		rb_tree_remove_node(&F->active, S);
473bea0f8c1Sriastradh 
474bea0f8c1Sriastradh 		/*
475bea0f8c1Sriastradh 		 * If the active syscall state matches this return,
476bea0f8c1Sriastradh 		 * invoke the syscall-specific logic to show a filemon
477bea0f8c1Sriastradh 		 * event.
478bea0f8c1Sriastradh 		 */
479bea0f8c1Sriastradh 		/* XXX What to do if syscall code doesn't match?  */
480bea0f8c1Sriastradh 		if (S->i == S->npath && S->syscode == ret->ktr_code)
4815658125dSrillig 			S->show(F, S, ret);
482bea0f8c1Sriastradh 
483bea0f8c1Sriastradh 		/* Free the state now that it is no longer active.  */
484bea0f8c1Sriastradh 		for (i = 0; i < S->i; i++)
485bea0f8c1Sriastradh 			free(S->path[i]);
486bea0f8c1Sriastradh 		free(S);
487bea0f8c1Sriastradh 		break;
488bea0f8c1Sriastradh 	}
489bea0f8c1Sriastradh 	default:
490bea0f8c1Sriastradh 		/* Ignore all other ktrace events.  */
491bea0f8c1Sriastradh 		break;
492bea0f8c1Sriastradh 	}
493bea0f8c1Sriastradh }
494bea0f8c1Sriastradh 
495bea0f8c1Sriastradh /*
496bea0f8c1Sriastradh  * filemon_process(F)
497bea0f8c1Sriastradh  *
498bea0f8c1Sriastradh  *	Process all pending events after filemon_readfd(F) has
499bea0f8c1Sriastradh  *	selected/polled ready for read.
500bea0f8c1Sriastradh  *
501bea0f8c1Sriastradh  *	Returns -1 on failure, 0 on end of events, and anything else if
502bea0f8c1Sriastradh  *	there may be more events.
503bea0f8c1Sriastradh  *
504bea0f8c1Sriastradh  *	XXX What about fairness to other activities in the event loop?
505bea0f8c1Sriastradh  *	If we stop while there's events buffered in F->in, then select
506bea0f8c1Sriastradh  *	or poll may not return ready even though there's work queued up
507bea0f8c1Sriastradh  *	in the buffer of F->in, but if we don't stop then ktrace events
508bea0f8c1Sriastradh  *	may overwhelm all other activity in the event loop.
509bea0f8c1Sriastradh  */
510bea0f8c1Sriastradh int
filemon_process(struct filemon * F)511bea0f8c1Sriastradh filemon_process(struct filemon *F)
512bea0f8c1Sriastradh {
513bea0f8c1Sriastradh 	size_t nread;
514bea0f8c1Sriastradh 
515bea0f8c1Sriastradh top:	/* If the child has exited, nothing to do.  */
516bea0f8c1Sriastradh 	/* XXX What if one thread calls exit while another is running?  */
517bea0f8c1Sriastradh 	if (F->child == 0)
518bea0f8c1Sriastradh 		return 0;
519bea0f8c1Sriastradh 
520bea0f8c1Sriastradh 	/* If we're waiting for input, read some.  */
521810790fcSrillig 	if (F->resid > 0) {
522bea0f8c1Sriastradh 		nread = fread(F->p, 1, F->resid, F->in);
523bea0f8c1Sriastradh 		if (nread == 0) {
524810790fcSrillig 			if (feof(F->in) != 0)
525bea0f8c1Sriastradh 				return 0;
52631940a95Srillig 			assert(ferror(F->in) != 0);
527bea0f8c1Sriastradh 			/*
528bea0f8c1Sriastradh 			 * If interrupted or would block, there may be
529bea0f8c1Sriastradh 			 * more events.  Otherwise fail.
530bea0f8c1Sriastradh 			 */
531bea0f8c1Sriastradh 			if (errno == EAGAIN || errno == EINTR)
532bea0f8c1Sriastradh 				return 1;
533bea0f8c1Sriastradh 			F->state = FILEMON_ERROR;
534bea0f8c1Sriastradh 			F->p = NULL;
535bea0f8c1Sriastradh 			F->resid = 0;
536bea0f8c1Sriastradh 			return -1;
537bea0f8c1Sriastradh 		}
538bea0f8c1Sriastradh 		assert(nread <= F->resid);
539bea0f8c1Sriastradh 		F->p += nread;
540bea0f8c1Sriastradh 		F->resid -= nread;
541810790fcSrillig 		if (F->resid > 0)	/* may be more events */
542bea0f8c1Sriastradh 			return 1;
543bea0f8c1Sriastradh 	}
544bea0f8c1Sriastradh 
545bea0f8c1Sriastradh 	/* Process a state transition now that we've read a buffer.  */
546bea0f8c1Sriastradh 	switch (F->state) {
547bea0f8c1Sriastradh 	case FILEMON_START:	/* just started filemon; read header next */
548bea0f8c1Sriastradh 		F->state = FILEMON_HEADER;
549bea0f8c1Sriastradh 		F->p = (void *)&F->hdr;
550bea0f8c1Sriastradh 		F->resid = sizeof F->hdr;
551bea0f8c1Sriastradh 		goto top;
552bea0f8c1Sriastradh 	case FILEMON_HEADER:	/* read header */
553bea0f8c1Sriastradh 		/* Sanity-check ktrace header; then read payload.  */
554bea0f8c1Sriastradh 		if (F->hdr.ktr_len < 0 ||
555bea0f8c1Sriastradh 		    (size_t)F->hdr.ktr_len > sizeof F->payload) {
556bea0f8c1Sriastradh 			F->state = FILEMON_ERROR;
557bea0f8c1Sriastradh 			F->p = NULL;
558bea0f8c1Sriastradh 			F->resid = 0;
559bea0f8c1Sriastradh 			errno = EIO;
560bea0f8c1Sriastradh 			return -1;
561bea0f8c1Sriastradh 		}
562bea0f8c1Sriastradh 		F->state = FILEMON_PAYLOAD;
563bea0f8c1Sriastradh 		F->p = (void *)&F->payload;
564bea0f8c1Sriastradh 		F->resid = (size_t)F->hdr.ktr_len;
565bea0f8c1Sriastradh 		goto top;
566bea0f8c1Sriastradh 	case FILEMON_PAYLOAD:	/* read header and payload */
567bea0f8c1Sriastradh 		/* Dispatch ktrace event; then read next header.  */
568bea0f8c1Sriastradh 		filemon_dispatch(F);
569bea0f8c1Sriastradh 		F->state = FILEMON_HEADER;
570bea0f8c1Sriastradh 		F->p = (void *)&F->hdr;
571bea0f8c1Sriastradh 		F->resid = sizeof F->hdr;
572bea0f8c1Sriastradh 		goto top;
573bea0f8c1Sriastradh 	default:		/* paranoia */
574bea0f8c1Sriastradh 		F->state = FILEMON_ERROR;
575bea0f8c1Sriastradh 		/*FALLTHROUGH*/
576bea0f8c1Sriastradh 	case FILEMON_ERROR:	/* persistent error indicator */
577bea0f8c1Sriastradh 		F->p = NULL;
578bea0f8c1Sriastradh 		F->resid = 0;
579bea0f8c1Sriastradh 		errno = EIO;
580bea0f8c1Sriastradh 		return -1;
581bea0f8c1Sriastradh 	}
582bea0f8c1Sriastradh }
583bea0f8c1Sriastradh 
584bea0f8c1Sriastradh static struct filemon_state *
syscall_enter(const struct filemon_key * key,const struct ktr_syscall * call,unsigned npath,void (* show)(struct filemon *,const struct filemon_state *,const struct ktr_sysret *))585462cbdfaSrillig syscall_enter(
586bea0f8c1Sriastradh     const struct filemon_key *key, const struct ktr_syscall *call,
587bea0f8c1Sriastradh     unsigned npath,
588bea0f8c1Sriastradh     void (*show)(struct filemon *, const struct filemon_state *,
589bea0f8c1Sriastradh 	const struct ktr_sysret *))
590bea0f8c1Sriastradh {
591bea0f8c1Sriastradh 	struct filemon_state *S;
592bea0f8c1Sriastradh 	unsigned i;
593bea0f8c1Sriastradh 
594bea0f8c1Sriastradh 	S = calloc(1, offsetof(struct filemon_state, path[npath]));
595bea0f8c1Sriastradh 	if (S == NULL)
596bea0f8c1Sriastradh 		return NULL;
597bea0f8c1Sriastradh 	S->key = *key;
598bea0f8c1Sriastradh 	S->show = show;
599bea0f8c1Sriastradh 	S->syscode = call->ktr_code;
600bea0f8c1Sriastradh 	S->i = 0;
601bea0f8c1Sriastradh 	S->npath = npath;
602bea0f8c1Sriastradh 	for (i = 0; i < npath; i++)
603bea0f8c1Sriastradh 		 S->path[i] = NULL; /* paranoia */
604bea0f8c1Sriastradh 
605bea0f8c1Sriastradh 	return S;
606bea0f8c1Sriastradh }
607bea0f8c1Sriastradh 
608bea0f8c1Sriastradh static void
show_paths(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret,const char * prefix)609bea0f8c1Sriastradh show_paths(struct filemon *F, const struct filemon_state *S,
610bea0f8c1Sriastradh     const struct ktr_sysret *ret, const char *prefix)
611bea0f8c1Sriastradh {
612bea0f8c1Sriastradh 	unsigned i;
613bea0f8c1Sriastradh 
614bea0f8c1Sriastradh 	/* Caller must ensure all paths have been specified.  */
615bea0f8c1Sriastradh 	assert(S->i == S->npath);
616bea0f8c1Sriastradh 
617bea0f8c1Sriastradh 	/*
618bea0f8c1Sriastradh 	 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
619bea0f8c1Sriastradh 	 * we're not producing output.
620bea0f8c1Sriastradh 	 */
62131940a95Srillig 	if (ret->ktr_error != 0 && ret->ktr_error != -2)
622bea0f8c1Sriastradh 		return;
623bea0f8c1Sriastradh 	if (F->out == NULL)
624bea0f8c1Sriastradh 		return;
625bea0f8c1Sriastradh 
626bea0f8c1Sriastradh 	/*
627bea0f8c1Sriastradh 	 * Print the prefix, pid, and paths -- with the paths quoted if
628bea0f8c1Sriastradh 	 * there's more than one.
629bea0f8c1Sriastradh 	 */
630bea0f8c1Sriastradh 	fprintf(F->out, "%s %jd", prefix, (intmax_t)S->key.pid);
631bea0f8c1Sriastradh 	for (i = 0; i < S->npath; i++) {
632bea0f8c1Sriastradh 		const char *q = S->npath > 1 ? "'" : "";
633bea0f8c1Sriastradh 		fprintf(F->out, " %s%s%s", q, S->path[i], q);
634bea0f8c1Sriastradh 	}
635bea0f8c1Sriastradh 	fprintf(F->out, "\n");
636bea0f8c1Sriastradh }
637bea0f8c1Sriastradh 
638bea0f8c1Sriastradh static void
show_retval(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret,const char * prefix)639bea0f8c1Sriastradh show_retval(struct filemon *F, const struct filemon_state *S,
640bea0f8c1Sriastradh     const struct ktr_sysret *ret, const char *prefix)
641bea0f8c1Sriastradh {
642bea0f8c1Sriastradh 
643bea0f8c1Sriastradh 	/*
644bea0f8c1Sriastradh 	 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
645bea0f8c1Sriastradh 	 * we're not producing output.
646bea0f8c1Sriastradh 	 */
64731940a95Srillig 	if (ret->ktr_error != 0 && ret->ktr_error != -2)
648bea0f8c1Sriastradh 		return;
649bea0f8c1Sriastradh 	if (F->out == NULL)
650bea0f8c1Sriastradh 		return;
651bea0f8c1Sriastradh 
652bea0f8c1Sriastradh 	fprintf(F->out, "%s %jd %jd\n", prefix, (intmax_t)S->key.pid,
653bea0f8c1Sriastradh 	    (intmax_t)ret->ktr_retval);
654bea0f8c1Sriastradh }
655bea0f8c1Sriastradh 
656bea0f8c1Sriastradh static void
show_chdir(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)657bea0f8c1Sriastradh show_chdir(struct filemon *F, const struct filemon_state *S,
658bea0f8c1Sriastradh     const struct ktr_sysret *ret)
659bea0f8c1Sriastradh {
660bea0f8c1Sriastradh 	show_paths(F, S, ret, "C");
661bea0f8c1Sriastradh }
662bea0f8c1Sriastradh 
663bea0f8c1Sriastradh static void
show_execve(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)664bea0f8c1Sriastradh show_execve(struct filemon *F, const struct filemon_state *S,
665bea0f8c1Sriastradh     const struct ktr_sysret *ret)
666bea0f8c1Sriastradh {
667462cbdfaSrillig 	show_paths(F, S, ret, "E");
668bea0f8c1Sriastradh }
669bea0f8c1Sriastradh 
670bea0f8c1Sriastradh static void
show_fork(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)671bea0f8c1Sriastradh show_fork(struct filemon *F, const struct filemon_state *S,
672bea0f8c1Sriastradh     const struct ktr_sysret *ret)
673bea0f8c1Sriastradh {
674bea0f8c1Sriastradh 	show_retval(F, S, ret, "F");
675bea0f8c1Sriastradh }
676bea0f8c1Sriastradh 
677bea0f8c1Sriastradh static void
show_link(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)678bea0f8c1Sriastradh show_link(struct filemon *F, const struct filemon_state *S,
679bea0f8c1Sriastradh     const struct ktr_sysret *ret)
680bea0f8c1Sriastradh {
681bea0f8c1Sriastradh 	show_paths(F, S, ret, "L"); /* XXX same as symlink */
682bea0f8c1Sriastradh }
683bea0f8c1Sriastradh 
684bea0f8c1Sriastradh static void
show_open_read(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)685bea0f8c1Sriastradh show_open_read(struct filemon *F, const struct filemon_state *S,
686bea0f8c1Sriastradh     const struct ktr_sysret *ret)
687bea0f8c1Sriastradh {
688bea0f8c1Sriastradh 	show_paths(F, S, ret, "R");
689bea0f8c1Sriastradh }
690bea0f8c1Sriastradh 
691bea0f8c1Sriastradh static void
show_open_write(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)692bea0f8c1Sriastradh show_open_write(struct filemon *F, const struct filemon_state *S,
693bea0f8c1Sriastradh     const struct ktr_sysret *ret)
694bea0f8c1Sriastradh {
695bea0f8c1Sriastradh 	show_paths(F, S, ret, "W");
696bea0f8c1Sriastradh }
697bea0f8c1Sriastradh 
698bea0f8c1Sriastradh static void
show_open_readwrite(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)699bea0f8c1Sriastradh show_open_readwrite(struct filemon *F, const struct filemon_state *S,
700bea0f8c1Sriastradh     const struct ktr_sysret *ret)
701bea0f8c1Sriastradh {
702bea0f8c1Sriastradh 	show_paths(F, S, ret, "R");
703bea0f8c1Sriastradh 	show_paths(F, S, ret, "W");
704bea0f8c1Sriastradh }
705bea0f8c1Sriastradh 
706bea0f8c1Sriastradh static void
show_openat_read(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)707bea0f8c1Sriastradh show_openat_read(struct filemon *F, const struct filemon_state *S,
708bea0f8c1Sriastradh     const struct ktr_sysret *ret)
709bea0f8c1Sriastradh {
710bea0f8c1Sriastradh 	if (S->path[0][0] != '/')
711bea0f8c1Sriastradh 		show_paths(F, S, ret, "A");
712bea0f8c1Sriastradh 	show_paths(F, S, ret, "R");
713bea0f8c1Sriastradh }
714bea0f8c1Sriastradh 
715bea0f8c1Sriastradh static void
show_openat_write(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)716bea0f8c1Sriastradh show_openat_write(struct filemon *F, const struct filemon_state *S,
717bea0f8c1Sriastradh     const struct ktr_sysret *ret)
718bea0f8c1Sriastradh {
719bea0f8c1Sriastradh 	if (S->path[0][0] != '/')
720bea0f8c1Sriastradh 		show_paths(F, S, ret, "A");
721bea0f8c1Sriastradh 	show_paths(F, S, ret, "W");
722bea0f8c1Sriastradh }
723bea0f8c1Sriastradh 
724bea0f8c1Sriastradh static void
show_openat_readwrite(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)725bea0f8c1Sriastradh show_openat_readwrite(struct filemon *F, const struct filemon_state *S,
726bea0f8c1Sriastradh     const struct ktr_sysret *ret)
727bea0f8c1Sriastradh {
728bea0f8c1Sriastradh 	if (S->path[0][0] != '/')
729bea0f8c1Sriastradh 		show_paths(F, S, ret, "A");
730bea0f8c1Sriastradh 	show_paths(F, S, ret, "R");
731bea0f8c1Sriastradh 	show_paths(F, S, ret, "W");
732bea0f8c1Sriastradh }
733bea0f8c1Sriastradh 
734bea0f8c1Sriastradh static void
show_symlink(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)735bea0f8c1Sriastradh show_symlink(struct filemon *F, const struct filemon_state *S,
736bea0f8c1Sriastradh     const struct ktr_sysret *ret)
737bea0f8c1Sriastradh {
738bea0f8c1Sriastradh 	show_paths(F, S, ret, "L"); /* XXX same as link */
739bea0f8c1Sriastradh }
740bea0f8c1Sriastradh 
741bea0f8c1Sriastradh static void
show_unlink(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)742bea0f8c1Sriastradh show_unlink(struct filemon *F, const struct filemon_state *S,
743bea0f8c1Sriastradh     const struct ktr_sysret *ret)
744bea0f8c1Sriastradh {
745bea0f8c1Sriastradh 	show_paths(F, S, ret, "D");
746bea0f8c1Sriastradh }
747bea0f8c1Sriastradh 
748bea0f8c1Sriastradh static void
show_rename(struct filemon * F,const struct filemon_state * S,const struct ktr_sysret * ret)749bea0f8c1Sriastradh show_rename(struct filemon *F, const struct filemon_state *S,
750bea0f8c1Sriastradh     const struct ktr_sysret *ret)
751bea0f8c1Sriastradh {
752bea0f8c1Sriastradh 	show_paths(F, S, ret, "M");
753bea0f8c1Sriastradh }
754bea0f8c1Sriastradh 
755462cbdfaSrillig /*ARGSUSED*/
756bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_chdir(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)757bea0f8c1Sriastradh filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
758bea0f8c1Sriastradh     const struct ktr_syscall *call)
759bea0f8c1Sriastradh {
760462cbdfaSrillig 	return syscall_enter(key, call, 1, &show_chdir);
761bea0f8c1Sriastradh }
762bea0f8c1Sriastradh 
763ad72e4a2Srillig /* TODO: monitor fchdir as well */
764ad72e4a2Srillig 
765462cbdfaSrillig /*ARGSUSED*/
766bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_execve(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)767bea0f8c1Sriastradh filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
768bea0f8c1Sriastradh     const struct ktr_syscall *call)
769bea0f8c1Sriastradh {
770462cbdfaSrillig 	return syscall_enter(key, call, 1, &show_execve);
771bea0f8c1Sriastradh }
772bea0f8c1Sriastradh 
773bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_exit(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)774bea0f8c1Sriastradh filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
775bea0f8c1Sriastradh     const struct ktr_syscall *call)
776bea0f8c1Sriastradh {
777bea0f8c1Sriastradh 	const register_t *args = (const void *)&call[1];
7785658125dSrillig 	int status = (int)args[0];
779bea0f8c1Sriastradh 
78071c58e78Srillig 	if (F->out != NULL) {
781bea0f8c1Sriastradh 		fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
782bea0f8c1Sriastradh 		if (key->pid == F->child) {
783bea0f8c1Sriastradh 			fprintf(F->out, "# Bye bye\n");
784bea0f8c1Sriastradh 			F->child = 0;
785bea0f8c1Sriastradh 		}
786bea0f8c1Sriastradh 	}
787bea0f8c1Sriastradh 	return NULL;
788bea0f8c1Sriastradh }
789bea0f8c1Sriastradh 
790462cbdfaSrillig /*ARGSUSED*/
791bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_fork(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)792bea0f8c1Sriastradh filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
793bea0f8c1Sriastradh     const struct ktr_syscall *call)
794bea0f8c1Sriastradh {
795462cbdfaSrillig 	return syscall_enter(key, call, 0, &show_fork);
796bea0f8c1Sriastradh }
797bea0f8c1Sriastradh 
798462cbdfaSrillig /*ARGSUSED*/
799bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_link(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)800bea0f8c1Sriastradh filemon_sys_link(struct filemon *F, const struct filemon_key *key,
801bea0f8c1Sriastradh     const struct ktr_syscall *call)
802bea0f8c1Sriastradh {
803462cbdfaSrillig 	return syscall_enter(key, call, 2, &show_link);
804bea0f8c1Sriastradh }
805bea0f8c1Sriastradh 
806462cbdfaSrillig /*ARGSUSED*/
807bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_open(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)808bea0f8c1Sriastradh filemon_sys_open(struct filemon *F, const struct filemon_key *key,
809bea0f8c1Sriastradh     const struct ktr_syscall *call)
810bea0f8c1Sriastradh {
811bea0f8c1Sriastradh 	const register_t *args = (const void *)&call[1];
812bea0f8c1Sriastradh 	int flags;
813bea0f8c1Sriastradh 
814bea0f8c1Sriastradh 	if (call->ktr_argsize < 2)
815bea0f8c1Sriastradh 		return NULL;
8165658125dSrillig 	flags = (int)args[1];
817bea0f8c1Sriastradh 
818bea0f8c1Sriastradh 	if ((flags & O_RDWR) == O_RDWR)
819462cbdfaSrillig 		return syscall_enter(key, call, 1, &show_open_readwrite);
820bea0f8c1Sriastradh 	else if ((flags & O_WRONLY) == O_WRONLY)
821462cbdfaSrillig 		return syscall_enter(key, call, 1, &show_open_write);
822bea0f8c1Sriastradh 	else if ((flags & O_RDONLY) == O_RDONLY)
823462cbdfaSrillig 		return syscall_enter(key, call, 1, &show_open_read);
824bea0f8c1Sriastradh 	else
825bea0f8c1Sriastradh 		return NULL;	/* XXX Do we care if no read or write?  */
826bea0f8c1Sriastradh }
827bea0f8c1Sriastradh 
828462cbdfaSrillig /*ARGSUSED*/
829bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_openat(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)830bea0f8c1Sriastradh filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
831bea0f8c1Sriastradh     const struct ktr_syscall *call)
832bea0f8c1Sriastradh {
833bea0f8c1Sriastradh 	const register_t *args = (const void *)&call[1];
834bea0f8c1Sriastradh 	int flags, fd;
835bea0f8c1Sriastradh 
836ad72e4a2Srillig 	/*
837ad72e4a2Srillig 	 * XXX: In the .meta log, the base directory is missing, which makes
838ad72e4a2Srillig 	 * all references to relative pathnames useless.
839ad72e4a2Srillig 	 */
840ad72e4a2Srillig 
841bea0f8c1Sriastradh 	if (call->ktr_argsize < 3)
842bea0f8c1Sriastradh 		return NULL;
8435658125dSrillig 	fd = (int)args[0];
8445658125dSrillig 	flags = (int)args[2];
845bea0f8c1Sriastradh 
846bea0f8c1Sriastradh 	if (fd == AT_CWD) {
847bea0f8c1Sriastradh 		if ((flags & O_RDWR) == O_RDWR)
848462cbdfaSrillig 			return syscall_enter(key, call, 1,
849bea0f8c1Sriastradh 			    &show_open_readwrite);
850bea0f8c1Sriastradh 		else if ((flags & O_WRONLY) == O_WRONLY)
851462cbdfaSrillig 			return syscall_enter(key, call, 1, &show_open_write);
852bea0f8c1Sriastradh 		else if ((flags & O_RDONLY) == O_RDONLY)
853462cbdfaSrillig 			return syscall_enter(key, call, 1, &show_open_read);
854bea0f8c1Sriastradh 		else
855bea0f8c1Sriastradh 			return NULL;
856bea0f8c1Sriastradh 	} else {
857bea0f8c1Sriastradh 		if ((flags & O_RDWR) == O_RDWR)
858462cbdfaSrillig 			return syscall_enter(key, call, 1,
859bea0f8c1Sriastradh 			    &show_openat_readwrite);
860bea0f8c1Sriastradh 		else if ((flags & O_WRONLY) == O_WRONLY)
861462cbdfaSrillig 			return syscall_enter(key, call, 1, &show_openat_write);
862bea0f8c1Sriastradh 		else if ((flags & O_RDONLY) == O_RDONLY)
863462cbdfaSrillig 			return syscall_enter(key, call, 1, &show_openat_read);
864bea0f8c1Sriastradh 		else
865bea0f8c1Sriastradh 			return NULL;
866bea0f8c1Sriastradh 	}
867bea0f8c1Sriastradh }
868bea0f8c1Sriastradh 
869ad72e4a2Srillig /* TODO: monitor the other *at syscalls as well, not only openat. */
870ad72e4a2Srillig 
871462cbdfaSrillig /*ARGSUSED*/
872bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_symlink(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)873bea0f8c1Sriastradh filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
874bea0f8c1Sriastradh     const struct ktr_syscall *call)
875bea0f8c1Sriastradh {
876462cbdfaSrillig 	return syscall_enter(key, call, 2, &show_symlink);
877bea0f8c1Sriastradh }
878bea0f8c1Sriastradh 
879462cbdfaSrillig /*ARGSUSED*/
880bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_unlink(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)881bea0f8c1Sriastradh filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
882bea0f8c1Sriastradh     const struct ktr_syscall *call)
883bea0f8c1Sriastradh {
884462cbdfaSrillig 	return syscall_enter(key, call, 1, &show_unlink);
885bea0f8c1Sriastradh }
886bea0f8c1Sriastradh 
887462cbdfaSrillig /*ARGSUSED*/
888bea0f8c1Sriastradh static struct filemon_state *
filemon_sys_rename(struct filemon * F,const struct filemon_key * key,const struct ktr_syscall * call)889bea0f8c1Sriastradh filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
890bea0f8c1Sriastradh     const struct ktr_syscall *call)
891bea0f8c1Sriastradh {
892462cbdfaSrillig 	return syscall_enter(key, call, 2, &show_rename);
893bea0f8c1Sriastradh }
894