about summary refs log tree commit diff
path: root/src/nix.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix.cc')
-rw-r--r--src/nix.cc228
1 files changed, 151 insertions, 77 deletions
diff --git a/src/nix.cc b/src/nix.cc
index 8108c2fca9fc..db9967537f15 100644
--- a/src/nix.cc
+++ b/src/nix.cc
@@ -16,8 +16,11 @@
 using namespace std;
 
 
+#define PKGINFO_ENVVAR "NIX_DB"
 #define PKGINFO_PATH "/pkg/sys/var/pkginfo"
 
+#define PKGHOME_ENVVAR "NIX_PKGHOME"
+
 
 static string dbRefs = "refs";
 static string dbInstPkgs = "pkginst";
@@ -27,6 +30,25 @@ static string prog;
 static string dbfile = PKGINFO_PATH;
 
 
+static string pkgHome = "/pkg";
+
+
+class Error : public exception
+{
+    string err;
+public:
+    Error(string _err) { err = _err; }
+    ~Error() throw () { };
+    const char * what() const throw () { return err.c_str(); }
+};
+
+class UsageError : public Error
+{
+public:
+    UsageError(string _err) : Error(_err) { };
+};
+
+
 /* Wrapper class that ensures that the database is closed upon
    object destruction. */
 class Db2 : public Db 
@@ -96,12 +118,12 @@ void checkRef(const string & s)
 {
     string err = "invalid reference: " + s;
     if (s.length() != 32)
-        throw err;
+        throw Error(err);
     for (int i = 0; i < 32; i++) {
         char c = s[i];
         if (!((c >= '0' && c <= '9') ||
               (c >= 'a' && c <= 'f')))
-            throw err;
+            throw Error(err);
     }
 }
 
@@ -112,10 +134,10 @@ string makeRef(string filename)
     char hash[33];
 
     FILE * pipe = popen(("md5sum " + filename).c_str(), "r");
-    if (!pipe) throw string("cannot execute md5sum");
+    if (!pipe) throw Error("cannot execute md5sum");
 
     if (fread(hash, 32, 1, pipe) != 1)
-        throw string("cannot read hash from md5sum");
+        throw Error("cannot read hash from md5sum");
     hash[32] = 0;
 
     pclose(pipe);
@@ -166,7 +188,7 @@ void readPkgDescr(const string & pkgfile,
             pkgImports.push_back(Dep(name, ref));
         else if (op == "=")
             fileImports.push_back(Dep(name, ref));
-        else throw string("invalid operator " + op);
+        else throw Error("invalid operator " + op);
     }
 }
 
@@ -187,13 +209,13 @@ void installPkg(string pkgref)
     string builder;
 
     if (!queryDB("refs", pkgref, pkgfile))
-        throw string("unknown package " + pkgref);
+        throw Error("unknown package " + pkgref);
 
     cerr << "installing package " + pkgref + " from " + pkgfile + "\n";
 
     /* Verify that the file hasn't changed. !!! race */
     if (makeRef(pkgfile) != pkgref)
-        throw string("file " + pkgfile + " is stale");
+        throw Error("file " + pkgfile + " is stale");
 
     /* Read the package description file. */
     DepList pkgImports, fileImports;
@@ -222,10 +244,10 @@ void installPkg(string pkgref)
         string file;
 
         if (!queryDB("refs", it->ref, file))
-            throw string("unknown file " + it->ref);
+            throw Error("unknown file " + it->ref);
 
         if (makeRef(file) != it->ref)
-            throw string("file " + file + " is stale");
+            throw Error("file " + file + " is stale");
 
         if (it->name == "build")
             builder = file;
@@ -234,57 +256,66 @@ void installPkg(string pkgref)
     }
 
     if (builder == "")
-        throw string("no builder specified");
+        throw Error("no builder specified");
 
     /* Construct a path for the installed package. */
-    path = "/pkg/" + pkgref;
+    path = pkgHome + "/" + pkgref;
 
     /* Create the path. */
     if (mkdir(path.c_str(), 0777))
-        throw string("unable to create directory " + path);
+        throw Error("unable to create directory " + path);
 
