Page tree

PATROL deals with OS commands because they are used frequently in your KMs to get data from your application. One way to get data out of your application is through a subprocess execution, and it's important to understand how this works.

One-Time Commands

One way to retrieve data from your application is to start a process that returns the data each time it is launched. This way is useful for commands that do not work in a conversational way. 

PSL offers two functions that allow you to do this retrieval:

  • execute(CMDTYPE,"cmd");
    • spawns CMDTYPE with arguments "cmd"
    • example: execute(LSCMD,"-al"); - causes the following on Unix: /bin/ls -la
  • system("cmd")
    • spawns the OS command interpreter with execution of "cmd"
    • example: /bin/sh /usr/local/bin/myshell.sh

Execute versus System

The following two commands are almost the same: 

execute("OS",...) = system(); 

The one you should use will depend on if a shell is required to execute and if pre- and post-command processing is desired.

system() command

The system() command will unconditionally spawn an OS command interpreter and then pass the arguments to that interpreter. 

For example, you call the following function: 

system("myprogram.sh"); 

This command spawns the OS command interpreter and passes the argument "myprogram.sh" to the interpreter.When you trace this execution on the OS, you will see that this command will cause the following to execute on a Unix system: 

/ bin/sh myprogram.sh 

For this example, the system() function is the correct one to use because "myprog.sh" will contain shell commands and therefore needs a shell interpreter to run. However, if you had typed system("ls"); then the shell interpreter is useless and will just be an extra process that could have been avoided, because the "ls" command does not need a command interpreter to run (like any executable). 

The following figure shows the (useless) shell process that is created to execute the ls command. 

 Executing a system Command 

execute() command

To avoid the extra creation of a shell process, you could use the execute() command. The following figure depicts how the execute command will directly spawn the desired process without implicitly calling the OS interpreter. 

 Executing an execute Command 

 

As a result, execute() will use less OS resources. For this reason, execute() is the preferred method for spawning processes in PSL. 

For execute() to work, you will have to define a command type. In addition, there are extra features that will make execute() more attractive. Limitations of execute() include:

  • Tokenization - execute() will only do limited tokenization
  • Batch files - To execute batch files, you must initiate a call to an OS command interpreter. To launch batch files, if you have no need for extra features that execute offers, use the system() command.

Connection-Oriented Functions

Another way to get data from an application (and using sub-process execution) is to use the connection-oriented method (also known as a channel), which allows you to communicate to the executable. These are functions where a continuous connection between the VM and an external process or file is maintained. The following are descriptions of the PSL functions that allow you to establish channels.

popen(CMDTYPE,"cmd") function

When popen() is called, the PSL interpreter will create a communication channel between your PSL process and the external process. This communication channel is returned to you. This channel is private for a PSL process. The following figure shows what happens when you execute the popen() function. 

 Execute the popen() Function 

 

When the channel is established, you can use functions to talk to the executable through the channel or listen to your executable with write() or read(). When you want to finish the communication you can use the close() function to stop the channel. The same function can also be used to do proper cleanup and stop the spawned child process. This possibility can greatly improve the performance of the KM you are writing, especially when the start-up of the child process can require a lot of CPU. 

An example is Oracle's SQLPLUS, which is a conversational command. Instead of writing select statements on stdin, you write it in the channel. Instead of reading from stdout, you read from the channel.

fopen("filename") function

Just like subprocess execution, you can use a similar function called fopen() to access files. This function will return a channel that is private to the PSL process that called this function. The following figure shows what happens when you execute the fpopen() function. 
 Executing the fopen() function 

 

With this function, you can use the following functions to read, write, reposition, tell current position, and close the channel to the file:

  • read()
  • write()
  • fseek()
  • ftell()
  • close()

Sharing a channel

By sharing a channel to a process or a file, you allow other PSL processes to use it. The channel will be named and known in the namespace. If you want to access the same executable or file from multiple PSL processes, it is a good habit to let them all share the channel instead of launching multiple OS commands and creating multiple channels. After creation of a channel, the channel is open for everyone to use. To prevent multiple PSL processes accessing it at the same time, you might consider writing the channel access functions in a library that will do all necessary locking to force command serialization over the channel.

 The following figure is an example of using the share() function to share a process channel. 

   Sharing a process channel 

Concurrency

Because only one PSL process can be in the running at any given time, there are no low-level concurrency problems. Any PSL process can be interrupted after execution of any statement (this can also happen during execution of blocking functions that require IO). 
Two or more PSL processes that logically share data structures (for example, a variable in the namespace) can be interrupted when halfway through a transaction, leaving the shared data structure in an undesired state. If this is the case, you must identify these transactions and use lock() to make sure that only a single PSL process can access the shared resource at any given time. 

This doesn't only apply to namespace variables, but also to channels. The following are some examples for solving concurrency through locking:

  • Chan=(p/f)open(....,....); local for the PSL process
  • Share(chan,"share_name"); channel named in namespace and made accessible by all other PSL processes
  • (un)lock("share_name",...); prevents simultaneous usage of channel
  • No labels