#!/usr/local/bin/expect
# $Id$
# To-do:
# - check bandwidth usage
# - status to show latency
# - support VNC listen mode
# - support other command-line arguments to vncsnapshot & sftp
# - let things change dynamically by a console on stdin
# - be able to disconnect, and control it from another application
# - support uploading to several different sites at a time
# - make subdirectories for hash-named files
# - (eventually) perhaps do md5, vncsnapshot, convert -crop in-process
set micro_image_width 64
set micro_image_height 64
set cropsize "${micro_image_width}x${micro_image_height}"
set pause_between_frames 2
# in seconds
set required_params [list server destination baseurl]
set params(tempdir) /tmp/vncpublish.[pid]
proc die {message} { puts stderr $message ; exit 1 }
# Parse command-line arguments
while {[llength $argv] > 0} {
set arg1 [lindex $argv 0]
set arg2 [lindex $argv 1]
set argv [lrange $argv 2 end]
switch $arg1 {
server { set params(server) $arg2 }
destination { set params(destination) $arg2 }
baseurl { set params(baseurl) $arg2 }
tempdir { set params(tempdir) $arg2 }
default { die "Unknown argument: $arg1" }
}
}
# Check that we have all necessary parameters
foreach p {server destination baseurl} {
if {[info exists params($p)]} {
} else {
puts stderr "$p must be specified"; exit 1
}
}
# Check that the destination is OK
if {[regexp {^sftp://([^/]*)/(.*)$} $params(destination) patternmatch destserver destdir]} {
# sftp connection, no problems
} else {
die "Unknown destination format. Currently supported formats are: sftp"
}
######################################################################
# Ready, steady, go!
log_user 0
file mkdir $params(tempdir)
spawn vncsnapshot -count 10000000 \
-fps $pause_between_frames \
$params(server) $params(tempdir)/snap
set vncsnap $spawn_id
spawn sftp $destserver
set sftp $spawn_id
set destserver_cd no
set imagemagicks {}
set md5ings {}
######################################################################
set file_upload_list {}
# Files which we want to send, but have not been sent yet
set ready_to_send no
# Whether the sftp session is back at the sftp> prompt
set completely_finished no
# Whether the VNC snapshot has shutdown
######################################################################
proc upload_next_file {} {
global file_upload_list
global ready_to_send
global completely_finished
global destserver_cd
global destdir
global sftp
if {[string is false $ready_to_send]} {
return
}
if {[string is false $destserver_cd]} {
send "cd /$destdir\n"
# Assume that it works, we'll find out soon enough
set ready_to_send false
set destserver_cd yes
return
}
#puts "Working through file_upload_list of $file_upload_list"
if {[llength $file_upload_list] > 0} {
set first_file [lindex $file_upload_list 0]
set file_upload_list [lrange $file_upload_list 1 end]
send -i $sftp "put $first_file\n"
set ready_to_send false
return
}
if {[string is true $completely_finished]} {
send "exit\n"
set ready_to_send false
return
}
}
proc get_password {who what id} {
# Should I be putting this into the loop below? Or does that become too
# complicated?
stty -echo
send_user "$what password for $who: "
expect_user -re "(.*)\n"
send_user "\n"
send -i $id "$expect_out(1,string)\n"
stty echo
}
proc create_html {shotnum} {
global frame
global params
global micro_image_width
global micro_image_height
global hashes
global file_upload_list
global pause_between_frames
array set myframe $frame($shotnum)
set htmlpath $params(tempdir)/page$shotnum.html
set htmlfile [open $htmlpath w]
puts $htmlfile "
Page $shotnum"
puts $htmlfile ""
puts $htmlfile ""
set row_width [expr $myframe(screen_width) / $micro_image_width]
if {[expr $myframe(screen_width) % $micro_image_width] != 0} { incr row_width }
set num_rows [expr $myframe(screen_height) / $micro_image_height]
if {[expr $myframe(screen_height) % $micro_image_height] != 0} { incr row_height }
set counter 0
for {set y 0} {$y < $num_rows} {incr y} {
puts $htmlfile ""
for {set x 0} {$x < $row_width} {incr x} {
set tailname $hashes($shotnum,$counter)
set img_source "$params(baseurl)$tailname"
puts $htmlfile " | "
unset hashes($shotnum,$counter)
incr counter
}
puts $htmlfile "
"
}
puts $htmlfile "
"
close $htmlfile
lappend file_upload_list $htmlpath
upload_next_file
# Don't have to worry about following race condition; it's
# OK if we end up uploading a different index.html to the
# one we generate now.
file copy -force $htmlpath $params(tempdir)/index.html
lappend file_upload_list $params(tempdir)/index.html
}
######################################################################
# The main loop...
while {1} {
expect {
-i $vncsnap assword: { get_password $params(server) VNC $vncsnap }
-i $sftp assword { get_password $destserver SFTP $sftp }
-i $sftp "Connection refused" { die "Could not connect to $destserver via sftp" }
-i $sftp "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED" { die "Unable to connect to $destserver because the .ssh/known_hosts file does not match received ID"
}
-i $sftp "The authenticity of host" { send -i $sftp "yes\n" }
-i $vncsnap "VNC authentication succeeded" { }
-i $sftp "sftp>" { set ready_to_send true ; upload_next_file }
-i $vncsnap -regexp {Image saved from .* (\d*)x(\d*) screen to (.*?)(\d*).jpg using (\d*)x(\d*)\+(\d*)\+(\d*) rectangle} {
set screen_width $expect_out(1,string)
set screen_height $expect_out(2,string)
set basename $expect_out(3,string)
set shotnum $expect_out(4,string)
set filename "$basename$shotnum.jpg"
set rect_width $expect_out(5,string)
set rect_height $expect_out(6,string)
set rect_x_offset $expect_out(7,string)
set rect_y_offset $expect_out(8,string)
if {[llength $imagemagicks] < 3} {
spawn convert -crop $cropsize $filename "$basename$shotnum-%d.jpg"
lappend imagemagicks $spawn_id
set imagemagics_spawn($spawn_id) $shotnum
set frame($shotnum) [list screen_width $screen_width \
screen_height $screen_height \
basename $basename]
} else {
puts stderr "Skipping frame $shotnum because conversion is behind schedule."
}
}
-i $imagemagicks eof {
set s $expect_out(spawn_id)
set pos [lsearch $imagemagicks $s]
set imagemagicks [lreplace $imagemagicks $pos $pos]
set shotnum $imagemagics_spawn($s)
unset imagemagics_spawn($s)
wait -i $s
array set myframe $frame($shotnum)
spawn sh -c "md5sum $myframe(basename)$shotnum-*.jpg"
lappend md5ings $spawn_id
set md5spawn($spawn_id) $shotnum
}
-i $md5ings -re {([a-z0-9]{32})\s+([^\r]*?)(\d+)-(\d+).([^.\r]*?)\r} {
set s $expect_out(spawn_id)
# I could also get shotnum from md5spawn($s)
set hash $expect_out(1,string)
set source_base $expect_out(2,string)
set shotnum $expect_out(3,string)
set seqnum $expect_out(4,string)
set fileext $expect_out(5,string)
set source "$source_base$shotnum-$seqnum.$fileext"
set tempfile "$params(tempdir)/$hash.$fileext"
set hashes($shotnum,$seqnum) "$hash.$fileext"
if {[info exists previously_uploaded($tempfile)]} {
# don't bother resending.
} else {
file rename -force $source $tempfile
lappend file_upload_list $tempfile
upload_next_file
set previously_uploaded($tempfile) 1
}
}
-i $md5ings eof {
set s $expect_out(spawn_id)
set pos [lsearch $md5ings $s]
set md5ings [lreplace $md5ings $pos $pos]
set shotnum $md5spawn($s)
create_html $shotnum
unset md5spawn($s)
wait -i $s
}
-i $vncsnap eof {
create_final_html
wait -i $vncsnap
set vncsnap {}
}
timeout { die "Timeout waiting for response" }
}
}