Procházet zdrojové kódy

Fix stdout races. Sometimes SIGCHILD appears before all data were read from process stdout

Serj Kalichev před 1 rokem
rodič
revize
2757cc017e
1 změnil soubory, kde provedl 24 přidání a 68 odebrání
  1. 24 68
      klish/ktp/ktpd_session.c

+ 24 - 68
klish/ktp/ktpd_session.c

@@ -60,6 +60,7 @@ static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	void *associated_data, void *user_data);
 static bool_t action_stderr_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	void *associated_data, void *user_data);
+static bool_t get_stream(ktpd_session_t *ktpd, int fd, bool_t is_stderr);
 
 
 ktpd_session_t *ktpd_session_new(int sock, kscheme_t *scheme,
@@ -435,6 +436,11 @@ static bool_t wait_for_actions_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	if (!kexec_retcode(ktpd->exec, &retcode))
 		return BOOL_TRUE; // Continue
 
+	// Sometimes SIGCHILD signal can appear before all data were really read
+	// from process stdout buffer. So read the least data before closing
+	// file descriptors and send it to client.
+	get_stream(ktpd, kexec_stdout(ktpd->exec), BOOL_FALSE);
+	get_stream(ktpd, kexec_stderr(ktpd->exec), BOOL_TRUE);
 	faux_eloop_del_fd(eloop, kexec_stdout(ktpd->exec));
 	faux_eloop_del_fd(eloop, kexec_stderr(ktpd->exec));
 
@@ -1096,11 +1102,8 @@ int ktpd_session_fd(const ktpd_session_t *ktpd)
 }
 
 
-static bool_t get_stdout(faux_eloop_t *eloop, faux_eloop_type_e type,
-	void *associated_data, void *user_data)
+static bool_t get_stream(ktpd_session_t *ktpd, int fd, bool_t is_stderr)
 {
-	faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
-	ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
 	ssize_t r = -1;
 	faux_buf_t *faux_buf = NULL;
 	char *buf = NULL;
@@ -1112,7 +1115,10 @@ static bool_t get_stdout(faux_eloop_t *eloop, faux_eloop_type_e type,
 	if (!ktpd->exec)
 		return BOOL_TRUE;
 
-	faux_buf = kexec_bufout(ktpd->exec);
+	if (is_stderr)
+		faux_buf = kexec_buferr(ktpd->exec);
+	else
+		faux_buf = kexec_bufout(ktpd->exec);
 	assert(faux_buf);
 
 	do {
@@ -1122,7 +1128,7 @@ static bool_t get_stdout(faux_eloop_t *eloop, faux_eloop_type_e type,
 			faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
 		// Non-blocked read. The fd became non-blocked while
 		// kexec_prepare().
-		r = read(info->fd, linear_buf, linear_len);
+		r = read(fd, linear_buf, linear_len);
 		if (r > 0)
 			really_readed = r;
 		faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
@@ -1135,18 +1141,14 @@ static bool_t get_stdout(faux_eloop_t *eloop, faux_eloop_type_e type,
 	buf = malloc(len);
 	faux_buf_read(faux_buf, buf, len);
 
-	// Create KTP_STDOUT message to send to client
-	ack = ktp_msg_preform(KTP_STDOUT, KTP_STATUS_NONE);
+	// Create KTP_STDOUT/KTP_STDERR message to send to client
+	ack = ktp_msg_preform(is_stderr ? KTP_STDERR : KTP_STDOUT, KTP_STATUS_NONE);
 	faux_msg_add_param(ack, KTP_PARAM_LINE, buf, len);
 	faux_msg_send_async(ack, ktpd->async);
 	faux_msg_free(ack);
 
 	free(buf);
 
-	// Happy compiler
-	eloop = eloop;
-	type = type;
-
 	return BOOL_TRUE;
 }
 
@@ -1156,6 +1158,7 @@ static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	void *associated_data, void *user_data)
 {
 	faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
+	ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
 
 	// Interactive command use these function as callback not only for
 	// getting stdout but for writing stdin too. Because pseudo-terminal
@@ -1163,19 +1166,13 @@ static bool_t action_stdout_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 	if (info->revents & POLLOUT)
 		push_stdin(eloop, type, associated_data, user_data);
 
-	// Some errors or fd is closed so remove it from polling
 	if (info->revents & POLLIN)
-		get_stdout(eloop, type, associated_data, user_data);
+		get_stream(ktpd, info->fd, BOOL_FALSE);
 
+	// Some errors or fd is closed so remove it from polling
 	// EOF || POLERR || POLLNVAL
-	if (info->revents & (POLLHUP | POLLERR | POLLNVAL)) {
+	if (info->revents & (POLLHUP | POLLERR | POLLNVAL))
 		faux_eloop_del_fd(eloop, info->fd);
-//		syslog(LOG_DEBUG, "Close fd %d (%x)", info->fd, info->revents);
-	}
-
-	// Happy compiler
-	eloop = eloop;
-	type = type;
 
 	return BOOL_TRUE;
 }
@@ -1186,57 +1183,16 @@ static bool_t action_stderr_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 {
 	faux_eloop_info_fd_t *info = (faux_eloop_info_fd_t *)associated_data;
 	ktpd_session_t *ktpd = (ktpd_session_t *)user_data;
-	ssize_t r = -1;
-	faux_buf_t *faux_buf = NULL;
-	char *buf = NULL;
-	ssize_t len = 0;
-	faux_msg_t *ack = NULL;
+
+	if (info->revents & POLLIN)
+		get_stream(ktpd, info->fd, BOOL_TRUE);
 
 	// Some errors or fd is closed so remove it from polling
-	if (!(info->revents & POLLIN)) {
+	// EOF || POLERR || POLLNVAL
+	if (info->revents & (POLLHUP | POLLERR | POLLNVAL))
 		faux_eloop_del_fd(eloop, info->fd);
-		return BOOL_TRUE;
-	}
 
-	if (!ktpd)
-		return BOOL_TRUE;
-	if (!ktpd->exec)
-		return BOOL_TRUE;
-
-	faux_buf = kexec_buferr(ktpd->exec);
-	assert(faux_buf);
-
-	do {
-		void *linear_buf = NULL;
-		ssize_t really_readed = 0;
-		ssize_t linear_len =
-			faux_buf_dwrite_lock_easy(faux_buf, &linear_buf);
-		// Non-blocked read. The fd became non-blocked while
-		// kexec_prepare().
-		r = read(info->fd, linear_buf, linear_len);
-		if (r > 0)
-			really_readed = r;
-		faux_buf_dwrite_unlock_easy(faux_buf, really_readed);
-	} while (r > 0);
-
-	len = faux_buf_len(faux_buf);
-	if (0 == len)
-		return BOOL_TRUE;
-
-	buf = malloc(len);
-	faux_buf_read(faux_buf, buf, len);
-
-	// Create KTP_STDERR message to send to client
-	ack = ktp_msg_preform(KTP_STDERR, KTP_STATUS_NONE);
-	faux_msg_add_param(ack, KTP_PARAM_LINE, buf, len);
-	faux_msg_send_async(ack, ktpd->async);
-	faux_msg_free(ack);
-
-	free(buf);
-
-	// Happy compiler
-	eloop = eloop;
-	type = type;
+	type = type; // Happy compiler
 
 	return BOOL_TRUE;
 }