xref: /openbsd/usr.sbin/pkg_add/OpenBSD/Add.pm (revision 73471bf0)
1# ex:ts=8 sw=4:
2# $OpenBSD: Add.pm,v 1.186 2021/08/09 16:41:21 espie Exp $
3#
4# Copyright (c) 2003-2014 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# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18use strict;
19use warnings;
20
21package OpenBSD::Add;
22use OpenBSD::Error;
23use OpenBSD::PackageInfo;
24use OpenBSD::ArcCheck;
25use OpenBSD::Paths;
26use File::Copy;
27
28sub manpages_index
29{
30	my ($state) = @_;
31	return unless defined $state->{addman};
32	my $destdir = $state->{destdir};
33
34	# fudge verbose for API differences
35	while (my ($k, $v) = each %{$state->{addman}}) {
36		my @l = map { "$destdir$k/$_" } @$v;
37		if ($state->{not}) {
38			$state->say("Merging manpages in #1: #2",
39			    $destdir.$k, join(' ', @l)) if $state->verbose;
40		} else {
41			$state->run_makewhatis(['-d', $destdir.$k], \@l);
42		}
43	}
44	delete $state->{addman};
45}
46
47sub register_installation
48{
49	my ($plist, $state) = @_;
50	if ($state->{not}) {
51		$plist->to_cache;
52	} else {
53		my $dest = installed_info($plist->pkgname);
54		mkdir($dest);
55		$plist->copy_info($dest, $state);
56		$plist->set_infodir($dest);
57		$plist->to_installation;
58	}
59}
60
61sub validate_plist
62{
63	my ($plist, $state, $set) = @_;
64
65	$plist->prepare_for_addition($state, $plist->pkgname, $set);
66}
67
68sub record_partial_installation
69{
70	my ($plist, $state, $h) = @_;
71
72	use OpenBSD::PackingElement;
73
74	my $n = $plist->make_shallow_copy($h);
75	my $borked = borked_package($plist->pkgname);
76	$n->set_pkgname($borked);
77
78	# last file may have not copied correctly
79	my $last = $n->{state}->{lastfile};
80	if (defined $last && defined($last->{d})) {
81
82		my $old = $last->{d};
83		my $lastname = $last->realname($state);
84		if (-f $lastname) {
85			$last->{d} = $last->compute_digest($lastname, $old);
86			if (!$old->equals($last->{d})) {
87				$state->say("Adjusting #1 for #2 from #3 to #4",
88				    $old->keyword, $lastname, $old->stringize,
89				    $last->{d}->stringize);
90			}
91		} else {
92			delete $last->{d};
93		}
94	}
95	register_installation($n, $state);
96	return $borked;
97}
98
99sub perform_installation
100{
101	my ($handle, $state) = @_;
102
103	return if $state->defines('stub');
104
105	$state->{partial} = $handle->{partial};
106	$state->progress->visit_with_size($handle->{plist}, 'install');
107	if ($handle->{location}{early_close}) {
108		$handle->{location}->close_now;
109	} else {
110		$handle->{location}->finish_and_close;
111	}
112}
113
114sub perform_extraction
115{
116	my ($handle, $state) = @_;
117
118	return if $state->defines('stub');
119
120	$handle->{partial} = {};
121	$state->{partial} = $handle->{partial};
122	$state->{archive} = $handle->{location};
123	$state->{check_digest} = $handle->{plist}{check_digest};
124	my ($wanted, $tied) = ({}, {});
125	$handle->{plist}->find_extractible($state, $wanted, $tied);
126	my $p = $state->progress->new_sizer($handle->{plist}, $state);
127	while (my $file = $state->{archive}->next) {
128		if (keys %$wanted == 0) {
129			$state->tweak_header("skipping");
130			for my $e (values %$tied) {
131				$e->tie($state);
132				$p->advance($e);
133			}
134			if (keys %$tied > 0) {
135				# skipped entries should still be read in CACHE mode
136				if (defined $state->cache_directory) {
137					while (my $e = $state->{archive}->next) {
138					}
139				} else {
140					$handle->{location}{early_close} = 1;
141				}
142			}
143			last;
144		}
145		my $e = $tied->{$file->name};
146		if (defined $e) {
147			delete $tied->{$file->name};
148			$e->prepare_to_extract($state, $file);
149			$e->tie($state);
150			$state->{archive}->skip;
151			$p->advance($e);
152			# skip to next;
153			next;
154		}
155		$e = $wanted->{$file->name};
156		if (!defined $e) {
157			$state->fatal("archive member not found #1",
158			    $file->name);
159		}
160		delete $wanted->{$file->name};
161		my $fullname = $e->fullname;
162		if ($fullname =~ m,^$state->{localbase}/share/doc/pkg-readmes/,) {
163			push(@{$state->{readmes}}, $fullname);
164	}
165
166		$e->prepare_to_extract($state, $file);
167		$e->extract($state, $file);
168		$p->advance($e);
169	}
170	if (keys %$wanted > 0) {
171		$state->fatal("Truncated archive");
172	}
173	$p->saved;
174}
175
176my $user_tagged = {};
177
178sub extract_pkgname
179{
180	my $pkgname = shift;
181	$pkgname =~ s/^.*\///;
182	$pkgname =~ s/\.tgz$//;
183	return $pkgname;
184}
185
186sub tweak_package_status
187{
188	my ($pkgname, $state) = @_;
189
190	$pkgname = extract_pkgname($pkgname);
191	return 0 unless is_installed($pkgname);
192	return 0 unless $user_tagged->{$pkgname};
193	return 1 if $state->{not};
194	my $plist = OpenBSD::PackingList->from_installation($pkgname);
195	if ($plist->has('manual-installation') && $state->{automatic} > 1) {
196		delete $plist->{'manual-installation'};
197		$plist->to_installation;
198		return 1;
199	} elsif (!$plist->has('manual-installation') && !$state->{automatic}) {
200		OpenBSD::PackingElement::ManualInstallation->add($plist);
201		$plist->to_installation;
202		return 1;
203	}
204	return 0;
205}
206
207sub tweak_plist_status
208{
209	my ($plist, $state) = @_;
210
211	my $pkgname = $plist->pkgname;
212	if ($state->defines('FW_UPDATE')) {
213		$plist->has('firmware') or
214			OpenBSD::PackingElement::Firmware->add($plist);
215	}
216	return 0 unless $user_tagged->{$pkgname};
217	if (!$plist->has('manual-installation') && !$state->{automatic}) {
218		OpenBSD::PackingElement::ManualInstallation->add($plist);
219	}
220}
221
222sub tag_user_packages
223{
224	for my $set (@_) {
225		for my $n ($set->newer_names) {
226			$user_tagged->{OpenBSD::PackageName::url2pkgname($n)} = 1;
227		}
228	}
229}
230
231# used by newuser/newgroup to deal with options.
232package OpenBSD::PackingElement;
233use OpenBSD::Error;
234
235my ($uidcache, $gidcache);
236
237sub prepare_for_addition
238{
239}
240
241sub find_extractible
242{
243}
244
245sub extract
246{
247	my ($self, $state) = @_;
248	$state->{partial}{$self} = 1;
249	if ($state->{interrupted}) {
250		die "Interrupted";
251	}
252}
253
254sub install
255{
256	my ($self, $state) = @_;
257	# XXX "normal" items are already in partial, but NOT stuff
258	# that's install-only, like symlinks and dirs...
259	$state->{partial}{$self} = 1;
260	if ($state->{interrupted}) {
261		die "Interrupted";
262	}
263}
264
265sub copy_info
266{
267}
268
269sub set_modes
270{
271	my ($self, $state, $name) = @_;
272
273	if (defined $self->{owner} || defined $self->{group}) {
274		require OpenBSD::IdCache;
275
276		if (!defined $uidcache) {
277			$uidcache = OpenBSD::UidCache->new;
278			$gidcache = OpenBSD::GidCache->new;
279		}
280		my ($uid, $gid) = (-1, -1);
281		if (defined $self->{owner}) {
282			$uid = $uidcache->lookup($self->{owner}, $uid);
283		}
284		if (defined $self->{group}) {
285			$gid = $gidcache->lookup($self->{group}, $gid);
286		}
287		chown $uid, $gid, $name;
288	}
289	if (defined $self->{mode}) {
290		my $v = $self->{mode};
291		if ($v =~ m/^\d+$/o) {
292			chmod oct($v), $name;
293		} else {
294			$state->system(OpenBSD::Paths->chmod,
295			    $self->{mode}, $name);
296		}
297	}
298	if (defined $self->{ts}) {
299		utime $self->{ts}, $self->{ts}, $name;
300	}
301}
302
303package OpenBSD::PackingElement::Meta;
304
305# XXX stuff that's invisible to find_extractible should be considered extracted
306# for the most part, otherwise we create broken partial packages
307sub find_extractible
308{
309	my ($self, $state, $wanted, $tied) = @_;
310	$state->{partial}{$self} = 1;
311}
312
313package OpenBSD::PackingElement::ExtraInfo;
314use OpenBSD::Error;
315
316sub prepare_for_addition
317{
318	my ($self, $state, $pkgname) = @_;
319
320	if ($state->{ftp_only} && $self->{ftp} ne 'yes') {
321	    $state->errsay("Package #1 is not for ftp", $pkgname);
322	    $state->{problems}++;
323	}
324}
325
326package OpenBSD::PackingElement::NewAuth;
327use OpenBSD::Error;
328
329sub add_entry
330{
331	shift;	# get rid of self
332	my $l = shift;
333	while (@_ >= 2) {
334		my $f = shift;
335		my $v = shift;
336		next if !defined $v or $v eq '';
337		if ($v =~ m/^\!(.*)$/o) {
338			push(@$l, $f, $1);
339		} else {
340			push(@$l, $f, $v);
341		}
342	}
343}
344
345sub prepare_for_addition
346{
347	my ($self, $state, $pkgname) = @_;
348	my $ok = $self->check;
349	if (defined $ok) {
350		if ($ok == 0) {
351			$state->errsay("#1 #2 does not match",
352			    $self->type, $self->name);
353			$state->{problems}++;
354		}
355	}
356	$self->{okay} = $ok;
357}
358
359sub install
360{
361	my ($self, $state) = @_;
362	$self->SUPER::install($state);
363	my $auth = $self->name;
364	$state->say("adding #1 #2", $self->type, $auth) if $state->verbose >= 2;
365	return if $state->{not};
366	return if defined $self->{okay};
367	my $l=[];
368	push(@$l, "-v") if $state->verbose >= 2;
369	$self->build_args($l);
370	$state->vsystem($self->command,, @$l, '--', $auth);
371}
372
373package OpenBSD::PackingElement::NewUser;
374
375sub command 	{ OpenBSD::Paths->useradd }
376
377sub build_args
378{
379	my ($self, $l) = @_;
380
381	$self->add_entry($l,
382	    '-u', $self->{uid},
383	    '-g', $self->{group},
384	    '-L', $self->{class},
385	    '-c', $self->{comment},
386	    '-d', $self->{home},
387	    '-s', $self->{shell});
388}
389
390package OpenBSD::PackingElement::NewGroup;
391
392sub command { OpenBSD::Paths->groupadd }
393
394sub build_args
395{
396	my ($self, $l) = @_;
397
398	$self->add_entry($l, '-g', $self->{gid});
399}
400
401package OpenBSD::PackingElement::FileBase;
402use OpenBSD::Error;
403use File::Basename;
404use File::Path;
405use OpenBSD::Temp;
406
407sub find_extractible
408{
409	my ($self, $state, $wanted, $tied) = @_;
410	if ($self->{tieto} || $self->{link} || $self->{symlink}) {
411		$tied->{$self->name} = $self;
412	} else {
413		$wanted->{$self->name} = $self;
414	}
415}
416
417sub prepare_for_addition
418{
419	my ($self, $state, $pkgname) = @_;
420	my $fname = $self->retrieve_fullname($state, $pkgname);
421	# check for collisions with existing stuff
422	if ($state->vstat->exists($fname)) {
423		push(@{$state->{colliding}}, $self);
424		$self->{newly_found} = $pkgname;
425		$state->{problems}++;
426		return;
427	}
428	return if $state->defines('stub');
429	my $s = $state->vstat->add($fname,
430	    $self->{tieto} ? 0 : $self->retrieve_size, $pkgname);
431	return unless defined $s;
432	if ($s->ro) {
433		$s->report_ro($state, $fname);
434	}
435	if ($s->avail < 0) {
436		$s->report_overflow($state, $fname);
437	}
438}
439
440sub prepare_to_extract
441{
442	my ($self, $state, $file) = @_;
443	my $fullname = $self->fullname;
444	my $destdir = $state->{destdir};
445
446	$file->{cwd} = $self->cwd;
447	if (!$file->validate_meta($self)) {
448		$state->fatal("can't continue");
449	}
450
451	$file->set_name($fullname);
452	$file->{destdir} = $destdir;
453}
454
455sub find_safe_dir
456{
457	my ($self, $state) = @_;
458	# figure out a safe directory where to put the temp file
459
460	my $fullname = $self->fullname;
461	my $filename = $state->{destdir}.$fullname;
462	my $d = dirname($filename);
463
464	# we go back up until we find an existing directory.
465	# hopefully this will be on the same file system.
466	my @candidates = ();
467	while (!-d $d) {
468		push(@candidates, $d);
469		$d = dirname($d);
470	}
471	# and now we try to go back down, creating the best path we can
472	while (@candidates > 0) {
473		my $c = pop @candidates;
474		last if -e $c; # okay this exists, but is not a directory
475		$d = $c;
476	}
477	if (!-e _ && !$state->{not}) {
478		$state->make_path($d, $fullname);
479	}
480	return $d;
481}
482
483sub create_temp
484{
485	my ($self, $d, $state) = @_;
486	my $fullname = $self->fullname;
487	my ($fh, $tempname) = OpenBSD::Temp::permanent_file($d, "pkg");
488	$self->{tempname} = $tempname;
489	if (!defined $tempname) {
490		if ($state->allow_nonroot($fullname)) {
491			$state->errsay("Can't create temp file outside localbase for #1", $fullname);
492			$state->errsay(OpenBSD::Temp->last_error);
493			return undef;
494		}
495		$state->fatal(OpenBSD::Temp->last_error);
496	}
497	return ($fh, $tempname);
498}
499
500sub tie
501{
502	my ($self, $state) = @_;
503	if (defined $self->{link} || defined $self->{symlink}) {
504		return;
505	}
506
507	$self->SUPER::extract($state);
508
509	my $d = $self->find_safe_dir($state);
510	if ($state->{not}) {
511		$state->say("link #1 -> #2",
512		    $self->name, $d) if $state->verbose >= 3;
513	} else {
514		my ($fh, $tempname) = $self->create_temp($d, $state);
515
516		return if !defined $tempname;
517		my $src = $self->{tieto}->realname($state);
518		unlink($tempname);
519		$state->say("link #1 -> #2", $src, $tempname)
520		    if $state->verbose >= 3;
521		link($src, $tempname) || $state->copy_file($src, $tempname);
522	}
523}
524
525sub extract
526{
527	my ($self, $state, $file) = @_;
528
529	$self->SUPER::extract($state);
530
531	my $d = $self->find_safe_dir($state);
532	if ($state->{not}) {
533		$state->say("extract #1 -> #2",
534		    $self->name, $d) if $state->verbose >= 3;
535		$state->{archive}->skip;
536	} else {
537		my ($fh, $tempname) = $self->create_temp($d, $state);
538		if (!defined $tempname) {
539			$state->{archive}->skip;
540			return;
541		}
542
543		$state->say("extract #1 -> #2", $self->name, $tempname)
544		    if $state->verbose >= 3;
545
546
547		if (!$file->isFile) {
548			$state->fatal("can't extract #1, it's not a file",
549			    $self->stringize);
550		}
551		$file->extract_to_fh($fh);
552		$self->may_check_digest($tempname, $state);
553	}
554}
555
556sub install
557{
558	my ($self, $state) = @_;
559	$self->SUPER::install($state);
560	my $fullname = $self->fullname;
561	my $destdir = $state->{destdir};
562	if ($state->{not}) {
563		$state->say("moving tempfile -> #1",
564		    $destdir.$fullname) if $state->verbose >= 5;
565		return;
566	}
567	$state->make_path(dirname($destdir.$fullname), $fullname);
568	if (defined $self->{link}) {
569		link($destdir.$self->{link}, $destdir.$fullname);
570	} elsif (defined $self->{symlink}) {
571		symlink($self->{symlink}, $destdir.$fullname);
572	} else {
573		if (!defined $self->{tempname}) {
574			return if $state->allow_nonroot($fullname);
575			$state->fatal("No tempname for #1", $fullname);
576		}
577		rename($self->{tempname}, $destdir.$fullname) or
578		    $state->fatal("can't move #1 to #2: #3",
579			$self->{tempname}, $fullname, $!);
580		$state->say("moving #1 -> #2",
581		    $self->{tempname}, $destdir.$fullname)
582			if $state->verbose >= 5;
583		delete $self->{tempname};
584	}
585	$self->set_modes($state, $destdir.$fullname);
586}
587
588package OpenBSD::PackingElement::RcScript;
589sub install
590{
591	my ($self, $state) = @_;
592	$state->{add_rcscripts}{$self->fullname} = 1;
593	$self->SUPER::install($state);
594}
595
596package OpenBSD::PackingElement::Sample;
597use OpenBSD::Error;
598use File::Copy;
599
600sub prepare_for_addition
601{
602	my ($self, $state, $pkgname) = @_;
603	if (!defined $self->{copyfrom}) {
604		$state->errsay("\@sample element #1 does not reference a valid file",
605		    $self->fullname);
606		$state->{problems}++;
607	}
608	my $fname = $state->{destdir}.$self->fullname;
609	# If file already exists, we won't change it
610	if ($state->vstat->exists($fname)) {
611		return;
612	}
613	my $size = $self->{copyfrom}->{size};
614	my $s = $state->vstat->add($fname, $size, $pkgname);
615	return unless defined $s;
616	if ($s->ro) {
617		$s->report_ro($state, $fname);
618	}
619	if ($s->avail < 0) {
620		$s->report_overflow($state, $fname);
621	}
622}
623
624sub find_extractible
625{
626}
627
628sub extract
629{
630}
631
632sub install
633{
634	my ($self, $state) = @_;
635
636	$self->SUPER::install($state);
637	my $destdir = $state->{destdir};
638	my $filename = $destdir.$self->fullname;
639	my $orig = $self->{copyfrom};
640	my $origname = $destdir.$orig->fullname;
641	if (-e $filename) {
642		if ($state->verbose) {
643		    $state->say("The existing file #1 has NOT been changed",
644		    	$filename);
645		    if (defined $orig->{d}) {
646
647			# XXX assume this would be the same type of file
648			my $d = $self->compute_digest($filename, $orig->{d});
649			if ($d->equals($orig->{d})) {
650			    $state->say("(but it seems to match the sample file #1)", $origname);
651			} else {
652			    $state->say("It does NOT match the sample file #1",
653				$origname);
654			    $state->say("You may wish to update it manually");
655			}
656		    }
657		}
658	} else {
659		if ($state->{not}) {
660			$state->say("The file #1 would be installed from #2",
661			    $filename, $origname) if $state->verbose >= 2;
662		} else {
663			if (!copy($origname, $filename)) {
664				$state->errsay("File #1 could not be installed:\n\t#2", $filename, $!);
665			}
666			$self->set_modes($state, $filename);
667			if ($state->verbose >= 2) {
668			    $state->say("installed #1 from #2",
669				$filename, $origname);
670			}
671		}
672	}
673}
674
675package OpenBSD::PackingElement::Sampledir;
676sub extract
677{
678}
679
680sub install
681{
682	&OpenBSD::PackingElement::Dir::install;
683}
684
685package OpenBSD::PackingElement::Mandir;
686
687sub install
688{
689	my ($self, $state) = @_;
690	$self->SUPER::install($state);
691	if (!$state->{current_set}{known_mandirs}{$self->fullname}) {
692		$state->log("You may wish to add #1 to /etc/man.conf",
693		    $self->fullname);
694	}
695}
696
697package OpenBSD::PackingElement::Manpage;
698
699sub install
700{
701	my ($self, $state) = @_;
702	$self->SUPER::install($state);
703	$self->register_manpage($state, 'addman');
704}
705
706package OpenBSD::PackingElement::InfoFile;
707use File::Basename;
708use OpenBSD::Error;
709
710sub install
711{
712	my ($self, $state) = @_;
713	$self->SUPER::install($state);
714	return if $state->{not};
715	my $fullname = $state->{destdir}.$self->fullname;
716	$state->vsystem(OpenBSD::Paths->install_info,
717	    "--info-dir=".dirname($fullname), '--', $fullname);
718}
719
720package OpenBSD::PackingElement::Shell;
721sub install
722{
723	my ($self, $state) = @_;
724	$self->SUPER::install($state);
725	return if $state->{not};
726	my $fullname = $self->fullname;
727	my $destdir = $state->{destdir};
728	# go append to /etc/shells if needed
729	open(my $shells, '<', $destdir.OpenBSD::Paths->shells) or return;
730	while(<$shells>) {
731		s/^\#.*//o;
732		return if m/^\Q$fullname\E\s*$/;
733	}
734	close($shells);
735	open(my $shells2, '>>', $destdir.OpenBSD::Paths->shells) or return;
736	print $shells2 $fullname, "\n";
737	close $shells2;
738	$state->say("Shell #1 appended to #2", $fullname,
739	    $destdir.OpenBSD::Paths->shells) if $state->verbose;
740}
741
742package OpenBSD::PackingElement::Dir;
743sub extract
744{
745	my ($self, $state) = @_;
746	my $fullname = $self->fullname;
747	my $destdir = $state->{destdir};
748
749	return if -e $destdir.$fullname;
750	$self->SUPER::extract($state);
751	$state->say("new directory #1", $destdir.$fullname)
752	    if $state->verbose >= 3;
753	return if $state->{not};
754	$state->make_path($destdir.$fullname, $fullname);
755}
756
757sub install
758{
759	my ($self, $state) = @_;
760	$self->SUPER::install($state);
761	my $fullname = $self->fullname;
762	my $destdir = $state->{destdir};
763
764	$state->say("new directory #1", $destdir.$fullname)
765	    if $state->verbose >= 5;
766	return if $state->{not};
767	$state->make_path($destdir.$fullname, $fullname);
768	$self->set_modes($state, $destdir.$fullname);
769}
770
771package OpenBSD::PackingElement::Exec;
772use OpenBSD::Error;
773
774sub install
775{
776	my ($self, $state) = @_;
777
778	$self->SUPER::install($state);
779	if ($self->should_run($state)) {
780		$self->run($state);
781	}
782}
783
784sub should_run() { 1 }
785
786package OpenBSD::PackingElement::ExecAdd;
787sub should_run
788{
789	my ($self, $state) = @_;
790	return !$state->replacing;
791}
792
793package OpenBSD::PackingElement::ExecUpdate;
794sub should_run
795{
796	my ($self, $state) = @_;
797	return $state->replacing;
798}
799
800package OpenBSD::PackingElement::Tag;
801
802sub install
803{
804	my ($self, $state) = @_;
805
806	for my $d (@{$self->{definition_list}}) {
807		$d->add_tag($self, "install", $state);
808	}
809}
810
811package OpenBSD::PackingElement::Lib;
812
813sub install
814{
815	my ($self, $state) = @_;
816	$self->SUPER::install($state);
817	$self->mark_ldconfig_directory($state);
818}
819
820package OpenBSD::PackingElement::SpecialFile;
821use OpenBSD::PackageInfo;
822use OpenBSD::Error;
823
824sub copy_info
825{
826	my ($self, $dest, $state) = @_;
827	require File::Copy;
828
829	File::Copy::move($self->fullname, $dest) or
830	    $state->errsay("Problem while moving #1 into #2: #3",
831		$self->fullname, $dest, $!);
832}
833
834sub extract
835{
836	my ($self, $state) = @_;
837	$self->may_verify_digest($state);
838}
839
840sub find_extractible
841{
842	my ($self, $state) = @_;
843	$self->may_verify_digest($state);
844}
845
846package OpenBSD::PackingElement::FCONTENTS;
847sub copy_info
848{
849}
850
851package OpenBSD::PackingElement::AskUpdate;
852sub prepare_for_addition
853{
854	my ($self, $state, $pkgname, $set) = @_;
855	my @old = $set->older_names;
856	if ($self->spec->match_ref(\@old) > 0) {
857		my $key = "update_".OpenBSD::PackageName::splitstem($pkgname);
858		return if $state->defines($key);
859		if ($state->is_interactive) {
860			if ($state->confirm_defaults_to_no(
861			    "#1: #2.\nDo you want to update now",
862			    $pkgname, $self->{message})) {
863			    	return;
864			}
865		} else {
866			$state->errsay("Can't update #1 now: #2",
867			    $pkgname, $self->{message});
868		}
869		$state->{problems}++;
870	}
871}
872
873package OpenBSD::PackingElement::FDISPLAY;
874sub install
875{
876	my ($self, $state) = @_;
877	my $d = $self->{d};
878	if (!$state->{current_set}{known_displays}{$self->{d}->key}) {
879		$self->prepare($state);
880	}
881	$self->SUPER::install($state);
882}
883
884package OpenBSD::PackingElement::FUNDISPLAY;
885sub find_extractible
886{
887	my ($self, $state, $wanted, $tied) = @_;
888	$state->{current_set}{known_displays}{$self->{d}->key} = 1;
889	$self->SUPER::find_extractible($state, $wanted, $tied);
890}
891
8921;
893