execvp() using vector<string *> - c++

_arguments = std::vector<std::string *>();
I have above vector declared and I want to use it for execvp()
int execvp(const char *file, char *const argv[]);
How do I convert the type to fit into this function?
It should be something like this
execvp(_arguments[0], _arguments)

You will need to create a new array of pointers to the strings' data, instead of to the std::strings themselves. There are many ways to do this, but one simple way is to use another std::vector and a structured for loop:
assert(_arguments.size() > 0); // else the executable will be invalid
std::vector<const char *> args;
args.reserve(_arguments.size() + 1);
for(string *sp: _arguments) {
args.push_back(sp->c_str());
}
args.push_back(nullptr); // needed to terminate the args list
execvp(args[0], args.data());

You can't use it directly. You'll need to copy pointers to each string's internal buffer into another vector.
Assuming arguments_ is initialized like this:
std::vector<std::string*> arguments_ = {
new std::string("mycmd"),
new std::string("arg1"),
new std::string("arg2")
}
You can call execvp something like this:
// execvp needs a NULL terminator, so make argv one element longer
// than arguments_
std::vector<char*> argv(arguments_.size() + 1);
// copy pointers to each string's buffer into the new vector
std::transform(arguments_.begin(), arguments_.end(), argv.begin(),
[](std::string* arg) {
return arg->data();
});
// The last element of argv was implicitly initialized to a null pointer
// so this isn't actually necessary.
// I just included it to be explicit
argv.back() = nullptr;
// pass a pointer to the array of char*s to execvp
execvp(argv[0], argv.data());
Note that this example relies on the non-const qualified overload of std::string::data added in C++17. Use &(*arg)[0] instead if you're using an older revision of the C++ standard.

_arguments = std::vector<std::string *>();
The standard string class manages its internal data as char array.
To access the internal data, call the method data(). The return type is const char*.
By the way, the pointer type for std::string is not necessary.
Also have a look on the reference: http://www.cplusplus.com/reference/string/string/data/

Related

Wrong types involving converting a vector to feed to execvp

