Browse Source

ksession: Get stdout of forked action

Serj Kalichev 2 years ago
parent
commit
8ea2b54f61
3 changed files with 101 additions and 0 deletions
  1. 10 0
      klish/kexec.h
  2. 36 0
      klish/ksession/kexec.c
  3. 55 0
      klish/ksession/ksession.c

+ 10 - 0
klish/kexec.h

@@ -7,6 +7,7 @@
 #define _klish_kexec_h
 
 #include <faux/list.h>
+#include <faux/buf.h>
 #include <klish/kcontext.h>
 
 typedef struct kexec_s kexec_t;
@@ -31,6 +32,15 @@ bool_t kexec_set_stdout(kexec_t *exec, int stdout);
 // STDERR
 int kexec_stderr(const kexec_t *exec);
 bool_t kexec_set_stderr(kexec_t *exec, int stderr);
+// BUFIN
+faux_buf_t *kexec_bufin(const kexec_t *exec);
+bool_t kexec_set_bufin(kexec_t *exec, faux_buf_t *bufin);
+// BUFOUT
+faux_buf_t *kexec_bufout(const kexec_t *exec);
+bool_t kexec_set_bufout(kexec_t *exec, faux_buf_t *bufout);
+// BUFERR
+faux_buf_t *kexec_buferr(const kexec_t *exec);
+bool_t kexec_set_buferr(kexec_t *exec, faux_buf_t *buferr);
 // Return code
 bool_t kexec_done(const kexec_t *exec);
 bool_t kexec_retcode(const kexec_t *exec, int *status);

+ 36 - 0
klish/ksession/kexec.c

@@ -6,8 +6,10 @@
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <fcntl.h>
 
 #include <faux/list.h>
+#include <faux/buf.h>
 #include <klish/khelper.h>
 #include <klish/kcontext.h>
 #include <klish/kexec.h>
@@ -18,6 +20,9 @@ struct kexec_s {
 	int stdin;
 	int stdout;
 	int stderr;
+	faux_buf_t *bufin;
+	faux_buf_t *bufout;
+	faux_buf_t *buferr;
 };
 
 // Dry-run
@@ -36,6 +41,18 @@ KSET(exec, int, stdout);
 KGET(exec, int, stderr);
 KSET(exec, int, stderr);
 
+// BufIN
+KGET(exec, faux_buf_t *, bufin);
+KSET(exec, faux_buf_t *, bufin);
+
+// BufOUT
+KGET(exec, faux_buf_t *, bufout);
+KSET(exec, faux_buf_t *, bufout);
+
+// BufERR
+KGET(exec, faux_buf_t *, buferr);
+KSET(exec, faux_buf_t *, buferr);
+
 // CONTEXT list
 KADD_NESTED(exec, kcontext_t *, contexts);
 KNESTED_LEN(exec, contexts);
@@ -65,6 +82,10 @@ kexec_t *kexec_new()
 	exec->stdout = -1;
 	exec->stderr = -1;
 
+	exec->bufin = faux_buf_new(0);
+	exec->bufout = faux_buf_new(0);
+	exec->buferr = faux_buf_new(0);
+
 	return exec;
 }
 
@@ -76,6 +97,10 @@ void kexec_free(kexec_t *exec)
 
 	faux_list_free(exec->contexts);
 
+	faux_buf_free(exec->bufin);
+	faux_buf_free(exec->bufout);
+	faux_buf_free(exec->buferr);
+
 	free(exec);
 }
 
@@ -165,6 +190,7 @@ static bool_t kexec_prepare(kexec_t *exec)
 	int pipefd[2] = {};
 	faux_list_node_t *iter = NULL;
 	int global_stderr = -1;
+	int fflags = 0;
 
 	assert(exec);
 	if (!exec)
@@ -187,6 +213,9 @@ static bool_t kexec_prepare(kexec_t *exec)
 	// STDOUT
 	if (pipe(pipefd) < 0)
 		return BOOL_FALSE;
