#!/bin/sh
#
# File: base-mibman-d.prc
#
# @(#)base-mibman-d.prc	1.161 01/09/21
# Copyright (c) 1993-1998 Halcyon Inc.
#
# MIB Manager Utility Procedures

proc keyname { name } {
    regsub -all {\.} $name {/} newname
    return $newname
}

proc relativename { name } {
    regexp {^.*\.(.*$)} $name dummy relname
    return $relname
}

proc superiorname { name } {
    regexp {(^.*)\..*$} $name dummy supername
    return $supername
}

#########################################################################
#
# method loadModule
#
# Method that loads a module
#
# Usage: loadModule modspec location module parameters reload
#
# Input: modspec - example: filemon+a
#        location - example: local.a.filemon
#        enterprise - example: halcyon
#        module - example: filemon
#        parameters - list of parameters for the module
#        reload - "true" if reload; else "" 
#
# Output: "loadok", "loadduplicate" or "loadfail", depending on the outcome.
#
#######################################################################
method loadModule { modspec location enterprise module parameters reload } {
    ddl print trace "loadModule: modspec=$modspec, location=$location, enterprise=$enterprise, module=$module, parameters=$parameters and reload=$reload\n"
    #
    #
    # do not load new module if already defined or loaded. But if this
    # is a reload, then don't worry about that.
    #
    if { ( [ ilookup -d "" modroot $modspec ] != "" ) && ($reload != "true") } {
        undefine moduleOwner $modspec
	ddl print error "Module Already Loaded ($modspec)\n"
	return [ list "loadduplicate" ]
    }

    #
    # define the module spec 
    #
    define modroot $modspec ""
    define module $modspec [ list $location $enterprise $module $parameters ]

    #
    # call doModuleLoad so that module is loaded immediately 
    #
    if { [ catch { doModuleLoad $modspec } error ] } {
	#
	# module load failed... undo dict changes
	#
        undefine moduleOwner $modspec
	undefine modroot $modspec
	undefine module $modspec
        ddl print error "loadModule: module load failed -- $error\n"
	return [ list "loadfail" ]
    } else {
	#
	# module load succeeded... sync slice, refresh table and return success
	#
	syncModuleSlice 

	set modroot [ ilookup -d "" modroot $modspec ]
	set modType [ toe_send $modroot getModuleParam moduleType "localApplication" ]
	toe_send [ locate $modType ] refreshValueAndTrap
	ddl print info "Module Successfully Loaded ($modspec)\n"
	return [ list "loadok" ]
    }
}

proc syncModuleSlice {} {
    if { [ getComponent ] != "subagent" } {
	syncSlice module
    }
}

#########################################################################
#
# method quickLoad
#
# Method invoked by loader function. This is the routine that gets
# invoked when a module load or a module parameter edit happens from
# the console.
#
# Usage: quickLoad parameters
#
# Input: parameters - parameters to be loaded.
#                     This contains _RELOAD=true parameter if a reload
#
# Output: "loadok", "loadfail" or "loadduplicate", depending on outcome
#
#######################################################################
method quickLoad { parameters pdu } {
    ddl print trace "quickLoad: parameters = $parameters [ toe_name -full ]\n"

    #
    # place all parameters in temporary dictionary
    #

    import string _tmp $parameters

    set module		[ lookup _tmp module ]
    set context		[ lookup -d "" _tmp instance ]
    set location	[ lookup -d "" _tmp location ]
    set enterprise	[ lookup -d "" _tmp enterprise ]
    set prefix		[ lookup -d "localApplication" _tmp moduleType ]
    set targetHost  	[ lookup -d "" _tmp targetHost ]
    set validateHost  	[ lookup -d "" _tmp validateHost ]
    set reload 		[ ilookup -d "" _tmp _RELOAD ]
    set useDefault	[ ilookup -d "" _tmp usedefault ]

    if { $location == "" } {
        set location	[ ilookup -d "" _tmp mibLocation ]
    }
    undefine _tmp _RELOAD

    if { $targetHost != "" } {
        if { [ catch { host2ip $targetHost } targetAddress ] } {
            ddl print warning "quickLoad: Invalid target host $targetHost\n"
            ddl print warning "quickLoad: $targetAddress\n"
            if { $validateHost == "no" } {
                define _tmp targetAddress 1
            } else {
                undefine _tmp
                return [ list "loadfail" ]
            }
        } else {
            define _tmp targetAddress $targetAddress
        }
    }

    if { $context == "" } {
       set context $targetHost
    }
    regsub -all {[^0-9a-zA-Z]} $context {_} context
    define _tmp instance $context

    if { $enterprise != "" } {
	# load the oids cache
	toe_csend errMsg [ lookup peer mib ] loadOidsMap $enterprise
	if { $errMsg != "" } {
            undefine _tmp
	    return [ error $errMsg ]
	}
    }
    set location [ getLocation $context $enterprise $prefix $module $location ]

    set modspec $module
    if { $context != "" } {
	append modspec "+" $context
    }

    export string _tmp parameters -minimal
    undefine _tmp

    # Remove dat file if loadWithDefault(...) is called from console
    if { $useDefault == "true" } {
	set file "/var/opt/SUNWsymon/cfg/$modspec.dat"
	if { [ file exists $file ] } {
	    if { [ catch { interface remove file:$file } msg ] } {
		ddl print error "loadWithDefaults: Failed to remove file $file\n"
		ddl print error "loadWithDefaults: $msg\n"
	    }
	} else {
	    ddl print info "loadWithDefaults: Cannot find file $file for removal\n"
	}
    }

    #
    # Check if this is a reload (that is, module parameters were
    # being editor, rather than the module being loaded for the first time).
    #
    # In that case, we need to ...
    #
    #  (a) make sure any private parameters such as passwords get
    #      retained from the current load of the module (such parameters
    #      may not have been included in the list of parameters sent from
    #      the console for security reasons).
    #
    #  (b) unload the module so it can be loaded again
    #
    if { $reload == "true" } {
        # if module is loaded ?
        set moduleslice [ locate .iso*base.mibman.modules ]
        set moduleInfo [ toe_send $moduleslice ilookup -d "" module $modspec ]
        if { $moduleInfo != "" } {
	    ddl print info "reload of module $modspec\n"
	    set parameters [ includePrivate $modspec $parameters ]
	    set return_string [ unloadModule [ list $modspec RELOAD ] ]
	    if { [ regexp "unloadok" $return_string ] != 1 } {
	       return [ list "loadfail" ]
	    }
        }
    }

    regsub -all "\n" $parameters "; " parameters

    #
    # define the sender of the request as the module owner
    #
    if { $pdu != "" } {
        define moduleOwner $modspec [ bob get $pdu securityName ]
    }

    set returnresult [ loadModule $modspec $location $enterprise $module $parameters $reload ]
    if { $returnresult == "loadok" } {
        ddl print info "quickLoad: sending mod load trap\n"
#       toe_send [ locate .services.snmp ] evaluateCommand null null "trap moduleLoaded {}" {}
        toe_send [ locate .services.snmp ] evaluateCommand null null "trap topoLicRefresh {}" {}
    }

    # sync the state in schedule table
    #---------------------------------
    if { $returnresult == "loadfail" } {
	set scheduleid [ locate *base.mibman.schedule.scheduleTable.scheduleEntry ]
	set state [ toe_send $scheduleid lookup -d "" value ${modspec}State ]
	if { $state != "" } {
	    toe_send $scheduleid schSetLoadState $modspec "SCHEDULED-UNLOAD"

	    set modinfotemp [ toe_send $scheduleid schMakeModuleInfo $parameters ]
	    toe_send $scheduleid define infotemp $modspec $modinfotemp 
	}
    }

    #
    # Set usedefault to false after it is used in lines 173-184 so that
    # it is not used twice.
    #
    if { $useDefault == "true" } {
	set moduleslice [ locate .iso*base.mibman.modules ]
        set moduleInfo [ toe_send $moduleslice ilookup -d "" module $modspec ]
	set moduleParams [ lindex $moduleInfo 3 ]
	if { ! [ catch { import string _tmp $moduleParams } ] } {
	    define _tmp usedefault "false"
	    export string _tmp moduleParams -minimal
	    regsub -all "\n" $moduleParams "; " moduleParams
	    undefine _tmp

	    define module $modspec [ lreplace $moduleInfo 3 3 $moduleParams ]
	    syncModuleSlice 
	} else {
	    ddl print error "quickLoad: Unable to set usedefault to false\n"
	}
    }

   return $returnresult

}

#########################################################################
#
# method includePrivate
#
# Method to add in the value of private parameters (such as passwords)
# to the new load parameters.
#
# Used only when a module is being reloaded after a user edits the mdoule
# parameters.
#
# Usage: set newparameters [ includePrivate modspec parameters ]
#
# Input: modspec - module spec (ie: filemon+aaa)
#        edit_parameters - list of edit parameters to be loaded.     
#                         private passwords will be ****
#
# Output: newparameters - parameter list modified to include current
#                         parameter values for any *** parameters.
#
#######################################################################
method includePrivate { modspec edit_parameters } {

   ddl print trace "includePrivate: entered\n"
   set modroot [ ilookup -d "" modroot $modspec ]

   #
   # Get the current loaded module parameters, excluding the private ones.
   #
   if { [ toe_csend private $modroot exportParameters PRIVATE ]  } {
      ddl print trace "includePrivate: cannot get private parameters for $modroot\n"
      return $edit_parameters
   }

   #
   # Get ALL currently loaded module parameters (including the private ones) 
   #
   if { [ toe_csend all $modroot exportParameters ALL ]  } {
      ddl print trace "includePrivate: cannot get all parameters for $modroot\n"
      return $edit_parameters
   }

   #
   # Okay, we now have three different sets of parameters:
   #
   # (a) edit_parameters : ones to be loaded, sent from console user
   # (b) private : currently loaded ones, with private parameters as ***
   # (c) all : currently loaded ones, with private parameters shown
   #
   # Load these into temporary slices where they are easy to work with
   #
   import string _tmp_edit $edit_parameters
   import string _tmp_private $private
   import string _tmp_all $all

   #
   # Now, find the private parameters. We can do this by going through the  
   # list of "all" parameters and checking which of those parameters were
   # left out of the private list.
   #
   # For such parameters, use the currently loaded value if the user
   # has not entered a new one through the editor.
   #
   sliceforeach key all_value _tmp_all {
      set private_value [ ilookup -d "" _tmp_private $key ]
      if { $all_value != $private_value } {
	  set edit_value [ ilookup -d "" _tmp_edit $key ]
	  if { $edit_value == $private_value } {
	     define _tmp_edit $key $all_value
	     ddl print trace "includePrivate: using cached value for parameter $key in module $modspec\n"
	  }
      }
   }

   #
   # Return the modified _tmp_edit slice to the user.
   # Clean up the temporary slices.
   # 
   export string _tmp_edit new_parameters -minimal
   undefine _tmp_edit
   undefine _tmp_private
   undefine _tmp_all

   return $new_parameters
}

