Patchwork [RFC,v7,05/16] virtagent: common helpers and init routines

login
register
mail settings
Submitter Michael Roth
Date March 7, 2011, 8:10 p.m.
Message ID <1299528642-23631-6-git-send-email-mdroth@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/85813/
State New
Headers show

Comments

Michael Roth - March 7, 2011, 8:10 p.m.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 virtagent-common.c |  206 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 virtagent-common.h |   95 ++++++++++++++++++++++++
 2 files changed, 301 insertions(+), 0 deletions(-)
 create mode 100644 virtagent-common.c
 create mode 100644 virtagent-common.h
Jes Sorensen - March 9, 2011, 10:38 a.m.
On 03/07/11 21:10, Michael Roth wrote:
> +#define VA_PIDFILE "/var/run/qemu-va.pid"
> +#define VA_HDR_LEN_MAX 4096 /* http header limit */
> +#define VA_CONTENT_LEN_MAX 2*1024*1024 /* rpc/http send limit */
> +#define VA_CLIENT_JOBS_MAX 5 /* max client rpcs we can queue */
> +#define VA_SERVER_JOBS_MAX 5 /* max server rpcs we can queue */
> +#define VA_SERVER_TIMEOUT_MS 5 * 1000
> +#define VA_CLIENT_TIMEOUT_MS 5 * 1000
> +#define VA_SENTINEL 0xFF
> +#define VA_BAUDRATE B38400 /* for isa-serial channels */
> +

I've been after these before - please put the ones that make sense to
tune into a config file, and the same with the pidfile.

Cheers,
Jes
Michael Roth - March 9, 2011, 1:17 p.m.
On 03/09/2011 04:38 AM, Jes Sorensen wrote:
> On 03/07/11 21:10, Michael Roth wrote:
>> +#define VA_PIDFILE "/var/run/qemu-va.pid"
>> +#define VA_HDR_LEN_MAX 4096 /* http header limit */
>> +#define VA_CONTENT_LEN_MAX 2*1024*1024 /* rpc/http send limit */
>> +#define VA_CLIENT_JOBS_MAX 5 /* max client rpcs we can queue */
>> +#define VA_SERVER_JOBS_MAX 5 /* max server rpcs we can queue */
>> +#define VA_SERVER_TIMEOUT_MS 5 * 1000
>> +#define VA_CLIENT_TIMEOUT_MS 5 * 1000
>> +#define VA_SENTINEL 0xFF
>> +#define VA_BAUDRATE B38400 /* for isa-serial channels */
>> +
>
> I've been after these before - please put the ones that make sense to
> tune into a config file, and the same with the pidfile.

I think my contention last time was that most of these weren't meant to 
be tweakable by an end-user, they're mainly just to avoid using magic 
numbers everywhere.

For stuff that is, like the pid file and socket/port paths, these would 
be the defaults, and the option to override them would be provided via 
the command line (virtagent chardev options on the host, command options 
on the guest).

I did plan to make the distinction between the 2 clearer though, by 
adding a DEFAULT_* or something along that line. Will get those in for 
the next pass.

>
> Cheers,
> Jes
>
>

Patch

