Linux fork/exec to application in same directory

2019-06-20 13:55发布

问题:

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.

回答1:

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?)



回答2:

I think you have 2 choices:

  1. Add '.' to $PATH.
  2. Prepend the result of getcwd() to the executable name.


回答3:

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



回答4:

  1. Never, ever, under any circumstances add "." to $PATH !!

  2. 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.