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