Przejdź do treści

⚙️ OpenBSD RC and Daemons

OpenBSD's rc system provides a robust framework for managing system services and daemons. Understanding this system is crucial for effective OpenBSD administration.


🎯 OpenBSD RC System Overview

RC System Architecture

OpenBSD uses a traditional rc system with clear separation between system and local services:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# RC system directories
/etc/rc.conf          # System configuration
/etc/rc.conf.local    # Local overrides
/etc/rc.d/            # System service scripts
/usr/local/etc/rc.d/  # Third-party service scripts

# Boot process
/boot                 # Boot loader
/etc/rc               # Main rc script
/etc/rc.local         # Local customization
/etc/rc.shutdown      # Shutdown script

Service Script Structure

Each rc.d script follows a standard structure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/ksh
#
# PROVIDE: service_name
# REQUIRE: LOGIN
# BEFORE: DAEMON
# KEYWORD: shutdown

. /etc/rc.d/rc.subr

pexp=".*service_binary.*"  # Process expression
rc_reload=NO               # Disable reload by default

rc_cmd $1

🔧 Service Management Commands

Basic Service Operations

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# List all services
rcctl ls all

# List running services
rcctl ls started

# List enabled services
rcctl ls on

# Check specific service status
rcctl get sshd status

# Start service
rcctl start sshd

# Stop service
rcctl stop sshd

# Restart service
rcctl restart sshd

# Reload service configuration
rcctl reload sshd

Service Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Enable service at boot
rcctl enable sshd

# Disable service at boot
rcctl disable sshd

# Check if service is enabled
rcctl get sshd status

# Set service flags
rcctl set sshd flags -4

# Get service flags
rcctl get sshd flags

# Set service timeout
rcctl set sshd timeout 30

# Configure service user
rcctl set httpd user www

📋 Service Script Development

Basic Service Script Template

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/ksh
#
# PROVIDE: myservice
# REQUIRE: DAEMON
# BEFORE: LOGIN
# KEYWORD: shutdown

. /etc/rc.d/rc.subr

name="myservice"
rc_reload=NO

# Process name for ps matching
pexp="/usr/local/bin/myservice.*"

# Default flags
: ${myservice_user:="_myservice"}
: ${myservice_flags:=""}

# Pre-start checks
myservice_precmd() {
    # Create required directories
    install -d -o ${myservice_user} /var/run/myservice

    # Check configuration
    if [ ! -f /etc/myservice.conf ]; then
        echo "Configuration file missing: /etc/myservice.conf"
        return 1
    fi
}

# Command to start service
myservice_cmd() {
    su -s /bin/sh -c "/usr/local/bin/myservice ${myservice_flags}" \
        ${myservice_user}
}

# Use rc.subr framework
rc_cmd $1

Advanced Service Script Features

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/bin/ksh
#
# PROVIDE: advanced_service
# REQUIRE: NETWORKING
# BEFORE: DAEMON
# KEYWORD: shutdown

. /etc/rc.d/rc.subr

name="advanced_service"
rc_bg=YES              # Run in background
rc_reload=YES          # Enable reload
rc_usercheck=YES       # Check user exists

# Process matching
pexp="/usr/local/bin/advanced_service.*"

# Configuration
: ${advanced_service_user:="_advsvc"}
: ${advanced_service_group:="_advsvc"}
: ${advanced_service_flags:="--config /etc/advanced.conf"}

# Pre-start validation
advanced_service_precmd() {
    # Validate user and group
    if ! id ${advanced_service_user} >/dev/null 2>&1; then
        echo "User ${advanced_service_user} does not exist"
        return 1
    fi

    # Check required files
    for file in /etc/advanced.conf /var/db/advanced.db; do
        if [ ! -f "$file" ]; then
            echo "Required file missing: $file"
            return 1
        fi
    done

    # Create runtime directories
    install -d -o ${advanced_service_user} -g ${advanced_service_group} \
        /var/run/advanced
}

# Custom reload handler
advanced_service_reload() {
    echo "Reloading advanced_service configuration"
    pkill -HUP -xf "${pexp}"
}

