1#! /usr/bin/perl
2
3# ex:ts=8 sw=4:
4# $OpenBSD: PkgCheck.pm,v 1.62 2016/08/26 18:19:21 espie Exp $
5#
6# Copyright (c) 2003-2014 Marc Espie <espie@openbsd.org>
7#
8# Permission to use, copy, modify, and distribute this software for any
9# purpose with or without fee is hereby granted, provided that the above
10# copyright notice and this permission notice appear in all copies.
11#
12# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20use strict;
21use warnings;
22
23use OpenBSD::AddCreateDelete;
24use OpenBSD::SharedLibs;
25
26package Installer::State;
27our @ISA = qw(OpenBSD::PkgAdd::State);
28sub new
29{
30	my ($class, $cmd) = @_;
31	my $state = $class->SUPER::new($cmd);
32	$state->{localbase} = OpenBSD::Paths->localbase;
33	return $state;
34}
35
36package Installer;
37our @ISA = qw(OpenBSD::PkgAdd);
38
39sub new
40{
41	my ($class, $mystate) = @_;
42	my $state = Installer::State->new("pkg_check");
43	$state->{v} = $mystate->{v};
44	$state->{subst} = $mystate->{subst};
45	$state->{interactive} = $mystate->{interactive};
46	$state->{destdir} = $mystate->{destdir};
47	$state->progress->setup($state->opt('x'), $state->opt('m'), $state);
48	bless { state => $state}, $class;
49}
50
51sub install
52{
53	my ($self, $pkg) = @_;
54	my $state = $self->{state};
55	push(@{$state->{setlist}},
56	    $state->updateset->add_hints2($pkg));
57	$self->framework($state);
58	return $state->{bad} != 0;
59}
60
61package OpenBSD::PackingElement;
62sub thorough_check
63{
64	my ($self, $state) = @_;
65	$self->basic_check($state);
66}
67
68sub basic_check
69{
70}
71
72sub find_dependencies
73{
74}
75
76package OpenBSD::PackingElement::FileBase;
77use File::Basename;
78
79sub basic_check
80{
81	my ($self, $state) = @_;
82
83	my $name = $state->destdir($self->fullname);
84	$state->{known}{dirname($name)}{basename($name)} = 1;
85	if ($self->{symlink}) {
86		if (!-l $name) {
87			if (!-e $name) {
88				$state->log("#1 should be a symlink but does not exist", $name);
89			} else {
90				$state->log("#1 is not a symlink", $name);
91			}
92		} else {
93			if (readlink($name) ne $self->{symlink}) {
94				$state->log("#1 should point to #2 but points to #3 instead",
95				    $name, $self->{symlink}, readlink($name));
96			}
97		}
98		return;
99	}
100	if (!-e $name) {
101		if (-l $name) {
102			$state->log("#1 points to non-existent #2",
103			    $name, readlink($name));
104		} else {
105			$state->log("#1 should exist", $name);
106		}
107	}
108	if (!-f _) {
109		$state->log("#1 is not a file", $name);
110	}
111	if ($self->{link}) {
112		my ($a, $b) = (stat _)[0, 1];
113		if (!-f $state->destdir($self->{link})) {
114			$state->log("#1 should link to non-existent #2",
115			    $name, $self->{link});
116		} else {
117			my ($c, $d) = (stat _)[0, 1];
118			if (defined $a && defined $c) {
119				if ($a != $c || $b != $d) {
120					$state->log("#1 doesn't link to #2",
121					    $name, $self->{link});
122				}
123			}
124		}
125	}
126}
127
128sub thorough_check
129{
130	my ($self, $state) = @_;
131	my $name = $state->destdir($self->fullname);
132	$self->basic_check($state);
133	return if $self->{link} or $self->{symlink} or $self->{nochecksum};
134	if (!-r $name) {
135		$state->log("can't read #1", $name);
136		return;
137	}
138	if (!defined $self->{d}) {
139		$state->log("no checksum for #1", $name);
140		return;
141	}
142	my $d = $self->compute_digest($name, ref($self->{d}));
143	if (!$d->equals($self->{d})) {
144		$state->log("checksum for #1 does not match", $name);
145	}
146}
147
148package OpenBSD::PackingElement::SpecialFile;
149sub basic_check
150{
151	&OpenBSD::PackingElement::FileBase::basic_check;
152}
153
154sub thorough_check
155{
156	&OpenBSD::PackingElement::FileBase::basic_check;
157}
158
159package OpenBSD::PackingElement::DirlikeObject;
160sub basic_check
161{
162	my ($self, $state) = @_;
163	my $name = $state->destdir($self->fullname);
164	$state->{known}{$name} //= {};
165	if (!-e $name) {
166		$state->log("#1 should exist", $name);
167	}
168	if (!-d _) {
169		$state->log("#1 is not a directory", $name);
170	}
171}
172
173package OpenBSD::PackingElement::Sample;
174use File::Basename;
175sub basic_check
176{
177	my ($self, $state) = @_;
178	my $name = $state->destdir($self->fullname);
179	$state->{known}{dirname($name)}{basename($name)} = 1;
180}
181
182package OpenBSD::PackingElement::Sampledir;
183sub basic_check
184{
185	my ($self, $state) = @_;
186	my $name = $state->destdir($self->fullname);
187	$state->{known}{$name} //= {};
188}
189
190package OpenBSD::PackingElement::Mandir;
191sub basic_check
192{
193	my ($self, $state) = @_;
194	$self->SUPER::basic_check($state);
195	my $name = $state->destdir($self->fullname);
196	for my $file (OpenBSD::Paths::man_cruft()) {
197		$state->{known}{$name}{$file} = 1;
198	}
199}
200
201package OpenBSD::PackingElement::Fontdir;
202sub basic_check
203{
204	my ($self, $state) = @_;
205	$self->SUPER::basic_check($state);
206	my $name = $state->destdir($self->fullname);
207	for my $i (qw(fonts.alias fonts.scale fonts.dir)) {
208		$state->{known}{$name}{$i} = 1;
209	}
210}
211
212package OpenBSD::PackingElement::Infodir;
213sub basic_check
214{
215	my ($self, $state) = @_;
216	$self->SUPER::basic_check($state);
217	my $name = $state->destdir($self->fullname);
218	$state->{known}{$name}{'dir'} = 1;
219}
220
221package OpenBSD::PackingElement::Dependency;
222sub find_dependencies
223{
224	my ($self, $state, $l, $checker) = @_;
225	# several ways to failure
226	if (!$self->spec->is_valid) {
227		$state->log("invalid \@", $self->keyword, " ",
228		    $self->stringize);
229		return;
230	}
231	my @deps = $self->spec->filter(@$l);
232	if (@deps == 0) {
233		$state->log("dependency #1 does not match any installed package",
234		    $self->stringize);
235		return;
236	}
237	my $okay = 0;
238	for my $i (@deps) {
239		if ($checker->find($i)) {
240			$okay = 1;
241		}
242	}
243	if (!$okay) {
244		$checker->not_found($deps[0]);
245	}
246}
247
248package OpenBSD::PackingElement::Wantlib;
249sub find_dependencies
250{
251	my ($self, $state, $l, $checker) = @_;
252	my $r = OpenBSD::SharedLibs::lookup_libspec($state->{localbase},
253	    $self->spec);
254	if (defined $r && @$r != 0) {
255		my $okay = 0;
256		for my $lib (@$r) {
257			my $i = $lib->origin;
258			if ($i eq 'system') {
259				$okay = 1;
260				$state->{needed_libs}{$lib->to_string} = 1;
261				next;
262			}
263			if ($checker->find($i)) {
264				$okay = 1;
265			}
266		}
267		if (!$okay) {
268			$checker->not_found($r->[0]->origin);
269		}
270	} else {
271		$state->log("#1 not found", $self->stringize);
272	}
273}
274
275package OpenBSD::PkgCheck::State;
276our @ISA = qw(OpenBSD::AddCreateDelete::State);
277
278use File::Spec;
279use OpenBSD::Log;
280use File::Basename;
281
282sub init
283{
284	my $self = shift;
285	$self->{l} = OpenBSD::Log->new($self);
286	$self->SUPER::init;
287}
288
289sub log
290{
291	my $self = shift;
292	if (@_ == 0) {
293		return $self->{l};
294	} else {
295		$self->{l}->say(@_);
296	}
297}
298
299sub safe
300{
301	my ($self, $string) = @_;
302	$string =~ s/[^\w\d\s\+\-\.\>\<\=\/\;\:\,\(\)\[\]]/?/g;
303	return $string;
304}
305
306sub handle_options
307{
308	my $self = shift;
309	$self->{no_exports} = 1;
310
311	$self->add_interactive_options;
312	$self->SUPER::handle_options('fFB:q',
313		'[-FfIimnqvx] [-B pkg-destdir] [-D value]');
314	$self->{force} = $self->opt('f');
315	$self->{quick} = $self->opt('q') // 0;
316	$self->{filesystem} = $self->opt('F');
317	if (defined $self->opt('B')) {
318		$self->{destdir} = $self->opt('B');
319	}
320	if (defined $self->{destdir}) {
321		$self->{destdir} .= '/';
322	} else {
323		$self->{destdir} = '';
324	}
325}
326
327sub build_tag
328{
329}
330
331sub destdir
332{
333	my ($self, $path) = @_;
334	return File::Spec->canonpath($self->{destdir}.$path);
335}
336
337sub process_entry
338{
339	my ($self, $entry) = @_;
340	my $name = $self->destdir($entry);
341	$self->{known}{dirname($name)}{basename($name)} = 1;
342}
343
344package OpenBSD::DependencyCheck;
345
346sub new
347{
348	my ($class, $state, $name, $req) = @_;
349	my $o = bless {
350		not_yet => {},
351		possible => {},
352		others => {},
353		name => $name,
354		req => $req
355	    }, $class;
356	for my $pkg ($req->list) {
357		$o->{not_yet}{$pkg} = 1;
358		if ($state->{exists}{$pkg}) {
359			$o->{possible}{$pkg} = 1;
360		} else {
361			$state->errsay("#1: bogus #2",
362			    $name, $o->string($state->safe($pkg)));
363		}
364	}
365	return $o;
366}
367
368sub find
369{
370	my ($self, $name) = @_;
371	if ($self->{possible}{$name}) {
372		delete $self->{not_yet}{$name};
373		return 1;
374	} else {
375		return 0;
376	}
377}
378
379sub not_found
380{
381	my ($self, $name) = @_;
382	$self->{others}{$name} = 1;
383}
384
385sub ask_delete_deps
386{
387	my ($self, $state, $l) = @_;
388	if ($state->{force}) {
389		$self->{req}->delete(@$l);
390	} elsif ($state->confirm("Remove missing ".
391		    $state->safe($self->string(@$l)))) {
392			$self->{req}->delete(@$l);
393	}
394}
395
396sub ask_add_deps
397{
398	my ($self, $state, $l) = @_;
399	if ($state->{force}) {
400		$self->{req}->add(@$l);
401	} elsif ($state->confirm("Add missing ".
402		    $self->string(@$l))) {
403			$self->{req}->add(@$l);
404	}
405}
406
407sub adjust
408{
409	my ($self, $state) = @_;
410	if (keys %{$self->{not_yet}} > 0) {
411		my @todo = sort keys %{$self->{not_yet}};
412		unless ($state->{subst}->value("weed_libs")) {
413			@todo = grep {!/^\.libs/} @todo;
414		}
415		if (@todo != 0) {
416			$state->errsay("#1 has too many #2",
417			    $self->{name}, $state->safe($self->string(@todo)));
418			$self->ask_delete_deps($state, \@todo);
419		}
420	}
421	if (keys %{$self->{others}} > 0) {
422		my @todo = sort keys %{$self->{others}};
423		$state->errsay("#1 is missing #2",
424		    $self->{name}, $self->string(@todo));
425		    if ($self->{name} =~ m/^partial/) {
426			    $state->errsay("not a problem, since this is a partial- package");
427		    } else {
428			    $self->ask_add_deps($state, \@todo);
429		    }
430	}
431}
432
433package OpenBSD::DirectDependencyCheck;
434our @ISA = qw(OpenBSD::DependencyCheck);
435use OpenBSD::RequiredBy;
436sub string
437{
438	my $self = shift;
439	return "dependencies: ". join(' ', @_);
440}
441
442sub new
443{
444	my ($class, $state, $name) = @_;
445	return $class->SUPER::new($state, $name,
446	    OpenBSD::Requiring->new($name));
447}
448
449package OpenBSD::ReverseDependencyCheck;
450our @ISA = qw(OpenBSD::DependencyCheck);
451use OpenBSD::RequiredBy;
452sub string
453{
454	my $self = shift;
455	return "reverse dependencies: ". join(' ', @_);
456}
457
458sub new
459{
460	my ($class, $state, $name) = @_;
461	return $class->SUPER::new($state, $name,
462	    OpenBSD::RequiredBy->new($name));
463}
464
465package OpenBSD::Pkglocate;
466sub new
467{
468	my ($class, $state) = @_;
469	bless {state => $state, result => {unknown => []},
470	    params => []}, $class;
471}
472
473sub add_param
474{
475	my ($self, @p) = @_;
476	push(@{$self->{params}}, @p);
477	while (@{$self->{params}} > 200) {
478		$self->run_command;
479	}
480}
481
482sub run_command
483{
484	my $self = shift;
485
486	if (@{$self->{params}} == 0) {
487		return;
488	}
489	my %h = map {($_, 1)} @{$self->{params}};
490	open(my $cmd, '-|', 'pkg_locate', map {"*:$_"} @{$self->{params}});
491	while (<$cmd>) {
492		chomp;
493		my ($pkgname, $pkgpath, $path) = split(':', $_, 3);
494
495		# pkglocate will return false positives, so trim them
496		if ($h{$path}) {
497			push(@{$self->{result}{"$pkgname:$pkgpath"} }, $path);
498			delete $h{$path};
499		}
500	}
501	close($cmd);
502	for my $k (keys %h) {
503		push(@{$self->{result}{unknown}}, $k);
504	}
505
506	$self->{params} = [];
507}
508
509sub result
510{
511	my $self = shift;
512	while (@{$self->{params}} > 0) {
513		$self->run_command;
514	}
515	my $state = $self->{state};
516	my $r = $self->{result};
517	my $u = $r->{unknown};
518	delete $r->{unknown};
519
520	$state->say("Not found:");
521	for my $e (sort @$u) {
522		$state->say("\t#1", $e);
523	}
524
525	for my $k (sort keys %{$r}) {
526		$state->say("In #1:", $k);
527		for my $e (sort @{$r->{$k}}) {
528			$state->say("\t#1", $e);
529		}
530	}
531}
532
533package OpenBSD::PkgCheck;
534our @ISA = qw(OpenBSD::AddCreateDelete);
535
536use OpenBSD::PackageInfo;
537use OpenBSD::PackingList;
538use File::Find;
539use OpenBSD::Paths;
540use OpenBSD::Mtree;
541
542sub fill_base_system
543{
544	my ($self, $state) = @_;
545	open(my $cmd, '-|', 'locate',
546	    '-d', OpenBSD::Paths->srclocatedb,
547	    '-d', OpenBSD::Paths->xlocatedb, ':');
548	while (<$cmd>) {
549		chomp;
550		my ($set, $path) = split(':', $_, 2);
551		$state->{basesystem}{$path} = 1;
552	}
553	close($cmd);
554}
555
556sub remove
557{
558	my ($self, $state, $name) = @_;
559	$state->{removed}{$name} = 1;
560	my $dir = installed_info($name);
561	for my $i (@OpenBSD::PackageInfo::info) {
562		if (-e $dir.$i) {
563			if ($state->verbose) {
564				$state->say("unlink(#1)", $dir.$i);
565			}
566			unless ($state->{not}) {
567				unlink($dir.$i) or
568				    $state->errsay("#1: Couldn't delete #2: #3",
569				    	$name, $dir.$i, $!);
570			}
571		}
572	}
573	if (-f $dir) {
574		if ($state->verbose) {
575			$state->say("unlink(#1)", $dir);
576		}
577		unless ($state->{not}) {
578			unlink($dir) or
579			    $state->errsay("#1: Couldn't delete #2: #3",
580				$name, $dir, $!);
581		}
582	} elsif (-d $dir) {
583		if ($state->verbose) {
584			$state->say("rmdir(#1)", $dir);
585		}
586		unless ($state->{not}) {
587			rmdir($dir) or
588			    $state->errsay("#1: Couldn't delete #2: #3",
589			    	$name, $dir, $!);
590		}
591	}
592}
593
594sub may_remove
595{
596	my ($self, $state, $name) = @_;
597	if ($state->{force}) {
598		$self->remove($state, $name);
599	} elsif ($state->confirm("Remove wrong package $name")) {
600			$self->remove($state, $name);
601	}
602	$state->{bogus}{$name} = 1;
603}
604
605sub for_all_packages
606{
607	my ($self, $state, $l, $msg, $code) = @_;
608
609	$state->progress->for_list($msg, $l,
610	    sub {
611		return if $state->{removed}{$_[0]};
612		if ($state->{bogus}{$_[0]}) {
613			$state->errsay("skipping #1", $_[0]);
614			return;
615		}
616		&$code;
617	    });
618}
619
620sub sanity_check
621{
622	my ($self, $state, $l) = @_;
623	$self->for_all_packages($state, $l, "Packing-list sanity", sub {
624		my $name = shift;
625		my $info = installed_info($name);
626		if (-f $info) {
627			$state->errsay("#1: #2 should be a directory",
628			    $state->safe($name), $state->safe($info));
629			if ($info =~ m/\.core$/) {
630				$state->errsay("looks like a core dump, ".
631					"removing");
632				$self->remove($state, $name);
633			} else {
634				$self->may_remove($state, $name);
635			}
636			return;
637		}
638		my $contents = $info.OpenBSD::PackageInfo::CONTENTS;
639		unless (-f $contents) {
640			$state->errsay("#1: missing #2",
641			    $state->safe($name), $state->safe($contents));
642			$self->may_remove($state, $name);
643			return;
644		}
645		my $plist;
646		eval {
647			$plist = OpenBSD::PackingList->fromfile($contents);
648		};
649		if ($@ || !defined $plist) {
650			$state->errsay("#1: bad packing-list", $state->safe($name));
651			$self->may_remove($state, $name);
652			return;
653		}
654		if (!defined $plist->pkgname) {
655			$state->errsay("#1: no pkgname in plist",
656			    $state->safe($name));
657			$self->may_remove($state, $name);
658			return;
659		}
660		if ($plist->pkgname ne $name) {
661			$state->errsay("#1: pkgname does not match",
662			    $state->safe($name));
663			$self->may_remove($state, $name);
664		}
665		$plist->mark_available_lib($plist->pkgname, $state);
666		$state->{exists}{$plist->pkgname} = 1;
667	});
668}
669
670sub dependencies_check
671{
672	my ($self, $state, $l) = @_;
673	OpenBSD::SharedLibs::add_libs_from_system($state->{destdir}, $state);
674	$self->for_all_packages($state, $l, "Direct dependencies", sub {
675		my $name = shift;
676		$state->log->set_context($name);
677		my $plist = OpenBSD::PackingList->from_installation($name,
678		    \&OpenBSD::PackingList::DependOnly);
679		my $checker = OpenBSD::DirectDependencyCheck->new($state,
680		    $name);
681		$state->{localbase} = $plist->localbase;
682		$plist->find_dependencies($state, $l, $checker);
683		$checker->adjust($state);
684		for my $dep ($checker->{req}->list) {
685			push(@{$state->{reverse}{$dep}}, $name);
686		}
687	});
688}
689
690sub reverse_dependencies_check
691{
692	my ($self, $state, $l) = @_;
693	$self->for_all_packages($state, $l, "Reverse dependencies", sub {
694		my $name = shift;
695		my $checker = OpenBSD::ReverseDependencyCheck->new($state,
696		    $name);
697		for my $i (@{$state->{reverse}{$name}}) {
698			$checker->find($i) or $checker->not_found($i);
699		}
700		$checker->adjust($state);
701	});
702}
703
704sub package_files_check
705{
706	my ($self, $state, $l) = @_;
707	$self->for_all_packages($state, $l, "Files from packages", sub {
708		my $name = shift;
709		my $plist = OpenBSD::PackingList->from_installation($name);
710		$state->log->set_context($name);
711		if ($state->{quick}) {
712			$plist->basic_check($state);
713		} else {
714			$plist->thorough_check($state);
715		}
716		$plist->mark_available_lib($plist->pkgname, $state);
717	});
718}
719
720sub install_pkglocate
721{
722	my ($self, $state) = @_;
723
724	my $spec = 'pkglocatedb->=1.1';
725
726	my @l = installed_stems()->find('pkglocatedb');
727	require OpenBSD::PkgSpec;
728	if (OpenBSD::PkgSpec->new($spec)->match_ref(\@l)) {
729		return 1;
730	}
731	unless ($state->confirm("Unknown file system entries.\n".
732	    "Do you want to install $spec to look them up")) {
733	    	return 0;
734	}
735
736	require OpenBSD::PkgAdd;
737
738	$state->{installer} //= Installer->new($state);
739	if ($state->{installer}->install('pkglocatedb--')) {
740		return 1;
741	} else {
742		$state->errsay("Couldn't install #1", $spec);
743		return 0;
744	}
745}
746
747# non fancy display of unknown objects
748sub display_unknown
749{
750	my ($self, $state) = @_;
751	if (defined $state->{unknown}{file}) {
752		$state->say("Unknown files:");
753		for my $e (sort @{$state->{unknown}{file}}) {
754			$state->say("\t#1", $e);
755		}
756	}
757	if (defined $state->{unknown}{dir}) {
758		$state->say("Unknown directories:");
759		for my $e (sort {$b cmp $a } @{$state->{unknown}{dir}}) {
760			$state->say("\t#1", $e);
761		}
762	}
763}
764
765sub display_tmps
766{
767	my ($self, $state) = @_;
768	$state->say("Unregistered temporary files:");
769	for my $e (sort @{$state->{tmps}}) {
770		$state->say("\t#1", $e);
771	}
772	if ($state->{force}) {
773		unlink(@{$state->{tmps}});
774	} elsif ($state->confirm("Remove")) {
775			unlink(@{$state->{tmps}});
776	}
777}
778
779sub display_unregs
780{
781	my ($self, $state) = @_;
782	$state->say("System libs NOT in locate dbs:");
783	for my $e (sort @{$state->{unreg_libs}}) {
784		$state->say("\t#1", $e);
785	}
786}
787
788sub locate_unknown
789{
790	my ($self, $state) = @_;
791	my $locator = OpenBSD::Pkglocate->new($state);
792	if (defined $state->{unknown}{file}) {
793		$state->progress->for_list("Locating unknown files",
794		    $state->{unknown}{file},
795			sub {
796				$locator->add_param($_[0]);
797			});
798	}
799	if (defined $state->{unknown}{dir}) {
800		$state->progress->for_list("Locating unknown directories",
801		    $state->{unknown}{dir},
802			sub {
803				$locator->add_param($_[0]);
804			});
805	}
806	$locator->result($state);
807}
808
809sub fill_localbase
810{
811	my ($self, $state, $base) = @_;
812	for my $file (OpenBSD::Paths::man_cruft()) {
813		$state->{known}{$base."/man"}{$file} = 1;
814	}
815	$state->{known}{$base."/info"}{'dir'} = 1;
816	$state->{known}{$base."/lib/X11"}{'app-defaults'} = 1;
817	$state->{known}{$base."/libdata"} = {};
818	$state->{known}{$base."/libdata/perl5"} = {};
819}
820
821sub fill_root
822{
823	my ($self, $state, $root) = @_;
824	OpenBSD::Mtree::parse($state->{known}, $root,
825	    '/etc/mtree/4.4BSD.dist', 1);
826	OpenBSD::Mtree::parse($state->{known}, $root,
827	    '/etc/mtree/BSD.x11.dist', 1);
828}
829
830sub filesystem_check
831{
832	my ($self, $state) = @_;
833	$state->{known} //= {};
834	$self->fill_localbase($state,
835	    $state->destdir(OpenBSD::Paths->localbase));
836	my $root = $state->{destdir} || '/';
837	$self->fill_root($state, $root);
838	$self->fill_base_system($state);
839
840	$state->progress->set_header("Checking file system");
841	find(sub {
842		$state->progress->working(1024);
843		if (-d $_) {
844			for my $i ('/dev', '/home', OpenBSD::Paths->pkgdb, '/var/log', '/var/backups', '/var/cron', '/var/run', '/tmp', '/var/tmp') {
845				if ($File::Find::name eq $state->destdir($i)) {
846					$File::Find::prune = 1;
847				}
848			}
849		}
850		if (defined $state->{basesystem}{$File::Find::name}) {
851			delete $state->{basesystem}{$File::Find::name};
852			return;
853		}
854		if (defined $state->{needed_libs}{$File::Find::name}) {
855			push(@{$state->{unreg_libs}}, $File::Find::name);
856			return;
857		}
858		if (-d $_) {
859			if ($_ eq "lost+found") {
860				$state->say("fsck(8) info found: #1",
861				    $File::Find::name);
862				$File::Find::prune = 1;
863				return;
864			}
865			# some directories we've got to ignore
866			if (! -r -x _) {
867				$File::Find::prune = 1;
868				$state->errsay("can't enter #1",
869				    $File::Find::name);
870			}
871			return if defined $state->{known}{$File::Find::name};
872			if (-l $_) {
873				return if $state->{known}{$File::Find::dir}{$_};
874			}
875			push(@{$state->{unknown}{dir}}, $File::Find::name);
876			$File::Find::prune = 1;
877		} else {
878			return if $state->{known}{$File::Find::dir}{$_};
879			if (m/^pkg\..{10}$/) {
880				push(@{$state->{tmps}}, $File::Find::name);
881			} else {
882				push(@{$state->{unknown}{file}},
883				    $File::Find::name);
884			}
885		}
886	}, $root);
887	if (defined $state->{tmps}) {
888		$self->display_tmps($state);
889	}
890	if (defined $state->{unreg_libs}) {
891		$self->display_unregs($state);
892	}
893	if (defined $state->{unknown}) {
894		if ($self->install_pkglocate($state)) {
895			$self->locate_unknown($state);
896		} else {
897			$self->display_unknown($state);
898		}
899	}
900}
901
902sub run
903{
904	my ($self, $state) = @_;
905
906	my @list = installed_packages();
907	$self->sanity_check($state, \@list);
908	$self->dependencies_check($state, \@list);
909	$state->log->dump;
910	$self->reverse_dependencies_check($state, \@list);
911	$state->log->dump;
912	if ($state->{quick} < 2) {
913		$self->package_files_check($state, \@list);
914		$state->log->dump;
915	}
916	if ($state->{filesystem}) {
917		$self->filesystem_check($state);
918		$state->progress->next;
919	}
920}
921
922sub parse_and_run
923{
924	my ($self, $cmd) = @_;
925
926	my $state = OpenBSD::PkgCheck::State->new($cmd);
927	$state->handle_options;
928	if (@ARGV != 0) {
929		$state->usage;
930	}
931	lock_db(0, $state) unless $state->{subst}->value('nolock');
932	$self->run($state);
933	return 0;
934}
935
9361;
937