Starting a process using posix_spawn - c++

i am using the folowing code to launch the new process in Linux
pid_t processID;
char *argV[] = {"192.168.1.40",(char *) 0};
int status = -1;
status = posix_spawn(&processID,"/home/user/application",NULL,NULL,argV,environ);
if(status == 0)
std::cout<<"Launched Application";
else
std::cout<<"Launching application Failed";
Application did launches but says no command line argument. What is the error in posix_spawn arguments?

From the posix_spawn manual page:
The argument argv is a pointer to a null-terminated array of character pointers to null-terminated character strings. These strings construct the argument list to be made available to the new process. At least argv[0] must be present in the array, and should contain the file name of the program being spawned, e.g. the last component of the path or file argument.
What's happening is that in the launched process, argv[0] will be 192.168.1.40 instead of the name of the executable, and there are no arguments to the program after that.
so change:
char *argV[] = {"192.168.1.40",(char *) 0};
to:
char *argV[] = {"/home/user/application", "192.168.1.40",(char *) 0};
The behaviour of the argv array is mentioned more explicitly later on:
When a program is executed as a result of a posix_spawn() or posix_spawnp() call, it is entered as follows:
main(argc, argv, envp)
int argc;
char **argv, **envp;
where argc is the number of elements in argv (the ''arg count'') and argv points to the array of character pointers to the arguments themselves.

Related

Issues with converting from string to char** while creating a command prompt

I am creating a command prompt and I take input from the user as a string and convert it to a char** using a parse function I have created. I can not see any issues with it but after running a few commands in my prompt I begin to run into the error of "corrupted size vs prev_size 0x00c06738. Here is my parse function. Any help would be appreciated.
char** parse(string s){
vector<string> commandvector;
istringstream iss(s);
for(string s; iss>>s;)
commandvector.push_back(s);
char** argv = new char*[commandvector.size() + 1];
for(int i = 0; i<commandvector.size();i++){
argv[i] = new char[commandvector[i].size()];
strcpy(argv[i],commandvector[i].c_str());
}
argv[commandvector.size() + 1] = NULL;
return argv;
}
If you want to use C-style strings, you have to take into account the null-terminator char.
While std::basic_string::c_str returns a pointer to a null-terminated (since C++11) array of char, std::basic_string::size doesn't count the null terminator, so in order to allocate enough memory, you have to add 1 to returned value.
Besides, as UnholySheep already noticed, you are accessing the argv array outside its bounds, beeing argv[commandvector.size()] its last element.

Passing non-NULL argv to MPI_Comm_spawn