# Custom status check
advanced_service_status() {
    if pgrep -qxf "${pexp}"; then
        echo "advanced_service is running"
        return 0
    else
        echo "advanced_service is not running"
        return 1
    fi
}

rc_cmd $1

🌐 Network Services Configuration

Web Server Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# HTTPD service management
rcctl enable httpd
rcctl set httpd flags -f /etc/httpd.conf
rcctl start httpd

# Check HTTPD status
rcctl get httpd status
rcctl get httpd flags

# Configure HTTPD user
rcctl set httpd user www
rcctl set httpd group www

SSH Service Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# SSHD service management
rcctl enable sshd
rcctl set sshd flags -4  # IPv4 only
rcctl start sshd

# Configure SSHD
rcctl set sshd timeout 60
rcctl set sshd user _sshd

# Check SSHD configuration
rcctl get sshd

Custom Network Service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Custom network service script
cat > /etc/rc.d/mynetwork << 'EOF'
#!/bin/ksh
#
# PROVIDE: mynetwork
# REQUIRE: NETWORKING
# BEFORE: DAEMON

. /etc/rc.d/rc.subr

name="mynetwork"
rc_reload=YES

pexp="/usr/local/bin/mynetwork.*"
: ${mynetwork_flags:="--port 8080"}

mynetwork_precmd() {
    # Check network interface
    if ! ifconfig em0 >/dev/null 2>&1; then
        echo "Network interface em0 not available"
        return 1
    fi
}

mynetwork_cmd() {
    /usr/local/bin/mynetwork ${mynetwork_flags}
}

rc_cmd $1
EOF

chmod 755 /etc/rc.d/mynetwork
rcctl enable mynetwork
rcctl start mynetwork

🛠️ System Integration

Dependency Management

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Service dependencies in rc script
#
# PROVIDE: myservice
# REQUIRE: NETWORKING DAEMON
# BEFORE: LOGIN
# KEYWORD: shutdown

# REQUIRE: Specifies services that must start first
# BEFORE: Specifies services that should start after this
# PROVIDE: Name of this service

# Example with complex dependencies
#
# PROVIDE: database_proxy
# REQUIRE: NETWORKING postgresql
# BEFORE: DAEMON LOGIN

Local Service Scripts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Create local service in /usr/local/etc/rc.d/
cat > /usr/local/etc/rc.d/myapp << 'EOF'
#!/bin/ksh
#
# PROVIDE: myapp
# REQUIRE: DAEMON
# BEFORE: LOGIN

. /etc/rc.d/rc.subr

name="myapp"
rc_bg=YES

pexp="/usr/local/bin/myapp.*"
: ${myapp_user:="_myapp"}
: ${myapp_flags:="--daemon"}

myapp_precmd() {
    install -d -o ${myapp_user} /var/run/myapp
    install -d -o ${myapp_user} /var/log/myapp
}

myapp_cmd() {
    su -s /bin/sh -c "/usr/local/bin/myapp ${myapp_flags}" ${myapp_user}
}

rc_cmd $1
EOF

chmod 755 /usr/local/etc/rc.d/myapp
rcctl enable myapp

🎨 Advanced RC Features

Conditional Service Startup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Service with conditional startup
cat > /etc/rc.d/conditional_svc << 'EOF'
#!/bin/ksh
#
# PROVIDE: conditional_svc
# REQUIRE: DAEMON

. /etc/rc.d/rc.subr

name="conditional_svc"
: ${conditional_svc_flags:=""}

# Only start if specific condition is met
conditional_svc_prestart() {
    # Check if required hardware is present
    if [ ! -c /dev/special_device ]; then
        echo "Special device not present, skipping service"
        return 1
    fi

    # Check system resources
    if [ $(sysctl -n hw.physmem) -lt 1073741824 ]; then
        echo "Insufficient memory for service"
        return 1
    fi
}

conditional_svc_cmd() {
    /usr/local/bin/conditional_svc ${conditional_svc_flags}
}

