// config.reef — TOML service definition parser // // Parses /etc/zyginit.d/*.toml service definitions into ServiceDef structs. // Uses Reef's built-in TOML parser: import encoding.toml // // This module should be implemented first — it has no illumos dependencies // and can be tested on any platform. import encoding.toml import io.fs // Service types enum ServiceType { Daemon, // long-running process, restarted on exit Oneshot, // runs once, considered "started" after exit 0 Transient, // runs once, not tracked after exit } // Restart policy enum RestartPolicy { Always, OnFailure, Never, } // Stop method enum StopMethod { Contract, // signal all processes in contract (default) Exec, // run a stop command } // Service definition — parsed from TOML struct ServiceDef { name: String, description: String, service_type: ServiceType, // exec start_cmd: String, stop_cmd: Option, // stop stop_method: StopMethod, stop_signal: String, // "TERM", "HUP", etc. stop_timeout: i32, // seconds // dependencies requires: Vec, // hard dependencies — must be running after: Vec, // ordering — start after these, but don't require // contract contract_param: Vec, contract_fatal: Vec, // restart restart_policy: RestartPolicy, restart_delay: i32, // seconds max_retries: i32, // -1 = unlimited } // Parse a single TOML file into a ServiceDef fn parse_service(path: String) -> Result { // TODO: implement // 1. Read file contents // 2. Parse TOML // 3. Extract fields with defaults // 4. Validate required fields Err("not yet implemented") } // Scan /etc/zyginit.d/enabled.d/ and return list of enabled service paths fn scan_enabled(config_dir: String) -> Result, String> { // TODO: implement // 1. Read symlinks in enabled.d/ // 2. Resolve each to its .toml file // 3. Return list of absolute paths Err("not yet implemented") } // Load all enabled services fn load_enabled_services(config_dir: String) -> Result, String> { // TODO: implement // 1. scan_enabled() // 2. parse_service() for each // 3. Return Vec of ServiceDefs Err("not yet implemented") } // contract.reef — illumos process contract FFI bindings // // Provides Reef wrappers around libcontract for managing service processes. // Process contracts are an illumos kernel feature — this module ONLY works // on illumos/Hammerhead. // // Reference: illumos libcontract(3LIB), contract(5), process(5) // Source reference: hammerhead/usr/src/cmd/svc/startd/contract.c // C FFI declarations for libcontract extern "C" { // Template management fn ct_tmpl_activate(fd: i32) -> i32; fn ct_tmpl_clear(fd: i32) -> i32; fn ct_tmpl_set_informative(fd: i32, events: u32) -> i32; fn ct_tmpl_set_critical(fd: i32, events: u32) -> i32; // Process contract template fn ct_pr_tmpl_set_fatal(fd: i32, events: u32) -> i32; fn ct_pr_tmpl_set_param(fd: i32, params: u32) -> i32; // Contract status fn ct_status_read(fd: i32, detail: i32, statp: *mut CtStatus) -> i32; fn ct_status_free(status: *mut CtStatus); fn ct_status_get_id(status: *CtStatus) -> i32; // Contract control fn ct_ctl_abandon(fd: i32) -> i32; fn ct_ctl_adopt(fd: i32) -> i32; // POSIX fn open(path: *const u8, flags: i32) -> i32; fn close(fd: i32) -> i32; fn fork() -> i32; fn execvp(file: *const u8, argv: *const *const u8) -> i32; fn setsid() -> i32; fn sigsend(idtype: i32, id: i64, sig: i32) -> i32; fn poll(fds: *mut PollFd, nfds: i32, timeout: i32) -> i32; } // Contract event flags const CT_PR_EV_EMPTY: u32 = 0x01; // all processes exited const CT_PR_EV_FORK: u32 = 0x02; // process forked const CT_PR_EV_EXIT: u32 = 0x04; // process exited const CT_PR_EV_CORE: u32 = 0x08; // process dumped core const CT_PR_EV_SIGNAL: u32 = 0x10; // fatal signal const CT_PR_EV_HWERR: u32 = 0x20; // hardware error // Contract parameter flags const CT_PR_INHERIT: u32 = 0x01; // inherit contract on fork const CT_PR_NOORPHAN: u32 = 0x02; // kill orphans on contract abandon // Contract template path const CTFS_PROCESS_TEMPLATE: &str = "/system/contract/process/template"; const CTFS_PROCESS_LATEST: &str = "/system/contract/process/latest"; const CTFS_PROCESS_BUNDLE: &str = "/system/contract/process/bundle"; // Managed contract handle struct Contract { id: i32, // contract ID from kernel service_name: String, bundle_fd: i32, // fd for event monitoring } // Set up a contract template for forking a service fn setup_template() -> Result { // TODO: implement // 1. open(CTFS_PROCESS_TEMPLATE, O_RDWR) // 2. ct_tmpl_set_informative(fd, CT_PR_EV_EMPTY) // 3. ct_tmpl_set_critical(fd, CT_PR_EV_EMPTY | CT_PR_EV_HWERR) // 4. ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR) // 5. ct_pr_tmpl_set_param(fd, CT_PR_INHERIT | CT_PR_NOORPHAN) // 6. ct_tmpl_activate(fd) // 7. Return fd Err("not yet implemented") } // After fork(), retrieve the contract ID for the new child fn get_latest_contract() -> Result { // TODO: implement // 1. open(CTFS_PROCESS_LATEST, O_RDONLY) // 2. ct_status_read() // 3. ct_status_get_id() // 4. Return contract ID Err("not yet implemented") } // Open the contract bundle fd for poll()-based event monitoring fn open_bundle() -> Result { // TODO: implement Err("not yet implemented") } // Kill all processes in a contract fn kill_contract(contract_id: i32, signal: i32) -> Result<(), String> { // TODO: implement using sigsend(P_CTID, contract_id, signal) Err("not yet implemented") } // Re-adopt an existing contract (after zyginit restart) fn adopt_contract(contract_id: i32) -> Result<(), String> { // TODO: implement Err("not yet implemented") } // depgraph.reef — Dependency graph and topological sort // // Builds a directed acyclic graph from ServiceDef dependencies, // performs topological sort to determine boot order, and identifies // services that can start in parallel. // // This module has no illumos dependencies — can be tested on any platform. import config.{ServiceDef} // A node in the dependency graph struct DepNode { name: String, service: ServiceDef, edges: Vec, // services this depends on (requires + after) in_degree: i32, // number of unsatisfied dependencies } // The full dependency graph struct DepGraph { nodes: Map, } // Build a dependency graph from a list of service definitions fn build_graph(services: Vec) -> Result { // TODO: implement // 1. Create a DepNode for each service // 2. Add edges for requires + after dependencies // 3. Detect missing dependencies (warn, don't fail) // 4. Detect cycles (fail with clear error message) Err("not yet implemented") } // Topological sort — returns services grouped by startup tier // Services within the same tier can start in parallel. // // Returns: Vec> — each inner Vec is a parallel tier // Example: [["root-fs"], ["filesystem", "loopback"], ["network", "syslog"], ["sshd", "cron"]] fn topo_sort(graph: DepGraph) -> Result>, String> { // TODO: implement using Kahn's algorithm // 1. Find all nodes with in_degree == 0 → first tier // 2. Remove those nodes, decrement in_degree of dependents // 3. Repeat until all nodes placed // 4. If nodes remain with in_degree > 0 → cycle detected Err("not yet implemented") } // Check for dependency cycles — returns the cycle path if found fn detect_cycle(graph: DepGraph) -> Option> { // TODO: implement using DFS with coloring None } // socket.reef — Unix domain socket server for zygctl communication // // Listens on /var/run/zyginit.sock for commands from zygctl. // Protocol: newline-delimited text commands, JSON responses. // // Commands: // status → list all services and their states // status → status of a single service // start → start a service (transient, not persisted) // stop → stop a service // restart → stop then start // enable → create symlink in enabled.d/ (persisted) // disable → remove symlink from enabled.d/ // list → list all known service definitions const SOCKET_PATH: &str = "/var/run/zyginit.sock"; // Create and bind the Unix domain socket fn create_socket() -> Result { // TODO: implement // 1. socket(AF_UNIX, SOCK_STREAM, 0) // 2. bind() to SOCKET_PATH // 3. listen() // 4. Return fd for poll() Err("not yet implemented") } // Handle a client connection — read command, dispatch, respond fn handle_client(client_fd: i32) -> Result<(), String> { // TODO: implement // 1. Read command line from client // 2. Parse command + args // 3. Dispatch to appropriate handler // 4. Send response (JSON) // 5. Close client fd Err("not yet implemented") } // supervisor.reef — Service lifecycle management // // Handles starting, stopping, and restarting services using process // contracts. Implements restart policies (always, on-failure, never). import config.{ServiceDef, ServiceType, RestartPolicy, StopMethod} import contract.{Contract, setup_template, get_latest_contract, kill_contract} // Runtime state of a service enum ServiceState { Disabled, // not enabled Waiting, // waiting for dependencies Starting, // exec'd, waiting for confirmation Running, // running (has contract) Stopping, // stop signal sent, waiting for exit Stopped, // cleanly stopped Failed, // exited with error Maintenance, // exceeded max retries } // Runtime info for a managed service struct ServiceRuntime { def: ServiceDef, state: ServiceState, contract: Option, pid: Option, restart_count: i32, last_exit_code: Option, last_start_time: i64, // unix timestamp } // Start a service — fork, set up contract, exec fn start_service(svc: ServiceDef) -> Result { // TODO: implement // 1. setup_template() for contract // 2. fork() // 3. In child: setsid(), exec(svc.start_cmd) // 4. In parent: get_latest_contract(), ct_tmpl_clear() // 5. Return ServiceRuntime with Running state Err("not yet implemented") } // Stop a service fn stop_service(rt: ServiceRuntime) -> Result { // TODO: implement // Based on svc.stop_method: // Contract: kill_contract(contract_id, signal) // Exec: fork/exec the stop command // Wait up to stop_timeout, then SIGKILL Err("not yet implemented") } // Handle a contract event (service exited) fn handle_exit(rt: ServiceRuntime, exit_code: i32) -> ServiceRuntime { // TODO: implement // Based on restart_policy: // Always: schedule restart after delay // OnFailure: restart only if exit_code != 0 // Never: mark as Stopped // Check max_retries — enter Maintenance if exceeded rt } // zygctl.reef — Administrative CLI for zyginit // // Communicates with the running zyginit daemon over Unix domain socket // at /var/run/zyginit.sock. // // Usage: // zygctl status # list all services // zygctl status # status of one service // zygctl start # start a service (transient) // zygctl stop # stop a service // zygctl restart # restart a service // zygctl enable # enable at boot (creates symlink) // zygctl disable # disable at boot (removes symlink) // zygctl list # list all service definitions const SOCKET_PATH: &str = "/var/run/zyginit.sock"; fn main() -> i32 { // TODO: implement // 1. Parse command-line arguments // 2. Connect to Unix domain socket // 3. Send command // 4. Read and display response // 5. For enable/disable: also manage symlinks in enabled.d/ 0 } // zyginit.reef — Main init daemon (PID 1) // // This is the entry point for the Zygaena init system. // It replaces SMF's svc.startd + svc.configd + master restarter. // // Boot sequence: // 1. Scan /etc/zyginit.d/enabled.d/ for enabled services // 2. Parse TOML definitions → ServiceDef structs // 3. Build dependency graph → topological sort // 4. Start services in parallel, respecting dependency order // 5. Mount tmpfs, create /var/run/zyginit.sock // 6. Enter poll(2) event loop import config.{load_enabled_services, ServiceDef} import depgraph.{build_graph, topo_sort} import contract.{open_bundle} import supervisor.{start_service, handle_exit, ServiceRuntime, ServiceState} import socket.{create_socket, handle_client} const CONFIG_DIR: &str = "/etc/zyginit.d"; const DIAG_DIR: &str = "/var/run/zyginit"; fn main() -> i32 { // TODO: implement // Phase 1: Load configuration // let services = load_enabled_services(CONFIG_DIR)?; // Phase 2: Build dependency graph and compute boot order // let graph = build_graph(services)?; // let tiers = topo_sort(graph)?; // Phase 3: Start services tier by tier // for tier in tiers { // // Start all services in this tier in parallel // for svc_name in tier { // start_service(services[svc_name]); // } // // Wait for all services in tier to reach Running state // } // Phase 4: Set up communication // let bundle_fd = open_bundle()?; // let socket_fd = create_socket()?; // let signal_fd = setup_signal_pipe()?; // self-pipe trick for signals // Phase 5: Event loop // loop { // poll([bundle_fd, socket_fd, signal_fd], -1); // // Handle contract events (service exits) // // Handle socket connections (zygctl commands) // // Handle signals (SIGCHLD, SIGTERM, SIGHUP for reload) // } 0 }