This patch implements 'kvm setup' command that can be used to setup a guest
filesystem that shares system libraries and binaries from host filesystem in
read-only mode.

You can setup a new shared rootfs guest with:

  ./kvm setup -n default

and launch it with:

  ./kvm run --9p /,hostfs -p "init=virt/init" -d ~/.kvm-tools/default/

We want to teach 'kvm run' to be able to launch guest filesystems by name in
the future. Furthermore, 'kvm run' should setup a 'default' filesystem and use
it by default unless the user specifies otherwise.

Cc: Asias He <[email protected]>
Cc: Cyrill Gorcunov <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Sasha Levin <[email protected]>
Signed-off-by: Pekka Enberg <[email protected]>
---
 tools/kvm/.gitignore                  |    1 +
 tools/kvm/Documentation/kvm-setup.txt |   15 +++
 tools/kvm/Makefile                    |   11 ++-
 tools/kvm/builtin-setup.c             |  184 +++++++++++++++++++++++++++++++++
 tools/kvm/command-list.txt            |    1 +
 tools/kvm/guest/init.c                |   40 +++++++
 tools/kvm/include/kvm/builtin-setup.h |    7 ++
 tools/kvm/kvm-cmd.c                   |    2 +
 8 files changed, 259 insertions(+), 2 deletions(-)
 create mode 100644 tools/kvm/Documentation/kvm-setup.txt
 create mode 100644 tools/kvm/builtin-setup.c
 create mode 100644 tools/kvm/guest/init.c
 create mode 100644 tools/kvm/include/kvm/builtin-setup.h

diff --git a/tools/kvm/.gitignore b/tools/kvm/.gitignore
index 852d052..6ace4ec 100644
--- a/tools/kvm/.gitignore
+++ b/tools/kvm/.gitignore
@@ -6,4 +6,5 @@ tags
 include/common-cmds.h
 tests/boot/boot_test.iso
 tests/boot/rootfs/
+guest/init
 KVMTOOLS-VERSION-FILE
diff --git a/tools/kvm/Documentation/kvm-setup.txt 
b/tools/kvm/Documentation/kvm-setup.txt
new file mode 100644
index 0000000..c845d17
--- /dev/null
+++ b/tools/kvm/Documentation/kvm-setup.txt
@@ -0,0 +1,15 @@
+kvm-setup(1)
+================
+
+NAME
+----
+kvm-setup - Setup a new virtual machine
+
+SYNOPSIS
+--------
+[verse]
+'kvm setup <name>'
+
+DESCRIPTION
+-----------
+The command setups a virtual machine.
diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index 669386f..25cbd7e 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -20,6 +20,8 @@ TAGS  := ctags
 
 PROGRAM        := kvm
 
+GUEST_INIT := guest/init
+
 OBJS   += builtin-balloon.o
 OBJS   += builtin-debug.o
 OBJS   += builtin-help.o
@@ -28,6 +30,7 @@ OBJS  += builtin-stat.o
 OBJS   += builtin-pause.o
 OBJS   += builtin-resume.o
 OBJS   += builtin-run.o
+OBJS   += builtin-setup.o
 OBJS   += builtin-stop.o
 OBJS   += builtin-version.o
 OBJS   += cpuid.o
@@ -159,7 +162,7 @@ WARNINGS += -Wunused-result
 
 CFLAGS += $(WARNINGS)
 
-all: $(PROGRAM)
+all: $(PROGRAM) $(GUEST_INIT)
 
 KVMTOOLS-VERSION-FILE:
        @$(SHELL_PATH) util/KVMTOOLS-VERSION-GEN $(OUTPUT)
@@ -169,6 +172,10 @@ $(PROGRAM): $(DEPS) $(OBJS)
        $(E) "  LINK    " $@
        $(Q) $(CC) $(OBJS) $(LIBS) -o $@
 
+$(GUEST_INIT): guest/init.c
+       $(E) "  LINK    " $@
+       $(Q) $(CC) -static guest/init.c -o $@
+
 $(DEPS):
 
 %.d: %.c
@@ -240,7 +247,7 @@ clean:
        $(Q) rm -f bios/bios-rom.h
        $(Q) rm -f tests/boot/boot_test.iso
        $(Q) rm -rf tests/boot/rootfs/