-    /* Fork a child to build the package. */
-    pid_t pid;
-    switch (pid = fork()) {
+    try {
 
-    case -1:
-        throw string("unable to fork");
+        /* Fork a child to build the package. */
+        pid_t pid;
+        switch (pid = fork()) {
+            
+        case -1:
+            throw Error("unable to fork");
 
-    case 0: /* child */
+        case 0: { /* child */
 
-        /* Go to the build directory. */
-        if (chdir(path.c_str())) {
-            cout << "unable to chdir to package directory\n";
-            _exit(1);
-        }
+            /* Go to the build directory. */
+            if (chdir(path.c_str())) {
+                cout << "unable to chdir to package directory\n";
+                _exit(1);
+            }
 
-        /* Fill in the environment.  We don't bother freeing the
-           strings, since we'll exec or die soon anyway. */
-        const char * env2[env.size() + 1];
-        int i = 0;
-        for (Environment::iterator it = env.begin();
-             it != env.end(); it++, i++)
-            env2[i] = (new string(it->first + "=" + it->second))->c_str();
-        env2[i] = 0;
+            /* Fill in the environment.  We don't bother freeing the
+               strings, since we'll exec or die soon anyway. */
+            const char * env2[env.size() + 1];
+            int i = 0;
+            for (Environment::iterator it = env.begin();
+                 it != env.end(); it++, i++)
+                env2[i] = (new string(it->first + "=" + it->second))->c_str();
+            env2[i] = 0;
 
-        /* Execute the builder.  This should not return. */
-        execle(builder.c_str(), builder.c_str(), 0, env2);
+            /* Execute the builder.  This should not return. */
+            execle(builder.c_str(), builder.c_str(), 0, env2);
 
-        cout << strerror(errno) << endl;
+            cout << strerror(errno) << endl;
 
-        cout << "unable to execute builder\n";
-        _exit(1);
-    }
+            cout << "unable to execute builder\n";
+            _exit(1);
+        }
+
+        }
 
-    /* parent */
+        /* parent */
 
-    /* Wait for the child to finish. */
-    int status;
-    if (waitpid(pid, &status, 0) != pid)
-        throw string("unable to wait for child");
+        /* Wait for the child to finish. */
+        int status;
+        if (waitpid(pid, &status, 0) != pid)
+            throw Error("unable to wait for child");
     
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
-        throw string("unable to build package");
+        if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+            throw Error("unable to build package");
+    
+    } catch (exception &) {
+        system(("rm -rf " + path).c_str());
+        throw;
+    }
 
     setDB(dbInstPkgs, pkgref, path);
 }
@@ -305,7 +336,7 @@ string absPath(string filename)
     if (filename[0] != '/') {
         char buf[PATH_MAX];
         if (!getcwd(buf, sizeof(buf)))
-            throw string("cannot get cwd");
+            throw Error("cannot get cwd");
         filename = string(buf) + "/" + filename;
         /* !!! canonicalise */
     }
@@ -343,69 +374,112 @@ void run(int argc, char * * argv)
     string cmd;
 
     if (argc < 1)
-        throw string("command not specified");
+        throw UsageError("no command specified");
 
     cmd = argv[0];
     argc--, argv++;
 
     if (cmd == "init") {
         if (argc != 0)
-            throw string("init doesn't have arguments");
+            throw UsageError("wrong number of arguments");
         initDB();
     } else if (cmd == "getpkg") {
         if (argc != 1)
-            throw string("arguments missing in getpkg");
+            throw UsageError("wrong number of arguments");
         string path = getPkg(argv[0]);
         cout << path << endl;
-    } else if (cmd == "reg") {
+    } else if (cmd == "regfile") {
         if (argc != 1)
-            throw string("arguments missing in reg");
+            throw UsageError("wrong number of arguments");
         registerFile(argv[0]);
-    } else if (cmd == "regpkg") {
+    } else if (cmd == "reginst") {
         if (argc != 2)
-            throw string("arguments missing in regpkg");
+            throw UsageError("wrong number of arguments");
         registerInstalledPkg(argv[0], argv[1]);
     } else
-        throw string("unknown command: " + string(cmd));
+        throw UsageError("unknown command: " + string(cmd));
 }
-    
-    
-int main(int argc, char * * argv)
+
+
+void printUsage()
 {
-    int c;
+    cerr <<
+"Usage: nix SUBCOMMAND OPTIONS...
+
+Subcommands:
+
+  init
+    Initialize the database.
+
+  regfile FILENAME
+    Register FILENAME keyed by its hash.
+
+  reginst HASH PATH
+    Register an installed package.
+
+  getpkg HASH
+    Ensure that the package referenced by HASH is installed. Prints
+    out the path of the package on stdout.
+";
+}
 
-    prog = argv[0];
+
+void main2(int argc, char * * argv)
+{
+    int c;
 
     umask(0022);
 
-    try {
+    if (getenv(PKGINFO_ENVVAR))
+        dbfile = getenv(PKGINFO_ENVVAR);
+
+    if (getenv(PKGHOME_ENVVAR))
+        pkgHome = getenv(PKGHOME_ENVVAR);
 
-        while ((c = getopt(argc, argv, "d:")) != EOF) {
+    opterr = 0;
+
+    while ((c = getopt(argc, argv, "hd:")) != EOF) {
         
-            switch (c) {
+        switch (c) {
 
-            case 'd':
-                dbfile = optarg;
-                break;
+        case 'h':
+            printUsage();
+            return;
 
-            default:
-                throw string("unknown option");
-                break;
+        case 'd':
+            dbfile = optarg;
+            break;
 
-            }
+        default:
+            throw UsageError("invalid option `" + string(1, optopt) + "'");
+            break;
         }
+    }
 
-        argc -= optind, argv += optind;
-        run(argc, argv);
+    argc -= optind, argv += optind;
+    run(argc, argv);
+}
 
-    } catch (DbException e) {
-        cerr << "db exception: " << e.what() << endl;
-        return 1;
-    } catch (exception e) {
-        cerr << e.what() << endl;
+    
+int main(int argc, char * * argv)
+{
+    prog = argv[0];
+
+    try { 
+        try {
+
+            main2(argc, argv);
+            
+        } catch (DbException e) {
+            throw Error(e.what());
+        }
+        
+    } catch (UsageError & e) {
+        cerr << "error: " << e.what() << endl
+             << "Try `nix -h' for more information.\n";
         return 1;
-    } catch (string s) {
-        cerr << s << endl;
+    } catch (exception & e) {
+        cerr << "error: " << e.what() << endl;
         return 1;
     }