qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Michael Roth <mdroth@linux.vnet.ibm.com>
To: Luiz Capitulino <lcapitulino@redhat.com>
Cc: aliguori@linux.vnet.ibm.com, agl@linux.vnet.ibm.com,
	qemu-devel@nongnu.org, Jes.Sorensen@redhat.com
Subject: Re: [Qemu-devel] [PATCH v5 3/5] guest agent: add guest agent RPCs/commands
Date: Fri, 17 Jun 2011 15:19:56 -0500	[thread overview]
Message-ID: <4DFBB6EC.70805@linux.vnet.ibm.com> (raw)
In-Reply-To: <20110616155247.1df00b53@doriath>

On 06/16/2011 01:52 PM, Luiz Capitulino wrote:
> On Tue, 14 Jun 2011 15:06:23 -0500
> Michael Roth<mdroth@linux.vnet.ibm.com>  wrote:
>
>> This adds the initial set of QMP/QAPI commands provided by the guest
>> agent:
>>
>> guest-sync
>> guest-ping
>> guest-info
>> guest-shutdown
>> guest-file-open
>> guest-file-read
>> guest-file-write
>> guest-file-seek
>> guest-file-close
>> guest-fsfreeze-freeze
>> guest-fsfreeze-thaw
>> guest-fsfreeze-status
>>
>> The input/output specification for these commands are documented in the
>> schema.
>>
>> Signed-off-by: Michael Roth<mdroth@linux.vnet.ibm.com>
>> ---
>>   qerror.c                   |    4 +
>>   qerror.h                   |    3 +
>>   qga/guest-agent-commands.c |  522 ++++++++++++++++++++++++++++++++++++++++++++
>>   qga/guest-agent-core.h     |    1 +
>>   4 files changed, 530 insertions(+), 0 deletions(-)
>>   create mode 100644 qga/guest-agent-commands.c
>>
>> diff --git a/qerror.c b/qerror.c
>> index d7fcd93..24f0c48 100644
>> --- a/qerror.c
>> +++ b/qerror.c
>> @@ -213,6 +213,10 @@ static const QErrorStringTable qerror_table[] = {
>>           .error_fmt = QERR_VNC_SERVER_FAILED,
>>           .desc      = "Could not start VNC server on %(target)",
>>       },
>> +    {
>> +        .error_fmt = QERR_QGA_LOGGING_FAILED,
>> +        .desc      = "failed to write log statement due to logging being disabled",
>> +    },
>>       {}
>>   };
>>
>> diff --git a/qerror.h b/qerror.h
>> index 7a89a50..bf3d5a9 100644
>> --- a/qerror.h
>> +++ b/qerror.h
>> @@ -184,4 +184,7 @@ QError *qobject_to_qerror(const QObject *obj);
>>   #define QERR_FEATURE_DISABLED \
>>       "{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }"
>>
>> +#define QERR_QGA_LOGGING_FAILED \
>> +    "{ 'class': 'QgaLoggingFailed', 'data': {} }"
>> +
>>   #endif /* QERROR_H */
>> diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c
>> new file mode 100644
>> index 0000000..6f9886a
>> --- /dev/null
>> +++ b/qga/guest-agent-commands.c
>> @@ -0,0 +1,522 @@
>> +/*
>> + * QEMU Guest Agent commands
>> + *
>> + * Copyright IBM Corp. 2011
>> + *
>> + * Authors:
>> + *  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<glib.h>
>> +#include<mntent.h>
>> +#include<sys/types.h>
>> +#include<sys/ioctl.h>
>> +#include<linux/fs.h>
>> +#include "qga/guest-agent-core.h"
>> +#include "qga-qmp-commands.h"
>> +#include "qerror.h"
>> +
>> +static GAState *ga_state;
>> +
>> +static bool logging_enabled(void)
>> +{
>> +    return ga_logging_enabled(ga_state);
>> +}
>> +
>> +static void disable_logging(void)
>> +{
>> +    ga_disable_logging(ga_state);
>> +}
>> +
>> +static void enable_logging(void)
>> +{
>> +    ga_enable_logging(ga_state);
>> +}
>> +
>> +/* Note: in some situations, like with the fsfreeze, logging may be
>> + * temporarilly disabled. if it is necessary that a command be able
>> + * to log for accounting purposes, check logging_enabled() beforehand,
>> + * and use the QERR_QGA_LOGGING_DISABLED to generate an error
>> + */
>> +static void slog(const char *fmt, ...)
>> +{
>> +    va_list ap;
>> +
>> +    va_start(ap, fmt);
>> +    g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
>> +    va_end(ap);
>> +}
>> +
>> +int64_t qmp_guest_sync(int64_t id, Error **errp)
>> +{
>> +    return id;
>> +}
>> +
>> +void qmp_guest_ping(Error **err)
>> +{
>> +    slog("guest-ping called");
>> +}
>> +
>> +struct GuestAgentInfo *qmp_guest_info(Error **err)
>> +{
>> +    GuestAgentInfo *info = g_malloc0(sizeof(GuestAgentInfo));
>> +
>> +    info->version = g_strdup(QGA_VERSION);
>> +
>> +    return info;
>> +}
>> +
>> +void qmp_guest_shutdown(const char *shutdown_mode, Error **err)
>
> I'd call the argument just 'mode'.
>
>> +{
>> +    int ret;
>> +    const char *shutdown_flag;
>> +
>> +    if (!logging_enabled()) {
>> +        error_set(err, QERR_QGA_LOGGING_FAILED);
>> +        return;
>> +    }
>> +
>> +    slog("guest-shutdown called, shutdown_mode: %s", shutdown_mode);
>> +    if (strcmp(shutdown_mode, "halt") == 0) {
>> +        shutdown_flag = "-H";
>> +    } else if (strcmp(shutdown_mode, "powerdown") == 0) {
>> +        shutdown_flag = "-P";
>> +    } else if (strcmp(shutdown_mode, "reboot") == 0) {
>> +        shutdown_flag = "-r";
>> +    } else {
>> +        error_set(err, QERR_INVALID_PARAMETER_VALUE, "shutdown_mode",
>> +                  "halt|powerdown|reboot");
>> +        return;
>> +    }
>> +
>> +    ret = fork();
>> +    if (ret == 0) {
>> +        /* child, start the shutdown */
>> +        setsid();
>> +        fclose(stdin);
>> +        fclose(stdout);
>> +        fclose(stderr);
>
> Would be nice to have a log file, whose descriptor is passed to the
> child.
>
>> +
>> +        sleep(5);
>
> Why sleep()?
>

Want to give the agent time to send a response. It's still racy, but 
less so that immediately issuing the shutdown. Ideal we'd push the 
sleep() into shutdown's time param, but that only has minute resolution.

>> +        ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
>> +                    "hypervisor initiated shutdown", (char*)NULL);
>> +        exit(!!ret);
>> +    } else if (ret<  0) {
>> +        error_set(err, QERR_UNDEFINED_ERROR);
>> +    }
>
> Doesn't have the parent process wait for the child, so that exec() errors
> can be reported?

