[mod.computers.vax] An enhanced SHOW SYSTEM utility

bruceb@telesoft.UUCP (Bruce Bergman @spot) (03/16/87)

It occurred to me the other day that I have been using this neat
utility for viewing processes (similar to SHOW SYSTEM), and that
there are others who may like this program.

Enclosed you will find a DCL archive which contains two files:

	bigsis.c
	bigsis.man

The .c file should be compiled with DEC C V2.0 or greater, on
VMS V4.2 or greater.  I wrote this to run on that configuration,
and since I no longer have access to a C compiler, I can't tell
you if it compiles on anything else.  However, if you get it to
compile, it will tell you all sorts of useful information about
the jobs currently running on your system (not cluster).

It was written as a quick hak, and I never got around to putting
in all the features I wanted (like ability to work on 80 column
displays, selective process info, etc).  It has one bug.  If you
run it on a non-clustered environment, it won't display the sub
process parent information correctly.  This can be fixed easily
as it is in a small routine at the bottom of the code.  Other
than that, there are no known bugs.  Read the man page to get an
idea of what it will tell you.

I'd be interested in hearing from you if you like the program!!

I've got a bunch of other nifty utilities I wrote, which I may
post at a later date.  I'd like to see how this one goes over
first, though.  Oh yes, the means of collecting the files into
one 'archive' is my own design.  I didn't have any of the shar
programs running around, so I wrote my own.  Hope it works okay.

Comments, suggestions, flames, etc., can be sent to me.

bruce bergman
$ !------------------------------ cut here ------------------------------
$ set noon
$ on control then goto YOU_QUIT
$ on error then goto I_QUIT
$ file = "BIGSIS.C"
$ chksum = 309885657
$ pass_fail = "FAILED"
$ write sys$output "Extracting ""''file'"" - original checksum was: ''chksum'"
$ create/nolog 'file'
$ deck/dollars="EoF@11:34:11.17"
/************************************************************************
 *									*
 *				B I G S I S				*
 *									*
 ************************************************************************

** PURPOSE --------------------------------------------------------------
	Show VMS system/process information concisely.

** PACKAGE --------------------------------------------------------------
	VAX/VMS BIGSIS

** HISTORY --------------------------------------------------------------
	1.0	01-Jun-85	Creation (Bruce A. Bergman)
	1.1	27-Jun-85	Added ability to see process children
				and parent id's.  (Bruce A. Bergman)
	1.1a	10-Sep-85	Suspended processes were hanging the
				program so added code to display them
				and continue.  (Bruce A. Bergman)

** NON-DISCLOSURE -------------------------------------------------------
	This program can be copied or modified, not for profit, as
	long as this disclosure notice and author/edit information
	remain with the source code at all times.  No use of this
	software or its associated source is permitted for commercial
	or personal monetary gain (profit) in any form.
	The author of this software is not responsible for any
	repercussions that may arise from use of this software.
	Bugs and/or suggestions may be sent to the author at:

		Bruce A. Bergman
		460 East Washington Avenue, #109
		Escondido, CA.  92025-2958
		(619) 489-8195
		fido: 103/203
		uucp: ...sdcsvax!telesoft!bruceb

** CODE -------------------------------------------------------------- */

#include <stdio.h>
#include <descrip.h>
#include <ssdef.h>
#include <jpidef.h>

/*
|	These weren't identified in the sys$library:jpidef.h
|	files so they MUST be included.
*/

globalvalue JPI$_CLINAME;
globalvalue JPI$_MODE;
globalvalue JPI$K_OTHER;
globalvalue JPI$K_INTERACTIVE;
globalvalue JPI$K_BATCH;
globalvalue JPI$K_NETWORK;
globalvalue JPI$_MASTER_PID;
globalvalue JPI$_MAXDETACH;
globalvalue JPI$_MAXJOBS;

#include <pcbdef.h>
#include <statedef.h>

