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

Conference csc32::consolemanager

Title:POLYCENTER Console Manager
Notice:Kits, Scans, Docs on CSC32:: as PCM$KITS:,PCM$DOCS:, PCM$SCANS:
Moderator:CSC32::BUTTERWORTH
Created:Thu Aug 06 1992
Last Modified:Fri Jun 06 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:1541
Total number of notes:6564

491.0. "Monitoring console's on OSF/1 machines" by STKEIS::BYSTAM (The answer is 42) Fri Nov 25 1994 09:39

Hi,

I have a problem at a customer site. We have tried setting up PCM V1.5 on
OSF/1. The customer is at the moment only interested in cathing console 
messages and not the full console connection capability so we were going
to use pseudo devices to catch the console messages. So what I started with 
was to monitor the PCM system itself using a pseudodevice and putting in the 
command xconsole.

This brings up the xconsole window on the machine and all looks ok at that 
point but the messages displayed in the xconsole does not show up in PCM
I have added events, and updated scanprofiles and set up the correct 
scanprofile for the machine and also defined the filters to look for some
specific consolemessages. But nothing happens at all in PCM but I can se the
messages in the xconsole window!

Am I doing something wrong here or should this not work? 

And then we tried setting up the same kind of thing to a remote system using
rsh but then we could not connect to the console since the system itself had a
console up on the machine. To fix this I guess we need to set up the system
to start without a console on the machine itself, but I am no Unix person
so I don't known exactly how do do this and if it really is possible. Anybody
having an idea on how to do this?

/Stefan
T.RTitleUserPersonal
Name
DateLines
491.1OPG::PHILIPAnd through the square window...Fri Nov 25 1994 10:0715
Stefan,

  xconsole is an X-Windows interface isnt it? What makes you think that PCM
  will pick up any messages in the display produced by xconsole?

  You need to run something in your pseudo-terminal which outputs messages
  to stdout, that is an application which is character cell based. I dont
  know if one exists on OSF/1 which does this, maybe someone who knows
  more about OSF can help here.

  Unless of course xconsole outputs its messages to stdout as well as the
  X-Window display.

Cheers,
Phil
491.2I suspected thatSTKEIS::BYSTAMThe answer is 42Fri Nov 25 1994 10:4520
>
>  xconsole is an X-Windows interface isnt it? What makes you think that PCM
>  will pick up any messages in the display produced by xconsole?
>

Yes it is an X-Windows application

>
>  You need to run something in your pseudo-terminal which outputs messages
>  to stdout, that is an application which is character cell based. I dont
>  know if one exists on OSF/1 which does this, maybe someone who knows
>  more about OSF can help here.
>

I was sort of suspecting this. I hope somebody else has an idea about an
character cell based application that can do this.

Thanks 

/Stefan
491.3I use vtconsHGOVC::JOSEPHKWOKThu Jan 05 1995 11:36403
Hi,

I have the same problem and it can be partially solved by using
a small program modified from xconsole.  I named it vtcons which
is essentially an xconsole but output is directed to stdout.
Attached is the source.  Simply compiled it by

# cc -O -o vtcons vtcons.c

The vtcons need to be owned by root with setuid, i.e. you need to
change mode to 4755.

