mirror of
https://github.com/webmin/webmin.git
synced 2026-06-27 22:40:26 +01:00
Fix to make packaged unit edits opt-in
ⓘ Default packaged unit files to read-only, keep drop-ins as the safe override path, hide boot controls for protected base units, and reject [Install] sections in drop-in overrides.
This commit is contained in:
@@ -5,6 +5,7 @@ visible_tabs=service,timer,socket,path,target,storage,resources,device,user
|
||||
show_runtime_units=1
|
||||
default_create_scope=system
|
||||
manual_vendor_units=1
|
||||
edit_vendor_units=0
|
||||
delete_vendor_units=0
|
||||
default_linger=1
|
||||
show_unit_suffixes=0
|
||||
|
||||
@@ -5,6 +5,7 @@ visible_tabs=Tabs to show on the index page,15,visible_tabs
|
||||
show_runtime_units=Show generated and transient units,1,1-Yes,0-No
|
||||
default_create_scope=Default scope for new units,1,system-System units,user-User units
|
||||
manual_vendor_units=Include vendor unit files in the manual editor,1,1-Yes,0-No
|
||||
edit_vendor_units=Allow editing packaged unit files,1,1-Yes,0-No
|
||||
delete_vendor_units=Allow deleting packaged unit files,1,1-Yes,0-No
|
||||
default_linger=Enable linger by default for new user units,1,1-Yes,0-No
|
||||
show_unit_suffixes=Show full unit names with type suffixes,1,1-Yes,0-No
|
||||
|
||||
@@ -23,6 +23,7 @@ my $info = $allowed{$in{'file'}} || $files[0];
|
||||
my $file = $info->{'file'};
|
||||
my $data = read_manual_unit_file($info);
|
||||
defined($data) || error($text{'manual_eread'});
|
||||
my $file_writable = manual_unit_file_writable($info);
|
||||
|
||||
ui_print_header(undef, $text{'manual_title'}, "");
|
||||
my $desc = $info->{'scope'} eq 'user' ?
|
||||
@@ -43,9 +44,11 @@ print ui_form_end();
|
||||
print ui_form_start("save_manual.cgi", "form-data");
|
||||
print ui_hidden("file", $file);
|
||||
print ui_table_start(undef, undef, 2);
|
||||
print ui_table_row(undef, ui_textarea("data", $data, 35, 120), 2);
|
||||
print ui_table_row(undef, ui_textarea("data", $data, 35, 120, undef,
|
||||
undef, $file_writable ? undef :
|
||||
"readonly='readonly'"), 2);
|
||||
print ui_table_end();
|
||||
print ui_form_end([ [ "save", $text{'save'} ] ]);
|
||||
print ui_form_end($file_writable ? [ [ "save", $text{'save'} ] ] : undef);
|
||||
|
||||
ui_print_footer("index.cgi", $text{'index_return'});
|
||||
|
||||
|
||||
@@ -143,7 +143,9 @@ my (@units, @unittypes, @types, @killmodes, @restarts, @protects);
|
||||
my (%creatable_types);
|
||||
my $default_unittype = 'service';
|
||||
my $unit_file_editable = 0;
|
||||
my $unit_file_writable = 0;
|
||||
my $can_save_unit = 0;
|
||||
my $can_write_current_file = 0;
|
||||
my $remote_uinfo = get_user_details($remote_user);
|
||||
|
||||
# New units start with an empty record. Existing units are looked up from the
|
||||
@@ -186,12 +188,17 @@ else {
|
||||
$dropin_file = $dropin_info->{'file'};
|
||||
}
|
||||
$unit_file_editable = unit_file_editable($u);
|
||||
$unit_file_writable = $edit_user_scope ? $unit_file_editable :
|
||||
system_unit_file_writable($u);
|
||||
$can_save_unit = $edit_dropin ?
|
||||
systemd_can_dropin($edit_user_scope, $unituser) :
|
||||
systemd_can_edit($edit_user_scope, $unituser);
|
||||
$can_write_current_file =
|
||||
($edit_dropin ? $unit_file_editable : $unit_file_writable) &&
|
||||
$can_save_unit ? 1 : 0;
|
||||
|
||||
# Runtime-managed units are inspect-only, so title them as views.
|
||||
my $title_key = $unit_file_editable && $can_save_unit ?
|
||||
my $title_key = $can_write_current_file ?
|
||||
($edit_user_scope ? 'systemd_title2_user' : 'systemd_title2') :
|
||||
($edit_user_scope ? 'systemd_title2_view_user' :
|
||||
'systemd_title2_view');
|
||||
@@ -985,8 +992,8 @@ else {
|
||||
}
|
||||
print ui_table_row(hlink($text{'systemd_conf'}, "systemd_conf"),
|
||||
ui_textarea("data", $conf, 20, 80, undef,
|
||||
undef, $unit_file_editable &&
|
||||
$can_save_unit ? undef :
|
||||
undef, $can_write_current_file ?
|
||||
undef :
|
||||
"readonly='readonly'"));
|
||||
|
||||
if ($edit_user_scope) {
|
||||
@@ -1010,6 +1017,7 @@ else {
|
||||
|
||||
# Only file-backed installable units can have their startup state changed.
|
||||
if (boot_state_changeable($u->{'unitstate'}, $u->{'name'}) &&
|
||||
($edit_user_scope || $unit_file_writable) &&
|
||||
systemd_can_boot($edit_user_scope, $unituser)) {
|
||||
print ui_table_row(hlink($text{'systemd_boot'}, "systemd_boot"),
|
||||
ui_yesno_radio("boot", $u->{'boot'}));
|
||||
@@ -1036,7 +1044,7 @@ if ($in{'new'}) {
|
||||
else {
|
||||
# Keep save, override, runtime and inspection actions in nearby clusters;
|
||||
# destructive actions stay isolated on the far side of the button row.
|
||||
my @save_buttons = $unit_file_editable && $can_save_unit ?
|
||||
my @save_buttons = $can_write_current_file ?
|
||||
( [ undef, $text{'save'} ] ) : ( );
|
||||
my @control_buttons;
|
||||
my @inspect_buttons = systemd_can_inspect($edit_user_scope, $unituser) ?
|
||||
@@ -1066,18 +1074,21 @@ else {
|
||||
|
||||
my @override_buttons;
|
||||
if ($edit_dropin) {
|
||||
my $base_unit_editable =
|
||||
$unit_file_writable &&
|
||||
systemd_can_edit($edit_user_scope, $unituser);
|
||||
my $stock_text = $base_unit_editable
|
||||
? $text{'edit_stockunitnow'}
|
||||
: $text{'edit_view_stockunitnow'};
|
||||
push(@override_buttons,
|
||||
[ 'stock_unit',
|
||||
$text{'edit_stockunitnow'} || "Stock Unit" ]);
|
||||
[ 'stock_unit', $stock_text ]);
|
||||
}
|
||||
elsif ($unit_file_editable &&
|
||||
systemd_can_dropin($edit_user_scope, $unituser)) {
|
||||
my $override_text = dropin_exists($edit_user_scope,
|
||||
$unituser, $in{'name'}) ?
|
||||
($text{'edit_editoverridenow'} ||
|
||||
"Edit Override") :
|
||||
($text{'edit_overridenow'} ||
|
||||
"Create Override");
|
||||
$text{'edit_editoverridenow'} :
|
||||
$text{'edit_overridenow'};
|
||||
push(@override_buttons, [ 'override', $override_text ]);
|
||||
}
|
||||
my @delete_buttons;
|
||||
@@ -1085,7 +1096,7 @@ else {
|
||||
systemd_can_dropin($edit_user_scope, $unituser)) {
|
||||
push(@delete_buttons,
|
||||
[ 'delete_override',
|
||||
$text{'edit_deleteoverridenow'} || "Delete Override" ]);
|
||||
$text{'edit_deleteoverridenow'} ]);
|
||||
}
|
||||
elsif (($edit_user_scope || system_unit_file_deletable($u)) &&
|
||||
$unit_file_editable && $in{'name'} ne 'webmin.service' &&
|
||||
|
||||
10
systemd/help/config_edit_vendor_units.html
Normal file
10
systemd/help/config_edit_vendor_units.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<header>Allow editing packaged unit files</header>
|
||||
<p>Controls whether the module may directly save changes to package-managed
|
||||
unit files from vendor directories such as <tt>/usr/lib/systemd/system</tt>
|
||||
or <tt>/lib/systemd/system</tt>. This is disabled by default because package
|
||||
updates may overwrite or depend on those files.</p>
|
||||
|
||||
<p>When set to no, packaged unit files can still be inspected in the manual
|
||||
editor when vendor files are included, but they are read-only. Use drop-in
|
||||
overrides or local units under <tt>/etc/systemd/system</tt> for normal
|
||||
customization.</p>
|
||||
@@ -51,6 +51,7 @@ edit_logsnow=Logs
|
||||
edit_overridenow=Create Override
|
||||
edit_editoverridenow=Edit Override
|
||||
edit_stockunitnow=Edit Base Unit
|
||||
edit_view_stockunitnow=View Base Unit
|
||||
edit_deleteoverridenow=Delete Override
|
||||
ss_ecannot=You are not allowed to start or stop systemd units
|
||||
acl_section_users=User unit owner restrictions
|
||||
@@ -217,6 +218,7 @@ systemd_title2_user=Edit Systemd User Unit
|
||||
systemd_title2_view=View Systemd Unit
|
||||
systemd_title2_view_user=View Systemd User Unit
|
||||
systemd_egone=Unit no longer exists!
|
||||
systemd_evendoredit=Editing packaged unit files is disabled in the module configuration. Create a drop-in override, or enable packaged unit editing first.
|
||||
systemd_elocaldelete=Deleting packaged unit files is disabled in the module configuration. Disable, mask or create a drop-in override, or enable packaged unit deletion first.
|
||||
systemd_header=Systemd unit details
|
||||
systemd_name=Unit name
|
||||
@@ -314,6 +316,7 @@ systemd_euserhome=Failed to create systemd user unit directory
|
||||
systemd_euserunitfile=Unsafe or invalid systemd user unit file
|
||||
systemd_euserunitdir=Unsafe or wrongly-owned systemd user unit directory
|
||||
systemd_edropinfile=Unsafe or invalid systemd drop-in override file
|
||||
systemd_edropininstall=Drop-in overrides cannot contain an [Install] section. Use enable or disable actions to change startup state.
|
||||
systemd_ereadonly=This systemd unit file is managed at runtime and cannot be edited directly.
|
||||
systemd_eloginctl=The loginctl command is not available on your system
|
||||
systemd_esystemctl=The systemctl command is not available on your system
|
||||
|
||||
@@ -697,6 +697,9 @@ else {
|
||||
if (!unit_file_editable($u)) {
|
||||
error($text{'systemd_ereadonly'});
|
||||
}
|
||||
if (!$edit_dropin && !$user_scope && !system_unit_file_writable($u)) {
|
||||
error($text{'systemd_evendoredit'});
|
||||
}
|
||||
$in{'data'} =~ /\S/ || error($text{'systemd_econf'});
|
||||
$in{'data'} =~ s/\r//g;
|
||||
my $save_data = $edit_dropin ?
|
||||
@@ -788,7 +791,8 @@ else {
|
||||
|
||||
# Apply startup state changes after saving the config.
|
||||
if (defined($in{'boot'}) &&
|
||||
boot_state_changeable($u->{'unitstate'}, $u->{'name'})) {
|
||||
boot_state_changeable($u->{'unitstate'}, $u->{'name'}) &&
|
||||
($user_scope || system_unit_file_writable($u))) {
|
||||
systemd_can_boot($user_scope, $unituser) ||
|
||||
systemd_acl_error('pboot');
|
||||
if ($user_scope) {
|
||||
|
||||
@@ -32,6 +32,9 @@ $config{"default_create_scope"} = "system"
|
||||
$config{"default_create_scope"} !~ /^(system|user)$/);
|
||||
$config{"manual_vendor_units"} = 1
|
||||
if (!defined($config{"manual_vendor_units"}));
|
||||
$config{"edit_vendor_units"} = 0
|
||||
if (!defined($config{"edit_vendor_units"}) ||
|
||||
$config{"edit_vendor_units"} !~ /^[01]$/);
|
||||
$config{"delete_vendor_units"} = 0
|
||||
if (!defined($config{"delete_vendor_units"}) ||
|
||||
$config{"delete_vendor_units"} !~ /^[01]$/);
|
||||
@@ -1756,10 +1759,12 @@ systemd-analyze cannot reliably load their temporary copies by name.
|
||||
sub verify_dropin_data
|
||||
{
|
||||
my ($file, $unit_data, $dropin_data, $user_scope, $unitstate, $user) = @_;
|
||||
my $analyze = has_command("systemd-analyze");
|
||||
return (1, undef) if (!$analyze);
|
||||
return (0, $text{'systemd_econf'})
|
||||
if (!defined($unit_data) || !defined($dropin_data));
|
||||
return (0, $text{'systemd_edropininstall'})
|
||||
if (dropin_has_install_section($dropin_data));
|
||||
my $analyze = has_command("systemd-analyze");
|
||||
return (1, undef) if (!$analyze);
|
||||
my $name = $file;
|
||||
$name =~ s/^.*\///;
|
||||
return (0, $text{'systemd_ename'}) if (!valid_unit_file_name($name));
|
||||
@@ -1905,6 +1910,8 @@ Writes the standard local drop-in override file for a system unit.
|
||||
sub write_system_dropin_file
|
||||
{
|
||||
my ($unit, $data) = @_;
|
||||
return (0, $text{'systemd_edropininstall'})
|
||||
if (dropin_has_install_section($data));
|
||||
my $file = system_dropin_file($unit);
|
||||
return (0, $text{'systemd_ename'}) if (!$file);
|
||||
my $dir = $file;
|
||||
@@ -2009,6 +2016,8 @@ Writes the standard user drop-in override file as the owning Unix user.
|
||||
sub write_user_dropin_file
|
||||
{
|
||||
my ($user, $unit, $data) = @_;
|
||||
return (0, $text{'systemd_edropininstall'})
|
||||
if (dropin_has_install_section($data));
|
||||
my $file = user_dropin_file($user, $unit);
|
||||
return (0, $text{'systemd_euserunitfile'})
|
||||
if (!$file || !user_dropin_file_safe($user, $file, 0));
|
||||
@@ -2174,6 +2183,8 @@ Writes a safe existing system drop-in config file.
|
||||
sub write_system_dropin_config_file
|
||||
{
|
||||
my ($file, $data) = @_;
|
||||
return (0, $text{'systemd_edropininstall'})
|
||||
if (dropin_has_install_section($data));
|
||||
return (0, $text{'systemd_edropinfile'})
|
||||
if (!system_dropin_config_file_safe($file, 1));
|
||||
return (1, undef) if (is_readonly_mode());
|
||||
@@ -2252,6 +2263,8 @@ Writes a safe existing user drop-in config file as the owning Unix user.
|
||||
sub write_user_dropin_config_file
|
||||
{
|
||||
my ($user, $file, $data) = @_;
|
||||
return (0, $text{'systemd_edropininstall'})
|
||||
if (dropin_has_install_section($data));
|
||||
return (0, $text{'systemd_edropinfile'})
|
||||
if (!user_dropin_config_file_safe($user, $file, 1));
|
||||
return (1, undef) if (is_readonly_mode());
|
||||
@@ -2418,6 +2431,24 @@ $data =~ s/^### Lines below this comment will be discarded\s*\n.*\z//ms;
|
||||
return $data;
|
||||
}
|
||||
|
||||
=head2 dropin_has_install_section(data)
|
||||
|
||||
Returns 1 if a drop-in contains an active C<[Install]> section. Drop-ins are
|
||||
for unit overrides; startup state is managed by enable and disable actions.
|
||||
|
||||
=cut
|
||||
sub dropin_has_install_section
|
||||
{
|
||||
my ($data) = @_;
|
||||
return 0 if (!defined($data));
|
||||
foreach my $line (split(/\n/, $data)) {
|
||||
$line =~ s/\r//g;
|
||||
next if ($line =~ /^\s*(?:[#;]|$)/);
|
||||
return 1 if ($line =~ /^\s*\[\s*Install\s*\]\s*(?:[#;].*)?$/i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
=head2 delete_user_unit_file(user, file)
|
||||
|
||||
Deletes a user unit file as the owning Unix user after path validation, so a
|
||||
@@ -3022,6 +3053,8 @@ sub write_manual_unit_file
|
||||
my ($info, $data) = @_;
|
||||
return (0, $text{'manual_efile'})
|
||||
if (!$info || !$info->{'file'});
|
||||
return (0, $text{'systemd_evendoredit'})
|
||||
if (!manual_unit_file_writable($info));
|
||||
$data = "" if (!defined($data));
|
||||
$data =~ s/\0//g;
|
||||
$data =~ s/\r//g;
|
||||
@@ -3067,6 +3100,20 @@ unlock_file($info->{'file'});
|
||||
return (1, undef);
|
||||
}
|
||||
|
||||
=head2 manual_unit_file_writable(info)
|
||||
|
||||
Returns 1 if a manual editor file descriptor may be saved.
|
||||
|
||||
=cut
|
||||
sub manual_unit_file_writable
|
||||
{
|
||||
my ($info) = @_;
|
||||
return 0 if (!$info || !$info->{'scope'});
|
||||
return 1 if ($info->{'scope'} eq 'user');
|
||||
return 1 if ($info->{'kind'} && $info->{'kind'} eq 'dropin');
|
||||
return system_unit_file_edit_allowed($info->{'file'}, $info->{'name'});
|
||||
}
|
||||
|
||||
=head2 mark_units_changed()
|
||||
|
||||
Updates the flag file indicating that manual unit-file edits need reload.
|
||||
@@ -3579,6 +3626,51 @@ return 0 if (!unit_file_editable($unit));
|
||||
return system_unit_file_delete_allowed($unit->{'file'}, $unit->{'name'});
|
||||
}
|
||||
|
||||
=head2 system_unit_file_writable(&unit)
|
||||
|
||||
Returns 1 if a system unit record points to a unit file that can be edited
|
||||
under the current module configuration.
|
||||
|
||||
=cut
|
||||
sub system_unit_file_writable
|
||||
{
|
||||
my ($unit) = @_;
|
||||
return 0 if (!unit_file_editable($unit));
|
||||
return system_unit_file_edit_allowed($unit->{'file'}, $unit->{'name'});
|
||||
}
|
||||
|
||||
=head2 system_unit_file_edit_allowed(file, name)
|
||||
|
||||
Returns 1 if a system unit path is in a root where editing unit files is
|
||||
currently permitted.
|
||||
|
||||
=cut
|
||||
sub system_unit_file_edit_allowed
|
||||
{
|
||||
my ($file, $name) = @_;
|
||||
return 0 if (!$file || !$name || !valid_unit_name($name));
|
||||
foreach my $root (get_system_unit_file_root_candidates()) {
|
||||
next if (!system_unit_root_edit_allowed($root));
|
||||
if ($file eq $root."/".$name) {
|
||||
return 0 if (-l $file);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
=head2 system_unit_root_edit_allowed(root)
|
||||
|
||||
Returns 1 if system unit files under this root may be edited.
|
||||
|
||||
=cut
|
||||
sub system_unit_root_edit_allowed
|
||||
{
|
||||
my ($root) = @_;
|
||||
return (local_unit_file_root($root) ||
|
||||
$config{'edit_vendor_units'} eq '1') ? 1 : 0;
|
||||
}
|
||||
|
||||
=head2 system_unit_file_delete_allowed(file, name)
|
||||
|
||||
Returns 1 if a system unit path is in a root where deleting unit files is
|
||||
|
||||
@@ -77,9 +77,11 @@ our (%access, %config, %in, %text, %gconfig, $remote_user);
|
||||
systemd_euserunitfile => 'bad user unit file',
|
||||
systemd_euserunitdir => 'bad user unit dir',
|
||||
systemd_edropinfile => 'bad drop-in file',
|
||||
systemd_edropininstall => 'bad drop-in install section',
|
||||
systemd_ereadonly => 'runtime unit file',
|
||||
systemd_ename => 'bad unit name',
|
||||
systemd_egone => 'unit gone',
|
||||
systemd_evendoredit => 'vendor unit edit disabled',
|
||||
systemd_elocaldelete => 'local unit delete only',
|
||||
systemd_eclash => 'unit clash',
|
||||
systemd_emountwhat => 'missing mount source',
|
||||
@@ -197,6 +199,27 @@ ok(!unit_file_editable({
|
||||
unitstate => 'generated',
|
||||
}),
|
||||
'generated unit files are read-only');
|
||||
ok(system_unit_file_writable({
|
||||
name => 'local.service',
|
||||
file => '/etc/systemd/system/local.service',
|
||||
unitstate => 'enabled',
|
||||
}),
|
||||
'local system unit files can be edited directly');
|
||||
ok(!system_unit_file_writable({
|
||||
name => 'vendor.service',
|
||||
file => '/usr/lib/systemd/system/vendor.service',
|
||||
unitstate => 'enabled',
|
||||
}),
|
||||
'packaged system unit files cannot be edited directly by default');
|
||||
{
|
||||
local $config{'edit_vendor_units'} = 1;
|
||||
ok(system_unit_file_writable({
|
||||
name => 'vendor.service',
|
||||
file => '/usr/lib/systemd/system/vendor.service',
|
||||
unitstate => 'enabled',
|
||||
}),
|
||||
'packaged system unit files can be edited directly when configured');
|
||||
}
|
||||
ok(system_unit_file_deletable({
|
||||
name => 'local.service',
|
||||
file => '/etc/systemd/system/local.service',
|
||||
@@ -1009,11 +1032,18 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
|
||||
"[Service]\nRestart=always\n");
|
||||
write_test_file("$packaged_root/vendor.socket", "");
|
||||
write_test_file("$packaged_root/vendor.mount", "");
|
||||
symlink("$packaged_root/vendor.socket", "$local_root/vendor-link.service");
|
||||
my @commands;
|
||||
local @main::list_units_cache = ();
|
||||
local *main::get_system_unit_file_roots = sub {
|
||||
return ($local_root, $packaged_root);
|
||||
};
|
||||
local *main::get_system_unit_file_root_candidates = sub {
|
||||
return ($local_root, $packaged_root);
|
||||
};
|
||||
local *main::get_local_unit_root = sub {
|
||||
return $local_root;
|
||||
};
|
||||
local *main::get_system_dropin_roots = sub {
|
||||
return ($local_root);
|
||||
};
|
||||
@@ -1148,6 +1178,21 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
|
||||
'manual unit files include system drop-in override files');
|
||||
is($manual{"$local_root/demo.service.d/00-local.conf"}->{'kind'},
|
||||
'dropin', 'manual drop-in descriptor is marked');
|
||||
ok(manual_unit_file_writable($manual{"$local_root/local.path"}),
|
||||
'manual local system unit files are writable');
|
||||
ok(!manual_unit_file_writable($manual{"$packaged_root/vendor.mount"}),
|
||||
'manual packaged unit files are read-only by default');
|
||||
ok(!system_unit_file_writable({
|
||||
name => 'vendor-link.service',
|
||||
file => "$local_root/vendor-link.service",
|
||||
unitstate => 'enabled',
|
||||
}),
|
||||
'local symlink unit files are not edited directly');
|
||||
{
|
||||
local $config{'edit_vendor_units'} = 1;
|
||||
ok(manual_unit_file_writable($manual{"$packaged_root/vendor.mount"}),
|
||||
'manual packaged unit files are writable when configured');
|
||||
}
|
||||
ok(!manual_system_unit_file_safe("$local_root/../escape.service"),
|
||||
'manual system unit file safety rejects traversal');
|
||||
my $manual_info = manual_unit_file("$local_root/local.path");
|
||||
@@ -1161,6 +1206,8 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
|
||||
manual_unit_file("$local_root/demo.service.d/00-local.conf");
|
||||
ok($manual_dropin_info,
|
||||
'manual_unit_file returns allowed drop-in descriptor');
|
||||
ok(manual_unit_file_writable($manual_dropin_info),
|
||||
'manual system drop-in files remain writable');
|
||||
is(read_manual_unit_file($manual_dropin_info),
|
||||
"[Service]\nRestart=always\n",
|
||||
'read_manual_unit_file reads system drop-in files');
|
||||
@@ -1170,6 +1217,12 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
|
||||
is(slurp_test_file("$local_root/demo.service.d/00-local.conf"),
|
||||
"[Service]\nRestart=on-failure\n",
|
||||
'manual system drop-in write preserves exact file');
|
||||
($ok, $err) = write_manual_unit_file(
|
||||
$manual{"$packaged_root/vendor.mount"},
|
||||
"[Mount]\nWhat=/tmp\nWhere=/vendor\n");
|
||||
ok(!$ok, 'write_manual_unit_file rejects packaged unit writes by default');
|
||||
is($err, $text{'systemd_evendoredit'},
|
||||
'write_manual_unit_file reports packaged unit edit policy');
|
||||
unlink($main::unit_config_change_flag);
|
||||
unlink($main::daemon_reload_time_flag);
|
||||
ok(!needs_daemon_reload(), 'daemon reload is not needed initially');
|
||||
@@ -1280,6 +1333,13 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
|
||||
'verify_dropin_data verifies the base unit name');
|
||||
ok(!-e "$verify_root/verify-4/drop.service.d/override.conf",
|
||||
'verify_dropin_data removes temporary override files');
|
||||
($ok, $err) = verify_dropin_data(
|
||||
'/etc/systemd/system/drop.service',
|
||||
"[Unit]\nDescription=Drop\n[Service]\nExecStart=/bin/true\n",
|
||||
"[Install]\nWantedBy=multi-user.target\n", 0);
|
||||
ok(!$ok, 'verify_dropin_data rejects Install sections');
|
||||
is($err, $text{'systemd_edropininstall'},
|
||||
'verify_dropin_data reports Install section policy');
|
||||
|
||||
my $before_transient_verify = scalar(@verify_commands);
|
||||
($ok, $err) = verify_dropin_data(
|
||||
@@ -1346,6 +1406,10 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
|
||||
"### Anything between here and the comment below will become ".
|
||||
"the new contents of the file\n\n\n\n",
|
||||
'dropin_effective_data discards commented base unit contents');
|
||||
ok(dropin_has_install_section("[Install]\nWantedBy=multi-user.target\n"),
|
||||
'drop-in install-section detector rejects active Install sections');
|
||||
ok(!dropin_has_install_section("# [Install]\n[Service]\nRestart=always\n"),
|
||||
'drop-in install-section detector ignores commented examples');
|
||||
|
||||
my ($ok, $out) = write_system_dropin_file('demo.service', $template);
|
||||
ok($ok, 'write_system_dropin_file writes standard override files');
|
||||
@@ -1368,6 +1432,12 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
|
||||
"$dropin_root/demo.service.d/10-extra.conf",
|
||||
"[Service]\nRestartSec=10s\n");
|
||||
ok($ok, 'system drop-in config writer updates exact safe file');
|
||||
($ok, $out) = write_system_dropin_config_file(
|
||||
"$dropin_root/demo.service.d/10-extra.conf",
|
||||
"[Install]\nWantedBy=multi-user.target\n");
|
||||
ok(!$ok, 'system drop-in config writer rejects Install sections');
|
||||
is($out, $text{'systemd_edropininstall'},
|
||||
'system drop-in config writer reports Install section policy');
|
||||
is(slurp_test_file("$dropin_root/demo.service.d/10-extra.conf"),
|
||||
"[Service]\nRestartSec=10s\n",
|
||||
'system drop-in config writer preserves non-standard filename');
|
||||
@@ -1459,6 +1529,12 @@ like(get_unit_root(), qr{^/(etc|usr/lib|lib)/systemd/system$},
|
||||
is(slurp_test_file("$root/demo.service.d/20-local.conf"),
|
||||
"[Service]\nEnvironment=DEMO=2\n",
|
||||
'user drop-in config writer preserves non-standard filename');
|
||||
($ok, $out) = write_user_dropin_config_file(
|
||||
'alice', "$root/demo.service.d/20-local.conf",
|
||||
"[Install]\nWantedBy=default.target\n");
|
||||
ok(!$ok, 'user drop-in config writer rejects Install sections');
|
||||
is($out, $text{'systemd_edropininstall'},
|
||||
'user drop-in config writer reports Install section policy');
|
||||
ok(dropin_exists(1, 'alice', 'demo.service'),
|
||||
'dropin_exists detects user override files');
|
||||
is(read_user_dropin_file('alice', 'demo.service'),
|
||||
@@ -2044,6 +2120,8 @@ like($config_source, qr/^default_create_scope=/m,
|
||||
'module config exposes default create scope');
|
||||
like($config_source, qr/^manual_vendor_units=/m,
|
||||
'module config exposes vendor-file manual editor visibility');
|
||||
like($config_source, qr/^edit_vendor_units=/m,
|
||||
'module config exposes packaged unit edit policy');
|
||||
like($config_source, qr/^delete_vendor_units=/m,
|
||||
'module config exposes packaged unit delete policy');
|
||||
like($config_source, qr/^default_linger=/m,
|
||||
@@ -2185,6 +2263,12 @@ like($save_source, qr/write_user_unit_file/,
|
||||
'save page uses safe user-unit writer');
|
||||
like($save_source, qr/unit_file_editable/,
|
||||
'save page rejects direct writes to runtime-managed unit files');
|
||||
like($save_source, qr/system_unit_file_writable/,
|
||||
'save page rejects direct writes to packaged unit files by default');
|
||||
like($save_source, qr/systemd_evendoredit/,
|
||||
'save page reports packaged unit edit policy');
|
||||
like($save_source, qr/boot_state_changeable.*?\(\$user_scope \|\| system_unit_file_writable\(\$u\)\)/s,
|
||||
'save page ignores edit-form boot changes for protected packaged units');
|
||||
like($save_source, qr/verify_unit_data/,
|
||||
'save page verifies raw unit edits before writing');
|
||||
like($save_source, qr/dropin_template/,
|
||||
@@ -2279,10 +2363,14 @@ like($edit_source,
|
||||
like($edit_source,
|
||||
qr/systemd_unituser.*systemd_runtime_state.*systemd_unit_state.*systemd_boot.*systemd_linger_user/s,
|
||||
'edit page orders user unit metadata rows');
|
||||
like($edit_source, qr/boot_state_changeable.*?\(\$edit_user_scope \|\| \$unit_file_writable\).*?systemd_can_boot/s,
|
||||
'edit page hides boot radio for protected packaged units');
|
||||
like($edit_source, qr/readonly='readonly'/,
|
||||
'edit page shows runtime-managed unit files as read-only');
|
||||
like($edit_source, qr/unit_file_editable/,
|
||||
'edit page hides save and delete for runtime-managed unit files');
|
||||
like($edit_source, qr/system_unit_file_writable/,
|
||||
'edit page makes packaged system unit files read-only by default');
|
||||
like($edit_source, qr/system_unit_file_deletable/,
|
||||
'edit page hides delete for packaged system unit files');
|
||||
like($edit_source, qr/edit_depsnow/,
|
||||
@@ -2297,6 +2385,8 @@ like($edit_source, qr/edit_deleteoverridenow/,
|
||||
'edit page labels override deletes clearly');
|
||||
like($edit_source, qr/edit_stockunitnow/,
|
||||
'edit page links override edits back to the stock unit');
|
||||
like($edit_source, qr/edit_view_stockunitnow/,
|
||||
'edit page labels protected base units as view-only from drop-ins');
|
||||
like($edit_source, qr/stock_unit/,
|
||||
'edit page uses a grouped button for stock-unit navigation');
|
||||
like($edit_source, qr/systemd_can_edit/,
|
||||
@@ -2353,6 +2443,8 @@ unlike($edit_manual_source, qr/action_links\(/,
|
||||
'manual editor header omits daemon reload action');
|
||||
like($edit_manual_source, qr/systemd_can_manual/,
|
||||
'manual editor filters files by ACL');
|
||||
like($edit_manual_source, qr/manual_unit_file_writable/,
|
||||
'manual editor hides save for read-only packaged unit files');
|
||||
like($dropins_source, qr/list_system_dropin_override_files/,
|
||||
'drop-in inventory lists system drop-ins');
|
||||
like($dropins_source, qr/list_all_user_dropin_override_files/,
|
||||
@@ -2365,6 +2457,8 @@ like($dropins_source, qr/sub dropin_file_arg\b/,
|
||||
'drop-in inventory links non-standard drop-ins by exact file');
|
||||
like($save_manual_source, qr/write_manual_unit_file/,
|
||||
'manual save uses constrained unit file writer');
|
||||
like($lib_source, qr/sub manual_unit_file_writable\b/,
|
||||
'library distinguishes writable manual files from read-only inventory');
|
||||
like($save_manual_source, qr/mark_units_changed/,
|
||||
'manual system save marks daemon reload as needed');
|
||||
like($save_manual_source, qr/mark_user_units_changed/,
|
||||
|
||||
Reference in New Issue
Block a user