main(argc, argv)		/* show vms system/process information */
int argc;
char *argv[];
{

	struct _itmlst {
		unsigned short int buf_len;
		unsigned short int item_code;
		unsigned int buf_addr;
		unsigned int ret_len;
	} item_list[20];
	char	username[13],
		procname[16],
		terminal[9],
		cliname[128],
		states[6],
		subs[6],
		cputime[10],
		imag_name[132],
		image_name[50];
	float	fcputim;
	unsigned
	int	ret_flag,
		pid,
		cur_pid,
		mem,
		pri,
		prib,
		cputim,
		susp_flag,
		ppgcnt,
		sts,
		state,
		mode,
		master_pid,
		jobprccnt,
		owner,
		all_p,
		net_w,
		iosb;
	unsigned
	short
	int	maxjobs;

		/*
		|	Set our options to none (until we evauluate
		|	them and determine that some are wanted.
		*/

	all_p = net_w = FALSE;

		/*
		|	Check arguments for all possible info (a)
		|	or just network info (n).  If no options
		|	just display the normal stuff.  'ALL' assumes
		|	user wants network info also.
		*/

	if (argc > 1 && argv[1][0] == '-') {
			if (argv[1][1] == 'a' || argv[1][1] == 'A')
				all_p = net_w = TRUE;
			if (argv[1][1] == 'n' || argv[1][1] == 'N')
				net_w = TRUE;
	}

		/*
		|	Set up item list with the information
		|	we want.  This includes:
		|		Username
		|		Process name
		|		Process identification
		|		Amount of memory used
		|		Priority (current)
		|		Priority (base)
		|		CPU time
		|		Page count for image
		|		Virtual terminal id
		|		Mode
		|		CLI name
		|		Job status
		|		Job state
		|		Process parent's PID
		|		Sub-process count (current)
		|		Sub-process count (max)
		|		PID of owner of job
		|		Image name
		*/

	item_list[0].buf_len = 12;
	item_list[0].item_code = JPI$_USERNAME;
	item_list[0].buf_addr = username;
	item_list[0].ret_len = 0;

	item_list[1].buf_len = 15;
	item_list[1].item_code = JPI$_PRCNAM;
	item_list[1].buf_addr = procname;
	item_list[1].ret_len = 0;

	item_list[2].buf_len = 4;
	item_list[2].item_code = JPI$_PID;
	item_list[2].buf_addr = &pid;
	item_list[2].ret_len = 0;

	item_list[3].buf_len = 4;
	item_list[3].item_code = JPI$_MEM;
	item_list[3].buf_addr = &mem;
	item_list[3].ret_len = 0;

	item_list[4].buf_len = 4;
	item_list[4].item_code = JPI$_PRI;
	item_list[4].buf_addr = &pri;
	item_list[4].ret_len = 0;

	item_list[5].buf_len = 4;
	item_list[5].item_code = JPI$_PRIB;
	item_list[5].buf_addr = &prib;
	item_list[5].ret_len = 0;

	item_list[6].buf_len = 4;
	item_list[6].item_code = JPI$_CPUTIM;
	item_list[6].buf_addr = &cputim;
	item_list[6].ret_len = 0;

	item_list[7].buf_len = 4;
	item_list[7].item_code = JPI$_PPGCNT;
	item_list[7].buf_addr = &ppgcnt;
	item_list[7].ret_len = 0;

	item_list[8].buf_len = 8;
	item_list[8].item_code = JPI$_TERMINAL;
	item_list[8].buf_addr = terminal;
	item_list[8].ret_len = 0;

	item_list[9].buf_len = 4;
	item_list[9].item_code = JPI$_MODE;
	item_list[9].buf_addr = &mode;
	item_list[9].ret_len = 0;

	item_list[10].buf_len = 39;
	item_list[10].item_code = JPI$_CLINAME;
	item_list[10].buf_addr = cliname;
	item_list[101].ret_len = 0;

	item_list[11].buf_len = 4;
	item_list[11].item_code = JPI$_STS;
	item_list[11].buf_addr = &sts;
	item_list[11].ret_len = 0;

	item_list[12].buf_len = 4;
	item_list[12].item_code = JPI$_STATE;
	item_list[12].buf_addr = &state;
	item_list[12].ret_len = 0;

	item_list[13].buf_len = 4;
	item_list[13].item_code = JPI$_MASTER_PID;
	item_list[13].buf_addr = &master_pid;
	item_list[13].ret_len = 0;

	item_list[14].buf_len = 4;
	item_list[14].item_code = JPI$_JOBPRCCNT;
	item_list[14].buf_addr = &jobprccnt;
	item_list[14].ret_len = 0;

	item_list[15].buf_len = 2;
	item_list[15].item_code = JPI$_MAXJOBS;
	item_list[15].buf_addr = &maxjobs;
	item_list[15].ret_len = 0;

	item_list[16].buf_len = 4;
	item_list[16].item_code = JPI$_OWNER;
	item_list[16].buf_addr = &owner;
	item_list[16].ret_len = 0;

	item_list[17].buf_len = 132;
	item_list[17].item_code = JPI$_IMAGNAME;
	item_list[17].buf_addr = imag_name;
	item_list[17].ret_len = 0;

		/*
		|	Signal no more information required.
		*/

	item_list[18].buf_len = 0;

		/*
		|	Print formatted headers.
		*/

	printf("%-8s %-12s %-15s %-8s ", "PID", "Username", "Process name", "Terminal");
	printf("%-5s %-7s %-9s %-5s %-5s ", "Sub's", "Pri's", "CPU time", "State", "Size");
	printf("%-11s\n", "Image name");
	printf("-------- ------------ --------------- -------- ");
	printf("----- ------- --------- ----- ----- ");
	printf("-------------------------------------------------\n");

		/*
		|	We want information from all processes.
		*/

	cur_pid = -1;
	while (1) {

			/*
			|	Set a flag saying we this process is
			|	NOT suspended (we handle this special).
			*/

		susp_flag = FALSE;

			/*
			|	Call $GETJPI to obtain the actual info.
			*/

		ret_flag = sys$getjpi(32, &cur_pid, 0, &item_list, &iosb, 0, 0);

			/*
			|	If $GETJPI says this process is suspended, mark
			|	it as such and pretend like it is okay - we will
			|	deal with it later.
			*/

		if (ret_flag == SS$_SUSPENDED) {
			ret_flag |= SS$_NORMAL;
			susp_flag = TRUE;
		}

			/*
			|	If $GETJPI says everything was 'queued' okay,
			|	let's wait until VMS says it has given us all
			|	the info we requested (we're being nice
			|	to VMS since we could have not waited).
			*/

		if (ret_flag & SS$_NORMAL) {
			if (susp_flag == FALSE) {
				ret_flag = sys$waitfr(32);
				if (!ret_flag & SS$_NORMAL)
					sys$exit(ret_flag);
			}

				/*
				|	Everything is okay, so now check and see
				|	if this job is at a terminal.
				*/

			if (terminal[0] == 0)

					/*
					|	If this job is a batch job, check
					|	its pid with its parent pid.  If they
					|	match, then this is a spawned job.
					|	If they don't match, it must be batch.
					*/

				if (mode & PCB$V_BATCH)
					if (master_pid == pid)
						strcpy(terminal, "*BATCH*");
					else
						strcpy(terminal, "Spawned");
				else

						/*
						|	This job is interactive (not batch).
						|	If this job is a network server
						|	and the user wants to know about
						|	networks, say so.  If we aren't
						|	network then if the user wants to
						|	know about all jobs, then announce
						|	system jobs too.
						*/

					if (net_w && mode & PCB$V_NETWRK)
						strcpy(terminal, "Network");
					else
						if (all_p)
							strcpy(terminal, "[System]");
						else
							continue;

				/*
				|	If this job has no children, set a value indicating
				|	how many children are possible out of the max allowed.
				*/
				
			if (master_pid == pid)
				if (jobprccnt > 0)
					sprintf(subs, "%2d/%-2d", jobprccnt, maxjobs);
				else
					strcpy(subs, "     ");
			else

					/*
					|	If this job has children, place the last 4 characters
					|	of the parent pid into the process count area instead
					|	of indicating how many the job could have.  This makes
					|	it easier to track down children processes.
					*/

				set_sub(subs, owner);

				/*
				|	Set process state.
				*/

			set_state(states, state);

				/*
				|	If user is not running an image, then
				|	check to see if it has a CLI, if not,
				|	it must be a dead system process so place
				|	defunct into image slot.
				|	Otherwise display the CLI name when appropriate.
				*/

			if (imag_name[0] == 0)
				if (cliname[0] == 0)
					sprintf(image_name, "<defunct>");
				else
					sprintf(image_name, "(%s)", cliname);
			else

					/*
					|	Now we see if the job was suspended.
					|	If it was, make it obvious to all and
					|	change state to relfect type of MWAIT.
					|	Otherwise just copy the image name into
					|	the slot (truncating and parsing as necessary).
					*/

				if (susp_flag == TRUE) {
					strcpy(states, "SUSP");
					sprintf(image_name, "[Out To Lunch]");
				}
				else
					set_name(image_name, imag_name);

				/*
				|	Change CPU time to float so we can display
				|	it as a decimal value rather than time; its
				|	easier to see change that way (who says?!).
				*/

			fcputim = cputim;

				/*
				|	If no username, just defunct it also.
				*/

			if (username[0] == 0)
				strcpy(username, "<defunct>");

				/*
				|	At long last, time to print the info.
				|	Format everything so my sense of taste is not
				|	offended and make sure all process id's are in
				|	hexadecimal and hope the CPU time never gets HUGE.
				*/

			printf("%-8x %-12s %-15s %-8s ", pid, username, procname, terminal);
			printf("%-5s %3d/%-3d %9.2f %-5s %-5d ", subs, pri, prib, fcputim/100, states, ppgcnt);
			printf("%-49s\n", image_name);

				/*
				|	Go get another process until done.
				*/

			continue;
		}

			/*
			|	Hooray we're done!  Let's clean up and get outta here.
			*/

		if (ret_flag == SS$_NOMOREPROC)
			break;

			/*
			|	The user has no oper priv so we must just
			|	skip over this process like a good little program.
			*/

		if (ret_flag == SS$_NOPRIV)
			continue;

			/*
			|	Some unknown nasty has interrupted our
			|	session (how rude!).  Let VMS handle it.
			*/

		sys$exit(ret_flag);
	}

}

