Richard W.M. Jones
2014-May-23 10:52 UTC
[Libguestfs] [PATCH 0/2] fuse: Add guestmount --fd option (RHBZ#1100498).
Add guestmount --fd option for RFE. See: https://bugzilla.redhat.com/show_bug.cgi?id=1100498
Richard W.M. Jones
2014-May-23 10:52 UTC
[Libguestfs] [PATCH 1/2] fuse: Add guestmount --fd option (RHBZ#1100498).
This implements the guestmount --fd option to allow you to run guestmount captive under another process (typically using `guestmount --fd=<FD> --no-fork'). See: https://bugzilla.redhat.com/show_bug.cgi?id=1100498 --- fuse/guestmount.c | 37 ++++++++++++++++++++++++++++++++++++- fuse/guestmount.pod | 7 +++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/fuse/guestmount.c b/fuse/guestmount.c index 4f98471..88c6024 100644 --- a/fuse/guestmount.c +++ b/fuse/guestmount.c @@ -41,6 +41,7 @@ #include "options.h" +static int write_pipe_fd (int fd); static int write_pid_file (const char *pid_file, pid_t pid); #ifndef HAVE_FUSE_OPT_ADD_OPT_ESCAPED @@ -111,6 +112,7 @@ usage (int status) " --dir-cache-timeout Set readdir cache timeout (default 5 sec)\n" " -d|--domain guest Add disks from libvirt guest\n" " --echo-keys Don't turn off echo for passphrases\n" + " --fd=FD Write to pipe FD when mountpoint is ready\n" " --format[=raw|..] Force disk format for -a option\n" " --fuse-help Display extra FUSE options\n" " -i|--inspector Automatically mount filesystems\n" @@ -155,6 +157,7 @@ main (int argc, char *argv[]) { "dir-cache-timeout", 1, 0, 0 }, { "domain", 1, 0, 'd' }, { "echo-keys", 0, 0, 0 }, + { "fd", 1, 0, 0 }, { "format", 2, 0, 0 }, { "fuse-help", 0, 0, 0 }, { "help", 0, 0, HELP_OPTION }, @@ -190,6 +193,7 @@ main (int argc, char *argv[]) int do_fork = 1; char *fuse_options = NULL; char *pid_file = NULL; + int pipe_fd = -1; struct guestfs_mount_local_argv optargs; @@ -237,6 +241,12 @@ main (int argc, char *argv[]) pid_file = optarg; } else if (STREQ (long_options[option_index].name, "no-fork")) { do_fork = 0; + } else if (STREQ (long_options[option_index].name, "fd")) { + if (sscanf (optarg, "%d", &pipe_fd) != 1 || pipe_fd < 0) { + fprintf (stderr, _("%s: unable to parse --fd option value: %s\n"), + program_name, optarg); + exit (EXIT_FAILURE); + } } else { fprintf (stderr, _("%s: unknown long option: %s (%d)\n"), program_name, long_options[option_index].name, option_index); @@ -413,6 +423,8 @@ main (int argc, char *argv[]) if (pid != 0) { /* parent */ if (write_pid_file (pid_file, pid) == -1) _exit (EXIT_FAILURE); + if (write_pipe_fd (pipe_fd) == -1) + _exit (EXIT_FAILURE); _exit (EXIT_SUCCESS); } @@ -435,9 +447,11 @@ main (int argc, char *argv[]) } } else { - /* not forking, write PID file anyway */ + /* not forking, write PID file and pipe FD anyway */ if (write_pid_file (pid_file, getpid ()) == -1) exit (EXIT_FAILURE); + if (write_pipe_fd (pipe_fd) == -1) + _exit (EXIT_FAILURE); } /* At the last minute, remove the libguestfs error handler. In code @@ -487,3 +501,24 @@ write_pid_file (const char *pid_file, pid_t pid) return 0; } + +static int +write_pipe_fd (int fd) +{ + char c = 0; + + if (fd < 0) + return 0; + + if (write (fd, &c, 1) != 1) { + perror ("write (--fd option)"); + return -1; + } + + if (close (fd) == -1) { + perror ("close (--fd option)"); + return -1; + } + + return 0; +} diff --git a/fuse/guestmount.pod b/fuse/guestmount.pod index 05a3c1d..7eddc1c 100644 --- a/fuse/guestmount.pod +++ b/fuse/guestmount.pod @@ -212,6 +212,13 @@ echoing off so you cannot see what you are typing. If you are not worried about Tempest attacks and there is no one else in the room you can specify this flag to see what you are typing. +=item B<--fd=FD> + +Specify a pipe or eventfd file descriptor. When the mountpoint is +ready to be used, guestmount writes a single byte to this file +descriptor. This can be used in conjunction with I<--no-fork> in +order to run guestmount captive under another process. + =item B<--format=raw|qcow2|..> =item B<--format> -- 1.9.0
Richard W.M. Jones
2014-May-23 10:52 UTC
[Libguestfs] [PATCH 2/2] fuse: Add a test of guestmount --fd option (RHBZ#1100498).
--- .gitignore | 1 + fuse/Makefile.am | 22 ++++++- fuse/test-guestmount-fd.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++ po/POTFILES | 1 + 4 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 fuse/test-guestmount-fd.c diff --git a/.gitignore b/.gitignore index 16eae54..1b9ce89 100644 --- a/.gitignore +++ b/.gitignore @@ -181,6 +181,7 @@ Makefile.in /fuse/guestunmount.1 /fuse/stamp-guestmount.pod /fuse/stamp-guestunmount.pod +/fuse/test-guestmount-fd /fuse/test-guestunmount-fd /generator/.depend /generator/files-generated.txt diff --git a/fuse/Makefile.am b/fuse/Makefile.am index 3fd64a2..0286338 100644 --- a/fuse/Makefile.am +++ b/fuse/Makefile.am @@ -132,14 +132,32 @@ TESTS = \ if ENABLE_APPLIANCE TESTS += \ test-fuse.sh \ - test-fuse-umount-race.sh + test-fuse-umount-race.sh \ + test-guestmount-fd endif ENABLE_APPLIANCE TESTS_ENVIRONMENT = \ top_builddir=.. \ $(top_builddir)/run --test -check_PROGRAMS = test-guestunmount-fd +check_PROGRAMS = test-guestmount-fd test-guestunmount-fd + +test_guestmount_fd_SOURCES = \ + test-guestmount-fd.c + +test_guestmount_fd_CPPFLAGS = \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + -I$(srcdir)/../gnulib/lib -I../gnulib/lib + +test_guestmount_fd_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) + +test_guestmount_fd_LDADD = \ + $(top_builddir)/src/libutils.la \ + $(top_builddir)/src/libguestfs.la \ + $(LIBXML2_LIBS) \ + $(LIBVIRT_LIBS) \ + ../gnulib/lib/libgnu.la test_guestunmount_fd_SOURCES = \ test-guestunmount-fd.c diff --git a/fuse/test-guestmount-fd.c b/fuse/test-guestmount-fd.c new file mode 100644 index 0000000..c143093 --- /dev/null +++ b/fuse/test-guestmount-fd.c @@ -0,0 +1,162 @@ +/* Test guestmount --fd option. + * Copyright (C) 2014 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "ignore-value.h" + +#include "guestfs.h" +#include "guestfs-internal-frontend.h" + +#define GUESTMOUNT_BINARY "./guestmount" +#define GUESTUNMOUNT_BINARY "./guestunmount" +#define TEST_IMAGE "../tests/guests/fedora.img" +#define MOUNTPOINT "test-guestmount-fd.d" +#define TEST_FILE MOUNTPOINT "/etc/fstab" + +int +main (int argc, char *argv[]) +{ + char *skip; + int pipefd[2]; + pid_t pid; + char c; + int r, status; + + /* Allow the test to be skipped. */ + skip = getenv ("SKIP_TEST_GUESTMOUNT_FD"); + if (skip && STREQ (skip, "1")) { + fprintf (stderr, "%s: test skipped because environment variable set.\n", + program_name); + exit (77); + } + + /* Skip the test if the test image can't be found. */ + if (access (TEST_IMAGE, R_OK) == -1) { + perror (TEST_IMAGE); + exit (77); + } + + /* Create the pipe. */ + if (pipe (pipefd) == -1) { + perror ("pipe"); + exit (EXIT_FAILURE); + } + + /* Create the mount point. */ + ignore_value (rmdir (MOUNTPOINT)); + if (mkdir (MOUNTPOINT, 0700) == -1) { + perror ("mkdir: " MOUNTPOINT); + exit (EXIT_FAILURE); + } + + /* Create the guestmount subprocess. */ + pid = fork (); + if (pid == -1) { + perror ("fork"); + exit (EXIT_FAILURE); + } + + if (pid == 0) { /* child - guestmount */ + char fd_str[64]; + + close (pipefd[0]); + + snprintf (fd_str, sizeof fd_str, "%d", pipefd[1]); + + execlp (GUESTMOUNT_BINARY, + "guestmount", + "--fd", fd_str, "--no-fork", + "--ro", "-a", TEST_IMAGE, "-i", MOUNTPOINT, NULL); + perror ("execlp"); + _exit (EXIT_FAILURE); + } + + /* Parent continues. */ + close (pipefd[1]); + + /* Wait for guestmount to start up. */ + r = read (pipefd[0], &c, 1); + if (r == -1) { + perror ("read (pipefd)"); + ignore_value (rmdir (MOUNTPOINT)); + exit (EXIT_FAILURE); + } + if (r == 0) { + fprintf (stderr, "%s: unexpected end of file on pipe fd.\n", + program_name); + ignore_value (rmdir (MOUNTPOINT)); + exit (EXIT_FAILURE); + } + + /* Check that the test image was mounted. */ + if (access (TEST_FILE, R_OK) == -1) { + fprintf (stderr, "%s: test failed because test image is not mounted and ready.", + program_name); + ignore_value (rmdir (MOUNTPOINT)); + exit (EXIT_FAILURE); + } + + /* Unmount it. */ + r = system (GUESTUNMOUNT_BINARY " " MOUNTPOINT); + if (r != 0) { + char status_string[80]; + + fprintf (stderr, "%s: test failed: %s\n", program_name, + guestfs___exit_status_to_string (r, GUESTUNMOUNT_BINARY, + status_string, + sizeof status_string)); + ignore_value (rmdir (MOUNTPOINT)); + exit (EXIT_FAILURE); + } + + close (pipefd[0]); + + /* Wait for guestmount to exit, and check it exits cleanly. */ + r = waitpid (pid, &status, 0); + if (r == -1) { + perror ("waitpid"); + ignore_value (rmdir (MOUNTPOINT)); + exit (EXIT_FAILURE); + } + if (!WIFEXITED (status) || WEXITSTATUS (status) != 0) { + char status_string[80]; + + fprintf (stderr, "%s: test failed: %s\n", + program_name, + guestfs___exit_status_to_string (status, GUESTMOUNT_BINARY, + status_string, + sizeof status_string)); + ignore_value (rmdir (MOUNTPOINT)); + exit (EXIT_FAILURE); + } + + ignore_value (rmdir (MOUNTPOINT)); + + exit (EXIT_SUCCESS); +} diff --git a/po/POTFILES b/po/POTFILES index 7d4a0c8..33d2a94 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -162,6 +162,7 @@ fish/uri.c format/format.c fuse/guestmount.c fuse/guestunmount.c +fuse/test-guestmount-fd.c fuse/test-guestunmount-fd.c gobject/src/optargs-add_domain.c gobject/src/optargs-add_drive.c -- 1.9.0