2003-03-13 17:28:32 +01:00
|
|
|
#include <iostream>
|
2003-03-14 17:43:14 +01:00
|
|
|
#include <fstream>
|
2003-03-13 17:28:32 +01:00
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
2003-03-14 17:43:14 +01:00
|
|
|
#include <sstream>
|
|
|
|
#include <list>
|
|
|
|
#include <cstdio>
|
2003-03-13 17:28:32 +01:00
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
#include <db4/db_cxx.h>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
#define PKGINFO_PATH "/pkg/sys/var/pkginfo"
|
|
|
|
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
static string dbRefs = "refs";
|
|
|
|
static string dbInstPkgs = "pkginst";
|
|
|
|
|
|
|
|
|
2003-03-13 17:28:32 +01:00
|
|
|
static string prog;
|
|
|
|
static string dbfile = PKGINFO_PATH;
|
|
|
|
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
/* Wrapper class that ensures that the database is closed upon
|
|
|
|
object destruction. */
|
2003-03-13 17:28:32 +01:00
|
|
|
class Db2 : public Db
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Db2(DbEnv *env, u_int32_t flags)
|
|
|
|
: Db(env, flags)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~Db2()
|
|
|
|
{
|
|
|
|
close(0);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
auto_ptr<Db2> openDB(const string & dbname, bool readonly)
|
|
|
|
{
|
|
|
|
auto_ptr<Db2> db;
|
|
|
|
|
|
|
|
db = auto_ptr<Db2>(new Db2(0, 0));
|
|
|
|
|
|
|
|
db->open(dbfile.c_str(), dbname.c_str(),
|
|
|
|
DB_HASH, readonly ? DB_RDONLY : DB_CREATE, 0666);
|
|
|
|
|
|
|
|
return db;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool queryDB(const string & dbname, const string & key, string & data)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
auto_ptr<Db2> db = openDB(dbname, true);
|
|
|
|
|
|
|
|
Dbt kt((void *) key.c_str(), key.length());
|
|
|
|
Dbt dt;
|
|
|
|
|
|
|
|
err = db->get(0, &kt, &dt, 0);
|
|
|
|
if (err) return false;
|
|
|
|
|
|
|
|
data = string((char *) dt.get_data(), dt.get_size());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void setDB(const string & dbname, const string & key, const string & data)
|
|
|
|
{
|
|
|
|
auto_ptr<Db2> db = openDB(dbname, false);
|
|
|
|
Dbt kt((void *) key.c_str(), key.length());
|
|
|
|
Dbt dt((void *) data.c_str(), data.length());
|
|
|
|
db->put(0, &kt, &dt, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void delDB(const string & dbname, const string & key)
|
|
|
|
{
|
|
|
|
auto_ptr<Db2> db = openDB(dbname, false);
|
|
|
|
Dbt kt((void *) key.c_str(), key.length());
|
|
|
|
db->del(0, &kt, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
/* Verify that a reference is valid (that is, is a MD5 hash code). */
|
|
|
|
void checkRef(const string & s)
|
2003-03-13 17:28:32 +01:00
|
|
|
{
|
2003-03-14 17:43:14 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2003-03-13 17:28:32 +01:00
|
|
|
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
|
|
|
|
struct Dep
|
|
|
|
{
|
|
|
|
string name;
|
|
|
|
string ref;
|
|
|
|
Dep(string _name, string _ref)
|
|
|
|
{
|
|
|
|
name = _name;
|
|
|
|
ref = _ref;
|
2003-03-13 17:28:32 +01:00
|
|
|
}
|
2003-03-14 17:43:14 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef list<Dep> DepList;
|
|
|
|
|
|
|
|
|
|
|
|
void readPkgDescr(const string & pkgfile,
|
|
|
|
DepList & pkgImports, DepList & fileImports)
|
|
|
|
{
|
|
|
|
ifstream file;
|
|
|
|
file.exceptions(ios::badbit);
|
|
|
|
file.open(pkgfile.c_str());
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
while (!file.eof()) {
|
|
|
|
string line;
|
|
|
|
getline(file, line);
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
int n = line.find('#');
|
|
|
|
if (n >= 0) line = line.erase(n);
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
if ((int) line.find_first_not_of(" ") < 0) continue;
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
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;
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
string file;
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
if (!queryDB("refs", it->ref, file))
|
|
|
|
throw string("unknown file " + it->ref);
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
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)
|
2003-03-13 17:28:32 +01:00
|
|
|
throw string("unable to build package");
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
setDB(dbInstPkgs, pkgref, path);
|
|
|
|
}
|
|
|
|
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
string getPkg(string pkgref)
|
|
|
|
{
|
|
|
|
string path;
|
|
|
|
checkRef(pkgref);
|
|
|
|
while (!queryDB(dbInstPkgs, pkgref, path))
|
|
|
|
installPkg(pkgref);
|
|
|
|
return path;
|
2003-03-13 17:28:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
string absPath(string filename)
|
2003-03-13 17:28:32 +01:00
|
|
|
{
|
2003-03-14 17:43:14 +01:00
|
|
|
if (filename[0] != '/') {
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
if (!getcwd(buf, sizeof(buf)))
|
|
|
|
throw string("cannot get cwd");
|
|
|
|
filename = string(buf) + "/" + filename;
|
|
|
|
/* !!! canonicalise */
|
|
|
|
}
|
|
|
|
return filename;
|
|
|
|
}
|
2003-03-13 17:28:32 +01:00
|
|
|
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
void registerFile(string filename)
|
|
|
|
{
|
|
|
|
filename = absPath(filename);
|
|
|
|
setDB(dbRefs, makeRef(filename), filename);
|
2003-03-13 17:28:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This is primarily used for bootstrapping. */
|
2003-03-14 17:43:14 +01:00
|
|
|
void registerInstalledPkg(string pkgref, string path)
|
2003-03-13 17:28:32 +01:00
|
|
|
{
|
2003-03-14 17:43:14 +01:00
|
|
|
checkRef(pkgref);
|
|
|
|
if (path == "")
|
|
|
|
delDB(dbInstPkgs, pkgref);
|
2003-03-13 17:28:32 +01:00
|
|
|
else
|
2003-03-14 17:43:14 +01:00
|
|
|
setDB(dbInstPkgs, pkgref, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void initDB()
|
|
|
|
{
|
|
|
|
openDB(dbRefs, false);
|
|
|
|
openDB(dbInstPkgs, false);
|
2003-03-13 17:28:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void run(int argc, char * * argv)
|
|
|
|
{
|
|
|
|
string cmd;
|
|
|
|
|
|
|
|
if (argc < 1)
|
2003-03-14 17:43:14 +01:00
|
|
|
throw string("command not specified");
|
2003-03-13 17:28:32 +01:00
|
|
|
|
|
|
|
cmd = argv[0];
|
|
|
|
argc--, argv++;
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
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
|
2003-03-13 17:28:32 +01:00
|
|
|
throw string("unknown command: " + string(cmd));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char * * argv)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
|
|
|
|
prog = argv[0];
|
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
umask(0022);
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
try {
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
while ((c = getopt(argc, argv, "d:")) != EOF) {
|
|
|
|
|
|
|
|
switch (c) {
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
case 'd':
|
|
|
|
dbfile = optarg;
|
|
|
|
break;
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
default:
|
|
|
|
throw string("unknown option");
|
|
|
|
break;
|
2003-03-13 17:28:32 +01:00
|
|
|
|
2003-03-14 17:43:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argc -= optind, argv += optind;
|
2003-03-13 17:28:32 +01:00
|
|
|
run(argc, argv);
|
2003-03-14 17:43:14 +01:00
|
|
|
|
2003-03-13 17:28:32 +01:00
|
|
|
} catch (DbException e) {
|
|
|
|
cerr << "db exception: " << e.what() << endl;
|
|
|
|
return 1;
|
2003-03-14 17:43:14 +01:00
|
|
|
} catch (exception e) {
|
|
|
|
cerr << e.what() << endl;
|
|
|
|
return 1;
|
2003-03-13 17:28:32 +01:00
|
|
|
} catch (string s) {
|
|
|
|
cerr << s << endl;
|
|
|
|
return 1;
|
|
|
|
}
|
2003-03-14 17:43:14 +01:00
|
|
|
|
|
|
|
return 0;
|
2003-03-13 17:28:32 +01:00
|
|
|
}
|