set_state(destin, state)		/* turn process state code into readable form */
char *destin;
unsigned int state;
{

		/*
		|	Assume we don't know what state we are in.
		*/

	strcpy(destin, "?????");

		/*	Start evaluating states until we find a match.
		|	Note: we could have done this with a switch() had
		|	these not been gloabalvalues.  We're really better
		|	off this way and we don't loose any functionality.
		*/

	if (state == SCH$C_CEF)
		strcpy(destin, "CEF");
	if (state == SCH$C_COM)
		strcpy(destin, "COM");
	if (state == SCH$C_COMO)
		strcpy(destin, "COMO");
	if (state == SCH$C_CUR)
		strcpy(destin, "CUR");
	if (state == SCH$C_COLPG)
		strcpy(destin, "COLPG");
	if (state == SCH$C_FPG)
		strcpy(destin, "FPG");
	if (state == SCH$C_HIB)
		strcpy(destin, "HIB");
	if (state == SCH$C_HIBO)
		strcpy(destin, "HIBO");
	if (state == SCH$C_LEF)
		strcpy(destin, "LEF");
	if (state == SCH$C_LEFO)
		strcpy(destin, "LEFO");
	if (state == SCH$C_MWAIT)
		strcpy(destin, "MWAIT");
	if (state == SCH$C_PFW)
		strcpy(destin, "PFW");
	if (state == SCH$C_SUSP)
		strcpy(destin, "SUSP");
	if (state == SCH$C_SUSPO)
		strcpy(destin, "SUSPO");

		/*
		|	Return pointer to state
		*/

	return (destin);

}

