1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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");
}
}
|