Skip to main content
Topic: Convert from dinit to openrc bash script (Read 913 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Convert from dinit to openrc bash script

I needed to convert a basestrapped dinit install; on a headless server with multiple raid1 partitions (hence the conversion vs reinstall), to openrc(Why, you ask? Cause k3s install script supports openrc, out da box!). 
I started following the wiki article on converting the init system, but ran into some issues with updated package naming conventions for openrc.
Code: [Select]
$ pacman -Sw $(sed 's/openrc/s6/g' < services.list)
error: target not found: openrc-init
[ERROR] Failed to download core OpenRC packages
Turns out OpenRC itself provides the init functionality - beginning with OpenRC 0.25, it replaced /sbin/init with its own program. In Artix, the core OpenRC package is simply called openrc.

So I headed over to claude4(Why on earth!?!, While I am an accomplished sysadmin, frontend sass, css, html, advanced drupal ui dude, I'm new to coding, well... other than by the seat of my pant's, skin of my teeth sutuations where I have to hack php to unbreakerate things.) Anyway, I passed on what I tried and, after overcoming a few issues, ended up with the following script.
 
Disclaimer:
Use at your own risk. Could break stuff. If you choose to use this weapon on your quest, it's on you. ;)

Code: [Select]
#!/bin/bash
# Artix Linux: Dinit to OpenRC Migration Script
# Run this from a live CD environment with the target system chrooted

set -e  # Exit on any error

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Logging function
log() {
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1" >&2
}

warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

# Check if we're in a chroot environment
check_chroot() {
    if [[ "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/.)" ]]; then
        log "Running in chroot environment ✓"
    else
        error "This script should be run from a chroot environment (live CD)"
        exit 1
    fi
}

# Check current init system status
check_init_system() {
    local has_dinit=$(pacman -Qs dinit 2>/dev/null | wc -l)
    local has_openrc=$(pacman -Qs openrc 2>/dev/null | wc -l)
   
    if [[ $has_dinit -gt 0 && $has_openrc -eq 0 ]]; then
        log "Dinit system detected, proceeding with migration ✓"
        return 0
    elif [[ $has_dinit -eq 0 && $has_openrc -gt 0 ]]; then
        log "OpenRC system detected, checking migration status..."
        check_migration_success
        return 1
    elif [[ $has_dinit -gt 0 && $has_openrc -gt 0 ]]; then
        warning "Both dinit and OpenRC packages found - partial migration detected"
        log "Continuing with migration cleanup..."
        return 0
    else
        error "No recognized init system found (neither dinit nor OpenRC)"
        exit 1
    fi
}

# Check if migration was successful
check_migration_success() {
    log "Checking OpenRC migration status..."
   
    # Check if OpenRC is installed
    if ! pacman -Qs openrc > /dev/null 2>&1; then
        error "OpenRC not found - migration incomplete"
        return 1
    fi
   
    # Check if dinit packages are removed
    local remaining_dinit=$(pacman -Qs dinit 2>/dev/null | wc -l)
    if [[ $remaining_dinit -gt 0 ]]; then
        warning "Some dinit packages still present:"
        pacman -Qs dinit
        log "You may need to remove these manually"
    else
        success "All dinit packages removed ✓"
    fi
   
    # Check if essential OpenRC services are configured
    local configured_services=0
    local essential_services=("hostname" "networking" "hwclock" "modules" "localmount")
   
    for service in "${essential_services[@]}"; do
        if rc-update show | grep -q "$service"; then
            ((configured_services++))
        fi
    done
   
    if [[ $configured_services -gt 0 ]]; then
        success "OpenRC services configured ($configured_services essential services found) ✓"
    else
        warning "No essential OpenRC services appear to be configured"
    fi
   
    # Check if OpenRC can manage services
    if command -v rc-service >/dev/null 2>&1; then
        success "OpenRC service management available ✓"
    else
        error "OpenRC service management not available"
        return 1
    fi
   
    # Overall status
    if [[ $remaining_dinit -eq 0 && $configured_services -gt 0 ]]; then
        success "Migration appears successful! ✓"
        log "System should boot with OpenRC after reboot"
        return 0
    else
        warning "Migration may be incomplete - manual intervention might be needed"
        return 1
    fi
}

# Backup current dinit services
backup_services() {
    log "Backing up current dinit services..."
   
    # Get dinit packages
    pacman -Qsq dinit > services.list 2>/dev/null || {
        error "Failed to query dinit packages"
        exit 1
    }
   
    # Also backup enabled services if possible
    if [ -d /etc/dinit.d ]; then
        find /etc/dinit.d -name "*.d" -type d > enabled_services.list 2>/dev/null || true
    fi
   
    local service_count=$(wc -l < services.list)
    success "Backed up $service_count dinit packages to services.list"
}

# Download OpenRC packages
download_openrc_packages() {
    log "Downloading OpenRC packages..."
   
    # Convert dinit package names to openrc equivalents
    # This sed command replaces 'dinit' with 'openrc' in package names
    local openrc_packages=$(sed 's/dinit/openrc/g' < services.list)
   
    # Create a temporary file to check which packages exist
    echo "$openrc_packages" > openrc_packages.list
   
    # Download essential OpenRC packages first
    local essential_packages="openrc elogind-openrc"
    log "Downloading essential OpenRC packages: $essential_packages"
    if pacman -Sw --noconfirm $essential_packages; then
        success "Downloaded essential OpenRC packages"
    else
        error "Failed to download essential OpenRC packages"
        exit 1
    fi
   
    # Download packages (this doesn't install them)
    log "Attempting to download OpenRC service packages..."
    local failed_packages=""
    while IFS= read -r package; do
        if [[ -n "$package" && "$package" != "openrc" ]]; then
            if pacman -Sw --noconfirm "$package" 2>/dev/null; then
                log "Downloaded: $package"
            else
                failed_packages="$failed_packages $package"
                warning "Could not download: $package"
            fi
        fi
    done < openrc_packages.list
   
    if [[ -n "$failed_packages" ]]; then
        warning "Some packages couldn't be downloaded:$failed_packages"
        log "These may not exist or have different names in OpenRC"
    fi
}

# Remove dinit packages
remove_dinit() {
    log "Removing dinit packages..."
   
    # Use -Rdd to remove without checking dependencies
    # This is necessary because we're replacing the init system
    if pacman -Rdd --noconfirm $(cat services.list); then
        success "Successfully removed dinit packages"
    else
        error "Failed to remove dinit packages"
        exit 1
    fi
}

# Install OpenRC
install_openrc() {
    log "Installing OpenRC..."
   
    # Install core OpenRC and elogind (session management)
    local core_packages="openrc elogind-openrc"
    if pacman -S --noconfirm $core_packages; then
        success "Installed core OpenRC packages: $core_packages"
    else
        error "Failed to install core OpenRC packages"
        exit 1
    fi
   
    # Try to install service equivalents
    log "Installing OpenRC service packages..."
    local openrc_packages=$(sed 's/dinit/openrc/g' < services.list)
   
    # Install packages one by one, skipping failures
    local installed_count=0
    local failed_count=0
    while IFS= read -r package; do
        if [[ -n "$package" && "$package" != "openrc" && "$package" != "elogind-openrc" ]]; then
            if pacman -S --noconfirm "$package" 2>/dev/null; then
                log "Installed: $package"
                ((installed_count++))
            else
                warning "Could not install: $package (may not exist or have different name)"
                ((failed_count++))
            fi
        fi
    done < openrc_packages.list
   
    success "Installed $installed_count OpenRC service packages ($failed_count failed)"
}

# Configure OpenRC services
configure_services() {
    log "Configuring OpenRC services..."
   
    # Suppress OpenRC help output by redirecting stderr
    export RC_QUIET=1
   
    # Enable essential services for boot runlevel
    local boot_services=(
        "hwclock"
        "modules"
        "fsck"
        "root"
        "localmount"
        "swap"
        "urandom"
    )
   
    for service in "${boot_services[@]}"; do
        if [ -f "/etc/init.d/$service" ]; then
            if rc-update add "$service" boot >/dev/null 2>&1; then
                log "Enabled boot service: $service"
            else
                warning "Could not enable boot service: $service"
            fi
        fi
    done
   
    # Enable essential services for default runlevel
    local default_services=(
        "hostname"
        "networking"
        "elogind"
        "local"
        "netmount"
    )
   
    for service in "${default_services[@]}"; do
        if [ -f "/etc/init.d/$service" ]; then
            if rc-update add "$service" default >/dev/null 2>&1; then
                log "Enabled default service: $service"
            else
                warning "Could not enable default service: $service"
            fi
        fi
    done
   
    # Add localmount to default runlevel as required by OpenRC 0.62.2
    if [ -f "/etc/init.d/localmount" ]; then
        rc-update add localmount default >/dev/null 2>&1 || true
    fi
   
    success "OpenRC services configured"
   
    # Show runlevel status (suppress help output)
    log "Current runlevel configuration:"
    rc-update show 2>/dev/null || log "Could not display runlevel status"
}

# Update bootloader if needed
update_bootloader() {
    log "Checking bootloader configuration..."
   
    # Check if using GRUB
    if [ -f "/boot/grub/grub.cfg" ]; then
        log "GRUB detected, updating configuration..."
        if command -v grub-mkconfig >/dev/null 2>&1; then
            grub-mkconfig -o /boot/grub/grub.cfg
            success "Updated GRUB configuration"
        else
            warning "grub-mkconfig not found, you may need to update bootloader manually"
        fi
    fi
   
    # Check for other bootloaders
    if [ -f "/boot/loader/loader.conf" ]; then
        warning "systemd-boot detected - you may need to update boot entries manually"
    fi
}

# Prepare for safe reboot
prepare_reboot() {
    log "Preparing for safe reboot..."
   
    # Sync filesystem
    log "Syncing filesystems..."
    sync
   
    # Remount root as read-only
    log "Remounting root filesystem as read-only..."
    mount / -o remount,ro 2>/dev/null || {
        warning "Could not remount root as read-only"
    }
   
    success "System prepared for reboot"
   
    echo
    echo "=========================================="
    echo "Migration completed successfully!"
    echo "=========================================="
    echo
    echo "Next steps:"
    echo "1. Exit the chroot environment"
    echo "2. Reboot the system (cold reboot required)"
    echo "3. The system should boot with OpenRC"
    echo
    echo "If you need to force a cold reboot, use:"
    echo "  echo s > /proc/sysrq-trigger"
    echo "  echo u > /proc/sysrq-trigger"
    echo "  echo b > /proc/sysrq-trigger"
    echo
}

# Main execution
main() {
    log "Starting Dinit to OpenRC migration check..."
   
    # Pre-flight checks
    check_chroot
   
    # Check what init system we have and what to do
    if check_init_system; then
        log "Migration needed - proceeding with dinit to OpenRC migration"
       
        # Create backup directory
        mkdir -p /root/dinit-migration-backup
        cd /root/dinit-migration-backup
       
        # Execute migration steps
        backup_services
        download_openrc_packages
        remove_dinit
        install_openrc
        configure_services
        update_bootloader
        prepare_reboot
       
        success "Migration script completed successfully!"
    else
        log "Migration check completed"
        exit 0
    fi
}

# Run main function
main "$@"

The script follows this decision tree:

- Check chroot environment (safety)
    - Detect init system state:
    - Dinit present, OpenRC absent → Run migration
    - OpenRC present, dinit absent → Check migration success
    - Both present → Continue migration (cleanup partial state)
    - Neither present → Error exit


- For migration success check:
    - Count remaining dinit packages
    - Verify OpenRC service configuration
    - Test OpenRC command availability
    - Report overall status

I hope someone else can benefit from this. I did, and I learned a lot!