mirror of
https://github.com/kaboomserver/buildpack.git
synced 2024-11-04 22:15:55 +00:00
801 lines
16 KiB
C
801 lines
16 KiB
C
|
/*
|
||
|
dtach - A simple program that emulates the detach feature of screen.
|
||
|
Copyright (C) 2004-2016 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 "dtach.h"
|
||
|
|
||
|
/* The pty struct - The pty information is stored here. */
|
||
|
struct pty
|
||
|
{
|
||
|
/* File descriptor of the pty */
|
||
|
int fd;
|
||
|
#ifdef BROKEN_MASTER
|
||
|
/* File descriptor of the slave side of the pty. For broken systems. */
|
||
|
int slave;
|
||
|
#endif
|
||
|
/* Process id of the child. */
|
||
|
pid_t pid;
|
||
|
/* 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;
|
||
|
};
|
||
|
|
||
|
/* A connected client */
|
||
|
struct client
|
||
|
{
|
||
|
/* The next client in the linked list. */
|
||
|
struct client *next;
|
||
|
/* The previous client in the linked list. */
|
||
|
struct client **pprev;
|
||
|
/* File descriptor of the client. */
|
||
|
int fd;
|
||
|
/* Whether or not the client is attached. */
|
||
|
int attached;
|
||
|
};
|
||
|
|
||
|
/* The list of connected clients. */
|
||
|
static struct client *clients;
|
||
|
/* The pseudo-terminal created for the child process. */
|
||
|
static struct pty the_pty;
|
||
|
|
||
|
#ifndef HAVE_FORKPTY
|
||
|
pid_t forkpty(int *amaster, char *name, struct termios *termp,
|
||
|
struct winsize *winp);
|
||
|
#endif
|
||
|
|
||
|
/* Unlink the socket */
|
||
|
static void
|
||
|
unlink_socket(void)
|
||
|
{
|
||
|
unlink(sockname);
|
||
|
}
|
||
|
|
||
|
/* Signal */
|
||
|
static RETSIGTYPE
|
||
|
die(int sig)
|
||
|
{
|
||
|
/* Well, the child died. */
|
||
|
if (sig == SIGCHLD)
|
||
|
{
|
||
|
#ifdef BROKEN_MASTER
|
||
|
/* Damn you Solaris! */
|
||
|
close(the_pty.fd);
|
||
|
#endif
|
||
|
return;
|
||
|
}
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* Sets a file descriptor to non-blocking mode. */
|
||
|
static int
|
||
|
setnonblocking(int fd)
|
||
|
{
|
||
|
int flags;
|
||
|
|
||
|
#if defined(O_NONBLOCK)
|
||
|
flags = fcntl(fd, F_GETFL);
|
||
|
if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
||
|
return -1;
|
||
|
return 0;
|
||
|
#elif defined(FIONBIO)
|
||
|
flags = 1;
|
||
|
if (ioctl(fd, FIONBIO, &flags) < 0)
|
||
|
return -1;
|
||
|
return 0;
|
||
|
#else
|
||
|
#warning Do not know how to set non-blocking mode.
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* Initialize the pty structure. */
|
||
|
static int
|
||
|
init_pty(char **argv, int statusfd)
|
||
|
{
|
||
|
/* 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. */
|
||
|
the_pty.term = orig_term;
|
||
|
memset(&the_pty.ws, 0, sizeof(struct winsize));
|
||
|
|
||
|
/* Create the pty process */
|
||
|
if (!dont_have_tty)
|
||
|
the_pty.pid = forkpty(&the_pty.fd, NULL, &the_pty.term, NULL);
|
||
|
else
|
||
|
the_pty.pid = forkpty(&the_pty.fd, NULL, NULL, NULL);
|
||
|
if (the_pty.pid < 0)
|
||
|
return -1;
|
||
|
else if (the_pty.pid == 0)
|
||
|
{
|
||
|
/* Child.. Execute the program. */
|
||
|
execvp(*argv, argv);
|
||
|
|
||
|
/* Report the error to statusfd if we can, or stdout if we
|
||
|
** can't. */
|
||
|
if (statusfd != -1)
|
||
|
dup2(statusfd, 1);
|
||
|
else
|
||
|
printf(EOS "\r\n");
|
||
|
|
||
|
printf("%s: could not execute %s: %s\r\n", progname,
|
||
|
*argv, strerror(errno));
|
||
|
fflush(stdout);
|
||
|
_exit(127);
|
||
|
}
|
||
|
/* Parent.. Finish up and return */
|
||
|
#ifdef BROKEN_MASTER
|
||
|
{
|
||
|
char *buf;
|
||
|
|
||
|
buf = ptsname(the_pty.fd);
|
||
|
the_pty.slave = open(buf, O_RDWR|O_NOCTTY);
|
||
|
}
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Send a signal to the slave side of a pseudo-terminal. */
|
||
|
static void
|
||
|
killpty(struct pty *pty, int sig)
|
||
|
{
|
||
|
pid_t pgrp = -1;
|
||
|
|
||
|
#ifdef TIOCSIGNAL
|
||
|
if (ioctl(pty->fd, TIOCSIGNAL, sig) >= 0)
|
||
|
return;
|
||
|
#endif
|
||
|
#ifdef TIOCSIG
|
||
|
if (ioctl(pty->fd, TIOCSIG, sig) >= 0)
|
||
|
return;
|
||
|
#endif
|
||
|
#ifdef TIOCGPGRP
|
||
|
#ifdef BROKEN_MASTER
|
||
|
if (ioctl(pty->slave, TIOCGPGRP, &pgrp) >= 0 && pgrp != -1 &&
|
||
|
kill(-pgrp, sig) >= 0)
|
||
|
return;
|
||
|
#endif
|
||
|
if (ioctl(pty->fd, TIOCGPGRP, &pgrp) >= 0 && pgrp != -1 &&
|
||
|
kill(-pgrp, sig) >= 0)
|
||
|
return;
|
||
|
#endif
|
||
|
|
||
|
/* Fallback using the child's pid. */
|
||
|
kill(-pty->pid, sig);
|
||
|
}
|
||
|
|
||
|
/* Creates a new unix domain socket. */
|
||
|
static int
|
||
|
create_socket(char *name)
|
||
|
{
|
||
|
int s;
|
||
|
struct sockaddr_un sockun;
|
||
|
|
||
|
if (strlen(name) > sizeof(sockun.sun_path) - 1)
|
||
|
{
|
||
|
errno = ENAMETOOLONG;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
s = socket(PF_UNIX, SOCK_STREAM, 0);
|
||
|
if (s < 0)
|
||
|
return -1;
|
||
|
sockun.sun_family = AF_UNIX;
|
||
|
strcpy(sockun.sun_path, name);
|
||
|
if (bind(s, (struct sockaddr*)&sockun, sizeof(sockun)) < 0)
|
||
|
{
|
||
|
close(s);
|
||
|
return -1;
|
||
|
}
|
||
|
if (listen(s, 128) < 0)
|
||
|
{
|
||
|
close(s);
|
||
|
return -1;
|
||
|
}
|
||
|
if (setnonblocking(s) < 0)
|
||
|
{
|
||
|
close(s);
|
||
|
return -1;
|
||
|
}
|
||
|
/* chmod it to prevent any suprises */
|
||
|
if (chmod(name, 0600) < 0)
|
||
|
{
|
||
|
close(s);
|
||
|
return -1;
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
/* Update the modes on the socket. */
|
||
|
static void
|
||
|
update_socket_modes(int exec)
|
||
|
{
|
||
|
struct stat st;
|
||
|
mode_t newmode;
|
||
|
|
||
|
if (stat(sockname, &st) < 0)
|
||
|
return;
|
||
|
|
||
|
if (exec)
|
||
|
newmode = st.st_mode | S_IXUSR;
|
||
|
else
|
||
|
newmode = st.st_mode & ~S_IXUSR;
|
||
|
|
||
|
if (st.st_mode != newmode)
|
||
|
chmod(sockname, newmode);
|
||
|
}
|
||
|
|
||
|
/* Process activity on the pty - Input and terminal changes are sent out to
|
||
|
** the attached clients. If the pty goes away, we die. */
|
||
|
static void
|
||
|
pty_activity(int s)
|
||
|
{
|
||
|
unsigned char buf[BUFSIZE];
|
||
|
ssize_t len;
|
||
|
struct client *p;
|
||
|
fd_set readfds, writefds;
|
||
|
int highest_fd, nclients;
|
||
|
|
||
|
/* Read the pty activity */
|
||
|
len = read(the_pty.fd, buf, sizeof(buf));
|
||
|
|
||
|
/* Error -> die */
|
||
|
if (len <= 0)
|
||
|
exit(1);
|
||
|
|
||
|
#ifdef BROKEN_MASTER
|
||
|
/* Get the current terminal settings. */
|
||
|
if (tcgetattr(the_pty.slave, &the_pty.term) < 0)
|
||
|
exit(1);
|
||
|
#else
|
||
|
/* Get the current terminal settings. */
|
||
|
if (tcgetattr(the_pty.fd, &the_pty.term) < 0)
|
||
|
exit(1);
|
||
|
#endif
|
||
|
|
||
|
top:
|
||
|
/*
|
||
|
** Wait until at least one client is writable. Also wait on the control
|
||
|
** socket in case a new client tries to connect.
|
||
|
*/
|
||
|
FD_ZERO(&readfds);
|
||
|
FD_ZERO(&writefds);
|
||
|
FD_SET(s, &readfds);
|
||
|
highest_fd = s;
|
||
|
for (p = clients, nclients = 0; p; p = p->next)
|
||
|
{
|
||
|
if (!p->attached)
|
||
|
continue;
|
||
|
FD_SET(p->fd, &writefds);
|
||
|
if (p->fd > highest_fd)
|
||
|
highest_fd = p->fd;
|
||
|
nclients++;
|
||
|
}
|
||
|
if (nclients == 0)
|
||
|
return;
|
||
|
if (select(highest_fd + 1, &readfds, &writefds, NULL, NULL) < 0)
|
||
|
return;
|
||
|
|
||
|
/* Send the data out to the clients. */
|
||
|
for (p = clients, nclients = 0; p; p = p->next)
|
||
|
{
|
||
|
ssize_t written;
|
||
|
|
||
|
if (!FD_ISSET(p->fd, &writefds))
|
||
|
continue;
|
||
|
|
||
|
written = 0;
|
||
|
while (written < len)
|
||
|
{
|
||
|
ssize_t n = write(p->fd, buf + written, len - written);
|
||
|
|
||
|
if (n > 0)
|
||
|
{
|
||
|
written += n;
|
||
|
continue;
|
||
|
}
|
||
|
else if (n < 0 && errno == EINTR)
|
||
|
continue;
|
||
|
else if (n < 0 && errno != EAGAIN)
|
||
|
nclients = -1;
|
||
|
break;
|
||
|
}
|
||
|
if (nclients != -1 && written == len)
|
||
|
nclients++;
|
||
|
}
|
||
|
|
||
|
/* Try again if nothing happened. */
|
||
|
if (!FD_ISSET(s, &readfds) && nclients == 0)
|
||
|
goto top;
|
||
|
}
|
||
|
|
||
|
/* Process activity on the control socket */
|
||
|
static void
|
||
|
control_activity(int s)
|
||
|
{
|
||
|
int fd;
|
||
|
struct client *p;
|
||
|
|
||
|
/* Accept the new client and link it in. */
|
||
|
fd = accept(s, NULL, NULL);
|
||
|
if (fd < 0)
|
||
|
return;
|
||
|
else if (setnonblocking(fd) < 0)
|
||
|
{
|
||
|
close(fd);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Link it in. */
|
||
|
p = malloc(sizeof(struct client));
|
||
|
p->fd = fd;
|
||
|
p->attached = 0;
|
||
|
p->pprev = &clients;
|
||
|
p->next = *(p->pprev);
|
||
|
if (p->next)
|
||
|
p->next->pprev = &p->next;
|
||
|
*(p->pprev) = p;
|
||
|
}
|
||
|
|
||
|
/* Process activity from a client. */
|
||
|
static void
|
||
|
client_activity(struct client *p)
|
||
|
{
|
||
|
ssize_t len;
|
||
|
struct packet pkt;
|
||
|
|
||
|
/* Read the activity. */
|
||
|
len = read(p->fd, &pkt, sizeof(struct packet));
|
||
|
if (len < 0 && (errno == EAGAIN || errno == EINTR))
|
||
|
return;
|
||
|
|
||
|
/* Close the client on an error. */
|
||
|
if (len <= 0)
|
||
|
{
|
||
|
close(p->fd);
|
||
|
if (p->next)
|
||
|
p->next->pprev = p->pprev;
|
||
|
*(p->pprev) = p->next;
|
||
|
free(p);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Push out data to the program. */
|
||
|
if (pkt.type == MSG_PUSH)
|
||
|
{
|
||
|
if (pkt.len <= sizeof(pkt.u.buf))
|
||
|
write(the_pty.fd, pkt.u.buf, pkt.len);
|
||
|
}
|
||
|
|
||
|
/* Attach or detach from the program. */
|
||
|
else if (pkt.type == MSG_ATTACH)
|
||
|
p->attached = 1;
|
||
|
else if (pkt.type == MSG_DETACH)
|
||
|
p->attached = 0;
|
||
|
|
||
|
/* Window size change request, without a forced redraw. */
|
||
|
else if (pkt.type == MSG_WINCH)
|
||
|
{
|
||
|
the_pty.ws = pkt.u.ws;
|
||
|
ioctl(the_pty.fd, TIOCSWINSZ, &the_pty.ws);
|
||
|
}
|
||
|
|
||
|
/* Force a redraw using a particular method. */
|
||
|
else if (pkt.type == MSG_REDRAW)
|
||
|
{
|
||
|
int method = pkt.len;
|
||
|
|
||
|
/* If the client didn't specify a particular method, use
|
||
|
** whatever we had on startup. */
|
||
|
if (method == REDRAW_UNSPEC)
|
||
|
method = redraw_method;
|
||
|
if (method == REDRAW_NONE)
|
||
|
return;
|
||
|
|
||
|
/* Set the window size. */
|
||
|
the_pty.ws = pkt.u.ws;
|
||
|
ioctl(the_pty.fd, TIOCSWINSZ, &the_pty.ws);
|
||
|
|
||
|
/* Send a ^L character if the terminal is in no-echo and
|
||
|
** character-at-a-time mode. */
|
||
|
if (method == REDRAW_CTRL_L)
|
||
|
{
|
||
|
char c = '\f';
|
||
|
|
||
|
if (((the_pty.term.c_lflag & (ECHO|ICANON)) == 0) &&
|
||
|
(the_pty.term.c_cc[VMIN] == 1))
|
||
|
{
|
||
|
write(the_pty.fd, &c, 1);
|
||
|
}
|
||
|
}
|
||
|
/* Send a WINCH signal to the program. */
|
||
|
else if (method == REDRAW_WINCH)
|
||
|
{
|
||
|
killpty(&the_pty, SIGWINCH);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* The master process - It watches over the pty process and the attached */
|
||
|
/* clients. */
|
||
|
static void
|
||
|
master_process(int s, char **argv, int waitattach, int statusfd)
|
||
|
{
|
||
|
struct client *p, *next;
|
||
|
fd_set readfds;
|
||
|
int highest_fd;
|
||
|
int nullfd;
|
||
|
|
||
|
int has_attached_client = 0;
|
||
|
|
||
|
/* Okay, disassociate ourselves from the original terminal, as we
|
||
|
** don't care what happens to it. */
|
||
|
setsid();
|
||
|
|
||
|
/* Set a trap to unlink the socket when we die. */
|
||
|
atexit(unlink_socket);
|
||
|
|
||
|
/* Create a pty in which the process is running. */
|
||
|
signal(SIGCHLD, die);
|
||
|
if (init_pty(argv, statusfd) < 0)
|
||
|
{
|
||
|
if (statusfd != -1)
|
||
|
dup2(statusfd, 1);
|
||
|
if (errno == ENOENT)
|
||
|
printf("%s: Could not find a pty.\n", progname);
|
||
|
else
|
||
|
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);
|
||
|
|
||
|
/* Close statusfd, since we don't need it anymore. */
|
||
|
if (statusfd != -1)
|
||
|
close(statusfd);
|
||
|
|
||
|
/* Make sure stdin/stdout/stderr point to /dev/null. We are now a
|
||
|
** daemon. */
|
||
|
nullfd = open("/dev/null", O_RDWR);
|
||
|
dup2(nullfd, 0);
|
||
|
dup2(nullfd, 1);
|
||
|
dup2(nullfd, 2);
|
||
|
if (nullfd > 2)
|
||
|
close(nullfd);
|
||
|
|
||
|
/* Loop forever. */
|
||
|
while (1)
|
||
|
{
|
||
|
int new_has_attached_client = 0;
|
||
|
|
||
|
/* Re-initialize the file descriptor set for select. */
|
||
|
FD_ZERO(&readfds);
|
||
|
FD_SET(s, &readfds);
|
||
|
highest_fd = s;
|
||
|
|
||
|
/*
|
||
|
** When waitattach is set, wait until the client attaches
|
||
|
** before trying to read from the pty.
|
||
|
*/
|
||
|
if (waitattach)
|
||
|
{
|
||
|
if (clients && clients->attached)
|
||
|
waitattach = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FD_SET(the_pty.fd, &readfds);
|
||
|
if (the_pty.fd > highest_fd)
|
||
|
highest_fd = the_pty.fd;
|
||
|
}
|
||
|
|
||
|
for (p = clients; p; p = p->next)
|
||
|
{
|
||
|
FD_SET(p->fd, &readfds);
|
||
|
if (p->fd > highest_fd)
|
||
|
highest_fd = p->fd;
|
||
|
|
||
|
if (p->attached)
|
||
|
new_has_attached_client = 1;
|
||
|
}
|
||
|
|
||
|
/* chmod the socket if necessary. */
|
||
|
if (has_attached_client != new_has_attached_client)
|
||
|
{
|
||
|
update_socket_modes(new_has_attached_client);
|
||
|
has_attached_client = new_has_attached_client;
|
||
|
}
|
||
|
|
||
|
/* Wait for something to happen. */
|
||
|
if (select(highest_fd + 1, &readfds, NULL, NULL, NULL) < 0)
|
||
|
{
|
||
|
if (errno == EINTR || errno == EAGAIN)
|
||
|
continue;
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* New client? */
|
||
|
if (FD_ISSET(s, &readfds))
|
||
|
control_activity(s);
|
||
|
/* Activity on a client? */
|
||
|
for (p = clients; p; p = next)
|
||
|
{
|
||
|
next = p->next;
|
||
|
if (FD_ISSET(p->fd, &readfds))
|
||
|
client_activity(p);
|
||
|
}
|
||
|
/* pty activity? */
|
||
|
if (FD_ISSET(the_pty.fd, &readfds))
|
||
|
pty_activity(s);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
master_main(char **argv, int waitattach, int dontfork)
|
||
|
{
|
||
|
int fd[2] = {-1, -1};
|
||
|
int s;
|
||
|
pid_t pid;
|
||
|
|
||
|
/* Use a default redraw method if one hasn't been specified yet. */
|
||
|
if (redraw_method == REDRAW_UNSPEC)
|
||
|
redraw_method = REDRAW_CTRL_L;
|
||
|
|
||
|
/* Create the unix domain socket. */
|
||
|
s = create_socket(sockname);
|
||
|
if (s < 0 && errno == ENAMETOOLONG)
|
||
|
{
|
||
|
char *slash = strrchr(sockname, '/');
|
||
|
|
||
|
/* Try to shorten the socket's path name by using chdir. */
|
||
|
if (slash)
|
||
|
{
|
||
|
int dirfd = open(".", O_RDONLY);
|
||
|
|
||
|
if (dirfd >= 0)
|
||
|
{
|
||
|
*slash = '\0';
|
||
|
if (chdir(sockname) >= 0)
|
||
|
{
|
||
|
s = create_socket(slash + 1);
|
||
|
fchdir(dirfd);
|
||
|
}
|
||
|
*slash = '/';
|
||
|
close(dirfd);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (s < 0)
|
||
|
{
|
||
|
printf("%s: %s: %s\n", progname, sockname, strerror(errno));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
#if defined(F_SETFD) && defined(FD_CLOEXEC)
|
||
|
fcntl(s, F_SETFD, FD_CLOEXEC);
|
||
|
|
||
|
/* If FD_CLOEXEC works, create a pipe and use it to report any errors
|
||
|
** that occur while trying to execute the program. */
|
||
|
if (dontfork)
|
||
|
{
|
||
|
fd[1] = dup(2);
|
||
|
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0)
|
||
|
{
|
||
|
close(fd[1]);
|
||
|
fd[1] = -1;
|
||
|
}
|
||
|
}
|
||
|
else if (pipe(fd) >= 0)
|
||
|
{
|
||
|
if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0 ||
|
||
|
fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0)
|
||
|
{
|
||
|
close(fd[0]);
|
||
|
close(fd[1]);
|
||
|
fd[0] = fd[1] = -1;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (dontfork)
|
||
|
{
|
||
|
master_process(s, argv, waitattach, fd[1]);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Fork off so we can daemonize and such */
|
||
|
pid = fork();
|
||
|
if (pid < 0)
|
||
|
{
|
||
|
printf("%s: fork: %s\n", progname, strerror(errno));
|
||
|
unlink_socket();
|
||
|
return 1;
|
||
|
}
|
||
|
else if (pid == 0)
|
||
|
{
|
||
|
/* Child - this becomes the master */
|
||
|
if (fd[0] != -1)
|
||
|
close(fd[0]);
|
||
|
master_process(s, argv, waitattach, fd[1]);
|
||
|
return 0;
|
||
|
}
|
||
|
/* Parent - just return. */
|
||
|
|
||
|
#if defined(F_SETFD) && defined(FD_CLOEXEC)
|
||
|
/* Check if an error occurred while trying to execute the program. */
|
||
|
if (fd[0] != -1)
|
||
|
{
|
||
|
char buf[1024];
|
||
|
ssize_t len;
|
||
|
|
||
|
close(fd[1]);
|
||
|
len = read(fd[0], buf, sizeof(buf));
|
||
|
if (len > 0)
|
||
|
{
|
||
|
write(2, buf, len);
|
||
|
kill(pid, SIGTERM);
|
||
|
return 1;
|
||
|
}
|
||
|
close(fd[0]);
|
||
|
}
|
||
|
#endif
|
||
|
close(s);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* BSDish functions for systems that don't have them. */
|
||
|
#ifndef HAVE_OPENPTY
|
||
|
#define HAVE_OPENPTY
|
||
|
/* openpty: Use /dev/ptmx and Unix98 if we have it. */
|
||
|
#if defined(HAVE_PTSNAME) && defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT)
|
||
|
int
|
||
|
openpty(int *amaster, int *aslave, char *name, struct termios *termp,
|
||
|
struct winsize *winp)
|
||
|
{
|
||
|
int master, slave;
|
||
|
char *buf;
|
||
|
|
||
|
#ifdef _AIX
|
||
|
master = open("/dev/ptc", O_RDWR|O_NOCTTY);
|
||
|
if (master < 0)
|
||
|
return -1;
|
||
|
buf = ttyname(master);
|
||
|
if (!buf)
|
||
|
return -1;
|
||
|
|
||
|
slave = open(buf, O_RDWR|O_NOCTTY);
|
||
|
if (slave < 0)
|
||
|
return -1;
|
||
|
#else
|
||
|
master = open("/dev/ptmx", O_RDWR);
|
||
|
if (master < 0)
|
||
|
return -1;
|
||
|
if (grantpt(master) < 0)
|
||
|
return -1;
|
||
|
if (unlockpt(master) < 0)
|
||
|
return -1;
|
||
|
buf = ptsname(master);
|
||
|
if (!buf)
|
||
|
return -1;
|
||
|
|
||
|
slave = open(buf, O_RDWR|O_NOCTTY);
|
||
|
if (slave < 0)
|
||
|
return -1;
|
||
|
|
||
|
#ifdef I_PUSH
|
||
|
if (ioctl(slave, I_PUSH, "ptem") < 0)
|
||
|
return -1;
|
||
|
if (ioctl(slave, I_PUSH, "ldterm") < 0)
|
||
|
return -1;
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
*amaster = master;
|
||
|
*aslave = slave;
|
||
|
if (name)
|
||
|
strcpy(name, buf);
|
||
|
if (termp)
|
||
|
tcsetattr(slave, TCSAFLUSH, termp);
|
||
|
if (winp)
|
||
|
ioctl(slave, TIOCSWINSZ, winp);
|
||
|
return 0;
|
||
|
}
|
||
|
#else
|
||
|
#error Do not know how to define openpty.
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#ifndef HAVE_FORKPTY
|
||
|
#if defined(HAVE_OPENPTY)
|
||
|
pid_t
|
||
|
forkpty(int *amaster, char *name, struct termios *termp,
|
||
|
struct winsize *winp)
|
||
|
{
|
||
|
pid_t pid;
|
||
|
int master, slave;
|
||
|
|
||
|
if (openpty(&master, &slave, name, termp, winp) < 0)
|
||
|
return -1;
|
||
|
*amaster = master;
|
||
|
|
||
|
/* Fork off... */
|
||
|
pid = fork();
|
||
|
if (pid < 0)
|
||
|
return -1;
|
||
|
else if (pid == 0)
|
||
|
{
|
||
|
char *buf;
|
||
|
int fd;
|
||
|
|
||
|
setsid();
|
||
|
#ifdef TIOCSCTTY
|
||
|
buf = NULL;
|
||
|
if (ioctl(slave, TIOCSCTTY, NULL) < 0)
|
||
|
_exit(1);
|
||
|
#elif defined(_AIX)
|
||
|
fd = open("/dev/tty", O_RDWR|O_NOCTTY);
|
||
|
if (fd >= 0)
|
||
|
{
|
||
|
ioctl(fd, TIOCNOTTY, NULL);
|
||
|
close(fd);
|
||
|
}
|
||
|
|
||
|
buf = ttyname(master);
|
||
|
fd = open(buf, O_RDWR);
|
||
|
close(fd);
|
||
|
|
||
|
fd = open("/dev/tty", O_WRONLY);
|
||
|
if (fd < 0)
|
||
|
_exit(1);
|
||
|
close(fd);
|
||
|
|
||
|
if (termp && tcsetattr(slave, TCSAFLUSH, termp) == -1)
|
||
|
_exit(1);
|
||
|
if (ioctl(slave, TIOCSWINSZ, winp) == -1)
|
||
|
_exit(1);
|
||
|
#else
|
||
|
buf = ptsname(master);
|
||
|
fd = open(buf, O_RDWR);
|
||
|
close(fd);
|
||
|
#endif
|
||
|
dup2(slave, 0);
|
||
|
dup2(slave, 1);
|
||
|
dup2(slave, 2);
|
||
|
|
||
|
if (slave > 2)
|
||
|
close(slave);
|
||
|
close(master);
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
close(slave);
|
||
|
return pid;
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
#error Do not know how to define forkpty.
|
||
|
#endif
|
||
|
#endif
|