The saga continues! :)I previously posted one set of patches that changes filemon to use fd_{get,put}file() only when the activity-log file is actually being accessed. This works, but doesn't prevent or detect when the application program closes-and-reopens that fd.
Attached to this Email is a separate patch, using exithooks. The current exithook implementation is extended to have an additional "phase" argument, with values of EXITHOOK_BEFORE_CLOSE and EXITHOOK_AFTER_CLOSE. (Existing uses of exithooks in sys_aio, sysv_sem, and rump are updated to use EXITHOOK_AFTER_CLOSE.)
Filemon(4) now establishes an EXITHOOK_BEFORE_CLOSE which searches the process's file descriptor table for entries to /dev/filemon. If any are found, the associated activity-log file is disassociated from the filemon fd and the extra reference is released.
(Note that establishing the exithook is actually done through a config finalizer routine, since the built-in MODULE_CLASS_DRIVER modules are initialized before the exithook stuff in exec_init().)
This exithook approach also works. There is still one problem, however. If the application program attempts to explicitly close the log-file fd while it is still associated with the filemon, we get the same hang.
Next, I'm going to have a look at fd_getfile2()/fclose() and see if that is a viable approach.
+------------------+--------------------------+------------------------+ | Paul Goyette | PGP Key fingerprint: | E-mail addresses: | | (Retired) | FA29 0E3B 35AF E8AE 6651 | paul at whooppee.com | | Kernel Developer | 0786 F758 55DE 53BA 7731 | pgoyette at netbsd.org | +------------------+--------------------------+------------------------+
Index: dev/filemon/filemon.c =================================================================== RCS file: /cvsroot/src/sys/dev/filemon/filemon.c,v retrieving revision 1.24 diff -u -p -r1.24 filemon.c --- dev/filemon/filemon.c 5 Jan 2016 22:08:54 -0000 1.24 +++ dev/filemon/filemon.c 7 Jan 2016 23:54:37 -0000 @@ -43,6 +43,8 @@ __KERNEL_RCSID(0, "$NetBSD: filemon.c,v #include <sys/kmem.h> #include <sys/syslog.h> #include <sys/kauth.h> +#include <sys/device.h> +#include <sys/errno.h> #include "filemon.h" #include "ioconf.h" @@ -89,6 +91,75 @@ static TAILQ_HEAD(, filemon) filemons_in static int logLevel = LOG_DEBUG; #endif +/* + * Use an exithook to make sure that the filemon device is closed and + * our additional reference to the output file is released before the + * normal closure of open files. The extra reference would otherwise + * cause process exit to hang indefinitely. + * + * We need to use a config_finalize() routine to register the hook + * due to system initialization sequence. + */ +void *hook = NULL; /* cookie from exithook_establish() */ + +static int filemon_register_hook(device_t dev __unused); +static void filemon_exithook(struct proc *p, void *arg); + +static int +filemon_register_hook(device_t dev __unused) +{ + + if (hook == NULL) + hook = exithook_establish(filemon_exithook, hook, + EXITHOOK_BEFORE_CLOSE); +printf("%s: hook %p\n", __func__, hook); + if (hook != NULL) + return 0; + return ENXIO; +} + +static void +filemon_exithook(struct proc *p, void *arg) +{ + int fd; + struct filemon *filemon; + fdfile_t *ff; + filedesc_t *fdp; + fdtab_t *dt; + + fdp = p->p_fd; + dt = fdp->fd_dt; + for (fd = 0; fd < dt->dt_nfiles; fd++) { + ff = dt->dt_ff[fd]; + KASSERT(fd >= NDFDFILE || + ff == (fdfile_t *)fdp->fd_dfdfile[fd]); + if (ff == NULL || ff->ff_file == NULL) + continue; + if (ff->ff_file->f_type != DTYPE_MISC || + ff->ff_file->f_ops != &filemon_fileops) + continue; +printf("%s: found pid %d fd %d\n", __func__, p->p_pid, fd); + rw_enter(&filemon_mtx, RW_WRITER); + filemon = ff->ff_file->f_data; + if (filemon != NULL) { + + /* + * If we have an output file, release our + * reference to it. + */ + rw_enter(&filemon->fm_mtx, RW_WRITER); + if (filemon->fm_fp) { +printf("%s: releasing %d\n", __func__, filemon->fm_fd); + KASSERT(p == curproc); + fd_putfile(filemon->fm_fd); + filemon->fm_fp = NULL; + } + rw_exit(&filemon->fm_mtx); + } + rw_exit(&filemon_mtx); + } +} + void filemon_output(struct filemon * filemon, char *msg, size_t len) { @@ -216,6 +287,9 @@ filemon_open(dev_t dev, int oflags __unu struct file *fp; int error, fd; + if (hook == NULL) + return ENXIO; + /* falloc() will fill in the descriptor for us. */ if ((error = fd_allocfile(&fp, &fd)) != 0) return error; @@ -265,6 +339,7 @@ filemon_close(struct file * fp) */ rw_enter(&filemon->fm_mtx, RW_WRITER); if (filemon->fm_fp) { +printf("%s: releasing %d\n", __func__, filemon->fm_fd); fd_putfile(filemon->fm_fd); /* release our reference */ filemon->fm_fp = NULL; } @@ -399,19 +474,35 @@ filemon_modcmd(modcmd_t cmd, void *data) switch (cmd) { case MODULE_CMD_INIT: + KASSERT(hook == NULL); #ifdef DEBUG logLevel = LOG_INFO; #endif error = filemon_load(data); + if (error != 0) + break; #ifdef _MODULE - if (!error) - error = devsw_attach("filemon", NULL, &bmajor, - &filemon_cdevsw, &cmajor); + error = devsw_attach("filemon", NULL, &bmajor, + &filemon_cdevsw, &cmajor); + if (error != 0) { + filemon_unload(); + break; + } #endif + error = config_finalize_register(NULL, filemon_register_hook); + if (error != 0) { + devsw_detach(NULL, &filemon_cdevsw); + filemon_unload(); + break; + } break; case MODULE_CMD_FINI: + if (hook != NULL) { + exithook_disestablish(hook, EXITHOOK_BEFORE_CLOSE); + hook = NULL; + } error = filemon_unload(); #ifdef _MODULE if (!error) Index: kern/kern_exit.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_exit.c,v retrieving revision 1.248 diff -u -p -r1.248 kern_exit.c --- kern/kern_exit.c 13 Oct 2015 06:47:21 -0000 1.248 +++ kern/kern_exit.c 7 Jan 2016 23:55:02 -0000 @@ -279,10 +279,11 @@ exit1(struct lwp *l, int rv) * Close open files, release open-file table and free signal * actions. This may block! */ + doexithooks(p, EXITHOOK_BEFORE_CLOSE); fd_free(); cwdfree(p->p_cwdi); p->p_cwdi = NULL; - doexithooks(p); + doexithooks(p, EXITHOOK_AFTER_CLOSE); sigactsfree(p->p_sigacts); /* Index: kern/kern_hook.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_hook.c,v retrieving revision 1.6 diff -u -p -r1.6 kern_hook.c --- kern/kern_hook.c 22 Nov 2013 21:04:11 -0000 1.6 +++ kern/kern_hook.c 7 Jan 2016 23:55:13 -0000 @@ -219,26 +219,33 @@ doexechooks(struct proc *p) hook_proc_run(&exechook_list, p); } -static hook_list_t exithook_list = LIST_HEAD_INITIALIZER(exithook_list); +static hook_list_t exithook_list0 = LIST_HEAD_INITIALIZER(exithook_list0); +static hook_list_t exithook_list1 = LIST_HEAD_INITIALIZER(exithook_list1); +static hook_list_t *exithook_table[] = { + &exithook_list0, /* EXITHOOK_BEFORE_CLOSE */ + &exithook_list1, /* EXITHOOK_AFTER_CLOSE */ +}; extern krwlock_t exec_lock; void * -exithook_establish(void (*fn)(struct proc *, void *), void *arg) +exithook_establish(void (*fn)(struct proc *, void *), void *arg, int ph) { void *rv; + KASSERT(ph >= 0 && ph < __arraycount(exithook_table)); rw_enter(&exec_lock, RW_WRITER); - rv = hook_establish(&exithook_list, (void (*)(void *))fn, arg); + rv = hook_establish(exithook_table[ph], (void (*)(void *))fn, arg); rw_exit(&exec_lock); return rv; } void -exithook_disestablish(void *vhook) +exithook_disestablish(void *vhook, int ph) { + KASSERT(ph >= 0 && ph < __arraycount(exithook_table)); rw_enter(&exec_lock, RW_WRITER); - hook_disestablish(&exithook_list, vhook); + hook_disestablish(exithook_table[ph], vhook); rw_exit(&exec_lock); } @@ -246,9 +253,11 @@ exithook_disestablish(void *vhook) * Run exit hooks. */ void -doexithooks(struct proc *p) +doexithooks(struct proc *p, int ph) { - hook_proc_run(&exithook_list, p); + + KASSERT(ph >= 0 && ph < __arraycount(exithook_table)); + hook_proc_run(exithook_table[ph], p); } static hook_list_t forkhook_list = LIST_HEAD_INITIALIZER(forkhook_list); Index: kern/sys_aio.c =================================================================== RCS file: /cvsroot/src/sys/kern/sys_aio.c,v retrieving revision 1.40 diff -u -p -r1.40 sys_aio.c --- kern/sys_aio.c 5 Sep 2014 09:20:59 -0000 1.40 +++ kern/sys_aio.c 7 Jan 2016 23:55:29 -0000 @@ -131,7 +131,7 @@ aio_fini(bool interface) sysctl_teardown(&aio_sysctl); KASSERT(aio_jobs_count == 0); - exithook_disestablish(aio_ehook); + exithook_disestablish(aio_ehook, EXITHOOK_AFTER_CLOSE); pool_destroy(&aio_job_pool); pool_destroy(&aio_lio_pool); return 0; @@ -149,7 +149,7 @@ aio_init(void) "aio_jobs_pool", &pool_allocator_nointr, IPL_NONE); pool_init(&aio_lio_pool, sizeof(struct lio_req), 0, 0, 0, "aio_lio_pool", &pool_allocator_nointr, IPL_NONE); - aio_ehook = exithook_establish(aio_exit, NULL); + aio_ehook = exithook_establish(aio_exit, NULL, EXITHOOK_AFTER_CLOSE); error = sysctl_aio_init(); if (error != 0) { Index: kern/sysv_sem.c =================================================================== RCS file: /cvsroot/src/sys/kern/sysv_sem.c,v retrieving revision 1.95 diff -u -p -r1.95 sysv_sem.c --- kern/sysv_sem.c 6 Nov 2015 02:26:42 -0000 1.95 +++ kern/sysv_sem.c 7 Jan 2016 23:55:46 -0000 @@ -153,7 +153,7 @@ static int seminit_exithook(void) { - hook = exithook_establish(semexit, NULL); + hook = exithook_establish(semexit, NULL, EXITHOOK_AFTER_CLOSE); return 0; } @@ -172,7 +172,7 @@ semfini(void) /* Remove the exit hook */ if (hook) - exithook_disestablish(hook); + exithook_disestablish(hook, EXITHOOK_AFTER_CLOSE); /* Destroy all our condvars */ for (i = 0; i < seminfo.semmni; i++) { Index: rump/librump/rumpkern/lwproc.c =================================================================== RCS file: /cvsroot/src/sys/rump/librump/rumpkern/lwproc.c,v retrieving revision 1.35 diff -u -p -r1.35 lwproc.c --- rump/librump/rumpkern/lwproc.c 18 Apr 2015 15:49:18 -0000 1.35 +++ rump/librump/rumpkern/lwproc.c 7 Jan 2016 23:56:08 -0000 @@ -117,7 +117,8 @@ lwproc_proc_free(struct proc *p) chgproccnt(kauth_cred_getuid(cred), -1); rump_proc_vfs_release(p); - doexithooks(p); + doexithooks(p, EXITHOOK_BEFORE_CLOSE); + doexithooks(p, EXITHOOK_AFTER_CLOSE); lim_free(p->p_limit); pstatsfree(p->p_stats); kauth_cred_free(p->p_cred); Index: sys/systm.h =================================================================== RCS file: /cvsroot/src/sys/sys/systm.h,v retrieving revision 1.269 diff -u -p -r1.269 systm.h --- sys/systm.h 29 Oct 2015 00:27:08 -0000 1.269 +++ sys/systm.h 7 Jan 2016 23:56:23 -0000 @@ -375,9 +375,11 @@ void doexechooks(struct proc *); /* * Exit hooks. Subsystems may want to do cleanup when a process exits. */ -void *exithook_establish(void (*)(struct proc *, void *), void *); -void exithook_disestablish(void *); -void doexithooks(struct proc *); +#define EXITHOOK_BEFORE_CLOSE 0 +#define EXITHOOK_AFTER_CLOSE 1 +void *exithook_establish(void (*)(struct proc *, void *), void *, int); +void exithook_disestablish(void *, int); +void doexithooks(struct proc *, int); /* * Fork hooks. Subsystems may want to do special processing when a process