How to read / write to Linux pseudo-terminals using separate processes, but without branching?

I would like to write a program that emulates a device on a serial port. I am trying to use pseudo terminals for this. I want the master to control one separate process. This process acts as a serial device emulator. I want another process (e.g. kermit) to communicate with the master using a slave terminal. Due to different process requirements, I do not use any forks. Almost every example of a pseudo-terminal on the Internet shows the use of fork () for master / slave.

I work in one direction. That is, I can force the slave process to write data to the slave pseudo-terminal, and the master will simply read it from the main pseudo-terminal.

The problem is in the other direction. I cannot get master to write data and a slave to read data.

I will show both non-working bidirectional code and working unidirectional code.

Non-working bidirectional master:

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

int main(int argc, char* argv[])
{
  // get the master fd
  int masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if(masterfd < 0)
  {
    perror("getpt");
    exit(1);
  }

  // grant access to the slave
  if(grantpt(masterfd) < 0)
  {
    perror("grantpt");
    exit(1);
  }

  // unlock the slave
  if(unlockpt(masterfd) < 0)
  {
    perror("unlockpt");
    exit(1);
  }

  // get the path to the slave
  char slavepath[64];
  if(ptsname_r(masterfd, slavepath, sizeof(slavepath)) < 0)
  {
    perror("ptsname_r");
    exit(1);
  }

  printf("Using %s\n", slavepath);

  char bufout = 'D';
  char bufin;

  int c;
  while(1)
  {
    printf("reading\n");
    c = read(masterfd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    if(bufout == 'D') bufout = 'E';
    else if(bufout == 'E') bufout = 'D';
    printf("writing %c\n", bufout);
    c = write(masterfd, &bufout, 1);
    printf("wrote %i bytes\n", c);
    if(c == -1) break;

    sleep(1);
  }

  close(masterfd);

  return 0;
}

Non-working bidirectional statement:

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

int main(int argc, char* argv[])
{
  int fd = open("/dev/pts/15", O_RDWR | O_NOCTTY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }

  char bufout = 'A';
  char bufin;

  int c;
  while(1)
  {
    if(bufout == 'A') bufout = 'B';
    else if(bufout == 'B') bufout = 'A';
    printf("writing %c\n", bufout);
    c = write(fd, &bufout, 1);
    if(c == -1) break;

    printf("reading\n");
    c = read(fd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    sleep(1);
  }

  close(fd);
}

Output of a non-working master: (Note that the first character received came from the slave, and the rest came from characters written by the master. In other words, the master reads the same characters that he wrote to the master and ignores what the slave writes, except for the first symbol.)

Using /dev/pts/15
reading
read 1 bytes: B     [<--- FROM THE SLAVE]
writing E
wrote 1 bytes
reading
read 1 bytes: E     [<--- REST FROM THE MASTER]
writing D
wrote 1 bytes
reading
read 1 bytes: D
writing E
wrote 1 bytes
reading
read 1 bytes: E
writing D
wrote 1 bytes
reading
read 1 bytes: D
^C

The output of a non-working subordinate: (never gets what the master writes).

writing B
reading
^C

Workman without direction:

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

int main(int argc, char* argv[])
{
  // get the master fd
  int masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if(masterfd < 0)
  {
    perror("getpt");
    exit(1);
  }

  // grant access to the slave
  if(grantpt(masterfd) < 0)
  {
    perror("grantpt");
    exit(1);
  }

  // unlock the slave
  if(unlockpt(masterfd) < 0)
  {
    perror("unlockpt");
    exit(1);
  }

  // get the path to the slave
  char slavepath[64];
  if(ptsname_r(masterfd, slavepath, sizeof(slavepath)) < 0)
  {
    perror("ptsname_r");
    exit(1);
  }

  printf("Using %s\n", slavepath);

  char bufout = 'D';
  char bufin;

  int c;
  while(1)
  {
    printf("reading\n");
    c = read(masterfd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    sleep(1);
  }

  close(masterfd);

  return 0;
}

Worker subordination:

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

int main(int argc, char* argv[])
{
  int fd = open("/dev/pts/15", O_RDWR | O_NOCTTY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }

  char bufout = 'A';
  char bufin;

  int c;
  while(1)
  {
    if(bufout == 'A') bufout = 'B';
    else if(bufout == 'B') bufout = 'A';
    printf("writing %c\n", bufout);
    c = write(fd, &bufout, 1);
    if(c == -1) break;

    sleep(1);
  }

  close(fd);
}

The output of the working wizard: (Reads that the slave writes successfully.)

Using /dev/pts/15
reading
read 1 bytes: B
reading
read 1 bytes: A
reading
read 1 bytes: B
reading
read 1 bytes: A
reading
read 1 bytes: B
^C

Worker Slave Output:

writing B
writing A
writing B
writing A
writing B
^C
+4
1

, , , screen picocom.

, "". pts :

$ stty -F /dev/pts/2 
speed 38400 baud; line = 0;
-brkint -imaxbel

, , screen:

$ stty -F /dev/pts/2 
speed 9600 baud; line = 0;
kill = ^H; min = 100; time = 2;
-icrnl -imaxbel
-opost -onlcr
-isig -icanon -echo

pts raw /. , .

struct termios ts;

if(tcgetattr(fd, &ts))
{
  perror("tcgetattr");
  exit(1);
}

cfmakeraw(&ts);
tcsetattr (fd, TCSANOW, &ts);

, .

:

$ ./pts_master 
Using /dev/pts/2
reading
read 1 bytes: B
writing E
wrote 1 bytes
reading
read 1 bytes: A
writing D
wrote 1 bytes
reading
read 1 bytes: B
writing E
wrote 1 bytes
...

:

$ ./pts_slave 
writing B
reading
read 1 bytes: E
writing A
reading
read 1 bytes: D
writing B
...
+5

Source: https://habr.com/ru/post/1539181/


All Articles