invoke-rc.d decides whether starting, stopping or restarting a service actually happens, and misreading its exit code can bite you. This guide walks through a safe workflow for checking a System V service action, reading the result, and running an approved action only when you understand its policy boundary. The examples use invoke-rc.d from Debian's init-system-helpers package, version 1.66ubuntu1 on this machine.
Allow about fifteen minutes. You need a shell and an installed init script under /etc/init.d/. Read-only checks normally need no elevated privilege. Starting, stopping or restarting a system service usually does, so use sudo only for the final state-changing command. This guide does not edit init scripts, runlevel links or policy-rc.d.
invoke-rc.d takes an init-script basename, an action, and optional parameters passed to the script. Check the installed command before copying an example:
$ command -v invoke-rc.d
/usr/sbin/invoke-rc.d
$ dpkg-query -W -f='${Package} ${Version}\n' init-system-helpers
init-system-helpers 1.66ubuntu1
$ invoke-rc.d --help
invoke-rc.d, Debian/SysVinit (/etc/rc?.d) initscript subsystem.
Use the basename, not a unit name with a .service suffix. For example, the local script is /etc/init.d/cron, so its basename is cron:
$ test -x /etc/init.d/cron && echo 'cron init script is executable'
cron init script is executable
Checkpoint: replace cron with the exact basename from your host. Do not guess a name from a package name, and do not use a made-up service as a test.
Use --query for a dry run. It does not execute the init script, and it automatically enables --disclose-deny and --no-fallback:
$ invoke-rc.d --query --disclose-deny cron start
$ printf 'invoke-rc.d status: %s\n' "$?"
invoke-rc.d status: 104
Status 104 means the action is allowed, but was not run because query mode is active. This is the useful pre-flight result: you have checked the policy decision without starting or restarting anything.
Other query results are deliberately not ordinary success or failure messages:
100 means the init-script ID is unknown.101 means policy or the runlevel disallows the requested action.102 means a subsystem error.103 means a syntax error.105 means the policy layer cannot determine the behaviour.106 means the policy layer supplied a fallback action.Do not treat every non-zero query status as permission to try the real command. Investigate the particular result first.
The status action is normally read-only from the service's point of view, but it still passes through policy and the init-script subsystem. Run it without sudo first:
$ invoke-rc.d cron status
... cron.service - Regular background program processing daemon
... Active: active (running) ...
$ printf 'invoke-rc.d status: %s\n' "$?"
invoke-rc.d status: 0
The exact service-manager output changes with the host and current uptime. The useful checks are that the command returned 0 and that the service reports the state you expected. A service's status output is not a promise that every action is available; individual init scripts may omit actions listed in the general interface.
For a missing or unsuitable script, capture the result rather than immediately retrying with elevated privileges:
$ invoke-rc.d definitely-not-an-init-script status
Unit definitely-not-an-init-script.service could not be found.
invoke-rc.d: initscript definitely-not-an-init-script, action "status" failed.
$ printf 'invoke-rc.d status: %s\n' "$?"
invoke-rc.d status: 4
That is an error from the attempted status action, not evidence that sudo would create the service. Check the basename and installed package instead.
Starting, stopping and restarting can disrupt users or dependent processes. Before using one, check the exact action in query mode and arrange a recovery path. For a restart, that usually means knowing the service's configuration file, logs and normal start command, and having a maintenance window.
$ invoke-rc.d --query cron restart
$ printf 'query status: %s\n' "$?"
query status: 104
Only if that result matches your intention should you run the real action, normally with elevated privileges:
$ sudo invoke-rc.d cron restart
$ printf 'restart status: %s\n' "$?"
restart status: 0
Status 0 means the init script returned success. It can also mean a policy decision prevented the action when --disclose-deny was not used, or that a fallback action succeeded. If the exact policy outcome matters, include --disclose-deny on a non-query invocation and record the output.
There is no universal undo for a restart. To recover from an unsuccessful or unwanted change, inspect the service status and logs, then run the service's documented opposite action or restore the known-good configuration. Do not use --force as a recovery shortcut.
The policy layer is usually implemented by /usr/sbin/policy-rc.d, if that helper exists. It can allow an action, deny it, or request a fallback such as stopping when a restart is not allowed. invoke-rc.d also applies runlevel constraints itself. This is why a package maintainer script should use this interface rather than calling /etc/init.d/name directly.
--no-fallback suppresses fallback requests. The manual warns that this is usually a bad idea for actions other than start; leave it out unless you have a specific reason and have checked the policy contract.
--force tries to run the init script despite policy and non-fatal subsystem errors. It can bypass a deliberate protection, including a chroot or package-upgrade safeguard. Treat it as a security-sensitive and service-disrupting override, not as the normal fix for a denied action. The manual severely discourages it in package maintainer scripts.
On a system using native systemd units, --skip-systemd-native can make the command exit before doing anything when the requested service is native. This is intended for package scripts that defer native systemd work to deb-systemd-invoke; it is not a general replacement for understanding which service manager owns the unit.
If a script needs a decision without changing service state, keep the query and its status together:
if invoke-rc.d --query --disclose-deny "$SERVICE_NAME" "$ACTION"; then
printf '%s\n' 'unexpected ordinary success from query mode' >&2
exit 1
fi
status=$?
case "$status" in
104) printf '%s\n' 'action would be allowed' ;;
101) printf '%s\n' 'action denied by policy or runlevel' ;;
100) printf '%s\n' 'unknown init-script ID' >&2; exit 1 ;;
*) printf 'invoke-rc.d query returned %s\n' "$status" >&2; exit 1 ;;
esac
In real shell code, capture the status immediately after the command. The compact example above illustrates the statuses, but its first branch intentionally rejects a zero result because query mode should return one of the documented 100 to 106 values. Keep SERVICE_NAME and ACTION controlled values, not unchecked user input.
--query before any start, stop or restart.sudo only for the approved state change.--force or --no-fallback without a documented reason.