Portability updates thanks to sourceforge's compile farm. dtach should now

work on: FreeBSD, Debian/alpha, Debian/sparc, Debian/PPC, and Solaris.
Bump version.
This commit is contained in:
Ned T. Crigler 2001-11-05 20:02:58 +00:00
parent ba4a3a502f
commit 203193838d
11 changed files with 3271 additions and 1184 deletions

View File

@ -1,9 +1,9 @@
srcdir = @srcdir@ srcdir = @srcdir@
CC = @CC@ CC = @CC@
CFLAGS = -W -Wall -I. @CFLAGS@ CFLAGS = @CFLAGS@ -I.
LDFLAGS = @LDFLAGS@ LDFLAGS = @LDFLAGS@
LIBS = @LIBS@ LIBS = @LIBS@
VERSION = 0.3 VERSION = 0.4
VPATH = $(srcdir) VPATH = $(srcdir)
OBJ = attach.o master.o main.o OBJ = attach.o master.o main.o
@ -14,10 +14,10 @@ TARFILES = $(srcdir)/README $(srcdir)/COPYING $(srcdir)/*.in $(srcdir)/*.c \
$(srcdir)/dtach.1 $(srcdir)/dtach.1
dtach: $(OBJ) dtach: $(OBJ)
$(CC) -o $@ $(LDFLAGS) $^ $(LIBS) $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
clean: clean:
rm -f .depend dtach $(OBJ) dtach-$(VERSION).tar.gz rm -f dtach $(OBJ) dtach-$(VERSION).tar.gz
distclean: clean distclean: clean
rm -f config.h Makefile config.log config.status config.cache rm -f config.h Makefile config.log config.status config.cache
@ -29,7 +29,7 @@ tar:
gzip -9f dtach-$(VERSION).tar gzip -9f dtach-$(VERSION).tar
rm -rf dtach-$(VERSION) rm -rf dtach-$(VERSION)
.depend: attach.o: @srcdir@/attach.c @srcdir@/detach.h config.h
@$(CC) $(CFLAGS) -MM $(SRC) > $@ master.o: @srcdir@/master.c @srcdir@/detach.h config.h
main.o: @srcdir@/main.c @srcdir@/detach.h config.h
-include .depend

4
README
View File

@ -96,8 +96,10 @@ to dtach when attaching.
5. CHANGES 5. CHANGES
The changes since version 0.3 are: The changes in version 0.4 are:
- Slightly improved README and dtach.1 - Slightly improved README and dtach.1
- Portability updates thanks to sourceforge's compile farm. dtach should now
work on: FreeBSD, Debian/alpha, Debian/sparc, Debian/PPC, and Solaris.
The changes in version 0.3 are: The changes in version 0.3 are:
- Fixed a typo in dtach.1 - Fixed a typo in dtach.1

View File

@ -26,49 +26,52 @@
#endif #endif
#endif #endif
// The current terminal settings. After coming back from a suspend, we /*
// restore this. ** The current terminal settings. After coming back from a suspend, we
** restore this.
*/
static struct termios cur_term; static struct termios cur_term;
// 1 if the window size changed /* 1 if the window size changed */
static int win_changed; static int win_changed;
// 1 if we want a redraw /* 1 if we want a redraw */
static int want_redraw; static int want_redraw;
// This hopefully moves to the bottom of the screen /* This hopefully moves to the bottom of the screen */
#define EOS "\033[999H" #define EOS "\033[999H"
// Restores the original terminal settings. /* Restores the original terminal settings. */
static void static void
restore_term(void) restore_term(void)
{ {
tcsetattr(0, TCSADRAIN, &orig_term); tcsetattr(0, TCSADRAIN, &orig_term);
// Make cursor visible. Assumes VT100.
/* Make cursor visible. Assumes VT100. */
printf("\033[?25h\033[?0c"); printf("\033[?25h\033[?0c");
fflush(stdout); fflush(stdout);
} }
// Connects to a unix domain socket /* Connects to a unix domain socket */
static int static int
connect_socket(char *name) connect_socket(char *name)
{ {
int s; int s;
struct sockaddr_un sun; struct sockaddr_un sockun;
s = socket(PF_UNIX, SOCK_STREAM, 0); s = socket(PF_UNIX, SOCK_STREAM, 0);
if (s < 0) if (s < 0)
return -1; return -1;
sun.sun_family = AF_UNIX; sockun.sun_family = AF_UNIX;
strcpy(sun.sun_path, name); strcpy(sockun.sun_path, name);
if (connect(s, (struct sockaddr*)&sun, sizeof(sun)) < 0) if (connect(s, (struct sockaddr*)&sockun, sizeof(sockun)) < 0)
return -1; return -1;
return s; return s;
} }
// Signal /* Signal */
static RETSIGTYPE static RETSIGTYPE
die(int sig) die(int sig)
{ {
// Print a nice pretty message for some things. /* Print a nice pretty message for some things. */
if (sig == SIGHUP || sig == SIGINT) if (sig == SIGHUP || sig == SIGINT)
printf(EOS "\r\n[detached]\r\n"); printf(EOS "\r\n[detached]\r\n");
else else
@ -76,50 +79,51 @@ die(int sig)
exit(1); exit(1);
} }
// Window size change. /* Window size change. */
static RETSIGTYPE static RETSIGTYPE
win_change() win_change()
{ {
win_changed = 1; win_changed = 1;
} }
// Handles input from the keyboard. /* Handles input from the keyboard. */
static void static void
process_kbd(int s, struct packet *pkt) process_kbd(int s, struct packet *pkt)
{ {
// Suspend? /* Suspend? */
if (!no_suspend && (pkt->u.buf[0] == cur_term.c_cc[VSUSP])) if (!no_suspend && (pkt->u.buf[0] == cur_term.c_cc[VSUSP]))
{ {
// Tell the master that we are suspending. /* Tell the master that we are suspending. */
pkt->type = MSG_DETACH; pkt->type = MSG_DETACH;
write(s, pkt, sizeof(*pkt)); write(s, pkt, sizeof(*pkt));
// And suspend... /* And suspend... */
tcsetattr(0, TCSADRAIN, &orig_term); tcsetattr(0, TCSADRAIN, &orig_term);
printf(EOS "\r\n");
kill(getpid(), SIGTSTP); kill(getpid(), SIGTSTP);
tcsetattr(0, TCSADRAIN, &cur_term); tcsetattr(0, TCSADRAIN, &cur_term);
// Tell the master that we are returning. /* Tell the master that we are returning. */
pkt->type = MSG_ATTACH; pkt->type = MSG_ATTACH;
write(s, pkt, sizeof(*pkt)); write(s, pkt, sizeof(*pkt));
// The window size might have changed, and we definately want /* The window size might have changed, and we definately want
// a redraw. We don't want to pass the suspend, though. ** a redraw. We don't want to pass the suspend, though. */
win_changed = 1; win_changed = 1;
want_redraw = 1; want_redraw = 1;
return; return;
} }
// Detach char? /* Detach char? */
else if (pkt->u.buf[0] == detach_char) else if (pkt->u.buf[0] == detach_char)
{ {
printf(EOS "\r\n[detached]\r\n"); printf(EOS "\r\n[detached]\r\n");
exit(1); exit(1);
} }
// Just in case something pukes out. /* Just in case something pukes out. */
else if (pkt->u.buf[0] == '\f') else if (pkt->u.buf[0] == '\f')
win_changed = 1; win_changed = 1;
// Push it out /* Push it out */
write(s, pkt, sizeof(*pkt)); write(s, pkt, sizeof(*pkt));
} }
@ -131,14 +135,14 @@ attach_main(int noerror)
struct packet pkt; struct packet pkt;
unsigned char buf[BUFSIZE]; unsigned char buf[BUFSIZE];
// The current terminal settings are equal to the original terminal /* The current terminal settings are equal to the original terminal
// settings at this point. ** settings at this point. */
cur_term = orig_term; cur_term = orig_term;
// Set a trap to restore the terminal when we die. /* Set a trap to restore the terminal when we die. */
atexit(restore_term); atexit(restore_term);
// Set some signals. /* Set some signals. */
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
signal(SIGXFSZ, SIG_IGN); signal(SIGXFSZ, SIG_IGN);
signal(SIGHUP, die); signal(SIGHUP, die);
@ -147,8 +151,8 @@ attach_main(int noerror)
signal(SIGQUIT, die); signal(SIGQUIT, die);
signal(SIGWINCH, win_change); signal(SIGWINCH, win_change);
// Attempt to open the socket. Don't display an error if noerror is /* Attempt to open the socket. Don't display an error if noerror is
// set. ** set. */
s = connect_socket(sockname); s = connect_socket(sockname);
if (s < 0) if (s < 0)
{ {
@ -158,20 +162,22 @@ attach_main(int noerror)
return 1; return 1;
} }
// Set raw mode, almost. We allow flow control to work, for instance. /* Set raw mode, almost. We allow flow control to work, for instance. */
cur_term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); cur_term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
cur_term.c_oflag &= ~(OPOST); cur_term.c_oflag &= ~(OPOST);
cur_term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); cur_term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
cur_term.c_cflag &= ~(CSIZE|PARENB); cur_term.c_cflag &= ~(CSIZE|PARENB);
cur_term.c_cflag |= CS8; cur_term.c_cflag |= CS8;
cur_term.c_cc[VLNEXT] = VDISABLE; cur_term.c_cc[VLNEXT] = VDISABLE;
cur_term.c_cc[VMIN] = 1;
cur_term.c_cc[VTIME] = 0;
tcsetattr(0, TCSADRAIN, &cur_term); tcsetattr(0, TCSADRAIN, &cur_term);
// Clear the screen. This assumes VT100. /* Clear the screen. This assumes VT100. */
write(1, "\33[H\33[J", 6); write(1, "\33[H\33[J", 6);
// Set up the poll structures /* Set up the poll structures */
polls[0].fd = 0; polls[0].fd = 0;
polls[0].events = POLLIN; polls[0].events = POLLIN;
polls[0].revents = 0; polls[0].revents = 0;
@ -179,15 +185,15 @@ attach_main(int noerror)
polls[1].events = POLLIN; polls[1].events = POLLIN;
polls[1].revents = 0; polls[1].revents = 0;
// Send our window size. /* Send our window size. */
pkt.type = MSG_WINCH; pkt.type = MSG_WINCH;
ioctl(0, TIOCGWINSZ, &pkt.u.ws); ioctl(0, TIOCGWINSZ, &pkt.u.ws);
write(s, &pkt, sizeof(pkt)); write(s, &pkt, sizeof(pkt));
// We would like a redraw, too. /* We would like a redraw, too. */
pkt.type = MSG_REDRAW; pkt.type = MSG_REDRAW;
write(s, &pkt, sizeof(pkt)); write(s, &pkt, sizeof(pkt));
// Wait for things to happen /* Wait for things to happen */
while (1) while (1)
{ {
if (poll(polls, 2, -1) < 0) if (poll(polls, 2, -1) < 0)
@ -198,7 +204,7 @@ attach_main(int noerror)
exit(1); exit(1);
} }
} }
// Pty activity /* Pty activity */
if (polls[1].revents != 0) if (polls[1].revents != 0)
{ {
int len = read(s, buf, sizeof(buf)); int len = read(s, buf, sizeof(buf));
@ -214,10 +220,10 @@ attach_main(int noerror)
printf(EOS "\r\n[read returned an error]\r\n"); printf(EOS "\r\n[read returned an error]\r\n");
exit(1); exit(1);
} }
// Send the data to the terminal. /* Send the data to the terminal. */
write(1, buf, len); write(1, buf, len);
} }
// stdin activity /* stdin activity */
if (polls[0].revents != 0) if (polls[0].revents != 0)
{ {
pkt.type = MSG_PUSH; pkt.type = MSG_PUSH;
@ -228,7 +234,7 @@ attach_main(int noerror)
exit(1); exit(1);
process_kbd(s, &pkt); process_kbd(s, &pkt);
} }
// Window size changed? /* Window size changed? */
if (win_changed) if (win_changed)
{ {
win_changed = 0; win_changed = 0;
@ -237,7 +243,7 @@ attach_main(int noerror)
ioctl(0, TIOCGWINSZ, &pkt.u.ws); ioctl(0, TIOCGWINSZ, &pkt.u.ws);
write(s, &pkt, sizeof(pkt)); write(s, &pkt, sizeof(pkt));
} }
// Want a redraw? /* Want a redraw? */
if (want_redraw) if (want_redraw)
{ {
want_redraw = 0; want_redraw = 0;

View File

@ -1,4 +1,4 @@
/* config.h.in. Generated automatically from configure.in by autoheader. */ /* config.h.in. Generated from configure.in by autoheader. */
/* Define if you have the `forkpty' function. */ /* Define if you have the `forkpty' function. */
#undef HAVE_FORKPTY #undef HAVE_FORKPTY
@ -6,30 +6,75 @@
/* Define if you have the `getrlimit' function. */ /* Define if you have the `getrlimit' function. */
#undef HAVE_GETRLIMIT #undef HAVE_GETRLIMIT
/* Define if you have the `grantpt' function. */
#undef HAVE_GRANTPT
/* Define if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
/* Define if you have the `util' library (-lutil). */ /* Define if you have the `util' library (-lutil). */
#undef HAVE_LIBUTIL #undef HAVE_LIBUTIL
/* Define if you have the <libutil.h> header file. */
#undef HAVE_LIBUTIL_H
/* Define if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define if you have the `openpty' function. */
#undef HAVE_OPENPTY
/* Define if you have the `ptsname' function. */
#undef HAVE_PTSNAME
/* Define if you have the <pty.h> header file. */ /* Define if you have the <pty.h> header file. */
#undef HAVE_PTY_H #undef HAVE_PTY_H
/* Define if you have the `socket' function. */ /* Define if you have the `socket' function. */
#undef HAVE_SOCKET #undef HAVE_SOCKET
/* Define if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define if you have the `strerror' function. */ /* Define if you have the `strerror' function. */
#undef HAVE_STRERROR #undef HAVE_STRERROR
/* Define if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define if you have the <stropts.h> header file. */
#undef HAVE_STROPTS_H
/* Define if you have the <sys/ioctl.h> header file. */ /* Define if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H #undef HAVE_SYS_IOCTL_H
/* Define if you have the <sys/resource.h> header file. */ /* Define if you have the <sys/resource.h> header file. */
#undef HAVE_SYS_RESOURCE_H #undef HAVE_SYS_RESOURCE_H
/* Define if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define if you have the <termios.h> header file. */ /* Define if you have the <termios.h> header file. */
#undef HAVE_TERMIOS_H #undef HAVE_TERMIOS_H
/* Define if you have the <unistd.h> header file. */ /* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H #undef HAVE_UNISTD_H
/* Define if you have the `unlockpt' function. */
#undef HAVE_UNLOCKPT
/* Define if you have the <util.h> header file. */ /* Define if you have the <util.h> header file. */
#undef HAVE_UTIL_H #undef HAVE_UTIL_H

3959
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -4,12 +4,18 @@ AC_INIT(main.c)
dnl Checks for programs. dnl Checks for programs.
AC_PROG_CC AC_PROG_CC
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -W -Wall";
fi
dnl Checks for libraries. dnl Checks for libraries.
AC_CHECK_LIB(util, forkpty) AC_CHECK_LIB(util, openpty)
AC_CHECK_LIB(socket, socket)
dnl Checks for header files. dnl Checks for header files.
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS(sys/ioctl.h sys/resource.h pty.h termios.h util.h unistd.h) AC_CHECK_HEADERS(sys/ioctl.h sys/resource.h pty.h termios.h util.h unistd.h)
AC_CHECK_HEADERS(libutil.h stropts.h)
dnl Checks for typedefs, structures, and compiler characteristics. dnl Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_PID_T AC_TYPE_PID_T
@ -17,7 +23,7 @@ AC_TYPE_PID_T
dnl Checks for library functions. dnl Checks for library functions.
AC_PROG_GCC_TRADITIONAL AC_PROG_GCC_TRADITIONAL
AC_TYPE_SIGNAL AC_TYPE_SIGNAL
AC_CHECK_FUNCS(socket strerror forkpty getrlimit) AC_CHECK_FUNCS(socket strerror getrlimit)
AC_CHECK_FUNCS(openpty forkpty ptsname grantpt unlockpt)
AC_CONFIG_HEADER(config.h) AC_CONFIG_HEADER(config.h)
AC_OUTPUT(Makefile) AC_OUTPUT(Makefile)

View File

@ -22,6 +22,7 @@
#include "config.h" #include "config.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -35,6 +36,14 @@
#include <util.h> #include <util.h>
#endif #endif
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
#endif
#ifdef HAVE_STROPTS_H
#include <stropts.h>
#endif
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
@ -67,7 +76,7 @@ enum
MSG_DETACH MSG_DETACH
}; };
// The client to master protocol. /* The client to master protocol. */
struct packet struct packet
{ {
unsigned char type; unsigned char type;
@ -79,13 +88,19 @@ struct packet
} u; } u;
}; };
// The master sends a simple stream of text to the attaching clients, without /*
// any protocol. This might change back to the packet based protocol in the ** The master sends a simple stream of text to the attaching clients, without
// future. In the meantime, however, we minimize the amount of data sent back ** any protocol. This might change back to the packet based protocol in the
// and forth between the client and the master. BUFSIZE is the size of the ** future. In the meantime, however, we minimize the amount of data sent back
// buffer used for the text stream. ** and forth between the client and the master. BUFSIZE is the size of the
** buffer used for the text stream.
*/
#define BUFSIZE 4096 #define BUFSIZE 4096
int attach_main(int noerror); int attach_main(int noerror);
int master_main(char **argv); int master_main(char **argv);
#ifdef sun
#define BROKEN_MASTER
#endif
#endif #endif

