r/C_Programming 5d 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 (?_?)

6 Upvotes

6 comments sorted by

View all comments

1

u/HugoNikanor 5d 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);
}