#########################################################################
#
# method unloadModule
#
# Method that unloads a module
#
# Usage: unloadModule modspec
#
# Input: modspec - well, actually two things:
#                  First piece is the actual modspec. The second piece
#                  is either "blank" or "RELOAD".
#
# Output: "unloadok" or "unloadfail", depending on the outcome.
#
#######################################################################
method unloadModule { modspec } {

    ddl print trace "unloadModule: entered with modspec = $modspec\n"
    
    #
    # Check if we are supposed to save the <module>.dat file or not
    #
    set unloadFlag [ lindex $modspec 1 ]
    if { $unloadFlag == "RELOAD" } {
	set reload 1 
	set subagentUnload 0 

    } elseif { $unloadFlag == "SUBAGENT" } {
	set subagentUnload 1 
	set reload 0 

    } else {
	set reload 0 
	set subagentUnload 0 
    }

    #
    # Decompose modspec url, if necessary.
    # Make sure the module specification is okay.
    #
    set modspec [ decomposeUrlModspec [ lindex $modspec 0 ] ]
    set modinfo [ getModinfo $modspec ]
    set modroot [ ilookup -d "" modroot $modspec ]

    if { $modinfo == "" || $modroot == "" } {
	ddl print error "Invalid Module Specification: $modspec\n"
	return [ list "unloadfail" ]
    }

    set moduleType [ toe_send $modroot getModuleParam moduleType localApplication ]

    ddl print info "unloading module $modspec\n"

    if { $reload } {
	define modroot $modspec ""

    } else {
        if { ! $subagentUnload } {
	    catch { sendModuleTrap moduleUnloaded $modspec $modroot }
        }
	undefine modroot $modspec
	undefine module $modspec
    } 

    ddl print trace "unloadModule: modroot is [ toe_name $modroot] \n"

    if { ! $reload } {
	syncModuleSlice 
    }

    #
    # remove status dependency for all objects
    # dependency set in doModuleLoad
    #
    if { $moduleType != "serverSupport" } {
        toe_send $modroot removeDependency status [ locate $moduleType ]
    }

    #
    # get object name
    #
    set name [ toe_name $modroot ]
    ddl print trace "unloadModule - destroying instance tree: $name\n"

    #
    # clear any status generated by this module
    #
    if { ! $reload } {
        toe_send $modroot clearAndPropagateStatus 
    }

    #
    # Log an Event so that the Event Management will close any
    # Open events for this module. We need to do that in the context
    # of the module itself
    #
    if { ! $reload } {
       toe_csend result $modroot writeEvent U "" "" "" "unload"
    } else {
       toe_csend result $modroot writeEvent U "" "" "" "reload"
    }

    set subagentService [ getSubagentService ]
    if { $subagentService != "" } {
	set subagent [ toe_send $modroot lookup -d "" value subagent ]
	if { $subagent != "" } {
	    #
	    # module is loaded in subagent, issue unload command to subagent
	    # and remove from subagent table
	    #
	    toe_send $subagentService unloadSubagentModule $subagent $modspec
	}
    }

    incrTemplateRefCounts $modroot -1

    #
    # Get module type and context for refresh first, before destroying
    #
    set contextName [ toe_send $modroot getContextName ]

    #
    # destroy tree and undefine object in superior
    #
    set superior [ toe_send $modroot toe_superior ]
    uninstantiate $modroot
    toe_send $superior undefine object $name

    if { ! $reload } {
 	toe_send [ locate $moduleType ] refreshValueAndTrap
        if { $moduleType != "serverSupport" } {
            toe_send [ locate $moduleType.moduleTable.moduleEntry.status ] setTrapInfo statusOID
        }
        checkDeadContext $contextName
        checkDeadTemplates

	#
	# notify the tracking table object 
	#
	toe_send [ locate info.modules ] refreshModuleCounts
    }

    ddl print info "module $modspec successfully unloaded\n"

    #
    # exit if subagent and no other modules loaded
    #
    # NOTE: don't do this right now - tom
#    if { [ getComponent ] == "subagent" && [ entries module ] == "" } {
#	ddl print info "All modules unloaded. Subagent exiting.\n"
#	exit
#    }

    toe_send [ locate .services.snmp ] evaluateCommand null null "trap topoLicRefresh {}" {}

    return [ list "unloadok" ]
}




#########################################################################
#
# proc checkDeadContext
# 
# Checks to see if there are still any modules loaded under this context,
# and if not, deletes the context.
#
#########################################################################
proc checkDeadContext { ctx } {

   if { $ctx == "" } {
       return
   }

   sliceforeach modspec id modroot {
       if { [ toe_send $id getContextName ] == $ctx } {
           return
       }
   }

   # Nope, nobody's in this context.
   uninstantiate [ locate .contexts.$ctx.iso ]
   toe_destroy [ locate .contexts.$ctx ]
   toe_send [ locate .contexts ] undefine object $ctx

   # 'uninstantiate' doesn't work for .contexts.$ctx because the node
   # doesn't inherit from class Object.
}
 

#########################################################################
#
# proc checkDeadTemplates
#
# Checks to see if there are any unused templates hanging around, and if
# so, destroys them.
#
#########################################################################

proc checkDeadTemplates { } {
    sliceforeach template count template {
        if { $count == 0 } {
            #
            # Objects in templates, especially the root, do not necessarily inherit
            # from class Object. Furthermore these being templates, it is maybe not
            # desirable to invoke the destructor anyway. Therefore toe_destroy is
            # directly used.
            #
            if { [ catch { locate .templates.$template } id ] } {
                ddl print debug "template not loaded: $template\n"
            } else {
                global templates
                toe_send [ toe_send $id toe_superior ] undefine object $template
                toe_send $id toe_recurse post { toe_destroy [toe_self] }
                undefine template $template
                unset templates($template)
            }
        }
    }
}
        

#########################################################################
#
# proc incrTemplateRefCounts
#
# increments or decrements the usage count of the templates used by the
# given module. The templates used are determined by looking up the
# "templates" key in the module, and if that fails, using
# $module-d and $module-models-d
#
#########################################################################

proc incrTemplateRefCounts { modroot { value 1 } } {
    if { [ toe_csend result $modroot lookup value templates ] } {
        set module [ toe_send $modroot lookup param module ]
        set result [ list $module-d $module-models-d ]
    }

    foreach template $result {
        set count [ ilookup -d 0 template $template ]
        incr count $value
        define template $template $count
    }
}

        

#########################################################################
#
# method getModules
#
# This method is the refreshCommand for mibman (see base-mibman-d.x).
#
# Usage: getModules type ?status?
#
# Input: type - "hardware", "operatingSystem", "localApplication", 
#               "remoteSystem", "serverSupport"
#        includeStatus - 1|0 to include/exclude status
#
# Output: List of name, url, status and toeid of the specified type.
#
#######################################################################
method getModules { type { includeStatus 1 } } {

    set result ""
    sliceforeach modspec modroot modroot {
	#
	# check moduleType
	#
	if { [ toe_csend mtype $modroot getModuleParam moduleType "localApplication" ] || $mtype != $type } {
	    continue
	}

        set consoleName [ toe_send $modroot getConsoleModuleDescription ]

        if { $includeStatus } {
	    set info [ toe_send $modroot getModuleInfo _name version _url _status ]
	    lextract $info 0 name 1 version 2 url 3 status
	    lappend result $modspec $name $consoleName $version $url $status $modroot
        } else {
	    set info [ toe_send $modroot getModuleInfo _name version _url ]
	    lextract $info 0 name 1 version 2 url 
	    lappend result $modspec $name $consoleName $version $url "" $modroot
        }
    }
    return $result
}

#########################################################################
#
# Method:  loadModules
#
# Purpose: Load modules on startup. This method must be executed in the 
#          context of the .iso*base.mibman.modules object. It checks for 
#          entries in the module slice that are not yet loaded (determined 
#          by checking for a corresponding entry in the modroot slice). If 
#          the module is not loaded, it loads it.
#
#          If more than one module needs to be loaded, a 5 second delay 
#          is imposed between each module load.
# 
# Input:   None
#
# Output:  None
#
#######################################################################
method loadModules {} {
    if { [ ilookup -d 0 internal moduleLoadPending ] } {
        ddl print debug "loadModules: module load already pending\n"
        return
    }

    #
    # load any modules in the module slice that are not in the modroot slice
    # NOTE: load only one module... set timer to trigger next
    #
    set pending 0
    sliceforeach modspec "" module {
	if { [ ilookup -d "" modroot $modspec ] == "" } {
	    incr pending
	    if { $pending == 1 } {
		ddl print info "loading module: $modspec\n"
		if { [ catch { doModuleLoad $modspec } error ] } {
		    undefine module $modspec
		    undefine moduleOwner $modspec
		    syncModuleSlice
		    ddl print error "loadModules: $error\n"

                    # sync the state in schedule table
                    #---------------------------------
                    set scheduleid [ locate *base.mibman.schedule.scheduleTable.scheduleEntry ]
                    set state [ toe_send $scheduleid lookup -d "" value ${modspec}State ]
                    if { $state != "" } {
                        ddl print error "loadModules: set the state of $modspec to SCHEDULED-UNLOAD\n"
                        toe_send $scheduleid schSetLoadState $modspec "SCHEDULED-UNLOAD"
                    }
		} else {
                    # sync the state in schedule table
                    #---------------------------------
                    set scheduleid [ locate *base.mibman.schedule.scheduleTable.scheduleEntry ]
                    toe_send $scheduleid refreshValueAndTrap
		    ddl print info "module $modspec successfully loaded\n"
		}
	    }
	}
    }

    if { $pending > 1 } {
	#
	# more modules to load... delay next module load 
	#
	set delay [ lookup -d 5 value loadDelay ]
	ddl print debug "loadModules: defer next module load by $delay seconds\n"
        define internal moduleLoadPending 1
	registerOneShot $delay loadModulesCallback

    } elseif { [ ilookup -d false internal initialized ] == "false" } {
	# 
	# set initialized flag and send warmstart trap
	#
	ddl print info "initial module loading complete\n"
	define internal initialized true
	toe_send [ locate .services.snmp ] \
	        evaluateCommand null null "trap warmStart {}" {}
        toe_send [ locate .services.snmp ] \
                evaluateCommand null null "trap topoLicRefresh {}" {}
    }
}

#########################################################################
#
# Method:  abortOnModuleLoad
#
# Purpose: Abort agent when an error is encountered during module
#          load -- sync the module slice to ensure that the "bad" 
#          module is not persistent.
#
#########################################################################
proc abortOnModuleLoad { modspec error } {
    ddl print error "abortOnModuleLoad: $modspec - $error\n"
    undefine module $modspec
    undefine moduleOwner $modspec
    syncModuleSlice
    abort "aborting on module load: $modspec - $error"
}

proc loadModulesCallback {} {
    undefine internal moduleLoadPending
    loadModules
}

#########################################################################
#
# Method directLoad
#
# Method that loads directly loads an internal module.  Used by the
# topology agent to create views (module instances) through an API.
#
# Usage: directLoad module context location parameters
#
# Input: module - example: topology
#        context - example: heisz_domain1_root
#        enterprise - example: halcyon
#        location - example: serverSupport
#        parameters - list of parameters for the module
#        userid - securityName to assign as module owner
#
# Output: "loadok", "loadduplicate" or "loadfail", depending on the outcome.
#
#######################################################################
method directLoad { module context enterprise moduleType parameters userid moduleparent } {
    ddl print info "directLoad: $module context $context\n"

    #
    # Build the load information.
    #

    undefine tmpParam
    # just in case

    import string tmpParam $parameters
    set location [ ilookup -d "" tmpParam location ]
    if { $location == "" } {
        set location [ ilookup -d "" tmpParam mibLocation ]
    }
    undefine tmpParam

    set modspec "$module+$context"
    set location [ getLocation $context $enterprise $moduleType $module $location ]

    #
    # Do not load new module if already defined or loaded.
    #
    if { [ ilookup -d "" modroot $modspec ] != "" } {
	ddl print error "Module Already Loaded ($modspec)\n"
	return [ list "loadduplicate" ]
    }

    #
    # Define the module spec.
    #
    define modroot $modspec ""
    define module $modspec [ list $location $enterprise $module $parameters ]
    define moduleOwner $modspec $userid
    define moduleParent $modspec $moduleparent

    #
    # Bypass the timed module loading
    #
    if { [ catch { doModuleLoad $modspec } error ] } {
	#
	# Module load failed... undo dict changes
	#
	undefine module $modspec
	undefine modroot $modspec
        undefine moduleOwner $modspec
        undefine moduleParent $modspec
	syncSlice module
	ddl print error "$error\n"
	return [ list "loadfail" ]
    } else {
	#
	# Module load succeeded... sync slice, refresh table and return success
	#
	syncSlice module
	
	set modroot [ ilookup -d "" modroot $modspec ]
	set modType [ toe_send $modroot getModuleParam moduleType "localApplication" ]
	toe_send [ locate $modType ] refreshValueAndTrap
	ddl print info "Module Successfully Loaded ($modspec)\n"
	return [ list "loadok" ]
    }
}

