--- /dev/null
+Xen PV Channels
+===============
+
+A channel is a low-bandwidth private byte stream similar to a serial
+link. Typical uses of channels are
+
+ 1. to provide initial configuration information to a VM on boot
+ (example use: CloudStack's cloud-early-config service)
+ 2. to signal/query an in-guest agent
+ (example use: oVirt's guest agent)
+
+Channels are similar to virtio-serial devices and emulated serial links.
+Channels are intended to be used in the implementation of libvirt <channel>s
+when running on Xen.
+
+Note: if an application requires a high-bandwidth link then it should use
+vchan instead.
+
+How to use channels: an example
+-------------------------------
+
+Consider a cloud deployment where VMs are cloned from pre-made templates,
+and customised on first boot by an in-guest agent which sets the IP address,
+hostname, ssh keys etc. To install the system the cloud administrator would
+first:
+
+ 1. Install a guest as normal (no channel configuration necessary)
+ 2. Install the in-guest agent specific to the cloud software. This will
+ prepare the guest to communicate over the channel, and also prepare
+ the guest to be cloned safely (sometimes known as "sysprepping")
+ 3. Shutdown the guest
+ 4. Register the guest as a template with the cloud orchestration software
+ 5. Install the cloud orchestration agent in dom0
+
+At runtime, when a cloud tenant requests that a VM is created from the template,
+the sequence of events would be: (assuming a Linux domU)
+
+ 1. A VM is "cloned" from the template
+ 2. A unique Unix domain socket path in dom0 is allocated
+ (e.g. /my/cloud/software/talk/to/domain/<vm uuid>)
+ 3. Domain configuration is created for the VM, listing the channel
+ name expected by the in-guest agent. In xl syntax this would be:
+
+ channel = [ "connection=socket, name=org.my.cloud.software.agent.version1,
+ path = /my/cloud/software/talk/to/domain/<vm uuid>" ]
+
+ 4. The VM is started
+ 5. In dom0 the cloud orchestration agent connects to the Unix domain
+ socket, writes a handshake message and waits for a reply
+ 6. Assuming the guest kernel has CONFIG_HVC_XEN_FRONTEND set then the console
+ driver will generate a hotplug event
+ 7. A udev rule is activated by the hotplug event.
+
+ The udev rule would look something like:
+
+ SUBSYSTEM=="xen", DEVPATH=="/devices/console-[0-9]", RUN+="xen-console-setup"
+
+ where the "xen-console-setup" script would read the channel name and
+ make a symlink in /dev/xen-channel/org.my.cloud.software.agent.version1
+
+ 8. The in-guest agent uses inotify to see the creation of the /dev/xen-channel
+ symlink and opens the device.
+ 9. The in-guest agent completes the handshake with the dom0 agent
+ 10. The dom0 agent transmits the unique VM configuration: hostname, IP
+ address, ssh keys etc etc
+ 11. The in-guest agent receives the configuration and applies it.
+
+Using channels avoids having to use a temporary disk device or network
+connection.
+
+Design recommendations and pitfalls
+-----------------------------------
+
+It's necessary to install channel-specific software (an "agent") into the guest
+before you can use a channel. By default a channel will appear as a device
+which could be mistaken for a serial port or regular console. It is known
+that some software will proactively seek out serial ports and issue AT commands
+at them; make sure such software is disabled!
+
+Since channels are identified by names, application authors must ensure their
+channel names are unique to avoid clashes. We recommend that channel names
+include parts unique to the application such as a domain names. To assist
+prevent clashes we recommend authors add their names to our global channel
+registry at the end of this document.
+
+Limitations
+-----------
+
+Hotplug and unplug of channels is not currently implemented.
+
+Channel name registry
+---------------------
+
+It is important that channel names are globally unique. To help ensure
+that no-one's name clashes with yours, please add yours to this list.
+
+Key:
+N: Name
+C: Contact
+D: Short description of use, possibly including a URL to your software
+ or API
+
+N: org.xenproject.guest.clipboard.0.1
+C: David Scott <dave.scott@citrix.com>
+D: Share clipboard data via an in-guest agent. See:
+ http://wiki.xenproject.org/wiki/Clipboard_sharing_protocol
Now many years after the introduction of the pv console we have
multiple pv consoles support for pv and hvm guests; multiple pv
-console backends (qemu and xenconsoled) and emulated serial cards too.
+console backends (qemu and xenconsoled, see limitations below) and
+emulated serial cards too.
This document tries to describe how the whole system works and how the
-different components interact with each others.
+different components interact with each other.
The first PV console path in xenstore remains:
/local/domain/$DOMID/device/console/$DEVID.
-The output of a PV console, whether it should be a file, a pty, a
-socket, or something else, is specified by the toolstack in the xenstore
-node "output", under the relevant console section.
-For example:
+PV consoles have
+* (optional) string names;
+* 'connection' information describing to what they should be
+ connected; and
+* a 'type' indicating which daemon should process the data.
+
+We call a PV console with a name a "channel", in reference to the libvirt
+concept with the same name and purpose. The file "channels.txt" describes
+how to use channels and includes a registry of well-known channel names.
+
+If the PV console has a name (i.e. it is a "channel") then the name
+is written to the frontend directory:
+
+name = <name>
+
+If the PV console has no name (i.e. it is a regular console) then the "name"
+key is omitted.
+
+The toolstack writes 'connection' information in the xenstore backend in
+the keys
+* connection: either 'pty' or 'socket'
+* path: only present if connection = 'socket', the path of the socket to
+ glue the channel to
+
+An artifact of the current implementation, the toolstack will write an
+extra backend key
+* output: an identifier only meaningful for qemu/xenconsoled
-# xenstore-read /local/domain/26/device/console/1/output
-pty
+If the toolstack wants the console to be connected to a pty, it will write
+to the backend:
-The backend chosen for a particular console is specified by the
-toolstack in the "type" node on xenstore, under the relevant console
-section.
+connection = pty
+output = pty
+
+The backend will write the pty device name to the "tty" node in the
+console frontend.
+
+If the toolstack wants a listening Unix domain socket to be created at path
+<path>, a connection accepted and data proxied to the console, it will write:
+
+connection = socket
+path = <path>
+output = chardev:<some internal identifier>
+
+where chardev:<some internal identifier> matches a qemu character device
+configured on the qemu command-line.
+
+The backend implementation daemon chosen for a particular console is specified
+by the toolstack in the "type" node in the xenstore frontend.
For example:
# xenstore-read /local/domain/26/console/1/type
-xenconsoled
+ioemu
The supported values are only xenconsoled or ioemu; xenconsoled has
several limitations: it can only be used for the first PV console and it
-can only have a pty as output.
-
-If the output is a pty, backends write the device name to the "tty" node
-in xenstore under the relevant console path.
+can only connect to a pty.
Emulated serials are provided by qemu-dm only to hvm guests; the number
of emulated serials depends on how many "-serial" command line options
/local/domain/$DOMID/serial/$SERIAL_NUM/tty
-
xenconsole is the tool to connect to a PV console or an emulated serial
that has a pty as output. Xenconsole takes a domid as parameter plus an
optional console type (pv for PV consoles or serial for emulated
#define PAGE_TO_MEMKB(pages) ((pages) * 4)
#define BACKEND_STRING_SIZE 5
+/* Utility to read backend xenstore keys */
+#define READ_BACKEND(tgc, subpath) ({ \
+ rc = libxl__xs_read_checked(tgc, XBT_NULL, \
+ GCSPRINTF("%s/" subpath, be_path), \
+ &tmp); \
+ if (rc) goto out; \
+ (char*)tmp; \
+ });
+
int libxl_ctx_alloc(libxl_ctx **pctx, int version,
unsigned flags, xentoollog_logger * lg)
{
libxl_device_nic_init(nic);
-#define READ_BACKEND(tgc, subpath) ({ \
- rc = libxl__xs_read_checked(tgc, XBT_NULL, \
- GCSPRINTF("%s/" subpath, be_path), \
- &tmp); \
- if (rc) goto out; \
- (char*)tmp; \
- });
-
tmp = READ_BACKEND(gc, "handle");
if (tmp)
nic->devid = atoi(tmp);
/******************************************************************************/
int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
libxl__device_console *console,
- libxl__domain_build_state *state)
+ libxl__domain_build_state *state,
+ libxl__device *device)
{
flexarray_t *front, *ro_front;
flexarray_t *back;
- libxl__device device;
int rc;
if (console->devid && state) {
rc = ERROR_INVAL;
goto out;
}
+ if (!console->devid && (console->name || console->path)) {
+ LOG(ERROR, "Primary console has invalid configuration");
+ rc = ERROR_INVAL;
+ goto out;
+ }
front = flexarray_make(gc, 16, 1);
ro_front = flexarray_make(gc, 16, 1);
back = flexarray_make(gc, 16, 1);
- device.backend_devid = console->devid;
- device.backend_domid = console->backend_domid;
- device.backend_kind = LIBXL__DEVICE_KIND_CONSOLE;
- device.devid = console->devid;
- device.domid = domid;
- device.kind = LIBXL__DEVICE_KIND_CONSOLE;
+ device->backend_devid = console->devid;
+ device->backend_domid = console->backend_domid;
+ device->backend_kind = LIBXL__DEVICE_KIND_CONSOLE;
+ device->devid = console->devid;
+ device->domid = domid;
+ device->kind = LIBXL__DEVICE_KIND_CONSOLE;
flexarray_append(back, "frontend-id");
flexarray_append(back, libxl__sprintf(gc, "%d", domid));
flexarray_append(back, "protocol");
flexarray_append(back, LIBXL_XENCONSOLE_PROTOCOL);
+ if (console->name) {
+ flexarray_append(ro_front, "name");
+ flexarray_append(ro_front, console->name);
+ }
+ if (console->connection) {
+ flexarray_append(back, "connection");
+ flexarray_append(back, console->connection);
+ }
+ if (console->path) {
+ flexarray_append(back, "path");
+ flexarray_append(back, console->path);
+ }
+
flexarray_append(front, "backend-id");
flexarray_append(front, libxl__sprintf(gc, "%d", console->backend_domid));
flexarray_append(front, "protocol");
flexarray_append(front, LIBXL_XENCONSOLE_PROTOCOL);
}
-
- libxl__device_generic_add(gc, XBT_NULL, &device,
+ libxl__device_generic_add(gc, XBT_NULL, device,
libxl__xs_kvs_of_flexarray(gc, back, back->count),
libxl__xs_kvs_of_flexarray(gc, front, front->count),
libxl__xs_kvs_of_flexarray(gc, ro_front, ro_front->count));
/******************************************************************************/
+int libxl__init_console_from_channel(libxl__gc *gc,
+ libxl__device_console *console,
+ int dev_num,
+ libxl_device_channel *channel)
+{
+ int rc;
+
+ libxl__device_console_init(console);
+
+ /* Perform validation first, allocate second. */
+
+ if (!channel->name) {
+ LOG(ERROR, "channel %d has no name", channel->devid);
+ return ERROR_INVAL;
+ }
+
+ if (channel->backend_domname) {
+ rc = libxl_domain_qualifier_to_domid(CTX, channel->backend_domname,
+ &channel->backend_domid);
+ if (rc < 0) return rc;
+ }
+
+ /* The xenstore 'output' node tells the backend what to connect the console
+ to. If the channel has "connection = pty" then the "output" node will be
+ set to "pty". If the channel has "connection = socket" then the "output"
+ node will be set to "chardev:libxl-channel%d". This tells the qemu
+ backend to proxy data between the console ring and the character device
+ with id "libxl-channel%d". These character devices are currently defined
+ on the qemu command-line via "-chardev" options in libxl_dm.c */
+
+ switch (channel->connection) {
+ case LIBXL_CHANNEL_CONNECTION_UNKNOWN:
+ LOG(ERROR, "channel %d has no defined connection; "
+ "to where should it be connected?", channel->devid);
+ return ERROR_INVAL;
+ case LIBXL_CHANNEL_CONNECTION_PTY:
+ console->connection = libxl__strdup(NOGC, "pty");
+ console->output = libxl__sprintf(NOGC, "pty");
+ break;
+ case LIBXL_CHANNEL_CONNECTION_SOCKET:
+ if (!channel->u.socket.path) {
+ LOG(ERROR, "channel %d has no path", channel->devid);
+ return ERROR_INVAL;
+ }
+ console->connection = libxl__strdup(NOGC, "socket");
+ console->path = libxl__strdup(NOGC, channel->u.socket.path);
+ console->output = libxl__sprintf(NOGC, "chardev:libxl-channel%d",
+ channel->devid);
+ break;
+ default:
+ /* We've forgotten to add the clause */
+ LOG(ERROR, "%s: missing implementation for channel connection %d",
+ __func__, channel->connection);
+ abort();
+ }
+
+ console->devid = dev_num;
+ console->consback = LIBXL__CONSOLE_BACKEND_IOEMU;
+ console->backend_domid = channel->backend_domid;
+ console->name = libxl__strdup(NOGC, channel->name);
+
+ return 0;
+}
+
+static int libxl__device_channel_from_xs_be(libxl__gc *gc,
+ const char *be_path,
+ libxl_device_channel *channel)
+{
+ const char *tmp;
+ int rc;
+
+ libxl_device_channel_init(channel);
+
+ /* READ_BACKEND is from libxl__device_nic_from_xs_be above */
+ channel->name = READ_BACKEND(NOGC, "name");
+ tmp = READ_BACKEND(gc, "connection");
+ if (!strcmp(tmp, "pty")) {
+ channel->connection = LIBXL_CHANNEL_CONNECTION_PTY;
+ } else if (!strcmp(tmp, "socket")) {
+ channel->connection = LIBXL_CHANNEL_CONNECTION_SOCKET;
+ channel->u.socket.path = READ_BACKEND(NOGC, "path");
+ } else {
+ rc = ERROR_INVAL;
+ goto out;
+ }
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+static int libxl__append_channel_list_of_type(libxl__gc *gc,
+ uint32_t domid,
+ const char *type,
+ libxl_device_channel **channels,
+ int *nchannels)
+{
+ char *fe_path = NULL, *be_path = NULL;
+ char **dir = NULL;
+ unsigned int n = 0, devid = 0;
+ libxl_device_channel *next = NULL;
+ int rc = 0, i;
+
+ fe_path = GCSPRINTF("%s/device/%s",
+ libxl__xs_get_dompath(gc, domid), type);
+ dir = libxl__xs_directory(gc, XBT_NULL, fe_path, &n);
+ if (!dir || !n)
+ goto out;
+
+ for (i = 0; i < n; i++) {
+ const char *p, *name;
+ libxl_device_channel *tmp;
+
+ p = libxl__sprintf(gc, "%s/%s", fe_path, dir[i]);
+ name = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/name", p));
+ /* 'channels' are consoles with names, so ignore all consoles
+ without names */
+ if (!name) continue;
+ be_path = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/backend", p));
+ tmp = realloc(*channels,
+ sizeof(libxl_device_channel) * (*nchannels + devid + 1));
+ if (!tmp) {
+ rc = ERROR_NOMEM;
+ goto out;
+ }
+ *channels = tmp;
+ next = *channels + *nchannels + devid;
+ rc = libxl__device_channel_from_xs_be(gc, be_path, next);
+ if (rc) goto out;
+ next->devid = devid;
+ devid++;
+ }
+ *nchannels += devid;
+ return 0;
+
+ out:
+ return rc;
+}
+
+libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
+ uint32_t domid,
+ int *num)
+{
+ GC_INIT(ctx);
+ libxl_device_channel *channels = NULL;
+ int rc;
+
+ *num = 0;
+
+ rc = libxl__append_channel_list_of_type(gc, domid, "console", &channels, num);
+ if (rc) goto out_err;
+
+ GC_FREE;
+ return channels;
+
+out_err:
+ LOG(ERROR, "Unable to list channels");
+ while (*num) {
+ (*num)--;
+ libxl_device_channel_dispose(&channels[*num]);
+ }
+ free(channels);
+ return NULL;
+}
+
+int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_channel *channel,
+ libxl_channelinfo *channelinfo)
+{
+ GC_INIT(ctx);
+ char *dompath, *fe_path;
+ char *val;
+
+ dompath = libxl__xs_get_dompath(gc, domid);
+ channelinfo->devid = channel->devid;
+
+ fe_path = libxl__sprintf(gc, "%s/device/console/%d", dompath,
+ channelinfo->devid + 1);
+ channelinfo->backend = xs_read(ctx->xsh, XBT_NULL,
+ libxl__sprintf(gc, "%s/backend",
+ fe_path), NULL);
+ if (!channelinfo->backend) {
+ GC_FREE;
+ return ERROR_FAIL;
+ }
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/backend-id", fe_path));
+ channelinfo->backend_id = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
+ channelinfo->state = val ? strtoul(val, NULL, 10) : -1;
+ channelinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
+ GCSPRINTF("%s/frontend",
+ channelinfo->backend), NULL);
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/frontend-id",
+ channelinfo->backend));
+ channelinfo->frontend_id = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
+ channelinfo->rref = val ? strtoul(val, NULL, 10) : -1;
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/port", fe_path));
+ channelinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+ channelinfo->connection = channel->connection;
+ switch (channel->connection) {
+ case LIBXL_CHANNEL_CONNECTION_PTY:
+ val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tty", fe_path));
+ channelinfo->u.pty.path = strdup(val);
+ break;
+ default:
+ break;
+ }
+ GC_FREE;
+ return 0;
+}
+
+/******************************************************************************/
+
int libxl__device_vkb_setdefault(libxl__gc *gc, libxl_device_vkb *vkb)
{
int rc;
DEFINE_DEVICE_REMOVE(vtpm, remove, 0)
DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)
+/* channel/console hotunplug is not implemented. There are 2 possibilities:
+ * 1. add support for secondary consoles to xenconsoled
+ * 2. dynamically add/remove qemu chardevs via qmp messages. */
+
#undef DEFINE_DEVICE_REMOVE
/******************************************************************************/
*/
#define LIBXL_HAVE_BUILDINFO_KERNEL 1
+/*
+ * LIBXL_HAVE_DEVICE_CHANNEL
+ *
+ * If this is defined, then the libxl_device_channel struct exists
+ * and channels can be attached to a domain. Channels manifest as consoles
+ * with names, see docs/misc/console.txt.
+ */
+#define LIBXL_HAVE_DEVICE_CHANNEL 1
+
/* Functions annotated with LIBXL_EXTERNAL_CALLERS_ONLY may not be
* called from within libxl itself. Callers outside libxl, who
* do not #include libxl_internal.h, are fine. */
int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid,
libxl_device_nic *nic, libxl_nicinfo *nicinfo);
+/*
+ * Virtual Channels
+ * Channels manifest as consoles with names, see docs/misc/channels.txt
+ */
+libxl_device_channel *libxl_device_channel_list(libxl_ctx *ctx,
+ uint32_t domid,
+ int *num);
+int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid,
+ libxl_device_channel *channel,
+ libxl_channelinfo *channelinfo);
+
/* Virtual TPMs */
int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm,
const libxl_asyncop_how *ao_how)
return 0;
}
-static int init_console_info(libxl__device_console *console, int dev_num)
+static void init_console_info(libxl__gc *gc,
+ libxl__device_console *console,
+ int dev_num)
{
libxl__device_console_init(console);
console->devid = dev_num;
console->consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
- console->output = strdup("pty");
- if (!console->output)
- return ERROR_NOMEM;
- return 0;
+ console->output = libxl__strdup(NOGC, "pty");
+ /* console->{name,connection,path} are NULL on normal consoles.
+ Only 'channels' when mapped to consoles have a string name. */
}
int libxl__domain_build(libxl__gc *gc,
}
}
+ /* For both HVM and PV the 0th console is a regular console. We
+ map channels to IOEMU consoles starting at 1 */
+ for (i = 0; i < d_config->num_channels; i++) {
+ libxl__device_console console;
+ libxl__device device;
+ ret = libxl__init_console_from_channel(gc, &console, i + 1,
+ &d_config->channels[i]);
+ if ( ret ) {
+ libxl__device_console_dispose(&console);
+ goto error_out;
+ }
+ libxl__device_console_add(gc, domid, &console, NULL, &device);
+ libxl__device_console_dispose(&console);
+ }
+
switch (d_config->c_info.type) {
case LIBXL_DOMAIN_TYPE_HVM:
{
libxl__device_console console;
+ libxl__device device;
libxl_device_vkb vkb;
- ret = init_console_info(&console, 0);
- if ( ret )
- goto error_out;
+ init_console_info(gc, &console, 0);
console.backend_domid = state->console_domid;
- libxl__device_console_add(gc, domid, &console, state);
+ libxl__device_console_add(gc, domid, &console, state, &device);
libxl__device_console_dispose(&console);
libxl_device_vkb_init(&vkb);
{
int need_qemu = 0;
libxl__device_console console;
+ libxl__device device;
for (i = 0; i < d_config->num_vfbs; i++) {
libxl__device_vfb_add(gc, domid, &d_config->vfbs[i]);
libxl__device_vkb_add(gc, domid, &d_config->vkbs[i]);
}
- ret = init_console_info(&console, 0);
- if ( ret )
- goto error_out;
+ init_console_info(gc, &console, 0);
need_qemu = libxl__need_xenpv_qemu(gc, 1, &console,
d_config->num_vfbs, d_config->vfbs,
- d_config->num_disks, &d_config->disks[0]);
+ d_config->num_disks, &d_config->disks[0],
+ d_config->num_channels, &d_config->channels[0]);
console.backend_domid = state->console_domid;
- libxl__device_console_add(gc, domid, &console, state);
+ libxl__device_console_add(gc, domid, &console, state, &device);
libxl__device_console_dispose(&console);
if (need_qemu) {
const libxl_sdl_info *sdl = dm_sdl(guest_config);
const char *keymap = dm_keymap(guest_config);
flexarray_t *dm_args;
- int i;
+ int i, connection, devid;
uint64_t ram_size;
+ const char *path, *chardev;
dm_args = flexarray_make(gc, 16, 1);
flexarray_append(dm_args, "-mon");
flexarray_append(dm_args, "chardev=libxl-cmd,mode=control");
+ for (i = 0; i < guest_config->num_channels; i++) {
+ connection = guest_config->channels[i].connection;
+ devid = guest_config->channels[i].devid;
+ switch (connection) {
+ case LIBXL_CHANNEL_CONNECTION_PTY:
+ chardev = GCSPRINTF("pty,id=libxl-channel%d", devid);
+ break;
+ case LIBXL_CHANNEL_CONNECTION_SOCKET:
+ path = guest_config->channels[i].u.socket.path;
+ chardev = GCSPRINTF("socket,id=libxl-channel%d,path=%s,"
+ "server,nowait", devid, path);
+ break;
+ default:
+ /* We've forgotten to add the clause */
+ LOG(ERROR, "%s: unknown channel connection %d",
+ __func__, connection);
+ return NULL;
+ }
+ flexarray_append(dm_args, "-chardev");
+ flexarray_append(dm_args, (void*)chardev);
+ }
+
/*
* Remove default devices created by qemu. Qemu will create only devices
* defined by xen, since the devices not defined by xen are not usable.
}
for (i = 0; i < num_console; i++) {
+ libxl__device device;
console[i].devid = i;
console[i].consback = LIBXL__CONSOLE_BACKEND_IOEMU;
/* STUBDOM_CONSOLE_LOGGING (console 0) is for minios logging
break;
}
ret = libxl__device_console_add(gc, dm_domid, &console[i],
- i == STUBDOM_CONSOLE_LOGGING ? stubdom_state : NULL);
+ i == STUBDOM_CONSOLE_LOGGING ? stubdom_state : NULL,
+ &device);
if (ret)
goto out;
}
int libxl__need_xenpv_qemu(libxl__gc *gc,
int nr_consoles, libxl__device_console *consoles,
int nr_vfbs, libxl_device_vfb *vfbs,
- int nr_disks, libxl_device_disk *disks)
+ int nr_disks, libxl_device_disk *disks,
+ int nr_channels, libxl_device_channel *channels)
{
int i, ret = 0;
uint32_t domid;
}
}
+ if (nr_channels > 0) {
+ ret = libxl__get_domid(gc, &domid);
+ if (ret) goto out;
+ for (i = 0; i < nr_channels; i++) {
+ if (channels[i].backend_domid == domid) {
+ /* xenconsoled is limited to the first console only.
+ Until this restriction is removed we must use qemu for
+ secondary consoles which includes all channels. */
+ ret = 1;
+ goto out;
+ }
+ }
+ }
+
out:
return ret;
}
_hidden int libxl__device_console_add(libxl__gc *gc, uint32_t domid,
libxl__device_console *console,
- libxl__domain_build_state *state);
+ libxl__domain_build_state *state,
+ libxl__device *device);
/* Returns 1 if device exists, 0 if not, ERROR_* (<0) on error. */
_hidden int libxl__device_exists(libxl__gc *gc, xs_transaction_t t,
const char *state);
_hidden int libxl__nic_type(libxl__gc *gc, libxl__device *dev,
libxl_nic_type *nictype);
+_hidden int libxl__init_console_from_channel(libxl__gc *gc,
+ libxl__device_console *console,
+ int dev_num,
+ libxl_device_channel *channel);
/*
* For each aggregate type which can be used as an input we provide:
_hidden int libxl__need_xenpv_qemu(libxl__gc *gc,
int nr_consoles, libxl__device_console *consoles,
int nr_vfbs, libxl_device_vfb *vfbs,
- int nr_disks, libxl_device_disk *disks);
+ int nr_disks, libxl_device_disk *disks,
+ int nr_channels, libxl_device_channel *channels);
/*
* This function will cause the whole libxl process to hang
(2, "PV"),
], init_val = "LIBXL_DOMAIN_TYPE_INVALID")
+libxl_channel_connection = Enumeration("channel_connection", [
+ (0, "UNKNOWN"),
+ (1, "PTY"),
+ (2, "SOCKET"), # a listening Unix domain socket
+ ])
+
libxl_device_model_version = Enumeration("device_model_version", [
(0, "UNKNOWN"),
(1, "QEMU_XEN_TRADITIONAL"), # Historical qemu-xen device model (qemu-dm)
("cpumap", libxl_bitmap)
], dir=DIR_OUT)
+libxl_channelinfo = Struct("channelinfo", [
+ ("backend", string),
+ ("backend_id", uint32),
+ ("frontend", string),
+ ("frontend_id", uint32),
+ ("devid", libxl_devid),
+ ("state", integer),
+ ("evtch", integer),
+ ("rref", integer),
+ ("u", KeyedUnion(None, libxl_channel_connection, "connection",
+ [("unknown", None),
+ ("pty", Struct(None, [("path", string),])),
+ ("socket", None),
+ ])),
+ ], dir=DIR_OUT)
+
libxl_vminfo = Struct("vminfo", [
("uuid", libxl_uuid),
("domid", libxl_domid),
("uuid", libxl_uuid),
])
+libxl_device_channel = Struct("device_channel", [
+ ("backend_domid", libxl_domid),
+ ("backend_domname", string),
+ ("devid", libxl_devid),
+ ("name", string),
+ ("u", KeyedUnion(None, libxl_channel_connection, "connection",
+ [("unknown", None),
+ ("pty", None),
+ ("socket", Struct(None, [("path", string)])),
+ ])),
+])
+
libxl_domain_config = Struct("domain_config", [
("c_info", libxl_domain_create_info),
("b_info", libxl_domain_build_info),
("vfbs", Array(libxl_device_vfb, "num_vfbs")),
("vkbs", Array(libxl_device_vkb, "num_vkbs")),
("vtpms", Array(libxl_device_vtpm, "num_vtpms")),
+ # a channel manifests as a console with a name,
+ # see docs/misc/channels.txt
+ ("channels", Array(libxl_device_channel, "num_channels")),
("on_poweroff", libxl_action_on_shutdown),
("on_reboot", libxl_action_on_shutdown),
("devid", integer),
("consback", libxl__console_backend),
("output", string),
+ # A regular console has no name.
+ # A console with a name is called a 'channel', see docs/misc/channels.txt
+ ("name", string),
+ ("connection", string),
+ ("path", string),
])
libxl__device_action = Enumeration("device_action", [