Skip to content
Permalink
653997b58d
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
889 lines (773 sloc) 19.8 KB
# ex:ts=8 sw=4:
# $OpenBSD: Add.pm,v 1.183 2019/08/19 12:25:40 espie Exp $
#
# Copyright (c) 2003-2014 Marc Espie <espie@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use strict;
use warnings;
package OpenBSD::Add;
use OpenBSD::Error;
use OpenBSD::PackageInfo;
use OpenBSD::ArcCheck;
use OpenBSD::Paths;
use File::Copy;
sub manpages_index
{
my ($state) = @_;
return unless defined $state->{addman};
my $destdir = $state->{destdir};
# fudge verbose for API differences
while (my ($k, $v) = each %{$state->{addman}}) {
my @l = map { "$destdir$k/$_" } @$v;
if ($state->{not}) {
$state->say("Merging manpages in #1: #2",
$destdir.$k, join(' ', @l)) if $state->verbose;
} else {
$state->run_makewhatis(['-d', $destdir.$k], \@l);
}
}
delete $state->{addman};
}
sub register_installation
{
my ($plist, $state) = @_;
if ($state->{not}) {
$plist->to_cache;
} else {
my $dest = installed_info($plist->pkgname);
mkdir($dest);
$plist->copy_info($dest, $state);
$plist->set_infodir($dest);
$plist->to_installation;
}
}
sub validate_plist
{
my ($plist, $state, $set) = @_;
$plist->prepare_for_addition($state, $plist->pkgname, $set);
}
sub record_partial_installation
{
my ($plist, $state, $h) = @_;
use OpenBSD::PackingElement;
my $n = $plist->make_shallow_copy($h);
my $borked = borked_package($plist->pkgname);
$n->set_pkgname($borked);
# last file may have not copied correctly
my $last = $n->{state}->{lastfile};
if (defined $last && defined($last->{d})) {
my $old = $last->{d};
my $lastname = $last->realname($state);
if (-f $lastname) {
$last->{d} = $last->compute_digest($lastname, $old);
if (!$old->equals($last->{d})) {
$state->say("Adjusting #1 for #2 from #3 to #4",
$old->keyword, $lastname, $old->stringize,
$last->{d}->stringize);
}
} else {
delete $last->{d};
}
}
register_installation($n, $state);
return $borked;
}
sub perform_installation
{
my ($handle, $state) = @_;
return if $state->defines('stub');
$state->{partial} = $handle->{partial};
$state->progress->visit_with_size($handle->{plist}, 'install');
if ($handle->{location}{early_close}) {
$handle->{location}->close_now;
} else {
$handle->{location}->finish_and_close;
}
}
sub perform_extraction
{
my ($handle, $state) = @_;
return if $state->defines('stub');
$handle->{partial} = {};
$state->{partial} = $handle->{partial};
$state->{archive} = $handle->{location};
$state->{check_digest} = $handle->{plist}{check_digest};
my ($wanted, $tied) = ({}, {});
$handle->{plist}->find_extractible($state, $wanted, $tied);
my $p = $state->progress->new_sizer($handle->{plist}, $state);
while (my $file = $state->{archive}->next) {
if (keys %$wanted == 0) {
$state->tweak_header("skipping");
for my $e (values %$tied) {
$e->tie($state);
$p->advance($e);
}
if (keys %$tied > 0) {
$handle->{location}{early_close} = 1;
}
last;
}
my $e = $tied->{$file->name};
if (defined $e) {
delete $tied->{$file->name};
$e->prepare_to_extract($state, $file);
$e->tie($state);
$state->{archive}->skip;
$p->advance($e);
# skip to next;
next;
}
$e = $wanted->{$file->name};
if (!defined $e) {
$state->fatal("archive member not found #1",
$file->name);
}
delete $wanted->{$file->name};
my $fullname = $e->fullname;
if ($fullname =~ m,^$state->{localbase}/share/doc/pkg-readmes/,) {
push(@{$state->{readmes}}, $fullname);
}
$e->prepare_to_extract($state, $file);
$e->extract($state, $file);
$p->advance($e);
}
if (keys %$wanted > 0) {
$state->fatal("Truncated archive");
}
$p->saved;
}
my $user_tagged = {};
sub extract_pkgname
{
my $pkgname = shift;
$pkgname =~ s/^.*\///;
$pkgname =~ s/\.tgz$//;
return $pkgname;
}
sub tweak_package_status
{
my ($pkgname, $state) = @_;
$pkgname = extract_pkgname($pkgname);
return 0 unless is_installed($pkgname);
return 0 unless $user_tagged->{$pkgname};
return 1 if $state->{not};
my $plist = OpenBSD::PackingList->from_installation($pkgname);
if ($plist->has('manual-installation') && $state->{automatic} > 1) {
delete $plist->{'manual-installation'};
$plist->to_installation;
return 1;
} elsif (!$plist->has('manual-installation') && !$state->{automatic}) {
OpenBSD::PackingElement::ManualInstallation->add($plist);
$plist->to_installation;
return 1;
}
return 0;
}
sub tweak_plist_status
{
my ($plist, $state) = @_;
my $pkgname = $plist->pkgname;
if ($state->defines('FW_UPDATE')) {
$plist->has('firmware') or
OpenBSD::PackingElement::Firmware->add($plist);
}
return 0 unless $user_tagged->{$pkgname};
if (!$plist->has('manual-installation') && !$state->{automatic}) {
OpenBSD::PackingElement::ManualInstallation->add($plist);
}
}
sub tag_user_packages
{
for my $set (@_) {
for my $n ($set->newer_names) {
$user_tagged->{OpenBSD::PackageName::url2pkgname($n)} = 1;
}
}
}
# used by newuser/newgroup to deal with options.
package OpenBSD::PackingElement;
use OpenBSD::Error;
my ($uidcache, $gidcache);
sub prepare_for_addition
{
}
sub find_extractible
{
}
sub extract
{
my ($self, $state) = @_;
$state->{partial}{$self} = 1;
if ($state->{interrupted}) {
die "Interrupted";
}
}
sub install
{
my ($self, $state) = @_;
# XXX "normal" items are already in partial, but NOT stuff
# that's install-only, like symlinks and dirs...
$state->{partial}{$self} = 1;
if ($state->{interrupted}) {
die "Interrupted";
}
}
sub copy_info
{
}
sub set_modes
{
my ($self, $state, $name) = @_;
if (defined $self->{owner} || defined $self->{group}) {
require OpenBSD::IdCache;
if (!defined $uidcache) {
$uidcache = OpenBSD::UidCache->new;
$gidcache = OpenBSD::GidCache->new;
}
my ($uid, $gid) = (-1, -1);
if (defined $self->{owner}) {
$uid = $uidcache->lookup($self->{owner}, $uid);
}
if (defined $self->{group}) {
$gid = $gidcache->lookup($self->{group}, $gid);
}
chown $uid, $gid, $name;
}
if (defined $self->{mode}) {
my $v = $self->{mode};
if ($v =~ m/^\d+$/o) {
chmod oct($v), $name;
} else {
$state->system(OpenBSD::Paths->chmod,
$self->{mode}, $name);
}
}
if (defined $self->{ts}) {
utime $self->{ts}, $self->{ts}, $name;
}
}
package OpenBSD::PackingElement::Meta;
# XXX stuff that's invisible to find_extractible should be considered extracted
# for the most part, otherwise we create broken partial packages
sub find_extractible
{
my ($self, $state, $wanted, $tied) = @_;
$state->{partial}{$self} = 1;
}
package OpenBSD::PackingElement::ExtraInfo;
use OpenBSD::Error;
sub prepare_for_addition
{
my ($self, $state, $pkgname) = @_;
if ($state->{ftp_only} && $self->{ftp} ne 'yes') {
$state->errsay("Package #1 is not for ftp", $pkgname);
$state->{problems}++;
}
}
package OpenBSD::PackingElement::NewAuth;
use OpenBSD::Error;
sub add_entry
{
shift; # get rid of self
my $l = shift;
while (@_ >= 2) {
my $f = shift;
my $v = shift;
next if !defined $v or $v eq '';
if ($v =~ m/^\!(.*)$/o) {
push(@$l, $f, $1);
} else {
push(@$l, $f, $v);
}
}
}
sub prepare_for_addition
{
my ($self, $state, $pkgname) = @_;
my $ok = $self->check;
if (defined $ok) {
if ($ok == 0) {
$state->errsay("#1 #2 does not match",
$self->type, $self->name);
$state->{problems}++;
}
}
$self->{okay} = $ok;
}
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
my $auth = $self->name;
$state->say("adding #1 #2", $self->type, $auth) if $state->verbose >= 2;
return if $state->{not};
return if defined $self->{okay};
my $l=[];
push(@$l, "-v") if $state->verbose >= 2;
$self->build_args($l);
$state->vsystem($self->command,, @$l, '--', $auth);
}
package OpenBSD::PackingElement::NewUser;
sub command { OpenBSD::Paths->useradd }
sub build_args
{
my ($self, $l) = @_;
$self->add_entry($l,
'-u', $self->{uid},
'-g', $self->{group},
'-L', $self->{class},
'-c', $self->{comment},
'-d', $self->{home},
'-s', $self->{shell});
}
package OpenBSD::PackingElement::NewGroup;
sub command { OpenBSD::Paths->groupadd }
sub build_args
{
my ($self, $l) = @_;
$self->add_entry($l, '-g', $self->{gid});
}
package OpenBSD::PackingElement::FileBase;
use OpenBSD::Error;
use File::Basename;
use File::Path;
use OpenBSD::Temp;
sub find_extractible
{
my ($self, $state, $wanted, $tied) = @_;
if ($self->{tieto} || $self->{link} || $self->{symlink}) {
$tied->{$self->name} = $self;
} else {
$wanted->{$self->name} = $self;
}
}
sub prepare_for_addition
{
my ($self, $state, $pkgname) = @_;
my $fname = $self->retrieve_fullname($state, $pkgname);
# check for collisions with existing stuff
if ($state->vstat->exists($fname)) {
push(@{$state->{colliding}}, $self);
$self->{newly_found} = $pkgname;
$state->{problems}++;
return;
}
return if $state->defines('stub');
my $s = $state->vstat->add($fname,
$self->{tieto} ? 0 : $self->retrieve_size, $pkgname);
return unless defined $s;
if ($s->ro) {
$s->report_ro($state, $fname);
}
if ($s->avail < 0) {
$s->report_overflow($state, $fname);
}
}
sub prepare_to_extract
{
my ($self, $state, $file) = @_;
my $fullname = $self->fullname;
my $destdir = $state->{destdir};
$file->{cwd} = $self->cwd;
if (!$file->validate_meta($self)) {
$state->fatal("can't continue");
}
$file->set_name($fullname);
$file->{destdir} = $destdir;
}
sub find_safe_dir
{
my ($self, $filename) = @_;
# figure out a safe directory where to put the temp file
my $d = dirname($filename);
# we go back up until we find an existing directory.
# hopefully this will be on the same file system.
my @candidates = ();
while (!-d $d) {
push(@candidates, $d);
$d = dirname($d);
}
# and now we try to go back down, creating the best path we can
while (@candidates > 0) {
my $c = pop @candidates;
last if -e $c; # okay this exists, but is not a directory
$d = $c;
}
return $d;
}
sub create_temp
{
my ($self, $d, $state, $fullname) = @_;
# XXX this takes over right after find_safe_dir
if (!-e _) {
$state->make_path($d, $fullname);
}
my ($fh, $tempname) = OpenBSD::Temp::permanent_file($d, "pkg");
$self->{tempname} = $tempname;
if (!defined $tempname) {
if ($state->allow_nonroot($fullname)) {
$state->errsay("Can't create temp file outside localbase for #1", $fullname);
$state->errsay(OpenBSD::Temp->last_error);
return undef;
}
$state->fatal(OpenBSD::Temp->last_error);
}
return ($fh, $tempname);
}
sub tie
{
my ($self, $state) = @_;
if (defined $self->{link} || defined $self->{symlink}) {
return;
}
$self->SUPER::extract($state);
my $d = $self->find_safe_dir($state->{destdir}.$self->fullname);
if ($state->{not}) {
$state->say("link #1 -> #2",
$self->name, $d) if $state->verbose >= 3;
} else {
my ($fh, $tempname) = $self->create_temp($d, $state,
$self->fullname);
return if !defined $tempname;
my $src = $self->{tieto}->realname($state);
unlink($tempname);
$state->say("link #1 -> #2", $src, $tempname)
if $state->verbose >= 3;
link($src, $tempname) || $state->copy_file($src, $tempname);
}
}
sub extract
{
my ($self, $state, $file) = @_;
$self->SUPER::extract($state);
my $d = $self->find_safe_dir($file->{destdir}.$file->name);
if ($state->{not}) {
$state->say("extract #1 -> #2",
$self->name, $d) if $state->verbose >= 3;
$state->{archive}->skip;
} else {
my ($fh, $tempname) = $self->create_temp($d, $state,
$file->name);
if (!defined $tempname) {
$state->{archive}->skip;
return;
}
# XXX don't apply destdir twice
$file->{destdir} = '';
$file->set_name($tempname);
$state->say("extract #1 -> #2", $self->name, $tempname)
if $state->verbose >= 3;
if (!$file->isFile) {
$state->fatal("can't extract #1, it's not a file",
$self->stringize);
}
$file->create;
$self->may_check_digest($file, $state);
}
}
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
my $fullname = $self->fullname;
my $destdir = $state->{destdir};
if ($state->{not}) {
$state->say("moving tempfile -> #1",
$destdir.$fullname) if $state->verbose >= 5;
return;
}
$state->make_path(dirname($destdir.$fullname), $fullname);
if (defined $self->{link}) {
link($destdir.$self->{link}, $destdir.$fullname);
} elsif (defined $self->{symlink}) {
symlink($self->{symlink}, $destdir.$fullname);
} else {
if (!defined $self->{tempname}) {
return if $state->allow_nonroot($fullname);
$state->fatal("No tempname for #1", $fullname);
}
rename($self->{tempname}, $destdir.$fullname) or
$state->fatal("can't move #1 to #2: #3",
$self->{tempname}, $fullname, $!);
$state->say("moving #1 -> #2",
$self->{tempname}, $destdir.$fullname)
if $state->verbose >= 5;
delete $self->{tempname};
}
$self->set_modes($state, $destdir.$fullname);
}
package OpenBSD::PackingElement::RcScript;
sub install
{
my ($self, $state) = @_;
$state->{add_rcscripts}{$self->fullname} = 1;
$self->SUPER::install($state);
}
package OpenBSD::PackingElement::Sample;
use OpenBSD::Error;
use File::Copy;
sub prepare_for_addition
{
my ($self, $state, $pkgname) = @_;
if (!defined $self->{copyfrom}) {
$state->errsay("\@sample element #1 does not reference a valid file",
$self->fullname);
$state->{problems}++;
}
my $fname = $state->{destdir}.$self->fullname;
# If file already exists, we won't change it
if ($state->vstat->exists($fname)) {
return;
}
my $size = $self->{copyfrom}->{size};
my $s = $state->vstat->add($fname, $size, $pkgname);
return unless defined $s;
if ($s->ro) {
$s->report_ro($state, $fname);
}
if ($s->avail < 0) {
$s->report_overflow($state, $fname);
}
}
sub find_extractible
{
}
sub extract
{
}
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
my $destdir = $state->{destdir};
my $filename = $destdir.$self->fullname;
my $orig = $self->{copyfrom};
my $origname = $destdir.$orig->fullname;
if (-e $filename) {
if ($state->verbose) {
$state->say("The existing file #1 has NOT been changed",
$filename);
if (defined $orig->{d}) {
# XXX assume this would be the same type of file
my $d = $self->compute_digest($filename, $orig->{d});
if ($d->equals($orig->{d})) {
$state->say("(but it seems to match the sample file #1)", $origname);
} else {
$state->say("It does NOT match the sample file #1",
$origname);
$state->say("You may wish to update it manually");
}
}
}
} else {
if ($state->{not}) {
$state->say("The file #1 would be installed from #2",
$filename, $origname) if $state->verbose >= 2;
} else {
if (!copy($origname, $filename)) {
$state->errsay("File #1 could not be installed:\n\t#2", $filename, $!);
}
$self->set_modes($state, $filename);
if ($state->verbose >= 2) {
$state->say("installed #1 from #2",
$filename, $origname);
}
}
}
}
package OpenBSD::PackingElement::Sampledir;
sub extract
{
}
sub install
{
&OpenBSD::PackingElement::Dir::install;
}
package OpenBSD::PackingElement::Mandir;
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
if (!$state->{current_set}{known_mandirs}{$self->fullname}) {
$state->log("You may wish to add #1 to /etc/man.conf",
$self->fullname);
}
}
package OpenBSD::PackingElement::Manpage;
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
$self->register_manpage($state, 'addman');
}
package OpenBSD::PackingElement::InfoFile;
use File::Basename;
use OpenBSD::Error;
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
return if $state->{not};
my $fullname = $state->{destdir}.$self->fullname;
$state->vsystem(OpenBSD::Paths->install_info,
"--info-dir=".dirname($fullname), '--', $fullname);
}
package OpenBSD::PackingElement::Shell;
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
return if $state->{not};
my $fullname = $self->fullname;
my $destdir = $state->{destdir};
# go append to /etc/shells if needed
open(my $shells, '<', $destdir.OpenBSD::Paths->shells) or return;
while(<$shells>) {
s/^\#.*//o;
return if m/^\Q$fullname\E\s*$/;
}
close($shells);
open(my $shells2, '>>', $destdir.OpenBSD::Paths->shells) or return;
print $shells2 $fullname, "\n";
close $shells2;
$state->say("Shell #1 appended to #2", $fullname,
$destdir.OpenBSD::Paths->shells) if $state->verbose;
}
package OpenBSD::PackingElement::Dir;
sub extract
{
my ($self, $state) = @_;
my $fullname = $self->fullname;
my $destdir = $state->{destdir};
return if -e $destdir.$fullname;
$self->SUPER::extract($state);
$state->say("new directory #1", $destdir.$fullname)
if $state->verbose >= 3;
return if $state->{not};
$state->make_path($destdir.$fullname, $fullname);
}
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
my $fullname = $self->fullname;
my $destdir = $state->{destdir};
$state->say("new directory #1", $destdir.$fullname)
if $state->verbose >= 5;
return if $state->{not};
$state->make_path($destdir.$fullname, $fullname);
$self->set_modes($state, $destdir.$fullname);
}
package OpenBSD::PackingElement::Exec;
use OpenBSD::Error;
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
if ($self->should_run($state)) {
$self->run($state);
}
}
sub should_run() { 1 }
package OpenBSD::PackingElement::ExecAdd;
sub should_run
{
my ($self, $state) = @_;
return !$state->replacing;
}
package OpenBSD::PackingElement::ExecUpdate;
sub should_run
{
my ($self, $state) = @_;
return $state->replacing;
}
package OpenBSD::PackingElement::Tag;
sub install
{
my ($self, $state) = @_;
for my $d (@{$self->{definition_list}}) {
$d->add_tag($self, "install", $state);
}
}
package OpenBSD::PackingElement::Lib;
sub install
{
my ($self, $state) = @_;
$self->SUPER::install($state);
$self->mark_ldconfig_directory($state);
}
package OpenBSD::PackingElement::SpecialFile;
use OpenBSD::PackageInfo;
use OpenBSD::Error;
sub copy_info
{
my ($self, $dest, $state) = @_;
require File::Copy;
File::Copy::move($self->fullname, $dest) or
$state->errsay("Problem while moving #1 into #2: #3",
$self->fullname, $dest, $!);
}
sub extract
{
my ($self, $state) = @_;
$self->may_verify_digest($state);
}
sub find_extractible
{
my ($self, $state) = @_;
$self->may_verify_digest($state);
}
package OpenBSD::PackingElement::FCONTENTS;
sub copy_info
{
}
package OpenBSD::PackingElement::AskUpdate;
sub prepare_for_addition
{
my ($self, $state, $pkgname, $set) = @_;
my @old = $set->older_names;
if ($self->spec->match_ref(\@old) > 0) {
my $key = "update_".OpenBSD::PackageName::splitstem($pkgname);
return if $state->defines($key);
if ($state->is_interactive) {
if ($state->confirm_defaults_to_no(
"#1: #2.\nDo you want to update now",
$pkgname, $self->{message})) {
return;
}
} else {
$state->errsay("Can't update #1 now: #2",
$pkgname, $self->{message});
}
$state->{problems}++;
}
}
package OpenBSD::PackingElement::FDISPLAY;
sub install
{
my ($self, $state) = @_;
my $d = $self->{d};
if (!$state->{current_set}{known_displays}{$self->{d}->key}) {
$self->prepare($state);
}
$self->SUPER::install($state);
}
package OpenBSD::PackingElement::FUNDISPLAY;
sub find_extractible
{
my ($self, $state, $wanted, $tied) = @_;
$state->{current_set}{known_displays}{$self->{d}->key} = 1;
$self->SUPER::find_extractible($state, $wanted, $tied);
}
1;