summaryrefslogtreecommitdiff
path: root/attach.c
diff options
context:
space:
mode:
Diffstat (limited to 'attach.c')
-rw-r--r--attach.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/attach.c b/attach.c
new file mode 100644
index 0000000..c5fb412
--- /dev/null
+++ b/attach.c
@@ -0,0 +1,250 @@
+/*
+ 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;
+}