set_sub(destin, pid)		/* evaulate and set parent process id */
char *destin;
unsigned int pid;
{

	char	temp[9];
	int	i;

		/*
		|	Print the parent process id into
		|	a holding region and put 'P' at
		|	first char position so user knows that
		|	the number following is a PARENT!
		*/

	sprintf(temp, "%-8x", pid);
	*destin = 'P';

		/*
		|	Copy the last 4 characters in the parent pid
		|	into destination.  If pid was 0, just blank
		|	out (occurs in some defunct system processes).
		*/

	for (i = 1; i <= 4; i++)
		*(destin+i) = temp[i+3];
	*(destin+i) = '\0';
	if (pid == 0)
		*destin = '\0';

		/*
		|	Return pointer to process string.
		*/

	return (destin);

}

set_name(destin, source)	/* parse and truncate image name */
char *destin, *source;
{

	int pos;

		/*
		|	Search for first occurence of a directory spec
		|	searching from the end backwards.  This will
		|	get rid of long (like in VMS 4.x) disk pathnames.
		|	Note: look to "SHOW DEVICE/FILES/NOSYSTEM" to see what
		|	is on which disk.
		*/

	pos = strrchr(source, '[');

		/*
		|	Only copy the next 49 characters (so won't run
		|	off end of 132 col screen).
		*/

	strncpy(destin, pos, 49);

		/*
		|	Return pointer to the image name.
		*/

	return (destin);

}
EoF@11:34:11.17
$ checksum 'file'
$ if 'chksum' .eq. 'checksum$checksum' then pass_fail = "PASSED"
$ write sys$output "    Computed checksum is: ''checksum$checksum' (''pass_fail')"
$ file = "BIGSIS.MAN"
$ chksum = 979708107
$ pass_fail = "FAILED"
$ write sys$output "Extracting ""''file'"" - original checksum was: ''chksum'"
$ create/nolog 'file'
$ deck/dollars="EoF@11:35:48.99"

BIGSIS(1)                      VAX/VMS Utilities                     BIGSIS(1) 



NAME
     bigsis - display system and process information
     
SYNOPSIS
     bigsis [ -an ]
     
DESCRIPTION
     Bigsis displays  VAX/VMS system and process information.  The information 
     taken is a 'snapshot' of the system at the moment the request was  given. 
     The -a option asks for information about all processes, including defunct 
     ones.  The -n option asks that network processes be included as  part  of 
     the display  (they  are not normally).  Specifying the -a option includes 
     the -n option automatically.   Bigsis  is  formatted  for  a  132  column 
     screen.  

     The information returned to the user includes:  
     
          PID       The process  id  of  the  current  process.  The PID is in 
                    hexadecimal notation.  
                    
          USERNAME  The username is the actual login name that was used.    It 
                    will  never  change,  whereas  the process name CAN change 
                    during the process' session.  The username may  appear  as 
                    "<defunct>"  if  the  process  is a defunct system process 
                    (e.g.  NULL or SWAPPER).  
                    
          NAME      The process name can be changed by the user, but initially 
                    is identical to the username unless more than one  process 
                    already share the same username.  
                    
          TERMINAL  The  terminal  name (a virtual keyboard id under VMS V4.x) 
                    will appear if the process is an active job connected to a 
                    physical terminal.  If the job is a _b_a_t_c_h job, the special 
                    identifier  "*BATCH*"  will  be  shown  in  place  of  the 
                    terminal name.    If  the  process  belongs  to the system 
                    (created  by  the  system),  "[SYSTEM]"  appears  in  this 
                    column.   If  the process is _s_p_a_w_n_e_d from another process, 
                    "Spawned" is displayed and the user is  shown  the  parent 
                    process   in  the  adjecent  column  (see  SUBS  for  more 
                    detail).  
                    
          SUBS      If a job has been spawned by this process, a count appears 
                    that shows how many processes have been  spawned  and  how 
                    many processes   CAN  be  spawned.    The  two  pieces  of 
                    information are separated by a slash (/).  If the  maximum 
                    number  of  possible sub-processes is zero, then unlimited 
                    processes may be spawned.    Note  that  this  information 
                    appears for the _p_a_r_e_n_t of the spawned process only.  

                    If  the  process  is  a _c_h_i_l_d of another process, then the 
                    special identifier "Pxxxx" is displayed  in  this  column, 
                    where  xxxx  refers to the last four (4) characters of the 
                    parent process.  The characters are also hexadecimal.  
                    
          PRIORITY  The priority of the  current  process  is  shown  in  this 
                    column, in  the  form  xx/xx.   The first xx refers to the 
                    process' CURRENT priority.  The second xx  refers  to  the 


                                    - 1 -                                    
 


