Linux fork/exec to application in same directory - c++

Is there an exec variant that will use the current application directory to locate the target program?
I am using C++ and Qt to implement a "last ditch" error reporting system. Using Google Breakpad, I can create a minidump and direct execution to a handler. Because my application is in an unstable state, I just want to fork and start a separate error handling process using minimal dependencies. The error reporting application will be deployed in the same directory as the application executable.
I am quite unfamiliar with the fork and exec options, and am not finding an exec option that includes the current application directory in the search path. Here is what I have so far:
static bool dumpCallback(const char* /*dump_path*/,
const char* /*minidump_id*/,
void* /*context*/,
bool succeeded)
{
pid_t pid = fork();
if (pid == 0)
{
// This is what I would *like* to work.
const char* error_reporter_path = "error_reporter";
// This works, but requires hard-coding the entire path, which seems lame,
// and really isn't an option, given our deployment model.
//
// const char* error_reporter_path = "/path/to/app/error_reporter";
// This also works, but I don't like the dependency on QApplication at this
// point, since the application is unstable.
//
// const char* error_reporter_path =
// QString("%1/%2")
// .arg(QApplication::applicationDirPath())
// .arg("error_reporter").toLatin1().constData();
execlp(error_reporter_path,
error_reporter_path,
(char *) 0);
}
return succeeded;
}
Any other suggestions on best practices for using fork and exec would be appreciated as well; this is my first introduction to using them. I'm only concerned about Linux (Ubuntu, Fedora) at this point; I will work on handlers for other operating systems later.

What you asked for is actually quite easy:
{
pid_t pid = fork();
if (pid == 0)
{
const char* error_reporter_path = "./error_reporter";
execl(error_reporter_path,
error_reporter_path,
(char *) 0);
_exit(127);
}
else
return pid != -1;
}
but it doesn't do what you want. The current working directory is not necessarily the same thing as the directory containing the current executable -- in fact, under almost all circumstances, it won't be.
What I would recommend you do is make error_reporter_path a global variable, and initialize it at the very beginning of main, using your "option 2" code
QString("%1/%2")
.arg(QApplication::applicationDirPath())
.arg("error_reporter").toLatin1().constData();
The QString object (not just its constData) then has to live for the lifetime of the program, but that shouldn't be a problem. Note that you should be converting to UTF-8, not Latin1 (I guess QString uses wide characters?)

I think you have 2 choices:
Add '.' to $PATH.
Prepend the result of getcwd() to the executable name.

You should build the path to your helper executable at your program's startup, and save it somewhere (in a global or static variable). If you only need to run on Linux, you can do this by reading /proc/self/exe to get the location of your executable. Something like this:
// Locate helper binary next to the current binary.
char self_path[PATH_MAX];
if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
exit(1);
}
string helper_path(self_path);
size_t pos = helper_path.rfind('/');
if (pos == string::npos) {
exit(1);
}
helper_path.erase(pos + 1);
helper_path += "helper";
Excerpted from a full working example here: http://code.google.com/p/google-breakpad/source/browse/trunk/src/client/linux/minidump_writer/linux_dumper_unittest.cc#92

Never, ever, under any circumstances add "." to $PATH !!
If you prepend getcwd() to the executable name (argv[0]), you have to do is as the first thing in main, before anything has the chance to change the current working directory. Then you have to consider what to do about symbolic links in the resulting filename. And even after that you can never be sure that argv[0] is set to the command used to execute your program
Option 3:
Hardcode the full filename in your executable, but use the configure script to set the filename. (You are using a configure script, right?)
Option 4;
Don't call exec. You don't have to call exec after a fork. Just pretend you have just entered "main", and call "exit" when your error reporting has finished.

Related

execve(...) does not execute program despite passing in PATH variable

