mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
Remove the tools.
These have now moved to a new repository. Provisionally it is here: https://github.com/rwmjones/guestfs-tools/ but this is not the final location, as it will eventually be hosted on gitlab.com. The tarballs are here: https://download.libguestfs.org/guestfs-tools/
This commit is contained in:
97
.gitignore
vendored
97
.gitignore
vendored
@@ -35,9 +35,6 @@ Makefile.in
|
||||
/.sc-*
|
||||
|
||||
/aclocal.m4
|
||||
/align/stamp-virt-alignment-scan.pod
|
||||
/align/virt-alignment-scan
|
||||
/align/virt-alignment-scan.1
|
||||
/appliance/guestfsd.deps
|
||||
/appliance/libguestfs-make-fixed-appliance
|
||||
/appliance/libguestfs-make-fixed-appliance.1
|
||||
@@ -83,45 +80,7 @@ Makefile.in
|
||||
/build-aux/snippet/
|
||||
/build-aux/test-driver
|
||||
/build-aux/ylwrap
|
||||
/builder/.depend
|
||||
/builder/*.img
|
||||
/builder/index-parse.c
|
||||
/builder/index-parse.h
|
||||
/builder/index_parser_tests
|
||||
/builder/index-scan.c
|
||||
/builder/libguestfs.conf
|
||||
/builder/opensuse.conf
|
||||
/builder/osinfo_config.ml
|
||||
/builder/oUnit-*
|
||||
/builder/*.out
|
||||
/builder/*.qcow2
|
||||
/builder/repository-testdata
|
||||
/builder/stamp-virt-builder.pod
|
||||
/builder/stamp-virt-builder-repository.pod
|
||||
/builder/stamp-virt-index-validate.pod
|
||||
/builder/test-config/virt-builder/repos.d/test-index.conf
|
||||
/builder/test-console-*.sh
|
||||
/builder/test-simplestreams/virt-builder/repos.d/cirros.conf
|
||||
/builder/test-website/virt-builder/repos.d/libguestfs.conf
|
||||
/builder/virt-builder
|
||||
/builder/virt-builder-repository
|
||||
/builder/virt-builder.1
|
||||
/builder/virt-builder-repository.1
|
||||
/builder/virt-index-validate
|
||||
/builder/virt-index-validate.1
|
||||
/builder/*.xz
|
||||
/bundled/ocaml-augeas/.depend
|
||||
/cat/stamp-virt-*.pod
|
||||
/cat/virt-cat
|
||||
/cat/virt-cat.1
|
||||
/cat/virt-filesystems
|
||||
/cat/virt-filesystems.1
|
||||
/cat/virt-log
|
||||
/cat/virt-log.1
|
||||
/cat/virt-ls
|
||||
/cat/virt-ls.1
|
||||
/cat/virt-tail
|
||||
/cat/virt-tail.1
|
||||
/ChangeLog
|
||||
/compile
|
||||
/config.cache
|
||||
@@ -134,14 +93,6 @@ Makefile.in
|
||||
/config.sub
|
||||
/configure
|
||||
/csharp/Libguestfs.cs
|
||||
/customize/.depend
|
||||
/customize/customize_cmdline.ml
|
||||
/customize/customize_cmdline.mli
|
||||
/customize/stamp-virt-customize.pod
|
||||
/customize/test-password-*.sh
|
||||
/customize/test-settings-*.sh
|
||||
/customize/virt-customize
|
||||
/customize/virt-customize.1
|
||||
/daemon/.depend
|
||||
/daemon/actions.h
|
||||
/daemon/blkid.mli
|
||||
@@ -186,17 +137,6 @@ Makefile.in
|
||||
/daemon/stubs.h
|
||||
/daemon/types.ml
|
||||
/depcomp
|
||||
/df/stamp-virt-df.pod
|
||||
/df/virt-df
|
||||
/df/virt-df.1
|
||||
/dib/.depend
|
||||
/dib/output_format_*.mli
|
||||
/dib/stamp-virt-dib.pod
|
||||
/dib/virt-dib
|
||||
/dib/virt-dib.1
|
||||
/diff/stamp-virt-diff.pod
|
||||
/diff/virt-diff
|
||||
/diff/virt-diff.1
|
||||
/docs/guestfs-building.1
|
||||
/docs/guestfs-faq.1
|
||||
/docs/guestfs-hacking.1
|
||||
@@ -216,9 +156,6 @@ Makefile.in
|
||||
/docs/stamp-guestfs-release-notes-*.pod
|
||||
/docs/stamp-guestfs-security.pod
|
||||
/docs/stamp-guestfs-testing.pod
|
||||
/edit/stamp-virt-*.pod
|
||||
/edit/virt-edit
|
||||
/edit/virt-edit.1
|
||||
/erlang/actions-?.c
|
||||
/erlang/actions.h
|
||||
/erlang/bindtests.erl
|
||||
@@ -270,9 +207,6 @@ Makefile.in
|
||||
/fish/virt-copy-out.1
|
||||
/fish/virt-tar-in.1
|
||||
/fish/virt-tar-out.1
|
||||
/format/stamp-virt-format.pod
|
||||
/format/virt-format
|
||||
/format/virt-format.1
|
||||
/fuse/guestmount
|
||||
/fuse/guestmount.1
|
||||
/fuse/guestunmount
|
||||
@@ -287,10 +221,6 @@ Makefile.in
|
||||
/generator/generator
|
||||
/generator/.pod2text.data*
|
||||
/generator/stamp-generator
|
||||
/get-kernel/.depend
|
||||
/get-kernel/stamp-virt-get-kernel.pod
|
||||
/get-kernel/virt-get-kernel
|
||||
/get-kernel/virt-get-kernel.1
|
||||
/.gitattributes
|
||||
/.git-module-status
|
||||
/gnulib
|
||||
@@ -312,10 +242,6 @@ Makefile.in
|
||||
/haskell/Guestfs050LVCreate
|
||||
/haskell/Guestfs.hs
|
||||
/include/guestfs.h
|
||||
/inspector/actual-*.xml
|
||||
/inspector/stamp-virt-inspector.pod
|
||||
/inspector/virt-inspector
|
||||
/inspector/virt-inspector.1
|
||||
/installcheck.sh
|
||||
/install-sh
|
||||
/java/actions-?.c
|
||||
@@ -366,9 +292,6 @@ Makefile.in
|
||||
/m4/ltsugar.m4
|
||||
/m4/ltversion.m4
|
||||
/maint.mk
|
||||
/make-fs/stamp-virt-make-fs.pod
|
||||
/make-fs/virt-make-fs
|
||||
/make-fs/virt-make-fs.1
|
||||
/missing
|
||||
/ocaml-dep.sh
|
||||
/ocaml-link.sh
|
||||
@@ -474,10 +397,6 @@ Makefile.in
|
||||
/rescue/stamp-virt-rescue.pod
|
||||
/rescue/virt-rescue
|
||||
/rescue/virt-rescue.1
|
||||
/resize/.depend
|
||||
/resize/stamp-virt-resize.pod
|
||||
/resize/virt-resize
|
||||
/resize/virt-resize.1
|
||||
/ruby/bindtests.rb
|
||||
/ruby/doc/site/api
|
||||
/ruby/examples/guestfs-ruby.3
|
||||
@@ -499,21 +418,7 @@ Makefile.in
|
||||
/rust/src/guestfs.rs
|
||||
/rust/target
|
||||
/run
|
||||
/sparsify/.depend
|
||||
/sparsify/stamp-virt-sparsify.pod
|
||||
/sparsify/virt-sparsify
|
||||
/sparsify/virt-sparsify.1
|
||||
/stamp-h1
|
||||
/sysprep/.depend
|
||||
/sysprep/stamp-script1.sh
|
||||
/sysprep/stamp-script2.sh
|
||||
/sysprep/stamp-script4.sh
|
||||
/sysprep/stamp-virt-sysprep.pod
|
||||
/sysprep/sysprep-extra-options.pod
|
||||
/sysprep/sysprep-operations.pod
|
||||
/sysprep/sysprep_operation_*.mli
|
||||
/sysprep/virt-sysprep
|
||||
/sysprep/virt-sysprep.1
|
||||
/tests/c-api/test-add-drive-opts
|
||||
/tests/c-api/test-add-libvirt-dom
|
||||
/tests/c-api/test-backend-settings
|
||||
@@ -592,8 +497,6 @@ Makefile.in
|
||||
/test-tool/libguestfs-test-tool.1
|
||||
/test-tool/libguestfs-test-tool-helper
|
||||
/test-tool/stamp-libguestfs-test-tool.pod
|
||||
/tools/stamp-virt-*.pod
|
||||
/tools/virt-*.1
|
||||
/website/download/builder/*.xz
|
||||
/website/*.html
|
||||
/website/README.txt
|
||||
|
||||
10
BUGS
10
BUGS
@@ -1,5 +1,5 @@
|
||||
NOTE: This file is automatically generated from "update-bugs.sh".
|
||||
Last updated: 2021-03-03
|
||||
Last updated: 2021-03-11
|
||||
|
||||
This contains a local list of the bugs that are open against
|
||||
libguestfs. Bugs are tracked in the Red Hat Bugzilla database
|
||||
@@ -623,6 +623,12 @@ Bugs in NEW or ASSIGNED state are open and waiting for someone to fix.
|
||||
1933640 NEW https://bugzilla.redhat.com/show_bug.cgi?id=1933640
|
||||
[Regression] lvcreate fails to wipe signatures again
|
||||
|
||||
1935647 NEW https://bugzilla.redhat.com/show_bug.cgi?id=1935647
|
||||
RFE: virt-builder debian-10 : Cannot (easily) update resolv.conf
|
||||
|
||||
1935753 NEW https://bugzilla.redhat.com/show_bug.cgi?id=1935753
|
||||
"Warning: Missing arginfo" - libguestfs needs porting for PHP 8
|
||||
|
||||
503134 ASSIGNED https://bugzilla.redhat.com/show_bug.cgi?id=503134
|
||||
guestfish's list splitting does not recognize internal quoting
|
||||
|
||||
@@ -644,7 +650,7 @@ Bugs in NEW or ASSIGNED state are open and waiting for someone to fix.
|
||||
1794518 ASSIGNED https://bugzilla.redhat.com/show_bug.cgi?id=1794518
|
||||
setfiles fails on file /etc/.ip with ext4 immutable bit set
|
||||
|
||||
(208 bugs)
|
||||
(210 bugs)
|
||||
|
||||
--------------------------------------------------
|
||||
Bugs in MODIFIED, POST or ON_QA state are fixed.
|
||||
|
||||
55
Makefile.am
55
Makefile.am
@@ -47,6 +47,7 @@ SUBDIRS += include lib docs examples
|
||||
SUBDIRS += common/mlutils
|
||||
SUBDIRS += bundled/ocaml-augeas
|
||||
SUBDIRS += common/mlpcre
|
||||
SUBDIRS += common/mlgettext
|
||||
if ENABLE_DAEMON
|
||||
SUBDIRS += daemon
|
||||
SUBDIRS += tests/daemon
|
||||
@@ -102,14 +103,8 @@ SUBDIRS += common/progress
|
||||
SUBDIRS += common/visit
|
||||
SUBDIRS += common/windows
|
||||
|
||||
# libguestfs-test-tool
|
||||
SUBDIRS += test-tool
|
||||
|
||||
# Guestfish.
|
||||
SUBDIRS += fish
|
||||
|
||||
# virt-tools in C.
|
||||
SUBDIRS += align cat diff df edit format inspector make-fs rescue
|
||||
# Small tools written in C.
|
||||
SUBDIRS += test-tool fish rescue
|
||||
|
||||
# bash-completion
|
||||
SUBDIRS += bash
|
||||
@@ -155,31 +150,6 @@ endif
|
||||
# Unconditional because nothing is built yet.
|
||||
SUBDIRS += csharp
|
||||
|
||||
# OCaml tools. Note 'common/ml*' and 'customize' contain shared code
|
||||
# used by other OCaml tools, so these must come first.
|
||||
if HAVE_OCAML
|
||||
SUBDIRS += common/mlgettext
|
||||
SUBDIRS += common/mlprogress
|
||||
SUBDIRS += common/mlvisit
|
||||
SUBDIRS += common/mlxml
|
||||
SUBDIRS += common/mltools
|
||||
SUBDIRS += common/mlcustomize
|
||||
SUBDIRS += customize
|
||||
SUBDIRS += builder builder/templates
|
||||
SUBDIRS += get-kernel
|
||||
SUBDIRS += resize
|
||||
SUBDIRS += sparsify
|
||||
SUBDIRS += sysprep
|
||||
if HAVE_FUSE
|
||||
SUBDIRS += dib
|
||||
endif
|
||||
endif
|
||||
|
||||
# Perl tools.
|
||||
if HAVE_TOOLS
|
||||
SUBDIRS += tools
|
||||
endif
|
||||
|
||||
# guestmount
|
||||
if HAVE_FUSE
|
||||
SUBDIRS += fuse
|
||||
@@ -292,8 +262,6 @@ maintainer-upload-website:
|
||||
# docs/C_SOURCE_FILES
|
||||
# - source files scanned for internal documentation
|
||||
# po/POTFILES - files with ordinary extensions, but not OCaml files
|
||||
# po/POTFILES-pl - Perl files that don't end in *.pl, which need a
|
||||
# special xgettext option [not generated here]
|
||||
# po/POTFILES-ml - OCaml files, which need a special tool to translate
|
||||
|
||||
dist-hook: BUGS ChangeLog docs/C_SOURCE_FILES po/POTFILES po/POTFILES-ml
|
||||
@@ -337,16 +305,6 @@ po/POTFILES: configure.ac
|
||||
LC_ALL=C sort -u > $@-t
|
||||
mv $@-t $@
|
||||
|
||||
po/POTFILES-ml: configure.ac
|
||||
rm -f $@ $@-t
|
||||
cd $(srcdir); \
|
||||
find builder common/ml* customize dib get-kernel resize sparsify sysprep -name '*.ml' | \
|
||||
grep -v '^builder/templates/' | \
|
||||
grep -v '^common/mlv2v/' | \
|
||||
grep -v -E '.*_tests\.ml$$' | \
|
||||
LC_ALL=C sort > $@-t
|
||||
mv $@-t $@
|
||||
|
||||
# Try to stop people using 'make install' without 'DESTDIR'.
|
||||
install:
|
||||
@if test "x$(DESTDIR)" != "x" || test "x$(REALLY_INSTALL)" = "xyes"; \
|
||||
@@ -584,7 +542,12 @@ maintainer-check-extra-dist:
|
||||
( git ls-files ; \
|
||||
cd common; git ls-files | sed 's,^,common/,' ) | \
|
||||
grep -v '^common$$' | \
|
||||
grep -v '^common/mlcustomize/' | \
|
||||
grep -v '^common/mlprogress/' | \
|
||||
grep -v '^common/mltools/' | \
|
||||
grep -v '^common/mlv2v/' | \
|
||||
grep -v '^common/mlvisit/' | \
|
||||
grep -v '^common/mlxml/' | \
|
||||
grep -v '^intltool-.*\.in' | \
|
||||
grep -v '^\.gitmodules' | \
|
||||
grep -v '^\.gnulib' | \
|
||||
@@ -594,7 +557,7 @@ maintainer-check-extra-dist:
|
||||
cat tmp/comm-out
|
||||
[ ! -s tmp/comm-out ]
|
||||
@echo Checking for generated files missing from the tarball ...
|
||||
@for f in `cat generator/files-generated.txt | grep -v '^common/mlv2v/'`; do \
|
||||
@for f in `cat generator/files-generated.txt | grep -v '^common/mlcustomize' | grep -v '^common/mlv2v/'`; do \
|
||||
if ! grep -sq "^$$f\$$" tmp/tarfiles; then \
|
||||
echo generated file missing from tarball: $$f; \
|
||||
exit 1; \
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
# libguestfs virt alignment tools
|
||||
# Copyright (C) 2011 Red Hat Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
include $(top_srcdir)/subdir-rules.mk
|
||||
|
||||
EXTRA_DIST = \
|
||||
test-virt-alignment-scan.sh \
|
||||
test-virt-alignment-scan-docs.sh \
|
||||
test-virt-alignment-scan-guests.sh \
|
||||
virt-alignment-scan.pod
|
||||
|
||||
bin_PROGRAMS = virt-alignment-scan
|
||||
|
||||
virt_alignment_scan_SOURCES = \
|
||||
scan.c
|
||||
|
||||
virt_alignment_scan_CPPFLAGS = \
|
||||
-DGUESTFS_NO_DEPRECATED=1 \
|
||||
-I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \
|
||||
-I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \
|
||||
-I$(top_srcdir)/lib -I$(top_builddir)/lib \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/common/options -I$(top_builddir)/common/options \
|
||||
-I$(top_srcdir)/common/parallel -I$(top_builddir)/common/parallel \
|
||||
-I$(srcdir)/../gnulib/lib -I../gnulib/lib \
|
||||
-DLOCALEBASEDIR=\""$(datadir)/locale"\"
|
||||
|
||||
virt_alignment_scan_CFLAGS = \
|
||||
-pthread \
|
||||
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
|
||||
$(LIBXML2_CFLAGS) \
|
||||
$(LIBVIRT_CFLAGS)
|
||||
|
||||
virt_alignment_scan_LDADD = \
|
||||
$(top_builddir)/common/options/liboptions.la \
|
||||
$(top_builddir)/common/parallel/libparallel.la \
|
||||
$(top_builddir)/common/structs/libstructs.la \
|
||||
$(top_builddir)/common/utils/libutils.la \
|
||||
$(top_builddir)/lib/libguestfs.la \
|
||||
$(LIBXML2_LIBS) \
|
||||
$(LIBVIRT_LIBS) \
|
||||
$(LTLIBINTL) \
|
||||
../gnulib/lib/libgnu.la \
|
||||
-lm
|
||||
|
||||
# Manual pages and HTML files for the website.
|
||||
man_MANS = virt-alignment-scan.1
|
||||
noinst_DATA = $(top_builddir)/website/virt-alignment-scan.1.html
|
||||
|
||||
virt-alignment-scan.1 $(top_builddir)/website/virt-alignment-scan.1.html: stamp-virt-alignment-scan.pod
|
||||
|
||||
stamp-virt-alignment-scan.pod: virt-alignment-scan.pod
|
||||
$(PODWRAPPER) \
|
||||
--man virt-alignment-scan.1 \
|
||||
--html $(top_builddir)/website/virt-alignment-scan.1.html \
|
||||
--path $(top_srcdir)/common/options \
|
||||
--license GPLv2+ \
|
||||
--warning safe \
|
||||
$<
|
||||
touch $@
|
||||
|
||||
# Tests.
|
||||
|
||||
TESTS_ENVIRONMENT = $(top_builddir)/run --test
|
||||
|
||||
TESTS = \
|
||||
test-virt-alignment-scan-docs.sh
|
||||
|
||||
if ENABLE_APPLIANCE
|
||||
TESTS += \
|
||||
test-virt-alignment-scan.sh
|
||||
|
||||
if HAVE_LIBVIRT
|
||||
TESTS += \
|
||||
test-virt-alignment-scan-guests.sh
|
||||
endif
|
||||
endif
|
||||
|
||||
check-valgrind:
|
||||
$(MAKE) VG="@VG@" check
|
||||
384
align/scan.c
384
align/scan.c
@@ -1,384 +0,0 @@
|
||||
/* virt-alignment-scan
|
||||
* Copyright (C) 2011 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <locale.h>
|
||||
#include <assert.h>
|
||||
#include <libintl.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef HAVE_LIBVIRT
|
||||
#include <libvirt/libvirt.h>
|
||||
#include <libvirt/virterror.h>
|
||||
#endif
|
||||
|
||||
#include "getprogname.h"
|
||||
|
||||
#include "guestfs.h"
|
||||
#include "structs-cleanups.h"
|
||||
#include "options.h"
|
||||
#include "display-options.h"
|
||||
#include "parallel.h"
|
||||
#include "domains.h"
|
||||
|
||||
/* This just needs to be larger than any alignment we care about. */
|
||||
static size_t worst_alignment = UINT_MAX;
|
||||
static pthread_mutex_t worst_alignment_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static int scan (guestfs_h *g, const char *prefix, FILE *fp);
|
||||
|
||||
#ifdef HAVE_LIBVIRT
|
||||
static int scan_work (guestfs_h *g, size_t i, FILE *fp);
|
||||
#endif
|
||||
|
||||
/* These globals are shared with options.c. */
|
||||
guestfs_h *g;
|
||||
|
||||
int read_only = 1;
|
||||
int live = 0;
|
||||
int verbose = 0;
|
||||
int keys_from_stdin = 0;
|
||||
int echo_keys = 0;
|
||||
const char *libvirt_uri = NULL;
|
||||
int inspector = 0;
|
||||
int in_guestfish = 0;
|
||||
int in_virt_rescue = 0;
|
||||
|
||||
static int quiet = 0; /* --quiet */
|
||||
static int uuid = 0; /* --uuid */
|
||||
|
||||
static void __attribute__((noreturn))
|
||||
usage (int status)
|
||||
{
|
||||
if (status != EXIT_SUCCESS)
|
||||
fprintf (stderr, _("Try ‘%s --help’ for more information.\n"),
|
||||
getprogname ());
|
||||
else {
|
||||
printf (_("%s: check alignment of virtual machine partitions\n"
|
||||
"Copyright (C) 2011 Red Hat Inc.\n"
|
||||
"Usage:\n"
|
||||
" %s [--options] -d domname\n"
|
||||
" %s [--options] -a disk.img [-a disk.img ...]\n"
|
||||
"Options:\n"
|
||||
" -a|--add image Add image\n"
|
||||
" --blocksize[=512|4096]\n"
|
||||
" Set sector size of the disk for -a option\n"
|
||||
" -c|--connect uri Specify libvirt URI for -d option\n"
|
||||
" -d|--domain guest Add disks from libvirt guest\n"
|
||||
" --format[=raw|..] Force disk format for -a option\n"
|
||||
" --help Display brief help\n"
|
||||
" -P nr_threads Use at most nr_threads\n"
|
||||
" -q|--quiet No output, just exit code\n"
|
||||
" --uuid Print UUIDs instead of names\n"
|
||||
" -v|--verbose Verbose messages\n"
|
||||
" -V|--version Display version and exit\n"
|
||||
" -x Trace libguestfs API calls\n"
|
||||
"For more information, see the manpage %s(1).\n"),
|
||||
getprogname (), getprogname (),
|
||||
getprogname (), getprogname ());
|
||||
}
|
||||
exit (status);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
setlocale (LC_ALL, "");
|
||||
bindtextdomain (PACKAGE, LOCALEBASEDIR);
|
||||
textdomain (PACKAGE);
|
||||
|
||||
enum { HELP_OPTION = CHAR_MAX + 1 };
|
||||
|
||||
static const char options[] = "a:c:d:P:qvVx";
|
||||
static const struct option long_options[] = {
|
||||
{ "add", 1, 0, 'a' },
|
||||
{ "blocksize", 2, 0, 0 },
|
||||
{ "connect", 1, 0, 'c' },
|
||||
{ "domain", 1, 0, 'd' },
|
||||
{ "format", 2, 0, 0 },
|
||||
{ "help", 0, 0, HELP_OPTION },
|
||||
{ "long-options", 0, 0, 0 },
|
||||
{ "quiet", 0, 0, 'q' },
|
||||
{ "short-options", 0, 0, 0 },
|
||||
{ "uuid", 0, 0, 0 },
|
||||
{ "verbose", 0, 0, 'v' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
struct drv *drvs = NULL;
|
||||
const char *format = NULL;
|
||||
bool format_consumed = true;
|
||||
int blocksize = 0;
|
||||
bool blocksize_consumed = true;
|
||||
int c;
|
||||
int option_index;
|
||||
int exit_code;
|
||||
size_t max_threads = 0;
|
||||
int r;
|
||||
|
||||
g = guestfs_create ();
|
||||
if (g == NULL)
|
||||
error (EXIT_FAILURE, errno, "guestfs_create");
|
||||
|
||||
for (;;) {
|
||||
c = getopt_long (argc, argv, options, long_options, &option_index);
|
||||
if (c == -1) break;
|
||||
|
||||
switch (c) {
|
||||
case 0: /* options which are long only */
|
||||
if (STREQ (long_options[option_index].name, "long-options"))
|
||||
display_long_options (long_options);
|
||||
else if (STREQ (long_options[option_index].name, "short-options"))
|
||||
display_short_options (options);
|
||||
else if (STREQ (long_options[option_index].name, "format")) {
|
||||
OPTION_format;
|
||||
} else if (STREQ (long_options[option_index].name, "blocksize")) {
|
||||
OPTION_blocksize;
|
||||
} else if (STREQ (long_options[option_index].name, "uuid")) {
|
||||
uuid = 1;
|
||||
} else
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("unknown long option: %s (%d)"),
|
||||
long_options[option_index].name, option_index);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
OPTION_a;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
OPTION_c;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
OPTION_d;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
if (sscanf (optarg, "%zu", &max_threads) != 1)
|
||||
error (EXIT_FAILURE, 0, _("-P option is not numeric"));
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
OPTION_v;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
OPTION_V;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
OPTION_x;
|
||||
break;
|
||||
|
||||
case HELP_OPTION:
|
||||
usage (EXIT_SUCCESS);
|
||||
|
||||
default:
|
||||
usage (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* These are really constants, but they have to be variables for the
|
||||
* options parsing code. Assert here that they have known-good
|
||||
* values.
|
||||
*/
|
||||
assert (read_only == 1);
|
||||
assert (inspector == 0);
|
||||
assert (live == 0);
|
||||
|
||||
/* Must be no extra arguments on the command line. */
|
||||
if (optind != argc)
|
||||
usage (EXIT_FAILURE);
|
||||
|
||||
CHECK_OPTION_format_consumed;
|
||||
CHECK_OPTION_blocksize_consumed;
|
||||
|
||||
/* virt-alignment-scan has two modes. If the user didn't specify
|
||||
* any drives, then we do the scan on every libvirt guest. That's
|
||||
* the if-clause below. If the user specified domains/drives, then
|
||||
* we assume they belong to a single guest. That's the else-clause
|
||||
* below.
|
||||
*/
|
||||
if (drvs == NULL) {
|
||||
#if defined(HAVE_LIBVIRT)
|
||||
get_all_libvirt_domains (libvirt_uri);
|
||||
r = start_threads (max_threads, g, scan_work);
|
||||
free_domains ();
|
||||
if (r == -1)
|
||||
exit (EXIT_FAILURE);
|
||||
#else
|
||||
error (EXIT_FAILURE, 0, _("compiled without support for libvirt"));
|
||||
#endif
|
||||
} else { /* Single guest. */
|
||||
if (uuid)
|
||||
error (EXIT_FAILURE, 0, _("--uuid option cannot be used with -a or -d"));
|
||||
|
||||
/* Add domains/drives from the command line (for a single guest). */
|
||||
add_drives (drvs);
|
||||
|
||||
if (guestfs_launch (g) == -1)
|
||||
exit (EXIT_FAILURE);
|
||||
|
||||
/* Free up data structures, no longer needed after this point. */
|
||||
free_drives (drvs);
|
||||
|
||||
/* Perform the scan. */
|
||||
r = scan (g, NULL, stdout);
|
||||
|
||||
guestfs_close (g);
|
||||
|
||||
if (r == -1)
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Decide on an appropriate exit code. */
|
||||
if (worst_alignment < 10) /* 2^10 = 4096 */
|
||||
exit_code = 3;
|
||||
else if (worst_alignment < 16) /* 2^16 = 65536 */
|
||||
exit_code = 2;
|
||||
else
|
||||
exit_code = 0;
|
||||
|
||||
exit (exit_code);
|
||||
}
|
||||
|
||||
static int
|
||||
scan (guestfs_h *g, const char *prefix, FILE *fp)
|
||||
{
|
||||
size_t i, j;
|
||||
size_t alignment;
|
||||
uint64_t start;
|
||||
int err;
|
||||
|
||||
CLEANUP_FREE_STRING_LIST char **devices = guestfs_list_devices (g);
|
||||
if (devices == NULL)
|
||||
return -1;
|
||||
|
||||
for (i = 0; devices[i] != NULL; ++i) {
|
||||
CLEANUP_FREE char *name = NULL;
|
||||
CLEANUP_FREE_PARTITION_LIST struct guestfs_partition_list *parts = NULL;
|
||||
|
||||
guestfs_push_error_handler (g, NULL, NULL);
|
||||
parts = guestfs_part_list (g, devices[i]);
|
||||
guestfs_pop_error_handler (g);
|
||||
if (parts == NULL) {
|
||||
if (guestfs_last_errno (g) == EINVAL) /* unrecognised disk label */
|
||||
continue;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Canonicalize the name of the device for printing. */
|
||||
name = guestfs_canonical_device_name (g, devices[i]);
|
||||
if (name == NULL)
|
||||
return -1;
|
||||
|
||||
for (j = 0; j < parts->len; ++j) {
|
||||
/* Start offset of the partition in bytes. */
|
||||
start = parts->val[j].part_start;
|
||||
|
||||
if (!quiet) {
|
||||
if (prefix)
|
||||
fprintf (fp, "%s:", prefix);
|
||||
|
||||
fprintf (fp, "%s%d %12" PRIu64 " ",
|
||||
name, (int) parts->val[j].part_num, start);
|
||||
}
|
||||
|
||||
/* What's the alignment? */
|
||||
if (start == 0) /* Probably not possible, but anyway. */
|
||||
alignment = 64;
|
||||
else
|
||||
for (alignment = 0; (start & 1) == 0; alignment++, start /= 2)
|
||||
;
|
||||
|
||||
if (!quiet) {
|
||||
if (alignment < 10)
|
||||
fprintf (fp, "%12" PRIu64 " ", UINT64_C(1) << alignment);
|
||||
else if (alignment < 64)
|
||||
fprintf (fp, "%12" PRIu64 "K ", UINT64_C(1) << (alignment - 10));
|
||||
else
|
||||
fprintf (fp, "- ");
|
||||
}
|
||||
|
||||
err = pthread_mutex_lock (&worst_alignment_mutex);
|
||||
assert (err == 0);
|
||||
if (alignment < worst_alignment)
|
||||
worst_alignment = alignment;
|
||||
err = pthread_mutex_unlock (&worst_alignment_mutex);
|
||||
assert (err == 0);
|
||||
|
||||
if (alignment < 12) { /* Bad in general: < 4K alignment */
|
||||
if (!quiet)
|
||||
fprintf (fp, "bad (%s)\n", _("alignment < 4K"));
|
||||
} else if (alignment < 16) { /* Bad on NetApps: < 64K alignment */
|
||||
if (!quiet)
|
||||
fprintf (fp, "bad (%s)\n", _("alignment < 64K"));
|
||||
} else {
|
||||
if (!quiet)
|
||||
fprintf (fp, "ok\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(HAVE_LIBVIRT)
|
||||
|
||||
/* The multi-threaded version. This callback is called from the code
|
||||
* in "parallel.c".
|
||||
*/
|
||||
|
||||
static int
|
||||
scan_work (guestfs_h *g, size_t i, FILE *fp)
|
||||
{
|
||||
struct guestfs_add_libvirt_dom_argv optargs;
|
||||
|
||||
optargs.bitmask =
|
||||
GUESTFS_ADD_LIBVIRT_DOM_READONLY_BITMASK |
|
||||
GUESTFS_ADD_LIBVIRT_DOM_READONLYDISK_BITMASK;
|
||||
optargs.readonly = 1;
|
||||
optargs.readonlydisk = "read";
|
||||
|
||||
if (guestfs_add_libvirt_dom_argv (g, domains[i].dom, &optargs) == -1)
|
||||
return -1;
|
||||
|
||||
if (guestfs_launch (g) == -1)
|
||||
return -1;
|
||||
|
||||
return scan (g, !uuid ? domains[i].name : domains[i].uuid, fp);
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBVIRT */
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash -
|
||||
# libguestfs
|
||||
# Copyright (C) 2016 Red Hat Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
set -e
|
||||
|
||||
$TEST_FUNCTIONS
|
||||
skip_if_skipped
|
||||
|
||||
$top_srcdir/podcheck.pl "$srcdir/virt-alignment-scan.pod" virt-alignment-scan \
|
||||
--path $top_srcdir/common/options
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash -
|
||||
# libguestfs
|
||||
# Copyright (C) 2013 Red Hat Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
$TEST_FUNCTIONS
|
||||
skip_if_skipped
|
||||
|
||||
libvirt_uri="test://$abs_top_builddir/test-data/phony-guests/guests-all-good.xml"
|
||||
|
||||
$VG virt-alignment-scan -c "$libvirt_uri"
|
||||
r=$?
|
||||
|
||||
# 0, 2 and 3 are reasonable non-error exit codes. Others are errors.
|
||||
if [ $r -ne 0 -a $r -ne 2 -a $r -ne 3 ]; then
|
||||
exit $r
|
||||
fi
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash -
|
||||
# libguestfs
|
||||
# Copyright (C) 2012 Red Hat Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
$TEST_FUNCTIONS
|
||||
skip_if_skipped
|
||||
|
||||
$VG virt-alignment-scan --format=raw -a ../test-data/phony-guests/fedora.img
|
||||
r=$?
|
||||
|
||||
# 0, 2 and 3 are reasonable non-error exit codes. Others are errors.
|
||||
if [ $r -ne 0 -a $r -ne 2 -a $r -ne 3 ]; then
|
||||
exit $r
|
||||
fi
|
||||
@@ -1,401 +0,0 @@
|
||||
=head1 NAME
|
||||
|
||||
virt-alignment-scan - Check alignment of virtual machine partitions
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
virt-alignment-scan [--options] -d domname
|
||||
|
||||
virt-alignment-scan [--options] -a disk.img [-a disk.img ...]
|
||||
|
||||
virt-alignment-scan [--options]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
When older operating systems install themselves, the partitioning
|
||||
tools place partitions at a sector misaligned with the underlying
|
||||
storage (commonly the first partition starts on sector C<63>).
|
||||
Misaligned partitions can result in an operating system issuing more
|
||||
I/O than should be necessary.
|
||||
|
||||
The virt-alignment-scan tool checks the alignment of partitions in
|
||||
virtual machines and disk images and warns you if there are alignment
|
||||
problems.
|
||||
|
||||
Currently there is no virt tool for fixing alignment problems. You
|
||||
can only reinstall the guest operating system. The following NetApp
|
||||
document summarises the problem and possible solutions:
|
||||
L<http://media.netapp.com/documents/tr-3747.pdf>
|
||||
|
||||
=head1 OUTPUT
|
||||
|
||||
To run this tool on a disk image directly, use the I<-a> option:
|
||||
|
||||
$ virt-alignment-scan -a winxp.img
|
||||
/dev/sda1 32256 512 bad (alignment < 4K)
|
||||
|
||||
$ virt-alignment-scan -a fedora16.img
|
||||
/dev/sda1 1048576 1024K ok
|
||||
/dev/sda2 2097152 2048K ok
|
||||
/dev/sda3 526385152 2048K ok
|
||||
|
||||
To run the tool on a guest known to libvirt, use the I<-d> option and
|
||||
possibly the I<-c> option:
|
||||
|
||||
# virt-alignment-scan -d RHEL5
|
||||
/dev/sda1 32256 512 bad (alignment < 4K)
|
||||
/dev/sda2 106928640 512 bad (alignment < 4K)
|
||||
|
||||
$ virt-alignment-scan -c qemu:///system -d Win7TwoDisks
|
||||
/dev/sda1 1048576 1024K ok
|
||||
/dev/sda2 105906176 1024K ok
|
||||
/dev/sdb1 65536 64K ok
|
||||
|
||||
Run virt-alignment-scan without any I<-a> or I<-d> options to scan all
|
||||
libvirt domains.
|
||||
|
||||
# virt-alignment-scan
|
||||
F16x64:/dev/sda1 1048576 1024K ok
|
||||
F16x64:/dev/sda2 2097152 2048K ok
|
||||
F16x64:/dev/sda3 526385152 2048K ok
|
||||
|
||||
The output consists of 4 or more whitespace-separated columns. Only
|
||||
the first 4 columns are significant if you want to parse this from a
|
||||
program. The columns are:
|
||||
|
||||
=over 4
|
||||
|
||||
=item col 1
|
||||
|
||||
The device and partition name (eg. F</dev/sda1> meaning the
|
||||
first partition on the first block device).
|
||||
|
||||
When listing all libvirt domains (no I<-a> or I<-d> option given) this
|
||||
column is prefixed by the libvirt name or UUID (if I<--uuid> is
|
||||
given). eg: C<WinXP:/dev/sda1>
|
||||
|
||||
=item col 2
|
||||
|
||||
the start of the partition in bytes
|
||||
|
||||
=item col 3
|
||||
|
||||
the alignment in bytes or Kbytes (eg. C<512> or C<4K>)
|
||||
|
||||
=item col 4
|
||||
|
||||
C<ok> if the alignment is best for performance, or C<bad> if the
|
||||
alignment can cause performance problems
|
||||
|
||||
=item cols 5+
|
||||
|
||||
optional free-text explanation.
|
||||
|
||||
=back
|
||||
|
||||
The exit code from the program changes depending on whether poorly
|
||||
aligned partitions were found. See L</EXIT STATUS> below.
|
||||
|
||||
If you just want the exit code with no output, use the I<-q> option.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<--help>
|
||||
|
||||
Display brief help.
|
||||
|
||||
=item B<-a> file
|
||||
|
||||
=item B<--add> file
|
||||
|
||||
Add I<file> which should be a disk image from a virtual machine.
|
||||
|
||||
The format of the disk image is auto-detected. To override this and
|
||||
force a particular format use the I<--format=..> option.
|
||||
|
||||
=item B<-a URI>
|
||||
|
||||
=item B<--add URI>
|
||||
|
||||
Add a remote disk. See L<guestfish(1)/ADDING REMOTE STORAGE>.
|
||||
|
||||
__INCLUDE:blocksize-option.pod__
|
||||
|
||||
=item B<-c> URI
|
||||
|
||||
=item B<--connect> URI
|
||||
|
||||
If using libvirt, connect to the given I<URI>. If omitted, then we
|
||||
connect to the default libvirt hypervisor.
|
||||
|
||||
If you specify guest block devices directly (I<-a>), then libvirt is
|
||||
not used at all.
|
||||
|
||||
=item B<-d> guest
|
||||
|
||||
=item B<--domain> guest
|
||||
|
||||
Add all the disks from the named libvirt guest. Domain UUIDs can be
|
||||
used instead of names.
|
||||
|
||||
=item B<--format=raw|qcow2|..>
|
||||
|
||||
=item B<--format>
|
||||
|
||||
The default for the I<-a> option is to auto-detect the format of the
|
||||
disk image. Using this forces the disk format for I<-a> options which
|
||||
follow on the command line. Using I<--format> with no argument
|
||||
switches back to auto-detection for subsequent I<-a> options.
|
||||
|
||||
For example:
|
||||
|
||||
virt-alignment-scan --format=raw -a disk.img
|
||||
|
||||
forces raw format (no auto-detection) for F<disk.img>.
|
||||
|
||||
virt-alignment-scan --format=raw -a disk.img --format -a another.img
|
||||
|
||||
forces raw format (no auto-detection) for F<disk.img> and reverts to
|
||||
auto-detection for F<another.img>.
|
||||
|
||||
If you have untrusted raw-format guest disk images, you should use
|
||||
this option to specify the disk format. This avoids a possible
|
||||
security problem with malicious guests (CVE-2010-3851).
|
||||
|
||||
=item B<-P> nr_threads
|
||||
|
||||
Since libguestfs 1.22, virt-alignment-scan is multithreaded and
|
||||
examines guests in parallel. By default the number of threads to use
|
||||
is chosen based on the amount of free memory available at the time
|
||||
that virt-alignment-scan is started. You can force
|
||||
virt-alignment-scan to use at most C<nr_threads> by using the I<-P>
|
||||
option.
|
||||
|
||||
Note that I<-P 0> means to autodetect, and I<-P 1> means to use a
|
||||
single thread.
|
||||
|
||||
=item B<-q>
|
||||
|
||||
=item B<--quiet>
|
||||
|
||||
Don’t produce any output. Just set the exit code
|
||||
(see L</EXIT STATUS> below).
|
||||
|
||||
=item B<--uuid>
|
||||
|
||||
Print UUIDs instead of names. This is useful for following a guest
|
||||
even when the guest is migrated or renamed, or when two guests happen
|
||||
to have the same name.
|
||||
|
||||
This option only applies when listing all libvirt domains (when no
|
||||
I<-a> or I<-d> options are specified).
|
||||
|
||||
=item B<-v>
|
||||
|
||||
=item B<--verbose>
|
||||
|
||||
Enable verbose messages for debugging.
|
||||
|
||||
=item B<-V>
|
||||
|
||||
=item B<--version>
|
||||
|
||||
Display version number and exit.
|
||||
|
||||
=item B<-x>
|
||||
|
||||
Enable tracing of libguestfs API calls.
|
||||
|
||||
=back
|
||||
|
||||
=head1 RECOMMENDED ALIGNMENT
|
||||
|
||||
Operating systems older than Windows 2008 and Linux before ca.2010
|
||||
place the first sector of the first partition at sector 63, with a 512
|
||||
byte sector size. This happens because of a historical accident.
|
||||
Drives have to report a cylinder / head / sector (CHS) geometry to the
|
||||
BIOS. The geometry is completely meaningless on modern drives, but it
|
||||
happens that the geometry reported always has 63 sectors per track.
|
||||
The operating system therefore places the first partition at the start
|
||||
of the second "track", at sector 63.
|
||||
|
||||
When the guest OS is virtualized, the host operating system and
|
||||
hypervisor may prefer accesses aligned to one of:
|
||||
|
||||
=over 4
|
||||
|
||||
=item * 512 bytes
|
||||
|
||||
if the host OS uses local storage directly on hard drive partitions,
|
||||
and the hard drive has 512 byte physical sectors.
|
||||
|
||||
=item * 4 Kbytes
|
||||
|
||||
for local storage on new hard drives with 4Kbyte physical sectors; for
|
||||
file-backed storage on filesystems with 4Kbyte block size; or for some
|
||||
types of network-attached storage.
|
||||
|
||||
=item * 64 Kbytes
|
||||
|
||||
for high-end network-attached storage. This is the optimal block size
|
||||
for some NetApp hardware.
|
||||
|
||||
=item * 1 Mbyte
|
||||
|
||||
see L</1 MB PARTITION ALIGNMENT> below.
|
||||
|
||||
=back
|
||||
|
||||
Partitions which are not aligned correctly to the underlying
|
||||
storage cause extra I/O. For example:
|
||||
|
||||
sect#63
|
||||
┌──────────────────────────┬ ─ ─ ─ ─
|
||||
│ guest │
|
||||
│ filesystem block │
|
||||
─ ┬──────────────────┴──────┬───────────────────┴─────┬ ─ ─
|
||||
│ host block │ host block │
|
||||
│ │ │
|
||||
─ ┴─────────────────────────┴─────────────────────────┴ ─ ─
|
||||
|
||||
In this example, each time a 4K guest block is read, two blocks on the
|
||||
host must be accessed (so twice as much I/O is done). When a 4K guest
|
||||
block is written, two host blocks must first be read, the old and new
|
||||
data combined, and the two blocks written back (4x I/O).
|
||||
|
||||
=head2 LINUX HOST BLOCK AND I/O SIZE
|
||||
|
||||
New versions of the Linux kernel expose the physical and logical block
|
||||
size, and minimum and recommended I/O size.
|
||||
|
||||
For a typical consumer hard drive with 512 byte sectors:
|
||||
|
||||
$ cat /sys/block/sda/queue/hw_sector_size
|
||||
512
|
||||
$ cat /sys/block/sda/queue/physical_block_size
|
||||
512
|
||||
$ cat /sys/block/sda/queue/logical_block_size
|
||||
512
|
||||
$ cat /sys/block/sda/queue/minimum_io_size
|
||||
512
|
||||
$ cat /sys/block/sda/queue/optimal_io_size
|
||||
0
|
||||
|
||||
For a new consumer hard drive with 4Kbyte sectors:
|
||||
|
||||
$ cat /sys/block/sda/queue/hw_sector_size
|
||||
4096
|
||||
$ cat /sys/block/sda/queue/physical_block_size
|
||||
4096
|
||||
$ cat /sys/block/sda/queue/logical_block_size
|
||||
4096
|
||||
$ cat /sys/block/sda/queue/minimum_io_size
|
||||
4096
|
||||
$ cat /sys/block/sda/queue/optimal_io_size
|
||||
0
|
||||
|
||||
For a NetApp LUN:
|
||||
|
||||
$ cat /sys/block/sdc/queue/logical_block_size
|
||||
512
|
||||
$ cat /sys/block/sdc/queue/physical_block_size
|
||||
512
|
||||
$ cat /sys/block/sdc/queue/minimum_io_size
|
||||
4096
|
||||
$ cat /sys/block/sdc/queue/optimal_io_size
|
||||
65536
|
||||
|
||||
The NetApp allows 512 byte accesses (but they will be very
|
||||
inefficient), prefers a minimum 4K I/O size, but the optimal I/O size
|
||||
is 64K.
|
||||
|
||||
For detailed information about what these numbers mean, see
|
||||
L<http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Storage_Administration_Guide/newstorage-iolimits.html>
|
||||
|
||||
[Thanks to Matt Booth for providing 4K drive data. Thanks to Mike
|
||||
Snitzer for providing NetApp data and additional information.]
|
||||
|
||||
=head2 1 MB PARTITION ALIGNMENT
|
||||
|
||||
Microsoft picked 1 MB as the default alignment for all partitions
|
||||
starting with Windows 2008 Server, and Linux has followed this.
|
||||
|
||||
Assuming 512 byte sectors in the guest, you will now see the first
|
||||
partition starting at sector 2048, and subsequent partitions (if any)
|
||||
will start at a multiple of 2048 sectors.
|
||||
|
||||
1 MB alignment is compatible with all current alignment requirements
|
||||
(4K, 64K) and provides room for future growth in physical block sizes.
|
||||
|
||||
=head2 SETTING ALIGNMENT
|
||||
|
||||
L<virt-resize(1)> can change the alignment of the partitions of some
|
||||
guests. Currently it can fully align all the partitions of all
|
||||
Windows guests, and it will fix the bootloader where necessary. For
|
||||
Linux guests, it can align the second and subsequent partitions, so
|
||||
the majority of OS accesses except at boot will be aligned.
|
||||
|
||||
Another way to correct partition alignment problems is to reinstall
|
||||
your guest operating systems. If you install operating systems from
|
||||
templates, ensure these have correct partition alignment too.
|
||||
|
||||
For older versions of Windows, the following NetApp document contains
|
||||
useful information: L<http://media.netapp.com/documents/tr-3747.pdf>
|
||||
|
||||
For Red Hat Enterprise Linux E<le> 5, use a Kickstart script that
|
||||
contains an explicit C<%pre> section that creates aligned partitions
|
||||
using L<parted(8)>. Do not use the Kickstart C<part> command. The
|
||||
NetApp document above contains an example.
|
||||
|
||||
=head1 EXIT STATUS
|
||||
|
||||
This program returns:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
0
|
||||
|
||||
successful exit, all partitions are aligned E<ge> 64K for best performance
|
||||
|
||||
=item *
|
||||
|
||||
1
|
||||
|
||||
an error scanning the disk image or guest
|
||||
|
||||
=item *
|
||||
|
||||
2
|
||||
|
||||
successful exit, some partitions have alignment E<lt> 64K which can result
|
||||
in poor performance on high end network storage
|
||||
|
||||
=item *
|
||||
|
||||
3
|
||||
|
||||
successful exit, some partitions have alignment E<lt> 4K which can result
|
||||
in poor performance on most hypervisors
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<guestfs(3)>,
|
||||
L<guestfish(1)>,
|
||||
L<virt-filesystems(1)>,
|
||||
L<virt-rescue(1)>,
|
||||
L<virt-resize(1)>,
|
||||
L<http://libguestfs.org/>.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (C) 2011 Red Hat Inc.
|
||||
@@ -1,486 +0,0 @@
|
||||
# libguestfs virt-builder tool
|
||||
# Copyright (C) 2013-2020 Red Hat Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
include $(top_srcdir)/subdir-rules.mk
|
||||
|
||||
AM_YFLAGS = -d
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(SOURCES_MLI) $(SOURCES_ML) $(SOURCES_C) \
|
||||
$(REPOSITORY_SOURCES_ML) \
|
||||
$(REPOSITORY_SOURCES_MLI) \
|
||||
index_parser_tests.ml \
|
||||
libguestfs.gpg \
|
||||
opensuse.gpg \
|
||||
test-console.sh \
|
||||
test-index \
|
||||
test-simplestreams/streams/v1/index.json \
|
||||
test-simplestreams/streams/v1/net.cirros-cloud_released_download.json \
|
||||
test-virt-builder.sh \
|
||||
test-docs.sh \
|
||||
test-virt-builder-cacheall.sh \
|
||||
test-virt-builder-list.sh \
|
||||
test-virt-builder-list-simplestreams.sh \
|
||||
test-virt-builder-planner.sh \
|
||||
test-virt-builder-repository.sh \
|
||||
test-virt-index-validate.sh \
|
||||
test-virt-index-validate-bad-1 \
|
||||
test-virt-index-validate-good-1 \
|
||||
test-virt-index-validate-good-2 \
|
||||
test-virt-index-validate-good-3 \
|
||||
test-virt-index-validate-good-4 \
|
||||
virt-builder.pod \
|
||||
virt-builder-repository.pod \
|
||||
virt-index-validate.pod
|
||||
|
||||
SOURCES_MLI = \
|
||||
builder.mli \
|
||||
cache.mli \
|
||||
cmdline.mli \
|
||||
downloader.mli \
|
||||
index.mli \
|
||||
index_parser.mli \
|
||||
ini_reader.mli \
|
||||
languages.mli \
|
||||
list_entries.mli \
|
||||
osinfo.mli \
|
||||
osinfo_config.mli \
|
||||
paths.mli \
|
||||
pxzcat.mli \
|
||||
repository_main.mli \
|
||||
setlocale.mli \
|
||||
sigchecker.mli \
|
||||
simplestreams_parser.mli \
|
||||
sources.mli \
|
||||
utils.mli
|
||||
|
||||
SOURCES_ML = \
|
||||
utils.ml \
|
||||
osinfo_config.ml \
|
||||
osinfo.ml \
|
||||
pxzcat.ml \
|
||||
setlocale.ml \
|
||||
index.ml \
|
||||
ini_reader.ml \
|
||||
paths.ml \
|
||||
languages.ml \
|
||||
cache.ml \
|
||||
sources.ml \
|
||||
downloader.ml \
|
||||
sigchecker.ml \
|
||||
index_parser.ml \
|
||||
simplestreams_parser.ml \
|
||||
list_entries.ml \
|
||||
cmdline.ml \
|
||||
builder.ml
|
||||
|
||||
SOURCES_C = \
|
||||
index-scan.c \
|
||||
index-struct.c \
|
||||
index-parse.c \
|
||||
index-parser-c.c \
|
||||
pxzcat-c.c \
|
||||
setlocale-c.c
|
||||
|
||||
REPOSITORY_SOURCES_ML = \
|
||||
utils.ml \
|
||||
index.ml \
|
||||
cache.ml \
|
||||
downloader.ml \
|
||||
sigchecker.ml \
|
||||
ini_reader.ml \
|
||||
index_parser.ml \
|
||||
paths.ml \
|
||||
sources.ml \
|
||||
osinfo_config.ml \
|
||||
osinfo.ml \
|
||||
repository_main.ml
|
||||
|
||||
REPOSITORY_SOURCES_MLI = \
|
||||
cache.mli \
|
||||
downloader.mli \
|
||||
index.mli \
|
||||
index_parser.mli \
|
||||
ini_reader.mli \
|
||||
sigchecker.mli \
|
||||
sources.mli
|
||||
|
||||
REPOSITORY_SOURCES_C = \
|
||||
index-scan.c \
|
||||
index-struct.c \
|
||||
index-parse.c \
|
||||
index-parser-c.c
|
||||
|
||||
man_MANS =
|
||||
noinst_DATA =
|
||||
bin_PROGRAMS =
|
||||
|
||||
if HAVE_OCAML
|
||||
|
||||
bin_PROGRAMS += virt-builder virt-builder-repository
|
||||
|
||||
virt_builder_SOURCES = $(SOURCES_C)
|
||||
virt_builder_CPPFLAGS = \
|
||||
-DCAML_NAME_SPACE \
|
||||
-I$(builddir) -I$(srcdir) \
|
||||
-I$(top_builddir) \
|
||||
-I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \
|
||||
-I$(shell $(OCAMLC) -where) \
|
||||
-I$(top_srcdir)/gnulib/lib \
|
||||
-I$(top_builddir)/common/utils \
|
||||
-I$(top_srcdir)/common/utils \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/include
|
||||
virt_builder_CFLAGS = \
|
||||
-pthread \
|
||||
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
|
||||
-Wno-unused-macros \
|
||||
$(LIBLZMA_CFLAGS) \
|
||||
$(LIBTINFO_CFLAGS) \
|
||||
$(LIBXML2_CFLAGS)
|
||||
|
||||
BOBJECTS = $(SOURCES_ML:.ml=.cmo)
|
||||
XOBJECTS = $(BOBJECTS:.cmo=.cmx)
|
||||
|
||||
virt_builder_repository_SOURCES = $(REPOSITORY_SOURCES_C)
|
||||
virt_builder_repository_CPPFLAGS = \
|
||||
-DCAML_NAME_SPACE \
|
||||
-I$(builddir) -I$(srcdir) \
|
||||
-I$(top_builddir) \
|
||||
-I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \
|
||||
-I$(shell $(OCAMLC) -where) \
|
||||
-I$(top_srcdir)/gnulib/lib \
|
||||
-I$(top_srcdir)/lib
|
||||
virt_builder_repository_CFLAGS = \
|
||||
-pthread \
|
||||
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
|
||||
-Wno-unused-macros \
|
||||
$(LIBTINFO_CFLAGS) \
|
||||
$(LIBXML2_CFLAGS)
|
||||
REPOSITORY_BOBJECTS = $(REPOSITORY_SOURCES_ML:.ml=.cmo)
|
||||
REPOSITORY_XOBJECTS = $(REPOSITORY_BOBJECTS:.cmo=.cmx)
|
||||
|
||||
# -I $(top_builddir)/lib/.libs is a hack which forces corresponding -L
|
||||
# option to be passed to gcc, so we don't try linking against an
|
||||
# installed copy of libguestfs.
|
||||
OCAMLPACKAGES = \
|
||||
-package str,unix \
|
||||
-I $(top_builddir)/common/utils/.libs \
|
||||
-I $(top_builddir)/common/mlxml \
|
||||
-I $(top_builddir)/lib/.libs \
|
||||
-I $(top_builddir)/gnulib/lib/.libs \
|
||||
-I $(top_builddir)/ocaml \
|
||||
-I $(top_builddir)/common/mlstdutils \
|
||||
-I $(top_builddir)/common/mlutils \
|
||||
-I $(top_builddir)/common/mlgettext \
|
||||
-I $(top_builddir)/common/mlpcre \
|
||||
-I $(top_builddir)/common/mltools \
|
||||
-I $(top_builddir)/common/mlcustomize \
|
||||
-I $(top_builddir)/customize
|
||||
OCAMLPACKAGES_TESTS =
|
||||
if HAVE_OCAML_PKG_GETTEXT
|
||||
OCAMLPACKAGES += -package gettext-stub
|
||||
endif
|
||||
if HAVE_OCAML_PKG_OUNIT
|
||||
OCAMLPACKAGES_TESTS += -package oUnit
|
||||
endif
|
||||
|
||||
OCAMLCLIBS = \
|
||||
-pthread -lpthread \
|
||||
-lutils \
|
||||
$(LIBTINFO_LIBS) \
|
||||
$(LIBCRYPT_LIBS) \
|
||||
$(LIBLZMA_LIBS) \
|
||||
$(LIBXML2_LIBS) \
|
||||
$(JANSSON_LIBS) \
|
||||
$(LIBINTL) \
|
||||
-lgnu
|
||||
|
||||
OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) -ccopt '$(CFLAGS)'
|
||||
|
||||
if !HAVE_OCAMLOPT
|
||||
OBJECTS = $(BOBJECTS)
|
||||
REPOSITORY_OBJECTS = $(REPOSITORY_BOBJECTS)
|
||||
else
|
||||
OBJECTS = $(XOBJECTS)
|
||||
REPOSITORY_OBJECTS = $(REPOSITORY_XOBJECTS)
|
||||
endif
|
||||
|
||||
OCAMLLINKFLAGS = \
|
||||
mlgettext.$(MLARCHIVE) \
|
||||
mlpcre.$(MLARCHIVE) \
|
||||
mlxml.$(MLARCHIVE) \
|
||||
mlstdutils.$(MLARCHIVE) \
|
||||
mlguestfs.$(MLARCHIVE) \
|
||||
mlcutils.$(MLARCHIVE) \
|
||||
mltools.$(MLARCHIVE) \
|
||||
mlcustomize.$(MLARCHIVE) \
|
||||
customize.$(MLARCHIVE) \
|
||||
$(LINK_CUSTOM_OCAMLC_ONLY)
|
||||
|
||||
virt_builder_DEPENDENCIES = \
|
||||
$(OBJECTS) \
|
||||
../common/mlpcre/mlpcre.$(MLARCHIVE) \
|
||||
../common/mlgettext/mlgettext.$(MLARCHIVE) \
|
||||
../common/mlstdutils/mlstdutils.$(MLARCHIVE) \
|
||||
../common/mlutils/mlcutils.$(MLARCHIVE) \
|
||||
../common/mltools/mltools.$(MLARCHIVE) \
|
||||
../common/mlcustomize/mlcustomize.$(MLARCHIVE) \
|
||||
../customize/customize.$(MLARCHIVE) \
|
||||
$(top_builddir)/ocaml-link.sh
|
||||
virt_builder_LINK = \
|
||||
$(top_builddir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \
|
||||
$(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) \
|
||||
$(OBJECTS) -o $@
|
||||
|
||||
virt_builder_repository_DEPENDENCIES = \
|
||||
$(REPOSITORY_OBJECTS) \
|
||||
../common/mltools/mltools.$(MLARCHIVE) \
|
||||
../common/mlxml/mlxml.$(MLARCHIVE) \
|
||||
$(top_builddir)/ocaml-link.sh
|
||||
virt_builder_repository_LINK = \
|
||||
$(top_builddir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \
|
||||
$(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) \
|
||||
$(REPOSITORY_OBJECTS) -o $@
|
||||
|
||||
# Manual pages and HTML files for the website.
|
||||
|
||||
man_MANS += virt-builder.1
|
||||
noinst_DATA += $(top_builddir)/website/virt-builder.1.html
|
||||
|
||||
virt-builder.1 $(top_builddir)/website/virt-builder.1.html: stamp-virt-builder.pod
|
||||
|
||||
stamp-virt-builder.pod: virt-builder.pod $(top_srcdir)/common/mlcustomize/customize-synopsis.pod $(top_srcdir)/common/mlcustomize/customize-options.pod
|
||||
$(PODWRAPPER) \
|
||||
--man virt-builder.1 \
|
||||
--html $(top_builddir)/website/virt-builder.1.html \
|
||||
--insert $(top_srcdir)/common/mlcustomize/customize-synopsis.pod:__CUSTOMIZE_SYNOPSIS__ \
|
||||
--insert $(top_srcdir)/common/mlcustomize/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
|
||||
--license GPLv2+ \
|
||||
--warning safe \
|
||||
$<
|
||||
touch $@
|
||||
|
||||
man_MANS += virt-builder-repository.1
|
||||
noinst_DATA += $(top_builddir)/website/virt-builder-repository.1.html
|
||||
|
||||
virt-builder-repository.1 $(top_builddir)/website/virt-builder-repository.1.html: stamp-virt-builder-repository.pod
|
||||
|
||||
stamp-virt-builder-repository.pod: virt-builder-repository.pod
|
||||
$(PODWRAPPER) \
|
||||
--man virt-builder-repository.1 \
|
||||
--html $(top_builddir)/website/virt-builder-repository.1.html \
|
||||
--license GPLv2+ \
|
||||
--warning safe \
|
||||
$<
|
||||
touch $@
|
||||
|
||||
# Tests.
|
||||
|
||||
TESTS_ENVIRONMENT = $(top_builddir)/run --test
|
||||
|
||||
disk_images := \
|
||||
$(shell for f in debian fedora ubuntu windows; do if [ -s "../test-data/phony-guests/$$f.img" ]; then echo $$f.xz; fi; done) \
|
||||
$(shell if [ -s "../test-data/phony-guests/fedora.img" ]; then echo fedora.qcow2 fedora.qcow2.xz; fi)
|
||||
|
||||
CLEANFILES += *.qcow2 *.xz
|
||||
|
||||
check_DATA = $(disk_images)
|
||||
|
||||
osinfo_config.ml: Makefile
|
||||
echo 'let libosinfo_db_path = "$(datadir)/libosinfo/db"' > $@-t
|
||||
mv $@-t $@
|
||||
|
||||
fedora.qcow2: ../test-data/phony-guests/fedora.img
|
||||
rm -f $@ $@-t
|
||||
qemu-img convert -f raw -O qcow2 $< $@-t
|
||||
mv $@-t $@
|
||||
|
||||
fedora.qcow2.xz: fedora.qcow2
|
||||
rm -f $@ $@-t
|
||||
xz --best -c $< > $@-t
|
||||
mv $@-t $@
|
||||
|
||||
%.xz: ../test-data/phony-guests/%.img
|
||||
rm -f $@ $@-t
|
||||
xz --best -c $< > $@-t
|
||||
mv $@-t $@
|
||||
|
||||
index_parser_tests_SOURCES = \
|
||||
index-scan.c \
|
||||
index-struct.c \
|
||||
index-parser-c.c \
|
||||
index-parse.c
|
||||
index_parser_tests_CPPFLAGS = $(virt_builder_CPPFLAGS)
|
||||
index_parser_tests_BOBJECTS = \
|
||||
utils.cmo \
|
||||
index.cmo \
|
||||
cache.cmo \
|
||||
downloader.cmo \
|
||||
sigchecker.cmo \
|
||||
ini_reader.cmo \
|
||||
index_parser.cmo \
|
||||
index_parser_tests.cmo
|
||||
index_parser_tests_XOBJECTS = $(index_parser_tests_BOBJECTS:.cmo=.cmx)
|
||||
|
||||
# Can't call the following as <test>_OBJECTS because automake gets confused.
|
||||
if HAVE_OCAMLOPT
|
||||
index_parser_tests_THEOBJECTS = $(index_parser_tests_XOBJECTS)
|
||||
index_parser_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS)
|
||||
else
|
||||
index_parser_tests_THEOBJECTS = $(index_parser_tests_BOBJECTS)
|
||||
index_parser_tests.cmo: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS)
|
||||
endif
|
||||
|
||||
index_parser_tests_DEPENDENCIES = \
|
||||
$(index_parser_tests_THEOBJECTS) \
|
||||
../common/mltools/mltools.$(MLARCHIVE) \
|
||||
$(top_builddir)/ocaml-link.sh
|
||||
index_parser_tests_LINK = \
|
||||
$(top_builddir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \
|
||||
$(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) $(OCAMLLINKFLAGS) \
|
||||
$(index_parser_tests_THEOBJECTS) -o $@
|
||||
|
||||
TESTS = \
|
||||
test-docs.sh \
|
||||
test-virt-builder-cacheall.sh \
|
||||
test-virt-builder-list.sh \
|
||||
test-virt-index-validate.sh \
|
||||
$(SLOW_TESTS)
|
||||
check_PROGRAMS =
|
||||
|
||||
TESTS += test-virt-builder-list-simplestreams.sh
|
||||
|
||||
if ENABLE_APPLIANCE
|
||||
TESTS += test-virt-builder.sh
|
||||
endif ENABLE_APPLIANCE
|
||||
if HAVE_OCAML_PKG_OUNIT
|
||||
check_PROGRAMS += index_parser_tests
|
||||
TESTS += index_parser_tests
|
||||
endif
|
||||
|
||||
check-valgrind:
|
||||
$(MAKE) VG="@VG@" check
|
||||
|
||||
# Slow tests.
|
||||
|
||||
SLOW_TESTS = \
|
||||
$(console_test_scripts) \
|
||||
test-virt-builder-planner.sh \
|
||||
test-virt-builder-repository.sh
|
||||
|
||||
check-slow:
|
||||
$(MAKE) check TESTS="$(SLOW_TESTS)" SLOW=1
|
||||
|
||||
# Test that the supplied guests boot with a serial console.
|
||||
#
|
||||
# Note that in future we might decide to make the serial console a
|
||||
# feature, eg. `virt-builder --add-serial-console' or `virt-builder
|
||||
# --remove-serial-console', so don't assume that having these tests
|
||||
# means that a serial console is a requirement.
|
||||
console_test_scripts := \
|
||||
test-console-centos-7.2.sh \
|
||||
test-console-rhel-6.8.sh \
|
||||
test-console-rhel-7.2.sh \
|
||||
test-console-debian-7.sh \
|
||||
test-console-debian-8.sh \
|
||||
test-console-fedora-24.sh \
|
||||
test-console-ubuntu-12.04.sh \
|
||||
test-console-ubuntu-14.04.sh \
|
||||
test-console-ubuntu-16.04.sh \
|
||||
test-console-ubuntu-18.04.sh
|
||||
|
||||
test-console-%.sh:
|
||||
rm -f $@ $@-t
|
||||
f=`echo "$@" | $(SED) 's/test-console-\(.*\).sh/\1/'`; \
|
||||
echo 'script=$@ exec $$srcdir/test-console.sh' "$$f" > $@-t
|
||||
chmod 0755 $@-t
|
||||
mv $@-t $@
|
||||
|
||||
CLEANFILES += \
|
||||
$(console_test_scripts) \
|
||||
console-*.img \
|
||||
console-*.out
|
||||
|
||||
# OCaml dependencies.
|
||||
.depend: $(srcdir)/*.mli $(srcdir)/*.ml osinfo_config.mli osinfo_config.ml
|
||||
$(top_builddir)/ocaml-dep.sh $^
|
||||
-include .depend
|
||||
|
||||
endif
|
||||
|
||||
.PHONY: docs
|
||||
|
||||
# virt-builder's default repository
|
||||
|
||||
repoconfdir = $(sysconfdir)/xdg/virt-builder/repos.d
|
||||
repoconf_DATA = libguestfs.conf libguestfs.gpg \
|
||||
opensuse.conf opensuse.gpg
|
||||
|
||||
install-exec-hook:
|
||||
$(LN_S) -f xdg/virt-builder $(DESTDIR)$(sysconfdir)/virt-builder
|
||||
|
||||
# Build a small C index validator program.
|
||||
bin_PROGRAMS += virt-index-validate
|
||||
|
||||
virt_index_validate_SOURCES = \
|
||||
index-parse.y \
|
||||
index-scan.l \
|
||||
index-struct.h \
|
||||
index-struct.c \
|
||||
index-validate.c
|
||||
|
||||
virt_index_validate_CPPFLAGS = \
|
||||
-DLOCALEBASEDIR=\""$(datadir)/locale"\" \
|
||||
-I. \
|
||||
-I$(top_builddir) \
|
||||
-I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \
|
||||
-I$(top_srcdir)/common/utils \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/include
|
||||
virt_index_validate_CFLAGS = \
|
||||
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
|
||||
-Wno-unused-macros
|
||||
virt_index_validate_LDADD = \
|
||||
$(LTLIBINTL) \
|
||||
../gnulib/lib/libgnu.la
|
||||
|
||||
man_MANS += virt-index-validate.1
|
||||
noinst_DATA += $(top_builddir)/website/virt-index-validate.1.html
|
||||
|
||||
virt-index-validate.1 $(top_builddir)/website/virt-index-validate.1.html: stamp-virt-index-validate.pod
|
||||
|
||||
stamp-virt-index-validate.pod: virt-index-validate.pod
|
||||
$(PODWRAPPER) \
|
||||
--man virt-index-validate.1 \
|
||||
--html $(top_builddir)/website/virt-index-validate.1.html \
|
||||
--license GPLv2+ \
|
||||
--warning safe \
|
||||
$<
|
||||
touch $@
|
||||
|
||||
CLEANFILES += \
|
||||
index-parse.c \
|
||||
index-parse.h \
|
||||
index-scan.c
|
||||
|
||||
BUILT_SOURCES = index-parse.h
|
||||
|
||||
# Apparently there's no clean way with Automake to not have them
|
||||
# in the distribution, so just remove them from the distdir.
|
||||
dist-hook:
|
||||
rm -f $(distdir)/index-parse.c $(distdir)/index-parse.h $(distdir)/index-scan.c
|
||||
@@ -1,794 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Common_gettext.Gettext
|
||||
|
||||
module G = Guestfs
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Unix_utils
|
||||
open Password
|
||||
open Planner
|
||||
open Utils
|
||||
|
||||
open Cmdline
|
||||
open Customize_cmdline
|
||||
|
||||
open Unix
|
||||
open Printf
|
||||
|
||||
let () = Random.self_init ()
|
||||
|
||||
let remove_duplicates index =
|
||||
let compare_revisions rev1 rev2 =
|
||||
match rev1, rev2 with
|
||||
| Rev_int n1, Rev_int n2 -> compare n1 n2
|
||||
| Rev_string s1, Rev_int n2 -> compare s1 (string_of_int n2)
|
||||
| Rev_int n1, Rev_string s2 -> compare (string_of_int n1) s2
|
||||
| Rev_string s1, Rev_string s2 -> compare s1 s2
|
||||
in
|
||||
(* Fill an hash with the higher revision of the available
|
||||
* (name, arch) tuples, so it possible to ignore duplicates,
|
||||
* and versions with a lower revision.
|
||||
*)
|
||||
let nseen = Hashtbl.create 13 in
|
||||
List.iter (
|
||||
fun (name, { Index.arch; revision }) ->
|
||||
let id = name, arch in
|
||||
try
|
||||
let rev = Hashtbl.find nseen id in
|
||||
if compare_revisions rev revision > 0 then
|
||||
Hashtbl.replace nseen id revision
|
||||
with Not_found ->
|
||||
Hashtbl.add nseen id revision
|
||||
) index;
|
||||
List.filter (
|
||||
fun (name, { Index.arch ; revision }) ->
|
||||
let id = name, arch in
|
||||
try
|
||||
let rev = Hashtbl.find nseen (name, arch) in
|
||||
(* Take the first occurrency with the higher revision,
|
||||
* removing it from the hash so the other occurrencies
|
||||
* are ignored.
|
||||
*)
|
||||
if revision = rev then (
|
||||
Hashtbl.remove nseen id;
|
||||
true
|
||||
) else
|
||||
false
|
||||
with Not_found ->
|
||||
(* Already taken, so ignore. *)
|
||||
false
|
||||
) index
|
||||
|
||||
(* Look for the specified os-version, resolving it as alias first. *)
|
||||
let selected_cli_item cmdline index =
|
||||
let arg =
|
||||
(* Try to resolve the alias. *)
|
||||
try
|
||||
let item =
|
||||
List.find (
|
||||
fun (name, { Index.aliases }) ->
|
||||
match aliases with
|
||||
| None -> false
|
||||
| Some l -> List.mem cmdline.arg l
|
||||
) index in
|
||||
fst item
|
||||
with Not_found -> cmdline.arg in
|
||||
let item =
|
||||
try List.find (
|
||||
fun (name, { Index.arch = a }) ->
|
||||
name = arg && cmdline.arch = normalize_arch (Index.string_of_arch a)
|
||||
) index
|
||||
with Not_found ->
|
||||
error (f_"cannot find os-version ‘%s’ with architecture ‘%s’.\nUse --list to list available guest types.")
|
||||
arg cmdline.arch in
|
||||
item
|
||||
|
||||
let main () =
|
||||
(* Command line argument parsing - see cmdline.ml. *)
|
||||
let cmdline = parse_cmdline () in
|
||||
|
||||
(* If debugging, echo the command line arguments and the sources. *)
|
||||
if verbose () then (
|
||||
printf "command line:";
|
||||
List.iter (printf " %s") (Array.to_list Sys.argv);
|
||||
print_newline ();
|
||||
List.iteri (
|
||||
fun i (source, fingerprint) ->
|
||||
printf "source[%d] = (%S, %S)\n" i source fingerprint
|
||||
) cmdline.sources
|
||||
);
|
||||
|
||||
(* Handle some modes here, some later on. *)
|
||||
let mode =
|
||||
match cmdline.mode with
|
||||
| `Get_kernel -> (* --get-kernel is really a different program ... *)
|
||||
let cmd = [ "virt-get-kernel" ] @
|
||||
(if verbose () then [ "--verbose" ] else []) @
|
||||
(if trace () then [ "-x" ] else []) @
|
||||
(match cmdline.format with
|
||||
| None -> []
|
||||
| Some format -> [ "--format"; format ]) @
|
||||
(match cmdline.output with
|
||||
| None -> []
|
||||
| Some output -> [ "--output"; output ]) @
|
||||
[ "--add"; cmdline.arg ] in
|
||||
exit (run_command cmd)
|
||||
|
||||
| `Delete_cache -> (* --delete-cache *)
|
||||
(match cmdline.cache with
|
||||
| Some cachedir ->
|
||||
message (f_"Deleting: %s") cachedir;
|
||||
Cache.clean_cachedir cachedir;
|
||||
exit 0
|
||||
| None ->
|
||||
error (f_"could not find cache directory. Is $HOME set?")
|
||||
)
|
||||
|
||||
| (`Install|`List|`Notes|`Print_cache|`Cache_all) as mode -> mode in
|
||||
|
||||
(* Check various programs/dependencies are installed. *)
|
||||
|
||||
(* Check that gpg is installed. Optional as long as the user
|
||||
* disables all signature checks.
|
||||
*)
|
||||
if cmdline.check_signature then (
|
||||
let cmd = sprintf "%s --help >/dev/null 2>&1" cmdline.gpg in
|
||||
if cmdline.gpg = "" || shell_command cmd <> 0 then
|
||||
error (f_"no GNU Privacy Guard (GnuPG, gpg) binary was found.\n\nEither gpg v1 or v2 can be installed to check signatures. Virt-builder looks for a binary called either ‘gpg2’ or ‘gpg‘ on the $PATH. You can also specify a binary using the ‘--gpg’ option. If you don't want to check signatures, use ’--no-check-signature’ but note that this may make you vulnerable to Man-In-The-Middle attacks.")
|
||||
);
|
||||
|
||||
(* Check that curl works. *)
|
||||
let cmd = sprintf "%s --help >/dev/null 2>&1" cmdline.curl in
|
||||
if shell_command cmd <> 0 then
|
||||
error (f_"curl is not installed (or does not work)");
|
||||
|
||||
(* Check that virt-resize works. *)
|
||||
let cmd = "virt-resize --help >/dev/null 2>&1" in
|
||||
if shell_command cmd <> 0 then
|
||||
error (f_"virt-resize is not installed (or does not work)");
|
||||
|
||||
(* Create the cache. *)
|
||||
let cache =
|
||||
match cmdline.cache with
|
||||
| None -> None
|
||||
| Some dir ->
|
||||
try Some (Cache.create ~directory:dir)
|
||||
with exn ->
|
||||
warning (f_"cache %s: %s") dir (Printexc.to_string exn);
|
||||
warning (f_"disabling the cache");
|
||||
None
|
||||
in
|
||||
|
||||
(* Create a single temporary directory for all the small-or-so
|
||||
* temporary files that Downloader, Sigchecker, etc, are going
|
||||
* create.
|
||||
*)
|
||||
let tmpdir = Mkdtemp.temp_dir "virt-builder." in
|
||||
rmdir_on_exit tmpdir;
|
||||
|
||||
(* Download the sources. *)
|
||||
let downloader = Downloader.create ~curl:cmdline.curl ~cache ~tmpdir in
|
||||
let repos = Sources.read_sources () in
|
||||
let sources = List.map (
|
||||
fun (source, fingerprint) ->
|
||||
{
|
||||
Sources.name = source; uri = source;
|
||||
gpgkey = Utils.Fingerprint fingerprint;
|
||||
proxy = Curl.SystemProxy;
|
||||
format = Sources.FormatNative;
|
||||
}
|
||||
) cmdline.sources in
|
||||
let sources = List.append sources repos in
|
||||
let index : Index.index =
|
||||
List.concat (
|
||||
List.map (
|
||||
fun source ->
|
||||
let sigchecker =
|
||||
Sigchecker.create ~gpg:cmdline.gpg
|
||||
~check_signature:cmdline.check_signature
|
||||
~gpgkey:source.Sources.gpgkey
|
||||
~tmpdir in
|
||||
match source.Sources.format with
|
||||
| Sources.FormatNative ->
|
||||
Index_parser.get_index ~downloader ~sigchecker source
|
||||
| Sources.FormatSimpleStreams ->
|
||||
Simplestreams_parser.get_index ~downloader ~sigchecker source
|
||||
) sources
|
||||
) in
|
||||
let index = remove_duplicates index in
|
||||
|
||||
(* Now handle the remaining modes. *)
|
||||
let mode =
|
||||
match mode with
|
||||
| `List -> (* --list *)
|
||||
let sources, index =
|
||||
match cmdline.arg with
|
||||
| "" -> sources, index (* no template -> all the available ones *)
|
||||
| arg -> (* just the specified template *)
|
||||
let item = selected_cli_item cmdline index in
|
||||
[], [item] in
|
||||
List_entries.list_entries ~list_format:cmdline.list_format ~sources index;
|
||||
exit 0
|
||||
|
||||
| `Print_cache -> (* --print-cache *)
|
||||
(match cache with
|
||||
| Some cache ->
|
||||
let l = List.filter (
|
||||
fun (_, { Index.hidden }) ->
|
||||
hidden <> true
|
||||
) index in
|
||||
let l = List.map (
|
||||
fun (name, { Index.revision; arch }) ->
|
||||
(name, arch, revision)
|
||||
) l in
|
||||
Cache.print_item_status cache ~header:true l
|
||||
| None -> printf (f_"no cache directory\n")
|
||||
);
|
||||
exit 0
|
||||
|
||||
| `Cache_all -> (* --cache-all-templates *)
|
||||
(match cache with
|
||||
| None ->
|
||||
error (f_"no cache directory")
|
||||
| Some _ ->
|
||||
List.iter (
|
||||
fun (name,
|
||||
{ Index.revision; file_uri; proxy; arch }) ->
|
||||
let template = name, arch, revision in
|
||||
message (f_"Downloading: %s") file_uri;
|
||||
let progress_bar = not (quiet ()) in
|
||||
ignore (Downloader.download downloader ~template ~progress_bar
|
||||
~proxy file_uri)
|
||||
) index;
|
||||
exit 0
|
||||
);
|
||||
|
||||
| (`Install|`Notes) as mode -> mode in
|
||||
|
||||
(* Which os-version (ie. index entry)? *)
|
||||
let item = selected_cli_item cmdline index in
|
||||
let arg = fst item in
|
||||
let entry = snd item in
|
||||
let sigchecker = entry.Index.sigchecker in
|
||||
|
||||
(match mode with
|
||||
| `Notes -> (* --notes *)
|
||||
let notes =
|
||||
Languages.find_notes (Languages.languages ()) entry.Index.notes in
|
||||
(match notes with
|
||||
| notes :: _ ->
|
||||
print_endline notes
|
||||
| [] ->
|
||||
printf (f_"There are no notes for %s\n") arg
|
||||
);
|
||||
exit 0
|
||||
|
||||
| `Install ->
|
||||
() (* fall through to create the guest *)
|
||||
);
|
||||
|
||||
(* --- If we get here, we want to create a guest. --- *)
|
||||
|
||||
(* Warn if the user might be writing to a partition on a USB key. *)
|
||||
(match cmdline.output with
|
||||
| Some device when is_partition device ->
|
||||
if cmdline.warn_if_partition then
|
||||
warning (f_"output device (%s) is a partition. If you are writing to a USB key or external drive then you probably need to write to the whole device, not to a partition. If this warning is wrong then you can disable it with --no-warn-if-partition")
|
||||
device;
|
||||
| Some _ | None -> ()
|
||||
);
|
||||
|
||||
(* Download the template, or it may be in the cache. *)
|
||||
let template =
|
||||
let template, delete_on_exit =
|
||||
let { Index.revision; file_uri; proxy } = entry in
|
||||
let template = arg, Index.Arch cmdline.arch, revision in
|
||||
message (f_"Downloading: %s") file_uri;
|
||||
let progress_bar = not (quiet ()) in
|
||||
Downloader.download downloader ~template ~progress_bar ~proxy
|
||||
file_uri in
|
||||
if delete_on_exit then unlink_on_exit template;
|
||||
template in
|
||||
|
||||
(* Check the signature of the file. *)
|
||||
let () =
|
||||
match entry with
|
||||
(* New-style: Using a checksum. *)
|
||||
| { Index.checksums = Some csums } ->
|
||||
(match Checksums.verify_checksums csums template with
|
||||
| Checksums.Good_checksum -> ()
|
||||
| Checksums.Mismatched_checksum (csum, csum_actual) ->
|
||||
error (f_"%s checksum of template did not match the expected checksum!\n found checksum: %s\n expected checksum: %s\nTry:\n - Use the ‘-v’ option and look for earlier error messages.\n - Delete the cache: virt-builder --delete-cache\n - Check no one has tampered with the website or your network!")
|
||||
(Checksums.string_of_csum_t csum) csum_actual (Checksums.string_of_csum csum)
|
||||
| Checksums.Missing_file ->
|
||||
error (f_"%s: template not downloaded or deleted. You may have run ‘virt-builder --delete-cache’ in parallel.")
|
||||
template
|
||||
)
|
||||
|
||||
| { Index.checksums = None } ->
|
||||
(* Old-style: detached signature. *)
|
||||
let sigfile =
|
||||
match entry with
|
||||
| { Index.signature_uri = None } -> None
|
||||
| { Index.signature_uri = Some signature_uri } ->
|
||||
let sigfile, delete_on_exit =
|
||||
Downloader.download downloader signature_uri in
|
||||
if delete_on_exit then unlink_on_exit sigfile;
|
||||
Some sigfile in
|
||||
|
||||
Sigchecker.verify_detached sigchecker template sigfile in
|
||||
|
||||
(* For an explanation of the Planner, see:
|
||||
* http://rwmj.wordpress.com/2013/12/14/writing-a-planner-to-solve-a-tricky-programming-optimization-problem/
|
||||
*)
|
||||
|
||||
(* Planner: Input tags. *)
|
||||
let itags =
|
||||
let { Index.size; format } = entry in
|
||||
let format_tag =
|
||||
match format with
|
||||
| None -> []
|
||||
| Some format -> [`Format, format] in
|
||||
let compression_tag =
|
||||
match detect_file_type template with
|
||||
| `XZ -> [ `XZ, "" ]
|
||||
| `GZip | `Tar | `Zip ->
|
||||
error (f_"input file (%s) has an unsupported type") template
|
||||
| `Unknown -> [] in
|
||||
[ `Template, ""; `Filename, template; `Size, Int64.to_string size ] @
|
||||
format_tag @ compression_tag in
|
||||
|
||||
(* Planner: Goal. *)
|
||||
let output_filename, output_format =
|
||||
match cmdline.output, cmdline.format with
|
||||
| None, None -> sprintf "%s.img" arg, "raw"
|
||||
| None, Some "raw" -> sprintf "%s.img" arg, "raw"
|
||||
| None, Some format -> sprintf "%s.%s" arg format, format
|
||||
| Some output, None -> output, "raw"
|
||||
| Some output, Some format -> output, format in
|
||||
|
||||
if is_char_device output_filename then
|
||||
error (f_"cannot output to a character device or /dev/null");
|
||||
|
||||
let blockdev_getsize64 dev =
|
||||
let cmd = sprintf "blockdev --getsize64 %s" (quote dev) in
|
||||
let lines = external_command cmd in
|
||||
assert (List.length lines >= 1);
|
||||
Int64.of_string (List.hd lines)
|
||||
in
|
||||
let output_is_block_dev, blockdev_size =
|
||||
let b = is_block_device output_filename in
|
||||
let sz = if b then blockdev_getsize64 output_filename else 0L in
|
||||
b, sz in
|
||||
|
||||
let output_size =
|
||||
let { Index.size = original_image_size } = entry in
|
||||
|
||||
let size =
|
||||
match cmdline.size with
|
||||
| Some size -> size
|
||||
(* --size parameter missing, output to file: use original image size *)
|
||||
| None when not output_is_block_dev -> original_image_size
|
||||
(* --size parameter missing, block device: use block device size *)
|
||||
| None -> blockdev_size in
|
||||
|
||||
if size < original_image_size then
|
||||
error (f_"images cannot be shrunk, the output size is too small for this image. Requested size = %s, minimum size = %s")
|
||||
(human_size size) (human_size original_image_size)
|
||||
else if output_is_block_dev && output_format = "raw" && size > blockdev_size then
|
||||
error (f_"output size is too large for this block device. Requested size = %s, output block device = %s, output block device size = %s")
|
||||
(human_size size) output_filename (human_size blockdev_size);
|
||||
size in
|
||||
|
||||
(* Goal: must *)
|
||||
let must = [
|
||||
`Filename, output_filename;
|
||||
`Size, Int64.to_string output_size;
|
||||
`Format, output_format
|
||||
] in
|
||||
|
||||
(* Goal: must not *)
|
||||
let must_not = [ `Template, ""; `XZ, "" ] in
|
||||
|
||||
let cache_dir = (open_guestfs ())#get_cachedir () in
|
||||
|
||||
(* Planner: Transitions. *)
|
||||
let transitions itags =
|
||||
let is t = List.mem_assoc t itags in
|
||||
let is_not t = not (is t) in
|
||||
let remove = List.remove_assoc in
|
||||
let ret = ref [] in
|
||||
|
||||
let infile = List.assoc `Filename itags in
|
||||
|
||||
(* The scheme for weights ranges from 0 = free to 100 = most expensive:
|
||||
*
|
||||
* 0 = free operations like renaming a file in the same directory
|
||||
* 10 = in-place conversions (like [qemu-img resize])
|
||||
* 20 = copy or move a file between two local filesystems
|
||||
* 30 = copy and convert a file between two local filesystems
|
||||
* 40 = copy a file within the same local filesystem
|
||||
* 50 = copy and convert a file within the same local filesystem
|
||||
* 80 = copy, move, convert if source or target is on network filesystem
|
||||
* 100 = complex operations like virt-resize
|
||||
*
|
||||
* Copies and moves across different local filesystems are
|
||||
* cheaper than copies within the same filesystem. The
|
||||
* theory because less bandwith is available if both source
|
||||
* and destination hit the same device (except in the special
|
||||
* case of moving within a filesystem which is free).
|
||||
*
|
||||
* We could estimate weights better by looking at file sizes.
|
||||
*)
|
||||
let weight task otags =
|
||||
let outfile = List.assoc `Filename otags in
|
||||
|
||||
(* If infile/outfile don't exist, get the containing directory. *)
|
||||
let infile =
|
||||
if Sys.file_exists infile then infile else Filename.dirname infile in
|
||||
let outfile =
|
||||
if Sys.file_exists outfile then outfile else Filename.dirname outfile in
|
||||
|
||||
match task with
|
||||
| `Virt_resize -> 100 (* virt-resize is a special case*)
|
||||
| (`Copy|`Move|`Pxzcat|`Disk_resize|`Convert) as task ->
|
||||
if StatVFS.is_network_filesystem infile ||
|
||||
StatVFS.is_network_filesystem outfile
|
||||
then 80 (* NFS etc. *)
|
||||
else (
|
||||
let across = (lstat infile).st_dev <> (lstat outfile).st_dev in
|
||||
match task, across with
|
||||
| `Move, false -> 0 (* rename in same filesystem *)
|
||||
| `Disk_resize, _ -> 10 (* in-place conversion *)
|
||||
| `Move, true (* move or copy between two filesystems *)
|
||||
| `Copy, true -> 20
|
||||
| (`Pxzcat|`Convert), true -> 30 (* convert between two local fses*)
|
||||
| `Copy, false -> 40 (* copy within same filesystem *)
|
||||
| (`Pxzcat|`Convert), false -> 50 (* convert with same local fs*)
|
||||
)
|
||||
in
|
||||
|
||||
(* Add a transition to the returned list. *)
|
||||
let tr task otags = List.push_front (task, weight task otags, otags) ret in
|
||||
|
||||
(* Since the final plan won't run in parallel, we don't only need
|
||||
* to choose unique tempfiles per transition, so this is OK:
|
||||
*)
|
||||
let tempfile = Filename.temp_file ~temp_dir:cache_dir "vb" ".img" in
|
||||
unlink_on_exit tempfile;
|
||||
|
||||
(* Always possible to copy from one place to another. The only
|
||||
* thing a copy does is to remove the template tag (since it's always
|
||||
* copied out of the cache directory).
|
||||
*)
|
||||
if infile <> output_filename then
|
||||
tr `Copy ((`Filename, output_filename) :: remove `Template itags);
|
||||
tr `Copy ((`Filename, tempfile) :: remove `Template itags);
|
||||
|
||||
(* We can rename a file instead of copying, but don't rename the
|
||||
* cache copy!
|
||||
*)
|
||||
if is_not `Template then (
|
||||
if not output_is_block_dev && infile <> output_filename then
|
||||
tr `Move ((`Filename, output_filename) :: itags);
|
||||
tr `Move ((`Filename, tempfile) :: itags)
|
||||
);
|
||||
|
||||
if is `XZ then (
|
||||
(* If the input is XZ-compressed, then we can run xzcat, either
|
||||
* to the output file or to a temp file.
|
||||
*)
|
||||
if not output_is_block_dev && infile <> output_filename then
|
||||
tr `Pxzcat
|
||||
((`Filename, output_filename) :: remove `XZ (remove `Template itags));
|
||||
tr `Pxzcat
|
||||
((`Filename, tempfile) :: remove `XZ (remove `Template itags));
|
||||
)
|
||||
else (
|
||||
(* If the input is NOT compressed then we could run virt-resize
|
||||
* if it makes sense to resize the image. Note that virt-resize
|
||||
* can do both size and format conversions.
|
||||
*)
|
||||
let old_size = Int64.of_string (List.assoc `Size itags) in
|
||||
let headroom = 256L *^ 1024L *^ 1024L in
|
||||
if output_size >= old_size +^ headroom then (
|
||||
if infile <> output_filename then
|
||||
tr `Virt_resize
|
||||
((`Size, Int64.to_string output_size) ::
|
||||
(`Filename, output_filename) ::
|
||||
(`Format, output_format) :: (remove `Template itags));
|
||||
tr `Virt_resize
|
||||
((`Size, Int64.to_string output_size) ::
|
||||
(`Filename, tempfile) ::
|
||||
(`Format, output_format) :: (remove `Template itags))
|
||||
)
|
||||
|
||||
(* If the size increase is smaller than the amount of headroom
|
||||
* inside the disk image, then virt-resize won't work. However
|
||||
* we can do a disk resize (using 'qemu-img resize') instead,
|
||||
* although it won't resize the filesystems for the user.
|
||||
*
|
||||
* 'qemu-img resize' works on the file in-place and won't change
|
||||
* the format. It must not be run on a template directly.
|
||||
*
|
||||
* Don't run 'qemu-img resize' on an auto format. This is to
|
||||
* force an explicit conversion step to a real format.
|
||||
*)
|
||||
else if output_size > old_size && is_not `Template
|
||||
&& List.mem_assoc `Format itags then
|
||||
tr `Disk_resize ((`Size, Int64.to_string output_size) :: itags);
|
||||
|
||||
(* qemu-img convert is always possible, and quicker. It doesn't
|
||||
* resize, but it does change the format.
|
||||
*)
|
||||
if infile <> output_filename then
|
||||
tr `Convert
|
||||
((`Filename, output_filename) :: (`Format, output_format) ::
|
||||
(remove `Template itags));
|
||||
tr `Convert
|
||||
((`Filename, tempfile) :: (`Format, output_format) ::
|
||||
(remove `Template itags));
|
||||
);
|
||||
|
||||
(* Return the list of possible transitions. *)
|
||||
!ret
|
||||
in
|
||||
|
||||
(* Plan how to create the disk image. *)
|
||||
message (f_"Planning how to build this image");
|
||||
let plan =
|
||||
match plan ~max_depth:5 transitions itags ~must ~must_not with
|
||||
| Some plan -> plan
|
||||
| None ->
|
||||
error (f_"no plan could be found for making a disk image with\nthe required size, format etc. This is a bug in libguestfs!\nPlease file a bug, giving the command line arguments you used.") in
|
||||
|
||||
(* Print out the plan. *)
|
||||
if verbose () then (
|
||||
let print_tags tags =
|
||||
(try
|
||||
let v = List.assoc `Filename tags in printf " +filename=%s" v
|
||||
with Not_found -> ());
|
||||
(try
|
||||
let v = List.assoc `Size tags in printf " +size=%s" v
|
||||
with Not_found -> ());
|
||||
(try
|
||||
let v = List.assoc `Format tags in printf " +format=%s" v
|
||||
with Not_found -> ());
|
||||
if List.mem_assoc `Template tags then printf " +template";
|
||||
if List.mem_assoc `XZ tags then printf " +xz"
|
||||
in
|
||||
let print_task = function
|
||||
| `Copy -> printf "cp"
|
||||
| `Move -> printf "mv"
|
||||
| `Pxzcat -> printf "pxzcat"
|
||||
| `Virt_resize -> printf "virt-resize"
|
||||
| `Disk_resize -> printf "qemu-img resize"
|
||||
| `Convert -> printf "qemu-img convert"
|
||||
in
|
||||
|
||||
List.iteri (
|
||||
fun i (itags, task, otags) ->
|
||||
printf "%d: itags:" i;
|
||||
print_tags itags;
|
||||
printf "\n";
|
||||
printf "%d: task : " i;
|
||||
print_task task;
|
||||
printf "\n";
|
||||
printf "%d: otags:" i;
|
||||
print_tags otags;
|
||||
printf "\n\n%!"
|
||||
) plan
|
||||
);
|
||||
|
||||
(* Delete the output file before we finish. However don't delete it
|
||||
* if it's block device, or if --no-delete-on-failure is set.
|
||||
*)
|
||||
let delete_output_file =
|
||||
ref (cmdline.delete_on_failure && not output_is_block_dev) in
|
||||
let delete_file () =
|
||||
if !delete_output_file then
|
||||
try unlink output_filename with _ -> ()
|
||||
in
|
||||
at_exit delete_file;
|
||||
|
||||
(* Carry out the plan. *)
|
||||
List.iter (
|
||||
function
|
||||
| itags, `Copy, otags ->
|
||||
let ifile = List.assoc `Filename itags in
|
||||
let ofile = List.assoc `Filename otags in
|
||||
message (f_"Copying");
|
||||
let cmd = [ "cp"; ifile; ofile ] in
|
||||
if run_command cmd <> 0 then exit 1
|
||||
|
||||
| itags, `Move, otags ->
|
||||
let ifile = List.assoc `Filename itags in
|
||||
let ofile = List.assoc `Filename otags in
|
||||
let cmd = [ "mv"; ifile; ofile ] in
|
||||
if run_command cmd <> 0 then exit 1
|
||||
|
||||
| itags, `Pxzcat, otags ->
|
||||
let ifile = List.assoc `Filename itags in
|
||||
let ofile = List.assoc `Filename otags in
|
||||
message (f_"Uncompressing");
|
||||
Pxzcat.pxzcat ifile ofile
|
||||
|
||||
| itags, `Virt_resize, otags ->
|
||||
let ifile = List.assoc `Filename itags in
|
||||
let iformat =
|
||||
try Some (List.assoc `Format itags) with Not_found -> None in
|
||||
let ofile = List.assoc `Filename otags in
|
||||
let osize = Int64.of_string (List.assoc `Size otags) in
|
||||
let osize = roundup64 osize 512L in
|
||||
let oformat = List.assoc `Format otags in
|
||||
let { Index.expand; lvexpand } = entry in
|
||||
message (f_"Resizing (using virt-resize) to expand the disk to %s")
|
||||
(human_size osize);
|
||||
let preallocation = if oformat = "qcow2" then Some "metadata" else None in
|
||||
let () =
|
||||
let g = open_guestfs () in
|
||||
g#disk_create ?preallocation ofile oformat osize in
|
||||
let cmd = [ "virt-resize" ] @
|
||||
(if verbose () then [ "--verbose" ] else [ "--quiet" ]) @
|
||||
(if is_block_device ofile then [ "--no-sparse" ] else []) @
|
||||
(match iformat with
|
||||
| None -> []
|
||||
| Some iformat -> [ "--format"; iformat ]) @
|
||||
[ "--output-format"; oformat ] @
|
||||
(match expand with
|
||||
| None -> []
|
||||
| Some expand -> [ "--expand"; expand ]) @
|
||||
(match lvexpand with
|
||||
| None -> []
|
||||
| Some lvexpand -> [ "--lv-expand"; lvexpand ]) @
|
||||
[ "--unknown-filesystems"; "error"; ifile; ofile ] in
|
||||
if run_command cmd <> 0 then exit 1
|
||||
|
||||
| itags, `Disk_resize, otags ->
|
||||
let ofile = List.assoc `Filename otags in
|
||||
let osize = Int64.of_string (List.assoc `Size otags) in
|
||||
let osize = roundup64 osize 512L in
|
||||
message (f_"Resizing container (but not filesystems) to expand the disk to %s")
|
||||
(human_size osize);
|
||||
let cmd = sprintf "qemu-img resize %s %Ld%s"
|
||||
(quote ofile) osize (if verbose () then "" else " >/dev/null") in
|
||||
if shell_command cmd <> 0 then exit 1
|
||||
|
||||
| itags, `Convert, otags ->
|
||||
let ifile = List.assoc `Filename itags in
|
||||
let iformat =
|
||||
try Some (List.assoc `Format itags) with Not_found -> None in
|
||||
let ofile = List.assoc `Filename otags in
|
||||
let oformat = List.assoc `Format otags in
|
||||
(match iformat with
|
||||
| None -> message (f_"Converting to %s") oformat
|
||||
| Some f -> message (f_"Converting %s to %s") f oformat
|
||||
);
|
||||
let cmd = sprintf "qemu-img convert%s %s -O %s %s%s"
|
||||
(match iformat with
|
||||
| None -> ""
|
||||
| Some iformat -> sprintf " -f %s" (quote iformat))
|
||||
(quote ifile) (quote oformat) (quote (qemu_input_filename ofile))
|
||||
(if verbose () then "" else " >/dev/null 2>&1") in
|
||||
if shell_command cmd <> 0 then exit 1
|
||||
) plan;
|
||||
|
||||
(* Now mount the output disk so we can make changes. *)
|
||||
message (f_"Opening the new disk");
|
||||
let g =
|
||||
let g = open_guestfs () in
|
||||
|
||||
Option.may g#set_memsize cmdline.memsize;
|
||||
Option.may g#set_smp cmdline.smp;
|
||||
g#set_network cmdline.network;
|
||||
|
||||
(* The output disk is being created, so use cache=unsafe here. *)
|
||||
g#add_drive_opts ~format:output_format ~cachemode:"unsafe" output_filename;
|
||||
|
||||
(* Attach ISOs, if we have any. *)
|
||||
List.iter (
|
||||
fun (format, file) ->
|
||||
g#add_drive_opts ?format ~readonly:true file;
|
||||
) cmdline.attach;
|
||||
|
||||
g#launch ();
|
||||
|
||||
g in
|
||||
|
||||
(* Inspect the disk and mount it up. *)
|
||||
let root =
|
||||
match Array.to_list (g#inspect_os ()) with
|
||||
| [root] ->
|
||||
inspect_mount_root g root;
|
||||
root
|
||||
| _ ->
|
||||
error (f_"no guest operating systems or multiboot OS found in this disk image\nThis is a failure of the source repository. Use -v for more information.")
|
||||
in
|
||||
|
||||
Customize_run.run g root cmdline.customize_ops;
|
||||
|
||||
(* Collect some stats about the final output file.
|
||||
* Notes:
|
||||
* - These are virtual disk stats.
|
||||
* - Never fail here.
|
||||
*)
|
||||
let stats =
|
||||
if not (quiet ()) then (
|
||||
try
|
||||
(* Calculate the free space (in bytes) across all mounted
|
||||
* filesystems in the guest.
|
||||
*)
|
||||
let free_bytes, total_bytes =
|
||||
let filesystems = List.map snd (g#mountpoints ()) in
|
||||
let stats = List.map g#statvfs filesystems in
|
||||
let stats = List.map (
|
||||
fun { G.bfree; bsize; blocks } ->
|
||||
bfree *^ bsize, blocks *^ bsize
|
||||
) stats in
|
||||
List.fold_left (
|
||||
fun (f,t) (f',t') -> f +^ f', t +^ t'
|
||||
) (0L, 0L) stats in
|
||||
let free_percent = 100L *^ free_bytes /^ total_bytes in
|
||||
|
||||
Some (
|
||||
String.concat "\n" [
|
||||
sprintf "%30s: %s" (s_"Output file") output_filename;
|
||||
sprintf "%30s: %s" (s_"Output size") (human_size output_size);
|
||||
sprintf "%30s: %s" (s_"Output format") output_format;
|
||||
sprintf "%30s: %s" (s_"Total usable space")
|
||||
(human_size total_bytes);
|
||||
sprintf "%30s: %s (%Ld%%)" (s_"Free space")
|
||||
(human_size free_bytes) free_percent;
|
||||
] ^ "\n"
|
||||
)
|
||||
with
|
||||
_ -> None
|
||||
)
|
||||
else None in
|
||||
|
||||
(* Unmount everything and we're done! *)
|
||||
message (f_"Finishing off");
|
||||
|
||||
g#umount_all ();
|
||||
g#shutdown ();
|
||||
g#close ();
|
||||
|
||||
(* Because we used cache=unsafe when writing the output file, the
|
||||
* file might not be committed to disk. This is a problem if qemu is
|
||||
* immediately used afterwards with cache=none (which uses O_DIRECT
|
||||
* and therefore bypasses the host cache). In general you should not
|
||||
* use cache=none.
|
||||
*)
|
||||
if cmdline.sync then
|
||||
Fsync.file output_filename;
|
||||
|
||||
(* Now that we've finished the build, don't delete the output file on
|
||||
* exit.
|
||||
*)
|
||||
delete_output_file := false;
|
||||
|
||||
(* Print the stats calculated above. *)
|
||||
Pervasives.flush Pervasives.stdout;
|
||||
Pervasives.flush Pervasives.stderr;
|
||||
|
||||
Option.may print_string stats
|
||||
|
||||
let () = run_main_and_handle_errors main
|
||||
@@ -1,19 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2017 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
(* Nothing is exported. *)
|
||||
@@ -1,61 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open Utils
|
||||
|
||||
open Unix
|
||||
open Printf
|
||||
|
||||
let clean_cachedir dir =
|
||||
let cmd = [ "rm"; "-rf"; dir ] in
|
||||
ignore (run_command cmd);
|
||||
|
||||
type t = {
|
||||
directory : string;
|
||||
}
|
||||
|
||||
let create ~directory =
|
||||
if not (is_directory directory) then
|
||||
mkdir_p directory 0o755;
|
||||
{
|
||||
directory = directory;
|
||||
}
|
||||
|
||||
let cache_of_name t name arch revision =
|
||||
t.directory // sprintf "%s.%s.%s" name
|
||||
(Index.string_of_arch arch)
|
||||
(string_of_revision revision)
|
||||
|
||||
let is_cached t name arch revision =
|
||||
let filename = cache_of_name t name arch revision in
|
||||
Sys.file_exists filename
|
||||
|
||||
let print_item_status t ~header l =
|
||||
if header then (
|
||||
printf (f_"cache directory: %s\n") t.directory
|
||||
);
|
||||
List.iter (
|
||||
fun (name, arch, revision) ->
|
||||
let cached = is_cached t name arch revision in
|
||||
printf "%-24s %-10s %s\n" name (Index.string_of_arch arch)
|
||||
(if cached then s_"cached" else (*s_*)"no")
|
||||
) l
|
||||
@@ -1,45 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
(** This module represents a local cache. *)
|
||||
|
||||
val clean_cachedir : string -> unit
|
||||
(** [clean_cachedir dir] clean the specified cache directory. *)
|
||||
|
||||
type t
|
||||
(** The abstract data type. *)
|
||||
|
||||
val create : directory:string -> t
|
||||
(** Create the abstract type. *)
|
||||
|
||||
val cache_of_name : t -> string -> Index.arch -> Utils.revision -> string
|
||||
(** [cache_of_name t name arch revision] return the filename
|
||||
of the cached file. (Note: It doesn't check if the filename
|
||||
exists, this is just a simple string transformation). *)
|
||||
|
||||
val is_cached : t -> string -> Index.arch -> Utils.revision -> bool
|
||||
(** [is_cached t name arch revision] return whether the file with
|
||||
specified name, architecture and revision is cached. *)
|
||||
|
||||
val print_item_status : t -> header:bool -> (string * Index.arch * Utils.revision) list -> unit
|
||||
(** [print_item_status t header items] print the status in the cache
|
||||
of the specified items (which are tuples of name, architecture,
|
||||
and revision).
|
||||
|
||||
If [~header:true] then display a header with the path of the
|
||||
cache. *)
|
||||
@@ -1,334 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
(* Command line argument parsing. *)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Common_gettext.Gettext
|
||||
open Getopt.OptionName
|
||||
|
||||
open Customize_cmdline
|
||||
|
||||
open Utils
|
||||
|
||||
module G = Guestfs
|
||||
|
||||
open Unix
|
||||
open Printf
|
||||
|
||||
type cmdline = {
|
||||
mode : [ `Cache_all | `Delete_cache | `Get_kernel | `Install | `List
|
||||
| `Notes | `Print_cache ];
|
||||
arg : string;
|
||||
arch : string;
|
||||
attach : (string option * string) list;
|
||||
cache : string option;
|
||||
check_signature : bool;
|
||||
curl : string;
|
||||
customize_ops : Customize_cmdline.ops;
|
||||
delete_on_failure : bool;
|
||||
format : string option;
|
||||
gpg : string;
|
||||
list_format : List_entries.format;
|
||||
memsize : int option;
|
||||
network : bool;
|
||||
output : string option;
|
||||
size : int64 option;
|
||||
smp : int option;
|
||||
sources : (string * string) list;
|
||||
sync : bool;
|
||||
warn_if_partition : bool;
|
||||
}
|
||||
|
||||
let parse_cmdline () =
|
||||
let mode = ref `Install in
|
||||
let list_mode () = mode := `List in
|
||||
let notes_mode () = mode := `Notes in
|
||||
let get_kernel_mode () = mode := `Get_kernel in
|
||||
let cache_all_mode () = mode := `Cache_all in
|
||||
let print_cache_mode () = mode := `Print_cache in
|
||||
let delete_cache_mode () = mode := `Delete_cache in
|
||||
|
||||
let arch = ref "" in
|
||||
|
||||
let attach = ref [] in
|
||||
let attach_format = ref None in
|
||||
let set_attach_format = function
|
||||
| "auto" -> attach_format := None
|
||||
| s -> attach_format := Some s
|
||||
in
|
||||
let attach_disk s = List.push_front (!attach_format, s) attach in
|
||||
|
||||
let cache = ref Paths.xdg_cache_home in
|
||||
let set_cache arg = cache := Some arg in
|
||||
let no_cache () = cache := None in
|
||||
|
||||
let check_signature = ref true in
|
||||
let curl = ref "curl" in
|
||||
|
||||
let delete_on_failure = ref true in
|
||||
|
||||
let fingerprints = ref [] in
|
||||
let add_fingerprint arg = List.push_front arg fingerprints in
|
||||
|
||||
let format = ref "" in
|
||||
let gpg =
|
||||
try which "gpg2"
|
||||
with Executable_not_found _ ->
|
||||
try which "gpg"
|
||||
with Executable_not_found _ ->
|
||||
"" in
|
||||
let gpg = ref gpg in
|
||||
|
||||
let list_format = ref List_entries.Short in
|
||||
let list_set_long () = list_format := List_entries.Long in
|
||||
let list_set_format arg =
|
||||
(* Do not catch the Invalid_argument that list_format_of_string
|
||||
* throws on invalid input, as it is already checked by the
|
||||
* Getopt handling of Symbol. *)
|
||||
list_format := List_entries.list_format_of_string arg in
|
||||
|
||||
let memsize = ref None in
|
||||
let set_memsize arg = memsize := Some arg in
|
||||
|
||||
let network = ref true in
|
||||
let output = ref "" in
|
||||
|
||||
let size = ref None in
|
||||
let set_size arg = size := Some (parse_size arg) in
|
||||
|
||||
let smp = ref None in
|
||||
let set_smp arg = smp := Some arg in
|
||||
|
||||
let sources = ref [] in
|
||||
let add_source arg = List.push_front arg sources in
|
||||
|
||||
let sync = ref true in
|
||||
let warn_if_partition = ref true in
|
||||
|
||||
let formats = List_entries.list_formats
|
||||
and formats_string = String.concat "|" List_entries.list_formats in
|
||||
|
||||
let argspec = [
|
||||
[ L"arch" ], Getopt.Set_string ("arch", arch), s_"Set the output architecture";
|
||||
[ L"attach" ], Getopt.String ("iso", attach_disk), s_"Attach data disk/ISO during install";
|
||||
[ L"attach-format" ], Getopt.String ("format", set_attach_format),
|
||||
s_"Set attach disk format";
|
||||
[ L"cache" ], Getopt.String ("dir", set_cache), s_"Set template cache dir";
|
||||
[ L"no-cache" ], Getopt.Unit no_cache, s_"Disable template cache";
|
||||
[ L"cache-all-templates" ], Getopt.Unit cache_all_mode,
|
||||
s_"Download all templates to the cache";
|
||||
[ L"check-signature"; L"check-signatures" ], Getopt.Set check_signature,
|
||||
s_"Check digital signatures";
|
||||
[ L"no-check-signature"; L"no-check-signatures" ], Getopt.Clear check_signature,
|
||||
s_"Disable digital signatures";
|
||||
[ L"curl" ], Getopt.Set_string ("curl", curl), s_"Set curl binary/command";
|
||||
[ L"delete-cache" ], Getopt.Unit delete_cache_mode,
|
||||
s_"Delete the template cache";
|
||||
[ L"no-delete-on-failure" ], Getopt.Clear delete_on_failure,
|
||||
s_"Don’t delete output file on failure";
|
||||
[ L"fingerprint" ], Getopt.String ("AAAA..", add_fingerprint),
|
||||
s_"Fingerprint of valid signing key";
|
||||
[ L"format" ], Getopt.Set_string ("raw|qcow2", format), s_"Output format (default: raw)";
|
||||
[ L"get-kernel" ], Getopt.Unit get_kernel_mode,
|
||||
s_"Get kernel from image";
|
||||
[ L"gpg" ], Getopt.Set_string ("gpg", gpg), s_"Set GPG binary/command";
|
||||
[ S 'l'; L"list" ], Getopt.Unit list_mode, s_"List available templates";
|
||||
[ L"long" ], Getopt.Unit list_set_long, s_"Shortcut for --list-format long";
|
||||
[ L"list-format" ], Getopt.Symbol (formats_string, formats, list_set_format),
|
||||
s_"Set the format for --list (default: short)";
|
||||
[ S 'm'; L"memsize" ], Getopt.Int ("mb", set_memsize), s_"Set memory size";
|
||||
[ L"network" ], Getopt.Set network, s_"Enable appliance network (default)";
|
||||
[ L"no-network" ], Getopt.Clear network, s_"Disable appliance network";
|
||||
[ L"notes" ], Getopt.Unit notes_mode, s_"Display installation notes";
|
||||
[ S 'o'; L"output" ], Getopt.Set_string ("file", output), s_"Set output filename";
|
||||
[ L"print-cache" ], Getopt.Unit print_cache_mode,
|
||||
s_"Print info about template cache";
|
||||
[ L"size" ], Getopt.String ("size", set_size), s_"Set output disk size";
|
||||
[ L"smp" ], Getopt.Int ("vcpus", set_smp), s_"Set number of vCPUs";
|
||||
[ L"source" ], Getopt.String ("URL", add_source), s_"Set source URL";
|
||||
[ L"no-sync" ], Getopt.Clear sync, s_"Do not fsync output file on exit";
|
||||
[ L"no-warn-if-partition" ], Getopt.Clear warn_if_partition,
|
||||
s_"Do not warn if writing to a partition";
|
||||
] in
|
||||
let customize_argspec, get_customize_ops = Customize_cmdline.argspec () in
|
||||
let customize_argspec =
|
||||
List.map (fun (spec, _, _) -> spec) customize_argspec in
|
||||
let argspec = argspec @ customize_argspec in
|
||||
|
||||
let args = ref [] in
|
||||
let anon_fun s = List.push_front s args in
|
||||
let usage_msg =
|
||||
sprintf (f_"\
|
||||
%s: build virtual machine images quickly
|
||||
|
||||
virt-builder OS-VERSION
|
||||
virt-builder -l
|
||||
virt-builder --notes OS-VERSION
|
||||
virt-builder --print-cache
|
||||
virt-builder --cache-all-templates
|
||||
virt-builder --delete-cache
|
||||
virt-builder --get-kernel IMAGE
|
||||
|
||||
A short summary of the options is given below. For detailed help please
|
||||
read the man page virt-builder(1).
|
||||
")
|
||||
prog in
|
||||
let opthandle = create_standard_options argspec ~anon_fun ~machine_readable:true usage_msg in
|
||||
Getopt.parse opthandle.getopt;
|
||||
|
||||
(* Dereference options. *)
|
||||
let args = List.rev !args in
|
||||
let mode = !mode in
|
||||
let arch = !arch in
|
||||
let attach = List.rev !attach in
|
||||
let cache = !cache in
|
||||
let check_signature = !check_signature in
|
||||
let curl = !curl in
|
||||
let delete_on_failure = !delete_on_failure in
|
||||
let fingerprints = List.rev !fingerprints in
|
||||
let format = match !format with "" -> None | s -> Some s in
|
||||
let gpg = !gpg in
|
||||
let list_format = !list_format in
|
||||
let memsize = !memsize in
|
||||
let network = !network in
|
||||
let ops = get_customize_ops () in
|
||||
let output = match !output with "" -> None | s -> Some s in
|
||||
let size = !size in
|
||||
let smp = !smp in
|
||||
let sources = List.rev !sources in
|
||||
let sync = !sync in
|
||||
let warn_if_partition = !warn_if_partition in
|
||||
|
||||
(* No arguments and machine-readable mode? Print some facts. *)
|
||||
(match args, machine_readable () with
|
||||
| [], Some { pr } ->
|
||||
pr "virt-builder\n";
|
||||
pr "arch\n";
|
||||
pr "config-file\n";
|
||||
pr "customize\n";
|
||||
pr "json-list\n";
|
||||
if Pxzcat.using_parallel_xzcat () then pr "pxzcat\n";
|
||||
exit 0
|
||||
| _, _ -> ()
|
||||
);
|
||||
|
||||
(* Check options. *)
|
||||
let arg =
|
||||
match mode with
|
||||
| `Install ->
|
||||
(match args with
|
||||
| [arg] -> arg
|
||||
| [] ->
|
||||
error (f_"virt-builder os-version\nMissing ‘os-version’. Use ‘--list’ to list available template names.")
|
||||
| _ ->
|
||||
error (f_"too many parameters, expecting ‘os-version’")
|
||||
)
|
||||
| `List ->
|
||||
if format <> None then
|
||||
error (f_"--list: use ‘--list-format’, not ‘--format’");
|
||||
(match args with
|
||||
| [arg] -> arg
|
||||
| [] -> ""
|
||||
| _ ->
|
||||
error (f_"too many parameters, at most one ‘os-version’ is allowed for --list")
|
||||
)
|
||||
| `Notes ->
|
||||
(match args with
|
||||
| [arg] -> arg
|
||||
| [] ->
|
||||
error (f_"virt-builder --notes os-version\nMissing ‘os-version’. Use ‘--list’ to list available template names.")
|
||||
| _ ->
|
||||
error (f_"--notes: too many parameters, expecting ‘os-version’");
|
||||
)
|
||||
| `Cache_all
|
||||
| `Print_cache
|
||||
| `Delete_cache ->
|
||||
(match args with
|
||||
| [] -> ""
|
||||
| _ ->
|
||||
error (f_"--cache-all-templates/--print-cache/--delete-cache does not need any extra arguments")
|
||||
)
|
||||
| `Get_kernel ->
|
||||
(match args with
|
||||
| [arg] -> arg
|
||||
| [] ->
|
||||
error (f_"virt-builder --get-kernel image\nMissing ‘image’ (disk image file) argument")
|
||||
| _ ->
|
||||
error (f_"--get-kernel: too many parameters")
|
||||
) in
|
||||
|
||||
(* Check source(s) and fingerprint(s). *)
|
||||
let sources =
|
||||
let rec repeat x = function
|
||||
| 0 -> [] | 1 -> [x]
|
||||
| n -> x :: repeat x (n-1)
|
||||
in
|
||||
|
||||
let nr_sources = List.length sources in
|
||||
let fingerprints =
|
||||
if check_signature then (
|
||||
match fingerprints with
|
||||
| [fingerprint] ->
|
||||
(* You're allowed to have multiple sources and one fingerprint: it
|
||||
* means that the same fingerprint is used for all sources.
|
||||
*)
|
||||
repeat fingerprint nr_sources
|
||||
| xs -> xs
|
||||
) else
|
||||
(* We are not checking signatures, so just ignore any fingerprint
|
||||
* specified. *)
|
||||
repeat "" nr_sources in
|
||||
|
||||
if List.length fingerprints <> nr_sources then
|
||||
error (f_"source and fingerprint lists are not the same length");
|
||||
|
||||
(* Combine the sources and fingerprints into a single list of pairs. *)
|
||||
List.combine sources fingerprints in
|
||||
|
||||
(* Check the architecture. *)
|
||||
let arch =
|
||||
match arch with
|
||||
| "" -> Guestfs_config.host_cpu
|
||||
| arch -> arch in
|
||||
let arch = normalize_arch arch in
|
||||
|
||||
(* If user didn't elect any root password, that means we set a random
|
||||
* root password.
|
||||
*)
|
||||
let customize_ops =
|
||||
let has_set_root_password = List.exists (
|
||||
function `RootPassword _ -> true | _ -> false
|
||||
) ops.ops in
|
||||
if has_set_root_password then ops
|
||||
else (
|
||||
let pw = Password.parse_selector "random" in
|
||||
{ ops with ops = ops.ops @ [ `RootPassword pw ] }
|
||||
) in
|
||||
|
||||
{ mode = mode; arg = arg;
|
||||
arch = arch; attach = attach; cache = cache;
|
||||
check_signature = check_signature; curl = curl;
|
||||
customize_ops = customize_ops;
|
||||
delete_on_failure = delete_on_failure; format = format;
|
||||
gpg = gpg; list_format = list_format; memsize = memsize;
|
||||
network = network; output = output;
|
||||
size = size; smp = smp; sources = sources; sync = sync;
|
||||
warn_if_partition = warn_if_partition;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
(** Command line argument parsing. *)
|
||||
|
||||
type cmdline = {
|
||||
mode : [ `Cache_all | `Delete_cache | `Get_kernel | `Install | `List
|
||||
| `Notes | `Print_cache ];
|
||||
arg : string;
|
||||
arch : string;
|
||||
attach : (string option * string) list;
|
||||
cache : string option;
|
||||
check_signature : bool;
|
||||
curl : string;
|
||||
customize_ops : Customize_cmdline.ops;
|
||||
delete_on_failure : bool;
|
||||
format : string option;
|
||||
gpg : string;
|
||||
list_format : List_entries.format;
|
||||
memsize : int option;
|
||||
network : bool;
|
||||
output : string option;
|
||||
size : int64 option;
|
||||
smp : int option;
|
||||
sources : (string * string) list;
|
||||
sync : bool;
|
||||
warn_if_partition : bool;
|
||||
}
|
||||
|
||||
val parse_cmdline : unit -> cmdline
|
||||
@@ -1,144 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open Utils
|
||||
|
||||
open Unix
|
||||
open Printf
|
||||
|
||||
type uri = string
|
||||
type filename = string
|
||||
|
||||
type t = {
|
||||
curl : string;
|
||||
tmpdir : string;
|
||||
cache : Cache.t option; (* cache for templates *)
|
||||
}
|
||||
|
||||
let create ~curl ~tmpdir ~cache = {
|
||||
curl = curl;
|
||||
tmpdir = tmpdir;
|
||||
cache = cache;
|
||||
}
|
||||
|
||||
let rec download t ?template ?progress_bar ?(proxy = Curl.SystemProxy) uri =
|
||||
match template with
|
||||
| None -> (* no cache, simple download *)
|
||||
(* Create a temporary name. *)
|
||||
let tmpfile = Filename.temp_file ~temp_dir:t.tmpdir "vbcache" ".txt" in
|
||||
download_to t ?progress_bar ~proxy uri tmpfile;
|
||||
(tmpfile, true)
|
||||
|
||||
| Some (name, arch, revision) ->
|
||||
match t.cache with
|
||||
| None ->
|
||||
(* Not using the cache at all? *)
|
||||
download t ?progress_bar ~proxy uri
|
||||
|
||||
| Some cache ->
|
||||
let filename = Cache.cache_of_name cache name arch revision in
|
||||
|
||||
(* Is the requested template name + revision in the cache already?
|
||||
* If not, download it.
|
||||
*)
|
||||
if not (Sys.file_exists filename) then
|
||||
download_to t ?progress_bar ~proxy uri filename;
|
||||
|
||||
(filename, false)
|
||||
|
||||
and download_to t ?(progress_bar = false) ~proxy uri filename =
|
||||
let parseduri =
|
||||
try URI.parse_uri uri
|
||||
with URI.Parse_failed ->
|
||||
error (f_"error parsing URI '%s'. Look for error messages printed above.")
|
||||
uri in
|
||||
|
||||
(* Note because there may be parallel virt-builder instances running
|
||||
* and also to avoid partial downloads in the cache if the network
|
||||
* fails, we download to a random name in the cache and then
|
||||
* atomically rename it to the final filename.
|
||||
*)
|
||||
let filename_new = filename ^ "." ^ String.random8 () in
|
||||
unlink_on_exit filename_new;
|
||||
|
||||
(match parseduri.URI.protocol with
|
||||
(* Download (ie. copy) from a local file. *)
|
||||
| "file" ->
|
||||
let path = parseduri.URI.path in
|
||||
let cmd = [ "cp" ] @
|
||||
(if verbose () then [ "-v" ] else []) @
|
||||
[ path; filename_new ] in
|
||||
let r = run_command cmd in
|
||||
if r <> 0 then
|
||||
error (f_"cp (download) command failed copying ‘%s’") path;
|
||||
|
||||
(* Any other protocol. *)
|
||||
| _ ->
|
||||
let common_args = [
|
||||
"location", None; (* Follow 3xx redirects. *)
|
||||
"url", Some uri; (* URI to download. *)
|
||||
] in
|
||||
|
||||
let quiet_args = [ "silent", None; "show-error", None ] in
|
||||
|
||||
(* Get the status code first to ensure the file exists. *)
|
||||
let curl_h =
|
||||
let curl_args = ref common_args in
|
||||
if not (verbose ()) then List.push_back_list curl_args quiet_args;
|
||||
List.push_back_list curl_args [
|
||||
"output", Some "/dev/null"; (* Write output to /dev/null. *)
|
||||
"head", None; (* Request only HEAD. *)
|
||||
"write-out", Some "%{http_code}" (* HTTP status code to stdout. *)
|
||||
];
|
||||
|
||||
Curl.create ~curl:t.curl ~tmpdir:t.tmpdir !curl_args in
|
||||
|
||||
let lines = Curl.run curl_h in
|
||||
if List.length lines < 1 then
|
||||
error (f_"unexpected output from curl command, enable debug and look at previous messages");
|
||||
let status_code = List.hd lines in
|
||||
let bad_status_code = function
|
||||
| "" -> true
|
||||
| s when s.[0] = '4' -> true (* 4xx *)
|
||||
| s when s.[0] = '5' -> true (* 5xx *)
|
||||
| _ -> false
|
||||
in
|
||||
if bad_status_code status_code then
|
||||
error (f_"failed to download %s: HTTP status code %s") uri status_code;
|
||||
|
||||
(* Now download the file. *)
|
||||
let curl_h =
|
||||
let curl_args = ref common_args in
|
||||
List.push_back curl_args ("output", Some filename_new);
|
||||
|
||||
if not (verbose ()) then (
|
||||
if progress_bar then List.push_back curl_args ("progress-bar", None)
|
||||
else List.push_back_list curl_args quiet_args
|
||||
);
|
||||
|
||||
Curl.create ~curl:t.curl ~tmpdir:t.tmpdir !curl_args in
|
||||
|
||||
ignore (Curl.run curl_h)
|
||||
);
|
||||
|
||||
(* Rename the file if the download was successful. *)
|
||||
rename filename_new filename
|
||||
@@ -1,45 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
(** This module is a wrapper around curl, plus local caching. *)
|
||||
|
||||
type uri = string
|
||||
type filename = string
|
||||
|
||||
type t
|
||||
(** The abstract data type. *)
|
||||
|
||||
val create : curl:string -> tmpdir:string -> cache:Cache.t option -> t
|
||||
(** Create the abstract type. *)
|
||||
|
||||
val download : t -> ?template:string * Index.arch * Utils.revision -> ?progress_bar:bool -> ?proxy:Curl.proxy -> uri -> filename * bool
|
||||
(** Download the URI, returning the downloaded filename and a
|
||||
temporary file flag. The temporary file flag is [true] iff
|
||||
the downloaded file is temporary and should be deleted by the
|
||||
caller (otherwise it's in the cache and you shouldn't delete it).
|
||||
|
||||
For templates, you must supply [~template:(name, arch, revision)].
|
||||
This causes the cache to be used (if possible). Name, arch(itecture)
|
||||
and revision are used for cache control (see the man page for details).
|
||||
|
||||
If [~progress_bar:true] then display a progress bar if the file
|
||||
doesn't come from the cache. In verbose mode, progress messages
|
||||
are always displayed.
|
||||
|
||||
[proxy] specifies the type of proxy to be used in the transfer,
|
||||
if possible. *)
|
||||
@@ -1,184 +0,0 @@
|
||||
/* libguestfs virt-builder tool -*- fundamental -*-
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
%{
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "index-struct.h"
|
||||
#include "index-parse.h"
|
||||
|
||||
/* The generated code uses frames > 5000 bytes. */
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
#pragma GCC diagnostic ignored "-Wstack-usage="
|
||||
#endif
|
||||
|
||||
#define YY_EXTRA_TYPE struct parse_context *
|
||||
|
||||
extern void yyerror (YYLTYPE * yylloc, yyscan_t scanner, struct parse_context *context, const char *msg);
|
||||
extern int yylex (YYSTYPE * yylval, YYLTYPE * yylloc, yyscan_t scanner);
|
||||
|
||||
extern int do_parse (struct parse_context *context, FILE *in);
|
||||
extern void scanner_init (yyscan_t *scanner, struct parse_context *context, FILE *in);
|
||||
extern void scanner_destroy (yyscan_t scanner);
|
||||
|
||||
/* Join two strings with \n */
|
||||
static char *
|
||||
concat_newline (const char *str1, const char *str2)
|
||||
{
|
||||
size_t len1, len2, len;
|
||||
char *ret;
|
||||
|
||||
if (str2 == NULL)
|
||||
return strdup (str1);
|
||||
|
||||
len1 = strlen (str1);
|
||||
len2 = strlen (str2);
|
||||
len = len1 + 1 /* \n */ + len2 + 1 /* \0 */;
|
||||
ret = malloc (len);
|
||||
memcpy (ret, str1, len1);
|
||||
ret[len1] = '\n';
|
||||
memcpy (ret + len1 + 1, str2, len2);
|
||||
ret[len-1] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%code requires {
|
||||
#ifndef YY_TYPEDEF_YY_SCANNER_T
|
||||
#define YY_TYPEDEF_YY_SCANNER_T
|
||||
typedef void *yyscan_t;
|
||||
#endif
|
||||
}
|
||||
|
||||
%locations
|
||||
|
||||
%union {
|
||||
struct section *section;
|
||||
struct field *field;
|
||||
char *str;
|
||||
}
|
||||
|
||||
%token <str> SECTION_HEADER
|
||||
%token <field> FIELD
|
||||
%token <str> VALUE_CONT
|
||||
%token EMPTY_LINE
|
||||
%token PGP_PROLOGUE
|
||||
%token PGP_EPILOGUE
|
||||
%token UNKNOWN_LINE
|
||||
|
||||
%type <section> sections section
|
||||
%type <field> fields field
|
||||
%type <str> continuations
|
||||
|
||||
%pure-parser
|
||||
|
||||
%lex-param { yyscan_t scanner }
|
||||
%parse-param { yyscan_t scanner }
|
||||
%parse-param { struct parse_context *context }
|
||||
|
||||
%destructor { section_free ($$); } <section>
|
||||
%destructor { field_free ($$); } <field>
|
||||
|
||||
%%
|
||||
|
||||
index:
|
||||
sections
|
||||
{ context->parsed_index = $1; }
|
||||
| PGP_PROLOGUE sections PGP_EPILOGUE
|
||||
{ context->parsed_index = $2; }
|
||||
|
||||
sections:
|
||||
emptylines section emptylines
|
||||
{ $$ = $2; }
|
||||
| emptylines section EMPTY_LINE emptylines sections
|
||||
{ $$ = $2; $$->next = $5; }
|
||||
| emptylines
|
||||
{ $$ = NULL; }
|
||||
|
||||
section:
|
||||
SECTION_HEADER fields
|
||||
{ $$ = malloc (sizeof (struct section));
|
||||
$$->next = NULL;
|
||||
$$->name = $1;
|
||||
$$->fields = $2; }
|
||||
|
||||
fields:
|
||||
/* empty */
|
||||
{ $$ = NULL; }
|
||||
| field fields
|
||||
{ $$ = $1; $$->next = $2; }
|
||||
|
||||
field: FIELD continuations
|
||||
{ $$ = $1;
|
||||
char *old_value = $$->value;
|
||||
$$->value = concat_newline (old_value, $2);
|
||||
free (old_value);
|
||||
free ($2); }
|
||||
|
||||
continuations:
|
||||
/* empty */
|
||||
{ $$ = NULL; }
|
||||
| VALUE_CONT continuations
|
||||
{ $$ = concat_newline ($1, $2);
|
||||
free ($1);
|
||||
free ($2); }
|
||||
|
||||
emptylines:
|
||||
/* empty */
|
||||
{}
|
||||
| EMPTY_LINE emptylines
|
||||
{}
|
||||
|
||||
%%
|
||||
|
||||
void
|
||||
yyerror (YYLTYPE * yylloc, yyscan_t scanner, struct parse_context *context, const char *msg)
|
||||
{
|
||||
const int has_suffix =
|
||||
context->error_suffix != NULL && context->error_suffix[0] != 0;
|
||||
|
||||
fprintf (stderr, "%s%s%s%ssyntax error at line %d: %s%s%s\n",
|
||||
context->progname ? context->progname : "",
|
||||
context->progname ? ": " : "",
|
||||
context->input_file ? context->input_file : "",
|
||||
context->input_file ? ": " : "",
|
||||
yylloc->first_line, msg,
|
||||
has_suffix ? " " : "",
|
||||
has_suffix ? context->error_suffix : "");
|
||||
}
|
||||
|
||||
int
|
||||
do_parse (struct parse_context *context, FILE *in)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
int res;
|
||||
|
||||
scanner_init (&scanner, context, in);
|
||||
res = yyparse (scanner, context);
|
||||
scanner_destroy (scanner);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file handles the interface between the C/lex/yacc index file
|
||||
* parser, and the OCaml world. See F<builder/index_parser.ml> for
|
||||
* the OCaml type definition.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <caml/alloc.h>
|
||||
#include <caml/fail.h>
|
||||
#include <caml/memory.h>
|
||||
#include <caml/mlvalues.h>
|
||||
#include <caml/unixsupport.h>
|
||||
|
||||
#include "index-struct.h"
|
||||
#include "index-parse.h"
|
||||
|
||||
extern int do_parse (struct parse_context *context, FILE *in);
|
||||
|
||||
extern value virt_builder_parse_index (value progv, value error_suffixv, value filenamev);
|
||||
|
||||
value
|
||||
virt_builder_parse_index (value progv, value error_suffixv, value filenamev)
|
||||
{
|
||||
CAMLparam2 (progv, filenamev);
|
||||
CAMLlocal5 (rv, v, sv, sv2, fv);
|
||||
struct section *sections;
|
||||
size_t i, nr_sections;
|
||||
struct parse_context context;
|
||||
FILE *in;
|
||||
|
||||
parse_context_init (&context);
|
||||
context.progname = String_val (progv);
|
||||
context.input_file = String_val (filenamev);
|
||||
context.error_suffix = String_val (error_suffixv);
|
||||
|
||||
in = fopen (String_val (filenamev), "r");
|
||||
if (in == NULL)
|
||||
unix_error (errno, (char *) "fopen", filenamev);
|
||||
|
||||
if (do_parse (&context, in) != 0) {
|
||||
fclose (in);
|
||||
caml_invalid_argument ("parse error");
|
||||
}
|
||||
|
||||
if (fclose (in) == EOF)
|
||||
unix_error (errno, (char *) "fclose", filenamev);
|
||||
|
||||
/* Convert the parsed data to OCaml structures. */
|
||||
nr_sections = 0;
|
||||
for (sections = context.parsed_index; sections != NULL; sections = sections->next)
|
||||
nr_sections++;
|
||||
rv = caml_alloc (nr_sections, 0);
|
||||
|
||||
for (i = 0, sections = context.parsed_index; sections != NULL;
|
||||
i++, sections = sections->next) {
|
||||
struct field *fields;
|
||||
size_t j, nr_fields;
|
||||
|
||||
nr_fields = 0;
|
||||
for (fields = sections->fields; fields != NULL; fields = fields->next)
|
||||
nr_fields++;
|
||||
fv = caml_alloc (nr_fields, 0);
|
||||
|
||||
for (j = 0, fields = sections->fields; fields != NULL;
|
||||
j++, fields = fields->next) {
|
||||
v = caml_alloc_tuple (3);
|
||||
sv = caml_copy_string (fields->key);
|
||||
Store_field (v, 0, sv); /* (key, Some subkey, value) */
|
||||
if (fields->subkey) {
|
||||
sv2 = caml_copy_string (fields->subkey);
|
||||
sv = caml_alloc (1, 0);
|
||||
Store_field (sv, 0, sv2);
|
||||
} else
|
||||
sv = Val_int (0);
|
||||
Store_field (v, 1, sv);
|
||||
sv = caml_copy_string (fields->value);
|
||||
Store_field (v, 2, sv);
|
||||
Store_field (fv, j, v); /* assign to return array of fields */
|
||||
}
|
||||
|
||||
v = caml_alloc_tuple (2);
|
||||
sv = caml_copy_string (sections->name);
|
||||
Store_field (v, 0, sv); /* (name, fields) */
|
||||
Store_field (v, 1, fv);
|
||||
Store_field (rv, i, v); /* assign to return array of sections */
|
||||
}
|
||||
|
||||
/* Free parsed data. */
|
||||
parse_context_free (&context);
|
||||
|
||||
CAMLreturn (rv);
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/* libguestfs virt-builder tool -*- fundamental -*-
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
%top{
|
||||
#include <config.h>
|
||||
}
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Silence gcc warnings from the generated code. */
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
/* flex creates macros that it doesn't use */
|
||||
#pragma GCC diagnostic ignored "-Wunused-macros"
|
||||
/* on aarch64, flex doesn't know that char is unsigned */
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
/* on debian-mipsel, flex doesn't create prototypes for all functions */
|
||||
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
|
||||
#endif
|
||||
|
||||
#include "index-struct.h"
|
||||
#include "index-parse.h"
|
||||
|
||||
#define YY_EXTRA_TYPE struct parse_context *
|
||||
#define YY_USER_ACTION yylloc->first_line = yylloc->last_line = yylineno;
|
||||
|
||||
extern void scanner_init (yyscan_t *scanner, struct parse_context *context, FILE *in);
|
||||
extern void scanner_destroy (yyscan_t scanner);
|
||||
|
||||
#if (YY_FLEX_MAJOR_VERSION > 2) \
|
||||
|| ((YY_FLEX_MAJOR_VERSION == 2) && (YY_FLEX_MINOR_VERSION > 6)) \
|
||||
|| ((YY_FLEX_MAJOR_VERSION == 2) && (YY_FLEX_MINOR_VERSION == 6) && (YY_FLEX_SUBMINOR_VERSION >= 1))
|
||||
#define IS_EOF 0
|
||||
#else
|
||||
#define IS_EOF EOF
|
||||
#endif
|
||||
|
||||
%}
|
||||
|
||||
%option nounput
|
||||
%option noyywrap
|
||||
%option yylineno
|
||||
%option reentrant
|
||||
%option bison-bridge
|
||||
%option bison-locations
|
||||
|
||||
%%
|
||||
|
||||
/* Apart from the PGP prologue/epilogue which is a hack, the
|
||||
* scanning strategy is to deal with the file strictly line by
|
||||
* line, and pass those lines up to the parser which deals with
|
||||
* whether they appear in the right order to be meaningful.
|
||||
* Note that flex does longest-match.
|
||||
*/
|
||||
|
||||
/* Ignore comments - '#' MUST appear at the start of a line. */
|
||||
^"#".*\n { yyextra->seen_comments++; }
|
||||
|
||||
/* An empty line is significant. */
|
||||
^\n { return EMPTY_LINE; }
|
||||
|
||||
/* [...] marks beginning of a section. */
|
||||
^"["[-A-Za-z0-9._]+"]"[[:blank:]]*\n {
|
||||
const char *end = strrchr (yytext, ']');
|
||||
yylval->str = strndup (yytext+1, end-yytext-1);
|
||||
return SECTION_HEADER;
|
||||
}
|
||||
|
||||
/* field=value or field[subfield]=value */
|
||||
^[A-Za-z0-9_.]+("["[A-Za-z0-9_,.]+"]")?"=".*\n {
|
||||
size_t i = strcspn (yytext, "=[");
|
||||
yylval->field = malloc (sizeof (struct field));
|
||||
yylval->field->next = NULL;
|
||||
yylval->field->key = strndup (yytext, i);
|
||||
if (yytext[i] == '[') {
|
||||
const size_t j = strcspn (yytext+i+1, "]");
|
||||
yylval->field->subkey = strndup (yytext+i+1, j);
|
||||
i += 1+j+1;
|
||||
} else {
|
||||
yylval->field->subkey = NULL;
|
||||
}
|
||||
/* Note we chop the final \n off here. */
|
||||
yylval->field->value = strndup (yytext+i+1, yyleng-(i+2));
|
||||
return FIELD;
|
||||
}
|
||||
|
||||
/* Continuation line for multi-line values. */
|
||||
^[[:blank:]].*\n {
|
||||
yylval->str = strndup (yytext+1, yyleng-2);
|
||||
return VALUE_CONT;
|
||||
}
|
||||
|
||||
/* Hack to eat the PGP prologue. */
|
||||
^"-----BEGIN PGP SIGNED MESSAGE-----\n" {
|
||||
int c, prevnl = 0;
|
||||
|
||||
/* Eat everything to the first blank line. */
|
||||
while ((c = input (yyscanner)) != IS_EOF) {
|
||||
if (c == '\n' && prevnl)
|
||||
break;
|
||||
prevnl = c == '\n';
|
||||
}
|
||||
|
||||
return PGP_PROLOGUE;
|
||||
}
|
||||
|
||||
/* Hack to eat the PGP epilogue. */
|
||||
^"-----BEGIN PGP SIGNATURE-----\n" {
|
||||
/* Eat everything to the end of the file. */
|
||||
while (input (yyscanner) != IS_EOF)
|
||||
;
|
||||
|
||||
return PGP_EPILOGUE;
|
||||
}
|
||||
|
||||
/* anything else is an error */
|
||||
. {
|
||||
return UNKNOWN_LINE;
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
void
|
||||
scanner_init (yyscan_t *scanner, struct parse_context *context, FILE *in)
|
||||
{
|
||||
yylex_init (scanner);
|
||||
yyset_extra (context, *scanner);
|
||||
yyset_in (in, *scanner);
|
||||
}
|
||||
|
||||
void
|
||||
scanner_destroy (yyscan_t scanner)
|
||||
{
|
||||
yylex_destroy (scanner);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/* libguestfs virt-builder tool
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "index-struct.h"
|
||||
|
||||
void
|
||||
parse_context_init (struct parse_context *context)
|
||||
{
|
||||
memset (context, 0, sizeof *context);
|
||||
}
|
||||
|
||||
void
|
||||
parse_context_free (struct parse_context *context)
|
||||
{
|
||||
section_free (context->parsed_index);
|
||||
}
|
||||
|
||||
void
|
||||
section_free (struct section *section)
|
||||
{
|
||||
if (section) {
|
||||
section_free (section->next);
|
||||
free (section->name);
|
||||
field_free (section->fields);
|
||||
free (section);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
field_free (struct field *field)
|
||||
{
|
||||
if (field) {
|
||||
field_free (field->next);
|
||||
free (field->key);
|
||||
free (field->subkey);
|
||||
free (field->value);
|
||||
free (field);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/* libguestfs virt-builder tool
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/* The data structures produced when parsing the index file. */
|
||||
|
||||
#ifndef INDEX_STRUCT_H
|
||||
#define INDEX_STRUCT_H
|
||||
|
||||
/* A section or list of sections. */
|
||||
struct section {
|
||||
struct section *next;
|
||||
char *name;
|
||||
struct field *fields;
|
||||
};
|
||||
|
||||
/* A field or list of fields. */
|
||||
struct field {
|
||||
struct field *next;
|
||||
char *key;
|
||||
char *subkey;
|
||||
char *value;
|
||||
};
|
||||
|
||||
/* A struct holding the data needed during the parsing. */
|
||||
struct parse_context {
|
||||
struct section *parsed_index; /* The result of the parsing. */
|
||||
/* yyparse sets this if any comments were seen. Required for checking
|
||||
* compatibility with virt-builder 1.24.
|
||||
*/
|
||||
int seen_comments;
|
||||
const char *input_file;
|
||||
const char *progname;
|
||||
const char *error_suffix;
|
||||
};
|
||||
|
||||
/* Initialize the content of a parse_context. */
|
||||
extern void parse_context_init (struct parse_context *state);
|
||||
|
||||
/* Free the content of a parse_context. The actual pointer is not freed. */
|
||||
extern void parse_context_free (struct parse_context *state);
|
||||
|
||||
/* Free the content of a section, recursively freeing also its fields.
|
||||
* The actual pointer is not freed.
|
||||
*/
|
||||
extern void section_free (struct section *section);
|
||||
|
||||
/* Free the content of a field, recursively freeing also its next field.
|
||||
* The actual pointer is not freed.
|
||||
*/
|
||||
extern void field_free (struct field *field);
|
||||
|
||||
#endif /* INDEX_STRUCT_H */
|
||||
@@ -1,186 +0,0 @@
|
||||
/* libguestfs virt-builder tool
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <getopt.h>
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <libintl.h>
|
||||
|
||||
#include <guestfs.h>
|
||||
|
||||
#include "getprogname.h"
|
||||
#include "guestfs-utils.h"
|
||||
|
||||
#include "index-struct.h"
|
||||
#include "index-parse.h"
|
||||
|
||||
extern int do_parse (struct parse_context *context, FILE *in);
|
||||
|
||||
static void __attribute__((noreturn))
|
||||
usage (int exit_status)
|
||||
{
|
||||
printf ("%s index\n", getprogname ());
|
||||
exit (exit_status);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
enum { HELP_OPTION = CHAR_MAX + 1 };
|
||||
static const char options[] = "V";
|
||||
static const struct option long_options[] = {
|
||||
{ "help", 0, 0, HELP_OPTION },
|
||||
{ "compat-1.24.0", 0, 0, 0 },
|
||||
{ "compat-1.24.1", 0, 0, 0 },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
int c;
|
||||
int option_index;
|
||||
int compat_1_24_0 = 0;
|
||||
int compat_1_24_1 = 0;
|
||||
const char *input;
|
||||
struct section *sections;
|
||||
struct parse_context context;
|
||||
FILE *in;
|
||||
int ret;
|
||||
|
||||
setlocale (LC_ALL, "");
|
||||
bindtextdomain (PACKAGE, LOCALEBASEDIR);
|
||||
textdomain (PACKAGE);
|
||||
|
||||
parse_context_init (&context);
|
||||
|
||||
for (;;) {
|
||||
c = getopt_long (argc, argv, options, long_options, &option_index);
|
||||
if (c == -1) break;
|
||||
|
||||
switch (c) {
|
||||
case 0: /* options which are long only */
|
||||
if (STREQ (long_options[option_index].name, "compat-1.24.0"))
|
||||
compat_1_24_0 = compat_1_24_1 = 1;
|
||||
else if (STREQ (long_options[option_index].name, "compat-1.24.1"))
|
||||
compat_1_24_1 = 1;
|
||||
else
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("unknown long option: %s (%d)"),
|
||||
long_options[option_index].name, option_index);
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
printf ("%s %s%s\n",
|
||||
getprogname (),
|
||||
PACKAGE_VERSION, PACKAGE_VERSION_EXTRA);
|
||||
exit (EXIT_SUCCESS);
|
||||
|
||||
case HELP_OPTION:
|
||||
usage (EXIT_SUCCESS);
|
||||
|
||||
default:
|
||||
usage (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc-1)
|
||||
usage (EXIT_FAILURE);
|
||||
|
||||
input = argv[optind++];
|
||||
|
||||
in = fopen (input, "r");
|
||||
if (in == NULL)
|
||||
error (EXIT_FAILURE, errno, "fopen: %s", input);
|
||||
|
||||
ret = do_parse (&context, in);
|
||||
|
||||
if (fclose (in) == EOF) {
|
||||
fprintf (stderr, _("%s: %s: error closing input file: %m (ignored)\n"),
|
||||
getprogname (), input);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
parse_context_free (&context);
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("‘%s’ could not be validated, see errors above"), input);
|
||||
}
|
||||
|
||||
if (compat_1_24_1 && context.seen_comments) {
|
||||
parse_context_free (&context);
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("%s contains comments which will not work with virt-builder 1.24.1"),
|
||||
input);
|
||||
}
|
||||
|
||||
/* Iterate over the parsed sections, semantically validating it. */
|
||||
for (sections = context.parsed_index; sections != NULL; sections = sections->next) {
|
||||
int seen_sig = 0;
|
||||
struct field *fields;
|
||||
|
||||
if (compat_1_24_0) {
|
||||
if (strchr (sections->name, '_')) {
|
||||
parse_context_free (&context);
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("%s: section [%s] has invalid characters which will not work with virt-builder 1.24.0"),
|
||||
input, sections->name);
|
||||
}
|
||||
}
|
||||
|
||||
for (fields = sections->fields; fields != NULL; fields = fields->next) {
|
||||
if (compat_1_24_0) {
|
||||
if (strchr (fields->key, '[') ||
|
||||
strchr (fields->key, ']')) {
|
||||
parse_context_free (&context);
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("%s: section [%s], field ‘%s’ has invalid characters which will not work with virt-builder 1.24.0"),
|
||||
input, sections->name, fields->key);
|
||||
}
|
||||
}
|
||||
if (compat_1_24_1) {
|
||||
if (strchr (fields->key, '.') ||
|
||||
strchr (fields->key, ',')) {
|
||||
parse_context_free (&context);
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("%s: section [%s], field ‘%s’ has invalid characters which will not work with virt-builder 1.24.1"),
|
||||
input, sections->name, fields->key);
|
||||
}
|
||||
}
|
||||
if (STREQ (fields->key, "sig"))
|
||||
seen_sig = 1;
|
||||
}
|
||||
|
||||
if (compat_1_24_0 && !seen_sig) {
|
||||
parse_context_free (&context);
|
||||
error (EXIT_FAILURE, 0,
|
||||
_("%s: section [%s] is missing a ‘sig’ field which will not work with virt-builder 1.24.0"),
|
||||
input, sections->name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the parsed data. */
|
||||
parse_context_free (&context);
|
||||
|
||||
printf ("%s validated OK\n", input);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open Utils
|
||||
|
||||
open Printf
|
||||
open Unix
|
||||
|
||||
type index = (string * entry) list (* string = "os-version" *)
|
||||
and entry = {
|
||||
printable_name : string option; (* the name= field *)
|
||||
osinfo : string option;
|
||||
file_uri : string;
|
||||
arch : arch;
|
||||
signature_uri : string option; (* deprecated, will be removed in 1.26 *)
|
||||
checksums : Checksums.csum_t list option;
|
||||
revision : Utils.revision;
|
||||
format : string option;
|
||||
size : int64;
|
||||
compressed_size : int64 option;
|
||||
expand : string option;
|
||||
lvexpand : string option;
|
||||
notes : (string * string) list;
|
||||
hidden : bool;
|
||||
aliases : string list option;
|
||||
|
||||
sigchecker : Sigchecker.t;
|
||||
proxy : Curl.proxy;
|
||||
}
|
||||
and arch =
|
||||
| Arch of string
|
||||
| GuessedArch of string
|
||||
|
||||
let string_of_arch = function Arch a | GuessedArch a -> a
|
||||
|
||||
let print_entry chan (name, { printable_name; file_uri; arch; osinfo;
|
||||
signature_uri; checksums; revision; format;
|
||||
size; compressed_size; expand; lvexpand;
|
||||
notes; aliases; hidden }) =
|
||||
let fp fs = fprintf chan fs in
|
||||
fp "[%s]\n" name;
|
||||
Option.may (fp "name=%s\n") printable_name;
|
||||
Option.may (fp "osinfo=%s\n") osinfo;
|
||||
fp "file=%s\n" file_uri;
|
||||
fp "arch=%s\n" (string_of_arch arch);
|
||||
Option.may (fp "sig=%s\n") signature_uri;
|
||||
Option.may (
|
||||
List.iter (
|
||||
fun c ->
|
||||
fp "checksum[%s]=%s\n"
|
||||
(Checksums.string_of_csum_t c) (Checksums.string_of_csum c)
|
||||
)
|
||||
) checksums;
|
||||
fp "revision=%s\n" (string_of_revision revision);
|
||||
Option.may (fp "format=%s\n") format;
|
||||
fp "size=%Ld\n" size;
|
||||
Option.may (fp "compressed_size=%Ld\n") compressed_size;
|
||||
Option.may (fp "expand=%s\n") expand;
|
||||
Option.may (fp "lvexpand=%s\n") lvexpand;
|
||||
List.iter (
|
||||
fun (lang, notes) ->
|
||||
match lang with
|
||||
| "" -> fp "notes=%s\n" notes
|
||||
| lang -> fp "notes[%s]=%s\n" lang notes
|
||||
) notes;
|
||||
Option.may (fun l -> fp "aliases=%s\n" (String.concat " " l)) aliases;
|
||||
if hidden then fp "hidden=true\n"
|
||||
@@ -1,50 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
type index = (string * entry) list (* string = "os-version" *)
|
||||
and entry = {
|
||||
printable_name : string option; (* the name= field *)
|
||||
osinfo : string option;
|
||||
file_uri : string;
|
||||
arch : arch;
|
||||
signature_uri : string option; (* deprecated, will be removed in 1.26 *)
|
||||
checksums : Checksums.csum_t list option;
|
||||
revision : Utils.revision;
|
||||
format : string option;
|
||||
size : int64;
|
||||
compressed_size : int64 option;
|
||||
expand : string option;
|
||||
lvexpand : string option;
|
||||
notes : (string * string) list;
|
||||
hidden : bool;
|
||||
aliases : string list option;
|
||||
|
||||
sigchecker : Sigchecker.t;
|
||||
proxy : Curl.proxy;
|
||||
}
|
||||
and arch =
|
||||
| Arch of string (** Specified in the metadata. *)
|
||||
| GuessedArch of string (** Guess from inspection data. *)
|
||||
|
||||
val string_of_arch : arch -> string
|
||||
(** [string_of_arch a]Get the string value of [a]. *)
|
||||
|
||||
val print_entry : out_channel -> (string * entry) -> unit
|
||||
(** Debugging helper function dumping an index entry to a stream.
|
||||
To write entries for non-debugging purpose, use the
|
||||
[Index_parser.write_entry] function. *)
|
||||
@@ -1,306 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open Utils
|
||||
|
||||
open Printf
|
||||
open Unix
|
||||
|
||||
let get_index ~downloader ~sigchecker ?(template = false) { Sources.uri; proxy } =
|
||||
let corrupt_file () =
|
||||
error (f_"The index file downloaded from ‘%s’ is corrupt.\nYou need to ask the supplier of this file to fix it and upload a fixed version.") uri
|
||||
in
|
||||
|
||||
let rec get_index () =
|
||||
(* Get the index page. *)
|
||||
let tmpfile, _ = Downloader.download downloader ~proxy uri in
|
||||
|
||||
(* Check index file signature (also verifies it was fully
|
||||
* downloaded and not corrupted in transit).
|
||||
*)
|
||||
Sigchecker.verify sigchecker tmpfile;
|
||||
|
||||
(* Try parsing the file. *)
|
||||
let sections = Ini_reader.read_ini tmpfile in
|
||||
|
||||
(* Check for repeated os-version+arch combination. *)
|
||||
let name_arch_map = List.map (
|
||||
fun (n, fields) ->
|
||||
let rec find_arch = function
|
||||
| ("arch", None, value) :: y -> value
|
||||
| _ :: y -> find_arch y
|
||||
| [] -> ""
|
||||
in
|
||||
n, (find_arch fields)
|
||||
) sections in
|
||||
let nseen = Hashtbl.create 13 in
|
||||
List.iter (
|
||||
fun (n, arch) ->
|
||||
let id = n, arch in
|
||||
if Hashtbl.mem nseen id then (
|
||||
eprintf (f_"%s: index is corrupt: os-version ‘%s’ with architecture ‘%s’ appears two or more times\n") prog n arch;
|
||||
corrupt_file ()
|
||||
);
|
||||
Hashtbl.add nseen id true
|
||||
) name_arch_map;
|
||||
|
||||
(* Check for repeated fields. *)
|
||||
List.iter (
|
||||
fun (n, fields) ->
|
||||
let fseen = Hashtbl.create 13 in
|
||||
List.iter (
|
||||
fun (field, subkey, _) ->
|
||||
let hashkey = (field, subkey) in
|
||||
if Hashtbl.mem fseen hashkey then (
|
||||
(match subkey with
|
||||
| Some value ->
|
||||
eprintf (f_"%s: index is corrupt: %s: field ‘%s[%s]’ appears two or more times\n") prog n field value
|
||||
| None ->
|
||||
eprintf (f_"%s: index is corrupt: %s: field ‘%s’ appears two or more times\n") prog n field);
|
||||
corrupt_file ()
|
||||
);
|
||||
Hashtbl.add fseen hashkey true
|
||||
) fields
|
||||
) sections;
|
||||
|
||||
(* Turn the sections into the final index. *)
|
||||
let entries =
|
||||
List.map (
|
||||
fun (n, fields) ->
|
||||
let fields = List.map (fun (k, sk, v) -> (k, sk), v) fields in
|
||||
let printable_name =
|
||||
try Some (List.assoc ("name", None) fields) with Not_found -> None in
|
||||
let osinfo =
|
||||
try Some (List.assoc ("osinfo", None) fields) with Not_found -> None in
|
||||
let file_uri =
|
||||
try make_absolute_uri (List.assoc ("file", None) fields)
|
||||
with Not_found ->
|
||||
eprintf (f_"%s: no ‘file’ (URI) entry for ‘%s’\n") prog n;
|
||||
corrupt_file () in
|
||||
let arch =
|
||||
try Index.Arch (List.assoc ("arch", None) fields)
|
||||
with Not_found ->
|
||||
if template then
|
||||
let g = open_guestfs ~identifier:"template" () in
|
||||
g#add_drive_ro file_uri;
|
||||
g#launch ();
|
||||
let roots = g#inspect_os () in
|
||||
let nroots = Array.length roots in
|
||||
if nroots <> 1 then (
|
||||
eprintf (f_"%s: no ‘arch’ entry for %s and failed to guess it\n") prog n;
|
||||
corrupt_file ()
|
||||
);
|
||||
let inspected_arch = g#inspect_get_arch (Array.get roots 0) in
|
||||
g#close();
|
||||
Index.GuessedArch inspected_arch
|
||||
else (
|
||||
eprintf (f_"%s: no ‘arch’ entry for ‘%s’\n") prog n;
|
||||
corrupt_file ()
|
||||
) in
|
||||
let signature_uri =
|
||||
try Some (make_absolute_uri (List.assoc ("sig", None) fields))
|
||||
with Not_found -> None in
|
||||
let checksum_sha512 =
|
||||
try Some (List.assoc ("checksum", Some "sha512") fields)
|
||||
with Not_found ->
|
||||
try Some (List.assoc ("checksum", None) fields)
|
||||
with Not_found -> None in
|
||||
let revision =
|
||||
try Rev_int (int_of_string (List.assoc ("revision", None) fields))
|
||||
with
|
||||
| Not_found -> if template then Rev_int 0 else Rev_int 1
|
||||
| Failure _ ->
|
||||
eprintf (f_"%s: cannot parse ‘revision’ field for ‘%s’\n") prog n;
|
||||
corrupt_file () in
|
||||
let format =
|
||||
try Some (List.assoc ("format", None) fields) with Not_found -> None in
|
||||
let size =
|
||||
let get_image_size filepath =
|
||||
(* If a compressed image manages to reach this code, qemu-img just
|
||||
returns a virtual-size equal to actual-size *)
|
||||
match detect_file_type filepath with
|
||||
| `Unknown ->
|
||||
let infos = Utils.get_image_infos filepath in
|
||||
JSON_parser.object_get_number "virtual-size" infos
|
||||
| `XZ | `GZip | `Tar | ` Zip ->
|
||||
eprintf (f_"%s: cannot determine the virtual size of %s due to compression")
|
||||
prog filepath;
|
||||
corrupt_file () in
|
||||
|
||||
try Int64.of_string (List.assoc ("size", None) fields)
|
||||
with
|
||||
| Not_found ->
|
||||
if template then
|
||||
get_image_size file_uri
|
||||
else (
|
||||
eprintf (f_"%s: no ‘size’ field for ‘%s’\n") prog n;
|
||||
corrupt_file ()
|
||||
)
|
||||
| Failure _ ->
|
||||
if template then
|
||||
get_image_size file_uri
|
||||
else (
|
||||
eprintf (f_"%s: cannot parse ‘size’ field for ‘%s’\n") prog n;
|
||||
corrupt_file ()
|
||||
) in
|
||||
let compressed_size =
|
||||
try Some (Int64.of_string (List.assoc ("compressed_size", None) fields))
|
||||
with
|
||||
| Not_found ->
|
||||
None
|
||||
| Failure _ ->
|
||||
eprintf (f_"%s: cannot parse ‘compressed_size’ field for ‘%s’\n")
|
||||
prog n;
|
||||
corrupt_file () in
|
||||
let expand =
|
||||
try Some (List.assoc ("expand", None) fields) with Not_found -> None in
|
||||
let lvexpand =
|
||||
try Some (List.assoc ("lvexpand", None) fields) with Not_found -> None in
|
||||
let notes =
|
||||
let rec loop = function
|
||||
| [] -> []
|
||||
| (("notes", subkey), value) :: xs ->
|
||||
let subkey = match subkey with
|
||||
| None -> ""
|
||||
| Some v -> v in
|
||||
(subkey, value) :: loop xs
|
||||
| _ :: xs -> loop xs in
|
||||
List.sort (
|
||||
fun (k1, _) (k2, _) ->
|
||||
String.compare k1 k2
|
||||
) (loop fields) in
|
||||
let hidden =
|
||||
try bool_of_string (List.assoc ("hidden", None) fields)
|
||||
with
|
||||
| Not_found -> false
|
||||
| Failure _ ->
|
||||
eprintf (f_"%s: cannot parse ‘hidden’ field for ‘%s’\n")
|
||||
prog n;
|
||||
corrupt_file () in
|
||||
let aliases =
|
||||
let l =
|
||||
try String.nsplit " " (List.assoc ("aliases", None) fields)
|
||||
with Not_found -> [] in
|
||||
match l with
|
||||
| [] -> None
|
||||
| l -> Some l in
|
||||
|
||||
let checksums =
|
||||
match checksum_sha512 with
|
||||
| Some c -> Some [Checksums.SHA512 c]
|
||||
| None -> None in
|
||||
|
||||
let entry = { Index.printable_name = printable_name;
|
||||
osinfo = osinfo;
|
||||
file_uri = file_uri;
|
||||
arch = arch;
|
||||
signature_uri = signature_uri;
|
||||
checksums = checksums;
|
||||
revision = revision;
|
||||
format = format;
|
||||
size = size;
|
||||
compressed_size = compressed_size;
|
||||
expand = expand;
|
||||
lvexpand = lvexpand;
|
||||
notes = notes;
|
||||
hidden = hidden;
|
||||
aliases = aliases;
|
||||
proxy = proxy;
|
||||
sigchecker = sigchecker } in
|
||||
n, entry
|
||||
) sections in
|
||||
|
||||
if verbose () then (
|
||||
printf "index file (%s) after parsing (C parser):\n" uri;
|
||||
List.iter (Index.print_entry Pervasives.stdout) entries
|
||||
);
|
||||
|
||||
entries
|
||||
|
||||
(* Verify same-origin policy for the file= and sig= fields. *)
|
||||
and make_absolute_uri path =
|
||||
if String.length path = 0 then (
|
||||
eprintf (f_"%s: zero length path in the index file\n") prog;
|
||||
corrupt_file ()
|
||||
)
|
||||
else if String.find path "://" >= 0 then (
|
||||
eprintf (f_"%s: cannot use a URI (‘%s’) in the index file\n") prog path;
|
||||
corrupt_file ()
|
||||
)
|
||||
else if path.[0] = '/' then (
|
||||
eprintf (f_"%s: you must use relative paths (not ‘%s’) in the index file\n") prog path;
|
||||
corrupt_file ()
|
||||
)
|
||||
else (
|
||||
(* Construct the URI. *)
|
||||
try
|
||||
let i = String.rindex uri '/' in
|
||||
String.sub uri 0 (i+1) ^ path
|
||||
with
|
||||
Not_found -> uri // path
|
||||
)
|
||||
in
|
||||
|
||||
get_index ()
|
||||
|
||||
let write_entry chan (name, { Index.printable_name; file_uri; arch; osinfo;
|
||||
signature_uri; checksums; revision; format; size;
|
||||
compressed_size; expand; lvexpand; notes;
|
||||
aliases; hidden}) =
|
||||
let fp fs = fprintf chan fs in
|
||||
fp "[%s]\n" name;
|
||||
Option.may (fp "name=%s\n") printable_name;
|
||||
Option.may (fp "osinfo=%s\n") osinfo;
|
||||
fp "file=%s\n" file_uri;
|
||||
fp "arch=%s\n" (Index.string_of_arch arch);
|
||||
Option.may (fp "sig=%s\n") signature_uri;
|
||||
(match checksums with
|
||||
| None -> ()
|
||||
| Some checksums ->
|
||||
List.iter (
|
||||
fun c ->
|
||||
fp "checksum[%s]=%s\n"
|
||||
(Checksums.string_of_csum_t c) (Checksums.string_of_csum c)
|
||||
) checksums
|
||||
);
|
||||
fp "revision=%s\n" (string_of_revision revision);
|
||||
Option.may (fp "format=%s\n") format;
|
||||
fp "size=%Ld\n" size;
|
||||
Option.may (fp "compressed_size=%Ld\n") compressed_size;
|
||||
Option.may (fp "expand=%s\n") expand;
|
||||
Option.may (fp "lvexpand=%s\n") lvexpand;
|
||||
|
||||
let format_notes notes =
|
||||
String.concat "\n " (String.nsplit "\n" notes) in
|
||||
|
||||
List.iter (
|
||||
fun (lang, notes) ->
|
||||
match lang with
|
||||
| "" -> fp "notes=%s\n" (format_notes notes)
|
||||
| lang -> fp "notes[%s]=%s\n" lang (format_notes notes)
|
||||
) notes;
|
||||
(match aliases with
|
||||
| None -> ()
|
||||
| Some l -> fp "aliases=%s\n" (String.concat " " l)
|
||||
);
|
||||
if hidden then fp "hidden=true\n";
|
||||
fp "\n"
|
||||
@@ -1,26 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
val get_index : downloader:Downloader.t -> sigchecker:Sigchecker.t -> ?template:bool -> Sources.source -> Index.index
|
||||
(** [get_index download sigchecker template source] will parse the source
|
||||
index file into an index entry list. If the template flag is set to
|
||||
true, the parser will be less picky about missing values. *)
|
||||
|
||||
val write_entry : out_channel -> (string * Index.entry) -> unit
|
||||
(** [write_entry chan entry] writes the index entry to the chan output
|
||||
stream.*)
|
||||
@@ -1,130 +0,0 @@
|
||||
(* builder
|
||||
* Copyright (C) 2017 SUSE Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
(* This file tests the Index_parser module. *)
|
||||
|
||||
open Printf
|
||||
|
||||
open OUnit2
|
||||
|
||||
open Std_utils
|
||||
open Unix_utils
|
||||
open Tools_utils
|
||||
|
||||
let tmpdir =
|
||||
let tmpdir = Mkdtemp.temp_dir "guestfs-tests." in
|
||||
rmdir_on_exit tmpdir;
|
||||
tmpdir
|
||||
|
||||
let dummy_sigchecker = Sigchecker.create ~gpg:"gpg"
|
||||
~check_signature:false
|
||||
~gpgkey:Utils.No_Key
|
||||
~tmpdir
|
||||
|
||||
let dummy_downloader = Downloader.create ~curl:"do-not-use-curl"
|
||||
~cache:None ~tmpdir
|
||||
|
||||
(* Utils. *)
|
||||
let write_entries file entries =
|
||||
let chan = open_out (tmpdir // file) in
|
||||
List.iter (Index_parser.write_entry chan) entries;
|
||||
close_out chan
|
||||
|
||||
let read_file file =
|
||||
read_whole_file (tmpdir // file)
|
||||
|
||||
let parse_file file =
|
||||
let source = { Sources.name = "input";
|
||||
uri = tmpdir // file;
|
||||
gpgkey = Utils.No_Key;
|
||||
proxy = Curl.SystemProxy;
|
||||
format = Sources.FormatNative } in
|
||||
let entries = Index_parser.get_index ~downloader:dummy_downloader
|
||||
~sigchecker:dummy_sigchecker
|
||||
source in
|
||||
List.map (
|
||||
fun (id, e) -> (id, { e with Index.file_uri = Filename.basename e.Index.file_uri })
|
||||
) entries
|
||||
|
||||
let format_entries entries =
|
||||
let format_entry entry =
|
||||
write_entries "out" [entry];
|
||||
read_file "out" in
|
||||
List.map format_entry entries
|
||||
|
||||
let assert_equal_string = assert_equal ~printer:(fun x -> sprintf "\"%s\"" x)
|
||||
let assert_equal_list formatter =
|
||||
let printer = (
|
||||
fun x -> "(" ^ (String.escaped (String.concat "," (formatter x))) ^ ")"
|
||||
) in
|
||||
assert_equal ~printer
|
||||
|
||||
let test_write_complete ctx =
|
||||
let entry =
|
||||
("test-id", { Index.printable_name = Some "test_name";
|
||||
osinfo = Some "osinfo_data";
|
||||
file_uri = "image_path";
|
||||
arch = Index.Arch "test_arch";
|
||||
signature_uri = None;
|
||||
checksums = Some [Checksums.SHA512 "512checksum"];
|
||||
revision = Utils.Rev_int 42;
|
||||
format = Some "qcow2";
|
||||
size = Int64.of_int 123456;
|
||||
compressed_size = Some (Int64.of_int 12345);
|
||||
expand = Some "/dev/sda1";
|
||||
lvexpand = Some "/some/lv";
|
||||
notes = [ ("", "Notes split\non several lines\n\n with starting space ") ];
|
||||
hidden = false;
|
||||
aliases = Some ["alias1"; "alias2"];
|
||||
sigchecker = dummy_sigchecker;
|
||||
proxy = Curl.SystemProxy }) in
|
||||
|
||||
write_entries "out" [entry];
|
||||
let actual = read_file "out" in
|
||||
let expected = "[test-id]
|
||||
name=test_name
|
||||
osinfo=osinfo_data
|
||||
file=image_path
|
||||
arch=test_arch
|
||||
checksum[sha512]=512checksum
|
||||
revision=42
|
||||
format=qcow2
|
||||
size=123456
|
||||
compressed_size=12345
|
||||
expand=/dev/sda1
|
||||
lvexpand=/some/lv
|
||||
notes=Notes split
|
||||
on several lines
|
||||
|
||||
with starting space
|
||||
aliases=alias1 alias2
|
||||
|
||||
" in
|
||||
assert_equal_string expected actual;
|
||||
|
||||
let parsed_entries = parse_file "out" in
|
||||
assert_equal_list format_entries [entry] parsed_entries
|
||||
|
||||
let suite =
|
||||
"builder Index_parser" >:::
|
||||
[
|
||||
"write.complete" >:: test_write_complete;
|
||||
]
|
||||
|
||||
let () =
|
||||
run_test_tt_main suite
|
||||
@@ -1,41 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
|
||||
type sections = section list
|
||||
and section = string * fields (* [name] + fields *)
|
||||
and fields = field list
|
||||
and field = string * string option * string (* key + subkey + value *)
|
||||
|
||||
(* Types returned by the C index parser. *)
|
||||
type c_sections = c_section array
|
||||
and c_section = string * c_fields (* [name] + fields *)
|
||||
and c_fields = field array
|
||||
|
||||
(* Calls yyparse in the C code. *)
|
||||
external parse_index : prog:string -> error_suffix:string -> string -> c_sections = "virt_builder_parse_index"
|
||||
|
||||
let read_ini ?(error_suffix = "") file =
|
||||
let sections = parse_index ~prog ~error_suffix file in
|
||||
let sections = Array.to_list sections in
|
||||
List.map (
|
||||
fun (n, fields) ->
|
||||
n, Array.to_list fields
|
||||
) sections
|
||||
@@ -1,24 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
type sections = section list
|
||||
and section = string * fields (* [name] + fields *)
|
||||
and fields = field list
|
||||
and field = string * string option * string (* key + subkey + value *)
|
||||
|
||||
val read_ini : ?error_suffix:string -> string -> sections
|
||||
@@ -1,57 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
|
||||
let re_locale =
|
||||
PCRE.compile ~caseless:true "^([a-z]+)(_([a-z]+))?(\\.([a-z0-9-]+))?(@([a-z]+))?$"
|
||||
|
||||
let split_locale loc =
|
||||
let l = ref [] in
|
||||
if PCRE.matches re_locale loc then (
|
||||
let match_or_empty n = try PCRE.sub n with Not_found -> "" in
|
||||
let lang = PCRE.sub 1 in
|
||||
let territory = match_or_empty 3 in
|
||||
(match territory with
|
||||
| "" -> ()
|
||||
| territory -> List.push_front (lang ^ "_" ^ territory) l);
|
||||
List.push_front lang l;
|
||||
);
|
||||
List.push_front "" l;
|
||||
List.rev !l
|
||||
|
||||
let languages () =
|
||||
match Setlocale.setlocale Setlocale.LC_MESSAGES None with
|
||||
| None -> [""]
|
||||
| Some locale -> split_locale locale
|
||||
|
||||
let find_notes languages notes =
|
||||
let notes = List.fold_left (
|
||||
fun acc lang ->
|
||||
let res = List.filter (
|
||||
fun (langkey, _) ->
|
||||
match langkey with
|
||||
| "C" -> lang = ""
|
||||
| langkey -> langkey = lang
|
||||
) notes in
|
||||
match res with
|
||||
| (_, noteskey) :: _ -> noteskey :: acc
|
||||
| [] -> acc
|
||||
) [] languages in
|
||||
List.rev notes
|
||||
@@ -1,21 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2014 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
val languages : unit -> string list
|
||||
|
||||
val find_notes : string list -> (string * string) list -> string list
|
||||
@@ -1,7 +0,0 @@
|
||||
[libguestfs.org]
|
||||
uri=http://builder.libguestfs.org/index.asc
|
||||
gpgkey=file://@SYSCONFDIR@/xdg/virt-builder/repos.d/libguestfs.gpg
|
||||
|
||||
[archive.libguestfs.org]
|
||||
uri=http://archive.libguestfs.org/builder/index.asc
|
||||
gpgkey=file://@SYSCONFDIR@/xdg/virt-builder/repos.d/libguestfs.gpg
|
||||
@@ -1,64 +0,0 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1.4.14 (GNU/Linux)
|
||||
|
||||
mQINBE6UMMEBEADM811hfTulaF4JpkVpAI10FImyb4ArvOiu8NdcUwTFo+cyWno3
|
||||
U85B86H1Bsk/LgLTYtthSrTgsCtdxy+i5OaMjxZDIwKQ2+IYI3FCn9T3Mn28Idyh
|
||||
kLHzrO9ph0Dv0BNfrlDZhQEC53aAFe/QxN7+A49BNBV7D1VAOOCsHjxMEDzcZkCa
|
||||
oCrtXw1aNm2vkkj5ukbfukHAyLcQL7kow0qKPSVa1G4lfQP0WiG259Ydy+sUmbVb
|
||||
TGdb6MEC84PQRDuw6/ZeoV04tn7ZNtQEMOS0uiciHOGfr2hBxQf9VIPNrHg42yaL
|
||||
dOv51D99GuaxZ9E0HSoH/RwB1oXgd6rFdqVNYaBIQnnkwJANUEeGBArtIOZNCADT
|
||||
Bt8vkSDm+lLEAFS+V8CACyW/LMIrGCvLdHeqtoAv0GDVyR2GPxldYfdtEmCUMWcb
|
||||
Jlf71V9iAse2gUdoiHp5FfpGMkA5j7idKuxIws11XxRZJXXbBqiBqmVEAQ/v0m6p
|
||||
kdo0MYTHydmecLuUK2bAGhpysfX97EfTSrxfrYphYWjTfKRD9GrADeZNfuz1DbKs
|
||||
7LSqVaQJSjQrfgAwcnZLRaU0V4P5zxiz50gz1Aj3AZRL+Y3meZenzZTXcLFdnusg
|
||||
wUfhhCuL3tluMtEh6tznumyxb43WO1yLwj6J6LtveiuJN1Z+KSQ6OieZcwARAQAB
|
||||
tCVSaWNoYXJkIFcuTS4gSm9uZXMgPHJpY2hAYW5uZXhpYS5vcmc+iQI4BBMBAgAi
|
||||
BQJOlDDBAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCRc49z4bdooHQY
|
||||
D/wJLklSZNyXIW+rG5sUbg7j9cTIF5p/lB9kI2yx6KodJp/2knKyvnmzz0gBw/OE
|
||||
HL4E4UW26oWKo+36I8wkBnuGa6UtANeITcJqFE19VpHEXHsxre64jNQnO8/w748W
|
||||
1ROW+Ry43xmrlRWKuCm4oPYUzlp0fq9ATAne8eblfG+NOs8DYuA8xZNQzFaI2kDC
|
||||
QLD4YoXLoNsP27Koga36b0KwxPFD9tyVZiu9XDH/3hMN7Nb15B66PFr+HcMmQ67G
|
||||
nUIN5ulcIwj38i40cyaTs1VRheOzTHXE/a6Q2AhMKiKqOoEjQ73/mV7cAVoPtM3o
|
||||
83Q/8aVKBH0bVRwAeV1tju6b14fqKoG0zNBEcXdlSkht6ScxJYIc/LPUxAMDwgSE
|
||||
OWshjmeRzKXypBbHn/DP8QVyM2gk5wY+mMSH7MpR0p/hgj+rFO8H9L7pC4dCog3E
|
||||
qzrYhRN+TaP6MPH3WkOwPH4d4IfQRFnHp+VPYPijKEiLrUl/o8k3DyAanAPBpJ/x
|
||||
na4wXAjlFBctOq6g+SrCUiHpwk7b2YNwGgr5Vl3GmZELzK/G8gg3uJYKQ9Bpv16t
|
||||
WWOz+IFiOFa0UULeo0QPmFAIMZiDojNsY1SwBKB3ZL1YWZezgMdQAbpze/IXoSt7
|
||||
zxWJoKH2jK7q9mvFiaY12l2YnKuCcegWVAViLxRpBnrbz7QmUmljaGFyZCBXLk0u
|
||||
IEpvbmVzIDxyam9uZXNAcmVkaGF0LmNvbT6JAjgEEwECACIFAk6UOQsCGwMGCwkI
|
||||
BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEJFzj3Pht2igIUYQAKomI0edLakahsUQ
|
||||
MxOZuhBbXJ4/VWF8bXYChDNPKvJp5nB7fBXujJ+39cIUM5fe2ViO6qSDpFC29imx
|
||||
F5pPbAqspZBPBkLLiZLji8R42hGarntdtTW0UWSBpq+nC5+G1psrnATI3uXGNxKQ
|
||||
R99c5HoMY7dBC2Y8TCGE64NINZ/XVh472s6IGLPn8MTn26YdRKC9BrVkCFMP2OBr
|
||||
6D4IprnyTAWAzb68ew20QmyWO+NBi9MplaDNQVl8PIOgfpyWlkgX1z9m67pcSDkw
|
||||
46hksp0yuOD1VwR4iVZ2/CmIsGRUlx41vWD6BIp9KxKyDIU1CYTRhq72dahHsl/8
|
||||
BjCndV5PO0GphqfCzmCv4DXjUwmrMTbH/GFnt5rfwcMcXUgcK0vV9vQ2SOU56Zd1
|
||||
fb27ZCFJKZc0Fu8krwFldCp/NYILf6ogUL/C1hfuCGSSuyDVY16Gg3dla1x+6zpF
|
||||
asnWQlaw8xT5LlMWvTZs5WsoSVHu7dVZWlgxINP++hlZrTz/S8l38yyQ15YFFl3W
|
||||
9M7dzkegOeDTPfx6B89WgfvfJjA/D0/FYxxWPXEtrn9DlJ4daEJqNsrvfLErz9R8
|
||||
4IQmfmhR93j+rdotner+6keC/wVByEfbW1wmXtmFKXQ6srdpj8VKRFrvkyXVgepM
|
||||
DypLgRH2v7lL2kdWhUu2y4EAgrwzuQINBE6UMMEBEADxQxMgUuDrw5GT4tqARTPI
|
||||
SSdNcUsRxRhVA8srYOyECliE+B3TwcRDFBs+MyPFJVEuX8fi4eGj/AK5t1GHerfk
|
||||
orUGlz72q4c7LLhkfZrsuJbk2dgkjvldKJnIazQJa6epGLqdsE5RlmSgwedIbtMd
|
||||
naGJBQH8aKP/Wi1+wUxsm5N3p7+R2WRx48VfpEhYB+Zf/FkFm1Ycjwh57KQ0+OHw
|
||||
ykf8VfMisxuH30tDxOCV+VptWKfOF2rDNdaNPWhij2YIjhJXRpkuRR+1PpI4jLaD
|
||||
JxcVZmG/0zucacupUN2g5OUH59ySU/totD6YMnmp3FONoyF1uIEJo6Vs30npHGkO
|
||||
XgBo3Pxt7oLJeykLPtdSLgm3cwXIYMWarVsAkKNXitQIVGpVRLeaK373VwmXFqoi
|
||||
M2SMHeawTUdOORFjpQzkknlJWM1TmUVtHHKt8Pl9+/5+wXKyt2IDdcUkMrB6K5qF
|
||||
fb7EwVhoI8ehJQK+eeDCjFwCAiwB3iV8JlyW+tEU7JuyXOQlwY1VWm/WqMD8gaRi
|
||||
rT+RFDFliZ3tQbW2pqUoZBROV5HN4tieDfwxGKCvk6Tsdb30zA9DPQp93+238bYf
|
||||
312sg9R+CD0AqxoxFG5FJu4HShcPRrPnYtRZqKRe40GDWvBEArXZprwL1qrP+Kl/
|
||||
mRrEQpxAGIoFG8HbVvD3EQARAQABiQIfBBgBAgAJBQJOlDDBAhsMAAoJEJFzj3Ph
|
||||
t2igSLQP/2uIrAY2CDr0kWBJiD3TztiHy8IdxwUpyTBTebwmAbi44/EvtJfIisrG
|
||||
YjKIEv/w0E61gO7O1JBG4+IG93W+v9fTT/e39JMyxsYqoZZHUhP11Okx5grDS5b0
|
||||
O8VXOmXVRMdVNfstRBr10HD9uNDq7ruKD18TxYTwN0GPD4gj1dbHQDR77Tr5cyBs
|
||||
6Ou5PBOH4r3qcqf/cJUSMeUUu75xLwixux6E7tD2S+t6F07wlWxntUcPtzyAHj20
|
||||
J89orUC+dT6r6MypBoI0jdJCp9JPGtR7i+fE5Gm4E5+AUSubLPtZGRY9Um2eMoS2
|
||||
DnQpGOKx1VvsixR/Kw44j2tRAvmYMS4iDKcuZU+nZ+xokAgObILj/b9n/Qe2/fXy
|
||||
CFdcgSvbm+dV1fZxsdMF/P9OU8aqdT9A9Fv5y+cDMEg4DVnhwMJTxGh/TCkw/H+A
|
||||
frHEtRc98lSQN5odpITNG17mG6JOdHM+wA57qHH0uy4+5RsbyAJahcdBcmObK/RF
|
||||
i4WZlThpbHftX5O/LH98aYQ2fJayIxv1EAjzOBOQ0MfBHI0KCJR1pysEisX28sJA
|
||||
Ic73gnJJ3BLZbqfBRgxjNMNroxC+5Tw6uPGFHa3YnuIAxxw0HcDVZ9vnTWBWFPGw
|
||||
ZvXkQ3FVJwZoLmHw47vvlVpLD/4gi1SuHWieRvZ+UdDq00E348pm
|
||||
=neBW
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -1,156 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open Printf
|
||||
|
||||
type format =
|
||||
| Short
|
||||
| Long
|
||||
| Json
|
||||
|
||||
let list_formats = [ "short"; "long"; "json" ]
|
||||
|
||||
let list_format_of_string = function
|
||||
| "short" -> Short
|
||||
| "long" -> Long
|
||||
| "json" -> Json
|
||||
| fmt -> invalid_arg fmt
|
||||
|
||||
let rec list_entries ~list_format ~sources index =
|
||||
match list_format with
|
||||
| Short -> list_entries_short index
|
||||
| Long -> list_entries_long ~sources index
|
||||
| Json -> list_entries_json ~sources index
|
||||
|
||||
and list_entries_short index =
|
||||
List.iter (
|
||||
fun (name, { Index.printable_name; arch; hidden }) ->
|
||||
if not hidden then (
|
||||
printf "%-24s" name;
|
||||
printf " %-10s" (Index.string_of_arch arch);
|
||||
Option.may (printf " %s") printable_name;
|
||||
printf "\n"
|
||||
)
|
||||
) index
|
||||
|
||||
and list_entries_long ~sources index =
|
||||
let langs = Languages.languages () in
|
||||
|
||||
List.iter (
|
||||
fun { Sources.uri; gpgkey } ->
|
||||
printf (f_"Source URI: %s\n") uri;
|
||||
(match gpgkey with
|
||||
| Utils.No_Key -> ()
|
||||
| Utils.Fingerprint fp ->
|
||||
printf (f_"Fingerprint: %s\n") fp;
|
||||
| Utils.KeyFile kf ->
|
||||
printf (f_"Key: %s\n") kf;
|
||||
);
|
||||
printf "\n"
|
||||
) sources;
|
||||
|
||||
List.iter (
|
||||
fun (name, { Index.printable_name; arch; size; compressed_size;
|
||||
notes; aliases; hidden }) ->
|
||||
if not hidden then (
|
||||
printf "%-24s %s\n" "os-version:" name;
|
||||
Option.may (printf "%-24s %s\n" (s_"Full name:")) printable_name;
|
||||
printf "%-24s %s\n" (s_"Architecture:") (Index.string_of_arch arch);
|
||||
printf "%-24s %s\n" (s_"Minimum/default size:") (human_size size);
|
||||
Option.may (fun size ->
|
||||
printf "%-24s %s\n" (s_"Download size:") (human_size size)
|
||||
) compressed_size;
|
||||
Option.may (
|
||||
fun l -> printf "%-24s %s\n" (s_"Aliases:") (String.concat " " l)
|
||||
) aliases;
|
||||
let notes = Languages.find_notes langs notes in
|
||||
(match notes with
|
||||
| notes :: _ ->
|
||||
printf "\n";
|
||||
printf (f_"Notes:\n\n%s\n") notes
|
||||
| [] -> ()
|
||||
);
|
||||
printf "\n"
|
||||
)
|
||||
) index
|
||||
|
||||
and list_entries_json ~sources index =
|
||||
let json_sources =
|
||||
List.map (
|
||||
fun { Sources.uri; gpgkey } ->
|
||||
let item = [ "uri", JSON.String uri ] in
|
||||
let item =
|
||||
match gpgkey with
|
||||
| Utils.No_Key -> item
|
||||
| Utils.Fingerprint fp ->
|
||||
("fingerprint", JSON.String fp) :: item
|
||||
| Utils.KeyFile kf ->
|
||||
("key", JSON.String kf) :: item in
|
||||
JSON.Dict item
|
||||
) sources in
|
||||
let json_templates =
|
||||
List.map (
|
||||
fun (name, { Index.printable_name; arch; size; compressed_size;
|
||||
notes; aliases; osinfo; hidden }) ->
|
||||
let item = [ "os-version", JSON.String name ] in
|
||||
let item =
|
||||
match printable_name with
|
||||
| None -> item
|
||||
| Some str -> ("full-name", JSON.String str) :: item in
|
||||
let item = ("arch", JSON.String (Index.string_of_arch arch)) :: item in
|
||||
let item = ("size", JSON.Int size) :: item in
|
||||
let item =
|
||||
match compressed_size with
|
||||
| None -> item
|
||||
| Some n -> ("compressed-size", JSON.String (Int64.to_string n)) :: item in
|
||||
let item =
|
||||
let json_notes =
|
||||
List.fold_right (
|
||||
fun (lang, langnotes) acc ->
|
||||
let lang =
|
||||
match lang with
|
||||
| "" -> "C"
|
||||
| x -> x in
|
||||
(lang, JSON.String langnotes) :: acc
|
||||
) notes [] in
|
||||
if List.length json_notes = 0 then item
|
||||
else ("notes", JSON.Dict json_notes) :: item in
|
||||
let item =
|
||||
match aliases with
|
||||
| None -> item
|
||||
| Some l ->
|
||||
let l = List.map (fun x -> JSON.String x) l in
|
||||
("aliases", JSON.List l) :: item in
|
||||
let item =
|
||||
match osinfo with
|
||||
| None -> item
|
||||
| Some str -> ("osinfo", JSON.String str) :: item in
|
||||
let item = ("hidden", JSON.Bool hidden) :: item in
|
||||
JSON.Dict (List.rev item)
|
||||
) index in
|
||||
let doc = [
|
||||
"version", JSON.Int 1L;
|
||||
"sources", JSON.List json_sources;
|
||||
"templates", JSON.List json_templates;
|
||||
] in
|
||||
print_string (JSON.string_of_doc ~fmt:JSON.Indented doc);
|
||||
print_newline ()
|
||||
@@ -1,33 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
type format =
|
||||
| Short
|
||||
| Long
|
||||
| Json
|
||||
|
||||
val list_formats : string list
|
||||
(** The string representation of the available formats. *)
|
||||
|
||||
val list_format_of_string : string -> format
|
||||
(** Convert from a string to the corresponding format.
|
||||
|
||||
Throw [Invalid_argument] if the string does not match any
|
||||
valid format. *)
|
||||
|
||||
val list_entries : list_format:format -> sources:Sources.source list -> Index.index -> unit
|
||||
@@ -1,3 +0,0 @@
|
||||
[opensuse.org]
|
||||
uri=http://download.opensuse.org/repositories/Virtualization:/virt-builder-images/images/index
|
||||
gpgkey=file://@SYSCONFDIR@/xdg/virt-builder/repos.d/opensuse.gpg
|
||||
@@ -1,21 +0,0 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1.4.5 (GNU/Linux)
|
||||
|
||||
mQENBFImAl0BCACkjaXGvVLHBGTVXVP0khtpUVHqFvCRtaIIMHaX/5oTr3nyehDQ
|
||||
Ex9VLsSRcNa0QxtnCHFRQzjWWqe+i6pBginnSjucgmjnIKyJsF4l6R+rwAiinHQX
|
||||
C4s6Lqg/wH9xDPRBrMYFqlc/7MVf0Glhk1+lAxgQjolMt+5AbbrWlBbwc/i+++zl
|
||||
ES3MaeH8aiwup/ogjhmk0SbCQQ/ib21p3XWBwx2oz/KM6Voq9tKDvMczjzNRY3ZT
|
||||
6Di3FsUSKI7kgljiNiuN+675YwqEqxWEJgdE5a7Zb67giH1Ik08b5wQiF5jSAICD
|
||||
DxW7/ibWBvZJnqhqQT2xJpLC5VaJqwkN8o83ABEBAAG0PlZpcnR1YWxpemF0aW9u
|
||||
IE9CUyBQcm9qZWN0IDxWaXJ0dWFsaXphdGlvbkBidWlsZC5vcGVuc3VzZS5vcmc+
|
||||
iQE7BBMBAgAmBQJSJgJdAhsDBQkEHrAABgsJCAcDAgQVAggDBBYCAwECHgECF4AA
|
||||
CgkQoZP7tXIXT8ITnwf3SVUUoVjVLFCjhIxdet8BL011cJDwr9TwKEQfq4Ybsq5L
|
||||
5Y1/Zk86rTzrVOZrODLwNRIC3fMuegZV5f85KMggXu37Di+UvX+dQW9v1hte+hAT
|
||||
+gsqb60kOnE/Yacgkb6D3xIzRudAB2q/xfvHl/hgfn416yGI8NvntT7n4Hk9wT28
|
||||
9JSFkun0uaessg77aXlAdsqHwdugm9hELeva89OoYoiZ4d9r4ScTMSj0UkNgnh7g
|
||||
CyIScZHYqiiOeosUtAX9u1PyUFfFsg9s5snfud7aF48EfXU0RTtZAGKtG4GPDv3q
|
||||
bYc5TJ2pQzs9y5Bk/jAMR/QQw8CKglBsn1cjYkKViEYEExECAAYFAlImAl0ACgkQ
|
||||
OzARt2udZSO5yACgr6Ei7QZ+PAmg4Mr5db+4M3aepAEAniU33RaTKBCGkwQi6kHr
|
||||
4VaII2/E
|
||||
=l8DH
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -1,76 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2017 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Osinfo_config
|
||||
|
||||
let rec fold fn base =
|
||||
let locations =
|
||||
(* (1) Try the shared osinfo directory, using either the
|
||||
* $OSINFO_SYSTEM_DIR envvar or its default value.
|
||||
*)
|
||||
let dir =
|
||||
try Sys.getenv "OSINFO_SYSTEM_DIR"
|
||||
with Not_found -> "/usr/share/osinfo" in
|
||||
((dir // "os"), read_osinfo_db_three_levels) ::
|
||||
|
||||
(* (2) Try the libosinfo directory, using the newer three-directory
|
||||
* layout ($LIBOSINFO_DB_PATH / "os" / $group-ID / [file.xml]).
|
||||
*)
|
||||
let path = Osinfo_config.libosinfo_db_path // "os" in
|
||||
(path, read_osinfo_db_three_levels) ::
|
||||
|
||||
(* (3) Try the libosinfo directory, using the old flat directory
|
||||
* layout ($LIBOSINFO_DB_PATH / "oses" / [file.xml]).
|
||||
*)
|
||||
let path = Osinfo_config.libosinfo_db_path // "oses" in
|
||||
(path, read_osinfo_db_flat) :: [] in
|
||||
|
||||
|
||||
let files =
|
||||
List.flatten (
|
||||
List.filter_map (
|
||||
fun (path, f) ->
|
||||
if is_directory path then Some (f path)
|
||||
(* This is not an error: RHBZ#948324. *)
|
||||
else None
|
||||
) locations
|
||||
) in
|
||||
|
||||
List.fold_left fn base files
|
||||
|
||||
and read_osinfo_db_three_levels path =
|
||||
debug "osinfo: loading 3-level-directories database from %s" path;
|
||||
let entries = Array.to_list (Sys.readdir path) in
|
||||
let entries = List.map ((//) path) entries in
|
||||
(* Iterate only on directories. *)
|
||||
let entries = List.filter is_directory entries in
|
||||
List.flatten (List.map read_osinfo_db_directory entries)
|
||||
|
||||
and read_osinfo_db_flat path =
|
||||
debug "osinfo: loading flat database from %s" path;
|
||||
read_osinfo_db_directory path
|
||||
|
||||
and read_osinfo_db_directory path =
|
||||
let entries = Sys.readdir path in
|
||||
let entries = Array.to_list entries in
|
||||
let entries = List.filter (fun x -> Filename.check_suffix x ".xml") entries in
|
||||
let entries = List.map ((//) path) entries in
|
||||
let entries = List.filter is_regular_file entries in
|
||||
entries
|
||||
@@ -1,22 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2017 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
val fold : ('a -> string -> 'a) -> 'a -> 'a
|
||||
(** [fold f base] folds function [f] over every file in the
|
||||
osinfo-db/libosinfo database of OS definitions.
|
||||
*)
|
||||
@@ -1,21 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2017 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
val libosinfo_db_path : string
|
||||
(** The path to the libosinfo database. Note the path is generated
|
||||
at runtime by [Makefile.am]. *)
|
||||
@@ -1,42 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2014 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
|
||||
let xdg_cache_home =
|
||||
try Some (Sys.getenv "XDG_CACHE_HOME" // "virt-builder")
|
||||
with Not_found ->
|
||||
try Some (Sys.getenv "HOME" // ".cache" // "virt-builder")
|
||||
with Not_found ->
|
||||
None (* no cache directory *)
|
||||
|
||||
let xdg_config_home () =
|
||||
try Some (Sys.getenv "XDG_CONFIG_HOME" // prog)
|
||||
with Not_found ->
|
||||
try Some (Sys.getenv "HOME" // ".config" // prog)
|
||||
with Not_found ->
|
||||
None (* no config directory *)
|
||||
|
||||
let xdg_config_dirs () =
|
||||
let dirs =
|
||||
try Sys.getenv "XDG_CONFIG_DIRS"
|
||||
with Not_found -> "/etc/xdg" in
|
||||
let dirs = String.nsplit ":" dirs in
|
||||
let dirs = List.filter (fun x -> x <> "") dirs in
|
||||
List.map (fun x -> x // prog) dirs
|
||||
@@ -1,28 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2014-2020 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
val xdg_cache_home : string option
|
||||
(** [$XDG_CACHE_HOME/virt-builder] or [$HOME/.cache/virt-builder] or [None]. *)
|
||||
|
||||
val xdg_config_home : unit -> string option
|
||||
(** [$XDG_CONFIG_HOME/prog] or [$HOME/.config/prog] or [None]. *)
|
||||
|
||||
val xdg_config_dirs : unit -> string list
|
||||
(** [$XDG_CONFIG_DIRS] (which is a colon-separated path), split. Empty
|
||||
elements are removed from the list. If the environment variable
|
||||
is not set [["/etc/xdg"]] is returned instead. *)
|
||||
@@ -1,692 +0,0 @@
|
||||
/* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <caml/alloc.h>
|
||||
#include <caml/fail.h>
|
||||
#include <caml/memory.h>
|
||||
#include <caml/mlvalues.h>
|
||||
#include <caml/unixsupport.h>
|
||||
|
||||
#include "guestfs.h"
|
||||
#include "guestfs-utils.h"
|
||||
|
||||
#include "ignore-value.h"
|
||||
|
||||
#if HAVE_LIBLZMA
|
||||
#include <lzma.h>
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LIBLZMA) && \
|
||||
defined (HAVE_LZMA_INDEX_STREAM_FLAGS) && \
|
||||
defined (HAVE_LZMA_INDEX_STREAM_PADDING)
|
||||
#define PARALLEL_XZCAT 1
|
||||
#else
|
||||
#define PARALLEL_XZCAT 0
|
||||
#endif
|
||||
|
||||
extern value virt_builder_using_parallel_xzcat (value unitv);
|
||||
|
||||
value
|
||||
virt_builder_using_parallel_xzcat (value unitv)
|
||||
{
|
||||
return PARALLEL_XZCAT ? Val_true : Val_false;
|
||||
}
|
||||
|
||||
#if PARALLEL_XZCAT
|
||||
static void pxzcat (value filenamev, value outputfilev, unsigned nr_threads);
|
||||
#endif /* PARALLEL_XZCAT */
|
||||
|
||||
extern value virt_builder_pxzcat (value inputfilev, value outputfilev);
|
||||
|
||||
value
|
||||
virt_builder_pxzcat (value inputfilev, value outputfilev)
|
||||
{
|
||||
CAMLparam2 (inputfilev, outputfilev);
|
||||
|
||||
#if PARALLEL_XZCAT
|
||||
|
||||
/* Parallel implementation of xzcat (pxzcat). */
|
||||
/* XXX Make number of threads configurable? */
|
||||
long i;
|
||||
unsigned nr_threads;
|
||||
|
||||
i = sysconf (_SC_NPROCESSORS_ONLN);
|
||||
if (i <= 0) {
|
||||
perror ("could not get number of cores");
|
||||
i = 1;
|
||||
}
|
||||
nr_threads = (unsigned) i;
|
||||
|
||||
/* NB: This might throw an exception if something fails. If it
|
||||
* does, this function won't return as a regular C function.
|
||||
*/
|
||||
pxzcat (inputfilev, outputfilev, nr_threads);
|
||||
|
||||
#else /* !PARALLEL_XZCAT */
|
||||
|
||||
/* Fallback: use regular xzcat. */
|
||||
int fd;
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
fd = open (String_val (outputfilev), O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666);
|
||||
if (fd == -1)
|
||||
unix_error (errno, (char *) "open", outputfilev);
|
||||
|
||||
pid = fork ();
|
||||
if (pid == -1) {
|
||||
const int err = errno;
|
||||
close (fd);
|
||||
unix_error (err, (char *) "fork", Nothing);
|
||||
}
|
||||
|
||||
if (pid == 0) { /* child - run xzcat */
|
||||
dup2 (fd, 1);
|
||||
execlp (XZCAT, XZCAT, String_val (inputfilev), NULL);
|
||||
perror (XZCAT);
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close (fd);
|
||||
|
||||
if (waitpid (pid, &status, 0) == -1)
|
||||
unix_error (errno, (char *) "waitpid", Nothing);
|
||||
if (!WIFEXITED (status) || WEXITSTATUS (status) != 0)
|
||||
caml_failwith (XZCAT " program failed, see earlier error messages");
|
||||
|
||||
#endif /* !PARALLEL_XZCAT */
|
||||
|
||||
CAMLreturn (Val_unit);
|
||||
}
|
||||
|
||||
#if PARALLEL_XZCAT
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
#define debug(fs,...) fprintf (stderr, "pxzcat: debug: " fs "\n", ## __VA_ARGS__)
|
||||
#else
|
||||
#define debug(fs,...) /* nothing */
|
||||
#endif
|
||||
|
||||
/* Size of buffers used in decompression loop. */
|
||||
#define BUFFER_SIZE (64*1024)
|
||||
|
||||
#define XZ_HEADER_MAGIC "\xfd" "7zXZ\0"
|
||||
#define XZ_HEADER_MAGIC_LEN 6
|
||||
|
||||
static int check_header_magic (int fd);
|
||||
static lzma_index *parse_indexes (value filenamev, int fd);
|
||||
static void iter_blocks (lzma_index *idx, unsigned nr_threads, value filenamev, int fd, value outputfilev, int ofd);
|
||||
|
||||
static void
|
||||
pxzcat (value filenamev, value outputfilev, unsigned nr_threads)
|
||||
{
|
||||
int fd, ofd;
|
||||
uint64_t size;
|
||||
lzma_index *idx;
|
||||
|
||||
/* Open the file. */
|
||||
fd = open (String_val (filenamev), O_RDONLY);
|
||||
if (fd == -1)
|
||||
unix_error (errno, (char *) "open", filenamev);
|
||||
|
||||
guestfs_int_fadvise_noreuse (fd);
|
||||
guestfs_int_fadvise_random (fd);
|
||||
|
||||
/* Check file magic. */
|
||||
if (!check_header_magic (fd)) {
|
||||
close (fd);
|
||||
caml_invalid_argument ("input file is not an xz file");
|
||||
}
|
||||
|
||||
/* Read and parse the indexes. */
|
||||
idx = parse_indexes (filenamev, fd);
|
||||
|
||||
/* Get the file uncompressed size, create the output file. */
|
||||
size = lzma_index_uncompressed_size (idx);
|
||||
debug ("uncompressed size = %" PRIu64 " bytes", size);
|
||||
|
||||
/* Avoid annoying ext4 auto_da_alloc which causes a flush on close
|
||||
* unless we are very careful about not truncating a regular file
|
||||
* from non-zero size to zero size. (Thanks Eric Sandeen)
|
||||
*/
|
||||
ofd = open (String_val (outputfilev), O_WRONLY|O_CREAT|O_NOCTTY, 0644);
|
||||
if (ofd == -1) {
|
||||
const int err = errno;
|
||||
close (fd);
|
||||
unix_error (err, (char *) "open", outputfilev);
|
||||
}
|
||||
|
||||
guestfs_int_fadvise_random (ofd);
|
||||
|
||||
if (ftruncate (ofd, 1) == -1) {
|
||||
const int err = errno;
|
||||
close (fd);
|
||||
unix_error (err, (char *) "ftruncate", outputfilev);
|
||||
}
|
||||
|
||||
if (lseek (ofd, 0, SEEK_SET) == -1) {
|
||||
const int err = errno;
|
||||
close (fd);
|
||||
unix_error (err, (char *) "lseek", outputfilev);
|
||||
}
|
||||
|
||||
if (write (ofd, "\0", 1) == -1) {
|
||||
const int err = errno;
|
||||
close (fd);
|
||||
unix_error (err, (char *) "write", outputfilev);
|
||||
}
|
||||
|
||||
if (ftruncate (ofd, size) == -1) {
|
||||
const int err = errno;
|
||||
close (fd);
|
||||
unix_error (err, (char *) "ftruncate", outputfilev);
|
||||
}
|
||||
|
||||
/* Iterate over blocks. */
|
||||
iter_blocks (idx, nr_threads, filenamev, fd, outputfilev, ofd);
|
||||
|
||||
lzma_index_end (idx, NULL);
|
||||
|
||||
if (close (fd) == -1)
|
||||
unix_error (errno, (char *) "close", filenamev);
|
||||
|
||||
if (close (ofd) == -1)
|
||||
unix_error (errno, (char *) "close", outputfilev);
|
||||
}
|
||||
|
||||
static int
|
||||
check_header_magic (int fd)
|
||||
{
|
||||
char buf[XZ_HEADER_MAGIC_LEN];
|
||||
|
||||
if (lseek (fd, 0, SEEK_SET) == -1)
|
||||
return 0;
|
||||
if (read (fd, buf, XZ_HEADER_MAGIC_LEN) != XZ_HEADER_MAGIC_LEN)
|
||||
return 0;
|
||||
if (memcmp (buf, XZ_HEADER_MAGIC, XZ_HEADER_MAGIC_LEN) != 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* For explanation of this function, see src/xz/list.c:parse_indexes
|
||||
* in the xz sources.
|
||||
*/
|
||||
static lzma_index *
|
||||
parse_indexes (value filenamev, int fd)
|
||||
{
|
||||
lzma_ret r;
|
||||
off_t pos, index_size;
|
||||
CLEANUP_FREE uint8_t *footer = NULL;
|
||||
CLEANUP_FREE uint8_t *header = NULL;
|
||||
lzma_stream_flags footer_flags;
|
||||
lzma_stream_flags header_flags;
|
||||
lzma_stream strm = LZMA_STREAM_INIT;
|
||||
ssize_t n;
|
||||
lzma_index *combined_index = NULL;
|
||||
lzma_index *this_index = NULL;
|
||||
lzma_vli stream_padding = 0;
|
||||
size_t nr_streams = 0;
|
||||
CLEANUP_FREE uint8_t *buf = NULL;
|
||||
|
||||
footer = malloc (sizeof (uint8_t) * LZMA_STREAM_HEADER_SIZE);
|
||||
header = malloc (sizeof (uint8_t) * LZMA_STREAM_HEADER_SIZE);
|
||||
buf = malloc (sizeof (uint8_t) * BUFSIZ);
|
||||
if (footer == NULL || header == NULL || buf == NULL)
|
||||
caml_raise_out_of_memory ();
|
||||
|
||||
/* Check file size is a multiple of 4 bytes. */
|
||||
pos = lseek (fd, 0, SEEK_END);
|
||||
if (pos == (off_t) -1)
|
||||
unix_error (errno, (char *) "lseek", filenamev);
|
||||
|
||||
if ((pos & 3) != 0)
|
||||
caml_invalid_argument ("input not an xz file: size is not a multiple of 4 bytes");
|
||||
|
||||
/* Jump backwards through the file identifying each stream. */
|
||||
while (pos > 0) {
|
||||
debug ("looping through streams: pos = %" PRIu64, (uint64_t) pos);
|
||||
|
||||
if (pos < LZMA_STREAM_HEADER_SIZE)
|
||||
caml_invalid_argument ("corrupted xz file");
|
||||
|
||||
if (lseek (fd, -LZMA_STREAM_HEADER_SIZE, SEEK_CUR) == -1)
|
||||
unix_error (errno, (char *) "lseek", filenamev);
|
||||
|
||||
if (read (fd, footer, LZMA_STREAM_HEADER_SIZE) != LZMA_STREAM_HEADER_SIZE)
|
||||
unix_error (errno, (char *) "read", filenamev);
|
||||
|
||||
/* Skip stream padding. */
|
||||
if (footer[8] == 0 && footer[9] == 0 &&
|
||||
footer[10] == 0 && footer[11] == 0) {
|
||||
stream_padding += 4;
|
||||
pos -= 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
pos -= LZMA_STREAM_HEADER_SIZE;
|
||||
nr_streams++;
|
||||
|
||||
debug ("decode stream footer at pos = %" PRIu64, (uint64_t) pos);
|
||||
|
||||
/* Does the stream footer look reasonable? */
|
||||
r = lzma_stream_footer_decode (&footer_flags, footer);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "invalid stream footer - error %u\n", r);
|
||||
caml_invalid_argument ("invalid stream footer");
|
||||
}
|
||||
|
||||
debug ("backward_size = %" PRIu64, (uint64_t) footer_flags.backward_size);
|
||||
index_size = footer_flags.backward_size;
|
||||
if (pos < index_size + LZMA_STREAM_HEADER_SIZE)
|
||||
caml_invalid_argument ("invalid stream footer");
|
||||
|
||||
pos -= index_size;
|
||||
debug ("decode index at pos = %" PRIu64, (uint64_t) pos);
|
||||
|
||||
/* Seek backwards to the index of this stream. */
|
||||
if (lseek (fd, pos, SEEK_SET) == -1)
|
||||
unix_error (errno, (char *) "lseek", filenamev);
|
||||
|
||||
/* Decode the index. */
|
||||
r = lzma_index_decoder (&strm, &this_index, UINT64_MAX);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "invalid stream index - error %u\n", r);
|
||||
caml_invalid_argument ("invalid stream index");
|
||||
}
|
||||
|
||||
do {
|
||||
strm.avail_in = index_size;
|
||||
if (strm.avail_in > BUFSIZ)
|
||||
strm.avail_in = BUFSIZ;
|
||||
|
||||
n = read (fd, buf, strm.avail_in);
|
||||
if (n == -1)
|
||||
unix_error (errno, (char *) "read", filenamev);
|
||||
|
||||
index_size -= strm.avail_in;
|
||||
|
||||
strm.next_in = buf;
|
||||
r = lzma_code (&strm, LZMA_RUN);
|
||||
} while (r == LZMA_OK);
|
||||
|
||||
if (r != LZMA_STREAM_END) {
|
||||
fprintf (stderr, "could not parse index - error %u\n", r);
|
||||
caml_invalid_argument ("could not parse index");
|
||||
}
|
||||
|
||||
pos -= lzma_index_total_size (this_index) + LZMA_STREAM_HEADER_SIZE;
|
||||
|
||||
debug ("decode stream header at pos = %" PRIu64, (uint64_t) pos);
|
||||
|
||||
/* Read and decode the stream header. */
|
||||
if (lseek (fd, pos, SEEK_SET) == -1)
|
||||
unix_error (errno, (char *) "lseek", filenamev);
|
||||
|
||||
if (read (fd, header, LZMA_STREAM_HEADER_SIZE) != LZMA_STREAM_HEADER_SIZE)
|
||||
unix_error (errno, (char *) "read stream header", filenamev);
|
||||
|
||||
r = lzma_stream_header_decode (&header_flags, header);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "invalid stream header - error %u\n", r);
|
||||
caml_invalid_argument ("invalid stream header");
|
||||
}
|
||||
|
||||
/* Header and footer of the stream should be equal. */
|
||||
r = lzma_stream_flags_compare (&header_flags, &footer_flags);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "header and footer of stream are not equal - error %u\n",
|
||||
r);
|
||||
caml_invalid_argument ("header and footer of stream are not equal");
|
||||
}
|
||||
|
||||
/* Store the decoded stream flags in this_index. */
|
||||
r = lzma_index_stream_flags (this_index, &footer_flags);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "cannot read stream_flags from index - error %u\n", r);
|
||||
caml_invalid_argument ("cannot read stream_flags from index");
|
||||
}
|
||||
|
||||
/* Store the amount of stream padding so far. Needed to calculate
|
||||
* compressed offsets correctly in multi-stream files.
|
||||
*/
|
||||
r = lzma_index_stream_padding (this_index, stream_padding);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "cannot set stream_padding in index - error %u\n", r);
|
||||
caml_invalid_argument ("cannot set stream_padding in index");
|
||||
}
|
||||
|
||||
if (combined_index != NULL) {
|
||||
r = lzma_index_cat (this_index, combined_index, NULL);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "cannot combine indexes - error %u\n", r);
|
||||
caml_invalid_argument ("cannot combine indexes");
|
||||
}
|
||||
}
|
||||
|
||||
combined_index = this_index;
|
||||
this_index = NULL;
|
||||
}
|
||||
|
||||
lzma_end (&strm);
|
||||
|
||||
return combined_index;
|
||||
}
|
||||
|
||||
struct global_state {
|
||||
/* Current iterator. Threads update this, but it is protected by a
|
||||
* mutex, and each thread takes a copy of it when working on it.
|
||||
*/
|
||||
lzma_index_iter iter;
|
||||
lzma_bool iter_finished;
|
||||
pthread_mutex_t iter_mutex;
|
||||
|
||||
/* Note that all threads are accessing these fds, so you have
|
||||
* to use pread/pwrite instead of lseek!
|
||||
*/
|
||||
|
||||
/* Input file. */
|
||||
const char *filename;
|
||||
int fd;
|
||||
|
||||
/* Output file. */
|
||||
const char *outputfile;
|
||||
int ofd;
|
||||
};
|
||||
|
||||
struct per_thread_state {
|
||||
unsigned thread_num;
|
||||
struct global_state *global;
|
||||
int status;
|
||||
};
|
||||
|
||||
/* Create threads to iterate over the blocks and uncompress. */
|
||||
static void *worker_thread (void *vp);
|
||||
|
||||
static void
|
||||
iter_blocks (lzma_index *idx, unsigned nr_threads,
|
||||
value filenamev, int fd, value outputfilev, int ofd)
|
||||
{
|
||||
struct global_state global;
|
||||
CLEANUP_FREE struct per_thread_state *per_thread = NULL;
|
||||
CLEANUP_FREE pthread_t *thread = NULL;
|
||||
unsigned u, nr_errors;
|
||||
int err;
|
||||
void *status;
|
||||
|
||||
per_thread = malloc (sizeof (struct per_thread_state) * nr_threads);
|
||||
thread = malloc (sizeof (pthread_t) * nr_threads);
|
||||
if (per_thread == NULL || thread == NULL)
|
||||
caml_raise_out_of_memory ();
|
||||
|
||||
lzma_index_iter_init (&global.iter, idx);
|
||||
global.iter_finished = 0;
|
||||
err = pthread_mutex_init (&global.iter_mutex, NULL);
|
||||
if (err != 0)
|
||||
unix_error (err, (char *) "pthread_mutex_init", Nothing);
|
||||
|
||||
global.filename = String_val (filenamev);
|
||||
global.fd = fd;
|
||||
global.outputfile = String_val (outputfilev);
|
||||
global.ofd = ofd;
|
||||
|
||||
for (u = 0; u < nr_threads; ++u) {
|
||||
per_thread[u].thread_num = u;
|
||||
per_thread[u].global = &global;
|
||||
}
|
||||
|
||||
/* Start the threads. */
|
||||
for (u = 0; u < nr_threads; ++u) {
|
||||
err = pthread_create (&thread[u], NULL, worker_thread, &per_thread[u]);
|
||||
if (err != 0)
|
||||
unix_error (err, (char *) "pthread_create", Nothing);
|
||||
}
|
||||
|
||||
/* Wait for the threads to exit. */
|
||||
nr_errors = 0;
|
||||
for (u = 0; u < nr_threads; ++u) {
|
||||
err = pthread_join (thread[u], &status);
|
||||
if (err != 0) {
|
||||
fprintf (stderr, "pthread_join (%u): %s\n", u, strerror (err));
|
||||
nr_errors++;
|
||||
}
|
||||
if (*(int *)status == -1)
|
||||
nr_errors++;
|
||||
}
|
||||
|
||||
if (nr_errors > 0)
|
||||
caml_invalid_argument ("some threads failed, see earlier errors");
|
||||
}
|
||||
|
||||
static int
|
||||
xpwrite (int fd, const void *bufvp, size_t count, off_t offset)
|
||||
{
|
||||
const char *buf = bufvp;
|
||||
ssize_t r;
|
||||
|
||||
while (count > 0) {
|
||||
r = pwrite (fd, buf, count, offset);
|
||||
if (r == -1)
|
||||
return -1;
|
||||
count -= r;
|
||||
offset += r;
|
||||
buf += r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Iterate over the blocks and uncompress. */
|
||||
static void *
|
||||
worker_thread (void *vp)
|
||||
{
|
||||
struct per_thread_state *state = vp;
|
||||
struct global_state *global = state->global;
|
||||
lzma_index_iter iter;
|
||||
int err;
|
||||
off_t position, oposition;
|
||||
CLEANUP_FREE uint8_t *header = NULL;
|
||||
ssize_t n;
|
||||
lzma_block block;
|
||||
CLEANUP_FREE lzma_filter *filters = NULL;
|
||||
lzma_ret r;
|
||||
lzma_stream strm = LZMA_STREAM_INIT;
|
||||
CLEANUP_FREE uint8_t *buf = NULL;
|
||||
CLEANUP_FREE uint8_t *outbuf = NULL;
|
||||
size_t i;
|
||||
lzma_bool iter_finished;
|
||||
|
||||
state->status = -1;
|
||||
|
||||
header = malloc (sizeof (uint8_t) * LZMA_BLOCK_HEADER_SIZE_MAX);
|
||||
filters = malloc (sizeof (lzma_filter) * (LZMA_FILTERS_MAX + 1));
|
||||
buf = malloc (sizeof (uint8_t) * BUFFER_SIZE);
|
||||
outbuf = malloc (sizeof (uint8_t) * BUFFER_SIZE);
|
||||
|
||||
if (header == NULL || filters == NULL || buf == NULL || outbuf == NULL) {
|
||||
perror ("malloc");
|
||||
return &state->status;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* Get the next block. */
|
||||
err = pthread_mutex_lock (&global->iter_mutex);
|
||||
if (err != 0) abort ();
|
||||
iter_finished = global->iter_finished;
|
||||
if (!iter_finished) {
|
||||
iter_finished = global->iter_finished =
|
||||
lzma_index_iter_next (&global->iter, LZMA_INDEX_ITER_NONEMPTY_BLOCK);
|
||||
if (!iter_finished)
|
||||
/* Take a local copy of this iterator since another thread will
|
||||
* update the global version.
|
||||
*/
|
||||
iter = global->iter;
|
||||
}
|
||||
err = pthread_mutex_unlock (&global->iter_mutex);
|
||||
if (err != 0) abort ();
|
||||
if (iter_finished)
|
||||
break;
|
||||
|
||||
/* Read the block header. Start by reading a single byte which
|
||||
* tell us how big the block header is.
|
||||
*/
|
||||
position = iter.block.compressed_file_offset;
|
||||
n = pread (global->fd, header, 1, position);
|
||||
if (n == 0) {
|
||||
fprintf (stderr,
|
||||
"%s: read: unexpected end of file reading block header byte\n",
|
||||
global->filename);
|
||||
return &state->status;
|
||||
}
|
||||
if (n == -1) {
|
||||
perror (String_val (global->filename));
|
||||
return &state->status;
|
||||
}
|
||||
position++;
|
||||
|
||||
if (header[0] == '\0') {
|
||||
fprintf (stderr,
|
||||
"%s: read: unexpected invalid block in file, header[0] = 0\n",
|
||||
global->filename);
|
||||
return &state->status;
|
||||
}
|
||||
|
||||
block.version = 0;
|
||||
block.check = iter.stream.flags->check;
|
||||
block.filters = filters;
|
||||
block.header_size = lzma_block_header_size_decode (header[0]);
|
||||
|
||||
/* Now read and decode the block header. */
|
||||
n = pread (global->fd, &header[1], block.header_size-1, position);
|
||||
if (n >= 0 && n != (ssize_t) block.header_size-1) {
|
||||
fprintf (stderr,
|
||||
"%s: read: unexpected end of file reading block header\n",
|
||||
global->filename);
|
||||
return &state->status;
|
||||
}
|
||||
if (n == -1) {
|
||||
perror (global->filename);
|
||||
return &state->status;
|
||||
}
|
||||
position += n;
|
||||
|
||||
r = lzma_block_header_decode (&block, NULL, header);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "%s: invalid block header (error %u)\n",
|
||||
global->filename, r);
|
||||
return &state->status;
|
||||
}
|
||||
|
||||
/* What this actually does is it checks that the block header
|
||||
* matches the index.
|
||||
*/
|
||||
r = lzma_block_compressed_size (&block, iter.block.unpadded_size);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr,
|
||||
"%s: cannot calculate compressed size (error %u)\n",
|
||||
global->filename, r);
|
||||
return &state->status;
|
||||
}
|
||||
|
||||
/* Where we will start writing to. */
|
||||
oposition = iter.block.uncompressed_file_offset;
|
||||
|
||||
/* Read the block data and uncompress it. */
|
||||
r = lzma_block_decoder (&strm, &block);
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr, "%s: invalid block (error %u)\n", global->filename, r);
|
||||
return &state->status;
|
||||
}
|
||||
|
||||
strm.next_in = NULL;
|
||||
strm.avail_in = 0;
|
||||
strm.next_out = outbuf;
|
||||
strm.avail_out = BUFFER_SIZE;
|
||||
|
||||
for (;;) {
|
||||
lzma_action action = LZMA_RUN;
|
||||
|
||||
if (strm.avail_in == 0) {
|
||||
strm.next_in = buf;
|
||||
n = pread (global->fd, buf, BUFFER_SIZE, position);
|
||||
if (n == -1) {
|
||||
perror (global->filename);
|
||||
return &state->status;
|
||||
}
|
||||
position += n;
|
||||
strm.avail_in = n;
|
||||
if (n == 0)
|
||||
action = LZMA_FINISH;
|
||||
}
|
||||
|
||||
r = lzma_code (&strm, action);
|
||||
|
||||
if (strm.avail_out == 0 || r == LZMA_STREAM_END) {
|
||||
size_t wsz = BUFFER_SIZE - strm.avail_out;
|
||||
|
||||
/* Don't write if the block is all zero, to preserve output file
|
||||
* sparseness. However we have to update oposition.
|
||||
*/
|
||||
if (!is_zero ((char *) outbuf, wsz)) {
|
||||
if (xpwrite (global->ofd, outbuf, wsz, oposition) == -1) {
|
||||
perror (global->outputfile);
|
||||
return &state->status;
|
||||
}
|
||||
}
|
||||
oposition += wsz;
|
||||
|
||||
strm.next_out = outbuf;
|
||||
strm.avail_out = BUFFER_SIZE;
|
||||
}
|
||||
|
||||
if (r == LZMA_STREAM_END)
|
||||
break;
|
||||
if (r != LZMA_OK) {
|
||||
fprintf (stderr,
|
||||
"%s: could not parse block data (error %u)\n",
|
||||
global->filename, r);
|
||||
return &state->status;
|
||||
}
|
||||
}
|
||||
|
||||
lzma_end (&strm);
|
||||
|
||||
for (i = 0; filters[i].id != LZMA_VLI_UNKNOWN; ++i)
|
||||
free (filters[i].options);
|
||||
}
|
||||
|
||||
state->status = 0;
|
||||
return &state->status;
|
||||
}
|
||||
|
||||
#endif /* PARALLEL_XZCAT */
|
||||
@@ -1,20 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
external pxzcat : string -> string -> unit = "virt_builder_pxzcat"
|
||||
external using_parallel_xzcat : unit -> bool = "virt_builder_using_parallel_xzcat" "noalloc"
|
||||
@@ -1,34 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
(** {1 Parallel xzcat (or fall back to regular xzcat).}
|
||||
|
||||
Eventually regular xzcat will be able to work in parallel and this
|
||||
code can go away.
|
||||
*)
|
||||
|
||||
val pxzcat : string -> string -> unit
|
||||
(** [pxzcat input output] uncompresses the file [input] to the file
|
||||
[output]. The input and output must both be seekable.
|
||||
|
||||
If liblzma was found at compile time, this uses an internal
|
||||
implementation of parallel xzcat. Otherwise regular xzcat is
|
||||
used. *)
|
||||
|
||||
val using_parallel_xzcat : unit -> bool
|
||||
(** Returns [true] iff the implementation uses parallel xzcat. *)
|
||||
@@ -1,583 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2016-2020 SUSE Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Common_gettext.Gettext
|
||||
open Tools_utils
|
||||
open Unix_utils
|
||||
open Getopt.OptionName
|
||||
open Utils
|
||||
open JSON_parser
|
||||
open Xpath_helpers
|
||||
|
||||
open Printf
|
||||
|
||||
type cmdline = {
|
||||
gpg : string;
|
||||
gpgkey : string option;
|
||||
interactive : bool;
|
||||
compression : bool;
|
||||
repo : string;
|
||||
}
|
||||
|
||||
type disk_image_info = {
|
||||
format : string;
|
||||
size : int64;
|
||||
}
|
||||
|
||||
let parse_cmdline () =
|
||||
let gpg = ref "gpg" in
|
||||
let gpgkey = ref None in
|
||||
let set_gpgkey arg = gpgkey := Some arg in
|
||||
|
||||
let interactive = ref false in
|
||||
let compression = ref true in
|
||||
|
||||
let argspec = [
|
||||
[ L"gpg" ], Getopt.Set_string ("gpg", gpg), s_"Set GPG binary/command";
|
||||
[ S 'K'; L"gpg-key" ], Getopt.String ("gpgkey", set_gpgkey),
|
||||
s_"ID of the GPG key to sign the repo with";
|
||||
[ S 'i'; L"interactive" ], Getopt.Set interactive, s_"Ask the user about missing data";
|
||||
[ L"no-compression" ], Getopt.Clear compression, s_"Don’t compress the new images in the index";
|
||||
] in
|
||||
|
||||
let args = ref [] in
|
||||
let anon_fun s = List.push_front s args in
|
||||
let usage_msg =
|
||||
sprintf (f_"\
|
||||
%s: create a repository for virt-builder
|
||||
|
||||
virt-builder-repository REPOSITORY_PATH
|
||||
|
||||
A short summary of the options is given below. For detailed help please
|
||||
read the man page virt-builder-repository(1).
|
||||
")
|
||||
prog in
|
||||
let opthandle = create_standard_options argspec ~anon_fun ~machine_readable:true usage_msg in
|
||||
Getopt.parse opthandle.getopt;
|
||||
|
||||
(* Machine-readable mode? Print out some facts about what
|
||||
* this binary supports.
|
||||
*)
|
||||
(match machine_readable () with
|
||||
| Some { pr } ->
|
||||
pr "virt-builder-repository\n";
|
||||
exit 0
|
||||
| None -> ()
|
||||
);
|
||||
|
||||
(* Dereference options. *)
|
||||
let args = List.rev !args in
|
||||
let gpg = !gpg in
|
||||
let gpgkey = !gpgkey in
|
||||
let interactive = !interactive in
|
||||
let compression = !compression in
|
||||
|
||||
(* Check options *)
|
||||
let repo =
|
||||
match args with
|
||||
| [repo] -> repo
|
||||
| [] ->
|
||||
error (f_"virt-builder-repository /path/to/repo
|
||||
|
||||
Use ‘/path/to/repo’ to point to the repository folder.")
|
||||
| _ ->
|
||||
error (f_"too many parameters, only one path to repository is allowed") in
|
||||
|
||||
{
|
||||
gpg = gpg;
|
||||
gpgkey = gpgkey;
|
||||
interactive = interactive;
|
||||
compression = compression;
|
||||
repo = repo;
|
||||
}
|
||||
|
||||
let do_mv src dest =
|
||||
let cmd = [ "mv"; src; dest ] in
|
||||
let r = run_command cmd in
|
||||
if r <> 0 then
|
||||
error (f_"moving file ‘%s’ to ‘%s’ failed") src dest
|
||||
|
||||
let checksums_get_sha512 = function
|
||||
| None -> None
|
||||
| Some csums ->
|
||||
let rec loop = function
|
||||
| [] -> None
|
||||
| Checksums.SHA512 csum :: _ -> Some (Checksums.SHA512 csum)
|
||||
| _ :: rest -> loop rest
|
||||
in
|
||||
loop csums
|
||||
|
||||
let osinfo_ids = ref None
|
||||
|
||||
let rec osinfo_get_short_ids () =
|
||||
match !osinfo_ids with
|
||||
| Some ids -> ids
|
||||
| None ->
|
||||
osinfo_ids :=
|
||||
Some (
|
||||
Osinfo.fold (
|
||||
fun set filepath ->
|
||||
let doc = Xml.parse_file filepath in
|
||||
let xpathctx = Xml.xpath_new_context doc in
|
||||
let nodes = xpath_get_nodes xpathctx "/libosinfo/os/short-id" in
|
||||
List.fold_left (
|
||||
fun set node ->
|
||||
let id = Xml.node_as_string node in
|
||||
StringSet.add id set
|
||||
) set nodes
|
||||
) StringSet.empty
|
||||
);
|
||||
osinfo_get_short_ids ()
|
||||
|
||||
let compress_to file outdir =
|
||||
let outimg = outdir // Filename.basename file ^ ".xz" in
|
||||
|
||||
info "Compressing ...";
|
||||
let cmd = [ "xz"; "-f"; "--best"; "--block-size=16777216"; "-c"; file ] in
|
||||
let file_flags = [ Unix.O_WRONLY; Unix.O_CREAT; Unix.O_TRUNC; ] in
|
||||
let outfd = Unix.openfile outimg file_flags 0o666 in
|
||||
let res = run_command cmd ~stdout_fd:outfd in
|
||||
if res <> 0 then
|
||||
error (f_"‘xz’ command failed");
|
||||
outimg
|
||||
|
||||
let get_mime_type filepath =
|
||||
let file_cmd = "file --mime-type --brief " ^ (quote filepath) in
|
||||
match external_command file_cmd with
|
||||
| [] -> None
|
||||
| line :: _ -> Some line
|
||||
|
||||
let get_disk_image_info filepath =
|
||||
let infos = get_image_infos filepath in
|
||||
{
|
||||
format = object_get_string "format" infos;
|
||||
size = object_get_number "virtual-size" infos
|
||||
}
|
||||
|
||||
let cmp a b =
|
||||
Index.string_of_arch a = Index.string_of_arch b
|
||||
|
||||
let has_entry id arch index =
|
||||
List.exists (
|
||||
fun (item_id, { Index.arch = item_arch }) ->
|
||||
item_id = id && cmp item_arch arch
|
||||
) index
|
||||
|
||||
let process_image acc_entries filename repo tmprepo index interactive
|
||||
compression sigchecker =
|
||||
message (f_"Preparing %s") filename;
|
||||
|
||||
let filepath = repo // filename in
|
||||
let { format; size } = get_disk_image_info filepath in
|
||||
let out_path =
|
||||
if not compression then filepath
|
||||
else compress_to filepath tmprepo in
|
||||
let out_filename = Filename.basename out_path in
|
||||
let checksum = Checksums.compute_checksum "sha512" out_path in
|
||||
let compressed_size = (Unix.LargeFile.stat out_path).Unix.LargeFile.st_size in
|
||||
|
||||
let ask ~default ?values message =
|
||||
printf "%s [%s] " message default;
|
||||
(match values with
|
||||
| None -> ()
|
||||
| Some x ->
|
||||
printf (f_"Choose one from the list below:\n %s\n")
|
||||
(String.concat "\n " x));
|
||||
let value = read_line () in
|
||||
|
||||
if value = "" then
|
||||
default
|
||||
else
|
||||
value
|
||||
in
|
||||
|
||||
let re_valid_id = PCRE.compile ~anchored:true "[-a-zA-Z0-9_.]+" in
|
||||
let rec ask_id default =
|
||||
let id = ask (s_"Identifier: ") ~default in
|
||||
if not (PCRE.matches re_valid_id id) then (
|
||||
warning (f_"Allowed characters are letters, digits, - _ and .");
|
||||
ask_id default
|
||||
) else
|
||||
id in
|
||||
|
||||
let ask_arch guess =
|
||||
let arches = [ "x86_64"; "aarch64"; "armv7l"; "i686"; "ppc64"; "ppc64le"; "s390x" ] in
|
||||
Index.Arch (ask (s_"Architecture: ") ~default:guess ~values:arches)
|
||||
in
|
||||
|
||||
let ask_osinfo default =
|
||||
let osinfo = ask (s_ "osinfo short ID: ") ~default in
|
||||
let osinfo_ids = osinfo_get_short_ids () in
|
||||
if not (StringSet.mem osinfo osinfo_ids) then
|
||||
warning (f_"‘%s’ is not a recognized osinfo OS id; using it anyway") osinfo;
|
||||
osinfo in
|
||||
|
||||
let extract_entry_data ?entry () =
|
||||
message (f_"Extracting data from the image...");
|
||||
let g = Tools_utils.open_guestfs () in
|
||||
g#add_drive_ro filepath;
|
||||
g#launch ();
|
||||
|
||||
let roots = g#inspect_os () in
|
||||
let nroots = Array.length roots in
|
||||
if nroots <> 1 then
|
||||
error (f_"virt-builder template images must have one and only one root file system, found %d")
|
||||
nroots;
|
||||
|
||||
let root = Array.get roots 0 in
|
||||
let inspected_arch = g#inspect_get_arch root in
|
||||
let product = g#inspect_get_product_name root in
|
||||
let shortid = g#inspect_get_osinfo root in
|
||||
let lvs = g#lvs () in
|
||||
let filesystems = g#inspect_get_filesystems root in
|
||||
|
||||
g#close ();
|
||||
|
||||
let id =
|
||||
match entry with
|
||||
| Some (id, _) -> id
|
||||
| None -> (
|
||||
if interactive then ask_id shortid
|
||||
else error (f_"missing image identifier")
|
||||
) in
|
||||
|
||||
let arch =
|
||||
match entry with
|
||||
| Some (_, { Index.arch }) -> (
|
||||
match arch with
|
||||
| Index.Arch arch -> Index.Arch arch
|
||||
| Index.GuessedArch arch ->
|
||||
if interactive then ask_arch arch
|
||||
else Index.Arch arch )
|
||||
| None ->
|
||||
if interactive then ask_arch inspected_arch
|
||||
else Index.Arch inspected_arch in
|
||||
|
||||
if has_entry id arch acc_entries then (
|
||||
let arch =
|
||||
match arch with
|
||||
| Index.Arch arch
|
||||
| Index.GuessedArch arch -> arch in
|
||||
error (f_"Already existing image with id %s and architecture %s") id arch
|
||||
);
|
||||
|
||||
let printable_name =
|
||||
match entry with
|
||||
| Some (_, { Index.printable_name }) ->
|
||||
if printable_name = None then
|
||||
if interactive then Some (ask (s_"Display name: ") ~default:product)
|
||||
else Some product
|
||||
else
|
||||
printable_name
|
||||
| None -> Some product in
|
||||
|
||||
let osinfo =
|
||||
match entry with
|
||||
| Some (_, { Index.osinfo }) ->
|
||||
if osinfo = None then
|
||||
Some (if interactive then ask_osinfo shortid else shortid)
|
||||
else
|
||||
osinfo
|
||||
| None ->
|
||||
Some (if interactive then ask_osinfo shortid else shortid) in
|
||||
|
||||
let expand =
|
||||
match entry with
|
||||
| Some (_, { Index.expand }) ->
|
||||
if expand = None then
|
||||
if interactive then
|
||||
Some (ask (s_"Expandable partition: ") ~default:root
|
||||
~values:(Array.to_list filesystems))
|
||||
else Some root
|
||||
else
|
||||
expand
|
||||
| None ->
|
||||
if interactive then
|
||||
Some (ask (s_"Expandable partition: ") ~default:root
|
||||
~values:(Array.to_list filesystems))
|
||||
else Some root in
|
||||
|
||||
let lvexpand =
|
||||
if lvs = [||] then
|
||||
None
|
||||
else
|
||||
match entry with
|
||||
| Some (_, { Index.lvexpand }) ->
|
||||
if lvexpand = None then
|
||||
if interactive then
|
||||
Some (ask (s_"Expandable volume: ") ~values:(Array.to_list lvs)
|
||||
~default:(Array.get lvs 0))
|
||||
else Some (Array.get lvs 0)
|
||||
else
|
||||
lvexpand
|
||||
| None ->
|
||||
if interactive then
|
||||
Some (ask (s_"Expandable volume: ") ~values:(Array.to_list lvs)
|
||||
~default:(Array.get lvs 0))
|
||||
else Some (Array.get lvs 0) in
|
||||
|
||||
let revision =
|
||||
match entry with
|
||||
| Some (_, { Index.revision }) ->
|
||||
Utils.increment_revision revision
|
||||
| None -> Rev_int 1 in
|
||||
|
||||
let notes =
|
||||
match entry with
|
||||
| Some (_, { Index.notes }) -> notes
|
||||
| None -> [] in
|
||||
|
||||
let hidden =
|
||||
match entry with
|
||||
| Some (_, { Index.hidden }) -> hidden
|
||||
| None -> false in
|
||||
|
||||
let aliases =
|
||||
match entry with
|
||||
| Some (_, { Index.aliases }) -> aliases
|
||||
| None -> None in
|
||||
|
||||
(id, { Index.printable_name;
|
||||
osinfo;
|
||||
file_uri = Filename.basename out_path;
|
||||
arch;
|
||||
signature_uri = None;
|
||||
checksums = Some [checksum];
|
||||
revision;
|
||||
format = Some format;
|
||||
size;
|
||||
compressed_size = Some compressed_size;
|
||||
expand;
|
||||
lvexpand;
|
||||
notes;
|
||||
hidden;
|
||||
aliases;
|
||||
sigchecker;
|
||||
proxy = Curl.SystemProxy })
|
||||
in
|
||||
|
||||
(* Do we have an entry for that file already? *)
|
||||
let file_entry =
|
||||
try
|
||||
List.hd (
|
||||
List.filter (
|
||||
fun (_, { Index.file_uri }) ->
|
||||
let basename = Filename.basename file_uri in
|
||||
basename = out_filename || basename = filename
|
||||
) index
|
||||
)
|
||||
with
|
||||
| Failure _ -> extract_entry_data () in
|
||||
|
||||
let _, { Index.checksums } = file_entry in
|
||||
let old_checksum = checksums_get_sha512 checksums in
|
||||
|
||||
match old_checksum with
|
||||
| Some old_sum ->
|
||||
if old_sum = checksum then
|
||||
let id, entry = file_entry in
|
||||
(id, { entry with Index.file_uri = out_filename })
|
||||
else
|
||||
extract_entry_data ~entry:file_entry ()
|
||||
| None ->
|
||||
extract_entry_data ~entry:file_entry ()
|
||||
|
||||
let unsafe_remove_directory_prefix parent path =
|
||||
if path = parent then
|
||||
""
|
||||
else if String.is_prefix path (parent // "") then (
|
||||
let len = String.length parent in
|
||||
String.sub path (len+1) (String.length path - len-1)
|
||||
) else
|
||||
invalid_arg (sprintf "%S is not a path prefix of %S" parent path)
|
||||
|
||||
let main () =
|
||||
let cmdline = parse_cmdline () in
|
||||
|
||||
(* If debugging, echo the command line arguments. *)
|
||||
debug "command line: %s" (String.concat " " (Array.to_list Sys.argv));
|
||||
|
||||
(* Check that the paths are existing *)
|
||||
if not (Sys.file_exists cmdline.repo) then
|
||||
error (f_"repository folder ‘%s’ doesn’t exist") cmdline.repo;
|
||||
|
||||
(* Create a temporary folder to work in *)
|
||||
let tmpdir = Mkdtemp.temp_dir ~base_dir:cmdline.repo
|
||||
"virt-builder-repository." in
|
||||
rmdir_on_exit tmpdir;
|
||||
|
||||
let tmprepo = tmpdir // "repo" in
|
||||
mkdir_p tmprepo 0o700;
|
||||
|
||||
let sigchecker = Sigchecker.create ~gpg:cmdline.gpg
|
||||
~check_signature:false
|
||||
~gpgkey:No_Key
|
||||
~tmpdir in
|
||||
|
||||
let index =
|
||||
try
|
||||
let index_filename =
|
||||
List.find (
|
||||
fun filename -> Sys.file_exists (cmdline.repo // filename)
|
||||
) [ "index.asc"; "index" ] in
|
||||
|
||||
let downloader = Downloader.create ~curl:"do-not-use-curl"
|
||||
~cache:None ~tmpdir in
|
||||
|
||||
let source = { Sources.name = index_filename;
|
||||
uri = cmdline.repo // index_filename;
|
||||
gpgkey = No_Key;
|
||||
proxy = Curl.SystemProxy;
|
||||
format = Sources.FormatNative } in
|
||||
|
||||
Index_parser.get_index ~downloader ~sigchecker ~template:true source
|
||||
with Not_found -> [] in
|
||||
|
||||
(* Check for index/interactive consistency *)
|
||||
if not cmdline.interactive && index = [] then
|
||||
error (f_"the repository must contain an index file when running in automated mode");
|
||||
|
||||
debug "Searching for images ...";
|
||||
|
||||
let images =
|
||||
let is_supported_format file =
|
||||
let extension = last_part_of file '.' in
|
||||
match extension with
|
||||
| Some ext -> List.mem ext [ "qcow2"; "raw"; "img" ]
|
||||
| None ->
|
||||
match get_mime_type file with
|
||||
| None -> false
|
||||
| Some mime -> mime = "application/octet-stream" in
|
||||
let is_new file =
|
||||
try
|
||||
let _, { Index.checksums } =
|
||||
List.find (
|
||||
fun (_, { Index.file_uri }) ->
|
||||
Filename.basename file_uri = file
|
||||
) index in
|
||||
let checksum = checksums_get_sha512 checksums in
|
||||
let path = cmdline.repo // file in
|
||||
let file_checksum = Checksums.compute_checksum "sha512" path in
|
||||
match checksum with
|
||||
| None -> true
|
||||
| Some sum -> sum <> file_checksum
|
||||
with Not_found -> true in
|
||||
let files = Array.to_list (Sys.readdir cmdline.repo) in
|
||||
let files = List.filter (
|
||||
fun file -> is_regular_file (cmdline.repo // file)
|
||||
) files in
|
||||
List.filter (
|
||||
fun file -> is_supported_format (cmdline.repo // file) && is_new file
|
||||
) files in
|
||||
|
||||
if images = [] then (
|
||||
info (f_ "No new image found");
|
||||
exit 0
|
||||
);
|
||||
|
||||
info (f_ "Found new images: %s") (String.concat " " images);
|
||||
|
||||
with_open_out (tmprepo // "index") (
|
||||
fun index_channel ->
|
||||
(* Generate entries for uncompressed images *)
|
||||
let images_entries = List.fold_right (
|
||||
fun filename acc ->
|
||||
let image_entry = process_image acc
|
||||
filename
|
||||
cmdline.repo
|
||||
tmprepo
|
||||
index
|
||||
cmdline.interactive
|
||||
cmdline.compression
|
||||
sigchecker in
|
||||
image_entry :: acc
|
||||
) images [] in
|
||||
|
||||
(* Filter out entries for newly found images and entries
|
||||
without a corresponding image file or with empty arch *)
|
||||
let index = List.filter (
|
||||
fun (id, { Index.arch; file_uri }) ->
|
||||
not (has_entry id arch images_entries) && Sys.file_exists file_uri
|
||||
) index in
|
||||
|
||||
(* Convert all URIs back to relative ones *)
|
||||
let index = List.map (
|
||||
fun (id, entry) ->
|
||||
let { Index.file_uri } = entry in
|
||||
let rel_path =
|
||||
try (* XXX wrong *)
|
||||
unsafe_remove_directory_prefix cmdline.repo file_uri
|
||||
with
|
||||
| Invalid_argument _ ->
|
||||
file_uri in
|
||||
let rel_entry = { entry with Index.file_uri = rel_path } in
|
||||
(id, rel_entry)
|
||||
) index in
|
||||
|
||||
(* Write all the entries *)
|
||||
List.iter (
|
||||
fun entry ->
|
||||
Index_parser.write_entry index_channel entry;
|
||||
) (index @ images_entries);
|
||||
);
|
||||
|
||||
(* GPG sign the generated index *)
|
||||
(match cmdline.gpgkey with
|
||||
| None ->
|
||||
debug "Skip index signing"
|
||||
| Some gpgkey ->
|
||||
message (f_"Signing index with the GPG key %s") gpgkey;
|
||||
let cmd = sprintf "%s --armor --output %s --export %s"
|
||||
(quote (cmdline.gpg // "index.gpg"))
|
||||
(quote tmprepo) (quote gpgkey) in
|
||||
if shell_command cmd <> 0 then
|
||||
error (f_"failed to export the GPG key %s") gpgkey;
|
||||
|
||||
let cmd = sprintf "%s --armor --default-key %s --clearsign %s"
|
||||
(quote cmdline.gpg) (quote gpgkey)
|
||||
(quote (tmprepo // "index" )) in
|
||||
if shell_command cmd <> 0 then
|
||||
error (f_"failed to sign index");
|
||||
);
|
||||
|
||||
message (f_"Creating index backup copy");
|
||||
|
||||
List.iter (
|
||||
fun filename ->
|
||||
let filepath = cmdline.repo // filename in
|
||||
if Sys.file_exists filepath then
|
||||
do_mv filepath (filepath ^ ".bak")
|
||||
) ["index"; "index.asc"];
|
||||
|
||||
message (f_"Moving files to final destination");
|
||||
|
||||
Array.iter (
|
||||
fun filename ->
|
||||
do_mv (tmprepo // filename) cmdline.repo
|
||||
) (Sys.readdir tmprepo);
|
||||
|
||||
debug "Cleanup";
|
||||
|
||||
(* Remove the processed image files *)
|
||||
if cmdline.compression then
|
||||
List.iter (
|
||||
fun filename -> Sys.remove (cmdline.repo // filename)
|
||||
) images
|
||||
|
||||
let () = run_main_and_handle_errors main
|
||||
@@ -1,19 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2016-2020 SUSE Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
(* empty *)
|
||||
@@ -1,64 +0,0 @@
|
||||
/* virt-builder
|
||||
* Copyright (C) 2014 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include <caml/alloc.h>
|
||||
#include <caml/fail.h>
|
||||
#include <caml/memory.h>
|
||||
#include <caml/mlvalues.h>
|
||||
|
||||
static const int lc_string_table[7] = {
|
||||
LC_ALL,
|
||||
LC_CTYPE,
|
||||
LC_NUMERIC,
|
||||
LC_TIME,
|
||||
LC_COLLATE,
|
||||
LC_MONETARY,
|
||||
LC_MESSAGES
|
||||
};
|
||||
|
||||
#ifndef Val_none
|
||||
#define Val_none (Val_int (0))
|
||||
#endif
|
||||
|
||||
extern value virt_builder_setlocale (value val_category, value val_name);
|
||||
|
||||
value
|
||||
virt_builder_setlocale (value val_category, value val_name)
|
||||
{
|
||||
CAMLparam2 (val_category, val_name);
|
||||
CAMLlocal2 (rv, rv2);
|
||||
const char *locstring;
|
||||
char *ret;
|
||||
int category;
|
||||
|
||||
category = lc_string_table[Int_val (val_category)];
|
||||
locstring = val_name == Val_none ? NULL : String_val (Field (val_name, 0));
|
||||
ret = setlocale (category, locstring);
|
||||
if (ret) {
|
||||
rv2 = caml_copy_string (ret);
|
||||
rv = caml_alloc (1, 0);
|
||||
Store_field (rv, 0, rv2);
|
||||
} else
|
||||
rv = Val_none;
|
||||
|
||||
CAMLreturn (rv);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2014 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
type localecategory =
|
||||
| LC_ALL
|
||||
| LC_CTYPE
|
||||
| LC_NUMERIC
|
||||
| LC_TIME
|
||||
| LC_COLLATE
|
||||
| LC_MONETARY
|
||||
| LC_MESSAGES
|
||||
;;
|
||||
|
||||
external setlocale : localecategory -> string option -> string option = "virt_builder_setlocale"
|
||||
@@ -1,30 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2014 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
type localecategory =
|
||||
| LC_ALL
|
||||
| LC_CTYPE
|
||||
| LC_NUMERIC
|
||||
| LC_TIME
|
||||
| LC_COLLATE
|
||||
| LC_MONETARY
|
||||
| LC_MESSAGES
|
||||
;;
|
||||
|
||||
val setlocale : localecategory -> string option -> string option
|
||||
(** [setlocale category newlocale] Tiny wrapper to the C [setlocale]. *)
|
||||
@@ -1,221 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Unix_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open Utils
|
||||
|
||||
open Printf
|
||||
open Unix
|
||||
|
||||
type t = {
|
||||
gpg : string;
|
||||
fingerprint : string;
|
||||
subkeys_fingerprints : string list;
|
||||
check_signature : bool;
|
||||
gpghome : string;
|
||||
tmpdir : string;
|
||||
}
|
||||
|
||||
(* Import the specified key file. *)
|
||||
let import_keyfile ~gpg ~gpghome ~tmpdir ?(trust = true) keyfile =
|
||||
let status_file = Filename.temp_file ~temp_dir:tmpdir "vbstat" ".txt" in
|
||||
let cmd = sprintf "%s --homedir %s --status-file %s --import %s%s"
|
||||
gpg gpghome (quote status_file) (quote keyfile)
|
||||
(if verbose () then "" else " >/dev/null 2>&1") in
|
||||
let r = shell_command cmd in
|
||||
if r <> 0 then
|
||||
error (f_"could not import public key\nUse the ‘-v’ option and look for earlier error messages.");
|
||||
let status = read_whole_file status_file in
|
||||
let status = String.nsplit "\n" status in
|
||||
let key_id = ref "" in
|
||||
let fingerprint = ref "" in
|
||||
List.iter (
|
||||
fun line ->
|
||||
let line = String.nsplit " " line in
|
||||
match line with
|
||||
| "[GNUPG:]" :: "IMPORT_OK" :: _ :: fp :: _ -> fingerprint := fp
|
||||
| "[GNUPG:]" :: "IMPORTED" :: key :: _ -> key_id := key
|
||||
| _ -> ()
|
||||
) status;
|
||||
if trust then (
|
||||
let cmd = sprintf "%s --homedir %s --trusted-key %s --list-keys%s"
|
||||
gpg gpghome (quote !key_id)
|
||||
(if verbose () then "" else " >/dev/null 2>&1") in
|
||||
let r = shell_command cmd in
|
||||
if r <> 0 then
|
||||
error (f_"GPG failure: could not trust the imported key\nUse the ‘-v’ option and look for earlier error messages.");
|
||||
);
|
||||
let subkeys =
|
||||
(* --with-fingerprint is specified twice so gpg outputs the full
|
||||
* fingerprint of the subkeys. *)
|
||||
let cmd = sprintf "%s --homedir %s --with-colons --with-fingerprint --with-fingerprint --list-keys %s"
|
||||
gpg gpghome !fingerprint in
|
||||
let lines = external_command cmd in
|
||||
let current = ref None in
|
||||
let subkeys = ref [] in
|
||||
List.iter (
|
||||
fun line ->
|
||||
let line = String.nsplit ":" line in
|
||||
match line with
|
||||
| "sub" :: ("u"|"-") :: _ :: _ :: id :: _ ->
|
||||
current := Some id
|
||||
| "fpr" :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: _ :: id :: _ ->
|
||||
(match !current with
|
||||
| None -> ()
|
||||
| Some k ->
|
||||
if String.is_suffix id k then List.push_front id subkeys;
|
||||
current := None
|
||||
)
|
||||
| _ -> ()
|
||||
) lines;
|
||||
!subkeys in
|
||||
!fingerprint, subkeys
|
||||
|
||||
let rec create ~gpg ~gpgkey ~check_signature ~tmpdir =
|
||||
(* Create a temporary directory for gnupg. *)
|
||||
let gpgtmpdir = Mkdtemp.temp_dir ~base_dir:tmpdir "vb.gpghome." in
|
||||
(* Make sure we have no check_signature=true with no actual key. *)
|
||||
let check_signature, gpgkey =
|
||||
match check_signature, gpgkey with
|
||||
| true, No_Key -> false, No_Key
|
||||
| x, y -> x, y in
|
||||
let fingerprint, subkeys =
|
||||
if check_signature then (
|
||||
(* Run gpg so it can setup its own home directory, failing if it
|
||||
* cannot.
|
||||
*)
|
||||
let cmd = sprintf "%s --homedir %s --list-keys%s"
|
||||
gpg gpgtmpdir (if verbose () then "" else " >/dev/null 2>&1") in
|
||||
let r = shell_command cmd in
|
||||
if r <> 0 then
|
||||
error (f_"GPG failure: could not run GPG the first time\nUse the ‘-v’ option and look for earlier error messages.");
|
||||
match gpgkey with
|
||||
| No_Key ->
|
||||
assert false
|
||||
| KeyFile kf ->
|
||||
import_keyfile gpg gpgtmpdir tmpdir kf
|
||||
| Fingerprint fp ->
|
||||
let filename = Filename.temp_file ~temp_dir:tmpdir "vbpubkey" ".asc" in
|
||||
let cmd = sprintf "%s --yes --armor --output %s --export %s%s"
|
||||
gpg (quote filename) (quote fp)
|
||||
(if verbose () then "" else " >/dev/null 2>&1") in
|
||||
let r = shell_command cmd in
|
||||
if r <> 0 then
|
||||
error (f_"could not export public key\nUse the ‘-v’ option and look for earlier error messages.");
|
||||
import_keyfile gpg gpgtmpdir tmpdir filename
|
||||
) else
|
||||
"", [] in
|
||||
{
|
||||
gpg = gpg;
|
||||
fingerprint = fingerprint;
|
||||
subkeys_fingerprints = subkeys;
|
||||
check_signature = check_signature;
|
||||
gpghome = gpgtmpdir;
|
||||
tmpdir = tmpdir;
|
||||
}
|
||||
|
||||
(* Compare two strings of hex digits ignoring whitespace and case. *)
|
||||
and equal_fingerprints fp1 fp2 =
|
||||
let len1 = String.length fp1 and len2 = String.length fp2 in
|
||||
let rec loop i j =
|
||||
if i = len1 && j = len2 then true (* match! *)
|
||||
else if i = len1 || j = len2 then false (* no match - different lengths *)
|
||||
else (
|
||||
let x1 = getxdigit fp1.[i] and x2 = getxdigit fp2.[j] in
|
||||
match x1, x2 with
|
||||
| Some x1, Some x2 when x1 = x2 -> loop (i+1) (j+1)
|
||||
| Some x1, Some x2 -> false (* no match - different content *)
|
||||
| Some _, None -> loop i (j+1)
|
||||
| None, Some _ -> loop (i+1) j
|
||||
| None, None -> loop (i+1) (j+1)
|
||||
)
|
||||
in
|
||||
loop 0 0
|
||||
|
||||
and getxdigit = function
|
||||
| '0'..'9' as c -> Some (Char.code c - Char.code '0')
|
||||
| 'a'..'f' as c -> Some (Char.code c - Char.code 'a')
|
||||
| 'A'..'F' as c -> Some (Char.code c - Char.code 'A')
|
||||
| _ -> None
|
||||
|
||||
let verifying_signatures t =
|
||||
t.check_signature
|
||||
|
||||
let rec verify t filename =
|
||||
if t.check_signature then (
|
||||
let args = quote filename in
|
||||
do_verify t args
|
||||
)
|
||||
|
||||
and verify_detached t filename sigfile =
|
||||
if t.check_signature then (
|
||||
match sigfile with
|
||||
| None ->
|
||||
error (f_"there is no detached signature file\nThis probably means the index file is missing a sig=... line.\nYou can use --no-check-signature to ignore this error, but that means you are susceptible to man-in-the-middle attacks.")
|
||||
| Some sigfile ->
|
||||
let args = sprintf "%s %s" (quote sigfile) (quote filename) in
|
||||
do_verify t args
|
||||
)
|
||||
|
||||
and verify_and_remove_signature t filename =
|
||||
if t.check_signature then (
|
||||
(* Copy the input file as temporary file with the .asc extension,
|
||||
* so gpg recognises that format. *)
|
||||
let asc_file = Filename.temp_file ~temp_dir:t.tmpdir "vbfile" ".asc" in
|
||||
let cmd = [ "cp"; filename; asc_file ] in
|
||||
if run_command cmd <> 0 then exit 1;
|
||||
let out_file = Filename.temp_file ~temp_dir:t.tmpdir "vbfile" "" in
|
||||
let args = sprintf "--yes --output %s %s" (quote out_file) (quote filename) in
|
||||
do_verify ~verify_only:false t args;
|
||||
Some out_file
|
||||
) else
|
||||
None
|
||||
|
||||
and do_verify ?(verify_only = true) t args =
|
||||
let status_file = Filename.temp_file ~temp_dir:t.tmpdir "vbstat" ".txt" in
|
||||
let cmd =
|
||||
sprintf "%s --homedir %s %s%s --status-file %s %s"
|
||||
t.gpg t.gpghome
|
||||
(if verify_only then "--verify" else "")
|
||||
(if verbose () then "" else " --batch -q --logger-file /dev/null")
|
||||
(quote status_file) args in
|
||||
let r = shell_command cmd in
|
||||
if r <> 0 then
|
||||
error (f_"GPG failure: could not verify digital signature of file\nTry:\n - Use the ‘-v’ option and look for earlier error messages.\n - Delete the cache: virt-builder --delete-cache\n - Check no one has tampered with the website or your network!");
|
||||
|
||||
(* Check the fingerprint is who it should be. *)
|
||||
let status = read_whole_file status_file in
|
||||
|
||||
let status = String.nsplit "\n" status in
|
||||
let fingerprint = ref "" in
|
||||
List.iter (
|
||||
fun line ->
|
||||
let line = String.nsplit " " line in
|
||||
match line with
|
||||
| "[GNUPG:]" :: "VALIDSIG" :: fp :: _ -> fingerprint := fp
|
||||
| _ -> ()
|
||||
) status;
|
||||
|
||||
if not (equal_fingerprints !fingerprint t.fingerprint) &&
|
||||
not (List.exists (equal_fingerprints !fingerprint) t.subkeys_fingerprints) then
|
||||
error (f_"fingerprint of signature does not match the expected fingerprint!\n found fingerprint: %s\n expected fingerprint: %s")
|
||||
!fingerprint t.fingerprint
|
||||
@@ -1,36 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2013 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
type t
|
||||
|
||||
val create : gpg:string -> gpgkey:Utils.gpgkey_type -> check_signature:bool -> tmpdir:string -> t
|
||||
|
||||
val verifying_signatures : t -> bool
|
||||
(** Return whether signatures are being verified by this
|
||||
Sigchecker.t. *)
|
||||
|
||||
val verify : t -> string -> unit
|
||||
(** Verify the file is signed (if check_signature is true). *)
|
||||
|
||||
val verify_detached : t -> string -> string option -> unit
|
||||
(** Verify the file is signed against the detached signature
|
||||
(if check_signature is true). *)
|
||||
|
||||
val verify_and_remove_signature : t -> string -> string option
|
||||
(** If check_signature is true, verify the file is signed and extract
|
||||
the content of the file (i.e. without the signature). *)
|
||||
@@ -1,153 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2015 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open JSON_parser
|
||||
open Utils
|
||||
|
||||
open Printf
|
||||
|
||||
let ensure_trailing_slash str =
|
||||
if String.length str > 0 && str.[String.length str - 1] <> '/' then str ^ "/"
|
||||
else str
|
||||
|
||||
let get_index ~downloader ~sigchecker { Sources.uri; proxy } =
|
||||
|
||||
let uri = ensure_trailing_slash uri in
|
||||
|
||||
let download_and_parse uri =
|
||||
let tmpfile, _ = Downloader.download downloader ~proxy uri in
|
||||
let file =
|
||||
if Sigchecker.verifying_signatures sigchecker then (
|
||||
let tmpunsigned =
|
||||
Sigchecker.verify_and_remove_signature sigchecker tmpfile in
|
||||
match tmpunsigned with
|
||||
| None -> assert false (* only when not verifying signatures *)
|
||||
| Some f -> f
|
||||
) else
|
||||
tmpfile in
|
||||
json_parser_tree_parse_file file in
|
||||
|
||||
let downloads =
|
||||
let uri_index =
|
||||
if Sigchecker.verifying_signatures sigchecker then
|
||||
uri ^ "streams/v1/index.sjson"
|
||||
else
|
||||
uri ^ "streams/v1/index.json" in
|
||||
let tree = download_and_parse uri_index in
|
||||
|
||||
let format = object_get_string "format" tree in
|
||||
if format <> "index:1.0" then
|
||||
error (f_"%s is not a Simple Streams (index) v1.0 JSON file (format: %s)")
|
||||
uri format;
|
||||
|
||||
let index = object_get_object "index" tree in
|
||||
List.filter_map (
|
||||
fun (_, desc) ->
|
||||
let format = object_get_string "format" desc in
|
||||
let datatype = object_get_string "datatype" desc in
|
||||
match format, datatype with
|
||||
| "products:1.0", "image-downloads" ->
|
||||
Some (object_get_string "path" desc)
|
||||
| _ -> None
|
||||
) index in
|
||||
|
||||
let scan_product_list path =
|
||||
let tree = download_and_parse (uri ^ path) in
|
||||
|
||||
let format = object_get_string "format" tree in
|
||||
if format <> "products:1.0" then
|
||||
error (f_"%s is not a Simple Streams (products) v1.0 JSON file (format: %s)")
|
||||
uri format;
|
||||
|
||||
let products = object_get_object "products" tree in
|
||||
|
||||
List.filter_map (
|
||||
fun (prod, prod_desc) ->
|
||||
let arch = Index.Arch (object_get_string "arch" prod_desc) in
|
||||
let prods = object_get_object "versions" prod_desc in
|
||||
let prods = List.filter_map (
|
||||
fun (rel, rel_desc) ->
|
||||
let pubname = objects_get_string "pubname" [rel_desc; prod_desc] in
|
||||
let items = object_find_object "items" rel_desc in
|
||||
let disk_items = object_find_objects (
|
||||
function
|
||||
| (("disk.img"|"disk1.img"), v) -> Some v
|
||||
| _ -> None
|
||||
) items in
|
||||
(match disk_items with
|
||||
| [] -> None
|
||||
| disk_item :: _ ->
|
||||
let printable_name = Some pubname in
|
||||
let file_uri = uri ^ (object_get_string "path" disk_item) in
|
||||
let checksums =
|
||||
let checksums = object_find_objects (
|
||||
function
|
||||
(* Since this catches all the keys, and not just
|
||||
* the ones related to checksums, explicitly filter
|
||||
* the supported checksums.
|
||||
*)
|
||||
| ("sha256"|"sha512" as t, JSON.String c) ->
|
||||
Some (Checksums.of_string t c)
|
||||
| _ -> None
|
||||
) disk_item in
|
||||
match checksums with
|
||||
| [] -> None
|
||||
| x -> Some x in
|
||||
let revision = Rev_string rel in
|
||||
let size = object_get_number "size" disk_item in
|
||||
let aliases = Some [pubname;] in
|
||||
|
||||
let entry = { Index.printable_name = printable_name;
|
||||
osinfo = None;
|
||||
file_uri = file_uri;
|
||||
arch = arch;
|
||||
signature_uri = None;
|
||||
checksums = checksums;
|
||||
revision = revision;
|
||||
format = None;
|
||||
size = size;
|
||||
compressed_size = None;
|
||||
expand = None;
|
||||
lvexpand = None;
|
||||
notes = [];
|
||||
hidden = false;
|
||||
aliases = aliases;
|
||||
sigchecker = sigchecker;
|
||||
proxy = proxy; } in
|
||||
Some (rel, (prod, entry))
|
||||
)
|
||||
) prods in
|
||||
(* Select the disk image with the bigger version (i.e. usually
|
||||
* the most recent one. *)
|
||||
let reverse_revision_compare (rev1, _) (rev2, _) = compare rev2 rev1 in
|
||||
let prods = List.sort reverse_revision_compare prods in
|
||||
match prods with
|
||||
| [] -> None
|
||||
| (_, entry) :: _ -> Some entry
|
||||
) products in
|
||||
|
||||
let entries = List.flatten (List.map scan_product_list downloads) in
|
||||
if verbose () then (
|
||||
printf "simplestreams tree (%s) after parsing:\n" uri;
|
||||
List.iter (Index.print_entry Pervasives.stdout) entries
|
||||
);
|
||||
entries
|
||||
@@ -1,19 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2015 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
val get_index : downloader:Downloader.t -> sigchecker:Sigchecker.t -> Sources.source -> Index.index
|
||||
@@ -1,142 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2014 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
open Std_utils
|
||||
open Tools_utils
|
||||
open Common_gettext.Gettext
|
||||
|
||||
open Printf
|
||||
open Unix
|
||||
|
||||
type source = {
|
||||
name : string;
|
||||
uri : string;
|
||||
gpgkey : Utils.gpgkey_type;
|
||||
proxy : Curl.proxy;
|
||||
format : source_format;
|
||||
}
|
||||
and source_format =
|
||||
| FormatNative
|
||||
| FormatSimpleStreams
|
||||
|
||||
let parse_conf file =
|
||||
debug "trying to read %s" file;
|
||||
let sections = Ini_reader.read_ini ~error_suffix:"[ignored]" file in
|
||||
|
||||
let sources = List.fold_right (
|
||||
fun (n, fields) acc ->
|
||||
let give_source n fields =
|
||||
let fields = List.map (fun (k, sk, v) -> (k, sk), v) fields in
|
||||
let uri =
|
||||
try List.assoc ("uri", None) fields
|
||||
with Not_found as ex ->
|
||||
eprintf (f_"%s: no ‘uri’ entry for ‘%s’ in %s, skipping it\n") prog n file;
|
||||
raise ex in
|
||||
let gpgkey =
|
||||
let k =
|
||||
try Some (URI.parse_uri (List.assoc ("gpgkey", None) fields)) with
|
||||
| Not_found -> None
|
||||
| URI.Parse_failed as ex ->
|
||||
debug "'%s' has invalid gpgkey URI" n;
|
||||
raise ex in
|
||||
match k with
|
||||
| None -> Utils.No_Key
|
||||
| Some uri ->
|
||||
(match uri.URI.protocol with
|
||||
| "file" -> Utils.KeyFile uri.URI.path
|
||||
| _ ->
|
||||
debug "'%s' has non-local gpgkey URI" n;
|
||||
Utils.No_Key
|
||||
) in
|
||||
let proxy =
|
||||
try
|
||||
(match (List.assoc ("proxy", None) fields) with
|
||||
| "no" | "off" -> Curl.UnsetProxy
|
||||
| "system" -> Curl.SystemProxy
|
||||
| _ as proxy -> Curl.ForcedProxy proxy
|
||||
)
|
||||
with
|
||||
Not_found -> Curl.SystemProxy in
|
||||
let format =
|
||||
try
|
||||
(match (List.assoc ("format", None) fields) with
|
||||
| "native" | "" -> FormatNative
|
||||
| "simplestreams" -> FormatSimpleStreams
|
||||
| fmt ->
|
||||
debug "unknown repository type '%s' in %s, skipping it" fmt file;
|
||||
invalid_arg fmt
|
||||
)
|
||||
with
|
||||
Not_found -> FormatNative in
|
||||
{
|
||||
name = n; uri = uri; gpgkey = gpgkey; proxy = proxy;
|
||||
format = format;
|
||||
}
|
||||
in
|
||||
try (give_source n fields) :: acc
|
||||
with Not_found | Invalid_argument _ -> acc
|
||||
) sections [] in
|
||||
|
||||
debug "read %d sources" (List.length sources);
|
||||
|
||||
sources
|
||||
|
||||
let merge_sources current_sources new_sources =
|
||||
List.fold_right (
|
||||
fun source acc ->
|
||||
if List.exists (fun { name = n } -> n = source.name) acc then
|
||||
acc
|
||||
else
|
||||
source :: acc
|
||||
) new_sources current_sources
|
||||
|
||||
let filter_filenames filename =
|
||||
Filename.check_suffix filename ".conf"
|
||||
|
||||
let read_sources () =
|
||||
let dirs = Paths.xdg_config_dirs () in
|
||||
let dirs =
|
||||
match Paths.xdg_config_home () with
|
||||
| None -> dirs
|
||||
| Some dir -> dir :: dirs in
|
||||
let dirs = List.map (fun x -> x // "repos.d") dirs in
|
||||
let fnseen = ref StringSet.empty in
|
||||
List.fold_left (
|
||||
fun acc dir ->
|
||||
let files =
|
||||
try List.filter filter_filenames (Array.to_list (Sys.readdir dir))
|
||||
with Sys_error _ -> [] in
|
||||
let files = List.filter (fun x -> StringSet.mem x !fnseen <> true) files in
|
||||
List.fold_left (
|
||||
fun acc file ->
|
||||
try (
|
||||
let s = merge_sources acc (parse_conf (dir // file)) in
|
||||
(* Add the current file name to the set only if its parsing
|
||||
* was successful.
|
||||
*)
|
||||
fnseen := StringSet.add file !fnseen;
|
||||
s
|
||||
) with
|
||||
| Unix_error (code, fname, _) ->
|
||||
debug "file error: %s: %s\n" fname (error_message code);
|
||||
acc
|
||||
| Invalid_argument msg ->
|
||||
debug "internal error: invalid argument: %s" msg;
|
||||
acc
|
||||
) acc files
|
||||
) [] dirs
|
||||
@@ -1,30 +0,0 @@
|
||||
(* virt-builder
|
||||
* Copyright (C) 2014 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*)
|
||||
|
||||
type source = {
|
||||
name : string;
|
||||
uri : string;
|
||||
gpgkey : Utils.gpgkey_type;
|
||||
proxy : Curl.proxy;
|
||||
format : source_format;
|
||||
}
|
||||
and source_format =
|
||||
| FormatNative
|
||||
| FormatSimpleStreams
|
||||
|
||||
val read_sources : unit -> source list
|
||||
@@ -1,48 +0,0 @@
|
||||
# libguestfs virt-builder tool
|
||||
# Copyright (C) 2013-2020 Red Hat Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
include $(top_srcdir)/subdir-rules.mk
|
||||
|
||||
index_fragments = $(wildcard *.index-fragment)
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(index_fragments) \
|
||||
*.ks \
|
||||
*.virt-install-cmd \
|
||||
debian.preseed \
|
||||
make-template.ml \
|
||||
ubuntu.preseed \
|
||||
validate.sh
|
||||
|
||||
# Create the index file under the top level website/ directory.
|
||||
noinst_DATA = $(top_builddir)/website/download/builder/index
|
||||
|
||||
$(top_builddir)/website/download/builder/index: $(index_fragments)
|
||||
rm -f $@ $@-t
|
||||
LANG=C sh -c 'cat *.index-fragment' > $@-t
|
||||
mv $@-t $@
|
||||
@echo "NOTE: $@.asc must be updated by running:"
|
||||
@echo " gpg --clearsign --armor $@"
|
||||
|
||||
# Validates the index file.
|
||||
TESTS_ENVIRONMENT = \
|
||||
top_srcdir="$(top_srcdir)" \
|
||||
$(top_builddir)/run --test
|
||||
TESTS = validate.sh
|
||||
|
||||
check-valgrind:
|
||||
$(MAKE) VG="@VG@" check
|
||||
@@ -1,23 +0,0 @@
|
||||
[centos-6]
|
||||
name=CentOS 6.6
|
||||
osinfo=centos6.6
|
||||
arch=x86_64
|
||||
file=centos-6.xz
|
||||
revision=6
|
||||
checksum=fc403ea3555a5608a25ad30ce2514b67288311a7197ddf9fb664475820f26db2bd95a86be9cd6e3f772187b384a02e0965430456dd518d343a80457057bc5441
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=199265736
|
||||
expand=/dev/sda3
|
||||
notes=CentOS 6.6
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/centos.sh
|
||||
|
||||
Note that ‘virt-builder centos-6’ will always install the latest
|
||||
6.x release.
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.0]
|
||||
name=CentOS 7.0
|
||||
osinfo=centos7.0
|
||||
arch=x86_64
|
||||
file=centos-7.0.xz
|
||||
checksum=cf9ae295f633fbd04e575eeca16f372e933c70c3107c44eb06864760d04354aa94b4f356bfc9a598c138e687304a52e96777e4467e7db1ec0cb5b2d2ec61affc
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=213203844
|
||||
expand=/dev/sda3
|
||||
notes=CentOS 7.0
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/centos.sh
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.1]
|
||||
name=CentOS 7.1
|
||||
osinfo=centos7.1
|
||||
arch=x86_64
|
||||
file=centos-7.1.xz
|
||||
checksum=4bd2536710daa27a70ff69a96d8a694bde1ecf48d811e75d5e6881cfdcd214c0af6949d5a8252ace06e4e8b33337f65ccb16305c85ff88156d49ac559e840b5c
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=238579176
|
||||
expand=/dev/sda3
|
||||
notes=CentOS 7.1
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/centos.sh
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.2]
|
||||
name=CentOS 7.2 (aarch64)
|
||||
osinfo=centos7.2
|
||||
arch=aarch64
|
||||
file=centos-7.2-aarch64.xz
|
||||
checksum=e61c5381026c419110ec42626c1cbb0e081240ae4d8c70f5bac2c80d771d5159b72dd3723068cf3cc9339e095b05b62d29ba9c22ef199f53a4e89c07e5615ca3
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=248167668
|
||||
expand=/dev/sda4
|
||||
notes=CentOS 7.2 (aarch64)
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/centos-aarch64.sh
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.2]
|
||||
name=CentOS 7.2
|
||||
osinfo=centos7.2
|
||||
arch=x86_64
|
||||
file=centos-7.2.xz
|
||||
checksum=b32e6003d1f15e3a97e3644e35bb3fdc345a9b2e7448655d951ec331af6cd2b5548d6acfc9d92f09ac3a8a6439069c27fa539997118cb8a3f77d3bfa45c659d0
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=252158848
|
||||
expand=/dev/sda3
|
||||
notes=CentOS 7.2
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/centos.sh
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
[centos-7.3]
|
||||
name=CentOS 7.3
|
||||
osinfo=centos7.3
|
||||
arch=x86_64
|
||||
file=centos-7.3.xz
|
||||
revision=2
|
||||
checksum[sha512]=07c8941506b1104a2571912060dc275455924415222397316fe28efd5501979c75a23c59b946acad481160b5260f61585c64777a4a9fe6f8365f9fa29df68e6c
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=294531760
|
||||
expand=/dev/sda2
|
||||
lvexpand=/dev/cl/root
|
||||
notes=CentOS 7.3
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# Kickstart file for centos-7.3
|
||||
# Generated by libguestfs.git/builder/templates/make-template.ml
|
||||
|
||||
install
|
||||
text
|
||||
reboot
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto dhcp
|
||||
rootpw builder
|
||||
firewall --enabled --ssh
|
||||
timezone --utc America/New_York
|
||||
selinux --enforcing
|
||||
|
||||
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||
|
||||
zerombr
|
||||
clearpart --all --initlabel
|
||||
autopart --type=lvm
|
||||
|
||||
# Halt the system once configuration has finished.
|
||||
poweroff
|
||||
|
||||
%packages
|
||||
@core
|
||||
%end
|
||||
|
||||
# EOF
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.4]
|
||||
name=CentOS 7.4
|
||||
osinfo=centos7.4
|
||||
arch=x86_64
|
||||
file=centos-7.4.xz
|
||||
checksum[sha512]=593a0c2534b097ddb7bcf55f64d0095c6042f9d611ee297d56e8cf37d31e9c72df77bcbdd1580d704cef25b063df43935c8bf05c79fca362a89e3a633616ea6d
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=309453024
|
||||
expand=/dev/sda3
|
||||
notes=CentOS 7.4
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# Kickstart file for centos-7.4
|
||||
# Generated by libguestfs.git/builder/templates/make-template.ml
|
||||
|
||||
install
|
||||
text
|
||||
reboot
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto dhcp
|
||||
rootpw builder
|
||||
firewall --enabled --ssh
|
||||
timezone --utc America/New_York
|
||||
selinux --enforcing
|
||||
|
||||
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||
|
||||
zerombr
|
||||
clearpart --all --initlabel
|
||||
autopart --type=plain
|
||||
|
||||
# Halt the system once configuration has finished.
|
||||
poweroff
|
||||
|
||||
%packages
|
||||
@core
|
||||
%end
|
||||
|
||||
# EOF
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.5]
|
||||
name=CentOS 7.5
|
||||
osinfo=centos7.5
|
||||
arch=x86_64
|
||||
file=centos-7.5.xz
|
||||
checksum[sha512]=4d9c64aae69d1a18c6137554e25ba77d7bc7dd4dced88d359ed6d9cf91c252e67afc0c297b10bf58309cc09bbaaf2cec647cd1dd14b63edf0c31d55e209c3b2e
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=333305636
|
||||
expand=/dev/sda4
|
||||
notes=CentOS 7.5
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# Kickstart file for centos-7.5
|
||||
# Generated by libguestfs.git/builder/templates/make-template.ml
|
||||
|
||||
install
|
||||
text
|
||||
reboot
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto dhcp
|
||||
rootpw builder
|
||||
firewall --enabled --ssh
|
||||
timezone --utc America/New_York
|
||||
selinux --enforcing
|
||||
|
||||
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||
|
||||
zerombr
|
||||
clearpart --all --initlabel --disklabel=gpt
|
||||
autopart --type=plain
|
||||
|
||||
# Halt the system once configuration has finished.
|
||||
poweroff
|
||||
|
||||
%packages
|
||||
@core
|
||||
%end
|
||||
|
||||
# EOF
|
||||
@@ -1,19 +0,0 @@
|
||||
# This is the virt-install command which was used to create
|
||||
# the virt-builder template 'centos-7.5'
|
||||
# NB: This file is generated for documentation purposes ONLY!
|
||||
# This script was never run, and is not intended to be run.
|
||||
|
||||
'virt-install' \
|
||||
'--transient' \
|
||||
'--name=tmp-mfilsn2l' \
|
||||
'--ram=2048' \
|
||||
'--cpu=host' \
|
||||
'--vcpus=4' \
|
||||
'--os-variant=centos7.0' \
|
||||
'--initrd-inject=centos-7.5.ks' \
|
||||
'--extra-args=ks=file:/centos-7.5.ks proxy=http://cache.home.annexia.org:3128 console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH' \
|
||||
'--disk=/home/rjones/d/libguestfs/builder/templates/tmp-mfilsn2l.img,size=6,format=raw' \
|
||||
'--location=http://mirror.centos.org/centos-7/7/os/x86_64/' \
|
||||
'--serial=pty' \
|
||||
'--nographics'
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.6]
|
||||
name=CentOS 7.6
|
||||
osinfo=centos7.6
|
||||
arch=x86_64
|
||||
file=centos-7.6.xz
|
||||
checksum[sha512]=aec0e3b2c012d97e01ff81afe924ce36f5fc8d8688f0ffc83f304f138619935af19d12525f1479f724c35a758feb92b659397805b0fab1fdc482ff5bf6e924d5
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=323800576
|
||||
expand=/dev/sda4
|
||||
notes=CentOS 7.6
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# Kickstart file for centos-7.6
|
||||
# Generated by libguestfs.git/builder/templates/make-template.ml
|
||||
|
||||
install
|
||||
text
|
||||
reboot
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto dhcp
|
||||
rootpw builder
|
||||
firewall --enabled --ssh
|
||||
timezone --utc America/New_York
|
||||
selinux --enforcing
|
||||
|
||||
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||
|
||||
zerombr
|
||||
clearpart --all --initlabel --disklabel=gpt
|
||||
autopart --type=plain
|
||||
|
||||
# Halt the system once configuration has finished.
|
||||
poweroff
|
||||
|
||||
%packages
|
||||
@core
|
||||
%end
|
||||
|
||||
# EOF
|
||||
@@ -1,20 +0,0 @@
|
||||
# This is the virt-install command which was used to create
|
||||
# the virt-builder template 'centos-7.6'
|
||||
# NB: This file is generated for documentation purposes ONLY!
|
||||
# This script was never run, and is not intended to be run.
|
||||
|
||||
'virt-install' \
|
||||
'--transient' \
|
||||
'--name=tmp-pi6i1jmm' \
|
||||
'--ram=2048' \
|
||||
'--arch=x86_64' \
|
||||
'--cpu=host' \
|
||||
'--vcpus=4' \
|
||||
'--os-variant=centos7.0' \
|
||||
'--initrd-inject=centos-7.6.ks' \
|
||||
'--extra-args=ks=file:/centos-7.6.ks proxy=http://cache.home.annexia.org:3128 console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH' \
|
||||
'--disk=/home/rjones/d/libguestfs/builder/templates/tmp-pi6i1jmm.img,size=6,format=raw' \
|
||||
'--location=http://mirror.centos.org/centos-7/7/os/x86_64/' \
|
||||
'--serial=pty' \
|
||||
'--nographics'
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.7]
|
||||
name=CentOS 7.7
|
||||
osinfo=centos7.7
|
||||
arch=x86_64
|
||||
file=centos-7.7.xz
|
||||
checksum[sha512]=0cc4d5a5ddff9d6dc11ddbf0161368630f4517cc4a12ac449619a0cdb7825b0b16973226617574ea8dc60338094f94db6a34d7fb7ee2906f0ef0b05215e8912d
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=353990508
|
||||
expand=/dev/sda4
|
||||
notes=CentOS 7.7
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# Kickstart file for centos-7.7
|
||||
# Generated by libguestfs.git/builder/templates/make-template.ml
|
||||
|
||||
install
|
||||
text
|
||||
reboot
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto dhcp
|
||||
rootpw builder
|
||||
firewall --enabled --ssh
|
||||
timezone --utc America/New_York
|
||||
selinux --enforcing
|
||||
|
||||
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||
|
||||
zerombr
|
||||
clearpart --all --initlabel --disklabel=gpt
|
||||
autopart --type=plain
|
||||
|
||||
# Halt the system once configuration has finished.
|
||||
poweroff
|
||||
|
||||
%packages
|
||||
@core
|
||||
%end
|
||||
|
||||
# EOF
|
||||
@@ -1,20 +0,0 @@
|
||||
# This is the virt-install command which was used to create
|
||||
# the virt-builder template 'centos-7.7'
|
||||
# NB: This file is generated for documentation purposes ONLY!
|
||||
# This script was never run, and is not intended to be run.
|
||||
|
||||
'virt-install' \
|
||||
'--transient' \
|
||||
'--name=tmp-sbpyaejm' \
|
||||
'--ram=2048' \
|
||||
'--arch=x86_64' \
|
||||
'--cpu=host' \
|
||||
'--vcpus=4' \
|
||||
'--os-variant=centos7.0' \
|
||||
'--initrd-inject=centos-7.7.ks' \
|
||||
'--extra-args=ks=file:/centos-7.7.ks console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH' \
|
||||
'--disk=/home/rjones/d/libguestfs/builder/templates/tmp-sbpyaejm.img,size=6,format=raw' \
|
||||
'--location=http://mirror.centos.org/centos-7/7/os/x86_64/' \
|
||||
'--serial=pty' \
|
||||
'--nographics'
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-7.8]
|
||||
name=CentOS 7.8
|
||||
osinfo=centos7.8
|
||||
arch=x86_64
|
||||
file=centos-7.8.xz
|
||||
checksum[sha512]=fe3c35515606d956a7a4e7894e4572d90f733740c7ff8d4e2c8724cd3a8ebe9cba70267da133955d331f671104cbe5c7959f9637cc57a744fec4253ac5d3fe3e
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=363772768
|
||||
expand=/dev/sda4
|
||||
notes=CentOS 7.8
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# Kickstart file for centos-7.8
|
||||
# Generated by libguestfs.git/builder/templates/make-template.ml
|
||||
|
||||
install
|
||||
text
|
||||
reboot
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto dhcp
|
||||
rootpw builder
|
||||
firewall --enabled --ssh
|
||||
timezone --utc America/New_York
|
||||
selinux --enforcing
|
||||
|
||||
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||
|
||||
|
||||
zerombr
|
||||
clearpart --all --initlabel --disklabel=gpt
|
||||
autopart --type=plain
|
||||
|
||||
# Halt the system once configuration has finished.
|
||||
poweroff
|
||||
|
||||
%packages
|
||||
@core
|
||||
%end
|
||||
|
||||
# EOF
|
||||
@@ -1,20 +0,0 @@
|
||||
# This is the virt-install command which was used to create
|
||||
# the virt-builder template 'centos-7.8'
|
||||
# NB: This file is generated for documentation purposes ONLY!
|
||||
# This script was never run, and is not intended to be run.
|
||||
|
||||
'virt-install' \
|
||||
'--transient' \
|
||||
'--name=tmp-bv7gf3r0' \
|
||||
'--ram=4096' \
|
||||
'--arch=x86_64' \
|
||||
'--cpu=host' \
|
||||
'--vcpus=4' \
|
||||
'--os-variant=centos7.0' \
|
||||
'--initrd-inject=centos-7.8.ks' \
|
||||
'--extra-args=ks=file:/centos-7.8.ks console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH' \
|
||||
'--disk=/home/rjones/d/libguestfs/builder/templates/tmp-bv7gf3r0.img,size=6,format=raw' \
|
||||
'--location=http://mirror.centos.org/centos-7/7/os/x86_64/' \
|
||||
'--serial=pty' \
|
||||
'--nographics'
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-8.0]
|
||||
name=CentOS 8.0
|
||||
osinfo=centos8.0
|
||||
arch=x86_64
|
||||
file=centos-8.0.xz
|
||||
checksum[sha512]=ac2f6639998867d96ac76f974024af6e3f122a6315e08dfbd2545460e8df6aef6bf5f708cefdf50bb74b1ccce91b10f35f025e599b5e21ba8972c3003d198bc1
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=351045520
|
||||
expand=/dev/sda4
|
||||
notes=CentOS 8.0
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# Kickstart file for centos-8.0
|
||||
# Generated by libguestfs.git/builder/templates/make-template.ml
|
||||
|
||||
install
|
||||
text
|
||||
reboot
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto dhcp
|
||||
rootpw builder
|
||||
firewall --enabled --ssh
|
||||
timezone --utc America/New_York
|
||||
selinux --enforcing
|
||||
|
||||
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||
|
||||
url --url="http://mirror.centos.org/centos-8/8/BaseOS/x86_64/os"
|
||||
|
||||
zerombr
|
||||
clearpart --all --initlabel --disklabel=gpt
|
||||
autopart --type=plain
|
||||
|
||||
# Halt the system once configuration has finished.
|
||||
poweroff
|
||||
|
||||
%packages
|
||||
@core
|
||||
%end
|
||||
|
||||
# EOF
|
||||
@@ -1,20 +0,0 @@
|
||||
# This is the virt-install command which was used to create
|
||||
# the virt-builder template 'centos-8.0'
|
||||
# NB: This file is generated for documentation purposes ONLY!
|
||||
# This script was never run, and is not intended to be run.
|
||||
|
||||
'virt-install' \
|
||||
'--transient' \
|
||||
'--name=tmp-1md2o05m' \
|
||||
'--ram=4096' \
|
||||
'--arch=x86_64' \
|
||||
'--cpu=host' \
|
||||
'--vcpus=4' \
|
||||
'--os-variant=rhel8.0' \
|
||||
'--initrd-inject=centos-8.0.ks' \
|
||||
'--extra-args=ks=file:/centos-8.0.ks console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH' \
|
||||
'--disk=/home/rjones/d/libguestfs/builder/templates/tmp-1md2o05m.img,size=6,format=raw' \
|
||||
'--location=http://mirror.centos.org/centos-8/8/BaseOS/x86_64/kickstart' \
|
||||
'--serial=pty' \
|
||||
'--nographics'
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[centos-8.2]
|
||||
name=CentOS 8.2
|
||||
osinfo=centos8.2
|
||||
arch=x86_64
|
||||
file=centos-8.2.xz
|
||||
checksum[sha512]=22ecd8a97321094178592862eb47ae7552bbafbbd0d7c03c10da8eadab9bcd9dafd99f048d7520278507839ee47b16bec54f0f70ad7c8402a5ff643e62b9ae5a
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=379922752
|
||||
expand=/dev/sda4
|
||||
notes=CentOS 8.2
|
||||
|
||||
This CentOS image contains only unmodified @Core group packages.
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# Kickstart file for centos-8.2
|
||||
# Generated by libguestfs.git/builder/templates/make-template.ml
|
||||
|
||||
install
|
||||
text
|
||||
reboot
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
network --bootproto dhcp
|
||||
rootpw builder
|
||||
firewall --enabled --ssh
|
||||
timezone --utc America/New_York
|
||||
selinux --enforcing
|
||||
|
||||
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||
|
||||
url --url="http://mirror.centos.org/centos-8/8/BaseOS/x86_64/os"
|
||||
|
||||
zerombr
|
||||
clearpart --all --initlabel --disklabel=gpt
|
||||
autopart --type=plain
|
||||
|
||||
# Halt the system once configuration has finished.
|
||||
poweroff
|
||||
|
||||
%packages
|
||||
@core
|
||||
%end
|
||||
|
||||
# EOF
|
||||
@@ -1,20 +0,0 @@
|
||||
# This is the virt-install command which was used to create
|
||||
# the virt-builder template 'centos-8.2'
|
||||
# NB: This file is generated for documentation purposes ONLY!
|
||||
# This script was never run, and is not intended to be run.
|
||||
|
||||
'virt-install' \
|
||||
'--transient' \
|
||||
'--name=tmp-y2rkkuka' \
|
||||
'--ram=4096' \
|
||||
'--arch=x86_64' \
|
||||
'--cpu=host' \
|
||||
'--vcpus=4' \
|
||||
'--os-variant=rhel8.0' \
|
||||
'--initrd-inject=centos-8.2.ks' \
|
||||
'--extra-args=ks=file:/centos-8.2.ks console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH' \
|
||||
'--disk=/home/rjones/d/libguestfs/builder/templates/tmp-y2rkkuka.img,size=6,format=raw' \
|
||||
'--location=http://mirror.centos.org/centos-8/8/BaseOS/x86_64/kickstart' \
|
||||
'--serial=pty' \
|
||||
'--nographics'
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
[cirros-0.3.1]
|
||||
name=CirrOS 0.3.1
|
||||
arch=x86_64
|
||||
file=cirros-0.3.1.xz
|
||||
checksum=096209f00eb62d5722accf3d22ca3a4ee5baaac6d7d4ce0be93b56bbd1c8ab2e3eb4f5db1deffcb570e2c3d41f4d721798a1c499675346cee9546554a4b10388
|
||||
format=raw
|
||||
size=41126400
|
||||
compressed_size=11419004
|
||||
expand=/dev/sda1
|
||||
notes=CirrOS 0.3.1
|
||||
|
||||
CirrOS is a commonly used test image, ideal because it is very
|
||||
small and boots into a minimally usable Linux system.
|
||||
|
||||
Note this is not a real Linux distribution, and several virt-builder
|
||||
features such as installing packages will not (and cannot) work.
|
||||
|
||||
This CirrOS image comes from https://launchpad.net/cirros
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
[cirros-0.3.5]
|
||||
name=CirrOS 0.3.5
|
||||
arch=x86_64
|
||||
file=cirros-0.3.5.xz
|
||||
checksum=77f74203aa26e843e83f522ba44d765c6ba9ab595c19c14d629064c71f875158beb7e93e7b6ac5e6895c6e3b6daa8b94d7286230b9fdf166ae65d0d6e9c9329a
|
||||
format=raw
|
||||
size=41126400
|
||||
compressed_size=11479284
|
||||
expand=/dev/sda1
|
||||
notes=CirrOS 0.3.5
|
||||
|
||||
CirrOS is a commonly used test image, ideal because it is very
|
||||
small and boots into a minimally usable Linux system.
|
||||
|
||||
Note this is not a real Linux distribution, and several virt-builder
|
||||
features such as installing packages will not (and cannot) work.
|
||||
|
||||
This CirrOS image comes from https://download.cirros-cloud.net/0.3.5/
|
||||
and was prepared by running ‘qemu-img convert .. -O raw’ followed by
|
||||
‘xz --best’.
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
[debian-10]
|
||||
name=Debian 10 (buster)
|
||||
osinfo=debian10
|
||||
arch=x86_64
|
||||
file=debian-10.xz
|
||||
checksum[sha512]=264d340e843d349f8caee14add56da4de95b22224ec48c6b3d9245afc764e4d460edabaf16fe6e4026008383128dc878a6d85eaf5dc55d66cef55cca88929c05
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=218919120
|
||||
expand=/dev/sda1
|
||||
notes=Debian 10 (buster)
|
||||
|
||||
This is a minimal Debian install.
|
||||
|
||||
This image is so very minimal that it only includes an ssh server
|
||||
This image does not contain SSH host keys. To regenerate them use:
|
||||
|
||||
--firstboot-command "dpkg-reconfigure openssh-server"
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# This is the virt-install command which was used to create
|
||||
# the virt-builder template 'debian-10'
|
||||
# NB: This file is generated for documentation purposes ONLY!
|
||||
# This script was never run, and is not intended to be run.
|
||||
|
||||
'virt-install' \
|
||||
'--transient' \
|
||||
'--name=tmp-ksggvz66' \
|
||||
'--ram=2048' \
|
||||
'--arch=x86_64' \
|
||||
'--cpu=host' \
|
||||
'--vcpus=4' \
|
||||
'--os-variant=debian8' \
|
||||
'--initrd-inject=/tmp/dqaxqsv8.tmp/preseed.cfg' \
|
||||
'--extra-args=auto mirror/http/proxy= console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH' \
|
||||
'--disk=/home/rjones/d/libguestfs/builder/templates/tmp-ksggvz66.img,size=6,format=raw' \
|
||||
'--location=http://deb.debian.org/debian/dists/buster/main/installer-amd64' \
|
||||
'--serial=pty' \
|
||||
'--nographics'
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
[debian-6]
|
||||
name=Debian 6 (Squeeze)
|
||||
osinfo=debian6
|
||||
arch=x86_64
|
||||
file=debian-6.xz
|
||||
revision=2
|
||||
checksum=bff9c28da0375fde65fa238d7a2ea644cbfad0ea3246783a2f44a98f2374850987679c3f1032a632d3c6238de8d9e43291d07a82efc1e824945000e206b9f6cc
|
||||
format=raw
|
||||
size=4294967296
|
||||
compressed_size=139615908
|
||||
expand=/dev/sda1
|
||||
notes=Debian 6 (Squeeze).
|
||||
|
||||
This is a default Debian install.
|
||||
|
||||
The preseed and virt-install scripts that produced this image
|
||||
can be found in the libguestfs source tree:
|
||||
|
||||
builder/website/debian.preseed
|
||||
builder/website/debian.sh
|
||||
|
||||
This image is so very minimal that it only includes an ssh
|
||||
server and no virtual consoles. To enable virtual consoles
|
||||
use this virt-builder option:
|
||||
|
||||
virt-builder debian-6 \
|
||||
--edit '/etc/inittab: s,^#([1-9].*respawn.*/sbin/getty.*),$1,'
|
||||
|
||||
This image does not contain SSH host keys. To regenerate them use:
|
||||
|
||||
--firstboot-command "dpkg-reconfigure openssh-server"
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
[debian-7]
|
||||
name=Debian 7 (Wheezy) (sparc64)
|
||||
osinfo=debian7
|
||||
arch=sparc64
|
||||
file=debian-7-sparc64.xz
|
||||
checksum=a81530ec2335d578e54fcf3c62b979a2985faee8e6480a49e7d24269097c89585f39a04b00d99e82aca00f3304c44dfbed843ce6ce5dcd7828256a51219b701f
|
||||
format=raw
|
||||
size=4294967296
|
||||
compressed_size=96292208
|
||||
expand=/dev/sda3
|
||||
notes=Debian 7 (Wheezy).
|
||||
|
||||
This is a Debian 7 (Wheezy) sparc64 image. This was not built using
|
||||
a reproducible script, but by installing Debian by hand, so don't use
|
||||
this in production.
|
||||
|
||||
There is also a 'debian' account which you should be aware of and
|
||||
may need to remove.
|
||||
|
||||
To build the image, use:
|
||||
|
||||
virt-builder [...] \
|
||||
--edit '/etc/inittab: s,^#([1-9].*respawn.*/sbin/getty.*),$1,' \
|
||||
--firstboot-command "dpkg-reconfigure openssh-server"
|
||||
|
||||
Resizing the image will not work because virt-builder does not
|
||||
understand the partition format.
|
||||
|
||||
To boot the image:
|
||||
|
||||
qemu-system-sparc64 -drive file=debian-7.img,format=raw -serial stdio
|
||||
|
||||
Console messages are lost after the bootconsole is disabled, but it
|
||||
is still booting and will eventually give you a login prompt.
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
[debian-7]
|
||||
name=Debian 7 (wheezy)
|
||||
osinfo=debian7
|
||||
arch=x86_64
|
||||
file=debian-7.xz
|
||||
revision=3
|
||||
checksum[sha512]=428d5867009c49bdffe3e752c68da7aa17fac415a308dffe1a0863376325aed93945500afedc0dd14b06f1b1e86598c0445c63c3bc3be91aafe4de7a0aefcbdd
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=200048080
|
||||
expand=/dev/sda1
|
||||
notes=Debian 7 (wheezy)
|
||||
|
||||
This is a minimal Debian install.
|
||||
|
||||
This image is so very minimal that it only includes an ssh server
|
||||
This image does not contain SSH host keys. To regenerate them use:
|
||||
|
||||
--firstboot-command "dpkg-reconfigure openssh-server"
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
[debian-8]
|
||||
name=Debian 8 (jessie)
|
||||
osinfo=debian8
|
||||
arch=x86_64
|
||||
file=debian-8.xz
|
||||
revision=2
|
||||
checksum[sha512]=bd182ce61636166e4cb9c98d786f61e4b53069cc945036ff04ddec3f39f3b2218183bbe059e0e237ba782622cbbee6683018cc8f45fc47a1ed7464e754b87ed3
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=232182108
|
||||
expand=/dev/sda1
|
||||
notes=Debian 8 (jessie)
|
||||
|
||||
This is a minimal Debian install.
|
||||
|
||||
This image does not contain SSH host keys. To regenerate them use:
|
||||
|
||||
--firstboot-command "dpkg-reconfigure openssh-server"
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
[debian-9]
|
||||
name=Debian 9 (stretch)
|
||||
osinfo=debian9
|
||||
arch=x86_64
|
||||
file=debian-9.xz
|
||||
revision=2
|
||||
checksum[sha512]=2e8bc3b34940157766b9ad628ddf8eb9a1b410798b921959fd57ae8a4e54fdfa74ac939fe214b8200f9d783be940f4b75b640dbf388e51ef5e74f0c96acd5529
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=194181968
|
||||
expand=/dev/sda1
|
||||
notes=Debian 9 (stretch)
|
||||
|
||||
This is a minimal Debian install.
|
||||
|
||||
This image does not contain SSH host keys. To regenerate them use:
|
||||
|
||||
--firstboot-command "dpkg-reconfigure openssh-server"
|
||||
|
||||
This template was generated by a script in the libguestfs source tree:
|
||||
builder/templates/make-template.ml
|
||||
Associated files used to prepare this template can be found in the
|
||||
same directory.
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
#### Contents of the preconfiguration file (for stretch)
|
||||
### Localization
|
||||
# Preseeding only locale sets language, country and locale.
|
||||
d-i debian-installer/locale string en_US
|
||||
|
||||
# The values can also be preseeded individually for greater flexibility.
|
||||
#d-i debian-installer/language string en
|
||||
#d-i debian-installer/country string NL
|
||||
#d-i debian-installer/locale string en_GB.UTF-8
|
||||
# Optionally specify additional locales to be generated.
|
||||
#d-i localechooser/supported-locales multiselect en_US.UTF-8, nl_NL.UTF-8
|
||||
|
||||
# Keyboard selection.
|
||||
d-i keyboard-configuration/xkb-keymap select us
|
||||
# d-i keyboard-configuration/toggle select No toggling
|
||||
|
||||
### Network configuration
|
||||
# Disable network configuration entirely. This is useful for cdrom
|
||||
# installations on non-networked devices where the network questions,
|
||||
# warning and long timeouts are a nuisance.
|
||||
d-i netcfg/enable boolean true
|
||||
|
||||
# netcfg will choose an interface that has link if possible. This makes it
|
||||
# skip displaying a list if there is more than one interface.
|
||||
d-i netcfg/choose_interface select auto
|
||||
|
||||
# To pick a particular interface instead:
|
||||
#d-i netcfg/choose_interface select eth1
|
||||
|
||||
# To set a different link detection timeout (default is 3 seconds).
|
||||
# Values are interpreted as seconds.
|
||||
#d-i netcfg/link_wait_timeout string 10
|
||||
|
||||
# If you have a slow dhcp server and the installer times out waiting for
|
||||
# it, this might be useful.
|
||||
#d-i netcfg/dhcp_timeout string 60
|
||||
#d-i netcfg/dhcpv6_timeout string 60
|
||||
|
||||
# If you prefer to configure the network manually, uncomment this line and
|
||||
# the static network configuration below.
|
||||
#d-i netcfg/disable_autoconfig boolean true
|
||||
|
||||
# If you want the preconfiguration file to work on systems both with and
|
||||
# without a dhcp server, uncomment these lines and the static network
|
||||
# configuration below.
|
||||
#d-i netcfg/dhcp_failed note
|
||||
#d-i netcfg/dhcp_options select Configure network manually
|
||||
|
||||
# Static network configuration.
|
||||
#
|
||||
# IPv4 example
|
||||
#d-i netcfg/get_ipaddress string 192.168.1.42
|
||||
#d-i netcfg/get_netmask string 255.255.255.0
|
||||
#d-i netcfg/get_gateway string 192.168.1.1
|
||||
#d-i netcfg/get_nameservers string 192.168.1.1
|
||||
#d-i netcfg/confirm_static boolean true
|
||||
#
|
||||
# IPv6 example
|
||||
#d-i netcfg/get_ipaddress string fc00::2
|
||||
#d-i netcfg/get_netmask string ffff:ffff:ffff:ffff::
|
||||
#d-i netcfg/get_gateway string fc00::1
|
||||
#d-i netcfg/get_nameservers string fc00::1
|
||||
#d-i netcfg/confirm_static boolean true
|
||||
|
||||
# Any hostname and domain names assigned from dhcp take precedence over
|
||||
# values set here. However, setting the values still prevents the questions
|
||||
# from being shown, even if values come from dhcp.
|
||||
d-i netcfg/get_hostname string unassigned-hostname
|
||||
d-i netcfg/get_domain string unassigned-domain
|
||||
|
||||
# If you want to force a hostname, regardless of what either the DHCP
|
||||
# server returns or what the reverse DNS entry for the IP is, uncomment
|
||||
# and adjust the following line.
|
||||
d-i netcfg/hostname string unassigned-hostname.unassigned-domain
|
||||
|
||||
# Disable that annoying WEP key dialog.
|
||||
d-i netcfg/wireless_wep string
|
||||
# The wacky dhcp hostname that some ISPs use as a password of sorts.
|
||||
#d-i netcfg/dhcp_hostname string radish
|
||||
|
||||
# If non-free firmware is needed for the network or other hardware, you can
|
||||
# configure the installer to always try to load it, without prompting. Or
|
||||
# change to false to disable asking.
|
||||
#d-i hw-detect/load_firmware boolean true
|
||||
|
||||
### Network console
|
||||
# Use the following settings if you wish to make use of the network-console
|
||||
# component for remote installation over SSH. This only makes sense if you
|
||||
# intend to perform the remainder of the installation manually.
|
||||
#d-i anna/choose_modules string network-console
|
||||
#d-i network-console/authorized_keys_url string http://10.0.0.1/openssh-key
|
||||
#d-i network-console/password password r00tme
|
||||
#d-i network-console/password-again password r00tme
|
||||
|
||||
### Mirror settings
|
||||
# If you select ftp, the mirror/country string does not need to be set.
|
||||
#d-i mirror/protocol string ftp
|
||||
d-i mirror/country string manual
|
||||
d-i mirror/http/hostname string deb.debian.org
|
||||
d-i mirror/http/directory string /debian
|
||||
#d-i mirror/http/proxy string
|
||||
|
||||
# Suite to install.
|
||||
#d-i mirror/suite string testing
|
||||
# Suite to use for loading installer components (optional).
|
||||
#d-i mirror/udeb/suite string testing
|
||||
|
||||
### Account setup
|
||||
# Skip creation of a root account (normal user account will be able to
|
||||
# use sudo).
|
||||
#d-i passwd/root-login boolean false
|
||||
# Alternatively, to skip creation of a normal user account.
|
||||
d-i passwd/make-user boolean false
|
||||
|
||||
# Root password, either in clear text
|
||||
d-i passwd/root-password password builder
|
||||
d-i passwd/root-password-again password builder
|
||||
# or encrypted using a crypt(3) hash.
|
||||
#d-i passwd/root-password-crypted password [crypt(3) hash]
|
||||
|
||||
# To create a normal user account.
|
||||
#d-i passwd/user-fullname string Debian User
|
||||
#d-i passwd/username string debian
|
||||
# Normal user's password, either in clear text
|
||||
#d-i passwd/user-password password insecure
|
||||
#d-i passwd/user-password-again password insecure
|
||||
# or encrypted using a crypt(3) hash.
|
||||
#d-i passwd/user-password-crypted password [crypt(3) hash]
|
||||
# Create the first user with the specified UID instead of the default.
|
||||
#d-i passwd/user-uid string 1010
|
||||
|
||||
# The user account will be added to some standard initial groups. To
|
||||
# override that, use this.
|
||||
#d-i passwd/user-default-groups string audio cdrom video
|
||||
|
||||
### Clock and time zone setup
|
||||
# Controls whether or not the hardware clock is set to UTC.
|
||||
d-i clock-setup/utc boolean true
|
||||
|
||||
# You may set this to any valid setting for $TZ; see the contents of
|
||||
# /usr/share/zoneinfo/ for valid values.
|
||||
d-i time/zone string US/Eastern
|
||||
|
||||
# Controls whether to use NTP to set the clock during the install
|
||||
d-i clock-setup/ntp boolean true
|
||||
# NTP server to use. The default is almost always fine here.
|
||||
#d-i clock-setup/ntp-server string ntp.example.com
|
||||
|
||||
### Partitioning
|
||||
## Partitioning example
|
||||
# If the system has free space you can choose to only partition that space.
|
||||
# This is only honoured if partman-auto/method (below) is not set.
|
||||
#d-i partman-auto/init_automatically_partition select biggest_free
|
||||
|
||||
# Alternatively, you may specify a disk to partition. If the system has only
|
||||
# one disk the installer will default to using that, but otherwise the device
|
||||
# name must be given in traditional, non-devfs format (so e.g. /dev/sda
|
||||
# and not e.g. /dev/discs/disc0/disc).
|
||||
# For example, to use the first SCSI/SATA hard disk:
|
||||
#d-i partman-auto/disk string /dev/sda
|
||||
# In addition, you'll need to specify the method to use.
|
||||
# The presently available methods are:
|
||||
# - regular: use the usual partition types for your architecture
|
||||
# - lvm: use LVM to partition the disk
|
||||
# - crypto: use LVM within an encrypted partition
|
||||
d-i partman-auto/method string regular
|
||||
|
||||
# If one of the disks that are going to be automatically partitioned
|
||||
# contains an old LVM configuration, the user will normally receive a
|
||||
# warning. This can be preseeded away...
|
||||
d-i partman-lvm/device_remove_lvm boolean true
|
||||
# The same applies to pre-existing software RAID array:
|
||||
d-i partman-md/device_remove_md boolean true
|
||||
# And the same goes for the confirmation to write the lvm partitions.
|
||||
d-i partman-lvm/confirm boolean true
|
||||
d-i partman-lvm/confirm_nooverwrite boolean true
|
||||
d-i partman-basicfilesystems/no_swap boolean false
|
||||
|
||||
d-i partman-auto/expert_recipe string myroot :: 500 10000 1000000 ext4 \
|
||||
$primary{ } \
|
||||
$bootable{ } \
|
||||
method{ format } \
|
||||
format{ } \
|
||||
use_filesystem{ } \
|
||||
filesystem{ ext4 } \
|
||||
mountpoint{ / } .
|
||||
|
||||
# You can choose one of the three predefined partitioning recipes:
|
||||
# - atomic: all files in one partition
|
||||
# - home: separate /home partition
|
||||
# - multi: separate /home, /var, and /tmp partitions
|
||||
|
||||
d-i partman-auto/choose_recipe select myroot
|
||||
|
||||
# Or provide a recipe of your own...
|
||||
# If you have a way to get a recipe file into the d-i environment, you can
|
||||
# just point at it.
|
||||
#d-i partman-auto/expert_recipe_file string /hd-media/recipe
|
||||
|
||||
# If not, you can put an entire recipe into the preconfiguration file in one
|
||||
# (logical) line. This example creates a small /boot partition, suitable
|
||||
# swap, and uses the rest of the space for the root partition:
|
||||
#d-i partman-auto/expert_recipe string \
|
||||
# boot-root :: \
|
||||
# 40 50 100 ext3 \
|
||||
# $primary{ } $bootable{ } \
|
||||
# method{ format } format{ } \
|
||||
# use_filesystem{ } filesystem{ ext3 } \
|
||||
# mountpoint{ /boot } \
|
||||
# . \
|
||||
# 500 10000 1000000000 ext3 \
|
||||
# method{ format } format{ } \
|
||||
# use_filesystem{ } filesystem{ ext3 } \
|
||||
# mountpoint{ / } \
|
||||
# . \
|
||||
# 64 512 300% linux-swap \
|
||||
# method{ swap } format{ } \
|
||||
# .
|
||||
|
||||
# The full recipe format is documented in the file partman-auto-recipe.txt
|
||||
# included in the 'debian-installer' package or available from D-I source
|
||||
# repository. This also documents how to specify settings such as file
|
||||
# system labels, volume group names and which physical devices to include
|
||||
# in a volume group.
|
||||
|
||||
# This makes partman automatically partition without confirmation, provided
|
||||
# that you told it what to do using one of the methods above.
|
||||
d-i partman-partitioning/confirm_write_new_label boolean true
|
||||
d-i partman/choose_partition select finish
|
||||
d-i partman/confirm boolean true
|
||||
d-i partman/confirm_nooverwrite boolean true
|
||||
|
||||
# When disk encryption is enabled, skip wiping the partitions beforehand.
|
||||
#d-i partman-auto-crypto/erase_disks boolean false
|
||||
|
||||
## Partitioning using RAID
|
||||
# The method should be set to "raid".
|
||||
#d-i partman-auto/method string raid
|
||||
# Specify the disks to be partitioned. They will all get the same layout,
|
||||
# so this will only work if the disks are the same size.
|
||||
#d-i partman-auto/disk string /dev/sda /dev/sdb
|
||||
|
||||
# Next you need to specify the physical partitions that will be used.
|
||||
#d-i partman-auto/expert_recipe string \
|
||||
# multiraid :: \
|
||||
# 1000 5000 4000 raid \
|
||||
# $primary{ } method{ raid } \
|
||||
# . \
|
||||
# 64 512 300% raid \
|
||||
# method{ raid } \
|
||||
# . \
|
||||
# 500 10000 1000000000 raid \
|
||||
# method{ raid } \
|
||||
# .
|
||||
|
||||
# Last you need to specify how the previously defined partitions will be
|
||||
# used in the RAID setup. Remember to use the correct partition numbers
|
||||
# for logical partitions. RAID levels 0, 1, 5, 6 and 10 are supported;
|
||||
# devices are separated using "#".
|
||||
# Parameters are:
|
||||
# <raidtype> <devcount> <sparecount> <fstype> <mountpoint> \
|
||||
# <devices> <sparedevices>
|
||||
|
||||
#d-i partman-auto-raid/recipe string \
|
||||
# 1 2 0 ext3 / \
|
||||
# /dev/sda1#/dev/sdb1 \
|
||||
# . \
|
||||
# 1 2 0 swap - \
|
||||
# /dev/sda5#/dev/sdb5 \
|
||||
# . \
|
||||
# 0 2 0 ext3 /home \
|
||||
# /dev/sda6#/dev/sdb6 \
|
||||
# .
|
||||
|
||||
# For additional information see the file partman-auto-raid-recipe.txt
|
||||
# included in the 'debian-installer' package or available from D-I source
|
||||
# repository.
|
||||
|
||||
# This makes partman automatically partition without confirmation.
|
||||
d-i partman-md/confirm boolean true
|
||||
d-i partman-partitioning/confirm_write_new_label boolean true
|
||||
d-i partman/choose_partition select finish
|
||||
d-i partman/confirm boolean true
|
||||
d-i partman/confirm_nooverwrite boolean true
|
||||
|
||||
## Controlling how partitions are mounted
|
||||
# The default is to mount by UUID, but you can also choose "traditional" to
|
||||
# use traditional device names, or "label" to try filesystem labels before
|
||||
# falling back to UUIDs.
|
||||
#d-i partman/mount_style select uuid
|
||||
|
||||
### Base system installation
|
||||
# Configure APT to not install recommended packages by default. Use of this
|
||||
# option can result in an incomplete system and should only be used by very
|
||||
# experienced users.
|
||||
#d-i base-installer/install-recommends boolean false
|
||||
|
||||
# The kernel image (meta) package to be installed; "none" can be used if no
|
||||
# kernel is to be installed.
|
||||
#d-i base-installer/kernel/image string linux-image-486
|
||||
|
||||
### Apt setup
|
||||
# You can choose to install non-free and contrib software.
|
||||
#d-i apt-setup/non-free boolean true
|
||||
#d-i apt-setup/contrib boolean true
|
||||
# Uncomment this if you don't want to use a network mirror.
|
||||
#d-i apt-setup/use_mirror boolean false
|
||||
# Select which update services to use; define the mirrors to be used.
|
||||
# Values shown below are the normal defaults.
|
||||
#d-i apt-setup/services-select multiselect security, volatile
|
||||
#d-i apt-setup/security_host string security.debian.org
|
||||
#d-i apt-setup/volatile_host string volatile.debian.org
|
||||
|
||||
# Additional repositories, local[0-9] available
|
||||
#d-i apt-setup/local0/repository string \
|
||||
# http://local.server/debian stable main
|
||||
#d-i apt-setup/local0/comment string local server
|
||||
# Enable deb-src lines
|
||||
#d-i apt-setup/local0/source boolean true
|
||||
# URL to the public key of the local repository; you must provide a key or
|
||||
# apt will complain about the unauthenticated repository and so the
|
||||
# sources.list line will be left commented out
|
||||
#d-i apt-setup/local0/key string http://local.server/key
|
||||
|
||||
# By default the installer requires that repositories be authenticated
|
||||
# using a known gpg key. This setting can be used to disable that
|
||||
# authentication. Warning: Insecure, not recommended.
|
||||
#d-i debian-installer/allow_unauthenticated boolean true
|
||||
|
||||
# Uncomment this to add multiarch configuration for i386
|
||||
#d-i apt-setup/multiarch string i386
|
||||
|
||||
|
||||
### Package selection
|
||||
tasksel tasksel/first multiselect standard, ssh-server
|
||||
|
||||
# Individual additional packages to install
|
||||
#d-i pkgsel/include string openssh-server build-essential
|
||||
# Whether to upgrade packages after debootstrap.
|
||||
# Allowed values: none, safe-upgrade, full-upgrade
|
||||
d-i pkgsel/upgrade select full-upgrade
|
||||
|
||||
# Some versions of the installer can report back on what software you have
|
||||
# installed, and what software you use. The default is not to report back,
|
||||
# but sending reports helps the project determine what software is most
|
||||
# popular and include it on CDs.
|
||||
popularity-contest popularity-contest/participate boolean false
|
||||
|
||||
### Boot loader installation
|
||||
# Grub is the default boot loader (for x86). If you want lilo installed
|
||||
# instead, uncomment this:
|
||||
#d-i grub-installer/skip boolean true
|
||||
# To also skip installing lilo, and install no bootloader, uncomment this
|
||||
# too:
|
||||
#d-i lilo-installer/skip boolean true
|
||||
|
||||
|
||||
# This is fairly safe to set, it makes grub install automatically to the MBR
|
||||
# if no other operating system is detected on the machine.
|
||||
d-i grub-installer/only_debian boolean true
|
||||
|
||||
# This one makes grub-installer install to the MBR if it also finds some other
|
||||
# OS, which is less safe as it might not be able to boot that other OS.
|
||||
d-i grub-installer/with_other_os boolean true
|
||||
|
||||
# Due notably to potential USB sticks, the location of the MBR can not be
|
||||
# determined safely in general, so this needs to be specified:
|
||||
#d-i grub-installer/bootdev string /dev/sda
|
||||
# To install to the first device (assuming it is not a USB stick):
|
||||
d-i grub-installer/bootdev string default
|
||||
|
||||
# Alternatively, if you want to install to a location other than the mbr,
|
||||
# uncomment and edit these lines:
|
||||
#d-i grub-installer/only_debian boolean false
|
||||
#d-i grub-installer/with_other_os boolean false
|
||||
#d-i grub-installer/bootdev string (hd0,1)
|
||||
# To install grub to multiple disks:
|
||||
#d-i grub-installer/bootdev string (hd0,1) (hd1,1) (hd2,1)
|
||||
|
||||
# Optional password for grub, either in clear text
|
||||
#d-i grub-installer/password password r00tme
|
||||
#d-i grub-installer/password-again password r00tme
|
||||
# or encrypted using an MD5 hash, see grub-md5-crypt(8).
|
||||
#d-i grub-installer/password-crypted password [MD5 hash]
|
||||
|
||||
# Use the following option to add additional boot parameters for the
|
||||
# installed system (if supported by the bootloader installer).
|
||||
# Note: options passed to the installer will be added automatically.
|
||||
#d-i debian-installer/add-kernel-opts string nousb
|
||||
|
||||
### Finishing up the installation
|
||||
# During installations from serial console, the regular virtual consoles
|
||||
# (VT1-VT6) are normally disabled in /etc/inittab. Uncomment the next
|
||||
# line to prevent this.
|
||||
#d-i finish-install/keep-consoles boolean true
|
||||
|
||||
# Avoid that last message about the install being complete.
|
||||
d-i finish-install/reboot_in_progress note
|
||||
|
||||
# This will prevent the installer from ejecting the CD during the reboot,
|
||||
# which is useful in some situations.
|
||||
#d-i cdrom-detect/eject boolean false
|
||||
|
||||
# This is how to make the installer shutdown when finished, but not
|
||||
# reboot into the installed system.
|
||||
#d-i debian-installer/exit/halt boolean true
|
||||
# This will power off the machine instead of just halting it.
|
||||
#d-i debian-installer/exit/poweroff boolean true
|
||||
|
||||
### Preseeding other packages
|
||||
# Depending on what software you choose to install, or if things go wrong
|
||||
# during the installation process, it's possible that other questions may
|
||||
# be asked. You can preseed those too, of course. To get a list of every
|
||||
# possible question that could be asked during an install, do an
|
||||
# installation, and then run these commands:
|
||||
# debconf-get-selections --installer > file
|
||||
# debconf-get-selections >> file
|
||||
|
||||
|
||||
#### Advanced options
|
||||
### Running custom commands during the installation
|
||||
# d-i preseeding is inherently not secure. Nothing in the installer checks
|
||||
# for attempts at buffer overflows or other exploits of the values of a
|
||||
# preconfiguration file like this one. Only use preconfiguration files from
|
||||
# trusted locations! To drive that home, and because it's generally useful,
|
||||
# here's a way to run any shell command you'd like inside the installer,
|
||||
# automatically.
|
||||
|
||||
# This first command is run as early as possible, just after
|
||||
# preseeding is read.
|
||||
#d-i preseed/early_command string anna-install some-udeb
|
||||
# This command is run immediately before the partitioner starts. It may be
|
||||
# useful to apply dynamic partitioner preseeding that depends on the state
|
||||
# of the disks (which may not be visible when preseed/early_command runs).
|
||||
#d-i partman/early_command \
|
||||
# string debconf-set partman-auto/disk "$(list-devices disk | head -n1)"
|
||||
# This command is run just before the install finishes, but when there is
|
||||
# still a usable /target directory. You can chroot to /target and use it
|
||||
# directly, or use the apt-install and in-target commands to easily install
|
||||
# packages and run commands in the target system.
|
||||
#d-i preseed/late_command string apt-install zsh; in-target chsh -s /bin/zsh
|
||||
d-i preseed/late_command string in-target sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="console=ttyS0,115200n8"/g; s/# *GRUB_TERMINAL=console/GRUB_TERMINAL=serial/g' /etc/default/grub; in-target update-grub
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
[fedora-18]
|
||||
name=Fedora® 18
|
||||
osinfo=fedora18
|
||||
arch=x86_64
|
||||
file=fedora-18.xz
|
||||
checksum=12435775193b69f6e22658aaa001d4ca9b15fd68a04b4b7e9be20b3b517e857e417dc3268a302979d4a702b20f25754025f7ae0e9fb7088419a4ca1669585e6f
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=148947524
|
||||
expand=/dev/sda3
|
||||
notes=Fedora 18.
|
||||
|
||||
This Fedora image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/fedora.sh
|
||||
|
||||
Fedora and the Infinity design logo are trademarks of Red Hat, Inc.
|
||||
Source and further information is available from http://fedoraproject.org/
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
[fedora-19]
|
||||
name=Fedora® 19
|
||||
osinfo=fedora19
|
||||
arch=x86_64
|
||||
file=fedora-19.xz
|
||||
revision=2
|
||||
checksum=acecd8d4bca0d6a3f937e0c9386f3185c916df3eaf5f825988c73d41e946a6dc4fda06cdd74a12bc60932edd65846097547b11aca2309a61dd6d0e91ab9d16f3
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=169531628
|
||||
expand=/dev/sda3
|
||||
notes=Fedora 19.
|
||||
|
||||
This Fedora image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/fedora.sh
|
||||
|
||||
Fedora and the Infinity design logo are trademarks of Red Hat, Inc.
|
||||
Source and further information is available from http://fedoraproject.org/
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
[fedora-20]
|
||||
name=Fedora® 20
|
||||
osinfo=fedora20
|
||||
arch=x86_64
|
||||
file=fedora-20.xz
|
||||
revision=2
|
||||
checksum=983a1b33c34cb311ea3a283f06312d24dc81041b64ebc90e40ef2fd7587362acd1a5654b13252f9f57001870aa95495065537e730d5225b49389c1a0478cb028
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=181164220
|
||||
expand=/dev/sda3
|
||||
notes=Fedora 20.
|
||||
|
||||
This Fedora image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/fedora.sh
|
||||
|
||||
Fedora and the Infinity design logo are trademarks of Red Hat, Inc.
|
||||
Source and further information is available from http://fedoraproject.org/
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
[fedora-21]
|
||||
name=Fedora® 21 Server (aarch64)
|
||||
osinfo=fedora21
|
||||
arch=aarch64
|
||||
file=fedora-21-aarch64.xz
|
||||
checksum=57026dd867cbc2e49894dd056ffdc1c397548f4f7e296f393a77ee55343a17f684ddcd3ff7661f514b54209c472f41fce809f2e36064fb66d4f92d8dce5e9b62
|
||||
format=raw
|
||||
size=6442450944
|
||||
compressed_size=186616612
|
||||
expand=/dev/sda4
|
||||
notes=Fedora 21 Server (aarch64)
|
||||
|
||||
This Fedora image contains only unmodified @Core group packages.
|
||||
|
||||
It is thus very minimal. The kickstart and install script can be
|
||||
found in the libguestfs source tree:
|
||||
|
||||
builder/website/fedora-aarch64.sh
|
||||
|
||||
Please note you will need to use the associated EFI NVRAM variables:
|
||||
http://libguestfs.org/download/builder/fedora-21-aarch64-nvram.xz
|
||||
|
||||
Fedora and the Infinity design logo are trademarks of Red Hat, Inc.
|
||||
Source and further information is available from http://fedoraproject.org/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user