2

I have a script below, that directs the redirects the data coming in from /dev/ttyUSB1 to a file.

#!/bin/bash
# start minicom with file name of todays date
cat /dev/ttyUSB1 > ~/serial_logs/$1.txt &

I call this script from a c program using system(), which creates a file with the current date and time.

//Get string with date & time
//Open minicom with logging to filename with date and time
strftime(s, sizeof(s), "%a%b%d%T", tm);
snprintf(buf, SM_BUF, "~/logging.sh %s",s);
system(buf); 

However at a later time in the c program I need to kill this process. System() doesn't return the PID of the cat process.

I came across this post which suggests to use the function below, but it is not that clear to me how I use it.

My question essentially is what do I pass as the arguments to it?

pid_t system2(const char * command, int * infp, int * outfp)
{
    int p_stdin[2];
    int p_stdout[2];
    pid_t pid;

    if (pipe(p_stdin) == -1)
        return -1;

    if (pipe(p_stdout) == -1) {
        close(p_stdin[0]);
        close(p_stdin[1]);
        return -1;
    }

    pid = fork();

    if (pid < 0) {
        close(p_stdin[0]);
        close(p_stdin[1]);
        close(p_stdout[0]);
        close(p_stdout[1]);
        return pid;
    } else if (pid == 0) {
        close(p_stdin[1]);
        dup2(p_stdin[0], 0);
        close(p_stdout[0]);
        dup2(p_stdout[1], 1);
        dup2(::open("/dev/null", O_RDONLY), 2);
        /// Close all other descriptors for the safety sake.
        for (int i = 3; i < 4096; ++i)
            ::close(i);

        setsid();
        execl("/bin/sh", "sh", "-c", command, NULL);
        _exit(1);
    }

    close(p_stdin[0]);
    close(p_stdout[1]);

    if (infp == NULL) {
        close(p_stdin[1]);
    } else {
        *infp = p_stdin[1];
    }

    if (outfp == NULL) {
        close(p_stdout[0]);
    } else {
        *outfp = p_stdout[0];
    }

    return pid;
}
riverrock
  • 97
  • 2
  • 8
  • Duplicate of this https://stackoverflow.com/questions/22802902/how-to-get-pid-of-process-executed-with-system-command-in-c ? – fghj Jul 27 '17 at 10:51
  • Possible duplicate of [How to get pid of process executed with system() command in c++](https://stackoverflow.com/questions/22802902/how-to-get-pid-of-process-executed-with-system-command-in-c) – fghj Jul 27 '17 at 10:51
  • @MarkPlotnick yes sorry, I'm looking for the PID of the cat process. – riverrock Jul 27 '17 at 10:58
  • Using `system()` is almost never the correct answer. You should replace the whole thing with `fork()` and a few lines of code to implement the "cat". – John Hascall Jul 27 '17 at 11:50
  • I've updated my question to incorporating the suggestion in [How to get pid of process executed with system() command in c++ ](https://stackoverflow.com/questions/22802902/how-to-get-pid-of-process-executed-with-system-command-in-c) – riverrock Jul 27 '17 at 11:50

3 Answers3

1

what do I pass as the arguments to it?

The function accepts pointers to integers (int*) and is writing to those integer addresses, hence you need to have two integers and pass their addresses like this:

int input, output; // input and output file descriptors
int pid = system2("script args", &input, &output);
mariusm
  • 1,483
  • 1
  • 11
  • 26
1

You're taking completely the wrong approach to this problem.

The sample code you posted using fork/exec and pipes is the right way to run an external command and read its output. However, the command you're running is cat. All this does is read a file and output it.

So rather than starting a separate process to call cat, just open the file and read from it:

FILE *fp = fopen("/dev/ttyUSB1", "r");
if (!fp) {
    perror("open failed");
    exit(1);
} else {
    // read from file
    ...
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • It sounds like the OP wants the reading (and writing to a file) to happen "in the background", to be killed at some point in the future. So even if they replaced the use of `cat`, they'd still need to do the reading and writing inside a `fork`ed copy of the code. – TripeHound Jul 27 '17 at 12:47
  • Hi @dbush, thanks for your helpful comment. @TripeHound yes what I wanted was to start reading (only) from the serial port to a file and then a later point stop reading into the file. So in order to do this I needed to know the PID of the cat process so that I could kill it from the c program using `system()` and `pkill`. – riverrock Jul 27 '17 at 15:55
  • 1
    @riverrock I'm not in a position to throw any code together, but you should be able to just use `fork` to create a child process (returns a PID) and instead of that executing `cat`, just have it loop repeatedly reading from USB1 and writing to a file. Meanwhile, the parent process can then do what it needs and kill the child when it needs to. – TripeHound Jul 27 '17 at 16:03
1

While you can use fork()/exec() to launch an external command (while acquiring a PID), as @dbush's answer points out, the operation you need (read from one file and write to another) is simple enough that you can do it in code. However, since you want this to run in the background, until the main code decides to stop it, you will still need to use fork(). The following code shows the basic idea:

#include <stdio.h>
#include <signal.h>

int main()
{
    // Prevent killed child-processes remaining as "defunct"
    struct sigaction sigchld_action = {
        .sa_handler = SIG_DFL,
        .sa_flags = SA_NOCLDWAIT
    };
    sigaction( SIGCHLD, &sigchld_action, NULL ) ;

    // Duplicate ("fork") the process. Will return zero in the child
    // process, and the child's PID in the parent (or negative on error).
    int pid = fork() ;
    if( pid < 0 ) {
        printf( "Fork failed\n" ) ;
        return 1 ;
    }

    if( pid == 0 ) {
        // ------------ Child process
        // Here is where you'd open "/dev/ttyUSB1" and the output file
        // and continually read data from one and write it to the other.
        while( 1 ) {
            printf( "child process\n" ) ;
            sleep(1) ;
        }
        return 0 ; //never reached
    }

    // ------------ Parent process
    // Here is where you'd do whatever the main program needs to do.
    printf( "Main program\n" ) ;
    sleep(10);

    // When you are ready, it can kill the child process
    printf( "Killing child\n" ) ;
    kill( pid, SIGTERM ) ;

    printf( "Finished\n" ) ;
    return 0 ;
}

The actual implementation of the read-write is left as an exercise to the reader.

Note: Without the sigaction call at the top of the code, the child process(es) will remain as <defunct> processes, with the expectation that the parent process will wait for them to finish so it can examine their exit status. In this case, we're not interested in any return codes so we let them die immediately.

TripeHound
  • 2,721
  • 23
  • 37