Skip to main content

Writing a PSLang script

PSLang scripts are broken down into functions. Each function must include a name, function parameters (if applicable), and a sequence of statements to execute:

 function <FunctionName>( <p1>, <p2>, … )

   {

   <statement>

   <statement>

   …

   }

The scripts can contain a mix of user-defined functions and built-in functions which are used to pass information to the agent.

Warning

Remember to remove all debugging statements and to review all statements that will create logs before using the script in a production environment. Ensure that no sensitive information is being captured in the logs.

User-defined functions

In order for agtdos , agttelnet , or agtssh to perform an agent operation, you must implement the corresponding function in your script. Write these functions to provide interaction details between Bravura Security Fabric and the target.

The agents use the following functions:

  • list( const $wantGroups, const $wantAttributes) Lists accounts on the target system, and the attributes for each account.

    The parameters passed to the function are as follows:

    • $wantGroups – determines if this function should retrieve group membership.

      If the value of $wantGroups is a non-zero value, write your function to retrieve the group(s) to which each account belongs while listing accounts.

    • $wantAttributes – determines if this function should retrieve attributes for each account.

      If the value of $wantAttributes is a non-zero value, write your function to list the attributes for each account.

    If both $wantGroups and $wantAttributes are 0 , write your function to list accounts on the target system only.

    See listmembermethod below for more information about listing group membership.

  • listgroups( const $wantMembers) Lists groups.

    The parameters passed to the function are as follows:

    • $wantMembers – determines if this function should retrieve group membership.

      If the value of $wantMembers is non-zero, write your function to retrieve the account(s) that belong to each group while listing groups.

      If this value of $wantMembers is 0, write your function to list groups only.

    See listmembermethod below for more information about listing group membership.

  • listresource( const $resourceType) Lists resources for Bravura Privilege ’s infrastructure auto discovery feature. The parameters passed to the function are as follows:

    • $resourceType defines which type of resource to list.

      Depending on target system configuration, listresource() will be called multiple times each with a different resourceType value. The values that resourceType can take on are:

      • ls_compsvr – list server computer objects.

      • ls_admmember – list members of administrative groups.

      • ls_taskacct – list scheduled task accounts.

      • ls_comacct – list COM object accounts.

      • ls_iisacct – list IIS virtual directory accounts.

      • ls_scmacct – list Service Control Manager accounts.

  • movecontext( const $info ) Moves an account to a new context or location on a context-sensitive target. Supported in Bravura Identity and Bravura Privilege .

  • isenabled( const $info ) Checks if an account is enabled. Supported in Bravura Pass and Bravura Privilege .

  • islocked( const $info ) Checks if an account is locked. Supported in Bravura Pass and Bravura Privilege .

  • ispwexpired( const $info ) Checks if an account’s password is expired. Supported in Bravura Pass and Bravura Privilege .

  • isacctexpired( const $info ) Checks if an account is expired. Supported in Bravura Pass and Bravura Privilege .

  • disable( const $info ) Disables an account. Supported in Bravura Identity and Bravura Privilege .

    Most systems differentiate between disabled and intruder-locked accounts.

  • enable( const $info ) Enables an account. Supported in Bravura Identity and Bravura Privilege .

  • lock( const $info ) Locks an account (sets the intruder lockout). Supported in Bravura Pass and Bravura Privilege .

    Most system differentiate between intruder-locked and disabled accounts.

  • unlock( const $info ) Unlocks an account (clears the intruder lockout). Supported in Bravura Pass and Bravura Privilege .

  • expirepw( const $info ) Expires an account’s password. Supported in Bravura Pass and Bravura Privilege .

  • unexpirepw( const $info ) Unexpires an account’s password. Supported in Bravura Pass and Bravura Privilege .

  • expireacct( const $info ) Expires an account. Supported in Bravura Pass and Bravura Privilege .

  • unexpireacct( const $info ) Unexpires an account. Supported in Bravura Pass and Bravura Privilege .

  • create( const $info ) Creates a new account on the target system. This operation creates the account (possibly using a template for some attribute values), then sets other attribute values – including the password for the new account. Supported in Bravura Identity and Bravura Privilege .

    This function should make use of the target system administrator credentials.

  • delete( const $info ) Deletes an existing account on the target system. The typical behavior is to first ensure that the account being deleted exists. Supported in Bravura Identity and Bravura Privilege .

    You may also wish to include instructions to delete any files or objects associated with the account.

  • verify( const $info ) Checks if a given password is the correct, current password for an account. If the application supports the concept of intruder lockout and the verification fails, the intruder lockout counter is incremented. Supported in Bravura Identity and Bravura Privilege .

  • admin_verify( const $info ) Checks if a given password is the correct, current password for an account without triggering an intruder lockout if the password is not correct. Supported in Bravura Pass and Bravura Privilege .

  • verifyreset( const $info ) Verifies if the account’s password matches the new password, and if the verification fails, administratively sets it to the new password. If the verification succeeds, then the reset is not necessary, and the operation returns success. Supported in Bravura Pass and Bravura Privilege .

  • reset( const $info ) Administratively resets an account’s password to a new value. If the application supports the concept of intruder lockout, then the intruder lockout counter is cleared and the account unlocked. If the application supports the concept of password expiry, then the expiry date is set according to the expiry policy of the application. Disabled accounts will remain disabled. Supported in Bravura Pass and Bravura Privilege .

  • change( const $info ) Changes the password for an account, from a known current value to a desired new value. If the application supports the concept of intruder lockout, then the intruder lockout counter is cleared and the account unlocked. If the application supports the concept of password expiry, then the expiry date is set according to the expiry policy of the application. Supported in Bravura Pass and Bravura Privilege .

  • resetexpirepw( const $info ) Administratively resets an account’s password to a new value and expires the account’s new password, so that the user is forced to change his password the next time he logs in. Supported in Bravura Pass and Bravura Privilege .

  • groupuseradd( const $info ) Adds an account to a group. Supported in Bravura Identity and Bravura Privilege .

  • groupuserdelete( const $info ) Removes an account from a group. Supported in Bravura Identity and Bravura Privilege .

  • groupgroupadd( const $info ) Adds a group to a group. Supported in Bravura Identity and Bravura Privilege .

  • groupgroupdelete( const $info ) Removes a group from a group. Supported in Bravura Identity and Bravura Privilege .

  • update( const $info ) Updates attributes for an existing account. Supported in Bravura Identity and Bravura Privilege .

  • userattributes( const $info ) Lists attributes for a specified account. Supported in Bravura Identity and Bravura Privilege .

