Prompt
[NWFTP]</p>
<p>mainprog=ftpget.pl</p>
<p>datasource=ftpssite.com/pub</p>
<p>hours=*</p>
<p>timing=5,35</p>
<p>conversion=toscv</p>
<p>outputname=kansas</p>
<p>mylog=ftpgetlog</p>
<p>[PFIELD]</p>
<p>mainprog=httpget.pl</p>
<p>datasource=http://xyz.com?dd=foo</p>
<p>hours=0,4,8,12,16,20,24</p>
<p>timing=4</p>
<p>conversion=none</p>
<p>outputname=fran</p>
<p>mylog=pfieldlog</p>
Each bracketed section names a job; the key/value pairs under that name define the command to run and the arguments that will be forwarded to it. The launcher reads this file into two data structures: a hash of job names to zero (placeholder for the PID) and a hash keyed by “job|parameter” that holds the value.
The main loop prints a menu showing every job and whether it is currently running. It then waits for user input. If the user selects a job and presses “S” the program calls a routine that forks a child and execs the desired script. If “K” is pressed, the launcher sends a SIGTERM to the job’s PID, falls back to SIGKILL if the process does not die, and logs the action. Pressing “L” shows the log file for that job.
Because the launcher never needs to know the exit status of its children, it sets Prompt
#!/usr/bin/perl</p>
<p>use strict;</p>
<p>use warnings;</p>
<p>use Errno qw(ESRCH EPERM);</p>
<p>use POSIX qw(strftime);</p>
<p>$SIG{CHLD} = 'IGNORE';</p>
<h1>Load the configuration file</h1>
my $config_file = 'config';</p>
<p>open my $fh, '
<p>my (%jobs, %settings, $current_job);</p>
<p>while (my $line = ) {</p>
<p>
chomp $line;</p>
<p>
next if $line =~ /^\s*$/;
# skip blanks</p>
<p>
if ($line =~ /^\[(\w+)\]$/) {
# new job section</p>
<p>
$current_job = $1;</p>
<p>
$jobs{$current_job} = 0;
# placeholder for PID</p>
<p>
next;</p>
<p>
}</p>
<p>
if ($line =~ /^(\w+)\s<em>=\s</em>(.*)$/) {
# key/value pair</p>
<p>
my ($key, $value) = ($1, $2);</p>
<p>
$settings{"$current_job|$key"} = $value;</p>
<p>
}</p>
<p>}</p>
<p>close $fh;</p>
<h1>Main interactive loop</h1>
while (1) {</p>
<p>
system 'clear';</p>
<p>
print "Choose a job:
";</p>
<p>
my $index = 1;</p>
<p>
foreach my $job (sort keys %jobs) {</p>
<p>
# Verify if the stored PID is still alive</p>
<p>
my $pid = $jobs{$job};</p>
<p>
my $alive = $pid && kill 0, $pid;</p>
<p>
$jobs{$job} = 0 unless $alive;</p>
<p>
my $status = $alive ? "[running ($pid)]" : "[not running]";</p>
<p>
printf "%2d) %-10s %s
", $index++, $job, $status;</p>
<p>
}</p>
<p>
print "
Enter number (or q to quit): ";</p>
<p>
my $choice = <STDIN>;</p>
<p>
last if defined $choice && $choice =~ /^\s*q/i;</p>
<p>
chomp $choice;</p>
<p>
next unless $choice =~ /^\d+$/ && $choice >= 1 && $choice
<p>
my $selected_job = (sort keys %jobs)[$choice-1];</p>
<p>
job_menu($selected_job);</p>
<p>}</p>
<h1>Subroutine that presents a submenu for the selected job</h1>
sub job_menu {</p>
<p>
my ($job) = @_;</p>
<p>
print "
---- $job -
";</p>
<p>
foreach my $k (sort keys %settings) {</p>
<p>
my ($j, $param) = split /\|/, $k, 2;</p>
<p>
next unless $j eq $job;</p>
<p>
printf "%s: %s
", $param, $settings{$k};</p>
<p>
}</p>
<p>
my $pid = $jobs{$job};</p>
<p>
my $status = $pid ? "[running ($pid)]" : "[not running]";</p>
<p>
print "
$status
";</p>
<p>
print "Options: (S)tart (K)ill (L)og (B)ack
";</p>
<p>
print "Choose: ";</p>
<p>
my $action = <STDIN>;</p>
<p>
chomp $action;</p>
<p>
if ($action =~ /^\s*s$/i) {</p>
<p>
start_job($job);</p>
<p>
} elsif ($action =~ /^\s*k$/i) {</p>
<p>
kill_job($job);</p>
<p>
} elsif ($action =~ /^\s*l$/i) {</p>
<p>
show_log($job);</p>
<p>
}</p>
<p>}</p>
<h1>Start a job</h1>
sub start_job {</p>
<p>
my ($job) = @_;</p>
<p>
print "Starting $job...
";</p>
<p>
my $pid = fork_job($job);</p>
<p>
if ($pid) {</p>
<p>
$jobs{$job} = $pid;</p>
<p>
# Give the child a moment to fail if it will</p>
<p>
sleep 2;</p>
<p>
unless (kill 0, $pid) {</p>
<p>
print "Failed to start $job.
";</p>
<p>
$jobs{$job} = 0;</p>
<p>
}</p>
<p>
} else {</p>
<p>
# In child: the fork_job routine does everything</p>
<p>
}</p>
<p>}</p>
<h1>Kill a running job</h1>
sub kill_job {</p>
<p>
my ($job) = @_;</p>
<p>
return unless $pid;</p>
<p>
my $result = kill 1, $pid;
# SIGTERM</p>
<p>
$result ||= kill 9, $pid;
# fallback to SIGKILL</p>
<p>
my $alive = kill 0, $pid;</p>
<p>
$jobs{$job} = 0 unless $alive;</p>
<p>
my $logfile = $settings{"$job|mylog"};</p>
<p>
open my $log, '>>', $logfile or warn "Can't write to $logfile: $!";</p>
<p>
my $now = strftime("%Y-%m-%d %H:%M:%S", localtime);</p>
<p>
print $log $alive ? "killed $job $pid $now
"</p>
<p>
: "couldn't kill $job $pid $now
";</p>
<p>
close $log;</p>
<p>
print $alive ? "Job $job stopped.
" : "Job $job not running.
";</p>
<p>}</p>
<h1>Show a job's log file</h1>
sub show_log {</p>
<p>
my ($job) = @_;</p>
<p>
open my $log, '
<p>
while (my $line = ) {</p>
<p>
print $line;</p>
<p>
}</p>
<p>
close $log;</p>
<p>
print "
Press Enter to return.
";</p><STDIN>;</p>
<p>}</p>
<h1>Fork a child and exec the target script</h1>
sub fork_job {</p>
<p>
my ($job) = @_;</p>
<p>
while (1) {</p>
<p>
my $pid = fork();</p>
<p>
if (defined $pid) {
# parent</p>
<p>
return $pid;</p>
<p>
} elsif ($! == EAGAIN) {
# retry if temporary failure</p>
<p>
sleep 3;</p>
<p>
next;</p>
<p>
} else {
# permanent error</p>
<p>
warn "Failed to fork for $job: $!
";</p>
<p>
return 0;</p>
<p>
}</p>
<p>
# Child</p>
<p>
my $prog = $settings{"$job|mainprog"};</p>
<p>
my @args = ($job);</p>
<p>
foreach my $k (sort keys %settings) {</p>
<p>
my ($j, $param) = split /\|/, $k, 2;</p>
<p>
next unless $j eq $job;</p>
<p>
push @args, "$param=$settings{$k}";</p>
<p>
}</p>
<p>
open my $log, '>>', $settings{"$job|mylog"} or warn "Can't write to log: $!";</p>
<p>
print $log "Running $prog with args: @args
";</p>
<p>
close $log;</p>
<p>
exec $prog, @args;</p>
<p>
# If exec fails, log and exit</p>
<p>
open my $log_fail, '>>', $settings{"$job|mylog"} or warn "Can't write to log: $!";</p>
<p>
print $log_fail "Failed to exec $prog for $job: $!
";</p>
<p>
close $log_fail;</p>
<p>
exit 0;</p>
<p>
}</p>
<p>}</p>
This launcher gives you a full‑featured way to manage short‑lived scripts without installing heavyweight daemon supervisors. By keeping the code in a single, readable Perl file you retain control over how jobs are started, stopped, and logged. The use of
No comments yet. Be the first to comment!