From fe64a1d8529cdca5a22c3b8d0cccd96fe4374d94 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Thu, 12 Jan 2006 15:38:07 +0000 Subject: [PATCH] * src/Makefile.am src/xend_internal.c src/xend_internal.h: added more of Anthony Liquori libxend code, commented and reformatted this still need to be plugged, it's still dead code ATM. Daniel --- ChangeLog | 6 + src/Makefile.am | 3 +- src/xend_internal.c | 1965 +++++++++++++++++++++++++++++++++++++++++++ src/xend_internal.h | 904 ++++++++++++++++++++ 4 files changed, 2877 insertions(+), 1 deletion(-) create mode 100644 src/xend_internal.c create mode 100644 src/xend_internal.h diff --git a/ChangeLog b/ChangeLog index 57fac00c7f..9ff699d076 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Thu Jan 12 16:36:21 CET 2006 Daniel Veillard + + * src/Makefile.am src/xend_internal.c src/xend_internal.h: + added more of Anthony Liquori libxend code, commented and reformatted + this still need to be plugged, it's still dead code ATM. + Wed Jan 11 14:57:01 CET 2006 Daniel Veillard * docs/libvir.html: grammatical fix diff --git a/src/Makefile.am b/src/Makefile.am index 7a5e436fb5..1994ef0e87 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,8 +14,9 @@ libvir_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvir_sym.version \ libvir_la_SOURCES = \ libvir.c internal.h \ hash.c hash.h \ - xen_internal.c xen_internal.h \ xml.c \ + xen_internal.c xen_internal.h \ + xend_internal.c xend_internal.h \ sexpr.c sexpr.h bin_PROGRAMS=virsh diff --git a/src/xend_internal.c b/src/xend_internal.c new file mode 100644 index 0000000000..17bc17dd8e --- /dev/null +++ b/src/xend_internal.c @@ -0,0 +1,1965 @@ +/* + * xend_internal.c: access to Xen though the Xen Daemon interface + * + * Copyright (C) 2005 + * + * Anthony Liguori + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING.LIB in the main directory of this + * archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sexpr.h" +#include "xend_internal.h" + +/** + * xend_connection_type: + * + * The connection to the Xen Daemon can be done either though a normal TCP + * socket or a local domain direct connection. + */ +enum xend_connection_type { + XEND_DOMAIN, + XEND_TCP, +}; + +/** + * xend: + * + * Structure associated to a connection to a Xen daemon + */ +struct xend { + int len; + int type; + struct sockaddr *addr; + struct sockaddr_un addr_un; + struct sockaddr_in addr_in; +}; + +#define foreach(iterator, start) \ + for (_for_i = (start), *iterator = (start)->car; \ + _for_i->kind == SEXPR_CONS; \ + _for_i = _for_i->cdr, iterator = _for_i->car) + +#define foreach_node(iterator, start, path) \ + foreach(iterator, start) \ + if (sexpr_lookup(iterator, path)) + +/** + * do_connect: + * @xend: pointer to the Xen Daemon structure + * + * Internal routine to (re)connect to the daemon + * + * Returns the socket file descriptor or -1 in case of error + */ +static int +do_connect(struct xend *xend) +{ + int s; + int serrno; + + s = socket(xend->type, SOCK_STREAM, 0); + if (s == -1) + return -1; + + if (connect(s, xend->addr, xend->len) == -1) { + serrno = errno; + close(s); + errno = serrno; + s = -1; + } + + return s; +} + +/** + * wr_sync: + * @fd: the file descriptor + * @buffer: the I/O buffer + * @size: the size of the I/O + * @do_read: write operation if 0, read operation otherwise + * + * Do a synchronous read or write on the file descriptor + * + * Returns the number of bytes exchanged, or -1 in case of error + */ +static size_t +wr_sync(int fd, void *buffer, size_t size, int do_read) +{ + size_t offset = 0; + + while (offset < size) { + ssize_t len; + + if (do_read) { + len = read(fd, ((char *)buffer) + offset, size - offset); + } else { + len = write(fd, ((char *)buffer) + offset, size - offset); + } + + /* recoverable error, retry */ + if ((len == -1) && ((errno == EAGAIN) || (errno == EINTR))) { + continue; + } + + /* eof */ + if (len == 0) { + break; + } + + /* unrecoverable error */ + if (len == -1) { + return(-1); + } + + offset += len; + } + + return offset; +} + +/** + * sread: + * @fd: the file descriptor + * @buffer: the I/O buffer + * @size: the size of the I/O + * + * Internal routine to do a synchronous read + * + * Returns the number of bytes read, or -1 in case of error + */ +static ssize_t +sread(int fd, void *buffer, size_t size) +{ + return wr_sync(fd, buffer, size, 1); +} + +/** + * swrite: + * @fd: the file descriptor + * @buffer: the I/O buffer + * @size: the size of the I/O + * + * Internal routine to do a synchronous write + * + * Returns the number of bytes written, or -1 in case of error + */ +static ssize_t +swrite(int fd, const void *buffer, size_t size) +{ + return wr_sync(fd, (void *) buffer, size, 0); +} + +/** + * swrites: + * @fd: the file descriptor + * @string: the string to write + * + * Internal routine to do a synchronous write of a string + * + * Returns the number of bytes written, or -1 in case of error + */ +static ssize_t +swrites(int fd, const char *string) +{ + return swrite(fd, string, strlen(string)); +} + +/** + * sreads: + * @fd: the file descriptor + * @buffer: the I/O buffer + * @n_buffer: the size of the I/O buffer + * + * Internal routine to do a synchronous read of a line + * + * Returns the number of bytes read, or -1 in case of error + */ +static ssize_t +sreads(int fd, char *buffer, size_t n_buffer) +{ + size_t offset; + + if (n_buffer < 1) + return(-1); + + for (offset = 0; offset < (n_buffer - 1); offset++) { + ssize_t ret; + + ret = sread(fd, buffer + offset, 1); + if (ret == 0) + break; + else if (ret == -1) + return ret; + + if (buffer[offset] == '\n') { + offset++; + break; + } + } + buffer[offset] = 0; + + return offset; +} + +static int +istartswith(const char *haystack, const char *needle) +{ + return (strncasecmp(haystack, needle, strlen(needle)) == 0); +} + +/** + * xend_req: + * @fd: the file descriptor + * @content: the buffer to store the content + * @n_content: the size of the buffer + * + * Read the HTTP response from a Xen Daemon request. + * + * Returns the HTTP return code. + */ +static int +xend_req(int fd, char *content, size_t n_content) +{ + char buffer[4096]; + int content_length = -1; + int retcode = 0; + + + while (sreads(fd, buffer, sizeof(buffer)) > 0) { + if (strcmp(buffer, "\r\n") == 0) + break; + + if (istartswith(buffer, "Content-Length: ")) + content_length = atoi(buffer + 16); + else if (istartswith(buffer, "HTTP/1.1 ")) + retcode = atoi(buffer + 9); + } + + if (content_length > -1) { + ssize_t ret; + + if ((unsigned int) content_length > (n_content + 1)) + content_length = n_content - 1; + + ret = sread(fd, content, content_length); + if (ret < 0) + return -1; + + content[ret] = 0; + } else { + content[0] = 0; + } + + return retcode; +} + +/** + * xend_get: + * @xend: pointer to the Xen Daemon structure + * @path: the path used for the HTTP request + * @content: the buffer to store the content + * @n_content: the size of the buffer + * + * Do an HTTP GET RPC with the Xen Daemon + * + * Returns the HTTP return code or -1 in case or error. + */ +static int +xend_get(struct xend *xend, const char *path, + char *content, size_t n_content) +{ + int ret; + int s = do_connect(xend); + + if (s == -1) + return s; + + swrites(s, "GET "); + swrites(s, path); + swrites(s, " HTTP/1.1\r\n"); + + swrites(s, + "Host: localhost:8000\r\n" + "Accept-Encoding: identity\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" "\r\n"); + + ret = xend_req(s, content, n_content); + close(s); + + return ret; +} + +/** + * xend_post: + * @xend: pointer to the Xen Daemon structure + * @path: the path used for the HTTP request + * @ops: the informations sent for the POST + * @content: the buffer to store the content + * @n_content: the size of the buffer + * + * Do an HTTP POST RPC with the Xen Daemon, this usually makes changes at the + * Xen level. + * + * Returns the HTTP return code or -1 in case or error. + */ +static int +xend_post(struct xend *xend, const char *path, const char *ops, + char *content, size_t n_content) +{ + char buffer[100]; + int ret; + int s = do_connect(xend); + + if (s == -1) + return s; + + swrites(s, "POST "); + swrites(s, path); + swrites(s, " HTTP/1.1\r\n"); + + swrites(s, + "Host: localhost:8000\r\n" + "Accept-Encoding: identity\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: "); + snprintf(buffer, sizeof(buffer), "%d", strlen(ops)); + swrites(s, buffer); + swrites(s, "\r\n\r\n"); + swrites(s, ops); + + ret = xend_req(s, content, n_content); + close(s); + + return ret; +} + +/** + * http2unix: + * @ret: the http return code + * + * Convert the HTTP return code to 0/-1 and set errno if needed + * + * Return -1 in case of error code 0 otherwise + */ +static int +http2unix(int ret) +{ + switch (ret) { + case -1: + break; + case 200: + case 201: + case 202: + return 0; + case 404: + errno = ESRCH; + break; + default: + printf("unknown error code %d\n", ret); + errno = EINVAL; + break; + } + return -1; +} + +/** + * xend_op_ext2: + * @xend: pointer to the Xen Daemon structure + * @path: path for the object + * @error: buffer for the error output + * @n_error: size of @error + * @key: the key for the operation + * @ap: input values to pass to the operation + * + * internal routine to run a POST RPC operation to the Xen Daemon + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xend_op_ext2(struct xend *xend, const char *path, char *error, + size_t n_error, const char *key, va_list ap) +{ + char ops[1024]; + const char *k = key, *v; + int offset = 0; + + while (k) { + v = va_arg(ap, const char *); + + offset += snprintf(ops + offset, sizeof(ops) - offset, "%s", k); + offset += snprintf(ops + offset, sizeof(ops) - offset, "%s", "="); + offset += snprintf(ops + offset, sizeof(ops) - offset, "%s", v); + k = va_arg(ap, const char *); + + if (k) + offset += snprintf(ops + offset, + sizeof(ops) - offset, "%s", "&"); + } + + return http2unix(xend_post(xend, path, ops, error, n_error)); +} + +/** + * xend_node_op: + * @xend: pointer to the Xen Daemon structure + * @path: path for the object + * @key: the key for the operation + * @...: input values to pass to the operation + * + * internal routine to run a POST RPC operation to the Xen Daemon + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xend_node_op(struct xend *xend, const char *path, const char *key, ...) +{ + va_list ap; + int ret; + char error[1024]; + + va_start(ap, key); + ret = xend_op_ext2(xend, path, error, sizeof(error), key, ap); + va_end(ap); + + return ret; +} + +/** + * xend_node_op: + * @xend: pointer to the Xen Daemon structure + * @name: the domain name target of this operation + * @error: buffer for the error output + * @n_error: size of @error + * @key: the key for the operation + * @ap: input values to pass to the operation + * @...: input values to pass to the operation + * + * internal routine to run a POST RPC operation to the Xen Daemon targetting + * a given domain. + * + * Returns 0 in case of success, -1 in case of failure. + */ +static int +xend_op_ext(struct xend *xend, const char *name, char *error, + size_t n_error, const char *key, ...) +{ + char buffer[1024]; + va_list ap; + int ret; + + snprintf(buffer, sizeof(buffer), "/xend/domain/%s", name); + + va_start(ap, key); + ret = xend_op_ext2(xend, buffer, error, n_error, key, ap); + va_end(ap); + + return ret; +} + +#define xend_op(xend, name, key, ...) ({char error[1024]; xend_op_ext(xend, name, error, sizeof(error), key, __VA_ARGS__);}) + +/** + * sexpr_get: + * @xend: pointer to the Xen Daemon structure + * @fmt: format string for the path of the operation + * @...: extra data to build the path of the operation + * + * Internal routine to run a simple GET RPC operation to the Xen Daemon + * + * Returns a parsed S-Expression in case of success, NULL in case of failure + */ +static struct sexpr * +sexpr_get(struct xend *xend, const char *fmt, ...) +{ + char buffer[4096]; + char path[1024]; + va_list ap; + int ret; + + va_start(ap, fmt); + vsnprintf(path, sizeof(path), fmt, ap); + va_end(ap); + + ret = xend_get(xend, path, buffer, sizeof(buffer)); + ret = http2unix(ret); + if (ret == -1) + return NULL; + + return string2sexpr(buffer); +} + +/** + * sexpr_append_str: + * @sexpr: an S-Expression + * @name: the name of the property + * @value: the string value + * + * convenience function appending a (name value) property to the S-Expression + * + * Returns the augmented S-Expression + */ +static struct sexpr * +sexpr_append_str(struct sexpr *sexpr, const char *name, const char *value) +{ + struct sexpr *lst; + + if (!value) + return sexpr; + + lst = sexpr_append(sexpr_nil(), sexpr_string(name, -1)); + lst = sexpr_append(lst, sexpr_string(value, -1)); + return sexpr_append(sexpr, lst); +} + +/** + * sexpr_append_u64: + * @sexpr: an S-Expression + * @name: the name of the property + * @value: a 64 bits unsigned int value + * + * convenience function appending a (name value) property to the S-Expression + * + * Returns the augmented S-Expression + */ +static struct sexpr * +sexpr_append_u64(struct sexpr *sexpr, const char *name, uint64_t value) +{ + char buffer[1024]; + + snprintf(buffer, sizeof(buffer), "%llu", value); + return sexpr_append_str(sexpr, name, buffer); +} + +/** + * sexpr_append_int: + * @sexpr: an S-Expression + * @name: the name of the property + * @value: an int value + * + * convenience function appending a (name value) property to the S-Expression + * + * Returns the augmented S-Expression + */ +static struct sexpr * +sexpr_append_int(struct sexpr *sexpr, const char *name, int value) +{ + char buffer[1024]; + + snprintf(buffer, sizeof(buffer), "%d", value); + return sexpr_append_str(sexpr, name, buffer); +} + +/** + * sexpr_append_uuid: + * @sexpr: an S-Expression + * @name: the name of the property + * @uuid: an unique identifier for a domain + * + * convenience function appending a (name uuid) property to the S-Expression + * + * Returns the augmented S-Expression + */ +static struct sexpr * +sexpr_append_uuid(struct sexpr *sexpr, + const char *name, unsigned char *uuid) +{ + char buffer[1024]; + + if (uuid == NULL) + return sexpr; + + snprintf(buffer, sizeof(buffer), + "%02x%02x%02x%02x-" + "%02x%02x%02x%02x-" + "%02x%02x%02x%02x-" + "%02x%02x%02x%02x", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + + return sexpr_append_str(sexpr, name, buffer); +} + +/** + * sexpr_int: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup an int value in the S-Expression + * + * Returns the value found or 0 if not found (but may not be an error) + */ +static int +sexpr_int(struct sexpr *sexpr, const char *name) +{ + const char *value = sexpr_node(sexpr, name); + + if (value) { + return strtol(value, NULL, 0); + } + return 0; +} + +/** + * sexpr_float: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup a float value in the S-Expression + * + * Returns the value found or 0 if not found (but may not be an error) + */ +static double +sexpr_float(struct sexpr *sexpr, const char *name) +{ + const char *value = sexpr_node(sexpr, name); + + if (value) { + return strtod(value, NULL); + } + return 0; +} + +/** + * sexpr_u64: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup a 64bits unsigned int value in the + * S-Expression + * + * Returns the value found or 0 if not found (but may not be an error) + */ +static uint64_t +sexpr_u64(struct sexpr *sexpr, const char *name) +{ + const char *value = sexpr_node(sexpr, name); + + if (value) { + return strtoll(value, NULL, 0); + } + return 0; +} + +/** + * sexpr_u64: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup a value describing the default process when + * a domain stops + * + * Returns the value found or 0 if not found (but may not be an error) + */ +static enum xend_domain_restart +sexpr_poweroff(struct sexpr *sexpr, const char *name) +{ + const char *value = sexpr_node(sexpr, name); + + if (value) { + if (strcmp(value, "poweroff") == 0) { + return XEND_DESTROY; + } else if (strcmp(value, "restart") == 0) { + return XEND_RESTART; + } else if (strcmp(value, "preserve") == 0) { + return XEND_PRESERVE; + } else if (strcmp(value, "rename-restart") == 0) { + return XEND_RENAME_RESTART; + } + } + return XEND_DEFAULT; +} + +static int +sexpr_strlen(struct sexpr *sexpr, const char *path) +{ + const char *r = sexpr_node(sexpr, path); + + return r ? (strlen(r) + 1) : 0; +} + +static const char * +sexpr_strcpy(char **ptr, struct sexpr *node, const char *path) +{ + const char *ret = sexpr_node(node, path); + + if (ret) { + strcpy(*ptr, ret); + ret = *ptr; + *ptr += (strlen(ret) + 1); + } + return ret; +} + +/** + * sexpr_mode: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup a value describing a virtual block device + * mode from the S-Expression + * + * Returns the value found or 0 if not found (but may not be an error) + */ +static enum xend_device_vbd_mode +sexpr_mode(struct sexpr *node, const char *path) +{ + const char *mode = sexpr_node(node, path); + enum xend_device_vbd_mode ret; + + if (!mode) { + ret = XEND_DEFAULT; + } else if (strcmp(mode, "r") == 0) { + ret = XEND_READ_ONLY; + } else if (strcmp(mode, "w") == 0) { + ret = XEND_READ_WRITE; + } else if (strcmp(mode, "w!") == 0) { + ret = XEND_READ_WRITE_FORCE; + } else { + ret = XEND_DEFAULT; + } + + return ret; +} + +/** + * sexpr_node_system: + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup a value describing the kind of system + * from the S-Expression + * + * Returns the value found or 0 if not found (but may not be an error) + */ +static enum xend_node_system +sexpr_node_system(struct sexpr *node, const char *path) +{ + const char *syst = sexpr_node(node, path); + + if (syst) { + if (strcmp(syst, "Linux") == 0) { + return XEND_SYSTEM_LINUX; + } + } + + return XEND_DEFAULT; +} + +/** + * sexpr_node_system: + * @mac: return value for the MAC address + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup a MAC address (assumed ethernet and hence + * six bytes in length) from the S-Expression + * The value is returned in @mac + */ +static void +sexpr_mac(uint8_t * mac, struct sexpr *node, const char *path) +{ + const char *r = sexpr_node(node, path); + int mmac[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + if (r) { + int i; + + sscanf(r, "%02x:%02x:%02x:%02x:%02x:%02x", + mmac + 0, mmac + 1, mmac + 2, mmac + 3, mmac + 4, mmac + 5); + for (i = 0; i < 6; i++) + mac[i] = mmac[i] & 0xFF; + } +} + +/** + * sexpr_uuid: + * @ptr: where to store the UUID, incremented + * @sexpr: an S-Expression + * @name: the name for the value + * + * convenience function to lookup an UUID value from the S-Expression + * + * Returns a pointer to the stored UUID or NULL in case of error. + */ +static unsigned char * +sexpr_uuid(char **ptr, struct sexpr *node, const char *path) +{ + const char *r = sexpr_node(node, path); + int uuid[16]; + unsigned char *dst_uuid = NULL; + int ret; + int i; + + memset(uuid, 0xFF, sizeof(uuid)); + + if (r == NULL) + goto error; + + ret = sscanf(r, + "%02x%02x%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + uuid + 0, uuid + 1, uuid + 2, uuid + 3, + uuid + 4, uuid + 5, uuid + 6, uuid + 7, + uuid + 8, uuid + 9, uuid + 10, uuid + 11, + uuid + 12, uuid + 13, uuid + 14, uuid + 15); + if (ret == 16) + goto done; + + ret = sscanf(r, + "%02x%02x%02x%02x-" + "%02x%02x%02x%02x-" + "%02x%02x%02x%02x-" + "%02x%02x%02x%02x", + uuid + 0, uuid + 1, uuid + 2, uuid + 3, + uuid + 4, uuid + 5, uuid + 6, uuid + 7, + uuid + 8, uuid + 9, uuid + 10, uuid + 11, + uuid + 12, uuid + 13, uuid + 14, uuid + 15); + if (ret != 16) + goto error; + + done: + dst_uuid = (unsigned char *) *ptr; + *ptr += 16; + + for (i = 0; i < 16; i++) + dst_uuid[i] = uuid[i] & 0xFF; + + error: + return dst_uuid; +} + + +/** + * urlencode: + * @string: the input URL + * + * Encode an URL see RFC 2396 and following + * + * Returns the new string or NULL in case of error. + */ +static char * +urlencode(const char *string) +{ + size_t len = strlen(string); + char *buffer = malloc(len * 3 + 1); + char *ptr = buffer; + size_t i; + + if (buffer == NULL) + return(NULL); + for (i = 0; i < len; i++) { + switch (string[i]) { + case ' ': + case '\n': + sprintf(ptr, "%%%02x", string[i]); + ptr += 3; + break; + default: + *ptr = string[i]; + ptr++; + } + } + + *ptr = 0; + + return buffer; +} + +/** + * xend_device_vbd_to_sexpr: + * @vbd: a virtual block device pointer + * + * Encode a virtual block device as an S-Expression understood by the + * Xen Daemon + * + * Returns the result S-Expression pointer + */ +static struct sexpr * +xend_device_vbd_to_sexpr(const struct xend_device_vbd *vbd) +{ + struct sexpr *device = sexpr_nil(); + const char *mode[] = { NULL, "r", "w", "w!" }; + + sexpr_append(device, sexpr_string("vbd", -1)); + sexpr_append_str(device, "dev", vbd->dev); + sexpr_append_str(device, "uname", vbd->uname); + sexpr_append_int(device, "backend", vbd->backend); + sexpr_append_str(device, "mode", mode[vbd->mode]); + + return device; +} + +/** + * xend_device_vif_to_sexpr: + * @vif: a virtual network interface pointer + * + * Encode a virtual network interface as an S-Expression understood by the + * Xen Daemon + * + * Returns the result S-Expression pointer + */ +static struct sexpr * +xend_device_vif_to_sexpr(const struct xend_device_vif *vif) +{ + struct sexpr *device; + char buffer[1024]; + + device = sexpr_append(sexpr_nil(), sexpr_string("vif", -1)); + + snprintf(buffer, sizeof(buffer), + "%02x:%02x:%02x:%02x:%02x:%02x", + vif->mac[0], vif->mac[1], vif->mac[2], + vif->mac[3], vif->mac[4], vif->mac[5]); + device = sexpr_append_str(device, "mac", buffer); + device = sexpr_append_int(device, "backend", vif->backend); + device = sexpr_append_str(device, "bridge", vif->bridge); + device = sexpr_append_str(device, "ip", vif->ip); + device = sexpr_append_str(device, "script", vif->script); + device = sexpr_append_str(device, "vifname", vif->vifname); + + return device; +} + +/* PUBLIC FUNCTIONS */ + +/** + * xend_new_unix: + * @path: the path for the Xen Daemon socket + * + * Creates a localhost Xen Daemon connection + * Note: this doesn't try to check if the connection actually works + * + * Returns the new pointer or NULL in case of error. + */ +struct xend * +xend_new_unix(const char *path) +{ + struct xend *xend = malloc(sizeof(*xend)); + struct sockaddr_un *addr; + + if (!xend) + return NULL; + + addr = &xend->addr_un; + addr->sun_family = AF_UNIX; + memset(addr->sun_path, 0, sizeof(addr->sun_path)); + strncpy(addr->sun_path, path, sizeof(addr->sun_path)); + + xend->len = sizeof(addr->sun_family) + strlen(addr->sun_path); + if ((unsigned int) xend->len > sizeof(addr->sun_path)) + xend->len = sizeof(addr->sun_path); + + xend->addr = (struct sockaddr *) addr; + xend->type = PF_UNIX; + + return xend; +} + +/** + * xend_new_unix: + * @host: the host name for the Xen Daemon + * @port: the port + * + * Creates a possibly remote Xen Daemon connection + * Note: this doesn't try to check if the connection actually works + * + * Returns the new pointer or NULL in case of error. + */ +struct xend * +xend_new_tcp(const char *host, int port) +{ + struct xend *xend = malloc(sizeof(*xend)); + struct in_addr ip; + struct hostent *pent; + + if (!xend) + return NULL; + + pent = gethostbyname(host); + if (pent == NULL) { + if (inet_aton(host, &ip) == 0) { + errno = ESRCH; + return NULL; + } + } else { + memcpy(&ip, pent->h_addr_list[0], sizeof(ip)); + } + + xend->len = sizeof(struct sockaddr_in); + xend->addr = (struct sockaddr *) &xend->addr_in; + xend->type = PF_INET; + + xend->addr_in.sin_family = AF_INET; + xend->addr_in.sin_port = htons(port); + memcpy(&xend->addr_in.sin_addr, &ip, sizeof(ip)); + + return xend; +} + +/** + * xend_new: + * + * Creates a localhost Xen Daemon connection + * Note: this doesn't try to check if the connection actually works + * + * Returns the new pointer or NULL in case of error. + */ +struct xend * +xend_new(void) +{ + return xend_new_unix("/var/lib/xend/xend-socket"); +} + +/** + * xend_delete: + * + * Free a Xen Daemon connection + */ +void +xend_delete(struct xend *xend) +{ + free(xend); +} + +/** + * xend_wait_for_devices: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * + * Block the domain until all the virtual devices are ready. This operation + * is needed when creating a domain before resuming it. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_wait_for_devices(struct xend *xend, const char *name) +{ + return xend_op(xend, name, "op", "wait_for_devices", NULL); +} + +/** + * xend_pause: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * + * Pause the domain, the domain is not scheduled anymore though its resources + * are preserved. Use xend_unpause() to resume execution. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_pause(struct xend *xend, const char *name) +{ + return xend_op(xend, name, "op", "pause", NULL); +} + +/** + * xend_unpause: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * + * Resume the domain after xend_pause() has been called + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_unpause(struct xend *xend, const char *name) +{ + return xend_op(xend, name, "op", "unpause", NULL); +} + +/** + * xend_rename: + * @xend: pointer to the Xem Daemon block + * @old: old name for the domain + * @new: new name for the domain + * + * Rename the domain + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_rename(struct xend *xend, const char *old, const char *new) +{ + if ((xend == NULL) || (old == NULL) || (new == NULL)) + return(-1); + return xend_op(xend, old, "op", "rename", "name", new, NULL); +} + +/** + * xend_reboot: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * + * Reboot the domain, the OS is properly shutdown and restarted + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_reboot(struct xend *xend, const char *name) +{ + if ((xend == NULL) || (name == NULL)) + return(-1); + return xend_op(xend, name, "op", "shutdown", "reason", "reboot", NULL); +} + +/** + * xend_shutdown: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * + * Shutdown the domain, the OS is properly shutdown and the resources allocated + * for the domain are freed. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_shutdown(struct xend *xend, const char *name) +{ + if ((xend == NULL) || (name == NULL)) + return(-1); + return xend_op(xend, name, + "op", "shutdown", "reason", "shutdown", NULL); +} + +/** + * xend_sysrq: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * @key: the SysReq key + * + * Send a SysReq key which is used to debug Linux kernels running in the domain + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_sysrq(struct xend *xend, const char *name, const char *key) +{ + if ((xend == NULL) || (name == NULL) || (key == NULL)) + return(-1); + return xend_op(xend, name, "op", "sysrq", "key", key, NULL); +} + +/** + * xend_destroy: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * + * Abruptly halt the domain, the OS is not properly shutdown and the + * resources allocated for the domain are immediately freed, mounted + * filesystems will be marked as uncleanly shutdown. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_destroy(struct xend *xend, const char *name) +{ + if ((xend == NULL) || (name == NULL)) + return(-1); + return xend_op(xend, name, "op", "destroy", NULL); +} + +/** + * xend_save: + * @xend: pointer to the Xem Daemon block + * @name: name for the domain + * @filename: path for the output file + * + * This method will suspend a domain and save its memory contents to + * a file on disk. Use xend_restore() to restore a domain after + * saving. + * Note that for remote Xen Daemon the file path will be interpreted in + * the remote host. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_save(struct xend *xend, const char *name, const char *filename) +{ + if ((xend == NULL) || (filename == NULL)) + return(-1); + return xend_op(xend, name, "op", "save", "file", filename, NULL); +} + +/** + * xend_restore: + * @xend: pointer to the Xem Daemon block + * @filename: path for the output file + * + * This method will restore a domain saved to disk by xend_save(). + * Note that for remote Xen Daemon the file path will be interpreted in + * the remote host. + * + * Returns 0 in case of success, -1 (with errno) in case of error. + */ +int +xend_restore(struct xend *xend, const char *filename) +{ + if ((xend == NULL) || (filename == NULL)) + return(-1); + return xend_op(xend, "", "op", "restore", "file", filename, NULL); +} + +/** + * xend_get_domains: + * @xend: pointer to the Xem Daemon block + * + * This method will return an array of names of currently running + * domains. The memory should be released will a call to free(). + * + * Returns a list of names or NULL in case of error. + */ +char ** +xend_get_domains(struct xend *xend) +{ + size_t extra = 0; + struct sexpr *root = NULL; + char **ret = NULL; + int count = 0; + int i; + char *ptr; + struct sexpr *_for_i, *node; + + root = sexpr_get(xend, "/xend/domain"); + if (root == NULL) + goto error; + + for (_for_i = root, node = root->car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->cdr, node = _for_i->car) { + if (node->kind != SEXPR_VALUE) + continue; + extra += strlen(node->value) + 1; + count++; + } + + ptr = malloc((count + 1) * sizeof(char *) + extra); + if (ptr == NULL) + goto error; + + ret = (char **) ptr; + ptr += sizeof(char *) * (count + 1); + + i = 0; + for (_for_i = root, node = root->car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->cdr, node = _for_i->car) { + if (node->kind != SEXPR_VALUE) + continue; + ret[i] = ptr; + strcpy(ptr, node->value); + ptr += strlen(node->value) + 1; + i++; + } + + ret[i] = NULL; + + error: + sexpr_free(root); + return ret; +} + +/** + * xend_domain_to_sexpr: + * @domain: a domain pointer + * + * Internal function converting the domain informations into an S-Expression + * that the Xen Daemon can use to create instances + * + * Returns the new S-Expression or NULL in case of error. + */ +static struct sexpr * +xend_domain_to_sexpr(const struct xend_domain *domain) +{ + struct sexpr *lst = sexpr_nil(); + struct sexpr *image = sexpr_nil(); + struct sexpr *builder = sexpr_nil(); + const char *restart[] = { NULL, "restart", + "preserve", "rename-restart" + }; + size_t i; + + if (domain == NULL) + return(NULL); + + lst = sexpr_append(lst, sexpr_string("vm", -1)); + lst = sexpr_append_str(lst, "name", domain->name); + lst = sexpr_append_uuid(lst, "uuid", domain->uuid); + lst = sexpr_append_u64(lst, "memory", domain->memory); + lst = sexpr_append_int(lst, "ssidref", domain->ssidref); + lst = sexpr_append_u64(lst, "maxmem", domain->max_memory); + lst = + sexpr_append_str(lst, "on_poweroff", restart[domain->on_poweroff]); + lst = sexpr_append_str(lst, "on_reboot", restart[domain->on_reboot]); + lst = sexpr_append_str(lst, "on_crash", restart[domain->on_crash]); + lst = sexpr_append_int(lst, "vcpus", domain->vcpus); + + builder = sexpr_append(builder, sexpr_string("linux", -1)); + builder = sexpr_append_str(builder, "kernel", domain->image.kernel); + builder = sexpr_append_str(builder, "ramdisk", domain->image.ramdisk); + builder = sexpr_append_str(builder, "root", domain->image.root); + builder = sexpr_append_str(builder, "args", domain->image.extra); + + image = sexpr_append(image, sexpr_string("image", -1)); + image = sexpr_append(image, builder); + + lst = sexpr_append(lst, image); + + for (i = 0; i < domain->n_vbds; i++) { + struct sexpr *device = sexpr_nil(); + struct sexpr *vbd = xend_device_vbd_to_sexpr(&domain->vbds[i]); + + device = sexpr_append(device, sexpr_string("device", -1)); + device = sexpr_append(device, vbd); + lst = sexpr_append(lst, device); + } + + for (i = 0; i < domain->n_vifs; i++) { + struct sexpr *device = sexpr_nil(); + struct sexpr *vif = xend_device_vif_to_sexpr(&domain->vifs[i]); + + device = sexpr_append(device, sexpr_string("device", -1)); + device = sexpr_append(device, vif); + lst = sexpr_append(lst, device); + } + + for (i = 0; i < domain->n_ioports; i++) { + struct sexpr *device = sexpr_nil(); + struct sexpr *ioport = sexpr_nil(); + + ioport = sexpr_append(ioport, sexpr_string("ioports", -1)); + ioport = sexpr_append_int(ioport, "from", domain->ioports[i].from); + ioport = sexpr_append_int(ioport, "to", domain->ioports[i].to); + + device = sexpr_append(device, sexpr_string("device", -1)); + device = sexpr_append(device, ioport); + lst = sexpr_append(lst, device); + } + + return lst; +} + +/** + * xend_create: + * @xend: A xend instance + * @info: A struct xen_domain instance describing the domain + * + * This method will create a domain based the passed in description. The + * domain will be paused after creation and must be unpaused with + * xend_unpause() to begin execution. + * + * Returns 0 for success, -1 (with errno) on error + */ + +int +xend_create(struct xend *xend, const struct xend_domain *dom) +{ + int ret, serrno; + struct sexpr *sexpr; + char buffer[4096]; + char *ptr; + + sexpr = xend_domain_to_sexpr(dom); + sexpr2string(sexpr, buffer, sizeof(buffer)); + ptr = urlencode(buffer); + + ret = xend_op(xend, "", "op", "create", "config", ptr, NULL); + + serrno = errno; + free(ptr); + sexpr_free(sexpr); + errno = serrno; + + return ret; +} + +/** + * xend_set_max_memory: + * @xend: A xend instance + * @name: The name of the domain + * @value: The maximum memory in bytes + * + * This method will set the maximum amount of memory that can be allocated to + * a domain. Please note that a domain is able to allocate up to this amount + * on its own (although under normal circumstances, memory allocation for a + * domain is only done through xend_set_memory()). + * + * Returns 0 for success; -1 (with errno) on error + */ +int +xend_set_max_memory(struct xend *xend, const char *name, uint64_t value) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "%llu", value >> 20); + return xend_op(xend, name, "op", "maxmem_set", "memory", buf, NULL); +} + +/** + * xend_set_memory: + * @xend: A xend instance + * @name: The name of the domain + * @value: The desired allocation in bytes + * + * This method will set a target memory allocation for a given domain and + * request that the guest meet this target. The guest may or may not actually + * achieve this target. When this function returns, it does not signify that + * the domain has actually reached that target. + * + * Memory for a domain can only be allocated up to the maximum memory setting. + * There is no safe guard for allocations that are too small so be careful + * when using this function to reduce a domain's memory usage. + * + * Returns 0 for success; -1 (with errno) on error + */ +int +xend_set_memory(struct xend *xend, const char *name, uint64_t value) +{ + char buf[1024]; + + snprintf(buf, sizeof(buf), "%llu", value >> 20); + return xend_op(xend, name, "op", "mem_target_set", "target", buf, + NULL); +} + +/** + * xend_vbd_create: + * @xend: A xend instance + * @name: The name of the domain + * @vbd: A virtual block device description + * + * This method creates and attachs a block device to a domain. A successful + * return value does not indicate that the device successfully attached, + * rather, one should use xend_wait_for_devices() to block until the device + * has been successfully attached. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_vbd_create(struct xend *xend, + const char *name, const struct xend_device_vbd *vbd) +{ + char buffer[4096]; + char *ptr; + int ret; + struct sexpr *device; + + device = xend_device_vbd_to_sexpr(vbd); + + sexpr2string(device, buffer, sizeof(buffer)); + ptr = urlencode(buffer); + + ret = xend_op(xend, name, "op", "device_create", "config", ptr, NULL); + + sexpr_free(device); + + return ret; +} + +/** + * xend_vbd_destroy: + * @xend: A xend instance + * @name: The name of the domain + * @vbd: A virtual block device description + * + * This method detachs a block device from a given domain. A successful return + * value does not indicate that the device successfully detached, rather, one + * should use xend_wait_for_devices() to block until the device has been + * successfully detached. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_vbd_destroy(struct xend *xend, + const char *name, const struct xend_device_vbd *vbd) +{ + return xend_op(xend, name, "op", "device_destroy", "type", "vbd", + "dev", vbd->dev, NULL); +} + +/** + * xend_vif_create: + * @xend: A xend instance + * @name: The name of the domain + * @vif: A virtual network device description + * + * This method creates and attachs a network device to a domain. A successful + * return value does not indicate that the device successfully attached, + * rather, one should use xend_wait_for_devices() to network until the device + * has been successfully attached. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_vif_create(struct xend *xend, + const char *name, const struct xend_device_vif *vif) +{ + char buffer[4096]; + char *ptr; + int ret; + struct sexpr *device; + + device = xend_device_vif_to_sexpr(vif); + + sexpr2string(device, buffer, sizeof(buffer)); + ptr = urlencode(buffer); + + ret = xend_op(xend, name, "op", "device_create", "config", ptr, NULL); + + sexpr_free(device); + + return ret; +} + +static int +get_vif_handle(struct xend *xend, + const char *name, + const struct xend_device_vif *vif, + char *buffer, size_t n_buffer) +{ + struct sexpr *root; + char path[4096]; + int ret = -1; + struct sexpr *_for_i, *node; + + root = sexpr_get(xend, "/xend/domain"); + if (root == NULL) + goto error; + + errno = ESRCH; + + for (_for_i = root, node = root->car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->cdr, node = _for_i->car) { + uint8_t mac[6]; + + if (node->car->kind != SEXPR_VALUE) + continue; + + snprintf(path, sizeof(path), "%s/mac", node->car->value); + sexpr_mac(mac, node, path); + if (memcmp(mac, vif->mac, 6) == 0) { + snprintf(buffer, n_buffer, "%s", node->car->value); + ret = 0; + break; + } + } + + error: + sexpr_free(root); + return ret; + +} + +/** + * xend_vif_destroy: + * @xend: A xend instance + * @name: The name of the domain + * @vif: A virtual network device description + * + * This method detachs a network device from a given domain. A successful + * return value does not indicate that the device successfully detached, + * rather, one should use xend_wait_for_devices() to network until the device + * has been successfully detached. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_vif_destroy(struct xend *xend, + const char *name, const struct xend_device_vif *vif) +{ + char handle[1024]; + + if (get_vif_handle(xend, name, vif, handle, sizeof(handle)) == -1) + return -1; + + return xend_op(xend, name, "op", "device_destroy", "type", "vif", + "dev", handle, NULL); +} + +/** + * sexpr_to_xend_domain_size: + * @sexpr: the S-Expression + * @n_vbds: the number of virtual block devices used (OUT) + * @n_vifs: the number of network interface devices used (OUT) + * + * Helper function to compute the size in byte needed for the strings + * of a domain. + * + * Returns the number of bytes and the output parameters + */ +static size_t +sexpr_to_xend_domain_size(struct sexpr *root, int *n_vbds, int *n_vifs) +{ + size_t size = 0; + struct sexpr *_for_i, *node; + + size += sexpr_strlen(root, "domain/name"); + size += sexpr_strlen(root, "domain/image/linux/kernel"); + size += sexpr_strlen(root, "domain/image/linux/ramdisk"); + size += sexpr_strlen(root, "domain/image/linux/root"); + size += sexpr_strlen(root, "domain/image/linux/args"); + if (sexpr_node(root, "domain/uuid")) + size += 16; + + for (_for_i = root, node = root->car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->cdr, node = _for_i->car) { + if (sexpr_lookup(node, "device/vbd")) { + size += sexpr_strlen(node, "device/vbd/dev"); + size += sexpr_strlen(node, "device/vbd/uname"); + (*n_vbds)++; + } + } + + for (_for_i = root, node = root->car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->cdr, node = _for_i->car) { + if (sexpr_lookup(node, "device/vif")) { + size += sexpr_strlen(node, "device/vif/bridge"); + size += sexpr_strlen(node, "device/vif/ip"); + size += sexpr_strlen(node, "device/vif/script"); + size += sexpr_strlen(node, "device/vif/vifname"); + (*n_vifs)++; + } + } + + size += (*n_vbds) * sizeof(struct xend_device_vbd *); + size += (*n_vbds) * sizeof(struct xend_device_vbd); + + size += (*n_vifs) * sizeof(struct xend_device_vif *); + size += (*n_vifs) * sizeof(struct xend_device_vif); + + size += sizeof(struct xend_domain_live); + + size += sizeof(struct xend_domain); + + return size; +} + +/** + * sexpr_to_xend_domain: + * @root: an S-Expression describing a domain + * + * Internal routine creating a domain node based on the S-Expression + * provided by the Xen Daemon + * + * Returns a new structure or NULL in case of error. + */ +static struct xend_domain * +sexpr_to_xend_domain(struct sexpr *root) +{ + struct xend_domain *dom = NULL; + char *ptr; + int i; + int n_vbds = 0; + int n_vifs = 0; + struct sexpr *_for_i, *node; + + ptr = malloc(sexpr_to_xend_domain_size(root, &n_vbds, &n_vifs)); + if (ptr == NULL) + goto error; + + dom = (struct xend_domain *) ptr; + ptr += sizeof(struct xend_domain); + + dom->vbds = (struct xend_device_vbd *) ptr; + dom->n_vbds = n_vbds; + ptr += n_vbds * sizeof(struct xend_device_vbd); + + dom->vifs = (struct xend_device_vif *) ptr; + dom->n_vifs = n_vifs; + ptr += n_vifs * sizeof(struct xend_device_vif); + + dom->live = (struct xend_domain_live *) ptr; + ptr += sizeof(struct xend_domain_live); + + dom->name = sexpr_strcpy(&ptr, root, "domain/name"); + dom->uuid = sexpr_uuid(&ptr, root, "domain/uuid"); + dom->image.kernel = + sexpr_strcpy(&ptr, root, "domain/image/linux/kernel"); + dom->image.ramdisk = + sexpr_strcpy(&ptr, root, "domain/image/linux/ramdisk"); + dom->image.root = sexpr_strcpy(&ptr, root, "domain/image/linux/root"); + dom->image.extra = sexpr_strcpy(&ptr, root, "domain/image/linux/args"); + dom->memory = sexpr_u64(root, "domain/memory") << 20; + dom->max_memory = sexpr_u64(root, "domain/maxmem") << 20; + dom->ssidref = sexpr_int(root, "domain/ssidref"); + dom->on_poweroff = sexpr_poweroff(root, "domain/on_poweroff"); + dom->on_reboot = sexpr_poweroff(root, "domain/on_reboot"); + dom->on_crash = sexpr_poweroff(root, "domain/on_crash"); + dom->vcpus = sexpr_int(root, "domain/vcpus"); + + { + const char *flags = sexpr_node(root, "domain/state"); + + if (flags) { + dom->live->running = strchr(flags, 'r'); + dom->live->crashed = strchr(flags, 'c'); + dom->live->blocked = strchr(flags, 'b'); + dom->live->dying = strchr(flags, 'd'); + dom->live->paused = strchr(flags, 'p'); + dom->live->poweroff = false; + dom->live->reboot = false; + dom->live->suspend = false; + if (strchr(flags, 's') && + (flags = sexpr_node(root, "domain/shutdown_reason"))) { + if (strcmp(flags, "poweroff") == 0) { + dom->live->poweroff = true; + } else if (strcmp(flags, "reboot") == 0) { + dom->live->reboot = true; + } else if (strcmp(flags, "suspend") == 0) { + dom->live->suspend = true; + } + } + } + } + + dom->live->cpu_time = sexpr_float(root, "domain/cpu_time"); + dom->live->up_time = sexpr_float(root, "domain/up_time"); + dom->live->start_time = sexpr_float(root, "domain/start_time"); + dom->live->online_vcpus = sexpr_int(root, "domain/online_vcpus"); + dom->live->vcpu_avail = sexpr_int(root, "domain/vcpu_avail"); + + i = 0; + for (_for_i = root, node = root->car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->cdr, node = _for_i->car) { + if (sexpr_lookup(node, "device/vbd")) { + dom->vbds[i].dev = sexpr_strcpy(&ptr, node, "device/vbd/dev"); + dom->vbds[i].uname = sexpr_strcpy(&ptr, node, "device/vbd/uname"); + dom->vbds[i].backend = sexpr_int(node, "device/vbd/backend"); + dom->vbds[i].mode = sexpr_mode(node, "device/vbd/mode"); + i++; + } + } + + i = 0; + for (_for_i = root, node = root->car; _for_i->kind == SEXPR_CONS; + _for_i = _for_i->cdr, node = _for_i->car) { + if (sexpr_lookup(node, "device/vif")) { + dom->vifs[i].backend = sexpr_int(node, "device/vif/backend"); + dom->vifs[i].bridge = + sexpr_strcpy(&ptr, node, "device/vif/bridge"); + dom->vifs[i].ip = sexpr_strcpy(&ptr, node, "device/vif/ip"); + sexpr_mac(dom->vifs[i].mac, node, "device/vif/mac"); + dom->vifs[i].script = + sexpr_strcpy(&ptr, node, "device/vif/script"); + dom->vifs[i].vifname = + sexpr_strcpy(&ptr, node, "device/vif/vifname"); + i++; + } + } + +error: + return dom; +} + +/** + * xend_get_domain: + * @xend: A xend instance + * @name: The name of the domain + * + * This method looks up information about a domain and returns + * it in the form of a struct xend_domain. This should be + * free()'d when no longer needed. + * + * Returns domain info on success; NULL (with errno) on error + */ +struct xend_domain * +xend_get_domain(struct xend *xend, const char *domname) +{ + struct sexpr *root; + struct xend_domain *dom = NULL; + + root = sexpr_get(xend, "/xend/domain/%s?detail=1", domname); + if (root == NULL) + goto error; + + dom = sexpr_to_xend_domain(root); + + error: + sexpr_free(root); + return dom; +} + +/** + * xend_get_node: + * @xend: A xend instance + * + * This method returns information about the physical host + * machine running Xen. + * + * Returns node info on success; NULL (with errno) on error + */ +struct xend_node * +xend_get_node(struct xend *xend) +{ + struct sexpr *root; + struct xend_node *node = NULL; + size_t size; + char *ptr; + + root = sexpr_get(xend, "/xend/node/"); + if (root == NULL) + goto error; + + size = sizeof(struct xend_node); + size += sexpr_strlen(root, "node/host"); + size += sexpr_strlen(root, "node/release"); + size += sexpr_strlen(root, "node/version"); + size += sexpr_strlen(root, "node/machine"); + size += sexpr_strlen(root, "node/hw_caps"); + size += sexpr_strlen(root, "node/xen_caps"); + size += sexpr_strlen(root, "node/platform_params"); + size += sexpr_strlen(root, "node/xen_changeset"); + size += sexpr_strlen(root, "node/cc_compiler"); + size += sexpr_strlen(root, "node/cc_compile_by"); + size += sexpr_strlen(root, "node/cc_compile_domain"); + size += sexpr_strlen(root, "node/cc_compile_date"); + + ptr = malloc(size); + if (ptr == NULL) + goto error; + + node = (struct xend_node *) ptr; + ptr += sizeof(struct xend_node); + + node->system = sexpr_node_system(root, "node/system"); + node->host = sexpr_strcpy(&ptr, root, "node/host"); + node->release = sexpr_strcpy(&ptr, root, "node/release"); + node->version = sexpr_strcpy(&ptr, root, "node/version"); + node->machine = sexpr_strcpy(&ptr, root, "node/machine"); + node->nr_cpus = sexpr_int(root, "node/nr_cpus"); + node->nr_nodes = sexpr_int(root, "node/nr_nodes"); + node->sockets_per_node = sexpr_int(root, "node/sockets_per_node"); + node->cores_per_socket = sexpr_int(root, "node/cores_per_socket"); + node->threads_per_core = sexpr_int(root, "node/threads_per_core"); + node->cpu_mhz = sexpr_int(root, "node/cpu_mhz"); + node->hw_caps = sexpr_strcpy(&ptr, root, "node/hw_caps"); + node->total_memory = sexpr_u64(root, "node/total_memory") << 12; + node->free_memory = sexpr_u64(root, "node/free_memory") << 12; + node->xen_major = sexpr_int(root, "node/xen_major"); + node->xen_minor = sexpr_int(root, "node/xen_minor"); + { + const char *tmp; + + tmp = sexpr_node(root, "node/xen_extra"); + if (tmp) { + if (*tmp == '.') + tmp++; + node->xen_extra = atoi(tmp); + } else { + node->xen_extra = 0; + } + } + node->xen_caps = sexpr_strcpy(&ptr, root, "node/xen_caps"); + node->platform_params = + sexpr_strcpy(&ptr, root, "node/platform_params"); + node->xen_changeset = sexpr_strcpy(&ptr, root, "node/xen_changeset"); + node->cc_compiler = sexpr_strcpy(&ptr, root, "node/cc_compiler"); + node->cc_compile_by = sexpr_strcpy(&ptr, root, "node/cc_compile_by"); + node->cc_compile_domain = + sexpr_strcpy(&ptr, root, "node/cc_compile_domain"); + node->cc_compile_date = + sexpr_strcpy(&ptr, root, "node/cc_compile_date"); + + error: + sexpr_free(root); + return node; +} + +/** + * xend_node_shutdown: + * @xend: A xend instance + * + * This method shuts down the physical machine running Xen. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_node_shutdown(struct xend *xend) +{ + return xend_node_op(xend, "/xend/node/", "op", "shutdown", NULL); +} + +/** + * xend_node_restart: + * @xend: A xend instance + * + * This method restarts the physical machine running Xen. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_node_restart(struct xend *xend) +{ + return xend_node_op(xend, "/xend/node/", "op", "restart", NULL); +} + +/** + * xend_dmesg: + * @xend: A xend instance + * @buffer: A buffer to hold the messages + * @n_buffer: Size of buffer (including null terminator) + * + * This function will place the debugging messages from the + * hypervisor into a buffer with a null terminator. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_dmesg(struct xend *xend, char *buffer, size_t n_buffer) +{ + return http2unix(xend_get(xend, "/xend/node/dmesg", buffer, n_buffer)); +} + +/** + * xend_dmesg_clear: + * @xend: A xend instance + * + * This function will clear the debugging message ring queue + * in the hypervisor. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_dmesg_clear(struct xend *xend) +{ + return xend_node_op(xend, "/xend/node/dmesg", "op", "clear", NULL); +} + +/** + * xend_log: + * @xend: A xend instance + * @buffer: The buffer to hold the messages + * @n_buffer: Size of buffer (including null terminator) + * + * This function will place the Xend debugging messages into + * a buffer with a null terminator. + * + * Returns 0 on success; -1 (with errno) on error + */ +int +xend_log(struct xend *xend, char *buffer, size_t n_buffer) +{ + return http2unix(xend_get(xend, "/xend/node/log", buffer, n_buffer)); +} diff --git a/src/xend_internal.h b/src/xend_internal.h new file mode 100644 index 0000000000..97c41980f3 --- /dev/null +++ b/src/xend_internal.h @@ -0,0 +1,904 @@ +/* + * libxend/xend.h -- Xend library + * + * Copyright (C) 2005 + * + * Anthony Liguori + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#ifndef _LIBXEND_XEND_H_ +#define _LIBXEND_XEND_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Use the default setting as determined by Xend. +*/ +#define XEND_DEFAULT 0 + +/** + Flags that determine the permission to expose a device to the guest as. +*/ +enum xend_device_vbd_mode +{ + /** + Expose the device as read only. + */ + XEND_READ_ONLY = 1, + + /** + Expose the device as read/write with an in-use check. + + If Xend thinks the device is already in use, it will generate an + error. It uses heuristics so it will not always catch every + instance of this and will sometimes generate false positives. + */ + XEND_READ_WRITE, + + /** + Expose the device as read/only without an in-use check. + */ + XEND_READ_WRITE_FORCE, +}; + +/** + Flags that determine the action to take on a shutdown or crash. +*/ +enum xend_domain_restart +{ + /** + Destroy the domain. + */ + XEND_DESTROY = 1, + + /** + Restart the domain. + */ + XEND_RESTART, + + /** + Take no action. The domain will have to be manually destroyed by + the user. Useful for debugging. + */ + XEND_PRESERVE, + + /** + Rename the domain to something unique and then create a new instance + of the domain. Useful for debugging crashes while avoiding + down time. + */ + XEND_RENAME_RESTART, +}; + +/** + Xend context. + + Private. +*/ +struct xend; + +/** + This structure the image information for a guest. +*/ +struct xend_image +{ + /** + A filename pointing to a paravirtual Xen kernel. + + Required. + */ + const char *kernel; + + /** + A filename pointing to an initrd. + + Optional + */ + const char *ramdisk; + + /** + The root block device. + + Optional + */ + const char *root; + + /** + The kernel command line. + + Optional. + */ + const char *extra; +}; + +/** + This structure represents a virtual block device. +*/ +struct xend_device_vbd +{ + /** + The domain ID of the backend. + + Required. + */ + int backend; + + /** + A URI representing the device. This is typically in the form + file:/path/to/image or phy:/dev/device + + Required. + */ + const char *uname; + + /** + The name (or number) of the device to expose to the frontend. + + Required. + */ + const char *dev; + + /** + A flag specifying the permissions to expose the device with. + + Required. + */ + enum xend_device_vbd_mode mode; +}; + +/** + This structure represents a range of PIO to enable for a guest. +*/ +struct xend_device_ioport +{ + /** + The beginning address of an ioport range to enable. + + Required. + */ + uint16_t from; + + /** + The ending address of an ioport range to enable. + + Required. + */ + uint16_t to; +}; + +/** + This structure represents a virtual network interface configuration. +*/ +struct xend_device_vif +{ + /** + A string representing the domain that will serve as the backend for + this device. + + Required. + */ + int backend; + + /** + The name of the bridge device to pass to the network script. + + Optional. + */ + const char *bridge; + + /** + The ip address to configure the virtal network device with. + + Optional. + */ + const char *ip; + + /** + The mac address to use for the virtual network device. + + Required. + */ + uint8_t mac[6]; + + /** + The path to the network script that is to be used for initializing + the network device. + + Optional. + */ + const char *script; + + /** + The name of the vif. The primary use for this is to allow the user + to operate on vifs by name. + + Optional. + */ + const char *vifname; +}; + +struct xend_domain_live +{ + /** + true is domain is currently scheduled. + */ + bool running; + + /** + true is domain has crashed. + */ + bool crashed; + + /** + true if domain has been shutdown. + */ + bool poweroff; + + /** + true if domain has requested a reboot. + */ + bool reboot; + + /** + true if domain has requested a suspend. + */ + bool suspend; + + /** + true if domain is blocked on IO + */ + bool blocked; + + /** + true if domain has been destroyed but resources are not + fully deallocated. + */ + bool dying; + + /** + true if domain is paused. + */ + bool paused; + + /** + the amount of time the domain has been running (in seconds) + */ + double cpu_time; + + /** + the wall clock time since the domain was created (in seconds) + */ + double up_time; + + /** + the time (in seconds since epoch) the domain was created + */ + double start_time; + + /** + the number of enabled VCPUs + */ + int online_vcpus; + + /** + the total number of available VCPUs + */ + int vcpu_avail; +}; + +/** + This structure represents the configuration of a domain. It's primary + purpose (currently) is for domain creation. +*/ +struct xend_domain +{ + /** + The name of the domain. + + Required. + */ + const char *name; + + /** + The amount of memory to assign to the domain before creation. + + Required. + */ + uint64_t memory; + + /** + The maximum amount of memory that can be given to the domain + while it's running. Please note that a domain can increase its + memory on its own while running up to this value. + + Required. + */ + uint64_t max_memory; + + /** + The uuid to use to identify the domain. This is compatible with + libuuid's uuid_t and should be able to be used interchangably. + + Optional. + */ + unsigned char *uuid; + + /** + The ssidref to assign to the domain. + + Optional. + */ + int ssidref; + + /** + The action to perform when the domain powers off. + + Optional. + */ + enum xend_domain_restart on_poweroff; + + /** + The action to perform when the domain reboots. + + Optional. + */ + enum xend_domain_restart on_reboot; + + /** + The action to perform when the domain crashes. + + Optional. + */ + enum xend_domain_restart on_crash; + + /** + The number of VCPUs to assign to the domain. + + Required. + */ + int vcpus; + + /* FIXME cpus */ + + struct xend_image image; + + /** + The number of VBDs pointed to be vbds. + + Optional. + */ + size_t n_vbds; + struct xend_device_vbd *vbds; + + /** + The number of IO port ranges pointed to by ioports. + + Optional. + */ + size_t n_ioports; + struct xend_device_ioport *ioports; + + /** + The number of VIFs pointed to be vifs. + + Optional. + */ + size_t n_vifs; + struct xend_device_vif *vifs; + + /** + A pointer to run-time information about the domain. + + Only set by xen_get_domain(). + */ + struct xend_domain_live *live; +}; + +enum xend_node_system +{ + XEND_SYSTEM_LINUX = 1, +}; + +struct xend_node +{ + /** + An enumeration value specifying the host system. + */ + enum xend_node_system system; + + /** + The DNS host name. + */ + const char *host; + + /** + The dom0 kernel release string. + */ + const char *release; + + /** + The result of uname -v. + */ + const char *version; + + /** + The machine type. + */ + const char *machine; + + /** + The number of physical cpus. + */ + int nr_cpus; + + /** + The number of NUMA nodes. + */ + int nr_nodes; + + /** + The number of sockets per NUMA node. + */ + int sockets_per_node; + + /** + The number of cores per NUMA socket. + */ + int cores_per_socket; + + /** + The number of hyperthreads per core. + */ + int threads_per_core; + + /** + The clock rating (in megahertz) of each core. + */ + int cpu_mhz; + + /** + I honestly don't know what this is. + */ + const char *hw_caps; + + /** + The total memory (in bytes). + */ + uint64_t total_memory; + + /** + The free memory (in bytes). + */ + uint64_t free_memory; + + /** + The Xen major version number. + */ + int xen_major; + + /** + The Xen minor version number. + */ + int xen_minor; + + /** + The Xen extra version number. + */ + int xen_extra; + + /** + A string descirbing the Xen platform. + */ + const char *xen_caps; + + /** + Dunno. + */ + const char *platform_params; + + /** + The build changeset. + */ + const char *xen_changeset; + + /** + The compiler version. + */ + const char *cc_compiler; + + /** + The user that compiled this binary. + */ + const char *cc_compile_by; + + /** + The system this binary was built on. + */ + const char *cc_compile_domain; + + /** + The date that this binary was built on. + */ + const char *cc_compile_date; +}; + +/** + * \brief Allocate a new Xend instance + * \return A new xend instance + * + * This method creates a new Xend instance preferrably trying + * to connect with the domain socket but if necessary using + * TCP (only on localhost though). + * + * This function may not fail if Xend is not running. + */ +struct xend *xend_new(void); + +/** + * \brief Creates a new xend instance via TCP + * \param host The host name to connect to + * \param port The port number to connect to + * \return A new xend instance + * + * This method creates a new Xend instance via TCP. + * + * This function may not fail if Xend is not running. + * + * Make sure to call xen_delete(). + */ +struct xend *xend_new_tcp(const char *host, int port); + +/** + * \brief Creates a new xend instance via a Unix domain socket + * \param path The path to the domain socket + * \return A new xend instance + * + * This method creates a new xend instance via a Unix domain socket. + * + * This function may not fail if Xend is not running. + * + * Make sure to call xen_delete(). + */ +struct xend *xend_new_unix(const char *path); + +/** + * \brief Delete a previously allocated Xend instance + * \param xend The xend instance + * + * This method should be called when a xend instance + * allocated with xend_new[_{tcp, unix}] is no longer needed + * to free the associated resources. + */ +void xend_delete(struct xend *xend); + +/** + * \brief Blocks until a domain's devices are initialized + * \param xend A xend instance + * \param name The domain's name + * \return 0 for success; -1 (with errno) on error + * + * xen_create() returns after a domain has been allocated including + * its memory. This does not guarentee, though, that the devices + * have come up properly. For instance, if you create a VBD with an + * invalid filename, the error won't occur until after this function + * returns. + */ +int xend_wait_for_devices(struct xend *xend, const char *name); + +/** + * \brief Pause a domain + * \param xend A xend instance + * \param name The domain's name + * \return 0 for success; -1 (with errno) on error + * + * This method will make sure that Xen does not schedule the domain + * anymore until after xend_unpause() has been called. + */ +int xend_pause(struct xend *xend, const char *name); + +/** + * \brief Unpause a domain + * \param xend A xend instance + * \param name The domain's name + * \return 0 for success; -1 (with errno) on error + * + * This method will allow a paused domain (the result of xen_pause()) + * to be scheduled in the future. + */ +int xend_unpause(struct xend *xend, const char *name); + +/** + * \brief Unpause a domain + * \param xend A xend instance + * \param oldname The domain's name + * \param name The new name + * \return 0 for success; -1 (with errno) on error + * + * This method allows a domain to have its name changed after creation. + */ +int xend_rename(struct xend *xend, const char *oldname, const char *name); + +/** + * \brief Sends a SYSRQ to a domain + * \param xend A xend instance + * \param name The domain's name + * \param key The key that was held during the SYSRQ + * \return 0 for success; -1 (with errno) on error + * + * This method simulates the pressing of a SYSRQ sequence. + */ +int xend_sysrq(struct xend *xend, const char *name, const char *key); + +/** + * \brief Request a domain to reboot + * \param xend A xend instance + * \param name The domain's name + * \return 0 for success; -1 (with errno) on error + * + * This method *requests* that a domain reboot itself. This is only + * a request and the domain may ignore it. It will return immediately + * after queuing the request. + */ +int xend_reboot(struct xend *xend, const char *name); + +/** + * \brief Request a domain to shutdown + * \param xend A xend instance + * \param name The domain's name + * \return 0 for success; -1 (with errno) on error + * + * This method *requests* that a domain shutdown itself. This is only + * a request and the domain may ignore it. It will return immediately + * after queuing the request. + */ +int xend_shutdown(struct xend *xend, const char *name); + +/** + * \brief Destroy a domain + * \param xend A xend instance + * \param name The domain's name + * \return 0 for success; -1 (with errno) on error + * + * This method will immediately destroy a domain. If you call this + * function while a domain is running, you risk corrupting its devices. + * After calling this function, the domain's status will change to + * dying and will go away completely once all of the resources have been + * unmapped (usually from the backend devices). + */ +int xend_destroy(struct xend *xend, const char *name); + +/** + * \brief Save a domain to the disk + * \param xend A xend instance + * \param name The domain's name + * \param filename The filename to save to + * \return 0 for success; -1 (with errno) on error + * + * This method will suspend a domain and save its memory contents to + * a file on disk. Use xend_restore() to restore a domain after + * saving. + */ +int xend_save(struct xend *xend, const char *name, const char *filename); + +/** + * \brief Restore a domain from the disk + * \param xend A xend instance + * \param filename The filename to restore from + * \return 0 for success; -1 (with errno) on error + * + * This method will restore a domain saved to disk by xend_save(). + */ +int xend_restore(struct xend *xend, const char *filename); + +/** + * \brief Obtain a list of currently running domains + * \param xend A xend instance + * \return a NULL terminated array of names; NULL (with errno) on error + * + * This method will return an array of names of currently running + * domains. The memory should be released will a call to free(). + */ +char **xend_get_domains(struct xend *xend); + +/** + * \brief Create a new domain + * \param xend A xend instance + * \param info A struct xen_domain instance describing the domain + * \return 0 for success; -1 (with errno) on error + * + * This method will create a domain based the passed in description. The + * domain will be paused after creation and must be unpaused with + * xend_unpause() to begin execution. + */ +int xend_create(struct xend *xend, const struct xend_domain *info); + +/** + * \brief Set the maximum memory for a domain + * \param xend A xend instance + * \param name The name of the domain + * \param value The maximum memory in bytes + * \return 0 for success; -1 (with errno) on error + * + * This method will set the maximum amount of memory that can be allocated to + * a domain. Please note that a domain is able to allocate up to this amount + * on its own (although under normal circumstances, memory allocation for a + * domain is only done through xend_set_memory()). + */ +int xend_set_max_memory(struct xend *xend, const char *name, uint64_t value); + +/** + * \brief Set the memory allocation for a domain + * \param xend A xend instance + * \param name The name of the domain + * \param value The desired allocation in bytes + * \return 0 for success; -1 (with errno) on error + * + * This method will set a target memory allocation for a given domain and + * request that the guest meet this target. The guest may or may not actually + * achieve this target. When this function returns, it does not signify that + * the domain has actually reached that target. + * + * Memory for a domain can only be allocated up to the maximum memory setting. + * There is no safe guard for allocations that are too small so be careful + * when using this function to reduce a domain's memory usage. + */ +int xend_set_memory(struct xend *xend, const char *name, uint64_t value); + +/** + * \brief Create a virtual block device + * \param xend A xend instance + * \param name The name of the domain + * \param vbd A virtual block device description + * \return 0 on success; -1 (with errno) on error + * + * This method creates and attachs a block device to a domain. A successful + * return value does not indicate that the device successfully attached, + * rather, one should use xend_wait_for_devices() to block until the device + * has been successfully attached. + */ +int xend_vbd_create(struct xend *xend, + const char *name, + const struct xend_device_vbd *vbd); + +/** + * \brief Destroy a virtual block device + * \param xend A xend instance + * \param name The name of the domain + * \param vbd A virtual block device description + * \return 0 on success; -1 (with errno) on error + * + * This method detachs a block device from a given domain. A successful return + * value does not indicate that the device successfully detached, rather, one + * should use xend_wait_for_devices() to block until the device has been + * successfully detached. + */ +int xend_vbd_destroy(struct xend *xend, + const char *name, + const struct xend_device_vbd *vbd); + +/** + * \brief Create a virtual network device + * \param xend A xend instance + * \param name The name of the domain + * \param vif A virtual network device description + * \return 0 on success; -1 (with errno) on error + * + * This method creates and attachs a network device to a domain. A successful + * return value does not indicate that the device successfully attached, + * rather, one should use xend_wait_for_devices() to network until the device + * has been successfully attached. + */ +int xend_vif_create(struct xend *xend, + const char *name, + const struct xend_device_vif *vif); + +/** + * \brief Destroy a virtual network device + * \param xend A xend instance + * \param name The name of the domain + * \param vif A virtual network device description + * \return 0 on success; -1 (with errno) on error + * + * This method detachs a network device from a given domain. A successful + * return value does not indicate that the device successfully detached, + * rather, one should use xend_wait_for_devices() to network until the device + * has been successfully detached. + */ +int xend_vif_destroy(struct xend *xend, + const char *name, + const struct xend_device_vif *vif); + +/** + * \brief Lookup information about a domain + * \param xend A xend instance + * \param name The name of the domain + * \return domain info on success; NULL (with errno) on error + * + * This method looks up information about a domain and returns + * it in the form of a struct xend_domain. This should be + * free()'d when no longer needed. + */ +struct xend_domain *xend_get_domain(struct xend *xend, + const char *name); + +/** + * \brief Lookup information about the host machine + * \param xend A xend instance + * \return node info on success; NULL (with errno) on error + * + * This method returns information about the physical host + * machine running Xen. + */ +struct xend_node *xend_get_node(struct xend *xend); + +/** + * \brief Shutdown physical host machine + * \param xend A xend instance + * \return 0 on success; -1 (with errno) on error + * + * This method shuts down the physical machine running Xen. + */ +int xend_node_shutdown(struct xend *xend); + +/** + * \brief Restarts physical host machine + * \param xend A xend instance + * \return 0 on success; -1 (with errno) on error + * + * This method restarts the physical machine running Xen. + */ +int xend_node_restart(struct xend *xend); + +/** + * \brief Return hypervisor debugging messages + * \param xend A xend instance + * \param buffer A buffer to hold the messages + * \param n_buffer Size of buffer (including null terminator) + * \return 0 on success; -1 (with errno) on error + * + * This function will place the debugging messages from the + * hypervisor into a buffer with a null terminator. + */ +int xend_dmesg(struct xend *xend, + char *buffer, + size_t n_buffer); + +/** + * \brief Clear the hypervisor debugging messages + * \param xend A xend instance + * \return 0 on success; -1 (with errno) on error + * + * This function will clear the debugging message ring queue + * in the hypervisor. + */ +int xend_dmesg_clear(struct xend *xend); + +/** + * \brief Obtain the Xend log messages + * \param xend A xend instance + * \param buffer The buffer to hold the messages + * \param n_buffer Size of buffer (including null terminator) + * \return 0 on success; -1 (with errno) on error + * + * This function will place the Xend debugging messages into + * a buffer with a null terminator. + */ +int xend_log(struct xend *xend, + char *buffer, + size_t n_buffer); + +#ifdef __cplusplus +} +#endif + +#endif