rc_cmd $1
EOF

Service Monitoring and Restart

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# Enhanced service with monitoring
cat > /etc/rc.d/monitored_svc << 'EOF'
#!/bin/ksh
#
# PROVIDE: monitored_svc
# REQUIRE: DAEMON

. /etc/rc.d/rc.subr

name="monitored_svc"
rc_bg=YES
rc_reload=YES

pexp="/usr/local/bin/monitored_svc.*"
: ${monitored_svc_user:="_monsvc"}

monitored_svc_check() {
    # Custom health check
    if ! /usr/local/bin/monitored_svc --health-check >/dev/null 2>&1; then
        echo "Service health check failed"
        return 1
    fi
}

monitored_svc_cmd() {
    su -s /bin/sh -c "/usr/local/bin/monitored_svc" ${monitored_svc_user}
}

# Override status to include health check
monitored_svc_status() {
    if pgrep -qxf "${pexp}"; then
        if monitored_svc_check; then
            echo "monitored_svc is running and healthy"
            return 0
        else
            echo "monitored_svc is running but unhealthy"
            return 1
        fi
    else
        echo "monitored_svc is not running"
        return 1
    fi
}

rc_cmd $1
EOF

🔍 Troubleshooting and Debugging

Service Debugging

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Debug service startup
rcctl debug on
rcctl start myservice
rcctl debug off

# Check service logs
tail -f /var/log/messages | grep myservice

# Manual service execution for testing
/usr/local/bin/myservice --debug

# Check process information
ps auxww | grep myservice
pargs $(pgrep myservice)

Common Issues and Solutions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Service won't start
# 1. Check permissions
ls -l /etc/rc.d/myservice
# 2. Check syntax
/etc/rc.d/myservice check

# Service starts but fails quickly
# 1. Check logs
tail /var/log/messages
# 2. Run manually with debug
/usr/local/bin/myservice --debug

# Dependency issues
# 1. Check REQUIRE statements
# 2. Verify dependent services
rcctl status dependent_service

Service Log Management

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Configure service logging
# In service script:
myservice_precmd() {
    # Redirect logs appropriately
    install -d -o ${myservice_user} /var/log/myservice
}

# In configuration file:
# logfile=/var/log/myservice/service.log
# loglevel=info

# Log rotation
cat > /etc/newsyslog.conf.d/myservice.conf << EOF
/var/log/myservice/service.log _myservice:_myservice 644 7 * @T00 J
EOF

🧾 Summary Quick Reference

RCCTL Commands

Command Description
rcctl ls all List all services
rcctl ls started List running services
rcctl start service Start service
rcctl stop service Stop service
rcctl restart service Restart service
rcctl enable service Enable at boot
rcctl disable service Disable at boot
rcctl get service status Check service status
rcctl set service flags value Set service flags

Service Script Keywords

Keyword Purpose
PROVIDE: Name of this service
REQUIRE: Services required before this
BEFORE: Services that should start after this
KEYWORD: Special keywords (shutdown, nostop)

🧠 Best Practices

Service Development Guidelines

Security First: - Run services as dedicated users - Use proper file permissions - Validate input and configuration - Implement proper logging

Robustness: - Include pre-start validation - Handle errors gracefully - Provide meaningful status information - Implement proper cleanup

Integration: - Follow rc.d script conventions - Specify correct dependencies - Use standard rc.subr framework - Respect system configuration

Common Mistakes to Avoid: - Running services as root unnecessarily - Missing dependency declarations - Inadequate error handling - Hard-coded paths - Ignoring system conventions

Production Deployment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Production service checklist:
# 1. Test service script thoroughly
# 2. Verify user and group creation
# 3. Check file permissions
# 4. Validate configuration files
# 5. Test startup and shutdown
# 6. Implement log rotation
# 7. Monitor service health
# 8. Document service requirements

# Enable service for production
rcctl enable production_service
rcctl set production_service flags --production
rcctl start production_service

# Verify service is running
rcctl get production_service status

🧾 See Also