mirror of
https://github.com/troglobit/finit.git
synced 2026-10-13 10:14:47 +00: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:
1 parent
0a269f3298
commit
dd1390a6c2
13 files changed
+854
-980
No files matched your search
@@ -72,6 +72,11 @@ EXTRA_DIST += signal-service.sh
|
||||
EXTRA_DIST += testserv.sh
|
||||
EXTRA_DIST += unexpected-restart.sh
|
||||
EXTRA_DIST += dbus-auth.sh
|
||||
EXTRA_DIST += dbus-bus.sh
|
||||
EXTRA_DIST += dbus-manager.sh
|
||||
EXTRA_DIST += dbus-service.sh
|
||||
EXTRA_DIST += dbus-cond.sh
|
||||
EXTRA_DIST += dbus-initctl.sh
|
||||
|
||||
AM_TESTS_ENVIRONMENT = SYSROOT='$(abs_builddir)/sysroot/';
|
||||
AM_TESTS_ENVIRONMENT += export SYSROOT;
|
||||
@@ -129,6 +134,11 @@ endif
|
||||
TESTS += unexpected-restart.sh
|
||||
if DBUS
|
||||
TESTS += dbus-auth.sh
|
||||
TESTS += dbus-bus.sh
|
||||
TESTS += dbus-manager.sh
|
||||
TESTS += dbus-service.sh
|
||||
TESTS += dbus-cond.sh
|
||||
TESTS += dbus-initctl.sh
|
||||
endif
|
||||
|
||||
check-recursive: setup-chroot
|
||||
|
||||
+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
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/sh
|
||||
# libink: org.freedesktop.DBus built-in interface.
|
||||
#
|
||||
# Covers the stock D-Bus interface every conforming bus implements:
|
||||
# Hello (peer name allocation), Introspect (XML), and AddMatch's
|
||||
# error-path rule parser.
|
||||
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/setup.sh"
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/dbus-setup.sh"
|
||||
|
||||
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"
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
say "Unknown method on a Finit interface 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
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
# libink: org.finit.Cond1 vtable + ConditionChanged signal.
|
||||
#
|
||||
# Covers user-condition manipulation: Get, Set (with signal fan-out),
|
||||
# the usr/* policy guard (non-usr conditions are rejected), and the
|
||||
# non-root authorization gate.
|
||||
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/setup.sh"
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/dbus-setup.sh"
|
||||
|
||||
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
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
# initctl: confirms the legacy CLI now routes through D-Bus.
|
||||
#
|
||||
# Subscribes to ServiceStateChanged on a background monitor and then
|
||||
# runs initctl -- if D-Bus is in use, the signal fires. If the legacy
|
||||
# socket were still in use, the dbus subscriber would see nothing.
|
||||
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/setup.sh"
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/dbus-setup.sh"
|
||||
|
||||
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
|
||||
|
||||
# A ConditionChanged signal can only originate from Cond1.Set going
|
||||
# through finit (the legacy filesystem path doesn't emit signals).
|
||||
# So if the monitor sees one, we know initctl cond set was routed
|
||||
# via D-Bus.
|
||||
say "initctl cond set drives Cond1.Set via D-Bus"
|
||||
rm -f /tmp/dbus-initctl-cond.out
|
||||
( texec "$CLIENT" monitor-signal "$BUS" \
|
||||
"type='signal',interface='org.finit.Cond1',member='ConditionChanged'" \
|
||||
5000 > /tmp/dbus-initctl-cond.out 2>&1 ) &
|
||||
ic_cond_pid=$!
|
||||
sleep 0.5
|
||||
texec initctl cond set "via-initctl" >/dev/null \
|
||||
|| fail "initctl cond set returned non-zero"
|
||||
set +e
|
||||
wait "$ic_cond_pid"
|
||||
ic_cond_rc=$?
|
||||
set -e
|
||||
assert "ConditionChanged fired from initctl cond set (rc=$ic_cond_rc)" \
|
||||
"$ic_cond_rc" -eq 0
|
||||
case "$(cat /tmp/dbus-initctl-cond.out)" in
|
||||
*"SIGNAL org.finit.Cond1 ConditionChanged"*"usr/via-initctl"*on*)
|
||||
assert "initctl cond set routed through D-Bus" 0 -eq 0 ;;
|
||||
*)
|
||||
fail "initctl cond set didn't produce expected signal: $(cat /tmp/dbus-initctl-cond.out)" ;;
|
||||
esac
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
# libink: org.finit.Manager1 vtable.
|
||||
#
|
||||
# Covers the Manager1 method surface: ListServices, Reload, Stop with
|
||||
# bogus service, plus per-method authorization (Restart from non-root
|
||||
# is rejected, ListServices remains reachable as non-root).
|
||||
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/setup.sh"
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/dbus-setup.sh"
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
# Send call-s-as-uid an "s" body where the server expects "" -- 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 (a transport error would print neither
|
||||
# AccessDenied nor InvalidArgs).
|
||||
say "Manager1.ListServices is reachable as non-root (not blocked by authz)"
|
||||
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
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#!/bin/sh
|
||||
# libink: org.finit.Service1 vtable + ServiceStateChanged signal.
|
||||
#
|
||||
# Covers per-service objects exposed at /org/finit/service/<encoded>:
|
||||
# GetService lookup, Introspect on a service object, Service1.Restart,
|
||||
# authorization (non-root rejected), and the Manager1.ServiceStateChanged
|
||||
# signal that Service1.Restart triggers.
|
||||
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/setup.sh"
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/dbus-setup.sh"
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
@@ -1 +1 @@
|
||||
EXTRA_DIST = exec.sh setup.sh start.sh sysroot.mk
|
||||
EXTRA_DIST = exec.sh setup.sh start.sh sysroot.mk dbus-setup.sh
|
||||
@@ -0,0 +1,20 @@
|
||||
# shellcheck shell=sh
|
||||
# Shared preamble for the D-Bus smoke tests. Expects test/lib/setup.sh
|
||||
# to have been sourced already (so texec, skip, retry, say, assert are
|
||||
# available). Skips the test when the libink-driven client was not
|
||||
# built, otherwise blocks until the bus socket appears.
|
||||
#
|
||||
# Exports: CLIENT, BUS.
|
||||
|
||||
command -v texec >/dev/null \
|
||||
|| { echo "dbus-setup.sh: source test/lib/setup.sh first" >&2; exit 99; }
|
||||
|
||||
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"
|
||||
@@ -15,7 +15,13 @@ make -C "$top_builddir" DESTDIR="$SYSROOT" install
|
||||
|
||||
mkdir -p "$SYSROOT/sbin/"
|
||||
cp "$top_builddir/test/src/serv" "$SYSROOT/sbin/"
|
||||
if [ -x "$top_builddir/test/src/dbus-auth-client" ]; then
|
||||
# Prefer the real ELF in .libs/ over the libtool wrapper script --
|
||||
# since the test client links libink.la, libtool wraps the top-level
|
||||
# dbus-auth-client as a shell script that re-execs the real binary
|
||||
# via its own RPATH, which falls apart inside the test namespace.
|
||||
if [ -x "$top_builddir/test/src/.libs/dbus-auth-client" ]; then
|
||||
cp "$top_builddir/test/src/.libs/dbus-auth-client" "$SYSROOT/sbin/"
|
||||
elif [ -x "$top_builddir/test/src/dbus-auth-client" ]; then
|
||||
cp "$top_builddir/test/src/dbus-auth-client" "$SYSROOT/sbin/"
|
||||
fi
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ serv_LDADD = $(lite_LIBS)
|
||||
endif
|
||||
|
||||
if DBUS
|
||||
noinst_PROGRAMS += dbus-auth-client
|
||||
dbus_auth_client_SOURCES = dbus-auth-client.c
|
||||
noinst_PROGRAMS += dbus-auth-client
|
||||
dbus_auth_client_SOURCES = dbus-auth-client.c
|
||||
dbus_auth_client_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir)/libink
|
||||
dbus_auth_client_LDADD = $(top_builddir)/libink/libink.la
|
||||
endif
|
||||
+301
-695
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user