All functions are optional. To indicate success your function must return 0. To indicate failure, your function must return 1.

Warning

Functions which reset or verify passwords in scripts used by Bravura Privilege for password randomization must always report the result of the reset or verification accurately. Inaccurate status information may result in checked out passwords not working; additionally, if the credential used to manage the target system is being randomized, Bravura Privilege may invalidate its own managed system credential when an erroneous status is returned. Do not assume scripted password changes are always successful; always check return codes.

If your target does not support a particular operation (for example, if there is no intruder lockout mechanism), then omit the corresponding function from the script. If the agent cannot find a particular function in the script, the agent returns a message saying "Function [<operation>] not found in script".

The following additional functions (optional) do not represent actual agent operations, however they are also called directly by the agent:

  • connecttarget( const $cinfo ) called before the first operation is performed.

    This function must return 0 on success, or 1 on failure.

  • disconnecttarget( const $cinfo ) called after the last operation is performed.

    This function must return 0 on success, or 1 on failure.

  • listmembermethod() used to determine the supported method for listing group membership on the target system.

    If both methods are supported, select the method that is the most efficient. This function must return:

    • 0 - if the script cannot list group membership

    • 1 - if the script list accounts in a group while listing groups

    • 2 - if the script lists groups while listing accounts

    The calling program uses the result of listmembermethod, along with your target-configuration settings, to determine the value of $wantGroups and $wantMembers.

    For example, if the calling program is executed with the option to list group membership, and the result of listmembermethod is 1, the calling program executes the function: listgroups(1)

  • addressattrs () used to extend the address wizard by adding additional address line elements. It is only possible to add new address line elements, not remove or change existing ones. This is useful when creating a scripted target system with a scripted platform definition file.

    • If not defined, the address wizard behaves as normal.

    • From within this function the addAddressElement callback function adds a single address line element. The callback function can be called multiple times and takes the following form:

      addAddressElement(name, type, defaultValue, isRequired,restrictedValuesList, description, advanced);

      Where:

      • name: is the string name of the of the address line component.

      • type: is one of ($AddressTypeInt $AddressTypeString $AddressTypeBoolean $AddressTypeKvgroup $AddressTypeRestricted $AddressTypeScript $AddressTypeDir $AddressTypeFile).

      • defaultValue: string default value or empty if no default value.

      • isRequired: boolean, where 1=required and 0=optional.

      • restrictedValuesList: array of display/actual values for each restricted value.

      • description: Textual description of the element.

      • advanced: boolean, 1=display attribute in advanced section and 0=default

    An example is included in the sample agttelnet-racf.psl.

