]> git.proxmox.com Git - aab.git/blame - PVE/AAB.pm
mask systemd-journald-audit.socket
[aab.git] / PVE / AAB.pm
CommitLineData
7b25f331
WB
1package PVE::AAB;
2
3use strict;
4use warnings;
5
6use File::Path;
7use File::Copy;
8use IO::File;
9use IO::Select;
7c20fd82 10use IPC::Open2;
7b25f331
WB
11use IPC::Open3;
12use UUID;
13use Cwd;
b36f9145 14my @BASE_PACKAGES = qw(base openssh vi nano);
6bd339d3
TL
15my @BASE_EXCLUDES = qw(
16 e2fsprogs
17 jfsutils
18 linux
19 linux-firmware
20 lvm2
21 mdadm
22 netctl
23 pcmciautils
24 reiserfsprogs
25 xfsprogs
26);
7b25f331
WB
27
28my $PKGDIR = "/var/cache/pacman/pkg";
29
30my ($aablibdir, $fake_init);
31
32sub setup_defaults($) {
33 my ($dir) = @_;
34 $aablibdir = $dir;
35 $fake_init = "$aablibdir/scripts/init.bash";
36}
37
38setup_defaults('/usr/lib/aab');
39
40sub write_file {
41 my ($data, $file, $perm) = @_;
42
43 die "no filename" if !$file;
44 unlink $file;
45
46 my $fh = IO::File->new ($file, O_WRONLY | O_CREAT, $perm) ||
47 die "unable to open file '$file'";
48
49 print $fh $data;
50 $fh->close;
51}
52
03406f3e
TL
53sub read_file {
54 my ($filename) = @_;
55
56 my $fh = IO::File->new ("<$filename") or die "failed to read $filename - $!\n";
57 my $rec = '';
58 while (defined (my $line = <$fh>)) {
59 $rec .= $line;
60 };
61 return $rec;
62}
63
7b25f331
WB
64sub copy_file {
65 my ($a, $b) = @_;
66 copy($a, $b) or die "failed to copy $a => $b: $!";
67}
68
69sub rename_file {
70 my ($a, $b) = @_;
71 rename($a, $b) or die "failed to rename $a => $b: $!";
72}
73
74sub symln {
75 my ($a, $b) = @_;
76 symlink($a, $b) or die "failed to symlink $a => $b: $!";
77}
78
79sub logmsg {
80 my $self = shift;
81 print STDERR @_;
82 $self->writelog (@_);
83}
84
85sub writelog {
86 my $self = shift;
87 my $fd = $self->{logfd};
88 print $fd @_;
89}
90
91sub read_config {
92 my ($filename) = @_;
93
94 my $res = {};
95
96 my $fh = IO::File->new ("<$filename") || return $res;
97 my $rec = '';
98
99 while (defined (my $line = <$fh>)) {
100 next if $line =~ m/^\#/;
101 next if $line =~ m/^\s*$/;
102 $rec .= $line;
103 };
104
105 close ($fh);
106
107 chomp $rec;
108 $rec .= "\n";
109
110 while ($rec) {
111 if ($rec =~ s/^Description:\s*([^\n]*)(\n\s+.*)*$//si) {
112 $res->{headline} = $1;
113 chomp $res->{headline};
114 my $long = $2;
115 $long =~ s/^\s+/ /;
116 $res->{description} = $long;
b8f914c1 117 chomp $res->{description};
7b25f331
WB
118 } elsif ($rec =~ s/^([^:]+):\s*(.*\S)\s*\n//) {
119 my ($key, $value) = (lc ($1), $2);
120 if ($key eq 'source' || $key eq 'mirror') {
121 push @{$res->{$key}}, $value;
122 } else {
123 die "duplicate key '$key'\n" if defined ($res->{$key});
124 $res->{$key} = $value;
125 }
126 } else {
127 die "unable to parse config file: $rec";
128 }
129 }
130
131 die "unable to parse config file" if $rec;
132
0cff4ef1
WB
133 $res->{architecture} = 'amd64' if $res->{architecture} eq 'x86_64';
134
7b25f331
WB
135 return $res;
136}
137
138sub new {
139 my ($class, $config) = @_;
140
141 $config = read_config ('aab.conf') if !$config;
142 my $version = $config->{version};
143 die "no 'version' specified\n" if !$version;
144 die "no 'section' specified\n" if !$config->{section};
145 die "no 'description' specified\n" if !$config->{headline};
146 die "no 'maintainer' specified\n" if !$config->{maintainer};
147
148 my $name = $config->{name} || die "no 'name' specified\n";
b8f914c1 149 $name =~ m/^[a-z][0-9a-z\-\*\.]+$/ ||
7b25f331
WB
150 die "illegal characters in name '$name'\n";
151
152 my $targetname;
153 if ($name =~ m/^archlinux/) {
154 $targetname = "${name}_${version}_$config->{architecture}";
155 } else {
156 $targetname = "archlinux-${name}_${version}_$config->{architecture}";
157 }
158
159 my $self = { logfile => 'logfile',
160 config => $config,
161 targetname => $targetname,
162 incl => [@BASE_PACKAGES],
163 excl => [@BASE_EXCLUDES],
164 };
165
166 $self->{logfd} = IO::File->new($self->{logfile}, O_WRONLY | O_APPEND | O_CREAT)
167 or die "unable to open log file";
168
169 bless $self, $class;
170
171 $self->__allocate_ve();
172
173 return $self;
174}
175
176sub __sample_config {
177 my ($self) = @_;
178
179 my $arch = $self->{config}->{architecture};
180
181 return <<"CFG";
182lxc.arch = $arch
183lxc.include = /usr/share/lxc/config/archlinux.common.conf
87fbfb3a
TL
184lxc.uts.name = localhost
185lxc.rootfs.path = $self->{rootfs}
7b25f331
WB
186lxc.mount.entry = $self->{pkgcache} $self->{pkgdir} none bind 0 0
187CFG
188}
189
190sub __allocate_ve {
191 my ($self) = @_;
192
193 my $cid;
194 if (my $fd = IO::File->new(".veid")) {
195 $cid = <$fd>;
196 chomp $cid;
197 close ($fd);
198 }
199
200
201 $self->{working_dir} = getcwd;
202 $self->{veconffile} = "$self->{working_dir}/config";
203 $self->{rootfs} = "$self->{working_dir}/rootfs";
204 $self->{pkgdir} = "$self->{working_dir}/rootfs/$PKGDIR";
205 $self->{pkgcache} = "$self->{working_dir}/pkgcache";
206 $self->{'pacman.conf'} = "$self->{working_dir}/pacman.conf";
207
208 if ($cid) {
209 $self->{veid} = $cid;
210 return $cid;
211 }
212
213 my $uuid;
214 my $uuid_str;
215 UUID::generate($uuid);
216 UUID::unparse($uuid, $uuid_str);
217 $self->{veid} = $uuid_str;
218
219 my $fd = IO::File->new (">.veid") ||
220 die "unable to write '.veid'\n";
221 print $fd "$self->{veid}\n";
222 close ($fd);
223 $self->logmsg("allocated VE $self->{veid}\n");
224}
225
226sub initialize {
227 my ($self) = @_;
228
229 my $config = $self->{config};
230
231 $self->{logfd} = IO::File->new($self->{logfile}, O_WRONLY | O_TRUNC | O_CREAT)
232 or die "unable to open log file";
233
234 my $cdata = $self->__sample_config();
235
236 my $fh = IO::File->new($self->{veconffile}, O_WRONLY|O_CREAT|O_EXCL) ||
237 die "unable to write lxc config file '$self->{veconffile}' - $!";
238 print $fh $cdata;
239 close ($fh);
240
241 if (!$config->{source} && !$config->{mirror}) {
242 die "no sources/mirrors specified";
243 }
244
ae71b49f
TL
245 $self->write_pacman_conf();
246
247 $self->logmsg("configured VE $self->{veid}\n");
248}
249
250sub write_pacman_conf {
fbec3675 251 my ($self, $config_fn, $siglevel) = @_;
ae71b49f
TL
252
253 my $config = $self->{config};
254
7b25f331
WB
255 $config->{source} //= [];
256 $config->{mirror} //= [];
257
fbec3675
TL
258 $siglevel ||= "Never";
259 $config_fn ||= $self->{'pacman.conf'};
260
ae71b49f
TL
261 my $servers = "Server = ".join("\nServer = ", @{$config->{source}}, @{$config->{mirror}}) ."\n";
262
fbec3675 263 my $fh = IO::File->new($config_fn, O_WRONLY | O_CREAT | O_EXCL)
ae71b49f 264 or die "unable to write pacman config file $self->{'pacman.conf'} - $!";
7b25f331 265
0cff4ef1
WB
266 my $arch = $config->{architecture};
267 $arch = 'x86_64' if $arch eq 'amd64';
ae71b49f 268
7b25f331
WB
269 print $fh <<"EOF";
270[options]
271HoldPkg = pacman glibc
0cff4ef1 272Architecture = $arch
7b25f331 273CheckSpace
fbec3675 274SigLevel = $siglevel
7b25f331
WB
275
276[core]
277$servers
278[extra]
279$servers
280[community]
281$servers
7b25f331
WB
282EOF
283
ae71b49f 284 print $fh "[multilib]\n$servers\n" if $config->{architecture} eq 'x86_64';
4eaaed91 285
ae71b49f 286 close($fh);
7b25f331
WB
287}
288
289sub ve_status {
290 my ($self) = @_;
291
292 my $veid = $self->{veid};
293
294 my $res = { running => 0 };
295
296 $res->{exist} = 1 if -d "$self->{rootfs}/usr";
297
298 my $filename = "/proc/net/unix";
299
300 # similar test is used by lcxcontainers.c: list_active_containers
301 my $fh = IO::File->new ($filename, "r");
302 return $res if !$fh;
303
304 while (defined(my $line = <$fh>)) {
305 if ($line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/) {
306 my $path = $1;
307 if ($path =~ m!^@/\S+/$veid/command$!) {
308 $res->{running} = 1;
309 }
310 }
311 }
312 close($fh);
b8f914c1 313
7b25f331
WB
314 return $res;
315}
316
317sub ve_destroy {
318 my ($self) = @_;
319
320 my $veid = $self->{veid}; # fixme
321
322 my $vestat = $self->ve_status();
323 if ($vestat->{running}) {
324 $self->stop_container();
325 }
326
327 rmtree $self->{rootfs};
328 unlink $self->{veconffile};
329}
330
331sub ve_init {
332 my ($self) = @_;
333
334
335 my $veid = $self->{veid};
d43d058d 336 my $conffile = $self->{veconffile};
7b25f331
WB
337
338 $self->logmsg ("initialize VE $veid\n");
339
340 my $vestat = $self->ve_status();
341 if ($vestat->{running}) {
d43d058d 342 $self->run_command ("lxc-stop -n $veid --rcfile $conffile --kill");
b8f914c1 343 }
7b25f331
WB
344
345 rmtree $self->{rootfs};
01756eba 346 mkpath "$self->{rootfs}/dev";
7b25f331
WB
347}
348
349sub ve_command {
350 my ($self, $cmd, $input) = @_;
351
352 my $veid = $self->{veid};
d43d058d 353 my $conffile = $self->{veconffile};
7b25f331
WB
354
355 if (ref ($cmd) eq 'ARRAY') {
d43d058d 356 unshift @$cmd, 'lxc-attach', '-n', $veid, '--rcfile', $conffile,'--clear-env', '--';
7b25f331
WB
357 $self->run_command ($cmd, $input);
358 } else {
d43d058d 359 $self->run_command ("lxc-attach -n $veid --rcfile $conffile --clear-env -- $cmd", $input);
7b25f331
WB
360 }
361}
362
363sub ve_exec {
364 my ($self, @cmd) = @_;
365
366 my $veid = $self->{veid};
d43d058d 367 my $conffile = $self->{veconffile};
7b25f331
WB
368
369 my $reader;
d43d058d 370 my $pid = open2($reader, "<&STDIN", 'lxc-attach', '-n', $veid, '--rcfile', $conffile, '--', @cmd)
7b25f331
WB
371 or die "unable to exec command";
372
373 while (defined (my $line = <$reader>)) {
374 $self->logmsg ($line);
375 }
376
377 waitpid ($pid, 0);
378 my $rc = $? >> 8;
379
380 die "ve_exec failed - status $rc\n" if $rc != 0;
381}
382
383sub run_command {
03483500 384 my ($self, $cmd, $input, $getoutput, $noerr) = @_;
7b25f331
WB
385
386 my $reader = IO::File->new();
387 my $writer = IO::File->new();
388 my $error = IO::File->new();
389
390 my $orig_pid = $$;
391
392 my $cmdstr = ref ($cmd) eq 'ARRAY' ? join (' ', @$cmd) : $cmd;
393
394 my $pid;
395 eval {
396 if (ref ($cmd) eq 'ARRAY') {
397 $pid = open3 ($writer, $reader, $error, @$cmd) || die $!;
398 } else {
399 $pid = open3 ($writer, $reader, $error, $cmdstr) || die $!;
400 }
401 };
402
403 my $err = $@;
404
405 # catch exec errors
406 if ($orig_pid != $$) {
407 $self->logmsg ("ERROR: command '$cmdstr' failed - fork failed\n");
b8f914c1
OB
408 POSIX::_exit (1);
409 kill ('KILL', $$);
7b25f331
WB
410 }
411
412 die $err if $err;
413
414 print $writer $input if defined $input;
415 close $writer;
416
417 my $select = new IO::Select;
418 $select->add ($reader);
419 $select->add ($error);
420
421 my $res = '';
422 my $logfd = $self->{logfd};
423
424 while ($select->count) {
425 my @handles = $select->can_read ();
426
427 foreach my $h (@handles) {
428 my $buf = '';
429 my $count = sysread ($h, $buf, 4096);
430 if (!defined ($count)) {
431 waitpid ($pid, 0);
432 die "command '$cmdstr' failed: $!";
433 }
434 $select->remove ($h) if !$count;
435
436 print $logfd $buf;
437
438 $res .= $buf if $getoutput;
439 }
440 }
441
442 waitpid ($pid, 0);
443 my $ec = ($? >> 8);
444
03483500 445 die "command '$cmdstr' failed with exit code $ec\n" if $ec && !$noerr;
7b25f331 446
03483500 447 return wantarray ? ($res, $ec) : $res;
7b25f331
WB
448}
449
450sub start_container {
451 my ($self) = @_;
452 my $veid = $self->{veid};
453 $self->run_command(['lxc-start', '-n', $veid, '-f', $self->{veconffile}, '/usr/bin/aab_fake_init']);
454}
455
456sub stop_container {
457 my ($self) = @_;
458 my $veid = $self->{veid};
d43d058d
WB
459 my $conffile = $self->{veconffile};
460 $self->run_command ("lxc-stop -n $veid --rcfile $conffile --kill");
7b25f331
WB
461}
462
463sub pacman_command {
fbec3675 464 my ($self, $config_fn) = @_;
7b25f331 465 my $root = $self->{rootfs};
fbec3675
TL
466 return (
467 '/usr/bin/pacman',
468 '--root', $root,
469 '--config', $config_fn || $self->{'pacman.conf'},
470 '--cachedir', $self->{pkgcache},
471 '--noconfirm',
472 );
7b25f331
WB
473}
474
475sub cache_packages {
476 my ($self, $packages) = @_;
477 my $root = $self->{rootfs};
478
f29113a8
TL
479 $self->write_pacman_conf('pacman.caching.conf', "Optional");
480 my @pacman = $self->pacman_command('pacman.caching.conf');
481 my ($_res, $ec) = $self->run_command([@pacman, '-Sw', '--', @$packages], undef, undef, 1);
482 $self->logmsg("ignore bad exit $ec due to unavailable keyring, the CT will verify that later.\n")
483 if $ec;
7b25f331
WB
484}
485
2d2bee91
TL
486sub mask_systemd_unit {
487 my ($self, $unit) = @_;
488 my $root = $self->{rootfs};
489 symln '/dev/null', "$root/etc/systemd/system/$unit";
490}
491
7b25f331
WB
492sub bootstrap {
493 my ($self, $include, $exclude) = @_;
494 my $root = $self->{rootfs};
495
496 my @pacman = $self->pacman_command();
497
498 print "Fetching package database...\n";
499 mkpath $self->{pkgcache};
500 mkpath $self->{pkgdir};
501 mkpath "$root/var/lib/pacman";
b8f914c1 502 $self->run_command([@pacman, '-Syy']);
7b25f331
WB
503
504 print "Figuring out what to install...\n";
505 my $incl = { map { $_ => 1 } @{$self->{incl}} };
506 my $excl = { map { $_ => 1 } @{$self->{excl}} };
507
508 foreach my $addinc (@$include) {
509 $incl->{$addinc} = 1;
510 delete $excl->{$addinc};
511 }
512 foreach my $addexc (@$exclude) {
513 $excl->{$addexc} = 1;
514 delete $incl->{$addexc};
515 }
516
517 my $expand = sub {
518 my ($lst) = @_;
519 foreach my $inc (keys %$lst) {
520 my $group;
521 eval { $group = $self->run_command([@pacman, '-Sqg', $inc], undef, 1); };
522 if ($group && !$@) {
523 # add the group
524 delete $lst->{$inc};
525 $lst->{$_} = 1 foreach split(/\s+/, $group);
526 }
527 }
528 };
529
530 $expand->($incl);
531 $expand->($excl);
532
533 my $packages = [ grep { !$excl->{$_} } keys %$incl ];
534
535 print "Setting up basic environment...\n";
536 mkpath "$root/etc";
537 mkpath "$root/usr/bin";
538
539 my $data = "# UNCONFIGURED FSTAB FOR BASE SYSTEM\n";
540 write_file ($data, "$root/etc/fstab", 0644);
541
542 write_file ("", "$root/etc/resolv.conf", 0644);
543 write_file("localhost\n", "$root/etc/hostname", 0644);
544 $self->run_command(['install', '-m0755', $fake_init, "$root/usr/bin/aab_fake_init"]);
545
546 unlink "$root/etc/localtime";
547 symln '/usr/share/zoneinfo/UTC', "$root/etc/localtime";
548
549 print "Caching packages...\n";
550 $self->cache_packages($packages);
551 #$self->copy_packages();
552
01756eba
SI
553 print "Creating device nodes for package manager...\n";
554 $self->create_dev();
555
7b25f331
WB
556 print "Installing package manager and essentials...\n";
557 # inetutils for 'hostname' for our init
558 $self->run_command([@pacman, '-S', 'pacman', 'inetutils', 'archlinux-keyring']);
559
560 print "Setting up pacman for installation from cache...\n";
561 my $file = "$root/etc/pacman.d/mirrorlist";
562 my $backup = "${file}.aab_orig";
563 if (!-f $backup) {
564 rename_file($file, $backup);
565 write_file("Server = file://$PKGDIR\n", $file);
566 }
567
568 print "Populating keyring...\n";
766f0fa3 569 $self->populate_keyring();
7b25f331 570
01756eba
SI
571 print "Removing device nodes...\n";
572 $self->cleanup_dev();
573
7b25f331
WB
574 print "Starting container...\n";
575 $self->start_container();
576
577 print "Installing packages...\n";
578 $self->ve_command(['pacman', '-S', '--needed', '--noconfirm', '--', @$packages]);
2d2bee91
TL
579
580 print "Masking problematic systemd units...\n";
acef423b 581 for my $unit (qw(sys-kernel-config.mount sys-kernel-debug.mount systemd-journald-audit.socket)) {
2d2bee91
TL
582 $self->mask_systemd_unit($unit);
583 }
7b25f331
WB
584}
585
01756eba
SI
586# devices needed for gnupg to function:
587my $devs = {
588 '/dev/null' => ['c', '1', '3'],
589 '/dev/random' => ['c', '1', '9'], # fake /dev/random (really urandom)
590 '/dev/urandom' => ['c', '1', '9'],
591 '/dev/tty' => ['c', '5', '0'],
592};
593
594sub cleanup_dev {
766f0fa3
WB
595 my ($self) = @_;
596 my $root = $self->{rootfs};
597
01756eba
SI
598 # remove temporary device files
599 unlink "${root}$_" foreach keys %$devs;
600}
766f0fa3 601
01756eba
SI
602sub create_dev {
603 my ($self) = @_;
604 my $root = $self->{rootfs};
605
606 local $SIG{INT} = $SIG{TERM} = sub { $self->cleanup_dev; };
766f0fa3 607
01756eba
SI
608 # we want to replace /dev/random, so delete devices first
609 $self->cleanup_dev();
766f0fa3
WB
610
611 foreach my $dev (keys %$devs) {
612 my ($type, $major, $minor) = @{$devs->{$dev}};
613 system('mknod', "${root}${dev}", $type, $major, $minor);
614 }
01756eba
SI
615}
616
617sub populate_keyring {
618 my ($self) = @_;
619 my $root = $self->{rootfs};
766f0fa3
WB
620
621 # generate weak master key and populate the keyring
622 system('unshare', '--fork', '--pid', 'chroot', "$root", 'pacman-key', '--init') == 0
623 or die "failed to initialize keyring: $?";
624 system('unshare', '--fork', '--pid', 'chroot', "$root", 'pacman-key', '--populate') == 0
625 or die "failed to populate keyring: $?";
626
766f0fa3
WB
627}
628
7b25f331
WB
629sub install {
630 my ($self, $pkglist) = @_;
631
632 $self->cache_packages($pkglist);
633 $self->ve_command(['pacman', '-S', '--needed', '--noconfirm', '--', @$pkglist]);
634}
635
636sub write_config {
637 my ($self, $filename, $size) = @_;
638
639 my $config = $self->{config};
640
641 my $data = '';
642
643 $data .= "Name: $config->{name}\n";
644 $data .= "Version: $config->{version}\n";
645 $data .= "Type: lxc\n";
646 $data .= "OS: archlinux\n";
647 $data .= "Section: $config->{section}\n";
648 $data .= "Maintainer: $config->{maintainer}\n";
649 $data .= "Architecture: $config->{architecture}\n";
b65bfe8c 650 $data .= "Infopage: https://www.archlinux.org\n";
7b25f331
WB
651 $data .= "Installed-Size: $size\n";
652
653 # optional
654 $data .= "Infopage: $config->{infopage}\n" if $config->{infopage};
655 $data .= "ManageUrl: $config->{manageurl}\n" if $config->{manageurl};
656 $data .= "Certified: $config->{certified}\n" if $config->{certified};
657
658 # description
659 $data .= "Description: $config->{headline}\n";
660 $data .= "$config->{description}\n" if $config->{description};
661
662 write_file ($data, $filename, 0644);
663}
664
665sub finalize {
b867882a
TL
666 my ($self, $compressor) = @_;
667
668 my $use_zstd = 1;
669 if (defined($compressor)) {
670 if ($compressor =~ /^\s*--zstd?\s*$/) {
671 $use_zstd = 1;
672 } elsif ($compressor =~ /^\s*--(?:gz|gzip)\s*$/) {
673 $use_zstd = 0; # just boolean for now..
674 } else {
903d29f8 675 die "finalize: unknown compressor '$compressor'!\n";
b867882a
TL
676 }
677 }
678
7b25f331
WB
679 my $rootdir = $self->{rootfs};
680
681 print "Stopping container...\n";
682 $self->stop_container();
683
684 print "Rolling back mirrorlist changes...\n";
685 my $file = "$rootdir/etc/pacman.d/mirrorlist";
686 unlink $file;
687 rename_file($file.'.aab_orig', $file);
688
03406f3e
TL
689 # experienced user can change it anytime and others do well to start out with an updatable system..
690 my $mirrors = eval { read_file($file) } // '';
691 $mirrors = "\nServer = https://geo.mirror.pkgbuild.com/\$repo/os/\$arch\n\n" . $mirrors;
692 write_file($mirrors, $file, 0644);
693
e61d6533
WB
694 print "Removing weak temporary pacman keyring...\n";
695 rmtree("$rootdir/etc/pacman.d/gnupg");
696
7b25f331
WB
697 my $sizestr = $self->run_command("du -sm $rootdir", undef, 1);
698 my $size;
699 if ($sizestr =~ m/^(\d+)\s+\Q$rootdir\E$/) {
700 $size = $1;
701 } else {
702 die "unable to detect size\n";
703 }
18ef2f8c 704 $self->logmsg ("uncompressed size: $size MB\n");
7b25f331
WB
705
706 $self->write_config ("$rootdir/etc/appliance.info", $size);
707
708 $self->logmsg ("creating final appliance archive\n");
709
903d29f8 710 my $compressor_ext = $use_zstd ? 'zst' : 'gz';
b867882a 711
7b25f331
WB
712 my $target = "$self->{targetname}.tar";
713 unlink $target;
b867882a 714 unlink "$target.$compressor_ext";
7b25f331
WB
715
716 $self->run_command ("tar cpf $target --numeric-owner -C '$rootdir' ./etc/appliance.info");
717 $self->run_command ("tar rpf $target --numeric-owner -C '$rootdir' --exclude ./etc/appliance.info .");
18ef2f8c 718
b867882a
TL
719 $self->logmsg ("compressing archive ($compressor_ext)\n");
720 if ($use_zstd) {
721 $self->run_command ("zstd -19 --rm $target");
722 } else {
723 $self->run_command ("gzip -9 $target");
724 }
18ef2f8c 725
b867882a
TL
726 my $target_size = int(-s "$target.$compressor_ext") >> 20;
727 $self->logmsg ("created '$target.$compressor_ext' with size: $target_size MB\n");
7b25f331
WB
728}
729
730sub enter {
731 my ($self) = @_;
732 my $veid = $self->{veid};
d43d058d 733 my $conffile = $self->{veconffile};
7b25f331
WB
734
735 my $vestat = $self->ve_status();
736 if (!$vestat->{exist}) {
737 $self->logmsg ("Please create the appliance first (bootstrap)");
738 return;
739 }
740
741 if (!$vestat->{running}) {
742 $self->start_container();
743 }
744
d43d058d 745 system ("lxc-attach -n $veid --rcfile $conffile --clear-env");
7b25f331
WB
746}
747
748sub clean {
749 my ($self, $all) = @_;
750
751 unlink $self->{logfile};
f29113a8 752 unlink $self->{'pacman.conf'}, 'pacman.caching.conf';
7b25f331
WB
753 $self->ve_destroy();
754 unlink '.veid';
dfb4fbaa
SI
755 unlink $self->{veconffile};
756
7b25f331
WB
757 rmtree $self->{pkgcache} if $all;
758}
759
7601;