diff --git a/pkg/aterm-2.0-build.sh b/pkg/aterm-2.0-build.sh new file mode 100755 index 000000000..5d65b7878 --- /dev/null +++ b/pkg/aterm-2.0-build.sh @@ -0,0 +1,13 @@ +#! /bin/sh + +export PATH=$utils/bin +export LIBRARY_PATH=$glibc/lib +export CC=$gcc/bin/gcc +export CFLAGS="-isystem $glibc/include -isystem $kernel/include" + +top=`pwd` +tar xvfz $src +cd aterm-2.0 +./configure --prefix=$top +make +make install diff --git a/pkg/aterm-2.0.nix b/pkg/aterm-2.0.nix new file mode 100644 index 000000000..2484001be --- /dev/null +++ b/pkg/aterm-2.0.nix @@ -0,0 +1,11 @@ +# Dependencies. +utils <- 5703121fe19cbeeaee7edd659cf4a25b # prog-bootstrap +gcc <- 02212b3dc4e50349376975367d433929 # gcc-bootstrap +glibc <- c0ce03ee0bab298babbe7e3b6159d36c # glibc-bootstrap +kernel <- 3dc8333a2c2b4d627b892755417acf89 # kernel-bootstrap + +# Original sources. +src = 853474e4bcf4a85f7d38a0676b36bded # aterm-2.0.tar.gz + +# Build script. +build = ee7ae4ade69f846d2385871bf532ef7e # aterm-2.0-build.sh 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 +#include #include #include +#include +#include +#include #include #include @@ -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; - - if (argc != 1) - throw string("arguments missing in get-pkg"); - - pkg = argv[0]; - - if (queryDB("pkginst", pkg, inst)) { - cout << inst << endl; - return; + 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; } - - cerr << "package " << pkg << " is not yet installed\n"; - - if (!queryDB("pkgsrc", pkg, src)) - throw string("source of package " + string(pkg) + " is not known"); - - inst = "/pkg/" + pkg; - - cmd = "rsync -a \"" + src + "\"/ \"" + inst + "\""; - - res = system(cmd.c_str()); - if (!WIFEXITED(res) || WEXITSTATUS(res) != 0) - throw string("unable to copy sources"); - - if (chdir(inst.c_str())) - throw string("unable to chdir to package directory"); - - /* Prepare for building. Clean the environment so that the - build process does not inherit things it shouldn't. */ - setenv("PATH", "/pkg/sys/bin", 1); - - res = system("./buildme"); - if (!WIFEXITED(res) || WEXITSTATUS(res) != 0) - throw string("unable to build package"); - - setDB("pkginst", pkg, inst); - - cout << inst << endl; } -void registerPkg(int argc, char * * argv) +/* Compute the MD5 hash of a file. */ +string makeRef(string filename) { - char * pkg; - char * src; + 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; +} + + +struct Dep +{ + string name; + string ref; + Dep(string _name, string _ref) + { + name = _name; + ref = _ref; + } +}; + +typedef list DepList; + + +void readPkgDescr(const string & pkgfile, + DepList & pkgImports, DepList & fileImports) +{ + ifstream file; + file.exceptions(ios::badbit); + file.open(pkgfile.c_str()); - if (argc != 2) - throw string("arguments missing in register-pkg"); + while (!file.eof()) { + string line; + getline(file, line); - pkg = argv[0]; - src = argv[1]; + int n = line.find('#'); + if (n >= 0) line = line.erase(n); - setDB("pkgsrc", pkg, src); + if ((int) line.find_first_not_of(" ") < 0) continue; + + 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 EnvPair; +typedef list 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; + + string file; + + if (!queryDB("refs", it->ref, file)) + throw string("unknown file " + it->ref); + + 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(dbInstPkgs, pkgref, path); +} + + +string getPkg(string pkgref) +{ + string path; + checkRef(pkgref); + while (!queryDB(dbInstPkgs, pkgref, path)) + installPkg(pkgref); + return path; +} + + +string absPath(string filename) +{ + if (filename[0] != '/') { + char buf[PATH_MAX]; + if (!getcwd(buf, sizeof(buf))) + throw string("cannot get cwd"); + filename = string(buf) + "/" + filename; + /* !!! canonicalise */ + } + return filename; +} + + +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) { - - case 'd': - dbfile = optarg; - break; - - default: - cerr << "unknown option\n"; - break; - } - - } - - argc -= optind, argv += optind; + umask(0022); try { + + while ((c = getopt(argc, argv, "d:")) != EOF) { + + switch (c) { + + case 'd': + dbfile = optarg; + break; + + default: + throw string("unknown option"); + break; + + } + } + + 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; } diff --git a/sys/bootstrap b/sys/bootstrap index e2265c1dc..f6aa16c86 100755 --- a/sys/bootstrap +++ b/sys/bootstrap @@ -26,7 +26,7 @@ mkdir -m 1777 $target/tmp pkgdb=$target/pkg/sys/var/pkginfo # Copy some programs and its libraries. -utils="/usr/bin/vi /bin/sh /bin/mount /bin/umount /bin/ls /bin/ln /bin/cp /bin/mv /bin/rm /bin/cat /bin/df /bin/pwd /usr/bin/ld /usr/bin/as /bin/sed /bin/chmod /bin/chown /usr/bin/expr /bin/mkdir /bin/rmdir /usr/bin/sort /usr/bin/uniq /bin/uname /usr/bin/grep /bin/sleep /usr/bin/rsync /usr/bin/make /usr/bin/cmp /bin/date /usr/bin/tr /usr/bin/ar /usr/bin/ranlib /usr/bin/basename /usr/bin/less ../src/nix" +utils="/usr/bin/vi /bin/sh /bin/mount /bin/umount /bin/ls /bin/ln /bin/cp /bin/mv /bin/rm /bin/cat /bin/df /bin/pwd /usr/bin/ld /usr/bin/as /bin/sed /bin/chmod /bin/chown /usr/bin/expr /bin/mkdir /bin/rmdir /usr/bin/sort /usr/bin/uniq /bin/uname /usr/bin/grep /bin/sleep /bin/gzip /usr/bin/make /usr/bin/cmp /bin/date /usr/bin/tr /usr/bin/ar /usr/bin/ranlib /usr/bin/basename /usr/bin/less /usr/bin/md5sum /bin/tar ../src/nix" bootlib=/pkg/prog-bootstrap/lib bootbin=/pkg/prog-bootstrap/bin mkdir -p $target/$bootlib @@ -36,27 +36,27 @@ libs=`ldd $utils | awk '{ print $3 }' | sort | uniq` echo $libs cp -p $libs $target/$bootlib for i in libc.so.6 libdl.so.2 libpthread.so.0 librt.so.1 libresolv.so.2 ld-linux.so.2; do rm $target/$bootlib/$i; done -../src/nix -d $pkgdb register-installed-pkg prog-bootstrap /pkg/prog-bootstrap +../src/nix -d $pkgdb regpkg 5703121fe19cbeeaee7edd659cf4a25b /pkg/prog-bootstrap mv $target/$bootbin/nix $target/pkg/sys/bin -../src/nix -d $pkgdb register-installed-pkg sys /pkg/sys +../src/nix -d $pkgdb regpkg 36bcbb801f5052739af8220c6ea51434 /pkg/sys # Copy the bootstrap gcc. echo Copying gcc... rsync -a ../bootstrap/gcc/inst/pkg $target -../src/nix -d $pkgdb register-installed-pkg gcc-bootstrap /pkg/gcc-bootstrap +../src/nix -d $pkgdb regpkg 02212b3dc4e50349376975367d433929 /pkg/gcc-bootstrap # Copy the bootstrap glibc. echo Copying glibc... glibcdir=/pkg/glibc-bootstrap rsync -a ../bootstrap/glibc/inst/pkg $target -../src/nix -d $pkgdb register-installed-pkg glibc-bootstrap $glibcdir +../src/nix -d $pkgdb regpkg c0ce03ee0bab298babbe7e3b6159d36c $glibcdir # Copy the bootstrap kernel header files. echo Copying kernel headers... kerneldir=/pkg/kernel-bootstrap rsync -a ../bootstrap/kernel/inst/pkg $target -../src/nix -d $pkgdb register-installed-pkg kernel-bootstrap $kerneldir +../src/nix -d $pkgdb regpkg 3dc8333a2c2b4d627b892755417acf89 $kerneldir # Compatibility. rm -rf $target/lib @@ -75,7 +75,7 @@ $target/$glibcdir/sbin/ldconfig -r $target # Source repository. rm -f $target/src -ln -sf /mnt/host/`pwd`/../src $target/src +ln -sf /mnt/host/`pwd`/../pkg $target/src # Copy boot script. cp -p ./start $target/pkg/sys/bin diff --git a/sys/start b/sys/start index f822b2fa7..b36cde297 100755 --- a/sys/start +++ b/sys/start @@ -4,7 +4,7 @@ export PATH=/pkg/sys/bin # Add in the utilities needed for booting. -export PATH=$PATH:`nix get-pkg prog-bootstrap`/bin +export PATH=$PATH:`nix getpkg 5703121fe19cbeeaee7edd659cf4a25b`/bin echo echo Starting up... @@ -14,17 +14,15 @@ mount -n -o remount,rw /dev/root / mount -n -t proc none /proc mount -n -t hostfs none /mnt/host -echo Registering available src packages... -( cd /src - for i in *; do - if test -d $i; then - echo " $i" - nix register-pkg $i /src/$i - fi - done +echo Registering available sources... +( if cd /src; then + for i in *; do + nix reg $i + done + fi ) -export PATH=`nix get-pkg coreutils-4.5.7`/bin:$PATH +export PATH=`nix getpkg coreutils-4.5.7`/bin:$PATH echo echo "=== starting interactive shell ==="