dtach 0.6

This commit is contained in:
Ned T. Crigler 2004-05-26 17:30:44 +00:00
parent b87318d1f3
commit abf2db3509
12 changed files with 2782 additions and 1224 deletions

View File

@ -3,7 +3,7 @@ CC = @CC@
CFLAGS = @CFLAGS@ -I. CFLAGS = @CFLAGS@ -I.
LDFLAGS = @LDFLAGS@ LDFLAGS = @LDFLAGS@
LIBS = @LIBS@ LIBS = @LIBS@
VERSION = 0.5 VERSION = @PACKAGE_VERSION@
VPATH = $(srcdir) VPATH = $(srcdir)
OBJ = attach.o master.o main.o OBJ = attach.o master.o main.o
@ -29,7 +29,7 @@ tar:
gzip -9f dtach-$(VERSION).tar gzip -9f dtach-$(VERSION).tar
rm -rf dtach-$(VERSION) rm -rf dtach-$(VERSION)
attach.o: @srcdir@/attach.c @srcdir@/detach.h config.h attach.o: @srcdir@/attach.c @srcdir@/dtach.h config.h
master.o: @srcdir@/master.c @srcdir@/detach.h config.h master.o: @srcdir@/master.c @srcdir@/dtach.h config.h
main.o: @srcdir@/main.c @srcdir@/detach.h config.h main.o: @srcdir@/main.c @srcdir@/dtach.h config.h

16
README
View File

@ -48,7 +48,7 @@ socket that dtach should use when creating or attaching to dtach sessions.
For example, let's create a new session that is running ircII. We will use For example, let's create a new session that is running ircII. We will use
/tmp/foozle as the session's socket: /tmp/foozle as the session's socket:
$ dtach -A /tmp/foozle irc RuneB irc.openprojects.net $ dtach -A /tmp/foozle irc RuneB irc.freenode.net
Here, -A tells dtach to either create a new session or attach to the Here, -A tells dtach to either create a new session or attach to the
existing session. If the session at /tmp/foozle does not exist yet, the existing session. If the session at /tmp/foozle does not exist yet, the
@ -96,6 +96,16 @@ to dtach when attaching.
5. CHANGES 5. CHANGES
The changes in version 0.6 are:
- Redraws are now handled by sending the child process a WINCH signal instead
of by sending a ^L character. This should help prevent line-oriented
programs (such as bash) from clearing the screen excessively.
- Flow control is now disabled when setting raw mode on the terminal.
- Switched to using select instead of poll.
- Changed some exits to exit succesfully instead of non-sucessfully.
- Updated my email address.
- Updated to Autoconf 2.59, renaming some files in the process.
The changes in version 0.5 are: The changes in version 0.5 are:
- Fix fd leakage. - Fix fd leakage.
- Prevent atexit from being called twice on dtach -A. - Prevent atexit from being called twice on dtach -A.
@ -119,8 +129,8 @@ The changes in version 0.3 are:
6. AUTHOR 6. AUTHOR
dtach is (C)Copyright 2001 Ned T. Crigler, and is under the GNU General dtach is (C)Copyright 2004 Ned T. Crigler, and is under the GNU General
Public License. Public License.
Comments and suggestions about dtach are welcome, and can be sent to Comments and suggestions about dtach are welcome, and can be sent to
the author at: <crigler@hell-city.org>. the author at: <crigler@users.sourceforge.net>.

View File

