mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	zed: use separate reaper thread and collect ZEDLETs asynchronously
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz> Closes #11807
This commit is contained in:
		
							parent
							
								
									84c1652a0a
								
							
						
					
					
						commit
						9160c441a4
					
				| @ -60,8 +60,8 @@ _setup_sig_handlers(void) | |||||||
| 		zed_log_die("Failed to initialize sigset"); | 		zed_log_die("Failed to initialize sigset"); | ||||||
| 
 | 
 | ||||||
| 	sa.sa_flags = SA_RESTART; | 	sa.sa_flags = SA_RESTART; | ||||||
| 	sa.sa_handler = SIG_IGN; |  | ||||||
| 
 | 
 | ||||||
|  | 	sa.sa_handler = SIG_IGN; | ||||||
| 	if (sigaction(SIGPIPE, &sa, NULL) < 0) | 	if (sigaction(SIGPIPE, &sa, NULL) < 0) | ||||||
| 		zed_log_die("Failed to ignore SIGPIPE"); | 		zed_log_die("Failed to ignore SIGPIPE"); | ||||||
| 
 | 
 | ||||||
| @ -75,6 +75,10 @@ _setup_sig_handlers(void) | |||||||
| 	sa.sa_handler = _hup_handler; | 	sa.sa_handler = _hup_handler; | ||||||
| 	if (sigaction(SIGHUP, &sa, NULL) < 0) | 	if (sigaction(SIGHUP, &sa, NULL) < 0) | ||||||
| 		zed_log_die("Failed to register SIGHUP handler"); | 		zed_log_die("Failed to register SIGHUP handler"); | ||||||
|  | 
 | ||||||
