mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
fish: Allow events to be processed in guestfish.
Add 'event', 'list-events' and 'delete-event' commands so that event handlers can be registered, listed and deleted in guestfish. The event handler is a shell script snippet or host command. Cc: Pádraig Brady <P@draigBrady.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -99,6 +99,7 @@ fish/cmds.c
|
||||
fish/cmds_gperf.c
|
||||
fish/cmds_gperf.gperf
|
||||
fish/completion.c
|
||||
fish/event-names.c
|
||||
fish/fish-cmds.h
|
||||
fish/guestfish
|
||||
fish/guestfish.1
|
||||
|
||||
@@ -30,6 +30,7 @@ generator_built = \
|
||||
cmds.c \
|
||||
cmds_gperf.gperf \
|
||||
completion.c \
|
||||
event-names.c \
|
||||
fish-cmds.h \
|
||||
guestfish-actions.pod \
|
||||
guestfish-commands.pod \
|
||||
@@ -81,6 +82,7 @@ guestfish_SOURCES = \
|
||||
display.c \
|
||||
echo.c \
|
||||
edit.c \
|
||||
events.c \
|
||||
fish.c \
|
||||
fish.h \
|
||||
glob.c \
|
||||
|
||||
272
fish/events.c
Normal file
272
fish/events.c
Normal file
@@ -0,0 +1,272 @@
|
||||
/* guestfish - the filesystem interactive shell
|
||||
* Copyright (C) 2011 Red Hat Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <guestfs.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "hash-pjw.h"
|
||||
|
||||
#include "fish.h"
|
||||
|
||||
/* The hash table maps names to multiple (linked list of) event handlers. */
|
||||
static Hash_table *event_handlers;
|
||||
|
||||
struct entry {
|
||||
struct entry *next; /* Next entry in linked list. */
|
||||
char *name; /* Event name. */
|
||||
char *command; /* Shell script / command that runs. */
|
||||
uint64_t event_bitmask; /* Events this is registered for. */
|
||||
int eh; /* Event handle (from guestfs_set_event_callback). */
|
||||
};
|
||||
|
||||
static void
|
||||
entry_free (void *x)
|
||||
{
|
||||
if (x) {
|
||||
struct entry *p = x;
|
||||
entry_free (p->next);
|
||||
free (p->name);
|
||||
free (p->command);
|
||||
free (p);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t
|
||||
entry_hash (void const *x, size_t table_size)
|
||||
{
|
||||
struct entry const *p = x;
|
||||
return hash_pjw (p->name, table_size);
|
||||
}
|
||||
|
||||
static bool
|
||||
entry_compare (void const *x, void const *y)
|
||||
{
|
||||
struct entry const *p = x;
|
||||
struct entry const *q = y;
|
||||
return STREQ (p->name, q->name);
|
||||
}
|
||||
|
||||
void
|
||||
init_event_handlers (void)
|
||||
{
|
||||
assert (event_handlers == NULL);
|
||||
event_handlers =
|
||||
hash_initialize (64, NULL, entry_hash, entry_compare, entry_free);
|
||||
}
|
||||
|
||||
void
|
||||
free_event_handlers (void)
|
||||
{
|
||||
assert (event_handlers != NULL);
|
||||
hash_free (event_handlers);
|
||||
event_handlers = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
do_event_handler (guestfs_h *g,
|
||||
void *opaque,
|
||||
uint64_t event,
|
||||
int event_handle,
|
||||
int flags,
|
||||
const char *buf, size_t buf_len,
|
||||
const uint64_t *array, size_t array_len)
|
||||
{
|
||||
pid_t pid;
|
||||
const char *argv[8 + array_len];
|
||||
const char *shell;
|
||||
struct entry *entry = opaque;
|
||||
size_t i, j;
|
||||
char *s;
|
||||
|
||||
pid = fork ();
|
||||
if (pid == -1) {
|
||||
perror ("event handler: fork");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) { /* Child process. */
|
||||
shell = getenv ("SHELL");
|
||||
if (!shell)
|
||||
shell = "/bin/sh";
|
||||
|
||||
setenv ("EVENT", event_name_of_event_bitmask (event), 1);
|
||||
|
||||
/* Construct the command and arguments. */
|
||||
i = 0;
|
||||
argv[i++] = shell;
|
||||
argv[i++] = "-c";
|
||||
argv[i++] = entry->command;
|
||||
argv[i++] = ""; /* $0 */
|
||||
|
||||
if (buf != NULL)
|
||||
/* XXX: So far, buf is always ASCII NUL-terminated. There is no
|
||||
* way to pass arbitrary 8 bit buffers.
|
||||
*/
|
||||
argv[i++] = buf;
|
||||
|
||||
for (j = 0; j < array_len; ++j) {
|
||||
if (asprintf (&s, "%" PRIu64, array[j]) == -1) {
|
||||
perror ("event handler: asprintf");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
argv[i++] = s;
|
||||
}
|
||||
|
||||
argv[i++] = NULL;
|
||||
|
||||
execvp (argv[0], (void *) argv);
|
||||
perror (argv[0]);
|
||||
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (waitpid (pid, NULL, 0) == -1)
|
||||
perror ("event handler: waitpid");
|
||||
}
|
||||
|
||||
int
|
||||
run_event (const char *cmd, size_t argc, char *argv[])
|
||||
{
|
||||
int r;
|
||||
struct entry *entry = NULL, *old_entry;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf (stderr,
|
||||
_("use 'event <name> <eventset> <script>' to register an event handler\n"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
entry = calloc (1, sizeof *entry);
|
||||
if (entry == NULL) {
|
||||
perror ("calloc");
|
||||
goto error;
|
||||
}
|
||||
entry->eh = -1;
|
||||
|
||||
r = event_bitmask_of_event_set (argv[1], &entry->event_bitmask);
|
||||
if (r == -1)
|
||||
goto error;
|
||||
|
||||
entry->name = strdup (argv[0]);
|
||||
if (entry->name == NULL) {
|
||||
perror ("strdup");
|
||||
goto error;
|
||||
}
|
||||
entry->command = strdup (argv[2]);
|
||||
if (entry->command == NULL) {
|
||||
perror ("strdup");
|
||||
goto error;
|
||||
}
|
||||
|
||||
entry->eh =
|
||||
guestfs_set_event_callback (g, do_event_handler,
|
||||
entry->event_bitmask, 0, entry);
|
||||
if (entry->eh == -1)
|
||||
goto error;
|
||||
|
||||
r = hash_insert_if_absent (event_handlers, entry, (const void **) &old_entry);
|
||||
if (r == -1)
|
||||
goto error;
|
||||
if (r == 0) { /* old_entry set to existing entry */
|
||||
entry->next = old_entry->next;
|
||||
/* XXX are we allowed to update the old entry? */
|
||||
old_entry->next = entry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (entry) {
|
||||
if (entry->eh >= 0)
|
||||
guestfs_delete_event_callback (g, entry->eh);
|
||||
free (entry->name);
|
||||
free (entry->command);
|
||||
free (entry);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
run_delete_event (const char *cmd, size_t argc, char *argv[])
|
||||
{
|
||||
if (argc != 1) {
|
||||
fprintf (stderr,
|
||||
_("use 'delete-event <name>' to delete an event handler\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct entry key = { .name = bad_cast (argv[0]) };
|
||||
struct entry *entry, *p;
|
||||
|
||||
entry = hash_delete (event_handlers, &key);
|
||||
if (!entry) {
|
||||
fprintf (stderr, _("delete-event: %s: no such event handler\n"), argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Delete them from the handle. */
|
||||
p = entry;
|
||||
while (p) {
|
||||
guestfs_delete_event_callback (g, p->eh);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
/* Free the structures. */
|
||||
entry_free (entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
list_event (void *x, void *data)
|
||||
{
|
||||
struct entry *entry = x;
|
||||
|
||||
while (entry) {
|
||||
printf ("\"%s\" (%d): ", entry->name, entry->eh);
|
||||
print_event_set (entry->event_bitmask, stdout);
|
||||
printf (": %s\n", entry->command);
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
run_list_events (const char *cmd, size_t argc, char *argv[])
|
||||
{
|
||||
if (argc != 0) {
|
||||
fprintf (stderr,
|
||||
_("use 'list-events' to list event handlers\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
hash_do_for_each (event_handlers, list_event, NULL);
|
||||
return 0;
|
||||
}
|
||||
@@ -208,6 +208,7 @@ main (int argc, char *argv[])
|
||||
int next_prepared_drive = 1;
|
||||
|
||||
initialize_readline ();
|
||||
init_event_handlers ();
|
||||
|
||||
memset (&sa, 0, sizeof sa);
|
||||
sa.sa_handler = SIG_IGN;
|
||||
@@ -548,6 +549,7 @@ main (int argc, char *argv[])
|
||||
progress_bar_free (bar);
|
||||
|
||||
guestfs_close (g);
|
||||
free_event_handlers ();
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -94,6 +94,15 @@ extern char **do_completion (const char *text, int start, int end);
|
||||
extern int complete_dest_paths;
|
||||
extern char *complete_dest_paths_generator (const char *text, int state);
|
||||
|
||||
/* in events.c */
|
||||
extern void init_event_handlers (void);
|
||||
extern void free_event_handlers (void);
|
||||
|
||||
/* in event-names.c (auto-generated) */
|
||||
extern const char *event_name_of_event_bitmask (uint64_t);
|
||||
extern void print_event_set (uint64_t, FILE *);
|
||||
extern int event_bitmask_of_event_set (const char *arg, uint64_t *);
|
||||
|
||||
/* in alloc.c */
|
||||
extern int alloc_disk (const char *filename, const char *size,
|
||||
int add, int sparse);
|
||||
|
||||
@@ -83,5 +83,12 @@ run_reopen (const char *cmd, size_t argc, char *argv[])
|
||||
guestfs_close (g);
|
||||
g = g2;
|
||||
|
||||
/* We don't bother copying event handlers over to the new handle,
|
||||
* but we have to reset the list because they were registered
|
||||
* against the old handle.
|
||||
*/
|
||||
free_event_handlers ();
|
||||
init_event_handlers ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,12 +62,12 @@ generator_capitests.cmx: generator_utils.cmx generator_types.cmx \
|
||||
generator_docstrings.cmx generator_actions.cmx
|
||||
generator_fish.cmo: generator_utils.cmi generator_types.cmo \
|
||||
generator_structs.cmi generator_prepopts.cmi generator_pr.cmi \
|
||||
generator_optgroups.cmo generator_docstrings.cmo generator_c.cmo \
|
||||
generator_actions.cmi
|
||||
generator_optgroups.cmo generator_events.cmo generator_docstrings.cmo \
|
||||
generator_c.cmo generator_actions.cmi
|
||||
generator_fish.cmx: generator_utils.cmx generator_types.cmx \
|
||||
generator_structs.cmx generator_prepopts.cmx generator_pr.cmx \
|
||||
generator_optgroups.cmx generator_docstrings.cmx generator_c.cmx \
|
||||
generator_actions.cmx
|
||||
generator_optgroups.cmx generator_events.cmx generator_docstrings.cmx \
|
||||
generator_c.cmx generator_actions.cmx
|
||||
generator_ocaml.cmo: generator_utils.cmi generator_types.cmo \
|
||||
generator_structs.cmi generator_pr.cmi generator_optgroups.cmo \
|
||||
generator_events.cmo generator_docstrings.cmo generator_c.cmo \
|
||||
|
||||
@@ -6675,6 +6675,16 @@ them with the help of L</glob> like this:
|
||||
|
||||
glob copy-out /home/* .");
|
||||
|
||||
("delete_event", (RErr,[], []), -1, [], [],
|
||||
"delete a previously registered event handler",
|
||||
" delete-event name
|
||||
|
||||
Delete the event handler which was previously registered as C<name>.
|
||||
If multiple event handlers were registered with the same name, they
|
||||
are all deleted.
|
||||
|
||||
See also the guestfish commands C<event> and C<list-events>.");
|
||||
|
||||
("display", (RErr,[], []), -1, [], [],
|
||||
"display an image",
|
||||
" display filename
|
||||
@@ -6706,6 +6716,39 @@ The editor is C<$EDITOR>. However if you use the alternate
|
||||
commands C<vi> or C<emacs> you will get those corresponding
|
||||
editors.");
|
||||
|
||||
("event", (RErr,[], []), -1, [], [],
|
||||
"register a handler for an event or events",
|
||||
" event name eventset \"shell script ...\"
|
||||
|
||||
Register a shell script fragment which is executed when an
|
||||
event is raised. See L<guestfs(3)/guestfs_set_event_callback>
|
||||
for a discussion of the event API in libguestfs.
|
||||
|
||||
The C<name> parameter is a name that you give to this event
|
||||
handler. It can be any string (even the empty string) and is
|
||||
simply there so you can delete the handler using the guestfish
|
||||
C<delete-event> command.
|
||||
|
||||
The C<eventset> parameter is a comma-separated list of one
|
||||
or more events, for example C<close> or C<close,trace>. The
|
||||
special value C<*> means all events.
|
||||
|
||||
The third and final parameter is the shell script fragment
|
||||
(or any external command) that is executed when any of the
|
||||
events in the eventset occurs. It is executed using
|
||||
C<$SHELL -c>, or if C<$SHELL> is not set then C</bin/sh -c>.
|
||||
|
||||
The shell script fragment receives callback parameters as
|
||||
arguments C<$1>, C<$2> etc. The actual event that was
|
||||
called is available in the environment variable C<$EVENT>.
|
||||
|
||||
event \"\" close \"echo closed\"
|
||||
event messages appliance,library,trace \"echo $@\"
|
||||
event \"\" progress \"echo progress: $3/$4\"
|
||||
event \"\" * \"echo $EVENT $@\"
|
||||
|
||||
See also the guestfish commands C<delete-event> and C<list-events>.");
|
||||
|
||||
("glob", (RErr,[], []), -1, [], [],
|
||||
"expand wildcards in command",
|
||||
" glob command args...
|
||||
@@ -6760,6 +6803,13 @@ itself.
|
||||
|
||||
Note that C<!cd> won't do what you might expect.");
|
||||
|
||||
("list_events", (RErr,[], []), -1, [], [],
|
||||
"list event handlers",
|
||||
" list-events
|
||||
|
||||
List the event handlers registered using the guestfish
|
||||
C<event> command.");
|
||||
|
||||
("man", (RErr,[], []), -1, [FishAlias "manual"], [],
|
||||
"open the manual",
|
||||
" man
|
||||
|
||||
@@ -29,6 +29,7 @@ open Generator_actions
|
||||
open Generator_structs
|
||||
open Generator_prepopts
|
||||
open Generator_c
|
||||
open Generator_events
|
||||
|
||||
let doc_opttype_of = function
|
||||
| Bool n -> "true|false"
|
||||
@@ -971,3 +972,93 @@ and generate_fish_prep_options_c () =
|
||||
name name;
|
||||
) prepopts;
|
||||
pr "};\n"
|
||||
|
||||
and generate_fish_event_names () =
|
||||
generate_header CStyle GPLv2plus;
|
||||
|
||||
pr "\
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include \"fish.h\"
|
||||
|
||||
const char *
|
||||
event_name_of_event_bitmask (uint64_t ev)
|
||||
{
|
||||
switch (ev) {
|
||||
";
|
||||
|
||||
List.iter (
|
||||
fun (name, _) ->
|
||||
pr " case GUESTFS_EVENT_%s:\n" (String.uppercase name);
|
||||
pr " return \"%s\";\n" name
|
||||
) events;
|
||||
|
||||
pr " default:
|
||||
abort (); /* should not happen */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_event_set (uint64_t event_bitmask, FILE *fp)
|
||||
{
|
||||
int comma = 0;
|
||||
|
||||
if (event_bitmask == GUESTFS_EVENT_ALL) {
|
||||
fputs (\"*\", fp);
|
||||
return;
|
||||
}
|
||||
|
||||
";
|
||||
|
||||
List.iter (
|
||||
fun (name, _) ->
|
||||
pr " if (event_bitmask & GUESTFS_EVENT_%s) {\n" (String.uppercase name);
|
||||
pr " if (comma) fputc (',', fp);\n";
|
||||
pr " comma = 1;\n";
|
||||
pr " fputs (\"%s\", fp);\n" name;
|
||||
pr " }\n"
|
||||
) events;
|
||||
|
||||
pr "\
|
||||
}
|
||||
|
||||
int
|
||||
event_bitmask_of_event_set (const char *arg, uint64_t *eventset_r)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
if (STREQ (arg, \"*\")) {
|
||||
*eventset_r = GUESTFS_EVENT_ALL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*eventset_r = 0;
|
||||
|
||||
while (*arg) {
|
||||
n = strcspn (arg, \",\");
|
||||
|
||||
";
|
||||
|
||||
List.iter (
|
||||
fun (name, _) ->
|
||||
pr "if (STREQLEN (arg, \"%s\", n))\n" name;
|
||||
pr " *eventset_r |= GUESTFS_EVENT_%s;\n" (String.uppercase name);
|
||||
pr " else ";
|
||||
) events;
|
||||
|
||||
pr "\
|
||||
{
|
||||
fprintf (stderr, _(\"unknown event name: %%s\\n\"), arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
arg += n;
|
||||
if (*arg == ',')
|
||||
arg++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
"
|
||||
|
||||
@@ -99,6 +99,7 @@ Run it from the top source directory using the command
|
||||
output_to "fish/cmds_gperf.gperf" generate_fish_cmds_gperf;
|
||||
output_to "fish/cmds.c" generate_fish_cmds;
|
||||
output_to "fish/completion.c" generate_fish_completion;
|
||||
output_to "fish/event-names.c" generate_fish_event_names;
|
||||
output_to "fish/fish-cmds.h" generate_fish_cmds_h;
|
||||
output_to "fish/guestfish-commands.pod" generate_fish_commands_pod;
|
||||
output_to "fish/guestfish-actions.pod" generate_fish_actions_pod;
|
||||
|
||||
@@ -97,6 +97,8 @@ fish/destpaths.c
|
||||
fish/display.c
|
||||
fish/echo.c
|
||||
fish/edit.c
|
||||
fish/event-names.c
|
||||
fish/events.c
|
||||
fish/fish.c
|
||||
fish/glob.c
|
||||
fish/help.c
|
||||
|
||||
@@ -42,6 +42,7 @@ TESTS = \
|
||||
test-guestfish-a.sh \
|
||||
test-guestfish-d.sh \
|
||||
test-guestfish-escapes.sh \
|
||||
test-guestfish-events.sh \
|
||||
test-guestfish-tilde.sh \
|
||||
test-inspect-fstab.sh \
|
||||
test-inspect-fstab-md.sh \
|
||||
|
||||
88
regressions/test-guestfish-events.sh
Executable file
88
regressions/test-guestfish-events.sh
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash -
|
||||
# libguestfs
|
||||
# Copyright (C) 2011 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.
|
||||
|
||||
# Test guestfish events.
|
||||
|
||||
set -e
|
||||
|
||||
rm -f test.out
|
||||
|
||||
../fish/guestfish -a /dev/null <<'EOF' > test.out
|
||||
trace true
|
||||
|
||||
event ev1 * "echo $EVENT $@"
|
||||
event ev1 * "echo $EVENT $@"
|
||||
event ev2 * "echo $EVENT $@"
|
||||
|
||||
list-events
|
||||
delete-event ev1
|
||||
list-events
|
||||
reopen
|
||||
list-events
|
||||
|
||||
event ev1 close,subprocess_quit "echo $EVENT $@"
|
||||
event ev2 close,subprocess_quit "echo $EVENT $@"
|
||||
event ev3 launch "echo $EVENT $@"
|
||||
|
||||
list-events
|
||||
-delete-event ev4
|
||||
list-events
|
||||
delete-event ev1
|
||||
list-events
|
||||
delete-event ev3
|
||||
list-events
|
||||
|
||||
EOF
|
||||
|
||||
if [ "$(cat test.out)" != '"ev1" (0): *: echo $EVENT $@
|
||||
"ev1" (1): *: echo $EVENT $@
|
||||
"ev2" (2): *: echo $EVENT $@
|
||||
"ev2" (2): *: echo $EVENT $@
|
||||
enter get_verbose
|
||||
trace get_verbose
|
||||
trace get_verbose = 0
|
||||
enter get_trace
|
||||
trace get_trace
|
||||
trace get_trace = 1
|
||||
enter get_autosync
|
||||
trace get_autosync
|
||||
trace get_autosync = 1
|
||||
enter get_path
|
||||
trace get_path
|
||||
trace get_path = "../appliance"
|
||||
enter get_pgroup
|
||||
trace get_pgroup
|
||||
trace get_pgroup = 0
|
||||
trace close
|
||||
close
|
||||
"ev1" (0): close,subprocess_quit: echo $EVENT $@
|
||||
"ev2" (1): close,subprocess_quit: echo $EVENT $@
|
||||
"ev3" (2): launch_done: echo $EVENT $@
|
||||
"ev1" (0): close,subprocess_quit: echo $EVENT $@
|
||||
"ev2" (1): close,subprocess_quit: echo $EVENT $@
|
||||
"ev3" (2): launch_done: echo $EVENT $@
|
||||
"ev2" (1): close,subprocess_quit: echo $EVENT $@
|
||||
"ev3" (2): launch_done: echo $EVENT $@
|
||||
"ev2" (1): close,subprocess_quit: echo $EVENT $@
|
||||
close' ]; then
|
||||
echo "$0: unexpected output from guestfish events"
|
||||
cat test.out
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm test.out
|
||||
Reference in New Issue
Block a user