LTP GCOV extension - code coverage report
Current view: directory - src/libutil - util.cc
Test: app.info
Date: 2006-10-11 Instrumented lines: 400
Code covered: 76.5 % Executed lines: 306

       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 : }

Generated by: LTP GCOV extension version 1.1