0

I have been trying to create a pseudo terminal to communicate with mpg123. From all my readings I believe I have written the code properly, but I can't figure out how I'm supposed to connect the master side (PTY) with the external program.

I get the first available master with posix_openpt, and enable the slave with grantpt and unlockpt. Then I get the PTS name with ptsname which can be used with open to get a file descriptor. Next, I replace STDIN, STDOUT and STDERR of PTY with dup2. Then I can use that file descriptor to communicate with the PTY. But how does the master end connect to the external program that I wish to communicate with?

Here is my code so far:

#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <libgen.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <termios.h>
#include <sys/wait.h>

int main (int argc, char *argv[])
{
    int fdm;
    int fds;
    pid_t pid;
    char *slave_name;
    char *program_name;

    program_name = basename(argv[0]);

    if(argc != 1)
    {
        fprintf(stderr, "usage: %s\n", program_name);
        exit(EXIT_FAILURE);
    }

    if((fdm = posix_openpt(O_RDWR)) == -1)
    {
        fprintf(stderr, "%s: posix_openpt failed (%s)\n", program_name, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if(grantpt(fdm) == -1)
    {
        fprintf(stderr, "%s: grantpt failed (%s)\n", program_name, strerror(errno));
        close(fdm);
        exit(EXIT_FAILURE);
    }

    if(unlockpt(fdm) == -1)
    {
        fprintf(stderr, "%s: unlockpt failed (%s)\n", program_name, strerror(errno));
        close(fdm);
        exit(EXIT_FAILURE);
    }

    if((slave_name = ptsname(fdm)) == NULL)
    {
        fprintf(stderr, "%s: ptsname failed\n", program_name);
        close(fdm);
        exit(EXIT_FAILURE);
    }

    if((pid = fork()) == -1)
    {
        fprintf(stderr, "%s: fork failed (%s)\n", program_name, strerror(errno));
        close(fdm);
        exit(EXIT_FAILURE);
    }

    if(pid == 0)    // Child
    {
        if(setsid() == -1)
        {
            fprintf(stderr, "%s: setsid failed (%s)\n", program_name, strerror(errno));
            close(fdm);
            exit(EXIT_FAILURE);
        }

        if((fds = open(slave_name, O_RDWR)) == -1)
        {
            fprintf(stderr, "%s: open failed (%s)\n", program_name, strerror(errno));
            close(fdm);
            exit(EXIT_FAILURE);
        }

        close(fdm);

        if(dup2(fds, STDIN_FILENO) == -1)
        {
            fprintf(stderr, "%s: dup2(1) failed (%s)\n", program_name, strerror(errno));
            close(fds);
            exit(EXIT_FAILURE);
        }

        if(dup2(fds, STDOUT_FILENO) == -1)
        {
            fprintf(stderr, "%s: dup2(2) failed (%s)\n", program_name, strerror(errno));
            close(fds);
            exit(EXIT_FAILURE);
        }

        if(dup2(fds, STDERR_FILENO) == -1)
        {
            fprintf(stderr, "%s: dup2(3) failed (%s)\n", program_name, strerror(errno));
            close(fds);
            exit(EXIT_FAILURE);
        }

        close(fds);
        exit(EXIT_SUCCESS);
    }
    else        // Parent
    {
        wait(&pid);
    }

    exit(EXIT_SUCCESS);
}

I'm not looking for a hand out, just an explaination. How do I connect to mpg123? Where do I connect to mpg123, in the child or the parent?

Deanie
  • 2,316
  • 2
  • 19
  • 35
  • I remember having played much with PTY long ago. If I remember well, I wrote several codes by hacing the source code of the standard `script` program. It is a short code, very easy to hack. – Thomas Baruchel Nov 21 '15 at 21:08

1 Answers1

1

Typically you would call execvp in the child to execute mpg123. You then communicate through the master file descriptor (fdm) which acts as the user of the terminal (TTYs are meant for human users).

Are you certain you need to use a pty for this? Most programs work fine with just normal pipes for stdin/stdout. Those you can create by calling pipe or sockerpair in the parent process. Even better, if you only need to read or only to write you can use popen which simplifies the interface a lot.

Per Johansson
  • 6,697
  • 27
  • 34
  • I did try popen. http://stackoverflow.com/questions/33678441/why-wont-popen-communicate-with-mpg123 – Deanie Nov 21 '15 at 21:10
  • Alright, I'm still wondering if there isn't a better interface than to simulate a human user, but that's not really my call. – Per Johansson Nov 21 '15 at 21:20
  • Your first paragraph was what I needed to complete my task. Ty. – Deanie Nov 21 '15 at 23:27
  • There are a couple of other ways to communicate with `mpg123`. You can use the -R to create a fifo to send commands through, or you can try and use libmpg123. The reason I chose the simulate human input is with the other two methods you must constantly read the output to find the end of a track. With human input, I can play an enitre playlist and use the forward and backward keys without ever needing to know the current state of `mpg123`. – Deanie Nov 22 '15 at 01:54