How to get forkpty/execvp() to properly handle redirection and other bash-isms? - c++

I've got a GUI C++ program that takes a shell command from the user, calls forkpty() and execvp() to execute that command in a child process, while the parent (GUI) process reads the child process's stdout/stderr output and displays it in the GUI.
This all works nicely (under Linux and MacOS/X). For example, if the user enters "ls -l /foo", the GUI will display the contents of the /foo folder.
However, bash niceties like output redirection aren't handled. For example, if the user enters "echo bar > /foo/bar.txt", the child process will output the text "bar > /foo/bar.txt", instead of writing the text "bar" to the file "/foo/bar.txt".
Presumably this is because execvp() is running the executable command "echo" directly, instead of running /bin/bash and handing it the user's command to massage/preprocess.
My question is, what is the correct child process invocation to use, in order to make the system behave exactly as if the user had typed in his string at the bash prompt? I tried wrapping the user's command with a /bin/bash invocation, like this: /bin/bash -c the_string_the_user_entered, but that didn't seem to work. Any hints?
ps Just calling system() isn't a good option, since it would cause my GUI to block until the child process exits, and some child processes may not exit for a long time (if ever!)

If you want the shell to do the I/O redirection, you need to invoke the shell so it does the I/O redirection.
char *args[4];
args[0] = "bash";
args[1] = "-c";
args[2] = ...string containing command line with I/O redirection...;
args[4] = 0;
execv("/bin/bash", args);
Note the change from execvp() to execv(); you know where the shell is - at least, I gave it an absolute path - so the path-search is not relevant any more.

Related

Transition to command prompt once my console program finishes?

