Reading from the `forkpty` of the child process:` ls / `output gives` EIO`

I try to use forkpty to execvp a ls / and then from the parent process read its output and write to stdout.

I got it, and running it under Valgrind also shows no errors.

EDIT: Valgrind does not show memory errors. The last read still gives EIO under Valgrind.

However, the last read always returns -1 and sets errno to EIO . I studied and read the manual pages, but I still do not understand why this is happening:

  EIO I/O error. This will happen for example when the process is in a background process group, tries to read from its controlling terminal, and either it is ignoring or blocking SIGTTIN or its process group is orphaned. It may also occur when there is a low-level I/O error while reading from a disk or tape. 

I also noticed that if I catch SIGCHLD signals that I can know when ls came out, but if I listed a larger directory like /usr/bin , then I get this signal for the ls child process even if there is a lot of read , who succeed.

Here is an example output:

 $ ./a.out bin dev initrd.img.old libx32 opt sbin usr boot etc lib lost+found proc srv var build home lib32 media root sys vmlinuz core initrd.img lib64 mnt run tmp vmlinuz.old # read: Input/output error 

Am I missing something obvious?

 #include <pty.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main(int argc, char* argv[]) { int master_fd; pid_t child_pid = forkpty(&master_fd, NULL, NULL, NULL); if (child_pid == -1) { perror("# forkpty"); return EXIT_FAILURE; } else if (!child_pid) { /* CHILD */ char* ls_argv[] = { "ls", "/", NULL, }; execvp(ls_argv[0], ls_argv); perror("# execvp"); abort(); } else { /* PARENT */ uint8_t buffer[BUFSIZ]; ssize_t read_count; while ((read_count = read(master_fd, buffer, BUFSIZ)) > 0) { uint8_t* ptr = buffer; size_t ptr_len = (size_t) read_count; while (ptr_len > 0) { ssize_t write_count = write(STDOUT_FILENO, ptr, ptr_len); if (write_count == -1) { perror("# write"); return EXIT_FAILURE; } ptr += write_count; ptr_len -= write_count; } } if (read_count == -1) { perror("# read"); return EXIT_FAILURE; } if (close(master_fd) == -1) { perror("# close"); return EXIT_FAILURE; } int child_status; if (waitpid(child_pid, &child_status, 0) == -1) { perror("# waitpid"); return EXIT_FAILURE; } if (!WIFEXITED(child_status)) { fprintf(stderr, "# main: subprocess did not exit normally\n"); return EXIT_FAILURE; } return child_status; } } 
+5
source share
2 answers

There seem to be three parts to the question:

a) Is the EIO a valid error in this case (at the last read)?

b) Why SIGCHLD is received much earlier than the expected completion of I / O,

c) Why the error with Valgrind does not occur?

a) The last read is expected to fail, as the reading looks like it was on a broken pipe with the completion of the child process. EOF seems to be a more significant error compared to EIO. (This can also happen if there is a low I / O error while reading from a disk or tape).

b) CPU processing, far ahead of I / O, caused this. Having received SIGCHLD (and setting a flag to indicate that the child has exited), the parent process should expect the reading to complete with an error after reading the information that the child wrote in slave-pty, and therefore it is safe to ignore EIO .

c) I am not sure about that. While Valgrind may have slowed down by providing enough time for I / O processing, the final read will still fail (return -1), in this case with EIO.

+1
source

The pty driver calls the kernel's internal function to nullify the working side of pty, which returns EIO from now on until the slave is closed. This always happens when the leading side is closed by the control process.

This process is standard on all unix variants. A workaround is to abandon the processes to continue using the device after the master side has passed. Even if you reattach the main side with a new process, the old subordinate process is blocked until it closes the open handle.

+1
source

Source: https://habr.com/ru/post/1266100/


All Articles