pseudo tty stderr capture problem.

From: Min Shei (minshei_at_hotmail.com)
Date: 07/16/03


Date: 15 Jul 2003 16:07:29 -0700

Hi, All,

I was trying to make a simple pseudo tty program to capture the stdout
and stderr from the child process to avoid the stdout buffering
problem. The following is a little program I found on one new group
here (written by Richard Tobin, 1990). However, this program captures
the child's stdout very well. But I just couldn't make it work for the
stderr of the child process. My platform is Solaris 2.7.

Has anyone here had the same problem? The program could be compiled
by:
cc -o nobuf nobuf.c pseudopipe.c

And if you run "./nobuf ls no-such-file", you should get message like
"no-such-file: No such file or directory". But there is not.

I just couldn't figure out what is wrong with this program. :(
Please help.

Thanks in advance.
Min

----------------------------------- nobuf.c
----------------------------------

/* A program to defeat buffering by stdio when the output is a pipe.
 * nobuf program args ...
 * runs the program with its standard input, output, and error
connected
 * to a pseudo-terminal. It transfers data between the
pseudo-terminal
 * and the real standard input etc. Since the program sees its output
is
 * a terminal, it won't buffer it.
 *
 * Richard Tobin, 1990. You may redistribute this program freely if
this
 * whole comment remains intact.
 */

#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <unistd.h>

int dead_baby(); /* called when child exits */
int pty, ifds0;

main(argc, argv)
     int argc;
     char *argv[];
{
  int ptyfds[2];
  
  pseudopipe(ptyfds); /* create pty to talk to child */
  
  ttymodes(ptyfds[0]);
  
  signal(SIGCHLD, dead_baby); /* so we can exit when child does */
  
  subprocess(&argv[1], ptyfds); /* start up the subshell */
  
  close(ptyfds[0]); /* parent doesn't need it */
  
  pty = ptyfds[1];
  
  transfer();
}

/* fork and exec the process in argv */

subprocess(argv, ptyfds)
     char **argv;
     int ptyfds[2];
{
  int tty;
  
  if(fork() != 0) return;
  
  /* close files child doesn't want */
  close(ptyfds[1]);
  close(0);
  close(1);
  close(2);
  
  /* get rid of controlling terminal */
  tty = open("/dev/tty", O_RDWR, 0);
  ioctl(tty, TIOCNOTTY, 0);
  close(tty);
  
  /* set up pty as std input, output, error */
  dup2(ptyfds[0], 0);
  dup2(ptyfds[0], 1);
  dup2(ptyfds[0], 2);

  close(ptyfds[0]);
  
  /* run the process */
  execvp(argv[0], argv);
  
  write(2, "exec failed\n", 12);
  _exit(1);
}

/* set the pseudo terminal to fairly sane modes */

ttymodes(pty)
     int pty;
{
  static struct sgttyb sgtty = {B9600, B9600, 255, 255, EVENP|ODDP};
  static struct tchars tchars = {255, 255, 255, 255, 4, 255};
  
  ioctl(pty, TIOCSETP, &sgtty);
  ioctl(pty, TIOCSETC, &tchars);
}

/* transfer characters between standard input/output and pty */

transfer()
{
  int ifds, nread;
  char buf[256], ctld=4;
  extern int errno;
  
  ifds0 = (1 << 0) | (1 << pty); /* listen to real and pseudo ttys */
  
  while(ifds0 != 0)
    {
      ifds = ifds0;
      
      if(select(pty+1, &ifds, (int *)0, (int *)0, (struct timeval *)0)
< 0)
        {
          if(errno == EINTR)
            continue;
          else
            {
              perror("select");
              exit(1);
            }
        }
      
      if(ifds & (1<<0))
        {
          nread = read(0, buf, 256); /* from tty to process */
          if(nread > 0)
            write(pty, buf, nread);
          else if(nread == 0)
            write(pty, &ctld, 1); /* writing zero bytes doesn't work
*/
          else
            ifds0 &= ~(1 << 0);
        }
      
      if(ifds & (1 << pty))
        {
          nread = read(pty, buf, 256); /* from process to tty */
          if(nread >= 0)
            write(1, buf, nread);
          else
            ifds0 &= ~(1 << pty);
        }
    }
  
  while(1)
    sigpause(0); /* wait for child to exit */
}

