Browse Source

pty: Begin to implement pseudo-terminal. Unfinished

Serj Kalichev 1 year ago
parent
commit
df3cf0bff9
6 changed files with 117 additions and 2 deletions
  1. 1 0
      klish/kentry.h
  2. 1 0
      klish/kexec.h
  3. 18 0
      klish/kscheme/kentry.c
  4. 69 2
      klish/ksession/kexec.c
  5. 26 0
      klish/ksession/ksession_parse.c
  6. 2 0
      klish/ktp/ktpd_session.c

+ 1 - 0
klish/kentry.h

@@ -110,6 +110,7 @@ bool_t kentry_add_actions(kentry_t *entry, kaction_t *action);
 ssize_t kentry_actions_len(const kentry_t *entry);
 kentry_actions_node_t *kentry_actions_iter(const kentry_t *entry);
 kaction_t *kentry_actions_each(kentry_actions_node_t **iter);
+bool_t kentry_interactive(const kentry_t *entry);
 
 // HOTKEYs
 faux_list_t *kentry_hotkeys(const kentry_t *entry);

+ 1 - 0
klish/kexec.h

@@ -55,6 +55,7 @@ kcontext_t *kexec_contexts_each(kexec_contexts_node_t **iter);
 
 bool_t kexec_continue_command_execution(kexec_t *exec, pid_t pid, int wstatus);
 bool_t kexec_exec(kexec_t *exec);
+bool_t kexec_interactive(const kexec_t *exec);
 
 
 C_DECL_END

+ 18 - 0
klish/kscheme/kentry.c

@@ -2,6 +2,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
+#include <syslog.h>
 
 #include <faux/faux.h>
 #include <faux/str.h>
@@ -302,3 +303,20 @@ bool_t kentry_set_udata(kentry_t *entry, void *data, kentry_udata_free_fn free_f
 
 	return BOOL_TRUE;
 }
+
+
+bool_t kentry_interactive(const kentry_t *entry)
+{
+	kentry_actions_node_t *iter = NULL;
+	kaction_t *action = NULL;
+
+	if (!entry)
+		return BOOL_FALSE;
+	iter = kentry_actions_iter(entry);
+	while ((action = kentry_actions_each(&iter))) {
+		if (kaction_interactive(action))
+			return BOOL_TRUE;
+	}
+
+	return BOOL_FALSE;
+}

+ 69 - 2
klish/ksession/kexec.c

@@ -1,12 +1,15 @@
 /** @file kexec.c
  */
-#include <assert.h>
-#include <stdio.h>
+#define _XOPEN_SOURCE
+#define _XOPEN_SOURCE_EXTENDED
 #include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <syslog.h>
 
 #include <faux/list.h>
 #include <faux/buf.h>
@@ -16,6 +19,10 @@
 #include <klish/kpath.h>
 #include <klish/kexec.h>
 
+
+#define PTMX_PATH "/dev/ptmx"
+
+
 // Declaration of grabber. Implementation is in the grabber.c
 void grabber(int fds[][2]);
 
@@ -235,6 +242,41 @@ static bool_t kexec_prepare(kexec_t *exec)
 	if (kexec_contexts_is_empty(exec))
 		return BOOL_FALSE;
 