+	// Read end of 'stdout' pipe must be non-blocked
+	fflags = fcntl(pipefd[0], F_GETFL);
+	fcntl(pipefd[0], F_SETFL, fflags | O_NONBLOCK);
 	kexec_set_stdout(exec, pipefd[0]); // Read end
 	kcontext_set_stdout(faux_list_data(faux_list_tail(exec->contexts)),
 		pipefd[1]); // Write end
@@ -194,6 +223,9 @@ static bool_t kexec_prepare(kexec_t *exec)
 	// STDERR
 	if (pipe(pipefd) < 0)
 		return BOOL_FALSE;
+	// Read end of 'stderr' pipe must be non-blocked
+	fflags = fcntl(pipefd[0], F_GETFL);
+	fcntl(pipefd[0], F_SETFL, fflags | O_NONBLOCK);
 	kexec_set_stderr(exec, pipefd[0]); // Read end
 	// STDERR write end will be set to all list members as stderr
 	global_stderr = pipefd[1]; // Write end
@@ -264,6 +296,10 @@ static bool_t exec_action(kcontext_t *context, const kaction_t *action,
 	}
 
 	// Child
+	dup2(kcontext_stdin(context), STDIN_FILENO);
+	dup2(kcontext_stdout(context), STDOUT_FILENO);
+	dup2(kcontext_stderr(context), STDERR_FILENO);
+
 	_exit(fn(context));
 
 	return BOOL_TRUE;

+ 55 - 0
klish/ksession/ksession.c

@@ -7,9 +7,11 @@
 #include <signal.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include <faux/argv.h>
 #include <faux/eloop.h>
+#include <faux/buf.h>
 #include <klish/khelper.h>
 #include <klish/kview.h>
 #include <klish/kscheme.h>
@@ -138,11 +140,47 @@ static bool_t action_terminated_ev(faux_eloop_t *eloop, faux_eloop_type_e type,
 }
 
 
+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;
+	kexec_t *exec = (kexec_t *)user_data;
+	ssize_t r = -1;
+	faux_buf_t *faux_buf = NULL;
+	void *linear_buf = NULL;
+
+	if (!exec)
+		return BOOL_FALSE;
+
+	faux_buf = kexec_bufout(exec);
+	assert(faux_buf);
+
+	do {
+		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);
+
+	// Happy compiler
+	eloop = eloop;
+	type = type;
+
+	return BOOL_TRUE;
+}
+
+
 bool_t ksession_exec_locally(ksession_t *session, const char *line,
 	int *retcode, faux_error_t *error)
 {
 	kexec_t *exec = NULL;
 	faux_eloop_t *eloop = NULL;
+	faux_buf_t *buf = NULL;
 
 	assert(session);
 	if (!session)
@@ -173,10 +211,27 @@ bool_t ksession_exec_locally(ksession_t *session, const char *line,
 	faux_eloop_add_signal(eloop, SIGTERM, stop_loop_ev, session);
 	faux_eloop_add_signal(eloop, SIGQUIT, stop_loop_ev, session);
 	faux_eloop_add_signal(eloop, SIGCHLD, action_terminated_ev, exec);
+	faux_eloop_add_fd(eloop, kexec_stdout(exec), POLLIN,
+		action_stdout_ev, exec);
 	faux_eloop_loop(eloop);
 	faux_eloop_free(eloop);
 
 	kexec_retcode(exec, retcode);
 
+	{
+	printf("STDOUT:\n");
+	ssize_t r = 0;
+	buf = kexec_bufout(exec);
+	do {
+		void *d = NULL;
+		ssize_t really_readed = 0;
+		r = faux_buf_dread_lock_easy(buf, &d);
+		if (r > 0) {
+			really_readed = write(STDOUT_FILENO, d, r);
+		}
+		faux_buf_dread_unlock_easy(buf, really_readed);
+	} while (r > 0);
+	}
+
 	return BOOL_TRUE;
 }