Wednesday, 14 October 2009

tcl quoting/arg seperation and calling vmrun.exe with tcl and vmware installtools for QNX

Some material to take note of in case you get bitten by tcl arg separation/quoting.

TCL Quoting/Arg seperation
I had a big argument with a bit of a tcl script when calling vmrun.exe
using exec. We could also call it using expect spawn but similar arg
seperation and quoting/slashifying issues arise. 

Executive summary:

#this works:
set res [exec ls -al .]; puts stdout $res;

# this doesn't: couldn't execute "ls -al .": no such file or directory
set cmd "ls -al ."; set res [exec $cmd ]; puts stdout $res;

# this doesn't: /usr/bin/ls: invalid option -- 

set cmd "ls"; set args "-al ."; set res [exec $cmd $args]; puts stdout


This explains it nicely:
"exec   doesn't use
a shell." Exec is a Tcl command. Exec expects its arguments as separate
More explaination there and linked in.

So this works (but the story is more complicated if args itself has
arguments with spaces/quotes/funny chars like [] {} \/):
set cmd "ls"; set args "-al ."; set res [eval exec $cmd $args]; puts
stdout $res;
# to store part of command to run and combine it later 

More useful reading on the quoting and passing of args to exec in tcl: 
"Q.B16- How can I get quoted strings to work the way I want?" 
The title there is "exec quotes problem" which is misleading because it
is an args seperation problem not a quoting/slashifying problem.

Calling vmrun from exec with tcl 
Here is the detail on how this arg seperation problem affects the
internals of our vmrun::run

In bash:

VMAUTHFLAGS="-T server -h https://192.168.xx.xx/sdk -u root -p aseekret
-gu root -gp ''"
vmrun $VMAUTHFLAGS list
vmrun $VMAUTHFLAGS listRegisteredVM

In tcl, all arguments hardcoded on the exec line works easily:
$ tclsh84
set res [exec "C:\\Program Files\\VMware\\VMware VIX\\vmrun.exe" -T
server -h https://192.168.xx.xx/sdk -u root -p ahhnon -gu root -gp ''
puts stdout $res

Total running VMs: 3
[datastore1 (1)] integration-datastore/VM 2/QNX_641_.vmx
[datastore1 (1)] integration-datastore/VM 1/QNX_641_.vmx
[datastore1 (1)] integration-datastore/VM 3/QNX_641_.vmx

Or instead of quoting and slashifying slashes and spaces you can use {}
which prevents further interpretation. But also note that tcl exec has
special quoting rules for windows.

set res [exec {C:\Program Files\VMware\VMware VIX\vmrun.exe} -T server
-h https://192.168.xx.xx/sdk -u root -p atishoo -gu root -gp '' "list"
In TclTestManager library Common/vmrun.tcl

We want set up vmrun.exe path and arguments from config one time. 
Provides basic functions:


Example of use in TestVmrun.tcl
source [file dirname [info script]]/packages.tcl 
package require "vmrun"

set sResult [vmrun::run [list listRegisteredVM]]
puts "listRegisteredVM: $sResult"

# note that {} are used to prevent [] expanding datastore as a command
set sVMID {[standard] Test_QNX641_/QNX_641_.vmx}

# note that one arg which is a list is passed to vmrun::run
set sResult [vmrun::run [list start \{$sVMID\} ]]
puts "start VM: $sResult"

set sResult [vmrun::run [list listDirectoryInGuest \{$sVMID\} "."]]
puts "btl list dir in VM: $sResult"

set sResult [vmrun::run [list listProcessesInGuest \"$sVMID\"]]
puts "btl list processes in VM: $sResult" 

set sResult [vmrun::run [list runScriptInGuest \"$sVMID\" ifconfig]]
puts "btl runScript in VM: $sResult" 

In vmrun::init these are done (loaded from config):

set vmrun::vmauthflags "-T server -h https://192.168.xx.xx/sdk -u root
-p blessyou -gu root -gp ''" 

set vmrun::vmrun_path "C:\\Program Files\\VMware\\VMware VIX\\vmrun.exe"


Inside vmrun::run this is done:

proc vmrun::run { lCmd } {
# put all args into one string
   # all args = (vmauthflags + vmrun command and args in lCmd) 
   # if there are quotes/spaces in the items in the list they are preserved

   set aArgs1 $vmrun::vmauthflags
   set aArgs2 [join $lCmd " "]
   set aArgsAll "$aArgs1 $aArgs2"

   # quote the vmrun.exe command with {} to prevent interpretation the first time
   return [eval exec {$vmrun::vmrun_path} $aArgsAll]

Awkward things: 
 *         The need to seperate args into tcl args in exec call
 *         at vmrun::run call our args are be tcl interpreted and inside
the vmrun::run call the args are tcl interpreted again on the exec line
 *         vmrun.exe path has spaces in it
 *         []'s in vmid (tcl evaluates anything inside [] as a command
unless surrounded by {})

Conclusion: Tcl is a very very interpreted language. In all languages
you run into cases where quoting and slashifying becomes a bit tricky
especially when wrapping up strings to send to e.g. Database or
command-line or xml. In interpreted languages you have an extra level of
interpretation to deal with already and more levels are introduced as
you have the power to wrap up commands to multiple levels within your

Vmware installtools and QNX
Vmware installtools are needed for the vmrun commands which interact
with the running VM. These are easily available for linux/windows but
not so easy for QNX.

So most of the code talking to running VMs will probably telnet into
host (using expect) (double telnet in cases where VM is not on wider

"command-line versions for a subset of the VMware Tools linked on his
website for Some additional
guest Oss."

"There was a QNX port but web site seems to be having problems."

# VMware installtools for QNX: 
# vm must be started, but "unknown error" 
# In VM/sdk  Commands - Configure VM - Power - "VMware Tools Scripts"
are checked ... but.
# In                                 - Advanced - Configuration Params is none
# Found it, 
# "VMware Tools is not supported on this guest OS.HelpOKCheck the
virtual machine's configuration to make sure the settings match the
installed operating system. Consult the documentation for more details
about supported guest operating systems."
# vmware tools are available for some versions of qnx ...
# but we don't want an install dependancy ... if we can help it.
#set sResult [vmrun::run [list installtools \{$sVMID\} ]]
#puts "btl installtools VM: $sResult"
# uname -> qnx 6.4.1 ...
#  also
alternate vmware tools
#  and open vm tools (by vmware)
# on dependant commands if we wait long enough we get:
# "Error: The VMware Tools are not running in the virtual machine:
[standard] Test_QNX641_/QNX_641_.vmx"

No comments: