From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id E56301FF16B for ; Fri, 21 Nov 2025 16:45:31 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 3FB9A25DE1; Fri, 21 Nov 2025 16:45:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=lchholdings-org.20230601.gappssmtp.com; s=20230601; t=1763503291; x=1764108091; darn=lists.proxmox.com; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=eP9TAS+yjahrlWkjVev/Ou6tQhSLaROpHRtAVH2bnkI=; b=vVF5HnPOC1ydMFONt/LkrESksCvWR4vI5zaXmN+fZn5bRXwy87e86SN/M+T8i9zo5J IkTDtuYqV99dgpbArpks4ZWqC4QRLspJ9sVAzjpHfbJ/2aPBt2EwTDsgww8M/cKScKvb TH2tqh1vBs2ciwkX+w9Szhbrqm5advmMKLClvjV5t9sdfz10mqOUidT9LILwFgR3nClh MRUTOIX1Dlc60xNYxBNVqmR07KfaJpzbyKhWt9z7NU9YH/KzFbo2/LJrMu72RTxqq5dN FuySfn/asjcoJ+8tWyoH5ha2eQG33GzStb0ugIA17bl9vfMRFCaNF6+P9An4F8yiOu/v oQrQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763503291; x=1764108091; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=eP9TAS+yjahrlWkjVev/Ou6tQhSLaROpHRtAVH2bnkI=; b=VPwj00he4QasqS3nmN6TDEdkBHVRcPUtwhst11kM5nzWUoU+ZTVpIdOwM5vmz1K/cO BfzUkETohyn7N/OKFFoPUj9XWi8vcpOH+II3whk7Nh2Tgz5u6STrylUiE60oU4W53FK/ NWlDfsMQQfroa+oWEqW92sasu/9uwrYu410Wu+nZAwANCpeKxTFgGEkyYcCxADkk8fno 5E8X/5Ss4GSJi6ysf1z2Jkn9naqs47dWy+S7VH8/fGPlbKzy6bxNaLSmSU46AZxJaHyi +wyYQB0L2va1mpNoDTOu+AoQtHgK6e+KRyYNY5Z76be1u81uDQXFeARhzoppuAigG2BT +RWA== X-Gm-Message-State: AOJu0YwCcVjEG+CQ698NZjDsapHjhIXVmRaIoYHlKW01ogWQlVwk66Aa lHqMeBI/9+qNUYlVsLe6zKvx1WXWGq7CT8lXK+AfMGL0dyAWvo3ZzJ/xaysKwgxIhJH/ExdiAEk sjh60O+RprQohrVS7xZ6g563KmuUNl+qWAFJc9iM8GQ59sPlUAY58wkGqGF+wl4jhmMZ4ihR4h5 7SjSGdBMlDv1fLY2pV+GHB7Uny5CjsPR3fbOMNvPFaJDgFcvS5oPfAWN/CZbouNJODVnMAabwgU Wbyhj+ObZAz X-Gm-Gg: ASbGnctihYy7hUb+BdBBcBAwPMXKYOMryDe739Pys/wGd9imG+r0c1XYe/5fro27anR J7l2vUG4Z+lXafvTm+sVmJPOshwTvfZudQygQDOzAHH4PTty6nTkxrTLdcViseU2KzzVo7FSpBP paSkVjZFW+6T/EQ7DSBMCsfgip5lrEWSD6hqYqYEES5cUimC39NbifYT+oQIK3P3ROCsyGFpLmO zO04EYmFTr7AjxgYfVA9UTw+MkGtzYG9whX0du3KwhP01FmnYCTTAaJJQci5YxYvhi31WikUvnc vqOPm4pOcScU+cpt3c2uaeP7h+vPrX8gIJD5X/iZrNCMfaaErULgkP5L/YGslxlzw3KdHEzXW45 tW6QeIRMYMvpHxjDLhb1HoMllUbWoKXVXk2wVW8UXKe8awF971plXbr5SBLKUqUuAqZCbL+VNMQ == X-Google-Smtp-Source: AGHT+IErw3ZlHbnC3lMgwCW1gxdayWC1HDwKQuSxbvTDjvsRrDVj3A4yUm6f0ie+dWPQf7bwlMFYmaPlm2ap X-Received: by 2002:a05:690c:30f:b0:786:8331:6a02 with SMTP id 00721157ae682-78929f40ac0mr308022437b3.69.1763503290886; Tue, 18 Nov 2025 14:01:30 -0800 (PST) X-Relaying-Domain: lukeh990.io From: Luke Harding To: pve-devel@lists.proxmox.com Date: Tue, 18 Nov 2025 17:01:21 -0500 Message-ID: <20251118220122.61257-7-luke@lukeh990.io> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251118220122.61257-1-luke@lukeh990.io> References: <20251118220122.61257-1-luke@lukeh990.io> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DMARC_MISSING 0.1 Missing DMARC policy RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record X-Mailman-Approved-At: Fri, 21 Nov 2025 16:45:23 +0100 Subject: [pve-devel] [PATCH dab 6/7] Run proxmox-perltidy X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Cc: Luke Harding Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" This made lots of changes feel free to drop it. Signed-off-by: Luke Harding --- PVE/DAB.pm | 1494 +++++++++++++++++++++++++++------------------------- dab | 235 +++++---- 2 files changed, 885 insertions(+), 844 deletions(-) diff --git a/PVE/DAB.pm b/PVE/DAB.pm index f42b937..b074460 100644 --- a/PVE/DAB.pm +++ b/PVE/DAB.pm @@ -63,72 +63,72 @@ sub __url_to_filename { # systemd: true my $suite_defaults = { debian => { - keyring => '/usr/share/keyrings/debian-archive-keyring.gpg', + keyring => '/usr/share/keyrings/debian-archive-keyring.gpg', }, devuan => { - #keyring => '/usr/share/keyrings/devuan-archive-keyring.gpg', # TODO: verify - systemd => 0, + #keyring => '/usr/share/keyrings/devuan-archive-keyring.gpg', # TODO: verify + systemd => 0, }, ubuntu => { - keyring => '/usr/share/keyrings/ubuntu-archive-keyring.gpg', + keyring => '/usr/share/keyrings/ubuntu-archive-keyring.gpg', }, }; my $supported_suites = { 'trixie' => { - ostype => "debian-13", - modern_apt_sources => 1, + ostype => "debian-13", + modern_apt_sources => 1, }, 'bookworm' => { - ostype => "debian-12", + ostype => "debian-12", }, 'bullseye' => { - ostype => "debian-11", + ostype => "debian-11", }, -# DEVUAN (imply systemd = 0 default) + # DEVUAN (imply systemd = 0 default) 'beowulf' => { - ostype => "devuan-3.0", - origin => 'devuan', + ostype => "devuan-3.0", + origin => 'devuan', }, 'chimaera' => { - ostype => "devuan-4.0", - origin => 'devuan', + ostype => "devuan-4.0", + origin => 'devuan', }, 'daedalus' => { - ostype => "devuan-5.0", - origin => 'devuan', + ostype => "devuan-5.0", + origin => 'devuan', }, 'excalibur' => { - ostype => "devuan-6.0", - origin => 'devuan', + ostype => "devuan-6.0", + origin => 'devuan', }, -# UBUNTU + # UBUNTU 'bionic' => { - ostype => "ubuntu-18.04", - origin => 'ubuntu', + ostype => "ubuntu-18.04", + origin => 'ubuntu', }, 'focal' => { - ostype => "ubuntu-20.04", - origin => 'ubuntu', + ostype => "ubuntu-20.04", + origin => 'ubuntu', }, 'jammy' => { - ostype => "ubuntu-22.04", - origin => 'ubuntu', + ostype => "ubuntu-22.04", + origin => 'ubuntu', }, 'noble' => { - ostype => "ubuntu-24.04", - origin => 'ubuntu', + ostype => "ubuntu-24.04", + origin => 'ubuntu', }, 'oracular' => { - ostype => "ubuntu-24.10", - origin => 'ubuntu', + ostype => "ubuntu-24.10", + origin => 'ubuntu', }, 'plucky' => { - ostype => "ubuntu-25.04", - origin => 'ubuntu', - modern_apt_sources => 1, + ostype => "ubuntu-25.04", + origin => 'ubuntu', + modern_apt_sources => 1, }, }; @@ -139,8 +139,8 @@ sub get_suite_info { # set defaults $suiteinfo->{origin} //= 'debian'; - if (my $defaults = $suite_defaults->{$suiteinfo->{origin}}) { - $suiteinfo->{$_} //= $defaults->{$_} for keys $defaults->%*; + if (my $defaults = $suite_defaults->{ $suiteinfo->{origin} }) { + $suiteinfo->{$_} //= $defaults->{$_} for keys $defaults->%*; } $suiteinfo->{suite} //= $suite; $suiteinfo->{systemd} //= 1; @@ -152,15 +152,15 @@ sub download { my ($self, $url, $path) = @_; my $tmpfn = "$path.tmp$$"; - $self->logmsg ("download: $url\n"); + $self->logmsg("download: $url\n"); - eval { $self->run_command ("wget -q '$url' -O '$tmpfn'") }; + eval { $self->run_command("wget -q '$url' -O '$tmpfn'") }; if (my $err = $@) { - unlink $tmpfn; - die $err; + unlink $tmpfn; + die $err; } - rename ($tmpfn, $path); + rename($tmpfn, $path); } sub write_file { @@ -170,8 +170,8 @@ sub write_file { unlink $file; - my $fh = IO::File->new ($file, O_WRONLY | O_CREAT, $perm) || - die "unable to open file '$file'"; + my $fh = IO::File->new($file, O_WRONLY | O_CREAT, $perm) + || die "unable to open file '$file'"; print $fh $data; @@ -183,11 +183,11 @@ sub read_file { die "no filename" if !$file; - my $fh = IO::File->new ($file) || - die "unable to open file '$file'"; + my $fh = IO::File->new($file) + || die "unable to open file '$file'"; local $/; # slurp mode - + my $data = <$fh>; $fh->close; @@ -205,39 +205,39 @@ sub read_config { my $res = {}; - my $fh = IO::File->new ("<$filename") || return $res; + my $fh = IO::File->new("<$filename") || return $res; my $rec = ''; - while (defined (my $line = <$fh>)) { - next if $line =~ m/^\#/; - next if $line =~ m/^\s*$/; - $rec .= $line; - }; + while (defined(my $line = <$fh>)) { + next if $line =~ m/^\#/; + next if $line =~ m/^\s*$/; + $rec .= $line; + } - close ($fh); + close($fh); chomp $rec; $rec .= "\n"; while ($rec) { - if ($rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si) { - $res->{headline} = $1; - chomp $res->{headline}; - my $long = $2; - $long =~ s/^\s+/ /; - $res->{description} = $long; - chomp $res->{description}; - } elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) { - my ($key, $value) = (lc ($1), $2); - if ($key eq 'source' || $key eq 'mirror' || $key eq 'install-source') { - push @{$res->{$key}}, $value; - } else { - die "duplicate key '$key'\n" if defined ($res->{$key}); - $res->{$key} = $value; - } - } else { - die "unable to parse config file: $rec"; - } + if ($rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si) { + $res->{headline} = $1; + chomp $res->{headline}; + my $long = $2; + $long =~ s/^\s+/ /; + $res->{description} = $long; + chomp $res->{description}; + } elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) { + my ($key, $value) = (lc($1), $2); + if ($key eq 'source' || $key eq 'mirror' || $key eq 'install-source') { + push @{ $res->{$key} }, $value; + } else { + die "duplicate key '$key'\n" if defined($res->{$key}); + $res->{$key} = $value; + } + } else { + die "unable to parse config file: $rec"; + } } die "unable to parse config file" if $rec; @@ -250,28 +250,28 @@ sub run_command { my $reader = IO::File->new(); my $writer = IO::File->new(); - my $error = IO::File->new(); + my $error = IO::File->new(); my $orig_pid = $$; - my $cmdstr = ref ($cmd) eq 'ARRAY' ? join (' ', @$cmd) : $cmd; + my $cmdstr = ref($cmd) eq 'ARRAY' ? join(' ', @$cmd) : $cmd; my $pid; eval { - if (ref ($cmd) eq 'ARRAY') { - $pid = open3 ($writer, $reader, $error, @$cmd) || die $!; - } else { - $pid = open3 ($writer, $reader, $error, $cmdstr) || die $!; - } + if (ref($cmd) eq 'ARRAY') { + $pid = open3($writer, $reader, $error, @$cmd) || die $!; + } else { + $pid = open3($writer, $reader, $error, $cmdstr) || die $!; + } }; my $err = $@; # catch exec errors if ($orig_pid != $$) { - $self->logmsg ("ERROR: command '$cmdstr' failed - fork failed\n"); - POSIX::_exit (1); - kill ('KILL', $$); + $self->logmsg("ERROR: command '$cmdstr' failed - fork failed\n"); + POSIX::_exit(1); + kill('KILL', $$); } die $err if $err; @@ -280,31 +280,31 @@ sub run_command { close $writer; my $select = IO::Select->new(); - $select->add ($reader); - $select->add ($error); + $select->add($reader); + $select->add($error); my $res = ''; my $logfd = $self->{logfd}; while ($select->count) { - my @handles = $select->can_read (); + my @handles = $select->can_read(); - foreach my $h (@handles) { - my $buf = ''; - my $count = sysread ($h, $buf, 4096); - if (!defined ($count)) { - waitpid ($pid, 0); - die "command '$cmdstr' failed: $!"; - } - $select->remove ($h) if !$count; + foreach my $h (@handles) { + my $buf = ''; + my $count = sysread($h, $buf, 4096); + if (!defined($count)) { + waitpid($pid, 0); + die "command '$cmdstr' failed: $!"; + } + $select->remove($h) if !$count; - print $logfd $buf; + print $logfd $buf; - $res .= $buf if $getoutput; - } + $res .= $buf if $getoutput; + } } - waitpid ($pid, 0); + waitpid($pid, 0); my $ec = ($? >> 8); die "command '$cmdstr' failed with exit code $ec\n" if $ec; @@ -315,7 +315,7 @@ sub run_command { sub logmsg { my $self = shift; print STDERR @_; - $self->writelog (@_); + $self->writelog(@_); } sub writelog { @@ -333,17 +333,17 @@ sub __sample_config { my $ostype = $self->{config}->{ostype}; if ($ostype =~ m/^de(bi|vu)an-/) { - $data .= "lxc.include = /usr/share/lxc/config/debian.common.conf\n"; - $data .= "lxc.include = /usr/share/lxc/config/debian.userns.conf\n" if $> != 0; + $data .= "lxc.include = /usr/share/lxc/config/debian.common.conf\n"; + $data .= "lxc.include = /usr/share/lxc/config/debian.userns.conf\n" if $> != 0; } elsif ($ostype =~ m/^ubuntu-/) { - $data .= "lxc.include = /usr/share/lxc/config/ubuntu.common.conf\n"; - $data .= "lxc.include = /usr/share/lxc/config/ubuntu.userns.conf\n" if $> != 0; + $data .= "lxc.include = /usr/share/lxc/config/ubuntu.common.conf\n"; + $data .= "lxc.include = /usr/share/lxc/config/ubuntu.userns.conf\n" if $> != 0; } else { - die "unknown os type '$ostype'\n"; + die "unknown os type '$ostype'\n"; } if ($> != 0) { - $data .= "lxc.idmap = u 0 100000 65536\n"; - $data .= "lxc.idmap = g 0 100000 65536\n"; + $data .= "lxc.idmap = u 0 100000 65536\n"; + $data .= "lxc.idmap = g 0 100000 65536\n"; } $data .= "lxc.uts.name = localhost\n"; $data .= "lxc.rootfs.path = $self->{rootfs}\n"; @@ -355,20 +355,19 @@ sub __allocate_ve { my ($self) = @_; my $cid; - if (my $fd = IO::File->new (".veid")) { - $cid = <$fd>; - chomp $cid; - close ($fd); + if (my $fd = IO::File->new(".veid")) { + $cid = <$fd>; + chomp $cid; + close($fd); } - $self->{working_dir} = getcwd; $self->{veconffile} = "$self->{working_dir}/config"; $self->{rootfs} = "$self->{working_dir}/rootfs"; if ($cid) { - $self->{veid} = $cid; - return $cid; + $self->{veid} = $cid; + return $cid; } my $uuid; @@ -377,21 +376,21 @@ sub __allocate_ve { UUID::unparse($uuid, $uuid_str); $self->{veid} = $uuid_str; - my $fd = IO::File->new (">.veid") || - die "unable to write '.veid'\n"; + my $fd = IO::File->new(">.veid") + || die "unable to write '.veid'\n"; print $fd "$self->{veid}\n"; - close ($fd); + close($fd); my $cdata = $self->__sample_config(); - my $fh = IO::File->new ($self->{veconffile}, O_WRONLY|O_CREAT|O_EXCL) || - die "unable to write lxc config file '$self->{veconffile}' - $!"; + my $fh = IO::File->new($self->{veconffile}, O_WRONLY | O_CREAT | O_EXCL) + || die "unable to write lxc config file '$self->{veconffile}' - $!"; print $fh $cdata; - close ($fh); + close($fh); mkdir $self->{rootfs} || die "unable to create rootfs - $!"; - $self->logmsg ("allocated VE $self->{veid}\n"); + $self->logmsg("allocated VE $self->{veid}\n"); return $self->{veid}; } @@ -405,9 +404,9 @@ sub can_usr_merge { # FIXME: add configuration override posibillity if ($ostype =~ m/^debian-(\d+)/) { - return int($1) >= 11; + return int($1) >= 11; } elsif ($ostype =~ m/^ubuntu-(\d+)/) { - return int($1) >= 21; + return int($1) >= 21; } return; # false } @@ -422,16 +421,16 @@ sub setup_usr_merge { my @merged_dirs = qw(bin sbin lib); if ($arch eq 'amd64') { - push @merged_dirs, qw(lib32 lib64 libx32); + push @merged_dirs, qw(lib32 lib64 libx32); } elsif ($arch eq 'i386') { - push @merged_dirs, qw(lib64 libx32); + push @merged_dirs, qw(lib64 libx32); } - $self->logmsg ("setup usr-merge symlinks for '" . join("', '", @merged_dirs) . "'\n"); + $self->logmsg("setup usr-merge symlinks for '" . join("', '", @merged_dirs) . "'\n"); for my $dir (@merged_dirs) { - symlink("usr/$dir", "$rootfs/$dir") or warn "could not create symlink - $!\n"; - mkpath "$rootfs/usr/$dir"; + symlink("usr/$dir", "$rootfs/$dir") or warn "could not create symlink - $!\n"; + mkpath "$rootfs/usr/$dir"; } } @@ -441,25 +440,25 @@ sub get_target_name { my $name = $config->{name} || die "no 'name' specified\n"; $name =~ m/^[a-z][0-9a-z\-\*\.]+$/ || die "illegal characters in name '$name'\n"; - my ($version, $arch, $ostype) = $config->@{'version', 'architecture', 'ostype'}; + my ($version, $arch, $ostype) = $config->@{ 'version', 'architecture', 'ostype' }; $name = "${ostype}-${name}" if $name !~ m/^$ostype/; - return "${name}_${version}_${arch}" + return "${name}_${version}_${arch}"; } sub new { my ($class, $config) = @_; - $class = ref ($class) || $class; - $config = read_config ('dab.conf') if !$config; + $class = ref($class) || $class; + $config = read_config('dab.conf') if !$config; my $self = { - config => $config, + config => $config, }; bless $self, $class; $self->{logfile} = "logfile"; - $self->{logfd} = IO::File->new (">>$self->{logfile}") || die "unable to open log file"; + $self->{logfd} = IO::File->new(">>$self->{logfile}") || die "unable to open log file"; my $arch = $config->{architecture} || die "no 'architecture' specified\n"; die "unsupported architecture '$arch'\n" if $arch !~ m/^(i386|amd64)$/; @@ -472,113 +471,118 @@ sub new { # assert required dab.conf keys exist for my $key (qw(version section headline maintainer)) { - die "no '$key' specified\n" if !$config->{$key}; + die "no '$key' specified\n" if !$config->{$key}; } $self->{targetname} = get_target_name($config); if (!$config->{source}) { - if (lc($suiteinfo->{origin}) eq 'debian') { - push @{$config->{source}}, ( - "http://deb.debian.org/debian SUITE main contrib", - "http://deb.debian.org/debian SUITE-updates main contrib", - "http://security.debian.org SUITE-security main contrib", - ); - } elsif (lc($suiteinfo->{origin}) eq 'ubuntu') { - my $comp = "main restricted universe multiverse"; - push @{$config->{source}}, ( - "http://archive.ubuntu.com/ubuntu SUITE $comp", - "http://archive.ubuntu.com/ubuntu SUITE-updates $comp", - "http://archive.ubuntu.com/ubuntu SUITE-security $comp", - ); - } else { - die "implement me"; - } + if (lc($suiteinfo->{origin}) eq 'debian') { + push @{ $config->{source} }, + ( + "http://deb.debian.org/debian SUITE main contrib", + "http://deb.debian.org/debian SUITE-updates main contrib", + "http://security.debian.org SUITE-security main contrib", + ); + } elsif (lc($suiteinfo->{origin}) eq 'ubuntu') { + my $comp = "main restricted universe multiverse"; + push @{ $config->{source} }, + ( + "http://archive.ubuntu.com/ubuntu SUITE $comp", + "http://archive.ubuntu.com/ubuntu SUITE-updates $comp", + "http://archive.ubuntu.com/ubuntu SUITE-security $comp", + ); + } else { + die "implement me"; + } } my $sources = undef; - foreach my $s (@{$config->{source}}) { - if ($s =~ m@^\s*((https?|ftp)://\S+)\s+(\S+)((\s+(\S+))+)$@) { - my ($url, $su, $components) = ($1, $3, $4); - $su =~ s/SUITE/$suite/; - $components =~ s/^\s+//; - $components =~ s/\s+$//; - my $ca; - foreach my $co (split (/\s+/, $components)) { - push @$ca, $co; - } - $ca = ['main'] if !$ca; - - push @$sources, { - source => $url, - comp => $ca, - suite => $su, - keep => 1, - }; - } else { - die "syntax error in source specification '$s'\n"; - } - } - - foreach my $is (@{$config->{'install-source'}}) { - if ($is =~ m@^\s*((https?|ftp)://\S+)\s+(\S+)((\s+(\S+))+)$@) { - my ($url, $su, $components) = ($1, $3, $4); - $su =~ s/SUITE/$suite/; - $components =~ s/^\s+//; - $components =~ s/\s+$//; - my $ca; - foreach my $co (split (/\s+/, $components)) { - push @$ca, $co; - } - $ca = ['main'] if !$ca; - - push @$sources, { - source => $url, - comp => $ca, - suite => $su, - keep => 0, - }; - } else { - die "syntax error in install-source specification '$is'\n"; - } - } - - foreach my $m (@{$config->{mirror}}) { - if ($m =~ m@^\s*((https?|ftp)://\S+)\s*=>\s*((https?|ftp)://\S+)\s*$@) { - my ($ms, $md) = ($1, $3); - my $found; - foreach my $ss (@$sources) { - if ($ss->{source} eq $ms) { - $found = 1; - $ss->{mirror} = $md; - last; - } - } - die "unusable mirror $ms\n" if !$found; - } else { - die "syntax error in mirror spezification '$m'\n"; - } + foreach my $s (@{ $config->{source} }) { + if ($s =~ m@^\s*((https?|ftp)://\S+)\s+(\S+)((\s+(\S+))+)$@) { + my ($url, $su, $components) = ($1, $3, $4); + $su =~ s/SUITE/$suite/; + $components =~ s/^\s+//; + $components =~ s/\s+$//; + my $ca; + foreach my $co (split(/\s+/, $components)) { + push @$ca, $co; + } + $ca = ['main'] if !$ca; + + push @$sources, + { + source => $url, + comp => $ca, + suite => $su, + keep => 1, + }; + } else { + die "syntax error in source specification '$s'\n"; + } + } + + foreach my $is (@{ $config->{'install-source'} }) { + if ($is =~ m@^\s*((https?|ftp)://\S+)\s+(\S+)((\s+(\S+))+)$@) { + my ($url, $su, $components) = ($1, $3, $4); + $su =~ s/SUITE/$suite/; + $components =~ s/^\s+//; + $components =~ s/\s+$//; + my $ca; + foreach my $co (split(/\s+/, $components)) { + push @$ca, $co; + } + $ca = ['main'] if !$ca; + + push @$sources, + { + source => $url, + comp => $ca, + suite => $su, + keep => 0, + }; + } else { + die "syntax error in install-source specification '$is'\n"; + } + } + + foreach my $m (@{ $config->{mirror} }) { + if ($m =~ m@^\s*((https?|ftp)://\S+)\s*=>\s*((https?|ftp)://\S+)\s*$@) { + my ($ms, $md) = ($1, $3); + my $found; + foreach my $ss (@$sources) { + if ($ss->{source} eq $ms) { + $found = 1; + $ss->{mirror} = $md; + last; + } + } + die "unusable mirror $ms\n" if !$found; + } else { + die "syntax error in mirror spezification '$m'\n"; + } } $self->{sources} = $sources; $self->{infodir} = "info"; $self->__allocate_ve(); - $self->{cachedir} = ($config->{cachedir} || 'cache') . "/$suite";; + $self->{cachedir} = ($config->{cachedir} || 'cache') . "/$suite"; my $incl = [qw (less ssh openssh-server logrotate)]; my $excl = [qw (modutils reiserfsprogs ppp pppconfig pppoe pppoeconf nfs-common mtools ntp)]; # ubuntu has too many dependencies on udev, so we cannot exclude it (instead we disable udevd) if (lc($suiteinfo->{origin}) eq 'ubuntu' && $suiteinfo->{systemd}) { - push @$incl, 'isc-dhcp-client'; - push @$excl, qw(libmodule-build-perl libdrm-common libdrm2 libplymouth5 plymouth plymouth-theme-ubuntu-text powermgmt-base); - if ($suite eq 'jammy') { - push @$excl, qw(fuse); # avoid fuse2 <-> fuse3 conflict - } + push @$incl, 'isc-dhcp-client'; + push @$excl, + qw(libmodule-build-perl libdrm-common libdrm2 libplymouth5 plymouth plymouth-theme-ubuntu-text powermgmt-base); + if ($suite eq 'jammy') { + push @$excl, qw(fuse); # avoid fuse2 <-> fuse3 conflict + } } else { - push @$excl, qw(module-init-tools pciutils hdparm memtest86+ parted); + push @$excl, qw(module-init-tools pciutils hdparm memtest86+ parted); } $self->{incl} = $incl; @@ -597,51 +601,51 @@ sub initialize { mkpath $infodir; # truncate log - my $logfd = $self->{logfd} = IO::File->new (">$self->{logfile}") || - die "unable to open log file"; + my $logfd = $self->{logfd} = IO::File->new(">$self->{logfile}") + || die "unable to open log file"; my $COMPRESSORS = [ - { - ext => 'xz', - decomp => 'xz -d', - }, - { - ext => 'gz', - decomp => 'gzip -d', - }, + { + ext => 'xz', + decomp => 'xz -d', + }, + { + ext => 'gz', + decomp => 'gzip -d', + }, ]; - foreach my $ss (@{$self->{sources}}) { - my $src = $ss->{mirror} || $ss->{source}; - my $path = "dists/$ss->{suite}/Release"; - my $url = "$src/$path"; - my $target = __url_to_filename ("$ss->{source}/$path"); - eval { - $self->download ($url, "$infodir/$target"); - $self->download ("$url.gpg", "$infodir/$target.gpg"); - # fixme: impl. verify (needs --keyring option) - }; - if (my $err = $@) { - print $logfd $@; - warn "Release info ignored\n"; - }; - - foreach my $comp (@{$ss->{comp}}) { - foreach my $compressor (@$COMPRESSORS) { - $path = "dists/$ss->{suite}/$comp/binary-$arch/Packages.$compressor->{ext}"; - $target = "$infodir/" . __url_to_filename ("$ss->{source}/$path"); - my $pkgsrc = "$src/$path"; - eval { - $self->download ($pkgsrc, $target); - $self->run_command ("$compressor->{decomp} '$target'"); - }; - if (my $err = $@) { - print $logfd "could not download Packages.$compressor->{ext}\n"; - } else { - last; - } - } - } + foreach my $ss (@{ $self->{sources} }) { + my $src = $ss->{mirror} || $ss->{source}; + my $path = "dists/$ss->{suite}/Release"; + my $url = "$src/$path"; + my $target = __url_to_filename("$ss->{source}/$path"); + eval { + $self->download($url, "$infodir/$target"); + $self->download("$url.gpg", "$infodir/$target.gpg"); + # fixme: impl. verify (needs --keyring option) + }; + if (my $err = $@) { + print $logfd $@; + warn "Release info ignored\n"; + } + + foreach my $comp (@{ $ss->{comp} }) { + foreach my $compressor (@$COMPRESSORS) { + $path = "dists/$ss->{suite}/$comp/binary-$arch/Packages.$compressor->{ext}"; + $target = "$infodir/" . __url_to_filename("$ss->{source}/$path"); + my $pkgsrc = "$src/$path"; + eval { + $self->download($pkgsrc, $target); + $self->run_command("$compressor->{decomp} '$target'"); + }; + if (my $err = $@) { + print $logfd "could not download Packages.$compressor->{ext}\n"; + } else { + last; + } + } + } } } @@ -670,7 +674,7 @@ sub write_config { $data .= "Description: $config->{headline}\n"; $data .= "$config->{description}\n" if $config->{description}; - write_file ($data, $filename, 0644); + write_file($data, $filename, 0644); } sub finalize { @@ -680,7 +684,7 @@ sub finalize { my $infodir = $self->{infodir}; my $arch = $self->{config}->{architecture}; - my $instpkgs = $self->read_installed (); + my $instpkgs = $self->read_installed(); my $pkginfo = $self->pkginfo(); my $veid = $self->{veid}; my $conffile = $self->{veconffile}; @@ -691,113 +695,116 @@ sub finalize { # cleanup mysqld if (-f "$rootdir/etc/init.d/mysql") { - $self->ve_command ("/etc/init.d/mysql stop"); + $self->ve_command("/etc/init.d/mysql stop"); } if (!($opts->{keepmycnf} || (-f "$rootdir/etc/init.d/mysql_randompw"))) { - unlink "$rootdir/root/.my.cnf"; + unlink "$rootdir/root/.my.cnf"; } - $self->logmsg ("cleanup package status\n"); + $self->logmsg("cleanup package status\n"); # prevent auto selection of all standard, required, or important packages which are not installed foreach my $pkg (keys %$pkginfo) { - my $pri = $pkginfo->{$pkg}->{priority}; - if ($pri && ($pri eq 'required' || $pri eq 'important' || $pri eq 'standard')) { - if (!$instpkgs->{$pkg}) { - $self->ve_dpkg_set_selection ($pkg, 'purge'); - } - } - } - - $self->ve_command ("apt-get clean"); - - $self->logmsg ("update available package list\n"); - - $self->ve_command ("dpkg --clear-avail"); - foreach my $ss (@{$self->{sources}}) { - my $relsrc = __url_to_filename ("$ss->{source}/dists/$ss->{suite}/Release"); - if (-f "$infodir/$relsrc" && -f "$infodir/$relsrc.gpg") { - $self->run_command ("cp '$infodir/$relsrc' '$rootdir/var/lib/apt/lists/$relsrc'"); - $self->run_command ("cp '$infodir/$relsrc.gpg' '$rootdir/var/lib/apt/lists/$relsrc.gpg'"); - } - foreach my $comp (@{$ss->{comp}}) { - my $src = __url_to_filename("$ss->{source}/dists/$ss->{suite}/${comp}/binary-${arch}/Packages"); - my $target = "/var/lib/apt/lists/$src"; - $self->run_command ("cp '$infodir/$src' '$rootdir/$target'"); - $self->ve_command ("dpkg --merge-avail '$target'"); - } + my $pri = $pkginfo->{$pkg}->{priority}; + if ($pri && ($pri eq 'required' || $pri eq 'important' || $pri eq 'standard')) { + if (!$instpkgs->{$pkg}) { + $self->ve_dpkg_set_selection($pkg, 'purge'); + } + } + } + + $self->ve_command("apt-get clean"); + + $self->logmsg("update available package list\n"); + + $self->ve_command("dpkg --clear-avail"); + foreach my $ss (@{ $self->{sources} }) { + my $relsrc = __url_to_filename("$ss->{source}/dists/$ss->{suite}/Release"); + if (-f "$infodir/$relsrc" && -f "$infodir/$relsrc.gpg") { + $self->run_command("cp '$infodir/$relsrc' '$rootdir/var/lib/apt/lists/$relsrc'"); + $self->run_command( + "cp '$infodir/$relsrc.gpg' '$rootdir/var/lib/apt/lists/$relsrc.gpg'"); + } + foreach my $comp (@{ $ss->{comp} }) { + my $src = __url_to_filename( + "$ss->{source}/dists/$ss->{suite}/${comp}/binary-${arch}/Packages"); + my $target = "/var/lib/apt/lists/$src"; + $self->run_command("cp '$infodir/$src' '$rootdir/$target'"); + $self->ve_command("dpkg --merge-avail '$target'"); + } } # set dselect default method - write_file ("apt apt\n", "$rootdir/var/lib/dpkg/cmethopt"); + write_file("apt apt\n", "$rootdir/var/lib/dpkg/cmethopt"); - $self->ve_divert_remove ("/usr/sbin/policy-rc.d"); + $self->ve_divert_remove("/usr/sbin/policy-rc.d"); - $self->ve_divert_remove ("/sbin/start-stop-daemon"); + $self->ve_divert_remove("/sbin/start-stop-daemon"); - $self->ve_divert_remove ("/sbin/init"); + $self->ve_divert_remove("/sbin/init"); # finally stop the VE - $self->run_command ("lxc-stop -n $veid --rcfile $conffile --kill"); + $self->run_command("lxc-stop -n $veid --rcfile $conffile --kill"); unlink "$rootdir/sbin/defenv"; unlink <$rootdir/root/dead.letter*>; unlink "$rootdir/var/log/init.log"; unlink "$rootdir/aquota.group", "$rootdir/aquota.user"; - write_file ("", "$rootdir/var/log/syslog"); + write_file("", "$rootdir/var/log/syslog"); my $get_path_size = sub { - my ($path) = @_; - my $sizestr = $self->run_command ("du -sm $path", undef, 1); - if ($sizestr =~ m/^(\d+)\s+\Q$path\E$/) { - return int($1); - } else { - die "unable to detect size for '$path'\n"; - } + my ($path) = @_; + my $sizestr = $self->run_command("du -sm $path", undef, 1); + if ($sizestr =~ m/^(\d+)\s+\Q$path\E$/) { + return int($1); + } else { + die "unable to detect size for '$path'\n"; + } }; - $self->logmsg ("detecting final appliance size: "); + $self->logmsg("detecting final appliance size: "); my $size = $get_path_size->($rootdir); - $self->logmsg ("$size MB\n"); + $self->logmsg("$size MB\n"); - $self->write_config ("$rootdir/etc/appliance.info", $size); + $self->write_config("$rootdir/etc/appliance.info", $size); - $self->logmsg ("creating final appliance archive\n"); + $self->logmsg("creating final appliance archive\n"); my $target = "$self->{targetname}.tar"; my $compressor = $opts->{compressor} // 'gz'; my $compressor2cmd_map = { - gz => 'gzip', - gzip => 'gzip', - zst => 'zstd --rm -9', - zstd => 'zstd --rm -9', - 'zstd-max' => 'zstd --rm -19 -T0', # maximal level where the decompressor can still run efficiently + gz => 'gzip', + gzip => 'gzip', + zst => 'zstd --rm -9', + zstd => 'zstd --rm -9', + 'zstd-max' => 'zstd --rm -19 -T0', # maximal level where the decompressor can still run efficiently }; my $compressor2ending = { - gzip => 'gz', - zstd => 'zst', - 'zstd-max' => 'zst', + gzip => 'gz', + zstd => 'zst', + 'zstd-max' => 'zst', }; my $compressor_cmd = $compressor2cmd_map->{$compressor}; - die "unkown compressor '$compressor', use one of: ". join(', ', sort keys %$compressor2cmd_map) - if !defined($compressor_cmd); + die "unkown compressor '$compressor', use one of: " . join(', ', sort keys %$compressor2cmd_map) + if !defined($compressor_cmd); my $ending = $compressor2ending->{$compressor} // $compressor; my $final_archive = "${target}.${ending}"; unlink $target; unlink $final_archive; - $self->run_command ("tar cpf $target --numeric-owner -C '$rootdir' ./etc/appliance.info"); - $self->run_command ("tar rpf $target --numeric-owner -C '$rootdir' --exclude ./etc/appliance.info ."); - $self->run_command ("$compressor_cmd $target"); + $self->run_command("tar cpf $target --numeric-owner -C '$rootdir' ./etc/appliance.info"); + $self->run_command( + "tar rpf $target --numeric-owner -C '$rootdir' --exclude ./etc/appliance.info ."); + $self->run_command("$compressor_cmd $target"); - $self->logmsg ("detecting final commpressed appliance size: "); + $self->logmsg("detecting final commpressed appliance size: "); $size = $get_path_size->($final_archive); - $self->logmsg ("$size MB\n"); + $self->logmsg("$size MB\n"); - $self->logmsg ("appliance archive: $final_archive\n"); + $self->logmsg("appliance archive: $final_archive\n"); } sub read_installed { @@ -812,26 +819,26 @@ sub read_installed { my $pkglist = {}; while (my $rec = <$PKGLST>) { - chomp $rec; - $rec =~ s/\n\s+/ /g; - $rec .= "\n"; - my $res = {}; + chomp $rec; + $rec =~ s/\n\s+/ /g; + $rec .= "\n"; + my $res = {}; - while ($rec =~ s/^([^:]+):\s+(.*?)\s*\n//) { - $res->{lc $1} = $2; - } + while ($rec =~ s/^([^:]+):\s+(.*?)\s*\n//) { + $res->{ lc $1 } = $2; + } - my $pkg = $res->{'package'}; - if (my $status = $res->{status}) { - my @sa = split (/\s+/, $status); - my $stat = $sa[0]; - if ($stat && ($stat ne 'purge')) { - $pkglist->{$pkg} = $res; - } - } + my $pkg = $res->{'package'}; + if (my $status = $res->{status}) { + my @sa = split(/\s+/, $status); + my $stat = $sa[0]; + if ($stat && ($stat ne 'purge')) { + $pkglist->{$pkg} = $res; + } + } } - close ($PKGLST); + close($PKGLST); return $pkglist; } @@ -848,16 +855,16 @@ sub ve_status { my $filename = "/proc/net/unix"; # similar test is used by lcxcontainers.c: list_active_containers - my $fh = IO::File->new ($filename, "r"); + my $fh = IO::File->new($filename, "r"); return $res if !$fh; while (defined(my $line = <$fh>)) { - if ($line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/) { - my $path = $1; - if ($path =~ m!^@/\S+/$veid/command$!) { - $res->{running} = 1; - } - } + if ($line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/) { + my $path = $1; + if ($path =~ m!^@/\S+/$veid/command$!) { + $res->{running} = 1; + } + } } close($fh); @@ -870,11 +877,14 @@ sub ve_command { my $veid = $self->{veid}; my $conffile = $self->{veconffile}; - if (ref ($cmd) eq 'ARRAY') { - unshift @$cmd, 'lxc-attach', '-n', $veid, '--rcfile', $conffile, '--clear-env', '--', 'defenv'; - $self->run_command($cmd, $input); + if (ref($cmd) eq 'ARRAY') { + unshift @$cmd, 'lxc-attach', '-n', $veid, '--rcfile', $conffile, '--clear-env', '--', + 'defenv'; + $self->run_command($cmd, $input); } else { - $self->run_command("lxc-attach -n $veid --rcfile $conffile --clear-env -- defenv $cmd", $input); + $self->run_command( + "lxc-attach -n $veid --rcfile $conffile --clear-env -- defenv $cmd", $input, + ); } } @@ -886,14 +896,24 @@ sub ve_exec { my $conffile = $self->{veconffile}; my $reader; - my $pid = open2($reader, "<&STDIN", 'lxc-attach', '-n', $veid, '--rcfile', $conffile, '--', - 'defenv', @cmd) || die "unable to exec command"; - - while (defined (my $line = <$reader>)) { - $self->logmsg ($line); - } - - waitpid ($pid, 0); + my $pid = open2( + $reader, + "<&STDIN", + 'lxc-attach', + '-n', + $veid, + '--rcfile', + $conffile, + '--', + 'defenv', + @cmd, + ) || die "unable to exec command"; + + while (defined(my $line = <$reader>)) { + $self->logmsg($line); + } + + waitpid($pid, 0); my $rc = $? >> 8; die "ve_exec failed - status $rc\n" if $rc != 0; @@ -904,13 +924,14 @@ sub ve_divert_add { $self->ve_command("dpkg-divert --add --divert '$filename.distrib' --rename '$filename'"); } + sub ve_divert_remove { my ($self, $filename) = @_; my $rootdir = $self->{rootfs}; unlink "$rootdir/$filename"; - $self->ve_command ("dpkg-divert --remove --rename '$filename'"); + $self->ve_command("dpkg-divert --remove --rename '$filename'"); } sub ve_debconfig_set { @@ -918,21 +939,21 @@ sub ve_debconfig_set { my $rootdir = $self->{rootfs}; my $cfgfile = "/tmp/debconf.txt"; - write_file ($dcdata, "$rootdir/$cfgfile"); - $self->ve_command ("debconf-set-selections $cfgfile"); - unlink "$rootdir/$cfgfile"; + write_file($dcdata, "$rootdir/$cfgfile"); + $self->ve_command("debconf-set-selections $cfgfile"); + unlink "$rootdir/$cfgfile"; } sub ve_dpkg_set_selection { my ($self, $pkg, $status) = @_; - $self->ve_command ("dpkg --set-selections", "$pkg $status"); + $self->ve_command("dpkg --set-selections", "$pkg $status"); } sub ve_dpkg { my ($self, $cmd, @pkglist) = @_; - return if !scalar (@pkglist); + return if !scalar(@pkglist); my $pkginfo = $self->pkginfo(); @@ -942,20 +963,20 @@ sub ve_dpkg { my @files; foreach my $pkg (@pkglist) { - my $filename = $self->getpkgfile ($pkg); - $self->run_command ("cp '$cachedir/$filename' '$rootdir/$filename'"); - push @files, "/$filename"; - $self->logmsg ("$cmd: $pkg\n"); + my $filename = $self->getpkgfile($pkg); + $self->run_command("cp '$cachedir/$filename' '$rootdir/$filename'"); + push @files, "/$filename"; + $self->logmsg("$cmd: $pkg\n"); } - my $fl = join (' ', @files); + my $fl = join(' ', @files); if ($cmd eq 'install') { - $self->ve_command ("dpkg --force-depends --force-confold --install $fl"); + $self->ve_command("dpkg --force-depends --force-confold --install $fl"); } elsif ($cmd eq 'unpack') { - $self->ve_command ("dpkg --force-depends --unpack $fl"); + $self->ve_command("dpkg --force-depends --unpack $fl"); } else { - die "internal error"; + die "internal error"; } foreach my $fn (@files) { unlink "$rootdir$fn"; } @@ -969,7 +990,7 @@ sub ve_destroy { my $vestat = $self->ve_status(); if ($vestat->{running}) { - $self->run_command ("lxc-stop -n $veid --rcfile $conffile --kill"); + $self->run_command("lxc-stop -n $veid --rcfile $conffile --kill"); } rmtree $self->{rootfs}; @@ -982,12 +1003,12 @@ sub ve_init { my $veid = $self->{veid}; my $conffile = $self->{veconffile}; - $self->logmsg ("initialize VE $veid\n"); + $self->logmsg("initialize VE $veid\n"); my $vestat = $self->ve_status(); if ($vestat->{running}) { - $self->run_command ("lxc-stop -n $veid --rcfile $conffile --kill"); - } + $self->run_command("lxc-stop -n $veid --rcfile $conffile --kill"); + } rmtree $self->{rootfs}; mkpath $self->{rootfs}; @@ -997,7 +1018,7 @@ sub __deb_version_cmp { my ($cur, $op, $new) = @_; if (system("dpkg", "--compare-versions", $cur, $op, $new) == 0) { - return 1; + return 1; } return 0; @@ -1010,35 +1031,35 @@ sub __parse_packages { open(my $PKGLST, '<', $filename) or die "unable to open '$filename' - $!"; while (my $rec = <$PKGLST>) { - $rec =~ s/\n\s+/ /g; - chomp $rec; - $rec .= "\n"; - - my $res = {}; - - while ($rec =~ s/^([^:]+):\s+(.*?)\s*\n//) { - $res->{lc $1} = $2; - } - - my $pkg = $res->{'package'}; - if ($pkg && $res->{'filename'}) { - my $cur; - if (my $info = $pkginfo->{$pkg}) { - $cur = $info->{version}; - } - my $new = $res->{version}; - if (!$cur || __deb_version_cmp ($cur, 'lt', $new)) { - if ($src) { - $res->{url} = "$src/$res->{'filename'}"; - } else { - die "no url for package '$pkg'" if !$res->{url}; - } - $pkginfo->{$pkg} = $res; - } - } - } - - close ($PKGLST); + $rec =~ s/\n\s+/ /g; + chomp $rec; + $rec .= "\n"; + + my $res = {}; + + while ($rec =~ s/^([^:]+):\s+(.*?)\s*\n//) { + $res->{ lc $1 } = $2; + } + + my $pkg = $res->{'package'}; + if ($pkg && $res->{'filename'}) { + my $cur; + if (my $info = $pkginfo->{$pkg}) { + $cur = $info->{version}; + } + my $new = $res->{version}; + if (!$cur || __deb_version_cmp($cur, 'lt', $new)) { + if ($src) { + $res->{url} = "$src/$res->{'filename'}"; + } else { + die "no url for package '$pkg'" if !$res->{url}; + } + $pkginfo->{$pkg} = $res; + } + } + } + + close($PKGLST); } sub pkginfo { @@ -1057,62 +1078,62 @@ sub pkginfo { # reading 'available' is faster, because it only contains latest version # (no need to do slow version compares) if (-f $availfn) { - __parse_packages ($pkginfo, $availfn); - $self->{pkginfo} = $pkginfo; - return $pkginfo; + __parse_packages($pkginfo, $availfn); + $self->{pkginfo} = $pkginfo; + return $pkginfo; } - $self->logmsg ("generating available package list\n"); + $self->logmsg("generating available package list\n"); - foreach my $ss (@{$self->{sources}}) { - foreach my $comp (@{$ss->{comp}}) { - my $url = "$ss->{source}/dists/$ss->{suite}/$comp/binary-$arch/Packages"; - my $pkgfilelist = "$infodir/" . __url_to_filename ($url); + foreach my $ss (@{ $self->{sources} }) { + foreach my $comp (@{ $ss->{comp} }) { + my $url = "$ss->{source}/dists/$ss->{suite}/$comp/binary-$arch/Packages"; + my $pkgfilelist = "$infodir/" . __url_to_filename($url); - my $src = $ss->{mirror} || $ss->{source}; + my $src = $ss->{mirror} || $ss->{source}; - __parse_packages ($pkginfo, $pkgfilelist, $src); - } + __parse_packages($pkginfo, $pkgfilelist, $src); + } } if (my $dep = $self->{config}->{depends}) { - foreach my $d (split (/,/, $dep)) { - if ($d =~ m/^\s*(\S+)\s*(\((\S+)\s+(\S+)\)\s*)?$/) { - my ($pkg, $op, $rver) = ($1, $3, $4); - $self->logmsg ("checking dependencies: $d\n"); - my $info = $pkginfo->{$pkg}; - die "package '$pkg' not available\n" if !$info; - if ($op) { - my $cver = $info->{version}; - if (!__deb_version_cmp ($cver, $op, $rver)) { - die "detected wrong version '$cver'\n"; - } - } - } else { - die "syntax error in depends field"; - } - } + foreach my $d (split(/,/, $dep)) { + if ($d =~ m/^\s*(\S+)\s*(\((\S+)\s+(\S+)\)\s*)?$/) { + my ($pkg, $op, $rver) = ($1, $3, $4); + $self->logmsg("checking dependencies: $d\n"); + my $info = $pkginfo->{$pkg}; + die "package '$pkg' not available\n" if !$info; + if ($op) { + my $cver = $info->{version}; + if (!__deb_version_cmp($cver, $op, $rver)) { + die "detected wrong version '$cver'\n"; + } + } + } else { + die "syntax error in depends field"; + } + } } $self->{pkginfo} = $pkginfo; my $tmpfn = "$availfn.tmp$$"; - my $fd = IO::File->new (">$tmpfn"); + my $fd = IO::File->new(">$tmpfn"); foreach my $pkg (sort keys %$pkginfo) { - my $info = $pkginfo->{$pkg}; - print $fd "package: $pkg\n"; - foreach my $k (sort keys %$info) { - next if $k eq 'description'; - next if $k eq 'package'; - my $v = $info->{$k}; - print $fd "$k: $v\n" if $v; - } - print $fd "description: $info->{description}\n" if $info->{description}; - print $fd "\n"; + my $info = $pkginfo->{$pkg}; + print $fd "package: $pkg\n"; + foreach my $k (sort keys %$info) { + next if $k eq 'description'; + next if $k eq 'package'; + my $v = $info->{$k}; + print $fd "$k: $v\n" if $v; + } + print $fd "description: $info->{description}\n" if $info->{description}; + print $fd "\n"; } - close ($fd); + close($fd); - rename ($tmpfn, $availfn); + rename($tmpfn, $availfn); return $pkginfo; } @@ -1121,24 +1142,24 @@ sub __record_provides { my ($pkginfo, $closure, $list, $skipself) = @_; foreach my $pname (@$list) { - my $info = $pkginfo->{$pname}; - # fixme: if someone install packages directly using dpkg, there - # is no entry in 'available', only in 'status'. In that case, we - # should extract info from $instpkgs - if (!$info) { - warn "hint: ignoring provides for '$pname' - package not in 'available' list.\n"; - next; - } - if (my $prov = $info->{provides}) { - my @pl = split (',', $prov); - foreach my $p (@pl) { - $p =~ m/\s*(\S+)/; - if (!($skipself && (grep { $1 eq $_ } @$list))) { - $closure->{$1} = 1; - } - } - } - $closure->{$pname} = 1 if !$skipself; + my $info = $pkginfo->{$pname}; + # fixme: if someone install packages directly using dpkg, there + # is no entry in 'available', only in 'status'. In that case, we + # should extract info from $instpkgs + if (!$info) { + warn "hint: ignoring provides for '$pname' - package not in 'available' list.\n"; + next; + } + if (my $prov = $info->{provides}) { + my @pl = split(',', $prov); + foreach my $p (@pl) { + $p =~ m/\s*(\S+)/; + if (!($skipself && (grep { $1 eq $_ } @$list))) { + $closure->{$1} = 1; + } + } + } + $closure->{$pname} = 1 if !$skipself; } } @@ -1148,14 +1169,14 @@ sub closure { my $pkginfo = $self->pkginfo(); # first, record provided packages - __record_provides ($pkginfo, $closure, $list, 1); + __record_provides($pkginfo, $closure, $list, 1); my $pkghash = {}; my $pkglist = []; # then resolve dependencies foreach my $pname (@$list) { - __closure_single ($pkginfo, $closure, $pkghash, $pkglist, $pname, $self->{excl}); + __closure_single($pkginfo, $closure, $pkghash, $pkglist, $pname, $self->{excl}); } return $pkglist; @@ -1181,64 +1202,64 @@ sub __closure_single { $url || die "$pname: no url for package '$pname'"; if (!$pkghash->{$pname}) { - unshift @$pkglist, $pname; - $pkghash->{$pname} = 1; + unshift @$pkglist, $pname; + $pkghash->{$pname} = 1; } - __record_provides ($pkginfo, $closure, [$pname]) if $info->{provides}; + __record_provides($pkginfo, $closure, [$pname]) if $info->{provides}; $closure->{$pname} = 1; - + #print "$url\n"; my @l; - push @l, split (/,/, $predep) if $predep; - push @l, split (/,/, $dep) if $dep; - - DEPEND: foreach my $p (@l) { - my @l1 = split (/\|/, $p); - foreach my $p1 (@l1) { - if ($p1 =~ m/^\s*(\S+).*/) { - #printf (STDERR "$pname: $p --> $1\n"); - if ($closure->{$1}) { - next DEPEND; # dependency already met - } - } - } - # search for non-excluded alternative - my $success; - foreach my $p1 (@l1) { - next unless $p1 =~ /^\s*(\S+)/; - my $candidate = $1; - - next if grep { $candidate eq $_ } @$excl; - - #print STDERR "$pname: trying $candidate for '$p'\n"; - - my $ok = eval { - __closure_single($pkginfo, $closure, $pkghash, $pkglist, $candidate, $excl); - 1; - }; - - if ($ok) { - $success = 1; - last; - } else { - print STDERR "$pname: $candidate failed, trying next alternative...\n"; - } - } - - die "package '$pname' could not satisfy dependency '$p' (all alternatives failed)\n" - unless $success; - } + push @l, split(/,/, $predep) if $predep; + push @l, split(/,/, $dep) if $dep; + +DEPEND: foreach my $p (@l) { + my @l1 = split(/\|/, $p); + foreach my $p1 (@l1) { + if ($p1 =~ m/^\s*(\S+).*/) { + #printf (STDERR "$pname: $p --> $1\n"); + if ($closure->{$1}) { + next DEPEND; # dependency already met + } + } + } + # search for non-excluded alternative + my $success; + foreach my $p1 (@l1) { + next unless $p1 =~ /^\s*(\S+)/; + my $candidate = $1; + + next if grep { $candidate eq $_ } @$excl; + + #print STDERR "$pname: trying $candidate for '$p'\n"; + + my $ok = eval { + __closure_single($pkginfo, $closure, $pkghash, $pkglist, $candidate, $excl); + 1; + }; + + if ($ok) { + $success = 1; + last; + } else { + print STDERR "$pname: $candidate failed, trying next alternative...\n"; + } + } + + die "package '$pname' could not satisfy dependency '$p' (all alternatives failed)\n" + unless $success; + } } sub cache_packages { my ($self, $pkglist) = @_; foreach my $pkg (@$pkglist) { - $self->getpkgfile ($pkg); + $self->getpkgfile($pkg); } } @@ -1253,16 +1274,16 @@ sub getpkgfile { my $filename; if ($url =~ m|/([^/]+.deb)$|) { - $filename = $1; + $filename = $1; } else { - die "internal error"; + die "internal error"; } return $filename if -f "$cachedir/$filename"; mkpath $cachedir; - $self->download ($url, "$cachedir/$filename"); + $self->download($url, "$cachedir/$filename"); return $filename; } @@ -1274,16 +1295,16 @@ sub install_init_script { my $suiteinfo = get_suite_info($suite); my $rootdir = $self->{rootfs}; - my $base = basename ($script); + my $base = basename($script); my $target = "$rootdir/etc/init.d/$base"; - $self->run_command ("install -m 0755 '$script' '$target'"); + $self->run_command("install -m 0755 '$script' '$target'"); if ($suiteinfo->{systemd}) { - die "unable to install init script (system uses systemd)\n"; + die "unable to install init script (system uses systemd)\n"; } elsif ($suite eq 'trusty' || $suite eq 'precise') { - die "unable to install init script (system uses upstart)\n"; + die "unable to install init script (system uses upstart)\n"; } else { - $self->ve_command ("insserv $base"); + $self->ve_command("insserv $base"); } return $target; @@ -1304,7 +1325,7 @@ sub bootstrap { my $suite = $self->{config}->{suite}; my $suiteinfo = get_suite_info($suite); - my $important = [ @{$self->{incl}} ]; + my $important = [@{ $self->{incl} }]; my $required; my $standard; @@ -1319,11 +1340,11 @@ sub bootstrap { 'openssh-server' => 1, ); - @{ $important } = grep { !$remove{$_} } @{ $important }; + @{$important} = grep { !$remove{$_} } @{$important}; } my $mta = $opts->{mta} ? $opts->{mta} : "postfix"; - + # Maintain compatibility with `--exim` flag if ($opts->{exim}) { $mta = "exim"; @@ -1334,25 +1355,25 @@ sub bootstrap { } if ($opts->{include}) { - push @$important, split(',', $opts->{include}); + push @$important, split(',', $opts->{include}); } my $exclude = {}; if ($opts->{exclude}) { - $exclude->{$_} = 1 for split(',', $opts->{exclude}); + $exclude->{$_} = 1 for split(',', $opts->{exclude}); } foreach my $p (sort keys %$pkginfo) { - next if grep { $p eq $_ } @{$self->{excl}}; - my $pri = $pkginfo->{$p}->{priority}; - next if !$pri; - next if $mta ne 'exim' && $p =~ m/exim/; - next if $p =~ m/(selinux|semanage|policycoreutils)/; + next if grep { $p eq $_ } @{ $self->{excl} }; + my $pri = $pkginfo->{$p}->{priority}; + next if !$pri; + next if $mta ne 'exim' && $p =~ m/exim/; + next if $p =~ m/(selinux|semanage|policycoreutils)/; - push @$required, $p if $pri eq 'required'; - next if $exclude->{$p}; - push @$important, $p if $pri eq 'important'; - push @$standard, $p if $pri eq 'standard' && !$opts->{minimal}; + push @$required, $p if $pri eq 'required'; + next if $exclude->{$p}; + push @$important, $p if $pri eq 'important'; + push @$standard, $p if $pri eq 'standard' && !$opts->{minimal}; } my $closure = {}; @@ -1360,277 +1381,282 @@ sub bootstrap { $important = $self->closure($closure, $important); if (!$opts->{minimal}) { - $standard = $self->closure($closure, $standard); + $standard = $self->closure($closure, $standard); } # test if we have all 'ubuntu-minimal' and 'ubuntu-standard' packages # except those explicitly excluded if ($suite eq 'hardy' || $suite eq 'intrepid' || $suite eq 'jaunty') { - my $mdeps = $pkginfo->{'ubuntu-minimal'}->{depends}; - foreach my $d (split (/,/, $mdeps)) { - if ($d =~ m/^\s*(\S+)$/) { - my $pkg = $1; - next if $closure->{$pkg}; - next if grep { $pkg eq $_ } @{$self->{excl}}; - die "missing ubuntu-minimal package '$pkg'\n"; - } - } - if (!$opts->{minimal}) { - $mdeps = $pkginfo->{'ubuntu-standard'}->{depends}; - foreach my $d (split (/,/, $mdeps)) { - if ($d =~ m/^\s*(\S+)$/) { - my $pkg = $1; - next if $closure->{$pkg}; - next if grep { $pkg eq $_ } @{$self->{excl}}; - die "missing ubuntu-standard package '$pkg'\n"; - } - } - } + my $mdeps = $pkginfo->{'ubuntu-minimal'}->{depends}; + foreach my $d (split(/,/, $mdeps)) { + if ($d =~ m/^\s*(\S+)$/) { + my $pkg = $1; + next if $closure->{$pkg}; + next if grep { $pkg eq $_ } @{ $self->{excl} }; + die "missing ubuntu-minimal package '$pkg'\n"; + } + } + if (!$opts->{minimal}) { + $mdeps = $pkginfo->{'ubuntu-standard'}->{depends}; + foreach my $d (split(/,/, $mdeps)) { + if ($d =~ m/^\s*(\S+)$/) { + my $pkg = $1; + next if $closure->{$pkg}; + next if grep { $pkg eq $_ } @{ $self->{excl} }; + die "missing ubuntu-standard package '$pkg'\n"; + } + } + } } # download/cache all files first - $self->cache_packages ($required); - $self->cache_packages ($important); - $self->cache_packages ($standard); - + $self->cache_packages($required); + $self->cache_packages($important); + $self->cache_packages($standard); + my $rootdir = $self->{rootfs}; # extract required packages first - $self->logmsg ("create basic environment\n"); + $self->logmsg("create basic environment\n"); if ($self->can_usr_merge()) { - $self->setup_usr_merge(); + $self->setup_usr_merge(); } my $compressor2opt = { - 'zst' => '--zstd', - 'gz' => '--gzip', - 'xz' => '--xz', + 'zst' => '--zstd', + 'gz' => '--gzip', + 'xz' => '--xz', }; my $compressor_re = join('|', keys $compressor2opt->%*); - $self->logmsg ("extract required packages to rootfs\n"); + $self->logmsg("extract required packages to rootfs\n"); foreach my $p (@$required) { - my $filename = $self->getpkgfile ($p); - my $content = $self->run_command("ar -t '$self->{cachedir}/$filename'", undef, 1); - if ($content =~ m/^(data.tar.($compressor_re))$/m) { - my $archive = $1; - my $tar_opts = "--keep-directory-symlink $compressor2opt->{$2}"; + my $filename = $self->getpkgfile($p); + my $content = $self->run_command("ar -t '$self->{cachedir}/$filename'", undef, 1); + if ($content =~ m/^(data.tar.($compressor_re))$/m) { + my $archive = $1; + my $tar_opts = "--keep-directory-symlink $compressor2opt->{$2}"; - $self->run_command("ar -p '$self->{cachedir}/$filename' '$archive' | tar -C '$rootdir' -xf - $tar_opts"); - } else { - die "unexpected error for $p: no data.tar.{xz,gz,zst} found..."; - } + $self->run_command( + "ar -p '$self->{cachedir}/$filename' '$archive' | tar -C '$rootdir' -xf - $tar_opts" + ); + } else { + die "unexpected error for $p: no data.tar.{xz,gz,zst} found..."; + } } # fake dpkg status - my $data = "Package: dpkg\n" . - "Version: $pkginfo->{dpkg}->{version}\n" . - "Status: install ok installed\n"; + my $data = + "Package: dpkg\n" + . "Version: $pkginfo->{dpkg}->{version}\n" + . "Status: install ok installed\n"; - write_file ($data, "$rootdir/var/lib/dpkg/status"); - write_file ("", "$rootdir/var/lib/dpkg/info/dpkg.list"); - write_file ("", "$rootdir/var/lib/dpkg/available"); + write_file($data, "$rootdir/var/lib/dpkg/status"); + write_file("", "$rootdir/var/lib/dpkg/info/dpkg.list"); + write_file("", "$rootdir/var/lib/dpkg/available"); $data = ''; if ($suiteinfo->{modern_apt_sources}) { - mkdir "$rootdir/etc/apt/sources.list.d"; - my $origin = lc($suiteinfo->{origin}); - my $keyring = $suiteinfo->{keyring} or die "missing keyring for origin '$origin'"; - my @keep_sources = grep { $_->{keep} } $self->{sources}->@*; - my $uris = { map { $_->{source} => 1 } @keep_sources }; - - for my $uri (keys $uris->%*) { - my $sources = [ grep { $_->{source} eq $uri } $self->{sources}->@* ]; - - my $suites = join(' ', (map { $_->{suite} } $sources->@*)); - my $unique_components = { map { $_ => 1 } (map { $_->{comp}->@* } $sources->@*) }; - my $components = join(' ', (sort keys $unique_components->%*)); - - $data .= "\n" if $data ne ''; - $data .= "Types: deb\n"; - $data .= "URIs: $uri\n"; - $data .= "Suites: $suites\n"; - $data .= "Components: $components\n"; - $data .= "Signed-By: $keyring\n"; - } - - write_file($data, "$rootdir/etc/apt/sources.list.d/${origin}.sources"); + mkdir "$rootdir/etc/apt/sources.list.d"; + my $origin = lc($suiteinfo->{origin}); + my $keyring = $suiteinfo->{keyring} or die "missing keyring for origin '$origin'"; + my @keep_sources = grep { $_->{keep} } $self->{sources}->@*; + my $uris = { map { $_->{source} => 1 } @keep_sources }; + + for my $uri (keys $uris->%*) { + my $sources = [grep { $_->{source} eq $uri } $self->{sources}->@*]; + + my $suites = join(' ', (map { $_->{suite} } $sources->@*)); + my $unique_components = { map { $_ => 1 } (map { $_->{comp}->@* } $sources->@*) }; + my $components = join(' ', (sort keys $unique_components->%*)); + + $data .= "\n" if $data ne ''; + $data .= "Types: deb\n"; + $data .= "URIs: $uri\n"; + $data .= "Suites: $suites\n"; + $data .= "Components: $components\n"; + $data .= "Signed-By: $keyring\n"; + } + + write_file($data, "$rootdir/etc/apt/sources.list.d/${origin}.sources"); } else { - foreach my $ss (@{$self->{sources}}) { - my $url = $ss->{source}; - my $comp = join (' ', @{$ss->{comp}}); - $data .= "deb $url $ss->{suite} $comp\n\n"; - } + foreach my $ss (@{ $self->{sources} }) { + my $url = $ss->{source}; + my $comp = join(' ', @{ $ss->{comp} }); + $data .= "deb $url $ss->{suite} $comp\n\n"; + } - write_file ($data, "$rootdir/etc/apt/sources.list"); + write_file($data, "$rootdir/etc/apt/sources.list"); } $data = "# UNCONFIGURED FSTAB FOR BASE SYSTEM\n"; - write_file ($data, "$rootdir/etc/fstab", 0644); + write_file($data, "$rootdir/etc/fstab", 0644); - write_file ("localhost\n", "$rootdir/etc/hostname", 0644); + write_file("localhost\n", "$rootdir/etc/hostname", 0644); # avoid warnings about non-existent resolv.conf - write_file ("", "$rootdir/etc/resolv.conf", 0644); + write_file("", "$rootdir/etc/resolv.conf", 0644); if (lc($suiteinfo->{origin}) eq 'ubuntu' && $suiteinfo->{systemd}) { - # no need to configure loopback device - # FIXME: Debian (systemd based?) too? + # no need to configure loopback device + # FIXME: Debian (systemd based?) too? } else { - $data = "auto lo\niface lo inet loopback\n"; - mkdir "$rootdir/etc/network"; - write_file ($data, "$rootdir/etc/network/interfaces", 0644); + $data = "auto lo\niface lo inet loopback\n"; + mkdir "$rootdir/etc/network"; + write_file($data, "$rootdir/etc/network/interfaces", 0644); } # setup devices - $self->run_command ("tar xzf '$devicetar' -C '$rootdir'") if $opts->{'device-skelleton'}; + $self->run_command("tar xzf '$devicetar' -C '$rootdir'") if $opts->{'device-skelleton'}; # avoid warnings about missing default locale - write_file ("LANG=\"C\"\n", "$rootdir/etc/default/locale", 0644); + write_file("LANG=\"C\"\n", "$rootdir/etc/default/locale", 0644); # fake init - rename ("$rootdir/sbin/init", "$rootdir/sbin/init.org") + rename("$rootdir/sbin/init", "$rootdir/sbin/init.org") or die "failed to backup distro 'init' for manual diversion - $!"; - $self->run_command ("cp '$fake_init' '$rootdir/sbin/init'"); + $self->run_command("cp '$fake_init' '$rootdir/sbin/init'"); - $self->run_command ("cp '$default_env' '$rootdir/sbin/defenv'"); + $self->run_command("cp '$default_env' '$rootdir/sbin/defenv'"); - $self->run_command ("lxc-start -n $veid -f $self->{veconffile}"); + $self->run_command("lxc-start -n $veid -f $self->{veconffile}"); - $self->logmsg ("initialize ld cache\n"); - $self->ve_command ("/sbin/ldconfig"); - $self->run_command ("ln -sf mawk '$rootdir/usr/bin/awk'"); + $self->logmsg("initialize ld cache\n"); + $self->ve_command("/sbin/ldconfig"); + $self->run_command("ln -sf mawk '$rootdir/usr/bin/awk'"); - $self->logmsg ("installing packages\n"); + $self->logmsg("installing packages\n"); - $self->ve_dpkg ('install', 'base-files', 'base-passwd'); + $self->ve_dpkg('install', 'base-files', 'base-passwd'); - $self->ve_dpkg ('install', 'dpkg'); + $self->ve_dpkg('install', 'dpkg'); - $self->run_command ("ln -sf /usr/share/zoneinfo/UTC '$rootdir/etc/localtime'"); + $self->run_command("ln -sf /usr/share/zoneinfo/UTC '$rootdir/etc/localtime'"); - $self->run_command ("ln -sf bash '$rootdir/bin/sh'"); + $self->run_command("ln -sf bash '$rootdir/bin/sh'"); - $self->ve_dpkg ('install', 'libc6'); - $self->ve_dpkg ('install', 'perl-base'); + $self->ve_dpkg('install', 'libc6'); + $self->ve_dpkg('install', 'perl-base'); unlink "$rootdir/usr/bin/awk"; - $self->ve_dpkg ('install', 'mawk'); - $self->ve_dpkg ('install', 'debconf'); + $self->ve_dpkg('install', 'mawk'); + $self->ve_dpkg('install', 'debconf'); # unpack required packages foreach my $p (@$required) { - $self->ve_dpkg ('unpack', $p); + $self->ve_dpkg('unpack', $p); } - rename ("$rootdir/sbin/init.org", "$rootdir/sbin/init") - or die "failed to restore distro 'init' for actual diversion - $!"; - $self->ve_divert_add ("/sbin/init"); - $self->run_command ("cp '$fake_init' '$rootdir/sbin/init'"); + rename("$rootdir/sbin/init.org", "$rootdir/sbin/init") + or die "failed to restore distro 'init' for actual diversion - $!"; + $self->ve_divert_add("/sbin/init"); + $self->run_command("cp '$fake_init' '$rootdir/sbin/init'"); # disable service activation - $self->ve_divert_add ("/usr/sbin/policy-rc.d"); + $self->ve_divert_add("/usr/sbin/policy-rc.d"); $data = "#!/bin/sh\nexit 101\n"; - write_file ($data, "$rootdir/usr/sbin/policy-rc.d", 755); + write_file($data, "$rootdir/usr/sbin/policy-rc.d", 755); # disable start-stop-daemon - $self->ve_divert_add ("/sbin/start-stop-daemon"); + $self->ve_divert_add("/sbin/start-stop-daemon"); $data = <ve_divert_add ("/sbin/udevd"); + $self->ve_divert_add("/sbin/udevd"); if ($suite eq 'etch') { - write_file ("NO_START=1\n", "$rootdir/etc/default/apache2"); # disable apache2 startup + write_file("NO_START=1\n", "$rootdir/etc/default/apache2"); # disable apache2 startup } - $self->logmsg ("configure required packages\n"); - $self->ve_command ("dpkg --force-confold --skip-same-version --configure -a"); + $self->logmsg("configure required packages\n"); + $self->ve_command("dpkg --force-confold --skip-same-version --configure -a"); # set postfix defaults if ($mta eq 'postfix') { - $data = "postfix postfix/main_mailer_type select Local only\n"; - $self->ve_debconfig_set ($data); + $data = "postfix postfix/main_mailer_type select Local only\n"; + $self->ve_debconfig_set($data); - $data = "postmaster: root\nwebmaster: root\n"; - write_file ($data, "$rootdir/etc/aliases"); + $data = "postmaster: root\nwebmaster: root\n"; + write_file($data, "$rootdir/etc/aliases"); } if ($suite eq 'jaunty') { - # jaunty does not create /var/run/network, so network startup fails. - # so we do not use tmpfs for /var/run and /var/lock - $self->run_command ("sed -e 's/RAMRUN=yes/RAMRUN=no/' -e 's/RAMLOCK=yes/RAMLOCK=no/' -i $rootdir/etc/default/rcS"); - # and create the directory here - $self->run_command ("mkdir $rootdir/var/run/network"); + # jaunty does not create /var/run/network, so network startup fails. + # so we do not use tmpfs for /var/run and /var/lock + $self->run_command( + "sed -e 's/RAMRUN=yes/RAMRUN=no/' -e 's/RAMLOCK=yes/RAMLOCK=no/' -i $rootdir/etc/default/rcS" + ); + # and create the directory here + $self->run_command("mkdir $rootdir/var/run/network"); } # unpack base packages foreach my $p (@$important) { - $self->ve_dpkg ('unpack', $p); + $self->ve_dpkg('unpack', $p); } # start loopback if (-x "$rootdir/sbin/ifconfig") { - $self->ve_command ("ifconfig lo up"); + $self->ve_command("ifconfig lo up"); } else { - $self->ve_command ("ip link set lo up"); + $self->ve_command("ip link set lo up"); } - $self->logmsg ("configure important packages\n"); - $self->ve_command ("dpkg --force-confold --skip-same-version --configure -a"); + $self->logmsg("configure important packages\n"); + $self->ve_command("dpkg --force-confold --skip-same-version --configure -a"); if (-d "$rootdir/etc/event.d") { - unlink <$rootdir/etc/event.d/tty*>; + unlink <$rootdir/etc/event.d/tty*>; } if (-f "$rootdir/etc/inittab") { - $self->run_command ("sed -i -e '/getty\\s38400\\stty[23456]/d' '$rootdir/etc/inittab'"); + $self->run_command("sed -i -e '/getty\\s38400\\stty[23456]/d' '$rootdir/etc/inittab'"); } # Link /etc/mtab to /proc/mounts, so df and friends will work: unlink "$rootdir/etc/mtab"; - $self->ve_command ("ln -s /proc/mounts /etc/mtab"); + $self->ve_command("ln -s /proc/mounts /etc/mtab"); # reset password - $self->ve_command ("usermod -L root"); + $self->ve_command("usermod -L root"); if ($mta eq 'postfix') { - $data = "postfix postfix/main_mailer_type select No configuration\n"; - $self->ve_debconfig_set ($data); + $data = "postfix postfix/main_mailer_type select No configuration\n"; + $self->ve_debconfig_set($data); - unlink "$rootdir/etc/mailname"; - write_file ($postfix_main_cf, "$rootdir/etc/postfix/main.cf"); + unlink "$rootdir/etc/mailname"; + write_file($postfix_main_cf, "$rootdir/etc/postfix/main.cf"); } if (!$opts->{minimal}) { - # unpack standard packages - foreach my $p (@$standard) { - $self->ve_dpkg ('unpack', $p); - } + # unpack standard packages + foreach my $p (@$standard) { + $self->ve_dpkg('unpack', $p); + } - $self->logmsg ("configure standard packages\n"); - $self->ve_command ("dpkg --force-confold --skip-same-version --configure -a"); + $self->logmsg("configure standard packages\n"); + $self->ve_command("dpkg --force-confold --skip-same-version --configure -a"); } # disable HWCLOCK access - $self->run_command ("echo 'HWCLOCKACCESS=no' >> '$rootdir/etc/default/rcS'"); + $self->run_command("echo 'HWCLOCKACCESS=no' >> '$rootdir/etc/default/rcS'"); # disable hald - $self->ve_divert_add ("/usr/sbin/hald"); + $self->ve_divert_add("/usr/sbin/hald"); # disable /dev/urandom init - $self->run_command ("install -m 0755 '$script_init_urandom' '$rootdir/etc/init.d/urandom'"); + $self->run_command("install -m 0755 '$script_init_urandom' '$rootdir/etc/init.d/urandom'"); if ($suite eq 'etch' || $suite eq 'hardy' || $suite eq 'intrepid' || $suite eq 'jaunty') { - # avoid klogd start - $self->ve_divert_add ("/sbin/klogd"); + # avoid klogd start + $self->ve_divert_add("/sbin/klogd"); } my $cmd = 'find'; @@ -1643,21 +1669,23 @@ EOD $cmd .= ' -e \'s/^\(fs\.inotify\.max_user_watches.*\)/#\1/\''; $cmd .= ' -e \'s/^\(vm\.mmap_min_addr.*\)/#\1/\''; $cmd .= " -i"; - $self->run_command ($cmd); + $self->run_command($cmd); my $bindv6only = "$rootdir/etc/sysctl.d/bindv6only.conf"; if (-f $bindv6only) { - $cmd = 'sed'; - $cmd .= ' -e \'s/^\(net\.ipv6\.bindv6only.*\)/#\1/\''; - $cmd .= " -i '$bindv6only'"; - $self->run_command ($cmd); + $cmd = 'sed'; + $cmd .= ' -e \'s/^\(net\.ipv6\.bindv6only.*\)/#\1/\''; + $cmd .= " -i '$bindv6only'"; + $self->run_command($cmd); } if ($suiteinfo->{systemd}) { - for my $unit (qw(sys-kernel-config.mount sys-kernel-debug.mount systemd-journald-audit.socket)) { - $self->logmsg("Masking problematic systemd unit '$unit'\n"); - $self->mask_systemd_unit($unit); - } + for my $unit ( + qw(sys-kernel-config.mount sys-kernel-debug.mount systemd-journald-audit.socket) + ) { + $self->logmsg("Masking problematic systemd unit '$unit'\n"); + $self->mask_systemd_unit($unit); + } } } @@ -1670,15 +1698,15 @@ sub enter { my $vestat = $self->ve_status(); if (!$vestat->{exist}) { - $self->logmsg ("Please create the appliance first (bootstrap)"); - return; + $self->logmsg("Please create the appliance first (bootstrap)"); + return; } if (!$vestat->{running}) { - $self->run_command ("lxc-start -n $veid -f $conffile"); + $self->run_command("lxc-start -n $veid -f $conffile"); } - system ("lxc-attach -n $veid --rcfile $conffile --clear-env"); + system("lxc-attach -n $veid --rcfile $conffile --clear-env"); } sub ve_mysql_command { @@ -1693,7 +1721,8 @@ sub ve_mysql_command { sub ve_mysql_bootstrap { my ($self, $sql, $password) = @_; - my $cmd = "/usr/sbin/mysqld --bootstrap --user=mysql --skip-grant-tables --skip-bdb --skip-innodb --skip-ndbcluster"; + my $cmd = + "/usr/sbin/mysqld --bootstrap --user=mysql --skip-grant-tables --skip-bdb --skip-innodb --skip-ndbcluster"; $self->ve_command($cmd, $sql); } @@ -1701,12 +1730,12 @@ sub compute_required { my ($self, $pkglist) = @_; my $pkginfo = $self->pkginfo(); - my $instpkgs = $self->read_installed (); + my $instpkgs = $self->read_installed(); my $closure = {}; __record_provides($pkginfo, $closure, [keys $instpkgs->%*]); - return $self->closure ($closure, $pkglist); + return $self->closure($closure, $pkglist); } sub task_postgres { @@ -1718,32 +1747,32 @@ sub task_postgres { my $suite = $self->{config}->{suite}; if ($suite eq 'buster') { - @supp = ('11'); - $pgversion = '11'; + @supp = ('11'); + $pgversion = '11'; } elsif ($suite eq 'bullseye') { - @supp = ('13'); + @supp = ('13'); } elsif ($suite eq 'bookworm') { - @supp = ('15'); + @supp = ('15'); } elsif ($suite eq 'trixie') { - @supp = ('16', '17'); + @supp = ('16', '17'); } $pgversion = $opts->{version} if $opts->{version}; my $required; if (defined($pgversion)) { - die "unsupported postgres version '$pgversion'\n" if !grep { $pgversion eq $_; } @supp; + die "unsupported postgres version '$pgversion'\n" if !grep { $pgversion eq $_; } @supp; - $required = $self->compute_required (["postgresql-$pgversion"]); + $required = $self->compute_required(["postgresql-$pgversion"]); } else { - $required = $self->compute_required (["postgresql"]); + $required = $self->compute_required(["postgresql"]); } - $self->cache_packages ($required); - - $self->ve_dpkg ('install', @$required); + $self->cache_packages($required); + + $self->ve_dpkg('install', @$required); my $iscript = "postgresql-$pgversion"; - $self->ve_command ("/etc/init.d/$iscript start") if $opts->{start}; + $self->ve_command("/etc/init.d/$iscript start") if $opts->{start}; } sub task_mysql { @@ -1758,27 +1787,29 @@ sub task_mysql { $self->cache_packages($required); - $self->ve_dpkg ('install', @$required); + $self->ve_dpkg('install', @$required); # fix security (see /usr/bin/mysql_secure_installation) - my $sql = "DELETE FROM mysql.user WHERE User='';\n" . - "DELETE FROM mysql.user WHERE User='root' AND Host!='localhost';\n" . - "FLUSH PRIVILEGES;\n"; - $self->ve_mysql_bootstrap ($sql); + my $sql = + "DELETE FROM mysql.user WHERE User='';\n" + . "DELETE FROM mysql.user WHERE User='root' AND Host!='localhost';\n" + . "FLUSH PRIVILEGES;\n"; + $self->ve_mysql_bootstrap($sql); if ($password) { - my $rpw = $password eq 'random' ? 'admin' : $password; + my $rpw = $password eq 'random' ? 'admin' : $password; - my $sql = "USE mysql;\n" . - "UPDATE user SET password=PASSWORD(\"$rpw\") WHERE user='root';\n" . - "FLUSH PRIVILEGES;\n"; - $self->ve_mysql_bootstrap ($sql); + my $sql = + "USE mysql;\n" + . "UPDATE user SET password=PASSWORD(\"$rpw\") WHERE user='root';\n" + . "FLUSH PRIVILEGES;\n"; + $self->ve_mysql_bootstrap($sql); - write_file ("[client]\nuser=root\npassword=\"$rpw\"\n", "$rootdir/root/.my.cnf", 0600); - if ($password eq 'random') { - $self->install_init_script ($script_mysql_randompw, 2, 20); - } + write_file("[client]\nuser=root\npassword=\"$rpw\"\n", "$rootdir/root/.my.cnf", 0600); + if ($password eq 'random') { + $self->install_init_script($script_mysql_randompw, 2, 20); + } } $self->ve_command("/etc/init.d/mysql start") if $opts->{start}; # FIXME: use systemd service?! @@ -1794,33 +1825,34 @@ sub task_php { my $base_set = [qw(php-cli libapache2-mod-php php-gd)]; my $required = $self->compute_required($base_set); - $self->cache_packages ($required); + $self->cache_packages($required); - $self->ve_dpkg ('install', @$required); + $self->ve_dpkg('install', @$required); if ($memlimit) { - my $sed_cmd = ['sed', '-e', "s/^\\s*memory_limit\\s*=.*;/memory_limit = ${memlimit}M;/", '-i']; - my $found = 0; - for my $fn (glob("'${rootdir}/etc/php/*/apache2/php.ini'")) { - push @$sed_cmd, "$rootdir/$fn"; - $found = 1; - } - if (!$found) { - warn "WARN: did not found any php.ini to set the memlimit!\n"; - return; - } - $self->run_command($sed_cmd); + my $sed_cmd = + ['sed', '-e', "s/^\\s*memory_limit\\s*=.*;/memory_limit = ${memlimit}M;/", '-i']; + my $found = 0; + for my $fn (glob("'${rootdir}/etc/php/*/apache2/php.ini'")) { + push @$sed_cmd, "$rootdir/$fn"; + $found = 1; + } + if (!$found) { + warn "WARN: did not found any php.ini to set the memlimit!\n"; + return; + } + $self->run_command($sed_cmd); } } sub install { my ($self, $pkglist, $unpack) = @_; - my $required = $self->compute_required ($pkglist); + my $required = $self->compute_required($pkglist); - $self->cache_packages ($required); + $self->cache_packages($required); - $self->ve_dpkg ($unpack ? 'unpack' : 'install', @$required); + $self->ve_dpkg($unpack ? 'unpack' : 'install', @$required); } sub cleanup { @@ -1830,7 +1862,7 @@ sub cleanup { unlink "$self->{targetname}.tar"; unlink "$self->{targetname}.tar.gz"; - $self->ve_destroy (); + $self->ve_destroy(); unlink ".veid"; rmtree $self->{cachedir} if $distclean && !$self->{config}->{cachedir}; diff --git a/dab b/dab index 9a010c0..11a4cba 100755 --- a/dab +++ b/dab @@ -32,11 +32,11 @@ sub print_usage { print STDERR "USAGE: dab [parameters]\n\n"; for my $cmd (sort keys %$commands) { - if (my $opts = $commands->{$cmd}) { - print STDERR " dab $cmd $opts\n"; - } else { - print STDERR " dab $cmd\n"; - } + if (my $opts = $commands->{$cmd}) { + print STDERR " dab $cmd $opts\n"; + } else { + print STDERR " dab $cmd\n"; + } } } @@ -46,14 +46,14 @@ sub fatal_usage { print STDERR "\nERROR: $msg\n\n" if $msg; print_usage(); - exit (-1); + exit(-1); } -if (scalar (@ARGV) == 0) { +if (scalar(@ARGV) == 0) { fatal_usage("no command specified"); } -my $cmdline = join (' ', @ARGV); +my $cmdline = join(' ', @ARGV); my $cmd = shift @ARGV; if (!$cmd) { @@ -62,16 +62,17 @@ if (!$cmd) { fatal_usage("unknown command '$cmd'"); } elsif ($cmd eq 'help') { print_usage(); - exit (0); + exit(0); } my $dab; -sub dab :prototype() { # make it a directly as `dab->foo()` callable singleton + +sub dab : prototype() { # make it a directly as `dab->foo()` callable singleton $dab = PVE::DAB->new() if !$dab; return $dab; } -dab->writelog ("dab: $cmdline\n"); +dab->writelog("dab: $cmdline\n"); $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub { die "interrupted by signal\n"; @@ -79,153 +80,161 @@ $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub { eval { if ($cmd eq 'init') { - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; - dab->initialize(); + dab->initialize(); } elsif ($cmd eq 'bootstrap') { - my $opts = {}; - if (!GetOptions ($opts, 'exim', 'minimal', 'device-skelleton', 'include=s', - 'exclude=s', 'no-ssh', 'mta=s')) { - fatal_usage(); - } - die "command 'bootstrap' expects no arguments.\n" if scalar (@ARGV) != 0; - - $dab->ve_init(); - $dab->bootstrap ($opts); + my $opts = {}; + if (!GetOptions( + $opts, + 'exim', + 'minimal', + 'device-skelleton', + 'include=s', + 'exclude=s', + 'no-ssh', + 'mta=s', + )) { + fatal_usage(); + } + die "command 'bootstrap' expects no arguments.\n" if scalar(@ARGV) != 0; + + $dab->ve_init(); + $dab->bootstrap($opts); } elsif ($cmd eq 'finalize') { - my $opts = {}; - if (!GetOptions ($opts, 'keepmycnf', 'compressor=s')) { - fatal_usage(); - } - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; + my $opts = {}; + if (!GetOptions($opts, 'keepmycnf', 'compressor=s')) { + fatal_usage(); + } + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; - $dab->finalize($opts); + $dab->finalize($opts); } elsif ($cmd eq 'veid') { - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; - print $dab->{veid} . "\n"; + print $dab->{veid} . "\n"; } elsif ($cmd eq 'basedir') { - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; - print $dab->{rootfs} . "\n"; + print $dab->{rootfs} . "\n"; } elsif ($cmd eq 'targetname') { - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; - print $dab->{targetname} . "\n"; + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; + print $dab->{targetname} . "\n"; } elsif ($cmd eq 'packagefile') { - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; - # FIXME: either drop this or make it compressor aware, not all end with .gz... - print "$dab->{targetname}.tar.gz\n"; + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; + # FIXME: either drop this or make it compressor aware, not all end with .gz... + print "$dab->{targetname}.tar.gz\n"; } elsif ($cmd eq 'list') { - my $verbose; - if (!GetOptions ('verbose' =>\$verbose)) { - fatal_usage(); - } - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; - - my $instpkgs = $dab->read_installed (); - - foreach my $pkg (sort keys %$instpkgs) { - if ($verbose) { - my $version = $instpkgs->{$pkg}->{version}; - print "$pkg $version\n"; - } else { - print "$pkg\n"; - } - } + my $verbose; + if (!GetOptions('verbose' => \$verbose)) { + fatal_usage(); + } + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; + + my $instpkgs = $dab->read_installed(); + + foreach my $pkg (sort keys %$instpkgs) { + if ($verbose) { + my $version = $instpkgs->{$pkg}->{version}; + print "$pkg $version\n"; + } else { + print "$pkg\n"; + } + } } elsif ($cmd eq 'task') { - my $task = shift @ARGV; - if (!$task) { - fatal_usage("no task specified"); - } + my $task = shift @ARGV; + if (!$task) { + fatal_usage("no task specified"); + } - my $opts = {}; - if ($task eq 'mysql') { - if (!GetOptions ($opts, 'password=s', 'start')) { - fatal_usage(); - } - die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0; + my $opts = {}; + if ($task eq 'mysql') { + if (!GetOptions($opts, 'password=s', 'start')) { + fatal_usage(); + } + die "task '$task' expects no arguments.\n" if scalar(@ARGV) != 0; - $dab->task_mysql ($opts); + $dab->task_mysql($opts); - } elsif ($task eq 'postgres') { - if (!GetOptions ($opts, 'version=s', 'start')) { - fatal_usage(); - } - die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0; + } elsif ($task eq 'postgres') { + if (!GetOptions($opts, 'version=s', 'start')) { + fatal_usage(); + } + die "task '$task' expects no arguments.\n" if scalar(@ARGV) != 0; - $dab->task_postgres ($opts); + $dab->task_postgres($opts); - } elsif ($task eq 'php') { - if (!GetOptions ($opts, 'memlimit=i')) { - fatal_usage(); - } - die "task '$task' expects no arguments.\n" if scalar (@ARGV) != 0; + } elsif ($task eq 'php') { + if (!GetOptions($opts, 'memlimit=i')) { + fatal_usage(); + } + die "task '$task' expects no arguments.\n" if scalar(@ARGV) != 0; - $dab->task_php ($opts); + $dab->task_php($opts); - } else { - fatal_usage("unknown task '$task'"); - } + } else { + fatal_usage("unknown task '$task'"); + } } elsif ($cmd eq 'install' || $cmd eq 'unpack') { - my $required; - foreach my $arg (@ARGV) { - if ($arg =~ m/\.pkglist$/) { - open (TMP, $arg) || - die "cant open package list '$arg' - $!"; - while (defined (my $line = )) { - chomp $line; - next if $line =~ m/^\s*$/; - next if $line =~ m/\#/; - if ($line =~ m/^\s*(\S+)\s*$/) { - push @$required, $1; - } else { - die "invalid package name in '$arg' - $line\n"; - } - } - } else { - push @$required, $arg; - } - - close (TMP); - } - - $dab->install ($required, $cmd eq 'unpack'); + my $required; + foreach my $arg (@ARGV) { + if ($arg =~ m/\.pkglist$/) { + open(TMP, $arg) + || die "cant open package list '$arg' - $!"; + while (defined(my $line = )) { + chomp $line; + next if $line =~ m/^\s*$/; + next if $line =~ m/\#/; + if ($line =~ m/^\s*(\S+)\s*$/) { + push @$required, $1; + } else { + die "invalid package name in '$arg' - $line\n"; + } + } + } else { + push @$required, $arg; + } + + close(TMP); + } + + $dab->install($required, $cmd eq 'unpack'); } elsif ($cmd eq 'exec') { - $dab->ve_exec (@ARGV); + $dab->ve_exec(@ARGV); } elsif ($cmd eq 'enter') { - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; - $dab->enter(); + $dab->enter(); } elsif ($cmd eq 'clean') { - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; - $dab->cleanup(0); + $dab->cleanup(0); } elsif ($cmd eq 'dist-clean') { - die "command '$cmd' expects no arguments.\n" if scalar (@ARGV) != 0; + die "command '$cmd' expects no arguments.\n" if scalar(@ARGV) != 0; - $dab->cleanup(1); + $dab->cleanup(1); } else { - fatal_usage("invalid command '$cmd'"); + fatal_usage("invalid command '$cmd'"); } }; if (my $err = $@) { - $dab->logmsg ($@); - die ($@); + $dab->logmsg($@); + die($@); } exit 0; @@ -493,7 +502,7 @@ characters long and must start with an alphanumeric character. Target architecture. -=item B I +=item B I The version number of an appliance. -- 2.52.0 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel