GT.M Job Examine / System Status Overview
Background
Many M systems other than GT.M are self contained environments that provide their own system administration utilities. In contrast, GT.M is an open architecture implementation that aims to integrate well with the underlying operating system, and to not duplicate utilities and functionality provided therein. Also, as a consequence of the predominance of VARs rather than end-users in the GT.M customer base, GT.M tends to provide underlying functionality that can be easily packaged and presented in the way that best suits each application developed on GT.M. While this is a powerful capability in general, for someone who is used to other M systems, this can represent an initial obstacle to adopting GT.M.
This document describes how to package a Job Examine and System (i.e., M process) Status capability in a presentation that would be somewhat familiar to users of other M implementations without compromising the security that GT.M users expect of it. The examples here are for GNU/Linux, but other UNIX systems would be almost identical and VMS would be very similar. Theory of Operation
The GT.M ZSHOW command generates substantial amount of information about the process, including the M stack, local variable values, etc. If no output option is specified, by default, ZSHOW will write its output to the M principal device ($P); however, the output can also be directed to a local or global variable. To implement Job Examine and System Status, a “system” database is created to which all processes have read/write access. The system database consists of a global directory file to be used in an M global reference and which directs all global references to the database file.
/home/myapp/2.04/sys: ls -l mumps.* -rw-rw-rw- 1 kbhaskar slocate 128512 Mar 2 11:52 mumps.dat -rw-r--r-- 1 kbhaskar slocate 1024 Mar 1 10:10 mumps.gld
The easiest way to create the system area is to temporarily change the value of the gtmgbldir environment variable to point to the desired system global directory file name, then use GDE utility to create the global directory, then use MUPIP to create the database file, and finally restore gtmgbldir to point to the normal application global directory. Note the use of the environment variable gtm_myapp in the global directory to specify the database file – in the event the location of the application is moved to a different point in the file system, a simple change in the value of gtm_myapp is all that is needed to point to the new location of the database file.
/home/myapp/2.04/sys 3:54pm 259: setenv gtmgbldir /home/myapp/2.04/sys/mumps.gld /home/myapp/2.04/sys 3:54pm 260: $gtm_dist/mumps -run ^GDE %GDE-I-GDUSEDEFS, Using defaults for Global Directory /home/myapp/2.04/sys/mumps.gld GDE> change -segment DEFAULT -file=$gtm_myapp/sys/mumps.dat GDE> show -s DEFAULT *** SEGMENTS *** Segment File (def ext: .dat)Acc Typ Block Alloc Exten Options ------------------------------------------------------------------------------ DEFAULT $gtm_myapp/sys/mumps.dat BG DYN 1024 100 100 GLOB=1024 LOCK= 40 RES = 0 GDE> exit %GDE-I-VERIFY, Verification OK %GDE-I-GDCREATE, Creating Global Directory file /home/myapp/2.04/sys/mumps.gld /home/myapp/2.04/sys 3:55pm 261: $gtm_dist/mupip create Created file /home/myapp/2.04/sys/mumps.dat /home/myapp/2.04/sys 3:55pm 262: setenv gtmgbldir /home/kbhaskar/vista/2.04/g/mumps.gld
GT.M has the ability to receive and respond to asynchronous USR1 signals, which can be sent with the mupip intrpt command. When the signal is received by a GT.M process, it XECUTEs the string in the Intrinsic Special Variable $ZINTERRUPT. This ISV can be explicitly SET inside a GT.M process, or, at process startup it is initialized to the string in the environment variable gtm_zinterrupt. Consider the following example (the long line will probably appear wrapped, but there are two lines in the example):
GTM>w $ZINTERRUPT new tmp set tmp=$H zshow "*":^|"/home/myapp/2.04/sys/mumps.gld"|job($J,tmp) set ^|"/home/myapp/2.04/sys/mumps.gld"|jobx(tmp,$J)=""
Thus, when the M process receives an interrupt, it directs the ZSHOW output to the ^job M global variable in the system database, and for ease of access also places a cross reference in the ^jobx global variable. Note the use of a temporary variable to ensure that ^job and ^jobx have the same timestamp. It should be noted that the system database will slowly increase with use. Either globals should be killed periodically, or the database should periodically be deleted and recreated.
Environment variables can be used to enable this behavior in an existing GT.M application, as in the example below of an csh/tcsh file to be source'd to create the environment variables to run myapp (this assumes that environment variables to set GT.M, in particular gtm_dist, have already been set). Note the use of single and double quotes to set the value of gtm_zinterrupt. (as before, the lines – there are five lines in the example).
setenv gtm_myapp /home/myapp/2.04 setenv gtmgbldir $gtm_myapp/g/mumps.gld setenv gtmroutines "$gtm_myapp/o($gtm_myapp/p $gtm_myapp/r) $gtm_dist" setenv gtmsysgbldir $gtm_myapp/sys/mumps.gld setenv gtm_zinterrupt 'new tmp set tmp=$H zshow "*":^|"'"$gtmsysgbldir"'"|job($J,tmp) set ^|"'"$gtmsysgbldir"'"|jobx(tmp,$J)=""'
The above is all that needs to be done to allow GT.M processes to respond to mupip intrpt by storing a cross-referenced snapshot of their states in the system database. Now all that remains is to package the sending of interrupts and display of information stored in the system database. A shell script gtmsendint is used to send mupip intrpt commands to M processes. The shell script can be invoked in three ways:
If invoked with no arguments, gtmsendint sends mupip intrpt commands to all mumps processes in the system. This facilitates getting a snapshot from the shell, without there being a controlling M process.
If invoked with one argument (all arguments are process ids), gtmsendint sends mupip intrpt commands to all mumps processes in the system except the process with process id equal to the argument. It then sends a mupip intrpt to the process with process id equal to the argument. This facilitates System Status – capturing the state of all processes except the process initiating the gtmsendint. The process is the last to receive the mupip intrpt, so that (in the typical case), it can know when to look in the system database for the status of other processes.
If invoked with more than one argument (in the typical case two arguments, but it is possible to generalize to more processes), gtmsendint sends a mupip intrpt to the processes in the list. This facilitates Job Examine, where gtmsendint would be invoked with the process id of the job to be examined followed by the process id of the process that wishes to examine the status of other jobs.
gtmsendint for x86 GNU/Linux csh/tcsh is shown below. A similar script could be written for the sh/ksh/bash family of shells. On other UNIX systems, the arguments for the ps command may be different. A functionally equivalent DCL command file could be created for VMS; alternatively the %ST utility could be used to obtain the list of M processes, and an ad hoc DCL command file could be generated in M and executed. It should be noted that %ST may itself suffice in many cases as a Job Examine surrogate.
#!/bin/tcsh -f switch ($#) case 0: foreach proc (`ps --no-headers -o pid= -C mumps`) $gtm_dist/mupip intrpt $proc >>&/tmp/gtmsendint.$$.log end breaksw case 1: foreach proc (`ps --no-headers -o pid= -C mumps`) if ($proc != $1) $gtm_dist/mupip intrpt $proc >>&/tmp/gtmsendint.$$.log end $gtm_dist/mupip intrpt $1 >>&/tmp/gtmsendint.$$.log breaksw default: foreach proc ($*) $gtm_dist/mupip intrpt $proc >>&/tmp/gtmsendint.$$.log end breaksw endsw
The M routine jobexam.m can be called thus: DO ^jobexam(<pid>) to use gtmsendint to send a mupip intrpt to the process to be examined and then a mupip intrpt to the process executing jobexam. Note that gtmsendint must be in the UNIX environment variable path so that ZSYSTEM can execute it. The example below is simply a template to be completed depending on the output format and fields desired.
jobexam(job) ; Get and display job status new int,time,tmp,x set int=$zinterrupt set $zinterrupt="set tmp=1" set time=$H zsystem "gtmsendint "_job_” “_$J ; interrupt job, then me set tmp=0 for hang 1 quit:tmp=1; wait for interrupt set x=$Q(^|$ZTRNLNM("gtmsysgbldir")|jobx(time) ; get xref ; find the $job entry corresponding to dump ; and display selected data from the $job nodes set $zinterrupt=int quit
The M routine sysstat.m is very similar, but it sends a mupip intrpt to all mumps processes except the process executing sysstat, and then a mupip intrpt to the process executing sysstat, which can then find and print the desired system status.
sysstat ; Get and display system status new int,time,tmp,x set int=$zinterrupt set $zinterrupt="set tmp=1" set time=$H zsystem "gtmsendint "_$J ; interrupt all jobs, then me set tmp=0 for hang 1 quit:tmp=1; wait for interrupt set x=$Q(^|$ZTRNLNM("gtmsysgbldir")|jobx(time) ; get xref ; find the $job entry corresponding to dump ; and display selected data from the $job nodes set $zinterrupt=int quit
Security Aspects
The GT.M security model requires the operating system to permit the sending of a signal from one process to another. For the gtmsendint shell script to be able to send a USR1 signal to a GT.M process, it must either have the same user id, or it must be root. If its user id is not root, a user will be able to initiate a Job Examine and System Status only for processes with the same user id. Note, however, that it is possible for it to be installed “set uid” root, so that when any user runs it, it will run as root and be able to send an interrupt to any process on the system. If it is installed in that manner, gtmsendint can be used by a rogue process to repeatedly interrupt processes on the system with a high frequency, leading to performance degradation and possible denial of service. Except on systems where all users are appropriately vetted, and which are secured against the possibility of infiltration by unauthorized users, do not install the script with the set uid bit turned on and owned by root.
The GT.M security model requires a process to have read/write access permissions by the operating system for a database file in order to read from and write to that database file. As described here, the system database can be accessed by all processes. Since zshow "*" dumps the values of process local variables, this potentially makes sensitive and confidential information (e.g., patient medical records, bank balances) visible to all processes in the system. On a computer system with production data, an acceptable alternative may be to use zshow "S" which only provides information on processes' M stack. A more secure technique, but one that is more complex to implement, would be to use environment variables to give each userid a private system database, and for the process collecting and displaying the information to run as root.
Revision History
Version 1 Date 03/12/03 Author K.S. Bhaskar Summary Initial release