home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 1997 November
/
PCWorld_1997-11_cd.bin
/
software
/
programy
/
komix
/
DATA.Z
/
repository.tcl
< prev
next >
Wrap
Text File
|
1997-06-04
|
48KB
|
1,706 lines
#---------------------------------------------------------------------------
#
# (c) Cayenne Software Inc. 1997
#
# File: @(#)repository.tcl /main/hindenburg/50
# Author: <generated>
# Description:
#---------------------------------------------------------------------------
# SccsId = @(#)repository.tcl /main/hindenburg/50 4 Jun 1997 Copyright 1997 Cayenne Software Inc.
# Start user added include file section
require repdbms.tcl
require options.tcl
# End user added include file section
Class Repository : {GCObject} {
constructor
method destructor
method msg
method message
method warning
method error
method execute
method quickTimeOut
method resetTimeOut
method getAvailableRepositories
method setCurrent
method currentCorporate
method checkCorporate
method currentOwner
method findDbServer
method currentDbServer
method shutdownDbServers
method getActiveClients
method getServerById
method getServerByObject
method getServerByName
method changeServerDefinition
method removeServerDefinition
method getInfoFromDatabase
method getInfoFromCmdLine
method getInfoFromCorporate
method makeOptions
method makeDbOptions
method makeCmdLine
method makeDbCmdLine
method makeDbToolCmd
method runDbScript
method startDbTool
method toolFinished
method checkRepositoryName
method createRepository
method changeRepository
method fixRepositoryDir
method deleteRepository
method deleteRepositoryDir
method deleteRepositoryDb
method deleteServerEntry
method optimizeRepository
method backupRepository
method restoreRepository
method expandArchiveCommand
method archiveRepository
method unarchiveRepository
method getExternalFileVersions
method getLockServer
method getLockServerFromId
method getLocks
method isHangingLock
method describeLock
method checkLock
method setLock
method removeLock
attribute currentName
attribute orbTimeOut
attribute extFiles
attribute extFilesLoaded
attribute extCorp
attribute extProj
attribute extConf
attribute useLockServerId
attribute toolFinishedScript
attribute context
attribute messageHandler
attribute lockServer
attribute badServers
}
constructor Repository {class this} {
set this [GCObject::constructor $class $this]
# Start constructor user section
# Make this usable by both otk and otsh.
#
catch { OtkRegister::repository }
catch { OtkRegister::lockServer }
catch { OtkRegister::reportWriter }
catch { OTShRegister::clientContext }
catch { OTShRegister::repository }
catch { OTShRegister::lockServer }
catch { OTShRegister::reportWriter }
catch { OTShRegister::semanticModel }
if [catch {$this context [ClientContext::global]} msg] {
# Do this again, since on error, it returns "",
# but only the first time.
#
$this context [ClientContext::global]
}
# Determine default corporate from M4_levelpath
#
set path [m4_var get M4_levelpath]
if [regexp {^/([^/]*)} $path dummy corpName] {
$this currentName $corpName
}
# Remember original ORB timeout.
#
$this orbTimeOut [m4_var get M4_orb_timeout]
$this extFiles [List new]
$this extFilesLoaded 0
$this extCorp ""
$this extProj ""
$this extConf ""
$this lockServer [ORB::nil]
$this useLockServerId 0
$this badServers [Dictionary new]
# End constructor user section
return $this
}
method Repository::destructor {this} {
# Start destructor user section
# End destructor user section
}
# Formats a number of seconds as a string with the
# format: HH:MM:SS where HH, MM, SS are hours, minutes
# and seconds respectively. If the number of seconds
# spans one day or more, the string "D day(s), " is
# prepended to the time string, where D is the
# number of days.
#
proc Repository::formatSeconds {seconds} {
set d [expr {$seconds / (24*3600)}]
set h [expr {($seconds % (24*3600)) / 3600}]
set m [expr {($seconds % 3600) / 60}]
set s [expr {$seconds % 60}]
set time [format "%02d:%02.2d:%02d" $h $m $s]
if {$d == 0} {
return $time
}
set s "s"
if {$d == 1} {
set s ""
}
return "$d day$s, $time"
}
# Expands special symbols and variables
# in the given file name, and returns
# the new file name. If 'escape' is true,
# backslashes are escaped as well.
#
proc Repository::expandFileName {file {escape 0}} {
# Substitute ~ and ~user. Cannot use glob directory on $file since
# that returns nothing if the directory/file does not exist.
#
if ![regsub {^~} $file [glob ~] file] {
if [regexp {^~([a-zA-Z0-9][a-zA-Z0-9]*)/} $file dummy user] {
regsub {^~[a-zA-Z0-9][a-zA-Z0-9]*} $file [glob ~${user}] file
}
}
# Substitute environment variables.
#
global env
while {[regexp -indices {\$([a-zA-Z][a-zA-Z0-9]*)} $file dummy list]} {
set var [string range $file [lindex $list 0] [lindex $list 1]]
regsub "\\\$${var}" $file $env($var) file
}
# Escape backslashes.
#
if $escape {
regsub -all {\\} $file {\\\\} file
}
return $file
}
# Does the same as 'expandFileName', but additionally
# stores the part of the directory that really exists in the
# file system in the variable named by 'existing'.
#
proc Repository::expandDirName {dir {existing ""} {escape 0}} {
set dir [Repository::expandFileName $dir $escape]
if {$existing != ""} {
upvar $existing existingPart
set existingPart $dir
while {![file exists $existingPart]} {
set existingPart [file dir $existingPart]
}
}
return $dir
}
method Repository::msg {this type msg {options ""}} {
set handler [$this messageHandler]
if {$handler != ""} {
eval $handler $type [list $msg] $options
}
}
method Repository::message {this msg} {
$this msg MESSAGE $msg
}
method Repository::warning {this warning} {
$this msg WARNING $warning
}
method Repository::error {this error {options ""}} {
$this msg ERROR $error $options
}
# Executes the given command line and returns an empty string
# if it was successful, else it returns an error string.
#
method Repository::execute {this cmd} {
if $win95 {
set outFile [BasicFS::tmpFile]
systemNoCon "cmd.exe /c \"$cmd\" > $outFile"
set result [BasicFS::readFile $outFile]
BasicFS::removeFile $outFile
if {$result == ""} {
# Should have gotten at least the dbserver banner.
return "Failed to execute '$cmd'"
}
} else {
set errFile [BasicFS::tmpFile]
catch {set result [eval "exec $cmd 2>$errFile"]}
set error [BasicFS::readFile $errFile]
BasicFS::removeFile $errFile
if {$error != ""} {
# If successful, no output should be found.
return $error
}
}
# Success.
return ""
}
# Changes the ORB timeout to a short time, for calls
# that should take little time.
#
method Repository::quickTimeOut {this {smallTimeOut 1000}} {
# NOTE: does not consider host-context of variable.
#
if {[m4_var get M4_orb_timeout] != $smallTimeOut} {
m4_var set M4_orb_timeout $smallTimeOut
m4_var saveStatus M4_orb_timeout 0
}
}
# Resets the ORB timeout to its original value.
#
method Repository::resetTimeOut {this} {
m4_var set M4_orb_timeout [$this orbTimeOut]
m4_var saveStatus M4_orb_timeout 0
}
# Returns a list with all available repository names.
#
method Repository::getAvailableRepositories {this} {
set names {}
foreach entry [[ORB::nameServer] serverDefinitions] {
set id [lindex $entry 2]
if {$id > 100 && $id < 1000} {
lappend names [lindex $entry 0]
}
}
return $names
}
# Changes the selected repository to the one with the new name.
# Always succeeds. Use currentCorporate or checkCorporate to find
# out if the repository could be accessed.
#
method Repository::setCurrent {this newName} {
if {[$this currentName] != ""} {
set dbserver [$this currentDbServer]
if ![$dbserver isNil] {
$dbserver disconnect
}
}
$this currentName $newName
[$this context] setLevelPath ""
if {$newName != ""} {
[$this context] setLevelPath /$newName
}
}
# Returns the currently selected corporate object. If none
# is selected or accessible, a nil object is returned.
#
method Repository::currentCorporate {this} {
set name [$this currentName]
set corp [[$this context] currentCorporate]
if {$name == "" || [$corp isNil]} {
return [ORB::nil]
}
return $corp
}
# Returns the currently selected corporate object. If none
# is selected or accessible, an error is returned.
#
method Repository::checkCorporate {this} {
set name [$this currentName]
if {$name == ""} {
return -code error "No current repository set."
}
set corp [[$this context] currentCorporate]
if {$name == "" || [$corp isNil]} {
return -code error "Cannot access repository \"$name\"."
}
return [$this currentCorporate]
}
# Returns the name of the user that owns
# the currently selected repository.
# This is determined by examining the
# owner of the corporate directory.
#
method Repository::currentOwner {this} {
set corp [$this checkCorporate]
set dir [path_name concat [$corp location] [$corp name]]
if [file exists $dir] {
if ![catch {set owner [BasicFS::owner $dir]} error] {
if {$owner == "everyone"} {
set owner ""
}
return $owner
} else {
$this error $error
}
}
return ""
}
# Returns the BrokerImplemServer object
# that represent the dbserver that is servicing
# the currently selected corporate. Returns
# [ORB::nil] if no corporate is selected.
#
method Repository::currentDbServer {this} {
set name [$this currentName]
if {$name == ""} {
return [ORB::nil]
}
set broker [ORB::broker]
if [$broker isNil] {
return [ORB::nil]
}
if ![$this getServerByName $name entry] {
return [ORB::nil]
}
set implem [$broker findImplementation $entry(fullId)]
if [$implem isNil] {
return [ORB::nil]
}
foreach server [$implem servers] {
set me [$server findClient [ORB::currentHost] [ORB::currentProcessId]]
if ![$me isNil] {
return $server
}
}
return [ORB::nil]
}
method Repository::findDbServer {this serverId} {
set broker [ORB::broker]
if [$broker isNil] {
return [ORB::nil]
}
set implem [$broker findImplementation $serverId]
if [$implem isNil] {
return [ORB::nil]
}
foreach server [$implem servers] {
set me [$server findClient [ORB::currentHost] [ORB::currentProcessId]]
if ![$me isNil] {
return $server
}
}
return [ORB::nil]
}
method Repository::shutdownDbServers {this name {shutdownRef ""} {delayedRef ""}} {
if {$delayedRef != ""} {
upvar $delayedRef delayedCount
}
if {$shutdownRef != ""} {
upvar $shutdownRef shutdownCount
}
set ns [ORB::nameServer]
if ![$this getServerByName $name serverDef] {
$this error "Server definition of server '$name' not found."
return 0
}
# Disconnect from current dbserver if shutting current.
#
if {[$this currentName] == $name} {
set dbserver [$this currentDbServer]
if ![$dbserver isNil] {
$dbserver disconnect
}
}
# Find all current dbservers and shut them down.
# If any servers are still running, abort name/dir change.
#
set delayedCount 0
set shutdownCount 0
foreach broker [$ns brokers] {
set implem [$broker findImplementation $serverDef(fullId)]
if [$implem isNil] {
continue
}
# TODO: tell implementation to shutdown, meaning that it won't
# start any new servers, because busy clients will try to
# restart a dbserver.
# $implem shutdown
foreach server [$implem servers] {
if [catch {$server shutdown} error] {
# Server could not be disconnected.
#
incr delayedCount
$this error $error
} else {
incr shutdownCount
}
}
}
if {$delayedCount > 0} {
if {$delayedCount == 1} {
set s "is still 1 server"
} else {
set s "are still $delayedCount servers"
}
$this error "There $s of implementation '$name' running.\nQuit all\
clients and wait for all servers to exit, then retry.\n" -add
return 0
}
if {$shutdownCount == 0} {
$this message "No servers of implementation '$name' were found."
} else {
if {$shutdownCount == 1} {
set servers "The only server"
set have "has"
} else {
set servers "All $shutdownCount servers"
set have "have"
}
$this message "$servers of implementation '$name' $have been shutdown."
}
return 1
}
# Returns the set of active clients. This
# set contains all clients that are currently
# connected to an OT server, be it a
# dbserver or lockserver.
#
method Repository::getActiveClients {this clientMapRef} {
upvar $clientMapRef clientMap
# Build active client map to improve search perforance.
#
set ns [ORB::nameServer]
foreach broker [$ns brokers] {
if [catch {set implems [$broker implementations]} error] {
# Skip this broker, but let user know something is wrong
# with it.
#
lappend brokerErrors $error
continue
}
foreach client [query -s servers.clients $implems] {
set clientKey "[$client host],[$client pid]"
set clientMap($clientKey) $client
}
}
if [info exists brokerErrors] {
$this warning "Could not consider clients of servers of not-responding\
broker(s) due to errors:\n\n[join $brokerErrors "\n\n"]"
}
if ![info exists clientMap] {
return 0
}
return [array size clientMap]
}
method Repository::getServerById {this implemId serverRef} {
upvar $serverRef serverDef
set entry [[ORB::nameServer] findServerDefinition $implemId]
if [lempty $entry] {
return 0
}
set serverDef(name) [lindex $entry 0]
set serverDef(fullId) [lindex $entry 1]
set serverDef(id) [lindex $entry 2]
set serverDef(version) [lindex $entry 3]
set serverDef(policy) [lindex $entry 4]
set serverDef(protocol) [lindex $entry 5]
set serverDef(executable) [lindex $entry 6]
set serverDef(cmdline) [lindex $entry 7]
set serverDef(host) [lindex $entry 8]
return 1
}
method Repository::getServerByObject {this objectId serverRef} {
upvar $serverRef serverDef
set decoded [ORB::decodeObjectId $objectId]
set implemId [ORB::makeImplemId [lindex $decoded 1] [lindex $decoded 2]]
return [$this getServerById $implemId serverDef]
}
method Repository::getServerByName {this implemName serverRef} {
upvar $serverRef serverDef
foreach entry [[ORB::nameServer] serverDefinitions] {
set name [lindex $entry 0]
if {$name == $implemName} {
return [$this getServerById [lindex $entry 1] serverDef]
}
}
return 0
}
method Repository::changeServerDefinition {this id version name policy protocol executable cmdline host} {
set ns [ORB::nameServer]
$ns changeServerDefinition \
$id $version $name $policy $protocol $executable $cmdline $host
# Tell all brokers that server definition has changed, in case they
# have retrieved it already from the name server.
#
set implemId [ORB::makeImplemId $id $version]
foreach broker [$ns brokers] {
if [catch {
$broker changeParameter "implementationChanged" $implemId
} error] {
$this error $error
}
}
return 0
}
method Repository::removeServerDefinition {this implemId} {
set ns [ORB::nameServer]
$ns removeServerDefinition $implemId
# Tell all brokers that server definition has been removed.
#
foreach broker [$ns brokers] {
if [catch {
$broker changeParameter "removeImplementation" $implemId
} error] {
$this error $error
}
}
return 0
}
# Retrieves Corporate object information from the named database.
# Returns a list with four elements: corporate object id, corporate name,
# product release string and corporate directory. Uses the dbserver
# to retrieve the info, therefore only available on Unix and NT.
#
method Repository::getInfoFromDatabase {this cmdInfoRef dbName dbInfoRef} {
upvar $cmdInfoRef cmdInfo
upvar $dbInfoRef dbInfo
set result [$this runDbScript cmdInfo $dbName dbcorpinfo.tcl]
if {[lindex $result 0] != "OK"} {
$this error "Could not retrieve Repository info from database\
'$dbName':\n[lindex $result 1]" -add
return 0
}
set info [lindex $result 1]
set dbInfo(id) [lindex $info 0]
set dbInfo(name) [lindex $info 1]
set dbInfo(productRelease) [lindex $info 2]
set dbInfo(location) [lindex $info 3]
return 1
}
# Retrieves database info from a given
# dbserver command line as found in the object
# servers file. The given variable name is used
# as an associative Tcl variable where its
# members are the following (if the repository
# RDBMS supports them): dbname, dbdir, dbuser,
# dbpassword, dbcryptedpassword, dbhost
# and dbserver.
#
method Repository::getInfoFromCmdLine {this implemId cmdLine cmdInfoRef} {
upvar $cmdInfoRef cmdInfo
set dbServer 0
if {$implemId > 100 && $implemId < 1000} {
set dbServer 1
}
set tool [lindex $cmdLine 0]
set argv [lrange $cmdLine 1 end]
set options(-M4) {m4options noarg {} "M4 options"}
if $dbServer {
if [RepositoryDBMS::hasDirectory] {
set options(-d) {dir arg "" "database directory"}
}
if [RepositoryDBMS::hasUser] {
set options(-u) {user arg "" "database user"}
}
if [RepositoryDBMS::hasPassword] {
set options(-p) {cryptedPassword arg "" "crypted password"}
set options(-P) {plainPassword arg "" "plain password"}
}
if [RepositoryDBMS::hasHost] {
set options(-h) {host arg "" "database host"}
}
if [RepositoryDBMS::hasServer] {
set options(-s) {server arg "" "database server"}
}
if [catch {Options::parse $tool options argv name} error] {
$this error "Error parsing command line of server entry:\n\n$error."
return 0
}
set cmdInfo(m4options) $m4options
set cmdInfo(dbname) $name
set cmdInfo(dbdir) ""
set cmdInfo(dbuser) ""
set cmdInfo(dbpassword) ""
set cmdInfo(dbcryptedpassword) ""
set cmdInfo(dbhost) ""
set cmdInfo(dbserver) ""
if [RepositoryDBMS::hasDirectory] {
set cmdInfo(dbdir) $dir
}
if [RepositoryDBMS::hasUser] {
set cmdInfo(dbuser) $user
}
if [RepositoryDBMS::hasPassword] {
if {$cryptedPassword != ""} {
set cmdInfo(dbcryptedpassword) $cryptedPassword
} elseif {$plainPassword != ""} {
set cmdInfo(dbpassword) $plainPassword
set cmdInfo(dbcryptedpassword) [ORB::cryptPassword \
$plainPassword]
}
}
if [RepositoryDBMS::hasHost] {
set cmdInfo(dbhost) $host
}
if [RepositoryDBMS::hasServer] {
set cmdInfo(dbserver) $server
}
} else {
if [catch {Options::parse $tool options argv} error] {
$this error "Error parsing command line of server entry:\n\n$error."
return 0
}
set cmdInfo(m4options) $m4options
}
return 1
}
method Repository::getInfoFromCorporate {this cmdInfoRef} {
upvar $cmdInfoRef cmdInfo
set corp [$this checkCorporate]
set cmdInfo(dbname) [$corp databaseName]
set cmdInfo(m4options) {}
set cmdInfo(dbdir) ""
set cmdInfo(dbuser) ""
set cmdInfo(dbpassword) ""
set cmdInfo(dbcryptedpassword) ""
set cmdInfo(dbhost) ""
set cmdInfo(dbserver) ""
if [RepositoryDBMS::hasDirectory] {
set cmdInfo(dbdir) [$corp databaseDirectory]
}
if [RepositoryDBMS::hasUser] {
set cmdInfo(dbuser) [$corp databaseUser]
}
if [RepositoryDBMS::hasPassword] {
set cmdInfo(dbcryptedpassword) [$corp databasePassword]
}
if [RepositoryDBMS::hasHost] {
set cmdInfo(dbhost) [$corp databaseHost]
}
if [RepositoryDBMS::hasServer] {
set cmdInfo(dbserver) [$corp databaseServer]
}
return 1
}
method Repository::makeOptions {this cmdInfoRef} {
upvar $cmdInfoRef cmdInfo
set options ""
if [info exists cmdInfo(m4options)] {
foreach m4option $cmdInfo(m4options) {
set list [split $m4option "="]
if {[llength $list] == 1} {
set option +$m4option
} else {
set name [lindex $list 0]
set value [join [lrange $list 1 end] =]
if {$value == 0} {
set option -${name}
} elseif {$value == 1} {
set option +${name}
} else {
set option -${name}=${value}
}
}
append options " [quoteIf $option]"
}
}
return $options
}
method Repository::makeDbOptions {this cmdInfoRef {plainPassword ""}} {
upvar $cmdInfoRef cmdInfo
set options ""
if [RepositoryDBMS::hasUser] {
if [string length $cmdInfo(dbuser)] {
append options " -u [quoteIf $cmdInfo(dbuser)]"
}
}
if [RepositoryDBMS::hasPassword] {
if [string length $plainPassword] {
append options " -P [quoteIf $plainPassword]"
} elseif [string length $cmdInfo(dbcryptedpassword)] {
append options " -p [quoteIf $cmdInfo(dbcryptedpassword)]"
} elseif [string length $cmdInfo(dbpassword)] {
append options " -P [quoteIf $cmdInfo(dbpassword)]"
}
}
if [RepositoryDBMS::hasDirectory] {
if [string length $cmdInfo(dbdir)] {
append options " -d [quoteIf $cmdInfo(dbdir)]"
}
}
if [RepositoryDBMS::hasServer] {
if [string length $cmdInfo(dbserver)] {
append options " -s [quoteIf $cmdInfo(dbserver)]"
}
}
if [RepositoryDBMS::hasHost] {
if [string length $cmdInfo(dbhost)] {
append options " -h [quoteIf $cmdInfo(dbhost)]"
}
}
set m4options [$this makeOptions cmdInfo]
if ![lempty $m4options] {
append options " $m4options"
}
return $options
}
method Repository::makeCmdLine {this tool cmdInfoRef} {
upvar $cmdInfoRef cmdInfo
if [info exists cmdInfo(dbname)] {
return "$tool[$this makeDbOptions cmdInfo] $cmdInfo(dbname)"
} else {
return "$tool[$this makeOptions cmdInfo]"
}
}
# Creates the command line for a database tool,
# based on the contents of the assiociative Tcl
# variable given. The same members as returned
# by getInfoFromCmdLine must be specified.
#
method Repository::makeDbCmdLine {this tool cmdInfoRef} {
upvar $cmdInfoRef cmdInfo
return "$tool[$this makeDbOptions cmdInfo] $cmdInfo(dbname)"
}
method Repository::makeDbToolCmd {this tool cmdInfoRef usePlainPassword argv} {
upvar $cmdInfoRef cmdInfo
if {[catch {set toolPath [m4_path_name bin $tool$EXE_EXT]}] ||
![file exists $toolPath]} {
$this warning "No '$tool' available."
return {}
}
# Always require an uncrypted password to be specified (or none).
#
if [RepositoryDBMS::hasPassword] {
if {$usePlainPassword} {
set cmdInfo(dbcryptedpassword) ""
} else {
if [string length $cmdInfo(dbpassword)] {
set crypted [ORB::cryptPassword $cmdInfo(dbpassword)]
set cmdInfo(dbcryptedpassword) $crypted
set cmdInfo(dbpassword) ""
}
}
}
set dbOptions [$this makeDbOptions cmdInfo]
set cmd "[quoteIf $toolPath] $dbOptions $argv"
# Only need an xtool if the plain password was not specified and the
# used DBMS needs one, since then the db tool will ask for it.
#
set type "mtool"
if [RepositoryDBMS::hasPassword] {
if {$cmdInfo(dbpassword) == "" && $cmdInfo(dbcryptedpassword) == ""} {
set type "xtool"
}
}
return [list $type $cmd]
}
method Repository::runDbScript {this cmdInfoRef database script {argv {}}} {
upvar $cmdInfoRef cmdInfo
# Need uncrypted password if executing a script via -f.
#
if {[RepositoryDBMS::hasPassword] && $cmdInfo(dbpassword) == ""} {
return [list ERROR "No password specified."]
}
set cmdInfo(dbcryptedpassword) ""
set script [quoteIf [m4_path_name tcl $script]]
if {$database == "-"} {
# Prepend -- to signify end-of-options.
set database "-- -"
}
set tmpFile [BasicFS::tmpFile]
set cmd [lindex [$this makeDbToolCmd dbserver cmdInfo 1 \
[concat -f $script $database $tmpFile $argv]] 1]
if {$cmd == ""} {
BasicFS::removeFile $tmpFile
return [list ERROR "Error in command line."]
}
set error [$this execute $cmd]
if {$error != ""} {
BasicFS::removeFile $tmpFile
return [list ERROR $error]
}
# Parse tmpFile contents: first line is OK or ERROR, further lines
# contain result or error message(s).
#
set f [BasicFS::readFile $tmpFile]
BasicFS::removeFile $tmpFile
set lines [split $f "\n"]
if {[lindex $lines 0] == "ERROR"} {
return [list "ERROR" [join [lrange $lines 1 end] "\n"]]
}
return [list "OK" [lindex $lines 1]]
}
method Repository::startDbTool {this tool endScript msg cmdInfoRef argv} {
upvar $cmdInfoRef cmdInfo
set cmdList [$this makeDbToolCmd $tool cmdInfo 0 $argv]
set type [lindex $cmdList 0]
set cmd [lindex $cmdList 1]
$this toolFinishedScript $endScript
$wmttoolObj startCommand $type $cmd "$this toolFinished" $msg {0 0} 1
}
method Repository::toolFinished {this} {
set exitCode 0
foreach exitCode [$wmttoolObj exitStatusList] {
# Get last status.
}
if {$exitCode == ""} {
set exitCode 0
}
set endScript [$this toolFinishedScript]
if {$endScript != ""} {
if [catch {eval "$endScript $exitCode"} error] {
$this error $error
}
}
}
method Repository::checkRepositoryName {this name} {
# Detect some invalid characters.
#
if [regexp {[~#$*()\{\}/\\|'`"?;]} $name invalid dummy] {
$this error "Repository name '$name' contains invalid\
character '$invalid'." -add
return 0
}
if {[string length $name] >= 80} {
$this error "Repository name '$name' is too long.\nAt most 80\
characters are allowed." -add
return 0
}
return 1
}
method Repository::createRepository {this endScript cmdInfoRef name dir} {
upvar $cmdInfoRef cmdInfo
$this startDbTool "dbserver" $endScript \
"Creating new repository '$name'..." cmdInfo \
[concat [list -c $name $cmdInfo(dbname)] [quoteIf $dir]]
}
method Repository::changeRepository {this cmdInfoRef name dir newName newDir moveDir} {
upvar $cmdInfoRef cmdInfo
set ns [ORB::nameServer]
if ![$this getServerByName $name serverDef] {
$this error "Server definition of server '$name' not found."
return 0
}
# Strip all spaces: leading, trailing, interior.
#
set newName [rmWhiteSpace $newName]
if ![$this checkRepositoryName $newName] {
return 0
}
# Script executed on error to undo previously succeeded actions.
#
set undoScript ""
if ![$this shutdownDbServers $name] {
return 0
}
if {$newName == "" && $newDir == ""} {
$this warning "Nothing to change."
return 0
}
if {$newName != "" || $newDir != ""} {
set argv {}
if {$newName != ""} {
lappend argv c_name=${newName}
# Change server entry.
#
if [catch {$this changeServerDefinition \
$serverDef(id) \
$serverDef(version) \
$newName \
$serverDef(policy) \
$serverDef(protocol) \
$serverDef(executable) \
$serverDef(cmdline) \
$serverDef(host)} error] {
$this error "Failed to change server definition of server\
'$name':\n\n$error"
return 0
}
set undoScript "
if \[catch {$this changeServerDefinition \
$serverDef(id) \
$serverDef(version) \
[list $serverDef(name)] \
[list $serverDef(policy)] \
[list $serverDef(protocol)] \
[list $serverDef(executable)] \
[list $serverDef(cmdline)] \
[list $serverDef(host)]} error] {
$this error $error
}
$undoScript
"
}
if {$newDir != ""} {
lappend argv c_directory=${newDir}
}
set r [$this runDbScript cmdInfo $cmdInfo(dbname) dbcorpch.tcl $argv]
if {[lindex $r 0] != "OK"} {
$this error [lindex $r 1]
eval $undoScript
return 0
}
set undoScript "
set argv \[list c_name=${name} c_directory=${dir}]
set r \[$this runDbScript cmdInfo \
$cmdInfo(dbname) dbcorpch.tcl \$argv]
if {\[lindex \$r 0] != \"OK\"} {
$this error \[lindex \$r 1]
}
$undoScript
"
}
if $moveDir {
if {$newName != ""} {
# Move repository directory within old parent directory,
# or move it into the new parent directory.
# '$dstDir' is assumed to exist.
#
if {$newDir == ""} {
set dstDir $dir
} else {
set dstDir $newDir
}
set orgRepDir [path_name concat $dir $name]
set newRepDir [path_name concat $dstDir $newName]
if [catch {BasicFS::renameDir $orgRepDir $newRepDir} error] {
$this error "Could not move repository directory:\n\n$error"
eval $undoScript
return 0
}
} elseif {$newDir != ""} {
# Move repository directory from old into new parent directory.
#
set orgRepDir [path_name concat $dir $name]
set newRepDir [path_name concat $newDir $name]
if [catch {BasicFS::renameDir $orgRepDir $newRepDir} error] {
$this error "Could not move repository directory:\n\n$error"
eval $undoScript
return 0
}
}
}
return 1
}
method Repository::fixRepositoryDir {this cmdInfoRef dir name nameInDb} {
upvar $cmdInfoRef cmdInfo
# Try to make directory in database correspond with
# directory in file system.
#
set repDir [path_name concat $dir $name]
set repDirInDb [path_name concat $dir $nameInDb]
if {$repDir == $repDirInDb} {
$this warning "Repository directory '$repDir'\
corresponds with repository database."
return 1
}
# Update database to have the correct name and directory.
#
if {$name != $nameInDb} {
set argv c_name=${name}
set r [$this runDbScript cmdInfo $cmdInfo(dbname) dbcorpch.tcl $argv]
if {[lindex $r 0] != "OK"} {
$this error [lindex $r 1]
return 0
}
}
set repDirExists [file exists $repDir]
set repDirInDbExists [file exists $repDirInDb]
if {!$repDirExists && $repDirInDbExists} {
# Move directory to correspond with database.
#
if [catch {BasicFS::renameDir $repDirInDb $repDir} error] {
$this error $error
return 0
}
} elseif {!$repDirExists && !$repDirInDbExists} {
$this error "Neither one of repository directory \
'$repDir' not '$repDirInDb' exists."
return 0
} elseif {$repDirExists && $repDirInDbExists} {
$this error "Both of repository directory \
'$repDir' and '$repDirInDb' exists. Cannot choose."
return 0
}
return 1
}
# Deletes the current repository.
# Database, directory and server entry are all deleted.
#
method Repository::deleteRepository {this} {
$this getInfoFromCorporate cmdInfo
$this deleteRepositoryDb cmdInfo
$this deleteRepositoryDir
$this deleteServerEntry
return 1
}
method Repository::deleteRepositoryDir {this} {
set corp [$this checkCorporate]
set location [path_name concat [$corp location] [$corp name]]
if [catch {BasicFS::removeDirAll $location} error] {
$this error $error
return 0
}
return 1
}
method Repository::deleteRepositoryDb {this cmdInfoRef} {
upvar $cmdInfoRef cmdInfo
set database $cmdInfo(dbname)
set result [$this runDbScript cmdInfo - dbcorpdrop.tcl $database]
if {[lindex $result 0] != "OK"} {
$this error [lindex $result 1]
return 0
}
return 1
}
method Repository::deleteServerEntry {this} {
set name [$this currentName]
if [$this getServerByName $name serverDef] {
set ns [ORB::nameServer]
if [catch {$this removeServerDefinition $serverDef(fullId)} error] {
$this error $error
return 0
}
} else {
$this error "Server entry of repository '$name' not found."
return 0
}
return 1
}
method Repository::optimizeRepository {this endScript cmdInfoRef options name projects} {
if ![$this shutdownDbServers $name] {
return
}
upvar $cmdInfoRef cmdInfo
$this startDbTool "dboptimize" $endScript \
"Optimizing repository '$name' ..." cmdInfo \
[concat $options $cmdInfo(dbname) $projects]
}
method Repository::backupRepository {this endScript cmdInfoRef options name projects archive toFile} {
upvar $cmdInfoRef cmdInfo
set corp [$this currentCorporate]
set repDir [$corp location]
set msg "Backing up repository '$name'"
set cmdList [$this makeDbToolCmd dbdump cmdInfo 0 \
[concat $options $cmdInfo(dbname) $projects]]
if [lempty $cmdList] {
return
}
set cmd [lindex $cmdList 1]
if $archive {
# Cd to repository directory so we can make a relative archive.
#
if [catch {cd $repDir} error] {
$this error "Could not change directory to repository\
directory '$repDir'"
return
}
set archiveCmd [$this expandArchiveCommand \
[m4_var get M4_archive_cmd] $repDir $name $toFile "archive"]
append cmd "; $archiveCmd"
if {$toFile != ""} {
append msg " in '$toFile'"
}
}
$this toolFinishedScript $endScript
$wmttoolObj startCommand [lindex $cmdList 0] $cmd \
"$this toolFinished" "$msg ..." {0 0} 1 [list [quoteIf $repDir]]
}
method Repository::restoreRepository {this endScript cmdInfoRef options name repDir projects unarchive fromFile} {
upvar $cmdInfoRef cmdInfo
set fromDir [path_name concat $repDir $name]
set msg "Restoring repository '$name'"
set cmd ""
if $unarchive {
# First unarchive $fromFile into $repDir.
# Archive must contain $name as top level directory (just like
# the archive created by backupRepository).
#
if [catch {cd $repDir} error] {
$this error "Could not change directory to repository\
directory '$repDir'"
return
}
set unarchiveCmd [$this expandArchiveCommand \
[m4_var get M4_unarchive_cmd] $repDir $name $fromFile "unarchive"]
set cmd "$unarchiveCmd; "
if {$fromFile != ""} {
append msg "from '$fromFile'"
}
}
set cmdList [$this makeDbToolCmd dbdump cmdInfo 0 \
[concat -r $fromDir $options $cmdInfo(dbname) $projects]]
append cmd [lindex $cmdList 1]
$this toolFinishedScript $endScript
$wmttoolObj startCommand [lindex $cmdList 0] $cmd \
"$this toolFinished" "$msg ..." {0 0} 1 [list [quoteIf $repDir]]
}
method Repository::expandArchiveCommand {this cmd repDir repName file type} {
# Name of repository. Not quoted since user may want to use this
# string to build another string.
regsub -all %N $cmd $repName cmd
# Parent of repository directory. Not quoted since user may want to add
# something to this string.
regsub -all %P $cmd $repDir cmd
# Repository directory.
regsub -all %R $cmd [quoteIf [path_name concat $repDir $repName]] cmd
# Full path to dump file.
regsub -all %F $cmd [quoteIf $file] cmd
# Directory part of dump file path.
regsub -all %D $cmd [file dirname $file] cmd
# File name part of dump file path.
regsub -all %T $cmd [file tail $file] cmd
# Type of command: "archive" or "unarchive"
regsub -all %W $cmd $type cmd
return $cmd
}
method Repository::archiveRepository {this endScript dstFile} {
set corp [$this currentCorporate]
set repDir [$corp location]
set cmd [$this expandArchiveCommand [m4_var get M4_archive_cmd] \
$repDir [$corp name] $dstFile "archive"]
$this toolFinishedScript $endScript
$wmttoolObj startCommand "mtool" $cmd "$this toolFinished" \
"Archiving repository in '$dstFile' ..." {0 0} 1 \
[list [quoteIf $repDir]]
}
method Repository::unarchiveRepository {this endScript repDir repName srcFile} {
set cmd [$this expandArchiveCommand [m4_var get M4_unarchive_cmd] \
$repDir $repName $srcFile "unarchive"]
$this toolFinishedScript $endScript
$wmttoolObj startCommand "mtool" $cmd "$this toolFinished" \
"Unarchiving repository from '$srcFile' ..." {0 0} 1 \
[list [quoteIf $repDir]]
}
# Retrieves a list of pairs each containing an ExternalFileVersion and
# a ConfigVersion in which that file exists. Only those external files are
# returned for which a file in the client's file system exists.
# The list of returned files can be restricted by specifying a Project
# or ConfigVersion. By default, the entire current corporate
# is searched for external file versions.
#
# This action may take a while.
#
# Options:
# -clear: clears the cached list of external file versions; always returns
# empty list
# -dirs: only returns objects that represent a directory in the client's
# files system (i.e. Corporate, Project, ConfigVersion,
# PhaseVersion or SystemVersion); the returned list now contains
# pairs with an object (Corporate, Project, ConfigVersion,
# PhaseVersion or SystemVersion) and a ConfigVersion (if object
# is not a Project, ConfigVersion or PhaseVersion)
# -proj: restricts search to the given Project object
# -conf: restricts search to the given ConfigVersion object
#
method Repository::getExternalFileVersions {this args} {
set corp [$this checkCorporate]
set opts(-dirs) { dirs }
set opts(-clear) { clear }
set opts(-proj) " proj arg [ORB::nil] "
set opts(-conf) " conf arg [ORB::nil] "
Options::parse getExternalFileVersions opts args
if ![$conf isNil] {
set proj [$conf project]
}
if {$clear || ($corp != [$this extCorp] ||
$proj != [$this extProj] ||
$conf != [$this extConf])} {
$this extFilesLoaded 0
if $clear {
return {}
}
}
$this extCorp $corp
$this extProj $proj
$this extConf $conf
if ![$this extFilesLoaded] {
[$this extFiles] contents {}
if [$conf isNil] {
if [$proj isNil] {
set configVersions [query $corp.projects.configVersions]
} else {
set configVersions [query $proj.configVersions]
}
} else {
set configVersions [list $conf]
}
foreach configV $configVersions {
foreach f [query "file.isExternal == 1" \
$configV.phaseVersions.systemVersions.localFileVersions] {
[$this extFiles] append [list $f $configV]
}
}
$this extFilesLoaded 1
}
if $dirs {
[$this extFiles] foreach pair {
set f [lindex $pair 0]
set c [lindex $pair 1]
# insert parent objects from corporate to configV
set dirMap([$f corporate]) [ORB::nil]
set dirMap([$f project]) [ORB::nil]
set dirMap($c) [ORB::nil]
# insert phaseV and systemV objects
set p [$c findPhaseVersion -byPhase [$f phase]]
set s [$p findSystemVersion -bySystem [$f system]]
set dirMap($p) $c
set dirMap($s) $c
}
set extDirs {}
if [info exists dirMap] {
foreach dir [lsort [flatten [array names dirMap]]] {
lappend extDirs [list $dir $dirMap($dir)]
}
}
return $extDirs
}
return [[$this extFiles] contents]
}
# Retrieves the lockserver object (LockAdmin).
# If the lockserver is not running, [ORB::nil] is
# returned unless startIfNotRunning is true, in which
# case the lockserver is started.
#
method Repository::getLockServer {this {startIfNotRunning 1}} {
if [$this useLockServerId] {
set ls [$this getLockServerFromId]
} elseif {[catch {set ls [ORB::lockServer]; $ls isNil} error]} {
$this useLockServerId 1
set ls [$this getLockServerFromId]
}
if {[$ls isNil] && $startIfNotRunning} {
# Lockserver is not running, cause it to startup.
#
$this message "Starting lockserver..."
set ls [$this getLockServerFromId]
if [catch {$ls pid} startupError] {
$this warning "Could not start lockserver:\n\n$startupError."
} else {
$this message "Lockserver started."
}
}
return $ls
}
# Retrieves the lockserver object by using its
# object id directly.
#
method Repository::getLockServerFromId {this} {
if [[$this lockServer] isNil] {
set lockServerId "LockAdmin:UVyGGcC7HYAUAAAAAAGQAAQAAAAAA"
$this lockServer [LockAdmin new $lockServerId]
}
return [$this lockServer]
}
# Returns all locks described by the description. If onlyHanging is 1, only
# the hanging locks in the set are returned.
#
method Repository::getLocks {this desc {onlyHangingLocks 0}} {
set ls [$this getLockServer]
if [$ls isNil] {
$this warning "Lockserver is not running."
return NO_LOCKSERVER
}
# For now, do pattern matching on reason here, instead of in lockserver.
#
set matchReason 0
if {[lsearch [$desc what] "Reason"] != -1} {
set reason [$desc reason]
if {[string first "*" $reason] != -1 ||
[string first "?" $reason] != -1} {
set matchReason 1
$desc setReason "*"
}
}
if [catch {set locks [$ls findLocks $desc]} error] {
$this error $error
return NO_LOCKSERVER
}
if $matchReason {
set matched {}
foreach lock $locks {
if [string match $reason [$lock reason]] {
lappend matched $lock
}
}
set locks $matched
}
if $onlyHangingLocks {
set hanging {}
$this getActiveClients clients
foreach lock $locks {
set clientKey "[$lock host],[$lock pid]"
if ![info exists clients($clientKey)] {
lappend hanging $lock
}
}
return $hanging
}
return $locks
}
# Returns 1 if the given lock is hanging, else 0.
#
method Repository::isHangingLock {this lock} {
if {[$this getActiveClients clients] == 0} {
return 1
}
set clientKey "[$lock host],[$lock pid]"
if [info exists clients($clientKey)] {
# A client exists with host and pid of the lock,
# so the lock is not hanging.
#
return 0
}
return 1
}
method Repository::describeLock {this lock} {
set objectId [$lock objectId]
set lockType [$lock type]
set text $objectId
set list [ORB::decodeObjectId $objectId]
set serverId [lindex $list 1].[lindex $list 2]
set isBadServer 0
if {[[$this badServers] set $serverId] == "1"} {
set isBadServer 1
}
if {!$isBadServer && [catch {
if {$lockType == "readLock" || $lockType == "writeLock"} {
# NOTE: This takes a long time if lots of locks are present...
#
regexp {^([^:]*):} $objectId dummy className
set obj [$className new $objectId]
set isVersion [$obj isA Version]
set isVersionable [$obj isA Versionable]
if {$isVersion || $isVersionable} {
if $isVersion {
set versable [$obj object]
set suffix "([$obj versionName])"
} else {
set versable $obj
set suffix ""
}
if {[$versable isA Phase] ||
[$versable isA System] ||
[$versable isA File]} {
set text "[$versable name].[$versable type]"
} else {
set text "[$versable name]"
}
if {$suffix != ""} {
append text " $suffix"
}
}
}
}]} {
# Remember that an error occurred while starting server,
# so the next time we do not try to start a server again.
#
[$this badServers] set $serverId 1
set text $objectId
}
set dbserver [$this findDbServer \
[ORB::makeImplemId [lindex $list 1] [lindex $list 2]]]
if ![$dbserver isNil] {
$dbserver disconnect
}
return $text
}
method Repository::checkLock {this lock} {
if [catch {
set lockType [$lock type]
set lockObject [$lock objectId]
}] {
$this error "Lock has been removed already."
return 0
}
if {$lockType == "noLock"} {
$this error \
"Lock on '[$this describeLock $lock]' has been removed already."
return 0
}
return 1
}
method Repository::setLock {this desc} {
set ls [$this getLockServer]
if [$ls isNil] {
$this error "Lockserver is not running."
return 0
}
$ls setLock $desc
return 1
}
method Repository::removeLock {this lock} {
set ls [$this getLockServer 0]
if [$ls isNil] {
$this warning "Lockserver is not running."
return 0
}
if ![$this checkLock $lock] {
return 0
}
if ![$this isHangingLock $lock] {
$this error "Lock on '[$this describeLock $lock]' is not hanging.\
Owning process [$lock pid] of '[$lock user]'\
is still running on host '[$lock host]'."
return 0
}
set rm [LockDescription new]
$rm setObject [$lock objectId]
$rm addType [LockFilter::lock2descType [$lock type]]
$rm setPid [$lock pid]
$rm setHost [$lock host]
$rm setUser [$lock user]
return [$ls removeLocks $rm]
}
# Do not delete this line -- regeneration end marker