The following function is available to agtssh:

  • credentialoverride(inout $cinfo ) called before all other functions and used to override the adminid and adminpw credentials on the connection to the SSH target.

    • The function is optional, and is not called if it is not defined.

    • The function is called before all other functions.

    • The adminid, adminpw, host, and address elements are readable in the function.

    • The adminid and adminpw elements are settable within the credentialoverride function to other values. The return value for the function does not affect the commitment of the override values - agtssh always uses the returned values for adminid and adminpw for the connection.

    • The host, address, sysid, and syspw elements do not commit overridden values.

    • The adminid and adminpw elements are accessible via a transitional variable to other functions.

    • The variable $COMMON_SHELL can be set in the function to specify a custom shell.

Input parameters ($cinfo and $info)

The connecttarget() and disconnecttarget() functions each take one parameter, an associative array ($cinfo). Each element is a string. Elements of the array are:

  • address the target address.

  • adminid the target system administrator’s ID.

  • adminpw the target system administrator’s password.

  • sysid the target system administrator’s ID if the Is this an additional system password? checkbox is selected.

  • syspw the target system administrator’s password if the Is this an additional system password? checkbox is selected.

Most of the functions that relate to Bravura Security Fabric operations (for example, create(), enable(),reset(), change() ) also take one parameter, an associative array ($info). Each element is a string unless otherwise indicated. Elements of the array are:

  • userid the profile ID of the user.

  • shortid the short ID of the user.

  • fullname the full name of the user.

  • acctid the ID for the account that the user has on this system.

  • newpw the new password (for password reset and create operations).

  • oldpw the old password (for verify operations).

  • modeluid the template ID (for create operations).

  • groupid the group ID (for group* operations).

  • groupname the group description (for group* operations).

  • attributes a KVGroup containing information about attributes to set (for create and update operations)

    The general format of the KVGroup is as follows:

    "attributes" "" = {
      "DEFAULT-ACTION" = "COPY"
      "attribute" "givenName" = {
        "SEQUENCE" = "0"
        "GROUP"    = "0"
        "ACTION"   = "VALUE"
        "VALUE"    = "John"
       }
      "attribute" "sn" = {
        "SEQUENCE" = "0"
        "GROUP"    = "0"
        "ACTION"   = "VALUE"
        "VALUE"    = "Doe"
       }
     }
  • ConnectionCredential a KVGroup containing information about target administrator credentials and system credentials (for reset operations).

    The general format of the KVGroup is as follows:

    "ConnectionCredential" "" = {
      "AdminCredential" "" = {
        "admin1" = "{AES}@KK|dE]=GqX=zqJyTIgqL]XCyECV?AsAJ[O?vBJGpAaEGgfK"
        "admin2" = "{AES}e[LkiHEv?=iAGTD|ZA^cKCYJCb?[tByFDp_=e]EhWDkgHzmD"
       }
      "SysCredential" "" = {
        "sysadm1" = "{AES}fN=>rLOB?^gDICJfc@fYLB@@COGlv=H@IFc@EW?XBJTwFEXB"
       }
     }

Return codes

The return codes for user-defined functions (with the exception of listmembermethod()) for agtdos , agttelnet , and agtssh are as follows:

Code

Value

Description

ACSuccess

0

The operation was successful.

ACUnknownError

1

The operation failed with an unknown error.

ACOperationNotSupported

2

The operation is not supported by the target system.

ACNotConnected

3

Can be used in the connecttarget() function if a connection could not be established.

ACAlreadyConnected

4

Can be used in the connecttarget() function if an attempt is made to connect to a target that already has a connection.

ACInvalidServer

5

Can be used in the connecttarget() function if the target address is invalid.

ACObjectAlreadyExists

6

The object to create (for example, user or group) already exists.

ACInvalidUser

7

The user account is invalid or does not exist.

ACInvalidModelUser

8

The template account is invalid or does not exist.

