dtach/attach.c

251 lines
5.5 KiB
C

/*
dtach - A simple program that emulates the detach feature of screen.
Copyright (C) 2001 Ned T. Crigler
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 Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "detach.h"
#ifndef VDISABLE
#ifdef _POSIX_VDISABLE
#define VDISABLE _POSIX_VDISABLE
#else
#define VDISABLE 0377
#endif
#endif
// The current terminal settings. After coming back from a suspend, we
// restore this.
static struct termios cur_term;
// 1 if the window size changed
static int win_changed;
// 1 if we want a redraw
static int want_redraw;
// This hopefully moves to the bottom of the screen
#define EOS "\033[999H"
// Restores the original terminal settings.
static void
restore_term(void)
{
tcsetattr(0, TCSADRAIN, &orig_term);
// Make cursor visible. Assumes VT100.
printf("\033[?25h\033[?0c");
fflush(stdout);
}
// Connects to a unix domain socket
static int
connect_socket(char *name)
{
int s;
struct sockaddr_un sun;
s = socket(PF_UNIX, SOCK_STREAM, 0);
if (s < 0)
return -1;
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, name);
if (connect(s, (struct sockaddr*)&sun, sizeof(sun)) < 0)
return -1;
return s;
}
// Signal
static RETSIGTYPE
die(int sig)
{
// Print a nice pretty message for some things.
if (sig == SIGHUP || sig == SIGINT)
printf(EOS "\r\n[detached]\r\n");
else
printf(EOS "\r\n[got signal %d - dying]\r\n", sig);
exit(1);
}
// Window size change.
static RETSIGTYPE
win_change()
{
win_changed = 1;
}
// Handles input from the keyboard.
static void
process_kbd(int s, struct packet *pkt)
{
// Suspend?
if (!no_suspend && (pkt->u.buf[0] == cur_term.c_cc[VSUSP]))
{
// Tell the master that we are suspending.
pkt->type = MSG_DETACH;
write(s, pkt, sizeof(*pkt));
// And suspend...
tcsetattr(0, TCSADRAIN, &orig_term);
kill(getpid(), SIGTSTP);
tcsetattr(0, TCSADRAIN, &cur_term);
// Tell the master that we are returning.
pkt->type = MSG_ATTACH;
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;
}
// Detach char?
else if (pkt->u.buf[0] == detach_char)
{
printf(EOS "\r\n[detached]\r\n");
exit(1);
}
// Just in case something pukes out.
else if (pkt->u.buf[0] == '\f')
win_changed = 1;
// Push it out
write(s, pkt, sizeof(*pkt));
}
int
attach_main(int noerror)
{
int s;
struct pollfd polls[2];
struct packet pkt;
unsigned char buf[BUFSIZE];
// The current terminal settings are equal to the original terminal
// settings at this point.
cur_term = orig_term;
// Set a trap to restore the terminal when we die.
atexit(restore_term);
// Set some signals.
signal(SIGPIPE, SIG_IGN);
signal(SIGXFSZ, SIG_IGN);
signal(SIGHUP, die);
signal(SIGTERM, die);
signal(SIGINT, die);
signal(SIGQUIT, die);
signal(SIGWINCH, win_change);
// Attempt to open the socket. Don't display an error if noerror is
// set.
s = connect_socket(sockname);
if (s < 0)
{
if (!noerror)
printf("%s: %s: %s\n", progname, sockname,
strerror(errno));
return 1;
}
// 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_oflag &= ~(OPOST);
cur_term.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
cur_term.c_cflag &= ~(CSIZE|PARENB);
cur_term.c_cflag |= CS8;
cur_term.c_cc[VLNEXT] = VDISABLE;
tcsetattr(0, TCSADRAIN, &cur_term);
// Clear the screen. This assumes VT100.
write(1, "\33[H\33[J", 6);
// Set up the poll structures
polls[0].fd = 0;
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);
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
while (1)
{
if (poll(polls, 2, -1) < 0)
{
if (errno != EINTR && errno != EAGAIN)
{
printf(EOS "\r\n[poll failed]\r\n");
exit(1);
}
}
// Pty activity
if (polls[1].revents != 0)
{
int len = read(s, buf, sizeof(buf));
if (len == 0)
{
printf(EOS "\r\n[EOF - dtach terminating]"
"\r\n");
exit(1);
}
else if (len < 0)
{
printf(EOS "\r\n[read returned an error]\r\n");
exit(1);
}
// Send the data to the terminal.
write(1, buf, len);
}
// stdin activity
if (polls[0].revents != 0)
{
pkt.type = MSG_PUSH;
memset(pkt.u.buf, 0, sizeof(pkt.u.buf));
pkt.len = read(0, pkt.u.buf, sizeof(pkt.u.buf));
if (pkt.len <= 0)
exit(1);
process_kbd(s, &pkt);
}
// Window size changed?
if (win_changed)
{
win_changed = 0;
pkt.type = MSG_WINCH;
ioctl(0, TIOCGWINSZ, &pkt.u.ws);
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;
}