dorne/cli/_pty.c

178 lines
5.1 KiB
C
Raw Normal View History

2025-09-09 17:35:37 +10:00
/* 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.
*/
2025-09-09 15:43:54 +10:00
/* _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>
2025-09-09 21:05:42 +10:00
#include "_pty.h"
2025-09-09 17:35:37 +10:00
2025-09-09 15:43:54 +10:00
/* Allocate PTY master and slave file descriptors.
* errno will have been set if newpty() fails.
*
* NOTE: This function is my alternative to GLibC's
2025-09-09 21:03:40 +10:00
* NOTE: openpty() function. It exists as a learning resource.
2025-09-09 17:35:37 +10:00
* REF: https://sourceware.org/git/glibc.git -> ./login/openpty.c
2025-09-09 15:43:54 +10:00
*/
int mkpty(int *fdmx, int *fds) {
int _fdmx = -1, _fds = -1;
2025-09-09 17:35:37 +10:00
char sname[TTYNAME_MAX];
2025-09-09 15:43:54 +10:00
// 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;
}
2025-09-10 22:49:05 +10:00
/* Propagate file descriptors via parameters */
2025-09-09 15:43:54 +10:00
*fdmx = _fdmx;
*fds = _fds;
return EXIT_SUCCESS;
fail:
if (_fdmx == -1) {
close(_fdmx);
if (_fds == -1)
close(_fds);
}
return EXIT_FAILURE;
}
2025-09-09 17:35:37 +10:00
/* 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
2025-09-09 21:03:40 +10:00
* NOTE: login_tty() function. It exists as a learning resource.
2025-09-09 17:35:37 +10:00
* REF: https://sourceware.org/git/glibc.git -> ./login/login_tty.c
2025-09-09 21:03:40 +10:00
*/
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.
2025-09-09 17:35:37 +10:00
* 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.
2025-09-09 21:03:40 +10:00
* REF: https://sourceware.org/git/glibc.git -> ./login/login_tty.c
2025-09-09 17:35:37 +10:00
*/
2025-09-09 21:03:40 +10:00
inline void bindpty(const int fdty) {
2025-09-09 17:35:37 +10:00
/* Adjust stdin/stdout/stderr to refer to fd*/
2025-09-09 21:03:40 +10:00
BIND(fdty, STDIN_FILENO);
BIND(fdty, STDOUT_FILENO);
BIND(fdty, STDERR_FILENO);
2025-09-09 17:35:37 +10:00
if (fdty > 2)
close(fdty);
}
2025-09-09 21:03:40 +10:00
/* Allocate a PTY and fork, giving ptmx (master) to the parent
* and binding the child's stdin/stdout/stderr to pts (slave).
2025-09-09 17:35:37 +10:00
* Return value is indentical to fork(2).
* NOTE: This function is my alternative to GLibC's
2025-09-09 21:03:40 +10:00
* NOTE: forkpty() function. It exists as a learning resource.
2025-09-09 17:35:37 +10:00
* REF: https://sourceware.org/git/glibc.git -> ./login/forkpty.c
2025-09-09 15:43:54 +10:00
*/
2025-09-09 21:03:40 +10:00
pid_t forkmkpty(int *fdmx) {
int _fdmx, fds;
2025-09-09 15:43:54 +10:00
pid_t pid;
2025-09-09 21:03:40 +10:00
if (mkpty(&_fdmx, &fds))
2025-09-09 15:43:54 +10:00
return EXIT_FAILURE;
switch (pid = fork()) {
case -1:
2025-09-09 21:03:40 +10:00
close(_fdmx);
2025-09-09 15:43:54 +10:00
close(fds);
return -1;
case 0:
/* Child Process */
2025-09-09 21:03:40 +10:00
close(_fdmx);
setctty(fds);
2025-09-09 17:35:37 +10:00
bindpty(fds);
2025-09-09 15:43:54 +10:00
break;
default:
/* Parent Process */
close(fds);
2025-09-09 21:03:40 +10:00
// propagate ptmx (master) fd
*fdmx = _fdmx;
2025-09-09 15:43:54 +10:00
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,
};
2025-09-09 17:35:37 +10:00
if (ioctl(fds, TIOCSWINSZ, &win) == -1)
2025-09-09 15:43:54 +10:00
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 };
2025-09-09 17:35:37 +10:00
if (ioctl(fds, TIOCGWINSZ, &win) == -1)
2025-09-09 15:43:54 +10:00
return EXIT_FAILURE;
*rows = win.ws_row;
*cols = win.ws_col;
return EXIT_SUCCESS;
#endif /* TIOCGWINSZ */
}
2025-09-09 17:35:37 +10:00