I have a vector of strings vector<string> args that I need to convert to a c string to in turn run as arguments through an execvp call. I keep getting the error "invalid conversion from const char* to char* for the line in my loop. I don't understand how to fix this while still resulting in something I can feed to execvp. While I have seen similar posts, none of the solutions I have come across seem to fix my issue.
int argsLen = c->args.size();
char **argv = new char* [c->args.size() + 1];
for (int index = 0; index < c->args.size(); ++index)
argv[index] = c->args[index].c_str();//note that c is the structure in which args is contained
argv[c->args.size() + 1] = NULL;
//execvp(argv[0], argv);
If you want to do this with no chance of leaking memory or memory overwrite, you can do the following:
typedef std::vector<char> CharArray;
typedef std::vector<CharArray> ArgumentVector;
//...
ArgumentVector argVec;
std::vector<char *>argv;
for (int index = 0; index < c->args.size(); ++index)
{
// get the command and create copy
argVec.push_back(CharArray(c->args[index].begin(), c->args[index].end()));
// get pointer to newly created entity and place in argv vector
argV.push_back(argVec.back().data());
}
// the last argument is NULL
argV.push_back(NULL);
execvp(argV[0], // the first item in the array
argV.data(), // the char** we are sending
All we did was create a std::vector<char*>, and the way we set the pointers in this vector is to point to data from another vector. Once the std::vector<char*> is populated, then char** is just a pointer to the first item.
BTW, this is one of the legitimate reasons to use a vector<char *> as opposed to a vector<string>. In most other cases, if you want a vector of strings, you use vector<string>.
c_str() returns a const char pointer to its internal buffer, whose contents you are not allowed to change (thats why its const) and obviously cannot assign to your char buffer. You need to allocate memory for each string, and copy it:
argv[index] = new char[c->args[index].length()];
strcpy(argv[index], c->args[index].c_str());
include cstring header for strcpy.
Maybe this can help:
char const **argv = new const char* [c->args.size() + 1];

execvp not working when converting from vector<string> to vector<char*> to char**

Going from a vector of strings to a vector of char* to a char**, was working when the argument came in as char**, but the conversion seems to have a problem and I'm not able to find the difference.
Is there a better way to do this?
vector<string> args;
/* code that correctly parses args from user input */
pid_t kidpid = fork();
if (kidpid < 0)
{
perror("Internal error: cannot fork.");
return -1;
}
else if (kidpid == 0)
{
// I am the child.
vector<char*>argcs;
for(int i=1;i<args.size();i++)
{
char * temp = new char[args.at(i).length()];
for(int k=0;k<args.at(i).length();k++)
{
temp[k] = args.at(i).at(k);
}
argcs.push_back(temp);
}
char** argv = new char*[argcs.size() + 1];
for (int i = 0; i < argcs.size(); i++)
{
argv[i] = argcs[i];
}
argv[args.size()] = NULL;
execvp(program, args);
return -1;
}
First, there's no point in copying the std::strings if the next thing you are going to do is call execvp.
If the execvp succeeds, then it will never return and the entire memory image will vanish into smoke (or, more accurately, be replaced by a completely new image). In the course of constructing the new image, exec* will copy the argv array (and the environment array) into it. In any event, the std::vector and std::string destructors will never be invoked.
If, on the other hand, the execvp fails, then the argument passed into it will not have been modified. (Posix: "The argv[] and envp[] arrays of pointers and the strings to which those arrays point shall not be modified by a call to one of the exec functions, except as a consequence of replacing the process image.")
In either case, there was no need to copy the character strings. You can use std::string::c_str() to extract a pointer to the underlying C string (as a const char*, but see below).
Second, if you're using C++11 or more recent, std::vector conveniently comes with a data() member function which returns a pointer to the underlying storage. So if you have std::vector<char*> svec, then svec.data() will be the underlying char*[], which is what you want to pass into execvp.
So the problem reduces to creating a std::vector<char*> from a std::vector<std::string>, which is straightforward:
else if (kidpid == 0) {
// I am the child.
std::vector<char*> argc;
// const_cast is needed because execvp prototype wants an
// array of char*, not const char*.
for (auto const& a : args)
argc.emplace_back(const_cast<char*>(a.c_str()));
// NULL terminate
argc.push_back(nullptr);
// The first argument to execvp should be the same as the
// first element in argc, but we'll assume the caller knew
// what they were doing, and that program is a std::string.
execvp(program.c_str(), argc.data());
// It's not clear to me what is returning here, but
// if it is main(), you should return a small positive value
// to indicate an error
return 1;
}

Needing to use char** in C++ instead of std::string*

I am working on an assignment for my operating systems class. We have the option of using C or C++, so I decided to use C++ since I practised it on the job a bit more recently than C.
I will need to call (from "
$ man execvp " on Linux)
int execvp(const char *file, char *const argv[]);
which (unless I am mistaken) means I need a C-style char* array (a string array in C), and will not be able to use std::string from C++.
My question is: what is the proper way to make/use char* arrays instead of string arrays in C++? Most people tend to say malloc is not used any more in C++ (which I tried now with some complications)
char** cmdList = (char**)malloc(128 * sizeof(char*));
but I don't know how to make a char* array without. Is it still appropriate to solve this how I would in C even though I'm using C++? I haven't ever run into a circumstance where I couldn't use string in C++.
Thanks for everyone's time.
If you put your arguments into a std::vector<std::string>, as you should in C++, then you need a small conversion to get to the char** that execvp wants. Luckily, both std::vector and std::string are continuous in memory. However, a std::vector<std::string> isn't an array of pointers, so you need to create one. But you can just use a vector for that too.
// given:
std::vector<std::string> args = the_args();
// Create the array with enough space.
// One additional entry will be NULL to signal the end of the arguments.
std::vector<char*> argv(args.size() + 1);
// Fill the array. The const_cast is necessary because execvp's
// signature doesn't actually promise that it won't modify the args,
// but the sister function execlp does, so this should be safe.
// There's a data() function that returns a non-const char*, but that
// one isn't guaranteed to be 0-terminated.
std::transform(args.begin(), args.end(), argv.begin(),
[](std::string& s) { return const_cast<char*>(s.c_str()); });
// You can now call the function. The last entry of argv is automatically
// NULL, as the function requires.
int error = execvp(path, argv.data());
// All memory is freed automatically in case of error. In case of
// success, your process has disappeared.
Instead of malloc use new[]:
char ** cmdlist = new char*[128];
There is no need for sizeof, since new knows the size of the type it creates. For classes this also calls the default constructor if it exists. But be careful: If there is no (public) default constructor for a type you can not use new[].
Instead of free use delete[] to release your memory when you are done:
delete[] cmdlist;
Of course, you could also use a vector. This has the advantage that the memory used to store the vector's content is automatically released when the vector is destroyed.
#include <vector>
...
std::vector<char*> cmdlist(128, nullptr); // initialize with nullpointers
// access to entries works like with arrays
char * firstCmd = cmdList[0];
cmdlist[42] = "some command";
// you can query the size of the vector
size_t numCmd = cmdlist.size();
// and you can add new elements to it
cmdlist.push_back("a new command");
...
// the vector's internal array is automatically released
// but you might have to destroy the memory of the char*s it contains, depending on how they were created
for (size_t i = 0; i < cmdlist.size(); ++i)
// Free cmdlist[i] depending on how it was created.
// For example if it was created using new char[], use delete[].
Assuming you have an std::vector<std::string> args variable that represents the argument list, you could do the following to get a C-style array of strings:
auto argvToPass = std::make_unique<const char*[]>(args.size() + 1);
int i = 0;
for (const auto& arg : args)
{
argvToPass[i++] = arg.c_str();
}
// make we have a "guard" element at the end
argvToPass[args.size()] = nullptr;
execvp(yourFile, argvToPass.get());
You can create an array of const char* and still use strings by using string.c_str() the code will look like this
const char ** argv = new const char*[128];
string arg1 = "arg";
argv[0] = arg1.c_str();
If you want to get fancy (and safe) about it you can use std::unique_ptr to good effect to ensure everything gets properly deleted/freed in the event of an error or an exception:
// Deleter to delete a std::vector and all its
// malloc allocated contents
struct malloc_vector_deleter
{
void operator()(std::vector<char*>* vp) const
{
if(!vp)
return;
for(auto p: *vp)
free(p);
delete vp;
}
};
// self deleting pointer (using the above deleter) to store the vector of char*
std::unique_ptr<std::vector<char*>, malloc_vector_deleter> cmds(new std::vector<char*>());
// fill the vector full of malloc'd data
cmds->push_back(strdup("arg0"));
cmds->push_back(strdup("arg1"));
cmds->push_back(strdup("arg2"));
// did any of the allocations fail?
if(std::find(cmds->begin(), cmds->end(), nullptr) != cmds->end())
{
// report error and return
}
cmds->push_back(nullptr); // needs to be null terminated
execvp("progname", cmds->data());
// all memory deallocated when cmds goes out of scope
which (unless I am mistaken) means I need a C-style char* array (a string array in C), and will not be able to use std::string from C++.
No you can still use string from C++. The string class has a constructor that takes C-string:
char str[] = "a string";
string cppStr(str);
Now you can manipulate your string using the string class in C++.

How to pass a vector of strings to execv

I have found that the easiest way to build my program argument list is as a vector of strings. However, execv expects an array of chars for the second argument. What's the easiest way to get it to accept of vector of strings?
execv() accepts only an array of string pointers. There is no way to get it to accept anything else. It is a standard interface, callable from every hosted language, not just C++.
I have tested compiling this:
std::vector<string> vector;
const char *programname = "abc";
const char **argv = new const char* [vector.size()+2]; // extra room for program name and sentinel
argv [0] = programname; // by convention, argv[0] is program name
for (int j = 0; j < vector.size()+1; ++j) // copy args
argv [j+1] = vector[j] .c_str();
argv [vector.size()+1] = NULL; // end of arguments sentinel is NULL
execv (programname, (char **)argv);
The prototype for execv is:
int execv(const char *path, char *const argv[]);
That means the argument list is an array of pointers to null-terminated c strings.
You have vector<string>. Find out the size of that vector and make an array of pointers to char. Then loop through the vector and for each string in the vector set the corresponding element of the array to point to it.
I stumbled over the same problem a while ago.
I ended up building the argument list in a std::basic_string<char const*>. Then I called the c_str() method and did a const_cast<char* const*> on the result to obtain the list in a format that execv accepts.
For composed arguments, I newed strings (ordinary strings made of ordinary chars ;) ), took their c_str() and let them leak.
The const_cast is necessary to remove an additional const as the c_str() method of the given string type returns a char const* const* iirc. Typing this, I think I could have used std::basic_string<char*> but I guess I had a reason...
I am well aware that the const-casting and memory leaking looks a bit rude and is indeed bad practise, but since execv replaces the whole process it won't matter anyway.
Yes, it can be done pretty cleanly by taking advantage of the internal array that vectors use. Best to not use C++ strings in the vector, and const_cast string literals and string.c_str()'s to char*.
This will work, since the standard guarantees its elements are stored contiguously (see https://stackoverflow.com/a/2923290/383983)
#include <unistd.h>
#include <vector>
using std::vector;
int main() {
vector<const char*> command;
// do a push_back for the command, then each of the arguments
command.push_back("echo");
command.push_back("testing");
command.push_back("1");
command.push_back("2");
command.push_back("3");
// push NULL to the end of the vector (execvp expects NULL as last element)
command.push_back(NULL);
// pass the vector's internal array to execvp
execvp(command[0], const_cast<char* const*>(command.data()));
return 1;
}
Code adapted from: How to pass a vector to execvp
Do a const_cast to avoid the "deprecated conversion from string constant to 'char*'". String literals are implemented as const char* in C++. const_cast is the safest form of cast here, as it only removes the const and does not do any other funny business. execvp() will not edit the values anyway.
If you want to avoid all casts, you have to complicate this code by copying all the values to char* types not really worth it.
Although if the number of arguments you want to pass to execv/execl is known, it's easier to write this in C.
You can't change the how execv works (not easily anyway), but you could overload the function name with one that works the way you want it to:
int execv(const string& path, const vector<string>& argv) {
vector<const char*> av;
for (const string& a : argv) {
av.push_back(a.c_str());
av.push_back(0);
return execv(path.c_str(), &av[0]);
}
Of course, this may cause some confusion. You would be better off giving it a name other than execv().
NB: I just typed this in off the top of my head. It may not work. It may not even compile ;-)

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

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++?
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);
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"
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);
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.