Decode child exit code properly
[Fedora-Rebuild.git] / lib / Fedora / Rebuild / Package / StateLock.pm
blob32ccc15965bc504f26ca3a4ba1817de020a3e215
1 package Fedora::Rebuild::Package::StateLock;
2 use strict;
3 use warnings;
4 use version 0.77; our $VERSION = version->declare("v0.1.0");
6 use Moose;
7 use Carp;
8 use Proc::SyncExec;
9 use POSIX;
10 use namespace::clean;
13 has package => (
14 is => 'ro',
15 isa => 'Fedora::Rebuild::Package',
16 required => 1
19 has state => (
20 is => 'ro',
21 isa => 'Str',
22 required => 1
25 has lockfile => (
26 is => 'ro',
27 isa => 'Str',
28 lazy_build => 1,
29 init_arg => undef
32 has logfile => (
33 is => 'ro',
34 isa => 'Str',
35 lazy_build => 1,
36 init_arg => undef
39 has logfd => (
40 is => 'ro',
41 isa => 'FileHandle',
42 lazy_build => 1,
43 init_arg => undef
45 sub BUILD {
46 my $self = shift;
48 if (! defined $self->state || $self->state eq '') {
49 croak "Invalid `state' attributed passed to StateLock constructor";
53 sub _build_lockfile {
54 my $self = shift;
55 return $self->package->packagedir . '/.' . $self->state;
58 sub _build_logfile {
59 my $self = shift;
60 return $self->lockfile. '.log';
63 sub _build_logfd {
64 my $self = shift;
65 my $file = IO::Handle->new();
66 open ($file, '>', $self->logfile) or
67 croak "Could not create `" . $self->logfile . "' logfile: $!";
68 return $file;
71 # Print array into log
72 sub log {
73 shift->logfd->print(@_);
76 # Return true if state is finshed, otherwise open log file.
77 sub is_done {
78 my $self = shift;
80 if (-e $self->lockfile) {
81 return 1;
82 } else {
83 $self->logfd;
84 return 0;
88 # Create lock file signalling the state has been finished. is_done() return
89 # true then. Return true if succeeded.
90 sub mark_done {
91 my $self = shift;
92 my $file = IO::Handle->new();
93 open ($file, '>', $self->lockfile) or
94 croak "Could not open `" . $self->lockfile .
95 "' lockfile for writing: $!";
96 $file->sync && close($file) or
97 croak "Could not sync and close `" . $self->lockfile .
98 "' lockfile: $!";
99 return 1;
102 # Close log file. Remove lock file if exist. is_done() return false then.
103 # Return false.
104 sub mark_failed {
105 my $self=shift;
106 $self->logfd->sync && $self->logfd->close or
107 croak "Could not sync and close `" . $self->logile . "' logfile: $!";
108 if (-e $self->lockfile) { unlink $self->lockfile; }
109 return 0;
112 # Convert ${^CHILD_ERROR_NATIVE} to string description.
113 # XXX: This is not a method.
114 sub child_error_as_string {
115 my $reason = ${^CHILD_ERROR_NATIVE};
116 if (WIFEXITED($reason)) {
117 $reason = "exit code " . WEXITSTATUS($reason);
118 } elsif (WIFSIGNALED($reason)) {
119 $reason = "signal " . WTERMSIG($reason);
121 return $reason;
124 # Run command while appending output to log. Blocks. If workdir is nonempty
125 # string, switch into it befere execution (and opening the log).
126 # Return true if command succeeds.
127 sub do {
128 my ($self, $workdir, @command) = @_;
130 my $redirect = sub {
131 open(STDOUT, '>&', $self->logfd->fileno) and
132 open(STDERR, '>&STDOUT');
133 print STDERR "Executing: " . join(' ', @command) . "\n";
134 if (defined $workdir && $workdir ne '' && !chdir $workdir) {
135 print STDERR "Could not change directory to $workdir: $!\n";
136 return 0;
138 return 1;
140 my $pid = Proc::SyncExec::sync_exec($redirect, @command);
141 if (!defined $pid) {
142 $self->log("Could not execute `" . join(' ', @command) . "': $!\n");
143 return 0;
145 if ($pid != waitpid($pid, 0) || $?) {
146 $self->log("Command `" . join(' ', @command) . "' failed: " .
147 child_error_as_string . "\n");
148 return 0;
150 $self->log("Command `" . join(' ', @command) .
151 "' returned successfully.\n");
152 return 1;
155 # Run command while appending stderr and stdout to log and stdout to refered
156 # output argument. In case of empty command output fill empty string;
157 # Blocks. If workdir is nonempty string, switch into it befere execution
158 # (and opening the log).
159 # Return true if command succeeds.
160 sub dooutput {
161 my ($self, $workdir, $output, @command) = @_;
163 my ($parent, $child);
164 if (!pipe $child, $parent) {
165 $self->log("Could not get connected pipes for command `" .
166 join(' ', @command) . "': $!\n");
167 return 0;
170 my $redirect = sub {
171 close $child and
172 open(STDOUT, '>&', fileno $parent) and
173 close $parent and
175 open(STDERR, '>&', $self->logfd->fileno) and
176 print STDERR "Executing: " . join(' ', @command) . "\n";
177 if (defined $workdir && $workdir ne '' && !chdir $workdir) {
178 print STDERR "Could not change directory to $workdir: $!\n";
179 return 0;
181 return 1;
183 my $pid = Proc::SyncExec::sync_exec($redirect, @command);
185 my $errno = $!;
186 close $parent;
187 $! = $errno;
189 if (!defined $pid) {
190 $self->log("Could not execute `" . join(' ', @command) . "': $!\n");
191 return 0;
194 for ($$output = ''; local $_ = <$child>;) {
195 $$output .= $_;
196 $self->log($_);
199 if ($pid != waitpid($pid, 0) || $?) {
200 $self->log("Command `" . join(' ', @command) . "' failed: " .
201 child_error_as_string . "\n");
202 return 0;
205 $self->log("Command `" . join(' ', @command) .
206 "' returned successfully.\n");
207 return 1;