diff --git a/virtagent-common.c b/virtagent-common.c
new file mode 100644
index 0000000..4b13ee8
--- /dev/null
+++ b/virtagent-common.c
@@ -0,0 +1,206 @@ 
+/*
+ * virtagent - common host/guest functions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ *  Adam Litke        <aglitke@linux.vnet.ibm.com>
+ *  Michael Roth      <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "virtagent-common.h"
+
+VAState *va_state;
+
+/* helper to avoid tedious key/type checking on QDict entries */
+bool va_qdict_haskey_with_type(const QDict *qdict, const char *key,
+                               qtype_code type)
+{
+    QObject *qobj;
+    if (!qdict) {
+        return false;
+    }
+    if (!qdict_haskey(qdict, key)) {
+        return false;
+    }
+    qobj = qdict_get(qdict, key);
+    if (qobject_type(qobj) != type) {
+        return false;
+    }
+
+    return true;
+}
+
+static void va_qdict_insert(const char *key, QObject *entry, void *opaque)
+{
+    QDict *dict = opaque;
+
+    if (key && entry) {
+        qdict_put_obj(dict, key, entry);
+    }
+}
+
+QDict *va_qdict_copy(const QDict *old)
+{
+    QDict *new;
+
+    if (!old) {
+        return NULL;
+    }
+
+    new = qdict_new();
+    qdict_iter(old, va_qdict_insert, new);
+
+    return new;
+}
+
+static int va_connect(void)
+{
+    QemuOpts *opts;
+    int fd, ret = 0;
+
+    TRACE("called");
+    if (va_state->channel_method == NULL) {
+        LOG("no channel method specified");
+        return -EINVAL;
+    }
+    if (va_state->channel_path == NULL) {
+        LOG("no channel path specified");
+        return -EINVAL;
+    }
+
+    if (strcmp(va_state->channel_method, "unix-connect") == 0) {
+        TRACE("connecting to %s", va_state->channel_path);
+        opts = qemu_opts_create(qemu_find_opts("chardev"), NULL, 0);
+        qemu_opt_set(opts, "path", va_state->channel_path);
+        fd = unix_connect_opts(opts);
+        if (fd == -1) {
+            qemu_opts_del(opts);
+            LOG("error opening channel: %s", strerror(errno));
+            return -errno;
+        }
+        qemu_opts_del(opts);
+        socket_set_nonblock(fd);
+    } else if (strcmp(va_state->channel_method, "virtio-serial") == 0) {
+        if (va_state->is_host) {
+            LOG("specified channel method not available for host");
+            return -EINVAL;
+        }
+        if (va_state->channel_path == NULL) {
+            va_state->channel_path = VA_GUEST_PATH_VIRTIO_DEFAULT;
+        }
+        TRACE("opening %s", va_state->channel_path);
+        fd = qemu_open(va_state->channel_path, O_RDWR);
+        if (fd == -1) {
+            LOG("error opening channel: %s", strerror(errno));
+            return -errno;
+        }
+        ret = fcntl(fd, F_GETFL);
+        if (ret < 0) {
+            LOG("error getting channel flags: %s", strerror(errno));
+            return -errno;
+        }
+        ret = fcntl(fd, F_SETFL, ret | O_ASYNC | O_NONBLOCK);
+        if (ret < 0) {
+            LOG("error setting channel flags: %s", strerror(errno));
+            return -errno;
+        }
+    } else if (strcmp(va_state->channel_method, "isa-serial") == 0) {
+        struct termios tio;
+        if (va_state->is_host) {
+            LOG("specified channel method not available for host");
+            return -EINVAL;
+        }
+        if (va_state->channel_path == NULL) {
+            LOG("you must specify the path of the serial device to use");
+            return -EINVAL;
+        }
+        TRACE("opening %s", va_state->channel_path);
+        fd = qemu_open(va_state->channel_path, O_RDWR | O_NOCTTY);
+        if (fd == -1) {
+            LOG("error opening channel: %s", strerror(errno));
+            return -errno;
+        }
+        tcgetattr(fd, &tio);
+        /* set up serial port for non-canonical, dumb byte streaming */
+        tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
+                         INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | IMAXBEL);
+        tio.c_oflag = 0;
+        tio.c_lflag = 0;
+        tio.c_cflag |= VA_BAUDRATE;
+        /* 1 available byte min, else reads will block (we'll set non-blocking
+         * elsewhere, else we'd have to deal with read()=0 instead)
+         */
+        tio.c_cc[VMIN] = 1;
+        tio.c_cc[VTIME] = 0;
+        /* flush everything waiting for read/xmit, it's garbage at this point */
+        tcflush(fd, TCIFLUSH);
+        tcsetattr(fd, TCSANOW, &tio);
+    } else {
+        LOG("invalid channel method");
+        return -EINVAL;
+    }
+
+    va_state->fd = fd;
+    return 0;
+}
+
+int va_init(VAContext ctx)
+{
+    VAState *s;
+    VAManager *m;
+    int ret;
+
+    TRACE("called");
+    if (va_state) {
+        LOG("virtagent already initialized");
+        return -EPERM;
+    }
+
+    s = qemu_mallocz(sizeof(VAState));
+    m = va_manager_new();
+
+    ret = va_server_init(m, &s->server_data, ctx.is_host);
+    if (ret) {
+        LOG("error initializing virtagent server");
+        goto out_bad;
+    }
+    ret = va_client_init(m, &s->client_data);
+    if (ret) {
+        LOG("error initializing virtagent client");
+        goto out_bad;
+    }
+
+    s->client_job_count = 0;
+    s->client_jobs_in_flight = 0;
+    s->server_job_count = 0;
+    s->channel_method = ctx.channel_method;
+    s->channel_path = ctx.channel_path;
+    s->is_host = ctx.is_host;
+    s->manager = m;
+    va_state = s;
+
+    /* connect to our end of the channel */
+    ret = va_connect();
+    if (ret) {
+        LOG("error connecting to channel");
+        goto out_bad;
+    }
+
+    /* start listening for requests/responses */
+    qemu_set_fd_handler(va_state->fd, va_http_read_handler, NULL, NULL);
+
+    if (!va_state->is_host) {
+        /* tell the host the agent is running */
+        va_send_hello();
+    }
+
+    return 0;
+out_bad:
+    qemu_free(s);
+    return ret;
+}
diff --git a/virtagent-common.h b/virtagent-common.h
new file mode 100644
index 0000000..5ae50d1
--- /dev/null
+++ b/virtagent-common.h
@@ -0,0 +1,95 @@ 
+/*
+ * virt-agent - host/guest RPC client functions
+ *
+ * Copyright IBM Corp. 2010
+ *
+ * Authors:
+ *  Adam Litke        <aglitke@linux.vnet.ibm.com>
+ *  Michael Roth      <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#ifndef VIRTAGENT_COMMON_H
+#define VIRTAGENT_COMMON_H
+
+#include <termios.h>
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "qemu-timer.h"
+#include "monitor.h"
+#include "virtagent-manager.h"
+#include "virtagent-server.h"
+#include "virtagent.h"
+
+#define DEBUG_VA
+
+#ifdef DEBUG_VA
+#define TRACE(msg, ...) do { \
+    fprintf(stderr, "%s:%s():L%d: " msg "\n", \
+            __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
+} while(0)
+#else
+#define TRACE(msg, ...) \
+    do { } while (0)
+#endif
+
+#define LOG(msg, ...) do { \
+    fprintf(stderr, "%s:%s(): " msg "\n", \
+            __FILE__, __FUNCTION__, ## __VA_ARGS__); \
+} while(0)
+
+#define VA_VERSION "1.0"
+#define EOL "\r\n"
+
+#define VA_PIDFILE "/var/run/qemu-va.pid"
+#define VA_HDR_LEN_MAX 4096 /* http header limit */
+#define VA_CONTENT_LEN_MAX 2*1024*1024 /* rpc/http send limit */
+#define VA_CLIENT_JOBS_MAX 5 /* max client rpcs we can queue */
+#define VA_SERVER_JOBS_MAX 5 /* max server rpcs we can queue */
+#define VA_SERVER_TIMEOUT_MS 5 * 1000
+#define VA_CLIENT_TIMEOUT_MS 5 * 1000
+#define VA_SENTINEL 0xFF
+#define VA_BAUDRATE B38400 /* for isa-serial channels */
+
+typedef struct VAContext {
+    bool is_host;
+    const char *channel_method;
+    const char *channel_path;
+} VAContext;
+
+typedef struct VAState {
+    bool is_host;
+    const char *channel_method;
+    const char *channel_path;
+    int fd;
+    QEMUTimer *client_timer;
+    QEMUTimer *server_timer;
+    VAClientData client_data;
+    VAServerData server_data;
+    int client_job_count;
+    int client_jobs_in_flight;
+    int server_job_count;
+    VAManager *manager;
+} VAState;
+
+enum va_job_status {
+    VA_JOB_STATUS_PENDING = 0,
+    VA_JOB_STATUS_OK,
+    VA_JOB_STATUS_ERROR,
+    VA_JOB_STATUS_CANCELLED,
+};
+
+typedef void (VAHTSendCallback)(const void *opaque);
+
+int va_init(VAContext ctx);
+bool va_qdict_haskey_with_type(const QDict *qdict, const char *key,
+                               qtype_code type);
+QDict *va_qdict_copy(const QDict *old);
+int va_xport_send_response(const char *content, size_t content_len, const char *tag,
+                           const void *opaque, VAHTSendCallback cb);
+int va_xport_send_request(const char *content, size_t content_len, const char *tag,
+                          const void *opaque, VAHTSendCallback cb);
+void va_http_read_handler(void *opaque);
+#endif /* VIRTAGENT_COMMON_H */