/****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2025, Leafscale, LLC - https://www.leafscale.com Project: Zygaena Filename: main.reef Authors: Chris Tusa License: Description: mkchroot - Create chrooted test environment for Coral ******************************************************************************/ import sys.args import sys.process import io.file import io.dir import io.path import core.str // Default chroot base path const DEFAULT_CHROOT: string = "/tmp/coral-chroot" // Ports source location const PORTS_SOURCE: string = "/home/ctusa/repos/zygaena-ports" fn main(): int println("mkchroot - Coral Test Environment Builder") println("") // Parse command line for custom path and coral-bin option let chroot_path = get_chroot_path() let coral_bin = get_coral_bin() // Handle help if chroot_path == "--help" or chroot_path == "-h" print_usage() return 0 end if println("Creating chroot at: " + chroot_path) println("") // Check if chroot already exists if dir.dir_exists(chroot_path) println("WARNING: Chroot directory already exists") println("Removing existing chroot...") let rm_cmd = "rm -rf \"" + chroot_path + "\"" let rm_pid = process.process_spawn_shell(rm_cmd) if rm_pid > 0 process.process_wait(rm_pid) end if end if // Create base filesystem structure if not create_filesystem(chroot_path) println("ERROR: Failed to create filesystem structure") return 1 end if // Create Coral-specific directories if not create_coral_dirs(chroot_path) println("ERROR: Failed to create Coral directories") return 1 end if // Create default configuration if not create_config(chroot_path) println("ERROR: Failed to create configuration") return 1 end if // Copy ports tree if not copy_ports(chroot_path) println("ERROR: Failed to copy ports tree") return 1 end if // Copy coral binary if specified if str.length(coral_bin) > 0 if not install_coral(chroot_path, coral_bin) println("WARNING: Failed to copy coral binary") end if end if // Generate setup and teardown scripts if not create_setup_script(chroot_path) println("WARNING: Failed to create setup script") end if if not create_teardown_script(chroot_path) println("WARNING: Failed to create teardown script") end if println("") println("Chroot environment created successfully!") println("") println("=== Usage ===") println("") println("Option 1: Use --root flag (no mounts needed):") println(" coral --root " + chroot_path + " db init") println(" coral --root " + chroot_path + " install zlib") println(" coral --root " + chroot_path + " list") println("") println("Option 2: Enter chroot (requires setup):") println(" 1. Setup mounts: sudo " + chroot_path + "-setup.sh " + chroot_path) println(" 2. Enter chroot: sudo chroot " + chroot_path + " /bin/bash") println(" 3. Init database: coral db init") println(" 4. Build/install: coral build core/zlib") println(" 5. Exit: exit") println(" 6. Cleanup: sudo " + chroot_path + "-teardown.sh " + chroot_path) println("") println("=== Directory layout ===") println(" " + chroot_path + "/etc/coral/ - Configuration") println(" " + chroot_path + "/var/lib/coral/ - Package database") println(" " + chroot_path + "/usr/zports/ - Ports tree") println("") println("=== Generated scripts ===") println(" " + chroot_path + "-setup.sh - Mount /dev, /proc, /sys, and host binaries") println(" " + chroot_path + "-teardown.sh - Unmount all chroot mounts") println("") return 0 end main fn get_chroot_path(): string let argc = args.count() // Check for --path argument if argc >= 3 if args.get(1) == "--path" return args.get(2) end if end if // Check for positional argument (skip if it's a flag) if argc >= 2 let arg = args.get(1) if not str.starts_with(arg, "-") return arg end if end if // Check for positional after flags mut i = 1 while i < argc let arg = args.get(i) if arg == "--coral-bin" and i + 1 < argc i = i + 2 // Skip flag and value continue end if if not str.starts_with(arg, "-") return arg end if i = i + 1 end while return DEFAULT_CHROOT end get_chroot_path fn get_coral_bin(): string let argc = args.count() mut i = 1 while i < argc if args.get(i) == "--coral-bin" and i + 1 < argc return args.get(i + 1) end if i = i + 1 end while return "" end get_coral_bin fn install_coral(base: string, coral_path: string): bool println("Installing coral binary from " + coral_path + "...") // Check if source exists if not file.fileExists(coral_path) println(" ERROR: Coral binary not found at " + coral_path) return false end if let dst = base + "/usr/local/bin/coral" let cmd = "cp \"" + coral_path + "\" \"" + dst + "\" && chmod +x \"" + dst + "\"" let pid = process.process_spawn_shell(cmd) if pid > 0 let exit_code = process.process_wait(pid) if exit_code == 0 println(" Installed coral to " + dst) return true end if end if return false end install_coral fn create_setup_script(base: string): bool let script_path = base + "-setup.sh" println("Creating " + script_path + "...") let script_content = "#!/bin/sh\n" + "# Setup script for coral chroot environment\n" + "# Generated by mkchroot\n" + "\n" + "CHROOT=\"$1\"\n" + "\n" + "if [ -z \"$CHROOT\" ]; then\n" + " echo \"Usage: $0 \"\n" + " exit 1\n" + "fi\n" + "\n" + "if [ ! -d \"$CHROOT\" ]; then\n" + " echo \"Error: Chroot directory does not exist: $CHROOT\"\n" + " exit 1\n" + "fi\n" + "\n" + "echo \"Setting up mounts for chroot: $CHROOT\"\n" + "\n" + "# Mount essential filesystems\n" + "mount --bind /dev \"$CHROOT/dev\" 2>/dev/null && echo \" Mounted /dev\"\n" + "mount --bind /proc \"$CHROOT/proc\" 2>/dev/null && echo \" Mounted /proc\"\n" + "mount -t sysfs sysfs \"$CHROOT/sys\" 2>/dev/null && echo \" Mounted /sys\"\n" + "\n" + "# Mount host binaries and libraries for building\n" + "mount --bind /usr/bin \"$CHROOT/usr/bin\" 2>/dev/null && echo \" Mounted /usr/bin\"\n" + "mount --bind /usr/lib \"$CHROOT/usr/lib\" 2>/dev/null && echo \" Mounted /usr/lib\"\n" + "mount --bind /lib \"$CHROOT/lib\" 2>/dev/null && echo \" Mounted /lib\"\n" + "\n" + "# illumos-specific mounts\n" + "[ -d /lib/64 ] && mount --bind /lib/64 \"$CHROOT/lib/64\" 2>/dev/null && echo \" Mounted /lib/64\"\n" + "[ -d /usr/lib/64 ] && mount --bind /usr/lib/64 \"$CHROOT/usr/lib/64\" 2>/dev/null && echo \" Mounted /usr/lib/64\"\n" + "\n" + "echo \"\"\n" + "echo \"Chroot is ready. Enter with:\"\n" + "echo \" sudo chroot $CHROOT /bin/bash\"\n" if not file.writeFile(script_path, script_content) return false end if // Make executable let chmod_cmd = "chmod +x \"" + script_path + "\"" let pid = process.process_spawn_shell(chmod_cmd) if pid > 0 process.process_wait(pid) end if println(" Created " + script_path) return true end create_setup_script fn create_teardown_script(base: string): bool let script_path = base + "-teardown.sh" println("Creating " + script_path + "...") let script_content = "#!/bin/sh\n" + "# Teardown script for coral chroot environment\n" + "# Generated by mkchroot\n" + "\n" + "CHROOT=\"$1\"\n" + "\n" + "if [ -z \"$CHROOT\" ]; then\n" + " echo \"Usage: $0 \"\n" + " exit 1\n" + "fi\n" + "\n" + "echo \"Unmounting chroot: $CHROOT\"\n" + "\n" + "# Unmount in reverse order\n" + "umount \"$CHROOT/usr/lib/64\" 2>/dev/null && echo \" Unmounted /usr/lib/64\"\n" + "umount \"$CHROOT/lib/64\" 2>/dev/null && echo \" Unmounted /lib/64\"\n" + "umount \"$CHROOT/lib\" 2>/dev/null && echo \" Unmounted /lib\"\n" + "umount \"$CHROOT/usr/lib\" 2>/dev/null && echo \" Unmounted /usr/lib\"\n" + "umount \"$CHROOT/usr/bin\" 2>/dev/null && echo \" Unmounted /usr/bin\"\n" + "umount \"$CHROOT/sys\" 2>/dev/null && echo \" Unmounted /sys\"\n" + "umount \"$CHROOT/proc\" 2>/dev/null && echo \" Unmounted /proc\"\n" + "umount \"$CHROOT/dev\" 2>/dev/null && echo \" Unmounted /dev\"\n" + "\n" + "echo \"\"\n" + "echo \"Chroot cleanup complete.\"\n" if not file.writeFile(script_path, script_content) return false end if // Make executable let chmod_cmd = "chmod +x \"" + script_path + "\"" let pid = process.process_spawn_shell(chmod_cmd) if pid > 0 process.process_wait(pid) end if println(" Created " + script_path) return true end create_teardown_script fn create_filesystem(base: string): bool println("Creating base filesystem structure...") // Create all directories using a single mkdir -p command let cmd = "mkdir -p " + "\"" + base + "/bin\" " + "\"" + base + "/sbin\" " + "\"" + base + "/lib\" " + "\"" + base + "/lib/64\" " + "\"" + base + "/tmp\" " + "\"" + base + "/dev\" " + "\"" + base + "/proc\" " + "\"" + base + "/usr\" " + "\"" + base + "/usr/bin\" " + "\"" + base + "/usr/sbin\" " + "\"" + base + "/usr/lib\" " + "\"" + base + "/usr/lib/64\" " + "\"" + base + "/usr/include\" " + "\"" + base + "/usr/share\" " + "\"" + base + "/usr/share/man\" " + "\"" + base + "/usr/share/man/man1\" " + "\"" + base + "/usr/share/man/man3\" " + "\"" + base + "/usr/share/man/man5\" " + "\"" + base + "/usr/share/man/man8\" " + "\"" + base + "/usr/share/doc\" " + "\"" + base + "/usr/local\" " + "\"" + base + "/usr/local/bin\" " + "\"" + base + "/usr/local/lib\" " + "\"" + base + "/etc\" " + "\"" + base + "/etc/default\" " + "\"" + base + "/etc/init.d\" " + "\"" + base + "/var\" " + "\"" + base + "/var/lib\" " + "\"" + base + "/var/log\" " + "\"" + base + "/var/run\" " + "\"" + base + "/var/tmp\" " + "\"" + base + "/var/cache\" " + "\"" + base + "/opt\" " + "\"" + base + "/home\" " + "\"" + base + "/root\"" let pid = process.process_spawn_shell(cmd) if pid > 0 process.process_wait(pid) end if println(" Created 35 system directories") return true end create_filesystem fn create_coral_dirs(base: string): bool println("Creating Coral directory structure...") let cmd = "mkdir -p " + "\"" + base + "/etc/coral\" " + "\"" + base + "/etc/coral/keys\" " + "\"" + base + "/etc/coral/repos.d\" " + "\"" + base + "/var/lib/coral\" " + "\"" + base + "/var/lib/coral/installed\" " + "\"" + base + "/var/lib/coral/db\" " + "\"" + base + "/var/lib/coral/repos\" " + "\"" + base + "/var/lib/coral/trusted-keys\" " + "\"" + base + "/usr/zports\" " + "\"" + base + "/usr/zports/groups\" " + "\"" + base + "/usr/zports/pkgs\" " + "\"" + base + "/usr/zports/pkgs/sources\" " + "\"" + base + "/usr/zports/pkgs/packages\" " + "\"" + base + "/usr/zports/pkgs/build\"" let pid = process.process_spawn_shell(cmd) if pid > 0 process.process_wait(pid) end if println(" Created 14 Coral directories") return true end create_coral_dirs fn create_config(base: string): bool println("Creating default configuration...") // Create coral.conf let conf_path = base + "/etc/coral/coral.conf" let conf_content = "# Coral Package Manager Configuration\n" + "# Generated by mkchroot for testing\n" + "\n" + "[paths]\n" + "# Ports tree location\n" + "ports_dir = /usr/zports\n" + "\n" + "# Build directories\n" + "sources_dir = /usr/zports/pkgs/sources\n" + "packages_dir = /usr/zports/pkgs/packages\n" + "build_dir = /usr/zports/pkgs/build\n" + "\n" + "# Package database\n" + "db_dir = /var/lib/coral/installed\n" + "index_dir = /var/lib/coral/db\n" + "\n" + "[build]\n" + "# Number of parallel jobs (0 = auto)\n" + "jobs = 0\n" + "\n" + "# Keep build directory after successful build\n" + "keep_build = false\n" + "\n" + "[network]\n" + "# Download timeout in seconds\n" + "timeout = 30\n" + "\n" + "# Retry count for failed downloads\n" + "retries = 3\n" if not file.writeFile(conf_path, conf_content) println(" Failed to write coral.conf") return false end if println(" Created coral.conf") // Create empty repos.d placeholder let repos_path = base + "/etc/coral/repos.d/default.repo" let repos_content = "# Default Repository Configuration\n" + "# Add repository URLs here\n" + "\n" + "[zygaena-main]\n" + "name = Zygaena Main Repository\n" + "url = https://pkg.zygaena.org/main\n" + "enabled = true\n" + "priority = 10\n" if not file.writeFile(repos_path, repos_content) println(" Failed to write default.repo") return false end if println(" Created default.repo") return true end create_config fn copy_ports(base: string): bool println("Copying ports tree from " + PORTS_SOURCE + "...") // Check if source exists if not dir.dir_exists(PORTS_SOURCE) println(" WARNING: Ports source not found at " + PORTS_SOURCE) println(" Creating empty ports tree structure...") return create_empty_ports(base) end if // Copy core category let core_src = PORTS_SOURCE + "/core" let core_dst = base + "/usr/zports/groups/core" if dir.dir_exists(core_src) println(" Copying core category...") let cmd1 = "cp -r \"" + core_src + "\" \"" + core_dst + "\"" let pid1 = process.process_spawn_shell(cmd1) if pid1 > 0 process.process_wait(pid1) end if end if // Copy base category let base_src = PORTS_SOURCE + "/base" let base_dst = base + "/usr/zports/groups/base" if dir.dir_exists(base_src) println(" Copying base category...") let cmd2 = "cp -r \"" + base_src + "\" \"" + base_dst + "\"" let pid2 = process.process_spawn_shell(cmd2) if pid2 > 0 process.process_wait(pid2) end if end if // Count copied ports let core_count = count_ports(core_dst) let base_count = count_ports(base_dst) println(" Copied " + int_to_str(core_count) + " core ports") println(" Copied " + int_to_str(base_count) + " base ports") return true end copy_ports fn create_empty_ports(base: string): bool // Create sample port directory let sample_port = base + "/usr/zports/groups/core/hello" let cmd = "mkdir -p \"" + sample_port + "\"" let pid = process.process_spawn_shell(cmd) if pid > 0 process.process_wait(pid) end if // Create a simple Portfile let portfile = sample_port + "/Portfile" let content = "# Sample port for testing\n" + "name = hello\n" + "version = 1.0.0\n" + "revision = 0\n" + "description = Hello World test package\n" + "homepage = https://example.com\n" + "license = MIT\n" + "\n" + "# No sources - just creates a hello script\n" + "sources = []\n" + "\n" + "[build]\n" + "type = script\n" + "\n" + "[scripts]\n" + "build = \"\"\"\n" + "echo 'Building hello...'\n" + "\"\"\"\n" + "\n" + "install = \"\"\"\n" + "mkdir -p $DESTDIR/usr/bin\n" + "echo '#!/bin/sh' > $DESTDIR/usr/bin/hello\n" + "echo 'echo Hello from Coral!' >> $DESTDIR/usr/bin/hello\n" + "chmod +x $DESTDIR/usr/bin/hello\n" + "\"\"\"\n" if not file.writeFile(portfile, content) return false end if println(" Created sample port: core/hello") return true end create_empty_ports fn count_ports(dir_path: string): int if not dir.dir_exists(dir_path) return 0 end if mut entries: [string] = new [string](256) let count = dir.list_dir(dir_path, entries, 256) // Count only directories (each is a port) mut ports = 0 mut i = 0 while i < count let entry = entries[i] if str.length(entry) > 0 and entry[0] != '.' let full_path = path.join_path(dir_path, entry) if dir.dir_exists(full_path) ports = ports + 1 end if end if i = i + 1 end while return ports end count_ports // Helper: convert int to string fn int_to_str(n: int): string if n == 0 return "0" end if mut negative = false mut value = n if n < 0 negative = true value = 0 - n end if mut result = "" while value > 0 let digit = value % 10 result = str.concat(str.substring("0123456789", digit, 1), result) value = value / 10 end while if negative result = str.concat("-", result) end if return result end int_to_str proc print_usage() println("mkchroot - Create chrooted test environment for Coral") println("") println("Usage: mkchroot [options] [path]") println("") println("Options:") println(" path Chroot base path (default: /tmp/coral-chroot)") println(" --coral-bin Copy coral binary into chroot at /usr/local/bin/coral") println(" --help, -h Show this help") println("") println("Description:") println(" Creates a minimal filesystem structure suitable for testing") println(" Coral package manager operations. Includes:") println("") println(" - Base system directories (/bin, /usr, /etc, /var, etc.)") println(" - Coral configuration in /etc/coral/") println(" - Package database directories in /var/lib/coral/") println(" - Ports tree in /usr/zports/") println(" - Sample ports copied from zygaena-ports repository") println(" - Setup/teardown scripts for chroot mount management") println("") println("Generated Scripts (created alongside chroot):") println(" -setup.sh Mount /dev, /proc, /sys, and host binaries") println(" -teardown.sh Unmount all chroot mounts") println("") println("Examples:") println(" # Create basic chroot") println(" mkchroot") println(" mkchroot /tmp/test-env") println("") println(" # Create chroot with coral binary") println(" mkchroot --coral-bin ./coral /tmp/test-env") println("") println(" # Use --root flag (recommended, no mounts needed):") println(" coral --root /tmp/coral-chroot db init") println(" coral --root /tmp/coral-chroot install zlib") println(" coral --root /tmp/coral-chroot list") println("") println(" # Or enter chroot directly:") println(" sudo /tmp/coral-chroot-setup.sh /tmp/coral-chroot") println(" sudo chroot /tmp/coral-chroot /bin/bash") println(" coral db init") println(" coral build core/zlib") println(" exit") println(" sudo /tmp/coral-chroot-teardown.sh /tmp/coral-chroot") end print_usage