Using PROC IOMOPERATE to list and stop your SAS sessions

If you're a SAS administrator, you probably know that you can use SAS Management Console to view active SAS processes. These are the SAS sessions that have been spawned by clients such as SAS Enterprise Guide or SAS Add-In for Microsoft Office, or those running SAS stored processes. But did you know that you can generate a list of these processes with SAS code? It's possible with the IOMOPERATE procedure.

To use PROC IOMOPERATE, you need to know the connection information for the SAS Object Spawner: the host, port, and credentials that are valid for connecting to the Object Spawner operator port. You plug this information into a URI scheme like the following:

iom://HOSTNAME:PORT;bridge;user=USERID,pass=PASSWORD

Here's an example:

iom://myserver.company.com:8581;bridge;user=sasadm@saspw,pass=Secret01

Are you squeamish about clear-text passwords? Good for you! You can also use PROC PWENCODE to obscure the password and replace its value in the URI, like this:

iom://myserver.company.com:8581;bridge;user=sasadm@saspw,
pass={SAS002}BA7B9D0645FD56CB1E51982946B26573

Getting useful information from PROC IOMOPERATE is an iterative process. First, you use the LIST SPAWNED command to show all of the spawned SAS processes:

%let connection=
'iom://myserver.company.com:8581;bridge;user=sasadm@saspw,pass=Secret01';

/* Get a list of processes */
proc iomoperate uri=&connection.;
list spawned out=spawned;
quit;

Example output:

You can retrieve more details about each process by running subsequent IOMOPERATE steps with the LIST ATTRS command. This can get tedious if you have a long list of spawned sessions. I've wrapped the whole shebang into a SAS program that discovers the processes and iterates through the list for you.

%let connection=
'iom://myserver.company.com:8581;bridge;user=sasadm@saspw,pass=Secret01';

/* Get a list of processes */
proc iomoperate uri=&connection.;
list spawned out=spawned;
quit;

/* Use DOSUBL to submit a PROC IOMOPERATE step for */
/* each SAS process to get details */
/* Then use PROC TRANSPOSE to get a row-wise version */
data _null_;
set spawned;
/* number each output data set */
/* for easier appending later */
/* TPIDS001, TPIDS002, etc. */
length y $ 3;
y = put(_n_,z3.);
x = dosubl("
proc iomoperate uri=&connection. launched='" || serverid || "';
list attrs cat='Information' out=pids" || y || ";
quit;
data pids" || y || ";
set pids" || y || ";
length sname $30;
sname = substr(name,find(name,'.')+1);
run;

proc transpose data=work.pids" || y || "
out=work.tpids" || y || "
;
id sname;
var value;
run;
");
run;

/* Append all transposed details together */
data allpids;
set tpids:;
/* calculate a legit datetime value */
length StartTime 8;
format StartTime datetime20.;
starttime = input(UpTime,anydtdtm19.);
run;

/* Clean up */
proc datasets lib=work nolist;
delete tpids:;
delete spawned;
quit;

The output details include "up time" (when the process was launched), the process ID (a.k.a. PID), the owner account, the SAS version, and more. Here's a snippet of some example output:

You can use this information to stop a process, if you want. That's right: from a SAS program, you can end any (or all) of the spawned SAS processes within your SAS environment. That's a handy addition to the SAS administrator toolbox, though it should be used carefully! If you stop a process that's in active use, an unsuspecting SAS Enterprise Guide user might lose work. And he won't thank you for that!

To end (kill) a SAS process, you need to reference it by its unique identifier. In this case, that's not the PID -- it's the UUID that the LIST ATTRS (or ATTRIBUTES) command provided. Here's an example of the STOP command:

/* To STOP a process */
proc iomoperate uri=&connection.;
STOP spawned server
id="03401A2E-F686-43A4-8872-F3438D272973";
quit;
/* ID = value is the UniqueIdentifier (UUID) */
/* Not the process ID (PID) */

It seemed to me that this entire process could be made easier with a SAS Enterprise Guide custom task, so I've built one! I'll share the details of that within my next blog post.