ACUserNotInGroup

9

The user is not in the specified group.

ACTimeout

10

The operation timed out.

ACAccessDenied

11

The target system administrator could not logon due to a bad ID/password pair, account restrictions, and so on.

ACInvalidGroup

12

The group is invalid or does not exist.

ACUserAlreadyGroupMember

13

The user is already a member of the specified group.

ACVerifyFailed

14

The password could not be verified. Use this code ONLY when the specified password is not correct.

ACInitFailed

15

The operation failed to initialize.

ACLockFailed

16

The operation failed to get a lock on a file while reading or writing.

ACScriptError

17

The operation encountered an error in the script.

ACNotLicensed

18

The operation is not licensed.

ACLogFileFailure

19

Failed to write to the log file.

ACImplementerFailure

20

The implementor failed to perform the operation.

ACAdminLocked

21

The target system administrator account is locked.

ACInvalidPasswd

22

The password is invalid.

ACDelayLoadError

23

The operation encountered an error loading a dll or function point.

ACReadOnlyResource

24

The operation could not write to a resource because it was read only.

ACPluginAborted

25

The plugin failed.

ACPasswordLocked

26

The password is locked.

ACPasswordExpired

27

The password has expired.

ACAccountExpired

28

The account has expired.

ACAccountDisabled

29

The account is disabled.

ACOperationRolledback

30

The operation was rolled back.

ACOperationAborted

31

The operation was aborted.

ACOperationRollbackFailed

32

The rollback operation has failed.

ACEndOfFile

33

The operation has reached the end of file.

ACGroupAlreadyGroupMember

34

The group is already a group member.

ACGroupNotInGroup

35

Group is not a member of group.

ACNull

0xffff

A null code.

Built-in Functions

Apart from the standard PSLang built-in functions, there are several built-in agent functions available to the agents.

You call the following built-ins from within functions representing agent operations in order to communicate the results of the operation back to the agent:

  • agentError reports an error message.

  • agentWarning reports a warning message.

  • agentInfo reports an information message.

  • agentListUser is called once for each user during the list operation.

  • agentListGroup (or agentListGroupSID) is called once for each group during the list operation.

  • agentListAttribute is called once for each attribute during the list attribute, create, and update operations.

  • agentListResourceComputer is called once for each computer object during the list resource operation.

  • agentListResourceAccount is called once for each account object during the list resource operation.

  • agentIsEnabled tells the agent whether or not the given user is enabled.

  • agentIsLocked tells the agent whether or not the given user is locked out.

  • agentIsPassExpired tells the agent whether or not the given user’s password has expired.

  • agentIsAcctExpired tells the agent whether or not the given user’s account has expired.

  • agentLongId provides the long ID of the user during create, update, rename, or move context operations.

  • agentShortID provides the short ID of the user during create, update, rename, or move context operations.

  • agentGroups provides the list of groups to which a user belongs.

  • agentOutput can return IP information on reset using agentOutput. The keys are ip-address and dns-host-name. This information is used by Bravura Privilege to load current information when the reset occurs. This is included in the agtssh-simple.psl sample. By default it is disabled. It can be enabled with the $emit_info at the top of the script by setting it to non-zero.

See the PSLang Reference Manual (pslang.pdf) for more information about these built-in functions.

Global variables

In any agent script, input passed to the agent is stored in the $_inVars "hidden" global variable. You can declare and define additional global variables for use throughout the script.

Following is an example of the contents of the $_inVars KVGroup when performing a list operation:

   "" "" = {
     "address" = "agtdos.psl"
     "adminid" = "null"
     "adminpw" = "<encrypted password value>"
     "hostid" = "DOS"
     "listdbfilename" = "C:\\Program Files\\Hitachi ID\\IDM Suite\\default\\psconfig\\AGTDOS.db"
     "operation" = "listobj"
     "platformname" = "Win32 Console Script"
     "timeout" = "-1"
    "listattributes" "" = {
     }
   }

Built-in variables