-       $(Q) rm -f $(DEPS) $(OBJS) $(PROGRAM)
+       $(Q) rm -f $(DEPS) $(OBJS) $(PROGRAM) $(GUEST_INIT)
        $(Q) rm -f cscope.*
        $(Q) rm -f $(KVM_INCLUDE)/common-cmds.h
        $(Q) rm -f KVMTOOLS-VERSION-FILE
diff --git a/tools/kvm/builtin-setup.c b/tools/kvm/builtin-setup.c
new file mode 100644
index 0000000..4280fe0
--- /dev/null
+++ b/tools/kvm/builtin-setup.c
@@ -0,0 +1,184 @@
+#include <kvm/util.h>
+#include <kvm/kvm-cmd.h>
+#include <kvm/builtin-setup.h>
+#include <kvm/kvm.h>
+#include <kvm/parse-options.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define KVM_PID_FILE_PATH      "/.kvm-tools/"
+#define HOME_DIR               getenv("HOME")
+
+static const char *instance_name;
+
+static const char * const setup_usage[] = {
+       "kvm setup [-n name]",
+       NULL
+};
+
+static const struct option setup_options[] = {
+       OPT_GROUP("General options:"),
+       OPT_STRING('n', "name", &instance_name, "name", "Instance name"),
+       OPT_END()
+};
+
+static void parse_setup_options(int argc, const char **argv)
+{
+       while (argc != 0) {
+               argc = parse_options(argc, argv, setup_options, setup_usage,
+                               PARSE_OPT_STOP_AT_NON_OPTION);
+               if (argc != 0)
+                       kvm_setup_help();
+       }
+}
+
+void kvm_setup_help(void)
+{
+       usage_with_options(setup_usage, setup_options);
+}
+
+static int copy_file(const char *from, const char *to)
+{
+       int in_fd, out_fd;
+       void *src, *dst;
+       struct stat st;
+       int err = -1;
+
+       in_fd = open(from, O_RDONLY);
+       if (in_fd < 0)
+               return err;
+
+       if (fstat(in_fd, &st) < 0)
+               goto error_close_in;
+
+       out_fd = open(to, O_RDWR | O_CREAT | O_TRUNC, st.st_mode & 
(S_IRWXU|S_IRWXG|S_IRWXO));
+       if (out_fd < 0)
+               goto error_close_in;
+
+       src = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, in_fd, 0);
+       if (src == MAP_FAILED)
+               goto error_close_out;
+
+       if (ftruncate(out_fd, st.st_size) < 0)
+               goto error_munmap_src;
+
+       dst = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, out_fd, 
0);
+       if (dst == MAP_FAILED)
+               goto error_munmap_src;
+
+       memcpy(dst, src, st.st_size);
+
+       if (fsync(out_fd) < 0)
+               goto error_munmap_dst;
+
+       err = 0;
+
+error_munmap_dst:
+       munmap(dst, st.st_size);
+error_munmap_src:
+       munmap(src, st.st_size);
+error_close_out:
+       close(out_fd);
+error_close_in:
+       close(in_fd);
+
+       return err;
+}
+
+static const char *guestfs_dirs[] = {
+       "/dev",
+       "/etc",
+       "/home",
+       "/host",
+       "/proc",
+       "/root",
+       "/sys",
+       "/var",
+       "/virt",
+};
+
+static const char *guestfs_symlinks[] = {
+       "/bin",
+       "/lib",
+       "/lib64",
+       "/sbin",
+       "/usr",
+};
+
+static int copy_init(const char *guestfs_name)
+{
+       char path[PATH_MAX];
+
+       snprintf(path, PATH_MAX, "%s%s%s/virt/init", HOME_DIR, 
KVM_PID_FILE_PATH, guestfs_name);
+
+       return copy_file("guest/init", path);
+}
+
+static int make_guestfs_symlink(const char *guestfs_name, const char *path)
+{
+       char target[PATH_MAX];
+       char name[PATH_MAX];
+
+       snprintf(name, PATH_MAX, "%s%s%s%s", HOME_DIR, KVM_PID_FILE_PATH, 
guestfs_name, path);
+
+       snprintf(target, PATH_MAX, "/host%s", path);
+
+       return symlink(target, name);
+}
+
+static void make_dir(const char *dir)
+{
+       char name[PATH_MAX];
+
+       snprintf(name, PATH_MAX, "%s%s%s", HOME_DIR, KVM_PID_FILE_PATH, dir);
+
+       mkdir(name, 0777);
+}
+
+static void make_guestfs_dir(const char *guestfs_name, const char *dir)
+{
+       char name[PATH_MAX];
+
+       snprintf(name, PATH_MAX, "%s%s", guestfs_name, dir);
+
+       make_dir(name);
+}
+
+static int do_setup(const char *guestfs_name)
+{
+       unsigned int i;
+
+       make_dir(guestfs_name);
+
+       for (i = 0; i < ARRAY_SIZE(guestfs_dirs); i++)
+               make_guestfs_dir(guestfs_name, guestfs_dirs[i]);
+
+       for (i = 0; i < ARRAY_SIZE(guestfs_symlinks); i++) {
+               make_guestfs_symlink(guestfs_name, guestfs_symlinks[i]);
+       }
+
+       return copy_init(guestfs_name);
+}
+
+int kvm_cmd_setup(int argc, const char **argv, const char *prefix)
+{
+       parse_setup_options(argc, argv);
+
+       if (instance_name == NULL)
+               kvm_setup_help();
+
+       return do_setup(instance_name);
+}
diff --git a/tools/kvm/command-list.txt b/tools/kvm/command-list.txt
index f201d75..0d16c62 100644
--- a/tools/kvm/command-list.txt
+++ b/tools/kvm/command-list.txt
@@ -3,6 +3,7 @@
 # command name                 category [deprecated] [common]
 #
 kvm-run                                mainporcelain common
