Running Programs or System Commands with $ZF(-100)
The $ZF(-100) function permits a Caché process to invoke an executable program or a command of the host operating system. This is the only InterSystems Callout function that can be used without a special Callout shared library (see “Creating an InterSystems Callout Library”). The following topics are discussed in this chapter:
-
Introduction — Overview of $ZF(-100) syntax and functionality.
-
Program Execution— Programs can optionally run asynchronously or within an operating system shell.
-
Logging Commands and Redirecting Output — Optional settings can log commands or redirect I/O.
-
Adding the %System_Callout:USE Privilege — Security settings higher than minimal require this privilege.
$ZF(-100) replaces deprecated functions $ZF(-1) and $ZF(-2), and should be preferred in all cases.
Introduction
$ZF(-100) provides functionality similar to that of a command line interface, allowing you to invoke an executable program or a command of the host operating system. The syntax of this function is:
sc = $ZF(-100, keywords, command, arguments )
The first argument must be a literal -100. The other three arguments specify the following information:
-
keywords — a string containing keywords that specify various options. For example, the string "/ASYNC/LOGCMD" specifies that the program should run asynchronously and write the command line to a log file.
-
command — a string specifying the program or system command to invoke. If the full path to an executable is not specified, the operating system will apply standard search path rules.
-
arguments — command arguments are specified as a series of comma-delimited expressions (as demonstrated in the example below).
The $ZF(-100) function returns an exit status code determined by the operating system and the program that was invoked.
The following example passes three strings to the echo command and then displays the status code. This example does not use any keywords, so the keywords argument is an empty string. The final command argument specifies a quoted string (following standard ObjectScript string rules):
USER>set status = $ZF(-100,"","echo","hello","world","""goodbye now""")
hello world "goodbye now"
USER>write status
0
The following sections provide more examples for various $ZF(-100) options. See “$ZF(-100): Running Programs or System Commands” in the Callout Quick Reference chapter for a summary of keywords and other options.
Program Execution
$ZF(-100) allows you to run a program or command either synchronously or asynchronously, with or without invoking the operating system shell. The default is to execute synchronously without invoking a shell. Default execution can be overridden by specifying optional keywords in the function call.
The following keywords can be used to control program execution:
-
/ASYNC — Indicates that the program should run asynchronously, allowing the $ZF(-100) call to return without waiting for the program to complete.
-
/SHELL — Indicates that the program should run in an operating system shell.
As mentioned in the last section, you can specify an empty string for the keyword parameter if you don’t want to use either of these options. This example deliberately tries to list nonexistent files so that an error code 1 will be generated:
USER>set status = $ZF(-100,"", "ls","*.scala")
ls: cannot access *.scala: No such file or directory
USER>write st
1
If we run the same command asynchronously, the output is not displayed and st is undefined because no error code has been returned:
USER>kill st
USER>set status = $ZF(-100,"/ASYNC", "ls","*.scala")
USER>write st
WRITE st
^
<UNDEFINED> *st
See “Using I/O Redirection” in the next section for a way to redirect error output in situations like this.
Logging Commands and Redirecting Output
The following keywords control logging and I/O redirection:
-
/LOGCMD — causes the program command and arguments to be sent to the console log.
-
/STDIN, /STDOUT, and /STDERR— are used to redirect standard input, standard output, and standard error for the program invoked. These keywords must be followed by a file specification (see “Using I/O Redirection” below).
Logging Command Arguments
The /LOGCMD keyword causes command arguments and the exit status code to be logged in the console log (<install-dir>\mgr\messages.log). This is intended primarily as a debugging tool that allows you to see how expressions passed to $ZF(-100) were actually evaluated.
In most cases, the command and its arguments are logged on one line, and the return value on the next. For example, set st=$ZF(-100,"/LOGCMD","echo","hello","world") produces the following log entry under Windows:
03/28/18-11:49:51:898 (26171) 0 $ZF(-100) cmd=echo "hello" "world"
03/28/18-11:49:51:905 (26171) 0 $ZF(-100) ret=0
However, on UNIX® when /SHELL is not specified, values are logged one per line:
03/28/18-12:09:22:243 (26171) 0 $ZF(-100) argv[0]=echo
03/28/18-12:09:22:500 (26171) 0 $ZF(-100) argv[1]=hello
03/28/18-12:09:22:559 (26171) 0 $ZF(-100) argv[2]=world
03/28/18-12:09:22:963 (26171) 0 $ZF(-100) ret=0
In either case, arguments are logged exactly as they are received by the program.
Using I/O Redirection
The following keywords and file specifiers control I/O redirection:
-
/STDIN=input-file
-
/STDOUT=output-file or /STDOUT+=output-file
-
/STDERR=error-file or /STDERR+=error-file
I/O redirection keywords are followed by an operator ( = or += ) and a filename or file path. Spaces are permitted around the operators. Standard input should point to an existing file. The standard output and standard error files are created if they don't exist and are truncated if they already exist. Use the = operator to create or truncate a file, or the += operator to append to an existing file. To make standard error and standard output to go to the same file, specify the same file for both keywords.
In the following example, the first line redirects the standard output from the echo command to file temp.txt., and the second line displays the resulting file contents:
USER>set st = $ZF(-100,"/STDOUT=""temp.txt""","echo","-e","three\ntwo\none\nblastoff")
USER>set st = $ZF(-100,"","cat","temp.txt")
three
two
one
blastoff
In this next example, we display two lines of temp.txt in a different way, by redirecting the file to standard input. The tail command accepts the input and displays the last two lines:
USER>set st=$ZF(-100,"/STDIN=""temp.txt""","tail","-n2")
one
blastoff
This final example redirects standard error to temp.txt, and attempts to display a nonexistent file. It also uses the /ASYNC keyword to run the command asynchronously, causing the $ZF(-100) call to return before the error message can be displayed. The second line (identical to the previous example) again displays the last two lines of the file, which now contain the redirected error message:
USER>set st = $ZF(-100,"/ASYNC /STDERR+=""temp.txt""","cat","nosuch.file")
USER>set st=$ZF(-100,"/STDIN=""temp.txt""","tail","-n2")
blastoff
cat: nosuch.file: No such file or directory
Adding the %System_Callout:USE Privilege
If your InterSystems security setting is higher than minimal, 0 $ZF(-100) requires the %System_Callout:USE privilege.
The %System_CallOut resource is already assigned to the %Developer Role, which you will have if you selected the Developer setup during installation. The following procedures describe how to assign this resource if you do not already have it:
Use the following procedure to assign the %Developer role to a User:
-
Open the Management Portal, go to System Administration, Security Management, Users and click Edit on the user description you want to use, then select the [Roles] tab.
-
Move the %Developer role from the Available column to the Selected column and click Assign. The role will appear in the list of roles assigned to this user. (If the %Developer role does not appear in the Available column, check the list of roles to see if you already have this role).
-
Click the Profile button. The %Developer role should be listed on the Roles: line.
In some cases, it may be desirable to have a role that allows use of $ZF(-100) but does not grant any other privileges. Use the following procedure to create a new role that grants only the %System_CallOut:USE privilege:
-
Open the Management Portal and go to System Administration, Security Management, Roles.
-
Click the Create New Role button to bring up the Edit Roles page.
-
Fill in the name and description:
-
Name: UseCallout
-
description: Grants privilege to use %System_CallOut resource
When you click Save, an Add button appears on the form.
-
-
Click the Add button to pop up a scrolling list of resources, select %System_CallOut from the list, and click Save. Click Close on the Edit Role form.
-
On the Roles page, the new UseCallout role is now in the list of role definitions.