#########################################################################
#
# method doModuleLoad
#
# The method that actually does the load of a single module.
# Invoked from loadModules.
#
# Usage: doModuleLoad modspec
#
# Input: modspec - example: filemon+a
#
# Output: None
#
#######################################################################
method doModuleLoad { modspec } {
    ddl print trace "doModuleLoad: modspec is $modspec\n"
    #
    # get module spec
    #

    # get the context
    set context [ lindex [ split $modspec "+" ] 1 ]

    # get the module info
    lextract [ ilookup module $modspec ] 0 location 1 enterprise 2 module 3 parameters

    ddl print trace "doModuleLoad: location is $location and enterprise is $enterprise and module is $module and parameters are $parameters\n"

    if { $enterprise == "" && $location == "" } {
	return [ error "Module enterprise or location must be specified" ]
    }

    if { $enterprise != "" } {
	# load the oids cache
	toe_csend errMsg [ lookup peer mib ] loadOidsMap $enterprise
	if { $errMsg != "" } {
	    return [ error $errMsg ]
	}
    }
 
    undefine tmpParam 
    # Just in case

    import string tmpParam $parameters
    set moduleType [ ilookup -d "localApplication" tmpParam moduleType ]

    if { $location == "" } {
	set location [ getLocation $context $enterprise $moduleType $module ]
	if { $location == "" } {
	    undefine tmpParam
	    return [ error "Cannot find the location for module $module" ]
	}
    }

    ddl print trace "doModuleLoad: location is now $location\n"

    #
    # check if the module root is already taken by some other module.
    #

    if { [ catch { locate $location } ] } {
        ddl print trace "doModuleLoad: $location doesnot exist. Okay to load module.\n"
    } else {
        ddl print trace "$location is already used by some other module."
	return [ error "module root already in use by some other module." ]
    }

    #
    # Handle oid preferences
    #
    set oidpref [ ilookup -d "" tmpParam oid ]
    undefine tmpParam
    setModuleOidPreferences $location $oidpref

    #
    # determine the template filename to load
    #
    set template $module-d

    #
    # Setup context
    #
    if { $context != "" } {
	set contextroot [ locate .contexts ]
	if { ! [ toe_send $contextroot exists object $context ] } {
	    set string "$context = {\ncontextName = $context\n};"
	    toe_send $contextroot import string internal $string
	}
    }

    #
    # check for subagent support
    #
    set subagentService [ getSubagentService ]

    if { $subagentService != "" } {
#	ddl print debug "doModuleLoad: subagents supported\n"
	set subagent [ toe_send $subagentService getSubagentByModspec $modspec ]
 
    } else {
	ddl print debug "doModuleLoad: subagents not supported\n"
	set subagent ""
    }
 
    #
    # START of patch to disable loading of modules into native subagents
    #
    if { $subagent != "" } {
	ddl print info "doModuleLoad: native subagents not supported\n"
	ddl print info "doModuleLoad: loading $modspec into master agent\n"
        set subagent ""
    }
    #
    # END of patch to disable loading of modules into native subagents
    #
 
    if { $subagent != "" } {
 
	ddl print info "doModuleLoad: $modspec to be loaded into subagent $subagent\n"
 
	if { [ toe_send $subagentService subagentModuleFileExists ] } {
	    set delay [ lookup -d 5 value loadDelay ]
	    ddl print debug "deferring load of subagent module for $delay seconds\n"
	    registerOneShot $delay refreshValueAndTrap
	    return
	}
 
	#
	# create modroot node in module subtree
	#
	set modroot [ 
	    toe_send [ lookup peer mib ] \
		instantiateTemplate $location subagent-proxy-d "" 
	]
 
	#
	# insert the parameters at the root of the module
	#
	toe_send $modroot define param moduleSpec $modspec
	toe_send $modroot import string param $parameters
 
	#
	# activate the new module (and any "inactive" superiors)
	#
	if { [ toe_csend error $modroot superactivate ] } { 
            ddl print error "doModuleLoad: activate failed\n" 
            abortOnModuleLoad "$modspec $error"
        }

	define modroot $modspec $modroot
 
	#
	# load the module in the subagent - start it if necessary
	#
	set result [ 
	    toe_send $subagentService loadSubagentModule $subagent \
		     $modroot $modspec $parameters 
	]
	if { $result == 0 } {
	     #
	     # failed to start subagent - port is not available, cleanup
	     #
	     unloadModule $modspec
	     return [ error "Failed to start subagent" ]
	}
 
    } else {

	#
	# instantiate the template
	#
	set modroot [
	    toe_send [ lookup peer mib ] \
		instantiateTemplate $location $template "" BRANCH
	]

	ddl print trace "doModuleLoad: modroot is $modroot [ toe_name $modroot ] \n"
    
	#
	# insert the parameters at the root of the module
	#
	toe_send $modroot define param moduleSpec $modspec
	toe_send $modroot import string param $parameters

        incrTemplateRefCounts $modroot

	#
	# activate the new module (and any "inactive" superiors)
	#
	if { [ toe_csend error $modroot superactivate ] } { 
            ddl print error "doModuleLoad: activate failed\n" 
            abortOnModuleLoad $modspec "$error"
        }

	define modroot $modspec $modroot

	#
	# sync oid preferences file
	#
	toe_send [ lookup peer mib ] syncOidPreferences

	#
	# if subagent, remove subagent modules file
	#
	if { [ getComponent ] == "subagent" } {
	    #
	    # remove subagent-modules-d.dat file
	    #
	    set file [ lookup value .config.subagent.moduleFile ]
	    set file [ lindex [ split $file ":" ] 1 ]
 
	    catch { interface remove file:$file }
	}

        #
        # set module ownership and ACLs
        #
        # case 1: module loading on agent init for the very first time
        #         - owner and parent are ""
        #         - loadinfo is ""
        #
        #         --> promoteMemberships from core
        #
        # case 2: module loading on agent init but not the very first time
        #         - owner and parent are ""
        #         - loadinfo is not ""
        #
        #         --> do nothing
        #
        # case 3: module loading via user set to mibman loader
        #         - owner is not ""
        #         - parent is ""
        #         - loadinfo is ""
        #
        #         --> promoteMemberships from core
        #         --> add owner 
        #
        # case 4: domain root topology module loading via directLoad
        # case 5: non-domain root topology module loading via directLoad
        #         - owner and parent are not ""
        #         - loadinfo is ""
        #
        #         --> promoteMemberships from parent
        #         --> add owner 
        #
	set owner [ lookup -d "" moduleOwner $modspec ]
	set parent [ lookup -d "" moduleParent $modspec ]

        if { [ toe_send $modroot getLoadInfo ] == "" } {
            toe_send $modroot setLoadInfo $owner
	    if { $owner == "" && $parent == "" } {
                #
                # check for existing memberships - for backward
                # compatibility with *.dat files with no loadinfo
                #
	        if { ! [ toe_send $modroot hasMemberships "" ] } {
	            toe_send $modroot promoteMemberships "" 
                }
            } else { 
	        toe_send $modroot promoteMemberships $parent 
	        if { $parent != "" } {
                    undefine moduleParent $modspec
                }
                if { $owner != "" } {
	            toe_send $modroot addLogicalMembers adminUsers $owner 
                    undefine moduleOwner $modspec
                }
            }
	}

	sendModuleTrap moduleLoaded $modspec $modroot
    }

    #
    # set status dependency. dependency removed in unloadModule
    #
    if { $moduleType != "serverSupport" } {
        toe_send $modroot registerDependency status [ locate $moduleType ] refreshValue
    } else {
        toe_send [ locate serverSupport ] refreshValue
    }

    #
    # notify the tracking table object
    #
    toe_send [ locate info.modules ] refreshModuleCounts

    #
    # Record the loaded module into the registry
    #
    if { $moduleType != "serverSupport" } {
	set modloadroot [ locate .iso*mibman.modules ]
	set modparams [ toe_send $modloadroot lookup -d "" module $modspec ]
	toe_send $modloadroot define modRegTemp $modspec "$modparams"
    }

    if { ![ catch { set moduleRegistry [ locate .iso*modules.mcp.moduleRegistryTable.moduleRegistryEntry ] } msg ] } {
	set modloadroot [ locate .iso*mibman.modules ]
	foreach modspec [ toe_send $modloadroot entries modRegTemp ] {
	    toe_send $moduleRegistry define modReg $modspec [ toe_send $modloadroot lookup -d "" modRegTemp $modspec ]
	}
	toe_send $modloadroot undefine modRegTemp
	toe_send $moduleRegistry syncSlice modReg
    } else {
	if { $moduleType != "serverSupport" } {
	    ddl print info "doModuleLoad: MCP module not yet loaded. Saving $modspec into a temporary registry.\n"
	}
    }
}

proc findModule { modspec } {
    #
    # locate the module that matches the spec
    #
 
    if { $modspec == "\{\}" } {
	return ""
    }
 
    lextract [ ilookup module $modspec ] 0 location 1 enterprise 2 module 3 parameters
 
    if { $location == "" } {
	# get the context
	set context [ lindex [ split $modspec "+" ] 1 ]
	set context [ lindex [ split $context ":" ] 0 ]
	import string tmpParam $parameters
	set prefix [ ilookup -d "localApplication" tmpParam moduleType ]
	undefine tmpParam
	set location [ getLocation $context $enterprise $prefix $module ]
    }
    return $location
}

proc findMIBObject { args } {

    set result ""
    foreach url $args {
	#
	# decompose URL
	#
	if { [ catch { decomposeSnmpURL $url } parts ] } {
	    lappend result "ERROR"
	    continue
	}

	lextract $parts 0 net_loc 1 type 2 spec 3 attribute 4 index

	switch $type \
	    {oid} {
		#
		# return numeric oids verbatim
		#
		lappend result $url
	    } \
	    {sym} - {mod} {
		#
		# resolve symbolic oids
		#
		set spec [ checkValidLocation $spec ]
		ddl print debug "NEW SPEC IS : $type $spec $attribute $index "
		set ref [ resolveSymbolicOid $type $spec $attribute $index ]
		lextract $ref 0 spec 1 attribute 2 index
		lappend result [
		    buildSnmpURL $net_loc "oid" $spec $attribute $index
		]
	    } \
	    default {
		lappend result "ERROR"
	    }
    }

    return $result
}

