#define MAX_BLOCKED 100 /* ** ** RMS_LOCK_HOLDERS.C Hein van den Heuvel, Jan-2010 ** ** If File argument is present, then up to MAX_BLOCKED RMS lock holders for the ** file are listed with PID, USERNAME, PROCESS_NAME and IMAGE_NAME ** ** I a second argument is present then it is treated as KEY value for the file(s) ** in question than for the initial version, the first lock holder will be reported on, as it is relatively ** non-standard to have concurrently held record locks (normal for file locks) ** Eventually we'll report up to MAX_BLOCKED lock holders for matching records are reported ** Options, key number, rfa, multi-match ** ** Eventually we'll get an VBN/ID argument, for Bucket locks and RFA locks. ** ** This program requires CMEXEC and WORLD privs, which it will grab if allowed. ** ** */ #include signal #include descrip #include prvdef #include dvidef #include psldef #include secdef #include jpidef #include lckdef #include lkidef #include iodef #include ssdef #include rms #include stdio #include signal #include stdlib #include string #include unistd #define MAX_DEVLOCKNAM 16 #define FSB_NAMLEN 36 #define EFN 0 #define TERMINATOR 0,0,0,0 typedef struct { unsigned short len, cod; void *address; unsigned short *retlen; } item; typedef struct { long count; void *address; } desc; int sys$close(), sys$open(), sys$parse(), sys$search(), sys$connect(), sys$get(), sys$getjpiw(), sys$getlkiw(), sys$getdvi(), sys$getsyiw(), sys$setprv(), sys$fao(), sys$cmexec(), lib$wait(), sys$gettim(), sys$asctim(), sys$exit(), sys$enq(), sys$enqw(), sys$deq(); int verbose_flag = 0; int wildcard = -1; unsigned short retlen, file_retlen, record_retlen, username_length, nodename_length, image_name_length, process_name_length; int parent, grand_parent, lock_id, lock_pid; char image_name[256], username[16], nodename[32], process_name[16]; struct lkidef lkibuf[MAX_BLOCKED]; struct { unsigned all : 16, one : 15, too_small : 1 ;} lkilen; struct { unsigned short status, filler; unsigned int lock_id; char valblk[16]; } file_lksb, record_lksb; #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] ;} file_resnam; struct { unsigned int id, vbn, fill[6];} record_resnam, resnam; item getdvi_items[] = { MAX_DEVLOCKNAM, DVI$_DEVLOCKNAM, file_resnam.devlocknam, 0, 0, 0, 0, 0 } ; desc devlocknam = { MAX_DEVLOCKNAM, file_resnam.devlocknam}; desc file_resnam_desc = { 26, &file_resnam }; desc record_resnam_desc = { 8, &record_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, sizeof lkibuf, LKI$_BLOCKING,&lkibuf, (unsigned short *) &lkilen, TERMINATOR}; // LKI$_LCKREFCNT item file_items[] = {31, LKI$_RESNAM, &file_resnam, &file_retlen, 4, LKI$_PARENT, &grand_parent, 0, TERMINATOR}; item record_items[] = {31, LKI$_RESNAM, &record_resnam, &record_retlen, 4, LKI$_PARENT, &parent, 0, TERMINATOR}; int getlki_args[] = {7, EFN, (int) &wildcard, (int) &getlki_items, 0,0,0,0}, file_args[] = {7, EFN, (int) &parent, (int) &file_items, 0,0,0,0}, record_args[] = {7, EFN, (int) &parent, (int) &record_items, 0,0,0,0}, block_args[] = {7, EFN, (int) &record_lksb.lock_id, (int) &block_items, 0,0,0,0}, // SYS$ENQ [efn] ,lkmode ,lksb ,[flags] ,[resnam] , // [parid] ,[astadr] ,[astprm] ,[blkast], [acmode] ,[rsdm_id] ,[nullarg] enq_file_null_args[] = { 12, 0, LCK$K_NLMODE, (int) &file_lksb, LCK$M_EXPEDITE | LCK$M_SYNCSTS | LCK$M_SYSTEM | LCK$M_VALBLK, (int) &file_resnam_desc, 0, 0, 0, 0, PSL$C_EXEC, 0, 0}, enq_record_null_args[] = { 12, 0, LCK$K_NLMODE, (int) &record_lksb, LCK$M_EXPEDITE | LCK$M_SYNCSTS | LCK$M_SYSTEM | LCK$M_VALBLK, (int) &record_resnam_desc, 0, 0, 0, 0, PSL$C_EXEC, 0, 0}, enq_file_exclusive_args[] = { 12, 0, LCK$K_EXMODE, (int) &file_lksb, LCK$M_CONVERT | LCK$M_NODLCKWT, 0, 0, 0, 0, 0, PSL$C_EXEC, 0, 0}, enq_record_exclusive_args[] = { 12, 0, LCK$K_EXMODE, (int) &record_lksb, LCK$M_CONVERT | LCK$M_NODLCKWT, 0, 0, 0, 0, 0, PSL$C_EXEC, 0, 0}, // int sys$deq (unsigned int lkid, void *valblk, unsigned int acmode, unsigned int flags); deq_cancel_args[] = { 4, 0, 0, PSL$C_EXEC, LCK$M_CANCEL }, //SS$_CANCELGRANT deq_args[] = { 4, 0, 0, PSL$C_EXEC, 0 }, help; #pragma standard item getjpi_items[] = { 15, JPI$_USERNAME, &username, &username_length, 31, JPI$_NODENAME, &nodename, &nodename_length, 80, JPI$_IMAGNAME, &image_name, &image_name_length, 15, JPI$_PRCNAM, &process_name, &process_name_length, TERMINATOR }; int find_a_locked_record (struct FAB *fab, desc *key_value, unsigned int rfa_vbn_or_krf, unsigned short rfa_id ) /* ** First argument is a FAB for an already opened file. ** Use second provided argument as key specification. ** If the key length is null, then a lookup by RFA is done. ** Report Lockerfor records matching the key specification. */ { struct RAB rab; struct NAM *nam; struct lkidef *lki = lkibuf; int status, i, s; unsigned short iosb[4]; char recbuf[32768]; desc devnam; nam = fab->fab$l_nam; rab = cc$rms_rab; rab.rab$l_fab = fab; rab.rab$b_mbf = 2; rab.rab$l_rop = RAB$M_RRL; rab.rab$w_rop_2 = RAB$M_NQL; rab.rab$l_ubf = recbuf; rab.rab$w_usz = 32767; if (key_value) { rab.rab$b_rac = RAB$C_KEY; rab.rab$l_kbf = key_value->address; rab.rab$b_ksz = key_value->count; rab.rab$b_krf = rfa_vbn_or_krf; } else { rab.rab$b_rac = RAB$C_RFA; rab.rab$l_rfa0 = rfa_vbn_or_krf; rab.rab$w_rfa4 = rfa_id; } status = sys$connect( &rab); if ( status & 1 ) { status = sys$get ( &rab ); sys$close ( fab ); // Clean out the real RMS locks. if ( !( status & 1 ) ) { if (verbose_flag) printf ("Could NOT read record. STS=%d, key=<%s>, ksz=%d, vbn=%d, id=%d\n", status, (char *) key_value->address, key_value->count, rfa_vbn_or_krf, rfa_id); } else { i = rab.rab$w_rsz; if (i > 60) i = 60; recbuf[i--] = 0; while (i-- > 0) { if (recbuf[i] < ' ') recbuf[i] = '.'; } if (verbose_flag) printf (" Got record RFA=(%d,%d) Some bytes: 60/%d=<%s>\n", rab.rab$l_rfa0, rab.rab$w_rfa4, rab.rab$w_rsz, rab.rab$l_rbf); devnam.count = nam->nam$b_dev; devnam.address = nam->nam$l_dev; status = sys$getdvi ( 0, 0, &devnam, getdvi_items,0,0,0,0); if (! (status & 1)) return status; file_resnam.rms = 'RMS$'; file_resnam.fid_num = nam->nam$w_fid[0]; file_resnam.fid_seq = nam->nam$w_fid[1]; file_resnam.fid_rvn = nam->nam$w_fid[2]; // file_resnam.devlocknam is filled in by getdvi call above. status = sys$cmexec (&sys$enqw, &enq_file_null_args); if (!(status & 1)) { fprintf (stderr, "enq_file_null_args %08X\n", status); return status; } if (verbose_flag) printf (" Got the file lock. Lock-id = %08X\n", file_lksb.lock_id); enq_record_null_args[6] = file_lksb.lock_id; record_resnam.vbn = rab.rab$l_rfa0; record_resnam.id = rab.rab$w_rfa4; status = sys$cmexec (&sys$enqw, &enq_record_null_args); if (!(status & 1)) { fprintf (stderr, "enq_record_null_args %08X\n", status); return status; } if (verbose_flag) printf (" Got the record lock. Lock-id = %08X\n", record_lksb.lock_id); status = sys$cmexec (&sys$enq, &enq_record_exclusive_args); /* ** I suspect the real SS$_SYNC status is mangled by the SYS$CMEXEC jacket. ** Tried the LKSB status, but that was 0 for waiting, as expected. ** ** if (1 & status) status = record_lksb.status; */ switch (status) { case SS$_SYNCH: printf ("No process blocking EXclusive Record Lock. T\n"); break; case SS$_NORMAL: status = sys$cmexec (&sys$getlkiw, &block_args); if (status & 1) { // for (i = 0; i < 1 ; i++) { // qqq if ( lki->lki$l_pid ) { if (verbose_flag) printf ( " Record Lock %08x/%08x Blocked by lock %08x/%08x Process %08x\n", record_lksb.lock_id, lki->lki$l_csid, lki->lki$l_mstlkid, lki->lki$l_mstcsid, lki->lki$l_pid); s = sys$getjpiw ( 0, &lki->lki$l_pid, 0, getjpi_items, iosb, 0, 0); if (s & 1 ) { process_name[process_name_length] = 0; image_name[image_name_length] = 0; username[username_length] = 0; nodename[nodename_length] = 0; printf ("Record lock held by PID = %08X, Node=%s User=%s, Process=<%s>\n", lki->lki$l_pid, nodename, username, process_name); if (image_name_length) { printf ( " Process image name = %s\n", image_name ); } else { printf ( " No process Image. Opened and locked by DCL?" ); } } else { printf ("Record lock held by PID = %08X, Could not get info ( %08X )\n", lki->lki$l_pid, s ); } // getjpi } else { printf ("PID=0. NO Process ID reported for Record lock holder!\n"); }// PID to play with? } // getlkiw /* Done waiting ** I suppose we could just walk away, but let's clean up nicely. */ deq_cancel_args[1] = record_lksb.lock_id; s = sys$cmexec (&sys$deq, &deq_cancel_args); // status is not critical if ( SS$_CANCELGRANT == s ) { if ( lki->lki$l_pid ) fprintf (stderr, "Record lock was granted, against expectation."); } else { if (!(s & 1)) fprintf (stderr, "deq_cancel_args %08x\n", s); } break; default: printf ("Status %08X returned by ENQW EXclusive File Lock\n", status); break; } // switch deq_args[1] = record_lksb.lock_id; s = sys$cmexec (&sys$deq, &deq_args); // status is not critical if (!(s & 1)) fprintf (stderr, "deq record lock %08x\n", s); deq_args[1] = file_lksb.lock_id; s = sys$cmexec (&sys$deq, &deq_args); // status is not critical if (!(s & 1)) fprintf (stderr, "deq file lock %08x\n", s); } // Succes get } // succes connect return status; } int find_some_files (char *wild_carded_file_name, desc *key_value, unsigned int rfa_vbn, unsigned short rfa_id ) /* ** Use provided argument as a wildcard file specification. ** Report Lockers. */ { struct FAB fab; struct NAM nam; short iosb[4]; desc devnam; int i, l, status, retadr[2] = {0,0}, found = 0; static char devlocknam_buf[MAX_DEVLOCKNAM]; int lock_id, lock_pid; char expanded_name[256], resultand_name[256], *name; /* ** RMS Parse, Search and Open data initialization. */ fab = cc$rms_fab; fab.fab$l_fna = wild_carded_file_name; fab.fab$b_fns = strlen(fab.fab$l_fna); fab.fab$l_fop = FAB$M_NAM; fab.fab$l_dna = ".IDX"; fab.fab$b_dns = strlen(fab.fab$l_dna); fab.fab$b_shr = FAB$M_UPD; // Allow everything fab.fab$l_nam = &nam; nam = cc$rms_nam; nam.nam$l_rsa = resultand_name; nam.nam$b_rss = 255; nam.nam$l_esa = expanded_name; nam.nam$b_ess = 255; status = sys$parse( &fab); if (status & 1) status = sys$search ( &fab); name = nam.nam$l_rsa; name[nam.nam$b_rsl] = 0; while (status & 1) { found++; if (verbose_flag) fprintf (stdout, "Found file : %s\n", name); #if defined qqq file_resnam.rms = 'RMS$'; // Preload 4 byte ascii lock name header. /* ** Report on processes holding the file lock by first, before opening. ** - request and obtain a NL lock for the file. ** - asynchroneously convert the lock to EX which will not be granted ** - Execute GETLKIW for LKI$_BLOCKEDBY ** - Execute $ENQW with LCK$M_CANCEL ** - $DEQ lock */ devnam.count = nam.nam$b_dev; devnam.address = nam.nam$l_dev; if (status & 1) status = sys$getdvi ( 0, 0, &devnam, getdvi_items,0,0,0,0); file_resnam.fid_num = nam.nam$w_fid[0]; file_resnam.fid_seq = nam.nam$w_fid[1]; file_resnam.fid_rvn = nam.nam$w_fid[2]; // file_resnam.devlocknam is filled in by getdvi call above. status = sys$cmexec (&sys$enqw, &enq_null_args); status = sys$cmexec (&sys$enq, &enq_exclusive_args); switch (status) { case SS$_SYNCH: printf ("No process blocking EXclusive File Lock. T\n"); break; case SS$_NORMAL: status = sys$cmexec (&sys$getlkiw, &block_args); if (status & 1) { for (i = 0; i < 1 ; i++) { // qqq status = f$getjpi ( ); printf ("File lock held by ..."); } } default: printf ("Status %08XL returned by ENQW EXclusive File Lock\n", status); break; } // switch deq_cancel_args[1] = record_lksb.lock_id; status = sys$cmexec (&sys$deq, &deq_cancel_args); status = sys$cmexec (&sys$getlkiw, &getlki_args); while (status & 1) { int granted; if (status & 1) status = sys$fao ( &fao_control, 0, &fsb_nam, &devlocknam, nam.nam$w_fid[0], nam.nam$w_fid[1], nam.nam$w_fid[2]); } #endif if (key_value->count || rfa_vbn ) { // need to look for record lock or bucket? status=sys$open(&fab); if (status & 1) { status = find_a_locked_record (&fab, key_value, rfa_vbn, rfa_id ); } else { if ( status != RMS$_FLK ) { printf ("Error %08X Opening %s\n", status, name); } else { /* qqq ** If we can not open the file in Shared mode, then use the FID to figure ** out the XQP file lock and it's owner. This has to be done in Kernel mode */ printf (" File locked error. Not implemented\n"); } } // open } else { // Reportiong on File lock only? printf ("Looking for File lock. Not implemented yet\n"); } // File lock, or record lock info? if (status & 1 && fab.fab$w_ifi) status = sys$close(&fab); if (nam.nam$l_fnb & NAM$M_WILDCARD) { // qqq not sure we want this if (status == RMS$_FLK) status = 1; /* locked is fine */ } if (status & 1) status = sys$search (&fab); } // while matching files if ( status == RMS$_NMF) status = 1; // No more files is fine really if ( status == RMS$_FNF) { // Let's do a special repot on file not found. status = 1; printf ("File Not Found: %s\n", name); } if (found != 1) printf ("Found %d files matching specifications.\n", found); return status; } // En of function find_some_files main(int argc, char *argv[]) { int error_flag=0, usage_flag=0, binary_flag=0, rfa_vbn = 0, rfa_id=0; int privs[] = { PRV$M_CMEXEC, 0}; int status, i, mode; item getjpi_items[] = { sizeof (int), JPI$_MODE, &mode, 0, TERMINATOR }; char *arg, *wild_carded_file_name, asctim[23+1]; short iosb[4], channel=0, retlen; char bin_key_value[256], c; $DESCRIPTOR (input, "SYS$INPUT:"); desc key_value; desc asctim_desc = { sizeof asctim, asctim }; extern char *optarg; extern int optind, optopt; char *p; /* ** Argument parsing. Here we find out what to do on which files. */ // Graph Numbers CSV-file Help Repeat-count while ((c = getopt(argc, argv, ":k:bhv")) != -1) { switch (c) { case 'k': p = optarg; // optimized away ?? rfa_vbn = atoi(p); break; case 'b': binary_flag = 1; break; case 'h': usage_flag = 1; error_flag++; break; case 'v': verbose_flag=1; break; case ':': /* symbol name argument without operand */ fprintf (stderr, "Option -%c requires an operand\n", optopt); error_flag++; break; case '?': fprintf (stderr, "Unrecognized option -%c. Try -h for help\n", optopt); error_flag++; } // switch } // while arguments if (optind == argc - 2 ) { wild_carded_file_name = argv[optind]; if (binary_flag) { char *p_in, *p_bin; int i = 0, one_byte; p_in = argv[optind + 1];; p_bin = bin_key_value; while ( 0 < sscanf ( p_in, "%02X", &one_byte ) ) { p_in += 2; *p_bin++ = one_byte; i++; } *p_bin = 0; key_value.address = bin_key_value; key_value.count = i; } else { key_value.address = argv[optind + 1]; key_value.count = strlen(argv[optind + 1]); } } else { error_flag++; } if (0==wild_carded_file_name && 0==rfa_vbn) error_flag++; if (error_flag ) { fprintf (stderr, "Usage: %s [-v] file_name \"key_value\". -h for help.\n", argv[0]); if (!usage_flag) exit (268435472); } if (usage_flag) { fprintf (stderr, "\nUsage: $ MCR RMS_LOCK_HOLDERS [-h] [-k #] [-v] \n"); fprintf (stderr, "Must provide key_value as quoted string to pass UPPERCASE and values with spaces\n"); fprintf (stderr, "Hein van den Heuvel, 29-June-2010\n\n"); fprintf (stderr, " -b Binary. Key provide as a string of 2-byte hex values.\n"); fprintf (stderr, " -k Key of Reference. Default 0 = Primary.\n"); fprintf (stderr, " -v Verbose. Report RFA, LOCK-IDs and first 50 bytes of data in record.\n"); fprintf (stderr, " -h This Text\n"); // key size // key type // vbn // id fprintf (stderr, " -v Verbose Output\n\n"); return 268435456; /* trivia... go figure */ } status = sys$getjpiw ( 0, 0, 0, getjpi_items, iosb, 0, 0); status = sys$setprv ( 1, privs, 0, 0); if (!(status & 1)) { printf ("Sorry... could not get required CMEXEC privs.\n"); return status; } status = find_some_files (wild_carded_file_name, &key_value, rfa_vbn, rfa_id ); return status; }