/* child process exited - do any remaining i/o and exit */

dead_baby()
{
  char buf[256];
  int nread;

  while(ioctl(pty, FIONREAD, &nread) == 0 && nread > 0)
    {
      nread = read(pty, buf, 256);
      write(1, buf, nread);
    }
  
  exit(0);
}

-------------------------------- pseudopipe.c
-----------------------------

/* like pipe(2) but uses pseudo-terminals. consequently, the
connection
 * is bi-directional, and will appear as a terminal to any forked
process.
 * copyright (C) 1986 Richard M Tobin
 * you may freely distribute/modify this provided this whole comment
remains
 * intact.
 */

#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <strings.h>
#include <stdio.h>

/* pseudopipe puts two file descriptors in its argument array. fd[0]
is the
 * slave side of a pseudo-terminal, fd[1] is the master side. to talk
to a
 * child process, dup fd[0] onto file descriptors 0, 1 and 2.
 * if anything goes wrong (probably no free pty) -1 is returned.
 */

int pseudopipe(fd)
int fd[2];
{
    char pty_master[128], pty_slave[128];
#define masterfd fd[1]
#define slavefd fd[0]
    FILE *cons;
    int start=0;

    masterfd = findpty(pty_master, pty_slave, &start);
    if(masterfd < 0) return -1;
    slavefd = open(pty_slave, O_RDWR, 0);

    if(slavefd >= 0) return 0;

    return -1;

#undef masterfd
#undef slavefd
}

/* findpty find a free pseudo-terminal, and opens the master side of
it.
 * the arguments are filled in to be the paths of the master and
slave.
 */

#define MASTER "/dev/ptyXY"
#define SLAVE "/dev/ttyXY"

findpty(master_path, slave_path, ptyno)
     char *master_path, *slave_path;
     int *ptyno;
{
  struct stat statbuf;
  char c1;
  int i, master;
  
  strcpy(master_path, MASTER);
  strcpy(slave_path, SLAVE );
  
  /* the pseudo-ttys are /dev/ptyXY, where X is p,q,or r and Y is a
   * hex digit. There are usually 16, 32 or 48 so we check for the
   * of /dev/ptyX0 before trying to open them all.
   */
  
  for(; *ptyno <= ('s'-'p') * 16; ++*ptyno)
    {
      master_path[strlen(MASTER)-2] = *ptyno / 16 + 'p';
      master_path[strlen(MASTER)-1] = "0123456789abcdef"[*ptyno & 15];
      master = open(master_path,O_RDWR);
      if(master < 0) continue;
      slave_path[strlen(SLAVE)-2] = master_path[strlen(MASTER)-2];
      slave_path[strlen(SLAVE)-1] = master_path[strlen(MASTER)-1];
      return master;
    }
  
  return -1;
}



Relevant Pages

  • [PATCH 3/7] dlm: recovery
    ... When a node is removed from a lockspace, recovery is required for that ... lockspace on all the remaining lockspace members. ... full rebuild of the distributed resource directory, selecting a new master ... +static int create_rcom(struct dlm_ls *ls, int to_nodeid, int type, int len, ...
    (Linux-Kernel)
  • [PATCH 4/8] dlm: recovery
    ... When a node is removed from a lockspace, recovery is required for that ... full rebuild of the distributed resource directory, selecting a new master ... +static int create_rcom(struct dlm_ls *ls, int to_nodeid, int type, int len, ... +static void dlm_wait_timer_fn ...
    (Linux-Kernel)
  • [patch 08/12] dlm: no directory option
    ... Per-lockspace option for dlm to run without using a resource ... statically assigned to be the master node instead. ... +static inline int dlm_no_directory ... static void purge_dead_locks ...
    (Linux-Kernel)
  • MPI program stalls for many MPI_Send/MPI_Recv calls
    ... is run with 2 processes (1 master, 1 slave) in which the master ... repeatedly sends by MPI_Send a 13-byte string buffer to one slave, ... void do_master(int rank); ... int main ...
    (comp.parallel.mpi)
  • Non-blocking Irecv and Isend
    ... The master process (rank 0) ... then send the integers back to the master. ... there are 2 slave processors and the integers to be ... void quicksort(int a, int lo, int hi) ...
    (comp.parallel.mpi)