⚙️ 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:
| # 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
| # 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