WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] Re: [PATCH V3] libxl, Introduce a QMP client

On Wed, 2011-06-15 at 19:44 +0100, Anthony PERARD wrote:
> On Wed, Jun 15, 2011 at 11:04, Ian Campbell <Ian.Campbell@xxxxxxxxxxxxx> 
> wrote:
> > On Tue, 2011-06-14 at 18:15 +0100, Anthony Perard wrote:
> >> From: Anthony PERARD <anthony.perard@xxxxxxxxxx>
> >>
> >> QMP stands for QEMU Monitor Protocol and it is used to query information
> >> from QEMU or to control QEMU.
> >>
> >> This implementation will ask QEMU the list of chardevice and store the
> >> path to serial0 in xenstored. So we will be able to use xl console with
> >> QEMU upstream.
> >>
> >> In order to connect to the QMP server, a socket file is created in
> >> /var/run/xen/qmp-$(domid).
> >>
> >> Signed-off-by: Anthony PERARD <anthony.perard@xxxxxxxxxx>
> >> ---
> >>
> >> Change v2->v3:
> >>   - Use of a timeout when wait for a reply from the server.
> >>   - Use of a command list, a list of pair "command" + callback. It's 
> >> associated
> >>     with an enum.
> >>   - Introduce libxl__qmp_initializations that will ask of all informations 
> >> need
> >>     through QMP. (It's just a rename of libxl__qmp_get_serial_console_path 
> >> from
> >>     the previous patch.)
> >
> > I would have sworn that initiali[zs]ations wasn't the plural of
> > initiali[zs]e (it sounds wrong to my ear) and that it was one of those
> > words with the same plural and singular form, but the Internet tells me
> > I'm wrong...
> >
> > On the other hand libxl__qmp_domain_create works, so would something
> > like libxl__qmp_gather_initial_info, the later conveys what's actually
> > going on too...
> 
> Actually, we could also set up the VNC password by this way. So in
> this case, it will not be anymore only "gather initial info".

Sure. In which case libxl__qmp_domain_create (or _setup) works.

> >> @@ -245,6 +245,12 @@ static char ** 
> >> libxl__build_device_model_args_new(libxl__gc *gc,
> >>      flexarray_vappend(dm_args, dm,
> >>                        "-xen-domid", libxl__sprintf(gc, "%d", 
> >> info->domid), NULL);
> >>
> >> +    flexarray_append(dm_args, "-qmp");
> >> +    flexarray_append(dm_args,
> >> +                     libxl__sprintf(gc, "unix:%s/qmp-%d,server,nowait",
> >> +                                    libxl_run_dir_path(),
> >> +                                    info->domid));
> >
> > Presumably qemu will clean this socket up in the normal shutdown case.
> > Do we need to do so as well to handle crashes and the like?
> 
> Actually, qemu doesn't remove the socket. So we will have do clean it
> when the domain is cleaned. Is libxl_domain_destroy() a good function
> to do that?

I think so. There must be a function somewhere which signals qemu to
shutdown (right?) -- I guess that be a good place?

> >> +        if (errno == ENOENT || errno == ECONNREFUSED) {
> >> +            /* ENOENT       : Socket may not have shown up yet
> >> +             * ECONNREFUSED : Leftover socket hasn't been removed yet */
> >> +            continue;
> >> +        }
> >> +        return -1;
> >> +    } while ((++i <= timeout * 5) && (usleep(.2 * 1000000) <= 0));
> >
> > usleep (effectively) takes an unsigned int, what typedoes .2 * x become?
> 
> It should be a float. But have 0.2 * 10^6 will be better to understand
> that we have 0.2 second of sleep, but not power in C. If you prefere,
> I will just wrote 200000, should be OK.

My concern was just the floating * int multiplication going into a
function which takes an int. I'm sure it all works out through the up-
and down-casting and constant propagation etc and turns into the right
thing, I just wondered if we could avoid it and make things clearer at
the same time.


> >> +#ifdef DEBUG_RECEIVE
> >> +    qmp->buffer[rd] = 0;
> >> +    LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG, "received: '%s'", qmp->buffer);
> >> +#endif
> >> +
> >> +    while (bytes_parsed < rd) {
> > [...]
> >> +        /* skip the CRLF of the end of a command */
> >> +        while (bytes_parsed < rd && (qmp->buffer[bytes_parsed] ==
> >> '\r'
> >> +                                     || qmp->buffer[bytes_parsed] ==
> >> '\n')) {
> >> +               bytes_parsed++;
> >> +        }
> >
> > The parser doesn't just eat \r & \n?
> 
> It does not eat it when it finished to parse a response but it will
> eat these after. So the loop do another round and end up with a
> partiel json data and an allocated parser.
> 
> This little loop is just here to avoid doing a not necessary turn but
> is not necessary

It seems worthwhile then.

> > [...]
> >> +static int qmp_send(libxl__qmp_handler *qmp, const char *cmd, 
> >> qmp_callback_t callback)
> >> +{
> >> +    yajl_gen_config conf = { 0, NULL };
> >> +    const unsigned char *buf;
> >> +    const char *ex = "execute";
> >> +    unsigned int len = 0;
> >> +    yajl_gen_status s;
> >> +    yajl_gen hand;
> >> +
> >> +    hand = yajl_gen_alloc(&conf, NULL);
> >> +    if (!hand) {
> >> +        return -1;
> >> +    }
> >> +
> >> +    yajl_gen_map_open(hand);
> >> +    yajl_gen_string(hand, (const unsigned char *)ex, strlen(ex));
> >> +    yajl_gen_string(hand, (const unsigned char *)cmd, strlen(cmd));
> >> +    yajl_gen_string(hand, (const unsigned char *)"id", 2);
> >
> > ex is a variable and "id" is not?
> 
> Well, it was easier to cound the char in "id" than in "execute", that
> why there is a ex variable. But I think the next step would to eithier
> use a #define QMP_{EXECUTE,ID} or a static const char* global to the
> file.
> 
> > You'd think yajl would have yajl_gen_asciiz or something.
> 
> Unfortunatly, there is no such thing, at least in the debian version
> (1.?). I did not check the 2.? version of libyajl.

Perhaps we should have our own little helper function/macro which does
this?

> >> +    yajl_gen_integer(hand, ++qmp->last_id_used);
> >> +    yajl_gen_map_close(hand);
> >> +
> >> +    s = yajl_gen_get_buf(hand, &buf, &len);
> >> +
> >> +    if (s) {
> >> +        LIBXL__LOG(qmp->ctx, LIBXL__LOG_ERROR,
> >> +                   "could not generate a qmp command");
> >> +        return -1;
> >> +    }
> >> +
> >> +    if (callback) {
> >> +        callback_id_pair *elm = malloc(sizeof (callback_id_pair));
> >> +        elm->id = qmp->last_id_used;
> >> +        elm->callback = callback;
> >> +        SIMPLEQ_INSERT_TAIL(&qmp->callback_list, elm, next);
> >> +    }
> >> +
> >> +    LIBXL__LOG(qmp->ctx, LIBXL__LOG_DEBUG, "next qmp command: '%s'", buf);
> >> +
> >> +    write(qmp->qmp_fd, buf, len);
> >> +    write(qmp->qmp_fd, "\r\n", 2);
> >
> > These need to handle partial writes?
> 
> What did you mean ?
> Call write twice was easier here than call a snprintf or other.

I meant handling of write() returning < len or EINTR, EAGAIN etc.

> Thanks for the review,

No problem!

Ian.


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel