1 : #include "config.h"
2 :
3 : #ifdef __CYGWIN__
4 : #include <windows.h>
5 : #endif
6 :
7 : #include <iostream>
8 : #include <cerrno>
9 : #include <cstdio>
10 : #include <sstream>
11 :
12 : #include <sys/stat.h>
13 : #include <sys/wait.h>
14 : #include <fcntl.h>
15 :
16 : #include <sys/types.h>
17 : #include <pwd.h>
18 : #include <grp.h>
19 :
20 : #include "util.hh"
21 :
22 :
23 : namespace nix {
24 :
25 :
26 : Error::Error(const format & f)
27 242 : {
28 121 : err = f.str();
29 : }
30 :
31 :
32 : Error & Error::addPrefix(const format & f)
33 17 : {
34 17 : err = f.str() + err;
35 17 : return *this;
36 : }
37 :
38 :
39 : SysError::SysError(const format & f)
40 7 : : Error(format("%1%: %2%") % f.str() % strerror(errno))
41 7 : {
42 : }
43 :
44 :
45 : string getEnv(const string & key, const string & def)
46 4035 : {
47 4035 : char * value = getenv(key.c_str());
48 4035 : return value ? string(value) : def;
49 : }
50 :
51 :
52 : Path absPath(Path path, Path dir)
53 495 : {
54 495 : if (path[0] != '/') {
55 205 : if (dir == "") {
56 87 : char buf[PATH_MAX];
57 87 : if (!getcwd(buf, sizeof(buf)))
58 0 : throw SysError("cannot get cwd");
59 87 : dir = buf;
60 : }
61 205 : path = dir + "/" + path;
62 : }
63 495 : return canonPath(path);
64 : }
65 :
66 :
67 : Path canonPath(const Path & path, bool resolveSymlinks)
68 8502 : {
69 8502 : string s;
70 :
71 8502 : if (path[0] != '/')
72 0 : throw Error(format("not an absolute path: `%1%'") % path);
73 :
74 8502 : string::const_iterator i = path.begin(), end = path.end();
75 8502 : string temp;
76 :
77 : /* Count the number of times we follow a symlink and stop at some
78 : arbitrary (but high) limit to prevent infinite loops. */
79 8502 : unsigned int followCount = 0, maxFollow = 1024;
80 :
81 124168 : while (1) {
82 :
83 : /* Skip slashes. */
84 124168 : while (i != end && *i == '/') i++;
85 66335 : if (i == end) break;
86 :
87 : /* Ignore `.'. */
88 57833 : if (*i == '.' && (i + 1 == end || i[1] == '/'))
89 163 : i++;
90 :
91 : /* If `..', delete the last component. */
92 57670 : else if (*i == '.' && i + 1 < end && i[1] == '.' &&
93 : (i + 2 == end || i[2] == '/'))
94 : {
95 10 : if (!s.empty()) s.erase(s.rfind('/'));
96 10 : i += 2;
97 : }
98 :
99 : /* Normal component; copy it. */
100 : else {
101 57660 : s += '/';
102 476003 : while (i != end && *i != '/') s += *i++;
103 :
104 : /* If s points to a symlink, resolve it and restart (since
105 : the symlink target might contain new symlinks). */
106 57660 : if (resolveSymlinks && isLink(s)) {
107 0 : followCount++;
108 0 : if (followCount >= maxFollow)
109 0 : throw Error(format("infinite symlink recursion in path `%1%'") % path);
110 0 : temp = absPath(readLink(s), dirOf(s))
111 : + string(i, end);
112 0 : i = temp.begin(); /* restart */
113 0 : end = temp.end();
114 0 : s = "";
115 : /* !!! potential for infinite loop */
116 : }
117 : }
118 : }
119 :
120 8502 : return s.empty() ? "/" : s;
121 : }
122 :
123 :
124 : Path dirOf(const Path & path)
125 2878 : {
126 2878 : Path::size_type pos = path.rfind('/');
127 2878 : if (pos == string::npos)
128 0 : throw Error(format("invalid file name: %1%") % path);
129 2878 : return pos == 0 ? "/" : Path(path, 0, pos);
130 : }
131 :
132 :
133 : string baseNameOf(const Path & path)
134 470 : {
135 470 : Path::size_type pos = path.rfind('/');
136 470 : if (pos == string::npos)
137 0 : throw Error(format("invalid file name %1% ") % path);
138 470 : return string(path, pos + 1);
139 : }
140 :
141 :
142 : bool pathExists(const Path & path)
143 2465 : {
144 2465 : int res;
145 2465 : struct stat st;
146 2465 : res = lstat(path.c_str(), &st);
147 2465 : if (!res) return true;
148 435 : if (errno != ENOENT && errno != ENOTDIR)
149 0 : throw SysError(format("getting status of %1%") % path);
150 435 : return false;
151 : }
152 :
153 :
154 : Path readLink(const Path & path)
155 166 : {
156 166 : struct stat st;
157 166 : if (lstat(path.c_str(), &st))
158 0 : throw SysError(format("getting status of `%1%'") % path);
159 166 : if (!S_ISLNK(st.st_mode))
160 0 : throw Error(format("`%1%' is not a symlink") % path);
161 166 : char buf[st.st_size];
162 166 : if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
163 0 : throw SysError(format("reading symbolic link `%1%'") % path);
164 166 : return string(buf, st.st_size);
165 : }
166 :
167 :
168 : bool isLink(const Path & path)
169 129 : {
170 129 : struct stat st;
171 129 : if (lstat(path.c_str(), &st))
172 0 : throw SysError(format("getting status of `%1%'") % path);
173 129 : return S_ISLNK(st.st_mode);
174 : }
175 :
176 :
177 : Strings readDirectory(const Path & path)
178 416 : {
179 416 : Strings names;
180 :
181 416 : AutoCloseDir dir = opendir(path.c_str());
182 416 : if (!dir) throw SysError(format("opening directory `%1%'") % path);
183 :
184 4801 : struct dirent * dirent;
185 4801 : while (errno = 0, dirent = readdir(dir)) { /* sic */
186 4385 : checkInterrupt();
187 4385 : string name = dirent->d_name;
188 4385 : if (name == "." || name == "..") continue;
189 3553 : names.push_back(name);
190 : }
191 416 : if (errno) throw SysError(format("reading directory `%1%'") % path);
192 :
193 416 : return names;
194 : }
195 :
196 :
197 : string readFile(int fd)
198 532 : {
199 532 : struct stat st;
200 532 : if (fstat(fd, &st) == -1)
201 0 : throw SysError("statting file");
202 532 : unsigned char buf[st.st_size]; /* !!! stack space */
203 532 : readFull(fd, buf, st.st_size);
204 :
205 532 : return string((char *) buf, st.st_size);
206 : }
207 :
208 :
209 : string readFile(const Path & path)
210 531 : {
211 531 : AutoCloseFD fd = open(path.c_str(), O_RDONLY);
212 531 : if (fd == -1)
213 0 : throw SysError(format("opening file `%1%'") % path);
214 531 : return readFile(fd);
215 : }
216 :
217 :
218 : void writeFile(const Path & path, const string & s)
219 17 : {
220 17 : AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
221 17 : if (fd == -1)
222 0 : throw SysError(format("opening file `%1%'") % path);
223 17 : writeFull(fd, (unsigned char *) s.c_str(), s.size());
224 : }
225 :
226 :
227 : static void _deletePath(const Path & path, unsigned long long & bytesFreed)
228 2861 : {
229 2861 : checkInterrupt();
230 :
231 2861 : printMsg(lvlVomit, format("%1%") % path);
232 :
233 2861 : struct stat st;
234 2861 : if (lstat(path.c_str(), &st))
235 6 : throw SysError(format("getting attributes of path `%1%'") % path);
236 :
237 2855 : bytesFreed += st.st_size;
238 :
239 2855 : if (S_ISDIR(st.st_mode)) {
240 122 : Strings names = readDirectory(path);
241 :
242 : /* Make the directory writable. */
243 122 : if (!(st.st_mode & S_IWUSR)) {
244 56 : if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
245 0 : throw SysError(format("making `%1%' writable") % path);
246 : }
247 :
248 234 : for (Strings::iterator i = names.begin(); i != names.end(); ++i)
249 112 : _deletePath(path + "/" + *i, bytesFreed);
250 : }
251 :
252 2855 : if (remove(path.c_str()) == -1)
253 0 : throw SysError(format("cannot unlink `%1%'") % path);
254 : }
255 :
256 :
257 : void deletePath(const Path & path)
258 79 : {
259 79 : unsigned long long dummy;
260 79 : deletePath(path, dummy);
261 : }
262 :
263 :
264 : void deletePath(const Path & path, unsigned long long & bytesFreed)
265 2749 : {
266 2749 : startNest(nest, lvlDebug,
267 : format("recursively deleting path `%1%'") % path);
268 2749 : bytesFreed = 0;
269 2749 : _deletePath(path, bytesFreed);
270 : }
271 :
272 :
273 : void makePathReadOnly(const Path & path)
274 0 : {
275 0 : checkInterrupt();
276 :
277 0 : struct stat st;
278 0 : if (lstat(path.c_str(), &st))
279 0 : throw SysError(format("getting attributes of path `%1%'") % path);
280 :
281 0 : if (!S_ISLNK(st.st_mode) && (st.st_mode & S_IWUSR)) {
282 0 : if (chmod(path.c_str(), st.st_mode & ~S_IWUSR) == -1)
283 0 : throw SysError(format("making `%1%' read-only") % path);
284 : }
285 :
286 0 : if (S_ISDIR(st.st_mode)) {
287 0 : Strings names = readDirectory(path);
288 0 : for (Strings::iterator i = names.begin(); i != names.end(); ++i)
289 0 : makePathReadOnly(path + "/" + *i);
290 : }
291 : }
292 :
293 :
294 : static Path tempName()
295 64 : {
296 64 : static int counter = 0;
297 64 : Path tmpRoot = canonPath(getEnv("TMPDIR", "/tmp"), true);
298 64 : return (format("%1%/nix-%2%-%3%") % tmpRoot % getpid() % counter++).str();
299 : }
300 :
301 :
302 : Path createTempDir()
303 64 : {
304 64 : while (1) {
305 64 : checkInterrupt();
306 64 : Path tmpDir = tempName();
307 64 : if (mkdir(tmpDir.c_str(), 0777) == 0) {
308 : /* Explicitly set the group of the directory. This is to
309 : work around around problems caused by BSD's group
310 : ownership semantics (directories inherit the group of
311 : the parent). For instance, the group of /tmp on
312 : FreeBSD is "wheel", so all directories created in /tmp
313 : will be owned by "wheel"; but if the user is not in
314 : "wheel", then "tar" will fail to unpack archives that
315 : have the setgid bit set on directories. */
316 64 : if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0)
317 0 : throw SysError(format("setting group of directory `%1%'") % tmpDir);
318 64 : return tmpDir;
319 : }
320 0 : if (errno != EEXIST)
321 0 : throw SysError(format("creating directory `%1%'") % tmpDir);
322 : }
323 : }
324 :
325 :
326 : void createDirs(const Path & path)
327 1500 : {
328 1500 : if (path == "/") return;
329 1340 : createDirs(dirOf(path));
330 1340 : if (!pathExists(path))
331 1 : if (mkdir(path.c_str(), 0777) == -1)
332 0 : throw SysError(format("creating directory `%1%'") % path);
333 : }
334 :
335 :
336 : void writeStringToFile(const Path & path, const string & s)
337 89 : {
338 : AutoCloseFD fd(open(path.c_str(),
339 89 : O_CREAT | O_EXCL | O_WRONLY, 0666));
340 89 : if (fd == -1)
341 0 : throw SysError(format("creating file `%1%'") % path);
342 89 : writeFull(fd, (unsigned char *) s.c_str(), s.size());
343 : }
344 :
345 :
346 : LogType logType = ltPretty;
347 : Verbosity verbosity = lvlInfo;
348 :
349 : static int nestingLevel = 0;
350 :
351 :
352 : Nest::Nest()
353 10441 : {
354 10441 : nest = false;
355 : }
356 :
357 :
358 : Nest::~Nest()
359 20882 : {
360 10441 : close();
361 : }
362 :
363 :
364 : static string escVerbosity(Verbosity level)
365 10 : {
366 10 : return int2String((int) level);
367 : }
368 :
369 :
370 : void Nest::open(Verbosity level, const format & f)
371 86 : {
372 86 : if (level <= verbosity) {
373 86 : if (logType == ltEscapes)
374 6 : std::cerr << "\033[" << escVerbosity(level) << "p"
375 : << f.str() << "\n";
376 : else
377 80 : printMsg_(level, f);
378 86 : nest = true;
379 86 : nestingLevel++;
380 : }
381 : }
382 :
383 :
384 : void Nest::close()
385 10441 : {
386 10441 : if (nest) {
387 86 : nestingLevel--;
388 86 : if (logType == ltEscapes)
389 6 : std::cerr << "\033[q";
390 86 : nest = false;
391 : }
392 : }
393 :
394 :
395 : void printMsg_(Verbosity level, const format & f)
396 2998 : {
397 2998 : checkInterrupt();
398 2998 : if (level > verbosity) return;
399 2998 : string prefix;
400 2998 : if (logType == ltPretty)
401 3013 : for (int i = 0; i < nestingLevel; i++)
402 19 : prefix += "| ";
403 4 : else if (logType == ltEscapes && level != lvlInfo)
404 4 : prefix = "\033[" + escVerbosity(level) + "s";
405 2998 : string s = (format("%1%%2%\n") % prefix % f.str()).str();
406 2998 : writeFull(STDERR_FILENO, (const unsigned char *) s.c_str(), s.size());
407 : }
408 :
409 :
410 : void warnOnce(bool & haveWarned, const format & f)
411 74 : {
412 74 : if (!haveWarned) {
413 50 : printMsg(lvlError, format("warning: %1%") % f.str());
414 50 : haveWarned = true;
415 : }
416 : }
417 :
418 :
419 : void readFull(int fd, unsigned char * buf, size_t count)
420 3344 : {
421 3344 : while (count) {
422 1672 : checkInterrupt();
423 1672 : ssize_t res = read(fd, (char *) buf, count);
424 1672 : if (res == -1) {
425 0 : if (errno == EINTR) continue;
426 0 : throw SysError("reading from file");
427 : }
428 1672 : if (res == 0) throw Error("unexpected end-of-file");
429 1672 : count -= res;
430 1672 : buf += res;
431 : }
432 : }
433 :
434 :
435 : void writeFull(int fd, const unsigned char * buf, size_t count)
436 17038 : {
437 17038 : while (count) {
438 8519 : checkInterrupt();
439 8519 : ssize_t res = write(fd, (char *) buf, count);
440 8519 : if (res == -1) {
441 0 : if (errno == EINTR) continue;
442 0 : throw SysError("writing to file");
443 : }
444 8519 : count -= res;
445 8519 : buf += res;
446 : }
447 : }
448 :
449 :
450 : string drainFD(int fd)
451 1 : {
452 1 : string result;
453 106 : unsigned char buffer[4096];
454 106 : while (1) {
455 106 : ssize_t rd = read(fd, buffer, sizeof buffer);
456 106 : if (rd == -1) {
457 0 : if (errno != EINTR)
458 0 : throw SysError("reading from file");
459 : }
460 106 : else if (rd == 0) break;
461 105 : else result.append((char *) buffer, rd);
462 : }
463 0 : return result;
464 : }
465 :
466 :
467 :
468 : //////////////////////////////////////////////////////////////////////
469 :
470 :
471 0 : AutoDelete::AutoDelete(const string & p) : path(p)
472 0 : {
473 0 : del = true;
474 : }
475 :
476 : AutoDelete::~AutoDelete()
477 0 : {
478 0 : if (del) deletePath(path);
479 : }
480 :
481 : void AutoDelete::cancel()
482 0 : {
483 0 : del = false;
484 : }
485 :
486 :
487 :
488 : //////////////////////////////////////////////////////////////////////
489 :
490 :
491 : AutoCloseFD::AutoCloseFD()
492 5070 : {
493 5070 : fd = -1;
494 : }
495 :
496 :
497 : AutoCloseFD::AutoCloseFD(int fd)
498 3928 : {
499 3928 : this->fd = fd;
500 : }
501 :
502 :
503 : AutoCloseFD::AutoCloseFD(const AutoCloseFD & fd)
504 0 : {
505 0 : abort();
506 : }
507 :
508 :
509 : AutoCloseFD::~AutoCloseFD()
510 17306 : {
511 8653 : try {
512 8653 : close();
513 0 : } catch (Error & e) {
514 0 : printMsg(lvlError, format("error (ignored): %1%") % e.msg());
515 : }
516 : }
517 :
518 :
519 : void AutoCloseFD::operator =(int fd)
520 1008 : {
521 1008 : if (this->fd != fd) close();
522 1008 : this->fd = fd;
523 : }
524 :
525 :
526 : AutoCloseFD::operator int() const
527 16182 : {
528 16182 : return fd;
529 : }
530 :
531 :
532 : void AutoCloseFD::close()
533 10153 : {
534 10153 : if (fd != -1) {
535 4301 : if (::close(fd) == -1)
536 : /* This should never happen. */
537 0 : throw SysError("closing file descriptor");
538 4301 : fd = -1;
539 : }
540 : }
541 :
542 :
543 : bool AutoCloseFD::isOpen()
544 64 : {
545 64 : return fd != -1;
546 : }
547 :
548 :
549 : /* Pass responsibility for closing this fd to the caller. */
550 : int AutoCloseFD::borrow()
551 722 : {
552 722 : int oldFD = fd;
553 722 : fd = -1;
554 722 : return oldFD;
555 : }
556 :
557 :
558 : void Pipe::create()
559 87 : {
560 87 : int fds[2];
561 87 : if (pipe(fds) != 0) throw SysError("creating pipe");
562 87 : readSide = fds[0];
563 87 : writeSide = fds[1];
564 : }
565 :
566 :
567 :
568 : //////////////////////////////////////////////////////////////////////
569 :
570 :
571 : AutoCloseDir::AutoCloseDir()
572 0 : {
573 0 : dir = 0;
574 : }
575 :
576 :
577 : AutoCloseDir::AutoCloseDir(DIR * dir)
578 416 : {
579 416 : this->dir = dir;
580 : }
581 :
582 :
583 : AutoCloseDir::~AutoCloseDir()
584 832 : {
585 416 : if (dir) closedir(dir);
586 : }
587 :
588 :
589 : void AutoCloseDir::operator =(DIR * dir)
590 0 : {
591 0 : this->dir = dir;
592 : }
593 :
594 :
595 : AutoCloseDir::operator DIR *()
596 5217 : {
597 5217 : return dir;
598 : }
599 :
600 :
601 :
602 : //////////////////////////////////////////////////////////////////////
603 :
604 :
605 : Pid::Pid()
606 334 : {
607 334 : pid = -1;
608 334 : separatePG = false;
609 : }
610 :
611 :
612 : Pid::~Pid()
613 668 : {
614 334 : kill();
615 : }
616 :
617 :
618 : void Pid::operator =(pid_t pid)
619 162 : {
620 162 : if (this->pid != pid) kill();
621 162 : this->pid = pid;
622 : }
623 :
624 :
625 : Pid::operator pid_t()
626 655 : {
627 655 : return pid;
628 : }
629 :
630 :
631 : void Pid::kill()
632 496 : {
633 496 : if (pid == -1) return;
634 :
635 0 : printMsg(lvlError, format("killing process %1%") % pid);
636 :
637 : /* Send a KILL signal to the child. If it has its own process
638 : group, send the signal to every process in the child process
639 : group (which hopefully includes *all* its children). */
640 0 : if (::kill(separatePG ? -pid : pid, SIGKILL) != 0)
641 0 : printMsg(lvlError, (SysError(format("killing process %1%") % pid).msg()));
642 :
643 : /* Wait until the child dies, disregarding the exit status. */
644 0 : int status;
645 0 : while (waitpid(pid, &status, 0) == -1)
646 0 : if (errno != EINTR) printMsg(lvlError,
647 : (SysError(format("waiting for process %1%") % pid).msg()));
648 :
649 0 : pid = -1;
650 : }
651 :
652 :
653 : int Pid::wait(bool block)
654 81 : {
655 81 : while (1) {
656 81 : int status;
657 81 : int res = waitpid(pid, &status, block ? 0 : WNOHANG);
658 81 : if (res == pid) {
659 81 : pid = -1;
660 81 : return status;
661 : }
662 0 : if (res == 0 && !block) return -1;
663 0 : if (errno != EINTR)
664 0 : throw SysError("cannot get child exit status");
665 : }
666 : }
667 :
668 :
669 : void Pid::setSeparatePG(bool separatePG)
670 80 : {
671 80 : this->separatePG = separatePG;
672 : }
673 :
674 :
675 :
676 : //////////////////////////////////////////////////////////////////////
677 :
678 :
679 : string runProgram(Path program)
680 1 : {
681 : /* Create a pipe. */
682 1 : Pipe pipe;
683 1 : pipe.create();
684 :
685 : /* Fork. */
686 1 : Pid pid;
687 1 : pid = fork();
688 2 : switch (pid) {
689 :
690 : case -1:
691 0 : throw SysError("unable to fork");
692 :
693 : case 0: /* child */
694 1 : try {
695 1 : pipe.readSide.close();
696 :
697 1 : if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
698 0 : throw SysError("dupping from-hook write side");
699 :
700 1 : execl(program.c_str(), program.c_str(), (char *) 0);
701 0 : throw SysError(format("executing `%1%'") % program);
702 :
703 0 : } catch (std::exception & e) {
704 0 : std::cerr << "error: " << e.what() << std::endl;
705 : }
706 0 : quickExit(1);
707 : }
708 :
709 : /* Parent. */
710 :
711 1 : pipe.writeSide.close();
712 :
713 1 : string result = drainFD(pipe.readSide);
714 :
715 : /* Wait for the child to finish. */
716 1 : int status = pid.wait(true);
717 1 : if (!statusOk(status))
718 0 : throw Error(format("program `%1% %2%")
719 : % program % statusToString(status));
720 :
721 1 : return result;
722 : }
723 :
724 :
725 : void quickExit(int status)
726 0 : {
727 : #ifdef __CYGWIN__
728 : /* Hack for Cygwin: _exit() doesn't seem to work quite right,
729 : since some Berkeley DB code appears to be called when a child
730 : exits through _exit() (e.g., because execve() failed). So call
731 : the Windows API directly. */
732 : ExitProcess(status);
733 : #else
734 0 : _exit(status);
735 : #endif
736 : }
737 :
738 :
739 : //////////////////////////////////////////////////////////////////////
740 :
741 :
742 : volatile sig_atomic_t _isInterrupted = 0;
743 :
744 : void _interrupted()
745 0 : {
746 : /* Block user interrupts while an exception is being handled.
747 : Throwing an exception while another exception is being handled
748 : kills the program! */
749 0 : if (!std::uncaught_exception()) {
750 0 : _isInterrupted = 0;
751 0 : throw Error("interrupted by the user");
752 : }
753 : }
754 :
755 :
756 :
757 : //////////////////////////////////////////////////////////////////////
758 :
759 :
760 : string packStrings(const Strings & strings)
761 2704 : {
762 2704 : string d;
763 5561 : for (Strings::const_iterator i = strings.begin();
764 : i != strings.end(); ++i)
765 : {
766 2857 : unsigned int len = i->size();
767 2857 : d += len & 0xff;
768 2857 : d += (len >> 8) & 0xff;
769 2857 : d += (len >> 16) & 0xff;
770 2857 : d += (len >> 24) & 0xff;
771 2857 : d += *i;
772 : }
773 0 : return d;
774 : }
775 :
776 :
777 : Strings unpackStrings(const string & s)
778 5626 : {
779 5626 : Strings strings;
780 :
781 5626 : string::const_iterator i = s.begin();
782 :
783 11753 : while (i != s.end()) {
784 :
785 6127 : if (i + 4 > s.end())
786 0 : throw Error(format("short db entry: `%1%'") % s);
787 :
788 6127 : unsigned int len;
789 6127 : len = (unsigned char) *i++;
790 6127 : len |= ((unsigned char) *i++) << 8;
791 6127 : len |= ((unsigned char) *i++) << 16;
792 6127 : len |= ((unsigned char) *i++) << 24;
793 :
794 6127 : if (len == 0xffffffff) return strings; /* explicit end-of-list */
795 :
796 6127 : if (i + len > s.end())
797 0 : throw Error(format("short db entry: `%1%'") % s);
798 :
799 6127 : strings.push_back(string(i, i + len));
800 6127 : i += len;
801 : }
802 :
803 0 : return strings;
804 : }
805 :
806 :
807 : Strings tokenizeString(const string & s, const string & separators)
808 1022 : {
809 1022 : Strings result;
810 1022 : string::size_type pos = s.find_first_not_of(separators, 0);
811 10932 : while (pos != string::npos) {
812 9910 : string::size_type end = s.find_first_of(separators, pos + 1);
813 9910 : if (end == string::npos) end = s.size();
814 9910 : string token(s, pos, end - pos);
815 9910 : result.push_back(token);
816 9910 : pos = s.find_first_not_of(separators, end);
817 : }
818 0 : return result;
819 : }
820 :
821 :
822 : string statusToString(int status)
823 3 : {
824 3 : if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
825 3 : if (WIFEXITED(status))
826 3 : return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
827 0 : else if (WIFSIGNALED(status))
828 0 : return (format("failed due to signal %1%") % WTERMSIG(status)).str();
829 : else
830 0 : return "died abnormally";
831 0 : } else return "succeeded";
832 : }
833 :
834 :
835 : bool statusOk(int status)
836 79 : {
837 79 : return WIFEXITED(status) && WEXITSTATUS(status) == 0;
838 : }
839 :
840 :
841 : string int2String(int n)
842 10 : {
843 10 : std::ostringstream str;
844 10 : str << n;
845 10 : return str.str();
846 : }
847 :
848 :
849 : bool string2Int(const string & s, int & n)
850 3578 : {
851 3578 : std::istringstream str(s);
852 3578 : str >> n;
853 3578 : return str && str.get() == EOF;
854 : }
855 :
856 :
857 : //////////////////////////////////////////////////////////////////////
858 :
859 :
860 : static bool haveSwitched;
861 : static uid_t savedUid, nixUid;
862 : static gid_t savedGid, nixGid;
863 :
864 :
865 : #if HAVE_SETRESUID
866 : #define _setuid(uid) setresuid(uid, uid, savedUid)
867 : #define _setgid(gid) setresgid(gid, gid, savedGid)
868 : #else
869 : /* Only works properly when run by root. */
870 : #define _setuid(uid) setuid(uid)
871 : #define _setgid(gid) setgid(gid)
872 : #endif
873 :
874 :
875 : SwitchToOriginalUser::SwitchToOriginalUser()
876 304 : {
877 : #if SETUID_HACK && HAVE_SETRESUID
878 : /* Temporarily switch the effective uid/gid back to the saved
879 : uid/gid (which is the uid/gid of the user that executed the Nix
880 : program; it's *not* the real uid/gid, since we changed that to
881 : the Nix user in switchToNixUser()). */
882 : if (haveSwitched) {
883 : if (setuid(savedUid) == -1)
884 : throw SysError(format("temporarily restoring uid to `%1%'") % savedUid);
885 : if (setgid(savedGid) == -1)
886 : throw SysError(format("temporarily restoring gid to `%1%'") % savedGid);
887 : }
888 : #endif
889 : }
890 :
891 :
892 : SwitchToOriginalUser::~SwitchToOriginalUser()
893 608 : {
894 : #if SETUID_HACK && HAVE_SETRESUID
895 : /* Switch the effective uid/gid back to the Nix user. */
896 : if (haveSwitched) {
897 : if (setuid(nixUid) == -1)
898 : throw SysError(format("restoring uid to `%1%'") % nixUid);
899 : if (setgid(nixGid) == -1)
900 : throw SysError(format("restoring gid to `%1%'") % nixGid);
901 : }
902 : #endif
903 : }
904 :
905 :
906 : void switchToNixUser()
907 345 : {
908 : #if SETUID_HACK
909 :
910 : /* Don't do anything if this is not a setuid binary. */
911 : if (getuid() == geteuid() && getgid() == getegid()) return;
912 :
913 : /* Here we set the uid and gid to the Nix user and group,
914 : respectively, IF the current (real) user is a member of the Nix
915 : group. Otherwise we just drop all privileges. */
916 :
917 : /* Lookup the Nix gid. */
918 : struct group * gr = getgrnam(NIX_GROUP);
919 : if (!gr) {
920 : std::cerr << format("missing group `%1%'\n") % NIX_GROUP;
921 : exit(1);
922 : }
923 :
924 : /* Get the supplementary group IDs for the current user. */
925 : int maxGids = 512, nrGids;
926 : gid_t gids[maxGids];
927 : if ((nrGids = getgroups(maxGids, gids)) == -1) {
928 : std::cerr << format("unable to query gids\n");
929 : exit(1);
930 : }
931 :
932 : /* !!! Apparently it is unspecified whether getgroups() includes
933 : the effective gid. In that case the following test is always
934 : true *if* the program is installed setgid (which we do when we
935 : have setresuid()). On Linux this doesn't appear to be the
936 : case, but we should switch to the real gid before doing this
937 : test, and then switch back to the saved gid. */
938 :
939 : /* Check that the current user is a member of the Nix group. */
940 : bool found = false;
941 : for (int i = 0; i < nrGids; ++i)
942 : if (gids[i] == gr->gr_gid) {
943 : found = true;
944 : break;
945 : }
946 :
947 : if (!found) {
948 : /* Not in the Nix group - drop all root/Nix privileges. */
949 : _setgid(getgid());
950 : _setuid(getuid());
951 : return;
952 : }
953 :
954 : savedUid = getuid();
955 : savedGid = getgid();
956 :
957 : /* Set the real, effective and saved gids to gr->gr_gid. Also
958 : make very sure that this succeeded. We switch the gid first
959 : because we cannot do it after we have dropped root uid. */
960 : nixGid = gr->gr_gid;
961 : if (_setgid(nixGid) != 0 || getgid() != nixGid || getegid() != nixGid) {
962 : std::cerr << format("unable to set gid to `%1%'\n") % NIX_GROUP;
963 : exit(1);
964 : }
965 :
966 : /* Lookup the Nix uid. */
967 : struct passwd * pw = getpwnam(NIX_USER);
968 : if (!pw) {
969 : std::cerr << format("missing user `%1%'\n") % NIX_USER;
970 : exit(1);
971 : }
972 :
973 : /* This will drop all root privileges, setting the real, effective
974 : and saved uids to pw->pw_uid. Also make very sure that this
975 : succeeded.*/
976 : nixUid = pw->pw_uid;
977 : if (_setuid(nixUid) != 0 || getuid() != nixUid || geteuid() != nixUid) {
978 : std::cerr << format("unable to set uid to `%1%'\n") % NIX_USER;
979 : exit(1);
980 : }
981 :
982 : /* !!! for setuid operation, we should: 1) wipe the environment;
983 : 2) verify file descriptors 0, 1, 2; 3) etc.
984 : See: http://www.daemon-systems.org/man/setuid.7.html
985 : */
986 :
987 : haveSwitched = true;
988 :
989 : #endif
990 : }
991 :
992 :
993 345 : }
|