aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nix.cc341
1 files changed, 267 insertions, 74 deletions
diff --git a/src/nix.cc b/src/nix.cc
index 0c7865270..8108c2fca 100644
--- a/src/nix.cc
+++ b/src/nix.cc
@@ -1,6 +1,10 @@
#include <iostream>
+#include <fstream>
#include <memory>
#include <string>
+#include <sstream>
+#include <list>
+#include <cstdio>
#include <unistd.h>
#include <sys/stat.h>
@@ -15,10 +19,16 @@ using namespace std;
#define PKGINFO_PATH "/pkg/sys/var/pkginfo"
+static string dbRefs = "refs";
+static string dbInstPkgs = "pkginst";
+
+
static string prog;
static string dbfile = PKGINFO_PATH;
+/* Wrapper class that ensures that the database is closed upon
+ object destruction. */
class Db2 : public Db
{
public:
@@ -81,85 +91,250 @@ void delDB(const string & dbname, const string & key)
}
-void getPkg(int argc, char * * argv)
+/* Verify that a reference is valid (that is, is a MD5 hash code). */
+void checkRef(const string & s)
{
- string pkg;
- string src;
- string inst;
- string cmd;
- int res;
+ string err = "invalid reference: " + s;
+ if (s.length() != 32)
+ throw err;
+ for (int i = 0; i < 32; i++) {
+ char c = s[i];
+ if (!((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f')))
+ throw err;
+ }
+}
- if (argc != 1)
- throw string("arguments missing in get-pkg");
- pkg = argv[0];
+/* Compute the MD5 hash of a file. */
+string makeRef(string filename)
+{
+ char hash[33];
+
+ FILE * pipe = popen(("md5sum " + filename).c_str(), "r");
+ if (!pipe) throw string("cannot execute md5sum");
+
+ if (fread(hash, 32, 1, pipe) != 1)
+ throw string("cannot read hash from md5sum");
+ hash[32] = 0;
+
+ pclose(pipe);
+
+ checkRef(hash);
+ return hash;
+}
- if (queryDB("pkginst", pkg, inst)) {
- cout << inst << endl;
- return;
+
+struct Dep
+{
+ string name;
+ string ref;
+ Dep(string _name, string _ref)
+ {
+ name = _name;
+ ref = _ref;
}
+};
+
+typedef list<Dep> DepList;
+
+
+void readPkgDescr(const string & pkgfile,
+ DepList & pkgImports, DepList & fileImports)
+{
+ ifstream file;
+ file.exceptions(ios::badbit);
+ file.open(pkgfile.c_str());
- cerr << "package " << pkg << " is not yet installed\n";
-
- if (!queryDB("pkgsrc", pkg, src))
- throw string("source of package " + string(pkg) + " is not known");
+ while (!file.eof()) {
+ string line;
+ getline(file, line);
- inst = "/pkg/" + pkg;
+ int n = line.find('#');
+ if (n >= 0) line = line.erase(n);
- cmd = "rsync -a \"" + src + "\"/ \"" + inst + "\"";
+ if ((int) line.find_first_not_of(" ") < 0) continue;
- res = system(cmd.c_str());
- if (!WIFEXITED(res) || WEXITSTATUS(res) != 0)
- throw string("unable to copy sources");
+ istringstream str(line);
+
+ string name, op, ref;
+ str >> name >> op >> ref;
+
+ checkRef(ref);
+
+ if (op == "<-")
+ pkgImports.push_back(Dep(name, ref));
+ else if (op == "=")
+ fileImports.push_back(Dep(name, ref));
+ else throw string("invalid operator " + op);
+ }
+}
+
+
+string getPkg(string pkgref);
+
+
+typedef pair<string, string> EnvPair;
+typedef list<EnvPair> Environment;
+
+
+void installPkg(string pkgref)
+{
+ string pkgfile;
+ string src;
+ string path;
+ string cmd;
+ string builder;
+
+ if (!queryDB("refs", pkgref, pkgfile))
+ throw string("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");
+
+ /* Read the package description file. */
+ DepList pkgImports, fileImports;
+ readPkgDescr(pkgfile, pkgImports, fileImports);
+
+ /* Recursively fetch all the dependencies, filling in the
+ environment as we go along. */
+ Environment env;
+
+ for (DepList::iterator it = pkgImports.begin();
+ it != pkgImports.end(); it++)
+ {
+ cerr << "fetching package dependency "
+ << it->name << " <- " << it->ref
+ << endl;
+ env.push_back(EnvPair(it->name, getPkg(it->ref)));
+ }
+
+ for (DepList::iterator it = fileImports.begin();
+ it != fileImports.end(); it++)
+ {
+ cerr << "fetching file dependency "
+ << it->name << " = " << it->ref
+ << endl;
- if (chdir(inst.c_str()))
- throw string("unable to chdir to package directory");
+ string file;
- /* Prepare for building. Clean the environment so that the
- build process does not inherit things it shouldn't. */
- setenv("PATH", "/pkg/sys/bin", 1);
+ if (!queryDB("refs", it->ref, file))
+ throw string("unknown file " + it->ref);
- res = system("./buildme");
- if (!WIFEXITED(res) || WEXITSTATUS(res) != 0)
+ if (makeRef(file) != it->ref)
+ throw string("file " + file + " is stale");
+
+ if (it->name == "build")
+ builder = file;
+ else
+ env.push_back(EnvPair(it->name, file));
+ }
+
+ if (builder == "")
+ throw string("no builder specified");
+
+ /* Construct a path for the installed package. */
+ path = "/pkg/" + pkgref;
+
+ /* Create the path. */
+ if (mkdir(path.c_str(), 0777))
+ throw string("unable to create directory " + path);
+
+ /* Fork a child to build the package. */
+ pid_t pid;
+ switch (pid = fork()) {
+
+ case -1:
+ throw string("unable to fork");
+
+ case 0: /* child */
+
+ /* 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;
+
+ /* Execute the builder. This should not return. */
+ execle(builder.c_str(), builder.c_str(), 0, env2);
+
+ cout << strerror(errno) << endl;
+
+ cout << "unable to execute builder\n";
+ _exit(1);
+ }
+
+ /* parent */
+
+ /* Wait for the child to finish. */
+ int status;
+ if (waitpid(pid, &status, 0) != pid)
+ throw string("unable to wait for child");
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
throw string("unable to build package");
- setDB("pkginst", pkg, inst);
+ setDB(dbInstPkgs, pkgref, path);
+}
+
- cout << inst << endl;
+string getPkg(string pkgref)
+{
+ string path;
+ checkRef(pkgref);
+ while (!queryDB(dbInstPkgs, pkgref, path))
+ installPkg(pkgref);
+ return path;
}
-void registerPkg(int argc, char * * argv)
+string absPath(string filename)
{
- char * pkg;
- char * src;
-
- if (argc != 2)
- throw string("arguments missing in register-pkg");
+ if (filename[0] != '/') {
+ char buf[PATH_MAX];
+ if (!getcwd(buf, sizeof(buf)))
+ throw string("cannot get cwd");
+ filename = string(buf) + "/" + filename;
+ /* !!! canonicalise */
+ }
+ return filename;
+}
- pkg = argv[0];
- src = argv[1];
- setDB("pkgsrc", pkg, src);
+void registerFile(string filename)
+{
+ filename = absPath(filename);
+ setDB(dbRefs, makeRef(filename), filename);
}
/* This is primarily used for bootstrapping. */
-void registerInstalledPkg(int argc, char * * argv)
+void registerInstalledPkg(string pkgref, string path)
{
- string pkg;
- string inst;
-
- if (argc != 2)
- throw string("arguments missing in register-installed-pkg");
-
- pkg = argv[0];
- inst = argv[1];
-
- if (inst == "")
- delDB("pkginst", pkg);
+ checkRef(pkgref);
+ if (path == "")
+ delDB(dbInstPkgs, pkgref);
else
- setDB("pkginst", pkg, inst);
+ setDB(dbInstPkgs, pkgref, path);
+}
+
+
+void initDB()
+{
+ openDB(dbRefs, false);
+ openDB(dbInstPkgs, false);
}
@@ -168,18 +343,29 @@ void run(int argc, char * * argv)
string cmd;
if (argc < 1)
- throw string("command not specified\n");
+ throw string("command not specified");
cmd = argv[0];
argc--, argv++;
- if (cmd == "get-pkg")
- getPkg(argc, argv);
- else if (cmd == "register-pkg")
- registerPkg(argc, argv);
- else if (cmd == "register-installed-pkg")
- registerInstalledPkg(argc, argv);
- else
+ if (cmd == "init") {
+ if (argc != 0)
+ throw string("init doesn't have arguments");
+ initDB();
+ } else if (cmd == "getpkg") {
+ if (argc != 1)
+ throw string("arguments missing in getpkg");
+ string path = getPkg(argv[0]);
+ cout << path << endl;
+ } else if (cmd == "reg") {
+ if (argc != 1)
+ throw string("arguments missing in reg");
+ registerFile(argv[0]);
+ } else if (cmd == "regpkg") {
+ if (argc != 2)
+ throw string("arguments missing in regpkg");
+ registerInstalledPkg(argv[0], argv[1]);
+ } else
throw string("unknown command: " + string(cmd));
}
@@ -190,31 +376,38 @@ int main(int argc, char * * argv)
prog = argv[0];
- while ((c = getopt(argc, argv, "d:")) != EOF) {
-
- switch (c) {
+ umask(0022);
- case 'd':
- dbfile = optarg;
- break;
+ try {
- default:
- cerr << "unknown option\n";
- break;
- }
+ while ((c = getopt(argc, argv, "d:")) != EOF) {
+
+ switch (c) {
- }
+ case 'd':
+ dbfile = optarg;
+ break;
- argc -= optind, argv += optind;
+ default:
+ throw string("unknown option");
+ break;
- try {
+ }
+ }
+
+ argc -= optind, argv += optind;
run(argc, argv);
- return 0;
+
} catch (DbException e) {
cerr << "db exception: " << e.what() << endl;
return 1;
+ } catch (exception e) {
+ cerr << e.what() << endl;
+ return 1;
} catch (string s) {
cerr << s << endl;
return 1;
}
+
+ return 0;
}