Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C - Redirecting stdout after forking of child from parent

Tags:

c

redirect

stdout

I'm writing a program to execute another program as a forked process and redirect it's output to a file or /dev/null on demand.

Currently I have forked and executed the external program using execvp(). Then redirected the stdout from a thread created before forking as the forked process will inherit parents file descriptor table allowing me to redirect after foking.

But, I can initially redirect stdout to a desired file and both parents and child's stdouts are being redirected. However if I try to redirect it again to another file, only parents stdout is redirected, child's stdout stays the same.

Here's the code without all the error checking bits.

struct params {
    const char *p;
    int fd;
    int wait;
};

#define EXIT_NOEXEC 126
#define EXIT_NOTFOUND   127
#define EXIT_MISC   127

static void dofile(struct params* st);
void dupit(const char *p, struct params* st);
void* reload_config(void* para);

int
main(int argc, char *argv[]) {
    int exit_status, prog_status;
    struct params init;
    pid_t prog_pid;


    dofile(&init);

    prog_pid = fork();
    if (prog_pid == 0) {
        execvp(*argv, argv);
        exit_status = (errno == ENOENT) ? EXIT_NOTFOUND : EXIT_NOEXEC;
        err(exit_status, "%s", argv[0]);
        exit(EXIT_FAILURE);
    } else {
        while (wait(&prog_status) != prog_pid);
        return prog_status;
    }
}

static void dofile(struct params* st) {
    const char *p
    p = out.txt;
    dupit(p, st);

}


void dupit(const char *p, struct params* st) {
    pthread_t tid;
    st->wait = 0;
    int err = pthread_create(&(tid), NULL, &reload_config, st);
    if (err != 0) {
        printf("\ncan't create thread :[%s]", strerror(err));
        exit(1);
    } else {
        while (st->wait == 0) {
            sleep(1)
        }
    }
}

void* reload_config(void* para) {
    struct params *passed = (struct params *) para;
    int pre_config = 3; 
    int cur_config = 1; 
    int saved_stdout = dup(STDOUT_FILENO);
    char infile[5];
    int devNull = open("/dev/null", O_WRONLY);
    int file = open("out.txt", O_WRONLY);
    FILE *config;
    config = fopen("config.txt", "r");
    if (access("config.txt", F_OK) != -1) {
        while (1) {
            fgets(infile, 5, config);
            fclose(config);
            cur_config = infile[0] - '0';
            printf("output from thread, current config = %d\n", cur_config);
            if (pre_config != cur_config) {
                if (cur_config == 1) {
                    if (dup2(file, STDOUT_FILENO) == -1) {
                        err(EXIT_MISC, NULL);
                    }
                } else {
                    dup2(devNull, STDOUT_FILENO);
                }
                pre_config = cur_config;
            }
            if (passed->wait==0) {
                passed->wait = 1;
            }
            sleep(1);
        }
    } else {
        if (dup2(passed->fd, STDOUT_FILENO) == -1) {
            err(EXIT_MISC, NULL);
        }
    }
}

Well, I changed the code a bit so you guys will understand, so some parts will make no sense. But you get the basic idea.

How can I redirect child's stdout as I wish after forking.

like image 824
ivcode Avatar asked Nov 09 '22 23:11

ivcode


1 Answers

Since you asked, here is a simple example. Some shortcuts have been taken for brevity but hopefully it gives you some idea. The program opens file1 and redirects stdout to that file. It then does a fork. The child process writes a counter to stdout (via printf) every 1 second. After a few seconds the parent process uses IPC, a pipe in this example, to tell the child to switch redirect file.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char **argv)
{  
    pid_t pid;
    const char *file1 = "file1.txt";
    const char *file2 = "file2.txt";
    int pipefd[2];
    int fd;
    int rval;

    fd = open(file1, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
    if (fd == -1) {
        perror("file1 open");
        exit(-1);
    }

    /*
     * This pipe will be used by parent process to tell child which file
     * to redirect to.
     */
    rval = pipe2(pipefd, O_NONBLOCK);
    if (fd == -1) {
        perror("pipe");
        exit(-1);
    }

    /* Redirect stdout to the file opened before the fork. */
    dup2(fd, STDOUT_FILENO);

    pid = fork();

    if (pid == -1) {
        perror("fork");
        exit(-1);
    } else if (pid == 0) {
        /* Child process. */
        int ix;
        char redirect_file[100];

        close(pipefd[1]);

        for (ix = 0; ix < 10; ix++) {

            printf("%d\n", ix);
            sleep(1);

            rval = read(pipefd[0], redirect_file, sizeof(redirect_file));
            if (rval > 0) {
                /*
                 * Parent process has written a filename to the pipe.
                 */ 
                fd = open(redirect_file, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
                if (fd == -1) {
                    perror("file2 open");
                    exit(-1);
                }

                /* Ensure previous output has been written to current file. */
                fflush(stdout);

                /* Change redirect now. */
                dup2(fd, STDOUT_FILENO);
            }
        }
    } else {
        /* Parent process. */
        close(pipefd[0]);

        /* Wait a little and then tell child to change redirect file. */
        sleep(5);
        write(pipefd[1], file2, strlen(file2) + 1);

        wait();
    }
}

If this program is run you will find that half the child output went to file1 (first redirect) and other half of the output goes to file2 (second redirect).

$ cat file1.txt 
0
1
2
3
4
$ cat file2.txt 
5
6
7
8
9

One final note. The example program does the first dup before the fork. I did it like that because that's how your code was shown and also to emphasise the before and after fork aspect of the issue. But in real code the conventional way of doing that is to do fork first, then dup and finally exec. The dup is done after the fork so that only the child process gets affected and not the parent (unless that is really what you want).

like image 50
kaylum Avatar answered Nov 14 '22 21:11

kaylum