'How to implement a pipe to read and write an external program to an automation script?

Recently I started to write my first program in C to automate a process on a Linux device. The script is supposed to run the program ppd terminal on the terminal, read the first 2 lines and assess whether there is an error and finally execute the command startmining within ppd terminal (and to execute startmining every 5 minutes if there is no error found). However, I didn't know exactly how to set up a 2-way pipe to read and write to the running program external program (previously started executed on the following script with popen()).

I asked online for a solution to this problem and someone sent me a script (from this post) that they build that does exactly what I needed. However, I am new to writing C and during the past 2 days, I have attempted to implement this idea in my script without success.

Could someone help me understand how to implement the following script:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

typedef struct pipe_rw
{
   pid_t cpid;
   int pipe_r[2];
   int pipe_w[2];
} RWPIPE;

static char *get_user_input(void)
{
   char buf[128];
   char *input;
   char ch;
   size_t len = 0;

   while((ch = fgetc(stdin)) != '\n' && ch != EOF && len < sizeof(buf) - 2)
      buf[len++] = ch;
   buf[len++] = '\n';
   buf[len] = '\0';

   input = malloc(sizeof(char) * (len + 1));
   strncpy(input, buf, (len + 1));
   printf("Got: [%s]\n", input);
   return input;
}

static int pclose_rw(RWPIPE *rwp)
{
   int status, ret = 0;

   if (rwp)
   {
      if (rwp->cpid > 0)
      {
         kill(rwp->cpid, SIGTERM);

         do {
            ret = waitpid(rwp->cpid, &status, WUNTRACED|WCONTINUED);
         } while (!WIFEXITED(status) && !WIFSIGNALED(status));
      }

      close(rwp->pipe_r[0]);
      close(rwp->pipe_w[2]);
      free(rwp);
   }

   return ret;
}

static RWPIPE *popen_rw(const char *command)
{
   RWPIPE *rwp = (RWPIPE *)malloc(sizeof(*rwp));
   if (rwp == NULL)
      return NULL;

   memset(rwp, 0x00, sizeof(*rwp));
   if (pipe(rwp->pipe_r) != 0 || pipe(rwp->pipe_w) != 0)
   {
      free(rwp);
      return NULL;
   }

   rwp->cpid = fork();
   if (rwp->cpid == -1)
   {
      free(rwp);
      return NULL;
   }

   if (rwp->cpid == 0)
   {
      dup2(rwp->pipe_w[0], STDIN_FILENO);
      dup2(rwp->pipe_r[2], STDOUT_FILENO);

      close(rwp->pipe_r[0]);
      close(rwp->pipe_r[2]);
      close(rwp->pipe_w[0]);
      close(rwp->pipe_w[2]);

      execl(command, command, NULL);
      fprintf(stderr, "Error: fail to exec command '%s'.\n", command);
      exit (1);
   }
   else
   {
      close(rwp->pipe_r[2]);
      close(rwp->pipe_w[0]);
   }

   return rwp;
}

static ssize_t read_p(RWPIPE *rwp, void *buf, size_t count)
{
   return read(rwp->pipe_r[0], buf, count);
}

static ssize_t write_p(RWPIPE *rwp, const void *buf, size_t count)
{
   return write(rwp->pipe_w[2], buf, count);
}

int main(void)
{
   char rbuf[BUFSIZ];
   int ret, len;
   char *string;

   signal(SIGPIPE, SIG_IGN);

   RWPIPE *rwp = popen_rw("./read_write");
   if (rwp == NULL)
   {
      printf("Error: fail to open command ..\n");
      return EXIT_FAILURE;
   }

   while (1)
   {
      memset(rbuf, 0x00, sizeof(rbuf));
      if (read_p(rwp, rbuf, sizeof(rbuf)) <= 0)
      {
         printf("No more input..\n");
         break;
      }
      printf("From child: [%s]\n", rbuf);

      string = get_user_input();
      len = strlen(string);
      printf("Length %d: [%s]\n", len, string);
      ret = write_p(rwp, string, len);
      if (ret != len)
      {
         fprintf(stderr, "Write %d bytes (expected %d) ..\n", ret, len);
         break;
      }
      printf("end cycle\n");
   }
   printf("End of loop\n");
   pclose_rw(rwp);

   return EXIT_SUCCESS;
}

into this script I have been developing:

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

#define MAX_LINE 350

//run the program inside rsnode directory
int main() {

    FILE *output;
    
    char buffer[MAX_LINE];
    int buffer_search;
    int read_line = 2;
    char error_message[] = "[ERROR]";
    
    output = popen("ppd terminal","r");
    sleep(5);

    bool keep_reading = true; 
    int current_line = 1;
    do {
        fgets(buffer, MAX_LINE, output);
        buffer_search = strncmp(error_message, buffer, 7);

        if (current_line > 2) {
            keep_reading = false;
        }
        else if (buffer_search == 0) {
            system("gnome-terminal -- ./smt1"); //this comand is to restart this same program on a new terminal
            system("ppd start");
            sleep(5);
                
            while (1);
        }
        current_line++;

    } while (keep_reading == true);
    
    sleep(30);

    system("ppd terminal && startmining");
    sleep(300);
    
    FILE *output2;
    char buffer2[MAX_LINE];
    char error_message2[] = "dial unix /home";
    int buffer_search2;
    int timer = 1;
    bool no_error = true;

    do {
        output2 = popen("startmining", "r");
        while (timer < 300) {
            fgets(buffer2, MAX_LINE, output2);
            buffer_search2 = strncmp(error_message2, buffer2, 15);
            if (buffer_search2 == 0) {
                system("exit");
                system("gnome-terminal -- ./smt1"); 
                system("exit");
                no_error = false;
                pclose(output2);
            }
            sleep (1);
            timer++;
        }
    } while (no_error == true);

    return 0;
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source