1# ex:ts=8 sw=4: 2# $OpenBSD: Error.pm,v 1.22 2010/01/17 11:56:46 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 76use Carp; 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 croak @_; 186} 187 188sub Warn 189{ 190 print STDERR @_; 191} 192 193sub new 194{ 195 my $class = shift; 196 bless {messages=>{}, dirs_okay=>{}}, $class; 197} 198 199sub set_pkgname 200{ 201 my ($self, $pkgname) = @_; 202 $self->{pkgname} = $pkgname; 203 if (!defined $self->{messages}->{$pkgname}) { 204 $self->{messages}->{$pkgname} = []; 205 } 206 $self->{output} = $self->{messages}->{$pkgname}; 207} 208 209sub warn 210{ 211 &OpenBSD::Error::print; 212} 213 214sub fatal 215{ 216 my $self = shift; 217 if (defined $self->{pkgname}) { 218 unshift @_, $self->{pkgname}, ':'; 219 } 220 croak @_; 221} 222 223sub print 224{ 225 my $self = shift; 226 push(@{$self->{output}}, join('', @_)); 227} 228 229sub delayed_output 230{ 231 my $self = shift; 232 for my $pkg (sort keys %{$self->{messages}}) { 233 my $msgs = $self->{messages}->{$pkg}; 234 if (@$msgs > 0) { 235 print "--- $pkg -------------------\n"; 236 print @$msgs; 237 } 238 } 239 $self->{messages} = {}; 240} 241 242sub system 243{ 244 my $self = shift; 245 if (open(my $grab, "-|", @_)) { 246 my $_; 247 while (<$grab>) { 248 $self->print($_); 249 } 250 if (!close $grab) { 251 $self->print("system(", join(", ", @_), ") failed: $! ", 252 child_error(), "\n"); 253 } 254 return $?; 255 } else { 256 $self->print("system(", join(", ", @_), 257 ") was not run: $!", child_error(), "\n"); 258 } 259} 260 261my @usage_line; 262 263sub set_usage 264{ 265 @usage_line = @_; 266} 267 268sub Usage 269{ 270 my $code = 0; 271 if (@_) { 272 print STDERR "$0: ", @_, "\n"; 273 $code = 1; 274 } 275 print STDERR "Usage: ", shift(@usage_line), "\n"; 276 for my $l (@usage_line) { 277 print STDERR " $l\n"; 278 } 279 exit($code); 280} 281 282sub dienow 283{ 284 my ($error, $handler) = @_; 285 if ($error) { 286 if ($error =~ m/^(.*?)(?:\s+at\s+(.*)\s+line\s+(\d+)\.?)?$/o) { 287 local $_ = $1; 288 $FileName = $2; 289 $Line = $3; 290 $FullMessage = $error; 291 292 $handler->exec($error, '', $1, $2, $3); 293 } else { 294 die "Fatal error: can't parse $error"; 295 } 296 } 297} 298 299sub try(&@) 300{ 301 my ($try, $catch) = @_; 302 eval { &$try }; 303 dienow($@, $catch); 304} 305 306sub throw 307{ 308 croak @_; 309 310} 311 312sub rethrow 313{ 314 my $e = shift; 315 die $e if $e; 316} 317 318sub catch(&) 319{ 320 bless $_[0], "OpenBSD::Error::catch"; 321} 322 323sub catchall(&) 324{ 325 bless $_[0], "OpenBSD::Error::catchall"; 326} 327 328package OpenBSD::Error::catch; 329sub exec 330{ 331 my ($self, $full, $e) = @_; 332 if ($e) { 333 &$self; 334 } else { 335 die $full; 336 } 337} 338 339package OpenBSD::Error::catchall; 340sub exec 341{ 342 my ($self, $full, $e) = @_; 343 &$self; 344} 345 3461; 347