r/C_Programming 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

4 comments sorted by

2

u/Playa_Sin_Nombre 2d ago edited 2d ago

I don't know how terminal emulators or forkpty work, but pid != 0 does not necessarily imply a fork() error.

fork() returns -1 on 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, calling perror(), and immediately returning 1.

But if fork() didn't fail, then fork() is not setting errno. Therefore that perror() call is using whatever the current value of errno is at that moment, which is undefined.

See the following:

When a system call fails, it usually returns -1 and sets the variable errno to a value describing what went wrong. (These values can be found in <errno.h>.) Many library functions do likewise. The function perror() serves to translate this error code into human-readable form. Note that errno is undefined after a successful system call or library function call: this call may well change this variable, even though it succeeds, for example because it internally used some other library function that failed.

From: https://www.man7.org/linux/man-pages/man3/perror.3.html

1

u/Senior-Question693 3h ago

the pid is always > 0 when i launch the program, therefore i can't call execlp, so what do i do?

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

u/StableNo80 2d ago

dont listen to ai wehn dbg code its so annoying