260 lines
5.7 KiB
C
260 lines
5.7 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 sockun;
|
|
|
|
s = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
if (s < 0)
|
|
return -1;
|
|
sockun.sun_family = AF_UNIX;
|
|
strcpy(sockun.sun_path, name);
|
|
if (connect(s, (struct sockaddr*)&sockun, sizeof(sockun)) < 0)
|
|
{
|
|
close(s);
|
|
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);
|
|
printf(EOS "\r\n");
|
|
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];
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
/* 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;
|
|
cur_term.c_cc[VMIN] = 1;
|
|
cur_term.c_cc[VTIME] = 0;
|
|
|
|
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;
|
|
}
|