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