Like Tree2Likes

dup question - file io

This is a discussion on dup question - file io within the C Programming forums, part of the General Programming Boards category; Lets say i want to use an execl call to netstat and then pipe it to a file. I know ...

  1. #1
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302

    dup question - file io

    Lets say i want to use an execl call to netstat and then pipe it to a file. I know i could use dup like so:

    Code:
    int fd = open("string.txt", O_CREAT | O_RDWR, 0755);
    	if(fd < 0) {
    	printf("OPEN(-1) error -> %s.\n", strerror(errno));
    	exit(EXIT_FAILURE);
    	}
    
    dup2(fd, STDOUT_FILENO);
    close(fd);
    execl("/bin/netstat", "netstat", "-a", "-n", "-t", "-p", ">", "string.txt", (char *)NULL);
    but how would i return stdout to its original state so i can then open string.txt and print the contents to the screen like so?
    Code:
    FILE *file = fopen("string.txt", "r");
    	if(file == NULL) {
    	printf("FOPEN[file](NULL) error -> %s.\n", strerror(errno));
    	exit(EXIT_FAILURE);
    	}
    
    char ch;
    	while(ch != EOF) {
    	ch = fgetc(file);
    	printf("%c", ch);
    	}

  2. #2
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    How about:
    Code:
    int stdout_fileno = dup(STDOUT_FILENO);
    
    // do your thing
    
    dup2(STDOUT_FILENO, stdout_fileno);
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  3. #3
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    Yeah, I tried that and unfortunately it did not work.

    Code:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(int argc, char *argv[]) {
    int stdout_fileno = dup(STDOUT_FILENO);
    
    int fd = open("string.txt", O_RDWR | O_CREAT, 0755);
    	if(fd < 0) {
    	printf("OPEN(-1) error -> %s.\n", strerror(errno));
    	exit(EXIT_FAILURE);
    	}
    
    dup2(fd, STDOUT_FILENO);
    execl("/bin/netstat", "netstat", "-a", "-n", "-t", "-p", ">", "string.txt", (char *)NULL);
    
    dup2(STDOUT_FILENO, stdout_fileno);
    close(fd);
    
    FILE *file = fopen("string.txt", "r");
    	if(file == NULL) {
    	printf("FOPEN[file](NULL) error -> %s.\n", strerror(errno));
    	exit(EXIT_FAILURE);
    	}
    
    char ch;
    	while(ch != EOF) {
    	ch = fgetc(file);
    	printf("%c", ch);
    	}
    
    return 0;
    }
    Just to give you an idea, I am converting my bash script to a C program to tighten up some GUI code.
    Code:
    netstat -antp | 
    awk 'NF="7" {print $5"\t""\t""\t""\t""\t"$7}' | 
    awk 'FS=":" {print $2}' | 
    awk '!x[$0]++' | 
    awk 'NF' > netstat.txt; 
    egrep -v "(443|80)" netstat.txt > netstat2.txt;
    
    LINES=$(awk 'END {print NR}' netstat2.txt);
    
    ONE=1;
    
    if [ $LINES -lt $ONE ]; then
    echo "NO SUSPICIOUS PORTS OPEN";
    cat netstat.txt;
    exit 0;
    
       else 
       echo "[OPEN PORTS]";
       cat netstat2.txt;
       sudo  warning
    
    fi
    Last edited by Annonymous; 09-17-2012 at 07:09 PM.

  4. #4
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    383
    You don't have to worry about redirecting stdout back to your terminal after netstat runs because execl does not return to your program (unless the command cannot be executed). I noticed that you're redirecting stdout to string.txt in the execl call anyway, which ignores your previous redirection. Why don't you use the system() function? It returns back to your program after the command exits.

    Then again, do you really need to convert your shell script to C in the first place? Unless you need to do something that you can't do in a script very easily (like a GUI), I'd leave it as a script. Performance isn't an issue because most processing time is spent in the subprograms, not the script itself.

    Or...could you call your script from C (with system())? That's another option.

    Edit: I forgot to mention the popen()/pclose() functions. These let you run a program and read from its output (or write to its input) as if it were a regular file without using a temporary file. These are POSIX standard functions. You would have to use about 3 pages of Win32 API code to do the same thing in the Windows world.
    Last edited by christop; 09-17-2012 at 08:08 PM.

  5. #5
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Quote Originally Posted by christop View Post
    You don't have to worry about redirecting stdout back to your terminal after netstat runs because execl does not return to your program (unless the command cannot be executed).
    I missed that. Yeah, you have to do a fork then the execl if you want the parent to continue.

    And ch should be an int (fgetc returns an int and EOF is an int).

    EDIT:
    I put the arguments in the wrong order for the dup2 to switch standard out back to the terminal. It should be:
    Code:
    dup2(stdout_fileno, STDOUT_FILENO);
    Last edited by oogabooga; 09-17-2012 at 08:14 PM.
    Annonymous likes this.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  6. #6
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    Quote Originally Posted by christop View Post
    You don't have to worry about redirecting stdout back to your terminal after netstat runs because execl does not return to your program (unless the command cannot be executed). I noticed that you're redirecting stdout to string.txt anyway, which ignores your previous redirection. Why don't you use the system() function? It returns back to your program after the command exits.
    Well I am redirecting stdout to the file. So i would need to redirect it back to the terminal. I assume, unles there is another reason I am getting nothing back to the screen when i try and print the file contents to stdout.

    Quote Originally Posted by christop View Post
    Then again, do you really need to convert your shell script to C in the first place? Unless you need to do something that you can't do in a script very easily (like a GUI), I'd leave it as a script. Performance isn't an issue because most processing time is spent in the subprograms, not the script itself.

    Or...could you call your script from C (with system())? That's another option.
    I don't need to but want to. I have 2 different scripts and one GUI. I would rather have one program. On the other hand I do want to build on the program so I would need to parse more information that would be a whole lot easier to do in Awk rather than C.

    My script isnt called from the GUI. The script is for parsing purposes and is called from the watch command every 60 seconds. It stores the parsed info in a file. Then once the GUI is launched, it loads the file into a scrollable text view widget. The script that is responsible for it all is a one line monitoring process ran by the watch command. Better than cron IMO. At least for the purpose i intend on using it for.

    I would like to continue with the approach I am taking. SO if anyone could tell me why this isnt working, i would appreciate it!

  7. #7
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    Quote Originally Posted by oogabooga View Post
    I missed that. Yeah, you have to do a fork then the execl if you want the parent to continue.
    Ahh, thanks.

    Quote Originally Posted by oogabooga View Post
    And ch should be an int (fgetc returns an int and EOF is an int).
    Oh.... yeah I don't know why i was making that a char lol

    Quote Originally Posted by oogabooga View Post
    EDIT:
    I put the arguments in the wrong order for the dup2 to switch standard out back to the terminal. It should be:
    Code:
    dup2(stdout_fileno, STDOUT_FILENO);
    yeah i just caught that, its dup2(old fd, new fd);

    Thanks oogabooga!!

    The fixed up code with solution to my problem(included fork function):
    Code:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(int argc, char *argv[]) {
    int stdout_fileno = dup(STDOUT_FILENO);
    
    pid_t pid = fork();
    
    	if(pid == 0) {
    	int fd = open("string.txt", O_RDWR | O_CREAT, 0755);
    		if(fd < 0) {
    		printf("OPEN(-1) error -> %s.\n", strerror(errno));
    		exit(EXIT_FAILURE);
    		}
    
    	dup2(fd, STDOUT_FILENO);
    	close(fd);
    	}
    
    	if(pid < 0) {
    	printf("FORK(-1) error -> %s.\n", strerror(errno));
    	exit(EXIT_FAILURE);
    	}
    
    	execl("/bin/netstat", "netstat", "-a", "-n", "-t", "-p", ">", "string.txt", (char *)NULL);
    	dup2(stdout_fileno, STDOUT_FILENO);
    	close(stdout_fileno);
    
    FILE *file = fopen("string.txt", "r");
    	if(file == NULL) {
    	printf("FOPEN[file](NULL) error -> %s.\n", strerror(errno));
    	exit(EXIT_FAILURE);
    	}
    
    	int ch;
    	while(ch != EOF) {
    	ch = fgetc(file);
    	printf("%c", ch);
    	}
    
    return 0;
    }
    On to the next line of the script.
    Last edited by Annonymous; 09-17-2012 at 08:58 PM. Reason: Provided the solution(code) to my problem

  8. #8
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    That's not quite right. You need to keep the child and parent code separate. As it's currently written, I believe that both processes are performing the execl.

    Actually, now that you have the fork, you don't need to set standard out back since you only change it in the child process. Try something like this:
    Code:
    void die(char *msg) {
        fprintf(stderr, "%s error -> %s.\n", msg, strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    // in main
    
    pid = fork();
    if (pid == -1)
        die("FORK(-1)");
    else if (pid == 0) {  // child
        int fd = open(...);
        if (fd < 0) die("OPEN(-1)");
        dup2(fd, STDOUT_FILENO);
        execl(...);  // replaces the process's program
        die("EXECL"); // error if this line is reached
    }
    
    //parent
    int status;
    wait(&status);  // wait for child to finish
    // ...
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  9. #9
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    Quote Originally Posted by oogabooga View Post
    That's not quite right. You need to keep the child and parent code separate. As it's currently written, I believe that both processes are performing the execl.

    Actually, now that you have the fork, you don't need to set standard out back since you only change it in the child process. Try something like this:
    Code:
    void die(char *msg) {
        fprintf(stderr, "%s error -> %s.\n", msg, strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    // in main
    
    pid = fork();
    if (pid == -1)
        die("FORK(-1)");
    else if (pid == 0) {  // child
        int fd = open(...);
        if (fd < 0) die("OPEN(-1)");
        dup2(fd, STDOUT_FILENO);
        execl(...);  // replaces the process's program
        die("EXECL"); // error if this line is reached
    }
    
    //parent
    int status;
    wait(&status);  // wait for child to finish
    // ...
    Yes, both the child and the parent are calling execl. The reason being is that when I had only the parent running execl, the program wasnt working properly. I would have to execute the program twice in a row in order for it to work right. So i figured why run it twice manually when i can just have both process run it concurrently and have the program do the work.

    Fill me in. I would run it once and all i would get is a small diamond with a question mark in the middle preceding the command line prompt. I would run it again and stdout would display the file properly. Why would it do this? Is my solution bad practice?
    Last edited by Annonymous; 09-17-2012 at 09:28 PM.

  10. #10
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    383
    Quote Originally Posted by Annonymous View Post
    Code:
    	int ch;
    	while(ch != EOF) {
    	ch = fgetc(file);
    	printf("%c", ch);
    	}
    This part would probably explain the question-mark-inside-a-diamond. You need to compare ch with EOF after reading a character from the file. As it is, you're comparing an uninitialized value against EOF. Instead, read a character with fgetc. If it's EOF, exit the loop. Otherwise write the character to stdout and loop around to read the next character.

    Your program seemed to work the second time because the file contained the output from the previous time you ran it.

    What you have to do before you can read from the file is wait for the netstat program to finish running. If you don't wait, the program might try to read from the file before there is anything in it. Hint: look up the wait() function. Or better yet, look up the system() function, which takes care of all the messy details with fork, exec, and wait for you.

  11. #11
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    Quote Originally Posted by christop View Post
    This part would probably explain the question-mark-inside-a-diamond. You need to compare ch with EOF after reading a character from the file. As it is, you're comparing an uninitialized value against EOF. Instead, read a character with fgetc. If it's EOF, exit the loop. Otherwise write the character to stdout and loop around to read the next character.

    Your program seemed to work the second time because the file contained the output from the previous time you ran it.

    What you have to do before you can read from the file is wait for the netstat program to finish running. If you don't wait, the program might try to read from the file before there is anything in it. Hint: look up the wait() function. Or better yet, look up the system() function, which takes care of all the messy details with fork, exec, and wait for you.
    Ok, i see. So, I'll just switch it around to a do while loop instead.

    I used sleep instead of wait. With the execl call being run by both the parent and child, i have no more problems.

    thanks Christop.

  12. #12
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Quote Originally Posted by Annonymous View Post
    Ok, i see. So, I'll just switch it around to a do while loop instead.
    You don't need to do that. Just initialize ch with 0.

    I used sleep instead of wait.
    That's not the same at all !

    With the execl call being run by both the parent and child, i have no more problems.
    Except of course that you said you were doing this for performance. Obviously running the program twice is inefficient (and idiotic).

    Also, your execl call is wrong. You're passing ">" and "string.txt" as if you're trying to cause a redirection that way, but I can't see how that would work. And besides, that's what the dup2 call is for!

    christop is right that system() will handle this stuff for you, even opening the file and redirection since it calls the shell (so you could pass it something like "/bin/netstat -antp > string.txt"). However, this is clearly less efficient than doing it yourself since the way you're doing it avoids calling the shell.

    Try this:
    Code:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    void die(char *msg) {
        fprintf(stderr, "%s error -> %s.\n", msg, strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    int main(int argc, char *argv[]) {
    
    	pid_t pid = fork();
    
    	if(pid == -1)
    		die("FORK(-1)");
    
    	else if(pid == 0) {  // child
    		int fd = open("string.txt", O_RDWR | O_CREAT, 0755);
    		if(fd == -1)
    			die("OPEN(-1)");
    		dup2(fd, STDOUT_FILENO);
    		execl("/bin/netstat", "netstat", "-antp", (char *)NULL);
    		die("EXECL");
    	}
    
    	// parent
    	int status;
    	wait(&status);
    	if(status != 0)
    		die("NETSTAT");
    
    	FILE *file = fopen("string.txt", "r");
    	if(file == NULL)
    		die("FOPEN[file](NULL)");
    
    	int ch = 0;
    	while(ch != EOF) {
    		ch = fgetc(file);
    		printf("%c", ch);
    	}
    
    	fclose(file);
    
    	return 0;
    }
    Annonymous likes this.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  13. #13
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    Quote Originally Posted by oogabooga View Post
    You don't need to do that. Just initialize ch with 0.


    That's not the same at all !


    Except of course that you said you were doing this for performance. Obviously running the program twice is inefficient (and idiotic).

    Also, your execl call is wrong. You're passing ">" and "string.txt" as if you're trying to cause a redirection that way, but I can't see how that would work. And besides, that's what the dup2 call is for!

    christop is right that system() will handle this stuff for you, even opening the file and redirection since it calls the shell (so you could pass it something like "/bin/netstat -antp > string.txt"). However, this is clearly less efficient than doing it yourself since the way you're doing it avoids calling the shell.

    Try this:
    Code:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    void die(char *msg) {
        fprintf(stderr, "%s error -> %s.\n", msg, strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    int main(int argc, char *argv[]) {
    
    	pid_t pid = fork();
    
    	if(pid == -1)
    		die("FORK(-1)");
    
    	else if(pid == 0) {  // child
    		int fd = open("string.txt", O_RDWR | O_CREAT, 0755);
    		if(fd == -1)
    			die("OPEN(-1)");
    		dup2(fd, STDOUT_FILENO);
    		execl("/bin/netstat", "netstat", "-antp", (char *)NULL);
    		die("EXECL");
    	}
    
    	// parent
    	int status;
    	wait(&status);
    	if(status != 0)
    		die("NETSTAT");
    
    	FILE *file = fopen("string.txt", "r");
    	if(file == NULL)
    		die("FOPEN[file](NULL)");
    
    	int ch = 0;
    	while(ch != EOF) {
    		ch = fgetc(file);
    		printf("%c", ch);
    	}
    
    	fclose(file);
    
    	return 0;
    }
    Ok, i see what you mean by initialize ch to zero and why using sleep was wrong. I thought i just needed to give execl time to finish but i see wait waits for a process to change states.

    So you have the child executing the process. I thought i had to call fork and then have the parent execute. I wasn't too far off lol But thanks again.

    Now i have to move on to the next line. Which I hope will be easier than what i just had to do. I can see that this will probably be more trouble than its worth! Maybe I should stick with the Awk for the script like Christop said. After all, I am going to need to parse more info to add to the program later on anyway. Awk is so easy to use and its so smooth versus what i am doing here. Meditate on this, i will.

  14. #14
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    383
    Code:
    	int ch = 0;
    	while(ch != EOF) {
    		ch = fgetc(file);
    		printf("%c", ch);
    	}
    That's better (no more uninitialized variable) but still wrong. It still prints EOF as if it were a character, which probably appears as a question mark inside a diamond in Annonymous's terminal.

    I don't really think it's worth worrying over a few microseconds more runtime and a few hundred kilobytes more memory by spawning a shell by calling system(). Sure, if you're calling some program millions of times, it might matter, but not for a few times. Shell scripts are 100x worse because almost every command is an external program, but it wasn't a big deal even 30 years ago for the kind of things you would write a shell script for. End of rant.

  15. #15
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Yep, you're right. It should be more like this:
    Code:
    int ch;
    while((ch = fgetc(file)) != EOF)
        printf("%c", ch);
    And I agree that it's likely not worth doing, but at least he learned something about fork, exec and wait!
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C++ File Question
    By iGuardian in forum C++ Programming
    Replies: 4
    Last Post: 10-27-2011, 12:59 PM
  2. Question about file IO
    By Raigne in forum C++ Programming
    Replies: 14
    Last Post: 05-15-2011, 06:30 PM
  3. FILE* question
    By keira in forum C Programming
    Replies: 22
    Last Post: 12-02-2007, 02:51 AM
  4. Newbish Question file reading question....
    By kas2002 in forum C Programming
    Replies: 23
    Last Post: 05-17-2007, 12:06 PM
  5. File question-Testing if file exists
    By fuh in forum C++ Programming
    Replies: 2
    Last Post: 01-28-2003, 06:11 PM

Tags for this Thread


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21