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