1 year ago

#356324

test-img

unami

Can't send input to a process over a pty in C

I'm trying to run a process (/usr/bin/calc) on the master/main side, and interact with it on the slave/secondary side, but for some reason, the calc process isn't reading input from the main end of the pty.

I've got the main and secondary sides in 2 files running separately, here's the gist of what each does:

main:

  • calls openpty() to get the main file descriptor (mfd) and the secondary name (sname)
  • forks
  • parent: displays the mfd, sfd, and sname, then loops to keep the mfd open
  • child: dup2's the mfd to stdin/out/err (dup2(mfd, STDIN_FILENO);), then calls execvp() to run the calc process

secondary:

  • takes the sname as an input (ie, /dev/pts/5), and calls open() on it to open the secondary end of the pty
  • forks
  • parent: in a never-ending while loop, reads input from stdin and writes it to the sfd
  • child: in a never-ending while loop, reads input from sfd and writes to stdout

What I'm seeing:

  • initially I see the output of the calc cmd on the secondary side, which is what I expected

And that's it. After that, I try typing on the secondary side (to send back to the main side, as input for the calc cmd), but I don't believe calc is reading it as input.

As a sanity test, I wrote a new program for the main side, which opened the pty, forked, and in one process, wrote a string to the mfd (for the sfd to display), and in the other, read from the mfd and displayed the input on the screen. This worked exactly as expected - the secondary side read/displayed everything the main side wrote to the pty, and everything I typed in on the secondary side displayed on the main side, so I'm able to read/write to the pty without any problem, but for some reason when I try to run calc, it doesn't seem to be receiving what I'm sending it over the pty.

This is my first time doing most of this (first time working with a pty, first time forking, first time calling exec), so I plead ignorance. Any idea what I might be doing wrong? I can post my code if needed, but it's all pretty straight-forward (that's the frustrating part - it seems so straight-forward, and yet still doesn't work :-/ ).

Here is the code, as requested - main, followed by the secondary side of the pty.

main:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pty.h>
#include <termios.h>
#include <errno.h>

struct termios tty;
#define PTSNAME_SIZE 1024

int main(void) {
  int mfd, sfd, i = 1;
  char *cmd[] = {"/usr/bin/calc", NULL};
  char sname[PTSNAME_SIZE];
  pid_t pid;
  
  memset(sname, 0, PTSNAME_SIZE);
  errno = 0;
  
  // open the pseudoterminal
  if (openpty(&mfd, &sfd, sname, NULL, NULL) == -1) {
    perror("error calling openpty\r\n");
    return errno;
  }
  
  if (tcgetattr(mfd, &tty) == -1) {
    perror("tcgetattr");
    return -1;
  }
    
  tty.c_lflag &= ~ECHO;

  if (tcsetattr(mfd, TCSANOW, &tty) != 0) {
    perror("tcsetattr");
    return -1;
  }
  
  close(sfd);
    
  // in the parent, write data
  // in the child, read data 
  pid = fork();
  switch(pid) {
  case -1:
    perror("error forking\r\n");
    return errno;
    break;
  case 0:
    // child
    // set stdin/out/err to the mfd
    dup2(mfd, STDIN_FILENO);
    dup2(mfd, STDOUT_FILENO);
    dup2(mfd, STDERR_FILENO);
    
    close(mfd);
    // and start a process - the output from this cmd should go to the secondary end
    // of the pty, and anything written on that end should come back as input to the process
    execvp(cmd[0], cmd);

    break;
  default:
    // parent
    printf("main %d, secondary %d, sname %s\r\n", mfd, sfd, sname);  

    for (i = 0; ; i++) {
      sleep(2);
    }
  }
  
  return 0;
}

Secondary:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pty.h>
#include <termios.h>
#include <errno.h>

struct termios tty;

int main(int argc, char *argv[]) {
  int sfd, rc, i = 1;
  char buf[256];
  pid_t pid;
  errno = 0;

  if (argc > 1)
    sfd = open(argv[1], O_RDWR);
  else {
    perror("invalid syntax - must specify /dev/pts/x\r\n");
    return errno;
  }

  if (sfd > 0) {
    pid = fork();
    switch(pid) {
    case -1:
      perror("error forking\r\n");
      return errno;
      break;
    case 0:
      // child
      // read data sent from mfd
      while (1) {
        memset(buf, 0, sizeof(buf));
        rc = read(sfd, buf, sizeof(buf));
        if (rc > 0) {
          printf("%d. Rec'd this from ptym (rc=%d): %s\r\n", i++, rc, buf);
        } else {
          perror("error reading on sfd\r\n");
        }
      }
      break;
    default:
      //parent
      while(1) {
        memset(buf, 0, sizeof(buf));

        // read input from stdin
        rc = read(STDIN_FILENO, buf, sizeof(buf));

        if (rc > 0) {
          buf[strlen(buf)-1] = 0;
          printf("sending to mfd: %s, len=%ld", buf, strlen(buf));
          // write data to sfd
          write(sfd, buf, strlen(buf));
          fflush(NULL);
        } else {
          perror("error reading on sfd\r\n");
        }       
      }
      break;
    }
  } else {
    perror("open(sfd) failed\r\n");
    return errno;
  }
      
  return 0;
}

c

linux

stdin

pty

execvp

0 Answers

Your Answer

Accepted video resources