Skip to content

Instantly share code, notes, and snippets.

@arachsys
Last active Feb 6, 2022
Embed
What would you like to do?
Run programs on a pty and capture stdout
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <pty.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char buffer[BUFSIZ];
int log, pty;
struct termios termios;
if (argc < 3) {
fprintf(stderr, "Usage: %s LOG CMD...\n", argv[0]);
exit(64);
};
log = open(argv[1], O_APPEND | O_CREAT | O_WRONLY, 0666);
if (log < 0)
err(EXIT_FAILURE, "open %s", argv[1]);
switch (fork()) {
case -1:
err(EXIT_FAILURE, "fork");
case 0:
break;
default:
return EXIT_SUCCESS;
}
dup2(log, STDOUT_FILENO);
dup2(log, STDERR_FILENO);
if (log != STDOUT_FILENO && log != STDERR_FILENO)
close(log);
close(STDIN_FILENO);
setsid();
pid_t pid = forkpty(&pty, NULL, NULL, NULL);
switch (pid) {
case -1:
err(EXIT_FAILURE, "forkpty");
case 0:
if (tcgetattr(0, &termios) == 0) {
cfmakeraw(&termios);
tcsetattr(0, TCSANOW, &termios);
}
execvp(argv[2], argv + 2);
err(EXIT_FAILURE, "execvp");
}
while (1) {
ssize_t length = read(pty, buffer, sizeof(buffer)), offset = 0;
if (length < 0 && errno != EINTR)
return EXIT_SUCCESS;
while (length > 0) {
ssize_t count = write(STDOUT_FILENO, buffer + offset, length);
if (count < 0 && errno != EINTR)
err(EXIT_FAILURE, "write");
if (count > 0)
length -= count, offset += count;
}
}
}
#include <err.h>
#include <errno.h>
#include <pty.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
char buffer[BUFSIZ];
int pty;
struct termios termios;
if (argc < 2) {
fprintf(stderr, "Usage: %s CMD...\n", argv[0]);
exit(64);
};
pid_t pid = forkpty(&pty, NULL, NULL, NULL);
switch (pid) {
case -1:
err(EXIT_FAILURE, "forkpty");
case 0:
if (tcgetattr(0, &termios) == 0) {
cfmakeraw(&termios);
tcsetattr(0, TCSANOW, &termios);
}
execvp(argv[1], argv + 1);
err(EXIT_FAILURE, "execvp");
}
while (1) {
ssize_t length = read(pty, buffer, sizeof(buffer)), offset = 0;
if (length < 0 && errno != EINTR)
return EXIT_SUCCESS;
while (length > 0) {
ssize_t count = write(STDOUT_FILENO, buffer + offset, length);
if (count < 0 && errno != EINTR)
err(EXIT_FAILURE, "write");
if (count > 0)
length -= count, offset += count;
}
}
}
#!/bin/python
import os
import pty
import sys
import tty
if len(sys.argv) < 2:
print('Usage: %s CMD...' % sys.argv[0], file = sys.stderr)
sys.exit(64)
pid, fd = pty.fork()
if pid == 0:
tty.setraw(sys.stdout.fileno())
os.execlp(sys.argv[1], *sys.argv[1:])
while True:
try:
buffer = os.read(fd, 1024)
except OSError:
break
sys.stdout.buffer.write(buffer)
sys.stdout.flush()
pid, status = os.waitpid(pid, 0)
code = os.waitstatus_to_exitcode(status)
sys.exit(code)
@arachsys
Copy link
Author

arachsys commented Feb 6, 2022

Unfortunately, Linux hasn't binned ttys and ptys in favour of clean character devices and pairs of pipes, so these wrappers can useful for backgrounding any jobs that produce nicer output when run on a tty.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment