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