/* 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 #include #include #include #include #include #include #include #include #include #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