How to run a bash script from C++ program - c++

Bash scripts are very useful and can save a lot of programming time. So how do you start a bash script in a C++ program? Also if you know how to make user become the super-user that would be nice also. Thanks!

Use the system function.
system("myfile.sh"); // myfile.sh should be chmod +x

#include <stdio.h>
#include <stdlib.h>
// ....
system("my_bash_script.sh");

Since this is a pretty old question, and this method hasn't been added (aside from the system() call function) I guess it would be useful to include creating the shell script with the C binary itself. The shell code will be housed inside the file.c source file. Here is an example of code:
#include <stdio.h>
#include <stdlib.h>
#define SHELLSCRIPT "\
#/bin/bash \n\
echo -e \"\" \n\
echo -e \"This is a test shell script inside C code!!\" \n\
read -p \"press <enter> to continue\" \n\
clear\
"
int main() {
system(SHELLSCRIPT);
return 0;
}
Basically, in a nutshell (pun intended), we are defining the script name, fleshing out the script, enclosing them in double quotes (while inserting proper escapes to ignore double quotes in the shell code), and then calling that script's name, which in this example is SHELLSCRIPT using the system() function in main().

The only standard mandated implementation dependent way is to use the system() function from stdlib.h.
Also if you know how to make user become the super-user that would be nice also.
Do you want the script to run as super-user or do you want to elevate the privileges of the C executable? The former can be done with sudo but there are a few things you need to know before you can go off using sudo.

StackOverflow: How to execute a command and get output of command within C++?
StackOverflow: (Using fork,pipe,select): ...nobody does things the hard way any more...
Also if you know how to make user become the super-user that would be nice also. Thanks!
sudo. su. chmod 04500. (setuid() & seteuid(), but they require you to already be root. E..g. chmod'ed 04***.)
Take care. These can open "interesting" security holes...
Depending on what you are doing, you may not need root. (For instance: I'll often chmod/chown /dev devices (serial ports, etc) (under sudo root) so I can use them from my software without being root. On the other hand, that doesn't work so well when loading/unloading kernel modules...)

Related

Include file read from terminal into function using #include

I have written a code which reads in mathematical functions from a text file which are in a C++ compatible format and applies mathematical operations to them. This is a brief summary of the code. I am aware that #include is a preprocessor directive so once the contents of the file have been read in it compiles the code via a bash script each time.
double myfunc(long double x){
return
#include "/.../filename"
}
int main{
"Maths stuff happens here"
}
This works as intended, but I want to be able to speed the process up and read the filename from the terminal rather than entering it into the script each time. To do this I tried using the following:
int g_argc;
char **g_argv;
void process_command_()
{
filename=argv[1]
}
double myfunc(long double x){
return
#include filename
}
int main{
"Maths stuff happens here"
}
It was somewhat of a stretch to think this would work, but I am unsure of how to read the filename from the terminal into my function as though I had typed it in myself due to the function being outside of int main(). I have looked around StackExchange and have found similar problems but none of the solutions has worked for my case.
Any help is greatly appreciated.
Edit for clarity This code is a numerical integration code which takes a very large integrand as input from a text file and the integration is done using the CUBA library. I moved the process from Maple into C++ to get a speed and accuracy increase. The script works wonderfully and can replicate known values ~ 400 times faster than what Maple/Mathematica can do.
Perhaps the easiest way for you would be this.
double myfunc(long double x){
return
#include MY_FILENAME
}
Then when you the compiler in process_command(), pass it an additional argument:
"-DMY_FILENAME=/path/to/your/file"
This is a string you need to build out of argv[1] or whatever stores your filename.
If you invoke a bash script that invokes the compiler, you need to pass your filename as an argument to the script, and arrange for the script to pass it along with -DMY_FILENAME= to the compiler itself.
WhiZTiM's comment/link made me realise this is a really silly question, so apologies for wasting your time. What I want can be done in the bash script. I just define a placeholder in the main.cpp
double myfunc(long double x){
return
#include <filename>
}
Next I put together a simple bash script to copy the main.cpp run file to the directory where the input .txt file is and using sed's find and replace command it switches the filename in main.cpp for the actual file which is taken from the terminal:
input_file=( "$PWD/${#}" )
cp ${HOME}/.../main.cpp $PWD
sed -i "s|filename|${input_file}|g" main.cpp
And then compile and run commands at the end of the script.
Thank you for all your help with this question. I can't believe I didn't see the solution was so simple.

System() function, and calling internet explorer from it, DevC++

I tried making a program that'd take website info, then feed it to system() to start website. I'm aware that characters like (\, ",') don't get fed to the output directly, so I used escape sequences.
I wrote this program, but the command prompt just refuses to go past C:\ path. But if I copy paste the command displayed by my program, internet explorer gets launched. But the case isn't so for my program. Can anybody tell me where is the error?
Here is my code:
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
cout<<"Please enter the website you wish to visit: ";
string website,web;
cin>>web;
web= " " + web;
website = "\"%ProgramFiles%\\Internet Explorer\\iexplore\""+web;
cout<<"\n"<<website<<endl<<endl<<endl;
system(website.c_str());
return 0;
}
You are using an environment variable, %ProgramFiles%, in your system command-line; these are specific to the MS-DOS prompt environment, and generally not available in system implementations.
I suggest replacing that with the full path, such as \"C:\Program Files\Internet Explorer\iexplore\", and see if that works.
If that works, then your implementation doesn't implicitly replace environment variables the way a full Command Prompt does, so you will need to query the environment variable separately and construct the path before you run system. See getenv for one possible way (I'm not sure what mingw32 supports, so you may have other options as well).
If that doesn't remedy the problem, I suggest checking if you can launch something simpler, like notepad.exe, to verify that there is nothing interfering with launching an application in general, such as your environment path or permissions.
Pass it in double double quotes:
website = "\"\"%ProgramFiles%\\Internet Explorer\\iexplore\"\""+web;
The system("something") call actually runs the command interpreter cmd in a way similar (but probably not identical) to cmd /c something. This has implications when there are spaces in the command name, see e.g this. I cannot tell exactly why single double quotes work when there's no environment variable involved, and do not work otherwise, but the fact is, double double quotes do work.
If you want to launch the user's preferred browser, consider calling
system("start http://" + websitename);
instead.
Get that environment variable value first.
#include <iostream>
#include <ShlObj.h>
int main() {
char pathToPf[MAX_PATH];
if (S_OK == SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES, NULL, 0, pathToPf))
std::cout << pathToPf << std::endl;
return 0;
}
See SHGetFolderPath documentation...
Note that I was lazy and using the ASCII version of this function. Use it without the A postfix and deal with the conversation ;)

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/