Suppose that my program (let's call it prog_A) starts as a single MPI process.
And later I want program prog_A to spawn n MPI processes (let's call them prog_B) using MPI_Comm_spawn with the same arguments I passed to prog_A.
For example, if I run prog_A with the arguments 200 100 10
mpiexec -n 1 prog_A 200 100 10
I want prog_B to be provided with the same argments 200 100 10.
How can I do this? I tried the following but it does not work.
char ** newargv = new char*[3];//create new argv for childs
newargv[0] = new char[50];
newargv[1] = new char[50];
newargv[2] = new char[50];
strcpy(newargv[0],argv[1]);//copy argv to newargv
strcpy(newargv[1],argv[2]);
strcpy(newargv[2],argv[3]);
MPI_Comm theother;
MPI_Init(&argc, &argv);
MPI_Comm_spawn("prog_B",newargv,numchildprocs,
MPI_INFO_NULL, 0, MPI_COMM_SELF, &theother,
MPI_ERRCODES_IGNORE);
MPI_Finalize();
Your problem is that you didn't NULL terminate your argv list. Here's the important part of the MPI standard (emphasis added):
The argv argument argv is an array of strings containing arguments
that are passed to the program. The first element of argv is the first
argument passed to command, not, as is conventional in some contexts,
the command itself. The argument list is terminated by NULL in C and
C++ and an empty string in Fortran. In Fortran, leading and trailing
spaces are always stripped, so that a string consisting of all spaces
is considered an empty string. The constant MPI_ARGV_NULL may be used
in C, C++ and Fortran to indicate an empty argument list. In C and
C++, this constant is the same as NULL.
You just need to add a NULL to the end of your list. Here's the corrected code (translated to C since I didn't have the C++ bindings installed on my laptop):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mpi.h"
int main(int argc, char ** argv) {
char ** newargv = malloc(sizeof(char *)*4);//create new argv for childs
int numchildprocs = 1;
MPI_Comm theother;
MPI_Init(&argc, &argv);
MPI_Comm_get_parent(&theother);
if (MPI_COMM_NULL != theother) {
fprintf(stderr, "SPAWNED!\n");
} else {
newargv[0] = (char *) malloc(sizeof(char)*50);
newargv[1] = (char *) malloc(sizeof(char)*50);
newargv[2] = (char *) malloc(sizeof(char)*50);
newargv[3] = NULL;
strncpy(newargv[0],argv[1], 50);//copy argv to newargv
strncpy(newargv[1],argv[2], 50);
strncpy(newargv[2],argv[3], 50);
fprintf(stderr, "SPAWNING!\n");
MPI_Comm_spawn("./prog_B",newargv,numchildprocs,
MPI_INFO_NULL, 0, MPI_COMM_SELF, &theother,
MPI_ERRCODES_IGNORE);
}
MPI_Comm_free(&theother);
MPI_Finalize();
}
You do not need to copy the argument vector at all. All you have to do is make use of the provisions of the C99 standard, which requires that argv should be NULL-terminated:
MPI_Comm theother;
// Passing &argc and &argv here is a thing of the past (MPI-1)
MPI_Init(NULL, NULL);
MPI_Comm_spawn("prog_B", argv+1, numchildprocs,
MPI_INFO_NULL, 0, MPI_COMM_SELF, &theother,
MPI_ERRCODES_IGNORE);
MPI_Finalize();
Note the use of argv+1 in order to skip over the first argument (the program name). The benefit of that code is that it works with any number of arguments passed to the original program.

Updating code from one command line argument to two

I have code which does the following :
const char* filename = argc >= 2 ? argv[1] : "stuff.jpg";
which reads in a photo as a command line argument and displays it.
I now want to take in two photo's, I tried this code :
const char* filename = argc >= 3 ? argv[1] : "stuff.jpg", argv[2] : "tester.jpg";
But I get an error like this :
error: expected initializer before ‘:’ token
Anybody know whats wrong? is there a similer way to do this input programmatically?
You're dealing with a ternary if-operator here. Have a look at this page. It's basically an inline if-statement.
Code that would do what you're looking for, looks something a little like this:
const char* filename1 = argc >= 2 ? argv[1] : "stuff.jpg";
const char* filename2 = argc >= 3 ? argv[2] : "tester.jpg";
That leaves you with two filename variables, storing either the supplied argument or the default values (stuff.jpg and tester.jpg, respectively).
To get all the arguments in an easy to use format I do:
int main(int argc, char* argv[])
{
std::vector<std::string> args(&argv[1], &argv[argc]);
// args.size() is the number of arguments.
// In your case the number of files.
// So now you can just loop over the file names and display each one.
// Note The above is guranteed to be OK
// As argv[] will always have a minimum of 2 members.
// argv[0] is the command name thus argc is always >= 1
// argv[argc] is always a NULL terminator.
}
What happens when you need 4, 5, or more photos?
Pseudo code:
vector<char *> photos;
if(argc > 1)
{
for i to argc-1
photos.push_back(argv[i]) ;
}

C/C++ Pointers and Arrays help

I have the following C++ code and I can't seem to get it working. What I am trying to do is read numerous entries from the command line, separated by ('|') pipe characters, and then splitting the resulting strings by spaces.
eg.
mkdir C:/unixcode/shells|cd D:/margins/code | pwd| finger kobojunkie | last -l kobojunkie
but so far, I get errors, something about declaring the size of the pointer:
Initializer fails to determine the size of argv2
cannot convert char** to char* for argument 1 to char strtok(char*, const char*)
int main(int argc, char *argv[])
{
char * pch;
pch = strtok (argv,"|");
//parse the contents of the generated arrays
while (pch != NULL)
{
printf ("%s\n",pch);
char * argv2[] = pch;
char * subpch = strtok(argv2," ");
while (subpch !=NULL)
{
printf ("%s\n",subpch);
subpch = strtok (NULL, " ");
}
pch = strtok (NULL, " ");
}
return 0;
}
the type of argv is char**, not char* hence you cannot pass it to strtok. Use argv[ 1 ] instead, but check that argc >= 2 first.
Or, since this is tagged c++, use stl to split the string, eg
void split( const std::string& s, char delim, std::vector<std::string>& elems )
{
std::stringstream ss( s );
std::string item;
while( std::getline( ss, item, delim ) )
if( !item.empty() )
elems.push_back( item );
}
int main( int argc, char *argv[] )
{
if( argc == 2 )
{
std::vector< std::string > elements;
split( argv[ 1 ], '|', elements );
//elements now contains all items..
}
}
The command-line is managed by a program: the shell (probably cmd.exe in Windows or bash in Linux). That shell gets all the stuff written in the command line and parses it and executes the commands specified.
Unless you are writing a shell, you cannot ever see the "|" of your example command-line inside the programs you write. They are effectively processed from the shell and removed from the parameters sent to the programs.
In
mkdir C:/unixcode/shells|cd D:/margins/code | pwd| finger kobojunkie | last -l kobojunkie
the shell calls the 5 following commands, each with the parameters specified
mkdir C:/unixcode/shells
cd D:/margins/code
pwd
finger kobojunkie
last -l kobojunkie
Note none of the programs receive a "|".
If you are indeed writing a shell, the command-line is not available in the argv array. It depends on the way you manage input inside your shell.
argv is an array of arrays pointers. You cannot pass it as is to strtok: you need to pass its elements in a loop
for (k = 1; k < argc; k++) {
pch = strtok(argv[k], "|");
/* ... */
}
Also: are you sure you want to delimit with "|"? That character has a special meaning to shells and, usually, does not make it to your program.
Unless you call your program with them escaped, eg
bash$ ./a.out 'one|two|three' 'four|five|six'
argv is not a string. argv is an array of strings. strtok takes a string, so you cannot pass it an array of strings and expect it to do something meaningful.
Each string element of the argv array is a separate command line parameter, except for the first which is the name of the executable. So what you should be doing is looking through each string entry for "|", and acting accordingly.

invalid conversion from ‘char*’ to ‘char’

I have a,
int main (int argc, char *argv[])
and one of the arguements im passing in is a char. It gives the error message in the title when i go to compile
How would i go about fixing this?
Regards
Paul
When you pass command line parameters, they are all passed as strings, regardless of what types they may represent. If you pass "10" on the command line, you are actually passing the character array
{ '1', '0', '\0' }
not the integer 10.
If the parameter you want consists of a single character, you can always just take the first character:
char timer_unit = argv[2][0];
If you only ever want the first character from the parameter the folowing will extract it from string:
char timer_unit = argv[2][0];
The issue is that argv[2] is a char* (C-string) not char.
You are probably not passing in what you think (though this should come from the command line). Please show the complete error message and code, but it looks like you need to deal with the second argument as char *argv[], instead of char argv[] -- that is, as a list of character arrays, as opposed to a single character array.
Everything stays strings when you pass them in to your program as arguments, even if they are single characters. For example, if your program was called "myprog" and you had this at the command line:
myprog arg1 53 c a "hey there!"
Then what you get in the program is the following:
printf("%d\n", argc);
for(int i = 0; i < argc; i++)
{
printf("%s\n", argv[0]);
}
The output of that would be:
6
myprog
arg1
53
c
a
hey there!
The point being that everything on the command line turns into null-terminated strings, even single characters. If you wanted to get the char 'c' from the command line, you'd need to do this:
char value = argv[3][0];
not
char value = argv[3]; // Error!
Even the value of "53" doesn't turn into an int. you can't do:
int number = argv[2]; // Error!
argv[2] is { '5', '2', '\0' }. You have to do this:
int number = atoi(argv[2]); // Converts from a string to an int
I hope this is clear.
Edit: btw, everything above is just as valid for C (hence the printf statements). It works EXACTLY the same in C++.