LTP GCOV extension - code coverage report
Current view: directory - src/libmain - shared.cc
Test: app.info
Date: 2004-12-21 Instrumented lines: 120
Code covered: 73.3 % Executed lines: 88

       1                 : #include <iostream>
       2                 : #include <cctype>
       3                 : 
       4                 : #include <sys/types.h>
       5                 : #include <sys/stat.h>
       6                 : #include <unistd.h>
       7                 : 
       8                 : #include <pwd.h>
       9                 : #include <grp.h>
      10                 : 
      11                 : extern "C" {
      12                 : #include <aterm2.h>
      13                 : }
      14                 : 
      15                 : #include "globals.hh"
      16                 : #include "shared.hh"
      17                 : 
      18                 : #include "config.h"
      19                 : 
      20                 : 
      21                 : volatile sig_atomic_t blockInt = 0;
      22                 : 
      23                 : 
      24                 : void sigintHandler(int signo)
      25               0 : {
      26               0 :     if (!blockInt) {
      27               0 :         _isInterrupted = 1;
      28              51 :         blockInt = 1;
      29             102 :     }
      30               0 : }
      31                 : 
      32                 : 
      33                 : void setLogType(string lt)
      34               0 : {
      35               0 :     if (lt == "pretty") logType = ltPretty;
      36               0 :     else if (lt == "escapes") logType = ltEscapes;
      37               0 :     else if (lt == "flat") logType = ltFlat;
      38               0 :     else throw UsageError("unknown log type");
      39                 : }
      40                 : 
      41                 : 
      42                 : void checkStoreNotSymlink(Path path)
      43              51 : {
      44             357 :     struct stat st;
      45             357 :     while (path.size()) {
      46             306 :         if (lstat(path.c_str(), &st))
      47               0 :             throw SysError(format("getting status of `%1%'") % path);
      48             306 :         if (S_ISLNK(st.st_mode))
      49               0 :             throw Error(format(
      50                 :                 "the path `%1%' is a symlink; "
      51                 :                 "this is not allowed for the Nix store and its parent directories")
      52                 :                 % path);
      53             306 :         path = dirOf(path);
      54                 :     }
      55                 : }
      56                 : 
      57                 : 
      58                 : void initStoreExprHelpers();
      59                 : 
      60                 : 
      61                 : /* Initialize and reorder arguments, then call the actual argument
      62                 :    processor. */
      63                 : static void initAndRun(int argc, char * * argv)
      64              51 : {
      65              51 :     string root = getEnv("NIX_ROOT");
      66              51 :     if (root != "") {
      67               0 :         if (chroot(root.c_str()) != 0)
      68               0 :             throw SysError(format("changing root to `%1%'") % root);
      69                 :     }
      70                 :     
      71                 :     /* Setup Nix paths. */
      72              51 :     nixStore = getEnv("NIX_STORE_DIR", canonPath(NIX_STORE_DIR));
      73              51 :     nixDataDir = getEnv("NIX_DATA_DIR", canonPath(NIX_DATA_DIR));
      74              51 :     nixLogDir = getEnv("NIX_LOG_DIR", canonPath(NIX_LOG_DIR));
      75              51 :     nixStateDir = getEnv("NIX_STATE_DIR", canonPath(NIX_STATE_DIR));
      76              51 :     nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
      77                 : 
      78                 :     /* Check that the store directory and its parent are not
      79                 :        symlinks. */
      80              51 :     if (getEnv("NIX_IGNORE_SYMLINK_STORE") != "1")
      81              51 :         checkStoreNotSymlink(nixStore);
      82                 : 
      83                 :     /* Catch SIGINT. */
      84              51 :     struct sigaction act, oact;
      85              51 :     act.sa_handler = sigintHandler;
      86              51 :     sigfillset(&act.sa_mask);
      87              51 :     act.sa_flags = 0;
      88              51 :     if (sigaction(SIGINT, &act, &oact))
      89               0 :         throw SysError("installing handler for SIGINT");
      90                 : 
      91                 :     /* Ignore SIGPIPE. */
      92              51 :     act.sa_handler = SIG_IGN;
      93              51 :     act.sa_flags = 0;
      94              51 :     if (sigaction(SIGPIPE, &act, &oact))
      95               0 :         throw SysError("ignoring SIGPIPE");
      96                 : 
      97                 :     /* There is no privacy in the Nix system ;-)  At least not for
      98                 :        now.  In particular, store objects should be readable by
      99                 :        everybody.  This prevents nasty surprises when using a shared
     100                 :        store (with the setuid() hack). */
     101              51 :     umask(0022);
     102                 : 
     103                 :     /* Process the NIX_LOG_TYPE environment variable. */
     104              51 :     string lt = getEnv("NIX_LOG_TYPE");
     105              51 :     if (lt != "") setLogType(lt);
     106                 : 
     107                 :     /* ATerm stuff.  !!! find a better place to put this */
     108              51 :     initStoreExprHelpers();
     109                 :     
     110                 :     /* Put the arguments in a vector. */
     111              51 :     Strings args, remaining;
     112             189 :     while (argc--) args.push_back(*argv++);
     113              51 :     args.erase(args.begin());
     114                 :     
     115                 :     /* Expand compound dash options (i.e., `-qlf' -> `-q -l -f'), and
     116                 :        ignore options for the ATerm library. */
     117             138 :     for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
     118              87 :         string arg = *i;
     119              87 :         if (string(arg, 0, 4) == "-at-") ;
     120              87 :         else if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-') {
     121             122 :             for (unsigned int j = 1; j < arg.length(); j++)
     122             106 :                 if (isalpha(arg[j]))
     123             105 :                     remaining.push_back((string) "-" + arg[j]);
     124                 :                 else {
     125               1 :                     remaining.push_back(string(arg, j));
     126               1 :                     break;
     127                 :                 }
     128              70 :         } else remaining.push_back(arg);
     129                 :     }
     130              51 :     args = remaining;
     131              51 :     remaining.clear();
     132                 : 
     133                 :     /* Process default options. */
     134             226 :     for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
     135             175 :         string arg = *i;
     136             175 :         if (arg == "--verbose" || arg == "-v")
     137              69 :             verbosity = (Verbosity) ((int) verbosity + 1);
     138             106 :         else if (arg == "--log-type") {
     139               0 :             ++i;
     140               0 :             if (i == args.end()) throw UsageError("`--log-type' requires an argument");
     141               0 :             setLogType(*i);
     142                 :         }
     143             106 :         else if (arg == "--build-output" || arg == "-B")
     144                 :             ; /* !!! obsolete - remove eventually */
     145             101 :         else if (arg == "--no-build-output" || arg == "-Q")
     146               0 :             buildVerbosity = lvlVomit;
     147             101 :         else if (arg == "--help") {
     148               0 :             printHelp();
     149               0 :             return;
     150                 :         }
     151             101 :         else if (arg == "--version") {
     152               0 :             cout << format("%1% (Nix) %2%") % programId % NIX_VERSION << endl;
     153               0 :             return;
     154                 :         }
     155             101 :         else if (arg == "--keep-failed" || arg == "-K")
     156               0 :             keepFailed = true;
     157             101 :         else if (arg == "--keep-going" || arg == "-k")
     158               0 :             keepGoing = true;
     159             101 :         else if (arg == "--fallback")
     160               1 :             tryFallback = true;
     161             100 :         else if (arg == "--max-jobs" || arg == "-j") {
     162               1 :             ++i;
     163               1 :             if (i == args.end()) throw UsageError("`--max-jobs' requires an argument");
     164               1 :             int n;
     165               1 :             if (!string2Int(*i, n) || n < 0)
     166               0 :                 throw UsageError(format("`--max-jobs' requires a non-negative integer"));
     167               1 :             maxBuildJobs = n;
     168                 :         }
     169              99 :         else if (arg == "--readonly-mode")
     170               0 :             readOnlyMode = true;
     171              99 :         else remaining.push_back(arg);
     172                 :     }
     173                 : 
     174              51 :     run(remaining);
     175                 : }
     176                 : 
     177                 : 
     178                 : static bool haveSwitched;
     179                 : static uid_t savedUid, nixUid;
     180                 : static gid_t savedGid, nixGid;
     181                 : 
     182                 : 
     183                 : #if HAVE_SETRESUID
     184                 : #define _setuid(uid) setresuid(uid, uid, savedUid)
     185                 : #define _setgid(gid) setresgid(gid, gid, savedGid)
     186                 : #else
     187                 : /* Only works properly when run by root. */
     188                 : #define _setuid(uid) setuid(uid)
     189                 : #define _setgid(gid) setgid(gid)
     190                 : #endif
     191                 : 
     192                 : 
     193                 : SwitchToOriginalUser::SwitchToOriginalUser()
     194              42 : {
     195                 : #if SETUID_HACK && HAVE_SETRESUID
     196                 :     /* Temporarily switch the effective uid/gid back to the saved
     197                 :        uid/gid (which is the uid/gid of the user that executed the Nix
     198                 :        program; it's *not* the real uid/gid, since we changed that to
     199                 :        the Nix user in switchToNixUser()). */
     200                 :     if (haveSwitched) {
     201                 :         if (setuid(savedUid) == -1)
     202                 :             throw SysError(format("temporarily restoring uid to `%1%'") % savedUid); 
     203                 :         if (setgid(savedGid) == -1)
     204                 :             throw SysError(format("temporarily restoring gid to `%1%'") % savedGid); 
     205                 :     }
     206                 : #endif
     207                 : }
     208                 : 
     209                 : 
     210                 : SwitchToOriginalUser::~SwitchToOriginalUser()
     211              84 : {
     212                 : #if SETUID_HACK && HAVE_SETRESUID
     213                 :     /* Switch the effective uid/gid back to the Nix user. */
     214                 :     if (haveSwitched) {
     215                 :         if (setuid(nixUid) == -1)
     216                 :             throw SysError(format("restoring uid to `%1%'") % nixUid); 
     217                 :         if (setgid(nixGid) == -1)
     218                 :             throw SysError(format("restoring gid to `%1%'") % nixGid); 
     219                 :     }
     220                 : #endif
     221                 : }
     222                 : 
     223                 : 
     224                 : void switchToNixUser()
     225              51 : {
     226                 : #if SETUID_HACK
     227                 : 
     228                 :     /* Don't do anything if this is not a setuid binary. */
     229                 :     if (getuid() == geteuid() && getgid() == getegid()) return;
     230                 : 
     231                 :     /* Here we set the uid and gid to the Nix user and group,
     232                 :        respectively, IF the current (real) user is a member of the Nix
     233                 :        group.  Otherwise we just drop all privileges. */
     234                 :     
     235                 :     /* Lookup the Nix gid. */
     236                 :     struct group * gr = getgrnam(NIX_GROUP);
     237                 :     if (!gr) {
     238                 :         cerr << format("missing group `%1%'\n") % NIX_GROUP;
     239                 :         exit(1);
     240                 :     }
     241                 : 
     242                 :     /* Get the supplementary group IDs for the current user. */
     243                 :     int maxGids = 512, nrGids;
     244                 :     gid_t gids[maxGids];
     245                 :     if ((nrGids = getgroups(maxGids, gids)) == -1) {
     246                 :         cerr << format("unable to query gids\n");
     247                 :         exit(1);
     248                 :     }
     249                 : 
     250                 :     /* !!! Apparently it is unspecified whether getgroups() includes
     251                 :        the effective gid.  In that case the following test is always
     252                 :        true *if* the program is installed setgid (which we do when we
     253                 :        have setresuid()).  On Linux this doesn't appear to be the
     254                 :        case, but we should switch to the real gid before doing this
     255                 :        test, and then switch back to the saved gid. */ 
     256                 : 
     257                 :     /* Check that the current user is a member of the Nix group. */
     258                 :     bool found = false;
     259                 :     for (int i = 0; i < nrGids; ++i)
     260                 :         if (gids[i] == gr->gr_gid) {
     261                 :             found = true;
     262                 :             break;
     263                 :         }
     264                 : 
     265                 :     if (!found) {
     266                 :         /* Not in the Nix group - drop all root/Nix privileges. */
     267                 :         _setgid(getgid());
     268                 :         _setuid(getuid());
     269                 :         return;
     270                 :     }
     271                 : 
     272                 :     savedUid = getuid();
     273                 :     savedGid = getgid();
     274                 : 
     275                 :     /* Set the real, effective and saved gids to gr->gr_gid.  Also
     276                 :        make very sure that this succeeded.  We switch the gid first
     277                 :        because we cannot do it after we have dropped root uid. */
     278                 :     nixGid = gr->gr_gid;
     279                 :     if (_setgid(nixGid) != 0 || getgid() != nixGid || getegid() != nixGid) {
     280                 :         cerr << format("unable to set gid to `%1%'\n") % NIX_GROUP;
     281                 :         exit(1);
     282                 :     }
     283                 : 
     284                 :     /* Lookup the Nix uid. */
     285                 :     struct passwd * pw = getpwnam(NIX_USER);
     286                 :     if (!pw) {
     287                 :         cerr << format("missing user `%1%'\n") % NIX_USER;
     288                 :         exit(1);
     289                 :     }
     290                 : 
     291                 :     /* This will drop all root privileges, setting the real, effective
     292                 :        and saved uids to pw->pw_uid.  Also make very sure that this
     293                 :        succeeded.*/
     294                 :     nixUid = pw->pw_uid;
     295                 :     if (_setuid(nixUid) != 0 || getuid() != nixUid || geteuid() != nixUid) {
     296                 :         cerr << format("unable to set uid to `%1%'\n") % NIX_USER;
     297                 :         exit(1);
     298                 :     }
     299                 : 
     300                 :     haveSwitched = true;
     301                 :     
     302                 : #endif
     303                 : }
     304                 : 
     305                 : 
     306                 : static char buf[1024];
     307                 : 
     308                 : int main(int argc, char * * argv)
     309              51 : {
     310                 :     /* If we are setuid root, we have to get rid of the excess
     311                 :        privileges ASAP. */
     312              51 :     switchToNixUser();
     313                 :     
     314                 :     /* ATerm setup. */
     315              51 :     ATerm bottomOfStack;
     316              51 :     ATinit(argc, argv, &bottomOfStack);
     317                 : 
     318                 :     /* Turn on buffering for cerr. */
     319                 : #if HAVE_PUBSETBUF
     320              51 :     cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
     321                 : #endif
     322                 : 
     323              51 :     try {
     324              51 :         try {
     325              51 :             initAndRun(argc, argv);
     326               2 :         } catch (...) {
     327                 :             /* Subtle: we have to make sure that any `interrupted'
     328                 :                condition is discharged before we reach printMsg()
     329                 :                below, since otherwise it will throw an (uncaught)
     330                 :                exception. */
     331               2 :             blockInt = 1; /* ignore further SIGINTs */
     332               2 :             _isInterrupted = 0;
     333               2 :             throw;
     334                 :         }
     335               2 :     } catch (UsageError & e) {
     336               0 :         printMsg(lvlError, 
     337                 :             format(
     338                 :                 "error: %1%\n"
     339                 :                 "Try `%2% --help' for more information.")
     340                 :             % e.what() % programId);
     341               0 :         return 1;
     342               2 :     } catch (Error & e) {
     343               2 :         printMsg(lvlError, format("error: %1%") % e.msg());
     344               2 :         return 1;
     345               0 :     } catch (exception & e) {
     346               0 :         printMsg(lvlError, format("error: %1%") % e.what());
     347               0 :         return 1;
     348                 :     }
     349                 : 
     350              49 :     return 0;
     351              51 : }

Generated by: LTP GCOV extension version 1.1