HP OpenVMS Systemsask the wizard |
The Question is: About a year ago I remember getting info on the latest version of VMS being having a utility which would allow quick ID of who is caught in a record lock. Do you know of any such critter? Also, I've been trying to use the lib$getdvi to return the lockID for the process which locks a record. Two things I've noticed: 1. When I use the channel as a parameter and not the device name, only 0000 gets returned. 2. When I use the device name, a lock ID is returned which doesn't match the one shown through the SDA "show process/lock/id=PID #" command. Are there any routines you know that I can use to ID a blocking process? Thanks Dave The Answer is : DECamds is far and away the easiest way to track the current holder of a lock, and the processes blocked by the lock. DECamds is a separately- installed kit, shipped with and licensed with OpenVMS. Work is underway in SDA to add a more direct access to the distributed lock management data structures for a release after OpenVMS V7.2. At present, identification of the lock holder involves traversing the lock data structures via SDA, including separate traversal of the lock trees on each cluster node involved as required. lib$getdvi and sys$getdvi return the high-level lock names for the particular target storage volumes -- these are used for volume-scale locking requirements. Typical RMS-level locking activity is typically at a much finer granularity, and involves file level or record level locks. For some background on the lock manager beyond what is in the system services and programming concepts manuals, see the VMS File System Internals book and the Internals and Data Structures Manuals. )These latter two books are available from Digital Press.) The attached tool may be of interest: #define MAXDEVICE 200 #define MAXDEVNAMLEN 64 #define MAXDEVLOCKNAM 13 #define MAXFILNAMLEN 80 #define MAXPID 16 /* ** BLOCKING.C, Hein van den Heuvel, Digital, July 1995 ** ** List all locks not granted (converting, waiting) for a specified PID. ** For each, print blocking lock information, resource name and parent. ** Have fun, ** Hein van den Heuvel, July 1995. */ #include <prvdef> #include <lkidef> #include <lckdef> #include <descrip> #include <string> #include <ctype> #include <stdio> #include <ssdef> #include <rms> #define terminator 0,0,0,0 #define EFN 1 static char devnam[MAXDEVICE][MAXDEVNAMLEN]; /* counted string */ static char devlocknames[MAXDEVICE][MAXDEVLOCKNAM+1]; static int device_count = 0; typedef struct { short len, cod; void *address; short *retlen; } item; typedef struct { unsigned int len; void *address; } desc; int sys$getlkiw(), sys$getjpi(), sys$setprv(), sys$cmexec(); void dump_data () ; int make_device_name_list(), print_filename_and_record() ; char *find_device_name(); char *mode[]={"NL", "CR", "CW", "PR", "PW", "EX", "??"}; main(int argc, char *argv[]) int stat, s, i, l, parent, grand_parent, lock_id, lock_pid; int wildcard=0, retlen=0, parent_retlen=0, pid=0, locks=0; char *x; struct lkidef lkibuf[100], *lki; struct { unsigned all : 16, one : 15, too_small : 1 ;} lkilen; int privs[] = { PRV$M_WORLD | PRV$M_CMEXEC , 0}; int *search_devnam=0; desc search_devnam_desc; #pragma nostandard /* Using address of variable where constant is standard */ struct { char rqmode, grmode, queue, fill;} lki_state; struct { int rms; unsigned short fid_num, fid_seq, fid_rvn; char devlocknam[22] ;} parent_resnam; struct { unsigned int id, vbn, fill[6];} resnam; item getlki_items[] = { 4, LKI$_LOCKID, &lock_id, 0, 4, LKI$_PID, &lock_pid, 0, 3, LKI$_STATE, &lki_state, 0, terminator}; item block_items[] = { 31, LKI$_RESNAM, &resnam, &retlen, 4, LKI$_PARENT, &parent, 0, sizeof lkibuf, LKI$_BLOCKING,&lkibuf, &lkilen, terminator}; item parent_items[] = {31, LKI$_RESNAM, &parent_resnam, &parent_retlen, 4, LKI$_PARENT, &grand_parent, 0, terminator}; int getlki_args[] = {7, EFN, (int) &wildcard, (int) &getlki_items,0,0,0,0}, parent_args[] = {7, EFN, (int) &parent, (int) &parent_items,0,0,0,0}, block_args[] = {7, EFN, (int) &lock_id, (int) &block_items,0,0,0,0}; #pragma standard /* ** First get some temporary privs for the GETLKI in EXEC mode later on. */ stat = sys$setprv ( 1, privs, 0, 0); if (stat != SS$_NORMAL) return (stat & -2); if (argc > 1) { /* ** First argument, if present, specifies PID to look for. 0 = All. */ sscanf (argv[1], "%x", &pid); if (pid > MAXPID) { printf (" Looking for non-granted locks for PID %8X\n\n", pid); } else { x = ""; if (pid > 0) x = "RMS "; printf (" Looking for non-granted %slocks for all PIDs\n\n", x); }; }; if (argc > 2) { /* ** Second argument, if present, specifies wildcarded device name ** to look for. For lock on matching devices, the RMS files name ** and locked record is attempted to be displayed. */ search_devnam_desc.len = strlen(argv[2]); search_devnam_desc.address = (int) argv[2]; search_devnam = &search_devnam_desc; str$upcase (&search_devnam_desc, &search_devnam_desc); } (void) make_device_name_list(search_devnam); /* ** Main loop. Get a lock, any lock. ** Find out wether it is held by specified process, and waiting. */ stat = sys$cmexec (&sys$getlkiw, &getlki_args); while (stat & 1) { locks++; if ( (lki_state.queue != LKI$C_GRANTED) && ((pid < MAXPID) || (lock_pid == pid)) ) { int rms = 0; parent = 0; /* ** Have blocked lock for specified PID, request details. */ stat = sys$cmexec (&sys$getlkiw, &block_args); if (!(stat & 1)) break; /* ** Let's see if we stumbled into an rms record or bucket lock... */ if (parent!=0) { s = sys$cmexec (&sys$getlkiw, &parent_args); if ((s & 1) && (grand_parent==0) && (parent_resnam.rms == 'RMS$')) rms = 1; } /* ** Print out all locks, or just RMS ? */ if ((pid == 0) || (pid > MAXPID) || rms) { l = lkilen.all / lkilen.one; printf ("Pid %08X Lock %08X, Rq=%s, Parent %08X, blocked by %d.\n", lock_pid, lock_id, mode[lki_state.rqmode], parent, l); dump_data (&resnam, retlen, "Resource Name "); lki = &lkibuf; for ( i = 0; i < l; i++) { printf (" PID=%8X, Gr=%s, Rq=%s, LockId=%08X, System=%08\n", lki->lki$l_pid, mode[lki->lki$b_grmode], mode[lki->lki$b_rqmode], lki->lki$l_lkid, lki->lki$l_csid); lki++; } if (parent!=0) dump_data (&parent_resnam, parent_retlen, "Parent Resource Name"); printf ("\n"); } if (rms) { printf (" RMS lock! VBN/ID %X/%X, File (%d,%d,%d) on %s\n", resnam.vbn, resnam.id, parent_resnam.fid_num, parent_resnam.fid_seq, parent_resnam.fid_rvn, &parent_resnam.devlocknam[1]); print_filename_and_record ( &parent_resnam.devlocknam[0], &parent_resnam.fid_num, resnam.vbn, resnam.id); } } /* PID ? */ if (stat &1) stat = sys$cmexec (&sys$getlkiw, &getlki_args); } /* stat ? */ if (stat == SS$_NOMORELOCK) stat = SS$_NORMAL; printf (" Done. Scanned %d locks.\n", locks); return stat; void dump_data (void *p, short len, char *t) int i, j, k, l; unsigned char (*x)[72]; char out[80], *in, c; x = in = p; l = len; if (l > 72) l = 72; /* sizeof out */ for (i=0; i<l; i++){ c = *in++; if (!isalnum (c)) c = '.'; out[i]=c; } out[i]=0; printf (" %s (%d):\n <%s>\n", t, len, &out[0]); j = 0; for (i=0; i<l;) { j += sprintf (&out[j], "%02X ", (*x)[i++]); if (!(i%24)) { printf (" %s\n",&out[0]); j = 0; } } if (j) printf (" %s\n",&out[0]); return; int print_filename_and_record ( char *devlocknam, unsigned short (*fid)[3], unsigned int vbn, unsigned int id) struct FAB fab; struct NAM nam; struct RAB rab; int device_number, stat, i, l; char buf[512]; static char filnam[MAXFILNAMLEN+1]; desc devnam_desc, filnam_desc = {0, filnam}; for (device_number=0; strcmp ((char *) devlocknames[device_number], devlocknam); device_number++) if (device_number == device_count) return; devnam_desc.len = devnam[device_number][0]; devnam_desc.address = &devnam[device_number][1]; filnam_desc.len = MAXFILNAMLEN; stat = lib$fid_to_name(&devnam_desc, fid, &filnam_desc, &filnam_desc); filnam[filnam_desc.len] = 0; printf ( " %s %s\n", &devnam[device_number][1], filnam); /* ** Now that we've printed out the file name, and previously the record ** or bucket data, lets try get display the data being waited for. ** I suppose this could end up becoming blocked for the very same lock! ** (VBN lock code is not at all tested as RMS does not normally block there) */ fab = cc$rms_fab; rab = cc$rms_rab; nam = cc$rms_nam; rab.rab$l_fab = &fab; fab.fab$l_nam = &nam; fab.fab$l_fop = FAB$M_NAM; /* will use dev+id, not name */ if (!vbn) fab.fab$b_fac = FAB$M_BIO;/* block IO if not an RFA */ fab.fab$b_shr = FAB$M_SHRUPD; /* allow writers */ for (i=0; i<3; i++) nam.nam$w_fid[i] = (*fid)[i]; for (i=0; i<devnam[device_number][0]; i++) nam.nam$t_dvi[i] = devnam[device_number][i]; nam.nam$t_dvi[0]--; /* strip colon */ rab.rab$l_ubf = buf; rab.rab$w_usz = sizeof buf; stat = sys$open ( &fab ); if (!(stat&1)) return stat; stat = sys$connect ( &rab ); if (!(stat&1)) return stat; if (vbn) { rab.rab$b_rac = RAB$C_RFA; rab.rab$v_rrl = 1; rab.rab$l_rfa0 = vbn; rab.rab$w_rfa4 = id; stat = sys$get ( &rab ); l = rab.rab$w_rsz; if (stat == RMS$_RTB) { l = rab.rab$l_stv; stat = 1; } } else { rab.rab$l_bkt = id; stat = sys$read ( &rab ); } dump_data (rab.rab$l_rbf, l, " Record "); return stat; int make_device_name_list(desc *search_devnam) { #include <dvidef> #include <dcdef> /* #include <dvsdef> does not exist, hand coded defines follow */ #define DVS$_DEVCLASS 1 #define DVS$_DEVTYPE 2 #define SS$_NOMOREDEV 2648 int i, stat, context[] = {0,0}, devclass = DC$_DISK; short int retlen; desc devnam_desc; item getdvi_items[] = { MAXDEVLOCKNAM, DVI$_DEVLOCKNAM,0, 0, terminator}; item device_items[] = { 4, DVS$_DEVCLASS, &devclass, 0, terminator}; /* ** Build two arrays with DISK device names and their lock names. */ for (i=0; i<MAXDEVICE; i++) { devnam_desc.len = MAXDEVNAMLEN-1; devnam_desc.address = &devnam[i][1]; stat = sys$device_scan (&devnam_desc, &devnam_desc, search_devnam, device_items, context); devnam[i][0]=devnam_desc.len; if (stat & 1) { devnam[i][devnam_desc.len+1]=0; /* Null terminate */ getdvi_items[0].address = devlocknames[i]; getdvi_items[0].retlen = &retlen; stat = sys$getdvi (0,0,&devnam_desc,getdvi_items,0,0,0,0); if (stat != SS$_NORMAL) return stat; devlocknames[i][retlen] = 0; } else { if (stat == SS$_NOMOREDEV) { device_count = i; i = MAXDEVICE; } break; } } return stat;
|