Command Types

Command types are a powerful but are not an intuitive mechanism of isolating specifics of execution of types of commands. Each KM has a set of attributes called "Command Types" that can be set on a per-class basis. Command Types can be specified for application or computer classes or instances via the "Types of Commands" menu entry.

Once defined, the command types must appear on the option menu in the Console GUI for the "Command Type" button when creating a command. However, this direct usage of the Command Type is not recommended, but it is better to use the name of a command type as the first argument to PSL execute or PSL popen in a PSL script. Sample PSL scripts might look like:

ret = execute("MY_CMD_TYPE", some sql script);

This use of command types in PSL execute or popen forms the basis of their use. In this way, they are often used in conjunction with global channels opened via PSL popen.

Command type properties

Command Type Attribute

Purpose

Command Template

The command that is launched.

Pre-Command Text

Text that is sent to the standard input of the launched child process, before the command.

Post-Command Text

Text that is sent to the standard input of the launched child process, after the command.

No failure detection

Do not kill the child process at all.

Terminate if output contains

Kill the spawned child process if the returned output from the command contains the specified text.

Kill process/Kill process group

Send the kill signal to the process or the process group.

with signal SIGTERM/SIGINT/SIG KILL

The signal used to kill the process.

Command Type Features

Command types offer a number of attributes. The full list is shown in Command type properties.

The three main fields are Command Template, Pre-Command Text, and Post-Command Text.

The Command Template is usually the primary command for execution.

The fields related to killing the process, such as "Terminate if output contains", are not usually used because the functionality offered is too limited. Killing the process is usually handled explicitly rather than using these flags.

Command Type Macros

Each of the command type fields can be literal text, but this is quite limited. For more power there are macros that are expanded at the time that the command is launched. The Command Template can use the macro's as described in the following table.

The %{password} macro allows command types to access secure information without making it available to the PSL code in general. Command types are the only place where the password built-in macros is available to a KM. For example, a PSL get on the password variable in the symbol table will fail returning the empty string.

The %{command} macro for command text is the text string that comes from the second argument to the PSL execute or popen call. In the case of directly launched commands using this command type, this is the full command-text of a command explicitly typed as this command type.

Macros in command types

Macro Name

Expanded Result

%{username}

Username specified in application or instance

%{password}

Password specified in application or instance

%{command}

Command text; this is the text string that comes as the second argument to PSL execute or popen.

%{commandFile}

Temporary file in which command is stored

%{shell}

Default shell of user running this command (Note that this user is not the same user as the %fusernamegmacro, since this user is an OS account and %fusernameg is an application account.)

%{fastShellStartFlags}

"f " if shell is csh or tcsh; otherwise value is the empty string.

%{shellflags}

(OS/2 or Windows NT agents only) "/C" or "/K" depending on the command shell.

%{anything}

User-defined macros can be specified, and are expanded based on the agent symbol tree values from PSL set

Using the %{command} Macro

The %{command} macro is very special in that its presence changes the behavior of the command type in a major way. If the Command Template contains the %{command} macro, then the %{command} macro expands out to become the command text. Neither pre-text nor post-text are used if there is a %{command} macro in the Command Template. So if the Command Template uses %{command}, pre-text and posttext are pointless. An example of a simple send-to-shell command type is:

COMMAND TEMPLATE: %{shell} %{command}

In this example, pre-text and post-text are irrelevant and ignored. This means that using the %{command} macro in the Command Template has limited value. This usage only achieves a wrapping of the command inside another command, but does not allow the use of pretext or post-text to send input to the command.

Not Using the %{command} Macro

The most common use of commands is without the %{command}macro. The absence of the %{command} macro in the command template (and also without the %{commandFile} macro), causes more useful behavior. There is no expanding the command text into the %{command} macro as this is now obviously impossible as there is no such macro in the command template. The command text, which is sent as the second argument to the PSL popen or execute function, is now piped to the command's standard input. Only the Command Template specifies the executed command.

For example, let us make a command that executes a SQL script against an interactive database command:

tool . ret = execute("MY_SQL", "select * from my_table;");

For this to operate correctly, say in Oracle, we need this SQL command in the second argument to be passed to some interactive command. In the Oracle case we could treat "sqlplus" or "svrmgr" as commands that take SQL scripts as input. This would lead us to use the following definition of the "MY SQL" command type:

COMMAND TEMPLATE: sqlplus

Now, when the PSL execute statement runs from above, the agent will execute the command "sqlplus". It will then send the SQL script that is the second command text argument to PSL execute, down to the standard input of sqlplus. In theory, sqlplus will then execute the SQL command it receives, and return the output. However, if you know Oracle, you know that this example does not yet actually work. There are a few problems.

The first issue is the sqlplus requires a username and password submitted before it will run a SQL command. We need to extend our command type to use the %{username} and %{password} macros to securely submit these to the standard input of sqlplus before sending the SQL script.

Secondly, sqlplus will also produce a lot of spurious output that we might not want to see in the result. This is more easily solved by using the -s ag to make sqlplus go into "silent" mode and suppress prompt output.

Thirdly, there is another hidden issue in that sqlplus does not actually exit after running a single SQL script. This would mean that, assuming we xed both other problems, the call to PSL execute would still hang indefinitely because it is waiting for the sqlplus process to die.We could use PSL popen to open an interactive channel, read the resulting output via PSL read, and then kill the process via the correct arguments to PSL close. However, it is simpler to ensure that the sqlplus process receives an "exit;" command after the SQL script. We could either explicitly add "n\nexit\n\n" to our PSL execute call, but it is better that we place the exit statement in the command type itself, rather than having to remember it for every call to PSL execute.

