[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference turris::digital_unix

Title:DIGITAL UNIX(FORMERLY KNOWN AS DEC OSF/1)
Notice:Welcome to the Digital UNIX Conference
Moderator:SMURF::DENHAM
Created:Thu Mar 16 1995
Last Modified:Fri Jun 06 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:10068
Total number of notes:35879

9581.0. "Problems with ptys" by TLE::FINLAYSON () Tue Apr 22 1997 16:45

I'm trying to fix a couple of problems with ptys in my application.  First,
when the child process on the other end of the pty dies, I get an EIO error
while reading from the pty which renders it unusable - there seems to be no
way to clear it.  Second, if I close that pty & open a new one, I get an ENXIO
error indicating that there are no more ptys, although this is obviously false
as I can start up other applications that use ptys, or other copies of my
application, and they have no trouble opening ptys.

At the moment, I'm mostly pursuing finding the problems in the application's
use of ptys rather than suspecting bugs in the pty code, but I don't know
enough about correct use of ptys to be able to tell what's wrong.  All the
examples I've seen of pty use seem to limit themselves to opening one pty
once & running one child process in it, and exiting when it's done, but my
application doesn't fit that model.

My application creates child processes whose standard I/O channels are
redirected to a pty.  It must be able to create multiple processes, although
only one at a time.  It calls openpty to open a pty.  It uses XtAppAddInput to
monitor the pty & read from it when there's data available.

Does anyone have any ideas about what I might be doing wrong?

Thanks,

Mark Finlayson
T.RTitleUserPersonal
Name
DateLines
9581.1NNTPD::"[email protected]"paradisWed Apr 23 1997 10:1110
Do you know if your kernel is built with streams or clist ptys? If you
look in the config file check to see if you have a line like:

	pseudo-device pty nnn (where nnn = # of ptys)

If so then you're using clist ptys otherwise streams. Next can you supply
a sample program that reproduces your problem? Thanks.

							--dennis
[Posted by WWW Notes gateway]
9581.2sample programTLE::FINLAYSONWed Apr 23 1997 10:47220
I looked in /usr/sys/conf/<nodename> and didn't find any pseudo-device pty lines.  I have a small test program
that reproduces the ENXIO error when opening the second pty, its source is included below.  At the moment the
small test program doesn't get the EIO error - I'll poke around a little and see if I can reproduce that in a
small program.

Mark Finlayson

The program is compiled & linked as follows:

cc -c -g -std1 -warnprotos testpty2.c
cc -g -o testpty2 testpty2.o

and run as

testpty2 5

where 5 is the number of times it should iterate through the loop.  Since it always fails on the 2nd iteration,
any number >= 2 will do.

#include <sys/ioctl.h>
#include <termios.h>
#include <stdio.h>
#include <sys/param.h>
#include <stdlib.h>
#include <unistd.h>
#include <pty.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <sys/wait.h>

struct pty_info {
    int master;
    int slave;
    char name[MAXPATHLEN];
};

static int OpenPty( struct pty_info *info );
static int ClosePty( struct pty_info *info );
static int ForkProcess( struct pty_info *info );
static void ChildSignalHandler( int sig_caught );

static volatile int done = 0;
static volatile pid_t child_pid = 0;

int main( int argc, char *argv[] )
{
    int total_count = 0;
    struct pty_info info;
    int i, j, n;
    int index;
    char buf[1024];
#ifdef _POSIX_SOURCE
    int             status;
#else
    union wait      status;
#endif
    pid_t           pid;

    if (argc != 2) {
	fprintf( stderr, "Usage: testpty2 total-count\n" );
	exit(-1);
    }
    total_count = atoi( argv[1] );

    for( i = 0; i < total_count; i++ ) {
	/* Open a pty */
	if (!OpenPty( &info ))
	    exit(-1);
	fprintf( stdout, "Opened pty %s\n", info.name );

	done = 0;
	/* Fork a child process that uses the pty */
	fprintf( stdout, "Starting process %d\n", i );
	if (!ForkProcess( &info ))
	    continue;

	/* Read from the pty master until the child process dies */
	while (!done) {
	    errno = 0;
	    while (errno == 0  && !done) {
		n = read( info.master, buf, sizeof(buf));
		if (n > 0) {
		        write( STDOUT_FILENO, buf, n );
		        break;
		}
		else if (n < 0 && errno == EINTR || errno == EAGAIN)
		        errno = 0;
		else if (n < 0 && errno != EINTR && errno != EAGAIN) {
		    fprintf( stderr, "testpty2: Error reading from pty\n" );
		    perror( "testpty2" );
		    sleep(1);
		}
	    }
	}

	if (child_pid != 0) {
	    pid = waitpid( child_pid, &status, 0 );
	    printf( "Status for %d = %d\n", pid, status );
	    child_pid = 0;
	}
 	/* Close the pty */
	ClosePty( &info );
    }

    exit(0);
}

static int ForkProcess( struct pty_info *info )
{
    static char *argv[] = 
	{ "/bin/csh", 
	  "-c",
	  "(cat testpty2.c;sleep 5)",
	  NULL 
	};
    pid_t pid;
    pid_t pgrp;

    struct sigaction action, old_action;
    /* Set up a signal handler to catch the death of a child */
    action.sa_handler = ChildSignalHandler;
    action.sa_flags   = 0;
    sigemptyset(&action.sa_mask);
    sigaddset(&action.sa_mask, SIGCHLD);
    sigprocmask( SIG_UNBLOCK, &action.sa_mask, NULL );
    if (sigaction( SIGCHLD, &action, &old_action ) == -1) {
	fprintf( stderr, "Error setting up child signal handler\n" );
	perror( "testpty2" );
	return 0;
    }

    pid = fork();
    if (pid == 0) {	/* Child process */
	int i;
	int j;

	/* Create all the standard file descriptors */
	if (dup2( info->slave, STDIN_FILENO ) == -1)
	    perror( "testpty2" );
	if (dup2( info->slave, STDOUT_FILENO ) == -1)
	    perror( "testpty2" );
	if (dup2( info->slave, STDERR_FILENO) == -1)
	    perror( "testpty2" );

	/* Close all other file descriptors */
        j = getdtablesize();
        for (i = 3; i < j; ++i) 
	    close(i);

	/* Exec the program in question */
	execv( argv[0], argv );

	/* If we get here, the exec failed */
	fprintf( stderr, "Error: exe failed\n" );
	perror( "testpty2" );
	exit(-1);
    }
    else if (pid < 0) {	/* Parent process, couldn't fork */
	fprintf( stderr, "Error forking child process\n" );
	perror( "testpty2" );
	return 0;
    }
    else {	/* Parent process, fork successful */
	child_pid = pid;
	return 1;
    }
}

/****************************************************************************
 * ChildSignalHandler
 *
 * Catches SIGCHLD signals and handles the death of our children.
 */
static void ChildSignalHandler( int sig_caught )
{
    assert( sig_caught == SIGCHLD );

    if (child_pid == 0)
	return;

    done = 1;
}


static int ClosePty( struct pty_info *info )
{
    int status;

    status = close( info->master );
    if (status != 0) {
        fprintf( stderr, "Error closing master pty %s\n", info->name );
        perror( "testpty2" );
        return 0;
    }
    status = close( info->slave );
    if (status != 0) {
        fprintf( stderr, "Error closing slave pty %s\n", info->name );
        perror( "testpty2" );
        return 0;
    }
    return 1;
}

static int OpenPty( struct pty_info *info )
{
    int status;

    status = openpty( &info->master, &info->slave, &info->name[0], NULL, NULL );
    if (status == -1) {
	fprintf( stderr, "testpty2: error opening pty\n" );
	perror( "testpty2" );
	return 0;
    }

    return 1;
}
9581.3TLE::FINLAYSONWed Apr 23 1997 16:1115
I've discovered that adding the lines

    setsid();
    ioctl( STDIN_FILENO, TIOCSCTTY, 0 );

just after the calls to dup2 in ForkProcess will cause the EIO problem to occur.
Removing the corresponding lines from my application eliminates the EIO errors
& thus eliminates the need to reopen the pty.  

Does this shed any light on anything?  I'd prefer to have the pty be the
controlling terminal for the child processes, because some things won't work
without it (ctrl-C, any I/O to /dev/tty from the child process, etc.).

Mark Finlayson
9581.4NNTPD::&quot;[email protected]&quot;paradisThu Apr 24 1997 11:266
I think what you want to use is the forkpty() which will fork a child
process and establish the slave pty as it's controlling terminal. You
can get more info by doing a man forkpty.

							--dennis
[Posted by WWW Notes gateway]
9581.5possibly similar problemHYDRA::DONSBACHJeff Donsbach, Software Partner Engineering, DTN 297-6862Thu Apr 24 1997 15:2622
I'm working with a partner that has what could be a related problem.

The partner's application is a Motif app that uses XtAppAddInput()
to add "stdin" to the sources of input that the main event look
monitors for input. When their application is started, it opens the
main Motif Widget window, but doesn't completely finish painting the
widgets and icons.

It turns out the application was somehow blocked in a "select()" call
on "stdin". We also, almost accidentally, stumbled across the fact
that if the kernel is rebuilt to use "clist" based ptys instead of
STREAMS based ptys, it fixes the problem!

Sound familiar to anyone? I don't have a simple reproducer yet, nor do
I know if this problem is unique to Unix 4.0 or not. As soon as I get
a reproducer, a QAR and CLD will be filed.....

Regards,
Jeff Donsbach
Software Partner Engineering
MRO Center
9581.6.4 got it - use forkpty/openptyRHETT::PARKERThu Apr 24 1997 16:3639
    
    Hi, Jeff.
    
    I think Dennis is right on the money in .4 because I ran into
    a similar issue recently. If you look at the pty(7)man page, 
    you will see :
    
    The Digital UNIX operating system supports a STREAMS-based and clist-based
    implementation of the pty subsystem.  The default configuration uses
    STREAMS-based ptys.  STREAMS-based ptys use the options RPTY line in the
    kernel configuration file, while clist-based ptys use the pseudo-device,
    pty.  By default, 32 pseudo-terminal special device files are created.
    
    Note that you cannot have both types of ptys configured at the same time.
    
    To enhance compatibility, STREAMS-based ptys offers two master pseudo
    terminal drivers, the BSD compatible master and the System V compatible
    master.  The BSD master is a non-STREAMS device which interfaces to the
    STREAMS-based slave pty.  The System V master is a STREAMS-based device
    which also interfaces to the STREAMS-based slave pty.  The BSD master is
    opened through the cloning device, /dev/ptmx_bsd, and through the master
    pty special files, /dev/ptyXX.  The System V master is opened only through
    the cloning device /dev/ptmx.  Currently the BSD master cloning
    device is used by the libc routine openpty(3).
    
    Older apps that used the clist based pseudo-driver usually open
    /dev/ptyXX as stated above. And, we do provide a BSD compatible master
    driver - it's just that instead of using open(2), you will want to use
    openpty(3)/fortpty(3) or the behavior you have seen will result. 
    
    Also mentioned on the man page for pty(7) :
    
    You should allocate ptys by using the openpty(3) function, which hides the
    pty name space that will change in the next major operating system release.
    
    Hth,
    
    Lee
    
9581.7help a beginner understandHYDRA::DONSBACHJeff Donsbach, Software Partner Engineering, DTN 297-6862Thu Apr 24 1997 19:2616
    Lee,
    
    Forgive my ignorance. I've never programmed pty's before.
    
    So, what your saying is that this Motif program just can't
    use the "stdin" that it inherited from it's parent shell/xterm;
    it needs to actually open it's own pty using "openpty()"? I'm not
    sure how the partner's application is opening "stdin" right now. I'm
    checking. Why would it need to call "forkpty()" to fork a child
    (or maybe it doesn't)?
    
    Is there a good primer document/book on pty programming somewhere?
    
    Thanks,
    -Jeff
    
9581.8Who me?RHETT::PARKERTue Apr 29 1997 17:3028
    
    Hi, Jeff. 
    
    Sorry for the late reply - I've been out since last thursday.
    
    > I've never programmed pty's before.
    
    Well, I'm no expert here either. :-) 
    
    All I meant was that the older method does not work well with the
    BSD master on Digital UNIX. That's what is documented on the pty(7)
    man page - i.e. instead of just opening /dev/ptyXX (if using the 
    BSD master) - you use openpty/fortpty instead. 
    
    Sounds like getting more specifics is a good idea. We are just 
    guessing w/o more specifics. 
    
    > Is there a good primer document/book on pty programming somewhere?
    
    The "Network Programmers Guide" has some good information on this
    and I'm sure there are other manuals out there. The souces are a
    good reference too...
    
    Hth, 
    
    Lee