@ -1,6 +1,6 @@
/* /*
dtach - A simple program that emulates the detach feature of screen. dtach - A simple program that emulates the detach feature of screen.
Copyright (C) 2001 Ned T. Crigler Copyright (C) 2004 Ned T. Crigler
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,7 +16,7 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include "detach.h" #include "dtach.h"
#ifndef VDISABLE #ifndef VDISABLE
#ifdef _POSIX_VDISABLE #ifdef _POSIX_VDISABLE
@ -33,8 +33,6 @@
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 */
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"
@ -108,19 +106,15 @@ process_kbd(int s, struct packet *pkt)
/* Tell the master that we are returning. */ /* Tell the master that we are returning. */
pkt->type = MSG_ATTACH; pkt->type = MSG_ATTACH;
ioctl(0, TIOCGWINSZ, &pkt->u.ws);
write(s, pkt, sizeof(*pkt)); write(s, pkt, sizeof(*pkt));
/* The window size might have changed, and we definately want
** a redraw. We don't want to pass the suspend, though. */
win_changed = 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(0);
} }
/* 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')
@ -133,10 +127,10 @@ process_kbd(int s, struct packet *pkt)
int int
attach_main(int noerror) attach_main(int noerror)
{ {
int s;
struct pollfd polls[2];
struct packet pkt; struct packet pkt;
unsigned char buf[BUFSIZE]; unsigned char buf[BUFSIZE];
fd_set readfds;
int s;
/* 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. */
@ -165,8 +159,9 @@ attach_main(int noerror)
signal(SIGQUIT, die); signal(SIGQUIT, die);
signal(SIGWINCH, win_change); signal(SIGWINCH, win_change);
/* Set raw mode, almost. We allow flow control to work, for instance. */ /* Set raw mode. */
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_iflag &= ~(IXON|IXOFF);
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);
@ -174,41 +169,32 @@ attach_main(int noerror)
cur_term.c_cc[VLNEXT] = VDISABLE; cur_term.c_cc[VLNEXT] = VDISABLE;
cur_term.c_cc[VMIN] = 1; cur_term.c_cc[VMIN] = 1;
cur_term.c_cc[VTIME] = 0; 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 */ /* Tell the master that we want to attach. */
polls[0].fd = 0; pkt.type = MSG_ATTACH;
polls[0].events = POLLIN;
polls[0].revents = 0;
polls[1].fd = s;
polls[1].events = POLLIN;
polls[1].revents = 0;
/* Send our window size. */
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. */
pkt.type = MSG_REDRAW;
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) FD_ZERO(&readfds);
FD_SET(0, &readfds);
FD_SET(s, &readfds);
if (select(s + 1, &readfds, NULL, NULL, NULL) < 0)
{ {
if (errno != EINTR && errno != EAGAIN) if (errno != EINTR && errno != EAGAIN)
{ {
printf(EOS "\r\n[poll failed]\r\n"); printf(EOS "\r\n[select failed]\r\n");
exit(1); exit(1);
} }
} }
/* Pty activity */ /* Pty activity */
if (polls[1].revents != 0) if (FD_ISSET(s, &readfds))
{ {
int len = read(s, buf, sizeof(buf)); int len = read(s, buf, sizeof(buf));
@ -216,7 +202,7 @@ attach_main(int noerror)
{ {
printf(EOS "\r\n[EOF - dtach terminating]" printf(EOS "\r\n[EOF - dtach terminating]"
"\r\n"); "\r\n");
exit(1); exit(0);
} }
else if (len < 0) else if (len < 0)
{ {
@ -227,7 +213,7 @@ attach_main(int noerror)
write(1, buf, len); write(1, buf, len);
} }
/* stdin activity */ /* stdin activity */
if (polls[0].revents != 0) if (FD_ISSET(0, &readfds))
{ {
pkt.type = MSG_PUSH; pkt.type = MSG_PUSH;
memset(pkt.u.buf, 0, sizeof(pkt.u.buf)); memset(pkt.u.buf, 0, sizeof(pkt.u.buf));
@ -246,14 +232,6 @@ 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? */
if (want_redraw)
{
want_redraw = 0;
pkt.type = MSG_REDRAW;
write(s, &pkt, sizeof(pkt));
}
} }
return 0; return 0;
} }

View File

