# HG changeset patch # User Heiko Schlittermann # Date 1249687227 -7200 # Node ID ebe549994431b25ef3d2df767fb6df0db812ce24 # Parent ed2b863f938f50640da73175df20f3553ffce1c3 started some real work. diff -r ed2b863f938f -r ebe549994431 .hgignore diff -r ed2b863f938f -r ebe549994431 .perltidyrc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.perltidyrc Sat Aug 08 01:20:27 2009 +0200 @@ -0,0 +1,1 @@ +--paren-tightness=2 diff -r ed2b863f938f -r ebe549994431 kvmtool --- a/kvmtool Wed Jan 14 20:26:10 2009 +0100 +++ b/kvmtool Sat Aug 08 01:20:27 2009 +0200 @@ -1,201 +1,130 @@ -#! /bin/bash -# $Id$ -# $URL$ -# status: in development -# 2008 Heiko Schlittermann - -# Should be written in Perl! +#! /usr/bin/perl +# © 2009 Heiko Schlittermann -# Structure of virtual machine directories -# $BASE (see below, hard coded into this script) -# | -# +-- -# | +--conf -# | | +--options -# | | +--id -# | +--var -# | | +--pid -# | | +--running_options -# | +-- -# | +-- .... -# | +-- -# | -# +-- -# | .... -# +-- +use strict; +use warnings; +use Getopt::Long; +use Pod::Usage; +use File::Basename; +use Perl6::Slurp; +use feature qw(:5.10); +use if $ENV{DEBUG} => "Smart::Comments"; +my $ME = basename $0; +my $opt_dev = "/dev/kvm"; +my $opt_dir = "/var/lib/kvm"; + +my $KVM = "kvm"; +my $CONFIG = "%s.kvm.conf"; +my $PIDFILE = "$ENV{HOME}/.kvm-%s.pid"; +my $KILL = 15; -action="${1?}"; shift # mandatory -vm="$1" ; shift # optional - -# no user service able parts below - -PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -unset ${!LC_*} LANG CDPATH +sub _start(@); +sub _kill(@); -ME=$(basename $0) -BASE=/kvm -KVM=$(command -v kvm) -NC=$(command -v nc) -FUSER=$(command -v fuser) -VNCVIEWER=$(command -v vncviewer) -TIMEOUT=30 +MAIN: { - -for f in "/etc/$ME.conf" ~/.$ME; do test -r "$f" && source "$f"; done - - -die() { echo $ME: "$@" >&2; exit 1; } -warn() { echo $ME: "$@" >&2; } -silently() { "$@" &>/dev/null; } + GetOptions( + "h|help" => sub { pod2usage(-verbose => 1, -exit => 0) }, + "m|man" => sub { pod2usage(-verbose => 2, -exit => 0) }, + "dev=s" => \$opt_dev, + "dir=s" => \$opt_dir, + ) or pod2usage(-exit => 1); -debug() { echo "DEBUG: $@" >&2; } + my $cmd = shift; + + -w $opt_dev or die "$ME: write access to $opt_dev: $!\n"; + -w $opt_dir or die "$ME: write access to $opt_dir: $!\n"; -unique_ids() { - local dups=$(cat $BASE/*/conf/id 2>/dev/null | sort -n | uniq -c \ - | egrep -v '^[[:space:]]+1 ' | tr -s ' ' | cut -f2 -d:) - test "$dups" || return 0 - ERR="$dups" && return 1 + _start(@ARGV) if $cmd eq "start"; + _kill(@ARGV) if $cmd eq "kill"; + die "internal error\n"; + } -convert_dirs() { - for dir in $BASE/*; do - test -d $dir/kvm || test -d $dir/conf || continue - local old=$dir/kvm - local new=$dir/conf - test -d $old && mv $old $new - test -f $new/cmdline && mv $new/cmdline $new/options - done -} - -monitor() { - local port=$1; shift - local command="$1"; shift +sub get_config($) { + defined(my $kvm = shift) or pod2usage; + my @config = slurp sprintf($CONFIG, $kvm), { chomp => 1 }; + (shift @config) =~ /^#\s+kvm/ + or die "wrong file format („# kvm” expected)\n"; - # check, if the monitor is available, kill if somebody - # else is using it - local client=$(netstat -tn | egrep ":$port[[:space:]]+EST" | tr -s ' ' | cut -f4 -d' ') - test "${client%:*}" = 127.0.0.1 && fuser -k ${client#*:}/tcp + if (not /^-pidfile/ ~~ @config) { + push @config, sprintf "-pidfile $PIDFILE", $kvm; + } - local pid0 pid1 - ( - local a=$tmp/a.fifo; mkfifo $a - trap "rm -v $a" EXIT - exec 10<>$a - $NC localhost $port <&10 | { - read || exit # banner - while read -d' '; do test "$REPLY" = "(qemu)" && break; done - echo "$command" >&10 - while read -d' '; do test "$REPLY" = "(qemu)" && break; done - } - ) & pid0=$! - ( sleep $TIMEOUT; kill $pid0 ) & pid1=$! - wait $pid0 - silently kill $pid0 $pid1 + if (not /^-name/ ~~ @config) { + push @config, "-name $kvm"; + }; + + return @config; } -### MAIN +sub _kill(@) { + my $kvm = shift; + my @config = get_config($kvm); -# sanity -test -x "$NC" || die "need nc (netcat)" -test -x "$KVM" || die "need kvm/qemu/kqemu" -test -x "$FUSER" || die "need fuser" -test -d "$BASE" || die "base dir $BASE does not exist" -test -d "$BASE/$vm" || die "vm $vm does not exist" + (/^-pidfile\s+(?.*)/ ~~ @config); + my $pid = slurp $+{pidfile}, { chomp => 1 }; -unique_ids || die "duplicate ids (id: $ERR)" -convert_dirs - -trap 'test "$TMP" && rm -fr $TMP' EXIT + kill $KILL => $pid; + waitpid($pid, 0); + unlink $+{pidfile}; -for vm in ${vm:-$(echo $BASE/*)}; do - vm=$(basename $vm) - conf=$BASE/$vm/conf - var=$BASE/$vm/var - - tmp=$(mktemp -d) - TMP="$TMP $tmp" + exit; +} - test -d $conf || continue - test -f $conf/id || { warn "need id file (vm: $vm)" && continue; } - test -f $conf/options || { warn "need options file (vm: $vm)" && continue; } - test -d $var || mkdir $var || exit 2 +sub _start(@) { + my $kvm = shift; + my @config = get_config($kvm); - id=$(cat $conf/id) - port=$((4000 + id)) - pid=$(cat $var/pid 2>/dev/null) - - case "$action" in + defined(my $pid = fork) or die "Can't fork: $!\n"; - start) - echo "starting $vm" - options="$(grep -v '^#' $conf/options) \ - -pidfile var/pid \ - -name "$vm" \ - -vnc :$id \ - -monitor tcp:localhost:$port,server,nowait \ - $@" - options=$(echo "" $options) # shorten it - echo "$options" > $var/running_options - test -f $var/frozen && options="$options $(cat $var/frozen)" + if ($pid == 0) { + exec $KVM "kvm-$KVM" => map { split } @config; + die "Can't exec: $!"; + } + + exit; +} + +__END__ + +=head1 NAME + +kvmtool - tool to start and stop kvm w/o any X11 - if silently fuser $var/pid; then - echo "running, pid: $pid, display :$id, monitor tcp:127.0.0.1:$port" - else - ( cd $BASE/$vm && $KVM $options -daemonize ) - pid=$(cat $var/pid 2>/dev/null) - test "$pid" || { warn "serious problem ($vm), no pid" && continue; } - echo "started, pid: $pid, display :$id, monitor tcp:127.0.0.1:$port" - fi +=head1 SYNOPSIS + + kvmtool start + +=head1 DESCRITPTION + +=head1 OPTIONS - test "$DISPLAY" && test "$VNCVIEWER" && test -t \ - && ( silently exec $VNCVIEWER :$id =I + +Pathname of the KVM device. (default: /dev/kvm) + +=item B<--dir>=I + +Directory for meta share information about KVMs. - ;; - stop) - { - echo "stopping (powerdown) $vm" - monitor $port "system_powerdown" - silently fuser $var/pid && $0 quit $vm - } & - ;; - quit) - { - echo "quit $vm" - monitor $port "quit" - silently fuser $var/pid && $0 kill $vm - } & - ;; - kill) - echo "killing $vm" - kill $pid - ;; - freeze) - { - echo "freezing $vm" - monitor $port "savevm HIBERNATED" - monitor $port "quit" - echo "-loadvm HIBERNATED" >$var/frozen - } & - ;; - qemu) - echo "DO *NOT* USE \"quit\", use ^C for exiting!" - $NC localhost $port - ;; +=item B<-h>|B<--help> + +Show a short help page. + +=item B<-m>|B<--man> + +Show this manual page. - list) - echo "$vm:" $(silently fuser $var/pid \ - && echo "running (pid: $pid)" \ - || echo "not running") - ;; +=back + +=head1 AUTHOR - *) die "unknown command \"$action\"" - ;; - - esac + Heiko Schlittermann -done +=cut -wait - -# vim:ts=4 sw=4 et: +# vim:sts=4 aw ai sm: diff -r ed2b863f938f -r ebe549994431 kvmtool.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kvmtool.sh Sat Aug 08 01:20:27 2009 +0200 @@ -0,0 +1,201 @@ +#! /bin/bash +# $Id$ +# $URL$ +# status: in development +# 2008 Heiko Schlittermann + +# Should be written in Perl! + +# Structure of virtual machine directories +# $BASE (see below, hard coded into this script) +# | +# +-- +# | +--conf +# | | +--options +# | | +--id +# | +--var +# | | +--pid +# | | +--running_options +# | +-- +# | +-- .... +# | +-- +# | +# +-- +# | .... +# +-- + + +action="${1?}"; shift # mandatory +vm="$1" ; shift # optional + +# no user service able parts below + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +unset ${!LC_*} LANG CDPATH + +ME=$(basename $0) +BASE=/kvm +KVM=$(command -v kvm) +NC=$(command -v nc) +FUSER=$(command -v fuser) +VNCVIEWER=$(command -v vncviewer) +TIMEOUT=30 + + +for f in "/etc/$ME.conf" ~/.$ME; do test -r "$f" && source "$f"; done + + +die() { echo $ME: "$@" >&2; exit 1; } +warn() { echo $ME: "$@" >&2; } +silently() { "$@" &>/dev/null; } + +debug() { echo "DEBUG: $@" >&2; } + +unique_ids() { + local dups=$(cat $BASE/*/conf/id 2>/dev/null | sort -n | uniq -c \ + | egrep -v '^[[:space:]]+1 ' | tr -s ' ' | cut -f2 -d:) + test "$dups" || return 0 + ERR="$dups" && return 1 +} + +convert_dirs() { + for dir in $BASE/*; do + test -d $dir/kvm || test -d $dir/conf || continue + local old=$dir/kvm + local new=$dir/conf + test -d $old && mv $old $new + test -f $new/cmdline && mv $new/cmdline $new/options + done +} + +monitor() { + local port=$1; shift + local command="$1"; shift + + # check, if the monitor is available, kill if somebody + # else is using it + local client=$(netstat -tn | egrep ":$port[[:space:]]+EST" | tr -s ' ' | cut -f4 -d' ') + test "${client%:*}" = 127.0.0.1 && fuser -k ${client#*:}/tcp + + local pid0 pid1 + ( + local a=$tmp/a.fifo; mkfifo $a + trap "rm -v $a" EXIT + exec 10<>$a + $NC localhost $port <&10 | { + read || exit # banner + while read -d' '; do test "$REPLY" = "(qemu)" && break; done + echo "$command" >&10 + while read -d' '; do test "$REPLY" = "(qemu)" && break; done + } + ) & pid0=$! + ( sleep $TIMEOUT; kill $pid0 ) & pid1=$! + wait $pid0 + silently kill $pid0 $pid1 +} + +### MAIN + +# sanity +test -x "$NC" || die "need nc (netcat)" +test -x "$KVM" || die "need kvm/qemu/kqemu" +test -x "$FUSER" || die "need fuser" +test -d "$BASE" || die "base dir $BASE does not exist" +test -d "$BASE/$vm" || die "vm $vm does not exist" + +unique_ids || die "duplicate ids (id: $ERR)" +convert_dirs + +trap 'test "$TMP" && rm -fr $TMP' EXIT + +for vm in ${vm:-$(echo $BASE/*)}; do + vm=$(basename $vm) + conf=$BASE/$vm/conf + var=$BASE/$vm/var + + tmp=$(mktemp -d) + TMP="$TMP $tmp" + + test -d $conf || continue + test -f $conf/id || { warn "need id file (vm: $vm)" && continue; } + test -f $conf/options || { warn "need options file (vm: $vm)" && continue; } + test -d $var || mkdir $var || exit 2 + + id=$(cat $conf/id) + port=$((4000 + id)) + pid=$(cat $var/pid 2>/dev/null) + + case "$action" in + + start) + echo "starting $vm" + options="$(grep -v '^#' $conf/options) \ + -pidfile var/pid \ + -name "$vm" \ + -vnc :$id \ + -monitor tcp:localhost:$port,server,nowait \ + $@" + options=$(echo "" $options) # shorten it + echo "$options" > $var/running_options + test -f $var/frozen && options="$options $(cat $var/frozen)" + + if silently fuser $var/pid; then + echo "running, pid: $pid, display :$id, monitor tcp:127.0.0.1:$port" + else + ( cd $BASE/$vm && $KVM $options -daemonize ) + pid=$(cat $var/pid 2>/dev/null) + test "$pid" || { warn "serious problem ($vm), no pid" && continue; } + echo "started, pid: $pid, display :$id, monitor tcp:127.0.0.1:$port" + fi + + test "$DISPLAY" && test "$VNCVIEWER" && test -t \ + && ( silently exec $VNCVIEWER :$id $var/frozen + } & + ;; + qemu) + echo "DO *NOT* USE \"quit\", use ^C for exiting!" + $NC localhost $port + ;; + + list) + echo "$vm:" $(silently fuser $var/pid \ + && echo "running (pid: $pid)" \ + || echo "not running") + ;; + + *) die "unknown command \"$action\"" + ;; + + esac + +done + +wait + +# vim:ts=4 sw=4 et: