Version 29 of clonerun

Updated 2007-06-27 12:00:10 by hoffi

Starpacks: Why and how to copy your own executable and launching the copied instance instead

The Problem
Executables, like starpacks, are often called from single network shares from many users in parallel. So the chance is good, that at least one user has an instance of that specific program running all the time. Now the administration wants to update that program. But this is not possible (at least) under MS-Windows, because each executable on disk, which has running instances active, is locked (such lock is invisible via net files).
The Solution
Each user has to launch his own private copy of the program. But this must not lead to an distribution overhead: the program is still distributed only once to the network share, then each instance copies itself to a temporary position, runs from there and terminates quickly. The main executable is locked only for short load periods, so the program file on disk remains in a state where it can be overwritten (with retries, if required) for update purposes. Ideally, the per-user-copy of the program is stored in an temporary location which is periodically freed, so the harddisk will not be cluttered (for the above mentioned reasons, the 'user-copy' cannot delete itself while terminating...).
Notes
I've discovered (anyone out there who can acknowledge this?) that a starpack cannot copy its own executable with tcl commands like file copy or a open/read/put/close-sequence (because as a result always the whole directory structure gets unpacked in the destination). Maybe it has something to do with the underlaying VFS-Implementation... one must use an external copy/xcopy etc. instead which treats the .exe like any other disk file. Or the VFS needs to be unmounted before the file copy, as described under the following link:

Here's the latest standardized version of the above snipped:

 ################################################################################
 #
 # Modul    : autoclone.tcl
 # Stand    : 27.06.2007
 # Zweck    : Kopieren des eigenen Programmes an eine temporäre Position und
 #            starte neu von dort
 # Autor    : M.Hoffmann
 # Hinweise :
 #
 # `Autoclone`-Verfahren  zur  Vermeidung  blockierter  .EXEs  (Updates!):   Das
 # Programm kopiert sich  selbst in Temp-Position  und lädt von  dort.  ACHTUNG:
 # Ein Starkit kann sich mittels 'file copy' oder 'open/fcopy/get/put' nur  dann
 # selbst kopieren, wenn das VFS vorübergehend abgehängt, oder ein externes Tool
 # (z.B.  XCOPY) verwendet wird (Warum XCOPY?  Leider geht die Methode,  cmd.exe
 # im Hintergrund zu starten und  einen copy-befehl dorthin zu pipen,  nicht, da
 # Benutzer teilweise kein Recht haben, cmd.exe zu starten).
 #
 # ACHTUNG:  Ist dieses Paket  eingebunden, wird das Verfahren automatisch  über
 # das  Paket  initscript  verwendet,  es  sei  denn, der LETZTE Kommandozeilen-
 # Parameter lautet:  --noautoclone. MOMENTAN NUR FÜR STARPACKS VERWENDBAR!
 #
 # Historie :
 # 13.11.05 v1.0: als Paket aus RECEIVE übernommen (Schalter inkompatibel!)
 # 11.05.07 v1.0: nur kosmetische Änderungen und Wiki-Update
 # 27.06.07 v2.0: Nutze suspendVFS statt XCOPY; Starpacks abfangen
 #
 ################################################################################

 package provide autoclone 2.0

 ################################################################################
 # Siehe http://permalink.gmane.org/gmane.comp.lang.tcl.starkit/2537
 #
 proc suspendVFS {code} {
      set me [info nameofexe]
      lassign [vfs::filesystem info $me] drv dbh
      vfs::filesystem unmount $me
      # unmounted, now run the code
      uplevel $code
      # Remount
      vfs::filesystem mount $me [list vfs::mk4::handler $dbh]
 }

 ################################################################################
 # Prozedur zur Vermeidung globaler Variablen!
 #
 proc autoClone {} {

      if {[string equal -nocase [lindex $::argv end] "--noautoclone"]} {

         # Paket ist zwar eingebunden, aber wegen --noautoclone das
         # Verfahren nicht verwenden; also Schalter entfernen, um das
         # spätere Kommandozeilenparsing nicht zu stören.
         set ::argv [lreplace $::argv end end]
         set ::argc [llength $::argv]

      } else {

         set myself  [file normalize [info nameofexecutable]]
         set mytempP $::env(temp); # Schwachpunkt: Umgebungsvar TEMP muss existieren!
         set mytemp  [file normalize [file join $mytempP [file tail $myself]]]

         # v2.0: Nur Starpacks berücksichtigen
         if {[string first $myself [file normalize [info script]]] != 0} {
            return
         }

         # Abfangen: Aufruf bereits aus TempVerzeichnis
         if {[string compare $myself $mytemp]} {
            # Voraussetzung: Aufruf von XCOPY muss möglich sein!
            # catch {exec -- [auto_execok xcopy] [file nativename $myself] [file nativename $mytempP] /Y}
            # v2.0: internes Kopieren!
            catch {suspendVFS [list file copy -force -- $myself $mytempP]}
            # Falls Kopieren scheiterte, aber Ziel schon da war, ebenfalls fortfahren
            if {[file exists $mytemp]} {
               set ::argv [linsert $::argv 0 $mytemp]
               # Versuch mit START, bislang erfolglos (EXEC ist eigenwillig!):
               # set ::argv [linsert $::argv 0 [auto_execok cmd] /c start \"[file nativename $mytemp]\"]
               lappend ::argv --noautoclone
               # durch Angabe von --noautoclone hier Rekursion verhindern!
               if {[catch {eval exec -- $::argv &} rc]} {
                  # Kopie ist aus irgendwelchen Gründen nicht aufrufbar;
                  # Fehler ignorieren und aktuelle Instanz weiter verwenden!
               } else {
                  exit 0; # sofort BEENDEN, da mit Kopie weitergearbeitet wird!
               }
            }
         }
      }
 }

 # ACHTUNG: Sofortige Ausführung durch PACKAGE REQUIRE!
 autoClone;

It should be mentioned that the code is intended for use by starpacks only, especially for GUI-Starpacks (problems with tclkitSH-version are likely, since the EXECd process eventually inherits some resources belonging to the console, so the EXECing process doesn't end...).


EF I was trying to point out that the locking mechanism that you describe here is the reason why my updater library [L1 ] does not work on Windows. However, the techniques that you are describing could be combined to the library in order to provide for binaries that update themselves when a new version appears at a known Internet location.


Category Tclkit