The exec() won't return until the shutdown is executed, so the RPC's 
behavior would be racy. At some point I documented that the shutdown is 
an async "request" that may or may not complete but that was lost in the 
reworking. I'll clarify in the schema.

>
>> +}
>> +
>> +typedef struct GuestFileHandle {
>> +    uint64_t id;
>> +    FILE *fh;
>> +} GuestFileHandle;
>> +
>> +static struct {
>> +    GSList *filehandles;
>
> Wouldn't this be simpler if we use qemu list implementation instead?
>

YES! This stuff is terribly verbose. I was trying to stick with glib 
outside of QMP/QAPI stuff though...and I think more glib stuff will find 
it's way into here over time that'll make appearances of 
GSList/gmalloc/etc inevitable.

But if intermixing is not a big deal I'm more than happy to "allow" qemu 
malloc/list stuff where it makes sense, and refactor these accordingly.

>> +    uint64_t last_id;
>> +} guest_file_state;
>> +
>> +static int64_t guest_file_handle_add(FILE *fh)
>> +{
>> +    GuestFileHandle *gfh;
>> +
>> +    gfh = g_malloc(sizeof(GuestFileHandle));
>> +    gfh->id = guest_file_state.last_id++;
>
> I don't know if the uint64_t limit can be reached in practice, but I'd
> expect a bitmap, so that you can return ids in guest_file_handle_remove().
>
> Another simpler option would be to use the real fd instead. I mean, the one
> returned by the guest kernel.
>

I think I'll go with this. I was hesitant to do this at first since I 
didn't want users specifying FDs opened outside of guest-file-open, but 
so long as we only rely on our internal list of open FDs I guess that's 
not applicable.

>> +    gfh->fh = fh;
>> +    guest_file_state.filehandles = g_slist_append(guest_file_state.filehandles,
>> +                                                  gfh);
>> +    return gfh->id;
>> +}
>> +
>> +static gint guest_file_handle_match(gconstpointer elem, gconstpointer id_p)
>> +{
>> +    const uint64_t *id = id_p;
>> +    const GuestFileHandle *gfh = elem;
>> +
>> +    g_assert(gfh);
>> +    return (gfh->id != *id);
>> +}
>> +
>> +static FILE *guest_file_handle_find(int64_t id)
>> +{
>> +    GSList *elem = g_slist_find_custom(guest_file_state.filehandles,&id,
>> +                                       guest_file_handle_match);
>> +    GuestFileHandle *gfh;
>> +
>> +    if (elem) {
>> +        g_assert(elem->data);
>> +        gfh = elem->data;
>> +        return gfh->fh;
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>> +static void guest_file_handle_remove(int64_t id)
>> +{
>> +    GSList *elem = g_slist_find_custom(guest_file_state.filehandles,&id,
>> +                                       guest_file_handle_match);
>> +    gpointer data = elem->data;
>> +
>> +    if (!data) {
>> +        return;
>> +    }
>> +    guest_file_state.filehandles = g_slist_remove(guest_file_state.filehandles,
>> +                                                  data);
>> +    g_free(data);
>> +}
>> +
>> +int64_t qmp_guest_file_open(const char *filepath, const char *mode, Error **err)
>> +{
>> +    FILE *fh;
>> +    int fd, ret;
>> +    int64_t id = -1;
>> +
>> +    if (!logging_enabled()) {
>> +        error_set(err, QERR_QGA_LOGGING_FAILED);
>> +        goto out;
>
> No need to have a goto here, just do return -1. This true for other functions
> too.
>
>> +    }
>> +    slog("guest-file-open called, filepath: %s, mode: %s", filepath, mode);
>> +    fh = fopen(filepath, mode);
>> +    if (!fh) {
>> +        error_set(err, QERR_OPEN_FILE_FAILED, filepath);
>> +        goto out;
>> +    }
>> +
>> +    /* set fd non-blocking to avoid common use cases (like reading from a
>> +     * named pipe) from hanging the agent
>> +     */
>> +    fd = fileno(fh);
>> +    ret = fcntl(fd, F_GETFL);
>> +    ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK);
>> +    if (ret == -1) {
>> +        error_set(err, QERR_OPEN_FILE_FAILED, filepath);
>> +        fclose(fh);
>> +        goto out;
>> +    }
>> +
>> +    id = guest_file_handle_add(fh);
>> +    slog("guest-file-open, filehandle: %ld", id);
>> +out:
>> +    return id;
>> +}
>> +
>> +struct GuestFileRead *qmp_guest_file_read(int64_t filehandle, int64_t count,
>> +                                          Error **err)
>> +{
>> +    GuestFileRead *read_data;
>> +    guchar *buf;
>> +    FILE *fh = guest_file_handle_find(filehandle);
>> +    size_t read_count;
>> +
>> +    if (!fh) {
>> +        error_set(err, QERR_FD_NOT_FOUND, "filehandle");
>> +        return NULL;
>> +    }
>> +
>> +    read_data = g_malloc0(sizeof(GuestFileRead));
>> +    buf = g_malloc(count);
>
> What happens if the client passes a bogus value? Like -1 or a very big
> number?
>
> I think count has to be checked against the file size. You could call stat()
> and store the value. Also, you're not checking g_malloc()'s return, so a bad
> allocation can crash the agent.
>

All good points

>> +
>> +    read_count = fread(buf, 1, count, fh);
>> +    buf[read_count] = 0;
>
> We need to allocate an additional byte to do that.
>
>> +    read_data->count = read_count;
>> +    read_data->eof = feof(fh);
>> +    if (read_count) {
>> +        read_data->buf = g_base64_encode(buf, read_count);
>> +    }
>> +    g_free(buf);
>> +    /* clear error and eof. error is generally due to EAGAIN from non-blocking
>> +     * mode, and no real way to differenitate from a real error since we only
>> +     * get boolean error flag from ferror()
>> +     */
>> +    clearerr(fh);
>> +
>> +    return read_data;
>> +}
>> +
>> +GuestFileWrite *qmp_guest_file_write(int64_t filehandle, const char *data_b64,
>> +                                     int64_t count, Error **err)
>> +{
>> +    GuestFileWrite *write_data;
>> +    guchar *data;
>> +    gsize data_len;
>> +    int write_count;
>> +    FILE *fh = guest_file_handle_find(filehandle);
>> +
>> +    if (!fh) {
>> +        error_set(err, QERR_FD_NOT_FOUND, "filehandle");
>> +        return NULL;
>> +    }
>> +
>> +    write_data = g_malloc0(sizeof(GuestFileWrite));
>> +    data = g_base64_decode(data_b64,&data_len);
>> +    write_count = fwrite(data, 1, MIN(count, data_len), fh);
>
> IMO, we should do what the user is asking us to do, if it's impossible we
> should return an error. IOW, we should use count. It's okay if the buffer
> is bigger then count, but if count is bigger then we should return an error.
>
> What does write() do in this case? segfaults?
>
>> +    write_data->count = write_count;
>> +    write_data->eof = feof(fh);
>> +    g_free(data);
>> +    clearerr(fh);
>> +
>> +    return write_data;
>> +}
>> +
>> +struct GuestFileSeek *qmp_guest_file_seek(int64_t filehandle, int64_t offset,
>> +                                          int64_t whence, Error **err)
>> +{
>> +    GuestFileSeek *seek_data;
>> +    FILE *fh = guest_file_handle_find(filehandle);
>> +    int ret;
>> +
>> +    if (!fh) {
>> +        error_set(err, QERR_FD_NOT_FOUND, "filehandle");
>> +        return NULL;
>> +    }
>> +
>> +    seek_data = g_malloc0(sizeof(GuestFileRead));
>> +    ret = fseek(fh, offset, whence);
>> +    if (ret == -1) {
>> +        error_set(err, QERR_UNDEFINED_ERROR);
>> +        g_free(seek_data);
>> +        return NULL;
>> +    }
>> +    seek_data->position = ftell(fh);
>> +    seek_data->eof = feof(fh);
>> +    clearerr(fh);
>> +
>> +    return seek_data;
>> +}
>> +
>> +void qmp_guest_file_close(int64_t filehandle, Error **err)
>> +{
>> +    FILE *fh = guest_file_handle_find(filehandle);
>> +
>> +    if (!logging_enabled()) {
>> +        error_set(err, QERR_QGA_LOGGING_FAILED);
>> +        return;
>> +    }
>> +    slog("guest-file-close called, filehandle: %ld", filehandle);
>> +    if (!fh) {
>> +        error_set(err, QERR_FD_NOT_FOUND, "filehandle");
>> +        return;
>> +    }
>> +
>> +    fclose(fh);
>> +    guest_file_handle_remove(filehandle);
>> +}
>> +
>> +/*
>> + * Walk the mount table and build a list of local file systems
>> + */
>> +
>> +struct direntry {
>> +    char *dirname;
>> +    char *devtype;
>> +    struct direntry *next;
>
> Wouldn't it be better to use qemu's list implementation?
>

yes, ill fix all the list stuff up to use qemu's

>> +};
>> +
>> +struct {
>> +    struct direntry *mount_list;
>> +    GuestFsfreezeStatus status;
>> +} guest_fsfreeze_state;
>> +
>> +static int guest_fsfreeze_build_mount_list(void)
>> +{
>> +    struct mntent *mnt;
>> +    struct direntry *entry;
>> +    struct direntry *next;
>> +    char const *mtab = MOUNTED;
>> +    FILE *fp;
>> +
>> +    fp = setmntent(mtab, "r");
>> +    if (!fp) {
>> +        g_warning("fsfreeze: unable to read mtab");
>> +        goto fail;
>> +    }
>> +
>> +    while ((mnt = getmntent(fp))) {
>> +        /*
>> +         * An entry which device name doesn't start with a '/' is
>> +         * either a dummy file system or a network file system.
>> +         * Add special handling for smbfs and cifs as is done by
>> +         * coreutils as well.
>> +         */
>> +        if ((mnt->mnt_fsname[0] != '/') ||
>> +            (strcmp(mnt->mnt_type, "smbfs") == 0) ||
>> +            (strcmp(mnt->mnt_type, "cifs") == 0)) {
>> +            continue;
>> +        }
>> +
>> +        entry = g_malloc(sizeof(struct direntry));
>> +        entry->dirname = qemu_strdup(mnt->mnt_dir);
>> +        entry->devtype = qemu_strdup(mnt->mnt_type);
>> +        entry->next = guest_fsfreeze_state.mount_list;
>> +
>> +        guest_fsfreeze_state.mount_list = entry;
>> +    }
>> +
>> +    endmntent(fp);
>> +
>> +    return 0;
>> +
>> +fail:
>> +    while(guest_fsfreeze_state.mount_list) {
>> +        next = guest_fsfreeze_state.mount_list->next;
>> +        g_free(guest_fsfreeze_state.mount_list->dirname);
>> +        g_free(guest_fsfreeze_state.mount_list->devtype);
>> +        g_free(guest_fsfreeze_state.mount_list);
>> +        guest_fsfreeze_state.mount_list = next;
>> +    }
>> +
>> +    return -1;
>> +}
>> +
>> +/*
>> + * Return status of freeze/thaw
>> + */
>> +GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
>> +{
>> +    return guest_fsfreeze_state.status;
>> +}
>> +
>> +/*
>> + * Walk list of mounted file systems in the guest, and freeze the ones which
>> + * are real local file systems.
>> + */
>> +int64_t qmp_guest_fsfreeze_freeze(Error **err)
>> +{
>> +    int ret = 0, i = 0;
>> +    struct direntry *entry;
>> +    int fd;
>> +
>> +    if (!logging_enabled()) {
>> +        error_set(err, QERR_QGA_LOGGING_FAILED);
>> +        goto out;
>> +    }
>> +
>> +    slog("guest-fsfreeze called");
>> +
>> +    if (guest_fsfreeze_state.status != GUEST_FSFREEZE_STATUS_THAWED) {
>> +        ret = 0;
>> +        goto out;
>> +    }
>> +
>> +    ret = guest_fsfreeze_build_mount_list();
>> +    if (ret<  0) {
>> +        goto out;
>> +    }
>> +
>> +    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_INPROGRESS;
>> +
>> +    /* cannot risk guest agent blocking itself on a write in this state */
>> +    disable_logging();
>> +
>> +    entry = guest_fsfreeze_state.mount_list;
>> +    while(entry) {
>
> A for() loop would be clearer, imho.
>
>> +        fd = qemu_open(entry->dirname, O_RDONLY);
>> +        if (fd == -1) {
>> +            ret = errno;
>> +            goto error;
>> +        }
>> +
>> +        /* we try to cull filesytems we know won't work in advance, but other
>> +         * filesytems may not implement fsfreeze for less obvious reasons.
>> +         * these will reason EOPNOTSUPP, so we simply ignore them. when
>> +         * thawing, these filesystems will return an EINVAL instead, due to
>> +         * not being in a frozen state. Other filesystem-specific
>> +         * errors may result in EINVAL, however, so the user should check the
>> +         * number * of filesystems returned here against those returned by the
>> +         * thaw operation to determine whether everything completed
>> +         * successfully
>> +         */
>> +        ret = ioctl(fd, FIFREEZE);
>> +        if (ret<  0&&  errno != EOPNOTSUPP) {
>> +            close(fd);
>> +            goto error;
>
> Does the FIFREEZE ioctl() call returns the errno code? If it doesn't, then
> we have to set it as the qemu_open() does above.
>

Nope, -1 + errno, good catch.

>> +        }
>> +        close(fd);
>> +
>> +        entry = entry->next;
>> +        i++;
>> +    }
>> +
>> +    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN;
>> +    ret = i;
>> +out:
>> +    return ret;
>> +error:
>> +    if (i>  0) {
>> +        guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR;
>> +    }
>
> I'm not sure this correct. What happens if an error happens in the
> first iteration of the while loop? A better way would to check 'ret'
> instead.
>

I believe this is meant to track states where some filesystems have been 
frozen and others not. We want to return the count, but not the error 
via fsfreeze_status. If we bail in the first iteration it means FIFREEZE 
was never completed successfully. We should reset state the THAWED then 
though.

>> +    goto out;
>> +}
>> +
>> +/*
>> + * Walk list of frozen file systems in the guest, and thaw them.
>> + */
>> +int64_t qmp_guest_fsfreeze_thaw(Error **err)
>> +{
>> +    int ret;
>> +    struct direntry *entry;
>> +    int fd, i = 0;
>> +
>> +    if (guest_fsfreeze_state.status != GUEST_FSFREEZE_STATUS_FROZEN&&
>> +        guest_fsfreeze_state.status != GUEST_FSFREEZE_STATUS_INPROGRESS) {
>> +        ret = 0;
>> +        goto out_enable_logging;
>> +    }
>> +
>> +    while((entry = guest_fsfreeze_state.mount_list)) {
>> +        fd = qemu_open(entry->dirname, O_RDONLY);
>> +        if (fd == -1) {
>> +            ret = -errno;
>> +            goto out;
>> +        }
>> +        ret = ioctl(fd, FITHAW);
>> +        if (ret<  0&&  errno != EOPNOTSUPP&&  errno != EINVAL) {
>> +            ret = -errno;
>> +            close(fd);
>> +            goto out;
>> +        }
>> +        close(fd);
>> +
>> +        guest_fsfreeze_state.mount_list = entry->next;
>> +        g_free(entry->dirname);
>> +        g_free(entry->devtype);
>> +        g_free(entry);
>> +        i++;
>> +    }
>> +
>> +    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
>> +    ret = i;
>> +out_enable_logging:
>> +    enable_logging();
>> +out:
>> +    return ret;
>> +}
>> +
>> +static void guest_fsfreeze_init(void)
>> +{
>> +    guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED;
>> +}
>> +
>> +static void guest_fsfreeze_cleanup(void)
>> +{
>> +    int64_t ret;
>> +    Error *err = NULL;
>> +
>> +    if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
>> +        ret = qmp_guest_fsfreeze_thaw(&err);
>> +        if (ret<  0 || err) {
>> +            slog("failed to clean up frozen filesystems");
>> +        }
>> +    }
>> +}
>> +
>> +/* register init/cleanup routines for stateful command groups */
>> +void ga_command_state_init(GAState *s, GACommandState *cs)
>> +{
>> +    ga_state = s;
>> +    ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup);
>> +}
>> diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
>> index 66d1729..a85a5e4 100644
>> --- a/qga/guest-agent-core.h
>> +++ b/qga/guest-agent-core.h
>> @@ -18,6 +18,7 @@
>>   typedef struct GAState GAState;
>>   typedef struct GACommandState GACommandState;
>>
>> +void ga_command_state_init(GAState *s, GACommandState *cs);
>>   void ga_command_state_add(GACommandState *cs,
>>                             void (*init)(void),
>>                             void (*cleanup)(void));
>

  reply	other threads:[~2011-06-17 20:20 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-06-14 20:06 [Qemu-devel] [QAPI+QGA 3/3] QEMU Guest Agent (virtagent) v5 Michael Roth
2011-06-14 20:06 ` [Qemu-devel] [PATCH v5 1/5] guest agent: command state class Michael Roth
2011-06-16 18:29   ` Luiz Capitulino
2011-06-16 18:46     ` Michael Roth
2011-06-16 19:04       ` Luiz Capitulino
2011-06-14 20:06 ` [Qemu-devel] [PATCH v5 2/5] guest agent: qemu-ga daemon Michael Roth
2011-06-16 18:42   ` Luiz Capitulino
2011-06-17 19:21     ` Michael Roth
2011-06-17 20:13       ` Luiz Capitulino
2011-06-17 21:25         ` Michael Roth
2011-06-18  3:25           ` Luiz Capitulino
2011-06-19 19:00             ` Michael Roth
2011-06-20 14:16               ` Luiz Capitulino
2011-06-20 23:40                 ` Michael Roth
2011-06-21 13:38                   ` Luiz Capitulino
2011-06-14 20:06 ` [Qemu-devel] [PATCH v5 3/5] guest agent: add guest agent RPCs/commands Michael Roth
2011-06-15  0:13   ` [Qemu-devel] [PATCH] guest agent: fix for " Michael Roth
2011-06-16 18:52   ` [Qemu-devel] [PATCH v5 3/5] guest agent: add " Luiz Capitulino
2011-06-17 20:19     ` Michael Roth [this message]
2011-06-18  2:38       ` Luiz Capitulino
2011-06-18 12:48         ` Luiz Capitulino
2011-06-14 20:06 ` [Qemu-devel] [PATCH v5 4/5] guest agent: add guest agent commands schema file Michael Roth
2011-06-14 20:06 ` [Qemu-devel] [PATCH v5 5/5] guest agent: Makefile, build qemu-ga Michael Roth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4DFBB6EC.70805@linux.vnet.ibm.com \
    --to=mdroth@linux.vnet.ibm.com \
    --cc=Jes.Sorensen@redhat.com \
    --cc=agl@linux.vnet.ibm.com \
    --cc=aliguori@linux.vnet.ibm.com \
    --cc=lcapitulino@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).