BIGSIS(1)                      VAX/VMS Utilities                     BIGSIS(1) 



                    process' BASE priority.  
                    
          CPUTIME   The  amount  of  CPU  time  the process has accumulated is 
                    displayed as an  integer  rather  than  an  actual  'time' 
                    value.    The   decimal   place   separates  seconds  from 
                    hundredth's of seconds.  Later, when cows fly,  the  value 
                    may be converted to an actual time.  
                    
          STATE     The  current  state  that the process was in at the moment 
                    the 'snapshot' was taken is displayed as an abbreviation.  
                    Refer to the VAX/VMS System Routines manual for a complete 
                    description of each state.    The  special  state,  "SUSP" 
                    appears  when  a  process  has  been  suspended  for  some 
                    reason.  If this is the case, the image name  will  appear 
                    as "[Out To Lunch]" to bring attention to the process.  
                    
          SIZE      The  size (in pages) of the image that the current process 
                    is executing is displayed.  This is an  approximate  value 
                    and  should  not be construed as an accurate reflection of 
                    the image size.  
                    
          IMAGE     The image name is displayed for the current process, if it 
                    is executing an image.  Otherwise, if the process  is  not 
                    suspended  (see  the STATE column for more detail), and if 
                    it is not a  defunct  system  process,  the  CLI  (Command 
                    Language Interpreter) for the current process is displayed 
                    enclosed in  parenthesis.    If  the  process is a defunct 
                    system process, "<defunct>" will appear in this column.  

                    The image name, if displayed, is parsed and  truncated  to 
                    show only the _l_a_s_t directory specification and the next 49 
                    characters (e.g.      [SYSEXE]BIGSIS.EXE;1   rather   than 
                    DUA0:[SYSEXE]BIGSIS.EXE;1).  
                    
     BUGS 
          Bigsis requires that the user have appropriate privileges to  obtain 
          information about  other processes.  If the user does not have these 
          privileges, only the user's process information is displayed.  

          The CPU time value is really not perfect, but it is easier to see  a 
          change in  the  value  in  this form.  I hope the process time never 
          exceeds the what can be stored as a 32 bit longword!  

          Finally, the parsing of the image name to fit on the screen  can  be 
          deceptive,  in  that  an  image from one directory can be on several 
          disks and never appear different from the bigsis display.  I suggest 
          using "SHOW DEVICE/FILES/NOSYSTEM" for process-disk information.  












                                    - 2 -                                    
 

EoF@11:35:48.99
$ checksum 'file'
$ if 'chksum' .eq. 'checksum$checksum' then pass_fail = "PASSED"
$ write sys$output "    Computed checksum is: ''checksum$checksum' (''pass_fail')"
$ write sys$output "Finished."
$ exit
$YOU_QUIT:
$ write sys$output "Aborting upon request of user..."
$ exit
$I_QUIT:
$ write sys$output "Encountered a fatal condition - aborting..."
$ exit
$ !------------------------------ cut here ------------------------------