xref: /openbsd/usr.sbin/pkg_add/OpenBSD/Error.pm (revision 91273194)
1# ex:ts=8 sw=4:
2# $OpenBSD: Error.pm,v 1.40 2019/07/24 18:05:26 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
20# this is a set of common classes related to error handling in pkg land
21
22package OpenBSD::Auto;
23sub cache(*&)
24{
25	my ($sym, $code) = @_;
26	my $callpkg = caller;
27	my $actual = sub {
28		my $self = shift;
29		return $self->{$sym} //= &$code($self);
30	};
31	no strict 'refs';
32	*{$callpkg."::$sym"} = $actual;
33}
34
35package OpenBSD::SigHandler;
36
37# instead of "local" sighandlers, let's do objects that revert
38# to their former state afterwards
39sub new
40{
41	my $class = shift;
42	# keep previous state
43	bless {}, $class;
44}
45
46
47sub DESTROY
48{
49	my $self = shift;
50	while (my ($s, $v) = each %$self) {
51		$SIG{$s} = $v;
52	}
53}
54
55sub set
56{
57	my $self = shift;
58	my $v = pop;
59	for my $s (@_) {
60		$self->{$s} = $SIG{$s};
61		$SIG{$s} = $v;
62	}
63	return $self;
64}
65
66sub intercept
67{
68	my $self = shift;
69	my $v = pop;
70	return $self->set(@_,
71	    sub {
72		my $sig = shift;
73		&$v($sig);
74		$SIG{$sig} = $self->{$sig};
75		kill -$sig, $$;
76	    });
77}
78
79package OpenBSD::Handler;
80
81# a bunch of other modules create persistent state that must be cleaned up
82# on exit (temporary files, network connections to abort properly...)
83# END blocks would do that (but see below...) but sig handling bypasses that,
84# so we MUST install SIG handlers.
85
86# note that END will be run for *each* process, so beware!
87# (temp files are registered per pid, for instance, so they only
88# get cleaned when the proper pid is used)
89# hash of code to run on ANY exit
90
91# hash of code to run on ANY exit
92my $atend = {};
93# hash of code to run on fatal signals
94my $cleanup = {};
95
96sub cleanup
97{
98	my ($class, $sig) = @_;
99	# XXX note that order of cleanup is "unpredictable"
100	for my $v (values %$cleanup) {
101		&$v($sig);
102	}
103}
104
105END {
106	# XXX localize $? so that cleanup doesn't fuck up our exit code
107	local $?;
108	for my $v (values %$atend) {
109		&$v();
110	}
111}
112
113# register each code block "by name" so that we can re-register each
114# block several times
115sub register
116{
117	my ($class, $code) = @_;
118	$cleanup->{$code} = $code;
119}
120
121sub atend
122{
123	my ($class, $code) = @_;
124	$cleanup->{$code} = $code;
125	$atend->{$code} = $code;
126}
127
128my $handler = sub {
129	my $sig = shift;
130	__PACKAGE__->cleanup($sig);
131	# after cleanup, just propagate the signal
132	$SIG{$sig} = 'DEFAULT';
133	kill $sig, $$;
134};
135
136sub reset
137{
138	for my $sig (qw(INT QUIT HUP KILL TERM)) {
139		$SIG{$sig} = $handler;
140	}
141}
142
143__PACKAGE__->reset;
144
145package OpenBSD::Error;
146require Exporter;
147our @ISA=qw(Exporter);
148our @EXPORT=qw(try throw catch rethrow INTetc);
149
150
151our ($FileName, $Line, $FullMessage);
152
153our @INTetc = (qw(INT QUIT HUP TERM));
154
155use Carp;
156sub dienow
157{
158	my ($error, $handler) = @_;
159	if ($error) {
160		if ($error =~ m/^(.*?)(?:\s+at\s+(.*)\s+line\s+(\d+)\.?)?$/o) {
161			local $_ = $1;
162			$FileName = $2;
163			$Line = $3;
164			$FullMessage = $error;
165
166			$handler->exec($error, $1, $2, $3);
167		} else {
168			die "Fatal error: can't parse $error";
169		}
170	}
171}
172
173sub try(&@)
174{
175	my ($try, $catch) = @_;
176	eval { &$try };
177	dienow($@, $catch);
178}
179
180sub throw
181{
182	croak @_;
183
184}
185
186sub rethrow
187{
188	my $e = shift;
189	die $e if $e;
190}
191
192sub catch(&)
193{
194		bless $_[0], "OpenBSD::Error::catch";
195}
196
197sub rmtree
198{
199	my $class = shift;
200	require File::Path;
201	require Cwd;
202
203	# XXX make sure we live somewhere
204	Cwd::getcwd() || chdir('/');
205
206	File::Path::rmtree(@_);
207}
208
209package OpenBSD::Error::catch;
210sub exec
211{
212	my ($self, $full, $e) = @_;
213	&$self;
214}
215
2161;
217