Skip to content

Instantly share code, notes, and snippets.

@Random832
Created March 31, 2017 17:10
Show Gist options
  • Save Random832/4ede1dfd9fac66e409589d039ca92ee0 to your computer and use it in GitHub Desktop.
Save Random832/4ede1dfd9fac66e409589d039ca92ee0 to your computer and use it in GitHub Desktop.
/* Copyright 2016-2017 random832@fastmail.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. */
#define _XOPEN_SOURCE 600
#define _BSD_SOURCE /* for cfmakeraw */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
/* Simple tool to relay data between a pty and the tty it is run on.
* This version deliberately does not handle SIGWINCH or propagate window
* resizes, though it does set the initial size of the window before running
* the subprocess. */
int ptyfd;
struct termios tio;
struct winsize ws;
int ptyproc(const char *path, char * const *argv) {
pid_t pid;
int ptyfd = posix_openpt(O_RDWR|O_NOCTTY);
if(ptyfd < 0) {
perror("openpt");
exit(1);
}
if(grantpt(ptyfd) < 0) {
perror("grantpt");
exit(1);
}
if(unlockpt(ptyfd) < 0) {
perror("unlockpt");
exit(1);
}
pid = fork();
if(pid < 0) {
perror("fork");
exit(1);
} else if(pid == 0) {
char *fn = ptsname(ptyfd);
int fd = open(fn, O_RDWR);
pid = setsid();
if(fd < 0) {
perror(fn);
exit(1);
}
if(ioctl(fd, TIOCSCTTY)) {
perror("sctty");
exit(1);
}
tcsetattr(fd, 0, &tio);
ioctl(fd, TIOCSWINSZ, &ws);
close(0); close(1); close(2);
dup(fd); dup(fd); dup(fd); close(fd);
execvp(path, argv);
perror(path);
_exit(errno == ENOENT ? 127 : 126);
} else {
return ptyfd;
}
}
void
do_input(void) {
char buf;
int n = read(0, &buf, 1);
if(n > 0) {
switch(buf) {
case 0x1d: // ^]
exit(1);
break;
default:
write(ptyfd, &buf, n);
break;
}
} else if(n < 0) {
perror("write pty");
exit(1);
} else {
return;
}
}
int eof;
void
do_output(void) {
char buf[512];
ssize_t n;
n = read(ptyfd, buf, sizeof buf);
if(n > 0) {
eof = 0;
write(1, &buf, n);
} else if(n < 0) {
if(errno == EIO)
/* Can happen on normal subprocess exit */
exit(0);
write(2, "\r\n", 2);
perror("read pty");
write(2, "\r\n", 2);
exit(1);
} else {
exit(0);
}
}
char *shellpath = "/bin/sh";
char *shellargv[] = { "sh", 0 };
void
do_cleanup(void) {
tcsetattr(0, 0, &tio);
}
void
sigexit(int sig) {
do_cleanup();
signal(sig, SIG_DFL);
raise(sig);
}
int main(int argc, char **argv) {
pid_t pid;
if(tcgetattr(0, &tio) < 0) {
perror("gtty");
exit(1);
}
ioctl(0, TIOCGWINSZ, &ws);
char *pprog = argv[1] ? argv[1] : shellpath;
char **pargs = argv[1] ? argv+1 : shellargv;
ptyfd = ptyproc(pprog, pargs);
struct termios tio2 = tio;
cfmakeraw(&tio2);
/* Don't expect to receive any signals normally since the outer terminal is
* in raw mode, but at least make an effort to clean up if killed. */
signal(SIGABRT, sigexit);
signal(SIGTERM, sigexit);
signal(SIGQUIT, sigexit);
atexit(do_cleanup);
tcsetattr(0, 0, &tio2);
for(;;) {
fd_set in_fds;
struct timeval tv;
FD_ZERO(&in_fds);
FD_SET(0, &in_fds);
FD_SET(ptyfd, &in_fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
int sr = select(ptyfd+1, &in_fds, 0, 0, &tv);
if(sr > 0) {
if(FD_ISSET(0, &in_fds)) {
do_input();
}
if(FD_ISSET(ptyfd, &in_fds)) {
do_output();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment