aboutsummaryrefslogtreecommitdiff
path: root/src/exec.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/exec.cc')
-rw-r--r--src/exec.cc123
1 files changed, 123 insertions, 0 deletions
diff --git a/src/exec.cc b/src/exec.cc
new file mode 100644
index 000000000..016dbaedd
--- /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");
+ }
+}
+
+