Skip to content

Instantly share code, notes, and snippets.

@bomsi
Created December 10, 2021 05:57
Show Gist options
  • Save bomsi/027fc0e454a2ec5e2822ce2a5bfbcda6 to your computer and use it in GitHub Desktop.
Save bomsi/027fc0e454a2ec5e2822ce2a5bfbcda6 to your computer and use it in GitHub Desktop.
#define _XOPEN_SOURCE 600
#define _BSD_SOURCE
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
int main(int argc, char** argv) {
int pmd, rc, sfd;
char *name;
pid_t child;
/* open an unused pseudoterminal master device */
pmd = posix_openpt(O_RDWR);
assert(pmd != -1);
/* grant access to the slave pseudoterminal */
rc = grantpt(pmd);
assert(rc != -1);
/* unlock the slave pseudoterminal device corresponding
to the master pseudoterminal referred */
rc = unlockpt(pmd);
assert(rc != -1);
/* get the slave pseudoterminal device name */
name = ptsname(pmd);
assert(name != NULL);
printf("Slave side name %s\n", name);
/* open the slave side */
sfd = open(name, O_RDWR);
assert(sfd != -1);
child = fork();
assert(child != -1);
if (child == 0) {
/* child process */
struct termios os, ns;
/* close the master side */
close(pmd);
/* save old settings */
rc = tcgetattr(sfd, &os);
/* set raw mode (input available character by character,
echoing disabled, all special processing disabled */
ns = os;
cfmakeraw(&ns);
/* apply the change now */
tcsetattr(sfd, TCSANOW, &ns);
/* sanity check */
rc = isatty(sfd);
assert(rc == 1);
/* close stdin, stdout and stderr of the current terminal
they will come from the slave side */
close(0);
close(1);
close(2);
/* pseudoterminal becomes stdin, stdout, stderr */
dup(sfd);
dup(sfd);
dup(sfd);
/* original file descriptor can be closed */
close(sfd);
/* make this process the new session leader */
setsid();
/* set the slave side to be the controlling terminal */
rc = ioctl(0, TIOCSCTTY, 1);
assert(rc != -1);
{ /* execute the child */
char** cargv;
int i;
cargv = (char**)malloc(argc * sizeof(char*));
assert(cargv != NULL);
for (i = 1; i < argc; ++i) {
cargv[i - 1] = strdup(argv[i]);
}
cargv[i - 1] = NULL;
rc = execvp(cargv[0], cargv);
}
}
else {
/* master process */
fd_set fi;
char buf[128];
/* close the slave side */
close(sfd);
while (1) {
/* wait on stdin and the master side */
FD_ZERO(&fi);
FD_SET(0, &fi);
FD_SET(pmd, &fi);
rc = select(pmd + 1, &fi, NULL, NULL, NULL);
assert(rc != -1);
if(FD_ISSET(0, &fi)) {
/* data on stdin */
rc = read(0, buf, sizeof(buf));
if (rc > 0) {
/* write it on the master side */
write(pmd, buf, rc);
}
else {
if (rc < 0) {
fprintf(stderr, "Error reading on stdin\n");
assert(0);
}
}
}
if(FD_ISSET(pmd, &fi)) {
/* data on the master side */
rc = read(pmd, buf, sizeof(buf));
if (rc > 0) {
/* write to stdout */
write(1, buf, rc);
}
else {
if (rc < 0) {
if (errno == EIO) {
/* child died */
break;
}
fprintf(stderr, "Error reading on master\n");
}
}
}
}
}
fprintf(stderr, "Done.\n");
return rc;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment