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