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