iwlist tries to scan all interfaces when called using execl - c++

I am doing a standard fork/execl in C++ on my Ubuntu PC to scan for Wi-Fi SSIDs. It looks like the interface name is not taking effect when called with excel.
execl( "/sbin/iwlist", "wlp4s0", "scanning", (char*) NULL );
This succeeds but I get this in stderr:
lo Interface doesn't support scanning.
enp0s25 Interface doesn't support scanning.
It looks like iwlist is trying to scan all interfaces.
If instead I do:
system( "/sbin/iwlist wlp4s0 scanning" );
I do not get these messages in stderr.
Is there something I am doing wrong in my execl call?

Figured it out.
In bash you always write something like
command arg1 arg2 ...
When the program 'command' parses the its arguments, the first argument (argv[0]) is 'command'.
However, when you call 'command' using using execl like this:
execl( "/path/to/command", "arg1", "arg2", (char*) NULL );
the first argument it gets is 'arg1'. This clearly does not work if the command is expecting this as argv[1] and is instead getting it as argv[0].
This explains why the command was ignoring my "wlp4s0" ifname. It was simply ignoring the first argument which it expected to be the command name/path.
So the solution to my original problem is this:
execl( "/sbin/iwlist", "iwlist", "wlp4s0", "scanning", (char*) NULL );

Related

how to add a space character to trigger var args c++ execlp()

