1 /*	$NetBSD: filemon_ktrace.c,v 1.14 2021/02/01 21:34:41 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 /* TODO: monitor fchdir as well */
765 
766 /*ARGSUSED*/
767 static struct filemon_state *
768 filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
769     const struct ktr_syscall *call)
770 {
771 	return syscall_enter(key, call, 1, &show_execve);
772 }
773 
774 static struct filemon_state *
775 filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
776     const struct ktr_syscall *call)
777 {
778 	const register_t *args = (const void *)&call[1];
779 	int status = (int)args[0];
780 
781 	if (F->out != NULL) {
782 		fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
783 		if (key->pid == F->child) {
784 			fprintf(F->out, "# Bye bye\n");
785 			F->child = 0;
786 		}
787 	}
788 	return NULL;
789 }
790 
791 /*ARGSUSED*/
792 static struct filemon_state *
793 filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
794     const struct ktr_syscall *call)
795 {
796 	return syscall_enter(key, call, 0, &show_fork);
797 }
798 
799 /*ARGSUSED*/
800 static struct filemon_state *
801 filemon_sys_link(struct filemon *F, const struct filemon_key *key,
802     const struct ktr_syscall *call)
803 {
804 	return syscall_enter(key, call, 2, &show_link);
805 }
806 
807 /*ARGSUSED*/
808 static struct filemon_state *
809 filemon_sys_open(struct filemon *F, const struct filemon_key *key,
810     const struct ktr_syscall *call)
811 {
812 	const register_t *args = (const void *)&call[1];
813 	int flags;
814 
815 	if (call->ktr_argsize < 2)
816 		return NULL;
817 	flags = (int)args[1];
818 
819 	if ((flags & O_RDWR) == O_RDWR)
820 		return syscall_enter(key, call, 1, &show_open_readwrite);
821 	else if ((flags & O_WRONLY) == O_WRONLY)
822 		return syscall_enter(key, call, 1, &show_open_write);
823 	else if ((flags & O_RDONLY) == O_RDONLY)
824 		return syscall_enter(key, call, 1, &show_open_read);
825 	else
826 		return NULL;	/* XXX Do we care if no read or write?  */
827 }
828 
829 /*ARGSUSED*/
830 static struct filemon_state *
831 filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
832     const struct ktr_syscall *call)
833 {
834 	const register_t *args = (const void *)&call[1];
835 	int flags, fd;
836 
837 	/*
838 	 * XXX: In the .meta log, the base directory is missing, which makes
839 	 * all references to relative pathnames useless.
840 	 */
841 
842 	if (call->ktr_argsize < 3)
843 		return NULL;
844 	fd = (int)args[0];
845 	flags = (int)args[2];
846 
847 	if (fd == AT_CWD) {
848 		if ((flags & O_RDWR) == O_RDWR)
849 			return syscall_enter(key, call, 1,
850 			    &show_open_readwrite);
851 		else if ((flags & O_WRONLY) == O_WRONLY)
852 			return syscall_enter(key, call, 1, &show_open_write);
853 		else if ((flags & O_RDONLY) == O_RDONLY)
854 			return syscall_enter(key, call, 1, &show_open_read);
855 		else
856 			return NULL;
857 	} else {
858 		if ((flags & O_RDWR) == O_RDWR)
859 			return syscall_enter(key, call, 1,
860 			    &show_openat_readwrite);
861 		else if ((flags & O_WRONLY) == O_WRONLY)
862 			return syscall_enter(key, call, 1, &show_openat_write);
863 		else if ((flags & O_RDONLY) == O_RDONLY)
864 			return syscall_enter(key, call, 1, &show_openat_read);
865 		else
866 			return NULL;
867 	}
868 }
869 
870 /* TODO: monitor the other *at syscalls as well, not only openat. */
871 
872 /*ARGSUSED*/
873 static struct filemon_state *
874 filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
875     const struct ktr_syscall *call)
876 {
877 	return syscall_enter(key, call, 2, &show_symlink);
878 }
879 
880 /*ARGSUSED*/
881 static struct filemon_state *
882 filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
883     const struct ktr_syscall *call)
884 {
885 	return syscall_enter(key, call, 1, &show_unlink);
886 }
887 
888 /*ARGSUSED*/
889 static struct filemon_state *
890 filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
891     const struct ktr_syscall *call)
892 {
893 	return syscall_enter(key, call, 2, &show_rename);
894 }
895