Re: pty: childs don't always react on close(2)
Dirk Gouders <[email protected]> Fri, 05 Dec 2025 23:20:03 +0100
| Newsgroups | gmane.linux.newbie,gmane.linux.kernel |
|---|---|
| Message-ID | <[email protected]> |
--=-=-= Content-Type: text/plain Al Viro <[email protected]> writes: > On Fri, Dec 05, 2025 at 10:37:44PM +0100, Dirk Gouders wrote: > >> child_pid1 = forkpty(&pty_fd1, NULL, NULL, NULL); > > You do realize that it will inherit all your opened descriptors, > including pty_fd, right? > > ... >> close(pty_fd); > > ... which doesn't do anything to the second child's descriptor > table, including the descriptor that refers to the same opened file. > IOW, the IO channel (== opened file) is very much opened after > that close() - descriptors refering to it still exist. Oh yes, thank you very much! I use FD_CLOEXEC nearly everywhere in the mentioned program but exactly not for the pty file descriptors. Oh well... For completenes, I'll attach the modified test-case. Would be interesting to hear if there are other possible fixes. Thank you very much, again, Dirk --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=test-close-pty.c Content-Description: PTY test program /* * Test closing file descriptors opened via forkpty() when not all data has been * read. A following waitpid() blocks, when we opened two childs and try to * close the file descriptor and then waitpid() for that child... */ #include <stdlib.h> #include <stdio.h> #include <pty.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/wait.h> #include <stdint.h> #include <fcntl.h> #define READ_SIZE 4096 /* * Set PAGER variables and start a man(1) process. */ void do_child(void); void do_child() { char *e_argv[3] = {"man", "groff_ms", NULL}; putenv("PAGER=cat"); putenv("MANPAGER=cat"); execvp("man", e_argv); } int main() { unsigned char read_buffer[READ_SIZE]; int pty_fd; int pty_fd1; int wstatus; pid_t child_pid; pid_t child_pid1; pid_t ret_pid; ssize_t ret; /* * Start a child to send us a manual page. */ child_pid = forkpty(&pty_fd, NULL, NULL, NULL); if (child_pid == -1) { fprintf(stderr, "forkpty(): %s\n", strerror(errno)); exit(EXIT_FAILURE); } ret = fcntl(pty_fd, F_SETFD, FD_CLOEXEC); if (child_pid == 0) do_child(); printf("child_pid = %jd\n", (intmax_t) child_pid); memset(read_buffer, '\0', READ_SIZE); ret = read(pty_fd, read_buffer, READ_SIZE); if (ret == -1) { fprintf(stderr, "read(): %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("%s\n", read_buffer); printf("%ld bytes read.\n", ret); /* * Start another child to send us a manual page. */ child_pid1 = forkpty(&pty_fd1, NULL, NULL, NULL); if (child_pid1 == -1) { fprintf(stderr, "forkpty(): %s\n", strerror(errno)); exit(EXIT_FAILURE); } ret = fcntl(pty_fd1, F_SETFD, FD_CLOEXEC); if (child_pid1 == 0) do_child(); printf("child_pid1 = %jd\n", (intmax_t) child_pid1); memset(read_buffer, '\0', READ_SIZE); ret = read(pty_fd1, read_buffer, READ_SIZE); if (ret == -1) { fprintf(stderr, "read(): %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("%s\n", read_buffer); printf("%ld bytes read.\n", ret); close(pty_fd); ret_pid = waitpid(child_pid, &wstatus, 0); printf("ret_pid = %jd\n", (intmax_t) ret_pid); exit(EXIT_SUCCESS); } --=-=-=--