Convert std::vector<char*> to a c-style argument vector arv
Asked Answered
A

4

5

I would like to prepare an old-school argument vector (argv) to use within the function

int execve(const char *filename, char *const argv[],char *const envp[]);

I tried it with the stl::vector class:

std::string arguments = std::string("arg1");    
std::vector<char*> argv; 
char argument[128];
strcpy(argument, arguments.c_str());
argv.push_back(argument); 
argv.push_back('\0'); // finish argv with zero

Finally I pass the vector to execve()

execve("bashscriptXY", &argv[0], NULL)

The code compiles but ArgV gets "ignored" by execve(). So it seems to be wrong, what I'm trying. How should I build an argV in a efficient way with c++?

Adaiha answered 5/11, 2009 at 20:12 Comment(1)
Why bother copying the string into an array? If you have a std::string, and you want a char* C-style NUL terminated string, then that's what c_str() returns already.Tugboat
D
5

I think the char[128] is redundant as the string local will have the same lifetime, also, try adding the program as argv[0] like rossoft said in his answer:

const std::string arguments("arg1");    
std::vector<const char*> argv;

argv.push_back("bashscriptXY");
// The string will live as long as a locally allocated char*
argv.push_back(arguments.c_str()); 
argv.push_back(NULL); // finish argv with zero

execve(argv[0], &argv[0], NULL);
Deidradeidre answered 5/11, 2009 at 20:34 Comment(3)
For "finish argv with zero" I'd prefer "argv.push_back(0)" or "argv.push_back(NULL)". The "argv.push_back('\0')" leads to the correct result, but makes you think too much about what is happening here - and there is the danger of mis-writing it as "argv.push_back("\0")" which would be wrong...Felicidadfelicie
Agreed, I'm a fan of saying what you mean.Deidradeidre
"...as the string local will have the same lifetime..." It has indeed, but changes to the string after having taken its c_str() could be disastrous. So you might want to make it a const string.Hercule
H
5

Beware that argv[0] is always the name of the command itself. So if you want to pass 1 parameter to a program, you have to fill two elements:

argv[0] should be "bashscriptXY" (or whatever you want...)
argv[1] = "your_argument"

Hildredhildreth answered 5/11, 2009 at 20:17 Comment(0)
D
5

I think the char[128] is redundant as the string local will have the same lifetime, also, try adding the program as argv[0] like rossoft said in his answer:

const std::string arguments("arg1");    
std::vector<const char*> argv;

argv.push_back("bashscriptXY");
// The string will live as long as a locally allocated char*
argv.push_back(arguments.c_str()); 
argv.push_back(NULL); // finish argv with zero

execve(argv[0], &argv[0], NULL);
Deidradeidre answered 5/11, 2009 at 20:34 Comment(3)
For "finish argv with zero" I'd prefer "argv.push_back(0)" or "argv.push_back(NULL)". The "argv.push_back('\0')" leads to the correct result, but makes you think too much about what is happening here - and there is the danger of mis-writing it as "argv.push_back("\0")" which would be wrong...Felicidadfelicie
Agreed, I'm a fan of saying what you mean.Deidradeidre
"...as the string local will have the same lifetime..." It has indeed, but changes to the string after having taken its c_str() could be disastrous. So you might want to make it a const string.Hercule
C
2

A few issues:

  • the first element of the argv array is supposed to be the program name
  • the argv vector can take char const* types as execve() doesn't modify the args (though the process that gets invoked might - but those will be copies in its own address space). So you can push c_str() strings onto it. (See comments)

Try this out:

// unfortunately, execve() wants an array of `char*`, not
//  am array of `char const*`.
//  So we we need to make sure the string data is 
//  actually null terminated
inline
std::string& null_terminate( std::string& s)
{
    s.append( 1, 0);
    return s;
}

// ...

std::string program = std::string( "bashscriptXY");
std::string arg1("arg1");
std::string arg2("arg2");

null_terminate(program);
null_terminate(arg1);
null_terminate(arg2);

std::vector<char *> argv; 

argv.push_back( &program[0]);
argv.push_back( &arg1[0]); 
argv.push_back( &arg2[0]); 
argv.push_back( NULL); // finish argv with zero

intptr_t result = execve( program.c_str(), &argv[0], NULL);
Clepsydra answered 5/11, 2009 at 21:2 Comment(4)
This fails to compile here without adding a const_cast. "invalid conversion from "const char**" to "char* const*".Vertical
@Parker: where would you put a const_cast here?Misadvise
@Nathan: it turns out Parker was right - the POSIX standard gives a type of char * const [] to the argv parameter (even if a copy of the strings is made for the new process), so the compiler complains if you try to pass it an array of char const*. I must have tested my snippet on a not-quite-compliant set of headers (probably MSVC's process.h). You could put a const_cast<char**> on the &argv[0] argument, but I'm not sure I'm thrilled with that.Clepsydra
@Nathan: I've updated the answer - to be honest, I think the simple const_cast might be better...Clepsydra
A
1

Unless the arguments are hard-coded, you'll have to do some dynamic memory allocation. First, create an array of character pointers for the number of arguments, then allocate memory for each argument and copy the data in.

This question is very similar and includes some code solutions.

Alagoas answered 5/11, 2009 at 20:19 Comment(2)
"execve() does not return on success, and the text, data, bss, and stack of the calling process are overwritten by that of the program loaded. The program invoked inherits the calling process's PID, and any open file descriptors that are not set to close on exec." - I think dynamic allocation would leak, I don't think execve free()s the argv char*sDeidradeidre
@joshperry: I think that's OK, though. You expect a program's argv array to be "leaked", in the sense that the C runtime (in this case the OS) does it for you at program (process) exit. An OS which doesn't clean up leaked resources for you on exit probably isn't going to implement any of the exec functions to actually do anything.Tugboat

© 2022 - 2024 — McMap. All rights reserved.