summaryrefslogtreecommitdiff
path: root/master.c
diff options
context:
space:
mode:
authorNed T. Crigler <crigler@users.sourceforge.net>2001-09-28 00:50:56 +0000
committerNed T. Crigler <crigler@users.sourceforge.net>2001-09-28 00:50:56 +0000
commit6ffbd82a64d472d700d97d6266ba6dc2b6bbc162 (patch)
tree6ac2da3d9997700b01eab0d118abb649e6795877 /master.c
initial import to sourceforge
Diffstat (limited to 'master.c')
-rw-r--r--master.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/master.c b/master.c
new file mode 100644
index 0000000..e31a6da
--- /dev/null
+++ b/master.c
@@ -0,0 +1,327 @@
+/*
+ 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"
+
+// The pty struct - The pty information is stored here.
+struct pty
+{
+ // File descriptor of the pty
+ int fd;
+ // The terminal parameters of the pty. Old and new for comparision
+ // purposes.
+ struct termios term;
+ // The current window size of the pty.
+ struct winsize ws;
+};
+
+// The poll structures
+static struct pollfd *polls;
+// The number of active poll slots
+static int num_polls;
+// Boolean array for whether a particular connection is attached.
+static int *attached;
+// The highest file descriptor possible, as returned by getrlimit.
+static int highest_fd;
+
+// The number of fixed slots in the poll structures
+#define FIXED_SLOTS 2
+
+// Unlink the socket
+static void
+unlink_socket(void)
+{
+ unlink(sockname);
+}
+
+// Signal
+static RETSIGTYPE
+die(int sig)
+{
+ // Well, the child died.
+ if (sig == SIGCHLD)
+ return;
+ exit(1);
+}
+
+// Initialize the pty structure.
+static int
+init_pty(struct pty *pty, char **argv)
+{
+ pid_t pid;
+
+ // 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.
+ pty->term = orig_term;
+
+ // Create the pty process
+ pid = forkpty(&pty->fd, NULL, &pty->term, NULL);
+ if (pid < 0)
+ return -1;
+ else if (pid == 0)
+ {
+ int i;
+
+ // Child.. Close some file descriptors and execute the program.
+ for (i = highest_fd; i > 2; --i)
+ close(i);
+
+ execvp(*argv, argv);
+ exit(127);
+ }
+ // Parent.. Finish up and return
+ return 0;
+}
+
+// Creates a new unix domain socket.
+static int
+create_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 (bind(s, (struct sockaddr*)&sun, sizeof(sun)) < 0)
+ return -1;
+ if (listen(s, 128) < 0)
+ return -1;
+ // chmod it to prevent any suprises
+ if (chmod(name, 0600) < 0)
+ return -1;
+ return s;
+}
+
+// Process activity on a pty - Input and terminal changes are sent out to
+// the attached clients. If the pty goes away, we die.
+static void
+pty_activity(struct pty *pty)
+{
+ int i, len;
+ unsigned char buf[BUFSIZE];
+
+ // Read the pty activity
+ len = read(pty->fd, buf, sizeof(buf));
+
+ // Error -> die
+ if (len <= 0)
+ exit(1);
+
+ // Get the current terminal settings.
+ if (tcgetattr(pty->fd, &pty->term) < 0)
+ exit(1);
+
+ // Send it out to the clients.
+ for (i = FIXED_SLOTS; i < num_polls; ++i)
+ {
+ if (attached[polls[i].fd])
+ write(polls[i].fd, buf, len);
+ }
+}
+
+// Process activity on the control socket
+static void
+control_activity(int s)
+{
+ int fd;
+
+ // Accept the new client and link it in.
+ fd = accept(s, 0, 0);
+ if (fd < 0)
+ return;
+
+ // Link it in.
+ polls[num_polls].fd = fd;
+ polls[num_polls].events = POLLIN;
+ polls[num_polls].revents = 0;
+ attached[fd] = 1;
+ ++num_polls;
+}
+
+// Process activity from a client.
+static void
+client_activity(int i, struct pty *pty)
+{
+ int len;
+ struct packet pkt;
+
+ // Read the activity.
+ len = read(polls[i].fd, &pkt, sizeof(pkt));
+ if (len <= 0)
+ {
+ // Close the socket and go bye bye
+ attached[polls[i].fd]=0;
+ close(polls[i].fd);
+ memcpy(polls + i, polls + i + 1, num_polls - i);
+ --num_polls;
+ return;
+ }
+
+ // Okay, check the command byte. Push out data if we need to.
+ if (pkt.type == MSG_PUSH)
+ write(pty->fd, pkt.u.buf, pkt.len);
+ // Window size change.
+ else if (pkt.type == MSG_WINCH)
+ {
+ pty->ws = pkt.u.ws;
+ ioctl(pty->fd, TIOCSWINSZ, &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
+// clients.
+static void
+master_process(int s, char **argv)
+{
+ struct pty pty;
+ int i;
+
+#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
+ // don't care what happens to it.
+ setsid();
+
+ // Create a pty in which the process is running.
+ if (init_pty(&pty, argv) < 0)
+ {
+ printf("%s: init_pty: %s\n", progname, strerror(errno));
+ exit(1);
+ }
+
+ // Set up some signals.
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGXFSZ, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGINT, die);
+ signal(SIGTERM, die);
+ signal(SIGCHLD, die);
+
+ // Close the original terminal. We are now a daemon.
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+
+ // Set a trap to unlink the socket when we die
+ 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.
+ while (1)
+ {
+ // Wait for something to happen.
+ if (poll(polls, num_polls, -1) < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ exit(1);
+ }
+ // pty activity?
+ if (polls[1].revents != 0)
+ pty_activity(&pty);
+ // New client?
+ if (polls[0].revents != 0)
+ control_activity(s);
+ // Activity on a client?
+ for (i = 2; i < num_polls; ++i)
+ {
+ if (polls[i].revents != 0)
+ client_activity(i, &pty);
+ }
+ }
+}
+
+int
+master_main(char **argv)
+{
+ int s;
+ pid_t pid;
+
+ // Create the unix domain socket.
+ s = create_socket(sockname);
+ if (s < 0)
+ {
+ printf("%s: %s: %s\n", progname, sockname, strerror(errno));
+ return 1;
+ }
+
+ // Fork off so we can daemonize and such
+ pid = fork();
+ if (pid < 0)
+ {
+ printf("%s: fork: %s\n", progname, strerror(errno));
+ return 1;
+ }
+ else if (pid == 0)
+ {
+ // Child - this becomes the master
+ master_process(s, argv);
+ return 0;
+ }
+ // Parent - just return.
+ return 0;
+}