tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: In-kernel process exit hooks?



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


Home | Main Index | Thread Index | Old Index