use IPC::Open3;
use UUID;
use Cwd;
-
-my @BASE_PACKAGES = qw(base openssh);
-my @BASE_EXCLUDES = qw(e2fsprogs
- jfsutils
- linux
- linux-firmware
- lvm2
- mdadm
- netctl
- pcmciautils
- reiserfsprogs
- xfsprogs);
+my @BASE_PACKAGES = qw(base openssh vi nano);
+my @BASE_EXCLUDES = qw(
+ e2fsprogs
+ jfsutils
+ linux
+ linux-firmware
+ lvm2
+ mdadm
+ netctl
+ pcmciautils
+ reiserfsprogs
+ xfsprogs
+);
my $PKGDIR = "/var/cache/pacman/pkg";
$fh->close;
}
+sub read_file {
+ my ($filename) = @_;
+
+ my $fh = IO::File->new ("<$filename") or die "failed to read $filename - $!\n";
+ my $rec = '';
+ while (defined (my $line = <$fh>)) {
+ $rec .= $line;
+ };
+ return $rec;
+}
+
sub copy_file {
my ($a, $b) = @_;
copy($a, $b) or die "failed to copy $a => $b: $!";
die "no sources/mirrors specified";
}
+ $self->write_pacman_conf();
+
+ $self->logmsg("configured VE $self->{veid}\n");
+}
+
+sub write_pacman_conf {
+ my ($self, $config_fn, $siglevel) = @_;
+
+ my $config = $self->{config};
+
$config->{source} //= [];
$config->{mirror} //= [];
- my $servers = "Server = "
- . join("\nServer = ", @{$config->{source}}, @{$config->{mirror}})
- . "\n";
+ $siglevel ||= "Never";
+ $config_fn ||= $self->{'pacman.conf'};
+
+ my $servers = "Server = ".join("\nServer = ", @{$config->{source}}, @{$config->{mirror}}) ."\n";
+
+ my $fh = IO::File->new($config_fn, O_WRONLY | O_CREAT | O_EXCL)
+ or die "unable to write pacman config file $self->{'pacman.conf'} - $!";
- $fh = IO::File->new($self->{'pacman.conf'}, O_WRONLY|O_CREAT|O_EXCL) ||
- die "unable to write pacman config file $self->{'pacman.conf'} - $!";
my $arch = $config->{architecture};
$arch = 'x86_64' if $arch eq 'amd64';
+
print $fh <<"EOF";
[options]
HoldPkg = pacman glibc
Architecture = $arch
CheckSpace
-SigLevel = Never
+SigLevel = $siglevel
[core]
$servers
$servers
EOF
- if ($config->{architecture} eq 'x86_64') {
- print $fh "[multilib]\n$servers\n";
- }
+ print $fh "[multilib]\n$servers\n" if $config->{architecture} eq 'x86_64';
- $self->logmsg("configured VE $self->{veid}\n");
+ close($fh);
}
sub ve_status {
}
sub run_command {
- my ($self, $cmd, $input, $getoutput) = @_;
+ my ($self, $cmd, $input, $getoutput, $noerr) = @_;
my $reader = IO::File->new();
my $writer = IO::File->new();
waitpid ($pid, 0);
my $ec = ($? >> 8);
- die "command '$cmdstr' failed with exit code $ec\n" if $ec;
+ die "command '$cmdstr' failed with exit code $ec\n" if $ec && !$noerr;
- return $res;
+ return wantarray ? ($res, $ec) : $res;
}
sub start_container {
}
sub pacman_command {
- my ($self) = @_;
+ my ($self, $config_fn) = @_;
my $root = $self->{rootfs};
- return ('/usr/bin/pacman',
- '--root', $root,
- '--config', $self->{'pacman.conf'},
- '--cachedir', $self->{pkgcache},
- '--noconfirm');
+ return (
+ '/usr/bin/pacman',
+ '--root', $root,
+ '--config', $config_fn || $self->{'pacman.conf'},
+ '--cachedir', $self->{pkgcache},
+ '--noconfirm',
+ );
}
sub cache_packages {
my ($self, $packages) = @_;
my $root = $self->{rootfs};
- my @pacman = $self->pacman_command();
- $self->run_command([@pacman, '-Sw', '--', @$packages]);
+ $self->write_pacman_conf('pacman.caching.conf', "Optional");
+ my @pacman = $self->pacman_command('pacman.caching.conf');
+ my ($_res, $ec) = $self->run_command([@pacman, '-Sw', '--', @$packages], undef, undef, 1);
+ $self->logmsg("ignore bad exit $ec due to unavailable keyring, the CT will verify that later.\n")
+ if $ec;
+}
+
+sub mask_systemd_unit {
+ my ($self, $unit) = @_;
+ my $root = $self->{rootfs};
+ symln '/dev/null', "$root/etc/systemd/system/$unit";
}
sub bootstrap {
print "Installing packages...\n";
$self->ve_command(['pacman', '-S', '--needed', '--noconfirm', '--', @$packages]);
+
+ print "Masking problematic systemd units...\n";
+ for my $unit (qw(sys-kernel-config.mount sys-kernel-debug.mount systemd-journald-audit.socket)) {
+ $self->mask_systemd_unit($unit);
+ }
}
# devices needed for gnupg to function:
}
sub finalize {
- my ($self) = @_;
+ my ($self, $compressor) = @_;
+
+ my $use_zstd = 1;
+ if (defined($compressor)) {
+ if ($compressor =~ /^\s*--zstd?\s*$/) {
+ $use_zstd = 1;
+ } elsif ($compressor =~ /^\s*--(?:gz|gzip)\s*$/) {
+ $use_zstd = 0; # just boolean for now..
+ } else {
+ die "finalize: unknown compressor '$compressor'!\n";
+ }
+ }
+
my $rootdir = $self->{rootfs};
print "Stopping container...\n";
unlink $file;
rename_file($file.'.aab_orig', $file);
+ # experienced user can change it anytime and others do well to start out with an updatable system..
+ my $mirrors = eval { read_file($file) } // '';
+ $mirrors = "\nServer = https://geo.mirror.pkgbuild.com/\$repo/os/\$arch\n\n" . $mirrors;
+ write_file($mirrors, $file, 0644);
+
print "Removing weak temporary pacman keyring...\n";
rmtree("$rootdir/etc/pacman.d/gnupg");
} else {
die "unable to detect size\n";
}
- $self->logmsg ("$size MB\n");
+ $self->logmsg ("uncompressed size: $size MB\n");
$self->write_config ("$rootdir/etc/appliance.info", $size);
$self->logmsg ("creating final appliance archive\n");
+ my $compressor_ext = $use_zstd ? 'zst' : 'gz';
+
my $target = "$self->{targetname}.tar";
unlink $target;
- unlink "$target.gz";
+ unlink "$target.$compressor_ext";
$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 ("gzip $target");
+
+ $self->logmsg ("compressing archive ($compressor_ext)\n");
+ if ($use_zstd) {
+ $self->run_command ("zstd -19 --rm $target");
+ } else {
+ $self->run_command ("gzip -9 $target");
+ }
+
+ my $target_size = int(-s "$target.$compressor_ext") >> 20;
+ $self->logmsg ("created '$target.$compressor_ext' with size: $target_size MB\n");
}
sub enter {
my ($self, $all) = @_;
unlink $self->{logfile};
- unlink $self->{'pacman.conf'};
+ unlink $self->{'pacman.conf'}, 'pacman.caching.conf';
$self->ve_destroy();
unlink '.veid';
unlink $self->{veconffile};