clawdie-iso/firstboot/shell-system.sh

336 lines
9.8 KiB
Bash
Executable file

#!/bin/sh
# Clawdie Shell — System Configuration Module
# Purpose: System-level config (rc.conf, hostname, services, environment)
# Author: Clawdie Project
# POSIX-compliant (no bash-isms)
set -eu
# FreeBSD /bin/sh doesn't support trap ERR
# Configuration (can be overridden for testing)
RC_CONF="${RC_CONF:-/etc/rc.conf}"
LOADER_CONF="${LOADER_CONF:-/boot/loader.conf}"
SYSCTL_CONF="${SYSCTL_CONF:-/etc/sysctl.conf}"
HOSTNAME_FILE="${HOSTNAME_FILE:-/etc/hostname}"
PROFILE_DIR="${PROFILE_DIR:-/etc/profile.d}"
LOG_FILE="${LOG_FILE:-/var/log/clawdie-firstboot.log}"
PROGRESS_FILE="${PROGRESS_FILE:-/var/log/clawdie-firstboot.progress}"
# Derived from wizard inputs (caller sets these)
# TZ - Timezone (e.g., "Europe/Ljubljana")
# AGENT_DOMAIN - FQDN (e.g., "clawdie.home.arpa" for local, or public domain)
# DETECTED_GPU - GPU vendor from gpu module (intel, amd, nvidia, vmware, vesa)
# ============================================================================
# MAIN ENTRY POINT
# ============================================================================
clawdie_shell_system_config() {
# Main orchestrator
log_msg "[system] Starting system configuration"
if [ -z "${TZ:-}" ]; then
log_msg "[system] ERROR: TZ not set"
return 1
fi
if [ -z "${AGENT_DOMAIN:-}" ]; then
log_msg "[system] ERROR: AGENT_DOMAIN not set"
return 1
fi
if [ -z "${SYSTEM_LOCALE:-}" ]; then
log_msg "[system] ERROR: SYSTEM_LOCALE not set"
return 1
fi
# Write rc.conf with timezone and services
clawdie_shell_system_write_rcconf
log_msg "[system] Updated rc.conf"
# Set hostname
clawdie_shell_system_set_hostname
log_msg "[system] Set hostname"
# Setup environment
clawdie_shell_system_setup_env
log_msg "[system] Setup environment"
# Setup locale + keymap
clawdie_shell_system_setup_locale
log_msg "[system] Setup locale"
# Enable FreeBSD mac_do framework with no credential grants yet
clawdie_shell_system_setup_mac_do
log_msg "[system] Setup mac_do framework"
# Enable services
clawdie_shell_system_enable_services
log_msg "[system] Enabled services"
echo "[SYSTEM] COMPLETE" >> "$PROGRESS_FILE"
log_msg "[system] System configuration complete"
}
# ============================================================================
# RC.CONF CONFIGURATION
# ============================================================================
clawdie_shell_system_write_rcconf() {
# Update /etc/rc.conf with:
# - timezone
# - service configurations (dbus, hald, seatd, lightdm)
# - Lumina desktop settings
if [ ! -f "$RC_CONF" ]; then
log_msg "[system] Creating $RC_CONF"
touch "$RC_CONF"
fi
# Helper to set or update rc.conf variable
clawdie_shell_system_sysrc "timezone=$TZ"
if [ -n "${KEYMAP:-}" ]; then
clawdie_shell_system_sysrc "keymap=$KEYMAP"
fi
clawdie_shell_system_sysrc "dbus_enable=YES"
clawdie_shell_system_sysrc "hald_enable=YES"
clawdie_shell_system_sysrc "seatd_enable=YES"
clawdie_shell_system_sysrc "display_manager=lightdm"
clawdie_shell_system_sysrc "lightdm_enable=YES"
log_msg "[system] Wrote rc.conf configuration"
}
clawdie_shell_system_sysrc() {
# Add or update a variable in rc.conf
# Input: VAR=VALUE
clawdie_shell_system_set_config_line "$RC_CONF" "$1"
}
clawdie_shell_system_set_config_line() {
# Add or update KEY=VALUE in a config file.
# Input: FILE KEY=VALUE
local config_file="$1"
local var_assignment="$2"
local var_name
var_name=$(echo "$var_assignment" | cut -d= -f1)
if [ ! -f "$config_file" ]; then
mkdir -p "$(dirname "$config_file")"
touch "$config_file"
fi
if grep -q "^${var_name}=" "$config_file" 2>/dev/null; then
sed -i '' "s|^${var_name}=.*|${var_assignment}|" "$config_file"
else
echo "$var_assignment" >> "$config_file"
fi
}
# ============================================================================
# MAC_DO FRAMEWORK
# ============================================================================
clawdie_shell_system_setup_mac_do() {
# Load FreeBSD mac_do at boot, but grant no credential transitions yet.
# FreeBSD 15 accepts an empty rule list as no-op policy: security.mac.do.rules=
clawdie_shell_system_set_config_line "$LOADER_CONF" 'mac_do_load="YES"'
clawdie_shell_system_set_config_line "$SYSCTL_CONF" 'security.mac.do.rules='
# Best-effort live activation for the firstboot session. The persistent
# loader/sysctl files above are the source of truth after reboot.
if command -v kldload >/dev/null 2>&1; then
kldload mac_do 2>/dev/null || true
elif [ -x /sbin/kldload ]; then
/sbin/kldload mac_do 2>/dev/null || true
fi
if command -v sysctl >/dev/null 2>&1; then
sysctl security.mac.do.rules= >/dev/null 2>&1 || true
elif [ -x /sbin/sysctl ]; then
/sbin/sysctl security.mac.do.rules= >/dev/null 2>&1 || true
fi
}
# ============================================================================
# HOSTNAME CONFIGURATION
# ============================================================================
clawdie_shell_system_set_hostname() {
# Set /etc/hostname and apply live
if [ ! -f "$HOSTNAME_FILE" ]; then
touch "$HOSTNAME_FILE"
fi
# Write to file
echo "$AGENT_DOMAIN" > "$HOSTNAME_FILE"
# Apply live (if not in chroot)
if command -v hostname >/dev/null 2>&1; then
hostname "$AGENT_DOMAIN" 2>/dev/null || true
fi
log_msg "[system] Set hostname to $AGENT_DOMAIN"
}
# ============================================================================
# ENVIRONMENT SETUP
# ============================================================================
clawdie_shell_system_setup_env() {
# Create /etc/profile.d/clawdie.sh for environment variables
# Sets up npm global paths and other Clawdie-specific variables
if [ ! -d "$PROFILE_DIR" ]; then
mkdir -p "$PROFILE_DIR"
fi
local clawdie_profile="$PROFILE_DIR/clawdie.sh"
local normalized_locale
normalized_locale=$(clawdie_shell_system_normalize_locale "$SYSTEM_LOCALE")
cat > "$clawdie_profile" <<EOF
# Clawdie-AI environment setup
# Adds npm global bin directory to PATH
export npm_config_prefix="/opt/clawdie/npm-global"
export PATH="/opt/clawdie/npm-global/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:\${PATH}"
export LANG="${normalized_locale}"
export LC_ALL="${normalized_locale}"
EOF
chmod 644 "$clawdie_profile"
log_msg "[system] Created $clawdie_profile"
}
# ============================================================================
# SERVICE ENABLEMENT
# ============================================================================
clawdie_shell_system_enable_services() {
# Enable and start required services
# Safe to fail if running in chroot (first boot)
local services="dbus hald seatd lightdm"
for service in $services; do
if command -v service >/dev/null 2>&1; then
# Try to start service
service "$service" onestart 2>/dev/null || {
log_msg "[system] Could not start $service (expected in chroot)"
}
fi
done
log_msg "[system] Service enablement complete"
}
# ============================================================================
# LOCALE + KEYMAP SETUP
# ============================================================================
clawdie_shell_system_setup_locale() {
local normalized_locale
normalized_locale=$(clawdie_shell_system_normalize_locale "$SYSTEM_LOCALE")
# Write per-user login_conf (must be UTF-8).
if id clawdie >/dev/null 2>&1; then
local login_conf="/home/clawdie/.login_conf"
cat > "$login_conf" <<EOF
me:\\
:charset=UTF-8:\\
:lang=${normalized_locale}:
EOF
chown clawdie:clawdie "$login_conf" 2>/dev/null || true
chmod 644 "$login_conf" 2>/dev/null || true
if command -v cap_mkdb >/dev/null 2>&1; then
cap_mkdb "$login_conf" 2>/dev/null || true
fi
fi
}
clawdie_shell_system_normalize_locale() {
local raw="$1"
raw=$(echo "$raw" | tr '-' '_')
raw=${raw%%.*}
raw=${raw%%@*}
if [ -z "$raw" ]; then
raw="C"
fi
echo "${raw}.UTF-8"
}
# ============================================================================
# VALIDATION
# ============================================================================
clawdie_shell_system_validate() {
# Verify system configuration completed
if [ ! -f "$RC_CONF" ]; then
echo "ERROR: rc.conf not found" >&2
return 1
fi
# Check timezone is set
if ! grep -q "^timezone=" "$RC_CONF"; then
echo "ERROR: timezone not set in rc.conf" >&2
return 1
fi
# Check hostname file exists
if [ ! -f "$HOSTNAME_FILE" ]; then
echo "ERROR: $HOSTNAME_FILE not created" >&2
return 1
fi
# Check mac_do framework config exists. Empty rules intentionally grant no
# credential transitions.
if ! grep -q '^mac_do_load="YES"' "$LOADER_CONF" 2>/dev/null; then
echo "ERROR: mac_do_load not set in $LOADER_CONF" >&2
return 1
fi
if ! grep -q '^security.mac.do.rules=' "$SYSCTL_CONF" 2>/dev/null; then
echo "ERROR: security.mac.do.rules not set in $SYSCTL_CONF" >&2
return 1
fi
# Check environment profile exists
if [ ! -f "$PROFILE_DIR/clawdie.sh" ]; then
echo "ERROR: $PROFILE_DIR/clawdie.sh not created" >&2
return 1
fi
log_msg "[system] Validation passed"
return 0
}
# ============================================================================
# UTILITY: Logging
# ============================================================================
log_msg() {
local msg="$1"
echo "$msg" >> "$LOG_FILE" 2>/dev/null || true
}
# ============================================================================
# Export for use by firstboot.sh
# ============================================================================
case "${0##*/}" in
clawdie-shell-system.sh)
# Direct execution (for testing)
clawdie_shell_system_config
clawdie_shell_system_validate
;;
*)
# Sourced from another script — functions available
;;
esac