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