120 lines
3.1 KiB
C++
120 lines
3.1 KiB
C++
#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");
|
|
|
|
/* 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");
|
|
}
|
|
}
|
|
|
|
|