diff --git a/src/fdlib.c b/src/fdlib.c index ff2f0bb7e22afbc33536aaf38b05d5efbb053332..41367bc6a793b492cdccaab1e86be94cf07e0a37 100644 --- a/src/fdlib.c +++ b/src/fdlib.c @@ -268,12 +268,22 @@ PMOD_EXPORT void set_errno_from_win32_error (unsigned long err) #include "ntlibfuncs.h" +void free_pty(struct my_pty *pty) +{ + if (sub_ref(pty)) return; + free(pty); +} + /* NB: fd_mutex MUST be held at entry. */ static void close_pty(struct my_pty *pty) { struct my_pty *other; + struct pid_status *pid; - if (sub_ref(pty)) return; + if (--pty->fd_refs) { + sub_ref(pty); + return; + } CloseHandle(pty->write_pipe); CloseHandle(pty->read_pipe); @@ -289,6 +299,11 @@ static void close_pty(struct my_pty *pty) pty->other = NULL; } + while ((pid = pty->clients)) { + pid->clients = pid_status_unlink_pty(pid); + } + + free_pty(pty); } #define ISSEPARATOR(a) ((a) == '\\' || (a) == '/') @@ -2152,6 +2167,15 @@ PMOD_EXPORT ptrdiff_t debug_fd_read(FD fd, void *to, ptrdiff_t len) { struct my_pty *pty = (struct my_pty *)handle; handle = pty->read_pipe; + + if (pty->conpty && !pty->other && !check_pty_clients(pty)) { + /* Master side, no local slave and all known clients are dead. + * + * Terminate the ConPTY so that we can get an EOF. + */ + Pike_NT_ClosePseudoConsole(pty->conpty); + pty->conpty = 0; + } } /* FALLTHRU */ @@ -2626,6 +2650,8 @@ PMOD_EXPORT FD debug_fd_dup(FD from) if (fd < 0) return -1; add_ref(pty); + pty->fd_refs++; + release_fd(fd); return fd; } @@ -2677,6 +2703,7 @@ PMOD_EXPORT FD debug_fd_dup2(FD from, FD to) * or freed by close_pty(). */ add_ref(pty); + pty->fd_refs++; x = h; } else if(!DuplicateHandle(p, h, p, &x, 0, 0, DUPLICATE_SAME_ACCESS)) { release_fd(from); @@ -2799,10 +2826,12 @@ PMOD_EXPORT int debug_fd_openpty(int *master, int *slave, } add_ref(master_pty); + master_pty->fd_refs++; 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->fd_refs++; slave_pty->read_pipe = INVALID_HANDLE_VALUE; slave_pty->write_pipe = INVALID_HANDLE_VALUE; slave_pty->other = master_pty; diff --git a/src/fdlib.h b/src/fdlib.h index 74bfdf3ce0d7b609859032ca8fb639ee9d1b2170..b632ff734f1f757e776474f7874941eac2b54bd1 100644 --- a/src/fdlib.h +++ b/src/fdlib.h @@ -171,6 +171,7 @@ struct winsize { /* Prototypes begin here */ PMOD_EXPORT void set_errno_from_win32_error (unsigned long err); +void free_pty(struct my_pty *pty); int fd_to_handle(int fd, int *type, HANDLE *handle, int exclusive); void release_fd(int fd); PMOD_EXPORT char *debug_fd_info(int fd); @@ -277,9 +278,11 @@ struct my_fd_set_s struct my_pty { - INT32 refs; /* Number of references from da_handle[]. */ + INT32 refs; /* Total number of references. */ + INT32 fd_refs; /* Number of references from da_handle[]. */ HPCON conpty; /* Only valid for master side. */ struct my_pty *other; /* Other end (if any), NULL otherwise. */ + struct pid_status *clients; /* List of client processes. */ HANDLE read_pipe; /* Pipe that supports read(). */ HANDLE write_pipe; /* Pipe that supports write(). */ }; diff --git a/src/signal_handler.c b/src/signal_handler.c index 243feb493ce5f3dc9e6860a93456b4be1d8321ee..0400aa5ee74cc71e33d3a394723693a769deacdb 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -1254,6 +1254,8 @@ struct pid_status INT_TYPE pid; #ifdef __NT__ HANDLE handle; + struct pid_status *next_pty_client; /* PTY client chain. */ + struct my_pty *pty; /* PTY master, refcounted. */ #else INT_TYPE sig; INT_TYPE flags; @@ -1279,6 +1281,48 @@ static void init_pid_status(struct object *UNUSED(o)) #endif } +#ifdef __NT__ +struct pid_status *pid_status_unlink_pty(struct pid_status *pid) +{ + struct pid_status *ret; + if (!pid) return NULL; + ret = pid->next_pty_client; + pid->next_pty_client = NULL; + if (pid->pty) { + free_pty(pid->pty); + pid->pty = NULL; + } + return ret; +} + +static void pid_status_link_pty(struct pid_status *pid, struct my_pty *pty) +{ + add_ref(pty); + pid->pty = pty; + pid->next_pty_client = pty->clients; + pty->clients = pid; +} + +int check_pty_clients(struct my_pty *pty) +{ + struct pid_status *pid; + while ((pid = pty->clients)) { + DWORD status; + /* FIXME: RACE: pid->handle my be INVALID_HANDLE_VALUE if + * the process is about to be started. + */ + if ((pid->pid == -1) && (pid->handle == INVALID_HANDLE_VALUE)) return 1; + if (GetExitCodeProcess(pid->handle, &status) && + (status == STILL_ACTIVE)) { + return 1; + } + /* Process has terminated. Unlink and check the next. */ + pty->clients = pid_status_unlink_pty(pid); + } + return 0; +} +#endif /* __NT__ */ + static void exit_pid_status(struct object *UNUSED(o)) { #ifdef USE_PID_MAPPING @@ -1291,6 +1335,16 @@ static void exit_pid_status(struct object *UNUSED(o)) #endif #ifdef __NT__ + if (THIS->pty) { + struct my_pty *pty = THIS->pty; + struct pid_status *pidptr = &pty->clients; + while (*pidptr && (*pidptr != THIS)) { + pidptr = &(*pidptr)->next_pty_client; + } + if (*pidptr) { + *pidptr = pid_status_unlink_pty(THIS); + } + } CloseHandle(THIS->handle); #endif } @@ -2187,6 +2241,7 @@ static HANDLE get_inheritable_handle(struct mapping *optional, * pty for stdin, stdout and stderr wins * (see above). */ + pid_status_link_pty(pty->other); *conpty = pty->other->conpty; release_fd(fd); return ret; diff --git a/src/signal_handler.h b/src/signal_handler.h index ce1a37aa7f80ab87b707bd094a03afdbbd2cd7eb..8a2c0d12451d5b4af2e4fd7a1fa0e62538c5a91c 100644 --- a/src/signal_handler.h +++ b/src/signal_handler.h @@ -14,6 +14,10 @@ struct sigdesc; void my_signal(int sig, sigfunctype fun); PMOD_EXPORT void check_signals(struct callback *foo, void *bar, void *gazonk); void set_default_signal_handler(int signum, void (*func)(INT32)); +#ifdef __NT__ +struct pid_status *pid_status_unlink_pty(struct pid_status *pid); +int check_pty_clients(struct my_pty *pty); +#endif void process_started(pid_t pid); void process_done(pid_t pid, const char *from); struct wait_data;