about summary refs log tree commit diff
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 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");
+    }
+}
+
+