+	// If command is interactive then prepare pseudoterminal. Note
+	// interactive commands can't have filters
+	if (kexec_interactive(exec)) {
+		int ptm = -1;
+		char *pts_name = NULL;
+		int pts = -1;
+		kcontext_t *context = (kcontext_t *)faux_list_data(
+			faux_list_head(exec->contexts));
+
+		ptm = open(PTMX_PATH, O_RDWR, O_NOCTTY);
+		if (ptm < 0)
+			return BOOL_FALSE;
+		// Set O_NONBLOCK flag here. Because this flag is ignored while
+		// open() ptmx. I don't know why. fcntl() is working fine.
+		fflags = fcntl(ptm, F_GETFL);
+		fcntl(ptm, F_SETFL, fflags | O_NONBLOCK);
+		grantpt(ptm);
+		unlockpt(ptm);
+		pts_name = ptsname(ptm);
+		pts = open(pts_name, O_RDWR, O_NOCTTY);
+		if (pts < 0) {
+			close(ptm);
+			return BOOL_FALSE;
+		}
+
+		kexec_set_stdin(exec, ptm);
+		kexec_set_stdout(exec, ptm);
+		kexec_set_stderr(exec, ptm);
+		kcontext_set_stdin(context, pts);
+		kcontext_set_stdout(context, pts);
+		kcontext_set_stderr(context, pts);
+
+		return BOOL_TRUE;
+	}
+
 	// Create "global" stdin, stdout, stderr for the whole job execution.
 	// Now function creates only the simple pipes but somedays it will be
 	// able to create pseudo-terminal for interactive sessions.
@@ -618,3 +660,28 @@ bool_t kexec_exec(kexec_t *exec)
 
 	return BOOL_TRUE;
 }
+
+
+bool_t kexec_interactive(const kexec_t *exec)
+{
+	faux_list_node_t *node = NULL;
+	kcontext_t *context = NULL;
+	const kentry_t *entry = NULL;
+
+	assert(exec);
+	if (!exec)
+		return BOOL_FALSE;
+
+	// Only the ACTION of first context can be interactive
+	node = faux_list_head(exec->contexts);
+	if (!node)
+		return BOOL_FALSE;
+	context = (kcontext_t *)faux_list_data(node);
+	if (!context)
+		return BOOL_FALSE;
+	entry = kcontext_command(context);
+	if (!entry)
+		return BOOL_FALSE;
+
+	return kentry_interactive(entry);
+}

+ 26 - 0
klish/ksession/ksession_parse.c

@@ -518,6 +518,7 @@ kexec_t *ksession_parse_for_exec(ksession_t *session, const char *raw_line,
 			faux_list_free(split);
 			return NULL;
 		}
+
 		// Only the first component can have 'restore=true' attribute
 		if ((iter != faux_list_head(split)) &&
 			kentry_restore(kpargv_command(pargv))) {
@@ -530,6 +531,31 @@ kexec_t *ksession_parse_for_exec(ksession_t *session, const char *raw_line,
 			return NULL;
 		}
 
+		// Only the first component can have 'interactive=true' attribute
+		if ((iter != faux_list_head(split)) &&
+			kentry_interactive(kpargv_command(pargv))) {
+			faux_error_sprintf(error, "The filter \"%s\" "
+				"can't be interactive",
+				kentry_name(kpargv_command(pargv)));
+			kpargv_free(pargv);
+			kexec_free(exec);
+			faux_list_free(split);
+			return NULL;
+		}
+
+		// Interactive command can't have filters
+		if ((iter != faux_list_head(split)) &&
+			kexec_interactive(exec)) {
+			faux_error_sprintf(error, "The interactive command \"%s\" "
+				"can't have filters",
+				kentry_name(kpargv_command(pargv)));
+			kpargv_free(pargv);
+			kexec_free(exec);
+			faux_list_free(split);
+			return NULL;
+		}
+
+
 		// Fill the kexec_t
 		context = kcontext_new(KCONTEXT_TYPE_ACTION);
 		assert(context);

+ 2 - 0
klish/ktp/ktpd_session.c

@@ -304,6 +304,8 @@ static bool_t ktpd_session_process_cmd(ktpd_session_t *ktpd, faux_msg_t *msg)
 	if (ktpd->exec) {
 		faux_msg_t *ack = NULL;
 		ktp_status_e status = KTP_STATUS_INCOMPLETED;
+		if (kexec_interactive(ktpd->exec))
+			status |= KTP_STATUS_INTERACTIVE;
 		ack = ktp_msg_preform(cmd, status);
 		faux_msg_send_async(ack, ktpd->async);
 		faux_msg_free(ack);