# usr/src/zyginit/Makefile — Hammerhead-side build for the vendored # zyginit init system, zygctl admin CLI, and sysv-wrapper compat shim. # # zyginit ships the engine (binary + schema docs + man pages). Hammerhead # owns the policy: 50 service.toml definitions, the enabled.d/ default- # enable policy, and the example configs. The split was agreed with the # zyginit team on 2026-05-21; before then, ~22 service definitions came # from the upstream tarball and we layered a small overrides/ tree on # top of them. That two-source model is gone — see docs/bugs/zyginit- # 0.2-service-toml-paths.md for the motivating bug. # # Engine source comes from a tarball pinned in tools/vendored.conf, # extracted by hh-build into ./source/. The Makefile here is the only # committed build glue; ./source/ is gitignored. # # Three binaries land in /sbin/: # /sbin/zyginit PID 1 init daemon (with /sbin/init -> zyginit) # /sbin/zygctl admin CLI # /sbin/sysv-wrapper legacy compat (halt/reboot/poweroff/telinit symlinks) # # Plus 50 service definitions installed under /etc/zyginit/services//, # the enabled.d/ symlink set, examples, runtime dirs under /var/{log,run}/ # zyginit/, and three man pages. # Always use the staged reefc from tools/bootstrap/root-hh/. bootstrap-hh.sh # runs earlier in hh-build's build_toolchain; this is the version pinned # by tools/bootstrap.conf and built fresh, never the system reefc. HAMMERHEAD_ROOT = $(SRC)/../../.. REEFC = $(HAMMERHEAD_ROOT)/tools/bootstrap/root-hh/usr/bin/reefc # Vendored source tree (extracted by hh-build, gitignored). ZSRC = source # Built binaries (paths inside ZSRC). ZYGINIT_BIN = $(ZSRC)/build/zyginit ZYGCTL_BIN = $(ZSRC)/tools/zygctl/build/zygctl SYSVW_BIN = $(ZSRC)/tools/sysv-wrapper/sysv-wrapper # Install destinations under proto. ROOTSBIN = $(ROOT)/sbin ROOTETC_ZYGINIT = $(ROOT)/etc/zyginit ROOTETC_ZYGINIT_ENABLED = $(ROOT)/etc/zyginit/enabled.d ROOTETC_ZYGINIT_EX = $(ROOT)/etc/zyginit/examples ROOTVAR_LOG = $(ROOT)/var/log/zyginit ROOTVAR_RUN = $(ROOT)/var/run/zyginit ROOTMAN5 = $(ROOT)/usr/share/man/man5 ROOTMAN8 = $(ROOT)/usr/share/man/man8 .PHONY: all install clean clobber _msg \ check_source build_zyginit build_zygctl build_sysvwrapper \ build_console_login_helper \ install_bins install_init_link install_services \ install_examples install_enabled_d install_man install_runtime_dirs all: check_source build_zyginit build_zygctl build_sysvwrapper \ build_console_login_helper check_source: @if [ ! -d "$(ZSRC)" ]; then \ echo "ERROR: $(ZSRC)/ missing — hh-build's extract step did not run." >&2; \ echo " Source comes from tools/vendored.conf via hh-build." >&2; \ exit 1; \ fi @if [ ! -x "$(REEFC)" ]; then \ echo "ERROR: staged reefc missing at $(REEFC)" >&2; \ echo " bootstrap-hh.sh build must run before zyginit." >&2; \ exit 1; \ fi # zyginit — PID 1 init daemon. # helpers.c provides illumos-side syscall wrappers; zyginit links libcontract. build_zyginit: @mkdir -p $(ZSRC)/build cd $(ZSRC) && \ gcc -c src/helpers.c -o build/helpers.o && \ $(REEFC) build -l contract --obj build/helpers.o # zygctl — admin CLI. # symlink_wrapper.c handles argv[0] dispatch for the multi-name binary. build_zygctl: @mkdir -p $(ZSRC)/tools/zygctl/build cd $(ZSRC)/tools/zygctl && \ gcc -c src/symlink_wrapper.c -o build/symlink_wrapper.o && \ $(REEFC) build --obj build/symlink_wrapper.o # sysv-wrapper — plain C shim with its own Makefile. build_sysvwrapper: $(MAKE) -C $(ZSRC)/tools/sysv-wrapper # Per-service compiled helpers shipped under ./services//. # Today only console-login has one (write_login_utmpx — writes a # LOGIN_PROCESS utmpx entry before exec'ing ttymon; required for # login(1) to find a matching ut_pid). build_console_login_helper: gcc -O2 -Wall \ -o services/console-login/write_login_utmpx \ services/console-login/write_login_utmpx.c install: all install_bins install_init_link install_services \ install_examples install_enabled_d install_man install_runtime_dirs install_bins: mkdir -p $(ROOTSBIN) # cp + chmod (not install(1) — illumos's /usr/sbin/install is a broken # shell script that needs a richer PATH than hh-build provides). cp $(ZYGINIT_BIN) $(ROOTSBIN)/zyginit cp $(ZYGCTL_BIN) $(ROOTSBIN)/zygctl cp $(SYSVW_BIN) $(ROOTSBIN)/sysv-wrapper chmod 0555 $(ROOTSBIN)/zyginit $(ROOTSBIN)/zygctl $(ROOTSBIN)/sysv-wrapper # legacy-name symlinks → sysv-wrapper ln -sf sysv-wrapper $(ROOTSBIN)/halt ln -sf sysv-wrapper $(ROOTSBIN)/reboot ln -sf sysv-wrapper $(ROOTSBIN)/poweroff ln -sf sysv-wrapper $(ROOTSBIN)/telinit # /sbin/init -> /sbin/zyginit. The kernel exec's /sbin/init at boot, # so this symlink is what makes zyginit PID 1. install_init_link: rm -f $(ROOTSBIN)/init ln -sf zyginit $(ROOTSBIN)/init # Service definitions are Hammerhead-owned (not from the zyginit # tarball) as of 2026-05-21. zyginit ships only the engine binary + # schema docs + a handful of representative examples; Hammerhead owns # the policy: 50 services under ./services// each containing a # service.toml plus any helper scripts and helper-binary sources. # Layout matches the runtime tree at /etc/zyginit/services//. install_services: mkdir -p $(ROOTETC_ZYGINIT)/services @for d in services/*/; do \ [ -d "$$d" ] || continue; \ name=$$(basename $$d); \ dst=$(ROOTETC_ZYGINIT)/services/$$name; \ mkdir -p $$dst; \ for f in $$d*; do \ [ -e "$$f" ] || continue; \ base=$$(basename "$$f"); \ case "$$base" in \ *.c) continue ;; \ esac; \ cp "$$f" $$dst/; \ done; \ [ -f $$dst/service.toml ] && chmod 0644 $$dst/service.toml || true; \ find $$dst -type f -name '*.sh' -exec chmod 0755 {} \; ; \ done # Per-service compiled helpers (e.g. write_login_utmpx). -find $(ROOTETC_ZYGINIT)/services -type f -name 'write_login_utmpx' -exec chmod 0755 {} \; install_examples: mkdir -p $(ROOTETC_ZYGINIT_EX) -cp examples/* $(ROOTETC_ZYGINIT_EX)/ 2>/dev/null -chmod 0644 $(ROOTETC_ZYGINIT_EX)/* 2>/dev/null # enabled.d/ policy comes from THIS directory (Hammerhead-side decision # of which services run by default), NOT from the vendored tarball. # Each entry in ./enabled.d/ is a symlink whose basename is the service # name. With zyginit 0.2.x per-service-directory layout, the proto-side # link points at ../services// (the directory). The on-disk source # symlinks may still target the old .toml — we read only the # basename and synthesize the new target, so we don't depend on the # source symlink's literal target or on a base-system readlink(1). install_enabled_d: mkdir -p $(ROOTETC_ZYGINIT_ENABLED) @for f in enabled.d/*; do \ [ -L "$$f" ] || continue; \ name=$$(basename "$$f"); \ rm -f $(ROOTETC_ZYGINIT_ENABLED)/$$name; \ ln -sf "../services/$$name" $(ROOTETC_ZYGINIT_ENABLED)/$$name; \ done install_man: mkdir -p $(ROOTMAN5) $(ROOTMAN8) cp $(ZSRC)/docs/man/man5/zyginit.5 $(ROOTMAN5)/zyginit.5 cp $(ZSRC)/docs/man/man8/zyginit.8 $(ROOTMAN8)/zyginit.8 cp $(ZSRC)/docs/man/man8/zygctl.8 $(ROOTMAN8)/zygctl.8 chmod 0444 $(ROOTMAN5)/zyginit.5 $(ROOTMAN8)/zyginit.8 $(ROOTMAN8)/zygctl.8 install_runtime_dirs: mkdir -p $(ROOTVAR_LOG) $(ROOTVAR_RUN) clean: rm -rf $(ZSRC)/build $(ZSRC)/tools/zygctl/build rm -f $(ZSRC)/tools/sysv-wrapper/sysv-wrapper clobber: clean rm -rf $(ZSRC) # zyginit is not localized — no .po messages. _msg: