I'm writing a program that reads input from stdin, manipulates the input, and writes output to stdout. However, many programs check whether stdin is a terminal or a pipe (by calling a function like isatty), and generate output differently. How do I have my program pretend to be a TTY?
The solution should work on both Linux and macOS. Any programming language that generates a standalone binary is acceptable, but Go is preferred.
Note that I'm asking a programming question, not asking for a tool. So, things like script or unbuffer is not something I'm looking for.
The following is fully working code for running a command in a pty and capturing its output. (Not as many lines as you may have thought.)
#include <signal.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <util.h>
pid_t child = 0;
void sighandler(int signum) {
if (child > 0) {
killpg(child, signum);
exit(signum);
}
}
// Run a command in a pty.
// Usage: /path/to/this/binary command to run
int main(int argc, char *argv[]) {
if (argc < 2) {
return EX_USAGE;
}
int master;
child = forkpty(&master, NULL, NULL, NULL);
if (child == -1) {
perror("failed to fork pty");
return EX_OSERR;
}
if (child == 0) {
// we're in the child process, so replace it with the command
execvp(argv[1], argv + 1);
perror("failed to execute command");
return EX_OSERR;
}
// trap kill signals and forward them to child process
signal(SIGHUP, sighandler);
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
const int buf_size = 1024;
char buf[buf_size];
fd_set fds;
ssize_t bytes_read;
// forward the output continuously
while (1) {
FD_ZERO(&fds);
FD_SET(master, &fds);
if (select(master + 1, &fds, NULL, NULL, NULL) > 0 && FD_ISSET(master, &fds)) {
bytes_read = read(master, buf, buf_size);
if (bytes_read <= 0) {
return EXIT_SUCCESS;
}
if (write(STDOUT_FILENO, buf, bytes_read) != bytes_read) {
perror("failed to write to stdout");
return EX_OSERR;
}
}
}
}
Related
I'm creating a C-program under Linux, and I want to catch FFMpeg's output-data and forward it. To do so, I'm calling FFMpeg via (an implementation of) popen2. It all is working fine: I'm able to get FFMpeg's data out of the pipe via read(). Things start to get awkward if FFMpeg stops working. read() seems to be blocking, which is the expected behaviour, but it never exits when the pipe is closed.
I learned that, for read() to break and detect an EOF, the pipe should be closed on both ends, so when I detect that FFMpeg is crashed or killed, I close the reading-end of my pipe. I'm assuming that, since FFMpeg is closed, it disconnects its end of the pipe.
My assumption seems to be wrong: when I close the reading-end of the pipe by using close(), the program still seems to be hanging in read().
Would somebody be able to explain me what's wrong with my assumption and point me in the right direction so that I can properly detect when the program that I'm piping to or from (in this case FFMpeg) has stopped sending data?
The relevant code:
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "helper.h"
#define READ 0
#define WRITE 1
FILE *sink_stream;
int ffmpeg_sink;
pid_t popen2_2(const char **command, int *infp, int *outfp);
void* pthread_sink_ffmpeg(void *arg);
pid_t popen2_2(const char **command, int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
int devNull = open("/dev/null", O_WRONLY);
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0) //first fork
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
//dup2(devNull,2); //pipes stderr to /dev/null....
execvp(*command, command);
_exit(1);
}
if (infp == NULL)
{
close(p_stdin[WRITE]);
close(p_stdin[READ]);
}
else
{
*infp = p_stdin[WRITE];
}
if (outfp == NULL)
{
close(p_stdout[WRITE]);
close(p_stdout[READ]);
}
else
{
*outfp = p_stdout[READ];
}
return pid;
}
void* pthread_sink_ffmpeg(void *arg)
{
char *pbuffer;
int length, res;
pbuffer = malloc(4096);
while(1)
{
/* Wait for input */
dbgfprintf("Waiting for input...");
while( (length = read(ffmpeg_sink, pbuffer, 1024)) )
{
fprintf(stderr, "Read [ %d ]...\r", length);
}
/* RIP */
dbgfprintf("Done for now..");
}
free(pbuffer);
}
int main( void )
{
int wstatus;
pid_t child_pid, w;
pthread_t t_ffmpeg_source;
const char *command_ffmpeg[] = {"ffmpeg",
"-hide_banner",
"-re",
"-i", "http://relay.ah.fm/192k",
"-c:a", "libfdk_aac",
"-profile:a", "aac_he_v2",
"-b:a", "128k",
"-f", "adts",
"pipe:1",
NULL};
child_pid = popen2_2(command_ffmpeg, NULL, &ffmpeg_sink);
dbgfprintf("Started ffmpeg with pid [ %d ]...", child_pid);
pthread_create(&t_ffmpeg_source, NULL, pthread_sink_ffmpeg, NULL);
do {
w = waitpid(child_pid, &wstatus, WUNTRACED | WCONTINUED);
} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
dbgfprintf("ffmpeg terminated...");
close(ffmpeg_sink);
pthread_join(t_ffmpeg_source, NULL);
}
This code is able to start the program, receive it's data, detect the execvp'd program crash/kill, but it never gets out of read() when the execvp'd program is killed.
I've also tried to read the pipe as a filestream;
sink_stream = fdopen(ffmpeg_sink, "r");
while( !feof(sink_stream) )
{
length = fread(pbuffer, 1, 4096, sink_stream);
fprintf(stderr, "Read [ %d ]...\r", length);
}
..which gives the same result: it reads the data but it doesn't get out of fread().
Any help would be highly appreciated!
Thanks in advance!
Thanks all for the comments.
Indeed, the while-loops are ineffective, but I've isolated this code from a bigger project so that I could troubleshoot this issue more effectively. That's also why the malloc() is still there, and indeed it is very pointless.
As Ian stated, it turns out to be as simple as closing the p_stdin[READ] and p_stdout[WRITE]. It all makes sense now: I figured that I didn't need to close them since the execvp'd program is using them but that's the whole thing: the execvp'd program (child) is using them, and not me (the parent).
popen2_2() now looks like this:
{
int p_stdin[2], p_stdout[2];
pid_t pid;
int devNull = open("/dev/null", O_WRONLY);
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0) //first fork
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
//dup2(devNull,2); //pipes stderr to /dev/null....
execvp(*command, command);
_exit(1);
}
if (infp == NULL)
{
close(p_stdin[WRITE]);
close(p_stdin[READ]);
}
else
{
close(p_stdin[READ]);
*infp = p_stdin[WRITE];
}
if (outfp == NULL)
{
close(p_stdout[WRITE]);
close(p_stdout[READ]);
}
else
{
*outfp = p_stdout[READ];
close(p_stdout[WRITE]);
}
return pid;
}
I can happily continue coding now! Thanks all :)
I am new to linux programming. I am trying to spawn two child processes and connect them through pipe. The generating child process should generate random numbers and the other child process should run a binary which takes those two numbers and finds their greatest common divider. The binary already works well with stdin so I am trying to redirect it to read end of a pipe. Similarly I do that with generating process with stdout connected to write end of a pipe.
But I think I didn't do good job wiring it together, because there is no output. Any help would be much appreciated!
I didn't find many materials on this online so suggesting those would also help a lot. Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define exec_path "nsd"
void create_pipe(int *proc_pipe);
int create_proc();
void sig_handler(int sig);
int main() {
int proc_pipe[2];
pid_t gen_proc;
pid_t nsd_proc;
int proc_stat;
create_pipe(proc_pipe);
gen_proc = create_proc();
if (gen_proc == 0) {
// child exec
struct sigaction act;
act.sa_handler = &sig_handler;
close(proc_pipe[0]);
dup2(proc_pipe[1], 1);
close(proc_pipe[1]);
while (1) {
printf("%d %d\n", rand() % 4096, rand() % 4096);
sleep(1);
if (sigaction(SIGTERM, &act, NULL) == -1) exit(2);
}
} else {
nsd_proc = create_proc();
if (nsd_proc == 0) {
// child exec
close(proc_pipe[1]);
dup2(proc_pipe[0], 0);
close(proc_pipe[0]);
execl(exec_path, "nsd", (char *) NULL);
} else {
close(proc_pipe[0]);
close(proc_pipe[1]);
sleep(5);
kill(gen_proc, SIGTERM);
wait(&proc_stat);
if (proc_stat != 0) {
perror("ERROR");
return 1;
} else {
printf("OK\n");
return 0;
}
}
}
}
void sig_handler(int sig) {
if (sig == SIGTERM) {
perror("GEN TERMINATED");
exit(0);
}
}
pid_t create_proc() {
pid_t pid = fork();
if (pid == -1) {
perror("error forking new process");
exit(2);
}
return pid;
}
void create_pipe(int *proc_pipe) {
if (pipe(proc_pipe) == -1) {
perror("failed to create pipe");
exit(2);
}
}
printf doesn't call write until it fills a buffer. You aren't giving your child enough time to fill that buffer. Add fflush(stdout) before the child goes to sleep so some data is actually written into the pipe.
My project is to fork and then use the parent process to read data from a file line by line and then send each line to the child process, which has to use execve to send the line as an argument for bc, and the output has to go back to the parent process. Right now, I'm just trying to send the data to the child and receive it properly, but it doesn't work. I have to use select to figure out if the child has output for the parent to get.
I have a file with 5 lines on it, and I use a while loop to go through the file. For each line I thought I would get the line back from the child, but it only does one line or two and stops. Then I get the same line twice for some reason.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <wait.h>
int main(int argc, char *argv[])
{
alarm(60);
fd_set rfds;
fd_set r2fds;
struct timeval tv;
int retval;
int retval2;
int i = argc;
int rc;
FILE *fp;
fp = fopen (argv[1],"r");
char *args[3];
int j = 0;
while (j < i)
{
args[j] = argv[j+2];
j++;
}
int stdin_pipe_fds[2], stdout_pipe_fds[2], stderr_pipe_fds[2];
pipe(stdin_pipe_fds);
pipe(stdout_pipe_fds);
pipe(stderr_pipe_fds);
rc = fork();
if (rc == -1)
{
while (rc == -1)
{
rc = fork();
}
}
pid_t child;
pid_t parent;
if (rc == 0)
{
child = getpid();
close(stdin_pipe_fds[1]);
close(stdout_pipe_fds[0]);
close(stdout_pipe_fds[0]);
close(0);
dup(stdin_pipe_fds[0]);//, 0);
close(stdin_pipe_fds[0]);
close(1);
dup(stdout_pipe_fds[1]);//,1);
close(stdout_pipe_fds[1]);
close(2);
dup(stderr_pipe_fds[1]);//,2);
close(stderr_pipe_fds[1]);
}
if (rc > 0)
{
parent = getpid();
close(stdin_pipe_fds[0]);
close(stdout_pipe_fds[1]);
close(stderr_pipe_fds[1]);
}
char str[100];
char buf2[100];
char buf[100];
FD_ZERO(&rfds);
FD_SET(stdout_pipe_fds[0], &rfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
while ((fgets(str,100,fp)) != NULL)
{
if (rc > 0)
{
int wstatus;
int wsta;
int status;
wsta = write(stdin_pipe_fds[1],str,strlen(str));
retval = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
if (FD_ISSET(stdout_pipe_fds[0], &rfds))
{
wstatus = read(stdout_pipe_fds[0], buf2, 100);
printf("From child: %s\n",buf2);
if (wstatus == -1)
{
printf("read failed\n");
//continue;
}
//wsta = write(stdin_pipe_fds[1],str,strlen(str));
}
}
if (rc == 0)
{
alarm(60);
scanf("%s",buf);
printf("%s", buf);
}
}
fclose(fp);
}
In the child
you close stdout_pipe_fds[0] twice, and not stderr_pipe_fds[0].
The scanf("%s", buf) will strip the newline from the string; you might not want that. Stdout, if you started from a terminal, will be line buffered; that is, it will take a newline to trigger an actual write. Since you snuck in and replaced the underlying FD, it doesn't know that a new buffering strategy may be appropriate.
In the parent:
you treat read and write as if they return a status; they return the number of bytes read or written. If it reads 0 bytes, the buf will contain the values from the previous read. I am not sure of the purpose of the read(), I would assume it just messes things up. I think you need to draw a picture of how the file descriptors are linked.
Suggestions: There is a generally accepted idiom when using fork:
if ((rc = fork()) == 0) {
/* do child stuff */
} else if (rc != -1) {
/* do parent stuff */
} else {
/* do error stuff */
}
Which is mainly followed to avoid warping peoples brains. It is really hard to read when it is interleaved. The "K" in K&R once quipped that "... if you're as clever as you can be when you write it, how will you ever debug it?"
The close(0); dup(); is much better expressed as dup2(fd, 0). The mere fact that you can compile code on your machine assures that dup2 functions correctly.
For my Operating Systems class I have an assignment due that is built onto a previous assignment. Unfortunately my previous project doesn't work correctly in addition to me not knowing where I need to start for the next project. The code which I have below is suppose to mimic a simple UNIX/Linux shell with some additional commands that cannot be performed with execvp: background processing via the ampersand operator, the 'jobs' shell command: list the pids of all living child processes (i.e. not ones that have terminated), "reaping" of "zombie" processes, and the 'cd' shell command: change the shell's working directory.
I believe, everything but the "jobs" command, and "cd" command work, but I'm not sure why these two don't.
The next assignment is to add some I/O redirection in the form of "mysh$ cmd arg1 arg2 argN > file.out" which I don't know where to even really begin...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc, char **argv) {
char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
int jobs[100];
int jobList = 0;
int background;
ssize_t rBytes;
int aCount;
pid_t pid;
int status;
while(!feof(stdin)) {
pid = waitpid(-1, &status, WNOHANG);
if (pid > 0)
printf("waitpid reaped child pid %d\n", pid);
write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
rBytes = read(0, bBuffer, BUFSIZ-1);
if(rBytes == -1) {
perror("read");
exit(1);
}
bBuffer[rBytes-1] = '\0';
if(!strcasecmp(bBuffer, "exit")){
exit(0);
}
sPtr = bBuffer;
aCount = 0;
do {
aPtr = strsep(&sPtr, " ");
pArgs[aCount++] = aPtr;
} while(aPtr);
background = (strcmp(pArgs[aCount-2], "&") == 0);
if (background)
pArgs[aCount-2] = NULL;
if (strlen(pArgs[0]) > 1) {
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
} else if (pid == 0) {
jobs[jobList] = pid;
jobList++;
if(!strcasecmp(pArgs[0], "jobs")){
for(int i; i<jobList; i++) {
if(kill(jobs[i],0)==0){
printf(jobs[i]);
}
printf("these are jobs\n");
exit(1);
}
if(!strcasecmp(pArgs[0], "cd")){
int ret;
if (!pArgs[1])
strcpy(bBuffer, "pwd");
ret = chdir(pArgs[1]);
strcpy(bBuffer, "pwd");
exit(1);
}
fclose(stdin);
fopen("/dev/null", "r");
execvp(pArgs[0], pArgs);
exit(1);
} else if (!background) {
pid = waitpid(pid, &status, 0);
if (pid > 0)
printf("waitpid reaped child pid %d\n", pid);
}
}
}
return 0;
}
First you;ll want to parse your line and detect that you need to redirect to a file. So let;s say you use strsep or whatever and you found out output is going to file.out or input is coming from file.in.
At this point you want to redirect output using dup / dup2. For example, to redirect STDOUT:
int
do_redirect(int fileno, const char *name)
{
int newfd;
switch (fileno) {
case STDOUT_FILENO:
newfd = open(name, O_WRONLY | O_CREAT, S_IRUSR | S_IRUSR);
break;
}
if (newfd == -1) {
perror("open");
return -1;
}
return dup2(fileno, newfd);
}
/* ... */
pid = fork();
do_redirect(STDOUT_FILENO, name);
Things to note:
I didn't test the code - it might not even compile
I didn't do much error-checking - you should (the way I did for open)
You need to implement STDIN_FILENO redirection on your own
Note how I used a separate function, your main is WAY to large as it is
Your code has something like 7 levels of indentation - ever heard about arrow code ?
Since this is homework, I will not give you code directly.
dup, dup2 and freopen are good to look at for input/output redirection.
fork for starting a concurrent process (ampersand)
You are on the right track using waitpid to reap child processes.
I've just started working with UNIX FIFOs, and I discovered something while experimenting with my first FIFO program. The program works this way: after creating the FIFO, two processes are started using the fork() function. The child process reads what the father passes to him through the FIFO, and prints it on the screen. The data exchanged is the string specified as an argument. The question is: in the father section, if I forget to close the input side of the FIFO (meaning that I exclude the close(fd) line) the program would just hang, even if the data between the processes is exchanged correctly. Otherwise, everything works fine and the program terminates withouth hanging. Can someone please explain me why?
Thanks for your patience. Here is the code of the main function:
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("An argument must be specified\n");
return -1;
}
int ret = mkfifo("./fifo.txt", 0644);
char buf;
if(ret < 0)
{
perror("Error creating FIFO");
return -1;
}
pid_t pid = fork();
if(pid < 0)
{
perror("Error creating child process");
return -1;
}
if(pid == 0) /* child */
{
int fd = open("./fifo.txt", O_RDONLY); /* opens the fifo in reading mode */
while(read(fd, &buf, 1) > 0)
{
write(STDOUT_FILENO, &buf, 1);
}
write(STDOUT_FILENO, "\n", 1);
close(fd);
return 0;
}
else /* father */
{
int fd = open("./fifo.txt", O_WRONLY); /* opens the fifo in writing mode */
write(fd, argv[1], strlen(argv[1]));
close(fd);
waitpid(pid, NULL, 0);
return 0;
}
}
read(2) blocks until there are characters available or the channel is closed at the other end. The father process must close the pipe in order for the last child read() to return. If you omit the close(fd) in the father, the child will block in the read() until the father exits (closing the pipe automatically) but father will hang in waitpid() until the child exits.
First things first: there are several issues with the code you posted.
There are no #include directives, hence no prototypes in scope for any of the functions you call. C89 requires prototypes for variadic functions such as printf(); C99 requires prototypes for all functions. Both C89 and C99 require declarations in scope for O_RDONLY, O_WRONLY, STDOUT_FILENO and NULL.
-1 is not an allowed return value for main().
C89 does not allow mixing declarations and statements.
A minor nit: the usual nomenclature is "parent and child", not "father and child".
I have modified your program to correct this issue and improve readability:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
if (argc != 2) {
printf("An argument must be specified\n");
return 1;
}
int ret = mkfifo("./fifo.txt", 0644);
char buf;
if (ret < 0) {
perror("Error creating FIFO");
return 1;
}
pid_t pid = fork();
if (pid < 0) {
perror("Error creating child process");
return 1;
}
if (pid == 0) { /* child */
int fd = open("./fifo.txt", O_RDONLY); /* opens the fifo in reading mode */
while(read(fd, &buf, 1) > 0) {
write(STDOUT_FILENO, &buf, 1);
}
write(STDOUT_FILENO, "\n", 1);
close(fd);
return 0;
} else { /* parent */
int fd = open("./fifo.txt", O_WRONLY); /* opens the fifo in writing mode */
write(fd, argv[1], strlen(argv[1]));
close(fd);
waitpid(pid, NULL, 0);
return 0;
}
}
But most importantly, you did not mention what operating system and compiler you are using.
I am unable to reproduce the issue, and I suspect it may be related to one of the issues listed above.