diff mbox series

[v5,14/50] mutli-process: build remote command line args

Message ID 588dafeecd20f8562f4a0dd68fa4bafbd6ea18bb.1582576372.git.jag.raman@oracle.com
State New
Headers show
Series Initial support for multi-process qemu | expand

Commit Message

Jag Raman Feb. 24, 2020, 8:55 p.m. UTC
From: Elena Ufimtseva <elena.ufimtseva@oracle.com>

Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
---
 v4 -> v5:
  - Added "exec" suboption to get the executable's name
  - Addressed feedback about variable names
  - Removed redundant check for spawning a process

 hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
 include/hw/proxy/qemu-proxy.h |  2 +-
 2 files changed, 54 insertions(+), 16 deletions(-)

Comments

Philippe Mathieu-Daudé March 2, 2020, 5:36 p.m. UTC | #1
typo "multi" in patch subject.

On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> 
> Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> ---
>   v4 -> v5:
>    - Added "exec" suboption to get the executable's name
>    - Addressed feedback about variable names
>    - Removed redundant check for spawning a process
> 
>   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
>   include/hw/proxy/qemu-proxy.h |  2 +-
>   2 files changed, 54 insertions(+), 16 deletions(-)
> 
> diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> index 828bbd7..d792e86 100644
> --- a/hw/proxy/qemu-proxy.c
> +++ b/hw/proxy/qemu-proxy.c
> @@ -19,19 +19,50 @@
>   
>   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
>   
> +static int add_argv(char *opts_str, char **argv, int argc)
> +{
> +    int max_args = 64;
> +
> +    if (argc < max_args - 1) {
> +        argv[argc++] = opts_str;
> +        argv[argc] = 0;
> +    } else {
> +        return 0;
> +    }
> +
> +    return argc;
> +}
> +
> +static int make_argv(char *opts_str, char **argv, int argc)
> +{
> +    int max_args = 64;
> +
> +    char *p2 = strtok(opts_str, " ");
> +    while (p2 && argc < max_args - 1) {
> +        argv[argc++] = p2;
> +        p2 = strtok(0, " ");
> +    }
> +    argv[argc] = 0;

Is there a GLib function to do that?

> +
> +    return argc;
> +}
> +
>   static int remote_spawn(PCIProxyDev *pdev, const char *opts,
>                           const char *exec_name, Error **errp)
>   {
> -    char *args[3];
>       pid_t rpid;
>       int fd[2] = {-1, -1};
>       Error *local_error = NULL;
> +    char *argv[64];
> +    int argc = 0;
> +    char *sfd;
> +    char *exec_dir;
>       int rc = -EINVAL;
>   
>       if (pdev->managed) {
>           /* Child is forked by external program (such as libvirt). */
>           error_setg(errp, "Remote processed is managed and launched by external program");
> -        return -1;
> +        return rc;
>       }
>   
>       if (!exec_name) {
> @@ -41,32 +72,38 @@ static int remote_spawn(PCIProxyDev *pdev, const char *opts,
>   
>       if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
>           error_setg(errp, "Unable to create unix socket.");
> -        return -1;
> +        return rc;
>       }
> +    exec_dir = g_strdup_printf("%s/%s", qemu_get_exec_dir(), exec_name);
> +    argc = add_argv(exec_dir, argv, argc);
> +    sfd = g_strdup_printf("%d", fd[1]);
> +    argc = add_argv(sfd, argv, argc);
> +    argc = make_argv((char *)opts, argv, argc);
> +
>       /* TODO: Restrict the forked process' permissions and capabilities. */
>       rpid = qemu_fork(&local_error);
>   
>       if (rpid == -1) {
>           error_setg(errp, "Unable to spawn emulation program.");
>           close(fd[0]);
> -        close(fd[1]);
> -        return -1;
> +        goto fail;
>       }
>   
>       if (rpid == 0) {
>           close(fd[0]);
>   
> -        args[0] = g_strdup(exec_name);
> -        args[1] = g_strdup_printf("%d", fd[1]);
> -        args[2] = NULL;
> -        execvp(args[0], (char *const *)args);
> +        rc = execv(argv[0], (char *const *)argv);
>           exit(1);
>       }
>       pdev->remote_pid = rpid;
> +    pdev->socket = fd[0];
> +
> +    rc = 0;
>   
> +fail:
>       close(fd[1]);
>   
> -    return 0;
> +    return rc;
>   }
>   
>   static int get_proxy_sock(PCIDevice *dev)
> @@ -177,16 +214,17 @@ static void pci_proxy_dev_register_types(void)
>   type_init(pci_proxy_dev_register_types)
>   
>   static void init_proxy(PCIDevice *dev, char *command, char *exec_name,
> -                       Error **errp)
> +                       bool need_spawn, Error **errp)
>   {
>       PCIProxyDev *pdev = PCI_PROXY_DEV(dev);
>       Error *local_error = NULL;
>   
>       if (!pdev->managed) {
> -        if (command) {
> -            remote_spawn(pdev, command, exec_name, &local_error);
> -        } else {
> -            return;
> +        if (need_spawn) {
> +            if (remote_spawn(pdev, command, exec_name, &local_error)) {
> +                error_propagate(errp, local_error);
> +                return;
> +            }
>           }
>       } else {
>           pdev->remote_pid = atoi(pdev->rid);
> diff --git a/include/hw/proxy/qemu-proxy.h b/include/hw/proxy/qemu-proxy.h
> index 28b0114..29fa2e9 100644
> --- a/include/hw/proxy/qemu-proxy.h
> +++ b/include/hw/proxy/qemu-proxy.h
> @@ -39,7 +39,7 @@ typedef struct PCIProxyDev {
>   
>       void (*proxy_ready) (PCIDevice *dev);
>       void (*init_proxy) (PCIDevice *dev, char *command, char *exec_name,
> -                        Error **errp);
> +                        bool need_spawn, Error **errp);
>   
>   } PCIProxyDev;
>   
>
Daniel P. Berrangé March 2, 2020, 5:47 p.m. UTC | #2
On Mon, Mar 02, 2020 at 06:36:13PM +0100, Philippe Mathieu-Daudé wrote:
> typo "multi" in patch subject.
> 
> On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > 
> > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > ---
> >   v4 -> v5:
> >    - Added "exec" suboption to get the executable's name
> >    - Addressed feedback about variable names
> >    - Removed redundant check for spawning a process
> > 
> >   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
> >   include/hw/proxy/qemu-proxy.h |  2 +-
> >   2 files changed, 54 insertions(+), 16 deletions(-)
> > 
> > diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> > index 828bbd7..d792e86 100644
> > --- a/hw/proxy/qemu-proxy.c
> > +++ b/hw/proxy/qemu-proxy.c
> > @@ -19,19 +19,50 @@
> >   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
> > +static int add_argv(char *opts_str, char **argv, int argc)
> > +{
> > +    int max_args = 64;
> > +
> > +    if (argc < max_args - 1) {
> > +        argv[argc++] = opts_str;
> > +        argv[argc] = 0;
> > +    } else {
> > +        return 0;
> > +    }
> > +
> > +    return argc;
> > +}
> > +
> > +static int make_argv(char *opts_str, char **argv, int argc)
> > +{
> > +    int max_args = 64;
> > +
> > +    char *p2 = strtok(opts_str, " ");
> > +    while (p2 && argc < max_args - 1) {
> > +        argv[argc++] = p2;
> > +        p2 = strtok(0, " ");
> > +    }
> > +    argv[argc] = 0;
> 
> Is there a GLib function to do that?

g_shell_parse_argv() perhaps

  https://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html


Though my preference would be to avoid the need to do this at all, by
not accepting a raw shell command line string in the first place.


Regards,
Daniel
Elena Ufimtseva March 2, 2020, 10:39 p.m. UTC | #3
On Mon, Mar 02, 2020 at 05:47:45PM +0000, Daniel P. Berrangé wrote:
> On Mon, Mar 02, 2020 at 06:36:13PM +0100, Philippe Mathieu-Daudé wrote:
> > typo "multi" in patch subject.
> >
Thank Philippe, will fix.
 
> > On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> > > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > 
> > > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > > ---
> > >   v4 -> v5:
> > >    - Added "exec" suboption to get the executable's name
> > >    - Addressed feedback about variable names
> > >    - Removed redundant check for spawning a process
> > > 
> > >   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
> > >   include/hw/proxy/qemu-proxy.h |  2 +-
> > >   2 files changed, 54 insertions(+), 16 deletions(-)
> > > 
> > > diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> > > index 828bbd7..d792e86 100644
> > > --- a/hw/proxy/qemu-proxy.c
> > > +++ b/hw/proxy/qemu-proxy.c
> > > @@ -19,19 +19,50 @@
> > >   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
> > > +static int add_argv(char *opts_str, char **argv, int argc)
> > > +{
> > > +    int max_args = 64;
> > > +
> > > +    if (argc < max_args - 1) {
> > > +        argv[argc++] = opts_str;
> > > +        argv[argc] = 0;
> > > +    } else {
> > > +        return 0;
> > > +    }
> > > +
> > > +    return argc;
> > > +}
> > > +
> > > +static int make_argv(char *opts_str, char **argv, int argc)
> > > +{
> > > +    int max_args = 64;
> > > +
> > > +    char *p2 = strtok(opts_str, " ");
> > > +    while (p2 && argc < max_args - 1) {
> > > +        argv[argc++] = p2;
> > > +        p2 = strtok(0, " ");
> > > +    }
> > > +    argv[argc] = 0;
> > 
> > Is there a GLib function to do that?
>

Hi Daniel

> g_shell_parse_argv() perhaps
>

Thanks for the suggestion.

>   https://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html
> 
> 
> Though my preference would be to avoid the need to do this at all, by
> not accepting a raw shell command line string in the first place.
>
Can you please clarify? Did you mean that it would be better if Qemu somehow
verifies the options and then passes it to a remote process via a message?

Thanks!

Elena
> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
>
Dr. David Alan Gilbert March 4, 2020, 10:09 a.m. UTC | #4
* Jagannathan Raman (jag.raman@oracle.com) wrote:
> From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> 
> Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> ---
>  v4 -> v5:
>   - Added "exec" suboption to get the executable's name
>   - Addressed feedback about variable names
>   - Removed redundant check for spawning a process
> 
>  hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
>  include/hw/proxy/qemu-proxy.h |  2 +-
>  2 files changed, 54 insertions(+), 16 deletions(-)
> 
> diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> index 828bbd7..d792e86 100644
> --- a/hw/proxy/qemu-proxy.c
> +++ b/hw/proxy/qemu-proxy.c
> @@ -19,19 +19,50 @@
>  
>  static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
>  
> +static int add_argv(char *opts_str, char **argv, int argc)
> +{
> +    int max_args = 64;

...

> +
> +static int make_argv(char *opts_str, char **argv, int argc)
> +{
> +    int max_args = 64;

.....

> +
>  static int remote_spawn(PCIProxyDev *pdev, const char *opts,
>                          const char *exec_name, Error **errp)
>  {
> -    char *args[3];
>      pid_t rpid;
>      int fd[2] = {-1, -1};
>      Error *local_error = NULL;
> +    char *argv[64];


Magic '64' in a lot of places; that should be one constant somewhere
(if it's actually needed).

Dave


--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
Daniel P. Berrangé March 4, 2020, 11 a.m. UTC | #5
On Mon, Mar 02, 2020 at 02:39:37PM -0800, Elena Ufimtseva wrote:
> On Mon, Mar 02, 2020 at 05:47:45PM +0000, Daniel P. Berrangé wrote:
> > On Mon, Mar 02, 2020 at 06:36:13PM +0100, Philippe Mathieu-Daudé wrote:
> > > typo "multi" in patch subject.
> > >
> Thank Philippe, will fix.
>  
> > > On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> > > > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > 
> > > > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > > > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > > > ---
> > > >   v4 -> v5:
> > > >    - Added "exec" suboption to get the executable's name
> > > >    - Addressed feedback about variable names
> > > >    - Removed redundant check for spawning a process
> > > > 
> > > >   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
> > > >   include/hw/proxy/qemu-proxy.h |  2 +-
> > > >   2 files changed, 54 insertions(+), 16 deletions(-)
> > > > 
> > > > diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> > > > index 828bbd7..d792e86 100644
> > > > --- a/hw/proxy/qemu-proxy.c
> > > > +++ b/hw/proxy/qemu-proxy.c
> > > > @@ -19,19 +19,50 @@
> > > >   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
> > > > +static int add_argv(char *opts_str, char **argv, int argc)
> > > > +{
> > > > +    int max_args = 64;
> > > > +
> > > > +    if (argc < max_args - 1) {
> > > > +        argv[argc++] = opts_str;
> > > > +        argv[argc] = 0;
> > > > +    } else {
> > > > +        return 0;
> > > > +    }
> > > > +
> > > > +    return argc;
> > > > +}
> > > > +
> > > > +static int make_argv(char *opts_str, char **argv, int argc)
> > > > +{
> > > > +    int max_args = 64;
> > > > +
> > > > +    char *p2 = strtok(opts_str, " ");
> > > > +    while (p2 && argc < max_args - 1) {
> > > > +        argv[argc++] = p2;
> > > > +        p2 = strtok(0, " ");
> > > > +    }
> > > > +    argv[argc] = 0;
> > > 
> > > Is there a GLib function to do that?
> >
> 
> Hi Daniel
> 
> > g_shell_parse_argv() perhaps
> >
> 
> Thanks for the suggestion.
> 
> >   https://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html
> > 
> > 
> > Though my preference would be to avoid the need to do this at all, by
> > not accepting a raw shell command line string in the first place.
> >
> Can you please clarify? Did you mean that it would be better if Qemu somehow
> verifies the options and then passes it to a remote process via a message?

I've not been able to trace the code paths back all the way, so I can't
point to where I think needs fixing. I assuming that something, somewhere
in this patch series should starts out with a binary name and a list of argv
as an array of char *. ie a "char **argv".  At some point this array gets
mashed together into a single 'char *' string where all the argv are separated
by a space. This patch now tries to parse this and turn it back into a
"char **argv" array.

So my key point is that we should try hard to avoid this intermediate
shell command line string stage entirely. Always keep the argv in an array
form, and never mash them together such that they then need parsing again.

I understand this is probably more complex, because we're having to pass
this across processes, via QemuOpts IIUC, but I still believe it is important
to have this data kept in array format if at all practical.

Regards,
Daniel
Elena Ufimtseva March 4, 2020, 4:25 p.m. UTC | #6
On Wed, Mar 04, 2020 at 11:00:32AM +0000, Daniel P. Berrangé wrote:
> On Mon, Mar 02, 2020 at 02:39:37PM -0800, Elena Ufimtseva wrote:
> > On Mon, Mar 02, 2020 at 05:47:45PM +0000, Daniel P. Berrangé wrote:
> > > On Mon, Mar 02, 2020 at 06:36:13PM +0100, Philippe Mathieu-Daudé wrote:
> > > > typo "multi" in patch subject.
> > > >
> > Thank Philippe, will fix.
> >  
> > > > On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> > > > > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > 
> > > > > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > > > > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > > > > ---
> > > > >   v4 -> v5:
> > > > >    - Added "exec" suboption to get the executable's name
> > > > >    - Addressed feedback about variable names
> > > > >    - Removed redundant check for spawning a process
> > > > > 
> > > > >   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
> > > > >   include/hw/proxy/qemu-proxy.h |  2 +-
> > > > >   2 files changed, 54 insertions(+), 16 deletions(-)
> > > > > 
> > > > > diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> > > > > index 828bbd7..d792e86 100644
> > > > > --- a/hw/proxy/qemu-proxy.c
> > > > > +++ b/hw/proxy/qemu-proxy.c
> > > > > @@ -19,19 +19,50 @@
> > > > >   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
> > > > > +static int add_argv(char *opts_str, char **argv, int argc)
> > > > > +{
> > > > > +    int max_args = 64;
> > > > > +
> > > > > +    if (argc < max_args - 1) {
> > > > > +        argv[argc++] = opts_str;
> > > > > +        argv[argc] = 0;
> > > > > +    } else {
> > > > > +        return 0;
> > > > > +    }
> > > > > +
> > > > > +    return argc;
> > > > > +}
> > > > > +
> > > > > +static int make_argv(char *opts_str, char **argv, int argc)
> > > > > +{
> > > > > +    int max_args = 64;
> > > > > +
> > > > > +    char *p2 = strtok(opts_str, " ");
> > > > > +    while (p2 && argc < max_args - 1) {
> > > > > +        argv[argc++] = p2;
> > > > > +        p2 = strtok(0, " ");
> > > > > +    }
> > > > > +    argv[argc] = 0;
> > > > 
> > > > Is there a GLib function to do that?
> > >
> > 
> > Hi Daniel
> > 
> > > g_shell_parse_argv() perhaps
> > >
> > 
> > Thanks for the suggestion.
> > 
> > >   https://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html
> > > 
> > > 
> > > Though my preference would be to avoid the need to do this at all, by
> > > not accepting a raw shell command line string in the first place.
> > >
> > Can you please clarify? Did you mean that it would be better if Qemu somehow
> > verifies the options and then passes it to a remote process via a message?
> 
> I've not been able to trace the code paths back all the way, so I can't
> point to where I think needs fixing. I assuming that something, somewhere
> in this patch series should starts out with a binary name and a list of argv
> as an array of char *. ie a "char **argv".  At some point this array gets
> mashed together into a single 'char *' string where all the argv are separated
> by a space. This patch now tries to parse this and turn it back into a
> "char **argv" array.
> 
> So my key point is that we should try hard to avoid this intermediate
> shell command line string stage entirely. Always keep the argv in an array
> form, and never mash them together such that they then need parsing again.
>
Hi Daniel

Thank you for explanation.
At this point there is no intermediate stage as we grab the arguments
as a raw string from the command line option -remote:

-remote rid=8,exec=qemu-scsi-dev,command="-drive id=drive_image2,,file=/root/remote-process-disk.img"

So the command="" string is being later parsed into the array and remote process
gets spawned with the "char **argv".

Stefan expressed his concern that its not convenient to use due to
the double escaping commas, spaces, quotes and we do agree with that.
We were seeking an advice on what is the better approach.

Few things we discussed internally is to have the remote drive
command line options passed over by messages or using QMP.

Thank you!
Elena


> I understand this is probably more complex, because we're having to pass
> this across processes, via QemuOpts IIUC, but I still believe it is important
> to have this data kept in array format if at all practical.
> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
>
Daniel P. Berrangé March 4, 2020, 4:33 p.m. UTC | #7
On Wed, Mar 04, 2020 at 08:25:34AM -0800, Elena Ufimtseva wrote:
> On Wed, Mar 04, 2020 at 11:00:32AM +0000, Daniel P. Berrangé wrote:
> > On Mon, Mar 02, 2020 at 02:39:37PM -0800, Elena Ufimtseva wrote:
> > > On Mon, Mar 02, 2020 at 05:47:45PM +0000, Daniel P. Berrangé wrote:
> > > > On Mon, Mar 02, 2020 at 06:36:13PM +0100, Philippe Mathieu-Daudé wrote:
> > > > > typo "multi" in patch subject.
> > > > >
> > > Thank Philippe, will fix.
> > >  
> > > > > On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> > > > > > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > > 
> > > > > > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > > > > > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > > > > > ---
> > > > > >   v4 -> v5:
> > > > > >    - Added "exec" suboption to get the executable's name
> > > > > >    - Addressed feedback about variable names
> > > > > >    - Removed redundant check for spawning a process
> > > > > > 
> > > > > >   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
> > > > > >   include/hw/proxy/qemu-proxy.h |  2 +-
> > > > > >   2 files changed, 54 insertions(+), 16 deletions(-)
> > > > > > 
> > > > > > diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> > > > > > index 828bbd7..d792e86 100644
> > > > > > --- a/hw/proxy/qemu-proxy.c
> > > > > > +++ b/hw/proxy/qemu-proxy.c
> > > > > > @@ -19,19 +19,50 @@
> > > > > >   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
> > > > > > +static int add_argv(char *opts_str, char **argv, int argc)
> > > > > > +{
> > > > > > +    int max_args = 64;
> > > > > > +
> > > > > > +    if (argc < max_args - 1) {
> > > > > > +        argv[argc++] = opts_str;
> > > > > > +        argv[argc] = 0;
> > > > > > +    } else {
> > > > > > +        return 0;
> > > > > > +    }
> > > > > > +
> > > > > > +    return argc;
> > > > > > +}
> > > > > > +
> > > > > > +static int make_argv(char *opts_str, char **argv, int argc)
> > > > > > +{
> > > > > > +    int max_args = 64;
> > > > > > +
> > > > > > +    char *p2 = strtok(opts_str, " ");
> > > > > > +    while (p2 && argc < max_args - 1) {
> > > > > > +        argv[argc++] = p2;
> > > > > > +        p2 = strtok(0, " ");
> > > > > > +    }
> > > > > > +    argv[argc] = 0;
> > > > > 
> > > > > Is there a GLib function to do that?
> > > >
> > > 
> > > Hi Daniel
> > > 
> > > > g_shell_parse_argv() perhaps
> > > >
> > > 
> > > Thanks for the suggestion.
> > > 
> > > >   https://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html
> > > > 
> > > > 
> > > > Though my preference would be to avoid the need to do this at all, by
> > > > not accepting a raw shell command line string in the first place.
> > > >
> > > Can you please clarify? Did you mean that it would be better if Qemu somehow
> > > verifies the options and then passes it to a remote process via a message?
> > 
> > I've not been able to trace the code paths back all the way, so I can't
> > point to where I think needs fixing. I assuming that something, somewhere
> > in this patch series should starts out with a binary name and a list of argv
> > as an array of char *. ie a "char **argv".  At some point this array gets
> > mashed together into a single 'char *' string where all the argv are separated
> > by a space. This patch now tries to parse this and turn it back into a
> > "char **argv" array.
> > 
> > So my key point is that we should try hard to avoid this intermediate
> > shell command line string stage entirely. Always keep the argv in an array
> > form, and never mash them together such that they then need parsing again.
> >
> Hi Daniel
> 
> Thank you for explanation.
> At this point there is no intermediate stage as we grab the arguments
> as a raw string from the command line option -remote:
> 
> -remote rid=8,exec=qemu-scsi-dev,command="-drive id=drive_image2,,file=/root/remote-process-disk.img"
> 
> So the command="" string is being later parsed into the array and remote process
> gets spawned with the "char **argv".
> 
> Stefan expressed his concern that its not convenient to use due to
> the double escaping commas, spaces, quotes and we do agree with that.
> We were seeking an advice on what is the better approach.

I've not looked closely enough to understand the range of different
options we need to be able to pass to the remote QEMU ? Is it just
"-drive" options, or can it be absolutely any QEMU option ?

If it is just -drive, then I could imagine a -remote-drive option
such that we end up with with a set of args

   $QEMU \
   -remote rid=8,exec=qemu-scsi-dev \
   -remote-drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
   -remote-drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img \
   -remote rid=9,exec=qemu-scsi-dev \
   -remote-drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
   -remote-drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img

And this gets turned into 2 execs:

   qemu-scsi-dev \
   -drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
   -drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img
   
   qemu-scsi-dev \
   -drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
   -drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img


Or maybe instead of having a '-remote-drive' arg, we can make the '-drive'
arg take an optional "rid" attribute to associate it with the remote process

   $QEMU \
   -remote rid=8,exec=qemu-scsi-dev \
   -drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
   -drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img \
   -remote rid=9,exec=qemu-scsi-dev \
   -drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
   -drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img

When 'rid' is seen, instead of creating a local block backend, the
args get used for the remote process.

This would have the nice user behaviour that you can take an existing
QEMU command line, and turn it into a multi-process command line
simply by adding the '-remote ...' arg, and adding 'rid=NN' to
each -drive. Nothing else about your existing command line need
change.

> Few things we discussed internally is to have the remote drive
> command line options passed over by messages or using QMP.

Regards,
Daniel
Elena Ufimtseva March 4, 2020, 10:37 p.m. UTC | #8
On Wed, Mar 04, 2020 at 04:33:57PM +0000, Daniel P. Berrangé wrote:
> On Wed, Mar 04, 2020 at 08:25:34AM -0800, Elena Ufimtseva wrote:
> > On Wed, Mar 04, 2020 at 11:00:32AM +0000, Daniel P. Berrangé wrote:
> > > On Mon, Mar 02, 2020 at 02:39:37PM -0800, Elena Ufimtseva wrote:
> > > > On Mon, Mar 02, 2020 at 05:47:45PM +0000, Daniel P. Berrangé wrote:
> > > > > On Mon, Mar 02, 2020 at 06:36:13PM +0100, Philippe Mathieu-Daudé wrote:
> > > > > > typo "multi" in patch subject.
> > > > > >
> > > > Thank Philippe, will fix.
> > > >  
> > > > > > On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> > > > > > > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > > > 
> > > > > > > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > > > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > > > > > > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > > > > > > ---
> > > > > > >   v4 -> v5:
> > > > > > >    - Added "exec" suboption to get the executable's name
> > > > > > >    - Addressed feedback about variable names
> > > > > > >    - Removed redundant check for spawning a process
> > > > > > > 
> > > > > > >   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
> > > > > > >   include/hw/proxy/qemu-proxy.h |  2 +-
> > > > > > >   2 files changed, 54 insertions(+), 16 deletions(-)
> > > > > > > 
> > > > > > > diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> > > > > > > index 828bbd7..d792e86 100644
> > > > > > > --- a/hw/proxy/qemu-proxy.c
> > > > > > > +++ b/hw/proxy/qemu-proxy.c
> > > > > > > @@ -19,19 +19,50 @@
> > > > > > >   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
> > > > > > > +static int add_argv(char *opts_str, char **argv, int argc)
> > > > > > > +{
> > > > > > > +    int max_args = 64;
> > > > > > > +
> > > > > > > +    if (argc < max_args - 1) {
> > > > > > > +        argv[argc++] = opts_str;
> > > > > > > +        argv[argc] = 0;
> > > > > > > +    } else {
> > > > > > > +        return 0;
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    return argc;
> > > > > > > +}
> > > > > > > +
> > > > > > > +static int make_argv(char *opts_str, char **argv, int argc)
> > > > > > > +{
> > > > > > > +    int max_args = 64;
> > > > > > > +
> > > > > > > +    char *p2 = strtok(opts_str, " ");
> > > > > > > +    while (p2 && argc < max_args - 1) {
> > > > > > > +        argv[argc++] = p2;
> > > > > > > +        p2 = strtok(0, " ");
> > > > > > > +    }
> > > > > > > +    argv[argc] = 0;
> > > > > > 
> > > > > > Is there a GLib function to do that?
> > > > >
> > > > 
> > > > Hi Daniel
> > > > 
> > > > > g_shell_parse_argv() perhaps
> > > > >
> > > > 
> > > > Thanks for the suggestion.
> > > > 
> > > > >   https://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html
> > > > > 
> > > > > 
> > > > > Though my preference would be to avoid the need to do this at all, by
> > > > > not accepting a raw shell command line string in the first place.
> > > > >
> > > > Can you please clarify? Did you mean that it would be better if Qemu somehow
> > > > verifies the options and then passes it to a remote process via a message?
> > > 
> > > I've not been able to trace the code paths back all the way, so I can't
> > > point to where I think needs fixing. I assuming that something, somewhere
> > > in this patch series should starts out with a binary name and a list of argv
> > > as an array of char *. ie a "char **argv".  At some point this array gets
> > > mashed together into a single 'char *' string where all the argv are separated
> > > by a space. This patch now tries to parse this and turn it back into a
> > > "char **argv" array.
> > > 
> > > So my key point is that we should try hard to avoid this intermediate
> > > shell command line string stage entirely. Always keep the argv in an array
> > > form, and never mash them together such that they then need parsing again.
> > >
> > Hi Daniel
> > 
> > Thank you for explanation.
> > At this point there is no intermediate stage as we grab the arguments
> > as a raw string from the command line option -remote:
> > 
> > -remote rid=8,exec=qemu-scsi-dev,command="-drive id=drive_image2,,file=/root/remote-process-disk.img"
> > 
> > So the command="" string is being later parsed into the array and remote process
> > gets spawned with the "char **argv".
> > 
> > Stefan expressed his concern that its not convenient to use due to
> > the double escaping commas, spaces, quotes and we do agree with that.
> > We were seeking an advice on what is the better approach.
> 
> I've not looked closely enough to understand the range of different
> options we need to be able to pass to the remote QEMU ? Is it just
> "-drive" options, or can it be absolutely any QEMU option ?
> 
> If it is just -drive, then I could imagine a -remote-drive option
> such that we end up with with a set of args
> 
>    $QEMU \
>    -remote rid=8,exec=qemu-scsi-dev \
>    -remote-drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
>    -remote-drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img \
>    -remote rid=9,exec=qemu-scsi-dev \
>    -remote-drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
>    -remote-drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> 
> And this gets turned into 2 execs:
> 
>    qemu-scsi-dev \
>    -drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
>    -drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img
>    
>    qemu-scsi-dev \
>    -drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
>    -drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> 
> 
> Or maybe instead of having a '-remote-drive' arg, we can make the '-drive'
> arg take an optional "rid" attribute to associate it with the remote process
> 
>    $QEMU \
>    -remote rid=8,exec=qemu-scsi-dev \
>    -drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
>    -drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img \
>    -remote rid=9,exec=qemu-scsi-dev \
>    -drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
>    -drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> 
> When 'rid' is seen, instead of creating a local block backend, the
> args get used for the remote process.
> 
> This would have the nice user behaviour that you can take an existing
> QEMU command line, and turn it into a multi-process command line
> simply by adding the '-remote ...' arg, and adding 'rid=NN' to
> each -drive. Nothing else about your existing command line need
> change.

This does look good, especially unmodified -drive.
And -monitor for the remote process could also be similarly added
with only rid specified instead of plugging it into the raw string.

Stefan did mention in the another patch that he thinks that adding
-remote option is too invasive and suggested using -object itself
to further separate remote process devices.

So to compile both replies, the command line for the remote process
will look something like this:


-object remote-device,id=rid0,exec=qemu-scsi-dev \
-device remote-pci-device,id=scsi0,remote-device=rid0 \
-device scsi-hd,drive=drive_image1,bus=scsi0.0,scsi-id=0,remote-device=rid0 \
-drive id=drive_image3,file=/root/remote-process-disk3.img,remote-device=rid0 \
-drive id=drive_image4,file=/root/remote-process-disk4.img,remote-device=rid0 \
-monitor unix:/home/qmp-sock,,server,,nowait,remote-device=rid0

And in experimental version we imply that remote-pci-device is the LSI controller.
For vfio-over-socket it will represent any remote PCI device.

What your thoughts on this?

Thank you!
Elena


Stefan, 
> 
> > Few things we discussed internally is to have the remote drive
> > command line options passed over by messages or using QMP.
> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
>
Daniel P. Berrangé March 5, 2020, 8:21 a.m. UTC | #9
On Wed, Mar 04, 2020 at 02:37:43PM -0800, Elena Ufimtseva wrote:
> On Wed, Mar 04, 2020 at 04:33:57PM +0000, Daniel P. Berrangé wrote:
> > On Wed, Mar 04, 2020 at 08:25:34AM -0800, Elena Ufimtseva wrote:
> > > On Wed, Mar 04, 2020 at 11:00:32AM +0000, Daniel P. Berrangé wrote:
> > > > On Mon, Mar 02, 2020 at 02:39:37PM -0800, Elena Ufimtseva wrote:
> > > > > On Mon, Mar 02, 2020 at 05:47:45PM +0000, Daniel P. Berrangé wrote:
> > > > > > On Mon, Mar 02, 2020 at 06:36:13PM +0100, Philippe Mathieu-Daudé wrote:
> > > > > > > typo "multi" in patch subject.
> > > > > > >
> > > > > Thank Philippe, will fix.
> > > > >  
> > > > > > > On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> > > > > > > > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > > > > 
> > > > > > > > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > > > > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > > > > > > > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > > > > > > > ---
> > > > > > > >   v4 -> v5:
> > > > > > > >    - Added "exec" suboption to get the executable's name
> > > > > > > >    - Addressed feedback about variable names
> > > > > > > >    - Removed redundant check for spawning a process
> > > > > > > > 
> > > > > > > >   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
> > > > > > > >   include/hw/proxy/qemu-proxy.h |  2 +-
> > > > > > > >   2 files changed, 54 insertions(+), 16 deletions(-)
> > > > > > > > 
> > > > > > > > diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> > > > > > > > index 828bbd7..d792e86 100644
> > > > > > > > --- a/hw/proxy/qemu-proxy.c
> > > > > > > > +++ b/hw/proxy/qemu-proxy.c
> > > > > > > > @@ -19,19 +19,50 @@
> > > > > > > >   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
> > > > > > > > +static int add_argv(char *opts_str, char **argv, int argc)
> > > > > > > > +{
> > > > > > > > +    int max_args = 64;
> > > > > > > > +
> > > > > > > > +    if (argc < max_args - 1) {
> > > > > > > > +        argv[argc++] = opts_str;
> > > > > > > > +        argv[argc] = 0;
> > > > > > > > +    } else {
> > > > > > > > +        return 0;
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    return argc;
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static int make_argv(char *opts_str, char **argv, int argc)
> > > > > > > > +{
> > > > > > > > +    int max_args = 64;
> > > > > > > > +
> > > > > > > > +    char *p2 = strtok(opts_str, " ");
> > > > > > > > +    while (p2 && argc < max_args - 1) {
> > > > > > > > +        argv[argc++] = p2;
> > > > > > > > +        p2 = strtok(0, " ");
> > > > > > > > +    }
> > > > > > > > +    argv[argc] = 0;
> > > > > > > 
> > > > > > > Is there a GLib function to do that?
> > > > > >
> > > > > 
> > > > > Hi Daniel
> > > > > 
> > > > > > g_shell_parse_argv() perhaps
> > > > > >
> > > > > 
> > > > > Thanks for the suggestion.
> > > > > 
> > > > > >   https://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html
> > > > > > 
> > > > > > 
> > > > > > Though my preference would be to avoid the need to do this at all, by
> > > > > > not accepting a raw shell command line string in the first place.
> > > > > >
> > > > > Can you please clarify? Did you mean that it would be better if Qemu somehow
> > > > > verifies the options and then passes it to a remote process via a message?
> > > > 
> > > > I've not been able to trace the code paths back all the way, so I can't
> > > > point to where I think needs fixing. I assuming that something, somewhere
> > > > in this patch series should starts out with a binary name and a list of argv
> > > > as an array of char *. ie a "char **argv".  At some point this array gets
> > > > mashed together into a single 'char *' string where all the argv are separated
> > > > by a space. This patch now tries to parse this and turn it back into a
> > > > "char **argv" array.
> > > > 
> > > > So my key point is that we should try hard to avoid this intermediate
> > > > shell command line string stage entirely. Always keep the argv in an array
> > > > form, and never mash them together such that they then need parsing again.
> > > >
> > > Hi Daniel
> > > 
> > > Thank you for explanation.
> > > At this point there is no intermediate stage as we grab the arguments
> > > as a raw string from the command line option -remote:
> > > 
> > > -remote rid=8,exec=qemu-scsi-dev,command="-drive id=drive_image2,,file=/root/remote-process-disk.img"
> > > 
> > > So the command="" string is being later parsed into the array and remote process
> > > gets spawned with the "char **argv".
> > > 
> > > Stefan expressed his concern that its not convenient to use due to
> > > the double escaping commas, spaces, quotes and we do agree with that.
> > > We were seeking an advice on what is the better approach.
> > 
> > I've not looked closely enough to understand the range of different
> > options we need to be able to pass to the remote QEMU ? Is it just
> > "-drive" options, or can it be absolutely any QEMU option ?
> > 
> > If it is just -drive, then I could imagine a -remote-drive option
> > such that we end up with with a set of args
> > 
> >    $QEMU \
> >    -remote rid=8,exec=qemu-scsi-dev \
> >    -remote-drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
> >    -remote-drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img \
> >    -remote rid=9,exec=qemu-scsi-dev \
> >    -remote-drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
> >    -remote-drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> > 
> > And this gets turned into 2 execs:
> > 
> >    qemu-scsi-dev \
> >    -drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
> >    -drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img
> >    
> >    qemu-scsi-dev \
> >    -drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
> >    -drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> > 
> > 
> > Or maybe instead of having a '-remote-drive' arg, we can make the '-drive'
> > arg take an optional "rid" attribute to associate it with the remote process
> > 
> >    $QEMU \
> >    -remote rid=8,exec=qemu-scsi-dev \
> >    -drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
> >    -drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img \
> >    -remote rid=9,exec=qemu-scsi-dev \
> >    -drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
> >    -drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> > 
> > When 'rid' is seen, instead of creating a local block backend, the
> > args get used for the remote process.
> > 
> > This would have the nice user behaviour that you can take an existing
> > QEMU command line, and turn it into a multi-process command line
> > simply by adding the '-remote ...' arg, and adding 'rid=NN' to
> > each -drive. Nothing else about your existing command line need
> > change.
> 
> This does look good, especially unmodified -drive.
> And -monitor for the remote process could also be similarly added
> with only rid specified instead of plugging it into the raw string.
> 
> Stefan did mention in the another patch that he thinks that adding
> -remote option is too invasive and suggested using -object itself
> to further separate remote process devices.
> 
> So to compile both replies, the command line for the remote process
> will look something like this:
> 
> 
> -object remote-device,id=rid0,exec=qemu-scsi-dev \
> -device remote-pci-device,id=scsi0,remote-device=rid0 \
> -device scsi-hd,drive=drive_image1,bus=scsi0.0,scsi-id=0,remote-device=rid0 \
> -drive id=drive_image3,file=/root/remote-process-disk3.img,remote-device=rid0 \
> -drive id=drive_image4,file=/root/remote-process-disk4.img,remote-device=rid0 \
> -monitor unix:/home/qmp-sock,,server,,nowait,remote-device=rid0
> 
> And in experimental version we imply that remote-pci-device is the LSI controller.
> For vfio-over-socket it will represent any remote PCI device.
> 
> What your thoughts on this?

Looks like a reasonable idea to me. I guess I'm not sure how much the block
maintainers will like having a -drive additional property. Probably depends
how much it impacts the code paths processing it.


Regards,
Daniel
Stefan Hajnoczi March 6, 2020, 4:25 p.m. UTC | #10
On Thu, Mar 05, 2020 at 08:21:14AM +0000, Daniel P. Berrangé wrote:
> On Wed, Mar 04, 2020 at 02:37:43PM -0800, Elena Ufimtseva wrote:
> > On Wed, Mar 04, 2020 at 04:33:57PM +0000, Daniel P. Berrangé wrote:
> > > On Wed, Mar 04, 2020 at 08:25:34AM -0800, Elena Ufimtseva wrote:
> > > > On Wed, Mar 04, 2020 at 11:00:32AM +0000, Daniel P. Berrangé wrote:
> > > > > On Mon, Mar 02, 2020 at 02:39:37PM -0800, Elena Ufimtseva wrote:
> > > > > > On Mon, Mar 02, 2020 at 05:47:45PM +0000, Daniel P. Berrangé wrote:
> > > > > > > On Mon, Mar 02, 2020 at 06:36:13PM +0100, Philippe Mathieu-Daudé wrote:
> > > > > > > > typo "multi" in patch subject.
> > > > > > > >
> > > > > > Thank Philippe, will fix.
> > > > > >  
> > > > > > > > On 2/24/20 9:55 PM, Jagannathan Raman wrote:
> > > > > > > > > From: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > > > > > 
> > > > > > > > > Signed-off-by: Elena Ufimtseva <elena.ufimtseva@oracle.com>
> > > > > > > > > Signed-off-by: Jagannathan Raman <jag.raman@oracle.com>
> > > > > > > > > Signed-off-by: John G Johnson <john.g.johnson@oracle.com>
> > > > > > > > > ---
> > > > > > > > >   v4 -> v5:
> > > > > > > > >    - Added "exec" suboption to get the executable's name
> > > > > > > > >    - Addressed feedback about variable names
> > > > > > > > >    - Removed redundant check for spawning a process
> > > > > > > > > 
> > > > > > > > >   hw/proxy/qemu-proxy.c         | 68 +++++++++++++++++++++++++++++++++----------
> > > > > > > > >   include/hw/proxy/qemu-proxy.h |  2 +-
> > > > > > > > >   2 files changed, 54 insertions(+), 16 deletions(-)
> > > > > > > > > 
> > > > > > > > > diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
> > > > > > > > > index 828bbd7..d792e86 100644
> > > > > > > > > --- a/hw/proxy/qemu-proxy.c
> > > > > > > > > +++ b/hw/proxy/qemu-proxy.c
> > > > > > > > > @@ -19,19 +19,50 @@
> > > > > > > > >   static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
> > > > > > > > > +static int add_argv(char *opts_str, char **argv, int argc)
> > > > > > > > > +{
> > > > > > > > > +    int max_args = 64;
> > > > > > > > > +
> > > > > > > > > +    if (argc < max_args - 1) {
> > > > > > > > > +        argv[argc++] = opts_str;
> > > > > > > > > +        argv[argc] = 0;
> > > > > > > > > +    } else {
> > > > > > > > > +        return 0;
> > > > > > > > > +    }
> > > > > > > > > +
> > > > > > > > > +    return argc;
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static int make_argv(char *opts_str, char **argv, int argc)
> > > > > > > > > +{
> > > > > > > > > +    int max_args = 64;
> > > > > > > > > +
> > > > > > > > > +    char *p2 = strtok(opts_str, " ");
> > > > > > > > > +    while (p2 && argc < max_args - 1) {
> > > > > > > > > +        argv[argc++] = p2;
> > > > > > > > > +        p2 = strtok(0, " ");
> > > > > > > > > +    }
> > > > > > > > > +    argv[argc] = 0;
> > > > > > > > 
> > > > > > > > Is there a GLib function to do that?
> > > > > > >
> > > > > > 
> > > > > > Hi Daniel
> > > > > > 
> > > > > > > g_shell_parse_argv() perhaps
> > > > > > >
> > > > > > 
> > > > > > Thanks for the suggestion.
> > > > > > 
> > > > > > >   https://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html
> > > > > > > 
> > > > > > > 
> > > > > > > Though my preference would be to avoid the need to do this at all, by
> > > > > > > not accepting a raw shell command line string in the first place.
> > > > > > >
> > > > > > Can you please clarify? Did you mean that it would be better if Qemu somehow
> > > > > > verifies the options and then passes it to a remote process via a message?
> > > > > 
> > > > > I've not been able to trace the code paths back all the way, so I can't
> > > > > point to where I think needs fixing. I assuming that something, somewhere
> > > > > in this patch series should starts out with a binary name and a list of argv
> > > > > as an array of char *. ie a "char **argv".  At some point this array gets
> > > > > mashed together into a single 'char *' string where all the argv are separated
> > > > > by a space. This patch now tries to parse this and turn it back into a
> > > > > "char **argv" array.
> > > > > 
> > > > > So my key point is that we should try hard to avoid this intermediate
> > > > > shell command line string stage entirely. Always keep the argv in an array
> > > > > form, and never mash them together such that they then need parsing again.
> > > > >
> > > > Hi Daniel
> > > > 
> > > > Thank you for explanation.
> > > > At this point there is no intermediate stage as we grab the arguments
> > > > as a raw string from the command line option -remote:
> > > > 
> > > > -remote rid=8,exec=qemu-scsi-dev,command="-drive id=drive_image2,,file=/root/remote-process-disk.img"
> > > > 
> > > > So the command="" string is being later parsed into the array and remote process
> > > > gets spawned with the "char **argv".
> > > > 
> > > > Stefan expressed his concern that its not convenient to use due to
> > > > the double escaping commas, spaces, quotes and we do agree with that.
> > > > We were seeking an advice on what is the better approach.
> > > 
> > > I've not looked closely enough to understand the range of different
> > > options we need to be able to pass to the remote QEMU ? Is it just
> > > "-drive" options, or can it be absolutely any QEMU option ?
> > > 
> > > If it is just -drive, then I could imagine a -remote-drive option
> > > such that we end up with with a set of args
> > > 
> > >    $QEMU \
> > >    -remote rid=8,exec=qemu-scsi-dev \
> > >    -remote-drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
> > >    -remote-drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img \
> > >    -remote rid=9,exec=qemu-scsi-dev \
> > >    -remote-drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
> > >    -remote-drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> > > 
> > > And this gets turned into 2 execs:
> > > 
> > >    qemu-scsi-dev \
> > >    -drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
> > >    -drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img
> > >    
> > >    qemu-scsi-dev \
> > >    -drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
> > >    -drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> > > 
> > > 
> > > Or maybe instead of having a '-remote-drive' arg, we can make the '-drive'
> > > arg take an optional "rid" attribute to associate it with the remote process
> > > 
> > >    $QEMU \
> > >    -remote rid=8,exec=qemu-scsi-dev \
> > >    -drive rid=8,id=drive_image1,file=/root/remote-process-disk1.img \
> > >    -drive rid=8,id=drive_image2,file=/root/remote-process-disk2.img \
> > >    -remote rid=9,exec=qemu-scsi-dev \
> > >    -drive rid=9,id=drive_image3,file=/root/remote-process-disk3.img \
> > >    -drive rid=9,id=drive_image4,file=/root/remote-process-disk4.img
> > > 
> > > When 'rid' is seen, instead of creating a local block backend, the
> > > args get used for the remote process.
> > > 
> > > This would have the nice user behaviour that you can take an existing
> > > QEMU command line, and turn it into a multi-process command line
> > > simply by adding the '-remote ...' arg, and adding 'rid=NN' to
> > > each -drive. Nothing else about your existing command line need
> > > change.
> > 
> > This does look good, especially unmodified -drive.
> > And -monitor for the remote process could also be similarly added
> > with only rid specified instead of plugging it into the raw string.
> > 
> > Stefan did mention in the another patch that he thinks that adding
> > -remote option is too invasive and suggested using -object itself
> > to further separate remote process devices.
> > 
> > So to compile both replies, the command line for the remote process
> > will look something like this:
> > 
> > 
> > -object remote-device,id=rid0,exec=qemu-scsi-dev \
> > -device remote-pci-device,id=scsi0,remote-device=rid0 \
> > -device scsi-hd,drive=drive_image1,bus=scsi0.0,scsi-id=0,remote-device=rid0 \
> > -drive id=drive_image3,file=/root/remote-process-disk3.img,remote-device=rid0 \
> > -drive id=drive_image4,file=/root/remote-process-disk4.img,remote-device=rid0 \
> > -monitor unix:/home/qmp-sock,,server,,nowait,remote-device=rid0
> > 
> > And in experimental version we imply that remote-pci-device is the LSI controller.
> > For vfio-over-socket it will represent any remote PCI device.
> > 
> > What your thoughts on this?
> 
> Looks like a reasonable idea to me. I guess I'm not sure how much the block
> maintainers will like having a -drive additional property. Probably depends
> how much it impacts the code paths processing it.

Not all of the remote program's command-line options may be known to
QEMU, so passing through just a few options like -drive does not fix the
problem.

I suggest just providing the arguments as a single string parameter like
this patch series already does, but make sure to consider the escaping
rules for commas and other special characters.

Stefan
diff mbox series

Patch

diff --git a/hw/proxy/qemu-proxy.c b/hw/proxy/qemu-proxy.c
index 828bbd7..d792e86 100644
--- a/hw/proxy/qemu-proxy.c
+++ b/hw/proxy/qemu-proxy.c
@@ -19,19 +19,50 @@ 
 
 static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
 
+static int add_argv(char *opts_str, char **argv, int argc)
+{
+    int max_args = 64;
+
+    if (argc < max_args - 1) {
+        argv[argc++] = opts_str;
+        argv[argc] = 0;
+    } else {
+        return 0;
+    }
+
+    return argc;
+}
+
+static int make_argv(char *opts_str, char **argv, int argc)
+{
+    int max_args = 64;
+
+    char *p2 = strtok(opts_str, " ");
+    while (p2 && argc < max_args - 1) {
+        argv[argc++] = p2;
+        p2 = strtok(0, " ");
+    }
+    argv[argc] = 0;
+
+    return argc;
+}
+
 static int remote_spawn(PCIProxyDev *pdev, const char *opts,
                         const char *exec_name, Error **errp)
 {
-    char *args[3];
     pid_t rpid;
     int fd[2] = {-1, -1};
     Error *local_error = NULL;
+    char *argv[64];
+    int argc = 0;
+    char *sfd;
+    char *exec_dir;
     int rc = -EINVAL;
 
     if (pdev->managed) {
         /* Child is forked by external program (such as libvirt). */
         error_setg(errp, "Remote processed is managed and launched by external program");
-        return -1;
+        return rc;
     }
 
     if (!exec_name) {
@@ -41,32 +72,38 @@  static int remote_spawn(PCIProxyDev *pdev, const char *opts,
 
     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
         error_setg(errp, "Unable to create unix socket.");
-        return -1;
+        return rc;
     }
+    exec_dir = g_strdup_printf("%s/%s", qemu_get_exec_dir(), exec_name);
+    argc = add_argv(exec_dir, argv, argc);
+    sfd = g_strdup_printf("%d", fd[1]);
+    argc = add_argv(sfd, argv, argc);
+    argc = make_argv((char *)opts, argv, argc);
+
     /* TODO: Restrict the forked process' permissions and capabilities. */
     rpid = qemu_fork(&local_error);
 
     if (rpid == -1) {
         error_setg(errp, "Unable to spawn emulation program.");
         close(fd[0]);
-        close(fd[1]);
-        return -1;
+        goto fail;
     }
 
     if (rpid == 0) {
         close(fd[0]);
 
-        args[0] = g_strdup(exec_name);
-        args[1] = g_strdup_printf("%d", fd[1]);
-        args[2] = NULL;
-        execvp(args[0], (char *const *)args);
+        rc = execv(argv[0], (char *const *)argv);
         exit(1);
     }
     pdev->remote_pid = rpid;
+    pdev->socket = fd[0];
+
+    rc = 0;
 
+fail:
     close(fd[1]);
 
-    return 0;
+    return rc;
 }
 
 static int get_proxy_sock(PCIDevice *dev)
@@ -177,16 +214,17 @@  static void pci_proxy_dev_register_types(void)
 type_init(pci_proxy_dev_register_types)
 
 static void init_proxy(PCIDevice *dev, char *command, char *exec_name,
-                       Error **errp)
+                       bool need_spawn, Error **errp)
 {
     PCIProxyDev *pdev = PCI_PROXY_DEV(dev);
     Error *local_error = NULL;
 
     if (!pdev->managed) {
-        if (command) {
-            remote_spawn(pdev, command, exec_name, &local_error);
-        } else {
-            return;
+        if (need_spawn) {
+            if (remote_spawn(pdev, command, exec_name, &local_error)) {
+                error_propagate(errp, local_error);
+                return;
+            }
         }
     } else {
         pdev->remote_pid = atoi(pdev->rid);
diff --git a/include/hw/proxy/qemu-proxy.h b/include/hw/proxy/qemu-proxy.h
index 28b0114..29fa2e9 100644
--- a/include/hw/proxy/qemu-proxy.h
+++ b/include/hw/proxy/qemu-proxy.h
@@ -39,7 +39,7 @@  typedef struct PCIProxyDev {
 
     void (*proxy_ready) (PCIDevice *dev);
     void (*init_proxy) (PCIDevice *dev, char *command, char *exec_name,
-                        Error **errp);
+                        bool need_spawn, Error **errp);
 
 } PCIProxyDev;