I'm trying to use forkpty
to execvp
the less
pager program, and then from the parent process write some text in such a way the child less
process will get that as its input.
I've been doing some research on how to accomplish this, but I can't get something to work using forkpty
, while I do using pipe
and fork
.
What happens is that I see no output and then the program exits normally.
EDIT: I noticed that if I read from master
after forkpty
I see "Missing filename ("less --help" for help)" from less
, but how come $ echo test | less
, works fine then? Changing exec_argv
to pass "-"
to less
still has the same original problem (no output).
Am I missing something obvious?
pipe
and fork
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
int read_write_fds[2];
if (pipe(read_write_fds) == -1) {
perror("pipe");
return EXIT_FAILURE;
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return EXIT_FAILURE;
}
if (!pid) {
if (close(read_write_fds[1]) == -1) {
perror("CHILD: close write");
return EXIT_FAILURE;
}
if (dup2(read_write_fds[0], STDIN_FILENO) == -1) {
perror("CHILD: dup2 read STDIN_FILENO");
return EXIT_FAILURE;
}
if (close(read_write_fds[0]) == -1) {
perror("CHILD: close read");
return EXIT_FAILURE;
}
char* exec_argv[] = {"less", NULL};
execvp(exec_argv[0], exec_argv);
perror("CHILD: execvp");
return EXIT_FAILURE;
}
if (close(read_write_fds[0]) == -1) {
perror("PARENT: close read");
return EXIT_FAILURE;
}
char text[] = "Hello, world!\n";
char* text_ptr = text;
size_t bytes_left = sizeof(text) - 1;
while (bytes_left > 0) {
ssize_t bytes_written = write(read_write_fds[1], text_ptr, bytes_left);
if (bytes_written == -1) {
perror("PARENT: write");
return EXIT_FAILURE;
}
bytes_left -= bytes_written;
text_ptr += bytes_written;
}
if (close(read_write_fds[1]) == -1) {
perror("PARENT: close write");
return EXIT_FAILURE;
}
if (waitpid(pid, NULL, 0) == -1) {
perror("PARENT: waitpid");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
forkpty
#include <sys/types.h>
#include <pty.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
int master;
pid_t pid = forkpty(&master, NULL, NULL, NULL);
if (pid == -1) {
perror("forkpty");
return EXIT_FAILURE;
}
if (!pid) {
char* exec_argv[] = {"less", NULL};
execvp(exec_argv[0], exec_argv);
perror("CHILD: execvp");
return EXIT_FAILURE;
}
char text[] = "Hello, world!\n";
char* text_ptr = text;
size_t bytes_left = sizeof(text) - 1;
while (bytes_left > 0) {
ssize_t bytes_written = write(master, text_ptr, bytes_left);
if (bytes_written == -1) {
perror("PARENT: write");
return EXIT_FAILURE;
}
bytes_left -= bytes_written;
text_ptr += bytes_written;
}
if (close(master) == -1) {
perror("PARENT: close");
return EXIT_FAILURE;
}
if (waitpid(pid, NULL, 0) == -1) {
perror("PARENT: waitpid");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
(Minor nit: in the child after fork()
you should use _exit()
.)
The reason you see no output here is because the child's stdin, stdout, and stderr are all attached to the new PTY. You need to read from the master and do something with the data.