Home | History | Annotate | Line # | Download | only in filemon
filemon_ktrace.c revision 1.13
      1 /*	$NetBSD: filemon_ktrace.c,v 1.13 2021/01/19 20:51:46 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 	(void)fclose(F->in);
    231 fail1:	(void)close(ktrpipe[0]);
    232 	(void)close(ktrpipe[1]);
    233 fail0:	free(F);
    234 	errno = error;
    235 	return NULL;
    236 }
    237 
    238 /*
    239  * filemon_closefd(F)
    240  *
    241  *	Internal subroutine to try to flush and close the output file.
    242  *	If F is not open for output, do nothing.  Never leaves F open
    243  *	for output even on failure.  Returns 0 on success; sets errno
    244  *	and return -1 on failure.
    245  */
    246 static int
    247 filemon_closefd(struct filemon *F)
    248 {
    249 	int error = 0;
    250 
    251 	/* If we're not open, nothing to do.  */
    252 	if (F->out == NULL)
    253 		return 0;
    254 
    255 	/*
    256 	 * Flush it, close it, and null it unconditionally, but be
    257 	 * careful to return the earliest error in errno.
    258 	 */
    259 	if (fflush(F->out) == EOF && error == 0)
    260 		error = errno;
    261 	if (fclose(F->out) == EOF && error == 0)
    262 		error = errno;
    263 	F->out = NULL;
    264 
    265 	/* Set errno and return -1 if anything went wrong.  */
    266 	if (error != 0) {
    267 		errno = error;
    268 		return -1;
    269 	}
    270 
    271 	/* Success!  */
    272 	return 0;
    273 }
    274 
    275 /*
    276  * filemon_setfd(F, fd)
    277  *
    278  *	Cause filemon activity on F to be sent to fd.  Claims ownership
    279  *	of fd; caller should not use fd afterward, and any duplicates
    280  *	of fd may see their file positions changed.
    281  */
    282 int
    283 filemon_setfd(struct filemon *F, int fd)
    284 {
    285 
    286 	/*
    287 	 * Close an existing output file if done.  Fail now if there's
    288 	 * an error closing.
    289 	 */
    290 	if ((filemon_closefd(F)) == -1)
    291 		return -1;
    292 	assert(F->out == NULL);
    293 
    294 	/* Open a file stream and claim ownership of the fd.  */
    295 	if ((F->out = fdopen(fd, "a")) == NULL)
    296 		return -1;
    297 
    298 	/*
    299 	 * Print the opening output.  Any failure will be deferred
    300 	 * until closing.  For hysterical raisins, we show the parent
    301 	 * pid, not the child pid.
    302 	 */
    303 	fprintf(F->out, "# filemon version 4\n");
    304 	fprintf(F->out, "# Target pid %jd\n", (intmax_t)getpid());
    305 	fprintf(F->out, "V 4\n");
    306 
    307 	/* Success!  */
    308 	return 0;
    309 }
    310 
    311 /*
    312  * filemon_setpid_parent(F, pid)
    313  *
    314  *	Set the traced pid, from the parent.  Never fails.
    315  */
    316 void
    317 filemon_setpid_parent(struct filemon *F, pid_t pid)
    318 {
    319 
    320 	F->child = pid;
    321 }
    322 
    323 /*
    324  * filemon_setpid_child(F, pid)
    325  *
    326  *	Set the traced pid, from the child.  Returns 0 on success; sets
    327  *	errno and returns -1 on failure.
    328  */
    329 int
    330 filemon_setpid_child(const struct filemon *F, pid_t pid)
    331 {
    332 	int ops, trpoints;
    333 
    334 	ops = KTROP_SET|KTRFLAG_DESCEND;
    335 	trpoints = KTRFACv2;
    336 	trpoints |= KTRFAC_SYSCALL|KTRFAC_NAMEI|KTRFAC_SYSRET;
    337 	trpoints |= KTRFAC_INHERIT;
    338 	if (fktrace(F->ktrfd, ops, trpoints, pid) == -1)
    339 		return -1;
    340 
    341 	return 0;
    342 }
    343 
    344 /*
    345  * filemon_close(F)
    346  *
    347  *	Close F for output if necessary, and free a filemon descriptor.
    348  *	Returns 0 on success; sets errno and returns -1 on failure, but
    349  *	frees the filemon descriptor either way;
    350  */
    351 int
    352 filemon_close(struct filemon *F)
    353 {
    354 	struct filemon_state *S;
    355 	int error = 0;
    356 
    357 	/* Close for output.  */
    358 	if (filemon_closefd(F) == -1 && error == 0)
    359 		error = errno;
    360 
    361 	/* Close the ktrace pipe.  */
    362 	if (fclose(F->in) == EOF && error == 0)
    363 		error = errno;
    364 	if (close(F->ktrfd) == -1 && error == 0)
    365 		error = errno;
    366 
    367 	/* Free any active records.  */
    368 	while ((S = RB_TREE_MIN(&F->active)) != NULL) {
    369 		rb_tree_remove_node(&F->active, S);
    370 		free(S);
    371 	}
    372 
    373 	/* Free the filemon descriptor.  */
    374 	free(F);
    375 
    376 	/* Set errno and return -1 if anything went wrong.  */
    377 	if (error != 0) {
    378 		errno = error;
    379 		return -1;
    380 	}
    381 
    382 	/* Success!  */
    383 	return 0;
    384 }
    385 
    386 /*
    387  * filemon_readfd(F)
    388  *
    389  *	Returns a file descriptor which will select/poll ready for read
    390  *	when there are filemon events to be processed by
    391  *	filemon_process, or -1 if anything has gone wrong.
    392  */
    393 int
    394 filemon_readfd(const struct filemon *F)
    395 {
    396 
    397 	if (F->state == FILEMON_ERROR)
    398 		return -1;
    399 	return fileno(F->in);
    400 }
    401 
    402 /*
    403  * filemon_dispatch(F)
    404  *
    405  *	Internal subroutine to dispatch a filemon ktrace event.
    406  *	Silently ignore events that we don't recognize.
    407  */
    408 static void
    409 filemon_dispatch(struct filemon *F)
    410 {
    411 	const struct filemon_key key = {
    412 		.pid = F->hdr.ktr_pid,
    413 		.lid = F->hdr.ktr_lid,
    414 	};
    415 	struct filemon_state *S;
    416 
    417 	switch (F->hdr.ktr_type) {
    418 	case KTR_SYSCALL: {
    419 		struct ktr_syscall *call = &F->payload.syscall;
    420 		struct filemon_state *S1;
    421 
    422 		/* Validate the syscall code.  */
    423 		if (call->ktr_code < 0 ||
    424 		    (size_t)call->ktr_code >= __arraycount(filemon_syscalls) ||
    425 		    filemon_syscalls[call->ktr_code] == NULL)
    426 			break;
    427 
    428 		/*
    429 		 * Invoke the syscall-specific logic to create a new
    430 		 * active state.
    431 		 */
    432 		S = (*filemon_syscalls[call->ktr_code])(F, &key, call);
    433 		if (S == NULL)
    434 			break;
    435 
    436 		/*
    437 		 * Insert the active state, or ignore it if there
    438 		 * already is one.
    439 		 *
    440 		 * Collisions shouldn't happen because the states are
    441 		 * keyed by <pid,lid>, in which syscalls should happen
    442 		 * sequentially in CALL/RET pairs, but let's be
    443 		 * defensive.
    444 		 */
    445 		S1 = rb_tree_insert_node(&F->active, S);
    446 		if (S1 != S) {
    447 			/* XXX Which one to drop?  */
    448 			free(S);
    449 			break;
    450 		}
    451 		break;
    452 	}
    453 	case KTR_NAMEI:
    454 		/* Find an active syscall state, or drop it.  */
    455 		S = rb_tree_find_node(&F->active, &key);
    456 		if (S == NULL)
    457 			break;
    458 		/* Find the position of the next path, or drop it.  */
    459 		if (S->i >= S->npath)
    460 			break;
    461 		/* Record the path.  */
    462 		S->path[S->i++] = strndup(F->payload.namei,
    463 		    sizeof F->payload.namei);
    464 		break;
    465 	case KTR_SYSRET: {
    466 		struct ktr_sysret *ret = &F->payload.sysret;
    467 		unsigned i;
    468 
    469 		/* Find and remove an active syscall state, or drop it.  */
    470 		S = rb_tree_find_node(&F->active, &key);
    471 		if (S == NULL)
    472 			break;
    473 		rb_tree_remove_node(&F->active, S);
    474 
    475 		/*
    476 		 * If the active syscall state matches this return,
    477 		 * invoke the syscall-specific logic to show a filemon
    478 		 * event.
    479 		 */
    480 		/* XXX What to do if syscall code doesn't match?  */
    481 		if (S->i == S->npath && S->syscode == ret->ktr_code)
    482 			S->show(F, S, ret);
    483 
    484 		/* Free the state now that it is no longer active.  */
    485 		for (i = 0; i < S->i; i++)
    486 			free(S->path[i]);
    487 		free(S);
    488 		break;
    489 	}
    490 	default:
    491 		/* Ignore all other ktrace events.  */
    492 		break;
    493 	}
    494 }
    495 
    496 /*
    497  * filemon_process(F)
    498  *
    499  *	Process all pending events after filemon_readfd(F) has
    500  *	selected/polled ready for read.
    501  *
    502  *	Returns -1 on failure, 0 on end of events, and anything else if
    503  *	there may be more events.
    504  *
    505  *	XXX What about fairness to other activities in the event loop?
    506  *	If we stop while there's events buffered in F->in, then select
    507  *	or poll may not return ready even though there's work queued up
    508  *	in the buffer of F->in, but if we don't stop then ktrace events
    509  *	may overwhelm all other activity in the event loop.
    510  */
    511 int
    512 filemon_process(struct filemon *F)
    513 {
    514 	size_t nread;
    515 
    516 top:	/* If the child has exited, nothing to do.  */
    517 	/* XXX What if one thread calls exit while another is running?  */
    518 	if (F->child == 0)
    519 		return 0;
    520 
    521 	/* If we're waiting for input, read some.  */
    522 	if (F->resid > 0) {
    523 		nread = fread(F->p, 1, F->resid, F->in);
    524 		if (nread == 0) {
    525 			if (feof(F->in) != 0)
    526 				return 0;
    527 			assert(ferror(F->in) != 0);
    528 			/*
    529 			 * If interrupted or would block, there may be
    530 			 * more events.  Otherwise fail.
    531 			 */
    532 			if (errno == EAGAIN || errno == EINTR)
    533 				return 1;
    534 			F->state = FILEMON_ERROR;
    535 			F->p = NULL;
    536 			F->resid = 0;
    537 			return -1;
    538 		}
    539 		assert(nread <= F->resid);
    540 		F->p += nread;
    541 		F->resid -= nread;
    542 		if (F->resid > 0)	/* may be more events */
    543 			return 1;
    544 	}
    545 
    546 	/* Process a state transition now that we've read a buffer.  */
    547 	switch (F->state) {
    548 	case FILEMON_START:	/* just started filemon; read header next */
    549 		F->state = FILEMON_HEADER;
    550 		F->p = (void *)&F->hdr;
    551 		F->resid = sizeof F->hdr;
    552 		goto top;
    553 	case FILEMON_HEADER:	/* read header */
    554 		/* Sanity-check ktrace header; then read payload.  */
    555 		if (F->hdr.ktr_len < 0 ||
    556 		    (size_t)F->hdr.ktr_len > sizeof F->payload) {
    557 			F->state = FILEMON_ERROR;
    558 			F->p = NULL;
    559 			F->resid = 0;
    560 			errno = EIO;
    561 			return -1;
    562 		}
    563 		F->state = FILEMON_PAYLOAD;
    564 		F->p = (void *)&F->payload;
    565 		F->resid = (size_t)F->hdr.ktr_len;
    566 		goto top;
    567 	case FILEMON_PAYLOAD:	/* read header and payload */
    568 		/* Dispatch ktrace event; then read next header.  */
    569 		filemon_dispatch(F);
    570 		F->state = FILEMON_HEADER;
    571 		F->p = (void *)&F->hdr;
    572 		F->resid = sizeof F->hdr;
    573 		goto top;
    574 	default:		/* paranoia */
    575 		F->state = FILEMON_ERROR;
    576 		/*FALLTHROUGH*/
    577 	case FILEMON_ERROR:	/* persistent error indicator */
    578 		F->p = NULL;
    579 		F->resid = 0;
    580 		errno = EIO;
    581 		return -1;
    582 	}
    583 }
    584 
    585 static struct filemon_state *
    586 syscall_enter(
    587     const struct filemon_key *key, const struct ktr_syscall *call,
    588     unsigned npath,
    589     void (*show)(struct filemon *, const struct filemon_state *,
    590 	const struct ktr_sysret *))
    591 {
    592 	struct filemon_state *S;
    593 	unsigned i;
    594 
    595 	S = calloc(1, offsetof(struct filemon_state, path[npath]));
    596 	if (S == NULL)
    597 		return NULL;
    598 	S->key = *key;
    599 	S->show = show;
    600 	S->syscode = call->ktr_code;
    601 	S->i = 0;
    602 	S->npath = npath;
    603 	for (i = 0; i < npath; i++)
    604 		 S->path[i] = NULL; /* paranoia */
    605 
    606 	return S;
    607 }
    608 
    609 static void
    610 show_paths(struct filemon *F, const struct filemon_state *S,
    611     const struct ktr_sysret *ret, const char *prefix)
    612 {
    613 	unsigned i;
    614 
    615 	/* Caller must ensure all paths have been specified.  */
    616 	assert(S->i == S->npath);
    617 
    618 	/*
    619 	 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
    620 	 * we're not producing output.
    621 	 */
    622 	if (ret->ktr_error != 0 && ret->ktr_error != -2)
    623 		return;
    624 	if (F->out == NULL)
    625 		return;
    626 
    627 	/*
    628 	 * Print the prefix, pid, and paths -- with the paths quoted if
    629 	 * there's more than one.
    630 	 */
    631 	fprintf(F->out, "%s %jd", prefix, (intmax_t)S->key.pid);
    632 	for (i = 0; i < S->npath; i++) {
    633 		const char *q = S->npath > 1 ? "'" : "";
    634 		fprintf(F->out, " %s%s%s", q, S->path[i], q);
    635 	}
    636 	fprintf(F->out, "\n");
    637 }
    638 
    639 static void
    640 show_retval(struct filemon *F, const struct filemon_state *S,
    641     const struct ktr_sysret *ret, const char *prefix)
    642 {
    643 
    644 	/*
    645 	 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
    646 	 * we're not producing output.
    647 	 */
    648 	if (ret->ktr_error != 0 && ret->ktr_error != -2)
    649 		return;
    650 	if (F->out == NULL)
    651 		return;
    652 
    653 	fprintf(F->out, "%s %jd %jd\n", prefix, (intmax_t)S->key.pid,
    654 	    (intmax_t)ret->ktr_retval);
    655 }
    656 
    657 static void
    658 show_chdir(struct filemon *F, const struct filemon_state *S,
    659     const struct ktr_sysret *ret)
    660 {
    661 	show_paths(F, S, ret, "C");
    662 }
    663 
    664 static void
    665 show_execve(struct filemon *F, const struct filemon_state *S,
    666     const struct ktr_sysret *ret)
    667 {
    668 	show_paths(F, S, ret, "E");
    669 }
    670 
    671 static void
    672 show_fork(struct filemon *F, const struct filemon_state *S,
    673     const struct ktr_sysret *ret)
    674 {
    675 	show_retval(F, S, ret, "F");
    676 }
    677 
    678 static void
    679 show_link(struct filemon *F, const struct filemon_state *S,
    680     const struct ktr_sysret *ret)
    681 {
    682 	show_paths(F, S, ret, "L"); /* XXX same as symlink */
    683 }
    684 
    685 static void
    686 show_open_read(struct filemon *F, const struct filemon_state *S,
    687     const struct ktr_sysret *ret)
    688 {
    689 	show_paths(F, S, ret, "R");
    690 }
    691 
    692 static void
    693 show_open_write(struct filemon *F, const struct filemon_state *S,
    694     const struct ktr_sysret *ret)
    695 {
    696 	show_paths(F, S, ret, "W");
    697 }
    698 
    699 static void
    700 show_open_readwrite(struct filemon *F, const struct filemon_state *S,
    701     const struct ktr_sysret *ret)
    702 {
    703 	show_paths(F, S, ret, "R");
    704 	show_paths(F, S, ret, "W");
    705 }
    706 
    707 static void
    708 show_openat_read(struct filemon *F, const struct filemon_state *S,
    709     const struct ktr_sysret *ret)
    710 {
    711 	if (S->path[0][0] != '/')
    712 		show_paths(F, S, ret, "A");
    713 	show_paths(F, S, ret, "R");
    714 }
    715 
    716 static void
    717 show_openat_write(struct filemon *F, const struct filemon_state *S,
    718     const struct ktr_sysret *ret)
    719 {
    720 	if (S->path[0][0] != '/')
    721 		show_paths(F, S, ret, "A");
    722 	show_paths(F, S, ret, "W");
    723 }
    724 
    725 static void
    726 show_openat_readwrite(struct filemon *F, const struct filemon_state *S,
    727     const struct ktr_sysret *ret)
    728 {
    729 	if (S->path[0][0] != '/')
    730 		show_paths(F, S, ret, "A");
    731 	show_paths(F, S, ret, "R");
    732 	show_paths(F, S, ret, "W");
    733 }
    734 
    735 static void
    736 show_symlink(struct filemon *F, const struct filemon_state *S,
    737     const struct ktr_sysret *ret)
    738 {
    739 	show_paths(F, S, ret, "L"); /* XXX same as link */
    740 }
    741 
    742 static void
    743 show_unlink(struct filemon *F, const struct filemon_state *S,
    744     const struct ktr_sysret *ret)
    745 {
    746 	show_paths(F, S, ret, "D");
    747 }
    748 
    749 static void
    750 show_rename(struct filemon *F, const struct filemon_state *S,
    751     const struct ktr_sysret *ret)
    752 {
    753 	show_paths(F, S, ret, "M");
    754 }
    755 
    756 /*ARGSUSED*/
    757 static struct filemon_state *
    758 filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
    759     const struct ktr_syscall *call)
    760 {
    761 	return syscall_enter(key, call, 1, &show_chdir);
    762 }
    763 
    764 /*ARGSUSED*/
    765 static struct filemon_state *
    766 filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
    767     const struct ktr_syscall *call)
    768 {
    769 	return syscall_enter(key, call, 1, &show_execve);
    770 }
    771 
    772 static struct filemon_state *
    773 filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
    774     const struct ktr_syscall *call)
    775 {
    776 	const register_t *args = (const void *)&call[1];
    777 	int status = (int)args[0];
    778 
    779 	if (F->out != NULL) {
    780 		fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
    781 		if (key->pid == F->child) {
    782 			fprintf(F->out, "# Bye bye\n");
    783 			F->child = 0;
    784 		}
    785 	}
    786 	return NULL;
    787 }
    788 
    789 /*ARGSUSED*/
    790 static struct filemon_state *
    791 filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
    792     const struct ktr_syscall *call)
    793 {
    794 	return syscall_enter(key, call, 0, &show_fork);
    795 }
    796 
    797 /*ARGSUSED*/
    798 static struct filemon_state *
    799 filemon_sys_link(struct filemon *F, const struct filemon_key *key,
    800     const struct ktr_syscall *call)
    801 {
    802 	return syscall_enter(key, call, 2, &show_link);
    803 }
    804 
    805 /*ARGSUSED*/
    806 static struct filemon_state *
    807 filemon_sys_open(struct filemon *F, const struct filemon_key *key,
    808     const struct ktr_syscall *call)
    809 {
    810 	const register_t *args = (const void *)&call[1];
    811 	int flags;
    812 
    813 	if (call->ktr_argsize < 2)
    814 		return NULL;
    815 	flags = (int)args[1];
    816 
    817 	if ((flags & O_RDWR) == O_RDWR)
    818 		return syscall_enter(key, call, 1, &show_open_readwrite);
    819 	else if ((flags & O_WRONLY) == O_WRONLY)
    820 		return syscall_enter(key, call, 1, &show_open_write);
    821 	else if ((flags & O_RDONLY) == O_RDONLY)
    822 		return syscall_enter(key, call, 1, &show_open_read);
    823 	else
    824 		return NULL;	/* XXX Do we care if no read or write?  */
    825 }
    826 
    827 /*ARGSUSED*/
    828 static struct filemon_state *
    829 filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
    830     const struct ktr_syscall *call)
    831 {
    832 	const register_t *args = (const void *)&call[1];
    833 	int flags, fd;
    834 
    835 	if (call->ktr_argsize < 3)
    836 		return NULL;
    837 	fd = (int)args[0];
    838 	flags = (int)args[2];
    839 
    840 	if (fd == AT_CWD) {
    841 		if ((flags & O_RDWR) == O_RDWR)
    842 			return syscall_enter(key, call, 1,
    843 			    &show_open_readwrite);
    844 		else if ((flags & O_WRONLY) == O_WRONLY)
    845 			return syscall_enter(key, call, 1, &show_open_write);
    846 		else if ((flags & O_RDONLY) == O_RDONLY)
    847 			return syscall_enter(key, call, 1, &show_open_read);
    848 		else
    849 			return NULL;
    850 	} else {
    851 		if ((flags & O_RDWR) == O_RDWR)
    852 			return syscall_enter(key, call, 1,
    853 			    &show_openat_readwrite);
    854 		else if ((flags & O_WRONLY) == O_WRONLY)
    855 			return syscall_enter(key, call, 1, &show_openat_write);
    856 		else if ((flags & O_RDONLY) == O_RDONLY)
    857 			return syscall_enter(key, call, 1, &show_openat_read);
    858 		else
    859 			return NULL;
    860 	}
    861 }
    862 
    863 /*ARGSUSED*/
    864 static struct filemon_state *
    865 filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
    866     const struct ktr_syscall *call)
    867 {
    868 	return syscall_enter(key, call, 2, &show_symlink);
    869 }
    870 
    871 /*ARGSUSED*/
    872 static struct filemon_state *
    873 filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
    874     const struct ktr_syscall *call)
    875 {
    876 	return syscall_enter(key, call, 1, &show_unlink);
    877 }
    878 
    879 /*ARGSUSED*/
    880 static struct filemon_state *
    881 filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
    882     const struct ktr_syscall *call)
    883 {
    884 	return syscall_enter(key, call, 2, &show_rename);
    885 }
    886