I'm writing some executables that use the Windows console, in C and C++.
I'm trying to get the console to not close after the logic of my program finishes... But not just merely pause or wait, I'd like it to become a cmd.exe command line console itself, ready to accept new prompts.
Essentially I'd like the behavior of running my program via double-clicking or drag-and-dropping to be equivalent to hitting winkey + r and running :
cmd /k "program.exe [list of drag+drop files if any]"
(While not opening a new console if run from a command-line itself.)
Is this possible at all?
Edit
I've been tinkering with this and arrived to a solution that seems to work:
std::getenv("PROMPT") will return 0 when not run from the commandline (I think anyway, not sure if that holds in all cases), and so that can be used to fork the logic depending on how the executable is run.
The following code works for me at least, in my limited experimentation with it. If it's run from the explorer, it uses it's first instance to invoke cmd.exe with parameters that lets THAT instance invoke our program again, with the original parameters.
int main(int argc, char * argv[]) {
// checks if we're in the explorer window, if so delegates to a new instance
if (std::getenv("PROMPT") == NULL) {
printf("Starting from explorer...\n");
std::string str("cmd /Q /k \"");
for (uint32 n = 0; n < argc; ++n) {
str.append("\"");
str.append(argv[n]);
str.append("\"");
if(n < argc-1)
str.append(" ");
}
str.append("\"\n");
system(str.c_str());
return 0;
}
// actual code we want to run
uint32 fileCount = 0;
for (uint32 n = 0; n < argc - 1; ++n) {
fileCount++;
printf("file %02u >> %s\n", n, argv[n+1]);
}
if (fileCount == 0) {
printf("No inputs...\n");
}
return 0;
}
So I guess conceptually, it looks like this.
____stays open_______________________ __closes when finished___
program.exe [paramList] ---> cmd.exe -+-> program.exe [paramList]
|
+-> any subsequent commands
|
etc
In my opinion, you have linked your program as a Windows console program, so it will always open a console terminal when you run it. In that case, your program is presenting the information it outputs in the console (as if the standard output had been redirected to the opened window) this means you cannot use it as a UNIX filter like dir or copy commands. (indeed, you are not in a unix system, so the console is emulated with a special windows library that is linked to your program).
To be able to run your program inside a cmd.exe invocation in a normal window terminal (as you do with the dir command --well, dir is internal to cmd.exe, but others, like xcopy.exe aren't), you need to build your program as a different program type (a unix filter command or a windowless console program, I don't remember the program type name as I'm not a frequent windows developer) so the standard input and the standard output (these are things that Windows hinerits from MS-DOS) are preserved on the program that started it, and you program is capable of running with no window at all.
Windows console program is a different thing that a windows filter program that doesn't require a console to run. The later is like any other ms-dos like command (like dir or copy) and they have an interface more similar to the unix like counterparts.
If you do this, you will be able to run your program from cmd in another window, and it will not create a Windows terminal console to show your program output.
You could simply insert the line
system( "cmd" );
at the end of your program, which will call the command prompt after your program finished executing.
However, this may not fulfil your following requirement:
(While not opening a new console if run from a command-line itself.)
Although using system( "cmd" ); will not open a new console window (it will use the existing one), it will create a new process cmd.exe, which means that you will now have 2 cmd.exe processes if your program was invoked by cmd.exe. Also, if the original cmd.exe process was invoked by your own program, then you will now have 2 processes running your program. If you now call your program again from this new command prompt, you will now have 3 cmd.exe processes and 3 processes running your program. This could get ugly very quickly, especially if you are repeatedly calling your program from a batch file.
In order to prevent this, then your program could try to somehow detect whether its parent process already is cmd.exe, and if it was, it should exit normally instead of invoking cmd.exe again.
Unfortunately, the Windows API does not seem to offer any official way for a child process to obtain the process ID of its parent process. However, according to this page, it is possible to accomplish this using undocumented functions.
Using undocumented functions is generally not advisable, though. Therefore, it would probably be better if you always called your program from a command prompt, so that it could simply exit normally.

Is std::system or exec better practice?

I have a program that calls a shell script tool that I made that goes through a directory and zips up files and gets the checksum value and calls some other tools to upload the files. The operation takes roughly 3 to 4 minutes.
I call the script like this:
int result = system("/bin/sh /path/to/my/script");
I've also got the same result by using the exec() family of functions:
int child = fork();
if(child == 0) {
execl( "/bin/sh", "sh", "/path/to/my/script", (char*)0 );
}
I know with exec you can redirect output to the parent program so it can read the output of the command line tools, but other than that when should you use system as opposed to exec?
Ignoring for the time being that use of system is portable while use of exec family of functions is not portable...
When you combine use of exec family of functions with other POSIX functions such as pipe, dup, wait, you get a lot more control over how to pass data between the parent process and the child process.
When you don't need any of those controls, i.e. you just want to execute a command, then using system is preferable, IMO.
The first system call in your question will do the same, what you are doing in the next piece of code (fork and execl)
From documentation:
The system() library function uses fork(2) to create a child process
that executes the shell command specified in command using execl(3)
http://man7.org/linux/man-pages/man3/system.3.html

How can I find why system can not run my application?

I have a c++ program that run a command and pass some arguments to it. The code is as follow:
int RunApplication(fs::path applicationPathName,std::string arguments)
{
std::string applicationShortPath=GetShortFileName(applicationPathName);
std::string cmd="\""+applicationShortPath +"\" "+ arguments+" >>log.txt 2>&1 \"";
std::cout<<cmd<<std::endl;
int result=std::system(cmd.c_str());
return result;
}
When I run system command, the cmd window appears shortly and then closes, but the result is 1 and the cmd was not run (the command should generate output which is not generated).
To check that the cmd is correct, I stopped the application just before system line and copy/ paste cmd content to a cmd window and it worked.
I am wondering how can I find why application is not run in system()?
the cmd has this value just before running it:
"D:/DEVELO~3/x64/Debug/enfuse.exe" -w --hard-mask --exposure-weight=1 --saturation-weight=0.328 --contrast-weight=0.164 -o "C:/Users/m/AppData/Local/Temp/1.tif" "C:/Users/m/AppData/Local/Temp/1.jpg" "C:/Users/m/AppData/Local/Temp/2.jpg" >>log.txt 2>&1 "
How can I find why it is not working?
Is there any way that I set the system so it doesn't close cmd window so I can inspect it?
is there any better way to run a command on OS?
Does Boost has any solution for this?
Edit
After running it with cmd /k, I get this error message:
The input line is too long.
How can I fix it other than reducing cmd line?
There are two different things here: if you have to start a suprocess, "system" is not the best way of doing it (better to use the proper API, like CreateProcess, or a multiplatform wrapper, but avoid to go through the command interpreter, to avoid to open to potential malware injection).
But in this case system() is probably the right way to go since you in fact need the command interpreter (you cannot manage things like >>log.txt 2>&1 with only a process creation.)
The problem looks like a failure in the called program: may be the path is not correct or some of the files it has to work with are not existent or accessible with appropriate-permission and so on.
One of the firt thing to do: open a command prompt and paste the string you posted, in there. Does it run? Does it say something about any error?
Another thing to check is how escape sequence are used in C++ literals: to get a '\', you need '\\' since the first is the escape for the second (like \n, or \t etc.). Although it seems not the case, here, it is one of the most common mistakes.
Use cmd /k to keep the terminal: http://ss64.com/nt/cmd.html
Or just spawn cmd.exe instead and inspect the environment, permissions, etc. You can manually paste that command to see whether it would work from that shell. If it does, you know that paths, permssions and environment are ok, so you have some other issue on your hands (argument escaping, character encoding issues)
Check here How to execute a command and get output of command within C++ using POSIX?
Boost.Process is not official yet http://www.highscore.de/boost/process/

execute and receive the output of mml command in c++

i have an interface where i use to execute the mml command in my solaris unix like below:
> eaw 0004
<RLTYP;
BSC SYSTEM TYPE DATA
GSYSTYPE
GSM1800
END
<
As soon as i do eaw <name> on the command line.It will start an interface where in i can execute mml commands and i can see the output of those commands executed.
My idea here is to parse the command output in c++.
I can do away with some logic for parsing.But to start with How can get the command to be executed inside c++ ? Is there any predefined way to do this.
This should be similar to executing sql queries inside c++.But we use other libraries to execute sql queries.I also donot want to run a shell script or create temporary files in between.
what i want is to execute the command inside c++ and get the output and even that in c++.
could anybody give me the right directions?
You have several options. From easiest and simplest to hardest and most complex to use:
Use the system() call to spawn a shell to run a command
Use the popen() call to spawn a subprocess and either write to its standard input stream or read from its standard output stream (but not both)
Use a combination of pipe(), fork(), dup()/dup2(), and exec*() to spawn a child process and set up pipes for a child process's standard input and output.
The below code is done with the sh command. This redirects stdout to a file named "out" which can be read later to process the output. Each command to the process can be written through the pipe.
#include <stdio.h>
int main()
{
FILE *fp;
fp = popen("sh > out", "w");
if (fp) {
fprintf(fp, "date\n");
fprintf(fp, "exit\n");
fclose(fp);
}
return 0;
}

started from command line?

I have a simple C/CPP process running on a linux system. This is a.out.
Another process is capable to start a.out inside its code. This is b.out.
What code do I need inside a.out to understand that it is executed from the command line?
Eg ./a.out
Is there a way a process to know if it started from the cmd or started from another process?
You can't find out in general whether a program was started "from the command line" (by a user's explicit command), but you can find out whether its standard input and output are talking to a terminal/command window:
#include <unistd.h>
isatty(fileno(stdin))
and stdout return whether standard input/standard output are terminals.
If you need to know what process starting your program, use the getppid system call to get the parent's process ID (ppid), then read the pseudo-file /proc/ppid/cmdline.
You can check its parent task ID, using getppid()
You can do multiple things, but none will be 100% reliable:
isatty(0) to check whether standard input is a TTY terminal,
check for the parent task ID (getppid()), then lookup the parent's PID to match it against its executable's path (using whatever you want. a call to ps and some parsing could do, or have fun using /proc/)
you could also just have a look at the environment variables set up. do a printout of all the values contained in the env. To do that, either use the extern environ variable:
extern char **environ;
or modify your main() prototype to be:
int main(int ac, char **av, char **environ)
I would set an environment variable, in the parent process, to some value (say the parent pid), and have the child process check for it.
It is unlikely that a shell user would set this variable (call it something unlikely to name-clash), so if this variable is set to the expected value, then you know that it is being started from the parent process.
You can check whether its standard input is a terminal:
if(isatty(0)) { ... }
In short: you can't doing it directly.
In long: look you can check the getppid() value and compare it with the bash PID orb.out PID
TO search for a process inside the process table with Known PID with C you can do this:
1) get the PPID of a.out and search with this value in /porc and then if you find the folder check the cmdline file and check if this process is b.out or shell process.
2) you can deal with sysctl system call and dealing with kernel param's(you can google it)
3)
pid_t ppid = getppid();
system("pidof bash > text.in");
the system will get the pid of any shell process and write the result to text.in file
it contains all bash PID's space separated you can compare this values with getppid() value.
Good Luck.