mirror of
https://github.com/troglobit/finit.git
synced 2026-10-01 05:22:48 +07:00
initctl: monitor and condition control over the bus
The bus can already answer questions and change services, so give initctl the two things it still did another way: watching signals as they happen, and getting or setting user conditions. The dbus tests move with it, split by area rather than one file that grew every time the library did. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
+7
-279
@@ -1,10 +1,9 @@
|
||||
#!/bin/sh
|
||||
# End-to-end smoke test for libink:
|
||||
# - AUTH EXTERNAL handshake (happy and wrong-uid paths)
|
||||
# - org.freedesktop.DBus.Hello
|
||||
# - org.freedesktop.DBus.Introspectable.Introspect (root, manager)
|
||||
# - org.finit.Manager1.ListServices
|
||||
# - Error reply for an unknown method.
|
||||
# libink: D-Bus AUTH EXTERNAL handshake.
|
||||
#
|
||||
# Verifies the SASL handshake itself in isolation -- everything else
|
||||
# the bus does (built-in DBus interface, vtables, signals, initctl
|
||||
# routing) lives in the other dbus-*.sh tests.
|
||||
|
||||
set -eu
|
||||
|
||||
@@ -12,23 +11,13 @@ TEST_DIR=$(dirname "$0")
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/setup.sh"
|
||||
|
||||
CLIENT=/sbin/dbus-auth-client
|
||||
BUS=/run/finit/bus
|
||||
|
||||
if ! texec test -x "$CLIENT"; then
|
||||
skip "dbus-auth-client not built (configured with --disable-dbus?)"
|
||||
fi
|
||||
|
||||
say "Wait for $BUS to appear"
|
||||
retry "texec test -S $BUS"
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/dbus-setup.sh"
|
||||
|
||||
say "Socket mode is 0666"
|
||||
mode=$(texec stat -c %a "$BUS")
|
||||
assert "Socket mode is 666 (got $mode)" "$mode" = "666"
|
||||
|
||||
# ---------- AUTH ----------
|
||||
|
||||
say "AUTH EXTERNAL: claim correct UID (root = 0)"
|
||||
reply=$(texec "$CLIENT" auth "$BUS" 0)
|
||||
assert "Reply starts with OK (got: $reply)" "${reply%% *}" = "OK"
|
||||
@@ -52,264 +41,3 @@ r2=$(texec "$CLIENT" auth "$BUS" 0)
|
||||
g1=${r1#OK }
|
||||
g2=${r2#OK }
|
||||
assert "Per-connection GUIDs differ ($g1 vs $g2)" "$g1" != "$g2"
|
||||
|
||||
# ---------- Built-in interfaces ----------
|
||||
|
||||
say "Hello() returns a unique name beginning with ':1.'"
|
||||
name=$(texec "$CLIENT" hello "$BUS")
|
||||
case "$name" in
|
||||
:1.*) assert "Hello returned a :1.N name (got $name)" 0 -eq 0 ;;
|
||||
*) fail "Hello returned unexpected name: $name" ;;
|
||||
esac
|
||||
|
||||
say "Two Hello() calls produce different unique names"
|
||||
n1=$(texec "$CLIENT" hello "$BUS")
|
||||
n2=$(texec "$CLIENT" hello "$BUS")
|
||||
assert "Unique names increment ($n1 vs $n2)" "$n1" != "$n2"
|
||||
|
||||
say "Introspect on root path returns valid XML referencing /manager"
|
||||
xml=$(texec "$CLIENT" introspect "$BUS" /)
|
||||
case "$xml" in
|
||||
*'<node'*) assert "XML has <node> root (good)" 0 -eq 0 ;;
|
||||
*) fail "Root introspect missing <node>: $xml" ;;
|
||||
esac
|
||||
|
||||
say "Introspect on /org/finit/manager exposes Manager1.ListServices"
|
||||
xml=$(texec "$CLIENT" introspect "$BUS" /org/finit/manager)
|
||||
case "$xml" in
|
||||
*'org.finit.Manager1'*'ListServices'*)
|
||||
assert "Manager1 and ListServices visible in XML" 0 -eq 0 ;;
|
||||
*)
|
||||
fail "Manager1 XML missing; got: $xml" ;;
|
||||
esac
|
||||
|
||||
# ---------- Real method call ----------
|
||||
|
||||
say "Manager1.ListServices returns the running services"
|
||||
list=$(texec "$CLIENT" liststrings "$BUS" /org/finit/manager \
|
||||
org.finit.Manager1 ListServices)
|
||||
assert "ListServices returned at least one service" \
|
||||
"$(printf '%s' "$list" | wc -l | tr -d ' ')" -ge 1
|
||||
echo "$list"
|
||||
|
||||
# ---------- Method with arguments ----------
|
||||
|
||||
say "Manager1.Reload (void) succeeds"
|
||||
texec "$CLIENT" call-void "$BUS" /org/finit/manager \
|
||||
org.finit.Manager1 Reload >/dev/null \
|
||||
|| fail "Reload returned non-zero"
|
||||
assert "Reload void method ok" 0 -eq 0
|
||||
|
||||
say "Manager1.Stop with bogus identity returns NoSuchService error"
|
||||
set +e
|
||||
texec "$CLIENT" call-s "$BUS" /org/finit/manager \
|
||||
org.finit.Manager1 Stop "no-such-service-here" >/tmp/dbus-stop.out 2>&1
|
||||
stop_rc=$?
|
||||
set -e
|
||||
assert "Bogus service rejected (rc=$stop_rc)" "$stop_rc" -eq 1
|
||||
case "$(cat /tmp/dbus-stop.out)" in
|
||||
*NoSuchService*) assert "Error is NoSuchService" 0 -eq 0 ;;
|
||||
*) fail "Unexpected error reply: $(cat /tmp/dbus-stop.out)" ;;
|
||||
esac
|
||||
|
||||
# ---------- Authorization ----------
|
||||
|
||||
say "Manager1.Restart from non-root is rejected with AccessDenied"
|
||||
set +e
|
||||
texec "$CLIENT" call-s-as-uid 1 "$BUS" /org/finit/manager \
|
||||
org.finit.Manager1 Restart "testserv" >/tmp/dbus-authz.out 2>&1
|
||||
authz_rc=$?
|
||||
set -e
|
||||
assert "Non-root Restart rejected (rc=$authz_rc)" "$authz_rc" -eq 1
|
||||
case "$(cat /tmp/dbus-authz.out)" in
|
||||
*AccessDenied*) assert "Error is AccessDenied" 0 -eq 0 ;;
|
||||
*) fail "Unexpected error: $(cat /tmp/dbus-authz.out)" ;;
|
||||
esac
|
||||
|
||||
say "Manager1.ListServices is reachable as non-root (not blocked by authz)"
|
||||
# call-s-as-uid sends an "s" body; ListServices expects "", so the
|
||||
# server must reply with org.freedesktop.DBus.Error.InvalidArgs.
|
||||
# Asserting that *positive* marker (not just "no AccessDenied")
|
||||
# ensures we don't silently pass if setuid() failed or the client
|
||||
# never reached the server (e.g. a transport error would print
|
||||
# neither AccessDenied nor InvalidArgs).
|
||||
set +e
|
||||
result=$(texec "$CLIENT" call-s-as-uid 1 "$BUS" /org/finit/manager \
|
||||
org.finit.Manager1 ListServices "" 2>&1)
|
||||
set -e
|
||||
case "$result" in
|
||||
*AccessDenied*) fail "Non-root ListServices rejected by authz: $result" ;;
|
||||
*InvalidArgs*) assert "Non-root reached signature check (InvalidArgs, not AccessDenied)" 0 -eq 0 ;;
|
||||
*) fail "Unexpected reply from non-root ListServices: $result" ;;
|
||||
esac
|
||||
|
||||
# ---------- Per-service objects (Service1) ----------
|
||||
|
||||
say "Manager1.GetService(keventd) returns the encoded object path"
|
||||
path=$(texec "$CLIENT" get-service "$BUS" keventd)
|
||||
expected="/org/finit/service/keventd"
|
||||
assert "GetService returned expected path (got: $path)" "$path" = "$expected"
|
||||
|
||||
say "Introspect on the service object exposes Service1 methods"
|
||||
xml=$(texec "$CLIENT" introspect "$BUS" /org/finit/service/keventd)
|
||||
case "$xml" in
|
||||
*'org.finit.Service1'*'Restart'*)
|
||||
assert "Service1.Restart visible in service-object XML" 0 -eq 0 ;;
|
||||
*)
|
||||
fail "Service1 not visible on /org/finit/service/keventd: $xml" ;;
|
||||
esac
|
||||
|
||||
say "Service1.Restart on /org/finit/service/keventd succeeds"
|
||||
texec "$CLIENT" call-void "$BUS" /org/finit/service/keventd \
|
||||
org.finit.Service1 Restart >/dev/null \
|
||||
|| fail "Service1.Restart returned non-zero"
|
||||
assert "Per-service Restart ok" 0 -eq 0
|
||||
|
||||
say "Service1.Restart from non-root is rejected with AccessDenied"
|
||||
set +e
|
||||
texec "$CLIENT" call-void-as-uid 1 "$BUS" /org/finit/service/keventd \
|
||||
org.finit.Service1 Restart >/tmp/dbus-svcauthz.out 2>&1
|
||||
svc_authz_rc=$?
|
||||
set -e
|
||||
assert "Non-root Service1.Restart rejected (rc=$svc_authz_rc)" \
|
||||
"$svc_authz_rc" -eq 1
|
||||
case "$(cat /tmp/dbus-svcauthz.out)" in
|
||||
*AccessDenied*) assert "Service1 authz fires" 0 -eq 0 ;;
|
||||
*) fail "Expected AccessDenied, got: $(cat /tmp/dbus-svcauthz.out)" ;;
|
||||
esac
|
||||
|
||||
# ---------- Signals ----------
|
||||
|
||||
say "Service1.Restart fires Manager1.ServiceStateChanged"
|
||||
rm -f /tmp/dbus-sig.out
|
||||
( texec "$CLIENT" monitor-signal "$BUS" \
|
||||
"type='signal',interface='org.finit.Manager1',member='ServiceStateChanged'" \
|
||||
5000 > /tmp/dbus-sig.out 2>&1 ) &
|
||||
mon_pid=$!
|
||||
sleep 0.5
|
||||
texec "$CLIENT" call-void "$BUS" /org/finit/service/keventd \
|
||||
org.finit.Service1 Restart >/dev/null \
|
||||
|| fail "Restart trigger returned non-zero"
|
||||
set +e
|
||||
wait "$mon_pid"
|
||||
mon_rc=$?
|
||||
set -e
|
||||
assert "monitor saw a signal (rc=$mon_rc)" "$mon_rc" -eq 0
|
||||
case "$(cat /tmp/dbus-sig.out)" in
|
||||
*"SIGNAL org.finit.Manager1 ServiceStateChanged"*keventd*)
|
||||
assert "Signal payload contains the keventd identity" 0 -eq 0 ;;
|
||||
*)
|
||||
fail "Unexpected signal output: $(cat /tmp/dbus-sig.out)" ;;
|
||||
esac
|
||||
|
||||
# ---------- Cond1 ----------
|
||||
|
||||
say "Cond1.Get returns 'off' for an unset condition"
|
||||
result=$(texec "$CLIENT" call-s "$BUS" /org/finit/cond \
|
||||
org.finit.Cond1 Get "no-such-cond")
|
||||
case "$result" in
|
||||
OK*) : ;; # ok, the cond reports a state, fall through
|
||||
*) fail "Cond1.Get failed: $result" ;;
|
||||
esac
|
||||
|
||||
say "Cond1.Set fires Cond1.ConditionChanged and Get reflects the change"
|
||||
rm -f /tmp/dbus-cond.out
|
||||
( texec "$CLIENT" monitor-signal "$BUS" \
|
||||
"type='signal',interface='org.finit.Cond1',member='ConditionChanged'" \
|
||||
5000 > /tmp/dbus-cond.out 2>&1 ) &
|
||||
cond_mon_pid=$!
|
||||
sleep 0.5
|
||||
texec "$CLIENT" call-s "$BUS" /org/finit/cond \
|
||||
org.finit.Cond1 Set "dbus-test-cond" >/dev/null \
|
||||
|| fail "Cond1.Set returned non-zero"
|
||||
set +e
|
||||
wait "$cond_mon_pid"
|
||||
cond_mon_rc=$?
|
||||
set -e
|
||||
assert "Cond1 monitor saw a signal (rc=$cond_mon_rc)" "$cond_mon_rc" -eq 0
|
||||
case "$(cat /tmp/dbus-cond.out)" in
|
||||
*"SIGNAL org.finit.Cond1 ConditionChanged"*"usr/dbus-test-cond"*on*)
|
||||
assert "ConditionChanged carries usr/dbus-test-cond and 'on'" 0 -eq 0 ;;
|
||||
*)
|
||||
fail "Unexpected Cond1 signal: $(cat /tmp/dbus-cond.out)" ;;
|
||||
esac
|
||||
|
||||
say "Cond1.Set/Clear on non-usr/* is rejected"
|
||||
set +e
|
||||
texec "$CLIENT" call-s "$BUS" /org/finit/cond \
|
||||
org.finit.Cond1 Set "pid/sshd" >/tmp/dbus-condrej.out 2>&1
|
||||
condrej_rc=$?
|
||||
set -e
|
||||
assert "pid/* rejected (rc=$condrej_rc)" "$condrej_rc" -eq 1
|
||||
case "$(cat /tmp/dbus-condrej.out)" in
|
||||
*InvalidArgs*) assert "Error is InvalidArgs" 0 -eq 0 ;;
|
||||
*) fail "Unexpected reply: $(cat /tmp/dbus-condrej.out)" ;;
|
||||
esac
|
||||
|
||||
say "Cond1.Set from non-root is rejected with AccessDenied"
|
||||
set +e
|
||||
texec "$CLIENT" call-s-as-uid 1 "$BUS" /org/finit/cond \
|
||||
org.finit.Cond1 Set "would-be-cond" >/tmp/dbus-condauthz.out 2>&1
|
||||
ca_rc=$?
|
||||
set -e
|
||||
assert "Non-root Cond1.Set rejected (rc=$ca_rc)" "$ca_rc" -eq 1
|
||||
case "$(cat /tmp/dbus-condauthz.out)" in
|
||||
*AccessDenied*) assert "Cond1 authz fires" 0 -eq 0 ;;
|
||||
*) fail "Unexpected reply: $(cat /tmp/dbus-condauthz.out)" ;;
|
||||
esac
|
||||
|
||||
say "AddMatch with a bogus key is rejected"
|
||||
set +e
|
||||
texec "$CLIENT" call-s "$BUS" /org/freedesktop/DBus \
|
||||
org.freedesktop.DBus AddMatch "bogus='whatever'" >/tmp/dbus-match.out 2>&1
|
||||
am_rc=$?
|
||||
set -e
|
||||
assert "Bad rule rejected (rc=$am_rc)" "$am_rc" -eq 1
|
||||
case "$(cat /tmp/dbus-match.out)" in
|
||||
*MatchRuleInvalid*) assert "Error is MatchRuleInvalid" 0 -eq 0 ;;
|
||||
*) fail "Unexpected reply: $(cat /tmp/dbus-match.out)" ;;
|
||||
esac
|
||||
|
||||
# ---------- initctl port ----------
|
||||
|
||||
# initctl now talks to /run/finit/bus when available. Verify by
|
||||
# subscribing to ServiceStateChanged on a background monitor and
|
||||
# then running initctl restart -- if D-Bus is in use, the signal
|
||||
# fires. If the legacy socket were still in use, the dbus subscriber
|
||||
# would see nothing.
|
||||
|
||||
say "initctl restart drives D-Bus (signal observed via dbus-auth-client)"
|
||||
rm -f /tmp/dbus-initctl-sig.out
|
||||
( texec "$CLIENT" monitor-signal "$BUS" \
|
||||
"type='signal',interface='org.finit.Manager1',member='ServiceStateChanged'" \
|
||||
5000 > /tmp/dbus-initctl-sig.out 2>&1 ) &
|
||||
ic_pid=$!
|
||||
sleep 0.5
|
||||
texec initctl restart keventd >/dev/null \
|
||||
|| fail "initctl restart returned non-zero"
|
||||
set +e
|
||||
wait "$ic_pid"
|
||||
ic_rc=$?
|
||||
set -e
|
||||
assert "ServiceStateChanged fired from initctl restart (rc=$ic_rc)" \
|
||||
"$ic_rc" -eq 0
|
||||
case "$(cat /tmp/dbus-initctl-sig.out)" in
|
||||
*"SIGNAL org.finit.Manager1 ServiceStateChanged"*keventd*)
|
||||
assert "initctl restart routed through D-Bus" 0 -eq 0 ;;
|
||||
*)
|
||||
fail "initctl restart didn't produce expected signal: $(cat /tmp/dbus-initctl-sig.out)" ;;
|
||||
esac
|
||||
|
||||
say "initctl reload (no args) routes through Manager1.Reload"
|
||||
texec initctl reload >/dev/null \
|
||||
|| fail "initctl reload returned non-zero"
|
||||
assert "initctl reload ok" 0 -eq 0
|
||||
|
||||
# ---------- Error reply ----------
|
||||
|
||||
say "Unknown method gets an org.freedesktop.DBus.Error.* reply"
|
||||
set +e
|
||||
texec "$CLIENT" unknown "$BUS"
|
||||
unknown_rc=$?
|
||||
set -e
|
||||
assert "Unknown method returned an error (rc=$unknown_rc)" "$unknown_rc" -eq 0
|
||||
|
||||
Reference in New Issue
Block a user