1 : #include "config.h"
2 :
3 : #include <iostream>
4 : #include <cerrno>
5 : #include <cstdio>
6 : #include <sstream>
7 :
8 : #include <sys/types.h>
9 : #include <sys/stat.h>
10 : #include <sys/wait.h>
11 : #include <unistd.h>
12 : #include <dirent.h>
13 : #include <fcntl.h>
14 : #include <signal.h>
15 :
16 : #include "util.hh"
17 :
18 :
19 51 : string thisSystem = SYSTEM;
20 :
21 :
22 : Error::Error(const format & f)
23 14 : {
24 7 : err = f.str();
25 : }
26 :
27 :
28 : SysError::SysError(const format & f)
29 0 : : Error(format("%1%: %2%") % f.str() % strerror(errno))
30 0 : {
31 : }
32 :
33 :
34 : string getEnv(const string & key, const string & def)
35 477 : {
36 477 : char * value = getenv(key.c_str());
37 477 : return value ? string(value) : def;
38 : }
39 :
40 :
41 : Path absPath(Path path, Path dir)
42 68 : {
43 68 : if (path[0] != '/') {
44 41 : if (dir == "") {
45 26 : char buf[PATH_MAX];
46 26 : if (!getcwd(buf, sizeof(buf)))
47 0 : throw SysError("cannot get cwd");
48 26 : dir = buf;
49 : }
50 41 : path = dir + "/" + path;
51 : }
52 68 : return canonPath(path);
53 : }
54 :
55 :
56 : Path canonPath(const Path & path)
57 494 : {
58 494 : string s;
59 :
60 494 : if (path[0] != '/')
61 0 : throw Error(format("not an absolute path: `%1%'") % path);
62 :
63 494 : string::const_iterator i = path.begin(), end = path.end();
64 :
65 6014 : while (1) {
66 :
67 : /* Skip slashes. */
68 6014 : while (i != end && *i == '/') i++;
69 3251 : if (i == end) break;
70 :
71 : /* Ignore `.'. */
72 2757 : if (*i == '.' && (i + 1 == end || i[1] == '/'))
73 27 : i++;
74 :
75 : /* If `..', delete the last component. */
76 2730 : else if (*i == '.' && i + 1 < end && i[1] == '.' &&
77 : (i + 2 == end || i[2] == '/'))
78 : {
79 0 : if (!s.empty()) s.erase(s.rfind('/'));
80 0 : i += 2;
81 : }
82 :
83 : /* Normal component; copy it. */
84 : else {
85 2730 : s += '/';
86 33628 : while (i != end && *i != '/') s += *i++;
87 : }
88 : }
89 :
90 494 : return s.empty() ? "/" : s;
91 : }
92 :
93 :
94 : Path dirOf(const Path & path)
95 317 : {
96 317 : unsigned int pos = path.rfind('/');
97 317 : if (pos == string::npos)
98 0 : throw Error(format("invalid file name: %1%") % path);
99 317 : return Path(path, 0, pos);
100 : }
101 :
102 :
103 : string baseNameOf(const Path & path)
104 125 : {
105 125 : unsigned int pos = path.rfind('/');
106 125 : if (pos == string::npos)
107 0 : throw Error(format("invalid file name %1% ") % path);
108 125 : return string(path, pos + 1);
109 : }
110 :
111 :
112 : bool pathExists(const Path & path)
113 208 : {
114 208 : int res;
115 208 : struct stat st;
116 208 : res = lstat(path.c_str(), &st);
117 208 : if (!res) return true;
118 102 : if (errno != ENOENT && errno != ENOTDIR)
119 0 : throw SysError(format("getting status of %1%") % path);
120 102 : return false;
121 : }
122 :
123 :
124 : Path readLink(const Path & path)
125 2 : {
126 2 : struct stat st;
127 2 : if (lstat(path.c_str(), &st))
128 0 : throw SysError(format("getting status of `%1%'") % path);
129 2 : if (!S_ISLNK(st.st_mode))
130 0 : throw Error(format("`%1%' is not a symlink") % path);
131 2 : char buf[st.st_size];
132 2 : if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
133 0 : throw SysError(format("reading symbolic link `%1%'") % path);
134 2 : return string(buf, st.st_size);
135 : }
136 :
137 :
138 : Strings readDirectory(const Path & path)
139 44 : {
140 44 : Strings names;
141 :
142 44 : AutoCloseDir dir = opendir(path.c_str());
143 44 : if (!dir) throw SysError(format("opening directory `%1%'") % path);
144 :
145 161 : struct dirent * dirent;
146 161 : while (errno = 0, dirent = readdir(dir)) { /* sic */
147 117 : checkInterrupt();
148 117 : string name = dirent->d_name;
149 117 : if (name == "." || name == "..") continue;
150 29 : names.push_back(name);
151 : }
152 44 : if (errno) throw SysError(format("reading directory `%1%'") % path);
153 :
154 44 : return names;
155 : }
156 :
157 :
158 : static void _deletePath(const Path & path)
159 25 : {
160 25 : checkInterrupt();
161 :
162 25 : printMsg(lvlVomit, format("%1%") % path);
163 :
164 25 : struct stat st;
165 25 : if (lstat(path.c_str(), &st))
166 0 : throw SysError(format("getting attributes of path `%1%'") % path);
167 :
168 25 : if (S_ISDIR(st.st_mode)) {
169 22 : Strings names = readDirectory(path);
170 :
171 : /* Make the directory writable. */
172 22 : if (!(st.st_mode & S_IWUSR)) {
173 0 : if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
174 0 : throw SysError(format("making `%1%' writable") % path);
175 : }
176 :
177 25 : for (Strings::iterator i = names.begin(); i != names.end(); ++i)
178 3 : _deletePath(path + "/" + *i);
179 : }
180 :
181 25 : if (remove(path.c_str()) == -1)
182 0 : throw SysError(format("cannot unlink `%1%'") % path);
183 : }
184 :
185 :
186 : void deletePath(const Path & path)
187 22 : {
188 22 : startNest(nest, lvlDebug,
189 : format("recursively deleting path `%1%'") % path);
190 22 : _deletePath(path);
191 : }
192 :
193 :
194 : void makePathReadOnly(const Path & path)
195 96 : {
196 96 : checkInterrupt();
197 :
198 96 : struct stat st;
199 96 : if (lstat(path.c_str(), &st))
200 0 : throw SysError(format("getting attributes of path `%1%'") % path);
201 :
202 96 : if (!S_ISLNK(st.st_mode) && (st.st_mode & S_IWUSR)) {
203 94 : if (chmod(path.c_str(), st.st_mode & ~S_IWUSR) == -1)
204 0 : throw SysError(format("making `%1%' read-only") % path);
205 : }
206 :
207 96 : if (S_ISDIR(st.st_mode)) {
208 12 : Strings names = readDirectory(path);
209 26 : for (Strings::iterator i = names.begin(); i != names.end(); ++i)
210 14 : makePathReadOnly(path + "/" + *i);
211 : }
212 : }
213 :
214 :
215 : static Path tempName()
216 22 : {
217 22 : static int counter = 0;
218 22 : Path tmpRoot = canonPath(getEnv("TMPDIR", "/tmp"));
219 22 : return (format("%1%/nix-%2%-%3%") % tmpRoot % getpid() % counter++).str();
220 : }
221 :
222 :
223 : Path createTempDir()
224 22 : {
225 22 : while (1) {
226 22 : checkInterrupt();
227 22 : Path tmpDir = tempName();
228 22 : if (mkdir(tmpDir.c_str(), 0777) == 0) return tmpDir;
229 0 : if (errno != EEXIST)
230 0 : throw SysError(format("creating directory `%1%'") % tmpDir);
231 : }
232 : }
233 :
234 :
235 : void writeStringToFile(const Path & path, const string & s)
236 53 : {
237 : AutoCloseFD fd = open(path.c_str(),
238 53 : O_CREAT | O_EXCL | O_WRONLY, 0666);
239 53 : if (fd == -1)
240 0 : throw SysError(format("creating file `%1%'") % path);
241 53 : writeFull(fd, (unsigned char *) s.c_str(), s.size());
242 : }
243 :
244 :
245 : LogType logType = ltPretty;
246 : Verbosity verbosity = lvlInfo;
247 :
248 : static int nestingLevel = 0;
249 :
250 :
251 : Nest::Nest()
252 964 : {
253 964 : nest = false;
254 : }
255 :
256 :
257 : Nest::~Nest()
258 1928 : {
259 964 : close();
260 : }
261 :
262 :
263 : static string escVerbosity(Verbosity level)
264 0 : {
265 0 : int l = (int) level;
266 0 : ostringstream st;
267 0 : st << l;
268 0 : return st.str();
269 : }
270 :
271 :
272 : void Nest::open(Verbosity level, const format & f)
273 102 : {
274 102 : if (level <= verbosity) {
275 102 : if (logType == ltEscapes)
276 0 : cerr << "\033[" << escVerbosity(level) << "p"
277 : << f.str() << "\n";
278 : else
279 102 : printMsg_(level, f);
280 102 : nest = true;
281 102 : nestingLevel++;
282 : }
283 : }
284 :
285 :
286 : void Nest::close()
287 984 : {
288 984 : if (nest) {
289 102 : nestingLevel--;
290 102 : if (logType == ltEscapes)
291 0 : cerr << "\033[q";
292 102 : nest = false;
293 : }
294 : }
295 :
296 :
297 : void printMsg_(Verbosity level, const format & f)
298 2927 : {
299 2927 : checkInterrupt();
300 2927 : if (level > verbosity) return;
301 2927 : string prefix;
302 2927 : if (logType == ltPretty)
303 8840 : for (int i = 0; i < nestingLevel; i++)
304 5913 : prefix += "| ";
305 0 : else if (logType == ltEscapes && level != lvlInfo)
306 0 : prefix = "\033[" + escVerbosity(level) + "s";
307 2927 : cerr << format("%1%%2%\n") % prefix % f.str();
308 : }
309 :
310 :
311 : void readFull(int fd, unsigned char * buf, size_t count)
312 62 : {
313 62 : while (count) {
314 31 : checkInterrupt();
315 31 : ssize_t res = read(fd, (char *) buf, count);
316 31 : if (res == -1) {
317 0 : if (errno == EINTR) continue;
318 0 : throw SysError("reading from file");
319 : }
320 31 : if (res == 0) throw Error("unexpected end-of-file");
321 31 : count -= res;
322 31 : buf += res;
323 : }
324 : }
325 :
326 :
327 : void writeFull(int fd, const unsigned char * buf, size_t count)
328 849 : {
329 849 : while (count) {
330 424 : checkInterrupt();
331 424 : ssize_t res = write(fd, (char *) buf, count);
332 424 : if (res == -1) {
333 0 : if (errno == EINTR) continue;
334 0 : throw SysError("writing to file");
335 : }
336 424 : count -= res;
337 424 : buf += res;
338 : }
339 : }
340 :
341 :
342 :
343 : //////////////////////////////////////////////////////////////////////
344 :
345 :
346 0 : AutoDelete::AutoDelete(const string & p) : path(p)
347 0 : {
348 0 : del = true;
349 : }
350 :
351 : AutoDelete::~AutoDelete()
352 0 : {
353 0 : if (del) deletePath(path);
354 : }
355 :
356 : void AutoDelete::cancel()
357 0 : {
358 0 : del = false;
359 : }
360 :
361 :
362 :
363 : //////////////////////////////////////////////////////////////////////
364 :
365 :
366 : AutoCloseFD::AutoCloseFD()
367 855 : {
368 855 : fd = -1;
369 : }
370 :
371 :
372 : AutoCloseFD::AutoCloseFD(int fd)
373 115 : {
374 115 : this->fd = fd;
375 : }
376 :
377 :
378 : AutoCloseFD::~AutoCloseFD()
379 1940 : {
380 970 : try {
381 970 : close();
382 0 : } catch (Error & e) {
383 0 : printMsg(lvlError, format("error (ignored): %1%") % e.msg());
384 : }
385 : }
386 :
387 :
388 : void AutoCloseFD::operator =(int fd)
389 104 : {
390 104 : if (this->fd != fd) close();
391 104 : this->fd = fd;
392 : }
393 :
394 :
395 : AutoCloseFD::operator int()
396 579 : {
397 579 : return fd;
398 : }
399 :
400 :
401 : void AutoCloseFD::close()
402 1206 : {
403 1206 : if (fd != -1) {
404 252 : if (::close(fd) == -1)
405 : /* This should never happen. */
406 0 : throw SysError("closing file descriptor");
407 252 : fd = -1;
408 : }
409 : }
410 :
411 :
412 : bool AutoCloseFD::isOpen()
413 22 : {
414 22 : return fd != -1;
415 : }
416 :
417 :
418 : void Pipe::create()
419 41 : {
420 41 : int fds[2];
421 41 : if (pipe(fds) != 0) throw SysError("creating pipe");
422 41 : readSide = fds[0];
423 41 : writeSide = fds[1];
424 : }
425 :
426 :
427 :
428 : //////////////////////////////////////////////////////////////////////
429 :
430 :
431 : AutoCloseDir::AutoCloseDir()
432 0 : {
433 0 : dir = 0;
434 : }
435 :
436 :
437 : AutoCloseDir::AutoCloseDir(DIR * dir)
438 44 : {
439 44 : this->dir = dir;
440 : }
441 :
442 :
443 : AutoCloseDir::~AutoCloseDir()
444 88 : {
445 44 : if (dir) closedir(dir);
446 : }
447 :
448 :
449 : void AutoCloseDir::operator =(DIR * dir)
450 0 : {
451 0 : this->dir = dir;
452 : }
453 :
454 :
455 : AutoCloseDir::operator DIR *()
456 205 : {
457 205 : return dir;
458 : }
459 :
460 :
461 :
462 : //////////////////////////////////////////////////////////////////////
463 :
464 :
465 : Pid::Pid()
466 270 : {
467 270 : pid = -1;
468 270 : separatePG = false;
469 : }
470 :
471 :
472 : Pid::~Pid()
473 540 : {
474 270 : kill();
475 : }
476 :
477 :
478 : void Pid::operator =(pid_t pid)
479 62 : {
480 62 : if (this->pid != pid) kill();
481 62 : this->pid = pid;
482 : }
483 :
484 :
485 : Pid::operator pid_t()
486 378 : {
487 378 : return pid;
488 : }
489 :
490 :
491 : void Pid::kill()
492 332 : {
493 332 : if (pid == -1) return;
494 :
495 0 : printMsg(lvlError, format("killing process %1%") % pid);
496 :
497 : /* Send a KILL signal to the child. If it has its own process
498 : group, send the signal to every process in the child process
499 : group (which hopefully includes *all* its children). */
500 0 : if (::kill(separatePG ? -pid : pid, SIGKILL) != 0)
501 0 : printMsg(lvlError, (SysError(format("killing process %1%") % pid).msg()));
502 :
503 : /* Wait until the child dies, disregarding the exit status. */
504 0 : int status;
505 0 : while (waitpid(pid, &status, 0) == -1)
506 0 : if (errno != EINTR) printMsg(lvlError,
507 : (SysError(format("waiting for process %1%") % pid).msg()));
508 :
509 0 : pid = -1;
510 : }
511 :
512 :
513 : int Pid::wait(bool block)
514 35 : {
515 35 : while (1) {
516 35 : int status;
517 35 : int res = waitpid(pid, &status, block ? 0 : WNOHANG);
518 35 : if (res == pid) {
519 35 : pid = -1;
520 35 : return status;
521 : }
522 0 : if (res == 0 && !block) return -1;
523 0 : if (errno != EINTR)
524 0 : throw SysError("cannot get child exit status");
525 : }
526 : }
527 :
528 :
529 : void Pid::setSeparatePG(bool separatePG)
530 24 : {
531 24 : this->separatePG = separatePG;
532 : }
533 :
534 :
535 :
536 : //////////////////////////////////////////////////////////////////////
537 :
538 :
539 : volatile sig_atomic_t _isInterrupted = 0;
540 :
541 : void _interrupted()
542 0 : {
543 : /* Block user interrupts while an exception is being handled.
544 : Throwing an exception while another exception is being handled
545 : kills the program! */
546 0 : if (!uncaught_exception()) {
547 0 : _isInterrupted = 0;
548 0 : throw Error("interrupted by the user");
549 : }
550 : }
551 :
552 :
553 :
554 : //////////////////////////////////////////////////////////////////////
555 :
556 :
557 : string packStrings(const Strings & strings)
558 55 : {
559 55 : string d;
560 152 : for (Strings::const_iterator i = strings.begin();
561 : i != strings.end(); ++i)
562 : {
563 97 : unsigned int len = i->size();
564 97 : d += len & 0xff;
565 97 : d += (len >> 8) & 0xff;
566 97 : d += (len >> 16) & 0xff;
567 97 : d += (len >> 24) & 0xff;
568 97 : d += *i;
569 : }
570 0 : return d;
571 : }
572 :
573 :
574 : Strings unpackStrings(const string & s)
575 52 : {
576 52 : Strings strings;
577 :
578 52 : string::const_iterator i = s.begin();
579 :
580 169 : while (i != s.end()) {
581 :
582 117 : if (i + 4 > s.end())
583 0 : throw Error(format("short db entry: `%1%'") % s);
584 :
585 117 : unsigned int len;
586 117 : len = (unsigned char) *i++;
587 117 : len |= ((unsigned char) *i++) << 8;
588 117 : len |= ((unsigned char) *i++) << 16;
589 117 : len |= ((unsigned char) *i++) << 24;
590 :
591 117 : if (i + len > s.end())
592 0 : throw Error(format("short db entry: `%1%'") % s);
593 :
594 117 : strings.push_back(string(i, i + len));
595 117 : i += len;
596 : }
597 :
598 0 : return strings;
599 : }
600 :
601 :
602 : string statusToString(int status)
603 1 : {
604 1 : if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
605 1 : if (WIFEXITED(status))
606 1 : return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
607 0 : else if (WIFSIGNALED(status))
608 0 : return (format("failed due to signal %1%") % WTERMSIG(status)).str();
609 : else
610 0 : return "died abnormally";
611 0 : } else return "succeeded";
612 : }
613 :
614 :
615 : bool statusOk(int status)
616 33 : {
617 33 : return WIFEXITED(status) && WEXITSTATUS(status) == 0;
618 : }
619 :
620 :
621 : bool string2Int(const string & s, int & n)
622 8 : {
623 8 : istringstream str(s);
624 8 : str >> n;
625 8 : return str && str.eof();
626 51 : }
|