1# ex:ts=8 sw=4: 2# $OpenBSD: PackingElement.pm,v 1.291 2024/04/30 14:26:50 sthen 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 v5.36; 19 20use OpenBSD::PackageInfo; 21use OpenBSD::Paths; 22 23# This is the basic class, which is mostly abstract, except for 24# create and register_with_factory. 25# It does provide base methods for stuff under it, though. 26 27# XXX PackingElement uses (very seldom) multiple inheritance: 28# the subclasses ::DirBase and ::Unique are used as mix-ins 29# and thus contain very limited functionality ! 30 31package OpenBSD::PackingElement; 32our %keyword; 33 34sub create($class, $line, $plist) 35{ 36 if ($line =~ m/^\@(\S+)\s*(.*)$/o) { 37 if (defined $keyword{$1}) { 38 $keyword{$1}->add($plist, $2); 39 } else { 40 die "Unknown element: $line"; 41 } 42 } else { 43 chomp $line; 44 OpenBSD::PackingElement::File->add($plist, $line); 45 } 46} 47 48sub register_with_factory($class, $k = $class->keyword, $o = $class) 49{ 50 $keyword{$k} = $o; 51} 52 53sub category($) { 'items' } 54 55sub new($class, $args) 56{ 57 bless { name => $args }, $class; 58} 59 60sub remove($self, $plist) 61{ 62 $self->{deleted} = 1; 63} 64 65sub clone($object) 66{ 67 # shallow copy 68 my %h = %$object; 69 bless \%h, ref($object); 70} 71 72 73# $self->register_manpage($plstate, $key) 74sub register_manpage($, $, $) 75{ 76} 77 78# plist keeps a "state" while reading a plist 79# $self->destate($plstate) 80sub destate($, $) 81{ 82} 83 84sub add_object($self, $plist) 85{ 86 $self->destate($plist->{state}); 87 $plist->add2list($self); 88 return $self; 89} 90 91# $class->add($plist, @args): 92# create an object with the correct arguments 93# returns the actual object created, IF ANY (XXX see subclasses 94# for instances of annotations like @symlink that DON'T create 95# an actual object) 96# 97# most add methods have ONE single argument, except for 98# subclasses generated from comments ! 99sub add($class, $plist, @args) 100{ 101 my $self = $class->new(@args); 102 return $self->add_object($plist); 103} 104 105sub needs_keyword($) { 1 } 106 107sub write($self, $fh) 108{ 109 my $s = $self->stringize; 110 if ($self->needs_keyword) { 111 $s = " $s" unless $s eq ''; 112 say $fh "\@", $self->keyword, "$s"; 113 } else { 114 say $fh $s; 115 } 116} 117 118# specialized version to avoid copying digital signatures over 119sub write_no_sig($self, $fh) 120{ 121 $self->write($fh); 122} 123 124sub write_without_variation($self, $fh) 125{ 126 $self->write_no_sig($fh); 127} 128 129# needed for comment checking 130sub fullstring($self) 131{ 132 my $s = $self->stringize; 133 if ($self->needs_keyword) { 134 $s = " $s" unless $s eq ''; 135 return "\@".$self->keyword.$s; 136 } else { 137 return $s; 138 } 139} 140 141sub name($self) 142{ 143 return $self->{name}; 144} 145 146sub set_name($self, $v) 147{ 148 $self->{name} = $v; 149} 150 151sub stringize($self) 152{ 153 return $self->name; 154} 155 156sub IsFile($) { 0 } 157 158sub is_a_library($) { 0 } 159sub NoDuplicateNames($) { 0 } 160 161 162sub copy_shallow_if($self, $copy, $h) 163{ 164 $self->add_object($copy) if defined $h->{$self}; 165} 166 167sub copy_deep_if($self, $copy, $h) 168{ 169 $self->clone->add_object($copy) if defined $h->{$self}; 170} 171 172sub finish($class, $state) 173{ 174 OpenBSD::PackingElement::Fontdir->finish($state); 175 OpenBSD::PackingElement::RcScript->report($state); 176 if (defined $state->{readmes}) { 177 $state->say("New and changed readme(s):"); 178 for my $file (sort @{$state->{readmes}}) { 179 $state->say("\t#1", $file); 180 } 181 } 182} 183 184# Basic class hierarchy 185 186# various stuff that's only linked to objects before/after them 187# this class doesn't have real objects: no valid new nor clone... 188package OpenBSD::PackingElement::Annotation; 189our @ISA=qw(OpenBSD::PackingElement); 190sub new($) { die "Can't create annotation objects" } 191 192# concrete objects 193package OpenBSD::PackingElement::Object; 194our @ISA=qw(OpenBSD::PackingElement); 195 196sub cwd($self) 197{ 198 return ${$self->{cwd}}; 199} 200 201# most objects should be fs relative, but there are 202# exceptions, such as sample files that will get installed 203# under /etc, or rc files ! 204sub absolute_okay($) { 0 } 205 206sub compute_fullname($self, $state) 207{ 208 $self->{cwd} = $state->{cwd}; 209 $self->set_name(File::Spec->canonpath($self->name)); 210 if ($self->name =~ m|^/|) { 211 unless ($self->absolute_okay) { 212 die "Absolute name forbidden: ", $self->name; 213 } 214 } 215} 216 217sub make_full($self, $path) 218{ 219 if ($path !~ m|^/|o && $self->cwd ne '.') { 220 $path = $self->cwd."/".$path; 221 $path =~ s,^//,/,; 222 } 223 return $path; 224} 225 226sub fullname($self) 227{ 228 return $self->make_full($self->name); 229} 230 231sub compute_modes($self, $state) 232{ 233 if (defined $state->{mode}) { 234 $self->{mode} = $state->{mode}; 235 } 236 if (defined $state->{owner}) { 237 $self->{owner} = $state->{owner}; 238 if (defined $state->{uid}) { 239 $self->{uid} = $state->{uid}; 240 } 241 } 242 if (defined $state->{group}) { 243 $self->{group} = $state->{group}; 244 if (defined $state->{gid}) { 245 $self->{gid} = $state->{gid}; 246 } 247 } 248} 249 250# concrete objects with file-like behavior 251package OpenBSD::PackingElement::FileObject; 252our @ISA=qw(OpenBSD::PackingElement::Object); 253 254sub NoDuplicateNames($) { 1 } 255 256sub dirclass($) { undef } 257 258sub new($class, $args) 259{ 260 if ($args =~ m/^(.*?)\/+$/o and defined $class->dirclass) { 261 bless { name => $1 }, $class->dirclass; 262 } else { 263 bless { name => $args }, $class; 264 } 265} 266 267sub destate($self, $state) 268{ 269 $state->{lastfileobject} = $self; 270 $self->compute_fullname($state); 271} 272 273sub set_tempname($self, $tempname) 274{ 275 $self->{tempname} = $tempname; 276} 277 278sub realname($self, $state) 279{ 280 my $name = $self->fullname; 281 if (defined $self->{tempname}) { 282 $name = $self->{tempname}; 283 } 284 return $state->{destdir}.$name; 285} 286 287sub compute_digest($self, $filename, $class = 'OpenBSD::sha') 288{ 289 require OpenBSD::md5; 290 return $class->new($filename); 291} 292 293# exec/unexec and friends 294package OpenBSD::PackingElement::Action; 295our @ISA=qw(OpenBSD::PackingElement::Object); 296 297# persistent state for following objects 298package OpenBSD::PackingElement::State; 299our @ISA=qw(OpenBSD::PackingElement::Object); 300 301# meta information, stored elsewhere 302package OpenBSD::PackingElement::Meta; 303our @ISA=qw(OpenBSD::PackingElement); 304 305# XXX mix-in class, see comment at top of file 306package OpenBSD::PackingElement::Unique; 307our @ISA=qw(OpenBSD::PackingElement::Meta); 308 309sub add_object($self, $plist) 310{ 311 $self->destate($plist->{state}); 312 $plist->addunique($self); 313 return $self; 314} 315 316sub remove($self, $plist) 317{ 318 delete $plist->{$self->category}; 319} 320 321sub category($self) 322{ 323 return ref($self); 324} 325 326# all the stuff that ends up in signatures 327package OpenBSD::PackingElement::VersionElement; 328our @ISA=qw(OpenBSD::PackingElement::Meta); 329 330# all dependency information 331package OpenBSD::PackingElement::Depend; 332our @ISA=qw(OpenBSD::PackingElement::VersionElement); 333 334# Abstract class for all file-like elements 335package OpenBSD::PackingElement::FileBase; 336our @ISA=qw(OpenBSD::PackingElement::FileObject); 337 338use File::Basename; 339 340sub write($self, $fh) 341{ 342 print $fh "\@comment no checksum\n" if defined $self->{nochecksum}; 343 print $fh "\@comment no debug\n" if defined $self->{nodebug}; 344 $self->SUPER::write($fh); 345 if (defined $self->{d}) { 346 $self->{d}->write($fh); 347 } 348 if (defined $self->{size}) { 349 say $fh "\@size ", $self->{size}; 350 } 351 if (defined $self->{ts}) { 352 say $fh "\@ts ", $self->{ts}; 353 } 354 if (defined $self->{symlink}) { 355 say $fh "\@symlink ", $self->{symlink}; 356 } 357 if (defined $self->{link}) { 358 say $fh "\@link ", $self->{link}; 359 } 360 if (defined $self->{tempname}) { 361 say $fh "\@temp ", $self->{tempname}; 362 } 363} 364 365sub destate($self, $state) 366{ 367 $self->SUPER::destate($state); 368 $state->{lastfile} = $self; 369 $state->{lastchecksummable} = $self; 370 $self->compute_modes($state); 371 if (defined $state->{nochecksum}) { 372 $self->{nochecksum} = 1; 373 undef $state->{nochecksum}; 374 } 375 if (defined $state->{nodebug}) { 376 $self->{nodebug} = 1; 377 undef $state->{nodebug}; 378 } 379} 380 381sub add_digest($self, $d) 382{ 383 $self->{d} = $d; 384} 385 386sub add_size($self, $sz) 387{ 388 $self->{size} = $sz; 389} 390 391sub add_timestamp($self, $ts) 392{ 393 $self->{ts} = $ts; 394} 395 396# XXX symlink/hardlinks are properties of File, 397# because we want to use inheritance for other stuff. 398 399sub make_symlink($self, $linkname) 400{ 401 $self->{symlink} = $linkname; 402} 403 404sub make_hardlink($self, $linkname) 405{ 406 $self->{link} = $linkname; 407} 408 409sub may_check_digest($self, $path, $state) 410{ 411 if ($state->{check_digest}) { 412 $self->check_digest($path, $state); 413 } 414} 415 416sub check_digest($self, $path, $state) 417{ 418 return if $self->{link} or $self->{symlink}; 419 if (!defined $self->{d}) { 420 $state->log->fatal($state->f("#1 does not have a signature", 421 $self->fullname)); 422 } 423 my $d = $self->compute_digest($path); 424 if (!$d->equals($self->{d})) { 425 $state->log->fatal($state->f("checksum for #1 does not match", 426 $self->fullname)); 427 } 428 if ($state->verbose >= 3) { 429 $state->say("Checksum match for #1", $self->fullname); 430 } 431} 432 433sub IsFile($) { 1 } 434 435package OpenBSD::PackingElement::FileWithDebugInfo; 436our @ISA=qw(OpenBSD::PackingElement::FileBase); 437 438package OpenBSD::PackingElement::File; 439our @ISA=qw(OpenBSD::PackingElement::FileBase); 440 441use OpenBSD::PackageInfo qw(is_info_name); 442sub keyword($) { "file" } 443__PACKAGE__->register_with_factory; 444 445sub dirclass($) { "OpenBSD::PackingElement::Dir" } 446 447sub needs_keyword($self) 448{ 449 # files/dirnames that starts with an @ will require a keyword 450 return $self->stringize =~ m/\^@/; 451} 452 453sub add_object($self, $plist) 454{ 455 $self->destate($plist->{state}); 456 my $j = is_info_name($self->name); 457 if ($j && $self->cwd eq '.') { 458 bless $self, "OpenBSD::PackingElement::$j"; 459 $self->add_object($plist); 460 } else { 461 $plist->add2list($self); 462 } 463 return $self; 464} 465 466package OpenBSD::PackingElement::Sample; 467our @ISA=qw(OpenBSD::PackingElement::FileObject); 468 469sub keyword($) { "sample" } 470sub absolute_okay($) { 1 } 471__PACKAGE__->register_with_factory; 472 473sub destate($self, $state) 474{ 475 if ($state->{lastfile} isa OpenBSD::PackingElement::SpecialFile) { 476 die "Can't \@sample a specialfile: ", 477 $state->{lastfile}->stringize; 478 } 479 $self->{copyfrom} = $state->{lastfile}; 480 $self->compute_fullname($state); 481 $self->compute_modes($state); 482} 483 484sub dirclass($) { "OpenBSD::PackingElement::Sampledir" } 485 486# TODO @ghost data is not yet used 487# it's meant for files that used to be "registered" but are 488# somewhat autogenerated or something, and should vanish in a transparent way. 489# 490# the keyword was introduced very early but is (still) not used 491 492# TODO @ghost data is not yet used 493# it's meant for files that used to be "registered" but are 494# somewhat autogenerated or something, and should vanish in a transparent way. 495# 496# the keyword was introduced very early but is (still) not used 497 498package OpenBSD::PackingElement::Ghost; 499our @ISA = qw(OpenBSD::PackingElement::FileObject); 500 501sub keyword($) { "ghost" } 502sub absolute_okay($) { 1 } 503__PACKAGE__->register_with_factory; 504 505sub destate($self, $state) 506{ 507 $self->compute_fullname($state); 508 $self->compute_modes($state); 509} 510 511package OpenBSD::PackingElement::Sampledir; 512our @ISA=qw(OpenBSD::PackingElement::DirBase OpenBSD::PackingElement::Sample); 513 514sub absolute_okay($) { 1 } 515 516sub destate($self, $state) 517{ 518 $self->compute_fullname($state); 519 $self->compute_modes($state); 520} 521 522package OpenBSD::PackingElement::RcScript; 523use File::Basename; 524our @ISA = qw(OpenBSD::PackingElement::FileBase); 525 526sub keyword($) { "rcscript" } 527sub absolute_okay($) { 1 } 528__PACKAGE__->register_with_factory; 529 530sub destate($self, $state) 531{ 532 $self->compute_fullname($state); 533 $state->{lastfile} = $self; 534 $state->{lastchecksummable} = $self; 535 $self->compute_modes($state); 536} 537 538sub report($class, $state) 539{ 540 my @l; 541 for my $script (sort keys %{$state->{add_rcscripts}}) { 542 next if $state->{delete_rcscripts}{$script}; 543 push(@l, $script); 544 } 545 if (@l > 0) { 546 $state->say("The following new rcscripts were installed: #1", 547 join(' ', @l)); 548 $state->say("See rcctl(8) for details."); 549 } 550} 551 552package OpenBSD::PackingElement::InfoFile; 553our @ISA=qw(OpenBSD::PackingElement::FileBase); 554 555sub keyword($) { "info" } 556__PACKAGE__->register_with_factory; 557sub dirclass($) { "OpenBSD::PackingElement::Infodir" } 558 559package OpenBSD::PackingElement::Shell; 560our @ISA=qw(OpenBSD::PackingElement::FileWithDebugInfo); 561 562sub keyword($) { "shell" } 563__PACKAGE__->register_with_factory; 564 565package OpenBSD::PackingElement::Manpage; 566use File::Basename; 567our @ISA=qw(OpenBSD::PackingElement::FileBase); 568 569sub keyword($) { "man" } 570__PACKAGE__->register_with_factory; 571 572sub register_manpage($self, $state, $key) 573{ 574 # optimization: don't bother registering stuff from partial packages 575 # (makewhatis will complain that the names don't match anyway) 576 return if defined $self->{tempname}; 577 my $fname = $self->fullname; 578 if ($fname =~ m,^(.*/man(?:/\w+)?)/((?:man|cat)[1-9n]\w*/.*),) { 579 push(@{$state->{$key}{$1}}, $2); 580 } 581} 582 583sub is_source($self) 584{ 585 return $self->name =~ m/man\/man[^\/]+\/[^\/]+\.[\dln][^\/]?$/o; 586} 587 588sub source_to_dest($self) 589{ 590 my $v = $self->name; 591 $v =~ s/(man\/)man([^\/]+\/[^\/]+)\.[\dln][^\/]?$/$1cat$2.0/; 592 return $v; 593} 594 595# assumes the source is nroff, launches nroff 596sub format($self, $state, $dest, $destfh) 597{ 598 my $base = $state->{base}; 599 my $fname = $base.$self->fullname; 600 if (-z $fname) { 601 $state->error("empty source manpage: #1", $fname); 602 return; 603 } 604 open(my $fh, '<', $fname) or die "Can't read $fname: $!"; 605 my $line = <$fh>; 606 close $fh; 607 my @extra = (); 608 # extra preprocessors as described in man. 609 if ($line =~ m/^\'\\\"\s+(.*)$/o) { 610 for my $letter (split '', $1) { 611 if ($letter =~ m/[ept]/o) { 612 push(@extra, "-$letter"); 613 } elsif ($letter eq 'r') { 614 push(@extra, "-R"); 615 } 616 } 617 } 618 my $d = dirname($dest); 619 unless (-d $d) { 620 mkdir($d); 621 } 622 if (my ($dir, $file) = $fname =~ m/^(.*)\/([^\/]+\/[^\/]+)$/) { 623 my $r = $state->system( 624 sub() { 625 open STDOUT, '>&', $destfh or 626 die "Can't write to $dest: $!"; 627 close $destfh; 628 chdir($dir) or die "Can't chdir to $dir: $!"; 629 }, 630 $state->{groff} // OpenBSD::Paths->groff, 631 qw(-mandoc -mtty-char -E -Ww -Tascii -P -c), 632 @extra, '--', $file); 633 if ($r != 0) { 634 # system already displays an error message 635 return; 636 } 637 } else { 638 $state->error("Can't parse source name #1", $fname); 639 return; 640 } 641 return 1; 642} 643 644package OpenBSD::PackingElement::Lib; 645our @ISA=qw(OpenBSD::PackingElement::FileWithDebugInfo); 646 647our $todo = 0; 648 649sub keyword($) { "lib" } 650__PACKAGE__->register_with_factory; 651 652sub mark_ldconfig_directory($self, $state) 653{ 654 $state->ldconfig->mark_directory($self->fullname); 655} 656 657sub parse($self, $filename) 658{ 659 if ($filename =~ m/^(.*?)\/?lib([^\/]+)\.so\.(\d+)\.(\d+)$/o) { 660 return ($2, $3, $4, $1); 661 } else { 662 return undef; 663 } 664} 665 666sub is_a_library($) { 1 } 667 668package OpenBSD::PackingElement::Binary; 669our @ISA=qw(OpenBSD::PackingElement::FileWithDebugInfo); 670 671sub keyword($) { "bin" } 672__PACKAGE__->register_with_factory; 673 674package OpenBSD::PackingElement::StaticLib; 675our @ISA=qw(OpenBSD::PackingElement::FileWithDebugInfo); 676 677sub keyword($) { "static-lib" } 678__PACKAGE__->register_with_factory; 679 680package OpenBSD::PackingElement::SharedObject; 681our @ISA=qw(OpenBSD::PackingElement::FileWithDebugInfo); 682 683sub keyword($) { "so" } 684__PACKAGE__->register_with_factory; 685 686package OpenBSD::PackingElement::PkgConfig; 687our @ISA=qw(OpenBSD::PackingElement::FileBase); 688 689sub keyword($) { "pkgconfig" } 690__PACKAGE__->register_with_factory; 691 692package OpenBSD::PackingElement::LibtoolLib; 693our @ISA=qw(OpenBSD::PackingElement::FileBase); 694 695sub keyword($) { "ltlib" } 696__PACKAGE__->register_with_factory; 697 698# Comment is very special: 699# - some annotations are comments for historic reasons 700# - CVSTags need to be recognized for register-plist (obsolescent) 701# - tools like update-plist will recognize @comment'ed entries 702# and thus destate needs to run on normal comments 703package OpenBSD::PackingElement::Comment; 704our @ISA=qw(OpenBSD::PackingElement::Meta); 705 706sub keyword($) { "comment" } 707__PACKAGE__->register_with_factory; 708 709sub destate($self, $state) 710{ 711 $self->{cwd} = $state->{cwd}; 712} 713 714sub add($class, $plist, $args) 715{ 716 if ($args =~ m/^\$OpenBSD.*\$\s*$/o) { 717 return OpenBSD::PackingElement::CVSTag->add($plist, $args); 718 } elsif ($args =~ m/^(?:subdir|pkgpath)\=(.*?)\s+cdrom\=(.*?)\s+ftp\=(.*?)\s*$/o) { 719 return OpenBSD::PackingElement::ExtraInfo->add($plist, $1, $2, $3); 720 } elsif ($args =~ m/^(?:subdir|pkgpath)\=(.*?)\s+ftp\=(.*?)\s*$/o) { 721 return OpenBSD::PackingElement::ExtraInfo->add($plist, $1, undef, $2); 722 } elsif ($args eq 'no checksum') { 723 $plist->{state}{nochecksum} = 1; 724 return; 725 } elsif ($args eq 'no debug') { 726 $plist->{state}{nodebug} = 1; 727 return; 728 } else { 729 return $class->SUPER::add($plist, $args); 730 } 731} 732 733package OpenBSD::PackingElement::CVSTag; 734our @ISA=qw(OpenBSD::PackingElement::Meta); 735 736sub keyword($) { 'comment' } 737 738sub category($) { 'cvstags'} 739 740# don't incorporate this into compared signatures 741sub write_without_variation($, $) 742{ 743} 744 745package OpenBSD::PackingElement::sha; 746our @ISA=qw(OpenBSD::PackingElement::Annotation); 747 748__PACKAGE__->register_with_factory('sha'); 749 750sub add($class, $plist, $args) 751{ 752 require OpenBSD::md5; 753 754 $plist->{state}->{lastchecksummable}->add_digest(OpenBSD::sha->fromstring($args)); 755 return; 756} 757 758package OpenBSD::PackingElement::symlink; 759our @ISA=qw(OpenBSD::PackingElement::Annotation); 760 761__PACKAGE__->register_with_factory('symlink'); 762 763sub add($class, $plist, $args) 764{ 765 $plist->{state}->{lastfile}->make_symlink($args); 766 return; 767} 768 769package OpenBSD::PackingElement::hardlink; 770our @ISA=qw(OpenBSD::PackingElement::Annotation); 771 772__PACKAGE__->register_with_factory('link'); 773 774sub add($class, $plist, $args) 775{ 776 $plist->{state}->{lastfile}->make_hardlink($args); 777 return; 778} 779 780package OpenBSD::PackingElement::temp; 781our @ISA=qw(OpenBSD::PackingElement::Annotation); 782 783__PACKAGE__->register_with_factory('temp'); 784 785sub add($class, $plist, $args) 786{ 787 $plist->{state}->{lastfile}->set_tempname($args); 788 return; 789} 790 791package OpenBSD::PackingElement::size; 792our @ISA=qw(OpenBSD::PackingElement::Annotation); 793 794__PACKAGE__->register_with_factory('size'); 795 796sub add($class, $plist, $args) 797{ 798 $plist->{state}->{lastfile}->add_size($args); 799 return; 800} 801 802package OpenBSD::PackingElement::ts; 803our @ISA=qw(OpenBSD::PackingElement::Annotation); 804 805__PACKAGE__->register_with_factory('ts'); 806 807sub add($class, $plist, $args) 808{ 809 $plist->{state}->{lastfile}->add_timestamp($args); 810 return; 811} 812 813package OpenBSD::PackingElement::Option; 814our @ISA=qw(OpenBSD::PackingElement::Meta); 815 816sub keyword($) { 'option' } 817__PACKAGE__->register_with_factory; 818 819sub new($class, $args) 820{ 821 if ($args eq 'no-default-conflict') { 822 return OpenBSD::PackingElement::NoDefaultConflict->new; 823 } elsif ($args eq 'manual-installation') { 824 return OpenBSD::PackingElement::ManualInstallation->new; 825 } elsif ($args eq 'firmware') { 826 return OpenBSD::PackingElement::Firmware->new; 827 } elsif ($args =~ m/always-update\s+(\S+)/) { 828 return OpenBSD::PackingElement::AlwaysUpdate->new_with_hash($1); 829 } elsif ($args eq 'always-update') { 830 return OpenBSD::PackingElement::AlwaysUpdate->new; 831 } elsif ($args eq 'updatedb') { 832 return OpenBSD::PackingElement::UpdateDB->new; 833 } elsif ($args eq 'is-branch') { 834 return OpenBSD::PackingElement::IsBranch->new; 835 } else { 836 die "Unknown option: $args"; 837 } 838} 839 840package OpenBSD::PackingElement::UniqueOption; 841our @ISA=qw(OpenBSD::PackingElement::Unique OpenBSD::PackingElement::Option); 842 843sub stringize($self) 844{ 845 return $self->category; 846} 847 848sub new($class, @) 849{ 850 bless {}, $class; 851} 852 853package OpenBSD::PackingElement::NoDefaultConflict; 854our @ISA=qw(OpenBSD::PackingElement::UniqueOption); 855 856sub category($) { 'no-default-conflict' } 857 858package OpenBSD::PackingElement::ManualInstallation; 859our @ISA=qw(OpenBSD::PackingElement::UniqueOption); 860 861sub category($) { 'manual-installation' } 862 863# don't incorporate this in signatures for obvious reasons 864sub write_no_sig($, $) 865{ 866} 867 868package OpenBSD::PackingElement::Firmware; 869our @ISA=qw(OpenBSD::PackingElement::ManualInstallation); 870sub category($) { 'firmware' } 871 872package OpenBSD::PackingElement::UpdateDB; 873our @ISA=qw(OpenBSD::PackingElement::UniqueOption); 874sub category($) 875{ 876 'updatedb'; 877} 878 879package OpenBSD::PackingElement::AlwaysUpdate; 880our @ISA=qw(OpenBSD::PackingElement::UniqueOption); 881 882sub category($) 883{ 884 'always-update'; 885} 886 887sub stringize($self) 888{ 889 my @l = ($self->category); 890 if (defined $self->{hash}) { 891 push(@l, $self->{hash}); 892 } 893 return join(' ', @l); 894} 895 896sub hash_plist($self, $plist) 897{ 898 delete $self->{hash}; 899 my $content; 900 open my $fh, '>', \$content; 901 $plist->write_without_variation($fh); 902 close $fh; 903 my $digest = Digest::SHA::sha256_base64($content); 904 $self->{hash} = $digest; 905} 906 907sub new_with_hash($class, $hash) 908{ 909 bless { hash => $hash}, $class; 910} 911 912package OpenBSD::PackingElement::IsBranch; 913our @ISA=qw(OpenBSD::PackingElement::UniqueOption); 914 915sub category($) 916{ 917 'is-branch'; 918} 919# The special elements that don't end in the right place 920package OpenBSD::PackingElement::ExtraInfo; 921our @ISA=qw(OpenBSD::PackingElement::Unique OpenBSD::PackingElement::Comment); 922 923sub category($) { 'extrainfo' } 924 925# TODO gc cdromn 926sub new($class, $subdir, $cdrom, $ftp) 927{ 928 $ftp =~ s/^\"(.*)\"$/$1/; 929 $ftp =~ s/^\'(.*)\'$/$1/; 930 my $o = bless { subdir => $subdir, 931 path => OpenBSD::PkgPath->new($subdir), 932 ftp => $ftp}, $class; 933 if (defined $cdrom) { 934 $cdrom =~ s/^\"(.*)\"$/$1/; 935 $cdrom =~ s/^\'(.*)\'$/$1/; 936 $o->{cdrom} = $cdrom; 937 } 938 return $o; 939} 940 941 942sub subdir($self) 943{ 944 return $self->{subdir}; 945} 946 947sub _may_quote($s) 948{ 949 if ($s =~ m/\s/) { 950 return '"'.$s.'"'; 951 } else { 952 return $s; 953 } 954} 955 956sub stringize($self) 957{ 958 my @l = ( 959 "pkgpath=".$self->{subdir}); 960 if (defined $self->{cdrom}) { 961 push @l, "cdrom="._may_quote($self->{cdrom}); 962 } 963 push(@l, "ftp="._may_quote($self->{ftp})); 964 return join(' ', @l); 965} 966 967package OpenBSD::PackingElement::Name; 968use File::Spec; 969our @ISA=qw(OpenBSD::PackingElement::Unique); 970 971sub keyword($) { "name" } 972__PACKAGE__->register_with_factory; 973sub category($) { "name" } 974 975package OpenBSD::PackingElement::LocalBase; 976our @ISA=qw(OpenBSD::PackingElement::Unique); 977 978sub keyword($) { "localbase" } 979__PACKAGE__->register_with_factory; 980sub category($) { "localbase" } 981 982# meta-info: where the package was downloaded/installed from 983# (TODO not as useful as could be, because the workflow isn't effective!) 984package OpenBSD::PackingElement::Url; 985our @ISA=qw(OpenBSD::PackingElement::Unique); 986 987sub keyword($) { "url" } 988__PACKAGE__->register_with_factory; 989sub category($) { "url" } 990 991# don't incorporate this in signatures for obvious reasons 992sub write_no_sig($, $) 993{ 994} 995 996package OpenBSD::PackingElement::Version; 997our @ISA=qw(OpenBSD::PackingElement::Unique OpenBSD::PackingElement::VersionElement); 998 999sub keyword($) { "version" } 1000__PACKAGE__->register_with_factory; 1001sub category($) { "version" } 1002 1003package OpenBSD::PackingElement::Conflict; 1004our @ISA=qw(OpenBSD::PackingElement::Meta); 1005 1006sub keyword($) { "conflict" } 1007__PACKAGE__->register_with_factory; 1008sub category($) { "conflict" } 1009 1010sub spec($self) 1011{ 1012 require OpenBSD::Search; 1013 return OpenBSD::Search::PkgSpec->new($self->name); 1014} 1015 1016package OpenBSD::PackingElement::Dependency; 1017our @ISA=qw(OpenBSD::PackingElement::Depend); 1018use OpenBSD::Error; 1019 1020sub keyword($) { "depend" } 1021__PACKAGE__->register_with_factory; 1022sub category($) { "depend" } 1023 1024sub new($class, $args) 1025{ 1026 my ($pkgpath, $pattern, $def) = split /\:/o, $args; 1027 bless { name => $def, pkgpath => $pkgpath, pattern => $pattern, 1028 def => $def }, $class; 1029} 1030 1031sub stringize($self) 1032{ 1033 return join(':', map { $self->{$_}} 1034 (qw(pkgpath pattern def))); 1035} 1036 1037OpenBSD::Auto::cache(spec, 1038 sub($self) { 1039 require OpenBSD::Search; 1040 1041 my $src; 1042 if ($self->{pattern} eq '=') { 1043 $src = $self->{def}; 1044 } else { 1045 $src = $self->{pattern}; 1046 } 1047 return OpenBSD::Search::PkgSpec->new($src) 1048 ->add_pkgpath_hint($self->{pkgpath}); 1049 }); 1050 1051package OpenBSD::PackingElement::Wantlib; 1052our @ISA=qw(OpenBSD::PackingElement::Depend); 1053 1054sub category($) { "wantlib" } 1055sub keyword($) { "wantlib" } 1056__PACKAGE__->register_with_factory; 1057 1058OpenBSD::Auto::cache(spec, 1059 sub($self) { 1060 require OpenBSD::LibSpec; 1061 return OpenBSD::LibSpec->from_string($self->name); 1062 }); 1063 1064package OpenBSD::PackingElement::Libset; 1065our @ISA=qw(OpenBSD::PackingElement::Meta); 1066 1067sub category($) { "libset" } 1068sub keyword($) { "libset" } 1069__PACKAGE__->register_with_factory; 1070 1071sub new($class, $args) 1072{ 1073 if ($args =~ m/(.*)\:(.*)/) { 1074 return bless {name => $1, libs => [split(/\,/, $2)]}, $class; 1075 } else { 1076 die "Bad args for libset: $args"; 1077 } 1078} 1079 1080sub stringize($self) 1081{ 1082 return $self->{name}.':'.join(',', @{$self->{libs}}); 1083} 1084 1085package OpenBSD::PackingElement::PkgPath; 1086our @ISA=qw(OpenBSD::PackingElement::Meta); 1087 1088sub keyword($) { "pkgpath" } 1089__PACKAGE__->register_with_factory; 1090sub category($) { "pkgpath" } 1091 1092sub new($class, $fullpkgpath) 1093{ 1094 bless {name => $fullpkgpath, 1095 path => OpenBSD::PkgPath::WithOpts->new($fullpkgpath)}, $class; 1096} 1097 1098sub subdir($self) 1099{ 1100 return $self->{name}; 1101} 1102 1103package OpenBSD::PackingElement::AskUpdate; 1104our @ISA=qw(OpenBSD::PackingElement::Meta); 1105 1106sub new($class, $args) 1107{ 1108 my ($pattern, $message) = split /\s+/o, $args, 2; 1109 bless { pattern => $pattern, message => $message}, $class; 1110} 1111 1112sub stringize($self) 1113{ 1114 return join(' ', map { $self->{$_}} 1115 (qw(pattern message))); 1116} 1117 1118sub keyword($) { "ask-update" } 1119__PACKAGE__->register_with_factory; 1120sub category($) { "ask-update" } 1121 1122OpenBSD::Auto::cache(spec, 1123 sub($self) { 1124 require OpenBSD::PkgSpec; 1125 1126 return OpenBSD::PkgSpec->new($self->{pattern}) 1127 }); 1128 1129package OpenBSD::PackingElement::NewAuth; 1130our @ISA=qw(OpenBSD::PackingElement::Action); 1131 1132package OpenBSD::PackingElement::NewUser; 1133our @ISA=qw(OpenBSD::PackingElement::NewAuth); 1134 1135sub type($) { "user" } 1136sub category($) { "users" } 1137sub keyword($) { "newuser" } 1138__PACKAGE__->register_with_factory; 1139 1140sub new($class, $args) 1141{ 1142 my ($name, $uid, $group, $loginclass, $comment, $home, $shell) = 1143 split /\:/o, $args; 1144 bless { name => $name, uid => $uid, group => $group, 1145 class => $loginclass, 1146 comment => $comment, home => $home, shell => $shell }, $class; 1147} 1148 1149sub destate($self, $state) 1150{ 1151 my $uid = $self->{uid}; 1152 $uid =~ s/^\!//; 1153 $state->{owners}{$self->{name}} = $uid; 1154} 1155 1156# $self->check: 1157# 3 possible results 1158# - undef: nothing to check, user/group was not there 1159# - 0: does not match 1160# - 1: exists and matches 1161sub check($self) 1162{ 1163 my ($name, $passwd, $uid, $gid, $quota, $class, $gcos, $dir, $shell, 1164 $expire) = getpwnam($self->name); 1165 return undef unless defined $name; 1166 if ($self->{uid} =~ m/^\!(.*)$/o) { 1167 return 0 unless $uid == $1; 1168 } 1169 if ($self->{group} =~ m/^\!(.*)$/o) { 1170 my $g = $1; 1171 unless ($g =~ m/^\d+$/o) { 1172 $g = getgrnam($g); 1173 return 0 unless defined $g; 1174 } 1175 return 0 unless $gid eq $g; 1176 } 1177 if ($self->{class} =~ m/^\!(.*)$/o) { 1178 return 0 unless $class eq $1; 1179 } 1180 if ($self->{comment} =~ m/^\!(.*)$/o) { 1181 return 0 unless $gcos eq $1; 1182 } 1183 if ($self->{home} =~ m/^\!(.*)$/o) { 1184 return 0 unless $dir eq $1; 1185 } 1186 if ($self->{shell} =~ m/^\!(.*)$/o) { 1187 return 0 unless $shell eq $1; 1188 } 1189 return 1; 1190} 1191 1192sub stringize($self) 1193{ 1194 return join(':', map { $self->{$_}} 1195 (qw(name uid group class comment home shell))); 1196} 1197 1198package OpenBSD::PackingElement::NewGroup; 1199our @ISA=qw(OpenBSD::PackingElement::NewAuth); 1200 1201 1202sub type($) { "group" } 1203sub category($) { "groups" } 1204sub keyword($) { "newgroup" } 1205__PACKAGE__->register_with_factory; 1206 1207sub new($class, $args) 1208{ 1209 my ($name, $gid) = split /\:/o, $args; 1210 bless { name => $name, gid => $gid }, $class; 1211} 1212 1213sub destate($self, $state) 1214{ 1215 my $gid = $self->{gid}; 1216 $gid =~ s/^\!//; 1217 $state->{groups}{$self->{name}} = $gid; 1218} 1219 1220sub check($self) 1221{ 1222 my ($name, $passwd, $gid, $members) = getgrnam($self->name); 1223 return undef unless defined $name; 1224 if ($self->{gid} =~ m/^\!(.*)$/o) { 1225 return 0 unless $gid == $1; 1226 } 1227 return 1; 1228} 1229 1230sub stringize($self) 1231{ 1232 return join(':', map { $self->{$_}} 1233 (qw(name gid))); 1234} 1235 1236package OpenBSD::PackingElement::Cwd; 1237use File::Spec; 1238our @ISA=qw(OpenBSD::PackingElement::State); 1239 1240 1241sub keyword($) { 'cwd' } 1242__PACKAGE__->register_with_factory; 1243 1244sub destate($self, $state) 1245{ 1246 $state->set_cwd($self->name); 1247} 1248 1249package OpenBSD::PackingElement::Owner; 1250our @ISA=qw(OpenBSD::PackingElement::State); 1251 1252sub keyword($) { 'owner' } 1253__PACKAGE__->register_with_factory; 1254 1255sub destate($self, $state) 1256{ 1257 delete $state->{uid}; 1258 if ($self->name eq '') { 1259 undef $state->{owner}; 1260 } else { 1261 $state->{owner} = $self->name; 1262 if (defined $state->{owners}{$self->name}) { 1263 $state->{uid} = $state->{owners}{$self->name}; 1264 } 1265 } 1266} 1267 1268package OpenBSD::PackingElement::Group; 1269our @ISA=qw(OpenBSD::PackingElement::State); 1270 1271sub keyword($) { 'group' } 1272__PACKAGE__->register_with_factory; 1273 1274sub destate($self, $state) 1275{ 1276 delete $state->{gid}; 1277 if ($self->name eq '') { 1278 undef $state->{group}; 1279 } else { 1280 $state->{group} = $self->name; 1281 if (defined $state->{groups}{$self->name}) { 1282 $state->{gid} = $state->{groups}{$self->name}; 1283 } 1284 } 1285} 1286 1287package OpenBSD::PackingElement::Mode; 1288our @ISA=qw(OpenBSD::PackingElement::State); 1289 1290sub keyword($) { 'mode' } 1291__PACKAGE__->register_with_factory; 1292 1293sub destate($self, $state) 1294{ 1295 if ($self->name eq '') { 1296 undef $state->{mode}; 1297 } else { 1298 $state->{mode} = $self->name; 1299 } 1300} 1301 1302package OpenBSD::PackingElement::ExeclikeAction; 1303use File::Basename; 1304use OpenBSD::Error; 1305our @ISA=qw(OpenBSD::PackingElement::Action); 1306 1307sub command($self) 1308{ 1309 return $self->name; 1310} 1311 1312sub expand($self, $state) 1313{ 1314 my $e = $self->command; 1315 if ($e =~ m/\%F/o) { 1316 die "Bad expand" unless defined $state->{lastfile}; 1317 $e =~ s/\%F/$state->{lastfile}->{name}/g; 1318 } 1319 if ($e =~ m/\%D/o) { 1320 die "Bad expand" unless defined $state->{cwd}; 1321 $e =~ s/\%D/$state->cwd/ge; 1322 } 1323 if ($e =~ m/\%B/o) { 1324 die "Bad expand" unless defined $state->{lastfile}; 1325 $e =~ s/\%B/dirname($state->{lastfile}->fullname)/ge; 1326 } 1327 if ($e =~ m/\%f/o) { 1328 die "Bad expand" unless defined $state->{lastfile}; 1329 $e =~ s/\%f/basename($state->{lastfile}->fullname)/ge; 1330 } 1331 return $e; 1332} 1333 1334sub destate($self, $state) 1335{ 1336 $self->{expanded} = $self->expand($state); 1337} 1338 1339sub run($self, $state, $v = $self->{expanded}) 1340{ 1341 $state->ldconfig->ensure; 1342 $state->say("#1 #2", $self->keyword, $v) if $state->verbose >= 2; 1343 $state->log->system(OpenBSD::Paths->sh, '-c', $v) unless $state->{not}; 1344} 1345 1346# so tags are going to get triggered by packages we depend on. 1347# turns out it's simpler to have them as "actions" because that's basically 1348# what's going to happen, so destate is good for them, gives us access 1349# to things like %D 1350package OpenBSD::PackingElement::TagBase; 1351our @ISA=qw(OpenBSD::PackingElement::ExeclikeAction); 1352 1353sub command($self) 1354{ 1355 return $self->{params}; 1356} 1357 1358package OpenBSD::PackingElement::Tag; 1359our @ISA=qw(OpenBSD::PackingElement::TagBase); 1360sub keyword($) { 'tag' } 1361 1362__PACKAGE__->register_with_factory; 1363 1364sub new($class, $args) 1365{ 1366 my ($tag, $params) = split(/\s+/, $args, 2); 1367 bless { 1368 name => $tag, 1369 params => $params // '', 1370 }, $class; 1371} 1372 1373sub stringize($self) 1374{ 1375 if ($self->{params} ne '') { 1376 return join(' ', $self->name, $self->{params}); 1377 } else { 1378 return $self->name; 1379 } 1380} 1381 1382# tags are a kind of dependency, we have a special list for them, BUT 1383# they're still part of the normal packing-list 1384sub add_object($self, $plist) 1385{ 1386 push(@{$plist->{tags}}, $self); 1387 $self->SUPER::add_object($plist); 1388} 1389 1390# and the define tag thingy is very similar... the main difference being 1391# how it's actually registered 1392package OpenBSD::PackingElement::DefineTag; 1393our @ISA=qw(OpenBSD::PackingElement::TagBase); 1394 1395sub category($) {'define-tag'} 1396sub keyword($) { 'define-tag' } 1397__PACKAGE__->register_with_factory; 1398 1399# define-tag may be parsed several times, but these objects must be 1400# unique for tag accumulation to work correctly 1401my $cache = {}; 1402 1403my $subclass = { 1404 'at-end' => 'Atend', 1405 'supersedes' => 'Supersedes', 1406 'cleanup' => 'Cleanup' }; 1407 1408sub new($class, $args) 1409{ 1410 my ($tag, $mode, $params) = split(/\s+/, $args, 3); 1411 $cache->{$args} //= bless { 1412 name => $tag, 1413 mode => $mode, 1414 params => $params, 1415 }, $class; 1416} 1417 1418sub stringize($self) 1419{ 1420 return join(' ', $self->name, $self->{mode}, $self->{params}); 1421} 1422 1423sub add_object($self, $plist) 1424{ 1425 my $sub = $subclass->{$self->{mode}}; 1426 if (!defined $sub) { 1427 die "unknown mode for \@define-tag"; 1428 } 1429 bless $self, "OpenBSD::PackingElement::DefineTag::$sub"; 1430 push(@{$plist->{tags_definitions}{$self->name}}, $self); 1431 $self->SUPER::add_object($plist); 1432} 1433 1434sub destate($, $) 1435{ 1436} 1437 1438package OpenBSD::PackingElement::DefineTag::Atend; 1439our @ISA = qw(OpenBSD::PackingElement::DefineTag); 1440 1441sub add_tag($self, $tag, $mode, $state) 1442{ 1443 # add the tag contents if they exist 1444 # they're stored in a hash because the order doesn't matter 1445 if ($tag->{params} ne '') { 1446 $self->{list}{$tag->{expanded}} = 1; 1447 } 1448 # special case: we have to run things *now* if deleting 1449 if ($mode eq 'delete' && $tag->{found_in_self} && !$state->replacing) { 1450 1451 $self->run_tag($state) 1452 unless $state->{tags}{superseded}{$self->name}; 1453 delete $state->{tags}{atend}{$self->name}; 1454 } else { 1455 $state->{tags}{atend}{$self->name} = $self; 1456 } 1457} 1458 1459sub run_tag($self, $state) 1460{ 1461 my $command = $self->command; 1462 if ($command =~ m/\%D/) { 1463 $command =~ s/\%D/$state->{localbase}/g; 1464 } 1465 1466 if ($command =~ m/\%l/) { 1467 my $l = join(' ', keys %{$self->{list}}); 1468 $command =~ s/\%l/$l/g; 1469 } 1470 if ($command =~ m/\%u/) { 1471 for my $p (keys %{$self->{list}}) { 1472 my $v = $command; 1473 $v =~ s/\%u/$p/g; 1474 $self->run($state, $v); 1475 $state->say("Running #1", $v) 1476 if $state->defines("TRACE_TAGS"); 1477 } 1478 } else { 1479 $self->run($state, $command); 1480 $state->say("Running #1", $command) 1481 if $state->defines("TRACE_TAGS"); 1482 } 1483} 1484 1485sub need_params($self) 1486{ 1487 return $self->{params} =~ m/\%[lu]/; 1488} 1489 1490package OpenBSD::PackingElement::DefineTag::Cleanup; 1491our @ISA = qw(OpenBSD::PackingElement::DefineTag); 1492 1493sub add_tag($self, $tag, $mode, $state) 1494{ 1495 # okay, we don't need to look at directories if we're not deleting 1496 return unless $mode eq 'delete'; 1497 # this does not work at all like 'at-end' 1498 # instead we record a hash of directories we may want to cleanup 1499 push(@{$state->{tag_cleanup}{$tag->{expanded}}}, $self); 1500} 1501 1502sub need_params($) 1503{ 1504 1 1505} 1506 1507package OpenBSD::PackingElement::DefineTag::Supersedes; 1508our @ISA = qw(OpenBSD::PackingElement::DefineTag); 1509 1510sub add_tag($self, $tag, $, $state) 1511{ 1512 $state->{tags}{superseded}{$self->{params}} = 1; 1513} 1514 1515sub need_params($) 1516{ 1517 0 1518} 1519 1520package OpenBSD::PackingElement::Exec; 1521our @ISA=qw(OpenBSD::PackingElement::ExeclikeAction); 1522 1523sub keyword($) { "exec" } 1524__PACKAGE__->register_with_factory; 1525 1526package OpenBSD::PackingElement::ExecAlways; 1527our @ISA=qw(OpenBSD::PackingElement::Exec); 1528 1529sub keyword($) { "exec-always" } 1530__PACKAGE__->register_with_factory; 1531 1532package OpenBSD::PackingElement::ExecAdd; 1533our @ISA=qw(OpenBSD::PackingElement::Exec); 1534 1535sub keyword($) { "exec-add" } 1536__PACKAGE__->register_with_factory; 1537 1538package OpenBSD::PackingElement::ExecUpdate; 1539our @ISA=qw(OpenBSD::PackingElement::Exec); 1540 1541sub keyword($) { "exec-update" } 1542__PACKAGE__->register_with_factory; 1543 1544package OpenBSD::PackingElement::Unexec; 1545our @ISA=qw(OpenBSD::PackingElement::ExeclikeAction); 1546 1547sub keyword($) { "unexec" } 1548__PACKAGE__->register_with_factory; 1549 1550package OpenBSD::PackingElement::UnexecAlways; 1551our @ISA=qw(OpenBSD::PackingElement::Unexec); 1552 1553sub keyword($) { "unexec-always" } 1554__PACKAGE__->register_with_factory; 1555 1556package OpenBSD::PackingElement::UnexecUpdate; 1557our @ISA=qw(OpenBSD::PackingElement::Unexec); 1558 1559sub keyword($) { "unexec-update" } 1560__PACKAGE__->register_with_factory; 1561 1562package OpenBSD::PackingElement::UnexecDelete; 1563our @ISA=qw(OpenBSD::PackingElement::Unexec); 1564 1565sub keyword($) { "unexec-delete" } 1566__PACKAGE__->register_with_factory; 1567 1568package OpenBSD::PackingElement::ExtraUnexec; 1569our @ISA=qw(OpenBSD::PackingElement::ExeclikeAction); 1570 1571sub keyword($) { "extraunexec" } 1572__PACKAGE__->register_with_factory; 1573 1574package OpenBSD::PackingElement::DirlikeObject; 1575our @ISA=qw(OpenBSD::PackingElement::FileObject); 1576 1577# XXX mix-in class, see comment at top of file 1578package OpenBSD::PackingElement::DirBase; 1579our @ISA=qw(OpenBSD::PackingElement::DirlikeObject); 1580 1581sub destate($self, $state) 1582{ 1583 $state->{lastdir} = $self; 1584 $self->SUPER::destate($state); 1585} 1586 1587 1588sub stringize($self) 1589{ 1590 return $self->name."/"; 1591} 1592 1593sub write($self, $fh) 1594{ 1595 $self->SUPER::write($fh); 1596} 1597 1598package OpenBSD::PackingElement::Dir; 1599our @ISA=qw(OpenBSD::PackingElement::DirBase); 1600 1601sub keyword($) { "dir" } 1602__PACKAGE__->register_with_factory; 1603 1604sub destate($self, $state) 1605{ 1606 $self->SUPER::destate($state); 1607 $self->compute_modes($state); 1608} 1609 1610sub needs_keyword($self) 1611{ 1612 return $self->stringize =~ m/\^@/o; 1613} 1614 1615package OpenBSD::PackingElement::Infodir; 1616our @ISA=qw(OpenBSD::PackingElement::Dir); 1617sub keyword($) { "info" } 1618sub needs_keyword($) { 1 } 1619 1620package OpenBSD::PackingElement::Fontdir; 1621our @ISA=qw(OpenBSD::PackingElement::Dir); 1622sub keyword($) { "fontdir" } 1623__PACKAGE__->register_with_factory; 1624sub needs_keyword($) { 1 } 1625sub dirclass($) { "OpenBSD::PackingElement::Fontdir" } 1626 1627sub install($self, $state) 1628{ 1629 $self->SUPER::install($state); 1630 $state->log("You may wish to update your font path for #1", $self->fullname) 1631 unless $self->fullname =~ /^\/usr\/local\/share\/fonts/; 1632 $state->{recorder}{fonts_todo}{$state->{destdir}.$self->fullname} = 1; 1633} 1634 1635sub reload($self, $state) 1636{ 1637 $state->{recorder}{fonts_todo}{$state->{destdir}.$self->fullname} = 1; 1638} 1639 1640sub _update_fontalias($state, $dirname) 1641{ 1642 my $alias_name = "$dirname/fonts.alias"; 1643 if ($state->verbose > 1) { 1644 $state->say("Assembling #1 from #2", 1645 $alias_name, "$alias_name-*"); 1646 } 1647 1648 if (open my $out, '>', $alias_name) { 1649 for my $alias (glob "$alias_name-*") { 1650 if (open my $f ,'<', $alias) { 1651 print {$out} <$f>; 1652 close $f; 1653 } else { 1654 $state->errsay("Couldn't read #1: #2", 1655 $alias, $!); 1656 } 1657 } 1658 close $out; 1659 } else { 1660 $state->errsay("Couldn't write #1: #2", $alias_name, $!); 1661 } 1662} 1663 1664sub _restore_fontdir($state, $dirname) 1665{ 1666 if (-f "$dirname/fonts.dir.dist") { 1667 1668 unlink("$dirname/fonts.dir"); 1669 $state->copy_file("$dirname/fonts.dir.dist", 1670 "$dirname/fonts.dir"); 1671 } 1672} 1673 1674sub _run_if_exists($state, $cmd, @l) 1675{ 1676 if (-x $cmd) { 1677 $state->vsystem($cmd, @l); 1678 } else { 1679 $state->errsay("#1 not found", $cmd); 1680 } 1681} 1682 1683sub finish($class, $state) 1684{ 1685 return if $state->{not}; 1686 1687 my @l = keys %{$state->{recorder}->{fonts_todo}}; 1688 @l = grep {-d $_} @l; 1689 1690 if (@l != 0) { 1691 $state->print("Updating font cache: ") if $state->verbose < 2; 1692 require OpenBSD::Error; 1693 1694 map { _update_fontalias($state, $_) } @l; 1695 _run_if_exists($state, OpenBSD::Paths->mkfontscale, '--', @l); 1696 _run_if_exists($state, OpenBSD::Paths->mkfontdir, '--', @l); 1697 map { _restore_fontdir($state, $_) } @l; 1698 1699 _run_if_exists($state, OpenBSD::Paths->fc_cache, '--', @l); 1700 $state->say("ok") if $state->verbose < 2; 1701 } 1702} 1703 1704 1705package OpenBSD::PackingElement::Mandir; 1706our @ISA=qw(OpenBSD::PackingElement::Dir); 1707 1708sub keyword($) { "mandir" } 1709__PACKAGE__->register_with_factory; 1710sub needs_keyword($) { 1 } 1711sub dirclass($) { "OpenBSD::PackingElement::Mandir" } 1712 1713package OpenBSD::PackingElement::Extra; 1714our @ISA=qw(OpenBSD::PackingElement::FileObject); 1715 1716sub keyword($) { 'extra' } 1717sub absolute_okay($) { 1 } 1718__PACKAGE__->register_with_factory; 1719 1720sub destate($self, $state) 1721{ 1722 $self->compute_fullname($state); 1723} 1724 1725sub dirclass($) { "OpenBSD::PackingElement::Extradir" } 1726 1727package OpenBSD::PackingElement::Extradir; 1728our @ISA=qw(OpenBSD::PackingElement::DirBase OpenBSD::PackingElement::Extra); 1729sub absolute_okay($) { 1 } 1730 1731sub destate # forwarder 1732{ 1733 &OpenBSD::PackingElement::Extra::destate; 1734} 1735 1736package OpenBSD::PackingElement::ExtraGlob; 1737our @ISA=qw(OpenBSD::PackingElement::FileObject); 1738 1739sub keyword($) { 'extraglob' } 1740sub absolute_okay($) { 1 } 1741__PACKAGE__->register_with_factory; 1742 1743package OpenBSD::PackingElement::SpecialFile; 1744our @ISA=qw(OpenBSD::PackingElement::Unique); 1745 1746sub add_digest # forwarder 1747{ 1748 &OpenBSD::PackingElement::FileBase::add_digest; 1749} 1750 1751sub add_size # forwarder 1752{ 1753 &OpenBSD::PackingElement::FileBase::add_size; 1754} 1755 1756sub add_timestamp($, $) 1757{ 1758 # just don't 1759} 1760 1761sub compute_digest # forwarder 1762{ 1763 &OpenBSD::PackingElement::FileObject::compute_digest; 1764} 1765 1766sub write # forwarder 1767{ 1768 &OpenBSD::PackingElement::FileBase::write; 1769} 1770 1771sub needs_keyword($) { 0 } 1772 1773sub add_object($self, $plist) 1774{ 1775 $self->{infodir} = $plist->{infodir}; 1776 $self->SUPER::add_object($plist); 1777} 1778 1779sub infodir($self) 1780{ 1781 return ${$self->{infodir}}; 1782} 1783 1784sub stringize($self) 1785{ 1786 return $self->category; 1787} 1788 1789sub fullname($self) 1790{ 1791 my $d = $self->infodir; 1792 if (defined $d) { 1793 return $d.$self->name; 1794 } else { 1795 return undef; 1796 } 1797} 1798 1799sub category($self) 1800{ 1801 return $self->name; 1802} 1803 1804sub new # forwarder 1805{ 1806 &OpenBSD::PackingElement::UniqueOption::new; 1807} 1808 1809sub may_verify_digest($self, $state) 1810{ 1811 if (!$state->{check_digest}) { 1812 return; 1813 } 1814 if (!defined $self->{d}) { 1815 $state->log->fatal($state->f("#1 does not have a signature", 1816 $self->fullname)); 1817 } 1818 my $d = $self->compute_digest($self->fullname); 1819 if (!$d->equals($self->{d})) { 1820 $state->log->fatal($state->f("checksum for #1 does not match", 1821 $self->fullname)); 1822 } 1823 if ($state->verbose >= 3) { 1824 $state->say("Checksum match for #1", $self->fullname); 1825 } 1826} 1827 1828package OpenBSD::PackingElement::FCONTENTS; 1829our @ISA=qw(OpenBSD::PackingElement::SpecialFile); 1830sub name($) { OpenBSD::PackageInfo::CONTENTS } 1831# XXX we don't write `self' 1832sub write($, $) 1833{} 1834 1835sub copy_shallow_if($, $, $) 1836{ 1837} 1838 1839sub copy_deep_if($, $, $) 1840{ 1841} 1842 1843# CONTENTS doesn't have a checksum 1844sub may_verify_digest($, $) 1845{ 1846} 1847 1848package OpenBSD::PackingElement::FDESC; 1849our @ISA=qw(OpenBSD::PackingElement::SpecialFile); 1850sub name($) { OpenBSD::PackageInfo::DESC } 1851 1852package OpenBSD::PackingElement::DisplayFile; 1853our @ISA=qw(OpenBSD::PackingElement::SpecialFile); 1854use OpenBSD::Error; 1855 1856sub prepare($self, $state) 1857{ 1858 my $fname = $self->fullname; 1859 if (open(my $src, '<', $fname)) { 1860 while (<$src>) { 1861 chomp; 1862 next if m/^\+\-+\s*$/o; 1863 s/^[+-] //o; 1864 $state->log("#1", $_); 1865 } 1866 } else { 1867 $state->errsay("Can't open #1: #2", $fname, $!); 1868 } 1869} 1870 1871package OpenBSD::PackingElement::FDISPLAY; 1872our @ISA=qw(OpenBSD::PackingElement::DisplayFile); 1873sub name($) { OpenBSD::PackageInfo::DISPLAY } 1874 1875package OpenBSD::PackingElement::FUNDISPLAY; 1876our @ISA=qw(OpenBSD::PackingElement::DisplayFile); 1877sub name($) { OpenBSD::PackageInfo::UNDISPLAY } 1878 1879package OpenBSD::PackingElement::Arch; 1880our @ISA=qw(OpenBSD::PackingElement::Unique); 1881 1882sub category($) { 'arch' } 1883sub keyword($) { 'arch' } 1884__PACKAGE__->register_with_factory; 1885 1886sub new($class, $args) 1887{ 1888 my @arches= split(/\,/o, $args); 1889 bless { arches => \@arches }, $class; 1890} 1891 1892sub stringize($self) 1893{ 1894 return join(',', @{$self->{arches}}); 1895} 1896 1897sub check($self, $forced_arch = undef) 1898{ 1899 for my $ok (@{$self->{arches}}) { 1900 return 1 if $ok eq '*'; 1901 if (defined $forced_arch) { 1902 if ($ok eq $forced_arch) { 1903 return 1; 1904 } else { 1905 next; 1906 } 1907 } 1908 return 1 if $ok eq OpenBSD::Paths->machine_architecture; 1909 return 1 if $ok eq OpenBSD::Paths->architecture; 1910 } 1911 return 0; 1912} 1913 1914package OpenBSD::PackingElement::Signer; 1915our @ISA=qw(OpenBSD::PackingElement::Unique); 1916sub keyword($) { 'signer' } 1917__PACKAGE__->register_with_factory; 1918sub category($) { "signer" } 1919sub new($class, $args) 1920{ 1921 unless ($args =~ m/^[\w\d\.\-\+\@]+$/) { 1922 die "Invalid characters in signer $args"; 1923 } 1924 $class->SUPER::new($args); 1925} 1926 1927# don't incorporate this into compared signatures 1928sub write_without_variation($, $) 1929{ 1930} 1931 1932# XXX digital-signatures have to be unique, since they are a part 1933# of the unsigned packing-list, with only the b64sig part removed 1934# (likewise for signer) 1935package OpenBSD::PackingElement::DigitalSignature; 1936our @ISA=qw(OpenBSD::PackingElement::Unique); 1937 1938sub keyword($) { 'digital-signature' } 1939__PACKAGE__->register_with_factory; 1940sub category($) { "digital-signature" } 1941 1942# parse to and from a subset of iso8601 1943# 1944# allows us to represent timestamps in a human readable format without 1945# any ambiguity 1946sub _time_to_iso8601($time) 1947{ 1948 my ($sec, $min, $hour, $day, $month, $year, @rest) = gmtime($time); 1949 return sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", 1950 $year+1900, $month+1, $day, $hour, $min, $sec); 1951} 1952 1953sub iso8601($self) 1954{ 1955 return _time_to_iso8601($self->{timestamp}); 1956} 1957 1958sub _iso8601_to_time($s) 1959{ 1960 # XXX RFC 9557 explicitly wants +00:00 instead of Z for UTC, 1961 # so recognize both 1962 if ($s =~ m/^(\d{4})\-(\d{2})\-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})(?:Z|\+00\:00)$/) { 1963 my ($year, $month, $day, $hour, $min, $sec) = 1964 ($1 - 1900, $2-1, $3, $4, $5, $6); 1965 require POSIX; 1966 local $ENV{TZ} = 'UTC'; 1967 my $t = POSIX::mktime($sec, $min, $hour, $day, $month, $year); 1968 return $t; 1969 } else { 1970 die "Incorrect ISO8601 timestamp: $s"; 1971 } 1972} 1973 1974sub new($class, $args) 1975{ 1976 my ($key, $tsbase, $tsmin, $tssec, $signature) = split(/\:/, $args); 1977 my $timestamp = _iso8601_to_time("$tsbase:$tsmin:$tssec"); 1978 bless { key => $key, timestamp => $timestamp, b64sig => $signature }, 1979 $class; 1980} 1981 1982sub blank($class, $type) 1983{ 1984 bless { key => $type, timestamp => time, b64sig => '' }, $class; 1985} 1986 1987sub stringize($self) 1988{ 1989 return join(':', $self->{key}, _time_to_iso8601($self->{timestamp}), 1990 $self->{b64sig}); 1991} 1992 1993sub write_no_sig($self, $fh) 1994{ 1995 say $fh "\@", $self->keyword, " ", $self->{key}, ":", 1996 _time_to_iso8601($self->{timestamp}); 1997} 1998 1999# don't incorporate this into compared signatures 2000sub write_without_variation($, $) 2001{ 2002} 2003 2004package OpenBSD::PackingElement::Old; 2005our @ISA=qw(OpenBSD::PackingElement); 2006 2007my $warned; 2008 2009sub new($class, $k, $args) 2010{ 2011 bless { keyword => $k, name => $args }, $class; 2012} 2013 2014sub add($o, $plist, $args) 2015{ 2016 my $keyword = $$o; 2017 if (!$warned->{$keyword}) { 2018 say STDERR "Warning: obsolete construct: \@$keyword $args"; 2019 $warned->{$keyword} = 1; 2020 } 2021 my $o2 = OpenBSD::PackingElement::Old->new($keyword, $args); 2022 $o2->add_object($plist); 2023 $plist->{deprecated} = 1; 2024 return undef; 2025} 2026 2027sub keyword($self) 2028{ 2029 return $self->{keyword}; 2030} 2031 2032sub register_old_keyword($class, $k) 2033{ 2034 $class->register_with_factory($k, bless \$k, $class); 2035} 2036 2037for my $k (qw(src display mtree ignore_inst dirrm pkgcfl pkgdep newdepend 2038 libdepend endfake ignore vendor incompatibility md5 sysctl)) { 2039 __PACKAGE__->register_old_keyword($k); 2040} 2041 2042# pkgpath objects are parsed in extrainfo and pkgpath objects 2043# so that erroneous pkgpaths will be flagged early 2044package OpenBSD::PkgPath; 2045sub new($class, $fullpkgpath) 2046{ 2047 my ($dir, @mandatory) = split(/\,/, $fullpkgpath); 2048 my $o = 2049 bless {dir => $dir, 2050 mandatory => {map {($_, 1)} @mandatory}, 2051 }, $class; 2052 my @sub = grep {/^\-/} @mandatory; 2053 if (@sub > 1) { 2054 say STDERR "Invalid $fullpkgpath (multiple subpackages)"; 2055 exit 1; 2056 } 2057 if (@sub == 1) { 2058 $o->{subpackage} = shift @sub; 2059 } 2060 return $o; 2061} 2062 2063sub fullpkgpath($self) 2064{ 2065 if(%{$self->{mandatory}}) { 2066 my $m = join(",", keys %{$self->{mandatory}}); 2067 return "$self->{dir},$m"; 2068 } else { 2069 return $self->{dir}; 2070 } 2071} 2072 2073# a pkgpath has a dir, and some flavors/multi parts. To match, we must 2074# remove them all. So, keep a full hash of everything we have (has), and 2075# when stuff $to_rm matches, remove them from $from. 2076# We match when we're left with nothing. 2077sub trim($self, $has, $from, $to_rm) 2078{ 2079 for my $f (keys %$to_rm) { 2080 if ($has->{$f}) { 2081 delete $from->{$f}; 2082 } else { 2083 return 0; 2084 } 2085 } 2086 return 1; 2087} 2088 2089# basic match: after mandatory, nothing left 2090sub match2($self, $has, $h) 2091{ 2092 if (keys %$h) { 2093 return 0; 2094 } else { 2095 return 1; 2096 } 2097} 2098 2099# zap mandatory, check that what's left is okay. 2100sub match($self, $other) 2101{ 2102 # make a copy of options 2103 my %h = %{$other->{mandatory}}; 2104 if (!$self->trim($other->{mandatory}, \%h, $self->{mandatory})) { 2105 return 0; 2106 } 2107 if ($self->match2($other->{mandatory}, \%h)) { 2108 return 1; 2109 } else { 2110 return 0; 2111 } 2112} 2113 2114package OpenBSD::PkgPath::WithOpts; 2115our @ISA = qw(OpenBSD::PkgPath); 2116 2117sub new($class, $fullpkgpath) 2118{ 2119 my @opts = (); 2120 while ($fullpkgpath =~ s/\[\,(.*?)\]//) { 2121 push(@opts, {map {($_, 1)} split(/\,/, $1) }); 2122 }; 2123 my $o = $class->SUPER::new($fullpkgpath); 2124 if (@opts == 0) { 2125 bless $o, "OpenBSD::PkgPath"; 2126 } else { 2127 $o->{opts} = \@opts; 2128 } 2129 return $o; 2130} 2131 2132# match with options: systematically trim any optional part that fully 2133# matches, until we're left with nothing, or some options keep happening. 2134sub match2($self, $has, $h) 2135{ 2136 if (!keys %$h) { 2137 return 1; 2138 } 2139 for my $opts (@{$self->{opts}}) { 2140 my %h2 = %$h; 2141 if ($self->trim($has, \%h2, $opts)) { 2142 $h = \%h2; 2143 if (!keys %$h) { 2144 return 1; 2145 } 2146 } 2147 } 2148 return 0; 2149} 2150 21511; 2152