Difficulty with switching between stdout and file using Dup2

173 views Asked by At

I have a problem with dup2 syscall. I added a while loop(a part of my program) that running over a students directory, each student has a "c" file that I compile to an "exe" file. the students' programs scan from testInput file (using dup2 to take the keyboard) 2 numbers, add them and then the answers is written down to programOutPut file.

afterward, I compare the 2 programs with a comparison program I wrote and it helps me with WEXITSTATUS to know whether a student succeeded in the test.

The problem is that I try to write the grade sheet into the file and then print it also to the screen. somehow it only appears on the file or the screen but not both.

while (myDirent = readdir(dir)) {
        if (strcmp(myDirent->d_name, ".") == 0 || strcmp(myDirent->d_name, "..") == 0)
            continue;

        if ((pid = fork()) == 0) {
            status=execlp("gcc", "gcc", "-o", mainPath, cPath, NULL); //compiling students code to main.out path
            if (status == -1) {
                fprintf(stderr, "gcc Exec failed\n");
                exit(-1);
            }
        }
        wait(&status);

        fdin = open(testInputPath, O_RDONLY); //test input file I wrote to compare with student's output
        if(fdin==-1){
            fprintf(stderr, "Error opening  test input file\n");
            exit(-1);
        }


        fdout = open(programOutputPath, O_WRONLY | O_CREAT | O_TRUNC,0777); //opening file for each student
        if(fdout==-1){
            fprintf(stderr, "Error opening Student's program output file\n");
            exit(-1);
        }

        
        if ((pid = fork()) == 0) {
            dup2(fdin, 0);
            dup2(fdout, 1);
            status= execlp(mainPath,mainPath, NULL);
            if (status == -1) {
                fprintf(stderr, "Student's main Exec failed\n");
                exit(-1);
            }
        }
        wait(&status);

        fdresults = open("results.txt", O_WRONLY | O_CREAT | O_APPEND, 0777); //grades sheet 
        if (fdresults == -1) {
            fprintf(stderr, "Error opening results.csv file\n");
            exit(-1);
        }
    
        
        if ((pid = fork()) == 0) {
            status= execlp("./comp.out", "./comp.out", programOutputPath, expectedOutputPath,  NULL); //compare program I wrote that simply compare 2 files and return value to status
            if (status == -1) {
                fprintf(stderr, "Compare Exec failed\n");
                exit(-1);
            }
        }
        wait(&status);

        **dup2(fdresults, 1); //trying to write to the file the grades
        printf("%s,%d\n", myDirent->d_name,WEXITSTATUS(status));
        dup2(fdscreen, 1);  // trying to return to stdout unsuccessfuly**
        
        
    }//end of while loop
1

There are 1 answers

3
Sagar On

First, do not open a file that is already open (source). Right now, you are opening testInputPath and programOutputPath during each iteration of the the loop without closing them. This can lead to undefined behavior.

It's unclear where fdscreen came from. If you do something similar to this answer, then you can achieve what you're looking for using dup(2). You should flush the corresponding userspace file buffer using fflush(3) before calling dup2.

A Better Solution

A much better solution is to open results.txt outside the loop, and then use dprintf(3) to write directly to the file. This is more elegant, and avoids having to change stdout. So, you can easily replace the last three lines of the loop with

dprintf(fdresults, "%s,%d\n", myDirent->d_name, WEXITSTATUS(status));

Don't forget to close fdresults after the loop is complete. In general, each open(2) should have a corresponding close(2). Using this approach is strongly encouraged over changing stdout every iteration of the loop.