proc resolveSymbolicOid { type spec attribute index } {
    set status ""
    set prefix ""
    set result ""
    set context ""
    set root [ locate modules ]

    #
    # determine url mode (used in metadata repository)
    #
    set urlmode [ lookup -d "" value .config.current.urlMode ]

    regsub -all "\{\}" $spec "" spec

    set parts [ split $spec "/" ]

    if { $type == "mod" } {
	#
	# strip off module spec.
	#
	set modspec [ lindex $parts 0 ]

	#
	# try to locate module - if one was specified.
	#
	if { $modspec != "" } {
	    #
	    # remove filter from modspec
	    #
	    set specparts [ split $modspec "+" ]
	    if { [ llength $specparts ] > 1 } {
	        set module [ lindex $specparts 0 ]
	        set context [ lindex $specparts 1 ]
		set contextparts [ split $context ":" ]
	        set subcontext [ lindex $contextparts 0 ]

		#
		# override module instance names if in metadata repository
		#
		if { $urlmode == "metadata" } {
		    set subcontext "meta_$module"
		    set contextparts [ lreplace $contextparts 0 0 $subcontext ]
		    set context [ join $contextparts ":" ]
		}

	        if { $subcontext != "" } {
	            set specparts [ lreplace $specparts 1 1 $subcontext ]
	        } else {
	            set specparts [ lreplace $specparts 1 1 ]
	        }
	        set modspec [ join $specparts "+" ]
	    } else {
		#
		# even single-instance modules are multi-instance in the MDR
		#
		if { $urlmode == "metadata" } {
		    set context meta_$modspec
		    set modspec $modspec+$context
		}
	    }

            #
            # verify that module is actually loaded
            #
            if { [ ilookup -d "" modroot $modspec ] == "" } {
		return [ list "ERROR" $attribute $index ]
	    }

	    set name [ findModule $modspec ]
	    if { $name == "" } {
		return [ list "ERROR" $attribute $index ]
	    }

	    #
	    # place new name in spec
	    #
	    set parts [ lreplace $parts 0 0 $name ]
	}
    } elseif { $type == "sym" } {
	set nodes [ split $spec "." ]
	if { [ lindex $nodes 1 ] == "contexts" } {
	    set context [ lindex $nodes 2 ]
	}
    }

    foreach name $parts {

#	Prune any empty tree components ( ".." -> ".").  This may completely 
#	empty out the tree string...
#	--------------------------------------------------------------------

	regsub -all "\\.\\." $name "." name
	regsub "^\\.$" $name "" name

	if { $name == "" } {
	    continue
	}

	#
	# locate the named object (relative to the last located object)
	#
	if { [ toe_csend oid $root fullOidOf $name ] } {
	    lappend result "ERROR"
	    set status "ERROR"
	    break
	}
	
	#
	# remove previously found oid from start of this oid
	#
	if { $prefix != "" } {
	    regsub "^$prefix" $oid "" relativeoid
	    regsub "^\." $relativeoid "" relativeoid
	    set prefix [ join [ lappend prefix $relativeoid ] "." ]
	} else {
	    set relativeoid $oid
	    set prefix $oid
	}

	#
	# append relative oid to result
	#
	lappend result $relativeoid

	#
	# set root of next search to named object
	#
	set root [ toe_send $root locate $name ]
    }

    set spec [ join $result "/" ]

#   If the result list is empty, we must be looking at the base of the
#   module tree.
#   ------------------------------------------------------------------
    if { $spec == "" } {
	set spec [ toe_send $root fullOidOf _self ]
    }

    if { $attribute != "" } {
	if { $status == "ERROR" } {
	    set attribute "ERROR"
	} else {

	    if { [ toe_csend attroid $root getShadowIndices $attribute ] } {
		set attribute "ERROR"
	    } else {

#		Append the attribute index length.
#		----------------------------------
		set attribute $attroid.[ llength [ split $attroid "." ] ]

	    }
	}
	#
	# remap spec to shadow spec
	#
	regsub {^[./]?1([./])} $spec {2\1} spec
    }

    set index [ join [ toe_send $root getIndexSpec ] "," ]

    if { $context != "" } {
	set spec "$context/$spec"
    }
    return [ list $spec $attribute $index ]
}


proc locateObject { args } {
    set result ""
    foreach url $args {
	#
	# decompose URL
	#
	if { [ catch { decomposeSnmpURL $url } parts ] } {
	    lappend result "ERROR"
	    continue
	}

	lextract $parts 0 net_loc 1 type 2 spec 3 attribute 4 index

	switch $type \
	    {sym} - {mod} {
		#
		# locate symbolic oids
		#
		lappend result [ locateBySymbol $type $spec ]
	    } \
	    default {
		error "Unsupported URL For Operation: $url"
	    }
    }

    return $result
}

proc locateBySymbol { type spec } {
    set object [ locate modules ]
    set parts [ split $spec "/" ]

    if { $type == "mod" } {
	if { [ catch { findModule [ lindex $parts 0 ] } name ] } {
	    error "Bad Object Specification: $name"
	}
	set parts [ lreplace $parts 0 0 $name ]
    }

    foreach name $parts {
	#
	# set object of next iteration to named object
	#
	if { $name != "" } {

#	    Prune any empty tree components ("{}." -> "").
#	    ----------------------------------------------

	    regsub -all "\{\}\." $name "" name

	    set object [ toe_send $object locate $name ]
	}
    }

    return $object
}

method refreshNodes { urls } {
    foreach url $urls {
	ddl print debug "processing refresh request for $url\n"
	toe_send [ locateObject $url ] refreshValueAndTrap
    }

    return [ list "Refresh Successful" ]
}

method ackNode { ackspec } {
    lextract $ackspec 0 urls 1 ack
    foreach url $urls {
	#
	# decompose URL
	#
	if { [ catch { decomposeSnmpURL $url } parts ] } {
	    lappend result "ERROR"
	    continue
	}

	lextract $parts 0 net_loc 1 type 2 spec 3 attribute 4 index

	set result "Acknowledgement Successful"

	switch $type \
	    {sym} - {mod} {
		#
		# ack the node
		#
		ddl print debug "processing ack request for ${url}\n"
		if { [ catch { locateBySymbol $type $spec } object ] } {
		    set result "Error: Field Reference Is Not Acknowledgeable"
		    break
		}
		if { [ regexp {^[0-9\.]+$} $index ] } {
		    set rowname [ toe_send $object getRowName $index ]
		} else {
		    set rowname $index
		}
		if { [ toe_csend error $object setAck $rowname $index $ack ] } {
		    set result "Acknowledgement Error: $error"
		    break
		}
	    } \
	    default {
		set result "Error: Unsupported URL For Operation: $url"
		break
	    }
    }

    return [ list $result ]
}


################################################################
#
# method disableModule
#
# Returns disableok, disablefail or currdisabled, depending on
# the outcome.
#
################################################################
method XXXdisableModule { modspec } {

    ddl print debug "disableModule Entry: modspec is $modspec\n"

    #
    # Make sure the module specification is okay
    #
    set modspec [ decomposeUrlModspec $modspec ]
    set modinfo [ getModinfo $modspec ]

    if { $modinfo == "" } {
	ddl print error "Invalid Module Specification: $modspec\n"
	return [ list "disablefail" ]
    }

    #
    # Disable the module, if it is not disabled already.
    #
    set modroot [ ilookup -d "" modroot $modspec ]
    ddl print trace "disableModule: modroot is $modroot\n"

    set current_state [ toe_send $modroot getCurrentModuleState ]
    ddl print trace "disableModule: current state is $current_state\n"

    if { $current_state == "DISABLED" } {
       ddl print info "Module Is Already Disabled ($modspec)\n"
       return [ list "currdisabled" ]
    } else {
       toe_send $modroot setPersistentModuleState DISABLED
       toe_send $modroot setCurrentModuleState DISABLED

       #
       # refresh the table
       #
       refreshValue
    }

    ddl print info "Module Successfully Disabled ($modspec)\n"
    return [ list "disableok" ]
}

################################################################
#
# method enableModule
#
# Returns enableok, enablefail or currenabled, depending on
# the outcome.
#
################################################################
method XXXenableModule { modspec } {

    ddl print debug "enableModule Entry: modspec is $modspec\n"

    #
    # Make sure the module specification is okay
    #
    set modspec [ decomposeUrlModspec $modspec ]
    set modinfo [ getModinfo $modspec ]

    if { $modinfo == "" } {
	ddl print error "Invalid Module Specification: $modspec\n" 
	return [ list "enablefail" ]
    }

    #
    # Enable the module, if it is not already enabled 
    #
    set modroot [ ilookup -d "" modroot $modspec ]
    ddl print trace "enableModule: modroot is $modroot\n"

    set current_state [ toe_send $modroot getCurrentModuleState ]
    ddl print trace "enableModule: current_state is $current_state\n"

    #
    # If state is:
    # (1) SCHEDULED-ON -- then do nothing (already enabled)
    # (2) SCHEDULED-OFF -- then set to MANUAL-ON
    # (3) DISABLED -- clear the disabled state, and figure out new state
    # (4) MANUAL-ON -- then do nothing (already enabled)
    #
    switch $current_state \
       {DISABLED} {
	  toe_send $modroot setPersistentModuleState ENABLED
	  if { [ toe_send $modroot checkModuleActiveTimeWindow ] == 1 } {
	     set new_state "SCHEDULED-ON"
	  } else {
	     set new_state "SCHEDULED-OFF"
	  }
	  set return_state "Module Successfully Enabled ($modspec)"
	  set return_string "enableok"
       } \
       {SCHEDULED-OFF} {
	  set new_state "MANUAL-ON"
	  set return_state "Module Successfully Enabled ($modspec)"
	  set return_string "enableok"
       } \
       {SCHEDULED-ON} {
	  set new_state ""
	  set return_state "Module Is Already Enabled ($modspec)"
	  set return_string "currenabled"
       } \
       {MANUAL-ON} {
	  set new_state ""
	  set return_state "Module Is Already Enabled ($modspec)"
	  set return_string "currenabled"
       } \
       default {
	  set new_state ""
	  set return_state "Module Enable Failed - Module in Unknown State $current_state ($modspec)"
	  set return_string "enablefail"
       }


    # Set new state if there is one. This does an implicit refreshStatus
    if { $new_state != ""  } {
       toe_send $modroot setCurrentModuleState $new_state
    }

    ddl print info "$return_state\n"

    return [ list $return_string ]
}

################################################################
#
# method decomposeUrlModspec
#
################################################################
method decomposeUrlModspec { modspec } {

    ddl print trace "decomposeUrlModspec: modspec is $modspec\n"
    #
    # see if modspec is a url
    #
    if { ! [ catch { decomposeSnmpURL $modspec } urlparts ] } {
	lextract $urlparts 2 modspec
	set modspec [ lindex [ split $modspec "/" ] 0 ]
    }

    ddl print trace "decomposeUrlModspec: new modspec is $modspec\n"

    return $modspec
}

################################################################
#
# method getModinfo
#
################################################################
method getModinfo { modspec } {

    #
    # get module's location as a location prefix
    #
    set modinfo [ lookup -d "" module $modspec ]

    ddl print trace "getModinfo: modinfo is $modinfo\n"

    return $modinfo
}

################################################################
#
# method getRootURL
# refreshCommand for browserRoot (base-mibman-d.x).
#
################################################################
method getRootURL {} {

    set url [ ilookup -d "" internal browserRoot ]

    if { $url == "" } {
        set net_loc [ snmp get address ]
        set url [ buildSnmpURL $net_loc "sym" "/base/mibman/modules" "" "" ] 
        define internal browserRoot $url
    }
    return $url
}

