Add get_unit_state to read fresh systemd unit properties
Some checks failed
Tests / prove (push) Has been cancelled
Package and upload artifacts / build (push) Has been cancelled

This commit is contained in:
Ilia Ross
2026-09-30 00:05:56 +02:00
parent 9458057978
commit e70bf0ffaa
2 changed files with 51 additions and 0 deletions

View File

@@ -662,6 +662,33 @@ my $out = backquote_logged(
return (!$?, $out);
}
=head2 get_unit_state(name)
Returns a fresh hash of LoadState, UnitFileState, ActiveState, SubState and
MainPID, followed by an error string. Missing units have LoadState not-found;
command or validation failures return undef and an error. No inventory cache
is used, so callers can verify a change immediately.
=cut
sub get_unit_state
{
my ($name) = @_;
return (undef, $text{'systemd_ename'}) if (!valid_unit_name($name));
my $out = backquote_logged("systemctl show --no-pager ".
"--property=LoadState,UnitFileState,ActiveState,SubState,MainPID ".
quotemeta($name)." 2>&1 </dev/null");
my $rv = $?;
my %state;
foreach my $line (split(/\r?\n/, $out)) {
$state{$1} = $2
if ($line =~ /^(LoadState|UnitFileState|ActiveState|SubState|MainPID)=(.*)$/);
}
# systemctl may return a nonzero status for an explicitly missing unit.
return (undef, $out || "Failed to read systemd unit $name")
if (!$state{'LoadState'} || ($rv && $state{'LoadState'} ne 'not-found'));
return (\%state, undef);
}
=head2 dependencies_unit(name)
Gets dependency tree output for a systemd unit.

View File

@@ -855,6 +855,30 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
like($out, qr/Start failed/, 'failed start includes diagnostics');
}
# Readiness needs fresh properties, including idle sockets and missing units.
{
my $reply = "LoadState=loaded\nUnitFileState=enabled\nActiveState=active\nSubState=listening\nMainPID=0\n";
my $status = 0;
my @commands;
local *main::backquote_logged = sub { push @commands, $_[0]; $? = $status; return $reply; };
my ($state, $error) = get_unit_state('demo.socket');
is($error, undef, 'valid unit state has no error');
is($state->{SubState}, 'listening', 'socket readiness is exposed');
like($commands[-1], qr/--property=LoadState,UnitFileState,ActiveState,SubState,MainPID/, 'only requested properties are read');
$reply = "LoadState=loaded\nUnitFileState=enabled\nActiveState=inactive\n";
($state, $error) = get_unit_state('demo.socket');
is($state->{ActiveState}, 'inactive', 'state is not cached');
$reply = "LoadState=not-found\nActiveState=inactive\n"; $status = 1024;
($state, $error) = get_unit_state('missing.service');
is($state->{LoadState}, 'not-found', 'missing unit is a valid discovery result');
$reply = 'Failed to connect to bus';
($state, $error) = get_unit_state('demo.socket');
ok(!defined($state), 'bus failure returns no state');
like($error, qr/connect to bus/, 'bus error is preserved');
($state, $error) = get_unit_state('bad;unit.service');
is($error, 'bad unit name', 'invalid state lookup is rejected');
}
{
my @cmds;
my $reloaded = 0;