This commit adds the <bootloader_stdin> node to the domain definition,
with the following semantics:
To pass standard input verbatim to the bootloader, set
<bootloader_stdin>some stdin</bootloader_stdin>
Multiline standard input can be set using a CDATA tag:
<bootloader_stdin><![CDATA[
this standard input
will be passed in with
newlines and indentation.
]]></bootloader_stdin>
Standard input can be read from a file as follows:
<bootloader_stdin file="/path/to/some/file"/>
Signed-off-by: Fabian Freyer <fabian.freyer(a)physik.tu-berlin.de>
---
docs/formatdomain.html.in | 19 ++++++
docs/schemas/domaincommon.rng | 10 ++++
src/bhyve/bhyve_driver.c | 10 ++++
src/bhyve/bhyve_parse_command.c | 70 ++++++++++++++++++++++
src/bhyve/bhyve_process.c | 22 +++++++
src/conf/domain_conf.c | 41 +++++++++++++
src/conf/domain_conf.h | 11 ++++
.../bhyveargv2xml-loader-stdin-file.args | 9 +++
.../bhyveargv2xml-loader-stdin-file.xml | 19 ++++++
.../bhyveargv2xml-loader-stdin-multiline.args | 13 ++++
.../bhyveargv2xml-loader-stdin-multiline.xml | 21 +++++++
.../bhyveargv2xml-loader-stdin-oneline.args | 11 ++++
.../bhyveargv2xml-loader-stdin-oneline.xml | 19 ++++++
tests/bhyveargv2xmltest.c | 3 +
.../bhyvexml2argv-grub-stdin-file.args | 9 +++
.../bhyvexml2argv-grub-stdin-file.devmap | 1 +
.../bhyvexml2argv-grub-stdin-file.ldargs | 4 ++
.../bhyvexml2argv-grub-stdin-file.xml | 25 ++++++++
.../bhyvexml2argv-grub-stdin-multiline.args | 9 +++
.../bhyvexml2argv-grub-stdin-multiline.devmap | 1 +
.../bhyvexml2argv-grub-stdin-multiline.ldargs | 4 ++
.../bhyvexml2argv-grub-stdin-multiline.xml | 30 ++++++++++
.../bhyvexml2argv-grub-stdin-oneline.args | 9 +++
.../bhyvexml2argv-grub-stdin-oneline.devmap | 1 +
.../bhyvexml2argv-grub-stdin-oneline.ldargs | 4 ++
.../bhyvexml2argv-grub-stdin-oneline.xml | 25 ++++++++
tests/bhyvexml2argvtest.c | 3 +
.../bhyvexml2xmlout-grub-stdin-file.xml | 34 +++++++++++
.../bhyvexml2xmlout-grub-stdin-multiline.xml | 39 ++++++++++++
.../bhyvexml2xmlout-grub-stdin-oneline.xml | 34 +++++++++++
tests/bhyvexml2xmltest.c | 3 +
31 files changed, 513 insertions(+)
create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-file.args
create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-file.xml
create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-multiline.args
create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-multiline.xml
create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-oneline.args
create mode 100644 tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-oneline.xml
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.args
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.devmap
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.ldargs
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.xml
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.args
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.devmap
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.ldargs
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.xml
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.args
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.devmap
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.ldargs
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.xml
create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-file.xml
create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-multiline.xml
create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-oneline.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 5e99884dc..cea024235 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -245,6 +245,11 @@
...
<bootloader>/usr/bin/pygrub</bootloader>
<bootloader_args>--append single</bootloader_args>
+<bootloader_stdin><![CDATA[
+kernel (hd)/path/to/kernel
+initrd (host)/path/to/initrd
+boot
+]]>
...</pre>
<dl>
@@ -259,6 +264,20 @@
command line arguments to be passed to the bootloader.
<span class="since">Since 0.2.3</span>
</dd>
+ <dt><code>bootloader_stdin</code></dt>
+ <dd>The optional <code>bootloader_stdin</code> element specifies
+ standard input to be passed to the bootloader. To pass multiple
+ lines of standard input to the bootloader, wrap the content in
+ a CDATA tag. Instead of specifying the standard input in the
+ domain XML, the path to a file to be read may be given using the
+ <code>file</code> attribute:
+<pre>
+...
+<bootloader_stdin file="/path/to/some/file"/>
+...
+</pre>
+ <span class="since">Since 4.3.0 (bhyve only)</span>
+ </dd>
</dl>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 4cab55f05..a44d88ef3 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1211,6 +1211,16 @@
<text/>
</element>
</optional>
+ <optional>
+ <choice>
+ <element name="bootloader_stdin">
+ <text/>
+ </element>
+ <element name="bootloader_stdin">
+ <attribute name="file"/>
+ </element>
+ </choice>
+ </optional>
</interleave>
</define>
<define name="osbootkernel">
diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c
index 24c4a9c80..7ac3ad3f0 100644
--- a/src/bhyve/bhyve_driver.c
+++ b/src/bhyve/bhyve_driver.c
@@ -743,6 +743,16 @@ bhyveConnectDomainXMLToNative(virConnectPtr conn,
goto cleanup;
virBufferAdd(&buf, virCommandToString(loadcmd), -1);
+
+ if (def->os.bootloaderStdinSource == VIR_DOMAIN_BOOTLOADER_STDIN_FILE)
+ virBufferEscapeString(&buf, " < %s",
def->os.bootloaderStdin);
+ else if (def->os.bootloaderStdinSource
+ == VIR_DOMAIN_BOOTLOADER_STDIN_LITERAL) {
+ virBufferEscapeString(&buf, " << END_LOADER_STDIN\n"
+ "%s\nEND_LOADER_STDIN",
+ def->os.bootloaderStdin);
+ }
+
virBufferAddChar(&buf, '\n');
}
diff --git a/src/bhyve/bhyve_parse_command.c b/src/bhyve/bhyve_parse_command.c
index fcaaed275..ef51a75f1 100644
--- a/src/bhyve/bhyve_parse_command.c
+++ b/src/bhyve/bhyve_parse_command.c
@@ -124,6 +124,8 @@ static int
bhyveCommandLineToArgv(const char *nativeConfig,
int *loader_argc,
char ***loader_argv,
+ char **loader_stdin_buffer,
+ char **loader_stdin_file,
int *bhyve_argc,
char ***bhyve_argv)
{
@@ -139,6 +141,10 @@ bhyveCommandLineToArgv(const char *nativeConfig,
char **_bhyve_argv = NULL;
char **_loader_argv = NULL;
+ virBuffer heredoc = VIR_BUFFER_INITIALIZER;
+ int in_heredoc = 0;
+ char *heredoc_delim = NULL;
+
nativeConfig_unescaped = bhyveParseCommandLineUnescape(nativeConfig);
if (nativeConfig_unescaped == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
@@ -178,6 +184,52 @@ bhyveCommandLineToArgv(const char *nativeConfig,
char **arglist = NULL;
size_t args_count = 0;
size_t args_alloc = 0;
+ char *stdin_redir = NULL;
+
+ /* are we in a heredoc? */
+ if ( in_heredoc ) {
+ if (STRPREFIX(curr, heredoc_delim)) {
+ in_heredoc = 0;
+ *loader_stdin_buffer = virBufferContentAndReset(&heredoc);
+ continue;
+ }
+
+ if (in_heredoc++ == 1)
+ virBufferAsprintf(&heredoc, "%s", curr);
+ else
+ virBufferAsprintf(&heredoc, "\n%s", curr);
+
+ continue;
+ }
+
+ /* check if this line contains standard input redirection. */
+ if ( (stdin_redir = strchr(curr, '<')) ) {
+ if (STREQLEN(stdin_redir, "<<", 2)) {
+ *stdin_redir = '\0';
+ in_heredoc = 1;
+ heredoc_delim = stdin_redir + 2;
+
+ /* skip non-alphanumeric chars */
+ while (*heredoc_delim && !c_isalnum(*heredoc_delim))
+ heredoc_delim ++;
+
+ if (!*heredoc_delim)
+ goto error;
+
+ virBufferFreeAndReset(&heredoc);
+ } else {
+ /* file redirection */
+ *stdin_redir = '\0';
+ stdin_redir ++;
+
+ /* skip non-alphanumeric chars */
+ while (*stdin_redir && !c_isalnum(*stdin_redir))
+ stdin_redir ++;
+
+ if (VIR_STRDUP(*loader_stdin_file, stdin_redir) != 1)
+ goto error;
+ }
+ }
/* iterate over each line, splitting on sequences of ' '. This code is
* adapted from qemu/qemu_parse_command.c. */
@@ -254,12 +306,16 @@ bhyveCommandLineToArgv(const char *nativeConfig,
if (!(*bhyve_argv = _bhyve_argv))
goto error;
+ if (in_heredoc)
+ goto error;
+
virStringListFree(lines);
return 0;
error:
VIR_FREE(_loader_argv);
VIR_FREE(_bhyve_argv);
+ virBufferFreeAndReset(&heredoc);
virStringListFree(lines);
return -1;
}
@@ -869,6 +925,8 @@ bhyveParseCommandLineString(const char* nativeConfig,
char **bhyve_argv = NULL;
int loader_argc = 0;
char **loader_argv = NULL;
+ char *loader_stdin_file = NULL;
+ char *loader_stdin_buffer = NULL;
if (!(def = virDomainDefNew()))
goto cleanup;
@@ -887,12 +945,21 @@ bhyveParseCommandLineString(const char* nativeConfig,
if (bhyveCommandLineToArgv(nativeConfig,
&loader_argc, &loader_argv,
+ &loader_stdin_buffer, &loader_stdin_file,
&bhyve_argc, &bhyve_argv)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to convert the command string to
argv-lists"));
goto error;
}
+ if (loader_stdin_file && !loader_stdin_buffer) {
+ def->os.bootloaderStdinSource = VIR_DOMAIN_BOOTLOADER_STDIN_FILE;
+ def->os.bootloaderStdin = loader_stdin_file;
+ } else if (loader_stdin_buffer && !loader_stdin_file) {
+ def->os.bootloaderStdinSource = VIR_DOMAIN_BOOTLOADER_STDIN_LITERAL,
+ def->os.bootloaderStdin = loader_stdin_buffer;
+ }
+
if (bhyveParseBhyveCommandLine(def, xmlopt, caps, bhyve_argc, bhyve_argv))
goto error;
if (loader_argv && STREQ(loader_argv[0], "/usr/sbin/bhyveload")) {
@@ -906,9 +973,12 @@ bhyveParseCommandLineString(const char* nativeConfig,
cleanup:
virStringListFree(loader_argv);
virStringListFree(bhyve_argv);
+
return def;
error:
virDomainDefFree(def);
+ VIR_FREE(loader_stdin_buffer);
+ VIR_FREE(loader_stdin_file);
def = NULL;
goto cleanup;
}
diff --git a/src/bhyve/bhyve_process.c b/src/bhyve/bhyve_process.c
index 9276d7d36..1a6f783d7 100644
--- a/src/bhyve/bhyve_process.c
+++ b/src/bhyve/bhyve_process.c
@@ -113,6 +113,7 @@ virBhyveProcessStart(virConnectPtr conn,
bhyveDomainObjPrivatePtr priv = vm->privateData;
int ret = -1, rc;
virCapsPtr caps = NULL;
+ int stdinfd = -1;
if (virAsprintf(&logfile, "%s/%s.log",
BHYVE_LOG_DIR, vm->def->name) < 0)
@@ -173,6 +174,26 @@ virBhyveProcessStart(virConnectPtr conn,
if (!(load_cmd = virBhyveProcessBuildLoadCmd(conn, vm->def, devmap_file,
&devicemap)))
goto cleanup;
+
+ switch (vm->def->os.bootloaderStdinSource) {
+ case VIR_DOMAIN_BOOTLOADER_STDIN_NONE:
+ break;
+ case VIR_DOMAIN_BOOTLOADER_STDIN_FILE:
+ if ((stdinfd = open(vm->def->os.bootloaderStdin, O_RDONLY)) < 0) {
+ virReportSystemError(errno, _("Failed to open '%s'"),
+ vm->def->os.bootloaderStdin);
+ goto cleanup;
+ }
+ virCommandSetInputFD(load_cmd, stdinfd);
+ break;
+ case VIR_DOMAIN_BOOTLOADER_STDIN_LITERAL:
+ virCommandSetInputBuffer(load_cmd, vm->def->os.bootloaderStdin);
+ break;
+ /* coverity[dead_error_begin] */
+ case VIR_DOMAIN_BOOTLOADER_STDIN_LAST:
+ break;
+ }
+
virCommandSetOutputFD(load_cmd, &logfd);
virCommandSetErrorFD(load_cmd, &logfd);
@@ -252,6 +273,7 @@ virBhyveProcessStart(virConnectPtr conn,
virCommandFree(load_cmd);
virCommandFree(cmd);
VIR_FREE(logfile);
+ VIR_FORCE_CLOSE(stdinfd);
VIR_FORCE_CLOSE(logfd);
return ret;
}
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index d23182f18..d99ecf9f7 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -3037,6 +3037,8 @@ void virDomainDefFree(virDomainDefPtr def)
VIR_FREE(def->os.bootloader);
VIR_FREE(def->os.bootloaderArgs);
+ VIR_FREE(def->os.bootloaderStdin);
+
virDomainClockDefClear(&def->clock);
VIR_FREE(def->name);
@@ -18700,6 +18702,16 @@ virDomainDefParseXML(xmlDocPtr xml,
def->os.bootloader = virXPathString("string(./bootloader)", ctxt);
def->os.bootloaderArgs = virXPathString("string(./bootloader_args)",
ctxt);
+ if ((def->os.bootloaderStdin =
virXPathString("string(./bootloader_stdin/"
+ "@file)", ctxt)))
+ def->os.bootloaderStdinSource = VIR_DOMAIN_BOOTLOADER_STDIN_FILE;
+ else if ((def->os.bootloaderStdin = virXPathString("string("
+ "./bootloader_stdin)",
+ ctxt)))
+ def->os.bootloaderStdinSource = VIR_DOMAIN_BOOTLOADER_STDIN_LITERAL;
+ else
+ def->os.bootloaderStdinSource = VIR_DOMAIN_BOOTLOADER_STDIN_NONE;
+
tmp = virXPathString("string(./os/type[1])", ctxt);
if (!tmp) {
if (def->os.bootloader) {
@@ -26717,6 +26729,35 @@ virDomainDefFormatInternal(virDomainDefPtr def,
virBufferEscapeString(buf,
"<bootloader_args>%s</bootloader_args>\n",
def->os.bootloaderArgs);
+
+ switch (def->os.bootloaderStdinSource) {
+ case VIR_DOMAIN_BOOTLOADER_STDIN_NONE:
+ break;
+ case VIR_DOMAIN_BOOTLOADER_STDIN_FILE:
+ virBufferEscapeString(buf, "<bootloader_stdin
file=\"%s\"/>\n",
+ def->os.bootloaderStdin);
+ break;
+ case VIR_DOMAIN_BOOTLOADER_STDIN_LITERAL:
+ if (strchr(def->os.bootloaderStdin, '\n')
+ || strchr(def->os.bootloaderStdin, '<')
+ || strchr(def->os.bootloaderStdin, '>')
+ || strchr(def->os.bootloaderStdin, '&'))
+ {
+ virBufferEscapeString(buf,
+
"<bootloader_stdin><![CDATA[%s]]>"
+ "</bootloader_stdin>\n",
+ def->os.bootloaderStdin);
+ } else {
+ virBufferEscapeString(buf,
+ "<bootloader_stdin>%s"
+ "</bootloader_stdin>\n",
+ def->os.bootloaderStdin);
+ }
+ break;
+ /* coverity[dead_error_begin] */
+ case VIR_DOMAIN_BOOTLOADER_STDIN_LAST:
+ break;
+ }
}
virBufferAddLit(buf, "<os>\n");
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index bbaa24137..41af6cc8a 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1897,6 +1897,15 @@ struct _virDomainOSEnv {
char *value;
};
+/* Bootloader standard input source */
+typedef enum {
+ VIR_DOMAIN_BOOTLOADER_STDIN_NONE = 0,
+ VIR_DOMAIN_BOOTLOADER_STDIN_FILE,
+ VIR_DOMAIN_BOOTLOADER_STDIN_LITERAL,
+
+ VIR_DOMAIN_BOOTLOADER_STDIN_LAST
+} virDomainBootloaderStdinSource;
+
typedef struct _virDomainOSDef virDomainOSDef;
typedef virDomainOSDef *virDomainOSDefPtr;
struct _virDomainOSDef {
@@ -1923,6 +1932,8 @@ struct _virDomainOSDef {
virDomainLoaderDefPtr loader;
char *bootloader;
char *bootloaderArgs;
+ virDomainBootloaderStdinSource bootloaderStdinSource;
+ char *bootloaderStdin;
int smbios_mode;
virDomainBIOSDef bios;
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-file.args
b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-file.args
new file mode 100644
index 000000000..ca51f2f04
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-file.args
@@ -0,0 +1,9 @@
+/usr/bin/custom-loader \
+-s ome \
+--args < path/to/some/file
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-H \
+-P \
+-s 0:0,hostbridge bhyve
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-file.xml
b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-file.xml
new file mode 100644
index 000000000..a56a4c451
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-file.xml
@@ -0,0 +1,19 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <bootloader>/usr/bin/custom-loader</bootloader>
+ <bootloader_args>-s ome --args</bootloader_args>
+ <bootloader_stdin file="path/to/some/file"/>
+ <os>
+ <type>hvm</type>
+ </os>
+ <clock offset='localtime'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>destroy</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ </devices>
+</domain>
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-multiline.args
b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-multiline.args
new file mode 100644
index 000000000..050ddf442
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-multiline.args
@@ -0,0 +1,13 @@
+/usr/bin/custom-loader \
+-s ome \
+--args << END_OF_THIS_HEREDOC
+some
+standard input
+here
+END_OF_THIS_HEREDOC
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-H \
+-P \
+-s 0:0,hostbridge bhyve
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-multiline.xml
b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-multiline.xml
new file mode 100644
index 000000000..496b5ea87
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-multiline.xml
@@ -0,0 +1,21 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <bootloader>/usr/bin/custom-loader</bootloader>
+ <bootloader_args>-s ome --args</bootloader_args>
+ <bootloader_stdin><![CDATA[some
+standard input
+here]]></bootloader_stdin>
+ <os>
+ <type>hvm</type>
+ </os>
+ <clock offset='localtime'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>destroy</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ </devices>
+</domain>
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-oneline.args
b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-oneline.args
new file mode 100644
index 000000000..f8bcdcddd
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-oneline.args
@@ -0,0 +1,11 @@
+/usr/bin/custom-loader \
+-s ome \
+--args << END_OF_THIS_HEREDOC
+some standard input here
+END_OF_THIS_HEREDOC
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-H \
+-P \
+-s 0:0,hostbridge bhyve
diff --git a/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-oneline.xml
b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-oneline.xml
new file mode 100644
index 000000000..17c9da664
--- /dev/null
+++ b/tests/bhyveargv2xmldata/bhyveargv2xml-loader-stdin-oneline.xml
@@ -0,0 +1,19 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <bootloader>/usr/bin/custom-loader</bootloader>
+ <bootloader_args>-s ome --args</bootloader_args>
+ <bootloader_stdin>some standard input here</bootloader_stdin>
+ <os>
+ <type>hvm</type>
+ </os>
+ <clock offset='localtime'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>destroy</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ </devices>
+</domain>
diff --git a/tests/bhyveargv2xmltest.c b/tests/bhyveargv2xmltest.c
index e5d78530c..fef01d7da 100644
--- a/tests/bhyveargv2xmltest.c
+++ b/tests/bhyveargv2xmltest.c
@@ -187,6 +187,9 @@ mymain(void)
DO_TEST("memsize-human");
DO_TEST_FAIL("memsize-fail");
DO_TEST("custom-loader");
+ DO_TEST("loader-stdin-file");
+ DO_TEST("loader-stdin-oneline");
+ DO_TEST("loader-stdin-multiline");
DO_TEST("bhyveload-custom");
DO_TEST("bhyveload-vda");
DO_TEST_FAIL("bhyveload-name-mismatch");
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.args
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.args
new file mode 100644
index 000000000..3ba5c1160
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.args
@@ -0,0 +1,9 @@
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-u \
+-H \
+-P \
+-s 0:0,hostbridge \
+-s 2:0,ahci,hd:/tmp/freebsd.img \
+-s 3:0,virtio-net,faketapdev,mac=52:54:00:ee:f5:79 bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.devmap
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.devmap
new file mode 100644
index 000000000..b312bfdaf
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.devmap
@@ -0,0 +1 @@
+(hd0) /tmp/freebsd.img
\ No newline at end of file
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.ldargs
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.ldargs
new file mode 100644
index 000000000..7d9a5155a
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.ldargs
@@ -0,0 +1,4 @@
+/usr/local/sbin/grub-bhyve \
+--root hd0,msdos1 \
+--device-map '<device.map>' \
+--memory 214 bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.xml
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.xml
new file mode 100644
index 000000000..f804da0db
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-file.xml
@@ -0,0 +1,25 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+ <memory>219136</memory>
+ <vcpu>1</vcpu>
+ <bootloader>/usr/local/sbin/grub-bhyve</bootloader>
+ <bootloader_stdin file="/path/to/some/file"/>
+ <os>
+ <type>hvm</type>
+ </os>
+ <devices>
+ <disk type='file'>
+ <driver name='file' type='raw'/>
+ <source file='/tmp/freebsd.img'/>
+ <target dev='hda' bus='sata'/>
+ <address type='drive' controller='0' bus='0'
target='2' unit='0'/>
+ </disk>
+ <interface type='bridge'>
+ <mac address='52:54:00:ee:f5:79'/>
+ <model type='virtio'/>
+ <source bridge="virbr0"/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.args
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.args
new file mode 100644
index 000000000..3ba5c1160
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.args
@@ -0,0 +1,9 @@
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-u \
+-H \
+-P \
+-s 0:0,hostbridge \
+-s 2:0,ahci,hd:/tmp/freebsd.img \
+-s 3:0,virtio-net,faketapdev,mac=52:54:00:ee:f5:79 bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.devmap
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.devmap
new file mode 100644
index 000000000..b312bfdaf
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.devmap
@@ -0,0 +1 @@
+(hd0) /tmp/freebsd.img
\ No newline at end of file
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.ldargs
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.ldargs
new file mode 100644
index 000000000..7d9a5155a
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.ldargs
@@ -0,0 +1,4 @@
+/usr/local/sbin/grub-bhyve \
+--root hd0,msdos1 \
+--device-map '<device.map>' \
+--memory 214 bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.xml
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.xml
new file mode 100644
index 000000000..456ab0443
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-multiline.xml
@@ -0,0 +1,30 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+ <memory>219136</memory>
+ <vcpu>1</vcpu>
+ <bootloader>/usr/local/sbin/grub-bhyve</bootloader>
+ <bootloader_stdin><![CDATA[
+multiple
+boot
+loader
+commands
+]]></bootloader_stdin>
+ <os>
+ <type>hvm</type>
+ </os>
+ <devices>
+ <disk type='file'>
+ <driver name='file' type='raw'/>
+ <source file='/tmp/freebsd.img'/>
+ <target dev='hda' bus='sata'/>
+ <address type='drive' controller='0' bus='0'
target='2' unit='0'/>
+ </disk>
+ <interface type='bridge'>
+ <mac address='52:54:00:ee:f5:79'/>
+ <model type='virtio'/>
+ <source bridge="virbr0"/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.args
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.args
new file mode 100644
index 000000000..3ba5c1160
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.args
@@ -0,0 +1,9 @@
+/usr/sbin/bhyve \
+-c 1 \
+-m 214 \
+-u \
+-H \
+-P \
+-s 0:0,hostbridge \
+-s 2:0,ahci,hd:/tmp/freebsd.img \
+-s 3:0,virtio-net,faketapdev,mac=52:54:00:ee:f5:79 bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.devmap
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.devmap
new file mode 100644
index 000000000..b312bfdaf
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.devmap
@@ -0,0 +1 @@
+(hd0) /tmp/freebsd.img
\ No newline at end of file
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.ldargs
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.ldargs
new file mode 100644
index 000000000..7d9a5155a
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.ldargs
@@ -0,0 +1,4 @@
+/usr/local/sbin/grub-bhyve \
+--root hd0,msdos1 \
+--device-map '<device.map>' \
+--memory 214 bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.xml
b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.xml
new file mode 100644
index 000000000..03b6987fd
--- /dev/null
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-grub-stdin-oneline.xml
@@ -0,0 +1,25 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+ <memory>219136</memory>
+ <vcpu>1</vcpu>
+ <bootloader>/usr/local/sbin/grub-bhyve</bootloader>
+ <bootloader_stdin>some input commands</bootloader_stdin>
+ <os>
+ <type>hvm</type>
+ </os>
+ <devices>
+ <disk type='file'>
+ <driver name='file' type='raw'/>
+ <source file='/tmp/freebsd.img'/>
+ <target dev='hda' bus='sata'/>
+ <address type='drive' controller='0' bus='0'
target='2' unit='0'/>
+ </disk>
+ <interface type='bridge'>
+ <mac address='52:54:00:ee:f5:79'/>
+ <model type='virtio'/>
+ <source bridge="virbr0"/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/bhyvexml2argvtest.c b/tests/bhyvexml2argvtest.c
index 6f3b0c2eb..e4cb0592e 100644
--- a/tests/bhyvexml2argvtest.c
+++ b/tests/bhyvexml2argvtest.c
@@ -188,6 +188,9 @@ mymain(void)
DO_TEST("grub-defaults");
DO_TEST("grub-bootorder");
DO_TEST("grub-bootorder2");
+ DO_TEST("grub-stdin-file");
+ DO_TEST("grub-stdin-oneline");
+ DO_TEST("grub-stdin-multiline");
DO_TEST("bhyveload-bootorder");
DO_TEST("bhyveload-bootorder1");
DO_TEST_FAILURE("bhyveload-bootorder2");
diff --git a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-file.xml
b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-file.xml
new file mode 100644
index 000000000..f07368d01
--- /dev/null
+++ b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-file.xml
@@ -0,0 +1,34 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <bootloader>/usr/local/sbin/grub-bhyve</bootloader>
+ <bootloader_stdin file="/path/to/some/file"/>
+ <os>
+ <type arch='x86_64'>hvm</type>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <disk type='file' device='disk'>
+ <driver name='file' type='raw'/>
+ <source file='/tmp/freebsd.img'/>
+ <target dev='hda' bus='sata'/>
+ <address type='drive' controller='0' bus='0'
target='2' unit='0'/>
+ </disk>
+ <controller type='pci' index='0' model='pci-root'/>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </controller>
+ <interface type='bridge'>
+ <mac address='52:54:00:ee:f5:79'/>
+ <source bridge='virbr0'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-multiline.xml
b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-multiline.xml
new file mode 100644
index 000000000..eae6df4b4
--- /dev/null
+++ b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-multiline.xml
@@ -0,0 +1,39 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <bootloader>/usr/local/sbin/grub-bhyve</bootloader>
+ <bootloader_stdin><![CDATA[
+multiple
+boot
+loader
+commands
+]]></bootloader_stdin>
+ <os>
+ <type arch='x86_64'>hvm</type>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <disk type='file' device='disk'>
+ <driver name='file' type='raw'/>
+ <source file='/tmp/freebsd.img'/>
+ <target dev='hda' bus='sata'/>
+ <address type='drive' controller='0' bus='0'
target='2' unit='0'/>
+ </disk>
+ <controller type='pci' index='0' model='pci-root'/>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </controller>
+ <interface type='bridge'>
+ <mac address='52:54:00:ee:f5:79'/>
+ <source bridge='virbr0'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-oneline.xml
b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-oneline.xml
new file mode 100644
index 000000000..b038a9065
--- /dev/null
+++ b/tests/bhyvexml2xmloutdata/bhyvexml2xmlout-grub-stdin-oneline.xml
@@ -0,0 +1,34 @@
+<domain type='bhyve'>
+ <name>bhyve</name>
+ <uuid>df3be7e7-a104-11e3-aeb0-50e5492bd3dc</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <bootloader>/usr/local/sbin/grub-bhyve</bootloader>
+ <bootloader_stdin>some input commands</bootloader_stdin>
+ <os>
+ <type arch='x86_64'>hvm</type>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <disk type='file' device='disk'>
+ <driver name='file' type='raw'/>
+ <source file='/tmp/freebsd.img'/>
+ <target dev='hda' bus='sata'/>
+ <address type='drive' controller='0' bus='0'
target='2' unit='0'/>
+ </disk>
+ <controller type='pci' index='0' model='pci-root'/>
+ <controller type='sata' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </controller>
+ <interface type='bridge'>
+ <mac address='52:54:00:ee:f5:79'/>
+ <source bridge='virbr0'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </interface>
+ </devices>
+</domain>
diff --git a/tests/bhyvexml2xmltest.c b/tests/bhyvexml2xmltest.c
index 4d9c1681d..fd386b504 100644
--- a/tests/bhyvexml2xmltest.c
+++ b/tests/bhyvexml2xmltest.c
@@ -98,6 +98,9 @@ mymain(void)
DO_TEST_DIFFERENT("grub-bootorder");
DO_TEST_DIFFERENT("grub-bootorder2");
DO_TEST_DIFFERENT("grub-defaults");
+ DO_TEST_DIFFERENT("grub-stdin-file");
+ DO_TEST_DIFFERENT("grub-stdin-oneline");
+ DO_TEST_DIFFERENT("grub-stdin-multiline");
DO_TEST_DIFFERENT("localtime");
DO_TEST_DIFFERENT("macaddr");
DO_TEST_DIFFERENT("metadata");
--
2.11.0