I'm writing this program on Ubuntu.
If I type this command into a shell
groups root sys bin
it outputs
root : root
sys : sys
bin : bin
However I'm writing a c++ program that calls groups with execlp using
execlp("groups", "groups", args.c_str(), NULL);
where args = "root sys bin". I just get a :No such user error since groups' is obviously just looking at that entire string as argv[0] which is the equivalent of running
groups "root sys bin"
How do i create the proper variable argument for execlp to run groups on each user, one at a time?
One option is to ask /bin/sh to deal with the input the way it normally would. Of course in addition to dealing with spaces, this would also deal with characters like $, #, ~, *, etc., which may or may not be what you want.
execl("/bin/sh", "sh", "-c", ("groups " + args).c_str(), nullptr);
Obviously, don't use this way if the data is user-entered and might contain nasty strings like:
root ;rm *
Otherwise, execl type functions won't work unless you know the number of command-line arguments at compile time. Assuming your args string could have varying numbers of arguments, you'll need execv type functions instead.
std::string args = "root sys bin";
std::vector<std::string> arg_vec;
std::istringstream arg_iss(args);
std::copy(std::istream_iterator<std::string>(arg_iss),
std::istream_iterator<std::string>(),
std::back_inserter(arg_vec));
char groups_exec[] = "groups";
std::vector<char*> arg_ptr_vec{ groups_exec };
std::for_each(arg_vec.begin(), arg_vec.end(),
[&](std::string& arg){ arg_ptr_vec.push_back(&arg[0]); } );
arg_ptr_vec.push_back(nullptr);
execvp("groups", arg_ptr_vec.data());
The args parameter to execlp is defined as "char *const argv[]", so I think you could do something like
const char *myArgs[3] = {"root, "sys", "bin"};
and then replace args.c_str() with myArgs.
I should admit to having zero experience of writing software for Ubuntu - this is what I would try next were I trying to get execlp to work.
EDIT: This is wrong - I had got mixed up and was looking at execv(), bobah and sleepy42 seem to have got it.

How to pass arguments when calling `source` command

I need to run source command from c++ program and pass filename and also some arguments. Is it possible? I want to use them in script like command line arguments (with argc, argv0, ...). http://www.astro.princeton.edu/~rhl/Tcl-Tk_docs/tcl/source.n.html here is not specified how to do it.
When doing this from C or C++, you should:
Initialise the Tcl library and create a Tcl interpreter.
Set the global variables argv0, argv and argc to the values expected by a normal Tcl script. This is exactly what tclsh does; the variables are entirely ordinary apart from being initialised this way.
argv0 is the name of the “main” script, which might be the script you're about to source.
argv is a Tcl list of all the other arguments; argc is the length of that list.
Use Tcl_FSEvalFileEx(interp,pathPtr,encoding) to execute the file; the source command is a very thin wrapper around that call. You probably want to pass the encoding argument as NULL, and the pathPtr argument is a Tcl_Obj reference.
If your script accepts the arguments in argv, just set this variable before you source this script.
But if this script calls exit, it will terminate the entire process, usually not what you want. You could use a slave interp to avoid this.
There are 3 predefined variables:
$argc - number items of arguments passed to a script.
$argv - list of the arguments.
$argv0 - name of the script.
So in your case, assuming the file sourced is in the same directory and its name is passed as a first argument:
source [lindex $argv 0]

QProcess not working on win32 systems?

The process method is not working if I pass the user home directory programmatically in windows XP and windows 32 bit systems
The below code works fine:
QProcess process;
process.execute("C:/DOCUME~1/pjo/myprok/tmp/APP.exe");
Not working Code:
Here I am getting the path of the APP.exe using QDir::homePath
process.execute("C:/Documents and Settings/pjo/myprok/tmp/APP.exe");
The errorString returns "UnKnown error"
I tried with start method also which never works:
B Not working Code:
process.start("C:/Documents and Settings/pjo/myprok/tmp/APP.exe");
Error: Path Not found
process.start("C:/DOCUME~1/pjo/myprok/tmp/APP.exe");
Error : Unknown error
execute() is a static function, so it should be called like this:
QProcess::execute("C:/Documents and Settings/pjo/myprok/tmp/APP.exe");
You are saying that you get the home directory programmatically, but the code you show does not do that. Maybe you are creating the path like this:
QProcess::execute(QDir::homePath() + "APP.exe");
and then the path will miss / between directory and filename like this:
"C:/Documents and Settings/pjo/myprok/tmpAPP.exe"
Your issue is probably due quoting issues caused by the spaces in the path (C:\Documents and Settings...).
Note that there are two overloads for start():
void start ( const QString & program, OpenMode mode = ReadWrite )
void start ( const QString & program, const QStringList & arguments, OpenMode mode = ReadWrite )
You are using the first, which takes the executable path and all args in one string, and expects it to be quoted correctly. Without quoting, "c:\documents" is interpreted as the executable and "and" "Settings..." etc. as the arguments.
The second version takes the arguments separately, and will interpret the executable path correctly, without any quoting needed. Thus, the easiest way is to use
process.start("C:/Documents and Settings/pjo/myprok/tmp/APP.exe", QStringList());
This ensure the second version to be used, and should save you from all quoting issues.
I suggest to always use that overload.
The same applies to execute(), which is, as already said, a static method, so the error codes of the QProcess object won't be set.

posix_spawn a shell script, trouble passing arguments

I need a to be able to use some of the process control facilities provided by posix_spawn() like suspend/resume/kill etc, something that system() does not give me.
So use the following call to posix_spawn() to launch a shell script from a c++ program:
int result = posix_spawn(&spawnedPid, processExecutable, 0, 0, argumentList, 0);
where processExecutable is "foo.sh" and argumentList is {"bar",0,0,0}; The process foo.sh starts up just fine, except for the argumentList part. If I do something like "echo $1" in foo.sh, I get an empty string. If I launch a binary the same way, the arguments are just fine and I can reach them via argv[]. Is there anything I should be doing differently in order to pass arguments to foo.sh ?
Shame on me, the argumentList parameter of posix_spawn() is 0-based so echo $1 actually outputs argumentList[1], which is, naturally, set to 0. Case closed.

Can system() return before piped command is finished

I am having trouble using system() from libc on Linux. My code is this:
system( "tar zxvOf some.tar.gz fileToExtract | sed 's/some text to remove//' > output" );
std::string line;
int count = 0;
std::ifstream inputFile( "output" );
while( std::getline( input, line != NULL ) )
++count;
I run this snippet repeatedly and occasionally I find that count == 0 at the end of the run - no lines have been read from the file. I look at the file system and the file has the contents I would expect (greater than zero lines).
My question is should system() return when the entire command passed in has completed or does the presence of the pipe '|' mean system() can return before the part of the command after the pipe is completed?
I have explicitly not used a '&' to background any part of the command to system().
To further clarify I do in practice run the code snippet multiples times in parallel but the output file is a unique filename named after the thread ID and a static integer incremented per call to system(). I'm confident that the file being output to and read is unique for each call to system().
According to the documentation
The system() function shall not return until the child process has terminated.
Perhaps capture the output of "output" when it fails and see what it is? In addition, checking the return value of system would be a good idea. One scenario is that the shell command you are running is failing and you aren't checking the return value.
system(...) calls the standard shell to execute the command, and the shell itself should return only after the shell has regained control over the terminal. So if there's one of the programs backgrounded, system will return early.
Backgrounding happens through suffixing a command with & so check if the string you pass to system(...) contains any & and if so make sure they're properly quoted from shell processing.
System will only return after completion of its command and the file output should be readable in full after that. But ...
... multiple instances of your code snippet run in parallel would interfere because all use the same file output. If you just want to examine the contents of output and do not need the file itself, I would use popen instead of system. popen allows you to read the output of the pipe via a FILE*.
In case of a full file system, you could also see an empty output while the popen version would have no trouble with this condition.
To notice errors like a full file system, always check the return code of your calls (system, popen, ...). If there is an error the manpage will tell you to check errno. The number errno can be converted to a human readable text by strerror and output by perror.