+kvm-setup                      mainporcelain common
 kvm-pause                      common
 kvm-resume                     common
 kvm-version                    common
diff --git a/tools/kvm/guest/init.c b/tools/kvm/guest/init.c
new file mode 100644
index 0000000..837acfb
--- /dev/null
+++ b/tools/kvm/guest/init.c
@@ -0,0 +1,40 @@
+/*
+ * This is a simple init for shared rootfs guests. It brings up critical
+ * mountpoints and then launches /bin/sh.
+ */
+#include <sys/mount.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+
+static int run_process(char *filename)
+{
+       char *new_argv[] = { filename, NULL };
+       char *new_env[] = { NULL };
+
+       return execve(filename, new_argv, new_env);
+}
+
+static void do_mounts(void)
+{
+       mount("hostfs", "/host", "9p", MS_RDONLY, 
"trans=virtio,version=9p2000.L");
+       mount("", "/sys", "sysfs", 0, NULL);
+       mount("proc", "/proc", "proc", 0, NULL);
+       mount("devtmpfs", "/dev", "devtmpfs", 0, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+       puts("Mounting...");
+
+       do_mounts();
+
+       puts("Starting '/bin/sh'...");
+
+       run_process("/bin/sh");
+
+       printf("Init failed: %s\n", strerror(errno));
+
+       return 0;
+}
diff --git a/tools/kvm/include/kvm/builtin-setup.h 
b/tools/kvm/include/kvm/builtin-setup.h
new file mode 100644
index 0000000..b0eb345
--- /dev/null
+++ b/tools/kvm/include/kvm/builtin-setup.h
@@ -0,0 +1,7 @@
+#ifndef KVM__SETUP_H
+#define KVM__SETUP_H
+
+int kvm_cmd_setup(int argc, const char **argv, const char *prefix);
+void kvm_setup_help(void);
+
+#endif
diff --git a/tools/kvm/kvm-cmd.c b/tools/kvm/kvm-cmd.c
index 8fc6d22..96108a8 100644
--- a/tools/kvm/kvm-cmd.c
+++ b/tools/kvm/kvm-cmd.c
@@ -11,6 +11,7 @@
 #include "kvm/builtin-balloon.h"
 #include "kvm/builtin-list.h"
 #include "kvm/builtin-version.h"
+#include "kvm/builtin-setup.h"
 #include "kvm/builtin-stop.h"
 #include "kvm/builtin-stat.h"
 #include "kvm/builtin-help.h"
@@ -29,6 +30,7 @@ struct cmd_struct kvm_commands[] = {
        { "stop",       kvm_cmd_stop,           kvm_stop_help,          0 },
        { "stat",       kvm_cmd_stat,           kvm_stat_help,          0 },
        { "help",       kvm_cmd_help,           NULL,                   0 },
+       { "setup",      kvm_cmd_setup,          kvm_setup_help,         0 },
        { "run",        kvm_cmd_run,            kvm_run_help,           0 },
        { NULL,         NULL,                   NULL,                   0 },
 };
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to