How to deal with spaces within paths when using the system()? - c++

I am still new to C++ and am working out a way to open a program within my C++ program.
The problem is that whenever I have spaces in my paths, it sees it as different parameters.
int _tmain(int argc, _TCHAR* argv[])
{
system("C:\\Users\\blah\\Desktop\\a\\ b.txt");
return 0;
}
The output I receive is:
'C:\Users\blah\Desktop\a\' is not recognized as an internal or external command, operable program or batch file.

You can double quote your string literal. Edit: Also just noticed that your backslashes were not escaped so updated below :P
system("\"C:\\Users\\blah\\Desktop\\a\\ b.txt\"");
Also let it be known for the record that you really shouldn't use system. Try fork, spawn, or perhaps even the unofficial boost.process class which has functionality similar to .NET process class depending on your needs. Also think about why you need launch a process from a process ... perhaps you could make a library?

On Unix, you could use fork() + exec().
On Windows, check out spawn.
These execute the program directly, avoiding the command shell interpreter, thus avoiding any special treatment of special characters like spaces.

Related

Running the command line dot program from a Qt application generates no output, what is wrong?

I have an app that generates a dependencies.dot file which I then want to convert to an SVG image.
When I do that from a simple application like so:
int main(int argc, char * argv[])
{
system("dot -Tsvg ../BUILD/dependencies.dot > ../BUILD/dependencies.svg");
return 0;
}
It works great. The SVG image is present and working.
When I instead run it from my Qt application, the SVG file is created (by the shell), but it remains empty.
Any idea what could prevent dot from outputting data to its stdout?
Just in case, I also tried a cat to send the input through stdin instead of a filename:
system("cat ../BUILD/dependencies.dot | dot -Tsvg > ../BUILD/dependencies.svg");
And that didn't make any differences.
Also using the full path (/usr/bin/dot) did not help either.
Another test, I tried to use popen() and the first fread() immediately returns 0 (i.e. the mark of EOF).
It may not be Qt, but something is interacting with dot's ability to do anything. Any pointers on why that is would be wonderful.
Maybe an important note? I start my app. from a console, so stdin, stdout and stderr should all work as expected. I actually can see debug logs appearing there and other apps seem to work just as expected (i.e. My Qt app. can successfully run make, for example).
Here is an example of the resulting SVG (when I don't run it from within my Qt app):
For reference, the source code can be found on github. This is part of the snapbuilder. A tool that I use to run a build on launchpad. It's still incomplete, but it's getting there.
https://github.com/m2osw/snapcpp/tree/master/snapbuilder
The specific function to look for: project::generate_svg().
I still have no clue what side effect Qt has on system() that the dot command would fail. However, if using my own fork() + execve(), then it works.
I wanted a new process class for my environment, so I implemented that. This newer version is using FIFOs or directly opening closing files that it passes to the process.
...
// write the script in `std::stringstream dot` then:
//
std::string script(dot.str());
g_dot_process = std::make_shared<cppprocess::process>("dependencies");
g_dot_process->set_command("/usr/bin/dot");
g_dot_process->add_argument("-Tsvg");
g_dot_process->add_input(cppprocess::buffer_t(script.data(),
script.data() + script.length()));
g_dot_process->set_capture_output();
g_dot_process->set_output_capture_done(output_captured);
g_dot_process->start(); // TODO: check return value for errors
...
// and in output_captured()
//
void snap_builder::svg_ready(std::string const & svg)
{
std::string const svg_filename(...);
{
std::ofstream out;
out.open(svg_filename);
if(!out.is_open())
{
std::cerr << "error: \n";
return;
}
out.write(svg.c_str(), svg.size());
}
dependency_tree->load(QString::fromUtf8(svg_filename.c_str()));
}
Now the dot file is generated and displayed as expected.
This is rather strange since most everything else I've done with a simple system() call works as expected. There must be something about stdin or stdout that makes dot not do its work.

Are ALL system() calls a security risk in c++?

A post in this (Are system() calls evil?) thread says:
Your program's privileges are inherited by its spawned programs. If your application ever runs as a privileged user, all someone has to do is put their own program with the name of the thing you shell out too, and then can execute arbitrary code (this implies you should never run a program that uses system as root or setuid root).
But system("PAUSE") and system("CLS") shell to the OS, so how could a hacker possibly intervene if it ONLY shells to a specific secure location on the hard-drive?
Does explicitly flush—by using fflush or _flushall—or closing any stream before calling system eliminate all risk?
The system function passes command to the command interpreter, which executes the string as an operating-system command. system uses the COMSPEC and PATH environment variables to locate the command-interpreter file CMD.exe. If command is NULL, the function just checks whether the command interpreter exists.
You must explicitly flush—by using fflush or _flushall—or close any stream before you call system.
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/system-wsystem
In case, there are any doubts here's the actual snippet from the MS' implementation (very simple and straightforward):
// omitted for brevity
argv[1] = _T("/c");
argv[2] = (_TSCHAR *) command;
argv[3] = NULL;
/* If there is a COMSPEC defined, try spawning the shell */
/* Do not try to spawn the null string */
if (argv[0])
{
// calls spawnve on value of COMSPEC vairable, if present
// omitted for brevity
}
/* No COMSPEC so set argv[0] to what COMSPEC should be. */
argv[0] = _T("cmd.exe");
/* Let the _spawnvpe routine do the path search and spawn. */
retval = (int)_tspawnvpe(_P_WAIT,argv[0],argv,NULL);
// clean-up part omitted
As to concerns of what _tspawnvpe may actually be doing, the answer is: nothing magical. The exact invocation sequence for spawnvpe and friends goes as following (as anybody with licensed version of MSVC can easily learn by inspecting the spanwnvpe.c source file):
Do some sanity checks on parameters
Try to invoke _tspawnve on the passed file name. spawnve will succeed if the file name represents an absolute path to an executable or a valid path relative to the current working directory. No further checks are done - so yes, if a file named cmd.exe exists in current directory it will be invoked first in the context of system() call discussed.
In a loop: obtain the next path element using `_getpath()
Append the file name to the path element
Pass the resulted path to spwanvpe, check if it was successful
That's it. No special tricks/checks involved.
The original question references POSIX not windows. Here there is no COMSPEC (there is SHELL but system() deliberately does not use it); however /bin/sh is completely, utterly vulnerable.
Suppose /opt/vuln/program does system("/bin/ls"); Looks completely harmless, right? Nope!
$ PATH=. IFS='/ ' /opt/vuln/program
This runs the program called bin in the current directory. Oops. Defending against this kind of thing is so difficult it should be left to the extreme experts, like the guys who wrote sudo. Sanitizing environment is extremely hard.
So you might be thinking what is that system() api for. I don't actually know why it was created, but if you wanted to do a feature like ftp has where !command is executed locally in the shell you could do ... else if (terminalline[0] == '!') system(terminalline+1); else ... Since it's going to be completely insecure anyway there's no point in making it secure. Of course a truly modern use case wouldn't do it that way because system() doesn't look at $SHELL but oh well.

Why does using system("some.exe") in C++ method not work like the command line?

I am writing a program for Windows that eventually has to launch a different pre-existing .exe that sits on the same computer. It passes multiple parameters to this .exe file. I am reading the actual command and parameters and constructing the command but I also tried hard coding it with the same results. Here's the hard coded version (I picked this out of an older C program that uses the same.exe):
system("c://IQapture//dmon2_6_IHD -p2 c://IQapture//mon_table_101_Tx8.txt 11 0 0");
So in the original program inside int _cdecl main(int argc, char**argv) this use of system works. In my C++ program inside a C++ class method when I issue the command the correct program launches but it immediately puts up an error dialog stating that an error has occurred. I echo'd the system string used to launch the exe out to the console. Right after it fails, I copy and paste the same line that was echo'd and this time the exe runs without error. This is repeatable. In case it was timing related I tried adding a 10 second delay before issuing the system command but it didn't matter. Plus the original older program doesn't require a delay. This implies to me that the string is correct and the target program works. Somehow the system() invocation is different from a direct command line invocation. The program compiles and builds fine. I'm using Visual Studio 2010.
Does anyone have ideas on how to make the system() invocation work like the command line invocation?
That really doesn't look like the kind of thing that Windows would be happy with... Try it with backslashes instead:
system("c:\\IQapture\\dmon2_6_IHD -p2 c:\\IQapture\\mon_table_101_Tx8.txt 11 0 0");
If that still doesn't work, you quite likely have one of the following issues:
Your current working directory is wrong;
An environment variable is missing;
Your program is running with the wrong user permissions;
Your program is tying up a resource that the spawned process requires (eg you have not closed a file that it requires as input).
There are a lot of things to consider - the environment, the user running the program, the parent process and what's inherited... Take a look at the parameters to the CreateProcess function. Chances are your system call's invocation isn't matching the command line's (though that may not be the issue, simpler things are more likely.)
I'd advise working backwards from the error to rule out simple causes such as the environment, current directory, etc. before delving into such things as creation flags and security attributes.
You have your slashes backwards. Try:
system("c:/IQapture/dmon2_6_IHD -p2 c:/IQapture/mon_table_101_Tx8.txt 11 0 0");
You can use the backslash \ but because that is an escape sequence starter in a string (for C/C++) that is why you use two in a row. As the compiler will convert \\ into a single slahs in the string:
Thus:
system("c:\\IQapture\\dmon2_6_IHD -p2 c:\\IQapture\\mon_table_101_Tx8.txt 11 0 0");
// Is equivelent to the command line string:
> c:\IQapture\dmon2_6_IHD -p2 c:\IQapture\mon_table_101_Tx8.txt 11 0 0
But Windows has supported both types of slashes for longer than I can remember. So the following command line is equivalent.
> c:/IQapture/dmon2_6_IHD -p2 c:/IQapture/mon_table_101_Tx8.txt 11 0 0
Using '/' in a string (in C/C++) does not require escaping. So you just need to use it as is:
system("c:/IQapture/dmon2_6_IHD -p2 c:/IQapture/mon_table_101_Tx8.txt 11 0 0");

Problems with system() calls in Linux

I'm working on a init for an initramfs in C++ for Linux. This script is used to unlock the DM-Crypt w/ LUKS encrypted drive, and set the LVM drives to be available.
Since I don't want to have to reimplement the functionality of cryptsetup and gpg I am using system calls to call the executables. Using a system call to call gpg works fine if I have the system fully brought up already (I already have a bash script based initramfs that works fine in bringing it up, and I use grub to edit the command line to bring it up using the old initramfs). However, in the initramfs it never even acts like it gets called. Even commands like system("echo BLAH"); fail.
So, does anyone have any input?
Edit: So I figured out what was causing my errors. I have no clue as to why it would cause errors, but I found it.
In order to allow hotplugging, I needed to write /sbin/mdev to /proc/sys/kernel/hotplug...however I ended up switching around the parameters (on a function I wrote myself no less) so I was writing /proc/sys/kernel/hotplug to /sbin/mdev.
I have no clue as to why that would cause the problem, however it did.
Amardeep is right, system() on POSIX type systems runs the command through /bin/sh.
I doubt you actually have a legitimate need to invoke these programs you speak of through a Bourne shell. A good reason would be if you needed them to have the default set of environment variables, but since /etc/profile is probably also unavailable so early in the boot process, I don't see how that can be the case here.
Instead, use the standard fork()/exec() pattern:
int system_alternative(const char* pgm, char *const argv[])
{
pid_t pid = fork();
if (pid > 0) {
// We're the parent, so wait for child to finish
int status;
waitpid(pid, &status, 0);
return status;
}
else if (pid == 0) {
// We're the child, so run the specified program. Our exit status will
// be that of the child program unless the execv() syscall fails.
return execv(pgm, argv);
}
else {
// Something horrible happened, like system out of memory
return -1;
}
}
If you need to read stdout from the called process or send data to its stdin, you'll need to do some standard handle redirection via pipe() or dup2() in there.
You can learn all about this sort of thing in any good Unix programming book. I recommend Advanced Programming in the UNIX Environment by W. Richard Stevens. The second edition coauthored by Rago adds material to cover platforms that appeared since Stevens wrote the first edition, like Linux and OS X, but basics like this haven't changed since the original edition.
I believe the system() function executes your command in a shell. Is the shell executable mounted and available that early in your startup process? You might want to look into using fork() and execve().
EDIT: Be sure your cryptography tools are also on a mounted volume.
what do you have in initramfs ? You could do the following :
int main() {
return system("echo hello world");
}
And then strace it in an initscript like this :
strace -o myprog.log myprog
Look at the log once your system is booted

Executing a command from C++, What is expected in argv[0]?

I am using execv() to run commands from /bin/ such as 'ls', 'pwd', 'echo' from my c++ program, and I am wondering what value I should provide in argv[0];
const char * path = getPath();
char ** argv = getArgs();
execv(path,argv);
argv[0] is supposed to be the program name. It's passed to the program's main function. Some programs differentiate their behavior depending on what string argv[0] is. For example the GNU bash shell will disable some of its features if called using sh instead of bash. Best give it the same value that you pass to path.
In linux, argv[0] is the process name displayed by the top utility (which it probably gets from reading entries in /proc/)
argv[0] should be the full path of the command that you want to run.
I know that this is not the answer you're looking for but is there a specific reason why you're doing this? The reason I ask is that most if not all of the actions people normally run with either system() or execv() are available in libraries on either Windows or Unix and are safer, faster and less likely to suffer from circumstantial errors. By that I mean, for example, when the PATH changes and suddenly your code stops working.
If you're passing in a string, either in whole or in part, and running it then you also leave yourself open to a user gaining access to the system by entering a command that could be damaging. E.g. imagine you've implemented a file search using find /home -name and your user types in:
"%" -exec rm {} \;
Ouch!