But it raise another problem for me because I am using a DEC7000 and
once I use vtcons to trap all the console message, I cannot login from
the console (or strictly speaking, I can login from the console but
output will be redirected to the vtcons's stdout).  This is exactly
the same behaviour as using dxconsole or xconsole for the DEC7000.

May be I need to make the stdin for vtcons as the stdin for the
console?  Any other good idea to solve it?

Regards,
Joseph


#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#include <stdio.h>
#undef _POSIX_SOURCE
#else
#include <stdio.h>
#endif
#include <ctype.h>

/* Fix ISC brain damage.  When using gcc fdopen isn't declared in <stdio.h>. */
#if defined(SYSV) && defined(SYSV386) && defined(__STDC__) && defined(ISC)
extern FILE *fdopen(int, char const *);
#endif

static FILE    *input;
static int     prcharonly = 0;

#ifdef ultrix
#define USE_FILE
#define FILE_NAME   "/dev/xcons"
#endif

#ifndef USE_FILE
#include    <sys/ioctl.h>
#ifdef hpux
#include    <termios.h>
#endif
#ifdef SVR4
#include    <termios.h>
#include    <sys/stropts.h>		/* for I_PUSH */
#ifdef sun
#include    <sys/strredir.h>
#endif
#endif

#if defined(TIOCCONS) || defined(SRIOCSREDIR)
#define USE_PTY
static int  tty_fd, pty_fd;
static char ttydev[64], ptydev[64];
#endif
#endif

#if defined(SVR4) || (defined(SYSV) && defined(SYSV386))
#define USE_OSM
#include <signal.h>
FILE *osm_pipe();
static int child_pid;
#endif

static void inputReady ();

static
OpenConsole ()
{
    struct stat sbuf;

    input = 0;
    /* must be owner and have read/write permission */
    if (!stat("/dev/console", &sbuf) &&
	(sbuf.st_uid == getuid()) &&
	!access("/dev/console", R_OK|W_OK))
    {
#ifdef USE_FILE
    	input = fopen (FILE_NAME, "r");
#endif
#ifdef USE_PTY
	if (get_pty (&pty_fd, &tty_fd, ttydev, ptydev) == 0)
	{
#ifdef TIOCCONS
	    int on = 1;
	    if (ioctl (tty_fd, TIOCCONS, (char *) &on) != -1)
		input = fdopen (pty_fd, "r");
#else
	    int consfd = open("/dev/console", O_RDONLY);
	    if (consfd >= 0)
	    {
		if (ioctl(consfd, SRIOCSREDIR, tty_fd) != -1)
		    input = fdopen (pty_fd, "r");
		close(consfd);
	    }
#endif
	}
#endif
    }
#ifdef USE_OSM
    /* Don't have to be owner of /dev/console when using /dev/osm. */
    if (!input)
	input = osm_pipe();
#endif
}


static
CloseConsole ()
{
    if (input) {
	fclose (input);
    }
#ifdef USE_PTY
    close (tty_fd);
#endif
}

#ifdef USE_OSM
static void
KillChild(sig)
    int sig;
{
    if (child_pid > 0)
	kill(child_pid, SIGTERM);
    exit(0);
}
#endif

/*ARGSUSED*/
static void
Quit ()
{
#ifdef USE_OSM
    if (child_pid > 0)
	kill(child_pid, SIGTERM);
#endif
    exit (0);
}

static void
stripNonprint (b)
    char    *b;
{
    char    *c;

    c = b;
    while (*b)
    {
	if (isprint (*b) || isspace (*b) && *b != '\r')
	{
	    if (c != b)
		*c = *b;
	    ++c;
	}
	++b;
    }
    *c = '\0';
}

static void
inputReady ()
{
    char    buffer[1025];
    int	    n;

    n = read (fileno(input), buffer, sizeof (buffer) - 1);
    if (n <= 0)
    {
	CloseConsole();
	Quit();
    }
    buffer[n] = '\0';
    if (prcharonly)
    {
	stripNonprint (buffer);
	n = strlen (buffer);
    }
    printf("%s", buffer);
}


main ()
{
    OpenConsole ();
    if (input)
	do {
	    inputReady ();
	} while (1);
}

#ifdef USE_PTY
/* This function opens up a pty master and stuffs it's value into pty.
 * If it finds one, it returns a value of 0.  If it does not find one,
 * it returns a value of !0.  This routine is designed to be re-entrant,
 * so that if a pty master is found and later, we find that the slave
 * has problems, we can re-enter this function and get another one.
 */

/*
** allow for mobility of the pty master/slave directories
*/
#ifndef PTYDEV
#ifdef hpux
#define	PTYDEV		"/dev/ptym/ptyxx"
#else	/* !hpux */
#define	PTYDEV		"/dev/ptyxx"
#endif	/* !hpux */
#endif	/* !PTYDEV */

#ifndef TTYDEV
#ifdef hpux
#define TTYDEV		"/dev/pty/ttyxx"
#else	/* !hpux */
#define	TTYDEV		"/dev/ttyxx"
#endif	/* !hpux */
#endif	/* !TTYDEV */

#ifndef PTYCHAR1
#ifdef hpux
#define PTYCHAR1	"zyxwvutsrqp"
#else	/* !hpux */
#define	PTYCHAR1	"pqrstuvwxyzPQRSTUVWXYZ"
#endif	/* !hpux */
#endif	/* !PTYCHAR1 */

#ifndef PTYCHAR2
#ifdef hpux
#define	PTYCHAR2	"fedcba9876543210"
#else	/* !hpux */
#define	PTYCHAR2	"0123456789abcdef"
#endif	/* !hpux */
#endif	/* !PTYCHAR2 */

get_pty (pty, tty, ttydev, ptydev)
    int	    *pty, *tty;
    char    *ttydev, *ptydev;
{
#ifdef SVR4
	if ((*pty = open ("/dev/ptmx", O_RDWR)) < 0) {
	    return 1;
	}
	grantpt(*pty);
	unlockpt(*pty);
	strcpy(ttydev, (char *)ptsname(*pty));
	if ((*tty = open(ttydev, O_RDWR)) >= 0) {
	    (void)ioctl(*tty, I_PUSH, "ttcompat");
	    return 0;
	}
	if (*pty >= 0)
	    close (*pty);
#else /* !SVR4, need lots of code */
#ifdef USE_GET_PSEUDOTTY
	if ((*pty = getpseudotty (&ttydev, &ptydev)) >= 0 &&
	    (*tty = open (ttydev, O_RDWR)) >= 0)
	    return 0;
	if (*pty >= 0)
	    close (*pty);
#else
	static int devindex, letter = 0;

#if defined(umips) && defined (SYSTYPE_SYSV)
	struct stat fstat_buf;

	*pty = open ("/dev/ptc", O_RDWR);
	if (*pty < 0 || (fstat (*pty, &fstat_buf)) < 0) {
	  return(1);
	}
	sprintf (ttydev, "/dev/ttyq%d", minor(fstat_buf.st_rdev));
	sprintf (ptydev, "/dev/ptyq%d", minor(fstat_buf.st_rdev));
	if ((*tty = open (ttydev, O_RDWR)) >= 0) {
	    /* got one! */
	    return(0);
	}
	close (*pty);
#else /* not (umips && SYSTYPE_SYSV) */
#ifdef CRAY
	for (; devindex < 256; devindex++) {
	    sprintf (ttydev, "/dev/ttyp%03d", devindex);
	    sprintf (ptydev, "/dev/pty/%03d", devindex);

	    if ((*pty = open (ptydev, O_RDWR)) >= 0 &&
		(*tty = open (ttydev, O_RDWR)) >= 0)
	    {
		/* We need to set things up for our next entry
		 * into this function!
		 */
		(void) devindex++;
		return(0);
	    }
	    if (*pty >= 0)
		close (*pty);
	}
#else /* !CRAY */
#ifdef sgi
	{ 
	    char *slave;
	    slave = _getpty (pty, O_RDWR, 0622, 0);
	    if ((*tty = open (slave, O_RDWR)) != -1)
		return 0;
	}
#else
	strcpy (ttydev, "/dev/ttyxx");
	strcpy (ptydev, "/dev/ptyxx");
	while (PTYCHAR1[letter]) {
	    ttydev [strlen(ttydev) - 2]  = ptydev [strlen(ptydev) - 2] =
		    PTYCHAR1 [letter];

	    while (PTYCHAR2[devindex]) {
		ttydev [strlen(ttydev) - 1] = ptydev [strlen(ptydev) - 1] =
			PTYCHAR2 [devindex];
		if ((*pty = open (ptydev, O_RDWR)) >= 0 &&
		    (*tty = open (ttydev, O_RDWR)) >= 0)
		{
			/* We need to set things up for our next entry
			 * into this function!
			 */
			(void) devindex++;
			return(0);
		}
		if (*pty >= 0)
		    close (*pty);
		devindex++;
	    }
	    devindex = 0;
	    (void) letter++;
	}
#endif /* sgi else not sgi */
#endif /* CRAY else not CRAY */
#endif /* umips && SYSTYPE_SYSV */
#endif /* USE_GET_PSEUDOTTY */
#endif /* SVR4 */
	/* We were unable to allocate a pty master!  Return an error
	 * condition and let our caller terminate cleanly.
	 */
	return(1);
}
#endif

#ifdef USE_OSM
/*
 * On SYSV386 there is a special device, /dev/osm, where system messages
 * are sent.  Problem is that we can't perform a select(2) on this device.
 * So this routine creates a streams-pty where one end reads the device and
 * sends the output to xconsole.
 */
FILE *
osm_pipe()
{
  int tty;
  char ttydev[64];
    
  if (access("/dev/osm", R_OK) < 0) return NULL;
  if ((tty = open("/dev/ptmx", O_RDWR)) < 0)  return NULL;

  grantpt(tty);
  unlockpt(tty);
  strcpy(ttydev, (char *)ptsname(tty));

  if ((child_pid = fork()) == 0) {
    int pty, osm, nbytes, skip;
    char cbuf[128];

    skip = 0;
    osm = open("/dev/osm1", O_RDONLY);
    if (osm >= 0) {
	while ((nbytes = read(osm, cbuf, sizeof(cbuf))) > 0)
	    skip += nbytes;
	close(osm);
    }
    pty = open(ttydev, O_RDWR);
    if (pty < 0) exit(1);
    osm = open("/dev/osm", O_RDONLY);
    if (osm < 0) exit(1);
    for (nbytes = 0; skip > 0 && nbytes >= 0; skip -= nbytes) {
	nbytes = skip;
	if (nbytes > sizeof(cbuf))
	    nbytes = sizeof(cbuf);
	nbytes = read(osm, cbuf, nbytes);
    }
    while ((nbytes = read(osm, cbuf, sizeof(cbuf))) >= 0)
      write(pty, cbuf, nbytes);
    exit(0);
  }
  signal(SIGHUP, KillChild);
  signal(SIGINT, KillChild);
  signal(SIGTERM, KillChild);
  return fdopen(tty, "r");
}
#endif  /* USE_OSM */
491.4STKEIS::BYSTAMThe answer is 42Wed Jan 18 1995 15:025
Joseph,

I will try this solution at the customer site. Thans a lot.

/Stefan
491.5added fflush to work with rshKAL::BJERKEHOLTBo Bjerkeholt, TPSG, SWAS, @GOOThu Mar 02 1995 15:00419
	Didn't get vtcons to print on stdout when used together with  rsh  .
	Added fflush(stdout) which seems to do the trick.  And some other 
	things

-------------------cut here--------------------------------------

#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#include <stdio.h>
#undef _POSIX_SOURCE
#else
#include <stdio.h>
#endif
#include <ctype.h>

/* Fix ISC brain damage.  When using gcc fdopen isn't declared in <stdio.h>. */
#if defined(SYSV) && defined(SYSV386) && defined(__STDC__) && defined(ISC)
extern FILE *fdopen(int, char const *);
#endif

static FILE    *input;
static int     prcharonly = 0;

#ifdef ultrix
#define USE_FILE
#define FILE_NAME   "/dev/xcons"
#endif

#ifndef USE_FILE
#include    <sys/ioctl.h>
#ifdef hpux
#include    <termios.h>
#endif
#ifdef SVR4
#include    <termios.h>
#include    <sys/stropts.h>		/* for I_PUSH */
#ifdef sun
#include    <sys/strredir.h>
#endif
#endif

#if defined(TIOCCONS) || defined(SRIOCSREDIR)
#define USE_PTY
static int  tty_fd, pty_fd;
static char ttydev[64], ptydev[64];
#endif
#endif

#if defined(SVR4) || (defined(SYSV) && defined(SYSV386))
#define USE_OSM
#include <signal.h>
FILE *osm_pipe();
static int child_pid;
#endif

static void inputReady ();
static void quit_signalled ( int );

static
OpenConsole ()
{
    struct stat sbuf;

    input = 0;

    /* must be owner and have read/write permission */
    if (!stat("/dev/console", &sbuf) &&
	(sbuf.st_uid == getuid()) &&
	!access("/dev/console", R_OK|W_OK))
    {
#ifdef USE_FILE
    	input = fopen (FILE_NAME, "r");
#endif
#ifdef USE_PTY
	if (get_pty (&pty_fd, &tty_fd, ttydev, ptydev) == 0)
	{
#ifdef TIOCCONS
	    int on = 1;
	    if (ioctl (tty_fd, TIOCCONS,  &on) != -1)
		input = fdopen (pty_fd, "r");
	    else 
		perror("ioctl error: ");
#else
	    int consfd = open("/dev/console", O_RDONLY);
	    if (consfd >= 0)
	    {
		if (ioctl(consfd, SRIOCSREDIR, tty_fd) != -1)
		    input = fdopen (pty_fd, "r");
		else 
		  perror("ioctl error: ");
		close(consfd);
	    }
#endif
	}
#endif
    }
#ifdef USE_OSM
    /* Don't have to be owner of /dev/console when using /dev/osm. */
    if (!input)
	input = osm_pipe();
#endif
}


static
CloseConsole ()
{
    if (input) {
	fclose (input);
    }
#ifdef USE_PTY
    close (tty_fd);
#endif
}

#ifdef USE_OSM
static void
KillChild(sig)
    int sig;
{
    if (child_pid > 0)
	kill(child_pid, SIGTERM);
    exit(0);
}
#endif

/*ARGSUSED*/
static void
Quit ()
{
#ifdef USE_OSM
    if (child_pid > 0)
	kill(child_pid, SIGTERM);
#endif
    exit (0);
}

static void
stripNonprint (b)
    char    *b;
{
    char    *c;

    c = b;
    while (*b)
    {
	if (isprint (*b) || isspace (*b) && *b != '\r')
	{
	    if (c != b)
		*c = *b;
	    ++c;
	}
	++b;
    }
    *c = '\0';
}

static void
inputReady ()
{
    char    buffer[1025];
    int	    n;

    n = read (fileno(input), buffer, sizeof (buffer) - 1);
    if (n <= 0)
    {
	CloseConsole();
	Quit();
    }
    buffer[n] = '\0';
    if (prcharonly)
    {
	stripNonprint (buffer);
	n = strlen (buffer);
    }
    printf("%s", buffer);
    fflush(stdout);
}

main ()
{
    /*
     * catch interrupt, hangup, and quit so we can unlock the front panel
     */
   
    (void) signal(SIGINT, quit_signalled);
    (void) signal(SIGHUP, quit_signalled);
    (void) signal(SIGQUIT, quit_signalled);
    (void) signal(SIGABRT, quit_signalled);
    (void) signal(SIGKILL, quit_signalled);
    (void) signal(SIGTERM, quit_signalled);

    OpenConsole ();
    if (input)
	do {
	    inputReady ();
	} while (1);
}

/************************************************************************
 *
 *      quit_signalled()... bye!
 *
 ************************************************************************/

static void quit_signalled(int sig)
{
  int on=0;
    ioctl (0, TIOCCONS, &on);
    printf("Exiting....\n");
    exit(0);
}


#ifdef USE_PTY
/* This function opens up a pty master and stuffs it's value into pty.
 * If it finds one, it returns a value of 0.  If it does not find one,
 * it returns a value of !0.  This routine is designed to be re-entrant,
 * so that if a pty master is found and later, we find that the slave
 * has problems, we can re-enter this function and get another one.
 */

/*
** allow for mobility of the pty master/slave directories
*/
#ifndef PTYDEV
#ifdef hpux
#define	PTYDEV		"/dev/ptym/ptyxx"
#else	/* !hpux */
#define	PTYDEV		"/dev/ptyxx"
#endif	/* !hpux */
#endif	/* !PTYDEV */

#ifndef TTYDEV
#ifdef hpux
#define TTYDEV		"/dev/pty/ttyxx"
#else	/* !hpux */
#define	TTYDEV		"/dev/ttyxx"
#endif	/* !hpux */
#endif	/* !TTYDEV */

#ifndef PTYCHAR1
#ifdef hpux
#define PTYCHAR1	"zyxwvutsrqp"
#else	/* !hpux */
#define	PTYCHAR1	"pqrstuvwxyzPQRSTUVWXYZ"
#endif	/* !hpux */
#endif	/* !PTYCHAR1 */

#ifndef PTYCHAR2
#ifdef hpux
#define	PTYCHAR2	"fedcba9876543210"
#else	/* !hpux */
#define	PTYCHAR2	"0123456789abcdef"
#endif	/* !hpux */
#endif	/* !PTYCHAR2 */

get_pty (pty, tty, ttydev, ptydev)
    int	    *pty, *tty;
    char    *ttydev, *ptydev;
{
#ifdef SVR4
	if ((*pty = open ("/dev/ptmx", O_RDWR)) < 0) {
	    return 1;
	}
	grantpt(*pty);
	unlockpt(*pty);
	strcpy(ttydev, (char *)ptsname(*pty));
	if ((*tty = open(ttydev, O_RDWR)) >= 0) {
	    (void)ioctl(*tty, I_PUSH, "ttcompat");
	    return 0;
	}
	if (*pty >= 0)
	    close (*pty);
#else /* !SVR4, need lots of code */
#ifdef USE_GET_PSEUDOTTY
	if ((*pty = getpseudotty (&ttydev, &ptydev)) >= 0 &&
	    (*tty = open (ttydev, O_RDWR)) >= 0)
	    return 0;
	if (*pty >= 0)
	    close (*pty);
#else
	static int devindex, letter = 0;

#if defined(umips) && defined (SYSTYPE_SYSV)
	struct stat fstat_buf;

	*pty = open ("/dev/ptc", O_RDWR);
	if (*pty < 0 || (fstat (*pty, &fstat_buf)) < 0) {
	  return(1);
	}
	sprintf (ttydev, "/dev/ttyq%d", minor(fstat_buf.st_rdev));
	sprintf (ptydev, "/dev/ptyq%d", minor(fstat_buf.st_rdev));
	if ((*tty = open (ttydev, O_RDWR)) >= 0) {
	    /* got one! */
	    return(0);
	}
	close (*pty);
#else /* not (umips && SYSTYPE_SYSV) */
#ifdef CRAY
	for (; devindex < 256; devindex++) {
	    sprintf (ttydev, "/dev/ttyp%03d", devindex);
	    sprintf (ptydev, "/dev/pty/%03d", devindex);

	    if ((*pty = open (ptydev, O_RDWR)) >= 0 &&
		(*tty = open (ttydev, O_RDWR)) >= 0)
	    {
		/* We need to set things up for our next entry
		 * into this function!
		 */
		(void) devindex++;
		return(0);
	    }
	    if (*pty >= 0)
		close (*pty);
	}
#else /* !CRAY */
#ifdef sgi
	{ 
	    char *slave;
	    slave = _getpty (pty, O_RDWR, 0622, 0);
	    if ((*tty = open (slave, O_RDWR)) != -1)
		return 0;
	}
#else
	strcpy (ttydev, "/dev/ttyxx");
	strcpy (ptydev, "/dev/ptyxx");
	while (PTYCHAR1[letter]) {
	    ttydev [strlen(ttydev) - 2]  = ptydev [strlen(ptydev) - 2] =
		    PTYCHAR1 [letter];

	    while (PTYCHAR2[devindex]) {
		ttydev [strlen(ttydev) - 1] = ptydev [strlen(ptydev) - 1] =
			PTYCHAR2 [devindex];
		if ((*pty = open (ptydev, O_RDWR)) >= 0 &&
		    (*tty = open (ttydev, O_RDWR)) >= 0)
		{
			/* We need to set things up for our next entry
			 * into this function!
			 */
			(void) devindex++;
			return(0);
		}
		if (*pty >= 0)
		    close (*pty);
		devindex++;
	    }
	    devindex = 0;
	    (void) letter++;
	}
#endif /* sgi else not sgi */
#endif /* CRAY else not CRAY */
#endif /* umips && SYSTYPE_SYSV */
#endif /* USE_GET_PSEUDOTTY */
#endif /* SVR4 */
	/* We were unable to allocate a pty master!  Return an error
	 * condition and let our caller terminate cleanly.
	 */
	return(1);
}
#endif

#ifdef USE_OSM
/*
 * On SYSV386 there is a special device, /dev/osm, where system messages
 * are sent.  Problem is that we can't perform a select(2) on this device.
 * So this routine creates a streams-pty where one end reads the device and
 * sends the output to xconsole.
 */
FILE *
osm_pipe()
{
  int tty;
  char ttydev[64];
    
  if (access("/dev/osm", R_OK) < 0) return NULL;
  if ((tty = open("/dev/ptmx", O_RDWR)) < 0)  return NULL;

  grantpt(tty);
  unlockpt(tty);
  strcpy(ttydev, (char *)ptsname(tty));

  if ((child_pid = fork()) == 0) {
    int pty, osm, nbytes, skip;
    char cbuf[128];

    skip = 0;
    osm = open("/dev/osm1", O_RDONLY);
    if (osm >= 0) {
	while ((nbytes = read(osm, cbuf, sizeof(cbuf))) > 0)
	    skip += nbytes;
	close(osm);
    }
    pty = open(ttydev, O_RDWR);
    if (pty < 0) exit(1);
    osm = open("/dev/osm", O_RDONLY);
    if (osm < 0) exit(1);
    for (nbytes = 0; skip > 0 && nbytes >= 0; skip -= nbytes) {
	nbytes = skip;
	if (nbytes > sizeof(cbuf))
	    nbytes = sizeof(cbuf);
	nbytes = read(osm, cbuf, nbytes);
    }
    while ((nbytes = read(osm, cbuf, sizeof(cbuf))) >= 0)
      write(pty, cbuf, nbytes);
    exit(0);
  }
  signal(SIGHUP, KillChild);
  signal(SIGINT, KillChild);
  signal(SIGTERM, KillChild);
  return fdopen(tty, "r");
}
#endif  /* USE_OSM */