diff options
| author | Ned T. Crigler <crigler@users.sourceforge.net> | 2001-09-28 00:50:56 +0000 |
|---|---|---|
| committer | Ned T. Crigler <crigler@users.sourceforge.net> | 2001-09-28 00:50:56 +0000 |
| commit | 6ffbd82a64d472d700d97d6266ba6dc2b6bbc162 (patch) | |
| tree | 6ac2da3d9997700b01eab0d118abb649e6795877 /master.c | |
initial import to sourceforge
Diffstat (limited to 'master.c')
| -rw-r--r-- | master.c | 327 |
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; +} |
