Solved

redirecting stderr in c

Posted on 1998-07-30
8
1,314 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 
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

Enroll in May's Course of the Month

May’s Course of the Month is now available! Experts Exchange’s Premium Members and Team Accounts have access to a complimentary course each month as part of their membership—an extra way to increase training and boost professional development.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
xyBalance chalenge 58 103
windows 10 how make picture as desktop background 2 71
ffmpeg - "rtsp://...... Operation not permitted" 4 92
Adjust the codes 3 63
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…
In this post we will learn different types of Android Layout and some basics of an Android App.
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.

737 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