########################################################
#
# method getConsoleDescription
#
# Provide the hostname as a useful description of this
# node (the mibman/modules table).
#
# Input: No input - operates using node information.
#
# Output: The node description information.
#
########################################################
method getConsoleDescription { } {
    if { [ toe_name ] == "modules" } {
	set hostname [ sysinfo hostname ]
	set hostaddr [ sysinfo address ]
	return "$hostname \[$hostaddr\]"
    }
    return [ ClassConsoleShadow:getConsoleDescription ]
}

########################################################
#
# method getFamilyName
#
# Return family information based on the type of this
# host.
#
# NOTE: THIS ROUTINE NEED SERIOUS UPDATES FOR ALL HOSTS.
#
# Input: No input - operates using node information.
#
# Output: The node description information.
#
########################################################
method getFamilyName { { comp "" } } {
 
    if { [ toe_name ] != "modules" } {
        return [ ClassConsoleShadow:getConsoleDescription ]
    }
 
    if { $comp == "" } {
        set comp [ getComponent ]
    }
 
    if { $comp != "agent" } {
	return [ toe_send [ locate .config.$comp ] \
		lookup -d "symon-server-agent" value family_name ]
    }
 
    if { [ sysinfo stdhw ] == "sun" } {
	return [ getFamilyType ]
    } else {
        return "generic-host"
    }
}

########################################################
#
# method checkModule
#
# Called by checker node in base-mibman-d.x. Check to see
# if the specified module spec is installed or loaded
#
# Input: <module>[-<subspec>][+<context>[:<filter>]] | 
#        snmp://<host>[:<port>/mod/<module>[-<subspec>][+<context>[:<filter>]]
#
# Output: notInstalled - if the module -m.x file cannot be found 
#         installed    - if the -m.x file is available but the module is
#                        not loaded
#         loaded       - the module is currently loaded
#
# Note: if <context> is specified, and the module is loaded with a
#       different context, then "installed" will be returned 
#
########################################################
method checkModule { modspec } {
    #
    # Decompose modspec url, if necessary.
    #
    set full_modspec [ decomposeUrlModspec $modspec ]
 
    #
    # remove context (if exists) from modspec
    #
    set modspec [ lindex [ split $full_modspec + ] 0 ]
 
    #
    # see if module is installed
    #
    set id [ locate .iso*base.info.modules.moduleTable.moduleEntry.name ]
    set installedModules [ toe_send $id getValues ]
 
    if { [ lsearch $installedModules $modspec ] == -1 } {
	return "notInstalled"
    }
 
    #
    # check to see if module loaded
    #
    set id [ locate .iso*mibman.modules ]
    set loadedModules [ toe_send $id entries module ]
 
    if { [ lsearch $loadedModules $full_modspec ] == -1 } {
	return "installed"
    }
 
    return "loaded"
}

method getLocation { context enterprise modType module args } {
    set location [ lindex $args 0 ]
    if { $location == "" } {
        if { $enterprise == "" } {
            error "getLocation: no enterprise and no location specified"
        }
        set path ".iso.org.dod.internet.private.enterprises.$enterprise"
        set sysObjectPath [ toe_send [ lookup peer mib ] lookup $enterprise sysObjectPath ]
        if { $sysObjectPath != "" } {
	    append path .$sysObjectPath
        }

        regsub -all -- {-} $module {.} modulePath
        if { $context == "" } {
	    return "$path.modules.$modType.$modulePath"
        } else {
	    return ".contexts.$context$path.modules.$modType.$modulePath"
        }
    } else {
        if { [ string index $location 0 ] != "." } {
            set path "iso.org.dod.internet.private.enterprises"
            if { $enterprise != "" } {
                append path ".$enterprise"
            }
            set location ".$path.$location"
        }
        if { $context == "" } {
            return $location
        } else {
            return ".contexts.$context$location"
        }
    }
}


method setModuleOidPreferences { location oid } {
    set mibman [ lookup peer mib ]
    set strict [ expr { [ lookup -d "false" value .config.current.strictModuleOids ] != "false" } ]
    set path [ split $location . ]
    if { [ lindex $path 1 ] == "contexts" } {
        set path [ lreplace $path 1 2 ]
    }
    set root {{}}
    if { $oid != "" } {
        set oids [ split $oid . ]
        set backwardsOid {}
        foreach oid $oids {
            set backwardsOid [ linsert $backwardsOid 0 $oid ]
        }
        foreach oid $backwardsOid {
            if { $path == $root } {
                error "Oid specification too long for location : module load refused"
            }
            toe_send $mibman addOidMapping [ join $path / ] $oid $strict
            set path [ lreplace $path end end ]
        }
    }
    if { $strict && $path != $root && ! [ toe_send $mibman existsOidMapping [ join $path / ] ] } {
        error "No oid defined for [ join $path . ] : module load refused"
    }
}
        

    
         
    



#########################################################################
#
# method: getModuleRevisions
#
# This method registers all modules for module revision control.
#
# Input:  
#
# Output:
#
#######################################################################
method getModuleRevisions { args } {

    set modspec [ lindex $args 0 ]
    if { $modspec != "" } { 
	set modroot [ ilookup -d "" modroot $modspec ]
	if { $modroot != "" } {
	    set info [ toe_send $modroot getModuleInfo module version ]
	    lappend info 1
	    return [ list $info ]
	} else {
	    return
	}
    }
	  
    set result ""
    undefine tmpversion
    undefine tmpcnt
    sliceforeach modspec modroot modroot {
	set info  [ toe_send $modroot getModuleInfo module version ]
	lextract $info 0 module 1 version
	set cnt [ lookup -d "0" tmpcnt $module ]
	incr cnt
	define tmpversion $module $version
	define tmpcnt $module $cnt
    }
    sliceforeach module version tmpversion {
	set cnt [ lookup -d "0" tmpcnt $module ]
	set info [ list $module $version $cnt ]
	lappend result $info
    } 
    undefine tmpversion
    undefine tmpcnt
    return $result
}

method sendModuleTrap { trap modspec modroot } {

    ddl print debug "sendModuleTrap: $trap $modspec $modroot\n"

    if { ! [ catch { lookup internal initialized } ] } {
        set version [ toe_send $modroot getModuleParam version "1.0" ]
        set value [ list $modspec $version ]
        set oid [ fullOidOf base.trapInfo.moduleInfo ].0
        set command "trap $trap {{oid//$oid {OCTET STRING} {$modspec $version}}}"
        toe_send [ locate .services.snmp ] evaluateCommand null null $command ""
    }
}


method unloadModuleByOid { oid } {
    if { [ catch { extractModuleFromOid $oid } modspec ] } {
        ddl print error "unloadModuleByOid: bad oid - $oid\n"
        return ""
    }
    ddl print debug "unloadModuleByOid: $oid --> $modspec\n"
    unloadModule [ list $modspec SUBAGENT ]
}

proc extractModuleFromOid {oid} {
    set id [ locate .iso*base.mibman.modules ]
    set baseoid [ toe_send $id lookup internal fulloid ]
    if { [ string first $baseoid $oid ] != 0 } {
       error "extractModuleFromOid - can't parse oid $oid"
    }
 
    # Strip off baseoid
    set len [ string length $baseoid ]
    incr len
    set modspec [ string range $oid $len end ]
 
   # strip off next 4 oid numbers representing <module type>.moduleTable.moduleEntry.<size>
    if { [ regexp "^\.?\[0-9]+\.\[0-9]+\.\[0-9]+\.\[0-9]+\.(.*)$" $modspec dummy modspec ] } {
       set modspec [ oid2string $modspec ]
       return $modspec
    } else {
       error "extractModuleFromOid - can't parse oid $oid"
    }
    return ""
}

################################################################
#
# Method:  updateModuleParams
#
# Purpose: Update the module params of a loaded module
#
# Inputs:  modspec
#          params  - list of lists of key-value parameter pairs
#
# Outputs: 1 - success
#          0 - unable to find module
#
# Notes:   Caller should catch errors.
# 
################################################################
proc updateModuleParams { modspec params } {
    set module [ ilookup -d "" module $modspec ]
    if { $module == "" } {
        return 0
    }

    set moduleparams [ lindex $module 3 ]
    import string _updateModuleParams $moduleparams

    foreach param $params {
        lextract $param 0 key 1 value
        define _updateModuleParams $key $value
    }

    export string _updateModuleParams moduleparams -minimal
    undefine _updateModuleParams

    regsub -all "\n" $moduleparams "; " moduleparams
    define module $modspec [ lreplace $module 3 3 $moduleparams ]
    syncModuleSlice  
    return 1
} 

proc getFamilyType { } {

    global env
    set deviceinfofile "deviceinfo.conf"

    if { ![ file exists "$env(ESDIR)/cfg/${deviceinfofile}" ] } {
        set familytype [ sysinfo details ]
    } else {
        set fd [open "$env(ESDIR)/cfg/${deviceinfofile}" r]
        set pattern "familytype"
        while { [ gets $fd line ] >= 0 } {
            if { [ regexp -nocase $pattern $line ] } {
                ddl print debug "getFamilyName:$line"
                set data [string trim $line]
                lextract [ split $data " \t" ] [ expr [ llength [ split $data " \t" ] ] -1 ] familytype
            }
        }
        close $fd
    }
    return "$familytype"
}

#------------------------------------
# module management: schedule service
#------------------------------------

#------------------------------------------------------------------------
# proc schGetLoadedModuleInfo
#
# this procedure used to get the loaded module info from the module slice
# in .iso*base.mibman.modules node
#------------------------------------------------------------------------
proc schGetLoadedModuleInfo { modspec } {

    ddl print trace "schGetLoadedModuleInfo: $modspec [ toe_name -full ]\n"

    set modinfo ""
    if { ![ catch { locate .iso*base.mibman.modules } ] }  {
        set moduleid [ locate .iso*base.mibman.modules ]
        set modinfo [ toe_send $moduleid ilookup -d "" module $modspec ]
    }

    return $modinfo
}

#---------------------------------------------------------------
# proc schSyncEnableSchedule
#
# 1. when unload module that has enableschedule
# 2. when modify the enabletimewindow in attribute editor
#---------------------------------------------------------------
proc schSyncEnableSchedule { modspec enabletimewindow } {

    ddl print trace "schSyncEnableSchedule: $modspec $enabletimewindow [ toe_name -full ]\n"

    set modinfo [ schGetLoadedModuleInfo $modspec ]
    if { $modinfo == "" } {
        return
    }

    set moduleparam [ lindex $modinfo 3 ]
    if { [ catch { import string _tmp $moduleparam } ] } {
        return
    }
    define _tmp enabletimewindow $enabletimewindow
    export string _tmp moduleparam -minimal
    regsub -all "\n" $moduleparam "; " moduleparam
    undefine _tmp

    set modinfo [ schMakeModuleInfo $moduleparam ]
    schSyncModuleInfo $modspec $modinfo
}

#---------------------------------------------------------------
# proc schSyncLoadSchedule
#
# this procedure used to sync the slice "value" that contain the
# module load schedule information when add, update or remove 
# those information
#---------------------------------------------------------------
proc schSyncLoadSchedule {} {

    ddl print trace "schSyncLoadSchedule: [ toe_name -full ]\n"

    syncSlice value
}

