1 : #include "serialise.hh"
2 : #include "util.hh"
3 : #include "remote-store.hh"
4 : #include "worker-protocol.hh"
5 : #include "archive.hh"
6 : #include "globals.hh"
7 :
8 : #include <sys/types.h>
9 : #include <sys/stat.h>
10 : #include <sys/socket.h>
11 : #include <sys/un.h>
12 : #include <fcntl.h>
13 :
14 : #include <iostream>
15 : #include <unistd.h>
16 :
17 :
18 : namespace nix {
19 :
20 :
21 373 : Path readStorePath(Source & from)
22 : {
23 373 : Path path = readString(from);
24 373 : assertStorePath(path);
25 0 : return path;
26 : }
27 :
28 :
29 137 : PathSet readStorePaths(Source & from)
30 : {
31 137 : PathSet paths = readStringSet(from);
32 289 : for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
33 152 : assertStorePath(*i);
34 0 : return paths;
35 : }
36 :
37 :
38 92 : RemoteStore::RemoteStore()
39 : {
40 92 : string remoteMode = getEnv("NIX_REMOTE");
41 :
42 184 : if (remoteMode == "slave")
43 : /* Fork off a setuid worker to do the privileged work. */
44 46 : forkSlave();
45 46 : else if (remoteMode == "daemon")
46 : /* Connect to a daemon that does the privileged work for
47 : us. */
48 46 : connectToDaemon();
49 : else
50 : throw Error(format("invalid setting for NIX_REMOTE, `%1%'")
51 0 : % remoteMode);
52 :
53 92 : from.fd = fdSocket;
54 92 : to.fd = fdSocket;
55 :
56 : /* Send the magic greeting, check for the reply. */
57 : try {
58 92 : writeInt(WORKER_MAGIC_1, to);
59 92 : unsigned int magic = readInt(from);
60 92 : if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
61 :
62 92 : daemonVersion = readInt(from);
63 92 : if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
64 0 : throw Error("Nix daemon protocol version not supported");
65 92 : writeInt(PROTOCOL_VERSION, to);
66 92 : processStderr();
67 :
68 0 : } catch (Error & e) {
69 : throw Error(format("cannot start worker (%1%)")
70 0 : % e.msg());
71 : }
72 :
73 184 : setOptions();
74 92 : }
75 :
76 :
77 46 : void RemoteStore::forkSlave()
78 : {
79 : int sockets[2];
80 46 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1)
81 0 : throw SysError("cannot create sockets");
82 :
83 46 : fdSocket = sockets[0];
84 46 : AutoCloseFD fdChild = sockets[1];
85 :
86 : /* Start the worker. */
87 46 : Path worker = getEnv("NIX_WORKER");
88 92 : if (worker == "")
89 46 : worker = nixBinDir + "/nix-worker";
90 :
91 46 : string verbosityArg = "-";
92 46 : for (int i = 1; i < verbosity; ++i)
93 0 : verbosityArg += "v";
94 :
95 46 : child = fork();
96 :
97 92 : switch (child) {
98 :
99 : case -1:
100 0 : throw SysError("unable to fork");
101 :
102 : case 0:
103 : try { /* child */
104 :
105 46 : if (dup2(fdChild, STDOUT_FILENO) == -1)
106 0 : throw SysError("dupping write side");
107 :
108 46 : if (dup2(fdChild, STDIN_FILENO) == -1)
109 0 : throw SysError("dupping read side");
110 :
111 46 : close(fdSocket);
112 46 : close(fdChild);
113 :
114 : execlp(worker.c_str(), worker.c_str(), "--slave",
115 : /* hacky - must be at the end */
116 : verbosityArg == "-" ? NULL : verbosityArg.c_str(),
117 46 : NULL);
118 :
119 0 : throw SysError(format("executing `%1%'") % worker);
120 :
121 0 : } catch (std::exception & e) {
122 0 : std::cerr << format("child error: %1%\n") % e.what();
123 : }
124 0 : quickExit(1);
125 : }
126 :
127 46 : fdChild.close();
128 :
129 46 : }
130 :
131 :
132 46 : void RemoteStore::connectToDaemon()
133 : {
134 46 : fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
135 46 : if (fdSocket == -1)
136 0 : throw SysError("cannot create Unix domain socket");
137 :
138 46 : string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
139 :
140 : /* Urgh, sockaddr_un allows path names of only 108 characters. So
141 : chdir to the socket directory so that we can pass a relative
142 : path name. !!! this is probably a bad idea in multi-threaded
143 : applications... */
144 46 : AutoCloseFD fdPrevDir = open(".", O_RDONLY);
145 46 : if (fdPrevDir == -1) throw SysError("couldn't open current directory");
146 46 : chdir(dirOf(socketPath).c_str());
147 46 : Path socketPathRel = "./" + baseNameOf(socketPath);
148 :
149 : struct sockaddr_un addr;
150 46 : addr.sun_family = AF_UNIX;
151 46 : if (socketPathRel.size() >= sizeof(addr.sun_path))
152 0 : throw Error(format("socket path `%1%' is too long") % socketPathRel);
153 46 : strcpy(addr.sun_path, socketPathRel.c_str());
154 :
155 46 : if (connect(fdSocket, (struct sockaddr *) &addr, sizeof(addr)) == -1)
156 0 : throw SysError(format("cannot connect to daemon at `%1%'") % socketPath);
157 :
158 46 : if (fchdir(fdPrevDir) == -1)
159 0 : throw SysError("couldn't change back to previous directory");
160 46 : }
161 :
162 :
163 92 : RemoteStore::~RemoteStore()
164 : {
165 : try {
166 92 : fdSocket.close();
167 92 : if (child != -1)
168 46 : child.wait(true);
169 0 : } catch (...) {
170 0 : ignoreException();
171 : }
172 92 : }
173 :
174 :
175 92 : void RemoteStore::setOptions()
176 : {
177 92 : writeInt(wopSetOptions, to);
178 92 : writeInt(keepFailed, to);
179 92 : writeInt(keepGoing, to);
180 92 : writeInt(tryFallback, to);
181 92 : writeInt(verbosity, to);
182 92 : writeInt(maxBuildJobs, to);
183 92 : writeInt(maxSilentTime, to);
184 92 : if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
185 92 : writeInt(useBuildHook, to);
186 92 : if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) {
187 92 : writeInt(buildVerbosity, to);
188 92 : writeInt(logType, to);
189 92 : writeInt(printBuildTrace, to);
190 : }
191 92 : processStderr();
192 92 : }
193 :
194 :
195 58 : bool RemoteStore::isValidPath(const Path & path)
196 : {
197 58 : writeInt(wopIsValidPath, to);
198 58 : writeString(path, to);
199 58 : processStderr();
200 58 : unsigned int reply = readInt(from);
201 58 : return reply != 0;
202 : }
203 :
204 :
205 0 : PathSet RemoteStore::queryValidPaths()
206 : {
207 0 : throw Error("not implemented");
208 : }
209 :
210 :
211 12 : bool RemoteStore::hasSubstitutes(const Path & path)
212 : {
213 12 : writeInt(wopHasSubstitutes, to);
214 12 : writeString(path, to);
215 12 : processStderr();
216 12 : unsigned int reply = readInt(from);
217 12 : return reply != 0;
218 : }
219 :
220 :
221 : bool RemoteStore::querySubstitutablePathInfo(const Path & path,
222 0 : SubstitutablePathInfo & info)
223 : {
224 0 : if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return false;
225 0 : writeInt(wopQuerySubstitutablePathInfo, to);
226 0 : writeString(path, to);
227 0 : processStderr();
228 0 : unsigned int reply = readInt(from);
229 0 : if (reply == 0) return false;
230 0 : info.deriver = readString(from);
231 0 : if (info.deriver != "") assertStorePath(info.deriver);
232 0 : info.references = readStorePaths(from);
233 0 : info.downloadSize = readLongLong(from);
234 0 : return true;
235 : }
236 :
237 :
238 0 : Hash RemoteStore::queryPathHash(const Path & path)
239 : {
240 0 : writeInt(wopQueryPathHash, to);
241 0 : writeString(path, to);
242 0 : processStderr();
243 0 : string hash = readString(from);
244 0 : return parseHash(htSHA256, hash);
245 : }
246 :
247 :
248 : void RemoteStore::queryReferences(const Path & path,
249 0 : PathSet & references)
250 : {
251 0 : writeInt(wopQueryReferences, to);
252 0 : writeString(path, to);
253 0 : processStderr();
254 0 : PathSet references2 = readStorePaths(from);
255 0 : references.insert(references2.begin(), references2.end());
256 0 : }
257 :
258 :
259 : void RemoteStore::queryReferrers(const Path & path,
260 0 : PathSet & referrers)
261 : {
262 0 : writeInt(wopQueryReferrers, to);
263 0 : writeString(path, to);
264 0 : processStderr();
265 0 : PathSet referrers2 = readStorePaths(from);
266 0 : referrers.insert(referrers2.begin(), referrers2.end());
267 0 : }
268 :
269 :
270 0 : Path RemoteStore::queryDeriver(const Path & path)
271 : {
272 0 : writeInt(wopQueryDeriver, to);
273 0 : writeString(path, to);
274 0 : processStderr();
275 0 : Path drvPath = readString(from);
276 0 : if (drvPath != "") assertStorePath(drvPath);
277 0 : return drvPath;
278 : }
279 :
280 :
281 : Path RemoteStore::addToStore(const Path & _srcPath, bool fixed,
282 42 : bool recursive, string hashAlgo, PathFilter & filter)
283 : {
284 42 : Path srcPath(absPath(_srcPath));
285 :
286 42 : writeInt(wopAddToStore, to);
287 42 : writeString(baseNameOf(srcPath), to);
288 42 : writeInt(fixed ? 1 : 0, to);
289 42 : writeInt(recursive ? 1 : 0, to);
290 42 : writeString(hashAlgo, to);
291 42 : dumpPath(srcPath, to, filter);
292 42 : processStderr();
293 42 : return readStorePath(from);
294 : }
295 :
296 :
297 : Path RemoteStore::addTextToStore(const string & suffix, const string & s,
298 74 : const PathSet & references)
299 : {
300 74 : writeInt(wopAddTextToStore, to);
301 74 : writeString(suffix, to);
302 74 : writeString(s, to);
303 74 : writeStringSet(references, to);
304 :
305 74 : processStderr();
306 74 : return readStorePath(from);
307 : }
308 :
309 :
310 : void RemoteStore::exportPath(const Path & path, bool sign,
311 0 : Sink & sink)
312 : {
313 0 : writeInt(wopExportPath, to);
314 0 : writeString(path, to);
315 0 : writeInt(sign ? 1 : 0, to);
316 0 : processStderr(&sink); /* sink receives the actual data */
317 0 : readInt(from);
318 0 : }
319 :
320 :
321 0 : Path RemoteStore::importPath(bool requireSignature, Source & source)
322 : {
323 0 : writeInt(wopImportPath, to);
324 : /* We ignore requireSignature, since the worker forces it to true
325 : anyway. */
326 :
327 0 : processStderr(0, &source);
328 0 : return readStorePath(from);
329 : }
330 :
331 :
332 56 : void RemoteStore::buildDerivations(const PathSet & drvPaths)
333 : {
334 56 : writeInt(wopBuildDerivations, to);
335 56 : writeStringSet(drvPaths, to);
336 56 : processStderr();
337 54 : readInt(from);
338 54 : }
339 :
340 :
341 44 : void RemoteStore::ensurePath(const Path & path)
342 : {
343 44 : writeInt(wopEnsurePath, to);
344 44 : writeString(path, to);
345 44 : processStderr();
346 44 : readInt(from);
347 44 : }
348 :
349 :
350 26 : void RemoteStore::addTempRoot(const Path & path)
351 : {
352 26 : writeInt(wopAddTempRoot, to);
353 26 : writeString(path, to);
354 26 : processStderr();
355 26 : readInt(from);
356 26 : }
357 :
358 :
359 0 : void RemoteStore::addIndirectRoot(const Path & path)
360 : {
361 0 : writeInt(wopAddIndirectRoot, to);
362 0 : writeString(path, to);
363 0 : processStderr();
364 0 : readInt(from);
365 0 : }
366 :
367 :
368 26 : void RemoteStore::syncWithGC()
369 : {
370 26 : writeInt(wopSyncWithGC, to);
371 26 : processStderr();
372 26 : readInt(from);
373 26 : }
374 :
375 :
376 26 : Roots RemoteStore::findRoots()
377 : {
378 26 : writeInt(wopFindRoots, to);
379 26 : processStderr();
380 26 : unsigned int count = readInt(from);
381 26 : Roots result;
382 112 : while (count--) {
383 112 : Path link = readString(from);
384 112 : Path target = readStorePath(from);
385 112 : result[link] = target;
386 : }
387 0 : return result;
388 : }
389 :
390 :
391 2 : void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
392 : {
393 2 : writeInt(wopCollectGarbage, to);
394 2 : writeInt(options.action, to);
395 2 : writeStringSet(options.pathsToDelete, to);
396 2 : writeInt(options.ignoreLiveness, to);
397 2 : writeLongLong(options.maxFreed, to);
398 2 : writeInt(options.maxLinks, to);
399 :
400 2 : processStderr();
401 :
402 2 : results.paths = readStringSet(from);
403 2 : results.bytesFreed = readLongLong(from);
404 2 : results.blocksFreed = readLongLong(from);
405 2 : }
406 :
407 :
408 550 : void RemoteStore::processStderr(Sink * sink, Source * source)
409 : {
410 : unsigned int msg;
411 1354 : while ((msg = readInt(from)) == STDERR_NEXT
412 : || msg == STDERR_READ || msg == STDERR_WRITE) {
413 254 : if (msg == STDERR_WRITE) {
414 0 : string s = readString(from);
415 0 : if (!sink) throw Error("no sink");
416 0 : (*sink)((const unsigned char *) s.c_str(), s.size());
417 : }
418 254 : else if (msg == STDERR_READ) {
419 0 : if (!source) throw Error("no source");
420 0 : unsigned int len = readInt(from);
421 0 : unsigned char * buf = new unsigned char[len];
422 0 : AutoDeleteArray<unsigned char> d(buf);
423 0 : (*source)(buf, len);
424 0 : writeString(string((const char *) buf, len), to);
425 : }
426 : else {
427 254 : string s = readString(from);
428 254 : writeToStderr((const unsigned char *) s.c_str(), s.size());
429 : }
430 : }
431 550 : if (msg == STDERR_ERROR)
432 2 : throw Error(readString(from));
433 548 : else if (msg != STDERR_LAST)
434 0 : throw Error("protocol error processing standard error");
435 548 : }
436 :
437 0 :
438 1106 : }
|