#!/bin/bash # Core tinsnip shared library functions # Color constants RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # Shared logging functions with customizable prefix log_with_prefix() { local prefix="$1" shift echo -e "${GREEN}[$prefix]${NC} $*" >&2 } error_with_prefix() { local prefix="$1" shift echo -e "${RED}[ERROR]${NC} $*" >&2 exit 1 } warn_with_prefix() { local prefix="$1" shift echo -e "${YELLOW}[WARNING]${NC} $*" >&2 } # Get current sheet (from environment, file, or default) get_sheet() { # Use sheet from environment, file, or default if [[ -n "${TIN_SHEET:-}" ]]; then echo "$TIN_SHEET" elif [[ -f "/etc/tinsnip-sheet" ]]; then cat "/etc/tinsnip-sheet" else echo "topsheet" fi } # Update shell configuration files (.bashrc or .profile) with environment variables # Usage: update_shell_config username config_file "VAR1=value1" "VAR2=value2" ... update_shell_config() { local username="$1" local config_file="$2" shift 2 local env_vars=("$@") sudo -u "$username" -i bash << EOF # Remove any existing environment variables to avoid duplicates grep -v "$(printf "%s\\\\|" "${env_vars[@]}" | sed 's/=.*/=/g' | sed 's/\\\\|$//')" ~/$config_file > ~/${config_file}.tmp 2>/dev/null && mv ~/${config_file}.tmp ~/$config_file || touch ~/$config_file # Add environment variables cat >> ~/$config_file << 'CONFIG_EOF' # Environment variables managed by tinsnip $(printf "export %s\n" "${env_vars[@]}") CONFIG_EOF EOF } # Create service environment file on NFS mount with all variables needed by service create_service_env_file() { local service_name="$1" local service_env="$2" local service_uid="$3" local mount_point="/mnt/${service_name}-${service_env}" local sheet="${TIN_SHEET:-topsheet}" local env_file="$mount_point/.env" log_with_prefix "Service Env" "Creating $env_file" # Create the .env file with all service variables sudo -u "${service_name}-${service_env}" tee "$env_file" > /dev/null << EOF # Tinsnip service environment variables # Generated automatically - do not edit manually TIN_SERVICE_NAME=$service_name TIN_SERVICE_ENVIRONMENT=$service_env TIN_SERVICE_UID=$service_uid TIN_SHEET=$sheet # XDG Base Directory variables (NFS-backed) XDG_DATA_HOME=$mount_point/data XDG_CONFIG_HOME=$mount_point/config XDG_STATE_HOME=$mount_point/state # Docker environment (set by install_docker.sh if applicable) # These will be added by Docker setup process EOF log_with_prefix "Service Env" "Created service environment file" } # Create service environment loader script and integrate with shell configs # This creates a standalone script file to avoid shell expansion issues create_service_env_loader_script() { local service_user="$1" local script_path="/home/$service_user/.tinsnip-env-loader.sh" log_with_prefix "Env Loader" "Creating environment loader script: $script_path" # Create environment loader for machine metadata (post-ACT-2) sudo bash -c "cat > '$script_path'" << 'EOF' #!/bin/bash # Tinsnip machine environment loader (ACT-2: centralized metadata) export XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}" # Load machine environment variables from centralized metadata # Pattern: ^.+-[^-]+$ matches machinename-env (machine can have dashes, env cannot) if [[ "$(whoami)" =~ ^.+-[^-]+$ ]]; then TIN_MACHINE_ENV_FILE="/mnt/$(whoami)/.machine/machine.env" TIN_MACHINE_ENV_CACHE="$XDG_CACHE_HOME/tinsnip/$(whoami).env" if [[ -f "$TIN_MACHINE_ENV_FILE" ]]; then source "$TIN_MACHINE_ENV_FILE" mkdir -p "$(dirname "$TIN_MACHINE_ENV_CACHE")" cp "$TIN_MACHINE_ENV_FILE" "$TIN_MACHINE_ENV_CACHE" 2>/dev/null || true elif [[ -f "$TIN_MACHINE_ENV_CACHE" ]]; then source "$TIN_MACHINE_ENV_CACHE" fi # Export essential variables for Docker and tinsnip if [[ -n "${TIN_MACHINE_NAME:-}" ]]; then export TIN_MACHINE_NAME TIN_MACHINE_ENVIRONMENT TIN_SERVICE_UID TIN_SHEET export DOCKER_HOST # Set XDG_RUNTIME_DIR if not already set (needed for Docker socket access) if [[ -z "${XDG_RUNTIME_DIR:-}" ]]; then export XDG_RUNTIME_DIR="/run/user/${TIN_SERVICE_UID}" fi fi fi EOF # Fix ownership and permissions sudo chown "$service_user:$service_user" "$script_path" sudo chmod 755 "$script_path" # Debug: Check what the actual permissions are log_with_prefix "Debug" "File permissions after setting:" sudo ls -la "$script_path" || true # Validate the loader script syntax (using root to avoid permission issues) if ! sudo bash -n "$script_path" 2>/tmp/syntax_error.log; then warn_with_prefix "Env Loader" "⚠️ Syntax error in loader script!" log_with_prefix "Debug" "Syntax error details:" sudo cat /tmp/syntax_error.log 2>/dev/null || true log_with_prefix "Debug" "Script content:" sudo head -10 "$script_path" 2>/dev/null || true return 1 else log_with_prefix "Env Loader" "Loader script syntax validated successfully" fi log_with_prefix "Env Loader" "Adding script to shell configurations" # Add source line to both shell configs using sudo with explicit paths log_with_prefix "Env Loader" "Updating .bashrc" # Check if already added to avoid duplicates if ! sudo grep -q "tinsnip-env-loader" "/home/$service_user/.bashrc" 2>/dev/null; then if echo "source ~/.tinsnip-env-loader.sh" | sudo tee -a "/home/$service_user/.bashrc" > /dev/null 2>&1; then sudo chown "$service_user:$service_user" "/home/$service_user/.bashrc" log_with_prefix "Env Loader" "Added to .bashrc" else warn_with_prefix "Env Loader" "⚠️ Failed to update .bashrc" fi else log_with_prefix "Env Loader" ".bashrc already configured" fi log_with_prefix "Env Loader" "Updating .profile" # Check if already added to avoid duplicates if ! sudo grep -q "tinsnip-env-loader" "/home/$service_user/.profile" 2>/dev/null; then if echo "source ~/.tinsnip-env-loader.sh" | sudo tee -a "/home/$service_user/.profile" > /dev/null 2>&1; then sudo chown "$service_user:$service_user" "/home/$service_user/.profile" log_with_prefix "Env Loader" "Added to .profile" else warn_with_prefix "Env Loader" "⚠️ Failed to update .profile" fi else log_with_prefix "Env Loader" ".profile already configured" fi # Test if the shell configs can be sourced without errors (with better debugging) log_with_prefix "Env Loader" "Validating shell configurations" # Check .bashrc if ! sudo -u "$service_user" bash -n "/home/$service_user/.bashrc" 2>/dev/null; then warn_with_prefix "Env Loader" "⚠️ Syntax error in .bashrc - checking content" # Show last few lines for debugging sudo tail -5 "/home/$service_user/.bashrc" | while read -r line; do log_with_prefix "Debug" ".bashrc: $line" done 2>/dev/null || true fi # Check .profile if ! sudo -u "$service_user" bash -n "/home/$service_user/.profile" 2>/dev/null; then warn_with_prefix "Env Loader" "⚠️ Syntax error in .profile - checking content" # Show last few lines for debugging sudo tail -5 "/home/$service_user/.profile" | while read -r line; do log_with_prefix "Debug" ".profile: $line" done 2>/dev/null || true fi log_with_prefix "Env Loader" "Environment loader script configured" } # Auto-discover NAS server for current sheet discover_nas_for_sheet() { local sheet sheet=$(get_sheet) # Check if topsheet.station-prod registry is available local registry_path="/mnt/station-prod/data/nas-credentials/nas-servers" if [[ -f "$registry_path" ]]; then # First check if explicitly registered for this sheet local nas_server=$(grep "^${sheet}=" "$registry_path" 2>/dev/null | cut -d= -f2) if [[ -n "$nas_server" ]]; then echo "$nas_server" return 0 fi # Only fall back to default for registered sheets # Check if this sheet exists in the sheet registry local sheet_registry="/mnt/station-prod/data/sheets" if [[ -f "$sheet_registry" ]] && grep -q "^${sheet}=" "$sheet_registry" 2>/dev/null; then nas_server=$(grep "^default=" "$registry_path" 2>/dev/null | cut -d= -f2) if [[ -n "$nas_server" ]]; then echo "$nas_server" return 0 fi fi fi # Registry not available or no server found return 1 }