Saik February 2016

Call a c program from C++ and pass arguments

I have a c++ program, and at some point in my program I need to call a c program and pass some arguments to it.

I am working in linux env.

the file simpsh is a compiled c file in the same dir. resulting_simpsh_command is a string with data of this type --creat --trunc --wronly f1 and so far.

When I check the values that I recieve in C program, it shows this instead

void execute_simpsh(string resulting_simpsh_command)
{
    pid_t pid = fork();

    if(pid == -1)
        perror("Fork failed!");
    else if(pid ==0)
    {
        char* args[256];

        string simpsh ="./simpsh";
        args [0] = (char*) simpsh.c_str();
        string temp_option_holder="";

        int nextCommand=1;

        for(int i=0;i<resulting_simpsh_command.length();i++)
        {
            if(resulting_simpsh_command[i] !=' ')
            {
                temp_option_holder += resulting_simpsh_command[i];
            }
            else
            {
                cout<<"saving to argument: "<<temp_option_holder<<endl;
                args [nextCommand] = (char*) temp_option_holder.c_str();
                temp_option_holder="";
                nextCommand +=1;
            }

        }


        cout<<"command numbers "<<nextCommand<<endl;
        args [nextCommand + 1] = NULL;

        if(execvp(args[0],args) == -1)
            cout<<"Failed to open simpsh, maybe you didnt compile?"<<endl;
        exit(1);
    }
    else
    {
        //not important for now

    }
}

Answers


molbdnilo February 2016

A lot of the args array will be invalid pointers due to your (mis)use of c_str.
The pointer becomes invalid as soon as the string's buffer is reallocated.
Some of the args pointers could also point to the same thing even though they're valid.

Two options for fixing this (off the top of my head):

Dynamic allocation:

args[nextCommand] = strdup(temp_option_holder.c_str());

which requires deallocation later.

Or, if you can live with limiting the argument length, you can use another array and avoid the memory management (but then you need to worry about overflows instead):

char arg_strings[256][ARGUMENT_LENGTH] = {0};
char* args[256] = {0};

// ...
assert(temp_option_holder.length() < ARGUMENT_LENGTH);
strncpy(arg_strings[nextCommand], temp_option_holder.c_str(), ARGUMENT_LENGTH);
args[nextCommand] = arg_strings[nextCommand];


DevSolar February 2016

C++ has so many nice things beyond std::cout. Use them!

The core of this solution is std::replace()ing the spaces in your command line with zero bytes, then taking pointers to the beginning of each (C) string in the command line (conveniently using the zero bytes from the replace() as terminators), pushing those pointers into a vector we will then pass to execve().

#include <string>
#include <vector>
#include <algorithm>
#include <iostream>

// Usually I would recommend using const std::string &
// to avoid the copy, but we are going to do violence
// on the argument so we really *want* a writeable copy.
void execute_simpsh( string cmd )
{
    pid_t pid = fork();

    if ( pid == -1 )
    {
        perror("Fork failed!");
    }
    else if ( pid == 0 )
    {
        // This will hold our pointers -- and is not limited
        // to a maximum amount other than available RAM
        std::vector< char * > args( { "./simpsh" } );

        if ( ! cmd.empty() )
        {
            // Tokenize -- std::string may contain null chars!
            std::replace( cmd.begin(), cmd.end(), ' ', '\0' );

            // std::string is zero-terminated in memory, but that
            // zero char is not considered by .find() so we add one,
            // making the loop below easier.
            cmd.append( '\0' );

            size_t i = 0;

            // Gather pointers to each argument. find_first_not_of()
            // skips leading (and embedded) ex-spaces.
            while ( ( i = cmd.find_first_not_of( '\0', i ) ) != std::string::npos )
            {
                args.push_back( cmd.data() + i );
                // find() skips to the end of the current argument.
                i = cmd.find( '\0', i );
            }
        }

        std::cout << "Tokens: " << args.size() << "\n";

        // args.data() is std::vector's underlying array
   

Post Status

Asked in February 2016
Viewed 2,277 times
Voted 9
Answered 2 times

Search




Leave an answer