I'm executing a simple shell program from the directory:
/home/user/shell.exe
Using the code below, I'm able to run files that are in the same folder as my shell executable, but am unable to run programs such as ls.exe.
The tokens container includes the file name as the first element and any subsequent tokens (such as "-l" in the input "ls.exe -l") in the following elements.
if (fork())
{
int status;
wait(&status);
}
else
{
std::vector<const char*> exeArgs;
std::vector<const char*> envArgs;
std::for_each(tokens.begin(), tokens.end(),
[&exeArgs](const string& elem){ exeArgs.push_back(elem.c_str()); }
);
exeArgs.push_back(nullptr);
string path = "PATH=";
path.append(getenv("PATH"));
envArgs.push_back(path.c_str());
envArgs.push_back(nullptr);
if (execve(exeArgs[0], const_cast<char *const *>(&exeArgs[0]),
const_cast<char *const *>(&envArgs[0])))
{
std::cout << word << ": command not found" << std::endl;
exit(0);
}
}
I've spent countless hours just googling and reading the man pages over and over but can't seem to get a clue why this code doesn't work.
The idea is that my shell program should allow users to set the PATH variable and then execute programs with that PATH variable, which is why I have to make execve() work properly instead of just using execvp().
I have a map of shell variables in a separate part of the file but since I can't even get this to work, I thought it would be pointless to include that.
You do know that the exec family of functions replaces the current process with the image of the new program? That's why it's so common to use fork before exec.
Armed with that knowledge, it's easy to find a solution for you, and how you can use execvp (which you need to use, execve doesn't really use the environment you pass, it just passes it along to the new program): You fork and use setenv to set the PATH of the new process, before calling execvp.

How to open/spawn a file with glib/gtkmm in Windows