#---------------------------------------------------------------
# proc schSyncModuleInfo
#
# this procedure used to sync the moduleInfo in
# 1. base.schedule.scheduleTable
# 2. module slice in base.mibman.modules node
# 3. param slice in module node
#---------------------------------------------------------------
proc schSyncModuleInfo { modspec modinfo } {

    ddl print trace "schSyncModuleInfo $modspec $modinfo : [ toe_name -full ]\n"

    # sync param slice in module node from base.schedule.scheduleTable
    set newmodinfo [ schGetLoadedModuleInfo $modspec ]
    if { $newmodinfo != "" } {
        set moduleurl  [ lindex $modinfo 0 ]
        set modparam   [ lindex $modinfo 3 ]
        if { [ catch { locate $moduleurl } ] != 1 } {
            set modulenode [ locate $moduleurl ]
            if { $modulenode != "" } {
                toe_send $modulenode undefine param
                toe_send $modulenode import string param $modparam
                toe_send $modulenode define param moduleSpec $modspec
            }
        }

        # sync module slice in base.mibman.modules node from base.schedule.scheduleTable
        set modulesid [ locate .iso*base.mibman.modules ]
        toe_send $modulesid define module $modspec $modinfo
    }

    # sync value slice in scheduleTable.scheduleEntry
    set isLoadScheduled [ schGetModuleLoadScheduleState $modspec ]
    if { $isLoadScheduled == "SCHEDULED" } {
        schSetScheduleInfo $modspec $modinfo
    }
}

#---------------------------------------------------------------------
# proc schSetEnableSchedule
#
# this procedure used to add or update the enable schedule information
#
# Input: modpsec - module spec
#        enabletimewindow - timewindow type for enable module
#---------------------------------------------------------------------
proc schSetEnableSchedule { modspec enabletimewindow } {

    ddl print trace "schSetEnableSchedule: $modspec $enabletimewindow [ toe_name -full ]\n"

    if { $modspec != "" } {
        set modinfo [ schGetLoadedModuleInfo $modspec ]
        if { $modinfo != "" } {
            set moduleurl [ lindex $modinfo 0 ]
            if { [ catch { locate $moduleurl } ] != 1 } {
                set modulenode [ locate $moduleurl ]
                toe_send $modulenode setModuleActiveTimeWindow $enabletimewindow
                refreshValueAndTrap
                return
            }
        }
        schSyncEnableSchedule $modspec ""
        refreshValueAndTrap
    }
}

#----------------------------------------------------------------------------
# proc schSetLoadSchedule
#
# this procedure used to add or update the load schedule information
#
# Input: modpsec - module spec
#        loadtimewindow - timewindow type for load module
#        loadstate - UNLOAD, MANUAL-LOAD, SCHEDULED-LOAD and SCHEDULED-UNLOAD
#        info - module parameters
#----------------------------------------------------------------------------
proc schSetLoadSchedule { modspec loadtimewindow loadstate info } {

    ddl print trace "schSetLoadSchedule: $modspec $loadtimewindow $loadstate $info [ toe_name -full ]\n"

    if { $modspec != "" } {
        define value ${modspec}Window $loadtimewindow
        define value ${modspec}State  $loadstate
        define value ${modspec}Info   $info
        schSyncLoadSchedule
        refreshValueAndTrap
    }
}

#-------------------------------------------------------------------------
# proc schGetEnableTimeWindow
#
# this procedure used to get the enabletimewindow of the scheduled modspec
#
# Usage: schGetEnableTimeWindow modspec
# Output: "" means that the modspec is not scheduled
#-------------------------------------------------------------------------
proc schGetEnableTimeWindow { modspec } {

    ddl print trace "schGetEnableTimeWindow: $modspec [ toe_name -full ]\n"

    if { $modspec != "" } {
        set modinfo [ schGetLoadedModuleInfo $modspec ]
        if { $modinfo == "" } {
            return ""
        }

        set moduleurl [ lindex $modinfo 0 ]
        if { [ catch { locate $moduleurl } ] != 1 } {
            set modulenode [ locate $moduleurl ]
            return [ toe_send $modulenode getModuleActiveTimeWindow ]
        }
    }

    return ""
}

#-----------------------------------------------------------------------
# proc schGetLoadTimeWindow
#
# this procedure used to get the loadtimewindow of the scheduled modspec
#
# Usage: schGetLoadTimeWindow modspec
# Output: "" means that the modspec is not scheduled
#-----------------------------------------------------------------------
proc schGetLoadTimeWindow { modspec } {

    ddl print trace "schGetLoadTimeWindow: $modspec [ toe_name -full ]\n"

    return [ lookup -d "" value ${modspec}Window ]
}

#-------------------------------------------------------------------
# proc schGetEnableState
#
# this procedure used to get the enableload of the scheduled modspec
#
# Usage: schGetEnableState modspec
# Output: SCHEDULED-ON, DISABLED, SCHEDULED-OFF, MANUAL-ON" and ""
#         "" means that the modspec is not scheduled
#-------------------------------------------------------------------
proc schGetEnableState { modspec } {

    ddl print trace "schGetEnableState: $modspec [ toe_name -full ]\n"

    if { $modspec != "" } {
        set modinfo [ schGetLoadedModuleInfo $modspec ]
        if { $modinfo == "" } {
            return ""
        }

        set moduleurl [ lindex $modinfo 0 ]
        if { [ catch { locate $moduleurl } ] != 1 } {
            set modulenode [ locate $moduleurl ]
            return [ toe_send $modulenode getCurrentModuleState ]
        }
    }

    return ""
}

#--------------------------------------------------------------------------
# proc schGetLoadState
#
# this procedure used to get the current loadstate of the scheduled modspec
#
# Usage: schGetScheduleState modspec
# Output: UNLOAD, MANUAL-LOAD, SCHEDULED-LOAD, SCHEDULED-UNLOAD and ""
#         "" means that the modspec is not scheduled
#--------------------------------------------------------------------------
proc schGetLoadState { modspec } {

    ddl print trace "schGetLoadState: $modspec [ toe_name -full ]\n"

    return [ lookup -d "" value ${modspec}State ]
}

#---------------------------------------------------------------------------
# proc schGetScheduleInfo
#
# this procedure used to get the loading information of the schedule modpsec
#
# Usage: schGetScheduleInfo modpsec
# Output: "" means that the modspec is not scheduled
#---------------------------------------------------------------------------
proc schGetScheduleInfo { modspec } {

    ddl print trace "schGetScheduleInfo: $modspec [ toe_name -full ]\n"

    return [ lookup -d "" value ${modspec}Info ]
}

#------------------------------------------------------------------------
# proc schGetScheduleList
#
# this procedure used to get the a string that contain the all scheduling
# information of the specified module or all modules
#
# Usage: schGetScheduleList modspec
#        if modspec is null, get all modules, this case used by the
#        refreshcommand in schedule.scheduleEntry
#------------------------------------------------------------------------
proc schGetScheduleList { modspec } {

    ddl print trace "schGetScheduleList: $modspec [ toe_name -full ]\n"

    set result ""

    if { $modspec != "" } {
        lappend result $modspec
        lappend result [ schGetEnableTimeWindow $modspec ]
        lappend result [ schGetEnableState $modspec ]
        lappend result [ schGetLoadTimeWindow $modspec ]
        lappend result [ schGetLoadState $modspec ]
        lappend result [ schGetScheduleInfo $modspec ]
    } else {
        set modspec ""
        set loadschmodlist ""
        set loadedmodlist ""

        sliceforeach key keyvalue value {
            if { [ regsub \[.\]*Window$ $key "" modspec ] } {
                lappend loadschmodlist $modspec
                lappend result $modspec
                lappend result [ schGetEnableTimeWindow $modspec ]
                lappend result [ schGetEnableState $modspec ]
                lappend result [ schGetLoadTimeWindow $modspec ]
                lappend result [ schGetLoadState $modspec ]
                lappend result [ schGetScheduleInfo $modspec ]
            }
        }

        set module_toeid [ locate .iso*base.mibman.modules ]
        toe_send $module_toeid sliceforeach key keyval module { lappend loadedmodlist $key }

        foreach modspec $loadedmodlist {
            if { [ lsearch $loadschmodlist $modspec ] == -1 } {
                if { [ schGetEnableTimeWindow $modspec ] != "" } {
                    lappend result $modspec
                    lappend result [ schGetEnableTimeWindow $modspec ]
                    lappend result [ schGetEnableState $modspec ]
                    lappend result [ schGetLoadTimeWindow $modspec ]
                    lappend result [ schGetLoadState $modspec ]
                    lappend result [ schGetLoadedModuleInfo $modspec ]
                }
            }
        }
    }

    return $result
}

#----------------------------------------------------------------------
# proc schGetLoadScheduleModules
#
# this procedure used to get the name of the all load scheduled modules
#
# Usage: schGetLoadScheduleModules
#----------------------------------------------------------------------
proc schGetLoadScheduleModules {} {

    ddl print trace "schGetLoadScheduleModules: [ toe_name -full ]\n"

    set result ""

    sliceforeach key keyvalue value {
        if { [ regsub \[.\]*Window$ $key "" modspec ] } {
            lappend result $modspec
        }
    }

    return $result
}

#------------------------------------------------------------------------
# proc schRemoveEnableSchedule
#
# this procedure used to remove the schedule from the enable schedule table
#
# Usage: schRemoveLoadSchedule modspec
#------------------------------------------------------------------------
proc schRemoveEnableSchedule { modspec } {

    ddl print trace "schRemoveEnableSchedule: $modspec [ toe_name -full ]\n"

    if { $modspec != "" } {
        set modinfo [ schGetLoadedModuleInfo $modspec ]
        if { $modinfo == "" } {
            return
        }

        set moduleurl [ lindex $modinfo 0 ]
        if { [ catch { locate $moduleurl } ] != 1 } {
            set modulenode [ locate $moduleurl ]
            toe_send $modulenode setModuleActiveTimeWindow ""
            refreshValueAndTrap
        }
    }
}

#------------------------------------------------------------------------
# proc schRemoveLoadSchedule
#
# this procedure used to remove the schedule from the load schedule table
#
# Usage: schRemoveLoadSchedule modspec
#------------------------------------------------------------------------
proc schRemoveLoadSchedule { modspec } {

    ddl print trace "schRemoveLoadSchedule: $modspec [ toe_name -full ]\n"

    undefine value ${modspec}Window
    undefine value ${modspec}State
    undefine value ${modspec}Info

    cancelAttributeTimeWindow $modspec

    schSyncLoadSchedule

    refreshValueAndTrap
}

#----------------------------------------------------------------------------------
# proc schAddScheduleRequest
#
# this procedure used to add or update loaded and enable scheduled module into the
# schedule table. this procedure call schSetLoadSchedule and schSetEnableSchedule
# to add and update a scheduled module first, then call refreshValueAndTrap to 
# refresh the schedule table
#
# Output: addok if add or update successful, or addfail
#----------------------------------------------------------------------------------
proc schAddScheduleRequest { modspec enabletimewindow loadtimewindow loadstate info } {

    ddl print trace "schAddScheduleRequest: {$modspec} {$enabletimewindow} {$loadtimewindow} {$loadstate} {$info} [ toe_name -full ]\n"

    if { $modspec == "" } { 
        return [ list "addfail" ] 
    }

    if { $enabletimewindow != "" } {
        schSetEnableSchedule $modspec $enabletimewindow
    }

    if { $loadtimewindow != "" } {
        schSetLoadSchedule $modspec $loadtimewindow $loadstate $info
    }
    return [ list "addok" ]
}

#---------------------------------------------------------------------------
# proc schRemoveLoadScheduleRequest
#
# Usage: schRemoveLoadScheduleRequest modspec
#---------------------------------------------------------------------------
proc schRemoveLoadScheduleRequest { modspec } {

    ddl print trace "schRemoveLoadScheduleRequest: $modspec [ toe_name -full ]\n"

    if { [ ilookup -d "" value ${modspec}Window ] != "" } {
        schRemoveLoadSchedule $modspec
    }
}

