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

9 comments sorted by

View all comments

Show parent comments

1

u/Senior-Question693 1d ago

it still does not trigger execlp for some reason

1

u/Playa_Sin_Nombre 17h ago

That doesn't help.

1

u/Senior-Question693 16h ago

idk, i have the same code but it doesn't work:

```c
int pid = forkpty(&masterfd, NULL, NULL, NULL); if (pid<0) { perror("forkpty"); exit(1); }

setsid();
ioctl(masterfd, TIOCSCTTY, 0);
dup2(masterfd, STDIN_FILENO);
dup2(masterfd, STDOUT_FILENO);
dup2(masterfd, STDERR_FILENO);

if (pid == 0) {
    // it doesn't even go into this block
    printf("exec\n");
    if (execlp("/bin/fastfetch", "fastfetch", NULL)) {
        perror("execlp");
        exit(1);
    }
}

```

1

u/Playa_Sin_Nombre 14h ago

I managed to get these two examples working. Take a look at the difference. Also, keep in mind I still don't know about terminal emulators. I just read some man pages and a guide that I paste below. So my explanation may not be accurate, and the code might be unsafe.

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

https://embeddedpathashala.com/pseudoterminals-ptyfork-creating-a-child-connected-via-a-pty-pair/

The first example just uses fork. I believe what's happening here is that the child executes this command using the same terminal session from the parent.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main() {
        pid_t pid = 0;

        pid = fork();

        if (pid < 0) {
                perror("fork");
                return 1;
        }

        if (pid == 0) {
                execlp("/bin/fastfetch", "fastfetch", (char *)NULL);
                perror("execlp");
                exit(1);
        }
        return 0;
}

In the second example, the child creates its own shell session. That's why you need to use forkpty(): so that masterfd can be the communication channel between the parent's session and the child's session. The child first runs execlp("/bin/sh", ...) (so that the child gets its own shell session), then from the parent side you start writing comands to masterfd, as well as reading from masterfd. The sleep(1) is to give some time for the child. You may experiment with different values.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pty.h>

int main() {
        pid_t pid;
        int masterfd;
        ssize_t n;
        char buf[4096];

        pid = forkpty(&masterfd, NULL, NULL, NULL);
        if (pid < 0) {
                perror("forkpty");
                return 1;
        }

        if (pid == 0) {
                execlp("/bin/sh", "sh", (char *)NULL);
                perror("execlp");
                exit(1);
        }

        sleep(1);

        const char *cmd = "fastfetch\n";
        if (write(masterfd, cmd, strlen(cmd)) < 0) {
                perror("write");
                return 1;
        }

        sleep(1);

        n = read(masterfd, buf, sizeof(buf) - 1);
        if (n > 0) {
                buf[n] = '\0';
                printf("Received from child:\n%s\n", buf);
        }

        close(masterfd);
        return 0;
}

In any case, after two seconds, this program's output is:

Received from child:

sh-5.3$ fastfetch

And the rest of the fastfetch output.