So the question is how to add the username and password before the command text, and the exit statement after the command text. Fortunately, this is exactly why a command type has pre-text and post-text. This example above shows that the command text is sent to the command's standard input. Actually, the full string sent down the pipe to the sub-process's standard input is the concatenation of the pre-text, command text, and post-text. If there is pre-text or post-text that are non-empty strings, then some extra newlines are added. A newline character is added after the pre-text, after the command text, and after the post-text (at the end). The above special command type macros are expanded in the pre-text and command template but not the posttext or command text. If there is no pre-text or post-text, the command text is sent down the pipe, followed by a newline (to ush). In summary, the string sent to standard input is:

  • Both pre-command text and post-command text PRETEXT\nCMDTEXT\nPOSTTEXT\n
  • Pre-command text but not post-command text PRETEXT\nCMDTEXT\n\n
  • Post-command text but no pre-command text \nCMDTEXT\nPOSTTEXT\n
  • Not post-command text nor pre-command text CMDTEXT\n

Therefore we can extend our example command type to handle security login, and to terminate the session afterwards. The new attributes to the "MY SQL" command type must be:

COMMAND TEMPLATE: sqlplus --sCommand}

PRE-COMMAND TEXT: %{username}/%{password}

POST-COMMAND TEXT: exit;

This is a typical practical use of command types. A command type without a %{command} macro is typically used for establishing database sessions. The command template is used to spawn the database session command-line program. The pre-text is used to send a session connection or login sequence using the %{username} and the %{password} special macros. Since these account details are on standard input rather than command-line arguments the password does not show up in process detail "ps" output for the spawned command. The post-text is used to specify a command to exit the session if this does not happen by default. In this way, the command text can be a SQL query on the database, without having to specify any of the login/logout protocol to the database server.

Using the %{commandFile} Macro

The %{commandFile} macro is also very special and expands out to be the name of a temporary file, as created by the standard tmpnam call. Under UNIX this is usually a file name starting with "/tmp/". The temporary file is also changed to be owned by the user account and changed to permissions 0500 under UNIX platforms. The presence of the %{commandFile} macro in the Command Template causes the command text (i.e. that which would have usually been expanded in the %{command} macro) to be written to the temporary file. Macros are expanded in the command template and the command text.

Note that pre-text and post-text are not written to the file, and in fact have no effect if %{commandFile} is used. This can easily be considered a bug or a feature. Note that whenever the command finishes, the temporary file will be automatically removed by the agent.

Command Types cannot use both %{command} and %{commandFile} and will receive a run time error from the agent, and any commands using that command type will not execute.

Command Types and UNIX Shell Issues

If you do not include any '%{shell}' macro in your command template then the agent will attempt to execute the command directly, without passing it to a shell for tokenization, quote handling, backslash handling, I/O redirection handling, piping, etc. This means that if you want to use piping or redirection, etc, then your command template should have a prefix of %{shell} or actually:

%{shell} -%{fastShellStartFlags} # UNIX

%{shell} %{shellflags} # Windows

Without all of the features provided by the shell you can't do a great deal but it does allow you to execute commands directly, rather than wasting memory/process slot on executing a shell, and then having the shell execute your command. So there are obvious tradeoffs.

This does have an important advantage: using a newly de ned command type without a %{shell} macro is more efficient than choosing OS command type for a command or PSL execute/popen call, since the builtin OS type will spawn a shell to handle the commands. These extra shell processes can be avoided by using a command type instead.

The tokenization (parsing of the command string into a list of arguments) performed by the agent is very, very simple. It splits the command string into a argument list based only on whitespace. So you can't have a token '"a b c"' and expect it to be split into one argument, 'a b c'. What you will actually get is three arguments: '"a', 'b', 'c"'. For example, if you have a command type defined as:

COMMAND TEMPLATE: /bin/cat < %{commandFile}

This is erroneous since the "cat" command will get two arguments of "<" and the file name, and will try to output a file named "<". Instead, it is necessary to use %{shell} here to ensure that the redirection occurs correctly.

COMMAND TEMPLATE: %{shell} /bin/cat < %{commandFile}

Command Types in (pre)discovery

A command type defined on application level (and not on computer class level) has to run in context of an instance of this application.

This instance must be specified in the 3rd argument of the execute function, in case you are using the execute function with such a command type in a discovery script. When execute is called in an infobox, menu command, parameter or recovery action, PATROL will find the nearest ancestor instance himself. However, since discovery ran on application level, there is no way the agent can determine the instance you would like to use.

In case you want to run execute() after you have already created an instance, you can specify that instance. For example, a discovery script like below will work.

... create(inst,inst,OK); ... execute(cmdtype,cmd,inst); ...

In case you are trying to discover which instances you should create for your application class (for example CHILD.km), you can define the command type in another KM that can even possibly be the parent of CHILD.km (for example PARENT.km) In this case, PARENT.km should already have at least one instance, for example, PARENT/TOPLEVEL could be the unconditionally created parent instance.

Now in discovery of CHILD.km, you can call execute() like this:

execute("CMDTYPE","cmd","/PARENT/TOPLEVEL");

Another option (not really advised) would be to create an invisible instance of your application (unconditionally). In your discovery you can do the following to create an invisible instance. Note that the name "invisible" is not important, but this create statement should only have one argument.

if (! exists("invisible"))
{
# Create invisible object
create("invisible");
}

Now every execute can use the "invisible" as the instance to provide the context. execute(cmdtype,cmd,"invisible");
Was this page helpful? Yes No Submitting... Thank you

Comments