Solved

redirecting stderr in c

Posted on 1998-07-30
8
1,277 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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
How to match SAS datasets by multiple key variables and then rename and create new variables? 2 86
sum28 challenge 31 107
java ^ examples 8 62
unix example issues 18 76
Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
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.
With the power of JIRA, there's an unlimited number of ways you can customize it, use it and benefit from it. With that in mind, there's bound to be things that I wasn't able to cover in this course. With this summary we'll look at some places to go…

863 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

28 Experts available now in Live!

Get 1:1 Help Now