diff --git a/pkgs/additional/bunpen/config/translate_opts.ha b/pkgs/additional/bunpen/config/translate_opts.ha index 1a4f69537..f0823bd43 100644 --- a/pkgs/additional/bunpen/config/translate_opts.ha +++ b/pkgs/additional/bunpen/config/translate_opts.ha @@ -81,8 +81,8 @@ fn ingest_paths(into: *[]path::buffer, from: []str, base: (str | void), allow_ab match (get_path(path_str, base, allow_abs)) { case let p: path::buffer => append(into, p); case let e: path::error => - log::printfln("translate_opts: omitting path {}: {}", path_str, path::strerror(e)); - case missing_base => log::printfln("translate_opts: omitting path {}: no base dir", path_str); + log::printfln("[translate_opts] omitting path {}: {}", path_str, path::strerror(e)); + case missing_base => log::printfln("[translate_opts] omitting path {}: no base dir", path_str); }; }; }; diff --git a/pkgs/additional/bunpen/log/tree/treelogger.ha b/pkgs/additional/bunpen/log/tree/treelogger.ha index 1a3ebe276..1740c595b 100644 --- a/pkgs/additional/bunpen/log/tree/treelogger.ha +++ b/pkgs/additional/bunpen/log/tree/treelogger.ha @@ -10,11 +10,10 @@ // - `printfln("[module] says {}", ...)`: has depth=1 // - `printfln("[module/child] says {}", ...)`: has depth=2 // - `printfln("[module/child/grandchild] says {}", ...)`: has depth=3 -// -// TODO: how are `println`'s handled? use fmt; use log; +use strings; export type logger = struct { // vtable (println, printfln) @@ -51,15 +50,14 @@ export fn set_level(me: *logger, level: uint) void = { fn log_println(base: *log::logger, fields: fmt::formattable...) void = { let me = base: *logger; - if (me.level > 0) { + if (me.level > 0 && (len(fields) == 0 || get_depth(fields[0]) < me.level)) { log::lprintln(get_forward(me), fields...); }; }; fn log_printfln(base: *log::logger, fmt: str, fields: fmt::field...) void = { let me = base: *logger; - if (me.level > 0) { + if (me.level != 0 && get_depth(fmt) < me.level) log::lprintfln(get_forward(me), fmt, fields...); - }; }; fn get_forward(me: *logger) *log::logger = { @@ -68,3 +66,23 @@ fn get_forward(me: *logger) *log::logger = { case let l: *log::logger => yield l; }; }; + +fn get_depth(label: fmt::formattable) uint = { + let label = match (label) { + case let l: str => yield l; + case => return 0; // nothing other than a string could reasonable hold a label + }; + if (!strings::hasprefix(label, '[')) + return 0; // no context + + let depth: uint = 1; + let iter = strings::iter(label); + for (let c => strings::next(&iter)) { + if (c == ']') + break; + if (c == '/') + depth += 1; + }; + + return depth; +}; diff --git a/pkgs/additional/bunpen/main.ha b/pkgs/additional/bunpen/main.ha index 10b1d9850..134112fe8 100644 --- a/pkgs/additional/bunpen/main.ha +++ b/pkgs/additional/bunpen/main.ha @@ -36,7 +36,7 @@ export fn main() void = { log::tree::install(tree::global); if (req.debug) { - log::tree::set_level(tree::global, 1); + log::tree::set_level(tree::global, 2); // TODO: make more configurable }; let what = restrict::resources { diff --git a/pkgs/additional/bunpen/restrict/landlock.ha b/pkgs/additional/bunpen/restrict/landlock.ha index 8bfda58c5..6b6326e33 100644 --- a/pkgs/additional/bunpen/restrict/landlock.ha +++ b/pkgs/additional/bunpen/restrict/landlock.ha @@ -53,7 +53,7 @@ fn allow_path_fd(ruleset_fd: u64, path_fd: i32) (rt::errno | void) = { export fn landlock_restrict(what: *resources) void = { let abi = rtext::landlock_create_ruleset(null, rtext::LANDLOCK_CREATE_RULESET_VERSION)!; - log::printfln("landlock: found version {}", abi); + log::printfln("[landlock] found version {}", abi); // determine the access modes we can ask this kernel to restrict on: let ruleset_attr = rtext::landlock_ruleset_attr { @@ -75,7 +75,7 @@ export fn landlock_restrict(what: *resources) void = { if (what.net) { // un-restrict net access - log::println("landlock: permit net"); + log::println("[landlock] permit net"); ruleset_attr.handled_access_net = 0; }; // XXX: `what.net` only affects TCP. UDP, and ICMP remain possible always @@ -83,10 +83,10 @@ export fn landlock_restrict(what: *resources) void = { for (let pathbuf .. what.paths) { let pathstr = path::string(&pathbuf); - log::printfln("landlock: permit path: {}", pathstr); + log::printfln("[landlock] permit path: {}", pathstr); match (rt::open(pathstr, rt::O_PATH | rt::O_CLOEXEC, 0)) { //< O_PATH allows for opening files which are `x` but not `r` - case rt::errno => log::printfln("landlock: omitting from sandbox (failed to `open`): {}", pathstr); - case let path_fd: int => rtext::swallow_error("landlock: allow_path_fd", allow_path_fd(ruleset_fd, path_fd)); + case rt::errno => log::printfln("[landlock/path] omitting from sandbox (failed to `open`): {}", pathstr); + case let path_fd: int => rtext::swallow_error("[landlock/path] allow_path_fd", allow_path_fd(ruleset_fd, path_fd)); }; }; diff --git a/pkgs/additional/bunpen/restrict/namespace.ha b/pkgs/additional/bunpen/restrict/namespace.ha index 383646a92..64cdf8af4 100644 --- a/pkgs/additional/bunpen/restrict/namespace.ha +++ b/pkgs/additional/bunpen/restrict/namespace.ha @@ -26,11 +26,11 @@ export fn namespace_restrict(what: *resources) void = { rtext::CLONE_NEWUTS ; if (what.net) { - log::println("namespace: permit net"); + log::println("[namespace] permit net"); what_to_unshare &= ~rtext::CLONE_NEWNET; }; - log::printfln("namespace: unshare {}", what_to_unshare); + log::printfln("[namespace] unshare {}", what_to_unshare); rtext::unshare(what_to_unshare)!; // before mounting anything, set up the uids and gids in this namespace. @@ -45,7 +45,7 @@ export fn namespace_restrict(what: *resources) void = { isolate_paths(what.paths); // try to change to the old working directory; // this can fail if it's not within the sandbox. - rtext::swallow_error("namespace: restore $PWD", os::chdir(pwd)); + rtext::swallow_error("[namespace] restore $PWD", os::chdir(pwd)); // TODO: CLONE_NEWPID (might not work without forking to also become reaper) }; @@ -56,7 +56,7 @@ export fn namespace_restrict(what: *resources) void = { fn isolate_paths(paths: []path::buffer) void = { // allow new mounts to propagate from the parent namespace into the child // namespace, but not vice versa: - rtext::check_error("reconfigure / as MS_SLAVE", rtext::mount("/", "/", "", rtext::MS_SLAVE | rtext::MS_REC, null)); + rtext::check_error("[namespace] reconfigure / as MS_SLAVE", rtext::mount("/", "/", "", rtext::MS_SLAVE | rtext::MS_REC, null)); // in order to mount ANY directory from the old root into the new root, // they have to be totally disparate. if we kept the old root at / and the new @@ -67,14 +67,14 @@ fn isolate_paths(paths: []path::buffer) void = { // 2. create a new rootfs at `new` and bind stuff into it. // 3. then pivot a 2nd time, into `new` (and drop `old` altogether) - rtext::check_error("mount -t tmpfs tmpfs /tmp", rtext::mount("tmpfs", "/tmp", "tmpfs", 0, null)); + rtext::check_error("[namespace] mount -t tmpfs tmpfs /tmp", rtext::mount("tmpfs", "/tmp", "tmpfs", 0, null)); pivot_into("/tmp", "old"); // now we have `/`, empty except for the old rootfs available at `/old` // prepare a new rootfs. it has to be its own mount (tmpfs), not just a dir. - rtext::check_error("mkdir new", rt::mkdir("new", 0o755)); - rtext::check_error("mount -t tmpfs tmpfs new", rtext::mount("tmpfs", "new", "tmpfs", 0, null)); + rtext::check_error("[namespace] mkdir new", rt::mkdir("new", 0o755)); + rtext::check_error("[namespace] mount -t tmpfs tmpfs new", rtext::mount("tmpfs", "new", "tmpfs", 0, null)); // bind all the user-requested paths from `old/$p` into `new/$p`. // use the `dirfd` abstraction so that paths meant for `old` can't crawl out @@ -92,6 +92,8 @@ fn isolate_paths(paths: []path::buffer) void = { // pivot into the new rootfs pivot_into("new"); + + log::println("namespace restrictions activated"); }; // walk from root to `p`, creating any ancestors necessary and then binding the @@ -117,14 +119,14 @@ fn isolate_paths(paths: []path::buffer) void = { // does not create the leaf *nor any of its ancestors* at/after the `..`. fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) void = { let path_str = path::string(user_path); - log::printfln("namespace: permit path: {}", path_str); + log::printfln("[namespace] permit path: {}", path_str); let it = path::iter(user_path); let cur_path = path::init()!; let cur_strpath = ""; for (let comp => path::nextiter(&it)) { if (comp == "..") { - log::printfln("not binding external path {} (of {})", cur_strpath, path_str); + log::printfln("[namespace] not binding external path {} (of {})", cur_strpath, path_str); return; }; if (len(comp) >= 1 && strings::sub(comp, 0, 1) == "/") { @@ -133,7 +135,7 @@ fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) void = }; cur_strpath = match (path::push(&cur_path, comp)) { case let e: path::error => - log::printfln("unable to construct intermediate path for binding {} / {}: {}", cur_strpath, comp, path::strerror(e)); + log::printfln("[namespace] unable to construct intermediate path for binding {} / {}: {}", cur_strpath, comp, path::strerror(e)); return; case let other: str => yield other; @@ -141,7 +143,7 @@ fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) void = match (bind_component(old_fs, new_fs, cur_strpath)) { case let e: fs::error => - log::printfln("unable to copy intermediate path {} of {}: {}", cur_strpath, path_str, fs::strerror(e)); + log::printfln("[namespace] unable to copy intermediate path {} of {}: {}", cur_strpath, path_str, fs::strerror(e)); return; case void => void; }; @@ -150,20 +152,20 @@ fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) void = // and now, perform the actual bind: let old_pathbuf = match (path::init("old", path_str)) { case let e: path::error => - log::printfln("unable to construct old path for binding {}: {}", path_str, path::strerror(e)); + log::printfln("[namespace] unable to construct old path for binding {}: {}", path_str, path::strerror(e)); return; case let other: path::buffer => yield other; }; let new_pathbuf = match (path::init("new", path_str)) { case let e: path::error => - log::printfln("unable to construct new path for binding {}: {}", path_str, path::strerror(e)); + log::printfln("[namespace] unable to construct new path for binding {}: {}", path_str, path::strerror(e)); return; case let other: path::buffer => yield other; }; - log::printfln("namespace: mount {} {}", path::string(&old_pathbuf), path::string(&new_pathbuf)); - rtext::swallow_error("namespace: bind_leaf: mount", rtext::mount( + log::printfln("[namespace/bind] mount {} {}", path::string(&old_pathbuf), path::string(&new_pathbuf)); + rtext::swallow_error("[namespace/bind] bind_leaf: mount", rtext::mount( path::string(&old_pathbuf), path::string(&new_pathbuf), "", rtext::MS_BIND | rtext::MS_REC, null )); }; @@ -180,15 +182,15 @@ fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str) (void | fs::er if (fs::islink(st.mode)) { let linktext = fs::readlink(old_fs, strpath)?; - log::printfln("namespace: ln new/{} -> {}", strpath, linktext); + log::printfln("[namespace/bind] ln new/{} -> {}", strpath, linktext); fs::symlink(new_fs, linktext, strpath)?; // TODO: lots of edge cases with symlinks i need to flesh out here. } else if (fs::isdir(st.mode)) { - log::printfln("namespace: mkdir new/{}", strpath); + log::printfln("[namespace/bind] mkdir new/{}", strpath); fs::mkdir(new_fs, strpath, st.mode)?; } else { // TODO: tune options (optional parameter; default is fs::flag::TRUNC) - log::printfln("namespace: creat new/{}", strpath); + log::printfln("[namespace/bind] creat new/{}", strpath); fs::create(new_fs, strpath, st.mode)?; }; }; @@ -196,18 +198,18 @@ fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str) (void | fs::er // make `new_root` the new `/`, and optionally make the old root accessible // at some directory (to be created) underneath it. fn pivot_into(new_root: str, stash_old_root: (str|void) = void) void = { - log::printfln("namespace: pivot_root {}", new_root); - rtext::check_error("cd ", os::chdir(new_root)); + log::printfln("[namespace] pivot_root {}", new_root); + rtext::check_error("[namespace] cd ", os::chdir(new_root)); match (stash_old_root) { case let old: str => - rtext::check_error("mkdir ", rt::mkdir(old, 0o755)); - rtext::check_error("pivot_root . ", rtext::pivot_root(".", old)); + rtext::check_error("[namespace] mkdir ", rt::mkdir(old, 0o755)); + rtext::check_error("[namespace] pivot_root . ", rtext::pivot_root(".", old)); case void => - rtext::check_error("pivot_root . .", rtext::pivot_root(".", ".")); + rtext::check_error("[namespace] pivot_root . .", rtext::pivot_root(".", ".")); // drop the old rootfs. weird idiom, but documented in `man 2 pivot_root`. - rtext::check_error("umount .", rt::umount2(".", rtext::MNT_DETACH)); + rtext::check_error("[namespace] umount .", rt::umount2(".", rtext::MNT_DETACH)); }; - rtext::check_error("cd /", os::chdir("/")); + rtext::check_error("[namespace] cd /", os::chdir("/")); }; fn write_uid_map(uid: unix::uid, gid: unix::gid) void = { diff --git a/pkgs/additional/bunpen/rtext/error.ha b/pkgs/additional/bunpen/rtext/error.ha index 0dc6846ef..2357e504c 100644 --- a/pkgs/additional/bunpen/rtext/error.ha +++ b/pkgs/additional/bunpen/rtext/error.ha @@ -15,9 +15,11 @@ export fn check_error(context: str, what: (void | fs::error | os::exec::error | export fn swallow_error(context: str, what: (void | fs::error | os::exec::error | rt::errno)) void = { match (what) { - case let e: fs::error => log::printfln("{}: {}", context, fs::strerror(e)); - case let e: os::exec::error => log::printfln("{}: {}", context, os::exec::strerror(e)); - case let e: rt::errno => log::printfln("{}: {}: {}", context, rt::errname(e), rt::strerror(e)); + // N.B.: use `println(...)` over `printfln("{}: {}")` so that my treelogger + // can extract the context. + case let e: fs::error => log::println(context, ": ", fs::strerror(e)); + case let e: os::exec::error => log::println(context, ": ", os::exec::strerror(e)); + case let e: rt::errno => log::println(context, ": ", rt::errname(e), ": ", rt::strerror(e)); case => void; }; };