mv cursetree/pty subdir
This commit is contained in:
parent
878be07a71
commit
69745c81f7
7 changed files with 1 additions and 1 deletions
177
cursetree/pty/_pty.c
Normal file
177
cursetree/pty/_pty.c
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/* WARNING: The mkpty(), forkmkpty(), & bindpty() functions are based on
|
||||
* WARNING: the glibc openpty(), forkpty(), & login_tty() functions respectively.
|
||||
* WARNING:
|
||||
* WARNING: The GNU C Library's COPYING & COPYING.LIB licenses are available at
|
||||
* WARNING: LICENSES/GLIBC-COPYING & LICENSES/GLIBC-COPYING.LIB in this repo.
|
||||
* WARNING:
|
||||
* WARNING: login_tty() maintains the original University of California license
|
||||
* WARNING: available at LICENSES/UC-LICENSE in this repo.
|
||||
*/
|
||||
|
||||
/* _XOPEN_SOURCE unlocks <stdlib.h> pty/ptmx/pts declarations. */
|
||||
#define _XOPEN_SOURCE 600
|
||||
/* _GNU_SOURCE unlocks the ptsname_r declaration*/
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <asm/termbits.h> /* TIOC* constants */
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "_pty.h"
|
||||
|
||||
/* Allocate PTY master and slave file descriptors.
|
||||
* errno will have been set if newpty() fails.
|
||||
*
|
||||
* NOTE: This function is my alternative to GLibC's
|
||||
* NOTE: openpty() function. It exists as a learning resource.
|
||||
* REF: https://sourceware.org/git/glibc.git -> ./login/openpty.c
|
||||
*/
|
||||
int mkpty(int *fdmx, int *fds) {
|
||||
int _fdmx = -1, _fds = -1;
|
||||
char sname[TTYNAME_MAX];
|
||||
|
||||
// Configure PTY master (file descriptor)
|
||||
_fdmx = posix_openpt(O_RDWR | O_NOCTTY);
|
||||
if (_fdmx == -1)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (grantpt(_fdmx))
|
||||
goto fail;
|
||||
if (unlockpt(_fdmx))
|
||||
goto fail;
|
||||
|
||||
#ifdef TIOCGPTPEER
|
||||
/* Try to allocate slave fd solely based on PTMX fd first. */
|
||||
_fds = ioctl(_fdmx, TIOCGPTPEER, O_RDWR | O_NOCTTY);
|
||||
#endif
|
||||
if (_fds == -1) {
|
||||
/* Fallback to path-based slave fd allocation
|
||||
* (if the kernel doesn't support TIOCGPTPEER, ie Linux <4.13) */
|
||||
if(ptsname_r(_fdmx, sname, sizeof(sname)))
|
||||
goto fail;
|
||||
|
||||
_fds = open(sname, O_RDWR | O_NOCTTY);
|
||||
if (_fds == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Propagate file descriptors via parameters */
|
||||
*fdmx = _fdmx;
|
||||
*fds = _fds;
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
fail:
|
||||
if (_fdmx == -1) {
|
||||
close(_fdmx);
|
||||
if (_fds == -1)
|
||||
close(_fds);
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Set fdty as the controlling terminal for the calling process.
|
||||
* Returns 0 on success, and 1 on failure.
|
||||
* NOTE: This function is my alternative to GLibC's
|
||||
* NOTE: login_tty() function. It exists as a learning resource.
|
||||
* REF: https://sourceware.org/git/glibc.git -> ./login/login_tty.c
|
||||
*/
|
||||
static inline int setctty(const int fdty) {
|
||||
/* We assume any kernel compiling this defines TIOCSCTTY,
|
||||
* otherwise this implementation won't exactly work... */
|
||||
return (ioctl(fdty, TIOCSCTTY, 0) != -1);
|
||||
}
|
||||
|
||||
/* Bind fdty (terminal fd) to stdin/stdout/stderr for the calling process.
|
||||
* This functions blocks until the EBUSY (see `man dup2`) race condition lifts.
|
||||
* NOTE: This function is my alternative to GLibC's
|
||||
* NOTE: login_tty() function. It exists as a learning resource.
|
||||
* WARNING: This function maintains the original University of California
|
||||
* WARNING: LICENSE (1990-1993) as per glibc.git:/login/login_tty.c
|
||||
* WARNING: available at LICENSES/UC-LICENSE in this repo.
|
||||
* REF: https://sourceware.org/git/glibc.git -> ./login/login_tty.c
|
||||
*/
|
||||
void bindpty(const int fdty) {
|
||||
/* Adjust stdin/stdout/stderr to refer to fd*/
|
||||
BIND(fdty, STDIN_FILENO);
|
||||
BIND(fdty, STDOUT_FILENO);
|
||||
BIND(fdty, STDERR_FILENO);
|
||||
if (fdty > 2)
|
||||
close(fdty);
|
||||
}
|
||||
|
||||
/* Allocate a PTY and fork, giving ptmx (master) to the parent
|
||||
* and binding the child's stdin/stdout/stderr to pts (slave).
|
||||
* Return value is indentical to fork(2).
|
||||
* NOTE: This function is my alternative to GLibC's
|
||||
* NOTE: forkpty() function. It exists as a learning resource.
|
||||
* REF: https://sourceware.org/git/glibc.git -> ./login/forkpty.c
|
||||
*/
|
||||
pid_t forkmkpty(int *fdmx) {
|
||||
int _fdmx, fds;
|
||||
pid_t pid;
|
||||
if (mkpty(&_fdmx, &fds))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
close(_fdmx);
|
||||
close(fds);
|
||||
return -1;
|
||||
case 0:
|
||||
/* Child Process */
|
||||
close(_fdmx);
|
||||
setctty(fds);
|
||||
bindpty(fds);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Parent Process */
|
||||
close(fds);
|
||||
// propagate ptmx (master) fd
|
||||
*fdmx = _fdmx;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Both Processes */
|
||||
return pid;
|
||||
}
|
||||
|
||||
/* Set pseudoterminal slave's window size.
|
||||
* Returns 0 on success, and fails with -1 if the kernel doesn't
|
||||
* implement this, or 1 for general errors (errno will be set).
|
||||
* NOTE: Typically this is part of a glibc openpty() call.
|
||||
*/
|
||||
int setptsxy(const unsigned short rows, const unsigned short cols, const int fds) {
|
||||
#ifndef TIOCSWINSZ
|
||||
/* Fail if kernel doesn't support TIOCSWINSZ. */
|
||||
return -1;
|
||||
#else
|
||||
struct winsize win = {
|
||||
.ws_row = rows,
|
||||
.ws_col = cols,
|
||||
};
|
||||
|
||||
if (ioctl(fds, TIOCSWINSZ, &win) == -1)
|
||||
return EXIT_FAILURE;
|
||||
return EXIT_SUCCESS;
|
||||
#endif /* TIOCSWINSZ */
|
||||
}
|
||||
|
||||
/* Get pseudoterminal slave's window size.
|
||||
* Returns 0 on success, and fails with -1 if the kernel doesn't
|
||||
* implement this, or 1 for general errors (errno will be set).
|
||||
*/
|
||||
int getptsxy(unsigned short *rows, unsigned short *cols, const int fds) {
|
||||
#ifndef TIOCGWINSZ
|
||||
/* Fail if kernel doesn't support TIOCGWINSZ. */
|
||||
return -1;
|
||||
#else
|
||||
struct winsize win = (struct winsize){ 0 };
|
||||
if (ioctl(fds, TIOCGWINSZ, &win) == -1)
|
||||
return EXIT_FAILURE;
|
||||
*rows = win.ws_row;
|
||||
*cols = win.ws_col;
|
||||
return EXIT_SUCCESS;
|
||||
#endif /* TIOCGWINSZ */
|
||||
}
|
||||
|
||||
30
cursetree/pty/_pty.h
Normal file
30
cursetree/pty/_pty.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef DORNE_MKPTY_H
|
||||
#define DORNE_MKPTY_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef PATH_MAX
|
||||
#define TTYNAME_MAX PATH_MAX
|
||||
#else
|
||||
#define TTYNAME_MAX 512
|
||||
#endif /* PATH_MAX */
|
||||
|
||||
#define BIND(fdsrc, fddst) \
|
||||
while (dup2(fdsrc, fddst) == -1 && errno == EBUSY) \
|
||||
;
|
||||
|
||||
/* Custom implementation of glibc::openpty() */
|
||||
int mkpty(int *fdmx, int *fds);
|
||||
/* Custom implementation of glibc::login_tty() */
|
||||
void bindpty(const int fdty);
|
||||
/* Custom implementation of glibc::forkpty() */
|
||||
pid_t forkmkpty(int *fdmx);
|
||||
|
||||
int setptsxy(const unsigned short rows, const unsigned short cols,
|
||||
const int fds);
|
||||
int getptsxy(unsigned short *rows, unsigned short *cols, const int fds);
|
||||
|
||||
#endif /* DORNE_MKPTY_H */
|
||||
63
cursetree/pty/child.c
Normal file
63
cursetree/pty/child.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "child.h"
|
||||
#include "epty.h"
|
||||
|
||||
/* SIGTERM grace period before a child is sent SIGKILL.
|
||||
* Value is in microseconds (us), 1000us = 1ms */
|
||||
#define CHILD_GRACE_US 100000
|
||||
|
||||
/* Check if a child is still alive by pid
|
||||
* by kill(3p) sending a null signal (0).
|
||||
*/
|
||||
static int is_alive(struct d_child *child) {
|
||||
return (kill(child->pid, 0) == 0);
|
||||
}
|
||||
|
||||
/* Force a child to die, first by sending SIGTERM, otherwise SIGKILL.
|
||||
* NOTE: killchild() ASSUMES the child is LIVING at the start.
|
||||
* NOTE: Keep this in mind when threading!
|
||||
*/
|
||||
void killchild(struct d_child *child) {
|
||||
int stat;
|
||||
|
||||
/* Request Death */
|
||||
kill(child->pid, SIGTERM);
|
||||
usleep(CHILD_GRACE_US);
|
||||
|
||||
/* Force Death */
|
||||
if (is_alive(child))
|
||||
kill(child->pid, SIGKILL);
|
||||
|
||||
/* Reap */
|
||||
waitpid(child->pid, &stat, 0);
|
||||
}
|
||||
|
||||
/* Fork to spawn a child process running bin/shfx in an "epty".
|
||||
*/
|
||||
int spawnchild(struct d_child *child) {
|
||||
*child = (struct d_child){ 0 };
|
||||
|
||||
/* fork(2) and allocate an "error-piped pseudoterminal" (epty) */
|
||||
switch (child->pid = forkepty(&child->fdmx, &child->fderr)) {
|
||||
case -1:
|
||||
perror("forkepty");
|
||||
return EXIT_FAILURE;
|
||||
case 0:
|
||||
char *args[] = {"shfx", NULL};
|
||||
execvp("shfx", args);
|
||||
perror("execvp");
|
||||
exit(1);
|
||||
default:
|
||||
usleep(1000);
|
||||
char bufout[10];
|
||||
bufout[9] = '\0';
|
||||
read(child->fdmx, bufout, 9);
|
||||
printf("child stdout: \"%s\"\n", bufout);
|
||||
break;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
14
cursetree/pty/child.h
Normal file
14
cursetree/pty/child.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef DORNE_CHILD_H
|
||||
#define DORNE_CHILD_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
struct d_child {
|
||||
pid_t pid;
|
||||
int fdmx; /* PTY master fd (read/write) */
|
||||
int fderr; /* child's stderr (readonly) */
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
int spawnchild(struct d_child *child);
|
||||
|
||||
#endif /* DORNE_CHILD_H */
|
||||
45
cursetree/pty/epty.c
Normal file
45
cursetree/pty/epty.c
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "_pty.h"
|
||||
|
||||
#define PIPE_READ 0
|
||||
#define PIPE_WRITE 1
|
||||
|
||||
/* Allocate a PTY and fork, giving fdmx (master) to the parent
|
||||
* and binding the child's stdin/stdout to fds (slave).
|
||||
* Return value is indentical to fork(2).
|
||||
* NOTE: This function is my alternative to GLibC's
|
||||
* NOTE: forkpty() function. It exists as a learning resource.
|
||||
* REF: https://sourceware.org/git/glibc.git -> ./login/forkpty.c
|
||||
*/
|
||||
pid_t forkepty(int *fdmx, int *fderr) {
|
||||
// master/slave, and stderr pipe fds
|
||||
int epipe[2];
|
||||
pid_t pid;
|
||||
|
||||
if (pipe(epipe) == -1)
|
||||
return EXIT_FAILURE;
|
||||
switch (pid = forkmkpty(fdmx)) {
|
||||
case -1:
|
||||
/* forkmkpty() will close fdmx/fds for us */
|
||||
close(epipe[PIPE_READ]);
|
||||
close(epipe[PIPE_WRITE]);
|
||||
return -1;
|
||||
case 0:
|
||||
/* Child Process */
|
||||
/* forkmkpty() will close fdmx for us */
|
||||
close(epipe[PIPE_READ]);
|
||||
BIND(epipe[PIPE_WRITE], STDERR_FILENO);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Parent Process */
|
||||
/* forkmkpty() will close fds for us */
|
||||
close(epipe[PIPE_WRITE]);
|
||||
*fderr = epipe[PIPE_READ];
|
||||
break;
|
||||
}
|
||||
|
||||
/* Both Processes */
|
||||
return pid;
|
||||
}
|
||||
8
cursetree/pty/epty.h
Normal file
8
cursetree/pty/epty.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef DORNE_EPTY_H
|
||||
#define DORNE_EPTY_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
pid_t forkepty(int *fdmx, int *fderr);
|
||||
|
||||
#endif /* DORNE_EPTY_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue