mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
This command run over the source: perl -pi.bak -e 's/(20[01][0-9])-2018/$1-2019/g' `git ls-files`
304 lines
6.8 KiB
C
304 lines
6.8 KiB
C
/* guestfish - guest filesystem shell
|
||
* Copyright (C) 2011-2019 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.
|
||
*/
|
||
|
||
/**
|
||
* This file implements the guestfish event-related commands,
|
||
* C<event>, C<delete-event> and C<list-events>.
|
||
*/
|
||
|
||
#include <config.h>
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <inttypes.h>
|
||
#include <libintl.h>
|
||
#include <assert.h>
|
||
#include <sys/types.h>
|
||
#include <sys/wait.h>
|
||
#include <errno.h>
|
||
#include <error.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;
|
||
CLEANUP_FREE const char **argv = NULL;
|
||
const char *shell;
|
||
struct entry *entry = opaque;
|
||
size_t i, j;
|
||
char *s;
|
||
|
||
argv = malloc (sizeof (const char *) * (8 + array_len));
|
||
if (argv == NULL) {
|
||
perror ("malloc");
|
||
return;
|
||
}
|
||
|
||
pid = fork ();
|
||
if (pid == -1) {
|
||
perror ("event handler: fork");
|
||
return;
|
||
}
|
||
|
||
if (pid == 0) { /* Child process. */
|
||
char *str;
|
||
|
||
shell = getenv ("SHELL");
|
||
if (!shell)
|
||
shell = "/bin/sh";
|
||
|
||
str = guestfs_event_to_string (event);
|
||
setenv ("EVENT", str, 1);
|
||
free (str);
|
||
|
||
/* 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 = 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 void
|
||
print_event_set (uint64_t event_bitmask, FILE *fp)
|
||
{
|
||
if (event_bitmask == GUESTFS_EVENT_ALL)
|
||
fputs ("*", fp);
|
||
else {
|
||
CLEANUP_FREE char *str = guestfs_event_to_string (event_bitmask);
|
||
if (!str)
|
||
error (EXIT_FAILURE, errno, "guestfs_event_to_string");
|
||
fputs (str, fp);
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|