forked from DOCGroup/autobuild
-
Notifications
You must be signed in to change notification settings - Fork 0
/
autobuild_spawner.pl
executable file
·209 lines (174 loc) · 5.29 KB
/
autobuild_spawner.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Spec::Functions;
## Get Environment variables used for locating
## directory containing script to run as child
## as well as storing child's process id file in
use Env qw(@JENKINS_HOME);
#print "@JENKINS_HOME\n";
my $PIDFILE = "@JENKINS_HOME\/buildPid.txt";
print "PIDFILE Loc: $PIDFILE\n";
## This is a parent process, so store own pid as parent
my $childs_pid;
my $childs_proc;
my $cmd = catfile($ENV{AUTOBUILD_ROOT}, 'autobuild.pl');
my $Win_cmd = "perl $cmd @ARGV";
my $debug = 1;
my $wait_pid_time_sec = 60 * 5; # Every 5 minutes
package Parent;
use POSIX ":sys_wait_h";
use POSIX "setsid";
sub create_pid_file {
print "Creating pid file\n";
my($pid) = @_;
open FILE, '+>'.$PIDFILE;
print FILE "$pid";
close FILE;
}
sub delete_pid_file {
unlink $PIDFILE;
}
sub get_childs_pid_from_file {
open FILE, $PIDFILE;
my @pids;
while(<FILE>) {
chomp;
push @pids, $_;
}
close FILE;
return $pids[0];
}
sub step_child_still_running {
my ($childs_pid) = @_;
#By sending SIGZERO to kill, simply sees if the
#process exists and can accept signals (does not
#actually kill)
return kill 0, $childs_pid;
}
sub start_child_process {
#die "cannot execute cmd: $cmd" unless -x $cmd;
# print "$^O\n";
if ($^O eq 'MSWin32') # Windows
{
require Win32::Process;
#Need to tell process which exe to use to run the cmd
#so find which perl is being used and format path to
#pass to process create
my $perl = `where perl`;
$perl =~ s/\\/\\\\/g;
chomp $perl;
print "Windows Perl location $perl\n";
print "Windows command: $Win_cmd\n";
#Child is either created or fails to spawn and exits
#thus ending child's logical processing
Win32::Process::Create($childs_proc, $perl, $Win_cmd, 1, 0, ".") ||
print "Could not spawn child (Windows): $!\n";
#parent
$childs_pid = $childs_proc->GetProcessID();
print "Windows: Child created with pid: $childs_pid\n";
}
else #Unix
{
$SIG{CHLD} = 'IGNORE';
$childs_pid = fork();
unless (defined $childs_pid)
{
print "Could not spawn child (Unix): $!\n";
}
if ($childs_pid == 0) #child
{
unless ($debug)
{
open STDIN, "<", "/dev/null" or die "Can't read /dev/null: $!";
open STDOUT, ">", "/dev/null" or die "Can't write /dev/null: $!";
}
setsid() or warn "setsid cannot start a new session: $!";
unless ($debug)
{
open STDERR, '>^STDOUT' or die "Can't dup stdout: $!";
}
local $| = 1;
print "Linux command: $cmd\n";
#Child either exec's or exits thus ending child's logical processing
unless (exec($cmd, @ARGV))
{
print "Could not start child: $cmd: $!\n";
CORE::exit(0);
}
}
#parent
$SIG{CHLD} = 'DEFAULT';
}
#parent
Parent::create_pid_file($childs_pid);
return $childs_pid;
}
sub wait_on_child {
my ($childs_pid) = @_;
my $child_exit_status;
while (1) {
## Wait on child to finish using waitpid with WNOHANG
## which returns:
## -1 - on error
## 0 - if child exists but has not yet changed state
## pid - when child with pid's state has changed
my $datestring = localtime();
print "Parent (PID:$$) waiting on child (PID: $childs_pid) at $datestring\n";
my $res = waitpid($childs_pid, WNOHANG);
if ($res == -1) {
my $child_exit_status = $? >> 8;
print "Child exited with ERROR and status ", $child_exit_status, "\n";
exit($child_exit_status);
}
if ($res != 0) {
my $child_exit_status = $? >> 8;
print "Child exited with status ", $child_exit_status, "\n";
return $child_exit_status;
}
sleep ($wait_pid_time_sec);
}
}
sub join_childs_thread {
wait();
}
package ReturnCode::Type;
## Use these for exit status return values
use constant {
EXIT_GOOD => 0,
EXIT_ERROR => 1,
EXIT_STILL_RUNNING => 3
};
package main;
print "THIS IS A PARENT PROCESS WITH PID: $$\n";
if(-e $PIDFILE) {
## The existence of a PIDFILE means this parent process is
## not the parent of the current, possibly running child.
## Open PIDFILE and extract child's process id
my $step_childs_pid = Parent::get_childs_pid_from_file();
print "PID FROM FILE: $step_childs_pid\n";
## Because this process is a step-parent, can't waitpid on
## child process, so instead simply check if it is still running
while(Parent::step_child_still_running($step_childs_pid)){
print "Child still running, but we don't own it. Wait for it to finish.\n";
sleep($wait_pid_time_sec);
}
print "Child's pid file still present, but no longer running!\n";
Parent::delete_pid_file();
print "Deleted child's pid file\n";
}
# PARENT PROCESSING CONTINUES HERE
# (Child processing all done in process started by star_child_process)
## PIDFILE doesn't exist, so this parent process
## can spawn the child process.
print "PID FILE DOESN'T EXIST, CREATE CHILD PROCESS\n";
$childs_pid = Parent::start_child_process();
Parent::wait_on_child($childs_pid);
print "About to wait on child thread to finish shutting down\n";
Parent::join_childs_thread();
print "Wait complete\n";
Parent::delete_pid_file();
print "Deleted child's pid file\n";
print "Exiting with status ", ReturnCode::Type->EXIT_GOOD, "\n";
exit(ReturnCode::Type->EXIT_GOOD);