The following built-in variables are available for use with PSLang scripts:

  • $KeyAccounts

    Value: "accounts" Description: List of accounts.

  • $KeyAcctID

    Value: "acctid" Description: Name of the target account to operate on.

  • $KeyAction

    Value: "ACTION" Description: Action to do to the attribute.

  • $KeyActionCopy

    Value: "COPY" Description: Action to do to the attribute.

  • $KeyActionIgnore

    Value: "IGNORE" Description: Action to do to the attribute.

  • $KeyActionReplace

    Value: "REPLACE" Description: Action to do to the attribute.

  • $KeyActionValue

    Value: "VALUE" Description: Value of the attribute.

  • $KeyAddress

    Value: "address" Description: The address line to use to connect to the target.

  • $KeyAddrComps

    Value: "addresscomponents" Description: Key to the sub-array containing the address elements already split.

  • $KeyAdminID

    Value: "adminid" Description: Name of the account to connect to target as.

  • $KeyAdminPW

    Value: "adminpw" Description: Password of the account to connect to the target with.

  • $KeyAttribute

    Value: "attribute" Description: Single account attribute details.

  • $KeyAttributes

    Value: "attributes" Description: List of account details.

  • $KeyAuthKey

    Value: "authkey" Description: SSH Authentication key attribute.

  • $KeyBoolean

    Value: "boolean" Description: true/false address attribute type

  • $KeyCAPath

    Value: "CApath" Description: Path to certificate file.

  • $KeyCAFile

    Value: "CAfile" Description: Certificate file name.

  • $KeyChildGroupID

    Value: "childgroupid"

  • $KeyCheckCert

    Value: "checkCert" Description: Should the agent check the SSL certificate.

  • $KeyCommand

    Value: "command" Description: Command to run during runcommand() operation.

  • $KeyCommandFile

    Value: "commandfilename" Description: File where the output from runcommand() operation is written to.

  • $KeyCompression

    Value: "compression" Description: SSH address attribute to compress the data transmitted.

  • $KeyFalse

    Value: "false" Description: String used for boolean address and account attributes.

  • $KeyFullName

    Value: "fullname" Description: Description attribute of an account.

  • $KeyGroup

    Value: "group" Description: Single sub KV of a group on the target.

  • $KeyGroupID

    Value: "groupid" Description: Identifier for the group.

  • $KeyGroupName

    Value: "groupname" Description: Name of the group.

  • $KeyGroups

    Value: "groups" Description: List of groups.

  • $KeyHostID

    Value: "hostid" Description:

  • $KeyHostKeysDenyUnmatched

    Value: "DenyUnmatch" Description: Tells the SSH library to connect ONLY if the host key matches an existing one.

  • $KeyHostKeysUpdate

    Value: "AllowUpdate" Description: Allows the SSH connection to update the host key if one already exists (unsafe).

  • $KeyHostKeysAppend

    Value: "AllowAppend" Description: Tells the SSH libary to add the host key if it is missing, but refuse if it does not match.

  • $KeyInteger

    Value: "integer" Description: Integral address attribute type.

  • $KeyKvgroup

    Value: "kvgroup" Description: Address attribute type.

  • $KeyManagedGroup

    Value: "managedGroup"

  • $KeyModelUID

    Value: "modeluid" Description: Account to use as a template when creating a new user.

  • $KeyNewPW

    Value: "newpw" Description: Password for the new account or the new password when resetting an existing account.

  • $KeyNoPTY

    Value: "nopty"

  • $KeyOldPW

    Value: "oldpw" Description: Current password for an account.

  • $KeyOperation

    Value: "operation" Description: The action that is currently underway.

  • $KeyPort

    Value: "port" Description: IP port to connect to on the target.

  • $KeyPosition

    Value: "position" Description:

  • $KeyReadOnly

    Value: "readonly" Description: Address attribute that is not modifiable by the user.

  • $KeyResource

    Value: "resource" Description:

  • $KeyResourceAddress

    Value: "resourceaddress" Description:

  • $KeyResourceType

    Value: "resourcetype" Description:

  • $KeyRestricted

    Value: "restricted" Description: Address attribute type of restricted values.

  • $KeyRestrictedValue

    Value: "restrictedvalue" Description: Key for the chosen restricted address attribute value.

  • $KeyRestrictedValueText

    Value: "restrictedvaluetext" Description: Display value for a restricted address attribute value.

  • $KeyRestart

    Value: "restart" Description:

  • $KeyRevision

    Value: "rev" Description:

  • $KeyScript

    Value: "script" Description: Address attribute type.

  • $KeyFile

    Value: "file" Description: Address attribute type.

  • $KeyDir

    Value: "dir" Description: Address attribute type.

  • $KeyPath

    Value: "path" Description: Address attribute type.

  • $KeyServer

    Value: "server" Description: Address attribute containing the target to connect to.

  • $KeyServerInfo

    Value: "serverinfo" Description: Operation to query the target for version and status information.

  • $KeySettings

    Value: "settings" Description:

  • $KeyShortID

    Value: "shortid" Description: User’s ID

  • $KeySSL

    Value: "SSL" Description: Address attribute determining if encrypted telnet should be used.

  • $KeyString

    Value: "string" Description:

  • $KeySysID

    Value: "sysID" Description: Username for secondary login to the target (e.g. sudo).

  • $KeySysPassword

    Value: "syspassword" Description: Password for the secondary login to the target.

  • $KeyTerm

    Value: "terminal" Description: Address attribute determining the terminal type for the telnet connection.

  • $KeyTimeout

    Value: "timeout" Description: Address attribute defining how long the agent should wait for connection to complete.

  • $KeyTrue

    Value: "true" Description: String used for boolean address and account attributes.

  • $KeyWriteOption

    Value: "writeoption" Description: Address attribute determining the terminal type for the telnet connection.

