Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 00/41] Refactor utilities, reimplement inspection in the daemon.
v5: https://www.redhat.com/archives/libguestfs/2017-June/msg00065.html Since v5, this now implements inspection almost completely for Linux and Windows guests. Rich.
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 01/41] mllib: Move Visit OCaml bindings to common/mlvisit.
The ‘Visit’ module is a self-contained library with the only dependencies being: - the C ‘visit’ implementation - the guestfs OCaml bindings Move it to a separate ‘common/mlvisit’ directory. This change is not entirely refactoring. Two other fixes are made: - remove unsafe use of CLEANUP_FREE from a function which could raise an OCaml exception (cleanup handlers would not be called correctly if the exception is thrown) - don't link directly to common/visit/visit.c, but instead use the library (common/visit/libvisit.la) --- .gitignore | 3 +- Makefile.am | 5 +- common/mlvisit/Makefile.am | 152 +++++++++++++++++++++++++++++++ common/mlvisit/dummy.c | 2 + {mllib => common/mlvisit}/visit-c.c | 6 +- {mllib => common/mlvisit}/visit.ml | 0 {mllib => common/mlvisit}/visit.mli | 0 {mllib => common/mlvisit}/visit_tests.ml | 0 configure.ac | 1 + docs/C_SOURCE_FILES | 3 +- docs/guestfs-hacking.pod | 4 + mllib/Makefile.am | 30 +----- sysprep/Makefile.am | 10 +- 13 files changed, 180 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 69e1ae160..2367cddcb 100644 --- a/.gitignore +++ b/.gitignore @@ -124,6 +124,8 @@ Makefile.in /common/errnostring/errnostring-gperf.gperf /common/errnostring/errnostring.h /common/miniexpect/miniexpect.3 +/common/mlvisit/.depend +/common/mlvisit/visit_tests /common/protocol/guestfs_protocol.c /common/protocol/guestfs_protocol.h /common/protocol/guestfs_protocol.x @@ -366,7 +368,6 @@ Makefile.in /mllib/JSON_tests /mllib/libdir.ml /mllib/oUnit-* -/mllib/visit_tests /ocaml/bindtests.bc /ocaml/bindtests.opt /ocaml/bindtests.ml diff --git a/Makefile.am b/Makefile.am index ae77cdda2..499a1d279 100644 --- a/Makefile.am +++ b/Makefile.am @@ -150,10 +150,11 @@ endif # Unconditional because nothing is built yet. SUBDIRS += csharp -# OCaml tools. Note 'mllib' and 'customize' contain shared code used -# by other OCaml tools, so these must come first. +# OCaml tools. Note 'common/ml*', 'mllib' and 'customize' contain +# shared code used by other OCaml tools, so these must come first. if HAVE_OCAML SUBDIRS += \ + common/mlvisit \ mllib \ customize \ builder builder/templates \ diff --git a/common/mlvisit/Makefile.am b/common/mlvisit/Makefile.am new file mode 100644 index 000000000..51cbd2de6 --- /dev/null +++ b/common/mlvisit/Makefile.am @@ -0,0 +1,152 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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. + +include $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + $(SOURCES_C) \ + visit_tests.ml + +SOURCES_MLI = \ + visit.mli + +SOURCES_ML = \ + visit.ml + +SOURCES_C = \ + visit-c.c + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlvisit.a + +if !HAVE_OCAMLOPT +MLVISIT_CMA = mlvisit.cma +else +MLVISIT_CMA = mlvisit.cmxa +endif + +noinst_DATA = $(MLVISIT_CMA) + +libmlvisit_a_SOURCES = $(SOURCES_C) +libmlvisit_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/common/visit +libmlvisit_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(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)/lib/.libs \ + -I $(top_builddir)/gnulib/lib/.libs \ + -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/visit/.libs \ + -I $(builddir) +OCAMLPACKAGES_TESTS = $(MLVISIT_CMA) + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlvisit_a_DEPENDENCIES = $(OBJECTS) + +$(MLVISIT_CMA): $(OBJECTS) libmlvisit.a + $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlvisit_a_OBJECTS) -cclib -lvisit -o mlvisit + +# Tests. + +visit_tests_SOURCES = dummy.c +visit_tests_BOBJECTS = visit_tests.cmo +visit_tests_XOBJECTS = $(visit_tests_BOBJECTS:.cmo=.cmx) + +# Can't call the following as <test>_OBJECTS because automake gets confused. +if !HAVE_OCAMLOPT +visit_tests_THEOBJECTS = $(visit_tests_BOBJECTS) +visit_tests.cmo: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) +else +visit_tests_THEOBJECTS = $(visit_tests_XOBJECTS) +visit_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) +endif + +OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) + +visit_tests_DEPENDENCIES = \ + $(visit_tests_THEOBJECTS) \ + $(MLVISIT_CMA) \ + $(top_srcdir)/ocaml-link.sh +visit_tests_LINK = \ + $(top_srcdir)/ocaml-link.sh \ + -cclib '-lvisit -lutils $(LIBXML2_LIBS) -lgnu' -- \ + $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ + $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ + $(visit_tests_THEOBJECTS) -o $@ + +TESTS_ENVIRONMENT = $(top_builddir)/run --test + +check_PROGRAMS +TESTS + +if ENABLE_APPLIANCE +check_PROGRAMS += visit_tests +TESTS += visit_tests +endif + +check-valgrind: + $(MAKE) VG="@VG@" check + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/common/mlvisit/dummy.c b/common/mlvisit/dummy.c new file mode 100644 index 000000000..ebab6198c --- /dev/null +++ b/common/mlvisit/dummy.c @@ -0,0 +1,2 @@ +/* Dummy source, to be used for OCaml-based tools with no C sources. */ +enum { foo = 1 }; diff --git a/mllib/visit-c.c b/common/mlvisit/visit-c.c similarity index 98% rename from mllib/visit-c.c rename to common/mlvisit/visit-c.c index b1c12166c..4cd1c6cea 100644 --- a/mllib/visit-c.c +++ b/common/mlvisit/visit-c.c @@ -31,7 +31,6 @@ #include <caml/mlvalues.h> #include "guestfs.h" -#include "guestfs-internal.h" #include "visit.h" #pragma GCC diagnostic ignored "-Wmissing-prototypes" @@ -59,7 +58,7 @@ guestfs_int_mllib_visit (value gv, value dirv, value fv) /* The dir string could move around when we call the * visitor_function, so we have to take a full copy of it. */ - CLEANUP_FREE char *dir = strdup (String_val (dirv)); + char *dir = strdup (String_val (dirv)); /* This stack address is used to point to the exception, if one is * raised in the visitor_function. */ @@ -71,6 +70,8 @@ guestfs_int_mllib_visit (value gv, value dirv, value fv) args.fvp = &fv; if (visit (g, dir, visitor_function_wrapper, &args) == -1) { + free (dir); + if (exn != Val_unit) { /* The failure was caused by visitor_function raising an * exception. Re-raise it here. @@ -84,6 +85,7 @@ guestfs_int_mllib_visit (value gv, value dirv, value fv) */ caml_failwith ("visit"); } + free (dir); CAMLreturn (Val_unit); } diff --git a/mllib/visit.ml b/common/mlvisit/visit.ml similarity index 100% rename from mllib/visit.ml rename to common/mlvisit/visit.ml diff --git a/mllib/visit.mli b/common/mlvisit/visit.mli similarity index 100% rename from mllib/visit.mli rename to common/mlvisit/visit.mli diff --git a/mllib/visit_tests.ml b/common/mlvisit/visit_tests.ml similarity index 100% rename from mllib/visit_tests.ml rename to common/mlvisit/visit_tests.ml diff --git a/configure.ac b/configure.ac index cbb5101fc..4fc226123 100644 --- a/configure.ac +++ b/configure.ac @@ -185,6 +185,7 @@ AC_CONFIG_FILES([Makefile common/errnostring/Makefile common/edit/Makefile common/miniexpect/Makefile + common/mlvisit/Makefile common/options/Makefile common/parallel/Makefile common/progress/Makefile diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 15abec124..578126403 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -15,6 +15,8 @@ common/edit/file-edit.c common/edit/file-edit.h common/miniexpect/miniexpect.c common/miniexpect/miniexpect.h +common/mlvisit/dummy.c +common/mlvisit/visit-c.c common/options/config.c common/options/decrypt.c common/options/display-options.c @@ -340,7 +342,6 @@ mllib/getopt-c.c mllib/progress-c.c mllib/unix_utils-c.c mllib/uri-c.c -mllib/visit-c.c mllib/xml-c.c ocaml/guestfs-c-actions.c ocaml/guestfs-c-errnos.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index d3621c7e6..ac6d1ccbf 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -100,6 +100,10 @@ A copy of the miniexpect library from L<http://git.annexia.org/?p=miniexpect.git;a=summary>. This is used in virt-p2v. +=item F<common/mlvisit> + +OCaml bindings for the visit functions (see F<common/visit>). + =item F<common/options> Common options parsing for guestfish, guestmount and some virt tools. diff --git a/mllib/Makefile.am b/mllib/Makefile.am index ee2f1a7a8..cb79f50f5 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -24,7 +24,6 @@ EXTRA_DIST = \ common_utils_tests.ml \ getopt_tests.ml \ JSON_tests.ml \ - visit_tests.ml \ test-getopt.sh SOURCES_MLI = \ @@ -41,8 +40,7 @@ SOURCES_MLI = \ regedit.mli \ registry.mli \ stringMap.mli \ - URI.mli \ - visit.mli + URI.mli SOURCES_ML = \ guestfs_config.ml \ @@ -55,7 +53,6 @@ SOURCES_ML = \ common_utils.ml \ progress.ml \ URI.ml \ - visit.ml \ planner.ml \ registry.ml \ regedit.ml \ @@ -66,7 +63,6 @@ SOURCES_ML = \ xpath_helpers.ml SOURCES_C = \ - ../common/visit/visit.c \ ../common/options/decrypt.c \ ../common/options/keys.c \ ../common/options/uri.c \ @@ -76,7 +72,6 @@ SOURCES_C = \ progress-c.c \ unix_utils-c.c \ uri-c.c \ - visit-c.c \ xml-c.c if HAVE_OCAML @@ -104,7 +99,6 @@ libmllib_a_CPPFLAGS = \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ - -I$(top_srcdir)/common/visit \ -I$(top_srcdir)/common/options \ -I$(top_srcdir)/common/progress libmllib_a_CFLAGS = \ @@ -187,10 +181,6 @@ JSON_tests_SOURCES = dummy.c JSON_tests_BOBJECTS = JSON_tests.cmo JSON_tests_XOBJECTS = $(JSON_tests_BOBJECTS:.cmo=.cmx) -visit_tests_SOURCES = dummy.c -visit_tests_BOBJECTS = visit_tests.cmo -visit_tests_XOBJECTS = $(visit_tests_BOBJECTS:.cmo=.cmx) - # Can't call the following as <test>_OBJECTS because automake gets confused. if !HAVE_OCAMLOPT common_utils_tests_THEOBJECTS = $(common_utils_tests_BOBJECTS) @@ -210,9 +200,6 @@ getopt_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) JSON_tests_THEOBJECTS = $(JSON_tests_XOBJECTS) JSON_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) - -visit_tests_THEOBJECTS = $(visit_tests_XOBJECTS) -visit_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) endif OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) @@ -247,16 +234,6 @@ JSON_tests_LINK = \ $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ $(JSON_tests_THEOBJECTS) -o $@ -visit_tests_DEPENDENCIES = \ - $(visit_tests_THEOBJECTS) \ - $(MLLIB_CMA) \ - $(top_srcdir)/ocaml-link.sh -visit_tests_LINK = \ - $(top_srcdir)/ocaml-link.sh -cclib '-lutils $(LIBXML2_LIBS) -lgnu' -- \ - $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ - $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ - $(visit_tests_THEOBJECTS) -o $@ - TESTS_ENVIRONMENT = $(top_builddir)/run --test TESTS = \ @@ -269,11 +246,6 @@ check_PROGRAMS += common_utils_tests JSON_tests TESTS += common_utils_tests JSON_tests endif -if ENABLE_APPLIANCE -check_PROGRAMS += visit_tests -TESTS += visit_tests -endif - check-valgrind: $(MAKE) VG="@VG@" check diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index 2a1ca25fd..68cb1814a 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -111,6 +111,8 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/visit/.libs \ + -I $(top_builddir)/common/mlvisit \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize if HAVE_OCAML_PKG_GETTEXT @@ -118,6 +120,7 @@ OCAMLPACKAGES += -package gettext-stub endif OCAMLCLIBS = \ + -lvisit \ -lutils \ $(LIBTINFO_LIBS) \ $(LIBCRYPT_LIBS) \ @@ -133,7 +136,12 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) customize.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + mlvisit.$(MLARCHIVE) \ + customize.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_sysprep_DEPENDENCIES = \ $(OBJECTS) \ -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 02/41] mllib: Move Progress OCaml bindings to common/mlprogress.
The ‘Progress’ module is a self-contained library with the only dependencies being: - the C ‘progress’ implementation Move it to a separate ‘common/mlprogress’ directory. This change is pure code refactoring. --- .gitignore | 1 + Makefile.am | 1 + common/mlprogress/Makefile.am | 111 ++++++++++++++++++++++++++++++ {mllib => common/mlprogress}/progress-c.c | 0 {mllib => common/mlprogress}/progress.ml | 0 {mllib => common/mlprogress}/progress.mli | 0 configure.ac | 1 + docs/C_SOURCE_FILES | 2 +- docs/guestfs-hacking.pod | 4 ++ mllib/Makefile.am | 7 +- resize/Makefile.am | 9 ++- sparsify/Makefile.am | 9 ++- 12 files changed, 136 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 2367cddcb..ea245c49d 100644 --- a/.gitignore +++ b/.gitignore @@ -124,6 +124,7 @@ Makefile.in /common/errnostring/errnostring-gperf.gperf /common/errnostring/errnostring.h /common/miniexpect/miniexpect.3 +/common/mlprogress/.depend /common/mlvisit/.depend /common/mlvisit/visit_tests /common/protocol/guestfs_protocol.c diff --git a/Makefile.am b/Makefile.am index 499a1d279..bd0fc94e7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -154,6 +154,7 @@ SUBDIRS += csharp # shared code used by other OCaml tools, so these must come first. if HAVE_OCAML SUBDIRS += \ + common/mlprogress \ common/mlvisit \ mllib \ customize \ diff --git a/common/mlprogress/Makefile.am b/common/mlprogress/Makefile.am new file mode 100644 index 000000000..d4a229451 --- /dev/null +++ b/common/mlprogress/Makefile.am @@ -0,0 +1,111 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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. + +include $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + $(SOURCES_C) + +SOURCES_MLI = \ + progress.mli + +SOURCES_ML = \ + progress.ml + +SOURCES_C = \ + progress-c.c + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlprogress.a + +if !HAVE_OCAMLOPT +MLPROGRESS_CMA = mlprogress.cma +else +MLPROGRESS_CMA = mlprogress.cmxa +endif + +noinst_DATA = $(MLPROGRESS_CMA) + +libmlprogress_a_SOURCES = $(SOURCES_C) +libmlprogress_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/common/utils \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/common/progress +libmlprogress_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(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)/lib/.libs \ + -I $(top_builddir)/gnulib/lib/.libs \ + -I $(top_builddir)/ocaml \ + -I $(builddir) + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlprogress_a_DEPENDENCIES = $(OBJECTS) + +$(MLPROGRESS_CMA): $(OBJECTS) libmlprogress.a + $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlprogress_a_OBJECTS) \ + -cclib -lprogress \ + -o mlprogress + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/mllib/progress-c.c b/common/mlprogress/progress-c.c similarity index 100% rename from mllib/progress-c.c rename to common/mlprogress/progress-c.c diff --git a/mllib/progress.ml b/common/mlprogress/progress.ml similarity index 100% rename from mllib/progress.ml rename to common/mlprogress/progress.ml diff --git a/mllib/progress.mli b/common/mlprogress/progress.mli similarity index 100% rename from mllib/progress.mli rename to common/mlprogress/progress.mli diff --git a/configure.ac b/configure.ac index 4fc226123..1abb72671 100644 --- a/configure.ac +++ b/configure.ac @@ -185,6 +185,7 @@ AC_CONFIG_FILES([Makefile common/errnostring/Makefile common/edit/Makefile common/miniexpect/Makefile + common/mlprogress/Makefile common/mlvisit/Makefile common/options/Makefile common/parallel/Makefile diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 578126403..1e2a99e7e 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -15,6 +15,7 @@ common/edit/file-edit.c common/edit/file-edit.h common/miniexpect/miniexpect.c common/miniexpect/miniexpect.h +common/mlprogress/progress-c.c common/mlvisit/dummy.c common/mlvisit/visit-c.c common/options/config.c @@ -339,7 +340,6 @@ make-fs/make-fs.c mllib/common_utils-c.c mllib/dummy.c mllib/getopt-c.c -mllib/progress-c.c mllib/unix_utils-c.c mllib/uri-c.c mllib/xml-c.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index ac6d1ccbf..f9cb88f05 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -100,6 +100,10 @@ A copy of the miniexpect library from L<http://git.annexia.org/?p=miniexpect.git;a=summary>. This is used in virt-p2v. +=item F<common/mlprogress> + +OCaml bindings for the progress bar functions (see F<common/progress>). + =item F<common/mlvisit> OCaml bindings for the visit functions (see F<common/visit>). diff --git a/mllib/Makefile.am b/mllib/Makefile.am index cb79f50f5..42f450323 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -36,7 +36,6 @@ SOURCES_MLI = \ getopt.mli \ JSON.mli \ planner.mli \ - progress.mli \ regedit.mli \ registry.mli \ stringMap.mli \ @@ -51,7 +50,6 @@ SOURCES_ML = \ getopt.ml \ unix_utils.ml \ common_utils.ml \ - progress.ml \ URI.ml \ planner.ml \ registry.ml \ @@ -66,10 +64,8 @@ SOURCES_C = \ ../common/options/decrypt.c \ ../common/options/keys.c \ ../common/options/uri.c \ - ../common/progress/progress.c \ common_utils-c.c \ getopt-c.c \ - progress-c.c \ unix_utils-c.c \ uri-c.c \ xml-c.c @@ -99,8 +95,7 @@ libmllib_a_CPPFLAGS = \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ - -I$(top_srcdir)/common/options \ - -I$(top_srcdir)/common/progress + -I$(top_srcdir)/common/options libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ diff --git a/resize/Makefile.am b/resize/Makefile.am index e097dd611..c35c3a78a 100644 --- a/resize/Makefile.am +++ b/resize/Makefile.am @@ -57,15 +57,18 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/progress/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlprogress \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub endif OCAMLCLIBS = \ + -lprogress \ -lutils \ $(LIBTINFO_LIBS) \ $(LIBXML2_LIBS) \ @@ -80,7 +83,11 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlguestfs.$(MLARCHIVE) \ + mlprogress.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_resize_DEPENDENCIES = \ $(OBJECTS) \ diff --git a/sparsify/Makefile.am b/sparsify/Makefile.am index b7229ffdd..97236829e 100644 --- a/sparsify/Makefile.am +++ b/sparsify/Makefile.am @@ -62,15 +62,18 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/progress/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlprogress \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub endif OCAMLCLIBS = \ + -lprogress \ -lutils \ $(LIBTINFO_LIBS) \ $(LIBXML2_LIBS) \ @@ -85,7 +88,11 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlguestfs.$(MLARCHIVE) \ + mlprogress.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_sparsify_DEPENDENCIES = \ $(OBJECTS) \ -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 03/41] mllib: Move Xml (libxml2) OCaml bindings to common/mlxml.
The ‘Xml’ module is a self-contained library of bindings for libxml2, with no other dependencies. Move it to a separate ‘common/mlxml’ directory. This is not pure refactoring. For unclear reasons, the previous version of ‘Xml.parse_file’ read the whole file into memory and then called ‘xmlReadMemory’. This was quite inefficient, and unnecessary because we could use ‘xmlReadFile’ to read and parse the file efficiently. Changing the code to use ‘xmlReadFile’ also removes the unnecessary dependency on ‘Common_utils.read_whole_file’. --- .gitignore | 1 + Makefile.am | 1 + common/mlxml/Makefile.am | 107 ++++++++++++++++++++++++++++++++++++++++ {mllib => common/mlxml}/xml-c.c | 21 ++++++++ {mllib => common/mlxml}/xml.ml | 8 +-- {mllib => common/mlxml}/xml.mli | 0 configure.ac | 1 + docs/C_SOURCE_FILES | 2 +- docs/guestfs-hacking.pod | 4 ++ mllib/Makefile.am | 13 +++-- v2v/Makefile.am | 11 ++++- v2v/test-harness/Makefile.am | 3 +- 12 files changed, 158 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index ea245c49d..a82a1f674 100644 --- a/.gitignore +++ b/.gitignore @@ -127,6 +127,7 @@ Makefile.in /common/mlprogress/.depend /common/mlvisit/.depend /common/mlvisit/visit_tests +/common/mlxml/.depend /common/protocol/guestfs_protocol.c /common/protocol/guestfs_protocol.h /common/protocol/guestfs_protocol.x diff --git a/Makefile.am b/Makefile.am index bd0fc94e7..48f538475 100644 --- a/Makefile.am +++ b/Makefile.am @@ -156,6 +156,7 @@ if HAVE_OCAML SUBDIRS += \ common/mlprogress \ common/mlvisit \ + common/mlxml \ mllib \ customize \ builder builder/templates \ diff --git a/common/mlxml/Makefile.am b/common/mlxml/Makefile.am new file mode 100644 index 000000000..1a989949f --- /dev/null +++ b/common/mlxml/Makefile.am @@ -0,0 +1,107 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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. + +include $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + $(SOURCES_C) + +SOURCES_MLI = \ + xml.mli + +SOURCES_ML = \ + xml.ml + +SOURCES_C = \ + xml-c.c + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlxml.a + +if !HAVE_OCAMLOPT +MLXML_CMA = mlxml.cma +else +MLXML_CMA = mlxml.cmxa +endif + +noinst_DATA = $(MLXML_CMA) + +libmlxml_a_SOURCES = $(SOURCES_C) +libmlxml_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(shell $(OCAMLC) -where) +libmlxml_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(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)/lib/.libs \ + -I $(top_builddir)/gnulib/lib/.libs \ + -I $(builddir) + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlxml_a_DEPENDENCIES = $(OBJECTS) + +$(MLXML_CMA): $(OBJECTS) libmlxml.a + $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlxml_a_OBJECTS) \ + -cclib '$(LIBXML2_LIBS)' \ + -o mlxml + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/mllib/xml-c.c b/common/mlxml/xml-c.c similarity index 95% rename from mllib/xml-c.c rename to common/mlxml/xml-c.c index d3e893076..92388d940 100644 --- a/mllib/xml-c.c +++ b/common/mlxml/xml-c.c @@ -129,6 +129,27 @@ mllib_xml_parse_memory (value xmlv) } value +mllib_xml_parse_file (value filenamev) +{ + CAMLparam1 (filenamev); + CAMLlocal1 (docv); + xmlDocPtr doc; + + /* For security reasons, call xmlReadFile (not xmlParseFile) and + * pass XML_PARSE_NONET. See commit 845daded5fddc70f. + */ + doc = xmlReadFile (String_val (filenamev), NULL, XML_PARSE_NONET); + if (doc == NULL) + caml_invalid_argument ("parse_file: unable to parse XML from file"); + + docv = caml_alloc_custom (&docptr_custom_operations, sizeof (xmlDocPtr), + 0, 1); + docptr_val (docv) = doc; + + CAMLreturn (docv); +} + +value mllib_xml_copy_doc (value docv, value recursivev) { CAMLparam2 (docv, recursivev); diff --git a/mllib/xml.ml b/common/mlxml/xml.ml similarity index 97% rename from mllib/xml.ml rename to common/mlxml/xml.ml index 78e75b8f2..5ccf42ddd 100644 --- a/mllib/xml.ml +++ b/common/mlxml/xml.ml @@ -67,9 +67,11 @@ let parse_memory xml Gc.finalise free_docptr docptr; docptr -let parse_file file - let xml = Common_utils.read_whole_file file in - parse_memory xml +external _parse_file : string -> docptr = "mllib_xml_parse_file" +let parse_file filename + let docptr = _parse_file filename in + Gc.finalise free_docptr docptr; + docptr external _copy_doc : docptr -> recursive:bool -> docptr = "mllib_xml_copy_doc" let copy_doc docptr ~recursive diff --git a/mllib/xml.mli b/common/mlxml/xml.mli similarity index 100% rename from mllib/xml.mli rename to common/mlxml/xml.mli diff --git a/configure.ac b/configure.ac index 1abb72671..7d0f0a1dd 100644 --- a/configure.ac +++ b/configure.ac @@ -187,6 +187,7 @@ AC_CONFIG_FILES([Makefile common/miniexpect/Makefile common/mlprogress/Makefile common/mlvisit/Makefile + common/mlxml/Makefile common/options/Makefile common/parallel/Makefile common/progress/Makefile diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 1e2a99e7e..f6ac73047 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -18,6 +18,7 @@ common/miniexpect/miniexpect.h common/mlprogress/progress-c.c common/mlvisit/dummy.c common/mlvisit/visit-c.c +common/mlxml/xml-c.c common/options/config.c common/options/decrypt.c common/options/display-options.c @@ -342,7 +343,6 @@ mllib/dummy.c mllib/getopt-c.c mllib/unix_utils-c.c mllib/uri-c.c -mllib/xml-c.c ocaml/guestfs-c-actions.c ocaml/guestfs-c-errnos.c ocaml/guestfs-c.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index f9cb88f05..1ff496381 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -108,6 +108,10 @@ OCaml bindings for the progress bar functions (see F<common/progress>). OCaml bindings for the visit functions (see F<common/visit>). +=item F<common/mlxml> + +OCaml bindings for the libxml2 library. + =item F<common/options> Common options parsing for guestfish, guestmount and some virt tools. diff --git a/mllib/Makefile.am b/mllib/Makefile.am index 42f450323..c84f5f36d 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -27,8 +27,6 @@ EXTRA_DIST = \ test-getopt.sh SOURCES_MLI = \ - xml.mli \ - xpath_helpers.mli \ checksums.mli \ unix_utils.mli \ common_utils.mli \ @@ -39,7 +37,8 @@ SOURCES_MLI = \ regedit.mli \ registry.mli \ stringMap.mli \ - URI.mli + URI.mli \ + xpath_helpers.mli SOURCES_ML = \ guestfs_config.ml \ @@ -57,7 +56,6 @@ SOURCES_ML = \ JSON.ml \ curl.ml \ checksums.ml \ - xml.ml \ xpath_helpers.ml SOURCES_C = \ @@ -67,8 +65,7 @@ SOURCES_C = \ common_utils-c.c \ getopt-c.c \ unix_utils-c.c \ - uri-c.c \ - xml-c.c + uri-c.c if HAVE_OCAML @@ -95,7 +92,8 @@ libmllib_a_CPPFLAGS = \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ - -I$(top_srcdir)/common/options + -I$(top_srcdir)/common/options \ + -I$(top_srcdir)/common/mlxml libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ @@ -113,6 +111,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlxml \ -I $(builddir) OCAMLPACKAGES_TESTS = $(MLLIB_CMA) if HAVE_OCAML_PKG_GETTEXT diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 5b6618adf..2de99ceb9 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -146,6 +146,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize if HAVE_OCAML_PKG_GETTEXT @@ -168,7 +169,11 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlguestfs.$(MLARCHIVE) \ + mlxml.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_v2v_DEPENDENCIES = $(OBJECTS) $(top_srcdir)/ocaml-link.sh virt_v2v_LINK = \ @@ -205,6 +210,7 @@ endif virt_v2v_copy_to_local_DEPENDENCIES = \ $(COPY_TO_LOCAL_OBJECTS) \ + ../common/mlxml/mlxml.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_v2v_copy_to_local_LINK = \ @@ -489,6 +495,7 @@ endif v2v_unit_tests_DEPENDENCIES = \ $(v2v_unit_tests_THEOBJECTS) \ + ../common/mlxml/mlxml.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh v2v_unit_tests_LINK = \ @@ -503,7 +510,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/test-harness/Makefile.am b/v2v/test-harness/Makefile.am index 8ce441222..9a548022a 100644 --- a/v2v/test-harness/Makefile.am +++ b/v2v/test-harness/Makefile.am @@ -42,6 +42,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/v2v @@ -128,7 +129,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize -I $(abs_top_builddir)/v2v $^ | \ + $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize -I $(abs_top_builddir)/v2v $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 04/41] mllib: Split ‘Common_utils’ into ‘Std_utils’ + ‘Common_utils’.
The new module ‘Std_utils’ contains only functions which are pure OCaml and depend only on the OCaml stdlib. Therefore these functions may be used by the generator. The new module is moved to ‘common/mlstdutils’. This also removes the "<stdlib>" hack, and the code which copied the library around. Also ‘Guestfs_config’, ‘Libdir’ and ‘StringMap’ modules are moved since these are essentially the same. The bulk of this change is just updating files which use ‘open Common_utils’ to add ‘open Std_utils’ where necessary. --- .gitignore | 8 +- Makefile.am | 2 +- builder/Makefile.am | 12 +- builder/builder.ml | 1 + builder/cache.ml | 3 +- builder/cmdline.ml | 3 +- builder/downloader.ml | 3 +- builder/index.ml | 3 +- builder/index_parser.ml | 3 +- builder/ini_reader.ml | 1 + builder/languages.ml | 1 + builder/list_entries.ml | 3 +- builder/paths.ml | 1 + builder/sigchecker.ml | 3 +- builder/simplestreams_parser.ml | 3 +- builder/sources.ml | 3 +- builder/yajl.ml | 3 +- common/mlstdutils/Makefile.am | 151 +++++ common/mlstdutils/dummy.c | 2 + {mllib => common/mlstdutils}/guestfs_config.ml.in | 0 common/mlstdutils/std_utils.ml | 664 +++++++++++++++++++++ common/mlstdutils/std_utils.mli | 338 +++++++++++ common/mlstdutils/std_utils_tests.ml | 95 +++ {mllib => common/mlstdutils}/stringMap.ml | 0 {mllib => common/mlstdutils}/stringMap.mli | 0 configure.ac | 3 +- customize/Makefile.am | 8 +- customize/SELinux_relabel.ml | 3 +- customize/append_line.ml | 1 + customize/customize_main.ml | 3 +- customize/customize_run.ml | 3 +- customize/firstboot.ml | 1 + customize/hostname.ml | 1 + customize/password.ml | 3 +- customize/perl_edit.ml | 1 + customize/ssh_key.ml | 7 +- customize/subscription_manager.ml | 3 +- dib/Makefile.am | 10 +- dib/cmdline.ml | 3 +- dib/dib.ml | 3 +- dib/elements.ml | 3 +- dib/output_format.ml | 1 + dib/output_format_qcow2.ml | 1 + dib/utils.ml | 3 +- docs/C_SOURCE_FILES | 1 + docs/guestfs-hacking.pod | 4 + generator/GObject.ml | 2 +- generator/Makefile.am | 34 +- generator/OCaml.ml | 2 +- generator/UEFI.ml | 2 +- generator/XDR.ml | 2 +- generator/actions.ml | 2 +- generator/authors.ml | 2 +- generator/bindtests.ml | 2 +- generator/c.ml | 2 +- generator/checks.ml | 2 +- generator/csharp.ml | 2 +- generator/customize.ml | 3 +- generator/daemon.ml | 2 +- generator/docstrings.ml | 2 +- generator/erlang.ml | 2 +- generator/errnostring.ml | 2 +- generator/events.ml | 2 +- generator/fish.ml | 2 +- generator/golang.ml | 2 +- generator/haskell.ml | 2 +- generator/java.ml | 2 +- generator/lua.ml | 2 +- generator/main.ml | 2 +- generator/optgroups.ml | 2 +- generator/perl.ml | 2 +- generator/php.ml | 2 +- generator/pr.ml | 2 +- generator/python.ml | 2 +- generator/ruby.ml | 2 +- generator/structs.ml | 2 +- generator/tests_c_api.ml | 2 +- generator/utils.ml | 2 +- get-kernel/Makefile.am | 10 +- get-kernel/get_kernel.ml | 3 +- mllib/Makefile.am | 25 +- mllib/checksums.ml | 3 +- mllib/common_utils.ml | 676 +--------------------- mllib/common_utils.mli | 347 ----------- mllib/common_utils_tests.ml | 62 +- mllib/curl.ml | 1 + mllib/getopt_tests.ml | 1 + mllib/regedit.ml | 1 + mllib/registry.ml | 3 +- mllib/xpath_helpers.ml | 3 +- resize/Makefile.am | 5 +- resize/resize.ml | 1 + sparsify/Makefile.am | 5 +- sparsify/cmdline.ml | 3 +- sparsify/copying.ml | 1 + sparsify/in_place.ml | 1 + sparsify/utils.ml | 2 +- sysprep/Makefile.am | 5 +- sysprep/main.ml | 1 + sysprep/sysprep_operation.ml | 4 +- sysprep/sysprep_operation_backup_files.ml | 3 +- sysprep/sysprep_operation_cron_spool.ml | 6 +- sysprep/sysprep_operation_net_hostname.ml | 4 +- sysprep/sysprep_operation_net_hwaddr.ml | 4 +- sysprep/sysprep_operation_script.ml | 3 +- sysprep/sysprep_operation_user_account.ml | 1 + v2v/DOM.ml | 1 + v2v/Makefile.am | 6 +- v2v/changeuid.ml | 3 +- v2v/cmdline.ml | 3 +- v2v/convert_linux.ml | 3 +- v2v/convert_windows.ml | 3 +- v2v/copy_to_local.ml | 3 +- v2v/create_libvirt_xml.ml | 3 +- v2v/create_ovf.ml | 7 +- v2v/input_disk.ml | 3 +- v2v/input_libvirtxml.ml | 3 +- v2v/input_ova.ml | 3 +- v2v/input_vmx.ml | 3 +- v2v/inspect_source.ml | 1 + v2v/linux.ml | 3 +- v2v/linux_bootloaders.ml | 3 +- v2v/linux_kernels.ml | 3 +- v2v/modules_list.ml | 2 +- v2v/output_glance.ml | 3 +- v2v/output_libvirt.ml | 3 +- v2v/output_local.ml | 3 +- v2v/output_null.ml | 3 +- v2v/output_qemu.ml | 3 +- v2v/output_rhv.ml | 3 +- v2v/output_vdsm.ml | 3 +- v2v/parse_libvirt_xml.ml | 7 +- v2v/parse_ovf_from_ova.ml | 3 +- v2v/parse_vmx.ml | 1 + v2v/target_bus_assignment.ml | 1 + v2v/test-harness/Makefile.am | 3 +- v2v/test-harness/v2v_test_harness.ml | 1 + v2v/utils.ml | 3 +- v2v/v2v.ml | 3 +- v2v/v2v_unit_tests.ml | 8 +- v2v/vCenter.ml | 1 + v2v/windows_virtio.ml | 3 +- 142 files changed, 1526 insertions(+), 1238 deletions(-) diff --git a/.gitignore b/.gitignore index a82a1f674..991b1ab94 100644 --- a/.gitignore +++ b/.gitignore @@ -125,6 +125,11 @@ Makefile.in /common/errnostring/errnostring.h /common/miniexpect/miniexpect.3 /common/mlprogress/.depend +/common/mlstdutils/.depend +/common/mlstdutils/guestfs_config.ml +/common/mlstdutils/libdir.ml +/common/mlstdutils/oUnit-* +/common/mlstdutils/std_utils_tests /common/mlvisit/.depend /common/mlvisit/visit_tests /common/mlxml/.depend @@ -276,7 +281,6 @@ Makefile.in /generator/common_utils.mli /generator/files-generated.txt /generator/generator -/generator/guestfs_config.ml /generator/.pod2text.data* /generator/stamp-generator /get-kernel/.depend @@ -366,9 +370,7 @@ Makefile.in /mllib/common_utils_tests /mllib/dummy /mllib/getopt_tests -/mllib/guestfs_config.ml /mllib/JSON_tests -/mllib/libdir.ml /mllib/oUnit-* /ocaml/bindtests.bc /ocaml/bindtests.opt diff --git a/Makefile.am b/Makefile.am index 48f538475..64ac23f2e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,7 @@ include $(top_srcdir)/common-rules.mk ACLOCAL_AMFLAGS = -I m4 # The generator - must be before anything else. -SUBDIRS = generator +SUBDIRS = common/mlstdutils generator # Must be the first tests that run. if ENABLE_APPLIANCE diff --git a/builder/Makefile.am b/builder/Makefile.am index d56b394b7..5f0606ca4 100644 --- a/builder/Makefile.am +++ b/builder/Makefile.am @@ -124,6 +124,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize OCAMLPACKAGES_TESTS @@ -153,10 +154,16 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) customize.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + customize.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_builder_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -228,6 +235,7 @@ endif yajl_tests_DEPENDENCIES = \ $(yajl_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -299,7 +307,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/builder/builder.ml b/builder/builder.ml index b0a48ea89..0e02bab3b 100644 --- a/builder/builder.ml +++ b/builder/builder.ml @@ -20,6 +20,7 @@ open Common_gettext.Gettext module G = Guestfs +open Std_utils open Common_utils open Unix_utils open Password diff --git a/builder/cache.ml b/builder/cache.ml index 19fcd15e2..494796edb 100644 --- a/builder/cache.ml +++ b/builder/cache.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/builder/cmdline.ml b/builder/cmdline.ml index f20c0936c..a1f901144 100644 --- a/builder/cmdline.ml +++ b/builder/cmdline.ml @@ -18,8 +18,9 @@ (* Command line argument parsing. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Customize_cmdline diff --git a/builder/downloader.ml b/builder/downloader.ml index ef3cd67cb..d6b27c8c7 100644 --- a/builder/downloader.ml +++ b/builder/downloader.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/builder/index.ml b/builder/index.ml index 8c59de651..54af6e719 100644 --- a/builder/index.ml +++ b/builder/index.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/builder/index_parser.ml b/builder/index_parser.ml index 468805cf8..fb546831f 100644 --- a/builder/index_parser.ml +++ b/builder/index_parser.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/builder/ini_reader.ml b/builder/ini_reader.ml index 0470d173d..2d8ff7e59 100644 --- a/builder/ini_reader.ml +++ b/builder/ini_reader.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils type sections = section list diff --git a/builder/languages.ml b/builder/languages.ml index 66f49cb06..d94f97c5c 100644 --- a/builder/languages.ml +++ b/builder/languages.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils let split_locale loc diff --git a/builder/list_entries.ml b/builder/list_entries.ml index 2a1aef4c8..ea607107c 100644 --- a/builder/list_entries.ml +++ b/builder/list_entries.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/builder/paths.ml b/builder/paths.ml index cbd9d4bd0..e0fb9a024 100644 --- a/builder/paths.ml +++ b/builder/paths.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils let xdg_cache_home diff --git a/builder/sigchecker.ml b/builder/sigchecker.ml index 6c1e691ee..f72c21ab0 100644 --- a/builder/sigchecker.ml +++ b/builder/sigchecker.ml @@ -16,9 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Utils diff --git a/builder/simplestreams_parser.ml b/builder/simplestreams_parser.ml index 8844d476b..c550675ba 100644 --- a/builder/simplestreams_parser.ml +++ b/builder/simplestreams_parser.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Yajl open Utils diff --git a/builder/sources.ml b/builder/sources.ml index 290151c3a..4c9ea0fff 100644 --- a/builder/sources.ml +++ b/builder/sources.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf open Unix diff --git a/builder/yajl.ml b/builder/yajl.ml index d933b5246..5ae1c5d9b 100644 --- a/builder/yajl.ml +++ b/builder/yajl.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext type yajl_val | Yajl_null diff --git a/common/mlstdutils/Makefile.am b/common/mlstdutils/Makefile.am new file mode 100644 index 000000000..9e0b34d42 --- /dev/null +++ b/common/mlstdutils/Makefile.am @@ -0,0 +1,151 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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. + +include $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + std_utils_tests.ml + +SOURCES_MLI = \ + std_utils.mli \ + stringMap.mli + +SOURCES_ML = \ + guestfs_config.ml \ + $(OCAML_BYTES_COMPAT_ML) \ + libdir.ml \ + stringMap.ml \ + std_utils.ml + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlstdutils.a + +if !HAVE_OCAMLOPT +MLSTDUTILS_CMA = mlstdutils.cma +else +MLSTDUTILS_CMA = mlstdutils.cmxa +endif + +noinst_DATA = $(MLSTDUTILS_CMA) + +libmlstdutils_a_SOURCES = dummy.c +libmlstdutils_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) +libmlstdutils_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(BOBJECTS:.cmo=.cmx) + +OCAMLPACKAGES = \ + -package str,unix \ + -I $(builddir) +OCAMLPACKAGES_TESTS = $(MLSTDUTILS_CMA) +if HAVE_OCAML_PKG_OUNIT +OCAMLPACKAGES_TESTS += -package oUnit +endif + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlstdutils_a_DEPENDENCIES = $(OBJECTS) + +$(MLSTDUTILS_CMA): $(OBJECTS) + $(OCAMLFIND) mklib $(OCAMLPACKAGES) $(OBJECTS) -o mlstdutils + +# This OCaml module has to be generated by make (configure will put +# unexpanded prefix macro in). + +libdir.ml: Makefile + echo 'let libdir = "$(libdir)"' > $@-t + mv $@-t $@ + +# Tests. + +std_utils_tests_SOURCES = dummy.c +std_utils_tests_CPPFLAGS = \ + -I. \ + -I$(top_builddir) +std_utils_tests_BOBJECTS = std_utils_tests.cmo +std_utils_tests_XOBJECTS = $(std_utils_tests_BOBJECTS:.cmo=.cmx) + +# Can't call the following as <test>_OBJECTS because automake gets confused. +if !HAVE_OCAMLOPT +std_utils_tests_THEOBJECTS = $(std_utils_tests_BOBJECTS) +std_utils_tests.cmo: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) +else +std_utils_tests_THEOBJECTS = $(std_utils_tests_XOBJECTS) +std_utils_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) +endif + +OCAMLLINKFLAGS = $(LINK_CUSTOM_OCAMLC_ONLY) + +std_utils_tests_DEPENDENCIES = \ + $(std_utils_tests_THEOBJECTS) \ + $(MLSTDUTILS_CMA) \ + $(top_srcdir)/ocaml-link.sh +std_utils_tests_LINK = \ + $(top_srcdir)/ocaml-link.sh -- \ + $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ + $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ + $(std_utils_tests_THEOBJECTS) -o $@ + +TESTS_ENVIRONMENT = $(top_builddir)/run --test + +TESTS +check_PROGRAMS + +if HAVE_OCAML_PKG_OUNIT +check_PROGRAMS += std_utils_tests +TESTS += std_utils_tests +endif + +check-valgrind: + $(MAKE) VG="@VG@" check + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/common/mlstdutils/dummy.c b/common/mlstdutils/dummy.c new file mode 100644 index 000000000..ebab6198c --- /dev/null +++ b/common/mlstdutils/dummy.c @@ -0,0 +1,2 @@ +/* Dummy source, to be used for OCaml-based tools with no C sources. */ +enum { foo = 1 }; diff --git a/mllib/guestfs_config.ml.in b/common/mlstdutils/guestfs_config.ml.in similarity index 100% rename from mllib/guestfs_config.ml.in rename to common/mlstdutils/guestfs_config.ml.in diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml new file mode 100644 index 000000000..7b8d65f66 --- /dev/null +++ b/common/mlstdutils/std_utils.ml @@ -0,0 +1,664 @@ +(* Common utilities for OCaml tools in libguestfs. + * Copyright (C) 2010-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 Printf + +module Char = struct + include Char + + let lowercase_ascii c + if (c >= 'A' && c <= 'Z') + then unsafe_chr (code c + 32) + else c + + let uppercase_ascii c + if (c >= 'a' && c <= 'z') + then unsafe_chr (code c - 32) + else c + + let isspace c + c = ' ' + (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *) + + let isdigit = function + | '0'..'9' -> true + | _ -> false + + let isxdigit = function + | '0'..'9' -> true + | 'a'..'f' -> true + | 'A'..'F' -> true + | _ -> false + + let isalpha = function + | 'a'..'z' -> true + | 'A'..'Z' -> true + | _ -> false + + let isalnum = function + | '0'..'9' -> true + | 'a'..'z' -> true + | 'A'..'Z' -> true + | _ -> false + + let hexdigit = function + | '0' -> 0 + | '1' -> 1 + | '2' -> 2 + | '3' -> 3 + | '4' -> 4 + | '5' -> 5 + | '6' -> 6 + | '7' -> 7 + | '8' -> 8 + | '9' -> 9 + | 'a' | 'A' -> 10 + | 'b' | 'B' -> 11 + | 'c' | 'C' -> 12 + | 'd' | 'D' -> 13 + | 'e' | 'E' -> 14 + | 'f' | 'F' -> 15 + | _ -> -1 +end + +module String = struct + include String + + let map f s + let len = String.length s in + let b = Bytes.create len in + for i = 0 to len-1 do + Bytes.unsafe_set b i (f (unsafe_get s i)) + done; + Bytes.to_string b + + let lowercase_ascii s = map Char.lowercase_ascii s + let uppercase_ascii s = map Char.uppercase_ascii s + + let capitalize_ascii s + let b = Bytes.of_string s in + Bytes.unsafe_set b 0 (Char.uppercase_ascii (Bytes.unsafe_get b 0)); + Bytes.to_string b + + let is_prefix str prefix + let n = length prefix in + length str >= n && sub str 0 n = prefix + + let is_suffix str suffix + let sufflen = length suffix + and len = length str in + len >= sufflen && sub str (len - sufflen) sufflen = suffix + + let rec find s sub + let len = length s in + let sublen = length sub in + let rec loop i + if i <= len-sublen then ( + let rec loop2 j + if j < sublen then ( + if s.[i+j] = sub.[j] then loop2 (j+1) + else -1 + ) else + i (* found *) + in + let r = loop2 0 in + if r = -1 then loop (i+1) else r + ) else + -1 (* not found *) + in + loop 0 + + let rec replace s s1 s2 + let len = length s in + let sublen = length s1 in + let i = find s s1 in + if i = -1 then s + else ( + let s' = sub s 0 i in + let s'' = sub s (i+sublen) (len-i-sublen) in + s' ^ s2 ^ replace s'' s1 s2 + ) + + let replace_char s c1 c2 + let b2 = Bytes.of_string s in + let r = ref false in + for i = 0 to Bytes.length b2 - 1 do + if Bytes.unsafe_get b2 i = c1 then ( + Bytes.unsafe_set b2 i c2; + r := true + ) + done; + if not !r then s else Bytes.to_string b2 + + let rec nsplit sep str + let len = length str in + let seplen = length sep in + let i = find str sep in + if i = -1 then [str] + else ( + let s' = sub str 0 i in + let s'' = sub str (i+seplen) (len-i-seplen) in + s' :: nsplit sep s'' + ) + + let split sep str + let len = length sep in + let seplen = length str in + let i = find str sep in + if i = -1 then str, "" + else ( + sub str 0 i, sub str (i + len) (seplen - i - len) + ) + + let rec lines_split str + let buf = Buffer.create 16 in + let len = length str in + let rec loop start len + try + let i = index_from str start '\n' in + if i > 0 && str.[i-1] = '\\' then ( + Buffer.add_substring buf str start (i-start-1); + Buffer.add_char buf '\n'; + loop (i+1) len + ) else ( + Buffer.add_substring buf str start (i-start); + i+1 + ) + with Not_found -> + if len > 0 && str.[len-1] = '\\' then ( + Buffer.add_substring buf str start (len-start-1); + Buffer.add_char buf '\n' + ) else + Buffer.add_substring buf str start (len-start); + len+1 + in + let endi = loop 0 len in + let line = Buffer.contents buf in + if endi > len then + [line] + else + line :: lines_split (sub str endi (len-endi)) + + let random8 + let chars = "abcdefghijklmnopqrstuvwxyz0123456789" in + fun () -> + concat "" ( + List.map ( + fun _ -> + let c = Random.int 36 in + let c = chars.[c] in + make 1 c + ) [1;2;3;4;5;6;7;8] + ) + + let triml ?(test = Char.isspace) str + let i = ref 0 in + let n = ref (String.length str) in + while !n > 0 && test str.[!i]; do + decr n; + incr i + done; + if !i = 0 then str + else String.sub str !i !n + + let trimr ?(test = Char.isspace) str + let n = ref (String.length str) in + while !n > 0 && test str.[!n-1]; do + decr n + done; + if !n = String.length str then str + else String.sub str 0 !n + + let trim ?(test = Char.isspace) str + trimr ~test (triml ~test str) + + let count_chars c str + let count = ref 0 in + for i = 0 to String.length str - 1 do + if c = String.unsafe_get str i then incr count + done; + !count + + let explode str + let r = ref [] in + for i = 0 to String.length str - 1 do + let c = String.unsafe_get str i in + r := c :: !r; + done; + List.rev !r + + let map_chars f str + List.map f (explode str) + + let spaces n = String.make n ' ' +end + +let (//) = Filename.concat +let quote = Filename.quote + +let subdirectory 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 ( +^ ) = Int64.add +let ( -^ ) = Int64.sub +let ( *^ ) = Int64.mul +let ( /^ ) = Int64.div +let ( &^ ) = Int64.logand +let ( ~^ ) = Int64.lognot + +external identity : 'a -> 'a = "%identity" + +let roundup64 i a = let a = a -^ 1L in (i +^ a) &^ (~^ a) +let div_roundup64 i a = (i +^ a -^ 1L) /^ a + +let int_of_le32 str + assert (String.length str = 4); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + let c2 = Char.code (String.unsafe_get str 2) in + let c3 = Char.code (String.unsafe_get str 3) in + Int64.of_int c0 +^ + (Int64.shift_left (Int64.of_int c1) 8) +^ + (Int64.shift_left (Int64.of_int c2) 16) +^ + (Int64.shift_left (Int64.of_int c3) 24) + +let le32_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in + let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in + let b = Bytes.create 4 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c2)); + Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c3)); + Bytes.to_string b + +type wrap_break_t = WrapEOS | WrapSpace | WrapNL + +let rec wrap ?(chan = stdout) ?(indent = 0) str + let len = String.length str in + _wrap chan indent 0 0 len str + +and _wrap chan indent column i len str + if i < len then ( + let (j, break) = _wrap_find_next_break i len str in + let next_column + if column + (j-i) >= 76 then ( + output_char chan '\n'; + output_spaces chan indent; + indent + (j-i) + 1 + ) + else column + (j-i) + 1 in + output chan (Bytes.of_string str) i (j-i); + match break with + | WrapEOS -> () + | WrapSpace -> + output_char chan ' '; + _wrap chan indent next_column (j+1) len str + | WrapNL -> + output_char chan '\n'; + output_spaces chan indent; + _wrap chan indent indent (j+1) len str + ) + +and _wrap_find_next_break i len str + if i >= len then (len, WrapEOS) + else if String.unsafe_get str i = ' ' then (i, WrapSpace) + else if String.unsafe_get str i = '\n' then (i, WrapNL) + else _wrap_find_next_break (i+1) len str + +and output_spaces chan n = for i = 0 to n-1 do output_char chan ' ' done + +let (|>) x f = f x + +(* Drop elements from a list while a predicate is true. *) +let rec dropwhile f = function + | [] -> [] + | x :: xs when f x -> dropwhile f xs + | xs -> xs + +(* Take elements from a list while a predicate is true. *) +let rec takewhile f = function + | x :: xs when f x -> x :: takewhile f xs + | _ -> [] + +let rec filter_map f = function + | [] -> [] + | x :: xs -> + match f x with + | Some y -> y :: filter_map f xs + | None -> filter_map f xs + +let rec find_map f = function + | [] -> raise Not_found + | x :: xs -> + match f x with + | Some y -> y + | None -> find_map f xs + +let iteri f xs + let rec loop i = function + | [] -> () + | x :: xs -> f i x; loop (i+1) xs + in + loop 0 xs + +let rec mapi i f + function + | [] -> [] + | a::l -> + let r = f i a in + r :: mapi (i + 1) f l +let mapi f l = mapi 0 f l + +let rec combine3 xs ys zs + match xs, ys, zs with + | [], [], [] -> [] + | x::xs, y::ys, z::zs -> (x, y, z) :: combine3 xs ys zs + | _ -> invalid_arg "combine3" + +let rec assoc ?(cmp = compare) ~default x = function + | [] -> default + | (y, y') :: _ when cmp x y = 0 -> y' + | _ :: ys -> assoc ~cmp ~default x ys + +let uniq ?(cmp = Pervasives.compare) xs + let rec loop acc = function + | [] -> acc + | [x] -> x :: acc + | x :: (y :: _ as xs) when cmp x y = 0 -> + loop acc xs + | x :: (y :: _ as xs) -> + loop (x :: acc) xs + in + List.rev (loop [] xs) + +let sort_uniq ?(cmp = Pervasives.compare) xs + let xs = List.sort cmp xs in + let xs = uniq ~cmp xs in + xs + +let remove_duplicates xs + let h = Hashtbl.create (List.length xs) in + let rec loop = function + | [] -> [] + | x :: xs when Hashtbl.mem h x -> xs + | x :: xs -> Hashtbl.add h x true; x :: loop xs + in + loop xs + +let push_back xsp x = xsp := !xsp @ [x] +let push_front x xsp = xsp := x :: !xsp +let pop_back xsp + let x, xs + match List.rev !xsp with + | x :: xs -> x, xs + | [] -> failwith "pop" in + xsp := List.rev xs; + x +let pop_front xsp + let x, xs + match !xsp with + | x :: xs -> x, xs + | [] -> failwith "shift" in + xsp := xs; + x + +let append xsp xs = xsp := !xsp @ xs +let prepend xs xsp = xsp := xs @ !xsp + +let unique = let i = ref 0 in fun () -> incr i; !i + +let may f = function + | None -> () + | Some x -> f x + +type ('a, 'b) maybe = Either of 'a | Or of 'b + +let protect ~f ~finally + let r + try Either (f ()) + with exn -> Or exn in + finally (); + match r with Either ret -> ret | Or exn -> raise exn + +let failwithf fs = ksprintf failwith fs + +exception Executable_not_found of string (* executable *) + +let which executable + let paths + try String.nsplit ":" (Sys.getenv "PATH") + with Not_found -> [] in + let paths = filter_map ( + fun p -> + let path = p // executable in + try Unix.access path [Unix.X_OK]; Some path + with Unix.Unix_error _ -> None + ) paths in + match paths with + | [] -> raise (Executable_not_found executable) + | x :: _ -> x + +(* Program name. *) +let prog = Filename.basename Sys.executable_name + +(* Stores the colours (--colours), quiet (--quiet), trace (-x) and + * verbose (-v) flags in a global variable. + *) +let colours = ref false +let set_colours () = colours := true +let colours () = !colours + +let quiet = ref false +let set_quiet () = quiet := true +let quiet () = !quiet + +let trace = ref false +let set_trace () = trace := true +let trace () = !trace + +let verbose = ref false +let set_verbose () = verbose := true +let verbose () = !verbose + +let read_whole_file path + let buf = Buffer.create 16384 in + let chan = open_in path in + let maxlen = 16384 in + let b = Bytes.create maxlen in + let rec loop () + let r = input chan b 0 maxlen in + if r > 0 then ( + Buffer.add_substring buf (Bytes.to_string b) 0 r; + loop () + ) + in + loop (); + close_in chan; + Buffer.contents buf + +(* Compare two version strings intelligently. *) +let rex_numbers = Str.regexp "^\\([0-9]+\\)\\(.*\\)$" +let rex_letters = Str.regexp_case_fold "^\\([a-z]+\\)\\(.*\\)$" + +let compare_version v1 v2 + let rec split_version = function + | "" -> [] + | str -> + let first, rest + if Str.string_match rex_numbers str 0 then ( + let n = Str.matched_group 1 str in + let rest = Str.matched_group 2 str in + let n + try `Number (int_of_string n) + with Failure _ -> `String n in + n, rest + ) + else if Str.string_match rex_letters str 0 then + `String (Str.matched_group 1 str), Str.matched_group 2 str + else ( + let len = String.length str in + `Char str.[0], String.sub str 1 (len-1) + ) in + first :: split_version rest + in + compare (split_version v1) (split_version v2) + +(* Annoying LVM2 returns a differing UUID strings for different + * function calls (sometimes containing or not containing '-' + * characters), so we have to normalize each string before + * comparison. c.f. 'compare_pvuuids' in virt-filesystem. + *) +let compare_lvm2_uuids uuid1 uuid2 + let n1 = String.length uuid1 and n2 = String.length uuid2 in + let rec loop i1 i2 + if i1 = n1 && i2 = n2 then 0 (* matching *) + else if i1 >= n1 then 1 (* different lengths *) + else if i2 >= n2 then -1 + else if uuid1.[i1] = '-' then loop (i1+1) i2 (* ignore '-' characters *) + else if uuid2.[i2] = '-' then loop i1 (i2+1) + else ( + let c = compare uuid1.[i1] uuid2.[i2] in + if c <> 0 then c (* not matching *) + else loop (i1+1) (i2+1) + ) + in + loop 0 0 + +let stringify_args args + let rec quote_args = function + | [] -> "" + | x :: xs -> " " ^ Filename.quote x ^ quote_args xs + in + match args with + | [] -> "" + | app :: xs -> app ^ quote_args xs + +(* Unlink a temporary file on exit. *) +let unlink_on_exit + let files = ref [] in + let registered_handlers = ref false in + + let rec unlink_files () + List.iter ( + fun file -> try Unix.unlink file with _ -> () + ) !files + and register_handlers () + (* Unlink on exit. *) + at_exit unlink_files + in + + fun file -> + files := file :: !files; + if not !registered_handlers then ( + register_handlers (); + registered_handlers := true + ) + +let is_block_device file + try (Unix.stat file).Unix.st_kind = Unix.S_BLK + with Unix.Unix_error _ -> false + +let is_char_device file + try (Unix.stat file).Unix.st_kind = Unix.S_CHR + with Unix.Unix_error _ -> false + +(* Annoyingly Sys.is_directory throws an exception on failure + * (RHBZ#1022431). + *) +let is_directory path + try Sys.is_directory path + with Sys_error _ -> false + +let absolute_path path + if not (Filename.is_relative path) then path + else Sys.getcwd () // path + +let qemu_input_filename filename + (* If the filename is something like "file:foo" then qemu-img will + * try to interpret that as "foo" in the file:/// protocol. To + * avoid that, if the path is relative prefix it with "./" since + * qemu-img won't try to interpret such a path. + *) + if String.length filename > 0 && filename.[0] <> '/' then + "./" ^ filename + else + filename + +let rec mkdir_p path permissions + try Unix.mkdir path permissions + with + | Unix.Unix_error (Unix.EEXIST, _, _) -> () + | Unix.Unix_error (Unix.ENOENT, _, _) -> + (* A component in the path does not exist, so first try + * creating the parent directory, and then again the requested + * directory. *) + mkdir_p (Filename.dirname path) permissions; + Unix.mkdir path permissions + +let normalize_arch = function + | "i486" | "i586" | "i686" -> "i386" + | "amd64" -> "x86_64" + | "powerpc" -> "ppc" + | "powerpc64" -> "ppc64" + | "powerpc64le" -> "ppc64le" + | arch -> arch + +(* Are guest arch and host_cpu compatible, in terms of being able + * to run commands in the libguestfs appliance? + *) +let guest_arch_compatible guest_arch + let own = normalize_arch Guestfs_config.host_cpu in + let guest_arch = normalize_arch guest_arch in + match own, guest_arch with + | x, y when x = y -> true + | "x86_64", "i386" -> true + | _ -> false + +(* Is the guest OS "Unix-like"? *) +let unix_like = function + | "hurd" + | "linux" + | "minix" -> true + | typ when String.is_suffix typ "bsd" -> true + | _ -> false + +(** Return the last part of a string, after the specified separator. *) +let last_part_of str sep + try + let i = String.rindex str sep in + Some (String.sub str (i+1) (String.length str - (i+1))) + with Not_found -> None + +let read_first_line_from_file filename + let chan = open_in filename in + let line = input_line chan in + close_in chan; + line + +let is_regular_file path = (* NB: follows symlinks. *) + try (Unix.stat path).Unix.st_kind = Unix.S_REG + with Unix.Unix_error _ -> false diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli new file mode 100644 index 000000000..820673764 --- /dev/null +++ b/common/mlstdutils/std_utils.mli @@ -0,0 +1,338 @@ +(* Common utilities for OCaml tools in libguestfs. + * Copyright (C) 2010-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. + *) + +module Char : sig + type t = char + val chr : int -> char + val code : char -> int + val compare: t -> t -> int + val escaped : char -> string + val unsafe_chr : int -> char + + val lowercase_ascii : char -> char + val uppercase_ascii : char -> char + + val isspace : char -> bool + (** Return true if char is a whitespace character. *) + val isdigit : char -> bool + (** Return true if the character is a digit [[0-9]]. *) + val isxdigit : char -> bool + (** Return true if the character is a hex digit [[0-9a-fA-F]]. *) + val isalpha : char -> bool + (** Return true if the character is a US ASCII 7 bit alphabetic. *) + val isalnum : char -> bool + (** Return true if the character is a US ASCII 7 bit alphanumeric. *) + + val hexdigit : char -> int + (** Return the value of a hex digit. If the char is not in + the set [[0-9a-fA-F]] then this returns [-1]. *) +end +(** Override the Char module from stdlib. *) + +module String : sig + type t = string + val compare: t -> t -> int + val concat : string -> string list -> string + val contains : string -> char -> bool + val contains_from : string -> int -> char -> bool + val copy : string -> string + val escaped : string -> string + val get : string -> int -> char + val index : string -> char -> int + val index_from : string -> int -> char -> int + val iter : (char -> unit) -> string -> unit + val length : string -> int + val make : int -> char -> string + val rcontains_from : string -> int -> char -> bool + val rindex : string -> char -> int + val rindex_from : string -> int -> char -> int + val sub : string -> int -> int -> string + val unsafe_get : string -> int -> char + + val map : (char -> char) -> string -> string + + val lowercase_ascii : string -> string + val uppercase_ascii : string -> string + val capitalize_ascii : string -> string + + val is_prefix : string -> string -> bool + (** [is_prefix str prefix] returns true if [prefix] is a prefix of [str]. *) + val is_suffix : string -> string -> bool + (** [is_suffix str suffix] returns true if [suffix] is a suffix of [str]. *) + val find : string -> string -> int + (** [find str sub] searches for [sub] as a substring of [str]. If + found it returns the index. If not found, it returns [-1]. *) + val replace : string -> string -> string -> string + (** [replace str s1 s2] replaces all instances of [s1] appearing in + [str] with [s2]. *) + val replace_char : string -> char -> char -> string + (** Replace character in string. *) + val nsplit : string -> string -> string list + (** [nsplit sep str] splits [str] into multiple strings at each + separator [sep]. *) + val split : string -> string -> string * string + (** [split sep str] splits [str] at the first occurrence of the + separator [sep], returning the part before and the part after. + If separator is not found, return the whole string and an + empty string. *) + val lines_split : string -> string list + (** [lines_split str] splits [str] into lines, keeping continuation + characters (i.e. [\] at the end of lines) into account. *) + val random8 : unit -> string + (** Return a string of 8 random printable characters. *) + val triml : ?test:(char -> bool) -> string -> string + (** Trim left. *) + val trimr : ?test:(char -> bool) -> string -> string + (** Trim right. *) + val trim : ?test:(char -> bool) -> string -> string + (** Trim left and right. *) + val count_chars : char -> string -> int + (** Count number of times the character occurs in string. *) + val explode : string -> char list + (** Explode a string into a list of characters. *) + val map_chars : (char -> 'a) -> string -> 'a list + (** Explode string, then map function over the characters. *) + val spaces : int -> string + (** [spaces n] creates a string of n spaces. *) +end +(** Override the String module from stdlib. *) + +val ( // ) : string -> string -> string +(** Concatenate directory and filename. *) + +val quote : string -> string +(** Shell-safe quoting of a string (alias for {!Filename.quote}). *) + +val subdirectory : string -> string -> string +(** [subdirectory parent path] returns subdirectory part of [path] relative + to the [parent]. If [path] and [parent] point to the same directory empty + string is returned. + + Note: path normalization on arguments is {b not} performed! + + If [parent] is not a path prefix of [path] the function raises + [Invalid_argument]. *) + +val ( +^ ) : int64 -> int64 -> int64 +val ( -^ ) : int64 -> int64 -> int64 +val ( *^ ) : int64 -> int64 -> int64 +val ( /^ ) : int64 -> int64 -> int64 +val ( &^ ) : int64 -> int64 -> int64 +val ( ~^ ) : int64 -> int64 +(** Various int64 operators. *) + +external identity : 'a -> 'a = "%identity" + +val roundup64 : int64 -> int64 -> int64 +(** [roundup64 i a] returns [i] rounded up to the next multiple of [a]. *) +val div_roundup64 : int64 -> int64 -> int64 +(** [div_roundup64 i a] returns [i] rounded up to the next multiple of [a], + with the result divided by [a]. *) +val int_of_le32 : string -> int64 +(** Unpack a 4 byte string as a little endian 32 bit integer. *) +val le32_of_int : int64 -> string +(** Pack a 32 bit integer a 4 byte string stored little endian. *) + +val wrap : ?chan:out_channel -> ?indent:int -> string -> unit +(** Wrap text. *) + +val output_spaces : out_channel -> int -> unit +(** Write [n] spaces to [out_channel]. *) + +val (|>) : 'a -> ('a -> 'b) -> 'b +(** Added in OCaml 4.01, we can remove our definition when we + can assume this minimum version of OCaml. *) + +val dropwhile : ('a -> bool) -> 'a list -> 'a list +(** [dropwhile f xs] drops leading elements from [xs] until + [f] returns false. *) +val takewhile : ('a -> bool) -> 'a list -> 'a list +(** [takewhile f xs] takes leading elements from [xs] until + [f] returns false. + + For any list [xs] and function [f], + [xs = takewhile f xs @ dropwhile f xs] *) +val filter_map : ('a -> 'b option) -> 'a list -> 'b list +(** [filter_map f xs] applies [f] to each element of [xs]. If + [f x] returns [Some y] then [y] is added to the returned list. *) +val find_map : ('a -> 'b option) -> 'a list -> 'b +(** [find_map f xs] applies [f] to each element of [xs] until + [f x] returns [Some y]. It returns [y]. If we exhaust the + list then this raises [Not_found]. *) +val iteri : (int -> 'a -> 'b) -> 'a list -> unit +(** [iteri f xs] calls [f i x] for each element, with [i] counting from [0]. *) +val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list +(** [mapi f xs] calls [f i x] for each element, with [i] counting from [0], + forming the return values from [f] into another list. *) + +val combine3 : 'a list -> 'b list -> 'c list -> ('a * 'b * 'c) list +(** Like {!List.combine} but for triples. All lists must be the same length. *) + +val assoc : ?cmp:('a -> 'a -> int) -> default:'b -> 'a -> ('a * 'b) list -> 'b +(** Like {!List.assoc} but with a user-defined comparison function, and + instead of raising [Not_found], it returns the [~default] value. *) + +val uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list +(** Uniquify a list (the list must be sorted first). *) + +val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list +(** Sort and uniquify a list. *) + +val remove_duplicates : 'a list -> 'a list +(** Remove duplicates from an unsorted list; useful when the order + of the elements matter. + + Please use [sort_uniq] when the order does not matter. *) + +val push_back : 'a list ref -> 'a -> unit +val push_front : 'a -> 'a list ref -> unit +val pop_back : 'a list ref -> 'a +val pop_front : 'a list ref -> 'a +(** Imperative list manipulation functions, similar to C++ STL + functions with the same names. (Although the names are similar, + the computational complexity of the functions is quite different.) + + These operate on list references, and each function modifies the + list reference that is passed to it. + + [push_back xsp x] appends the element [x] to the end of the list + [xsp]. This function is not tail-recursive. + + [push_front x xsp] prepends the element [x] to the head of the + list [xsp]. (The arguments are reversed compared to the same Perl + function, but OCaml is type safe so that's OK.) + + [pop_back xsp] removes the last element of the list [xsp] and + returns it. The list is modified to become the list minus the + final element. If a zero-length list is passed in, this raises + [Failure "pop_back"]. This function is not tail-recursive. + + [pop_front xsp] removes the head element of the list [xsp] and + returns it. The list is modified to become the tail of the list. + If a zero-length list is passed in, this raises [Failure + "pop_front"]. *) + +val append : 'a list ref -> 'a list -> unit +val prepend : 'a list -> 'a list ref -> unit +(** More imperative list manipulation functions. + + [append] is like {!push_back} above, except it appends a list to + the list reference. This function is not tail-recursive. + + [prepend] is like {!push_front} above, except it prepends a list + to the list reference. *) + +val unique : unit -> int +(** Returns a unique number each time called. *) + +val may : ('a -> unit) -> 'a option -> unit +(** [may f (Some x)] runs [f x]. [may f None] does nothing. *) + +type ('a, 'b) maybe = Either of 'a | Or of 'b +(** Like the Haskell [Either] type. *) + +val protect : f:(unit -> 'a) -> finally:(unit -> unit) -> 'a +(** Execute [~f] and afterwards execute [~finally]. + + If [~f] throws an exception then [~finally] is run and the + original exception from [~f] is re-raised. + + If [~finally] throws an exception, then the original exception + is lost. (NB: Janestreet core {!Exn.protectx}, on which this + function is modelled, doesn't throw away the exception in this + case, but requires a lot more work by the caller. Perhaps we + will change this in future.) *) + +val failwithf : ('a, unit, string, 'b) format4 -> 'a +(** Like [failwith] but supports printf-like arguments. *) + +exception Executable_not_found of string (* executable *) +(** Exception thrown by [which] when the specified executable is not found + in [$PATH]. *) + +val which : string -> string +(** Return the full path of the specified executable from [$PATH]. + + Throw [Executable_not_found] if not available. *) + +val prog : string +(** The program name (derived from {!Sys.executable_name}). *) + +val set_colours : unit -> unit +val colours : unit -> bool +val set_quiet : unit -> unit +val quiet : unit -> bool +val set_trace : unit -> unit +val trace : unit -> bool +val set_verbose : unit -> unit +val verbose : unit -> bool +(** Stores the colours ([--colours]), quiet ([--quiet]), trace ([-x]) + and verbose ([-v]) flags in global variables. *) + +val read_whole_file : string -> string +(** Read in the whole file as a string. *) + +val compare_version : string -> string -> int +(** Compare two version strings. *) + +val compare_lvm2_uuids : string -> string -> int +(** Compare two LVM2 UUIDs, ignoring '-' characters. *) + +val stringify_args : string list -> string +(** Create a "pretty-print" representation of a program invocation + (i.e. executable and its arguments). *) + +val unlink_on_exit : string -> unit +(** Unlink a temporary file on exit. *) + +val is_block_device : string -> bool +val is_char_device : string -> bool +val is_directory : string -> bool +(** These don't throw exceptions, unlike the [Sys] functions. *) + +val absolute_path : string -> string +(** Convert any path to an absolute path. *) + +val qemu_input_filename : string -> string +(** Sanitizes a filename for passing it safely to qemu/qemu-img. *) + +val mkdir_p : string -> int -> unit +(** Creates a directory, and its parents if missing. *) + +val normalize_arch : string -> string +(** Normalize the architecture name, i.e. maps it into a defined + identifier for it -- e.g. i386, i486, i586, and i686 are + normalized as i386. *) + +val guest_arch_compatible : string -> bool +(** Are guest arch and host_cpu compatible, in terms of being able + to run commands in the libguestfs appliance? *) + +val unix_like : string -> bool +(** Is the guest OS "Unix-like"? Call this with the result of + {!Guestfs.inspect_get_type}. *) + +val last_part_of : string -> char -> string option +(** Return the last part of a string, after the specified separator. *) + +val read_first_line_from_file : string -> string +(** Read only the first line (i.e. until the first newline character) + of a file. *) + +val is_regular_file : string -> bool +(** Checks whether the file is a regular file. *) diff --git a/common/mlstdutils/std_utils_tests.ml b/common/mlstdutils/std_utils_tests.ml new file mode 100644 index 000000000..1003f931c --- /dev/null +++ b/common/mlstdutils/std_utils_tests.ml @@ -0,0 +1,95 @@ +(* Utilities for OCaml tools in libguestfs. + * Copyright (C) 2011-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. + *) + +(* This file tests the Std_utils module. *) + +open OUnit2 +open Std_utils + +(* Utils. *) +let assert_equal_string = assert_equal ~printer:(fun x -> x) +let assert_equal_int = assert_equal ~printer:(fun x -> string_of_int x) +let assert_equal_int64 = assert_equal ~printer:(fun x -> Int64.to_string x) +let assert_equal_stringlist = assert_equal ~printer:(fun x -> "(" ^ (String.escaped (String.concat "," x)) ^ ")") + +let test_subdirectory ctx + assert_equal_string "" (subdirectory "/foo" "/foo"); + assert_equal_string "" (subdirectory "/foo" "/foo/"); + assert_equal_string "bar" (subdirectory "/foo" "/foo/bar"); + assert_equal_string "bar/baz" (subdirectory "/foo" "/foo/bar/baz") + +(* Test Common_utils.int_of_le32 and Common_utils.le32_of_int. *) +let test_le32 ctx + assert_equal_int64 0x20406080L (int_of_le32 "\x80\x60\x40\x20"); + assert_equal_string "\x80\x60\x40\x20" (le32_of_int 0x20406080L) + +(* Test Std_utils.String.is_prefix. *) +let test_string_is_prefix ctx + assert_bool "String.is_prefix,," (String.is_prefix "" ""); + assert_bool "String.is_prefix,foo," (String.is_prefix "foo" ""); + assert_bool "String.is_prefix,foo,foo" (String.is_prefix "foo" "foo"); + assert_bool "String.is_prefix,foo123,foo" (String.is_prefix "foo123" "foo"); + assert_bool "not (String.is_prefix,,foo" (not (String.is_prefix "" "foo")) + +(* Test Std_utils.String.is_suffix. *) +let test_string_is_suffix ctx + assert_bool "String.is_suffix,," (String.is_suffix "" ""); + assert_bool "String.is_suffix,foo," (String.is_suffix "foo" ""); + assert_bool "String.is_suffix,foo,foo" (String.is_suffix "foo" "foo"); + assert_bool "String.is_suffix,123foo,foo" (String.is_suffix "123foo" "foo"); + assert_bool "not String.is_suffix,,foo" (not (String.is_suffix "" "foo")) + +(* Test Std_utils.String.find. *) +let test_string_find ctx + assert_equal_int 0 (String.find "" ""); + assert_equal_int 0 (String.find "foo" ""); + assert_equal_int 1 (String.find "foo" "o"); + assert_equal_int 3 (String.find "foobar" "bar"); + assert_equal_int (-1) (String.find "" "baz"); + assert_equal_int (-1) (String.find "foobar" "baz") + +(* Test Std_utils.String.lines_split. *) +let test_string_lines_split ctx + assert_equal_stringlist [""] (String.lines_split ""); + assert_equal_stringlist ["A"] (String.lines_split "A"); + assert_equal_stringlist ["A"; ""] (String.lines_split "A\n"); + assert_equal_stringlist ["A"; "B"] (String.lines_split "A\nB"); + assert_equal_stringlist ["A"; "B"; "C"] (String.lines_split "A\nB\nC"); + assert_equal_stringlist ["A"; "B"; "C"; "D"] (String.lines_split "A\nB\nC\nD"); + assert_equal_stringlist ["A\n"] (String.lines_split "A\\"); + assert_equal_stringlist ["A\nB"] (String.lines_split "A\\\nB"); + assert_equal_stringlist ["A"; "B\nC"] (String.lines_split "A\nB\\\nC"); + assert_equal_stringlist ["A"; "B\nC"; "D"] (String.lines_split "A\nB\\\nC\nD"); + assert_equal_stringlist ["A"; "B\nC\nD"] (String.lines_split "A\nB\\\nC\\\nD"); + assert_equal_stringlist ["A\nB"; ""] (String.lines_split "A\\\nB\n"); + assert_equal_stringlist ["A\nB\n"] (String.lines_split "A\\\nB\\\n") + +(* Suites declaration. *) +let suite + "mllib Std_utils" >::: + [ + "subdirectory" >:: test_subdirectory; + "numeric.le32" >:: test_le32; + "strings.is_prefix" >:: test_string_is_prefix; + "strings.is_suffix" >:: test_string_is_suffix; + "strings.find" >:: test_string_find; + "strings.lines_split" >:: test_string_lines_split; + ] + +let () + run_test_tt_main suite diff --git a/mllib/stringMap.ml b/common/mlstdutils/stringMap.ml similarity index 100% rename from mllib/stringMap.ml rename to common/mlstdutils/stringMap.ml diff --git a/mllib/stringMap.mli b/common/mlstdutils/stringMap.mli similarity index 100% rename from mllib/stringMap.mli rename to common/mlstdutils/stringMap.mli diff --git a/configure.ac b/configure.ac index 7d0f0a1dd..eba149241 100644 --- a/configure.ac +++ b/configure.ac @@ -186,6 +186,8 @@ AC_CONFIG_FILES([Makefile common/edit/Makefile common/miniexpect/Makefile common/mlprogress/Makefile + common/mlstdutils/Makefile + common/mlstdutils/guestfs_config.ml common/mlvisit/Makefile common/mlxml/Makefile common/options/Makefile @@ -230,7 +232,6 @@ AC_CONFIG_FILES([Makefile lua/examples/Makefile make-fs/Makefile mllib/Makefile - mllib/guestfs_config.ml ocaml/META ocaml/Makefile ocaml/examples/Makefile diff --git a/customize/Makefile.am b/customize/Makefile.am index 07398b2e8..674134b70 100644 --- a/customize/Makefile.am +++ b/customize/Makefile.am @@ -123,6 +123,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/mllib \ -I $(builddir) if HAVE_OCAML_PKG_GETTEXT @@ -149,7 +150,12 @@ else CUSTOMIZE_THEOBJECTS = $(CUSTOMIZE_XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) customize.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + customize.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) OCAMLCLIBS = \ -lutils \ diff --git a/customize/SELinux_relabel.ml b/customize/SELinux_relabel.ml index 11999299b..ab373b33a 100644 --- a/customize/SELinux_relabel.ml +++ b/customize/SELinux_relabel.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/customize/append_line.ml b/customize/append_line.ml index e967b4201..405080617 100644 --- a/customize/append_line.ml +++ b/customize/append_line.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/customize/customize_main.ml b/customize/customize_main.ml index 5b4641237..55ec3cb78 100644 --- a/customize/customize_main.ml +++ b/customize/customize_main.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Customize_cmdline diff --git a/customize/customize_run.ml b/customize/customize_run.ml index f71ae3535..5564684b4 100644 --- a/customize/customize_run.ml +++ b/customize/customize_run.ml @@ -19,8 +19,9 @@ open Unix open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Customize_cmdline open Password diff --git a/customize/firstboot.ml b/customize/firstboot.ml index 9208daa0a..41aa52dac 100644 --- a/customize/firstboot.ml +++ b/customize/firstboot.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/customize/hostname.ml b/customize/hostname.ml index 23c149402..b49db8714 100644 --- a/customize/hostname.ml +++ b/customize/hostname.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Printf diff --git a/customize/password.ml b/customize/password.ml index 4ab5a14d1..d26b94865 100644 --- a/customize/password.ml +++ b/customize/password.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/customize/perl_edit.ml b/customize/perl_edit.ml index 5cd250b49..bb44ea062 100644 --- a/customize/perl_edit.ml +++ b/customize/perl_edit.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils external c_edit_file : verbose:bool -> Guestfs.t -> int64 -> string -> string -> unit diff --git a/customize/ssh_key.ml b/customize/ssh_key.ml index 4302a8e92..185536d1d 100644 --- a/customize/ssh_key.ml +++ b/customize/ssh_key.ml @@ -16,13 +16,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext -open Common_utils - open Printf open Sys open Unix +open Std_utils +open Common_utils +open Common_gettext.Gettext + module G = Guestfs type ssh_key_selector diff --git a/customize/subscription_manager.ml b/customize/subscription_manager.ml index a23efe546..56ba28ab9 100644 --- a/customize/subscription_manager.ml +++ b/customize/subscription_manager.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext type sm_credentials = { sm_username : string; diff --git a/dib/Makefile.am b/dib/Makefile.am index 6780ee249..b10fa94c9 100644 --- a/dib/Makefile.am +++ b/dib/Makefile.am @@ -79,6 +79,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -99,10 +100,15 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_dib_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_dib_LINK = \ @@ -138,7 +144,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/dib/cmdline.ml b/dib/cmdline.ml index 67194704e..549f01546 100644 --- a/dib/cmdline.ml +++ b/dib/cmdline.ml @@ -18,8 +18,9 @@ (* Command line argument parsing. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Utils diff --git a/dib/dib.ml b/dib/dib.ml index 8d078aabb..78b109da8 100644 --- a/dib/dib.ml +++ b/dib/dib.ml @@ -16,9 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Cmdline open Utils diff --git a/dib/elements.ml b/dib/elements.ml index 4c2875ae1..d237eeb7f 100644 --- a/dib/elements.ml +++ b/dib/elements.ml @@ -18,8 +18,9 @@ (* Parsing and handling of elements. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils diff --git a/dib/output_format.ml b/dib/output_format.ml index 851cefc43..6499ee259 100644 --- a/dib/output_format.ml +++ b/dib/output_format.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/dib/output_format_qcow2.ml b/dib/output_format_qcow2.ml index afb564ce7..a32b2a4f9 100644 --- a/dib/output_format_qcow2.ml +++ b/dib/output_format_qcow2.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/dib/utils.ml b/dib/utils.ml index afa2ec944..8b6bb1576 100644 --- a/dib/utils.ml +++ b/dib/utils.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index f6ac73047..71c17603f 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -16,6 +16,7 @@ common/edit/file-edit.h common/miniexpect/miniexpect.c common/miniexpect/miniexpect.h common/mlprogress/progress-c.c +common/mlstdutils/dummy.c common/mlvisit/dummy.c common/mlvisit/visit-c.c common/mlxml/xml-c.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index 1ff496381..beb44d2dc 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -100,6 +100,10 @@ A copy of the miniexpect library from L<http://git.annexia.org/?p=miniexpect.git;a=summary>. This is used in virt-p2v. +=item F<common/mlstdutils> + +A library of pure OCaml utility functions used in many places. + =item F<common/mlprogress> OCaml bindings for the progress bar functions (see F<common/progress>). diff --git a/generator/GObject.ml b/generator/GObject.ml index bb95b170c..8fa17c219 100644 --- a/generator/GObject.ml +++ b/generator/GObject.ml @@ -22,7 +22,7 @@ open Printf -open Common_utils +open Std_utils open Actions open Docstrings open Events diff --git a/generator/Makefile.am b/generator/Makefile.am index 81b49cab1..401029d34 100644 --- a/generator/Makefile.am +++ b/generator/Makefile.am @@ -53,8 +53,6 @@ sources = \ c.mli \ checks.ml \ checks.mli \ - common_utils.ml \ - common_utils.mli \ csharp.ml \ csharp.mli \ customize.ml \ @@ -77,7 +75,6 @@ sources = \ GObject.mli \ golang.ml \ golang.mli \ - guestfs_config.ml \ haskell.ml \ haskell.mli \ java.ml \ @@ -118,8 +115,8 @@ sources = \ # In build dependency order. objects = \ $(OCAML_GENERATOR_BYTES_COMPAT_CMO) \ - guestfs_config.cmo \ - common_utils.cmo \ + ../common/mlstdutils/guestfs_config.cmo \ + ../common/mlstdutils/std_utils.cmo \ types.cmo \ utils.cmo \ proc_nr.cmo \ @@ -170,7 +167,12 @@ objects = \ EXTRA_DIST = $(sources) files-generated.txt -OCAMLPACKAGES = -package unix,str -I $(srcdir) -I . +OCAMLPACKAGES = \ + -package unix,str \ + -I $(srcdir) \ + -I . \ + -I $(top_srcdir)/common/mlstdutils \ + -I $(top_builddir)/common/mlstdutils OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) noinst_PROGRAM = generator @@ -183,9 +185,9 @@ generator: $(objects) # Dependencies. depend: .depend -.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) common_utils.ml common_utils.mli guestfs_config.ml +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) $(wildcard $(abs_srcdir)/common/mlstdutils/*.mli) $(wildcard $(abs_srcdir)/common/mlstdutils/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) $^ | \ + $(OCAMLFIND) ocamldep -I ../common/mlstdutils -I $(abs_srcdir) $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ @@ -224,22 +226,6 @@ stamp-generator: generator cd $(top_srcdir) && $(abs_builddir)/generator touch $@ -# We share common_utils.ml{,i} with the mllib directory. However we -# have to remove functions which depend on any modules which are not -# part of the OCaml stdlib. -common_utils.ml: $(top_srcdir)/mllib/common_utils.ml - rm -f $@ $@-t - echo '(* This file is generated from mllib/common_utils.ml *)' > $@-t - sed -n '/^(\*<stdlib>\*)$$/,/^(\*<\/stdlib>\*)$$/p' $< >> $@-t - mv $@-t $@ -common_utils.mli: $(top_srcdir)/mllib/common_utils.mli - rm -f $@ $@-t - echo '(* This file is generated from mllib/common_utils.mli *)' > $@-t - sed -n '/^(\*<stdlib>\*)$$/,/^(\*<\/stdlib>\*)$$/p' $< >> $@-t - mv $@-t $@ -guestfs_config.ml: ../mllib/guestfs_config.ml - cp $< $@ - CLEANFILES += $(noinst_DATA) $(noinst_PROGRAM) DISTCLEANFILES += .pod2text.data.version.2 diff --git a/generator/OCaml.ml b/generator/OCaml.ml index 955da6f09..f6a4292b9 100644 --- a/generator/OCaml.ml +++ b/generator/OCaml.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/UEFI.ml b/generator/UEFI.ml index 95797aad9..5c5e02bab 100644 --- a/generator/UEFI.ml +++ b/generator/UEFI.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Utils open Pr open Docstrings diff --git a/generator/XDR.ml b/generator/XDR.ml index 2d799929b..4b0a552d1 100644 --- a/generator/XDR.ml +++ b/generator/XDR.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/actions.ml b/generator/actions.ml index 2722f3dcd..a9b3b5906 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Types open Utils diff --git a/generator/authors.ml b/generator/authors.ml index d4547bdb1..ca5242983 100644 --- a/generator/authors.ml +++ b/generator/authors.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Utils open Pr open Docstrings diff --git a/generator/bindtests.ml b/generator/bindtests.ml index c3caebfce..d225146c0 100644 --- a/generator/bindtests.ml +++ b/generator/bindtests.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/c.ml b/generator/c.ml index 1f099a221..27bf1ebf9 100644 --- a/generator/c.ml +++ b/generator/c.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/checks.ml b/generator/checks.ml index 881069489..be7b272a3 100644 --- a/generator/checks.ml +++ b/generator/checks.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Types open Utils open Actions diff --git a/generator/csharp.ml b/generator/csharp.ml index 6a280011a..0eab21f0d 100644 --- a/generator/csharp.ml +++ b/generator/csharp.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/customize.ml b/generator/customize.ml index b158eb5d9..381ed0627 100644 --- a/generator/customize.ml +++ b/generator/customize.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Docstrings open Pr @@ -623,6 +623,7 @@ and generate_customize_cmdline_ml () open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/generator/daemon.ml b/generator/daemon.ml index 84686973c..0300dc54b 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/docstrings.ml b/generator/docstrings.ml index 2ce595dae..696f1c52a 100644 --- a/generator/docstrings.ml +++ b/generator/docstrings.ml @@ -21,7 +21,7 @@ open Unix open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/erlang.ml b/generator/erlang.ml index 602380966..03cca3368 100644 --- a/generator/erlang.ml +++ b/generator/erlang.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/errnostring.ml b/generator/errnostring.ml index b3d718815..e5f4c69f8 100644 --- a/generator/errnostring.ml +++ b/generator/errnostring.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/events.ml b/generator/events.ml index 7188e1203..f3b682a5a 100644 --- a/generator/events.ml +++ b/generator/events.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Utils (* NB: DO NOT REORDER THESE, as doing so will change the ABI. Only diff --git a/generator/fish.ml b/generator/fish.ml index 45289132f..3d99c9081 100644 --- a/generator/fish.ml +++ b/generator/fish.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/golang.ml b/generator/golang.ml index f32ccf2c1..67f360839 100644 --- a/generator/golang.ml +++ b/generator/golang.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/haskell.ml b/generator/haskell.ml index 592d817fa..ec3f311df 100644 --- a/generator/haskell.ml +++ b/generator/haskell.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/java.ml b/generator/java.ml index c44e669a0..7c3212a49 100644 --- a/generator/java.ml +++ b/generator/java.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/lua.ml b/generator/lua.ml index c4ab4cc47..b40c51753 100644 --- a/generator/lua.ml +++ b/generator/lua.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/main.ml b/generator/main.ml index d4316c085..0e1c01f74 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -21,7 +21,7 @@ open Unix open Printf -open Common_utils +open Std_utils open Pr open Actions open Structs diff --git a/generator/optgroups.ml b/generator/optgroups.ml index e9a37e19c..4b9b66f77 100644 --- a/generator/optgroups.ml +++ b/generator/optgroups.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Types open Utils open Actions diff --git a/generator/perl.ml b/generator/perl.ml index bf2dc4a81..8e3dad75e 100644 --- a/generator/perl.ml +++ b/generator/perl.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/php.ml b/generator/php.ml index 48cd89fdc..0721e431a 100644 --- a/generator/php.ml +++ b/generator/php.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/pr.ml b/generator/pr.ml index e8b32b67d..0c56f3e67 100644 --- a/generator/pr.ml +++ b/generator/pr.ml @@ -21,7 +21,7 @@ open Unix open Printf -open Common_utils +open Std_utils open Utils (* Output channel, 'pr' prints to this. *) diff --git a/generator/python.ml b/generator/python.ml index 4cae24757..c6c237241 100644 --- a/generator/python.ml +++ b/generator/python.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/ruby.ml b/generator/ruby.ml index 4d2ebbf0d..825cab32a 100644 --- a/generator/ruby.ml +++ b/generator/ruby.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/structs.ml b/generator/structs.ml index 834fa9c54..57975b564 100644 --- a/generator/structs.ml +++ b/generator/structs.ml @@ -18,7 +18,7 @@ (* Please read generator/README first. *) -open Common_utils +open Std_utils open Types open Utils diff --git a/generator/tests_c_api.ml b/generator/tests_c_api.ml index f9f14f6dc..a680521f4 100644 --- a/generator/tests_c_api.ml +++ b/generator/tests_c_api.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils open Types open Utils open Pr diff --git a/generator/utils.ml b/generator/utils.ml index a745a02b7..b818a0b3c 100644 --- a/generator/utils.ml +++ b/generator/utils.ml @@ -23,7 +23,7 @@ * makes this a bit harder than it should be. *) -open Common_utils +open Std_utils open Unix open Printf diff --git a/get-kernel/Makefile.am b/get-kernel/Makefile.am index bda3a8db1..c6454d7a4 100644 --- a/get-kernel/Makefile.am +++ b/get-kernel/Makefile.am @@ -63,6 +63,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -83,10 +84,15 @@ else OBJECTS = $(XOBJECTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) mllib.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + mllib.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) virt_get_kernel_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_get_kernel_LINK = \ @@ -121,7 +127,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/get-kernel/get_kernel.ml b/get-kernel/get_kernel.ml index e45838811..1c9ece44b 100644 --- a/get-kernel/get_kernel.ml +++ b/get-kernel/get_kernel.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName module G = Guestfs diff --git a/mllib/Makefile.am b/mllib/Makefile.am index c84f5f36d..5f6f7fa85 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -19,7 +19,7 @@ include $(top_srcdir)/subdir-rules.mk EXTRA_DIST = \ $(SOURCES_MLI) \ - $(filter-out guestfs_config.ml libdir.ml,$(SOURCES_ML)) \ + $(SOURCES_ML) \ $(SOURCES_C) \ common_utils_tests.ml \ getopt_tests.ml \ @@ -36,15 +36,11 @@ SOURCES_MLI = \ planner.mli \ regedit.mli \ registry.mli \ - stringMap.mli \ URI.mli \ xpath_helpers.mli SOURCES_ML = \ - guestfs_config.ml \ $(OCAML_BYTES_COMPAT_ML) \ - libdir.ml \ - stringMap.ml \ common_gettext.ml \ getopt.ml \ unix_utils.ml \ @@ -93,7 +89,8 @@ libmllib_a_CPPFLAGS = \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/common/options \ - -I$(top_srcdir)/common/mlxml + -I$(top_srcdir)/common/mlxml \ + -I$(top_srcdir)/common/mlstdutils libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ @@ -112,6 +109,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlxml \ + -I $(top_builddir)/common/mlstdutils \ -I $(builddir) OCAMLPACKAGES_TESTS = $(MLLIB_CMA) if HAVE_OCAML_PKG_GETTEXT @@ -144,13 +142,6 @@ $(MLLIB_CMA): $(OBJECTS) libmllib.a $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ $(OBJECTS) $(libmllib_a_OBJECTS) -o mllib -# This OCaml module has to be generated by make (configure will put -# unexpanded prefix macro in). - -libdir.ml: Makefile - echo 'let libdir = "$(libdir)"' > $@-t - mv $@-t $@ - # Tests. common_utils_tests_SOURCES = dummy.c @@ -196,10 +187,14 @@ JSON_tests_THEOBJECTS = $(JSON_tests_XOBJECTS) JSON_tests.cmx: OCAMLPACKAGES += $(OCAMLPACKAGES_TESTS) endif -OCAMLLINKFLAGS = mlguestfs.$(MLARCHIVE) $(LINK_CUSTOM_OCAMLC_ONLY) +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlguestfs.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) common_utils_tests_DEPENDENCIES = \ $(common_utils_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ $(MLLIB_CMA) \ $(top_srcdir)/ocaml-link.sh common_utils_tests_LINK = \ @@ -210,6 +205,7 @@ common_utils_tests_LINK = \ getopt_tests_DEPENDENCIES = \ $(getopt_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ $(MLLIB_CMA) \ $(top_srcdir)/ocaml-link.sh getopt_tests_LINK = \ @@ -220,6 +216,7 @@ getopt_tests_LINK = \ JSON_tests_DEPENDENCIES = \ $(JSON_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ $(MLLIB_CMA) \ $(top_srcdir)/ocaml-link.sh JSON_tests_LINK = \ diff --git a/mllib/checksums.ml b/mllib/checksums.ml index 61deac2d1..f4c414f57 100644 --- a/mllib/checksums.ml +++ b/mllib/checksums.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Printf diff --git a/mllib/common_utils.ml b/mllib/common_utils.ml index 6a9b08973..1220de7a0 100644 --- a/mllib/common_utils.ml +++ b/mllib/common_utils.ml @@ -16,14 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(* The parts between <stdlib>..</stdlib> are copied into the - * generator/common_utils.ml file. These parts must ONLY use - * functions from the OCaml stdlib. - *) -(*<stdlib>*) open Printf -(*</stdlib>*) +open Std_utils open Common_gettext.Gettext open Getopt.OptionName @@ -31,474 +26,6 @@ external c_inspect_decrypt : Guestfs.t -> int64 -> unit = "guestfs_int_mllib_ins external c_set_echo_keys : unit -> unit = "guestfs_int_mllib_set_echo_keys" "noalloc" external c_set_keys_from_stdin : unit -> unit = "guestfs_int_mllib_set_keys_from_stdin" "noalloc" -(*<stdlib>*) - -module Char = struct - include Char - - let lowercase_ascii c - if (c >= 'A' && c <= 'Z') - then unsafe_chr (code c + 32) - else c - - let uppercase_ascii c - if (c >= 'a' && c <= 'z') - then unsafe_chr (code c - 32) - else c - - let isspace c - c = ' ' - (* || c = '\f' *) || c = '\n' || c = '\r' || c = '\t' (* || c = '\v' *) - - let isdigit = function - | '0'..'9' -> true - | _ -> false - - let isxdigit = function - | '0'..'9' -> true - | 'a'..'f' -> true - | 'A'..'F' -> true - | _ -> false - - let isalpha = function - | 'a'..'z' -> true - | 'A'..'Z' -> true - | _ -> false - - let isalnum = function - | '0'..'9' -> true - | 'a'..'z' -> true - | 'A'..'Z' -> true - | _ -> false - - let hexdigit = function - | '0' -> 0 - | '1' -> 1 - | '2' -> 2 - | '3' -> 3 - | '4' -> 4 - | '5' -> 5 - | '6' -> 6 - | '7' -> 7 - | '8' -> 8 - | '9' -> 9 - | 'a' | 'A' -> 10 - | 'b' | 'B' -> 11 - | 'c' | 'C' -> 12 - | 'd' | 'D' -> 13 - | 'e' | 'E' -> 14 - | 'f' | 'F' -> 15 - | _ -> -1 -end - -module String = struct - include String - - let map f s - let len = String.length s in - let b = Bytes.create len in - for i = 0 to len-1 do - Bytes.unsafe_set b i (f (unsafe_get s i)) - done; - Bytes.to_string b - - let lowercase_ascii s = map Char.lowercase_ascii s - let uppercase_ascii s = map Char.uppercase_ascii s - - let capitalize_ascii s - let b = Bytes.of_string s in - Bytes.unsafe_set b 0 (Char.uppercase_ascii (Bytes.unsafe_get b 0)); - Bytes.to_string b - - let is_prefix str prefix - let n = length prefix in - length str >= n && sub str 0 n = prefix - - let is_suffix str suffix - let sufflen = length suffix - and len = length str in - len >= sufflen && sub str (len - sufflen) sufflen = suffix - - let rec find s sub - let len = length s in - let sublen = length sub in - let rec loop i - if i <= len-sublen then ( - let rec loop2 j - if j < sublen then ( - if s.[i+j] = sub.[j] then loop2 (j+1) - else -1 - ) else - i (* found *) - in - let r = loop2 0 in - if r = -1 then loop (i+1) else r - ) else - -1 (* not found *) - in - loop 0 - - let rec replace s s1 s2 - let len = length s in - let sublen = length s1 in - let i = find s s1 in - if i = -1 then s - else ( - let s' = sub s 0 i in - let s'' = sub s (i+sublen) (len-i-sublen) in - s' ^ s2 ^ replace s'' s1 s2 - ) - - let replace_char s c1 c2 - let b2 = Bytes.of_string s in - let r = ref false in - for i = 0 to Bytes.length b2 - 1 do - if Bytes.unsafe_get b2 i = c1 then ( - Bytes.unsafe_set b2 i c2; - r := true - ) - done; - if not !r then s else Bytes.to_string b2 - - let rec nsplit sep str - let len = length str in - let seplen = length sep in - let i = find str sep in - if i = -1 then [str] - else ( - let s' = sub str 0 i in - let s'' = sub str (i+seplen) (len-i-seplen) in - s' :: nsplit sep s'' - ) - - let split sep str - let len = length sep in - let seplen = length str in - let i = find str sep in - if i = -1 then str, "" - else ( - sub str 0 i, sub str (i + len) (seplen - i - len) - ) - - let rec lines_split str - let buf = Buffer.create 16 in - let len = length str in - let rec loop start len - try - let i = index_from str start '\n' in - if i > 0 && str.[i-1] = '\\' then ( - Buffer.add_substring buf str start (i-start-1); - Buffer.add_char buf '\n'; - loop (i+1) len - ) else ( - Buffer.add_substring buf str start (i-start); - i+1 - ) - with Not_found -> - if len > 0 && str.[len-1] = '\\' then ( - Buffer.add_substring buf str start (len-start-1); - Buffer.add_char buf '\n' - ) else - Buffer.add_substring buf str start (len-start); - len+1 - in - let endi = loop 0 len in - let line = Buffer.contents buf in - if endi > len then - [line] - else - line :: lines_split (sub str endi (len-endi)) - - let random8 - let chars = "abcdefghijklmnopqrstuvwxyz0123456789" in - fun () -> - concat "" ( - List.map ( - fun _ -> - let c = Random.int 36 in - let c = chars.[c] in - make 1 c - ) [1;2;3;4;5;6;7;8] - ) - - let triml ?(test = Char.isspace) str - let i = ref 0 in - let n = ref (String.length str) in - while !n > 0 && test str.[!i]; do - decr n; - incr i - done; - if !i = 0 then str - else String.sub str !i !n - - let trimr ?(test = Char.isspace) str - let n = ref (String.length str) in - while !n > 0 && test str.[!n-1]; do - decr n - done; - if !n = String.length str then str - else String.sub str 0 !n - - let trim ?(test = Char.isspace) str - trimr ~test (triml ~test str) - - let count_chars c str - let count = ref 0 in - for i = 0 to String.length str - 1 do - if c = String.unsafe_get str i then incr count - done; - !count - - let explode str - let r = ref [] in - for i = 0 to String.length str - 1 do - let c = String.unsafe_get str i in - r := c :: !r; - done; - List.rev !r - - let map_chars f str - List.map f (explode str) - - let spaces n = String.make n ' ' -end - -let (//) = Filename.concat -let quote = Filename.quote - -let subdirectory 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 ( +^ ) = Int64.add -let ( -^ ) = Int64.sub -let ( *^ ) = Int64.mul -let ( /^ ) = Int64.div -let ( &^ ) = Int64.logand -let ( ~^ ) = Int64.lognot - -external identity : 'a -> 'a = "%identity" - -let roundup64 i a = let a = a -^ 1L in (i +^ a) &^ (~^ a) -let div_roundup64 i a = (i +^ a -^ 1L) /^ a - -let int_of_le32 str - assert (String.length str = 4); - let c0 = Char.code (String.unsafe_get str 0) in - let c1 = Char.code (String.unsafe_get str 1) in - let c2 = Char.code (String.unsafe_get str 2) in - let c3 = Char.code (String.unsafe_get str 3) in - Int64.of_int c0 +^ - (Int64.shift_left (Int64.of_int c1) 8) +^ - (Int64.shift_left (Int64.of_int c2) 16) +^ - (Int64.shift_left (Int64.of_int c3) 24) - -let le32_of_int i - let c0 = i &^ 0xffL in - let c1 = Int64.shift_right (i &^ 0xff00L) 8 in - let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in - let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in - let b = Bytes.create 4 in - Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c0)); - Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c1)); - Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c2)); - Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c3)); - Bytes.to_string b - -type wrap_break_t = WrapEOS | WrapSpace | WrapNL - -let rec wrap ?(chan = stdout) ?(indent = 0) str - let len = String.length str in - _wrap chan indent 0 0 len str - -and _wrap chan indent column i len str - if i < len then ( - let (j, break) = _wrap_find_next_break i len str in - let next_column - if column + (j-i) >= 76 then ( - output_char chan '\n'; - output_spaces chan indent; - indent + (j-i) + 1 - ) - else column + (j-i) + 1 in - output chan (Bytes.of_string str) i (j-i); - match break with - | WrapEOS -> () - | WrapSpace -> - output_char chan ' '; - _wrap chan indent next_column (j+1) len str - | WrapNL -> - output_char chan '\n'; - output_spaces chan indent; - _wrap chan indent indent (j+1) len str - ) - -and _wrap_find_next_break i len str - if i >= len then (len, WrapEOS) - else if String.unsafe_get str i = ' ' then (i, WrapSpace) - else if String.unsafe_get str i = '\n' then (i, WrapNL) - else _wrap_find_next_break (i+1) len str - -and output_spaces chan n = for i = 0 to n-1 do output_char chan ' ' done - -let (|>) x f = f x - -(* Drop elements from a list while a predicate is true. *) -let rec dropwhile f = function - | [] -> [] - | x :: xs when f x -> dropwhile f xs - | xs -> xs - -(* Take elements from a list while a predicate is true. *) -let rec takewhile f = function - | x :: xs when f x -> x :: takewhile f xs - | _ -> [] - -let rec filter_map f = function - | [] -> [] - | x :: xs -> - match f x with - | Some y -> y :: filter_map f xs - | None -> filter_map f xs - -let rec find_map f = function - | [] -> raise Not_found - | x :: xs -> - match f x with - | Some y -> y - | None -> find_map f xs - -let iteri f xs - let rec loop i = function - | [] -> () - | x :: xs -> f i x; loop (i+1) xs - in - loop 0 xs - -let rec mapi i f - function - | [] -> [] - | a::l -> - let r = f i a in - r :: mapi (i + 1) f l -let mapi f l = mapi 0 f l - -let rec combine3 xs ys zs - match xs, ys, zs with - | [], [], [] -> [] - | x::xs, y::ys, z::zs -> (x, y, z) :: combine3 xs ys zs - | _ -> invalid_arg "combine3" - -let rec assoc ?(cmp = compare) ~default x = function - | [] -> default - | (y, y') :: _ when cmp x y = 0 -> y' - | _ :: ys -> assoc ~cmp ~default x ys - -let uniq ?(cmp = Pervasives.compare) xs - let rec loop acc = function - | [] -> acc - | [x] -> x :: acc - | x :: (y :: _ as xs) when cmp x y = 0 -> - loop acc xs - | x :: (y :: _ as xs) -> - loop (x :: acc) xs - in - List.rev (loop [] xs) - -let sort_uniq ?(cmp = Pervasives.compare) xs - let xs = List.sort cmp xs in - let xs = uniq ~cmp xs in - xs - -let remove_duplicates xs - let h = Hashtbl.create (List.length xs) in - let rec loop = function - | [] -> [] - | x :: xs when Hashtbl.mem h x -> xs - | x :: xs -> Hashtbl.add h x true; x :: loop xs - in - loop xs - -let push_back xsp x = xsp := !xsp @ [x] -let push_front x xsp = xsp := x :: !xsp -let pop_back xsp - let x, xs - match List.rev !xsp with - | x :: xs -> x, xs - | [] -> failwith "pop" in - xsp := List.rev xs; - x -let pop_front xsp - let x, xs - match !xsp with - | x :: xs -> x, xs - | [] -> failwith "shift" in - xsp := xs; - x - -let append xsp xs = xsp := !xsp @ xs -let prepend xs xsp = xsp := xs @ !xsp - -let unique = let i = ref 0 in fun () -> incr i; !i - -let may f = function - | None -> () - | Some x -> f x - -type ('a, 'b) maybe = Either of 'a | Or of 'b - -let protect ~f ~finally - let r - try Either (f ()) - with exn -> Or exn in - finally (); - match r with Either ret -> ret | Or exn -> raise exn - -let failwithf fs = ksprintf failwith fs - -exception Executable_not_found of string (* executable *) - -let which executable - let paths - try String.nsplit ":" (Sys.getenv "PATH") - with Not_found -> [] in - let paths = filter_map ( - fun p -> - let path = p // executable in - try Unix.access path [Unix.X_OK]; Some path - with Unix.Unix_error _ -> None - ) paths in - match paths with - | [] -> raise (Executable_not_found executable) - | x :: _ -> x - -(* Program name. *) -let prog = Filename.basename Sys.executable_name - -(* Stores the colours (--colours), quiet (--quiet), trace (-x) and - * verbose (-v) flags in a global variable. - *) -let colours = ref false -let set_colours () = colours := true -let colours () = !colours - -let quiet = ref false -let set_quiet () = quiet := true -let quiet () = !quiet - -let trace = ref false -let set_trace () = trace := true -let trace () = !trace - -let verbose = ref false -let set_verbose () = verbose := true -let verbose () = !verbose - (* ANSI terminal colours. *) let istty chan Unix.isatty (Unix.descr_of_out_channel chan) @@ -514,8 +41,6 @@ let ansi_magenta ?(chan = stdout) () let ansi_restore ?(chan = stdout) () if colours () || istty chan then output_string chan "\x1b[0m" -(*</stdlib>*) - (* Timestamped progress messages, used for ordinary messages when not * --quiet. *) @@ -630,26 +155,6 @@ let virt_tools_data_dir ) in fun () -> Lazy.force dir -(*<stdlib>*) - -let read_whole_file path - let buf = Buffer.create 16384 in - let chan = open_in path in - let maxlen = 16384 in - let b = Bytes.create maxlen in - let rec loop () - let r = input chan b 0 maxlen in - if r > 0 then ( - Buffer.add_substring buf (Bytes.to_string b) 0 r; - loop () - ) - in - loop (); - close_in chan; - Buffer.contents buf - -(*</stdlib>*) - (* Parse a size field, eg. "10G". *) let parse_size let const_re = Str.regexp "^\\([.0-9]+\\)\\([bKMG]\\)$" in @@ -764,67 +269,6 @@ let create_standard_options argspec ?anon_fun ?(key_opts = false) usage_msg else []) in Getopt.create argspec ?anon_fun usage_msg -(*<stdlib>*) - -(* Compare two version strings intelligently. *) -let rex_numbers = Str.regexp "^\\([0-9]+\\)\\(.*\\)$" -let rex_letters = Str.regexp_case_fold "^\\([a-z]+\\)\\(.*\\)$" - -let compare_version v1 v2 - let rec split_version = function - | "" -> [] - | str -> - let first, rest - if Str.string_match rex_numbers str 0 then ( - let n = Str.matched_group 1 str in - let rest = Str.matched_group 2 str in - let n - try `Number (int_of_string n) - with Failure _ -> `String n in - n, rest - ) - else if Str.string_match rex_letters str 0 then - `String (Str.matched_group 1 str), Str.matched_group 2 str - else ( - let len = String.length str in - `Char str.[0], String.sub str 1 (len-1) - ) in - first :: split_version rest - in - compare (split_version v1) (split_version v2) - -(* Annoying LVM2 returns a differing UUID strings for different - * function calls (sometimes containing or not containing '-' - * characters), so we have to normalize each string before - * comparison. c.f. 'compare_pvuuids' in virt-filesystem. - *) -let compare_lvm2_uuids uuid1 uuid2 - let n1 = String.length uuid1 and n2 = String.length uuid2 in - let rec loop i1 i2 - if i1 = n1 && i2 = n2 then 0 (* matching *) - else if i1 >= n1 then 1 (* different lengths *) - else if i2 >= n2 then -1 - else if uuid1.[i1] = '-' then loop (i1+1) i2 (* ignore '-' characters *) - else if uuid2.[i2] = '-' then loop i1 (i2+1) - else ( - let c = compare uuid1.[i1] uuid2.[i2] in - if c <> 0 then c (* not matching *) - else loop (i1+1) (i2+1) - ) - in - loop 0 0 - -let stringify_args args - let rec quote_args = function - | [] -> "" - | x :: xs -> " " ^ Filename.quote x ^ quote_args xs - in - match args with - | [] -> "" - | app :: xs -> app ^ quote_args xs - -(*</stdlib>*) - (* Run an external command, slurp up the output as a list of lines. *) let external_command ?(echo_cmd = true) cmd if echo_cmd then @@ -889,31 +333,6 @@ let uuidgen () if len < 10 then assert false; (* sanity check on uuidgen *) uuid -(*<stdlib>*) - -(* Unlink a temporary file on exit. *) -let unlink_on_exit - let files = ref [] in - let registered_handlers = ref false in - - let rec unlink_files () - List.iter ( - fun file -> try Unix.unlink file with _ -> () - ) !files - and register_handlers () - (* Unlink on exit. *) - at_exit unlink_files - in - - fun file -> - files := file :: !files; - if not !registered_handlers then ( - register_handlers (); - registered_handlers := true - ) - -(*</stdlib>*) - (* Remove a temporary directory on exit. *) let rmdir_on_exit let dirs = ref [] in @@ -1050,18 +469,6 @@ let detect_file_type filename close_in chan; ret -(*<stdlib>*) - -let is_block_device file - try (Unix.stat file).Unix.st_kind = Unix.S_BLK - with Unix.Unix_error _ -> false - -let is_char_device file - try (Unix.stat file).Unix.st_kind = Unix.S_CHR - with Unix.Unix_error _ -> false - -(*</stdlib>*) - let is_partition dev try if not (is_block_device dev) then false @@ -1075,87 +482,6 @@ let is_partition dev ) with Unix.Unix_error _ -> false -(*<stdlib>*) - -(* Annoyingly Sys.is_directory throws an exception on failure - * (RHBZ#1022431). - *) -let is_directory path - try Sys.is_directory path - with Sys_error _ -> false - -let absolute_path path - if not (Filename.is_relative path) then path - else Sys.getcwd () // path - -let qemu_input_filename filename - (* If the filename is something like "file:foo" then qemu-img will - * try to interpret that as "foo" in the file:/// protocol. To - * avoid that, if the path is relative prefix it with "./" since - * qemu-img won't try to interpret such a path. - *) - if String.length filename > 0 && filename.[0] <> '/' then - "./" ^ filename - else - filename - -let rec mkdir_p path permissions - try Unix.mkdir path permissions - with - | Unix.Unix_error (Unix.EEXIST, _, _) -> () - | Unix.Unix_error (Unix.ENOENT, _, _) -> - (* A component in the path does not exist, so first try - * creating the parent directory, and then again the requested - * directory. *) - mkdir_p (Filename.dirname path) permissions; - Unix.mkdir path permissions - -let normalize_arch = function - | "i486" | "i586" | "i686" -> "i386" - | "amd64" -> "x86_64" - | "powerpc" -> "ppc" - | "powerpc64" -> "ppc64" - | "powerpc64le" -> "ppc64le" - | arch -> arch - -(* Are guest arch and host_cpu compatible, in terms of being able - * to run commands in the libguestfs appliance? - *) -let guest_arch_compatible guest_arch - let own = normalize_arch Guestfs_config.host_cpu in - let guest_arch = normalize_arch guest_arch in - match own, guest_arch with - | x, y when x = y -> true - | "x86_64", "i386" -> true - | _ -> false - -(* Is the guest OS "Unix-like"? *) -let unix_like = function - | "hurd" - | "linux" - | "minix" -> true - | typ when String.is_suffix typ "bsd" -> true - | _ -> false - -(** Return the last part of a string, after the specified separator. *) -let last_part_of str sep - try - let i = String.rindex str sep in - Some (String.sub str (i+1) (String.length str - (i+1))) - with Not_found -> None - -let read_first_line_from_file filename - let chan = open_in filename in - let line = input_line chan in - close_in chan; - line - -let is_regular_file path = (* NB: follows symlinks. *) - try (Unix.stat path).Unix.st_kind = Unix.S_REG - with Unix.Unix_error _ -> false - -(*</stdlib>*) - let inspect_mount_root g ?mount_opts_fn root let mps = g#inspect_get_mountpoints root in let cmp (a,_) (b,_) diff --git a/mllib/common_utils.mli b/mllib/common_utils.mli index c088f8497..b72f7ee62 100644 --- a/mllib/common_utils.mli +++ b/mllib/common_utils.mli @@ -16,280 +16,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -(* The parts between <stdlib>..</stdlib> are copied into the - * generator/common_utils.ml file. These parts must ONLY use - * functions from the OCaml stdlib. - *) -(*<stdlib>*) - -module Char : sig - type t = char - val chr : int -> char - val code : char -> int - val compare: t -> t -> int - val escaped : char -> string - val unsafe_chr : int -> char - - val lowercase_ascii : char -> char - val uppercase_ascii : char -> char - - val isspace : char -> bool - (** Return true if char is a whitespace character. *) - val isdigit : char -> bool - (** Return true if the character is a digit [[0-9]]. *) - val isxdigit : char -> bool - (** Return true if the character is a hex digit [[0-9a-fA-F]]. *) - val isalpha : char -> bool - (** Return true if the character is a US ASCII 7 bit alphabetic. *) - val isalnum : char -> bool - (** Return true if the character is a US ASCII 7 bit alphanumeric. *) - - val hexdigit : char -> int - (** Return the value of a hex digit. If the char is not in - the set [[0-9a-fA-F]] then this returns [-1]. *) -end -(** Override the Char module from stdlib. *) - -module String : sig - type t = string - val compare: t -> t -> int - val concat : string -> string list -> string - val contains : string -> char -> bool - val contains_from : string -> int -> char -> bool - val copy : string -> string - val escaped : string -> string - val get : string -> int -> char - val index : string -> char -> int - val index_from : string -> int -> char -> int - val iter : (char -> unit) -> string -> unit - val length : string -> int - val make : int -> char -> string - val rcontains_from : string -> int -> char -> bool - val rindex : string -> char -> int - val rindex_from : string -> int -> char -> int - val sub : string -> int -> int -> string - val unsafe_get : string -> int -> char - - val map : (char -> char) -> string -> string - - val lowercase_ascii : string -> string - val uppercase_ascii : string -> string - val capitalize_ascii : string -> string - - val is_prefix : string -> string -> bool - (** [is_prefix str prefix] returns true if [prefix] is a prefix of [str]. *) - val is_suffix : string -> string -> bool - (** [is_suffix str suffix] returns true if [suffix] is a suffix of [str]. *) - val find : string -> string -> int - (** [find str sub] searches for [sub] as a substring of [str]. If - found it returns the index. If not found, it returns [-1]. *) - val replace : string -> string -> string -> string - (** [replace str s1 s2] replaces all instances of [s1] appearing in - [str] with [s2]. *) - val replace_char : string -> char -> char -> string - (** Replace character in string. *) - val nsplit : string -> string -> string list - (** [nsplit sep str] splits [str] into multiple strings at each - separator [sep]. *) - val split : string -> string -> string * string - (** [split sep str] splits [str] at the first occurrence of the - separator [sep], returning the part before and the part after. - If separator is not found, return the whole string and an - empty string. *) - val lines_split : string -> string list - (** [lines_split str] splits [str] into lines, keeping continuation - characters (i.e. [\] at the end of lines) into account. *) - val random8 : unit -> string - (** Return a string of 8 random printable characters. *) - val triml : ?test:(char -> bool) -> string -> string - (** Trim left. *) - val trimr : ?test:(char -> bool) -> string -> string - (** Trim right. *) - val trim : ?test:(char -> bool) -> string -> string - (** Trim left and right. *) - val count_chars : char -> string -> int - (** Count number of times the character occurs in string. *) - val explode : string -> char list - (** Explode a string into a list of characters. *) - val map_chars : (char -> 'a) -> string -> 'a list - (** Explode string, then map function over the characters. *) - val spaces : int -> string - (** [spaces n] creates a string of n spaces. *) -end -(** Override the String module from stdlib. *) - -val ( // ) : string -> string -> string -(** Concatenate directory and filename. *) - -val quote : string -> string -(** Shell-safe quoting of a string (alias for {!Filename.quote}). *) - -val subdirectory : string -> string -> string -(** [subdirectory parent path] returns subdirectory part of [path] relative - to the [parent]. If [path] and [parent] point to the same directory empty - string is returned. - - Note: path normalization on arguments is {b not} performed! - - If [parent] is not a path prefix of [path] the function raises - [Invalid_argument]. *) - -val ( +^ ) : int64 -> int64 -> int64 -val ( -^ ) : int64 -> int64 -> int64 -val ( *^ ) : int64 -> int64 -> int64 -val ( /^ ) : int64 -> int64 -> int64 -val ( &^ ) : int64 -> int64 -> int64 -val ( ~^ ) : int64 -> int64 -(** Various int64 operators. *) - -external identity : 'a -> 'a = "%identity" - -val roundup64 : int64 -> int64 -> int64 -(** [roundup64 i a] returns [i] rounded up to the next multiple of [a]. *) -val div_roundup64 : int64 -> int64 -> int64 -(** [div_roundup64 i a] returns [i] rounded up to the next multiple of [a], - with the result divided by [a]. *) -val int_of_le32 : string -> int64 -(** Unpack a 4 byte string as a little endian 32 bit integer. *) -val le32_of_int : int64 -> string -(** Pack a 32 bit integer a 4 byte string stored little endian. *) - -val wrap : ?chan:out_channel -> ?indent:int -> string -> unit -(** Wrap text. *) - -val output_spaces : out_channel -> int -> unit -(** Write [n] spaces to [out_channel]. *) - -val (|>) : 'a -> ('a -> 'b) -> 'b -(** Added in OCaml 4.01, we can remove our definition when we - can assume this minimum version of OCaml. *) - -val dropwhile : ('a -> bool) -> 'a list -> 'a list -(** [dropwhile f xs] drops leading elements from [xs] until - [f] returns false. *) -val takewhile : ('a -> bool) -> 'a list -> 'a list -(** [takewhile f xs] takes leading elements from [xs] until - [f] returns false. - - For any list [xs] and function [f], - [xs = takewhile f xs @ dropwhile f xs] *) -val filter_map : ('a -> 'b option) -> 'a list -> 'b list -(** [filter_map f xs] applies [f] to each element of [xs]. If - [f x] returns [Some y] then [y] is added to the returned list. *) -val find_map : ('a -> 'b option) -> 'a list -> 'b -(** [find_map f xs] applies [f] to each element of [xs] until - [f x] returns [Some y]. It returns [y]. If we exhaust the - list then this raises [Not_found]. *) -val iteri : (int -> 'a -> 'b) -> 'a list -> unit -(** [iteri f xs] calls [f i x] for each element, with [i] counting from [0]. *) -val mapi : (int -> 'a -> 'b) -> 'a list -> 'b list -(** [mapi f xs] calls [f i x] for each element, with [i] counting from [0], - forming the return values from [f] into another list. *) - -val combine3 : 'a list -> 'b list -> 'c list -> ('a * 'b * 'c) list -(** Like {!List.combine} but for triples. All lists must be the same length. *) - -val assoc : ?cmp:('a -> 'a -> int) -> default:'b -> 'a -> ('a * 'b) list -> 'b -(** Like {!List.assoc} but with a user-defined comparison function, and - instead of raising [Not_found], it returns the [~default] value. *) - -val uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list -(** Uniquify a list (the list must be sorted first). *) - -val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list -(** Sort and uniquify a list. *) - -val remove_duplicates : 'a list -> 'a list -(** Remove duplicates from an unsorted list; useful when the order - of the elements matter. - - Please use [sort_uniq] when the order does not matter. *) - -val push_back : 'a list ref -> 'a -> unit -val push_front : 'a -> 'a list ref -> unit -val pop_back : 'a list ref -> 'a -val pop_front : 'a list ref -> 'a -(** Imperative list manipulation functions, similar to C++ STL - functions with the same names. (Although the names are similar, - the computational complexity of the functions is quite different.) - - These operate on list references, and each function modifies the - list reference that is passed to it. - - [push_back xsp x] appends the element [x] to the end of the list - [xsp]. This function is not tail-recursive. - - [push_front x xsp] prepends the element [x] to the head of the - list [xsp]. (The arguments are reversed compared to the same Perl - function, but OCaml is type safe so that's OK.) - - [pop_back xsp] removes the last element of the list [xsp] and - returns it. The list is modified to become the list minus the - final element. If a zero-length list is passed in, this raises - [Failure "pop_back"]. This function is not tail-recursive. - - [pop_front xsp] removes the head element of the list [xsp] and - returns it. The list is modified to become the tail of the list. - If a zero-length list is passed in, this raises [Failure - "pop_front"]. *) - -val append : 'a list ref -> 'a list -> unit -val prepend : 'a list -> 'a list ref -> unit -(** More imperative list manipulation functions. - - [append] is like {!push_back} above, except it appends a list to - the list reference. This function is not tail-recursive. - - [prepend] is like {!push_front} above, except it prepends a list - to the list reference. *) - -val unique : unit -> int -(** Returns a unique number each time called. *) - -val may : ('a -> unit) -> 'a option -> unit -(** [may f (Some x)] runs [f x]. [may f None] does nothing. *) - -type ('a, 'b) maybe = Either of 'a | Or of 'b -(** Like the Haskell [Either] type. *) - -val protect : f:(unit -> 'a) -> finally:(unit -> unit) -> 'a -(** Execute [~f] and afterwards execute [~finally]. - - If [~f] throws an exception then [~finally] is run and the - original exception from [~f] is re-raised. - - If [~finally] throws an exception, then the original exception - is lost. (NB: Janestreet core {!Exn.protectx}, on which this - function is modelled, doesn't throw away the exception in this - case, but requires a lot more work by the caller. Perhaps we - will change this in future.) *) - -val failwithf : ('a, unit, string, 'b) format4 -> 'a -(** Like [failwith] but supports printf-like arguments. *) - -exception Executable_not_found of string (* executable *) -(** Exception thrown by [which] when the specified executable is not found - in [$PATH]. *) - -val which : string -> string -(** Return the full path of the specified executable from [$PATH]. - - Throw [Executable_not_found] if not available. *) - -val prog : string -(** The program name (derived from {!Sys.executable_name}). *) - -val set_quiet : unit -> unit -val quiet : unit -> bool -val set_trace : unit -> unit -val trace : unit -> bool -val set_verbose : unit -> unit -val verbose : unit -> bool -(** Stores the quiet ([--quiet]), trace ([-x]) and verbose ([-v]) flags - in global variables. *) - -(*</stdlib>*) - val message : ('a, unit, string, unit) format4 -> 'a (** Timestamped progress messages. Used for ordinary messages when not [--quiet]. *) @@ -328,13 +54,6 @@ val virt_tools_data_dir : unit -> string the environment variable is not set, a default value is calculated based on configure settings. *) -(*<stdlib>*) - -val read_whole_file : string -> string -(** Read in the whole file as a string. *) - -(*</stdlib>*) - val parse_size : string -> int64 (** Parse a size field, eg. [10G] *) @@ -354,20 +73,6 @@ val create_standard_options : Getopt.speclist -> ?anon_fun:Getopt.anon_fun -> ?k Returns a new [Getopt.t] handle. *) -(*<stdlib>*) - -val compare_version : string -> string -> int -(** Compare two version strings. *) - -val compare_lvm2_uuids : string -> string -> int -(** Compare two LVM2 UUIDs, ignoring '-' characters. *) - -val stringify_args : string list -> string -(** Create a "pretty-print" representation of a program invocation - (i.e. executable and its arguments). *) - -(*</stdlib>*) - val external_command : ?echo_cmd:bool -> string -> string list (** Run an external command, slurp up the output as a list of lines. @@ -389,13 +94,6 @@ val shell_command : ?echo_cmd:bool -> string -> int val uuidgen : unit -> string (** Run uuidgen to return a random UUID. *) -(*<stdlib>*) - -val unlink_on_exit : string -> unit -(** Unlink a temporary file on exit. *) - -(*</stdlib>*) - val rmdir_on_exit : string -> unit (** Remove a temporary directory on exit (using [rm -rf]). *) @@ -431,55 +129,10 @@ val debug_augeas_errors : Guestfs.guestfs -> unit val detect_file_type : string -> [`GZip | `Tar | `XZ | `Zip | `Unknown] (** Detect type of a file (for a very limited range of file types). *) -(*<stdlib>*) - -val is_block_device : string -> bool -val is_char_device : string -> bool -val is_directory : string -> bool -(** These don't throw exceptions, unlike the [Sys] functions. *) - -(*</stdlib>*) - val is_partition : string -> bool (** Return true if the host device [dev] is a partition. If it's anything else, or missing, returns false. *) -(*<stdlib>*) - -val absolute_path : string -> string -(** Convert any path to an absolute path. *) - -val qemu_input_filename : string -> string -(** Sanitizes a filename for passing it safely to qemu/qemu-img. *) - -val mkdir_p : string -> int -> unit -(** Creates a directory, and its parents if missing. *) - -val normalize_arch : string -> string -(** Normalize the architecture name, i.e. maps it into a defined - identifier for it -- e.g. i386, i486, i586, and i686 are - normalized as i386. *) - -val guest_arch_compatible : string -> bool -(** Are guest arch and host_cpu compatible, in terms of being able - to run commands in the libguestfs appliance? *) - -val unix_like : string -> bool -(** Is the guest OS "Unix-like"? Call this with the result of - {!Guestfs.inspect_get_type}. *) - -val last_part_of : string -> char -> string option -(** Return the last part of a string, after the specified separator. *) - -val read_first_line_from_file : string -> string -(** Read only the first line (i.e. until the first newline character) - of a file. *) - -val is_regular_file : string -> bool -(** Checks whether the file is a regular file. *) - -(*</stdlib>*) - val inspect_mount_root : Guestfs.guestfs -> ?mount_opts_fn:(string -> string) -> string -> unit (** Mounts all the mount points of the specified root, just like [guestfish -i] does. diff --git a/mllib/common_utils_tests.ml b/mllib/common_utils_tests.ml index aacc01e04..def5ea932 100644 --- a/mllib/common_utils_tests.ml +++ b/mllib/common_utils_tests.ml @@ -19,24 +19,13 @@ (* This file tests the Common_utils module. *) open OUnit2 + +open Std_utils open Common_utils (* Utils. *) let assert_equal_string = assert_equal ~printer:(fun x -> x) -let assert_equal_int = assert_equal ~printer:(fun x -> string_of_int x) let assert_equal_int64 = assert_equal ~printer:(fun x -> Int64.to_string x) -let assert_equal_stringlist = assert_equal ~printer:(fun x -> "(" ^ (String.escaped (String.concat "," x)) ^ ")") - -let test_subdirectory ctx - assert_equal_string "" (subdirectory "/foo" "/foo"); - assert_equal_string "" (subdirectory "/foo" "/foo/"); - assert_equal_string "bar" (subdirectory "/foo" "/foo/bar"); - assert_equal_string "bar/baz" (subdirectory "/foo" "/foo/bar/baz") - -(* Test Common_utils.int_of_le32 and Common_utils.le32_of_int. *) -let test_le32 ctx - assert_equal_int64 0x20406080L (int_of_le32 "\x80\x60\x40\x20"); - assert_equal_string "\x80\x60\x40\x20" (le32_of_int 0x20406080L) (* Test Common_utils.parse_size. *) let test_parse_resize ctx @@ -90,59 +79,12 @@ let test_human_size ctx assert_equal_string "3.4G" (human_size 3650722201_L); assert_equal_string "-3.4G" (human_size (-3650722201_L)) -(* Test Common_utils.String.is_prefix. *) -let test_string_is_prefix ctx - assert_bool "String.is_prefix,," (String.is_prefix "" ""); - assert_bool "String.is_prefix,foo," (String.is_prefix "foo" ""); - assert_bool "String.is_prefix,foo,foo" (String.is_prefix "foo" "foo"); - assert_bool "String.is_prefix,foo123,foo" (String.is_prefix "foo123" "foo"); - assert_bool "not (String.is_prefix,,foo" (not (String.is_prefix "" "foo")) - -(* Test Common_utils.String.is_suffix. *) -let test_string_is_suffix ctx - assert_bool "String.is_suffix,," (String.is_suffix "" ""); - assert_bool "String.is_suffix,foo," (String.is_suffix "foo" ""); - assert_bool "String.is_suffix,foo,foo" (String.is_suffix "foo" "foo"); - assert_bool "String.is_suffix,123foo,foo" (String.is_suffix "123foo" "foo"); - assert_bool "not String.is_suffix,,foo" (not (String.is_suffix "" "foo")) - -(* Test Common_utils.String.find. *) -let test_string_find ctx - assert_equal_int 0 (String.find "" ""); - assert_equal_int 0 (String.find "foo" ""); - assert_equal_int 1 (String.find "foo" "o"); - assert_equal_int 3 (String.find "foobar" "bar"); - assert_equal_int (-1) (String.find "" "baz"); - assert_equal_int (-1) (String.find "foobar" "baz") - -(* Test Common_utils.String.lines_split. *) -let test_string_lines_split ctx - assert_equal_stringlist [""] (String.lines_split ""); - assert_equal_stringlist ["A"] (String.lines_split "A"); - assert_equal_stringlist ["A"; ""] (String.lines_split "A\n"); - assert_equal_stringlist ["A"; "B"] (String.lines_split "A\nB"); - assert_equal_stringlist ["A"; "B"; "C"] (String.lines_split "A\nB\nC"); - assert_equal_stringlist ["A"; "B"; "C"; "D"] (String.lines_split "A\nB\nC\nD"); - assert_equal_stringlist ["A\n"] (String.lines_split "A\\"); - assert_equal_stringlist ["A\nB"] (String.lines_split "A\\\nB"); - assert_equal_stringlist ["A"; "B\nC"] (String.lines_split "A\nB\\\nC"); - assert_equal_stringlist ["A"; "B\nC"; "D"] (String.lines_split "A\nB\\\nC\nD"); - assert_equal_stringlist ["A"; "B\nC\nD"] (String.lines_split "A\nB\\\nC\\\nD"); - assert_equal_stringlist ["A\nB"; ""] (String.lines_split "A\\\nB\n"); - assert_equal_stringlist ["A\nB\n"] (String.lines_split "A\\\nB\\\n") - (* Suites declaration. *) let suite "mllib Common_utils" >::: [ - "subdirectory" >:: test_subdirectory; - "numeric.le32" >:: test_le32; "sizes.parse_resize" >:: test_parse_resize; "sizes.human_size" >:: test_human_size; - "strings.is_prefix" >:: test_string_is_prefix; - "strings.is_suffix" >:: test_string_is_suffix; - "strings.find" >:: test_string_find; - "strings.lines_split" >:: test_string_lines_split; ] let () diff --git a/mllib/curl.ml b/mllib/curl.ml index ed0b8960a..ccf98acef 100644 --- a/mllib/curl.ml +++ b/mllib/curl.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils type t = { diff --git a/mllib/getopt_tests.ml b/mllib/getopt_tests.ml index 9d432e922..22e4282fa 100644 --- a/mllib/getopt_tests.ml +++ b/mllib/getopt_tests.ml @@ -22,6 +22,7 @@ open Printf +open Std_utils open Common_utils open Getopt.OptionName diff --git a/mllib/regedit.ml b/mllib/regedit.ml index dd03f5a23..e07700bb1 100644 --- a/mllib/regedit.ml +++ b/mllib/regedit.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/mllib/registry.ml b/mllib/registry.ml index 767092c6d..8d62e3bb5 100644 --- a/mllib/registry.ml +++ b/mllib/registry.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext type node = int64 type value = int64 diff --git a/mllib/xpath_helpers.ml b/mllib/xpath_helpers.ml index d651fab23..e6185bf3d 100644 --- a/mllib/xpath_helpers.ml +++ b/mllib/xpath_helpers.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext (* Parse an xpath expression and return a string/int. Returns * [Some v], or [None] if the expression doesn't match. diff --git a/resize/Makefile.am b/resize/Makefile.am index c35c3a78a..3707d73b4 100644 --- a/resize/Makefile.am +++ b/resize/Makefile.am @@ -61,6 +61,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlprogress \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT @@ -84,6 +85,7 @@ OBJECTS = $(XOBJECTS) endif OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlprogress.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ @@ -91,6 +93,7 @@ OCAMLLINKFLAGS = \ virt_resize_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_resize_LINK = \ @@ -135,7 +138,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/resize/resize.ml b/resize/resize.ml index f9b612e28..66ef9e11d 100644 --- a/resize/resize.ml +++ b/resize/resize.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Unix_utils diff --git a/sparsify/Makefile.am b/sparsify/Makefile.am index 97236829e..a1395ccbd 100644 --- a/sparsify/Makefile.am +++ b/sparsify/Makefile.am @@ -66,6 +66,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlprogress \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT @@ -89,6 +90,7 @@ OBJECTS = $(XOBJECTS) endif OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlprogress.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ @@ -96,6 +98,7 @@ OCAMLLINKFLAGS = \ virt_sparsify_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_sparsify_LINK = \ @@ -142,7 +145,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/sparsify/cmdline.ml b/sparsify/cmdline.ml index 4629aa7a4..6e0594f12 100644 --- a/sparsify/cmdline.ml +++ b/sparsify/cmdline.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Utils diff --git a/sparsify/copying.ml b/sparsify/copying.ml index 9042bd53d..02a53b9b4 100644 --- a/sparsify/copying.ml +++ b/sparsify/copying.ml @@ -23,6 +23,7 @@ open Unix open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Unix_utils diff --git a/sparsify/in_place.ml b/sparsify/in_place.ml index 88f30c0b3..1f3da2c70 100644 --- a/sparsify/in_place.ml +++ b/sparsify/in_place.ml @@ -21,6 +21,7 @@ open Unix open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/sparsify/utils.ml b/sparsify/utils.ml index 3bb64b737..27723c3a2 100644 --- a/sparsify/utils.ml +++ b/sparsify/utils.ml @@ -20,7 +20,7 @@ open Printf -open Common_utils +open Std_utils module G = Guestfs diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index 68cb1814a..c2adb1a6e 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -112,6 +112,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/visit/.libs \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlvisit \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize @@ -137,6 +138,7 @@ OBJECTS = $(XOBJECTS) endif OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ mlvisit.$(MLARCHIVE) \ @@ -145,6 +147,7 @@ OCAMLLINKFLAGS = \ virt_sysprep_DEPENDENCIES = \ $(OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -213,7 +216,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/sysprep/main.ml b/sysprep/main.ml index 82164c62f..ab631c479 100644 --- a/sysprep/main.ml +++ b/sysprep/main.ml @@ -19,6 +19,7 @@ open Unix open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/sysprep/sysprep_operation.ml b/sysprep/sysprep_operation.ml index b2286f642..17d298fc1 100644 --- a/sysprep/sysprep_operation.ml +++ b/sysprep/sysprep_operation.ml @@ -16,10 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_utils - open Printf +open Std_utils +open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/sysprep/sysprep_operation_backup_files.ml b/sysprep/sysprep_operation_backup_files.ml index 6b1a100e6..64df8d758 100644 --- a/sysprep/sysprep_operation_backup_files.ml +++ b/sysprep/sysprep_operation_backup_files.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Visit open Unix_utils.Fnmatch open Sysprep_operation diff --git a/sysprep/sysprep_operation_cron_spool.ml b/sysprep/sysprep_operation_cron_spool.ml index 063f75a83..f48a5201a 100644 --- a/sysprep/sysprep_operation_cron_spool.ml +++ b/sysprep/sysprep_operation_cron_spool.ml @@ -16,9 +16,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Sysprep_operation -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext + +open Sysprep_operation module G = Guestfs diff --git a/sysprep/sysprep_operation_net_hostname.ml b/sysprep/sysprep_operation_net_hostname.ml index 7284d630f..b455e5c93 100644 --- a/sysprep/sysprep_operation_net_hostname.ml +++ b/sysprep/sysprep_operation_net_hostname.ml @@ -16,10 +16,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils -open Sysprep_operation open Common_gettext.Gettext +open Sysprep_operation + module G = Guestfs let net_hostname_perform (g : Guestfs.guestfs) root side_effects diff --git a/sysprep/sysprep_operation_net_hwaddr.ml b/sysprep/sysprep_operation_net_hwaddr.ml index 439da6d81..21cae1be4 100644 --- a/sysprep/sysprep_operation_net_hwaddr.ml +++ b/sysprep/sysprep_operation_net_hwaddr.ml @@ -16,10 +16,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils -open Sysprep_operation open Common_gettext.Gettext +open Sysprep_operation + module G = Guestfs let net_hwaddr_perform (g : Guestfs.guestfs) root side_effects diff --git a/sysprep/sysprep_operation_script.ml b/sysprep/sysprep_operation_script.ml index aa656727e..cf911043a 100644 --- a/sysprep/sysprep_operation_script.ml +++ b/sysprep/sysprep_operation_script.ml @@ -19,9 +19,10 @@ open Printf open Unix -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Getopt.OptionName open Sysprep_operation diff --git a/sysprep/sysprep_operation_user_account.ml b/sysprep/sysprep_operation_user_account.ml index 6f44b9dfd..2a633f5d8 100644 --- a/sysprep/sysprep_operation_user_account.ml +++ b/sysprep/sysprep_operation_user_account.ml @@ -19,6 +19,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext open Getopt.OptionName diff --git a/v2v/DOM.ml b/v2v/DOM.ml index 29ce64fa6..9986fc912 100644 --- a/v2v/DOM.ml +++ b/v2v/DOM.ml @@ -18,6 +18,7 @@ (* Poor man's XML DOM, mutable for ease of modification. *) +open Std_utils open Common_utils open Printf diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 2de99ceb9..8a831a700 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -146,6 +146,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize @@ -170,6 +171,7 @@ OBJECTS = $(XOBJECTS) endif OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlxml.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ @@ -210,6 +212,7 @@ endif virt_v2v_copy_to_local_DEPENDENCIES = \ $(COPY_TO_LOCAL_OBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../common/mlxml/mlxml.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -495,6 +498,7 @@ endif v2v_unit_tests_DEPENDENCIES = \ $(v2v_unit_tests_THEOBJECTS) \ + ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../common/mlxml/mlxml.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -510,7 +514,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/changeuid.ml b/v2v/changeuid.ml index dbb05bc94..639fcfe12 100644 --- a/v2v/changeuid.ml +++ b/v2v/changeuid.ml @@ -21,9 +21,10 @@ open Unix open Printf +open Std_utils open Common_utils -open Common_gettext.Gettext open Unix_utils +open Common_gettext.Gettext open Utils diff --git a/v2v/cmdline.ml b/v2v/cmdline.ml index 70301ab40..a19510b3f 100644 --- a/v2v/cmdline.ml +++ b/v2v/cmdline.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Types diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml index 42a19947b..ffb43564f 100644 --- a/v2v/convert_linux.ml +++ b/v2v/convert_linux.ml @@ -28,8 +28,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils open Types diff --git a/v2v/convert_windows.ml b/v2v/convert_windows.ml index dfb90d079..2c8708878 100644 --- a/v2v/convert_windows.ml +++ b/v2v/convert_windows.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Utils open Types diff --git a/v2v/copy_to_local.ml b/v2v/copy_to_local.ml index 88fd9abde..0a2b7ed75 100644 --- a/v2v/copy_to_local.ml +++ b/v2v/copy_to_local.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Getopt.OptionName open Utils diff --git a/v2v/create_libvirt_xml.ml b/v2v/create_libvirt_xml.ml index 246cacd21..3f22f3764 100644 --- a/v2v/create_libvirt_xml.ml +++ b/v2v/create_libvirt_xml.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/create_ovf.ml b/v2v/create_ovf.ml index 6c7aba6d7..fd7ec5fe8 100644 --- a/v2v/create_ovf.ml +++ b/v2v/create_ovf.ml @@ -18,12 +18,13 @@ (* Create OVF and related files for RHV. *) -open Common_gettext.Gettext -open Common_utils - open Unix open Printf +open Std_utils +open Common_utils +open Common_gettext.Gettext + open Types open Utils open DOM diff --git a/v2v/input_disk.ml b/v2v/input_disk.ml index d28f45ece..a92f3a602 100644 --- a/v2v/input_disk.ml +++ b/v2v/input_disk.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/input_libvirtxml.ml b/v2v/input_libvirtxml.ml index d829ee523..570541d7d 100644 --- a/v2v/input_libvirtxml.ml +++ b/v2v/input_libvirtxml.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Parse_libvirt_xml diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml index b509326dd..e8be68ed7 100644 --- a/v2v/input_ova.ml +++ b/v2v/input_ova.ml @@ -18,9 +18,10 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/input_vmx.ml b/v2v/input_vmx.ml index c48a0155a..bb1650ae9 100644 --- a/v2v/input_vmx.ml +++ b/v2v/input_vmx.ml @@ -19,8 +19,9 @@ open Printf open Scanf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/inspect_source.ml b/v2v/inspect_source.ml index 7476c3d85..e5d1fd3aa 100644 --- a/v2v/inspect_source.ml +++ b/v2v/inspect_source.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/linux.ml b/v2v/linux.ml index 5f40c4196..799654511 100644 --- a/v2v/linux.ml +++ b/v2v/linux.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/linux_bootloaders.ml b/v2v/linux_bootloaders.ml index 33a6dc4e9..b5ad25508 100644 --- a/v2v/linux_bootloaders.ml +++ b/v2v/linux_bootloaders.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/linux_kernels.ml b/v2v/linux_kernels.ml index e8c3a93c6..6e1ca4bf1 100644 --- a/v2v/linux_kernels.ml +++ b/v2v/linux_kernels.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types diff --git a/v2v/modules_list.ml b/v2v/modules_list.ml index 3ee0bd7dc..e3c6d5934 100644 --- a/v2v/modules_list.ml +++ b/v2v/modules_list.ml @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_utils +open Std_utils let input_modules = ref [] and output_modules = ref [] diff --git a/v2v/output_glance.ml b/v2v/output_glance.ml index 3feb2e493..e26bc0732 100644 --- a/v2v/output_glance.ml +++ b/v2v/output_glance.ml @@ -18,9 +18,10 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_libvirt.ml b/v2v/output_libvirt.ml index b3e695387..61e1efddb 100644 --- a/v2v/output_libvirt.ml +++ b/v2v/output_libvirt.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_local.ml b/v2v/output_local.ml index 9c105ef8d..3553150ff 100644 --- a/v2v/output_local.ml +++ b/v2v/output_local.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_null.ml b/v2v/output_null.ml index b0e99b4de..9b31c2d00 100644 --- a/v2v/output_null.ml +++ b/v2v/output_null.ml @@ -18,9 +18,10 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml index 031279cb3..00814e8f0 100644 --- a/v2v/output_qemu.ml +++ b/v2v/output_qemu.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/output_rhv.ml b/v2v/output_rhv.ml index 82e745a94..0c02df612 100644 --- a/v2v/output_rhv.ml +++ b/v2v/output_rhv.ml @@ -16,9 +16,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Unix open Printf diff --git a/v2v/output_vdsm.ml b/v2v/output_vdsm.ml index d8cd20156..361a8e555 100644 --- a/v2v/output_vdsm.ml +++ b/v2v/output_vdsm.ml @@ -16,8 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Unix open Printf diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml index 4ac9b51a5..56f9ea297 100644 --- a/v2v/parse_libvirt_xml.ml +++ b/v2v/parse_libvirt_xml.ml @@ -18,12 +18,13 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils - -open Types +open Common_gettext.Gettext open Xpath_helpers +open Types + type parsed_disk = { p_source_disk : source_disk; p_source : parsed_source; diff --git a/v2v/parse_ovf_from_ova.ml b/v2v/parse_ovf_from_ova.ml index 2a3752776..6dc032407 100644 --- a/v2v/parse_ovf_from_ova.ml +++ b/v2v/parse_ovf_from_ova.ml @@ -18,9 +18,10 @@ (* Parse OVF from an externally produced OVA file. *) -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/parse_vmx.ml b/v2v/parse_vmx.ml index 33ec17d3d..770dc29d3 100644 --- a/v2v/parse_vmx.ml +++ b/v2v/parse_vmx.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/target_bus_assignment.ml b/v2v/target_bus_assignment.ml index a9010c245..de6b0148d 100644 --- a/v2v/target_bus_assignment.ml +++ b/v2v/target_bus_assignment.ml @@ -16,6 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/test-harness/Makefile.am b/v2v/test-harness/Makefile.am index 9a548022a..bcfcdf21d 100644 --- a/v2v/test-harness/Makefile.am +++ b/v2v/test-harness/Makefile.am @@ -42,6 +42,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ + -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/v2v @@ -129,7 +130,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize -I $(abs_top_builddir)/v2v $^ | \ + $(OCAMLFIND) ocamldep -I ../../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize -I $(abs_top_builddir)/v2v $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/test-harness/v2v_test_harness.ml b/v2v/test-harness/v2v_test_harness.ml index 3c29a9430..ba8c5eeab 100644 --- a/v2v/test-harness/v2v_test_harness.ml +++ b/v2v/test-harness/v2v_test_harness.ml @@ -23,6 +23,7 @@ module D = Libvirt.Domain open Unix open Printf +open Std_utils open Common_utils type test_plan = { diff --git a/v2v/utils.ml b/v2v/utils.ml index e0275db53..0dab5816e 100644 --- a/v2v/utils.ml +++ b/v2v/utils.ml @@ -20,8 +20,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext external drive_name : int -> string = "v2v_utils_drive_name" external drive_index : string -> int = "v2v_utils_drive_index" diff --git a/v2v/v2v.ml b/v2v/v2v.ml index 59f5ef17e..f1ce9335a 100644 --- a/v2v/v2v.ml +++ b/v2v/v2v.ml @@ -19,9 +19,10 @@ open Unix open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils open Unix_utils +open Common_gettext.Gettext open Types open Utils diff --git a/v2v/v2v_unit_tests.ml b/v2v/v2v_unit_tests.ml index 7f98e09d3..be0bf0172 100644 --- a/v2v/v2v_unit_tests.ml +++ b/v2v/v2v_unit_tests.ml @@ -18,13 +18,15 @@ (* This file tests individual virt-v2v functions. *) -open OUnit2 -open Types - open Printf +open OUnit2 + +open Std_utils open Common_utils +open Types + let inspect_defaults = { i_type = ""; i_distro = ""; i_arch = ""; i_major_version = 0; i_minor_version = 0; diff --git a/v2v/vCenter.ml b/v2v/vCenter.ml index 468261d3d..d84bf8b58 100644 --- a/v2v/vCenter.ml +++ b/v2v/vCenter.ml @@ -18,6 +18,7 @@ open Printf +open Std_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/windows_virtio.ml b/v2v/windows_virtio.ml index 9891a770c..76af7ab2f 100644 --- a/v2v/windows_virtio.ml +++ b/v2v/windows_virtio.ml @@ -18,8 +18,9 @@ open Printf -open Common_gettext.Gettext +open Std_utils open Common_utils +open Common_gettext.Gettext open Regedit -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 05/41] utils: Split out cleanups into common/cleanups.
Those cleanups which only depend on libc, gnulib or libxml2 are split out into a separate common/cleanups directory. --- .gitignore | 3 +- Makefile.am | 4 +- align/Makefile.am | 2 + builder/Makefile.am | 4 + cat/Makefile.am | 10 +++ common/cleanups/Makefile.am | 35 ++++++++ common/cleanups/cleanups.h | 78 ++++++++++++++++++ common/cleanups/gnulib-cleanups.c | 37 +++++++++ common/cleanups/libxml2-cleanups.c | 94 ++++++++++++++++++++++ .../cleanup.c => cleanups/stdlib-cleanups.c} | 90 +-------------------- common/edit/Makefile.am | 2 + common/options/Makefile.am | 2 + common/parallel/Makefile.am | 2 + common/progress/Makefile.am | 2 + common/utils/Makefile.am | 2 +- common/utils/guestfs-internal-frontend.h | 52 +----------- common/utils/utils.c | 6 ++ common/visit/Makefile.am | 1 + common/windows/Makefile.am | 2 + configure.ac | 1 + customize/Makefile.am | 3 + df/Makefile.am | 2 + diff/Makefile.am | 2 + docs/C_SOURCE_FILES | 5 +- docs/guestfs-hacking.pod | 5 ++ edit/Makefile.am | 2 + erlang/Makefile.am | 2 + fish/Makefile.am | 4 + format/Makefile.am | 2 + fuse/Makefile.am | 10 +++ get-kernel/Makefile.am | 3 + inspector/Makefile.am | 2 + java/Makefile.am | 2 + lib/Makefile.am | 4 + lua/Makefile.am | 2 + make-fs/Makefile.am | 2 + mllib/Makefile.am | 9 ++- ocaml/Makefile.am | 4 +- p2v/Makefile.am | 2 + php/Makefile.am | 2 +- python/Makefile.am | 14 +++- rescue/Makefile.am | 2 + ruby/Rakefile.in | 2 +- sysprep/Makefile.am | 3 + test-tool/Makefile.am | 2 + tests/c-api/Makefile.am | 24 ++++++ tests/charsets/Makefile.am | 2 + tests/disks/Makefile.am | 2 + tests/events/Makefile.am | 2 + tests/mount-local/Makefile.am | 2 + tests/parallel/Makefile.am | 2 + tests/regressions/Makefile.am | 10 +++ utils/boot-analysis/Makefile.am | 2 + utils/boot-benchmark/Makefile.am | 2 + utils/qemu-boot/Makefile.am | 2 + utils/qemu-speed-test/Makefile.am | 2 + v2v/Makefile.am | 4 + 57 files changed, 426 insertions(+), 151 deletions(-) diff --git a/.gitignore b/.gitignore index 991b1ab94..97fe2f050 100644 --- a/.gitignore +++ b/.gitignore @@ -488,6 +488,7 @@ Makefile.in /python/bindtests.py /python/build /python/c-ctype.h +/python/cleanups.h /python/config.h /python/dist /python/examples/guestfs-python.3 @@ -496,11 +497,11 @@ Makefile.in /python/guestfs.pyc /python/guestfs.pyo /python/guestfs-internal-all.h -/python/guestfs-internal-frontend-cleanups.h /python/guestfs-internal-frontend.h /python/ignore-value.h /python/MANIFEST /python/module.c +/python/stdlib-cleanups.c /python/structs.c /python/__pycache__ /python/setup.py diff --git a/Makefile.am b/Makefile.am index 64ac23f2e..7189519fb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,9 @@ SUBDIRS += gnulib/tests endif # Basic source for the library. -SUBDIRS += common/errnostring common/protocol common/qemuopts common/utils +SUBDIRS += common/errnostring common/protocol common/qemuopts +SUBDIRS += common/cleanups +SUBDIRS += common/utils SUBDIRS += lib docs examples po # The daemon and the appliance. diff --git a/align/Makefile.am b/align/Makefile.am index 8d4fce11b..99a80dd73 100644 --- a/align/Makefile.am +++ b/align/Makefile.am @@ -30,6 +30,7 @@ virt_alignment_scan_SOURCES = \ virt_alignment_scan_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -47,6 +48,7 @@ virt_alignment_scan_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/parallel/libparallel.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/builder/Makefile.am b/builder/Makefile.am index 5f0606ca4..cfe302f9d 100644 --- a/builder/Makefile.am +++ b/builder/Makefile.am @@ -100,6 +100,7 @@ virt_builder_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/fish @@ -121,6 +122,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ @@ -138,6 +140,7 @@ endif OCAMLCLIBS = \ -pthread -lpthread \ -lutils \ + -lcleanups \ $(LIBTINFO_LIBS) \ $(LIBCRYPT_LIBS) \ $(LIBLZMA_LIBS) \ @@ -344,6 +347,7 @@ virt_index_validate_CPPFLAGS = \ -I. \ -I$(top_builddir) \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib virt_index_validate_CFLAGS = \ diff --git a/cat/Makefile.am b/cat/Makefile.am index 4b9171937..94e0285af 100644 --- a/cat/Makefile.am +++ b/cat/Makefile.am @@ -38,6 +38,7 @@ virt_cat_SOURCES = \ virt_cat_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -52,6 +53,7 @@ virt_cat_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ @@ -64,6 +66,7 @@ virt_filesystems_SOURCES = \ virt_filesystems_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -78,6 +81,7 @@ virt_filesystems_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ @@ -90,6 +94,7 @@ virt_log_SOURCES = \ virt_log_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -103,6 +108,7 @@ virt_log_CFLAGS = \ virt_log_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ @@ -115,6 +121,7 @@ virt_ls_SOURCES = \ virt_ls_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/visit \ @@ -130,6 +137,7 @@ virt_ls_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/visit/libvisit.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ @@ -142,6 +150,7 @@ virt_tail_SOURCES = \ virt_tail_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -156,6 +165,7 @@ virt_tail_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/common/cleanups/Makefile.am b/common/cleanups/Makefile.am new file mode 100644 index 000000000..f36b8d47c --- /dev/null +++ b/common/cleanups/Makefile.am @@ -0,0 +1,35 @@ +# libguestfs +# 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. + +include $(top_srcdir)/subdir-rules.mk + +noinst_LTLIBRARIES = libcleanups.la + +# These are split into several source files so that we can link +# to libcleanup.a and have it work even if gnulib or libxml2 are +# not linked to the main program. +libcleanups_la_SOURCES = \ + cleanups.h \ + stdlib-cleanups.c \ + gnulib-cleanups.c \ + libxml2-cleanups.c +libcleanups_la_CPPFLAGS = \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib +libcleanups_la_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(GCC_VISIBILITY_HIDDEN) \ + $(LIBXML2_CFLAGS) diff --git a/common/cleanups/cleanups.h b/common/cleanups/cleanups.h new file mode 100644 index 000000000..211bce98a --- /dev/null +++ b/common/cleanups/cleanups.h @@ -0,0 +1,78 @@ +/* libguestfs + * Copyright (C) 2013-2017 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GUESTFS_CLEANUPS_H_ +#define GUESTFS_CLEANUPS_H_ + +#ifdef HAVE_ATTRIBUTE_CLEANUP +#define CLEANUP_FREE \ + __attribute__((cleanup(guestfs_int_cleanup_free))) +#define CLEANUP_HASH_FREE \ + __attribute__((cleanup(guestfs_int_cleanup_hash_free))) +#define CLEANUP_UNLINK_FREE \ + __attribute__((cleanup(guestfs_int_cleanup_unlink_free))) +#define CLEANUP_FCLOSE \ + __attribute__((cleanup(guestfs_int_cleanup_fclose))) +#define CLEANUP_PCLOSE \ + __attribute__((cleanup(guestfs_int_cleanup_pclose))) +#define CLEANUP_XMLFREE \ + __attribute__((cleanup(guestfs_int_cleanup_xmlFree))) +#define CLEANUP_XMLBUFFERFREE \ + __attribute__((cleanup(guestfs_int_cleanup_xmlBufferFree))) +#define CLEANUP_XMLFREEDOC \ + __attribute__((cleanup(guestfs_int_cleanup_xmlFreeDoc))) +#define CLEANUP_XMLFREEURI \ + __attribute__((cleanup(guestfs_int_cleanup_xmlFreeURI))) +#define CLEANUP_XMLFREETEXTWRITER \ + __attribute__((cleanup(guestfs_int_cleanup_xmlFreeTextWriter))) +#define CLEANUP_XMLXPATHFREECONTEXT \ + __attribute__((cleanup(guestfs_int_cleanup_xmlXPathFreeContext))) +#define CLEANUP_XMLXPATHFREEOBJECT \ + __attribute__((cleanup(guestfs_int_cleanup_xmlXPathFreeObject))) +#else +#define CLEANUP_FREE +#define CLEANUP_HASH_FREE +#define CLEANUP_UNLINK_FREE +#define CLEANUP_FCLOSE +#define CLEANUP_PCLOSE +#define CLEANUP_XMLFREE +#define CLEANUP_XMLBUFFERFREE +#define CLEANUP_XMLFREEDOC +#define CLEANUP_XMLFREEURI +#define CLEANUP_XMLFREETEXTWRITER +#define CLEANUP_XMLXPATHFREECONTEXT +#define CLEANUP_XMLXPATHFREEOBJECT +#endif + +/* These functions are used internally by the CLEANUP_* macros. + * Don't call them directly. + */ +extern void guestfs_int_cleanup_free (void *ptr); +extern void guestfs_int_cleanup_hash_free (void *ptr); +extern void guestfs_int_cleanup_unlink_free (char **ptr); +extern void guestfs_int_cleanup_fclose (void *ptr); +extern void guestfs_int_cleanup_pclose (void *ptr); +extern void guestfs_int_cleanup_xmlFree (void *ptr); +extern void guestfs_int_cleanup_xmlBufferFree (void *ptr); +extern void guestfs_int_cleanup_xmlFreeDoc (void *ptr); +extern void guestfs_int_cleanup_xmlFreeURI (void *ptr); +extern void guestfs_int_cleanup_xmlFreeTextWriter (void *ptr); +extern void guestfs_int_cleanup_xmlXPathFreeContext (void *ptr); +extern void guestfs_int_cleanup_xmlXPathFreeObject (void *ptr); + +#endif /* GUESTFS_CLEANUPS_H_ */ diff --git a/common/cleanups/gnulib-cleanups.c b/common/cleanups/gnulib-cleanups.c new file mode 100644 index 000000000..45b99c513 --- /dev/null +++ b/common/cleanups/gnulib-cleanups.c @@ -0,0 +1,37 @@ +/* libguestfs + * Copyright (C) 2013-2017 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 <unistd.h> +#include <string.h> + +#include "hash.h" + +#include "cleanups.h" + +void +guestfs_int_cleanup_hash_free (void *ptr) +{ + Hash_table *h = * (Hash_table **) ptr; + + if (h) + hash_free (h); +} diff --git a/common/cleanups/libxml2-cleanups.c b/common/cleanups/libxml2-cleanups.c new file mode 100644 index 000000000..066573fef --- /dev/null +++ b/common/cleanups/libxml2-cleanups.c @@ -0,0 +1,94 @@ +/* libguestfs + * Copyright (C) 2013-2017 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 <unistd.h> +#include <string.h> + +#include <libxml/uri.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xmlwriter.h> + +#include "cleanups.h" + +void +guestfs_int_cleanup_xmlFree (void *ptr) +{ + xmlChar *buf = * (xmlChar **) ptr; + + if (buf) + xmlFree (buf); +} + +void +guestfs_int_cleanup_xmlBufferFree (void *ptr) +{ + xmlBufferPtr xb = * (xmlBufferPtr *) ptr; + + if (xb) + xmlBufferFree (xb); +} + +void +guestfs_int_cleanup_xmlFreeDoc (void *ptr) +{ + xmlDocPtr doc = * (xmlDocPtr *) ptr; + + if (doc) + xmlFreeDoc (doc); +} + +void +guestfs_int_cleanup_xmlFreeURI (void *ptr) +{ + xmlURIPtr uri = * (xmlURIPtr *) ptr; + + if (uri) + xmlFreeURI (uri); +} + +void +guestfs_int_cleanup_xmlFreeTextWriter (void *ptr) +{ + xmlTextWriterPtr xo = * (xmlTextWriterPtr *) ptr; + + if (xo) + xmlFreeTextWriter (xo); +} + +void +guestfs_int_cleanup_xmlXPathFreeContext (void *ptr) +{ + xmlXPathContextPtr ctx = * (xmlXPathContextPtr *) ptr; + + if (ctx) + xmlXPathFreeContext (ctx); +} + +void +guestfs_int_cleanup_xmlXPathFreeObject (void *ptr) +{ + xmlXPathObjectPtr obj = * (xmlXPathObjectPtr *) ptr; + + if (obj) + xmlXPathFreeObject (obj); +} diff --git a/common/utils/cleanup.c b/common/cleanups/stdlib-cleanups.c similarity index 62% rename from common/utils/cleanup.c rename to common/cleanups/stdlib-cleanups.c index 6c4558c39..0512a86a2 100644 --- a/common/utils/cleanup.c +++ b/common/cleanups/stdlib-cleanups.c @@ -1,5 +1,5 @@ /* libguestfs - * Copyright (C) 2013 Red Hat Inc. + * Copyright (C) 2013-2017 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -63,15 +63,7 @@ #include <unistd.h> #include <string.h> -#include <libxml/uri.h> -#include <libxml/tree.h> -#include <libxml/xpath.h> -#include <libxml/xmlwriter.h> - -#include "hash.h" - -#include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "cleanups.h" void guestfs_int_cleanup_free (void *ptr) @@ -80,21 +72,6 @@ guestfs_int_cleanup_free (void *ptr) } void -guestfs_int_cleanup_free_string_list (char ***ptr) -{ - guestfs_int_free_string_list (*ptr); -} - -void -guestfs_int_cleanup_hash_free (void *ptr) -{ - Hash_table *h = * (Hash_table **) ptr; - - if (h) - hash_free (h); -} - -void guestfs_int_cleanup_unlink_free (char **ptr) { char *filename = *ptr; @@ -106,69 +83,6 @@ guestfs_int_cleanup_unlink_free (char **ptr) } void -guestfs_int_cleanup_xmlFree (void *ptr) -{ - xmlChar *buf = * (xmlChar **) ptr; - - if (buf) - xmlFree (buf); -} - -void -guestfs_int_cleanup_xmlBufferFree (void *ptr) -{ - xmlBufferPtr xb = * (xmlBufferPtr *) ptr; - - if (xb) - xmlBufferFree (xb); -} - -void -guestfs_int_cleanup_xmlFreeDoc (void *ptr) -{ - xmlDocPtr doc = * (xmlDocPtr *) ptr; - - if (doc) - xmlFreeDoc (doc); -} - -void -guestfs_int_cleanup_xmlFreeURI (void *ptr) -{ - xmlURIPtr uri = * (xmlURIPtr *) ptr; - - if (uri) - xmlFreeURI (uri); -} - -void -guestfs_int_cleanup_xmlFreeTextWriter (void *ptr) -{ - xmlTextWriterPtr xo = * (xmlTextWriterPtr *) ptr; - - if (xo) - xmlFreeTextWriter (xo); -} - -void -guestfs_int_cleanup_xmlXPathFreeContext (void *ptr) -{ - xmlXPathContextPtr ctx = * (xmlXPathContextPtr *) ptr; - - if (ctx) - xmlXPathFreeContext (ctx); -} - -void -guestfs_int_cleanup_xmlXPathFreeObject (void *ptr) -{ - xmlXPathObjectPtr obj = * (xmlXPathObjectPtr *) ptr; - - if (obj) - xmlXPathFreeObject (obj); -} - -void guestfs_int_cleanup_fclose (void *ptr) { FILE *f = * (FILE **) ptr; diff --git a/common/edit/Makefile.am b/common/edit/Makefile.am index 592f6fc36..93a1bf5b0 100644 --- a/common/edit/Makefile.am +++ b/common/edit/Makefile.am @@ -26,10 +26,12 @@ libedit_la_SOURCES = \ file-edit.h libedit_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib libedit_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) libedit_la_LIBADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la diff --git a/common/options/Makefile.am b/common/options/Makefile.am index 4aab6b110..372653e82 100644 --- a/common/options/Makefile.am +++ b/common/options/Makefile.am @@ -35,6 +35,7 @@ liboptions_la_SOURCES = \ uri.c liboptions_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib @@ -44,6 +45,7 @@ liboptions_la_CFLAGS = \ $(LIBXML2_CFLAGS) liboptions_la_LIBADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBCONFIG_LIBS) \ $(LIBXML2_LIBS) \ diff --git a/common/parallel/Makefile.am b/common/parallel/Makefile.am index 5518e8909..ba541bc4e 100644 --- a/common/parallel/Makefile.am +++ b/common/parallel/Makefile.am @@ -31,6 +31,7 @@ libparallel_la_SOURCES = \ parallel.h libparallel_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -43,6 +44,7 @@ libparallel_la_CFLAGS = \ libparallel_la_LIBADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/common/progress/Makefile.am b/common/progress/Makefile.am index afb03c876..d70b34bf6 100644 --- a/common/progress/Makefile.am +++ b/common/progress/Makefile.am @@ -26,6 +26,7 @@ libprogress_la_SOURCES = \ progress.h libprogress_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib libprogress_la_CFLAGS = \ @@ -33,5 +34,6 @@ libprogress_la_CFLAGS = \ $(LIBTINFO_CFLAGS) libprogress_la_LIBADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBTINFO_LIBS) diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index 81a567b86..485909bbe 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -34,7 +34,6 @@ noinst_LTLIBRARIES = libutils.la libutils_la_SOURCES = \ ../../lib/guestfs.h \ - cleanup.c \ guestfs-internal-frontend.h \ guestfs-internal-frontend-cleanups.h \ structs-cleanup.c \ @@ -46,6 +45,7 @@ libutils_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DGUESTFS_PRIVATE=1 \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/lib -I$(top_builddir)/lib libutils_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ diff --git a/common/utils/guestfs-internal-frontend.h b/common/utils/guestfs-internal-frontend.h index e48f4eb49..dacc92e49 100644 --- a/common/utils/guestfs-internal-frontend.h +++ b/common/utils/guestfs-internal-frontend.h @@ -35,48 +35,16 @@ #include <stdbool.h> #include "guestfs-internal-all.h" +#include "cleanups.h" #define _(str) dgettext(PACKAGE, (str)) #define N_(str) dgettext(PACKAGE, (str)) #ifdef HAVE_ATTRIBUTE_CLEANUP -#define CLEANUP_FREE __attribute__((cleanup(guestfs_int_cleanup_free))) #define CLEANUP_FREE_STRING_LIST \ __attribute__((cleanup(guestfs_int_cleanup_free_string_list))) -#define CLEANUP_HASH_FREE \ - __attribute__((cleanup(guestfs_int_cleanup_hash_free))) -#define CLEANUP_UNLINK_FREE \ - __attribute__((cleanup(guestfs_int_cleanup_unlink_free))) -#define CLEANUP_XMLFREE \ - __attribute__((cleanup(guestfs_int_cleanup_xmlFree))) -#define CLEANUP_XMLBUFFERFREE \ - __attribute__((cleanup(guestfs_int_cleanup_xmlBufferFree))) -#define CLEANUP_XMLFREEDOC \ - __attribute__((cleanup(guestfs_int_cleanup_xmlFreeDoc))) -#define CLEANUP_XMLFREEURI \ - __attribute__((cleanup(guestfs_int_cleanup_xmlFreeURI))) -#define CLEANUP_XMLFREETEXTWRITER \ - __attribute__((cleanup(guestfs_int_cleanup_xmlFreeTextWriter))) -#define CLEANUP_XMLXPATHFREECONTEXT \ - __attribute__((cleanup(guestfs_int_cleanup_xmlXPathFreeContext))) -#define CLEANUP_XMLXPATHFREEOBJECT \ - __attribute__((cleanup(guestfs_int_cleanup_xmlXPathFreeObject))) -#define CLEANUP_FCLOSE __attribute__((cleanup(guestfs_int_cleanup_fclose))) -#define CLEANUP_PCLOSE __attribute__((cleanup(guestfs_int_cleanup_pclose))) #else -#define CLEANUP_FREE #define CLEANUP_FREE_STRING_LIST -#define CLEANUP_HASH_FREE -#define CLEANUP_UNLINK_FREE -#define CLEANUP_XMLFREE -#define CLEANUP_XMLBUFFERFREE -#define CLEANUP_XMLFREEDOC -#define CLEANUP_XMLFREEURI -#define CLEANUP_XMLFREETEXTWRITER -#define CLEANUP_XMLXPATHFREECONTEXT -#define CLEANUP_XMLXPATHFREEOBJECT -#define CLEANUP_FCLOSE -#define CLEANUP_PCLOSE #endif /* utils.c */ @@ -101,6 +69,7 @@ extern void guestfs_int_fadvise_noreuse (int fd); //extern void guestfs_int_fadvise_dontneed (int fd); //extern void guestfs_int_fadvise_willneed (int fd); extern char *guestfs_int_shell_unquote (const char *str); +extern void guestfs_int_cleanup_free_string_list (char ***ptr); /* uefi.c */ struct uefi_firmware { @@ -114,23 +83,6 @@ extern struct uefi_firmware guestfs_int_uefi_i386_firmware[]; extern struct uefi_firmware guestfs_int_uefi_x86_64_firmware[]; extern struct uefi_firmware guestfs_int_uefi_aarch64_firmware[]; -/* These functions are used internally by the CLEANUP_* macros. - * Don't call them directly. - */ -extern void guestfs_int_cleanup_free (void *ptr); -extern void guestfs_int_cleanup_free_string_list (char ***ptr); -extern void guestfs_int_cleanup_hash_free (void *ptr); -extern void guestfs_int_cleanup_unlink_free (char **ptr); -extern void guestfs_int_cleanup_xmlFree (void *ptr); -extern void guestfs_int_cleanup_xmlBufferFree (void *ptr); -extern void guestfs_int_cleanup_xmlFreeDoc (void *ptr); -extern void guestfs_int_cleanup_xmlFreeURI (void *ptr); -extern void guestfs_int_cleanup_xmlFreeTextWriter (void *ptr); -extern void guestfs_int_cleanup_xmlXPathFreeContext (void *ptr); -extern void guestfs_int_cleanup_xmlXPathFreeObject (void *ptr); -extern void guestfs_int_cleanup_fclose (void *ptr); -extern void guestfs_int_cleanup_pclose (void *ptr); - /* These are in a separate header so the header can be generated. * Don't include the following file directly: */ diff --git a/common/utils/utils.c b/common/utils/utils.c index bdc8449a2..4ac214ded 100644 --- a/common/utils/utils.c +++ b/common/utils/utils.c @@ -624,3 +624,9 @@ guestfs_int_shell_unquote (const char *str) return strdup (str); } + +void +guestfs_int_cleanup_free_string_list (char ***ptr) +{ + guestfs_int_free_string_list (*ptr); +} diff --git a/common/visit/Makefile.am b/common/visit/Makefile.am index e95954a11..519088769 100644 --- a/common/visit/Makefile.am +++ b/common/visit/Makefile.am @@ -27,6 +27,7 @@ libvisit_la_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils libvisit_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ diff --git a/common/windows/Makefile.am b/common/windows/Makefile.am index 043252b71..16cfc6ce0 100644 --- a/common/windows/Makefile.am +++ b/common/windows/Makefile.am @@ -26,6 +26,7 @@ libwindows_la_SOURCES = \ windows.h libwindows_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib @@ -33,5 +34,6 @@ libwindows_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) libwindows_la_LIBADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LTLIBINTL) diff --git a/configure.ac b/configure.ac index eba149241..33cdbd39e 100644 --- a/configure.ac +++ b/configure.ac @@ -182,6 +182,7 @@ AC_CONFIG_FILES([Makefile builder/test-simplestreams/virt-builder/repos.d/cirros.conf builder/test-website/virt-builder/repos.d/libguestfs.conf cat/Makefile + common/cleanups/Makefile common/errnostring/Makefile common/edit/Makefile common/miniexpect/Makefile diff --git a/customize/Makefile.am b/customize/Makefile.am index 674134b70..815513b25 100644 --- a/customize/Makefile.am +++ b/customize/Makefile.am @@ -96,6 +96,7 @@ libcustomize_a_CPPFLAGS = \ -I$(top_builddir) \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/common/cleanups \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/common/edit @@ -120,6 +121,7 @@ endif OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ @@ -159,6 +161,7 @@ OCAMLLINKFLAGS = \ OCAMLCLIBS = \ -lutils \ + -lcleanups \ $(LIBTINFO_LIBS) \ $(LIBCRYPT_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/df/Makefile.am b/df/Makefile.am index 8725402bc..30faf3ee1 100644 --- a/df/Makefile.am +++ b/df/Makefile.am @@ -35,6 +35,7 @@ virt_df_SOURCES = \ virt_df_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -51,6 +52,7 @@ virt_df_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/parallel/libparallel.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/diff/Makefile.am b/diff/Makefile.am index 5e71b74de..06378dc78 100644 --- a/diff/Makefile.am +++ b/diff/Makefile.am @@ -30,6 +30,7 @@ virt_diff_SOURCES = \ virt_diff_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/visit -I$(top_builddir)/common/visit \ @@ -45,6 +46,7 @@ virt_diff_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/visit/libvisit.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 71c17603f..3382fce56 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -11,6 +11,10 @@ cat/filesystems.c cat/log.c cat/ls.c cat/tail.c +common/cleanups/cleanups.h +common/cleanups/gnulib-cleanups.c +common/cleanups/libxml2-cleanups.c +common/cleanups/stdlib-cleanups.c common/edit/file-edit.c common/edit/file-edit.h common/miniexpect/miniexpect.c @@ -42,7 +46,6 @@ common/progress/progress.h common/qemuopts/qemuopts-tests.c common/qemuopts/qemuopts.c common/qemuopts/qemuopts.h -common/utils/cleanup.c common/utils/guestfs-internal-frontend-cleanups.h common/utils/guestfs-internal-frontend.h common/utils/structs-cleanup.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index beb44d2dc..fdb7a9e63 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -83,6 +83,11 @@ subdirectory: =over 4 +=item F<common/cleanups> + +Common code for implementing C<CLEANUP_FREE> (and similar) macros used +to automatically free pointers at the end of the current code block. + =item F<common/edit> Common code for interactively and non-interactively editing files diff --git a/edit/Makefile.am b/edit/Makefile.am index f98e374f5..9fb9e5a12 100644 --- a/edit/Makefile.am +++ b/edit/Makefile.am @@ -30,6 +30,7 @@ virt_edit_SOURCES = \ virt_edit_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/edit -I$(top_builddir)/common/edit \ @@ -46,6 +47,7 @@ virt_edit_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/erlang/Makefile.am b/erlang/Makefile.am index 75b3ec9d5..93f6f1e3d 100644 --- a/erlang/Makefile.am +++ b/erlang/Makefile.am @@ -80,6 +80,7 @@ erl_guestfs_SOURCES = \ erl_guestfs_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ @@ -93,6 +94,7 @@ erl_guestfs_LDADD = \ $(ERLANG_LIB_DIR_erl_interface)/lib/libei.a \ -lpthread \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/fish/Makefile.am b/fish/Makefile.am index 9c07761e2..d74a70af7 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -107,12 +107,14 @@ librc_protocol_la_CFLAGS = -Wall -Wno-unused -fno-strict-aliasing # lots of warnings so we must compile it in a separate mini-library. libcmds_la_SOURCES = cmds-gperf.c libcmds_la_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib libcmds_la_CFLAGS libcmds_la_LIBADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(LTLIBINTL) cmds-gperf.c: cmds-gperf.gperf @@ -123,6 +125,7 @@ cmds-gperf.c: cmds-gperf.gperf guestfish_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/edit -I$(top_builddir)/common/edit \ @@ -142,6 +145,7 @@ guestfish_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/progress/libprogress.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBCONFIG_LIBS) \ diff --git a/format/Makefile.am b/format/Makefile.am index 2d3cc774c..fa04c86ea 100644 --- a/format/Makefile.am +++ b/format/Makefile.am @@ -30,6 +30,7 @@ virt_format_SOURCES = \ virt_format_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -44,6 +45,7 @@ virt_format_CFLAGS = \ virt_format_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/fuse/Makefile.am b/fuse/Makefile.am index c3d4398a0..486ab0048 100644 --- a/fuse/Makefile.am +++ b/fuse/Makefile.am @@ -38,6 +38,7 @@ guestmount_SOURCES = \ guestmount_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -53,6 +54,7 @@ guestmount_LDADD = \ $(FUSE_LIBS) \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBCONFIG_LIBS) \ $(LIBXML2_LIBS) \ @@ -67,6 +69,7 @@ guestunmount_SOURCES = \ guestunmount_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -79,6 +82,7 @@ guestunmount_CFLAGS = \ guestunmount_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ @@ -140,6 +144,7 @@ test_fuse_SOURCES = \ test-fuse.c test_fuse_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib @@ -149,6 +154,7 @@ test_fuse_CFLAGS = \ test_fuse_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ @@ -159,6 +165,7 @@ test_guestmount_fd_SOURCES = \ test-guestmount-fd.c test_guestmount_fd_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib @@ -168,6 +175,7 @@ test_guestmount_fd_CFLAGS = \ test_guestmount_fd_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ @@ -178,6 +186,7 @@ test_guestunmount_fd_SOURCES = \ test-guestunmount-fd.c test_guestunmount_fd_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(srcdir)/../gnulib/lib -I../gnulib/lib @@ -187,6 +196,7 @@ test_guestunmount_fd_CFLAGS = \ test_guestunmount_fd_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/get-kernel/Makefile.am b/get-kernel/Makefile.am index c6454d7a4..16cf90eb9 100644 --- a/get-kernel/Makefile.am +++ b/get-kernel/Makefile.am @@ -43,6 +43,7 @@ virt_get_kernel_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/fish @@ -59,6 +60,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) # installed copy of libguestfs. OCAMLPACKAGES = \ -package str,unix \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/common/utils/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ @@ -72,6 +74,7 @@ endif OCAMLCLIBS = \ -pthread -lpthread \ -lutils \ + -lcleanups \ $(LIBXML2_LIBS) \ $(LIBINTL) \ -lgnu diff --git a/inspector/Makefile.am b/inspector/Makefile.am index 753e2c93c..980852cde 100644 --- a/inspector/Makefile.am +++ b/inspector/Makefile.am @@ -58,6 +58,7 @@ virt_inspector_SOURCES = \ virt_inspector_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -71,6 +72,7 @@ virt_inspector_CFLAGS = \ virt_inspector_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/java/Makefile.am b/java/Makefile.am index ea64f5525..089ac0177 100644 --- a/java/Makefile.am +++ b/java/Makefile.am @@ -108,6 +108,7 @@ libguestfs_jni_la_SOURCES = \ libguestfs_jni_la_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib @@ -117,6 +118,7 @@ libguestfs_jni_la_CFLAGS = \ libguestfs_jni_la_LIBADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la libguestfs_jni_la_LDFLAGS = -version-info $(JNI_VERSION_INFO) -shared diff --git a/lib/Makefile.am b/lib/Makefile.am index 360ce9c92..90c657514 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -138,6 +138,7 @@ libguestfs_la_CPPFLAGS = \ -I$(top_srcdir)/common/errnostring -I$(top_builddir)/common/errnostring \ -I$(top_srcdir)/common/protocol -I$(top_builddir)/common/protocol \ -I$(top_srcdir)/common/qemuopts -I$(top_builddir)/common/qemuopts \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib @@ -155,6 +156,7 @@ libguestfs_la_LIBADD = \ ../common/protocol/libprotocol.la \ ../common/qemuopts/libqemuopts.la \ ../common/utils/libutils.la \ + ../common/cleanups/libcleanups.la \ $(PCRE_LIBS) $(MAGIC_LIBS) \ $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ $(SELINUX_LIBS) \ @@ -214,6 +216,7 @@ check_PROGRAMS = unit-tests unit_tests_SOURCES = unit-tests.c unit_tests_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I. unit_tests_CFLAGS = \ @@ -223,6 +226,7 @@ unit_tests_CFLAGS = \ # library. unit_tests_LDADD = \ ../common/utils/libutils.la \ + ../common/cleanups/libcleanups.la \ $(libguestfs_la_OBJECTS) \ $(libguestfs_la_LIBADD) diff --git a/lua/Makefile.am b/lua/Makefile.am index f90c1d7cb..83d1621a5 100644 --- a/lua/Makefile.am +++ b/lua/Makefile.am @@ -41,6 +41,7 @@ libluaguestfs_la_SOURCES = lua-guestfs.c libluaguestfs_la_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib @@ -50,6 +51,7 @@ libluaguestfs_la_CFLAGS = \ libluaguestfs_la_LIBADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/make-fs/Makefile.am b/make-fs/Makefile.am index bef0e7bf8..7b5dee127 100644 --- a/make-fs/Makefile.am +++ b/make-fs/Makefile.am @@ -30,6 +30,7 @@ virt_make_fs_SOURCES = \ virt_make_fs_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -43,6 +44,7 @@ virt_make_fs_CFLAGS = \ virt_make_fs_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LTLIBINTL) \ diff --git a/mllib/Makefile.am b/mllib/Makefile.am index 5f6f7fa85..ee251e99d 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -86,6 +86,7 @@ libmllib_a_CPPFLAGS = \ -I$(top_builddir) \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/common/cleanups \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/common/options \ @@ -105,6 +106,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ @@ -121,6 +123,7 @@ endif OCAMLCLIBS = \ -lutils \ + -lcleanups \ $(LIBTINFO_LIBS) \ $(LIBCRYPT_LIBS) \ $(LIBVIRT_LIBS) \ @@ -198,7 +201,8 @@ common_utils_tests_DEPENDENCIES = \ $(MLLIB_CMA) \ $(top_srcdir)/ocaml-link.sh common_utils_tests_LINK = \ - $(top_srcdir)/ocaml-link.sh -cclib '-lutils $(LIBXML2_LIBS) -lgnu' -- \ + $(top_srcdir)/ocaml-link.sh \ + -cclib '-lutils -lcleanups $(LIBXML2_LIBS) -lgnu' -- \ $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ $(common_utils_tests_THEOBJECTS) -o $@ @@ -209,7 +213,8 @@ getopt_tests_DEPENDENCIES = \ $(MLLIB_CMA) \ $(top_srcdir)/ocaml-link.sh getopt_tests_LINK = \ - $(top_srcdir)/ocaml-link.sh -cclib '-lutils $(LIBXML2_LIBS) -lgnu' -- \ + $(top_srcdir)/ocaml-link.sh \ + -cclib '-lutils -lcleanups $(LIBXML2_LIBS) -lgnu' -- \ $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ $(getopt_tests_THEOBJECTS) -o $@ diff --git a/ocaml/Makefile.am b/ocaml/Makefile.am index 57ecd608b..d85e8ea6d 100644 --- a/ocaml/Makefile.am +++ b/ocaml/Makefile.am @@ -82,6 +82,7 @@ endif libguestfsocaml_a_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ -I$(top_builddir) -I$(OCAMLLIB) -I$(top_srcdir)/ocaml \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib -I../gnulib/lib @@ -94,7 +95,8 @@ libguestfsocaml_a_SOURCES = \ guestfs-c.c \ guestfs-c-actions.c \ guestfs-c-errnos.c \ - ../common/utils/utils.c + ../common/utils/utils.c \ + ../common/cleanups/stdlib-cleanups.c if HAVE_OCAMLDOC diff --git a/p2v/Makefile.am b/p2v/Makefile.am index 12509c369..c1473bd7a 100644 --- a/p2v/Makefile.am +++ b/p2v/Makefile.am @@ -98,6 +98,7 @@ virt_p2v_SOURCES = \ virt_p2v_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/miniexpect -I$(top_builddir)/common/miniexpect \ @@ -113,6 +114,7 @@ virt_p2v_CFLAGS = \ virt_p2v_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/common/miniexpect/libminiexpect.la \ $(PCRE_LIBS) \ $(LIBXML2_LIBS) \ diff --git a/php/Makefile.am b/php/Makefile.am index a974cdf21..74921d388 100644 --- a/php/Makefile.am +++ b/php/Makefile.am @@ -38,7 +38,7 @@ php_DATA = guestfs_php.ini # and we need to add the library to EXTRA_LDFLAGS. all: check-builddir-equals-srcdir extension/config.h $(MAKE) -C extension \ - EXTRA_INCLUDES="-I$(abs_srcdir)/../common/utils -I$(abs_srcdir)/../lib" \ + EXTRA_INCLUDES="-I$(abs_srcdir)/../common/cleanups -I$(abs_srcdir)/../common/utils -I$(abs_srcdir)/../lib" \ EXTRA_LDFLAGS="-L$(abs_srcdir)/../lib/.libs -lguestfs" \ EXTRA_CFLAGS="-DGUESTFS_PRIVATE=1" \ all diff --git a/python/Makefile.am b/python/Makefile.am index ae90aa01d..fd0825648 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -67,6 +67,7 @@ libguestfsmod_la_SOURCES = \ libguestfsmod_la_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ $(PYTHON_CFLAGS) \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib @@ -75,6 +76,7 @@ libguestfsmod_la_CFLAGS = \ libguestfsmod_la_LIBADD = \ $(top_builddir)/common/utils/libutils_la-utils.lo \ + $(top_builddir)/common/cleanups/libcleanups_la-stdlib-cleanups.lo \ $(top_builddir)/lib/libguestfs.la libguestfsmod_la_LDFLAGS = -avoid-version -shared -module -shrext $(PYTHON_EXT_SUFFIX) @@ -97,11 +99,12 @@ setup-install: setup.py stamp-extra-files # to hard-link any extra files we need into the local directory. stamp-extra-files: \ c-ctype.h \ + cleanups.h \ config.h \ guestfs-internal-all.h \ - guestfs-internal-frontend-cleanups.h \ guestfs-internal-frontend.h \ ignore-value.h \ + stdlib-cleanups.c \ utils.c touch $@ @@ -111,18 +114,21 @@ config.h: c-ctype.h: ln $(top_srcdir)/gnulib/lib/c-ctype.h $@ +cleanups.h: + ln $(top_srcdir)/common/cleanups/cleanups.h $@ + ignore-value.h: ln $(top_srcdir)/gnulib/lib/ignore-value.h $@ guestfs-internal-all.h: ln $(top_srcdir)/lib/guestfs-internal-all.h $@ -guestfs-internal-frontend-cleanups.h: - ln $(top_srcdir)/common/utils/guestfs-internal-frontend-cleanups.h $@ - guestfs-internal-frontend.h: ln $(top_srcdir)/common/utils/guestfs-internal-frontend.h $@ +stdlib-cleanups.c: + ln $(top_srcdir)/common/cleanups/stdlib-cleanups.c $@ + utils.c: ln $(top_srcdir)/common/utils/utils.c $@ diff --git a/rescue/Makefile.am b/rescue/Makefile.am index d478c8e3d..20950d2f8 100644 --- a/rescue/Makefile.am +++ b/rescue/Makefile.am @@ -35,6 +35,7 @@ virt_rescue_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -DGUESTFS_PRIVATE=1 \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -49,6 +50,7 @@ virt_rescue_LDADD = \ $(top_builddir)/common/windows/libwindows.la \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBCONFIG_LIBS) \ $(LIBXML2_LIBS) \ diff --git a/ruby/Rakefile.in b/ruby/Rakefile.in index 18ec311c5..ee4222501 100644 --- a/ruby/Rakefile.in +++ b/ruby/Rakefile.in @@ -65,7 +65,7 @@ CLOBBER.include [ "@builddir@/config.save", "@builddir@/ext/**/mkmf.log", # Build locally file MAKEFILE => EXT_CONF do |t| - unless sh "top_srcdir=$(pwd)/@top_srcdir@; top_builddir=$(pwd)/@top_builddir@; export ARCHFLAGS=\"-arch $(uname -m)\"; mkdir -p @builddir@/ext/guestfs; cd @builddir@/ext/guestfs; @RUBY@ #{EXT_CONF} --with-_guestfs-include=$top_srcdir/common/utils:$top_srcdir/lib:$top_builddir --with-_guestfs-lib=$top_builddir/lib/.libs" + unless sh "top_srcdir=$(pwd)/@top_srcdir@; top_builddir=$(pwd)/@top_builddir@; export ARCHFLAGS=\"-arch $(uname -m)\"; mkdir -p @builddir@/ext/guestfs; cd @builddir@/ext/guestfs; @RUBY@ #{EXT_CONF} --with-_guestfs-include=$top_srcdir/common/cleanups:$top_srcdir/common/utils:$top_srcdir/lib:$top_builddir --with-_guestfs-lib=$top_builddir/lib/.libs" $stderr.puts "Failed to run extconf" break end diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index c2adb1a6e..fcd2923f5 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -92,6 +92,7 @@ virt_sysprep_CPPFLAGS = \ -I$(top_builddir) \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/common/cleanups \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/fish @@ -107,6 +108,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) # installed copy of libguestfs. OCAMLPACKAGES = \ -package str,unix \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/common/utils/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ @@ -123,6 +125,7 @@ endif OCAMLCLIBS = \ -lvisit \ -lutils \ + -lcleanups \ $(LIBTINFO_LIBS) \ $(LIBCRYPT_LIBS) \ $(LIBXML2_LIBS) \ diff --git a/test-tool/Makefile.am b/test-tool/Makefile.am index c4a1b1805..8747c561e 100644 --- a/test-tool/Makefile.am +++ b/test-tool/Makefile.am @@ -25,6 +25,7 @@ man_MANS = libguestfs-test-tool.1 libguestfs_test_tool_SOURCES = test-tool.c libguestfs_test_tool_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ @@ -37,6 +38,7 @@ libguestfs_test_tool_CFLAGS = \ libguestfs_test_tool_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LTLIBINTL) \ diff --git a/tests/c-api/Makefile.am b/tests/c-api/Makefile.am index cb653f7a6..cc68e5e58 100644 --- a/tests/c-api/Makefile.am +++ b/tests/c-api/Makefile.am @@ -92,6 +92,7 @@ tests_SOURCES = \ tests_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib tests_CFLAGS = \ @@ -100,6 +101,7 @@ tests_CFLAGS = \ tests_LDADD = \ $(PCRE_LIBS) \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ @@ -137,12 +139,14 @@ endif test_create_handle_SOURCES = test-create-handle.c test_create_handle_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_create_handle_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_create_handle_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la if HAVE_LIBDL @@ -158,60 +162,71 @@ endif test_config_SOURCES = test-config.c test_config_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_config_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_config_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la test_add_drive_opts_SOURCES = test-add-drive-opts.c test_add_drive_opts_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_add_drive_opts_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_add_drive_opts_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la test_last_errno_SOURCES = test-last-errno.c test_last_errno_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_last_errno_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_last_errno_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(top_builddir)/gnulib/lib/libgnu.la test_backend_settings_SOURCES = test-backend-settings.c test_backend_settings_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_backend_settings_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_backend_settings_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(LTLIBINTL) \ $(top_builddir)/lib/libguestfs.la test_private_data_SOURCES = test-private-data.c test_private_data_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_private_data_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_private_data_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la test_user_cancel_SOURCES = test-user-cancel.c test_user_cancel_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_user_cancel_CFLAGS = \ @@ -219,11 +234,13 @@ test_user_cancel_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_user_cancel_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la -lm \ $(top_builddir)/gnulib/lib/libgnu.la test_debug_to_file_SOURCES = test-debug-to-file.c test_debug_to_file_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib \ @@ -232,11 +249,13 @@ test_debug_to_file_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_debug_to_file_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(top_builddir)/gnulib/lib/libgnu.la test_environment_SOURCES = test-environment.c test_environment_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib \ @@ -245,12 +264,14 @@ test_environment_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_environment_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LTLIBINTL) \ $(top_builddir)/gnulib/lib/libgnu.la test_event_string_SOURCES = test-event-string.c test_event_string_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib \ @@ -259,6 +280,7 @@ test_event_string_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_event_string_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LTLIBINTL) \ $(top_builddir)/gnulib/lib/libgnu.la @@ -266,6 +288,7 @@ test_event_string_LDADD = \ if HAVE_LIBVIRT test_add_libvirt_dom_SOURCES = test-add-libvirt-dom.c test_add_libvirt_dom_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib \ @@ -275,6 +298,7 @@ test_add_libvirt_dom_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_add_libvirt_dom_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la $(LIBVIRT_LIBS) \ $(LTLIBINTL) \ $(LTLIBTHREAD) $(top_builddir)/gnulib/lib/libgnu.la diff --git a/tests/charsets/Makefile.am b/tests/charsets/Makefile.am index 7621dc252..883127c46 100644 --- a/tests/charsets/Makefile.am +++ b/tests/charsets/Makefile.am @@ -27,12 +27,14 @@ check_PROGRAMS = $(TESTS) test_charset_fidelity_SOURCES = test-charset-fidelity.c test_charset_fidelity_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_charset_fidelity_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_charset_fidelity_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/tests/disks/Makefile.am b/tests/disks/Makefile.am index 779871aff..9f2d913d5 100644 --- a/tests/disks/Makefile.am +++ b/tests/disks/Makefile.am @@ -49,6 +49,7 @@ check_PROGRAMS = test-add-disks test_add_disks_SOURCES = \ test-add-disks.c test_add_disks_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib @@ -56,6 +57,7 @@ test_add_disks_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_add_disks_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(top_builddir)/gnulib/lib/libgnu.la \ $(LIBXML2_LIBS) diff --git a/tests/events/Makefile.am b/tests/events/Makefile.am index 944011c9f..00238e863 100644 --- a/tests/events/Makefile.am +++ b/tests/events/Makefile.am @@ -34,6 +34,7 @@ check_PROGRAMS += test-libvirt-auth-callbacks test_libvirt_auth_callbacks_SOURCES = test-libvirt-auth-callbacks.c test_libvirt_auth_callbacks_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_libvirt_auth_callbacks_CFLAGS = \ @@ -41,6 +42,7 @@ test_libvirt_auth_callbacks_CFLAGS = \ $(LIBVIRT_CFLAGS) test_libvirt_auth_callbacks_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBVIRT_LIBS) \ $(LIBXML2_LIBS) \ diff --git a/tests/mount-local/Makefile.am b/tests/mount-local/Makefile.am index fc4862457..777c546b0 100644 --- a/tests/mount-local/Makefile.am +++ b/tests/mount-local/Makefile.am @@ -31,6 +31,7 @@ test_parallel_mount_local_SOURCES = \ test_parallel_mount_local_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/parallel -I$(top_builddir)/common/parallel \ @@ -43,6 +44,7 @@ test_parallel_mount_local_LDADD = \ $(FUSE_LIBS) \ $(top_builddir)/common/parallel/libparallel.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/tests/parallel/Makefile.am b/tests/parallel/Makefile.am index 7f6144089..5bcadfbe4 100644 --- a/tests/parallel/Makefile.am +++ b/tests/parallel/Makefile.am @@ -28,6 +28,7 @@ test_parallel_SOURCES = test-parallel.c test_parallel_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_parallel_CFLAGS = \ @@ -35,6 +36,7 @@ test_parallel_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_parallel_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LTLIBINTL) \ $(top_builddir)/gnulib/lib/libgnu.la diff --git a/tests/regressions/Makefile.am b/tests/regressions/Makefile.am index aef6982d1..1721d861a 100644 --- a/tests/regressions/Makefile.am +++ b/tests/regressions/Makefile.am @@ -104,17 +104,20 @@ check_PROGRAMS = \ rhbz501893_SOURCES = rhbz501893.c rhbz501893_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib rhbz501893_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) rhbz501893_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la rhbz790721_SOURCES = rhbz790721.c rhbz790721_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib rhbz790721_CFLAGS = \ @@ -122,12 +125,14 @@ rhbz790721_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) rhbz790721_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(top_builddir)/gnulib/lib/libgnu.la rhbz914931_SOURCES = rhbz914931.c rhbz914931_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -DGUESTFS_PRIVATE=1 @@ -136,12 +141,14 @@ rhbz914931_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) rhbz914931_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(LTLIBINTL) \ $(top_builddir)/lib/libguestfs.la \ $(top_builddir)/gnulib/lib/libgnu.la rhbz1055452_SOURCES = rhbz1055452.c rhbz1055452_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib rhbz1055452_CFLAGS = \ @@ -149,16 +156,19 @@ rhbz1055452_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) rhbz1055452_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la test_big_heap_SOURCES = test-big-heap.c test_big_heap_CPPFLAGS = \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib test_big_heap_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) test_big_heap_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la SLOW_TESTS = \ diff --git a/utils/boot-analysis/Makefile.am b/utils/boot-analysis/Makefile.am index 4d9149e7a..f5cafa2db 100644 --- a/utils/boot-analysis/Makefile.am +++ b/utils/boot-analysis/Makefile.am @@ -29,6 +29,7 @@ boot_analysis_SOURCES = \ boot-analysis-utils.h boot_analysis_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib boot_analysis_CFLAGS = \ @@ -37,6 +38,7 @@ boot_analysis_CFLAGS = \ $(PCRE_CFLAGS) boot_analysis_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(PCRE_LIBS) \ $(LIBXML2_LIBS) \ diff --git a/utils/boot-benchmark/Makefile.am b/utils/boot-benchmark/Makefile.am index 2e6ca465c..d741a0dba 100644 --- a/utils/boot-benchmark/Makefile.am +++ b/utils/boot-benchmark/Makefile.am @@ -31,6 +31,7 @@ boot_benchmark_SOURCES = \ ../boot-analysis/boot-analysis-utils.h boot_benchmark_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/utils/boot-analysis @@ -38,6 +39,7 @@ boot_benchmark_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) boot_benchmark_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LTLIBINTL) \ diff --git a/utils/qemu-boot/Makefile.am b/utils/qemu-boot/Makefile.am index 3936a0744..5bc5fe72b 100644 --- a/utils/qemu-boot/Makefile.am +++ b/utils/qemu-boot/Makefile.am @@ -23,6 +23,7 @@ qemu_boot_SOURCES = \ qemu-boot.c qemu_boot_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/parallel -I$(top_builddir)/common/parallel \ @@ -33,6 +34,7 @@ qemu_boot_CFLAGS = \ qemu_boot_LDADD = \ $(top_builddir)/common/parallel/libparallel.la \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/utils/qemu-speed-test/Makefile.am b/utils/qemu-speed-test/Makefile.am index d7bf59a29..16dc9280b 100644 --- a/utils/qemu-speed-test/Makefile.am +++ b/utils/qemu-speed-test/Makefile.am @@ -23,6 +23,7 @@ qemu_speed_test_SOURCES = \ qemu-speed-test.c qemu_speed_test_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/df @@ -30,6 +31,7 @@ qemu_speed_test_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) qemu_speed_test_LDADD = \ $(top_builddir)/common/utils/libutils.la \ + $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ $(LIBXML2_LIBS) \ $(LIBVIRT_LIBS) \ diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 8a831a700..41ff9af0b 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -124,6 +124,7 @@ virt_v2v_CPPFLAGS = \ -I$(top_builddir) \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/common/qemuopts \ + -I$(top_srcdir)/common/cleanups \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib virt_v2v_CFLAGS = \ @@ -141,6 +142,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) # installed copy of libguestfs. OCAMLPACKAGES = \ -package str,unix \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/common/utils/.libs \ -I $(top_builddir)/common/qemuopts/.libs \ -I $(top_builddir)/lib/.libs \ @@ -156,6 +158,7 @@ endif OCAMLCLIBS = \ -lutils \ + -lcleanups \ -lqemuopts \ $(LIBVIRT_LIBS) \ $(LIBXML2_LIBS) \ @@ -190,6 +193,7 @@ virt_v2v_copy_to_local_CPPFLAGS = \ -I. \ -I$(top_builddir) \ -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/common/cleanups \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib virt_v2v_copy_to_local_CFLAGS = \ -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 06/41] common/cleanups: Add CLEANUP_CLOSE function.
This is present in the daemon, but the function could be used throughout the code. --- common/cleanups/cleanups.h | 4 ++++ common/cleanups/stdlib-cleanups.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/common/cleanups/cleanups.h b/common/cleanups/cleanups.h index 211bce98a..70021e94e 100644 --- a/common/cleanups/cleanups.h +++ b/common/cleanups/cleanups.h @@ -26,6 +26,8 @@ __attribute__((cleanup(guestfs_int_cleanup_hash_free))) #define CLEANUP_UNLINK_FREE \ __attribute__((cleanup(guestfs_int_cleanup_unlink_free))) +#define CLEANUP_CLOSE \ + __attribute__((cleanup(guestfs_int_cleanup_close))) #define CLEANUP_FCLOSE \ __attribute__((cleanup(guestfs_int_cleanup_fclose))) #define CLEANUP_PCLOSE \ @@ -48,6 +50,7 @@ #define CLEANUP_FREE #define CLEANUP_HASH_FREE #define CLEANUP_UNLINK_FREE +#define CLEANUP_CLOSE #define CLEANUP_FCLOSE #define CLEANUP_PCLOSE #define CLEANUP_XMLFREE @@ -65,6 +68,7 @@ extern void guestfs_int_cleanup_free (void *ptr); extern void guestfs_int_cleanup_hash_free (void *ptr); extern void guestfs_int_cleanup_unlink_free (char **ptr); +extern void guestfs_int_cleanup_close (void *ptr); extern void guestfs_int_cleanup_fclose (void *ptr); extern void guestfs_int_cleanup_pclose (void *ptr); extern void guestfs_int_cleanup_xmlFree (void *ptr); diff --git a/common/cleanups/stdlib-cleanups.c b/common/cleanups/stdlib-cleanups.c index 0512a86a2..e77708db4 100644 --- a/common/cleanups/stdlib-cleanups.c +++ b/common/cleanups/stdlib-cleanups.c @@ -83,6 +83,15 @@ guestfs_int_cleanup_unlink_free (char **ptr) } void +guestfs_int_cleanup_close (void *ptr) +{ + const int fd = * (int *) ptr; + + if (fd >= 0) + close (fd); +} + +void guestfs_int_cleanup_fclose (void *ptr) { FILE *f = * (FILE **) ptr; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 07/41] daemon: Use common/cleanups to implement most cleanups.
Remove duplicate reimplementation of (most) cleanup functions in the daemon. Use the code from common/cleanups instead. --- daemon/Makefile.am | 7 +++++-- daemon/cleanups.c | 53 ++++++----------------------------------------------- daemon/cleanups.h | 51 --------------------------------------------------- daemon/daemon.h | 25 +++++++++++++++++++++++-- docs/C_SOURCE_FILES | 1 - 5 files changed, 34 insertions(+), 103 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 0d3dde516..9695500bf 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -49,6 +49,7 @@ endif guestfsd_SOURCES = \ ../common/errnostring/errnostring.h \ ../common/protocol/guestfs_protocol.h \ + ../common/cleanups/cleanups.h \ 9p.c \ acl.c \ actions.h \ @@ -62,7 +63,6 @@ guestfsd_SOURCES = \ cap.c \ checksum.c \ cleanups.c \ - cleanups.h \ cmp.c \ command.c \ command.h \ @@ -178,6 +178,7 @@ guestfsd_SOURCES = \ guestfsd_LDADD = \ ../common/errnostring/liberrnostring.la \ ../common/protocol/libprotocol.la \ + ../common/cleanups/libcleanups.la \ $(ACL_LIBS) \ $(CAP_LIBS) \ $(YAJL_LIBS) \ @@ -206,7 +207,9 @@ guestfsd_CPPFLAGS = \ -I$(top_srcdir)/common/errnostring \ -I$(top_builddir)/common/errnostring \ -I$(top_srcdir)/common/protocol \ - -I$(top_builddir)/common/protocol + -I$(top_builddir)/common/protocol \ + -I$(top_srcdir)/common/cleanups \ + -I$(top_builddir)/common/cleanups guestfsd_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(RPC_CFLAGS) \ diff --git a/daemon/cleanups.c b/daemon/cleanups.c index 3102cf94b..c73b9e492 100644 --- a/daemon/cleanups.c +++ b/daemon/cleanups.c @@ -24,51 +24,7 @@ #include <augeas.h> -#include "cleanups.h" - -/* Use by the CLEANUP_* macros. Do not call these directly. */ -void -cleanup_free (void *ptr) -{ - free (* (void **) ptr); -} - -extern void free_strings (char **argv); - -void -cleanup_free_string_list (void *ptr) -{ - free_strings (* (char ***) ptr); -} - -void -cleanup_unlink_free (void *ptr) -{ - char *filename = * (char **) ptr; - - if (filename) { - unlink (filename); - free (filename); - } -} - -void -cleanup_close (void *ptr) -{ - const int fd = * (int *) ptr; - - if (fd >= 0) - close (fd); -} - -void -cleanup_fclose (void *ptr) -{ - FILE *f = * (FILE **) ptr; - - if (f) - fclose (f); -} +#include "daemon.h" void cleanup_aug_close (void *ptr) @@ -79,8 +35,11 @@ cleanup_aug_close (void *ptr) aug_close (aug); } -struct stringsbuf; -extern void free_stringsbuf (struct stringsbuf *sb); +void +cleanup_free_string_list (void *ptr) +{ + free_strings (* (char ***) ptr); +} void cleanup_free_stringsbuf (void *ptr) diff --git a/daemon/cleanups.h b/daemon/cleanups.h deleted file mode 100644 index a791244cb..000000000 --- a/daemon/cleanups.h +++ /dev/null @@ -1,51 +0,0 @@ -/* libguestfs - the guestfsd daemon - * Copyright (C) 2009-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. - */ - -#ifndef GUESTFSD_CLEANUPS_H -#define GUESTFSD_CLEANUPS_H - -/* These functions are used internally by the CLEANUP_* macros. - * Don't call them directly. - */ -extern void cleanup_free (void *ptr); -extern void cleanup_free_string_list (void *ptr); -extern void cleanup_unlink_free (void *ptr); -extern void cleanup_close (void *ptr); -extern void cleanup_fclose (void *ptr); -extern void cleanup_aug_close (void *ptr); -extern void cleanup_free_stringsbuf (void *ptr); - -#ifdef HAVE_ATTRIBUTE_CLEANUP -#define CLEANUP_FREE __attribute__((cleanup(cleanup_free))) -#define CLEANUP_FREE_STRING_LIST \ - __attribute__((cleanup(cleanup_free_string_list))) -#define CLEANUP_UNLINK_FREE __attribute__((cleanup(cleanup_unlink_free))) -#define CLEANUP_CLOSE __attribute__((cleanup(cleanup_close))) -#define CLEANUP_FCLOSE __attribute__((cleanup(cleanup_fclose))) -#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close))) -#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf))) -#else -#define CLEANUP_FREE -#define CLEANUP_FREE_STRING_LIST -#define CLEANUP_UNLINK_FREE -#define CLEANUP_CLOSE -#define CLEANUP_AUG_CLOSE -#define CLEANUP_FREE_STRINGSBUF -#endif - -#endif /* GUESTFSD_CLEANUPS_H */ diff --git a/daemon/daemon.h b/daemon/daemon.h index 400116514..746af22b9 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -30,9 +30,10 @@ #include "guestfs_protocol.h" -#include "guestfs-internal-all.h" - #include "cleanups.h" + +#include "guestfs-internal-all.h" + #include "structs-cleanups.h" #include "command.h" @@ -76,6 +77,26 @@ extern int xread (int sock, void *buf, size_t len) extern char *mountable_to_string (const mountable_t *mountable); +/*-- in cleanups.c --*/ + +/* These functions are used internally by the CLEANUP_* macros. + * Don't call them directly. + */ +extern void cleanup_aug_close (void *ptr); +extern void cleanup_free_string_list (void *ptr); +extern void cleanup_free_stringsbuf (void *ptr); + +#ifdef HAVE_ATTRIBUTE_CLEANUP +#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close))) +#define CLEANUP_FREE_STRING_LIST \ + __attribute__((cleanup(cleanup_free_string_list))) +#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf))) +#else +#define CLEANUP_AUG_CLOSE +#define CLEANUP_FREE_STRING_LIST +#define CLEANUP_FREE_STRINGSBUF +#endif + /*-- in mount.c --*/ extern int mount_vfs_nochroot (const char *options, const char *vfstype, diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 3382fce56..ceed9581d 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -73,7 +73,6 @@ daemon/btrfs.c daemon/cap.c daemon/checksum.c daemon/cleanups.c -daemon/cleanups.h daemon/cmp.c daemon/command.c daemon/command.h -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 08/41] common/utils: Move ‘uefi.c’ to ‘lib/’.
This was only used inside the library, so move it there. --- .gitignore | 2 +- common/utils/Makefile.am | 4 +--- common/utils/guestfs-internal-frontend.h | 12 ------------ docs/C_SOURCE_FILES | 2 +- generator/UEFI.ml | 3 ++- generator/main.ml | 2 +- lib/Makefile.am | 3 ++- lib/appliance-uefi.c | 2 +- lib/guestfs-internal.h | 12 ++++++++++++ 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 97fe2f050..e0dbe280c 100644 --- a/.gitignore +++ b/.gitignore @@ -141,7 +141,6 @@ Makefile.in /common/utils/structs-cleanup.c /common/utils/structs-print.c /common/utils/structs-print.h -/common/utils/uefi.c /compile /config.cache /config.guess @@ -341,6 +340,7 @@ Makefile.in /lib/structs-copy.c /lib/structs-free.c /lib/unit-tests +/lib/uefi.c /libguestfs.spec /libguestfs-*.tar.gz /libtool diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index 485909bbe..47a4186d7 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -21,8 +21,7 @@ generator_built = \ guestfs-internal-frontend-cleanups.h \ structs-cleanup.c \ structs-print.c \ - structs-print.h \ - uefi.c + structs-print.h BUILT_SOURCES = \ $(generator_built) @@ -39,7 +38,6 @@ libutils_la_SOURCES = \ structs-cleanup.c \ structs-print.c \ structs-print.h \ - uefi.c \ utils.c libutils_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ diff --git a/common/utils/guestfs-internal-frontend.h b/common/utils/guestfs-internal-frontend.h index dacc92e49..92c9ae902 100644 --- a/common/utils/guestfs-internal-frontend.h +++ b/common/utils/guestfs-internal-frontend.h @@ -71,18 +71,6 @@ extern void guestfs_int_fadvise_noreuse (int fd); extern char *guestfs_int_shell_unquote (const char *str); extern void guestfs_int_cleanup_free_string_list (char ***ptr); -/* uefi.c */ -struct uefi_firmware { - const char *code; /* code file (NULL = end of list) */ - const char *code_debug; /* code file with debugging msgs (may be NULL)*/ - const char *vars; /* vars template file */ - int flags; /* various flags, see below */ -#define UEFI_FLAG_SECURE_BOOT_REQUIRED 1 /* secure boot (see RHBZ#1367615) */ -}; -extern struct uefi_firmware guestfs_int_uefi_i386_firmware[]; -extern struct uefi_firmware guestfs_int_uefi_x86_64_firmware[]; -extern struct uefi_firmware guestfs_int_uefi_aarch64_firmware[]; - /* These are in a separate header so the header can be generated. * Don't include the following file directly: */ diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index ceed9581d..0ba99e259 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -51,7 +51,6 @@ common/utils/guestfs-internal-frontend.h common/utils/structs-cleanup.c common/utils/structs-print.c common/utils/structs-print.h -common/utils/uefi.c common/utils/utils.c common/visit/visit.c common/visit/visit.h @@ -333,6 +332,7 @@ lib/structs-copy.c lib/structs-free.c lib/tmpdirs.c lib/tsk.c +lib/uefi.c lib/umask.c lib/unit-tests.c lib/version.c diff --git a/generator/UEFI.ml b/generator/UEFI.ml index 5c5e02bab..17418f473 100644 --- a/generator/UEFI.ml +++ b/generator/UEFI.ml @@ -86,7 +86,8 @@ let generate_uefi_c () pr "\n"; pr "#include <stdio.h>\n"; pr "\n"; - pr "#include \"guestfs-internal-frontend.h\"\n"; + pr "#include \"guestfs.h\"\n"; + pr "#include \"guestfs-internal.h\"\n"; List.iter ( fun arch -> diff --git a/generator/main.ml b/generator/main.ml index 0e1c01f74..8ff698130 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -94,7 +94,7 @@ Run it from the top source directory using the command C.generate_client_structs_print_c; output_to "common/utils/structs-print.h" C.generate_client_structs_print_h; - output_to "common/utils/uefi.c" + output_to "lib/uefi.c" UEFI.generate_uefi_c; output_to "lib/guestfs.h" C.generate_guestfs_h; diff --git a/lib/Makefile.am b/lib/Makefile.am index 90c657514..12fdbcd6c 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -124,10 +124,11 @@ libguestfs_la_SOURCES = \ structs-free.c \ tmpdirs.c \ tsk.c \ + uefi.c \ umask.c \ + version.c \ wait.c \ whole-file.c \ - version.c \ yara.c \ libguestfs.syms diff --git a/lib/appliance-uefi.c b/lib/appliance-uefi.c index 1612c5db5..986989e67 100644 --- a/lib/appliance-uefi.c +++ b/lib/appliance-uefi.c @@ -19,7 +19,7 @@ /** * Find the UEFI firmware needed to boot the appliance. * - * See also F<common/utils/uefi.c> (autogenerated file) containing the + * See also F<lib/uefi.c> (autogenerated file) containing the * firmware file locations. */ diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h index ec70336e2..190b5cdd2 100644 --- a/lib/guestfs-internal.h +++ b/lib/guestfs-internal.h @@ -1011,4 +1011,16 @@ extern bool guestfs_int_version_cmp_ge (const struct version *a, const struct ve #define version_init_null(v) guestfs_int_version_from_values (v, 0, 0, 0) #define version_is_null(v) ((v)->v_major == 0 && (v)->v_minor == 0 && (v)->v_micro == 0) +/* uefi.c */ +struct uefi_firmware { + const char *code; /* code file (NULL = end of list) */ + const char *code_debug; /* code file with debugging msgs (may be NULL)*/ + const char *vars; /* vars template file */ + int flags; /* various flags, see below */ +#define UEFI_FLAG_SECURE_BOOT_REQUIRED 1 /* secure boot (see RHBZ#1367615) */ +}; +extern struct uefi_firmware guestfs_int_uefi_i386_firmware[]; +extern struct uefi_firmware guestfs_int_uefi_x86_64_firmware[]; +extern struct uefi_firmware guestfs_int_uefi_aarch64_firmware[]; + #endif /* GUESTFS_INTERNAL_H_ */ -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:05 UTC
[Libguestfs] [PATCH v6 09/41] utils: Split out structs cleanups and printing into common/structs.
These won't be used by the daemon, so interfere with us using common/utils in the daemon, so they are moved to a different library. --- .gitignore | 8 +++--- Makefile.am | 1 + align/Makefile.am | 2 ++ align/scan.c | 1 + cat/Makefile.am | 10 ++++++++ cat/filesystems.c | 1 + cat/log.c | 1 + cat/tail.c | 1 + common/mlvisit/Makefile.am | 3 ++- common/structs/Makefile.am | 44 ++++++++++++++++++++++++++++++++ common/utils/Makefile.am | 16 ------------ common/utils/guestfs-internal-frontend.h | 5 ---- common/visit/Makefile.am | 3 ++- common/visit/visit.c | 1 + configure.ac | 1 + df/Makefile.am | 2 ++ df/df.c | 1 + diff/Makefile.am | 1 + docs/C_SOURCE_FILES | 8 +++--- docs/guestfs-hacking.pod | 5 ++++ fish/Makefile.am | 4 +++ generator/c.ml | 17 +++++------- generator/c.mli | 4 +-- generator/java.ml | 1 + generator/main.ml | 12 ++++----- generator/tests_c_api.ml | 1 + inspector/Makefile.am | 2 ++ inspector/inspector.c | 1 + java/Makefile.am | 2 ++ lib/Makefile.am | 6 ++++- lib/file.c | 1 + lib/fuse.c | 1 + lib/inspect-apps.c | 1 + lib/inspect-fs-cd.c | 1 + lib/inspect-fs-windows.c | 1 + lib/inspect-fs.c | 1 + lib/launch.c | 1 + lib/listfs.c | 1 + lib/mountable.c | 2 +- make-fs/Makefile.am | 2 ++ make-fs/make-fs.c | 1 + sysprep/Makefile.am | 2 ++ tests/c-api/Makefile.am | 2 ++ tests/c-api/tests-main.c | 1 + 44 files changed, 132 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index e0dbe280c..934d9f291 100644 --- a/.gitignore +++ b/.gitignore @@ -137,10 +137,10 @@ Makefile.in /common/protocol/guestfs_protocol.h /common/protocol/guestfs_protocol.x /common/qemuopts/qemuopts-tests -/common/utils/guestfs-internal-frontend-cleanups.h -/common/utils/structs-cleanup.c -/common/utils/structs-print.c -/common/utils/structs-print.h +/common/structs/structs-cleanups.c +/common/structs/structs-cleanups.h +/common/structs/structs-print.c +/common/structs/structs-print.h /compile /config.cache /config.guess diff --git a/Makefile.am b/Makefile.am index 7189519fb..b3eb35349 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,7 @@ endif SUBDIRS += common/errnostring common/protocol common/qemuopts SUBDIRS += common/cleanups SUBDIRS += common/utils +SUBDIRS += common/structs SUBDIRS += lib docs examples po # The daemon and the appliance. diff --git a/align/Makefile.am b/align/Makefile.am index 99a80dd73..9cfa4cc91 100644 --- a/align/Makefile.am +++ b/align/Makefile.am @@ -32,6 +32,7 @@ virt_alignment_scan_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/parallel -I$(top_builddir)/common/parallel \ @@ -47,6 +48,7 @@ virt_alignment_scan_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)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/align/scan.c b/align/scan.c index 4fa95c0a3..b9f29868c 100644 --- a/align/scan.c +++ b/align/scan.c @@ -41,6 +41,7 @@ #include "getprogname.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" #include "parallel.h" diff --git a/cat/Makefile.am b/cat/Makefile.am index 94e0285af..aeabab6e7 100644 --- a/cat/Makefile.am +++ b/cat/Makefile.am @@ -40,6 +40,7 @@ virt_cat_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ @@ -52,6 +53,7 @@ virt_cat_CFLAGS = \ virt_cat_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ @@ -68,6 +70,7 @@ virt_filesystems_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ @@ -80,6 +83,7 @@ virt_filesystems_CFLAGS = \ virt_filesystems_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ @@ -96,6 +100,7 @@ virt_log_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ @@ -107,6 +112,7 @@ virt_log_CFLAGS = \ virt_log_LDADD = \ $(top_builddir)/common/options/liboptions.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ @@ -123,6 +129,7 @@ virt_ls_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/visit \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -136,6 +143,7 @@ virt_ls_CFLAGS = \ virt_ls_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/visit/libvisit.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ @@ -152,6 +160,7 @@ virt_tail_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ @@ -164,6 +173,7 @@ virt_tail_CFLAGS = \ virt_tail_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/windows/libwindows.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/cat/filesystems.c b/cat/filesystems.c index f3dd265ab..74e994169 100644 --- a/cat/filesystems.c +++ b/cat/filesystems.c @@ -37,6 +37,7 @@ #include "getprogname.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" diff --git a/cat/log.c b/cat/log.c index 92272b8d1..6e445af0b 100644 --- a/cat/log.c +++ b/cat/log.c @@ -38,6 +38,7 @@ #include "getprogname.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" diff --git a/cat/tail.c b/cat/tail.c index 4ac73ad36..e932820e6 100644 --- a/cat/tail.c +++ b/cat/tail.c @@ -37,6 +37,7 @@ #include "ignore-value.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" #include "windows.h" diff --git a/common/mlvisit/Makefile.am b/common/mlvisit/Makefile.am index 51cbd2de6..2019efd75 100644 --- a/common/mlvisit/Makefile.am +++ b/common/mlvisit/Makefile.am @@ -74,6 +74,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/structs/.libs \ -I $(top_builddir)/common/visit/.libs \ -I $(builddir) OCAMLPACKAGES_TESTS = $(MLVISIT_CMA) @@ -115,7 +116,7 @@ visit_tests_DEPENDENCIES = \ $(top_srcdir)/ocaml-link.sh visit_tests_LINK = \ $(top_srcdir)/ocaml-link.sh \ - -cclib '-lvisit -lutils $(LIBXML2_LIBS) -lgnu' -- \ + -cclib '-lvisit -lstructs -lutils $(LIBXML2_LIBS) -lgnu' -- \ $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ $(OCAMLPACKAGES) $(OCAMLPACKAGES_TESTS) \ $(visit_tests_THEOBJECTS) -o $@ diff --git a/common/structs/Makefile.am b/common/structs/Makefile.am new file mode 100644 index 000000000..1762af276 --- /dev/null +++ b/common/structs/Makefile.am @@ -0,0 +1,44 @@ +# libguestfs +# 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. + +include $(top_srcdir)/subdir-rules.mk + +generator_built = \ + structs-cleanups.c \ + structs-cleanups.h \ + structs-print.c \ + structs-print.h + +BUILT_SOURCES = \ + $(generator_built) + +EXTRA_DIST = \ + $(BUILT_SOURCES) + +noinst_LTLIBRARIES = libstructs.la + +libstructs_la_SOURCES = \ + ../../lib/guestfs.h \ + $(BUILT_SOURCES) +libstructs_la_CPPFLAGS = \ + -DGUESTFS_WARN_DEPRECATED=1 \ + -DGUESTFS_PRIVATE=1 \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/lib -I$(top_builddir)/lib +libstructs_la_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + $(GCC_VISIBILITY_HIDDEN) diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index 47a4186d7..9aabda4e2 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -17,27 +17,11 @@ include $(top_srcdir)/subdir-rules.mk -generator_built = \ - guestfs-internal-frontend-cleanups.h \ - structs-cleanup.c \ - structs-print.c \ - structs-print.h - -BUILT_SOURCES = \ - $(generator_built) - -EXTRA_DIST = \ - $(BUILT_SOURCES) - noinst_LTLIBRARIES = libutils.la libutils_la_SOURCES = \ ../../lib/guestfs.h \ guestfs-internal-frontend.h \ - guestfs-internal-frontend-cleanups.h \ - structs-cleanup.c \ - structs-print.c \ - structs-print.h \ utils.c libutils_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ diff --git a/common/utils/guestfs-internal-frontend.h b/common/utils/guestfs-internal-frontend.h index 92c9ae902..8de38bf9b 100644 --- a/common/utils/guestfs-internal-frontend.h +++ b/common/utils/guestfs-internal-frontend.h @@ -71,11 +71,6 @@ extern void guestfs_int_fadvise_noreuse (int fd); extern char *guestfs_int_shell_unquote (const char *str); extern void guestfs_int_cleanup_free_string_list (char ***ptr); -/* These are in a separate header so the header can be generated. - * Don't include the following file directly: - */ -#include "guestfs-internal-frontend-cleanups.h" - /* Not all language bindings know how to deal with Pointer arguments. * Those that don't will use this macro which complains noisily and * returns NULL. diff --git a/common/visit/Makefile.am b/common/visit/Makefile.am index 519088769..a2d22f10e 100644 --- a/common/visit/Makefile.am +++ b/common/visit/Makefile.am @@ -28,7 +28,8 @@ libvisit_la_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ - -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils + -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs libvisit_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(GCC_VISIBILITY_HIDDEN) diff --git a/common/visit/visit.c b/common/visit/visit.c index 5045f9f71..491f9dda3 100644 --- a/common/visit/visit.c +++ b/common/visit/visit.c @@ -37,6 +37,7 @@ #include "guestfs.h" #include "guestfs-internal-frontend.h" +#include "structs-cleanups.h" #include "visit.h" diff --git a/configure.ac b/configure.ac index 33cdbd39e..9b7f61a2e 100644 --- a/configure.ac +++ b/configure.ac @@ -196,6 +196,7 @@ AC_CONFIG_FILES([Makefile common/progress/Makefile common/protocol/Makefile common/qemuopts/Makefile + common/structs/Makefile common/utils/Makefile common/visit/Makefile common/windows/Makefile diff --git a/df/Makefile.am b/df/Makefile.am index 30faf3ee1..cffcb55d6 100644 --- a/df/Makefile.am +++ b/df/Makefile.am @@ -37,6 +37,7 @@ virt_df_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/common/parallel -I$(top_builddir)/common/parallel \ @@ -51,6 +52,7 @@ virt_df_CFLAGS = \ virt_df_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)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/df/df.c b/df/df.c index a13cc5910..66b1d8334 100644 --- a/df/df.c +++ b/df/df.c @@ -27,6 +27,7 @@ #include <errno.h> #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "domains.h" #include "virt-df.h" diff --git a/diff/Makefile.am b/diff/Makefile.am index 06378dc78..de65bcf8c 100644 --- a/diff/Makefile.am +++ b/diff/Makefile.am @@ -45,6 +45,7 @@ virt_diff_CFLAGS = \ virt_diff_LDADD = \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/visit/libvisit.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 0ba99e259..fce01da3e 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -46,11 +46,11 @@ common/progress/progress.h common/qemuopts/qemuopts-tests.c common/qemuopts/qemuopts.c common/qemuopts/qemuopts.h -common/utils/guestfs-internal-frontend-cleanups.h +common/structs/structs-cleanups.c +common/structs/structs-cleanups.h +common/structs/structs-print.c +common/structs/structs-print.h common/utils/guestfs-internal-frontend.h -common/utils/structs-cleanup.c -common/utils/structs-print.c -common/utils/structs-print.h common/utils/utils.c common/visit/visit.c common/visit/visit.h diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index fdb7a9e63..6ee4c236b 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -142,6 +142,11 @@ and the daemon running inside the appliance is defined here. Mini-library for writing qemu command lines and qemu config files. +=item F<common/structs> + +Common code for printing and freeing libguestfs structs, used by the +library and some tools. + =item F<common/utils> Various utility functions used throughout the library and tools. diff --git a/fish/Makefile.am b/fish/Makefile.am index d74a70af7..4aac8067b 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -109,10 +109,12 @@ libcmds_la_SOURCES = cmds-gperf.c libcmds_la_CPPFLAGS = \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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$(srcdir)/../gnulib/lib -I../gnulib/lib libcmds_la_CFLAGS libcmds_la_LIBADD = \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(LTLIBINTL) @@ -127,6 +129,7 @@ guestfish_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/edit -I$(top_builddir)/common/edit \ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ @@ -144,6 +147,7 @@ guestfish_LDADD = \ $(top_builddir)/common/edit/libedit.la \ $(top_builddir)/common/options/liboptions.la \ $(top_builddir)/common/progress/libprogress.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/generator/c.ml b/generator/c.ml index 27bf1ebf9..c9fd867de 100644 --- a/generator/c.ml +++ b/generator/c.ml @@ -794,20 +794,17 @@ and generate_internal_actions_h () pr "\n"; pr "#endif /* GUESTFS_INTERNAL_ACTIONS_H_ */\n" -(* Generate guestfs-internal-frontend-cleanups.h file. *) -and generate_internal_frontend_cleanups_h () +(* Generate structs-cleanups.h file. *) +and generate_client_structs_cleanups_h () generate_header CStyle LGPLv2plus; pr "\ /* These CLEANUP_* macros automatically free the struct or struct list * pointed to by the local variable at the end of the current scope. - * - * Don't include this file directly! To use these cleanups in library - * bindings and tools, include \"guestfs-internal-frontend.h\" only. */ -#ifndef GUESTFS_INTERNAL_FRONTEND_CLEANUPS_H_ -#define GUESTFS_INTERNAL_FRONTEND_CLEANUPS_H_ +#ifndef GUESTFS_STRUCTS_CLEANUPS_H_ +#define GUESTFS_STRUCTS_CLEANUPS_H_ #ifdef HAVE_ATTRIBUTE_CLEANUP "; @@ -846,7 +843,7 @@ and generate_internal_frontend_cleanups_h () ) structs; pr "\n"; - pr "#endif /* GUESTFS_INTERNAL_FRONTEND_CLEANUPS_H_ */\n" + pr "#endif /* GUESTFS_STRUCTS_CLEANUPS_H_ */\n" (* Functions to free structures. *) and generate_client_structs_free () @@ -1166,7 +1163,7 @@ and generate_client_structs_copy () ) structs (* Functions to free structures used by the CLEANUP_* macros. *) -and generate_client_structs_cleanup () +and generate_client_structs_cleanups_c () generate_header CStyle LGPLv2plus; pr "\ @@ -1176,7 +1173,7 @@ and generate_client_structs_cleanup () #include <stdlib.h> #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"structs-cleanups.h\" "; diff --git a/generator/c.mli b/generator/c.mli index 0884a8dfe..6d8cae891 100644 --- a/generator/c.mli +++ b/generator/c.mli @@ -26,7 +26,8 @@ val generate_actions_pod : unit -> unit val generate_availability_pod : unit -> unit val generate_client_actions : Types.action list -> unit -> unit val generate_client_actions_variants : unit -> unit -val generate_client_structs_cleanup : unit -> unit +val generate_client_structs_cleanups_h : unit -> unit +val generate_client_structs_cleanups_c : unit -> unit val generate_client_structs_compare : unit -> unit val generate_client_structs_copy : unit -> unit val generate_client_structs_free : unit -> unit @@ -35,7 +36,6 @@ val generate_client_structs_print_c : unit -> unit val generate_event_string_c : unit -> unit val generate_guestfs_h : unit -> unit val generate_internal_actions_h : unit -> unit -val generate_internal_frontend_cleanups_h : unit -> unit val generate_linker_script : unit -> unit val generate_max_proc_nr : unit -> unit val generate_structs_pod : unit -> unit diff --git a/generator/java.ml b/generator/java.ml index 7c3212a49..a7d0ed359 100644 --- a/generator/java.ml +++ b/generator/java.ml @@ -586,6 +586,7 @@ and generate_java_c actions () #include \"com_redhat_et_libguestfs_GuestFS.h\" #include \"guestfs.h\" #include \"guestfs-internal-frontend.h\" +#include \"structs-cleanups.h\" /* Note that this function returns. The exception is not thrown * until after the wrapper function returns. diff --git a/generator/main.ml b/generator/main.ml index 8ff698130..33fe2b2ee 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -86,13 +86,13 @@ Run it from the top source directory using the command Errnostring.generate_errnostring_h; output_to "common/protocol/guestfs_protocol.x" XDR.generate_xdr; - output_to "common/utils/guestfs-internal-frontend-cleanups.h" - C.generate_internal_frontend_cleanups_h; - output_to "common/utils/structs-cleanup.c" - C.generate_client_structs_cleanup; - output_to "common/utils/structs-print.c" + output_to "common/structs/structs-cleanups.h" + C.generate_client_structs_cleanups_h; + output_to "common/structs/structs-cleanups.c" + C.generate_client_structs_cleanups_c; + output_to "common/structs/structs-print.c" C.generate_client_structs_print_c; - output_to "common/utils/structs-print.h" + output_to "common/structs/structs-print.h" C.generate_client_structs_print_h; output_to "lib/uefi.c" UEFI.generate_uefi_c; diff --git a/generator/tests_c_api.ml b/generator/tests_c_api.ml index a680521f4..c3cb62c4d 100644 --- a/generator/tests_c_api.ml +++ b/generator/tests_c_api.ml @@ -48,6 +48,7 @@ let rec generate_c_api_tests () #include \"guestfs.h\" #include \"guestfs-internal-frontend.h\" +#include \"structs-cleanups.h\" #include \"tests.h\" diff --git a/inspector/Makefile.am b/inspector/Makefile.am index 980852cde..5009cc57c 100644 --- a/inspector/Makefile.am +++ b/inspector/Makefile.am @@ -60,6 +60,7 @@ virt_inspector_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/fish \ @@ -71,6 +72,7 @@ virt_inspector_CFLAGS = \ virt_inspector_LDADD = \ $(top_builddir)/common/options/liboptions.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/inspector/inspector.c b/inspector/inspector.c index b00c85208..104310d1f 100644 --- a/inspector/inspector.c +++ b/inspector/inspector.c @@ -40,6 +40,7 @@ #include "getprogname.h" #include "guestfs.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" diff --git a/java/Makefile.am b/java/Makefile.am index 089ac0177..834130b20 100644 --- a/java/Makefile.am +++ b/java/Makefile.am @@ -110,6 +110,7 @@ libguestfs_jni_la_CPPFLAGS = \ -DGUESTFS_PRIVATE=1 \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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 libguestfs_jni_la_CFLAGS = \ @@ -117,6 +118,7 @@ libguestfs_jni_la_CFLAGS = \ $(JNI_CFLAGS) libguestfs_jni_la_LIBADD = \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la diff --git a/lib/Makefile.am b/lib/Makefile.am index 12fdbcd6c..9cca1268a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -58,7 +58,7 @@ libguestfs_la_SOURCES = \ ../common/protocol/guestfs_protocol.h \ ../common/qemuopts/qemuopts.h \ ../common/utils/guestfs-internal-frontend.h \ - ../common/utils/guestfs-internal-frontend-cleanups.h \ + ../common/structs/structs-cleanups.h \ guestfs.h \ guestfs-internal.h \ guestfs-internal-all.h \ @@ -141,6 +141,7 @@ libguestfs_la_CPPFLAGS = \ -I$(top_srcdir)/common/qemuopts -I$(top_builddir)/common/qemuopts \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib libguestfs_la_CFLAGS = \ @@ -156,6 +157,7 @@ libguestfs_la_LIBADD = \ ../common/errnostring/liberrnostring.la \ ../common/protocol/libprotocol.la \ ../common/qemuopts/libqemuopts.la \ + ../common/structs/libstructs.la \ ../common/utils/libutils.la \ ../common/cleanups/libcleanups.la \ $(PCRE_LIBS) $(MAGIC_LIBS) \ @@ -219,6 +221,7 @@ unit_tests_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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. unit_tests_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) @@ -226,6 +229,7 @@ unit_tests_CFLAGS = \ # non-exported functions we have to link with the objects not the # library. unit_tests_LDADD = \ + ../common/structs/libstructs.la \ ../common/utils/libutils.la \ ../common/cleanups/libcleanups.la \ $(libguestfs_la_OBJECTS) \ diff --git a/lib/file.c b/lib/file.c index 53b859d4b..73c983c2b 100644 --- a/lib/file.c +++ b/lib/file.c @@ -32,6 +32,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" static int compare (const void *vp1, const void *vp2) diff --git a/lib/fuse.c b/lib/fuse.c index cde3783d9..de8c4d8f2 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -54,6 +54,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" #if HAVE_FUSE diff --git a/lib/inspect-apps.c b/lib/inspect-apps.c index c324f3bf0..25192340c 100644 --- a/lib/inspect-apps.c +++ b/lib/inspect-apps.c @@ -43,6 +43,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" #ifdef DB_DUMP static struct guestfs_application2_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs); diff --git a/lib/inspect-fs-cd.c b/lib/inspect-fs-cd.c index 1cff5606b..c9a4e219e 100644 --- a/lib/inspect-fs-cd.c +++ b/lib/inspect-fs-cd.c @@ -32,6 +32,7 @@ #include "guestfs.h" #include "guestfs-internal.h" +#include "structs-cleanups.h" /* Debian/Ubuntu install disks are easy ... * diff --git a/lib/inspect-fs-windows.c b/lib/inspect-fs-windows.c index 35f7cc821..b14dc2e14 100644 --- a/lib/inspect-fs-windows.c +++ b/lib/inspect-fs-windows.c @@ -48,6 +48,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" COMPILE_REGEXP (re_windows_version, "^(\\d+)\\.(\\d+)", 0) COMPILE_REGEXP (re_boot_ini_os_header, "^\\[operating systems\\]\\s*$", 0) diff --git a/lib/inspect-fs.c b/lib/inspect-fs.c index 9f7630bcf..2da73d310 100644 --- a/lib/inspect-fs.c +++ b/lib/inspect-fs.c @@ -35,6 +35,7 @@ #include "guestfs.h" #include "guestfs-internal.h" +#include "structs-cleanups.h" static int check_filesystem (guestfs_h *g, const char *mountable, const struct guestfs_internal_mountable *m, diff --git a/lib/launch.c b/lib/launch.c index 04d69d867..70e7f8897 100644 --- a/lib/launch.c +++ b/lib/launch.c @@ -45,6 +45,7 @@ #include "guestfs-internal.h" #include "guestfs-internal-actions.h" #include "guestfs_protocol.h" +#include "structs-cleanups.h" static struct backend { struct backend *next; diff --git a/lib/listfs.c b/lib/listfs.c index 88446cc9f..60aff3305 100644 --- a/lib/listfs.c +++ b/lib/listfs.c @@ -25,6 +25,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" +#include "structs-cleanups.h" /* List filesystems. * diff --git a/lib/mountable.c b/lib/mountable.c index 9f7b451fd..6e4a0c293 100644 --- a/lib/mountable.c +++ b/lib/mountable.c @@ -23,7 +23,7 @@ #include "guestfs.h" #include "guestfs-internal.h" #include "guestfs-internal-actions.h" - +#include "structs-cleanups.h" char * guestfs_impl_mountable_device (guestfs_h *g, const char *mountable) diff --git a/make-fs/Makefile.am b/make-fs/Makefile.am index 7b5dee127..7ee453067 100644 --- a/make-fs/Makefile.am +++ b/make-fs/Makefile.am @@ -32,6 +32,7 @@ virt_make_fs_CPPFLAGS = \ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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)/common/options -I$(top_builddir)/common/options \ -I$(top_srcdir)/fish \ @@ -43,6 +44,7 @@ virt_make_fs_CFLAGS = \ virt_make_fs_LDADD = \ $(top_builddir)/common/options/liboptions.la \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/make-fs/make-fs.c b/make-fs/make-fs.c index 27cdb9fd2..e30745a0f 100644 --- a/make-fs/make-fs.c +++ b/make-fs/make-fs.c @@ -40,6 +40,7 @@ #include "xstrtol.h" #include "getprogname.h" +#include "structs-cleanups.h" #include "options.h" #include "display-options.h" diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index fcd2923f5..8c94473da 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -110,6 +110,7 @@ OCAMLPACKAGES = \ -package str,unix \ -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/structs/.libs \ -I $(top_builddir)/lib/.libs \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ @@ -124,6 +125,7 @@ endif OCAMLCLIBS = \ -lvisit \ + -lstructs \ -lutils \ -lcleanups \ $(LIBTINFO_LIBS) \ diff --git a/tests/c-api/Makefile.am b/tests/c-api/Makefile.am index cc68e5e58..ab764db8d 100644 --- a/tests/c-api/Makefile.am +++ b/tests/c-api/Makefile.am @@ -94,12 +94,14 @@ tests_CPPFLAGS = \ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ -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 tests_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(PCRE_CFLAGS) tests_LDADD = \ $(PCRE_LIBS) \ + $(top_builddir)/common/structs/libstructs.la \ $(top_builddir)/common/utils/libutils.la \ $(top_builddir)/common/cleanups/libcleanups.la \ $(top_builddir)/lib/libguestfs.la \ diff --git a/tests/c-api/tests-main.c b/tests/c-api/tests-main.c index 0fd895423..f202a70ff 100644 --- a/tests/c-api/tests-main.c +++ b/tests/c-api/tests-main.c @@ -38,6 +38,7 @@ #include "guestfs.h" #include "guestfs-internal-frontend.h" +#include "structs-cleanups.h" #include "tests.h" -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 10/41] mllib, v2v: Split out OCaml utils bindings ‘common/mlutils’.
Create a module ‘C_utils’ containing functions like ‘drive_name’ and ‘shell_unquote’ which come from the C utilities. The new directory ‘common/mlutils’ also contains the ‘Unix_utils’ wrappers around POSIX functions missing from the OCaml stdlib. --- .gitignore | 3 + Makefile.am | 24 ++--- builder/Makefile.am | 6 +- common/mlutils/Makefile.am | 156 ++++++++++++++++++++++++++++ v2v/utils-c.c => common/mlutils/c_utils-c.c | 6 +- common/mlutils/c_utils.ml | 26 +++++ common/mlutils/c_utils.mli | 30 ++++++ common/mlutils/c_utils_unit_tests.ml | 81 +++++++++++++++ common/mlutils/dummy.c | 2 + {mllib => common/mlutils}/unix_utils-c.c | 0 {mllib => common/mlutils}/unix_utils.ml | 0 {mllib => common/mlutils}/unix_utils.mli | 0 configure.ac | 1 + customize/Makefile.am | 5 +- dib/Makefile.am | 6 +- docs/C_SOURCE_FILES | 5 +- docs/guestfs-hacking.pod | 5 + get-kernel/Makefile.am | 5 +- mllib/Makefile.am | 8 +- resize/Makefile.am | 6 +- sparsify/Makefile.am | 6 +- sysprep/Makefile.am | 5 +- v2v/Makefile.am | 12 ++- v2v/convert_linux.ml | 3 +- v2v/create_libvirt_xml.ml | 1 + v2v/parse_libvirt_xml.ml | 3 +- v2v/utils.ml | 5 - v2v/utils.mli | 11 -- v2v/v2v.ml | 1 + v2v/v2v_unit_tests.ml | 46 -------- 30 files changed, 371 insertions(+), 97 deletions(-) diff --git a/.gitignore b/.gitignore index 934d9f291..b20b5f5b8 100644 --- a/.gitignore +++ b/.gitignore @@ -130,6 +130,9 @@ Makefile.in /common/mlstdutils/libdir.ml /common/mlstdutils/oUnit-* /common/mlstdutils/std_utils_tests +/common/mlutils/.depend +/common/mlutils/c_utils_unit_tests +/common/mlutils/oUnit-* /common/mlvisit/.depend /common/mlvisit/visit_tests /common/mlxml/.depend diff --git a/Makefile.am b/Makefile.am index b3eb35349..509bcae83 100644 --- a/Makefile.am +++ b/Makefile.am @@ -156,18 +156,18 @@ SUBDIRS += csharp # OCaml tools. Note 'common/ml*', 'mllib' and 'customize' contain # shared code used by other OCaml tools, so these must come first. if HAVE_OCAML -SUBDIRS += \ - common/mlprogress \ - common/mlvisit \ - common/mlxml \ - mllib \ - customize \ - builder builder/templates \ - get-kernel \ - resize \ - sparsify \ - sysprep \ - v2v +SUBDIRS += common/mlutils +SUBDIRS += common/mlprogress +SUBDIRS += common/mlvisit +SUBDIRS += common/mlxml +SUBDIRS += mllib +SUBDIRS += customize +SUBDIRS += builder builder/templates +SUBDIRS += get-kernel +SUBDIRS += resize +SUBDIRS += sparsify +SUBDIRS += sysprep +SUBDIRS += v2v if HAVE_OCAML_PKG_LIBVIRT SUBDIRS += v2v/test-harness endif diff --git a/builder/Makefile.am b/builder/Makefile.am index cfe302f9d..355cfd0d1 100644 --- a/builder/Makefile.am +++ b/builder/Makefile.am @@ -127,6 +127,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize OCAMLPACKAGES_TESTS @@ -160,6 +161,7 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ customize.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) @@ -167,6 +169,7 @@ OCAMLLINKFLAGS = \ virt_builder_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -239,6 +242,7 @@ endif yajl_tests_DEPENDENCIES = \ $(yajl_tests_THEOBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -310,7 +314,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mlutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/common/mlutils/Makefile.am b/common/mlutils/Makefile.am new file mode 100644 index 000000000..d2cc9c36f --- /dev/null +++ b/common/mlutils/Makefile.am @@ -0,0 +1,156 @@ +# libguestfs OCaml tools common code +# Copyright (C) 2011-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. + +include $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + $(SOURCES_MLI) \ + $(SOURCES_ML) \ + $(SOURCES_C) + +SOURCES_MLI = \ + c_utils.mli \ + unix_utils.mli + +SOURCES_ML = \ + c_utils.ml \ + unix_utils.ml + +SOURCES_C = \ + c_utils-c.c \ + unix_utils-c.c + +if HAVE_OCAML + +# We pretend that we're building a C library. automake handles the +# compilation of the C sources for us. At the end we take the C +# objects and OCaml objects and link them into the OCaml library. +# This C library is never used. + +noinst_LIBRARIES = libmlcutils.a + +if !HAVE_OCAMLOPT +MLCUTILS_CMA = mlcutils.cma +else +MLCUTILS_CMA = mlcutils.cmxa +endif + +noinst_DATA = $(MLCUTILS_CMA) + +# lib/guestfs-internal-all.h header is used here. It probably +# shouldn't be located under lib. XXX +libmlcutils_a_SOURCES = $(SOURCES_C) +libmlcutils_a_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/common/cleanups -I$(top_builddir)/common/cleanups \ + -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/lib -I$(top_builddir)/lib \ + -I$(shell $(OCAMLC) -where) +libmlcutils_a_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) \ + -fPIC + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(BOBJECTS:.cmo=.cmx) + +OCAMLPACKAGES = \ + -package str,unix \ + -I $(top_builddir)/gnulib/lib/.libs \ + -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/cleanups/.libs \ + -I $(top_builddir)/common/mlstdutils \ + -I $(builddir) + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +else +OBJECTS = $(XOBJECTS) +endif + +libmlcutils_a_DEPENDENCIES = $(OBJECTS) + +$(MLCUTILS_CMA): $(OBJECTS) libmlcutils.a + $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlcutils_a_OBJECTS) \ + -cclib -lutils -cclib -lcleanups \ + -o mlcutils + +# Tests. + +TESTS +check_PROGRAMS + +if HAVE_OCAML_PKG_OUNIT +TESTS += c_utils_unit_tests +check_PROGRAMS += c_utils_unit_tests +endif + +c_utils_unit_tests_BOBJECTS = \ + c_utils_unit_tests.cmo +c_utils_unit_tests_XOBJECTS = $(c_utils_unit_tests_BOBJECTS:.cmo=.cmx) + +c_utils_unit_tests_SOURCES = dummy.c +c_utils_unit_tests_CPPFLAGS = $(libmlcutils_a_CPPFLAGS) +c_utils_unit_tests_CFLAGS = $(libmlcutils_a_CFLAGS) + +if !HAVE_OCAMLOPT +# Can't call this c_utils_unit_tests_OBJECTS because automake gets confused. +c_utils_unit_tests_THEOBJECTS = $(c_utils_unit_tests_BOBJECTS) +c_utils_unit_tests.cmo: OCAMLPACKAGES += -package oUnit +else +c_utils_unit_tests_THEOBJECTS = $(c_utils_unit_tests_XOBJECTS) +c_utils_unit_tests.cmx: OCAMLPACKAGES += -package oUnit +endif + +OCAMLLINKFLAGS = \ + mlstdutils.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) + +c_utils_unit_tests_DEPENDENCIES = \ + $(c_utils_unit_tests_THEOBJECTS) \ + ../mlstdutils/mlstdutils.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ + $(top_srcdir)/ocaml-link.sh +c_utils_unit_tests_LINK = \ + $(top_srcdir)/ocaml-link.sh -cclib '-lutils -lcleanups -lgnu' -- \ + $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) \ + $(OCAMLPACKAGES) -package oUnit \ + $(OCAMLLINKFLAGS) \ + $(c_utils_unit_tests_THEOBJECTS) -o $@ + +# Dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I $(abs_srcdir) $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + +endif + +.PHONY: depend docs diff --git a/v2v/utils-c.c b/common/mlutils/c_utils-c.c similarity index 93% rename from v2v/utils-c.c rename to common/mlutils/c_utils-c.c index 69b070fc2..32edbd4a7 100644 --- a/v2v/utils-c.c +++ b/common/mlutils/c_utils-c.c @@ -41,7 +41,7 @@ extern void unix_error (int errcode, char * cmdname, value arg) Noreturn; #pragma GCC diagnostic ignored "-Wmissing-prototypes" value -v2v_utils_drive_name (value indexv) +guestfs_int_mlutils_drive_name (value indexv) { CAMLparam1 (indexv); CAMLlocal1 (namev); @@ -54,7 +54,7 @@ v2v_utils_drive_name (value indexv) } value -v2v_utils_drive_index (value strv) +guestfs_int_mlutils_drive_index (value strv) { CAMLparam1 (strv); ssize_t r; @@ -67,7 +67,7 @@ v2v_utils_drive_index (value strv) } value -v2v_utils_shell_unquote (value strv) +guestfs_int_mlutils_shell_unquote (value strv) { CAMLparam1 (strv); CAMLlocal1 (retv); diff --git a/common/mlutils/c_utils.ml b/common/mlutils/c_utils.ml new file mode 100644 index 000000000..e4263962d --- /dev/null +++ b/common/mlutils/c_utils.ml @@ -0,0 +1,26 @@ +(* virt-v2v + * Copyright (C) 2009-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. + *) + +(* OCaml bindings for C utility functions in [common/utils]. *) + +open Printf + +external drive_name : int -> string = "guestfs_int_mlutils_drive_name" +external drive_index : string -> int = "guestfs_int_mlutils_drive_index" + +external shell_unquote : string -> string = "guestfs_int_mlutils_shell_unquote" diff --git a/common/mlutils/c_utils.mli b/common/mlutils/c_utils.mli new file mode 100644 index 000000000..7824f9658 --- /dev/null +++ b/common/mlutils/c_utils.mli @@ -0,0 +1,30 @@ +(* virt-v2v + * Copyright (C) 2009-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. + *) + +(** OCaml bindings for C utility functions in [common/utils]. *) + +val drive_name : int -> string +val drive_index : string -> int + +val shell_unquote : string -> string +(** If the string looks like a shell quoted string, then attempt to + unquote it. + + This is just intended to deal with quoting in configuration files + (like ones under /etc/sysconfig), and it doesn't deal with some + situations such as $variable interpolation. *) diff --git a/common/mlutils/c_utils_unit_tests.ml b/common/mlutils/c_utils_unit_tests.ml new file mode 100644 index 000000000..8840d6620 --- /dev/null +++ b/common/mlutils/c_utils_unit_tests.ml @@ -0,0 +1,81 @@ +(* virt-v2v + * Copyright (C) 2011-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. + *) + +(* This file tests individual OCaml bindings for C utility functions. *) + +open Printf + +open OUnit2 + +open Std_utils +open C_utils + +let test_drive_name ctx + let printer = identity in + assert_equal ~printer "a" (drive_name 0); + assert_equal ~printer "z" (drive_name 25); + assert_equal ~printer "aa" (drive_name 26); + assert_equal ~printer "ab" (drive_name 27); + assert_equal ~printer "az" (drive_name 51); + assert_equal ~printer "ba" (drive_name 52); + assert_equal ~printer "zz" (drive_name 701); + assert_equal ~printer "aaa" (drive_name 702); + assert_equal ~printer "zzz" (drive_name 18277) + +let test_drive_index ctx + let printer = string_of_int in + assert_equal ~printer 0 (drive_index "a"); + assert_equal ~printer 25 (drive_index "z"); + assert_equal ~printer 26 (drive_index "aa"); + assert_equal ~printer 27 (drive_index "ab"); + assert_equal ~printer 51 (drive_index "az"); + assert_equal ~printer 52 (drive_index "ba"); + assert_equal ~printer 701 (drive_index "zz"); + assert_equal ~printer 702 (drive_index "aaa"); + assert_equal ~printer 18277 (drive_index "zzz"); + let exn = Invalid_argument "drive_index: invalid parameter" in + assert_raises exn (fun () -> drive_index ""); + assert_raises exn (fun () -> drive_index "abc123"); + assert_raises exn (fun () -> drive_index "123"); + assert_raises exn (fun () -> drive_index "Z"); + assert_raises exn (fun () -> drive_index "aB") + +let test_shell_unquote ctx + let printer = identity in + assert_equal ~printer "a" (shell_unquote "a"); + assert_equal ~printer "b" (shell_unquote "'b'"); + assert_equal ~printer "c" (shell_unquote "\"c\""); + assert_equal ~printer "dd" (shell_unquote "\"dd\""); + assert_equal ~printer "e\\e" (shell_unquote "\"e\\\\e\""); + assert_equal ~printer "f\\" (shell_unquote "\"f\\\\\""); + assert_equal ~printer "\\g" (shell_unquote "\"\\\\g\""); + assert_equal ~printer "h\\-h" (shell_unquote "\"h\\-h\""); + assert_equal ~printer "i`" (shell_unquote "\"i\\`\""); + assert_equal ~printer "j\"" (shell_unquote "\"j\\\"\"") + +(* Suites declaration. *) +let suite + "C_utils" >::: + [ + "C_utils.drive_name" >:: test_drive_name; + "C_utils.drive_index" >:: test_drive_index; + "C_utils.shell_unquote" >:: test_shell_unquote; + ] + +let () + run_test_tt_main suite diff --git a/common/mlutils/dummy.c b/common/mlutils/dummy.c new file mode 100644 index 000000000..ebab6198c --- /dev/null +++ b/common/mlutils/dummy.c @@ -0,0 +1,2 @@ +/* Dummy source, to be used for OCaml-based tools with no C sources. */ +enum { foo = 1 }; diff --git a/mllib/unix_utils-c.c b/common/mlutils/unix_utils-c.c similarity index 100% rename from mllib/unix_utils-c.c rename to common/mlutils/unix_utils-c.c diff --git a/mllib/unix_utils.ml b/common/mlutils/unix_utils.ml similarity index 100% rename from mllib/unix_utils.ml rename to common/mlutils/unix_utils.ml diff --git a/mllib/unix_utils.mli b/common/mlutils/unix_utils.mli similarity index 100% rename from mllib/unix_utils.mli rename to common/mlutils/unix_utils.mli diff --git a/configure.ac b/configure.ac index 9b7f61a2e..000c09868 100644 --- a/configure.ac +++ b/configure.ac @@ -189,6 +189,7 @@ AC_CONFIG_FILES([Makefile common/mlprogress/Makefile common/mlstdutils/Makefile common/mlstdutils/guestfs_config.ml + common/mlutils/Makefile common/mlvisit/Makefile common/mlxml/Makefile common/options/Makefile diff --git a/customize/Makefile.am b/customize/Makefile.am index 815513b25..b845e512f 100644 --- a/customize/Makefile.am +++ b/customize/Makefile.am @@ -126,6 +126,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib \ -I $(builddir) if HAVE_OCAML_PKG_GETTEXT @@ -155,6 +156,7 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ customize.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) @@ -175,6 +177,7 @@ virt_customize_DEPENDENCIES = \ $(top_srcdir)/ocaml-link.sh \ $(CUSTOMIZE_THEOBJECTS) \ $(CUSTOMIZE_CMA) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) virt_customize_LINK = \ $(top_srcdir)/ocaml-link.sh -cclib '$(OCAMLCLIBS)' -- \ @@ -307,7 +310,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/dib/Makefile.am b/dib/Makefile.am index b10fa94c9..877225a85 100644 --- a/dib/Makefile.am +++ b/dib/Makefile.am @@ -75,11 +75,13 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) # installed copy of libguestfs. OCAMLPACKAGES = \ -package str,unix \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/common/utils/.libs \ -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)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -103,12 +105,14 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) virt_dib_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_dib_LINK = \ @@ -144,7 +148,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index fce01da3e..e856ffda0 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -21,6 +21,9 @@ common/miniexpect/miniexpect.c common/miniexpect/miniexpect.h common/mlprogress/progress-c.c common/mlstdutils/dummy.c +common/mlutils/c_utils-c.c +common/mlutils/dummy.c +common/mlutils/unix_utils-c.c common/mlvisit/dummy.c common/mlvisit/visit-c.c common/mlxml/xml-c.c @@ -344,7 +347,6 @@ make-fs/make-fs.c mllib/common_utils-c.c mllib/dummy.c mllib/getopt-c.c -mllib/unix_utils-c.c mllib/uri-c.c ocaml/guestfs-c-actions.c ocaml/guestfs-c-errnos.c @@ -410,4 +412,3 @@ utils/qemu-speed-test/qemu-speed-test.c v2v/libvirt_utils-c.c v2v/qemuopts-c.c v2v/test-harness/dummy.c -v2v/utils-c.c diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index 6ee4c236b..bd3d92233 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -113,6 +113,11 @@ A library of pure OCaml utility functions used in many places. OCaml bindings for the progress bar functions (see F<common/progress>). +=item F<common/mlutils> + +OCaml bindings for C functions in C<common/utils>, and some POSIX +bindings which are missing from the OCaml stdlib. + =item F<common/mlvisit> OCaml bindings for the visit functions (see F<common/visit>). diff --git a/get-kernel/Makefile.am b/get-kernel/Makefile.am index 16cf90eb9..1ca8be92c 100644 --- a/get-kernel/Makefile.am +++ b/get-kernel/Makefile.am @@ -66,6 +66,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -90,12 +91,14 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) virt_get_kernel_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_get_kernel_LINK = \ @@ -130,7 +133,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/mllib/Makefile.am b/mllib/Makefile.am index ee251e99d..51d71465b 100644 --- a/mllib/Makefile.am +++ b/mllib/Makefile.am @@ -28,7 +28,6 @@ EXTRA_DIST = \ SOURCES_MLI = \ checksums.mli \ - unix_utils.mli \ common_utils.mli \ curl.mli \ getopt.mli \ @@ -43,7 +42,6 @@ SOURCES_ML = \ $(OCAML_BYTES_COMPAT_ML) \ common_gettext.ml \ getopt.ml \ - unix_utils.ml \ common_utils.ml \ URI.ml \ planner.ml \ @@ -60,7 +58,6 @@ SOURCES_C = \ ../common/options/uri.c \ common_utils-c.c \ getopt-c.c \ - unix_utils-c.c \ uri-c.c if HAVE_OCAML @@ -91,7 +88,8 @@ libmllib_a_CPPFLAGS = \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/common/options \ -I$(top_srcdir)/common/mlxml \ - -I$(top_srcdir)/common/mlstdutils + -I$(top_srcdir)/common/mlstdutils \ + -I$(top_srcdir)/common/mlutils libmllib_a_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBVIRT_CFLAGS) $(LIBXML2_CFLAGS) \ @@ -112,6 +110,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(builddir) OCAMLPACKAGES_TESTS = $(MLLIB_CMA) if HAVE_OCAML_PKG_GETTEXT @@ -192,6 +191,7 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) diff --git a/resize/Makefile.am b/resize/Makefile.am index 3707d73b4..454ea4660 100644 --- a/resize/Makefile.am +++ b/resize/Makefile.am @@ -56,6 +56,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) # installed copy of libguestfs. OCAMLPACKAGES = \ -package str,unix \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/common/utils/.libs \ -I $(top_builddir)/common/progress/.libs \ -I $(top_builddir)/lib/.libs \ @@ -63,6 +64,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlprogress \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -88,12 +90,14 @@ OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlprogress.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) virt_resize_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_resize_LINK = \ @@ -138,7 +142,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/sparsify/Makefile.am b/sparsify/Makefile.am index a1395ccbd..8cd33d2be 100644 --- a/sparsify/Makefile.am +++ b/sparsify/Makefile.am @@ -61,6 +61,7 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) # installed copy of libguestfs. OCAMLPACKAGES = \ -package str,unix \ + -I $(top_builddir)/common/cleanups/.libs \ -I $(top_builddir)/common/utils/.libs \ -I $(top_builddir)/common/progress/.libs \ -I $(top_builddir)/lib/.libs \ @@ -68,6 +69,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ -I $(top_builddir)/common/mlprogress \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/mllib if HAVE_OCAML_PKG_GETTEXT OCAMLPACKAGES += -package gettext-stub @@ -93,12 +95,14 @@ OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlprogress.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) virt_sparsify_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_sparsify_LINK = \ @@ -145,7 +149,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/mllib $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am index 8c94473da..19a8b8803 100644 --- a/sysprep/Makefile.am +++ b/sysprep/Makefile.am @@ -116,6 +116,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/visit/.libs \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/common/mlvisit \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize @@ -145,6 +146,7 @@ endif OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ mlvisit.$(MLARCHIVE) \ customize.$(MLARCHIVE) \ @@ -153,6 +155,7 @@ OCAMLLINKFLAGS = \ virt_sysprep_DEPENDENCIES = \ $(OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ ../customize/customize.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh @@ -221,7 +224,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mlstdutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/Makefile.am b/v2v/Makefile.am index 41ff9af0b..4c4e3e210 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -111,8 +111,7 @@ SOURCES_ML = \ SOURCES_C = \ libvirt_utils-c.c \ - qemuopts-c.c \ - utils-c.c + qemuopts-c.c if HAVE_OCAML @@ -149,6 +148,7 @@ OCAMLPACKAGES = \ -I $(top_builddir)/gnulib/lib/.libs \ -I $(top_builddir)/ocaml \ -I $(top_builddir)/common/mlstdutils \ + -I $(top_builddir)/common/mlutils \ -I $(top_builddir)/common/mlxml \ -I $(top_builddir)/mllib \ -I $(top_builddir)/customize @@ -177,6 +177,7 @@ OCAMLLINKFLAGS = \ mlstdutils.$(MLARCHIVE) \ mlguestfs.$(MLARCHIVE) \ mlxml.$(MLARCHIVE) \ + mlcutils.$(MLARCHIVE) \ mllib.$(MLARCHIVE) \ $(LINK_CUSTOM_OCAMLC_ONLY) @@ -187,8 +188,7 @@ virt_v2v_LINK = \ $(OBJECTS) -o $@ virt_v2v_copy_to_local_SOURCES = \ - libvirt_utils-c.c \ - utils-c.c + libvirt_utils-c.c virt_v2v_copy_to_local_CPPFLAGS = \ -I. \ -I$(top_builddir) \ @@ -218,6 +218,7 @@ virt_v2v_copy_to_local_DEPENDENCIES = \ $(COPY_TO_LOCAL_OBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../common/mlxml/mlxml.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh virt_v2v_copy_to_local_LINK = \ @@ -504,6 +505,7 @@ v2v_unit_tests_DEPENDENCIES = \ $(v2v_unit_tests_THEOBJECTS) \ ../common/mlstdutils/mlstdutils.$(MLARCHIVE) \ ../common/mlxml/mlxml.$(MLARCHIVE) \ + ../common/mlutils/mlcutils.$(MLARCHIVE) \ ../mllib/mllib.$(MLARCHIVE) \ $(top_srcdir)/ocaml-link.sh v2v_unit_tests_LINK = \ @@ -518,7 +520,7 @@ depend: .depend .depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) rm -f $@ $@-t - $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ + $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils -I $(abs_top_builddir)/common/mlxml -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \ $(SED) 's/ *$$//' | \ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml index ffb43564f..c34bf3e91 100644 --- a/v2v/convert_linux.ml +++ b/v2v/convert_linux.ml @@ -28,6 +28,7 @@ open Printf +open C_utils open Std_utils open Common_utils open Common_gettext.Gettext @@ -186,7 +187,7 @@ let rec convert (g : G.guestfs) inspect source output rcaps fun line -> if Str.string_match rex line 0 then ( let path = Str.matched_group 1 line in - let path = Utils.shell_unquote path in + let path = shell_unquote path in if String.length path >= 1 && path.[0] = '/' then ( let vboxuninstall = path ^ "/uninstall.sh" in Some vboxuninstall diff --git a/v2v/create_libvirt_xml.ml b/v2v/create_libvirt_xml.ml index 3f22f3764..f5dca2d57 100644 --- a/v2v/create_libvirt_xml.ml +++ b/v2v/create_libvirt_xml.ml @@ -19,6 +19,7 @@ open Printf open Std_utils +open C_utils open Common_utils open Common_gettext.Gettext diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml index 56f9ea297..285cf2f44 100644 --- a/v2v/parse_libvirt_xml.ml +++ b/v2v/parse_libvirt_xml.ml @@ -18,6 +18,7 @@ open Printf +open C_utils open Std_utils open Common_utils open Common_gettext.Gettext @@ -39,7 +40,7 @@ and parsed_source *) let get_drive_slot str offset let name = String.sub str offset (String.length str - offset) in - try Some (Utils.drive_index name) + try Some (drive_index name) with Invalid_argument _ -> warning (f_"could not parse device name ‘%s’ from the source libvirt XML") str; None diff --git a/v2v/utils.ml b/v2v/utils.ml index 0dab5816e..0c6a1f444 100644 --- a/v2v/utils.ml +++ b/v2v/utils.ml @@ -24,11 +24,6 @@ open Std_utils open Common_utils open Common_gettext.Gettext -external drive_name : int -> string = "v2v_utils_drive_name" -external drive_index : string -> int = "v2v_utils_drive_index" - -external shell_unquote : string -> string = "v2v_utils_shell_unquote" - (* Map guest architecture found by inspection to the architecture * that KVM must emulate. Note for x86 we assume a 64 bit hypervisor. *) diff --git a/v2v/utils.mli b/v2v/utils.mli index 4906f0023..f267717d4 100644 --- a/v2v/utils.mli +++ b/v2v/utils.mli @@ -18,17 +18,6 @@ (** Utilities used in virt-v2v only. *) -val drive_name : int -> string -val drive_index : string -> int - -val shell_unquote : string -> string -(** If the string looks like a shell quoted string, then attempt to - unquote it. - - This is just intended to deal with quoting in configuration files - (like ones under /etc/sysconfig), and it doesn't deal with some - situations such as $variable interpolation. *) - val kvm_arch : string -> string (** Map guest architecture found by inspection to the architecture that KVM must emulate. Note for x86 we assume a 64 bit hypervisor. *) diff --git a/v2v/v2v.ml b/v2v/v2v.ml index f1ce9335a..00fbff2bc 100644 --- a/v2v/v2v.ml +++ b/v2v/v2v.ml @@ -19,6 +19,7 @@ open Unix open Printf +open C_utils open Std_utils open Common_utils open Unix_utils diff --git a/v2v/v2v_unit_tests.ml b/v2v/v2v_unit_tests.ml index be0bf0172..76f04f6fe 100644 --- a/v2v/v2v_unit_tests.ml +++ b/v2v/v2v_unit_tests.ml @@ -110,36 +110,6 @@ let test_get_ostype ctx i_product_variant = "Server"; i_arch = "x86_64" }) -let test_drive_name ctx - let printer = identity in - assert_equal ~printer "a" (Utils.drive_name 0); - assert_equal ~printer "z" (Utils.drive_name 25); - assert_equal ~printer "aa" (Utils.drive_name 26); - assert_equal ~printer "ab" (Utils.drive_name 27); - assert_equal ~printer "az" (Utils.drive_name 51); - assert_equal ~printer "ba" (Utils.drive_name 52); - assert_equal ~printer "zz" (Utils.drive_name 701); - assert_equal ~printer "aaa" (Utils.drive_name 702); - assert_equal ~printer "zzz" (Utils.drive_name 18277) - -let test_drive_index ctx - let printer = string_of_int in - assert_equal ~printer 0 (Utils.drive_index "a"); - assert_equal ~printer 25 (Utils.drive_index "z"); - assert_equal ~printer 26 (Utils.drive_index "aa"); - assert_equal ~printer 27 (Utils.drive_index "ab"); - assert_equal ~printer 51 (Utils.drive_index "az"); - assert_equal ~printer 52 (Utils.drive_index "ba"); - assert_equal ~printer 701 (Utils.drive_index "zz"); - assert_equal ~printer 702 (Utils.drive_index "aaa"); - assert_equal ~printer 18277 (Utils.drive_index "zzz"); - let exn = Invalid_argument "drive_index: invalid parameter" in - assert_raises exn (fun () -> Utils.drive_index ""); - assert_raises exn (fun () -> Utils.drive_index "abc123"); - assert_raises exn (fun () -> Utils.drive_index "123"); - assert_raises exn (fun () -> Utils.drive_index "Z"); - assert_raises exn (fun () -> Utils.drive_index "aB") - let test_virtio_iso_path_matches_guest_os ctx (* Windows OSes fake inspection data. *) let make_win name major minor variant arch = { @@ -779,19 +749,6 @@ let test_virtio_iso_path_matches_guest_os ctx ) all_windows ) paths -let test_shell_unquote ctx - let printer = identity in - assert_equal ~printer "a" (Utils.shell_unquote "a"); - assert_equal ~printer "b" (Utils.shell_unquote "'b'"); - assert_equal ~printer "c" (Utils.shell_unquote "\"c\""); - assert_equal ~printer "dd" (Utils.shell_unquote "\"dd\""); - assert_equal ~printer "e\\e" (Utils.shell_unquote "\"e\\\\e\""); - assert_equal ~printer "f\\" (Utils.shell_unquote "\"f\\\\\""); - assert_equal ~printer "\\g" (Utils.shell_unquote "\"\\\\g\""); - assert_equal ~printer "h\\-h" (Utils.shell_unquote "\"h\\-h\""); - assert_equal ~printer "i`" (Utils.shell_unquote "\"i\\`\""); - assert_equal ~printer "j\"" (Utils.shell_unquote "\"j\\\"\"") - let test_qemu_img_supports ctx (* No assertion here, we don't know if qemu-img supports the * feature, so just run the code and make sure it doesn't crash. @@ -945,11 +902,8 @@ let suite "virt-v2v" >::: [ "Create_ovf.get_ostype" >:: test_get_ostype; - "Utils.drive_name" >:: test_drive_name; - "Utils.drive_index" >:: test_drive_index; "Windows_virtio.virtio_iso_path_matches_guest_os" >:: test_virtio_iso_path_matches_guest_os; - "Utils.shell_unquote" >:: test_shell_unquote; "Utils.qemu_img_supports" >:: test_qemu_img_supports; "Parse_vmx.parse_string" >::test_vmx_parse_string; ] -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 11/41] utils: Rename ‘guestfs-internal-frontend.h’ to ‘utils.h’.
--- .gitignore | 2 +- builder/index-validate.c | 2 +- builder/pxzcat-c.c | 2 +- common/edit/file-edit.c | 2 +- common/mlutils/c_utils-c.c | 2 +- common/options/options.h | 2 +- common/options/uri.c | 2 +- common/parallel/domains.c | 2 +- common/parallel/estimate-max-threads.c | 2 +- common/parallel/parallel.c | 2 +- common/progress/progress.c | 2 +- common/utils/Makefile.am | 2 +- common/utils/utils.c | 2 +- common/utils/{guestfs-internal-frontend.h => utils.h} | 6 +++--- common/visit/visit.c | 2 +- common/windows/windows.c | 2 +- docs/C_SOURCE_FILES | 2 +- erlang/main.c | 2 +- fish/fish.h | 2 +- fuse/guestunmount.c | 2 +- fuse/test-fuse.c | 2 +- fuse/test-guestmount-fd.c | 2 +- fuse/test-guestunmount-fd.c | 2 +- generator/OCaml.ml | 2 +- generator/erlang.ml | 6 +++--- generator/fish.ml | 4 ++-- generator/java.ml | 2 +- generator/lua.ml | 2 +- generator/php.ml | 2 +- generator/python.ml | 2 +- generator/ruby.ml | 2 +- generator/tests_c_api.ml | 2 +- java/handle.c | 2 +- lib/Makefile.am | 2 +- lib/guestfs-internal-all.h | 9 ++++----- lib/guestfs-internal.h | 5 ++--- lib/unit-tests.c | 2 +- make-fs/make-fs.c | 2 +- mllib/getopt-c.c | 2 +- mllib/uri-c.c | 2 +- ocaml/guestfs-c.c | 2 +- p2v/p2v.h | 4 ++-- python/MANIFEST.in | 4 ++-- python/Makefile.am | 8 ++++---- rescue/escape.c | 2 +- rescue/rescue.c | 2 +- rescue/suggest.c | 2 +- test-tool/test-tool.c | 2 +- tests/c-api/test-add-drive-opts.c | 2 +- tests/c-api/test-add-libvirt-dom.c | 2 +- tests/c-api/test-backend-settings.c | 2 +- tests/c-api/test-config.c | 2 +- tests/c-api/test-create-handle.c | 2 +- tests/c-api/test-debug-to-file.c | 2 +- tests/c-api/test-environment.c | 2 +- tests/c-api/test-event-string.c | 2 +- tests/c-api/test-last-errno.c | 2 +- tests/c-api/test-private-data.c | 2 +- tests/c-api/test-user-cancel.c | 2 +- tests/c-api/tests-main.c | 2 +- tests/charsets/test-charset-fidelity.c | 2 +- tests/disks/test-add-disks.c | 2 +- tests/events/test-libvirt-auth-callbacks.c | 2 +- tests/mount-local/test-parallel-mount-local.c | 2 +- tests/parallel/test-parallel.c | 2 +- tests/regressions/rhbz1055452.c | 2 +- tests/regressions/rhbz501893.c | 2 +- tests/regressions/rhbz790721.c | 2 +- tests/regressions/rhbz914931.c | 2 +- tests/regressions/test-big-heap.c | 2 +- utils/boot-analysis/boot-analysis-timeline.c | 2 +- utils/boot-analysis/boot-analysis-utils.c | 2 +- utils/boot-analysis/boot-analysis.c | 2 +- utils/boot-benchmark/boot-benchmark.c | 2 +- utils/qemu-boot/qemu-boot.c | 2 +- utils/qemu-speed-test/qemu-speed-test.c | 2 +- v2v/libvirt_utils-c.c | 2 +- 77 files changed, 91 insertions(+), 93 deletions(-) diff --git a/.gitignore b/.gitignore index b20b5f5b8..ec332bc32 100644 --- a/.gitignore +++ b/.gitignore @@ -500,7 +500,6 @@ Makefile.in /python/guestfs.pyc /python/guestfs.pyo /python/guestfs-internal-all.h -/python/guestfs-internal-frontend.h /python/ignore-value.h /python/MANIFEST /python/module.c @@ -511,6 +510,7 @@ Makefile.in /python/stamp-extra-files /python/t/tests_helper.py /python/utils.c +/python/utils.h /qemu-wrapper.sh /rescue/stamp-virt-rescue.pod /rescue/virt-rescue diff --git a/builder/index-validate.c b/builder/index-validate.c index 224cd674a..a830ad8c6 100644 --- a/builder/index-validate.c +++ b/builder/index-validate.c @@ -31,7 +31,7 @@ #include <guestfs.h> #include "getprogname.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "index-struct.h" #include "index-parse.h" diff --git a/builder/pxzcat-c.c b/builder/pxzcat-c.c index ef37849ed..296d0d170 100644 --- a/builder/pxzcat-c.c +++ b/builder/pxzcat-c.c @@ -36,7 +36,7 @@ #include <caml/mlvalues.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "ignore-value.h" diff --git a/common/edit/file-edit.c b/common/edit/file-edit.c index b0347e78f..3c09d1985 100644 --- a/common/edit/file-edit.c +++ b/common/edit/file-edit.c @@ -41,7 +41,7 @@ #include <utime.h> #include <sys/wait.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "file-edit.h" diff --git a/common/mlutils/c_utils-c.c b/common/mlutils/c_utils-c.c index 32edbd4a7..b8ccee0af 100644 --- a/common/mlutils/c_utils-c.c +++ b/common/mlutils/c_utils-c.c @@ -36,7 +36,7 @@ extern void unix_error (int errcode, char * cmdname, value arg) Noreturn; #endif #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #pragma GCC diagnostic ignored "-Wmissing-prototypes" diff --git a/common/options/options.h b/common/options/options.h index 6bf2c5863..7eafa73e4 100644 --- a/common/options/options.h +++ b/common/options/options.h @@ -23,7 +23,7 @@ #include <stdbool.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" /* Provided by guestfish or guestmount. */ extern guestfs_h *g; diff --git a/common/options/uri.c b/common/options/uri.c index e7ba7a51a..a6a315dc7 100644 --- a/common/options/uri.c +++ b/common/options/uri.c @@ -37,7 +37,7 @@ #include "getprogname.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "uri.h" static int is_uri (const char *arg); diff --git a/common/parallel/domains.c b/common/parallel/domains.c index d5d3ae9a1..a3431bd76 100644 --- a/common/parallel/domains.c +++ b/common/parallel/domains.c @@ -41,7 +41,7 @@ #endif #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "domains.h" #if defined(HAVE_LIBVIRT) diff --git a/common/parallel/estimate-max-threads.c b/common/parallel/estimate-max-threads.c index 27ed80db9..44f3b53a3 100644 --- a/common/parallel/estimate-max-threads.c +++ b/common/parallel/estimate-max-threads.c @@ -26,7 +26,7 @@ #include <libintl.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "estimate-max-threads.h" static char *read_line_from (const char *cmd); diff --git a/common/parallel/parallel.c b/common/parallel/parallel.c index 97b9a3a0c..0e9fadcca 100644 --- a/common/parallel/parallel.c +++ b/common/parallel/parallel.c @@ -47,7 +47,7 @@ #include "getprogname.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "options.h" #include "domains.h" #include "estimate-max-threads.h" diff --git a/common/progress/progress.c b/common/progress/progress.c index 20c15eab4..79b137fef 100644 --- a/common/progress/progress.c +++ b/common/progress/progress.c @@ -32,7 +32,7 @@ #include <langinfo.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "progress.h" diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am index 9aabda4e2..49e0bc617 100644 --- a/common/utils/Makefile.am +++ b/common/utils/Makefile.am @@ -21,7 +21,7 @@ noinst_LTLIBRARIES = libutils.la libutils_la_SOURCES = \ ../../lib/guestfs.h \ - guestfs-internal-frontend.h \ + utils.h \ utils.c libutils_la_CPPFLAGS = \ -DGUESTFS_WARN_DEPRECATED=1 \ diff --git a/common/utils/utils.c b/common/utils/utils.c index 4ac214ded..35f1c08a6 100644 --- a/common/utils/utils.c +++ b/common/utils/utils.c @@ -47,7 +47,7 @@ /* NB: MUST NOT include "guestfs-internal.h". */ #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" void guestfs_int_free_string_list (char **argv) diff --git a/common/utils/guestfs-internal-frontend.h b/common/utils/utils.h similarity index 97% rename from common/utils/guestfs-internal-frontend.h rename to common/utils/utils.h index 8de38bf9b..8ea1fb37b 100644 --- a/common/utils/guestfs-internal-frontend.h +++ b/common/utils/utils.h @@ -29,8 +29,8 @@ * B<not> be here! */ -#ifndef GUESTFS_INTERNAL_FRONTEND_H_ -#define GUESTFS_INTERNAL_FRONTEND_H_ +#ifndef GUESTFS_UTILS_H_ +#define GUESTFS_UTILS_H_ #include <stdbool.h> @@ -111,4 +111,4 @@ extern void guestfs_int_cleanup_free_string_list (char ***ptr); fputs ("\033[0m", (fp)); \ } while (0) -#endif /* GUESTFS_INTERNAL_FRONTEND_H_ */ +#endif /* GUESTFS_UTILS_H_ */ diff --git a/common/visit/visit.c b/common/visit/visit.c index 491f9dda3..454191d0c 100644 --- a/common/visit/visit.c +++ b/common/visit/visit.c @@ -36,7 +36,7 @@ #include "getprogname.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "structs-cleanups.h" #include "visit.h" diff --git a/common/windows/windows.c b/common/windows/windows.c index 1fd94e0e6..b9269a794 100644 --- a/common/windows/windows.c +++ b/common/windows/windows.c @@ -34,7 +34,7 @@ #include <langinfo.h> #include <libintl.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "c-ctype.h" diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index e856ffda0..99de982a6 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -53,8 +53,8 @@ common/structs/structs-cleanups.c common/structs/structs-cleanups.h common/structs/structs-print.c common/structs/structs-print.h -common/utils/guestfs-internal-frontend.h common/utils/utils.c +common/utils/utils.h common/visit/visit.c common/visit/visit.h common/windows/windows.c diff --git a/erlang/main.c b/erlang/main.c index 54df4822f..4e22e3aa0 100644 --- a/erlang/main.c +++ b/erlang/main.c @@ -36,7 +36,7 @@ instead of erl_interface. #include "full-write.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" guestfs_h *g; diff --git a/fish/fish.h b/fish/fish.h index df22e34e7..b858b9bf1 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -21,7 +21,7 @@ #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "fish-cmds.h" diff --git a/fuse/guestunmount.c b/fuse/guestunmount.c index 2b2c493f8..5938b8a71 100644 --- a/fuse/guestunmount.c +++ b/fuse/guestunmount.c @@ -35,7 +35,7 @@ #include <sys/wait.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "ignore-value.h" #include "getprogname.h" diff --git a/fuse/test-fuse.c b/fuse/test-fuse.c index 546c08a0b..c7b6881a4 100644 --- a/fuse/test-fuse.c +++ b/fuse/test-fuse.c @@ -50,7 +50,7 @@ #endif #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "ignore-value.h" diff --git a/fuse/test-guestmount-fd.c b/fuse/test-guestmount-fd.c index abef7b293..63ac8c1d8 100644 --- a/fuse/test-guestmount-fd.c +++ b/fuse/test-guestmount-fd.c @@ -33,7 +33,7 @@ #include "getprogname.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #define GUESTMOUNT_BINARY "guestmount" #define GUESTUNMOUNT_BINARY "guestunmount" diff --git a/fuse/test-guestunmount-fd.c b/fuse/test-guestunmount-fd.c index 6756f18cb..4daa72de4 100644 --- a/fuse/test-guestunmount-fd.c +++ b/fuse/test-guestunmount-fd.c @@ -36,7 +36,7 @@ #include "ignore-value.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/generator/OCaml.ml b/generator/OCaml.ml index f6a4292b9..d3929475a 100644 --- a/generator/OCaml.ml +++ b/generator/OCaml.ml @@ -425,7 +425,7 @@ and generate_ocaml_c () #include <caml/signals.h> #include <guestfs.h> -#include \"guestfs-internal-frontend.h\" +#include \"utils.h\" #include \"guestfs-c.h\" diff --git a/generator/erlang.ml b/generator/erlang.ml index 03cca3368..a0e295768 100644 --- a/generator/erlang.ml +++ b/generator/erlang.ml @@ -254,7 +254,7 @@ instead of erl_interface. */ #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"utils.h\" #include \"actions.h\" "; @@ -344,7 +344,7 @@ instead of erl_interface. */ #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"utils.h\" #include \"actions.h\" "; @@ -535,7 +535,7 @@ instead of erl_interface. */ #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"utils.h\" #include \"actions.h\" diff --git a/generator/fish.ml b/generator/fish.ml index 3d99c9081..c09423b79 100644 --- a/generator/fish.ml +++ b/generator/fish.ml @@ -99,7 +99,7 @@ let generate_fish_run_cmds actions () pr "#include \"getprogname.h\"\n"; pr "\n"; pr "#include \"guestfs.h\"\n"; - pr "#include \"guestfs-internal-frontend.h\"\n"; + pr "#include \"utils.h\"\n"; pr "#include \"structs-print.h\"\n"; pr "\n"; pr "#include \"fish.h\"\n"; @@ -607,7 +607,7 @@ let generate_fish_cmds () pr "#include <errno.h>\n"; pr "\n"; pr "#include \"guestfs.h\"\n"; - pr "#include \"guestfs-internal-frontend.h\"\n"; + pr "#include \"utils.h\"\n"; pr "#include \"structs-print.h\"\n"; pr "\n"; pr "#include \"fish.h\"\n"; diff --git a/generator/java.ml b/generator/java.ml index a7d0ed359..6a3b09992 100644 --- a/generator/java.ml +++ b/generator/java.ml @@ -585,7 +585,7 @@ and generate_java_c actions () #include \"com_redhat_et_libguestfs_GuestFS.h\" #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"utils.h\" #include \"structs-cleanups.h\" /* Note that this function returns. The exception is not thrown diff --git a/generator/lua.ml b/generator/lua.ml index b40c51753..c76e429b5 100644 --- a/generator/lua.ml +++ b/generator/lua.ml @@ -64,7 +64,7 @@ let generate_lua_c () #endif #include <guestfs.h> -#include \"guestfs-internal-frontend.h\" +#include \"utils.h\" #define GUESTFS_LUA_HANDLE \"guestfs handle\" diff --git a/generator/php.ml b/generator/php.ml index 0721e431a..7c6caae6b 100644 --- a/generator/php.ml +++ b/generator/php.ml @@ -90,7 +90,7 @@ and generate_php_c () #include <php_guestfs_php.h> #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" /* Only for POINTER_NOT_IMPLEMENTED */ +#include \"utils.h\" /* Only for POINTER_NOT_IMPLEMENTED */ static int res_guestfs_h; diff --git a/generator/python.ml b/generator/python.ml index c6c237241..20bab0312 100644 --- a/generator/python.ml +++ b/generator/python.ml @@ -42,7 +42,7 @@ let rec generate_python_actions_h () #define GUESTFS_PYTHON_ACTIONS_H_ #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"utils.h\" #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; diff --git a/generator/ruby.ml b/generator/ruby.ml index 825cab32a..75a93398d 100644 --- a/generator/ruby.ml +++ b/generator/ruby.ml @@ -55,7 +55,7 @@ let rec generate_ruby_h () #endif #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" /* Only for POINTER_NOT_IMPLEMENTED */ +#include \"utils.h\" /* Only for POINTER_NOT_IMPLEMENTED */ #include \"extconf.h\" diff --git a/generator/tests_c_api.ml b/generator/tests_c_api.ml index c3cb62c4d..ba4647ab3 100644 --- a/generator/tests_c_api.ml +++ b/generator/tests_c_api.ml @@ -47,7 +47,7 @@ let rec generate_c_api_tests () #include <errno.h> #include \"guestfs.h\" -#include \"guestfs-internal-frontend.h\" +#include \"utils.h\" #include \"structs-cleanups.h\" #include \"tests.h\" diff --git a/java/handle.c b/java/handle.c index 0993f33ed..9080caaf4 100644 --- a/java/handle.c +++ b/java/handle.c @@ -25,7 +25,7 @@ #include "com_redhat_et_libguestfs_GuestFS.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" /* This is the opaque data passed between _set_event_callback and * the C wrapper which calls the Java event callback. diff --git a/lib/Makefile.am b/lib/Makefile.am index 9cca1268a..b1cb39105 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -57,7 +57,7 @@ libguestfs_la_SOURCES = \ ../common/errnostring/errnostring.h \ ../common/protocol/guestfs_protocol.h \ ../common/qemuopts/qemuopts.h \ - ../common/utils/guestfs-internal-frontend.h \ + ../common/utils/utils.h \ ../common/structs/structs-cleanups.h \ guestfs.h \ guestfs-internal.h \ diff --git a/lib/guestfs-internal-all.h b/lib/guestfs-internal-all.h index bf87b8295..e7ffd1826 100644 --- a/lib/guestfs-internal-all.h +++ b/lib/guestfs-internal-all.h @@ -22,11 +22,10 @@ * tools (ie. I<all> C code). * * If you need a definition used by only the library, put it in - * F<lib/guestfs-internal.h> instead. If you need a definition used - * by only the frontend (non-daemon) parts of libguestfs, try - * F<lib/guestfs-internal-frontend.h>. If a definition is used by - * only a single tool, it should not be in any shared header file at - * all. + * F<lib/guestfs-internal.h> instead. + * + * If a definition is used by only a single tool, it should not be in + * any shared header file at all. */ #ifndef GUESTFS_INTERNAL_ALL_H_ diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h index 190b5cdd2..b1c3db5ff 100644 --- a/lib/guestfs-internal.h +++ b/lib/guestfs-internal.h @@ -20,8 +20,7 @@ * This header file is included in the libguestfs library (F<lib/>) * only. * - * See also F<lib/guestfs-internal-frontend.h> and - * F<lib/guestfs-internal-all.h> + * See also F<lib/guestfs-internal-all.h>. */ #ifndef GUESTFS_INTERNAL_H_ @@ -55,7 +54,7 @@ #include "hash.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #if ENABLE_PROBES #include <sys/sdt.h> diff --git a/lib/unit-tests.c b/lib/unit-tests.c index d44bc41e0..a9ba4cbc2 100644 --- a/lib/unit-tests.c +++ b/lib/unit-tests.c @@ -38,7 +38,7 @@ #include "guestfs.h" #include "guestfs-internal.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" /** * Test C<guestfs_int_split_string>. diff --git a/make-fs/make-fs.c b/make-fs/make-fs.c index e30745a0f..103dbebd1 100644 --- a/make-fs/make-fs.c +++ b/make-fs/make-fs.c @@ -35,7 +35,7 @@ #include <sys/wait.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "xstrtol.h" #include "getprogname.h" diff --git a/mllib/getopt-c.c b/mllib/getopt-c.c index d2d98768c..7b9af5958 100644 --- a/mllib/getopt-c.c +++ b/mllib/getopt-c.c @@ -40,7 +40,7 @@ #include <caml/callback.h> #include <caml/printexc.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" extern value guestfs_int_mllib_getopt_parse (value argsv, value specsv, value anon_funv, value usage_msgv); diff --git a/mllib/uri-c.c b/mllib/uri-c.c index ffb55306b..592d09939 100644 --- a/mllib/uri-c.c +++ b/mllib/uri-c.c @@ -31,7 +31,7 @@ #include <caml/mlvalues.h> #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "uri.h" extern value guestfs_int_mllib_parse_uri (value argv); diff --git a/ocaml/guestfs-c.c b/ocaml/guestfs-c.c index 0df57758d..94288f464 100644 --- a/ocaml/guestfs-c.c +++ b/ocaml/guestfs-c.c @@ -23,7 +23,7 @@ #include <errno.h> #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" #include <caml/config.h> #include <caml/alloc.h> diff --git a/p2v/p2v.h b/p2v/p2v.h index be913c024..1b225db6f 100644 --- a/p2v/p2v.h +++ b/p2v/p2v.h @@ -32,10 +32,10 @@ /* We don't use libguestfs directly here, and we don't link to it * either (in fact, we don't want libguestfs on the ISO). However * we include this just so that we can use the convenience macros in - * guestfs-internal-frontend.h. + * utils.h. */ #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" /* Ensure we don't use libguestfs. */ #define guestfs_h DO_NOT_USE diff --git a/python/MANIFEST.in b/python/MANIFEST.in index 116367d16..69db7e5ce 100644 --- a/python/MANIFEST.in +++ b/python/MANIFEST.in @@ -17,8 +17,8 @@ include actions.h include c-ctype.h +include cleanups.h include config.h include guestfs-internal-all.h -include guestfs-internal-frontend-cleanups.h -include guestfs-internal-frontend.h +include utils.h include ignore-value.h diff --git a/python/Makefile.am b/python/Makefile.am index fd0825648..d0f324785 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -102,7 +102,7 @@ stamp-extra-files: \ cleanups.h \ config.h \ guestfs-internal-all.h \ - guestfs-internal-frontend.h \ + utils.h \ ignore-value.h \ stdlib-cleanups.c \ utils.c @@ -123,8 +123,8 @@ ignore-value.h: guestfs-internal-all.h: ln $(top_srcdir)/lib/guestfs-internal-all.h $@ -guestfs-internal-frontend.h: - ln $(top_srcdir)/common/utils/guestfs-internal-frontend.h $@ +utils.h: + ln $(top_srcdir)/common/utils/utils.h $@ stdlib-cleanups.c: ln $(top_srcdir)/common/cleanups/stdlib-cleanups.c $@ @@ -153,7 +153,7 @@ CLEANFILES += \ config.h \ guestfs-internal-all.h \ guestfs-internal-frontend-cleanups.h \ - guestfs-internal-frontend.h \ + utils.h \ ignore-value.h \ stamp-extra-files \ utils.c diff --git a/rescue/escape.c b/rescue/escape.c index f7f7d84c4..a60c6b9db 100644 --- a/rescue/escape.c +++ b/rescue/escape.c @@ -30,7 +30,7 @@ #include "c-ctype.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "rescue.h" diff --git a/rescue/rescue.c b/rescue/rescue.c index 25bd55077..901a864a3 100644 --- a/rescue/rescue.c +++ b/rescue/rescue.c @@ -40,7 +40,7 @@ #include "xvasprintf.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "windows.h" #include "options.h" diff --git a/rescue/suggest.c b/rescue/suggest.c index f32bb83c7..d9903c33a 100644 --- a/rescue/suggest.c +++ b/rescue/suggest.c @@ -25,7 +25,7 @@ #include <libintl.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "options.h" diff --git a/test-tool/test-tool.c b/test-tool/test-tool.c index 2d63b4f4e..106ce399b 100644 --- a/test-tool/test-tool.c +++ b/test-tool/test-tool.c @@ -36,7 +36,7 @@ #include <libintl.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "ignore-value.h" diff --git a/tests/c-api/test-add-drive-opts.c b/tests/c-api/test-add-drive-opts.c index 4f39e2690..504e7024d 100644 --- a/tests/c-api/test-add-drive-opts.c +++ b/tests/c-api/test-add-drive-opts.c @@ -26,7 +26,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-add-libvirt-dom.c b/tests/c-api/test-add-libvirt-dom.c index 672e8612f..fb841350c 100644 --- a/tests/c-api/test-add-libvirt-dom.c +++ b/tests/c-api/test-add-libvirt-dom.c @@ -31,7 +31,7 @@ #include "xgetcwd.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" static void make_test_xml (FILE *fp, const char *cwd) diff --git a/tests/c-api/test-backend-settings.c b/tests/c-api/test-backend-settings.c index 371a24776..d639579ca 100644 --- a/tests/c-api/test-backend-settings.c +++ b/tests/c-api/test-backend-settings.c @@ -29,7 +29,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-config.c b/tests/c-api/test-config.c index fef6c64e5..c8ed83cc7 100644 --- a/tests/c-api/test-config.c +++ b/tests/c-api/test-config.c @@ -26,7 +26,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-create-handle.c b/tests/c-api/test-create-handle.c index 170649926..3fa6f1633 100644 --- a/tests/c-api/test-create-handle.c +++ b/tests/c-api/test-create-handle.c @@ -26,7 +26,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-debug-to-file.c b/tests/c-api/test-debug-to-file.c index 10e36c63b..1e5330c64 100644 --- a/tests/c-api/test-debug-to-file.c +++ b/tests/c-api/test-debug-to-file.c @@ -30,7 +30,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "ignore-value.h" diff --git a/tests/c-api/test-environment.c b/tests/c-api/test-environment.c index d5a03d4fb..f5d4642eb 100644 --- a/tests/c-api/test-environment.c +++ b/tests/c-api/test-environment.c @@ -31,7 +31,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-event-string.c b/tests/c-api/test-event-string.c index e94000d0a..8cbd1220f 100644 --- a/tests/c-api/test-event-string.c +++ b/tests/c-api/test-event-string.c @@ -27,7 +27,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-last-errno.c b/tests/c-api/test-last-errno.c index acd5cd97d..113bdd84f 100644 --- a/tests/c-api/test-last-errno.c +++ b/tests/c-api/test-last-errno.c @@ -31,7 +31,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/c-api/test-private-data.c b/tests/c-api/test-private-data.c index 9ebe7a892..88752e585 100644 --- a/tests/c-api/test-private-data.c +++ b/tests/c-api/test-private-data.c @@ -29,7 +29,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #define PREFIX "test_" diff --git a/tests/c-api/test-user-cancel.c b/tests/c-api/test-user-cancel.c index d429a0fe3..d7c1dfe3d 100644 --- a/tests/c-api/test-user-cancel.c +++ b/tests/c-api/test-user-cancel.c @@ -48,7 +48,7 @@ #include "cloexec.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" static const off_t filesize = 1024*1024*1024; diff --git a/tests/c-api/tests-main.c b/tests/c-api/tests-main.c index f202a70ff..171376a12 100644 --- a/tests/c-api/tests-main.c +++ b/tests/c-api/tests-main.c @@ -37,7 +37,7 @@ #define GUESTFS_WARN_DEPRECATED 1 #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "structs-cleanups.h" #include "tests.h" diff --git a/tests/charsets/test-charset-fidelity.c b/tests/charsets/test-charset-fidelity.c index 952256edd..04956d75e 100644 --- a/tests/charsets/test-charset-fidelity.c +++ b/tests/charsets/test-charset-fidelity.c @@ -31,7 +31,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "getprogname.h" diff --git a/tests/disks/test-add-disks.c b/tests/disks/test-add-disks.c index b968bd352..91a120316 100644 --- a/tests/disks/test-add-disks.c +++ b/tests/disks/test-add-disks.c @@ -34,7 +34,7 @@ #include <assert.h> #include <guestfs.h> -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "getprogname.h" diff --git a/tests/events/test-libvirt-auth-callbacks.c b/tests/events/test-libvirt-auth-callbacks.c index a18ab2429..11f31f294 100644 --- a/tests/events/test-libvirt-auth-callbacks.c +++ b/tests/events/test-libvirt-auth-callbacks.c @@ -28,7 +28,7 @@ #include <libvirt/libvirt.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #define EXPECT_OK 1 #define EXPECT_FAIL -1 diff --git a/tests/mount-local/test-parallel-mount-local.c b/tests/mount-local/test-parallel-mount-local.c index 542f1ed3d..2d1c35368 100644 --- a/tests/mount-local/test-parallel-mount-local.c +++ b/tests/mount-local/test-parallel-mount-local.c @@ -38,7 +38,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "estimate-max-threads.h" #include "ignore-value.h" diff --git a/tests/parallel/test-parallel.c b/tests/parallel/test-parallel.c index 276340e4b..437535791 100644 --- a/tests/parallel/test-parallel.c +++ b/tests/parallel/test-parallel.c @@ -36,7 +36,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "ignore-value.h" #include "getprogname.h" diff --git a/tests/regressions/rhbz1055452.c b/tests/regressions/rhbz1055452.c index 79cd54033..d00a40a8f 100644 --- a/tests/regressions/rhbz1055452.c +++ b/tests/regressions/rhbz1055452.c @@ -33,7 +33,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/regressions/rhbz501893.c b/tests/regressions/rhbz501893.c index b763a107b..fc6a9e63a 100644 --- a/tests/regressions/rhbz501893.c +++ b/tests/regressions/rhbz501893.c @@ -24,7 +24,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/tests/regressions/rhbz790721.c b/tests/regressions/rhbz790721.c index 2dfcd2768..778b68ccf 100644 --- a/tests/regressions/rhbz790721.c +++ b/tests/regressions/rhbz790721.c @@ -43,7 +43,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "getprogname.h" diff --git a/tests/regressions/rhbz914931.c b/tests/regressions/rhbz914931.c index bfc8f83ea..26c24e7db 100644 --- a/tests/regressions/rhbz914931.c +++ b/tests/regressions/rhbz914931.c @@ -32,7 +32,7 @@ #include <error.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "getprogname.h" diff --git a/tests/regressions/test-big-heap.c b/tests/regressions/test-big-heap.c index 1cbf8d23c..012c5b803 100644 --- a/tests/regressions/test-big-heap.c +++ b/tests/regressions/test-big-heap.c @@ -33,7 +33,7 @@ #include <assert.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" int main (int argc, char *argv[]) diff --git a/utils/boot-analysis/boot-analysis-timeline.c b/utils/boot-analysis/boot-analysis-timeline.c index 8198c5677..98150bf64 100644 --- a/utils/boot-analysis/boot-analysis-timeline.c +++ b/utils/boot-analysis/boot-analysis-timeline.c @@ -33,7 +33,7 @@ #include "ignore-value.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "boot-analysis.h" diff --git a/utils/boot-analysis/boot-analysis-utils.c b/utils/boot-analysis/boot-analysis-utils.c index 4308bead3..faf2c5bc6 100644 --- a/utils/boot-analysis/boot-analysis-utils.c +++ b/utils/boot-analysis/boot-analysis-utils.c @@ -27,7 +27,7 @@ #include "ignore-value.h" #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "boot-analysis-utils.h" diff --git a/utils/boot-analysis/boot-analysis.c b/utils/boot-analysis/boot-analysis.c index 1bec9a57b..2bd866182 100644 --- a/utils/boot-analysis/boot-analysis.c +++ b/utils/boot-analysis/boot-analysis.c @@ -40,7 +40,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "boot-analysis.h" #include "boot-analysis-utils.h" diff --git a/utils/boot-benchmark/boot-benchmark.c b/utils/boot-benchmark/boot-benchmark.c index 4af394387..b07543e37 100644 --- a/utils/boot-benchmark/boot-benchmark.c +++ b/utils/boot-benchmark/boot-benchmark.c @@ -34,7 +34,7 @@ #include <math.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "boot-analysis-utils.h" diff --git a/utils/qemu-boot/qemu-boot.c b/utils/qemu-boot/qemu-boot.c index 68818358b..b34b4898a 100644 --- a/utils/qemu-boot/qemu-boot.c +++ b/utils/qemu-boot/qemu-boot.c @@ -36,7 +36,7 @@ #include <pthread.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "estimate-max-threads.h" #include "getprogname.h" diff --git a/utils/qemu-speed-test/qemu-speed-test.c b/utils/qemu-speed-test/qemu-speed-test.c index 5aa663c14..59193e52a 100644 --- a/utils/qemu-speed-test/qemu-speed-test.c +++ b/utils/qemu-speed-test/qemu-speed-test.c @@ -39,7 +39,7 @@ #include <sys/time.h> #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #include "getprogname.h" diff --git a/v2v/libvirt_utils-c.c b/v2v/libvirt_utils-c.c index 98ed30e06..54ba6fb48 100644 --- a/v2v/libvirt_utils-c.c +++ b/v2v/libvirt_utils-c.c @@ -41,7 +41,7 @@ #endif #include "guestfs.h" -#include "guestfs-internal-frontend.h" +#include "utils.h" #pragma GCC diagnostic ignored "-Wmissing-prototypes" -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 12/41] common/mlstdutils: Implement complete set of byte swapping functions.
This implements all of: val int_of_le16 : string -> int64 val le16_of_int : int64 -> string val int_of_be16 : string -> int64 val be16_of_int : int64 -> string val int_of_le32 : string -> int64 val le32_of_int : int64 -> string val int_of_be32 : string -> int64 val be32_of_int : int64 -> string val int_of_le64 : string -> int64 val le64_of_int : int64 -> string val int_of_be64 : string -> int64 val be64_of_int : int64 -> string and tests. --- common/mlstdutils/std_utils.ml | 131 +++++++++++++++++++++++++++++++++++ common/mlstdutils/std_utils.mli | 23 +++++- common/mlstdutils/std_utils_tests.ml | 22 ++++-- 3 files changed, 169 insertions(+), 7 deletions(-) diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml index 7b8d65f66..f545c6f7a 100644 --- a/common/mlstdutils/std_utils.ml +++ b/common/mlstdutils/std_utils.ml @@ -272,6 +272,21 @@ external identity : 'a -> 'a = "%identity" let roundup64 i a = let a = a -^ 1L in (i +^ a) &^ (~^ a) let div_roundup64 i a = (i +^ a -^ 1L) /^ a +let int_of_le16 str + assert (String.length str = 2); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + Int64.of_int c0 +^ + (Int64.shift_left (Int64.of_int c1) 8) + +let le16_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let b = Bytes.create 2 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.to_string b + let int_of_le32 str assert (String.length str = 4); let c0 = Char.code (String.unsafe_get str 0) in @@ -295,6 +310,122 @@ let le32_of_int i Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c3)); Bytes.to_string b +let int_of_le64 str + assert (String.length str = 8); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + let c2 = Char.code (String.unsafe_get str 2) in + let c3 = Char.code (String.unsafe_get str 3) in + let c4 = Char.code (String.unsafe_get str 4) in + let c5 = Char.code (String.unsafe_get str 5) in + let c6 = Char.code (String.unsafe_get str 6) in + let c7 = Char.code (String.unsafe_get str 7) in + Int64.of_int c0 +^ + (Int64.shift_left (Int64.of_int c1) 8) +^ + (Int64.shift_left (Int64.of_int c2) 16) +^ + (Int64.shift_left (Int64.of_int c3) 24) +^ + (Int64.shift_left (Int64.of_int c4) 32) +^ + (Int64.shift_left (Int64.of_int c5) 40) +^ + (Int64.shift_left (Int64.of_int c6) 48) +^ + (Int64.shift_left (Int64.of_int c7) 56) + +let le64_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in + let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in + let c4 = Int64.shift_right (i &^ 0xff00000000L) 32 in + let c5 = Int64.shift_right (i &^ 0xff0000000000L) 40 in + let c6 = Int64.shift_right (i &^ 0xff000000000000L) 48 in + let c7 = Int64.shift_right (i &^ 0xff00000000000000L) 56 in + let b = Bytes.create 8 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c2)); + Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c3)); + Bytes.unsafe_set b 4 (Char.unsafe_chr (Int64.to_int c4)); + Bytes.unsafe_set b 5 (Char.unsafe_chr (Int64.to_int c5)); + Bytes.unsafe_set b 6 (Char.unsafe_chr (Int64.to_int c6)); + Bytes.unsafe_set b 7 (Char.unsafe_chr (Int64.to_int c7)); + Bytes.to_string b + +let int_of_be16 str + assert (String.length str = 2); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + Int64.of_int c1 +^ + (Int64.shift_left (Int64.of_int c0) 8) + +let be16_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let b = Bytes.create 2 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.to_string b + +let int_of_be32 str + assert (String.length str = 4); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + let c2 = Char.code (String.unsafe_get str 2) in + let c3 = Char.code (String.unsafe_get str 3) in + Int64.of_int c3 +^ + (Int64.shift_left (Int64.of_int c2) 8) +^ + (Int64.shift_left (Int64.of_int c1) 16) +^ + (Int64.shift_left (Int64.of_int c0) 24) + +let be32_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in + let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in + let b = Bytes.create 4 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c3)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c2)); + Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.to_string b + +let int_of_be64 str + assert (String.length str = 8); + let c0 = Char.code (String.unsafe_get str 0) in + let c1 = Char.code (String.unsafe_get str 1) in + let c2 = Char.code (String.unsafe_get str 2) in + let c3 = Char.code (String.unsafe_get str 3) in + let c4 = Char.code (String.unsafe_get str 4) in + let c5 = Char.code (String.unsafe_get str 5) in + let c6 = Char.code (String.unsafe_get str 6) in + let c7 = Char.code (String.unsafe_get str 7) in + Int64.of_int c7 +^ + (Int64.shift_left (Int64.of_int c6) 8) +^ + (Int64.shift_left (Int64.of_int c5) 16) +^ + (Int64.shift_left (Int64.of_int c4) 24) +^ + (Int64.shift_left (Int64.of_int c3) 32) +^ + (Int64.shift_left (Int64.of_int c2) 40) +^ + (Int64.shift_left (Int64.of_int c1) 48) +^ + (Int64.shift_left (Int64.of_int c0) 56) + +let be64_of_int i + let c0 = i &^ 0xffL in + let c1 = Int64.shift_right (i &^ 0xff00L) 8 in + let c2 = Int64.shift_right (i &^ 0xff0000L) 16 in + let c3 = Int64.shift_right (i &^ 0xff000000L) 24 in + let c4 = Int64.shift_right (i &^ 0xff00000000L) 32 in + let c5 = Int64.shift_right (i &^ 0xff0000000000L) 40 in + let c6 = Int64.shift_right (i &^ 0xff000000000000L) 48 in + let c7 = Int64.shift_right (i &^ 0xff00000000000000L) 56 in + let b = Bytes.create 8 in + Bytes.unsafe_set b 0 (Char.unsafe_chr (Int64.to_int c7)); + Bytes.unsafe_set b 1 (Char.unsafe_chr (Int64.to_int c6)); + Bytes.unsafe_set b 2 (Char.unsafe_chr (Int64.to_int c5)); + Bytes.unsafe_set b 3 (Char.unsafe_chr (Int64.to_int c4)); + Bytes.unsafe_set b 4 (Char.unsafe_chr (Int64.to_int c3)); + Bytes.unsafe_set b 5 (Char.unsafe_chr (Int64.to_int c2)); + Bytes.unsafe_set b 6 (Char.unsafe_chr (Int64.to_int c1)); + Bytes.unsafe_set b 7 (Char.unsafe_chr (Int64.to_int c0)); + Bytes.to_string b + type wrap_break_t = WrapEOS | WrapSpace | WrapNL let rec wrap ?(chan = stdout) ?(indent = 0) str diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli index 820673764..686d4193f 100644 --- a/common/mlstdutils/std_utils.mli +++ b/common/mlstdutils/std_utils.mli @@ -143,10 +143,29 @@ val roundup64 : int64 -> int64 -> int64 val div_roundup64 : int64 -> int64 -> int64 (** [div_roundup64 i a] returns [i] rounded up to the next multiple of [a], with the result divided by [a]. *) + +val int_of_le16 : string -> int64 +val le16_of_int : int64 -> string +val int_of_be16 : string -> int64 +val be16_of_int : int64 -> string val int_of_le32 : string -> int64 -(** Unpack a 4 byte string as a little endian 32 bit integer. *) val le32_of_int : int64 -> string -(** Pack a 32 bit integer a 4 byte string stored little endian. *) +val int_of_be32 : string -> int64 +val be32_of_int : int64 -> string +val int_of_le64 : string -> int64 +val le64_of_int : int64 -> string +val int_of_be64 : string -> int64 +val be64_of_int : int64 -> string +(** [int_of_X] functions unpack a string and return the equivalent integer. + + [X_of_int] functions pack an integer into a string. + + The value of [X] encodes whether the string is stored as + little endian [le] or big endian [be] and the size in bits + [16], [32] or [64]. + + On the OCaml side, 64 bit integers are always used so that you + can use the [.^] operators on them for bit manipulation. *) val wrap : ?chan:out_channel -> ?indent:int -> string -> unit (** Wrap text. *) diff --git a/common/mlstdutils/std_utils_tests.ml b/common/mlstdutils/std_utils_tests.ml index 1003f931c..6bc74fb63 100644 --- a/common/mlstdutils/std_utils_tests.ml +++ b/common/mlstdutils/std_utils_tests.ml @@ -33,10 +33,22 @@ let test_subdirectory ctx assert_equal_string "bar" (subdirectory "/foo" "/foo/bar"); assert_equal_string "bar/baz" (subdirectory "/foo" "/foo/bar/baz") -(* Test Common_utils.int_of_le32 and Common_utils.le32_of_int. *) -let test_le32 ctx - assert_equal_int64 0x20406080L (int_of_le32 "\x80\x60\x40\x20"); - assert_equal_string "\x80\x60\x40\x20" (le32_of_int 0x20406080L) +(* Test Std_utils.int_of_X and Std_utils.X_of_int byte swapping + * functions. + *) +let rec test_byteswap ctx + test_swap int_of_le16 le16_of_int 0x2040L "\x40\x20"; + test_swap int_of_le32 le32_of_int 0x20406080L "\x80\x60\x40\x20"; + test_swap int_of_le64 le64_of_int + 0x20406080A0C0E0F0L "\xF0\xE0\xC0\xA0\x80\x60\x40\x20"; + test_swap int_of_be16 be16_of_int 0x2040L "\x20\x40"; + test_swap int_of_be32 be32_of_int 0x20406080L "\x20\x40\x60\x80"; + test_swap int_of_be64 be64_of_int + 0x20406080A0C0E0F0L "\x20\x40\x60\x80\xA0\xC0\xE0\xF0" + +and test_swap int_of_x x_of_int i s + assert_equal_int64 i (int_of_x s); + assert_equal_string s (x_of_int i) (* Test Std_utils.String.is_prefix. *) let test_string_is_prefix ctx @@ -84,7 +96,7 @@ let suite "mllib Std_utils" >::: [ "subdirectory" >:: test_subdirectory; - "numeric.le32" >:: test_le32; + "numeric.byteswap" >:: test_byteswap; "strings.is_prefix" >:: test_string_is_prefix; "strings.is_suffix" >:: test_string_is_suffix; "strings.find" >:: test_string_find; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 13/41] common/mlstdutils: Implement ‘Char.mem’, ‘String.span’ and ‘String.cspan’.
Char.mem tells you if a byte is a member of a string. String.span and String.cspan are like the C functions strspn and strcspn. --- common/mlstdutils/std_utils.ml | 27 +++++++++++++++++++++++++++ common/mlstdutils/std_utils.mli | 12 ++++++++++++ common/mlstdutils/std_utils_tests.ml | 21 +++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml index f545c6f7a..a153ceb7f 100644 --- a/common/mlstdutils/std_utils.ml +++ b/common/mlstdutils/std_utils.ml @@ -74,6 +74,15 @@ module Char = struct | 'e' | 'E' -> 14 | 'f' | 'F' -> 15 | _ -> -1 + + let mem c str + let len = String.length str in + let rec loop i + if i >= len then false + else if String.unsafe_get str i = c then true + else loop (i+1) + in + loop 0 end module String = struct @@ -246,6 +255,24 @@ module String = struct List.map f (explode str) let spaces n = String.make n ' ' + + let span str accept + let len = String.length str in + let rec loop i + if i >= len then len + else if Char.mem (String.unsafe_get str i) accept then loop (i+1) + else i + in + loop 0 + + let cspan str reject + let len = String.length str in + let rec loop i + if i >= len then len + else if Char.mem (String.unsafe_get str i) reject then i + else loop (i+1) + in + loop 0 end let (//) = Filename.concat diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli index 686d4193f..b61b9bb02 100644 --- a/common/mlstdutils/std_utils.mli +++ b/common/mlstdutils/std_utils.mli @@ -41,6 +41,9 @@ module Char : sig val hexdigit : char -> int (** Return the value of a hex digit. If the char is not in the set [[0-9a-fA-F]] then this returns [-1]. *) + + val mem : char -> string -> bool + (** [mem c str] returns true if the byte [c] is contained in [str]. *) end (** Override the Char module from stdlib. *) @@ -109,6 +112,15 @@ module String : sig (** Explode string, then map function over the characters. *) val spaces : int -> string (** [spaces n] creates a string of n spaces. *) + val span : string -> string -> int + val cspan : string -> string -> int + (** [span str accept] returns the length in bytes of the initial + segment of [str] which contains only bytes in [accept]. + + [cspan str reject] returns the length in bytes of the initial + segment of [str] which contains only bytes {!i not} in [reject]. + + These work exactly like the C functions [strspn] and [strcspn]. *) end (** Override the String module from stdlib. *) diff --git a/common/mlstdutils/std_utils_tests.ml b/common/mlstdutils/std_utils_tests.ml index 6bc74fb63..2789766c6 100644 --- a/common/mlstdutils/std_utils_tests.ml +++ b/common/mlstdutils/std_utils_tests.ml @@ -50,6 +50,14 @@ and test_swap int_of_x x_of_int i s assert_equal_int64 i (int_of_x s); assert_equal_string s (x_of_int i) +(* Test Std_utils.Char.mem. *) +let test_char_mem ctx + assert_bool "Char.mem" (Char.mem 'a' "abc"); + assert_bool "Char.mem" (Char.mem 'b' "abc"); + assert_bool "Char.mem" (Char.mem 'c' "abc"); + assert_bool "Char.mem" (not (Char.mem 'd' "abc")); + assert_bool "Char.mem" (not (Char.mem 'a' "")) + (* Test Std_utils.String.is_prefix. *) let test_string_is_prefix ctx assert_bool "String.is_prefix,," (String.is_prefix "" ""); @@ -91,16 +99,29 @@ let test_string_lines_split ctx assert_equal_stringlist ["A\nB"; ""] (String.lines_split "A\\\nB\n"); assert_equal_stringlist ["A\nB\n"] (String.lines_split "A\\\nB\\\n") +(* Test Std_utils.String.span and cspan. *) +let test_string_span ctx + assert_equal_int 3 (String.span "aaabb" "a"); + assert_equal_int 3 (String.span "aaaba" "a"); + assert_equal_int 3 (String.span "aba" "ab"); + assert_equal_int 0 (String.span "" "ab"); + assert_equal_int 3 (String.cspan "defab" "ab"); + assert_equal_int 3 (String.cspan "defba" "ab"); + assert_equal_int 3 (String.cspan "def" "ab"); + assert_equal_int 0 (String.cspan "" "ab") + (* Suites declaration. *) let suite "mllib Std_utils" >::: [ "subdirectory" >:: test_subdirectory; "numeric.byteswap" >:: test_byteswap; + "char.mem" >:: test_char_mem; "strings.is_prefix" >:: test_string_is_prefix; "strings.is_suffix" >:: test_string_is_suffix; "strings.find" >:: test_string_find; "strings.lines_split" >:: test_string_lines_split; + "strings.span" >:: test_string_span; ] let () -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 14/41] daemon: Link guestfsd with libutils.
After the previous refactoring, we are able to link the daemon to common/utils, and also remove some of the "duplicate" functions that the daemon carried ("duplicate" in quotes because they were often not exact duplicates). It also allows us in future (but not in this commit) to move utility functions from the daemon into libutils. --- daemon/Makefile.am | 6 ++++- daemon/augeas.c | 2 +- daemon/btrfs.c | 18 +++++++-------- daemon/cleanups.c | 6 ----- daemon/daemon.h | 14 +----------- daemon/debug.c | 4 ++-- daemon/echo-daemon.c | 2 +- daemon/guestfsd.c | 64 ---------------------------------------------------- daemon/ldm.c | 2 +- daemon/lvm.c | 4 ++-- daemon/md.c | 8 ++++--- daemon/stat.c | 2 +- generator/daemon.ml | 8 +++---- 13 files changed, 32 insertions(+), 108 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 9695500bf..84d9d279e 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -50,6 +50,7 @@ guestfsd_SOURCES = \ ../common/errnostring/errnostring.h \ ../common/protocol/guestfs_protocol.h \ ../common/cleanups/cleanups.h \ + ../common/utils/utils.h \ 9p.c \ acl.c \ actions.h \ @@ -179,6 +180,7 @@ guestfsd_LDADD = \ ../common/errnostring/liberrnostring.la \ ../common/protocol/libprotocol.la \ ../common/cleanups/libcleanups.la \ + ../common/utils/libutils.la \ $(ACL_LIBS) \ $(CAP_LIBS) \ $(YAJL_LIBS) \ @@ -209,7 +211,9 @@ guestfsd_CPPFLAGS = \ -I$(top_srcdir)/common/protocol \ -I$(top_builddir)/common/protocol \ -I$(top_srcdir)/common/cleanups \ - -I$(top_builddir)/common/cleanups + -I$(top_builddir)/common/cleanups \ + -I$(top_srcdir)/common/utils \ + -I$(top_builddir)/common/utils guestfsd_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(RPC_CFLAGS) \ diff --git a/daemon/augeas.c b/daemon/augeas.c index 5adc959a5..bd54c4849 100644 --- a/daemon/augeas.c +++ b/daemon/augeas.c @@ -436,7 +436,7 @@ do_aug_ls (const char *path) if (matches == NULL) return NULL; /* do_aug_match has already sent the error */ - sort_strings (matches, count_strings ((void *) matches)); + sort_strings (matches, guestfs_int_count_strings ((void *) matches)); return matches; /* Caller frees. */ } diff --git a/daemon/btrfs.c b/daemon/btrfs.c index ae2310b53..5f1e5d1d0 100644 --- a/daemon/btrfs.c +++ b/daemon/btrfs.c @@ -152,7 +152,7 @@ do_mkfs_btrfs (char *const *devices, int leafsize, const char *label, const char *metadata, int nodesize, int sectorsize) { - const size_t nr_devices = count_strings (devices); + const size_t nr_devices = guestfs_int_count_strings (devices); const size_t MAX_ARGS = nr_devices + 64; const char *argv[MAX_ARGS]; size_t i = 0, j; @@ -500,7 +500,7 @@ do_btrfs_subvolume_list (const mountable_t *fs) guestfs_int_btrfssubvolume_list *ret = NULL; - const size_t nr_subvolumes = count_strings (lines); + const size_t nr_subvolumes = guestfs_int_count_strings (lines); ret = malloc (sizeof *ret); if (!ret) { @@ -733,7 +733,7 @@ int do_btrfs_device_add (char *const *devices, const char *fs) { static int btrfs_device_add_needs_force = -1; - const size_t nr_devices = count_strings (devices); + const size_t nr_devices = guestfs_int_count_strings (devices); const size_t MAX_ARGS = nr_devices + 8; const char *argv[MAX_ARGS]; size_t i = 0, j; @@ -781,7 +781,7 @@ do_btrfs_device_add (char *const *devices, const char *fs) int do_btrfs_device_delete (char *const *devices, const char *fs) { - const size_t nr_devices = count_strings (devices); + const size_t nr_devices = guestfs_int_count_strings (devices); if (nr_devices == 0) return 0; @@ -1391,7 +1391,7 @@ do_btrfs_qgroup_show (const char *path) * 0/5 9249849344 9249849344 * */ - const size_t nr_qgroups = count_strings (lines) - 2; + const size_t nr_qgroups = guestfs_int_count_strings (lines) - 2; guestfs_int_btrfsqgroup_list *ret = NULL; ret = malloc (sizeof *ret); if (!ret) { @@ -1821,7 +1821,7 @@ do_btrfs_balance_status (const char *path) if (!lines) return NULL; - nlines = count_strings (lines); + nlines = guestfs_int_count_strings (lines); ret = calloc (1, sizeof *ret); if (ret == NULL) { @@ -1938,7 +1938,7 @@ do_btrfs_scrub_status (const char *path) if (!lines) return NULL; - if (count_strings (lines) < 2) { + if (guestfs_int_count_strings (lines) < 2) { reply_with_error ("truncated output from 'btrfs scrub status -R' command"); return NULL; } @@ -2124,7 +2124,7 @@ int do_btrfs_image (char *const *sources, const char *image, int compresslevel) { - const size_t nr_sources = count_strings (sources); + const size_t nr_sources = guestfs_int_count_strings (sources); const size_t MAX_ARGS = 64 + nr_sources; const char *argv[MAX_ARGS]; size_t i = 0, j; @@ -2229,7 +2229,7 @@ do_btrfs_filesystem_show (const char *device) if (!lines) return NULL; - if (count_strings (lines) < 3) { + if (guestfs_int_count_strings (lines) < 3) { reply_with_error ("truncated output from 'btrfs filesystem show' command"); return NULL; } diff --git a/daemon/cleanups.c b/daemon/cleanups.c index c73b9e492..b4767178a 100644 --- a/daemon/cleanups.c +++ b/daemon/cleanups.c @@ -36,12 +36,6 @@ cleanup_aug_close (void *ptr) } void -cleanup_free_string_list (void *ptr) -{ - free_strings (* (char ***) ptr); -} - -void cleanup_free_stringsbuf (void *ptr) { free_stringsbuf ((struct stringsbuf *) ptr); diff --git a/daemon/daemon.h b/daemon/daemon.h index 746af22b9..2d296a6c2 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -31,6 +31,7 @@ #include "guestfs_protocol.h" #include "cleanups.h" +#include "utils.h" #include "guestfs-internal-all.h" @@ -83,17 +84,13 @@ extern char *mountable_to_string (const mountable_t *mountable); * Don't call them directly. */ extern void cleanup_aug_close (void *ptr); -extern void cleanup_free_string_list (void *ptr); extern void cleanup_free_stringsbuf (void *ptr); #ifdef HAVE_ATTRIBUTE_CLEANUP #define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close))) -#define CLEANUP_FREE_STRING_LIST \ - __attribute__((cleanup(cleanup_free_string_list))) #define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf))) #else #define CLEANUP_AUG_CLOSE -#define CLEANUP_FREE_STRING_LIST #define CLEANUP_FREE_STRINGSBUF #endif @@ -130,21 +127,12 @@ extern int end_stringsbuf (struct stringsbuf *sb); extern char **take_stringsbuf (struct stringsbuf *sb); extern void free_stringsbuf (struct stringsbuf *sb); -extern size_t count_strings (char *const *argv); extern void sort_strings (char **argv, size_t len); -extern void free_strings (char **argv); extern void free_stringslen (char **argv, size_t len); extern void sort_device_names (char **argv, size_t len); extern int compare_device_names (const char *a, const char *b); -/* Concatenate strings, optionally with a separator string between - * each. On error, these return NULL but do NOT call reply_with_* nor - * free anything. - */ -extern char *concat_strings (char *const *argv); -extern char *join_strings (const char *separator, char *const *argv); - extern struct stringsbuf split_lines_sb (char *str); extern char **split_lines (char *str); diff --git a/daemon/debug.c b/daemon/debug.c index b18d87c26..e2d43a7ca 100644 --- a/daemon/debug.c +++ b/daemon/debug.c @@ -444,7 +444,7 @@ debug_ldd (const char *subcmd, size_t argc, char *const *const argv) static char * debug_ls (const char *subcmd, size_t argc, char *const *const argv) { - const size_t len = count_strings (argv); + const size_t len = guestfs_int_count_strings (argv); CLEANUP_FREE const char **cargv = NULL; size_t i; int r; @@ -477,7 +477,7 @@ debug_ls (const char *subcmd, size_t argc, char *const *const argv) static char * debug_ll (const char *subcmd, size_t argc, char *const *const argv) { - const size_t len = count_strings (argv); + const size_t len = guestfs_int_count_strings (argv); CLEANUP_FREE const char **cargv = NULL; size_t i; int r; diff --git a/daemon/echo-daemon.c b/daemon/echo-daemon.c index d566a9bb2..15429f072 100644 --- a/daemon/echo-daemon.c +++ b/daemon/echo-daemon.c @@ -28,7 +28,7 @@ do_echo_daemon (char *const *argv) { char *out; - out = join_strings (" ", argv); + out = guestfs_int_join_strings (" ", argv); if (out == NULL) { reply_with_perror ("malloc"); return NULL; diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index db2bb702f..b3f40628b 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -583,16 +583,6 @@ take_stringsbuf (struct stringsbuf *sb) return ret; } -size_t -count_strings (char *const *argv) -{ - size_t argc; - - for (argc = 0; argv[argc] != NULL; ++argc) - ; - return argc; -} - /** * Returns true if C<v> is a power of 2. * @@ -620,19 +610,6 @@ sort_strings (char **argv, size_t len) } void -free_strings (char **argv) -{ - size_t argc; - - if (!argv) - return; - - for (argc = 0; argv[argc] != NULL; ++argc) - free (argv[argc]); - free (argv); -} - -void free_stringslen (char **argv, size_t len) { size_t i; @@ -720,47 +697,6 @@ sort_device_names (char **argv, size_t len) qsort (argv, len, sizeof (char *), compare_device_names_vp); } -char * -concat_strings (char *const *argv) -{ - return join_strings ("", argv); -} - -char * -join_strings (const char *separator, char *const *argv) -{ - size_t i, len, seplen, rlen; - char *r; - - seplen = strlen (separator); - - len = 0; - for (i = 0; argv[i] != NULL; ++i) { - if (i > 0) - len += seplen; - len += strlen (argv[i]); - } - len++; /* for final \0 */ - - r = malloc (len); - if (r == NULL) - return NULL; - - rlen = 0; - for (i = 0; argv[i] != NULL; ++i) { - if (i > 0) { - memcpy (&r[rlen], separator, seplen); - rlen += seplen; - } - len = strlen (argv[i]); - memcpy (&r[rlen], argv[i], len); - rlen += len; - } - r[rlen] = '\0'; - - return r; -} - /** * Split an output string into a NULL-terminated list of lines, * wrapped into a stringsbuf. diff --git a/daemon/ldm.c b/daemon/ldm.c index 7753b0d82..75418e8d3 100644 --- a/daemon/ldm.c +++ b/daemon/ldm.c @@ -320,7 +320,7 @@ do_ldmtool_scan_devices (char * const * devices) int r; CLEANUP_FREE char *out = NULL, *err = NULL; - nr_devices = count_strings (devices); + nr_devices = guestfs_int_count_strings (devices); argv = malloc ((3 + nr_devices) * sizeof (char *)); if (argv == NULL) { reply_with_perror ("malloc"); diff --git a/daemon/lvm.c b/daemon/lvm.c index 6c57046ff..5d12b009f 100644 --- a/daemon/lvm.c +++ b/daemon/lvm.c @@ -337,7 +337,7 @@ do_vgcreate (const char *volgroup, char *const *physvols) CLEANUP_FREE char *err = NULL; CLEANUP_FREE const char **argv = NULL; - argc = count_strings (physvols) + 3; + argc = guestfs_int_count_strings (physvols) + 3; argv = malloc (sizeof (char *) * (argc + 1)); if (argv == NULL) { reply_with_perror ("malloc"); @@ -643,7 +643,7 @@ do_vg_activate (int activate, char *const *volgroups) CLEANUP_FREE char *err = NULL; CLEANUP_FREE const char **argv = NULL; - argc = count_strings (volgroups) + 4; + argc = guestfs_int_count_strings (volgroups) + 4; argv = malloc (sizeof (char *) * (argc+1)); if (argv == NULL) { reply_with_perror ("malloc"); diff --git a/daemon/md.c b/daemon/md.c index 3f31529e2..64d98fae5 100644 --- a/daemon/md.c +++ b/daemon/md.c @@ -97,7 +97,8 @@ do_md_create (const char *name, char *const *devices, } } else - nrdevices = count_strings (devices) + count_bits (umissingbitmap); + nrdevices + guestfs_int_count_strings (devices) + count_bits (umissingbitmap); if (optargs_bitmask & GUESTFS_MD_CREATE_LEVEL_BITMASK) { if (STRNEQ (level, "linear") && STRNEQ (level, "raid0") && @@ -124,10 +125,11 @@ do_md_create (const char *name, char *const *devices, } /* Check invariant. */ - if (count_strings (devices) + count_bits (umissingbitmap) !+ if (guestfs_int_count_strings (devices) + count_bits (umissingbitmap) ! (size_t) (nrdevices + spare)) { reply_with_error ("devices (%zu) + bits set in missingbitmap (%zu) is not equal to nrdevices (%d) + spare (%d)", - count_strings (devices), count_bits (umissingbitmap), + guestfs_int_count_strings (devices), + count_bits (umissingbitmap), nrdevices, spare); return -1; } diff --git a/daemon/stat.c b/daemon/stat.c index 73f19226b..a1cd49245 100644 --- a/daemon/stat.c +++ b/daemon/stat.c @@ -127,7 +127,7 @@ do_internal_lstatnslist (const char *path, char *const *names) guestfs_int_statns_list *ret; size_t i, nr_names; - nr_names = count_strings (names); + nr_names = guestfs_int_count_strings (names); ret = malloc (sizeof *ret); if (!ret) { diff --git a/generator/daemon.ml b/generator/daemon.ml index 0300dc54b..2ae462864 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -397,7 +397,7 @@ let generate_daemon_stubs actions () | RStringList (RPlainString, n) | RHashtable (RPlainString, RPlainString, n) -> pr " struct guestfs_%s_ret ret;\n" name; - pr " ret.%s.%s_len = count_strings (r);\n" n n; + pr " ret.%s.%s_len = guestfs_int_count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name @@ -413,7 +413,7 @@ let generate_daemon_stubs actions () pr " free (r[i]);\n"; pr " r[i] = rr;\n"; pr " }\n"; - pr " ret.%s.%s_len = count_strings (r);\n" n n; + pr " ret.%s.%s_len = guestfs_int_count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name @@ -428,7 +428,7 @@ let generate_daemon_stubs actions () pr " free (r[i]);\n"; pr " r[i] = rr;\n"; pr " }\n"; - pr " ret.%s.%s_len = count_strings (r);\n" n n; + pr " ret.%s.%s_len = guestfs_int_count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name @@ -443,7 +443,7 @@ let generate_daemon_stubs actions () pr " free (r[i+1]);\n"; pr " r[i+1] = rr;\n"; pr " }\n"; - pr " ret.%s.%s_len = count_strings (r);\n" n n; + pr " ret.%s.%s_len = guestfs_int_count_strings (r);\n" n n; pr " ret.%s.%s_val = r;\n" n n; pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n" name -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 15/41] daemon: Allow parts of the daemon and APIs to be written in OCaml.
This change allows parts of the daemon to be written in the OCaml programming language. I am using the ‘Main Program in C’ method along with ‘-output-obj’ to create an object file from the OCaml code / runtime, as described here: https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html Furthermore, change the generator to allow individual APIs to be implemented in OCaml. This is picked by setting: impl = OCaml <ocaml_function>; The generator creates ‘do_function’ (the same one you would have to write by hand in C), with the function calling the named ‘ocaml_function’ and dealing with marshalling/unmarshalling the OCaml parameters. Note that the OCaml compiler (either ocamlc or ocamlopt) is now required even for building from tarballs. --- .gitignore | 6 +- Makefile.am | 2 +- daemon/Makefile.am | 103 +++++++++++++++++++++++-- daemon/chroot.ml | 85 +++++++++++++++++++++ daemon/chroot.mli | 35 +++++++++ daemon/daemon-c.c | 35 +++++++++ daemon/daemon.ml | 39 ++++++++++ daemon/guestfsd.c | 45 +++++++++++ daemon/sysroot-c.c | 37 +++++++++ daemon/sysroot.ml | 19 +++++ daemon/sysroot.mli | 22 ++++++ daemon/utils.ml | 156 ++++++++++++++++++++++++++++++++++++++ daemon/utils.mli | 65 ++++++++++++++++ docs/guestfs-building.pod | 10 ++- docs/guestfs-hacking.pod | 7 ++ generator/actions.ml | 5 ++ generator/actions.mli | 4 + generator/daemon.ml | 187 ++++++++++++++++++++++++++++++++++++++++++++++ generator/daemon.mli | 3 + generator/main.ml | 6 ++ generator/types.ml | 7 +- 21 files changed, 866 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index ec332bc32..50f322974 100644 --- a/.gitignore +++ b/.gitignore @@ -165,22 +165,26 @@ Makefile.in /customize/test-settings-*.sh /customize/virt-customize /customize/virt-customize.1 +/daemon/.depend /daemon/actions.h +/daemon/callbacks.ml +/daemon/caml-stubs.c /daemon/dispatch.c /daemon/guestfsd /daemon/guestfsd.8 /daemon/guestfsd.exe /daemon/install-sh +/daemon/lvm-tokenization.c /daemon/missing /daemon/names.c /daemon/optgroups.c /daemon/optgroups.h -/daemon/lvm-tokenization.c /daemon/stamp-guestfsd.pod /daemon/structs-cleanups.c /daemon/structs-cleanups.h /daemon/stubs-?.c /daemon/stubs.h +/daemon/types.ml /depcomp /df/stamp-virt-df.pod /df/virt-df diff --git a/Makefile.am b/Makefile.am index 509bcae83..7b70c6545 100644 --- a/Makefile.am +++ b/Makefile.am @@ -45,6 +45,7 @@ SUBDIRS += common/structs SUBDIRS += lib docs examples po # The daemon and the appliance. +SUBDIRS += common/mlutils if ENABLE_DAEMON SUBDIRS += daemon SUBDIRS += tests/daemon @@ -156,7 +157,6 @@ SUBDIRS += csharp # OCaml tools. Note 'common/ml*', 'mllib' and 'customize' contain # shared code used by other OCaml tools, so these must come first. if HAVE_OCAML -SUBDIRS += common/mlutils SUBDIRS += common/mlprogress SUBDIRS += common/mlvisit SUBDIRS += common/mlxml diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 84d9d279e..3fb70fe52 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -19,6 +19,7 @@ include $(top_srcdir)/subdir-rules.mk generator_built = \ actions.h \ + caml-stubs.c \ dispatch.c \ names.c \ lvm-tokenization.c \ @@ -31,13 +32,30 @@ generator_built = \ stubs-4.c \ stubs-5.c \ stubs-6.c \ - stubs.h + stubs.h \ + callbacks.ml \ + types.ml BUILT_SOURCES = \ - $(generator_built) + actions.h \ + caml-stubs.c \ + dispatch.c \ + names.c \ + lvm-tokenization.c \ + structs-cleanups.c \ + structs-cleanups.h \ + stubs-0.c \ + stubs-1.c \ + stubs-2.c \ + stubs-3.c \ + stubs-4.c \ + stubs-5.c \ + stubs-6.c \ + stubs.h EXTRA_DIST = \ - $(BUILT_SOURCES) \ + $(generator_built) \ + $(SOURCES_MLI) $(SOURCES_ML) \ guestfsd.pod if INSTALL_DAEMON @@ -61,6 +79,7 @@ guestfsd_SOURCES = \ blkid.c \ blockdev.c \ btrfs.c \ + caml-stubs.c \ cap.c \ checksum.c \ cleanups.c \ @@ -71,6 +90,7 @@ guestfsd_SOURCES = \ copy.c \ cpio.c \ cpmv.c \ + daemon-c.c \ daemon.h \ dd.c \ debug.c \ @@ -161,6 +181,7 @@ guestfsd_SOURCES = \ swap.c \ sync.c \ syslinux.c \ + sysroot-c.c \ tar.c \ tsk.c \ truncate.c \ @@ -176,11 +197,17 @@ guestfsd_SOURCES = \ zero.c \ zerofree.c +guestfsd_LDFLAGS = \ + -L$(shell $(OCAMLC) -where) \ + -L$(shell $(OCAMLC) -where)/hivex \ + -L../common/mlutils \ + -L../common/mlstdutils guestfsd_LDADD = \ ../common/errnostring/liberrnostring.la \ ../common/protocol/libprotocol.la \ ../common/cleanups/libcleanups.la \ ../common/utils/libutils.la \ + camldaemon.o \ $(ACL_LIBS) \ $(CAP_LIBS) \ $(YAJL_LIBS) \ @@ -199,9 +226,12 @@ guestfsd_LDADD = \ $(PCRE_LIBS) \ $(TSK_LIBS) \ $(RPC_LIBS) \ - $(YARA_LIBS) + $(YARA_LIBS) \ + $(OCAML_LIBS) guestfsd_CPPFLAGS = \ + -I$(shell $(OCAMLC) -where) \ + -I$(shell $(OCAMLC) -where)/hivex \ -I$(top_srcdir)/gnulib/lib \ -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/lib \ @@ -223,6 +253,69 @@ guestfsd_CFLAGS = \ $(YAJL_CFLAGS) \ $(PCRE_CFLAGS) +# Parts of the daemon are written in OCaml. These are linked into a +# library and then linked to the daemon. See +# https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html +SOURCES_MLI = \ + chroot.mli \ + sysroot.mli \ + utils.mli + +SOURCES_ML = \ + types.ml \ + utils.ml \ + sysroot.ml \ + chroot.ml \ + callbacks.ml \ + daemon.ml + +BOBJECTS = $(SOURCES_ML:.ml=.cmo) +XOBJECTS = $(BOBJECTS:.cmo=.cmx) + +OCAMLPACKAGES = \ + -package str,unix,hivex \ + -I $(top_srcdir)/common/mlstdutils \ + -I $(top_srcdir)/common/mlutils + +OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_FLAGS) + +if !HAVE_OCAMLOPT +OBJECTS = $(BOBJECTS) +CAMLRUN = camlrun +else +OBJECTS = $(XOBJECTS) +CAMLRUN = asmrun +endif +OCAML_LIBS = \ + -lmlcutils \ + -lmlstdutils \ + -lmlhivex \ + -lcamlstr \ + -lunix \ + -l$(CAMLRUN) -ldl -lm + +CLEANFILES += camldaemon.o + +camldaemon.o: $(OBJECTS) + $(OCAMLFIND) $(BEST) -output-obj -o $@ \ + $(OCAMLFLAGS) $(OCAMLPACKAGES) \ + -linkpkg mlcutils.$(MLARCHIVE) mlstdutils.$(MLARCHIVE) \ + $(OBJECTS) + +# OCaml dependencies. +depend: .depend + +.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml) + rm -f $@ $@-t + $(OCAMLFIND) ocamldep -I $(abs_srcdir) -I $(abs_top_builddir)/common/mlstdutils -I $(abs_top_builddir)/common/mlutils $^ | \ + $(SED) 's/ *$$//' | \ + $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \ + $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \ + sort > $@-t + mv $@-t $@ + +-include .depend + # Manual pages and HTML files for the website. if INSTALL_DAEMON man_MANS = guestfsd.8 @@ -244,4 +337,4 @@ stamp-guestfsd.pod: guestfsd.pod $< touch $@ -.PHONY: force +.PHONY: depend force diff --git a/daemon/chroot.ml b/daemon/chroot.ml new file mode 100644 index 000000000..40dfa1dde --- /dev/null +++ b/daemon/chroot.ml @@ -0,0 +1,85 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf +open Unix + +open Std_utils +open Unix_utils + +type t = { + name : string; + chroot : string; +} + +let create ?(name = prog) chroot + { name = name; chroot = chroot } + +let f t func arg + if verbose () then + eprintf "chroot: %s: running ‘%s’\n%!" t.chroot t.name; + + let rfd, wfd = pipe () in + + let pid = fork () in + if pid = 0 then ( + (* Child. *) + close rfd; + + chdir t.chroot; + chroot t.chroot; + + let ret + try Either (func arg) + with exn -> Or exn in + + try + let chan = out_channel_of_descr wfd in + output_value chan ret; + Pervasives.flush chan; + Exit._exit 0 + with + exn -> + prerr_endline (Printexc.to_string exn); + Exit._exit 1 + ); + + (* Parent. *) + close wfd; + + let _, status = waitpid [] pid in + (match status with + | WEXITED 0 -> () + | WEXITED i -> + close rfd; + failwithf "chroot ‘%s’ exited with non-zero error %d" t.name i + | WSIGNALED i -> + close rfd; + failwithf "chroot ‘%s’ killed by signal %d" t.name i + | WSTOPPED i -> + close rfd; + failwithf "chroot ‘%s’ stopped by signal %d" t.name i + ); + + let chan = in_channel_of_descr rfd in + let ret = input_value chan in + close_in chan; + + match ret with + | Either ret -> ret + | Or exn -> raise exn diff --git a/daemon/chroot.mli b/daemon/chroot.mli new file mode 100644 index 000000000..eda3a785f --- /dev/null +++ b/daemon/chroot.mli @@ -0,0 +1,35 @@ +(* guestfs-inspection + * Copyright (C) 2009-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. + *) + +(** This is a generic module for running functions in a chroot. + The function runs in a forked subprocess too so that we can + restore the root afterwards. + + It handles passing the parmeter, forking, running the + function and marshalling the result or any exceptions. *) + +type t + +val create : ?name:string -> string -> t +(** Create a chroot handle. [?name] is an optional name used in + debugging and error messages. The string is the chroot + directory. *) + +val f : t -> ('a -> 'b) -> 'a -> 'b +(** Run a function in the chroot, returning the result or re-raising + any exception thrown. *) diff --git a/daemon/daemon-c.c b/daemon/daemon-c.c new file mode 100644 index 000000000..da382bc35 --- /dev/null +++ b/daemon/daemon-c.c @@ -0,0 +1,35 @@ +/* guestfs-inspection + * 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. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <caml/mlvalues.h> + +#include "daemon.h" + +extern value guestfs_int_daemon_get_verbose_flag (value unitv); + +/* NB: This is a "noalloc" call. */ +value +guestfs_int_daemon_get_verbose_flag (value unitv) +{ + return Val_bool (verbose); +} diff --git a/daemon/daemon.ml b/daemon/daemon.ml new file mode 100644 index 000000000..45bac029a --- /dev/null +++ b/daemon/daemon.ml @@ -0,0 +1,39 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +external get_verbose_flag : unit -> bool + "guestfs_int_daemon_get_verbose_flag" "noalloc" + +(* When guestfsd starts up, after initialization but before accepting + * messages, it calls 'caml_startup' which runs all initialization code + * in the OCaml modules, including this one. Therefore this is where + * we can place OCaml initialization code for the daemon. + *) +let () + (* Connect the guestfsd [-v] (verbose) flag into 'verbose ()' + * used in OCaml code to print debugging messages. + *) + if get_verbose_flag () then ( + Std_utils.set_verbose (); + eprintf "OCaml daemon loaded\n%!" + ); + + (* Register the callbacks which are used to call OCaml code from C. *) + Callbacks.init_callbacks () diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index b3f40628b..05337b31c 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -56,6 +56,10 @@ #include <augeas.h> +#include <caml/callback.h> +#include <caml/mlvalues.h> +#include <caml/unixsupport.h> + #include "sockets.h" #include "c-ctype.h" #include "ignore-value.h" @@ -348,6 +352,9 @@ main (int argc, char *argv[]) */ udev_settle (); + /* Initialize the OCaml stubs. */ + caml_startup (argv); + /* Send the magic length message which indicates that * userspace is up inside the guest. */ @@ -1205,3 +1212,41 @@ cleanup_free_mountable (mountable_t *mountable) free (mountable->volume); } } + +/* Convert an OCaml exception to a reply_with_error_errno call + * as best we can. + */ +extern void ocaml_exn_to_reply_with_error (const char *func, value exn); + +void +ocaml_exn_to_reply_with_error (const char *func, value exn) +{ + const char *exn_name; + + /* This is not the official way to do this, but I could not get the + * official way to work, and this way does work. See + * http://caml.inria.fr/pub/ml-archives/caml-list/2006/05/097f63cfb39a80418f95c70c3c520aa8.en.html + * http://caml.inria.fr/pub/ml-archives/caml-list/2009/06/797e2f797f57b8ea2a2c0e431a2df312.en.html + */ + exn_name = String_val (Field (Field (exn, 0), 0)); + if (verbose) + fprintf (stderr, "ocaml_exn: '%s' raised '%s' exception\n", + func, exn_name); + + if (STREQ (exn_name, "Unix.Unix_error")) { + int errcode = code_of_unix_error (Field (exn, 1)); + reply_with_perror_errno (errcode, "%s: %s", + String_val (Field (exn, 2)), + String_val (Field (exn, 3))); + } + else if (STREQ (exn_name, "Failure")) + reply_with_error ("%s", String_val (Field (exn, 1))); + else if (STREQ (exn_name, "Sys_error")) + reply_with_error ("%s", String_val (Field (exn, 1))); + else if (STREQ (exn_name, "Invalid_argument")) + reply_with_error ("invalid argument: %s", + String_val (Field (exn, 1))); + else + reply_with_error ("internal error: %s: unknown exception thrown: %s", + func, exn_name); +} diff --git a/daemon/sysroot-c.c b/daemon/sysroot-c.c new file mode 100644 index 000000000..ad31d36ee --- /dev/null +++ b/daemon/sysroot-c.c @@ -0,0 +1,37 @@ +/* guestfs-inspection + * 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. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <caml/alloc.h> +#include <caml/fail.h> +#include <caml/memory.h> +#include <caml/mlvalues.h> + +#include "daemon.h" + +extern value guestfs_int_daemon_sysroot (value unitv); + +value +guestfs_int_daemon_sysroot (value unitv) +{ + return caml_copy_string (sysroot); +} diff --git a/daemon/sysroot.ml b/daemon/sysroot.ml new file mode 100644 index 000000000..ecf0d7362 --- /dev/null +++ b/daemon/sysroot.ml @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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. + *) + +external sysroot : unit -> string = "guestfs_int_daemon_sysroot" diff --git a/daemon/sysroot.mli b/daemon/sysroot.mli new file mode 100644 index 000000000..88f976476 --- /dev/null +++ b/daemon/sysroot.mli @@ -0,0 +1,22 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 sysroot : unit -> string +(** Return the current sysroot path where filesystems are mounted. + This comes from the daemon command line ([-r] option) or a built + in default. *) diff --git a/daemon/utils.ml b/daemon/utils.ml new file mode 100644 index 000000000..7630a5534 --- /dev/null +++ b/daemon/utils.ml @@ -0,0 +1,156 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Unix +open Printf + +open Std_utils + +let prog_exists prog + try ignore (which prog); true + with Executable_not_found _ -> false + +let commandr prog args + if verbose () then + eprintf "command: %s %s\n%!" + prog (String.concat " " args); + + let argv = Array.of_list (prog :: args) in + + let stdout_file, stdout_chan = Filename.open_temp_file "cmd" ".out" in + let stderr_file, stderr_chan = Filename.open_temp_file "cmd" ".err" in + let stdout_fd = descr_of_out_channel stdout_chan in + let stderr_fd = descr_of_out_channel stderr_chan in + let stdin_fd = openfile "/dev/null" [O_RDONLY] 0 in + + let pid = fork () in + if pid = 0 then ( + (* Child process. *) + dup2 stdin_fd stdin; + close stdin_fd; + dup2 stdout_fd stdout; + close stdout_fd; + dup2 stderr_fd stderr; + close stderr_fd; + + execvp prog argv + ); + + (* Parent process. *) + close stdin_fd; + close stdout_fd; + close stderr_fd; + let _, status = waitpid [] pid in + let r + match status with + | WEXITED i -> i + | WSIGNALED i -> + failwithf "external command ‘%s’ killed by signal %d" prog i + | WSTOPPED i -> + failwithf "external command ‘%s’ stopped by signal %d" prog i in + + if verbose () then + eprintf "command: %s returned %d\n" prog r; + + let stdout = read_whole_file stdout_file in + let stderr = read_whole_file stderr_file in + + if verbose () then ( + if stdout <> "" then ( + eprintf "command: %s: stdout:\n%s%!" prog stdout; + if not (String.is_suffix stdout "\n") then eprintf "\n%!" + ); + if stderr <> "" then ( + eprintf "command: %s: stderr:\n%s%!" prog stderr; + if not (String.is_suffix stderr "\n") then eprintf "\n%!" + ) + ); + + (* Strip trailing \n from stderr but NOT from stdout. *) + let stderr + let n = String.length stderr in + if n > 0 && stderr.[n-1] = '\n' then + String.sub stderr 0 (n-1) + else + stderr in + + (r, stdout, stderr) + +let command prog args + let r, stdout, stderr = commandr prog args in + if r <> 0 then + failwithf "%s exited with status %d: %s" prog r stderr; + stdout + +let udev_settle ?filename () + let args = ref [] in + if verbose () then + push_back args "--debug"; + push_back args "settle"; + (match filename with + | None -> () + | Some filename -> + push_back args "-E"; + push_back args filename + ); + let args = !args in + let r, _, err = commandr "udevadm" args in + if r <> 0 then + eprintf "udevadm settle: %s\n" err + +let root_device = lazy ((stat "/").st_dev) + +let is_root_device_stat statbuf + statbuf.st_rdev = Lazy.force root_device + +let is_root_device device + udev_settle ~filename:device (); + try + let statbuf = stat device in + is_root_device_stat statbuf + with + Unix_error (err, func, arg) -> + eprintf "is_root_device: %s: %s: %s: %s\n" + device func arg (error_message err); + false + +let proc_unmangle_path path + let n = String.length path in + let b = Buffer.create n in + let rec loop i + if i < n-3 && path.[i] = '\\' then ( + let to_int c = Char.code c - Char.code '0' in + let v + (to_int path.[i+1] lsl 6) lor + (to_int path.[i+2] lsl 3) lor + to_int path.[i+3] in + Buffer.add_char b (Char.chr v); + loop (i+4) + ) + else if i < n then ( + Buffer.add_char b path.[i]; + loop (i+1) + ) + else + Buffer.contents b + in + loop 0 + +let is_small_file path + is_regular_file path && + (stat path).st_size <= 2 * 1048 * 1024 diff --git a/daemon/utils.mli b/daemon/utils.mli new file mode 100644 index 000000000..57f703c6c --- /dev/null +++ b/daemon/utils.mli @@ -0,0 +1,65 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 prog_exists : string -> bool +(** Return true iff the program is found on [$PATH]. *) + +val udev_settle : ?filename:string -> unit -> unit +(** + * LVM and other commands aren't synchronous, especially when udev is + * involved. eg. You can create or remove some device, but the + * [/dev] device node won't appear until some time later. This means + * that you get an error if you run one command followed by another. + * + * Use [udevadm settle] after certain commands, but don't be too + * fussed if it fails. + * + * The optional [?filename] passes the [udevadm settle -E filename] + * option, which means udevadm stops waiting as soon as the named + * file is created (or if it exists at the start). + *) + +val is_root_device : string -> bool +(** Return true if this is the root (appliance) device. *) + +val is_root_device_stat : Unix.stats -> bool +(** As for {!is_root_device} but operates on a statbuf instead of + a device name. *) + +val proc_unmangle_path : string -> string +(** Reverse kernel path escaping done in fs/seq_file.c:mangle_path. + This is inconsistently used for /proc fields. *) + +val command : string -> string list -> string +(** Run an external command without using the shell, and collect + stdout and stderr separately. Returns stdout if the command + runs successfully. + + On failure of the command, this throws an exception containing + the stderr from the command. *) + +val commandr : string -> string list -> (int * string * string) +(** Run an external command without using the shell, and collect + stdout and stderr separately. + + Returns [status, stdout, stderr]. As with the C function in + [daemon/command.c], this strips the trailing [\n] from stderr, + but {b not} from stdout. *) + +val is_small_file : string -> bool +(** Return true if the path is a small regular file. *) diff --git a/docs/guestfs-building.pod b/docs/guestfs-building.pod index 0f9ed2893..4e1ff7df4 100644 --- a/docs/guestfs-building.pod +++ b/docs/guestfs-building.pod @@ -120,8 +120,7 @@ I<Required>. Part of Perl core. =item OCaml findlib -I<Required> if compiling from git. -Optional (but recommended) if compiling from tarball. +I<Required>. =item autoconf @@ -594,8 +593,11 @@ See L</USING A PREBUILT BINARY APPLIANCE> below. Disable specific language bindings, even if C<./configure> finds all the necessary libraries are installed so that they could be compiled. -Note that disabling OCaml or Perl will have the knock-on effect of -disabling large numbers of virt tools and parts of the test suite. +Note that disabling Perl will have the knock-on effect of disabling +parts of the test suite and some tools. + +Disabling OCaml only disables the bindings and several virt tools. +OCaml is required to build libguestfs. =item B<--disable-fuse> diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index bd3d92233..4bca11fce 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -421,6 +421,13 @@ in the C<lib/> directory. In either case, use another function as an example of what to do. +=item 3. + +As an alternative to step 2: Since libguestfs 1.37.15, daemon actions +can be implemented in OCaml. You have to set the C<impl = OCaml ...> +flag in the generator. Take a look at F<daemon/file.ml> for an +example. + =back After making these changes, use C<make> to compile. diff --git a/generator/actions.ml b/generator/actions.ml index a9b3b5906..75742397a 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -185,6 +185,11 @@ let is_fish { visibility = v; style = (_, args, _) } not (List.exists (function Pointer _ -> true | _ -> false) args) let fish_functions = List.filter is_fish +let is_ocaml_function = function + | { impl = OCaml _ } -> true + | { impl = C } -> false +let impl_ocaml_functions = List.filter is_ocaml_function + (* In some places we want the functions to be displayed sorted * alphabetically, so this is useful: *) diff --git a/generator/actions.mli b/generator/actions.mli index 0d326b609..82217cbdc 100644 --- a/generator/actions.mli +++ b/generator/actions.mli @@ -40,6 +40,10 @@ val internal_functions : Types.action list -> Types.action list val fish_functions : Types.action list -> Types.action list (** Filter {!actions}, returning only functions in guestfish. *) +val impl_ocaml_functions : Types.action list -> Types.action list +(** Filter {!actions}, returning only functions implemented + in OCaml (in the daemon). *) + val documented_functions : Types.action list -> Types.action list (** Filter {!actions}, returning only functions requiring documentation. *) diff --git a/generator/daemon.ml b/generator/daemon.ml index 2ae462864..ac410b733 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -471,6 +471,193 @@ let generate_daemon_stubs actions () pr "}\n\n"; ) (actions |> daemon_functions |> sort) +let generate_daemon_caml_types_ml () + generate_header OCamlStyle GPLv2plus + +let generate_daemon_caml_callbacks_ml () + generate_header OCamlStyle GPLv2plus; + + if actions |> impl_ocaml_functions <> [] then ( + pr "let init_callbacks () =\n"; + pr " (* Initialize callbacks to OCaml code. *)\n"; + List.iter ( + fun ({ name = name; style = ret, args, optargs } as f) -> + let ocaml_function + match f.impl with + | OCaml f -> f + | C -> assert false in + + pr " Callback.register %S %s;\n" ocaml_function ocaml_function + ) (actions |> impl_ocaml_functions |> sort) + ) + else + pr "let init_callbacks () = ()\n" + +(* Generate stubs for the functions implemented in OCaml. + * Basically we implement the do_<name> function here, and + * have it call out to OCaml code. + *) +let generate_daemon_caml_stubs () + generate_header CStyle GPLv2plus; + + pr "\ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <inttypes.h> +#include <errno.h> + +#include <caml/alloc.h> +#include <caml/callback.h> +#include <caml/fail.h> +#include <caml/memory.h> +#include <caml/mlvalues.h> + +#include \"daemon.h\" +#include \"actions.h\" + +/* This is not declared in <daemon.h> because we don't want to + * include the OCaml headers (to get 'value') for the whole daemon. + */ +extern void ocaml_exn_to_reply_with_error (const char *func, value exn); + +"; + + List.iter ( + fun ({ name = name; style = ret, args, optargs } as f) -> + let ocaml_function + match f.impl with + | OCaml f -> f + | C -> assert false in + + pr "/* Wrapper for OCaml function ‘%s’. */\n" ocaml_function; + + let args_do_function = args @ args_of_optargs optargs in + let args_do_function + List.filter (function + | String ((FileIn|FileOut), _) -> false | _ -> true) + args_do_function in + let style = ret, args_do_function, [] in + generate_prototype ~extern:false ~semicolon:false + ~single_line:false ~newline:false + ~in_daemon:true ~prefix:"do_" + name style; + pr "\n"; + + let add_unit_arg + let args = List.filter + (function + | String ((FileIn|FileOut), _) -> false | _ -> true) + args in + args = [] in + let nr_args = List.length args_do_function in + + pr "{\n"; + pr " static value *cb = NULL;\n"; + pr " CAMLparam0 ();\n"; + pr " CAMLlocal2 (v, retv);\n"; + pr " CAMLlocalN (args, %d);\n" + (nr_args + if add_unit_arg then 1 else 0); + pr "\n"; + pr " if (cb == NULL)\n"; + pr " cb = caml_named_value (\"%s\");\n" ocaml_function; + pr "\n"; + + (* Construct the actual call, but note that we want to pass + * the optional arguments first in the list. + *) + let i = ref 0 in + List.iter ( + fun optarg -> + let n = name_of_optargt optarg in + let uc_n = String.uppercase_ascii n in + + (* optargs are all passed as [None|Some _] *) + pr " if ((optargs_bitmask & %s_%s_BITMASK) == 0)\n" + f.c_optarg_prefix uc_n; + pr " args[%d] = Val_int (0); /* None */\n" !i; + pr " else {\n"; + pr " v = "; + (match optarg with + | OBool _ -> + pr "Val_bool (%s)" n; + | OInt _ -> assert false + | OInt64 _ -> assert false + | OString _ -> assert false + | OStringList _ -> assert false + ); + pr ";\n"; + pr " args[%d] = caml_alloc (1, 0);\n" !i; + pr " Store_field (args[%d], 0, v);\n" !i; + pr " }\n"; + incr i + ) optargs; + List.iter ( + fun arg -> + pr " args[%d] = " !i; + (match arg with + | Bool n -> pr "Val_bool (%s)" n + | Int n -> pr "Val_int (%s)" n + | Int64 n -> pr "caml_copy_int64 (%s)" n + | String (_, n) -> pr "caml_copy_string (%s)" n + | OptString _ -> assert false + | StringList _ -> assert false + | BufferIn _ -> assert false + | Pointer _ -> assert false + ); + pr ";\n"; + incr i + ) args; + assert (!i = nr_args); + + (* If there are no non-optional arguments, we add a unit arg. *) + if add_unit_arg then + pr " args[%d] = Val_unit;\n" !i; + + pr " retv = caml_callbackN_exn (*cb, %d, args);\n" + (nr_args + if add_unit_arg then 1 else 0); + pr "\n"; + pr " if (Is_exception_result (retv)) {\n"; + pr " retv = Extract_exception (retv);\n"; + pr " ocaml_exn_to_reply_with_error (%S, retv);\n" name; + (match errcode_of_ret ret with + | `CannotReturnError -> + pr " CAMLreturn0;\n" + | `ErrorIsMinusOne -> + pr " CAMLreturnT (int, -1);\n" + | `ErrorIsNULL -> + pr " CAMLreturnT (void *, NULL);\n" + ); + pr " }\n"; + pr "\n"; + + (match ret with + | RErr -> assert false + | RInt _ -> assert false + | RInt64 _ -> assert false + | RBool _ -> assert false + | RConstString _ -> assert false + | RConstOptString _ -> assert false + | RString _ -> + pr " char *ret = strdup (String_val (retv));\n"; + pr " if (ret == NULL) {\n"; + pr " reply_with_perror (\"strdup\");\n"; + pr " CAMLreturnT (char *, NULL);\n"; + pr " }\n"; + pr " CAMLreturnT (char *, ret); /* caller frees */\n" + | RStringList _ -> assert false + | RStruct _ -> assert false + | RStructList _ -> assert false + | RHashtable _ -> assert false + | RBufferOut _ -> assert false + ); + pr "}\n"; + pr "\n" + ) (actions |> impl_ocaml_functions |> sort) + let generate_daemon_dispatch () generate_header CStyle GPLv2plus; diff --git a/generator/daemon.mli b/generator/daemon.mli index ff008bf85..314a6da8f 100644 --- a/generator/daemon.mli +++ b/generator/daemon.mli @@ -19,6 +19,9 @@ val generate_daemon_actions_h : unit -> unit val generate_daemon_stubs_h : unit -> unit val generate_daemon_stubs : Types.action list -> unit -> unit +val generate_daemon_caml_stubs : unit -> unit +val generate_daemon_caml_callbacks_ml : unit -> unit +val generate_daemon_caml_types_ml : unit -> unit val generate_daemon_dispatch : unit -> unit val generate_daemon_lvm_tokenization : unit -> unit val generate_daemon_names : unit -> unit diff --git a/generator/main.ml b/generator/main.ml index 33fe2b2ee..a6c805e2e 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -133,6 +133,12 @@ Run it from the top source directory using the command Daemon.generate_daemon_stubs_h; output_to_subset "daemon/stubs-%d.c" Daemon.generate_daemon_stubs; + output_to "daemon/caml-stubs.c" + Daemon.generate_daemon_caml_stubs; + output_to "daemon/callbacks.ml" + Daemon.generate_daemon_caml_callbacks_ml; + output_to "daemon/types.ml" + Daemon.generate_daemon_caml_types_ml; output_to "daemon/dispatch.c" Daemon.generate_daemon_dispatch; output_to "daemon/names.c" diff --git a/generator/types.ml b/generator/types.ml index 740bc7750..fb6c3bc06 100644 --- a/generator/types.ml +++ b/generator/types.ml @@ -379,11 +379,16 @@ type deprecated_by | Replaced_by of string (* replaced by another function *) | Deprecated_no_replacement (* deprecated with no replacement *) +type impl + | C (* implemented in C by "do_<name>" *) + | OCaml of string (* implemented in OCaml by named function *) + (* Type of an action as declared in Actions module. *) type action = { name : string; (* name, not including "guestfs_" *) added : version; (* which version was the API first added *) style : style; (* args and return value *) + impl : impl; (* implementation language (C or OCaml) *) proc_nr : int option; (* proc number, None for non-daemon *) tests : c_api_tests; (* C API tests *) test_excuse : string; (* if there's no tests ... *) @@ -439,7 +444,7 @@ type action = { *) let defaults = { name = ""; added = (-1,-1,-1); - style = RErr, [], []; proc_nr = None; + style = RErr, [], []; impl = C; proc_nr = None; tests = []; test_excuse = ""; shortdesc = ""; longdesc = ""; protocol_limit_warning = false; fish_alias = []; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 16/41] daemon: Reimplement ‘file’ API in OCaml.
‘file’ is a small, self-contained API which runs a single command, so it's a good test case for reimplementing APIs. --- daemon/Makefile.am | 2 ++ daemon/file.c | 80 ----------------------------------------------- daemon/file.ml | 60 +++++++++++++++++++++++++++++++++++ daemon/file.mli | 19 +++++++++++ generator/actions_core.ml | 1 + 5 files changed, 82 insertions(+), 80 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 3fb70fe52..f354a0d6f 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -259,6 +259,7 @@ guestfsd_CFLAGS = \ SOURCES_MLI = \ chroot.mli \ sysroot.mli \ + file.mli \ utils.mli SOURCES_ML = \ @@ -266,6 +267,7 @@ SOURCES_ML = \ utils.ml \ sysroot.ml \ chroot.ml \ + file.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/file.c b/daemon/file.c index 84874dc6f..ee79eb507 100644 --- a/daemon/file.c +++ b/daemon/file.c @@ -30,7 +30,6 @@ #include "actions.h" #include "optgroups.h" -GUESTFSD_EXT_CMD(str_file, file); GUESTFSD_EXT_CMD(str_zcat, zcat); GUESTFSD_EXT_CMD(str_bzcat, bzcat); @@ -449,85 +448,6 @@ do_pwrite_device (const char *device, const char *content, size_t size, return pwrite_fd (fd, content, size, offset, device, 1); } -/* This runs the 'file' command. */ -char * -do_file (const char *path) -{ - CLEANUP_FREE char *buf = NULL; - const char *display_path = path; - const int is_dev = STRPREFIX (path, "/dev/"); - struct stat statbuf; - - if (!is_dev) { - buf = sysroot_path (path); - if (!buf) { - reply_with_perror ("malloc"); - return NULL; - } - path = buf; - - /* For non-dev, check this is a regular file, else just return the - * file type as a string (RHBZ#582484). - */ - if (lstat (path, &statbuf) == -1) { - reply_with_perror ("lstat: %s", display_path); - return NULL; - } - - if (! S_ISREG (statbuf.st_mode)) { - char *ret; - - if (S_ISDIR (statbuf.st_mode)) - ret = strdup ("directory"); - else if (S_ISCHR (statbuf.st_mode)) - ret = strdup ("character device"); - else if (S_ISBLK (statbuf.st_mode)) - ret = strdup ("block device"); - else if (S_ISFIFO (statbuf.st_mode)) - ret = strdup ("FIFO"); - else if (S_ISLNK (statbuf.st_mode)) - ret = strdup ("symbolic link"); - else if (S_ISSOCK (statbuf.st_mode)) - ret = strdup ("socket"); - else - ret = strdup ("unknown, not regular file"); - - if (ret == NULL) - reply_with_perror ("strdup"); - return ret; - } - } - - /* Which flags to use? For /dev paths, follow links because - * /dev/VG/LV is a symbolic link. - */ - const char *flags = is_dev ? "-zbsL" : "-zb"; - - char *out; - CLEANUP_FREE char *err = NULL; - int r = command (&out, &err, str_file, flags, path, NULL); - - if (r == -1) { - free (out); - reply_with_error ("%s: %s", display_path, err); - return NULL; - } - - /* We need to remove the trailing \n from output of file(1). */ - size_t len = strlen (out); - if (len > 0 && out[len-1] == '\n') - out[--len] = '\0'; - - /* Some upstream versions of file add a space at the end of the - * output. This is fixed in the Fedora version, but we might as - * well fix it here too. (RHBZ#928995). - */ - if (len > 0 && out[len-1] == ' ') - out[--len] = '\0'; - - return out; /* caller frees */ -} - /* zcat | file */ char * do_zfile (const char *method, const char *path) diff --git a/daemon/file.ml b/daemon/file.ml new file mode 100644 index 000000000..557de764b --- /dev/null +++ b/daemon/file.ml @@ -0,0 +1,60 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Unix +open Printf + +open Std_utils + +open Utils + +(* This runs the [file] command. *) +let file path + let is_dev = String.is_prefix path "/dev/" in + + (* For non-dev, check this is a regular file, else just return the + * file type as a string (RHBZ#582484). + *) + if not is_dev then ( + let sysroot = Sysroot.sysroot () in + let chroot = Chroot.create sysroot ~name:(sprintf "file: %s" path) in + + let statbuf = Chroot.f chroot lstat path in + match statbuf.st_kind with + | S_DIR -> "directory" + | S_CHR -> "character device" + | S_BLK -> "block device" + | S_FIFO -> "FIFO" + | S_LNK -> "symbolic link" + | S_SOCK -> "socket" + | S_REG -> + (* Regular file, so now run [file] on it. *) + let out = command "file" ["-zb"; sysroot // path] in + + (* We need to remove the trailing \n from output of file(1). + * + * Some upstream versions of file add a space at the end of the + * output. This is fixed in the Fedora version, but we might as + * well fix it here too. (RHBZ#928995). + *) + String.trimr out + ) + else (* it's a device *) ( + let out = command "file" ["-zbsL"; path] in + String.trimr out + ) diff --git a/daemon/file.mli b/daemon/file.mli new file mode 100644 index 000000000..bd49bad0b --- /dev/null +++ b/daemon/file.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 file : string -> string diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 0e667eff1..26ed1274e 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -2321,6 +2321,7 @@ and physical volumes." }; { defaults with name = "file"; added = (1, 9, 1); style = RString (RPlainString, "description"), [String (Dev_or_Path, "path")], []; + impl = OCaml "File.file"; tests = [ InitISOFS, Always, TestResultString ( [["file"; "/empty"]], "empty"), []; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 17/41] daemon: Reimplement ‘vfs_type’ API in OCaml.
This also implements support for String (Mountable, _) parameters. --- daemon/Makefile.am | 4 ++++ daemon/blkid.c | 6 ------ daemon/blkid.ml | 40 ++++++++++++++++++++++++++++++++++++++++ daemon/blkid.mli | 19 +++++++++++++++++++ daemon/mountable.ml | 43 +++++++++++++++++++++++++++++++++++++++++++ daemon/mountable.mli | 34 ++++++++++++++++++++++++++++++++++ generator/actions_core.ml | 1 + generator/daemon.ml | 38 ++++++++++++++++++++++++++++++++++++-- 8 files changed, 177 insertions(+), 8 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index f354a0d6f..fc99c500f 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -257,16 +257,20 @@ guestfsd_CFLAGS = \ # library and then linked to the daemon. See # https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html SOURCES_MLI = \ + blkid.mli \ chroot.mli \ sysroot.mli \ file.mli \ + mountable.mli \ utils.mli SOURCES_ML = \ types.ml \ utils.ml \ sysroot.ml \ + mountable.ml \ chroot.ml \ + blkid.ml \ file.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/blkid.c b/daemon/blkid.c index 1fe5ff93a..7757b5ad0 100644 --- a/daemon/blkid.c +++ b/daemon/blkid.c @@ -69,12 +69,6 @@ get_blkid_tag (const char *device, const char *tag) } char * -do_vfs_type (const mountable_t *mountable) -{ - return get_blkid_tag (mountable->device, "TYPE"); -} - -char * do_vfs_label (const mountable_t *mountable) { CLEANUP_FREE char *type = do_vfs_type (mountable); diff --git a/daemon/blkid.ml b/daemon/blkid.ml new file mode 100644 index 000000000..3345f826e --- /dev/null +++ b/daemon/blkid.ml @@ -0,0 +1,40 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Utils + +let rec vfs_type { Mountable.m_device = device } + get_blkid_tag device "TYPE" + +and get_blkid_tag device tag + let r, out, err + commandr "blkid" + [(* Adding -c option kills all caching, even on RHEL 5. *) + "-c"; "/dev/null"; + "-o"; "value"; "-s"; tag; device] in + match r with + | 0 -> (* success *) + String.trimr out + + | 2 -> (* means tag not found, we return "" *) + "" + + | _ -> + failwithf "blkid: %s: %s" tag err diff --git a/daemon/blkid.mli b/daemon/blkid.mli new file mode 100644 index 000000000..59a86ac2c --- /dev/null +++ b/daemon/blkid.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 vfs_type : Mountable.t -> string diff --git a/daemon/mountable.ml b/daemon/mountable.ml new file mode 100644 index 000000000..96dffb80b --- /dev/null +++ b/daemon/mountable.ml @@ -0,0 +1,43 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +type t = { + m_type : mountable_type; + m_device : string; +} +and mountable_type + | MountableDevice + | MountablePath + | MountableBtrfsVol of string (* volume *) + +let to_string { m_type = t; m_device = device } + match t with + | MountableDevice | MountablePath -> device + | MountableBtrfsVol volume -> + sprintf "btrfsvol:%s/%s" device volume + +let of_device device + { m_type = MountableDevice; m_device = device } + +let of_path path + { m_type = MountablePath; m_device = path } + +let of_btrfsvol device volume + { m_type = MountableBtrfsVol volume; m_device = device } diff --git a/daemon/mountable.mli b/daemon/mountable.mli new file mode 100644 index 000000000..52f1ad45b --- /dev/null +++ b/daemon/mountable.mli @@ -0,0 +1,34 @@ +(* guestfs-inspection + * Copyright (C) 2009-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. + *) + +type t = { + m_type : mountable_type; + m_device : string; +} +and mountable_type + | MountableDevice + | MountablePath + | MountableBtrfsVol of string (* volume *) + +val to_string : t -> string +(** Convert the mountable back to the string used in the public API. *) + +val of_device : string -> t +val of_path : string -> t +val of_btrfsvol : string -> string -> t +(** Create a mountable from various objects. *) diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 26ed1274e..a6eb2c273 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -4872,6 +4872,7 @@ See also C<guestfs_realpath>." }; { defaults with name = "vfs_type"; added = (1, 0, 75); style = RString (RPlainString, "fstype"), [String (Mountable, "mountable")], []; + impl = OCaml "Blkid.vfs_type"; tests = [ InitScratchFS, Always, TestResultString ( [["vfs_type"; "/dev/sdb1"]], "ext2"), [] diff --git a/generator/daemon.ml b/generator/daemon.ml index ac410b733..121634806 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -524,6 +524,35 @@ let generate_daemon_caml_stubs () */ extern void ocaml_exn_to_reply_with_error (const char *func, value exn); +/* Implement String (Mountable, _) parameter. */ +static value +copy_mountable (const mountable_t *mountable) +{ + CAMLparam0 (); + CAMLlocal4 (r, typev, devicev, volumev); + + switch (mountable->type) { + case MOUNTABLE_DEVICE: + typev = Val_int (0); /* MountableDevice */ + break; + case MOUNTABLE_PATH: + typev = Val_int (1); /* MountablePath */ + break; + case MOUNTABLE_BTRFSVOL: + volumev = caml_copy_string (mountable->volume); + typev = caml_alloc (1, 0); /* MountableBtrfsVol */ + Store_field (typev, 0, volumev); + } + + devicev = caml_copy_string (mountable->device); + + r = caml_alloc_tuple (2); + Store_field (r, 0, typev); + Store_field (r, 1, devicev); + + CAMLreturn (r); +} + "; List.iter ( @@ -602,7 +631,11 @@ extern void ocaml_exn_to_reply_with_error (const char *func, value exn); | Bool n -> pr "Val_bool (%s)" n | Int n -> pr "Val_int (%s)" n | Int64 n -> pr "caml_copy_int64 (%s)" n - | String (_, n) -> pr "caml_copy_string (%s)" n + | String ((PlainString|Device|Dev_or_Path), n) -> + pr "caml_copy_string (%s)" n + | String (Mountable, n) -> + pr "copy_mountable (%s)" n + | String _ -> assert false | OptString _ -> assert false | StringList _ -> assert false | BufferIn _ -> assert false @@ -641,13 +674,14 @@ extern void ocaml_exn_to_reply_with_error (const char *func, value exn); | RBool _ -> assert false | RConstString _ -> assert false | RConstOptString _ -> assert false - | RString _ -> + | RString (RPlainString, _) -> pr " char *ret = strdup (String_val (retv));\n"; pr " if (ret == NULL) {\n"; pr " reply_with_perror (\"strdup\");\n"; pr " CAMLreturnT (char *, NULL);\n"; pr " }\n"; pr " CAMLreturnT (char *, ret); /* caller frees */\n" + | RString _ -> assert false | RStringList _ -> assert false | RStruct _ -> assert false | RStructList _ -> assert false -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 18/41] daemon: Reimplement several devsparts APIs in OCaml.
The reimplemented APIs are: * list_devices * list_partitions * part_to_dev * part_to_partnum * is_whole_device --- daemon/Makefile.am | 2 + daemon/daemon.h | 3 - daemon/devsparts.c | 257 ---------------------------------------------- daemon/devsparts.ml | 109 ++++++++++++++++++++ daemon/devsparts.mli | 25 +++++ daemon/guestfsd.c | 75 -------------- daemon/utils.ml | 84 +++++++++++++++ daemon/utils.mli | 15 +++ generator/actions_core.ml | 5 + generator/daemon.ml | 32 +++++- 10 files changed, 268 insertions(+), 339 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index fc99c500f..e616de73b 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -260,6 +260,7 @@ SOURCES_MLI = \ blkid.mli \ chroot.mli \ sysroot.mli \ + devsparts.mli \ file.mli \ mountable.mli \ utils.mli @@ -271,6 +272,7 @@ SOURCES_ML = \ mountable.ml \ chroot.ml \ blkid.ml \ + devsparts.ml \ file.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/daemon.h b/daemon/daemon.h index 2d296a6c2..684f9b096 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -130,9 +130,6 @@ extern void free_stringsbuf (struct stringsbuf *sb); extern void sort_strings (char **argv, size_t len); extern void free_stringslen (char **argv, size_t len); -extern void sort_device_names (char **argv, size_t len); -extern int compare_device_names (const char *a, const char *b); - extern struct stringsbuf split_lines_sb (char *str); extern char **split_lines (char *str); diff --git a/daemon/devsparts.c b/daemon/devsparts.c index 82467b92f..1aacb8e16 100644 --- a/daemon/devsparts.c +++ b/daemon/devsparts.c @@ -33,263 +33,6 @@ #include "daemon.h" #include "actions.h" -typedef int (*block_dev_func_t) (const char *dev, struct stringsbuf *r); - -/* Execute a given function for each discovered block device */ -static char ** -foreach_block_device (block_dev_func_t func, bool return_md) -{ - CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (r); - DIR *dir; - int err = 0; - struct dirent *d; - int fd; - - dir = opendir ("/sys/block"); - if (!dir) { - reply_with_perror ("opendir: /sys/block"); - return NULL; - } - - for (;;) { - errno = 0; - d = readdir (dir); - if (!d) break; - - if (STREQLEN (d->d_name, "sd", 2) || - STREQLEN (d->d_name, "hd", 2) || - STREQLEN (d->d_name, "ubd", 3) || - STREQLEN (d->d_name, "vd", 2) || - STREQLEN (d->d_name, "sr", 2) || - (return_md && - STREQLEN (d->d_name, "md", 2) && c_isdigit (d->d_name[2]))) { - CLEANUP_FREE char *dev_path = NULL; - if (asprintf (&dev_path, "/dev/%s", d->d_name) == -1) { - reply_with_perror ("asprintf"); - closedir (dir); - return NULL; - } - - /* Ignore the root device. */ - if (is_root_device (dev_path)) - continue; - - /* RHBZ#514505: Some versions of qemu <= 0.10 add a - * CD-ROM device even though we didn't request it. Try to - * detect this by seeing if the device contains media. - */ - fd = open (dev_path, O_RDONLY|O_CLOEXEC); - if (fd == -1) { - perror (dev_path); - continue; - } - close (fd); - - /* Call the map function for this device */ - if ((*func)(d->d_name, &r) != 0) { - err = 1; - break; - } - } - } - - /* Check readdir didn't fail */ - if (errno != 0) { - reply_with_perror ("readdir: /sys/block"); - closedir (dir); - return NULL; - } - - /* Close the directory handle */ - if (closedir (dir) == -1) { - reply_with_perror ("closedir: /sys/block"); - return NULL; - } - - if (err) - return NULL; - - /* Sort the devices. */ - if (r.size > 0) - sort_device_names (r.argv, r.size); - - /* NULL terminate the list */ - if (end_stringsbuf (&r) == -1) { - return NULL; - } - - return take_stringsbuf (&r); -} - -/* Add a device to the list of devices */ -static int -add_device (const char *device, struct stringsbuf *r) -{ - char *dev_path; - - if (asprintf (&dev_path, "/dev/%s", device) == -1) { - reply_with_perror ("asprintf"); - return -1; - } - - if (add_string_nodup (r, dev_path) == -1) - return -1; - - return 0; -} - -char ** -do_list_devices (void) -{ - /* For backwards compatibility, don't return MD devices in the list - * returned by guestfs_list_devices. This is because most API users - * expect that this list is effectively the same as the list of - * devices added by guestfs_add_drive. - * - * Also, MD devices are special devices - unlike the devices exposed - * by QEMU, and there is a special API for them, - * guestfs_list_md_devices. - */ - return foreach_block_device (add_device, false); -} - -static int -add_partitions (const char *device, struct stringsbuf *r) -{ - CLEANUP_FREE char *devdir = NULL; - - /* Open the device's directory under /sys/block */ - if (asprintf (&devdir, "/sys/block/%s", device) == -1) { - reply_with_perror ("asprintf"); - return -1; - } - - DIR *dir = opendir (devdir); - if (!dir) { - reply_with_perror ("opendir: %s", devdir); - return -1; - } - - /* Look in /sys/block/<device>/ for entries starting with <device> - * e.g. /sys/block/sda/sda1 - */ - errno = 0; - struct dirent *d; - while ((d = readdir (dir)) != NULL) { - if (STREQLEN (d->d_name, device, strlen (device))) { - CLEANUP_FREE char *part = NULL; - if (asprintf (&part, "/dev/%s", d->d_name) == -1) { - perror ("asprintf"); - closedir (dir); - return -1; - } - - if (add_string (r, part) == -1) { - closedir (dir); - return -1; - } - } - } - - /* Check if readdir failed */ - if (0 != errno) { - reply_with_perror ("readdir: %s", devdir); - closedir (dir); - return -1; - } - - /* Close the directory handle */ - if (closedir (dir) == -1) { - reply_with_perror ("closedir: /sys/block/%s", device); - return -1; - } - - return 0; -} - -char ** -do_list_partitions (void) -{ - return foreach_block_device (add_partitions, true); -} - -char * -do_part_to_dev (const char *part) -{ - int err = 1; - size_t n = strlen (part); - - while (n >= 1 && c_isdigit (part[n-1])) { - err = 0; - n--; - } - - if (err) { - reply_with_error ("device name is not a partition"); - return NULL; - } - - /* Deal with <device>p<N> partition names such as /dev/md0p1. */ - if (part[n-1] == 'p') - n--; - - char *r = strndup (part, n); - if (r == NULL) { - reply_with_perror ("strdup"); - return NULL; - } - - return r; -} - -int -do_part_to_partnum (const char *part) -{ - int err = 1; - size_t n = strlen (part); - - while (n >= 1 && c_isdigit (part[n-1])) { - err = 0; - n--; - } - - if (err) { - reply_with_error ("device name is not a partition"); - return -1; - } - - int r; - if (sscanf (&part[n], "%d", &r) != 1) { - reply_with_error ("could not parse number"); - return -1; - } - - return r; -} - -int -do_is_whole_device (const char *device) -{ - /* A 'whole' block device will have a symlink to the device in its - * /sys/block directory */ - CLEANUP_FREE char *devpath = NULL; - if (asprintf (&devpath, "/sys/block/%s/device", - device + strlen ("/dev/")) == -1) { - reply_with_perror ("asprintf"); - return -1; - } - - struct stat statbuf; - if (stat (devpath, &statbuf) == -1) { - if (errno == ENOENT || errno == ENOTDIR) return 0; - - reply_with_perror ("stat"); - return -1; - } - - return 1; -} - int do_device_index (const char *device) { diff --git a/daemon/devsparts.ml b/daemon/devsparts.ml new file mode 100644 index 000000000..e97ff1267 --- /dev/null +++ b/daemon/devsparts.ml @@ -0,0 +1,109 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf +open Unix + +open Std_utils + +open Utils + +let map_block_devices ~return_md f + let devs = Sys.readdir "/sys/block" in + let devs = Array.to_list devs in + let devs = List.filter ( + fun dev -> + String.is_prefix dev "sd" || + String.is_prefix dev "hd" || + String.is_prefix dev "ubd" || + String.is_prefix dev "vd" || + String.is_prefix dev "sr" || + (return_md && String.is_prefix dev "md" && + String.length dev >= 3 && Char.isdigit dev.[2]) + ) devs in + + (* Ignore the root device. *) + let devs + List.filter (fun dev -> not (is_root_device ("/dev/" ^ dev))) devs in + + (* RHBZ#514505: Some versions of qemu <= 0.10 add a + * CD-ROM device even though we didn't request it. Try to + * detect this by seeing if the device contains media. + *) + let devs + List.filter ( + fun dev -> + try close (openfile ("/dev/" ^ dev) [O_RDONLY; O_CLOEXEC] 0); true + with _ -> false + ) devs in + + (* Call the map function for the devices left in the list. *) + List.map f devs + +let list_devices () + (* For backwards compatibility, don't return MD devices in the list + * returned by guestfs_list_devices. This is because most API users + * expect that this list is effectively the same as the list of + * devices added by guestfs_add_drive. + * + * Also, MD devices are special devices - unlike the devices exposed + * by QEMU, and there is a special API for them, + * guestfs_list_md_devices. + *) + let devices + map_block_devices ~return_md:false (fun dev -> "/dev/" ^ dev) in + sort_device_names devices + +let rec list_partitions () + let partitions = map_block_devices ~return_md:true add_partitions in + let partitions = List.flatten partitions in + sort_device_names partitions + +and add_partitions dev + (* Open the device's directory under /sys/block *) + let parts = Sys.readdir ("/sys/block/" ^ dev) in + let parts = Array.to_list parts in + + (* Look in /sys/block/<device>/ for entries starting with + * <device>, eg. /sys/block/sda/sda1. + *) + let parts = List.filter (fun part -> String.is_prefix part dev) parts in + List.map (fun part -> "/dev/" ^ part) parts + +let part_to_dev part + let dev, part = split_device_partition part in + if part = 0 then + failwithf "device name is not a partition"; + "/dev/" ^ dev + +let part_to_partnum part + let _, part = split_device_partition part in + if part = 0 then + failwithf "device name is not a partition"; + part + +let is_whole_device device + (* A 'whole' block device will have a symlink to the device in its + * /sys/block directory + *) + assert (String.is_prefix device "/dev/"); + let device = String.sub device 5 (String.length device - 5) in + let devpath = sprintf "/sys/block/%s/device" device in + + try ignore (stat devpath); true + with Unix_error ((ENOENT|ENOTDIR), _, _) -> false diff --git a/daemon/devsparts.mli b/daemon/devsparts.mli new file mode 100644 index 000000000..4dfaa86e6 --- /dev/null +++ b/daemon/devsparts.mli @@ -0,0 +1,25 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 list_devices : unit -> string list +val list_partitions : unit -> string list + +val part_to_dev : string -> string +val part_to_partnum : string -> int + +val is_whole_device : string -> bool diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index 05337b31c..9704094a6 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -630,81 +630,6 @@ free_stringslen (char **argv, size_t len) } /** - * Compare device names (including partition numbers if present). - * - * L<https://rwmj.wordpress.com/2011/01/09/how-are-linux-drives-named-beyond-drive-26-devsdz/> - */ -int -compare_device_names (const char *a, const char *b) -{ - size_t alen, blen; - int r; - int a_partnum, b_partnum; - - /* Skip /dev/ prefix if present. */ - if (STRPREFIX (a, "/dev/")) - a += 5; - if (STRPREFIX (b, "/dev/")) - b += 5; - - /* Skip sd/hd/ubd/vd. */ - alen = strcspn (a, "d"); - blen = strcspn (b, "d"); - assert (alen > 0 && alen <= 2); - assert (blen > 0 && blen <= 2); - a += alen + 1; - b += blen + 1; - - /* Get device name part, that is, just 'a', 'ab' etc. */ - alen = strcspn (a, "0123456789"); - blen = strcspn (b, "0123456789"); - - /* If device name part is longer, it is always greater, eg. - * "/dev/sdz" < "/dev/sdaa". - */ - if (alen != blen) - return alen - blen; - - /* Device name parts are the same length, so do a regular compare. */ - r = strncmp (a, b, alen); - if (r != 0) - return r; - - /* Compare partitions numbers. */ - a += alen; - b += alen; - - /* If no partition numbers, bail -- the devices are the same. This - * can happen in one peculiar case: where you have a mix of devices - * with different interfaces (eg. /dev/sda and /dev/vda). - * (RHBZ#858128). - */ - if (!*a && !*b) - return 0; - - r = sscanf (a, "%d", &a_partnum); - assert (r == 1); - r = sscanf (b, "%d", &b_partnum); - assert (r == 1); - - return a_partnum - b_partnum; -} - -static int -compare_device_names_vp (const void *vp1, const void *vp2) -{ - char * const *p1 = (char * const *) vp1; - char * const *p2 = (char * const *) vp2; - return compare_device_names (*p1, *p2); -} - -void -sort_device_names (char **argv, size_t len) -{ - qsort (argv, len, sizeof (char *), compare_device_names_vp); -} - -/** * Split an output string into a NULL-terminated list of lines, * wrapped into a stringsbuf. * diff --git a/daemon/utils.ml b/daemon/utils.ml index 7630a5534..48f6b9c5c 100644 --- a/daemon/utils.ml +++ b/daemon/utils.ml @@ -129,6 +129,90 @@ let is_root_device device device func arg (error_message err); false +(* XXX This function is copied from C, but is misconceived. It + * cannot by design work for devices like /dev/md0. It would be + * better if it checked for the existence of devices and partitions + * in /sys/block so we know what the kernel thinks is a device or + * partition. The same applies to APIs such as part_to_partnum + * and part_to_dev which rely on this function. + *) +let split_device_partition dev + (* Skip /dev/ prefix if present. *) + let dev + if String.is_prefix dev "/dev/" then + String.sub dev 5 (String.length dev - 5) + else dev in + + (* Find the partition number (if present). *) + let dev, part + let n = String.length dev in + let i = ref n in + while !i >= 1 && Char.isdigit dev.[!i-1] do + decr i + done; + let i = !i in + if i = n then + dev, 0 (* no partition number, whole device *) + else + String.sub dev 0 i, int_of_string (String.sub dev i (n-i)) in + + (* Deal with device names like /dev/md0p1. *) + (* XXX This function is buggy (as was the old C function) when + * presented with a whole device like /dev/md0. + *) + let dev + let n = String.length dev in + if n < 2 || dev.[n-1] <> 'p' || not (Char.isdigit dev.[n-2]) then + dev + else ( + let i = ref (n-1) in + while !i >= 0 && Char.isdigit dev.[!i] do + decr i; + done; + let i = !i in + String.sub dev 0 i + ) in + + dev, part + +let rec sort_device_names devs + List.sort compare_device_names devs + +and compare_device_names a b + (* This takes the device name like "/dev/sda1" and returns ("sda", 1). *) + let dev_a, part_a = split_device_partition a + and dev_b, part_b = split_device_partition b in + + (* Skip "sd|hd|ubd..." so that /dev/sda and /dev/vda sort together. + * (This is what the old C function did, but it's not clear if it + * is still relevant. XXX) + *) + let skip_prefix dev + let n = String.length dev in + if n >= 2 && dev.[1] = 'd' then + String.sub dev 2 (String.length dev - 2) + else if n >= 3 && dev.[2] = 'd' then + String.sub dev 3 (String.length dev - 3) + else + dev in + let dev_a = skip_prefix dev_a + and dev_b = skip_prefix dev_b in + + (* If device name part is longer, it is always greater, eg. + * "/dev/sdz" < "/dev/sdaa". + *) + let r = compare (String.length dev_a) (String.length dev_b) in + if r <> 0 then r + else ( + (* Device name parts are the same length, so do a regular compare. *) + let r = compare dev_a dev_b in + if r <> 0 then r + else ( + (* Device names are identical, so compare partition numbers. *) + compare part_a part_b + ) + ) + let proc_unmangle_path path let n = String.length path in let b = Buffer.create n in diff --git a/daemon/utils.mli b/daemon/utils.mli index 57f703c6c..a1f956be3 100644 --- a/daemon/utils.mli +++ b/daemon/utils.mli @@ -41,6 +41,21 @@ val is_root_device_stat : Unix.stats -> bool (** As for {!is_root_device} but operates on a statbuf instead of a device name. *) +val split_device_partition : string -> string * int +(** Split a device name like [/dev/sda1] into a device name and + partition number, eg. ["sda", 1]. + + The [/dev/] prefix is skipped and removed, if present. + + If the partition number is not present (a whole device), 0 is returned. + + This function splits [/dev/md0p1] to ["md0", 1]. *) + +val sort_device_names : string list -> string list +(** Sort device names correctly so that /dev/sdaa appears after /dev/sdz. + This also deals with partition numbers, and works whether or not + [/dev/] is present. *) + val proc_unmangle_path : string -> string (** Reverse kernel path escaping done in fs/seq_file.c:mangle_path. This is inconsistently used for /proc fields. *) diff --git a/generator/actions_core.ml b/generator/actions_core.ml index a6eb2c273..94391288f 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -1817,6 +1817,7 @@ is I<not> intended that you try to parse the output string." }; { defaults with name = "list_devices"; added = (0, 0, 4); style = RStringList (RDevice, "devices"), [], []; + impl = OCaml "Devsparts.list_devices"; tests = [ InitEmpty, Always, TestResult ( [["list_devices"]], @@ -1833,6 +1834,7 @@ See also C<guestfs_list_filesystems>." }; { defaults with name = "list_partitions"; added = (0, 0, 4); style = RStringList (RDevice, "partitions"), [], []; + impl = OCaml "Devsparts.list_partitions"; tests = [ InitBasicFS, Always, TestResult ( [["list_partitions"]], @@ -6086,6 +6088,7 @@ See also C<guestfs_stat>." }; { defaults with name = "part_to_dev"; added = (1, 5, 15); style = RString (RDevice, "device"), [String (Device, "partition")], []; + impl = OCaml "Devsparts.part_to_dev"; tests = [ InitPartition, Always, TestResultDevice ( [["part_to_dev"; "/dev/sda1"]], "/dev/sda"), []; @@ -6533,6 +6536,7 @@ as in C<guestfs_compress_out>." }; { defaults with name = "part_to_partnum"; added = (1, 13, 25); style = RInt "partnum", [String (Device, "partition")], []; + impl = OCaml "Devsparts.part_to_partnum"; tests = [ InitPartition, Always, TestResult ( [["part_to_partnum"; "/dev/sda1"]], "ret == 1"), []; @@ -8480,6 +8484,7 @@ you are better to use C<guestfs_mv> instead." }; { defaults with name = "is_whole_device"; added = (1, 21, 9); style = RBool "flag", [String (Device, "device")], []; + impl = OCaml "Devsparts.is_whole_device"; tests = [ InitEmpty, Always, TestResultTrue ( [["is_whole_device"; "/dev/sda"]]), []; diff --git a/generator/daemon.ml b/generator/daemon.ml index 121634806..3ffe91537 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -553,6 +553,26 @@ copy_mountable (const mountable_t *mountable) CAMLreturn (r); } +/* Implement RStringList. */ +static char ** +return_string_list (value retv) +{ + CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); + value v; + + while (retv != Val_int (0)) { + v = Field (retv, 0); + if (add_string (&ret, String_val (v)) == -1) + return NULL; + retv = Field (retv, 1); + } + + if (end_stringsbuf (&ret) == -1) + return NULL; + + return take_stringsbuf (&ret); /* caller frees */ +} + "; List.iter ( @@ -669,12 +689,14 @@ copy_mountable (const mountable_t *mountable) (match ret with | RErr -> assert false - | RInt _ -> assert false + | RInt _ -> + pr " CAMLreturnT (int, Int_val (retv));\n" | RInt64 _ -> assert false - | RBool _ -> assert false + | RBool _ -> + pr " CAMLreturnT (int, Bool_val (retv));\n" | RConstString _ -> assert false | RConstOptString _ -> assert false - | RString (RPlainString, _) -> + | RString ((RPlainString|RDevice), _) -> pr " char *ret = strdup (String_val (retv));\n"; pr " if (ret == NULL) {\n"; pr " reply_with_perror (\"strdup\");\n"; @@ -682,7 +704,9 @@ copy_mountable (const mountable_t *mountable) pr " }\n"; pr " CAMLreturnT (char *, ret); /* caller frees */\n" | RString _ -> assert false - | RStringList _ -> assert false + | RStringList _ -> + pr " char **ret = return_string_list (retv);\n"; + pr " CAMLreturnT (char **, ret); /* caller frees */\n" | RStruct _ -> assert false | RStructList _ -> assert false | RHashtable _ -> assert false -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 19/41] daemon: Add unit tests of the ‘Utils’ module.
--- .gitignore | 1 + daemon/Makefile.am | 44 +++++++++++++++++++++++++++++++++++++++- daemon/daemon_utils_tests.ml | 48 ++++++++++++++++++++++++++++++++++++++++++++ daemon/dummy.c | 2 ++ docs/C_SOURCE_FILES | 1 + 5 files changed, 95 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 50f322974..6fceba555 100644 --- a/.gitignore +++ b/.gitignore @@ -169,6 +169,7 @@ Makefile.in /daemon/actions.h /daemon/callbacks.ml /daemon/caml-stubs.c +/daemon/daemon_utils_tests /daemon/dispatch.c /daemon/guestfsd /daemon/guestfsd.8 diff --git a/daemon/Makefile.am b/daemon/Makefile.am index e616de73b..306f371a1 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -56,6 +56,7 @@ BUILT_SOURCES = \ EXTRA_DIST = \ $(generator_built) \ $(SOURCES_MLI) $(SOURCES_ML) \ + daemon_utils_tests.ml \ guestfsd.pod if INSTALL_DAEMON @@ -283,7 +284,9 @@ XOBJECTS = $(BOBJECTS:.cmo=.cmx) OCAMLPACKAGES = \ -package str,unix,hivex \ -I $(top_srcdir)/common/mlstdutils \ - -I $(top_srcdir)/common/mlutils + -I $(top_srcdir)/common/mlutils \ + -I $(top_builddir)/common/utils/.libs \ + -I $(top_builddir)/common/cleanups/.libs OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_FLAGS) @@ -310,6 +313,45 @@ camldaemon.o: $(OBJECTS) -linkpkg mlcutils.$(MLARCHIVE) mlstdutils.$(MLARCHIVE) \ $(OBJECTS) +# Unit tests. + +check_PROGRAMS = daemon_utils_tests +TESTS = daemon_utils_tests + +daemon_utils_tests_SOURCES = dummy.c +daemon_utils_tests_CPPFLAGS = \ + -I. \ + -I$(top_builddir) \ + -I$(shell $(OCAMLC) -where) \ + -I$(top_srcdir)/lib +daemon_utils_tests_BOBJECTS = \ + utils.cmo \ + daemon_utils_tests.cmo +daemon_utils_tests_XOBJECTS = $(daemon_utils_tests_BOBJECTS:.cmo=.cmx) + +if !HAVE_OCAMLOPT +daemon_utils_tests_THEOBJECTS = $(daemon_utils_tests_BOBJECTS) +else +daemon_utils_tests_THEOBJECTS = $(daemon_utils_tests_XOBJECTS) +endif + +OCAMLLINKFLAGS = \ + mlcutils.$(MLARCHIVE) \ + mlstdutils.$(MLARCHIVE) \ + $(LINK_CUSTOM_OCAMLC_ONLY) + +daemon_utils_tests_DEPENDENCIES = \ + $(daemon_utils_tests_THEOBJECTS) \ + $(top_srcdir)/ocaml-link.sh +daemon_utils_tests_LINK = \ + $(top_srcdir)/ocaml-link.sh -- \ + $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLLINKFLAGS) \ + $(OCAMLPACKAGES) \ + $(daemon_utils_tests_THEOBJECTS) -o $@ + +check-valgrind: + $(MAKE) VG="@VG@" check + # OCaml dependencies. depend: .depend diff --git a/daemon/daemon_utils_tests.ml b/daemon/daemon_utils_tests.ml new file mode 100644 index 000000000..892509d89 --- /dev/null +++ b/daemon/daemon_utils_tests.ml @@ -0,0 +1,48 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Unix +open Printf + +open Utils + +(* Test prog_exists. *) +let () + assert (prog_exists "ls"); + assert (prog_exists "true") + +(* Test command, commandr. *) +let () + ignore (command "true" []); + + let r, _, _ = commandr "false" [] in + assert (r = 1) + +(* Test split_device_partition. *) +let () + assert (split_device_partition "/dev/sda1" = ("sda", 1)); + assert (split_device_partition "/dev/sdb" = ("sdb", 0)); + assert (split_device_partition "/dev/ubda9" = ("ubda", 9)); + assert (split_device_partition "/dev/md0p1" = ("md0", 1)) + (* XXX The function is buggy: + assert (split_device_partition "/dev/md0" = ("md0", 0)) *) + +(* Test proc_unmangle_path. *) +let () + assert (proc_unmangle_path "\\040" = " "); + assert (proc_unmangle_path "\\040\\040" = " ") diff --git a/daemon/dummy.c b/daemon/dummy.c new file mode 100644 index 000000000..ebab6198c --- /dev/null +++ b/daemon/dummy.c @@ -0,0 +1,2 @@ +/* Dummy source, to be used for OCaml-based tools with no C sources. */ +enum { foo = 1 }; diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 99de982a6..7e8770a9b 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -94,6 +94,7 @@ daemon/dispatch.c daemon/dmesg.c daemon/dropcaches.c daemon/du.c +daemon/dummy.c daemon/echo-daemon.c daemon/ext2.c daemon/fallocate.c -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 20/41] daemon: Reimplement ‘is_dir’, ‘is_file’ and ‘is_symlink’ APIs in OCaml.
This also demonstrates usage of optional arguments. --- daemon/Makefile.am | 2 ++ daemon/is.c | 41 ----------------------------------------- daemon/is.ml | 44 ++++++++++++++++++++++++++++++++++++++++++++ daemon/is.mli | 21 +++++++++++++++++++++ generator/actions_core.ml | 3 +++ generator/daemon.ml | 7 ++++--- 6 files changed, 74 insertions(+), 44 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 306f371a1..12f1c8c5c 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -263,6 +263,7 @@ SOURCES_MLI = \ sysroot.mli \ devsparts.mli \ file.mli \ + is.mli \ mountable.mli \ utils.mli @@ -275,6 +276,7 @@ SOURCES_ML = \ blkid.ml \ devsparts.ml \ file.ml \ + is.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/is.c b/daemon/is.c index 4d5e911c2..a91dab32b 100644 --- a/daemon/is.c +++ b/daemon/is.c @@ -39,36 +39,6 @@ do_exists (const char *path) /* Takes optional arguments, consult optargs_bitmask. */ int -do_is_file (const char *path, int followsymlinks) -{ - mode_t mode; - int r; - - if (!(optargs_bitmask & GUESTFS_IS_FILE_FOLLOWSYMLINKS_BITMASK)) - followsymlinks = 0; - - r = get_mode (path, &mode, followsymlinks); - if (r <= 0) return r; - return S_ISREG (mode); -} - -/* Takes optional arguments, consult optargs_bitmask. */ -int -do_is_dir (const char *path, int followsymlinks) -{ - mode_t mode; - int r; - - if (!(optargs_bitmask & GUESTFS_IS_DIR_FOLLOWSYMLINKS_BITMASK)) - followsymlinks = 0; - - r = get_mode (path, &mode, followsymlinks); - if (r <= 0) return r; - return S_ISDIR (mode); -} - -/* Takes optional arguments, consult optargs_bitmask. */ -int do_is_chardev (const char *path, int followsymlinks) { mode_t mode; @@ -112,17 +82,6 @@ do_is_fifo (const char *path, int followsymlinks) return S_ISFIFO (mode); } -int -do_is_symlink (const char *path) -{ - mode_t mode; - int r; - - r = get_mode (path, &mode, 0); - if (r <= 0) return r; - return S_ISLNK (mode); -} - /* Takes optional arguments, consult optargs_bitmask. */ int do_is_socket (const char *path, int followsymlinks) diff --git a/daemon/is.ml b/daemon/is.ml new file mode 100644 index 000000000..b99215737 --- /dev/null +++ b/daemon/is.ml @@ -0,0 +1,44 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf +open Unix + +let rec is_file ?(followsymlinks = false) path + let sysroot = Sysroot.sysroot () in + let chroot = Chroot.create sysroot ~name:(sprintf "is_file: %s" path) in + Chroot.f chroot get_kind (path, followsymlinks) = Some S_REG + +and is_dir ?(followsymlinks = false) path + let sysroot = Sysroot.sysroot () in + let chroot = Chroot.create sysroot ~name:(sprintf "is_dir: %s" path) in + Chroot.f chroot get_kind (path, followsymlinks) = Some S_DIR + +and is_symlink path + let sysroot = Sysroot.sysroot () in + let chroot = Chroot.create sysroot ~name:(sprintf "is_symlink: %s" path) in + Chroot.f chroot get_kind (path, false) = Some S_LNK + +and get_kind (path, followsymlinks) + let statfun = if followsymlinks then stat else lstat in + try + let statbuf = statfun path in + Some statbuf.st_kind + with + Unix_error ((ENOENT|ENOTDIR), _, _) -> + None (* File doesn't exist => return None *) diff --git a/daemon/is.mli b/daemon/is.mli new file mode 100644 index 000000000..20622c39f --- /dev/null +++ b/daemon/is.mli @@ -0,0 +1,21 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 is_file : ?followsymlinks:bool -> string -> bool +val is_dir : ?followsymlinks:bool -> string -> bool +val is_symlink : string -> bool diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 94391288f..421f3ac6b 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -2114,6 +2114,7 @@ See also C<guestfs_is_file>, C<guestfs_is_dir>, C<guestfs_stat>." }; { defaults with name = "is_file"; added = (0, 0, 8); style = RBool "fileflag", [String (Pathname, "path")], [OBool "followsymlinks"]; + impl = OCaml "Is.is_file"; once_had_no_optargs = true; tests = [ InitISOFS, Always, TestResultTrue ( @@ -2138,6 +2139,7 @@ See also C<guestfs_stat>." }; { defaults with name = "is_dir"; added = (0, 0, 8); style = RBool "dirflag", [String (Pathname, "path")], [OBool "followsymlinks"]; + impl = OCaml "Is.is_dir"; once_had_no_optargs = true; tests = [ InitISOFS, Always, TestResultFalse ( @@ -6052,6 +6054,7 @@ See also C<guestfs_stat>." }; { defaults with name = "is_symlink"; added = (1, 5, 10); style = RBool "flag", [String (Pathname, "path")], []; + impl = OCaml "Is.is_symlink"; tests = [ InitISOFS, Always, TestResultFalse ( [["is_symlink"; "/directory"]]), []; diff --git a/generator/daemon.ml b/generator/daemon.ml index 3ffe91537..ef6086bfe 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -577,6 +577,7 @@ return_string_list (value retv) List.iter ( fun ({ name = name; style = ret, args, optargs } as f) -> + let uc_name = String.uppercase_ascii name in let ocaml_function match f.impl with | OCaml f -> f @@ -625,8 +626,8 @@ return_string_list (value retv) let uc_n = String.uppercase_ascii n in (* optargs are all passed as [None|Some _] *) - pr " if ((optargs_bitmask & %s_%s_BITMASK) == 0)\n" - f.c_optarg_prefix uc_n; + pr " if ((optargs_bitmask & GUESTFS_%s_%s_BITMASK) == 0)\n" + uc_name uc_n; pr " args[%d] = Val_int (0); /* None */\n" !i; pr " else {\n"; pr " v = "; @@ -651,7 +652,7 @@ return_string_list (value retv) | Bool n -> pr "Val_bool (%s)" n | Int n -> pr "Val_int (%s)" n | Int64 n -> pr "caml_copy_int64 (%s)" n - | String ((PlainString|Device|Dev_or_Path), n) -> + | String ((PlainString|Device|Pathname|Dev_or_Path), n) -> pr "caml_copy_string (%s)" n | String (Mountable, n) -> pr "copy_mountable (%s)" n -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 21/41] daemon: Reimplement ‘readlink’ API in OCaml.
--- daemon/Makefile.am | 2 ++ daemon/link.c | 16 ---------------- daemon/link.ml | 25 +++++++++++++++++++++++++ daemon/link.mli | 19 +++++++++++++++++++ generator/actions_core.ml | 1 + 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 12f1c8c5c..e0c770b76 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -264,6 +264,7 @@ SOURCES_MLI = \ devsparts.mli \ file.mli \ is.mli \ + link.mli \ mountable.mli \ utils.mli @@ -277,6 +278,7 @@ SOURCES_ML = \ devsparts.ml \ file.ml \ is.ml \ + link.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/link.c b/daemon/link.c index 3ce54fa37..dde61a1c2 100644 --- a/daemon/link.c +++ b/daemon/link.c @@ -32,22 +32,6 @@ GUESTFSD_EXT_CMD(str_ln, ln); -char * -do_readlink (const char *path) -{ - char *link; - - CHROOT_IN; - link = areadlink (path); - CHROOT_OUT; - if (link == NULL) { - reply_with_perror ("%s", path); - return NULL; - } - - return link; /* caller frees */ -} - char ** do_internal_readlinklist (const char *path, char *const *names) { diff --git a/daemon/link.ml b/daemon/link.ml new file mode 100644 index 000000000..ba53fd6b5 --- /dev/null +++ b/daemon/link.ml @@ -0,0 +1,25 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf +open Unix + +let readlink path + let sysroot = Sysroot.sysroot () in + let chroot = Chroot.create sysroot ~name:(sprintf "readlink: %s" path) in + Chroot.f chroot readlink path diff --git a/daemon/link.mli b/daemon/link.mli new file mode 100644 index 000000000..6ca0283b4 --- /dev/null +++ b/daemon/link.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 readlink : string -> string diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 421f3ac6b..7d6755fdc 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -4489,6 +4489,7 @@ The I<-f> option removes the link (C<linkname>) if it exists already." }; { defaults with name = "readlink"; added = (1, 0, 66); style = RString (RPlainString, "link"), [String (Pathname, "path")], []; + impl = OCaml "Link.readlink"; shortdesc = "read the target of a symbolic link"; longdesc = "\ This command reads the target of a symbolic link." }; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 22/41] daemon: Reimplement ‘mount’, ‘mount_ro’, ‘mount_options’, ‘mount_vfs’ APIs in OCaml.
Some of the oldest and most core APIs, reimplemented. This also moves the strange ‘mount_vfs_nochroot’ function into btrfs.c. --- daemon/Makefile.am | 2 + daemon/btrfs.c | 43 ++++++++++++++++++++ daemon/daemon.h | 6 --- daemon/mount.c | 99 ----------------------------------------------- daemon/mount.ml | 62 +++++++++++++++++++++++++++++ daemon/mount.mli | 22 +++++++++++ generator/actions_core.ml | 4 ++ generator/daemon.ml | 3 +- 8 files changed, 135 insertions(+), 106 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index e0c770b76..ae31bf356 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -265,6 +265,7 @@ SOURCES_MLI = \ file.mli \ is.mli \ link.mli \ + mount.mli \ mountable.mli \ utils.mli @@ -279,6 +280,7 @@ SOURCES_ML = \ file.ml \ is.ml \ link.ml \ + mount.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/btrfs.c b/daemon/btrfs.c index 5f1e5d1d0..4f52b71e8 100644 --- a/daemon/btrfs.c +++ b/daemon/btrfs.c @@ -37,6 +37,7 @@ GUESTFSD_EXT_CMD(str_btrfs, btrfs); GUESTFSD_EXT_CMD(str_btrfstune, btrfstune); GUESTFSD_EXT_CMD(str_btrfsck, btrfsck); GUESTFSD_EXT_CMD(str_mkfs_btrfs, mkfs.btrfs); +GUESTFSD_EXT_CMD(str_mount, mount); GUESTFSD_EXT_CMD(str_umount, umount); GUESTFSD_EXT_CMD(str_btrfsimage, btrfs-image); @@ -387,6 +388,48 @@ do_btrfs_subvolume_create (const char *dest, const char *qgroupid) return 0; } +static int +mount_vfs_nochroot (const char *options, const char *vfstype, + const mountable_t *mountable, + const char *mp, const char *user_mp) +{ + CLEANUP_FREE char *options_plus = NULL; + const char *device = mountable->device; + if (mountable->type == MOUNTABLE_BTRFSVOL) { + if (options && strlen (options) > 0) { + if (asprintf (&options_plus, "subvol=%s,%s", + mountable->volume, options) == -1) { + reply_with_perror ("asprintf"); + return -1; + } + } + else { + if (asprintf (&options_plus, "subvol=%s", mountable->volume) == -1) { + reply_with_perror ("asprintf"); + return -1; + } + } + } + + CLEANUP_FREE char *error = NULL; + int r; + if (vfstype) + r = command (NULL, &error, + str_mount, "-o", options_plus ? options_plus : options, + "-t", vfstype, device, mp, NULL); + else + r = command (NULL, &error, + str_mount, "-o", options_plus ? options_plus : options, + device, mp, NULL); + if (r == -1) { + reply_with_error ("%s on %s (options: '%s'): %s", + device, user_mp, options, error); + return -1; + } + + return 0; +} + static char * mount (const mountable_t *fs) { diff --git a/daemon/daemon.h b/daemon/daemon.h index 684f9b096..9f4a7f8a6 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -94,12 +94,6 @@ extern void cleanup_free_stringsbuf (void *ptr); #define CLEANUP_FREE_STRINGSBUF #endif -/*-- in mount.c --*/ - -extern int mount_vfs_nochroot (const char *options, const char *vfstype, - const mountable_t *mountable, - const char *mp, const char *user_mp); - /* Growable strings buffer. */ struct stringsbuf { char **argv; diff --git a/daemon/mount.c b/daemon/mount.c index 0ad9626a7..962b86079 100644 --- a/daemon/mount.c +++ b/daemon/mount.c @@ -111,105 +111,6 @@ is_device_mounted (const char *device) return 0; } -/* The "simple mount" call offers no complex options, you can just - * mount a device on a mountpoint. The variations like mount_ro, - * mount_options and mount_vfs let you set progressively more things. - * - * It's tempting to try a direct mount(2) syscall, but that doesn't - * do any autodetection, so we are better off calling out to - * /bin/mount. - */ - -int -do_mount_vfs (const char *options, const char *vfstype, - const mountable_t *mountable, const char *mountpoint) -{ - CLEANUP_FREE char *mp = NULL; - struct stat statbuf; - - ABS_PATH (mountpoint, 0, return -1); - - mp = sysroot_path (mountpoint); - if (!mp) { - reply_with_perror ("malloc"); - return -1; - } - - /* Check the mountpoint exists and is a directory. */ - if (stat (mp, &statbuf) == -1) { - reply_with_perror ("mount: %s", mountpoint); - return -1; - } - if (!S_ISDIR (statbuf.st_mode)) { - reply_with_perror ("mount: %s: mount point is not a directory", mountpoint); - return -1; - } - - return mount_vfs_nochroot (options, vfstype, mountable, mp, mountpoint); -} - -int -mount_vfs_nochroot (const char *options, const char *vfstype, - const mountable_t *mountable, - const char *mp, const char *user_mp) -{ - CLEANUP_FREE char *options_plus = NULL; - const char *device = mountable->device; - if (mountable->type == MOUNTABLE_BTRFSVOL) { - if (options && strlen (options) > 0) { - if (asprintf (&options_plus, "subvol=%s,%s", - mountable->volume, options) == -1) { - reply_with_perror ("asprintf"); - return -1; - } - } - - else { - if (asprintf (&options_plus, "subvol=%s", mountable->volume) == -1) { - reply_with_perror ("asprintf"); - return -1; - } - } - } - - CLEANUP_FREE char *error = NULL; - int r; - if (vfstype) - r = command (NULL, &error, - str_mount, "-o", options_plus ? options_plus : options, - "-t", vfstype, device, mp, NULL); - else - r = command (NULL, &error, - str_mount, "-o", options_plus ? options_plus : options, - device, mp, NULL); - if (r == -1) { - reply_with_error ("%s on %s (options: '%s'): %s", - device, user_mp, options, error); - return -1; - } - - return 0; -} - -int -do_mount (const mountable_t *mountable, const char *mountpoint) -{ - return do_mount_vfs ("", NULL, mountable, mountpoint); -} - -int -do_mount_ro (const mountable_t *mountable, const char *mountpoint) -{ - return do_mount_vfs ("ro", NULL, mountable, mountpoint); -} - -int -do_mount_options (const char *options, const mountable_t *mountable, - const char *mountpoint) -{ - return do_mount_vfs (options, NULL, mountable, mountpoint); -} - /* Takes optional arguments, consult optargs_bitmask. */ int do_umount (const char *pathordevice, diff --git a/daemon/mount.ml b/daemon/mount.ml new file mode 100644 index 000000000..4bb74fb82 --- /dev/null +++ b/daemon/mount.ml @@ -0,0 +1,62 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Mountable +open Utils + +let mount_vfs options vfs mountable mountpoint + let mp = Sysroot.sysroot () // mountpoint in + + (* Check the mountpoint exists and is a directory. *) + if not (is_directory mp) then + failwithf "mount: %s: mount point is not a directory" mountpoint; + + let args = ref [] in + + (* -o options *) + (match options, mountable.m_type with + | (None | Some ""), (MountableDevice | MountablePath) -> () + | Some options, (MountableDevice | MountablePath) -> + push_back args "-o"; + push_back args options + | (None | Some ""), MountableBtrfsVol subvol -> + push_back args "-o"; + push_back args ("subvol=" ^ subvol) + | Some options, MountableBtrfsVol subvol -> + push_back args "-o"; + push_back args ("subvol=" ^ subvol ^ "," ^ options) + ); + + (* -t vfs *) + (match vfs with + | None | Some "" -> () + | Some t -> + push_back args "-t"; + push_back args t + ); + + push_back args mountable.m_device; + push_back args mp; + + ignore (command "mount" !args) + +let mount = mount_vfs None None +let mount_ro = mount_vfs (Some "ro") None +let mount_options options = mount_vfs (Some options) None diff --git a/daemon/mount.mli b/daemon/mount.mli new file mode 100644 index 000000000..e43d97c42 --- /dev/null +++ b/daemon/mount.mli @@ -0,0 +1,22 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 mount : Mountable.t -> string -> unit +val mount_ro : Mountable.t -> string -> unit +val mount_options : string -> Mountable.t -> string -> unit +val mount_vfs : string option -> string option -> Mountable.t -> string -> unit diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 7d6755fdc..f33bc5320 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -1739,6 +1739,7 @@ let daemon_functions = [ { defaults with name = "mount"; added = (0, 0, 3); style = RErr, [String (Mountable, "mountable"); String (PlainString, "mountpoint")], []; + impl = OCaml "Mount.mount"; tests = [ InitEmpty, Always, TestResultString ( [["part_disk"; "/dev/sda"; "mbr"]; @@ -2922,6 +2923,7 @@ If set to true, POSIX ACLs are saved in the output tar. { defaults with name = "mount_ro"; added = (1, 0, 10); style = RErr, [String (Mountable, "mountable"); String (PlainString, "mountpoint")], []; + impl = OCaml "Mount.mount_ro"; tests = [ InitBasicFS, Always, TestLastFail ( [["umount"; "/"; "false"; "false"]; @@ -2941,6 +2943,7 @@ mounts the filesystem with the read-only (I<-o ro>) flag." }; { defaults with name = "mount_options"; added = (1, 0, 10); style = RErr, [String (PlainString, "options"); String (Mountable, "mountable"); String (PlainString, "mountpoint")], []; + impl = OCaml "Mount.mount_options"; shortdesc = "mount a guest disk with mount options"; longdesc = "\ This is the same as the C<guestfs_mount> command, but it @@ -2954,6 +2957,7 @@ the filesystem uses)." }; { defaults with name = "mount_vfs"; added = (1, 0, 10); style = RErr, [String (PlainString, "options"); String (PlainString, "vfstype"); String (Mountable, "mountable"); String (PlainString, "mountpoint")], []; + impl = OCaml "Mount.mount_vfs"; shortdesc = "mount a guest disk with mount options and vfstype"; longdesc = "\ This is the same as the C<guestfs_mount> command, but it diff --git a/generator/daemon.ml b/generator/daemon.ml index ef6086bfe..fd01e5d8a 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -689,7 +689,8 @@ return_string_list (value retv) pr "\n"; (match ret with - | RErr -> assert false + | RErr -> + pr " CAMLreturnT (int, 0);\n" | RInt _ -> pr " CAMLreturnT (int, Int_val (retv));\n" | RInt64 _ -> assert false -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 23/41] daemon: Reimplement ‘part_get_mbr_id’ API in OCaml.
--- daemon/Makefile.am | 2 ++ daemon/parted.c | 42 ------------------------------------ daemon/parted.ml | 55 +++++++++++++++++++++++++++++++++++++++++++++++ daemon/parted.mli | 19 ++++++++++++++++ generator/actions_core.ml | 1 + 5 files changed, 77 insertions(+), 42 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index ae31bf356..7e06daa02 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -267,6 +267,7 @@ SOURCES_MLI = \ link.mli \ mount.mli \ mountable.mli \ + parted.mli \ utils.mli SOURCES_ML = \ @@ -281,6 +282,7 @@ SOURCES_ML = \ is.ml \ link.ml \ mount.ml \ + parted.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/parted.c b/daemon/parted.c index 03e83cb32..a1e5c81cf 100644 --- a/daemon/parted.c +++ b/daemon/parted.c @@ -521,48 +521,6 @@ test_sfdisk_has_part_type (void) return tested; } -/* Currently we use sfdisk for getting and setting the ID byte. In - * future, extend parted to provide this functionality. As a result - * of using sfdisk, this won't work for non-MBR-style partitions, but - * that limitation is noted in the documentation and we can extend it - * later without breaking the ABI. - */ -int -do_part_get_mbr_id (const char *device, int partnum) -{ - if (partnum <= 0) { - reply_with_error ("partition number must be >= 1"); - return -1; - } - - const char *param = test_sfdisk_has_part_type () ? "--part-type" : "--print-id"; - - char partnum_str[16]; - snprintf (partnum_str, sizeof partnum_str, "%d", partnum); - - CLEANUP_FREE char *out = NULL, *err = NULL; - int r; - - udev_settle (); - - r = command (&out, &err, str_sfdisk, param, device, partnum_str, NULL); - if (r == -1) { - reply_with_error ("sfdisk %s: %s", param, err); - return -1; - } - - udev_settle (); - - /* It's printed in hex ... */ - unsigned id; - if (sscanf (out, "%x", &id) != 1) { - reply_with_error ("sfdisk --print-id: cannot parse output: %s", out); - return -1; - } - - return id; -} - int do_part_set_mbr_id (const char *device, int partnum, int idbyte) { diff --git a/daemon/parted.ml b/daemon/parted.ml new file mode 100644 index 000000000..6be41cf66 --- /dev/null +++ b/daemon/parted.ml @@ -0,0 +1,55 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Scanf + +open Std_utils + +open Utils + +(* Test if [sfdisk] is recent enough to have [--part-type], to be used + * instead of [--print-id] and [--change-id]. + *) +let test_sfdisk_has_part_type = lazy ( + let out = command "sfdisk" ["--help"] in + String.find out "--part-type" >= 0 +) + +(* Currently we use sfdisk for getting and setting the ID byte. In + * future, extend parted to provide this functionality. As a result + * of using sfdisk, this won't work for non-MBR-style partitions, but + * that limitation is noted in the documentation and we can extend it + * later without breaking the ABI. + *) +let part_get_mbr_id device partnum + if partnum <= 0 then + failwith "partition number must be >= 1"; + + let param + if Lazy.force test_sfdisk_has_part_type then + "--part-type" + else + "--print-id" in + + udev_settle (); + let out + command "sfdisk" [param; device; string_of_int partnum] in + udev_settle (); + + (* It's printed in hex, possibly with a leading space. *) + sscanf out " %x" identity diff --git a/daemon/parted.mli b/daemon/parted.mli new file mode 100644 index 000000000..33eb6d30d --- /dev/null +++ b/daemon/parted.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 part_get_mbr_id : string -> int -> int diff --git a/generator/actions_core.ml b/generator/actions_core.ml index f33bc5320..4bf0c7b70 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -5513,6 +5513,7 @@ See also C<guestfs_part_set_bootable>." }; { defaults with name = "part_get_mbr_id"; added = (1, 3, 2); style = RInt "idbyte", [String (Device, "device"); Int "partnum"], []; + impl = OCaml "Parted.part_get_mbr_id"; fish_output = Some FishOutputHexadecimal; tests = [ InitEmpty, Always, TestResult ( -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 24/41] daemon: Reimplement ‘case_sensitive_path’ API in OCaml.
--- daemon/Makefile.am | 2 + daemon/realpath.c | 187 ---------------------------------------------- daemon/realpath.ml | 83 ++++++++++++++++++++ daemon/realpath.mli | 19 +++++ generator/actions_core.ml | 1 + 5 files changed, 105 insertions(+), 187 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 7e06daa02..e49d78dd4 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -268,6 +268,7 @@ SOURCES_MLI = \ mount.mli \ mountable.mli \ parted.mli \ + realpath.mli \ utils.mli SOURCES_ML = \ @@ -283,6 +284,7 @@ SOURCES_ML = \ link.ml \ mount.ml \ parted.ml \ + realpath.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/realpath.c b/daemon/realpath.c index 24ab133e2..f9d22d28d 100644 --- a/daemon/realpath.c +++ b/daemon/realpath.c @@ -48,190 +48,3 @@ do_realpath (const char *path) return ret; /* caller frees */ } - -static int find_path_element (int fd_cwd, int is_end, const char *name, char **name_ret); - -char * -do_case_sensitive_path (const char *path) -{ - size_t next; - int fd_cwd, fd2, err, is_end; - char *ret; - - ret = strdup ("/"); - if (ret == NULL) { - reply_with_perror ("strdup"); - return NULL; - } - next = 1; /* next position in 'ret' buffer */ - - /* 'fd_cwd' here is a surrogate for the current working directory, so - * that we don't have to actually call chdir(2). - */ - fd_cwd = open (sysroot, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (fd_cwd == -1) { - reply_with_perror ("%s", sysroot); - goto error; - } - - /* First character is a '/'. Take each subsequent path element - * and follow it. - */ - while (*path) { - char *t; - size_t i, len; - CLEANUP_FREE char *name_in = NULL, *name_out = NULL; - - i = strcspn (path, "/"); - if (i == 0) { - path++; - continue; - } - - if ((i == 1 && path[0] == '.') || - (i == 2 && path[0] == '.' && path[1] == '.')) { - reply_with_error ("path contained . or .. elements"); - goto error; - } - - name_in = strndup (path, i); - if (name_in == NULL) { - reply_with_perror ("strdup"); - goto error; - } - - /* Skip to next element in path (for the next loop iteration). */ - path += i; - is_end = *path == 0; - - /* Read the current directory looking (case insensitively) for - * this element of the path. This replaces 'name' with the - * correct case version. - */ - if (find_path_element (fd_cwd, is_end, name_in, &name_out) == -1) - goto error; - len = strlen (name_out); - - /* Add the real name of this path element to the return value. */ - if (next > 1) - ret[next++] = '/'; - - t = realloc (ret, next+len+1); - if (t == NULL) { - reply_with_perror ("realloc"); - goto error; - } - ret = t; - - strcpy (&ret[next], name_out); - next += len; - - /* Is it a directory? Try going into it. */ - fd2 = openat (fd_cwd, name_out, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - err = errno; - close (fd_cwd); - fd_cwd = fd2; - errno = err; - if (fd_cwd == -1) { - /* Some errors are OK provided we've reached the end of the path. */ - if (is_end && (errno == ENOTDIR || errno == ENOENT)) - break; - - reply_with_perror ("openat: %s", name_out); - goto error; - } - } - - if (fd_cwd >= 0) - close (fd_cwd); - - return ret; /* caller frees */ - - error: - if (fd_cwd >= 0) - close (fd_cwd); - free (ret); - - return NULL; -} - -/* 'fd_cwd' is a file descriptor pointing to an open directory. - * 'name' is the path element to search for. 'is_end' is a flag - * indicating if this is the last path element. - * - * We search the directory looking for a path element that case - * insensitively matches 'name', returning the actual name in '*name_ret'. - * - * If this is successful, return 0. If it fails, reply with an error - * and return -1. - */ -static int -find_path_element (int fd_cwd, int is_end, const char *name, char **name_ret) -{ - int fd2; - DIR *dir; - struct dirent *d; - - fd2 = dup_cloexec (fd_cwd); /* because closedir will close it */ - if (fd2 == -1) { - reply_with_perror ("dup"); - return -1; - } - dir = fdopendir (fd2); - if (dir == NULL) { - reply_with_perror ("opendir"); - close (fd2); - return -1; - } - - for (;;) { - errno = 0; - d = readdir (dir); - if (d == NULL) - break; - if (STRCASEEQ (d->d_name, name)) - break; - } - - if (d == NULL && errno != 0) { - reply_with_perror ("readdir"); - closedir (dir); - return -1; - } - - if (d == NULL && is_end) { - /* Last path element: return it as-is, assuming that the user will - * create a new file or directory (RHBZ#840115). - */ - closedir (dir); - *name_ret = strdup (name); - if (*name_ret == NULL) { - reply_with_perror ("strdup"); - return -1; - } - return 0; - } - - if (d == NULL) { - reply_with_error ("%s: no file or directory found with this name", name); - closedir (dir); - return -1; - } - - *name_ret = strdup (d->d_name); - if (*name_ret == NULL) { - reply_with_perror ("strdup"); - closedir (dir); - return -1; - } - - /* NB: closedir frees the structure associated with 'd', so we must - * do this last. - */ - if (closedir (dir) == -1) { - reply_with_perror ("closedir"); - return -1; - } - - return 0; -} diff --git a/daemon/realpath.ml b/daemon/realpath.ml new file mode 100644 index 000000000..cffe86322 --- /dev/null +++ b/daemon/realpath.ml @@ -0,0 +1,83 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open Std_utils + +(* The infamous case_sensitive_path function, which works around + * the bug in ntfs-3g that all paths are case sensitive even though + * the underlying filesystem is case insensitive. + *) +let rec case_sensitive_path path + let elems = String.nsplit "/" path in + + (* The caller ensures that the first element of [path] is [/], + * and therefore the first element of the split list must be + * empty. + *) + assert (List.length elems > 0); + assert (List.hd elems = ""); + let elems = List.tl elems in + + let sysroot = Sysroot.sysroot () in + let chroot = Chroot.create sysroot + ~name:(sprintf "case_sensitive_path: %s" path) in + + (* Now we iterate down the tree starting at the sysroot. *) + let elems + Chroot.f chroot ( + fun () -> + let rec loop = function + | [] -> [] + | [ "."|".." ] -> + failwithf "path contains \".\" or \"..\" elements" + | "" :: elems -> + (* For compatibility with C implementation, we ignore + * "//" in the middle of the path. + *) + loop elems + | [ file ] -> + (* If it's the final element, it's allowed to be missing. *) + (match find_path_element file with + | None -> [ file ] (* return the original *) + | Some file -> [ file ] + ); + | elem :: elems -> + (match find_path_element elem with + | None -> + failwithf "%s: not found" elem + | Some elem -> + (* This will fail intentionally if not a directory. *) + Unix.chdir elem; + elem :: loop elems + ) + in + loop elems + ) () in + + (* Reconstruct the case sensitive path. *) + "/" ^ String.concat "/" elems + +and find_path_element name + let dir = Sys.readdir "." in + let dir = Array.to_list dir in + let lc_name = String.lowercase_ascii name in + let cmp n = String.lowercase_ascii n = lc_name in + try Some (List.find cmp dir) + with Not_found -> None diff --git a/daemon/realpath.mli b/daemon/realpath.mli new file mode 100644 index 000000000..371e619fc --- /dev/null +++ b/daemon/realpath.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 case_sensitive_path : string -> string diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 4bf0c7b70..54d0a6ca8 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -4797,6 +4797,7 @@ The result list is not sorted. { defaults with name = "case_sensitive_path"; added = (1, 0, 75); style = RString (RPlainString, "rpath"), [String (Pathname, "path")], []; + impl = OCaml "Realpath.case_sensitive_path"; tests = [ InitISOFS, Always, TestResultString ( [["case_sensitive_path"; "/DIRECTORY"]], "/directory"), []; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 25/41] daemon: Reimplement ‘file_architecture’ API in OCaml.
The previously library-side ‘file_architecture’ API is reimplemented in the daemon, in OCaml. There are some significant differences compared to the C implementation: - The C code used libmagic. That is replaced by calling the ‘file’ command (because that is simpler than using the library). - The C code had extra cases to deal with compressed files. This is not necessary since the ‘file’ command supports the ‘-z’ option which transparently looks inside compressed content (this is a consequence of the change above). This commit demonstrates a number of techniques which will be useful for moving inspection code to the daemon: - Moving an API from the C library to the OCaml daemon. - Calling from one OCaml API inside the daemon to another (from ‘Filearch.file_architecture’ to ‘File.file’). This can be done and is done with C daemon APIs but correct reply_with_error handling is more difficult in C. - Use of Str for regular expression matching within the appliance. --- daemon/Makefile.am | 2 + daemon/filearch.ml | 137 +++++++++++++++++ daemon/filearch.mli | 19 +++ docs/C_SOURCE_FILES | 4 +- generator/actions_core.ml | 377 +++++++++++++++++++++++----------------------- generator/proc_nr.ml | 1 + lib/MAX_PROC_NR | 2 +- lib/Makefile.am | 3 +- lib/filearch.c | 362 -------------------------------------------- po/POTFILES | 1 - 10 files changed, 353 insertions(+), 555 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index e49d78dd4..101ad877e 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -263,6 +263,7 @@ SOURCES_MLI = \ sysroot.mli \ devsparts.mli \ file.mli \ + filearch.mli \ is.mli \ link.mli \ mount.mli \ @@ -280,6 +281,7 @@ SOURCES_ML = \ blkid.ml \ devsparts.ml \ file.ml \ + filearch.ml \ is.ml \ link.ml \ mount.ml \ diff --git a/daemon/filearch.ml b/daemon/filearch.ml new file mode 100644 index 000000000..68ddd61ea --- /dev/null +++ b/daemon/filearch.ml @@ -0,0 +1,137 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Unix +open Printf + +open Std_utils + +open Utils + +let re_file_elf + Str.regexp "ELF \\([0-9]+\\)-bit \\(MSB\\|LSB\\).*\\(executable\\|shared object\\|relocatable\\), \\([^,]+\\)," + +let re_file_elf_ppc64 = Str.regexp ".*64.*PowerPC" + +let initrd_binaries = [ + "bin/ls"; + "bin/rm"; + "bin/modprobe"; + "sbin/modprobe"; + "bin/sh"; + "bin/bash"; + "bin/dash"; + "bin/nash"; +] + +let rec file_architecture orig_path + (* Get the output of the "file" command. Note that because this + * is running in the daemon, LANG=C so it's in English. + *) + let magic = File.file orig_path in + file_architecture_of_magic magic orig_path orig_path + +and file_architecture_of_magic magic orig_path path + if Str.string_match re_file_elf magic 0 then ( + let bits = Str.matched_group 1 magic in + let endianness = Str.matched_group 2 magic in + let elf_arch = Str.matched_group 4 magic in + canonical_elf_arch bits endianness elf_arch + ) + else if String.find magic "PE32 executable" >= 0 then + "i386" + else if String.find magic "PE32+ executable" >= 0 then + "x86_64" + else if String.find magic "cpio archive" >= 0 then + cpio_arch magic orig_path path + else + failwithf "unknown architecture: %s" path + +(* Convert output from 'file' command on ELF files to the canonical + * architecture string. Caller must free the result. + *) +and canonical_elf_arch bits endianness elf_arch + let substr s = String.find elf_arch s >= 0 in + if substr "Intel 80386" || substr "Intel 80486" then + "i386" + else if substr "x86-64" || substr "AMD x86-64" then + "x86_64" + else if substr "SPARC32" then + "sparc" + else if substr "SPARC V9" then + "sparc64" + else if substr "IA-64" then + "ia64" + else if Str.string_match re_file_elf_ppc64 elf_arch 0 then ( + match endianness with + | "MSB" -> "ppc64" + | "LSB" -> "ppc64le" + | _ -> failwithf "unknown endianness '%s'" endianness + ) + else if substr "PowerPC" then + "ppc" + else if substr "ARM aarch64" then + "aarch64" + else if substr "ARM" then + "arm" + else if substr "UCB RISC-V" then + sprintf "riscv%s" bits + else if substr "IBM S/390" then ( + match bits with + | "32" -> "s390" + | "64" -> "s390x" + | _ -> failwithf "unknown S/390 bit size: %s" bits + ) + else + elf_arch + +and cpio_arch magic orig_path path + let sysroot = Sysroot.sysroot () in + + let zcat + if String.find magic "gzip" >= 0 then "zcat" + else if String.find magic "bzip2" >= 0 then "bzcat" + else if String.find magic "XZ compressed" >= 0 then "xzcat" + else "cat" in + + let tmpdir = sprintf "/tmp/%s" (String.random8 ()) in + mkdir tmpdir 0o700; + + (* Construct a command to extract named binaries from the initrd file. *) + let cmd + sprintf "cd %s && %s %s | cpio --quiet -id %s" + tmpdir zcat (quote (sysroot // path)) + (String.concat " " (List.map quote initrd_binaries)) in + if verbose () then eprintf "%s\n%!" cmd; + if Sys.command cmd <> 0 then + failwith "cpio command failed"; + + (* See if any of the binaries were present in the output. *) + let rec loop = function + | bin :: bins -> + let bin_path = tmpdir // bin in + if is_regular_file bin_path then ( + let out = command "file" ["-zb"; bin_path] in + file_architecture_of_magic out orig_path bin_path + ) + else + loop bins + | [] -> + failwithf "could not determine architecture of cpio archive: %s" path + in + loop initrd_binaries diff --git a/daemon/filearch.mli b/daemon/filearch.mli new file mode 100644 index 000000000..c4630225b --- /dev/null +++ b/daemon/filearch.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 file_architecture : string -> string diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 7e8770a9b..64518f1ae 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -72,6 +72,7 @@ daemon/blkdiscard.c daemon/blkid.c daemon/blockdev.c daemon/btrfs.c +daemon/caml-stubs.c daemon/cap.c daemon/checksum.c daemon/cleanups.c @@ -82,6 +83,7 @@ daemon/compress.c daemon/copy.c daemon/cpio.c daemon/cpmv.c +daemon/daemon-c.c daemon/daemon.h daemon/dd.c daemon/debug-bmap.c @@ -173,6 +175,7 @@ daemon/stubs.h daemon/swap.c daemon/sync.c daemon/syslinux.c +daemon/sysroot-c.c daemon/tar.c daemon/truncate.c daemon/tsk.c @@ -296,7 +299,6 @@ lib/errors.c lib/event-string.c lib/events.c lib/file.c -lib/filearch.c lib/fuse.c lib/guestfs-internal-actions.h lib/guestfs-internal-all.h diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 54d0a6ca8..bfd96589e 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -183,194 +183,6 @@ making this an unreliable way to test for features. Use C<guestfs_available> or C<guestfs_feature_available> instead." }; { defaults with - name = "file_architecture"; added = (1, 5, 3); - style = RString (RPlainString, "arch"), [String (Pathname, "filename")], []; - tests = [ - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-aarch64-dynamic"]], "aarch64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-armv7-dynamic"]], "arm"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-i586-dynamic"]], "i386"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-ppc64-dynamic"]], "ppc64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-ppc64le-dynamic"]], "ppc64le"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-riscv64-dynamic"]], "riscv64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-s390x-dynamic"]], "s390x"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-sparc-dynamic"]], "sparc"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-win32.exe"]], "i386"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-win64.exe"]], "x86_64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-x86_64-dynamic"]], "x86_64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-aarch64.so"]], "aarch64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-armv7.so"]], "arm"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-i586.so"]], "i386"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-ppc64.so"]], "ppc64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-ppc64le.so"]], "ppc64le"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-riscv64.so"]], "riscv64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-s390x.so"]], "s390x"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-sparc.so"]], "sparc"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-win32.dll"]], "i386"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-win64.dll"]], "x86_64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-x86_64.so"]], "x86_64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/initrd-x86_64.img"]], "x86_64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/initrd-x86_64.img.gz"]], "x86_64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/bin-x86_64-dynamic.gz"]], "x86_64"), []; - InitISOFS, Always, TestResultString ( - [["file_architecture"; "/lib-i586.so.xz"]], "i386"), []; - ]; - shortdesc = "detect the architecture of a binary file"; - longdesc = "\ -This detects the architecture of the binary F<filename>, -and returns it if known. - -Currently defined architectures are: - -=over 4 - -=item \"aarch64\" - -64 bit ARM. - -=item \"arm\" - -32 bit ARM. - -=item \"i386\" - -This string is returned for all 32 bit i386, i486, i586, i686 binaries -irrespective of the precise processor requirements of the binary. - -=item \"ia64\" - -Intel Itanium. - -=item \"ppc\" - -32 bit Power PC. - -=item \"ppc64\" - -64 bit Power PC (big endian). - -=item \"ppc64le\" - -64 bit Power PC (little endian). - -=item \"riscv32\" - -=item \"riscv64\" - -=item \"riscv128\" - -RISC-V 32-, 64- or 128-bit variants. - -=item \"s390\" - -31 bit IBM S/390. - -=item \"s390x\" - -64 bit IBM S/390. - -=item \"sparc\" - -32 bit SPARC. - -=item \"sparc64\" - -64 bit SPARC V9 and above. - -=item \"x86_64\" - -64 bit x86-64. - -=back - -Libguestfs may return other architecture strings in future. - -The function works on at least the following types of files: - -=over 4 - -=item * - -many types of Un*x and Linux binary - -=item * - -many types of Un*x and Linux shared library - -=item * - -Windows Win32 and Win64 binaries - -=item * - -Windows Win32 and Win64 DLLs - -Win32 binaries and DLLs return C<i386>. - -Win64 binaries and DLLs return C<x86_64>. - -=item * - -Linux kernel modules - -=item * - -Linux new-style initrd images - -=item * - -some non-x86 Linux vmlinuz kernels - -=back - -What it can't do currently: - -=over 4 - -=item * - -static libraries (libfoo.a) - -=item * - -Linux old-style initrd as compressed ext2 filesystem (RHEL 3) - -=item * - -x86 Linux vmlinuz kernels - -x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and -compressed code, and are horribly hard to unpack. If you want to find -the architecture of a kernel, use the architecture of the associated -initrd or kernel module(s) instead. - -=back" }; - - { defaults with name = "mountable_device"; added = (1, 33, 15); style = RString (RDevice, "device"), [String (Mountable, "mountable")], []; shortdesc = "extract the device part of a mountable"; @@ -9628,4 +9440,193 @@ wildcards. Please note that this API may fail when used to compress directories with large files, such as the resulting squashfs will be over 3GB big." }; + { defaults with + name = "file_architecture"; added = (1, 5, 3); + style = RString (RPlainString, "arch"), [String (Pathname, "filename")], []; + impl = OCaml "Filearch.file_architecture"; + tests = [ + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-aarch64-dynamic"]], "aarch64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-armv7-dynamic"]], "arm"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-i586-dynamic"]], "i386"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-ppc64-dynamic"]], "ppc64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-ppc64le-dynamic"]], "ppc64le"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-riscv64-dynamic"]], "riscv64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-s390x-dynamic"]], "s390x"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-sparc-dynamic"]], "sparc"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-win32.exe"]], "i386"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-win64.exe"]], "x86_64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-x86_64-dynamic"]], "x86_64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-aarch64.so"]], "aarch64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-armv7.so"]], "arm"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-i586.so"]], "i386"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-ppc64.so"]], "ppc64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-ppc64le.so"]], "ppc64le"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-riscv64.so"]], "riscv64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-s390x.so"]], "s390x"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-sparc.so"]], "sparc"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-win32.dll"]], "i386"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-win64.dll"]], "x86_64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-x86_64.so"]], "x86_64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/initrd-x86_64.img"]], "x86_64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/initrd-x86_64.img.gz"]], "x86_64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/bin-x86_64-dynamic.gz"]], "x86_64"), []; + InitISOFS, Always, TestResultString ( + [["file_architecture"; "/lib-i586.so.xz"]], "i386"), []; + ]; + shortdesc = "detect the architecture of a binary file"; + longdesc = "\ +This detects the architecture of the binary F<filename>, +and returns it if known. + +Currently defined architectures are: + +=over 4 + +=item \"aarch64\" + +64 bit ARM. + +=item \"arm\" + +32 bit ARM. + +=item \"i386\" + +This string is returned for all 32 bit i386, i486, i586, i686 binaries +irrespective of the precise processor requirements of the binary. + +=item \"ia64\" + +Intel Itanium. + +=item \"ppc\" + +32 bit Power PC. + +=item \"ppc64\" + +64 bit Power PC (big endian). + +=item \"ppc64le\" + +64 bit Power PC (little endian). + +=item \"riscv32\" + +=item \"riscv64\" + +=item \"riscv128\" + +RISC-V 32-, 64- or 128-bit variants. + +=item \"s390\" + +31 bit IBM S/390. + +=item \"s390x\" + +64 bit IBM S/390. + +=item \"sparc\" + +32 bit SPARC. + +=item \"sparc64\" + +64 bit SPARC V9 and above. + +=item \"x86_64\" + +64 bit x86-64. + +=back + +Libguestfs may return other architecture strings in future. + +The function works on at least the following types of files: + +=over 4 + +=item * + +many types of Un*x and Linux binary + +=item * + +many types of Un*x and Linux shared library + +=item * + +Windows Win32 and Win64 binaries + +=item * + +Windows Win32 and Win64 DLLs + +Win32 binaries and DLLs return C<i386>. + +Win64 binaries and DLLs return C<x86_64>. + +=item * + +Linux kernel modules + +=item * + +Linux new-style initrd images + +=item * + +some non-x86 Linux vmlinuz kernels + +=back + +What it can't do currently: + +=over 4 + +=item * + +static libraries (libfoo.a) + +=item * + +Linux old-style initrd as compressed ext2 filesystem (RHEL 3) + +=item * + +x86 Linux vmlinuz kernels + +x86 vmlinuz images (bzImage format) consist of a mix of 16-, 32- and +compressed code, and are horribly hard to unpack. If you want to find +the architecture of a kernel, use the architecture of the associated +initrd or kernel module(s) instead. + +=back" }; + ] diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml index c7619638a..1b0feae87 100644 --- a/generator/proc_nr.ml +++ b/generator/proc_nr.ml @@ -482,6 +482,7 @@ let proc_nr = [ 472, "yara_load"; 473, "yara_destroy"; 474, "internal_yara_scan"; +475, "file_architecture"; ] (* End of list. If adding a new entry, add it at the end of the list diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR index 5f3bb9813..7573eff88 100644 --- a/lib/MAX_PROC_NR +++ b/lib/MAX_PROC_NR @@ -1 +1 @@ -474 +475 diff --git a/lib/Makefile.am b/lib/Makefile.am index b1cb39105..c7a9880eb 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -89,7 +89,6 @@ libguestfs_la_SOURCES = \ event-string.c \ events.c \ file.c \ - filearch.c \ fuse.c \ guid.c \ handle.c \ @@ -160,7 +159,7 @@ libguestfs_la_LIBADD = \ ../common/structs/libstructs.la \ ../common/utils/libutils.la \ ../common/cleanups/libcleanups.la \ - $(PCRE_LIBS) $(MAGIC_LIBS) \ + $(PCRE_LIBS) \ $(LIBVIRT_LIBS) $(LIBXML2_LIBS) \ $(SELINUX_LIBS) \ $(YAJL_LIBS) \ diff --git a/lib/filearch.c b/lib/filearch.c deleted file mode 100644 index e1d3daeef..000000000 --- a/lib/filearch.c +++ /dev/null @@ -1,362 +0,0 @@ -/* libguestfs - * Copyright (C) 2010 Red Hat Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; 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 <inttypes.h> -#include <unistd.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <libintl.h> - -#include <magic.h> - -#include "ignore-value.h" - -#include "guestfs.h" -#include "guestfs-internal.h" -#include "guestfs-internal-actions.h" - -# ifdef HAVE_ATTRIBUTE_CLEANUP -# define CLEANUP_MAGIC_T_FREE __attribute__((cleanup(cleanup_magic_t_free))) - -static void -cleanup_magic_t_free (void *ptr) -{ - magic_t m = *(magic_t *) ptr; - - if (m) - magic_close (m); -} - -# else -# define CLEANUP_MAGIC_T_FREE -# endif - -COMPILE_REGEXP (re_file_elf, - "ELF (\\d+)-bit (MSB|LSB).*(?:executable|shared object|relocatable), (.+?),", 0) -COMPILE_REGEXP (re_elf_ppc64, ".*64.*PowerPC", 0) - -/* Convert output from 'file' command on ELF files to the canonical - * architecture string. Caller must free the result. - */ -static char * -canonical_elf_arch (guestfs_h *g, - const char *bits, const char *endianness, - const char *elf_arch) -{ - const char *r; - char *ret; - - if (strstr (elf_arch, "Intel 80386") || - strstr (elf_arch, "Intel 80486")) - r = "i386"; - else if (strstr (elf_arch, "x86-64") || - strstr (elf_arch, "AMD x86-64")) - r = "x86_64"; - else if (strstr (elf_arch, "SPARC32")) - r = "sparc"; - else if (strstr (elf_arch, "SPARC V9")) - r = "sparc64"; - else if (strstr (elf_arch, "IA-64")) - r = "ia64"; - else if (match (g, elf_arch, re_elf_ppc64)) { - if (strstr (endianness, "MSB")) - r = "ppc64"; - else if (strstr (endianness, "LSB")) - r = "ppc64le"; - else { - error (g, "file_architecture: unknown endianness '%s'", endianness); - return NULL; - } - } - else if (strstr (elf_arch, "PowerPC")) - r = "ppc"; - else if (strstr (elf_arch, "ARM aarch64")) - r = "aarch64"; - else if (strstr (elf_arch, "ARM")) - r = "arm"; - else if (strstr (elf_arch, "UCB RISC-V")) { - ret = safe_asprintf (g, "riscv%s", bits); - goto no_strdup; - } - else if (strstr (elf_arch, "IBM S/390")) { - if (STREQ (bits, "32")) - r = "s390"; - else if (STREQ (bits, "64")) - r = "s390x"; - else { - error (g, "file_architecture: unknown S/390 bit size: %s", bits); - return NULL; - } - } - else - r = elf_arch; - - ret = safe_strdup (g, r); - no_strdup: - return ret; -} - -static int -is_regular_file (const char *filename) -{ - struct stat statbuf; - - return lstat (filename, &statbuf) == 0 && S_ISREG (statbuf.st_mode); -} - -static char * -magic_for_file (guestfs_h *g, const char *filename, bool *loading_ok, - bool *matched) -{ - int flags; - CLEANUP_MAGIC_T_FREE magic_t m = NULL; - const char *line; - CLEANUP_FREE char *bits = NULL; - CLEANUP_FREE char *elf_arch = NULL; - CLEANUP_FREE char *endianness = NULL; - - flags = g->verbose ? MAGIC_DEBUG : 0; - flags |= MAGIC_ERROR | MAGIC_RAW; - - if (loading_ok) - *loading_ok = false; - if (matched) - *matched = false; - - m = magic_open (flags); - if (m == NULL) { - perrorf (g, "magic_open"); - return NULL; - } - - if (magic_load (m, NULL) == -1) { - perrorf (g, "magic_load: default magic database file"); - return NULL; - } - - line = magic_file (m, filename); - if (line == NULL) { - perrorf (g, "magic_file: %s", filename); - return NULL; - } - - if (loading_ok) - *loading_ok = true; - - if (!match3 (g, line, re_file_elf, &bits, &endianness, &elf_arch)) { - error (g, "no re_file_elf match in '%s'", line); - return NULL; - } - - if (matched) - *matched = true; - - return canonical_elf_arch (g, bits, endianness, elf_arch); -} - -/* Download and uncompress the cpio file to find binaries within. */ -static const char *initrd_binaries[] = { - "bin/ls", - "bin/rm", - "bin/modprobe", - "sbin/modprobe", - "bin/sh", - "bin/bash", - "bin/dash", - "bin/nash", - NULL -}; - -static char * -cpio_arch (guestfs_h *g, const char *file, const char *path) -{ - CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *dir = NULL; - CLEANUP_FREE char *initrd = NULL; - CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); - char *ret = NULL; - const char *method; - int64_t size; - int r; - size_t i; - - if (asprintf (&dir, "%s/libguestfsXXXXXX", tmpdir) == -1) { - perrorf (g, "asprintf"); - return NULL; - } - - if (strstr (file, "gzip")) - method = "zcat"; - else if (strstr (file, "bzip2")) - method = "bzcat"; - else - method = "cat"; - - /* Security: Refuse to download initrd if it is huge. */ - size = guestfs_filesize (g, path); - if (size == -1 || size > 100000000) { - error (g, _("size of %s unreasonable (%" PRIi64 " bytes)"), - path, size); - goto out; - } - - if (mkdtemp (dir) == NULL) { - perrorf (g, "mkdtemp"); - goto out; - } - - initrd = safe_asprintf (g, "%s/initrd", dir); - if (guestfs_download (g, path, initrd) == -1) - goto out; - - /* Construct a command to extract named binaries from the initrd file. */ - guestfs_int_cmd_add_string_unquoted (cmd, "cd "); - guestfs_int_cmd_add_string_quoted (cmd, dir); - guestfs_int_cmd_add_string_unquoted (cmd, " && "); - guestfs_int_cmd_add_string_unquoted (cmd, method); - guestfs_int_cmd_add_string_unquoted (cmd, " initrd | cpio --quiet -id"); - for (i = 0; initrd_binaries[i] != NULL; ++i) { - guestfs_int_cmd_add_string_unquoted (cmd, " "); - guestfs_int_cmd_add_string_quoted (cmd, initrd_binaries[i]); - } - - r = guestfs_int_cmd_run (cmd); - if (r == -1) - goto out; - if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { - guestfs_int_external_command_failed (g, r, "cpio", path); - goto out; - } - - for (i = 0; initrd_binaries[i] != NULL; ++i) { - CLEANUP_FREE char *bin - safe_asprintf (g, "%s/%s", dir, initrd_binaries[i]); - - if (is_regular_file (bin)) { - bool loading_ok, matched; - - ret = magic_for_file (g, bin, &loading_ok, &matched); - if (!loading_ok || matched) - goto out; - } - } - error (g, "file_architecture: could not determine architecture of cpio archive"); - - out: - guestfs_int_recursive_remove_dir (g, dir); - - return ret; -} - -static char * -compressed_file_arch (guestfs_h *g, const char *path, const char *method) -{ - CLEANUP_FREE char *tmpdir = guestfs_get_tmpdir (g), *dir = NULL; - CLEANUP_FREE char *tempfile = NULL, *tempfile_extracted = NULL; - CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); - char *ret = NULL; - int64_t size; - int r; - bool matched; - - if (asprintf (&dir, "%s/libguestfsXXXXXX", tmpdir) == -1) { - perrorf (g, "asprintf"); - return NULL; - } - - /* Security: Refuse to download file if it is huge. */ - size = guestfs_filesize (g, path); - if (size == -1 || size > 10000000) { - error (g, _("size of %s unreasonable (%" PRIi64 " bytes)"), - path, size); - goto out; - } - - if (mkdtemp (dir) == NULL) { - perrorf (g, "mkdtemp"); - goto out; - } - - tempfile = safe_asprintf (g, "%s/file", dir); - if (guestfs_download (g, path, tempfile) == -1) - goto out; - - tempfile_extracted = safe_asprintf (g, "%s/file_extracted", dir); - - /* Construct a command to extract named binaries from the initrd file. */ - guestfs_int_cmd_add_string_unquoted (cmd, method); - guestfs_int_cmd_add_string_unquoted (cmd, " "); - guestfs_int_cmd_add_string_quoted (cmd, tempfile); - guestfs_int_cmd_add_string_unquoted (cmd, " > "); - guestfs_int_cmd_add_string_quoted (cmd, tempfile_extracted); - - r = guestfs_int_cmd_run (cmd); - if (r == -1) - goto out; - if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { - guestfs_int_external_command_failed (g, r, method, path); - goto out; - } - - ret = magic_for_file (g, tempfile_extracted, NULL, &matched); - if (!matched) - error (g, "file_architecture: could not determine architecture of compressed file"); - - out: - guestfs_int_recursive_remove_dir (g, dir); - - return ret; -} - -char * -guestfs_impl_file_architecture (guestfs_h *g, const char *path) -{ - CLEANUP_FREE char *file = NULL; - CLEANUP_FREE char *bits = NULL; - CLEANUP_FREE char *elf_arch = NULL; - CLEANUP_FREE char *endianness = NULL; - char *ret = NULL; - - /* Get the output of the "file" command. Note that because this - * runs in the daemon, LANG=C so it's in English. - */ - file = guestfs_file (g, path); - if (file == NULL) - return NULL; - - if ((match3 (g, file, re_file_elf, &bits, &endianness, &elf_arch)) != 0) - ret = canonical_elf_arch (g, bits, endianness, elf_arch); - else if (strstr (file, "PE32 executable")) - ret = safe_strdup (g, "i386"); - else if (strstr (file, "PE32+ executable")) - ret = safe_strdup (g, "x86_64"); - else if (strstr (file, "cpio archive")) - ret = cpio_arch (g, file, path); - else if (strstr (file, "gzip compressed data")) - ret = compressed_file_arch (g, path, "zcat"); - else if (strstr (file, "XZ compressed data")) - ret = compressed_file_arch (g, path, "xzcat"); - else - error (g, "file_architecture: unknown architecture: %s", path); - - return ret; /* caller frees */ -} diff --git a/po/POTFILES b/po/POTFILES index 0d8a924b6..1a38e8ed4 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -354,7 +354,6 @@ lib/errors.c lib/event-string.c lib/events.c lib/file.c -lib/filearch.c lib/fuse.c lib/guid.c lib/handle.c -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 26/41] daemon: Reimplement ‘list_ldm_(volumes|partitions)’ APIs in OCaml.
--- daemon/Makefile.am | 2 ++ daemon/ldm.c | 82 ----------------------------------------------- daemon/ldm.ml | 52 ++++++++++++++++++++++++++++++ daemon/ldm.mli | 20 ++++++++++++ generator/actions_core.ml | 2 ++ 5 files changed, 76 insertions(+), 82 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 101ad877e..fde09c34b 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -265,6 +265,7 @@ SOURCES_MLI = \ file.mli \ filearch.mli \ is.mli \ + ldm.mli \ link.mli \ mount.mli \ mountable.mli \ @@ -283,6 +284,7 @@ SOURCES_ML = \ file.ml \ filearch.ml \ is.ml \ + ldm.ml \ link.ml \ mount.ml \ parted.ml \ diff --git a/daemon/ldm.c b/daemon/ldm.c index 75418e8d3..5106e65f9 100644 --- a/daemon/ldm.c +++ b/daemon/ldm.c @@ -23,7 +23,6 @@ #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> -#include <glob.h> #include <string.h> #include <yajl/yajl_tree.h> @@ -47,87 +46,6 @@ optgroup_ldm_available (void) return prog_exists (str_ldmtool); } -static int -glob_errfunc (const char *epath, int eerrno) -{ - fprintf (stderr, "glob: failure reading %s: %s\n", epath, strerror (eerrno)); - return 1; -} - -static char ** -get_devices (const char *pattern) -{ - CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); - glob_t devs; - int err; - size_t i; - - memset (&devs, 0, sizeof devs); - - err = glob (pattern, GLOB_ERR, glob_errfunc, &devs); - if (err == GLOB_NOSPACE) { - reply_with_error ("glob: returned GLOB_NOSPACE: " - "rerun with LIBGUESTFS_DEBUG=1"); - goto error; - } else if (err == GLOB_ABORTED) { - reply_with_error ("glob: returned GLOB_ABORTED: " - "rerun with LIBGUESTFS_DEBUG=1"); - goto error; - } - - for (i = 0; i < devs.gl_pathc; ++i) { - if (add_string (&ret, devs.gl_pathv[i]) == -1) - goto error; - } - - if (end_stringsbuf (&ret) == -1) goto error; - - globfree (&devs); - return take_stringsbuf (&ret); - - error: - globfree (&devs); - - return NULL; -} - -/* All device mapper devices called /dev/mapper/ldm_vol_*. XXX We - * could tighten this up in future if ldmtool had a way to read these - * names back after they have been created. - */ -char ** -do_list_ldm_volumes (void) -{ - struct stat buf; - - /* If /dev/mapper doesn't exist at all, don't give an error. */ - if (stat ("/dev/mapper", &buf) == -1) { - if (errno == ENOENT) - return empty_list (); - reply_with_perror ("/dev/mapper"); - return NULL; - } - - return get_devices ("/dev/mapper/ldm_vol_*"); -} - -/* Same as above but /dev/mapper/ldm_part_*. See comment above. */ -char ** -do_list_ldm_partitions (void) -{ - struct stat buf; - - /* If /dev/mapper doesn't exist at all, don't give an error. */ - if (stat ("/dev/mapper", &buf) == -1) { - if (errno == ENOENT) - return empty_list (); - reply_with_perror ("/dev/mapper"); - return NULL; - } - - return get_devices ("/dev/mapper/ldm_part_*"); -} - int do_ldmtool_create_all (void) { diff --git a/daemon/ldm.ml b/daemon/ldm.ml new file mode 100644 index 000000000..dc7b36f9c --- /dev/null +++ b/daemon/ldm.ml @@ -0,0 +1,52 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Utils + +(* All device mapper devices are called /dev/mapper/ldm_vol_*. XXX We + * could tighten this up in future if ldmtool had a way to read these + * names back after they have been created. + *) +let list_ldm_volumes () + (* If /dev/mapper doesn't exist at all, don't give an error. *) + if not (is_directory "/dev/mapper") then + [] + else ( + let dir = Sys.readdir "/dev/mapper" in + let dir = Array.to_list dir in + let dir + List.filter (fun d -> String.is_prefix d "ldm_vol_") dir in + let dir = List.map ((^) "/dev/mapper/") dir in + List.sort compare dir + ) + +(* Same as above but /dev/mapper/ldm_part_*. *) +let list_ldm_partitions () + (* If /dev/mapper doesn't exist at all, don't give an error. *) + if not (is_directory "/dev/mapper") then + [] + else ( + let dir = Sys.readdir "/dev/mapper" in + let dir = Array.to_list dir in + let dir + List.filter (fun d -> String.is_prefix d "ldm_part_") dir in + let dir = List.map ((^) "/dev/mapper/") dir in + List.sort compare dir + ) diff --git a/daemon/ldm.mli b/daemon/ldm.mli new file mode 100644 index 000000000..789abb0b3 --- /dev/null +++ b/daemon/ldm.mli @@ -0,0 +1,20 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 list_ldm_volumes : unit -> string list +val list_ldm_partitions : unit -> string list diff --git a/generator/actions_core.ml b/generator/actions_core.ml index bfd96589e..331a5feb1 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -8114,6 +8114,7 @@ The capabilities set C<cap> should be passed in text form { defaults with name = "list_ldm_volumes"; added = (1, 20, 0); style = RStringList (RDevice, "devices"), [], []; + impl = OCaml "Ldm.list_ldm_volumes"; optional = Some "ldm"; shortdesc = "list all Windows dynamic disk volumes"; longdesc = "\ @@ -8124,6 +8125,7 @@ device names." }; { defaults with name = "list_ldm_partitions"; added = (1, 20, 0); style = RStringList (RDevice, "devices"), [], []; + impl = OCaml "Ldm.list_ldm_partitions"; optional = Some "ldm"; shortdesc = "list all Windows dynamic disk partitions"; longdesc = "\ -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 27/41] daemon: Reimplement ‘lvs’ API in OCaml.
--- daemon/Makefile.am | 2 + daemon/lvm.c | 151 ---------------------------------------------- daemon/lvm.ml | 92 ++++++++++++++++++++++++++++ daemon/lvm.mli | 19 ++++++ generator/actions_core.ml | 1 + 5 files changed, 114 insertions(+), 151 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index fde09c34b..0525b4d72 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -267,6 +267,7 @@ SOURCES_MLI = \ is.mli \ ldm.mli \ link.mli \ + lvm.mli \ mount.mli \ mountable.mli \ parted.mli \ @@ -286,6 +287,7 @@ SOURCES_ML = \ is.ml \ ldm.ml \ link.ml \ + lvm.ml \ mount.ml \ parted.ml \ realpath.ml \ diff --git a/daemon/lvm.c b/daemon/lvm.c index 5d12b009f..072bf53b4 100644 --- a/daemon/lvm.c +++ b/daemon/lvm.c @@ -103,89 +103,6 @@ convert_lvm_output (char *out, const char *prefix) return take_stringsbuf (&ret); } -/* Filter a colon-separated output of - * lvs -o lv_attr,vg_name,lv_name - * removing thin layouts, and building the device path as we expect it. - * - * This is used only when lvm has no -S. - */ -static char ** -filter_convert_old_lvs_output (char *out) -{ - char *p, *pend; - CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); - - p = out; - while (p) { - size_t len; - char *saveptr; - char *lv_attr, *vg_name, *lv_name; - - pend = strchr (p, '\n'); /* Get the next line of output. */ - if (pend) { - *pend = '\0'; - pend++; - } - - while (*p && c_isspace (*p)) /* Skip any leading whitespace. */ - p++; - - /* Sigh, skip trailing whitespace too. "pvs", I'm looking at you. */ - len = strlen (p)-1; - while (*p && c_isspace (p[len])) - p[len--] = '\0'; - - if (!*p) { /* Empty line? Skip it. */ - skip_line: - p = pend; - continue; - } - - lv_attr = strtok_r (p, ":", &saveptr); - if (!lv_attr) - goto skip_line; - - vg_name = strtok_r (NULL, ":", &saveptr); - if (!vg_name) - goto skip_line; - - lv_name = strtok_r (NULL, ":", &saveptr); - if (!lv_name) - goto skip_line; - - /* Ignore thin layouts (RHBZ#1278878). */ - if (lv_attr[0] == 't') - goto skip_line; - - /* Ignore activationskip (RHBZ#1306666). */ - if (strlen (lv_attr) >= 10 && lv_attr[9] == 'k') - goto skip_line; - - /* Ignore "unknown device" message (RHBZ#1054761). */ - if (STRNEQ (p, "unknown device")) { - char buf[256]; - - snprintf (buf, sizeof buf, "/dev/%s/%s", vg_name, lv_name); - if (add_string (&ret, buf) == -1) { - free (out); - return NULL; - } - } - - p = pend; - } - - free (out); - - if (ret.size > 0) - sort_strings (ret.argv, ret.size); - - if (end_stringsbuf (&ret) == -1) - return NULL; - - return take_stringsbuf (&ret); -} - char ** do_pvs (void) { @@ -222,74 +139,6 @@ do_vgs (void) return convert_lvm_output (out, NULL); } -/* Check whether lvs has -S to filter its output. - * It is available only in lvm2 >= 2.02.107. - */ -static int -test_lvs_has_S_opt (void) -{ - static int result = -1; - if (result != -1) - return result; - - CLEANUP_FREE char *out = NULL; - CLEANUP_FREE char *err = NULL; - - int r = command (&out, &err, str_lvm, "lvs", "--help", NULL); - if (r == -1) { - reply_with_error ("lvm lvs --help: %s", err); - return -1; - } - - if (strstr (out, "-S") == NULL) - result = 0; - else - result = 1; - - return result; -} - -char ** -do_lvs (void) -{ - char *out; - CLEANUP_FREE char *err = NULL; - int r; - const int has_S = test_lvs_has_S_opt (); - - if (has_S < 0) - return NULL; - - if (has_S > 0) { - r = command (&out, &err, - str_lvm, "lvs", - "-o", "vg_name,lv_name", - "-S", "lv_role=public && lv_skip_activation!=yes", - "--noheadings", - "--separator", "/", NULL); - if (r == -1) { - reply_with_error ("%s", err); - free (out); - return NULL; - } - - return convert_lvm_output (out, "/dev/"); - } else { - r = command (&out, &err, - str_lvm, "lvs", - "-o", "lv_attr,vg_name,lv_name", - "--noheadings", - "--separator", ":", NULL); - if (r == -1) { - reply_with_error ("%s", err); - free (out); - return NULL; - } - - return filter_convert_old_lvs_output (out); - } -} - /* These were so complex to implement that I ended up auto-generating * the code. That code is in stubs.c, and it is generated as usual * by generator.ml. diff --git a/daemon/lvm.ml b/daemon/lvm.ml new file mode 100644 index 000000000..55421b628 --- /dev/null +++ b/daemon/lvm.ml @@ -0,0 +1,92 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open Std_utils + +open Utils + +let lvs_has_S_opt = lazy ( + let out = command "lvm" ["lvs"; "--help"] in + String.find out "-S" >= 0 +) + +let rec lvs () + let has_S = Lazy.force lvs_has_S_opt in + if has_S then ( + let out = command "lvm" ["lvs"; + "-o"; "vg_name,lv_name"; + "-S"; "lv_role=public && lv_skip_activation!=yes"; + "--noheadings"; + "--separator"; "/"] in + convert_lvm_output ~prefix:"/dev/" out + ) + else ( + let out = command "lvm" ["lvs"; + "-o"; "lv_attr,vg_name,lv_name"; + "--noheadings"; + "--separator"; ":"] in + filter_convert_old_lvs_output out + ) + +and convert_lvm_output ?(prefix = "") out + let lines = String.nsplit "\n" out in + + (* Skip leading and trailing ("pvs", I'm looking at you) whitespace. *) + let lines = List.map String.trim lines in + + (* Skip empty lines. *) + let lines = List.filter ((<>) "") lines in + + (* Ignore "unknown device" message (RHBZ#1054761). *) + let lines = List.filter ((<>) "unknown device") lines in + + (* Add a prefix? *) + let lines = List.map ((^) prefix) lines in + + (* Sort and return. *) + List.sort compare lines + +(* Filter a colon-separated output of + * lvs -o lv_attr,vg_name,lv_name + * removing thin layouts, and building the device path as we expect it. + * + * This is used only when lvm has no -S. + *) +and filter_convert_old_lvs_output out + let lines = String.nsplit "\n" out in + let lines = List.map String.trim lines in + let lines = List.filter ((<>) "") lines in + let lines = List.filter ((<>) "unknown device") lines in + + let lines = filter_map ( + fun line -> + match String.nsplit ":" line with + | [ lv_attr; vg_name; lv_name ] -> + (* Ignore thin layouts (RHBZ#1278878). *) + if String.length lv_attr > 0 && lv_attr.[0] = 't' then None + (* Ignore activationskip (RHBZ#1306666). *) + else if String.length lv_attr > 9 && lv_attr.[9] = 'k' then None + else + Some (sprintf "/dev/%s/%s" vg_name lv_name) + | _ -> + None + ) lines in + + List.sort compare lines diff --git a/daemon/lvm.mli b/daemon/lvm.mli new file mode 100644 index 000000000..f254728cb --- /dev/null +++ b/daemon/lvm.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 lvs : unit -> string list diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 331a5feb1..f6f006eee 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -1732,6 +1732,7 @@ See also C<guestfs_vgs_full>." }; { defaults with name = "lvs"; added = (0, 0, 4); style = RStringList (RDevice, "logvols"), [], []; + impl = OCaml "Lvm.lvs"; optional = Some "lvm2"; tests = [ InitBasicFSonLVM, Always, TestResult ( -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 28/41] daemon: Reimplement ‘list_md_devices’ API in OCaml.
--- daemon/Makefile.am | 2 + daemon/md.c | 125 ++++++++++++---------------------------------- daemon/md.ml | 48 ++++++++++++++++++ daemon/md.mli | 19 +++++++ generator/actions_core.ml | 1 + 5 files changed, 101 insertions(+), 94 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 0525b4d72..0c5389557 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -268,6 +268,7 @@ SOURCES_MLI = \ ldm.mli \ link.mli \ lvm.mli \ + md.mli \ mount.mli \ mountable.mli \ parted.mli \ @@ -288,6 +289,7 @@ SOURCES_ML = \ ldm.ml \ link.ml \ lvm.ml \ + md.ml \ mount.ml \ parted.ml \ realpath.ml \ diff --git a/daemon/md.c b/daemon/md.c index 64d98fae5..5c9ecd136 100644 --- a/daemon/md.c +++ b/daemon/md.c @@ -24,7 +24,6 @@ #include <inttypes.h> #include <unistd.h> #include <fcntl.h> -#include <glob.h> #ifdef HAVE_LINUX_RAID_MD_U_H #include <sys/ioctl.h> @@ -32,6 +31,8 @@ #include <linux/raid/md_u.h> #endif /* HAVE_LINUX_RAID_MD_U_H */ +#include <caml/mlvalues.h> + #include "daemon.h" #include "actions.h" #include "optgroups.h" @@ -45,6 +46,35 @@ optgroup_mdadm_available (void) return prog_exists (str_mdadm); } +/* Check if 'dev' is a real RAID device, because in the case where md + * is linked directly into the kernel (not a module), /dev/md0 is + * sometimes created. This is called from OCaml function + * Md.list_md_devices. + */ +extern value guestfs_int_daemon_is_raid_device (value devicev); + +/* NB: This is a "noalloc" call. */ +value +guestfs_int_daemon_is_raid_device (value devv) +{ + const char *dev = String_val (devv); + int ret = 1; + +#if defined(HAVE_LINUX_RAID_MD_U_H) && defined(GET_ARRAY_INFO) + int fd; + mdu_array_info_t array; + + fd = open (dev, O_RDONLY); + if (fd >= 0) { + if (ioctl (fd, GET_ARRAY_INFO, &array) == -1 && errno == ENODEV) + ret = 0; + close (fd); + } +#endif + + return Val_bool (ret); +} + static size_t count_bits (uint64_t bitmap) { @@ -188,99 +218,6 @@ do_md_create (const char *name, char *const *devices, #pragma GCC diagnostic pop #endif -static int -glob_errfunc (const char *epath, int eerrno) -{ - fprintf (stderr, "glob: failure reading %s: %s\n", epath, strerror (eerrno)); - return 1; -} - -/* Check if 'dev' is a real RAID device, because in the case where md - * is linked directly into the kernel (not a module), /dev/md0 is - * sometimes created. - */ -static int -is_raid_device (const char *dev) -{ - int ret = 1; - -#if defined(HAVE_LINUX_RAID_MD_U_H) && defined(GET_ARRAY_INFO) - int fd; - mdu_array_info_t array; - - fd = open (dev, O_RDONLY); - if (fd >= 0) { - if (ioctl (fd, GET_ARRAY_INFO, &array) == -1 && errno == ENODEV) - ret = 0; - close (fd); - } -#endif - - return ret; -} - -char ** -do_list_md_devices (void) -{ - CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); - glob_t mds; - - memset (&mds, 0, sizeof mds); - -#define PREFIX "/sys/block/md" -#define SUFFIX "/md" - - /* Look for directories under /sys/block matching md[0-9]* - * As an additional check, we also make sure they have a md subdirectory. - */ - const int err = glob (PREFIX "[0-9]*" SUFFIX, GLOB_ERR, glob_errfunc, &mds); - if (err == GLOB_NOSPACE) { - reply_with_error ("glob: returned GLOB_NOSPACE: " - "rerun with LIBGUESTFS_DEBUG=1"); - goto error; - } else if (err == GLOB_ABORTED) { - reply_with_error ("glob: returned GLOB_ABORTED: " - "rerun with LIBGUESTFS_DEBUG=1"); - goto error; - } - - for (size_t i = 0; i < mds.gl_pathc; i++) { - size_t len; - char *dev, *n; - - len = strlen (mds.gl_pathv[i]) - strlen (PREFIX) - strlen (SUFFIX); - -#define DEV "/dev/md" - dev = malloc (strlen (DEV) + len + 1); - if (NULL == dev) { - reply_with_perror ("malloc"); - goto error; - } - - n = dev; - n = mempcpy (n, DEV, strlen (DEV)); - n = mempcpy (n, &mds.gl_pathv[i][strlen (PREFIX)], len); - *n = '\0'; - - if (!is_raid_device (dev)) { - free (dev); - continue; - } - - if (add_string_nodup (&ret, dev) == -1) goto error; - } - - if (end_stringsbuf (&ret) == -1) goto error; - globfree (&mds); - - return take_stringsbuf (&ret); - - error: - globfree (&mds); - - return NULL; -} - char ** do_md_detail (const char *md) { diff --git a/daemon/md.ml b/daemon/md.ml new file mode 100644 index 000000000..caf87cf8f --- /dev/null +++ b/daemon/md.ml @@ -0,0 +1,48 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open Std_utils + +open Utils + +external is_raid_device : string -> bool + "guestfs_int_daemon_is_raid_device" "noalloc" + +let re_md = Str.regexp "^md[0-9]+$" + +let list_md_devices () + (* Look for directories under /sys/block matching md[0-9]+ + * As an additional check, we also make sure they have a md subdirectory. + *) + let devs = Sys.readdir "/sys/block" in + let devs = Array.to_list devs in + let devs = List.filter (fun d -> Str.string_match re_md d 0) devs in + let devs = List.filter ( + fun d -> is_directory (sprintf "/sys/block/%s/md" d) + ) devs in + + (* Construct the equivalent /dev/md[0-9]+ device names. *) + let devs = List.map ((^) "/dev/") devs in + + (* Check they are really RAID devices. *) + let devs = List.filter is_raid_device devs in + + (* Return the list sorted. *) + sort_device_names devs diff --git a/daemon/md.mli b/daemon/md.mli new file mode 100644 index 000000000..56b6ea65e --- /dev/null +++ b/daemon/md.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 list_md_devices : unit -> string list diff --git a/generator/actions_core.ml b/generator/actions_core.ml index f6f006eee..140ba6c1b 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -6632,6 +6632,7 @@ If not set, this defaults to C<raid1>. { defaults with name = "list_md_devices"; added = (1, 15, 4); style = RStringList (RDevice, "devices"), [], []; + impl = OCaml "Md.list_md_devices"; shortdesc = "list Linux md (RAID) devices"; longdesc = "\ List all Linux md devices." }; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 29/41] daemon: Generate OCaml wrappers for optgroup_*_available functions.
It is sometimes useful to be able to call these from OCaml code. --- generator/daemon.ml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/generator/daemon.ml b/generator/daemon.ml index fd01e5d8a..1d7461f8c 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -976,6 +976,10 @@ let generate_daemon_optgroups_c () generate_header CStyle GPLv2plus; pr "#include <config.h>\n"; + pr "#include <stdio.h>\n"; + pr "#include <stdlib.h>\n"; + pr "\n"; + pr "#include <caml/mlvalues.h>\n"; pr "\n"; pr "#include \"daemon.h\"\n"; pr "#include \"optgroups.h\"\n"; @@ -999,7 +1003,24 @@ let generate_daemon_optgroups_c () pr " { \"%s\", optgroup_%s_available },\n" group group ) optgroups_names_all; pr " { NULL, NULL }\n"; - pr "};\n" + pr "};\n"; + pr "\n"; + pr "/* Wrappers so these functions can be called from OCaml code. */\n"; + List.iter ( + fun group -> + if not (List.mem group optgroups_retired) then ( + pr "extern value guestfs_int_daemon_optgroup_%s_available (value);\n" + group; + pr "\n"; + pr "/* NB: This is a \"noalloc\" call. */\n"; + pr "value\n"; + pr "guestfs_int_daemon_optgroup_%s_available (value unitv)\n" group; + pr "{\n"; + pr " return Val_bool (optgroup_%s_available ());\n" group; + pr "}\n"; + pr "\n" + ) + ) optgroups_names_all let generate_daemon_optgroups_h () generate_header CStyle GPLv2plus; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 30/41] daemon: Enable RStruct, RStructList for OCaml-implemented APIs.
--- .gitignore | 1 + daemon/Makefile.am | 1 + generator/OCaml.ml | 8 ++++ generator/OCaml.mli | 1 + generator/daemon.ml | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++- generator/main.ml | 2 + 6 files changed, 127 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 6fceba555..bca927afc 100644 --- a/.gitignore +++ b/.gitignore @@ -183,6 +183,7 @@ Makefile.in /daemon/stamp-guestfsd.pod /daemon/structs-cleanups.c /daemon/structs-cleanups.h +/daemon/structs.ml /daemon/stubs-?.c /daemon/stubs.h /daemon/types.ml diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 0c5389557..02afabcab 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -278,6 +278,7 @@ SOURCES_MLI = \ SOURCES_ML = \ types.ml \ utils.ml \ + structs.ml \ sysroot.ml \ mountable.ml \ chroot.ml \ diff --git a/generator/OCaml.ml b/generator/OCaml.ml index d3929475a..d05bb495b 100644 --- a/generator/OCaml.ml +++ b/generator/OCaml.ml @@ -888,3 +888,11 @@ and generate_ocaml_function_type ?(extra_unit = false) (ret, args, optargs) | RStructList (_, typ) -> pr "%s array" typ | RHashtable _ -> pr "(string * string) list" ) + +(* Structure definitions (again). These are used in the daemon, + * but it's convenient to generate them here. + *) +and generate_ocaml_daemon_structs () + generate_header OCamlStyle GPLv2plus; + + generate_ocaml_structure_decls () diff --git a/generator/OCaml.mli b/generator/OCaml.mli index 4e79a5b5a..a36fbe02f 100644 --- a/generator/OCaml.mli +++ b/generator/OCaml.mli @@ -20,3 +20,4 @@ val generate_ocaml_c : unit -> unit val generate_ocaml_c_errnos : unit -> unit val generate_ocaml_ml : unit -> unit val generate_ocaml_mli : unit -> unit +val generate_ocaml_daemon_structs : unit -> unit diff --git a/generator/daemon.ml b/generator/daemon.ml index 1d7461f8c..8cac5ccb1 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -575,6 +575,110 @@ return_string_list (value retv) "; + (* Implement code for returning structs and struct lists. *) + let emit_return_struct typ + let struc = Structs.lookup_struct typ in + pr "/* Implement RStruct (%S, _). */\n" typ; + pr "static guestfs_int_%s *\n" typ; + pr "return_%s (value retv)\n" typ; + pr "{\n"; + pr " guestfs_int_%s *ret;\n" typ; + pr " value v;\n"; + pr "\n"; + pr " ret = malloc (sizeof (*ret));\n"; + pr " if (ret == NULL) {\n"; + pr " reply_with_perror (\"malloc\");\n"; + pr " return NULL;\n"; + pr " }\n"; + pr "\n"; + iteri ( + fun i -> + pr " v = Field (retv, %d);\n" i; + function + | n, (FString|FUUID) -> + pr " ret->%s = strdup (String_val (v));\n" n; + pr " if (ret->%s == NULL) return NULL;\n" n + | n, FBuffer -> + pr " ret->%s_len = caml_string_length (v);\n" n; + pr " ret->%s = strdup (String_val (v));\n" n; + pr " if (ret->%s == NULL) return NULL;\n" n + | n, (FBytes|FInt64|FUInt64) -> + pr " ret->%s = Int64_val (v);\n" n + | n, (FInt32|FUInt32) -> + pr " ret->%s = Int32_val (v);\n" n + | n, FOptPercent -> + pr " if (v == Val_int (0)) /* None */\n"; + pr " ret->%s = -1;\n" n; + pr " else {\n"; + pr " v = Field (v, 0);\n"; + pr " ret->%s = Double_val (v);\n" n; + pr " }\n" + | n, FChar -> + pr " ret->%s = Int_val (v);\n" n + ) struc.s_cols; + pr "\n"; + pr " return ret;\n"; + pr "}\n"; + pr "\n" + + and emit_return_struct_list typ + pr "/* Implement RStructList (%S, _). */\n" typ; + pr "static guestfs_int_%s_list *\n" typ; + pr "return_%s_list (value retv)\n" typ; + pr "{\n"; + pr " guestfs_int_%s_list *ret;\n" typ; + pr " guestfs_int_%s *r;\n" typ; + pr " size_t i, len;\n"; + pr " value v, rv;\n"; + pr "\n"; + pr " /* Count the number of elements in the list. */\n"; + pr " rv = retv;\n"; + pr " len = 0;\n"; + pr " while (rv != Val_int (0)) {\n"; + pr " len++;\n"; + pr " rv = Field (rv, 1);\n"; + pr " }\n"; + pr "\n"; + pr " ret = malloc (sizeof *ret);\n"; + pr " if (ret == NULL) {\n"; + pr " reply_with_perror (\"malloc\");\n"; + pr " return NULL;\n"; + pr " }\n"; + pr " ret->guestfs_int_%s_list_len = len;\n" typ; + pr " ret->guestfs_int_%s_list_val =\n" typ; + pr " calloc (len, sizeof (guestfs_int_%s));\n" typ; + pr " if (ret->guestfs_int_%s_list_val == NULL) {\n" typ; + pr " reply_with_perror (\"calloc\");\n"; + pr " free (ret);\n"; + pr " return NULL;\n"; + pr " }\n"; + pr "\n"; + pr " rv = retv;\n"; + pr " for (i = 0; i < len; ++i) {\n"; + pr " v = Field (rv, 0);\n"; + pr " r = return_%s (v);\n" typ; + pr " if (r == NULL)\n"; + pr " return NULL; /* XXX leaks memory along this error path */\n"; + pr " memcpy (&ret->guestfs_int_%s_list_val[i], r, sizeof (*r));\n" typ; + pr " free (r);\n"; + pr " rv = Field (rv, 1);\n"; + pr " }\n"; + pr "\n"; + pr " return ret;\n"; + pr "}\n"; + pr "\n"; + in + + List.iter ( + function + | typ, RStructOnly -> + emit_return_struct typ + | typ, (RStructListOnly | RStructAndList) -> + emit_return_struct typ; + emit_return_struct_list typ + ) (rstructs_used_by (actions |> impl_ocaml_functions)); + + (* Implement the wrapper functions. *) List.iter ( fun ({ name = name; style = ret, args, optargs } as f) -> let uc_name = String.uppercase_ascii name in @@ -709,8 +813,16 @@ return_string_list (value retv) | RStringList _ -> pr " char **ret = return_string_list (retv);\n"; pr " CAMLreturnT (char **, ret); /* caller frees */\n" - | RStruct _ -> assert false - | RStructList _ -> assert false + | RStruct (_, typ) -> + pr " guestfs_int_%s *ret =\n" typ; + pr " return_%s (retv);\n" typ; + pr " /* caller frees */\n"; + pr " CAMLreturnT (guestfs_int_%s *, ret);\n" typ + | RStructList (_, typ) -> + pr " guestfs_int_%s_list *ret =\n" typ; + pr " return_%s_list (retv);\n" typ; + pr " /* caller frees */\n"; + pr " CAMLreturnT (guestfs_int_%s_list *, ret);\n" typ | RHashtable _ -> assert false | RBufferOut _ -> assert false ); diff --git a/generator/main.ml b/generator/main.ml index a6c805e2e..72f704b8e 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -191,6 +191,8 @@ Run it from the top source directory using the command OCaml.generate_ocaml_c; output_to "ocaml/guestfs-c-errnos.c" OCaml.generate_ocaml_c_errnos; + output_to "daemon/structs.ml" + OCaml.generate_ocaml_daemon_structs; output_to "ocaml/bindtests.ml" Bindtests.generate_ocaml_bindtests; -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 31/41] daemon: Reimplement ‘btrfs_subvolume_list’ and ‘btrfs_subvolume_get_default’ in OCaml.
--- daemon/Makefile.am | 2 + daemon/btrfs.c | 175 ---------------------------------------------- daemon/btrfs.ml | 127 +++++++++++++++++++++++++++++++++ daemon/btrfs.mli | 26 +++++++ generator/actions_core.ml | 2 + generator/daemon.ml | 5 +- 6 files changed, 160 insertions(+), 177 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 02afabcab..439c71bd3 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -259,6 +259,7 @@ guestfsd_CFLAGS = \ # https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html SOURCES_MLI = \ blkid.mli \ + btrfs.mli \ chroot.mli \ sysroot.mli \ devsparts.mli \ @@ -283,6 +284,7 @@ SOURCES_ML = \ mountable.ml \ chroot.ml \ blkid.ml \ + btrfs.ml \ devsparts.ml \ file.ml \ filearch.ml \ diff --git a/daemon/btrfs.c b/daemon/btrfs.c index 4f52b71e8..d9043d53c 100644 --- a/daemon/btrfs.c +++ b/daemon/btrfs.c @@ -41,11 +41,6 @@ GUESTFSD_EXT_CMD(str_mount, mount); GUESTFSD_EXT_CMD(str_umount, umount); GUESTFSD_EXT_CMD(str_btrfsimage, btrfs-image); -COMPILE_REGEXP (re_btrfs_subvolume_list, - "ID\\s+(\\d+).*\\s" - "top level\\s+(\\d+).*\\s" - "path\\s(.*)", - 0) COMPILE_REGEXP (re_btrfs_balance_status, "Balance on '.*' is (.*)", 0) int @@ -483,137 +478,6 @@ umount (char *fs_buf, const mountable_t *fs) return 0; } -guestfs_int_btrfssubvolume_list * -do_btrfs_subvolume_list (const mountable_t *fs) -{ - CLEANUP_FREE_STRING_LIST char **lines = NULL; - size_t i = 0; - const size_t MAX_ARGS = 64; - const char *argv[MAX_ARGS]; - - /* Execute 'btrfs subvolume list <fs>', and split the output into lines */ - { - char *fs_buf = mount (fs); - - if (!fs_buf) - return NULL; - - ADD_ARG (argv, i, str_btrfs); - ADD_ARG (argv, i, "subvolume"); - ADD_ARG (argv, i, "list"); - ADD_ARG (argv, i, fs_buf); - ADD_ARG (argv, i, NULL); - - CLEANUP_FREE char *out = NULL, *errout = NULL; - int r = commandv (&out, &errout, argv); - - if (umount (fs_buf, fs) != 0) - return NULL; - - if (r == -1) { - CLEANUP_FREE char *fs_desc = mountable_to_string (fs); - if (fs_desc == NULL) { - fprintf (stderr, "malloc: %m"); - } - reply_with_error ("%s: %s", fs_desc ? fs_desc : "malloc", errout); - return NULL; - } - - lines = split_lines (out); - if (!lines) return NULL; - } - - /* Output is: - * - * ID 256 gen 30 top level 5 path test1 - * ID 257 gen 30 top level 5 path dir/test2 - * ID 258 gen 30 top level 5 path test3 - * - * "ID <n>" is the subvolume ID. - * "gen <n>" is the generation when the root was created or last - * updated. - * "top level <n>" is the top level subvolume ID. - * "path <str>" is the subvolume path, relative to the top of the - * filesystem. - * - * Note that the order that each of the above is fixed, but - * different versions of btrfs may display different sets of data. - * Specifically, older versions of btrfs do not display gen. - */ - - guestfs_int_btrfssubvolume_list *ret = NULL; - - const size_t nr_subvolumes = guestfs_int_count_strings (lines); - - ret = malloc (sizeof *ret); - if (!ret) { - reply_with_perror ("malloc"); - return NULL; - } - - ret->guestfs_int_btrfssubvolume_list_len = nr_subvolumes; - ret->guestfs_int_btrfssubvolume_list_val - calloc (nr_subvolumes, sizeof (struct guestfs_int_btrfssubvolume)); - if (ret->guestfs_int_btrfssubvolume_list_val == NULL) { - reply_with_perror ("calloc"); - goto error; - } - - for (i = 0; i < nr_subvolumes; ++i) { - /* To avoid allocations, reuse the 'line' buffer to store the - * path. Thus we don't need to free 'line', since it will be - * freed by the calling (XDR) code. - */ - char *line = lines[i]; -#define N_MATCHES 4 - int ovector[N_MATCHES * 3]; - - if (pcre_exec (re_btrfs_subvolume_list, NULL, line, strlen (line), 0, 0, - ovector, N_MATCHES * 3) < 0) -#undef N_MATCHES - { - unexpected_output: - reply_with_error ("unexpected output from 'btrfs subvolume list' command: %s", line); - goto error; - } - - struct guestfs_int_btrfssubvolume *this - &ret->guestfs_int_btrfssubvolume_list_val[i]; - -#if __WORDSIZE == 64 -#define XSTRTOU64 xstrtoul -#else -#define XSTRTOU64 xstrtoull -#endif - - if (XSTRTOU64 (line + ovector[2], NULL, 10, - &this->btrfssubvolume_id, NULL) != LONGINT_OK) - goto unexpected_output; - if (XSTRTOU64 (line + ovector[4], NULL, 10, - &this->btrfssubvolume_top_level_id, NULL) != LONGINT_OK) - goto unexpected_output; - -#undef XSTRTOU64 - - this->btrfssubvolume_path - strndup (line + ovector[6], ovector[7] - ovector[6]); - if (this->btrfssubvolume_path == NULL) - goto error; - } - - return ret; - - error: - if (ret->guestfs_int_btrfssubvolume_list_val) { - for (i = 0; i < nr_subvolumes; ++i) - free (ret->guestfs_int_btrfssubvolume_list_val[i].btrfssubvolume_path); - free (ret->guestfs_int_btrfssubvolume_list_val); - } - free (ret); - - return NULL; -} - int do_btrfs_subvolume_set_default (int64_t id, const char *fs) { @@ -649,45 +513,6 @@ do_btrfs_subvolume_set_default (int64_t id, const char *fs) return 0; } -int64_t -do_btrfs_subvolume_get_default (const mountable_t *fs) -{ - const size_t MAX_ARGS = 64; - const char *argv[MAX_ARGS]; - size_t i = 0; - char *fs_buf = NULL; - CLEANUP_FREE char *err = NULL; - CLEANUP_FREE char *out = NULL; - int r; - int64_t ret = -1; - - fs_buf = mount (fs); - if (fs_buf == NULL) - goto error; - - ADD_ARG (argv, i, str_btrfs); - ADD_ARG (argv, i, "subvolume"); - ADD_ARG (argv, i, "get-default"); - ADD_ARG (argv, i, fs_buf); - ADD_ARG (argv, i, NULL); - - r = commandv (&out, &err, argv); - if (r == -1) { - reply_with_error ("%s: %s", fs_buf, err); - goto error; - } - if (sscanf (out, "ID %" SCNi64, &ret) != 1) { - reply_with_error ("%s: could not parse subvolume id: %s", argv[0], out); - ret = -1; - goto error; - } - - error: - if (fs_buf && umount (fs_buf, fs) != 0) - return -1; - return ret; -} - int do_btrfs_filesystem_sync (const char *fs) { diff --git a/daemon/btrfs.ml b/daemon/btrfs.ml new file mode 100644 index 000000000..554212ccf --- /dev/null +++ b/daemon/btrfs.ml @@ -0,0 +1,127 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf +open Scanf +open Unix + +open Std_utils + +open Mountable +open Utils + +include Structs + +(* In order to examine subvolumes, quota and other things, the btrfs + * filesystem has to be mounted. However we're passed a mountable + * in these cases, so we must mount the filesystem. But we cannot + * mount it under the sysroot, as something else might be mounted + * there so this function mounts the filesystem on a temporary + * directory and ensures it is always unmounted afterwards. + *) +let with_mounted mountable f + let tmpdir = sprintf "/tmp/%s" (String.random8 ()) in + (* This is the cleanup function which is called to unmount and + * remove the temporary directory. This is called on error and + * ordinary exit paths. + *) + let finally () + ignore (Sys.command (sprintf "umount %s" (quote tmpdir))); + rmdir tmpdir + in + + match mountable.m_type with + | MountablePath -> + (* This corner-case happens for Mountable_or_Path parameters, where + * a path was supplied by the caller. The path (the m_device + * field) is relative to the sysroot. + *) + f (Sysroot.sysroot () // mountable.m_device) + + | MountableDevice -> + protect ~finally ~f:( + fun () -> + mkdir tmpdir 0o700; + ignore (command "mount" [mountable.m_device; tmpdir]); + f tmpdir + ) + + | MountableBtrfsVol subvol -> + protect ~finally ~f:( + fun () -> + mkdir tmpdir 0o700; + ignore (command "mount" ["-o"; "subvol=" ^ subvol (* XXX quoting? *); + mountable.m_device; tmpdir]); + f tmpdir + ) + +let re_btrfs_subvolume_list + Str.regexp ("ID[ \t]+\\([0-9]+\\).*[ \t]" ^ + "top level[ \t]+\\([0-9]+\\).*[ \t]" ^ + "path[ \t]+\\(.*\\)") + +let btrfs_subvolume_list mountable + (* Execute 'btrfs subvolume list <fs>', and split the output into lines *) + let lines + with_mounted mountable ( + fun mp -> command "btrfs" ["subvolume"; "list"; mp] + ) in + let lines = String.nsplit "\n" lines in + + (* Output is: + * + * ID 256 gen 30 top level 5 path test1 + * ID 257 gen 30 top level 5 path dir/test2 + * ID 258 gen 30 top level 5 path test3 + * + * "ID <n>" is the subvolume ID. + * "gen <n>" is the generation when the root was created or last + * updated. + * "top level <n>" is the top level subvolume ID. + * "path <str>" is the subvolume path, relative to the top of the + * filesystem. + * + * Note that the order that each of the above is fixed, but + * different versions of btrfs may display different sets of data. + * Specifically, older versions of btrfs do not display gen. + *) + filter_map ( + fun line -> + if line = "" then None + else if Str.string_match re_btrfs_subvolume_list line 0 then ( + let id = Int64.of_string (Str.matched_group 1 line) + and top_level_id = Int64.of_string (Str.matched_group 2 line) + and path = Str.matched_group 3 line in + + Some { + btrfssubvolume_id = id; + btrfssubvolume_top_level_id = top_level_id; + btrfssubvolume_path = path + } + ) + else + failwithf "unexpected output from 'btrfs subvolume list' command: %s" + line + ) lines + +let btrfs_subvolume_get_default mountable + let out + with_mounted mountable ( + fun mp -> command "btrfs" ["subvolume"; "get-default"; mp] + ) in + sscanf out "ID %Ld" identity diff --git a/daemon/btrfs.mli b/daemon/btrfs.mli new file mode 100644 index 000000000..55a38e42d --- /dev/null +++ b/daemon/btrfs.mli @@ -0,0 +1,26 @@ +(* guestfs-inspection + * Copyright (C) 2009-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. + *) + +type btrfssubvolume = { + btrfssubvolume_id : int64; + btrfssubvolume_top_level_id : int64; + btrfssubvolume_path : string; +} + +val btrfs_subvolume_list : Mountable.t -> btrfssubvolume list +val btrfs_subvolume_get_default : Mountable.t -> int64 diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 140ba6c1b..bd3c21d3b 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -7304,6 +7304,7 @@ created subvolume will be added to." }; { defaults with name = "btrfs_subvolume_list"; added = (1, 17, 35); style = RStructList ("subvolumes", "btrfssubvolume"), [String (Mountable_or_Path, "fs")], []; + impl = OCaml "Btrfs.btrfs_subvolume_list"; optional = Some "btrfs"; camel_name = "BTRFSSubvolumeList"; test_excuse = "tested in tests/btrfs"; shortdesc = "list btrfs snapshots and subvolumes"; @@ -8783,6 +8784,7 @@ This uses the L<blockdev(8)> command." }; { defaults with name = "btrfs_subvolume_get_default"; added = (1, 29, 17); style = RInt64 "id", [String (Mountable_or_Path, "fs")], []; + impl = OCaml "Btrfs.btrfs_subvolume_get_default"; optional = Some "btrfs"; camel_name = "BTRFSSubvolumeGetDefault"; tests = [ InitPartition, Always, TestResult ( diff --git a/generator/daemon.ml b/generator/daemon.ml index 8cac5ccb1..83994e9d3 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -758,7 +758,7 @@ return_string_list (value retv) | Int64 n -> pr "caml_copy_int64 (%s)" n | String ((PlainString|Device|Pathname|Dev_or_Path), n) -> pr "caml_copy_string (%s)" n - | String (Mountable, n) -> + | String ((Mountable|Mountable_or_Path), n) -> pr "copy_mountable (%s)" n | String _ -> assert false | OptString _ -> assert false @@ -797,7 +797,8 @@ return_string_list (value retv) pr " CAMLreturnT (int, 0);\n" | RInt _ -> pr " CAMLreturnT (int, Int_val (retv));\n" - | RInt64 _ -> assert false + | RInt64 _ -> + pr " CAMLreturnT (int, Int64_val (retv));\n" | RBool _ -> pr " CAMLreturnT (int, Bool_val (retv));\n" | RConstString _ -> assert false -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 32/41] daemon: Reimplement ‘list_filesystems’ API in the daemon, in OCaml.
Move the list_filesystems API into the daemon, reimplementing it in OCaml. Since this API makes many other API calls, it runs a lot faster in the daemon. --- daemon/Makefile.am | 2 + daemon/ldm.ml | 3 + daemon/ldm.mli | 2 + daemon/listfs.ml | 156 +++++++++++++++++++++++++++++ daemon/listfs.mli | 19 ++++ daemon/lvm.ml | 3 + daemon/lvm.mli | 2 + docs/C_SOURCE_FILES | 1 - generator/actions_core.ml | 75 +++++++------- generator/daemon.ml | 59 ++++++++++- generator/proc_nr.ml | 1 + lib/MAX_PROC_NR | 2 +- lib/Makefile.am | 1 - lib/listfs.c | 246 ---------------------------------------------- 14 files changed, 285 insertions(+), 287 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 439c71bd3..a3c486046 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -268,6 +268,7 @@ SOURCES_MLI = \ is.mli \ ldm.mli \ link.mli \ + listfs.mli \ lvm.mli \ md.mli \ mount.mli \ @@ -295,6 +296,7 @@ SOURCES_ML = \ md.ml \ mount.ml \ parted.ml \ + listfs.ml \ realpath.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/ldm.ml b/daemon/ldm.ml index dc7b36f9c..19cd03e83 100644 --- a/daemon/ldm.ml +++ b/daemon/ldm.ml @@ -20,6 +20,9 @@ open Std_utils open Utils +external available : unit -> bool + "guestfs_int_daemon_optgroup_lvm2_available" "noalloc" + (* All device mapper devices are called /dev/mapper/ldm_vol_*. XXX We * could tighten this up in future if ldmtool had a way to read these * names back after they have been created. diff --git a/daemon/ldm.mli b/daemon/ldm.mli index 789abb0b3..e6edfabd8 100644 --- a/daemon/ldm.mli +++ b/daemon/ldm.mli @@ -16,5 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +val available : unit -> bool + val list_ldm_volumes : unit -> string list val list_ldm_partitions : unit -> string list diff --git a/daemon/listfs.ml b/daemon/listfs.ml new file mode 100644 index 000000000..df5404f81 --- /dev/null +++ b/daemon/listfs.ml @@ -0,0 +1,156 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open Std_utils + +let rec list_filesystems () + let has_lvm2 = Lvm.available () in + let has_ldm = Ldm.available () in + + let devices = Devsparts.list_devices () in + let partitions = Devsparts.list_partitions () in + let mds = Md.list_md_devices () in + + (* Look to see if any devices directly contain filesystems + * (RHBZ#590167). However vfs-type will fail to tell us anything + * useful about devices which just contain partitions, so we also + * get the list of partitions and exclude the corresponding devices + * by using part-to-dev. + *) + let devices = List.fold_left ( + fun devices part -> + let d = Devsparts.part_to_dev part in + List.filter ((<>) d) devices + ) devices partitions in + + (* Use vfs-type to check for filesystems on devices. *) + let ret = filter_map check_with_vfs_type devices in + + (* Use vfs-type to check for filesystems on partitions, but + * ignore MBR partition type 42 used by LDM. + *) + let ret + ret @ + filter_map ( + fun part -> + if not has_ldm || not (is_mbr_partition_type_42 part) then + check_with_vfs_type part + else + None (* ignore type 42 *) + ) partitions in + + (* Use vfs-type to check for filesystems on md devices. *) + let ret = ret @ filter_map check_with_vfs_type mds in + + (* LVM. *) + let ret + if has_lvm2 then ( + let lvs = Lvm.lvs () in + (* Use vfs-type to check for filesystems on LVs. *) + ret @ filter_map check_with_vfs_type lvs + ) + else ret in + + (* LDM. *) + let ret + if has_ldm then ( + let ldmvols = Ldm.list_ldm_volumes () in + let ldmparts = Ldm.list_ldm_partitions () in + (* Use vfs-type to check for filesystems on Windows dynamic disks. *) + ret @ + filter_map check_with_vfs_type ldmvols @ + filter_map check_with_vfs_type ldmparts + ) + else ret in + + List.flatten ret + +(* Use vfs-type to check for a filesystem of some sort of [device]. + * Returns [Some [device, vfs_type; ...]] if found (there may be + * multiple devices found in the case of btrfs), else [None] if nothing + * is found. + *) +and check_with_vfs_type device + let mountable = Mountable.of_device device in + let vfs_type + try Blkid.vfs_type mountable + with exn -> + if verbose () then + eprintf "check_with_vfs_type: %s: %s\n" + device (Printexc.to_string exn); + "" in + + if vfs_type = "" then + Some [mountable, "unknown"] + + (* Ignore all "*_member" strings. In libblkid these are returned + * for things which are members of some RAID or LVM set, most + * importantly "LVM2_member" which is a PV. + *) + else if String.is_suffix vfs_type "_member" then + None + + (* Ignore LUKS-encrypted partitions. These are also containers, as above. *) + else if vfs_type = "crypto_LUKS" then + None + + (* A single btrfs device can turn into many volumes. *) + else if vfs_type = "btrfs" then ( + let vols = Btrfs.btrfs_subvolume_list mountable in + + (* Filter out the default subvolume. You can access that by + * simply mounting the whole device, so we will add the whole + * device at the beginning of the returned list instead. + *) + let default_volume = Btrfs.btrfs_subvolume_get_default mountable in + let vols + List.filter ( + fun { Btrfs.btrfssubvolume_id = id } -> id <> default_volume + ) vols in + + Some ( + (mountable, vfs_type) (* whole device = default volume *) + :: List.map ( + fun { Btrfs.btrfssubvolume_path = path } -> + let mountable = Mountable.of_btrfsvol device path in + (mountable, "btrfs") + ) vols + ) + ) + + else + Some [mountable, vfs_type] + +(* We should ignore partitions that have MBR type byte 0x42, because + * these are members of a Windows dynamic disk group. Trying to read + * them will cause errors (RHBZ#887520). Assuming that libguestfs was + * compiled with ldm support, we'll get the filesystems on these later. + *) +and is_mbr_partition_type_42 partition + try + let partnum = Devsparts.part_to_partnum partition in + let device = Devsparts.part_to_dev partition in + let mbr_id = Parted.part_get_mbr_id device partnum in + mbr_id = 0x42 + with exn -> + if verbose () then + eprintf "is_mbr_partition_type_42: %s: %s\n" + partition (Printexc.to_string exn); + false diff --git a/daemon/listfs.mli b/daemon/listfs.mli new file mode 100644 index 000000000..69958da77 --- /dev/null +++ b/daemon/listfs.mli @@ -0,0 +1,19 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 list_filesystems : unit -> (Mountable.t * string) list diff --git a/daemon/lvm.ml b/daemon/lvm.ml index 55421b628..14f0a8578 100644 --- a/daemon/lvm.ml +++ b/daemon/lvm.ml @@ -22,6 +22,9 @@ open Std_utils open Utils +external available : unit -> bool + "guestfs_int_daemon_optgroup_lvm2_available" "noalloc" + let lvs_has_S_opt = lazy ( let out = command "lvm" ["lvs"; "--help"] in String.find out "-S" >= 0 diff --git a/daemon/lvm.mli b/daemon/lvm.mli index f254728cb..1cf61ecfb 100644 --- a/daemon/lvm.mli +++ b/daemon/lvm.mli @@ -16,4 +16,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +val available : unit -> bool + val lvs : unit -> string list diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 64518f1ae..c01064803 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -323,7 +323,6 @@ lib/launch.c lib/libvirt-auth.c lib/libvirt-domain.c lib/libvirt-is-version.c -lib/listfs.c lib/lpj.c lib/match.c lib/mountable.c diff --git a/generator/actions_core.ml b/generator/actions_core.ml index bd3c21d3b..d5946b3f5 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -209,43 +209,6 @@ If the mountable does not represent a btrfs subvolume, then this function fails and the C<errno> is set to C<EINVAL>." }; { defaults with - name = "list_filesystems"; added = (1, 5, 15); - style = RHashtable (RMountable, RPlainString, "fses"), [], []; - shortdesc = "list filesystems"; - longdesc = "\ -This inspection command looks for filesystems on partitions, -block devices and logical volumes, returning a list of C<mountables> -containing filesystems and their type. - -The return value is a hash, where the keys are the devices -containing filesystems, and the values are the filesystem types. -For example: - - \"/dev/sda1\" => \"ntfs\" - \"/dev/sda2\" => \"ext2\" - \"/dev/vg_guest/lv_root\" => \"ext4\" - \"/dev/vg_guest/lv_swap\" => \"swap\" - -The key is not necessarily a block device. It may also be an opaque -‘mountable’ string which can be passed to C<guestfs_mount>. - -The value can have the special value \"unknown\", meaning the -content of the device is undetermined or empty. -\"swap\" means a Linux swap partition. - -This command runs other libguestfs commands, which might include -C<guestfs_mount> and C<guestfs_umount>, and therefore you should -use this soon after launch and only when nothing is mounted. - -Not all of the filesystems returned will be mountable. In -particular, swap partitions are returned in the list. Also -this command does not check that each filesystem -found is valid and mountable, and some filesystems might -be mountable but require special options. Filesystems may -not all belong to a single logical operating system -(use C<guestfs_inspect_os> to look for OSes)." }; - - { defaults with name = "add_drive"; added = (0, 0, 3); style = RErr, [String (PlainString, "filename")], [OBool "readonly"; OString "format"; OString "iface"; OString "name"; OString "label"; OString "protocol"; OStringList "server"; OString "username"; OString "secret"; OString "cachemode"; OString "discard"; OBool "copyonread"]; once_had_no_optargs = true; @@ -9635,4 +9598,42 @@ initrd or kernel module(s) instead. =back" }; + { defaults with + name = "list_filesystems"; added = (1, 5, 15); + style = RHashtable (RMountable, RPlainString, "fses"), [], []; + impl = OCaml "Listfs.list_filesystems"; + shortdesc = "list filesystems"; + longdesc = "\ +This inspection command looks for filesystems on partitions, +block devices and logical volumes, returning a list of C<mountables> +containing filesystems and their type. + +The return value is a hash, where the keys are the devices +containing filesystems, and the values are the filesystem types. +For example: + + \"/dev/sda1\" => \"ntfs\" + \"/dev/sda2\" => \"ext2\" + \"/dev/vg_guest/lv_root\" => \"ext4\" + \"/dev/vg_guest/lv_swap\" => \"swap\" + +The key is not necessarily a block device. It may also be an opaque +‘mountable’ string which can be passed to C<guestfs_mount>. + +The value can have the special value \"unknown\", meaning the +content of the device is undetermined or empty. +\"swap\" means a Linux swap partition. + +This command runs other libguestfs commands, which might include +C<guestfs_mount> and C<guestfs_umount>, and therefore you should +use this soon after launch and only when nothing is mounted. + +Not all of the filesystems returned will be mountable. In +particular, swap partitions are returned in the list. Also +this command does not check that each filesystem +found is valid and mountable, and some filesystems might +be mountable but require special options. Filesystems may +not all belong to a single logical operating system +(use C<guestfs_inspect_os> to look for OSes)." }; + ] diff --git a/generator/daemon.ml b/generator/daemon.ml index 83994e9d3..66b625388 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -573,6 +573,58 @@ return_string_list (value retv) return take_stringsbuf (&ret); /* caller frees */ } +/* Implement RString (RMountable, _). */ +static char * +return_string_mountable (value retv) +{ + value typev = Field (retv, 0); + value devicev = Field (retv, 1); + value subvolv; + char *ret; + + if (Is_long (typev)) { /* MountableDevice or MountablePath */ + ret = strdup (String_val (devicev)); + if (ret == NULL) + reply_with_perror (\"strdup\"); + return ret; + } + else { /* MountableBtrfsVol of subvol */ + subvolv = Field (typev, 0); + if (asprintf (&ret, \"btrfsvol:%%s/%%s\", + String_val (devicev), String_val (subvolv)) == -1) + reply_with_perror (\"asprintf\"); + return ret; + } +} + +/* Implement RHashtable (RMountable, RPlainString, _). */ +static char ** +return_hashtable_mountable_string (value retv) +{ + CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); + value v, mv, sv; + char *m; + + while (retv != Val_int (0)) { + v = Field (retv, 0); /* (Mountable.t, string) */ + mv = Field (v, 0); /* Mountable.t */ + m = return_string_mountable (mv); + if (m == NULL) + return NULL; + if (add_string_nodup (&ret, m) == -1) + return NULL; + sv = Field (v, 1); /* string */ + if (add_string (&ret, String_val (sv)) == -1) + return NULL; + retv = Field (retv, 1); + } + + if (end_stringsbuf (&ret) == -1) + return NULL; + + return take_stringsbuf (&ret); /* caller frees */ +} + "; (* Implement code for returning structs and struct lists. *) @@ -810,7 +862,9 @@ return_string_list (value retv) pr " CAMLreturnT (char *, NULL);\n"; pr " }\n"; pr " CAMLreturnT (char *, ret); /* caller frees */\n" - | RString _ -> assert false + | RString (RMountable, _) -> + pr " char *ret = return_string_mountable (retv);\n"; + pr " CAMLreturnT (char *, ret); /* caller frees */\n" | RStringList _ -> pr " char **ret = return_string_list (retv);\n"; pr " CAMLreturnT (char **, ret); /* caller frees */\n" @@ -824,6 +878,9 @@ return_string_list (value retv) pr " return_%s_list (retv);\n" typ; pr " /* caller frees */\n"; pr " CAMLreturnT (guestfs_int_%s_list *, ret);\n" typ + | RHashtable (RMountable, RPlainString, _) -> + pr " char **ret = return_hashtable_mountable_string (retv);\n"; + pr " CAMLreturnT (char **, ret); /* caller frees */\n" | RHashtable _ -> assert false | RBufferOut _ -> assert false ); diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml index 1b0feae87..dec02f5fa 100644 --- a/generator/proc_nr.ml +++ b/generator/proc_nr.ml @@ -483,6 +483,7 @@ let proc_nr = [ 473, "yara_destroy"; 474, "internal_yara_scan"; 475, "file_architecture"; +476, "list_filesystems"; ] (* End of list. If adding a new entry, add it at the end of the list diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR index 7573eff88..b86395733 100644 --- a/lib/MAX_PROC_NR +++ b/lib/MAX_PROC_NR @@ -1 +1 @@ -475 +476 diff --git a/lib/Makefile.am b/lib/Makefile.am index c7a9880eb..f12cd055e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -108,7 +108,6 @@ libguestfs_la_SOURCES = \ launch-unix.c \ libvirt-auth.c \ libvirt-domain.c \ - listfs.c \ lpj.c \ match.c \ mountable.c \ diff --git a/lib/listfs.c b/lib/listfs.c deleted file mode 100644 index 60aff3305..000000000 --- a/lib/listfs.c +++ /dev/null @@ -1,246 +0,0 @@ -/* libguestfs - * Copyright (C) 2010 Red Hat Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; 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 "guestfs.h" -#include "guestfs-internal.h" -#include "guestfs-internal-actions.h" -#include "structs-cleanups.h" - -/* List filesystems. - * - * The current implementation just uses guestfs_vfs_type and doesn't - * try mounting anything, but we reserve the right in future to try - * mounting filesystems. - */ - -static void remove_from_list (char **list, const char *item); -static int check_with_vfs_type (guestfs_h *g, const char *dev, struct stringsbuf *sb); -static int is_mbr_partition_type_42 (guestfs_h *g, const char *partition); - -char ** -guestfs_impl_list_filesystems (guestfs_h *g) -{ - size_t i; - DECLARE_STRINGSBUF (ret); - - const char *lvm2[] = { "lvm2", NULL }; - const int has_lvm2 = guestfs_feature_available (g, (char **) lvm2); - const char *ldm[] = { "ldm", NULL }; - const int has_ldm = guestfs_feature_available (g, (char **) ldm); - - CLEANUP_FREE_STRING_LIST char **devices = NULL; - CLEANUP_FREE_STRING_LIST char **partitions = NULL; - CLEANUP_FREE_STRING_LIST char **mds = NULL; - CLEANUP_FREE_STRING_LIST char **lvs = NULL; - CLEANUP_FREE_STRING_LIST char **ldmvols = NULL; - CLEANUP_FREE_STRING_LIST char **ldmparts = NULL; - - /* Look to see if any devices directly contain filesystems - * (RHBZ#590167). However vfs-type will fail to tell us anything - * useful about devices which just contain partitions, so we also - * get the list of partitions and exclude the corresponding devices - * by using part-to-dev. - */ - devices = guestfs_list_devices (g); - if (devices == NULL) goto error; - partitions = guestfs_list_partitions (g); - if (partitions == NULL) goto error; - mds = guestfs_list_md_devices (g); - if (mds == NULL) goto error; - - for (i = 0; partitions[i] != NULL; ++i) { - CLEANUP_FREE char *dev = guestfs_part_to_dev (g, partitions[i]); - if (dev) - remove_from_list (devices, dev); - } - - /* Use vfs-type to check for filesystems on devices. */ - for (i = 0; devices[i] != NULL; ++i) - if (check_with_vfs_type (g, devices[i], &ret) == -1) - goto error; - - /* Use vfs-type to check for filesystems on partitions. */ - for (i = 0; partitions[i] != NULL; ++i) { - if (has_ldm == 0 || ! is_mbr_partition_type_42 (g, partitions[i])) { - if (check_with_vfs_type (g, partitions[i], &ret) == -1) - goto error; - } - } - - /* Use vfs-type to check for filesystems on md devices. */ - for (i = 0; mds[i] != NULL; ++i) - if (check_with_vfs_type (g, mds[i], &ret) == -1) - goto error; - - if (has_lvm2 > 0) { - /* Use vfs-type to check for filesystems on LVs. */ - lvs = guestfs_lvs (g); - if (lvs == NULL) goto error; - - for (i = 0; lvs[i] != NULL; ++i) - if (check_with_vfs_type (g, lvs[i], &ret) == -1) - goto error; - } - - if (has_ldm > 0) { - /* Use vfs-type to check for filesystems on Windows dynamic disks. */ - ldmvols = guestfs_list_ldm_volumes (g); - if (ldmvols == NULL) goto error; - - for (i = 0; ldmvols[i] != NULL; ++i) - if (check_with_vfs_type (g, ldmvols[i], &ret) == -1) - goto error; - - ldmparts = guestfs_list_ldm_partitions (g); - if (ldmparts == NULL) goto error; - - for (i = 0; ldmparts[i] != NULL; ++i) - if (check_with_vfs_type (g, ldmparts[i], &ret) == -1) - goto error; - } - - /* Finish off the list and return it. */ - guestfs_int_end_stringsbuf (g, &ret); - return ret.argv; - - error: - guestfs_int_free_stringsbuf (&ret); - return NULL; -} - -/* If 'item' occurs in 'list', remove and free it. */ -static void -remove_from_list (char **list, const char *item) -{ - size_t i; - - for (i = 0; list[i] != NULL; ++i) - if (STREQ (list[i], item)) { - free (list[i]); - for (; list[i+1] != NULL; ++i) - list[i] = list[i+1]; - list[i] = NULL; - return; - } -} - -/* Use vfs-type to look for a filesystem of some sort on 'dev'. - * Apart from some types which we ignore, add the result to the - * 'ret' string list. - */ -static int -check_with_vfs_type (guestfs_h *g, const char *device, struct stringsbuf *sb) -{ - const char *v; - CLEANUP_FREE char *vfs_type = NULL; - - guestfs_push_error_handler (g, NULL, NULL); - vfs_type = guestfs_vfs_type (g, device); - guestfs_pop_error_handler (g); - - if (!vfs_type) - v = "unknown"; - else if (STREQ (vfs_type, "")) - v = "unknown"; - else if (STREQ (vfs_type, "btrfs")) { - CLEANUP_FREE_BTRFSSUBVOLUME_LIST struct guestfs_btrfssubvolume_list *vols - guestfs_btrfs_subvolume_list (g, device); - - if (vols == NULL) - return -1; - - const int64_t default_volume - guestfs_btrfs_subvolume_get_default (g, device); - - for (size_t i = 0; i < vols->len; i++) { - struct guestfs_btrfssubvolume *this = &vols->val[i]; - - /* Ignore the default subvolume. We get it by simply mounting - * the whole device of this btrfs filesystem. - */ - if (this->btrfssubvolume_id == (uint64_t) default_volume) - continue; - - guestfs_int_add_sprintf (g, sb, - "btrfsvol:%s/%s", - device, this->btrfssubvolume_path); - guestfs_int_add_string (g, sb, "btrfs"); - } - - v = vfs_type; - } - else { - /* Ignore all "*_member" strings. In libblkid these are returned - * for things which are members of some RAID or LVM set, most - * importantly "LVM2_member" which is a PV. - */ - const size_t n = strlen (vfs_type); - if (n >= 7 && STREQ (&vfs_type[n-7], "_member")) - return 0; - - /* Ignore LUKS-encrypted partitions. These are also containers. */ - if (STREQ (vfs_type, "crypto_LUKS")) - return 0; - - v = vfs_type; - } - - guestfs_int_add_string (g, sb, device); - guestfs_int_add_string (g, sb, v); - - return 0; -} - -/* We should ignore partitions that have MBR type byte 0x42, because - * these are members of a Windows dynamic disk group. Trying to read - * them will cause errors (RHBZ#887520). Assuming that libguestfs was - * compiled with ldm support, we'll get the filesystems on these later. - */ -static int -is_mbr_partition_type_42 (guestfs_h *g, const char *partition) -{ - CLEANUP_FREE char *device = NULL; - int partnum; - int mbr_id; - int ret = 0; - - guestfs_push_error_handler (g, NULL, NULL); - - partnum = guestfs_part_to_partnum (g, partition); - if (partnum == -1) - goto out; - - device = guestfs_part_to_dev (g, partition); - if (device == NULL) - goto out; - - mbr_id = guestfs_part_get_mbr_id (g, device, partnum); - - ret = mbr_id == 0x42; - - out: - guestfs_pop_error_handler (g); - - return ret; -} -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 33/41] daemon: Reimplement ‘part_list’ API in OCaml.
--- daemon/parted.c | 56 ----------------------------------------------- daemon/parted.ml | 51 ++++++++++++++++++++++++++++++++++++++++++ daemon/parted.mli | 8 +++++++ generator/actions_core.ml | 1 + 4 files changed, 60 insertions(+), 56 deletions(-) diff --git a/daemon/parted.c b/daemon/parted.c index a1e5c81cf..125aec60b 100644 --- a/daemon/parted.c +++ b/daemon/parted.c @@ -387,62 +387,6 @@ do_part_get_parttype (const char *device) return r; } -guestfs_int_partition_list * -do_part_list (const char *device) -{ - CLEANUP_FREE char *out = print_partition_table (device, true); - if (!out) - return NULL; - - CLEANUP_FREE_STRING_LIST char **lines = split_lines (out); - - if (!lines) - return NULL; - - guestfs_int_partition_list *r; - - /* lines[0] is "BYT;", lines[1] is the device line which we ignore, - * lines[2..] are the partitions themselves. Count how many. - */ - size_t nr_rows = 0, row; - for (row = 2; lines[row] != NULL; ++row) - ++nr_rows; - - r = malloc (sizeof *r); - if (r == NULL) { - reply_with_perror ("malloc"); - return NULL; - } - r->guestfs_int_partition_list_len = nr_rows; - r->guestfs_int_partition_list_val - malloc (nr_rows * sizeof (guestfs_int_partition)); - if (r->guestfs_int_partition_list_val == NULL) { - reply_with_perror ("malloc"); - goto error2; - } - - /* Now parse the lines. */ - size_t i; - for (i = 0, row = 2; lines[row] != NULL; ++i, ++row) { - if (sscanf (lines[row], "%d:%" SCNi64 "B:%" SCNi64 "B:%" SCNi64 "B", - &r->guestfs_int_partition_list_val[i].part_num, - &r->guestfs_int_partition_list_val[i].part_start, - &r->guestfs_int_partition_list_val[i].part_end, - &r->guestfs_int_partition_list_val[i].part_size) != 4) { - reply_with_error ("could not parse row from output of parted print command: %s", lines[row]); - goto error3; - } - } - - return r; - - error3: - free (r->guestfs_int_partition_list_val); - error2: - free (r); - return NULL; -} - int do_part_get_bootable (const char *device, int partnum) { diff --git a/daemon/parted.ml b/daemon/parted.ml index 6be41cf66..37e1b42be 100644 --- a/daemon/parted.ml +++ b/daemon/parted.ml @@ -22,6 +22,8 @@ open Std_utils open Utils +include Structs + (* Test if [sfdisk] is recent enough to have [--part-type], to be used * instead of [--print-id] and [--change-id]. *) @@ -53,3 +55,52 @@ let part_get_mbr_id device partnum (* It's printed in hex, possibly with a leading space. *) sscanf out " %x" identity + +let print_partition_table ~add_m_option device + udev_settle (); + + let args = ref [] in + if add_m_option then push_back args "-m"; + push_back args "-s"; + push_back args "--"; + push_back args device; + push_back args "unit"; + push_back args "b"; + push_back args "print"; + + let out + try command "parted" !args + with + (* Translate "unrecognised disk label" into an errno code. *) + Failure str when String.find str "unrecognised disk label" >= 0 -> + raise (Unix.Unix_error (Unix.EINVAL, "parted", device ^ ": " ^ str)) in + + udev_settle (); + + (* Split the output into lines. *) + let out = String.trim out in + let lines = String.nsplit "\n" out in + + (* lines[0] is "BYT;", lines[1] is the device line which we ignore, + * lines[2..] are the partitions themselves. + *) + match lines with + | "BYT;" :: _ :: lines -> lines + | [] | [_] -> + failwith "too few rows of output from 'parted print' command" + | _ -> + failwith "did not see 'BYT;' magic value in 'parted print' command" + +let part_list device + let lines = print_partition_table ~add_m_option:true device in + + List.map ( + fun line -> + try sscanf line "%d:%LdB:%LdB:%LdB" + (fun num start end_ size -> + { part_num = Int32.of_int num; + part_start = start; part_end = end_; part_size = size }) + with Scan_failure err -> + failwithf "could not parse row from output of 'parted print' command: %s: %s" + line err + ) lines diff --git a/daemon/parted.mli b/daemon/parted.mli index 33eb6d30d..057d7e8c7 100644 --- a/daemon/parted.mli +++ b/daemon/parted.mli @@ -16,4 +16,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +type partition = { + part_num : int32; + part_start : int64; + part_end : int64; + part_size : int64; +} + val part_get_mbr_id : string -> int -> int +val part_list : string -> partition list diff --git a/generator/actions_core.ml b/generator/actions_core.ml index d5946b3f5..b1e2559e0 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -5039,6 +5039,7 @@ table. This works on C<gpt> but not on C<mbr> partitions." }; { defaults with name = "part_list"; added = (1, 0, 78); style = RStructList ("partitions", "partition"), [String (Device, "device")], []; + impl = OCaml "Parted.part_list"; tests = [] (* XXX Add a regression test for this. *); shortdesc = "list partitions on a device"; longdesc = "\ -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 34/41] daemon: Reimplement ‘findfs_uuid’ and ‘findfs_label’ APIs in OCaml.
--- daemon/Makefile.am | 3 +- daemon/findfs.c | 94 ----------------------------------------------- daemon/findfs.ml | 56 ++++++++++++++++++++++++++++ daemon/findfs.mli | 20 ++++++++++ daemon/lvm.c | 31 ++++++++++++++++ daemon/lvm.ml | 3 ++ daemon/lvm.mli | 8 ++++ docs/C_SOURCE_FILES | 1 - generator/actions_core.ml | 2 + 9 files changed, 122 insertions(+), 96 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index a3c486046..15b78d6d8 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -108,7 +108,6 @@ guestfsd_SOURCES = \ ext2.c \ fallocate.c \ file.c \ - findfs.c \ fill.c \ find.c \ format.c \ @@ -265,6 +264,7 @@ SOURCES_MLI = \ devsparts.mli \ file.mli \ filearch.mli \ + findfs.mli \ is.mli \ ldm.mli \ link.mli \ @@ -293,6 +293,7 @@ SOURCES_ML = \ ldm.ml \ link.ml \ lvm.ml \ + findfs.ml \ md.ml \ mount.ml \ parted.ml \ diff --git a/daemon/findfs.c b/daemon/findfs.c deleted file mode 100644 index f44137038..000000000 --- a/daemon/findfs.c +++ /dev/null @@ -1,94 +0,0 @@ -/* libguestfs - the guestfsd daemon - * Copyright (C) 2010 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 <unistd.h> - -#include "daemon.h" -#include "actions.h" - -GUESTFSD_EXT_CMD(str_findfs, findfs); - -static char * -findfs (const char *tag, const char *label_or_uuid) -{ - char *out; - CLEANUP_FREE char *err = NULL; - CLEANUP_FREE char *arg = NULL; - int r; - size_t len; - - /* Kill the cache file, forcing blkid to reread values from the - * original filesystems. In blkid there is a '-p' option which is - * supposed to do this, but (a) it doesn't work and (b) that option - * is not supported in RHEL 5. - */ - unlink ("/etc/blkid/blkid.tab"); - unlink ("/run/blkid/blkid.tab"); - - if (asprintf (&arg, "%s=%s", tag, label_or_uuid) == -1) { - reply_with_perror ("asprintf"); - return NULL; - } - - r = command (&out, &err, str_findfs, arg, NULL); - if (r == -1) { - reply_with_error ("%s", err); - free (out); - return NULL; - } - - /* Trim trailing \n if present. */ - len = strlen (out); - if (len > 0 && out[len-1] == '\n') - out[len-1] = '\0'; - - if (STRPREFIX (out, "/dev/mapper/") || STRPREFIX (out, "/dev/dm-")) { - char *canonical; - r = lv_canonical (out, &canonical); - if (r == -1) { - free (out); - return NULL; - } - if (r == 1) { - free (out); - out = canonical; - } - /* Ignore the case where r == 0. /dev/mapper does not correspond - * to an LV, so the best we can do is just return it as-is. - */ - } - - return out; /* caller frees */ -} - -char * -do_findfs_uuid (const char *uuid) -{ - return findfs ("UUID", uuid); -} - -char * -do_findfs_label (const char *label) -{ - return findfs ("LABEL", label); -} diff --git a/daemon/findfs.ml b/daemon/findfs.ml new file mode 100644 index 000000000..8acb72928 --- /dev/null +++ b/daemon/findfs.ml @@ -0,0 +1,56 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf +open Unix + +open Std_utils + +open Utils + +let rec findfs_uuid uuid + findfs "UUID" uuid +and findfs_label label + findfs "LABEL"label + +and findfs tag str + (* Kill the cache file, forcing blkid to reread values from the + * original filesystems. In blkid there is a '-p' option which is + * supposed to do this, but (a) it doesn't work and (b) that option + * is not supported in RHEL 5. + *) + (try unlink "/etc/blkid/blkid.tab" with Unix_error _ -> ()); + (try unlink "/run/blkid/blkid.tab" with Unix_error _ -> ()); + + let out = command "findfs" [ sprintf "%s=%s" tag str ] in + + (* Trim trailing \n if present. *) + let out = String.trim out in + + if String.is_prefix out "/dev/mapper/" || + String.is_prefix out "/dev/dm-" then ( + match Lvm.lv_canonical out with + | None -> + (* Ignore the case where 'out' doesn't appear to be an LV. + * The best we can do is return the original as-is. + *) + out + | Some out -> out + ) + else + out diff --git a/daemon/findfs.mli b/daemon/findfs.mli new file mode 100644 index 000000000..acef0395c --- /dev/null +++ b/daemon/findfs.mli @@ -0,0 +1,20 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 findfs_uuid : string -> string +val findfs_label : string -> string diff --git a/daemon/lvm.c b/daemon/lvm.c index 072bf53b4..268d87416 100644 --- a/daemon/lvm.c +++ b/daemon/lvm.c @@ -27,6 +27,11 @@ #include <sys/stat.h> #include <dirent.h> +#include <caml/alloc.h> +#include <caml/fail.h> +#include <caml/memory.h> +#include <caml/mlvalues.h> + #include "daemon.h" #include "c-ctype.h" #include "actions.h" @@ -969,3 +974,29 @@ do_vgchange_uuid_all (void) return 0; } + +/* Allow lv_canonical to be called from OCaml functions in the daemon. */ +extern value guestfs_int_daemon_lv_canonical (value devicev); + +value +guestfs_int_daemon_lv_canonical (value devicev) +{ + CAMLparam1 (devicev); + CAMLlocal2 (rv, v); + int r; + CLEANUP_FREE char *rdevice = NULL; + + r = lv_canonical (String_val (devicev), &rdevice); + if (r == -1) + caml_failwith ("lv_canonical"); + + if (r == 0) /* Return None. */ + rv = Val_int (0); + else { /* Return Some rdevice. */ + v = caml_copy_string (rdevice); + rv = caml_alloc (1, 0); + caml_modify (&Field (rv, 0), v); + } + + CAMLreturn (rv); +} diff --git a/daemon/lvm.ml b/daemon/lvm.ml index 14f0a8578..8647f3246 100644 --- a/daemon/lvm.ml +++ b/daemon/lvm.ml @@ -25,6 +25,9 @@ open Utils external available : unit -> bool "guestfs_int_daemon_optgroup_lvm2_available" "noalloc" +external lv_canonical : string -> string option + "guestfs_int_daemon_lv_canonical" + let lvs_has_S_opt = lazy ( let out = command "lvm" ["lvs"; "--help"] in String.find out "-S" >= 0 diff --git a/daemon/lvm.mli b/daemon/lvm.mli index 1cf61ecfb..f01f1f80c 100644 --- a/daemon/lvm.mli +++ b/daemon/lvm.mli @@ -19,3 +19,11 @@ val available : unit -> bool val lvs : unit -> string list + +val lv_canonical : string -> string option +(** This is a binding for the C lv_canonical function used in a + few cases. On error this raises an exception. There are + two possible non-error cases: + + Some lv = conversion was successful, returning the canonical LV + None = input path was not an LV, it could not be made canonical *) diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index c01064803..651d294c5 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -103,7 +103,6 @@ daemon/fallocate.c daemon/file.c daemon/fill.c daemon/find.c -daemon/findfs.c daemon/format.c daemon/fs-min-size.c daemon/fsck.c diff --git a/generator/actions_core.ml b/generator/actions_core.ml index b1e2559e0..0a967f76d 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -5746,6 +5746,7 @@ returns true iff this is the case." }; { defaults with name = "findfs_uuid"; added = (1, 5, 3); style = RString (RDevice, "device"), [String (PlainString, "uuid")], []; + impl = OCaml "Findfs.findfs_uuid"; shortdesc = "find a filesystem by UUID"; longdesc = "\ This command searches the filesystems and returns the one @@ -5757,6 +5758,7 @@ To find the UUID of a filesystem, use C<guestfs_vfs_uuid>." }; { defaults with name = "findfs_label"; added = (1, 5, 3); style = RString (RDevice, "device"), [String (PlainString, "label")], []; + impl = OCaml "Findfs.findfs_label"; shortdesc = "find a filesystem by label"; longdesc = "\ This command searches the filesystems and returns the one -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 35/41] daemon: Reimplement ‘nr_devices’ API in OCaml.
--- daemon/devsparts.c | 15 --------------- daemon/devsparts.ml | 2 ++ daemon/devsparts.mli | 2 ++ generator/actions_core.ml | 1 + 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/daemon/devsparts.c b/daemon/devsparts.c index 1aacb8e16..12e779326 100644 --- a/daemon/devsparts.c +++ b/daemon/devsparts.c @@ -54,21 +54,6 @@ do_device_index (const char *device) return ret; } -int -do_nr_devices (void) -{ - size_t i; - CLEANUP_FREE_STRING_LIST char **devices = do_list_devices (); - - if (devices == NULL) - return -1; - - for (i = 0; devices[i] != NULL; ++i) - ; - - return (int) i; -} - #define GUESTFSDIR "/dev/disk/guestfs" char ** diff --git a/daemon/devsparts.ml b/daemon/devsparts.ml index e97ff1267..273612516 100644 --- a/daemon/devsparts.ml +++ b/daemon/devsparts.ml @@ -85,6 +85,8 @@ and add_partitions dev let parts = List.filter (fun part -> String.is_prefix part dev) parts in List.map (fun part -> "/dev/" ^ part) parts +let nr_devices () = List.length (list_devices ()) + let part_to_dev part let dev, part = split_device_partition part in if part = 0 then diff --git a/daemon/devsparts.mli b/daemon/devsparts.mli index 4dfaa86e6..8be47e752 100644 --- a/daemon/devsparts.mli +++ b/daemon/devsparts.mli @@ -19,6 +19,8 @@ val list_devices : unit -> string list val list_partitions : unit -> string list +val nr_devices : unit -> int + val part_to_dev : string -> string val part_to_partnum : string -> int diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 0a967f76d..db1411ff8 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -7432,6 +7432,7 @@ See also C<guestfs_list_devices>, C<guestfs_part_to_dev>." }; { defaults with name = "nr_devices"; added = (1, 19, 15); + impl = OCaml "Devsparts.nr_devices"; style = RInt "nrdisks", [], []; tests = [ InitEmpty, Always, TestResult ( -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 36/41] daemon: Reimplement ‘md_detail’ API in OCaml.
--- daemon/md.c | 66 ----------------------------------------------- daemon/md.ml | 37 ++++++++++++++++++++++++++ daemon/md.mli | 1 + generator/actions_core.ml | 1 + generator/daemon.ml | 27 +++++++++++++++++++ 5 files changed, 66 insertions(+), 66 deletions(-) diff --git a/daemon/md.c b/daemon/md.c index 5c9ecd136..549dd89fa 100644 --- a/daemon/md.c +++ b/daemon/md.c @@ -218,72 +218,6 @@ do_md_create (const char *name, char *const *devices, #pragma GCC diagnostic pop #endif -char ** -do_md_detail (const char *md) -{ - size_t i; - int r; - - CLEANUP_FREE char *out = NULL, *err = NULL; - CLEANUP_FREE_STRING_LIST char **lines = NULL; - - CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); - - const char *mdadm[] = { str_mdadm, "-D", "--export", md, NULL }; - r = commandv (&out, &err, mdadm); - if (r == -1) { - reply_with_error ("%s", err); - return NULL; - } - - /* Split the command output into lines */ - lines = split_lines (out); - if (lines == NULL) - return NULL; - - /* Parse the output of mdadm -D --export: - * MD_LEVEL=raid1 - * MD_DEVICES=2 - * MD_METADATA=1.0 - * MD_UUID=cfa81b59:b6cfbd53:3f02085b:58f4a2e1 - * MD_NAME=localhost.localdomain:0 - */ - for (i = 0; lines[i] != NULL; ++i) { - char *line = lines[i]; - - /* Skip blank lines (shouldn't happen) */ - if (line[0] == '\0') continue; - - /* Split the line in 2 at the equals sign */ - char *eq = strchr (line, '='); - if (eq) { - *eq = '\0'; eq++; - - /* Remove the MD_ prefix from the key and translate the remainder to lower - * case */ - if (STRPREFIX (line, "MD_")) { - line += 3; - for (char *j = line; *j != '\0'; j++) { - *j = c_tolower (*j); - } - } - - /* Add the key/value pair to the output */ - if (add_string (&ret, line) == -1 || - add_string (&ret, eq) == -1) return NULL; - } else { - /* Ignore lines with no equals sign (shouldn't happen). Log to stderr so - * it will show up in LIBGUESTFS_DEBUG. */ - fprintf (stderr, "md-detail: unexpected mdadm output ignored: %s", line); - } - } - - if (end_stringsbuf (&ret) == -1) - return NULL; - - return take_stringsbuf (&ret); -} - int do_md_stop (const char *md) { diff --git a/daemon/md.ml b/daemon/md.ml index caf87cf8f..ba045b5f7 100644 --- a/daemon/md.ml +++ b/daemon/md.ml @@ -46,3 +46,40 @@ let list_md_devices () (* Return the list sorted. *) sort_device_names devs + +let md_detail md + let out = command "mdadm" ["-D"; "--export"; md] in + + (* Split the command output into lines. *) + let out = String.trim out in + let lines = String.nsplit "\n" out in + + (* Parse the output of mdadm -D --export: + * MD_LEVEL=raid1 + * MD_DEVICES=2 + * MD_METADATA=1.0 + * MD_UUID=cfa81b59:b6cfbd53:3f02085b:58f4a2e1 + * MD_NAME=localhost.localdomain:0 + *) + filter_map ( + fun line -> + (* Skip blank lines (shouldn't happen). *) + if line = "" then None + else ( + (* Split the line at the equals sign. *) + let key, value = String.split "=" line in + + (* Remove the MD_ prefix from the key and translate the + * remainder to lower case. + *) + let key + if String.is_prefix key "MD_" then + String.sub key 3 (String.length key - 3) + else + key in + let key = String.lowercase_ascii key in + + (* Add the key/value pair to the output. *) + Some (key, value) + ) + ) lines diff --git a/daemon/md.mli b/daemon/md.mli index 56b6ea65e..8f0c79a7f 100644 --- a/daemon/md.mli +++ b/daemon/md.mli @@ -17,3 +17,4 @@ *) val list_md_devices : unit -> string list +val md_detail : string -> (string * string) list diff --git a/generator/actions_core.ml b/generator/actions_core.ml index db1411ff8..070a1c641 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -6606,6 +6606,7 @@ List all Linux md devices." }; { defaults with name = "md_detail"; added = (1, 15, 6); style = RHashtable (RPlainString, RPlainString, "info"), [String (Device, "md")], []; + impl = OCaml "Md.md_detail"; optional = Some "mdadm"; shortdesc = "obtain metadata for an MD device"; longdesc = "\ diff --git a/generator/daemon.ml b/generator/daemon.ml index 66b625388..f20c87bea 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -597,6 +597,30 @@ return_string_mountable (value retv) } } +/* Implement RHashtable (RPlainString, RPlainString, _). */ +static char ** +return_hashtable_string_string (value retv) +{ + CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); + value v, sv; + + while (retv != Val_int (0)) { + v = Field (retv, 0); /* (string, string) */ + sv = Field (v, 0); /* string */ + if (add_string (&ret, String_val (sv)) == -1) + return NULL; + sv = Field (v, 1); /* string */ + if (add_string (&ret, String_val (sv)) == -1) + return NULL; + retv = Field (retv, 1); + } + + if (end_stringsbuf (&ret) == -1) + return NULL; + + return take_stringsbuf (&ret); /* caller frees */ +} + /* Implement RHashtable (RMountable, RPlainString, _). */ static char ** return_hashtable_mountable_string (value retv) @@ -878,6 +902,9 @@ return_hashtable_mountable_string (value retv) pr " return_%s_list (retv);\n" typ; pr " /* caller frees */\n"; pr " CAMLreturnT (guestfs_int_%s_list *, ret);\n" typ + | RHashtable (RPlainString, RPlainString, _) -> + pr " char **ret = return_hashtable_string_string (retv);\n"; + pr " CAMLreturnT (char **, ret); /* caller frees */\n" | RHashtable (RMountable, RPlainString, _) -> pr " char **ret = return_hashtable_mountable_string (retv);\n"; pr " CAMLreturnT (char **, ret); /* caller frees */\n" -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 37/41] daemon: Reimplement ‘realpath’ API in OCaml.
--- daemon/Makefile.am | 1 - daemon/realpath.c | 50 ----------------------------------------------- daemon/realpath.ml | 5 +++++ daemon/realpath.mli | 1 + docs/C_SOURCE_FILES | 1 - generator/actions_core.ml | 1 + 6 files changed, 7 insertions(+), 52 deletions(-) diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 15b78d6d8..db61b4f0d 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -153,7 +153,6 @@ guestfsd_SOURCES = \ pingdaemon.c \ proto.c \ readdir.c \ - realpath.c \ rename.c \ rsync.c \ scrub.c \ diff --git a/daemon/realpath.c b/daemon/realpath.c deleted file mode 100644 index f9d22d28d..000000000 --- a/daemon/realpath.c +++ /dev/null @@ -1,50 +0,0 @@ -/* libguestfs - the guestfsd daemon - * Copyright (C) 2009-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. - */ - -#include <config.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <limits.h> -#include <sys/types.h> -#include <dirent.h> - -#include "cloexec.h" - -#include "daemon.h" -#include "optgroups.h" -#include "actions.h" - -char * -do_realpath (const char *path) -{ - char *ret; - - CHROOT_IN; - ret = realpath (path, NULL); - CHROOT_OUT; - if (ret == NULL) { - reply_with_perror ("%s", path); - return NULL; - } - - return ret; /* caller frees */ -} diff --git a/daemon/realpath.ml b/daemon/realpath.ml index cffe86322..4b4971dd7 100644 --- a/daemon/realpath.ml +++ b/daemon/realpath.ml @@ -20,6 +20,11 @@ open Printf open Std_utils +let realpath path + let chroot = Chroot.create ~name:(sprintf "realpath: %s" path) + (Sysroot.sysroot ()) in + Chroot.f chroot Unix_utils.Realpath.realpath path + (* The infamous case_sensitive_path function, which works around * the bug in ntfs-3g that all paths are case sensitive even though * the underlying filesystem is case insensitive. diff --git a/daemon/realpath.mli b/daemon/realpath.mli index 371e619fc..3da53c461 100644 --- a/daemon/realpath.mli +++ b/daemon/realpath.mli @@ -16,4 +16,5 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *) +val realpath : string -> string val case_sensitive_path : string -> string diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 651d294c5..02adbf73a 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -146,7 +146,6 @@ daemon/parted.c daemon/pingdaemon.c daemon/proto.c daemon/readdir.c -daemon/realpath.c daemon/rename.c daemon/rsync.c daemon/scrub.c diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 070a1c641..4ec83d22d 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -4197,6 +4197,7 @@ compress- or gzip-compressed. { defaults with name = "realpath"; added = (1, 0, 66); style = RString (RPlainString, "rpath"), [String (Pathname, "path")], []; + impl = OCaml "Realpath.realpath"; tests = [ InitISOFS, Always, TestResultString ( [["realpath"; "/../directory"]], "/directory"), [] -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 38/41] daemon: Implement command flag CommandFlagFoldStdoutOnStderr.
Used to handle broken commands like parted, sgdisk which print errors on stdout. --- daemon/utils.ml | 19 ++++++++++++++----- daemon/utils.mli | 11 +++++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/daemon/utils.ml b/daemon/utils.ml index 48f6b9c5c..808e575fd 100644 --- a/daemon/utils.ml +++ b/daemon/utils.ml @@ -25,9 +25,15 @@ let prog_exists prog try ignore (which prog); true with Executable_not_found _ -> false -let commandr prog args +type command_flag + CommandFlagFoldStdoutOnStderr + +let commandr ?(flags = []) prog args + let fold_stdout_on_stderr = List.mem CommandFlagFoldStdoutOnStderr flags in + if verbose () then - eprintf "command: %s %s\n%!" + eprintf "command:%s %s %s\n%!" + (if fold_stdout_on_stderr then " fold-stdout-on-stderr" else "") prog (String.concat " " args); let argv = Array.of_list (prog :: args) in @@ -43,7 +49,10 @@ let commandr prog args (* Child process. *) dup2 stdin_fd stdin; close stdin_fd; - dup2 stdout_fd stdout; + if not fold_stdout_on_stderr then + dup2 stdout_fd stdout + else + dup2 stderr_fd stdout; close stdout_fd; dup2 stderr_fd stderr; close stderr_fd; @@ -91,8 +100,8 @@ let commandr prog args (r, stdout, stderr) -let command prog args - let r, stdout, stderr = commandr prog args in +let command ?flags prog args + let r, stdout, stderr = commandr ?flags prog args in if r <> 0 then failwithf "%s exited with status %d: %s" prog r stderr; stdout diff --git a/daemon/utils.mli b/daemon/utils.mli index a1f956be3..d3c8bdf4d 100644 --- a/daemon/utils.mli +++ b/daemon/utils.mli @@ -60,7 +60,14 @@ val proc_unmangle_path : string -> string (** Reverse kernel path escaping done in fs/seq_file.c:mangle_path. This is inconsistently used for /proc fields. *) -val command : string -> string list -> string +type command_flag + CommandFlagFoldStdoutOnStderr + (** For broken external commands that send error messages to stdout + (hello, parted) but that don't have any useful stdout information, + use this flag to capture the error messages in the [stderr] + buffer. Nothing will be captured on stdout if you use this flag. *) + +val command : ?flags:command_flag list -> string -> string list -> string (** Run an external command without using the shell, and collect stdout and stderr separately. Returns stdout if the command runs successfully. @@ -68,7 +75,7 @@ val command : string -> string list -> string On failure of the command, this throws an exception containing the stderr from the command. *) -val commandr : string -> string list -> (int * string * string) +val commandr : ?flags:command_flag list -> string -> string list -> (int * string * string) (** Run an external command without using the shell, and collect stdout and stderr separately. -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 39/41] daemon: Reimplement ‘part_get_parttype’, ‘part_get_gpt_type’, ‘part_get_gpt_guid’ APIs in OCaml.
--- daemon/parted.c | 176 +++++----------------------------------------- daemon/parted.ml | 70 +++++++++++++++++- daemon/parted.mli | 5 ++ generator/actions_core.ml | 3 + 4 files changed, 92 insertions(+), 162 deletions(-) diff --git a/daemon/parted.c b/daemon/parted.c index 125aec60b..1c81cd968 100644 --- a/daemon/parted.c +++ b/daemon/parted.c @@ -348,45 +348,6 @@ print_partition_table (const char *device, bool add_m_option) return out; } -char * -do_part_get_parttype (const char *device) -{ - CLEANUP_FREE char *out = print_partition_table (device, true); - if (!out) - return NULL; - - CLEANUP_FREE_STRING_LIST char **lines = split_lines (out); - if (!lines) - return NULL; - - if (lines[0] == NULL || STRNEQ (lines[0], "BYT;")) { - reply_with_error ("unknown signature, expected \"BYT;\" as first line of the output: %s", - lines[0] ? lines[0] : "(signature was null)"); - return NULL; - } - - if (lines[1] == NULL) { - reply_with_error ("parted didn't return a line describing the device"); - return NULL; - } - - /* lines[1] is something like: - * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;" - */ - char *r = get_table_field (lines[1], 5); - if (r == NULL) - return NULL; - - /* If "loop" return an error (RHBZ#634246). */ - if (STREQ (r, "loop")) { - free (r); - reply_with_error ("not a partitioned device"); - return NULL; - } - - return r; -} - int do_part_get_bootable (const char *device, int partnum) { @@ -557,126 +518,6 @@ do_part_set_gpt_guid (const char *device, int partnum, const char *guid) return 0; } -static char * -sgdisk_info_extract_field (const char *device, int partnum, const char *field, - char *(*extract) (const char *path)) -{ - if (partnum <= 0) { - reply_with_error ("partition number must be >= 1"); - return NULL; - } - - CLEANUP_FREE char *partnum_str = NULL; - if (asprintf (&partnum_str, "%i", partnum) == -1) { - reply_with_perror ("asprintf"); - return NULL; - } - - udev_settle (); - - CLEANUP_FREE char *err = NULL; - int r = commandf (NULL, &err, COMMAND_FLAG_FOLD_STDOUT_ON_STDERR, - str_sgdisk, device, "-i", partnum_str, NULL); - - if (r == -1) { - reply_with_error ("%s %s -i %s: %s", str_sgdisk, device, partnum_str, err); - return NULL; - } - - udev_settle (); - - CLEANUP_FREE_STRING_LIST char **lines = split_lines (err); - if (lines == NULL) { - reply_with_error ("'%s %s -i %i' returned no output", - str_sgdisk, device, partnum); - return NULL; - } - - const int fieldlen = strlen (field); - - /* Parse the output of sgdisk -i: - * Partition GUID code: 21686148-6449-6E6F-744E-656564454649 (BIOS boot partition) - * Partition unique GUID: 19AEC5FE-D63A-4A15-9D37-6FCBFB873DC0 - * First sector: 2048 (at 1024.0 KiB) - * Last sector: 411647 (at 201.0 MiB) - * Partition size: 409600 sectors (200.0 MiB) - * Attribute flags: 0000000000000000 - * Partition name: 'EFI System Partition' - */ - for (char **i = lines; *i != NULL; i++) { - char *line = *i; - - /* Skip blank lines */ - if (line[0] == '\0') continue; - - /* Split the line in 2 at the colon */ - char *colon = strchr (line, ':'); - if (colon) { - if (colon - line == fieldlen && - memcmp (line, field, fieldlen) == 0) - { - /* The value starts after the colon */ - char *value = colon + 1; - - /* Skip any leading whitespace */ - value += strspn (value, " \t"); - - /* Extract the actual information from the field. */ - char *ret = extract (value); - if (ret == NULL) { - /* The extraction function already sends the error. */ - return NULL; - } - - return ret; - } - } else { - /* Ignore lines with no colon. Log to stderr so it will show up in - * LIBGUESTFS_DEBUG. */ - if (verbose) { - fprintf (stderr, "get-gpt-type: unexpected sgdisk output ignored: %s\n", - line); - } - } - } - - /* If we got here it means we didn't find the field */ - reply_with_error ("sgdisk output did not contain '%s'. " - "See LIBGUESTFS_DEBUG output for more details", field); - return NULL; -} - -static char * -extract_uuid (const char *value) -{ - /* The value contains only valid GUID characters */ - const size_t value_len = strspn (value, "-0123456789ABCDEF"); - - char *ret = malloc (value_len + 1); - if (ret == NULL) { - reply_with_perror ("malloc"); - return NULL; - } - - memcpy (ret, value, value_len); - ret[value_len] = '\0'; - return ret; -} - -char * -do_part_get_gpt_type (const char *device, int partnum) -{ - return sgdisk_info_extract_field (device, partnum, - "Partition GUID code", extract_uuid); -} - -char * -do_part_get_gpt_guid (const char *device, int partnum) -{ - return sgdisk_info_extract_field (device, partnum, - "Partition unique GUID", extract_uuid); -} - char * do_part_get_name (const char *device, int partnum) { @@ -840,6 +681,23 @@ do_part_get_mbr_part_type (const char *device, int partnum) return NULL; } +static char * +extract_uuid (const char *value) +{ + /* The value contains only valid GUID characters */ + const size_t value_len = strspn (value, "-0123456789ABCDEF"); + + char *ret = malloc (value_len + 1); + if (ret == NULL) { + reply_with_perror ("malloc"); + return NULL; + } + + memcpy (ret, value, value_len); + ret[value_len] = '\0'; + return ret; +} + char * do_part_get_disk_guid (const char *device) { diff --git a/daemon/parted.ml b/daemon/parted.ml index 37e1b42be..737b1a96e 100644 --- a/daemon/parted.ml +++ b/daemon/parted.ml @@ -81,18 +81,18 @@ let print_partition_table ~add_m_option device let out = String.trim out in let lines = String.nsplit "\n" out in - (* lines[0] is "BYT;", lines[1] is the device line which we ignore, + (* lines[0] is "BYT;", lines[1] is the device line, * lines[2..] are the partitions themselves. *) match lines with - | "BYT;" :: _ :: lines -> lines + | "BYT;" :: device_line :: lines -> device_line, lines | [] | [_] -> failwith "too few rows of output from 'parted print' command" | _ -> failwith "did not see 'BYT;' magic value in 'parted print' command" let part_list device - let lines = print_partition_table ~add_m_option:true device in + let _, lines = print_partition_table ~add_m_option:true device in List.map ( fun line -> @@ -104,3 +104,67 @@ let part_list device failwithf "could not parse row from output of 'parted print' command: %s: %s" line err ) lines + +let part_get_parttype device + let device_line, _ = print_partition_table ~add_m_option:true device in + + (* device_line is something like: + * "/dev/sda:1953525168s:scsi:512:512:msdos:ATA Hitachi HDT72101;" + *) + let fields = String.nsplit ":" device_line in + match fields with + | _::_::_::_::_::"loop"::_ -> (* If "loop" return an error (RHBZ#634246). *) + failwithf "%s: not a partitioned device" device + | _::_::_::_::_::ret::_ -> ret + | _ -> + failwithf "%s: cannot parse the output of parted" device + +let rec part_get_gpt_type device partnum + sgdisk_info_extract_uuid_field device partnum "Partition GUID code" +and part_get_gpt_guid device partnum + sgdisk_info_extract_uuid_field device partnum "Partition unique GUID" + +and sgdisk_info_extract_uuid_field device partnum field + if partnum <= 0 then failwith "partition number must be >= 1"; + + udev_settle (); + + let out = command ~flags:[CommandFlagFoldStdoutOnStderr] + "sgdisk" [ device; "-i"; string_of_int partnum ] in + + udev_settle (); + + let out = String.trim out in + let lines = String.nsplit "\n" out in + + (* Parse the output of sgdisk -i: + * Partition GUID code: 21686148-6449-6E6F-744E-656564454649 (BIOS boot partition) + * Partition unique GUID: 19AEC5FE-D63A-4A15-9D37-6FCBFB873DC0 + * First sector: 2048 (at 1024.0 KiB) + * Last sector: 411647 (at 201.0 MiB) + * Partition size: 409600 sectors (200.0 MiB) + * Attribute flags: 0000000000000000 + * Partition name: 'EFI System Partition' + *) + let field_len = String.length field in + let rec loop = function + | [] -> + failwithf "%s: sgdisk output did not contain '%s'" device field + | line :: lines when String.is_prefix line field && + String.length line > field_len && + line.[field_len] = ':' -> + let value + String.sub line field_len (String.length line - field_len) in + + (* Skip any whitespace after the colon. *) + let value = String.triml value in + + (* Extract the UUID. *) + extract_uuid value + + | _ :: lines -> loop lines in + loop lines + +and extract_uuid value + (* The value contains only valid GUID characters. *) + String.sub value 0 (String.span value "-0123456789ABCDEF") diff --git a/daemon/parted.mli b/daemon/parted.mli index 057d7e8c7..5a77a8779 100644 --- a/daemon/parted.mli +++ b/daemon/parted.mli @@ -25,3 +25,8 @@ type partition = { val part_get_mbr_id : string -> int -> int val part_list : string -> partition list + +val part_get_parttype : string -> string + +val part_get_gpt_type : string -> int -> string +val part_get_gpt_guid : string -> int -> string diff --git a/generator/actions_core.ml b/generator/actions_core.ml index 4ec83d22d..c3421133e 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -5073,6 +5073,7 @@ Size of the partition in bytes. { defaults with name = "part_get_parttype"; added = (1, 0, 78); style = RString (RPlainString, "parttype"), [String (Device, "device")], []; + impl = OCaml "Parted.part_get_parttype"; tests = [ InitEmpty, Always, TestResultString ( [["part_disk"; "/dev/sda"; "gpt"]; @@ -8247,6 +8248,7 @@ for a useful list of type GUIDs." }; { defaults with name = "part_get_gpt_type"; added = (1, 21, 1); style = RString (RPlainString, "guid"), [String (Device, "device"); Int "partnum"], []; + impl = OCaml "Parted.part_get_gpt_type"; optional = Some "gdisk"; tests = [ InitGPT, Always, TestResultString ( @@ -9067,6 +9069,7 @@ valid GUID." }; { defaults with name = "part_get_gpt_guid"; added = (1, 29, 25); style = RString (RPlainString, "guid"), [String (Device, "device"); Int "partnum"], []; + impl = OCaml "Parted.part_get_gpt_guid"; optional = Some "gdisk"; tests = [ InitGPT, Always, TestResultString ( -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 40/41] lib: Move implementation of ‘hivex_value_utf8’ to new file ‘lib/hivex.c’.
Just a code movement, no change. --- lib/Makefile.am | 1 + lib/hivex.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++ lib/inspect-fs-windows.c | 83 ----------------------------------- 3 files changed, 112 insertions(+), 83 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index f12cd055e..109330452 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -92,6 +92,7 @@ libguestfs_la_SOURCES = \ fuse.c \ guid.c \ handle.c \ + hivex.c \ info.c \ inspect.c \ inspect-apps.c \ diff --git a/lib/hivex.c b/lib/hivex.c new file mode 100644 index 000000000..2d782e192 --- /dev/null +++ b/lib/hivex.c @@ -0,0 +1,111 @@ +/* libguestfs + * Copyright (C) 2010-2012 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 <errno.h> +#include <iconv.h> + +#include "guestfs.h" +#include "guestfs-internal.h" +#include "guestfs-internal-actions.h" + +/* Read the data from 'valueh', assume it is UTF16LE and convert it to + * UTF8. This is copied from hivex_value_string which doesn't work in + * the appliance because it uses iconv_open which doesn't work because + * we delete all the i18n databases. + */ +static char *utf16_to_utf8 (/* const */ char *input, size_t len); + +char * +guestfs_impl_hivex_value_utf8 (guestfs_h *g, int64_t valueh) +{ + char *ret; + size_t buflen; + + CLEANUP_FREE char *buf = guestfs_hivex_value_value (g, valueh, &buflen); + if (buf == NULL) + return NULL; + + ret = utf16_to_utf8 (buf, buflen); + if (ret == NULL) { + perrorf (g, "hivex: conversion of registry value to UTF8 failed"); + return NULL; + } + + return ret; +} + +static char * +utf16_to_utf8 (/* const */ char *input, size_t len) +{ + iconv_t ic = iconv_open ("UTF-8", "UTF-16LE"); + if (ic == (iconv_t) -1) + return NULL; + + /* iconv(3) has an insane interface ... */ + + /* Mostly UTF-8 will be smaller, so this is a good initial guess. */ + size_t outalloc = len; + + again:; + size_t inlen = len; + size_t outlen = outalloc; + char *out = malloc (outlen + 1); + if (out == NULL) { + int err = errno; + iconv_close (ic); + errno = err; + return NULL; + } + char *inp = input; + char *outp = out; + + const size_t r + iconv (ic, (ICONV_CONST char **) &inp, &inlen, &outp, &outlen); + if (r == (size_t) -1) { + if (errno == E2BIG) { + const int err = errno; + const size_t prev = outalloc; + /* Try again with a larger output buffer. */ + free (out); + outalloc *= 2; + if (outalloc < prev) { + iconv_close (ic); + errno = err; + return NULL; + } + goto again; + } + else { + /* Else some conversion failure, eg. EILSEQ, EINVAL. */ + const int err = errno; + iconv_close (ic); + free (out); + errno = err; + return NULL; + } + } + + *outp = '\0'; + iconv_close (ic); + + return out; +} diff --git a/lib/inspect-fs-windows.c b/lib/inspect-fs-windows.c index b14dc2e14..34f33c908 100644 --- a/lib/inspect-fs-windows.c +++ b/lib/inspect-fs-windows.c @@ -737,86 +737,3 @@ guestfs_int_case_sensitive_path_silently (guestfs_h *g, const char *path) return ret; } - -/* Read the data from 'valueh', assume it is UTF16LE and convert it to - * UTF8. This is copied from hivex_value_string which doesn't work in - * the appliance because it uses iconv_open which doesn't work because - * we delete all the i18n databases. - */ -static char *utf16_to_utf8 (/* const */ char *input, size_t len); - -char * -guestfs_impl_hivex_value_utf8 (guestfs_h *g, int64_t valueh) -{ - char *ret; - size_t buflen; - - CLEANUP_FREE char *buf = guestfs_hivex_value_value (g, valueh, &buflen); - if (buf == NULL) - return NULL; - - ret = utf16_to_utf8 (buf, buflen); - if (ret == NULL) { - perrorf (g, "hivex: conversion of registry value to UTF8 failed"); - return NULL; - } - - return ret; -} - -static char * -utf16_to_utf8 (/* const */ char *input, size_t len) -{ - iconv_t ic = iconv_open ("UTF-8", "UTF-16LE"); - if (ic == (iconv_t) -1) - return NULL; - - /* iconv(3) has an insane interface ... */ - - /* Mostly UTF-8 will be smaller, so this is a good initial guess. */ - size_t outalloc = len; - - again:; - size_t inlen = len; - size_t outlen = outalloc; - char *out = malloc (outlen + 1); - if (out == NULL) { - int err = errno; - iconv_close (ic); - errno = err; - return NULL; - } - char *inp = input; - char *outp = out; - - const size_t r - iconv (ic, (ICONV_CONST char **) &inp, &inlen, &outp, &outlen); - if (r == (size_t) -1) { - if (errno == E2BIG) { - const int err = errno; - const size_t prev = outalloc; - /* Try again with a larger output buffer. */ - free (out); - outalloc *= 2; - if (outalloc < prev) { - iconv_close (ic); - errno = err; - return NULL; - } - goto again; - } - else { - /* Else some conversion failure, eg. EILSEQ, EINVAL. */ - const int err = errno; - iconv_close (ic); - free (out); - errno = err; - return NULL; - } - } - - *outp = '\0'; - iconv_close (ic); - - return out; -} -- 2.13.0
Richard W.M. Jones
2017-Jun-15 17:06 UTC
[Libguestfs] [PATCH v6 41/41] UNFINISHED daemon: Reimplement most inspection APIs in the daemon, in OCaml.
Move the following APIs into the daemon, reimplemented in OCaml: * inspect_os * inspect_get_roots * inspect_get_mountpoints * inspect_get_filesystems * inspect_get_format * inspect_get_type * inspect_get_distro * inspect_get_package_format * inspect_get_package_management * inspect_get_product_name * inspect_get_product_variant * inspect_get_major_version * inspect_get_minor_version * inspect_get_arch * inspect_get_hostname * inspect_get_windows_systemroot * inspect_get_windows_software_hive * inspect_get_windows_system_hive * inspect_get_windows_current_control_set * inspect_get_drive_mappings * inspect_is_live * inspect_is_netinst * inspect_is_multipart The following inspection APIs have NOT been reimplemented in this commit: * inspect_list_applications [deprecated] * inspect_list_applications2 * inspect_get_icon This also embeds the ocaml-augeas library (upstream here: http://git.annexia.org/?p=ocaml-augeas.git;a=summary), but it's identical to the upstream version and should remain so. --- .gitignore | 2 + daemon/Makefile.am | 19 + daemon/augeas-c.c | 288 +++++++ daemon/augeas.README | 8 + daemon/augeas.ml | 59 ++ daemon/augeas.mli | 95 +++ daemon/chroot.ml | 2 +- daemon/daemon_utils_tests.ml | 15 + daemon/inspect.ml | 253 ++++++ daemon/inspect.mli | 41 + daemon/inspect_fs.ml | 395 +++++++++ daemon/inspect_fs.mli | 23 + daemon/inspect_fs_cd.ml | 23 + daemon/inspect_fs_cd.mli | 25 + daemon/inspect_fs_unix.ml | 563 +++++++++++++ daemon/inspect_fs_unix.mli | 53 ++ daemon/inspect_fs_unix_fstab.ml | 518 ++++++++++++ daemon/inspect_fs_unix_fstab.mli | 34 + daemon/inspect_fs_windows.ml | 498 ++++++++++++ daemon/inspect_fs_windows.mli | 25 + daemon/inspect_types.ml | 325 ++++++++ daemon/inspect_types.mli | 175 ++++ daemon/inspect_utils.ml | 162 ++++ daemon/inspect_utils.mli | 48 ++ daemon/mount.ml | 61 ++ daemon/mount.mli | 2 + daemon/utils.ml | 100 +++ daemon/utils.mli | 12 + docs/C_SOURCE_FILES | 2 +- generator/actions.ml | 1 + generator/actions_inspection.ml | 424 +++++----- generator/actions_inspection.mli | 1 + generator/daemon.ml | 63 +- generator/proc_nr.ml | 23 + lib/MAX_PROC_NR | 2 +- lib/Makefile.am | 1 - lib/guestfs-internal.h | 218 ++--- lib/handle.c | 1 - lib/inspect-apps.c | 107 ++- lib/inspect-fs-cd.c | 4 + lib/inspect-fs-unix.c | 1659 +------------------------------------- lib/inspect-fs-windows.c | 739 ----------------- lib/inspect-fs.c | 681 ---------------- lib/inspect-icon.c | 261 +++--- lib/inspect.c | 732 +---------------- 45 files changed, 4363 insertions(+), 4380 deletions(-) diff --git a/.gitignore b/.gitignore index bca927afc..b645229c9 100644 --- a/.gitignore +++ b/.gitignore @@ -181,6 +181,8 @@ Makefile.in /daemon/optgroups.c /daemon/optgroups.h /daemon/stamp-guestfsd.pod +/daemon/stringMap.ml +/daemon/stringMap.mli /daemon/structs-cleanups.c /daemon/structs-cleanups.h /daemon/structs.ml diff --git a/daemon/Makefile.am b/daemon/Makefile.am index db61b4f0d..18396f2a3 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -75,6 +75,7 @@ guestfsd_SOURCES = \ actions.h \ available.c \ augeas.c \ + augeas-c.c \ base64.c \ blkdiscard.c \ blkid.c \ @@ -256,6 +257,7 @@ guestfsd_CFLAGS = \ # library and then linked to the daemon. See # https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html SOURCES_MLI = \ + augeas.mli \ blkid.mli \ btrfs.mli \ chroot.mli \ @@ -264,6 +266,14 @@ SOURCES_MLI = \ file.mli \ filearch.mli \ findfs.mli \ + inspect.mli \ + inspect_fs_cd.mli \ + inspect_fs.mli \ + inspect_fs_unix.mli \ + inspect_fs_unix_fstab.mli \ + inspect_fs_windows.mli \ + inspect_types.mli \ + inspect_utils.mli \ is.mli \ ldm.mli \ link.mli \ @@ -277,6 +287,7 @@ SOURCES_MLI = \ utils.mli SOURCES_ML = \ + augeas.ml \ types.ml \ utils.ml \ structs.ml \ @@ -298,6 +309,14 @@ SOURCES_ML = \ parted.ml \ listfs.ml \ realpath.ml \ + inspect_types.ml \ + inspect_utils.ml \ + inspect_fs_cd.ml \ + inspect_fs_unix_fstab.ml \ + inspect_fs_unix.ml \ + inspect_fs_windows.ml \ + inspect_fs.ml \ + inspect.ml \ callbacks.ml \ daemon.ml diff --git a/daemon/augeas-c.c b/daemon/augeas-c.c new file mode 100644 index 000000000..c06bf92da --- /dev/null +++ b/daemon/augeas-c.c @@ -0,0 +1,288 @@ +/* Augeas OCaml bindings + * Copyright (C) 2008-2012 Red Hat Inc., Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: augeas_c.c,v 1.1 2008/05/06 10:48:20 rjones Exp $ + */ + +#include "config.h" + +#include <augeas.h> + +#include <caml/alloc.h> +#include <caml/memory.h> +#include <caml/mlvalues.h> +#include <caml/fail.h> +#include <caml/callback.h> +#include <caml/custom.h> + +typedef augeas *augeas_t; + +/* Raise an Augeas.Error exception. */ +static void +raise_error (const char *msg) +{ + caml_raise_with_string (*caml_named_value ("Augeas.Error"), msg); +} + +/* Map OCaml flags to C flags. */ +static int flag_map[] = { + /* AugSaveBackup */ AUG_SAVE_BACKUP, + /* AugSaveNewFile */ AUG_SAVE_NEWFILE, + /* AugTypeCheck */ AUG_TYPE_CHECK, + /* AugNoStdinc */ AUG_NO_STDINC, + /* AugSaveNoop */ AUG_SAVE_NOOP, + /* AugNoLoad */ AUG_NO_LOAD, +}; + +/* Wrap and unwrap augeas_t handles, with a finalizer. */ +#define Augeas_t_val(rv) (*(augeas_t *)Data_custom_val(rv)) + +static void +augeas_t_finalize (value tv) +{ + augeas_t t = Augeas_t_val (tv); + if (t) aug_close (t); +} + +static struct custom_operations custom_operations = { + (char *) "augeas_t_custom_operations", + augeas_t_finalize, + custom_compare_default, + custom_hash_default, + custom_serialize_default, + custom_deserialize_default +}; + +static value Val_augeas_t (augeas_t t) +{ + CAMLparam0 (); + CAMLlocal1 (rv); + /* We could choose these so that the GC can make better decisions. + * See 18.9.2 of the OCaml manual. + */ + const int used = 0; + const int max = 1; + + rv = caml_alloc_custom (&custom_operations, + sizeof (augeas_t), used, max); + Augeas_t_val(rv) = t; + + CAMLreturn (rv); +} + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +/* val create : string -> string option -> flag list -> t */ +CAMLprim value +ocaml_augeas_create (value rootv, value loadpathv, value flagsv) +{ + CAMLparam1 (rootv); + char *root = String_val (rootv); + char *loadpath; + int flags = 0, i; + augeas_t t; + + /* Optional loadpath. */ + loadpath + loadpathv == Val_int (0) + ? NULL + : String_val (Field (loadpathv, 0)); + + /* Convert list of flags to C. */ + for (; flagsv != Val_int (0); flagsv = Field (flagsv, 1)) { + i = Int_val (Field (flagsv, 0)); + flags |= flag_map[i]; + } + + t = aug_init (root, loadpath, flags); + + if (t == NULL) + raise_error ("Augeas.create"); + + CAMLreturn (Val_augeas_t (t)); +} + +/* val close : t -> unit */ +CAMLprim value +ocaml_augeas_close (value tv) +{ + CAMLparam1 (tv); + augeas_t t = Augeas_t_val (tv); + + if (t) { + aug_close (t); + Augeas_t_val(tv) = NULL; /* So the finalizer doesn't double-free. */ + } + + CAMLreturn (Val_unit); +} + +/* val get : t -> path -> value option */ +CAMLprim value +ocaml_augeas_get (value tv, value pathv) +{ + CAMLparam2 (tv, pathv); + CAMLlocal2 (optv, v); + augeas_t t = Augeas_t_val (tv); + char *path = String_val (pathv); + const char *val; + int r; + + r = aug_get (t, path, &val); + if (r == 1) { /* Return Some val */ + v = caml_copy_string (val); + optv = caml_alloc (1, 0); + Field (optv, 0) = v; + } else if (r == 0) /* Return None */ + optv = Val_int (0); + else if (r == -1) /* Error or multiple matches */ + raise_error ("Augeas.get"); + else + failwith ("Augeas.get: bad return value"); + + CAMLreturn (optv); +} + +/* val exists : t -> path -> bool */ +CAMLprim value +ocaml_augeas_exists (value tv, value pathv) +{ + CAMLparam2 (tv, pathv); + CAMLlocal1 (v); + augeas_t t = Augeas_t_val (tv); + char *path = String_val (pathv); + int r; + + r = aug_get (t, path, NULL); + if (r == 1) /* Return true. */ + v = Val_int (1); + else if (r == 0) /* Return false */ + v = Val_int (0); + else if (r == -1) /* Error or multiple matches */ + raise_error ("Augeas.exists"); + else + failwith ("Augeas.exists: bad return value"); + + CAMLreturn (v); +} + +/* val insert : t -> ?before:bool -> path -> string -> unit */ +CAMLprim value +ocaml_augeas_insert (value tv, value beforev, value pathv, value labelv) +{ + CAMLparam4 (tv, beforev, pathv, labelv); + augeas_t t = Augeas_t_val (tv); + char *path = String_val (pathv); + char *label = String_val (labelv); + int before; + + before = beforev == Val_int (0) ? 0 : Int_val (Field (beforev, 0)); + + if (aug_insert (t, path, label, before) == -1) + raise_error ("Augeas.insert"); + + CAMLreturn (Val_unit); +} + +/* val rm : t -> path -> int */ +CAMLprim value +ocaml_augeas_rm (value tv, value pathv) +{ + CAMLparam2 (tv, pathv); + augeas_t t = Augeas_t_val (tv); + char *path = String_val (pathv); + int r; + + r = aug_rm (t, path); + if (r == -1) + raise_error ("Augeas.rm"); + + CAMLreturn (Val_int (r)); +} + +/* val matches : t -> path -> path list */ +CAMLprim value +ocaml_augeas_match (value tv, value pathv) +{ + CAMLparam2 (tv, pathv); + CAMLlocal3 (rv, v, cons); + augeas_t t = Augeas_t_val (tv); + char *path = String_val (pathv); + char **matches; + int r, i; + + r = aug_match (t, path, &matches); + if (r == -1) + raise_error ("Augeas.matches"); + + /* Copy the paths to a list. */ + rv = Val_int (0); + for (i = 0; i < r; ++i) { + v = caml_copy_string (matches[i]); + free (matches[i]); + cons = caml_alloc (2, 0); + Field (cons, 1) = rv; + Field (cons, 0) = v; + rv = cons; + } + + free (matches); + + CAMLreturn (rv); +} + +/* val count_matches : t -> path -> int */ +CAMLprim value +ocaml_augeas_count_matches (value tv, value pathv) +{ + CAMLparam2 (tv, pathv); + augeas_t t = Augeas_t_val (tv); + char *path = String_val (pathv); + int r; + + r = aug_match (t, path, NULL); + if (r == -1) + raise_error ("Augeas.count_matches"); + + CAMLreturn (Val_int (r)); +} + +/* val save : t -> unit */ +CAMLprim value +ocaml_augeas_save (value tv) +{ + CAMLparam1 (tv); + augeas_t t = Augeas_t_val (tv); + + if (aug_save (t) == -1) + raise_error ("Augeas.save"); + + CAMLreturn (Val_unit); +} + +/* val load : t -> unit */ +CAMLprim value +ocaml_augeas_load (value tv) +{ + CAMLparam1 (tv); + augeas_t t = Augeas_t_val (tv); + + if (aug_load (t) == -1) + raise_error ("Augeas.load"); + + CAMLreturn (Val_unit); +} diff --git a/daemon/augeas.README b/daemon/augeas.README new file mode 100644 index 000000000..938dfd255 --- /dev/null +++ b/daemon/augeas.README @@ -0,0 +1,8 @@ +The files augeas-c.c, augeas.ml and augeas.mli come from the +ocaml-augeas library: + + http://git.annexia.org/?p=ocaml-augeas.git + +which is released under a compatible license. We try to keep them +identical, so if you make changes to these files then you must also +submit the changes to ocaml-augeas, and vice versa. \ No newline at end of file diff --git a/daemon/augeas.ml b/daemon/augeas.ml new file mode 100644 index 000000000..f556df0f1 --- /dev/null +++ b/daemon/augeas.ml @@ -0,0 +1,59 @@ +(* Augeas OCaml bindings + * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: augeas.ml,v 1.2 2008/05/06 10:48:20 rjones Exp $ + *) + +type t + +exception Error of string + +type flag + | AugSaveBackup + | AugSaveNewFile + | AugTypeCheck + | AugNoStdinc + | AugSaveNoop + | AugNoLoad + +type path = string + +type value = string + +external create : string -> string option -> flag list -> t + = "ocaml_augeas_create" +external close : t -> unit + = "ocaml_augeas_close" +external get : t -> path -> value option + = "ocaml_augeas_get" +external exists : t -> path -> bool + = "ocaml_augeas_exists" +external insert : t -> ?before:bool -> path -> string -> unit + = "ocaml_augeas_insert" +external rm : t -> path -> int + = "ocaml_augeas_rm" +external matches : t -> path -> path list + = "ocaml_augeas_match" +external count_matches : t -> path -> int + = "ocaml_augeas_count_matches" +external save : t -> unit + = "ocaml_augeas_save" +external load : t -> unit + = "ocaml_augeas_load" + +let () + Callback.register_exception "Augeas.Error" (Error "") diff --git a/daemon/augeas.mli b/daemon/augeas.mli new file mode 100644 index 000000000..64e824014 --- /dev/null +++ b/daemon/augeas.mli @@ -0,0 +1,95 @@ +(** Augeas OCaml bindings *) +(* Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: augeas.mli,v 1.2 2008/05/06 10:48:20 rjones Exp $ + *) + +type t + (** Augeas library handle. *) + +exception Error of string + (** This exception is thrown when the underlying Augeas library + returns an error. *) + +type flag + | AugSaveBackup (** Rename original with .augsave *) + | AugSaveNewFile (** Save changes to .augnew *) + | AugTypeCheck (** Type-check lenses *) + | AugNoStdinc + | AugSaveNoop + | AugNoLoad + (** Flags passed to the {!create} function. *) + +type path = string + (** A path expression. + + Note in future we may replace this with a type-safe path constructor. *) + +type value = string + (** A value. *) + +val create : string -> string option -> flag list -> t + (** [create root loadpath flags] creates an Augeas handle. + + [root] is a file system path describing the location + of the configuration files. + + [loadpath] is an optional colon-separated list of directories + which are searched for schema definitions. + + [flags] is a list of flags. *) + +val close : t -> unit + (** [close handle] closes the handle. + + You don't need to close handles explicitly with this function: + they will be finalized eventually by the garbage collector. + However calling this function frees up any resources used by the + underlying Augeas library immediately. + + Do not use the handle after closing it. *) + +val get : t -> path -> value option + (** [get t path] returns the value at [path], or [None] if there + is no value. *) + +val exists : t -> path -> bool + (** [exists t path] returns true iff there is a value at [path]. *) + +val insert : t -> ?before:bool -> path -> string -> unit + (** [insert t ?before path label] inserts [label] as a sibling + of [path]. By default it is inserted after [path], unless + [~before:true] is specified. *) + +val rm : t -> path -> int + (** [rm t path] removes all nodes matching [path]. + + Returns the number of nodes removed (which may be 0). *) + +val matches : t -> path -> path list + (** [matches t path] returns a list of path expressions + of all nodes matching [path]. *) + +val count_matches : t -> path -> int + (** [count_matches t path] counts the number of nodes matching + [path] but does not return them (see {!matches}). *) + +val save : t -> unit + (** [save t] saves all pending changes to disk. *) + +val load : t -> unit + (** [load t] loads files into the tree. *) diff --git a/daemon/chroot.ml b/daemon/chroot.ml index 40dfa1dde..0fddfcffa 100644 --- a/daemon/chroot.ml +++ b/daemon/chroot.ml @@ -32,7 +32,7 @@ let create ?(name = prog) chroot let f t func arg if verbose () then - eprintf "chroot: %s: running ‘%s’\n%!" t.chroot t.name; + eprintf "chroot: %s: running '%s'\n%!" t.chroot t.name; let rfd, wfd = pipe () in diff --git a/daemon/daemon_utils_tests.ml b/daemon/daemon_utils_tests.ml index 892509d89..b1f02de30 100644 --- a/daemon/daemon_utils_tests.ml +++ b/daemon/daemon_utils_tests.ml @@ -46,3 +46,18 @@ let () let () assert (proc_unmangle_path "\\040" = " "); assert (proc_unmangle_path "\\040\\040" = " ") + +(* Test unix_canonical_path. *) +let () + assert (unix_canonical_path "/" = "/"); + assert (unix_canonical_path "/usr" = "/usr"); + assert (unix_canonical_path "/usr/" = "/usr"); + assert (unix_canonical_path "/usr/local" = "/usr/local"); + assert (unix_canonical_path "///" = "/"); + assert (unix_canonical_path "///usr//local//" = "/usr/local"); + assert (unix_canonical_path "/usr///" = "/usr") + +(* Test utf16le_to_utf8. *) +let () + assert (utf16le_to_utf8 "\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00" = "Windows"); + assert (utf16le_to_utf8 "\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\xae\x00" = "Windows\xc2\xae") diff --git a/daemon/inspect.ml b/daemon/inspect.ml new file mode 100644 index 000000000..5d9c84d14 --- /dev/null +++ b/daemon/inspect.ml @@ -0,0 +1,253 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open Std_utils + +open Utils +open Inspect_types + +let rec inspect_os () + Mount.umount_all (); + + (* Iterate over all detected filesystems. Inspect each one in turn. *) + let fses = Listfs.list_filesystems () in + + let fses + filter_map ( + fun (mountable, vfs_type) -> + Inspect_fs.check_for_filesystem_on mountable vfs_type + ) fses in + if verbose () then ( + eprintf "inspect_os: fses:\n"; + List.iter (fun fs -> eprintf "\t%s\n" (string_of_fs fs)) fses; + flush stderr + ); + +(* XXX + (* The OS inspection information for CoreOS are gathered by inspecting + * multiple filesystems. Gather all the inspected information in the + * inspect_fs struct of the root filesystem. + *) + let fses = collect_coreos_inspection_info fses in + + (* Check if the same filesystem was listed twice as root in fses. + * This may happen for the *BSD root partition where an MBR partition + * is a shadow of the real root partition probably /dev/sda5 + *) + let fses = check_for_duplicated_bsd_root fses in + + (* For Linux guests with a separate /usr filesystem, merge some of the + * inspected information in that partition to the inspect_fs struct + * of the root filesystem. + *) + let fses = collect_linux_inspection_info fses in + *) + + (* Save what we found in a global variable. *) + Inspect_types.inspect_fses := fses; + + (* At this point we have, in the handle, a list of all filesystems + * found and data about each one. Now we assemble the list of + * filesystems which are root devices. + * + * Fall through to inspect_get_roots to do that. + *) + inspect_get_roots () + +and inspect_get_roots () + let fses = !Inspect_types.inspect_fses in + + let roots + filter_map ( + fun fs -> try Some (root_of_fs fs) with Invalid_argument _ -> None + ) fses in + if verbose () then ( + eprintf "inspect_get_roots: roots:\n"; + List.iter (fun root -> eprintf "%s" (string_of_root root)) roots; + flush stderr + ); + + (* Only return the list of mountables, since subsequent calls will + * be used to retrieve the other information. + *) + List.map (fun { root_location = { mountable = m } } -> m) roots + +and root_of_fs + function + | { fs_location = location; role = RoleRoot data } -> + { root_location = location; inspection_data = data } + | { role = (RoleUsr _ | RoleSwap | RoleOther) } -> + invalid_arg "root_of_fs" + +and inspect_get_mountpoints root_mountable + let root = search_for_root root_mountable in + let fstab = root.inspection_data.fstab in + + (* If no fstab information (Windows) return just the root. *) + if fstab = [] then + [ "/", root_mountable ] + else ( + filter_map ( + fun (mountable, mp) -> + if String.length mp > 0 && mp.[0] = '/' then + Some (mp, mountable) + else + None + ) fstab + ) + +and inspect_get_filesystems root_mountable + let root = search_for_root root_mountable in + let fstab = root.inspection_data.fstab in + + (* If no fstab information (Windows) return just the root. *) + if fstab = [] then + [ root_mountable ] + else + List.map fst fstab + +and inspect_get_format root + let root = search_for_root root in + match root.inspection_data.format with + | Some v -> string_of_format v + | None -> "unknown" + +and inspect_get_type root + let root = search_for_root root in + match root.inspection_data.os_type with + | Some v -> string_of_os_type v + | None -> "unknown" + +and inspect_get_distro root + let root = search_for_root root in + match root.inspection_data.distro with + | Some v -> string_of_distro v + | None -> "unknown" + +and inspect_get_package_format root + let root = search_for_root root in + match root.inspection_data.package_format with + | Some v -> string_of_package_format v + | None -> "unknown" + +and inspect_get_package_management root + let root = search_for_root root in + match root.inspection_data.package_management with + | Some v -> string_of_package_management v + | None -> "unknown" + +and inspect_get_product_name root + let root = search_for_root root in + match root.inspection_data.product_name with + | Some v -> v + | None -> "unknown" + +and inspect_get_product_variant root + let root = search_for_root root in + match root.inspection_data.product_variant with + | Some v -> v + | None -> "unknown" + +and inspect_get_major_version root + let root = search_for_root root in + match root.inspection_data.version with + | Some (major, _) -> major + | None -> 0 + +and inspect_get_minor_version root + let root = search_for_root root in + match root.inspection_data.version with + | Some (_, minor) -> minor + | None -> 0 + +and inspect_get_arch root + let root = search_for_root root in + match root.inspection_data.arch with + | Some v -> v + | None -> "unknown" + +and inspect_get_hostname root + let root = search_for_root root in + match root.inspection_data.hostname with + | Some v -> v + | None -> "unknown" + +and inspect_get_windows_systemroot root + let root = search_for_root root in + match root.inspection_data.windows_systemroot with + | Some v -> v + | None -> + failwith "not a Windows guest, or systemroot could not be determined" + +and inspect_get_windows_system_hive root + let root = search_for_root root in + match root.inspection_data.windows_system_hive with + | Some v -> v + | None -> + failwith "not a Windows guest, or system hive not found" + +and inspect_get_windows_software_hive root + let root = search_for_root root in + match root.inspection_data.windows_software_hive with + | Some v -> v + | None -> + failwith "not a Windows guest, or software hive not found" + +and inspect_get_windows_current_control_set root + let root = search_for_root root in + match root.inspection_data.windows_current_control_set with + | Some v -> v + | None -> + failwith "not a Windows guest, or CurrentControlSet could not be determined" + +and inspect_is_live root + let root = search_for_root root in + root.inspection_data.is_live_disk + +and inspect_is_netinst root + let root = search_for_root root in + root.inspection_data.is_netinst_disk + +and inspect_is_multipart root + let root = search_for_root root in + root.inspection_data.is_multipart_disk + +and inspect_get_drive_mappings root + let root = search_for_root root in + root.inspection_data.drive_mappings + +and search_for_root root + let fses = !Inspect_types.inspect_fses in + if fses = [] then + failwith "no inspection data: call guestfs_inspect_os first"; + + let root + try + List.find ( + function + | { fs_location = { mountable = m }; role = RoleRoot _ } -> root = m + | _ -> false + ) fses + with + Not_found -> + failwithf "%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os" + (Mountable.to_string root) in + + root_of_fs root diff --git a/daemon/inspect.mli b/daemon/inspect.mli new file mode 100644 index 000000000..29a1c1759 --- /dev/null +++ b/daemon/inspect.mli @@ -0,0 +1,41 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 inspect_os : unit -> Mountable.t list +val inspect_get_roots : unit -> Mountable.t list +val inspect_get_mountpoints : Mountable.t -> (string * Mountable.t) list +val inspect_get_filesystems : Mountable.t -> Mountable.t list +val inspect_get_format : Mountable.t -> string +val inspect_get_type : Mountable.t -> string +val inspect_get_distro : Mountable.t -> string +val inspect_get_package_format : Mountable.t -> string +val inspect_get_package_management : Mountable.t -> string +val inspect_get_product_name : Mountable.t -> string +val inspect_get_product_variant : Mountable.t -> string +val inspect_get_major_version : Mountable.t -> int +val inspect_get_minor_version : Mountable.t -> int +val inspect_get_arch : Mountable.t -> string +val inspect_get_hostname : Mountable.t -> string +val inspect_get_windows_systemroot : Mountable.t -> string +val inspect_get_windows_software_hive : Mountable.t -> string +val inspect_get_windows_system_hive : Mountable.t -> string +val inspect_get_windows_current_control_set : Mountable.t -> string +val inspect_get_drive_mappings : Mountable.t -> (string * string) list +val inspect_is_live : Mountable.t -> bool +val inspect_is_netinst : Mountable.t -> bool +val inspect_is_multipart : Mountable.t -> bool diff --git a/daemon/inspect_fs.ml b/daemon/inspect_fs.ml new file mode 100644 index 000000000..24ca18e93 --- /dev/null +++ b/daemon/inspect_fs.ml @@ -0,0 +1,395 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open Std_utils + +open Mountable +open Inspect_types +open Inspect_utils + +let rec check_for_filesystem_on mountable vfs_type + if verbose () then + eprintf "check_for_filesystem_on: %s (%s)\n%!" + (Mountable.to_string mountable) vfs_type; + + let role + let is_swap = vfs_type = "swap" in + if is_swap then + Some RoleSwap + else ( + (* If it's a whole device, see if it is an install ISO. *) + let is_whole_device = Devsparts.is_whole_device mountable.m_device in + let installer_role + if is_whole_device then + Inspect_fs_cd.check_installer_iso mountable.m_device + else + None in + match installer_role with + | Some _ as role -> role + | None -> + (* Try mounting the device. Ignore errors if we can't do this. *) + let mounted + if vfs_type = "ufs" then ( (* Hack for the *BSDs. *) + (* FreeBSD fs is a variant of ufs called ufs2 ... *) + try + Mount.mount_vfs (Some "ro,ufstype=ufs2") (Some "ufs") + mountable "/"; + true + with _ -> + (* while NetBSD and OpenBSD use another variant labeled 44bsd *) + try + Mount.mount_vfs (Some "ro,ufstype=44bsd") (Some "ufs") + mountable "/"; + true + with _ -> false + ) else ( + try Mount.mount_ro mountable "/"; + true + with _ -> false + ) in + if not mounted then None + else ( + let role = check_filesystem mountable is_whole_device in + Mount.umount_all (); + role + ) + ) in + + match role with + | None -> None + | Some role -> + Some { fs_location = { mountable = mountable; vfs_type = vfs_type }; + role = role } + +(* When this function is called, the filesystem is mounted on sysroot (). *) +and check_filesystem mountable is_whole_device + let is_only_partition + if is_whole_device then false + else is_only_partition mountable in + + let role = ref `Other in + let data = ref null_inspection_data in + + (* Grub /boot? *) + if Is.is_file "/grub/menu.lst" || + Is.is_file "/grub/grub.conf" || + Is.is_file "/grub2/grub.cfg" then + () + (* FreeBSD root? *) + else if Is.is_dir "/etc" && + Is.is_dir "/bin" && + Is.is_file "/etc/freebsd-update.conf" && + Is.is_file "/etc/fstab" then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLED }; + data := Inspect_fs_unix.check_freebsd_root !data + ) + (* NetBSD root? *) + else if Is.is_dir "/etc" && + Is.is_dir "/bin" && + Is.is_file "/netbsd" && + Is.is_file "/etc/fstab" && + Is.is_file "/etc/release" then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLED }; + data := Inspect_fs_unix.check_netbsd_root !data; + ) + (* OpenBSD root? *) + else if Is.is_dir "/etc" && + Is.is_dir "/bin" && + Is.is_file "/bsd" && + Is.is_file "/etc/fstab" && + Is.is_file "/etc/motd" then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLED }; + data := Inspect_fs_unix.check_openbsd_root !data; + ) + (* Hurd root? *) + else if Is.is_file "/hurd/console" && + Is.is_file "/hurd/hello" && + Is.is_file "/hurd/null" then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLED }; + data := Inspect_fs_unix.check_hurd_root !data; + ) + (* Minix root? *) + else if Is.is_dir "/etc" && + Is.is_dir "/bin" && + Is.is_file "/service/vm" && + Is.is_file "/etc/fstab" && + Is.is_file "/etc/version" then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLED }; + data := Inspect_fs_unix.check_minix_root !data; + ) + (* Linux root? *) + else if Is.is_dir "/etc" && + (Is.is_dir "/bin" || + is_symlink_to "/bin" "usr/bin") && + (Is.is_file "/etc/fstab" || + Is.is_file "/etc/hosts") then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLED }; + data := Inspect_fs_unix.check_linux_root mountable !data; + ) + (* CoreOS root? *) + else if Is.is_dir "/etc" && + Is.is_dir "/root" && + Is.is_dir "/home" && + Is.is_dir "/usr" && + Is.is_file "/etc/coreos/update.conf" then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLED }; + data := Inspect_fs_unix.check_coreos_root !data; + ) + (* Linux /usr/local? *) + else if Is.is_dir "/etc" && + Is.is_dir "/bin" && + Is.is_dir "/share" && + not (Is.is_dir "/local") && + not (Is.is_file "/etc/fstab") then + () + (* Linux /usr? *) + else if Is.is_dir "/etc" && + Is.is_dir "/bin" && + Is.is_dir "/share" && + Is.is_dir "/local" && + not (Is.is_file "/etc/fstab") then ( + data := Inspect_fs_unix.check_linux_usr !data; + ) + (* CoreOS /usr? *) + else if Is.is_dir "/bin" && + Is.is_dir "/share" && + Is.is_dir "/local" && + Is.is_dir "/share/coreos" then ( + data := Inspect_fs_unix.check_coreos_usr !data; + ) + (* Linux /var? *) + else if Is.is_dir "/log" && + Is.is_dir "/run" && + Is.is_dir "/spool" then + () + (* Windows root? *) + else if Inspect_fs_windows.is_windows_systemroot () then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLED }; + data := Inspect_fs_windows.check_windows_root !data; + ) + (* Windows volume with installed applications (but not root)? *) + else if is_dir_nocase "/System Volume Information" && + is_dir_nocase "/Program Files" then + () + (* Windows volume (but not root)? *) + else if is_dir_nocase "/System Volume Information" then + () + (* FreeDOS? *) + else if is_dir_nocase "/FDOS" && + is_file_nocase "/FDOS/FREEDOS.BSS" then ( + role := `Root; + data := { !data with + format = Some FORMAT_INSTALLED; + os_type = Some OS_TYPE_DOS; + distro = Some DISTRO_FREEDOS; + (* FreeDOS is a mix of 16 and 32 bit, but + * assume it requires a 32 bit i386 processor. + *) + arch = Some "i386" } + ) + (* Install CD/disk? + * + * Note that we checked (above) for an install ISO, but there are + * other types of install image (eg. USB keys) which that check + * wouldn't have picked up. + * + * Skip these checks if it's not a whole device (eg. CD) or the + * first partition (eg. bootable USB key). + *) + else if (is_whole_device || is_only_partition) && + Is.is_file "/isolinux/isolinux.cfg" || + Is.is_dir "/EFI/BOOT" || + Is.is_file "/images/install.img" || + Is.is_dir "/.disk" || + Is.is_file "/.discinfo" || + Is.is_file "/i386/txtsetup.sif" || + Is.is_file "/amd64/txtsetup.sif" || + Is.is_file "/freedos/freedos.ico" || + Is.is_file "/boot/loader.rc" then ( + role := `Root; + data := { !data with format = Some FORMAT_INSTALLER }; + data := Inspect_fs_cd.check_installer_root !data; + ); + + (* The above code should have set [data.os_type] and [data.distro] + * fields, so we can now guess the package management system. + *) + let data = !data in + let data = { data with + package_format = check_package_format data; + package_management = check_package_management data } in + match !role with + | `Root -> Some (RoleRoot data) + | `Usr -> Some (RoleUsr data) + | `Other -> Some RoleOther + +(* The mountable is the first and only partition on a device + * with a single device. + *) +and is_only_partition = function + | { m_type = MountablePath | MountableBtrfsVol _ } -> false + | { m_type = MountableDevice; m_device = device } -> + let partnum, nr_partitions = get_partition_context device in + partnum = 1 && nr_partitions = 1 + +and get_partition_context partition + let partnum = Devsparts.part_to_partnum partition in + let device = Devsparts.part_to_dev partition in + let nr_partitions = List.length (Parted.part_list device) in + partnum, nr_partitions + +and is_symlink_to file wanted_target + if not (Is.is_symlink file) then false + else Link.readlink file = wanted_target + +(* At the moment, package format and package management are just a + * simple function of the [distro] and [version[0]] fields, so these + * can never return an error. We might be cleverer in future. + *) +and check_package_format { distro = distro } + match distro with + | None -> None + | Some DISTRO_FEDORA + | Some DISTRO_MEEGO + | Some DISTRO_REDHAT_BASED + | Some DISTRO_RHEL + | Some DISTRO_MAGEIA + | Some DISTRO_MANDRIVA + | Some DISTRO_SUSE_BASED + | Some DISTRO_OPENSUSE + | Some DISTRO_SLES + | Some DISTRO_CENTOS + | Some DISTRO_SCIENTIFIC_LINUX + | Some DISTRO_ORACLE_LINUX + | Some DISTRO_ALTLINUX -> + Some PACKAGE_FORMAT_RPM + | Some DISTRO_DEBIAN + | Some DISTRO_UBUNTU + | Some DISTRO_LINUX_MINT -> + Some PACKAGE_FORMAT_DEB + | Some DISTRO_ARCHLINUX -> + Some PACKAGE_FORMAT_PACMAN + | Some DISTRO_GENTOO -> + Some PACKAGE_FORMAT_EBUILD + | Some DISTRO_PARDUS -> + Some PACKAGE_FORMAT_PISI + | Some DISTRO_ALPINE_LINUX -> + Some PACKAGE_FORMAT_APK + | Some DISTRO_VOID_LINUX -> + Some PACKAGE_FORMAT_XBPS + | Some DISTRO_SLACKWARE + | Some DISTRO_TTYLINUX + | Some DISTRO_COREOS + | Some DISTRO_WINDOWS + | Some DISTRO_BUILDROOT + | Some DISTRO_CIRROS + | Some DISTRO_FREEDOS + | Some DISTRO_FREEBSD + | Some DISTRO_NETBSD + | Some DISTRO_OPENBSD + | Some DISTRO_FRUGALWARE + | Some DISTRO_PLD_LINUX -> + None + +and check_package_management { distro = distro; version = version } + let major = match version with None -> 0 | Some (major, _) -> major in + match distro with + | None -> None + + | Some DISTRO_MEEGO -> + Some PACKAGE_MANAGEMENT_YUM + + | Some DISTRO_FEDORA -> + (* If Fedora >= 22 and dnf is installed, say "dnf". *) + if major >= 22 && Is.is_file ~followsymlinks:true "/usr/bin/dnf" then + Some PACKAGE_MANAGEMENT_DNF + else if major >= 1 then + Some PACKAGE_MANAGEMENT_YUM + else + (* Probably parsing the release file failed, see RHBZ#1332025. *) + None + + | Some DISTRO_REDHAT_BASED + | Some DISTRO_RHEL + | Some DISTRO_CENTOS + | Some DISTRO_SCIENTIFIC_LINUX + | Some DISTRO_ORACLE_LINUX -> + if major >= 8 then + Some PACKAGE_MANAGEMENT_DNF + else if major >= 5 then + Some PACKAGE_MANAGEMENT_YUM + else if major >= 2 then + Some PACKAGE_MANAGEMENT_UP2DATE + else + (* Probably parsing the release file failed, see RHBZ#1332025. *) + None + + | Some DISTRO_DEBIAN + | Some DISTRO_UBUNTU + | Some DISTRO_LINUX_MINT + | Some DISTRO_ALTLINUX -> + Some PACKAGE_MANAGEMENT_APT + + | Some DISTRO_ARCHLINUX -> + Some PACKAGE_MANAGEMENT_PACMAN + + | Some DISTRO_GENTOO -> + Some PACKAGE_MANAGEMENT_PORTAGE + + | Some DISTRO_PARDUS -> + Some PACKAGE_MANAGEMENT_PISI + + | Some DISTRO_MAGEIA + | Some DISTRO_MANDRIVA -> + Some PACKAGE_MANAGEMENT_URPMI + + | Some DISTRO_SUSE_BASED + | Some DISTRO_OPENSUSE + | Some DISTRO_SLES -> + Some PACKAGE_MANAGEMENT_ZYPPER + + | Some DISTRO_ALPINE_LINUX -> + Some PACKAGE_MANAGEMENT_APK + + | Some DISTRO_VOID_LINUX -> + Some PACKAGE_MANAGEMENT_XBPS; + + | Some DISTRO_SLACKWARE + | Some DISTRO_TTYLINUX + | Some DISTRO_COREOS + | Some DISTRO_WINDOWS + | Some DISTRO_BUILDROOT + | Some DISTRO_CIRROS + | Some DISTRO_FREEDOS + | Some DISTRO_FREEBSD + | Some DISTRO_NETBSD + | Some DISTRO_OPENBSD + | Some DISTRO_FRUGALWARE + | Some DISTRO_PLD_LINUX -> + None + diff --git a/daemon/inspect_fs.mli b/daemon/inspect_fs.mli new file mode 100644 index 000000000..53ea01587 --- /dev/null +++ b/daemon/inspect_fs.mli @@ -0,0 +1,23 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 check_for_filesystem_on : Mountable.t -> string -> + Inspect_types.fs option +(** [check_for_filesystem_on cmdline mountable vfs_type] inspects + [mountable] looking for a single mountpoint from an operating + system. *) diff --git a/daemon/inspect_fs_cd.ml b/daemon/inspect_fs_cd.ml new file mode 100644 index 000000000..d16ee8095 --- /dev/null +++ b/daemon/inspect_fs_cd.ml @@ -0,0 +1,23 @@ +(* guestfs-inspection + * Copyright (C) 2009-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. + *) + +let check_installer_iso device + None (* XXX *) + +let check_installer_root data + data (* XXX *) diff --git a/daemon/inspect_fs_cd.mli b/daemon/inspect_fs_cd.mli new file mode 100644 index 000000000..052753336 --- /dev/null +++ b/daemon/inspect_fs_cd.mli @@ -0,0 +1,25 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 check_installer_iso : string -> Inspect_types.role option +(** Check the named device to see if it could be an install ISO image. + If so, returns [Some (RoleRoot ...)]. *) + +val check_installer_root : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the install CD filesystem mounted on sysroot. *) diff --git a/daemon/inspect_fs_unix.ml b/daemon/inspect_fs_unix.ml new file mode 100644 index 000000000..f1856e61e --- /dev/null +++ b/daemon/inspect_fs_unix.ml @@ -0,0 +1,563 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open C_utils +open Std_utils + +open Utils +open Inspect_types +open Inspect_utils + +let re_fedora = Str.regexp "Fedora release \\([0-9]+\\)" +let re_rhel_old = Str.regexp "Red Hat.*release \\([0-9]+\\).*Update \\([0-9]+\\)" +let re_rhel = Str.regexp "Red Hat.*release \\([0-9]+\\)\\.\\([0-9]+\\)" +let re_rhel_no_minor = Str.regexp "Red Hat.*release \\([0-9]+\\)" +let re_centos_old = Str.regexp "CentOS.*release \\([0-9]+\\).*Update \\([0-9]+\\)" +let re_centos = Str.regexp "CentOS.*release \\([0-9]+\\)\\.\\([0-9]+\\)" +let re_centos_no_minor = Str.regexp "CentOS.*release \\([0-9]+\\)" +let re_scientific_linux_old + Str.regexp "Scientific Linux.*release \\([0-9]+\\).*Update \\([0-9]+\\)" +let re_scientific_linux + Str.regexp "Scientific Linux.*release \\([0-9]+\\)\\.\\([0-9]+\\)" +let re_scientific_linux_no_minor + Str.regexp "Scientific Linux.*release \\([0-9]+\\)" +let re_oracle_linux_old + Str.regexp "Oracle Linux.*release \\([0-9]+\\).*Update \\([0-9]+\\)" +let re_oracle_linux + Str.regexp "Oracle Linux.*release \\([0-9]+\\)\\.\\([0-9]+\\)" +let re_oracle_linux_no_minor = Str.regexp "Oracle Linux.*release \\([0-9]+\\)" +let re_netbsd = Str.regexp "^NetBSD \\([0-9]+\\)\\.\\([0-9]+\\)" +let re_opensuse = Str.regexp "^\\(openSUSE|SuSE Linux|SUSE LINUX\\) " +let re_sles = Str.regexp "^SUSE \\(Linux|LINUX\\) Enterprise " +let re_nld = Str.regexp "^Novell Linux Desktop " +let re_sles_version = Str.regexp "^VERSION = \\([0-9]+\\)" +let re_sles_patchlevel = Str.regexp "^PATCHLEVEL = \\([0-9]+\\)" +let re_minix = Str.regexp "^\\([0-9]+\\)\\.\\([0-9]+\\)\\(\\.\\([0-9]+\\)\\)?" +let re_openbsd = Str.regexp "^OpenBSD \\([0-9]+|\\?\\)\\.\\([0-9]+|\\?\\)" +let re_frugalware = Str.regexp "Frugalware \\([0-9]+\\)\\.\\([0-9]+\\)" +let re_pldlinux = Str.regexp "\\([0-9]+\\)\\.\\([0-9]+\\) PLD Linux" + +let arch_binaries + [ "/bin/bash"; "/bin/ls"; "/bin/echo"; "/bin/rm"; "/bin/sh" ] + +let rec check_linux_root mountable data + let os_type = OS_TYPE_LINUX in + let data = { data with os_type = Some os_type } in + + let tests = [ + (* systemd distros include /etc/os-release which is reasonably + * standardized. This entry should be first. + *) + "/etc/os-release", parse_os_release; + (* LSB is also a reasonable standard. This entry should be second. *) + "/etc/lsb-release", parse_lsb_release; + + (* Now we enter the Wild West ... *) + + (* RHEL-based distros include a [/etc/redhat-release] file, hence their + * checks need to be performed before the Red-Hat one. + *) + "/etc/oracle-release", parse_generic ~rex:re_oracle_linux_old + DISTRO_ORACLE_LINUX; + "/etc/oracle-release", parse_generic ~rex:re_oracle_linux + DISTRO_ORACLE_LINUX; + "/etc/oracle-release", parse_generic ~rex:re_oracle_linux_no_minor + DISTRO_ORACLE_LINUX; + "/etc/centos-release", parse_generic ~rex:re_centos_old + DISTRO_CENTOS; + "/etc/centos-release", parse_generic ~rex:re_centos + DISTRO_CENTOS; + "/etc/centos-release", parse_generic ~rex:re_centos_no_minor + DISTRO_CENTOS; + "/etc/altlinux-release", parse_generic DISTRO_ALTLINUX; + "/etc/redhat-release", parse_generic ~rex:re_fedora + DISTRO_FEDORA; + "/etc/redhat-release", parse_generic ~rex:re_rhel_old + DISTRO_RHEL; + "/etc/redhat-release", parse_generic ~rex:re_rhel + DISTRO_RHEL; + "/etc/redhat-release", parse_generic ~rex:re_rhel_no_minor + DISTRO_RHEL; + "/etc/redhat-release", parse_generic ~rex:re_centos_old + DISTRO_CENTOS; + "/etc/redhat-release", parse_generic ~rex:re_centos + DISTRO_CENTOS; + "/etc/redhat-release", parse_generic ~rex:re_centos_no_minor + DISTRO_CENTOS; + "/etc/redhat-release", parse_generic ~rex:re_scientific_linux_old + DISTRO_SCIENTIFIC_LINUX; + "/etc/redhat-release", parse_generic ~rex:re_scientific_linux + DISTRO_SCIENTIFIC_LINUX; + "/etc/redhat-release", parse_generic ~rex:re_scientific_linux_no_minor + DISTRO_SCIENTIFIC_LINUX; + + (* If there's an /etc/redhat-release file, but nothing above + * matches, then it is a generic Red Hat-based distro. + *) + "/etc/redhat-release", parse_generic DISTRO_REDHAT_BASED; + "/etc/redhat-release", + (fun _ data -> { data with distro = Some DISTRO_REDHAT_BASED }); + + "/etc/debian_version", parse_generic DISTRO_DEBIAN; + "/etc/pardus-release", parse_generic DISTRO_PARDUS; + + (* /etc/arch-release file is empty and I can't see a way to + * determine the actual release or product string. + *) + "/etc/arch-release", + (fun _ data -> { data with distro = Some DISTRO_ARCHLINUX }); + + "/etc/gentoo-release", parse_generic DISTRO_GENTOO; + "/etc/meego-release", parse_generic DISTRO_MEEGO; + "/etc/slackware-version", parse_generic DISTRO_SLACKWARE; + "/etc/ttylinux-target", parse_generic DISTRO_TTYLINUX; + + "/etc/SuSE-release", parse_suse_release; + "/etc/SuSE-release", + (fun _ data -> { data with distro = Some DISTRO_SUSE_BASED }); + + "/etc/cirros/version", parse_generic DISTRO_CIRROS; + "/etc/br-version", + (fun release_file data -> + let distro + if Is.is_file ~followsymlinks:true "/usr/share/cirros/logo" then + DISTRO_CIRROS + else + DISTRO_BUILDROOT in + (* /etc/br-version has the format YYYY.MM[-git/hg/svn release] *) + parse_generic distro release_file data); + + "/etc/alpine-release", parse_generic DISTRO_ALPINE_LINUX; + "/etc/frugalware-release", parse_generic ~rex:re_frugalware + DISTRO_FRUGALWARE; + "/etc/pld-release", parse_generic ~rex:re_pldlinux + DISTRO_PLD_LINUX; + ] in + + let rec loop = function + | (release_file, parse_fun) :: tests -> + if verbose () then + eprintf "check_linux_root: checking %s\n%!" release_file; + (try + if not (Is.is_file ~followsymlinks:true release_file) then + raise Not_found; + parse_fun release_file data + with + Not_found -> loop tests) + | [] -> data + in + let data = loop tests in + + let data = { + data with + arch = check_architecture (); + fstab + Inspect_fs_unix_fstab.check_fstab ~mdadm_conf:true mountable os_type; + hostname = check_hostname_linux (); + } in + + data + +(* Parse a os-release file. + * + * Only few fields are parsed, falling back to the usual detection if we + * cannot read all of them. + * + * For the format of os-release, see also: + * http://www.freedesktop.org/software/systemd/man/os-release.html + *) +and parse_os_release release_file data + let chroot = Chroot.create ~name:"parse_os_release" (Sysroot.sysroot ()) in + let lines + Chroot.f chroot ( + fun () -> + if not (is_small_file release_file) then ( + eprintf "%s: not a regular file or too large\n" release_file; + raise Not_found + ); + read_whole_file release_file + ) () in + let lines = String.nsplit "\n" lines in + + let data = List.fold_left ( + fun data line -> + let line = String.trim line in + if line = "" || line.[0] = '#' then + data + else ( + let key, value = String.split "=" line in + let value + let n = String.length value in + if n >= 2 && value.[0] = '"' && value.[n-1] = '"' then + String.sub value 1 (n-2) + else + value in + if key = "ID" then ( + let distro = distro_of_os_release_id value in + match distro with + | Some _ as distro -> { data with distro = distro } + | None -> data + ) + else if key = "PRETTY_NAME" then + { data with product_name = Some value } + else if key = "VERSION_ID" then + parse_version_from_major_minor value data + else + data + ) + ) data lines in + + (* os-release in Debian and CentOS does not provide the full + * version number (VERSION_ID), just the major part of it. If + * we detect that situation then bail out and use the release + * files instead. + *) + (match data with + | { distro = Some (DISTRO_DEBIAN|DISTRO_CENTOS); version = Some (_, 0) } -> + raise Not_found + | _ -> () + ); + + data + +(* ID="fedora" => Some DISTRO_FEDORA *) +and distro_of_os_release_id = function + | "alpine" -> Some DISTRO_ALPINE_LINUX + | "altlinux" -> Some DISTRO_ALTLINUX + | "arch" -> Some DISTRO_ARCHLINUX + | "centos" -> Some DISTRO_CENTOS + | "coreos" -> Some DISTRO_COREOS + | "debian" -> Some DISTRO_DEBIAN + | "fedora" -> Some DISTRO_FEDORA + | "frugalware" -> Some DISTRO_FRUGALWARE + | "mageia" -> Some DISTRO_MAGEIA + | "opensuse" -> Some DISTRO_OPENSUSE + | "pld" -> Some DISTRO_PLD_LINUX + | "rhel" -> Some DISTRO_RHEL + | "sles" | "sled" -> Some DISTRO_SLES + | "ubuntu" -> Some DISTRO_UBUNTU + | "void" -> Some DISTRO_VOID_LINUX + | value -> + eprintf "/etc/os-release: unknown ID=%s\n" value; + None + +(* Ubuntu has /etc/lsb-release containing: + * DISTRIB_ID=Ubuntu # Distro + * DISTRIB_RELEASE=10.04 # Version + * DISTRIB_CODENAME=lucid + * DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS" # Product name + * + * [Ubuntu-derived ...] Linux Mint was found to have this: + * DISTRIB_ID=LinuxMint + * DISTRIB_RELEASE=10 + * DISTRIB_CODENAME=julia + * DISTRIB_DESCRIPTION="Linux Mint 10 Julia" + * Linux Mint also has /etc/linuxmint/info with more information, + * but we can use the LSB file. + * + * Mandriva has: + * LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch + * DISTRIB_ID=MandrivaLinux + * DISTRIB_RELEASE=2010.1 + * DISTRIB_CODENAME=Henry_Farman + * DISTRIB_DESCRIPTION="Mandriva Linux 2010.1" + * Mandriva also has a normal release file called /etc/mandriva-release. + * + * CoreOS has a /etc/lsb-release link to /usr/share/coreos/lsb-release containing: + * DISTRIB_ID=CoreOS + * DISTRIB_RELEASE=647.0.0 + * DISTRIB_CODENAME="Red Dog" + * DISTRIB_DESCRIPTION="CoreOS 647.0.0" + *) +and parse_lsb_release release_file data + let chroot = Chroot.create ~name:"parse_lsb_release" (Sysroot.sysroot ()) in + let lines + Chroot.f chroot ( + fun () -> + if not (is_small_file release_file) then ( + eprintf "%s: not a regular file or too large\n" release_file; + raise Not_found + ); + read_whole_file release_file + ) () in + let lines = String.nsplit "\n" lines in + + let data = List.fold_left ( + fun data line -> + if data.distro = None && line = "DISTRIB_ID=Ubuntu" then + { data with distro = Some DISTRO_UBUNTU } + else if data.distro = None && line = "DISTRIB_ID=LinuxMint" then + { data with distro = Some DISTRO_LINUX_MINT } + else if data.distro = None && line = "DISTRIB_ID=\"Mageia\"" then + { data with distro = Some DISTRO_MAGEIA } + else if data.distro = None && line = "DISTRIB_ID=CoreOS" then + { data with distro = Some DISTRO_COREOS } + else if String.is_prefix line "DISTRIB_RELEASE=" then + parse_version_from_major_minor line data + else if String.is_prefix line "DISTRIB_DESCRIPTION=\"" || + String.is_prefix line "DISTRIB_DESCRIPTION='" then ( + let n = String.length line in + let product_name = String.sub line 21 (n-20) in + { data with product_name = Some product_name } + ) + else if String.is_prefix line "DISTRIB_DESCRIPTION=" then ( + let n = String.length line in + let product_name = String.sub line 20 (n-20) in + { data with product_name = Some product_name } + ) + else + data + ) data lines in + + data + +and parse_suse_release release_file data + let chroot = Chroot.create ~name:"parse_suse_release" (Sysroot.sysroot ()) in + let lines + Chroot.f chroot ( + fun () -> + if not (is_small_file release_file) then ( + eprintf "%s: not a regular file or too large\n" release_file; + raise Not_found + ); + read_whole_file release_file + ) () in + let lines = String.nsplit "\n" lines in + + if lines = [] then raise Not_found; + + (* First line is dist release name. *) + let product_name = List.hd lines in + let data = { + data with + product_name = Some product_name + } in + + (* Match SLES first because openSuSE regex overlaps some SLES + * release strings. + *) + if Str.string_match re_sles product_name 0 || + Str.string_match re_nld product_name 0 then ( + (* Second line contains version string. *) + let major + if List.length lines >= 2 then ( + let line = List.nth lines 1 in + if Str.string_match re_sles_version line 0 then + Some (int_of_string (Str.matched_group 1 line)) + else None + ) + else None in + + (* Third line contains service pack string. *) + let minor + if List.length lines >= 3 then ( + let line = List.nth lines 2 in + if Str.string_match re_sles_patchlevel line 0 then + Some (int_of_string (Str.matched_group 1 line)) + else None + ) + else None in + + let version + match major, minor with + | Some major, Some minor -> Some (major, minor) + | Some major, None -> Some (major, 0) + | None, Some _ | None, None -> None in + + { data with + distro = Some DISTRO_SLES; + version = version } + ) + else if Str.string_match re_opensuse product_name 0 then ( + (* Second line contains version string. *) + let data + if List.length lines >= 2 then ( + let line = List.nth lines 1 in + parse_version_from_major_minor line data + ) + else data in + + { data with distro = Some DISTRO_OPENSUSE } + ) + else + data + +(* Parse any generic /etc/x-release file. + * + * The optional regular expression which may match 0, 1 or 2 + * substrings, which are used as the major and minor numbers. + * + * The fixed distro is always set, and the product name is + * set to the first line of the release file. + *) +and parse_generic ?rex distro release_file data + let chroot = Chroot.create ~name:"parse_generic" (Sysroot.sysroot ()) in + let product_name + Chroot.f chroot ( + fun () -> + if not (is_small_file release_file) then ( + eprintf "%s: not a regular file or too large\n" release_file; + raise Not_found + ); + read_first_line_from_file release_file + ) () in + if product_name = "" then + raise Not_found; + + let data + { data with product_name = Some product_name; + distro = Some distro } in + + match rex with + | Some rex -> + (* If ~rex was supplied, then it must match the release file, + * else the parsing fails. + *) + if not (Str.string_match rex product_name 0) then + raise Not_found; + + (* Although it's not documented, matched_group raises + * Invalid_argument if called with an unknown group number. + *) + let major + try Some (int_of_string (Str.matched_group 1 product_name)) + with Not_found | Invalid_argument _ | Failure _ -> None in + let minor + try Some (int_of_string (Str.matched_group 2 product_name)) + with Not_found | Invalid_argument _ | Failure _ -> None in + (match major, minor with + | None, None -> data + | None, Some _ -> data + | Some major, None -> { data with version = Some (major, 0) } + | Some major, Some minor -> { data with version = Some (major, minor) } + ) + + | None -> + (* However if no ~rex was supplied, then we make a best + * effort attempt to parse a version number, but don't + * fail if one cannot be found. + *) + parse_version_from_major_minor product_name data + +and check_architecture () + let rec loop = function + | [] -> None + | bin :: bins -> + (* Allow symlinks when checking the binaries:,so in case they are + * relative ones (which can be resolved within the same partition), + * then we can check the architecture of their target. + *) + if Is.is_file ~followsymlinks:true bin then ( + try + let resolved = Realpath.realpath bin in + let arch = Filearch.file_architecture resolved in + Some arch + with exn -> + if verbose () then + eprintf "check_architecture: %s: %s\n%!" bin + (Printexc.to_string exn); + loop bins + ) + else + loop bins + in + loop arch_binaries + +and check_hostname_linux () + let chroot + Chroot.create ~name:"check_hostname_linux" (Sysroot.sysroot ()) in + + (* Red Hat-derived would be in /etc/sysconfig/network or + * /etc/hostname (RHEL 7+, F18+). Debian-derived in the file + * /etc/hostname. Very old Debian and SUSE use /etc/HOSTNAME. + * It's best to just look for each of these files in turn, rather + * than try anything clever based on distro. + *) + let rec loop = function + | [] -> None + | filename :: rest -> + let hostname + Chroot.f chroot ( + fun () -> + if not (is_small_file filename) then ( + eprintf "%s: not a regular file or too large\n" filename; + None + ) + else + Some (read_first_line_from_file filename) + ) () in + match hostname with + | Some hostname -> Some hostname + | None -> loop rest + in + let hostname = loop [ "/etc/HOSTNAME"; "/etc/hostname" ] in + match hostname with + | Some hostname -> Some hostname + | None -> + if Is.is_file "/etc/sysconfig/network" then + Inspect_utils.with_augeas ["/etc/sysconfig/network"] + check_hostname_from_sysconfig_network + else + None + +(* Parse the hostname from /etc/sysconfig/network. This must be + * called from the 'with_augeas' wrapper. Note that F18+ and + * RHEL7+ use /etc/hostname just like Debian. + *) +and check_hostname_from_sysconfig_network aug + (* Errors here are not fatal (RHBZ#726739), since it could be + * just missing HOSTNAME field in the file. + *) + Augeas.get aug "/files/etc/sysconfig/network/HOSTNAME" + +let check_linux_usr data + (* XXX *) data + +let check_coreos_root data + (* XXX *) data + +let check_coreos_usr data + (* XXX *) data + +let check_freebsd_root data + (* XXX *) data + +and check_hostname_freebsd () + (* XXX *) None + +let check_netbsd_root data + (* XXX *) data + +let rec check_openbsd_root data + (* XXX *) data + +and check_hostname_openbsd () + (* XXX *) None + +let check_hurd_root data + (* XXX *) data + +and check_hostname_hurd () = check_hostname_linux () + +let rec check_minix_root data + (* XXX *) data + +and check_hostname_minix () + (* XXX *) None diff --git a/daemon/inspect_fs_unix.mli b/daemon/inspect_fs_unix.mli new file mode 100644 index 000000000..7ca1e2fbb --- /dev/null +++ b/daemon/inspect_fs_unix.mli @@ -0,0 +1,53 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 check_coreos_usr : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the CoreOS [/usr] filesystem mounted on sysroot. *) + +val check_coreos_root : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the CoreOS filesystem mounted on sysroot. *) + +val check_freebsd_root : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the FreeBSD filesystem mounted on sysroot. *) + +val check_hurd_root : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the Hurd filesystem mounted on sysroot. *) + +val check_linux_usr : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the Linux [/usr] filesystem mounted on sysroot. *) + +val check_linux_root : Mountable.t -> Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the Linux filesystem mounted on sysroot. *) + +val check_minix_root : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the Minix filesystem mounted on sysroot. *) + +val check_netbsd_root : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the NetBSD filesystem mounted on sysroot. *) + +val check_openbsd_root : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the OpenBSD filesystem mounted on sysroot. *) diff --git a/daemon/inspect_fs_unix_fstab.ml b/daemon/inspect_fs_unix_fstab.ml new file mode 100644 index 000000000..c566a7618 --- /dev/null +++ b/daemon/inspect_fs_unix_fstab.ml @@ -0,0 +1,518 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open C_utils +open Std_utils + +open Utils +open Inspect_types +open Inspect_utils + +let re_cciss = Str.regexp "^/dev/\\(cciss/c[0-9]+d[0-9]+\\)\\(p\\([0-9]+\\)\\)?$" +let re_diskbyid = Str.regexp "^/dev/disk/by-id/.*-part\\([0-9]+\\)$" +let re_freebsd_gpt = Str.regexp "^/dev/\\(ada{0,1}|vtbd\\)\\([0-9]+\\)p\\([0-9]+\\)$" +let re_freebsd_mbr = Str.regexp "^/dev/\\(ada{0,1}|vtbd\\)\\([0-9]+\\)s\\([0-9]+\\)\\([a-z]\\)$" +let re_hurd_dev = Str.regexp "^/dev/\\(h\\)d\\([0-9]+\\)s\\([0-9]+\\)$" +let re_mdN = Str.regexp "^/dev/md[0-9]+$" +let re_netbsd_dev = Str.regexp "^/dev/\\(l|s\\)d\\([0-9]\\)\\([a-z]\\)$" +let re_openbsd_dev = Str.regexp "^/dev/\\(s|w\\)d\\([0-9]\\)\\([a-z]\\)$" +let re_openbsd_duid = Str.regexp "^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\.\\([a-z]\\)" +let re_xdev = Str.regexp "^/dev/\\(h|s|v|xv\\)d\\([a-z]+\\)\\([0-9]*\\)$" + +let rec check_fstab ?(mdadm_conf = false) (root_mountable : Mountable.t) + os_type + let configfiles + "/etc/fstab" :: if mdadm_conf then ["/etc/mdadm.conf"] else [] in + + with_augeas configfiles (check_fstab_aug mdadm_conf root_mountable os_type) + +and check_fstab_aug mdadm_conf root_mountable os_type aug + (* Generate a map of MD device paths listed in /etc/mdadm.conf + * to MD device paths in the guestfs appliance. + *) + let md_map = if mdadm_conf then map_md_devices aug else StringMap.empty in + + let path = "/files/etc/fstab/*[label() != '#comment']" in + let entries = Augeas.matches aug path in + filter_map (check_fstab_entry md_map root_mountable os_type aug) entries + +and check_fstab_entry md_map root_mountable os_type aug entry + if verbose () then + eprintf "check_fstab_entry: augeas path: %s\n%!" entry; + + let is_bsd + os_type = OS_TYPE_FREEBSD || + os_type = OS_TYPE_NETBSD || + os_type = OS_TYPE_OPENBSD in + + let spec = Augeas.get aug (entry ^ "/spec") in + let mp = Augeas.get aug (entry ^ "/file") in + let vfstype = Augeas.get aug (entry ^ "/vfstype") in + + match spec, mp, vfstype with + | None, _, _ | Some _, None, _ | Some _, Some _, None -> None + | Some spec, Some mp, Some vfstype -> + if verbose () then + eprintf "check_fstab_entry: spec=%s mp=%s vfstype=%s\n%!" + spec mp vfstype; + + (* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. + * + * /dev/iso9660/FREEBSD_INSTALL can be found in FreeBSD's + * installation discs. + *) + if (String.is_prefix spec "/dev/fd" && + String.length spec >= 8 && Char.isdigit spec.[7]) || + (String.is_prefix spec "/dev/cd" && + String.length spec >= 8 && Char.isdigit spec.[7]) || + spec = "/dev/floppy" || + spec = "/dev/cdrom" || + String.is_prefix spec "/dev/iso9660/" then + None + else ( + (* Canonicalize the path, so "///usr//local//" -> "/usr/local" *) + let mp = unix_canonical_path mp in + + (* Ignore certain mountpoints. *) + if String.is_prefix mp "/dev/" || + mp = "/dev" || + String.is_prefix mp "/media/" || + String.is_prefix mp "/proc/" || + mp = "/proc" || + String.is_prefix mp "/selinux/" || + mp = "/selinux" || + String.is_prefix mp "/sys/" || + mp = "/sys" then + None + else ( + let mountable + (* Resolve UUID= and LABEL= to the actual device. *) + if String.is_prefix spec "UUID=" then ( + let uuid = String.sub spec 5 (String.length spec - 5) in + let uuid = shell_unquote uuid in + Some (Mountable.of_device (Findfs.findfs_uuid uuid)) + ) + else if String.is_prefix spec "LABEL=" then ( + let label = String.sub spec 6 (String.length spec - 6) in + let label = shell_unquote label in + Some (Mountable.of_device (Findfs.findfs_label label)) + ) + (* Resolve /dev/root to the current device. + * Do the same for the / partition of the *BSD + * systems, since the BSD -> Linux device + * translation is not straight forward. + *) + else if spec = "/dev/root" || (is_bsd && mp = "/") then + Some root_mountable + (* Resolve guest block device names. *) + else if String.is_prefix spec "/dev/" then + Some (resolve_fstab_device spec md_map os_type) + (* In OpenBSD's fstab you can specify partitions + * on a disk by appending a period and a partition + * letter to a Disklable Unique Identifier. The + * DUID is a 16 hex digit field found in the + * OpenBSD's altered BSD disklabel. For more info + * see here: + * http://www.openbsd.org/faq/faq14.html#intro + *) + else if Str.string_match re_openbsd_duid spec 0 then ( + let part = Str.matched_group 1 spec in + (* We cannot peep into disklabels, we can only + * assume that this is the first disk. + *) + let device = sprintf "/dev/sd0%s" part in + Some (resolve_fstab_device device md_map os_type) + ) + (* Ignore "/.swap" (Pardus) and pseudo-devices + * like "tmpfs". If we haven't resolved the device + * successfully by this point, just ignore it. + *) + else + None in + + match mountable with + | None -> None + | Some mountable -> + let mountable + if vfstype = "btrfs" then + get_btrfs_mountable aug entry mountable + else mountable in + + Some (mountable, mp) + ) + ) + +(* If an fstab entry corresponds to a btrfs filesystem, look for + * the 'subvol' option and if it is present then return a btrfs + * subvolume (else return the whole device). + *) +and get_btrfs_mountable aug entry mountable + let device + match mountable with + | { Mountable.m_type = Mountable.MountableDevice; m_device = device } -> + Some device + | { Mountable.m_type + (Mountable.MountablePath|Mountable.MountableBtrfsVol _) } -> + None in + + match device with + | None -> mountable + | Some device -> + let opts = Augeas.matches aug (entry ^ "/opt") in + let rec loop = function + | [] -> mountable (* no subvol, return whole device *) + | opt :: opts -> + let optname = Augeas.get aug opt in + match optname with + | None -> loop opts + | Some "subvol" -> + let subvol = Augeas.get aug (opt ^ "/value") in + (match subvol with + | None -> loop opts + | Some subvol -> + Mountable.of_btrfsvol device subvol + ) + | Some _ -> + loop opts + in + loop opts + +(* Get a map of md device names in mdadm.conf to their device names + * in the appliance. + *) +and map_md_devices aug + (* Get a map of md device uuids to their device names in the appliance. *) + let uuid_map = map_app_md_devices () in + + (* Nothing to do if there are no md devices. *) + if StringMap.is_empty uuid_map then StringMap.empty + else ( + (* Get all arrays listed in mdadm.conf. *) + let entries = Augeas.matches aug "/files/etc/mdadm.conf/array" in + + (* Log a debug entry if we've got md devices but nothing in mdadm.conf. *) + if verbose () && entries = [] then + eprintf "warning: appliance has MD devices, but augeas returned no array matches in /etc/mdadm.conf\n%!"; + + List.fold_left ( + fun md_map entry -> + try + (* Get device name and uuid for each array. *) + let dev = Augeas.get aug (entry ^ "/devicename") in + let uuid = Augeas.get aug (entry ^ "/uuid") in + let dev + match dev with None -> raise Not_found | Some dev -> dev in + let uuid + match uuid with None -> raise Not_found | Some uuid -> uuid in + + (* Parse the uuid into an md_uuid structure so we can look + * it up in the uuid_map. + *) + let uuid = parse_md_uuid uuid in + + let md = StringMap.find uuid uuid_map in + + (* If there's a corresponding uuid in the appliance, create + * a new entry in the transitive map. + *) + StringMap.add dev md md_map + with + (* No Augeas devicename or uuid node found, or could not parse + * uuid, or uuid not present in the uuid_map. + * + * This is not fatal, just ignore the entry. + *) + Not_found | Invalid_argument _ -> md_map + ) StringMap.empty entries + ) + +(* Create a mapping of uuids to appliance md device names. *) +and map_app_md_devices () + let mds = Md.list_md_devices () in + List.fold_left ( + fun map md -> + let detail = Md.md_detail md in + + try + (* Find the value of the "uuid" key. *) + let uuid = List.assoc "uuid" detail in + let uuid = parse_md_uuid uuid in + StringMap.add uuid md map + with + (* uuid not found, or could not be parsed - just ignore the entry *) + Not_found | Invalid_argument _ -> map + ) StringMap.empty mds + +(* Taken from parse_uuid in mdadm. + * + * Raises Invalid_argument if the input is not an MD UUID. + *) +and parse_md_uuid uuid + let len = String.length uuid in + let out = Bytes.create len in + let j = ref 0 in + + for i = 0 to len-1 do + let c = uuid.[i] in + if Char.isxdigit c then ( + Bytes.set out !j c; + incr j + ) + else if c = ':' || c = '.' || c = ' ' || c = '-' then + () + else + invalid_arg "parse_md_uuid: invalid character" + done; + + if !j <> 32 then + invalid_arg "parse_md_uuid: invalid length"; + + Bytes.sub_string out 0 !j + +(* Resolve block device name to the libguestfs device name, eg. + * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV. This + * assumes that disks were added in the same order as they appear to + * the real VM, which is a reasonable assumption to make. Return + * anything we don't recognize unchanged. + *) +and resolve_fstab_device spec md_map os_type + (* In any case where we didn't match a device pattern or there was + * another problem, return this default mountable derived from [spec]. + *) + let default = Mountable.of_device spec in + + if String.is_prefix spec "/dev/mapper" then ( + (* LVM2 does some strange munging on /dev/mapper paths for VGs and + * LVs which contain '-' character: + * + * ><fs> lvcreate LV--test VG--test 32 + * ><fs> debug ls /dev/mapper + * VG----test-LV----test + * + * This makes it impossible to reverse those paths directly, so + * we have implemented lvm_canonical_lv_name in the daemon. + *) + try + match Lvm.lv_canonical spec with + | None -> Mountable.of_device spec + | Some device -> Mountable.of_device device + with + (* Ignore devices that don't exist. (RHBZ#811872) *) + | Unix.Unix_error (Unix.ENOENT, _, _) -> default + ) + + else if Str.string_match re_xdev spec 0 then ( + let typ = Str.matched_group 1 spec + and disk = Str.matched_group 2 spec + and part = int_of_string (Str.matched_group 3 spec) in + resolve_xdev typ disk part default + ) + + else if Str.string_match re_cciss spec 0 then ( + let disk = Str.matched_group 1 spec + (* group 2 = optional p<NN>, group 3 = <NN> *) + and part + try Some (int_of_string (Str.matched_group 3 spec)) + with Not_found | Invalid_argument _ -> None in + resolve_cciss disk part default + ) + + else if Str.string_match re_mdN spec 0 then ( + try + Mountable.of_device (StringMap.find spec md_map) + with + | Not_found -> default + ) + + else if Str.string_match re_diskbyid spec 0 then ( + let part = int_of_string (Str.matched_group 1 spec) in + resolve_diskbyid part default + ) + + else if Str.string_match re_freebsd_gpt spec 0 then ( + (* group 1 (type) is not used *) + let disk = int_of_string (Str.matched_group 2 spec) + and part = int_of_string (Str.matched_group 3 spec) in + + (* If the FreeBSD disk contains GPT partitions, the translation to Linux + * device names is straight forward. Partitions on a virtio disk are + * prefixed with [vtbd]. IDE hard drives used to be prefixed with [ad] + * and now prefixed with [ada]. + *) + if disk >= 0 && disk <= 26 && part >= 0 && part <= 128 then ( + let dev = sprintf "/dev/sd%c%d" + (Char.chr (disk + Char.code 'a')) part in + Mountable.of_device dev + ) + else default + ) + + else if Str.string_match re_freebsd_mbr spec 0 then ( + (* group 1 (type) is not used *) + let disk = int_of_string (Str.matched_group 2 spec) + and slice = int_of_string (Str.matched_group 3 spec) + (* partition number counting from 0: *) + and part = Char.code (Str.matched_group 4 spec).[0] - Char.code 'a' in + + (* FreeBSD MBR disks are organized quite differently. See: + * http://www.freebsd.org/doc/handbook/disk-organization.html + * FreeBSD "partitions" are exposed as quasi-extended partitions + * numbered from 5 in Linux. I have no idea what happens when you + * have multiple "slices" (the FreeBSD term for MBR partitions). + *) + + (* Partition 'c' has the size of the enclosing slice. + * Not mapped under Linux. + *) + let part = if part > 2 then part - 1 else part in + + if disk >= 0 && disk <= 26 && + slice > 0 && slice <= 1 (* > 4 .. see comment above *) && + part >= 0 && part < 25 then ( + let dev = sprintf "/dev/sd%c%d" + (Char.chr (disk + Char.code 'a')) (part + 5) in + Mountable.of_device dev + ) + else default + ) + + else if os_type = OS_TYPE_NETBSD && + Str.string_match re_netbsd_dev spec 0 then ( + (* group 1 (type) is not used *) + let disk = int_of_string (Str.matched_group 2 spec) + (* partition number counting from 0: *) + and part = Char.code (Str.matched_group 3 spec).[0] - Char.code 'a' in + + (* Partition 'c' is the disklabel partition and 'd' the hard disk itself. + * Not mapped under Linux. + *) + let part = if part > 3 then part - 2 else part in + + if disk >= 0 && part >= 0 && part < 24 then ( + let dev = sprintf "/dev/sd%c%d" + (Char.chr (disk + Char.code 'a')) (part + 5) in + Mountable.of_device dev + ) + else default + ) + + else if os_type = OS_TYPE_OPENBSD && + Str.string_match re_openbsd_dev spec 0 then ( + (* group 1 (type) is not used *) + let disk = int_of_string (Str.matched_group 2 spec) + (* partition number counting from 0: *) + and part = Char.code (Str.matched_group 3 spec).[0] - Char.code 'a' in + + (* Partition 'c' is the hard disk itself. Not mapped under Linux. *) + let part = if part > 2 then part - 1 else part in + + (* In OpenBSD MAXPARTITIONS is defined to 16 for all architectures. *) + if disk >= 0 && part >= 0 && part < 15 then ( + let dev = sprintf "/dev/sd%c%d" + (Char.chr (disk + Char.code 'a')) (part + 5) in + Mountable.of_device dev + ) + else default + ) + + else if Str.string_match re_hurd_dev spec 0 then ( + let typ = Str.matched_group 1 spec + and disk = int_of_string (Str.matched_group 2 spec) + and part = int_of_string (Str.matched_group 3 spec) in + + (* Hurd disk devices are like /dev/hdNsM, where hdN is the + * N-th disk and M is the M-th partition on that disk. + * Turn the disk number into a letter-based identifier, so + * we can resolve it easily. + *) + let disk = sprintf "%c" (Char.chr (disk + Char.code 'a')) in + + resolve_xdev typ disk part default + ) + + else default + +(* type: (h|s|v|xv) + * disk: [a-z]+ + * part: \d* + *) +and resolve_xdev typ disk part default + let devices = Devsparts.list_devices () in + let devices = Array.of_list devices in + + (* XXX Check any hints we were passed for a non-heuristic mapping. + * The C code used hints here to map device names as known by + * the library user (eg. from metadata) to libguestfs devices here. + * However none of the libguestfs tools ever used this feature. + * Nevertheless we should reimplement it at some point because + * outside callers might require it, and it's a good idea in general. + *) + + (* Guess the appliance device name if we didn't find a matching hint. *) + let i = drive_index disk in + if i >= 0 && i < Array.length devices then ( + let dev = Array.get devices i in + let dev = dev ^ string_of_int part in + if is_block_device dev then + Mountable.of_device dev + else + default + ) + else + default + +(* disk: (cciss/c\d+d\d+) + * part: (\d+)? + *) +and resolve_cciss disk part default + (* XXX Check any hints we were passed for a non-heuristic mapping. + * The C code used hints here to map device names as known by + * the library user (eg. from metadata) to libguestfs devices here. + * However none of the libguestfs tools ever used this feature. + * Nevertheless we should reimplement it at some point because + * outside callers might require it, and it's a good idea in general. + *) + + (* We don't try to guess mappings for cciss devices. *) + default + +(* For /dev/disk/by-id there is a limit to what we can do because + * original SCSI ID information has likely been lost. This + * heuristic will only work for guests that have a single block + * device. + * + * So the main task here is to make sure the assumptions above are + * true. + * + * XXX Use hints from virt-p2v if available. + * See also: https://bugzilla.redhat.com/show_bug.cgi?id=836573#c3 + *) +and resolve_diskbyid part default + let nr_devices = Devsparts.nr_devices () in + + (* If #devices isn't 1, give up trying to translate this fstab entry. *) + if nr_devices <> 1 then + default + else ( + (* Make the partition name and check it exists. *) + let dev = sprintf "/dev/sda%d" part in + if is_block_device dev then Mountable.of_device dev + else default + ) diff --git a/daemon/inspect_fs_unix_fstab.mli b/daemon/inspect_fs_unix_fstab.mli new file mode 100644 index 000000000..3ce3aef05 --- /dev/null +++ b/daemon/inspect_fs_unix_fstab.mli @@ -0,0 +1,34 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 check_fstab : ?mdadm_conf:bool -> Mountable.t -> Inspect_types.os_type -> + (Mountable.t * string) list +(** [check_fstab] examines the [/etc/fstab] file of a mounted root + filesystem, returning the list of devices and their mount points. + Various devices (like CD-ROMs) are ignored in the process, and + this function also knows how to map (eg) BSD device names into + Linux/libguestfs device names. + + [mdadm_conf] is true if you want to check [/etc/mdadm.conf] as well. + + [root_mountable] is the [Mountable.t] of the root filesystem. (Note + that the root filesystem must be mounted on sysroot before this + function is called.) + + [os_type] is the presumed operating system type of this root, and + is used to make some adjustments to fstab parsing. *) diff --git a/daemon/inspect_fs_windows.ml b/daemon/inspect_fs_windows.ml new file mode 100644 index 000000000..bb949be47 --- /dev/null +++ b/daemon/inspect_fs_windows.ml @@ -0,0 +1,498 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open Std_utils + +open Utils +open Inspect_types +open Inspect_utils + +(* Check a predefined list of common windows system root locations. *) +let systemroot_paths + [ "/windows"; "/winnt"; "/win32"; "/win"; "/reactos" ] + +let re_boot_ini_os + Str.regexp "^\\(multi|scsi\\)(\\([0-9]+\\))disk(\\([0-9]+\\))rdisk(\\([0-9]+\\))partition(\\([0-9]+\\))\\([^=]+\\)=" + +let rec check_windows_root data + let systemroot + match get_windows_systemroot () with + | None -> assert false (* Should never happen - see caller. *) + | Some systemroot -> systemroot in + + let data = { + data with os_type = Some OS_TYPE_WINDOWS; + distro = Some DISTRO_WINDOWS; + windows_systemroot = Some systemroot; + arch = Some (check_windows_arch systemroot) + } in + + (* Load further fields from the Windows registry. *) + check_windows_registry systemroot data + +and is_windows_systemroot () + get_windows_systemroot () <> None + +and get_windows_systemroot () + let rec loop = function + | [] -> None + | path :: paths -> + let path = case_sensitive_path_silently path in + match path with + | None -> loop paths + | Some path -> + if is_systemroot path then Some path + else loop paths + in + let systemroot = loop systemroot_paths in + + let systemroot + match systemroot with + | Some systemroot -> Some systemroot + | None -> + (* If the fs contains boot.ini, check it for non-standard + * systemroot locations. + *) + let boot_ini_path = case_sensitive_path_silently "/boot.ini" in + match boot_ini_path with + | None -> None + | Some boot_ini_path -> + get_windows_systemroot_from_boot_ini boot_ini_path in + + match systemroot with + | None -> None + | Some systemroot -> + if verbose () then + eprintf "get_windows_systemroot: windows %%SYSTEMROOT%% = %s\n%!" + systemroot; + Some systemroot + +and get_windows_systemroot_from_boot_ini boot_ini_path + let chroot + Chroot.create ~name:"get_windows_systemroot_from_boot_ini" + (Sysroot.sysroot ()) in + let lines + Chroot.f chroot ( + fun () -> + if not (is_small_file boot_ini_path) then ( + eprintf "%s: not a regular file or too large\n" boot_ini_path; + None + ) + else + Some (read_whole_file boot_ini_path) + ) () in + match lines with + | None -> None + | Some lines -> + let lines = String.nsplit "\n" lines in + + (* Find: + * [operating systems] + * followed by multiple lines starting with "multi" or "scsi". + *) + let rec loop = function + | [] -> None + | str :: rest when String.is_prefix str "[operating systems]" -> + let rec loop2 = function + | [] -> [] + | str :: rest when String.is_prefix str "multi(" || + String.is_prefix str "scsi(" -> + str :: loop2 rest + | _ -> [] + in + Some (loop2 rest) + | _ :: rest -> loop rest + in + match loop lines with + | None -> None + | Some oses -> + (* Rewrite multi|scsi lines, removing any which we cannot parse. *) + let oses + filter_map ( + fun line -> + if Str.string_match re_boot_ini_os line 0 then ( + let ctrlr_type = Str.matched_group 1 line + and ctrlr = int_of_string (Str.matched_group 2 line) + and disk = int_of_string (Str.matched_group 3 line) + and rdisk = int_of_string (Str.matched_group 4 line) + and part = int_of_string (Str.matched_group 5 line) + and path = Str.matched_group 6 line in + + (* Swap backslashes for forward slashes in the + * system root path. + *) + let path = String.replace_char path '\\' '/' in + + Some (ctrlr_type, ctrlr, disk, rdisk, part, path) + ) + else None + ) oses in + + (* The Windows system root may be on any disk. However, there + * are currently (at least) 2 practical problems preventing us + * from locating it on another disk: + * + * 1. We don't have enough metadata about the disks we were + * given to know if what controller they were on and what + * index they had. + * + * 2. The way inspection of filesystems currently works, we + * can't mark another filesystem, which we may have already + * inspected, to be inspected for a specific Windows system + * root. + * + * Solving 1 properly would require a new API at a minimum. We + * might be able to fudge something practical without this, + * though, e.g. by looking at the <partition>th partition of + * every disk for the specific windows root. + * + * Solving 2 would probably require a significant refactoring + * of the way filesystems are inspected. We should probably do + * this some time. + * + * For the moment, we ignore all partition information and + * assume the system root is on the current partition. In + * practice, this will normally be correct. + *) + + let rec loop = function + | [] -> None + | (_, _, _, _, _, path) :: rest -> + if is_systemroot path then Some path + else loop rest + in + loop oses + +(* Try to find Windows systemroot using some common locations. + * + * Notes: + * + * (1) We check for some directories inside to see if it is a real + * systemroot, and not just a directory that happens to have the same + * name. + * + * (2) If a Windows guest has multiple disks and applications are + * installed on those other disks, then those other disks will contain + * "/Program Files" and "/System Volume Information". Those would + * *not* be Windows root disks. (RHBZ#674130) + *) +and is_systemroot systemroot + is_dir_nocase (systemroot ^ "/system32") && + is_dir_nocase (systemroot ^ "/system32/config") && + is_file_nocase (systemroot ^ "/system32/cmd.exe") + +(* Return the architecture of the guest from cmd.exe. *) +and check_windows_arch systemroot + let cmd_exe = sprintf "%s/system32/cmd.exe" systemroot in + + (* Should exist because of previous check above in is_systemroot. *) + let cmd_exe = Realpath.case_sensitive_path cmd_exe in + + Filearch.file_architecture cmd_exe + +(* Read further fields from the Windows registry. *) +and check_windows_registry systemroot data + (* We know (from is_systemroot) that the config directory exists. *) + let software_hive = sprintf "%s/system32/config/software" systemroot in + let software_hive = Realpath.case_sensitive_path software_hive in + let software_hive + if Is.is_file software_hive then Some software_hive else None in + let data = { data with windows_software_hive = software_hive } in + + let system_hive = sprintf "%s/system32/config/system" systemroot in + let system_hive = Realpath.case_sensitive_path system_hive in + let system_hive + if Is.is_file system_hive then Some system_hive else None in + let data = { data with windows_system_hive = system_hive } in + + match software_hive, system_hive with + | None, _ | Some _, None -> data + | Some software_hive, Some system_hive -> + (* Check software hive. *) + let data = check_windows_software_registry software_hive data in + + (* Check system hive. *) + let data = check_windows_system_registry system_hive data in + + data + +(* At the moment, pull just the ProductName and version numbers from + * the registry. In future there is a case for making many more + * registry fields available to callers. + *) +and check_windows_software_registry software_hive data + with_hive (Sysroot.sysroot () // software_hive) ( + fun h root -> + try + let path = [ "Microsoft"; "Windows NT"; "CurrentVersion" ] in + let node = get_node h root path in + let values = Hivex.node_values h node in + let values = Array.to_list values in + (* Convert to a list of (key, value) to make the following easier. *) + let values = List.map (fun v -> Hivex.value_key h v, v) values in + + (* Look for ProductName key. *) + let data + try + let v = List.assoc "ProductName" values in + { data with product_name = Some (hivex_value_as_utf8 h v) } + with + Not_found -> data in + + (* Version is complicated. Use CurrentMajorVersionNumber and + * CurrentMinorVersionNumber if present. If they are not + * found, fall back on CurrentVersion. + *) + let data + try + let major_v = List.assoc "CurrentMajorVersionNumber" values + and minor_v = List.assoc "CurrentMinorVersionNumber" values in + let major = Int32.to_int (Hivex.value_dword h major_v) + and minor = Int32.to_int (Hivex.value_dword h minor_v) in + { data with version = Some (major, minor) } + with + Not_found -> + let v = List.assoc "CurrentVersion" values in + let v = hivex_value_as_utf8 h v in + parse_version_from_major_minor v data in + + (* InstallationType (product_variant). *) + let data + try + let v = List.assoc "InstallationType" values in + { data with product_variant = Some (hivex_value_as_utf8 h v) } + with + Not_found -> data in + + data + with + | Not_found -> + if verbose () then + eprintf "check_windows_software_registry: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\n%!"; + data + ) (* with_hive *) + +and check_windows_system_registry system_hive data + with_hive (Sysroot.sysroot () // system_hive) ( + fun h root -> + let data = get_drive_mappings h root data in + + let current_control_set = get_current_control_set h root in + let data + { data with windows_current_control_set = current_control_set } in + + match current_control_set with + | None -> data + | Some current_control_set -> + let hostname = get_hostname h root current_control_set in + let data = { data with hostname = hostname } in + data + ) (* with_hive *) + +(* Get the CurrentControlSet. *) +and get_current_control_set h root + try + let path = [ "Select" ] in + let node = get_node h root path in + let current_v = Hivex.node_get_value h node "Current" in + let current_control_set + sprintf "ControlSet%03ld" (Hivex.value_dword h current_v) in + Some current_control_set + with + | Not_found -> + if verbose () then + eprintf "check_windows_system_registry: cannot locate HKLM\\SYSTEM\\Select\n%!"; + None + +(* Get the drive mappings. + * This page explains the contents of HKLM\System\MountedDevices: + * http://www.goodells.net/multiboot/partsigs.shtml + *) +and get_drive_mappings h root data + let devices = lazy (Devsparts.list_devices ()) in + let partitions = lazy (Devsparts.list_partitions ()) in + try + let path = [ "MountedDevices" ] in + let node = get_node h root path in + let values = Hivex.node_values h node in + let values = Array.to_list values in + let values + filter_map ( + fun value -> + let key = Hivex.value_key h value in + let keylen = String.length key in + if keylen >= 14 && + String.lowercase_ascii (String.sub key 0 12) = "\\dosdevices\\" && + Char.isalpha key.[12] && key.[13] = ':' then ( + let drive_letter = String.sub key 12 1 in + + (* Get the binary value. Is it a fixed disk? *) + let (typ, blob) = Hivex.value_value h value in + let device + if typ = Hivex.REG_BINARY then ( + if String.length blob >= 24 && + String.is_prefix blob "DMIO:ID:" (* GPT *) then + map_registry_disk_blob_gpt (Lazy.force partitions) blob + else if String.length blob = 12 then + map_registry_disk_blob (Lazy.force devices) blob + else + None + ) + else None in + + match device with + | None -> None + | Some device -> Some (drive_letter, device) + ) + else + None + ) values in + + { data with drive_mappings = values } + + with + | Not_found -> + if verbose () then + eprintf "check_windows_system_registry: cannot find drive mappings\n%!"; + data + +(* Windows Registry HKLM\SYSTEM\MountedDevices uses a blob of data + * to store partitions. This blob is described here: + * http://www.goodells.net/multiboot/partsigs.shtml + * The following function maps this blob to a libguestfs partition + * name, if possible. + *) +and map_registry_disk_blob devices blob + try + (* First 4 bytes are the disk ID. Search all devices to find the + * disk with this disk ID. + *) + let diskid = String.sub blob 0 4 in + let device = List.find (fun dev -> pread dev 4 0x01b8 = diskid) devices in + + (* Next 8 bytes are the offset of the partition in bytes(!) given as + * a 64 bit little endian number. Luckily it's easy to get the + * partition byte offset from Parted.part_list. + *) + let offset = String.sub blob 4 8 in + let offset = int_of_le64 offset in + let partitions = Parted.part_list device in + let partition + List.find (fun { Parted.part_start = s } -> s = offset) partitions in + + (* Construct the full device name. *) + Some (sprintf "%s%ld" device partition.Parted.part_num) + with + | Not_found -> None + +(* Matches Windows registry HKLM\SYSYTEM\MountedDevices\DosDevices blob to + * to libguestfs GPT partition device. For GPT disks, the blob is made of + * "DMIO:ID:" prefix followed by the GPT partition GUID. + *) +and map_registry_disk_blob_gpt partitions blob + let blob_guid + String.lowercase_ascii (extract_guid_from_registry_blob blob) in + + try + let partition + List.find ( + fun part -> + let partnum = Devsparts.part_to_partnum part in + let device = Devsparts.part_to_dev part in + let typ = Parted.part_get_parttype device in + if typ <> "gpt" then false + else ( + let guid = Parted.part_get_gpt_guid device partnum in + String.lowercase_ascii guid = blob_guid + ) + ) partitions in + Some partition + with + | Not_found -> None + +(* Extracts the binary GUID stored in blob from Windows registry + * HKLM\SYSTYEM\MountedDevices\DosDevices value and converts it to a + * GUID string so that it can be matched against libguestfs partition + * device GPT GUID. + *) +and extract_guid_from_registry_blob blob + (* Copy relevant sections from blob to respective ints. + * Note we have to skip 8 byte "DMIO:ID:" prefix. + *) + let data1 = int_of_le32 (String.sub blob 8 4) + and data2 = int_of_le16 (String.sub blob 12 2) + and data3 = int_of_le16 (String.sub blob 14 2) + and data4 = int_of_be64 (String.sub blob 16 8) (* really big endian! *) in + + sprintf "%08Lx-%04Lx-%04Lx-%04Lx-%012Lx" + data1 data2 data3 + (Int64.shift_right data4 48) + (data4 &^ 0xffffffffffff_L) + +and pread device size offset + let fd = Unix.openfile device [Unix.O_RDONLY; Unix.O_CLOEXEC] 0 in + let ret + protect ~f:( + fun () -> + ignore (Unix.lseek fd offset Unix.SEEK_SET); + let ret = Bytes.create size in + if Unix.read fd ret 0 size < size then + failwithf "pread: %s: short read" device; + ret + ) ~finally:(fun () -> Unix.close fd) in + Bytes.to_string ret + +(* Get the hostname. *) +and get_hostname h root current_control_set + try + let path = [ current_control_set; "Services"; "Tcpip"; "Parameters" ] in + let node = get_node h root path in + let values = Hivex.node_values h node in + let values = Array.to_list values in + (* Convert to a list of (key, value) to make the following easier. *) + let values = List.map (fun v -> Hivex.value_key h v, v) values in + let hostname_v = List.assoc "Hostname" values in + Some (hivex_value_as_utf8 h hostname_v) + with + | Not_found -> + if verbose () then + eprintf "check_windows_system_registry: cannot locate HKLM\\SYSTEM\\%s\\Services\\Tcpip\\Parameters and/or Hostname key\n%!" current_control_set; + None + +(* Raises [Not_found] if the node is not found. *) +and get_node h node = function + | [] -> node + | x :: xs -> + let node = Hivex.node_get_child h node x in + get_node h node xs + +(* NB: This function DOES NOT test for the existence of the file. It + * will return non-NULL even if the file/directory does not exist. + * You have to call guestfs_is_file{,_opts} etc. + *) +and case_sensitive_path_silently path + try + Some (Realpath.case_sensitive_path path) + with + | exn -> + if verbose () then + eprintf "case_sensitive_path_silently: %s: %s\n%!" path + (Printexc.to_string exn); + None diff --git a/daemon/inspect_fs_windows.mli b/daemon/inspect_fs_windows.mli new file mode 100644 index 000000000..936d695c6 --- /dev/null +++ b/daemon/inspect_fs_windows.mli @@ -0,0 +1,25 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 check_windows_root : Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Inspect the Windows [C:] filesystem mounted on sysroot. *) + +val is_windows_systemroot : unit -> bool +(** Decide if the filesystem mounted on sysroot looks like a + Windows [C:] filesystem. *) diff --git a/daemon/inspect_types.ml b/daemon/inspect_types.ml new file mode 100644 index 000000000..f0fa52447 --- /dev/null +++ b/daemon/inspect_types.ml @@ -0,0 +1,325 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Printf + +open Std_utils + +type fs = { + fs_location : location; + role : role; (** Special cases: root filesystem or /usr *) +} +and root = { + root_location : location; + inspection_data : inspection_data; +} +and location = { + mountable : Mountable.t; (** The device name or other mountable object.*) + vfs_type : string; (** Returned from [vfs_type] API. *) +} + +and role + | RoleRoot of inspection_data + | RoleUsr of inspection_data + | RoleSwap + | RoleOther +and inspection_data = { + format : format option; + os_type : os_type option; + distro : distro option; + package_format : package_format option; + package_management : package_management option; + product_name : string option; + product_variant : string option; + version : version option; + arch : string option; + hostname : string option; + fstab : fstab_entry list; + windows_systemroot : string option; + windows_software_hive : string option; + windows_system_hive : string option; + windows_current_control_set : string option; + drive_mappings : drive_mapping list; + is_live_disk : bool; + is_netinst_disk : bool; + is_multipart_disk : bool; +} +and format + | FORMAT_INSTALLED + | FORMAT_INSTALLER + (* in future: supplemental install disks *) +and os_type + | OS_TYPE_DOS + | OS_TYPE_FREEBSD + | OS_TYPE_HURD + | OS_TYPE_LINUX + | OS_TYPE_MINIX + | OS_TYPE_NETBSD + | OS_TYPE_OPENBSD + | OS_TYPE_WINDOWS +and distro + | DISTRO_ALPINE_LINUX + | DISTRO_ALTLINUX + | DISTRO_ARCHLINUX + | DISTRO_BUILDROOT + | DISTRO_CENTOS + | DISTRO_CIRROS + | DISTRO_COREOS + | DISTRO_DEBIAN + | DISTRO_FEDORA + | DISTRO_FREEBSD + | DISTRO_FREEDOS + | DISTRO_FRUGALWARE + | DISTRO_GENTOO + | DISTRO_LINUX_MINT + | DISTRO_MAGEIA + | DISTRO_MANDRIVA + | DISTRO_MEEGO + | DISTRO_NETBSD + | DISTRO_OPENBSD + | DISTRO_OPENSUSE + | DISTRO_ORACLE_LINUX + | DISTRO_PARDUS + | DISTRO_PLD_LINUX + | DISTRO_REDHAT_BASED + | DISTRO_RHEL + | DISTRO_SCIENTIFIC_LINUX + | DISTRO_SLACKWARE + | DISTRO_SLES + | DISTRO_SUSE_BASED + | DISTRO_TTYLINUX + | DISTRO_UBUNTU + | DISTRO_VOID_LINUX + | DISTRO_WINDOWS +and package_format + | PACKAGE_FORMAT_APK + | PACKAGE_FORMAT_DEB + | PACKAGE_FORMAT_EBUILD + | PACKAGE_FORMAT_PACMAN + | PACKAGE_FORMAT_PISI + | PACKAGE_FORMAT_PKGSRC + | PACKAGE_FORMAT_RPM + | PACKAGE_FORMAT_XBPS +and package_management + | PACKAGE_MANAGEMENT_APK + | PACKAGE_MANAGEMENT_APT + | PACKAGE_MANAGEMENT_DNF + | PACKAGE_MANAGEMENT_PACMAN + | PACKAGE_MANAGEMENT_PISI + | PACKAGE_MANAGEMENT_PORTAGE + | PACKAGE_MANAGEMENT_UP2DATE + | PACKAGE_MANAGEMENT_URPMI + | PACKAGE_MANAGEMENT_XBPS + | PACKAGE_MANAGEMENT_YUM + | PACKAGE_MANAGEMENT_ZYPPER +and version = int * int +and fstab_entry = Mountable.t * string (* mountable, mountpoint *) +and drive_mapping = string * string (* drive name, device *) + +let rec string_of_fs { fs_location = location; role = role } + sprintf "fs: %s role: %s" + (string_of_location location) + (match role with + | RoleRoot _ -> "root" + | RoleUsr _ -> "usr" + | RoleSwap -> "swap" + | RoleOther -> "other") + +and string_of_location { mountable = mountable; vfs_type = vfs_type } + sprintf "%s (%s)" (Mountable.to_string mountable) vfs_type + +and string_of_root { root_location = location; + inspection_data = inspection_data } + sprintf "%s:\n%s" + (string_of_location location) + (string_of_inspection_data inspection_data) + +and string_of_inspection_data data + let b = Buffer.create 1024 in + let bpf fs = bprintf b fs in + may (fun v -> bpf "\tformat: %s\n" (string_of_format v)) + data.format; + may (fun v -> bpf "\ttype: %s\n" (string_of_os_type v)) + data.os_type; + may (fun v -> bpf "\tdistro: %s\n" (string_of_distro v)) + data.distro; + may (fun v -> bpf "\tpackage_format: %s\n" (string_of_package_format v)) + data.package_format; + may (fun v -> bpf "\tpackage_management: %s\n" (string_of_package_management v)) + data.package_management; + may (fun v -> bpf "\tproduct_name: %s\n" v) + data.product_name; + may (fun v -> bpf "\tproduct_variant: %s\n" v) + data.product_variant; + may (fun (major, minor) -> bpf "\tversion: %d.%d\n" major minor) + data.version; + may (fun v -> bpf "\tarch: %s\n" v) + data.arch; + may (fun v -> bpf "\thostname: %s\n" v) + data.hostname; + if data.fstab <> [] then ( + let v = List.map ( + fun (a, b) -> sprintf "(%s, %s)" (Mountable.to_string a) b + ) data.fstab in + bpf "\tfstab: [%s]\n" (String.concat ", " v) + ); + may (fun v -> bpf "\twindows_systemroot: %s\n" v) + data.windows_systemroot; + may (fun v -> bpf "\twindows_software_hive: %s\n" v) + data.windows_software_hive; + may (fun v -> bpf "\twindows_system_hive: %s\n" v) + data.windows_system_hive; + may (fun v -> bpf "\twindows_current_control_set: %s\n" v) + data.windows_current_control_set; + if data.drive_mappings <> [] then ( + let v + List.map (fun (a, b) -> sprintf "(%s, %s)" a b) data.drive_mappings in + bpf "\tdrive_mappings: [%s]\n" (String.concat ", " v) + ); + bpf "\tis_live_disk: %b\n" data.is_live_disk; + bpf "\tis_netinst_disk: %b\n" data.is_netinst_disk; + bpf "\tis_multipart_disk: %b\n" data.is_multipart_disk; + Buffer.contents b + +and string_of_format = function + | FORMAT_INSTALLED -> "installed" + | FORMAT_INSTALLER -> "installer" + +and string_of_os_type = function + | OS_TYPE_DOS -> "dos" + | OS_TYPE_FREEBSD -> "freebsd" + | OS_TYPE_HURD -> "hurd" + | OS_TYPE_LINUX -> "linux" + | OS_TYPE_MINIX -> "minix" + | OS_TYPE_NETBSD -> "netbsd" + | OS_TYPE_OPENBSD -> "openbsd" + | OS_TYPE_WINDOWS -> "windows" + +and string_of_distro = function + | DISTRO_ALPINE_LINUX -> "alpinelinux" + | DISTRO_ALTLINUX -> "altlinux" + | DISTRO_ARCHLINUX -> "archlinux" + | DISTRO_BUILDROOT -> "buildroot" + | DISTRO_CENTOS -> "centos" + | DISTRO_CIRROS -> "cirros" + | DISTRO_COREOS -> "coreos" + | DISTRO_DEBIAN -> "debian" + | DISTRO_FEDORA -> "fedora" + | DISTRO_FREEBSD -> "freebsd" + | DISTRO_FREEDOS -> "freedos" + | DISTRO_FRUGALWARE -> "frugalware" + | DISTRO_GENTOO -> "gentoo" + | DISTRO_LINUX_MINT -> "linuxmint" + | DISTRO_MAGEIA -> "mageia" + | DISTRO_MANDRIVA -> "mandriva" + | DISTRO_MEEGO -> "meego" + | DISTRO_NETBSD -> "netbsd" + | DISTRO_OPENBSD -> "openbsd" + | DISTRO_OPENSUSE -> "opensuse" + | DISTRO_ORACLE_LINUX -> "oraclelinux" + | DISTRO_PARDUS -> "pardus" + | DISTRO_PLD_LINUX -> "pldlinux" + | DISTRO_REDHAT_BASED -> "redhat-based" + | DISTRO_RHEL -> "rhel" + | DISTRO_SCIENTIFIC_LINUX -> "scientificlinux" + | DISTRO_SLACKWARE -> "slackware" + | DISTRO_SLES -> "sles" + | DISTRO_SUSE_BASED -> "suse-based" + | DISTRO_TTYLINUX -> "ttylinux" + | DISTRO_UBUNTU -> "ubuntu" + | DISTRO_VOID_LINUX -> "voidlinux" + | DISTRO_WINDOWS -> "windows" + +and string_of_package_format = function + | PACKAGE_FORMAT_APK -> "apk" + | PACKAGE_FORMAT_DEB -> "deb" + | PACKAGE_FORMAT_EBUILD -> "ebuild" + | PACKAGE_FORMAT_PACMAN -> "pacman" + | PACKAGE_FORMAT_PISI -> "pisi" + | PACKAGE_FORMAT_PKGSRC -> "pkgsrc" + | PACKAGE_FORMAT_RPM -> "rpm" + | PACKAGE_FORMAT_XBPS -> "xbps" + +and string_of_package_management = function + | PACKAGE_MANAGEMENT_APK -> "apk" + | PACKAGE_MANAGEMENT_APT -> "apt" + | PACKAGE_MANAGEMENT_DNF -> "dnf" + | PACKAGE_MANAGEMENT_PACMAN -> "pacman" + | PACKAGE_MANAGEMENT_PISI -> "pisi" + | PACKAGE_MANAGEMENT_PORTAGE -> "portage" + | PACKAGE_MANAGEMENT_UP2DATE -> "up2date" + | PACKAGE_MANAGEMENT_URPMI -> "urpmi" + | PACKAGE_MANAGEMENT_XBPS -> "xbps" + | PACKAGE_MANAGEMENT_YUM -> "yum" + | PACKAGE_MANAGEMENT_ZYPPER -> "zypper" + +let null_inspection_data = { + format = None; + os_type = None; + distro = None; + package_format = None; + package_management = None; + product_name = None; + product_variant = None; + version = None; + arch = None; + hostname = None; + fstab = []; + windows_systemroot = None; + windows_software_hive = None; + windows_system_hive = None; + windows_current_control_set = None; + drive_mappings = []; + is_live_disk = false; + is_netinst_disk = false; + is_multipart_disk = false; +} + +let merge_inspection_data child parent + let merge child parent = if parent = None then child else parent in + + { format = merge child.format parent.format; + os_type = merge child.os_type parent.os_type; + distro = merge child.distro parent.distro; + package_format = merge child.package_format parent.package_format; + package_management + merge child.package_management parent.package_management; + product_name = merge child.product_name parent.product_name; + product_variant = merge child.product_variant parent.product_variant; + version = merge child.version parent.version; + arch = merge child.arch parent.arch; + hostname = merge child.hostname parent.hostname; + fstab = child.fstab @ parent.fstab; + windows_systemroot + merge child.windows_systemroot parent.windows_systemroot; + windows_software_hive + merge child.windows_software_hive parent.windows_software_hive; + windows_system_hive + merge child.windows_system_hive parent.windows_system_hive; + windows_current_control_set + merge child.windows_current_control_set parent.windows_current_control_set; + + (* This is what the old C code did, but I doubt that it's correct. *) + drive_mappings = child.drive_mappings @ parent.drive_mappings; + + is_live_disk = child.is_live_disk || parent.is_live_disk; + is_netinst_disk = child.is_netinst_disk || parent.is_netinst_disk; + is_multipart_disk = child.is_multipart_disk || parent.is_multipart_disk; + } + +let inspect_fses = ref [] diff --git a/daemon/inspect_types.mli b/daemon/inspect_types.mli new file mode 100644 index 000000000..99bffea6f --- /dev/null +++ b/daemon/inspect_types.mli @@ -0,0 +1,175 @@ +(* guestfs-inspection + * Copyright (C) 2009-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. + *) + +type fs = { + fs_location : location; + role : role; (** Special cases: root filesystem or /usr *) +} +and root = { + root_location : location; + inspection_data : inspection_data; +} +and location = { + mountable : Mountable.t; (** The device name or other mountable object.*) + vfs_type : string; (** Returned from [vfs_type] API. *) +} + +and role + | RoleRoot of inspection_data + | RoleUsr of inspection_data + | RoleSwap + | RoleOther +and inspection_data = { + format : format option; + os_type : os_type option; + distro : distro option; + package_format : package_format option; + package_management : package_management option; + product_name : string option; + product_variant : string option; + version : version option; + arch : string option; + hostname : string option; + fstab : fstab_entry list; + windows_systemroot : string option; + windows_software_hive : string option; + windows_system_hive : string option; + windows_current_control_set : string option; + drive_mappings : drive_mapping list; + is_live_disk : bool; + is_netinst_disk : bool; + is_multipart_disk : bool; +} +and format + | FORMAT_INSTALLED + | FORMAT_INSTALLER + (* in future: supplemental install disks *) +and os_type + | OS_TYPE_DOS + | OS_TYPE_FREEBSD + | OS_TYPE_HURD + | OS_TYPE_LINUX + | OS_TYPE_MINIX + | OS_TYPE_NETBSD + | OS_TYPE_OPENBSD + | OS_TYPE_WINDOWS +and distro + | DISTRO_ALPINE_LINUX + | DISTRO_ALTLINUX + | DISTRO_ARCHLINUX + | DISTRO_BUILDROOT + | DISTRO_CENTOS + | DISTRO_CIRROS + | DISTRO_COREOS + | DISTRO_DEBIAN + | DISTRO_FEDORA + | DISTRO_FREEBSD + | DISTRO_FREEDOS + | DISTRO_FRUGALWARE + | DISTRO_GENTOO + | DISTRO_LINUX_MINT + | DISTRO_MAGEIA + | DISTRO_MANDRIVA + | DISTRO_MEEGO + | DISTRO_NETBSD + | DISTRO_OPENBSD + | DISTRO_OPENSUSE + | DISTRO_ORACLE_LINUX + | DISTRO_PARDUS + | DISTRO_PLD_LINUX + | DISTRO_REDHAT_BASED + | DISTRO_RHEL + | DISTRO_SCIENTIFIC_LINUX + | DISTRO_SLACKWARE + | DISTRO_SLES + | DISTRO_SUSE_BASED + | DISTRO_TTYLINUX + | DISTRO_UBUNTU + | DISTRO_VOID_LINUX + | DISTRO_WINDOWS +and package_format + | PACKAGE_FORMAT_APK + | PACKAGE_FORMAT_DEB + | PACKAGE_FORMAT_EBUILD + | PACKAGE_FORMAT_PACMAN + | PACKAGE_FORMAT_PISI + | PACKAGE_FORMAT_PKGSRC + | PACKAGE_FORMAT_RPM + | PACKAGE_FORMAT_XBPS +and package_management + | PACKAGE_MANAGEMENT_APK + | PACKAGE_MANAGEMENT_APT + | PACKAGE_MANAGEMENT_DNF + | PACKAGE_MANAGEMENT_PACMAN + | PACKAGE_MANAGEMENT_PISI + | PACKAGE_MANAGEMENT_PORTAGE + | PACKAGE_MANAGEMENT_UP2DATE + | PACKAGE_MANAGEMENT_URPMI + | PACKAGE_MANAGEMENT_XBPS + | PACKAGE_MANAGEMENT_YUM + | PACKAGE_MANAGEMENT_ZYPPER +and version = int * int +and fstab_entry = Mountable.t * string (* mountable, mountpoint *) +and drive_mapping = string * string (* drive name, device *) + +val merge_inspection_data : inspection_data -> inspection_data -> inspection_data +(** [merge_inspection_data child parent] merges two sets of inspection + data into a single set. The parent inspection data fields, if + present, take precedence over the child inspection data fields. + + It's intended that you merge upwards, ie. + [merge_inspection_data usr root] *) + +val string_of_fs : fs -> string +(** Convert [fs] into a single line string, for debugging only. *) + +val string_of_root : root -> string +(** Convert [root] into a multi-line string, for debugging only. *) + +val string_of_location : location -> string +(** Convert [location] into a string, for debugging only. *) + +val string_of_inspection_data : inspection_data -> string +(** Convert [inspection_data] into a multi-line string, for debugging only. *) + +val string_of_format : format -> string +(** Convert [format] to a string. + The string is part of the public API. *) + +val string_of_os_type : os_type -> string +(** Convert [os_type] to a string. + The string is part of the public API. *) + +val string_of_distro : distro -> string +(** Convert [distro] to a string. + The string is part of the public API. *) + +val string_of_package_format : package_format -> string +(** Convert [package_format] to a string. + The string is part of the public API. *) + +val string_of_package_management : package_management -> string +(** Convert [package_management] to a string. + The string is part of the public API. *) + +val null_inspection_data : inspection_data +(** {!inspection_data} structure with all fields set to [None]. *) + +val inspect_fses : fs list ref +(** The global list of filesystems found by the previous call to + inspect_os. *) diff --git a/daemon/inspect_utils.ml b/daemon/inspect_utils.ml new file mode 100644 index 000000000..a75884d6b --- /dev/null +++ b/daemon/inspect_utils.ml @@ -0,0 +1,162 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 Unix +open Printf + +open Std_utils + +open Utils +open Inspect_types + +let max_augeas_file_size = 100 * 1000 + +let rec with_augeas configfiles f + let sysroot = Sysroot.sysroot () in + let chroot = Chroot.create (Sysroot.sysroot ()) in + + (* Security: + * + * The old C code had a few problems: It ignored non-regular-file + * objects (eg. devices), passing them to Augeas, so relying on + * Augeas to do the right thing. Also too-large regular files + * caused the whole inspection operation to fail. + * + * I have tried to improve this so that non-regular files and + * too large files are ignored (dropped from the configfiles list), + * so that Augeas won't touch them, but they also won't stop + * inspection. + *) + let safe_file file + Is.is_file ~followsymlinks:true file && ( + let size = (Chroot.f chroot Unix.stat file).Unix.st_size in + size <= max_augeas_file_size + ) + in + let configfiles = List.filter safe_file configfiles in + + let aug + Augeas.create sysroot None [Augeas.AugSaveNoop; Augeas.AugNoLoad] in + + protect + ~f:(fun () -> + (* Tell Augeas to only load configfiles and no other files. This + * prevents a rogue guest from performing a denial of service attack + * by having large, over-complicated configuration files which are + * unrelated to the task at hand. (Thanks Dominic Cleal). + * Note this requires Augeas >= 1.0.0 because of RHBZ#975412. + *) + let pathexpr = make_augeas_path_expression configfiles in + ignore (Augeas.rm aug pathexpr); + Augeas.load aug; + + (* Check that augeas did not get a parse error for any of the + * configfiles, otherwise we are silently missing information. + *) + let matches = Augeas.matches aug "/augeas/files//error" in + List.iter ( + fun match_ -> + List.iter ( + fun file -> + let errorpath = sprintf "/augeas/files%s/error" file in + if match_ = errorpath then ( + (* There's been an error - get the error details. *) + let get path + match Augeas.get aug (errorpath ^ path) with + | None -> "<missing>" + | Some v -> v + in + let message = get "message" in + let line = get "line" in + let charp = get "char" in + failwithf "%s:%s:%s: augeas parse failure: %s" + file line charp message + ) + ) configfiles + ) matches; + + f aug + ) + ~finally:( + fun () -> Augeas.close aug + ) + +(* Explained here: https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 *) +and make_augeas_path_expression files + let subexprs + List.map ( + fun file -> + (* v NB trailing '/' after filename *) + sprintf "\"%s/\" !~ regexp('^') + glob(incl) + regexp('/.*')" file + ) files in + let subexprs = String.concat " and " subexprs in + + let ret = sprintf "/augeas/load/*[ %s ]" subexprs in + if verbose () then + eprintf "augeas pathexpr = %s\n%!" ret; + + ret + +let is_file_nocase path + let path + try Some (Realpath.case_sensitive_path path) + with _ -> None in + match path with + | None -> false + | Some path -> Is.is_file path + +and is_dir_nocase path + let path + try Some (Realpath.case_sensitive_path path) + with _ -> None in + match path with + | None -> false + | Some path -> Is.is_dir path + +let re_major_minor = Str.regexp "\\([0-9]+\\)\\.\\([0-9]+\\)" +let re_major_no_minor = Str.regexp "\\([0-9]+\\)" + +let parse_version_from_major_minor str data + if Str.string_match re_major_minor str 0 || + Str.string_match re_major_no_minor str 0 then ( + let major + try Some (int_of_string (Str.matched_group 1 str)) + with Not_found | Invalid_argument _ | Failure _ -> None in + let minor + try Some (int_of_string (Str.matched_group 2 str)) + with Not_found | Invalid_argument _ | Failure _ -> None in + match major, minor with + | None, None -> data + | None, Some _ -> data + | Some major, None -> { data with version = Some (major, 0) } + | Some major, Some minor -> { data with version = Some (major, minor) } + ) + else ( + eprintf "parse_version_from_major_minor: cannot parse version from ‘%s’\n" + str; + data + ) + +let with_hive hive_filename f + let flags = [ Hivex.OPEN_UNSAFE ] in + let flags = if verbose () then Hivex.OPEN_VERBOSE :: flags else flags in + let h = Hivex.open_file hive_filename flags in + protect ~f:(fun () -> f h (Hivex.root h)) ~finally:(fun () -> Hivex.close h) + +let hivex_value_as_utf8 h value + utf16le_to_utf8 (snd (Hivex.value_value h value)) diff --git a/daemon/inspect_utils.mli b/daemon/inspect_utils.mli new file mode 100644 index 000000000..9dd57ac09 --- /dev/null +++ b/daemon/inspect_utils.mli @@ -0,0 +1,48 @@ +(* guestfs-inspection + * Copyright (C) 2009-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 with_augeas : string list -> (Augeas.t -> 'a) -> 'a +(** Open an Augeas handle, parse only 'configfiles' (these + files must exist), and then call 'f' with the Augeas handle. + + As a security measure, this bails if any file is too large for + a reasonable configuration file. After the call to 'f' the + Augeas handle is closed. *) + +val is_file_nocase : string -> bool +val is_dir_nocase : string -> bool +(** With a filesystem mounted under sysroot, check if [path] is + a file or directory under that sysroot. The [path] is + checked case-insensitively. *) + +val parse_version_from_major_minor : string -> Inspect_types.inspection_data -> + Inspect_types.inspection_data +(** Make a best effort attempt to parse either X or X.Y from a string, + usually the product_name string. *) + +val with_hive : string -> (Hivex.t -> Hivex.node -> 'a) -> 'a +(** Open a Windows registry "hive", and call the function on the + handle and root node. + + After the call to the function, the hive is always closed. + + The hive is opened readonly. *) + +val hivex_value_as_utf8 : Hivex.t -> Hivex.value -> string +(** Convert a Hivex value which we interpret as UTF-16LE to UTF-8. + The type field stored in the registry is ignored. *) diff --git a/daemon/mount.ml b/daemon/mount.ml index 4bb74fb82..40c81be0e 100644 --- a/daemon/mount.ml +++ b/daemon/mount.ml @@ -60,3 +60,64 @@ let mount_vfs options vfs mountable mountpoint let mount = mount_vfs None None let mount_ro = mount_vfs (Some "ro") None let mount_options options = mount_vfs (Some options) None + +(* Unmount everything mounted under /sysroot. + * + * We have to unmount in the correct order, so we sort the paths by + * longest first to ensure that child paths are unmounted by parent + * paths. + * + * This call is more important than it appears at first, because it + * is widely used by both test and production code in order to + * get back to a known state (nothing mounted, everything synchronized). + *) +let rec umount_all () + (* This is called from internal_autosync and generally as a cleanup + * function, and since the umount will definitely fail if any + * handles are open, we may as well close them. + *) + (* XXX + aug_finalize (); + hivex_finalize (); + journal_finalize (); + *) + + let sysroot = Sysroot.sysroot () in + let sysroot_len = String.length sysroot in + + let info = read_whole_file "/proc/self/mountinfo" in + let info = String.nsplit "\n" info in + + let mps = ref [] in + List.iter ( + fun line -> + let line = String.nsplit " " line in + (* The field of interest is the 5th field. Whitespace is escaped + * with octal sequences like \040 (for space). + * See fs/seq_file.c:mangle_path. + *) + if List.length line >= 5 then ( + let mp = List.nth line 4 in + let mp = proc_unmangle_path mp in + + (* Allow a mount directory like "/sysroot" or "/sysroot/..." *) + if (sysroot_len > 0 && String.is_prefix mp sysroot) || + (String.is_prefix mp sysroot && + String.length mp > sysroot_len && + mp.[sysroot_len] = '/') then + push_front mp mps + ) + ) info; + + let mps = !mps in + let mps = List.sort compare_longest_first mps in + + (* Unmount them. *) + List.iter ( + fun mp -> ignore (command "umount" [mp]) + ) mps + +and compare_longest_first s1 s2 + let n1 = String.length s1 in + let n2 = String.length s2 in + n2 - n1 diff --git a/daemon/mount.mli b/daemon/mount.mli index e43d97c42..abf538521 100644 --- a/daemon/mount.mli +++ b/daemon/mount.mli @@ -20,3 +20,5 @@ val mount : Mountable.t -> string -> unit val mount_ro : Mountable.t -> string -> unit val mount_options : string -> Mountable.t -> string -> unit val mount_vfs : string option -> string option -> Mountable.t -> string -> unit + +val umount_all : unit -> unit diff --git a/daemon/utils.ml b/daemon/utils.ml index 808e575fd..e53b4bf02 100644 --- a/daemon/utils.ml +++ b/daemon/utils.ml @@ -247,3 +247,103 @@ let proc_unmangle_path path let is_small_file path is_regular_file path && (stat path).st_size <= 2 * 1048 * 1024 + +let unix_canonical_path path + let is_absolute = String.length path > 0 && path.[0] = '/' in + let path = String.nsplit "/" path in + let path = List.filter ((<>) "") path in + (if is_absolute then "/" else "") ^ String.concat "/" path + +(* Note that we cannot use iconv here because inside the appliance + * all i18n databases are deleted. For the same reason we cannot + * use functions like hivex_value_string, as they also use iconv + * internally. + * + * https://en.wikipedia.org/wiki/UTF-16 + * Also inspired by functions in glib's glib/gutf8.c + *) +let rec utf16le_to_utf8 instr + (* If the length is odd and the last character is ASCII NUL, just + * drop that. (If it's not ASCII NUL, then there's an error) + *) + let len = String.length instr in + let instr + if len mod 1 = 1 then ( + if instr.[len-1] = '\000' then String.sub instr 0 (len-1) + else invalid_arg "input is not a valid UTF16-LE string: length is odd" + ) else instr in + + (* The length should now be even. If the last two bytes are + * '\0\0' then assume it's a NUL-terminated string from the + * Windows registry and drop both characters. + *) + let len = String.length instr in + let instr + if len >= 2 && instr.[len-2] = '\000' && instr.[len-1] = '\000' then + String.sub instr 0 (len-2) + else instr in + + let outbuf = Buffer.create len in + + (* Encode a wide character as UTF-8 and write to outbuf. + * Basically this is g_unichar_to_utf8 implemented in OCaml. + *) + let encode_utf8 c + let first, len + if c < 0x80 then + (0, 1) + else if c < 0x800 then + (0xc0, 2) + else if c < 0x10000 then + (0xe0, 3) + else if c < 0x200000 then + (0xf0, 4) + else if c < 0x4000000 then + (0xf8, 5) + else + (0xfc, 6) in + let rec loop i c + if i = 0 then Buffer.add_char outbuf (Char.chr (c lor first)) + else if i > 0 then ( + loop (i-1) (c lsr 6); + Buffer.add_char outbuf (Char.chr ((c land 0x3f) lor 0x80)) + ) + in + loop (len-1) c + in + + (* Loop over the input UTF16-LE characters. *) + let is_high_surrogate c = c >= 0xd800 && c < 0xdc00 + and is_low_surrogate c = c >= 0xdc00 && c < 0xe000 + and surrogate_value highc lowc + 0x1_0000 + (highc - 0xd800) * 0x400 + lowc - 0xdc00 + in + + let len = String.length instr in + let rec loop i + if i+1 >= len then () + else ( + let c = Char.code instr.[i] + (Char.code instr.[i+1] lsl 8) in + + let wc, skip + (* High surrogate - must come first. *) + if is_high_surrogate c then ( + if i+3 >= len then + invalid_arg "input is not a valid UTF16-LE string: high surrogate at end of string"; + let lowc = Char.code instr.[i+2] + (Char.code instr.[i+3] lsl 8) in + if not (is_low_surrogate lowc) then + invalid_arg "input is not a valid UTF16-LE string: high surrogate not followed by low surrogate"; + (surrogate_value c lowc, 4) + ) + else if is_low_surrogate c then + invalid_arg "input is not a valid UTF16-LE string: unexpected low surrogate" + else + (c, 2) in + + encode_utf8 wc; + loop (i+skip) + ) + in + loop 0; + + Buffer.contents outbuf diff --git a/daemon/utils.mli b/daemon/utils.mli index d3c8bdf4d..94a77de01 100644 --- a/daemon/utils.mli +++ b/daemon/utils.mli @@ -85,3 +85,15 @@ val commandr : ?flags:command_flag list -> string -> string list -> (int * strin val is_small_file : string -> bool (** Return true if the path is a small regular file. *) + +val unix_canonical_path : string -> string +(** Canonicalize a Unix path, so "///usr//local//" -> "/usr/local" + + The path is modified in place because the result is always + the same length or shorter than the argument passed. *) + +val utf16le_to_utf8 : string -> string +(** Convert a UTF16-LE string to UTF-8. + + This uses a simple internal implementation since we cannot use + iconv inside the daemon. *) diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES index 02adbf73a..7fec72a62 100644 --- a/docs/C_SOURCE_FILES +++ b/docs/C_SOURCE_FILES @@ -65,6 +65,7 @@ customize/perl_edit-c.c daemon/9p.c daemon/acl.c daemon/actions.h +daemon/augeas-c.c daemon/augeas.c daemon/available.c daemon/base64.c @@ -308,7 +309,6 @@ lib/info.c lib/inspect-apps.c lib/inspect-fs-cd.c lib/inspect-fs-unix.c -lib/inspect-fs-windows.c lib/inspect-fs.c lib/inspect-icon.c lib/inspect.c diff --git a/generator/actions.ml b/generator/actions.ml index 75742397a..a745f6244 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -51,6 +51,7 @@ let daemon_functions Actions_core_deprecated.daemon_functions @ Actions_debug.daemon_functions @ Actions_hivex.daemon_functions @ + Actions_inspection.daemon_functions @ Actions_tsk.daemon_functions @ Actions_yara.daemon_functions diff --git a/generator/actions_inspection.ml b/generator/actions_inspection.ml index b7ea5a4de..93a6ed6a7 100644 --- a/generator/actions_inspection.ml +++ b/generator/actions_inspection.ml @@ -22,10 +22,11 @@ open Types (* Inspection APIs. *) -let non_daemon_functions = [ +let daemon_functions = [ { defaults with name = "inspect_os"; added = (1, 5, 3); style = RStringList (RMountable, "roots"), [], []; + impl = OCaml "Inspect.inspect_os"; shortdesc = "inspect disk and return list of operating systems found"; longdesc = "\ This function uses other libguestfs functions and certain @@ -61,8 +62,24 @@ Please read L<guestfs(3)/INSPECTION> for more details. See also C<guestfs_list_filesystems>." }; { defaults with + name = "inspect_get_roots"; added = (1, 7, 3); + style = RStringList (RMountable, "roots"), [], []; + impl = OCaml "Inspect.inspect_get_roots"; + shortdesc = "return list of operating systems found by last inspection"; + longdesc = "\ +This function is a convenient way to get the list of root +devices, as returned from a previous call to C<guestfs_inspect_os>, +but without redoing the whole inspection process. + +This returns an empty list if either no root devices were +found or the caller has not called C<guestfs_inspect_os>. + +Please read L<guestfs(3)/INSPECTION> for more details." }; + + { defaults with name = "inspect_get_type"; added = (1, 5, 3); style = RString (RPlainString, "name"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_type"; shortdesc = "get type of inspected operating system"; longdesc = "\ This returns the type of the inspected operating system. @@ -116,6 +133,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_get_arch"; added = (1, 5, 3); style = RString (RPlainString, "arch"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_arch"; shortdesc = "get architecture of inspected operating system"; longdesc = "\ This returns the architecture of the inspected operating system. @@ -130,6 +148,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_get_distro"; added = (1, 5, 3); style = RString (RPlainString, "distro"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_distro"; shortdesc = "get distro of inspected operating system"; longdesc = "\ This returns the distro (distribution) of the inspected operating @@ -286,6 +305,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_get_major_version"; added = (1, 5, 3); style = RInt "major", [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_major_version"; shortdesc = "get major version of inspected operating system"; longdesc = "\ This returns the major version number of the inspected operating @@ -305,6 +325,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_get_minor_version"; added = (1, 5, 3); style = RInt "minor", [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_minor_version"; shortdesc = "get minor version of inspected operating system"; longdesc = "\ This returns the minor version number of the inspected operating @@ -318,6 +339,7 @@ See also C<guestfs_inspect_get_major_version>." }; { defaults with name = "inspect_get_product_name"; added = (1, 5, 3); style = RString (RPlainString, "product"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_product_name"; shortdesc = "get product name of inspected operating system"; longdesc = "\ This returns the product name of the inspected operating @@ -331,55 +353,9 @@ string C<unknown> is returned. Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with - name = "inspect_get_mountpoints"; added = (1, 5, 3); - style = RHashtable (RPlainString, RMountable, "mountpoints"), [String (Mountable, "root")], []; - shortdesc = "get mountpoints of inspected operating system"; - longdesc = "\ -This returns a hash of where we think the filesystems -associated with this operating system should be mounted. -Callers should note that this is at best an educated guess -made by reading configuration files such as F</etc/fstab>. -I<In particular note> that this may return filesystems -which are non-existent or not mountable and callers should -be prepared to handle or ignore failures if they try to -mount them. - -Each element in the returned hashtable has a key which -is the path of the mountpoint (eg. F</boot>) and a value -which is the filesystem that would be mounted there -(eg. F</dev/sda1>). - -Non-mounted devices such as swap devices are I<not> -returned in this list. - -For operating systems like Windows which still use drive -letters, this call will only return an entry for the first -drive \"mounted on\" F</>. For information about the -mapping of drive letters to partitions, see -C<guestfs_inspect_get_drive_mappings>. - -Please read L<guestfs(3)/INSPECTION> for more details. -See also C<guestfs_inspect_get_filesystems>." }; - - { defaults with - name = "inspect_get_filesystems"; added = (1, 5, 3); - style = RStringList (RMountable, "filesystems"), [String (Mountable, "root")], []; - shortdesc = "get filesystems associated with inspected operating system"; - longdesc = "\ -This returns a list of all the filesystems that we think -are associated with this operating system. This includes -the root filesystem, other ordinary filesystems, and -non-mounted devices like swap partitions. - -In the case of a multi-boot virtual machine, it is possible -for a filesystem to be shared between operating systems. - -Please read L<guestfs(3)/INSPECTION> for more details. -See also C<guestfs_inspect_get_mountpoints>." }; - - { defaults with name = "inspect_get_windows_systemroot"; added = (1, 5, 25); style = RString (RPlainString, "systemroot"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_windows_systemroot"; shortdesc = "get Windows systemroot of inspected operating system"; longdesc = "\ This returns the Windows systemroot of the inspected guest. @@ -392,22 +368,9 @@ the case then an error is returned. Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with - name = "inspect_get_roots"; added = (1, 7, 3); - style = RStringList (RMountable, "roots"), [], []; - shortdesc = "return list of operating systems found by last inspection"; - longdesc = "\ -This function is a convenient way to get the list of root -devices, as returned from a previous call to C<guestfs_inspect_os>, -but without redoing the whole inspection process. - -This returns an empty list if either no root devices were -found or the caller has not called C<guestfs_inspect_os>. - -Please read L<guestfs(3)/INSPECTION> for more details." }; - - { defaults with name = "inspect_get_package_format"; added = (1, 7, 5); style = RString (RPlainString, "packageformat"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_package_format"; shortdesc = "get package format used by the operating system"; longdesc = "\ This function and C<guestfs_inspect_get_package_management> return @@ -430,6 +393,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_get_package_management"; added = (1, 7, 5); style = RString (RPlainString, "packagemanagement"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_package_management"; shortdesc = "get package management tool used by the operating system"; longdesc = "\ C<guestfs_inspect_get_package_format> and this function return @@ -450,111 +414,9 @@ Future versions of libguestfs may return other strings. Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with - name = "inspect_list_applications2"; added = (1, 19, 56); - style = RStructList ("applications2", "application2"), [String (Mountable, "root")], []; - shortdesc = "get list of applications installed in the operating system"; - longdesc = "\ -Return the list of applications installed in the operating system. - -I<Note:> This call works differently from other parts of the -inspection API. You have to call C<guestfs_inspect_os>, then -C<guestfs_inspect_get_mountpoints>, then mount up the disks, -before calling this. Listing applications is a significantly -more difficult operation which requires access to the full -filesystem. Also note that unlike the other -C<guestfs_inspect_get_*> calls which are just returning -data cached in the libguestfs handle, this call actually reads -parts of the mounted filesystems during the call. - -This returns an empty list if the inspection code was not able -to determine the list of applications. - -The application structure contains the following fields: - -=over 4 - -=item C<app2_name> - -The name of the application. For Red Hat-derived and Debian-derived -Linux guests, this is the package name. - -=item C<app2_display_name> - -The display name of the application, sometimes localized to the -install language of the guest operating system. - -If unavailable this is returned as an empty string C<\"\">. -Callers needing to display something can use C<app2_name> instead. - -=item C<app2_epoch> - -For package managers which use epochs, this contains the epoch of -the package (an integer). If unavailable, this is returned as C<0>. - -=item C<app2_version> - -The version string of the application or package. If unavailable -this is returned as an empty string C<\"\">. - -=item C<app2_release> - -The release string of the application or package, for package -managers that use this. If unavailable this is returned as an -empty string C<\"\">. - -=item C<app2_arch> - -The architecture string of the application or package, for package -managers that use this. If unavailable this is returned as an empty -string C<\"\">. - -=item C<app2_install_path> - -The installation path of the application (on operating systems -such as Windows which use installation paths). This path is -in the format used by the guest operating system, it is not -a libguestfs path. - -If unavailable this is returned as an empty string C<\"\">. - -=item C<app2_trans_path> - -The install path translated into a libguestfs path. -If unavailable this is returned as an empty string C<\"\">. - -=item C<app2_publisher> - -The name of the publisher of the application, for package -managers that use this. If unavailable this is returned -as an empty string C<\"\">. - -=item C<app2_url> - -The URL (eg. upstream URL) of the application. -If unavailable this is returned as an empty string C<\"\">. - -=item C<app2_source_package> - -For packaging systems which support this, the name of the source -package. If unavailable this is returned as an empty string C<\"\">. - -=item C<app2_summary> - -A short (usually one line) description of the application or package. -If unavailable this is returned as an empty string C<\"\">. - -=item C<app2_description> - -A longer description of the application or package. -If unavailable this is returned as an empty string C<\"\">. - -=back - -Please read L<guestfs(3)/INSPECTION> for more details." }; - - { defaults with name = "inspect_get_hostname"; added = (1, 7, 9); style = RString (RPlainString, "hostname"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_hostname"; shortdesc = "get hostname of the operating system"; longdesc = "\ This function returns the hostname of the operating system @@ -568,6 +430,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_get_format"; added = (1, 9, 4); style = RString (RPlainString, "format"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_format"; shortdesc = "get format of inspected operating system"; longdesc = "\ This returns the format of the inspected operating system. You @@ -600,6 +463,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_is_live"; added = (1, 9, 4); style = RBool "live", [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_is_live"; shortdesc = "get live flag for install disk"; longdesc = "\ If C<guestfs_inspect_get_format> returns C<installer> (this @@ -611,6 +475,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_is_netinst"; added = (1, 9, 4); style = RBool "netinst", [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_is_netinst"; shortdesc = "get netinst (network installer) flag for install disk"; longdesc = "\ If C<guestfs_inspect_get_format> returns C<installer> (this @@ -624,6 +489,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_is_multipart"; added = (1, 9, 4); style = RBool "multipart", [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_is_multipart"; shortdesc = "get multipart flag for install disk"; longdesc = "\ If C<guestfs_inspect_get_format> returns C<installer> (this @@ -635,6 +501,7 @@ Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with name = "inspect_get_product_variant"; added = (1, 9, 13); style = RString (RPlainString, "variant"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_product_variant"; shortdesc = "get product variant of inspected operating system"; longdesc = "\ This returns the product variant of the inspected operating @@ -663,6 +530,7 @@ C<guestfs_inspect_get_major_version>." }; { defaults with name = "inspect_get_windows_current_control_set"; added = (1, 9, 17); style = RString (RPlainString, "controlset"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_windows_current_control_set"; shortdesc = "get Windows CurrentControlSet of inspected operating system"; longdesc = "\ This returns the Windows CurrentControlSet of the inspected guest. @@ -675,8 +543,94 @@ the case then an error is returned. Please read L<guestfs(3)/INSPECTION> for more details." }; { defaults with + name = "inspect_get_windows_software_hive"; added = (1, 35, 26); + style = RString (RPlainString, "path"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_windows_software_hive"; + shortdesc = "get the path of the Windows software hive"; + longdesc = "\ +This returns the path to the hive (binary Windows Registry file) +corresponding to HKLM\\SOFTWARE. + +This call assumes that the guest is Windows and that the guest +has a software hive file with the right name. If this is not the +case then an error is returned. This call does not check that the +hive is a valid Windows Registry hive. + +You can use C<guestfs_hivex_open> to read or write to the hive. + +Please read L<guestfs(3)/INSPECTION> for more details." }; + + { defaults with + name = "inspect_get_windows_system_hive"; added = (1, 35, 26); + style = RString (RPlainString, "path"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_windows_system_hive"; + shortdesc = "get the path of the Windows system hive"; + longdesc = "\ +This returns the path to the hive (binary Windows Registry file) +corresponding to HKLM\\SYSTEM. + +This call assumes that the guest is Windows and that the guest +has a system hive file with the right name. If this is not the +case then an error is returned. This call does not check that the +hive is a valid Windows Registry hive. + +You can use C<guestfs_hivex_open> to read or write to the hive. + +Please read L<guestfs(3)/INSPECTION> for more details." }; + + { defaults with + name = "inspect_get_mountpoints"; added = (1, 5, 3); + style = RHashtable (RPlainString, RMountable, "mountpoints"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_mountpoints"; + shortdesc = "get mountpoints of inspected operating system"; + longdesc = "\ +This returns a hash of where we think the filesystems +associated with this operating system should be mounted. +Callers should note that this is at best an educated guess +made by reading configuration files such as F</etc/fstab>. +I<In particular note> that this may return filesystems +which are non-existent or not mountable and callers should +be prepared to handle or ignore failures if they try to +mount them. + +Each element in the returned hashtable has a key which +is the path of the mountpoint (eg. F</boot>) and a value +which is the filesystem that would be mounted there +(eg. F</dev/sda1>). + +Non-mounted devices such as swap devices are I<not> +returned in this list. + +For operating systems like Windows which still use drive +letters, this call will only return an entry for the first +drive \"mounted on\" F</>. For information about the +mapping of drive letters to partitions, see +C<guestfs_inspect_get_drive_mappings>. + +Please read L<guestfs(3)/INSPECTION> for more details. +See also C<guestfs_inspect_get_filesystems>." }; + + { defaults with + name = "inspect_get_filesystems"; added = (1, 5, 3); + style = RStringList (RMountable, "filesystems"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_filesystems"; + shortdesc = "get filesystems associated with inspected operating system"; + longdesc = "\ +This returns a list of all the filesystems that we think +are associated with this operating system. This includes +the root filesystem, other ordinary filesystems, and +non-mounted devices like swap partitions. + +In the case of a multi-boot virtual machine, it is possible +for a filesystem to be shared between operating systems. + +Please read L<guestfs(3)/INSPECTION> for more details. +See also C<guestfs_inspect_get_mountpoints>." }; + + { defaults with name = "inspect_get_drive_mappings"; added = (1, 9, 17); style = RHashtable (RPlainString, RDevice, "drives"), [String (Mountable, "root")], []; + impl = OCaml "Inspect.inspect_get_drive_mappings"; shortdesc = "get drive letter mappings"; longdesc = "\ This call is useful for Windows which uses a primitive system @@ -708,6 +662,112 @@ Please read L<guestfs(3)/INSPECTION> for more details. See also C<guestfs_inspect_get_mountpoints>, C<guestfs_inspect_get_filesystems>." }; +] + +let non_daemon_functions = [ + { defaults with + name = "inspect_list_applications2"; added = (1, 19, 56); + style = RStructList ("applications2", "application2"), [String (Mountable, "root")], []; + shortdesc = "get list of applications installed in the operating system"; + longdesc = "\ +Return the list of applications installed in the operating system. + +I<Note:> This call works differently from other parts of the +inspection API. You have to call C<guestfs_inspect_os>, then +C<guestfs_inspect_get_mountpoints>, then mount up the disks, +before calling this. Listing applications is a significantly +more difficult operation which requires access to the full +filesystem. Also note that unlike the other +C<guestfs_inspect_get_*> calls which are just returning +data cached in the libguestfs handle, this call actually reads +parts of the mounted filesystems during the call. + +This returns an empty list if the inspection code was not able +to determine the list of applications. + +The application structure contains the following fields: + +=over 4 + +=item C<app2_name> + +The name of the application. For Red Hat-derived and Debian-derived +Linux guests, this is the package name. + +=item C<app2_display_name> + +The display name of the application, sometimes localized to the +install language of the guest operating system. + +If unavailable this is returned as an empty string C<\"\">. +Callers needing to display something can use C<app2_name> instead. + +=item C<app2_epoch> + +For package managers which use epochs, this contains the epoch of +the package (an integer). If unavailable, this is returned as C<0>. + +=item C<app2_version> + +The version string of the application or package. If unavailable +this is returned as an empty string C<\"\">. + +=item C<app2_release> + +The release string of the application or package, for package +managers that use this. If unavailable this is returned as an +empty string C<\"\">. + +=item C<app2_arch> + +The architecture string of the application or package, for package +managers that use this. If unavailable this is returned as an empty +string C<\"\">. + +=item C<app2_install_path> + +The installation path of the application (on operating systems +such as Windows which use installation paths). This path is +in the format used by the guest operating system, it is not +a libguestfs path. + +If unavailable this is returned as an empty string C<\"\">. + +=item C<app2_trans_path> + +The install path translated into a libguestfs path. +If unavailable this is returned as an empty string C<\"\">. + +=item C<app2_publisher> + +The name of the publisher of the application, for package +managers that use this. If unavailable this is returned +as an empty string C<\"\">. + +=item C<app2_url> + +The URL (eg. upstream URL) of the application. +If unavailable this is returned as an empty string C<\"\">. + +=item C<app2_source_package> + +For packaging systems which support this, the name of the source +package. If unavailable this is returned as an empty string C<\"\">. + +=item C<app2_summary> + +A short (usually one line) description of the application or package. +If unavailable this is returned as an empty string C<\"\">. + +=item C<app2_description> + +A longer description of the application or package. +If unavailable this is returned as an empty string C<\"\">. + +=back + +Please read L<guestfs(3)/INSPECTION> for more details." }; + { defaults with name = "inspect_get_icon"; added = (1, 11, 12); style = RBufferOut "icon", [String (Mountable, "root")], [OBool "favicon"; OBool "highquality"]; @@ -773,38 +833,4 @@ advice before using trademarks in applications. =back" }; - { defaults with - name = "inspect_get_windows_software_hive"; added = (1, 35, 26); - style = RString (RPlainString, "path"), [String (Mountable, "root")], []; - shortdesc = "get the path of the Windows software hive"; - longdesc = "\ -This returns the path to the hive (binary Windows Registry file) -corresponding to HKLM\\SOFTWARE. - -This call assumes that the guest is Windows and that the guest -has a software hive file with the right name. If this is not the -case then an error is returned. This call does not check that the -hive is a valid Windows Registry hive. - -You can use C<guestfs_hivex_open> to read or write to the hive. - -Please read L<guestfs(3)/INSPECTION> for more details." }; - - { defaults with - name = "inspect_get_windows_system_hive"; added = (1, 35, 26); - style = RString (RPlainString, "path"), [String (Mountable, "root")], []; - shortdesc = "get the path of the Windows system hive"; - longdesc = "\ -This returns the path to the hive (binary Windows Registry file) -corresponding to HKLM\\SYSTEM. - -This call assumes that the guest is Windows and that the guest -has a system hive file with the right name. If this is not the -case then an error is returned. This call does not check that the -hive is a valid Windows Registry hive. - -You can use C<guestfs_hivex_open> to read or write to the hive. - -Please read L<guestfs(3)/INSPECTION> for more details." }; - ] diff --git a/generator/actions_inspection.mli b/generator/actions_inspection.mli index 327f7aa4f..06b8116c4 100644 --- a/generator/actions_inspection.mli +++ b/generator/actions_inspection.mli @@ -19,3 +19,4 @@ (* Please read generator/README first. *) val non_daemon_functions : Types.action list +val daemon_functions : Types.action list diff --git a/generator/daemon.ml b/generator/daemon.ml index f20c87bea..b8d0a3a88 100644 --- a/generator/daemon.ml +++ b/generator/daemon.ml @@ -597,6 +597,30 @@ return_string_mountable (value retv) } } +/* Implement RStringList (RMountable, _). */ +static char ** +return_string_mountable_list (value retv) +{ + CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); + value v; + char *m; + + while (retv != Val_int (0)) { + v = Field (retv, 0); + m = return_string_mountable (v); + if (m == NULL) + return NULL; + if (add_string_nodup (&ret, m) == -1) + return NULL; + retv = Field (retv, 1); + } + + if (end_stringsbuf (&ret) == -1) + return NULL; + + return take_stringsbuf (&ret); /* caller frees */ +} + /* Implement RHashtable (RPlainString, RPlainString, _). */ static char ** return_hashtable_string_string (value retv) @@ -649,6 +673,34 @@ return_hashtable_mountable_string (value retv) return take_stringsbuf (&ret); /* caller frees */ } +/* Implement RHashtable (RPlainString, RMountable, _). */ +static char ** +return_hashtable_string_mountable (value retv) +{ + CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (ret); + value sv, v, mv; + char *m; + + while (retv != Val_int (0)) { + v = Field (retv, 0); /* (string, Mountable.t) */ + sv = Field (v, 0); /* string */ + if (add_string (&ret, String_val (sv)) == -1) + return NULL; + mv = Field (v, 1); /* Mountable.t */ + m = return_string_mountable (mv); + if (m == NULL) + return NULL; + if (add_string_nodup (&ret, m) == -1) + return NULL; + retv = Field (retv, 1); + } + + if (end_stringsbuf (&ret) == -1) + return NULL; + + return take_stringsbuf (&ret); /* caller frees */ +} + "; (* Implement code for returning structs and struct lists. *) @@ -889,9 +941,12 @@ return_hashtable_mountable_string (value retv) | RString (RMountable, _) -> pr " char *ret = return_string_mountable (retv);\n"; pr " CAMLreturnT (char *, ret); /* caller frees */\n" - | RStringList _ -> + | RStringList ((RPlainString|RDevice), _) -> pr " char **ret = return_string_list (retv);\n"; pr " CAMLreturnT (char **, ret); /* caller frees */\n" + | RStringList (RMountable, _) -> + pr " char **ret = return_string_mountable_list (retv);\n"; + pr " CAMLreturnT (char **, ret); /* caller frees */\n" | RStruct (_, typ) -> pr " guestfs_int_%s *ret =\n" typ; pr " return_%s (retv);\n" typ; @@ -902,12 +957,16 @@ return_hashtable_mountable_string (value retv) pr " return_%s_list (retv);\n" typ; pr " /* caller frees */\n"; pr " CAMLreturnT (guestfs_int_%s_list *, ret);\n" typ - | RHashtable (RPlainString, RPlainString, _) -> + | RHashtable (RPlainString, RPlainString, _) + | RHashtable (RPlainString, RDevice, _) -> pr " char **ret = return_hashtable_string_string (retv);\n"; pr " CAMLreturnT (char **, ret); /* caller frees */\n" | RHashtable (RMountable, RPlainString, _) -> pr " char **ret = return_hashtable_mountable_string (retv);\n"; pr " CAMLreturnT (char **, ret); /* caller frees */\n" + | RHashtable (RPlainString, RMountable, _) -> + pr " char **ret = return_hashtable_string_mountable (retv);\n"; + pr " CAMLreturnT (char **, ret); /* caller frees */\n" | RHashtable _ -> assert false | RBufferOut _ -> assert false ); diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml index dec02f5fa..0a41f9c24 100644 --- a/generator/proc_nr.ml +++ b/generator/proc_nr.ml @@ -484,6 +484,29 @@ let proc_nr = [ 474, "internal_yara_scan"; 475, "file_architecture"; 476, "list_filesystems"; +477, "inspect_os"; +478, "inspect_get_roots"; +479, "inspect_get_format"; +480, "inspect_get_type"; +481, "inspect_get_distro"; +482, "inspect_get_package_format"; +483, "inspect_get_package_management"; +484, "inspect_get_product_name"; +485, "inspect_get_product_variant"; +486, "inspect_get_major_version"; +487, "inspect_get_minor_version"; +488, "inspect_get_arch"; +489, "inspect_get_hostname"; +490, "inspect_get_windows_systemroot"; +491, "inspect_get_windows_software_hive"; +492, "inspect_get_windows_system_hive"; +493, "inspect_get_windows_current_control_set"; +494, "inspect_is_live"; +495, "inspect_is_netinst"; +496, "inspect_is_multipart"; +497, "inspect_get_mountpoints"; +498, "inspect_get_filesystems"; +499, "inspect_get_drive_mappings"; ] (* End of list. If adding a new entry, add it at the end of the list diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR index b86395733..761fcd3ac 100644 --- a/lib/MAX_PROC_NR +++ b/lib/MAX_PROC_NR @@ -1 +1 @@ -476 +499 diff --git a/lib/Makefile.am b/lib/Makefile.am index 109330452..cc59a62d8 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -99,7 +99,6 @@ libguestfs_la_SOURCES = \ inspect-fs.c \ inspect-fs-cd.c \ inspect-fs-unix.c \ - inspect-fs-windows.c \ inspect-icon.c \ journal.c \ launch.c \ diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h index b1c3db5ff..52072b65e 100644 --- a/lib/guestfs-internal.h +++ b/lib/guestfs-internal.h @@ -129,7 +129,7 @@ * those. */ #define MAX_SMALL_FILE_SIZE (2 * 1000 * 1000) -#define MAX_AUGEAS_FILE_SIZE (100 * 1000) +#define MAX_AUGEAS_FILE_SIZE (100 * 1000) /* XXX REMOVE */ /* Maximum RPM or dpkg database we will download to /tmp. RPM * 'Packages' database can get very large: 70 MB is roughly the @@ -470,12 +470,6 @@ struct guestfs_h { struct event *events; size_t nr_events; - /* Information gathered by inspect_os. Must be freed by calling - * guestfs_int_free_inspect_info. - */ - struct inspect_fs *fses; - size_t nr_fses; - /* Private data area. */ struct hash_table *pda; struct pda_entry *pda_next; @@ -536,133 +530,6 @@ struct version { int v_micro; }; -/* Per-filesystem data stored for inspect_os. */ -enum inspect_os_format { - OS_FORMAT_UNKNOWN = 0, - OS_FORMAT_INSTALLED, - OS_FORMAT_INSTALLER, - /* in future: supplemental disks */ -}; - -enum inspect_os_type { - OS_TYPE_UNKNOWN = 0, - OS_TYPE_LINUX, - OS_TYPE_WINDOWS, - OS_TYPE_FREEBSD, - OS_TYPE_NETBSD, - OS_TYPE_HURD, - OS_TYPE_DOS, - OS_TYPE_OPENBSD, - OS_TYPE_MINIX, -}; - -enum inspect_os_distro { - OS_DISTRO_UNKNOWN = 0, - OS_DISTRO_DEBIAN, - OS_DISTRO_FEDORA, - OS_DISTRO_REDHAT_BASED, - OS_DISTRO_RHEL, - OS_DISTRO_WINDOWS, - OS_DISTRO_PARDUS, - OS_DISTRO_ARCHLINUX, - OS_DISTRO_GENTOO, - OS_DISTRO_UBUNTU, - OS_DISTRO_MEEGO, - OS_DISTRO_LINUX_MINT, - OS_DISTRO_MANDRIVA, - OS_DISTRO_SLACKWARE, - OS_DISTRO_CENTOS, - OS_DISTRO_SCIENTIFIC_LINUX, - OS_DISTRO_TTYLINUX, - OS_DISTRO_MAGEIA, - OS_DISTRO_OPENSUSE, - OS_DISTRO_BUILDROOT, - OS_DISTRO_CIRROS, - OS_DISTRO_FREEDOS, - OS_DISTRO_SUSE_BASED, - OS_DISTRO_SLES, - OS_DISTRO_OPENBSD, - OS_DISTRO_ORACLE_LINUX, - OS_DISTRO_FREEBSD, - OS_DISTRO_NETBSD, - OS_DISTRO_COREOS, - OS_DISTRO_ALPINE_LINUX, - OS_DISTRO_ALTLINUX, - OS_DISTRO_FRUGALWARE, - OS_DISTRO_PLD_LINUX, - OS_DISTRO_VOID_LINUX, -}; - -enum inspect_os_package_format { - OS_PACKAGE_FORMAT_UNKNOWN = 0, - OS_PACKAGE_FORMAT_RPM, - OS_PACKAGE_FORMAT_DEB, - OS_PACKAGE_FORMAT_PACMAN, - OS_PACKAGE_FORMAT_EBUILD, - OS_PACKAGE_FORMAT_PISI, - OS_PACKAGE_FORMAT_PKGSRC, - OS_PACKAGE_FORMAT_APK, - OS_PACKAGE_FORMAT_XBPS, -}; - -enum inspect_os_package_management { - OS_PACKAGE_MANAGEMENT_UNKNOWN = 0, - OS_PACKAGE_MANAGEMENT_YUM, - OS_PACKAGE_MANAGEMENT_UP2DATE, - OS_PACKAGE_MANAGEMENT_APT, - OS_PACKAGE_MANAGEMENT_PACMAN, - OS_PACKAGE_MANAGEMENT_PORTAGE, - OS_PACKAGE_MANAGEMENT_PISI, - OS_PACKAGE_MANAGEMENT_URPMI, - OS_PACKAGE_MANAGEMENT_ZYPPER, - OS_PACKAGE_MANAGEMENT_DNF, - OS_PACKAGE_MANAGEMENT_APK, - OS_PACKAGE_MANAGEMENT_XBPS, -}; - -enum inspect_os_role { - OS_ROLE_UNKNOWN = 0, - OS_ROLE_ROOT, - OS_ROLE_USR, -}; - -/** - * The inspection code maintains one of these structures per mountable - * filesystem found in the disk image. The struct (or structs) which - * have the C<role> attribute set to C<OS_ROLE_ROOT> are inspection roots, - * each corresponding to a single guest. Note that a filesystem can be - * shared between multiple guests. - */ -struct inspect_fs { - enum inspect_os_role role; - char *mountable; - enum inspect_os_type type; - enum inspect_os_distro distro; - enum inspect_os_package_format package_format; - enum inspect_os_package_management package_management; - char *product_name; - char *product_variant; - struct version version; - char *arch; - char *hostname; - char *windows_systemroot; - char *windows_software_hive; - char *windows_system_hive; - char *windows_current_control_set; - char **drive_mappings; - enum inspect_os_format format; - int is_live_disk; - int is_netinst_disk; - int is_multipart_disk; - struct inspect_fstab_entry *fstab; - size_t nr_fstab; -}; - -struct inspect_fstab_entry { - char *mountable; - char *mountpoint; -}; - struct guestfs_message_header; struct guestfs_message_error; struct guestfs_progress; @@ -854,44 +721,12 @@ extern int guestfs_int_set_backend (guestfs_h *g, const char *method); } while (0) /* inspect.c */ -extern void guestfs_int_free_inspect_info (guestfs_h *g); -extern char *guestfs_int_download_to_tmp (guestfs_h *g, struct inspect_fs *fs, const char *filename, const char *basename, uint64_t max_size); -extern struct inspect_fs *guestfs_int_search_for_root (guestfs_h *g, const char *root); -extern int guestfs_int_is_partition (guestfs_h *g, const char *partition); +extern char *guestfs_int_download_to_tmp (guestfs_h *g, const char *filename, const char *basename, uint64_t max_size); /* inspect-fs.c */ -extern int guestfs_int_is_file_nocase (guestfs_h *g, const char *); -extern int guestfs_int_is_dir_nocase (guestfs_h *g, const char *); -extern int guestfs_int_check_for_filesystem_on (guestfs_h *g, - const char *mountable); extern int guestfs_int_parse_unsigned_int (guestfs_h *g, const char *str); extern int guestfs_int_parse_unsigned_int_ignore_trailing (guestfs_h *g, const char *str); -extern int guestfs_int_parse_major_minor (guestfs_h *g, struct inspect_fs *fs); -extern char *guestfs_int_first_line_of_file (guestfs_h *g, const char *filename); extern int guestfs_int_first_egrep_of_file (guestfs_h *g, const char *filename, const char *eregex, int iflag, char **ret); -extern void guestfs_int_check_package_format (guestfs_h *g, struct inspect_fs *fs); -extern void guestfs_int_check_package_management (guestfs_h *g, struct inspect_fs *fs); -extern void guestfs_int_merge_fs_inspections (guestfs_h *g, struct inspect_fs *dst, struct inspect_fs *src); - -/* inspect-fs-unix.c */ -extern int guestfs_int_check_linux_root (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_linux_usr (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_freebsd_root (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_netbsd_root (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_openbsd_root (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_hurd_root (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_minix_root (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_coreos_root (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_coreos_usr (guestfs_h *g, struct inspect_fs *fs); - -/* inspect-fs-windows.c */ -extern char *guestfs_int_case_sensitive_path_silently (guestfs_h *g, const char *); -extern char * guestfs_int_get_windows_systemroot (guestfs_h *g); -extern int guestfs_int_check_windows_root (guestfs_h *g, struct inspect_fs *fs, char *windows_systemroot); - -/* inspect-fs-cd.c */ -extern int guestfs_int_check_installer_root (guestfs_h *g, struct inspect_fs *fs); -extern int guestfs_int_check_installer_iso (guestfs_h *g, struct inspect_fs *fs, const char *device); /* dbdump.c */ typedef int (*guestfs_int_db_dump_callback) (guestfs_h *g, const unsigned char *key, size_t keylen, const unsigned char *value, size_t valuelen, void *opaque); @@ -911,6 +746,55 @@ extern virConnectPtr guestfs_int_open_libvirt_connection (guestfs_h *g, const ch #endif /* osinfo.c */ +enum inspect_os_type { + OS_TYPE_UNKNOWN = 0, + OS_TYPE_LINUX, + OS_TYPE_WINDOWS, + OS_TYPE_FREEBSD, + OS_TYPE_NETBSD, + OS_TYPE_HURD, + OS_TYPE_DOS, + OS_TYPE_OPENBSD, + OS_TYPE_MINIX, +}; + +enum inspect_os_distro { + OS_DISTRO_UNKNOWN = 0, + OS_DISTRO_DEBIAN, + OS_DISTRO_FEDORA, + OS_DISTRO_REDHAT_BASED, + OS_DISTRO_RHEL, + OS_DISTRO_WINDOWS, + OS_DISTRO_PARDUS, + OS_DISTRO_ARCHLINUX, + OS_DISTRO_GENTOO, + OS_DISTRO_UBUNTU, + OS_DISTRO_MEEGO, + OS_DISTRO_LINUX_MINT, + OS_DISTRO_MANDRIVA, + OS_DISTRO_SLACKWARE, + OS_DISTRO_CENTOS, + OS_DISTRO_SCIENTIFIC_LINUX, + OS_DISTRO_TTYLINUX, + OS_DISTRO_MAGEIA, + OS_DISTRO_OPENSUSE, + OS_DISTRO_BUILDROOT, + OS_DISTRO_CIRROS, + OS_DISTRO_FREEDOS, + OS_DISTRO_SUSE_BASED, + OS_DISTRO_SLES, + OS_DISTRO_OPENBSD, + OS_DISTRO_ORACLE_LINUX, + OS_DISTRO_FREEBSD, + OS_DISTRO_NETBSD, + OS_DISTRO_COREOS, + OS_DISTRO_ALPINE_LINUX, + OS_DISTRO_ALTLINUX, + OS_DISTRO_FRUGALWARE, + OS_DISTRO_PLD_LINUX, + OS_DISTRO_VOID_LINUX, +}; + struct osinfo { /* Data provided by libosinfo database. */ enum inspect_os_type type; diff --git a/lib/handle.c b/lib/handle.c index 91f5f755d..57fda24b1 100644 --- a/lib/handle.c +++ b/lib/handle.c @@ -369,7 +369,6 @@ guestfs_close (guestfs_h *g) guestfs_int_free_fuse (g); #endif - guestfs_int_free_inspect_info (g); guestfs_int_free_drives (g); for (hp = g->hv_params; hp; hp = hp_next) { diff --git a/lib/inspect-apps.c b/lib/inspect-apps.c index 25192340c..7a5d45926 100644 --- a/lib/inspect-apps.c +++ b/lib/inspect-apps.c @@ -46,12 +46,12 @@ #include "structs-cleanups.h" #ifdef DB_DUMP -static struct guestfs_application2_list *list_applications_rpm (guestfs_h *g, struct inspect_fs *fs); +static struct guestfs_application2_list *list_applications_rpm (guestfs_h *g, const char *root); #endif -static struct guestfs_application2_list *list_applications_deb (guestfs_h *g, struct inspect_fs *fs); -static struct guestfs_application2_list *list_applications_pacman (guestfs_h *g, struct inspect_fs *fs); -static struct guestfs_application2_list *list_applications_apk (guestfs_h *g, struct inspect_fs *fs); -static struct guestfs_application2_list *list_applications_windows (guestfs_h *g, struct inspect_fs *fs); +static struct guestfs_application2_list *list_applications_deb (guestfs_h *g, const char *root); +static struct guestfs_application2_list *list_applications_pacman (guestfs_h *g, const char *root); +static struct guestfs_application2_list *list_applications_apk (guestfs_h *g, const char *root); +static struct guestfs_application2_list *list_applications_windows (guestfs_h *g, const char *root); static void add_application (guestfs_h *g, struct guestfs_application2_list *, const char *name, const char *display_name, int32_t epoch, const char *version, const char *release, const char *arch, const char *install_path, const char *publisher, const char *url, const char *source, const char *summary, const char *description); static void sort_applications (struct guestfs_application2_list *); @@ -109,66 +109,52 @@ struct guestfs_application2_list * guestfs_impl_inspect_list_applications2 (guestfs_h *g, const char *root) { struct guestfs_application2_list *ret = NULL; - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) + CLEANUP_FREE char *format = NULL; + CLEANUP_FREE char *type = NULL; + CLEANUP_FREE char *package_format = NULL; + + format = guestfs_inspect_get_format (g, root); + if (!format) + return NULL; + type = guestfs_inspect_get_type (g, root); + if (!type) + return NULL; + package_format = guestfs_inspect_get_package_format (g, root); + if (!package_format) return NULL; /* Presently we can only list applications for installed disks. It * is possible in future to get lists of packages from installers. */ - if (fs->format == OS_FORMAT_INSTALLED) { - switch (fs->type) { - case OS_TYPE_LINUX: - case OS_TYPE_HURD: - switch (fs->package_format) { - case OS_PACKAGE_FORMAT_RPM: + if (STREQ (format, "installed")) { + if (STREQ (type, "linux") || STREQ (type, "hurd")) { + if (STREQ (package_format, "rpm")) { #ifdef DB_DUMP - ret = list_applications_rpm (g, fs); + ret = list_applications_rpm (g, root); if (ret == NULL) return NULL; #endif - break; - - case OS_PACKAGE_FORMAT_DEB: - ret = list_applications_deb (g, fs); + } + else if (STREQ (package_format, "deb")) { + ret = list_applications_deb (g, root); if (ret == NULL) return NULL; - break; - - case OS_PACKAGE_FORMAT_PACMAN: - ret = list_applications_pacman (g, fs); + } + else if (STREQ (package_format, "pacman")) { + ret = list_applications_pacman (g, root); if (ret == NULL) return NULL; - break; - - case OS_PACKAGE_FORMAT_APK: - ret = list_applications_apk (g, fs); + } + else if (STREQ (package_format, "apk")) { + ret = list_applications_apk (g, root); if (ret == NULL) return NULL; - break; - - case OS_PACKAGE_FORMAT_EBUILD: - case OS_PACKAGE_FORMAT_PISI: - case OS_PACKAGE_FORMAT_PKGSRC: - case OS_PACKAGE_FORMAT_XBPS: - case OS_PACKAGE_FORMAT_UNKNOWN: - ; /* nothing */ } - break; - - case OS_TYPE_WINDOWS: - ret = list_applications_windows (g, fs); + } + else if (STREQ (type, "windows")) { + ret = list_applications_windows (g, root); if (ret == NULL) return NULL; - break; - - case OS_TYPE_FREEBSD: - case OS_TYPE_MINIX: - case OS_TYPE_NETBSD: - case OS_TYPE_DOS: - case OS_TYPE_OPENBSD: - case OS_TYPE_UNKNOWN: - ; /* nothing */ } } @@ -386,20 +372,20 @@ read_package (guestfs_h *g, #pragma GCC diagnostic pop static struct guestfs_application2_list * -list_applications_rpm (guestfs_h *g, struct inspect_fs *fs) +list_applications_rpm (guestfs_h *g, const char *root) { CLEANUP_FREE char *Name = NULL, *Packages = NULL; struct rpm_names_list list = { .names = NULL, .len = 0 }; struct guestfs_application2_list *apps = NULL; struct read_package_data data; - Name = guestfs_int_download_to_tmp (g, fs, + Name = guestfs_int_download_to_tmp (g, "/var/lib/rpm/Name", "rpm_Name", MAX_PKG_DB_SIZE); if (Name == NULL) goto error; - Packages = guestfs_int_download_to_tmp (g, fs, + Packages = guestfs_int_download_to_tmp (g, "/var/lib/rpm/Packages", "rpm_Packages", MAX_PKG_DB_SIZE); if (Packages == NULL) @@ -437,7 +423,7 @@ list_applications_rpm (guestfs_h *g, struct inspect_fs *fs) #endif /* defined DB_DUMP */ static struct guestfs_application2_list * -list_applications_deb (guestfs_h *g, struct inspect_fs *fs) +list_applications_deb (guestfs_h *g, const char *root) { CLEANUP_FREE char *status = NULL; struct guestfs_application2_list *apps = NULL, *ret = NULL; @@ -452,7 +438,7 @@ list_applications_deb (guestfs_h *g, struct inspect_fs *fs) char **continuation_field = NULL; size_t continuation_field_len = 0; - status = guestfs_int_download_to_tmp (g, fs, "/var/lib/dpkg/status", "status", + status = guestfs_int_download_to_tmp (g, "/var/lib/dpkg/status", "status", MAX_PKG_DB_SIZE); if (status == NULL) return NULL; @@ -594,7 +580,7 @@ list_applications_deb (guestfs_h *g, struct inspect_fs *fs) } static struct guestfs_application2_list * -list_applications_pacman (guestfs_h *g, struct inspect_fs *fs) +list_applications_pacman (guestfs_h *g, const char *root) { CLEANUP_FREE char *desc_file = NULL, *fname = NULL, *line = NULL; CLEANUP_FREE_DIRENT_LIST struct guestfs_dirent_list *local_db = NULL; @@ -628,7 +614,7 @@ list_applications_pacman (guestfs_h *g, struct inspect_fs *fs) fname = safe_malloc (g, strlen (curr->name) + path_len + 1); sprintf (fname, "/var/lib/pacman/local/%s/desc", curr->name); free (desc_file); - desc_file = guestfs_int_download_to_tmp (g, fs, fname, curr->name, 8192); + desc_file = guestfs_int_download_to_tmp (g, fname, curr->name, 8192); /* The desc files are small (4K). If the desc file does not exist or is * larger than the 8K limit we've used, the database is probably corrupted, @@ -725,7 +711,7 @@ list_applications_pacman (guestfs_h *g, struct inspect_fs *fs) } static struct guestfs_application2_list * -list_applications_apk (guestfs_h *g, struct inspect_fs *fs) +list_applications_apk (guestfs_h *g, const char *root) { CLEANUP_FREE char *installed = NULL, *line = NULL; struct guestfs_application2_list *apps = NULL, *ret = NULL; @@ -736,7 +722,7 @@ list_applications_apk (guestfs_h *g, struct inspect_fs *fs) CLEANUP_FREE char *name = NULL, *version = NULL, *release = NULL, *arch = NULL, *url = NULL, *description = NULL; - installed = guestfs_int_download_to_tmp (g, fs, "/lib/apk/db/installed", + installed = guestfs_int_download_to_tmp (g, "/lib/apk/db/installed", "installed", MAX_PKG_DB_SIZE); if (installed == NULL) return NULL; @@ -843,14 +829,15 @@ list_applications_apk (guestfs_h *g, struct inspect_fs *fs) static void list_applications_windows_from_path (guestfs_h *g, struct guestfs_application2_list *apps, const char **path, size_t path_len); static struct guestfs_application2_list * -list_applications_windows (guestfs_h *g, struct inspect_fs *fs) +list_applications_windows (guestfs_h *g, const char *root) { struct guestfs_application2_list *ret = NULL; - - if (!fs->windows_software_hive) + CLEANUP_FREE char *software_hive + guestfs_inspect_get_windows_software_hive (g, root); + if (!software_hive) return NULL; - if (guestfs_hivex_open (g, fs->windows_software_hive, + if (guestfs_hivex_open (g, software_hive, GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, GUESTFS_HIVEX_OPEN_UNSAFE, 1, -1) == -1) diff --git a/lib/inspect-fs-cd.c b/lib/inspect-fs-cd.c index c9a4e219e..ad524c66b 100644 --- a/lib/inspect-fs-cd.c +++ b/lib/inspect-fs-cd.c @@ -34,6 +34,8 @@ #include "guestfs-internal.h" #include "structs-cleanups.h" +#if 0 + /* Debian/Ubuntu install disks are easy ... * * These files are added by the debian-cd program, and it is worth @@ -605,3 +607,5 @@ guestfs_int_check_installer_iso (guestfs_h *g, struct inspect_fs *fs, return 1; } + +#endif diff --git a/lib/inspect-fs-unix.c b/lib/inspect-fs-unix.c index 9b6bfbf38..cc2628e12 100644 --- a/lib/inspect-fs-unix.c +++ b/lib/inspect-fs-unix.c @@ -37,6 +37,7 @@ #include "guestfs.h" #include "guestfs-internal.h" +#if 0 COMPILE_REGEXP (re_fedora, "Fedora release (\\d+)", 0) COMPILE_REGEXP (re_rhel_old, "Red Hat.*release (\\d+).*Update (\\d+)", 0) COMPILE_REGEXP (re_rhel, "Red Hat.*release (\\d+)\\.(\\d+)", 0) @@ -79,715 +80,6 @@ COMPILE_REGEXP (re_altlinux, " (?:(\\d+)(?:\\.(\\d+)(?:[\\.\\d]+)?)?)\\s+\\((?:[ COMPILE_REGEXP (re_frugalware, "Frugalware (\\d+)\\.(\\d+)", 0) COMPILE_REGEXP (re_pldlinux, "(\\d+)\\.(\\d+) PLD Linux", 0) -static void check_architecture (guestfs_h *g, struct inspect_fs *fs); -static int check_hostname_unix (guestfs_h *g, struct inspect_fs *fs); -static int check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs); -static int check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs); -static int check_fstab (guestfs_h *g, struct inspect_fs *fs); -static void add_fstab_entry (guestfs_h *g, struct inspect_fs *fs, - const char *mountable, const char *mp); -static char *resolve_fstab_device (guestfs_h *g, const char *spec, - Hash_table *md_map, - enum inspect_os_type os_type); -static int inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, const char **configfiles, int (*f) (guestfs_h *, struct inspect_fs *)); -static void canonical_mountpoint (char *mp); - -/* Hash structure for uuid->path lookups */ -typedef struct md_uuid { - uint32_t uuid[4]; - char *path; -} md_uuid; - -static size_t uuid_hash(const void *x, size_t table_size); -static bool uuid_cmp(const void *x, const void *y); -static void md_uuid_free(void *x); - -static int parse_uuid(const char *str, uint32_t *uuid); - -/* Hash structure for path(mdadm)->path(appliance) lookup */ -typedef struct { - char *mdadm; - char *app; -} mdadm_app; - -static size_t mdadm_app_hash(const void *x, size_t table_size); -static bool mdadm_app_cmp(const void *x, const void *y); -static void mdadm_app_free(void *x); - -static ssize_t map_app_md_devices (guestfs_h *g, Hash_table **map); -static int map_md_devices(guestfs_h *g, Hash_table **map); - -/* Set fs->product_name to the first line of the release file. */ -static int -parse_release_file (guestfs_h *g, struct inspect_fs *fs, - const char *release_filename) -{ - fs->product_name = guestfs_int_first_line_of_file (g, release_filename); - if (fs->product_name == NULL) - return -1; - if (STREQ (fs->product_name, "")) { - free (fs->product_name); - fs->product_name = NULL; - error (g, _("release file %s is empty or malformed"), release_filename); - return -1; - } - return 0; -} - -/* Parse a os-release file. - * - * Only few fields are parsed, falling back to the usual detection if we - * cannot read all of them. - * - * For the format of os-release, see also: - * http://www.freedesktop.org/software/systemd/man/os-release.html - */ -static int -parse_os_release (guestfs_h *g, struct inspect_fs *fs, const char *filename) -{ - int64_t size; - CLEANUP_FREE_STRING_LIST char **lines = NULL; - size_t i; - enum inspect_os_distro distro = OS_DISTRO_UNKNOWN; - CLEANUP_FREE char *product_name = NULL; - struct version version; - guestfs_int_version_from_values (&version, -1, -1, 0); - - /* Don't trust guestfs_read_lines not to break with very large files. - * Check the file size is something reasonable first. - */ - size = guestfs_filesize (g, filename); - if (size == -1) - /* guestfs_filesize failed and has already set error in handle */ - return -1; - if (size > MAX_SMALL_FILE_SIZE) { - error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), - filename, size); - return -1; - } - - lines = guestfs_read_lines (g, filename); - if (lines == NULL) - return -1; - - for (i = 0; lines[i] != NULL; ++i) { - const char *line = lines[i]; - const char *value; - size_t value_len; - - if (line[0] == '#') - continue; - - value = strchr (line, '='); - if (value == NULL) - continue; - - ++value; - value_len = strlen (line) - (value - line); - if (value_len > 1 && value[0] == '"' && value[value_len-1] == '"') { - ++value; - value_len -= 2; - } - -#define VALUE_IS(a) STREQLEN(value, a, value_len) - if (STRPREFIX (line, "ID=")) { - if (VALUE_IS ("alpine")) - distro = OS_DISTRO_ALPINE_LINUX; - else if (VALUE_IS ("altlinux")) - distro = OS_DISTRO_ALTLINUX; - else if (VALUE_IS ("arch")) - distro = OS_DISTRO_ARCHLINUX; - else if (VALUE_IS ("centos")) - distro = OS_DISTRO_CENTOS; - else if (VALUE_IS ("coreos")) - distro = OS_DISTRO_COREOS; - else if (VALUE_IS ("debian")) - distro = OS_DISTRO_DEBIAN; - else if (VALUE_IS ("fedora")) - distro = OS_DISTRO_FEDORA; - else if (VALUE_IS ("frugalware")) - distro = OS_DISTRO_FRUGALWARE; - else if (VALUE_IS ("mageia")) - distro = OS_DISTRO_MAGEIA; - else if (VALUE_IS ("opensuse")) - distro = OS_DISTRO_OPENSUSE; - else if (VALUE_IS ("pld")) - distro = OS_DISTRO_PLD_LINUX; - else if (VALUE_IS ("rhel")) - distro = OS_DISTRO_RHEL; - else if (VALUE_IS ("sles") || VALUE_IS ("sled")) - distro = OS_DISTRO_SLES; - else if (VALUE_IS ("ubuntu")) - distro = OS_DISTRO_UBUNTU; - else if (VALUE_IS ("void")) - distro = OS_DISTRO_VOID_LINUX; - } else if (STRPREFIX (line, "PRETTY_NAME=")) { - free (product_name); - product_name = safe_strndup (g, value, value_len); - } else if (STRPREFIX (line, "VERSION_ID=")) { - CLEANUP_FREE char *buf - safe_asprintf (g, "%.*s", (int) value_len, value); - if (guestfs_int_version_from_x_y_or_x (g, &version, buf) == -1) - return -1; - } -#undef VALUE_IS - } - - /* If we haven't got all the fields, exit right away. */ - if (distro == OS_DISTRO_UNKNOWN || product_name == NULL) - return 0; - - if (version.v_major == -1 || version.v_minor == -1) { - /* Void Linux has no VERSION_ID (yet), but since it's a rolling - * distro and has no other version/release-like file. */ - if (distro == OS_DISTRO_VOID_LINUX) - version_init_null (&version); - else - return 0; - } - - /* Apparently, os-release in Debian and CentOS does not provide the full - * version number in VERSION_ID, but just the "major" part of it. - * Hence, if version.v_minor is 0, act as there was no information in - * os-release, which will continue the inspection using the release files - * as done previously. - */ - if ((distro == OS_DISTRO_DEBIAN || distro == OS_DISTRO_CENTOS) && - version.v_minor == 0) - return 0; - - /* We got everything, so set the fields and report the inspection - * was successful. - */ - fs->distro = distro; - fs->product_name = product_name; - product_name = NULL; - fs->version = version; - - return 1; -} - -/* Ubuntu has /etc/lsb-release containing: - * DISTRIB_ID=Ubuntu # Distro - * DISTRIB_RELEASE=10.04 # Version - * DISTRIB_CODENAME=lucid - * DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS" # Product name - * - * [Ubuntu-derived ...] Linux Mint was found to have this: - * DISTRIB_ID=LinuxMint - * DISTRIB_RELEASE=10 - * DISTRIB_CODENAME=julia - * DISTRIB_DESCRIPTION="Linux Mint 10 Julia" - * Linux Mint also has /etc/linuxmint/info with more information, - * but we can use the LSB file. - * - * Mandriva has: - * LSB_VERSION=lsb-4.0-amd64:lsb-4.0-noarch - * DISTRIB_ID=MandrivaLinux - * DISTRIB_RELEASE=2010.1 - * DISTRIB_CODENAME=Henry_Farman - * DISTRIB_DESCRIPTION="Mandriva Linux 2010.1" - * Mandriva also has a normal release file called /etc/mandriva-release. - * - * CoreOS has a /etc/lsb-release link to /usr/share/coreos/lsb-release containing: - * DISTRIB_ID=CoreOS - * DISTRIB_RELEASE=647.0.0 - * DISTRIB_CODENAME="Red Dog" - * DISTRIB_DESCRIPTION="CoreOS 647.0.0" - */ -static int -parse_lsb_release (guestfs_h *g, struct inspect_fs *fs, const char *filename) -{ - int64_t size; - CLEANUP_FREE_STRING_LIST char **lines = NULL; - size_t i; - int r = 0; - - /* Don't trust guestfs_head_n not to break with very large files. - * Check the file size is something reasonable first. - */ - size = guestfs_filesize (g, filename); - if (size == -1) - /* guestfs_filesize failed and has already set error in handle */ - return -1; - if (size > MAX_SMALL_FILE_SIZE) { - error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), - filename, size); - return -1; - } - - lines = guestfs_head_n (g, 10, filename); - if (lines == NULL) - return -1; - - for (i = 0; lines[i] != NULL; ++i) { - if (fs->distro == 0 && - STREQ (lines[i], "DISTRIB_ID=Ubuntu")) { - fs->distro = OS_DISTRO_UBUNTU; - r = 1; - } - else if (fs->distro == 0 && - STREQ (lines[i], "DISTRIB_ID=LinuxMint")) { - fs->distro = OS_DISTRO_LINUX_MINT; - r = 1; - } - else if (fs->distro == 0 && - STREQ (lines[i], "DISTRIB_ID=MandrivaLinux")) { - fs->distro = OS_DISTRO_MANDRIVA; - r = 1; - } - else if (fs->distro == 0 && - STREQ (lines[i], "DISTRIB_ID=\"Mageia\"")) { - fs->distro = OS_DISTRO_MAGEIA; - r = 1; - } - else if (fs->distro == 0 && - STREQ (lines[i], "DISTRIB_ID=CoreOS")) { - fs->distro = OS_DISTRO_COREOS; - r = 1; - } - else if (STRPREFIX (lines[i], "DISTRIB_RELEASE=")) { - if (guestfs_int_version_from_x_y_or_x (g, &fs->version, &lines[i][16]) == -1) - return -1; - } - else if (fs->product_name == NULL && - (STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=\"") || - STRPREFIX (lines[i], "DISTRIB_DESCRIPTION='"))) { - const size_t len = strlen (lines[i]) - 21 - 1; - fs->product_name = safe_strndup (g, &lines[i][21], len); - r = 1; - } - else if (fs->product_name == NULL && - STRPREFIX (lines[i], "DISTRIB_DESCRIPTION=")) { - const size_t len = strlen (lines[i]) - 20; - fs->product_name = safe_strndup (g, &lines[i][20], len); - r = 1; - } - } - - /* The unnecessary construct in the next line is required to - * workaround -Wstrict-overflow warning in gcc 4.5.1. - */ - return r ? 1 : 0; -} - -static int -parse_suse_release (guestfs_h *g, struct inspect_fs *fs, const char *filename) -{ - int64_t size; - CLEANUP_FREE_STRING_LIST char **lines = NULL; - int r = -1; - - /* Don't trust guestfs_head_n not to break with very large files. - * Check the file size is something reasonable first. - */ - size = guestfs_filesize (g, filename); - if (size == -1) - /* guestfs_filesize failed and has already set error in handle */ - return -1; - if (size > MAX_SMALL_FILE_SIZE) { - error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), - filename, size); - return -1; - } - - lines = guestfs_head_n (g, 10, filename); - if (lines == NULL) - return -1; - - if (lines[0] == NULL) - goto out; - - /* First line is dist release name */ - fs->product_name = safe_strdup (g, lines[0]); - - /* Match SLES first because openSuSE regex overlaps some SLES release strings */ - if (match (g, fs->product_name, re_sles) || match (g, fs->product_name, re_nld)) { - char *major, *minor; - - fs->distro = OS_DISTRO_SLES; - - /* Second line contains version string */ - if (lines[1] == NULL) - goto out; - major = match1 (g, lines[1], re_sles_version); - if (major == NULL) - goto out; - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) - goto out; - - /* Third line contains service pack string */ - if (lines[2] == NULL) - goto out; - minor = match1 (g, lines[2], re_sles_patchlevel); - if (minor == NULL) - goto out; - fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor); - free (minor); - if (fs->version.v_minor == -1) - goto out; - } - else if (match (g, fs->product_name, re_opensuse)) { - fs->distro = OS_DISTRO_OPENSUSE; - - /* Second line contains version string */ - if (lines[1] == NULL) - goto out; - if (guestfs_int_version_from_x_y_re (g, &fs->version, lines[1], - re_opensuse_version) == -1) - goto out; - } - - r = 0; - - out: - return r; -} - -/* The currently mounted device is known to be a Linux root. Try to - * determine from this the distro, version, etc. Also parse - * /etc/fstab to determine the arrangement of mountpoints and - * associated devices. - */ -int -guestfs_int_check_linux_root (guestfs_h *g, struct inspect_fs *fs) -{ - int r; - char *major, *minor; - - fs->type = OS_TYPE_LINUX; - - if (guestfs_is_file_opts (g, "/etc/os-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - r = parse_os_release (g, fs, "/etc/os-release"); - if (r == -1) /* error */ - return -1; - if (r == 1) /* ok - detected the release from this file */ - goto skip_release_checks; - } - - if (guestfs_is_file_opts (g, "/etc/lsb-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - r = parse_lsb_release (g, fs, "/etc/lsb-release"); - if (r == -1) /* error */ - return -1; - if (r == 1) /* ok - detected the release from this file */ - goto skip_release_checks; - } - - /* RHEL-based distros include a "/etc/redhat-release" file, hence their - * checks need to be performed before the Red-Hat one. - */ - if (guestfs_is_file_opts (g, "/etc/oracle-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - - fs->distro = OS_DISTRO_ORACLE_LINUX; - - if (parse_release_file (g, fs, "/etc/oracle-release") == -1) - return -1; - - if (match2 (g, fs->product_name, re_oracle_linux_old, &major, &minor) || - match2 (g, fs->product_name, re_oracle_linux, &major, &minor)) { - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) { - free (minor); - return -1; - } - fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor); - free (minor); - if (fs->version.v_minor == -1) - return -1; - } else if ((major = match1 (g, fs->product_name, re_oracle_linux_no_minor)) != NULL) { - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) - return -1; - fs->version.v_minor = 0; - } - } - else if (guestfs_is_file_opts (g, "/etc/centos-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_CENTOS; - - if (parse_release_file (g, fs, "/etc/centos-release") == -1) - return -1; - - if (match2 (g, fs->product_name, re_centos_old, &major, &minor) || - match2 (g, fs->product_name, re_centos, &major, &minor)) { - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) { - free (minor); - return -1; - } - fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor); - free (minor); - if (fs->version.v_minor == -1) - return -1; - } - else if ((major = match1 (g, fs->product_name, re_centos_no_minor)) != NULL) { - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) - return -1; - fs->version.v_minor = 0; - } - } - else if (guestfs_is_file_opts (g, "/etc/altlinux-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_ALTLINUX; - - if (parse_release_file (g, fs, "/etc/altlinux-release") == -1) - return -1; - - if (guestfs_int_version_from_x_y_re (g, &fs->version, fs->product_name, - re_altlinux) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/redhat-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_REDHAT_BASED; /* Something generic Red Hat-like. */ - - if (parse_release_file (g, fs, "/etc/redhat-release") == -1) - return -1; - - if ((major = match1 (g, fs->product_name, re_fedora)) != NULL) { - fs->distro = OS_DISTRO_FEDORA; - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) - return -1; - } - else if (match2 (g, fs->product_name, re_rhel_old, &major, &minor) || - match2 (g, fs->product_name, re_rhel, &major, &minor)) { - fs->distro = OS_DISTRO_RHEL; - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) { - free (minor); - return -1; - } - fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor); - free (minor); - if (fs->version.v_minor == -1) - return -1; - } - else if ((major = match1 (g, fs->product_name, re_rhel_no_minor)) != NULL) { - fs->distro = OS_DISTRO_RHEL; - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) - return -1; - fs->version.v_minor = 0; - } - else if (match2 (g, fs->product_name, re_centos_old, &major, &minor) || - match2 (g, fs->product_name, re_centos, &major, &minor)) { - fs->distro = OS_DISTRO_CENTOS; - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) { - free (minor); - return -1; - } - fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor); - free (minor); - if (fs->version.v_minor == -1) - return -1; - } - else if ((major = match1 (g, fs->product_name, re_centos_no_minor)) != NULL) { - fs->distro = OS_DISTRO_CENTOS; - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) - return -1; - fs->version.v_minor = 0; - } - else if (match2 (g, fs->product_name, re_scientific_linux_old, &major, &minor) || - match2 (g, fs->product_name, re_scientific_linux, &major, &minor)) { - fs->distro = OS_DISTRO_SCIENTIFIC_LINUX; - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) { - free (minor); - return -1; - } - fs->version.v_minor = guestfs_int_parse_unsigned_int (g, minor); - free (minor); - if (fs->version.v_minor == -1) - return -1; - } - else if ((major = match1 (g, fs->product_name, re_scientific_linux_no_minor)) != NULL) { - fs->distro = OS_DISTRO_SCIENTIFIC_LINUX; - fs->version.v_major = guestfs_int_parse_unsigned_int (g, major); - free (major); - if (fs->version.v_major == -1) - return -1; - fs->version.v_minor = 0; - } - } - else if (guestfs_is_file_opts (g, "/etc/debian_version", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_DEBIAN; - - if (parse_release_file (g, fs, "/etc/debian_version") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/pardus-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_PARDUS; - - if (parse_release_file (g, fs, "/etc/pardus-release") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/arch-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_ARCHLINUX; - - /* /etc/arch-release file is empty and I can't see a way to - * determine the actual release or product string. - */ - } - else if (guestfs_is_file_opts (g, "/etc/gentoo-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_GENTOO; - - if (parse_release_file (g, fs, "/etc/gentoo-release") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/meego-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_MEEGO; - - if (parse_release_file (g, fs, "/etc/meego-release") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/slackware-version", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_SLACKWARE; - - if (parse_release_file (g, fs, "/etc/slackware-version") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/ttylinux-target", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_TTYLINUX; - - if (parse_release_file (g, fs, "/etc/ttylinux-target") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/SuSE-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_SUSE_BASED; - - if (parse_suse_release (g, fs, "/etc/SuSE-release") == -1) - return -1; - - } - /* CirrOS versions providing a own version file. - */ - else if (guestfs_is_file_opts (g, "/etc/cirros/version", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_CIRROS; - - if (parse_release_file (g, fs, "/etc/cirros/version") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - /* Buildroot (http://buildroot.net) is an embedded Linux distro - * toolkit. It is used by specific distros such as Cirros. - */ - else if (guestfs_is_file_opts (g, "/etc/br-version", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - if (guestfs_is_file_opts (g, "/usr/share/cirros/logo", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) - fs->distro = OS_DISTRO_CIRROS; - else - fs->distro = OS_DISTRO_BUILDROOT; - - /* /etc/br-version has the format YYYY.MM[-git/hg/svn release] */ - if (parse_release_file (g, fs, "/etc/br-version") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/alpine-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_ALPINE_LINUX; - - if (parse_release_file (g, fs, "/etc/alpine-release") == -1) - return -1; - - if (guestfs_int_parse_major_minor (g, fs) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/frugalware-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_FRUGALWARE; - - if (parse_release_file (g, fs, "/etc/frugalware-release") == -1) - return -1; - - if (guestfs_int_version_from_x_y_re (g, &fs->version, fs->product_name, - re_frugalware) == -1) - return -1; - } - else if (guestfs_is_file_opts (g, "/etc/pld-release", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - fs->distro = OS_DISTRO_PLD_LINUX; - - if (parse_release_file (g, fs, "/etc/pld-release") == -1) - return -1; - - if (guestfs_int_version_from_x_y_re (g, &fs->version, fs->product_name, - re_pldlinux) == -1) - return -1; - } - - skip_release_checks:; - - /* Determine the architecture. */ - check_architecture (g, fs); - - /* We already know /etc/fstab exists because it's part of the test - * for Linux root above. We must now parse this file to determine - * which filesystems are used by the operating system and how they - * are mounted. - */ - const char *configfiles[] = { "/etc/fstab", "/etc/mdadm.conf", NULL }; - if (inspect_with_augeas (g, fs, configfiles, check_fstab) == -1) - return -1; - - /* Determine hostname. */ - if (check_hostname_unix (g, fs) == -1) - return -1; - - return 0; -} - /* The currently mounted device looks like a Linux /usr. */ int guestfs_int_check_linux_usr (guestfs_h *g, struct inspect_fs *fs) @@ -1072,44 +364,6 @@ guestfs_int_check_coreos_usr (guestfs_h *g, struct inspect_fs *fs) return 0; } -static void -check_architecture (guestfs_h *g, struct inspect_fs *fs) -{ - const char *binaries[] - { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" }; - size_t i; - char *arch = NULL; - - for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) { - /* Allow symlinks when checking the binaries:,so in case they are - * relative ones (which can be resolved within the same partition), - * then we can check the architecture of their target. - */ - if (guestfs_is_file_opts (g, binaries[i], - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) { - CLEANUP_FREE char *resolved = NULL; - - /* Ignore errors from realpath and file_architecture calls. */ - guestfs_push_error_handler (g, NULL, NULL); - resolved = guestfs_realpath (g, binaries[i]); - /* If is_file above succeeded realpath should too, but better - * be safe than sorry. - */ - if (resolved) - arch = guestfs_file_architecture (g, resolved); - guestfs_pop_error_handler (g); - - if (arch) { - /* String will be owned by handle, freed by - * guestfs_int_free_inspect_info. - */ - fs->arch = arch; - break; - } - } - } -} - /* Try several methods to determine the hostname from a Linux or * FreeBSD guest. Note that type and distro have been set, so we can * use that information to direct the search. @@ -1198,29 +452,6 @@ check_hostname_unix (guestfs_h *g, struct inspect_fs *fs) return 0; } -/* Parse the hostname from /etc/sysconfig/network. This must be - * called from the inspect_with_augeas wrapper. Note that F18+ and - * RHEL7+ use /etc/hostname just like Debian. - */ -static int -check_hostname_redhat (guestfs_h *g, struct inspect_fs *fs) -{ - char *hostname; - - /* Errors here are not fatal (RHBZ#726739), since it could be - * just missing HOSTNAME field in the file. - */ - guestfs_push_error_handler (g, NULL, NULL); - hostname = guestfs_aug_get (g, "/files/etc/sysconfig/network/HOSTNAME"); - guestfs_pop_error_handler (g); - - /* This is freed by guestfs_int_free_inspect_info. Note that hostname - * could be NULL because we ignored errors above. - */ - fs->hostname = hostname; - return 0; -} - /* Parse the hostname from /etc/rc.conf. On FreeBSD this file * contains comments, blank lines and: * hostname="freebsd8.example.com" @@ -1269,890 +500,4 @@ check_hostname_freebsd (guestfs_h *g, struct inspect_fs *fs) return 0; } -static int -check_fstab (guestfs_h *g, struct inspect_fs *fs) -{ - CLEANUP_FREE_STRING_LIST char **entries = NULL; - char **entry; - char augpath[256]; - CLEANUP_HASH_FREE Hash_table *md_map = NULL; - bool is_bsd = (fs->type == OS_TYPE_FREEBSD || - fs->type == OS_TYPE_NETBSD || - fs->type == OS_TYPE_OPENBSD); - - /* Generate a map of MD device paths listed in /etc/mdadm.conf to MD device - * paths in the guestfs appliance */ - if (map_md_devices (g, &md_map) == -1) return -1; - - entries = guestfs_aug_match (g, "/files/etc/fstab/*[label() != '#comment']"); - if (entries == NULL) - return -1; - - for (entry = entries; *entry != NULL; entry++) { - CLEANUP_FREE char *spec = NULL; - CLEANUP_FREE char *mp = NULL; - CLEANUP_FREE char *mountable = NULL; - CLEANUP_FREE char *vfstype = NULL; - - snprintf (augpath, sizeof augpath, "%s/spec", *entry); - spec = guestfs_aug_get (g, augpath); - if (spec == NULL) - return -1; - - /* Ignore /dev/fd (floppy disks) (RHBZ#642929) and CD-ROM drives. - * - * /dev/iso9660/FREEBSD_INSTALL can be found in FreeBSDs installation - * discs. - */ - if ((STRPREFIX (spec, "/dev/fd") && c_isdigit (spec[7])) || - (STRPREFIX (spec, "/dev/cd") && c_isdigit (spec[7])) || - STREQ (spec, "/dev/floppy") || - STREQ (spec, "/dev/cdrom") || - STRPREFIX (spec, "/dev/iso9660/")) - continue; - - snprintf (augpath, sizeof augpath, "%s/file", *entry); - mp = guestfs_aug_get (g, augpath); - if (mp == NULL) - return -1; - - /* Canonicalize the path, so "///usr//local//" -> "/usr/local" */ - canonical_mountpoint (mp); - - /* Ignore certain mountpoints. */ - if (STRPREFIX (mp, "/dev/") || - STREQ (mp, "/dev") || - STRPREFIX (mp, "/media/") || - STRPREFIX (mp, "/proc/") || - STREQ (mp, "/proc") || - STRPREFIX (mp, "/selinux/") || - STREQ (mp, "/selinux") || - STRPREFIX (mp, "/sys/") || - STREQ (mp, "/sys")) - continue; - - /* Resolve UUID= and LABEL= to the actual device. */ - if (STRPREFIX (spec, "UUID=")) { - CLEANUP_FREE char *s = guestfs_int_shell_unquote (&spec[5]); - if (s == NULL) { perrorf (g, "guestfs_int_shell_unquote"); return -1; } - mountable = guestfs_findfs_uuid (g, s); - } - else if (STRPREFIX (spec, "LABEL=")) { - CLEANUP_FREE char *s = guestfs_int_shell_unquote (&spec[6]); - if (s == NULL) { perrorf (g, "guestfs_int_shell_unquote"); return -1; } - mountable = guestfs_findfs_label (g, s); - } - /* Ignore "/.swap" (Pardus) and pseudo-devices like "tmpfs". */ - else if (STREQ (spec, "/dev/root") || (is_bsd && STREQ (mp, "/"))) - /* Resolve /dev/root to the current device. - * Do the same for the / partition of the *BSD systems, since the - * BSD -> Linux device translation is not straight forward. - */ - mountable = safe_strdup (g, fs->mountable); - else if (STRPREFIX (spec, "/dev/")) - /* Resolve guest block device names. */ - mountable = resolve_fstab_device (g, spec, md_map, fs->type); - else if (match (g, spec, re_openbsd_duid)) { - /* In OpenBSD's fstab you can specify partitions on a disk by appending a - * period and a partition letter to a Disklable Unique Identifier. The - * DUID is a 16 hex digit field found in the OpenBSD's altered BSD - * disklabel. For more info see here: - * http://www.openbsd.org/faq/faq14.html#intro - */ - char device[10]; /* /dev/sd[0-9][a-z] */ - char part = spec[17]; - - /* We cannot peep into disklables, we can only assume that this is the - * first disk. - */ - snprintf(device, 10, "%s%c", "/dev/sd0", part); - mountable = resolve_fstab_device (g, device, md_map, fs->type); - } - - /* If we haven't resolved the device successfully by this point, - * we don't care, just ignore it. - */ - if (mountable == NULL) - continue; - - snprintf (augpath, sizeof augpath, "%s/vfstype", *entry); - vfstype = guestfs_aug_get (g, augpath); - if (vfstype == NULL) return -1; - - if (STREQ (vfstype, "btrfs")) { - size_t i; - - snprintf (augpath, sizeof augpath, "%s/opt", *entry); - CLEANUP_FREE_STRING_LIST char **opts = guestfs_aug_match (g, augpath); - if (opts == NULL) return -1; - - for (i = 0; opts[i] != NULL; ++i) { - CLEANUP_FREE char *optname = NULL, *optvalue = NULL, *subvol = NULL; - char *old_mountable; - - optname = guestfs_aug_get (g, opts[i]); - if (optname == NULL) return -1; - - if (STREQ (optname, "subvol")) { - optvalue = safe_asprintf (g, "%s/value", opts[i]); - - subvol = guestfs_aug_get (g, optvalue); - if (subvol == NULL) return -1; - - old_mountable = mountable; - mountable = safe_asprintf (g, "btrfsvol:%s/%s", mountable, subvol); - free (old_mountable); - } - } - } - - add_fstab_entry (g, fs, mountable, mp); - } - - return 0; -} - -/* Add a filesystem and possibly a mountpoint entry for - * the root filesystem 'fs'. - * - * 'spec' is the fstab spec field, which might be a device name or a - * pseudodevice or 'UUID=...' or 'LABEL=...'. - * - * 'mp' is the mount point, which could also be 'swap' or 'none'. - */ -static void -add_fstab_entry (guestfs_h *g, struct inspect_fs *fs, - const char *mountable, const char *mountpoint) -{ - /* Add this to the fstab entry in 'fs'. - * Note these are further filtered by guestfs_inspect_get_mountpoints - * and guestfs_inspect_get_filesystems. - */ - const size_t n = fs->nr_fstab + 1; - struct inspect_fstab_entry *p; - - p = safe_realloc (g, fs->fstab, n * sizeof (struct inspect_fstab_entry)); - - fs->fstab = p; - fs->nr_fstab = n; - - /* These are owned by the handle and freed by guestfs_int_free_inspect_info. */ - fs->fstab[n-1].mountable = safe_strdup (g, mountable); - fs->fstab[n-1].mountpoint = safe_strdup (g, mountpoint); - - debug (g, "fstab: mountable=%s mountpoint=%s", mountable, mountpoint); -} - -/* Compute a uuid hash as a simple xor of of its 4 32bit components */ -static size_t -uuid_hash(const void *x, size_t table_size) -{ - const md_uuid *a = x; - size_t h, i; - - h = a->uuid[0]; - for (i = 1; i < 4; i++) { - h ^= a->uuid[i]; - } - - return h % table_size; -} - -static bool -uuid_cmp(const void *x, const void *y) -{ - const md_uuid *a = x; - const md_uuid *b = y; - size_t i; - - for (i = 0; i < 1; i++) { - if (a->uuid[i] != b->uuid[i]) return 0; - } - - return 1; -} - -static void -md_uuid_free(void *x) -{ - md_uuid *a = x; - free(a->path); - free(a); -} - -/* Taken from parse_uuid in mdadm */ -static int -parse_uuid (const char *str, uint32_t *uuid) -{ - size_t hit = 0; /* number of Hex digIT */ - char c; - size_t i; - int n; - - for (i = 0; i < 4; i++) - uuid[i] = 0; - - while ((c = *str++)) { - if (c >= '0' && c <= '9') - n = c - '0'; - else if (c >= 'a' && c <= 'f') - n = 10 + c - 'a'; - else if (c >= 'A' && c <= 'F') - n = 10 + c - 'A'; - else if (strchr (":. -", c)) - continue; - else - return -1; - - if (hit < 32) { - uuid[hit / 8] <<= 4; - uuid[hit / 8] += n; - } - hit++; - } - if (hit == 32) return 0; - - return -1; -} - -/* Create a mapping of uuids to appliance md device names */ -static ssize_t -map_app_md_devices (guestfs_h *g, Hash_table **map) -{ - CLEANUP_FREE_STRING_LIST char **mds = NULL; - size_t n = 0; - char **md; - - /* A hash mapping uuids to md device names */ - *map = hash_initialize(16, NULL, uuid_hash, uuid_cmp, md_uuid_free); - if (*map == NULL) g->abort_cb(); - - mds = guestfs_list_md_devices(g); - if (mds == NULL) goto error; - - for (md = mds; *md != NULL; md++) { - char **i; - CLEANUP_FREE_STRING_LIST char **detail = guestfs_md_detail (g, *md); - if (detail == NULL) goto error; - - /* Iterate over keys until we find uuid */ - for (i = detail; *i != NULL; i += 2) { - if (STREQ(*i, "uuid")) break; - } - - /* We found it */ - if (*i) { - md_uuid *entry; - - /* Next item is the uuid value */ - i++; - - entry = safe_malloc(g, sizeof(md_uuid)); - entry->path = safe_strdup(g, *md); - - if (parse_uuid(*i, entry->uuid) == -1) { - /* Invalid UUID is weird, but not fatal. */ - debug(g, "inspect-os: guestfs_md_detail returned invalid " - "uuid for %s: %s", *md, *i); - md_uuid_free(entry); - continue; - } - - const void *matched = NULL; - switch (hash_insert_if_absent(*map, entry, &matched)) { - case -1: - g->abort_cb(); - - case 0: - /* Duplicate uuid in for md device is weird, but not fatal. */ - debug(g, "inspect-os: md devices %s and %s have the same uuid", - ((md_uuid *)matched)->path, entry->path); - md_uuid_free(entry); - break; - - default: - n++; - } - } - } - - return n; - - error: - hash_free (*map); *map = NULL; - - return -1; -} - -static size_t -mdadm_app_hash(const void *x, size_t table_size) -{ - const mdadm_app *a = x; - return hash_pjw(a->mdadm, table_size); -} - -static bool -mdadm_app_cmp(const void *x, const void *y) -{ - const mdadm_app *a = x; - const mdadm_app *b = y; - - return STREQ (a->mdadm, b->mdadm); -} - -static void -mdadm_app_free(void *x) -{ - mdadm_app *a = x; - free(a->mdadm); - free(a->app); - free(a); -} - -/* Get a map of md device names in mdadm.conf to their device names in the - * appliance */ -static int -map_md_devices(guestfs_h *g, Hash_table **map) -{ - CLEANUP_HASH_FREE Hash_table *app_map = NULL; - CLEANUP_FREE_STRING_LIST char **matches = NULL; - ssize_t n_app_md_devices; - - *map = NULL; - - /* Get a map of md device uuids to their device names in the appliance */ - n_app_md_devices = map_app_md_devices (g, &app_map); - if (n_app_md_devices == -1) goto error; - - /* Nothing to do if there are no md devices */ - if (n_app_md_devices == 0) - return 0; - - /* Get all arrays listed in mdadm.conf */ - matches = guestfs_aug_match(g, "/files/etc/mdadm.conf/array"); - if (!matches) goto error; - - /* Log a debug message if we've got md devices, but nothing in mdadm.conf */ - if (matches[0] == NULL) { - debug(g, "Appliance has MD devices, but augeas returned no array matches " - "in mdadm.conf"); - return 0; - } - - *map = hash_initialize(16, NULL, mdadm_app_hash, mdadm_app_cmp, - mdadm_app_free); - if (!*map) g->abort_cb(); - - for (char **m = matches; *m != NULL; m++) { - /* Get device name and uuid for each array */ - CLEANUP_FREE char *dev_path = safe_asprintf (g, "%s/devicename", *m); - char *dev = guestfs_aug_get (g, dev_path); - if (!dev) goto error; - - CLEANUP_FREE char *uuid_path = safe_asprintf (g, "%s/uuid", *m); - CLEANUP_FREE char *uuid = guestfs_aug_get (g, uuid_path); - if (!uuid) { - free (dev); - continue; - } - - /* Parse the uuid into an md_uuid structure so we can look it up in the - * uuid->appliance device map */ - md_uuid mdadm; - mdadm.path = dev; - if (parse_uuid(uuid, mdadm.uuid) == -1) { - /* Invalid uuid. Weird, but not fatal. */ - debug(g, "inspect-os: mdadm.conf contains invalid uuid for %s: %s", - dev, uuid); - free (dev); - continue; - } - - /* If there's a corresponding uuid in the appliance, create a new - * entry in the transitive map */ - md_uuid *app = hash_lookup(app_map, &mdadm); - if (app) { - mdadm_app *entry = safe_malloc(g, sizeof(mdadm_app)); - entry->mdadm = dev; - entry->app = safe_strdup(g, app->path); - - switch (hash_insert_if_absent(*map, entry, NULL)) { - case -1: - g->abort_cb(); - - case 0: - /* Duplicate uuid in for md device is weird, but not fatal. */ - debug(g, "inspect-os: mdadm.conf contains multiple entries for %s", - app->path); - mdadm_app_free(entry); - continue; - } - } else - free (dev); - } - - return 0; - - error: - if (*map) hash_free (*map); - - return -1; -} - -static int -resolve_fstab_device_xdev (guestfs_h *g, const char *type, const char *disk, - const char *part, char **device_ret) -{ - CLEANUP_FREE char *name = NULL; - char *device; - CLEANUP_FREE_STRING_LIST char **devices = NULL; - size_t i, count; - struct drive *drv; - const char *p; - - /* type: (h|s|v|xv) - * disk: ([a-z]+) - * part: (\d*) - */ - - devices = guestfs_list_devices (g); - if (devices == NULL) - return -1; - - /* Check any hints we were passed for a non-heuristic mapping */ - name = safe_asprintf (g, "%sd%s", type, disk); - ITER_DRIVES (g, i, drv) { - if (drv->name && STREQ (drv->name, name)) { - device = safe_asprintf (g, "%s%s", devices[i], part); - if (!guestfs_int_is_partition (g, device)) { - free (device); - return 0; - } - *device_ret = device; - break; - } - } - - /* Guess the appliance device name if we didn't find a matching hint */ - if (!*device_ret) { - /* Count how many disks the libguestfs appliance has */ - for (count = 0; devices[count] != NULL; count++) - ; - - /* Calculate the numerical index of the disk */ - i = disk[0] - 'a'; - for (p = disk + 1; *p != '\0'; p++) { - i += 1; i *= 26; - i += *p - 'a'; - } - - /* Check the index makes sense wrt the number of disks the appliance has. - * If it does, map it to an appliance disk. - */ - if (i < count) { - device = safe_asprintf (g, "%s%s", devices[i], part); - if (!guestfs_int_is_partition (g, device)) { - free (device); - return 0; - } - *device_ret = device; - } - } - - return 0; -} - -static int -resolve_fstab_device_cciss (guestfs_h *g, const char *disk, const char *part, - char **device_ret) -{ - char *device; - CLEANUP_FREE_STRING_LIST char **devices = NULL; - size_t i; - struct drive *drv; - - /* disk: (cciss/c\d+d\d+) - * part: (\d+)? - */ - - devices = guestfs_list_devices (g); - if (devices == NULL) - return -1; - - /* Check any hints we were passed for a non-heuristic mapping */ - ITER_DRIVES (g, i, drv) { - if (drv->name && STREQ (drv->name, disk)) { - if (part) { - device = safe_asprintf (g, "%s%s", devices[i], part); - if (!guestfs_int_is_partition (g, device)) { - free (device); - return 0; - } - *device_ret = device; - } - else - *device_ret = safe_strdup (g, devices[i]); - break; - } - } - - /* We don't try to guess mappings for cciss devices */ - return 0; -} - -static int -resolve_fstab_device_diskbyid (guestfs_h *g, const char *part, - char **device_ret) -{ - int nr_devices; - char *device; - - /* For /dev/disk/by-id there is a limit to what we can do because - * original SCSI ID information has likely been lost. This - * heuristic will only work for guests that have a single block - * device. - * - * So the main task here is to make sure the assumptions above are - * true. - * - * XXX Use hints from virt-p2v if available. - * See also: https://bugzilla.redhat.com/show_bug.cgi?id=836573#c3 - */ - - nr_devices = guestfs_nr_devices (g); - if (nr_devices == -1) - return -1; - - /* If #devices isn't 1, give up trying to translate this fstab entry. */ - if (nr_devices != 1) - return 0; - - /* Make the partition name and check it exists. */ - device = safe_asprintf (g, "/dev/sda%s", part); - if (!guestfs_int_is_partition (g, device)) { - free (device); - return 0; - } - - *device_ret = device; - return 0; -} - -/* Resolve block device name to the libguestfs device name, eg. - * /dev/xvdb1 => /dev/vdb1; and /dev/mapper/VG-LV => /dev/VG/LV. This - * assumes that disks were added in the same order as they appear to - * the real VM, which is a reasonable assumption to make. Return - * anything we don't recognize unchanged. - */ -static char * -resolve_fstab_device (guestfs_h *g, const char *spec, Hash_table *md_map, - enum inspect_os_type os_type) -{ - char *device = NULL; - char *type, *slice, *disk, *part; - int r; - - if (STRPREFIX (spec, "/dev/mapper/")) { - /* LVM2 does some strange munging on /dev/mapper paths for VGs and - * LVs which contain '-' character: - * - * ><fs> lvcreate LV--test VG--test 32 - * ><fs> debug ls /dev/mapper - * VG----test-LV----test - * - * This makes it impossible to reverse those paths directly, so - * we have implemented lvm_canonical_lv_name in the daemon. - */ - guestfs_push_error_handler (g, NULL, NULL); - device = guestfs_lvm_canonical_lv_name (g, spec); - guestfs_pop_error_handler (g); - if (device == NULL) { - if (guestfs_last_errno (g) == ENOENT) { - /* Ignore devices that don't exist. (RHBZ#811872) */ - } else { - guestfs_int_error_errno (g, guestfs_last_errno (g), "%s", guestfs_last_error (g)); - return NULL; - } - } - } - else if (match3 (g, spec, re_xdev, &type, &disk, &part)) { - r = resolve_fstab_device_xdev (g, type, disk, part, &device); - free (type); - free (disk); - free (part); - if (r == -1) - return NULL; - } - else if (match2 (g, spec, re_cciss, &disk, &part)) { - r = resolve_fstab_device_cciss (g, disk, part, &device); - free (disk); - free (part); - if (r == -1) - return NULL; - } - else if (md_map && (disk = match1 (g, spec, re_mdN)) != NULL) { - mdadm_app entry; - entry.mdadm = disk; - - mdadm_app *app = hash_lookup (md_map, &entry); - if (app) device = safe_strdup (g, app->app); - - free (disk); - } - else if (match3 (g, spec, re_freebsd_gpt, &type, &disk, &part)) { - /* If the FreeBSD disk contains GPT partitions, the translation to Linux - * device names is straight forward. Partitions on a virtio disk are - * prefixed with vtbd. IDE hard drives used to be prefixed with ad and now - * are with ada. - */ - const int disk_i = guestfs_int_parse_unsigned_int (g, disk); - const int part_i = guestfs_int_parse_unsigned_int (g, part); - free (type); - free (disk); - free (part); - - if (disk_i != -1 && disk_i <= 26 && part_i > 0 && part_i <= 128) - device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a', part_i); - } - else if (match4 (g, spec, re_freebsd_mbr, &type, &disk, &slice, &part)) { - /* FreeBSD disks are organized quite differently. See: - * http://www.freebsd.org/doc/handbook/disk-organization.html - * FreeBSD "partitions" are exposed as quasi-extended partitions - * numbered from 5 in Linux. I have no idea what happens when you - * have multiple "slices" (the FreeBSD term for MBR partitions). - */ - const int disk_i = guestfs_int_parse_unsigned_int (g, disk); - const int slice_i = guestfs_int_parse_unsigned_int (g, slice); - int part_i = part[0] - 'a' /* counting from 0 */; - free (type); - free (disk); - free (slice); - free (part); - - if (part_i > 2) - /* Partition 'c' has the size of the enclosing slice. Not mapped under Linux. */ - part_i -= 1; - - if (disk_i != -1 && disk_i <= 26 && - slice_i > 0 && slice_i <= 1 /* > 4 .. see comment above */ && - part_i >= 0 && part_i < 25) { - device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a', part_i + 5); - } - } - else if ((os_type == OS_TYPE_NETBSD) && - match3 (g, spec, re_netbsd_dev, &type, &disk, &part)) { - const int disk_i = guestfs_int_parse_unsigned_int (g, disk); - int part_i = part[0] - 'a'; /* counting from 0 */ - free (type); - free (disk); - free (part); - - if (part_i > 3) - /* Partition 'c' is the disklabel partition and 'd' the hard disk itself. - * Not mapped under Linux. - */ - part_i -= 2; - - if (disk_i != -1 && part_i >= 0 && part_i < 24) - device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a', part_i + 5); - } - else if ((os_type == OS_TYPE_OPENBSD) && - match3 (g, spec, re_openbsd_dev, &type, &disk, &part)) { - const int disk_i = guestfs_int_parse_unsigned_int (g, disk); - int part_i = part[0] - 'a'; /* counting from 0 */ - free (type); - free (disk); - free (part); - - if (part_i > 2) - /* Partition 'c' is the hard disk itself. Not mapped under Linux */ - part_i -= 1; - - /* In OpenBSD MAXPARTITIONS is defined to 16 for all architectures */ - if (disk_i != -1 && part_i >= 0 && part_i < 15) - device = safe_asprintf (g, "/dev/sd%c%d", disk_i + 'a', part_i + 5); - } - else if ((part = match1 (g, spec, re_diskbyid)) != NULL) { - r = resolve_fstab_device_diskbyid (g, part, &device); - free (part); - if (r == -1) - return NULL; - } - else if (match3 (g, spec, re_hurd_dev, &type, &disk, &part)) { - /* Hurd disk devices are like /dev/hdNsM, where hdN is the - * N-th disk and M is the M-th partition on that disk. - * Turn the disk number into a letter-based identifier, so - * we can resolve it easily. - */ - const int disk_i = guestfs_int_parse_unsigned_int (g, disk); - const char disk_as_letter[2] = { disk_i + 'a', 0 }; - r = resolve_fstab_device_xdev (g, type, disk_as_letter, part, &device); - free (type); - free (disk); - free (part); - if (r == -1) - return NULL; - } - - /* Didn't match device pattern, return original spec unchanged. */ - if (device == NULL) - device = safe_strdup (g, spec); - - return device; -} - -static char *make_augeas_path_expression (guestfs_h *g, const char **configfiles); - -/* Call 'f' with Augeas opened and having parsed 'configfiles' (these - * files must exist). As a security measure, this bails if any file - * is too large for a reasonable configuration file. After the call - * to 'f' the Augeas handle is closed. - */ -static int -inspect_with_augeas (guestfs_h *g, struct inspect_fs *fs, - const char **configfiles, - int (*f) (guestfs_h *, struct inspect_fs *)) -{ - size_t i; - int64_t size; - int r; - CLEANUP_FREE char *pathexpr = NULL; - CLEANUP_FREE_STRING_LIST char **matches = NULL; - char **match; - - /* Security: Refuse to do this if a config file is too large. */ - for (i = 0; configfiles[i] != NULL; ++i) { - if (guestfs_is_file_opts (g, configfiles[i], - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) == 0) - continue; - - size = guestfs_filesize (g, configfiles[i]); - if (size == -1) - /* guestfs_filesize failed and has already set error in handle */ - return -1; - if (size > MAX_AUGEAS_FILE_SIZE) { - error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), - configfiles[i], size); - return -1; - } - } - - if (guestfs_aug_init (g, "/", 16|32 /* AUG_SAVE_NOOP|AUG_NO_LOAD */) == -1) - return -1; - - r = -1; - - /* Tell Augeas to only load configfiles and no other files. This - * prevents a rogue guest from performing a denial of service attack - * by having large, over-complicated configuration files which are - * unrelated to the task at hand. (Thanks Dominic Cleal). - * Note this requires Augeas >= 1.0.0 because of RHBZ#975412. - */ - pathexpr = make_augeas_path_expression (g, configfiles); - if (guestfs_aug_rm (g, pathexpr) == -1) - goto out; - - if (guestfs_aug_load (g) == -1) - goto out; - - /* Check that augeas did not get a parse error for any of the configfiles, - * otherwise we are silently missing information. - */ - matches = guestfs_aug_match (g, "/augeas/files//error"); - for (match = matches; *match != NULL; ++match) { - for (i = 0; configfiles[i] != NULL; ++i) { - CLEANUP_FREE char *errorpath - safe_asprintf (g, "/augeas/files%s/error", configfiles[i]); - - if (STREQ (*match, errorpath)) { - /* Get the various error details. */ - guestfs_push_error_handler (g, NULL, NULL); - CLEANUP_FREE char *messagepath - safe_asprintf (g, "%s/message", errorpath); - CLEANUP_FREE char *message = guestfs_aug_get (g, messagepath); - CLEANUP_FREE char *linepath - safe_asprintf (g, "%s/line", errorpath); - CLEANUP_FREE char *line = guestfs_aug_get (g, linepath); - CLEANUP_FREE char *charpath - safe_asprintf (g, "%s/char", errorpath); - CLEANUP_FREE char *charp = guestfs_aug_get (g, charpath); - guestfs_pop_error_handler (g); - - error (g, _("%s:%s:%s: augeas parse failure: %s"), - configfiles[i], - line ? : "<none>", - charp ? : "<none>", - message ? : "<none>"); - goto out; - } - } - } - - r = f (g, fs); - - out: - guestfs_aug_close (g); - - return r; -} - -/* Explained here: https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 */ -static char * -make_augeas_path_expression (guestfs_h *g, const char **configfiles) -{ - size_t i; - size_t nr_files; - CLEANUP_FREE_STRING_LIST char **subexprs = NULL; - CLEANUP_FREE char *subexpr = NULL; - char *ret; - - nr_files = guestfs_int_count_strings ((char **) configfiles); - subexprs = safe_malloc (g, sizeof (char *) * (nr_files + 1)); - - for (i = 0; i < nr_files; ++i) { - subexprs[i] = /* v NB trailing '/' after filename */ - safe_asprintf (g, "\"%s/\" !~ regexp('^') + glob(incl) + regexp('/.*')", - configfiles[i]); - } - subexprs[nr_files] = NULL; - - subexpr = guestfs_int_join_strings (" and ", subexprs); - if (subexpr == NULL) - g->abort_cb (); - - ret = safe_asprintf (g, "/augeas/load/*[ %s ]", subexpr); - debug (g, "augeas pathexpr = %s", ret); - return ret; -} - -/* Canonicalize the path, so "///usr//local//" -> "/usr/local" - * - * The path is modified in place because the result is always - * the same length or shorter than the argument passed. - */ -static void -canonical_mountpoint (char *s) -{ - size_t len = strlen (s); - char *orig = s; - - s = strchr (s, '/'); - while (s != NULL && *s != 0) { - char *pos = s + 1; - char *p = pos; - /* Find how many consecutive slashes are there after the one found, - * and shift the characters after them accordingly. */ - while (*p == '/') - ++p; - if (p - pos > 0) { - memmove (pos, p, len - (p - orig) + 1); - len -= p - pos; - } - - s = strchr (pos, '/'); - } - /* Ignore the trailing slash, but avoid removing it for "/". */ - if (len > 1 && orig[len-1] == '/') - --len; - orig[len] = 0; -} +#endif diff --git a/lib/inspect-fs-windows.c b/lib/inspect-fs-windows.c deleted file mode 100644 index 34f33c908..000000000 --- a/lib/inspect-fs-windows.c +++ /dev/null @@ -1,739 +0,0 @@ -/* libguestfs - * Copyright (C) 2010-2012 Red Hat Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; 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 <stdbool.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <iconv.h> -#include <inttypes.h> - -#ifdef HAVE_ENDIAN_H -#include <endian.h> -#endif -#ifdef HAVE_SYS_ENDIAN_H -#include <sys/endian.h> -#endif - -#if defined __APPLE__ && defined __MACH__ -#include <libkern/OSByteOrder.h> -#define le32toh(x) OSSwapLittleToHostInt32(x) -#define le64toh(x) OSSwapLittleToHostInt64(x) -#endif - -#include <pcre.h> - -#include "c-ctype.h" -#include "ignore-value.h" - -#include "guestfs.h" -#include "guestfs-internal.h" -#include "guestfs-internal-actions.h" -#include "structs-cleanups.h" - -COMPILE_REGEXP (re_windows_version, "^(\\d+)\\.(\\d+)", 0) -COMPILE_REGEXP (re_boot_ini_os_header, "^\\[operating systems\\]\\s*$", 0) -COMPILE_REGEXP (re_boot_ini_os, - "^(multi|scsi)\\((\\d+)\\)disk\\((\\d+)\\)rdisk\\((\\d+)\\)partition\\((\\d+)\\)([^=]+)=", 0) - -static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs); -static int check_windows_registry_paths (guestfs_h *g, struct inspect_fs *fs); -static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs); -static int check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs); -static char *map_registry_disk_blob (guestfs_h *g, const void *blob); -static char *map_registry_disk_blob_gpt (guestfs_h *g, const void *blob); -static char *extract_guid_from_registry_blob (guestfs_h *g, const void *blob); - -/* XXX Handling of boot.ini in the Perl version was pretty broken. It - * essentially didn't do anything for modern Windows guests. - * Therefore I've omitted all that code. - */ - -/* Try to find Windows systemroot using some common locations. - * - * Notes: - * - * (1) We check for some directories inside to see if it is a real - * systemroot, and not just a directory that happens to have the same - * name. - * - * (2) If a Windows guest has multiple disks and applications are - * installed on those other disks, then those other disks will contain - * "/Program Files" and "/System Volume Information". Those would - * *not* be Windows root disks. (RHBZ#674130) - */ - -static int -is_systemroot (guestfs_h *const g, const char *systemroot) -{ - CLEANUP_FREE char *path1 = NULL, *path2 = NULL, *path3 = NULL; - - path1 = safe_asprintf (g, "%s/system32", systemroot); - if (!guestfs_int_is_dir_nocase (g, path1)) - return 0; - - path2 = safe_asprintf (g, "%s/system32/config", systemroot); - if (!guestfs_int_is_dir_nocase (g, path2)) - return 0; - - path3 = safe_asprintf (g, "%s/system32/cmd.exe", systemroot); - if (!guestfs_int_is_file_nocase (g, path3)) - return 0; - - return 1; -} - -char * -guestfs_int_get_windows_systemroot (guestfs_h *g) -{ - /* Check a predefined list of common windows system root locations */ - static const char *systemroots[] - { "/windows", "/winnt", "/win32", "/win", "/reactos", NULL }; - - for (size_t i = 0; i < sizeof systemroots / sizeof systemroots[0]; ++i) { - char *systemroot - guestfs_int_case_sensitive_path_silently (g, systemroots[i]); - if (!systemroot) - continue; - - if (is_systemroot (g, systemroot)) { - debug (g, "windows %%SYSTEMROOT%% = %s", systemroot); - - return systemroot; - } else { - free (systemroot); - } - } - - /* If the fs contains boot.ini, check it for non-standard - * systemroot locations */ - CLEANUP_FREE char *boot_ini_path - guestfs_int_case_sensitive_path_silently (g, "/boot.ini"); - if (boot_ini_path && guestfs_is_file (g, boot_ini_path) > 0) { - CLEANUP_FREE_STRING_LIST char **boot_ini - guestfs_read_lines (g, boot_ini_path); - if (!boot_ini) { - debug (g, "error reading %s", boot_ini_path); - return NULL; - } - - int found_os = 0; - for (char **i = boot_ini; *i != NULL; i++) { - CLEANUP_FREE char *controller_type = NULL; - CLEANUP_FREE char *controller = NULL; - CLEANUP_FREE char *disk = NULL; - CLEANUP_FREE char *rdisk = NULL; - CLEANUP_FREE char *partition = NULL; - CLEANUP_FREE char *path = NULL; - - char *line = *i; - - if (!found_os) { - if (match (g, line, re_boot_ini_os_header)) { - found_os = 1; - continue; - } - } - - /* See http://support.microsoft.com/kb/102873 for a discussion - * of what this line means */ - if (match6 (g, line, re_boot_ini_os, &controller_type, - &controller, &disk, &rdisk, &partition, &path)) - { - /* The Windows system root may be on any disk. However, there - * are currently (at least) 2 practical problems preventing us - * from locating it on another disk: - * - * 1. We don't have enough metadata about the disks we were - * given to know if what controller they were on and what - * index they had. - * - * 2. The way inspection of filesystems currently works, we - * can't mark another filesystem, which we may have already - * inspected, to be inspected for a specific Windows system - * root. - * - * Solving 1 properly would require a new API at a minimum. We - * might be able to fudge something practical without this, - * though, e.g. by looking at the <partition>th partition of - * every disk for the specific windows root. - * - * Solving 2 would probably require a significant refactoring - * of the way filesystems are inspected. We should probably do - * this some time. - * - * For the moment, we ignore all partition information and - * assume the system root is on the current partition. In - * practice, this will normally be correct. - */ - - /* Swap backslashes for forward slashes in the system root - * path */ - for (char *j = path; *j != '\0'; j++) { - if (*j == '\\') *j = '/'; - } - - char *systemroot = guestfs_int_case_sensitive_path_silently (g, path); - if (systemroot && is_systemroot (g, systemroot)) { - debug (g, "windows %%SYSTEMROOT%% = %s", systemroot); - - return systemroot; - } else { - free (systemroot); - } - } - } - } - - return NULL; /* not found */ -} - -int -guestfs_int_check_windows_root (guestfs_h *g, struct inspect_fs *fs, - char *const systemroot) -{ - fs->type = OS_TYPE_WINDOWS; - fs->distro = OS_DISTRO_WINDOWS; - - /* Freed by guestfs_int_free_inspect_info. */ - fs->windows_systemroot = systemroot; - - if (check_windows_arch (g, fs) == -1) - return -1; - - /* Get system and software registry paths. */ - if (check_windows_registry_paths (g, fs) == -1) - return -1; - - /* Product name and version. */ - if (check_windows_software_registry (g, fs) == -1) - return -1; - - /* Hostname. */ - if (check_windows_system_registry (g, fs) == -1) - return -1; - - return 0; -} - -static int -check_windows_arch (guestfs_h *g, struct inspect_fs *fs) -{ - CLEANUP_FREE char *cmd_exe - safe_asprintf (g, "%s/system32/cmd.exe", fs->windows_systemroot); - - /* Should exist because of previous check above in get_windows_systemroot. */ - CLEANUP_FREE char *cmd_exe_path = guestfs_case_sensitive_path (g, cmd_exe); - if (!cmd_exe_path) - return -1; - - char *arch = guestfs_file_architecture (g, cmd_exe_path); - if (!arch) - return -1; - - fs->arch = arch; /* freed by guestfs_int_free_inspect_info */ - - return 0; -} - -static int -check_windows_registry_paths (guestfs_h *g, struct inspect_fs *fs) -{ - int r; - CLEANUP_FREE char *software = NULL, *system = NULL; - - if (!fs->windows_systemroot) - return 0; - - software = safe_asprintf (g, "%s/system32/config/software", - fs->windows_systemroot); - - fs->windows_software_hive = guestfs_case_sensitive_path (g, software); - if (!fs->windows_software_hive) - return -1; - - r = guestfs_is_file (g, fs->windows_software_hive); - if (r == -1) { - free (fs->windows_software_hive); - fs->windows_software_hive = NULL; - return -1; - } - - if (r == 0) { /* doesn't exist, or not a file */ - free (fs->windows_software_hive); - fs->windows_software_hive = NULL; - /*FALLTHROUGH*/ - } - - system = safe_asprintf (g, "%s/system32/config/system", - fs->windows_systemroot); - - fs->windows_system_hive = guestfs_case_sensitive_path (g, system); - if (!fs->windows_system_hive) - return -1; - - r = guestfs_is_file (g, fs->windows_system_hive); - if (r == -1) { - free (fs->windows_system_hive); - fs->windows_system_hive = NULL; - return -1; - } - - if (r == 0) { /* doesn't exist, or not a file */ - free (fs->windows_system_hive); - fs->windows_system_hive = NULL; - /*FALLTHROUGH*/ - } - - return 0; -} - -/* At the moment, pull just the ProductName and version numbers from - * the registry. In future there is a case for making many more - * registry fields available to callers. - */ -static int -check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs) -{ - int ret = -1; - int64_t node; - const char *hivepath[] - { "Microsoft", "Windows NT", "CurrentVersion" }; - size_t i; - CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values = NULL; - bool ignore_currentversion = false; - - /* If the software hive doesn't exist, just accept that we cannot - * find product_name etc. - */ - if (!fs->windows_software_hive) - return 0; - - if (guestfs_hivex_open (g, fs->windows_software_hive, - GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, - GUESTFS_HIVEX_OPEN_UNSAFE, 1, - -1) == -1) - return -1; - - node = guestfs_hivex_root (g); - for (i = 0; node > 0 && i < sizeof hivepath / sizeof hivepath[0]; ++i) - node = guestfs_hivex_node_get_child (g, node, hivepath[i]); - - if (node == -1) - goto out; - - if (node == 0) { - perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); - goto out; - } - - values = guestfs_hivex_node_values (g, node); - - for (i = 0; i < values->len; ++i) { - const int64_t value = values->val[i].hivex_value_h; - CLEANUP_FREE char *key = guestfs_hivex_value_key (g, value); - if (key == NULL) - goto out; - - if (STRCASEEQ (key, "ProductName")) { - fs->product_name = guestfs_hivex_value_utf8 (g, value); - if (!fs->product_name) - goto out; - } - else if (STRCASEEQ (key, "CurrentMajorVersionNumber")) { - size_t vsize; - const int64_t vtype = guestfs_hivex_value_type (g, value); - CLEANUP_FREE char *vbuf = guestfs_hivex_value_value (g, value, &vsize); - - if (vbuf == NULL) - goto out; - if (vtype != 4 || vsize != 4) { - error (g, "hivex: expected CurrentVersion\\%s to be a DWORD field", - "CurrentMajorVersionNumber"); - goto out; - } - - fs->version.v_major = le32toh (*(int32_t *)vbuf); - - /* Ignore CurrentVersion if we see it after this key. */ - ignore_currentversion = true; - } - else if (STRCASEEQ (key, "CurrentMinorVersionNumber")) { - size_t vsize; - const int64_t vtype = guestfs_hivex_value_type (g, value); - CLEANUP_FREE char *vbuf = guestfs_hivex_value_value (g, value, &vsize); - - if (vbuf == NULL) - goto out; - if (vtype != 4 || vsize != 4) { - error (g, "hivex: expected CurrentVersion\\%s to be a DWORD field", - "CurrentMinorVersionNumber"); - goto out; - } - - fs->version.v_minor = le32toh (*(int32_t *)vbuf); - - /* Ignore CurrentVersion if we see it after this key. */ - ignore_currentversion = true; - } - else if (!ignore_currentversion && STRCASEEQ (key, "CurrentVersion")) { - CLEANUP_FREE char *version = guestfs_hivex_value_utf8 (g, value); - if (!version) - goto out; - if (guestfs_int_version_from_x_y_re (g, &fs->version, version, - re_windows_version) == -1) - goto out; - } - else if (STRCASEEQ (key, "InstallationType")) { - fs->product_variant = guestfs_hivex_value_utf8 (g, value); - if (!fs->product_variant) - goto out; - } - } - - ret = 0; - - out: - guestfs_hivex_close (g); - - return ret; -} - -static int -check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) -{ - static const char gpt_prefix[] = "DMIO:ID:"; - int ret = -1; - int64_t root, node, value; - CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values = NULL; - CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values2 = NULL; - int32_t dword; - size_t i, count; - CLEANUP_FREE void *buf = NULL; - size_t buflen; - const char *hivepath[] - { NULL /* current control set */, "Services", "Tcpip", "Parameters" }; - - /* If the system hive doesn't exist, just accept that we cannot - * find hostname etc. - */ - if (!fs->windows_system_hive) - return 0; - - if (guestfs_hivex_open (g, fs->windows_system_hive, - GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, - GUESTFS_HIVEX_OPEN_UNSAFE, 1, - -1) == -1) - goto out; - - root = guestfs_hivex_root (g); - if (root == 0) - goto out; - - /* Get the CurrentControlSet. */ - node = guestfs_hivex_node_get_child (g, root, "Select"); - if (node == -1) - goto out; - - if (node == 0) { - error (g, "hivex: could not locate HKLM\\SYSTEM\\Select"); - goto out; - } - - value = guestfs_hivex_node_get_value (g, node, "Current"); - if (value == -1) - goto out; - - if (value == 0) { - error (g, "hivex: HKLM\\System\\Select Default entry not found"); - goto out; - } - - /* XXX Should check the type. */ - buf = guestfs_hivex_value_value (g, value, &buflen); - if (buflen != 4) { - error (g, "hivex: HKLM\\System\\Select\\Current expected to be DWORD"); - goto out; - } - dword = le32toh (*(int32_t *)buf); - fs->windows_current_control_set = safe_asprintf (g, "ControlSet%03d", dword); - - /* Get the drive mappings. - * This page explains the contents of HKLM\System\MountedDevices: - * http://www.goodells.net/multiboot/partsigs.shtml - */ - node = guestfs_hivex_node_get_child (g, root, "MountedDevices"); - if (node == -1) - goto out; - - if (node == 0) - /* Not found: skip getting drive letter mappings (RHBZ#803664). */ - goto skip_drive_letter_mappings; - - values = guestfs_hivex_node_values (g, node); - - /* Count how many DOS drive letter mappings there are. This doesn't - * ignore removable devices, so it overestimates, but that doesn't - * matter because it just means we'll allocate a few bytes extra. - */ - for (i = count = 0; i < values->len; ++i) { - CLEANUP_FREE char *key - guestfs_hivex_value_key (g, values->val[i].hivex_value_h); - if (key == NULL) - goto out; - if (STRCASEEQLEN (key, "\\DosDevices\\", 12) && - c_isalpha (key[12]) && key[13] == ':') - count++; - } - - fs->drive_mappings = safe_calloc (g, 2*count + 1, sizeof (char *)); - - for (i = count = 0; i < values->len; ++i) { - const int64_t v = values->val[i].hivex_value_h; - CLEANUP_FREE char *key = guestfs_hivex_value_key (g, v); - if (key == NULL) - goto out; - if (STRCASEEQLEN (key, "\\DosDevices\\", 12) && - c_isalpha (key[12]) && key[13] == ':') { - /* Get the binary value. Is it a fixed disk? */ - CLEANUP_FREE char *blob = NULL; - char *device; - int64_t type; - bool is_gpt; - size_t len; - - type = guestfs_hivex_value_type (g, v); - blob = guestfs_hivex_value_value (g, v, &len); - is_gpt = memcmp (blob, gpt_prefix, 8) == 0; - if (blob != NULL && type == 3 && (len == 12 || is_gpt)) { - /* Try to map the blob to a known disk and partition. */ - if (is_gpt) - device = map_registry_disk_blob_gpt (g, blob); - else - device = map_registry_disk_blob (g, blob); - - if (device != NULL) { - fs->drive_mappings[count++] = safe_strndup (g, &key[12], 1); - fs->drive_mappings[count++] = device; - } - } - } - } - - skip_drive_letter_mappings:; - /* Get the hostname. */ - hivepath[0] = fs->windows_current_control_set; - for (node = root, i = 0; - node > 0 && i < sizeof hivepath / sizeof hivepath[0]; - ++i) { - node = guestfs_hivex_node_get_child (g, node, hivepath[i]); - } - - if (node == -1) - goto out; - - if (node == 0) { - perrorf (g, "hivex: cannot locate HKLM\\SYSTEM\\%s\\Services\\Tcpip\\Parameters", - fs->windows_current_control_set); - goto out; - } - - values2 = guestfs_hivex_node_values (g, node); - if (values2 == NULL) - goto out; - - for (i = 0; i < values2->len; ++i) { - const int64_t v = values2->val[i].hivex_value_h; - CLEANUP_FREE char *key = guestfs_hivex_value_key (g, v); - if (key == NULL) - goto out; - - if (STRCASEEQ (key, "Hostname")) { - fs->hostname = guestfs_hivex_value_utf8 (g, v); - if (!fs->hostname) - goto out; - } - /* many other interesting fields here ... */ - } - - ret = 0; - - out: - guestfs_hivex_close (g); - - return ret; -} - -/* Windows Registry HKLM\SYSTEM\MountedDevices uses a blob of data - * to store partitions. This blob is described here: - * http://www.goodells.net/multiboot/partsigs.shtml - * The following function maps this blob to a libguestfs partition - * name, if possible. - */ -static char * -map_registry_disk_blob (guestfs_h *g, const void *blob) -{ - CLEANUP_FREE_STRING_LIST char **devices = NULL; - CLEANUP_FREE_PARTITION_LIST struct guestfs_partition_list *partitions = NULL; - size_t i, j, len; - uint64_t part_offset; - - /* First 4 bytes are the disk ID. Search all devices to find the - * disk with this disk ID. - */ - devices = guestfs_list_devices (g); - if (devices == NULL) - return NULL; - - for (i = 0; devices[i] != NULL; ++i) { - /* Read the disk ID. */ - CLEANUP_FREE char *diskid - guestfs_pread_device (g, devices[i], 4, 0x01b8, &len); - if (diskid == NULL) - continue; - if (len < 4) - continue; - if (memcmp (diskid, blob, 4) == 0) /* found it */ - goto found_disk; - } - return NULL; - - found_disk: - /* Next 8 bytes are the offset of the partition in bytes(!) given as - * a 64 bit little endian number. Luckily it's easy to get the - * partition byte offset from guestfs_part_list. - */ - memcpy (&part_offset, (char *) blob + 4, sizeof (part_offset)); - part_offset = le64toh (part_offset); - - partitions = guestfs_part_list (g, devices[i]); - if (partitions == NULL) - return NULL; - - for (j = 0; j < partitions->len; ++j) { - if (partitions->val[j].part_start == part_offset) /* found it */ - goto found_partition; - } - return NULL; - - found_partition: - /* Construct the full device name. */ - return safe_asprintf (g, "%s%d", devices[i], partitions->val[j].part_num); -} - -/* Matches Windows registry HKLM\SYSYTEM\MountedDevices\DosDevices blob to - * to libguestfs GPT partition device. For GPT disks, the blob is made of - * "DMIO:ID:" prefix followed by the GPT partition GUID. - */ -static char * -map_registry_disk_blob_gpt (guestfs_h *g, const void *blob) -{ - CLEANUP_FREE_STRING_LIST char **parts = NULL; - CLEANUP_FREE char *blob_guid = extract_guid_from_registry_blob (g, blob); - size_t i; - - parts = guestfs_list_partitions (g); - if (parts == NULL) - return NULL; - - for (i = 0; parts[i] != NULL; ++i) { - CLEANUP_FREE char *fs_guid = NULL; - int partnum; - CLEANUP_FREE char *device = NULL; - CLEANUP_FREE char *type = NULL; - - partnum = guestfs_part_to_partnum (g, parts[i]); - if (partnum == -1) - continue; - - device = guestfs_part_to_dev (g, parts[i]); - if (device == NULL) - continue; - - type = guestfs_part_get_parttype (g, device); - if (type == NULL) - continue; - - if (STRCASENEQ (type, "gpt")) - continue; - - /* get the GPT parition GUID from the partition block device */ - fs_guid = guestfs_part_get_gpt_guid (g, device, partnum); - if (fs_guid == NULL) - continue; - - /* if both GUIDs match, we have found the mapping for our device */ - if (STRCASEEQ (fs_guid, blob_guid)) - return safe_strdup (g, parts[i]); - } - - return NULL; -} - -/* Extracts the binary GUID stored in blob from Windows registry - * HKLM\SYSTYEM\MountedDevices\DosDevices value and converts it to a - * GUID string so that it can be matched against libguestfs partition - * device GPT GUID. - */ -static char * -extract_guid_from_registry_blob (guestfs_h *g, const void *blob) -{ - char guid_bytes[16]; - uint32_t data1; - uint16_t data2, data3; - uint64_t data4; - - /* get the GUID bytes from blob (skip 8 byte "DMIO:ID:" prefix) */ - memcpy (&guid_bytes, (char *) blob + 8, sizeof (guid_bytes)); - - /* copy relevant sections from blob to respective ints */ - memcpy (&data1, guid_bytes, sizeof (data1)); - memcpy (&data2, guid_bytes + 4, sizeof (data2)); - memcpy (&data3, guid_bytes + 6, sizeof (data3)); - memcpy (&data4, guid_bytes + 8, sizeof (data4)); - - /* ensure proper endianness */ - data1 = le32toh (data1); - data2 = le16toh (data2); - data3 = le16toh (data3); - data4 = be64toh (data4); - - return safe_asprintf (g, - "%08" PRIX32 "-%04" PRIX16 "-%04" PRIX16 "-%04" PRIX64 "-%012" PRIX64, - data1, data2, data3, data4 >> 48, data4 & 0xffffffffffff); -} - -/* NB: This function DOES NOT test for the existence of the file. It - * will return non-NULL even if the file/directory does not exist. - * You have to call guestfs_is_file{,_opts} etc. - */ -char * -guestfs_int_case_sensitive_path_silently (guestfs_h *g, const char *path) -{ - char *ret; - - guestfs_push_error_handler (g, NULL, NULL); - ret = guestfs_case_sensitive_path (g, path); - guestfs_pop_error_handler (g); - - return ret; -} diff --git a/lib/inspect-fs.c b/lib/inspect-fs.c index 2da73d310..f93842ae2 100644 --- a/lib/inspect-fs.c +++ b/lib/inspect-fs.c @@ -37,379 +37,6 @@ #include "guestfs-internal.h" #include "structs-cleanups.h" -static int check_filesystem (guestfs_h *g, const char *mountable, - const struct guestfs_internal_mountable *m, - int whole_device); -static void extend_fses (guestfs_h *g); -static int get_partition_context (guestfs_h *g, const char *partition, int *partnum_ret, int *nr_partitions_ret); -static int is_symlink_to (guestfs_h *g, const char *file, const char *wanted_target); - -/* Find out if 'device' contains a filesystem. If it does, add - * another entry in g->fses. - */ -int -guestfs_int_check_for_filesystem_on (guestfs_h *g, const char *mountable) -{ - CLEANUP_FREE char *vfs_type = NULL; - int is_swap, r; - struct inspect_fs *fs; - CLEANUP_FREE_INTERNAL_MOUNTABLE struct guestfs_internal_mountable *m = NULL; - int whole_device = 0; - - /* Get vfs-type in order to check if it's a Linux(?) swap device. - * If there's an error we should ignore it, so to do that we have to - * temporarily replace the error handler with a null one. - */ - guestfs_push_error_handler (g, NULL, NULL); - vfs_type = guestfs_vfs_type (g, mountable); - guestfs_pop_error_handler (g); - - is_swap = vfs_type && STREQ (vfs_type, "swap"); - debug (g, "check_for_filesystem_on: %s (%s)", - mountable, vfs_type ? vfs_type : "failed to get vfs type"); - - if (is_swap) { - extend_fses (g); - fs = &g->fses[g->nr_fses-1]; - fs->mountable = safe_strdup (g, mountable); - return 0; - } - - m = guestfs_internal_parse_mountable (g, mountable); - if (m == NULL) - return -1; - - /* If it's a whole device, see if it is an install ISO. */ - if (m->im_type == MOUNTABLE_DEVICE) { - whole_device = guestfs_is_whole_device (g, m->im_device); - if (whole_device == -1) { - return -1; - } - } - - if (whole_device) { - extend_fses (g); - fs = &g->fses[g->nr_fses-1]; - - r = guestfs_int_check_installer_iso (g, fs, m->im_device); - if (r == -1) { /* Fatal error. */ - g->nr_fses--; - return -1; - } - if (r > 0) /* Found something. */ - return 0; - - /* Didn't find anything. Fall through ... */ - g->nr_fses--; - } - - /* Try mounting the device. As above, ignore errors. */ - guestfs_push_error_handler (g, NULL, NULL); - if (vfs_type && STREQ (vfs_type, "ufs")) { /* Hack for the *BSDs. */ - /* FreeBSD fs is a variant of ufs called ufs2 ... */ - r = guestfs_mount_vfs (g, "ro,ufstype=ufs2", "ufs", mountable, "/"); - if (r == -1) - /* while NetBSD and OpenBSD use another variant labeled 44bsd */ - r = guestfs_mount_vfs (g, "ro,ufstype=44bsd", "ufs", mountable, "/"); - } else { - r = guestfs_mount_ro (g, mountable, "/"); - } - guestfs_pop_error_handler (g); - if (r == -1) - return 0; - - /* Do the rest of the checks. */ - r = check_filesystem (g, mountable, m, whole_device); - - /* Unmount the filesystem. */ - if (guestfs_umount_all (g) == -1) - return -1; - - return r; -} - -static int -check_filesystem (guestfs_h *g, const char *mountable, - const struct guestfs_internal_mountable *m, - int whole_device) -{ - int partnum = -1, nr_partitions = -1; - /* Not CLEANUP_FREE, as it will be cleaned up with inspection info */ - char *windows_systemroot = NULL; - - extend_fses (g); - - if (!whole_device && m->im_type == MOUNTABLE_DEVICE && - guestfs_int_is_partition (g, m->im_device)) { - if (get_partition_context (g, m->im_device, - &partnum, &nr_partitions) == -1) - return -1; - } - - struct inspect_fs *fs = &g->fses[g->nr_fses-1]; - - fs->mountable = safe_strdup (g, mountable); - - /* Optimize some of the tests by avoiding multiple tests of the same thing. */ - const int is_dir_etc = guestfs_is_dir (g, "/etc") > 0; - const int is_dir_bin = guestfs_is_dir (g, "/bin") > 0; - const int is_dir_share = guestfs_is_dir (g, "/share") > 0; - - /* Grub /boot? */ - if (guestfs_is_file (g, "/grub/menu.lst") > 0 || - guestfs_is_file (g, "/grub/grub.conf") > 0 || - guestfs_is_file (g, "/grub2/grub.cfg") > 0) - ; - /* FreeBSD root? */ - else if (is_dir_etc && - is_dir_bin && - guestfs_is_file (g, "/etc/freebsd-update.conf") > 0 && - guestfs_is_file (g, "/etc/fstab") > 0) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; - if (guestfs_int_check_freebsd_root (g, fs) == -1) - return -1; - } - /* NetBSD root? */ - else if (is_dir_etc && - is_dir_bin && - guestfs_is_file (g, "/netbsd") > 0 && - guestfs_is_file (g, "/etc/fstab") > 0 && - guestfs_is_file (g, "/etc/release") > 0) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; - if (guestfs_int_check_netbsd_root (g, fs) == -1) - return -1; - } - /* OpenBSD root? */ - else if (is_dir_etc && - is_dir_bin && - guestfs_is_file (g, "/bsd") > 0 && - guestfs_is_file (g, "/etc/fstab") > 0 && - guestfs_is_file (g, "/etc/motd") > 0) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; - if (guestfs_int_check_openbsd_root (g, fs) == -1) - return -1; - } - /* Hurd root? */ - else if (guestfs_is_file (g, "/hurd/console") > 0 && - guestfs_is_file (g, "/hurd/hello") > 0 && - guestfs_is_file (g, "/hurd/null") > 0) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; /* XXX could be more specific */ - if (guestfs_int_check_hurd_root (g, fs) == -1) - return -1; - } - /* Minix root? */ - else if (is_dir_etc && - is_dir_bin && - guestfs_is_file (g, "/service/vm") > 0 && - guestfs_is_file (g, "/etc/fstab") > 0 && - guestfs_is_file (g, "/etc/version") > 0) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; - if (guestfs_int_check_minix_root (g, fs) == -1) - return -1; - } - /* Linux root? */ - else if (is_dir_etc && - (is_dir_bin || - is_symlink_to (g, "/bin", "usr/bin") > 0) && - (guestfs_is_file (g, "/etc/fstab") > 0 || - guestfs_is_file (g, "/etc/hosts") > 0)) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; - if (guestfs_int_check_linux_root (g, fs) == -1) - return -1; - } - /* CoreOS root? */ - else if (is_dir_etc && - guestfs_is_dir (g, "/root") > 0 && - guestfs_is_dir (g, "/home") > 0 && - guestfs_is_dir (g, "/usr") > 0 && - guestfs_is_file (g, "/etc/coreos/update.conf") > 0) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; - if (guestfs_int_check_coreos_root (g, fs) == -1) - return -1; - } - /* Linux /usr/local? */ - else if (is_dir_etc && - is_dir_bin && - is_dir_share && - guestfs_is_dir (g, "/local") == 0 && - guestfs_is_file (g, "/etc/fstab") == 0) - ; - /* Linux /usr? */ - else if (is_dir_etc && - is_dir_bin && - is_dir_share && - guestfs_is_dir (g, "/local") > 0 && - guestfs_is_file (g, "/etc/fstab") == 0) { - if (guestfs_int_check_linux_usr (g, fs) == -1) - return -1; - } - /* CoreOS /usr? */ - else if (is_dir_bin && - is_dir_share && - guestfs_is_dir (g, "/local") > 0 && - guestfs_is_dir (g, "/share/coreos") > 0) { - if (guestfs_int_check_coreos_usr (g, fs) == -1) - return -1; - } - /* Linux /var? */ - else if (guestfs_is_dir (g, "/log") > 0 && - guestfs_is_dir (g, "/run") > 0 && - guestfs_is_dir (g, "/spool") > 0) - ; - /* Windows root? */ - else if ((windows_systemroot = guestfs_int_get_windows_systemroot (g)) != NULL) - { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; - if (guestfs_int_check_windows_root (g, fs, windows_systemroot) == -1) - return -1; - } - /* Windows volume with installed applications (but not root)? */ - else if (guestfs_int_is_dir_nocase (g, "/System Volume Information") > 0 && - guestfs_int_is_dir_nocase (g, "/Program Files") > 0) - ; - /* Windows volume (but not root)? */ - else if (guestfs_int_is_dir_nocase (g, "/System Volume Information") > 0) - ; - /* FreeDOS? */ - else if (guestfs_int_is_dir_nocase (g, "/FDOS") > 0 && - guestfs_int_is_file_nocase (g, "/FDOS/FREEDOS.BSS") > 0) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLED; - fs->type = OS_TYPE_DOS; - fs->distro = OS_DISTRO_FREEDOS; - /* FreeDOS is a mix of 16 and 32 bit, but assume it requires a - * 32 bit i386 processor. - */ - fs->arch = safe_strdup (g, "i386"); - } - /* Install CD/disk? - * - * Note that we checked (above) for an install ISO, but there are - * other types of install image (eg. USB keys) which that check - * wouldn't have picked up. - * - * Skip these checks if it's not a whole device (eg. CD) or the - * first partition (eg. bootable USB key). - */ - else if ((whole_device || (partnum == 1 && nr_partitions == 1)) && - (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0 || - guestfs_is_dir (g, "/EFI/BOOT") > 0 || - guestfs_is_file (g, "/images/install.img") > 0 || - guestfs_is_dir (g, "/.disk") > 0 || - guestfs_is_file (g, "/.discinfo") > 0 || - guestfs_is_file (g, "/i386/txtsetup.sif") > 0 || - guestfs_is_file (g, "/amd64/txtsetup.sif") > 0 || - guestfs_is_file (g, "/freedos/freedos.ico") > 0 || - guestfs_is_file (g, "/boot/loader.rc") > 0)) { - fs->role = OS_ROLE_ROOT; - fs->format = OS_FORMAT_INSTALLER; - if (guestfs_int_check_installer_root (g, fs) == -1) - return -1; - } - - /* The above code should have set fs->type and fs->distro fields, so - * we can now guess the package management system. - */ - guestfs_int_check_package_format (g, fs); - guestfs_int_check_package_management (g, fs); - - return 0; -} - -static void -extend_fses (guestfs_h *g) -{ - const size_t n = g->nr_fses + 1; - struct inspect_fs *p; - - p = safe_realloc (g, g->fses, n * sizeof (struct inspect_fs)); - - g->fses = p; - g->nr_fses = n; - - memset (&g->fses[n-1], 0, sizeof (struct inspect_fs)); -} - -/* Given a partition (eg. /dev/sda2) then return the partition number - * (eg. 2) and the total number of other partitions. - */ -static int -get_partition_context (guestfs_h *g, const char *partition, - int *partnum_ret, int *nr_partitions_ret) -{ - int partnum, nr_partitions; - CLEANUP_FREE char *device = NULL; - CLEANUP_FREE_PARTITION_LIST struct guestfs_partition_list *partitions = NULL; - - partnum = guestfs_part_to_partnum (g, partition); - if (partnum == -1) - return -1; - - device = guestfs_part_to_dev (g, partition); - if (device == NULL) - return -1; - - partitions = guestfs_part_list (g, device); - if (partitions == NULL) - return -1; - - nr_partitions = partitions->len; - - *partnum_ret = partnum; - *nr_partitions_ret = nr_partitions; - return 0; -} - -static int -is_symlink_to (guestfs_h *g, const char *file, const char *wanted_target) -{ - CLEANUP_FREE char *target = NULL; - - if (guestfs_is_symlink (g, file) == 0) - return 0; - - target = guestfs_readlink (g, file); - /* This should not fail, but play safe. */ - if (target == NULL) - return 0; - - return STREQ (target, wanted_target); -} - -int -guestfs_int_is_file_nocase (guestfs_h *g, const char *path) -{ - CLEANUP_FREE char *p = NULL; - int r; - - p = guestfs_int_case_sensitive_path_silently (g, path); - if (!p) - return 0; - r = guestfs_is_file (g, p); - return r > 0; -} - -int -guestfs_int_is_dir_nocase (guestfs_h *g, const char *path) -{ - CLEANUP_FREE char *p = NULL; - int r; - - p = guestfs_int_case_sensitive_path_silently (g, path); - if (!p) - return 0; - r = guestfs_is_dir (g, p); - return r > 0; -} - /* Parse small, unsigned ints, as used in version numbers. */ int guestfs_int_parse_unsigned_int (guestfs_h *g, const char *str) @@ -436,214 +63,6 @@ guestfs_int_parse_unsigned_int_ignore_trailing (guestfs_h *g, const char *str) return ret; } -/* Parse generic MAJOR.MINOR from the fs->product_name string. */ -int -guestfs_int_parse_major_minor (guestfs_h *g, struct inspect_fs *fs) -{ - if (guestfs_int_version_from_x_y (g, &fs->version, fs->product_name) == -1) - return -1; - - return 0; -} - -/* At the moment, package format and package management is just a - * simple function of the distro and version.v_major fields, so these - * can never return an error. We might be cleverer in future. - */ -void -guestfs_int_check_package_format (guestfs_h *g, struct inspect_fs *fs) -{ - switch (fs->distro) { - case OS_DISTRO_FEDORA: - case OS_DISTRO_MEEGO: - case OS_DISTRO_REDHAT_BASED: - case OS_DISTRO_RHEL: - case OS_DISTRO_MAGEIA: - case OS_DISTRO_MANDRIVA: - case OS_DISTRO_SUSE_BASED: - case OS_DISTRO_OPENSUSE: - case OS_DISTRO_SLES: - case OS_DISTRO_CENTOS: - case OS_DISTRO_SCIENTIFIC_LINUX: - case OS_DISTRO_ORACLE_LINUX: - case OS_DISTRO_ALTLINUX: - fs->package_format = OS_PACKAGE_FORMAT_RPM; - break; - - case OS_DISTRO_DEBIAN: - case OS_DISTRO_UBUNTU: - case OS_DISTRO_LINUX_MINT: - fs->package_format = OS_PACKAGE_FORMAT_DEB; - break; - - case OS_DISTRO_ARCHLINUX: - fs->package_format = OS_PACKAGE_FORMAT_PACMAN; - break; - case OS_DISTRO_GENTOO: - fs->package_format = OS_PACKAGE_FORMAT_EBUILD; - break; - case OS_DISTRO_PARDUS: - fs->package_format = OS_PACKAGE_FORMAT_PISI; - break; - - case OS_DISTRO_ALPINE_LINUX: - fs->package_format = OS_PACKAGE_FORMAT_APK; - break; - - case OS_DISTRO_VOID_LINUX: - fs->package_format = OS_PACKAGE_FORMAT_XBPS; - break; - - case OS_DISTRO_SLACKWARE: - case OS_DISTRO_TTYLINUX: - case OS_DISTRO_COREOS: - case OS_DISTRO_WINDOWS: - case OS_DISTRO_BUILDROOT: - case OS_DISTRO_CIRROS: - case OS_DISTRO_FREEDOS: - case OS_DISTRO_FREEBSD: - case OS_DISTRO_NETBSD: - case OS_DISTRO_OPENBSD: - case OS_DISTRO_FRUGALWARE: - case OS_DISTRO_PLD_LINUX: - case OS_DISTRO_UNKNOWN: - fs->package_format = OS_PACKAGE_FORMAT_UNKNOWN; - break; - } -} - -void -guestfs_int_check_package_management (guestfs_h *g, struct inspect_fs *fs) -{ - switch (fs->distro) { - case OS_DISTRO_MEEGO: - fs->package_management = OS_PACKAGE_MANAGEMENT_YUM; - break; - - case OS_DISTRO_FEDORA: - /* If Fedora >= 22 and dnf is installed, say "dnf". */ - if (guestfs_int_version_ge (&fs->version, 22, 0, 0) && - guestfs_is_file_opts (g, "/usr/bin/dnf", - GUESTFS_IS_FILE_OPTS_FOLLOWSYMLINKS, 1, -1) > 0) - fs->package_management = OS_PACKAGE_MANAGEMENT_DNF; - else if (guestfs_int_version_ge (&fs->version, 1, 0, 0)) - fs->package_management = OS_PACKAGE_MANAGEMENT_YUM; - else - /* Probably parsing the release file failed, see RHBZ#1332025. */ - fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN; - break; - - case OS_DISTRO_REDHAT_BASED: - case OS_DISTRO_RHEL: - case OS_DISTRO_CENTOS: - case OS_DISTRO_SCIENTIFIC_LINUX: - case OS_DISTRO_ORACLE_LINUX: - if (guestfs_int_version_ge (&fs->version, 5, 0, 0)) - fs->package_management = OS_PACKAGE_MANAGEMENT_YUM; - else if (guestfs_int_version_ge (&fs->version, 2, 0, 0)) - fs->package_management = OS_PACKAGE_MANAGEMENT_UP2DATE; - else - /* Probably parsing the release file failed, see RHBZ#1332025. */ - fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN; - break; - - case OS_DISTRO_DEBIAN: - case OS_DISTRO_UBUNTU: - case OS_DISTRO_LINUX_MINT: - case OS_DISTRO_ALTLINUX: - fs->package_management = OS_PACKAGE_MANAGEMENT_APT; - break; - - case OS_DISTRO_ARCHLINUX: - fs->package_management = OS_PACKAGE_MANAGEMENT_PACMAN; - break; - case OS_DISTRO_GENTOO: - fs->package_management = OS_PACKAGE_MANAGEMENT_PORTAGE; - break; - case OS_DISTRO_PARDUS: - fs->package_management = OS_PACKAGE_MANAGEMENT_PISI; - break; - case OS_DISTRO_MAGEIA: - case OS_DISTRO_MANDRIVA: - fs->package_management = OS_PACKAGE_MANAGEMENT_URPMI; - break; - - case OS_DISTRO_SUSE_BASED: - case OS_DISTRO_OPENSUSE: - case OS_DISTRO_SLES: - fs->package_management = OS_PACKAGE_MANAGEMENT_ZYPPER; - break; - - case OS_DISTRO_ALPINE_LINUX: - fs->package_management = OS_PACKAGE_MANAGEMENT_APK; - break; - - case OS_DISTRO_VOID_LINUX: - fs->package_management = OS_PACKAGE_MANAGEMENT_XBPS; - break; - - case OS_DISTRO_SLACKWARE: - case OS_DISTRO_TTYLINUX: - case OS_DISTRO_COREOS: - case OS_DISTRO_WINDOWS: - case OS_DISTRO_BUILDROOT: - case OS_DISTRO_CIRROS: - case OS_DISTRO_FREEDOS: - case OS_DISTRO_FREEBSD: - case OS_DISTRO_NETBSD: - case OS_DISTRO_OPENBSD: - case OS_DISTRO_FRUGALWARE: - case OS_DISTRO_PLD_LINUX: - case OS_DISTRO_UNKNOWN: - fs->package_management = OS_PACKAGE_MANAGEMENT_UNKNOWN; - break; - } -} - -/* Get the first line of a small file, without any trailing newline - * character. - * - * NOTE: If the file is completely empty or begins with a '\n' - * character, this returns an empty string (not NULL). The caller - * will usually need to check for this case. - */ -char * -guestfs_int_first_line_of_file (guestfs_h *g, const char *filename) -{ - char **lines = NULL; /* sic: not CLEANUP_FREE_STRING_LIST */ - int64_t size; - char *ret; - - /* Don't trust guestfs_head_n not to break with very large files. - * Check the file size is something reasonable first. - */ - size = guestfs_filesize (g, filename); - if (size == -1) - /* guestfs_filesize failed and has already set error in handle */ - return NULL; - if (size > MAX_SMALL_FILE_SIZE) { - error (g, _("size of %s is unreasonably large (%" PRIi64 " bytes)"), - filename, size); - return NULL; - } - - lines = guestfs_head_n (g, 1, filename); - if (lines == NULL) - return NULL; - if (lines[0] == NULL) { - guestfs_int_free_string_list (lines); - /* Empty file: Return an empty string as explained above. */ - return safe_strdup (g, ""); - } - /* lines[1] should be NULL because of '1' argument above ... */ - - ret = lines[0]; /* caller frees */ - - free (lines); - - return ret; -} - /* Get the first matching line (using egrep [-i]) of a small file, * without any trailing newline character. * @@ -696,103 +115,3 @@ guestfs_int_first_egrep_of_file (guestfs_h *g, const char *filename, return 1; } - -/* Merge the missing OS inspection information found on the src inspect_fs into - * the ones of the dst inspect_fs. This function is useful if the inspection - * information for an OS are gathered by inspecting multiple filesystems. - */ -void -guestfs_int_merge_fs_inspections (guestfs_h *g, struct inspect_fs *dst, struct inspect_fs *src) -{ - size_t n, i, old; - struct inspect_fstab_entry *fstab = NULL; - char ** mappings = NULL; - - if (dst->type == 0) - dst->type = src->type; - - if (dst->distro == 0) - dst->distro = src->distro; - - if (dst->package_format == 0) - dst->package_format = src->package_format; - - if (dst->package_management == 0) - dst->package_management = src->package_management; - - if (dst->product_name == NULL) { - dst->product_name = src->product_name; - src->product_name = NULL; - } - - if (dst->product_variant == NULL) { - dst->product_variant= src->product_variant; - src->product_variant = NULL; - } - - if (version_is_null (&dst->version)) - dst->version = src->version; - - if (dst->arch == NULL) { - dst->arch = src->arch; - src->arch = NULL; - } - - if (dst->hostname == NULL) { - dst->hostname = src->hostname; - src->hostname = NULL; - } - - if (dst->windows_systemroot == NULL) { - dst->windows_systemroot = src->windows_systemroot; - src->windows_systemroot = NULL; - } - - if (dst->windows_current_control_set == NULL) { - dst->windows_current_control_set = src->windows_current_control_set; - src->windows_current_control_set = NULL; - } - - if (src->drive_mappings != NULL) { - if (dst->drive_mappings == NULL) { - /* Adopt the drive mappings of src */ - dst->drive_mappings = src->drive_mappings; - src->drive_mappings = NULL; - } else { - n = 0; - for (; dst->drive_mappings[n] != NULL; n++) - ; - old = n; - for (; src->drive_mappings[n] != NULL; n++) - ; - - /* Merge the src mappings to dst */ - mappings = safe_realloc (g, dst->drive_mappings,(n + 1) * sizeof (char *)); - - for (i = old; i < n; i++) - mappings[i] = src->drive_mappings[i - old]; - - mappings[n] = NULL; - dst->drive_mappings = mappings; - - free(src->drive_mappings); - src->drive_mappings = NULL; - } - } - - if (src->nr_fstab > 0) { - n = dst->nr_fstab + src->nr_fstab; - fstab = safe_realloc (g, dst->fstab, n * sizeof (struct inspect_fstab_entry)); - - for (i = 0; i < src->nr_fstab; i++) { - fstab[dst->nr_fstab + i].mountable = src->fstab[i].mountable; - fstab[dst->nr_fstab + i].mountpoint = src->fstab[i].mountpoint; - } - free(src->fstab); - src->fstab = NULL; - src->nr_fstab = 0; - - dst->fstab = fstab; - dst->nr_fstab = n; - } -} diff --git a/lib/inspect-icon.c b/lib/inspect-icon.c index 89c232f5b..f4f5f0660 100644 --- a/lib/inspect-icon.c +++ b/lib/inspect-icon.c @@ -51,22 +51,24 @@ * An icon was found. 'ret' points to the icon buffer, and *size_r * is the size. */ -static char *icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); -static char *icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); -static char *icon_rhel (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); -static char *icon_debian (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); -static char *icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); -static char *icon_mageia (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); -static char *icon_opensuse (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); +static char *icon_favicon (guestfs_h *g, const char *type, size_t *size_r); +static char *icon_fedora (guestfs_h *g, size_t *size_r); +static char *icon_rhel (guestfs_h *g, int major, size_t *size_r); +static char *icon_debian (guestfs_h *g, size_t *size_r); +static char *icon_ubuntu (guestfs_h *g, size_t *size_r); +static char *icon_mageia (guestfs_h *g, size_t *size_r); +static char *icon_opensuse (guestfs_h *g, size_t *size_r); #if CAN_DO_CIRROS -static char *icon_cirros (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); +static char *icon_cirros (guestfs_h *g, size_t *size_r); #endif -static char *icon_voidlinux (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); -static char *icon_altlinux (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); +static char *icon_voidlinux (guestfs_h *g, size_t *size_r); +static char *icon_altlinux (guestfs_h *g, size_t *size_r); #if CAN_DO_WINDOWS -static char *icon_windows (guestfs_h *g, struct inspect_fs *fs, size_t *size_r); +static char *icon_windows (guestfs_h *g, const char *root, size_t *size_r); #endif +static char *case_sensitive_path_silently (guestfs_h *g, const char *path); + /* Dummy static object. */ static char *NOT_FOUND = (char *) "not_found"; @@ -82,13 +84,17 @@ char * guestfs_impl_inspect_get_icon (guestfs_h *g, const char *root, size_t *size_r, const struct guestfs_inspect_get_icon_argv *optargs) { - struct inspect_fs *fs; char *r = NOT_FOUND; int favicon, highquality; size_t size; + CLEANUP_FREE char *type = NULL; + CLEANUP_FREE char *distro = NULL; - fs = guestfs_int_search_for_root (g, root); - if (!fs) + type = guestfs_inspect_get_type (g, root); + if (!type) + return NULL; + distro = guestfs_inspect_get_distro (g, root); + if (!distro) return NULL; /* Get optargs, or defaults. */ @@ -106,7 +112,7 @@ guestfs_impl_inspect_get_icon (guestfs_h *g, const char *root, size_t *size_r, /* Try looking for a favicon first. */ if (favicon) { - r = icon_favicon (g, fs, &size); + r = icon_favicon (g, type, &size); if (!r) return NULL; @@ -120,96 +126,52 @@ guestfs_impl_inspect_get_icon (guestfs_h *g, const char *root, size_t *size_r, /* Favicon failed, so let's try a method based on the detected operating * system. */ - switch (fs->type) { - case OS_TYPE_LINUX: - case OS_TYPE_HURD: - switch (fs->distro) { - case OS_DISTRO_FEDORA: - r = icon_fedora (g, fs, &size); - break; - - case OS_DISTRO_RHEL: - case OS_DISTRO_REDHAT_BASED: - case OS_DISTRO_CENTOS: - case OS_DISTRO_SCIENTIFIC_LINUX: - case OS_DISTRO_ORACLE_LINUX: - r = icon_rhel (g, fs, &size); - break; - - case OS_DISTRO_DEBIAN: - r = icon_debian (g, fs, &size); - break; - - case OS_DISTRO_UBUNTU: + if (STREQ (type, "linux") || STREQ (type, "hurd")) { + if (STREQ (distro, "fedora")) { + r = icon_fedora (g, &size); + } + else if (STREQ (distro, "rhel") || + STREQ (distro, "redhat-based") || + STREQ (distro, "centos") || + STREQ (distro, "scientificlinux") || + STREQ (distro, "oraclelinux")) { + r = icon_rhel (g, guestfs_inspect_get_major_version (g, root), &size); + } + else if (STREQ (distro, "debian")) { + r = icon_debian (g, &size); + } + else if (STREQ (distro, "ubuntu")) { if (!highquality) - r = icon_ubuntu (g, fs, &size); - break; - - case OS_DISTRO_MAGEIA: - r = icon_mageia (g, fs, &size); - break; - - case OS_DISTRO_SUSE_BASED: - case OS_DISTRO_OPENSUSE: - case OS_DISTRO_SLES: - r = icon_opensuse (g, fs, &size); - break; - - case OS_DISTRO_CIRROS: + r = icon_ubuntu (g, &size); + } + else if (STREQ (distro, "mageia")) { + r = icon_mageia (g, &size); + } + else if (STREQ (distro, "suse-based") || + STREQ (distro, "opensuse") || + STREQ (distro, "sles")) { + r = icon_opensuse (g, &size); + } + else if (STREQ (distro, "cirros")) { #if CAN_DO_CIRROS - r = icon_cirros (g, fs, &size); + r = icon_cirros (g, &size); #endif - break; - - case OS_DISTRO_VOID_LINUX: - r = icon_voidlinux (g, fs, &size); - break; - - case OS_DISTRO_ALTLINUX: - r = icon_altlinux (g, fs, &size); - break; - - /* These are just to keep gcc warnings happy. */ - case OS_DISTRO_ARCHLINUX: - case OS_DISTRO_BUILDROOT: - case OS_DISTRO_COREOS: - case OS_DISTRO_FREEDOS: - case OS_DISTRO_GENTOO: - case OS_DISTRO_LINUX_MINT: - case OS_DISTRO_MANDRIVA: - case OS_DISTRO_MEEGO: - case OS_DISTRO_PARDUS: - case OS_DISTRO_SLACKWARE: - case OS_DISTRO_TTYLINUX: - case OS_DISTRO_WINDOWS: - case OS_DISTRO_FREEBSD: - case OS_DISTRO_NETBSD: - case OS_DISTRO_OPENBSD: - case OS_DISTRO_ALPINE_LINUX: - case OS_DISTRO_FRUGALWARE: - case OS_DISTRO_PLD_LINUX: - case OS_DISTRO_UNKNOWN: - ; /* nothing */ } - break; - - case OS_TYPE_WINDOWS: + else if (STREQ (distro, "voidlinux")) { + r = icon_voidlinux (g, &size); + } + else if (STREQ (distro, "altlinux")) { + r = icon_altlinux (g, &size); + } + } + else if (STREQ (type, "windows")) { #if CAN_DO_WINDOWS /* We don't know how to get high quality icons from a Windows guest, * so disable this if high quality was specified. */ if (!highquality) - r = icon_windows (g, fs, &size); + r = icon_windows (g, root, &size); #endif - break; - - case OS_TYPE_FREEBSD: - case OS_TYPE_NETBSD: - case OS_TYPE_DOS: - case OS_TYPE_OPENBSD: - case OS_TYPE_MINIX: - case OS_TYPE_UNKNOWN: - ; /* nothing */ } if (r == NOT_FOUND) { @@ -229,8 +191,7 @@ guestfs_impl_inspect_get_icon (guestfs_h *g, const char *root, size_t *size_r, * If it is, download and return it. */ static char * -get_png (guestfs_h *g, struct inspect_fs *fs, const char *filename, - size_t *size_r, size_t max_size) +get_png (guestfs_h *g, const char *filename, size_t *size_r, size_t max_size) { char *ret; CLEANUP_FREE char *real = NULL; @@ -270,7 +231,7 @@ get_png (guestfs_h *g, struct inspect_fs *fs, const char *filename, if (max_size == 0) max_size = 4 * w * h; - local = guestfs_int_download_to_tmp (g, fs, real, "icon", max_size); + local = guestfs_int_download_to_tmp (g, real, "icon", max_size); if (!local) return NOT_FOUND; @@ -285,20 +246,20 @@ get_png (guestfs_h *g, struct inspect_fs *fs, const char *filename, * it has a reasonable size and format. */ static char * -icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_favicon (guestfs_h *g, const char *type, size_t *size_r) { char *ret; char *filename = safe_strdup (g, "/etc/favicon.png"); - if (fs->type == OS_TYPE_WINDOWS) { - char *f = guestfs_int_case_sensitive_path_silently (g, filename); + if (STREQ (type, "windows")) { + char *f = case_sensitive_path_silently (g, filename); if (f) { free (filename); filename = f; } } - ret = get_png (g, fs, filename, size_r, 0); + ret = get_png (g, filename, size_r, 0); free (filename); return ret; } @@ -309,9 +270,9 @@ icon_favicon (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) #define FEDORA_ICON "/usr/share/icons/hicolor/96x96/apps/fedora-logo-icon.png" static char * -icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_fedora (guestfs_h *g, size_t *size_r) { - return get_png (g, fs, FEDORA_ICON, size_r, 0); + return get_png (g, FEDORA_ICON, size_r, 0); } /* RHEL 3, 4: @@ -330,28 +291,28 @@ icon_fedora (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) * RHEL clones have different sizes. */ static char * -icon_rhel (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_rhel (guestfs_h *g, int major, size_t *size_r) { const char *shadowman; - if (!guestfs_int_version_ge (&fs->version, 7, 0, 0)) + if (major < 7) shadowman = "/usr/share/pixmaps/redhat/shadowman-transparent.png"; else shadowman = "/usr/share/pixmaps/fedora-logo-sprite.png"; - return get_png (g, fs, shadowman, size_r, 102400); + return get_png (g, shadowman, size_r, 102400); } #define DEBIAN_ICON "/usr/share/pixmaps/debian-logo.png" static char * -icon_debian (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_debian (guestfs_h *g, size_t *size_r) { - return get_png (g, fs, DEBIAN_ICON, size_r, 2048); + return get_png (g, DEBIAN_ICON, size_r, 2048); } static char * -icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_ubuntu (guestfs_h *g, size_t *size_r) { const char *icons[] = { "/usr/share/icons/gnome/24x24/places/ubuntu-logo.png", @@ -366,7 +327,7 @@ icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) char *ret; for (i = 0; icons[i] != NULL; ++i) { - ret = get_png (g, fs, icons[i], size_r, 2048); + ret = get_png (g, icons[i], size_r, 2048); if (ret == NULL) return NULL; if (ret != NOT_FOUND) @@ -378,17 +339,17 @@ icon_ubuntu (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) #define MAGEIA_ICON "/usr/share/icons/mageia.png" static char * -icon_mageia (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_mageia (guestfs_h *g, size_t *size_r) { - return get_png (g, fs, MAGEIA_ICON, size_r, 2048); + return get_png (g, MAGEIA_ICON, size_r, 2048); } #define OPENSUSE_ICON "/usr/share/icons/hicolor/24x24/apps/distributor.png" static char * -icon_opensuse (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_opensuse (guestfs_h *g, size_t *size_r) { - return get_png (g, fs, OPENSUSE_ICON, size_r, 2048); + return get_png (g, OPENSUSE_ICON, size_r, 2048); } #if CAN_DO_CIRROS @@ -397,7 +358,7 @@ icon_opensuse (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) #define CIRROS_LOGO "/usr/share/cirros/logo" static char * -icon_cirros (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_cirros (guestfs_h *g, size_t *size_r) { char *ret; CLEANUP_FREE char *type = NULL; @@ -421,7 +382,7 @@ icon_cirros (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) if (!STRPREFIX (type, "ASCII text")) return NOT_FOUND; - local = guestfs_int_download_to_tmp (g, fs, CIRROS_LOGO, "icon", 1024); + local = guestfs_int_download_to_tmp (g, CIRROS_LOGO, "icon", 1024); if (!local) return NOT_FOUND; @@ -450,17 +411,17 @@ icon_cirros (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) #define VOIDLINUX_ICON "/usr/share/void-artwork/void-logo.png" static char * -icon_voidlinux (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_voidlinux (guestfs_h *g, size_t *size_r) { - return get_png (g, fs, VOIDLINUX_ICON, size_r, 20480); + return get_png (g, VOIDLINUX_ICON, size_r, 20480); } #define ALTLINUX_ICON "/usr/share/icons/hicolor/48x48/apps/altlinux.png" static char * -icon_altlinux (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_altlinux (guestfs_h *g, size_t *size_r) { - return get_png (g, fs, ALTLINUX_ICON, size_r, 20480); + return get_png (g, ALTLINUX_ICON, size_r, 20480); } #if CAN_DO_WINDOWS @@ -481,7 +442,7 @@ icon_altlinux (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) */ static char * -icon_windows_xp (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_windows_xp (guestfs_h *g, const char *systemroot, size_t *size_r) { CLEANUP_FREE char *filename = NULL; CLEANUP_FREE char *filename_case = NULL; @@ -492,7 +453,7 @@ icon_windows_xp (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) char *ret; /* Download %systemroot%\explorer.exe */ - filename = safe_asprintf (g, "%s/explorer.exe", fs->windows_systemroot); + filename = safe_asprintf (g, "%s/explorer.exe", systemroot); filename_case = guestfs_case_sensitive_path (g, filename); if (filename_case == NULL) return NULL; @@ -505,7 +466,7 @@ icon_windows_xp (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) if (r == 0) return NOT_FOUND; - filename_downloaded = guestfs_int_download_to_tmp (g, fs, filename_case, + filename_downloaded = guestfs_int_download_to_tmp (g, filename_case, "explorer.exe", MAX_WINDOWS_EXPLORER_SIZE); if (filename_downloaded == NULL) @@ -543,7 +504,7 @@ static const char *win7_explorer[] = { }; static char * -icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_windows_7 (guestfs_h *g, const char *systemroot, size_t *size_r) { size_t i; CLEANUP_FREE char *filename_case = NULL; @@ -556,11 +517,10 @@ icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) for (i = 0; win7_explorer[i] != NULL; ++i) { CLEANUP_FREE char *filename = NULL; - filename = safe_asprintf (g, "%s/%s", - fs->windows_systemroot, win7_explorer[i]); + filename = safe_asprintf (g, "%s/%s", systemroot, win7_explorer[i]); free (filename_case); - filename_case = guestfs_int_case_sensitive_path_silently (g, filename); + filename_case = case_sensitive_path_silently (g, filename); if (filename_case == NULL) continue; @@ -575,7 +535,7 @@ icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) if (win7_explorer[i] == NULL) return NOT_FOUND; - filename_downloaded = guestfs_int_download_to_tmp (g, fs, filename_case, + filename_downloaded = guestfs_int_download_to_tmp (g, filename_case, "explorer.exe", MAX_WINDOWS_EXPLORER_SIZE); if (filename_downloaded == NULL) @@ -609,14 +569,14 @@ icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) * - /Windows/System32/slui.exe --type=14 group icon #2 */ static char * -icon_windows_8 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_windows_8 (guestfs_h *g, size_t *size_r) { CLEANUP_FREE char *filename_case = NULL; CLEANUP_FREE char *filename_downloaded = NULL; int r; char *ret; - filename_case = guestfs_int_case_sensitive_path_silently + filename_case = case_sensitive_path_silently (g, "/ProgramData/Microsoft/Windows Live/WLive48x48.png"); if (filename_case == NULL) return NOT_FOUND; /* Not an error since a parent dir might not exist. */ @@ -629,7 +589,7 @@ icon_windows_8 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) if (r == 0) return NOT_FOUND; - filename_downloaded = guestfs_int_download_to_tmp (g, fs, filename_case, + filename_downloaded = guestfs_int_download_to_tmp (g, filename_case, "wlive48x48.png", 8192); if (filename_downloaded == NULL) return NOT_FOUND; @@ -641,25 +601,46 @@ icon_windows_8 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) } static char * -icon_windows (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) +icon_windows (guestfs_h *g, const char *root, size_t *size_r) { - if (fs->windows_systemroot == NULL) + CLEANUP_FREE char *systemroot + guestfs_inspect_get_windows_systemroot (g, root); + int major = guestfs_inspect_get_major_version (g, root); + int minor = guestfs_inspect_get_minor_version (g, root); + + if (systemroot == NULL) return NOT_FOUND; /* Windows XP. */ - if (fs->version.v_major == 5 && fs->version.v_minor == 1) - return icon_windows_xp (g, fs, size_r); + if (major == 5 && minor == 1) + return icon_windows_xp (g, systemroot, size_r); /* Windows 7. */ - else if (fs->version.v_major == 6 && fs->version.v_minor == 1) - return icon_windows_7 (g, fs, size_r); + else if (major == 6 && minor == 1) + return icon_windows_7 (g, systemroot, size_r); /* Windows 8. */ - else if (fs->version.v_major == 6 && fs->version.v_minor == 2) - return icon_windows_8 (g, fs, size_r); + else if (major == 6 && minor == 2) + return icon_windows_8 (g, size_r); /* Not (yet) a supported version of Windows. */ else return NOT_FOUND; } #endif /* CAN_DO_WINDOWS */ + +/* NB: This function DOES NOT test for the existence of the file. It + * will return non-NULL even if the file/directory does not exist. + * You have to call guestfs_is_file{,_opts} etc. + */ +static char * +case_sensitive_path_silently (guestfs_h *g, const char *path) +{ + char *ret; + + guestfs_push_error_handler (g, NULL, NULL); + ret = guestfs_case_sensitive_path (g, path); + guestfs_pop_error_handler (g); + + return ret; +} diff --git a/lib/inspect.c b/lib/inspect.c index 1cc0942f1..f2d64b61e 100644 --- a/lib/inspect.c +++ b/lib/inspect.c @@ -43,688 +43,6 @@ #include "guestfs-internal.h" #include "guestfs-internal-actions.h" -COMPILE_REGEXP (re_primary_partition, "^/dev/(?:h|s|v)d.[1234]$", 0) - -static void check_for_duplicated_bsd_root (guestfs_h *g); -static void collect_coreos_inspection_info (guestfs_h *g); -static void collect_linux_inspection_info (guestfs_h *g); -static void collect_linux_inspection_info_for (guestfs_h *g, struct inspect_fs *root); - -/** - * The main inspection API. - */ -char ** -guestfs_impl_inspect_os (guestfs_h *g) -{ - CLEANUP_FREE_STRING_LIST char **fses = NULL; - char **fs, **ret; - - /* Remove any information previously stored in the handle. */ - guestfs_int_free_inspect_info (g); - - if (guestfs_umount_all (g) == -1) - return NULL; - - /* Iterate over all detected filesystems. Inspect each one in turn - * and add that information to the handle. - */ - - fses = guestfs_list_filesystems (g); - if (fses == NULL) return NULL; - - for (fs = fses; *fs; fs += 2) { - if (guestfs_int_check_for_filesystem_on (g, *fs)) { - guestfs_int_free_inspect_info (g); - return NULL; - } - } - - /* The OS inspection information for CoreOS are gathered by inspecting - * multiple filesystems. Gather all the inspected information in the - * inspect_fs struct of the root filesystem. - */ - collect_coreos_inspection_info (g); - - /* Check if the same filesystem was listed twice as root in g->fses. - * This may happen for the *BSD root partition where an MBR partition - * is a shadow of the real root partition probably /dev/sda5 - */ - check_for_duplicated_bsd_root (g); - - /* For Linux guests with a separate /usr filesyste, merge some of the - * inspected information in that partition to the inspect_fs struct - * of the root filesystem. - */ - collect_linux_inspection_info (g); - - /* At this point we have, in the handle, a list of all filesystems - * found and data about each one. Now we assemble the list of - * filesystems which are root devices and return that to the user. - * Fall through to guestfs_inspect_get_roots to do that. - */ - ret = guestfs_inspect_get_roots (g); - if (ret == NULL) - guestfs_int_free_inspect_info (g); - return ret; -} - -/** - * Traverse through the filesystem list and find out if it contains - * the C</> and C</usr> filesystems of a CoreOS image. If this is the - * case, sum up all the collected information on the root fs. - */ -static void -collect_coreos_inspection_info (guestfs_h *g) -{ - size_t i; - struct inspect_fs *root = NULL, *usr = NULL; - - for (i = 0; i < g->nr_fses; ++i) { - struct inspect_fs *fs = &g->fses[i]; - - if (fs->distro == OS_DISTRO_COREOS && fs->role == OS_ROLE_ROOT) - root = fs; - } - - if (root == NULL) - return; - - for (i = 0; i < g->nr_fses; ++i) { - struct inspect_fs *fs = &g->fses[i]; - - if (fs->distro != OS_DISTRO_COREOS || fs->role != OS_ROLE_USR) - continue; - - /* CoreOS is designed to contain 2 /usr partitions (USR-A, USR-B): - * https://coreos.com/docs/sdk-distributors/sdk/disk-partitions/ - * One is active and one passive. During the initial boot, the passive - * partition is empty and it gets filled up when an update is performed. - * Then, when the system reboots, the boot loader is instructed to boot - * from the passive partition. If both partitions are valid, we cannot - * determine which the active and which the passive is, unless we peep into - * the boot loader. As a workaround, we check the OS versions and pick the - * one with the higher version as active. - */ - if (usr && guestfs_int_version_cmp_ge (&usr->version, &fs->version)) - continue; - - usr = fs; - } - - if (usr == NULL) - return; - - guestfs_int_merge_fs_inspections (g, root, usr); -} - -/** - * Traverse through the filesystems and find the /usr filesystem for - * the specified C<root>: if found, merge its basic inspection details - * to the root when they were set (i.e. because the /usr had os-release - * or other ways to identify the OS). - */ -static void -collect_linux_inspection_info_for (guestfs_h *g, struct inspect_fs *root) -{ - size_t i; - struct inspect_fs *usr = NULL; - - for (i = 0; i < g->nr_fses; ++i) { - struct inspect_fs *fs = &g->fses[i]; - size_t j; - - if (!(fs->distro == root->distro || fs->distro == OS_DISTRO_UNKNOWN) || - fs->role != OS_ROLE_USR) - continue; - - for (j = 0; j < root->nr_fstab; ++j) { - if (STREQ (fs->mountable, root->fstab[j].mountable)) { - usr = fs; - goto got_usr; - } - } - } - - assert (usr == NULL); - return; - - got_usr: - /* If the version information in /usr is not null, then most probably - * there was an os-release file there, so reset what is in root - * and pick the results from /usr. - */ - if (!version_is_null (&usr->version)) { - root->distro = OS_DISTRO_UNKNOWN; - free (root->product_name); - root->product_name = NULL; - } - - guestfs_int_merge_fs_inspections (g, root, usr); -} - -/** - * Traverse through the filesystem list and find out if it contains - * the C</> and C</usr> filesystems of a Linux image (but not CoreOS, - * for which there is a separate C<collect_coreos_inspection_info>). - * If this is the case, sum up all the collected information on each - * root fs from the respective /usr filesystems. - */ -static void -collect_linux_inspection_info (guestfs_h *g) -{ - size_t i; - - for (i = 0; i < g->nr_fses; ++i) { - struct inspect_fs *fs = &g->fses[i]; - - if (fs->distro != OS_DISTRO_COREOS && fs->role == OS_ROLE_ROOT) - collect_linux_inspection_info_for (g, fs); - } -} - -/** - * On *BSD systems, sometimes F</dev/sda[1234]> is a shadow of the - * real root filesystem that is probably F</dev/sda5> (see: - * L<http://www.freebsd.org/doc/handbook/disk-organization.html>) - */ -static void -check_for_duplicated_bsd_root (guestfs_h *g) -{ - size_t i; - struct inspect_fs *bsd_primary = NULL; - - for (i = 0; i < g->nr_fses; ++i) { - bool is_bsd; - struct inspect_fs *fs = &g->fses[i]; - - is_bsd - fs->type == OS_TYPE_FREEBSD || - fs->type == OS_TYPE_NETBSD || - fs->type == OS_TYPE_OPENBSD; - - if (fs->role == OS_ROLE_ROOT && is_bsd && - match (g, fs->mountable, re_primary_partition)) { - bsd_primary = fs; - continue; - } - - if (fs->role == OS_ROLE_ROOT && bsd_primary && - bsd_primary->type == fs->type) { - /* remove the root role from the bsd_primary */ - bsd_primary->role = OS_ROLE_UNKNOWN; - bsd_primary->format = OS_FORMAT_UNKNOWN; - return; - } - } -} - -static int -compare_strings (const void *vp1, const void *vp2) -{ - const char *s1 = * (char * const *) vp1; - const char *s2 = * (char * const *) vp2; - - return strcmp (s1, s2); -} - -char ** -guestfs_impl_inspect_get_roots (guestfs_h *g) -{ - size_t i; - DECLARE_STRINGSBUF (ret); - - /* NB. Doesn't matter if g->nr_fses == 0. We just return an empty - * list in this case. - */ - for (i = 0; i < g->nr_fses; ++i) { - if (g->fses[i].role == OS_ROLE_ROOT) - guestfs_int_add_string (g, &ret, g->fses[i].mountable); - } - guestfs_int_end_stringsbuf (g, &ret); - - qsort (ret.argv, ret.size-1, sizeof (char *), compare_strings); - - return ret.argv; -} - -char * -guestfs_impl_inspect_get_type (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - char *ret = NULL; - - if (!fs) - return NULL; - - switch (fs->type) { - case OS_TYPE_DOS: ret = safe_strdup (g, "dos"); break; - case OS_TYPE_FREEBSD: ret = safe_strdup (g, "freebsd"); break; - case OS_TYPE_HURD: ret = safe_strdup (g, "hurd"); break; - case OS_TYPE_LINUX: ret = safe_strdup (g, "linux"); break; - case OS_TYPE_MINIX: ret = safe_strdup (g, "minix"); break; - case OS_TYPE_NETBSD: ret = safe_strdup (g, "netbsd"); break; - case OS_TYPE_OPENBSD: ret = safe_strdup (g, "openbsd"); break; - case OS_TYPE_WINDOWS: ret = safe_strdup (g, "windows"); break; - case OS_TYPE_UNKNOWN: ret = safe_strdup (g, "unknown"); break; - } - - if (ret == NULL) - abort (); - - return ret; -} - -char * -guestfs_impl_inspect_get_arch (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - return safe_strdup (g, fs->arch ? : "unknown"); -} - -char * -guestfs_impl_inspect_get_distro (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - char *ret = NULL; - - if (!fs) - return NULL; - - switch (fs->distro) { - case OS_DISTRO_ALPINE_LINUX: ret = safe_strdup (g, "alpinelinux"); break; - case OS_DISTRO_ALTLINUX: ret = safe_strdup (g, "altlinux"); break; - case OS_DISTRO_ARCHLINUX: ret = safe_strdup (g, "archlinux"); break; - case OS_DISTRO_BUILDROOT: ret = safe_strdup (g, "buildroot"); break; - case OS_DISTRO_CENTOS: ret = safe_strdup (g, "centos"); break; - case OS_DISTRO_CIRROS: ret = safe_strdup (g, "cirros"); break; - case OS_DISTRO_COREOS: ret = safe_strdup (g, "coreos"); break; - case OS_DISTRO_DEBIAN: ret = safe_strdup (g, "debian"); break; - case OS_DISTRO_FEDORA: ret = safe_strdup (g, "fedora"); break; - case OS_DISTRO_FREEBSD: ret = safe_strdup (g, "freebsd"); break; - case OS_DISTRO_FREEDOS: ret = safe_strdup (g, "freedos"); break; - case OS_DISTRO_FRUGALWARE: ret = safe_strdup (g, "frugalware"); break; - case OS_DISTRO_GENTOO: ret = safe_strdup (g, "gentoo"); break; - case OS_DISTRO_LINUX_MINT: ret = safe_strdup (g, "linuxmint"); break; - case OS_DISTRO_MAGEIA: ret = safe_strdup (g, "mageia"); break; - case OS_DISTRO_MANDRIVA: ret = safe_strdup (g, "mandriva"); break; - case OS_DISTRO_MEEGO: ret = safe_strdup (g, "meego"); break; - case OS_DISTRO_NETBSD: ret = safe_strdup (g, "netbsd"); break; - case OS_DISTRO_OPENBSD: ret = safe_strdup (g, "openbsd"); break; - case OS_DISTRO_OPENSUSE: ret = safe_strdup (g, "opensuse"); break; - case OS_DISTRO_ORACLE_LINUX: ret = safe_strdup (g, "oraclelinux"); break; - case OS_DISTRO_PARDUS: ret = safe_strdup (g, "pardus"); break; - case OS_DISTRO_PLD_LINUX: ret = safe_strdup (g, "pldlinux"); break; - case OS_DISTRO_REDHAT_BASED: ret = safe_strdup (g, "redhat-based"); break; - case OS_DISTRO_RHEL: ret = safe_strdup (g, "rhel"); break; - case OS_DISTRO_SCIENTIFIC_LINUX: ret = safe_strdup (g, "scientificlinux"); break; - case OS_DISTRO_SLACKWARE: ret = safe_strdup (g, "slackware"); break; - case OS_DISTRO_SLES: ret = safe_strdup (g, "sles"); break; - case OS_DISTRO_SUSE_BASED: ret = safe_strdup (g, "suse-based"); break; - case OS_DISTRO_TTYLINUX: ret = safe_strdup (g, "ttylinux"); break; - case OS_DISTRO_WINDOWS: ret = safe_strdup (g, "windows"); break; - case OS_DISTRO_UBUNTU: ret = safe_strdup (g, "ubuntu"); break; - case OS_DISTRO_VOID_LINUX: ret = safe_strdup (g, "voidlinux"); break; - case OS_DISTRO_UNKNOWN: ret = safe_strdup (g, "unknown"); break; - } - - if (ret == NULL) - abort (); - - return ret; -} - -int -guestfs_impl_inspect_get_major_version (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return -1; - - return fs->version.v_major; -} - -int -guestfs_impl_inspect_get_minor_version (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return -1; - - return fs->version.v_minor; -} - -char * -guestfs_impl_inspect_get_product_name (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - return safe_strdup (g, fs->product_name ? : "unknown"); -} - -char * -guestfs_impl_inspect_get_product_variant (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - return safe_strdup (g, fs->product_variant ? : "unknown"); -} - -char * -guestfs_impl_inspect_get_windows_systemroot (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - if (!fs->windows_systemroot) { - error (g, _("not a Windows guest, or systemroot could not be determined")); - return NULL; - } - - return safe_strdup (g, fs->windows_systemroot); -} - -char * -guestfs_impl_inspect_get_windows_software_hive (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - if (!fs->windows_software_hive) { - error (g, _("not a Windows guest, or software hive not found")); - return NULL; - } - - return safe_strdup (g, fs->windows_software_hive); -} - -char * -guestfs_impl_inspect_get_windows_system_hive (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - if (!fs->windows_system_hive) { - error (g, _("not a Windows guest, or system hive not found")); - return NULL; - } - - return safe_strdup (g, fs->windows_system_hive); -} - -char * -guestfs_impl_inspect_get_windows_current_control_set (guestfs_h *g, - const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - if (!fs->windows_current_control_set) { - error (g, _("not a Windows guest, or CurrentControlSet could not be determined")); - return NULL; - } - - return safe_strdup (g, fs->windows_current_control_set); -} - -char * -guestfs_impl_inspect_get_format (guestfs_h *g, const char *root) -{ - char *ret = NULL; - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - switch (fs->format) { - case OS_FORMAT_INSTALLED: ret = safe_strdup (g, "installed"); break; - case OS_FORMAT_INSTALLER: ret = safe_strdup (g, "installer"); break; - case OS_FORMAT_UNKNOWN: ret = safe_strdup (g, "unknown"); break; - } - - if (ret == NULL) - abort (); - - return ret; -} - -int -guestfs_impl_inspect_is_live (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return -1; - - return fs->is_live_disk; -} - -int -guestfs_impl_inspect_is_netinst (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return -1; - - return fs->is_netinst_disk; -} - -int -guestfs_impl_inspect_is_multipart (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return -1; - - return fs->is_multipart_disk; -} - -char ** -guestfs_impl_inspect_get_mountpoints (guestfs_h *g, const char *root) -{ - char **ret; - size_t i, count, nr; - struct inspect_fs *fs; - - fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - -#define CRITERION(fs, i) fs->fstab[i].mountpoint[0] == '/' - - nr = fs->nr_fstab; - - if (nr == 0) - count = 1; - else { - count = 0; - for (i = 0; i < nr; ++i) - if (CRITERION (fs, i)) - count++; - } - - /* Hashtables have 2N+1 entries. */ - ret = calloc (2*count+1, sizeof (char *)); - if (ret == NULL) { - perrorf (g, "calloc"); - return NULL; - } - - /* If no fstab information (Windows) return just the root. */ - if (nr == 0) { - ret[0] = safe_strdup (g, "/"); - ret[1] = safe_strdup (g, root); - ret[2] = NULL; - return ret; - } - - count = 0; - for (i = 0; i < nr; ++i) - if (CRITERION (fs, i)) { - ret[2*count] = safe_strdup (g, fs->fstab[i].mountpoint); - ret[2*count+1] = safe_strdup (g, fs->fstab[i].mountable); - count++; - } -#undef CRITERION - - return ret; -} - -char ** -guestfs_impl_inspect_get_filesystems (guestfs_h *g, const char *root) -{ - char **ret; - size_t i, nr; - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - - if (!fs) - return NULL; - - nr = fs->nr_fstab; - ret = calloc (nr == 0 ? 2 : nr+1, sizeof (char *)); - if (ret == NULL) { - perrorf (g, "calloc"); - return NULL; - } - - /* If no fstab information (Windows) return just the root. */ - if (nr == 0) { - ret[0] = safe_strdup (g, root); - ret[1] = NULL; - return ret; - } - - for (i = 0; i < nr; ++i) - ret[i] = safe_strdup (g, fs->fstab[i].mountable); - - return ret; -} - -char ** -guestfs_impl_inspect_get_drive_mappings (guestfs_h *g, const char *root) -{ - DECLARE_STRINGSBUF (ret); - size_t i; - struct inspect_fs *fs; - - fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - if (fs->drive_mappings) { - for (i = 0; fs->drive_mappings[i] != NULL; ++i) - guestfs_int_add_string (g, &ret, fs->drive_mappings[i]); - } - - guestfs_int_end_stringsbuf (g, &ret); - return ret.argv; -} - -char * -guestfs_impl_inspect_get_package_format (guestfs_h *g, const char *root) -{ - char *ret = NULL; - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - switch (fs->package_format) { - case OS_PACKAGE_FORMAT_RPM: ret = safe_strdup (g, "rpm"); break; - case OS_PACKAGE_FORMAT_DEB: ret = safe_strdup (g, "deb"); break; - case OS_PACKAGE_FORMAT_PACMAN: ret = safe_strdup (g, "pacman"); break; - case OS_PACKAGE_FORMAT_EBUILD: ret = safe_strdup (g, "ebuild"); break; - case OS_PACKAGE_FORMAT_PISI: ret = safe_strdup (g, "pisi"); break; - case OS_PACKAGE_FORMAT_PKGSRC: ret = safe_strdup (g, "pkgsrc"); break; - case OS_PACKAGE_FORMAT_APK: ret = safe_strdup (g, "apk"); break; - case OS_PACKAGE_FORMAT_XBPS: ret = safe_strdup (g, "xbps"); break; - case OS_PACKAGE_FORMAT_UNKNOWN: - ret = safe_strdup (g, "unknown"); - break; - } - - if (ret == NULL) - abort (); - - return ret; -} - -char * -guestfs_impl_inspect_get_package_management (guestfs_h *g, const char *root) -{ - char *ret = NULL; - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - switch (fs->package_management) { - case OS_PACKAGE_MANAGEMENT_APK: ret = safe_strdup (g, "apk"); break; - case OS_PACKAGE_MANAGEMENT_APT: ret = safe_strdup (g, "apt"); break; - case OS_PACKAGE_MANAGEMENT_DNF: ret = safe_strdup (g, "dnf"); break; - case OS_PACKAGE_MANAGEMENT_PACMAN: ret = safe_strdup (g, "pacman"); break; - case OS_PACKAGE_MANAGEMENT_PISI: ret = safe_strdup (g, "pisi"); break; - case OS_PACKAGE_MANAGEMENT_PORTAGE: ret = safe_strdup (g, "portage"); break; - case OS_PACKAGE_MANAGEMENT_UP2DATE: ret = safe_strdup (g, "up2date"); break; - case OS_PACKAGE_MANAGEMENT_URPMI: ret = safe_strdup (g, "urpmi"); break; - case OS_PACKAGE_MANAGEMENT_XBPS: ret = safe_strdup (g, "xbps"); break; - case OS_PACKAGE_MANAGEMENT_YUM: ret = safe_strdup (g, "yum"); break; - case OS_PACKAGE_MANAGEMENT_ZYPPER: ret = safe_strdup (g, "zypper"); break; - case OS_PACKAGE_MANAGEMENT_UNKNOWN: - ret = safe_strdup (g, "unknown"); - break; - } - - if (ret == NULL) - abort (); - - return ret; -} - -char * -guestfs_impl_inspect_get_hostname (guestfs_h *g, const char *root) -{ - struct inspect_fs *fs = guestfs_int_search_for_root (g, root); - if (!fs) - return NULL; - - return safe_strdup (g, fs->hostname ? : "unknown"); -} - -void -guestfs_int_free_inspect_info (guestfs_h *g) -{ - size_t i, j; - - for (i = 0; i < g->nr_fses; ++i) { - free (g->fses[i].mountable); - free (g->fses[i].product_name); - free (g->fses[i].product_variant); - free (g->fses[i].arch); - free (g->fses[i].hostname); - free (g->fses[i].windows_systemroot); - free (g->fses[i].windows_software_hive); - free (g->fses[i].windows_system_hive); - free (g->fses[i].windows_current_control_set); - for (j = 0; j < g->fses[i].nr_fstab; ++j) { - free (g->fses[i].fstab[j].mountable); - free (g->fses[i].fstab[j].mountpoint); - } - free (g->fses[i].fstab); - if (g->fses[i].drive_mappings) - guestfs_int_free_string_list (g->fses[i].drive_mappings); - } - free (g->fses); - g->nr_fses = 0; - g->fses = NULL; -} - /** * Download a guest file to a local temporary file. The file is * cached in the temporary directory, and is not downloaded again. @@ -740,7 +58,7 @@ guestfs_int_free_inspect_info (guestfs_h *g) * handle the case of multiple roots. */ char * -guestfs_int_download_to_tmp (guestfs_h *g, struct inspect_fs *fs, +guestfs_int_download_to_tmp (guestfs_h *g, const char *filename, const char *basename, uint64_t max_size) { @@ -749,10 +67,7 @@ guestfs_int_download_to_tmp (guestfs_h *g, struct inspect_fs *fs, char devfd[32]; int64_t size; - /* Make the basename unique by prefixing it with the fs number. - * This also ensures there is one cache per filesystem. - */ - if (asprintf (&r, "%s/%td-%s", g->tmpdir, fs - g->fses, basename) == -1) { + if (asprintf (&r, "%s/%s", g->tmpdir, basename) == -1) { perrorf (g, "asprintf"); return NULL; } @@ -798,46 +113,3 @@ guestfs_int_download_to_tmp (guestfs_h *g, struct inspect_fs *fs, free (r); return NULL; } - -struct inspect_fs * -guestfs_int_search_for_root (guestfs_h *g, const char *root) -{ - size_t i; - - if (g->nr_fses == 0) { - error (g, _("no inspection data: call guestfs_inspect_os first")); - return NULL; - } - - for (i = 0; i < g->nr_fses; ++i) { - struct inspect_fs *fs = &g->fses[i]; - if (fs->role == OS_ROLE_ROOT && STREQ (root, fs->mountable)) - return fs; - } - - error (g, _("%s: root device not found: only call this function with a root device previously returned by guestfs_inspect_os"), - root); - return NULL; -} - -int -guestfs_int_is_partition (guestfs_h *g, const char *partition) -{ - CLEANUP_FREE char *device = NULL; - - guestfs_push_error_handler (g, NULL, NULL); - - if ((device = guestfs_part_to_dev (g, partition)) == NULL) { - guestfs_pop_error_handler (g); - return 0; - } - - if (guestfs_device_index (g, device) == -1) { - guestfs_pop_error_handler (g); - return 0; - } - - guestfs_pop_error_handler (g); - - return 1; -} -- 2.13.0
Pino Toscano
2017-Jun-16 13:24 UTC
Re: [Libguestfs] [PATCH v6 04/41] mllib: Split ‘Common_utils’ into ‘Std_utils’ + ‘Common_utils’.
On Thursday, 15 June 2017 19:05:54 CEST Richard W.M. Jones wrote:> The new module ‘Std_utils’ contains only functions which are pure > OCaml and depend only on the OCaml stdlib. Therefore these functions > may be used by the generator.Hm can we please use a better name than Std_utils? Otherwise there's a bit of confusion between two generic names such as Std_utils and Common_utils. -- Pino Toscano
Pino Toscano
2017-Jun-16 13:24 UTC
Re: [Libguestfs] [PATCH v6 05/41] utils: Split out cleanups into common/cleanups.
On Thursday, 15 June 2017 19:05:55 CEST Richard W.M. Jones wrote:> Those cleanups which only depend on libc, gnulib or libxml2 are split > out into a separate common/cleanups directory. > ---IMHO a single cleanups.c source should be enough, otherwise it's overly split... -- Pino Toscano
Pino Toscano
2017-Jun-16 13:38 UTC
Re: [Libguestfs] [PATCH v6 10/41] mllib, v2v: Split out OCaml utils bindings ‘common/mlutils’.
On Thursday, 15 June 2017 19:06:00 CEST Richard W.M. Jones wrote:> Create a module ‘C_utils’ containing functions like ‘drive_name’ and > ‘shell_unquote’ which come from the C utilities. > > The new directory ‘common/mlutils’ also contains the ‘Unix_utils’ > wrappers around POSIX functions missing from the OCaml stdlib. > ---I fear we are spreading the code among too many helper libraries... Why not just add these small bindings to Common_utils directly? -- Pino Toscano
Pino Toscano
2017-Jun-16 13:42 UTC
Re: [Libguestfs] [PATCH v6 11/41] utils: Rename ‘guestfs-internal-frontend.h’ to ‘utils.h’.
NACK to utils.h -- in the past I've seen a couple of libraries installing public includes as utils.h. They have been fixed, but I'd like to avoid conflicts between an installed header and a project header. -- Pino Toscano
Seemingly Similar Threads
- [PATCH v2 2/7] lib: Move utilities to new directory common/utils.
- [PATCH v7 10/13] utils: Split out structs cleanups and printing into common/structs.
- [PATCH 3/3] tools: build common fish sources in a static library
- [PATCH 3/9] build: check for libintl, and use it
- Re: [PATCH 3/9] build: check for libintl, and use it