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