1# ex:ts=8 sw=4: 2# $OpenBSD: Tracker.pm,v 1.33 2023/10/08 09:16:39 espie Exp $ 3# 4# Copyright (c) 2009 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 17# In order to deal with dependencies, we have to know what's actually installed, 18# and what can actually be updated. 19# Specifically, to solve a dependency: 20# - look at packages to_install 21# - look at installed packages 22# - if it's marked to_update, then we must process the update first 23# - if it's marked as installed, or as cant_update, or uptodate, then 24# we can use the installed packages. 25# - otherwise, in update mode, put a request to update the package (e.g., 26# create a new UpdateSet. 27 28# the Tracker object does maintain that information globally so that 29# Update/Dependencies can do its job. 30 31use v5.36; 32use warnings; 33 34package OpenBSD::Tracker; 35 36# XXX we're a singleton class 37our $s; 38 39sub new($class) 40{ 41 return $s //= bless {}, $class; 42} 43 44sub dump2($set) 45{ 46 if (defined $set->{merged}) { 47 return "merged from ".dump2($set->{merged}); 48 } 49 return join("/", 50 join(",", $set->newer_names), 51 join(",", $set->older_names), 52 join(",", $set->kept_names), 53 join(",", $set->hint_names)); 54} 55 56sub dump($) 57{ 58 return unless defined $s; 59 for my $l ('to_install', 'to_update') { 60 next unless defined $s->{$l}; 61 print STDERR "$l:\n"; 62 while (my ($k, $e) = each %{$s->{$l}}) { 63 print STDERR "\t$k => ", dump2($e), "\n"; 64 } 65 } 66 for my $l ('uptodate', 'can_install', 'cant_update') { 67 next unless defined $s->{$l}; 68 print STDERR "$l: ", join(' ', keys %{$s->{$l}}), "\n"; 69 } 70} 71 72sub sets_todo($self, $offset = 0) 73{ 74 return sprintf("%u/%u", (scalar keys %{$self->{done}})-$offset, 75 scalar keys %{$self->{total}}); 76} 77 78sub handle_set($self, $set) 79{ 80 $self->{total}{$set} = 1; 81 if ($set->{finished}) { 82 $self->{done}{$set} = 1; 83 } 84} 85 86sub known($self, $set) 87{ 88 for my $n ($set->newer, $set->older, $set->hints) { 89 $self->{known}{$n->pkgname} = 1; 90 } 91} 92 93sub add_set($self, $set) 94{ 95 for my $n ($set->newer) { 96 $self->{to_install}{$n->pkgname} = $set; 97 } 98 for my $n ($set->older, $set->hints) { 99 $self->{to_update}{$n->pkgname} = $set; 100 } 101 for my $n ($set->kept) { 102 delete $self->{to_update}{$n->pkgname}; 103 $self->{uptodate}{$n->pkgname} = 1; 104 if ($n->{is_firmware}) { 105 $self->{firmware}{$n->pkgname} = 1; 106 } 107 } 108 $self->known($set); 109 $self->handle_set($set); 110 return $self; 111} 112 113sub todo($self, @sets) 114{ 115 for my $set (@sets) { 116 $self->add_set($set); 117 } 118 return $self; 119} 120 121sub remove_set($self, $set) 122{ 123 for my $n ($set->newer) { 124 delete $self->{to_install}{$n->pkgname}; 125 delete $self->{cant_install}{$n->pkgname}; 126 } 127 for my $n ($set->kept, $set->older, $set->hints) { 128 delete $self->{to_update}{$n->pkgname}; 129 delete $self->{cant_update}{$n->pkgname}; 130 } 131 $self->handle_set($set); 132} 133 134sub uptodate($self, $set) 135{ 136 $set->{finished} = 1; 137 $self->remove_set($set); 138 for my $n ($set->older, $set->kept) { 139 $self->{uptodate}{$n->pkgname} = 1; 140 if ($n->{is_firmware}) { 141 $self->{firmware}{$n->pkgname} = 1; 142 } 143 } 144} 145 146sub cant($self, $set) 147{ 148 $set->{finished} = 1; 149 $self->remove_set($set); 150 $self->known($set); 151 for my $n ($set->older) { 152 $self->{cant_update}{$n->pkgname} = 1; 153 } 154 for my $n ($set->newer) { 155 $self->{cant_install}{$n->pkgname} = 1; 156 } 157 for my $n ($set->kept) { 158 $self->{uptodate}{$n->pkgname} = 1; 159 } 160} 161 162sub done($self, $set) 163{ 164 $set->{finished} = 1; 165 $self->remove_set($set); 166 $self->known($set); 167 168 for my $n ($set->newer) { 169 $self->{uptodate}{$n->pkgname} = 1; 170 $self->{installed}{$n->pkgname} = 1; 171 } 172 for my $n ($set->kept) { 173 $self->{uptodate}{$n->pkgname} = 1; 174 } 175} 176 177sub is($self, $k, $pkg) 178{ 179 my $set = $self->{$k}{$pkg}; 180 if (ref $set) { 181 return $set->real_set; 182 } else { 183 return $set; 184 } 185} 186 187sub is_known($self, $pkg) 188{ 189 return $self->is('known', $pkg); 190} 191 192sub is_installed($self, $pkg) 193{ 194 return $self->is('installed', $pkg); 195} 196 197sub is_to_update($self, $pkg) 198{ 199 return $self->is('to_update', $pkg); 200} 201 202sub cant_list($self) 203{ 204 return keys %{$self->{cant_update}}; 205} 206 207sub did_something($self) 208{ 209 for my $k (keys %{$self->{uptodate}}) { 210 next if $self->{firmware}{$k}; 211 return 1; 212 } 213 return 0; 214} 215 216sub cant_install_list($self) 217{ 218 return keys %{$self->{cant_install}}; 219} 220 2211; 222