From 1bc540ffd0521140600aa319cced7feb42ec5c0d Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Wed, 10 Jun 2009 15:45:00 +0100 Subject: Prepare for 1.0.44. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 2ba0dd99..925e0259 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.43]) +AC_INIT([libguestfs],[1.0.44]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) -- cgit From 8a58d6b6252d07a2d157e892bddfdbb510616705 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Thu, 11 Jun 2009 16:57:29 +0100 Subject: More TODO-list suggestions and a summary of PPC situation. --- TODO | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/TODO b/TODO index d0e93207..3c123d49 100644 --- a/TODO +++ b/TODO @@ -107,3 +107,25 @@ This is mainly useful from live CDs, ie. virt-p2v. Should we bother having the daemon at all and just link the guestfsd code directly into libguestfs? + +---------------------------------------------------------------------- + +guestfs rescue shell + +Have a kernel command line option to enable a rescue shell +from guestfsd. + +---------------------------------------------------------------------- + +PPC problems: + + ppc (32 bit) works with qemu from git, however there is no serial console + + ppc64 requires extra parameters: + -M mac99 -cpu ppc64 + however it still fails: + invalid/unsupported opcode: 01 - 01 - 1a (06301e83) 00000000018c2738 1 + invalid bits: 00400000 for opcode: 0b - 19 - 15 (2d746572) 0000000000009230 + + no serial console in ppc or ppc64 because no one can tell us what + console=ttyXX option to use -- cgit From 6a623e37e191385c0d9285e3ec1136b2a02e6156 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Thu, 11 Jun 2009 16:57:59 +0100 Subject: Catching hanging qemu in tests (RHBZ#505329). --- capitests/tests.c | 7 +++++++ src/generator.ml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/capitests/tests.c b/capitests/tests.c index 536da7f2..0e42cb40 100644 --- a/capitests/tests.c +++ b/capitests/tests.c @@ -15753,11 +15753,18 @@ int main (int argc, char *argv[]) printf ("guestfs_launch FAILED\n"); exit (1); } + + /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */ + alarm (600); + if (guestfs_wait_ready (g) == -1) { printf ("guestfs_wait_ready FAILED\n"); exit (1); } + /* Cancel previous alarm. */ + alarm (0); + nr_tests = 146; test_num++; diff --git a/src/generator.ml b/src/generator.ml index b3f27cd7..0a0f9b17 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -3980,11 +3980,18 @@ int main (int argc, char *argv[]) printf (\"guestfs_launch FAILED\\n\"); exit (1); } + + /* Set a timeout in case qemu hangs during launch (RHBZ#505329). */ + alarm (600); + if (guestfs_wait_ready (g) == -1) { printf (\"guestfs_wait_ready FAILED\\n\"); exit (1); } + /* Cancel previous alarm. */ + alarm (0); + nr_tests = %d; " (500 * 1024 * 1024) (50 * 1024 * 1024) (10 * 1024 * 1024) nr_tests; -- cgit From f6018d198782c1e9ed062ba60dbd0a590c3b7d7b Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Thu, 11 Jun 2009 17:27:18 +0100 Subject: Add guestfs_rescue=1 appliance option to start a rescue shell. --- TODO | 7 ------- appliance/make-initramfs.sh.in | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index 3c123d49..1e265c22 100644 --- a/TODO +++ b/TODO @@ -110,13 +110,6 @@ code directly into libguestfs? ---------------------------------------------------------------------- -guestfs rescue shell - -Have a kernel command line option to enable a rescue shell -from guestfsd. - ----------------------------------------------------------------------- - PPC problems: ppc (32 bit) works with qemu from git, however there is no serial console diff --git a/appliance/make-initramfs.sh.in b/appliance/make-initramfs.sh.in index b1d3c396..b39b13d6 100755 --- a/appliance/make-initramfs.sh.in +++ b/appliance/make-initramfs.sh.in @@ -156,6 +156,9 @@ modprobe dm_mod ||: /sbin/route add default gw 10.0.2.2 lvm vgscan --ignorelockingfailure lvm vgchange -ay --ignorelockingfailure +if grep -sq guestfs_rescue=1 /proc/cmdline; then + bash -i +fi exec guestfsd -f __EOF__ -- cgit From bf4eda4d89468b232070dbe0161848acaac1c04a Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Fri, 12 Jun 2009 13:39:32 +0100 Subject: Prepare for 1.0.45 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 925e0259..3e613989 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.44]) +AC_INIT([libguestfs],[1.0.45]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) -- cgit From 2e25c4255746b144932f84b7b6671d7d03f52278 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 13 Jun 2009 10:48:16 +0100 Subject: Rename (make|update)-initramfs.sh.in -> (make|update).sh.in --- .gitignore | 5 +- appliance/Makefile.am | 10 +-- appliance/make-initramfs.sh.in | 179 --------------------------------------- appliance/make.sh.in | 179 +++++++++++++++++++++++++++++++++++++++ appliance/update-initramfs.sh.in | 36 -------- appliance/update.sh.in | 36 ++++++++ configure.ac | 4 +- 7 files changed, 224 insertions(+), 225 deletions(-) delete mode 100755 appliance/make-initramfs.sh.in create mode 100755 appliance/make.sh.in delete mode 100755 appliance/update-initramfs.sh.in create mode 100755 appliance/update.sh.in diff --git a/.gitignore b/.gitignore index 296db62f..b64c3778 100644 --- a/.gitignore +++ b/.gitignore @@ -21,9 +21,8 @@ Makefile.in Makefile aclocal.m4 appliance/initramfs.*.img -appliance/make-initramfs.sh -appliance/make-initramfs.sh.in.old -appliance/update-initramfs.sh +appliance/make.sh +appliance/update.sh appliance/vmlinuz.* autom4te.cache capitests/test-command diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 066becbb..1886e3e1 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -16,7 +16,7 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. EXTRA_DIST = \ - make-initramfs.sh update-initramfs.sh + make.sh update.sh # Build the root filesystem (appliance). # Currently this is arch-dependent, so it seems like putting it in @@ -32,17 +32,17 @@ fs_DATA = $(INITRAMFSIMG) $(VMLINUZ) $(INITRAMFSIMG) $(VMLINUZ): $(top_builddir)/initramfs/fakeroot.log -$(top_builddir)/initramfs/fakeroot.log: make-initramfs.sh.in +$(top_builddir)/initramfs/fakeroot.log: make.sh -mv $(INITRAMFSIMG) $(INITRAMFSIMG).bak -mv $(VMLINUZ) $(VMLINUZ).bak - if ! bash make-initramfs.sh; then rm -f $@; exit 1; fi + if ! bash make.sh; then rm -f $@; exit 1; fi $(INITRAMFSIMG): $(top_builddir)/initramfs/fakeroot.log $(top_builddir)/daemon/guestfsd rm -f $@ - bash update-initramfs.sh + bash update.sh touch $@ -make-initramfs.sh: make-initramfs.sh.in +make.sh: make.sh.in cd .. && ./config.status appliance/$@ # Test-boot the image. diff --git a/appliance/make-initramfs.sh.in b/appliance/make-initramfs.sh.in deleted file mode 100755 index b39b13d6..00000000 --- a/appliance/make-initramfs.sh.in +++ /dev/null @@ -1,179 +0,0 @@ -#!/bin/bash - -# @configure_input@ -# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -# This is called from the Makefile to build the initramfs. - -unset CDPATH - -set -e - -cd @top_builddir@ - -modules=" --i augeas-libs --i bash --i binutils --i coreutils --i dosfstools --i file --i grub --i iputils --i kernel --i lvm2 --i MAKEDEV --i module-init-tools --i net-tools --i ntfs-3g --i ntfsprogs --i procps --i strace --i util-linux-ng --i zerofree -" - -# Decide on names for the final output. These have to match Makefile.am. -output=appliance/initramfs.@REPO@.@host_cpu@.img -koutput=appliance/vmlinuz.@REPO@.@host_cpu@ -rm -f $output -rm -f $koutput - -# Create the basic initramfs. -@FEBOOTSTRAP@ $modules -u @UPDATES@ @REPO@ initramfs @MIRROR@ - -# /sysroot is where the guest root filesystem will be mounted. -@FEBOOTSTRAP_RUN@ initramfs -- mkdir -p --mode=0777 /sysroot - -# Create /tmp if it is missing. -@FEBOOTSTRAP_RUN@ initramfs -- mkdir -p --mode=0777 /tmp - -# Nuke some stuff. The kernel pulls mkinitrd and plymouth which pulls in -# all of Python. Sheez. -(cd initramfs && find -name '*plymouth*' -print0) | - xargs -0 @FEBOOTSTRAP_RUN@ initramfs -- rm -rf -(cd initramfs && find -name '*python*' -print0) | - xargs -0 @FEBOOTSTRAP_RUN@ initramfs -- rm -rf - -# In Fedora >= 11, it pulls in all of Perl from somewhere. Nuke from orbit. -@FEBOOTSTRAP_RUN@ initramfs -- rm -rf /usr/lib/perl5 /usr/lib64/perl5 - -# Anaconda? JPEG images? -@FEBOOTSTRAP_RUN@ initramfs -- rm -rf /usr/lib/anaconda-runtime - -# Modules take up nearly half of the image. It's a rough guess that -# we don't need many drivers (which take up most of the space). -(cd initramfs && find lib/modules/*/kernel \ - -name '*.ko' \ - -a ! -name 'virtio.ko' \ - -a ! -name 'virtio_net.ko' \ - -a ! -name 'virtio_pci.ko' \ - -a ! -name 'virtio_ring.ko' \ - -a ! -name 'ext2.ko' \ - -a ! -name 'ext3.ko' \ - -a ! -name 'ext4*.ko' \ - -a ! -name 'crc16.ko' \ - -a ! -name 'jbd.ko' \ - -a ! -name 'jbd2.ko' \ - -a ! -name 'fuse.ko' \ - -a ! -name 'vfat.ko' \ - -a ! -name 'fat.ko' \ - -a ! -name 'udf.ko' \ - -a ! -name 'crc_itu_t.ko' \ - -a ! -name 'nls_utf8.ko' \ - -a ! -name 'dm-*.ko' \ - -a ! -name 'cramfs.ko' \ - -a ! -name 'squashfs.ko' \ - -a ! -name 'hfsplus.ko' \ - -a ! -name 'ufs.ko' \ - -a ! -name 'exportfs.ko' \ - -a ! -name 'xfs.ko' \ - -a -print0 ) | - xargs -0 @FEBOOTSTRAP_RUN@ initramfs -- rm - -# Pull the kernel out into the current directory. We don't want it in -# the initramfs image. -cp initramfs/boot/vmlinuz* $koutput -@FEBOOTSTRAP_RUN@ initramfs -- rm -rf boot - -# Minimize the image. -@FEBOOTSTRAP_MINIMIZE@ initramfs - -# Add some missing configuration files. -if [ ! -f initramfs/etc/hosts ]; then - cat > hosts.new <<'__EOF__' -127.0.0.1 guestfs localhost.localdomain localhost -::1 localhost6.localdomain6 localhost6 -__EOF__ - @FEBOOTSTRAP_INSTALL@ initramfs hosts.new /etc/hosts 0644 root.root - rm hosts.new -fi - -if [ ! -f initramfs/etc/fstab ]; then - @FEBOOTSTRAP_RUN@ initramfs -- touch /etc/fstab -fi - -echo nameserver 10.0.2.3 > resolv.conf.new -@FEBOOTSTRAP_INSTALL@ initramfs resolv.conf.new /etc/resolv.conf 0644 root.root -rm resolv.conf.new - -# Create the init script. -cat > init.new <<'__EOF__' -#!/bin/sh -echo Starting /init script ... -PATH=/sbin:/usr/sbin:$PATH -mount -t tmpfs none /dev -mkdir /dev/pts /dev/shm /dev/mapper -MAKEDEV mem null port zero core full ram tty console fd \ - hda hdb hdc hdd sda sdb sdc sdd loop sd -mknod /dev/ptmx c 5 2; chmod 0666 /dev/ptmx -mknod /dev/random c 1 8; chmod 0666 /dev/random -mknod /dev/urandom c 1 9; chmod 0444 /dev/urandom -mount -t proc /proc /proc -mount -t sysfs /sys /sys -mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts -ln -sf /proc/self/fd/0 /dev/stdin -ln -sf /proc/self/fd/1 /dev/stdout -ln -sf /proc/self/fd/2 /dev/stderr -modprobe virtio_pci -modprobe virtio_net -modprobe dm_mod ||: -/sbin/ifconfig lo 127.0.0.1 -/sbin/ifconfig eth0 10.0.2.10 -/sbin/route add default gw 10.0.2.2 -lvm vgscan --ignorelockingfailure -lvm vgchange -ay --ignorelockingfailure -if grep -sq guestfs_rescue=1 /proc/cmdline; then - bash -i -fi -exec guestfsd -f -__EOF__ - -@FEBOOTSTRAP_INSTALL@ initramfs init.new /init 0755 root.root -rm init.new - -# Just in case the kernel isn't looking for /init, make /sbin/init -# be our script, not the real init. -#@FEBOOTSTRAP_RUN@ initramfs -- ln -f /init /sbin/init - -# Copy the daemon into the filesystem. -@FEBOOTSTRAP_INSTALL@ initramfs daemon/guestfsd /sbin/guestfsd 0755 root.root - -# Generate final image. -@FEBOOTSTRAP_TO_INITRAMFS@ initramfs > $output-t -mv $output-t $output -ls -lh $output -ls -lh $koutput diff --git a/appliance/make.sh.in b/appliance/make.sh.in new file mode 100755 index 00000000..b39b13d6 --- /dev/null +++ b/appliance/make.sh.in @@ -0,0 +1,179 @@ +#!/bin/bash - +# @configure_input@ +# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +# This is called from the Makefile to build the initramfs. + +unset CDPATH + +set -e + +cd @top_builddir@ + +modules=" +-i augeas-libs +-i bash +-i binutils +-i coreutils +-i dosfstools +-i file +-i grub +-i iputils +-i kernel +-i lvm2 +-i MAKEDEV +-i module-init-tools +-i net-tools +-i ntfs-3g +-i ntfsprogs +-i procps +-i strace +-i util-linux-ng +-i zerofree +" + +# Decide on names for the final output. These have to match Makefile.am. +output=appliance/initramfs.@REPO@.@host_cpu@.img +koutput=appliance/vmlinuz.@REPO@.@host_cpu@ +rm -f $output +rm -f $koutput + +# Create the basic initramfs. +@FEBOOTSTRAP@ $modules -u @UPDATES@ @REPO@ initramfs @MIRROR@ + +# /sysroot is where the guest root filesystem will be mounted. +@FEBOOTSTRAP_RUN@ initramfs -- mkdir -p --mode=0777 /sysroot + +# Create /tmp if it is missing. +@FEBOOTSTRAP_RUN@ initramfs -- mkdir -p --mode=0777 /tmp + +# Nuke some stuff. The kernel pulls mkinitrd and plymouth which pulls in +# all of Python. Sheez. +(cd initramfs && find -name '*plymouth*' -print0) | + xargs -0 @FEBOOTSTRAP_RUN@ initramfs -- rm -rf +(cd initramfs && find -name '*python*' -print0) | + xargs -0 @FEBOOTSTRAP_RUN@ initramfs -- rm -rf + +# In Fedora >= 11, it pulls in all of Perl from somewhere. Nuke from orbit. +@FEBOOTSTRAP_RUN@ initramfs -- rm -rf /usr/lib/perl5 /usr/lib64/perl5 + +# Anaconda? JPEG images? +@FEBOOTSTRAP_RUN@ initramfs -- rm -rf /usr/lib/anaconda-runtime + +# Modules take up nearly half of the image. It's a rough guess that +# we don't need many drivers (which take up most of the space). +(cd initramfs && find lib/modules/*/kernel \ + -name '*.ko' \ + -a ! -name 'virtio.ko' \ + -a ! -name 'virtio_net.ko' \ + -a ! -name 'virtio_pci.ko' \ + -a ! -name 'virtio_ring.ko' \ + -a ! -name 'ext2.ko' \ + -a ! -name 'ext3.ko' \ + -a ! -name 'ext4*.ko' \ + -a ! -name 'crc16.ko' \ + -a ! -name 'jbd.ko' \ + -a ! -name 'jbd2.ko' \ + -a ! -name 'fuse.ko' \ + -a ! -name 'vfat.ko' \ + -a ! -name 'fat.ko' \ + -a ! -name 'udf.ko' \ + -a ! -name 'crc_itu_t.ko' \ + -a ! -name 'nls_utf8.ko' \ + -a ! -name 'dm-*.ko' \ + -a ! -name 'cramfs.ko' \ + -a ! -name 'squashfs.ko' \ + -a ! -name 'hfsplus.ko' \ + -a ! -name 'ufs.ko' \ + -a ! -name 'exportfs.ko' \ + -a ! -name 'xfs.ko' \ + -a -print0 ) | + xargs -0 @FEBOOTSTRAP_RUN@ initramfs -- rm + +# Pull the kernel out into the current directory. We don't want it in +# the initramfs image. +cp initramfs/boot/vmlinuz* $koutput +@FEBOOTSTRAP_RUN@ initramfs -- rm -rf boot + +# Minimize the image. +@FEBOOTSTRAP_MINIMIZE@ initramfs + +# Add some missing configuration files. +if [ ! -f initramfs/etc/hosts ]; then + cat > hosts.new <<'__EOF__' +127.0.0.1 guestfs localhost.localdomain localhost +::1 localhost6.localdomain6 localhost6 +__EOF__ + @FEBOOTSTRAP_INSTALL@ initramfs hosts.new /etc/hosts 0644 root.root + rm hosts.new +fi + +if [ ! -f initramfs/etc/fstab ]; then + @FEBOOTSTRAP_RUN@ initramfs -- touch /etc/fstab +fi + +echo nameserver 10.0.2.3 > resolv.conf.new +@FEBOOTSTRAP_INSTALL@ initramfs resolv.conf.new /etc/resolv.conf 0644 root.root +rm resolv.conf.new + +# Create the init script. +cat > init.new <<'__EOF__' +#!/bin/sh +echo Starting /init script ... +PATH=/sbin:/usr/sbin:$PATH +mount -t tmpfs none /dev +mkdir /dev/pts /dev/shm /dev/mapper +MAKEDEV mem null port zero core full ram tty console fd \ + hda hdb hdc hdd sda sdb sdc sdd loop sd +mknod /dev/ptmx c 5 2; chmod 0666 /dev/ptmx +mknod /dev/random c 1 8; chmod 0666 /dev/random +mknod /dev/urandom c 1 9; chmod 0444 /dev/urandom +mount -t proc /proc /proc +mount -t sysfs /sys /sys +mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts +ln -sf /proc/self/fd/0 /dev/stdin +ln -sf /proc/self/fd/1 /dev/stdout +ln -sf /proc/self/fd/2 /dev/stderr +modprobe virtio_pci +modprobe virtio_net +modprobe dm_mod ||: +/sbin/ifconfig lo 127.0.0.1 +/sbin/ifconfig eth0 10.0.2.10 +/sbin/route add default gw 10.0.2.2 +lvm vgscan --ignorelockingfailure +lvm vgchange -ay --ignorelockingfailure +if grep -sq guestfs_rescue=1 /proc/cmdline; then + bash -i +fi +exec guestfsd -f +__EOF__ + +@FEBOOTSTRAP_INSTALL@ initramfs init.new /init 0755 root.root +rm init.new + +# Just in case the kernel isn't looking for /init, make /sbin/init +# be our script, not the real init. +#@FEBOOTSTRAP_RUN@ initramfs -- ln -f /init /sbin/init + +# Copy the daemon into the filesystem. +@FEBOOTSTRAP_INSTALL@ initramfs daemon/guestfsd /sbin/guestfsd 0755 root.root + +# Generate final image. +@FEBOOTSTRAP_TO_INITRAMFS@ initramfs > $output-t +mv $output-t $output +ls -lh $output +ls -lh $koutput diff --git a/appliance/update-initramfs.sh.in b/appliance/update-initramfs.sh.in deleted file mode 100755 index 3b779f85..00000000 --- a/appliance/update-initramfs.sh.in +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# @configure_input@ -# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -# Update the daemon inside an existing initramfs. Avoids the -# timeconsuming rebuild. - -unset CDPATH - -set -e - -cd @top_builddir@ - -output=appliance/initramfs.@REPO@.@host_cpu@.img - -# Copy the daemon into the filesystem. -@FEBOOTSTRAP_INSTALL@ initramfs daemon/guestfsd /sbin/guestfsd 0755 root.root - -# Generate final image. -@FEBOOTSTRAP_TO_INITRAMFS@ initramfs > $output-t -mv $output-t $output -ls -lh $output diff --git a/appliance/update.sh.in b/appliance/update.sh.in new file mode 100755 index 00000000..3b779f85 --- /dev/null +++ b/appliance/update.sh.in @@ -0,0 +1,36 @@ +#!/bin/bash - +# @configure_input@ +# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Update the daemon inside an existing initramfs. Avoids the +# timeconsuming rebuild. + +unset CDPATH + +set -e + +cd @top_builddir@ + +output=appliance/initramfs.@REPO@.@host_cpu@.img + +# Copy the daemon into the filesystem. +@FEBOOTSTRAP_INSTALL@ initramfs daemon/guestfsd /sbin/guestfsd 0755 root.root + +# Generate final image. +@FEBOOTSTRAP_TO_INITRAMFS@ initramfs > $output-t +mv $output-t $output +ls -lh $output diff --git a/configure.ac b/configure.ac index 3e613989..f1562f49 100644 --- a/configure.ac +++ b/configure.ac @@ -452,7 +452,7 @@ AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile src/Makefile fish/Makefile po/Makefile.in examples/Makefile appliance/Makefile - appliance/make-initramfs.sh appliance/update-initramfs.sh + appliance/make.sh appliance/update.sh images/Makefile capitests/Makefile regressions/Makefile @@ -468,7 +468,7 @@ AC_CONFIG_FILES([Makefile AC_OUTPUT dnl WTF? -chmod +x appliance/make-initramfs.sh appliance/update-initramfs.sh +chmod +x appliance/make.sh appliance/update.sh dnl Produce summary. echo -- cgit From aa7676aa74f3396fc65ae6b4bf01af7efe021e7e Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 13 Jun 2009 10:59:51 +0100 Subject: make.sh calls update.sh directly. Combine the common tail of make.sh and update.sh so that make just calls update at the end directly. The effect is the same. --- appliance/make.sh.in | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/appliance/make.sh.in b/appliance/make.sh.in index b39b13d6..cab88133 100755 --- a/appliance/make.sh.in +++ b/appliance/make.sh.in @@ -169,11 +169,8 @@ rm init.new # be our script, not the real init. #@FEBOOTSTRAP_RUN@ initramfs -- ln -f /init /sbin/init -# Copy the daemon into the filesystem. -@FEBOOTSTRAP_INSTALL@ initramfs daemon/guestfsd /sbin/guestfsd 0755 root.root - -# Generate final image. -@FEBOOTSTRAP_TO_INITRAMFS@ initramfs > $output-t -mv $output-t $output -ls -lh $output ls -lh $koutput + +# Now directly run the update script to copy/update the daemon in the +# initramfs. +cd appliance && bash update.sh -- cgit From 812a2d57a8b847247796da2cd79a73aa1ab16b66 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 13 Jun 2009 11:11:03 +0100 Subject: Remove firmware from the appliance. --- appliance/make.sh.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appliance/make.sh.in b/appliance/make.sh.in index cab88133..fc42d7e7 100755 --- a/appliance/make.sh.in +++ b/appliance/make.sh.in @@ -74,6 +74,9 @@ rm -f $koutput # Anaconda? JPEG images? @FEBOOTSTRAP_RUN@ initramfs -- rm -rf /usr/lib/anaconda-runtime +# Don't need any firmware. +@FEBOOTSTRAP_RUN@ initramfs -- rm -rf /lib/firmware + # Modules take up nearly half of the image. It's a rough guess that # we don't need many drivers (which take up most of the space). (cd initramfs && find lib/modules/*/kernel \ -- cgit From 3ed322c7c8f1b7342af095ba6a8fb60a8e9a076b Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 13 Jun 2009 11:16:43 +0100 Subject: Remove /lib/kbd (keyboard maps) from the appliance. --- appliance/make.sh.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appliance/make.sh.in b/appliance/make.sh.in index fc42d7e7..9c52830b 100755 --- a/appliance/make.sh.in +++ b/appliance/make.sh.in @@ -77,6 +77,9 @@ rm -f $koutput # Don't need any firmware. @FEBOOTSTRAP_RUN@ initramfs -- rm -rf /lib/firmware +# Don't need any keyboard maps. +@FEBOOTSTRAP_RUN@ initramfs -- rm -rf /lib/kbd + # Modules take up nearly half of the image. It's a rough guess that # we don't need many drivers (which take up most of the space). (cd initramfs && find lib/modules/*/kernel \ -- cgit From 67232e4a701f51ac770a085727a665277086a5ae Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 13 Jun 2009 11:18:54 +0100 Subject: Update status of libguestfs in Debian. --- README | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README b/README index f470a58e..5d340d30 100644 --- a/README +++ b/README @@ -120,10 +120,13 @@ or build from our source RPMs. Debian ---------------------------------------------------------------------- -libguestfs should build and run on Debian. At the moment we don't -provide Debian packages, and because of the appliance it's rather -complicated to provide a package which could be accepted into the -Debian repositories. Want to help? Please contact us. +libguestfs should build and run on Debian. + +febootstrap, yum, rpm, fakeroot, fakechroot are all packaged in +Debian. + +Please see the fedora-virt mailing list for the status of libguestfs +in Debian. qemu -- cgit From 161c7cd7f9c9d1099c35db20f78d7e5a20ea6517 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 15 Jun 2009 14:45:05 +0100 Subject: Move kernel module list to a separate whitelist file. --- appliance/Makefile.am | 2 +- appliance/kmod.whitelist | 38 ++++++++++++++++++++++++++++++++++++++ appliance/make.sh.in | 45 ++++++++++++++++----------------------------- 3 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 appliance/kmod.whitelist diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 1886e3e1..5dd6521e 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -32,7 +32,7 @@ fs_DATA = $(INITRAMFSIMG) $(VMLINUZ) $(INITRAMFSIMG) $(VMLINUZ): $(top_builddir)/initramfs/fakeroot.log -$(top_builddir)/initramfs/fakeroot.log: make.sh +$(top_builddir)/initramfs/fakeroot.log: make.sh kmod.whitelist -mv $(INITRAMFSIMG) $(INITRAMFSIMG).bak -mv $(VMLINUZ) $(VMLINUZ).bak if ! bash make.sh; then rm -f $@; exit 1; fi diff --git a/appliance/kmod.whitelist b/appliance/kmod.whitelist new file mode 100644 index 00000000..4bf4cf81 --- /dev/null +++ b/appliance/kmod.whitelist @@ -0,0 +1,38 @@ +# List of kernel modules that we leave in the appliance. This has to +# include any dependencies needed by modules. + +# Any kernel module not listed here is deleted from the appliance. + +# Wildcards are permitted. + +virtio.ko +virtio_net.ko +virtio_pci.ko +virtio_ring.ko + +ext2.ko +ext3.ko +ext4*.ko + +crc16.ko +jbd.ko +jbd2.ko + +fuse.ko + +vfat.ko +fat.ko + +udf.ko +crc_itu_t.ko +nls_utf8.ko + +dm-*.ko + +cramfs.ko +squashfs.ko + +hfsplus.ko +ufs.ko +exportfs.ko +xfs.ko diff --git a/appliance/make.sh.in b/appliance/make.sh.in index 9c52830b..8f3b2127 100755 --- a/appliance/make.sh.in +++ b/appliance/make.sh.in @@ -80,35 +80,22 @@ rm -f $koutput # Don't need any keyboard maps. @FEBOOTSTRAP_RUN@ initramfs -- rm -rf /lib/kbd -# Modules take up nearly half of the image. It's a rough guess that -# we don't need many drivers (which take up most of the space). -(cd initramfs && find lib/modules/*/kernel \ - -name '*.ko' \ - -a ! -name 'virtio.ko' \ - -a ! -name 'virtio_net.ko' \ - -a ! -name 'virtio_pci.ko' \ - -a ! -name 'virtio_ring.ko' \ - -a ! -name 'ext2.ko' \ - -a ! -name 'ext3.ko' \ - -a ! -name 'ext4*.ko' \ - -a ! -name 'crc16.ko' \ - -a ! -name 'jbd.ko' \ - -a ! -name 'jbd2.ko' \ - -a ! -name 'fuse.ko' \ - -a ! -name 'vfat.ko' \ - -a ! -name 'fat.ko' \ - -a ! -name 'udf.ko' \ - -a ! -name 'crc_itu_t.ko' \ - -a ! -name 'nls_utf8.ko' \ - -a ! -name 'dm-*.ko' \ - -a ! -name 'cramfs.ko' \ - -a ! -name 'squashfs.ko' \ - -a ! -name 'hfsplus.ko' \ - -a ! -name 'ufs.ko' \ - -a ! -name 'exportfs.ko' \ - -a ! -name 'xfs.ko' \ - -a -print0 ) | - xargs -0 @FEBOOTSTRAP_RUN@ initramfs -- rm +# Kernel modules take up nearly half of the image. Only include ones +# which are on the whitelist. +grep -v '^[[:space:]]*$' < appliance/kmod.whitelist | + grep -v '^#' > kmod.whitelist.tmp +exec 5 Date: Sat, 13 Jun 2009 12:58:47 +0100 Subject: Documentation for the supermin appliance. --- README | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/README b/README index 5d340d30..92bfc075 100644 --- a/README +++ b/README @@ -171,6 +171,62 @@ On some systems, the chmod will not survive a reboot, and you will need to make edits to the udev configuration. +Supermin appliance +---------------------------------------------------------------------- + +If you configure with --enable-supermin then we will build a supermin +appliance (supermin = super-minimized). This is a very specialized +appliance which is built on-the-fly at runtime (specifically, when you +call guestfs_launch). + +The normal appliance is a self-contained Linux operating system, based +on the Fedora/RHEL/CentOS Linux distro. So it contains a complete +copy of all the libraries and programs needed, like kernel, libc, +bash, coreutils etc etc. + +The supermin appliance removes the kernel and all the executable +libraries and programs from the appliance. That just leaves a +skeleton of config files and some data files, which is obviously +massively smaller than the normal appliance. At runtime we rebuild +the appliance on-the-fly from the libraries and programs on the host +(eg. pulling in the real /lib/libc.so, the real /bin/bash etc.) + +Although this process of rebuilding the appliance each time sounds +slow, it turns out to be faster than using the prebuilt appliance. +(Most of the saving comes from not compressing the appliance - it +transpires that decompressing the appliance is the slowest part of the +whole boot sequence). On my machine, a new appliance can be built in +under a fifth of a second, and the boot time is several seconds +shorter. + +The big advantage of the supermin appliance for distributions like +Fedora is that it gets security fixes automatically from the host, so +there is no need to rebuild the whole of libguestfs for a security +update in some underlying library. + +There are several DISADVANTAGES: + +It won't work at all except in very narrow, controlled cases like the +Fedora packaging case. We control the dependencies of the libguestfs +RPM tightly to ensure that the required binaries are actually present +on the host. + +Furthermore there are certain unlikely changes in the packages on the +host which could break a supermin appliance, eg. an updated library +which depends on an additional data file. + +Also supermin appliances are subjected to changes in the host kernel +which might break compatibility with qemu -- these are, of course, +real bugs in any case. + +Lastly, supermin appliances really can't be moved between branches of +distributions (eg. built on Fedora 12 and moved to Fedora 10) because +they are not self-contained and they rely on certain libraries being +around. You shouldn't do this anyway. + +Use supermin appliances with caution. + + Notes on cross-architecture support ---------------------------------------------------------------------- -- cgit From d6ca37a95b5ed5b7e5c8e0a54fa5a5bbf0fa2075 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 13 Jun 2009 12:58:24 +0100 Subject: Add --enable-supermin option. --- configure.ac | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/configure.ac b/configure.ac index f1562f49..c83f8561 100644 --- a/configure.ac +++ b/configure.ac @@ -179,6 +179,15 @@ AC_ARG_WITH([mirror], MIRROR="$with_mirror" AC_SUBST(MIRROR) +dnl Build the supermin appliance? Please see README file before +dnl enabling this option. +AC_ARG_ENABLE([supermin], + [AS_HELP_STRING([--enable-supermin], + [enable supermin appliance (see README) @<:@default=no@:>@])], + [], + [enable_supermin=no]) +AM_CONDITIONAL([SUPERMIN],[test "x$enable_supermin" = "xyes"]) + dnl Readline. AC_ARG_WITH([readline], [AS_HELP_STRING([--with-readline], @@ -493,6 +502,7 @@ echo -n "Haskell bindings .................... " if test "x$HAVE_HASKELL" = "x"; then echo "yes"; else echo "no"; fi echo -n "virt-inspector ...................... " if test "x$HAVE_INSPECTOR" = "x"; then echo "yes"; else echo "no"; fi +echo "supermin appliance .................. $enable_supermin" echo echo "If any optional component is configured 'no' when you expected 'yes'" echo "then you should check the preceeding messages." -- cgit From eebec43a15a5de3a5b9f1281654f9cbdd44e19cf Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 15 Jun 2009 11:37:58 +0100 Subject: Check for febootstrap-to-initramfs --files option. --- configure.ac | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/configure.ac b/configure.ac index c83f8561..e73799a6 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,24 @@ AC_ARG_ENABLE([supermin], [enable_supermin=no]) AM_CONDITIONAL([SUPERMIN],[test "x$enable_supermin" = "xyes"]) +if test "x$enable_supermin" = "xyes"; then + dnl Check febootstrap-to-initramfs accepts the --files option + dnl (febootstrap >= 2.2). + AC_MSG_CHECKING([for --files support in $FEBOOTSTRAP_TO_INITRAMFS]) + out=`$FEBOOTSTRAP_TO_INITRAMFS 2>&1 ||:` + echo "febootstrap_to_initramfs test command output: $out" >&AS_MESSAGE_LOG_FD + if ! echo $out | grep -sq -- "--files" ; then + AC_MSG_RESULT([no]) + AC_MSG_FAILURE( +[febootstrap-to-initramfs does not support the --files option. + +To build the supermin appliance, you need to upgrade to the latest +version of febootstrap. +]) + fi + AC_MSG_RESULT([yes]) +fi + dnl Readline. AC_ARG_WITH([readline], [AS_HELP_STRING([--with-readline], -- cgit From 414aa67f2bcbbc5009b92f32611aa9196836736b Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 15 Jun 2009 11:50:35 +0100 Subject: Experimental implementation of the supermin appliance (passes most tests). --- .gitignore | 6 ++ appliance/Makefile.am | 69 ++++++++++++-- appliance/guestfs-supermin-helper.in | 80 ++++++++++++++++ appliance/kmod.whitelist | 38 -------- appliance/kmod.whitelist.in | 38 ++++++++ appliance/make.sh.in | 14 ++- appliance/supermin-make.sh.in | 33 +++++++ appliance/supermin-split.sh.in | 93 +++++++++++++++++++ configure.ac | 4 +- src/guestfs.c | 173 +++++++++++++++++++++++++++++------ 10 files changed, 467 insertions(+), 81 deletions(-) create mode 100755 appliance/guestfs-supermin-helper.in delete mode 100644 appliance/kmod.whitelist create mode 100644 appliance/kmod.whitelist.in create mode 100755 appliance/supermin-make.sh.in create mode 100755 appliance/supermin-split.sh.in diff --git a/.gitignore b/.gitignore index b64c3778..3e5ba214 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,14 @@ ChangeLog Makefile.in Makefile aclocal.m4 +appliance/guestfs-supermin-helper appliance/initramfs.*.img +appliance/initramfs.*.supermin.hostfiles +appliance/kmod.whitelist appliance/make.sh +appliance/supermin-make.sh +appliance/supermin-split.sh +appliance/supermin.incfiles appliance/update.sh appliance/vmlinuz.* autom4te.cache diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 5dd6521e..3362992c 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -16,20 +16,37 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. EXTRA_DIST = \ - make.sh update.sh + make.sh update.sh supermin-split.sh supermin-make.sh # Build the root filesystem (appliance). # Currently this is arch-dependent, so it seems like putting it in # $(libdir) is best. When we build cross-architecture filesystems we # should probably move them to $(datadir). - fsdir = $(libdir)/guestfs - +fs_DATA = $(APPLIANCE_FILES) + +# These are the resulting output files from the whole process: +# VMLINUZ kernel for the full appliance +# INITRAMFSIMG initramfs (ie. root fs) for the full appliance +# For details of the supermin appliance, read the README file: +# SUPERMINIMG initramfs (ie. partial root fs) for the supermin appliance +# SUPERMINFILES list of missing files (the ones we will pull out of the +# host filesystem at runtime) in the supermin appliance +APPLIANCE_FILES = $(INITRAMFSIMG) $(VMLINUZ) +if SUPERMIN +APPLIANCE_FILES += $(SUPERMINIMG) $(SUPERMINFILES) kmod.whitelist +bin_SCRIPTS = guestfs-supermin-helper +endif + +# Don't change these names - they must be the same as in '*.sh' scripts. INITRAMFSIMG = initramfs.$(REPO).$(host_cpu).img VMLINUZ = vmlinuz.$(REPO).$(host_cpu) +if SUPERMIN +SUPERMINIMG = initramfs.$(REPO).$(host_cpu).supermin.img +SUPERMINFILES = initramfs.$(REPO).$(host_cpu).supermin.hostfiles +endif -fs_DATA = $(INITRAMFSIMG) $(VMLINUZ) - +# This is for building the normal appliance: $(INITRAMFSIMG) $(VMLINUZ): $(top_builddir)/initramfs/fakeroot.log $(top_builddir)/initramfs/fakeroot.log: make.sh kmod.whitelist @@ -37,15 +54,49 @@ $(top_builddir)/initramfs/fakeroot.log: make.sh kmod.whitelist -mv $(VMLINUZ) $(VMLINUZ).bak if ! bash make.sh; then rm -f $@; exit 1; fi -$(INITRAMFSIMG): $(top_builddir)/initramfs/fakeroot.log $(top_builddir)/daemon/guestfsd +$(INITRAMFSIMG): $(top_builddir)/initramfs/fakeroot.log $(top_builddir)/daemon/guestfsd update.sh rm -f $@ bash update.sh touch $@ -make.sh: make.sh.in +kmod.whitelist: kmod.whitelist.in + grep -v '^[[:space:]]*$$' < $< | grep -v '^#' > $@ + +# This is for building the supermin appliance. It has to be enabled +# specifically with './configure --enable-supermin'. You really need +# to read the README file. + +if SUPERMIN + +# First we need to decide which files go in and out of the supermin +# appliance. This decision is made by 'supermin-split.sh'. +$(SUPERMINFILES): supermin.incfiles +supermin.incfiles: $(top_builddir)/initramfs/fakeroot.log supermin-split.sh + rm -f supermin.incfiles $(SUPERMINFILES) + bash supermin-split.sh + +# Second we need to create a supermin appliance with just the included +# files (leaving out the host files, which we'll add back at runtime). +$(SUPERMINIMG): supermin.incfiles supermin-make.sh + rm -f $@ + bash supermin-make.sh + +endif + +# This should rebuild the scripts if the input files change, although +# it doesn't always seem to work. +%.sh: %.sh.in cd .. && ./config.status appliance/$@ + chmod +x $@ + +guestfs-supermin-helper: guestfs-supermin-helper.in + cd .. && ./config.status appliance/$@ + chmod +x $@ + +#---------------------------------------------------------------------- +# Extra rules for testing the appliance. -# Test-boot the image. +# Test-boot the appliance. test-boot: emptydisk qemu-system-$(host_cpu) \ @@ -75,7 +126,7 @@ test-boot-realistic: emptydisk # Make clean. -CLEANFILES = $(fs_DATA) +CLEANFILES = $(APPLIANCE_FILES) clean-local: rm -rf $(top_builddir)/initramfs diff --git a/appliance/guestfs-supermin-helper.in b/appliance/guestfs-supermin-helper.in new file mode 100755 index 00000000..1384ef66 --- /dev/null +++ b/appliance/guestfs-supermin-helper.in @@ -0,0 +1,80 @@ +#!/bin/bash - +# @configure_input@ +# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Helper script which constructs the supermin appliance at runtime. + +unset CDPATH + +set -e + +# Source directory containing the supermin input files. +sourcedir=$(cd "$1" > /dev/null; pwd) + +# Output files. +kernel="$2" +initrd="$3" + +# Look for the kernel first. This is very unsophisticated: We +# just look for any kernel named vmlinuz-*.$host_cpu which has a +# corresponding /lib/modules/*.$host_cpu directory. + +for f in /boot/vmlinuz-*.@host_cpu@; do + b=$(basename "$f") + b=$(echo "$b" | sed 's,vmlinuz-,,') + modpath="/lib/modules/$b" + if [ -d "$modpath" ]; then + ln -sf "$f" "$kernel" + break + fi + modpath= +done + +if [ -z "$modpath" ]; then + echo "$0: failed to find a suitable kernel" >&2 + exit 1 +fi + +# The initrd consists of these components: +# (1) The base skeleton appliance that we constructed at build time. +# name = initramfs.@REPO@.@host_cpu@.supermin.img +# format = compressed cpio +# (2) The modules from modpath which are on the module whitelist. +# format = plain cpio +# (3) The host files which match wildcards in *.supermin.hostfiles. +# format = plain cpio + +cp "$sourcedir"/initramfs.@REPO@.@host_cpu@.supermin.img "$initrd" + +# Kernel modules (2). +exec 5<"$sourcedir"/kmod.whitelist +whitelist= +while read kmod 0<&5; do + whitelist="$whitelist -a -not -name $kmod" +done +exec 5<&- + +find "$modpath" -not -name '*.ko' -o \( -name '*.ko' $whitelist \) -a -print0 | + cpio --quiet -o -0 -H newc >> "$initrd" + +# Host files (3). + +(cd / && \ + ls -1df $( + cat "$sourcedir"/initramfs.@REPO@.@host_cpu@.supermin.hostfiles + ) 2>/dev/null | + cpio --quiet -o -H newc ) >> "$initrd" diff --git a/appliance/kmod.whitelist b/appliance/kmod.whitelist deleted file mode 100644 index 4bf4cf81..00000000 --- a/appliance/kmod.whitelist +++ /dev/null @@ -1,38 +0,0 @@ -# List of kernel modules that we leave in the appliance. This has to -# include any dependencies needed by modules. - -# Any kernel module not listed here is deleted from the appliance. - -# Wildcards are permitted. - -virtio.ko -virtio_net.ko -virtio_pci.ko -virtio_ring.ko - -ext2.ko -ext3.ko -ext4*.ko - -crc16.ko -jbd.ko -jbd2.ko - -fuse.ko - -vfat.ko -fat.ko - -udf.ko -crc_itu_t.ko -nls_utf8.ko - -dm-*.ko - -cramfs.ko -squashfs.ko - -hfsplus.ko -ufs.ko -exportfs.ko -xfs.ko diff --git a/appliance/kmod.whitelist.in b/appliance/kmod.whitelist.in new file mode 100644 index 00000000..4bf4cf81 --- /dev/null +++ b/appliance/kmod.whitelist.in @@ -0,0 +1,38 @@ +# List of kernel modules that we leave in the appliance. This has to +# include any dependencies needed by modules. + +# Any kernel module not listed here is deleted from the appliance. + +# Wildcards are permitted. + +virtio.ko +virtio_net.ko +virtio_pci.ko +virtio_ring.ko + +ext2.ko +ext3.ko +ext4*.ko + +crc16.ko +jbd.ko +jbd2.ko + +fuse.ko + +vfat.ko +fat.ko + +udf.ko +crc_itu_t.ko +nls_utf8.ko + +dm-*.ko + +cramfs.ko +squashfs.ko + +hfsplus.ko +ufs.ko +exportfs.ko +xfs.ko diff --git a/appliance/make.sh.in b/appliance/make.sh.in index 8f3b2127..9d77bea2 100755 --- a/appliance/make.sh.in +++ b/appliance/make.sh.in @@ -80,18 +80,22 @@ rm -f $koutput # Don't need any keyboard maps. @FEBOOTSTRAP_RUN@ initramfs -- rm -rf /lib/kbd +# Remove anything in home directory. Because this is potentially +# liable to monstrous fuck-ups, we don't put a slash before 'home'. +(cd initramfs && echo home/*) | + xargs @FEBOOTSTRAP_RUN@ initramfs -- rm -rf + +# Remove /var/lib/yum stuff. +@FEBOOTSTRAP_RUN@ initramfs -- rm -rf /var/lib/yum + # Kernel modules take up nearly half of the image. Only include ones # which are on the whitelist. -grep -v '^[[:space:]]*$' < appliance/kmod.whitelist | - grep -v '^#' > kmod.whitelist.tmp -exec 5 $output-t +mv $output-t $output +ls -lh $output diff --git a/appliance/supermin-split.sh.in b/appliance/supermin-split.sh.in new file mode 100755 index 00000000..fe4ab6ef --- /dev/null +++ b/appliance/supermin-split.sh.in @@ -0,0 +1,93 @@ +#!/bin/bash - +# @configure_input@ +# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Decide which files will stay in the supermin appliance and which +# files will be pulled out of the host at runtime. +# +# Read the README file! +# +# The basic idea is that we create two output files, one containing +# the files that will stay, and the other listing the files that +# will be pulled from the host (ie. not go into the appliance now). +# +# The list of files that stay ('supermin.incfiles') is just a straight +# list of files and directories. +# +# The list of files that come from the host ('*.supermin.hostfiles') +# can include wildcards, to allow libraries to be upgraded on the +# host. + +unset CDPATH + +set -e + +cd @top_builddir@/initramfs + +incfiles=../appliance/supermin.incfiles +hostfiles=../appliance/initramfs.@REPO@.@host_cpu@.supermin.hostfiles + +exec 5>$incfiles +exec 6>$hostfiles + +# Note currently the initramfs contains ~2500 files, and none have +# "funny characters" in the names. So this is reasonable just to +# simplify the script. +for path in $(find -not -name fakeroot.log); do + dir=$(dirname "$path") + file=$(basename "$path") + + # All we're going to keep are the special files /init, the daemon, + # configuration files (/etc), devices and modifiable stuff (/var). + if [ "$path" = "./init" -o "$file" = "guestfsd" ]; then + echo "$path" >&5 + + elif [[ "$path" =~ '^\./etc' || "$path" =~ '^./dev' || "$path" =~ '^\./var' ]]; then + echo "$path" >&5 + + # Kernel modules are always copied in from the host, including all + # the dependency files. + elif [[ "$path" =~ '^\./lib/modules/' ]]; then + : + + elif [ -d "$path" ]; then + # Always write directory names to both output files. + echo "$path" >&5 + echo "$path" >&6 + + # Some libraries need fixed version numbers replaced by wildcards. + + elif [[ "$file" =~ '^ld-[.0-9]+\.so$' ]]; then + echo "$dir/ld-*.so" >&6 + + # libfoo-1.2.3.so + elif [[ "$file" =~ '^lib(.*)-[-.0-9]+\.so$' ]]; then + echo "$dir/lib${BASH_REMATCH[1]}-*.so" >&6 + + # libfoo-1.2.3.so.1.2.3 (but NOT '*.so.N') + elif [[ "$file" =~ '^lib(.*)-[-.0-9]+\.so\.([0-9]+)\.' ]]; then + echo "$dir/lib${BASH_REMATCH[1]}-*.so.${BASH_REMATCH[2]}.*" >&6 + + # libfoo.so.1.2.3 (but NOT '*.so.N') + elif [[ "$file" =~ '^lib(.*)\.so\.([0-9]+)\.' ]]; then + echo "$dir/lib${BASH_REMATCH[1]}.so.${BASH_REMATCH[2]}.*" >&6 + + else + # Anything else comes from the host directly. + echo "$path" >&6 + fi +done diff --git a/configure.ac b/configure.ac index e73799a6..be1ed6bc 100644 --- a/configure.ac +++ b/configure.ac @@ -480,6 +480,8 @@ AC_CONFIG_FILES([Makefile src/Makefile fish/Makefile po/Makefile.in examples/Makefile appliance/Makefile appliance/make.sh appliance/update.sh + appliance/supermin-split.sh appliance/supermin-make.sh + appliance/guestfs-supermin-helper images/Makefile capitests/Makefile regressions/Makefile @@ -495,7 +497,7 @@ AC_CONFIG_FILES([Makefile AC_OUTPUT dnl WTF? -chmod +x appliance/make.sh appliance/update.sh +chmod +x appliance/*.sh appliance/guestfs-supermin-helper dnl Produce summary. echo diff --git a/src/guestfs.c b/src/guestfs.c index fea8107d..8f06d3ba 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -309,6 +310,12 @@ guestfs_close (guestfs_h *g) snprintf (filename, sizeof filename, "%s/sock", g->tmpdir); unlink (filename); + snprintf (filename, sizeof filename, "%s/initrd", g->tmpdir); + unlink (filename); + + snprintf (filename, sizeof filename, "%s/kernel", g->tmpdir); + unlink (filename); + rmdir (g->tmpdir); free (g->tmpdir); @@ -706,6 +713,46 @@ guestfs_add_cdrom (guestfs_h *g, const char *filename) return guestfs_config (g, "-cdrom", filename); } +/* Returns true iff file is contained in dir. */ +static int +dir_contains_file (const char *dir, const char *file) +{ + int dirlen = strlen (dir); + int filelen = strlen (file); + int len = dirlen+filelen+2; + char path[len]; + + snprintf (path, len, "%s/%s", dir, file); + return access (path, F_OK) == 0; +} + +/* Returns true iff every listed file is contained in 'dir'. */ +static int +dir_contains_files (const char *dir, ...) +{ + va_list args; + const char *file; + + va_start (args, dir); + while ((file = va_arg (args, const char *)) != NULL) { + if (!dir_contains_file (dir, file)) { + va_end (args); + return 0; + } + } + va_end (args); + return 1; +} + +static int build_supermin_appliance (guestfs_h *g, const char *path, char **kernel, char **initrd); + +static const char *kernel_name = "vmlinuz." REPO "." host_cpu; +static const char *initrd_name = "initramfs." REPO "." host_cpu ".img"; +static const char *supermin_name = + "initramfs." REPO "." host_cpu ".supermin.img"; +static const char *supermin_hostfiles_name = + "initramfs." REPO "." host_cpu ".supermin.hostfiles"; + int guestfs_launch (guestfs_h *g) { @@ -714,8 +761,6 @@ guestfs_launch (guestfs_h *g) size_t len; int wfd[2], rfd[2]; int tries; - const char *kernel_name = "vmlinuz." REPO "." host_cpu; - const char *initrd_name = "initramfs." REPO "." host_cpu ".img"; char *path, *pelem, *pend; char *kernel = NULL, *initrd = NULL; char unixsock[256]; @@ -732,7 +777,20 @@ guestfs_launch (guestfs_h *g) return -1; } - /* Search g->path for the kernel and initrd. */ + /* Make the temporary directory. */ + if (!g->tmpdir) { + g->tmpdir = safe_strdup (g, dir_template); + if (mkdtemp (g->tmpdir) == NULL) { + perrorf (g, _("%s: cannot create temporary directory"), dir_template); + goto cleanup0; + } + } + + /* First search g->path for the supermin appliance, and try to + * synthesize a kernel and initrd from that. If it fails, we + * try the path search again looking for a backup ordinary + * appliance. + */ pelem = path = safe_strdup (g, g->path); do { pend = strchrnul (pelem, ':'); @@ -740,32 +798,31 @@ guestfs_launch (guestfs_h *g) *pend = '\0'; len = pend - pelem; - /* Empty element or "." means cwd. */ + /* Empty element of "." means cwd. */ if (len == 0 || (len == 1 && *pelem == '.')) { if (g->verbose) fprintf (stderr, - "looking for kernel and initrd in current directory\n"); - if (access (kernel_name, F_OK) == 0 && access (initrd_name, F_OK) == 0) { - kernel = safe_strdup (g, kernel_name); - initrd = safe_strdup (g, initrd_name); + "looking for supermin appliance in current directory\n"); + if (dir_contains_files (".", + supermin_name, supermin_hostfiles_name, + "kmod.whitelist", NULL)) { + if (build_supermin_appliance (g, ".", &kernel, &initrd) == -1) + return -1; break; } } - /* Look at /kernel etc. */ + /* Look at /supermin* etc. */ else { - kernel = safe_malloc (g, len + strlen (kernel_name) + 2); - initrd = safe_malloc (g, len + strlen (initrd_name) + 2); - sprintf (kernel, "%s/%s", pelem, kernel_name); - sprintf (initrd, "%s/%s", pelem, initrd_name); - if (g->verbose) - fprintf (stderr, "looking for %s and %s\n", kernel, initrd); + fprintf (stderr, "looking for supermin appliance in %s\n", pelem); - if (access (kernel, F_OK) == 0 && access (initrd, F_OK) == 0) + if (dir_contains_files (pelem, + supermin_name, supermin_hostfiles_name, + "kmod.whitelist", NULL)) { + if (build_supermin_appliance (g, pelem, &kernel, &initrd) == -1) + return -1; break; - free (kernel); - free (initrd); - kernel = initrd = NULL; + } } pelem = pend + 1; @@ -773,6 +830,46 @@ guestfs_launch (guestfs_h *g) free (path); + if (kernel == NULL || initrd == NULL) { + /* Search g->path for the kernel and initrd. */ + pelem = path = safe_strdup (g, g->path); + do { + pend = strchrnul (pelem, ':'); + pmore = *pend == ':'; + *pend = '\0'; + len = pend - pelem; + + /* Empty element or "." means cwd. */ + if (len == 0 || (len == 1 && *pelem == '.')) { + if (g->verbose) + fprintf (stderr, + "looking for appliance in current directory\n"); + if (dir_contains_files (".", kernel_name, initrd_name, NULL)) { + kernel = safe_strdup (g, kernel_name); + initrd = safe_strdup (g, initrd_name); + break; + } + } + /* Look at /kernel etc. */ + else { + if (g->verbose) + fprintf (stderr, "looking for appliance in %s\n", pelem); + + if (dir_contains_files (pelem, kernel_name, initrd_name, NULL)) { + kernel = safe_malloc (g, len + strlen (kernel_name) + 2); + initrd = safe_malloc (g, len + strlen (initrd_name) + 2); + sprintf (kernel, "%s/%s", pelem, kernel_name); + sprintf (initrd, "%s/%s", pelem, initrd_name); + break; + } + } + + pelem = pend + 1; + } while (pmore); + + free (path); + } + if (kernel == NULL || initrd == NULL) { error (g, _("cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)"), kernel_name, initrd_name, g->path); @@ -788,15 +885,7 @@ guestfs_launch (guestfs_h *g) */ memsize = 384; - /* Make the temporary directory containing the socket. */ - if (!g->tmpdir) { - g->tmpdir = safe_strdup (g, dir_template); - if (mkdtemp (g->tmpdir) == NULL) { - perrorf (g, _("%s: cannot create temporary directory"), dir_template); - goto cleanup0; - } - } - + /* Make the vmchannel socket. */ snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir); unlink (unixsock); @@ -1048,6 +1137,34 @@ guestfs_launch (guestfs_h *g) return -1; } +/* This function does the hard work of building the supermin appliance + * on the fly. 'path' is the directory containing the control files. + * 'kernel' and 'initrd' are where we will return the names of the + * kernel and initrd (only initrd is built). The work is done by + * an external script. We just tell it where to put the result. + */ +static int +build_supermin_appliance (guestfs_h *g, const char *path, + char **kernel, char **initrd) +{ + char cmd[4096]; + int r; + + snprintf (cmd, sizeof cmd, + "PATH='%s':$PATH " + "guestfs-supermin-helper '%s' %s/kernel %s/initrd", + path, + path, g->tmpdir, g->tmpdir); + + r = system (cmd); + if (r == -1 || WEXITSTATUS(r) != 0) { + error (g, _("external command failed: %s"), cmd); + return -1; + } + + return 0; +} + static void finish_wait_ready (guestfs_h *g, void *vp) { -- cgit From 5dc2f852d275b79c3e338c6ea5629c6baf4e1a8e Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 15 Jun 2009 22:39:57 +0100 Subject: Prepare for 1.0.46. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index be1ed6bc..b00ddb42 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.45]) +AC_INIT([libguestfs],[1.0.46]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) -- cgit From 9c662c7ea543ca5bc9ffae45283e92db7ba68e1f Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 15 Jun 2009 22:47:09 +0100 Subject: Generated files for 1.0.46 release. --- po/libguestfs.pot | 117 +++++++++++++++++++++++++++------------------------- po/pl.po | 121 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 124 insertions(+), 114 deletions(-) diff --git a/po/libguestfs.pot b/po/libguestfs.pot index 42cfa887..988339c8 100644 --- a/po/libguestfs.pot +++ b/po/libguestfs.pot @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-09 10:58+0100\n" +"POT-Creation-Date: 2009-06-15 22:45+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -218,239 +218,244 @@ msgstr "" msgid "%s: command not known, use -h to list all commands\n" msgstr "" -#: src/guestfs.c:287 +#: src/guestfs.c:288 #, c-format msgid "guestfs_close: called twice on the same handle\n" msgstr "" -#: src/guestfs.c:368 +#: src/guestfs.c:375 #, c-format msgid "libguestfs: error: %s\n" msgstr "" -#: src/guestfs.c:609 +#: src/guestfs.c:616 msgid "command line cannot be altered after qemu subprocess launched" msgstr "" -#: src/guestfs.c:623 +#: src/guestfs.c:630 msgid "guestfs_config: parameter must begin with '-' character" msgstr "" -#: src/guestfs.c:638 +#: src/guestfs.c:645 #, c-format msgid "guestfs_config: parameter '%s' isn't allowed" msgstr "" -#: src/guestfs.c:658 src/guestfs.c:679 src/guestfs.c:697 +#: src/guestfs.c:665 src/guestfs.c:686 src/guestfs.c:704 msgid "filename cannot contain ',' (comma) character" msgstr "" -#: src/guestfs.c:726 +#: src/guestfs.c:771 msgid "you must call guestfs_add_drive before guestfs_launch" msgstr "" -#: src/guestfs.c:731 +#: src/guestfs.c:776 msgid "qemu has already been launched" msgstr "" -#: src/guestfs.c:777 +#: src/guestfs.c:784 #, c-format -msgid "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)" +msgid "%s: cannot create temporary directory" msgstr "" -#: src/guestfs.c:795 +#: src/guestfs.c:874 #, c-format -msgid "%s: cannot create temporary directory" +msgid "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)" msgstr "" -#: src/guestfs.c:991 +#: src/guestfs.c:1080 msgid "failed to connect to vmchannel socket" msgstr "" -#: src/guestfs.c:1010 +#: src/guestfs.c:1099 msgid "could not watch qemu stdout" msgstr "" -#: src/guestfs.c:1069 +#: src/guestfs.c:1161 +#, c-format +msgid "external command failed: %s" +msgstr "" + +#: src/guestfs.c:1186 msgid "qemu has finished launching already" msgstr "" -#: src/guestfs.c:1074 +#: src/guestfs.c:1191 msgid "qemu has not been launched yet" msgstr "" -#: src/guestfs.c:1087 +#: src/guestfs.c:1204 msgid "guestfs_wait_ready failed, see earlier error messages" msgstr "" -#: src/guestfs.c:1097 +#: src/guestfs.c:1214 msgid "qemu launched and contacted daemon, but state != READY" msgstr "" -#: src/guestfs.c:1108 +#: src/guestfs.c:1225 msgid "no subprocess to kill" msgstr "" -#: src/guestfs.c:1156 +#: src/guestfs.c:1273 #, c-format msgid "guestfs_set_ready: called when in state %d != BUSY" msgstr "" -#: src/guestfs.c:1168 +#: src/guestfs.c:1285 #, c-format msgid "guestfs_set_busy: called when in state %d != READY" msgstr "" -#: src/guestfs.c:1189 +#: src/guestfs.c:1306 #, c-format msgid "guestfs_end_busy: called when in state %d" msgstr "" -#: src/guestfs.c:1278 +#: src/guestfs.c:1395 #, c-format msgid "stdout_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1321 +#: src/guestfs.c:1438 #, c-format msgid "sock_read_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1351 +#: src/guestfs.c:1468 msgid "can't decode length word" msgstr "" -#: src/guestfs.c:1361 +#: src/guestfs.c:1478 #, c-format msgid "received magic signature from guestfsd, but in state %d" msgstr "" -#: src/guestfs.c:1364 +#: src/guestfs.c:1481 #, c-format msgid "received magic signature from guestfsd, but msg size is %d" msgstr "" -#: src/guestfs.c:1389 +#: src/guestfs.c:1506 #, c-format msgid "message length (%u) > maximum possible size (%d)" msgstr "" -#: src/guestfs.c:1422 +#: src/guestfs.c:1539 #, c-format msgid "state %d != BUSY" msgstr "" -#: src/guestfs.c:1462 +#: src/guestfs.c:1579 #, c-format msgid "sock_write_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1467 +#: src/guestfs.c:1584 #, c-format msgid "sock_write_event: state %d != BUSY" msgstr "" -#: src/guestfs.c:1571 src/guestfs.c:1594 +#: src/guestfs.c:1688 src/guestfs.c:1711 msgid "remove_handle failed" msgstr "" -#: src/guestfs.c:1582 src/guestfs.c:1605 +#: src/guestfs.c:1699 src/guestfs.c:1722 msgid "add_handle failed" msgstr "" -#: src/guestfs.c:1638 +#: src/guestfs.c:1755 #, c-format msgid "guestfs__send_sync: state %d != BUSY" msgstr "" -#: src/guestfs.c:1646 src/guestfs.c:1826 +#: src/guestfs.c:1763 src/guestfs.c:1943 msgid "guestfs__send_sync: msg_out should be NULL" msgstr "" -#: src/guestfs.c:1668 +#: src/guestfs.c:1785 msgid "xdr_guestfs_message_header failed" msgstr "" -#: src/guestfs.c:1677 +#: src/guestfs.c:1794 msgid "dispatch failed to marshal args" msgstr "" -#: src/guestfs.c:1703 +#: src/guestfs.c:1820 msgid "send failed, see earlier error messages" msgstr "" -#: src/guestfs.c:1818 +#: src/guestfs.c:1935 #, c-format msgid "send_file_chunk_sync: state %d != READY" msgstr "" -#: src/guestfs.c:1849 +#: src/guestfs.c:1966 #, c-format msgid "xdr_guestfs_chunk failed (buf = %p, buflen = %zu)" msgstr "" -#: src/guestfs.c:1874 +#: src/guestfs.c:1991 msgid "send file chunk failed, see earlier error messages" msgstr "" -#: src/guestfs.c:1925 +#: src/guestfs.c:2042 #, c-format msgid "check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n" msgstr "" -#: src/guestfs.c:1963 +#: src/guestfs.c:2080 #, c-format msgid "%s: error in chunked encoding" msgstr "" -#: src/guestfs.c:1987 +#: src/guestfs.c:2104 msgid "write to daemon socket" msgstr "" -#: src/guestfs.c:2034 +#: src/guestfs.c:2151 msgid "failed to parse file chunk" msgstr "" -#: src/guestfs.c:2066 +#: src/guestfs.c:2183 msgid "receive_file_data_sync: reply callback not called\n" msgstr "" -#: src/guestfs.c:2071 +#: src/guestfs.c:2188 msgid "receive_file_data_sync: parse error in reply callback\n" msgstr "" -#: src/guestfs.c:2085 +#: src/guestfs.c:2202 msgid "file receive cancelled by daemon" msgstr "" -#: src/guestfs.c:2120 src/guestfs.c:2178 +#: src/guestfs.c:2237 src/guestfs.c:2295 #, c-format msgid "fd %d is out of range" msgstr "" -#: src/guestfs.c:2128 +#: src/guestfs.c:2245 #, c-format msgid "set of events (0x%x) contains unknown events" msgstr "" -#: src/guestfs.c:2133 +#: src/guestfs.c:2250 msgid "set of events is empty" msgstr "" -#: src/guestfs.c:2140 +#: src/guestfs.c:2257 #, c-format msgid "fd %d is already registered" msgstr "" -#: src/guestfs.c:2145 +#: src/guestfs.c:2262 msgid "callback is NULL" msgstr "" -#: src/guestfs.c:2185 +#: src/guestfs.c:2302 #, c-format msgid "fd %d was not registered" msgstr "" -#: src/guestfs.c:2234 +#: src/guestfs.c:2351 msgid "select_main_loop_run: this cannot be called recursively" msgstr "" diff --git a/po/pl.po b/po/pl.po index 0e5b1472..81555ec5 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: libguestfs 1.0.31\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-09 10:58+0100\n" +"POT-Creation-Date: 2009-06-15 22:45+0100\n" "PO-Revision-Date: 2009-05-26 00:50+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" @@ -284,246 +284,251 @@ msgid "%s: command not known, use -h to list all commands\n" msgstr "" "%s: nieznane polecenie, użyj -h, aby wyświetlić listę wszystkich poleceń\n" -#: src/guestfs.c:287 +#: src/guestfs.c:288 #, c-format msgid "guestfs_close: called twice on the same handle\n" msgstr "guestfs_close: wywołano dwa razy w tym samym programie obsługującym\n" -#: src/guestfs.c:368 +#: src/guestfs.c:375 #, c-format msgid "libguestfs: error: %s\n" msgstr "libguestfs: błąd: %s\n" -#: src/guestfs.c:609 +#: src/guestfs.c:616 msgid "command line cannot be altered after qemu subprocess launched" msgstr "" "wiersz poleceń nie może zostać zmieniony po uruchomieniu podprocesu QEMU" -#: src/guestfs.c:623 +#: src/guestfs.c:630 msgid "guestfs_config: parameter must begin with '-' character" msgstr "guestfs_config: parametr musi zaczynać się od znaku \"-\"" -#: src/guestfs.c:638 +#: src/guestfs.c:645 #, c-format msgid "guestfs_config: parameter '%s' isn't allowed" msgstr "guestfs_config: parametr \"%s\" nie jest dozwolony" -#: src/guestfs.c:658 src/guestfs.c:679 src/guestfs.c:697 +#: src/guestfs.c:665 src/guestfs.c:686 src/guestfs.c:704 msgid "filename cannot contain ',' (comma) character" msgstr "nazwa pliku nie może zawierać znaku \",\" (przecinka)" -#: src/guestfs.c:726 +#: src/guestfs.c:771 msgid "you must call guestfs_add_drive before guestfs_launch" msgstr "należy wywołać guestfs_add_drive przed guestfs_launch" -#: src/guestfs.c:731 +#: src/guestfs.c:776 msgid "qemu has already been launched" msgstr "QEMU zostało już uruchomione" -#: src/guestfs.c:777 -#, c-format -msgid "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)" -msgstr "nie można znaleźć %s lub %s w LIBGUESTFS_PATH (bieżąca ścieżka = %s)" - -#: src/guestfs.c:795 +#: src/guestfs.c:784 #, c-format msgid "%s: cannot create temporary directory" msgstr "%s: nie można utworzyć folderu tymczasowego" -#: src/guestfs.c:991 +#: src/guestfs.c:874 +#, c-format +msgid "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)" +msgstr "nie można znaleźć %s lub %s w LIBGUESTFS_PATH (bieżąca ścieżka = %s)" + +#: src/guestfs.c:1080 msgid "failed to connect to vmchannel socket" msgstr "połączenie się z gniazdem kanału maszyny wirtualnej nie powiodło się" -#: src/guestfs.c:1010 +#: src/guestfs.c:1099 msgid "could not watch qemu stdout" msgstr "nie można obserwować standardowego wyjścia QEMU" -#: src/guestfs.c:1069 +#: src/guestfs.c:1161 +#, c-format +msgid "external command failed: %s" +msgstr "" + +#: src/guestfs.c:1186 msgid "qemu has finished launching already" msgstr "QEMU zakończyło już uruchamianie" -#: src/guestfs.c:1074 +#: src/guestfs.c:1191 msgid "qemu has not been launched yet" msgstr "QEMU nie zostało jeszcze uruchomione" -#: src/guestfs.c:1087 +#: src/guestfs.c:1204 msgid "guestfs_wait_ready failed, see earlier error messages" msgstr "" "guestfs_wait_ready nie powiodło się, zobacz wcześniejsze komunikaty błędów" -#: src/guestfs.c:1097 +#: src/guestfs.c:1214 msgid "qemu launched and contacted daemon, but state != READY" msgstr "" "QEMU zostało uruchomione i skontaktowano się z demonem, ale stan != GOTOWY" -#: src/guestfs.c:1108 +#: src/guestfs.c:1225 msgid "no subprocess to kill" msgstr "brak podprocesu do zniszczenia" -#: src/guestfs.c:1156 +#: src/guestfs.c:1273 #, c-format msgid "guestfs_set_ready: called when in state %d != BUSY" msgstr "guestfs_set_ready: wywołano, kiedy w stanie %d != ZAJĘTY" -#: src/guestfs.c:1168 +#: src/guestfs.c:1285 #, c-format msgid "guestfs_set_busy: called when in state %d != READY" msgstr "guestfs_set_busy: wywołano, kiedy w stanie %d != GOTOWY" -#: src/guestfs.c:1189 +#: src/guestfs.c:1306 #, c-format msgid "guestfs_end_busy: called when in state %d" msgstr "guestfs_end_busy: wywołano, kiedy w stanie %d" -#: src/guestfs.c:1278 +#: src/guestfs.c:1395 #, c-format msgid "stdout_event: internal error: %d != %d" msgstr "stdout_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1321 +#: src/guestfs.c:1438 #, c-format msgid "sock_read_event: internal error: %d != %d" msgstr "sock_read_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1351 +#: src/guestfs.c:1468 msgid "can't decode length word" msgstr "nie można dekodować długości słowa" -#: src/guestfs.c:1361 +#: src/guestfs.c:1478 #, c-format msgid "received magic signature from guestfsd, but in state %d" msgstr "otrzymano podpis magic z guestfsd, ale w stanie %d" -#: src/guestfs.c:1364 +#: src/guestfs.c:1481 #, c-format msgid "received magic signature from guestfsd, but msg size is %d" msgstr "otrzymano podpis magic z guestfsd, ale rozmiar komunikatu to %d" -#: src/guestfs.c:1389 +#: src/guestfs.c:1506 #, c-format msgid "message length (%u) > maximum possible size (%d)" msgstr "długość komunikatu (%u) > maksymalny możliwy rozmiar (%d)" -#: src/guestfs.c:1422 +#: src/guestfs.c:1539 #, c-format msgid "state %d != BUSY" msgstr "stan %d != ZAJĘTY" -#: src/guestfs.c:1462 +#: src/guestfs.c:1579 #, c-format msgid "sock_write_event: internal error: %d != %d" msgstr "sock_write_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1467 +#: src/guestfs.c:1584 #, c-format msgid "sock_write_event: state %d != BUSY" msgstr "sock_write_event: stan %d != ZAJĘTY" -#: src/guestfs.c:1571 src/guestfs.c:1594 +#: src/guestfs.c:1688 src/guestfs.c:1711 msgid "remove_handle failed" msgstr "remove_handle nie powiodło się" -#: src/guestfs.c:1582 src/guestfs.c:1605 +#: src/guestfs.c:1699 src/guestfs.c:1722 msgid "add_handle failed" msgstr "add_handle nie powiodło się" -#: src/guestfs.c:1638 +#: src/guestfs.c:1755 #, c-format msgid "guestfs__send_sync: state %d != BUSY" msgstr "guestfs__send_sync: stan %d != ZAJĘTY" -#: src/guestfs.c:1646 src/guestfs.c:1826 +#: src/guestfs.c:1763 src/guestfs.c:1943 msgid "guestfs__send_sync: msg_out should be NULL" msgstr "guestfs__send_sync: msg_out powinno być PUSTE" -#: src/guestfs.c:1668 +#: src/guestfs.c:1785 msgid "xdr_guestfs_message_header failed" msgstr "xdr_guestfs_message_header nie powiodło się" -#: src/guestfs.c:1677 +#: src/guestfs.c:1794 msgid "dispatch failed to marshal args" msgstr "rozdzielenie parametrów marszałka nie powiodło się" -#: src/guestfs.c:1703 +#: src/guestfs.c:1820 msgid "send failed, see earlier error messages" msgstr "wysłanie nie powiodło się, zobacz wcześniejsze komunikaty błędów" -#: src/guestfs.c:1818 +#: src/guestfs.c:1935 #, c-format msgid "send_file_chunk_sync: state %d != READY" msgstr "send_file_chunk_sync: stan %d != GOTOWY" -#: src/guestfs.c:1849 +#: src/guestfs.c:1966 #, c-format msgid "xdr_guestfs_chunk failed (buf = %p, buflen = %zu)" msgstr "xdr_guestfs_chunk nie powiodło się (bufor = %p, długość bufora = %zu)" -#: src/guestfs.c:1874 +#: src/guestfs.c:1991 msgid "send file chunk failed, see earlier error messages" msgstr "" "wysłanie fragmentu pliku nie powiodło się, zobacz wcześniejsze komunikaty " "błędów" -#: src/guestfs.c:1925 +#: src/guestfs.c:2042 #, c-format msgid "check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n" msgstr "" "check_for_daemon_cancellation: odczytano 0x%x z demona, oczekiwano 0x%x\n" -#: src/guestfs.c:1963 +#: src/guestfs.c:2080 #, c-format msgid "%s: error in chunked encoding" msgstr "%s: błąd w kodowaniu fragmentu" -#: src/guestfs.c:1987 +#: src/guestfs.c:2104 msgid "write to daemon socket" msgstr "zapisz do gniazda demona" -#: src/guestfs.c:2034 +#: src/guestfs.c:2151 msgid "failed to parse file chunk" msgstr "przeanalizowanie fragmentu pliku nie powiodło się" -#: src/guestfs.c:2066 +#: src/guestfs.c:2183 msgid "receive_file_data_sync: reply callback not called\n" msgstr "receive_file_data_sync: nie wywołano odpowiedzi wywołania zwrotnego\n" -#: src/guestfs.c:2071 +#: src/guestfs.c:2188 msgid "receive_file_data_sync: parse error in reply callback\n" msgstr "" "receive_file_data_sync: błąd analizy w odpowiedzi wywołania zwrotnego\n" -#: src/guestfs.c:2085 +#: src/guestfs.c:2202 msgid "file receive cancelled by daemon" msgstr "otrzymanie pliku zostało anulowane przez demona" -#: src/guestfs.c:2120 src/guestfs.c:2178 +#: src/guestfs.c:2237 src/guestfs.c:2295 #, c-format msgid "fd %d is out of range" msgstr "fd %d jest spoza zakresu" -#: src/guestfs.c:2128 +#: src/guestfs.c:2245 #, c-format msgid "set of events (0x%x) contains unknown events" msgstr "zestaw zdarzeń (0x%x) zawiera nieznane zdarzenia" -#: src/guestfs.c:2133 +#: src/guestfs.c:2250 msgid "set of events is empty" msgstr "zestaw zdarzeń jest pusty" -#: src/guestfs.c:2140 +#: src/guestfs.c:2257 #, c-format msgid "fd %d is already registered" msgstr "fd %d jest już zarejestrowane" -#: src/guestfs.c:2145 +#: src/guestfs.c:2262 msgid "callback is NULL" msgstr "wywołanie zwrotne jest PUSTE" -#: src/guestfs.c:2185 +#: src/guestfs.c:2302 #, c-format msgid "fd %d was not registered" msgstr "fd %d nie zostało zarejestrowane" -#: src/guestfs.c:2234 +#: src/guestfs.c:2351 msgid "select_main_loop_run: this cannot be called recursively" msgstr "select_main_loop_run: nie może zostać wywołane rekursywnie" -- cgit From 3087b47c3abac1ad8773dd07500b51423e62079f Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 15 Jun 2009 23:02:36 +0100 Subject: Missing files from previous release. --- appliance/Makefile.am | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 3362992c..2c8a0be7 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -16,7 +16,10 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. EXTRA_DIST = \ - make.sh update.sh supermin-split.sh supermin-make.sh + make.sh update.sh supermin-split.sh supermin-make.sh \ + guestfs-supermin-helper \ + kmod.whitelist \ + kmod.whitelist.in # Build the root filesystem (appliance). # Currently this is arch-dependent, so it seems like putting it in -- cgit From 2b4e82d6ca502581bc63fd873e4b55aa93463f62 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 15 Jun 2009 23:06:21 +0100 Subject: Missing files in previous release, so version 1.0.47 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b00ddb42..f8ed7ba0 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.46]) +AC_INIT([libguestfs],[1.0.47]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) -- cgit From 28e2bceefeedbef82dcc24e60e8a22da0960354a Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Tue, 16 Jun 2009 09:56:19 +0100 Subject: Fix build_supermin_appliance to return kernel / initrd names. --- src/guestfs.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/guestfs.c b/src/guestfs.c index 8f06d3ba..20fafc12 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -1148,17 +1148,26 @@ build_supermin_appliance (guestfs_h *g, const char *path, char **kernel, char **initrd) { char cmd[4096]; - int r; + int r, len; + + len = strlen (g->tmpdir); + *kernel = safe_malloc (g, len + 8); + snprintf (*kernel, len+8, "%s/kernel", g->tmpdir); + *initrd = safe_malloc (g, len + 8); + snprintf (*initrd, len+8, "%s/initrd", g->tmpdir); snprintf (cmd, sizeof cmd, "PATH='%s':$PATH " - "guestfs-supermin-helper '%s' %s/kernel %s/initrd", + "guestfs-supermin-helper '%s' %s %s", path, - path, g->tmpdir, g->tmpdir); + path, *kernel, *initrd); r = system (cmd); if (r == -1 || WEXITSTATUS(r) != 0) { error (g, _("external command failed: %s"), cmd); + free (*kernel); + free (*initrd); + *kernel = *initrd = NULL; return -1; } -- cgit From 95b902b657dbce901f01fb97bd3cd52bd61636cb Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Tue, 16 Jun 2009 09:56:44 +0100 Subject: Reverse sense of whitelist test. --- appliance/guestfs-supermin-helper.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appliance/guestfs-supermin-helper.in b/appliance/guestfs-supermin-helper.in index 1384ef66..87fb6882 100755 --- a/appliance/guestfs-supermin-helper.in +++ b/appliance/guestfs-supermin-helper.in @@ -64,11 +64,11 @@ cp "$sourcedir"/initramfs.@REPO@.@host_cpu@.supermin.img "$initrd" exec 5<"$sourcedir"/kmod.whitelist whitelist= while read kmod 0<&5; do - whitelist="$whitelist -a -not -name $kmod" + whitelist="$whitelist -o -name $kmod" done exec 5<&- -find "$modpath" -not -name '*.ko' -o \( -name '*.ko' $whitelist \) -a -print0 | +find "$modpath" \( -not -name '*.ko' $whitelist \) -a -print0 | cpio --quiet -o -0 -H newc >> "$initrd" # Host files (3). -- cgit From 71322ebf4ee7dfb53db344aaedd70518a3c7a552 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Tue, 16 Jun 2009 10:55:36 +0100 Subject: Don't compress the supermin base initramfs. --- README | 2 +- appliance/supermin-make.sh.in | 5 ++++- configure.ac | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README b/README index 92bfc075..3dea215c 100644 --- a/README +++ b/README @@ -37,7 +37,7 @@ Requirements - recent QEMU >= 0.10 with vmchannel support http://lists.gnu.org/archive/html/qemu-devel/2009-02/msg01042.html -- febootstrap >= 2.0 +- febootstrap >= 2.3 - fakeroot diff --git a/appliance/supermin-make.sh.in b/appliance/supermin-make.sh.in index b993833a..c40dec9b 100755 --- a/appliance/supermin-make.sh.in +++ b/appliance/supermin-make.sh.in @@ -28,6 +28,9 @@ cd @top_builddir@ output=appliance/initramfs.@REPO@.@host_cpu@.supermin.img # Generate final image. -@FEBOOTSTRAP_TO_INITRAMFS@ --files=$(pwd)/appliance/supermin.incfiles initramfs > $output-t +@FEBOOTSTRAP_TO_INITRAMFS@ \ + --nocompress \ + --files=$(pwd)/appliance/supermin.incfiles \ + initramfs > $output-t mv $output-t $output ls -lh $output diff --git a/configure.ac b/configure.ac index f8ed7ba0..bee1a59d 100644 --- a/configure.ac +++ b/configure.ac @@ -199,6 +199,22 @@ if test "x$enable_supermin" = "xyes"; then AC_MSG_FAILURE( [febootstrap-to-initramfs does not support the --files option. +To build the supermin appliance, you need to upgrade to the latest +version of febootstrap. +]) + fi + AC_MSG_RESULT([yes]) + + dnl Check febootstrap-to-initramfs accepts the --nocompress option + dnl (febootstrap >= 2.3). + AC_MSG_CHECKING([for --nocompress support in $FEBOOTSTRAP_TO_INITRAMFS]) + out=`$FEBOOTSTRAP_TO_INITRAMFS 2>&1 ||:` + echo "febootstrap_to_initramfs test command output: $out" >&AS_MESSAGE_LOG_FD + if ! echo $out | grep -sq -- "--nocompress" ; then + AC_MSG_RESULT([no]) + AC_MSG_FAILURE( +[febootstrap-to-initramfs does not support the --nocompress option. + To build the supermin appliance, you need to upgrade to the latest version of febootstrap. ]) -- cgit From be4508dbc4542e9f68fc8c539f8b1bf5193c3a2f Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Tue, 16 Jun 2009 11:22:31 +0100 Subject: Version 1.0.48. --- configure.ac | 2 +- po/libguestfs.pot | 86 +++++++++++++++++++++++++++---------------------------- po/pl.po | 86 +++++++++++++++++++++++++++---------------------------- 3 files changed, 87 insertions(+), 87 deletions(-) diff --git a/configure.ac b/configure.ac index bee1a59d..440a2b65 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.47]) +AC_INIT([libguestfs],[1.0.48]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) diff --git a/po/libguestfs.pot b/po/libguestfs.pot index 988339c8..8fa95295 100644 --- a/po/libguestfs.pot +++ b/po/libguestfs.pot @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-15 22:45+0100\n" +"POT-Creation-Date: 2009-06-16 11:22+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -271,191 +271,191 @@ msgstr "" msgid "could not watch qemu stdout" msgstr "" -#: src/guestfs.c:1161 +#: src/guestfs.c:1167 #, c-format msgid "external command failed: %s" msgstr "" -#: src/guestfs.c:1186 +#: src/guestfs.c:1195 msgid "qemu has finished launching already" msgstr "" -#: src/guestfs.c:1191 +#: src/guestfs.c:1200 msgid "qemu has not been launched yet" msgstr "" -#: src/guestfs.c:1204 +#: src/guestfs.c:1213 msgid "guestfs_wait_ready failed, see earlier error messages" msgstr "" -#: src/guestfs.c:1214 +#: src/guestfs.c:1223 msgid "qemu launched and contacted daemon, but state != READY" msgstr "" -#: src/guestfs.c:1225 +#: src/guestfs.c:1234 msgid "no subprocess to kill" msgstr "" -#: src/guestfs.c:1273 +#: src/guestfs.c:1282 #, c-format msgid "guestfs_set_ready: called when in state %d != BUSY" msgstr "" -#: src/guestfs.c:1285 +#: src/guestfs.c:1294 #, c-format msgid "guestfs_set_busy: called when in state %d != READY" msgstr "" -#: src/guestfs.c:1306 +#: src/guestfs.c:1315 #, c-format msgid "guestfs_end_busy: called when in state %d" msgstr "" -#: src/guestfs.c:1395 +#: src/guestfs.c:1404 #, c-format msgid "stdout_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1438 +#: src/guestfs.c:1447 #, c-format msgid "sock_read_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1468 +#: src/guestfs.c:1477 msgid "can't decode length word" msgstr "" -#: src/guestfs.c:1478 +#: src/guestfs.c:1487 #, c-format msgid "received magic signature from guestfsd, but in state %d" msgstr "" -#: src/guestfs.c:1481 +#: src/guestfs.c:1490 #, c-format msgid "received magic signature from guestfsd, but msg size is %d" msgstr "" -#: src/guestfs.c:1506 +#: src/guestfs.c:1515 #, c-format msgid "message length (%u) > maximum possible size (%d)" msgstr "" -#: src/guestfs.c:1539 +#: src/guestfs.c:1548 #, c-format msgid "state %d != BUSY" msgstr "" -#: src/guestfs.c:1579 +#: src/guestfs.c:1588 #, c-format msgid "sock_write_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1584 +#: src/guestfs.c:1593 #, c-format msgid "sock_write_event: state %d != BUSY" msgstr "" -#: src/guestfs.c:1688 src/guestfs.c:1711 +#: src/guestfs.c:1697 src/guestfs.c:1720 msgid "remove_handle failed" msgstr "" -#: src/guestfs.c:1699 src/guestfs.c:1722 +#: src/guestfs.c:1708 src/guestfs.c:1731 msgid "add_handle failed" msgstr "" -#: src/guestfs.c:1755 +#: src/guestfs.c:1764 #, c-format msgid "guestfs__send_sync: state %d != BUSY" msgstr "" -#: src/guestfs.c:1763 src/guestfs.c:1943 +#: src/guestfs.c:1772 src/guestfs.c:1952 msgid "guestfs__send_sync: msg_out should be NULL" msgstr "" -#: src/guestfs.c:1785 +#: src/guestfs.c:1794 msgid "xdr_guestfs_message_header failed" msgstr "" -#: src/guestfs.c:1794 +#: src/guestfs.c:1803 msgid "dispatch failed to marshal args" msgstr "" -#: src/guestfs.c:1820 +#: src/guestfs.c:1829 msgid "send failed, see earlier error messages" msgstr "" -#: src/guestfs.c:1935 +#: src/guestfs.c:1944 #, c-format msgid "send_file_chunk_sync: state %d != READY" msgstr "" -#: src/guestfs.c:1966 +#: src/guestfs.c:1975 #, c-format msgid "xdr_guestfs_chunk failed (buf = %p, buflen = %zu)" msgstr "" -#: src/guestfs.c:1991 +#: src/guestfs.c:2000 msgid "send file chunk failed, see earlier error messages" msgstr "" -#: src/guestfs.c:2042 +#: src/guestfs.c:2051 #, c-format msgid "check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n" msgstr "" -#: src/guestfs.c:2080 +#: src/guestfs.c:2089 #, c-format msgid "%s: error in chunked encoding" msgstr "" -#: src/guestfs.c:2104 +#: src/guestfs.c:2113 msgid "write to daemon socket" msgstr "" -#: src/guestfs.c:2151 +#: src/guestfs.c:2160 msgid "failed to parse file chunk" msgstr "" -#: src/guestfs.c:2183 +#: src/guestfs.c:2192 msgid "receive_file_data_sync: reply callback not called\n" msgstr "" -#: src/guestfs.c:2188 +#: src/guestfs.c:2197 msgid "receive_file_data_sync: parse error in reply callback\n" msgstr "" -#: src/guestfs.c:2202 +#: src/guestfs.c:2211 msgid "file receive cancelled by daemon" msgstr "" -#: src/guestfs.c:2237 src/guestfs.c:2295 +#: src/guestfs.c:2246 src/guestfs.c:2304 #, c-format msgid "fd %d is out of range" msgstr "" -#: src/guestfs.c:2245 +#: src/guestfs.c:2254 #, c-format msgid "set of events (0x%x) contains unknown events" msgstr "" -#: src/guestfs.c:2250 +#: src/guestfs.c:2259 msgid "set of events is empty" msgstr "" -#: src/guestfs.c:2257 +#: src/guestfs.c:2266 #, c-format msgid "fd %d is already registered" msgstr "" -#: src/guestfs.c:2262 +#: src/guestfs.c:2271 msgid "callback is NULL" msgstr "" -#: src/guestfs.c:2302 +#: src/guestfs.c:2311 #, c-format msgid "fd %d was not registered" msgstr "" -#: src/guestfs.c:2351 +#: src/guestfs.c:2360 msgid "select_main_loop_run: this cannot be called recursively" msgstr "" diff --git a/po/pl.po b/po/pl.po index 81555ec5..c5fd5de2 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: libguestfs 1.0.31\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-15 22:45+0100\n" +"POT-Creation-Date: 2009-06-16 11:22+0100\n" "PO-Revision-Date: 2009-05-26 00:50+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" @@ -338,197 +338,197 @@ msgstr "połączenie się z gniazdem kanału maszyny wirtualnej nie powiodło si msgid "could not watch qemu stdout" msgstr "nie można obserwować standardowego wyjścia QEMU" -#: src/guestfs.c:1161 +#: src/guestfs.c:1167 #, c-format msgid "external command failed: %s" msgstr "" -#: src/guestfs.c:1186 +#: src/guestfs.c:1195 msgid "qemu has finished launching already" msgstr "QEMU zakończyło już uruchamianie" -#: src/guestfs.c:1191 +#: src/guestfs.c:1200 msgid "qemu has not been launched yet" msgstr "QEMU nie zostało jeszcze uruchomione" -#: src/guestfs.c:1204 +#: src/guestfs.c:1213 msgid "guestfs_wait_ready failed, see earlier error messages" msgstr "" "guestfs_wait_ready nie powiodło się, zobacz wcześniejsze komunikaty błędów" -#: src/guestfs.c:1214 +#: src/guestfs.c:1223 msgid "qemu launched and contacted daemon, but state != READY" msgstr "" "QEMU zostało uruchomione i skontaktowano się z demonem, ale stan != GOTOWY" -#: src/guestfs.c:1225 +#: src/guestfs.c:1234 msgid "no subprocess to kill" msgstr "brak podprocesu do zniszczenia" -#: src/guestfs.c:1273 +#: src/guestfs.c:1282 #, c-format msgid "guestfs_set_ready: called when in state %d != BUSY" msgstr "guestfs_set_ready: wywołano, kiedy w stanie %d != ZAJĘTY" -#: src/guestfs.c:1285 +#: src/guestfs.c:1294 #, c-format msgid "guestfs_set_busy: called when in state %d != READY" msgstr "guestfs_set_busy: wywołano, kiedy w stanie %d != GOTOWY" -#: src/guestfs.c:1306 +#: src/guestfs.c:1315 #, c-format msgid "guestfs_end_busy: called when in state %d" msgstr "guestfs_end_busy: wywołano, kiedy w stanie %d" -#: src/guestfs.c:1395 +#: src/guestfs.c:1404 #, c-format msgid "stdout_event: internal error: %d != %d" msgstr "stdout_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1438 +#: src/guestfs.c:1447 #, c-format msgid "sock_read_event: internal error: %d != %d" msgstr "sock_read_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1468 +#: src/guestfs.c:1477 msgid "can't decode length word" msgstr "nie można dekodować długości słowa" -#: src/guestfs.c:1478 +#: src/guestfs.c:1487 #, c-format msgid "received magic signature from guestfsd, but in state %d" msgstr "otrzymano podpis magic z guestfsd, ale w stanie %d" -#: src/guestfs.c:1481 +#: src/guestfs.c:1490 #, c-format msgid "received magic signature from guestfsd, but msg size is %d" msgstr "otrzymano podpis magic z guestfsd, ale rozmiar komunikatu to %d" -#: src/guestfs.c:1506 +#: src/guestfs.c:1515 #, c-format msgid "message length (%u) > maximum possible size (%d)" msgstr "długość komunikatu (%u) > maksymalny możliwy rozmiar (%d)" -#: src/guestfs.c:1539 +#: src/guestfs.c:1548 #, c-format msgid "state %d != BUSY" msgstr "stan %d != ZAJĘTY" -#: src/guestfs.c:1579 +#: src/guestfs.c:1588 #, c-format msgid "sock_write_event: internal error: %d != %d" msgstr "sock_write_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1584 +#: src/guestfs.c:1593 #, c-format msgid "sock_write_event: state %d != BUSY" msgstr "sock_write_event: stan %d != ZAJĘTY" -#: src/guestfs.c:1688 src/guestfs.c:1711 +#: src/guestfs.c:1697 src/guestfs.c:1720 msgid "remove_handle failed" msgstr "remove_handle nie powiodło się" -#: src/guestfs.c:1699 src/guestfs.c:1722 +#: src/guestfs.c:1708 src/guestfs.c:1731 msgid "add_handle failed" msgstr "add_handle nie powiodło się" -#: src/guestfs.c:1755 +#: src/guestfs.c:1764 #, c-format msgid "guestfs__send_sync: state %d != BUSY" msgstr "guestfs__send_sync: stan %d != ZAJĘTY" -#: src/guestfs.c:1763 src/guestfs.c:1943 +#: src/guestfs.c:1772 src/guestfs.c:1952 msgid "guestfs__send_sync: msg_out should be NULL" msgstr "guestfs__send_sync: msg_out powinno być PUSTE" -#: src/guestfs.c:1785 +#: src/guestfs.c:1794 msgid "xdr_guestfs_message_header failed" msgstr "xdr_guestfs_message_header nie powiodło się" -#: src/guestfs.c:1794 +#: src/guestfs.c:1803 msgid "dispatch failed to marshal args" msgstr "rozdzielenie parametrów marszałka nie powiodło się" -#: src/guestfs.c:1820 +#: src/guestfs.c:1829 msgid "send failed, see earlier error messages" msgstr "wysłanie nie powiodło się, zobacz wcześniejsze komunikaty błędów" -#: src/guestfs.c:1935 +#: src/guestfs.c:1944 #, c-format msgid "send_file_chunk_sync: state %d != READY" msgstr "send_file_chunk_sync: stan %d != GOTOWY" -#: src/guestfs.c:1966 +#: src/guestfs.c:1975 #, c-format msgid "xdr_guestfs_chunk failed (buf = %p, buflen = %zu)" msgstr "xdr_guestfs_chunk nie powiodło się (bufor = %p, długość bufora = %zu)" -#: src/guestfs.c:1991 +#: src/guestfs.c:2000 msgid "send file chunk failed, see earlier error messages" msgstr "" "wysłanie fragmentu pliku nie powiodło się, zobacz wcześniejsze komunikaty " "błędów" -#: src/guestfs.c:2042 +#: src/guestfs.c:2051 #, c-format msgid "check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n" msgstr "" "check_for_daemon_cancellation: odczytano 0x%x z demona, oczekiwano 0x%x\n" -#: src/guestfs.c:2080 +#: src/guestfs.c:2089 #, c-format msgid "%s: error in chunked encoding" msgstr "%s: błąd w kodowaniu fragmentu" -#: src/guestfs.c:2104 +#: src/guestfs.c:2113 msgid "write to daemon socket" msgstr "zapisz do gniazda demona" -#: src/guestfs.c:2151 +#: src/guestfs.c:2160 msgid "failed to parse file chunk" msgstr "przeanalizowanie fragmentu pliku nie powiodło się" -#: src/guestfs.c:2183 +#: src/guestfs.c:2192 msgid "receive_file_data_sync: reply callback not called\n" msgstr "receive_file_data_sync: nie wywołano odpowiedzi wywołania zwrotnego\n" -#: src/guestfs.c:2188 +#: src/guestfs.c:2197 msgid "receive_file_data_sync: parse error in reply callback\n" msgstr "" "receive_file_data_sync: błąd analizy w odpowiedzi wywołania zwrotnego\n" -#: src/guestfs.c:2202 +#: src/guestfs.c:2211 msgid "file receive cancelled by daemon" msgstr "otrzymanie pliku zostało anulowane przez demona" -#: src/guestfs.c:2237 src/guestfs.c:2295 +#: src/guestfs.c:2246 src/guestfs.c:2304 #, c-format msgid "fd %d is out of range" msgstr "fd %d jest spoza zakresu" -#: src/guestfs.c:2245 +#: src/guestfs.c:2254 #, c-format msgid "set of events (0x%x) contains unknown events" msgstr "zestaw zdarzeń (0x%x) zawiera nieznane zdarzenia" -#: src/guestfs.c:2250 +#: src/guestfs.c:2259 msgid "set of events is empty" msgstr "zestaw zdarzeń jest pusty" -#: src/guestfs.c:2257 +#: src/guestfs.c:2266 #, c-format msgid "fd %d is already registered" msgstr "fd %d jest już zarejestrowane" -#: src/guestfs.c:2262 +#: src/guestfs.c:2271 msgid "callback is NULL" msgstr "wywołanie zwrotne jest PUSTE" -#: src/guestfs.c:2302 +#: src/guestfs.c:2311 #, c-format msgid "fd %d was not registered" msgstr "fd %d nie zostało zarejestrowane" -#: src/guestfs.c:2351 +#: src/guestfs.c:2360 msgid "select_main_loop_run: this cannot be called recursively" msgstr "select_main_loop_run: nie może zostać wywołane rekursywnie" -- cgit From 6cb84b6e94f424afb9e25b392148fe679d4c7d51 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Thu, 18 Jun 2009 14:27:20 +0100 Subject: Rename guestfs-supermin-helper -> libguestfs-supermin-helper. --- .gitignore | 2 +- appliance/Makefile.am | 6 +-- appliance/guestfs-supermin-helper.in | 80 --------------------------------- appliance/libguestfs-supermin-helper.in | 80 +++++++++++++++++++++++++++++++++ configure.ac | 4 +- src/guestfs.c | 2 +- 6 files changed, 87 insertions(+), 87 deletions(-) delete mode 100755 appliance/guestfs-supermin-helper.in create mode 100755 appliance/libguestfs-supermin-helper.in diff --git a/.gitignore b/.gitignore index 3e5ba214..b99ffbce 100644 --- a/.gitignore +++ b/.gitignore @@ -20,10 +20,10 @@ ChangeLog Makefile.in Makefile aclocal.m4 -appliance/guestfs-supermin-helper appliance/initramfs.*.img appliance/initramfs.*.supermin.hostfiles appliance/kmod.whitelist +appliance/libguestfs-supermin-helper appliance/make.sh appliance/supermin-make.sh appliance/supermin-split.sh diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 2c8a0be7..883bda56 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -17,7 +17,7 @@ EXTRA_DIST = \ make.sh update.sh supermin-split.sh supermin-make.sh \ - guestfs-supermin-helper \ + libguestfs-supermin-helper \ kmod.whitelist \ kmod.whitelist.in @@ -38,7 +38,7 @@ fs_DATA = $(APPLIANCE_FILES) APPLIANCE_FILES = $(INITRAMFSIMG) $(VMLINUZ) if SUPERMIN APPLIANCE_FILES += $(SUPERMINIMG) $(SUPERMINFILES) kmod.whitelist -bin_SCRIPTS = guestfs-supermin-helper +bin_SCRIPTS = libguestfs-supermin-helper endif # Don't change these names - they must be the same as in '*.sh' scripts. @@ -92,7 +92,7 @@ endif cd .. && ./config.status appliance/$@ chmod +x $@ -guestfs-supermin-helper: guestfs-supermin-helper.in +libguestfs-supermin-helper: libguestfs-supermin-helper.in cd .. && ./config.status appliance/$@ chmod +x $@ diff --git a/appliance/guestfs-supermin-helper.in b/appliance/guestfs-supermin-helper.in deleted file mode 100755 index 87fb6882..00000000 --- a/appliance/guestfs-supermin-helper.in +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash - -# @configure_input@ -# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -# Helper script which constructs the supermin appliance at runtime. - -unset CDPATH - -set -e - -# Source directory containing the supermin input files. -sourcedir=$(cd "$1" > /dev/null; pwd) - -# Output files. -kernel="$2" -initrd="$3" - -# Look for the kernel first. This is very unsophisticated: We -# just look for any kernel named vmlinuz-*.$host_cpu which has a -# corresponding /lib/modules/*.$host_cpu directory. - -for f in /boot/vmlinuz-*.@host_cpu@; do - b=$(basename "$f") - b=$(echo "$b" | sed 's,vmlinuz-,,') - modpath="/lib/modules/$b" - if [ -d "$modpath" ]; then - ln -sf "$f" "$kernel" - break - fi - modpath= -done - -if [ -z "$modpath" ]; then - echo "$0: failed to find a suitable kernel" >&2 - exit 1 -fi - -# The initrd consists of these components: -# (1) The base skeleton appliance that we constructed at build time. -# name = initramfs.@REPO@.@host_cpu@.supermin.img -# format = compressed cpio -# (2) The modules from modpath which are on the module whitelist. -# format = plain cpio -# (3) The host files which match wildcards in *.supermin.hostfiles. -# format = plain cpio - -cp "$sourcedir"/initramfs.@REPO@.@host_cpu@.supermin.img "$initrd" - -# Kernel modules (2). -exec 5<"$sourcedir"/kmod.whitelist -whitelist= -while read kmod 0<&5; do - whitelist="$whitelist -o -name $kmod" -done -exec 5<&- - -find "$modpath" \( -not -name '*.ko' $whitelist \) -a -print0 | - cpio --quiet -o -0 -H newc >> "$initrd" - -# Host files (3). - -(cd / && \ - ls -1df $( - cat "$sourcedir"/initramfs.@REPO@.@host_cpu@.supermin.hostfiles - ) 2>/dev/null | - cpio --quiet -o -H newc ) >> "$initrd" diff --git a/appliance/libguestfs-supermin-helper.in b/appliance/libguestfs-supermin-helper.in new file mode 100755 index 00000000..87fb6882 --- /dev/null +++ b/appliance/libguestfs-supermin-helper.in @@ -0,0 +1,80 @@ +#!/bin/bash - +# @configure_input@ +# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Helper script which constructs the supermin appliance at runtime. + +unset CDPATH + +set -e + +# Source directory containing the supermin input files. +sourcedir=$(cd "$1" > /dev/null; pwd) + +# Output files. +kernel="$2" +initrd="$3" + +# Look for the kernel first. This is very unsophisticated: We +# just look for any kernel named vmlinuz-*.$host_cpu which has a +# corresponding /lib/modules/*.$host_cpu directory. + +for f in /boot/vmlinuz-*.@host_cpu@; do + b=$(basename "$f") + b=$(echo "$b" | sed 's,vmlinuz-,,') + modpath="/lib/modules/$b" + if [ -d "$modpath" ]; then + ln -sf "$f" "$kernel" + break + fi + modpath= +done + +if [ -z "$modpath" ]; then + echo "$0: failed to find a suitable kernel" >&2 + exit 1 +fi + +# The initrd consists of these components: +# (1) The base skeleton appliance that we constructed at build time. +# name = initramfs.@REPO@.@host_cpu@.supermin.img +# format = compressed cpio +# (2) The modules from modpath which are on the module whitelist. +# format = plain cpio +# (3) The host files which match wildcards in *.supermin.hostfiles. +# format = plain cpio + +cp "$sourcedir"/initramfs.@REPO@.@host_cpu@.supermin.img "$initrd" + +# Kernel modules (2). +exec 5<"$sourcedir"/kmod.whitelist +whitelist= +while read kmod 0<&5; do + whitelist="$whitelist -o -name $kmod" +done +exec 5<&- + +find "$modpath" \( -not -name '*.ko' $whitelist \) -a -print0 | + cpio --quiet -o -0 -H newc >> "$initrd" + +# Host files (3). + +(cd / && \ + ls -1df $( + cat "$sourcedir"/initramfs.@REPO@.@host_cpu@.supermin.hostfiles + ) 2>/dev/null | + cpio --quiet -o -H newc ) >> "$initrd" diff --git a/configure.ac b/configure.ac index 440a2b65..6e760171 100644 --- a/configure.ac +++ b/configure.ac @@ -497,7 +497,7 @@ AC_CONFIG_FILES([Makefile appliance/Makefile appliance/make.sh appliance/update.sh appliance/supermin-split.sh appliance/supermin-make.sh - appliance/guestfs-supermin-helper + appliance/libguestfs-supermin-helper images/Makefile capitests/Makefile regressions/Makefile @@ -513,7 +513,7 @@ AC_CONFIG_FILES([Makefile AC_OUTPUT dnl WTF? -chmod +x appliance/*.sh appliance/guestfs-supermin-helper +chmod +x appliance/*.sh appliance/libguestfs-supermin-helper dnl Produce summary. echo diff --git a/src/guestfs.c b/src/guestfs.c index 20fafc12..0b562537 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -1158,7 +1158,7 @@ build_supermin_appliance (guestfs_h *g, const char *path, snprintf (cmd, sizeof cmd, "PATH='%s':$PATH " - "guestfs-supermin-helper '%s' %s %s", + "libguestfs-supermin-helper '%s' %s %s", path, path, *kernel, *initrd); -- cgit From e00b27fea31a67f3bae2f91c70ab1e6816673c4d Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Thu, 18 Jun 2009 14:37:12 +0100 Subject: Remove unreadable binaries that give warnings in supermin appliance. --- appliance/make.sh.in | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/appliance/make.sh.in b/appliance/make.sh.in index 9d77bea2..15a2f65b 100755 --- a/appliance/make.sh.in +++ b/appliance/make.sh.in @@ -88,6 +88,26 @@ rm -f $koutput # Remove /var/lib/yum stuff. @FEBOOTSTRAP_RUN@ initramfs -- rm -rf /var/lib/yum +# Remove some unreadable binaries which are incompatible with +# the supermin appliance. Since these binaries can't be read +# from the host filesystem, they generate warnings like: +# cpio: ./usr/bin/chfn: Cannot open: Permission denied +# These binaries are not needed for operation of the appliance. +@FEBOOTSTRAP_RUN@ initramfs -- rm -f \ + /usr/bin/chfn \ + /usr/bin/chsh \ + /usr/libexec/pt_chown \ + /usr/sbin/groupdel \ + /usr/sbin/groupadd \ + /usr/sbin/useradd \ + /usr/sbin/tzdata-update \ + /usr/sbin/userdel \ + /usr/sbin/usermod \ + /usr/sbin/groupmod \ + /usr/sbin/groupmems \ + /sbin/unix_update \ + $(cd initramfs && echo usr/sbin/glibc_post_upgrade.*) + # Kernel modules take up nearly half of the image. Only include ones # which are on the whitelist. exec 5 Date: Mon, 15 Jun 2009 20:27:52 +0200 Subject: check for Debian tools --- configure.ac | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/configure.ac b/configure.ac index 6e760171..11fa957b 100644 --- a/configure.ac +++ b/configure.ac @@ -105,38 +105,37 @@ fi dnl Check for febootstrap etc. AC_CHECK_PROG([FEBOOTSTRAP], [febootstrap],[febootstrap],[no]) -test "x$FEBOOTSTRAP" = "xno" && \ - AC_MSG_ERROR([febootstrap must be installed]) -AC_CHECK_PROG([FEBOOTSTRAP_RUN], +if test "x$FEBOOTSTRAP" != "xno"; then + AC_CHECK_PROG([FEBOOTSTRAP_RUN], [febootstrap-run],[febootstrap-run],[no]) -test "x$FEBOOTSTRAP_RUN" = "xno" && \ + test "x$FEBOOTSTRAP_RUN" = "xno" && \ AC_MSG_ERROR([febootstrap-run must be installed]) -AC_CHECK_PROG([FEBOOTSTRAP_INSTALL], + AC_CHECK_PROG([FEBOOTSTRAP_INSTALL], [febootstrap-install],[febootstrap-install],[no]) -test "x$FEBOOTSTRAP_INSTALL" = "xno" && \ + test "x$FEBOOTSTRAP_INSTALL" = "xno" && \ AC_MSG_ERROR([febootstrap-install must be installed]) -AC_CHECK_PROG([FEBOOTSTRAP_MINIMIZE], + AC_CHECK_PROG([FEBOOTSTRAP_MINIMIZE], [febootstrap-minimize],[febootstrap-minimize],[no]) -test "x$FEBOOTSTRAP_MINIMIZE" = "xno" && \ + test "x$FEBOOTSTRAP_MINIMIZE" = "xno" && \ AC_MSG_ERROR([febootstrap-minimize must be installed]) -AC_CHECK_PROG([FEBOOTSTRAP_TO_INITRAMFS], + AC_CHECK_PROG([FEBOOTSTRAP_TO_INITRAMFS], [febootstrap-to-initramfs],[febootstrap-to-initramfs],[no]) -test "x$FEBOOTSTRAP_TO_INITRAMFS" = "xno" && \ + test "x$FEBOOTSTRAP_TO_INITRAMFS" = "xno" && \ AC_MSG_ERROR([febootstrap-to-initramfs must be installed]) -dnl Check we have fakechroot >= 2.9 (it's an indirect requirement -dnl of febootstrap, but old versions will fail with yum). -AC_CHECK_PROG([FAKECHROOT], + dnl Check we have fakechroot >= 2.9 (it's an indirect requirement + dnl of febootstrap, but old versions will fail with yum). + AC_CHECK_PROG([FAKECHROOT], [fakechroot],[fakechroot],[no]) -test "x$FAKECHROOT" = "xno" && \ + test "x$FAKECHROOT" = "xno" && \ AC_MSG_ERROR([fakechroot must be installed]) -AC_MSG_CHECKING([fakechroot version]) -fakechroot_version=`$FAKECHROOT --version | awk '{print $3}'` -if test -z "$fakechroot_version"; then + AC_MSG_CHECKING([fakechroot version]) + fakechroot_version=`$FAKECHROOT --version | awk '{print $3}'` + if test -z "$fakechroot_version"; then AC_MSG_RESULT([failed]) AC_MSG_WARN([fakechroot --version command failed, proceeding anyway]) -else + else AC_MSG_RESULT([$fakechroot_version]) fakechroot_major=`echo $fakechroot_version | awk -F. '{print $1}'` fakechroot_minor=`echo $fakechroot_version | awk -F. '{print $2}'` @@ -144,8 +143,19 @@ else \( "$fakechroot_major" -eq 2 -a "$fakechroot_minor" -lt 9 \); then AC_MSG_ERROR([fakechroot version must be >= 2.9]) fi + fi +else + # check for debootstrap and debirf + AC_CHECK_PROG([DEBOOTSTRAP], + [debootstrap],[debootstrap],[no]) + test "x$DEBOOTSTRAP" = "xno" && \ + AC_MSG_ERROR([Either febootstrap or debootstrap must be installed]) + AC_CHECK_PROG([DEBIRF],[debirf],[debirf],[no]) + test "x$DEBIRF" = "xno" && + AC_MSG_ERROR([debirf must be installed]) fi + dnl --with-repo to specify a Fedora repository. AC_ARG_WITH([repo], [AS_HELP_STRING([--with-repo], -- cgit From e395d7db8fd3fe3d1b121258fe2f401d6b3e64c8 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Thu, 18 Jun 2009 21:06:22 +0100 Subject: Add tab-completion of guest filenames (currently disabled). --- fish/Makefile.am | 1 + fish/completion.c | 6 ++ fish/destpaths.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fish/fish.c | 16 +++++ fish/fish.h | 5 ++ guestfish.pod | 7 +++ src/generator.ml | 6 ++ 7 files changed, 214 insertions(+) create mode 100644 fish/destpaths.c diff --git a/fish/Makefile.am b/fish/Makefile.am index 5f888946..c3fbc6f1 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -21,6 +21,7 @@ guestfish_SOURCES = \ alloc.c \ cmds.c \ completion.c \ + destpaths.c \ echo.c \ edit.c \ fish.c \ diff --git a/fish/completion.c b/fish/completion.c index 264c5840..43c4f647 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -191,6 +191,8 @@ generator (const char *text, int state) len = strlen (text); } + rl_attempted_completion_over = 1; + while ((name = commands[index]) != NULL) { index++; if (strncasecmp (name, text, len) == 0) @@ -207,8 +209,12 @@ char **do_completion (const char *text, int start, int end) char **matches = NULL; #ifdef HAVE_LIBREADLINE + rl_completion_append_character = ' '; + if (start == 0) matches = rl_completion_matches (text, generator); + else if (complete_dest_paths) + matches = rl_completion_matches (text, complete_dest_paths_generator); #endif return matches; diff --git a/fish/destpaths.c b/fish/destpaths.c new file mode 100644 index 00000000..6cddafa2 --- /dev/null +++ b/fish/destpaths.c @@ -0,0 +1,173 @@ +/* guestfish - the filesystem interactive shell + * Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#define _GNU_SOURCE // for strndup, asprintf + +#include +#include +#include + +#ifdef HAVE_LIBREADLINE +#include +#endif + +#include + +#include "fish.h" + +/* Readline completion for paths on the guest filesystem, also for + * devices and LVM names. + */ + +int complete_dest_paths = 0; /* SEE NOTE */ + +/* NOTE: This is currently disabled by default (with no way to + * enable it). That's because it's not particularly natural. + * + * Also there is a quite serious performance problem. When listing + * even moderately long directories, this takes many seconds. The + * reason is because it calls guestfs_is_dir on each directory + * entry, thus lots of round trips to the server. We could have + * a "readdir and stat each entry" call to ease this. + */ + +char * +complete_dest_paths_generator (const char *text, int state) +{ +#ifdef HAVE_LIBREADLINE + + static int len, index; + static char **words = NULL; + static int nr_words = 0; + char *word; + guestfs_error_handler_cb old_error_cb; + void *old_error_cb_data; + + /* Temporarily replace the error handler so that messages don't + * get printed to stderr while we are issuing commands. + */ +#define SAVE_ERROR_CB \ + old_error_cb = guestfs_get_error_handler (g, &old_error_cb_data); \ + guestfs_set_error_handler (g, NULL, NULL); + + /* Restore error handler. */ +#define RESTORE_ERROR_CB \ + guestfs_set_error_handler (g, old_error_cb, old_error_cb_data); + + if (!state) { + char **strs; + int i, n; + + len = strlen (text); + index = 0; + + if (words) { + /* NB. 'words' array is NOT NULL-terminated. */ + for (i = 0; i < nr_words; ++i) + free (words[i]); + free (words); + } + + words = NULL; + nr_words = 0; + + SAVE_ERROR_CB + +#define APPEND_STRS_AND_FREE \ + if (strs) { \ + n = count_strings (strs); \ + words = realloc (words, sizeof (char *) * (nr_words + n)); \ + for (i = 0; i < n; ++i) \ + words[nr_words++] = strs[i]; \ + free (strs); \ + } + + /* Is it a device? */ + if (len < 5 || strncmp (text, "/dev/", 5) == 0) { + /* Get a list of everything that can possibly begin with /dev/ */ + strs = guestfs_list_devices (g); + APPEND_STRS_AND_FREE + + strs = guestfs_list_partitions (g); + APPEND_STRS_AND_FREE + + strs = guestfs_lvs (g); + APPEND_STRS_AND_FREE + } + + if (len < 1 || text[0] == '/') { + /* If we've got a partial path already, we need to list everything + * in that directory, otherwise list everything in / + */ + char *p, *dir; + + p = strrchr (text, '/'); + dir = p && p > text ? strndup (text, p - text) : strdup ("/"); + + strs = guestfs_ls (g, dir); + + /* Prepend directory to names. */ + if (strs) { + for (i = 0; strs[i]; ++i) { + p = NULL; + if (strcmp (dir, "/") == 0) + asprintf (&p, "/%s", strs[i]); + else + asprintf (&p, "%s/%s", dir, strs[i]); + free (strs[i]); + strs[i] = p; + } + } + + free (dir); + APPEND_STRS_AND_FREE + } + + /* else ... In theory we could complete other things here such as VG + * names. At the moment we don't do that. + */ + + RESTORE_ERROR_CB + } + + /* This inhibits ordinary (local filename) completion. */ + rl_attempted_completion_over = 1; + + /* Complete the string. */ + while (index < nr_words) { + word = words[index]; + index++; + if (strncasecmp (word, text, len) == 0) { + /* Is it a directory? */ + if (strncmp (word, "/dev/", 5) != 0) { + SAVE_ERROR_CB + if (guestfs_is_dir (g, word) > 0) + rl_completion_append_character = '/'; + RESTORE_ERROR_CB + } + + return strdup (word); + } + } + +#endif /* HAVE_LIBREADLINE */ + + return NULL; +} diff --git a/fish/fish.c b/fish/fish.c index da2d6d21..e66880f2 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -102,6 +102,7 @@ usage (void) " -h|--cmd-help List available commands\n" " -h|--cmd-help cmd Display detailed help on 'cmd'\n" " -a|--add image Add image\n" + " -D|--no-dest-paths Don't tab-complete paths from guest fs\n" " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n" " -n|--no-sync Don't autosync\n" " -r|--ro Mount read-only\n" @@ -119,6 +120,7 @@ main (int argc, char *argv[]) { "cmd-help", 2, 0, 'h' }, { "help", 0, 0, '?' }, { "mount", 1, 0, 'm' }, + { "no-dest-paths", 0, 0, 'D' }, { "no-sync", 0, 0, 'n' }, { "ro", 0, 0, 'r' }, { "verbose", 0, 0, 'v' }, @@ -178,6 +180,10 @@ main (int argc, char *argv[]) drvs = drv; break; + case 'D': + complete_dest_paths = 0; + break; + case 'h': if (optarg) display_command (optarg); @@ -694,6 +700,16 @@ free_strings (char **argv) free (argv); } +int +count_strings (char * const * const argv) +{ + int c; + + for (c = 0; argv[c]; ++c) + ; + return c; +} + void print_strings (char * const * const argv) { diff --git a/fish/fish.h b/fish/fish.h index 0dd1fc2e..028deecf 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -38,6 +38,7 @@ extern void pod2text (const char *heading, const char *body); extern void list_builtin_commands (void); extern void display_builtin_command (const char *cmd); extern void free_strings (char **argv); +extern int count_strings (char * const * const argv); extern void print_strings (char * const * const argv); extern void print_table (char * const * const argv); extern int launch (guestfs_h *); @@ -52,6 +53,10 @@ extern int run_action (const char *cmd, int argc, char *argv[]); /* in completion.c (auto-generated) */ extern char **do_completion (const char *text, int start, int end); +/* in destpaths.c */ +extern int complete_dest_paths; +extern char *complete_dest_paths_generator (const char *text, int state); + /* in alloc.c */ extern int do_alloc (const char *cmd, int argc, char *argv[]); diff --git a/guestfish.pod b/guestfish.pod index 28daa0ab..19c9d434 100644 --- a/guestfish.pod +++ b/guestfish.pod @@ -107,6 +107,13 @@ This changes the C<-m> option so that mounts are done read-only Enable very verbose messages. This is particularly useful if you find a bug. +=item B<-D> | B<--no-dest-paths> + +Don't tab-complete paths on the guest filesystem. It is useful to be +able to hit the tab key to complete paths on the guest filesystem, but +this causes extra "hidden" guestfs calls to be made, so this option is +here to allow this feature to be disabled. + =back =head1 COMMANDS ON COMMAND LINE diff --git a/src/generator.ml b/src/generator.ml index 0a0f9b17..d8abfd63 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -4803,6 +4803,8 @@ generator (const char *text, int state) len = strlen (text); } + rl_attempted_completion_over = 1; + while ((name = commands[index]) != NULL) { index++; if (strncasecmp (name, text, len) == 0) @@ -4819,8 +4821,12 @@ char **do_completion (const char *text, int start, int end) char **matches = NULL; #ifdef HAVE_LIBREADLINE + rl_completion_append_character = ' '; + if (start == 0) matches = rl_completion_matches (text, generator); + else if (complete_dest_paths) + matches = rl_completion_matches (text, complete_dest_paths_generator); #endif return matches; -- cgit From 677b280411cd3596051f288eecb73b5d8ae23caa Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 19 Jun 2009 14:26:44 +0100 Subject: Move distro package list to a separate packagelist.in file. --- .gitignore | 1 + appliance/Makefile.am | 9 +++++++-- appliance/make.sh.in | 31 ++++++++----------------------- appliance/packagelist.in | 28 ++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 appliance/packagelist.in diff --git a/.gitignore b/.gitignore index b99ffbce..dca51356 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ appliance/initramfs.*.supermin.hostfiles appliance/kmod.whitelist appliance/libguestfs-supermin-helper appliance/make.sh +appliance/packagelist appliance/supermin-make.sh appliance/supermin-split.sh appliance/supermin.incfiles diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 883bda56..4f5eeee0 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -19,7 +19,8 @@ EXTRA_DIST = \ make.sh update.sh supermin-split.sh supermin-make.sh \ libguestfs-supermin-helper \ kmod.whitelist \ - kmod.whitelist.in + kmod.whitelist.in \ + packagelist.in # Build the root filesystem (appliance). # Currently this is arch-dependent, so it seems like putting it in @@ -52,7 +53,7 @@ endif # This is for building the normal appliance: $(INITRAMFSIMG) $(VMLINUZ): $(top_builddir)/initramfs/fakeroot.log -$(top_builddir)/initramfs/fakeroot.log: make.sh kmod.whitelist +$(top_builddir)/initramfs/fakeroot.log: make.sh kmod.whitelist packagelist -mv $(INITRAMFSIMG) $(INITRAMFSIMG).bak -mv $(VMLINUZ) $(VMLINUZ).bak if ! bash make.sh; then rm -f $@; exit 1; fi @@ -65,6 +66,10 @@ $(INITRAMFSIMG): $(top_builddir)/initramfs/fakeroot.log $(top_builddir)/daemon/g kmod.whitelist: kmod.whitelist.in grep -v '^[[:space:]]*$$' < $< | grep -v '^#' > $@ +packagelist: packagelist.in + cpp -undef -DREDHAT=1 < $< | \ + grep -v '^[[:space:]]*$$' | grep -v '^#' > $@ + # This is for building the supermin appliance. It has to be enabled # specifically with './configure --enable-supermin'. You really need # to read the README file. diff --git a/appliance/make.sh.in b/appliance/make.sh.in index 15a2f65b..32b38194 100755 --- a/appliance/make.sh.in +++ b/appliance/make.sh.in @@ -24,28 +24,6 @@ set -e cd @top_builddir@ -modules=" --i augeas-libs --i bash --i binutils --i coreutils --i dosfstools --i file --i grub --i iputils --i kernel --i lvm2 --i MAKEDEV --i module-init-tools --i net-tools --i ntfs-3g --i ntfsprogs --i procps --i strace --i util-linux-ng --i zerofree -" - # Decide on names for the final output. These have to match Makefile.am. output=appliance/initramfs.@REPO@.@host_cpu@.img koutput=appliance/vmlinuz.@REPO@.@host_cpu@ @@ -53,7 +31,14 @@ rm -f $output rm -f $koutput # Create the basic initramfs. -@FEBOOTSTRAP@ $modules -u @UPDATES@ @REPO@ initramfs @MIRROR@ +exec 5 Date: Fri, 19 Jun 2009 14:47:22 +0100 Subject: Move init script into a separate file. --- appliance/Makefile.am | 5 +++-- appliance/init | 28 ++++++++++++++++++++++++++++ appliance/make.sh.in | 34 +--------------------------------- 3 files changed, 32 insertions(+), 35 deletions(-) create mode 100755 appliance/init diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 4f5eeee0..d692c7f4 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -20,7 +20,8 @@ EXTRA_DIST = \ libguestfs-supermin-helper \ kmod.whitelist \ kmod.whitelist.in \ - packagelist.in + packagelist.in \ + init # Build the root filesystem (appliance). # Currently this is arch-dependent, so it seems like putting it in @@ -53,7 +54,7 @@ endif # This is for building the normal appliance: $(INITRAMFSIMG) $(VMLINUZ): $(top_builddir)/initramfs/fakeroot.log -$(top_builddir)/initramfs/fakeroot.log: make.sh kmod.whitelist packagelist +$(top_builddir)/initramfs/fakeroot.log: make.sh kmod.whitelist packagelist init -mv $(INITRAMFSIMG) $(INITRAMFSIMG).bak -mv $(VMLINUZ) $(VMLINUZ).bak if ! bash make.sh; then rm -f $@; exit 1; fi diff --git a/appliance/init b/appliance/init new file mode 100755 index 00000000..41ce9166 --- /dev/null +++ b/appliance/init @@ -0,0 +1,28 @@ +#!/bin/sh +echo Starting /init script ... +PATH=/sbin:/usr/sbin:$PATH +mount -t tmpfs none /dev +mkdir /dev/pts /dev/shm /dev/mapper +MAKEDEV mem null port zero core full ram tty console fd \ + hda hdb hdc hdd sda sdb sdc sdd loop sd +mknod /dev/ptmx c 5 2; chmod 0666 /dev/ptmx +mknod /dev/random c 1 8; chmod 0666 /dev/random +mknod /dev/urandom c 1 9; chmod 0444 /dev/urandom +mount -t proc /proc /proc +mount -t sysfs /sys /sys +mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts +ln -sf /proc/self/fd/0 /dev/stdin +ln -sf /proc/self/fd/1 /dev/stdout +ln -sf /proc/self/fd/2 /dev/stderr +modprobe virtio_pci +modprobe virtio_net +modprobe dm_mod ||: +/sbin/ifconfig lo 127.0.0.1 +/sbin/ifconfig eth0 10.0.2.10 +/sbin/route add default gw 10.0.2.2 +lvm vgscan --ignorelockingfailure +lvm vgchange -ay --ignorelockingfailure +if grep -sq guestfs_rescue=1 /proc/cmdline; then + bash -i +fi +exec guestfsd -f diff --git a/appliance/make.sh.in b/appliance/make.sh.in index 32b38194..637bd07e 100755 --- a/appliance/make.sh.in +++ b/appliance/make.sh.in @@ -133,39 +133,7 @@ echo nameserver 10.0.2.3 > resolv.conf.new rm resolv.conf.new # Create the init script. -cat > init.new <<'__EOF__' -#!/bin/sh -echo Starting /init script ... -PATH=/sbin:/usr/sbin:$PATH -mount -t tmpfs none /dev -mkdir /dev/pts /dev/shm /dev/mapper -MAKEDEV mem null port zero core full ram tty console fd \ - hda hdb hdc hdd sda sdb sdc sdd loop sd -mknod /dev/ptmx c 5 2; chmod 0666 /dev/ptmx -mknod /dev/random c 1 8; chmod 0666 /dev/random -mknod /dev/urandom c 1 9; chmod 0444 /dev/urandom -mount -t proc /proc /proc -mount -t sysfs /sys /sys -mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts -ln -sf /proc/self/fd/0 /dev/stdin -ln -sf /proc/self/fd/1 /dev/stdout -ln -sf /proc/self/fd/2 /dev/stderr -modprobe virtio_pci -modprobe virtio_net -modprobe dm_mod ||: -/sbin/ifconfig lo 127.0.0.1 -/sbin/ifconfig eth0 10.0.2.10 -/sbin/route add default gw 10.0.2.2 -lvm vgscan --ignorelockingfailure -lvm vgchange -ay --ignorelockingfailure -if grep -sq guestfs_rescue=1 /proc/cmdline; then - bash -i -fi -exec guestfsd -f -__EOF__ - -@FEBOOTSTRAP_INSTALL@ initramfs init.new /init 0755 root.root -rm init.new +@FEBOOTSTRAP_INSTALL@ initramfs appliance/init /init 0755 root.root # Just in case the kernel isn't looking for /init, make /sbin/init # be our script, not the real init. -- cgit From e250646e7cc99df9bb617c28d13c327ff79ab601 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 19 Jun 2009 18:25:38 +0100 Subject: Supermin: choose newest available kernel. --- appliance/libguestfs-supermin-helper.in | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/appliance/libguestfs-supermin-helper.in b/appliance/libguestfs-supermin-helper.in index 87fb6882..d7c8d96b 100755 --- a/appliance/libguestfs-supermin-helper.in +++ b/appliance/libguestfs-supermin-helper.in @@ -29,11 +29,13 @@ sourcedir=$(cd "$1" > /dev/null; pwd) kernel="$2" initrd="$3" -# Look for the kernel first. This is very unsophisticated: We -# just look for any kernel named vmlinuz-*.$host_cpu which has a -# corresponding /lib/modules/*.$host_cpu directory. +# Kernel: +# Look for any kernel named vmlinuz-*.$host_cpu* which has +# a corresponding /lib/modules/*.$host_cpu* directory. +# However by sorting on reverse version (ls -vr) we ensure +# we choose the newest kernels. -for f in /boot/vmlinuz-*.@host_cpu@; do +for f in $(ls -1vr /boot/vmlinuz-*.@host_cpu@* | grep -v xen); do b=$(basename "$f") b=$(echo "$b" | sed 's,vmlinuz-,,') modpath="/lib/modules/$b" -- cgit From 606d8ccee17c27e6ecf6fcfc9d05c5758ba77611 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 19 Jun 2009 19:28:51 +0100 Subject: Prepare for version 1.0.49. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 11fa957b..5cb7c6de 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.48]) +AC_INIT([libguestfs],[1.0.49]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) -- cgit From 3216e9505da3652afad20c0a4612e931359117c2 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 19 Jun 2009 19:48:05 +0100 Subject: Generated files for 1.0.49. --- po/libguestfs.pot | 47 ++++++++++++++++++++++++----------------------- po/pl.po | 49 +++++++++++++++++++++++++------------------------ 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/po/libguestfs.pot b/po/libguestfs.pot index 8fa95295..090d4d37 100644 --- a/po/libguestfs.pot +++ b/po/libguestfs.pot @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-16 11:22+0100\n" +"POT-Creation-Date: 2009-06-19 19:37+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -56,6 +56,7 @@ msgid "" " -h|--cmd-help List available commands\n" " -h|--cmd-help cmd Display detailed help on 'cmd'\n" " -a|--add image Add image\n" +" -D|--no-dest-paths Don't tab-complete paths from guest fs\n" " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n" " -n|--no-sync Don't autosync\n" " -r|--ro Mount read-only\n" @@ -64,17 +65,17 @@ msgid "" "For more information, see the manpage guestfish(1).\n" msgstr "" -#: fish/fish.c:142 +#: fish/fish.c:144 #, c-format msgid "guestfs_create: failed to create handle\n" msgstr "" -#: fish/fish.c:229 +#: fish/fish.c:235 #, c-format msgid "guestfish: unexpected command line option 0x%x\n" msgstr "" -#: fish/fish.c:375 +#: fish/fish.c:381 #, c-format msgid "" "\n" @@ -86,61 +87,61 @@ msgid "" "\n" msgstr "" -#: fish/fish.c:452 +#: fish/fish.c:458 #, c-format msgid "guestfish: unterminated double quote\n" msgstr "" -#: fish/fish.c:457 fish/fish.c:472 +#: fish/fish.c:463 fish/fish.c:478 #, c-format msgid "guestfish: command arguments not separated by whitespace\n" msgstr "" -#: fish/fish.c:467 +#: fish/fish.c:473 #, c-format msgid "guestfish: unterminated single quote\n" msgstr "" -#: fish/fish.c:508 +#: fish/fish.c:514 #, c-format msgid "guestfish: internal error parsing string at '%s'\n" msgstr "" -#: fish/fish.c:521 +#: fish/fish.c:527 #, c-format msgid "guestfish: too many arguments\n" msgstr "" -#: fish/fish.c:548 +#: fish/fish.c:554 #, c-format msgid "guestfish: empty command on command line\n" msgstr "" -#: fish/fish.c:607 +#: fish/fish.c:613 msgid "display a list of commands or help on a command" msgstr "" -#: fish/fish.c:609 +#: fish/fish.c:615 msgid "quit guestfish" msgstr "" -#: fish/fish.c:612 +#: fish/fish.c:618 msgid "allocate an image" msgstr "" -#: fish/fish.c:614 +#: fish/fish.c:620 msgid "display a line of text" msgstr "" -#: fish/fish.c:616 +#: fish/fish.c:622 msgid "edit a file in the image" msgstr "" -#: fish/fish.c:618 +#: fish/fish.c:624 msgid "local change directory" msgstr "" -#: fish/fish.c:630 +#: fish/fish.c:636 #, c-format msgid "" "alloc - allocate an image\n" @@ -160,7 +161,7 @@ msgid "" " sects number of 512 byte sectors\n" msgstr "" -#: fish/fish.c:646 +#: fish/fish.c:652 #, c-format msgid "" "echo - display a line of text\n" @@ -169,7 +170,7 @@ msgid "" " This echos the parameters to the terminal.\n" msgstr "" -#: fish/fish.c:653 +#: fish/fish.c:659 #, c-format msgid "" "edit - edit a file in the image\n" @@ -187,7 +188,7 @@ msgid "" " (> 2 MB) or binary files containing \\0 bytes.\n" msgstr "" -#: fish/fish.c:667 +#: fish/fish.c:673 #, c-format msgid "" "lcd - local change directory\n" @@ -198,7 +199,7 @@ msgid "" " place.\n" msgstr "" -#: fish/fish.c:674 +#: fish/fish.c:680 #, c-format msgid "" "help - display a list of commands or help on a command\n" @@ -206,14 +207,14 @@ msgid "" " help\n" msgstr "" -#: fish/fish.c:680 +#: fish/fish.c:686 #, c-format msgid "" "quit - quit guestfish\n" " quit\n" msgstr "" -#: fish/fish.c:683 +#: fish/fish.c:689 #, c-format msgid "%s: command not known, use -h to list all commands\n" msgstr "" diff --git a/po/pl.po b/po/pl.po index c5fd5de2..12a01028 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: libguestfs 1.0.31\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-16 11:22+0100\n" +"POT-Creation-Date: 2009-06-19 19:37+0100\n" "PO-Revision-Date: 2009-05-26 00:50+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" @@ -35,7 +35,7 @@ msgid "use '%s filename' to edit a file\n" msgstr "użyj \"%s nazwapliku\", aby zmodyfikować plik\n" #: fish/fish.c:89 -#, c-format +#, fuzzy, c-format msgid "" "guestfish: guest filesystem shell\n" "guestfish lets you edit virtual machine filesystems\n" @@ -53,6 +53,7 @@ msgid "" " -h|--cmd-help List available commands\n" " -h|--cmd-help cmd Display detailed help on 'cmd'\n" " -a|--add image Add image\n" +" -D|--no-dest-paths Don't tab-complete paths from guest fs\n" " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n" " -n|--no-sync Don't autosync\n" " -r|--ro Mount read-only\n" @@ -83,17 +84,17 @@ msgstr "" " -V|--version Wyświetla wersję i kończy pracę\n" "Aby dowiedzieć się więcej, zobacz stronę podręcznika guestfish(1).\n" -#: fish/fish.c:142 +#: fish/fish.c:144 #, c-format msgid "guestfs_create: failed to create handle\n" msgstr "guestfs_create: utworzenie programu obsługi nie powiodło się\n" -#: fish/fish.c:229 +#: fish/fish.c:235 #, c-format msgid "guestfish: unexpected command line option 0x%x\n" msgstr "guestfish: nieoczekiwane polecenie wiersza poleceń 0x%x\n" -#: fish/fish.c:375 +#: fish/fish.c:381 #, c-format msgid "" "\n" @@ -112,61 +113,61 @@ msgstr "" " \"quit\", aby zakończyć powłokę\n" "\n" -#: fish/fish.c:452 +#: fish/fish.c:458 #, c-format msgid "guestfish: unterminated double quote\n" msgstr "guestfish: niezakończony podwójny cudzysłów\n" -#: fish/fish.c:457 fish/fish.c:472 +#: fish/fish.c:463 fish/fish.c:478 #, c-format msgid "guestfish: command arguments not separated by whitespace\n" msgstr "guestfish: parametry poleceń nie są oddzielone spacjami\n" -#: fish/fish.c:467 +#: fish/fish.c:473 #, c-format msgid "guestfish: unterminated single quote\n" msgstr "guestfish: niezakończony pojedynczy cudzysłów\n" -#: fish/fish.c:508 +#: fish/fish.c:514 #, c-format msgid "guestfish: internal error parsing string at '%s'\n" msgstr "guestfish: wewnętrzny błąd analizowania łańcucha \"%s\"\n" -#: fish/fish.c:521 +#: fish/fish.c:527 #, c-format msgid "guestfish: too many arguments\n" msgstr "guestfish: za dużo parametrów\n" -#: fish/fish.c:548 +#: fish/fish.c:554 #, c-format msgid "guestfish: empty command on command line\n" msgstr "guestfish: puste polecenie wiersza poleceń\n" -#: fish/fish.c:607 +#: fish/fish.c:613 msgid "display a list of commands or help on a command" msgstr "wyświetla listę poleceń lub pomoc polecenia" -#: fish/fish.c:609 +#: fish/fish.c:615 msgid "quit guestfish" msgstr "kończy pracę guestfish" -#: fish/fish.c:612 +#: fish/fish.c:618 msgid "allocate an image" msgstr "przydziela obraz" -#: fish/fish.c:614 +#: fish/fish.c:620 msgid "display a line of text" msgstr "wyświetla wiersz tekstu" -#: fish/fish.c:616 +#: fish/fish.c:622 msgid "edit a file in the image" msgstr "modyfikuje plik w obrazie" -#: fish/fish.c:618 +#: fish/fish.c:624 msgid "local change directory" msgstr "" -#: fish/fish.c:630 +#: fish/fish.c:636 #, c-format msgid "" "alloc - allocate an image\n" @@ -202,7 +203,7 @@ msgstr "" " G lub GB liczba gigabajtów\n" " sektory liczba 512 bajtowych sektorów\n" -#: fish/fish.c:646 +#: fish/fish.c:652 #, c-format msgid "" "echo - display a line of text\n" @@ -215,7 +216,7 @@ msgstr "" "\n" " Wyświetla ostatnie parametry w terminalu.\n" -#: fish/fish.c:653 +#: fish/fish.c:659 #, c-format msgid "" "edit - edit a file in the image\n" @@ -247,7 +248,7 @@ msgstr "" " UWAGA: nie będzie działało poprawnie dla dużych plików\n" " (> 2 MB) lub plików binarnych zawierających \\0 bajtów.\n" -#: fish/fish.c:667 +#: fish/fish.c:673 #, c-format msgid "" "lcd - local change directory\n" @@ -258,7 +259,7 @@ msgid "" " place.\n" msgstr "" -#: fish/fish.c:674 +#: fish/fish.c:680 #, c-format msgid "" "help - display a list of commands or help on a command\n" @@ -269,7 +270,7 @@ msgstr "" " help polecenie\n" " help\n" -#: fish/fish.c:680 +#: fish/fish.c:686 #, c-format msgid "" "quit - quit guestfish\n" @@ -278,7 +279,7 @@ msgstr "" "quit - kończy pracę guestfish\n" " quit\n" -#: fish/fish.c:683 +#: fish/fish.c:689 #, c-format msgid "%s: command not known, use -h to list all commands\n" msgstr "" -- cgit From 65acb799d2c94e6628f647f2319eb8d16f29a0d5 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 19 Jun 2009 19:48:19 +0100 Subject: Update to-do list. --- TODO | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO b/TODO index 1e265c22..ad9e6e9b 100644 --- a/TODO +++ b/TODO @@ -122,3 +122,7 @@ PPC problems: no serial console in ppc or ppc64 because no one can tell us what console=ttyXX option to use + +---------------------------------------------------------------------- + +Supermin appliance should be moved into febootstrap. -- cgit From 3c5b447efd42b03c24104bdc1f3260e879bb1d25 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 20 Jun 2009 14:55:49 +0100 Subject: Check return value from pclose. --- daemon/find.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon/find.c b/daemon/find.c index a873bca0..287aae39 100644 --- a/daemon/find.c +++ b/daemon/find.c @@ -113,7 +113,11 @@ do_find (char *dir) return NULL; } } - pclose (fp); + if (pclose (fp) == -1) { + reply_with_perror ("pclose: find"); + free_stringslen (res, size); + return NULL; + } if (r == -1) { free_stringslen (res, size); -- cgit From 3ab8336db394683dad5f485388461e9146ac04ad Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 20 Jun 2009 15:30:39 +0100 Subject: Add strong note about deprecation of functions which take either device names or filenames. --- daemon/daemon.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/daemon/daemon.h b/daemon/daemon.h index c3b91209..115db093 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -139,7 +139,13 @@ extern void reply (xdrproc_t xdrp, char *ret); /* Helper for functions which need either an absolute path in the * mounted filesystem, OR a /dev/ device which exists. + * * NB. Cannot be used for FileIn functions. + * + * NB #2: Functions which mix filenames and device paths should be + * avoided, and existing functions should be deprecated. This is + * because we intend in future to make device parameters a distinct + * type from filenames. */ #define NEED_ROOT_OR_IS_DEVICE(path,errcode) \ do { \ -- cgit From 6654f617a6f720baa8f1ced89179e11679353d1e Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 20 Jun 2009 16:05:12 +0100 Subject: Check return value from waitpid call in command*() functions. --- daemon/guestfsd.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/daemon/guestfsd.c b/daemon/guestfsd.c index 2e83b9fa..030aabea 100644 --- a/daemon/guestfsd.c +++ b/daemon/guestfsd.c @@ -453,7 +453,8 @@ commandrv (char **stdoutput, char **stderror, char * const* const argv) { int so_size = 0, se_size = 0; int so_fd[2], se_fd[2]; - int pid, r, quit, i; + pid_t pid; + int r, quit, i; fd_set rset, rset2; char buf[256]; char *p; @@ -589,7 +590,10 @@ commandrv (char **stdoutput, char **stderror, char * const* const argv) } /* Get the exit status of the command. */ - waitpid (pid, &r, 0); + if (waitpid (pid, &r, 0) != pid) { + perror ("waitpid"); + return -1; + } if (WIFEXITED (r)) { return WEXITSTATUS (r); -- cgit From 57d2dfab18ad3d987d9273bb7c1f42e73e0bbcb2 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 07:49:37 +0100 Subject: Add 'sh' and 'sh-lines' commands. --- daemon/command.c | 16 ++++++++++++++++ src/generator.ml | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/daemon/command.c b/daemon/command.c index 3254de58..b33f2dbe 100644 --- a/daemon/command.c +++ b/daemon/command.c @@ -101,3 +101,19 @@ do_command_lines (char **argv) return lines; /* Caller frees. */ } + +char * +do_sh (char *command) +{ + char *argv[] = { "/bin/sh", "-c", command, NULL }; + + return do_command (argv); +} + +char ** +do_sh_lines (char *command) +{ + char *argv[] = { "/bin/sh", "-c", command, NULL }; + + return do_command_lines (argv); +} diff --git a/src/generator.ml b/src/generator.ml index d8abfd63..64a8ab92 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -1370,7 +1370,9 @@ or compatible processor architecture). The single parameter is an argv-style list of arguments. The first element is the name of the program to run. Subsequent elements are parameters. The list must be -non-empty (ie. must contain a program name). +non-empty (ie. must contain a program name). Note that +the command runs directly, and is I invoked via +the shell (see C). The return value is anything printed to I by the command. @@ -1438,7 +1440,9 @@ locations."); "run a command, returning lines", "\ This is the same as C, but splits the -result into a list of lines."); +result into a list of lines. + +See also: C"); ("stat", (RStat "statbuf", [String "path"]), 52, [], [InitBasicFS, Always, TestOutputStruct ( @@ -2343,6 +2347,33 @@ The return value is an integer which C<0> if the operation would succeed, or some non-zero value documented in the L manual page."); + ("sh", (RString "output", [String "command"]), 111, [], + [], (* XXX needs tests *) + "run a command via the shell", + "\ +This call runs a command from the guest filesystem via the +guest's C. + +This is like C, but passes the command to: + + /bin/sh -c \"command\" + +Depending on the guest's shell, this usually results in +wildcards being expanded, shell expressions being interpolated +and so on. + +All the provisos about C apply to this call."); + + ("sh_lines", (RStringList "lines", [String "command"]), 112, [], + [], (* XXX needs tests *) + "run a command via the shell returning lines", + "\ +This is the same as C, but splits the result +into a list of lines. + +See also: C"); + + ] let all_functions = non_daemon_functions @ daemon_functions -- cgit From 4211c7a258debd236017a19c70965bc1b3658edb Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 07:49:50 +0100 Subject: Generated code for 'sh' and 'sh-lines' commands. --- capitests/tests.c | 2 + daemon/actions.h | 2 + daemon/stubs.c | 61 ++++++++++ fish/cmds.c | 52 +++++++- fish/completion.c | 2 + guestfish-actions.pod | 32 ++++- guestfs-actions.pod | 41 ++++++- java/com/redhat/et/libguestfs/GuestFS.java | 54 ++++++++- java/com_redhat_et_libguestfs_GuestFS.c | 54 +++++++++ ocaml/guestfs.ml | 2 + ocaml/guestfs.mli | 6 + ocaml/guestfs_c_actions.c | 50 ++++++++ perl/Guestfs.xs | 34 ++++++ perl/lib/Sys/Guestfs.pm | 28 ++++- python/guestfs-py.c | 52 ++++++++ python/guestfs.py | 32 ++++- ruby/ext/guestfs/_guestfs.c | 56 +++++++++ src/guestfs-actions.c | 189 +++++++++++++++++++++++++++++ src/guestfs-actions.h | 2 + src/guestfs_protocol.c | 41 +++++++ src/guestfs_protocol.h | 35 +++++- src/guestfs_protocol.x | 18 +++ 22 files changed, 837 insertions(+), 8 deletions(-) diff --git a/capitests/tests.c b/capitests/tests.c index 0e42cb40..e5466b5e 100644 --- a/capitests/tests.c +++ b/capitests/tests.c @@ -148,6 +148,8 @@ static void no_test_warnings (void) fprintf (stderr, "warning: \"guestfs_vg_activate\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_resize2fs\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_e2fsck_f\" has no tests\n"); + fprintf (stderr, "warning: \"guestfs_sh\" has no tests\n"); + fprintf (stderr, "warning: \"guestfs_sh_lines\" has no tests\n"); } static int test_ntfs_3g_probe_0_skip (void) diff --git a/daemon/actions.h b/daemon/actions.h index c8e285c8..a76498f3 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -131,3 +131,5 @@ extern char **do_find (char *directory); extern int do_e2fsck_f (char *device); extern int do_sleep (int secs); extern int do_ntfs_3g_probe (int rw, char *device); +extern char *do_sh (char *command); +extern char **do_sh_lines (char *command); diff --git a/daemon/stubs.c b/daemon/stubs.c index 78426409..dcdc51c0 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -2771,6 +2771,61 @@ done: xdr_free ((xdrproc_t) xdr_guestfs_ntfs_3g_probe_args, (char *) &args); } +static void sh_stub (XDR *xdr_in) +{ + char *r; + struct guestfs_sh_args args; + char *command; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_sh_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "sh"); + return; + } + command = args.command; + + r = do_sh (command); + if (r == NULL) + /* do_sh has already called reply_with_error */ + goto done; + + struct guestfs_sh_ret ret; + ret.output = r; + reply ((xdrproc_t) &xdr_guestfs_sh_ret, (char *) &ret); + free (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_sh_args, (char *) &args); +} + +static void sh_lines_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_sh_lines_args args; + char *command; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_sh_lines_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "sh_lines"); + return; + } + command = args.command; + + r = do_sh_lines (command); + if (r == NULL) + /* do_sh_lines has already called reply_with_error */ + goto done; + + struct guestfs_sh_lines_ret ret; + ret.lines.lines_len = count_strings (r); + ret.lines.lines_val = r; + reply ((xdrproc_t) &xdr_guestfs_sh_lines_ret, (char *) &ret); + free_strings (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_sh_lines_args, (char *) &args); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -3104,6 +3159,12 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_NTFS_3G_PROBE: ntfs_3g_probe_stub (xdr_in); break; + case GUESTFS_PROC_SH: + sh_stub (xdr_in); + break; + case GUESTFS_PROC_SH_LINES: + sh_lines_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr); } diff --git a/fish/cmds.c b/fish/cmds.c index 334d7154..fe1ef92e 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -138,6 +138,8 @@ void list_commands (void) printf ("%-20s %s\n", "sfdisk-disk-geometry", "display the disk geometry from the partition table"); printf ("%-20s %s\n", "sfdisk-kernel-geometry", "display the kernel geometry"); printf ("%-20s %s\n", "sfdisk-l", "display the partition table"); + printf ("%-20s %s\n", "sh", "run a command via the shell"); + printf ("%-20s %s\n", "sh-lines", "run a command via the shell returning lines"); printf ("%-20s %s\n", "sleep", "sleep for some seconds"); printf ("%-20s %s\n", "stat", "get file information"); printf ("%-20s %s\n", "statvfs", "get file system statistics"); @@ -378,10 +380,10 @@ void display_command (const char *cmd) pod2text ("file - determine file type", " file \n\nThis call uses the standard L command to determine\nthe type or contents of the file. This also works on devices,\nfor example to find out whether a partition contains a filesystem.\n\nThe exact command which runs is C. Note in\nparticular that the filename is not prepended to the output\n(the C<-b> option)."); else if (strcasecmp (cmd, "command") == 0) - pod2text ("command - run a command from the guest filesystem", " command \n\nThis call runs a command from the guest filesystem. The\nfilesystem must be mounted, and must contain a compatible\noperating system (ie. something Linux, with the same\nor compatible processor architecture).\n\nThe single parameter is an argv-style list of arguments.\nThe first element is the name of the program to run.\nSubsequent elements are parameters. The list must be\nnon-empty (ie. must contain a program name).\n\nThe return value is anything printed to I by\nthe command.\n\nIf the command returns a non-zero exit status, then\nthis function returns an error message. The error message\nstring is the content of I from the command.\n\nThe C<$PATH> environment variable will contain at least\nC and C. If you require a program from\nanother location, you should provide the full path in the\nfirst parameter.\n\nShared libraries and data files required by the program\nmust be available on filesystems which are mounted in the\ncorrect places. It is the caller's responsibility to ensure\nall filesystems that are needed are mounted at the right\nlocations.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); + pod2text ("command - run a command from the guest filesystem", " command \n\nThis call runs a command from the guest filesystem. The\nfilesystem must be mounted, and must contain a compatible\noperating system (ie. something Linux, with the same\nor compatible processor architecture).\n\nThe single parameter is an argv-style list of arguments.\nThe first element is the name of the program to run.\nSubsequent elements are parameters. The list must be\nnon-empty (ie. must contain a program name). Note that\nthe command runs directly, and is I invoked via\nthe shell (see C).\n\nThe return value is anything printed to I by\nthe command.\n\nIf the command returns a non-zero exit status, then\nthis function returns an error message. The error message\nstring is the content of I from the command.\n\nThe C<$PATH> environment variable will contain at least\nC and C. If you require a program from\nanother location, you should provide the full path in the\nfirst parameter.\n\nShared libraries and data files required by the program\nmust be available on filesystems which are mounted in the\ncorrect places. It is the caller's responsibility to ensure\nall filesystems that are needed are mounted at the right\nlocations.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); else if (strcasecmp (cmd, "command_lines") == 0 || strcasecmp (cmd, "command-lines") == 0) - pod2text ("command-lines - run a command, returning lines", " command-lines \n\nThis is the same as C, but splits the\nresult into a list of lines.\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); + pod2text ("command-lines - run a command, returning lines", " command-lines \n\nThis is the same as C, but splits the\nresult into a list of lines.\n\nSee also: C\n\nBecause of the message protocol, there is a transfer limit \nof somewhere between 2MB and 4MB. To transfer large files you should use\nFTP."); else if (strcasecmp (cmd, "stat") == 0) pod2text ("stat - get file information", " stat \n\nReturns file information for the given C.\n\nThis is the same as the C system call."); @@ -559,6 +561,12 @@ void display_command (const char *cmd) else if (strcasecmp (cmd, "ntfs_3g_probe") == 0 || strcasecmp (cmd, "ntfs-3g-probe") == 0) pod2text ("ntfs-3g-probe - probe NTFS volume", " ntfs-3g-probe \n\nThis command runs the L command which probes\nan NTFS C for mountability. (Not all NTFS volumes can\nbe mounted read-write, and some cannot be mounted at all).\n\nC is a boolean flag. Set it to true if you want to test\nif the volume can be mounted read-write. Set it to false if\nyou want to test if the volume can be mounted read-only.\n\nThe return value is an integer which C<0> if the operation\nwould succeed, or some non-zero value documented in the\nL manual page."); + else + if (strcasecmp (cmd, "sh") == 0) + pod2text ("sh - run a command via the shell", " sh \n\nThis call runs a command from the guest filesystem via the\nguest's C.\n\nThis is like C, but passes the command to:\n\n /bin/sh -c \"command\"\n\nDepending on the guest's shell, this usually results in\nwildcards being expanded, shell expressions being interpolated\nand so on.\n\nAll the provisos about C apply to this call."); + else + if (strcasecmp (cmd, "sh_lines") == 0 || strcasecmp (cmd, "sh-lines") == 0) + pod2text ("sh-lines - run a command via the shell returning lines", " sh-lines \n\nThis is the same as C, but splits the result\ninto a list of lines.\n\nSee also: C"); else display_builtin_command (cmd); } @@ -2738,6 +2746,40 @@ static int run_ntfs_3g_probe (const char *cmd, int argc, char *argv[]) return 0; } +static int run_sh (const char *cmd, int argc, char *argv[]) +{ + char *r; + const char *command; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + command = argv[0]; + r = guestfs_sh (g, command); + if (r == NULL) return -1; + printf ("%s\n", r); + free (r); + return 0; +} + +static int run_sh_lines (const char *cmd, int argc, char *argv[]) +{ + char **r; + const char *command; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + command = argv[0]; + r = guestfs_sh_lines (g, command); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -3132,6 +3174,12 @@ int run_action (const char *cmd, int argc, char *argv[]) else if (strcasecmp (cmd, "ntfs_3g_probe") == 0 || strcasecmp (cmd, "ntfs-3g-probe") == 0) return run_ntfs_3g_probe (cmd, argc, argv); + else + if (strcasecmp (cmd, "sh") == 0) + return run_sh (cmd, argc, argv); + else + if (strcasecmp (cmd, "sh_lines") == 0 || strcasecmp (cmd, "sh-lines") == 0) + return run_sh_lines (cmd, argc, argv); else { fprintf (stderr, "%s: unknown command\n", cmd); diff --git a/fish/completion.c b/fish/completion.c index 43c4f647..4ac0fad9 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -177,6 +177,8 @@ static const char *const commands[] = { "e2fsck-f", "sleep", "ntfs-3g-probe", + "sh", + "sh-lines", NULL }; diff --git a/guestfish-actions.pod b/guestfish-actions.pod index ee3f6411..3217d922 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -401,7 +401,9 @@ or compatible processor architecture). The single parameter is an argv-style list of arguments. The first element is the name of the program to run. Subsequent elements are parameters. The list must be -non-empty (ie. must contain a program name). +non-empty (ie. must contain a program name). Note that +the command runs directly, and is I invoked via +the shell (see C). The return value is anything printed to I by the command. @@ -432,6 +434,8 @@ FTP. This is the same as C, but splits the result into a list of lines. +See also: C + Because of the message protocol, there is a transfer limit of somewhere between 2MB and 4MB. To transfer large files you should use FTP. @@ -1226,6 +1230,32 @@ This displays the partition table on C, in the human-readable output of the L command. It is not intended to be parsed. +=head2 sh + + sh command + +This call runs a command from the guest filesystem via the +guest's C. + +This is like C, but passes the command to: + + /bin/sh -c "command" + +Depending on the guest's shell, this usually results in +wildcards being expanded, shell expressions being interpolated +and so on. + +All the provisos about C apply to this call. + +=head2 sh-lines + + sh-lines command + +This is the same as C, but splits the result +into a list of lines. + +See also: C + =head2 sleep sleep secs diff --git a/guestfs-actions.pod b/guestfs-actions.pod index f37ec50b..93a9fbb7 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -511,7 +511,9 @@ or compatible processor architecture). The single parameter is an argv-style list of arguments. The first element is the name of the program to run. Subsequent elements are parameters. The list must be -non-empty (ie. must contain a program name). +non-empty (ie. must contain a program name). Note that +the command runs directly, and is I invoked via +the shell (see C). The return value is anything printed to I by the command. @@ -546,6 +548,8 @@ FTP. This is the same as C, but splits the result into a list of lines. +See also: C + This function returns a NULL-terminated array of strings (like L), or NULL if there was an error. I. @@ -1649,6 +1653,41 @@ not intended to be parsed. This function returns a string, or NULL on error. I. +=head2 guestfs_sh + + char *guestfs_sh (guestfs_h *handle, + const char *command); + +This call runs a command from the guest filesystem via the +guest's C. + +This is like C, but passes the command to: + + /bin/sh -c "command" + +Depending on the guest's shell, this usually results in +wildcards being expanded, shell expressions being interpolated +and so on. + +All the provisos about C apply to this call. + +This function returns a string, or NULL on error. +I. + +=head2 guestfs_sh_lines + + char **guestfs_sh_lines (guestfs_h *handle, + const char *command); + +This is the same as C, but splits the result +into a list of lines. + +See also: C + +This function returns a NULL-terminated array of strings +(like L), or NULL if there was an error. +I. + =head2 guestfs_sleep int guestfs_sleep (guestfs_h *handle, diff --git a/java/com/redhat/et/libguestfs/GuestFS.java b/java/com/redhat/et/libguestfs/GuestFS.java index d29ae54f..40754204 100644 --- a/java/com/redhat/et/libguestfs/GuestFS.java +++ b/java/com/redhat/et/libguestfs/GuestFS.java @@ -1998,7 +1998,9 @@ public HashMap test0rhashtableerr () * The single parameter is an argv-style list of arguments. * The first element is the name of the program to run. * Subsequent elements are parameters. The list must be - * non-empty (ie. must contain a program name). + * non-empty (ie. must contain a program name). Note that + * the command runs directly, and is *not* invoked via the + * shell (see "g.sh"). *

* The return value is anything printed to *stdout* by the * command. @@ -2040,6 +2042,8 @@ public HashMap test0rhashtableerr () * This is the same as "g.command", but splits the result * into a list of lines. *

+ * See also: "g.sh_lines" + *

* Because of the message protocol, there is a transfer * limit of somewhere between 2MB and 4MB. To transfer * large files you should use FTP. @@ -3383,4 +3387,52 @@ public HashMap test0rhashtableerr () private native int _ntfs_3g_probe (long g, boolean rw, String device) throws LibGuestFSException; + /** + * run a command via the shell + *

+ * This call runs a command from the guest filesystem via + * the guest's "/bin/sh". + *

+ * This is like "g.command", but passes the command to: + *

+ * /bin/sh -c "command" + *

+ * Depending on the guest's shell, this usually results in + * wildcards being expanded, shell expressions being + * interpolated and so on. + *

+ * All the provisos about "g.command" apply to this call. + *

+ * @throws LibGuestFSException + */ + public String sh (String command) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("sh: handle is closed"); + return _sh (g, command); + } + private native String _sh (long g, String command) + throws LibGuestFSException; + + /** + * run a command via the shell returning lines + *

+ * This is the same as "g.sh", but splits the result into a + * list of lines. + *

+ * See also: "g.command_lines" + *

+ * @throws LibGuestFSException + */ + public String[] sh_lines (String command) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("sh_lines: handle is closed"); + return _sh_lines (g, command); + } + private native String[] _sh_lines (long g, String command) + throws LibGuestFSException; + } diff --git a/java/com_redhat_et_libguestfs_GuestFS.c b/java/com_redhat_et_libguestfs_GuestFS.c index 91e3cf19..631e48f6 100644 --- a/java/com_redhat_et_libguestfs_GuestFS.c +++ b/java/com_redhat_et_libguestfs_GuestFS.c @@ -3986,3 +3986,57 @@ Java_com_redhat_et_libguestfs_GuestFS__1ntfs_13g_1probe return (jint) r; } +JNIEXPORT jstring JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1sh + (JNIEnv *env, jobject obj, jlong jg, jstring jcommand) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jstring jr; + char *r; + const char *command; + + command = (*env)->GetStringUTFChars (env, jcommand, NULL); + r = guestfs_sh (g, command); + (*env)->ReleaseStringUTFChars (env, jcommand, command); + if (r == NULL) { + throw_exception (env, guestfs_last_error (g)); + return NULL; + } + jr = (*env)->NewStringUTF (env, r); + free (r); + return jr; +} + +JNIEXPORT jobjectArray JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1sh_1lines + (JNIEnv *env, jobject obj, jlong jg, jstring jcommand) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jobjectArray jr; + int r_len; + jclass cl; + jstring jstr; + char **r; + const char *command; + int i; + + command = (*env)->GetStringUTFChars (env, jcommand, NULL); + r = guestfs_sh_lines (g, command); + (*env)->ReleaseStringUTFChars (env, jcommand, command); + if (r == NULL) { + throw_exception (env, guestfs_last_error (g)); + return NULL; + } + for (r_len = 0; r[r_len] != NULL; ++r_len) ; + cl = (*env)->FindClass (env, "java/lang/String"); + jstr = (*env)->NewStringUTF (env, ""); + jr = (*env)->NewObjectArray (env, r_len, cl, jstr); + for (i = 0; i < r_len; ++i) { + jstr = (*env)->NewStringUTF (env, r[i]); + (*env)->SetObjectArrayElement (env, jr, i, jstr); + free (r[i]); + } + free (r); + return jr; +} + diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index fb432142..f102459b 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -277,3 +277,5 @@ external find : t -> string -> string array = "ocaml_guestfs_find" external e2fsck_f : t -> string -> unit = "ocaml_guestfs_e2fsck_f" external sleep : t -> int -> unit = "ocaml_guestfs_sleep" external ntfs_3g_probe : t -> bool -> string -> int = "ocaml_guestfs_ntfs_3g_probe" +external sh : t -> string -> string = "ocaml_guestfs_sh" +external sh_lines : t -> string -> string array = "ocaml_guestfs_sh_lines" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 859774aa..8983a164 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -610,3 +610,9 @@ val sleep : t -> int -> unit val ntfs_3g_probe : t -> bool -> string -> int (** probe NTFS volume *) +val sh : t -> string -> string +(** run a command via the shell *) + +val sh_lines : t -> string -> string array +(** run a command via the shell returning lines *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index 45c9883f..e4dc5099 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -4198,3 +4198,53 @@ ocaml_guestfs_ntfs_3g_probe (value gv, value rwv, value devicev) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_sh (value gv, value commandv) +{ + CAMLparam2 (gv, commandv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("sh: used handle after closing it"); + + const char *command = String_val (commandv); + char *r; + + caml_enter_blocking_section (); + r = guestfs_sh (g, command); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "sh"); + + rv = caml_copy_string (r); + free (r); + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_sh_lines (value gv, value commandv) +{ + CAMLparam2 (gv, commandv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("sh_lines: used handle after closing it"); + + const char *command = String_val (commandv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_sh_lines (g, command); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "sh_lines"); + + rv = caml_copy_string_array ((const char **) r); + for (i = 0; r[i] != NULL; ++i) free (r[i]); + free (r); + CAMLreturn (rv); +} + diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index c26faa16..8a6c4bb9 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -2549,3 +2549,37 @@ PREINIT: OUTPUT: RETVAL +SV * +sh (g, command) + guestfs_h *g; + char *command; +PREINIT: + char *output; + CODE: + output = guestfs_sh (g, command); + if (output == NULL) + croak ("sh: %s", guestfs_last_error (g)); + RETVAL = newSVpv (output, 0); + free (output); + OUTPUT: + RETVAL + +void +sh_lines (g, command) + guestfs_h *g; + char *command; +PREINIT: + char **lines; + int i, n; + PPCODE: + lines = guestfs_sh_lines (g, command); + if (lines == NULL) + croak ("sh_lines: %s", guestfs_last_error (g)); + for (n = 0; lines[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (lines[i], 0))); + free (lines[i]); + } + free (lines); + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index 05adfcb4..9329b769 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -432,7 +432,9 @@ or compatible processor architecture). The single parameter is an argv-style list of arguments. The first element is the name of the program to run. Subsequent elements are parameters. The list must be -non-empty (ie. must contain a program name). +non-empty (ie. must contain a program name). Note that +the command runs directly, and is I invoked via +the shell (see C<$h-Esh>). The return value is anything printed to I by the command. @@ -461,6 +463,8 @@ FTP. This is the same as C<$h-Ecommand>, but splits the result into a list of lines. +See also: C<$h-Esh_lines> + Because of the message protocol, there is a transfer limit of somewhere between 2MB and 4MB. To transfer large files you should use FTP. @@ -1125,6 +1129,28 @@ This displays the partition table on C, in the human-readable output of the L command. It is not intended to be parsed. +=item $output = $h->sh ($command); + +This call runs a command from the guest filesystem via the +guest's C. + +This is like C<$h-Ecommand>, but passes the command to: + + /bin/sh -c "command" + +Depending on the guest's shell, this usually results in +wildcards being expanded, shell expressions being interpolated +and so on. + +All the provisos about C<$h-Ecommand> apply to this call. + +=item @lines = $h->sh_lines ($command); + +This is the same as C<$h-Esh>, but splits the result +into a list of lines. + +See also: C<$h-Ecommand_lines> + =item $h->sleep ($secs); Sleep for C seconds. diff --git a/python/guestfs-py.c b/python/guestfs-py.c index 8e90d762..f5bc1093 100644 --- a/python/guestfs-py.c +++ b/python/guestfs-py.c @@ -4456,6 +4456,56 @@ py_guestfs_ntfs_3g_probe (PyObject *self, PyObject *args) return py_r; } +static PyObject * +py_guestfs_sh (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + char *r; + const char *command; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_sh", + &py_g, &command)) + return NULL; + g = get_handle (py_g); + + r = guestfs_sh (g, command); + if (r == NULL) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + py_r = PyString_FromString (r); + free (r); + return py_r; +} + +static PyObject * +py_guestfs_sh_lines (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + char **r; + const char *command; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_sh_lines", + &py_g, &command)) + return NULL; + g = get_handle (py_g); + + r = guestfs_sh_lines (g, command); + if (r == NULL) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + py_r = put_string_list (r); + free_strings (r); + return py_r; +} + static PyMethodDef methods[] = { { (char *) "create", py_guestfs_create, METH_VARARGS, NULL }, { (char *) "close", py_guestfs_close, METH_VARARGS, NULL }, @@ -4621,6 +4671,8 @@ static PyMethodDef methods[] = { { (char *) "e2fsck_f", py_guestfs_e2fsck_f, METH_VARARGS, NULL }, { (char *) "sleep", py_guestfs_sleep, METH_VARARGS, NULL }, { (char *) "ntfs_3g_probe", py_guestfs_ntfs_3g_probe, METH_VARARGS, NULL }, + { (char *) "sh", py_guestfs_sh, METH_VARARGS, NULL }, + { (char *) "sh_lines", py_guestfs_sh_lines, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/python/guestfs.py b/python/guestfs.py index 2600ff2a..c9658cd1 100644 --- a/python/guestfs.py +++ b/python/guestfs.py @@ -947,7 +947,9 @@ class GuestFS: The single parameter is an argv-style list of arguments. The first element is the name of the program to run. Subsequent elements are parameters. The list must be - non-empty (ie. must contain a program name). + non-empty (ie. must contain a program name). Note that + the command runs directly, and is *not* invoked via the + shell (see "g.sh"). The return value is anything printed to *stdout* by the command. @@ -977,6 +979,8 @@ class GuestFS: u"""This is the same as "g.command", but splits the result into a list of lines. + See also: "g.sh_lines" + This function returns a list of strings. Because of the message protocol, there is a transfer @@ -1621,3 +1625,29 @@ class GuestFS: """ return libguestfsmod.ntfs_3g_probe (self._o, rw, device) + def sh (self, command): + u"""This call runs a command from the guest filesystem via + the guest's "/bin/sh". + + This is like "g.command", but passes the command to: + + /bin/sh -c "command" + + Depending on the guest's shell, this usually results in + wildcards being expanded, shell expressions being + interpolated and so on. + + All the provisos about "g.command" apply to this call. + """ + return libguestfsmod.sh (self._o, command) + + def sh_lines (self, command): + u"""This is the same as "g.sh", but splits the result into a + list of lines. + + See also: "g.command_lines" + + This function returns a list of strings. + """ + return libguestfsmod.sh_lines (self._o, command) + diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index 38256dc0..81e36910 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -4003,6 +4003,58 @@ static VALUE ruby_guestfs_ntfs_3g_probe (VALUE gv, VALUE rwv, VALUE devicev) return INT2NUM (r); } +static VALUE ruby_guestfs_sh (VALUE gv, VALUE commandv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "sh"); + + const char *command = StringValueCStr (commandv); + if (!command) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "command", "sh"); + + char *r; + + r = guestfs_sh (g, command); + if (r == NULL) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + VALUE rv = rb_str_new2 (r); + free (r); + return rv; +} + +static VALUE ruby_guestfs_sh_lines (VALUE gv, VALUE commandv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "sh_lines"); + + const char *command = StringValueCStr (commandv); + if (!command) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "command", "sh_lines"); + + char **r; + + r = guestfs_sh_lines (g, command); + if (r == NULL) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + int i, len = 0; + for (i = 0; r[i] != NULL; ++i) len++; + VALUE rv = rb_ary_new2 (len); + for (i = 0; r[i] != NULL; ++i) { + rb_ary_push (rv, rb_str_new2 (r[i])); + free (r[i]); + } + free (r); + return rv; +} + /* Initialize the module. */ void Init__guestfs () { @@ -4337,4 +4389,8 @@ void Init__guestfs () ruby_guestfs_sleep, 1); rb_define_method (c_guestfs, "ntfs_3g_probe", ruby_guestfs_ntfs_3g_probe, 2); + rb_define_method (c_guestfs, "sh", + ruby_guestfs_sh, 1); + rb_define_method (c_guestfs, "sh_lines", + ruby_guestfs_sh_lines, 1); } diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index ecaa5662..f36fcf06 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -10074,3 +10074,192 @@ int guestfs_ntfs_3g_probe (guestfs_h *g, return ctx.ret.status; } +struct sh_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_sh_ret ret; +}; + +static void sh_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct sh_ctx *ctx = (struct sh_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_sh"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_sh"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_sh"); + return; + } + goto done; + } + if (!xdr_guestfs_sh_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_sh"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char *guestfs_sh (guestfs_h *g, + const char *command) +{ + struct guestfs_sh_args args; + struct sh_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_sh") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.command = (char *) command; + serial = guestfs__send_sync (g, GUESTFS_PROC_SH, + (xdrproc_t) xdr_guestfs_sh_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, sh_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_sh"); + guestfs_end_busy (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_SH, serial) == -1) { + guestfs_end_busy (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return NULL; + } + + guestfs_end_busy (g); + return ctx.ret.output; /* caller will free */ +} + +struct sh_lines_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_sh_lines_ret ret; +}; + +static void sh_lines_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct sh_lines_ctx *ctx = (struct sh_lines_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_sh_lines"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_sh_lines"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_sh_lines"); + return; + } + goto done; + } + if (!xdr_guestfs_sh_lines_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_sh_lines"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char **guestfs_sh_lines (guestfs_h *g, + const char *command) +{ + struct guestfs_sh_lines_args args; + struct sh_lines_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_sh_lines") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.command = (char *) command; + serial = guestfs__send_sync (g, GUESTFS_PROC_SH_LINES, + (xdrproc_t) xdr_guestfs_sh_lines_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, sh_lines_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_sh_lines"); + guestfs_end_busy (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_SH_LINES, serial) == -1) { + guestfs_end_busy (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return NULL; + } + + guestfs_end_busy (g); + /* caller will free this, but we need to add a NULL entry */ + ctx.ret.lines.lines_val = + safe_realloc (g, ctx.ret.lines.lines_val, + sizeof (char *) * (ctx.ret.lines.lines_len + 1)); + ctx.ret.lines.lines_val[ctx.ret.lines.lines_len] = NULL; + return ctx.ret.lines.lines_val; +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index 7f8f0deb..fe9967f2 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -181,3 +181,5 @@ extern char **guestfs_find (guestfs_h *handle, const char *directory); extern int guestfs_e2fsck_f (guestfs_h *handle, const char *device); extern int guestfs_sleep (guestfs_h *handle, int secs); extern int guestfs_ntfs_3g_probe (guestfs_h *handle, int rw, const char *device); +extern char *guestfs_sh (guestfs_h *handle, const char *command); +extern char **guestfs_sh_lines (guestfs_h *handle, const char *command); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index fc88d05f..96951d9d 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -1870,6 +1870,47 @@ xdr_guestfs_ntfs_3g_probe_ret (XDR *xdrs, guestfs_ntfs_3g_probe_ret *objp) return TRUE; } +bool_t +xdr_guestfs_sh_args (XDR *xdrs, guestfs_sh_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->command, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_sh_ret (XDR *xdrs, guestfs_sh_ret *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->output, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_sh_lines_args (XDR *xdrs, guestfs_sh_lines_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->command, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_sh_lines_ret (XDR *xdrs, guestfs_sh_lines_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->lines.lines_val, (u_int *) &objp->lines.lines_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 9f3bd8d0..a9863ccf 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -946,6 +946,29 @@ struct guestfs_ntfs_3g_probe_ret { }; typedef struct guestfs_ntfs_3g_probe_ret guestfs_ntfs_3g_probe_ret; +struct guestfs_sh_args { + char *command; +}; +typedef struct guestfs_sh_args guestfs_sh_args; + +struct guestfs_sh_ret { + char *output; +}; +typedef struct guestfs_sh_ret guestfs_sh_ret; + +struct guestfs_sh_lines_args { + char *command; +}; +typedef struct guestfs_sh_lines_args guestfs_sh_lines_args; + +struct guestfs_sh_lines_ret { + struct { + u_int lines_len; + str *lines_val; + } lines; +}; +typedef struct guestfs_sh_lines_ret guestfs_sh_lines_ret; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -1057,7 +1080,9 @@ enum guestfs_procedure { GUESTFS_PROC_E2FSCK_F = 108, GUESTFS_PROC_SLEEP = 109, GUESTFS_PROC_NTFS_3G_PROBE = 110, - GUESTFS_PROC_NR_PROCS = 110 + 1, + GUESTFS_PROC_SH = 111, + GUESTFS_PROC_SH_LINES = 112, + GUESTFS_PROC_NR_PROCS = 112 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -1258,6 +1283,10 @@ extern bool_t xdr_guestfs_e2fsck_f_args (XDR *, guestfs_e2fsck_f_args*); extern bool_t xdr_guestfs_sleep_args (XDR *, guestfs_sleep_args*); extern bool_t xdr_guestfs_ntfs_3g_probe_args (XDR *, guestfs_ntfs_3g_probe_args*); extern bool_t xdr_guestfs_ntfs_3g_probe_ret (XDR *, guestfs_ntfs_3g_probe_ret*); +extern bool_t xdr_guestfs_sh_args (XDR *, guestfs_sh_args*); +extern bool_t xdr_guestfs_sh_ret (XDR *, guestfs_sh_ret*); +extern bool_t xdr_guestfs_sh_lines_args (XDR *, guestfs_sh_lines_args*); +extern bool_t xdr_guestfs_sh_lines_ret (XDR *, guestfs_sh_lines_ret*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -1417,6 +1446,10 @@ extern bool_t xdr_guestfs_e2fsck_f_args (); extern bool_t xdr_guestfs_sleep_args (); extern bool_t xdr_guestfs_ntfs_3g_probe_args (); extern bool_t xdr_guestfs_ntfs_3g_probe_ret (); +extern bool_t xdr_guestfs_sh_args (); +extern bool_t xdr_guestfs_sh_ret (); +extern bool_t xdr_guestfs_sh_lines_args (); +extern bool_t xdr_guestfs_sh_lines_ret (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index cc514e74..94d2e849 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -732,6 +732,22 @@ struct guestfs_ntfs_3g_probe_ret { int status; }; +struct guestfs_sh_args { + string command<>; +}; + +struct guestfs_sh_ret { + string output<>; +}; + +struct guestfs_sh_lines_args { + string command<>; +}; + +struct guestfs_sh_lines_ret { + str lines<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -843,6 +859,8 @@ enum guestfs_procedure { GUESTFS_PROC_E2FSCK_F = 108, GUESTFS_PROC_SLEEP = 109, GUESTFS_PROC_NTFS_3G_PROBE = 110, + GUESTFS_PROC_SH = 111, + GUESTFS_PROC_SH_LINES = 112, GUESTFS_PROC_NR_PROCS }; -- cgit From 05c34c1c1479bb07b31cfbf912743a8cf014a636 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 08:20:32 +0100 Subject: Add 'glob-expand' command. --- daemon/Makefile.am | 1 + daemon/glob.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/generator.ml | 28 ++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 daemon/glob.c diff --git a/daemon/Makefile.am b/daemon/Makefile.am index ea4b721b..7ef2a6b8 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -36,6 +36,7 @@ guestfsd_SOURCES = \ file.c \ find.c \ fsck.c \ + glob.c \ grub.c \ guestfsd.c \ hexdump.c \ diff --git a/daemon/glob.c b/daemon/glob.c new file mode 100644 index 00000000..f39832ea --- /dev/null +++ b/daemon/glob.c @@ -0,0 +1,63 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include + +#include "daemon.h" +#include "actions.h" + +char ** +do_glob_expand (char *pattern) +{ + int r; + glob_t buf; + + NEED_ROOT (NULL); + ABS_PATH (pattern, NULL); /* Required so chroot can be used. */ + + /* glob(3) in glibc never calls chdir, so this seems to be safe: */ + CHROOT_IN; + r = glob (pattern, GLOB_MARK|GLOB_BRACE, NULL, &buf); + CHROOT_OUT; + + if (r == GLOB_NOMATCH) { /* Return an empty list instead of an error. */ + char **rv; + + rv = malloc (sizeof (char *) * 1); + rv[0] = NULL; + return rv; /* Caller frees. */ + } + + if (r != 0) { + if (errno != 0) + reply_with_perror ("glob: %s", pattern); + else + reply_with_error ("glob failed: %s", pattern); + return NULL; + } + + /* We take a bit of a liberty here. 'globfree' just frees the + * strings in the glob_t structure. We will pass them out directly + * and the caller will free them. + */ + return buf.gl_pathv; +} diff --git a/src/generator.ml b/src/generator.ml index 64a8ab92..b716e0af 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -2373,6 +2373,34 @@ into a list of lines. See also: C"); + ("glob_expand", (RStringList "paths", [String "pattern"]), 113, [], + [InitBasicFS, Always, TestOutputList ( + [["mkdir_p"; "/a/b/c"]; + ["touch"; "/a/b/c/d"]; + ["touch"; "/a/b/c/e"]; + ["glob_expand"; "/a/b/c/*"]], ["/a/b/c/d"; "/a/b/c/e"]); + InitBasicFS, Always, TestOutputList ( + [["mkdir_p"; "/a/b/c"]; + ["touch"; "/a/b/c/d"]; + ["touch"; "/a/b/c/e"]; + ["glob_expand"; "/a/*/c/*"]], ["/a/b/c/d"; "/a/b/c/e"]); + InitBasicFS, Always, TestOutputList ( + [["mkdir_p"; "/a/b/c"]; + ["touch"; "/a/b/c/d"]; + ["touch"; "/a/b/c/e"]; + ["glob_expand"; "/a/*/x/*"]], [])], + "expand a wildcard path", + "\ +This command searches for all the pathnames matching +C according to the wildcard expansion rules +used by the shell. + +If no paths match, then this returns an empty list +(note: not an error). + +It is just a wrapper around the C L function +with flags C. +See that manual page for more details."); ] -- cgit From ad8a256f54a6cb99f89bb444c8597a152a793dce Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 08:20:42 +0100 Subject: Generated code for 'glob-expand'. --- capitests/tests.c | 419 ++++++++++++++++++++++++++++- daemon/actions.h | 1 + daemon/stubs.c | 31 +++ fish/cmds.c | 24 ++ fish/completion.c | 1 + guestfish-actions.pod | 15 ++ guestfs-actions.pod | 20 ++ java/com/redhat/et/libguestfs/GuestFS.java | 26 ++ java/com_redhat_et_libguestfs_GuestFS.c | 33 +++ ocaml/guestfs.ml | 1 + ocaml/guestfs.mli | 3 + ocaml/guestfs_c_actions.c | 26 ++ perl/Guestfs.xs | 19 ++ perl/lib/Sys/Guestfs.pm | 13 + python/guestfs-py.c | 26 ++ python/guestfs.py | 16 ++ ruby/ext/guestfs/_guestfs.c | 31 +++ src/guestfs-actions.c | 97 +++++++ src/guestfs-actions.h | 1 + src/guestfs_protocol.c | 21 ++ src/guestfs_protocol.h | 20 +- src/guestfs_protocol.x | 9 + 22 files changed, 851 insertions(+), 2 deletions(-) diff --git a/capitests/tests.c b/capitests/tests.c index e5466b5e..309bac3d 100644 --- a/capitests/tests.c +++ b/capitests/tests.c @@ -152,6 +152,405 @@ static void no_test_warnings (void) fprintf (stderr, "warning: \"guestfs_sh_lines\" has no tests\n"); } +static int test_glob_expand_0_skip (void) +{ + const char *str; + + str = getenv ("SKIP_TEST_GLOB_EXPAND_0"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_GLOB_EXPAND"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_glob_expand_0 (void) +{ + if (test_glob_expand_0_skip ()) { + printf ("%s skipped (reason: SKIP_TEST_* variable set)\n", "test_glob_expand_0"); + return 0; + } + + /* InitBasicFS for test_glob_expand_0: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + int r; + suppress_error = 0; + r = guestfs_blockdev_setrw (g, device); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_umount_all (g); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_lvm_remove_all (g); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda"; + char lines_0[] = ","; + char *lines[] = { + lines_0, + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, device, 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + char fstype[] = "ext2"; + char device[] = "/dev/sda1"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for glob_expand (0) */ + { + char path[] = "/a/b/c"; + int r; + suppress_error = 0; + r = guestfs_mkdir_p (g, path); + if (r == -1) + return -1; + } + { + char path[] = "/a/b/c/d"; + int r; + suppress_error = 0; + r = guestfs_touch (g, path); + if (r == -1) + return -1; + } + { + char path[] = "/a/b/c/e"; + int r; + suppress_error = 0; + r = guestfs_touch (g, path); + if (r == -1) + return -1; + } + { + char pattern[] = "/a/b/c/*"; + char **r; + int i; + suppress_error = 0; + r = guestfs_glob_expand (g, pattern); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_glob_expand_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "/a/b/c/d"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_glob_expand_0: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_glob_expand_0: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "/a/b/c/e"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_glob_expand_0: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (r[2] != NULL) { + fprintf (stderr, "test_glob_expand_0: extra elements returned from command\n"); + print_strings (r); + return -1; + } + for (i = 0; r[i] != NULL; ++i) + free (r[i]); + free (r); + } + return 0; +} + +static int test_glob_expand_1_skip (void) +{ + const char *str; + + str = getenv ("SKIP_TEST_GLOB_EXPAND_1"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_GLOB_EXPAND"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_glob_expand_1 (void) +{ + if (test_glob_expand_1_skip ()) { + printf ("%s skipped (reason: SKIP_TEST_* variable set)\n", "test_glob_expand_1"); + return 0; + } + + /* InitBasicFS for test_glob_expand_1: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + int r; + suppress_error = 0; + r = guestfs_blockdev_setrw (g, device); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_umount_all (g); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_lvm_remove_all (g); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda"; + char lines_0[] = ","; + char *lines[] = { + lines_0, + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, device, 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + char fstype[] = "ext2"; + char device[] = "/dev/sda1"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for glob_expand (1) */ + { + char path[] = "/a/b/c"; + int r; + suppress_error = 0; + r = guestfs_mkdir_p (g, path); + if (r == -1) + return -1; + } + { + char path[] = "/a/b/c/d"; + int r; + suppress_error = 0; + r = guestfs_touch (g, path); + if (r == -1) + return -1; + } + { + char path[] = "/a/b/c/e"; + int r; + suppress_error = 0; + r = guestfs_touch (g, path); + if (r == -1) + return -1; + } + { + char pattern[] = "/a/*/c/*"; + char **r; + int i; + suppress_error = 0; + r = guestfs_glob_expand (g, pattern); + if (r == NULL) + return -1; + if (!r[0]) { + fprintf (stderr, "test_glob_expand_1: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "/a/b/c/d"; + if (strcmp (r[0], expected) != 0) { + fprintf (stderr, "test_glob_expand_1: expected \"%s\" but got \"%s\"\n", expected, r[0]); + return -1; + } + } + if (!r[1]) { + fprintf (stderr, "test_glob_expand_1: short list returned from command\n"); + print_strings (r); + return -1; + } + { + char expected[] = "/a/b/c/e"; + if (strcmp (r[1], expected) != 0) { + fprintf (stderr, "test_glob_expand_1: expected \"%s\" but got \"%s\"\n", expected, r[1]); + return -1; + } + } + if (r[2] != NULL) { + fprintf (stderr, "test_glob_expand_1: extra elements returned from command\n"); + print_strings (r); + return -1; + } + for (i = 0; r[i] != NULL; ++i) + free (r[i]); + free (r); + } + return 0; +} + +static int test_glob_expand_2_skip (void) +{ + const char *str; + + str = getenv ("SKIP_TEST_GLOB_EXPAND_2"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_GLOB_EXPAND"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_glob_expand_2 (void) +{ + if (test_glob_expand_2_skip ()) { + printf ("%s skipped (reason: SKIP_TEST_* variable set)\n", "test_glob_expand_2"); + return 0; + } + + /* InitBasicFS for test_glob_expand_2: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + int r; + suppress_error = 0; + r = guestfs_blockdev_setrw (g, device); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_umount_all (g); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_lvm_remove_all (g); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda"; + char lines_0[] = ","; + char *lines[] = { + lines_0, + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, device, 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + char fstype[] = "ext2"; + char device[] = "/dev/sda1"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestOutputList for glob_expand (2) */ + { + char path[] = "/a/b/c"; + int r; + suppress_error = 0; + r = guestfs_mkdir_p (g, path); + if (r == -1) + return -1; + } + { + char path[] = "/a/b/c/d"; + int r; + suppress_error = 0; + r = guestfs_touch (g, path); + if (r == -1) + return -1; + } + { + char path[] = "/a/b/c/e"; + int r; + suppress_error = 0; + r = guestfs_touch (g, path); + if (r == -1) + return -1; + } + { + char pattern[] = "/a/*/x/*"; + char **r; + int i; + suppress_error = 0; + r = guestfs_glob_expand (g, pattern); + if (r == NULL) + return -1; + if (r[0] != NULL) { + fprintf (stderr, "test_glob_expand_2: extra elements returned from command\n"); + print_strings (r); + return -1; + } + for (i = 0; r[i] != NULL; ++i) + free (r[i]); + free (r); + } + return 0; +} + static int test_ntfs_3g_probe_0_skip (void) { const char *str; @@ -15767,8 +16166,26 @@ int main (int argc, char *argv[]) /* Cancel previous alarm. */ alarm (0); - nr_tests = 146; + nr_tests = 149; + test_num++; + printf ("%3d/%3d test_glob_expand_0\n", test_num, nr_tests); + if (test_glob_expand_0 () == -1) { + printf ("test_glob_expand_0 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_glob_expand_1\n", test_num, nr_tests); + if (test_glob_expand_1 () == -1) { + printf ("test_glob_expand_1 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_glob_expand_2\n", test_num, nr_tests); + if (test_glob_expand_2 () == -1) { + printf ("test_glob_expand_2 FAILED\n"); + failed++; + } test_num++; printf ("%3d/%3d test_ntfs_3g_probe_0\n", test_num, nr_tests); if (test_ntfs_3g_probe_0 () == -1) { diff --git a/daemon/actions.h b/daemon/actions.h index a76498f3..cc8bc787 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -133,3 +133,4 @@ extern int do_sleep (int secs); extern int do_ntfs_3g_probe (int rw, char *device); extern char *do_sh (char *command); extern char **do_sh_lines (char *command); +extern char **do_glob_expand (char *pattern); diff --git a/daemon/stubs.c b/daemon/stubs.c index dcdc51c0..2a6035cf 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -2826,6 +2826,34 @@ done: xdr_free ((xdrproc_t) xdr_guestfs_sh_lines_args, (char *) &args); } +static void glob_expand_stub (XDR *xdr_in) +{ + char **r; + struct guestfs_glob_expand_args args; + char *pattern; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_glob_expand_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "glob_expand"); + return; + } + pattern = args.pattern; + + r = do_glob_expand (pattern); + if (r == NULL) + /* do_glob_expand has already called reply_with_error */ + goto done; + + struct guestfs_glob_expand_ret ret; + ret.paths.paths_len = count_strings (r); + ret.paths.paths_val = r; + reply ((xdrproc_t) &xdr_guestfs_glob_expand_ret, (char *) &ret); + free_strings (r); +done: + xdr_free ((xdrproc_t) xdr_guestfs_glob_expand_args, (char *) &args); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -3165,6 +3193,9 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_SH_LINES: sh_lines_stub (xdr_in); break; + case GUESTFS_PROC_GLOB_EXPAND: + glob_expand_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr); } diff --git a/fish/cmds.c b/fish/cmds.c index fe1ef92e..8c35eea2 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -84,6 +84,7 @@ void list_commands (void) printf ("%-20s %s\n", "get-qemu", "get the qemu binary"); printf ("%-20s %s\n", "get-state", "get the current state"); printf ("%-20s %s\n", "get-verbose", "get verbose mode"); + printf ("%-20s %s\n", "glob-expand", "expand a wildcard path"); printf ("%-20s %s\n", "grub-install", "install GRUB"); printf ("%-20s %s\n", "hexdump", "dump a file in hexadecimal"); printf ("%-20s %s\n", "is-busy", "is busy processing a command"); @@ -567,6 +568,9 @@ void display_command (const char *cmd) else if (strcasecmp (cmd, "sh_lines") == 0 || strcasecmp (cmd, "sh-lines") == 0) pod2text ("sh-lines - run a command via the shell returning lines", " sh-lines \n\nThis is the same as C, but splits the result\ninto a list of lines.\n\nSee also: C"); + else + if (strcasecmp (cmd, "glob_expand") == 0 || strcasecmp (cmd, "glob-expand") == 0) + pod2text ("glob-expand - expand a wildcard path", " glob-expand \n\nThis command searches for all the pathnames matching\nC according to the wildcard expansion rules\nused by the shell.\n\nIf no paths match, then this returns an empty list\n(note: not an error).\n\nIt is just a wrapper around the C L function\nwith flags C.\nSee that manual page for more details."); else display_builtin_command (cmd); } @@ -2780,6 +2784,23 @@ static int run_sh_lines (const char *cmd, int argc, char *argv[]) return 0; } +static int run_glob_expand (const char *cmd, int argc, char *argv[]) +{ + char **r; + const char *pattern; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + pattern = argv[0]; + r = guestfs_glob_expand (g, pattern); + if (r == NULL) return -1; + print_strings (r); + free_strings (r); + return 0; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -3180,6 +3201,9 @@ int run_action (const char *cmd, int argc, char *argv[]) else if (strcasecmp (cmd, "sh_lines") == 0 || strcasecmp (cmd, "sh-lines") == 0) return run_sh_lines (cmd, argc, argv); + else + if (strcasecmp (cmd, "glob_expand") == 0 || strcasecmp (cmd, "glob-expand") == 0) + return run_glob_expand (cmd, argc, argv); else { fprintf (stderr, "%s: unknown command\n", cmd); diff --git a/fish/completion.c b/fish/completion.c index 4ac0fad9..e8d8cea5 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -179,6 +179,7 @@ static const char *const commands[] = { "ntfs-3g-probe", "sh", "sh-lines", + "glob-expand", NULL }; diff --git a/guestfish-actions.pod b/guestfish-actions.pod index 3217d922..48459901 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -683,6 +683,21 @@ For more information on states, see L. This returns the verbose messages flag. +=head2 glob-expand + + glob-expand pattern + +This command searches for all the pathnames matching +C according to the wildcard expansion rules +used by the shell. + +If no paths match, then this returns an empty list +(note: not an error). + +It is just a wrapper around the C L function +with flags C. +See that manual page for more details. + =head2 grub-install grub-install root device diff --git a/guestfs-actions.pod b/guestfs-actions.pod index 93a9fbb7..d35d1aa2 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -884,6 +884,26 @@ This returns the verbose messages flag. This function returns a C truth value on success or -1 on error. +=head2 guestfs_glob_expand + + char **guestfs_glob_expand (guestfs_h *handle, + const char *pattern); + +This command searches for all the pathnames matching +C according to the wildcard expansion rules +used by the shell. + +If no paths match, then this returns an empty list +(note: not an error). + +It is just a wrapper around the C L function +with flags C. +See that manual page for more details. + +This function returns a NULL-terminated array of strings +(like L), or NULL if there was an error. +I. + =head2 guestfs_grub_install int guestfs_grub_install (guestfs_h *handle, diff --git a/java/com/redhat/et/libguestfs/GuestFS.java b/java/com/redhat/et/libguestfs/GuestFS.java index 40754204..cdc0f094 100644 --- a/java/com/redhat/et/libguestfs/GuestFS.java +++ b/java/com/redhat/et/libguestfs/GuestFS.java @@ -3435,4 +3435,30 @@ public HashMap test0rhashtableerr () private native String[] _sh_lines (long g, String command) throws LibGuestFSException; + /** + * expand a wildcard path + *

+ * This command searches for all the pathnames matching + * "pattern" according to the wildcard expansion rules used + * by the shell. + *

+ * If no paths match, then this returns an empty list + * (note: not an error). + *

+ * It is just a wrapper around the C glob(3) function with + * flags "GLOB_MARK|GLOB_BRACE". See that manual page for + * more details. + *

+ * @throws LibGuestFSException + */ + public String[] glob_expand (String pattern) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("glob_expand: handle is closed"); + return _glob_expand (g, pattern); + } + private native String[] _glob_expand (long g, String pattern) + throws LibGuestFSException; + } diff --git a/java/com_redhat_et_libguestfs_GuestFS.c b/java/com_redhat_et_libguestfs_GuestFS.c index 631e48f6..8a5afe93 100644 --- a/java/com_redhat_et_libguestfs_GuestFS.c +++ b/java/com_redhat_et_libguestfs_GuestFS.c @@ -4040,3 +4040,36 @@ Java_com_redhat_et_libguestfs_GuestFS__1sh_1lines return jr; } +JNIEXPORT jobjectArray JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1glob_1expand + (JNIEnv *env, jobject obj, jlong jg, jstring jpattern) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + jobjectArray jr; + int r_len; + jclass cl; + jstring jstr; + char **r; + const char *pattern; + int i; + + pattern = (*env)->GetStringUTFChars (env, jpattern, NULL); + r = guestfs_glob_expand (g, pattern); + (*env)->ReleaseStringUTFChars (env, jpattern, pattern); + if (r == NULL) { + throw_exception (env, guestfs_last_error (g)); + return NULL; + } + for (r_len = 0; r[r_len] != NULL; ++r_len) ; + cl = (*env)->FindClass (env, "java/lang/String"); + jstr = (*env)->NewStringUTF (env, ""); + jr = (*env)->NewObjectArray (env, r_len, cl, jstr); + for (i = 0; i < r_len; ++i) { + jstr = (*env)->NewStringUTF (env, r[i]); + (*env)->SetObjectArrayElement (env, jr, i, jstr); + free (r[i]); + } + free (r); + return jr; +} + diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index f102459b..fb83545b 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -279,3 +279,4 @@ external sleep : t -> int -> unit = "ocaml_guestfs_sleep" external ntfs_3g_probe : t -> bool -> string -> int = "ocaml_guestfs_ntfs_3g_probe" external sh : t -> string -> string = "ocaml_guestfs_sh" external sh_lines : t -> string -> string array = "ocaml_guestfs_sh_lines" +external glob_expand : t -> string -> string array = "ocaml_guestfs_glob_expand" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 8983a164..345c7ec6 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -616,3 +616,6 @@ val sh : t -> string -> string val sh_lines : t -> string -> string array (** run a command via the shell returning lines *) +val glob_expand : t -> string -> string array +(** expand a wildcard path *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index e4dc5099..1ce55f0a 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -4248,3 +4248,29 @@ ocaml_guestfs_sh_lines (value gv, value commandv) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_glob_expand (value gv, value patternv) +{ + CAMLparam2 (gv, patternv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("glob_expand: used handle after closing it"); + + const char *pattern = String_val (patternv); + int i; + char **r; + + caml_enter_blocking_section (); + r = guestfs_glob_expand (g, pattern); + caml_leave_blocking_section (); + if (r == NULL) + ocaml_guestfs_raise_error (g, "glob_expand"); + + rv = caml_copy_string_array ((const char **) r); + for (i = 0; r[i] != NULL; ++i) free (r[i]); + free (r); + CAMLreturn (rv); +} + diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index 8a6c4bb9..5905dc5e 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -2583,3 +2583,22 @@ PREINIT: } free (lines); +void +glob_expand (g, pattern) + guestfs_h *g; + char *pattern; +PREINIT: + char **paths; + int i, n; + PPCODE: + paths = guestfs_glob_expand (g, pattern); + if (paths == NULL) + croak ("glob_expand: %s", guestfs_last_error (g)); + for (n = 0; paths[n] != NULL; ++n) /**/; + EXTEND (SP, n); + for (i = 0; i < n; ++i) { + PUSHs (sv_2mortal (newSVpv (paths[i], 0))); + free (paths[i]); + } + free (paths); + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index 9329b769..436f218c 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -676,6 +676,19 @@ For more information on states, see L. This returns the verbose messages flag. +=item @paths = $h->glob_expand ($pattern); + +This command searches for all the pathnames matching +C according to the wildcard expansion rules +used by the shell. + +If no paths match, then this returns an empty list +(note: not an error). + +It is just a wrapper around the C L function +with flags C. +See that manual page for more details. + =item $h->grub_install ($root, $device); This command installs GRUB (the Grand Unified Bootloader) on diff --git a/python/guestfs-py.c b/python/guestfs-py.c index f5bc1093..60e20d2e 100644 --- a/python/guestfs-py.c +++ b/python/guestfs-py.c @@ -4506,6 +4506,31 @@ py_guestfs_sh_lines (PyObject *self, PyObject *args) return py_r; } +static PyObject * +py_guestfs_glob_expand (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + char **r; + const char *pattern; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_glob_expand", + &py_g, &pattern)) + return NULL; + g = get_handle (py_g); + + r = guestfs_glob_expand (g, pattern); + if (r == NULL) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + py_r = put_string_list (r); + free_strings (r); + return py_r; +} + static PyMethodDef methods[] = { { (char *) "create", py_guestfs_create, METH_VARARGS, NULL }, { (char *) "close", py_guestfs_close, METH_VARARGS, NULL }, @@ -4673,6 +4698,7 @@ static PyMethodDef methods[] = { { (char *) "ntfs_3g_probe", py_guestfs_ntfs_3g_probe, METH_VARARGS, NULL }, { (char *) "sh", py_guestfs_sh, METH_VARARGS, NULL }, { (char *) "sh_lines", py_guestfs_sh_lines, METH_VARARGS, NULL }, + { (char *) "glob_expand", py_guestfs_glob_expand, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/python/guestfs.py b/python/guestfs.py index c9658cd1..39766109 100644 --- a/python/guestfs.py +++ b/python/guestfs.py @@ -1651,3 +1651,19 @@ class GuestFS: """ return libguestfsmod.sh_lines (self._o, command) + def glob_expand (self, pattern): + u"""This command searches for all the pathnames matching + "pattern" according to the wildcard expansion rules used + by the shell. + + If no paths match, then this returns an empty list + (note: not an error). + + It is just a wrapper around the C glob(3) function with + flags "GLOB_MARK|GLOB_BRACE". See that manual page for + more details. + + This function returns a list of strings. + """ + return libguestfsmod.glob_expand (self._o, pattern) + diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index 81e36910..bcb6fefc 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -4055,6 +4055,35 @@ static VALUE ruby_guestfs_sh_lines (VALUE gv, VALUE commandv) return rv; } +static VALUE ruby_guestfs_glob_expand (VALUE gv, VALUE patternv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "glob_expand"); + + const char *pattern = StringValueCStr (patternv); + if (!pattern) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "pattern", "glob_expand"); + + char **r; + + r = guestfs_glob_expand (g, pattern); + if (r == NULL) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + int i, len = 0; + for (i = 0; r[i] != NULL; ++i) len++; + VALUE rv = rb_ary_new2 (len); + for (i = 0; r[i] != NULL; ++i) { + rb_ary_push (rv, rb_str_new2 (r[i])); + free (r[i]); + } + free (r); + return rv; +} + /* Initialize the module. */ void Init__guestfs () { @@ -4393,4 +4422,6 @@ void Init__guestfs () ruby_guestfs_sh, 1); rb_define_method (c_guestfs, "sh_lines", ruby_guestfs_sh_lines, 1); + rb_define_method (c_guestfs, "glob_expand", + ruby_guestfs_glob_expand, 1); } diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index f36fcf06..e1ec1589 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -10263,3 +10263,100 @@ char **guestfs_sh_lines (guestfs_h *g, return ctx.ret.lines.lines_val; } +struct glob_expand_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; + struct guestfs_glob_expand_ret ret; +}; + +static void glob_expand_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct glob_expand_ctx *ctx = (struct glob_expand_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_glob_expand"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_glob_expand"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_glob_expand"); + return; + } + goto done; + } + if (!xdr_guestfs_glob_expand_ret (xdr, &ctx->ret)) { + error (g, "%s: failed to parse reply", "guestfs_glob_expand"); + return; + } + done: + ctx->cb_sequence = 1; +} + +char **guestfs_glob_expand (guestfs_h *g, + const char *pattern) +{ + struct guestfs_glob_expand_args args; + struct glob_expand_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_glob_expand") == -1) return NULL; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.pattern = (char *) pattern; + serial = guestfs__send_sync (g, GUESTFS_PROC_GLOB_EXPAND, + (xdrproc_t) xdr_guestfs_glob_expand_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return NULL; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, glob_expand_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_glob_expand"); + guestfs_end_busy (g); + return NULL; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_GLOB_EXPAND, serial) == -1) { + guestfs_end_busy (g); + return NULL; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return NULL; + } + + guestfs_end_busy (g); + /* caller will free this, but we need to add a NULL entry */ + ctx.ret.paths.paths_val = + safe_realloc (g, ctx.ret.paths.paths_val, + sizeof (char *) * (ctx.ret.paths.paths_len + 1)); + ctx.ret.paths.paths_val[ctx.ret.paths.paths_len] = NULL; + return ctx.ret.paths.paths_val; +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index fe9967f2..19996e8f 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -183,3 +183,4 @@ extern int guestfs_sleep (guestfs_h *handle, int secs); extern int guestfs_ntfs_3g_probe (guestfs_h *handle, int rw, const char *device); extern char *guestfs_sh (guestfs_h *handle, const char *command); extern char **guestfs_sh_lines (guestfs_h *handle, const char *command); +extern char **guestfs_glob_expand (guestfs_h *handle, const char *pattern); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index 96951d9d..b2d435ee 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -1911,6 +1911,27 @@ xdr_guestfs_sh_lines_ret (XDR *xdrs, guestfs_sh_lines_ret *objp) return TRUE; } +bool_t +xdr_guestfs_glob_expand_args (XDR *xdrs, guestfs_glob_expand_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->pattern, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_glob_expand_ret (XDR *xdrs, guestfs_glob_expand_ret *objp) +{ + register int32_t *buf; + + if (!xdr_array (xdrs, (char **)&objp->paths.paths_val, (u_int *) &objp->paths.paths_len, ~0, + sizeof (str), (xdrproc_t) xdr_str)) + return FALSE; + return TRUE; +} + bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index a9863ccf..8fdb6640 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -969,6 +969,19 @@ struct guestfs_sh_lines_ret { }; typedef struct guestfs_sh_lines_ret guestfs_sh_lines_ret; +struct guestfs_glob_expand_args { + char *pattern; +}; +typedef struct guestfs_glob_expand_args guestfs_glob_expand_args; + +struct guestfs_glob_expand_ret { + struct { + u_int paths_len; + str *paths_val; + } paths; +}; +typedef struct guestfs_glob_expand_ret guestfs_glob_expand_ret; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -1082,7 +1095,8 @@ enum guestfs_procedure { GUESTFS_PROC_NTFS_3G_PROBE = 110, GUESTFS_PROC_SH = 111, GUESTFS_PROC_SH_LINES = 112, - GUESTFS_PROC_NR_PROCS = 112 + 1, + GUESTFS_PROC_GLOB_EXPAND = 113, + GUESTFS_PROC_NR_PROCS = 113 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -1287,6 +1301,8 @@ extern bool_t xdr_guestfs_sh_args (XDR *, guestfs_sh_args*); extern bool_t xdr_guestfs_sh_ret (XDR *, guestfs_sh_ret*); extern bool_t xdr_guestfs_sh_lines_args (XDR *, guestfs_sh_lines_args*); extern bool_t xdr_guestfs_sh_lines_ret (XDR *, guestfs_sh_lines_ret*); +extern bool_t xdr_guestfs_glob_expand_args (XDR *, guestfs_glob_expand_args*); +extern bool_t xdr_guestfs_glob_expand_ret (XDR *, guestfs_glob_expand_ret*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -1450,6 +1466,8 @@ extern bool_t xdr_guestfs_sh_args (); extern bool_t xdr_guestfs_sh_ret (); extern bool_t xdr_guestfs_sh_lines_args (); extern bool_t xdr_guestfs_sh_lines_ret (); +extern bool_t xdr_guestfs_glob_expand_args (); +extern bool_t xdr_guestfs_glob_expand_ret (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index 94d2e849..d6627267 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -748,6 +748,14 @@ struct guestfs_sh_lines_ret { str lines<>; }; +struct guestfs_glob_expand_args { + string pattern<>; +}; + +struct guestfs_glob_expand_ret { + str paths<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -861,6 +869,7 @@ enum guestfs_procedure { GUESTFS_PROC_NTFS_3G_PROBE = 110, GUESTFS_PROC_SH = 111, GUESTFS_PROC_SH_LINES = 112, + GUESTFS_PROC_GLOB_EXPAND = 113, GUESTFS_PROC_NR_PROCS }; -- cgit From 394c8bec21d47b74567a4148fdbf87318c301441 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 09:35:43 +0100 Subject: Add 'glob' command for guestfish. --- fish/Makefile.am | 1 + fish/fish.c | 14 ++++- fish/fish.h | 9 ++- fish/glob.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ guestfish.pod | 36 ++++++++++++ 5 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 fish/glob.c diff --git a/fish/Makefile.am b/fish/Makefile.am index c3fbc6f1..1e5b1081 100644 --- a/fish/Makefile.am +++ b/fish/Makefile.am @@ -26,6 +26,7 @@ guestfish_SOURCES = \ edit.c \ fish.c \ fish.h \ + glob.c \ lcd.c guestfish_CFLAGS = \ diff --git a/fish/fish.c b/fish/fish.c index e66880f2..bf82b8af 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -56,7 +56,6 @@ static void interactive (void); static void shell_script (void); static void script (int prompt); static void cmdline (char *argv[], int optind, int argc); -static int issue_command (const char *cmd, char *argv[]); static void initialize_readline (void); static void cleanup_readline (void); static void add_history_line (const char *); @@ -569,7 +568,7 @@ cmdline (char *argv[], int optind, int argc) } } -static int +int issue_command (const char *cmd, char *argv[]) { int argc; @@ -601,6 +600,8 @@ issue_command (const char *cmd, char *argv[]) return do_edit (cmd, argc, argv); else if (strcasecmp (cmd, "lcd") == 0) return do_lcd (cmd, argc, argv); + else if (strcasecmp (cmd, "glob") == 0) + return do_glob (cmd, argc, argv); else return run_action (cmd, argc, argv); } @@ -622,6 +623,8 @@ list_builtin_commands (void) "edit", _("edit a file in the image")); printf ("%-20s %s\n", "lcd", _("local change directory")); + printf ("%-20s %s\n", + "glob", _("expand wildcards in command")); /* actions are printed after this (see list_commands) */ } @@ -676,6 +679,13 @@ display_builtin_command (const char *cmd) " Change guestfish's current directory. This command is\n" " useful if you want to download files to a particular\n" " place.\n")); + else if (strcasecmp (cmd, "glob") == 0) + printf (_("glob - expand wildcards in command\n" + " glob [ ...]\n" + "\n" + " Glob runs with wildcards expanded in any\n" + " command args. Note that the command is run repeatedly\n" + " once for each expanded argument.\n")); else if (strcasecmp (cmd, "help") == 0) printf (_("help - display a list of commands or help on a command\n" " help cmd\n" diff --git a/fish/fish.h b/fish/fish.h index 028deecf..88158071 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -34,6 +34,7 @@ extern guestfs_h *g; extern int quit; extern int verbose; +extern int issue_command (const char *cmd, char *argv[]); extern void pod2text (const char *heading, const char *body); extern void list_builtin_commands (void); extern void display_builtin_command (const char *cmd); @@ -69,6 +70,9 @@ extern int do_edit (const char *cmd, int argc, char *argv[]); /* in lcd.c */ extern int do_lcd (const char *cmd, int argc, char *argv[]); +/* in glob.c */ +extern int do_glob (const char *cmd, int argc, char *argv[]); + /* This should just list all the built-in commands so they can * be added to the generated auto-completion code. */ @@ -77,7 +81,8 @@ extern int do_lcd (const char *cmd, int argc, char *argv[]); "quit", "exit", "q", \ "alloc", "allocate", \ "echo", \ - "edit", "vi", "emacs" \ - "lcd" + "edit", "vi", "emacs", \ + "lcd", \ + "glob" #endif /* FISH_H */ diff --git a/fish/glob.c b/fish/glob.c new file mode 100644 index 00000000..827e0624 --- /dev/null +++ b/fish/glob.c @@ -0,0 +1,163 @@ +/* guestfish - the filesystem interactive shell + * Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include +#include + +#include "fish.h" + +/* A bit tricky because in the case where there are multiple + * paths we have to perform a Cartesian product. + */ +static void glob_issue (char *cmd, int argc, char ***globs, int *posn, int *count, int *r); + +int +do_glob (const char *cmd, int argc, char *argv[]) +{ + /* For 'glob cmd foo /s* /usr/s*' this could be: + * + * (globs[0]) globs[1] globs[1] globs[2] + * (cmd) foo /sbin /usr/sbin + * /srv /usr/share + * /sys /usr/src + * + * and then we call every combination (ie. 1x3x3) of + * argv[1-]. + */ + char **globs[argc]; + int posn[argc]; + int count[argc]; + int i, r = 0; + + if (argc < 1) { + fprintf (stderr, _("use 'glob command [args...]'\n")); + return -1; + } + + /* This array will record the current execution position + * in the Cartesian product. + * NB. globs[0], posn[0], count[0] are ignored. + */ + for (i = 1; i < argc; ++i) + posn[i] = 0; + for (i = 1; i < argc; ++i) + globs[i] = NULL; + + for (i = 1; i < argc; ++i) { + char **pp; + + /* Only if it begins with '/' can it possibly be a globbable path. */ + if (argv[i][0] == '/') { + pp = guestfs_glob_expand (g, argv[i]); + if (pp == NULL) { /* real error in glob_expand */ + fprintf (stderr, _("glob: guestfs_glob_expand call failed: %s\n"), + argv[i]); + goto error0; + } + + /* If there were no matches, then we add a single element list + * containing just the original argv[i] string. + */ + if (pp[0] == NULL) { + char **pp2; + + pp2 = realloc (pp, sizeof (char *) * 2); + if (pp2 == NULL) { + perror ("realloc"); + free (pp); + goto error0; + } + pp = pp2; + + pp[0] = strdup (argv[i]); + if (pp[0] == NULL) { + perror ("strdup"); + free (pp); + goto error0; + } + pp[1] = NULL; + } + } + /* Doesn't begin with '/' */ + else { + pp = malloc (sizeof (char *) * 2); + if (pp == NULL) { + perror ("malloc"); + goto error0; + } + pp[0] = strdup (argv[i]); + if (pp[0] == NULL) { + perror ("strdup"); + free (pp); + goto error0; + } + pp[1] = NULL; + } + + globs[i] = pp; + count[i] = count_strings (pp); + } + + /* Issue the commands. */ + glob_issue (argv[0], argc, globs, posn, count, &r); + + /* Free resources. */ + error0: + for (i = 1; i < argc; ++i) + if (globs[i]) + free_strings (globs[i]); + return r; +} + +static void +glob_issue (char *cmd, int argc, + char ***globs, int *posn, int *count, + int *r) +{ + int i; + char *argv[argc+1]; + + argv[0] = cmd; + argv[argc] = NULL; + + again: + printf ("%s", argv[0]); + for (i = 1; i < argc; ++i) { + argv[i] = globs[i][posn[i]]; + printf (" %s", argv[i]); + } + printf ("\n"); + + if (issue_command (argv[0], &argv[1]) == -1) + r = -1; /* ... but don't exit */ + + for (i = argc-1; i >= 1; --i) { + posn[i]++; + if (posn[i] < count[i]) + break; + posn[i] = 0; + } + if (i == 0) /* All done. */ + return; + + goto again; +} diff --git a/guestfish.pod b/guestfish.pod index 19c9d434..d83a61ca 100644 --- a/guestfish.pod +++ b/guestfish.pod @@ -184,6 +184,33 @@ a space-separated list, enclosed in quotes. For example: vgcreate VG "/dev/sda1 /dev/sdb1" +=head1 WILDCARDS AND GLOBBING + +Neither guestfish nor the underlying guestfs API performs +wildcard expansion (globbing) by default. So for example the +following will not do what you expect: + + rm-rf /home/* + +Assuming you don't have a directory literally called C +then the above command will return an error. + +To perform wildcard expansion, use the C command. + + glob rm-rf /home/* + +runs C on each path that matches (ie. potentially running +the command many times), equivalent to: + + rm-rf /home/jim + rm-rf /home/joe + rm-rf /home/mary + +C only works on simple guest paths and not on device names. + +If you have several parameters, each containing a wildcard, then glob +will perform a cartesian product. + =head1 COMMENTS Any line which starts with a I<#> character is treated as a comment @@ -294,6 +321,15 @@ itself. Note that C won't do what you might expect. +=head2 glob + + glob command args... + +Expand wildcards in any paths in the args list, and run C +repeatedly on each matching path. + +See section WILDCARDS AND GLOBBING. + @ACTIONS@ =head1 ENVIRONMENT VARIABLES -- cgit From 8736b6fa7173ddc33687c4bd9e3fb22d65922fd1 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 10:02:11 +0100 Subject: Version 1.0.50. --- configure.ac | 2 +- po/libguestfs.pot | 63 ++++++++++++++++++++++++++++++++++--------------------- po/pl.po | 63 ++++++++++++++++++++++++++++++++++--------------------- 3 files changed, 79 insertions(+), 49 deletions(-) diff --git a/configure.ac b/configure.ac index 5cb7c6de..eeaa988e 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.49]) +AC_INIT([libguestfs],[1.0.50]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) diff --git a/po/libguestfs.pot b/po/libguestfs.pot index 090d4d37..67d4dc1e 100644 --- a/po/libguestfs.pot +++ b/po/libguestfs.pot @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-19 19:37+0100\n" +"POT-Creation-Date: 2009-06-22 10:02+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -37,7 +37,7 @@ msgstr "" msgid "use '%s filename' to edit a file\n" msgstr "" -#: fish/fish.c:89 +#: fish/fish.c:88 #, c-format msgid "" "guestfish: guest filesystem shell\n" @@ -65,17 +65,17 @@ msgid "" "For more information, see the manpage guestfish(1).\n" msgstr "" -#: fish/fish.c:144 +#: fish/fish.c:143 #, c-format msgid "guestfs_create: failed to create handle\n" msgstr "" -#: fish/fish.c:235 +#: fish/fish.c:234 #, c-format msgid "guestfish: unexpected command line option 0x%x\n" msgstr "" -#: fish/fish.c:381 +#: fish/fish.c:380 #, c-format msgid "" "\n" @@ -87,61 +87,65 @@ msgid "" "\n" msgstr "" -#: fish/fish.c:458 +#: fish/fish.c:457 #, c-format msgid "guestfish: unterminated double quote\n" msgstr "" -#: fish/fish.c:463 fish/fish.c:478 +#: fish/fish.c:462 fish/fish.c:477 #, c-format msgid "guestfish: command arguments not separated by whitespace\n" msgstr "" -#: fish/fish.c:473 +#: fish/fish.c:472 #, c-format msgid "guestfish: unterminated single quote\n" msgstr "" -#: fish/fish.c:514 +#: fish/fish.c:513 #, c-format msgid "guestfish: internal error parsing string at '%s'\n" msgstr "" -#: fish/fish.c:527 +#: fish/fish.c:526 #, c-format msgid "guestfish: too many arguments\n" msgstr "" -#: fish/fish.c:554 +#: fish/fish.c:553 #, c-format msgid "guestfish: empty command on command line\n" msgstr "" -#: fish/fish.c:613 +#: fish/fish.c:614 msgid "display a list of commands or help on a command" msgstr "" -#: fish/fish.c:615 +#: fish/fish.c:616 msgid "quit guestfish" msgstr "" -#: fish/fish.c:618 +#: fish/fish.c:619 msgid "allocate an image" msgstr "" -#: fish/fish.c:620 +#: fish/fish.c:621 msgid "display a line of text" msgstr "" -#: fish/fish.c:622 +#: fish/fish.c:623 msgid "edit a file in the image" msgstr "" -#: fish/fish.c:624 +#: fish/fish.c:625 msgid "local change directory" msgstr "" -#: fish/fish.c:636 +#: fish/fish.c:627 +msgid "expand wildcards in command" +msgstr "" + +#: fish/fish.c:639 #, c-format msgid "" "alloc - allocate an image\n" @@ -161,7 +165,7 @@ msgid "" " sects number of 512 byte sectors\n" msgstr "" -#: fish/fish.c:652 +#: fish/fish.c:655 #, c-format msgid "" "echo - display a line of text\n" @@ -170,7 +174,7 @@ msgid "" " This echos the parameters to the terminal.\n" msgstr "" -#: fish/fish.c:659 +#: fish/fish.c:662 #, c-format msgid "" "edit - edit a file in the image\n" @@ -188,7 +192,7 @@ msgid "" " (> 2 MB) or binary files containing \\0 bytes.\n" msgstr "" -#: fish/fish.c:673 +#: fish/fish.c:676 #, c-format msgid "" "lcd - local change directory\n" @@ -199,7 +203,18 @@ msgid "" " place.\n" msgstr "" -#: fish/fish.c:680 +#: fish/fish.c:683 +#, c-format +msgid "" +"glob - expand wildcards in command\n" +" glob [ ...]\n" +"\n" +" Glob runs with wildcards expanded in any\n" +" command args. Note that the command is run repeatedly\n" +" once for each expanded argument.\n" +msgstr "" + +#: fish/fish.c:690 #, c-format msgid "" "help - display a list of commands or help on a command\n" @@ -207,14 +222,14 @@ msgid "" " help\n" msgstr "" -#: fish/fish.c:686 +#: fish/fish.c:696 #, c-format msgid "" "quit - quit guestfish\n" " quit\n" msgstr "" -#: fish/fish.c:689 +#: fish/fish.c:699 #, c-format msgid "%s: command not known, use -h to list all commands\n" msgstr "" diff --git a/po/pl.po b/po/pl.po index 12a01028..8852f2cb 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: libguestfs 1.0.31\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-19 19:37+0100\n" +"POT-Creation-Date: 2009-06-22 10:02+0100\n" "PO-Revision-Date: 2009-05-26 00:50+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" @@ -34,7 +34,7 @@ msgstr "nie można przeanalizować określenia rozmiaru \"%s\"\n" msgid "use '%s filename' to edit a file\n" msgstr "użyj \"%s nazwapliku\", aby zmodyfikować plik\n" -#: fish/fish.c:89 +#: fish/fish.c:88 #, fuzzy, c-format msgid "" "guestfish: guest filesystem shell\n" @@ -84,17 +84,17 @@ msgstr "" " -V|--version Wyświetla wersję i kończy pracę\n" "Aby dowiedzieć się więcej, zobacz stronę podręcznika guestfish(1).\n" -#: fish/fish.c:144 +#: fish/fish.c:143 #, c-format msgid "guestfs_create: failed to create handle\n" msgstr "guestfs_create: utworzenie programu obsługi nie powiodło się\n" -#: fish/fish.c:235 +#: fish/fish.c:234 #, c-format msgid "guestfish: unexpected command line option 0x%x\n" msgstr "guestfish: nieoczekiwane polecenie wiersza poleceń 0x%x\n" -#: fish/fish.c:381 +#: fish/fish.c:380 #, c-format msgid "" "\n" @@ -113,61 +113,65 @@ msgstr "" " \"quit\", aby zakończyć powłokę\n" "\n" -#: fish/fish.c:458 +#: fish/fish.c:457 #, c-format msgid "guestfish: unterminated double quote\n" msgstr "guestfish: niezakończony podwójny cudzysłów\n" -#: fish/fish.c:463 fish/fish.c:478 +#: fish/fish.c:462 fish/fish.c:477 #, c-format msgid "guestfish: command arguments not separated by whitespace\n" msgstr "guestfish: parametry poleceń nie są oddzielone spacjami\n" -#: fish/fish.c:473 +#: fish/fish.c:472 #, c-format msgid "guestfish: unterminated single quote\n" msgstr "guestfish: niezakończony pojedynczy cudzysłów\n" -#: fish/fish.c:514 +#: fish/fish.c:513 #, c-format msgid "guestfish: internal error parsing string at '%s'\n" msgstr "guestfish: wewnętrzny błąd analizowania łańcucha \"%s\"\n" -#: fish/fish.c:527 +#: fish/fish.c:526 #, c-format msgid "guestfish: too many arguments\n" msgstr "guestfish: za dużo parametrów\n" -#: fish/fish.c:554 +#: fish/fish.c:553 #, c-format msgid "guestfish: empty command on command line\n" msgstr "guestfish: puste polecenie wiersza poleceń\n" -#: fish/fish.c:613 +#: fish/fish.c:614 msgid "display a list of commands or help on a command" msgstr "wyświetla listę poleceń lub pomoc polecenia" -#: fish/fish.c:615 +#: fish/fish.c:616 msgid "quit guestfish" msgstr "kończy pracę guestfish" -#: fish/fish.c:618 +#: fish/fish.c:619 msgid "allocate an image" msgstr "przydziela obraz" -#: fish/fish.c:620 +#: fish/fish.c:621 msgid "display a line of text" msgstr "wyświetla wiersz tekstu" -#: fish/fish.c:622 +#: fish/fish.c:623 msgid "edit a file in the image" msgstr "modyfikuje plik w obrazie" -#: fish/fish.c:624 +#: fish/fish.c:625 msgid "local change directory" msgstr "" -#: fish/fish.c:636 +#: fish/fish.c:627 +msgid "expand wildcards in command" +msgstr "" + +#: fish/fish.c:639 #, c-format msgid "" "alloc - allocate an image\n" @@ -203,7 +207,7 @@ msgstr "" " G lub GB liczba gigabajtów\n" " sektory liczba 512 bajtowych sektorów\n" -#: fish/fish.c:652 +#: fish/fish.c:655 #, c-format msgid "" "echo - display a line of text\n" @@ -216,7 +220,7 @@ msgstr "" "\n" " Wyświetla ostatnie parametry w terminalu.\n" -#: fish/fish.c:659 +#: fish/fish.c:662 #, c-format msgid "" "edit - edit a file in the image\n" @@ -248,7 +252,7 @@ msgstr "" " UWAGA: nie będzie działało poprawnie dla dużych plików\n" " (> 2 MB) lub plików binarnych zawierających \\0 bajtów.\n" -#: fish/fish.c:673 +#: fish/fish.c:676 #, c-format msgid "" "lcd - local change directory\n" @@ -259,7 +263,18 @@ msgid "" " place.\n" msgstr "" -#: fish/fish.c:680 +#: fish/fish.c:683 +#, c-format +msgid "" +"glob - expand wildcards in command\n" +" glob [ ...]\n" +"\n" +" Glob runs with wildcards expanded in any\n" +" command args. Note that the command is run repeatedly\n" +" once for each expanded argument.\n" +msgstr "" + +#: fish/fish.c:690 #, c-format msgid "" "help - display a list of commands or help on a command\n" @@ -270,7 +285,7 @@ msgstr "" " help polecenie\n" " help\n" -#: fish/fish.c:686 +#: fish/fish.c:696 #, c-format msgid "" "quit - quit guestfish\n" @@ -279,7 +294,7 @@ msgstr "" "quit - kończy pracę guestfish\n" " quit\n" -#: fish/fish.c:689 +#: fish/fish.c:699 #, c-format msgid "%s: command not known, use -h to list all commands\n" msgstr "" -- cgit From 23273711bdbb07cabb19c095797a33d71b7fe711 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 12:23:12 +0100 Subject: Rebuild supermin appliance when the daemon is updated. --- appliance/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appliance/Makefile.am b/appliance/Makefile.am index d692c7f4..d15e5f25 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -80,7 +80,7 @@ if SUPERMIN # First we need to decide which files go in and out of the supermin # appliance. This decision is made by 'supermin-split.sh'. $(SUPERMINFILES): supermin.incfiles -supermin.incfiles: $(top_builddir)/initramfs/fakeroot.log supermin-split.sh +supermin.incfiles: $(top_builddir)/initramfs/fakeroot.log $(top_builddir)/daemon/guestfsd supermin-split.sh rm -f supermin.incfiles $(SUPERMINFILES) bash supermin-split.sh -- cgit From bb3a781783734e9f89f2cfa2cabca1efb32bcc45 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 12:23:31 +0100 Subject: test-boot-realistic rule should boot the rescue shell. --- appliance/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appliance/Makefile.am b/appliance/Makefile.am index d15e5f25..74157b08 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -126,7 +126,7 @@ test-boot-realistic: emptydisk -m 384 \ -kernel $(VMLINUZ) -initrd $(INITRAMFSIMG) \ -hda emptydisk \ - -append "console=ttyS0 guestfs=10.0.2.4:6666" \ + -append "console=ttyS0 guestfs=10.0.2.4:6666 guestfs_rescue=1" \ -nographic \ -serial stdio \ -net channel,6666:unix:/tmp/sock,server,nowait \ -- cgit From baa5a4099425803f01fa7eb2dce40c80c9c7611e Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 12:23:56 +0100 Subject: The 'debug sh' command now uses a real shell. --- daemon/debug.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/daemon/debug.c b/daemon/debug.c index ce760e6f..d6e469b4 100644 --- a/daemon/debug.c +++ b/daemon/debug.c @@ -189,23 +189,47 @@ debug_segv (const char *subcmd, int argc, char *const *const argv) return NULL; } -/* Run an arbitrary shell command. */ +/* Run an arbitrary shell command using /bin/sh from the appliance. + * + * Note this is somewhat different from the ordinary guestfs_sh command + * because it's not using the guest shell, and is not chrooted. + * + * Also we ignore any errors and you can see the full output if you + * add 2>&1 to the end of the command string. + */ static char * debug_sh (const char *subcmd, int argc, char *const *const argv) { - int r; - char *out, *err; + char *cmd; + int len, i, j; + char *out; - r = commandv (&out, &err, argv); - if (r == -1) { - reply_with_error ("sh: %s", err); - free (out); - free (err); + if (argc < 1) { + reply_with_error ("debug: sh: expecting a command to run"); return NULL; } - free (err); + /* guestfish splits the parameter(s) into a list of strings, + * and we have to reassemble them here. Not ideal. XXX + */ + for (i = len = 0; i < argc; ++i) + len += strlen (argv[i]) + 1; + cmd = malloc (len); + if (!cmd) { + reply_with_perror ("malloc"); + return NULL; + } + for (i = j = 0; i < argc; ++i) { + len = strlen (argv[i]); + memcpy (&cmd[j], argv[i], len); + j += len; + cmd[j] = ' '; + j++; + } + cmd[j-1] = '\0'; + command (&out, NULL, "/bin/sh", "-c", cmd, NULL); + free (cmd); return out; } -- cgit From 697f50aea8638fc9f5d1a250de6c1b9f4697500e Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 12:24:14 +0100 Subject: Check return value from close() call. --- daemon/file.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon/file.c b/daemon/file.c index 0eef0c09..3f07ffc1 100644 --- a/daemon/file.c +++ b/daemon/file.c @@ -58,7 +58,11 @@ do_touch (char *path) return -1; } - close (fd); + if (close (fd) == -1) { + reply_with_perror ("close: %s", path); + return -1; + } + return 0; } -- cgit From 7c2e6fd3e98840c91b0bac8e13e2eb564810ba04 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 12:24:27 +0100 Subject: Include the lsof package. --- appliance/packagelist.in | 1 + 1 file changed, 1 insertion(+) diff --git a/appliance/packagelist.in b/appliance/packagelist.in index f335531f..68270f07 100644 --- a/appliance/packagelist.in +++ b/appliance/packagelist.in @@ -16,6 +16,7 @@ file grub iputils kernel +lsof lvm2 MAKEDEV module-init-tools -- cgit From 3dfc0a8c5da8f3a4a6c13e936d4306b79b231dde Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 12:40:57 +0100 Subject: Make CHROOT_IN/OUT macros should loudly if the syscall fails. --- daemon/daemon.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/daemon/daemon.h b/daemon/daemon.h index 115db093..70f0beef 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -162,11 +162,22 @@ extern void reply (xdrproc_t xdrp, char *ret); * (2) You must not change directory! cwd must always be "/", otherwise * we can't escape our own chroot. * (3) All paths specified must be absolute. - * (4) CHROOT_OUT does not affect errno. + * (4) Neither macro affects errno. */ -#define CHROOT_IN chroot ("/sysroot"); -#define CHROOT_OUT \ - do { int old_errno = errno; chroot ("."); errno = old_errno; } while (0) +#define CHROOT_IN \ + do { \ + int __old_errno = errno; \ + if (chroot ("/sysroot") == -1) \ + perror ("CHROOT_IN: sysroot"); \ + errno = __old_errno; \ + } while (0) +#define CHROOT_OUT \ + do { \ + int __old_errno = errno; \ + if (chroot (".") == -1) \ + perror ("CHROOT_OUT: ."); \ + errno = __old_errno; \ + } while (0) /* Marks functions which are not implemented. * NB. Cannot be used for FileIn functions. -- cgit From ca4a0c718083db4485edebe4d1b931090e098fa5 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 14:14:45 +0100 Subject: Missing \n character in Ruby bindings. --- ruby/ext/guestfs/_guestfs.c | 21 ++++++++++++++------- src/generator.ml | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index bcb6fefc..f78506b5 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -83,7 +83,8 @@ static VALUE ruby_guestfs_test0 (VALUE gv, VALUE strv, VALUE optstrv, VALUE strl rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "str", "test0"); const char *optstr = !NIL_P (optstrv) ? StringValueCStr (optstrv) : NULL; - char **strlist; { + char **strlist; + { int i, len; len = RARRAY_LEN (strlistv); strlist = guestfs_safe_malloc (g, sizeof (char *) * (len+1)); @@ -2268,7 +2269,8 @@ static VALUE ruby_guestfs_vgcreate (VALUE gv, VALUE volgroupv, VALUE physvolsv) if (!volgroup) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "volgroup", "vgcreate"); - char **physvols; { + char **physvols; + { int i, len; len = RARRAY_LEN (physvolsv); physvols = guestfs_safe_malloc (g, sizeof (char *) * (len+1)); @@ -2354,7 +2356,8 @@ static VALUE ruby_guestfs_sfdisk (VALUE gv, VALUE devicev, VALUE cylsv, VALUE he int cyls = NUM2INT (cylsv); int heads = NUM2INT (headsv); int sectors = NUM2INT (sectorsv); - char **lines; { + char **lines; + { int i, len; len = RARRAY_LEN (linesv); lines = guestfs_safe_malloc (g, sizeof (char *) * (len+1)); @@ -2511,7 +2514,8 @@ static VALUE ruby_guestfs_command (VALUE gv, VALUE argumentsv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "command"); - char **arguments; { + char **arguments; + { int i, len; len = RARRAY_LEN (argumentsv); arguments = guestfs_safe_malloc (g, sizeof (char *) * (len+1)); @@ -2541,7 +2545,8 @@ static VALUE ruby_guestfs_command_lines (VALUE gv, VALUE argumentsv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "command_lines"); - char **arguments; { + char **arguments; + { int i, len; len = RARRAY_LEN (argumentsv); arguments = guestfs_safe_malloc (g, sizeof (char *) * (len+1)); @@ -3191,7 +3196,8 @@ static VALUE ruby_guestfs_debug (VALUE gv, VALUE subcmdv, VALUE extraargsv) if (!subcmd) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "subcmd", "debug"); - char **extraargs; { + char **extraargs; + { int i, len; len = RARRAY_LEN (extraargsv); extraargs = guestfs_safe_malloc (g, sizeof (char *) * (len+1)); @@ -3849,7 +3855,8 @@ static VALUE ruby_guestfs_vg_activate (VALUE gv, VALUE activatev, VALUE volgroup rb_raise (rb_eArgError, "%s: used handle after closing it", "vg_activate"); int activate = RTEST (activatev); - char **volgroups; { + char **volgroups; + { int i, len; len = RARRAY_LEN (volgroupsv); volgroups = guestfs_safe_malloc (g, sizeof (char *) * (len+1)); diff --git a/src/generator.ml b/src/generator.ml index b716e0af..d639199f 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -6507,7 +6507,7 @@ static VALUE ruby_guestfs_close (VALUE gv) | OptString n -> pr " const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n | StringList n -> - pr " char **%s;" n; + pr " char **%s;\n" n; pr " {\n"; pr " int i, len;\n"; pr " len = RARRAY_LEN (%sv);\n" n; -- cgit From 3eab7260a5e36ce6da0dff39456eb9100a13222b Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 14:24:52 +0100 Subject: Improve error message when appliance doesn't match library. --- daemon/stubs.c | 2 +- src/generator.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/daemon/stubs.c b/daemon/stubs.c index 2a6035cf..af87a994 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -3197,7 +3197,7 @@ void dispatch_incoming_message (XDR *xdr_in) glob_expand_stub (xdr_in); break; default: - reply_with_error ("dispatch_incoming_message: unknown procedure number %d", proc_nr); + reply_with_error ("dispatch_incoming_message: unknown procedure number %d, set LIBGUESTFS_PATH to point to the matching libguestfs appliance directory", proc_nr); } } diff --git a/src/generator.ml b/src/generator.ml index d639199f..f4b56050 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -3686,7 +3686,7 @@ and generate_daemon_actions () = ) daemon_functions; pr " default:\n"; - pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d\", proc_nr);\n"; + pr " reply_with_error (\"dispatch_incoming_message: unknown procedure number %%d, set LIBGUESTFS_PATH to point to the matching libguestfs appliance directory\", proc_nr);\n"; pr " }\n"; pr "}\n"; pr "\n"; -- cgit From 227b1eea90713d190a9cf5463af106af0b4eee2c Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 15:07:42 +0100 Subject: Check parameter types in Ruby bindings (RHBZ#507346). --- ruby/ext/guestfs/_guestfs.c | 152 ++++++++++++++++++++++++++++++++++++++++++++ ruby/tests/tc_rhbz507346.rb | 42 ++++++++++++ src/generator.ml | 2 + 3 files changed, 196 insertions(+) create mode 100644 ruby/tests/tc_rhbz507346.rb diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index f78506b5..202c3cac 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -78,12 +78,14 @@ static VALUE ruby_guestfs_test0 (VALUE gv, VALUE strv, VALUE optstrv, VALUE strl if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0"); + Check_Type (strv, T_STRING); const char *str = StringValueCStr (strv); if (!str) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "str", "test0"); const char *optstr = !NIL_P (optstrv) ? StringValueCStr (optstrv) : NULL; char **strlist; + Check_Type (strlistv, T_ARRAY); { int i, len; len = RARRAY_LEN (strlistv); @@ -96,10 +98,12 @@ static VALUE ruby_guestfs_test0 (VALUE gv, VALUE strv, VALUE optstrv, VALUE strl } int b = RTEST (bv); int integer = NUM2INT (integerv); + Check_Type (fileinv, T_STRING); const char *filein = StringValueCStr (fileinv); if (!filein) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "filein", "test0"); + Check_Type (fileoutv, T_STRING); const char *fileout = StringValueCStr (fileoutv); if (!fileout) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -122,6 +126,7 @@ static VALUE ruby_guestfs_test0rint (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rint"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -160,6 +165,7 @@ static VALUE ruby_guestfs_test0rint64 (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rint64"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -198,6 +204,7 @@ static VALUE ruby_guestfs_test0rbool (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rbool"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -236,6 +243,7 @@ static VALUE ruby_guestfs_test0rconststring (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rconststring"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -274,6 +282,7 @@ static VALUE ruby_guestfs_test0rstring (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rstring"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -316,6 +325,7 @@ static VALUE ruby_guestfs_test0rstringlist (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rstringlist"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -370,6 +380,7 @@ static VALUE ruby_guestfs_test0rintbool (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rintbool"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -416,6 +427,7 @@ static VALUE ruby_guestfs_test0rpvlist (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rpvlist"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -496,6 +508,7 @@ static VALUE ruby_guestfs_test0rvglist (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rvglist"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -586,6 +599,7 @@ static VALUE ruby_guestfs_test0rlvlist (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rlvlist"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -670,6 +684,7 @@ static VALUE ruby_guestfs_test0rstat (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rstat"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -738,6 +753,7 @@ static VALUE ruby_guestfs_test0rstatvfs (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rstatvfs"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -802,6 +818,7 @@ static VALUE ruby_guestfs_test0rhashtable (VALUE gv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "test0rhashtable"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -907,6 +924,7 @@ static VALUE ruby_guestfs_add_drive (VALUE gv, VALUE filenamev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "add_drive"); + Check_Type (filenamev, T_STRING); const char *filename = StringValueCStr (filenamev); if (!filename) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -928,6 +946,7 @@ static VALUE ruby_guestfs_add_cdrom (VALUE gv, VALUE filenamev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "add_cdrom"); + Check_Type (filenamev, T_STRING); const char *filename = StringValueCStr (filenamev); if (!filename) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -949,6 +968,7 @@ static VALUE ruby_guestfs_add_drive_ro (VALUE gv, VALUE filenamev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "add_drive_ro"); + Check_Type (filenamev, T_STRING); const char *filename = StringValueCStr (filenamev); if (!filename) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -970,6 +990,7 @@ static VALUE ruby_guestfs_config (VALUE gv, VALUE qemuparamv, VALUE qemuvaluev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "config"); + Check_Type (qemuparamv, T_STRING); const char *qemuparam = StringValueCStr (qemuparamv); if (!qemuparam) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -992,6 +1013,7 @@ static VALUE ruby_guestfs_set_qemu (VALUE gv, VALUE qemuv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "set_qemu"); + Check_Type (qemuv, T_STRING); const char *qemu = StringValueCStr (qemuv); if (!qemu) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1030,6 +1052,7 @@ static VALUE ruby_guestfs_set_path (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "set_path"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1068,6 +1091,7 @@ static VALUE ruby_guestfs_set_append (VALUE gv, VALUE appendv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "set_append"); + Check_Type (appendv, T_STRING); const char *append = StringValueCStr (appendv); if (!append) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1312,10 +1336,12 @@ static VALUE ruby_guestfs_mount (VALUE gv, VALUE devicev, VALUE mountpointv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "mount"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "device", "mount"); + Check_Type (mountpointv, T_STRING); const char *mountpoint = StringValueCStr (mountpointv); if (!mountpoint) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1354,6 +1380,7 @@ static VALUE ruby_guestfs_touch (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "touch"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1375,6 +1402,7 @@ static VALUE ruby_guestfs_cat (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "cat"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1398,6 +1426,7 @@ static VALUE ruby_guestfs_ll (VALUE gv, VALUE directoryv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "ll"); + Check_Type (directoryv, T_STRING); const char *directory = StringValueCStr (directoryv); if (!directory) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1421,6 +1450,7 @@ static VALUE ruby_guestfs_ls (VALUE gv, VALUE directoryv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "ls"); + Check_Type (directoryv, T_STRING); const char *directory = StringValueCStr (directoryv); if (!directory) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1696,6 +1726,7 @@ static VALUE ruby_guestfs_read_lines (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "read_lines"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1725,6 +1756,7 @@ static VALUE ruby_guestfs_aug_init (VALUE gv, VALUE rootv, VALUE flagsv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_init"); + Check_Type (rootv, T_STRING); const char *root = StringValueCStr (rootv); if (!root) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1764,6 +1796,7 @@ static VALUE ruby_guestfs_aug_defvar (VALUE gv, VALUE namev, VALUE exprv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_defvar"); + Check_Type (namev, T_STRING); const char *name = StringValueCStr (namev); if (!name) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1786,14 +1819,17 @@ static VALUE ruby_guestfs_aug_defnode (VALUE gv, VALUE namev, VALUE exprv, VALUE if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_defnode"); + Check_Type (namev, T_STRING); const char *name = StringValueCStr (namev); if (!name) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "name", "aug_defnode"); + Check_Type (exprv, T_STRING); const char *expr = StringValueCStr (exprv); if (!expr) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "expr", "aug_defnode"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1819,6 +1855,7 @@ static VALUE ruby_guestfs_aug_get (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_get"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1842,10 +1879,12 @@ static VALUE ruby_guestfs_aug_set (VALUE gv, VALUE pathv, VALUE valv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_set"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "path", "aug_set"); + Check_Type (valv, T_STRING); const char *val = StringValueCStr (valv); if (!val) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1867,10 +1906,12 @@ static VALUE ruby_guestfs_aug_insert (VALUE gv, VALUE pathv, VALUE labelv, VALUE if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_insert"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "path", "aug_insert"); + Check_Type (labelv, T_STRING); const char *label = StringValueCStr (labelv); if (!label) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1893,6 +1934,7 @@ static VALUE ruby_guestfs_aug_rm (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_rm"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1914,10 +1956,12 @@ static VALUE ruby_guestfs_aug_mv (VALUE gv, VALUE srcv, VALUE destv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_mv"); + Check_Type (srcv, T_STRING); const char *src = StringValueCStr (srcv); if (!src) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "src", "aug_mv"); + Check_Type (destv, T_STRING); const char *dest = StringValueCStr (destv); if (!dest) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -1939,6 +1983,7 @@ static VALUE ruby_guestfs_aug_match (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_match"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2002,6 +2047,7 @@ static VALUE ruby_guestfs_aug_ls (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "aug_ls"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2031,6 +2077,7 @@ static VALUE ruby_guestfs_rm (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "rm"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2052,6 +2099,7 @@ static VALUE ruby_guestfs_rmdir (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "rmdir"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2073,6 +2121,7 @@ static VALUE ruby_guestfs_rm_rf (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "rm_rf"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2094,6 +2143,7 @@ static VALUE ruby_guestfs_mkdir (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "mkdir"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2115,6 +2165,7 @@ static VALUE ruby_guestfs_mkdir_p (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "mkdir_p"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2137,6 +2188,7 @@ static VALUE ruby_guestfs_chmod (VALUE gv, VALUE modev, VALUE pathv) rb_raise (rb_eArgError, "%s: used handle after closing it", "chmod"); int mode = NUM2INT (modev); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2160,6 +2212,7 @@ static VALUE ruby_guestfs_chown (VALUE gv, VALUE ownerv, VALUE groupv, VALUE pat int owner = NUM2INT (ownerv); int group = NUM2INT (groupv); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2181,6 +2234,7 @@ static VALUE ruby_guestfs_exists (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "exists"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2202,6 +2256,7 @@ static VALUE ruby_guestfs_is_file (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "is_file"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2223,6 +2278,7 @@ static VALUE ruby_guestfs_is_dir (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "is_dir"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2244,6 +2300,7 @@ static VALUE ruby_guestfs_pvcreate (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "pvcreate"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2265,11 +2322,13 @@ static VALUE ruby_guestfs_vgcreate (VALUE gv, VALUE volgroupv, VALUE physvolsv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "vgcreate"); + Check_Type (volgroupv, T_STRING); const char *volgroup = StringValueCStr (volgroupv); if (!volgroup) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "volgroup", "vgcreate"); char **physvols; + Check_Type (physvolsv, T_ARRAY); { int i, len; len = RARRAY_LEN (physvolsv); @@ -2298,10 +2357,12 @@ static VALUE ruby_guestfs_lvcreate (VALUE gv, VALUE logvolv, VALUE volgroupv, VA if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "lvcreate"); + Check_Type (logvolv, T_STRING); const char *logvol = StringValueCStr (logvolv); if (!logvol) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "logvol", "lvcreate"); + Check_Type (volgroupv, T_STRING); const char *volgroup = StringValueCStr (volgroupv); if (!volgroup) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2324,10 +2385,12 @@ static VALUE ruby_guestfs_mkfs (VALUE gv, VALUE fstypev, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "mkfs"); + Check_Type (fstypev, T_STRING); const char *fstype = StringValueCStr (fstypev); if (!fstype) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "fstype", "mkfs"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2349,6 +2412,7 @@ static VALUE ruby_guestfs_sfdisk (VALUE gv, VALUE devicev, VALUE cylsv, VALUE he if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "sfdisk"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2357,6 +2421,7 @@ static VALUE ruby_guestfs_sfdisk (VALUE gv, VALUE devicev, VALUE cylsv, VALUE he int heads = NUM2INT (headsv); int sectors = NUM2INT (sectorsv); char **lines; + Check_Type (linesv, T_ARRAY); { int i, len; len = RARRAY_LEN (linesv); @@ -2385,10 +2450,12 @@ static VALUE ruby_guestfs_write_file (VALUE gv, VALUE pathv, VALUE contentv, VAL if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "write_file"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "path", "write_file"); + Check_Type (contentv, T_STRING); const char *content = StringValueCStr (contentv); if (!content) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2411,6 +2478,7 @@ static VALUE ruby_guestfs_umount (VALUE gv, VALUE pathordevicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "umount"); + Check_Type (pathordevicev, T_STRING); const char *pathordevice = StringValueCStr (pathordevicev); if (!pathordevice) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2491,6 +2559,7 @@ static VALUE ruby_guestfs_file (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "file"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2515,6 +2584,7 @@ static VALUE ruby_guestfs_command (VALUE gv, VALUE argumentsv) rb_raise (rb_eArgError, "%s: used handle after closing it", "command"); char **arguments; + Check_Type (argumentsv, T_ARRAY); { int i, len; len = RARRAY_LEN (argumentsv); @@ -2546,6 +2616,7 @@ static VALUE ruby_guestfs_command_lines (VALUE gv, VALUE argumentsv) rb_raise (rb_eArgError, "%s: used handle after closing it", "command_lines"); char **arguments; + Check_Type (argumentsv, T_ARRAY); { int i, len; len = RARRAY_LEN (argumentsv); @@ -2582,6 +2653,7 @@ static VALUE ruby_guestfs_stat (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "stat"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2618,6 +2690,7 @@ static VALUE ruby_guestfs_lstat (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "lstat"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2654,6 +2727,7 @@ static VALUE ruby_guestfs_statvfs (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "statvfs"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2688,6 +2762,7 @@ static VALUE ruby_guestfs_tune2fs_l (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "tune2fs_l"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2717,6 +2792,7 @@ static VALUE ruby_guestfs_blockdev_setro (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_setro"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2738,6 +2814,7 @@ static VALUE ruby_guestfs_blockdev_setrw (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_setrw"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2759,6 +2836,7 @@ static VALUE ruby_guestfs_blockdev_getro (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_getro"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2780,6 +2858,7 @@ static VALUE ruby_guestfs_blockdev_getss (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_getss"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2801,6 +2880,7 @@ static VALUE ruby_guestfs_blockdev_getbsz (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_getbsz"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2822,6 +2902,7 @@ static VALUE ruby_guestfs_blockdev_setbsz (VALUE gv, VALUE devicev, VALUE blocks if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_setbsz"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2844,6 +2925,7 @@ static VALUE ruby_guestfs_blockdev_getsz (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_getsz"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2865,6 +2947,7 @@ static VALUE ruby_guestfs_blockdev_getsize64 (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_getsize64"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2886,6 +2969,7 @@ static VALUE ruby_guestfs_blockdev_flushbufs (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_flushbufs"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2907,6 +2991,7 @@ static VALUE ruby_guestfs_blockdev_rereadpt (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "blockdev_rereadpt"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2928,10 +3013,12 @@ static VALUE ruby_guestfs_upload (VALUE gv, VALUE filenamev, VALUE remotefilenam if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "upload"); + Check_Type (filenamev, T_STRING); const char *filename = StringValueCStr (filenamev); if (!filename) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "filename", "upload"); + Check_Type (remotefilenamev, T_STRING); const char *remotefilename = StringValueCStr (remotefilenamev); if (!remotefilename) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2953,10 +3040,12 @@ static VALUE ruby_guestfs_download (VALUE gv, VALUE remotefilenamev, VALUE filen if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "download"); + Check_Type (remotefilenamev, T_STRING); const char *remotefilename = StringValueCStr (remotefilenamev); if (!remotefilename) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "remotefilename", "download"); + Check_Type (filenamev, T_STRING); const char *filename = StringValueCStr (filenamev); if (!filename) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -2978,10 +3067,12 @@ static VALUE ruby_guestfs_checksum (VALUE gv, VALUE csumtypev, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "checksum"); + Check_Type (csumtypev, T_STRING); const char *csumtype = StringValueCStr (csumtypev); if (!csumtype) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "csumtype", "checksum"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3005,10 +3096,12 @@ static VALUE ruby_guestfs_tar_in (VALUE gv, VALUE tarfilev, VALUE directoryv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "tar_in"); + Check_Type (tarfilev, T_STRING); const char *tarfile = StringValueCStr (tarfilev); if (!tarfile) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "tarfile", "tar_in"); + Check_Type (directoryv, T_STRING); const char *directory = StringValueCStr (directoryv); if (!directory) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3030,10 +3123,12 @@ static VALUE ruby_guestfs_tar_out (VALUE gv, VALUE directoryv, VALUE tarfilev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "tar_out"); + Check_Type (directoryv, T_STRING); const char *directory = StringValueCStr (directoryv); if (!directory) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "directory", "tar_out"); + Check_Type (tarfilev, T_STRING); const char *tarfile = StringValueCStr (tarfilev); if (!tarfile) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3055,10 +3150,12 @@ static VALUE ruby_guestfs_tgz_in (VALUE gv, VALUE tarballv, VALUE directoryv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "tgz_in"); + Check_Type (tarballv, T_STRING); const char *tarball = StringValueCStr (tarballv); if (!tarball) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "tarball", "tgz_in"); + Check_Type (directoryv, T_STRING); const char *directory = StringValueCStr (directoryv); if (!directory) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3080,10 +3177,12 @@ static VALUE ruby_guestfs_tgz_out (VALUE gv, VALUE directoryv, VALUE tarballv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "tgz_out"); + Check_Type (directoryv, T_STRING); const char *directory = StringValueCStr (directoryv); if (!directory) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "directory", "tgz_out"); + Check_Type (tarballv, T_STRING); const char *tarball = StringValueCStr (tarballv); if (!tarball) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3105,10 +3204,12 @@ static VALUE ruby_guestfs_mount_ro (VALUE gv, VALUE devicev, VALUE mountpointv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "mount_ro"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "device", "mount_ro"); + Check_Type (mountpointv, T_STRING); const char *mountpoint = StringValueCStr (mountpointv); if (!mountpoint) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3130,14 +3231,17 @@ static VALUE ruby_guestfs_mount_options (VALUE gv, VALUE optionsv, VALUE devicev if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "mount_options"); + Check_Type (optionsv, T_STRING); const char *options = StringValueCStr (optionsv); if (!options) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "options", "mount_options"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "device", "mount_options"); + Check_Type (mountpointv, T_STRING); const char *mountpoint = StringValueCStr (mountpointv); if (!mountpoint) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3159,18 +3263,22 @@ static VALUE ruby_guestfs_mount_vfs (VALUE gv, VALUE optionsv, VALUE vfstypev, V if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "mount_vfs"); + Check_Type (optionsv, T_STRING); const char *options = StringValueCStr (optionsv); if (!options) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "options", "mount_vfs"); + Check_Type (vfstypev, T_STRING); const char *vfstype = StringValueCStr (vfstypev); if (!vfstype) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "vfstype", "mount_vfs"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "device", "mount_vfs"); + Check_Type (mountpointv, T_STRING); const char *mountpoint = StringValueCStr (mountpointv); if (!mountpoint) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3192,11 +3300,13 @@ static VALUE ruby_guestfs_debug (VALUE gv, VALUE subcmdv, VALUE extraargsv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "debug"); + Check_Type (subcmdv, T_STRING); const char *subcmd = StringValueCStr (subcmdv); if (!subcmd) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "subcmd", "debug"); char **extraargs; + Check_Type (extraargsv, T_ARRAY); { int i, len; len = RARRAY_LEN (extraargsv); @@ -3227,6 +3337,7 @@ static VALUE ruby_guestfs_lvremove (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "lvremove"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3248,6 +3359,7 @@ static VALUE ruby_guestfs_vgremove (VALUE gv, VALUE vgnamev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "vgremove"); + Check_Type (vgnamev, T_STRING); const char *vgname = StringValueCStr (vgnamev); if (!vgname) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3269,6 +3381,7 @@ static VALUE ruby_guestfs_pvremove (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "pvremove"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3290,10 +3403,12 @@ static VALUE ruby_guestfs_set_e2label (VALUE gv, VALUE devicev, VALUE labelv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "set_e2label"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "device", "set_e2label"); + Check_Type (labelv, T_STRING); const char *label = StringValueCStr (labelv); if (!label) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3315,6 +3430,7 @@ static VALUE ruby_guestfs_get_e2label (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "get_e2label"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3338,10 +3454,12 @@ static VALUE ruby_guestfs_set_e2uuid (VALUE gv, VALUE devicev, VALUE uuidv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "set_e2uuid"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "device", "set_e2uuid"); + Check_Type (uuidv, T_STRING); const char *uuid = StringValueCStr (uuidv); if (!uuid) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3363,6 +3481,7 @@ static VALUE ruby_guestfs_get_e2uuid (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "get_e2uuid"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3386,10 +3505,12 @@ static VALUE ruby_guestfs_fsck (VALUE gv, VALUE fstypev, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "fsck"); + Check_Type (fstypev, T_STRING); const char *fstype = StringValueCStr (fstypev); if (!fstype) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "fstype", "fsck"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3411,6 +3532,7 @@ static VALUE ruby_guestfs_zero (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "zero"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3432,10 +3554,12 @@ static VALUE ruby_guestfs_grub_install (VALUE gv, VALUE rootv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "grub_install"); + Check_Type (rootv, T_STRING); const char *root = StringValueCStr (rootv); if (!root) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "root", "grub_install"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3457,10 +3581,12 @@ static VALUE ruby_guestfs_cp (VALUE gv, VALUE srcv, VALUE destv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "cp"); + Check_Type (srcv, T_STRING); const char *src = StringValueCStr (srcv); if (!src) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "src", "cp"); + Check_Type (destv, T_STRING); const char *dest = StringValueCStr (destv); if (!dest) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3482,10 +3608,12 @@ static VALUE ruby_guestfs_cp_a (VALUE gv, VALUE srcv, VALUE destv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "cp_a"); + Check_Type (srcv, T_STRING); const char *src = StringValueCStr (srcv); if (!src) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "src", "cp_a"); + Check_Type (destv, T_STRING); const char *dest = StringValueCStr (destv); if (!dest) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3507,10 +3635,12 @@ static VALUE ruby_guestfs_mv (VALUE gv, VALUE srcv, VALUE destv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "mv"); + Check_Type (srcv, T_STRING); const char *src = StringValueCStr (srcv); if (!src) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "src", "mv"); + Check_Type (destv, T_STRING); const char *dest = StringValueCStr (destv); if (!dest) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3586,10 +3716,12 @@ static VALUE ruby_guestfs_equal (VALUE gv, VALUE file1v, VALUE file2v) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "equal"); + Check_Type (file1v, T_STRING); const char *file1 = StringValueCStr (file1v); if (!file1) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "file1", "equal"); + Check_Type (file2v, T_STRING); const char *file2 = StringValueCStr (file2v); if (!file2) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3611,6 +3743,7 @@ static VALUE ruby_guestfs_strings (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "strings"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3640,10 +3773,12 @@ static VALUE ruby_guestfs_strings_e (VALUE gv, VALUE encodingv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "strings_e"); + Check_Type (encodingv, T_STRING); const char *encoding = StringValueCStr (encodingv); if (!encoding) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", "encoding", "strings_e"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3673,6 +3808,7 @@ static VALUE ruby_guestfs_hexdump (VALUE gv, VALUE pathv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "hexdump"); + Check_Type (pathv, T_STRING); const char *path = StringValueCStr (pathv); if (!path) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3696,6 +3832,7 @@ static VALUE ruby_guestfs_zerofree (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "zerofree"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3717,6 +3854,7 @@ static VALUE ruby_guestfs_pvresize (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "pvresize"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3738,6 +3876,7 @@ static VALUE ruby_guestfs_sfdisk_N (VALUE gv, VALUE devicev, VALUE nv, VALUE cyl if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "sfdisk_N"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3746,6 +3885,7 @@ static VALUE ruby_guestfs_sfdisk_N (VALUE gv, VALUE devicev, VALUE nv, VALUE cyl int cyls = NUM2INT (cylsv); int heads = NUM2INT (headsv); int sectors = NUM2INT (sectorsv); + Check_Type (linev, T_STRING); const char *line = StringValueCStr (linev); if (!line) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3767,6 +3907,7 @@ static VALUE ruby_guestfs_sfdisk_l (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "sfdisk_l"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3790,6 +3931,7 @@ static VALUE ruby_guestfs_sfdisk_kernel_geometry (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "sfdisk_kernel_geometry"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3813,6 +3955,7 @@ static VALUE ruby_guestfs_sfdisk_disk_geometry (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "sfdisk_disk_geometry"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3856,6 +3999,7 @@ static VALUE ruby_guestfs_vg_activate (VALUE gv, VALUE activatev, VALUE volgroup int activate = RTEST (activatev); char **volgroups; + Check_Type (volgroupsv, T_ARRAY); { int i, len; len = RARRAY_LEN (volgroupsv); @@ -3884,6 +4028,7 @@ static VALUE ruby_guestfs_lvresize (VALUE gv, VALUE devicev, VALUE mbytesv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "lvresize"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3906,6 +4051,7 @@ static VALUE ruby_guestfs_resize2fs (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "resize2fs"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3927,6 +4073,7 @@ static VALUE ruby_guestfs_find (VALUE gv, VALUE directoryv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "find"); + Check_Type (directoryv, T_STRING); const char *directory = StringValueCStr (directoryv); if (!directory) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3956,6 +4103,7 @@ static VALUE ruby_guestfs_e2fsck_f (VALUE gv, VALUE devicev) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "e2fsck_f"); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -3996,6 +4144,7 @@ static VALUE ruby_guestfs_ntfs_3g_probe (VALUE gv, VALUE rwv, VALUE devicev) rb_raise (rb_eArgError, "%s: used handle after closing it", "ntfs_3g_probe"); int rw = RTEST (rwv); + Check_Type (devicev, T_STRING); const char *device = StringValueCStr (devicev); if (!device) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -4017,6 +4166,7 @@ static VALUE ruby_guestfs_sh (VALUE gv, VALUE commandv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "sh"); + Check_Type (commandv, T_STRING); const char *command = StringValueCStr (commandv); if (!command) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -4040,6 +4190,7 @@ static VALUE ruby_guestfs_sh_lines (VALUE gv, VALUE commandv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "sh_lines"); + Check_Type (commandv, T_STRING); const char *command = StringValueCStr (commandv); if (!command) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", @@ -4069,6 +4220,7 @@ static VALUE ruby_guestfs_glob_expand (VALUE gv, VALUE patternv) if (!g) rb_raise (rb_eArgError, "%s: used handle after closing it", "glob_expand"); + Check_Type (patternv, T_STRING); const char *pattern = StringValueCStr (patternv); if (!pattern) rb_raise (rb_eTypeError, "expected string for parameter %s of %s", diff --git a/ruby/tests/tc_rhbz507346.rb b/ruby/tests/tc_rhbz507346.rb new file mode 100644 index 00000000..3bd4bb60 --- /dev/null +++ b/ruby/tests/tc_rhbz507346.rb @@ -0,0 +1,42 @@ +# libguestfs Ruby bindings -*- ruby -*- +# Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +require 'test/unit' +$:.unshift(File::join(File::dirname(__FILE__), "..", "lib")) +$:.unshift(File::join(File::dirname(__FILE__), "..", "ext", "guestfs")) +require 'guestfs' + +class TestLoad < Test::Unit::TestCase + def test_rhbz507346 + g = Guestfs::create() + + File.open("test.img", "w") { + |f| f.seek(10*1024*1024); f.write("\0") + } + + g.add_drive("test.img") + g.launch() + g.wait_ready() + + exception = assert_raise TypeError do + g.command(1) + end + assert_match /wrong argument type Fixnum \(expected Array\)/, exception.message + + File.unlink("test.img") + end +end diff --git a/src/generator.ml b/src/generator.ml index f4b56050..341924f1 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -6500,6 +6500,7 @@ static VALUE ruby_guestfs_close (VALUE gv) List.iter ( function | String n | FileIn n | FileOut n -> + pr " Check_Type (%sv, T_STRING);\n" n; pr " const char *%s = StringValueCStr (%sv);\n" n n; pr " if (!%s)\n" n; pr " rb_raise (rb_eTypeError, \"expected string for parameter %%s of %%s\",\n"; @@ -6508,6 +6509,7 @@ static VALUE ruby_guestfs_close (VALUE gv) pr " const char *%s = !NIL_P (%sv) ? StringValueCStr (%sv) : NULL;\n" n n n | StringList n -> pr " char **%s;\n" n; + pr " Check_Type (%sv, T_ARRAY);\n" n; pr " {\n"; pr " int i, len;\n"; pr " len = RARRAY_LEN (%sv);\n" n; -- cgit From 6f63b699aa03271468f4af9f90933e2d8dbd3db0 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 16:43:42 +0100 Subject: Issue MAKEDEV commands in a loop (RHBZ#507374). --- appliance/init | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/appliance/init b/appliance/init index 41ce9166..96d785c5 100755 --- a/appliance/init +++ b/appliance/init @@ -3,8 +3,12 @@ echo Starting /init script ... PATH=/sbin:/usr/sbin:$PATH mount -t tmpfs none /dev mkdir /dev/pts /dev/shm /dev/mapper -MAKEDEV mem null port zero core full ram tty console fd \ - hda hdb hdc hdd sda sdb sdc sdd loop sd +# Must do each MAKEDEV individually, because if one device fails, +# MAKEDEV will quit without creating the rest (RHBZ#507374). +for dev in mem null port zero core full ram tty console fd \ + hda hdb hdc hdd sda sdb sdc sdd loop sd; do + MAKEDEV $dev ||: +done mknod /dev/ptmx c 5 2; chmod 0666 /dev/ptmx mknod /dev/random c 1 8; chmod 0666 /dev/random mknod /dev/urandom c 1 9; chmod 0444 /dev/urandom -- cgit From ead9aa23e2192471d2030f38ece828adfd50de3e Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 16:53:24 +0100 Subject: Add whitespace to the init script (no functional change). --- appliance/init | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/appliance/init b/appliance/init index 96d785c5..770e5585 100755 --- a/appliance/init +++ b/appliance/init @@ -1,6 +1,9 @@ #!/bin/sh + echo Starting /init script ... + PATH=/sbin:/usr/sbin:$PATH + mount -t tmpfs none /dev mkdir /dev/pts /dev/shm /dev/mapper # Must do each MAKEDEV individually, because if one device fails, @@ -9,23 +12,29 @@ for dev in mem null port zero core full ram tty console fd \ hda hdb hdc hdd sda sdb sdc sdd loop sd; do MAKEDEV $dev ||: done -mknod /dev/ptmx c 5 2; chmod 0666 /dev/ptmx +mknod /dev/ptmx c 5 2; chmod 0666 /dev/ptmx mknod /dev/random c 1 8; chmod 0666 /dev/random mknod /dev/urandom c 1 9; chmod 0444 /dev/urandom + mount -t proc /proc /proc mount -t sysfs /sys /sys mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts + ln -sf /proc/self/fd/0 /dev/stdin ln -sf /proc/self/fd/1 /dev/stdout ln -sf /proc/self/fd/2 /dev/stderr + modprobe virtio_pci modprobe virtio_net modprobe dm_mod ||: + /sbin/ifconfig lo 127.0.0.1 /sbin/ifconfig eth0 10.0.2.10 /sbin/route add default gw 10.0.2.2 + lvm vgscan --ignorelockingfailure lvm vgchange -ay --ignorelockingfailure + if grep -sq guestfs_rescue=1 /proc/cmdline; then bash -i fi -- cgit From 0b00aa44ea85ccce17a3579d14e9d4b0f0dd9185 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 22 Jun 2009 17:02:15 +0100 Subject: Version 1.0.51 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index eeaa988e..ec64cd3f 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.50]) +AC_INIT([libguestfs],[1.0.51]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) -- cgit From 1d07f1f88a08fc84d91db0931fcf34d1f9db36d3 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 23 Jun 2009 08:27:52 +0100 Subject: '-no-kqemu' option is no longer necessary to avoid a warning. --- src/guestfs.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/guestfs.c b/src/guestfs.c index 0b562537..f7f64def 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -930,9 +930,6 @@ guestfs_launch (guestfs_h *g) add_cmdline (g, "-m"); add_cmdline (g, memsize_str); -#if 0 - add_cmdline (g, "-no-kqemu"); /* Avoids a warning. */ -#endif add_cmdline (g, "-no-reboot"); /* Force exit instead of reboot on panic */ add_cmdline (g, "-kernel"); add_cmdline (g, (char *) kernel); -- cgit From 675411d34f807420d1b714436fc5a58fc0022dae Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 23 Jun 2009 10:07:46 +0100 Subject: Command line argument handling. --- TODO | 15 -------- guestfs.pod | 3 ++ src/generator.ml | 2 +- src/guestfs.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 110 insertions(+), 19 deletions(-) diff --git a/TODO b/TODO index ad9e6e9b..1d7cb364 100644 --- a/TODO +++ b/TODO @@ -54,21 +54,6 @@ ie. CentOS 5.3, Fedora 11, Debian. ---------------------------------------------------------------------- -Qemu options -- After discussion with the KVM developers, they have -recommended some flags which will improve the safety and reliability -of KVM. Need to test that these also work under qemu (or at least, do -no harm): - --no-hpet HPET support is broken and should be disabled. - --rtc-td-hack Keeps the rtc clock source track time correctly. - --drive file=...,if=[ide|virtio],cache=off - cache=off is necessary to improve reliability in the - event of a system crash when writing. - ----------------------------------------------------------------------- - "Standalone/local mode" Instead of running guestfsd (the daemon) inside qemu, there should be diff --git a/guestfs.pod b/guestfs.pod index f26880cf..10c6ad8c 100644 --- a/guestfs.pod +++ b/guestfs.pod @@ -759,6 +759,9 @@ For example: LIBGUESTFS_QEMU=/tmp/qemu.wrapper guestfish +Note that libguestfs also calls qemu with the -help and -version +options in order to determine features. + =head1 ENVIRONMENT VARIABLES =over 4 diff --git a/src/generator.ml b/src/generator.ml index 341924f1..f8e3934f 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -373,7 +373,7 @@ for whatever operations you want to perform (ie. read access if you just want to read the image or write access if you want to modify the image). -This is equivalent to the qemu parameter C<-drive file=filename>. +This is equivalent to the qemu parameter C<-drive file=filename,cache=off>. Note that this call checks for the existence of C. This stops you from specifying other types of drive which are supported diff --git a/src/guestfs.c b/src/guestfs.c index f7f64def..02b3ceb3 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -162,6 +162,8 @@ struct guestfs_h char *tmpdir; /* Temporary directory containing socket. */ + char *qemu_help, *qemu_version; /* Output of qemu -help, qemu -version. */ + char **cmdline; /* Qemu command line. */ int cmdline_size; @@ -346,6 +348,8 @@ guestfs_close (guestfs_h *g) free (g->path); free (g->qemu); free (g->append); + free (g->qemu_help); + free (g->qemu_version); free (g); } @@ -638,7 +642,6 @@ guestfs_config (guestfs_h *g, strcmp (qemu_param, "-initrd") == 0 || strcmp (qemu_param, "-nographic") == 0 || strcmp (qemu_param, "-serial") == 0 || - strcmp (qemu_param, "-vnc") == 0 || strcmp (qemu_param, "-full-screen") == 0 || strcmp (qemu_param, "-std-vga") == 0 || strcmp (qemu_param, "-vnc") == 0) { @@ -671,7 +674,8 @@ guestfs_add_drive (guestfs_h *g, const char *filename) return -1; } - snprintf (buf, len, "file=%s", filename); + /* cache=off improves reliability in the event of a host crash. */ + snprintf (buf, len, "file=%s,cache=off", filename); return guestfs_config (g, "-drive", buf); } @@ -745,6 +749,8 @@ dir_contains_files (const char *dir, ...) } static int build_supermin_appliance (guestfs_h *g, const char *path, char **kernel, char **initrd); +static int test_qemu (guestfs_h *g); +static int qemu_supports (guestfs_h *g, const char *option); static const char *kernel_name = "vmlinuz." REPO "." host_cpu; static const char *initrd_name = "initramfs." REPO "." host_cpu ".img"; @@ -879,12 +885,16 @@ guestfs_launch (guestfs_h *g) /* Choose a suitable memory size. Previously we tried to choose * a minimal memory size, but this isn't really necessary since * recent QEMU and KVM don't do anything nasty like locking - * memory into core any more. This we can safely choose a + * memory into core any more. Thus we can safely choose a * large, generous amount of memory, and it'll just get swapped * on smaller systems. */ memsize = 384; + /* Get qemu help text and version. */ + if (test_qemu (g) == -1) + goto cleanup0; + /* Make the vmchannel socket. */ snprintf (unixsock, sizeof unixsock, "%s/sock", g->tmpdir); unlink (unixsock); @@ -946,6 +956,15 @@ guestfs_launch (guestfs_h *g) add_cmdline (g, "user,vlan=0"); add_cmdline (g, "-net"); add_cmdline (g, "nic,model=virtio,vlan=0"); + + /* These options recommended by KVM developers to improve reliability. */ + if (qemu_supports (g, "-no-hpet")) + add_cmdline (g, "-no-hpet"); + + if (qemu_supports (g, "-rtc-td-hack")) + add_cmdline (g, "-rtc-td-hack"); + + /* Finish off the command line. */ incr_cmdline_size (g); g->cmdline[g->cmdline_size-1] = NULL; @@ -1171,6 +1190,90 @@ build_supermin_appliance (guestfs_h *g, const char *path, return 0; } +static int read_all (guestfs_h *g, FILE *fp, char **ret); + +/* Test qemu binary (or wrapper) runs, and do 'qemu -help' and + * 'qemu -version' so we know what options this qemu supports and + * the version. + */ +static int +test_qemu (guestfs_h *g) +{ + char cmd[1024]; + FILE *fp; + + free (g->qemu_help); + free (g->qemu_version); + g->qemu_help = NULL; + g->qemu_version = NULL; + + snprintf (cmd, sizeof cmd, "'%s' -help", g->qemu); + + fp = popen (cmd, "r"); + /* qemu -help should always work (qemu -version OTOH wasn't + * supported by qemu 0.9). If this command doesn't work then it + * probably indicates that the qemu binary is missing. + */ + if (!fp) { + /* XXX This error is never printed, even if the qemu binary + * doesn't exist. Why? + */ + error: + perrorf (g, _("%s: command failed: If qemu is located on a non-standard path, try setting the LIBGUESTFS_QEMU environment variable."), cmd); + return -1; + } + + if (read_all (g, fp, &g->qemu_help) == -1) + goto error; + + if (pclose (fp) == -1) + goto error; + + snprintf (cmd, sizeof cmd, "'%s' -version 2>/dev/null", g->qemu); + + fp = popen (cmd, "r"); + if (fp) { + /* Intentionally ignore errors. */ + read_all (g, fp, &g->qemu_version); + pclose (fp); + } + + return 0; +} + +static int +read_all (guestfs_h *g, FILE *fp, char **ret) +{ + int r, n = 0; + char *p; + + again: + if (feof (fp)) { + *ret = safe_realloc (g, *ret, n + 1); + (*ret)[n] = '\0'; + return n; + } + + *ret = safe_realloc (g, *ret, n + BUFSIZ); + p = &(*ret)[n]; + r = fread (p, 1, BUFSIZ, fp); + if (ferror (fp)) { + perrorf (g, "read"); + return -1; + } + n += r; + goto again; +} + +/* Test if option is supported by qemu command line (just by grepping + * the help text). + */ +static int +qemu_supports (guestfs_h *g, const char *option) +{ + return g->qemu_help && strstr (g->qemu_help, option) != NULL; +} + static void finish_wait_ready (guestfs_h *g, void *vp) { -- cgit From 2eda36dbadd4687f7488a86eea9313bbe30cde82 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 23 Jun 2009 10:17:59 +0100 Subject: Updated Polish translation (RHBZ#502533). --- po/pl.po | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/po/pl.po b/po/pl.po index 8852f2cb..e84e1360 100644 --- a/po/pl.po +++ b/po/pl.po @@ -3,11 +3,11 @@ # msgid "" msgstr "" -"Project-Id-Version: libguestfs 1.0.31\n" +"Project-Id-Version: pl\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" "POT-Creation-Date: 2009-06-22 10:02+0100\n" -"PO-Revision-Date: 2009-05-26 00:50+0200\n" +"PO-Revision-Date: 2009-06-22 21:16+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" @@ -35,7 +35,7 @@ msgid "use '%s filename' to edit a file\n" msgstr "użyj \"%s nazwapliku\", aby zmodyfikować plik\n" #: fish/fish.c:88 -#, fuzzy, c-format +#, c-format msgid "" "guestfish: guest filesystem shell\n" "guestfish lets you edit virtual machine filesystems\n" @@ -75,8 +75,10 @@ msgstr "" " EOF\n" "Opcje:\n" " -h|--cmd-help Wyświetla listę dostępnych poleceń\n" -" -h|--cmd-help polecenie Wyświetla szczegółową pomoc o \"poleceniu\"\n" +" -h|--cmd-help polecenie Wyświetla szczegółową pomoc o \"poleceniu\"\n" " -a|--add image Dodaje obraz\n" +" -D|--no-dest-paths Nie uzupełnia ścieżek z systemu plików gościa za\n" +" pomocą Tab\n" " -m|--mount dev[:mnt] Montuje dev w mnt (jeśli pominięto, w /)\n" " -n|--no-sync Nie synchronizuje automatycznie\n" " -r|--ro Montuje w trybie tylko do odczytu\n" @@ -165,11 +167,11 @@ msgstr "modyfikuje plik w obrazie" #: fish/fish.c:625 msgid "local change directory" -msgstr "" +msgstr "zmienia lokalny folder" #: fish/fish.c:627 msgid "expand wildcards in command" -msgstr "" +msgstr "rozwija wieloznaczniki w poleceniach" #: fish/fish.c:639 #, c-format @@ -262,6 +264,12 @@ msgid "" " useful if you want to download files to a particular\n" " place.\n" msgstr "" +"lcd - zmienia lokalny folder\n" +" lcd \n" +"\n" +" Zmienia bieżący folder guestfish. Te polecenie jest\n" +" przydatne, jeśli chcesz pobrać pliki do konkretnego\n" +" miejsca.\n" #: fish/fish.c:683 #, c-format @@ -273,6 +281,13 @@ msgid "" " command args. Note that the command is run repeatedly\n" " once for each expanded argument.\n" msgstr "" +"glob - rozwija wieloznaczniki w poleceniach\n" +" glob [ ...]\n" +"\n" +" Glob wykonuje z wieloznacznikami\n" +" rozwiniętymi we wszystkie parametry polecenia.\n" +" Zauważ, że polecenie jest wykonywane raz dla\n" +" każdego rozwiniętego parametru.\n" #: fish/fish.c:690 #, c-format @@ -357,7 +372,7 @@ msgstr "nie można obserwować standardowego wyjścia QEMU" #: src/guestfs.c:1167 #, c-format msgid "external command failed: %s" -msgstr "" +msgstr "zewnętrzne polecenie nie powiodło się: %s" #: src/guestfs.c:1195 msgid "qemu has finished launching already" -- cgit From 69a481362c20b0ac3985d3003a0078a349ace0a2 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 23 Jun 2009 15:52:45 +0100 Subject: Bump up default guest size to 500M. --- src/guestfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guestfs.c b/src/guestfs.c index 02b3ceb3..fdf5cd36 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -889,7 +889,7 @@ guestfs_launch (guestfs_h *g) * large, generous amount of memory, and it'll just get swapped * on smaller systems. */ - memsize = 384; + memsize = 500; /* Get qemu help text and version. */ if (test_qemu (g) == -1) -- cgit From da7cf3670fe60301beeb175ff6c284b737d5b7f4 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 23 Jun 2009 15:53:21 +0100 Subject: Added 'scrub-*' commands for securely scrubbing filesystems. --- appliance/packagelist.in | 1 + daemon/Makefile.am | 1 + daemon/scrub.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ src/generator.ml | 42 ++++++++++++++++- 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 daemon/scrub.c diff --git a/appliance/packagelist.in b/appliance/packagelist.in index 68270f07..1d1f63a0 100644 --- a/appliance/packagelist.in +++ b/appliance/packagelist.in @@ -24,6 +24,7 @@ net-tools ntfs-3g ntfsprogs procps +scrub strace util-linux-ng zerofree diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 7ef2a6b8..c84a563b 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -46,6 +46,7 @@ guestfsd_SOURCES = \ ntfs.c \ pingdaemon.c \ proto.c \ + scrub.c \ sfdisk.c \ sleep.c \ stat.c \ diff --git a/daemon/scrub.c b/daemon/scrub.c new file mode 100644 index 00000000..9b6d49df --- /dev/null +++ b/daemon/scrub.c @@ -0,0 +1,114 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2009 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include +#include +#include + +#include "daemon.h" +#include "actions.h" + +int +do_scrub_device (char *device) +{ + char *err; + int r; + + IS_DEVICE (device, -1); + + r = command (NULL, &err, "scrub", device, NULL); + if (r == -1) { + reply_with_error ("scrub_device: %s: %s", device, err); + free (err); + return -1; + } + + free (err); + + return 0; +} + +int +do_scrub_file (char *file) +{ + char *buf; + int len; + char *err; + int r; + + NEED_ROOT (-1); + ABS_PATH (file, -1); + + /* Make the path relative to /sysroot. */ + len = strlen (file) + 9; + buf = malloc (len); + if (!buf) { + reply_with_perror ("malloc"); + return -1; + } + snprintf (buf, len, "/sysroot%s", file); + + r = command (NULL, &err, "scrub", "-r", buf, NULL); + free (buf); + if (r == -1) { + reply_with_error ("scrub_file: %s: %s", file, err); + free (err); + return -1; + } + + free (err); + + return 0; +} + +int +do_scrub_freespace (char *dir) +{ + char *buf; + int len; + char *err; + int r; + + NEED_ROOT (-1); + ABS_PATH (dir, -1); + + /* Make the path relative to /sysroot. */ + len = strlen (dir) + 9; + buf = malloc (len); + if (!buf) { + reply_with_perror ("malloc"); + return -1; + } + snprintf (buf, len, "/sysroot%s", dir); + + r = command (NULL, &err, "scrub", "-X", buf, NULL); + free (buf); + if (r == -1) { + reply_with_error ("scrub_freespace: %s: %s", dir, err); + free (err); + return -1; + } + + free (err); + + return 0; +} diff --git a/src/generator.ml b/src/generator.ml index f8e3934f..f1a9a45f 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -1982,7 +1982,9 @@ This command writes zeroes over the first few blocks of C. How many blocks are zeroed isn't specified (but it's I enough to securely wipe the device). It should be sufficient to remove -any partition tables, filesystem superblocks and so on."); +any partition tables, filesystem superblocks and so on. + +See also: C."); ("grub_install", (RErr, [String "root"; String "device"]), 86, [], [InitBasicFS, Always, TestOutputTrue ( @@ -2402,6 +2404,44 @@ It is just a wrapper around the C L function with flags C. See that manual page for more details."); + ("scrub_device", (RErr, [String "device"]), 114, [DangerWillRobinson], + [InitNone, Always, TestRun ( (* use /dev/sdc because it's smaller *) + [["scrub_device"; "/dev/sdc"]])], + "scrub (securely wipe) a device", + "\ +This command writes patterns over C to make data retrieval +more difficult. + +It is an interface to the L program. See that +manual page for more details."); + + ("scrub_file", (RErr, [String "file"]), 115, [], + [InitBasicFS, Always, TestRun ( + [["write_file"; "/file"; "content"; "0"]; + ["scrub_file"; "/file"]])], + "scrub (securely wipe) a file", + "\ +This command writes patterns over a file to make data retrieval +more difficult. + +The file is I after scrubbing. + +It is an interface to the L program. See that +manual page for more details."); + + ("scrub_freespace", (RErr, [String "dir"]), 116, [], + [], (* XXX needs testing *) + "scrub (securely wipe) free space", + "\ +This command creates the directory C

and then fills it +with files until the filesystem is full, and scrubs the files +as for C, and deletes them. +The intention is to scrub any free space on the partition +containing C. + +It is an interface to the L program. See that +manual page for more details."); + ] let all_functions = non_daemon_functions @ daemon_functions -- cgit From bcb3fc0c3336c05e9ecbbfb25c7c31b42bd3e32e Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 23 Jun 2009 15:53:44 +0100 Subject: Generated code for 'scrub-*' commands. --- capitests/tests.c | 161 +++++++++++++++++- daemon/actions.h | 3 + daemon/stubs.c | 81 +++++++++ fish/cmds.c | 67 +++++++- fish/completion.c | 3 + guestfish-actions.pod | 42 ++++- guestfs-actions.pod | 51 +++++- haskell/Guestfs.hs | 41 ++++- java/com/redhat/et/libguestfs/GuestFS.java | 75 ++++++++- java/com_redhat_et_libguestfs_GuestFS.c | 51 ++++++ ocaml/guestfs.ml | 3 + ocaml/guestfs.mli | 9 + ocaml/guestfs_c_actions.c | 69 ++++++++ perl/Guestfs.xs | 33 ++++ perl/lib/Sys/Guestfs.pm | 36 +++- python/guestfs-py.c | 78 +++++++++ python/guestfs.py | 39 ++++- ruby/ext/guestfs/_guestfs.c | 72 ++++++++ src/guestfs-actions.c | 261 +++++++++++++++++++++++++++++ src/guestfs-actions.h | 3 + src/guestfs_protocol.c | 30 ++++ src/guestfs_protocol.h | 26 ++- src/guestfs_protocol.x | 15 ++ 23 files changed, 1239 insertions(+), 10 deletions(-) diff --git a/capitests/tests.c b/capitests/tests.c index 309bac3d..62d4805f 100644 --- a/capitests/tests.c +++ b/capitests/tests.c @@ -150,6 +150,153 @@ static void no_test_warnings (void) fprintf (stderr, "warning: \"guestfs_e2fsck_f\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_sh\" has no tests\n"); fprintf (stderr, "warning: \"guestfs_sh_lines\" has no tests\n"); + fprintf (stderr, "warning: \"guestfs_scrub_freespace\" has no tests\n"); +} + +static int test_scrub_file_0_skip (void) +{ + const char *str; + + str = getenv ("SKIP_TEST_SCRUB_FILE_0"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_SCRUB_FILE"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_scrub_file_0 (void) +{ + if (test_scrub_file_0_skip ()) { + printf ("%s skipped (reason: SKIP_TEST_* variable set)\n", "test_scrub_file_0"); + return 0; + } + + /* InitBasicFS for test_scrub_file_0: create ext2 on /dev/sda1 */ + { + char device[] = "/dev/sda"; + int r; + suppress_error = 0; + r = guestfs_blockdev_setrw (g, device); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_umount_all (g); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_lvm_remove_all (g); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda"; + char lines_0[] = ","; + char *lines[] = { + lines_0, + NULL + }; + int r; + suppress_error = 0; + r = guestfs_sfdisk (g, device, 0, 0, 0, lines); + if (r == -1) + return -1; + } + { + char fstype[] = "ext2"; + char device[] = "/dev/sda1"; + int r; + suppress_error = 0; + r = guestfs_mkfs (g, fstype, device); + if (r == -1) + return -1; + } + { + char device[] = "/dev/sda1"; + char mountpoint[] = "/"; + int r; + suppress_error = 0; + r = guestfs_mount (g, device, mountpoint); + if (r == -1) + return -1; + } + /* TestRun for scrub_file (0) */ + { + char path[] = "/file"; + char content[] = "content"; + int r; + suppress_error = 0; + r = guestfs_write_file (g, path, content, 0); + if (r == -1) + return -1; + } + { + char file[] = "/file"; + int r; + suppress_error = 0; + r = guestfs_scrub_file (g, file); + if (r == -1) + return -1; + } + return 0; +} + +static int test_scrub_device_0_skip (void) +{ + const char *str; + + str = getenv ("SKIP_TEST_SCRUB_DEVICE_0"); + if (str && strcmp (str, "1") == 0) return 1; + str = getenv ("SKIP_TEST_SCRUB_DEVICE"); + if (str && strcmp (str, "1") == 0) return 1; + return 0; +} + +static int test_scrub_device_0 (void) +{ + if (test_scrub_device_0_skip ()) { + printf ("%s skipped (reason: SKIP_TEST_* variable set)\n", "test_scrub_device_0"); + return 0; + } + + /* InitNone|InitEmpty for test_scrub_device_0 */ + { + char device[] = "/dev/sda"; + int r; + suppress_error = 0; + r = guestfs_blockdev_setrw (g, device); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_umount_all (g); + if (r == -1) + return -1; + } + { + int r; + suppress_error = 0; + r = guestfs_lvm_remove_all (g); + if (r == -1) + return -1; + } + /* TestRun for scrub_device (0) */ + { + char device[] = "/dev/sdc"; + int r; + suppress_error = 0; + r = guestfs_scrub_device (g, device); + if (r == -1) + return -1; + } + return 0; } static int test_glob_expand_0_skip (void) @@ -16166,8 +16313,20 @@ int main (int argc, char *argv[]) /* Cancel previous alarm. */ alarm (0); - nr_tests = 149; + nr_tests = 151; + test_num++; + printf ("%3d/%3d test_scrub_file_0\n", test_num, nr_tests); + if (test_scrub_file_0 () == -1) { + printf ("test_scrub_file_0 FAILED\n"); + failed++; + } + test_num++; + printf ("%3d/%3d test_scrub_device_0\n", test_num, nr_tests); + if (test_scrub_device_0 () == -1) { + printf ("test_scrub_device_0 FAILED\n"); + failed++; + } test_num++; printf ("%3d/%3d test_glob_expand_0\n", test_num, nr_tests); if (test_glob_expand_0 () == -1) { diff --git a/daemon/actions.h b/daemon/actions.h index cc8bc787..17ec338c 100644 --- a/daemon/actions.h +++ b/daemon/actions.h @@ -134,3 +134,6 @@ extern int do_ntfs_3g_probe (int rw, char *device); extern char *do_sh (char *command); extern char **do_sh_lines (char *command); extern char **do_glob_expand (char *pattern); +extern int do_scrub_device (char *device); +extern int do_scrub_file (char *file); +extern int do_scrub_freespace (char *dir); diff --git a/daemon/stubs.c b/daemon/stubs.c index af87a994..be97a863 100644 --- a/daemon/stubs.c +++ b/daemon/stubs.c @@ -2854,6 +2854,78 @@ done: xdr_free ((xdrproc_t) xdr_guestfs_glob_expand_args, (char *) &args); } +static void scrub_device_stub (XDR *xdr_in) +{ + int r; + struct guestfs_scrub_device_args args; + char *device; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_scrub_device_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "scrub_device"); + return; + } + device = args.device; + + r = do_scrub_device (device); + if (r == -1) + /* do_scrub_device has already called reply_with_error */ + goto done; + + reply (NULL, NULL); +done: + xdr_free ((xdrproc_t) xdr_guestfs_scrub_device_args, (char *) &args); +} + +static void scrub_file_stub (XDR *xdr_in) +{ + int r; + struct guestfs_scrub_file_args args; + char *file; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_scrub_file_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "scrub_file"); + return; + } + file = args.file; + + r = do_scrub_file (file); + if (r == -1) + /* do_scrub_file has already called reply_with_error */ + goto done; + + reply (NULL, NULL); +done: + xdr_free ((xdrproc_t) xdr_guestfs_scrub_file_args, (char *) &args); +} + +static void scrub_freespace_stub (XDR *xdr_in) +{ + int r; + struct guestfs_scrub_freespace_args args; + char *dir; + + memset (&args, 0, sizeof args); + + if (!xdr_guestfs_scrub_freespace_args (xdr_in, &args)) { + reply_with_error ("%s: daemon failed to decode procedure arguments", "scrub_freespace"); + return; + } + dir = args.dir; + + r = do_scrub_freespace (dir); + if (r == -1) + /* do_scrub_freespace has already called reply_with_error */ + goto done; + + reply (NULL, NULL); +done: + xdr_free ((xdrproc_t) xdr_guestfs_scrub_freespace_args, (char *) &args); +} + void dispatch_incoming_message (XDR *xdr_in) { switch (proc_nr) { @@ -3196,6 +3268,15 @@ void dispatch_incoming_message (XDR *xdr_in) case GUESTFS_PROC_GLOB_EXPAND: glob_expand_stub (xdr_in); break; + case GUESTFS_PROC_SCRUB_DEVICE: + scrub_device_stub (xdr_in); + break; + case GUESTFS_PROC_SCRUB_FILE: + scrub_file_stub (xdr_in); + break; + case GUESTFS_PROC_SCRUB_FREESPACE: + scrub_freespace_stub (xdr_in); + break; default: reply_with_error ("dispatch_incoming_message: unknown procedure number %d, set LIBGUESTFS_PATH to point to the matching libguestfs appliance directory", proc_nr); } diff --git a/fish/cmds.c b/fish/cmds.c index 8c35eea2..b94b322a 100644 --- a/fish/cmds.c +++ b/fish/cmds.c @@ -127,6 +127,9 @@ void list_commands (void) printf ("%-20s %s\n", "rm", "remove a file"); printf ("%-20s %s\n", "rm-rf", "remove a file or directory recursively"); printf ("%-20s %s\n", "rmdir", "remove a directory"); + printf ("%-20s %s\n", "scrub-device", "scrub (securely wipe) a device"); + printf ("%-20s %s\n", "scrub-file", "scrub (securely wipe) a file"); + printf ("%-20s %s\n", "scrub-freespace", "scrub (securely wipe) free space"); printf ("%-20s %s\n", "set-append", "add options to kernel command line"); printf ("%-20s %s\n", "set-autosync", "set autosync mode"); printf ("%-20s %s\n", "set-e2label", "set the ext2/3/4 filesystem label"); @@ -177,7 +180,7 @@ void display_command (const char *cmd) pod2text ("kill-subprocess - kill the qemu subprocess", " kill-subprocess\n\nThis kills the qemu subprocess. You should never need to call this."); else if (strcasecmp (cmd, "add_drive") == 0 || strcasecmp (cmd, "add-drive") == 0 || strcasecmp (cmd, "add") == 0) - pod2text ("add-drive - add an image to examine or modify", " add-drive \n\nThis function adds a virtual machine disk image C to the\nguest. The first time you call this function, the disk appears as IDE\ndisk 0 (C) in the guest, the second time as C, and\nso on.\n\nYou don't necessarily need to be root when using libguestfs. However\nyou obviously do need sufficient permissions to access the filename\nfor whatever operations you want to perform (ie. read access if you\njust want to read the image or write access if you want to modify the\nimage).\n\nThis is equivalent to the qemu parameter C<-drive file=filename>.\n\nNote that this call checks for the existence of C. This\nstops you from specifying other types of drive which are supported\nby qemu such as C and C URLs. To specify those, use\nthe general C call instead.\n\nYou can use 'add' as an alias for this command."); + pod2text ("add-drive - add an image to examine or modify", " add-drive \n\nThis function adds a virtual machine disk image C to the\nguest. The first time you call this function, the disk appears as IDE\ndisk 0 (C) in the guest, the second time as C, and\nso on.\n\nYou don't necessarily need to be root when using libguestfs. However\nyou obviously do need sufficient permissions to access the filename\nfor whatever operations you want to perform (ie. read access if you\njust want to read the image or write access if you want to modify the\nimage).\n\nThis is equivalent to the qemu parameter C<-drive file=filename,cache=off>.\n\nNote that this call checks for the existence of C. This\nstops you from specifying other types of drive which are supported\nby qemu such as C and C URLs. To specify those, use\nthe general C call instead.\n\nYou can use 'add' as an alias for this command."); else if (strcasecmp (cmd, "add_cdrom") == 0 || strcasecmp (cmd, "add-cdrom") == 0 || strcasecmp (cmd, "cdrom") == 0) pod2text ("add-cdrom - add a CD-ROM disk image to examine", " add-cdrom \n\nThis function adds a virtual CD-ROM disk image to the guest.\n\nThis is equivalent to the qemu parameter C<-cdrom filename>.\n\nNote that this call checks for the existence of C. This\nstops you from specifying other types of drive which are supported\nby qemu such as C and C URLs. To specify those, use\nthe general C call instead.\n\nYou can use 'cdrom' as an alias for this command."); @@ -486,7 +489,7 @@ void display_command (const char *cmd) pod2text ("fsck - run the filesystem checker", " fsck \n\nThis runs the filesystem checker (fsck) on C which\nshould have filesystem type C.\n\nThe returned integer is the status. See L for the\nlist of status codes from C.\n\nNotes:\n\n=over 4\n\n=item *\n\nMultiple status codes can be summed together.\n\n=item *\n\nA non-zero return code can mean \"success\", for example if\nerrors have been corrected on the filesystem.\n\n=item *\n\nChecking or repairing NTFS volumes is not supported\n(by linux-ntfs).\n\n=back\n\nThis command is entirely equivalent to running C."); else if (strcasecmp (cmd, "zero") == 0) - pod2text ("zero - write zeroes to the device", " zero \n\nThis command writes zeroes over the first few blocks of C.\n\nHow many blocks are zeroed isn't specified (but it's I enough\nto securely wipe the device). It should be sufficient to remove\nany partition tables, filesystem superblocks and so on."); + pod2text ("zero - write zeroes to the device", " zero \n\nThis command writes zeroes over the first few blocks of C.\n\nHow many blocks are zeroed isn't specified (but it's I enough\nto securely wipe the device). It should be sufficient to remove\nany partition tables, filesystem superblocks and so on.\n\nSee also: C."); else if (strcasecmp (cmd, "grub_install") == 0 || strcasecmp (cmd, "grub-install") == 0) pod2text ("grub-install - install GRUB", " grub-install \n\nThis command installs GRUB (the Grand Unified Bootloader) on\nC, with the root directory being C."); @@ -571,6 +574,15 @@ void display_command (const char *cmd) else if (strcasecmp (cmd, "glob_expand") == 0 || strcasecmp (cmd, "glob-expand") == 0) pod2text ("glob-expand - expand a wildcard path", " glob-expand \n\nThis command searches for all the pathnames matching\nC according to the wildcard expansion rules\nused by the shell.\n\nIf no paths match, then this returns an empty list\n(note: not an error).\n\nIt is just a wrapper around the C L function\nwith flags C.\nSee that manual page for more details."); + else + if (strcasecmp (cmd, "scrub_device") == 0 || strcasecmp (cmd, "scrub-device") == 0) + pod2text ("scrub-device - scrub (securely wipe) a device", " scrub-device \n\nThis command writes patterns over C to make data retrieval\nmore difficult.\n\nIt is an interface to the L program. See that\nmanual page for more details.\n\nB."); + else + if (strcasecmp (cmd, "scrub_file") == 0 || strcasecmp (cmd, "scrub-file") == 0) + pod2text ("scrub-file - scrub (securely wipe) a file", " scrub-file \n\nThis command writes patterns over a file to make data retrieval\nmore difficult.\n\nThe file is I after scrubbing.\n\nIt is an interface to the L program. See that\nmanual page for more details."); + else + if (strcasecmp (cmd, "scrub_freespace") == 0 || strcasecmp (cmd, "scrub-freespace") == 0) + pod2text ("scrub-freespace - scrub (securely wipe) free space", " scrub-freespace \n\nThis command creates the directory C and then fills it\nwith files until the filesystem is full, and scrubs the files\nas for C, and deletes them.\nThe intention is to scrub any free space on the partition\ncontaining C.\n\nIt is an interface to the L program. See that\nmanual page for more details."); else display_builtin_command (cmd); } @@ -2801,6 +2813,48 @@ static int run_glob_expand (const char *cmd, int argc, char *argv[]) return 0; } +static int run_scrub_device (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *device; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + device = argv[0]; + r = guestfs_scrub_device (g, device); + return r; +} + +static int run_scrub_file (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *file; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + file = argv[0]; + r = guestfs_scrub_file (g, file); + return r; +} + +static int run_scrub_freespace (const char *cmd, int argc, char *argv[]) +{ + int r; + const char *dir; + if (argc != 1) { + fprintf (stderr, "%s should have 1 parameter(s)\n", cmd); + fprintf (stderr, "type 'help %s' for help on %s\n", cmd, cmd); + return -1; + } + dir = argv[0]; + r = guestfs_scrub_freespace (g, dir); + return r; +} + int run_action (const char *cmd, int argc, char *argv[]) { if (strcasecmp (cmd, "launch") == 0 || strcasecmp (cmd, "run") == 0) @@ -3204,6 +3258,15 @@ int run_action (const char *cmd, int argc, char *argv[]) else if (strcasecmp (cmd, "glob_expand") == 0 || strcasecmp (cmd, "glob-expand") == 0) return run_glob_expand (cmd, argc, argv); + else + if (strcasecmp (cmd, "scrub_device") == 0 || strcasecmp (cmd, "scrub-device") == 0) + return run_scrub_device (cmd, argc, argv); + else + if (strcasecmp (cmd, "scrub_file") == 0 || strcasecmp (cmd, "scrub-file") == 0) + return run_scrub_file (cmd, argc, argv); + else + if (strcasecmp (cmd, "scrub_freespace") == 0 || strcasecmp (cmd, "scrub-freespace") == 0) + return run_scrub_freespace (cmd, argc, argv); else { fprintf (stderr, "%s: unknown command\n", cmd); diff --git a/fish/completion.c b/fish/completion.c index e8d8cea5..92843e56 100644 --- a/fish/completion.c +++ b/fish/completion.c @@ -180,6 +180,9 @@ static const char *const commands[] = { "sh", "sh-lines", "glob-expand", + "scrub-device", + "scrub-file", + "scrub-freespace", NULL }; diff --git a/guestfish-actions.pod b/guestfish-actions.pod index 48459901..d4f49301 100644 --- a/guestfish-actions.pod +++ b/guestfish-actions.pod @@ -26,7 +26,7 @@ for whatever operations you want to perform (ie. read access if you just want to read the image or write access if you want to modify the image). -This is equivalent to the qemu parameter C<-drive file=filename>. +This is equivalent to the qemu parameter C<-drive file=filename,cache=off>. Note that this call checks for the existence of C. This stops you from specifying other types of drive which are supported @@ -1092,6 +1092,44 @@ command. Remove the single directory C. +=head2 scrub-device + + scrub-device device + +This command writes patterns over C to make data retrieval +more difficult. + +It is an interface to the L program. See that +manual page for more details. + +B. + +=head2 scrub-file + + scrub-file file + +This command writes patterns over a file to make data retrieval +more difficult. + +The file is I after scrubbing. + +It is an interface to the L program. See that +manual page for more details. + +=head2 scrub-freespace + + scrub-freespace dir + +This command creates the directory C and then fills it +with files until the filesystem is full, and scrubs the files +as for C, and deletes them. +The intention is to scrub any free space on the partition +containing C. + +It is an interface to the L program. See that +manual page for more details. + =head2 set-append | append set-append append @@ -1520,6 +1558,8 @@ How many blocks are zeroed isn't specified (but it's I enough to securely wipe the device). It should be sufficient to remove any partition tables, filesystem superblocks and so on. +See also: C. + =head2 zerofree zerofree device diff --git a/guestfs-actions.pod b/guestfs-actions.pod index d35d1aa2..9afa5de9 100644 --- a/guestfs-actions.pod +++ b/guestfs-actions.pod @@ -30,7 +30,7 @@ for whatever operations you want to perform (ie. read access if you just want to read the image or write access if you want to modify the image). -This is equivalent to the qemu parameter C<-drive file=filename>. +This is equivalent to the qemu parameter C<-drive file=filename,cache=off>. Note that this call checks for the existence of C. This stops you from specifying other types of drive which are supported @@ -1448,6 +1448,53 @@ Remove the single directory C. This function returns 0 on success or -1 on error. +=head2 guestfs_scrub_device + + int guestfs_scrub_device (guestfs_h *handle, + const char *device); + +This command writes patterns over C to make data retrieval +more difficult. + +It is an interface to the L program. See that +manual page for more details. + +This function returns 0 on success or -1 on error. + +B. + +=head2 guestfs_scrub_file + + int guestfs_scrub_file (guestfs_h *handle, + const char *file); + +This command writes patterns over a file to make data retrieval +more difficult. + +The file is I after scrubbing. + +It is an interface to the L program. See that +manual page for more details. + +This function returns 0 on success or -1 on error. + +=head2 guestfs_scrub_freespace + + int guestfs_scrub_freespace (guestfs_h *handle, + const char *dir); + +This command creates the directory C and then fills it +with files until the filesystem is full, and scrubs the files +as for C, and deletes them. +The intention is to scrub any free space on the partition +containing C. + +It is an interface to the L program. See that +manual page for more details. + +This function returns 0 on success or -1 on error. + =head2 guestfs_set_append int guestfs_set_append (guestfs_h *handle, @@ -2051,6 +2098,8 @@ How many blocks are zeroed isn't specified (but it's I enough to securely wipe the device). It should be sufficient to remove any partition tables, filesystem superblocks and so on. +See also: C. + This function returns 0 on success or -1 on error. =head2 guestfs_zerofree diff --git a/haskell/Guestfs.hs b/haskell/Guestfs.hs index 210274ef..ddbad469 100644 --- a/haskell/Guestfs.hs +++ b/haskell/Guestfs.hs @@ -83,7 +83,10 @@ module Guestfs ( zerofree, pvresize, resize2fs, - e2fsck_f + e2fsck_f, + scrub_device, + scrub_file, + scrub_freespace ) where import Foreign import Foreign.C @@ -853,3 +856,39 @@ e2fsck_f h device = do fail err else return () +foreign import ccall unsafe "guestfs_scrub_device" c_scrub_device + :: GuestfsP -> CString -> IO (CInt) + +scrub_device :: GuestfsH -> String -> IO () +scrub_device h device = do + r <- withCString device $ \device -> withForeignPtr h (\p -> c_scrub_device p device) + if (r == -1) + then do + err <- last_error h + fail err + else return () + +foreign import ccall unsafe "guestfs_scrub_file" c_scrub_file + :: GuestfsP -> CString -> IO (CInt) + +scrub_file :: GuestfsH -> String -> IO () +scrub_file h file = do + r <- withCString file $ \file -> withForeignPtr h (\p -> c_scrub_file p file) + if (r == -1) + then do + err <- last_error h + fail err + else return () + +foreign import ccall unsafe "guestfs_scrub_freespace" c_scrub_freespace + :: GuestfsP -> CString -> IO (CInt) + +scrub_freespace :: GuestfsH -> String -> IO () +scrub_freespace h dir = do + r <- withCString dir $ \dir -> withForeignPtr h (\p -> c_scrub_freespace p dir) + if (r == -1) + then do + err <- last_error h + fail err + else return () + diff --git a/java/com/redhat/et/libguestfs/GuestFS.java b/java/com/redhat/et/libguestfs/GuestFS.java index cdc0f094..36689700 100644 --- a/java/com/redhat/et/libguestfs/GuestFS.java +++ b/java/com/redhat/et/libguestfs/GuestFS.java @@ -427,7 +427,7 @@ public HashMap test0rhashtableerr () * to modify the image). *

* This is equivalent to the qemu parameter "-drive - * file=filename". + * file=filename,cache=off". *

* Note that this call checks for the existence of * "filename". This stops you from specifying other types @@ -2801,6 +2801,8 @@ public HashMap test0rhashtableerr () * sufficient to remove any partition tables, filesystem * superblocks and so on. *

+ * See also: "g.scrub_device". + *

* @throws LibGuestFSException */ public void zero (String device) @@ -3461,4 +3463,75 @@ public HashMap test0rhashtableerr () private native String[] _glob_expand (long g, String pattern) throws LibGuestFSException; + /** + * scrub (securely wipe) a device + *

+ * This command writes patterns over "device" to make data + * retrieval more difficult. + *

+ * It is an interface to the scrub(1) program. See that + * manual page for more details. + *

+ * This command is dangerous. Without careful use you can + * easily destroy all your data. + *

+ * @throws LibGuestFSException + */ + public void scrub_device (String device) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("scrub_device: handle is closed"); + _scrub_device (g, device); + } + private native void _scrub_device (long g, String device) + throws LibGuestFSException; + + /** + * scrub (securely wipe) a file + *

+ * This command writes patterns over a file to make data + * retrieval more difficult. + *

+ * The file is *removed* after scrubbing. + *

+ * It is an interface to the scrub(1) program. See that + * manual page for more details. + *

+ * @throws LibGuestFSException + */ + public void scrub_file (String file) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("scrub_file: handle is closed"); + _scrub_file (g, file); + } + private native void _scrub_file (long g, String file) + throws LibGuestFSException; + + /** + * scrub (securely wipe) free space + *

+ * This command creates the directory "dir" and then fills + * it with files until the filesystem is full, and scrubs + * the files as for "g.scrub_file", and deletes them. The + * intention is to scrub any free space on the partition + * containing "dir". + *

+ * It is an interface to the scrub(1) program. See that + * manual page for more details. + *

+ * @throws LibGuestFSException + */ + public void scrub_freespace (String dir) + throws LibGuestFSException + { + if (g == 0) + throw new LibGuestFSException ("scrub_freespace: handle is closed"); + _scrub_freespace (g, dir); + } + private native void _scrub_freespace (long g, String dir) + throws LibGuestFSException; + } diff --git a/java/com_redhat_et_libguestfs_GuestFS.c b/java/com_redhat_et_libguestfs_GuestFS.c index 8a5afe93..37cd4c9a 100644 --- a/java/com_redhat_et_libguestfs_GuestFS.c +++ b/java/com_redhat_et_libguestfs_GuestFS.c @@ -4073,3 +4073,54 @@ Java_com_redhat_et_libguestfs_GuestFS__1glob_1expand return jr; } +JNIEXPORT void JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1scrub_1device + (JNIEnv *env, jobject obj, jlong jg, jstring jdevice) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + int r; + const char *device; + + device = (*env)->GetStringUTFChars (env, jdevice, NULL); + r = guestfs_scrub_device (g, device); + (*env)->ReleaseStringUTFChars (env, jdevice, device); + if (r == -1) { + throw_exception (env, guestfs_last_error (g)); + return ; + } +} + +JNIEXPORT void JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1scrub_1file + (JNIEnv *env, jobject obj, jlong jg, jstring jfile) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + int r; + const char *file; + + file = (*env)->GetStringUTFChars (env, jfile, NULL); + r = guestfs_scrub_file (g, file); + (*env)->ReleaseStringUTFChars (env, jfile, file); + if (r == -1) { + throw_exception (env, guestfs_last_error (g)); + return ; + } +} + +JNIEXPORT void JNICALL +Java_com_redhat_et_libguestfs_GuestFS__1scrub_1freespace + (JNIEnv *env, jobject obj, jlong jg, jstring jdir) +{ + guestfs_h *g = (guestfs_h *) (long) jg; + int r; + const char *dir; + + dir = (*env)->GetStringUTFChars (env, jdir, NULL); + r = guestfs_scrub_freespace (g, dir); + (*env)->ReleaseStringUTFChars (env, jdir, dir); + if (r == -1) { + throw_exception (env, guestfs_last_error (g)); + return ; + } +} + diff --git a/ocaml/guestfs.ml b/ocaml/guestfs.ml index fb83545b..f0c789ba 100644 --- a/ocaml/guestfs.ml +++ b/ocaml/guestfs.ml @@ -280,3 +280,6 @@ external ntfs_3g_probe : t -> bool -> string -> int = "ocaml_guestfs_ntfs_3g_pro external sh : t -> string -> string = "ocaml_guestfs_sh" external sh_lines : t -> string -> string array = "ocaml_guestfs_sh_lines" external glob_expand : t -> string -> string array = "ocaml_guestfs_glob_expand" +external scrub_device : t -> string -> unit = "ocaml_guestfs_scrub_device" +external scrub_file : t -> string -> unit = "ocaml_guestfs_scrub_file" +external scrub_freespace : t -> string -> unit = "ocaml_guestfs_scrub_freespace" diff --git a/ocaml/guestfs.mli b/ocaml/guestfs.mli index 345c7ec6..d93cfdc8 100644 --- a/ocaml/guestfs.mli +++ b/ocaml/guestfs.mli @@ -619,3 +619,12 @@ val sh_lines : t -> string -> string array val glob_expand : t -> string -> string array (** expand a wildcard path *) +val scrub_device : t -> string -> unit +(** scrub (securely wipe) a device *) + +val scrub_file : t -> string -> unit +(** scrub (securely wipe) a file *) + +val scrub_freespace : t -> string -> unit +(** scrub (securely wipe) free space *) + diff --git a/ocaml/guestfs_c_actions.c b/ocaml/guestfs_c_actions.c index 1ce55f0a..a8204e8a 100644 --- a/ocaml/guestfs_c_actions.c +++ b/ocaml/guestfs_c_actions.c @@ -4274,3 +4274,72 @@ ocaml_guestfs_glob_expand (value gv, value patternv) CAMLreturn (rv); } +CAMLprim value +ocaml_guestfs_scrub_device (value gv, value devicev) +{ + CAMLparam2 (gv, devicev); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("scrub_device: used handle after closing it"); + + const char *device = String_val (devicev); + int r; + + caml_enter_blocking_section (); + r = guestfs_scrub_device (g, device); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "scrub_device"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_scrub_file (value gv, value filev) +{ + CAMLparam2 (gv, filev); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("scrub_file: used handle after closing it"); + + const char *file = String_val (filev); + int r; + + caml_enter_blocking_section (); + r = guestfs_scrub_file (g, file); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "scrub_file"); + + rv = Val_unit; + CAMLreturn (rv); +} + +CAMLprim value +ocaml_guestfs_scrub_freespace (value gv, value dirv) +{ + CAMLparam2 (gv, dirv); + CAMLlocal1 (rv); + + guestfs_h *g = Guestfs_val (gv); + if (g == NULL) + caml_failwith ("scrub_freespace: used handle after closing it"); + + const char *dir = String_val (dirv); + int r; + + caml_enter_blocking_section (); + r = guestfs_scrub_freespace (g, dir); + caml_leave_blocking_section (); + if (r == -1) + ocaml_guestfs_raise_error (g, "scrub_freespace"); + + rv = Val_unit; + CAMLreturn (rv); +} + diff --git a/perl/Guestfs.xs b/perl/Guestfs.xs index 5905dc5e..4b059101 100644 --- a/perl/Guestfs.xs +++ b/perl/Guestfs.xs @@ -2602,3 +2602,36 @@ PREINIT: } free (paths); +void +scrub_device (g, device) + guestfs_h *g; + char *device; +PREINIT: + int r; + PPCODE: + r = guestfs_scrub_device (g, device); + if (r == -1) + croak ("scrub_device: %s", guestfs_last_error (g)); + +void +scrub_file (g, file) + guestfs_h *g; + char *file; +PREINIT: + int r; + PPCODE: + r = guestfs_scrub_file (g, file); + if (r == -1) + croak ("scrub_file: %s", guestfs_last_error (g)); + +void +scrub_freespace (g, dir) + guestfs_h *g; + char *dir; +PREINIT: + int r; + PPCODE: + r = guestfs_scrub_freespace (g, dir); + if (r == -1) + croak ("scrub_freespace: %s", guestfs_last_error (g)); + diff --git a/perl/lib/Sys/Guestfs.pm b/perl/lib/Sys/Guestfs.pm index 436f218c..9c5aa183 100644 --- a/perl/lib/Sys/Guestfs.pm +++ b/perl/lib/Sys/Guestfs.pm @@ -115,7 +115,7 @@ for whatever operations you want to perform (ie. read access if you just want to read the image or write access if you want to modify the image). -This is equivalent to the qemu parameter C<-drive file=filename>. +This is equivalent to the qemu parameter C<-drive file=filename,cache=off>. Note that this call checks for the existence of C. This stops you from specifying other types of drive which are supported @@ -999,6 +999,38 @@ command. Remove the single directory C. +=item $h->scrub_device ($device); + +This command writes patterns over C to make data retrieval +more difficult. + +It is an interface to the L program. See that +manual page for more details. + +B. + +=item $h->scrub_file ($file); + +This command writes patterns over a file to make data retrieval +more difficult. + +The file is I after scrubbing. + +It is an interface to the L program. See that +manual page for more details. + +=item $h->scrub_freespace ($dir); + +This command creates the directory C

and then fills it +with files until the filesystem is full, and scrubs the files +as for C<$h-Escrub_file>, and deletes them. +The intention is to scrub any free space on the partition +containing C. + +It is an interface to the L program. See that +manual page for more details. + =item $h->set_append ($append); This function is used to add additional options to the @@ -1365,6 +1397,8 @@ How many blocks are zeroed isn't specified (but it's I enough to securely wipe the device). It should be sufficient to remove any partition tables, filesystem superblocks and so on. +See also: C<$h-Escrub_device>. + =item $h->zerofree ($device); This runs the I program on C. This program diff --git a/python/guestfs-py.c b/python/guestfs-py.c index 60e20d2e..b1623fe5 100644 --- a/python/guestfs-py.c +++ b/python/guestfs-py.c @@ -4531,6 +4531,81 @@ py_guestfs_glob_expand (PyObject *self, PyObject *args) return py_r; } +static PyObject * +py_guestfs_scrub_device (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + const char *device; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_scrub_device", + &py_g, &device)) + return NULL; + g = get_handle (py_g); + + r = guestfs_scrub_device (g, device); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + +static PyObject * +py_guestfs_scrub_file (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + const char *file; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_scrub_file", + &py_g, &file)) + return NULL; + g = get_handle (py_g); + + r = guestfs_scrub_file (g, file); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + +static PyObject * +py_guestfs_scrub_freespace (PyObject *self, PyObject *args) +{ + PyObject *py_g; + guestfs_h *g; + PyObject *py_r; + int r; + const char *dir; + + if (!PyArg_ParseTuple (args, (char *) "Os:guestfs_scrub_freespace", + &py_g, &dir)) + return NULL; + g = get_handle (py_g); + + r = guestfs_scrub_freespace (g, dir); + if (r == -1) { + PyErr_SetString (PyExc_RuntimeError, guestfs_last_error (g)); + return NULL; + } + + Py_INCREF (Py_None); + py_r = Py_None; + return py_r; +} + static PyMethodDef methods[] = { { (char *) "create", py_guestfs_create, METH_VARARGS, NULL }, { (char *) "close", py_guestfs_close, METH_VARARGS, NULL }, @@ -4699,6 +4774,9 @@ static PyMethodDef methods[] = { { (char *) "sh", py_guestfs_sh, METH_VARARGS, NULL }, { (char *) "sh_lines", py_guestfs_sh_lines, METH_VARARGS, NULL }, { (char *) "glob_expand", py_guestfs_glob_expand, METH_VARARGS, NULL }, + { (char *) "scrub_device", py_guestfs_scrub_device, METH_VARARGS, NULL }, + { (char *) "scrub_file", py_guestfs_scrub_file, METH_VARARGS, NULL }, + { (char *) "scrub_freespace", py_guestfs_scrub_freespace, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; diff --git a/python/guestfs.py b/python/guestfs.py index 39766109..0bc041e6 100644 --- a/python/guestfs.py +++ b/python/guestfs.py @@ -197,7 +197,7 @@ class GuestFS: to modify the image). This is equivalent to the qemu parameter "-drive - file=filename". + file=filename,cache=off". Note that this call checks for the existence of "filename". This stops you from specifying other types @@ -1342,6 +1342,8 @@ class GuestFS: *not* enough to securely wipe the device). It should be sufficient to remove any partition tables, filesystem superblocks and so on. + + See also: "g.scrub_device". """ return libguestfsmod.zero (self._o, device) @@ -1667,3 +1669,38 @@ class GuestFS: """ return libguestfsmod.glob_expand (self._o, pattern) + def scrub_device (self, device): + u"""This command writes patterns over "device" to make data + retrieval more difficult. + + It is an interface to the scrub(1) program. See that + manual page for more details. + + This command is dangerous. Without careful use you can + easily destroy all your data. + """ + return libguestfsmod.scrub_device (self._o, device) + + def scrub_file (self, file): + u"""This command writes patterns over a file to make data + retrieval more difficult. + + The file is *removed* after scrubbing. + + It is an interface to the scrub(1) program. See that + manual page for more details. + """ + return libguestfsmod.scrub_file (self._o, file) + + def scrub_freespace (self, dir): + u"""This command creates the directory "dir" and then fills + it with files until the filesystem is full, and scrubs + the files as for "g.scrub_file", and deletes them. The + intention is to scrub any free space on the partition + containing "dir". + + It is an interface to the scrub(1) program. See that + manual page for more details. + """ + return libguestfsmod.scrub_freespace (self._o, dir) + diff --git a/ruby/ext/guestfs/_guestfs.c b/ruby/ext/guestfs/_guestfs.c index 202c3cac..ba9de13e 100644 --- a/ruby/ext/guestfs/_guestfs.c +++ b/ruby/ext/guestfs/_guestfs.c @@ -4243,6 +4243,72 @@ static VALUE ruby_guestfs_glob_expand (VALUE gv, VALUE patternv) return rv; } +static VALUE ruby_guestfs_scrub_device (VALUE gv, VALUE devicev) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "scrub_device"); + + Check_Type (devicev, T_STRING); + const char *device = StringValueCStr (devicev); + if (!device) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "device", "scrub_device"); + + int r; + + r = guestfs_scrub_device (g, device); + if (r == -1) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + return Qnil; +} + +static VALUE ruby_guestfs_scrub_file (VALUE gv, VALUE filev) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "scrub_file"); + + Check_Type (filev, T_STRING); + const char *file = StringValueCStr (filev); + if (!file) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "file", "scrub_file"); + + int r; + + r = guestfs_scrub_file (g, file); + if (r == -1) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + return Qnil; +} + +static VALUE ruby_guestfs_scrub_freespace (VALUE gv, VALUE dirv) +{ + guestfs_h *g; + Data_Get_Struct (gv, guestfs_h, g); + if (!g) + rb_raise (rb_eArgError, "%s: used handle after closing it", "scrub_freespace"); + + Check_Type (dirv, T_STRING); + const char *dir = StringValueCStr (dirv); + if (!dir) + rb_raise (rb_eTypeError, "expected string for parameter %s of %s", + "dir", "scrub_freespace"); + + int r; + + r = guestfs_scrub_freespace (g, dir); + if (r == -1) + rb_raise (e_Error, "%s", guestfs_last_error (g)); + + return Qnil; +} + /* Initialize the module. */ void Init__guestfs () { @@ -4583,4 +4649,10 @@ void Init__guestfs () ruby_guestfs_sh_lines, 1); rb_define_method (c_guestfs, "glob_expand", ruby_guestfs_glob_expand, 1); + rb_define_method (c_guestfs, "scrub_device", + ruby_guestfs_scrub_device, 1); + rb_define_method (c_guestfs, "scrub_file", + ruby_guestfs_scrub_file, 1); + rb_define_method (c_guestfs, "scrub_freespace", + ruby_guestfs_scrub_freespace, 1); } diff --git a/src/guestfs-actions.c b/src/guestfs-actions.c index e1ec1589..74021053 100644 --- a/src/guestfs-actions.c +++ b/src/guestfs-actions.c @@ -10360,3 +10360,264 @@ char **guestfs_glob_expand (guestfs_h *g, return ctx.ret.paths.paths_val; } +struct scrub_device_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void scrub_device_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct scrub_device_ctx *ctx = (struct scrub_device_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_scrub_device"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_scrub_device"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_scrub_device"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1; +} + +int guestfs_scrub_device (guestfs_h *g, + const char *device) +{ + struct guestfs_scrub_device_args args; + struct scrub_device_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_scrub_device") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.device = (char *) device; + serial = guestfs__send_sync (g, GUESTFS_PROC_SCRUB_DEVICE, + (xdrproc_t) xdr_guestfs_scrub_device_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return -1; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, scrub_device_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_scrub_device"); + guestfs_end_busy (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_SCRUB_DEVICE, serial) == -1) { + guestfs_end_busy (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return -1; + } + + guestfs_end_busy (g); + return 0; +} + +struct scrub_file_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void scrub_file_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct scrub_file_ctx *ctx = (struct scrub_file_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_scrub_file"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_scrub_file"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_scrub_file"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1; +} + +int guestfs_scrub_file (guestfs_h *g, + const char *file) +{ + struct guestfs_scrub_file_args args; + struct scrub_file_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_scrub_file") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.file = (char *) file; + serial = guestfs__send_sync (g, GUESTFS_PROC_SCRUB_FILE, + (xdrproc_t) xdr_guestfs_scrub_file_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return -1; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, scrub_file_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_scrub_file"); + guestfs_end_busy (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_SCRUB_FILE, serial) == -1) { + guestfs_end_busy (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return -1; + } + + guestfs_end_busy (g); + return 0; +} + +struct scrub_freespace_ctx { + /* This flag is set by the callbacks, so we know we've done + * the callbacks as expected, and in the right sequence. + * 0 = not called, 1 = reply_cb called. + */ + int cb_sequence; + struct guestfs_message_header hdr; + struct guestfs_message_error err; +}; + +static void scrub_freespace_reply_cb (guestfs_h *g, void *data, XDR *xdr) +{ + guestfs_main_loop *ml = guestfs_get_main_loop (g); + struct scrub_freespace_ctx *ctx = (struct scrub_freespace_ctx *) data; + + /* This should definitely not happen. */ + if (ctx->cb_sequence != 0) { + ctx->cb_sequence = 9999; + error (g, "%s: internal error: reply callback called twice", "guestfs_scrub_freespace"); + return; + } + + ml->main_loop_quit (ml, g); + + if (!xdr_guestfs_message_header (xdr, &ctx->hdr)) { + error (g, "%s: failed to parse reply header", "guestfs_scrub_freespace"); + return; + } + if (ctx->hdr.status == GUESTFS_STATUS_ERROR) { + if (!xdr_guestfs_message_error (xdr, &ctx->err)) { + error (g, "%s: failed to parse reply error", "guestfs_scrub_freespace"); + return; + } + goto done; + } + done: + ctx->cb_sequence = 1; +} + +int guestfs_scrub_freespace (guestfs_h *g, + const char *dir) +{ + struct guestfs_scrub_freespace_args args; + struct scrub_freespace_ctx ctx; + guestfs_main_loop *ml = guestfs_get_main_loop (g); + int serial; + + if (check_state (g, "guestfs_scrub_freespace") == -1) return -1; + guestfs_set_busy (g); + + memset (&ctx, 0, sizeof ctx); + + args.dir = (char *) dir; + serial = guestfs__send_sync (g, GUESTFS_PROC_SCRUB_FREESPACE, + (xdrproc_t) xdr_guestfs_scrub_freespace_args, (char *) &args); + if (serial == -1) { + guestfs_end_busy (g); + return -1; + } + + guestfs__switch_to_receiving (g); + ctx.cb_sequence = 0; + guestfs_set_reply_callback (g, scrub_freespace_reply_cb, &ctx); + (void) ml->main_loop_run (ml, g); + guestfs_set_reply_callback (g, NULL, NULL); + if (ctx.cb_sequence != 1) { + error (g, "%s reply failed, see earlier error messages", "guestfs_scrub_freespace"); + guestfs_end_busy (g); + return -1; + } + + if (check_reply_header (g, &ctx.hdr, GUESTFS_PROC_SCRUB_FREESPACE, serial) == -1) { + guestfs_end_busy (g); + return -1; + } + + if (ctx.hdr.status == GUESTFS_STATUS_ERROR) { + error (g, "%s", ctx.err.error_message); + free (ctx.err.error_message); + guestfs_end_busy (g); + return -1; + } + + guestfs_end_busy (g); + return 0; +} + diff --git a/src/guestfs-actions.h b/src/guestfs-actions.h index 19996e8f..0f85df7a 100644 --- a/src/guestfs-actions.h +++ b/src/guestfs-actions.h @@ -184,3 +184,6 @@ extern int guestfs_ntfs_3g_probe (guestfs_h *handle, int rw, const char *device) extern char *guestfs_sh (guestfs_h *handle, const char *command); extern char **guestfs_sh_lines (guestfs_h *handle, const char *command); extern char **guestfs_glob_expand (guestfs_h *handle, const char *pattern); +extern int guestfs_scrub_device (guestfs_h *handle, const char *device); +extern int guestfs_scrub_file (guestfs_h *handle, const char *file); +extern int guestfs_scrub_freespace (guestfs_h *handle, const char *dir); diff --git a/src/guestfs_protocol.c b/src/guestfs_protocol.c index b2d435ee..a241aa0e 100644 --- a/src/guestfs_protocol.c +++ b/src/guestfs_protocol.c @@ -1932,6 +1932,36 @@ xdr_guestfs_glob_expand_ret (XDR *xdrs, guestfs_glob_expand_ret *objp) return TRUE; } +bool_t +xdr_guestfs_scrub_device_args (XDR *xdrs, guestfs_scrub_device_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->device, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_scrub_file_args (XDR *xdrs, guestfs_scrub_file_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->file, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_guestfs_scrub_freespace_args (XDR *xdrs, guestfs_scrub_freespace_args *objp) +{ + register int32_t *buf; + + if (!xdr_string (xdrs, &objp->dir, ~0)) + return FALSE; + return TRUE; +} + bool_t xdr_guestfs_procedure (XDR *xdrs, guestfs_procedure *objp) { diff --git a/src/guestfs_protocol.h b/src/guestfs_protocol.h index 8fdb6640..dc88c70f 100644 --- a/src/guestfs_protocol.h +++ b/src/guestfs_protocol.h @@ -982,6 +982,21 @@ struct guestfs_glob_expand_ret { }; typedef struct guestfs_glob_expand_ret guestfs_glob_expand_ret; +struct guestfs_scrub_device_args { + char *device; +}; +typedef struct guestfs_scrub_device_args guestfs_scrub_device_args; + +struct guestfs_scrub_file_args { + char *file; +}; +typedef struct guestfs_scrub_file_args guestfs_scrub_file_args; + +struct guestfs_scrub_freespace_args { + char *dir; +}; +typedef struct guestfs_scrub_freespace_args guestfs_scrub_freespace_args; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -1096,7 +1111,10 @@ enum guestfs_procedure { GUESTFS_PROC_SH = 111, GUESTFS_PROC_SH_LINES = 112, GUESTFS_PROC_GLOB_EXPAND = 113, - GUESTFS_PROC_NR_PROCS = 113 + 1, + GUESTFS_PROC_SCRUB_DEVICE = 114, + GUESTFS_PROC_SCRUB_FILE = 115, + GUESTFS_PROC_SCRUB_FREESPACE = 116, + GUESTFS_PROC_NR_PROCS = 116 + 1, }; typedef enum guestfs_procedure guestfs_procedure; #define GUESTFS_MESSAGE_MAX 4194304 @@ -1303,6 +1321,9 @@ extern bool_t xdr_guestfs_sh_lines_args (XDR *, guestfs_sh_lines_args*); extern bool_t xdr_guestfs_sh_lines_ret (XDR *, guestfs_sh_lines_ret*); extern bool_t xdr_guestfs_glob_expand_args (XDR *, guestfs_glob_expand_args*); extern bool_t xdr_guestfs_glob_expand_ret (XDR *, guestfs_glob_expand_ret*); +extern bool_t xdr_guestfs_scrub_device_args (XDR *, guestfs_scrub_device_args*); +extern bool_t xdr_guestfs_scrub_file_args (XDR *, guestfs_scrub_file_args*); +extern bool_t xdr_guestfs_scrub_freespace_args (XDR *, guestfs_scrub_freespace_args*); extern bool_t xdr_guestfs_procedure (XDR *, guestfs_procedure*); extern bool_t xdr_guestfs_message_direction (XDR *, guestfs_message_direction*); extern bool_t xdr_guestfs_message_status (XDR *, guestfs_message_status*); @@ -1468,6 +1489,9 @@ extern bool_t xdr_guestfs_sh_lines_args (); extern bool_t xdr_guestfs_sh_lines_ret (); extern bool_t xdr_guestfs_glob_expand_args (); extern bool_t xdr_guestfs_glob_expand_ret (); +extern bool_t xdr_guestfs_scrub_device_args (); +extern bool_t xdr_guestfs_scrub_file_args (); +extern bool_t xdr_guestfs_scrub_freespace_args (); extern bool_t xdr_guestfs_procedure (); extern bool_t xdr_guestfs_message_direction (); extern bool_t xdr_guestfs_message_status (); diff --git a/src/guestfs_protocol.x b/src/guestfs_protocol.x index d6627267..094200eb 100644 --- a/src/guestfs_protocol.x +++ b/src/guestfs_protocol.x @@ -756,6 +756,18 @@ struct guestfs_glob_expand_ret { str paths<>; }; +struct guestfs_scrub_device_args { + string device<>; +}; + +struct guestfs_scrub_file_args { + string file<>; +}; + +struct guestfs_scrub_freespace_args { + string dir<>; +}; + enum guestfs_procedure { GUESTFS_PROC_MOUNT = 1, GUESTFS_PROC_SYNC = 2, @@ -870,6 +882,9 @@ enum guestfs_procedure { GUESTFS_PROC_SH = 111, GUESTFS_PROC_SH_LINES = 112, GUESTFS_PROC_GLOB_EXPAND = 113, + GUESTFS_PROC_SCRUB_DEVICE = 114, + GUESTFS_PROC_SCRUB_FILE = 115, + GUESTFS_PROC_SCRUB_FREESPACE = 116, GUESTFS_PROC_NR_PROCS }; -- cgit From d2c0ee0ff7a9afdcdc1605dd464760f98d1b57d7 Mon Sep 17 00:00:00 2001 From: Matthew Booth Date: Tue, 23 Jun 2009 17:35:40 +0100 Subject: Make the supermin helper look for any x86 kernel If you've got a non-PAE kernel installed on an i686 machine, the kernel architecture is i586. This change makes sure that supermin finds the installed kernel. --- appliance/libguestfs-supermin-helper.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appliance/libguestfs-supermin-helper.in b/appliance/libguestfs-supermin-helper.in index d7c8d96b..2afd0cfa 100755 --- a/appliance/libguestfs-supermin-helper.in +++ b/appliance/libguestfs-supermin-helper.in @@ -30,12 +30,12 @@ kernel="$2" initrd="$3" # Kernel: -# Look for any kernel named vmlinuz-*.$host_cpu* which has -# a corresponding /lib/modules/*.$host_cpu* directory. -# However by sorting on reverse version (ls -vr) we ensure -# we choose the newest kernels. +# Look for the most recent kernel named vmlinuz-*.* which has a +# corresponding directory in /lib/modules/. If the architecture is x86, look +# for any x86 kernel. -for f in $(ls -1vr /boot/vmlinuz-*.@host_cpu@* | grep -v xen); do +arch=$(echo "@host_cpu@" | sed 's/^i.86$/i?86/') +for f in $(ls -1vr /boot/vmlinuz-*.$arch* 2>/dev/null | grep -v xen); do b=$(basename "$f") b=$(echo "$b" | sed 's,vmlinuz-,,') modpath="/lib/modules/$b" -- cgit From d3270cfadd3416bd9961a2c6fb1cc131c43979da Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 23 Jun 2009 22:02:10 +0100 Subject: Implement libtool library versioning. Use maximum proc_nr (MAX_PROC_NR) as a surrogate for the library ABI version, resulting in version numbers such as libguestfs.so.0..0 for the final library. Add ABI guarantee to the documentation. --- configure.ac | 4 ++++ guestfs.pod | 8 ++++++++ src/MAX_PROC_NR | 1 + src/Makefile.am | 44 +++++++++++++++++++++++++++++++++++++++++++- src/generator.ml | 17 +++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/MAX_PROC_NR diff --git a/configure.ac b/configure.ac index ec64cd3f..91e47925 100644 --- a/configure.ac +++ b/configure.ac @@ -497,6 +497,10 @@ fi AM_CONDITIONAL([HAVE_INSPECTOR], [test "x$PERL" != "xno" -a "x$missing_perl_modules" != "xyes"]) +dnl Library versioning. +MAX_PROC_NR=`cat $srcdir/src/MAX_PROC_NR` +AC_SUBST(MAX_PROC_NR) + dnl Run in subdirs. AC_CONFIG_SUBDIRS([daemon]) diff --git a/guestfs.pod b/guestfs.pod index 10c6ad8c..3dad35c4 100644 --- a/guestfs.pod +++ b/guestfs.pod @@ -205,6 +205,14 @@ search the current directory and then C. =head1 HIGH-LEVEL API ACTIONS +=head2 ABI GUARANTEE + +We guarantee the libguestfs ABI (binary interface), for public, +high-level actions as outlined in this section. Although we will +deprecate some actions, for example if they get replaced by newer +calls, we will keep the old actions forever. This allows you the +developer to program in confidence against libguestfs. + @ACTIONS@ =head1 STRUCTURES diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR new file mode 100644 index 00000000..4699eb3c --- /dev/null +++ b/src/MAX_PROC_NR @@ -0,0 +1 @@ +116 diff --git a/src/Makefile.am b/src/Makefile.am index c310c9c4..6b1088ca 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,7 +25,49 @@ include_HEADERS = guestfs.h guestfs-actions.h guestfs-structs.h lib_LTLIBRARIES = libguestfs.la -libguestfs_la_LDFLAGS = -version-info 0:0:0 +# From the libtool info file, with comments: +# +# | 1. Start with version information of `0:0:0' for each libtool library. +# | +# | 2. Update the version information only immediately before a public +# | release of your software. More frequent updates are unnecessary, +# | and only guarantee that the current interface number gets larger +# | faster. +# | +# | 3. If the library source code has changed at all since the last +# | update, then increment REVISION (`C:R:A' becomes `C:r+1:A'). +# +# [So it seems like we should always update the middle 'R' field +# for any release.] +# +# | 4. If any interfaces have been added, removed, or changed since the +# | last update, increment CURRENT, and set REVISION to 0. +# | +# | 5. If any interfaces have been added since the last public release, +# | then increment AGE. +# +# [These two rules seem to mean that if any change is made to the +# generator, we should increment C and A, and set R to 0, so: +# C+1:0:A+1.] +# +# | 6. If any interfaces have been removed since the last public release, +# | then set AGE to 0. +# +# [Our ABI guarantee means we won't remove interfaces except in +# very exceptional circumstances.] +# +# The maximum proc number (see guestfs_protocol.x:guestfs_procedure) is +# a mostly accurate stand-in for C & A in rules 5 & 6, so we use that. It +# is always incremented when we add a new appliance interface, and easy to +# calculate. +# +# The middle number is hard to increment-and-reset as described in rules +# 4 & 5, so for the moment it is always set to 0. +# +# Note that this scheme means the real library version will always be +# 'libguestfs.so.0.$(MAX_PROC_NR).0'. + +libguestfs_la_LDFLAGS = -version-info $(MAX_PROC_NR):0:$(MAX_PROC_NR) libguestfs_la_SOURCES = \ guestfs.c \ guestfs.h \ diff --git a/src/generator.ml b/src/generator.ml index f1a9a45f..51d32357 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -7829,6 +7829,19 @@ and generate_lang_bindtests call = (* XXX Add here tests of the return and error functions. *) +(* This is used to generate the src/MAX_PROC_NR file which + * contains the maximum procedure number, a surrogate for the + * ABI version number. See src/Makefile.am for the details. + *) +and generate_max_proc_nr () = + let proc_nrs = List.map ( + fun (_, _, proc_nr, _, _, _, _) -> proc_nr + ) daemon_functions in + + let max_proc_nr = List.fold_left max 0 proc_nrs in + + pr "%d\n" max_proc_nr + let output_to filename = let filename_new = filename ^ ".new" in chan := open_out filename_new; @@ -8001,3 +8014,7 @@ Run it from the top source directory using the command let close = output_to "haskell/bindtests.hs" in generate_haskell_bindtests (); close (); + + let close = output_to "src/MAX_PROC_NR" in + generate_max_proc_nr (); + close (); -- cgit From ca5239918ce6de45a694610a11678cadc20cf2fb Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 09:45:14 +0100 Subject: Todo items: guestfish options -i and -f. --- TODO | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/TODO b/TODO index 1d7cb364..b3860b1f 100644 --- a/TODO +++ b/TODO @@ -111,3 +111,11 @@ PPC problems: ---------------------------------------------------------------------- Supermin appliance should be moved into febootstrap. + +---------------------------------------------------------------------- + +guestfish -i (runs inspector) + +---------------------------------------------------------------------- + +guestfish -f -- cgit From 94a49c8207277ec4017a0fb9a3cd8f1e61a518f6 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 09:56:34 +0100 Subject: Incorrect assignment on glob error path. --- fish/glob.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fish/glob.c b/fish/glob.c index 827e0624..f20da84a 100644 --- a/fish/glob.c +++ b/fish/glob.c @@ -148,7 +148,7 @@ glob_issue (char *cmd, int argc, printf ("\n"); if (issue_command (argv[0], &argv[1]) == -1) - r = -1; /* ... but don't exit */ + *r = -1; /* ... but don't exit */ for (i = argc-1; i >= 1; --i) { posn[i]++; -- cgit From 46551d9c51193a4bca2e1b249b8c5f111e1dc7b5 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 09:59:39 +0100 Subject: Implement guestfish -f option to allow guestfish scripts. New '-f' option allows scripts to be written using: #!/usr/bin/guestfish -f --- TODO | 4 ---- fish/fish.c | 23 +++++++++++++++++++++-- guestfish.pod | 18 ++++++++++++++++-- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index b3860b1f..233ac3c4 100644 --- a/TODO +++ b/TODO @@ -115,7 +115,3 @@ Supermin appliance should be moved into febootstrap. ---------------------------------------------------------------------- guestfish -i (runs inspector) - ----------------------------------------------------------------------- - -guestfish -f diff --git a/fish/fish.c b/fish/fish.c index bf82b8af..cba0343b 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -102,6 +102,7 @@ usage (void) " -h|--cmd-help cmd Display detailed help on 'cmd'\n" " -a|--add image Add image\n" " -D|--no-dest-paths Don't tab-complete paths from guest fs\n" + " -f|--file file Read commands from file\n" " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n" " -n|--no-sync Don't autosync\n" " -r|--ro Mount read-only\n" @@ -113,10 +114,11 @@ usage (void) int main (int argc, char *argv[]) { - static const char *options = "a:h::m:nrv?V"; + static const char *options = "a:f:h::m:nrv?V"; static struct option long_options[] = { { "add", 1, 0, 'a' }, { "cmd-help", 2, 0, 'h' }, + { "file", 1, 0, 'f' }, { "help", 0, 0, '?' }, { "mount", 1, 0, 'm' }, { "no-dest-paths", 0, 0, 'D' }, @@ -130,7 +132,7 @@ main (int argc, char *argv[]) struct drv *drv; struct mp *mps = NULL; struct mp *mp; - char *p; + char *p, *file = NULL; int c; initialize_readline (); @@ -183,6 +185,14 @@ main (int argc, char *argv[]) complete_dest_paths = 0; break; + case 'f': + if (file) { + fprintf (stderr, _("guestfish: only one -f parameter can be given\n")); + exit (1); + } + file = optarg; + break; + case 'h': if (optarg) display_command (optarg); @@ -246,6 +256,15 @@ main (int argc, char *argv[]) mount_mps (mps); } + /* -f (file) parameter? */ + if (file) { + close (0); + if (open (file, O_RDONLY) == -1) { + perror (file); + exit (1); + } + } + /* Interactive, shell script, or command(s) on the command line? */ if (optind >= argc) { if (isatty (0)) diff --git a/guestfish.pod b/guestfish.pod index d83a61ca..2d39cbf4 100644 --- a/guestfish.pod +++ b/guestfish.pod @@ -40,8 +40,7 @@ Remove C (in reality not such a great idea): guestfish --add disk.img \ --mount /dev/VolGroup00/LogVol00 \ --mount /dev/sda1:/boot \ - rm /boot/grub/menu.lst : \ - sync : exit + rm /boot/grub/menu.lst =head2 As an interactive shell @@ -55,6 +54,14 @@ Remove C (in reality not such a great idea): > help +=head2 As a script interpreter + + #!/usr/bin/guestfish -f + alloc /tmp/output.img 10M + run + sfdisk /dev/sda 0 0 0 , + mkfs ext2 /dev/sda1 + =head1 DESCRIPTION Guestfish is a shell and command-line tool for examining and modifying @@ -81,6 +88,13 @@ Displays detailed help on a single command C. Add a block device or virtual machine image to the shell. +=item B<-f file> | B<--file file> + +Read commands from C. To write pure guestfish +scripts, use: + + #!/usr/bin/guestfish -f + =item B<-m dev[:mountpoint]> | B<--mount dev[:mountpoint]> Mount the named partition or logical volume on the given mountpoint. -- cgit From 5e332cc6c06532191f793a6789bafe818f726258 Mon Sep 17 00:00:00 2001 From: Matthew Booth Date: Wed, 24 Jun 2009 11:50:08 +0100 Subject: Fix dependencies in perl so it doesn't always rebuild --- perl/Makefile.PL.in | 3 ++- perl/Makefile.am | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/perl/Makefile.PL.in b/perl/Makefile.PL.in index 423b4a18..40d1d6c4 100644 --- a/perl/Makefile.PL.in +++ b/perl/Makefile.PL.in @@ -24,5 +24,6 @@ WriteMakefile ( VERSION => '@PACKAGE_VERSION@', LIBS => '-L@abs_top_builddir@/src/.libs -lguestfs', - INC => '-Wall @CFLAGS@ -I@abs_top_builddir@/src', + INC => '-I@abs_top_builddir@/src', + CCFLAGS => '@CFLAGS@', ); diff --git a/perl/Makefile.am b/perl/Makefile.am index 44ac42c0..747ac436 100644 --- a/perl/Makefile.am +++ b/perl/Makefile.am @@ -41,10 +41,12 @@ TESTS_ENVIRONMENT = \ INSTALLDIRS = site -all: - perl Makefile.PL INSTALLDIRS=$(INSTALLDIRS) PREFIX=$(prefix) +all: Makefile-pl $(MAKE) -f Makefile-pl +Makefile-pl: Makefile.PL + perl Makefile.PL INSTALLDIRS=$(INSTALLDIRS) PREFIX=$(prefix) + install-data-hook: $(MAKE) -f Makefile-pl DESTDIR=$(DESTDIR) install -- cgit From e1c62f6332ae07cb6f95130dcc9852701d085e2b Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 11:54:48 +0100 Subject: Added guestfish -i option to run virt-inspector. You can invoke guestfish with: guestfish -i libvirt-domain guestfish -i disk-image(s) --- TODO | 4 --- fish/fish.c | 60 +++++++++++++++++++++++++++++++++++++++++++-- guestfish.pod | 28 +++++++++++++++++++++ inspector/virt-inspector.pl | 11 ++++----- 4 files changed, 91 insertions(+), 12 deletions(-) diff --git a/TODO b/TODO index 233ac3c4..1d7cb364 100644 --- a/TODO +++ b/TODO @@ -111,7 +111,3 @@ PPC problems: ---------------------------------------------------------------------- Supermin appliance should be moved into febootstrap. - ----------------------------------------------------------------------- - -guestfish -i (runs inspector) diff --git a/fish/fish.c b/fish/fish.c index cba0343b..b46fd7e1 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -90,6 +90,8 @@ usage (void) "Copyright (C) 2009 Red Hat Inc.\n" "Usage:\n" " guestfish [--options] cmd [: cmd : cmd ...]\n" + " guestfish -i libvirt-domain\n" + " guestfish -i disk-image(s)\n" "or for interactive use:\n" " guestfish\n" "or from a shell script:\n" @@ -103,6 +105,7 @@ usage (void) " -a|--add image Add image\n" " -D|--no-dest-paths Don't tab-complete paths from guest fs\n" " -f|--file file Read commands from file\n" + " -i|--inspector Run virt-inspector to get disk mountpoints\n" " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n" " -n|--no-sync Don't autosync\n" " -r|--ro Mount read-only\n" @@ -114,12 +117,13 @@ usage (void) int main (int argc, char *argv[]) { - static const char *options = "a:f:h::m:nrv?V"; + static const char *options = "a:f:h::im:nrv?V"; static struct option long_options[] = { { "add", 1, 0, 'a' }, { "cmd-help", 2, 0, 'h' }, { "file", 1, 0, 'f' }, { "help", 0, 0, '?' }, + { "inspector", 0, 0, 'i' }, { "mount", 1, 0, 'm' }, { "no-dest-paths", 0, 0, 'D' }, { "no-sync", 0, 0, 'n' }, @@ -133,7 +137,7 @@ main (int argc, char *argv[]) struct mp *mps = NULL; struct mp *mp; char *p, *file = NULL; - int c; + int c, inspector = 0; initialize_readline (); @@ -202,6 +206,10 @@ main (int argc, char *argv[]) list_commands (); exit (0); + case 'i': + inspector = 1; + break; + case 'm': mp = malloc (sizeof (struct mp)); if (!mp) { @@ -247,6 +255,54 @@ main (int argc, char *argv[]) } } + /* Inspector mode invalidates most of the other arguments. */ + if (inspector) { + char cmd[1024]; + int r; + + if (drvs || mps) { + fprintf (stderr, _("guestfish: cannot use -i option with -a or -m\n")); + exit (1); + } + if (optind >= argc) { + fprintf (stderr, _("guestfish -i requires a libvirt domain or path(s) to disk image(s)\n")); + exit (1); + } + + strcpy (cmd, "a=`virt-inspector"); + while (optind < argc) { + if (strlen (cmd) + strlen (argv[optind]) + strlen (argv[0]) + 60 + >= sizeof cmd) { + fprintf (stderr, _("guestfish: virt-inspector command too long for fixed-size buffer\n")); + exit (1); + } + strcat (cmd, " "); + strcat (cmd, argv[optind]); + optind++; + } + + if (read_only) + strcat (cmd, " --ro-fish"); + else + strcat (cmd, " --fish"); + + sprintf (&cmd[strlen(cmd)], "` && %s $a", argv[0]); + + if (guestfs_get_verbose (g)) + strcat (cmd, " -v"); + if (!guestfs_get_autosync (g)) + strcat (cmd, " -n"); + + /*printf ("%s\n", cmd);*/ + + r = system (cmd); + if (r == -1) { + perror ("system"); + exit (1); + } + exit (WEXITSTATUS (r)); + } + /* If we've got drives to add, add them now. */ add_drives (drvs); diff --git a/guestfish.pod b/guestfish.pod index 2d39cbf4..56d941f8 100644 --- a/guestfish.pod +++ b/guestfish.pod @@ -8,6 +8,10 @@ guestfish - the libguestfs filesystem interactive shell guestfish [--options] [commands] + guestfish -i libvirt-domain + + guestfish -i disk-image(s) + =head1 EXAMPLES =head2 From shell scripts @@ -95,6 +99,30 @@ scripts, use: #!/usr/bin/guestfish -f +=item B<-i> | B<--inspector> + +Run virt-inspector on the named libvirt domain or list of disk +images. If virt-inspector is available and if it can identify +the domain or disk images, then partitions will be mounted +correctly at start-up. + +Typical usage is either: + + guestfish -i myguest + +(for an inactive libvirt domain called I), or: + + guestfish --ro -i myguest + +(for active domains, readonly), or specify the block device directly: + + guestfish -i /dev/Guests/MyGuest + +You cannot use I<-a> or I<-m> in conjunction with this option, and +options other than I<--ro> might not behave correctly. + +See also: L. + =item B<-m dev[:mountpoint]> | B<--mount dev[:mountpoint]> Mount the named partition or logical volume on the given mountpoint. diff --git a/inspector/virt-inspector.pl b/inspector/virt-inspector.pl index 1d8a84b4..717ccb61 100755 --- a/inspector/virt-inspector.pl +++ b/inspector/virt-inspector.pl @@ -145,7 +145,7 @@ If you select I<--fish> then we print a L command line which will automatically mount up the filesystems on the correct mount points. Try this for example: - eval `virt-inspector --fish guest.img` + guestfish $(virt-inspector --fish guest.img) I<--ro-fish> is the same, but the I<--ro> option is passed to guestfish so that the filesystems are mounted read-only. @@ -299,7 +299,7 @@ L command line parameters, so that you can go in afterwards and inspect the guest with everything mounted in the right place. For example: - eval `virt-inspector --ro-fish guest.img` + guestfish $(virt-inspector --ro-fish guest.img) ==> guestfish --ro -a guest.img -m /dev/VG/LV:/ -m /dev/sda1:/boot =cut @@ -905,18 +905,17 @@ if ($output eq "fish" || $output eq "ro-fish") { my $root_dev = $osdevs[0]; - print "guestfish"; if ($output eq "ro-fish") { - print " --ro"; + print "--ro "; } - print " -a $_" foreach @images; + print "-a $_ " foreach @images; my $mounts = $oses{$root_dev}->{mounts}; # Have to mount / first. Luckily '/' is early in the ASCII # character set, so this should be OK. foreach (sort keys %$mounts) { - print " -m $mounts->{$_}:$_" if $_ ne "swap"; + print "-m $mounts->{$_}:$_ " if $_ ne "swap"; } print "\n" } -- cgit From b96fb7eb3aa0ac67957ed15a7dd22bc7399cad44 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 12:15:57 +0100 Subject: Quote command line arguments to virt-inspector. --- fish/fish.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fish/fish.c b/fish/fish.c index b46fd7e1..5e8a6e9e 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -276,8 +276,9 @@ main (int argc, char *argv[]) fprintf (stderr, _("guestfish: virt-inspector command too long for fixed-size buffer\n")); exit (1); } - strcat (cmd, " "); + strcat (cmd, " '"); strcat (cmd, argv[optind]); + strcat (cmd, "'"); optind++; } -- cgit From 63d28e74b3a2c4e6f3e31fc1f8e1219a1a943404 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 12:41:24 +0100 Subject: Version 1.0.52. --- configure.ac | 2 +- po/libguestfs.pot | 189 +++++++++++++++++++++++++++++++---------------------- po/pl.po | 191 +++++++++++++++++++++++++++++++----------------------- src/Makefile.am | 3 +- 4 files changed, 224 insertions(+), 161 deletions(-) diff --git a/configure.ac b/configure.ac index 91e47925..42f0127a 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.51]) +AC_INIT([libguestfs],[1.0.52]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) diff --git a/po/libguestfs.pot b/po/libguestfs.pot index 67d4dc1e..2a913eac 100644 --- a/po/libguestfs.pot +++ b/po/libguestfs.pot @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-22 10:02+0100\n" +"POT-Creation-Date: 2009-06-24 12:47+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -45,6 +45,8 @@ msgid "" "Copyright (C) 2009 Red Hat Inc.\n" "Usage:\n" " guestfish [--options] cmd [: cmd : cmd ...]\n" +" guestfish -i libvirt-domain\n" +" guestfish -i disk-image(s)\n" "or for interactive use:\n" " guestfish\n" "or from a shell script:\n" @@ -57,6 +59,8 @@ msgid "" " -h|--cmd-help cmd Display detailed help on 'cmd'\n" " -a|--add image Add image\n" " -D|--no-dest-paths Don't tab-complete paths from guest fs\n" +" -f|--file file Read commands from file\n" +" -i|--inspector Run virt-inspector to get disk mountpoints\n" " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n" " -n|--no-sync Don't autosync\n" " -r|--ro Mount read-only\n" @@ -65,17 +69,37 @@ msgid "" "For more information, see the manpage guestfish(1).\n" msgstr "" -#: fish/fish.c:143 +#: fish/fish.c:149 #, c-format msgid "guestfs_create: failed to create handle\n" msgstr "" -#: fish/fish.c:234 +#: fish/fish.c:194 +#, c-format +msgid "guestfish: only one -f parameter can be given\n" +msgstr "" + +#: fish/fish.c:252 #, c-format msgid "guestfish: unexpected command line option 0x%x\n" msgstr "" -#: fish/fish.c:380 +#: fish/fish.c:264 +#, c-format +msgid "guestfish: cannot use -i option with -a or -m\n" +msgstr "" + +#: fish/fish.c:268 +#, c-format +msgid "guestfish -i requires a libvirt domain or path(s) to disk image(s)\n" +msgstr "" + +#: fish/fish.c:276 +#, c-format +msgid "guestfish: virt-inspector command too long for fixed-size buffer\n" +msgstr "" + +#: fish/fish.c:456 #, c-format msgid "" "\n" @@ -87,65 +111,65 @@ msgid "" "\n" msgstr "" -#: fish/fish.c:457 +#: fish/fish.c:533 #, c-format msgid "guestfish: unterminated double quote\n" msgstr "" -#: fish/fish.c:462 fish/fish.c:477 +#: fish/fish.c:538 fish/fish.c:553 #, c-format msgid "guestfish: command arguments not separated by whitespace\n" msgstr "" -#: fish/fish.c:472 +#: fish/fish.c:548 #, c-format msgid "guestfish: unterminated single quote\n" msgstr "" -#: fish/fish.c:513 +#: fish/fish.c:589 #, c-format msgid "guestfish: internal error parsing string at '%s'\n" msgstr "" -#: fish/fish.c:526 +#: fish/fish.c:602 #, c-format msgid "guestfish: too many arguments\n" msgstr "" -#: fish/fish.c:553 +#: fish/fish.c:629 #, c-format msgid "guestfish: empty command on command line\n" msgstr "" -#: fish/fish.c:614 +#: fish/fish.c:690 msgid "display a list of commands or help on a command" msgstr "" -#: fish/fish.c:616 +#: fish/fish.c:692 msgid "quit guestfish" msgstr "" -#: fish/fish.c:619 +#: fish/fish.c:695 msgid "allocate an image" msgstr "" -#: fish/fish.c:621 +#: fish/fish.c:697 msgid "display a line of text" msgstr "" -#: fish/fish.c:623 +#: fish/fish.c:699 msgid "edit a file in the image" msgstr "" -#: fish/fish.c:625 +#: fish/fish.c:701 msgid "local change directory" msgstr "" -#: fish/fish.c:627 +#: fish/fish.c:703 msgid "expand wildcards in command" msgstr "" -#: fish/fish.c:639 +#: fish/fish.c:715 #, c-format msgid "" "alloc - allocate an image\n" @@ -165,7 +189,7 @@ msgid "" " sects number of 512 byte sectors\n" msgstr "" -#: fish/fish.c:655 +#: fish/fish.c:731 #, c-format msgid "" "echo - display a line of text\n" @@ -174,7 +198,7 @@ msgid "" " This echos the parameters to the terminal.\n" msgstr "" -#: fish/fish.c:662 +#: fish/fish.c:738 #, c-format msgid "" "edit - edit a file in the image\n" @@ -192,7 +216,7 @@ msgid "" " (> 2 MB) or binary files containing \\0 bytes.\n" msgstr "" -#: fish/fish.c:676 +#: fish/fish.c:752 #, c-format msgid "" "lcd - local change directory\n" @@ -203,7 +227,7 @@ msgid "" " place.\n" msgstr "" -#: fish/fish.c:683 +#: fish/fish.c:759 #, c-format msgid "" "glob - expand wildcards in command\n" @@ -214,7 +238,7 @@ msgid "" " once for each expanded argument.\n" msgstr "" -#: fish/fish.c:690 +#: fish/fish.c:766 #, c-format msgid "" "help - display a list of commands or help on a command\n" @@ -222,256 +246,263 @@ msgid "" " help\n" msgstr "" -#: fish/fish.c:696 +#: fish/fish.c:772 #, c-format msgid "" "quit - quit guestfish\n" " quit\n" msgstr "" -#: fish/fish.c:699 +#: fish/fish.c:775 #, c-format msgid "%s: command not known, use -h to list all commands\n" msgstr "" -#: src/guestfs.c:288 +#: src/guestfs.c:290 #, c-format msgid "guestfs_close: called twice on the same handle\n" msgstr "" -#: src/guestfs.c:375 +#: src/guestfs.c:379 #, c-format msgid "libguestfs: error: %s\n" msgstr "" -#: src/guestfs.c:616 +#: src/guestfs.c:620 msgid "command line cannot be altered after qemu subprocess launched" msgstr "" -#: src/guestfs.c:630 +#: src/guestfs.c:634 msgid "guestfs_config: parameter must begin with '-' character" msgstr "" -#: src/guestfs.c:645 +#: src/guestfs.c:648 #, c-format msgid "guestfs_config: parameter '%s' isn't allowed" msgstr "" -#: src/guestfs.c:665 src/guestfs.c:686 src/guestfs.c:704 +#: src/guestfs.c:668 src/guestfs.c:690 src/guestfs.c:708 msgid "filename cannot contain ',' (comma) character" msgstr "" -#: src/guestfs.c:771 +#: src/guestfs.c:777 msgid "you must call guestfs_add_drive before guestfs_launch" msgstr "" -#: src/guestfs.c:776 +#: src/guestfs.c:782 msgid "qemu has already been launched" msgstr "" -#: src/guestfs.c:784 +#: src/guestfs.c:790 #, c-format msgid "%s: cannot create temporary directory" msgstr "" -#: src/guestfs.c:874 +#: src/guestfs.c:880 #, c-format msgid "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)" msgstr "" -#: src/guestfs.c:1080 +#: src/guestfs.c:1096 msgid "failed to connect to vmchannel socket" msgstr "" -#: src/guestfs.c:1099 +#: src/guestfs.c:1115 msgid "could not watch qemu stdout" msgstr "" -#: src/guestfs.c:1167 +#: src/guestfs.c:1183 #, c-format msgid "external command failed: %s" msgstr "" -#: src/guestfs.c:1195 +#: src/guestfs.c:1222 +#, c-format +msgid "" +"%s: command failed: If qemu is located on a non-standard path, try setting " +"the LIBGUESTFS_QEMU environment variable." +msgstr "" + +#: src/guestfs.c:1295 msgid "qemu has finished launching already" msgstr "" -#: src/guestfs.c:1200 +#: src/guestfs.c:1300 msgid "qemu has not been launched yet" msgstr "" -#: src/guestfs.c:1213 +#: src/guestfs.c:1313 msgid "guestfs_wait_ready failed, see earlier error messages" msgstr "" -#: src/guestfs.c:1223 +#: src/guestfs.c:1323 msgid "qemu launched and contacted daemon, but state != READY" msgstr "" -#: src/guestfs.c:1234 +#: src/guestfs.c:1334 msgid "no subprocess to kill" msgstr "" -#: src/guestfs.c:1282 +#: src/guestfs.c:1382 #, c-format msgid "guestfs_set_ready: called when in state %d != BUSY" msgstr "" -#: src/guestfs.c:1294 +#: src/guestfs.c:1394 #, c-format msgid "guestfs_set_busy: called when in state %d != READY" msgstr "" -#: src/guestfs.c:1315 +#: src/guestfs.c:1415 #, c-format msgid "guestfs_end_busy: called when in state %d" msgstr "" -#: src/guestfs.c:1404 +#: src/guestfs.c:1504 #, c-format msgid "stdout_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1447 +#: src/guestfs.c:1547 #, c-format msgid "sock_read_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1477 +#: src/guestfs.c:1577 msgid "can't decode length word" msgstr "" -#: src/guestfs.c:1487 +#: src/guestfs.c:1587 #, c-format msgid "received magic signature from guestfsd, but in state %d" msgstr "" -#: src/guestfs.c:1490 +#: src/guestfs.c:1590 #, c-format msgid "received magic signature from guestfsd, but msg size is %d" msgstr "" -#: src/guestfs.c:1515 +#: src/guestfs.c:1615 #, c-format msgid "message length (%u) > maximum possible size (%d)" msgstr "" -#: src/guestfs.c:1548 +#: src/guestfs.c:1648 #, c-format msgid "state %d != BUSY" msgstr "" -#: src/guestfs.c:1588 +#: src/guestfs.c:1688 #, c-format msgid "sock_write_event: internal error: %d != %d" msgstr "" -#: src/guestfs.c:1593 +#: src/guestfs.c:1693 #, c-format msgid "sock_write_event: state %d != BUSY" msgstr "" -#: src/guestfs.c:1697 src/guestfs.c:1720 +#: src/guestfs.c:1797 src/guestfs.c:1820 msgid "remove_handle failed" msgstr "" -#: src/guestfs.c:1708 src/guestfs.c:1731 +#: src/guestfs.c:1808 src/guestfs.c:1831 msgid "add_handle failed" msgstr "" -#: src/guestfs.c:1764 +#: src/guestfs.c:1864 #, c-format msgid "guestfs__send_sync: state %d != BUSY" msgstr "" -#: src/guestfs.c:1772 src/guestfs.c:1952 +#: src/guestfs.c:1872 src/guestfs.c:2052 msgid "guestfs__send_sync: msg_out should be NULL" msgstr "" -#: src/guestfs.c:1794 +#: src/guestfs.c:1894 msgid "xdr_guestfs_message_header failed" msgstr "" -#: src/guestfs.c:1803 +#: src/guestfs.c:1903 msgid "dispatch failed to marshal args" msgstr "" -#: src/guestfs.c:1829 +#: src/guestfs.c:1929 msgid "send failed, see earlier error messages" msgstr "" -#: src/guestfs.c:1944 +#: src/guestfs.c:2044 #, c-format msgid "send_file_chunk_sync: state %d != READY" msgstr "" -#: src/guestfs.c:1975 +#: src/guestfs.c:2075 #, c-format msgid "xdr_guestfs_chunk failed (buf = %p, buflen = %zu)" msgstr "" -#: src/guestfs.c:2000 +#: src/guestfs.c:2100 msgid "send file chunk failed, see earlier error messages" msgstr "" -#: src/guestfs.c:2051 +#: src/guestfs.c:2151 #, c-format msgid "check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n" msgstr "" -#: src/guestfs.c:2089 +#: src/guestfs.c:2189 #, c-format msgid "%s: error in chunked encoding" msgstr "" -#: src/guestfs.c:2113 +#: src/guestfs.c:2213 msgid "write to daemon socket" msgstr "" -#: src/guestfs.c:2160 +#: src/guestfs.c:2260 msgid "failed to parse file chunk" msgstr "" -#: src/guestfs.c:2192 +#: src/guestfs.c:2292 msgid "receive_file_data_sync: reply callback not called\n" msgstr "" -#: src/guestfs.c:2197 +#: src/guestfs.c:2297 msgid "receive_file_data_sync: parse error in reply callback\n" msgstr "" -#: src/guestfs.c:2211 +#: src/guestfs.c:2311 msgid "file receive cancelled by daemon" msgstr "" -#: src/guestfs.c:2246 src/guestfs.c:2304 +#: src/guestfs.c:2346 src/guestfs.c:2404 #, c-format msgid "fd %d is out of range" msgstr "" -#: src/guestfs.c:2254 +#: src/guestfs.c:2354 #, c-format msgid "set of events (0x%x) contains unknown events" msgstr "" -#: src/guestfs.c:2259 +#: src/guestfs.c:2359 msgid "set of events is empty" msgstr "" -#: src/guestfs.c:2266 +#: src/guestfs.c:2366 #, c-format msgid "fd %d is already registered" msgstr "" -#: src/guestfs.c:2271 +#: src/guestfs.c:2371 msgid "callback is NULL" msgstr "" -#: src/guestfs.c:2311 +#: src/guestfs.c:2411 #, c-format msgid "fd %d was not registered" msgstr "" -#: src/guestfs.c:2360 +#: src/guestfs.c:2460 msgid "select_main_loop_run: this cannot be called recursively" msgstr "" diff --git a/po/pl.po b/po/pl.po index e84e1360..d1dd1328 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,7 +6,7 @@ msgstr "" "Project-Id-Version: pl\n" "Report-Msgid-Bugs-To: https://bugzilla.redhat.com/enter_bug.cgi?" "component=libguestfs&product=Virtualization+Tools\n" -"POT-Creation-Date: 2009-06-22 10:02+0100\n" +"POT-Creation-Date: 2009-06-24 12:47+0100\n" "PO-Revision-Date: 2009-06-22 21:16+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" @@ -35,13 +35,15 @@ msgid "use '%s filename' to edit a file\n" msgstr "użyj \"%s nazwapliku\", aby zmodyfikować plik\n" #: fish/fish.c:88 -#, c-format +#, fuzzy, c-format msgid "" "guestfish: guest filesystem shell\n" "guestfish lets you edit virtual machine filesystems\n" "Copyright (C) 2009 Red Hat Inc.\n" "Usage:\n" " guestfish [--options] cmd [: cmd : cmd ...]\n" +" guestfish -i libvirt-domain\n" +" guestfish -i disk-image(s)\n" "or for interactive use:\n" " guestfish\n" "or from a shell script:\n" @@ -54,6 +56,8 @@ msgid "" " -h|--cmd-help cmd Display detailed help on 'cmd'\n" " -a|--add image Add image\n" " -D|--no-dest-paths Don't tab-complete paths from guest fs\n" +" -f|--file file Read commands from file\n" +" -i|--inspector Run virt-inspector to get disk mountpoints\n" " -m|--mount dev[:mnt] Mount dev on mnt (if omitted, /)\n" " -n|--no-sync Don't autosync\n" " -r|--ro Mount read-only\n" @@ -86,17 +90,37 @@ msgstr "" " -V|--version Wyświetla wersję i kończy pracę\n" "Aby dowiedzieć się więcej, zobacz stronę podręcznika guestfish(1).\n" -#: fish/fish.c:143 +#: fish/fish.c:149 #, c-format msgid "guestfs_create: failed to create handle\n" msgstr "guestfs_create: utworzenie programu obsługi nie powiodło się\n" -#: fish/fish.c:234 +#: fish/fish.c:194 +#, c-format +msgid "guestfish: only one -f parameter can be given\n" +msgstr "" + +#: fish/fish.c:252 #, c-format msgid "guestfish: unexpected command line option 0x%x\n" msgstr "guestfish: nieoczekiwane polecenie wiersza poleceń 0x%x\n" -#: fish/fish.c:380 +#: fish/fish.c:264 +#, c-format +msgid "guestfish: cannot use -i option with -a or -m\n" +msgstr "" + +#: fish/fish.c:268 +#, c-format +msgid "guestfish -i requires a libvirt domain or path(s) to disk image(s)\n" +msgstr "" + +#: fish/fish.c:276 +#, c-format +msgid "guestfish: virt-inspector command too long for fixed-size buffer\n" +msgstr "" + +#: fish/fish.c:456 #, c-format msgid "" "\n" @@ -115,65 +139,65 @@ msgstr "" " \"quit\", aby zakończyć powłokę\n" "\n" -#: fish/fish.c:457 +#: fish/fish.c:533 #, c-format msgid "guestfish: unterminated double quote\n" msgstr "guestfish: niezakończony podwójny cudzysłów\n" -#: fish/fish.c:462 fish/fish.c:477 +#: fish/fish.c:538 fish/fish.c:553 #, c-format msgid "guestfish: command arguments not separated by whitespace\n" msgstr "guestfish: parametry poleceń nie są oddzielone spacjami\n" -#: fish/fish.c:472 +#: fish/fish.c:548 #, c-format msgid "guestfish: unterminated single quote\n" msgstr "guestfish: niezakończony pojedynczy cudzysłów\n" -#: fish/fish.c:513 +#: fish/fish.c:589 #, c-format msgid "guestfish: internal error parsing string at '%s'\n" msgstr "guestfish: wewnętrzny błąd analizowania łańcucha \"%s\"\n" -#: fish/fish.c:526 +#: fish/fish.c:602 #, c-format msgid "guestfish: too many arguments\n" msgstr "guestfish: za dużo parametrów\n" -#: fish/fish.c:553 +#: fish/fish.c:629 #, c-format msgid "guestfish: empty command on command line\n" msgstr "guestfish: puste polecenie wiersza poleceń\n" -#: fish/fish.c:614 +#: fish/fish.c:690 msgid "display a list of commands or help on a command" msgstr "wyświetla listę poleceń lub pomoc polecenia" -#: fish/fish.c:616 +#: fish/fish.c:692 msgid "quit guestfish" msgstr "kończy pracę guestfish" -#: fish/fish.c:619 +#: fish/fish.c:695 msgid "allocate an image" msgstr "przydziela obraz" -#: fish/fish.c:621 +#: fish/fish.c:697 msgid "display a line of text" msgstr "wyświetla wiersz tekstu" -#: fish/fish.c:623 +#: fish/fish.c:699 msgid "edit a file in the image" msgstr "modyfikuje plik w obrazie" -#: fish/fish.c:625 +#: fish/fish.c:701 msgid "local change directory" msgstr "zmienia lokalny folder" -#: fish/fish.c:627 +#: fish/fish.c:703 msgid "expand wildcards in command" msgstr "rozwija wieloznaczniki w poleceniach" -#: fish/fish.c:639 +#: fish/fish.c:715 #, c-format msgid "" "alloc - allocate an image\n" @@ -209,7 +233,7 @@ msgstr "" " G lub GB liczba gigabajtów\n" " sektory liczba 512 bajtowych sektorów\n" -#: fish/fish.c:655 +#: fish/fish.c:731 #, c-format msgid "" "echo - display a line of text\n" @@ -222,7 +246,7 @@ msgstr "" "\n" " Wyświetla ostatnie parametry w terminalu.\n" -#: fish/fish.c:662 +#: fish/fish.c:738 #, c-format msgid "" "edit - edit a file in the image\n" @@ -254,7 +278,7 @@ msgstr "" " UWAGA: nie będzie działało poprawnie dla dużych plików\n" " (> 2 MB) lub plików binarnych zawierających \\0 bajtów.\n" -#: fish/fish.c:676 +#: fish/fish.c:752 #, c-format msgid "" "lcd - local change directory\n" @@ -271,7 +295,7 @@ msgstr "" " przydatne, jeśli chcesz pobrać pliki do konkretnego\n" " miejsca.\n" -#: fish/fish.c:683 +#: fish/fish.c:759 #, c-format msgid "" "glob - expand wildcards in command\n" @@ -289,7 +313,7 @@ msgstr "" " Zauważ, że polecenie jest wykonywane raz dla\n" " każdego rozwiniętego parametru.\n" -#: fish/fish.c:690 +#: fish/fish.c:766 #, c-format msgid "" "help - display a list of commands or help on a command\n" @@ -300,7 +324,7 @@ msgstr "" " help polecenie\n" " help\n" -#: fish/fish.c:696 +#: fish/fish.c:772 #, c-format msgid "" "quit - quit guestfish\n" @@ -309,257 +333,264 @@ msgstr "" "quit - kończy pracę guestfish\n" " quit\n" -#: fish/fish.c:699 +#: fish/fish.c:775 #, c-format msgid "%s: command not known, use -h to list all commands\n" msgstr "" "%s: nieznane polecenie, użyj -h, aby wyświetlić listę wszystkich poleceń\n" -#: src/guestfs.c:288 +#: src/guestfs.c:290 #, c-format msgid "guestfs_close: called twice on the same handle\n" msgstr "guestfs_close: wywołano dwa razy w tym samym programie obsługującym\n" -#: src/guestfs.c:375 +#: src/guestfs.c:379 #, c-format msgid "libguestfs: error: %s\n" msgstr "libguestfs: błąd: %s\n" -#: src/guestfs.c:616 +#: src/guestfs.c:620 msgid "command line cannot be altered after qemu subprocess launched" msgstr "" "wiersz poleceń nie może zostać zmieniony po uruchomieniu podprocesu QEMU" -#: src/guestfs.c:630 +#: src/guestfs.c:634 msgid "guestfs_config: parameter must begin with '-' character" msgstr "guestfs_config: parametr musi zaczynać się od znaku \"-\"" -#: src/guestfs.c:645 +#: src/guestfs.c:648 #, c-format msgid "guestfs_config: parameter '%s' isn't allowed" msgstr "guestfs_config: parametr \"%s\" nie jest dozwolony" -#: src/guestfs.c:665 src/guestfs.c:686 src/guestfs.c:704 +#: src/guestfs.c:668 src/guestfs.c:690 src/guestfs.c:708 msgid "filename cannot contain ',' (comma) character" msgstr "nazwa pliku nie może zawierać znaku \",\" (przecinka)" -#: src/guestfs.c:771 +#: src/guestfs.c:777 msgid "you must call guestfs_add_drive before guestfs_launch" msgstr "należy wywołać guestfs_add_drive przed guestfs_launch" -#: src/guestfs.c:776 +#: src/guestfs.c:782 msgid "qemu has already been launched" msgstr "QEMU zostało już uruchomione" -#: src/guestfs.c:784 +#: src/guestfs.c:790 #, c-format msgid "%s: cannot create temporary directory" msgstr "%s: nie można utworzyć folderu tymczasowego" -#: src/guestfs.c:874 +#: src/guestfs.c:880 #, c-format msgid "cannot find %s or %s on LIBGUESTFS_PATH (current path = %s)" msgstr "nie można znaleźć %s lub %s w LIBGUESTFS_PATH (bieżąca ścieżka = %s)" -#: src/guestfs.c:1080 +#: src/guestfs.c:1096 msgid "failed to connect to vmchannel socket" msgstr "połączenie się z gniazdem kanału maszyny wirtualnej nie powiodło się" -#: src/guestfs.c:1099 +#: src/guestfs.c:1115 msgid "could not watch qemu stdout" msgstr "nie można obserwować standardowego wyjścia QEMU" -#: src/guestfs.c:1167 +#: src/guestfs.c:1183 #, c-format msgid "external command failed: %s" msgstr "zewnętrzne polecenie nie powiodło się: %s" -#: src/guestfs.c:1195 +#: src/guestfs.c:1222 +#, c-format +msgid "" +"%s: command failed: If qemu is located on a non-standard path, try setting " +"the LIBGUESTFS_QEMU environment variable." +msgstr "" + +#: src/guestfs.c:1295 msgid "qemu has finished launching already" msgstr "QEMU zakończyło już uruchamianie" -#: src/guestfs.c:1200 +#: src/guestfs.c:1300 msgid "qemu has not been launched yet" msgstr "QEMU nie zostało jeszcze uruchomione" -#: src/guestfs.c:1213 +#: src/guestfs.c:1313 msgid "guestfs_wait_ready failed, see earlier error messages" msgstr "" "guestfs_wait_ready nie powiodło się, zobacz wcześniejsze komunikaty błędów" -#: src/guestfs.c:1223 +#: src/guestfs.c:1323 msgid "qemu launched and contacted daemon, but state != READY" msgstr "" "QEMU zostało uruchomione i skontaktowano się z demonem, ale stan != GOTOWY" -#: src/guestfs.c:1234 +#: src/guestfs.c:1334 msgid "no subprocess to kill" msgstr "brak podprocesu do zniszczenia" -#: src/guestfs.c:1282 +#: src/guestfs.c:1382 #, c-format msgid "guestfs_set_ready: called when in state %d != BUSY" msgstr "guestfs_set_ready: wywołano, kiedy w stanie %d != ZAJĘTY" -#: src/guestfs.c:1294 +#: src/guestfs.c:1394 #, c-format msgid "guestfs_set_busy: called when in state %d != READY" msgstr "guestfs_set_busy: wywołano, kiedy w stanie %d != GOTOWY" -#: src/guestfs.c:1315 +#: src/guestfs.c:1415 #, c-format msgid "guestfs_end_busy: called when in state %d" msgstr "guestfs_end_busy: wywołano, kiedy w stanie %d" -#: src/guestfs.c:1404 +#: src/guestfs.c:1504 #, c-format msgid "stdout_event: internal error: %d != %d" msgstr "stdout_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1447 +#: src/guestfs.c:1547 #, c-format msgid "sock_read_event: internal error: %d != %d" msgstr "sock_read_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1477 +#: src/guestfs.c:1577 msgid "can't decode length word" msgstr "nie można dekodować długości słowa" -#: src/guestfs.c:1487 +#: src/guestfs.c:1587 #, c-format msgid "received magic signature from guestfsd, but in state %d" msgstr "otrzymano podpis magic z guestfsd, ale w stanie %d" -#: src/guestfs.c:1490 +#: src/guestfs.c:1590 #, c-format msgid "received magic signature from guestfsd, but msg size is %d" msgstr "otrzymano podpis magic z guestfsd, ale rozmiar komunikatu to %d" -#: src/guestfs.c:1515 +#: src/guestfs.c:1615 #, c-format msgid "message length (%u) > maximum possible size (%d)" msgstr "długość komunikatu (%u) > maksymalny możliwy rozmiar (%d)" -#: src/guestfs.c:1548 +#: src/guestfs.c:1648 #, c-format msgid "state %d != BUSY" msgstr "stan %d != ZAJĘTY" -#: src/guestfs.c:1588 +#: src/guestfs.c:1688 #, c-format msgid "sock_write_event: internal error: %d != %d" msgstr "sock_write_event: wewnętrzny błąd: %d != %d" -#: src/guestfs.c:1593 +#: src/guestfs.c:1693 #, c-format msgid "sock_write_event: state %d != BUSY" msgstr "sock_write_event: stan %d != ZAJĘTY" -#: src/guestfs.c:1697 src/guestfs.c:1720 +#: src/guestfs.c:1797 src/guestfs.c:1820 msgid "remove_handle failed" msgstr "remove_handle nie powiodło się" -#: src/guestfs.c:1708 src/guestfs.c:1731 +#: src/guestfs.c:1808 src/guestfs.c:1831 msgid "add_handle failed" msgstr "add_handle nie powiodło się" -#: src/guestfs.c:1764 +#: src/guestfs.c:1864 #, c-format msgid "guestfs__send_sync: state %d != BUSY" msgstr "guestfs__send_sync: stan %d != ZAJĘTY" -#: src/guestfs.c:1772 src/guestfs.c:1952 +#: src/guestfs.c:1872 src/guestfs.c:2052 msgid "guestfs__send_sync: msg_out should be NULL" msgstr "guestfs__send_sync: msg_out powinno być PUSTE" -#: src/guestfs.c:1794 +#: src/guestfs.c:1894 msgid "xdr_guestfs_message_header failed" msgstr "xdr_guestfs_message_header nie powiodło się" -#: src/guestfs.c:1803 +#: src/guestfs.c:1903 msgid "dispatch failed to marshal args" msgstr "rozdzielenie parametrów marszałka nie powiodło się" -#: src/guestfs.c:1829 +#: src/guestfs.c:1929 msgid "send failed, see earlier error messages" msgstr "wysłanie nie powiodło się, zobacz wcześniejsze komunikaty błędów" -#: src/guestfs.c:1944 +#: src/guestfs.c:2044 #, c-format msgid "send_file_chunk_sync: state %d != READY" msgstr "send_file_chunk_sync: stan %d != GOTOWY" -#: src/guestfs.c:1975 +#: src/guestfs.c:2075 #, c-format msgid "xdr_guestfs_chunk failed (buf = %p, buflen = %zu)" msgstr "xdr_guestfs_chunk nie powiodło się (bufor = %p, długość bufora = %zu)" -#: src/guestfs.c:2000 +#: src/guestfs.c:2100 msgid "send file chunk failed, see earlier error messages" msgstr "" "wysłanie fragmentu pliku nie powiodło się, zobacz wcześniejsze komunikaty " "błędów" -#: src/guestfs.c:2051 +#: src/guestfs.c:2151 #, c-format msgid "check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n" msgstr "" "check_for_daemon_cancellation: odczytano 0x%x z demona, oczekiwano 0x%x\n" -#: src/guestfs.c:2089 +#: src/guestfs.c:2189 #, c-format msgid "%s: error in chunked encoding" msgstr "%s: błąd w kodowaniu fragmentu" -#: src/guestfs.c:2113 +#: src/guestfs.c:2213 msgid "write to daemon socket" msgstr "zapisz do gniazda demona" -#: src/guestfs.c:2160 +#: src/guestfs.c:2260 msgid "failed to parse file chunk" msgstr "przeanalizowanie fragmentu pliku nie powiodło się" -#: src/guestfs.c:2192 +#: src/guestfs.c:2292 msgid "receive_file_data_sync: reply callback not called\n" msgstr "receive_file_data_sync: nie wywołano odpowiedzi wywołania zwrotnego\n" -#: src/guestfs.c:2197 +#: src/guestfs.c:2297 msgid "receive_file_data_sync: parse error in reply callback\n" msgstr "" "receive_file_data_sync: błąd analizy w odpowiedzi wywołania zwrotnego\n" -#: src/guestfs.c:2211 +#: src/guestfs.c:2311 msgid "file receive cancelled by daemon" msgstr "otrzymanie pliku zostało anulowane przez demona" -#: src/guestfs.c:2246 src/guestfs.c:2304 +#: src/guestfs.c:2346 src/guestfs.c:2404 #, c-format msgid "fd %d is out of range" msgstr "fd %d jest spoza zakresu" -#: src/guestfs.c:2254 +#: src/guestfs.c:2354 #, c-format msgid "set of events (0x%x) contains unknown events" msgstr "zestaw zdarzeń (0x%x) zawiera nieznane zdarzenia" -#: src/guestfs.c:2259 +#: src/guestfs.c:2359 msgid "set of events is empty" msgstr "zestaw zdarzeń jest pusty" -#: src/guestfs.c:2266 +#: src/guestfs.c:2366 #, c-format msgid "fd %d is already registered" msgstr "fd %d jest już zarejestrowane" -#: src/guestfs.c:2271 +#: src/guestfs.c:2371 msgid "callback is NULL" msgstr "wywołanie zwrotne jest PUSTE" -#: src/guestfs.c:2311 +#: src/guestfs.c:2411 #, c-format msgid "fd %d was not registered" msgstr "fd %d nie zostało zarejestrowane" -#: src/guestfs.c:2360 +#: src/guestfs.c:2460 msgid "select_main_loop_run: this cannot be called recursively" msgstr "select_main_loop_run: nie może zostać wywołane rekursywnie" diff --git a/src/Makefile.am b/src/Makefile.am index 6b1088ca..06d4522c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,7 +17,8 @@ EXTRA_DIST = guestfs_protocol.x \ guestfs_protocol.c \ - guestfs_protocol.h + guestfs_protocol.h \ + MAX_PROC_NR EXTRA_DIST += generator.ml -- cgit From 53f5ea28ffe26c112e51ebdef6d46c25919ced4f Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 15:35:59 +0100 Subject: Fix permissions on generated scripts in the appliance/ directory. --- appliance/Makefile.am | 10 ---------- configure.ac | 17 +++++++++++------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 74157b08..c06d7ae9 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -92,16 +92,6 @@ $(SUPERMINIMG): supermin.incfiles supermin-make.sh endif -# This should rebuild the scripts if the input files change, although -# it doesn't always seem to work. -%.sh: %.sh.in - cd .. && ./config.status appliance/$@ - chmod +x $@ - -libguestfs-supermin-helper: libguestfs-supermin-helper.in - cd .. && ./config.status appliance/$@ - chmod +x $@ - #---------------------------------------------------------------------- # Extra rules for testing the appliance. diff --git a/configure.ac b/configure.ac index 42f0127a..386bec57 100644 --- a/configure.ac +++ b/configure.ac @@ -506,12 +506,20 @@ AC_CONFIG_SUBDIRS([daemon]) dnl Produce output files. AC_CONFIG_HEADERS([config.h]) +dnl http://www.mail-archive.com/automake@gnu.org/msg10204.html +AC_CONFIG_FILES([appliance/make.sh], + [chmod +x appliance/make.sh]) +AC_CONFIG_FILES([appliance/update.sh], + [chmod +x appliance/update.sh]) +AC_CONFIG_FILES([appliance/supermin-split.sh], + [chmod +x appliance/supermin-split.sh]) +AC_CONFIG_FILES([appliance/supermin-make.sh], + [chmod +x appliance/supermin-make.sh]) +AC_CONFIG_FILES([appliance/libguestfs-supermin-helper], + [chmod +x appliance/libguestfs-supermin-helper]) AC_CONFIG_FILES([Makefile src/Makefile fish/Makefile po/Makefile.in examples/Makefile appliance/Makefile - appliance/make.sh appliance/update.sh - appliance/supermin-split.sh appliance/supermin-make.sh - appliance/libguestfs-supermin-helper images/Makefile capitests/Makefile regressions/Makefile @@ -526,9 +534,6 @@ AC_CONFIG_FILES([Makefile ocaml/META perl/Makefile.PL]) AC_OUTPUT -dnl WTF? -chmod +x appliance/*.sh appliance/libguestfs-supermin-helper - dnl Produce summary. echo echo -- cgit From 1f6bc26fc0967c6e4ae4a4514d9734288839c0fd Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 15:36:58 +0100 Subject: Fix libvirt integration in virt-inspector. --- README | 3 +++ inspector/virt-inspector.pl | 13 +++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README b/README index 3dea215c..e376cc71 100644 --- a/README +++ b/README @@ -73,6 +73,9 @@ bindings - (Optional) GHC if you want to build the Haskell bindings +- (Optional) Perl XML::XPath, Sys::Virt modules (for libvirt support +in virt-inspector). + Running ./configure will check you have all the requirements installed on your machine. diff --git a/inspector/virt-inspector.pl b/inspector/virt-inspector.pl index 717ccb61..c645cbdc 100755 --- a/inspector/virt-inspector.pl +++ b/inspector/virt-inspector.pl @@ -27,6 +27,8 @@ use File::Temp qw/tempdir/; # Optional: eval "use Sys::Virt;"; +eval "use XML::XPath;"; +eval "use XML::XPath::XMLParser;"; =encoding utf8 @@ -231,14 +233,9 @@ if (-e $ARGV[0]) { # Get the names of the image(s). my $xml = $dom->get_xml_description (); - my $p = new XML::XPath::XMLParser (xml => $xml); - my $disks = $p->find ("//devices/disk"); - print "disks:\n"; - foreach ($disks->get_nodelist) { - print XML::XPath::XMLParser::as_string($_); - } - - die "XXX" + my $p = XML::XPath->new (xml => $xml); + my @disks = $p->findnodes ('//devices/disk/source/@dev'); + @images = map { $_->getData } @disks; } # We've now got the list of @images, so feed them to libguestfs. -- cgit From af0cfda7e4942c14c9db7304962f8471ccad170f Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 17:16:17 +0100 Subject: Version 1.0.53. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 386bec57..b8b8e1e9 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -AC_INIT([libguestfs],[1.0.52]) +AC_INIT([libguestfs],[1.0.53]) AM_INIT_AUTOMAKE AC_CONFIG_MACRO_DIR([m4]) -- cgit From 8228eec99045ae720d8ef35851aa8c278f6b4e5c Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 24 Jun 2009 18:22:37 +0100 Subject: Add mkdtemp command. --- daemon/dir.c | 30 ++++++++++++++++++++++++++++++ src/generator.ml | 22 ++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/daemon/dir.c b/daemon/dir.c index 83536ef5..753323d1 100644 --- a/daemon/dir.c +++ b/daemon/dir.c @@ -201,3 +201,33 @@ do_is_dir (char *path) return S_ISDIR (buf.st_mode); } + +char * +do_mkdtemp (char *template) +{ + char *r; + + NEED_ROOT (NULL); + ABS_PATH (template, NULL); + + CHROOT_IN; + r = mkdtemp (template); + CHROOT_OUT; + + if (r == NULL) { + reply_with_perror ("mkdtemp: %s", template); + return NULL; + } + + /* The caller will free template AND try to free the return value, + * so we must make a copy here. + */ + if (r == template) { + r = strdup (template); + if (r == NULL) { + reply_with_perror ("strdup"); + return NULL; + } + } + return r; +} diff --git a/src/generator.ml b/src/generator.ml index 51d32357..e378395a 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -2442,6 +2442,28 @@ containing C. It is an interface to the L program. See that manual page for more details."); + ("mkdtemp", (RString "dir", [String "template"]), 117, [], + [InitBasicFS, Always, TestRun ( + [["mkdir"; "/tmp"]; + ["mkdtemp"; "/tmp/tmpXXXXXX"]])], + "create a temporary directory", + "\ +This command creates a temporary directory. The +C