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