Live debugging

You can use the termdebug KVGroup to interactively monitor what is sent and received by agent scripts over raw TCP connections, SSL raw connections (not http/https), telnet and SSH connections. To do this, add a supplemental termdebug KVGroup to the agent input. For example:

# KVGROUP-V1.0
"" "" = {
  "address" = "telnet_unix.psl"
  "adminid" = "root"
  "adminpw" = "<encrypted password value>"
  "hostid" = "SSH1"
  "listfilename" = "C:\\listing-test.txt"
  "instance" = "globoco"
  "listresource" "" = {
  }
  "operation" = "list"
  "timeout" = "30"
     "termdebug" "" = {
       ## mandatory configurations
         "address" = "127.0.0.1"
          #String representation of the
          #address of the interface to listen to.
         "port" = "3210"
          #string representation of the TCP port to listen to.
          #agtssh listens to this port on IDM Suite server
         "command" = "c:\\windows\\system32\\telnet.exe localhost 3210"
          #Command line to run with full executable path and parameters
          #Quotes and backslashes need to be escaped with \.
          #See below for more info.
       ## optional configurations
         "interactive" = "1"
           #0=no 1=yes
           #If 1, shows a dialog box with the string to
           # be sent, and waits for OK before it is sent.
         "waitforjointimeout" = "10000"
           #Timeout, in milliseconds, of the agent waiting
           # for a client connection.
         "sendtimeout" = "1000"
           #Timeout, in milliseconds, of sending data back to the
           # client.
       }
}

Warning

Data seen on the debug port is raw decrypted data.

Command options

The command value can take Cygwin telnet.exe, or nc.exe, or a tn3270/tn5250 client as required, for example:

"command" = "c:\\cywgin\\bin\\bash -c \"/bin/telnet.exe localhost 3210 | tee /tmp/log1; sleep 3600\""

To prevent the window closing after the debug port closes, include cmd.exe /k telnet ... for telnet, or sleep for Cygwin telnet.exe or nc.exe.

For non-interactive debugging, the command can be something like:

"command" = "c:\\windows\\system32\\cmd.exe /c telnet localhost 3210 >> c:\\debuglog.txt 2>&1"

Interactivity options

When interactivity is turned on ("interactive" = "1" ) the script waits for one of the following inputs before proceeding:

  • ok = next step

  • cancel = skip next questions, run all steps automatically without any more prompting

If the original agent script or the target itself has very strict timeouts, it is recommended that you do not use the interactive option, or cancel it before the section with critical timing.

Adding the termdebug KVGroup to input

You can add the termdebug KVGroup to the agent input by:

  • Capturing agent input using the pstee program, then manually adding the inner KVGroup.

  • Wrapping the actual agent in an agtdos script as described in the PSLang Reference Manual (pslang.pdf) .

    The script should:

    1. Read $_inVars

    2. Replace its address ( "address" = "doswrapper.psl" ) with the telnet or ssh address line, then add the termdebug KVGroup

    3. Call the original agent with this group (converted to string) as input. It could also log the new modified agent input for debugging the PSLang wrapper script.

This method is more complicated, but has the advantage of working for every agent call and not replaying operations. See the PSLang Reference Manual (pslang.pdf) for more information about KVGroup functions, and system() calls.