682 lines
18 KiB
Perl
Executable file
682 lines
18 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
# Copyright 2014 Jan Pazdziora
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
use strict;
|
|
use warnings FATAL => 'all';
|
|
|
|
use IO::File ();
|
|
use IO::Dir ();
|
|
use IO::Socket::UNIX ();
|
|
use Socket ();
|
|
use POSIX ();
|
|
use Time::HiRes qw(sleep time);
|
|
|
|
my $SYSTEMCTL_LOG = $ENV{"_SYSTEMCTL_LITE_LOGFILE"};
|
|
if (not defined $SYSTEMCTL_LOG) {
|
|
$SYSTEMCTL_LOG = '/var/log/systemctl.log';
|
|
}
|
|
sub log_command {
|
|
local * LOG;
|
|
open(LOG, '>>', $SYSTEMCTL_LOG);
|
|
print LOG @_;
|
|
close LOG;
|
|
}
|
|
log_command("[@ARGV]\n");
|
|
shift if @ARGV and $ARGV[0] eq '-q';
|
|
|
|
for (my $i = 0; $i < @ARGV; $i++) {
|
|
if ($ARGV[$i] eq '--ignore-dependencies') {
|
|
splice @ARGV, $i, 1;
|
|
last;
|
|
}
|
|
}
|
|
if (@ARGV == 1 and $ARGV[0] eq 'daemon-reload') {
|
|
exit 0;
|
|
}
|
|
if (@ARGV == 2 and $ARGV[0] eq '--system' and $ARGV[1] eq 'daemon-reload') {
|
|
exit 0;
|
|
}
|
|
|
|
for (keys %ENV) {
|
|
delete $ENV{$_} unless $_ =~ /^_SYSTEMCTL_LITE/;
|
|
}
|
|
|
|
my $RUNNING_DIR = '/run/systemctl-lite-running';
|
|
if (not -d $RUNNING_DIR) {
|
|
mkdir $RUNNING_DIR;
|
|
}
|
|
my $ENABLED_DIR = '/etc/systemctl-lite-enabled';
|
|
if (defined $ENV{_SYSTEMCTL_LITE_ENABLED_DIR}) {
|
|
$ENABLED_DIR = $ENV{_SYSTEMCTL_LITE_ENABLED_DIR};
|
|
}
|
|
|
|
if (@ARGV == 1) {
|
|
if ($ARGV[0] eq 'start-enabled' and -d $ENABLED_DIR) {
|
|
local * ENABLED;
|
|
opendir ENABLED, $ENABLED_DIR;
|
|
my %services;
|
|
while (defined(my $f = readdir ENABLED)) {
|
|
next if $f eq '.' or $f eq '..';
|
|
my $modified = (stat "$ENABLED_DIR/$f")[9];
|
|
if (defined $modified) {
|
|
$services{$f} = $modified;
|
|
}
|
|
}
|
|
close ENABLED;
|
|
for my $s (sort { $services{$a} <=> $services{$b} or $a cmp $b } keys %services) {
|
|
print "Starting [$s]\n";
|
|
system $0, 'start', $s;
|
|
exit ($? >> 8) if ($? >> 8);
|
|
}
|
|
}
|
|
if ($ARGV[0] eq 'stop-running' and -d $RUNNING_DIR) {
|
|
local * RUNNING;
|
|
opendir RUNNING, $RUNNING_DIR;
|
|
my %services;
|
|
while (defined(my $f = readdir RUNNING)) {
|
|
next if $f eq '.' or $f eq '..';
|
|
my $modified = (stat "$RUNNING_DIR/$f")[9];
|
|
if (defined $modified) {
|
|
my $trimmed = $f;
|
|
$trimmed =~ s/\..+$//;
|
|
$services{$trimmed} = $modified;
|
|
}
|
|
}
|
|
close RUNNING;
|
|
for my $s (sort { $services{$b} <=> $services{$a} or $a cmp $b } keys %services) {
|
|
system $0, 'stop', $s;
|
|
}
|
|
}
|
|
exit;
|
|
}
|
|
|
|
if (@ARGV != 2) {
|
|
die "Usage: $0 (start|stop|status|...) (service|target|socket thing)\n";
|
|
}
|
|
|
|
my ($COMMAND, $SERVICE) = @ARGV;
|
|
my $TYPE = 'service';
|
|
if ($SERVICE =~ /\.(target|socket)$/) {
|
|
$TYPE = $1;
|
|
} elsif (not $SERVICE =~ /\.service$/) {
|
|
$SERVICE .= '.service';
|
|
}
|
|
|
|
my @PATHS = (
|
|
'/etc/systemd/system',
|
|
'/run/systemd/system',
|
|
'/usr/lib/systemd/system',
|
|
);
|
|
|
|
my $FULL_SERVICE = $SERVICE;
|
|
my $INSTANCE = undef;
|
|
|
|
my $file = undef;
|
|
if ($SERVICE =~ s/\@(.+)\.service$/\@.service/) {
|
|
$INSTANCE = $1;
|
|
}
|
|
for my $p (@PATHS) {
|
|
if (-e "$p/$SERVICE") {
|
|
if (-l "$p/$SERVICE") {
|
|
my $new_service = readlink("$p/$SERVICE");
|
|
log_command("Service [$p/$SERVICE] is a symlink to [$new_service]\n");
|
|
$new_service =~ s!^.*/!!;
|
|
$SERVICE = $new_service;
|
|
}
|
|
$file = "$p/$SERVICE";
|
|
last;
|
|
}
|
|
}
|
|
|
|
if ($SERVICE =~ s/\@(.+)\.service$/\@.service/) {
|
|
$INSTANCE = $1;
|
|
for my $p (@PATHS) {
|
|
if (-f "$p/$SERVICE") {
|
|
$file = "$p/$SERVICE";
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (not defined $file) {
|
|
if ($COMMAND eq 'is-enabled') {
|
|
exit 1;
|
|
}
|
|
if ($COMMAND eq 'is-active') {
|
|
exit 3;
|
|
}
|
|
warn "No service definition found for [$FULL_SERVICE].\n";
|
|
exit 2;
|
|
}
|
|
|
|
sub parse_file {
|
|
my ($file, $data) = @_;
|
|
if (-d $file) {
|
|
my $op;
|
|
if ($file =~ /\.requires$/) {
|
|
$op = 'Unit.Requires';
|
|
} elsif ($file =~ /\.wants$/) {
|
|
$op = 'Unit.Wants';
|
|
} else {
|
|
die "Unknown directory [$file].\n";
|
|
}
|
|
my $dh = new IO::Dir($file);
|
|
while (defined(my $de = $dh->read)) {
|
|
next if $de eq '.' or $de eq '..';
|
|
push @{$data->{$op}}, $de unless $de =~ /\.target$/;
|
|
}
|
|
$dh->close;
|
|
return;
|
|
}
|
|
my $fh = new IO::File($file);
|
|
my $section = 'undefined';
|
|
while (my $line = <$fh>) {
|
|
chomp $line;
|
|
if ($line =~ /^\[(.+)\]\s*$/) {
|
|
$section = $1;
|
|
next;
|
|
}
|
|
next if $line =~ /^\s*(#|$)/;
|
|
if ($line =~ /^\.include\s(.+)/) {
|
|
parse_file($1, $data);
|
|
next;
|
|
}
|
|
my ($key, $value) = split /=/, $line, 2;
|
|
if (defined $INSTANCE) {
|
|
$value =~ s/\%i/$INSTANCE/g;
|
|
}
|
|
if ($key eq 'EnvironmentFile') {
|
|
if ($value eq '') {
|
|
delete $data->{"$section.$key"};
|
|
} else {
|
|
push @{ $data->{"$section.$key"} }, $value;
|
|
}
|
|
} elsif ($key =~ /^(Wants|Requires)$/) {
|
|
push @{ $data->{"$section.$key"} }, $value unless $value =~ /\.target$/;
|
|
} elsif ($key =~ /^(ExecStart(Pre|Post)|ExecReload|ExecStop(Pre|Post)?)$/) {
|
|
push @{ $data->{"$section.$key"} }, $value;
|
|
} else {
|
|
$data->{"$section.$key"} = $value;
|
|
}
|
|
}
|
|
$fh->close;
|
|
if (defined $data->{'Service.ExecStart'} and not defined $data->{'Service.PIDFile'}) {
|
|
my ($pidfile) = grep /^\/.+\.pid$/, split /\s+/, $data->{'Service.ExecStart'};
|
|
if (defined $pidfile) {
|
|
$data->{'Service.PIDFile'} = $pidfile;
|
|
log_command("Guessing pid file [$data->{'Service.PIDFile'}] from ExecStart [$data->{'Service.ExecStart'}]\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
my $data = {};
|
|
parse_file($file, $data);
|
|
for my $p (@PATHS) {
|
|
if (-d "$p/$SERVICE.wants") {
|
|
$file = "$p/$SERVICE.wants";
|
|
parse_file("$p/$SERVICE.wants", $data);
|
|
last;
|
|
}
|
|
}
|
|
|
|
sub pidof {
|
|
my $command = shift;
|
|
my $pids = `/usr/sbin/pidof $command`;
|
|
chomp $pids;
|
|
if ($pids ne '') {
|
|
return split /\s+/, $pids;
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub get_exec_start {
|
|
my $data = shift;
|
|
my $d = $data->{'Service.ExecStart'};
|
|
if (not defined $d) {
|
|
warn "No ExecStart value found for [$SERVICE].\n";
|
|
exit 3;
|
|
}
|
|
return split /\s+/, $d;
|
|
}
|
|
|
|
sub get_pid {
|
|
my $file = shift;
|
|
if (not $file =~ m!^/!) {
|
|
$file = "$RUNNING_DIR/$file";
|
|
}
|
|
if (-f $file) {
|
|
local * PIDFILE;
|
|
open PIDFILE, '<', $file;
|
|
my $pid = <PIDFILE>;
|
|
close PIDFILE;
|
|
if (defined $pid) {
|
|
chomp $pid;
|
|
return $pid;
|
|
}
|
|
}
|
|
}
|
|
|
|
sub is_running {
|
|
my $data = shift;
|
|
my $ret;
|
|
if (-f "$RUNNING_DIR/$FULL_SERVICE.oneshot") {
|
|
return 1;
|
|
} elsif (defined $data->{'Service.PIDFile'}) {
|
|
my $pid = get_pid($data->{'Service.PIDFile'});
|
|
if (defined $pid) {
|
|
$ret = kill 0, $pid;
|
|
}
|
|
} else {
|
|
my $path = get_pid("$FULL_SERVICE.name");
|
|
if (defined $path) {
|
|
if (pidof($path)) {
|
|
$ret = 1;
|
|
}
|
|
} else {
|
|
my $pid = get_pid("$FULL_SERVICE.pid");
|
|
if (defined $pid) {
|
|
$ret = kill 0, $pid;
|
|
}
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
if ($COMMAND eq 'is-active' or $COMMAND eq 'status') {
|
|
if (is_running($data)) {
|
|
print "active\n";
|
|
exit;
|
|
}
|
|
print "inactive\n";
|
|
exit 3;
|
|
}
|
|
|
|
sub exec_stop_pre {
|
|
my ($data) = @_;
|
|
if (defined $data->{'Socket.ExecStopPre'}) {
|
|
for my $x (@{ $data->{'Socket.ExecStopPre'} }) {
|
|
log_command("Running stop pre [$x]\n");
|
|
system $x;
|
|
}
|
|
}
|
|
}
|
|
sub exec_stop_post {
|
|
my ($data) = @_;
|
|
if (defined $data->{'Service.ExecStopPost'}) {
|
|
for my $x (@{ $data->{'Service.ExecStopPost'} }) {
|
|
log_command("Running stop post [$x]\n");
|
|
system $x;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($TYPE eq 'target' and not defined $data->{'Unit.Wants'} and not defined $data->{'Unit.Requires'}) {
|
|
warn "No Unit.Wants/.wants/Unit.Requires/.requires list for target [$SERVICE]\n";
|
|
exit 8;
|
|
}
|
|
|
|
if ($COMMAND eq 'restart') {
|
|
if ($TYPE ne 'service' or is_running($data)) {
|
|
system $0, 'stop', $FULL_SERVICE;
|
|
}
|
|
my $ret = system $0, 'start', $FULL_SERVICE;
|
|
exit $ret >> 8;
|
|
}
|
|
|
|
sub pids_went_away_timeout {
|
|
my $timeout = shift;
|
|
my $start = time;
|
|
while (kill 0, @_) {
|
|
if (time - $start > $timeout) {
|
|
log_command(sprintf(" ** pid(s) [%s] not killed within %d s\n", join(', ', @_), $timeout));
|
|
return 0;
|
|
}
|
|
sleep 0.1;
|
|
}
|
|
log_command(sprintf(" ** pid(s) [%s] got killed after %.2f s\n", join(', ', @_), time - $start));
|
|
return 1;
|
|
}
|
|
|
|
sub stop_pids {
|
|
my @pids = @_;
|
|
kill 15, @pids;
|
|
if (not pids_went_away_timeout(5, @pids)) {
|
|
kill 9, @pids;
|
|
if (not pids_went_away_timeout(5, @pids)) {
|
|
log_command("Failed to kill [@pids] even with 9\n");
|
|
warn "Failed to kill [@pids].\n";
|
|
return 1;
|
|
}
|
|
log_command("Killed [@pids] with 9\n");
|
|
} else {
|
|
log_command("Killed [@pids] with 15\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if ($COMMAND eq 'stop') {
|
|
my $mainpid;
|
|
if (defined $data->{'Service.PIDFile'}) {
|
|
$mainpid = get_pid($data->{'Service.PIDFile'});
|
|
} else {
|
|
$mainpid = get_pid("$FULL_SERVICE.pid");
|
|
}
|
|
if (defined $data->{'Service.ExecStop'}) {
|
|
for my $x (@{ $data->{'Service.ExecStop'} }) {
|
|
my $runit = 1;
|
|
if (defined $mainpid) {
|
|
$x =~ s!\$MAINPID\b|\$\{MAINPID\}!$mainpid!g;
|
|
} elsif ($x =~ /\$MAINPID\b|\$\{MAINPID\}/) {
|
|
$runit = 0;
|
|
log_command("Service [$FULL_SERVICE] would like to stop via ExecStop [$x] but we have no pid file, skipping.\n");
|
|
}
|
|
if ($runit) {
|
|
log_command("Running stop [$x]\n");
|
|
system $x;
|
|
sleep 1;
|
|
}
|
|
}
|
|
}
|
|
my $ret = undef;
|
|
if ($TYPE eq 'target') {
|
|
# noop
|
|
} elsif (defined $data->{'Service.Type'} and $data->{'Service.Type'} eq 'oneshot') {
|
|
# noop
|
|
} elsif ($TYPE eq 'socket') {
|
|
my $pids = `/usr/sbin/fuser $data->{'Socket.ListenStream'} 2> /dev/null`;
|
|
if (defined $pids) {
|
|
chomp $pids;
|
|
$pids =~ s/^\s+//;
|
|
my @pids = split /\s+/, $pids;
|
|
if (@pids) {
|
|
exec_stop_pre($data);
|
|
log_command("Will kill [@pids] as fuser [$data->{'Socket.ListenStream'}] of [$FULL_SERVICE]\n");
|
|
$ret = stop_pids(@pids);
|
|
}
|
|
}
|
|
} elsif (defined $data->{'Service.PIDFile'}) {
|
|
my $pid = get_pid($data->{'Service.PIDFile'});
|
|
if (defined $pid) {
|
|
log_command("Will kill [$pid] found in Service.PIDFile of [$FULL_SERVICE]\n");
|
|
$ret = stop_pids($pid);
|
|
}
|
|
} else {
|
|
my $path = get_pid("$FULL_SERVICE.name");
|
|
if (defined $path) {
|
|
if (my @pids = pidof($path)) {
|
|
log_command("Will kill [@pids] as pidof [$path] found in [$FULL_SERVICE.name]\n");
|
|
$ret = stop_pids(@pids);
|
|
} else {
|
|
warn "No pidof for [$path] found in [$FULL_SERVICE.name].\n";
|
|
}
|
|
} else {
|
|
my $pid = get_pid("$FULL_SERVICE.pid");
|
|
if (defined $pid) {
|
|
log_command("Will kill [$pid] found in [$FULL_SERVICE.pid]\n");
|
|
$ret = stop_pids($pid);
|
|
} else {
|
|
warn "No pid and no name for [$FULL_SERVICE].\n";
|
|
}
|
|
}
|
|
}
|
|
exec_stop_post($data);
|
|
unlink "$RUNNING_DIR/$FULL_SERVICE.pid", "$RUNNING_DIR/$FULL_SERVICE.name", "$RUNNING_DIR/$FULL_SERVICE.oneshot";
|
|
if (defined $data->{'Unit.Wants'}) {
|
|
for my $x (@{ $data->{'Unit.Wants'} }) {
|
|
system $0, 'stop', $x;
|
|
}
|
|
}
|
|
if (defined $data->{'Unit.Requires'}) {
|
|
for my $x (@{ $data->{'Unit.Requires'} }) {
|
|
system $0, 'stop', $x;
|
|
}
|
|
}
|
|
exit ( defined $ret ? $ret : 0 );
|
|
}
|
|
|
|
sub add_runuser {
|
|
my ($data, $cmd) = @_;
|
|
if (defined $data->{'Service.User'}) {
|
|
unshift @$cmd, '-u', $data->{'Service.User'}, '--';
|
|
if (defined $data->{'Service.Group'}) {
|
|
unshift @$cmd, '-g', $data->{'Service.Group'};
|
|
}
|
|
unshift @$cmd, '/usr/sbin/runuser';
|
|
}
|
|
}
|
|
|
|
if ($COMMAND eq 'start') {
|
|
my (@starting_stack, %starting_stack);
|
|
if (defined $ENV{_SYSTEMCTL_LITE_STARTING}) {
|
|
@starting_stack = split /:/, $ENV{_SYSTEMCTL_LITE_STARTING};
|
|
@starting_stack{@starting_stack} = ();
|
|
$ENV{_SYSTEMCTL_LITE_STARTING} .= ":$FULL_SERVICE";
|
|
} else {
|
|
$ENV{_SYSTEMCTL_LITE_STARTING} = $FULL_SERVICE;
|
|
}
|
|
|
|
if ($TYPE eq 'service' and is_running($data)) {
|
|
log_command("Service [$FULL_SERVICE] already found running, not starting again.\n");
|
|
exit;
|
|
}
|
|
if (defined $data->{'Service.PIDFile'}) {
|
|
log_command("Service [$FULL_SERVICE] defines PIDFile [$data->{'Service.PIDFile'}], unlinking it before start\n");
|
|
unlink $data->{'Service.PIDFile'};
|
|
}
|
|
if (defined $data->{'Unit.Wants'}) {
|
|
for my $x (@{ $data->{'Unit.Wants'} }) {
|
|
if (exists $starting_stack{$x}) {
|
|
log_command("Skipping start of [$x], we are already in the process of starting it.\n");
|
|
next;
|
|
}
|
|
my @cmd = ($0, 'start', $x);
|
|
log_command("Running [@cmd] for Unit.Wants of [$FULL_SERVICE]\n");
|
|
system @cmd;
|
|
}
|
|
}
|
|
if (defined $data->{'Unit.Requires'}) {
|
|
for my $x (@{ $data->{'Unit.Requires'} }) {
|
|
if (exists $starting_stack{$x}) {
|
|
log_command("Skipping start of [$x], we are already in the process of starting it.\n");
|
|
next;
|
|
}
|
|
my @cmd = ($0, 'start', $x);
|
|
log_command("Running [@cmd] for Unit.Requires of [$FULL_SERVICE]\n");
|
|
if (system @cmd) {
|
|
log_command("Failed to start [$x], aborting start\n");
|
|
exit 1;
|
|
}
|
|
}
|
|
}
|
|
if (defined $data->{'Service.PIDFile'} and is_running($data)) {
|
|
log_command("Service [$FULL_SERVICE] defines PIDFile [$data->{'Service.PIDFile'}] and it seems to have already started, not starting again.\n");
|
|
exit;
|
|
}
|
|
|
|
if ($TYPE eq 'target') {
|
|
exit;
|
|
}
|
|
if ($TYPE eq 'socket' and -S $data->{'Socket.ListenStream'}) {
|
|
my $out = `/usr/sbin/fuser $data->{'Socket.ListenStream'} 2> /dev/null`;
|
|
if (defined $out and $out ne '') {
|
|
log_command("Service [$FULL_SERVICE] already found active on socket [$data->{'Socket.ListenStream'}], not starting again.\n");
|
|
exit;
|
|
}
|
|
}
|
|
if (defined $data->{'Service.Type'} and $data->{'Service.Type'} eq 'dbus') {
|
|
my @cmd = ($0, 'start', 'dbus.socket');
|
|
log_command("Running [@cmd] for Service.Type dbus\n");
|
|
system @cmd;
|
|
}
|
|
if (defined $data->{'Service.ExecStartPre'}) {
|
|
for my $x (@{ $data->{'Service.ExecStartPre'} }) {
|
|
my $can_fail = 0;
|
|
if ($x =~ s/^-//) {
|
|
$can_fail = 1;
|
|
}
|
|
my @cmd = split /\s+/, $x;
|
|
if (not $data->{'Service.PermissionsStartOnly'} or $data->{'Service.PermissionsStartOnly'} =~ /^(false|0)$/i) {
|
|
add_runuser($data, \@cmd);
|
|
}
|
|
no warnings 'uninitialized';
|
|
log_command("Running start pre [@cmd]\n");
|
|
if (system @cmd and not $can_fail) {
|
|
exit 1;
|
|
}
|
|
}
|
|
}
|
|
my @paths;
|
|
if ($TYPE eq 'socket') {
|
|
my $service = $SERVICE;
|
|
if (defined $data->{'Socket.Accept'} and $data->{'Socket.Accept'} eq 'true') {
|
|
$service =~ s/\.socket$/\@.service/;
|
|
@paths = ( '/bin/systemctl-socket-daemon', $data->{'Socket.ListenStream'}, $data->{'Socket.SocketMode'} // '0666', $service );
|
|
} else {
|
|
$service =~ s/\.socket$/\.service/;
|
|
my $ret = system "$0 is-active $service > /dev/null";
|
|
if (($ret >> 8) == 0) {
|
|
log_command("Service [$service] is already running for [$SERVICE]\n");
|
|
exit;
|
|
}
|
|
@paths = ( $0, 'start', $service );
|
|
}
|
|
} else {
|
|
@paths = get_exec_start($data);
|
|
}
|
|
my $first_path = $paths[0];
|
|
|
|
my $ENV = '';
|
|
if (exists $data->{'Service.EnvironmentFile'}) {
|
|
for my $e (@{ $data->{'Service.EnvironmentFile'} }) {
|
|
my $error_fail = 1;
|
|
if ($e =~ s/^-//) {
|
|
$error_fail = 0;
|
|
}
|
|
my $fh = new IO::File($e);
|
|
if (not defined $fh) {
|
|
if ($error_fail) {
|
|
warn "Error reading EnvironmentFile [$e]: $!\n";
|
|
exit 5;
|
|
}
|
|
} else {
|
|
while (my $line = <$fh>) {
|
|
chomp $line;
|
|
next if $line =~ /^\s*(#|$)/;
|
|
$ENV .= "export $line; ";
|
|
}
|
|
$fh->close();
|
|
}
|
|
}
|
|
}
|
|
if (defined $data->{'Service.Environment'}) {
|
|
my $x = $data->{'Service.Environment'};
|
|
$x =~ s/^"(.*)"$/$1/;
|
|
$ENV .= "export $x; ";
|
|
}
|
|
add_runuser($data, \@paths);
|
|
log_command("Running [$ENV@paths]\n");
|
|
if (defined $data->{'Service.Type'} and $data->{'Service.Type'} eq 'oneshot') {
|
|
system "$ENV@paths";
|
|
if (defined $data->{'Service.RemainAfterExit'} and $data->{'Service.RemainAfterExit'} eq 'yes') {
|
|
local * PIDFILE;
|
|
open PIDFILE, '>', "$RUNNING_DIR/$FULL_SERVICE.oneshot";
|
|
close PIDFILE;
|
|
}
|
|
exit;
|
|
}
|
|
my $pid = fork();
|
|
die "Failed to fork for [@_]\n" if not defined $pid;
|
|
if ($pid == 0) {
|
|
open(STDOUT, '>>', $SYSTEMCTL_LOG);
|
|
open(STDERR, '>>', $SYSTEMCTL_LOG);
|
|
open(STDIN, '<', '/dev/null');
|
|
POSIX::setsid();
|
|
exec "$ENV@paths";
|
|
}
|
|
if (defined $data->{'Service.PIDFile'}) {
|
|
log_command("Service [$FULL_SERVICE] defines PIDFile [$data->{'Service.PIDFile'}], not marking pid\n");
|
|
unlink "$RUNNING_DIR/$FULL_SERVICE.pid";
|
|
my $i = 0;
|
|
while (not -f $data->{'Service.PIDFile'}) {
|
|
sleep 0.2;
|
|
$i++;
|
|
if ($i > 250) {
|
|
log_command("Starting [$FULL_SERVICE] did not create PIDFile [$data->{'Service.PIDFile'}]\n");
|
|
exit 10;
|
|
}
|
|
}
|
|
my $pid = get_pid($data->{'Service.PIDFile'});
|
|
if (not defined $pid or not kill 0, $pid) {
|
|
log_command("Starting [$FULL_SERVICE] created PIDFile [$data->{'Service.PIDFile'}] but the process is not running\n");
|
|
exit 11;
|
|
}
|
|
} else {
|
|
local * PIDFILE;
|
|
open PIDFILE, '>', "$RUNNING_DIR/$FULL_SERVICE.pid";
|
|
print PIDFILE "$pid\n";
|
|
close PIDFILE;
|
|
log_command("Marked pid [$pid] for [$FULL_SERVICE]\n");
|
|
sleep 1;
|
|
if (defined $data->{'Service.Type'} and $data->{'Service.Type'} ne 'simple' and $data->{'Service.Type'} ne 'notify' and pidof($first_path)) {
|
|
local * PIDFILE;
|
|
open PIDFILE, '>', "$RUNNING_DIR/$FULL_SERVICE.name";
|
|
print PIDFILE "$first_path\n";
|
|
close PIDFILE;
|
|
log_command("Marked process name [$first_path] for [$FULL_SERVICE]\n");
|
|
}
|
|
}
|
|
if (defined $data->{'Service.Type'}
|
|
and $data->{'Service.Type'} eq 'dbus'
|
|
and defined $data->{'Service.BusName'}) {
|
|
my $busname = $data->{'Service.BusName'};
|
|
my $objectpath = $busname;
|
|
$objectpath =~ s!^|\.!/!g;
|
|
for (0 .. 10) {
|
|
system "/usr/bin/dbus-send --system --type=method_call --print-reply --dest=$busname $objectpath org.freedesktop.DBus.Introspectable.Introspect > /dev/null";
|
|
exit if ($? >> 8) == 0;
|
|
sleep 1;
|
|
}
|
|
exit 9;
|
|
}
|
|
exit;
|
|
}
|
|
|
|
if ($COMMAND eq 'is-enabled') {
|
|
if (-e "$ENABLED_DIR/$FULL_SERVICE") {
|
|
print "enabled\n";
|
|
exit 0;
|
|
}
|
|
print "disabled\n";
|
|
exit 1;
|
|
}
|
|
|
|
if ($COMMAND eq 'enable') {
|
|
if (not -d $ENABLED_DIR) {
|
|
mkdir $ENABLED_DIR;
|
|
}
|
|
local * FILE;
|
|
open FILE, '>', "$ENABLED_DIR/$FULL_SERVICE" or die "Error creating $ENABLED_DIR/$FULL_SERVICE: $!\n";
|
|
exit;
|
|
}
|
|
|
|
if ($COMMAND eq 'disable') {
|
|
unlink "$ENABLED_DIR/$FULL_SERVICE" or die "Error removing $ENABLED_DIR/$FULL_SERVICE: $!\n";
|
|
exit;
|
|
}
|
|
|
|
die "Unknown command [$COMMAND].\n";
|
|
|
|
1;
|
|
|