2 use Cwd
qw(abs_path getcwd);
3 use Getopt
::Long
qw(:config bundling);
7 use File
::Spec
::Functions
qw(tmpdir);
8 use File
::Temp
qw(tempfile tempdir);
9 use Fatal
qw(:void copy rename move chdir mkdir rmdir unlink rmtree);
16 use Verbose
qw(:2 copy rename move utime chdir mkdir mkpath unlink rmtree tempfile tempdir);
18 our @ISA = qw(Exporter);
20 Verbose GetOptions pod2usage shellwords
21 $datadir $libexecdir @common_options $help $raw_errors
22 child_error runval runval_raw runstr runstr_err runval_in runval_infile runval_outfile
24 get_stage set_stage set_debug_level set_partial get_abort_cause get_patch update_loaded
25 get_debug_output get_conflicts get_raw_conflicts get_short_description
27 abs_path getcwd basename dirname tmpdir
28 copy rename move utime chdir mkdir mkpath unlink rmtree tempfile tempdir
29 print_abort_error print_abort_code
32 our ($datadir, $libexecdir) = qw(KSPLICE_DATA_DIR KSPLICE_LIBEXEC_DIR);
36 our $printed_abort_code;
38 our @common_options = (
40 "raw-errors" => \
$raw_errors,
41 "version" => sub { print "Ksplice version PACKAGE_VERSION\n"; exit(0); },
42 "api-version" => sub { print "KSPLICE_API_VERSION\n"; exit(0); },
43 "verbose|v:+" => \
$Verbose::level
,
44 "quiet|q:+" => sub { $Verbose::level
-= $_[1]; },
48 die @_ if $^S
|| !$raw_errors;
50 if(!$printed_abort_code) {
51 print STDERR
"OTHER\n$msg";
61 print STDERR
"Failed to exec child\n";
62 } elsif(($?
& 127) != 0) {
63 print STDERR
"Child exited with signal ", ($?
& 127), ($?
& 128) ?
" (core dumped)\n" : "\n";
64 } elsif($?
>> 8 != 0) {
65 print STDERR
"Child exited with status ", $?
>> 8, "\n";
74 if(runval_raw
(@cmd) != 0) {
76 die "Failed during: @cmd\n";
83 print "+ @cmd\n" if($Verbose::level
>= 1);
85 my $pid = open3
(fileno STDIN
, ">&STDOUT", ">/dev/null", @cmd);
95 print "+ @cmd\n" if($Verbose::level
>= 1);
99 open3
(fileno STDIN
, \
*PIPE
, ">/dev/null", @cmd);
101 open PIPE
, '-|', @cmd or die "Can't run @cmd: $!";
104 close PIPE
or $! == 0 or die "Can't run @cmd: $!";
110 print "+ @cmd\n" if($Verbose::level
>= 1);
112 my $pid = open3
(fileno STDIN
, '>&STDOUT', \
*ERROR
, @cmd);
116 print STDERR
$error unless $raw_errors;
122 print "+ @cmd <<'EOF'\n${in}EOF\n" if($Verbose::level >= 1);
125 open3(\*WRITE, ">&STDOUT
", ">/dev/null
", @cmd);
127 open(WRITE, '|-', @cmd) or die "Can
't run @cmd: $!";
130 close(WRITE) or $! == 0 or die "Can't run
@cmd: $!";
132 die "Failed during
: @cmd";
137 my ($infile, @cmd) = @_;
138 print "+ @cmd < $infile\n" if($Verbose::level >= 1);
140 open(INFILE, '<', $infile) or die "Can
't open $infile: $!";
143 $pid = open3('<&INFILE
', '>&STDOUT
', ">/dev/null", @cmd);
145 $pid = open2('>&STDOUT
', '<&INFILE
', @cmd);
149 die "Failed during: @cmd";
154 my ($outfile, @cmd) = @_;
155 print "+ @cmd > $outfile\n" if($Verbose::level >= 1);
157 open(OUTFILE, '>', $outfile) or die "Can't
open $outfile: $!";
160 $pid = open3('</dev/null', '>&OUTFILE', ">/dev/null
", @cmd);
162 $pid = open2('>&OUTFILE', '</dev/null', @cmd);
166 die "Failed during
: @cmd";
175 my $tmpdir = tempdir('ksplice-tmp-XXXXXX', TMPDIR => 1, CLEANUP => 1);
176 runval("tar
", "-C
", $tmpdir, "--force
-local", "-zxf
", $file);
177 my ($ksplice) = glob("$tmpdir/*/");
178 chop($ksplice); # remove the trailing slash
184 if(! -d "/sys/module
") {
185 die "/sys
not mounted?
\n";
187 my $update = "ksplice_
$kid";
188 if (-d "/sys/kernel
/ksplice/$kid") {
189 return "/sys/kernel
/ksplice/$kid";
191 if (-d "/sys/module
/$update/ksplice
") {
192 return "/sys/module
/$update/ksplice
";
199 return defined(get_sysfs($kid));
205 open(INPUT, "<", $file) or die $!;
210 my ($file, $string) = @_;
212 open(INPUT, ">", $file) or die $!;
217 my ($kid, $attr) = @_;
218 my $sysfs = get_sysfs($kid);
219 return undef if (!defined($sysfs));
220 return read_file("$sysfs/$attr");
224 my ($kid, $attr, $string) = @_;
225 my $sysfs = get_sysfs($kid);
226 return undef if (!defined($sysfs));
227 write_file("$sysfs/$attr", $string);
230 sub get_debug_output {
231 my ($kid, $debugfs_out) = @_;
232 my $update = "ksplice_
$kid";
234 (undef, $debugfs_out) = tempfile('ksplice-debug-XXXXXX', DIR => tmpdir());
236 if (runval_raw("grep", "-qFx
", "nodev
\tdebugfs
", "/proc/filesystems
") == 0) {
237 my $debugfsdir = tempdir('ksplice-debugfs-XXXXXX', TMPDIR => 1);
238 runval(qw(mount -t debugfs debugfs), $debugfsdir);
239 if (-e
"$debugfsdir/$update") {
240 copy
("$debugfsdir/$update", $debugfs_out);
242 runval
(qw(umount), $debugfsdir);
245 } elsif ($?
>> 8 == 1) {
255 chomp(my $result = read_sysfs
($kid, "stage"));
259 sub get_abort_cause
{
261 chomp(my $result = read_sysfs
($kid, "abort_cause"));
267 chomp(my $conflicts = read_sysfs
($kid, "conflicts"));
268 my @conflicts = split('\n', $conflicts);
270 foreach my $conflict (@conflicts) {
271 my ($name, $pid, @symbols) = split(' ', $conflict);
273 $out .= "Process $name(pid $pid) is using the following symbols changed by update $kid:\n";
274 foreach my $symbol (@symbols) {
275 $out .= " $symbol\n";
281 sub get_raw_conflicts
{
283 my $conflicts = read_sysfs
($kid, "conflicts");
289 my $result = read_file
("/var/run/ksplice/updates/$kid/patch");
293 sub get_short_description
{
295 open(INPUT
, "<", "/var/run/ksplice/updates/$kid/description") or return undef;
296 my $result = <INPUT
>;
302 my ($kid, $string) = @_;
303 write_sysfs
($kid, "stage", "$string\n");
306 sub set_debug_level
{
307 my ($kid, $string) = @_;
308 write_sysfs
($kid, "debug", "$string\n");
312 my ($kid, $string) = @_;
313 write_sysfs
($kid, "partial", "$string\n");
316 sub print_abort_error
{
317 my ($kid, %errors) = @_;
318 my $error = get_abort_cause
($kid);
320 print_abort_code
($error, %errors);
321 if ($error eq 'code_busy') {
323 print STDERR get_raw_conflicts
($kid);
325 print STDERR get_conflicts
($kid);
328 $printed_abort_code = 1;
331 sub print_abort_code
{
332 my ($error, %errors) = @_;
334 print STDERR
"$error\n";
336 $error = "UNKNOWN" if (!exists $errors{$error});
337 print STDERR
"\n$errors{$error}\n";
339 $printed_abort_code = 1;