1# ex:ts=8 sw=4: 2# $OpenBSD: Add.pm,v 1.196 2023/10/11 13:54:43 espie 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 20package OpenBSD::Add; 21use OpenBSD::Error; 22use OpenBSD::PackageInfo; 23use OpenBSD::ArcCheck; 24use OpenBSD::Paths; 25use File::Copy; 26 27sub manpages_index($state) 28{ 29 return unless defined $state->{addman}; 30 my $destdir = $state->{destdir}; 31 32 # fudge verbose for API differences 33 while (my ($k, $v) = each %{$state->{addman}}) { 34 my @l = map { "$destdir$k/$_" } @$v; 35 if ($state->{not}) { 36 $state->say("Merging manpages in #1: #2", 37 $destdir.$k, join(' ', @l)) if $state->verbose; 38 } else { 39 $state->run_makewhatis(['-d', $destdir.$k], \@l); 40 } 41 } 42 delete $state->{addman}; 43} 44 45sub register_installation($plist, $state) 46{ 47 if ($state->{not}) { 48 $plist->to_cache; 49 } else { 50 my $dest = installed_info($plist->pkgname); 51 mkdir($dest); 52 $plist->copy_info($dest, $state); 53 $plist->set_infodir($dest); 54 $plist->to_installation; 55 } 56} 57 58sub validate_plist($plist, $state, $set) 59{ 60 $plist->prepare_for_addition($state, $plist->pkgname, $set); 61} 62 63sub record_partial_installation($plist, $state, $h) 64{ 65 use OpenBSD::PackingElement; 66 67 my $n = $plist->make_shallow_copy($h); 68 my $borked = borked_package($plist->pkgname); 69 $n->set_pkgname($borked); 70 71 # last file may have not copied correctly 72 my $last = $n->{state}{lastfile}; 73 if (defined $last && defined($last->{d})) { 74 75 my $old = $last->{d}; 76 my $lastname = $last->realname($state); 77 if (-f $lastname) { 78 $last->{d} = $last->compute_digest($lastname, $old); 79 if (!$old->equals($last->{d})) { 80 $state->say("Adjusting #1 for #2 from #3 to #4", 81 $old->keyword, $lastname, $old->stringize, 82 $last->{d}->stringize); 83 } 84 } else { 85 delete $last->{d}; 86 } 87 } 88 register_installation($n, $state); 89 return $borked; 90} 91 92sub perform_installation($handle, $state) 93{ 94 return if $state->{regression}{stub} && $handle->pkgname !~ /^quirks\-/; 95 96 $state->{partial} = $handle->{partial}; 97 $state->progress->visit_with_size($handle->{plist}, 'install'); 98 if ($handle->{location}{early_close}) { 99 $handle->{location}->close_now; 100 } else { 101 $handle->{location}->finish_and_close; 102 } 103} 104 105sub skip_to_the_end($handle, $state, $tied, $p) 106{ 107 $state->tweak_header("skipping"); 108 for my $e (values %$tied) { 109 $e->tie($state); 110 $p->advance($e); 111 } 112 if (keys %$tied > 0) { 113 # skipped entries should still be read in CACHE mode 114 if (defined $state->cache_directory) { 115 while (my $e = $state->{archive}->next) { 116 } 117 } else { 118 $handle->{location}{early_close} = 1; 119 } 120 } 121} 122 123sub perform_extraction($handle, $state) 124{ 125 return if $state->{regression}{stub} && $handle->pkgname !~ /^quirks\-/; 126 127 $handle->{partial} = {}; 128 $state->{partial} = $handle->{partial}; 129 $state->{archive} = $handle->{location}; 130 $state->{check_digest} = $handle->{plist}{check_digest}; 131 132 # archives are actually stored out of order, find_extractible 133 # will dispatch the packing-list entries into hashes keyed by names. 134 # For "tied" entries, also see tie_files in OpenBSD::PkgAdd. 135 my ($wanted, $tied) = ({}, {}); 136 $handle->{plist}->find_extractible($state, $wanted, $tied); 137 my $p = $state->progress->new_sizer($handle->{plist}); 138 139 # so iterate over the archive, and "consume" hashes entry as we go 140 # it's necessary to delete them so that skip_to_the_end will work 141 # correctly (relies on wanted being empty to trigger, and requires 142 # tied to be correct for the progress meter). 143 if (keys %$wanted == 0) { 144 skip_to_the_end($handle, $state, $tied, $p); 145 return; 146 } 147 while (my $file = $state->{archive}->next) { 148 my $e = $tied->{$file->name}; 149 if (defined $e) { 150 delete $tied->{$file->name}; 151 $e->prepare_to_extract($state, $file); 152 $e->tie($state); 153 $state->{archive}->skip; 154 $p->advance($e); 155 # skip to next; 156 next; 157 } 158 $e = $wanted->{$file->name}; 159 if (!defined $e) { 160 $state->fatal("archive member not found #1", 161 $file->name); 162 } 163 delete $wanted->{$file->name}; 164 # note that readmes are only recorded when !tied, since 165 # we only care if they changed 166 my $fullname = $e->fullname; 167 if ($fullname =~ m,^$state->{localbase}/share/doc/pkg-readmes/,) { 168 push(@{$state->{readmes}}, $fullname); 169 } 170 171 $e->prepare_to_extract($state, $file); 172 $e->extract($state, $file); 173 $p->advance($e); 174 if (keys %$wanted == 0) { 175 skip_to_the_end($handle, $state, $tied, $p); 176 last; 177 } 178 } 179 if (keys %$wanted > 0) { 180 $state->fatal("Truncated archive"); 181 } 182} 183 184my $user_tagged = {}; 185 186sub extract_pkgname($pkgname) 187{ 188 $pkgname =~ s/^.*\///; 189 $pkgname =~ s/\.tgz$//; 190 return $pkgname; 191} 192 193sub tweak_package_status($pkgname, $state) 194{ 195 $pkgname = extract_pkgname($pkgname); 196 return 0 unless is_installed($pkgname); 197 return 0 unless $user_tagged->{$pkgname}; 198 return 1 if $state->{not}; 199 my $plist = OpenBSD::PackingList->from_installation($pkgname); 200 if ($plist->has('manual-installation') && $state->{automatic} > 1) { 201 delete $plist->{'manual-installation'}; 202 $plist->to_installation; 203 return 1; 204 } elsif (!$plist->has('manual-installation') && !$state->{automatic}) { 205 OpenBSD::PackingElement::ManualInstallation->add($plist); 206 $plist->to_installation; 207 return 1; 208 } 209 return 0; 210} 211 212sub tweak_plist_status($plist, $state) 213{ 214 my $pkgname = $plist->pkgname; 215 if ($state->defines('FW_UPDATE')) { 216 $plist->has('firmware') or 217 OpenBSD::PackingElement::Firmware->add($plist); 218 } 219 return 0 unless $user_tagged->{$pkgname}; 220 if (!$plist->has('manual-installation') && !$state->{automatic}) { 221 OpenBSD::PackingElement::ManualInstallation->add($plist); 222 } 223} 224 225sub tag_user_packages(@p) 226{ 227 for my $set (@p) { 228 for my $n ($set->newer_names) { 229 $user_tagged->{OpenBSD::PackageName::url2pkgname($n)} = 1; 230 } 231 } 232} 233 234# The whole package addition/replacecement works like this: 235# first we run tie_files in PkgAdd to figure out tieto 236# then "find_extractible" figures out the element of the plist that 237# belong in the archive (thus find_extractible is the hook that always 238# gets run on every plist entry just prior to extraction/skipping) 239# 240# Then the actual extraction proceeds through "prepare_to_extract" and 241# either "tie' OR "extract" depending on the element status. 242# Then later on, we run "install". 243# 244# Actual file system entries may get a tempname, or avoid temp altogether 245# 246# In case of replacement, tempname will get used if the name is the same 247# but the file content is different. 248# 249# If pkg_add can figure out the name is the same, it will set avoidtemp 250# 251# Note that directories, hardlinks and symlinks are purely plist objects 252# with no archive existence: 253# Links always get deleted/re-added even in replacement mode, while directory 254# deletion is delayed into OpenBSD::SharedItems, since several packages 255# may mention the same directory. 256# 257package OpenBSD::PackingElement; 258use OpenBSD::Error; 259 260# used by newuser/newgroup to deal with options. 261my ($uidcache, $gidcache); 262 263# $self->prepare_for_addition($state, $pkgname, $set) 264sub prepare_for_addition($, $, $, $) 265{ 266} 267 268# $self->find_extractible($state, $wanted, $tied): 269# sort item into wanted (needed from archive) / tied (already there) 270sub find_extractible($, $, $, $) 271{ 272} 273 274sub extract($self, $state) 275{ 276 $state->{partial}{$self} = 1; 277 if ($state->{interrupted}) { 278 die "Interrupted"; 279 } 280} 281 282sub install($self, $state) 283{ 284 # XXX "normal" items are already in partial, but NOT stuff 285 # that's install-only, like symlinks and dirs... 286 $state->{partial}{$self} = 1; 287 if ($state->{interrupted}) { 288 die "Interrupted"; 289 } 290} 291 292# $self->copy_info($dest, $state) 293sub copy_info($, $, $) 294{ 295} 296 297sub set_modes($self, $state, $name) 298{ 299 if (defined $self->{owner} || defined $self->{group}) { 300 require OpenBSD::IdCache; 301 302 if (!defined $uidcache) { 303 $uidcache = OpenBSD::UidCache->new; 304 $gidcache = OpenBSD::GidCache->new; 305 } 306 my ($uid, $gid) = (-1, -1); 307 if (defined $self->{owner}) { 308 $uid = $uidcache->lookup($self->{owner}, $uid); 309 } 310 if (defined $self->{group}) { 311 $gid = $gidcache->lookup($self->{group}, $gid); 312 } 313 chown $uid, $gid, $name; 314 } 315 if (defined $self->{mode}) { 316 my $v = $self->{mode}; 317 if ($v =~ m/^\d+$/o) { 318 chmod oct($v), $name; 319 } else { 320 $state->system(OpenBSD::Paths->chmod, 321 $self->{mode}, $name); 322 } 323 } 324 if (defined $self->{ts}) { 325 utime $self->{ts}, $self->{ts}, $name; 326 } 327} 328 329package OpenBSD::PackingElement::Meta; 330 331# XXX stuff that's invisible to find_extractible should be considered extracted 332# for the most part, otherwise we create broken partial packages 333sub find_extractible($self, $state, $wanted, $tied) 334{ 335 $state->{partial}{$self} = 1; 336} 337 338package OpenBSD::PackingElement::Cwd; 339sub find_extractible # forwarder 340{ 341 &OpenBSD::PackingElement::Meta::find_extractible; 342} 343 344package OpenBSD::PackingElement::ExtraInfo; 345use OpenBSD::Error; 346 347sub prepare_for_addition($self, $state, $pkgname, $) 348{ 349 if ($state->{ftp_only} && $self->{ftp} ne 'yes') { 350 $state->errsay("Package #1 is not for ftp", $pkgname); 351 $state->{problems}++; 352 } 353} 354 355package OpenBSD::PackingElement::NewAuth; 356use OpenBSD::Error; 357 358sub add_entry($, $l, @p) 359{ 360 while (@p >= 2) { 361 my $f = shift @p; 362 my $v = shift @p; 363 next if !defined $v or $v eq ''; 364 if ($v =~ m/^\!(.*)$/o) { 365 push(@$l, $f, $1); 366 } else { 367 push(@$l, $f, $v); 368 } 369 } 370} 371 372sub prepare_for_addition($self, $state, $pkgname, $) 373{ 374 my $ok = $self->check; 375 if (defined $ok) { 376 if ($ok == 0) { 377 $state->errsay("#1 #2 does not match", 378 $self->type, $self->name); 379 $state->{problems}++; 380 } 381 } 382 $self->{okay} = $ok; 383} 384 385sub install($self, $state) 386{ 387 $self->SUPER::install($state); 388 my $auth = $self->name; 389 $state->say("adding #1 #2", $self->type, $auth) if $state->verbose >= 2; 390 return if $state->{not}; 391 return if defined $self->{okay}; 392 my $l=[]; 393 push(@$l, "-v") if $state->verbose >= 2; 394 $self->build_args($l); 395 $state->vsystem($self->command,, @$l, '--', $auth); 396} 397 398package OpenBSD::PackingElement::NewUser; 399 400sub command($) { OpenBSD::Paths->useradd } 401 402sub build_args($self, $l) 403{ 404 $self->add_entry($l, 405 '-u', $self->{uid}, 406 '-g', $self->{group}, 407 '-L', $self->{class}, 408 '-c', $self->{comment}, 409 '-d', $self->{home}, 410 '-s', $self->{shell}); 411} 412 413package OpenBSD::PackingElement::NewGroup; 414 415sub command($) { OpenBSD::Paths->groupadd } 416 417sub build_args($self, $l) 418{ 419 $self->add_entry($l, '-g', $self->{gid}); 420} 421 422package OpenBSD::PackingElement::FileBase; 423use OpenBSD::Error; 424use File::Basename; 425use File::Path; 426use OpenBSD::Temp; 427 428sub find_extractible($self, $state, $wanted, $tied) 429{ 430 if ($self->{tieto} || $self->{link} || $self->{symlink}) { 431 $tied->{$self->name} = $self; 432 } else { 433 $wanted->{$self->name} = $self; 434 } 435} 436 437sub prepare_for_addition($self, $state, $pkgname, $) 438{ 439 my $fname = $self->retrieve_fullname($state, $pkgname); 440 # check for collisions with existing stuff 441 if ($state->vstat->exists($fname)) { 442 push(@{$state->{colliding}}, $self); 443 $self->{newly_found} = $pkgname; 444 $state->{problems}++; 445 return; 446 } 447 return if $state->{regression}{stub} && $pkgname !~ /^quirks\-/; 448 my $s = $state->vstat->add($fname, 449 $self->{tieto} ? 0 : $self->retrieve_size, $pkgname); 450 return unless defined $s; 451 if ($s->ro) { 452 $s->report_ro($state, $fname); 453 } 454 if ($s->avail < 0) { 455 $s->report_overflow($state, $fname); 456 } 457} 458 459sub prepare_to_extract($self, $state, $file) 460{ 461 my $fullname = $self->fullname; 462 my $destdir = $state->{destdir}; 463 464 $file->{cwd} = $self->cwd; 465 if (!$file->validate_meta($self)) { 466 $state->fatal("can't continue"); 467 } 468 469 $file->set_name($fullname); 470 $file->{destdir} = $destdir; 471} 472 473sub find_safe_dir($self, $state) 474{ 475 # figure out a safe directory where to put the temp file 476 477 my $fullname = $self->fullname; 478 my $filename = $state->{destdir}.$fullname; 479 my $d = dirname($filename); 480 my $orig = $d; 481 482 # we go back up until we find an existing directory. 483 # hopefully this will be on the same file system. 484 my @candidates = (); 485 while (!-d $d) { 486 push(@candidates, $d); 487 $d = dirname($d); 488 } 489 # and now we try to go back down, creating the best path we can 490 while (@candidates > 0) { 491 my $c = pop @candidates; 492 last if -e $c; # okay this exists, but is not a directory 493 $d = $c; 494 } 495 if (!-e _ && !$state->{not}) { 496 $state->make_path($d, $fullname); 497 } 498 if ($state->{current_set}{simple_update} && 499 $d eq $orig && 500 !-e $filename) { 501 $self->{avoid_temp} = $filename; 502 } 503 504 return $d; 505} 506 507sub create_temp($self, $d, $state) 508{ 509 my $fullname = $self->fullname; 510 my ($fh, $tempname) = OpenBSD::Temp::permanent_file($d, "pkg"); 511 $self->{tempname} = $tempname; 512 if (!defined $tempname) { 513 if ($state->allow_nonroot($fullname)) { 514 $state->errsay("Can't create temp file outside localbase for #1", $fullname); 515 $state->errsay(OpenBSD::Temp->last_error); 516 return undef; 517 } 518 $state->fatal(OpenBSD::Temp->last_error); 519 } 520 return ($fh, $tempname); 521} 522 523sub may_create_temp($self, $d, $state) 524{ 525 if ($self->{avoid_temp}) { 526 if (open(my $fh, '>', $self->{avoid_temp})) { 527 return ($fh, $self->{avoid_temp}); 528 } 529 } 530 delete $self->{avoid_temp}; 531 return $self->create_temp($d, $state); 532} 533 534sub tie($self, $state) 535{ 536 if (defined $self->{link} || defined $self->{symlink}) { 537 return; 538 } 539 540 $self->SUPER::extract($state); 541 542 my $d = $self->find_safe_dir($state); 543 my $src = $self->{tieto}->realname($state); 544 my $dest = $self->realname($state); 545 if ($state->{current_set}{simple_update} && $src eq $dest) { 546 $state->say("No name change on tied file #1", $src) 547 if $state->verbose >= 3; 548 $state->{current_set}{dont_delete}{$dest} = 1; 549 $self->{avoid_temp} = 1; 550 return; 551 } 552 if ($state->{not}) { 553 $state->say("link #1 -> #2", 554 $self->name, $d) if $state->verbose >= 3; 555 } else { 556 my ($fh, $tempname) = $self->may_create_temp($d, $state); 557 558 return if !defined $tempname; 559 unlink($tempname); 560 $state->say("link #1 -> #2", $src, $tempname) 561 if $state->verbose >= 3; 562 link($src, $tempname) || $state->copy_file($src, $tempname); 563 } 564} 565 566 567sub extract($self, $state, $file) 568{ 569 $self->SUPER::extract($state); 570 571 my $d = $self->find_safe_dir($state); 572 if ($state->{not}) { 573 $state->say("extract #1 -> #2", 574 $self->name, $d) if $state->verbose >= 3; 575 $state->{archive}->skip; 576 } else { 577 my ($fh, $filename) = $self->may_create_temp($d, $state); 578 if (!defined $filename) { 579 $state->{archive}->skip; 580 return; 581 } 582 583 if ($self->{avoid_temp}) { 584 $state->{current_set}{dont_delete}{$filename} = 1; 585 } 586 $state->say("extract #1 -> #2", $self->name, $filename) 587 if $state->verbose >= 3; 588 589 590 if (!$file->isFile) { 591 $state->fatal("can't extract #1, it's not a file", 592 $self->stringize); 593 } 594 $file->extract_to_fh($fh); 595 $self->may_check_digest($filename, $state); 596 } 597} 598 599sub install($self, $state) 600{ 601 $self->SUPER::install($state); 602 my $fullname = $self->fullname; 603 my $destdir = $state->{destdir}; 604 if ($state->{not}) { 605 $state->say("moving tempfile -> #1", 606 $destdir.$fullname) if $state->verbose >= 5; 607 return; 608 } 609 $state->make_path(dirname($destdir.$fullname), $fullname); 610 if (defined $self->{link}) { 611 link($destdir.$self->{link}, $destdir.$fullname); 612 $state->say("link #1 -> #2", $destdir.$self->{link}, 613 $destdir.$fullname) if $state->verbose >= 5; 614 } elsif (defined $self->{symlink}) { 615 symlink($self->{symlink}, $destdir.$fullname); 616 $state->say("symlink #1 -> #2", $self->{symlink}, 617 $destdir.$fullname) if $state->verbose >= 5; 618 } else { 619 if (defined $self->{avoid_temp}) { 620 delete $self->{avoid_temp}; 621 } else { 622 if (!defined $self->{tempname}) { 623 return if $state->allow_nonroot($fullname); 624 $state->fatal("No tempname for #1", $fullname); 625 } 626 rename($self->{tempname}, $destdir.$fullname) or 627 $state->fatal("can't move #1 to #2: #3", 628 $self->{tempname}, $fullname, $!); 629 $state->say("moving #1 -> #2", 630 $self->{tempname}, $destdir.$fullname) 631 if $state->verbose >= 5; 632 delete $self->{tempname}; 633 } 634 } 635 $self->set_modes($state, $destdir.$fullname); 636} 637 638package OpenBSD::PackingElement::Extra; 639sub find_extractible($self, $state, $wanted, $tied) 640{ 641 $state->{current_set}{known_extra}{$self->fullname} = 1; 642} 643 644package OpenBSD::PackingElement::RcScript; 645sub install($self, $state) 646{ 647 $state->{add_rcscripts}{$self->fullname} = 1; 648 $self->SUPER::install($state); 649} 650 651package OpenBSD::PackingElement::Sample; 652use OpenBSD::Error; 653use File::Copy; 654 655sub prepare_for_addition($self, $state, $pkgname, $) 656{ 657 if (!defined $self->{copyfrom}) { 658 $state->errsay("\@sample element #1 does not reference a valid file", 659 $self->fullname); 660 $state->{problems}++; 661 } 662 my $fname = $state->{destdir}.$self->fullname; 663 # If file already exists, we won't change it 664 if ($state->vstat->exists($fname)) { 665 return; 666 } 667 my $size = $self->{copyfrom}->{size}; 668 my $s = $state->vstat->add($fname, $size, $pkgname); 669 return unless defined $s; 670 if ($s->ro) { 671 $s->report_ro($state, $fname); 672 } 673 if ($s->avail < 0) { 674 $s->report_overflow($state, $fname); 675 } 676} 677 678sub find_extractible($self, $state, $wanted, $tied) 679{ 680 $state->{current_set}{known_sample}{$self->fullname} = 1; 681} 682 683# $self->extract($state) 684sub extract($, $) 685{ 686} 687 688sub install($self, $state) 689{ 690 $self->SUPER::install($state); 691 my $destdir = $state->{destdir}; 692 my $filename = $destdir.$self->fullname; 693 my $orig = $self->{copyfrom}; 694 my $origname = $destdir.$orig->fullname; 695 if (-e $filename) { 696 if ($state->verbose) { 697 $state->say("The existing file #1 has NOT been changed", 698 $filename); 699 if (defined $orig->{d}) { 700 701 # XXX assume this would be the same type of file 702 my $d = $self->compute_digest($filename, $orig->{d}); 703 if ($d->equals($orig->{d})) { 704 $state->say("(but it seems to match the sample file #1)", $origname); 705 } else { 706 $state->say("It does NOT match the sample file #1", 707 $origname); 708 $state->say("You may wish to update it manually"); 709 } 710 } 711 } 712 } else { 713 if ($state->{not}) { 714 $state->say("The file #1 would be installed from #2", 715 $filename, $origname) if $state->verbose >= 2; 716 } else { 717 if (!copy($origname, $filename)) { 718 $state->errsay("File #1 could not be installed:\n\t#2", $filename, $!); 719 } 720 $self->set_modes($state, $filename); 721 if ($state->verbose >= 2) { 722 $state->say("installed #1 from #2", 723 $filename, $origname); 724 } 725 } 726 } 727} 728 729package OpenBSD::PackingElement::Sampledir; 730sub extract($, $) 731{ 732} 733 734sub install # forwarder 735{ 736 &OpenBSD::PackingElement::Dir::install; 737} 738 739package OpenBSD::PackingElement::Mandir; 740 741sub install($self, $state) 742{ 743 $self->SUPER::install($state); 744 if (!$state->{current_set}{known_mandirs}{$self->fullname}) { 745 $state->log("You may wish to add #1 to /etc/man.conf", 746 $self->fullname); 747 } 748} 749 750package OpenBSD::PackingElement::Manpage; 751 752sub install($self, $state) 753{ 754 $self->SUPER::install($state); 755 $self->register_manpage($state, 'addman'); 756} 757 758package OpenBSD::PackingElement::InfoFile; 759use File::Basename; 760use OpenBSD::Error; 761 762sub install($self, $state) 763{ 764 $self->SUPER::install($state); 765 return if $state->{not}; 766 my $fullname = $state->{destdir}.$self->fullname; 767 $state->vsystem(OpenBSD::Paths->install_info, 768 "--info-dir=".dirname($fullname), '--', $fullname); 769} 770 771package OpenBSD::PackingElement::Shell; 772sub install($self, $state) 773{ 774 $self->SUPER::install($state); 775 return if $state->{not}; 776 my $fullname = $self->fullname; 777 my $destdir = $state->{destdir}; 778 # go append to /etc/shells if needed 779 open(my $shells, '<', $destdir.OpenBSD::Paths->shells) or return; 780 while(<$shells>) { 781 s/^\#.*//o; 782 return if m/^\Q$fullname\E\s*$/; 783 } 784 close($shells); 785 open(my $shells2, '>>', $destdir.OpenBSD::Paths->shells) or return; 786 print $shells2 $fullname, "\n"; 787 close $shells2; 788 $state->say("Shell #1 appended to #2", $fullname, 789 $destdir.OpenBSD::Paths->shells) if $state->verbose; 790} 791 792package OpenBSD::PackingElement::Dir; 793sub extract($self, $state) 794{ 795 my $fullname = $self->fullname; 796 my $destdir = $state->{destdir}; 797 798 return if -e $destdir.$fullname; 799 $self->SUPER::extract($state); 800 $state->say("new directory #1", $destdir.$fullname) 801 if $state->verbose >= 3; 802 return if $state->{not}; 803 $state->make_path($destdir.$fullname, $fullname); 804} 805 806sub install($self, $state) 807{ 808 $self->SUPER::install($state); 809 my $fullname = $self->fullname; 810 my $destdir = $state->{destdir}; 811 812 $state->say("new directory #1", $destdir.$fullname) 813 if $state->verbose >= 5; 814 return if $state->{not}; 815 $state->make_path($destdir.$fullname, $fullname); 816 $self->set_modes($state, $destdir.$fullname); 817} 818 819package OpenBSD::PackingElement::Exec; 820use OpenBSD::Error; 821 822sub install($self, $state) 823{ 824 $self->SUPER::install($state); 825 if ($self->should_run($state)) { 826 $self->run($state); 827 } 828} 829 830sub should_run($, $) { 1 } 831 832package OpenBSD::PackingElement::ExecAdd; 833sub should_run($self, $state) 834{ 835 return !$state->replacing; 836} 837 838package OpenBSD::PackingElement::ExecUpdate; 839sub should_run($self, $state) 840{ 841 return $state->replacing; 842} 843 844package OpenBSD::PackingElement::Tag; 845 846sub install($self, $state) 847{ 848 for my $d (@{$self->{definition_list}}) { 849 $d->add_tag($self, "install", $state); 850 } 851} 852 853package OpenBSD::PackingElement::Lib; 854 855sub install($self, $state) 856{ 857 $self->SUPER::install($state); 858 $self->mark_ldconfig_directory($state); 859} 860 861package OpenBSD::PackingElement::SpecialFile; 862use OpenBSD::PackageInfo; 863use OpenBSD::Error; 864 865sub copy_info($self, $dest, $state) 866{ 867 require File::Copy; 868 869 File::Copy::move($self->fullname, $dest) or 870 $state->errsay("Problem while moving #1 into #2: #3", 871 $self->fullname, $dest, $!); 872} 873 874sub extract($self, $state) 875{ 876 $self->may_verify_digest($state); 877} 878 879sub find_extractible($self, $state, $, $) 880{ 881 $self->may_verify_digest($state); 882} 883 884package OpenBSD::PackingElement::FCONTENTS; 885sub copy_info($, $, $) 886{ 887} 888 889package OpenBSD::PackingElement::AskUpdate; 890sub prepare_for_addition($self, $state, $pkgname, $set) 891{ 892 my @old = $set->older_names; 893 if ($self->spec->match_ref(\@old) > 0) { 894 my $key = "update_".OpenBSD::PackageName::splitstem($pkgname); 895 return if $state->defines($key); 896 if ($state->is_interactive) { 897 if ($state->confirm_defaults_to_no( 898 "#1: #2.\nDo you want to update now", 899 $pkgname, $self->{message})) { 900 return; 901 } 902 } else { 903 $state->errsay("Can't update #1 now: #2", 904 $pkgname, $self->{message}); 905 } 906 $state->{problems}++; 907 } 908} 909 910package OpenBSD::PackingElement::FDISPLAY; 911sub install($self, $state) 912{ 913 my $d = $self->{d}; 914 if (!$state->{current_set}{known_displays}{$self->{d}->key}) { 915 $self->prepare($state); 916 } 917 $self->SUPER::install($state); 918} 919 920package OpenBSD::PackingElement::FUNDISPLAY; 921sub find_extractible($self, $state, $wanted, $tied) 922{ 923 $state->{current_set}{known_displays}{$self->{d}->key} = 1; 924 $self->SUPER::find_extractible($state, $wanted, $tied); 925} 926 9271; 928