Jeff Abrahamson on Mon, 26 Aug 2002 16:40:19 +0200


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[PLUG] C, pipe/fork/exec, SIGPIPE


I've been hitting my head on the wall for several hours now. I wrote a
simple program, mostly cribbed from Stevens (APUE), to let me run a
process and talk to it.

As my example, I just use dc. I want to type "1 2 +p" to it and see it
say "3". I know I could have buffering issues, but that would be an
improvement. Eventually, it'll be my own program I talk to and I can
turn off buffering.

As it happens, dc just sits there defunct before I first write to it,
so when I do write I get a SIGPIPE. I can see this by breaking in gdb
at the fgets(...stdin) at line 74. So I'm guessing dc's stdin is
closed.

I've attached the sample program, it may be compiled with

    gcc -g -O2 -Wall -W test-fork.c -o test-fork

Anyone see what I'm doing wrong?

Thanks much in advance for any tips.

-- 
 Jeff

 Jeff Abrahamson  <http://www.purple.com/jeff/>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>


static void sig_pipe(int signo)
{
        signo=0;
        printf("SIGPIPE caught.\n");
        exit(-2);
}



void set_blocking(int fd)
{
	int val;

	if((val = fcntl(fd, F_GETFL, 0)) == -1)
		return;
	if (val & O_NONBLOCK) {
		val &= ~O_NONBLOCK;
		fcntl(fd, F_SETFL, val);
	}
}



void set_nonblocking(int fd)
{
	int val;

	if((val = fcntl(fd, F_GETFL, 0)) == -1)
		return;
	if (!(val & O_NONBLOCK)) {
		val |= O_NONBLOCK;
		fcntl(fd, F_SETFL, val);
	}
}



int fd_pair(int fd[2])
{
	int ret;

	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
	/* ret = pipe(fd); */

	if (ret == 0) {
		set_nonblocking(fd[0]);
		set_nonblocking(fd[1]);
	}
	
	return ret;
}



#define BUF_SIZE 1024
void talk_to_subprocess(int f_in, int f_out)
{
        char buf[BUF_SIZE];
        int num;
        char *str, buf2[BUF_SIZE];
        
        while(fgets(buf, BUF_SIZE, stdin)) {

                str = "  ==> Writing '";
                write(STDOUT_FILENO, str, strlen(str));
                write(1, buf, strlen(buf));
                write(f_out, buf, strlen(buf));
                str = "' to subprocess.\n";
                write(STDOUT_FILENO, str, strlen(str));
                
                num = read(f_in, buf, BUF_SIZE);
                buf[num] = '\0';
                sprintf(buf2, "  --> num = %d, read '%s'.\n", num, buf);
                write(STDOUT_FILENO, buf2, strlen(buf2));
                printf("%s\n", buf);
        }
        printf("Finished talking to sub-process.\n");
        return;
}



pid_t piped_child(char **command, int *f_in, int *f_out)
{
	pid_t pid;
	int to_child_pipe[2];
	int from_child_pipe[2];
        
	if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) {
		fprintf(stderr, "pipe: %s\n", strerror(errno));
		exit(-1);
	}

	pid = fork();
	if (pid < 0) {
		fprintf(stderr, "fork: %s\n", strerror(errno));
		exit(-1);
	}

	if (pid == 0) {
                /* child */
                close(to_child_pipe[1]);
                close(from_child_pipe[0]);
                
		if(to_child_pipe[0] != STDIN_FILENO) {
                        if(dup2(to_child_pipe[0], STDIN_FILENO) < 0) {
                                fprintf(stderr, "Failed to dup/close : %s\n",
                                        strerror(errno));
                                exit(-1);
                        }
                        close(to_child_pipe[0]);
                }
		if(from_child_pipe[1] != STDOUT_FILENO) {
                        if(dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
                                fprintf(stderr, "Failed to dup/close : %s\n",
                                        strerror(errno));
                                exit(-1);
                        }
                        close(from_child_pipe[1]);
                }
			
		set_blocking(STDIN_FILENO);
		/* if (blocking_io) {
			set_blocking(STDOUT_FILENO);
		}
                */
		execvp(command[0], command);
		fprintf(stderr, "Failed to exec %s : %s\n",
			command[0], strerror(errno));
		exit(-1);
	}

        /* parent */
	if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) {
		fprintf(stderr, "Failed to close : %s\n", strerror(errno));
		exit(-1);
	}

	*f_out = to_child_pipe[1];
	*f_in = from_child_pipe[0];

	return pid;
}



pid_t do_cmd(char *cmd, int *f_in, int *f_out)
{
	char *args[100];
	int argc=0;
	pid_t ret;
	char *tok;
        
        cmd = strdup(cmd);
        if (!cmd) goto oom;
        
        for (tok=strtok(cmd," "); tok; tok=strtok(NULL," ")) {
                args[argc++] = tok;
        }

	args[argc++] = ".";
	args[argc] = NULL;

        ret = piped_child(args,f_in,f_out);
        if(ret) talk_to_subprocess(*f_in, *f_out);
        
	return ret;

oom:
	fprintf(stderr, "Out of memory.\n");
	return 0;
}



int main(int argc,char *argv[])
{       
	pid_t pid;
	int f_in,f_out;
	char *shell_cmd = "/usr/bin/dc";

        argc = 0; argv = (char **)0;
        if(signal(SIGPIPE, sig_pipe) == SIG_ERR)
                fprintf(stderr, "signal SIGPIPE error.\n");
	pid = do_cmd(shell_cmd,&f_in,&f_out);

	return 0;               /* optimist! */
}

Attachment: pgp8uizcJcI2z.pgp
Description: PGP signature