/* xterm.c -- Definition of functions and structures for
|
/* xterm.c -- Definition of functions and structures for
|
peripheral to communicate with host through an xterm.
|
peripheral to communicate with host through an xterm.
|
Inspired from SWI-Prolog by Jan Wielemaker (GPL too)
|
Inspired from SWI-Prolog by Jan Wielemaker (GPL too)
|
even if there is really few in common.
|
even if there is really few in common.
|
|
|
Copyright (C) 2002 Richard Prescott <rip@step.polymtl.ca>
|
Copyright (C) 2002 Richard Prescott <rip@step.polymtl.ca>
|
Copyright (C) 2008 Embecosm Limited
|
Copyright (C) 2008 Embecosm Limited
|
|
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
|
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
|
|
This program is free software; you can redistribute it and/or modify it
|
This program is free software; you can redistribute it and/or modify it
|
under the terms of the GNU General Public License as published by the Free
|
under the terms of the GNU General Public License as published by the Free
|
Software Foundation; either version 3 of the License, or (at your option)
|
Software Foundation; either version 3 of the License, or (at your option)
|
any later version.
|
any later version.
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
more details.
|
more details.
|
|
|
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU General Public License along
|
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
/* This program is commented throughout in a fashion suitable for processing
|
/* This program is commented throughout in a fashion suitable for processing
|
with Doxygen. */
|
with Doxygen. */
|
|
|
|
|
/* Autoconf and/or portability configuration */
|
/* Autoconf and/or portability configuration */
|
#include "config.h"
|
#include "config.h"
|
#include "port.h"
|
#include "port.h"
|
|
|
/* System includes */
|
/* System includes */
|
#if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT) && defined(HAVE_PTSNAME)
|
#if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT) && defined(HAVE_PTSNAME)
|
#endif
|
#endif
|
|
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <unistd.h>
|
#include <unistd.h>
|
#include <signal.h>
|
#include <signal.h>
|
#include <termios.h>
|
#include <termios.h>
|
#include <errno.h>
|
#include <errno.h>
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <fcntl.h>
|
#include <fcntl.h>
|
#include <sys/types.h>
|
#include <sys/types.h>
|
#include <sys/wait.h>
|
#include <sys/wait.h>
|
|
|
#if HAVE_SYS_STROPTS_H
|
#if HAVE_SYS_STROPTS_H
|
#include <sys/stropts.h> /* I_PUSH ioctl */
|
#include <sys/stropts.h> /* I_PUSH ioctl */
|
#endif
|
#endif
|
|
|
#if HAVE_BASENAME
|
#if HAVE_BASENAME
|
#include <libgen.h> /* basename() */
|
#include <libgen.h> /* basename() */
|
#endif
|
#endif
|
|
|
/* Package includes */
|
/* Package includes */
|
#include "channel.h"
|
#include "channel.h"
|
#include "generic.h"
|
#include "generic.h"
|
#include "fd.h"
|
#include "fd.h"
|
|
|
/*! Data structure to represent the connection to the xterm */
|
/*! Data structure to represent the connection to the xterm */
|
struct xterm_channel
|
struct xterm_channel
|
{
|
{
|
struct fd_channel fds;
|
struct fd_channel fds;
|
int pid;
|
int pid;
|
char **argv;
|
char **argv;
|
};
|
};
|
|
|
/* Forward declaration of static functions */
|
/* Forward declaration of static functions */
|
static void xterm_close (void *data);
|
static void xterm_close (void *data);
|
static void *xterm_init (const char *input);
|
static void *xterm_init (const char *input);
|
static int xterm_open (void *data);
|
static int xterm_open (void *data);
|
|
|
/*! Global data structure with the xterm interface functions */
|
/*! Global data structure with the xterm interface functions */
|
struct channel_ops xterm_channel_ops = {
|
struct channel_ops xterm_channel_ops = {
|
.init = xterm_init,
|
.init = xterm_init,
|
.open = xterm_open,
|
.open = xterm_open,
|
.close = xterm_close,
|
.close = xterm_close,
|
.read = fd_read,
|
.read = fd_read,
|
.write = fd_write,
|
.write = fd_write,
|
.free = generic_free,
|
.free = generic_free,
|
};
|
};
|
|
|
|
|
#if !(HAVE_BASENAME)
|
#if !(HAVE_BASENAME)
|
static char *
|
static char *
|
basename (const char *filename)
|
basename (const char *filename)
|
{
|
{
|
char *p = strrchr (filename, '/');
|
char *p = strrchr (filename, '/');
|
|
|
return p ? p + 1 : (char *) filename;
|
return p ? p + 1 : (char *) filename;
|
}
|
}
|
#endif /* HAVE_BASENAME */
|
#endif /* HAVE_BASENAME */
|
|
|
static void
|
static void
|
xterm_close (void *data)
|
xterm_close (void *data)
|
{
|
{
|
struct xterm_channel *xt = data;
|
struct xterm_channel *xt = data;
|
|
|
if (!xt)
|
if (!xt)
|
return;
|
return;
|
|
|
if (xt->fds.fdin != -1)
|
if (xt->fds.fdin != -1)
|
close (xt->fds.fdin);
|
close (xt->fds.fdin);
|
|
|
if (xt->pid != -1)
|
if (xt->pid != -1)
|
{
|
{
|
kill (xt->pid, SIGKILL);
|
kill (xt->pid, SIGKILL);
|
waitpid (xt->pid, NULL, 0);
|
waitpid (xt->pid, NULL, 0);
|
}
|
}
|
|
|
if (xt->argv)
|
if (xt->argv)
|
free (xt->argv);
|
free (xt->argv);
|
|
|
xt->fds.fdin = -1;
|
xt->fds.fdin = -1;
|
xt->fds.fdout = -1;
|
xt->fds.fdout = -1;
|
xt->pid = -1;
|
xt->pid = -1;
|
xt->argv = NULL;
|
xt->argv = NULL;
|
|
|
}
|
}
|
|
|
#if defined(HAVE_ON_EXIT)
|
#if defined(HAVE_ON_EXIT)
|
static void
|
static void
|
xterm_exit (int i, void *data)
|
xterm_exit (int i, void *data)
|
{
|
{
|
xterm_close (data);
|
xterm_close (data);
|
}
|
}
|
#endif
|
#endif
|
|
|
#define MAX_XTERM_ARGS 100
|
#define MAX_XTERM_ARGS 100
|
static void *
|
static void *
|
xterm_init (const char *input)
|
xterm_init (const char *input)
|
{
|
{
|
struct xterm_channel *retval = malloc (sizeof (struct xterm_channel));
|
struct xterm_channel *retval = malloc (sizeof (struct xterm_channel));
|
if (retval)
|
if (retval)
|
{
|
{
|
int i;
|
int i;
|
char *arglist;
|
char *arglist;
|
|
|
retval->fds.fdin = -1;
|
retval->fds.fdin = -1;
|
retval->fds.fdout = -1;
|
retval->fds.fdout = -1;
|
retval->pid = -1;
|
retval->pid = -1;
|
|
|
#if defined(HAVE_ON_EXIT)
|
#if defined(HAVE_ON_EXIT)
|
/* reset cause exit(1), leaving an xterm opened */
|
/* reset cause exit(1), leaving an xterm opened */
|
on_exit (xterm_exit, retval);
|
on_exit (xterm_exit, retval);
|
#endif
|
#endif
|
|
|
i = 2;
|
i = 2;
|
arglist = (char *) input;
|
arglist = (char *) input;
|
retval->argv = malloc (sizeof (char *) * MAX_XTERM_ARGS);
|
retval->argv = malloc (sizeof (char *) * MAX_XTERM_ARGS);
|
if (!retval->argv)
|
if (!retval->argv)
|
{
|
{
|
free (retval);
|
free (retval);
|
return NULL;
|
return NULL;
|
}
|
}
|
/* Assume xterm arguments are separated by whitespace */
|
/* Assume xterm arguments are separated by whitespace */
|
while ((retval->argv[i++] = strtok (arglist, " \t\n")))
|
while ((retval->argv[i++] = strtok (arglist, " \t\n")))
|
{
|
{
|
arglist = NULL;
|
arglist = NULL;
|
if (i == MAX_XTERM_ARGS - 1)
|
if (i == MAX_XTERM_ARGS - 1)
|
{
|
{
|
free (retval);
|
free (retval);
|
return NULL;
|
return NULL;
|
}
|
}
|
}
|
}
|
|
|
}
|
}
|
return (void *) retval;
|
return (void *) retval;
|
}
|
}
|
|
|
|
|
|
|
static int
|
static int
|
xterm_open (void *data)
|
xterm_open (void *data)
|
{
|
{
|
#if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT) && defined(HAVE_PTSNAME)
|
#if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT) && defined(HAVE_PTSNAME)
|
struct xterm_channel *xt = data;
|
struct xterm_channel *xt = data;
|
int master, retval;
|
int master, retval;
|
char *slavename;
|
char *slavename;
|
struct termios termio;
|
struct termios termio;
|
char arg[64], *fin;
|
char arg[64], *fin;
|
|
|
if (!data)
|
if (!data)
|
{
|
{
|
errno = ENODEV;
|
errno = ENODEV;
|
return -1;
|
return -1;
|
}
|
}
|
|
|
master = open ("/dev/ptmx", O_RDWR);
|
master = open ("/dev/ptmx", O_RDWR);
|
|
|
if (master < 0)
|
if (master < 0)
|
return -1;
|
return -1;
|
|
|
grantpt (master);
|
grantpt (master);
|
unlockpt (master);
|
unlockpt (master);
|
slavename = (char *) ptsname (master);
|
slavename = (char *) ptsname (master);
|
|
|
if (!slavename)
|
if (!slavename)
|
{
|
{
|
errno = ENOTTY;
|
errno = ENOTTY;
|
goto closemastererror;
|
goto closemastererror;
|
}
|
}
|
|
|
xt->fds.fdout = xt->fds.fdin = open (slavename, O_RDWR);
|
xt->fds.fdout = xt->fds.fdin = open (slavename, O_RDWR);
|
if (xt->fds.fdout < 0)
|
if (xt->fds.fdout < 0)
|
goto closemastererror;
|
goto closemastererror;
|
|
|
#if HAVE_DECL_I_PUSH
|
#if HAVE_DECL_I_PUSH
|
/* These modules must be pushed onto the stream for some non-Linux and
|
/* These modules must be pushed onto the stream for some non-Linux and
|
non-Cygwin operating systems. */
|
non-Cygwin operating systems. */
|
retval = ioctl (xt->fds.fdin, I_PUSH, "ptem");
|
retval = ioctl (xt->fds.fdin, I_PUSH, "ptem");
|
if (retval < 0)
|
if (retval < 0)
|
goto closeslaveerror;
|
goto closeslaveerror;
|
|
|
retval = ioctl (xt->fds.fdin, I_PUSH, "ldterm");
|
retval = ioctl (xt->fds.fdin, I_PUSH, "ldterm");
|
if (retval < 0)
|
if (retval < 0)
|
goto closeslaveerror;
|
goto closeslaveerror;
|
#endif
|
#endif
|
|
|
retval = tcgetattr (xt->fds.fdin, &termio);
|
retval = tcgetattr (xt->fds.fdin, &termio);
|
if (retval < 0)
|
if (retval < 0)
|
goto closeslaveerror;
|
goto closeslaveerror;
|
termio.c_lflag &= ~ECHO;
|
termio.c_lflag &= ~ECHO;
|
retval = tcsetattr (xt->fds.fdin, TCSADRAIN, &termio);
|
retval = tcsetattr (xt->fds.fdin, TCSADRAIN, &termio);
|
if (retval < 0)
|
if (retval < 0)
|
goto closeslaveerror;
|
goto closeslaveerror;
|
|
|
xt->pid = fork ();
|
xt->pid = fork ();
|
|
|
if (xt->pid == -1)
|
if (xt->pid == -1)
|
goto closeslaveerror;
|
goto closeslaveerror;
|
|
|
if (xt->pid == 0)
|
if (xt->pid == 0)
|
{
|
{
|
/* Ctrl-C on sim still kill the xterm, grrr */
|
/* Ctrl-C on sim still kill the xterm, grrr */
|
signal (SIGINT, SIG_IGN);
|
signal (SIGINT, SIG_IGN);
|
|
|
fin = slavename + strlen (slavename) - 2;
|
fin = slavename + strlen (slavename) - 2;
|
if (strchr (fin, '/'))
|
if (strchr (fin, '/'))
|
{
|
{
|
sprintf (arg, "-S%s/%d", basename (slavename), master);
|
sprintf (arg, "-S%s/%d", basename (slavename), master);
|
}
|
}
|
else
|
else
|
{
|
{
|
sprintf (arg, "-S%c%c%d", fin[0], fin[1], master);
|
sprintf (arg, "-S%c%c%d", fin[0], fin[1], master);
|
}
|
}
|
xt->argv[0] = "xterm";
|
xt->argv[0] = "xterm";
|
xt->argv[1] = arg;
|
xt->argv[1] = arg;
|
execvp ("xterm", xt->argv);
|
execvp ("xterm", xt->argv);
|
write (master, "\n", 1);
|
if (write (master, "\n", 1) < 0) /* Don't ignore result */
|
|
{
|
|
printf ("ERROR: xterm: write failed\n");
|
|
}
|
exit (1);
|
exit (1);
|
}
|
}
|
|
|
do
|
do
|
retval = read (xt->fds.fdin, &arg, 1);
|
retval = read (xt->fds.fdin, &arg, 1);
|
while (retval >= 0 && arg[0] != '\n');
|
while (retval >= 0 && arg[0] != '\n');
|
if (retval < 0)
|
if (retval < 0)
|
goto closeslaveerror;
|
goto closeslaveerror;
|
|
|
termio.c_lflag |= ECHO;
|
termio.c_lflag |= ECHO;
|
retval = tcsetattr (xt->fds.fdin, TCSADRAIN, &termio);
|
retval = tcsetattr (xt->fds.fdin, TCSADRAIN, &termio);
|
|
|
if (retval < 0)
|
if (retval < 0)
|
goto closeslaveerror;
|
goto closeslaveerror;
|
|
|
return 0;
|
return 0;
|
|
|
closeslaveerror:
|
closeslaveerror:
|
close (xt->fds.fdin);
|
close (xt->fds.fdin);
|
|
|
closemastererror:
|
closemastererror:
|
close (master);
|
close (master);
|
xt->pid = xt->fds.fdin = xt->fds.fdout = -1;
|
xt->pid = xt->fds.fdin = xt->fds.fdout = -1;
|
return -1;
|
return -1;
|
|
|
#else
|
#else
|
/* I don't see how this stuff should be working on a system that doesn't know
|
/* I don't see how this stuff should be working on a system that doesn't know
|
grantpt(), unlockpt(), ptsname(). Mac OS X also does not have /dev/ptmx.
|
grantpt(), unlockpt(), ptsname(). Mac OS X also does not have /dev/ptmx.
|
-hpanther
|
-hpanther
|
*/
|
*/
|
return -1;
|
return -1;
|
#endif
|
#endif
|
}
|
}
|
|
|