home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 1998 October
/
PCWorld_1998-10_cd.bin
/
software
/
prehled
/
komix
/
DATA.Z
/
repository.tcl
< prev
next >
Wrap
Text File
|
1997-11-28
|
52KB
|
1,869 lines
#---------------------------------------------------------------------------
#
# (c) Cayenne Software Inc. 1997
#
# File: @(#)repository.tcl /main/titanic/37
# Author: <generated>
# Description:
#---------------------------------------------------------------------------
# SccsId = @(#)repository.tcl /main/titanic/37 28 Nov 1997 Copyright 1997 Cayenne Software Inc.
# Start user added include file section
require repdbms.tcl
require options.tcl
require caynutil.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 currentDbServer
method findDbServer
method currentRepDir
method currentObjDir
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 dumpRepository
method dumpObject
method restoreRepository
method restoreObject
method expandArchiveCommand
method archiveRepositoryDirectory
method archiveObjectDirectory
method unarchiveRepositoryDirectory
method unarchiveObjectDirectory
method getExternalFileVersions
method getLockServer
method getLocks
method isHangingLock
method describeLock
method setLock
method removeLock
method upgradeLocks
attribute currentName
attribute lastRepDir
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]
}
RepositoryDBMS::setCurrent [ORB::nil]
# 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 [regexp {^~[\\/](.*)} $file dummy path] {
set file [location [otglob -nocomplain ~] $path]
} elseif [regexp {^~(.*)} $file dummy path] {
set expandedDir [path_name directory [otglob -nocomplain ~]]
set file [location $expandedDir $path]
}
# 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
}
if $win95 {
regsub -all {/} $file {\\} 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]
}
}
if $win95 {
regsub -all {/} $dir {\\} dir
}
return $dir
}
proc Repository::defaultDumpFile {dumpDir} {
set repDir [file dir $dumpDir]
if $win95 {
regsub -all {/} $repDir {\\} repDir
}
set name [file tail $dumpDir]
return [path_name concat $repDir ${name}_backup]
}
proc Repository::showDumpInfo {dir} {
$wmttoolObj startCommand mtool "dbdump -l [quoteIf $dir]" "" \
"Getting info from dump directory '$dir' ..." {0 0} 1
return 1
}
proc Repository::orbOptions {} {
return {
M4_brokerport
M4_dblockmode
M4_dbms
M4_dbtracesql
M4_heartbeat_interval
M4_imphost
M4_max_missed_heartbeats
M4_nameserverhost
M4_nameserverport
M4_orb_linger
M4_orb_maxclients
M4_orb_maxinstances
M4_orb_report
M4_orb_timeout
M4_parent_pid
M4_parent_threshold
M4_probe_control
M4_probe_maxdelay
M4_probe_timeout
M4_protocol
M4_services
}
}
proc Repository::serverOptions {} {
return {
M4_dblockmode
M4_dbtracesql
M4_heartbeat_interval
M4_lockfile_update
M4_max_missed_heartbeats
M4_orb_linger
M4_orb_maxclients
M4_orb_maxinstances
M4_orb_report
M4_orb_timeout
M4_probe_maxdelay
}
}
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} {
set errFile [BasicFS::tmpFile]
regsub -all {\\} "$cmd 2>$errFile" {\\\\} cmd
if [catch {eval "exec $cmd"} execError] {
set error [BasicFS::readFile $errFile]
BasicFS::removeFile $errFile
# Check if error occurred while starting command, or because
# the command exited with status != 0.
#
if [string match "*child process exited abnormally*" $execError] {
# Child exited with status != 0.
# The reason for this has been read from stderr.
#
return $error
}
# Other exec error.
#
return "$execError:\n$error"
}
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.
# 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] {
catch {$dbserver disconnect}
}
}
$this currentName $newName
[$this context] setLevelPath ""
if {$newName != ""} {
[$this context] setLevelPath /$newName
$this lastRepDir [$this currentRepDir]
}
RepositoryDBMS::setCurrent [$this currentCorporate]
return 1
}
# 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 [location [$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::currentRepDir {this} {
set corp [$this checkCorporate]
set repDir [location [$corp location] [$corp name]]
if $win95 {
regsub -all {/} $repDir {\\} repDir
}
return $repDir
}
method Repository::currentObjDir {this obj} {
set corp [$this checkCorporate]
set objDir [location [$corp location] [$corp name] \
[$obj repositoryDirectory]]
if $win95 {
regsub -all {/} $objDir {\\} objDir
}
return $objDir
}
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] {
catch {$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
return 0
}
method Repository::removeServerDefinition {this implemId} {
set ns [ORB::nameServer]
$ns removeServerDefinition $implemId
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 error "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]
if [lempty $cmdList] {
return 0
}
$this toolFinishedScript $endScript
$wmttoolObj startCommand $type $cmd "$this toolFinished" $msg {0 0} 1
return 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
}
}
}
proc Repository::goodRepositoryName {name {errorRef ""}} {
if {$errorRef != ""} {
upvar $errorRef error
}
# Detect invalid characters.
#
if ![regexp {^[-_a-zA-Z0-9][-_a-zA-Z0-9]*$} $name] {
set error "Repository name '$name' contains invalid character."
return 0
}
# Detect name length overflow.
#
if {[string length $name] >= 80} {
set error "Repository name '$name' is too long.\nAt most 80\
characters are allowed."
return 0
}
return 1
}
method Repository::checkRepositoryName {this name} {
if ![Repository::goodRepositoryName $name error] {
$this error $error -add
return 0
}
return 1
}
method Repository::createRepository {this endScript cmdInfoRef name dir} {
upvar $cmdInfoRef cmdInfo
return [$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
}
if {$newName != ""} {
# 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 {$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 [location $dir $name]
set newRepDir [location $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 [location $dir $name]
set newRepDir [location $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 [location $dir $name]
set repDirInDb [location $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 [location [$corp location] [$corp name]]
if $win95 {
regsub -all {/} $location {\\} location
}
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] -add
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 objects} {
upvar $cmdInfoRef cmdInfo
if ![lempty objects] {
if {[lsearch $options -m] == -1} {
set msg "model(s) '[join $objects "' '"]'"
} else {
set msg "projects(s) '[join $objects "' '"]'"
}
} else {
set msg "repository '$name'"
}
return [$this startDbTool "dboptimize" $endScript \
"Optimizing $msg ..." cmdInfo \
[concat $options $cmdInfo(dbname) $objects]]
}
method Repository::dumpRepository {this endScript cmdInfoRef options name} {
upvar $cmdInfoRef cmdInfo
set msg "Dumping repository '$name' ..."
return [$this startDbTool "dbdump" $endScript $msg cmdInfo \
[concat $options $cmdInfo(dbname)]]
}
method Repository::dumpObject {this endScript cmdInfoRef options type object} {
upvar $cmdInfoRef cmdInfo
set msg "Dumping $type '$object' ..."
if {$type == "model"} {
append options " -m"
}
return [$this startDbTool "dbdump" $endScript $msg cmdInfo \
[concat $options $cmdInfo(dbname) $object]]
}
method Repository::restoreRepository {this endScript cmdInfoRef options repDir {newName ""}} {
upvar $cmdInfoRef cmdInfo
set name [file tail $repDir]
if {$newName == ""} {
set newMsg ""
} else {
set newMsg "under new name '$newName' "
set newDir [location [file dir $repDir] $newName]
if $win95 {
regsub -all {/} $newDir {\\} newDir
}
$this message "Moving '$repDir' to '$newDir' ..."
BasicFS::renameDir $repDir $newDir
set repDir $newDir
}
set msg "Restoring repository '$name' ${newMsg}..."
return [$this startDbTool "dbdump" $endScript $msg cmdInfo \
[concat -r $options $cmdInfo(dbname) $repDir]]
}
method Repository::restoreObject {this endScript cmdInfoRef options type objDir {newName ""}} {
upvar $cmdInfoRef cmdInfo
if {$newName == ""} {
set newMsg ""
} else {
set newMsg "as '$newName' "
}
set msg "Restoring $type ${newMsg}..."
if {$type == "model"} {
append options " -m"
}
return [$this startDbTool "dbdump" $endScript $msg cmdInfo \
[concat -x $options $cmdInfo(dbname) $objDir $newName]]
}
method Repository::expandArchiveCommand {this cmd repDir repName objName objDir objType file type} {
# Check to see if cmd exists in $M4_home/bin using m4_path_name.
# This will also find it if it's in a bin directory added by a module.
#
protect_backslashes {cmd tool} {
set tool [lindex $cmd 0]
set toolEnd [string length [quoteIf $tool]]
incr toolEnd
catch {set tool [m4_path_name bin $tool]}
set cmd "[quoteIf $tool] [string range $cmd $toolEnd end]"
}
# Parent of repository directory. Not quoted since user may want to add
# something to this string.
regsub -all %P $cmd $repDir cmd
# Name of repository. Not quoted since user may want to use this
# string to build another string.
regsub -all %N $cmd $repName cmd
# Repository subdirectory.
regsub -all %R $cmd $repName cmd
# Name of the project or model. Not quoted since user may want to use
# this string to build another string.
regsub -all %O $cmd $objName cmd
# Name of the project or model subdirectory. Not quoted since user
# may want to use this string to build another string.
regsub -all %S $cmd [file tail $objDir] 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::archiveRepositoryDirectory {this endScript dstFile repName repDir} {
set msg "Archiving repository directory '$repDir' ..."
set dir [file dir $repDir]
set cmd [$this expandArchiveCommand \
[m4_var get M4_archive_cmd -context corporate] \
$repDir $repName "" "" "" $dstFile "archive"]
$this toolFinishedScript $endScript
$wmttoolObj startCommand "mtool" $cmd "$this toolFinished" \
$msg {0 0} 1 [list $dir]
return 1
}
method Repository::unarchiveRepositoryDirectory {this endScript srcFile repParentDir} {
set msg "Unarchiving into repository directory '$repParentDir' ..."
set dir $repParentDir
set cmd [$this expandArchiveCommand \
[m4_var get M4_unarchive_cmd -context corporate] \
$repParentDir "" "" "" "" $srcFile "unarchive"]
$this toolFinishedScript $endScript
$wmttoolObj startCommand "mtool" $cmd "$this toolFinished" \
$msg {0 0} 1 [list $dir]
return 1
}
method Repository::archiveObjectDirectory {this endScript dstFile repName repDir objDir objType} {
set msg "Archiving $objType directory '$objDir' ..."
set dir $repDir
set cmd [$this expandArchiveCommand \
[m4_var get M4_archive_cmd -context [string tolower $objType]] \
$repDir $repName "" $objDir $objType $dstFile "archive"]
$this toolFinishedScript $endScript
$wmttoolObj startCommand "mtool" $cmd "$this toolFinished" \
$msg {0 0} 1 [list $dir]
return 1
}
method Repository::unarchiveObjectDirectory {this endScript srcFile repName repDir objType} {
set msg "Unarchiving $objType directory into '$repDir' ..."
set dir $repDir
set cmd [$this expandArchiveCommand \
[m4_var get M4_unarchive_cmd -context [string tolower $objType]] \
$repDir [file tail $repDir] "" "" $objType $srcFile "unarchive"]
$this toolFinishedScript $endScript
$wmttoolObj startCommand "mtool" $cmd "$this toolFinished" \
$msg {0 0} 1 [list $dir]
return 1
}
# 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 lm [ORB::lockManager -nocheck]
} elseif {[catch {set lm [ORB::lockManager]; $lm isNil} error]} {
$this useLockServerId 1
set lm [ORB::lockManager -nocheck]
}
if {[$lm isNil] && $startIfNotRunning} {
# Lockserver is not running, cause it to startup.
#
$this message "Starting lockserver..."
set lm [ORB::lockManager -nocheck]
if [catch {$lm pid} startupError] {
$this warning "Could not start lockserver:\n\n$startupError."
} else {
$this message "Lockserver started."
}
}
return $lm
}
# 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 {}
set desc [LockDescription new]
$this getActiveClients clients
foreach lock $locks {
$desc clear
if { [catch { $ls describeLock $lock $desc }] == 0 } {
set clientKey "[$desc host],[$desc 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 lockId} {
if {[$this getActiveClients clients] == 0} {
return 1
}
set desc [LockDescription new]
set ls [$this getLockServer]
if [catch {$ls describeLock $lockId $desc} error] {
return 0
}
set clientKey "[$desc host],[$desc 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
}
# Returns a textual description of the given lock.
#
method Repository::describeLock {this lockId} {
set ls [$this getLockServer 0]
if [$ls isNil] {
$this warning "Lockserver is not running."
return 0
}
set desc [LockDescription new]
if [catch { $ls describeLock $lockId $desc }] {
$this error "Lock has been removed already."
return 0
}
set objectId [$desc object]
set lockType [$desc types]
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 == "Read" || $lockType == "Write"} {
# 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] {
catch {$dbserver disconnect}
}
return $text
}
# Sets a lock based on the given lock description.
#
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
}
# Removes a lock describes by the given
# lock description.
#
method Repository::removeLock {this lockId {checkHanging 1}} {
set ls [$this getLockServer 0]
if [$ls isNil] {
$this warning "Lockserver is not running."
return 0
}
set desc [LockDescription new]
if [catch { $ls describeLock $lockId $desc }] {
$this error "Lock has been removed already."
return 0
}
if {$checkHanging && ![$this isHangingLock $lockId]} {
$this error "Lock $lockId is not hanging.\
Owning process [$desc pid] of '[$desc user]'\
is still running on host '[$desc host]',\
or was terminated abnormally, possibly due to a system reboot."
return 0
}
set descId [LockDescription new]
$descId setId $lockId
return [$ls removeLocks $descId]
}
# Upgrades the given read-locks to write-locks.
#
#
method Repository::upgradeLocks {this lockIds reason} {
set ls [$this getLockServer 0]
if [$ls isNil] {
$this warning "Lockserver is not running."
return 0
}
return [$ls upgrade $lockIds $reason]
}
# Do not delete this line -- regeneration end marker