#!/usr/bin/perl use strict; use warnings; use File::Slurp qw/read_file/; die("$0 service_name\n") if scalar @ARGV != 1; my $service = $ARGV[0]; my $rv = system('/sbin/service', $service, 'stop'); exit(0) if $rv == 0; if ($? == -1) { print "failed to execute: $!\n"; } elsif ($? & 127) { printf "child died with signal %d, %s coredump\n", ($? & 127), ($? & 128) ? 'with' : 'without'; } else { printf "child exited with value %d\n", $? >> 8; } my $tasks_fn = "/sys/fs/cgroup/openrc/$service/tasks"; sub pids { return {} if !-e $tasks_fn; return { map { $_ => 1 } split /\n/, read_file($tasks_fn) }; }; sub roots { my $pids = shift; return [ grep { read_file("/proc/$_/stat") =~ /^\d+ \(.*\) \w (\d+)/; !defined $pids->{$1}; } keys %$pids ]; }; sub send_signal { my ($pids, $sig) = @_; kill 'STOP', @$pids; my $cg_pids = pids; for my $pid (@$pids) { kill($sig, $pid) if grep { $pid eq $_ } keys %$cg_pids; } kill 'CONT', @$pids; }; my $tries = 3; my $delay = 10; sub send_signal_retry { my ($f, $sig, $msg) = @_; for(1..$tries) { my $return = eval { my $pids = $f->(); return 1 if !scalar @$pids; print "$msg\n"; send_signal($pids, $sig); return 0; }; return if $return == 1; warn "$@\n" if $@; for(1..$delay) { my $pids = eval { return $f->() }; return if scalar @$pids == 0; warn "$@\n" if $@; sleep(1); } } }; send_signal_retry( sub { return roots(pids); }, 'TERM', 'Sending TERM signal to process tree roots' ); send_signal_retry( sub { return [ keys %{ pids() } ] }, 'TERM', 'Sending TERM signal to remaining processes' ); send_signal_retry( sub { return [ keys %{ pids() } ] }, 'KILL', 'Sending KILL signal to remaining processes' ); exit(1) if scalar keys %{ pids() }; unlink("/run/openrc/started/$service"); exit(0);