• Uncategorized

About c : How-do-I-execute-an-external-program-within-C-code-in-Linux-with-arguments

Question Detail

I want to execute another program within C code.
For example, I want to execute a command

./foo 1 2 3

foo is the program which exists in the same folder, and 1 2 3 are arguments.
foo program creates a file which will be used in my code.

How do I do this?

Question Answer

For a simple way, use system():

#include <stdlib.h>
...
int status = system("./foo 1 2 3");

system() will wait for foo to complete execution, then return a status variable which you can use to check e.g. exitcode (the command’s exitcode gets multiplied by 256, so divide system()’s return value by that to get the actual exitcode: int exitcode = status / 256).

The manpage for wait() (in section 2, man 2 wait on your Linux system) lists the various macros you can use to examine the status, the most interesting ones would be WIFEXITED and WEXITSTATUS.

Alternatively, if you need to read foo’s standard output, use popen(3), which returns a file pointer (FILE *); interacting with the command’s standard input/output is then the same as reading from or writing to a file.

The system function invokes a shell to run the command. While this is convenient, it has well known security implications. If you can fully specify the path to the program or script that you want to execute, and you can afford losing the platform independence that system provides, then you can use an execve wrapper as illustrated in the exec_prog function below to more securely execute your program.

Here’s how you specify the arguments in the caller:

const char    *my_argv[64] = {"/foo/bar/baz" , "-foo" , "-bar" , NULL};

Then call the exec_prog function like this:

int rc = exec_prog(my_argv);

Here’s the exec_prog function:

static int exec_prog(const char **argv)
{
    pid_t   my_pid;
    int     status, timeout /* unused ifdef WAIT_FOR_COMPLETION */;

    if (0 == (my_pid = fork())) {
            if (-1 == execve(argv[0], (char **)argv , NULL)) {
                    perror("child process execve failed [%m]");
                    return -1;
            }
    }

#ifdef WAIT_FOR_COMPLETION
    timeout = 1000;

    while (0 == waitpid(my_pid , &status , WNOHANG)) {
            if ( --timeout < 0 ) {
                    perror("timeout");
                    return -1;
            }
            sleep(1);
    }

    printf("%s WEXITSTATUS %d WIFEXITED %d [status %d]\n",
            argv[0], WEXITSTATUS(status), WIFEXITED(status), status);

    if (1 != WIFEXITED(status) || 0 != WEXITSTATUS(status)) {
            perror("%s failed, halt system");
            return -1;
    }

#endif
    return 0;
}

Remember the includes:

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

See related SE post for situations that require communication with the executed program via file descriptors such as stdin and stdout.

You can use fork() and system() so that your program doesn’t have to wait until system() returns.

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

int main(int argc,char* argv[]){

    int status;

    // By calling fork(), a child process will be created as a exact duplicate of the calling process.
    // Search for fork() (maybe "man fork" on Linux) for more information.
    if(fork() == 0){ 
        // Child process will return 0 from fork()
        printf("I'm the child process.\n");
        status = system("my_app");
        exit(0);
    }else{
        // Parent process will return a non-zero value from fork()
        printf("I'm the parent.\n");
    }

    printf("This is my main program and it will continue running and doing anything i want to...\n");

    return 0;
}

system() executes a shell which is then responsible for parsing the arguments and executing the desired program. To execute the program directly, use fork() and exec() (which is what system() uses to execute the shell as well as what the shell itself uses to execute commands).

#include <unistd.h>

int main() {
     if (fork() == 0) {
          /*
           * fork() returns 0 to the child process
           * and the child's PID to the parent.
           */
          execl("/path/to/foo", "foo", "arg1", "arg2", "arg3", 0);
          /*
           * We woundn't still be here if execl() was successful,
           * so a non-zero exit value is appropriate.
           */
          return 1;
     }

     return 0;
}

In C

#include <stdlib.h>

system("./foo 1 2 3");

In C++

#include <cstdlib>

std::system("./foo 1 2 3");

Then open and read the file as usual.

How about like this:

char* cmd = "./foo 1 2 3";
system(cmd);

Here’s the way to extend to variable args when you don’t have the args hard coded (although they are still technically hard coded in this example, but should be easy to figure out how to extend…):

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

int argcount = 3;
const char* args[] = {"1", "2", "3"};
const char* binary_name = "mybinaryname";
char myoutput_array[5000];

sprintf(myoutput_array, "%s", binary_name);
for(int i = 0; i < argcount; ++i)
{
    strcat(myoutput_array, " ");
    strcat(myoutput_array, args[i]);
}
system(myoutput_array);

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.