qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v2 0/8] qemu: guest agent: implement guest-exec


From: Michael Roth
Subject: Re: [Qemu-devel] [PATCH v2 0/8] qemu: guest agent: implement guest-exec command for Linux
Date: Fri, 09 Jan 2015 11:06:21 -0600
User-agent: alot/0.3.4

Quoting Denis V. Lunev (2014-12-31 07:06:46)
> hese patches for guest-agent add the functionality to execute commands on
> a guest UNIX machine.

Hi Denis,

Glad to see these getting picked up. I did at some point hack up a rewrite
of the original code though which has some elements might be worth considering
incorporating into your patchset.

The main one is the use of g_spawn_async_with_pipes(), which wraps fork/exec
vs. CreateProcess() and allows for more shared code between posix/win32.

It also creates the stdin/out/err FDs for you, which I suppose we could
do ourselves manually here as well, but it also raises the question of
whether guest-pipe-open is really necessary. In my code I ended up dropping
it since I can't imagine a use-case outside of guest-exec, but in doing so
also I dropped the ability to pipe one process into another...

But thinking about it more I think you can still pipe one process into
another by dup2()'ing the stdin FD returned by g_spawn_async_with_pipes()
to whatever stdout/stderr you wish to specify from a previous process.

The other one worth considering is allowing cmdline to simply be a string,
to parse it into arguments using g_shell_parse_argv(), which should also
be cross-platform.

If you do things that the core exec code ends up looking something like
this:

static GuestExecInfo *guest_exec_spawn(const char *cmdline, bool interactive,
                                       Error **errp)
{
    GSpawnFlags default_flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD;
    gboolean ret;
    GPid gpid;
    gchar **argv;
    gint argc;
    GError *gerr = NULL;
    gint fd_in = -1, fd_out = -1, fd_err = -1;

    ret = g_shell_parse_argv(cmdline, &argc, &argv, &gerr);
    if (!ret || gerr) {
        error_setg(errp, "failed to parse command: %s, %s", cmdline,
                  gerr->message);
        return NULL;
    }

    ret = g_spawn_async_with_pipes(NULL, argv, NULL,
                                   default_flags, NULL, NULL, &gpid,
                                   interactive ? &fd_in : NULL, &fd_out, 
&fd_err, &gerr);
    if (gerr) {
        error_setg(errp, "failed to execute command: %s, %s", cmdline,
                  gerr->message);
        return NULL;
    }
    if (!ret) {
        error_setg(errp, "failed to execute command");
        return NULL;
    }

    return guest_exec_info_new(gpid, cmdline, fd_in, fd_out, fd_err);
}

GuestExecResponse *qmp_guest_exec_async(const char *cmdline,
                                             bool has_interactive,
                                             bool interactive,
                                             Error **errp)
{   
    GuestExecResponse *ger;
    GuestExecInfo *gei;
    int32_t handle;
    
    gei = guest_exec_spawn(cmdline, has_interactive && interactive, errp);
    if (error_is_set(errp)) {
        return NULL;
    }
    
    print_gei(gei);
    ger = g_new0(GuestExecResponse, 1);
    
    if (has_interactive && interactive) {
        ger->has_handle_stdin = true;
        ger->handle_stdin =
            guest_file_handle_add_fd(gei->fd_in, "a", errp);
        if (error_is_set(errp)) {
            return NULL;
        }
    }
    
    ger->has_handle_stdout = true;
    ger->handle_stdout =
            guest_file_handle_add_fd(gei->fd_out, "r", errp);
    if (error_is_set(errp)) {
        return NULL;
    }
    
    ger->has_handle_stderr = true;
    ger->handle_stderr =
            guest_file_handle_add_fd(gei->fd_err, "r", errp);
    if (error_is_set(errp)) {
        return NULL;
    }
    
    handle = guest_exec_info_register(gei);
    ger->status = qmp_guest_exec_status(handle, false, false, false, 0, errp);
    if (error_is_set(errp)) {
        return NULL;
    }

    return ger;
} 

Where GuestExecResponse takes the place of the original PID return, since
we now need to fetch the stdin/stdout/stderr handles as well. In my code
it was defined as:

