diff options
Diffstat (limited to 'src/exec.cc')
-rw-r--r-- | src/exec.cc | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/exec.cc b/src/exec.cc new file mode 100644 index 000000000000..016dbaeddf73 --- /dev/null +++ b/src/exec.cc @@ -0,0 +1,123 @@ +#include <iostream> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> + +#include "exec.hh" +#include "util.hh" +#include "globals.hh" + + +class AutoDelete +{ + string path; + bool del; +public: + + AutoDelete(const string & p) : path(p) + { + del = true; + } + + ~AutoDelete() + { + if (del) deletePath(path); + } + + void cancel() + { + del = false; + } +}; + + +/* Run a program. */ +void runProgram(const string & program, Environment env) +{ + /* Create a log file. */ + string logFileName = nixLogDir + "/run.log"; + /* !!! auto-pclose on exit */ + FILE * logFile = popen(("tee -a " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */ + if (!logFile) + throw SysError(format("creating log file `%1%'") % logFileName); + + /* Create a temporary directory where the build will take + place. */ + static int counter = 0; + string tmpDir = (format("/tmp/nix-%1%-%2%") % getpid() % counter++).str(); + + if (mkdir(tmpDir.c_str(), 0777) == -1) + throw SysError(format("creating directory `%1%'") % tmpDir); + + AutoDelete delTmpDir(tmpDir); + + /* Fork a child to build the package. */ + pid_t pid; + switch (pid = fork()) { + + case -1: + throw SysError("unable to fork"); + + case 0: + + try { /* child */ + + if (chdir(tmpDir.c_str()) == -1) + throw SysError(format("changing into to `%1%'") % tmpDir); + + /* Fill in the environment. We don't bother freeing + the strings, since we'll exec or die soon + anyway. */ + const char * env2[env.size() + 1]; + int i = 0; + for (Environment::iterator it = env.begin(); + it != env.end(); it++, i++) + env2[i] = (new string(it->first + "=" + it->second))->c_str(); + env2[i] = 0; + + /* Dup the log handle into stderr. */ + if (dup2(fileno(logFile), STDERR_FILENO) == -1) + throw SysError("cannot pipe standard error into log file"); + + /* Dup stderr to stdin. */ + if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1) + throw SysError("cannot dup stderr into stdout"); + + /* Make the program executable. !!! hack. */ + if (chmod(program.c_str(), 0755)) + throw SysError("cannot make program executable"); + + /* Execute the program. This should not return. */ + execle(program.c_str(), baseNameOf(program).c_str(), 0, env2); + + throw SysError(format("unable to execute %1%") % program); + + } catch (exception & e) { + cerr << format("build error: %1%\n") % e.what(); + } + _exit(1); + + } + + /* parent */ + + /* Close the logging pipe. Note that this should not cause + the logger to exit until builder exits (because the latter + has an open file handle to the former). */ + pclose(logFile); + + /* Wait for the child to finish. */ + int status; + if (waitpid(pid, &status, 0) != pid) + throw Error("unable to wait for child"); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + delTmpDir.cancel(); + throw Error("unable to build package"); + } +} + + |