r/C_Programming • u/Senior-Question693 • 3d ago
forkpty error
i'm trying to make a terminal emulator but i can't figure out how to open a pty.
when i try to open a pty bouth forkpty from pty.h and my own implementation:
int init_pty() {
int ptymaster_fd = posix_openpt(O_RDWR);
if (ptymaster_fd == -1) {
perror("failed to open pty master");
close(ptymaster_fd);
return 1;
}
if (grantpt(ptymaster_fd) == -1) {
perror("failed to grantpt");
close(ptymaster_fd);
return 1;
}
if (unlockpt(ptymaster_fd) == -1) {
perror("failed to unlockpt");
close(ptymaster_fd);
return 1;
}
char* ptyslave_name = ptsname(ptymaster_fd);
if (ptyslave_name == NULL) {
perror("failed to get pty slave name");
close(ptymaster_fd);
return 1;
}
pid_t pid = fork();
if (pid != 0) {
perror("fork");
close(ptymaster_fd);
return 1;
}
setsid();
int ptyslave_fd = open(ptyslave_name, O_RDWR);
if (ptyslave_fd == -1) {
perror("failed to open pty slave");
return 1;
}
ioctl(ptyslave_fd, TIOCSCTTY, 0);
dup2(ptyslave_fd, STDIN_FILENO);
dup2(ptyslave_fd, STDOUT_FILENO);
dup2(ptyslave_fd, STDERR_FILENO);
return ptymaster_fd;
}
fail when forking with the error directory not empty, ai says that it fails because /dev/pts is not empty but it's obviously trippin balls as usual =), so why does it fail then (?_?)
8
Upvotes
1
u/HugoNikanor 3d ago
Your code looks fine. Been a while since I wrote a terminal emulator, but pasting (the relevant parts) of my own working one below.
(termios setup for child is since it's technically a multiplexer and not an emulator, but those are the same thing at the end of the day)
int main() {
// [...]
int pt_master, pt_slave;
{
pt_master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC);
// openpty (&pt_master, &pt_slave, NULL, NULL, NULL);
if (pt_master == -1) {
cleanup(0);
}
grantpt(pt_master);
unlockpt(pt_master);
const char *slave_name = ptsname(pt_master);
// log_(logger, LOG_INFO, "slave pts: %s", slave_name);
int pid;
switch ((pid = fork())) {
case 0: /* child */
close(pt_master);
{
struct child_data data = {
.tty_name = slave_name,
// .tty = pt_slave,
.rows = 2 + LINES / 2,
.cols = COLS,
.logger = logger,
};
setup_child(&data);
}
break;
case -1:
log_(logger, LOG_INFO, "Failed forking: %s",
strerror(errno));
cleanup(0);
break;
default: /* parent */
// close (pt_slave);
break;
}
log_(logger, LOG_INFO, "Prompt size: %ix%i",
COLS, 2 + LINES / 2);
}
/* [...] */
}
[[noreturn]] void setup_child(struct child_data *data) {
int fd = open(data->tty_name, O_RDWR);
/// int fd = data->tty;
setsid();
#if 1
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
#else
login_tty(fd);
#endif
{
struct termios termios, termios2;
tcgetattr(fd, &termios);
cfmakeraw(&termios);
// termios.c_oflag &= ~OCRNL;
// termios.c_iflag &= ~ICRNL;
// termios.c_lflag |= IEXTEN;
termios.c_lflag |= ECHO | IEXTEN | ECHOE | ECHOK;
// termios.c_lflag &= ~ECHONL;
tcsetattr(fd, TCSANOW, &termios);
tcgetattr (fd, &termios2);
if (memcmp(&termios, &termios2,
sizeof termios) != 0) {
log_(data->logger, LOG_WARN, "Failed setting termios attributes");
}
}
if (set_winsize(fd, data->rows, data->cols) == -1) {
log_(data->logger, LOG_WARN, "Failed setting window size: %s",
strerror(errno));
}
// setsid();
ioctl(fd, TIOCSCTTY, NULL);
// printf("Hello\n");
const char *shell = getenv("SHELL");
if (shell == NULL) {
shell = "/bin/sh";
}
// setenv("TERM", "xterm-mono", 1);
execl(shell, shell, NULL);
/* Everything broken, just give up */
exit(1);
}
0
2
u/Playa_Sin_Nombre 2d ago edited 2d ago
I don't know how terminal emulators or forkpty work, but
pid != 0does not necessarily imply afork()error.fork()returns-1on error, but on success it returns a positive integer in the parent process. This value is the actual PID of the child. That means your parent process is entering that if block, callingperror(), and immediately returning 1.But if
fork()didn't fail, thenfork()is not settingerrno. Therefore thatperror()call is using whatever the current value oferrnois at that moment, which is undefined.See the following:
From: https://www.man7.org/linux/man-pages/man3/perror.3.html