Permissions issue calling bash script from c++ code that apache is running

The goal of this code is to create a stack trace whenever a sigterm/sigint/sigsegv/etc is caught. In order to not rely on memory management inside of the C++ code in the case of a sigsegv, I have decided to write a bash script that will receive the PID and memory addresses in the trace array.
The Sig events are being caught.
Below is where I generate the call to the bash script
trace_size = backtrace(trace, 16);
trace[1] = (void *)ctx->rsi;
messages = backtrace_symbols(trace, trace_size);
char syscom[356] = {0};
sprintf(syscom,"bash_crash_supp.sh %d", getpid());
for (i=1; i<(trace_size-1) && i < 10; ++i)
{
sprintf(syscom,"%s %p",syscom,trace[i]);
}
Below is where my issue arises. The command in syscom is generating correctly. I can stop the code before the following popen, run the command in a terminal, and it functions correctly.
However running the script directly from the c++ code does not seem to work.
setuid(0);
FILE *bashCommand = popen(syscom,"r");
char buf[256] = {0};
while(fgets(buf,sizeof(buf),bashCommand) != 0) {
LogMessage(LOG_WARNING, "%s\n", buf);
}
pclose(bashCommand);
exit(sig);
The purpose of the bash script is to get the offset from /proc/pid/maps, and then use that to run addr2line to get the file name/line number.
strResult=$(sudo cat /proc/"$1"/maps | grep "target_file" | grep -m 1 '[0-9a-fA-F]')
offset=$( cut -d '-' -f 1 <<< "$strResult");
However offset is getting 0 when I run from the c++ code, but when I run the exact same command (that is stored in syscom in the c++ code) in a terminal, I get the expected output.
I have been trying to fix this for a while. Permissions are most likely the issue, but I've tried to work around these with every way I know of/have seen via google. The user trying to run the script (currently running the c++ code) is apache.
The fix does not need to worry about the security of the box. If something as simple as "chmod 777 /proc -r" worked, that would have been the solution (sadly the OS doesn't let me mess do such commands with /proc).
Things I've already tried:
Adding `` around the command that's stored in syscom from the c++ code
chown the script to apache
chmod 4755 on the bash_crash_supp.sh script, allowing it to always fire as root.
I have added apache to sudoers (visudo), allowing them to run sudo without using a password
I have added a sub file to sudoers.d (just in case) that does the same as above
I have looked into objdump, however it does not give me either the offset or the file/line num for an addr (from what I can see)
I have setuid(0) in the c++ code to set the current user to root
Command generated in C++
bash_crash_supp.sh 25817 0x7f4bfe600ec8 0x7f4bf28f7400 0x7f4bf28f83c6 0x7f4bf2904f02 0x7f4bfdf0fbb0 0x7f4bfdf1346e 0x7f4bfdf1eb30 0x7f4bfdf1b9a8 0x7f4bfdf176b8
Params in bash:
25817 0x7f4bfe600ec8 0x7f4bf28f7400 0x7f4bf28f83c6 0x7f4bf2904f02 0x7f4bfdf0fbb0 0x7f4bfdf1346e 0x7f4bfdf1eb30 0x7f4bfdf1b9a8 0x7f4bfdf176b8
Can anyone think of any other ways to solve this?
Long story short, almost all Unix-based systems ignore setuid on any interpreted script (anything with a shebang #!) as a security precaution.
You may use setuid on actual executables, but not the shell scripts themselves. If you're willing to take a massive security risk, you can make a wrapper executable to run the shell script and give the executable setuid.
For more information, see this question on the Unix StackExchange: https://unix.stackexchange.com/a/2910

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!