@ -1,88 +1,130 @@
/* config.h.in. Generated from configure.in by autoheader. */ /* config.h.in. Generated from configure.ac by autoheader. */
/* Define if you have the `forkpty' function. */ /* Define to 1 if you have the `atexit' function. */
#undef HAVE_ATEXIT
/* Define to 1 if you have the `dup2' function. */
#undef HAVE_DUP2
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the `forkpty' function. */
#undef HAVE_FORKPTY #undef HAVE_FORKPTY
/* Define if you have the `getrlimit' function. */ /* Define to 1 if you have the `grantpt' function. */
#undef HAVE_GETRLIMIT
/* Define if you have the `grantpt' function. */
#undef HAVE_GRANTPT #undef HAVE_GRANTPT
/* Define if you have the <inttypes.h> header file. */ /* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H #undef HAVE_INTTYPES_H
/* Define if you have the `socket' library (-lsocket). */ /* Define to 1 if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET #undef HAVE_LIBSOCKET
/* Define if you have the `util' library (-lutil). */ /* Define to 1 if you have the `util' library (-lutil). */
#undef HAVE_LIBUTIL #undef HAVE_LIBUTIL
/* Define if you have the <libutil.h> header file. */ /* Define to 1 if you have the <libutil.h> header file. */
#undef HAVE_LIBUTIL_H #undef HAVE_LIBUTIL_H
/* Define if you have the <memory.h> header file. */ /* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H #undef HAVE_MEMORY_H
/* Define if you have the `openpty' function. */ /* Define to 1 if you have the `memset' function. */
#undef HAVE_MEMSET
/* Define to 1 if you have the `openpty' function. */
#undef HAVE_OPENPTY #undef HAVE_OPENPTY
/* Define if you have the `ptsname' function. */ /* Define to 1 if you have the `ptsname' function. */
#undef HAVE_PTSNAME #undef HAVE_PTSNAME
/* Define if you have the <pty.h> header file. */ /* Define to 1 if you have the <pty.h> header file. */
#undef HAVE_PTY_H #undef HAVE_PTY_H
/* Define if you have the `socket' function. */ /* Define to 1 if you have the `select' function. */
#undef HAVE_SELECT
/* Define to 1 if you have the `socket' function. */
#undef HAVE_SOCKET #undef HAVE_SOCKET
/* Define if you have the <stdint.h> header file. */ /* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H #undef HAVE_STDINT_H
/* Define if you have the <stdlib.h> header file. */ /* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H #undef HAVE_STDLIB_H
/* Define if you have the `strerror' function. */ /* Define to 1 if you have the `strerror' function. */
#undef HAVE_STRERROR #undef HAVE_STRERROR
/* Define if you have the <strings.h> header file. */ /* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H #undef HAVE_STRINGS_H
/* Define if you have the <string.h> header file. */ /* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H #undef HAVE_STRING_H
/* Define if you have the <stropts.h> header file. */ /* Define to 1 if you have the <stropts.h> header file. */
#undef HAVE_STROPTS_H #undef HAVE_STROPTS_H
/* Define if you have the <sys/ioctl.h> header file. */ /* Define to 1 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 to 1 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. */ /* Define to 1 if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H #undef HAVE_SYS_STAT_H
/* Define if you have the <sys/types.h> header file. */ /* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H #undef HAVE_SYS_TYPES_H
/* Define if you have the <termios.h> header file. */ /* Define to 1 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 to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H #undef HAVE_UNISTD_H
/* Define if you have the `unlockpt' function. */ /* Define to 1 if you have the `unlockpt' function. */
#undef HAVE_UNLOCKPT #undef HAVE_UNLOCKPT
/* Define if you have the <util.h> header file. */ /* Define to 1 if you have the <util.h> header file. */
#undef HAVE_UTIL_H #undef HAVE_UTIL_H
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define as the return type of signal handlers (`int' or `void'). */ /* Define as the return type of signal handlers (`int' or `void'). */
#undef RETSIGTYPE #undef RETSIGTYPE
/* Define if you have the ANSI C header files. */ /* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS #undef STDC_HEADERS
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `int' if <sys/types.h> does not define. */ /* Define to `int' if <sys/types.h> does not define. */
#undef pid_t #undef pid_t

3460
configure vendored

File diff suppressed because it is too large Load Diff

36
configure.ac Normal file
View File

@ -0,0 +1,36 @@
# Process this file with autoconf to produce a configure script.
AC_INIT(dtach, 0.6, crigler@users.sourceforge.net)
AC_PREREQ(2.59)
AC_CONFIG_SRCDIR(main.c)
AC_CONFIG_HEADER(config.h)
# Checks for programs.
AC_PROG_CC
AC_PROG_GCC_TRADITIONAL
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -W -Wall";
fi
# Checks for libraries.
AC_CHECK_LIB(util, openpty)
AC_CHECK_LIB(socket, socket)
# Checks for header files.
AC_CHECK_HEADERS(fcntl.h sys/select.h sys/socket.h sys/time.h)
AC_CHECK_HEADERS(sys/ioctl.h sys/resource.h pty.h termios.h util.h)
AC_CHECK_HEADERS(libutil.h stropts.h)
AC_HEADER_TIME
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_PID_T
# Checks for library functions.
AC_TYPE_SIGNAL
AC_CHECK_FUNCS(atexit dup2 memset)
AC_CHECK_FUNCS(select socket strerror)
AC_CHECK_FUNCS(openpty forkpty ptsname grantpt unlockpt)
AC_CONFIG_FILES(Makefile)
AC_OUTPUT

View File

@ -1,29 +0,0 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(main.c)
dnl Checks for programs.
AC_PROG_CC
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -W -Wall";
fi
dnl Checks for libraries.
AC_CHECK_LIB(util, openpty)
AC_CHECK_LIB(socket, socket)
dnl Checks for header files.
AC_HEADER_STDC
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.
AC_TYPE_PID_T
dnl Checks for library functions.
AC_PROG_GCC_TRADITIONAL
AC_TYPE_SIGNAL
AC_CHECK_FUNCS(socket strerror getrlimit)
AC_CHECK_FUNCS(openpty forkpty ptsname grantpt unlockpt)
AC_CONFIG_HEADER(config.h)
AC_OUTPUT(Makefile)

View File

@ -1,4 +1,4 @@
.TH dtach 1 "November 2001" "dtach 0.5" .TH dtach 1 "May 2004" "dtach 0.6"
.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
@ -119,7 +119,7 @@ way to detach from the session is then by sending the attaching process an
appropriate signal. appropriate signal.
.TP .TP
.B \-z .B \-z
Inhibits processing of the suspend key. Disables processing of the suspend key.
Normally, Normally,
.B dtach .B dtach
will suspend itself when the suspend key is pressed. With this option, the will suspend itself when the suspend key is pressed. With this option, the
@ -146,8 +146,8 @@ Processing of the suspend character is also disabled for the attach instance.
.fi .fi
.PP .PP
.SH AUTHORS .SH AUTHOR
Ned T. Crigler <crigler@hell-city.org>. Ned T. Crigler <crigler@users.sourceforge.net>.
.SH "SEE ALSO" .SH "SEE ALSO"
.BR screen "(1)" .BR screen "(1)"

View File

@ -1,6 +1,6 @@
/* /*
dtach - A simple program that emulates the detach feature of screen. dtach - A simple program that emulates the detach feature of screen.
Copyright (C) 2001 Ned T. Crigler Copyright (C) 2001, 2004 Ned T. Crigler
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,10 +16,10 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifndef detach_h #ifndef dtach_h
#define detach_h #define dtach_h
#include "config.h" #include <config.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -28,6 +28,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#ifdef HAVE_PTY_H #ifdef HAVE_PTY_H
#include <pty.h> #include <pty.h>
#endif #endif
@ -56,9 +67,8 @@
#include <sys/resource.h> #include <sys/resource.h>
#endif #endif
#include <poll.h>
#include <termios.h> #include <termios.h>
#include <sys/select.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
@ -70,10 +80,9 @@ extern struct termios orig_term;
enum enum
{ {
MSG_PUSH, MSG_PUSH,
MSG_WINCH,
MSG_REDRAW,
MSG_ATTACH, MSG_ATTACH,
MSG_DETACH MSG_DETACH,
MSG_WINCH,
}; };
/* The client to master protocol. */ /* The client to master protocol. */

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.5 Version: 0.6
Release: 1 Release: 1
License: GPL License: GPL
URL: http://dtach.sourceforge.net URL: http://dtach.sourceforge.net

33
main.c
View File

@ -1,6 +1,6 @@
/* /*
dtach - A simple program that emulates the detach feature of screen. dtach - A simple program that emulates the detach feature of screen.
Copyright (C) 2001 Ned T. Crigler Copyright (C) 2004 Ned T. Crigler
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,19 +16,16 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include "detach.h" #include "dtach.h"
/* /*
** detach is a quick hack, since I wanted the detach feature of screen without ** dtach is a quick hack, since I wanted the detach feature of screen without
** all the other crud. It'll work best with full-screen applications, as it ** all the other crud. It'll work best with full-screen applications, as it
** 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 */
#define VERSION "0.5"
/* 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 " PACKAGE_VERSION "(C)Copyright 2004 Ned T. Crigler";
/* argv[0] from the program */ /* argv[0] from the program */
char *progname; char *progname;
@ -66,9 +63,9 @@ usage()
" -e <char>\tSet the detach character to <char>, defaults " " -e <char>\tSet the detach character to <char>, defaults "
"to ^\\.\n" "to ^\\.\n"
" -E\t\tDisable the detach character.\n" " -E\t\tDisable the detach character.\n"
" -z\t\tInhibit processing of the suspend key.\n" " -z\t\tDisable processing of the suspend key.\n"
"\nReport any bugs to <crigler@hell-city.org>.\n", "\nReport any bugs to <" PACKAGE_BUGREPORT ">.\n",
VERSION, __DATE__, __TIME__); PACKAGE_VERSION, __DATE__, __TIME__);
exit(0); exit(0);
} }
@ -89,7 +86,7 @@ main(int argc, char **argv)
else if (strncmp(*argv, "--version", strlen(*argv)) == 0) else if (strncmp(*argv, "--version", strlen(*argv)) == 0)
{ {
printf("dtach - version %s, compiled on %s at %s.\n", printf("dtach - version %s, compiled on %s at %s.\n",
VERSION, __DATE__, __TIME__); PACKAGE_VERSION, __DATE__, __TIME__);
return 0; return 0;
} }
@ -100,7 +97,7 @@ main(int argc, char **argv)
mode != 'A') mode != 'A')
{ {
printf("%s: Invalid mode '-%c'\n", progname, mode); printf("%s: Invalid mode '-%c'\n", progname, mode);
printf("Try '%s -?' for more information.\n", printf("Try '%s --help' for more information.\n",
progname); progname);
return 1; return 1;
} }
@ -108,7 +105,7 @@ main(int argc, char **argv)
if (!mode) if (!mode)
{ {
printf("%s: No mode was specified.\n", progname); printf("%s: No mode was specified.\n", progname);
printf("Try '%s -?' for more information.\n", printf("Try '%s --help' for more information.\n",
progname); progname);
return 1; return 1;
} }
@ -117,7 +114,7 @@ main(int argc, char **argv)
if (argc < 1) if (argc < 1)
{ {
printf("%s: No socket was specified.\n", progname); printf("%s: No socket was specified.\n", progname);
printf("Try '%s -?' for more information.\n", printf("Try '%s --help' for more information.\n",
progname); progname);
return 1; return 1;
} }
@ -141,7 +138,7 @@ main(int argc, char **argv)
{ {
printf("%s: No escape character " printf("%s: No escape character "
"specified.\n", progname); "specified.\n", progname);
printf("Try '%s -?' for more " printf("Try '%s --help' for more "
"information.\n", progname); "information.\n", progname);
return 1; return 1;
} }
@ -160,7 +157,7 @@ main(int argc, char **argv)
{ {
printf("%s: Invalid option '-%c'\n", printf("%s: Invalid option '-%c'\n",
progname, *p); progname, *p);
printf("Try '%s -?' for more information.\n", printf("Try '%s --help' for more information.\n",
progname); progname);
return 1; return 1;
} }
@ -171,7 +168,7 @@ main(int argc, char **argv)
if (mode != 'a' && argc < 1) if (mode != 'a' && argc < 1)
{ {
printf("%s: No command was specified.\n", progname); printf("%s: No command was specified.\n", progname);
printf("Try '%s -?' for more information.\n", printf("Try '%s --help' for more information.\n",
progname); progname);
return 1; return 1;
} }
@ -189,7 +186,7 @@ main(int argc, char **argv)
{ {
printf("%s: Invalid number of arguments.\n", printf("%s: Invalid number of arguments.\n",
progname); progname);
printf("Try '%s -?' for more information.\n", printf("Try '%s --help' for more information.\n",
progname); progname);
return 1; return 1;
} }

