#!/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" } } }