#----------------------------------------------------------------------------------
# proc schCheckLoadTimeWindow
#
# this procedure used to check whether the current time is in the active timewindow
#
# Usage: set isInTimeWindow [ schCheckLoadTimeWindow ]
# Input: none
# Output: 1 - if is within active time window
#         0 - if is *not* within active time window
# NOTE:   If there is any problem, "1" is returned
#----------------------------------------------------------------------------------
proc schCheckLoadTimeWindow { modspec } {

    ddl print trace "schCheckLoadTimeWindow: $modspec [ toe_name -full ]\n"

    return [ isTrueAttributeTimeWindow $modspec ]
}

#-----------------------------------
# proc schLoadModule
#
# this procedure used to load module
#-----------------------------------
proc schLoadModule { whichmodule } {

    ddl print trace "schLoadModule: $whichmodule [ toe_name -full ]\n"

    if { $whichmodule == "" } { return [ list "loadfail" ] }
    set modinfo [ schGetScheduleInfo $whichmodule ]
    set parameters [ lindex $modinfo 3 ]
    if { $parameters == "" } { return [ list "loadfail" ] }

    set modulesid [ locate .iso*mibman.modules ]
    set result [ toe_send $modulesid quickLoad $parameters "" ]

    refreshValueAndTrap
    return $result
}

#---------------------------------------
# proc schUnloadModule
#
# this procedure used to unload a module
#---------------------------------------
proc schUnloadModule { modspec } {

    ddl print trace "schUnloadModule: $modspec [ toe_name -full ]\n"

    schSyncEnableSchedule $modspec ""

    set modulesid [ locate .iso*base.mibman.modules ]
    set result [ toe_send $modulesid unloadModule $modspec ]

    refreshValueAndTrap
    return $result
}

#--------------------------------------------------
# proc schRefreshSchedule
#
# this procedure used to refresh the schedule table
#--------------------------------------------------
proc schRefreshSchedule {} {

    ddl print trace "schRefreshSchedule: [ toe_name -full ]\n"

    set result [ schGetScheduleList "" ]
    if { $result == "" } {
        setAttribute refreshInterval 0
        if {[existsAttributeTimer refresh]} {
            resetAttributeTimer refresh refreshValue ""
        }
        undefine value refreshInterval
    } else {
        setAttribute refreshInterval 600
        if {[existsAttributeTimer refresh]} {
            resetAttributeTimer refresh refreshValue ""
        }
        undefine value refreshInterval
    }

    return  $result
}

#----------------------------------------------------------------
# proc schGetModuleLoadState
#
# this procedure used to get the current load state of the module
#
# Usage: schGetModuleLoadState modspec
# Output: LOAD or UNLOAD
#----------------------------------------------------------------
proc schGetModuleLoadState { modspec } {

    ddl print trace "schGetModuleLoadState: $modspec [ toe_name -full ]\n"

    set modulesid [ locate .iso*base.mibman.modules ]
    set modinfo [ toe_send $modulesid ilookup -d "" module $modspec ]
    set modparams [ lindex $modinfo 3 ]

    if { $modinfo == "" } {
        return "UNLOAD"
    }
    return "LOAD"
}

#-------------------------------------------------------------------------
# proc schGetModuleLoadScheduleState
#
# this procedure used to get the current load schedule state of the module
#
# Usage: schGetModuleLoadScheduleState modspec
# Output: SCHEDULED or UNSCHEDULED
#-------------------------------------------------------------------------
proc schGetModuleLoadScheduleState { modspec } {

    ddl print trace "schGetModuleLoadScheduleState: $modspec [ toe_name -full ]\n"

    set timewindow [ lookup -d "" value ${modspec}Window ]

    if { $timewindow == "" } {
        return "UNSCHEDULED"
    }
    return "SCHEDULED"
}

#---------------------------------------------------------
# proc schLoadScheduleCallback
#
# this procedure used to set the loadtimewindow callback
#
# Usage: schLoadScheduleCallback reason modspec
# Input: reason - turnon: into the active time window
#                 turnoff: out from the active time window
#        modspec - the specified scheduled module
#---------------------------------------------------------
proc schLoadScheduleCallback { reason modspec } {

    ddl print trace "schLoadScheduleCallback: CALLBACK $reason $modspec [ toe_name -full ]\n"

    set loadscheduleState [ schGetModuleLoadScheduleState $modspec ]
    if { $loadscheduleState != "SCHEDULED" } {
        ddl print trace "schLoadScheduleCallback: module $modspec is not in load schedule table\n"
        return
    }

    set isInTimeWindow [ schCheckLoadTimeWindow $modspec ]
    set currentState [ schGetLoadState $modspec ]
    ddl print trace "schLoadScheduleCallback: module is $modspec, reason is $reason, isInTimeWindow is $isInTimeWindow and the current state is $currentState\n"

    #
    # Now look at our current state, and everything else we know.
    # Based on that, decide what our new state should be.
    #
    # Here's the algorithm:
    #
    # (1) If the current state of the module is UNLOAD, keep
    #     its state
    # (2) Otherwise, if we are being called to "turn on", go
    #     to the "SCHEDULED-LOAD" state
    # (3) Otherwise, if we are being called to "turn off", go
    #     to the "SCHEDULED-UNLOAD" state
    # (4) If we are being called just to check, then look at the
    #     module schedule time window. Go to scheduled off or on as
    #     appropriate -- with *one* exception. And that exception is:
    #     if we are in "MANUAL-LOAD" (which means a user manually
    #     loaded it when the module was scheduled unload), do not
    #     change back to SCHEDULED-UNLOAD.
    #
    set newState $currentState
    if { $currentState == "UNLOAD" } {
        set newState "UNLOAD"
    } else {
        switch $reason \
            {turnon} {
                set newState SCHEDULED-LOAD
                ddl print info "schScheduleCallback: module $modspec set to $newState as scheduled\n"
            } \
            {turnoff} {
                set newState SCHEDULED-UNLOAD
                ddl print info "schScheduleCallback: module $modspec set to $newState as scheduled\n"
            } \
            default {
                if { $isInTimeWindow == 1 || $currentState == "MANUAL-LOAD" } {
                    set newState "SCHEDULED-LOAD"
                } else {
                    set newState "SCHEDULED-UNLOAD"
                }
            }
    }

    if { $newState == $currentState } {
        ddl print info "schLoadScheduleCallback: $modspec remains the state $newState\n"
    } else {
        ddl print trace "schLoadScheduleCallback: change $modspec state from $currentState to $newState\n"
        schSetModuleLoadScheduleState $newState $modspec
    }
}

#----------------------------------------------------------------------------------
# proc schSetModuleLoadScheduleState
#
# this procedure used to update the current load state of the load scheduled module
#
# Usage: schSetModuleLoadScheduleState state modspec
# Input: state - UNLOAD, MANUAL-LOAD, SCHEDULED-LOAD or SCHEDULED-UNLOAD
#        modspec - the specified shceduled module
#----------------------------------------------------------------------------------
proc schSetModuleLoadScheduleState { state modspec } {

    ddl print trace "schLoadSetModuleScheduleState: $state $modspec [ toe_name -full ]\n"

    set result ""

    set isScheduled [ schGetModuleLoadScheduleState $modspec ]
    if { $isScheduled == "UNSCHEDULED" } {
        ddl print trace "schSetModuleLoadScheduleState: $modspec is not in load schedule table\n"
        return $result
    }

    switch $state \
        {UNLOAD} {
            ddl print trace "schSetLoadScheduleModuleState: user UNLOAD module $modspec\n"
            set result [ schUnloadModule $modspec ]
            ddl print trace "schSetLoadScheduleModuleState: UNLOAD $modspec is $result\n"
        } \
        {MANUAL-LOAD} {
            ddl print trace "schSetLoadScheduleModuleState: user LOAD module $modspec\n"
            set result [ schLoadModule $modspec ]
            if { $result == "loadfail" } {
                set state "SCHEDULED-UNLOAD"
            }
            ddl print trace "schSetLoadScheduleModuleState: LOAD $modspec is $result\n"
        } \
        {SCHEDULED-LOAD} {
            ddl print trace "schSetLoadScheduleModuleState: LOAD module $modspec as scheduled\n"
            set result [ schLoadModule $modspec ]
            if { $result == "loadfail" } {
                set state "SCHEDULED-UNLOAD"
                ddl print error "schLoadModule: LOAD $modspec failed\n"
                ddl print error "schLoadModule: Set $modspec state to SCHEDULED-UNLOAD\n"
            }
            ddl print trace "schSetLoadScheduleModuleState: LOAD $modspec is $result\n"
        } \
        {SCHEDULED-UNLOAD} {
            ddl print trace "schSetLoadScheduleModuleState: UNLOAD module $modspec as scheduled\n"
            set result [ schUnloadModule $modspec ]
            if { $result == "unloadfail" } {
               ddl print error "schUnloadModule: UNLOAD $modspec failed\n"
            }
            ddl print trace "schSetLoadScheduleModuleState: UNLOAD $modspec is $result\n"
        }

    schSetLoadState $modspec $state

    refreshStatus
    return $result
}

#----------------------------------------
# proc schSetLoadScheduleCallback
#
# this procedure used to set the callback
#----------------------------------------
proc schSetLoadScheduleCallback { modspec } {

    ddl print trace "schSetLoadScheduleCallback: $modspec [ toe_name -full ]\n"

    set scheduleState [ schGetModuleLoadScheduleState $modspec ]
    if { $scheduleState != "SCHEDULED" } { reurn [ list "setfail" ] }

    resetAttributeTimeWindow $modspec "schLoadScheduleCallback turnon $modspec" "schLoadScheduleCallback turnoff $modspec" "schLoadScheduleCallback check $modspec"
    ddl print trace "schSetLoadScheduleCallback: module $modspec callback timers have been set now\n"
    if { [ schCheckLoadTimeWindow $modspec ] == 1 } {
        schLoadScheduleCallback turnon $modspec
    } else {
        schLoadScheduleCallback turnoff $modspec
    }

    return [ list "setok" ]
}

#----------------------------------------------------------------------------------------
# proc schMakeModuleInfo
#
# this procedure used to make the module loading information from the module parameters
#
# Usage: schMakeModuleInfo modparams
# Input: parameters - module parameters
# Output: module loading information
#----------------------------------------------------------------------------------------
proc schMakeModuleInfo { parameters } {

    ddl print trace "schMakeModuleInfo: $parameters [ toe_name -full ]\n"

    #
    # place all parameters in temporary dictionary
    #
    if { [ catch { import string _tmp $parameters } ] } { return "" }
    set module          [ lookup -d "" _tmp module ]
    set context         [ lookup -d "" _tmp instance ]
    set location        [ lookup -d "" _tmp location ]
    set enterprise      [ lookup -d "" _tmp enterprise ]
    set prefix          [ lookup -d "localApplication" _tmp moduleType ]
    set targetHost      [ lookup -d "" _tmp targetHost ]
    set validateHost    [ lookup -d "" _tmp validateHost ]
    set reload          [ ilookup -d "" _tmp _RELOAD ]
    if { $location == "" } {
        set location    [ ilookup -d "" _tmp mibLocation ]
    }
    if { $context == "" } {
       set context $targetHost
    }
    regsub -all {[^0-9a-zA-Z]} $context {_} context
    define _tmp instance $context
    set location [ toe_send [ locate .iso*mibman.modules ] getLocation $context $enterprise $prefix $module $location ]
    set modspec $module
    if { $context != "" } {
        append modspec "+" $context
    }
    export string _tmp parameters -minimal
    undefine _tmp
    regsub -all "\n" $parameters "; " parameters
    set modinfo [ list $location $enterprise $module $parameters ]

    return $modinfo
}