227
master.c
View File

@ -1,6 +1,6 @@
/* /*
dtach - A simple program that emulates the detach feature of screen. dtach - A simple program that emulates the detach feature of screen.
Copyright (C) 2001 Ned T. Crigler Copyright (C) 2004 Ned T. Crigler
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -16,7 +16,7 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include "detach.h" #include "dtach.h"
/* The pty struct - The pty information is stored here. */ /* The pty struct - The pty information is stored here. */
struct pty struct pty
@ -27,6 +27,8 @@ struct pty
/* File descriptor of the slave side of the pty. For broken systems. */ /* File descriptor of the slave side of the pty. For broken systems. */
int slave; int slave;
#endif #endif
/* Process id of the child. */
pid_t pid;
/* The terminal parameters of the pty. Old and new for comparision /* The terminal parameters of the pty. Old and new for comparision
** purposes. */ ** purposes. */
struct termios term; struct termios term;
@ -34,17 +36,23 @@ struct pty
struct winsize ws; struct winsize ws;
}; };
/* The poll structures */ /* A connected client */
static struct pollfd *polls; struct client
/* The number of active poll slots */ {
static int num_polls; /* The next client in the linked list. */
/* Boolean array for whether a particular connection is attached. */ struct client *next;
static int *attached; /* The previous client in the linked list. */
/* The highest file descriptor possible, as returned by getrlimit. */ struct client **pprev;
static int highest_fd; /* File descriptor of the client. */
int fd;
/* Whether or not the client is attached. */
int attached;
};
/* The number of fixed slots in the poll structures */ /* The list of connected clients. */
#define FIXED_SLOTS 2 static struct client *clients;
/* The pseudo-terminal created for the child process. */
static struct pty the_pty;
#ifndef HAVE_FORKPTY #ifndef HAVE_FORKPTY
pid_t forkpty(int *amaster, char *name, struct termios *termp, pid_t forkpty(int *amaster, char *name, struct termios *termp,
@ -67,7 +75,7 @@ die(int sig)
{ {
#ifdef BROKEN_MASTER #ifdef BROKEN_MASTER
/* Damn you Solaris! */ /* Damn you Solaris! */
close(polls[1].fd); close(the_pty.fd);
#endif #endif
return; return;
} }
@ -76,27 +84,20 @@ die(int sig)
/* Initialize the pty structure. */ /* Initialize the pty structure. */
static int static int
init_pty(struct pty *pty, char **argv) init_pty(char **argv)
{ {
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; the_pty.term = orig_term;
memset(&the_pty.ws, 0, sizeof(struct winsize));
/* Create the pty process */ /* Create the pty process */
pid = forkpty(&pty->fd, NULL, &pty->term, NULL); the_pty.pid = forkpty(&the_pty.fd, NULL, &the_pty.term, NULL);
if (pid < 0) if (the_pty.pid < 0)
return -1; return -1;
else if (pid == 0) else if (the_pty.pid == 0)
{ {
int i; /* Child.. Execute the program. */
/* Child.. Close some file descriptors and execute the
** program. */
for (i = highest_fd; i > 2; --i)
close(i);
execvp(*argv, argv); execvp(*argv, argv);
exit(127); exit(127);
} }
@ -105,8 +106,8 @@ init_pty(struct pty *pty, char **argv)
{ {
char *buf; char *buf;
buf = ptsname(pty->fd); buf = ptsname(the_pty.fd);
pty->slave = open(buf, O_RDWR|O_NOCTTY); the_pty.slave = open(buf, O_RDWR|O_NOCTTY);
} }
#endif #endif
return 0; return 0;
@ -143,16 +144,17 @@ create_socket(char *name)
return s; return s;
} }
/* Process activity on a pty - Input and terminal changes are sent out to /* Process activity on the 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()
{ {
int i, len;
unsigned char buf[BUFSIZE]; unsigned char buf[BUFSIZE];
struct client *p;
int len;
/* Read the pty activity */ /* Read the pty activity */
len = read(pty->fd, buf, sizeof(buf)); len = read(the_pty.fd, buf, sizeof(buf));
/* Error -> die */ /* Error -> die */
if (len <= 0) if (len <= 0)
@ -160,19 +162,19 @@ pty_activity(struct pty *pty)
#ifdef BROKEN_MASTER #ifdef BROKEN_MASTER
/* Get the current terminal settings. */ /* Get the current terminal settings. */
if (tcgetattr(pty->slave, &pty->term) < 0) if (tcgetattr(the_pty.slave, &the_pty.term) < 0)
exit(1); exit(1);
#else #else
/* Get the current terminal settings. */ /* Get the current terminal settings. */
if (tcgetattr(pty->fd, &pty->term) < 0) if (tcgetattr(the_pty.fd, &the_pty.term) < 0)
exit(1); exit(1);
#endif #endif
/* Send it out to the clients. */ /* Send it out to the clients. */
for (i = FIXED_SLOTS; i < num_polls; ++i) for (p = clients; p; p = p->next)
{ {
if (attached[polls[i].fd]) if (p->attached)
write(polls[i].fd, buf, len); write(p->fd, buf, len);
} }
} }
@ -181,65 +183,75 @@ static void
control_activity(int s) control_activity(int s)
{ {
int fd; int fd;
struct client *p;
/* Accept the new client and link it in. */ /* Accept the new client and link it in. */
fd = accept(s, 0, 0); fd = accept(s, NULL, NULL);
if (fd < 0) if (fd < 0)
return; return;
/* Link it in. */ /* Link it in. */
polls[num_polls].fd = fd; p = malloc(sizeof(struct client));
polls[num_polls].events = POLLIN; p->fd = fd;
polls[num_polls].revents = 0; p->attached = 0;
attached[fd] = 1; p->pprev = &clients;
++num_polls; p->next = *(p->pprev);
if (p->next)
p->next->pprev = &p->next;
*(p->pprev) = p;
} }
/* Process activity from a client. */ /* Process activity from a client. */
static void static void
client_activity(int i, struct pty *pty) client_activity(struct client *p)
{ {
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(p->fd, &pkt, sizeof(pkt));
/* Close the client on an error. */
if (len <= 0) if (len <= 0)
{ {
/* Close the socket and go bye bye */ close(p->fd);
attached[polls[i].fd]=0; if (p->next)
close(polls[i].fd); p->next->pprev = p->pprev;
memcpy(polls + i, polls + i + 1, num_polls - i); *(p->pprev) = p->next;
--num_polls; free(p);
return; return;
} }
/* Okay, check the command byte. Push out data if we need to. */ /* Push out data to the program. */
if (pkt.type == MSG_PUSH) if (pkt.type == MSG_PUSH)
write(pty->fd, pkt.u.buf, pkt.len); write(the_pty.fd, pkt.u.buf, pkt.len);
/* Window size change. */
/* When attaching, we set the window size and force a redraw by sending
** the WINCH signal to the program.
**
** XXX: Are there any programs that don't handle the WINCH signal
** properly? Full-screen programs should fully redraw themselves, and
** line-oriented programs should redraw the prompt, or do nothing.
*/
else if (pkt.type == MSG_ATTACH)
{
p->attached = 1;
if (memcmp(&the_pty.ws, &pkt.u.ws, sizeof(struct winsize)) != 0)
{
the_pty.ws = pkt.u.ws;
ioctl(the_pty.fd, TIOCSWINSZ, &the_pty.ws);
}
else
kill(-the_pty.pid, SIGWINCH);
}
else if (pkt.type == MSG_DETACH)
p->attached = 0;
/* Window size change request, without a forced redraw. */
else if (pkt.type == MSG_WINCH) else if (pkt.type == MSG_WINCH)
{ {
pty->ws = pkt.u.ws; the_pty.ws = pkt.u.ws;
ioctl(pty->fd, TIOCSWINSZ, &pty->ws); ioctl(the_pty.fd, TIOCSWINSZ, &the_pty.ws);
} }
/* Redraw request? */
else if (pkt.type == MSG_REDRAW)
{
char c = '\f';
/* Guess that ^L might work under certain conditions. */
if (((pty->term.c_lflag & (ECHO|ICANON)) == 0) &&
(pty->term.c_cc[VMIN] == 1))
{
write(pty->fd, &c, sizeof(c));
}
}
/* Attach request? */
else if (pkt.type == MSG_ATTACH)
attached[polls[i].fd] = 1;
else if (pkt.type == MSG_DETACH)
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 */
@ -247,35 +259,16 @@ client_activity(int i, struct pty *pty)
static void static void
master_process(int s, char **argv) master_process(int s, char **argv)
{ {
struct pty pty; struct client *p, *next;
int i; fd_set readfds;
int highest_fd;
#ifdef HAVE_GETRLIMIT
struct rlimit rlim;
/* Dynamically allocate structures based on the number of file
** descriptors. */
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
{
printf("%s: getrlimit: %s\n", progname, strerror(errno));
exit(1);
}
highest_fd = rlim.rlim_cur;
#else
/* We can't query the OS for the number of file descriptors, so
** we pull a number out of the air. */
highest_fd = 1024;
#endif
polls = (struct pollfd*)malloc(highest_fd * sizeof(struct pollfd));
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(argv) < 0)
{ {
printf("%s: init_pty: %s\n", progname, strerror(errno)); printf("%s: init_pty: %s\n", progname, strerror(errno));
exit(1); exit(1);
@ -296,40 +289,46 @@ master_process(int s, char **argv)
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
** is the pty, and slot 2 .. n is the connected clients. */
polls[0].fd = s;
polls[0].events = POLLIN;
polls[0].revents = 0;
polls[1].fd = pty.fd;
polls[1].events = POLLIN;
polls[1].revents = 0;
num_polls = FIXED_SLOTS;
/* Loop forever. */ /* Loop forever. */
while (1) while (1)
{ {
/* Re-initialize the file descriptor set for select. */
FD_ZERO(&readfds);
FD_SET(s, &readfds);
FD_SET(the_pty.fd, &readfds);
if (s > the_pty.fd)
highest_fd = s;
else
highest_fd = the_pty.fd;
for (p = clients; p; p = p->next)
{
FD_SET(p->fd, &readfds);
if (p->fd > highest_fd)
highest_fd = p->fd;
}
/* Wait for something to happen. */ /* Wait for something to happen. */
if (poll(polls, num_polls, -1) < 0) if (select(highest_fd + 1, &readfds, NULL, NULL, NULL) < 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 (FD_ISSET(the_pty.fd, &readfds))
pty_activity(&pty); pty_activity(&the_pty);
/* New client? */ /* New client? */
if (polls[0].revents != 0) if (FD_ISSET(s, &readfds))
control_activity(s); control_activity(s);
/* Activity on a client? */ /* Activity on a client? */
for (i = 2; i < num_polls; ++i) for (p = clients; p; p = next)
{ {
if (polls[i].revents != 0) next = p->next;
client_activity(i, &pty); if (FD_ISSET(p->fd, &readfds))
client_activity(p);
} }
} }
} }