mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
Run this command across the source:
perl -pi.bak -e 's/(20[012][0-9])-20[12][012]/$1-2023/g' `git ls-files`
and remove changes to po{,-docs}/*.po{,t} (these will be regenerated
later when we run 'make dist').
941 lines
20 KiB
C
941 lines
20 KiB
C
/* libguestfs
|
||
* Copyright (C) 2009-2023 Red Hat Inc.
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2 of the License, or (at your option) any later version.
|
||
*
|
||
* This library 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
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public
|
||
* License along with this library; if not, write to the Free Software
|
||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
*/
|
||
|
||
/**
|
||
* This file deals with the C<guestfs_h> handle, creating it, closing
|
||
* it, and initializing/setting/getting fields.
|
||
*/
|
||
|
||
#include <config.h>
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <libintl.h>
|
||
|
||
#include <libxml/parser.h>
|
||
#include <libxml/xmlversion.h>
|
||
|
||
#include "hash.h"
|
||
#include "ignore-value.h"
|
||
#include "c-ctype.h"
|
||
#include "getprogname.h"
|
||
|
||
#include "guestfs.h"
|
||
#include "guestfs-internal.h"
|
||
#include "guestfs-internal-actions.h"
|
||
|
||
static int shutdown_backend (guestfs_h *g, int check_for_errors);
|
||
static void close_handles (void);
|
||
|
||
static pthread_mutex_t handles_lock = PTHREAD_MUTEX_INITIALIZER;
|
||
static guestfs_h *handles = NULL;
|
||
static int atexit_handler_set = 0;
|
||
|
||
static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
|
||
|
||
static void init_libguestfs (void) __attribute__((constructor));
|
||
|
||
/**
|
||
* No initialization is required by libguestfs, but libvirt and
|
||
* libxml2 require initialization if they might be called from
|
||
* multiple threads. Hence this constructor function which is called
|
||
* when libguestfs is first loaded.
|
||
*/
|
||
static void
|
||
init_libguestfs (void)
|
||
{
|
||
ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&init_lock);
|
||
|
||
#ifdef HAVE_LIBVIRT
|
||
virInitialize ();
|
||
#endif
|
||
|
||
xmlInitParser ();
|
||
LIBXML_TEST_VERSION;
|
||
}
|
||
|
||
guestfs_h *
|
||
guestfs_create (void)
|
||
{
|
||
return guestfs_create_flags (0);
|
||
}
|
||
|
||
guestfs_h *
|
||
guestfs_create_flags (unsigned flags, ...)
|
||
{
|
||
guestfs_h *g;
|
||
pthread_mutexattr_t attr;
|
||
|
||
g = calloc (1, sizeof (*g));
|
||
if (!g) return NULL;
|
||
|
||
/* The per-handle lock is recursive. */
|
||
pthread_mutexattr_init (&attr);
|
||
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
|
||
pthread_mutex_init (&g->lock, &attr);
|
||
|
||
pthread_mutex_init (&g->error_data_list_lock, NULL);
|
||
|
||
g->state = CONFIG;
|
||
|
||
g->conn = NULL;
|
||
|
||
pthread_key_create (&g->error_data, NULL);
|
||
g->abort_cb = abort;
|
||
|
||
g->recovery_proc = 1;
|
||
g->autosync = 1;
|
||
|
||
g->memsize = DEFAULT_MEMSIZE;
|
||
|
||
g->qemu_img_supports_U_option = -1; /* not tested, see lib/info.c */
|
||
|
||
/* Start with large serial numbers so they are easy to spot
|
||
* inside the protocol.
|
||
*/
|
||
g->msg_next_serial = 0x00123400;
|
||
|
||
/* Default is uniprocessor appliance. */
|
||
g->smp = 1;
|
||
|
||
g->path = strdup (GUESTFS_DEFAULT_PATH);
|
||
if (!g->path) goto error;
|
||
|
||
#ifdef QEMU
|
||
g->hv = strdup (QEMU);
|
||
#else
|
||
/* configure --without-qemu, so set QEMU to something which will
|
||
* definitely fail. The user is expected to override the hypervisor
|
||
* by setting an environment variable or calling set_hv.
|
||
*/
|
||
g->hv = strdup ("false");
|
||
#endif
|
||
if (!g->hv) goto error;
|
||
|
||
/* Get program name. */
|
||
if (STRPREFIX (getprogname (), "lt-"))
|
||
/* Remove libtool (lt-*) prefix from short name. */
|
||
g->program = strdup (getprogname () + 3);
|
||
else
|
||
g->program = strdup (getprogname ());
|
||
if (!g->program) goto error;
|
||
|
||
g->identifier = strdup ("");
|
||
if (!g->identifier) goto error;
|
||
|
||
if (guestfs_int_set_backend (g, DEFAULT_BACKEND) == -1) {
|
||
warning (g, _("libguestfs was built with an invalid default backend, using ‘direct’ instead"));
|
||
if (guestfs_int_set_backend (g, "direct") == -1) {
|
||
warning (g, _("‘direct’ backend does not work"));
|
||
goto error;
|
||
}
|
||
}
|
||
|
||
if (!(flags & GUESTFS_CREATE_NO_ENVIRONMENT))
|
||
ignore_value (guestfs_parse_environment (g));
|
||
|
||
if (!(flags & GUESTFS_CREATE_NO_CLOSE_ON_EXIT)) {
|
||
g->close_on_exit = true;
|
||
|
||
/* Link the handles onto a global list. */
|
||
ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&handles_lock);
|
||
g->next = handles;
|
||
handles = g;
|
||
if (!atexit_handler_set) {
|
||
atexit (close_handles);
|
||
atexit_handler_set = 1;
|
||
}
|
||
}
|
||
|
||
debug (g, "create: flags = %u, handle = %p, program = %s",
|
||
flags, g, g->program);
|
||
|
||
return g;
|
||
|
||
error:
|
||
guestfs_int_free_string_list (g->backend_settings);
|
||
free (g->backend);
|
||
free (g->identifier);
|
||
free (g->program);
|
||
free (g->path);
|
||
free (g->hv);
|
||
free (g->append);
|
||
guestfs_int_free_error_data_list (g);
|
||
pthread_key_delete (g->error_data);
|
||
pthread_mutex_destroy (&g->error_data_list_lock);
|
||
pthread_mutex_destroy (&g->lock);
|
||
free (g);
|
||
return NULL;
|
||
}
|
||
|
||
static int
|
||
parse_environment (guestfs_h *g,
|
||
char *(*do_getenv) (const void *data, const char *),
|
||
const void *data)
|
||
{
|
||
int memsize, b;
|
||
char *str;
|
||
|
||
/* Don't bother checking the return values of functions
|
||
* that cannot return errors.
|
||
*/
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_TRACE");
|
||
if (str) {
|
||
b = guestfs_int_is_true (str);
|
||
if (b == -1) {
|
||
error (g, _("%s=%s: non-boolean value"), "LIBGUESTFS_TRACE", str);
|
||
return -1;
|
||
}
|
||
guestfs_set_trace (g, b);
|
||
}
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_DEBUG");
|
||
if (str) {
|
||
b = guestfs_int_is_true (str);
|
||
if (b == -1) {
|
||
error (g, _("%s=%s: non-boolean value"), "LIBGUESTFS_DEBUG", str);
|
||
return -1;
|
||
}
|
||
guestfs_set_verbose (g, b);
|
||
}
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_TMPDIR");
|
||
if (str && STRNEQ (str, "")) {
|
||
if (guestfs_set_tmpdir (g, str) == -1)
|
||
return -1;
|
||
}
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_CACHEDIR");
|
||
if (str && STRNEQ (str, "")) {
|
||
if (guestfs_set_cachedir (g, str) == -1)
|
||
return -1;
|
||
}
|
||
|
||
str = do_getenv (data, "TMPDIR");
|
||
if (guestfs_int_set_env_tmpdir (g, "TMPDIR", str) == -1)
|
||
return -1;
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_PATH");
|
||
if (str && STRNEQ (str, ""))
|
||
guestfs_set_path (g, str);
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_HV");
|
||
if (str && STRNEQ (str, ""))
|
||
guestfs_set_hv (g, str);
|
||
else {
|
||
str = do_getenv (data, "LIBGUESTFS_QEMU");
|
||
if (str && STRNEQ (str, ""))
|
||
guestfs_set_hv (g, str);
|
||
}
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_APPEND");
|
||
if (str)
|
||
guestfs_set_append (g, str);
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_MEMSIZE");
|
||
if (str && STRNEQ (str, "")) {
|
||
if (sscanf (str, "%d", &memsize) != 1) {
|
||
error (g, _("non-numeric value for LIBGUESTFS_MEMSIZE"));
|
||
return -1;
|
||
}
|
||
if (guestfs_set_memsize (g, memsize) == -1) {
|
||
/* set_memsize produces an error message already. */
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_BACKEND");
|
||
if (str && STRNEQ (str, "")) {
|
||
if (guestfs_set_backend (g, str) == -1)
|
||
return -1;
|
||
}
|
||
else {
|
||
str = do_getenv (data, "LIBGUESTFS_ATTACH_METHOD");
|
||
if (str && STRNEQ (str, "")) {
|
||
if (guestfs_set_backend (g, str) == -1)
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
str = do_getenv (data, "LIBGUESTFS_BACKEND_SETTINGS");
|
||
if (str) {
|
||
CLEANUP_FREE_STRING_LIST char **settings = guestfs_int_split_string (':', str);
|
||
|
||
if (settings == NULL) {
|
||
perrorf (g, "split_string: malloc");
|
||
return -1;
|
||
}
|
||
|
||
if (guestfs_set_backend_settings (g, settings) == -1)
|
||
return -1;
|
||
}
|
||
|
||
str = do_getenv (data, "XDG_RUNTIME_DIR");
|
||
if (guestfs_int_set_env_runtimedir (g, "XDG_RUNTIME_DIR", str) == -1)
|
||
return -1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static char *
|
||
call_getenv (const void *data, const char *name)
|
||
{
|
||
return getenv (name);
|
||
}
|
||
|
||
int
|
||
guestfs_impl_parse_environment (guestfs_h *g)
|
||
{
|
||
return parse_environment (g, call_getenv, NULL);
|
||
}
|
||
|
||
static char *
|
||
getenv_from_strings (const void *stringsv, const char *name)
|
||
{
|
||
char **strings = (char **) stringsv;
|
||
const size_t len = strlen (name);
|
||
size_t i;
|
||
|
||
for (i = 0; strings[i] != NULL; ++i)
|
||
if (STRPREFIX (strings[i], name) && strings[i][len] == '=')
|
||
return &strings[i][len+1];
|
||
return NULL;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_parse_environment_list (guestfs_h *g, char * const *strings)
|
||
{
|
||
return parse_environment (g, getenv_from_strings, strings);
|
||
}
|
||
|
||
void
|
||
guestfs_close (guestfs_h *g)
|
||
{
|
||
struct hv_param *hp, *hp_next;
|
||
guestfs_h **gg;
|
||
|
||
if (g->state == NO_HANDLE) {
|
||
/* Not safe to call ANY callbacks here, so ... */
|
||
fprintf (stderr, _("guestfs_close: called twice on the same handle\n"));
|
||
return;
|
||
}
|
||
|
||
/* Remove the handle from the handles list. */
|
||
if (g->close_on_exit) {
|
||
ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&handles_lock);
|
||
for (gg = &handles; *gg != g; gg = &(*gg)->next)
|
||
;
|
||
*gg = g->next;
|
||
}
|
||
|
||
if (g->trace) {
|
||
const char trace_msg[] = "close";
|
||
|
||
guestfs_int_call_callbacks_message (g, GUESTFS_EVENT_TRACE,
|
||
trace_msg, strlen (trace_msg));
|
||
}
|
||
|
||
debug (g, "closing guestfs handle %p (state %d)", g, (int) g->state);
|
||
|
||
if (g->state != CONFIG)
|
||
shutdown_backend (g, 0);
|
||
|
||
/* Run user close callbacks. */
|
||
guestfs_int_call_callbacks_void (g, GUESTFS_EVENT_CLOSE);
|
||
|
||
/* Test output file used by bindtests. */
|
||
if (g->test_fp != NULL)
|
||
fclose (g->test_fp);
|
||
|
||
/* Remove temporary directories. */
|
||
guestfs_int_remove_tmpdir (g);
|
||
guestfs_int_remove_sockdir (g);
|
||
|
||
/* Mark the handle as dead and then free up all memory. */
|
||
g->state = NO_HANDLE;
|
||
|
||
free (g->events);
|
||
g->nr_events = 0;
|
||
g->events = NULL;
|
||
|
||
#if HAVE_FUSE
|
||
guestfs_int_free_fuse (g);
|
||
#endif
|
||
|
||
guestfs_int_free_drives (g);
|
||
|
||
for (hp = g->hv_params; hp; hp = hp_next) {
|
||
free (hp->hv_param);
|
||
free (hp->hv_value);
|
||
hp_next = hp->next;
|
||
free (hp);
|
||
}
|
||
|
||
if (g->pda)
|
||
hash_free (g->pda);
|
||
free (g->tmpdir);
|
||
free (g->sockdir);
|
||
free (g->env_tmpdir);
|
||
free (g->env_runtimedir);
|
||
free (g->int_tmpdir);
|
||
free (g->int_cachedir);
|
||
free (g->identifier);
|
||
free (g->program);
|
||
free (g->path);
|
||
free (g->hv);
|
||
free (g->backend);
|
||
free (g->backend_data);
|
||
#if HAVE_FUSE
|
||
free (g->localmountpoint);
|
||
#endif
|
||
guestfs_int_free_string_list (g->backend_settings);
|
||
free (g->append);
|
||
guestfs_int_free_error_data_list (g);
|
||
pthread_key_delete (g->error_data);
|
||
pthread_mutex_destroy (&g->lock);
|
||
free (g);
|
||
}
|
||
|
||
#pragma GCC diagnostic push
|
||
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
|
||
int64_t
|
||
guestfs_impl_c_pointer (guestfs_h *g)
|
||
{
|
||
return (int64_t) g;
|
||
}
|
||
#pragma GCC diagnostic pop
|
||
|
||
int
|
||
guestfs_impl_shutdown (guestfs_h *g)
|
||
{
|
||
return shutdown_backend (g, 1);
|
||
}
|
||
|
||
/**
|
||
* This function is the common path for shutting down the backend
|
||
* qemu process.
|
||
*
|
||
* C<guestfs_shutdown> calls C<shutdown_backend> with
|
||
* C<check_for_errors=1>. C<guestfs_close> calls C<shutdown_backend>
|
||
* with C<check_for_errors=0>.
|
||
*
|
||
* C<check_for_errors> is a hint to the backend about whether we care
|
||
* about errors or not. In the libvirt case it can be used to
|
||
* optimize the shutdown for speed when we don't care.
|
||
*/
|
||
static int
|
||
shutdown_backend (guestfs_h *g, int check_for_errors)
|
||
{
|
||
int ret = 0;
|
||
size_t i;
|
||
|
||
if (g->state == CONFIG)
|
||
return 0;
|
||
|
||
/* Try to sync if autosync flag is set. */
|
||
if (g->autosync && g->state == READY) {
|
||
if (guestfs_internal_autosync (g) == -1)
|
||
ret = -1;
|
||
}
|
||
|
||
/* Shut down the backend. */
|
||
if (g->backend_ops->shutdown (g, g->backend_data, check_for_errors) == -1)
|
||
ret = -1;
|
||
|
||
/* Close sockets. */
|
||
if (g->conn) {
|
||
g->conn->ops->free_connection (g, g->conn);
|
||
g->conn = NULL;
|
||
}
|
||
|
||
guestfs_int_free_drives (g);
|
||
|
||
for (i = 0; i < g->nr_features; ++i)
|
||
free (g->features[i].group);
|
||
free (g->features);
|
||
g->features = NULL;
|
||
g->nr_features = 0;
|
||
|
||
g->state = CONFIG;
|
||
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* Close all open handles (called from L<atexit(3)>).
|
||
*/
|
||
static void
|
||
close_handles (void)
|
||
{
|
||
while (handles) guestfs_close (handles);
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_verbose (guestfs_h *g, int v)
|
||
{
|
||
g->verbose = !!v;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_verbose (guestfs_h *g)
|
||
{
|
||
return g->verbose;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_autosync (guestfs_h *g, int a)
|
||
{
|
||
g->autosync = !!a;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_autosync (guestfs_h *g)
|
||
{
|
||
return g->autosync;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_path (guestfs_h *g, const char *path)
|
||
{
|
||
free (g->path);
|
||
g->path = NULL;
|
||
|
||
g->path =
|
||
path == NULL ?
|
||
safe_strdup (g, GUESTFS_DEFAULT_PATH) : safe_strdup (g, path);
|
||
return 0;
|
||
}
|
||
|
||
const char *
|
||
guestfs_impl_get_path (guestfs_h *g)
|
||
{
|
||
return g->path;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_qemu (guestfs_h *g, const char *qemu)
|
||
{
|
||
char *new_hv;
|
||
|
||
/* Only this deprecated set_qemu API supports using NULL as a
|
||
* parameter, to mean set it back to the default QEMU. The new
|
||
* set_hv API does not allow callers to do this.
|
||
*/
|
||
if (qemu == NULL) {
|
||
#ifdef QEMU
|
||
new_hv = safe_strdup (g, QEMU);
|
||
#else
|
||
error (g, _("configured --without-qemu so calling guestfs_set_qemu with qemu == NULL is an error"));
|
||
return -1;
|
||
#endif
|
||
}
|
||
else
|
||
new_hv = safe_strdup (g, qemu);
|
||
|
||
free (g->hv);
|
||
g->hv = new_hv;
|
||
|
||
return 0;
|
||
}
|
||
|
||
const char *
|
||
guestfs_impl_get_qemu (guestfs_h *g)
|
||
{
|
||
return g->hv;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_hv (guestfs_h *g, const char *hv)
|
||
{
|
||
free (g->hv);
|
||
g->hv = safe_strdup (g, hv);
|
||
return 0;
|
||
}
|
||
|
||
char *
|
||
guestfs_impl_get_hv (guestfs_h *g)
|
||
{
|
||
return safe_strdup (g, g->hv);
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_append (guestfs_h *g, const char *append)
|
||
{
|
||
free (g->append);
|
||
g->append = NULL;
|
||
|
||
g->append = append ? safe_strdup (g, append) : NULL;
|
||
return 0;
|
||
}
|
||
|
||
const char *
|
||
guestfs_impl_get_append (guestfs_h *g)
|
||
{
|
||
return g->append;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_memsize (guestfs_h *g, int memsize)
|
||
{
|
||
if (memsize < MIN_MEMSIZE) {
|
||
error (g, _("too small value for memsize (must be at least %d)"), MIN_MEMSIZE);
|
||
return -1;
|
||
}
|
||
g->memsize = memsize;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_memsize (guestfs_h *g)
|
||
{
|
||
return g->memsize;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_selinux (guestfs_h *g, int selinux)
|
||
{
|
||
g->selinux = selinux;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_selinux (guestfs_h *g)
|
||
{
|
||
return g->selinux;
|
||
}
|
||
|
||
struct guestfs_version *
|
||
guestfs_impl_version (guestfs_h *g)
|
||
{
|
||
struct guestfs_version *r;
|
||
|
||
r = safe_malloc (g, sizeof *r);
|
||
r->major = PACKAGE_VERSION_MAJOR;
|
||
r->minor = PACKAGE_VERSION_MINOR;
|
||
r->release = PACKAGE_VERSION_RELEASE;
|
||
r->extra = safe_strdup (g, PACKAGE_VERSION_EXTRA);
|
||
return r;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_trace (guestfs_h *g, int t)
|
||
{
|
||
g->trace = !!t;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_trace (guestfs_h *g)
|
||
{
|
||
return g->trace;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_direct (guestfs_h *g, int d)
|
||
{
|
||
g->direct_mode = !!d;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_direct (guestfs_h *g)
|
||
{
|
||
return g->direct_mode;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_recovery_proc (guestfs_h *g, int f)
|
||
{
|
||
g->recovery_proc = !!f;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_recovery_proc (guestfs_h *g)
|
||
{
|
||
return g->recovery_proc;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_network (guestfs_h *g, int v)
|
||
{
|
||
g->enable_network = !!v;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_network (guestfs_h *g)
|
||
{
|
||
return g->enable_network;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_program (guestfs_h *g, const char *program)
|
||
{
|
||
free (g->program);
|
||
g->program = safe_strdup (g, program);
|
||
|
||
return 0;
|
||
}
|
||
|
||
const char *
|
||
guestfs_impl_get_program (guestfs_h *g)
|
||
{
|
||
return g->program;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_identifier (guestfs_h *g, const char *identifier)
|
||
{
|
||
size_t i, len;
|
||
|
||
/* Check the identifier contains only permitted characters. */
|
||
len = strlen (identifier);
|
||
for (i = 0; i < len; ++i) {
|
||
char c = identifier[i];
|
||
|
||
if (!c_isalnum (c) && c != '_' && c != '-') {
|
||
error (g, _("identifier must contain only alphanumeric characters, underscore or minus sign"));
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
free (g->identifier);
|
||
g->identifier = safe_strdup (g, identifier);
|
||
|
||
return 0;
|
||
}
|
||
|
||
const char *
|
||
guestfs_impl_get_identifier (guestfs_h *g)
|
||
{
|
||
return g->identifier;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_backend (guestfs_h *g, const char *method)
|
||
{
|
||
if (guestfs_int_set_backend (g, method) == -1) {
|
||
error (g, "invalid backend: %s", method);
|
||
return -1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_attach_method (guestfs_h *g, const char *method)
|
||
{
|
||
return guestfs_set_backend (g, method);
|
||
}
|
||
|
||
char *
|
||
guestfs_impl_get_backend (guestfs_h *g)
|
||
{
|
||
return safe_strdup (g, g->backend);
|
||
}
|
||
|
||
char *
|
||
guestfs_impl_get_attach_method (guestfs_h *g)
|
||
{
|
||
if (STREQ (g->backend, "direct"))
|
||
/* Return 'appliance' here for backwards compatibility. */
|
||
return safe_strdup (g, "appliance");
|
||
|
||
return guestfs_get_backend (g);
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_backend_settings (guestfs_h *g, char *const *settings)
|
||
{
|
||
char **copy;
|
||
|
||
copy = guestfs_int_copy_string_list (settings);
|
||
if (copy == NULL) {
|
||
perrorf (g, "copy: malloc");
|
||
return -1;
|
||
}
|
||
|
||
guestfs_int_free_string_list (g->backend_settings);
|
||
g->backend_settings = copy;
|
||
|
||
return 0;
|
||
}
|
||
|
||
char **
|
||
guestfs_impl_get_backend_settings (guestfs_h *g)
|
||
{
|
||
char *empty_list[1] = { NULL };
|
||
char **ret;
|
||
|
||
if (g->backend_settings == NULL)
|
||
ret = guestfs_int_copy_string_list (empty_list);
|
||
else
|
||
ret = guestfs_int_copy_string_list (g->backend_settings);
|
||
|
||
if (ret == NULL) {
|
||
perrorf (g, "copy: malloc");
|
||
return NULL;
|
||
}
|
||
|
||
return ret; /* caller frees */
|
||
}
|
||
|
||
char *
|
||
guestfs_impl_get_backend_setting (guestfs_h *g, const char *name)
|
||
{
|
||
char **settings = g->backend_settings;
|
||
const size_t namelen = strlen (name);
|
||
size_t i;
|
||
|
||
if (settings == NULL)
|
||
goto not_found;
|
||
|
||
for (i = 0; settings[i] != NULL; ++i) {
|
||
/* "name" is the same as "name=1" */
|
||
if (STREQ (settings[i], name))
|
||
return safe_strdup (g, "1");
|
||
/* "name=...", return value */
|
||
if (STRPREFIX (settings[i], name) && settings[i][namelen] == '=')
|
||
return safe_strdup (g, &settings[i][namelen+1]);
|
||
}
|
||
|
||
not_found:
|
||
guestfs_int_error_errno (g, ESRCH, _("setting not found"));
|
||
return NULL;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_clear_backend_setting (guestfs_h *g, const char *name)
|
||
{
|
||
char **settings = g->backend_settings;
|
||
const size_t namelen = strlen (name);
|
||
size_t i, j;
|
||
int count = 0;
|
||
|
||
if (settings == NULL)
|
||
return 0;
|
||
|
||
for (i = 0; settings[i] != NULL; ++i) {
|
||
if (STREQ (settings[i], name) ||
|
||
(STRPREFIX (settings[i], name) && settings[i][namelen] == '=')) {
|
||
count++;
|
||
free (settings[i]);
|
||
|
||
/* We move all the following strings down one place, including the NULL. */
|
||
for (j = i; settings[j] != NULL; ++j)
|
||
settings[j] = settings[j+1];
|
||
|
||
i--;
|
||
}
|
||
}
|
||
|
||
return count;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_backend_setting (guestfs_h *g, const char *name, const char *value)
|
||
{
|
||
char *new_setting;
|
||
size_t len;
|
||
|
||
new_setting = safe_asprintf (g, "%s=%s", name, value);
|
||
|
||
if (g->backend_settings == NULL) {
|
||
g->backend_settings = safe_malloc (g, sizeof (char *));
|
||
g->backend_settings[0] = NULL;
|
||
len = 0;
|
||
}
|
||
else {
|
||
ignore_value (guestfs_clear_backend_setting (g, name));
|
||
len = guestfs_int_count_strings (g->backend_settings);
|
||
}
|
||
|
||
g->backend_settings =
|
||
safe_realloc (g, g->backend_settings, (len+2) * sizeof (char *));
|
||
g->backend_settings[len++] = new_setting;
|
||
g->backend_settings[len++] = NULL;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* This is a convenience function, but we might consider exporting it
|
||
* as an API in future.
|
||
*/
|
||
int
|
||
guestfs_int_get_backend_setting_bool (guestfs_h *g, const char *name)
|
||
{
|
||
CLEANUP_FREE char *value = NULL;
|
||
int b;
|
||
|
||
guestfs_push_error_handler (g, NULL, NULL);
|
||
value = guestfs_get_backend_setting (g, name);
|
||
guestfs_pop_error_handler (g);
|
||
|
||
if (value == NULL && guestfs_last_errno (g) == ESRCH)
|
||
return 0;
|
||
|
||
if (value == NULL)
|
||
return -1;
|
||
|
||
b = guestfs_int_is_true (value);
|
||
if (b == -1)
|
||
return -1;
|
||
|
||
return b;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_pgroup (guestfs_h *g, int v)
|
||
{
|
||
g->pgroup = !!v;
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_pgroup (guestfs_h *g)
|
||
{
|
||
return g->pgroup;
|
||
}
|
||
|
||
int
|
||
guestfs_impl_set_smp (guestfs_h *g, int v)
|
||
{
|
||
if (v > 255) {
|
||
error (g, "unsupported number of smp vcpus: %d", v);
|
||
return -1;
|
||
} else if (v >= 1) {
|
||
g->smp = v;
|
||
return 0;
|
||
} else {
|
||
error (g, "invalid smp parameter: %d", v);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
int
|
||
guestfs_impl_get_smp (guestfs_h *g)
|
||
{
|
||
return g->smp;
|
||
}
|