diff --git a/.gitignore b/.gitignore index f27dc9af4..5d643a318 100644 --- a/.gitignore +++ b/.gitignore @@ -174,6 +174,8 @@ Makefile.in /gobject/guestfs-gobject*.c /gobject/guestfs-gobject*.h /golang/bindtests.go +/golang/examples/guestfs-golang.3 +/golang/examples/stamp-guestfs-golang.pod /golang/pkg /guestfs-release-notes.1 /guestfsd-in-wine.log @@ -188,6 +190,7 @@ Makefile.in /html/guestfs-erlang.3.html /html/guestfs-examples.3.html /html/guestfs-faq.1.html +/html/guestfs-golang.3.html /html/guestfs-java.3.html /html/guestfs-lua.3.html /html/guestfs-ocaml.3.html diff --git a/Makefile.am b/Makefile.am index 2d8a45011..9109ae64b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -105,7 +105,7 @@ if HAVE_GOBJECT SUBDIRS += gobject endif if HAVE_GOLANG -SUBDIRS += golang +SUBDIRS += golang golang/examples endif # Unconditional because nothing is built yet. @@ -184,6 +184,7 @@ HTMLFILES = \ html/guestfs-examples.3.html \ html/guestfs-erlang.3.html \ html/guestfs-faq.1.html \ + html/guestfs-golang.3.html \ html/guestfs-java.3.html \ html/guestfs-lua.3.html \ html/guestfs-ocaml.3.html \ diff --git a/configure.ac b/configure.ac index 0115ca656..925b4f7c2 100644 --- a/configure.ac +++ b/configure.ac @@ -1617,6 +1617,7 @@ AC_CONFIG_FILES([Makefile gobject/docs/Makefile gobject/docs/guestfs-docs.sgml golang/Makefile + golang/examples/Makefile haskell/Makefile inspector/Makefile java/Makefile diff --git a/erlang/examples/guestfs-erlang.pod b/erlang/examples/guestfs-erlang.pod index 0134a4cce..ea5ba3faa 100644 --- a/erlang/examples/guestfs-erlang.pod +++ b/erlang/examples/guestfs-erlang.pod @@ -98,6 +98,7 @@ C is the name of the unknown argument. L, L, +L, L, L, L, diff --git a/examples/guestfs-examples.pod b/examples/guestfs-examples.pod index 410391dae..f8c51f7a8 100644 --- a/examples/guestfs-examples.pod +++ b/examples/guestfs-examples.pod @@ -58,6 +58,7 @@ libguestfs, you also need to read L. L, L, +L, L, L, L, diff --git a/examples/guestfs-recipes.pod b/examples/guestfs-recipes.pod index 04b311074..37e7bd4e6 100644 --- a/examples/guestfs-recipes.pod +++ b/examples/guestfs-recipes.pod @@ -547,6 +547,7 @@ L, L, L, L, +L, L, L, L, diff --git a/golang/examples/LICENSE b/golang/examples/LICENSE new file mode 100644 index 000000000..1b95b54a7 --- /dev/null +++ b/golang/examples/LICENSE @@ -0,0 +1,2 @@ +All the examples in the golang/examples/ subdirectory may be freely +copied without any restrictions. diff --git a/golang/examples/Makefile.am b/golang/examples/Makefile.am new file mode 100644 index 000000000..b5093ef28 --- /dev/null +++ b/golang/examples/Makefile.am @@ -0,0 +1,42 @@ +# libguestfs Go examples +# Copyright (C) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +include $(top_srcdir)/subdir-rules.mk + +EXTRA_DIST = \ + LICENSE \ + create-disk.go \ + inspect-vm.go \ + guestfs-golang.pod + +CLEANFILES = stamp-guestfs-golang.pod + +man_MANS = guestfs-golang.3 +noinst_DATA = $(top_builddir)/html/guestfs-golang.3.html + +guestfs-golang.3 $(top_builddir)/html/guestfs-golang.3.html: stamp-guestfs-golang.pod + +stamp-guestfs-golang.pod: guestfs-golang.pod create-disk.go inspect-vm.go + $(PODWRAPPER) \ + --section 3 \ + --man guestfs-golang.3 \ + --html $(top_builddir)/html/guestfs-golang.3.html \ + --verbatim $(srcdir)/create-disk.go:@EXAMPLE1@ \ + --verbatim $(srcdir)/inspect-vm.go:@EXAMPLE2@ \ + --license examples \ + $< + touch $@ diff --git a/golang/examples/create-disk.go b/golang/examples/create-disk.go new file mode 100644 index 000000000..336fd6ced --- /dev/null +++ b/golang/examples/create-disk.go @@ -0,0 +1,122 @@ +/* Example showing how to create a disk image. */ + +package main + +import ( + "fmt" + "os" + "libguestfs.org/guestfs" +) + +func main() { + output := "disk.img" + + g, errno := guestfs.Create () + if errno != nil { + panic (errno) + } + defer g.Close () + + /* Create a raw-format sparse disk image, 512 MB in size. */ + f, ferr := os.Create (output) + if ferr != nil { + panic (fmt.Sprintf ("could not create file: %s: %s", + output, ferr)) + } + defer f.Close () + + if ferr := f.Truncate (512 * 1024 * 1024); ferr != nil { + panic (fmt.Sprintf ("could not truncate file: %s", ferr)) + } + + /* Set the trace flag so that we can see each libguestfs call. */ + g.Set_trace (true) + + /* Attach the disk image to libguestfs. */ + optargs := guestfs.OptargsAdd_drive{ + Format_is_set: true, + Format: "raw", + Readonly_is_set: true, + Readonly: false, + } + if err := g.Add_drive (output, &optargs); err != nil { + panic (err) + } + + /* Run the libguestfs back-end. */ + if err := g.Launch (); err != nil { + panic (err) + } + + /* Get the list of devices. Because we only added one drive + * above, we expect that this list should contain a single + * element. + */ + devices, err := g.List_devices () + if err != nil { + panic (err) + } + if len(devices) != 1 { + panic ("expected a single device from list-devices") + } + + /* Partition the disk as one single MBR partition. */ + err = g.Part_disk (devices[0], "mbr") + if err != nil { + panic (err) + } + + /* Get the list of partitions. We expect a single element, which + * is the partition we have just created. + */ + partitions, err := g.List_partitions () + if err != nil { + panic (err) + } + if len(partitions) != 1 { + panic ("expected a single partition from list-partitions") + } + + /* Create a filesystem on the partition. */ + err = g.Mkfs ("ext4", partitions[0], nil) + if err != nil { + panic (err) + } + + /* Now mount the filesystem so that we can add files. */ + err = g.Mount (partitions[0], "/") + if err != nil { + panic (err) + } + + /* Create some files and directories. */ + err = g.Touch ("/empty") + if err != nil { + panic (err) + } + message := []byte("Hello, world\n") + err = g.Write ("/hello", message) + if err != nil { + panic (err) + } + err = g.Mkdir ("/foo") + if err != nil { + panic (err) + } + + /* This one uploads the local file /etc/resolv.conf into + * the disk image. + */ + err = g.Upload ("/etc/resolv.conf", "/foo/resolv.conf") + if err != nil { + panic (err) + } + + /* Because we wrote to the disk and we want to detect write + * errors, call g:shutdown. You don't need to do this: + * g.Close will do it implicitly. + */ + if err = g.Shutdown (); err != nil { + panic (fmt.Sprintf ("write to disk failed: %s", err)) + } +} diff --git a/golang/examples/guestfs-golang.pod b/golang/examples/guestfs-golang.pod new file mode 100644 index 000000000..d63486b92 --- /dev/null +++ b/golang/examples/guestfs-golang.pod @@ -0,0 +1,99 @@ +=encoding utf8 + +=head1 NAME + +guestfs-golang - How to use libguestfs from Go + +=head1 SYNOPSIS + + import "libguestfs.org/guestfs" + + g, errno := guestfs.Create () + if errno != nil { + panic (fmt.Sprintf ("could not create handle: %s", errno)) + } + defer g.Close () + if err := g.Add_drive ("test.img"); err != nil { + panic (err) + } + if err := g.Launch (); err != nil { + panic (err) + } + if err := g.Shutdown (); err != nil { + panic (err) + } + +=head1 DESCRIPTION + +This manual page documents how to call libguestfs from the Go +programming language. This page just documents the differences from +the C API and gives some examples. If you are not familiar with using +libguestfs, you also need to read L. + +=head2 IMPORTING THE MODULE + +The module is called C. The full package name to import is +C. + +=head2 CREATING AND CLOSING THE HANDLE + +Use either C or C to create the +handle. The handle is closed implicitly if it is garbage collected. +However it is probably a good idea to close it explicitly, either by +calling S> or by deferring the same. + +=head2 ERRORS + +C and C return a simple +C<*error>, which is really just a C C wrapped up in the +appropriate golang struct. + +All other calls return a C<*GuestfsError> which (if non-nil) is a +richer struct that contains the error string from libguestfs, the +errno (if available) and the operation which failed. This can also be +converted to a string for display. + +=head2 LIMITATIONS + +=over 4 + +=item * + +No support for events (see L). + +=item * + +UUIDs are not returned in structures. + +=back + +=head1 EXAMPLE 1: CREATE A DISK IMAGE + +@EXAMPLE1@ + +=head1 EXAMPLE 2: INSPECT A VIRTUAL MACHINE DISK IMAGE + +@EXAMPLE2@ + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L, +L, +L, +L, +L, +L, +L. + +=head1 AUTHORS + +Richard W.M. Jones (C) + +=head1 COPYRIGHT + +Copyright (C) 2013 Red Hat Inc. diff --git a/golang/examples/inspect-vm.go b/golang/examples/inspect-vm.go new file mode 100644 index 000000000..74a27a9c5 --- /dev/null +++ b/golang/examples/inspect-vm.go @@ -0,0 +1,65 @@ +/* Example showing how to inspect a virtual machine disk. */ + +package main + +import ( + "fmt" + "os" + "libguestfs.org/guestfs" +) + +func main() { + if len(os.Args) < 2 { + panic ("usage: inspect-vm disk.img") + } + disk := os.Args[1] + + g, errno := guestfs.Create () + if errno != nil { + panic (fmt.Sprintf ("could not create handle: %s", errno)) + } + + /* Attach the disk image read-only to libguestfs. */ + optargs := guestfs.OptargsAdd_drive{ + Format_is_set: true, + Format: "raw", + Readonly_is_set: true, + Readonly: true, + } + if err := g.Add_drive (disk, &optargs); err != nil { + panic (err) + } + + /* Run the libguestfs back-end. */ + if err := g.Launch (); err != nil { + panic (err) + } + + /* Ask libguestfs to inspect for operating systems. */ + roots, err := g.Inspect_os () + if err != nil { + panic (err) + } + if len(roots) == 0 { + panic ("inspect-vm: no operating systems found") + } + + for _, root := range roots { + fmt.Printf ("Root device: %s\n", root) + + /* Print basic information about the operating system. */ + s, _ := g.Inspect_get_product_name (root) + fmt.Printf (" Product name: %s\n", s) + major, _ := g.Inspect_get_major_version (root) + minor, _ := g.Inspect_get_minor_version (root) + fmt.Printf (" Version: %d.%d\n", major, minor) + s, _ = g.Inspect_get_type (root) + fmt.Printf (" Type: %s\n", s) + s, _ = g.Inspect_get_distro (root) + fmt.Printf (" Distro: %s\n", s) + + /* XXX Incomplete example. Sorting the keys by length + * is unnecessarily hard in golang. + */ + } +} diff --git a/java/examples/guestfs-java.pod b/java/examples/guestfs-java.pod index c777e054a..ae24820dc 100644 --- a/java/examples/guestfs-java.pod +++ b/java/examples/guestfs-java.pod @@ -83,6 +83,7 @@ The output looks similar to this: L, L, L, +L, L, L, L, diff --git a/lua/examples/guestfs-lua.pod b/lua/examples/guestfs-lua.pod index 4c72899b4..cb93db6e8 100644 --- a/lua/examples/guestfs-lua.pod +++ b/lua/examples/guestfs-lua.pod @@ -140,6 +140,7 @@ returned when you registered the callback: L, L, L, +L, L, L, L, diff --git a/ocaml/examples/guestfs-ocaml.pod b/ocaml/examples/guestfs-ocaml.pod index f91d6d2c7..86becfd87 100644 --- a/ocaml/examples/guestfs-ocaml.pod +++ b/ocaml/examples/guestfs-ocaml.pod @@ -80,6 +80,7 @@ function that you called. L, L, L, +L, L, L, L, diff --git a/perl/examples/guestfs-perl.pod b/perl/examples/guestfs-perl.pod index 95a1b604e..0571bcd08 100644 --- a/perl/examples/guestfs-perl.pod +++ b/perl/examples/guestfs-perl.pod @@ -43,6 +43,7 @@ L, L, L, L, +L, L, L, L, diff --git a/po-docs/ja/Makefile.am b/po-docs/ja/Makefile.am index 0b1b10f8a..0ec661989 100644 --- a/po-docs/ja/Makefile.am +++ b/po-docs/ja/Makefile.am @@ -31,6 +31,7 @@ MANPAGES = \ guestfs-erlang.3 \ guestfs-examples.3 \ guestfs-faq.1 \ + guestfs-golang.3 \ guestfs-java.3 \ guestfs-lua.3 \ guestfs-ocaml.3 \ diff --git a/po-docs/podfiles b/po-docs/podfiles index 3347554d4..21d18e441 100644 --- a/po-docs/podfiles +++ b/po-docs/podfiles @@ -23,6 +23,7 @@ ../format/virt-format.pod ../fuse/guestmount.pod ../fuse/guestunmount.pod +../golang/examples/guestfs-golang.pod ../guestfs-release-notes.pod ../inspector/virt-inspector.pod ../java/examples/guestfs-java.pod diff --git a/po-docs/uk/Makefile.am b/po-docs/uk/Makefile.am index 0b1b10f8a..0ec661989 100644 --- a/po-docs/uk/Makefile.am +++ b/po-docs/uk/Makefile.am @@ -31,6 +31,7 @@ MANPAGES = \ guestfs-erlang.3 \ guestfs-examples.3 \ guestfs-faq.1 \ + guestfs-golang.3 \ guestfs-java.3 \ guestfs-lua.3 \ guestfs-ocaml.3 \ diff --git a/python/examples/guestfs-python.pod b/python/examples/guestfs-python.pod index 56be347fc..710a673be 100644 --- a/python/examples/guestfs-python.pod +++ b/python/examples/guestfs-python.pod @@ -55,6 +55,7 @@ Type: L, L, L, +L, L, L, L, diff --git a/ruby/examples/guestfs-ruby.pod b/ruby/examples/guestfs-ruby.pod index 82b1325ad..df7f99aa0 100644 --- a/ruby/examples/guestfs-ruby.pod +++ b/ruby/examples/guestfs-ruby.pod @@ -38,6 +38,7 @@ string). L, L, L, +L, L, L, L, diff --git a/src/guestfs.pod b/src/guestfs.pod index fed4d57da..33d3742bd 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -1092,20 +1092,7 @@ available. See the C directory in the source. =item B -Experimental Go bindings are available. See the C directory -in the source. These are incomplete: - -=over 4 - -=item * - -No support for L. - -=item * - -UUIDs cannot be returned in structures. - -=back +See . =item B @@ -4507,6 +4494,7 @@ See L, L. L, L, +L, L, L, L,