From 67b5eb0d9ca7eea63d23a4b0f0c7dff25527c872 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20Grubbstr=C3=B6m=20=28Grubba=29?=
 <grubba@grubba.org>
Date: Mon, 10 Feb 2020 11:38:01 +0100
Subject: [PATCH] I/O [NT]: Added debug_fd_openpty().

This simulates BSD-style openpty(3) with two pipes and ConPTY.
---
 src/fdlib.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/fdlib.h |  15 +++++
 2 files changed, 173 insertions(+), 4 deletions(-)

diff --git a/src/fdlib.c b/src/fdlib.c
index a22d751e9c..4db672e98c 100644
--- a/src/fdlib.c
+++ b/src/fdlib.c
@@ -269,6 +269,31 @@ PMOD_EXPORT void set_errno_from_win32_error (unsigned long err)
 
 #include "ntlibfuncs.h"
 
+static void close_pty(struct my_pty *pty)
+{
+  struct my_pty *other;
+
+  if (sub_ref(pty)) return;
+
+  CloseHandle(pty->write_pipe);
+  CloseHandle(pty->read_pipe);
+  if (pty->conpty) {
+    /* Closing the master side. */
+    Pike_NT_ClosePseudoConsole(pty->conpty);
+  }
+
+  /* Unlink the pair. */
+  mt_lock(&fd_mutex);
+  other = pty->other;
+  if (other) {
+    other->other = NULL;
+    pty->other = NULL;
+  }
+  mt_unlock(&fd_mutex);
+
+  free(pty);
+}
+
 #define ISSEPARATOR(a) ((a) == '\\' || (a) == '/')
 
 #ifdef PIKE_DEBUG
@@ -468,6 +493,9 @@ static int reallocate_fd(int fd, int type, HANDLE handle)
   if (fd_type[fd] == FD_SOCKET) {
     FDDEBUG(fprintf(stderr, "Closing socket that was fd %d.\n", fd));
     closesocket((SOCKET)da_handle[fd]);
+  } else if (fd_type[fd] == FD_PTY) {
+    FDDEBUG(fprintf(stderr, "Closing pty that was fd %d.\n", fd));
+    close_pty((struct my_pty *)da_handle[fd]);
   } else {
     FDDEBUG(fprintf(stderr, "Closing handle that was fd %d.\n", fd));
     CloseHandle(da_handle[fd]);
@@ -562,6 +590,7 @@ PMOD_EXPORT char *debug_fd_info(int fd)
     case FD_CONSOLE: return "IS CONSOLE";
     case FD_FILE: return "IS FILE";
     case FD_PIPE: return "IS PIPE";
+    case FD_PTY: return "IS PTY";
     default: return "NOT OPEN";
   }
 }
@@ -588,6 +617,9 @@ PMOD_EXPORT int debug_fd_query_properties(int fd, int guess)
 
     case FD_PIPE:
       return fd_INTERPROCESSABLE | fd_BUFFERED;
+
+    case FD_PTY:
+      return fd_INTERPROCESSABLE | fd_BUFFERED | fd_BIDIRECTIONAL;
     default: return 0;
   }
 }
@@ -1999,6 +2031,12 @@ PMOD_EXPORT int debug_fd_close(FD fd)
       }
       break;
 
+    case FD_PTY:
+      {
+	close_pty((struct my_pty *)h);
+      }
+      break;
+
     default:
       if(!CloseHandle(h))
       {
@@ -2047,6 +2085,13 @@ PMOD_EXPORT ptrdiff_t debug_fd_write(FD fd, void *buf, ptrdiff_t len)
 	return ret;
       }
 
+    case FD_PTY:
+      {
+	struct my_pty *pty = (struct my_pty *)handle;
+	handle = pty->write_pipe;
+      }
+      /* FALLTHRU */
+
     case FD_CONSOLE:
     case FD_FILE:
     case FD_PIPE:
@@ -2104,6 +2149,13 @@ PMOD_EXPORT ptrdiff_t debug_fd_read(FD fd, void *to, ptrdiff_t len)
       FDDEBUG(fprintf(stderr,"Read on %d returned %ld\n",fd,rret));
       return rret;
 