{ 'type': 'GuestExecResponse',
  'data': { 'status': 'GuestExecStatus',
            '*handle-stdin': 'int', '*handle-stdout': 'int',
            '*handle-stderr': 'int' } }

Sorry for not just posting the patchset somewhere, the code was initially lost
in an accidental wipe of /home so I currently only have vim backup files to
piece it together from atm.

> 
> These patches add the following interfaces:
> 
> guest-pipe-open
> guest-exec
> guest-exec-status
> 
> With these interfaces it's possible to:
> 
> * Open an anonymous pipe and work with it as with a file using already
> implemented interfaces guest-file-{read,write,flush,close}.
> 
> * Execute a binary or a script on a guest machine.
> We can pass arbitrary arguments and environment to a new child process.
> 
> * Pass redirected IO from/to stdin, stdout, stderr from a child process to a
> local file or an anonymous pipe.
> 
> * Check the status of a child process, get its exit status if it exited or get
> signal number if it was killed.
> 
> We plan to add support for Windows in the near future using the same 
> interfaces
> we introduce here.
> 
> Example of usage:
> 
> {"execute": "guest-pipe-open", "arguments":{"mode": "r"}}
> {'return': 1000}
> 
> {"execute":"guest-exec", "arguments":{ "path": "id", "params": ["user"],
>     "env": ["MYENV=myvalue"], "handle_stdout": 1000 }}'
> {"return": 2636}
> 
> {"execute": "guest-exec-status", "arguments": {"pid": 2636}}
> {"return":{"exit":0,"handle_stderr":-1,"handle_stdin":-1,"handle_stdout":1000,"signal":-1}}
> 
> {"execute": "guest-file-read", "arguments": {"handle": 1000, "count":128}}
> {"return":{"count":58,"buf-b64":"dWlk...","eof":true}}
> 
> {"execute": "guest-file-close", "arguments": {"handle": 1000}}
> {"return":{}}
> 
> 
> These patches are based on the patches proposed by Michael Roth in 2011:
> http://lists.gnu.org/archive/html/qemu-devel/2011-12/msg00722.html
> 
> We made several modifications to the interfaces proposed by Michael to 
> simplify
> their usage and we also added several new functions:
> 
> * Arguments to an executable file are passed as an array of strings.
> 
>     Before that we had to pass parameters like this:
>     'params': [ {'param': '-b'}, {'param': '-n1'} ]
> 
>     Now we may pass them just as an array of strings:
>     'params': [ '-b', '-n1' ]
> 
> * Environment can be passed to a child process.
> 
>     "env": ["MYENV=myvalue"]
> 
> * Removed "detach" argument from "guest-exec" - it never waits for a child
> process to signal.  With this change it's possible to return just PID from
> "guest-exec", a user must call "guest-exec-status" to get the status of a 
> child.
> 
> * Removed "wait" argument from "guest-exec-status" - waiting for a child 
> process
> to signal is dangerous, because it may block the whole qemu-ga.  Instead, the
> command polls just once a status of a child.
> 
> * "guest-exec-status" returns exit status of a child process or a signal 
> number,
> in case a process was killed.
> 
> * Simplified the command "guest-pipe-open" - the way how it stores the pipe
> fd's.  No additional logic is needed about which end of pipe should be closed
> with "guest-file-close".  This way we avoid modifying the interface of the
> existing "guest-file-close".
> 
> In the conversation about the original patches there was a suggestion to merge
> "path" and "params" into one single list.  But we didn't do so, because having
> separate "path" is similar to exec*() family functions in UNIX.  That way it
> looks more consistent.
> 
> Changes from v1:
> - Windows version of the patchset is added
> - SIGPIPE processing is added for Unix version of the patchset
> - added guest_exec_file_busy() to prevent GuestFileHandle* object from being
>   deleted in case it's used in guest-exec command.
> 
> Signed-off-by: Semen Zolin <address@hidden>
> Signed-off-by: Olga Krishtal <address@hidden>
> Signed-off-by: Denis V. Lunev <address@hidden>




reply via email to

[Prev in Thread] Current Thread [Next in Thread]