I've already tried:
GError *pError = NULL;
string uri = g_filename_to_uri(file.c_str(), NULL, &pError);
if (!g_app_info_launch_default_for_uri(uri.c_str(), NULL, &pError)) {
cout << "Failed to open uri: " << pError->message;
}
Here I get the error "URIs not supported". Is the uri I create here wrong?
My second approach was to spawn the file with an asynchronous command line:
file = quoteStr(file);
try {
Glib::spawn_command_line_async(file);
} catch (Glib::SpawnError error) {
cout << error.what();
} catch (Glib::ShellError error) {
cout << error.what();
}
Here the Glib::SpawnError exception is thrown with the error: "Failed to execute helper program (Invalid argument)". I mean, when I execute the quoted absolute file path in the Windows cmd, it opens the file (in this case a pdf file). Does this function work different?
Hopefully this is related and can provide a real answer rather than just a (clever!) workaround.
I ran into a strange situation: Launching a file (specifically an HTML document) by g_app_info_launch_default_for_uri() or gtk_show_uri_on_window() worked when the executable was run from my build directory. However, it did not work if I copied the exe to another directory (for distribution) and ran it from there.
In the latter case, I got the same error as your 2nd quote:
Failed to execute helper program (Invalid argument)
The build directory is not in my path, and nor is it special for any other reason (it's in a temp RAM drive). So I was completely baffled.
I then thought about that error... What helper program could it possibly be talking about?
And why might that program be found when running from the build directory? Well, my build uses a libtool wrapper, and that puts a bunch of things in the path, so that we don't need to copy all the DLLs etc in just to test builds.
So, I went to investigate whether there was anything relevant-looking in paths that might be searched by the MSYS2 shell and its libtool wrapper. The prime suspect, of course, is C:\msys64\mingw64\bin. And look what I found there:
gspawn-win64-helper-console.exe
After copying this executable to the directory from which my application is launched, my program now successfully launches the URI, regardless of which folder its executable currently resides in.
Edit
After updating my packages in MSYS2, it was back to the same error - as it seems now this is the helper that is required:
gspawn-win64-helper.exe
That actually makes more sense, since my application is graphical, not console. I guess maybe something changed here recently. You could distribute both to be extra safe.
I had a similar problem and I had to give up using glib to do that and ended up implementing a simple crossplatform (win, mac and linux) compatible way to do it:
// open an URI, different for each operating system
void
openuri(const char *url)
{
#ifdef WIN32
ShellExecute(GetActiveWindow(),
"open", url, NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
char buffer[512];
::snprintf(buffer, sizeof(buffer), "open %s", url);
::system(buffer);
#else
char buffer[512];
::snprintf(buffer, sizeof(buffer), "xdg-open %s", url);
::system(buffer);
#endif
}
... it's not very nice but it's small and it works :)

Open HTML file in the directory with C++

I want to open the HTML file named "myHTML.html" using C++ code in Ubuntu. The file is located in the same directory as my C++ source files.
May I know how do I do that?
First, you could start a process running the web browser (in the background), e.g.
char cmd[256];
char mypwd[200];
memset (mypwd, 0, sizeof(mypwd));
if (!getcwd(mypwd, sizeof(mypwd)))
{ perror("getcwd"); exit (EXIT_FAILURE); };
snprintf (cmd, sizeof(cmd),
"/usr/bin/x-www-browser 'file://%s/myHTML.html' &", mypwd);
int notok = system(cmd);
Of course if the current directory has a weird name (e.g. contains a quote, which is uncommon), you might end up with some code injection. But it is unlikely. and you might replace mypwd with "/proc/self/cwd"
If the HTML file you want to open is builtin, e.g./etc/yourapp/myHTML.html (or some other nice fixed file path, without naughty characters) you could just use
int notok = system("/usr/bin/x-www-browser /etc/yourapp/myHTML.html &");
or
int notok = system("xdg-open /etc/yourapp/myHTML.html &");
or
pid_t pid = fork();
if (pid == 0) {
// child process
execlp("xdg-open", "/etc/yourapp/myHTML.html", NULL);
_exit(127);
};
(you may want to waitpid for your pid later)
And even better, you could make your C++ application an HTTP server, e.g. with Wt or libonion

How to write a a self replacing/updating binary?

I am trying to write a C program which may lookup a url and incase a new version of it is avaiable it should be able to update itself.
The method i have tried:
Forkout a new process to Download the new binary say BINARY.tmp, code i am using to forkout the is:
int
forkout_cmd(char *cmdstr) {
pid_t pid;
char *cmd[4];
cmd[0] = "/bin/bash";
cmd[1] = "-c";
cmd[2] = cmdstr;
cmd[3] = NULL;
pid = vfork();
if( pid == -1 ) {
logmsg("Forking for upgradation failed.");
return -1;
}else if( pid == 0 ){
/* we are in child process */
execvp(cmd[0], cmd);
logmsg("execl failed while executing upgradation job.");
}else{
/* need not to wait for the child to complete. */
wait(NULL);
}
return 0;
}
The new process tries to overwrite the original BINARY
for example you may consider the routine which forks out may be doing:
forkout_cmd("wget -O BINARY.tmp https://someurl.com/BINARY_LATEST; /bin/mv -f BINARY.tmp BINARY");
But, the overwriting fails since the original binary is still in execution and hence busy on disk, can somebody provide me some suggestions here to overcome this problem.
Thanks in advance.
Rename the currently running binary to something else, write the new binary, run it, then delete the renamed binary later.
I would save binary.tmp to the same directory as the executable, verify its checksum/signature (whatever it takes to be 100% sure no error occurred), and then atomically rename it to the executable's name.
Under Linux, this can be done while the program is running, no problem whatsoever (you are only changing the link, the underlying file persists while mappings to it are open, that is until the program is closed or restarted).
I would under no circumstances rename the original file or even overwrite it. This is unsafe and not necessary. You can do all "unsafe" operations that could fail on the temp file before touching the original. If anything goes wrong in the atomic rename, you still have the working original.
Then prompt the user to restart the program (if interactive) and done.

Use a personal program in Firebreath

I'm building a plugin using Firebreath. I made a personal method in ABCPluginAPI.cpp called exe_program() and I would like to call another program using popen called my_program. All the files are into firebreath/projects/ABCPlugin/.
My method is:
string ABCPluginAPI::exe_program()
{
FILE * pPipe;
fd_set readfd;
char buff[1024];
char command[128];
int ret;
strcpy(command, "my_program");
if (!(pPipe = popen(command, "r"))) {
// Problem to execute the command
return "failed";
}
while(fgets(buff, sizeof(buff), pPipe)!=NULL){
cout << buff;
return buff;
}
}
The problem I have is that the plugin is not running my_program, actually if I execute the pwd command, it shows my $HOME directory. pwd works because is a general command but I don't want to put my program into $PATH variable because this plugin must be portable.
Probably Firebreath use a special directory to refer to this kind of files or something similar.
You probably need to specify a full path and filename of the application you want to run; the current working directory is not garanteed to always be the same value.
From the Tips and Tricks page of firebreath.org there is code you can add to your PluginCore-derived object that will give you the full path and filename of your plugin file:
// From inside your Plugin class (that extends PluginCore)
std::string MyPlugin::getFilesystemPath()
{
return m_filesystemPath;
}
You can take that path, strip off the last part, and change it to your executable filename; as long as you place the executable in the same directory as your plugin that should work fine. Alternately you could install it in some other well-known location.
Note that to call a method on your main Plugin object from your JSAPI object there should be a helper method getPlugin() on your JSAPI object (if you used fbgen to generate it):
std::string pluginPath = getPlugin()->getFilesystemPath();
Hope that helps