diff options
author | Richard W.M. Jones <rjones@redhat.com> | 2009-06-27 15:00:48 +0200 |
---|---|---|
committer | Richard W.M. Jones <rjones@redhat.com> | 2009-06-28 22:22:30 +0100 |
commit | 88da5cf8a32e683ed1d78419fcde609a389a2f65 (patch) | |
tree | d5ba4eace6c03ef8374c2d9394711ae724f744cf | |
parent | d164ae963297a99e2222bc32b11928c1635c45d8 (diff) | |
download | libguestfs-88da5cf8a32e683ed1d78419fcde609a389a2f65.tar.gz libguestfs-88da5cf8a32e683ed1d78419fcde609a389a2f65.tar.xz libguestfs-88da5cf8a32e683ed1d78419fcde609a389a2f65.zip |
Guestfish pipes.
-rw-r--r-- | fish/fish.c | 86 | ||||
-rw-r--r-- | fish/fish.h | 2 | ||||
-rw-r--r-- | fish/glob.c | 2 | ||||
-rw-r--r-- | guestfish.pod | 26 |
4 files changed, 99 insertions, 17 deletions
diff --git a/fish/fish.c b/fish/fish.c index 5e8a6e9e..5b0a0656 100644 --- a/fish/fish.c +++ b/fish/fish.c @@ -29,6 +29,8 @@ #include <signal.h> #include <assert.h> #include <ctype.h> +#include <sys/types.h> +#include <sys/wait.h> #ifdef HAVE_LIBREADLINE #include <readline/readline.h> @@ -138,9 +140,15 @@ main (int argc, char *argv[]) struct mp *mp; char *p, *file = NULL; int c, inspector = 0; + struct sigaction sa; initialize_readline (); + memset (&sa, 0, sizeof sa); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_RESTART; + sigaction (SIGPIPE, &sa, NULL); + /* guestfs_create is meant to be a lightweight operation, so * it's OK to do it early here. */ @@ -462,6 +470,8 @@ script (int prompt) "\n")); while (!quit) { + char *pipe = NULL; + exit_on_error = global_exit_on_error; buf = rl_gets (prompt); @@ -522,9 +532,8 @@ script (int prompt) /* Get the parameters. */ while (*p && i < sizeof argv / sizeof argv[0]) { - /* Parameters which start with quotes or square brackets - * are treated specially. Bare parameters are delimited - * by whitespace. + /* Parameters which start with quotes or pipes are treated + * specially. Bare parameters are delimited by whitespace. */ if (*p == '"') { p++; @@ -556,6 +565,10 @@ script (int prompt) } p[len] = '\0'; pend = p[len+1] ? &p[len+2] : &p[len+1]; + } else if (*p == '|') { + *p = '\0'; + pipe = p+1; + continue; /* } else if (*p == '[') { int c = 1; @@ -607,7 +620,7 @@ script (int prompt) argv[i] = NULL; got_command: - if (issue_command (cmd, argv) == -1) { + if (issue_command (cmd, argv, pipe) == -1) { if (exit_on_error) exit (1); } @@ -636,18 +649,51 @@ cmdline (char *argv[], int optind, int argc) optind++; if (optind == argc) { - if (issue_command (cmd, params) == -1) exit (1); + if (issue_command (cmd, params, NULL) == -1) exit (1); } else { argv[optind] = NULL; - if (issue_command (cmd, params) == -1) exit (1); + if (issue_command (cmd, params, NULL) == -1) exit (1); cmdline (argv, optind+1, argc); } } int -issue_command (const char *cmd, char *argv[]) +issue_command (const char *cmd, char *argv[], const char *pipecmd) { int argc; + int stdout_saved_fd = -1; + int pid = 0; + int r; + + /* For | ... commands. Annoyingly we can't use popen(3) here. */ + if (pipecmd) { + int fd[2]; + + fflush (stdout); + pipe (fd); + pid = fork (); + if (pid == -1) { + perror ("fork"); + return -1; + } + + if (pid == 0) { /* Child process. */ + close (fd[1]); + dup2 (fd[0], 0); + + r = system (pipecmd); + if (r == -1) { + perror (pipecmd); + _exit (1); + } + _exit (WEXITSTATUS (r)); + } + + stdout_saved_fd = dup (1); + close (fd[0]); + dup2 (fd[1], 1); + close (fd[1]); + } for (argc = 0; argv[argc] != NULL; ++argc) ; @@ -657,29 +703,39 @@ issue_command (const char *cmd, char *argv[]) list_commands (); else display_command (argv[0]); - return 0; + r = 0; } else if (strcasecmp (cmd, "quit") == 0 || strcasecmp (cmd, "exit") == 0 || strcasecmp (cmd, "q") == 0) { quit = 1; - return 0; + r = 0; } else if (strcasecmp (cmd, "alloc") == 0 || strcasecmp (cmd, "allocate") == 0) - return do_alloc (cmd, argc, argv); + r = do_alloc (cmd, argc, argv); else if (strcasecmp (cmd, "echo") == 0) - return do_echo (cmd, argc, argv); + r = do_echo (cmd, argc, argv); else if (strcasecmp (cmd, "edit") == 0 || strcasecmp (cmd, "vi") == 0 || strcasecmp (cmd, "emacs") == 0) - return do_edit (cmd, argc, argv); + r = do_edit (cmd, argc, argv); else if (strcasecmp (cmd, "lcd") == 0) - return do_lcd (cmd, argc, argv); + r = do_lcd (cmd, argc, argv); else if (strcasecmp (cmd, "glob") == 0) - return do_glob (cmd, argc, argv); + r = do_glob (cmd, argc, argv); else - return run_action (cmd, argc, argv); + r = run_action (cmd, argc, argv); + + if (pipecmd) { + fflush (stdout); + close (1); + dup2 (stdout_saved_fd, 1); + close (stdout_saved_fd); + waitpid (pid, NULL, 0); + } + + return r; } void diff --git a/fish/fish.h b/fish/fish.h index 88158071..8f575955 100644 --- a/fish/fish.h +++ b/fish/fish.h @@ -34,7 +34,7 @@ extern guestfs_h *g; extern int quit; extern int verbose; -extern int issue_command (const char *cmd, char *argv[]); +extern int issue_command (const char *cmd, char *argv[], const char *pipe); extern void pod2text (const char *heading, const char *body); extern void list_builtin_commands (void); extern void display_builtin_command (const char *cmd); diff --git a/fish/glob.c b/fish/glob.c index f20da84a..a8ac58ab 100644 --- a/fish/glob.c +++ b/fish/glob.c @@ -147,7 +147,7 @@ glob_issue (char *cmd, int argc, } printf ("\n"); - if (issue_command (argv[0], &argv[1]) == -1) + if (issue_command (argv[0], &argv[1], NULL) == -1) *r = -1; /* ... but don't exit */ for (i = argc-1; i >= 1; --i) { diff --git a/guestfish.pod b/guestfish.pod index 0870b9ee..26312fe4 100644 --- a/guestfish.pod +++ b/guestfish.pod @@ -282,6 +282,32 @@ will create a directory C<local> on the host, and then export the contents of C</remote> on the mounted filesystem to C<local/remote-data.tar.gz>. (See C<tgz-out>). +=head1 PIPES + +Use C<command E<lt>spaceE<gt> | command> to pipe the output of the +first command (a guestfish command) to the second command (any host +command). For example: + + cat /etc/passwd | awk -F: '$3 == 0 { print }' + +(where C<cat> is the guestfish cat command, but C<awk> is the host awk +program). The above command would list all accounts in the guest +filesystem which have UID 0, ie. root accounts including backdoors. +Other examples: + + hexdump /bin/ls | head + list-devices | tail -1 + +The space before the pipe symbol is required, any space after the pipe +symbol is optional. Everything after the pipe symbol is just passed +straight to the host shell, so it can contain redirections, globs and +anything else that makes sense on the host side. + +To use a literal argument which begins with a pipe symbol, you have +to quote it, eg: + + echo "|" + =head1 EXIT ON ERROR BEHAVIOUR By default, guestfish will ignore any errors when in interactive mode |