aboutsummaryrefslogtreecommitdiff
path: root/home-manager/modules/systemd-activate.rb
diff options
context:
space:
mode:
Diffstat (limited to 'home-manager/modules/systemd-activate.rb')
-rw-r--r--home-manager/modules/systemd-activate.rb95
1 files changed, 54 insertions, 41 deletions
diff --git a/home-manager/modules/systemd-activate.rb b/home-manager/modules/systemd-activate.rb
index 8382c840e93..31d06d8fc19 100644
--- a/home-manager/modules/systemd-activate.rb
+++ b/home-manager/modules/systemd-activate.rb
@@ -1,6 +1,5 @@
require 'set'
require 'open3'
-require 'shellwords'
@dry_run = ENV['DRY_RUN']
@verbose = ENV['VERBOSE']
@@ -28,33 +27,36 @@ def setup_services(old_gen_path, new_gen_path, start_timeout_ms_string)
exit if old_services.empty? && new_services.empty?
+ all_services = get_active_targets_units(new_units_path)
+ maybe_changed = all_services & old_services
+ changed_services = get_changed_services(old_units_path, new_units_path, maybe_changed)
+ unchanged_oneshots = get_oneshot_services(maybe_changed - changed_services)
+
# These services should be running when this script is finished
- services_to_run = get_services_to_run(new_units_path)
- maybe_changed_services = services_to_run & old_services
+ services_to_run = all_services - unchanged_oneshots
# Only stop active services, otherwise we might get a 'service not loaded' error
# for inactive services that were removed in the current generation.
to_stop = get_active_units(old_services - new_services)
- to_restart = get_changed_services(old_units_path, new_units_path, maybe_changed_services)
+ to_restart = changed_services
to_start = get_inactive_units(services_to_run - to_restart)
- raise "daemon-reload failed" unless run_cmd('systemctl --user daemon-reload')
+ raise "daemon-reload failed" unless run_cmd('systemctl', '--user', 'daemon-reload')
- # Exclude services that aren't allowed to be manually started or stopped
+ # Exclude units that shouldn't be (re)started or stopped
no_manual_start, no_manual_stop, no_restart = get_restricted_units(to_stop + to_restart + to_start)
- to_stop -= no_manual_stop + no_restart
+ notify_skipped_units(to_restart & no_restart)
+ to_stop -= no_manual_stop
to_restart -= no_manual_stop + no_manual_start + no_restart
to_start -= no_manual_start
- puts "Not restarting: #{no_restart.join(' ')}" unless no_restart.empty?
-
if to_stop.empty? && to_start.empty? && to_restart.empty?
print_service_msg("All services are already running", services_to_run)
else
puts "Setting up services" if @verbose
- systemctl('stop', to_stop)
- systemctl('start', to_start)
- systemctl('restart', to_restart)
+ systemctl_action('stop', to_stop)
+ systemctl_action('start', to_start)
+ systemctl_action('restart', to_restart)
started_services = to_start + to_restart
if start_timeout_ms > 0 && !started_services.empty? && !@dry_run
failed = wait_and_get_failed_services(started_services, start_timeout_ms)
@@ -76,7 +78,7 @@ def get_services(dir)
end
def get_service_files(dir)
- Dir.chdir(dir) { Dir['*.{service,socket}'] }
+ Dir.chdir(dir) { Dir['*[^@].{service,socket,timer}'] }
end
def get_changed_services(dir_a, dir_b, services)
@@ -89,24 +91,24 @@ end
TargetDirRegexp = /^(.*\.target)\.wants$/
-# @return all services wanted by active targets
-def get_services_to_run(units_dir)
+# @return all units wanted by active targets
+def get_active_targets_units(units_dir)
return Set.new unless Dir.exists?(units_dir)
targets = Dir.entries(units_dir).map { |entry| entry[TargetDirRegexp, 1] }.compact
active_targets = get_active_units(targets)
- services_to_run = active_targets.map do |target|
+ active_units = active_targets.map do |target|
get_service_files(File.join(units_dir, "#{target}.wants"))
end.flatten
- Set.new(services_to_run)
+ Set.new(active_units)
end
# @return true on success
-def run_cmd(cmd)
+def run_cmd(*cmd)
print_cmd cmd
- @dry_run || system(cmd)
+ @dry_run || system(*cmd)
end
-def systemctl(cmd, services)
+def systemctl_action(cmd, services)
return if services.empty?
verb = (cmd == 'stop') ? 'Stopping' : "#{cmd.capitalize}ing"
@@ -114,7 +116,7 @@ def systemctl(cmd, services)
cmd = ['systemctl', '--user', cmd, *services]
if @dry_run
- puts cmd
+ puts cmd.join(' ')
return
end
@@ -131,32 +133,44 @@ def systemctl(cmd, services)
end
end
+def systemctl(*cmd)
+ output, _ = Open3.capture2('systemctl', '--user', *cmd)
+ output
+end
+
def print_cmd(cmd)
- puts cmd if @verbose || @dry_run
+ puts [*cmd].join(' ') if @verbose || @dry_run
end
def get_active_units(units)
- get_units_by_activity(units, true)
+ filter_units(units) { |state| state == 'active' }
end
def get_inactive_units(units)
- get_units_by_activity(units, false)
+ filter_units(units) { |state| state != 'active' }
+end
+
+def get_failed_units(units)
+ filter_units(units) { |state| state == 'failed' }
end
-def get_units_by_activity(units, active)
+def filter_units(units)
return [] if units.empty?
- units = units.to_a
- is_active = `systemctl --user is-active #{units.shelljoin}`.split
+ states = systemctl('is-active', *units).split
+ units.select.with_index { |_, i| yield states[i] }
+end
+
+def get_oneshot_services(units)
+ return [] if units.empty?
+ types = systemctl('show', '-p', 'Type', *units).split
units.select.with_index do |_, i|
- (is_active[i] == 'active') == active
+ types[i] == 'Type=oneshot'
end
end
def get_restricted_units(units)
- units = units.to_a
- infos = `systemctl --user show -p RefuseManualStart -p RefuseManualStop #{units.shelljoin}`
+ infos = systemctl('show', '-p', 'RefuseManualStart', '-p', 'RefuseManualStop', *units)
.split("\n\n")
- no_restart = []
no_manual_start = []
no_manual_stop = []
infos.zip(units).each do |info, unit|
@@ -164,14 +178,9 @@ def get_restricted_units(units)
no_manual_start << unit if no_start.end_with?('yes')
no_manual_stop << unit if no_stop.end_with?('yes')
end
- # Regular expression that indicates that a service should not be
- # restarted even if a change has been detected.
- restartRe = /^[ \t]*X-RestartIfChanged[ \t]*=[ \t]*false[ \t]*(?:#.*)?$/
- units.each do |unit|
- if `systemctl --user cat #{unit.shellescape}` =~ restartRe
- no_restart << unit
- end
- end
+ # Get units that should not be restarted even if a change has been detected.
+ no_restart_regexp = /^\s*X-RestartIfChanged\s*=\s*false\b/
+ no_restart = units.select { |unit| systemctl('cat', unit) =~ no_restart_regexp }
[no_manual_start, no_manual_stop, no_restart]
end
@@ -180,13 +189,13 @@ def wait_and_get_failed_services(services, start_timeout_ms)
# Force the previous message to always be visible before sleeping
STDOUT.flush
sleep(start_timeout_ms / 1000.0)
- get_inactive_units(services)
+ get_failed_units(services)
end
def show_failed_services_status(services)
puts
services.each do |service|
- run_cmd("systemctl --user status #{service.shellescape}")
+ run_cmd('systemctl', '--user', 'status', service)
puts
end
end
@@ -200,4 +209,8 @@ def print_service_msg(msg, services)
end
end
+def notify_skipped_units(no_restart)
+ puts "Not restarting: #{no_restart.join(' ')}" unless no_restart.empty?
+end
+
setup_services(*ARGV)