View File

@ -1,4 +1,4 @@
.TH dtach 1 "September 2001" "dtach 0.3" .TH dtach 1 "September 2001" "dtach 0.4"
.SH NAME .SH NAME
dtach \- simple program that emulates the detach feature of screen. dtach \- simple program that emulates the detach feature of screen.
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -1,6 +1,6 @@
Summary: A simple program that emulates the detach feature of screen. Summary: A simple program that emulates the detach feature of screen.
Name: dtach Name: dtach
Version: 0.3 Version: 0.4
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
URL: http://dtach.sourceforge.net URL: http://dtach.sourceforge.net
@ -45,6 +45,11 @@ rm -rf $RPM_BUILD_DIR/%{name}-%{version}
%{_mandir}/man1/* %{_mandir}/man1/*
%changelog %changelog
* Sat Nov 03 2001 Ned T. Crigler <crigler@hell-city.org> 0.4
- Portability updates thanks to sourceforge's compile farm. dtach should now
work on: FreeBSD, Debian/alpha, Debian/PPC, Debian/sparc, Debian/PPC, and
Solaris.
* Thu Sep 27 2001 Ned T. Crigler <crigler@hell-city.org> * Thu Sep 27 2001 Ned T. Crigler <crigler@hell-city.org>
- Modified spec file URL: to point to http://dtach.sourceforge.net - Modified spec file URL: to point to http://dtach.sourceforge.net

32
main.c
View File

@ -24,24 +24,26 @@
** does not keep track of the screen or anything like that. ** does not keep track of the screen or anything like that.
*/ */
// The program version /* The program version */
#define VERSION "0.3" #define VERSION "0.4"
// Make sure the binary has a copyright. /* Make sure the binary has a copyright. */
const char copyright[] = "dtach - version " VERSION " (C)Copyright 2001 Ned T. Crigler"; const char copyright[] = "dtach - version " VERSION " (C)Copyright 2001 Ned T. Crigler";
// argv[0] from the program /* argv[0] from the program */
char *progname; char *progname;
// The name of the passed in socket. /* The name of the passed in socket. */
char *sockname; char *sockname;
// The character used for detaching. Defaults to '^\' /* The character used for detaching. Defaults to '^\' */
int detach_char = '\\' - 64; int detach_char = '\\' - 64;
// 1 if we should not interpret the suspend character. /* 1 if we should not interpret the suspend character. */
int no_suspend; int no_suspend;
// The original terminal settings. Shared between the master and attach /*
// processes. The master uses it to initialize the pty, and the attacher uses ** The original terminal settings. Shared between the master and attach
// it to restore the original settings. ** processes. The master uses it to initialize the pty, and the attacher uses
** it to restore the original settings.
*/
struct termios orig_term; struct termios orig_term;
static void static void
@ -75,11 +77,11 @@ main(int argc, char **argv)
{ {
int mode = 0; int mode = 0;
// Save the program name /* Save the program name */
progname = argv[0]; progname = argv[0];
++argv; --argc; ++argv; --argc;
// Parse the arguments /* Parse the arguments */
if (argc >= 1 && **argv == '-') if (argc >= 1 && **argv == '-')
{ {
if (strncmp(*argv, "--help", strlen(*argv)) == 0) if (strncmp(*argv, "--help", strlen(*argv)) == 0)
@ -174,7 +176,7 @@ main(int argc, char **argv)
return 1; return 1;
} }
// Save the original terminal settings. /* Save the original terminal settings. */
if (tcgetattr(0, &orig_term) < 0) if (tcgetattr(0, &orig_term) < 0)
{ {
printf("%s: tcgetattr: %s\n", progname, strerror(errno)); printf("%s: tcgetattr: %s\n", progname, strerror(errno));
@ -203,8 +205,8 @@ main(int argc, char **argv)
} }
else if (mode == 'A') else if (mode == 'A')
{ {
// Try to attach first. If that doesn't work, create a new /* Try to attach first. If that doesn't work, create a new
// socket. ** socket. */
if (attach_main(1) != 0) if (attach_main(1) != 0)
{ {
if (master_main(argv) != 0) if (master_main(argv) != 0)

261
master.c
View File

@ -18,58 +18,73 @@
*/ */
#include "detach.h" #include "detach.h"
// The pty struct - The pty information is stored here. /* The pty struct - The pty information is stored here. */
struct pty struct pty
{ {
// File descriptor of the pty /* File descriptor of the pty */
int fd; int fd;
// The terminal parameters of the pty. Old and new for comparision #ifdef BROKEN_MASTER
// purposes. /* File descriptor of the slave side of the pty. For broken systems. */
int slave;
#endif
/* The terminal parameters of the pty. Old and new for comparision
** purposes. */
struct termios term; struct termios term;
// The current window size of the pty. /* The current window size of the pty. */
struct winsize ws; struct winsize ws;
}; };
// The poll structures /* The poll structures */
static struct pollfd *polls; static struct pollfd *polls;
// The number of active poll slots /* The number of active poll slots */
static int num_polls; static int num_polls;
// Boolean array for whether a particular connection is attached. /* Boolean array for whether a particular connection is attached. */
static int *attached; static int *attached;
// The highest file descriptor possible, as returned by getrlimit. /* The highest file descriptor possible, as returned by getrlimit. */
static int highest_fd; static int highest_fd;
// The number of fixed slots in the poll structures /* The number of fixed slots in the poll structures */
#define FIXED_SLOTS 2 #define FIXED_SLOTS 2
// Unlink the socket #ifndef HAVE_FORKPTY
pid_t forkpty(int *amaster, char *name, struct termios *termp,
struct winsize *winp);
#endif
/* Unlink the socket */
static void static void
unlink_socket(void) unlink_socket(void)
{ {
unlink(sockname); unlink(sockname);
} }
// Signal /* Signal */
static RETSIGTYPE static RETSIGTYPE
die(int sig) die(int sig)
{ {
// Well, the child died. /* Well, the child died. */
if (sig == SIGCHLD) if (sig == SIGCHLD)
{
#ifdef BROKEN_MASTER
/* Damn you Solaris! */
close(polls[1].fd);
#endif
return; return;
}
exit(1); exit(1);
} }
// Initialize the pty structure. /* Initialize the pty structure. */
static int static int
init_pty(struct pty *pty, char **argv) init_pty(struct pty *pty, char **argv)
{ {
pid_t pid; pid_t pid;
// Use the original terminal's settings. We don't have to set the /* Use the original terminal's settings. We don't have to set the
// window size here, because the attacher will send it in a packet. ** window size here, because the attacher will send it in a packet. */
pty->term = orig_term; pty->term = orig_term;
// Create the pty process /* Create the pty process */
pid = forkpty(&pty->fd, NULL, &pty->term, NULL); pid = forkpty(&pty->fd, NULL, &pty->term, NULL);
if (pid < 0) if (pid < 0)
return -1; return -1;
@ -77,59 +92,74 @@ init_pty(struct pty *pty, char **argv)
{ {
int i; int i;
// Child.. Close some file descriptors and execute the program. /* Child.. Close some file descriptors and execute the
** program. */
for (i = highest_fd; i > 2; --i) for (i = highest_fd; i > 2; --i)
close(i); close(i);
execvp(*argv, argv); execvp(*argv, argv);
exit(127); exit(127);
} }
// Parent.. Finish up and return /* Parent.. Finish up and return */
#ifdef BROKEN_MASTER
{
char *buf;
buf = ptsname(pty->fd);
pty->slave = open(buf, O_RDWR|O_NOCTTY);
}
#endif
return 0; return 0;
} }
// Creates a new unix domain socket. /* Creates a new unix domain socket. */
static int static int
create_socket(char *name) create_socket(char *name)
{ {
int s; int s;
struct sockaddr_un sun; struct sockaddr_un sockun;
s = socket(PF_UNIX, SOCK_STREAM, 0); s = socket(PF_UNIX, SOCK_STREAM, 0);
if (s < 0) if (s < 0)
return -1; return -1;
sun.sun_family = AF_UNIX; sockun.sun_family = AF_UNIX;
strcpy(sun.sun_path, name); strcpy(sockun.sun_path, name);
if (bind(s, (struct sockaddr*)&sun, sizeof(sun)) < 0) if (bind(s, (struct sockaddr*)&sockun, sizeof(sockun)) < 0)
return -1; return -1;
if (listen(s, 128) < 0) if (listen(s, 128) < 0)
return -1; return -1;
// chmod it to prevent any suprises /* chmod it to prevent any suprises */
if (chmod(name, 0600) < 0) if (chmod(name, 0600) < 0)
return -1; return -1;
return s; return s;
} }
// Process activity on a pty - Input and terminal changes are sent out to /* Process activity on a pty - Input and terminal changes are sent out to
// the attached clients. If the pty goes away, we die. ** the attached clients. If the pty goes away, we die. */
static void static void
pty_activity(struct pty *pty) pty_activity(struct pty *pty)
{ {
int i, len; int i, len;
unsigned char buf[BUFSIZE]; unsigned char buf[BUFSIZE];
// Read the pty activity /* Read the pty activity */
len = read(pty->fd, buf, sizeof(buf)); len = read(pty->fd, buf, sizeof(buf));
// Error -> die /* Error -> die */
if (len <= 0) if (len <= 0)
exit(1); exit(1);
// Get the current terminal settings. #ifdef BROKEN_MASTER
/* Get the current terminal settings. */
if (tcgetattr(pty->slave, &pty->term) < 0)
exit(1);
#else
/* Get the current terminal settings. */
if (tcgetattr(pty->fd, &pty->term) < 0) if (tcgetattr(pty->fd, &pty->term) < 0)
exit(1); exit(1);
#endif
// Send it out to the clients. /* Send it out to the clients. */
for (i = FIXED_SLOTS; i < num_polls; ++i) for (i = FIXED_SLOTS; i < num_polls; ++i)
{ {
if (attached[polls[i].fd]) if (attached[polls[i].fd])
@ -137,18 +167,18 @@ pty_activity(struct pty *pty)
} }
} }
// Process activity on the control socket /* Process activity on the control socket */
static void static void
control_activity(int s) control_activity(int s)
{ {
int fd; int fd;
// Accept the new client and link it in. /* Accept the new client and link it in. */
fd = accept(s, 0, 0); fd = accept(s, 0, 0);
if (fd < 0) if (fd < 0)
return; return;
// Link it in. /* Link it in. */
polls[num_polls].fd = fd; polls[num_polls].fd = fd;
polls[num_polls].events = POLLIN; polls[num_polls].events = POLLIN;
polls[num_polls].revents = 0; polls[num_polls].revents = 0;
@ -156,18 +186,18 @@ control_activity(int s)
++num_polls; ++num_polls;
} }
// Process activity from a client. /* Process activity from a client. */
static void static void
client_activity(int i, struct pty *pty) client_activity(int i, struct pty *pty)
{ {
int len; int len;
struct packet pkt; struct packet pkt;
// Read the activity. /* Read the activity. */
len = read(polls[i].fd, &pkt, sizeof(pkt)); len = read(polls[i].fd, &pkt, sizeof(pkt));
if (len <= 0) if (len <= 0)
{ {
// Close the socket and go bye bye /* Close the socket and go bye bye */
attached[polls[i].fd]=0; attached[polls[i].fd]=0;
close(polls[i].fd); close(polls[i].fd);
memcpy(polls + i, polls + i + 1, num_polls - i); memcpy(polls + i, polls + i + 1, num_polls - i);
@ -175,36 +205,36 @@ client_activity(int i, struct pty *pty)
return; return;
} }
// Okay, check the command byte. Push out data if we need to. /* Okay, check the command byte. Push out data if we need to. */
if (pkt.type == MSG_PUSH) if (pkt.type == MSG_PUSH)
write(pty->fd, pkt.u.buf, pkt.len); write(pty->fd, pkt.u.buf, pkt.len);
// Window size change. /* Window size change. */
else if (pkt.type == MSG_WINCH) else if (pkt.type == MSG_WINCH)
{ {
pty->ws = pkt.u.ws; pty->ws = pkt.u.ws;
ioctl(pty->fd, TIOCSWINSZ, &pty->ws); ioctl(pty->fd, TIOCSWINSZ, &pty->ws);
} }
// Redraw request? /* Redraw request? */
else if (pkt.type == MSG_REDRAW) else if (pkt.type == MSG_REDRAW)
{ {
char c = '\f'; char c = '\f';
// Guess that ^L might work under certain conditions. /* Guess that ^L might work under certain conditions. */
if (((pty->term.c_lflag & (ECHO|ICANON)) == 0) && if (((pty->term.c_lflag & (ECHO|ICANON)) == 0) &&
(pty->term.c_cc[VMIN] == 1)) (pty->term.c_cc[VMIN] == 1))
{ {
write(pty->fd, &c, sizeof(c)); write(pty->fd, &c, sizeof(c));
} }
} }
// Attach request? /* Attach request? */
else if (pkt.type == MSG_ATTACH) else if (pkt.type == MSG_ATTACH)
attached[polls[i].fd] = 1; attached[polls[i].fd] = 1;
else if (pkt.type == MSG_DETACH) else if (pkt.type == MSG_DETACH)
attached[polls[i].fd] = 0; attached[polls[i].fd] = 0;
} }
// The master process - It watches over the pty process and the attached /* The master process - It watches over the pty process and the attached */
// clients. /* clients. */
static void static void
master_process(int s, char **argv) master_process(int s, char **argv)
{ {
@ -214,8 +244,8 @@ master_process(int s, char **argv)
#ifdef HAVE_GETRLIMIT #ifdef HAVE_GETRLIMIT
struct rlimit rlim; struct rlimit rlim;
// Dynamically allocate structures based on the number of file /* Dynamically allocate structures based on the number of file
// descriptors. ** descriptors. */
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
{ {
@ -224,25 +254,25 @@ master_process(int s, char **argv)
} }
highest_fd = rlim.rlim_cur; highest_fd = rlim.rlim_cur;
#else #else
// We can't query the OS for the number of file descriptors, so /* We can't query the OS for the number of file descriptors, so
// we pull a number out of the air. ** we pull a number out of the air. */
highest_fd = 1024; highest_fd = 1024;
#endif #endif
polls = (struct pollfd*)malloc(highest_fd * sizeof(struct pollfd)); polls = (struct pollfd*)malloc(highest_fd * sizeof(struct pollfd));
attached = (int*)malloc(highest_fd * sizeof(int)); attached = (int*)malloc(highest_fd * sizeof(int));
// Okay, disassociate ourselves from the original terminal, as we /* Okay, disassociate ourselves from the original terminal, as we
// don't care what happens to it. ** don't care what happens to it. */
setsid(); setsid();
// Create a pty in which the process is running. /* Create a pty in which the process is running. */
if (init_pty(&pty, argv) < 0) if (init_pty(&pty, argv) < 0)
{ {
printf("%s: init_pty: %s\n", progname, strerror(errno)); printf("%s: init_pty: %s\n", progname, strerror(errno));
exit(1); exit(1);
} }
// Set up some signals. /* Set up some signals. */
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
signal(SIGXFSZ, SIG_IGN); signal(SIGXFSZ, SIG_IGN);
signal(SIGHUP, SIG_IGN); signal(SIGHUP, SIG_IGN);
@ -252,16 +282,16 @@ master_process(int s, char **argv)
signal(SIGTERM, die); signal(SIGTERM, die);
signal(SIGCHLD, die); signal(SIGCHLD, die);
// Close the original terminal. We are now a daemon. /* Close the original terminal. We are now a daemon. */
fclose(stdin); fclose(stdin);
fclose(stdout); fclose(stdout);
fclose(stderr); fclose(stderr);
// Set a trap to unlink the socket when we die /* Set a trap to unlink the socket when we die */
atexit(unlink_socket); atexit(unlink_socket);
// Set up the poll structures. Slot 0 is the control socket, slot 1 /* Set up the poll structures. Slot 0 is the control socket, slot 1
// is the pty, and slot 2 .. n is the connected clients. ** is the pty, and slot 2 .. n is the connected clients. */
polls[0].fd = s; polls[0].fd = s;
polls[0].events = POLLIN; polls[0].events = POLLIN;
polls[0].revents = 0; polls[0].revents = 0;
@ -270,23 +300,23 @@ master_process(int s, char **argv)
polls[1].revents = 0; polls[1].revents = 0;
num_polls = FIXED_SLOTS; num_polls = FIXED_SLOTS;
// Loop forever. /* Loop forever. */
while (1) while (1)
{ {
// Wait for something to happen. /* Wait for something to happen. */
if (poll(polls, num_polls, -1) < 0) if (poll(polls, num_polls, -1) < 0)
{ {
if (errno == EINTR || errno == EAGAIN) if (errno == EINTR || errno == EAGAIN)
continue; continue;
exit(1); exit(1);
} }
// pty activity? /* pty activity? */
if (polls[1].revents != 0) if (polls[1].revents != 0)
pty_activity(&pty); pty_activity(&pty);
// New client? /* New client? */
if (polls[0].revents != 0) if (polls[0].revents != 0)
control_activity(s); control_activity(s);
// Activity on a client? /* Activity on a client? */
for (i = 2; i < num_polls; ++i) for (i = 2; i < num_polls; ++i)
{ {
if (polls[i].revents != 0) if (polls[i].revents != 0)
@ -301,7 +331,7 @@ master_main(char **argv)
int s; int s;
pid_t pid; pid_t pid;
// Create the unix domain socket. /* Create the unix domain socket. */
s = create_socket(sockname); s = create_socket(sockname);
if (s < 0) if (s < 0)
{ {
@ -309,7 +339,7 @@ master_main(char **argv)
return 1; return 1;
} }
// Fork off so we can daemonize and such /* Fork off so we can daemonize and such */
pid = fork(); pid = fork();
if (pid < 0) if (pid < 0)
{ {
@ -318,10 +348,111 @@ master_main(char **argv)
} }
else if (pid == 0) else if (pid == 0)
{ {
// Child - this becomes the master /* Child - this becomes the master */
master_process(s, argv); master_process(s, argv);
return 0; return 0;
} }
// Parent - just return. /* Parent - just return. */
return 0; return 0;
} }
/* BSDish functions for systems that don't have them. */
#ifndef HAVE_OPENPTY
#define HAVE_OPENPTY
/* openpty: Use /dev/ptmx and Unix98 if we have it. */
#if defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT)
int
openpty(int *amaster, int *aslave, char *name, struct termios *termp,
struct winsize *winp)
{
int master, slave;
char *buf;
master = open("/dev/ptmx", O_RDWR);
if (master < 0)
return -1;
if (grantpt(master) < 0)
return -1;
if (unlockpt(master) < 0)
return -1;
buf = ptsname(master);
if (!buf)
return -1;
slave = open(buf, O_RDWR|O_NOCTTY);
if (slave < 0)
return -1;
#ifdef I_PUSH
if (ioctl(slave, I_PUSH, "ptem") < 0)
return -1;
if (ioctl(slave, I_PUSH, "ldterm") < 0)
return -1;
#endif
*amaster = master;
*aslave = slave;
if (name)
strcpy(name, buf);
if (termp)
tcsetattr(slave, TCSAFLUSH, termp);
if (winp)
ioctl(slave, TIOCSWINSZ, winp);
return 0;
}
#else
#error Do not know how to define openpty.
#endif
#endif
#ifndef HAVE_FORKPTY
#if defined(HAVE_OPENPTY)
pid_t
forkpty(int *amaster, char *name, struct termios *termp,
struct winsize *winp)
{
pid_t pid;
int master, slave;
if (openpty(&master, &slave, name, termp, winp) < 0)
return -1;
*amaster = master;
/* Fork off... */
pid = fork();
if (pid < 0)
return -1;
else if (pid == 0)
{
char *buf;
int fd;
setsid();
#ifdef TIOCSCTTY
buf = NULL;
if (ioctl(slave, TIOCSCTTY, NULL) < 0)
_exit(1);
#else
buf = ptsname(master);
fd = open(buf, O_RDWR);
close(fd);
#endif
dup2(slave, 0);
dup2(slave, 1);
dup2(slave, 2);
if (slave > 2)
close(slave);
close(master);
return 0;
}
else
{
close(slave);
return pid;
}
}
#else
#error Do not know how to define forkpty.
#endif
#endif