1 /* $NetBSD: filemon_ktrace.c,v 1.4 2020/11/05 17:27:16 rillig Exp $ */
4 * Copyright (c) 2019 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
32 #define _KERNTYPES /* register_t */
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/rbtree.h>
39 #include <sys/syscall.h>
44 #include <sys/ktrace.h>
65 typedef struct filemon_state *filemon_syscall_t(struct filemon *,
66 const struct filemon_key *, const struct ktr_syscall *);
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;
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,
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 */
99 /* I/O state machine. */
110 struct ktr_header hdr;
112 struct ktr_syscall syscall;
113 struct ktr_sysret sysret;
114 char namei[PATH_MAX];
115 unsigned char buf[4096];
119 struct filemon_state {
126 void (*show)(struct filemon *, const struct filemon_state *,
127 const struct ktr_sysret *);
130 char *path[/*npath*/];
134 compare_filemon_states(void *cookie, const void *na, const void *nb)
136 const struct filemon_state *Sa = na;
137 const struct filemon_state *Sb = nb;
139 if (Sa->key.pid < Sb->key.pid)
141 if (Sa->key.pid > Sb->key.pid)
143 if (Sa->key.lid < Sb->key.lid)
145 if (Sa->key.lid > Sb->key.lid)
151 compare_filemon_key(void *cookie, const void *n, const void *k)
153 const struct filemon_state *S = n;
154 const struct filemon_key *key = k;
156 if (S->key.pid < key->pid)
158 if (S->key.pid > key->pid)
160 if (S->key.lid < key->lid)
162 if (S->key.lid > key->lid)
167 static const rb_tree_ops_t filemon_rb_ops = {
168 .rbto_compare_nodes = &compare_filemon_states,
169 .rbto_compare_key = &compare_filemon_key,
170 .rbto_node_offset = offsetof(struct filemon_state, node),
171 .rbto_context = NULL,
177 * Return a pointer to a constant string denoting the `path' of
190 * Allocate a filemon descriptor. Returns NULL and sets errno on
200 /* Allocate and zero a struct filemon object. */
201 F = calloc(1, sizeof *F);
205 /* Create a pipe for ktrace events. */
206 if (pipe2(ktrpipe, O_CLOEXEC|O_NONBLOCK) == -1) {
211 /* Create a file stream for reading the ktrace events. */
212 if ((F->in = fdopen(ktrpipe[0], "r")) == NULL) {
216 ktrpipe[0] = -1; /* claimed by fdopen */
219 * Set the fd for writing ktrace events and initialize the
220 * rbtree. The rest can be safely initialized to zero.
222 F->ktrfd = ktrpipe[1];
223 rb_tree_init(&F->active, &filemon_rb_ops);
230 fail1: (void)close(ktrpipe[0]);
231 (void)close(ktrpipe[1]);
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.
246 filemon_closefd(struct filemon *F)
250 /* If we're not open, nothing to do. */
255 * Flush it, close it, and null it unconditionally, but be
256 * careful to return the earliest error in errno.
258 if (fflush(F->out) == EOF && error == 0)
260 if (fclose(F->out) == EOF && error == 0)
264 /* Set errno and return -1 if anything went wrong. */
275 * filemon_setfd(F, fd)
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.
282 filemon_setfd(struct filemon *F, int fd)
286 * Close an existing output file if done. Fail now if there's
289 if ((filemon_closefd(F)) == -1)
291 assert(F->out == NULL);
293 /* Open a file stream and claim ownership of the fd. */
294 if ((F->out = fdopen(fd, "a")) == NULL)
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.
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");
311 * filemon_setpid_parent(F, pid)
313 * Set the traced pid, from the parent. Never fails.
316 filemon_setpid_parent(struct filemon *F, pid_t pid)
323 * filemon_setpid_child(F, pid)
325 * Set the traced pid, from the child. Returns 0 on success; sets
326 * errno and returns -1 on failure.
329 filemon_setpid_child(const struct filemon *F, pid_t pid)
333 ops = KTROP_SET|KTRFLAG_DESCEND;
335 trpoints |= KTRFAC_SYSCALL|KTRFAC_NAMEI|KTRFAC_SYSRET;
336 trpoints |= KTRFAC_INHERIT;
337 if (fktrace(F->ktrfd, ops, trpoints, pid) == -1)
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;
351 filemon_close(struct filemon *F)
353 struct filemon_state *S;
356 /* Close for output. */
357 if (filemon_closefd(F) == -1 && error == 0)
360 /* Close the ktrace pipe. */
361 if (fclose(F->in) == EOF && error == 0)
363 if (close(F->ktrfd) == -1 && error == 0)
366 /* Free any active records. */
367 while ((S = RB_TREE_MIN(&F->active)) != NULL) {
368 rb_tree_remove_node(&F->active, S);
372 /* Free the filemon descriptor. */
375 /* Set errno and return -1 if anything went wrong. */
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.
393 filemon_readfd(const struct filemon *F)
396 if (F->state == FILEMON_ERROR)
398 return fileno(F->in);
402 * filemon_dispatch(F)
404 * Internal subroutine to dispatch a filemon ktrace event.
405 * Silently ignore events that we don't recognize.
408 filemon_dispatch(struct filemon *F)
410 const struct filemon_key key = {
411 .pid = F->hdr.ktr_pid,
412 .lid = F->hdr.ktr_lid,
414 struct filemon_state *S;
416 switch (F->hdr.ktr_type) {
418 struct ktr_syscall *call = &F->payload.syscall;
419 struct filemon_state *S1;
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)
428 * Invoke the syscall-specific logic to create a new
431 S = (*filemon_syscalls[call->ktr_code])(F, &key, call);
436 * Insert the active state, or ignore it if there
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
444 S1 = rb_tree_insert_node(&F->active, S);
446 /* XXX Which one to drop? */
453 /* Find an active syscall state, or drop it. */
454 S = rb_tree_find_node(&F->active, &key);
457 /* Find the position of the next path, or drop it. */
458 if (S->i >= S->npath)
460 /* Record the path. */
461 S->path[S->i++] = strndup(F->payload.namei,
462 sizeof F->payload.namei);
465 struct ktr_sysret *ret = &F->payload.sysret;
468 /* Find and remove an active syscall state, or drop it. */
469 S = rb_tree_find_node(&F->active, &key);
472 rb_tree_remove_node(&F->active, S);
475 * If the active syscall state matches this return,
476 * invoke the syscall-specific logic to show a filemon
479 /* XXX What to do if syscall code doesn't match? */
480 if (S->i == S->npath && S->syscode == ret->ktr_code)
483 /* Free the state now that it is no longer active. */
484 for (i = 0; i < S->i; i++)
490 /* Ignore all other ktrace events. */
498 * Process all pending events after filemon_readfd(F) has
499 * selected/polled ready for read.
501 * Returns -1 on failure, 0 on end of events, and anything else if
502 * there may be more events.
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.
511 filemon_process(struct filemon *F)
515 top: /* If the child has exited, nothing to do. */
516 /* XXX What if one thread calls exit while another is running? */
520 /* If we're waiting for input, read some. */
522 nread = fread(F->p, 1, F->resid, F->in);
526 assert(ferror(F->in));
528 * If interrupted or would block, there may be
529 * more events. Otherwise fail.
531 if (errno == EAGAIN || errno == EINTR)
533 F->state = FILEMON_ERROR;
538 assert(nread <= F->resid);
541 if (F->resid) /* may be more events */
545 /* Process a state transition now that we've read a buffer. */
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;
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;
562 F->state = FILEMON_PAYLOAD;
563 F->p = (void *)&F->payload;
564 F->resid = (size_t)F->hdr.ktr_len;
566 case FILEMON_PAYLOAD: /* read header and payload */
567 /* Dispatch ktrace event; then read next header. */
569 F->state = FILEMON_HEADER;
570 F->p = (void *)&F->hdr;
571 F->resid = sizeof F->hdr;
573 default: /* paranoia */
574 F->state = FILEMON_ERROR;
576 case FILEMON_ERROR: /* persistent error indicator */
584 static struct filemon_state *
585 syscall_enter(struct filemon *F,
586 const struct filemon_key *key, const struct ktr_syscall *call,
588 void (*show)(struct filemon *, const struct filemon_state *,
589 const struct ktr_sysret *))
591 struct filemon_state *S;
594 S = calloc(1, offsetof(struct filemon_state, path[npath]));
599 S->syscode = call->ktr_code;
602 for (i = 0; i < npath; i++)
603 S->path[i] = NULL; /* paranoia */
609 show_paths(struct filemon *F, const struct filemon_state *S,
610 const struct ktr_sysret *ret, const char *prefix)
614 /* Caller must ensure all paths have been specified. */
615 assert(S->i == S->npath);
618 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
619 * we're not producing output.
621 if (ret->ktr_error && ret->ktr_error != -2)
627 * Print the prefix, pid, and paths -- with the paths quoted if
628 * there's more than one.
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);
635 fprintf(F->out, "\n");
639 show_retval(struct filemon *F, const struct filemon_state *S,
640 const struct ktr_sysret *ret, const char *prefix)
644 * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
645 * we're not producing output.
647 if (ret->ktr_error && ret->ktr_error != -2)
652 fprintf(F->out, "%s %jd %jd\n", prefix, (intmax_t)S->key.pid,
653 (intmax_t)ret->ktr_retval);
657 show_chdir(struct filemon *F, const struct filemon_state *S,
658 const struct ktr_sysret *ret)
660 show_paths(F, S, ret, "C");
664 show_execve(struct filemon *F, const struct filemon_state *S,
665 const struct ktr_sysret *ret)
667 return show_paths(F, S, ret, "E");
671 show_fork(struct filemon *F, const struct filemon_state *S,
672 const struct ktr_sysret *ret)
674 show_retval(F, S, ret, "F");
678 show_link(struct filemon *F, const struct filemon_state *S,
679 const struct ktr_sysret *ret)
681 show_paths(F, S, ret, "L"); /* XXX same as symlink */
685 show_open_read(struct filemon *F, const struct filemon_state *S,
686 const struct ktr_sysret *ret)
688 show_paths(F, S, ret, "R");
692 show_open_write(struct filemon *F, const struct filemon_state *S,
693 const struct ktr_sysret *ret)
695 show_paths(F, S, ret, "W");
699 show_open_readwrite(struct filemon *F, const struct filemon_state *S,
700 const struct ktr_sysret *ret)
702 show_paths(F, S, ret, "R");
703 show_paths(F, S, ret, "W");
707 show_openat_read(struct filemon *F, const struct filemon_state *S,
708 const struct ktr_sysret *ret)
710 if (S->path[0][0] != '/')
711 show_paths(F, S, ret, "A");
712 show_paths(F, S, ret, "R");
716 show_openat_write(struct filemon *F, const struct filemon_state *S,
717 const struct ktr_sysret *ret)
719 if (S->path[0][0] != '/')
720 show_paths(F, S, ret, "A");
721 show_paths(F, S, ret, "W");
725 show_openat_readwrite(struct filemon *F, const struct filemon_state *S,
726 const struct ktr_sysret *ret)
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");
735 show_symlink(struct filemon *F, const struct filemon_state *S,
736 const struct ktr_sysret *ret)
738 show_paths(F, S, ret, "L"); /* XXX same as link */
742 show_unlink(struct filemon *F, const struct filemon_state *S,
743 const struct ktr_sysret *ret)
745 show_paths(F, S, ret, "D");
749 show_rename(struct filemon *F, const struct filemon_state *S,
750 const struct ktr_sysret *ret)
752 show_paths(F, S, ret, "M");
755 static struct filemon_state *
756 filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
757 const struct ktr_syscall *call)
759 return syscall_enter(F, key, call, 1, &show_chdir);
762 static struct filemon_state *
763 filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
764 const struct ktr_syscall *call)
766 return syscall_enter(F, key, call, 1, &show_execve);
769 static struct filemon_state *
770 filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
771 const struct ktr_syscall *call)
773 const register_t *args = (const void *)&call[1];
774 int status = (int)args[0];
777 fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
778 if (key->pid == F->child) {
779 fprintf(F->out, "# Bye bye\n");
786 static struct filemon_state *
787 filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
788 const struct ktr_syscall *call)
790 return syscall_enter(F, key, call, 0, &show_fork);
793 static struct filemon_state *
794 filemon_sys_link(struct filemon *F, const struct filemon_key *key,
795 const struct ktr_syscall *call)
797 return syscall_enter(F, key, call, 2, &show_link);
800 static struct filemon_state *
801 filemon_sys_open(struct filemon *F, const struct filemon_key *key,
802 const struct ktr_syscall *call)
804 const register_t *args = (const void *)&call[1];
807 if (call->ktr_argsize < 2)
809 flags = (int)args[1];
811 if ((flags & O_RDWR) == O_RDWR)
812 return syscall_enter(F, key, call, 1, &show_open_readwrite);
813 else if ((flags & O_WRONLY) == O_WRONLY)
814 return syscall_enter(F, key, call, 1, &show_open_write);
815 else if ((flags & O_RDONLY) == O_RDONLY)
816 return syscall_enter(F, key, call, 1, &show_open_read);
818 return NULL; /* XXX Do we care if no read or write? */
821 static struct filemon_state *
822 filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
823 const struct ktr_syscall *call)
825 const register_t *args = (const void *)&call[1];
828 if (call->ktr_argsize < 3)
831 flags = (int)args[2];
834 if ((flags & O_RDWR) == O_RDWR)
835 return syscall_enter(F, key, call, 1,
836 &show_open_readwrite);
837 else if ((flags & O_WRONLY) == O_WRONLY)
838 return syscall_enter(F, key, call, 1,
840 else if ((flags & O_RDONLY) == O_RDONLY)
841 return syscall_enter(F, key, call, 1, &show_open_read);
845 if ((flags & O_RDWR) == O_RDWR)
846 return syscall_enter(F, key, call, 1,
847 &show_openat_readwrite);
848 else if ((flags & O_WRONLY) == O_WRONLY)
849 return syscall_enter(F, key, call, 1,
851 else if ((flags & O_RDONLY) == O_RDONLY)
852 return syscall_enter(F, key, call, 1,
859 static struct filemon_state *
860 filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
861 const struct ktr_syscall *call)
863 return syscall_enter(F, key, call, 2, &show_symlink);
866 static struct filemon_state *
867 filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
868 const struct ktr_syscall *call)
870 return syscall_enter(F, key, call, 1, &show_unlink);
873 static struct filemon_state *
874 filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
875 const struct ktr_syscall *call)
877 return syscall_enter(F, key, call, 2, &show_rename);