#!/bin/bash set -euo pipefail shopt -s lastpipe UID_MAP="/etc/jimlab/uid.map" SUBID_MAP="/etc/jimlab/subid.map" DEFAULT_SUBID_SIZE=4096 if [[ ! -f "$UID_MAP" ]]; then echo "❌ Error: $UID_MAP not found." exit 1 fi touch "$SUBID_MAP" # Return true if two ranges [start1, end1) and [start2, end2) overlap ranges_overlap() { local start1=$1 local size1=$2 local start2=$3 local size2=$4 [[ -z "$start1" || -z "$size1" || -z "$start2" || -z "$size2" ]] && return 1 [[ ! "$start1" =~ ^[0-9]+$ || ! "$size1" =~ ^[0-9]+$ || ! "$start2" =~ ^[0-9]+$ || ! "$size2" =~ ^[0-9]+$ ]] && return 1 local end1=$((start1 + size1)) local end2=$((start2 + size2)) [[ $start1 -lt $end2 && $start2 -lt $end1 ]] } # Get next available subid base get_next_subid_base() { local max=100000 grep -v '^\s*$' "$SUBID_MAP" | while IFS=: read -r _ base size; do base=$(echo "$base" | xargs) size=$(echo "$size" | xargs) [[ "$base" =~ ^[0-9]+$ && "$size" =~ ^[0-9]+$ ]] || continue local end=$((base + size)) (( end > max )) && max=$end done echo "$max" } # Warn if overlapping any prior block check_overlap() { local new_start=$1 local new_size=$2 grep -v '^\s*$' "$SUBID_MAP" | while IFS=: read -r user base size; do if ranges_overlap "$new_start" "$new_size" "$base" "$size"; then echo "⚠️ Warning: new block $new_start:$new_size overlaps with $user:$base:$size" >&2 fi done } grep -v '^\s*$' "$UID_MAP" | while IFS=: read -r service uid; do service=$(echo "$service" | xargs) uid=$(echo "$uid" | xargs) home="/var/lib/$service" [[ -z "$service" || -z "$uid" || "$service" == \#* ]] && continue echo "▶️ Processing $service (UID=$uid, HOME=$home)" # Create group if missing if ! getent group "$service" > /dev/null; then echo " ➕ Creating group $service with GID $uid" groupadd --system --gid "$uid" "$service" fi # Create or fix user if ! id "$service" &>/dev/null; then echo " ➕ Creating user $service with UID $uid and home $home" useradd --system --uid "$uid" --gid "$uid" --home-dir "$home" --create-home --shell /sbin/nologin "$service" else current_home=$(getent passwd "$service" | cut -d: -f6) if [[ "$current_home" != "$home" ]]; then echo " 🛠 Updating home dir for $service → $home" usermod -d "$home" "$service" mkdir -p "$home" chown "$service:$service" "$home" fi fi # Enable lingering if ! loginctl show-user "$uid" &>/dev/null || ! loginctl show-user "$uid" | grep -q "Linger=yes"; then echo " 🔄 Enabling lingering for $service" loginctl enable-linger "$service" fi # Subuid/subgid mapping if ! grep -q "^$service:" "$SUBID_MAP"; then next_base=$(get_next_subid_base) check_overlap "$next_base" "$DEFAULT_SUBID_SIZE" echo " 📦 Assigning subid range: $service:$next_base:$DEFAULT_SUBID_SIZE" echo "$service:$next_base:$DEFAULT_SUBID_SIZE" >> "$SUBID_MAP" fi # Sync to /etc/subuid and /etc/subgid map_line=$(grep "^$service:" "$SUBID_MAP" | head -n1) for file in /etc/subuid /etc/subgid; do if ! grep -q "^$service:" "$file"; then echo "$map_line" | sudo tee -a "$file" >/dev/null elif ! grep -q "^$map_line\$" "$file"; then echo "⚠️ Mismatch in $file for $service. Check manually!" >&2 fi done done