#-----------------------------------------------------------------
# proc schInitLoadScheduleCallback
#
# this procedure used to init the callback only when agent startup
#-----------------------------------------------------------------
proc schInitLoadScheduleCallback { modspec } {

    ddl print trace "schInitLoadScheduleCallback: $modspec [ toe_name -full ]\n"

    set scheduleState [ schGetModuleLoadScheduleState $modspec ]
    if { $scheduleState != "SCHEDULED" } { reurn [ list "initfail" ] }

    resetAttributeTimeWindow $modspec "schLoadScheduleCallback turnon $modspec" "schLoadScheduleCallback turnoff $modspec" "schLoadScheduleCallback check $modspec"

    set modulesid [ locate .iso*mibman.modules ]
    set modinfo [ schGetScheduleInfo $modspec ]
    if { [ schCheckLoadTimeWindow $modspec ] == 1 } {
        toe_send $modulesid define module $modspec $modinfo
        toe_send $modulesid syncModuleSlice

        schSetLoadState $modspec "SCHEDULED-LOAD"
        ddl print trace "schInitLoadScheduleCallback: add module $modspec into the module list to loading\n"
    } else {
        toe_send $modulesid undefine module $modspec
        toe_send $modulesid syncModuleSlice

        schSetLoadState $modspec "SCHEDULED-UNLOAD"
        ddl print trace "schInitLoadScheduleCallback: remove $modspec from the module list to unloading\n"
    }

    return [ list "setok" ]
}

#-------------------------------------------------------
# proc schModuleScheduling
#
# this procedure used to deal with module scheduling
#
# Usage: schModuleScheduling request modinfo pdu
# Input: request - load, loadupdate, unload and ""
# Output: loadok, loadfail, unloadok, unloadfail, and ""
#-------------------------------------------------------
proc schModuleScheduling { request modinfo pdu } {

    ddl print trace "schModuleScheduling: $request $modinfo $pdu [ toe_name -full ]\n"

    #
    # check the module info
    #----------------------
    if { $modinfo == "" } {
        ddl print trace "schModuleScheduling: invalid module information\n"
        return ""
    }

    #
    # get module parameters
    #----------------------
    if { $request != "loadupdate" && $request != "unload" } {
        set paramsTmp $modinfo
        set modinfo [ schMakeModuleInfo $paramsTmp ]
    }
    set parameters [ lindex $modinfo 3 ]

    #
    # get module, context, modspec and timewindow from the module parameters
    #-----------------------------------------------------------------------
    if { [ catch { import string _tmp $parameters } ] } { 
        return "" 
    }
    set module [ lookup -d "" _tmp module ]
    set context [ lookup -d "" _tmp instance ]
    set etimewindow [ lookup -d "" _tmp enabletimewindow ]
    set ltimewindow [ lookup -d "" _tmp loadtimewindow ]
    if { $module == "" } { 
        return "" 
    }
    set modspec $module
    if { $context != "" } { 
        append modspec "+" $context 
    }
    undefine _tmp

    #
    # check timewindow
    #------------------
    set enabletimewindow $etimewindow
    if { $etimewindow != "" } {
        set enabletimewindow [ string trim $etimewindow ]
        set validate [ windowValidate $enabletimewindow ]
        if { $validate != ""} {
            ddl print info "schModuleScheduling: invalid enabletimewindow {$enabletimewindow}. Problem is $validate \n"
            error "$validate"
        }
    }
    set loadtimewindow $ltimewindow
    if { $ltimewindow != "" } {
        set loadtimewindow [ string trim $ltimewindow ]
        set validate [ windowValidate $loadtimewindow ]
        if { $validate != ""} {
            ddl print info "schModuleScheduling: invalid loadtimewindow {$loadtimewindow}. Problem is $validate \n"
            error "$validate"
        }
    }

    #
    # get the loaded and loadscheduled state
    # loaded state is UNLOAD or LOAD
    # loadscheduled state is SCHEDULED or UNSCHEDULED
    #------------------------------------------------
    set isLoaded [ schGetModuleLoadState $modspec ]
    set isLoadScheduled [ schGetModuleLoadScheduleState $modspec ]

    ddl print trace "schModuleScheduling: MODULE is {$modspec}, REQUEST is {$request}, PARAMS is {$parameters}\n"

    set result ""
    switch $request \
    {schedulerequest} {

        ddl print info "schModuleScheduling: SCHEDULEREQUEST request for $modspec\n"

        set modulesid [ locate .iso*base.mibman.modules ]

        #
        # This case (schedule.scheduleRequest) deal with:
        #
        # if loadtimewindow is null
        #     if modeule is loadscheduled
        #         remove the loadschedule
        #     endif
        # else
        #     load/reload module 
        #     add or update the loadschedule
        #     set loadschedule callback
        # endif
        #------------------------------------------------
        if { $loadtimewindow == "" } {
            if { $isLoadScheduled == "SCHEDULED" } {
                ddl print info "schModuleScheduling(SCHEDULEREQUEST): remove the loadschedule of $modspec\n"
                schRemoveLoadScheduleRequest $modspec
                schSyncModuleInfo $modspec $modinfo
            }
        } else {
            set loadstate SCHEDULED-LOAD

            set result [ toe_send $modulesid quickLoad $parameters $pdu ]
            if { $result == "loadfail" } {
                ddl print trace "schModuleScheduling(SCHEDULEREQUEST): load module $modspec failed\n"
                set loadstate SCHEDULED-UNLOAD
                set modinfo [ lookup -d "" infotemp $modspec ]
            } else {
                set loadstate SCHEDULED-LOAD
                set modinfo [ schGetLoadedModuleInfo $modspec ]
            }

            schAddScheduleRequest $modspec "" $loadtimewindow $loadstate $modinfo
            schSyncModuleInfo $modspec $modinfo
            schSetLoadScheduleCallback $modspec
        }

        #schAddScheduleRequest $modspec $enabletimewindow "" "" ""
        schSetEnableSchedule $modspec $enabletimewindow

        return $result
    } \
    {load} {

        ddl print info "schModuleScheduling: LOAD request for $modspec\n"

        #
        # This case (loader node) ONLY deal with MANUAL-LOAD
        #
        # load module
        # if module is loadscheduled
        #     if load module failed
        #         set loadschedule state to SCHEDULED-UNLOAD
        #     else
        #         set loadschedule state to MANUAL-LOAD
        #     endif
        # endif
        #-----------------------------------------------
        set modulesid [ locate .iso*base.mibman.modules ]
        set result [ toe_send $modulesid quickLoad $parameters $pdu ]

        set modinfo [ schGetScheduleInfo $modspec ]
        if { $modinfo != "" } { schSyncModuleInfo $modspec $modinfo }

        set loadstate MANUAL-LOAD
        set isLoadScheduled [ schGetModuleLoadScheduleState $modspec ]
        if { $isLoadScheduled == "SCHEDULED" } {
            if { $result == "loadfail" } {
                ddl print trace "schModuleScheduling(LOAD): load module $modspec fail, set schedule state to SCHEDULED-UNLOAD\n"
                schSetLoadState $modspec "SCHEDULED-UNLOAD"
            } else {
                ddl print trace "schModuleScheduling(LOAD): load module $modspec fine, set schedule state to MANUAL-LOAD\n"
                schSetLoadState $modspec "MANUAL-LOAD"
            }
        } else {
            ddl print trace "schModuleScheduling(LOAD): load module $modspec without scheduling\n"
        }

        #schAddScheduleRequest $modspec $enabletimewindow "" "" ""
        schSetEnableSchedule $modspec $enabletimewindow

        return $result
    } \
    {unload} {

        ddl print info "schModuleScheduling: UNLOAD request for $modspec\n"

        if { $isLoadScheduled == "SCHEDULED" } {
            ddl print info "schModuleScheduling: user ask to unload $modspec\n"
            set result [ schSetModuleLoadScheduleState UNLOAD $modspec ]
        } else {
            set result [ schUnloadModule $modspec ]
        }

        return $result
    } \
    {loadupdate} {

        ddl print info "schModuleScheduling: LOADUPDATE request for $modspec\n"

        #
        # if loadtimewindow is null
        #     if module is loadscheduled
        #         remove the loadschedule
        #     endif
        # else
        #     if the new loadtimewindow is not equ the current loadtimewindow
        #         add or update the new loadschedule
        #         reset the loadtimewindow and callback
        #     endif
        # endif
        #--------------------------------------------------------------------
        if { $loadtimewindow == "" } {
            if { $isLoadScheduled == "SCHEDULED" } {
                ddl print info "schModuleScheduling: remove the loadschedule of $modspec\n"
                schRemoveLoadScheduleRequest $modspec
                schSyncModuleInfo $modspec $modinfo
            }
        } else {
            set currenttimewindow [ schGetLoadTimeWindow $modspec ]
            if { $loadtimewindow != $currenttimewindow } {
                ddl print info "schModuleScheduling: update the loadschedule and set callback\n"
                set loadstate SCHEDULED-LOAD
                schAddScheduleRequest $modspec "" $loadtimewindow $loadstate $modinfo
                schSyncModuleInfo $modspec $modinfo
                schSetLoadScheduleCallback $modspec
            }
        }

        schAddScheduleRequest $modspec $enabletimewindow "" "" ""

        return $result
    } \
    default {

        ddl print info "schModuleScheduling: unknown schedule request $request\n"
        return ""
    }
}

#--------------------------------------------------------
# proc schInitSchedule
#
# this procedure used to init schedule when agent startup
#--------------------------------------------------------
proc schInitSchedule {} {

    ddl print trace "schInitSchedule: [ toe_name -full ]\n"

    set modspec ""
    sliceforeach key keyvalue value {
        if { [ regsub \[.\]*Window$ $key "" modspec ] } {
            # reset the timer for time window
            schInitLoadScheduleCallback $modspec
        }
    }
}

#------------------------------------------------------------
# proc schNotify
#
# this procedure used to refresh schedule table and sent trap
#------------------------------------------------------------
proc schNotify {} {

    ddl print info "schNotify: [ toe_name -full ]\n"

    toe_send [ locate .iso*base.mibman.schedule.scheduleTable.scheduleEntry ] refreshValueAndTrap
}

#------------------------------------------------------------
# proc schSetLoadState
#
# this procedure used to set the load state in schedule table
#------------------------------------------------------------
proc schSetLoadState { modspec loadstate } {

    ddl print trace "schSetLoadState: $modspec $loadstate [toe_name -full ]\n"

    define value ${modspec}State $loadstate
    schSyncLoadSchedule
    refreshValueAndTrap
}

#------------------------------------------------------------
# proc schSetScheduleInfo
#
# this procedure used to set the info in schedule table
#------------------------------------------------------------
proc schSetScheduleInfo { modspec info } {

    ddl print trace "schSetScheduleInfo: $modspec $info [toe_name -full ]\n"

    define value ${modspec}Info $info
    schSyncLoadSchedule
    refreshValueAndTrap
}

#-------------------------------------------------------
#proc checkValidLocation 
#
#this procedure replaces /sun/products with /sun/prod
#-----------------------------------------------------------


proc checkValidLocation { spec } {

	if {  [ expr [ regsub /sun/products $spec /sun/prod newspec ] == 1 ] } {
    	   return $newspec 
	} else { return $spec }
}