|  | 	(void) sigaddset(&sa.sa_mask, SIGCHLD); | ||||||
|  | 	if (pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL) < 0) | ||||||
|  | 		zed_log_die("Failed to block SIGCHLD"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ | |||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <libzfs.h>			/* FIXME: Replace with libzfs_core. */ | #include <libzfs_core.h> | ||||||
| #include <paths.h> | #include <paths.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| @ -96,6 +96,8 @@ zed_event_fini(struct zed_conf *zcp) | |||||||
| 		libzfs_fini(zcp->zfs_hdl); | 		libzfs_fini(zcp->zfs_hdl); | ||||||
| 		zcp->zfs_hdl = NULL; | 		zcp->zfs_hdl = NULL; | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	zed_exec_fini(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | |||||||
| @ -18,10 +18,13 @@ | |||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <sys/avl.h> | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
| #include <sys/wait.h> | #include <sys/wait.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
|  | #include <pthread.h> | ||||||
| #include "zed_exec.h" | #include "zed_exec.h" | ||||||
| #include "zed_file.h" | #include "zed_file.h" | ||||||
| #include "zed_log.h" | #include "zed_log.h" | ||||||
| @ -29,6 +32,38 @@ | |||||||
| 
 | 
 | ||||||
| #define	ZEVENT_FILENO	3 | #define	ZEVENT_FILENO	3 | ||||||
| 
 | 
 | ||||||
|  | struct launched_process_node { | ||||||
|  | 	avl_node_t node; | ||||||
|  | 	pid_t pid; | ||||||
|  | 	uint64_t eid; | ||||||
|  | 	char *name; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | _launched_process_node_compare(const void *x1, const void *x2) | ||||||
|  | { | ||||||
|  | 	pid_t p1; | ||||||
|  | 	pid_t p2; | ||||||
|  | 
 | ||||||
|  | 	assert(x1 != NULL); | ||||||
|  | 	assert(x2 != NULL); | ||||||
|  | 
 | ||||||
|  | 	p1 = ((const struct launched_process_node *) x1)->pid; | ||||||
|  | 	p2 = ((const struct launched_process_node *) x2)->pid; | ||||||
|  | 
 | ||||||
|  | 	if (p1 < p2) | ||||||
|  | 		return (-1); | ||||||
|  | 	else if (p1 == p2) | ||||||
|  | 		return (0); | ||||||
|  | 	else | ||||||
|  | 		return (1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static pthread_t _reap_children_tid = (pthread_t)-1; | ||||||
|  | static volatile boolean_t _reap_children_stop; | ||||||
|  | static avl_tree_t _launched_processes; | ||||||
|  | static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER; | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Create an environment string array for passing to execve() using the |  * Create an environment string array for passing to execve() using the | ||||||
|  * NAME=VALUE strings in container [zsp]. |  * NAME=VALUE strings in container [zsp]. | ||||||
| @ -85,8 +120,8 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog, | |||||||
| 	int n; | 	int n; | ||||||
| 	pid_t pid; | 	pid_t pid; | ||||||
| 	int fd; | 	int fd; | ||||||
| 	pid_t wpid; | 	struct launched_process_node *node; | ||||||
| 	int status; | 	sigset_t mask; | ||||||
| 
 | 
 | ||||||
| 	assert(dir != NULL); | 	assert(dir != NULL); | ||||||
| 	assert(prog != NULL); | 	assert(prog != NULL); | ||||||
| @ -107,6 +142,9 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog, | |||||||
| 		    prog, eid, strerror(errno)); | 		    prog, eid, strerror(errno)); | ||||||
| 		return; | 		return; | ||||||
| 	} else if (pid == 0) { | 	} else if (pid == 0) { | ||||||
|  | 		(void) sigemptyset(&mask); | ||||||
|  | 		(void) sigprocmask(SIG_SETMASK, &mask, NULL); | ||||||
|  | 
 | ||||||
| 		(void) umask(022); | 		(void) umask(022); | ||||||
| 		if ((fd = open("/dev/null", O_RDWR)) != -1) { | 		if ((fd = open("/dev/null", O_RDWR)) != -1) { | ||||||
| 			(void) dup2(fd, STDIN_FILENO); | 			(void) dup2(fd, STDIN_FILENO); | ||||||
| @ -124,59 +162,110 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog, | |||||||
| 	zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d", | 	zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d", | ||||||
| 	    prog, eid, pid); | 	    prog, eid, pid); | ||||||
| 
 | 
 | ||||||
| 	/* FIXME: Timeout rogue child processes with sigalarm? */ | 	node = calloc(1, sizeof (*node)); | ||||||
| 
 | 	if (node) { | ||||||
| 	/*
 | 		node->pid = pid; | ||||||
| 	 * Wait for child process using WNOHANG to limit | 		node->eid = eid; | ||||||
| 	 * the time spent waiting to 10 seconds (10,000ms). | 		node->name = strdup(prog); | ||||||
| 	 */ | 		(void) pthread_mutex_lock(&_launched_processes_lock); | ||||||
| 	for (n = 0; n < 1000; n++) { | 		avl_add(&_launched_processes, node); | ||||||
| 		wpid = waitpid(pid, &status, WNOHANG); | 		(void) pthread_mutex_unlock(&_launched_processes_lock); | ||||||
| 		if (wpid == (pid_t)-1) { |  | ||||||
| 			if (errno == EINTR) |  | ||||||
| 				continue; |  | ||||||
| 			zed_log_msg(LOG_WARNING, |  | ||||||
| 			    "Failed to wait for \"%s\" eid=%llu pid=%d", |  | ||||||
| 			    prog, eid, pid); |  | ||||||
| 			break; |  | ||||||
| 		} else if (wpid == 0) { |  | ||||||
| 			struct timespec t; |  | ||||||
| 
 |  | ||||||
| 			/* child still running */ |  | ||||||
| 			t.tv_sec = 0; |  | ||||||
| 			t.tv_nsec = 10000000;	/* 10ms */ |  | ||||||
| 			(void) nanosleep(&t, NULL); |  | ||||||
| 			continue; |  | ||||||
| 	} | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | _nop(int sig) | ||||||
|  | {} | ||||||
|  | 
 | ||||||
|  | static void * | ||||||
|  | _reap_children(void *arg) | ||||||
|  | { | ||||||
|  | 	struct launched_process_node node, *pnode; | ||||||
|  | 	pid_t pid; | ||||||
|  | 	int status; | ||||||
|  | 	struct sigaction sa = {}; | ||||||
|  | 
 | ||||||
|  | 	(void) sigfillset(&sa.sa_mask); | ||||||
|  | 	(void) sigdelset(&sa.sa_mask, SIGCHLD); | ||||||
|  | 	(void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL); | ||||||
|  | 
 | ||||||
|  | 	(void) sigemptyset(&sa.sa_mask); | ||||||
|  | 	sa.sa_handler = _nop; | ||||||
|  | 	sa.sa_flags = SA_NOCLDSTOP; | ||||||
|  | 	(void) sigaction(SIGCHLD, &sa, NULL); | ||||||
|  | 
 | ||||||
|  | 	for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) { | ||||||
|  | 		pid = waitpid(0, &status, 0); | ||||||
|  | 
 | ||||||
|  | 		if (pid == (pid_t)-1) { | ||||||
|  | 			if (errno == ECHILD) | ||||||
|  | 				pause(); | ||||||
|  | 			else if (errno != EINTR) | ||||||
|  | 				zed_log_msg(LOG_WARNING, | ||||||
|  | 				    "Failed to wait for children: %s", | ||||||
|  | 				    strerror(errno)); | ||||||
|  | 		} else { | ||||||
|  | 			memset(&node, 0, sizeof (node)); | ||||||
|  | 			node.pid = pid; | ||||||
|  | 			(void) pthread_mutex_lock(&_launched_processes_lock); | ||||||
|  | 			pnode = avl_find(&_launched_processes, &node, NULL); | ||||||
|  | 			if (pnode) { | ||||||
|  | 				memcpy(&node, pnode, sizeof (node)); | ||||||
|  | 
 | ||||||
|  | 				avl_remove(&_launched_processes, pnode); | ||||||
|  | 				free(pnode); | ||||||
|  | 			} | ||||||
|  | 			(void) pthread_mutex_unlock(&_launched_processes_lock); | ||||||
| 
 | 
 | ||||||
| 			if (WIFEXITED(status)) { | 			if (WIFEXITED(status)) { | ||||||
| 				zed_log_msg(LOG_INFO, | 				zed_log_msg(LOG_INFO, | ||||||
| 				    "Finished \"%s\" eid=%llu pid=%d exit=%d", | 				    "Finished \"%s\" eid=%llu pid=%d exit=%d", | ||||||
| 			    prog, eid, pid, WEXITSTATUS(status)); | 				    node.name, node.eid, pid, | ||||||
|  | 				    WEXITSTATUS(status)); | ||||||
| 			} else if (WIFSIGNALED(status)) { | 			} else if (WIFSIGNALED(status)) { | ||||||
| 				zed_log_msg(LOG_INFO, | 				zed_log_msg(LOG_INFO, | ||||||
| 				    "Finished \"%s\" eid=%llu pid=%d sig=%d/%s", | 				    "Finished \"%s\" eid=%llu pid=%d sig=%d/%s", | ||||||
| 			    prog, eid, pid, WTERMSIG(status), | 				    node.name, node.eid, pid, WTERMSIG(status), | ||||||
| 				    strsignal(WTERMSIG(status))); | 				    strsignal(WTERMSIG(status))); | ||||||
| 			} else { | 			} else { | ||||||
| 				zed_log_msg(LOG_INFO, | 				zed_log_msg(LOG_INFO, | ||||||
| 			    "Finished \"%s\" eid=%llu pid=%d status=0x%X", | 				    "Finished \"%s\" eid=%llu pid=%d " | ||||||
| 			    prog, eid, (unsigned int) status); | 				    "status=0x%X", | ||||||
| 		} | 				    node.name, node.eid, (unsigned int) status); | ||||||
| 		break; |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 	/*
 | 			free(node.name); | ||||||
| 	 * kill child process after 10 seconds |  | ||||||
| 	 */ |  | ||||||
| 	if (wpid == 0) { |  | ||||||
| 		zed_log_msg(LOG_WARNING, "Killing hung \"%s\" pid=%d", |  | ||||||
| 		    prog, pid); |  | ||||||
| 		(void) kill(pid, SIGKILL); |  | ||||||
| 		(void) waitpid(pid, &status, 0); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	return (NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | zed_exec_fini(void) | ||||||
|  | { | ||||||
|  | 	struct launched_process_node *node; | ||||||
|  | 	void *ck = NULL; | ||||||
|  | 
 | ||||||
|  | 	if (_reap_children_tid == (pthread_t)-1) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	_reap_children_stop = B_TRUE; | ||||||
|  | 	(void) pthread_kill(_reap_children_tid, SIGCHLD); | ||||||
|  | 	(void) pthread_join(_reap_children_tid, NULL); | ||||||
|  | 
 | ||||||
|  | 	while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) { | ||||||
|  | 		free(node->name); | ||||||
|  | 		free(node); | ||||||
|  | 	} | ||||||
|  | 	avl_destroy(&_launched_processes); | ||||||
|  | 
 | ||||||
|  | 	(void) pthread_mutex_destroy(&_launched_processes_lock); | ||||||
|  | 	(void) pthread_mutex_init(&_launched_processes_lock, NULL); | ||||||
|  | 
 | ||||||
|  | 	_reap_children_tid = (pthread_t)-1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Process the event [eid] by synchronously invoking all zedlets with a |  * Process the event [eid] by synchronously invoking all zedlets with a | ||||||
|  * matching class prefix. |  * matching class prefix. | ||||||
| @ -206,6 +295,17 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass, | |||||||
| 	if (!dir || !zedlets || !envs || zfd < 0) | 	if (!dir || !zedlets || !envs || zfd < 0) | ||||||
| 		return (-1); | 		return (-1); | ||||||
| 
 | 
 | ||||||
|  | 	if (_reap_children_tid == (pthread_t)-1) { | ||||||
|  | 		if (pthread_create(&_reap_children_tid, NULL, | ||||||
|  | 		    _reap_children, NULL) != 0) | ||||||
|  | 			return (-1); | ||||||
|  | 		pthread_setname_np(_reap_children_tid, "reap ZEDLETs"); | ||||||
|  | 
 | ||||||
|  | 		avl_create(&_launched_processes, _launched_process_node_compare, | ||||||
|  | 		    sizeof (struct launched_process_node), | ||||||
|  | 		    offsetof(struct launched_process_node, node)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	csp = class_strings; | 	csp = class_strings; | ||||||
| 
 | 
 | ||||||
| 	if (class) | 	if (class) | ||||||
|  | |||||||
| @ -18,6 +18,8 @@ | |||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include "zed_strings.h" | #include "zed_strings.h" | ||||||
| 
 | 
 | ||||||
|  | void zed_exec_fini(void); | ||||||
|  | 
 | ||||||
| int zed_exec_process(uint64_t eid, const char *class, const char *subclass, | int zed_exec_process(uint64_t eid, const char *class, const char *subclass, | ||||||
|     const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, |     const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, | ||||||
|     int zevent_fd); |     int zevent_fd); | ||||||
|  | |||||||
| @ -231,12 +231,6 @@ Terminate the daemon. | |||||||
| 
 | 
 | ||||||
| .SH BUGS | .SH BUGS | ||||||
| .PP | .PP | ||||||
| Events are processed synchronously by a single thread.  This can delay the |  | ||||||
| processing of simultaneous zevents. |  | ||||||
| .PP |  | ||||||
| ZEDLETs are killed after a maximum of ten seconds. |  | ||||||
| This can lead to a violation of a ZEDLET's atomicity assumptions. |  | ||||||
| .PP |  | ||||||
| The ownership and permissions of the \fIenabled-zedlets\fR directory (along | The ownership and permissions of the \fIenabled-zedlets\fR directory (along | ||||||
| with all parent directories) are not checked.  If any of these directories | with all parent directories) are not checked.  If any of these directories | ||||||
| are improperly owned or permissioned, an unprivileged user could insert a | are improperly owned or permissioned, an unprivileged user could insert a | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 наб
						наб