xref: /openbsd/usr.sbin/pkg_add/OpenBSD/Delete.pm (revision e5dd7070)
1# ex:ts=8 sw=4:
2# $OpenBSD: Delete.pm,v 1.160 2019/07/24 18:05:26 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::Delete;
22use OpenBSD::Error;
23use OpenBSD::PackageInfo;
24use OpenBSD::RequiredBy;
25use OpenBSD::Paths;
26use File::Basename;
27
28sub keep_old_files
29{
30	my ($state, $plist) = @_;
31	my $p = OpenBSD::PackingList->new;
32	my $borked = borked_package($plist->pkgname);
33	$p->set_infodir(installed_info($borked));
34	mkdir($p->infodir);
35
36	$plist->copy_old_stuff($p, $state);
37	$p->set_pkgname($borked);
38	$p->to_installation;
39	return $borked;
40}
41
42sub manpages_unindex
43{
44	my ($state) = @_;
45	return unless defined $state->{rmman};
46	my $destdir = $state->{destdir};
47
48	while (my ($k, $v) = each %{$state->{rmman}}) {
49		my @l = map { "$destdir$k/$_" } @$v;
50		if ($state->{not}) {
51			$state->say("Removing manpages in #1: #2",
52			    $destdir.$k, join(' ', @l)) if $state->verbose;
53		} else {
54			$state->run_makewhatis(['-u', $destdir.$k], \@l);
55		}
56	}
57	delete $state->{rmman};
58}
59
60sub validate_plist
61{
62	my ($plist, $state) = @_;
63
64	$plist->prepare_for_deletion($state, $plist->pkgname);
65}
66
67sub remove_packing_info
68{
69	my ($plist, $state) = @_;
70
71	my $dir = $plist->infodir;
72
73	for my $fname (info_names()) {
74		unlink($dir.$fname);
75	}
76	OpenBSD::RequiredBy->forget($dir);
77	OpenBSD::Requiring->forget($dir);
78	rmdir($dir) or
79	    $state->fatal("can't finish removing directory #1: #2", $dir, $!);
80}
81
82sub delete_handle
83{
84	my ($handle, $state) = @_;
85	my $pkgname = $handle->pkgname;
86	$state->progress->message($state->f("reading list for #1", $pkgname));
87	my $plist = $handle->plist;
88	if ($plist->has('firmware') && !$state->defines('FW_UPDATE')) {
89		if ($state->is_interactive) {
90			if (!$state->confirm_defaults_to_no(
91			    "\nDelete firmware #1", $pkgname)) {
92				$state->errsay("NOT deleting #1", $pkgname);
93				return;
94			}
95		} else {
96			$state->errsay("NOT deleting #1: use fw_update -d",
97			    $pkgname);
98			return;
99		}
100	}
101
102	$state->{problems} = 0;
103	validate_plist($plist, $state);
104	$state->fatal("can't recover from deinstalling #1", $pkgname)
105	    if $state->{problems};
106	$state->vstat->synchronize;
107
108	delete_plist($plist, $state);
109}
110
111sub unregister_dependencies
112{
113	my ($plist, $state) = @_;
114
115	my $pkgname = $plist->pkgname;
116	my $l = OpenBSD::Requiring->new($pkgname);
117
118	for my $name ($l->list) {
119		$state->say("remove dependency on #1", $name)
120		    if $state->verbose >= 3;
121		local $@;
122		try {
123			OpenBSD::RequiredBy->new($name)->delete($pkgname);
124		} catch {
125			$state->errsay($_);
126		};
127	}
128	$l->erase;
129}
130
131sub delete_plist
132{
133	my ($plist, $state) = @_;
134
135	my $pkgname = $plist->pkgname;
136	$state->{pkgname} = $pkgname;
137	if (!$state->defines('stub')) {
138		if (!$state->{size_only}) {
139			$plist->register_manpage($state, 'rmman');
140			manpages_unindex($state);
141			$state->progress->visit_with_size($plist, 'delete');
142		}
143	}
144
145	unregister_dependencies($plist, $state);
146	return if $state->{not};
147	if ($state->{baddelete}) {
148	    my $borked = keep_old_files($state, $plist);
149	    $state->log("Files kept as #1 package", $borked);
150	    delete $state->{baddelete};
151	}
152
153
154	remove_packing_info($plist, $state);
155	delete_installed($pkgname);
156}
157
158package OpenBSD::PackingElement;
159
160sub rename_file_to_temp
161{
162	my ($self, $state) = @_;
163	require OpenBSD::Temp;
164
165	my $n = $self->realname($state);
166
167	my (undef, $j) = OpenBSD::Temp::permanent_file(undef, $n);
168	if (!defined $j) {
169		$state->errsay(OpenBSD::Temp->last_error);
170		return;
171	}
172	if (rename($n, $j)) {
173		$state->say("Renaming old file #1 to #2", $n, $j);
174		if ($self->name !~ m/^\//o && $self->cwd ne '.') {
175			my $c = $self->cwd;
176			$j =~ s|^\Q$c\E/||;
177		}
178		$self->set_name($j);
179	} else {
180		$state->errsay("Bad rename #1 to #2: #3", $n, $j, $!);
181	}
182}
183
184sub prepare_for_deletion
185{
186}
187
188sub delete
189{
190}
191
192sub record_shared
193{
194}
195
196sub copy_old_stuff
197{
198}
199
200package OpenBSD::PackingElement::Cwd;
201
202sub copy_old_stuff
203{
204	my ($self, $plist, $state) = @_;
205	$self->add_object($plist);
206}
207
208package OpenBSD::PackingElement::FileObject;
209use File::Basename;
210
211sub mark_directory
212{
213	my ($self, $state, $dir) = @_;
214
215	$state->{dirs_okay}{$dir} = 1;
216	my $d2 = dirname($dir);
217	if ($d2 ne $dir) {
218		$self->mark_directory($state, $d2);
219	}
220}
221
222sub mark_dir
223{
224	my ($self, $state) = @_;
225
226	$self->mark_directory($state, dirname($self->fullname));
227}
228
229sub do_not_delete
230{
231	my ($self, $state) = @_;
232
233	my $realname = $self->realname($state);
234	$state->{baddelete} = 1;
235	$self->{stillaround} = 1;
236
237	delete $self->{symlink};
238	delete $self->{link};
239	my $algo = $self->{d};
240	delete $self->{d};
241
242	if (-l $realname) {
243		$self->{symlink} = readlink $realname;
244	} elsif (-f _) {
245		$self->{d} = $self->compute_digest($realname, $algo);
246	} elsif (-d _) {
247		# what should we do ?
248	}
249}
250
251
252package OpenBSD::PackingElement::DirlikeObject;
253sub mark_dir
254{
255	my ($self, $state) = @_;
256	$self->mark_directory($state, $self->fullname);
257}
258
259package OpenBSD::PackingElement::RcScript;
260# XXX we should check stuff more thoroughly
261
262sub delete
263{
264	my ($self, $state) = @_;
265	$state->{delete_rcscripts}{$self->fullname} = 1;
266	$self->SUPER::delete($state);
267}
268
269package OpenBSD::PackingElement::NewUser;
270sub delete
271{
272	my ($self, $state) = @_;
273
274	if ($state->verbose >= 2) {
275		$state->say("rmuser: #1", $self->name);
276	}
277
278	$self->record_shared($state->{recorder}, $state->{pkgname});
279}
280
281sub record_shared
282{
283	my ($self, $recorder, $pkgname) = @_;
284	$recorder->{users}{$self->name} = $pkgname;
285}
286
287package OpenBSD::PackingElement::NewGroup;
288sub delete
289{
290	my ($self, $state) = @_;
291
292	if ($state->verbose >= 2) {
293		$state->say("rmgroup: #1", $self->name);
294	}
295
296	$self->record_shared($state->{recorder}, $state->{pkgname});
297}
298
299sub record_shared
300{
301	my ($self, $recorder, $pkgname) = @_;
302	$recorder->{groups}{$self->name} = $pkgname;
303}
304
305package OpenBSD::PackingElement::DirBase;
306sub prepare_for_deletion
307{
308	my ($self, $state, $pkgname) = @_;
309	$state->vstat->remove_directory(
310	    $self->retrieve_fullname($state, $pkgname), $self);
311}
312
313sub delete
314{
315	my ($self, $state) = @_;
316
317	if ($state->verbose >= 5) {
318		$state->say("rmdir: #1", $self->fullname);
319	}
320
321	$self->record_shared($state->{recorder}, $state->{pkgname});
322}
323
324sub record_shared
325{
326	my ($self, $recorder, $pkgname) = @_;
327	# enough for the entry to exist, we only record interesting
328	# entries more thoroughly
329	$recorder->{dirs}{$self->fullname} //= [];
330}
331
332package OpenBSD::PackingElement::Mandir;
333sub record_shared
334{
335	my ($self, $recorder, $pkgname) = @_;
336	$self->{pkgname} = $pkgname;
337	push(@{$recorder->{dirs}{$self->fullname}} , $self);
338}
339
340package OpenBSD::PackingElement::Fontdir;
341sub record_shared
342{
343	my ($self, $recorder, $pkgname) = @_;
344	$self->{pkgname} = $pkgname;
345	push(@{$recorder->{dirs}{$self->fullname}} , $self);
346	$recorder->{fonts_todo}{$self->fullname} = 1;
347}
348
349package OpenBSD::PackingElement::Infodir;
350sub record_shared
351{
352	&OpenBSD::PackingElement::Mandir::record_shared;
353}
354
355package OpenBSD::PackingElement::Unexec;
356sub delete
357{
358	my ($self, $state) = @_;
359	if ($self->should_run($state)) {
360		$self->run($state);
361	}
362}
363
364sub should_run() { 1 }
365
366package OpenBSD::PackingElement::UnexecDelete;
367sub should_run
368{
369	my ($self, $state) = @_;
370	return !$state->replacing;
371}
372
373package OpenBSD::PackingElement::UnexecUpdate;
374sub should_run
375{
376	my ($self, $state) = @_;
377	return $state->replacing;
378}
379
380package OpenBSD::PackingElement::DefineTag::Atend;
381sub delete
382{
383	my ($self, $state) = @_;
384	if (!$state->replacing) {
385		$state->{tags}{deleted}{$self->name} = 1;
386	}
387}
388
389
390package OpenBSD::PackingElement::Tag;
391sub delete
392{
393	my ($self, $state) = @_;
394
395	for my $d (@{$self->{definition_list}}) {
396		$d->add_tag($self, "delete", $state);
397	}
398}
399
400package OpenBSD::PackingElement::FileBase;
401use OpenBSD::Error;
402
403sub prepare_for_deletion
404{
405	my ($self, $state, $pkgname) = @_;
406
407	my $fname = $self->retrieve_fullname($state, $pkgname);
408	my $s;
409	my $size = $self->{tied} ? 0 : $self->retrieve_size;
410	if ($state->{delete_first}) {
411		$s = $state->vstat->remove_first($fname, $size);
412	} else {
413		$s = $state->vstat->remove($fname, $size);
414	}
415	return unless defined $s;
416	if ($s->ro) {
417		$s->report_ro($state, $fname);
418	}
419}
420
421sub is_intact
422{
423	my ($self, $state, $realname) = @_;
424	return 1 if defined($self->{link}) or $self->{nochecksum};
425	if (!defined $self->{d}) {
426		if ($self->fullname eq $realname) {
427			$state->say("NOT deleting #1 (no checksum)", $realname);
428		} else {
429			$state->say("Not deleting #1 (no checksum for #2",
430			    $realname, $self->fullname);
431		}
432		$state->log("Couldn't delete #1 (no checksum)", $realname);
433		return 0;
434	}
435	return 1 unless $state->defines('checksum');
436	my $d = $self->compute_digest($realname, $self->{d});
437	return 1 if $d->equals($self->{d});
438	if ($self->fullname eq $realname) {
439		$state->say("NOT deleting #1 (bad checksum)", $realname);
440	} else {
441		$state->say("Not deleting #1 (bad checksum for #2)",
442		    $realname, $self->fullname);
443	}
444	$state->log("Couldn't delete #1 (bad checksum)", $realname);
445	return 0;
446}
447
448sub delete
449{
450	my ($self, $state) = @_;
451	my $realname = $self->realname($state);
452
453	if (defined $self->{symlink}) {
454		if (-l $realname) {
455			my $contents = readlink $realname;
456			if ($contents ne $self->{symlink}) {
457				$state->say("Symlink does not match: #1 (#2 vs. #3)",
458				    $realname, $contents, $self->{symlink});
459				$self->do_not_delete($state);
460				return;
461			}
462		} else  {
463			$state->say("Bogus symlink: #1", $realname);
464			$self->do_not_delete($state);
465			return;
466		}
467	} else {
468		if (-l $realname) {
469				$state->say("Unexpected symlink: #1", $realname);
470				$self->do_not_delete($state);
471		} else {
472			if (!-f $realname) {
473				$state->say("File #1 does not exist", $realname);
474				return;
475			}
476			if (!$self->is_intact($state, $realname)) {
477				$self->do_not_delete($state);
478				return;
479			}
480		}
481	}
482	if ($state->verbose >= 5) {
483		$state->say("deleting: #1", $realname);
484	}
485	return if $state->{not};
486	if ($state->{delete_first} && $self->{tied}) {
487		push(@{$state->{delayed}}, $realname);
488	} else {
489		if (!unlink $realname) {
490			$state->errsay("Problem deleting #1: #2", $realname,
491			    $!);
492			$state->log("deleting #1 failed: #2", $realname, $!);
493		}
494	}
495}
496
497sub copy_old_stuff
498{
499	my ($self, $plist, $state) = @_;
500
501	if (defined $self->{stillaround}) {
502		delete $self->{stillaround};
503		if ($state->replacing) {
504			$self->rename_file_to_temp($state);
505		}
506		$self->add_object($plist);
507	}
508}
509
510package OpenBSD::PackingElement::SpecialFile;
511use OpenBSD::PackageInfo;
512
513sub copy_old_stuff
514{
515}
516
517package OpenBSD::PackingElement::Meta;
518sub copy_old_stuff
519{
520	my ($self, $plist, $state) = @_;
521	$self->add_object($plist);
522}
523
524package OpenBSD::PackingElement::DigitalSignature;
525sub copy_old_stuff
526{
527}
528
529package OpenBSD::PackingElement::FDESC;
530sub copy_old_stuff
531{
532	my ($self, $plist, $state) = @_;
533	require File::Copy;
534
535	File::Copy::copy($self->fullname, $plist->infodir);
536	$self->add_object($plist);
537}
538
539package OpenBSD::PackingElement::Sample;
540use OpenBSD::Error;
541use File::Basename;
542
543sub delete
544{
545	my ($self, $state) = @_;
546	my $realname = $self->realname($state);
547
548	my $orig = $self->{copyfrom};
549	if (!defined $orig) {
550		$state->fatal("\@sample element does not reference a valid file");
551	}
552	my $action = $state->replacing ? "check" : "remove";
553	my $origname = $orig->realname($state);
554	if (! -e $realname) {
555		$state->log("File #1 does not exist", $realname);
556		return;
557	}
558	if (! -f $realname) {
559		$state->log("File #1 is not a file", $realname);
560		return;
561	}
562
563	if (!defined $orig->{d}) {
564		$state->log("Couldn't delete #1 (no checksum)", $realname);
565		return;
566	}
567
568	if ($state->{quick} && $state->{quick} >= 2) {
569		unless ($state->{extra}) {
570			$self->mark_dir($state);
571			$state->log("You should also #1 #2", $action, $realname );
572			return;
573		}
574	} else {
575		my $d = $self->compute_digest($realname, $orig->{d});
576		if ($d->equals($orig->{d})) {
577			$state->say("File #1 identical to sample", $realname) if $state->verbose >= 2;
578		} else {
579			unless ($state->{extra}) {
580				$self->mark_dir($state);
581				$state->log("You should also #1 #2 (which was modified)", $action, $realname);
582				return;
583			}
584		}
585	}
586	$state->say("deleting #1", $realname) if $state->verbose >= 2;
587	return if $state->{not};
588	if (!unlink $realname) {
589		$state->errsay("Problem deleting #1: #2", $realname, $!);
590		$state->log("deleting #1 failed: #2", $realname, $!);
591	}
592}
593
594
595package OpenBSD::PackingElement::InfoFile;
596use File::Basename;
597use OpenBSD::Error;
598sub delete
599{
600	my ($self, $state) = @_;
601	unless ($state->{not}) {
602	    my $fullname = $state->{destdir}.$self->fullname;
603	    $state->vsystem(OpenBSD::Paths->install_info,
604		"--delete", "--info-dir=".dirname($fullname), '--', $fullname);
605	}
606	$self->SUPER::delete($state);
607}
608
609package OpenBSD::PackingElement::Shell;
610sub delete
611{
612	my ($self, $state) = @_;
613	unless ($state->{not}) {
614		my $destdir = $state->{destdir};
615		my $fullname = $self->fullname;
616		my @l=();
617		if (open(my $shells, '<', $destdir.OpenBSD::Paths->shells)) {
618			while(<$shells>) {
619				push(@l, $_);
620				s/^\#.*//o;
621				if ($_ =~ m/^\Q$fullname\E\s*$/) {
622					pop(@l);
623				}
624			}
625			close($shells);
626			open(my $shells2, '>', $destdir.OpenBSD::Paths->shells);
627			print $shells2 @l;
628			close $shells2;
629			$state->say("Shell #1 removed from #2",
630			    $fullname, $destdir.OpenBSD::Paths->shells)
631			    	if $state->verbose;
632		}
633	}
634	$self->SUPER::delete($state);
635}
636
637package OpenBSD::PackingElement::Extra;
638use File::Basename;
639
640sub delete
641{
642	my ($self, $state) = @_;
643	my $realname = $self->realname($state);
644	if ($state->verbose >= 2 && $state->{extra}) {
645		$state->say("deleting extra file: #1", $realname);
646	}
647	return if $state->{not};
648	return unless -e $realname or -l $realname;
649	if ($state->replacing) {
650		$state->log("Remember to update #1", $realname);
651		$self->mark_dir($state);
652	} elsif ($state->{extra}) {
653		unlink($realname) or
654		    $state->say("problem deleting extra file #1: #2", $realname, $!);
655	} else {
656		$state->log("You should also remove #1", $realname);
657		$self->mark_dir($state);
658	}
659}
660
661
662package OpenBSD::PackingElement::Extradir;
663sub delete
664{
665	my ($self, $state) = @_;
666	return unless $state->{extra};
667	my $realname = $self->realname($state);
668	return if $state->replacing;
669	if ($state->{extra}) {
670		$self->SUPER::delete($state);
671	} else {
672		$state->log("You should also remove the directory #1", $realname);
673		$self->mark_dir($state);
674	}
675}
676
677package OpenBSD::PackingElement::ExtraUnexec;
678
679sub delete
680{
681	my ($self, $state) = @_;
682	if ($state->{extra}) {
683		$self->run($state);
684	} else {
685		$state->log("You should also run #1", $self->{expanded});
686	}
687}
688
689package OpenBSD::PackingElement::Lib;
690sub delete
691{
692	my ($self, $state) = @_;
693	$self->SUPER::delete($state);
694	$self->mark_ldconfig_directory($state);
695}
696
697package OpenBSD::PackingElement::Depend;
698sub copy_old_stuff
699{
700	my ($self, $plist, $state) = @_;
701
702	OpenBSD::PackingElement::Comment->add($plist, "\@".$self->keyword." ".$self->stringize);
703}
704
705package OpenBSD::PackingElement::FDISPLAY;
706sub delete
707{
708	my ($self, $state) = @_;
709	$state->{current_set}{known_displays}{$self->{d}->key} = 1;
710	$self->SUPER::delete($state);
711}
712
713package OpenBSD::PackingElement::FUNDISPLAY;
714sub delete
715{
716	my ($self, $state) = @_;
717	my $d = $self->{d};
718	if (!$state->{current_set}{known_displays}{$self->{d}->key}) {
719		$self->prepare($state);
720	}
721	$self->SUPER::delete($state);
722}
723
724package OpenBSD::PackingElement::Mandir;
725sub delete
726{
727	my ($self, $state) = @_;
728	$state->{current_set}{known_mandirs}{$self->fullname} = 1;
729	$self->SUPER::delete($state);
730}
731
7321;
733