1# ex:ts=8 sw=4: 2# $OpenBSD: Error.pm,v 1.21 2010/01/09 14:49:53 espie Exp $ 3# 4# Copyright (c) 2004-2010 Marc Espie <espie@openbsd.org> 5# 6# Permission to use, copy, modify, and distribute this software for any 7# purpose with or without fee is hereby granted, provided that the above 8# copyright notice and this permission notice appear in all copies. 9# 10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 17use strict; 18use warnings; 19 20package OpenBSD::Auto; 21sub cache(*&) 22{ 23 my ($sym, $code) = @_; 24 my $callpkg = caller; 25 my $actual = sub { 26 my $self = shift; 27 if (!defined $self->{$sym}) { 28 $self->{$sym} = &$code($self); 29 } 30 return $self->{$sym}; 31 }; 32 no strict 'refs'; 33 *{$callpkg."::$sym"} = $actual; 34} 35 36package OpenBSD::Handler; 37 38my $list = []; 39 40sub register 41{ 42 my ($class, $code) = @_; 43 push(@$list, $code); 44} 45 46my $handler = sub { 47 my $sig = shift; 48 for my $c (@$list) { 49 &$c($sig); 50 } 51 $SIG{$sig} = 'DEFAULT'; 52 kill $sig, $$; 53}; 54 55sub reset 56{ 57 $SIG{'INT'} = $handler; 58 $SIG{'QUIT'} = $handler; 59 $SIG{'HUP'} = $handler; 60 $SIG{'KILL'} = $handler; 61 $SIG{'TERM'} = $handler; 62} 63 64__PACKAGE__->reset; 65 66package OpenBSD::Error; 67require Exporter; 68our @ISA=qw(Exporter); 69our @EXPORT=qw(System VSystem Copy Unlink Fatal Warn Usage set_usage 70 try throw catch catchall rethrow); 71 72our ($FileName, $Line, $FullMessage); 73 74my @signal_name = (); 75 76sub Carp::croak; 77 78sub fillup_names 79{ 80 { 81 # XXX force autoload 82 package verylocal; 83 84 require POSIX; 85 POSIX->import(qw(signal_h)); 86 } 87 88 for my $sym (keys %POSIX::) { 89 next unless $sym =~ /^SIG([A-Z].*)/; 90 $signal_name[eval "&POSIX::$sym()"] = $1; 91 } 92 # extra BSD signals 93 $signal_name[5] = 'TRAP'; 94 $signal_name[7] = 'IOT'; 95 $signal_name[10] = 'BUS'; 96 $signal_name[12] = 'SYS'; 97 $signal_name[16] = 'URG'; 98 $signal_name[23] = 'IO'; 99 $signal_name[24] = 'XCPU'; 100 $signal_name[25] = 'XFSZ'; 101 $signal_name[26] = 'VTALRM'; 102 $signal_name[27] = 'PROF'; 103 $signal_name[28] = 'WINCH'; 104 $signal_name[29] = 'INFO'; 105} 106 107sub find_signal 108{ 109 my $number = shift; 110 111 if (@signal_name == 0) { 112 fillup_names(); 113 } 114 115 return $signal_name[$number] || $number; 116} 117 118sub child_error 119{ 120 my $error = $?; 121 122 my $extra = ""; 123 124 if ($error & 128) { 125 $extra = " (core dumped)"; 126 } 127 if ($error & 127) { 128 return "killed by signal ". find_signal($error & 127).$extra; 129 } else { 130 return "exit(". ($error >> 8) . ")$extra"; 131 } 132} 133 134sub System 135{ 136 my $r = system(@_); 137 if ($r != 0) { 138 print "system(", join(", ", @_), ") failed: ", child_error(), 139 "\n"; 140 } 141 return $r; 142} 143 144sub VSystem 145{ 146 my $verbose = shift; 147 if (!$verbose) { 148 &System; 149 } else { 150 print "Running ", join(' ', @_); 151 my $r = system(@_); 152 if ($r != 0) { 153 print "... failed: ", child_error(), "\n"; 154 } else { 155 print "\n"; 156 } 157 } 158} 159 160sub Copy 161{ 162 require File::Copy; 163 164 my $r = File::Copy::copy(@_); 165 if (!$r) { 166 print "copy(", join(',', @_),") failed: $!\n"; 167 } 168 return $r; 169} 170 171sub Unlink 172{ 173 my $verbose = shift; 174 my $r = unlink @_; 175 if ($r != @_) { 176 print "rm @_ failed: removed only $r targets, $!\n"; 177 } elsif ($verbose) { 178 print "rm @_\n"; 179 } 180 return $r; 181} 182 183sub Fatal 184{ 185 require Carp; 186 Carp::croak "Expected: @_"; 187} 188 189sub Warn 190{ 191 print STDERR @_; 192} 193 194sub new 195{ 196 my $class = shift; 197 bless {messages=>{}, dirs_okay=>{}}, $class; 198} 199 200sub set_pkgname 201{ 202 my ($self, $pkgname) = @_; 203 $self->{pkgname} = $pkgname; 204 if (!defined $self->{messages}->{$pkgname}) { 205 $self->{messages}->{$pkgname} = []; 206 } 207 $self->{output} = $self->{messages}->{$pkgname}; 208} 209 210sub warn 211{ 212 &OpenBSD::Error::print; 213} 214 215sub fatal 216{ 217 my $self = shift; 218 require Carp; 219 if (defined $self->{pkgname}) { 220 Carp::croak("Expected: ", $self->{pkgname}, ':', @_); 221 } else { 222 Carp::croak("Expected: ", @_); 223 } 224} 225 226sub print 227{ 228 my $self = shift; 229 push(@{$self->{output}}, join('', @_)); 230} 231 232sub delayed_output 233{ 234 my $self = shift; 235 for my $pkg (sort keys %{$self->{messages}}) { 236 my $msgs = $self->{messages}->{$pkg}; 237 if (@$msgs > 0) { 238 print "--- $pkg -------------------\n"; 239 print @$msgs; 240 } 241 } 242 $self->{messages} = {}; 243} 244 245sub system 246{ 247 my $self = shift; 248 if (open(my $grab, "-|", @_)) { 249 my $_; 250 while (<$grab>) { 251 $self->print($_); 252 } 253 if (!close $grab) { 254 $self->print("system(", join(", ", @_), ") failed: $! ", 255 child_error(), "\n"); 256 } 257 return $?; 258 } else { 259 $self->print("system(", join(", ", @_), 260 ") was not run: $!", child_error(), "\n"); 261 } 262} 263 264my @usage_line; 265 266sub set_usage 267{ 268 @usage_line = @_; 269} 270 271sub Usage 272{ 273 my $code = 0; 274 if (@_) { 275 print STDERR "$0: ", @_, "\n"; 276 $code = 1; 277 } 278 print STDERR "Usage: ", shift(@usage_line), "\n"; 279 for my $l (@usage_line) { 280 print STDERR " $l\n"; 281 } 282 exit($code); 283} 284 285sub dienow 286{ 287 my ($error, $handler) = @_; 288 if ($error) { 289 if ($error =~ m/^(Expected\:\s+)?(.*?)(?:\s+at\s+(.*)\s+line\s+(\d+)\.?)?$/o) { 290 local $_ = $2; 291 $FileName = $3; 292 $Line = $4; 293 $FullMessage = $error; 294 295 $handler->exec($error, $1, $2, $3, $4); 296 } else { 297 die "Fatal error: can't parse $error"; 298 } 299 } 300} 301 302sub try(&@) 303{ 304 my ($try, $catch) = @_; 305 eval { &$try }; 306 dienow($@, $catch); 307} 308 309sub throw 310{ 311 require Carp; 312 Carp::croak "Expected: @_"; 313 314} 315 316sub rethrow 317{ 318 my $e = shift; 319 die $e if $e; 320} 321 322sub catch(&) 323{ 324 bless $_[0], "OpenBSD::Error::catch"; 325} 326 327sub catchall(&) 328{ 329 bless $_[0], "OpenBSD::Error::catchall"; 330} 331 332package OpenBSD::Error::catch; 333sub exec 334{ 335 my ($self, $full, $e) = @_; 336 if ($e) { 337 &$self; 338 } else { 339 die $full; 340 } 341} 342 343package OpenBSD::Error::catchall; 344sub exec 345{ 346 my ($self, $full, $e) = @_; 347 &$self; 348} 349 3501; 351