1# ex:ts=8 sw=4: 2# $OpenBSD: Cache.pm,v 1.13 2023/09/16 09:33:13 espie Exp $ 3# 4# Copyright (c) 2022 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 v5.36; 19 20# supplementary glue to add support for reading the update.db locate(1) 21# database in quirks 22package OpenBSD::PackageRepository::Cache; 23 24sub new($class, $state, $setlist) 25{ 26 return undef unless -f OpenBSD::Paths->updateinfodb; 27 28 my $o = bless { 29 raw_data => {}, 30 stems => {}, 31 state => $state }, $class; 32 33 $o->prime_update_info_cache($state, $setlist); 34 return $o; 35 36} 37sub pipe_locate($self, @params) 38{ 39 unshift(@params, OpenBSD::Paths->locate, 40 '-d', OpenBSD::Paths->updateinfodb, '--'); 41 my $state = $self->{state}; 42 $state->errsay("Running #1", join(' ', @params)) 43 if $state->defines("CACHING_VERBOSE"); 44 return @params; 45} 46 47# this is a hack to talk to quirks: the interface expects a list of 48# search objects such that the last one can do add_stem, so we oblige 49# (probably TODO: add a secondary interface in quirks, but this can do 50# in the meantime) 51sub add_stem($self, $stem) 52{ 53 $self->{stems}{$stem} = 1; 54} 55 56sub prime_update_info_cache($self, $state, $setlist) 57{ 58 my $progress = $state->progress; 59 my $found = {}; 60 61 my $pseudo_search = [$self]; 62 63 # figure out a list of names to precache 64 65 # okay, so basically instead of hitting locate once for each 66 # package on the distant repository, we precache all the stems 67 # we are asking to update/install 68 # this is based on the assumption that most names are "regular" 69 # and we won't cache too little or too much 70 for my $set (@{$setlist}) { 71 for my $h ($set->older, $set->hints) { 72 next if $h->{update_found}; 73 my $name = $h->pkgname; 74 my $stem = OpenBSD::PackageName::splitstem($name); 75 next if $stem =~ m/^\.libs\d*\-/; 76 next if $stem =~ m/^partial\-/; 77 $stem =~ s/\%.*//; # zap branch info 78 $stem =~ s/\-\-.*//; # and set flavors 79 $self->add_stem($stem); 80 $state->run_quirks( 81 sub($quirks) { 82 $quirks->tweak_search($pseudo_search, $h, 83 $state); 84 }); 85 } 86 } 87 my @list = sort keys %{$self->{stems}}; 88 return if @list == 0; 89 90 my $total = scalar @list; 91 $progress->set_header( 92 $state->f("Reading update info for installed packages", 93 $total)); 94 my $done = 0; 95 my $oldname = ""; 96 # This can't go much faster, I've tried splitting the params 97 # and running several locate(1) in //, but this yields negligible 98 # gains for a lot of added complexity (reduced from 18 to 14 seconds 99 # on my usual package install). 100 open my $fh, "-|", $self->pipe_locate(map { "$_-[0-9]*"} @list) 101 or $state->fatal("Can't run locate: #1", $!); 102 while (<$fh>) { 103 if (m/^(.*?)\:(.*)/) { 104 my ($pkgname, $value) = ($1, $2); 105 $found->{OpenBSD::PackageName::splitstem($pkgname)} = 1; 106 $self->{raw_data}{$pkgname} //= ''; 107 $self->{raw_data}{$pkgname} .= "$value\n"; 108 if ($pkgname ne $oldname) { 109 $oldname = $pkgname; 110 $done++; 111 } 112 $progress->show($done, $total); 113 } 114 } 115 close($fh); 116 return unless $state->defines("CACHING_VERBOSE"); 117 for my $k (@list) { 118 if (!defined $found->{$k}) { 119 $state->say("No cache entry for #1", $k); 120 } 121 } 122} 123 124sub get_cached_info($self, $name) 125{ 126 my $state = $self->{state}; 127 my $content; 128 if (exists $self->{raw_data}{$name}) { 129 $content = $self->{raw_data}{$name}; 130 } else { 131 my $stem = OpenBSD::PackageName::splitstem($name); 132 if (exists $self->{stems}{$stem}) { 133 $state->say("Negative caching for #1", $name) 134 if $state->defines("CACHING_VERBOSE"); 135 return undef; 136 } 137 $content = ''; 138 open my $fh, "-|", $self->pipe_locate($name.":*") or die $!; 139 while (<$fh>) { 140 if (m/^.*?\:(.*)/) { 141 $content .= $1."\n"; 142 } else { 143 return undef; 144 } 145 } 146 close ($fh); 147 } 148 if ($content eq '') { 149 $state->say("Cache miss for #1", $name) 150 if $state->defines("CACHING_VERBOSE"); 151 return undef; 152 } 153 open my $fh2, "<", \$content; 154 return OpenBSD::PackingList->read($fh2); 155} 156 1571; 158