Solved

redirecting stderr in c

Posted on 1998-07-30
8
1,263 Views
Last Modified: 2013-12-26
I want to execute a program which outputs to stderr, in a section of c code.  I could use popen() to monitor output, but this only pipes stdout back to the calling program.  I need to read from stderr to recieve update information until the program has finished executing.

How do I do this?
0
Comment
Question by:zed1
8 Comments
 

Author Comment

by:zed1
ID: 1295239
Adjusted points to 200
0
 
LVL 3

Expert Comment

by:braveheart
ID: 1295240
/*
 * Command program to echo supplied arguments to standard error
 */
#include <stdio.h>
#include <stdlib.h>

main(int argc, char **argv)
{
    /* Skip program name */
    argc--;argv++;

    /* Print remaining arguments, separated by spaces, terminated by newline */
    while (argc--)
        fprintf(stderr,"%s%c", *argv++, argc ? '\n' : ' ' );
}
0
 

Author Comment

by:zed1
ID: 1295241
Hmmm...  Perhaps you only read the first sentence of my question.  Sorry for not explaining it well enough.

I want to execute a program using a shell command in c.  The program that I want to execute outputs to stderr.  Using the popen() function, only stdout gets piped back to my program, NOT stderr which is what I need.

If the program outputted to stdout I could simply use the following code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

      main()
            {
                    char *cmd = "<command>";
                     char buf[BUFSIZ];
                     FILE *ptr;

                    if ((ptr = popen(cmd, "r")) != NULL)
                          while (fgets(buf, BUFSIZ, ptr) != NULL)
                                
                          //each line from stdout gets
                          //put into the buf variable
                return 0;
            }

However, this code wont work for me.

0
 
LVL 3

Expert Comment

by:braveheart
ID: 1295242
How are you piping the stderr output of your program into your other program?  Are you using "|&" instead of "|" ?
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 84

Expert Comment

by:ozo
ID: 1295243
char *cmd = "command 2>&1";
0
 
LVL 1

Expert Comment

by:grisu072798
ID: 1295244
I have not tried this, but let me make a suggestion:
(1) The external program could close or redirect it's standard output and instead write it's stderr to that file descriptor. This could be done by exchanging the file descriptors _iobuf[1]
and _iobuf[2].
(2) If you don't have the source for the external program, you could write a wrapper around it which does the above thing. If you then execute the 'external' prg by calling vfork() (or was it clone(), anyway, so that the new program overlays the old one and inherits all its file descriptors) these settings should remain.
0
 
LVL 3

Accepted Solution

by:
dhm earned 200 total points
ID: 1295245
This is a little tedious to set up, but it's not that hard.  What you have to do is explicitly do all the magic that popen() normally does for you.  Here's code that you can use to do it.  The super_popen() function is what you want; I've added a main() that shows you how to use it, and a sample subprogram that echos back whatever the main program sends to it on its on stdin, and then prints messages on both stdout and stderr.  I've tested this on Solaris-2.5.1, but it should work on any recent unix.

d.

---------------------------------------------------
Main Program:
/*
 * Sample program to demonstrate super_popen
 *
 * by dave madden <dhm@webvision.com>
 */
#include <stdio.h>
#include <errno.h>
#include <sys/select.h>

int
super_popen( FILE **SI_SO_SE, const char *cmd )
{
      int             si[2] = { -1, -1 };
      int             so[2] = { -1, -1 };
      int             se[2] = { -1, -1 };
      pid_t       pid;
      int             e;
      int             fd;

      /*
       * Create 3 pipes to talk to subprocess on stdin/stdout/stderr
       */
      if (pipe( si ) != 0 || pipe( so ) != 0 || pipe( se ) != 0) goto SHUTDOWN;

      if ((pid = fork( )) == -1) goto SHUTDOWN;

      if (pid) {                              /* in parent */
            close( si[0] );
            close( so[1] );
            close( se[1] );
            SI_SO_SE[0] = fdopen( si[1], "w" );
            SI_SO_SE[1] = fdopen( so[0], "r" );
            SI_SO_SE[2] = fdopen( se[0], "r" );
            
            return 0;                        /* OK! */
      }

      /*
       * This is the child process...we have to fiddle around with the
       * pipe descriptors and then exec the program.
       */

      /*
       * Make sure none of our stdin/stdout/stderr descriptors are
       * on FDs 0, 1, or 2 (if they are, it'll screw things up when
       * we do the dup2()'s)
       */
      if (si[0] < 2 || so[1] < 2 || se[1] < 2) {
            int       free_index = 0;
            int       free_fds[3];
            printf( "have to (Edited by Computer101) with FDs in child\n" );
            for (fd = 3; fd < FD_SETSIZE; fd++) {
                  if (fd != si[0] && fd != si[1] && fd != se[1]) {
                        free_fds[free_index++] = fd;
                        if (free_index == 3) break;
                  }
            }
            if (dup2( si[0], free_fds[0] ) == -1 ||
                  dup2( so[1], free_fds[1] ) == -1 ||
                  dup2( se[1], free_fds[2] ) == -1) exit( errno );
            si[0] = free_fds[0];
            so[1] = free_fds[1];
            se[1] = free_fds[2];
      }
            
      /* close all other descriptors...don't want child to have copies */
      for (fd = 0; fd < FD_SETSIZE; fd++)
            if (fd != si[0] && fd != so[1] && fd != se[1]) close( fd );
      
      dup2( si[0], 0 );                  /* attach pipes to FD 0, 1, and 2 */
      dup2( so[1], 1 );
      dup2( se[1], 2 );

      close( si[0] );
      close( so[1] );
      close( se[1] );

/*      write( 1, "Hello\n", 6 );
      write( 2, "World\n", 6 );
      exit( 0 );*/

      exit( execl( "/usr/bin/sh", "sh", "-c", cmd, 0 ) );

  SHUTDOWN:      /* something went wrong...close the pipes & etc */
      e = errno;
      close( si[0] ); close( si[1] );
      close( so[0] ); close( so[1] );
      close( se[0] ); close( se[1] );
      errno = e;
      return -1;
}

int
main( int argc, char **argv )
{
      FILE      *child_io[3];

      if (argc != 2) {
            printf( "usage: %s <name-of-program-to-run>\n", argv[0] );
            exit( 1 );
      }

      if (super_popen( child_io, argv[1] ) == 0) {
            char       buf[256];
            
            fprintf( child_io[0], "Message to child\n" );
            fflush( child_io[0] );

            while (fgets( buf, sizeof(buf), child_io[1] ) != 0) {
                  printf( "Child STDOUT: \"%.*s\"\n", strlen(buf) - 1, buf );
            }

            while (fgets( buf, sizeof(buf), child_io[2] ) != 0) {
                  printf( "Child STDERR: \"%.*s\"\n", strlen(buf) - 1, buf );
            }
      } else {
            printf("couldn't run %s: %d (%s)\n", argv[1], errno, strerror(errno));
      }

      exit( 0 );
}
---------------------------------------------------------
Sample subprogram:

#include <stdio.h>

int
main( int argc, char **argv )
{
      char       buf[128];

      fgets( buf, sizeof(buf), stdin );
      printf( "Child received \"%s\" on stdin\n", buf );
      fprintf( stdout, "This is a message on stdout\n" );
      fprintf( stderr, "This is a message on stderr\n" );
      return 0;
}

0
 

Author Comment

by:zed1
ID: 1295246
Thanx dhm, your solution seems to be working fine.  At first I had a little trouble recieving output from stderr and stdout simultaneously, but all I had to do was alter the while loops in the main.
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Suggested Solutions

Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now