Skip to content

Commit

Permalink
Initial commit of virtme.
Browse files Browse the repository at this point in the history
Signed-off-by: Andy Lutomirski <[email protected]>
  • Loading branch information
amluto committed Feb 6, 2014
0 parents commit 6fa0e03
Show file tree
Hide file tree
Showing 7 changed files with 612 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*~
__pycache__
339 changes: 339 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
What is virtme?
===============

Virtme is a set of simple tools to run a virtualized Linux kernel that
uses the host Linux distribution instead of a separate image.

Virtme is tiny, easy to use, and makes testing kernel changes quite simple.

Some day this might be useful as a sort of sandbox. Right now it's not
really configurable enough for that.

How to use virtme
=================

You'll need a Linux kernel compiled with these options:

CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_NET_9P=y
CONFIG_NET_9P_VIRTIO=y
CONFIG_9P_FS=y


Virtme does not (yet) support modular virtio or 9p, so you can't use your
distro kernel. Fixing this is a high priority.

Once you have such a kernel, run:

virtme-runkernel PATH_TO_BZIMAGE

On x86, you can usually find a bzImage in `arch/x86/boot/bzImage` once you've
compiled your kernel. To save time (since modules aren't supported yet),
build your kernel with `make bzImage`.

You can then do things like `cd /home/username` and you will have readonly
access to all your files.

Upcoming features
=================

In the near term, the high-priority features are:

* Support for modular virtfs and 9p.
* Some way to configure writable mounts.
74 changes: 74 additions & 0 deletions cpiowriter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/python3
# -*- mode: python -*-
import sys

class CpioWriter(object):
TYPE_DIR = 0o0040000
TYPE_REG = 0o0100000
TYPE_SYMLINK = 0o0120000
TYPE_MASK = 0o0170000

def __init__(self, f):
self.__f = f
self.__totalsize = 0
self.__next_ino = 0

def __write(self, data):
self.__f.write(data)
self.__totalsize += len(data)

def write_object(self, name, body, mode, ino=None, nlink=None,
uid=0, gid=0, mtime=0, devmajor=0, devminor=0,
rdevmajor=0, rdevminor=0):
if nlink is None:
nlink = (2 if (mode & CpioWriter.TYPE_MASK) == CpioWriter.TYPE_DIR
else 1)

if b'\0' in name:
raise ValueError('Filename cannot contain a NUL')

namesize = len(name) + 1

if isinstance(body, bytes):
filesize = len(body)
else:
filesize = body.seek(0, 2)
body.seek(0)

if ino is None:
ino = self.__next_ino
self.__next_ino += 1

fields = [ino, mode, uid, gid, nlink, mtime, filesize,
devmajor, devminor, rdevmajor, rdevminor, namesize, 0]
hdr = ('070701' + ''.join('%08X' % f for f in fields)).encode('ascii')

self.__write(hdr)
self.__write(name)
self.__write(b'\0')
self.__write(((2-namesize) % 4) * b'\0')

if isinstance(body, bytes):
self.__write(body)
else:
while True:
buf = body.read(65536)
if buf == b'':
break
self.__write(buf)

self.__write(((-filesize) % 4) * b'\0')

def write_trailer(self):
self.write_object(name=b'TRAILER!!!', body=b'', mode=0, ino=0, nlink=1)
self.__write(((-self.__totalsize) % 512) * b'\0')

def mkdir(self, name, mode):
self.write_object(name=name, mode=CpioWriter.TYPE_DIR | mode, body=b'')

def symlink(self, src, dst):
self.write_object(name=dst, mode=CpioWriter.TYPE_SYMLINK | 0o777,
body=src)

def write_file(self, name, body, mode):
self.write_object(name=name, body=body, mode=CpioWriter.TYPE_REG | mode)
82 changes: 82 additions & 0 deletions virtme-init
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/bin/sh
# virtme-init: virtme's basic init (PID 1) process
# Copyright © 2013 Andy Lutomirski
# Licensed under the GPLv2, which is available in the virtme distribution
# as a file called LICENSE with SHA-256 hash:
# 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643

export PATH=/bin:/sbin:/usr/bin:/usr/sbin
export TERM=linux

# Set up a minimally functional system
mount -t proc -o nosuid,noexec,nodev /proc/
mount -t sysfs -o nosuid,noexec,nodev /sys/

mount -t tmpfs tmpfs /tmp/
mount -t tmpfs tmpfs /run/

# Find udevd
if [[ -x /usr/lib/systemd/systemd-udevd ]]; then
udevd=/usr/lib/systemd/systemd-udevd
else
udevd=`which udevd`
fi

# Ideally we'll use devtmpfs
if mount -t devtmpfs -o mode=0755,nosuid,noexec none /dev &>/dev/null; then
"$udevd" --daemon --resolve-names=never
else
# The running kernel doesn't have devtmpfs. Use regular tmpfs.
mount -t tmpfs -o mode=0755,nosuid,noexec none /dev

# Make some basic devices first, let udev handle the rest
mknod -m 0666 /dev/null c 1 3
mknod -m 0660 /dev/kmsg c 1 11
mknod -m 0600 /dev/console c 5 1

# Try to get udevd to coldplug everything
"$udevd" --daemon --resolve-names=never
udevadm trigger --action=add >/dev/null 2>&1
udevadm settle
fi

mkdir -p -m 0755 /dev/shm /dev/pts
mount -t devpts -o gid=tty,mode=620,noexec,nosuid devpts /dev/pts
mount -t tmpfs -o mode=1777,nosuid,nodev tmpfs /dev/shm

# Bring up networking
ip link set dev lo up

# Create some VTs
deallocvt
openvt -c 2 -- /bin/bash
openvt -c 3 -- /bin/bash
openvt -c 4 -- /bin/bash

# Figure out what the main console is
consdev="`grep ' ... (.C' /proc/consoles |cut -d' ' -f1`"
if [[ -z "$consdev" ]]; then
echo "Can't deduce console device" >&2
exec bash --login # At least try to be helpful
fi
if [[ "$consdev" == "tty0" ]]; then
consdev=tty1 # sigh
fi
if [[ ! -e "/dev/$consdev" ]]; then
echo "/dev/$consdev doesn't exist."
exec bash --login
fi

echo "virtme-init: console is $consdev"

# Bring up a functioning shell on the console. This is a bit magical:
# We have no controlling terminal because we're attached to a fake
# console device (probably something like /dev/console), which can't
# be a controlling terminal. We are also not a member of a session.
# Init apparently can't setsid (whether that's a limitation of the
# setsid binary or the system call, I don't know).
while true; do
setsid bash 0<>"/dev/$consdev" 1>&0 2>&0
echo "Shell died. Will respawn."
sleep 0.5
done
54 changes: 54 additions & 0 deletions virtme-mkinitramfs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/python3
# -*- mode: python -*-
# virtme-mkinitramfs: Generate an initramfs image for virtme
# Copyright © 2013 Andy Lutomirski
# Licensed under the GPLv2, which is available in the virtme distribution
# as a file called LICENSE with SHA-256 hash:
# 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643

import argparse
import shutil
import cpiowriter
import inspect
import io
import os.path
import shlex

def make_base_layout(cw):
for dir in (b'lib', b'bin', b'var', b'etc', b'newroot'):
cw.mkdir(dir, 0o755)

cw.symlink(b'bin', b'sbin')
cw.symlink(b'lib', b'lib64')

def install_busybox(cw):
bbpath = shutil.which('busybox')
with open(bbpath, 'rb') as busybox:
cw.write_file(name=b'bin/busybox', body=busybox, mode=0o755)

for tool in ('sh', 'mount', 'umount', 'modprobe', 'switch_root'):
cw.symlink(b'busybox', ('bin/%s' % tool).encode('ascii'))

def generate_init():
mypath = os.path.dirname(os.path.abspath(
inspect.getfile(inspect.currentframe())))

out = io.StringIO()
out.write('#!/bin/sh\n')
out.write('echo \'Mounting hostfs...\'\n')
out.write('/bin/mount -t 9p -o ro,version=9p2000.L,trans=virtio,access=any hostroot /newroot/\n')

out.write('exec /bin/switch_root /newroot %s\n' % shlex.quote(os.path.join(mypath, 'virtme-init')))
return out.getvalue().encode('utf-8')

def main():
import sys
cw = cpiowriter.CpioWriter(sys.stdout.buffer)
make_base_layout(cw)
install_busybox(cw)
cw.write_file(b'init', body=generate_init(), mode=0o755)
cw.write_trailer()
return 0

if __name__ == '__main__':
exit(main())
17 changes: 17 additions & 0 deletions virtme-runkernel
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
# virtme-runkernel: A silly script to run a kernel image
# Copyright © 2013 Andy Lutomirski
# Licensed under the GPLv2, which is available in the virtme distribution
# as a file called LICENSE with SHA-256 hash:
# 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643

# This is a giant hack. I'll make it better at some point.

tmpfile="`mktemp --tmpdir initramfs.XXXXXXXXXX`"
exec 3<>"$tmpfile"
rm "$tmpfile"

v_mki="$(dirname $0)/virtme-mkinitramfs"
"$v_mki" >&3

exec qemu-kvm -virtfs local,id=rootfs,path=/,security_model=passthrough,mount_tag=hostroot,readonly -initrd /proc/self/fd/3 -kernel "$@"

0 comments on commit 6fa0e03

Please sign in to comment.