When implementing system-wide startup/shutdown scripts that execute user-specific scripts from their home directories (~/.startUp and ~/.shutDown), we face two technical challenges:
- Privilege escalation to execute as target users
- Process detachment to avoid lingering parent processes
The first approach using sudo creates unwanted root-owned parent processes:
sudo -b -u username /home/username/.startUp/watchdog.sh
This leaves permanent sudo processes in the process tree, which isn't ideal for system monitoring.
We discovered two working solutions that properly detach child processes:
sudo with Background Execution
sudo -u username bash -c "/home/username/.startUp/script.sh &"
su Alternative
su username -c "/home/username/.startUp/script.sh &"
Method | Requires sudoers config | Password prompt | Environment |
---|---|---|---|
sudo | Yes | Configurable | More controlled |
su | No | Possible | Closer to user login |
Here's a production-ready example for /etc/init.d/user-startup:
#!/bin/bash
# chkconfig: 2345 99 01
# description: User-specific startup scripts
case "$1" in
start)
for user in /home/*; do
if [ -d "$user/.startUp" ]; then
for script in "$user/.startUp/"*; do
if [ -x "$script" ]; then
sudo -u ${user##*/} bash -c "$script &" >/dev/null 2>&1
fi
done
fi
done
;;
stop)
for user in /home/*; do
if [ -d "$user/.shutDown" ]; then
for script in "$user/.shutDown/"*; do
if [ -x "$script" ]; then
sudo -u ${user##*/} bash -c "$script &" >/dev/null 2>&1
fi
done
fi
done
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
- Ensure ~/.startUp and ~/.shutDown directories have 700 permissions
- Regularly audit user scripts for malicious content
- Consider setting up resource limits via /etc/security/limits.conf
- Implement logging for troubleshooting
For modern systems, consider using systemd's per-user manager:
# Enable lingering to allow user services to run without active login
loginctl enable-linger username
# User service example (~/.config/systemd/user/myservice.service)
[Unit]
Description=My User Service
[Service]
ExecStart=/home/username/.startUp/myscript.sh
[Install]
WantedBy=default.target
When launching user scripts via sudo -u
from init.d scripts, many administrators encounter the persistent root-owned sudo processes that remain as parents to the user processes. Here's why this happens and how to properly detach them:
# Problematic approach (leaves sudo parent)
sudo -u username /home/username/watchdog.sh
For modern Linux systems, these are the most reliable methods to launch user scripts without leaving parent processes:
# Method 1: Using sudo with shell detachment
sudo -u username bash -c "nohup /home/username/script.sh &>/dev/null &"
# Method 2: Using su with full process detachment
su username -s /bin/bash -c "exec /home/username/daemon.sh >/dev/null 2>&1 &"
# Method 3: Systemd-friendly approach (for newer systems)
sudo -u username systemd-run --scope --user /home/username/service.sh
The key difference between these methods lies in how they handle process relationships:
$ pstree -p | grep -A5 "watchdog"
# Bad output:
init(1)───sudo(1234)───watchdog(1235)
# Good output:
init(1)───watchdog(1235)
For system-wide implementation, consider these architectural approaches:
#!/bin/bash
### /etc/init.d/user-scripts
case "$1" in
start)
for user in /home/*; do
username=$(basename $user)
[ -f "$user/.startUp/run" ] && \
su "$username" -c "nohup \"$user/.startUp/run\" &>/dev/null &"
done
;;
stop)
# Similar implementation for shutdown scripts
;;
esac
When implementing this solution:
- Always validate script paths to prevent directory traversal
- Consider resource limits with
ulimit
- Log the execution results to
/var/log/user-scripts.log
For systems using systemd, consider implementing per-user systemd units:
# /etc/systemd/system/user@.service
[Unit]
Description=User startup scripts for %i
[Service]
Type=oneshot
ExecStart=/usr/local/bin/run-user-startup %i
User=%i
Group=%i