+    case FD_PTY:
+      {
+	struct my_pty *pty = (struct my_pty *)handle;
+	handle = pty->read_pipe;
+      }
+      break;
+
     case FD_PIPE:
       if (len) {
 	DWORD available_bytes = 0;
@@ -2405,6 +2457,10 @@ PMOD_EXPORT int debug_fd_fstat(FD fd, PIKE_STAT_T *s)
       s->st_mode=S_IFSOCK;
       break;
 
+    case FD_PTY:
+      s->st_mode = S_IFIFO;
+      break;
+
     default:
       switch(GetFileType(h))
       {
@@ -2461,7 +2517,17 @@ static void dump_FDSET(FD_SET *x, int fds)
     fprintf(stderr,"[");
     for(e = 0; e < FD_SETSIZE; e++)
     {
-      if(FD_ISSET(da_handle[e],x))
+      HANDLE h = da_handle[e];
+      if (fd_type[e] == FD_PTY){
+	struct my_pty *pty = (struct my_pty *)h;
+	if(FD_ISSET(pty->read_pipe, x))
+	{
+	  h = pty->read_pipe;
+	} else {
+	  h = pty->write_pipe;
+	}
+      }
+      if(FD_ISSET(h, x))
       {
 	if(!first) fprintf(stderr,",",e);
 	fprintf(stderr,"%d",e);
@@ -2551,6 +2617,20 @@ PMOD_EXPORT FD debug_fd_dup(FD from)
 
   if (fd_to_handle(from, &type, &h, 0) < 0) return -1;
 
+  if (type == FD_PTY) {
+    struct my_pty *pty = (struct my_pty *)h;
+
+    fd = allocate_fd(type, h);
+
+    release_fd(from);
+
+    if (fd < 0) return -1;
+
+    add_ref(pty);
+    release_fd(fd);
+    return fd;
+  }
+
   fd = allocate_fd(type,
 		   (type == FD_SOCKET)?
 		   (HANDLE)INVALID_SOCKET:INVALID_HANDLE_VALUE);
@@ -2589,8 +2669,17 @@ PMOD_EXPORT FD debug_fd_dup2(FD from, FD to)
 
   if (fd_to_handle(from, &type, &h, 0) < 0) return -1;
 
-  if(!DuplicateHandle(p, h, p, &x, 0, 0, DUPLICATE_SAME_ACCESS))
-  {
+  if (type == FD_PTY) {
+    struct my_pty *pty = (struct my_pty *)h;
+    /* Note that we need to hold a reference, so that we
+     * do not disappear when from is released below.
+     *
+     * The reference is then either stolen by reallocate_fd()
+     * or freed by close_pty().
+     */
+    add_ref(pty);
+    x = h;
+  } else if(!DuplicateHandle(p, h, p, &x, 0, 0, DUPLICATE_SAME_ACCESS)) {
     release_fd(from);
     set_errno_from_win32_error (GetLastError());
     return -1;
@@ -2605,7 +2694,9 @@ PMOD_EXPORT FD debug_fd_dup2(FD from, FD to)
   if (reallocate_fd(to, type, x) < 0) {
     release_fd(to);
 
-    if (type == FD_SOCKET) {
+    if (type == FD_PTY) {
+      close_pty((struct my_pty *)x);
+    } else if (type == FD_SOCKET) {
       closesocket((SOCKET)x);
     } else {
       CloseHandle(x);
@@ -2682,6 +2773,69 @@ PMOD_EXPORT const char *debug_fd_inet_ntop(int af, const void *addr,
 }
 #endif /* HAVE_WINSOCK_H && !__GNUC__ */
 
+PMOD_EXPORT int debug_fd_openpty(int *master, int *slave,
+				 char *ignored_name,
+				 void *ignored_term,
+				 void *ignored_winp)
+{
+  struct my_pty *master_pty = NULL;
+  struct my_pty *slave_pty = NULL;
+  int master_fd = -1;
+  int slave_fd = -1;
+  if (!Pike_NT_CreatePseudoConsole) {
+    errno = ENOENT;
+    return -1;
+  }
+
+  if (!(master_pty = calloc(sizeof(struct my_pty), 1))) {
+    errno = ENOMEM;
+    goto fail;
+  }
+  if (!(slave_pty = calloc(sizeof(struct my_pty), 1))) {
+    errno = ENOMEM;
+    goto fail;
+  }
+
+  add_ref(master_pty);
+  master_pty->read_pipe = INVALID_HANDLE_VALUE;
+  master_pty->write_pipe = INVALID_HANDLE_VALUE;
+  master_pty->other = slave_pty;
+  add_ref(slave_pty);
+  slave_pty->read_pipe = INVALID_HANDLE_VALUE;
+  slave_pty->write_pipe = INVALID_HANDLE_VALUE;
+  slave_pty->other = master_pty;
+
+  master_fd = allocate_fd(FD_PTY, (HANDLE)master_pty);
+  if (master_fd < 0) goto fail;
+
+  slave_fd = allocate_fd(FD_PTY, (HANDLE)slave_pty);
+  if (slave_fd < 0) goto fail;
+
+  if (!CreatePipe(&master_pty->write_pipe, &slave_pty->read_pipe, NULL, 0)) {
+    goto fail;
+  }
+  if (!CreatePipe(&slave_pty->write_pipe, &master_pty->read_pipe, NULL, 0)) {
+    goto fail;
+  }
+
+  if (FAILED(Pike_NT_CreatePseudoConsole(sz, slave_pty->write_pipe,
+					 slave_pty->read_pipe,
+					 0, &master->pty))) {
+    goto fail;
+  }
+
+ fail:
+  /* NB: Order significant!
+   *
+   * In the case where master_fd >= 0 and slave_fd < 0, the
+   * slave_pty must not have been freed when master_fd is closed.
+   */
+  if (master_fd >= 0) fd_close(master_fd);
+  else if (master_pty) free(master_pty);
+  if (slave_fd >= 0) fd_close(slave_fd);
+  else if (slave_pty) free(slave_pty);
+}
+
 #ifdef EMULATE_DIRECT
 PMOD_EXPORT DIR *opendir(char *dir)
 {
diff --git a/src/fdlib.h b/src/fdlib.h
index d13624f01e..04ce2a5ac8 100644
--- a/src/fdlib.h
+++ b/src/fdlib.h
@@ -159,6 +159,7 @@ typedef struct _STARTUPINFOEXW
 #define fd_dup2(fd,to) dmalloc_register_fd(debug_fd_dup2(dmalloc_touch_fd(fd),dmalloc_close_fd(to)))
 #define fd_connect(fd,X,Z) debug_fd_connect(dmalloc_touch_fd(fd),(X),(Z))
 #define fd_inet_ntop(af,addr,cp,sz) debug_fd_inet_ntop(af,addr,cp,sz)
+#define fd_openpty(m,s,name,term,win) debug_fd_openpty(m,s,name,term,win)
 
 
 /* Prototypes begin here */
@@ -210,6 +211,10 @@ PMOD_EXPORT FD debug_fd_dup(FD from);
 PMOD_EXPORT FD debug_fd_dup2(FD from, FD to);
 PMOD_EXPORT const char *debug_fd_inet_ntop(int af, const void *addr,
 					   char *cp, size_t sz);
+PMOD_EXPORT int debug_fd_openpty(int *master, int *slave,
+				 char *ignored_name,
+				 void *ignored_term,
+				 void *ignored_winp);
 /* Prototypes end here */
 
 #undef SOCKFUN1
@@ -245,6 +250,7 @@ PMOD_EXPORT const char *debug_fd_inet_ntop(int af, const void *addr,
 #define fd_shutdown_write SD_SEND
 #define fd_shutdown_both SD_BOTH
 
+#define FD_PTY -6
 #define FD_PIPE -5
 #define FD_SOCKET -4
 #define FD_CONSOLE -3
@@ -262,6 +268,15 @@ struct my_fd_set_s
   char bits[FD_SETSIZE/8];
 };
 
+struct my_pty
+{
+  INT32 refs;		/* Number of references from da_handle[]. */
+  HPCON conpty;		/* Only valid for master side. */
+  struct my_pty *other;	/* Other end (if any), NULL otherwise. */
+  HANDLE read_pipe;	/* Pipe that supports read(). */
+  HANDLE write_pipe;	/* Pipe that supports write(). */
+};
+
 typedef struct my_fd_set_s my_fd_set;
 
 #ifdef PIKE_DEBUG
-- 
GitLab