%TITLE 'GETCMD' MODULE GETCMD (MAIN = main, IDENT = '03-000') = BEGIN ! ! Copyright © 1992 by Hunter Goatley. This code may be freely distributed ! and modified for non-commercial purposes as long as this copyright notice ! is retained. ! !++ ! ! Facility: GETCMD ! ! Author: Hunter Goatley ! ! Date: February 5, 1992 ! ! Abstract: ! ! This program will display another process's DCL command recall ! buffer. A special kernel mode AST is queued to the target process ! to gather the information; this AST then queues another AST back ! to the requesting process to copy the information to this program's ! buffer. ! ! Modified by: ! ! 03-000 Hunter Goatley 5-FEB-1992 06:49 ! BLISS rewrite of original MACRO-32 program. ! !-- LIBRARY 'SYS$LIBRARY:LIB'; !Pull stuff from LIB ! ! Define offsets for extended ACB (starting at ACB$K_LENGTH) ! MACRO ACB_L_USERBUF = ACB$K_LENGTH, 0, 32, 0 %; !Pointer to local buff MACRO ACB_L_PID = ACB$K_LENGTH+4, 0, 32, 0 %; !PID of this process MACRO ACB_L_2ND_KAST = ACB$K_LENGTH+8, 0, 32, 0 %; !2nd KAST address MACRO ACB_L_EFN = ACB$K_LENGTH+12, 0, 32, 0 %; !Event flag to set MACRO ACB_L_IMGCNT = ACB$K_LENGTH+16, 0, 32, 0 %; !Process image count MACRO ACB_L_USERPTR = ACB$K_LENGTH+20, 0, 32, 0 %; !Address for offset MACRO ACB_L_RECALLPTR = ACB$K_LENGTH+24, 0, 32, 0 %; !Offset into buffer LITERAL ACB_S_DCLBUFFER = 1028; !Actual size is 1025 MACRO ACB_T_DCLBUFFER = ACB$K_LENGTH+28, 0, 0, 0 %; !Reserve space LITERAL ACB_K_LENGTH = ACB$K_LENGTH+28+ACB_S_DCLBUFFER; MACRO ACB_T_ASTCODE = ACB_K_LENGTH, 0, 0, 0 %; !Stick AST code at end ! ! MACROS ! LITERAL true = 1, false = 0; MACRO $STATICDESC (len, addr) = !Static descriptor declaration $BBLOCK[DSC$C_S_BLN] PRESET ([DSC$W_LENGTH] = len, [DSC$B_DTYPE] = DSC$K_DTYPE_T, [DSC$B_CLASS] = DSC$K_CLASS_S, [DSC$A_POINTER]= addr) %, G_HAT = !General addressing mode ADDRESSING_MODE (GENERAL) %; ! ! Macro BITFLD takes a normal structure reference macro and converts it ! to a bit field reference (it leaves out the byte offset). ! MACRO BITFLD (b,o,s,t) = %; %SBTTL 'Linkages' ! ! Define JSB linkages for external and internal routines. ! LINKAGE jsb_qast = JSB ( REGISTER = 2, !Priority Increment REGISTER = 5; !Pointer to AST block REGISTER = 4) : !Target's PCB NOPRESERVE(1,3) NOTUSED(6,7,8,9,10,11), ast_jsb = JSB ( REGISTER = 4, !PCB is passed in R4 REGISTER = 5) : !ACB is passed in R5 NOPRESERVE(1,2,3,4,5) !Don't save registers NOTUSED(6,7,8,9,10,11), alon_call = JSB ( REGISTER = 1; !Input is size to allo REGISTER = 1, !Returns size in R1 REGISTER = 2), !... & addr in R2 deal_call = JSB ( REGISTER = 0) : !Address of block NOTUSED(1,2,3,4,5,6,7,8,9,10,11), pid_call = JSB ( REGISTER = 0; !PID passed in R0 REGISTER = 0) : !PCB returned in R0 PRESERVE(1,2,3,4,5), !R0--R5 are OK chsep_call = JSB ( REGISTER = 0, !New priority in R0 REGISTER = 4) : !PCB address NOPRESERVE(2) PRESERVE(1,3,5), clref_call = JSB ( REGISTER = 3, !EFN passed in R3 REGISTER = 4), !PCB addr in R4 postef_call = JSB ( REGISTER = 1, !The PID in R1 REGISTER = 2, !Priority increment REGISTER = 3) : !The EFN in R3 NOTUSED(4,5,6,7,8,9,10,11); %SBTTL 'Forwards and Externals' FORWARD ROUTINE main, !Main entry point list_cmds, !List the commands krnl_get_dclbuffer, !Kernel mode routine ast_code : NOVALUE ast_jsb, !Kernel AST for target prc ast2_code : NOVALUE ast_jsb !Second AST for original prc ; EXTERNAL ROUTINE LIB$GET_FOREIGN : G_HAT, !Get foreign command line LIB$PUT_OUTPUT : G_HAT, !Write to SYS$OUTPUT OTS$CVT_TZ_L : G_HAT; !Convert ASCII # to hex EXTERNAL ROUTINE EXE$ALONONPAGED : G_HAT alon_call, !Allocate from non-paged pool EXE$DEANONPAGED : G_HAT deal_call, !Deallocate non-paged EXE$EPID_TO_PCB : G_HAT pid_call, !Convert EPID to PCB address EXE$IPID_TO_PCB : G_HAT pid_call, !Convert IPID to PCB address SCH$CHSEP : G_HAT chsep_call, !Make process executable SCH$CLREF : G_HAT clref_call, !Clear Event Flag SCH$POSTEF : G_HAT postef_call, !Post Event Flag SCH$QAST : G_HAT jsb_qast; !Queue an AST BUILTIN MOVPSL; EXTERNAL CTL$GL_PCB : G_HAT REF $BBLOCK, !P1 pointer to PCB CTL$GL_PHD : G_HAT REF $BBLOCK; !P1 pointer to PHD %SBTTL 'MAIN' ROUTINE main = BEGIN !+ ! ! Routine: MAIN ! ! Functional description: ! ! This is the main routine for GETCMD. It calls LIB$GET_FOREIGN to ! retrieve the PID of the target process, converts it to binary, ! calls KRNL_GET_DCLBUFFER to queue the necessary ASTs, waits for ! an event flag to be set, and calls LIST_CMDS to display the ! target process's command recall buffer. ! ! Environment: ! ! User-mode. ! ! Formal parameters: ! ! None. ! ! Implicit inputs: ! ! None. ! ! Outputs: ! ! None. ! ! Returns: ! ! R0 - Status ! !- LITERAL event_flag = 13; !Pick some event flag OWN pid : UNSIGNED LONG, !Longword for target PID pid_buff : $BBLOCK[8], !Buffer for ASCII PID pid_d : $staticdesc(8,pid_buff), !Descriptor for buffer cmd_buffer : $BBLOCK[1025], !Local copy of DCL recall buffer recall_ptr : UNSIGNED LONG, !Offset of last command krnl_arglst : VECTOR[5,LONG] !KRNL_GET_DCLBUFFER arglst INITIAL(4, !... Four arguments follow event_flag, !... The event flag to wait for pid, !... The PID of the target prc cmd_buffer, !... Address to receive buffer recall_ptr); !... Address to receive offset REGISTER status : UNSIGNED LONG; !The status ! ! Call LIB$GET_FOREIGN to pick up the target process's PID. If the PID ! is not given on the command line, the user will be prompted to enter it. ! status = LIB$GET_FOREIGN (pid_d, %ASCID'_PID: ', pid_d); IF NOT(.status) !Was an error returned? THEN !If so, check to see if user RETURN( (IF (.status EQLU RMS$_EOF) !... entered ^Z; if so, change THEN !... it to SS$_NORMAL and SS$_NORMAL !... return ELSE !Otherwise, just return .status)); !... the error status = OTS$CVT_TZ_L (pid_d, pid); !Convert from ASCII to hex IF NOT(.status) THEN RETURN (.status); !Return any error ! ! Now switch to kernel mode to queue an AST to the target process. ! status = $CMKRNL (ROUTIN = krnl_get_dclbuffer, !Call krnl_get_dclbuffer ARGLST = krnl_arglst); !... with its arglst IF (.status) !AST queued successfully? THEN !If so, then wait for the status = $WAITFR (EFN = event_flag); !... event flag to be set IF (.status) !If successful, then dump THEN !... the commands to SYS$OUTPUT status = list_cmds (cmd_buffer, recall_ptr); RETURN (.status); !Return status back to VMS END; !End of routine %SBTTL 'LIST_CMDS' ROUTINE list_cmds (buf_a, ptr_a) = BEGIN !+ ! ! Routine: LIST_CMDS ! ! Functional Description: ! ! This routine mimics the RECALL/ALL command. It steps through a ! command recall buffer, writing each command to SYS$OUTPUT. The ! commands are printed in LIFO order (last in, first out, so the ! most recently executed command is printed first). ! ! Environment: ! ! User-mode. ! ! Formal parameters: ! ! buf_a - Address of the command buffer. By reference. ! ptr_a - Offset in buffer of the end of the last command. ! Longword, by reference. ! ! Implicit inputs: ! ! PRC_S_COMMANDS ! ! Outputs: ! ! None. ! ! Returns: ! ! R0 - Status ! !- BIND buffer = .buf_a : REF $BBLOCK, lastptr = .ptr_a : UNSIGNED LONG; EXTERNAL PRC_S_COMMANDS : ADDRESSING_MODE (ABSOLUTE); BIND fao_ctr_str = %ASCID'!3UB !AS', no_commands = %ASCID'No DCL commands to recall'; MACRO overflow (address) = !Macro to check for (address) GEQU end_buff_ptr %, !... wrapping past end underflow (address) = !Macro to check for (address) LSSU buffer %; !... wrapping past begin LOCAL loop : UNSIGNED LONG, !Loop control flag src : REF $BBLOCK, !Work pointer into buffer save_lastptr : REF $BBLOCK, !Saved address of last command end_buff_ptr : UNSIGNED LONG !Pointer to last byte in buffer INITIAL(buffer + PRC_S_COMMANDS), length : UNSIGNED LONG, !Length of each command status : UNSIGNED LONG, !Status from LIB$PUT_OUTPUT cmd_buffer : $BBLOCK[256], !Local copy of each command cmd_buffer_d : $staticdesc(256,cmd_buffer), fao_buffer : $BBLOCK[270], !Output buffer for FAOL fao_buffer_d : $staticdesc(260,fao_buffer), faol_prmlst : VECTOR[2,LONG] !$FAOL parameter list INITIAL (0, !... Command number cmd_buffer_d); !... Command descriptor IF (.lastptr EQLU 0) !If the offset is 0, then THEN !... there are no commands RETURN (LIB$PUT_OUTPUT(no_commands)); !... in the buffer. Return. save_lastptr = buffer + .lastptr; !Save address of beginning ! save_lastptr = .lastptr; src = .save_lastptr; !And start there loop = true; !Initialize loop control WHILE .loop DO !Loop until no more commands... BEGIN src = .src - 1; !Point to the length byte IF underflow(.src) !See if we passed the beginning THEN !... of the buffer. If so, we src = .end_buff_ptr - 1; !... need to point to the end length = .src[0,0,8,0]; !Now get the command length IF (.length NEQU 0) !If we have a command, THEN !... then process it BEGIN IF underflow(.src - .length) !If we'll underflow, we must THEN !... copy the string in two BEGIN !... pieces LOCAL temp1 : UNSIGNED LONG, temp2 : UNSIGNED LONG; temp1 = (.src - buffer); !Size of second piece of cmd temp2 = .length - .temp1; !Size of command at buffer end CH$MOVE (.temp2, .end_buff_ptr - .temp2, cmd_buffer); CH$MOVE (.temp1, buffer, cmd_buffer + .temp2); src = .end_buff_ptr - .temp2; !Now point src to the beginning END ELSE !No underflow---just copy it BEGIN src = .src - .length; CH$MOVE (.length, .src, cmd_buffer); !Copy the string END; ! ! Here, src is pointing to the first character in the cmd string. ! Now back to the preceding length byte and check for overflow.... ! src = .src - 1; IF underflow(.src) THEN src = .end_buff_ptr-1; ! ! And back up so src is pointing to the null byte (with overflow ! check). ! src = .src - 1; IF underflow(.src) THEN src = .end_buff_ptr-1; ! ! Now src points to 0, so we're ready to process the next command. ! ! This command is formatted using $FAOL and written to SYS$OUTPUT. ! faol_prmlst[0] = .faol_prmlst[0] + 1; !Increment the command number fao_buffer_d[DSC$W_LENGTH] = 260; !Reset length of $FAO buffer cmd_buffer_d[DSC$W_LENGTH] = .length; !Copy cmd length to descriptor IF ($FAOL (CTRSTR = fao_ctr_str, !Format the string OUTLEN = fao_buffer_d, !... and store in fao_buffer OUTBUF = fao_buffer_d, !... PRMLST = faol_prmlst)) !... THEN !If format was OK, then write status = LIB$PUT_OUTPUT (fao_buffer_d); !... to SYS$OUTPUT ! ! To avoid a potential infinite loop, we need to make sure that ! we haven't worked our way through the buffer and landed on our ! starting point. ! IF (.src EQLU .save_lastptr) !Are we back where we started? THEN !If so, we need to exit the loop = false; !... (infinite) loop END ELSE !Length was 0, which means we loop = false; !... are finished looping END; !WHILE loop DO RETURN (SS$_NORMAL); END; %SBTTL 'KRNL_GET_DCLBUFFER' FORWARD wslock_end; ROUTINE krnl_get_dclbuffer (efn, pid_a, buf_a, ptr_a) = !+ ! ! Routine: KRNL_GET_DCLBUFFER ! ! Functional Description: ! ! This routine queues a special Kernel AST to the target process. ! It peforms the following steps: ! ! 1. Make sure the target process exists ! 2. Clear the specified Event Flag ! 3. Allocates an extended ACB with enough room for a command ! buffer and the code for two ASTs. ! 4. Initializes the ACB and copies the AST code to it. ! 5. Queues the AST to the target process. ! ! Environment: ! ! Kernel-mode, elevated IPL (2 and IPL$_SCHED). ! ! Formal parameters: ! ! efn - The event flag number. Longword, by value. ! pid_a - EPID of the process. Longword, by reference. ! buf_a - 1025-byte buffer to receive DCL command buffer. By reference. ! ptr_a - Longword to receive PRC_L_RECALLPTR. By reference. ! ! Implicit inputs: ! ! CTL$GL_PCB, CTL$GL_PHD ! ! Outputs: ! ! None. ! ! Returns: ! ! R0 - Status ! ! Side effects: ! ! Raises IPL to 2 and IPL$_SCHED; acquires and releases the SCHED ! spinlock. ! !- BEGIN MAP efn : UNSIGNED LONG; !Event flag (by value) BIND pid = .pid_a : UNSIGNED LONG, !Target EPID bufadr = .buf_a : $BBLOCK, !Local buffer ptradr = .ptr_a : $BBLOCK; !Local pointer LOCAL old_ipl : UNSIGNED LONG, !Place to save old IPL wslock_range : VECTOR[2,LONG] !Addr range for $LKWSET INITIAL (krnl_get_dclbuffer, !Lock down all code wslock_end); !... from here to end REGISTER temp_ptr : REF $BBLOCK, temp : UNSIGNED LONG, ast_size : UNSIGNED LONG, acb_size : UNSIGNED LONG, acb_addr : REF $BBLOCK, ipid : UNSIGNED LONG, pcb : REF $BBLOCK, status : UNSIGNED LONG; ! ! Page faulting is not allowed at IPLs above 2. Because this routine ! raises IPL to IPL$_SCHED, we must ensure that the routine is locked ! into memory. The $LKWSET system service is called to lock down all ! the pages for this routine and the two AST routines. The AST routines ! are only accessed from IPL 2, but let's be safe and lock them down too. ! status = $LKWSET (INADR = wslock_range, !Lock this routine into RETADR = wslock_range); !... memory IF NOT(.status) THEN RETURN(.status); !Return any errors ! ! Before allocating non-paged pool and initializing the extended ACB, ! check to make sure that the target process exists and get its internal ! PID (the IPID). ! ! The process could be deleted while we're in this routine, but we ! check its validity up front so that we don't waste the processing ! time for a process that never existed. ! IF ((ipid = .pid) EQLU 0) !If the specified PID is 0, THEN !... then use our own process ! ! It's inefficient to queue the ASTs to our own process when we could ! just go get the buffer. However, it *is* useful for debugging. ! ipid = .CTL$GL_PCB[PCB$L_PID] !This process is the target ELSE BEGIN ! ! Get the IPID of the target process. We call EXE$EPID_TO_PCB instead ! of EXE$EPID_TO_IPID because the latter doesn't check to see if either ! PID is valid, while the former ensures that the IPID matches the IPID ! stored in the PCB, returning 0 if the process doesn't exist. ! ! The SCHED spinlock is held while we copy the PID out of the PCB. ! Because it is released afterward, it is possible that the process ! could be deleted before the AST is queued. SCH$QAST will catch ! this condition. ! $SYS_LOCK (LOCKNAME = SCHED, !Grab the SCHED spinlock SAVIPL = old_ipl); !... Save IPL for restoring ! ! EXE$EPID_TO_PCB is described in module SYSPCNTRL in the VMS source. ! EXE$EPID_TO_PCB (.ipid; pcb); !Convert to PCB address IF (.pcb NEQU 0) !Is the process real? THEN !If so, copy the IPID out ipid = .pcb[PCB$L_PID]; !... of the PCB $SYS_UNLOCK (LOCKNAME = SCHED, !Release the SCHED spinlock NEWIPL = .old_ipl); !... and restore the IPL IF (.pcb EQLU 0) !If there was no process with THEN !... the specified PID, then RETURN (SS$_NONEXPR); !... return an error END; ! ! At this point, the process exists (or did) and its IPID is stored in ipid. ! ! Clear the specified event flag using SCH$CLREF. SCH$CLREF is described ! in module SYSEVTSRV; it accepts two inputs: R3 = event flag number, ! R4 = PCB address. On success, it returns via RSB, otherwise it RETs, ! eliminating the need to check for errors because it will RETurn to ! the CMKRNL dispatcher. ! status = SCH$CLREF (.efn, .CTL$GL_PCB); !Clear the event flag !No need to check status.... ! ! Now raise IPL to 2 to avoid process deletion while non-paged pool is ! is held. ! $SETIPL (NEWIPL = IPL$_ASTDEL, ENVIRON=UNIPROCESSOR); ! ! Now allocate an extended ACB from non-paged pool by calling ! EXE$ALONONPAGED (in module MEMORYALC in SYS). ! ast_size = CH$DIFF(wslock_end, ast_code); !Size of both AST routines status = EXE$ALONONPAGED (.ast_size + ACB_K_LENGTH; acb_size, acb_addr); IF (.status) !Enough memory allocated? THEN !If so, begin filling in the BEGIN !... fields in the ACB acb_addr[ACB$L_PID] = .ipid; !Save the PID of target process acb_addr[ACB$W_SIZE] = .acb_size; !Save size of ACB acb_addr[ACB$B_TYPE] = DYN$C_ACB; !Set type to ACB MOVPSL(temp_ptr); !Get the current PSL temp = .temp_ptr BITFLD(PSL$V_CURMOD); !Get our access mode (kernel) acb_addr[ACB$B_RMOD] = ACB$M_KAST OR .temp; !Store in ACB with Special KAST ! ! Now fill in the extended ACB fields ! acb_addr[ACB_L_EFN] = .efn; !Store the event flag to set acb_addr[ACB_L_USERBUF] = bufadr; !Store the process buffer addr acb_addr[ACB_L_USERPTR] = ptradr; !Store the process pointer addr acb_addr[ACB_L_PID] = .CTL$GL_PCB[PCB$L_PID]; !Store our PID acb_addr[ACB_L_IMGCNT] = .CTL$GL_PHD[PHD$L_IMGCNT]; !And our image count ! ! Now copy the AST routines to the extended ACB. ! CH$MOVE(.ast_size, ast_code, acb_addr[ACB_T_ASTCODE]); acb_addr[ACB$L_KAST] = acb_addr[ACB_T_ASTCODE]; !AST address acb_addr[ACB_L_2ND_KAST] = acb_addr[ACB_T_ASTCODE] + !2nd AST address CH$DIFF(ast2_code, ast_code); ! ! Now we're ready to queue the AST to the target process. We could just ! call SCH$QAST, but it doesn't check to see if the target process is ! swapped out of memory. To avoid swapping the process into memory just ! to get its recall buffer, let's make those checks and then queue it.... ! $SYS_LOCK (LOCKNAME = SCHED, !Grab the SCHED spinlock again SAVIPL = old_ipl); !... and save the IPL EXE$IPID_TO_PCB (.ipid; pcb); !Look up the PCB again IF ((.pcb NEQU 0) AND !If there is a PCB and it is (.ipid EQLU .pcb[PCB$L_PID]) AND !... still the same process NOT(.pcb[PCB$V_DELPEN]) AND !... and delete is not pending NOT(.pcb[PCB$V_SUSPEN]) AND !... and suspend is not pending (.pcb[PCB$W_STATE] NEQU SCH$C_SUSP) AND !... and state is not SUSP, (.pcb[PCB$W_STATE] NEQU SCH$C_SUSPO) AND!... SUSPO, or MWAIT (.pcb[PCB$W_STATE] NEQU SCH$C_MWAIT)) !... THEN !Then queue the AST with a BEGIN !... priority increment status = SCH$QAST (PRI$_TICOM, .acb_addr; pcb); ! ! If the AST was successfully queued to the target process and ! the process is in the COM state, temporarily boost its priority ! to match ours so this runs at quickly as possible. ! IF ((.status) AND !If AST was queued and the ((.pcb[PCB$W_STATE] EQLU SCH$C_COM) OR !... process is in (.pcb[PCB$W_STATE] EQLU SCH$C_COMO))) !... COM state THEN IF ((.CTL$GL_PCB[PCB$B_PRI] LSSU 16) AND (.CTL$GL_PCB[PCB$B_PRI] GTRU .pcb[PCB$B_PRI])) THEN !Boost its priority ! SCH$CHSEP is described in module RSE SCH$CHSEP (.CTL$GL_PCB[PCB$B_PRI], .pcb); END ELSE !Here, the target process was status = SS$_NONEXPR; !... deleted or swapped out $SYS_UNLOCK (LOCKNAME = SCHED, !Release the SCHED spinlock NEWIPL = .old_ipl); !... and restore IPL IF NOT(.status) !If an error occurred queueing THEN !... the AST, then release the EXE$DEANONPAGED (.acb_addr); !... ACB back to nonpaged pool END; $SETIPL (NEWIPL = 0, ENVIRON=UNIPROCESSOR); !Lower IPL from 2 to 0 $ULWSET (INADR = wslock_range); !Unlock these pages RETURN (.status); !Return the final status END; %SBTTL 'AST_CODE' ROUTINE ast_code (pcb, acb) : NOVALUE ast_jsb = BEGIN !+ ! ! Routine: AST_CODE ! ! Functional Description: ! ! This routine executes as a special Kernel-mode AST in the context ! of the target process. It copies the process's DCL command recall ! buffer into the extended ACB and then queues an AST back to the ! requesting process to return the buffer to it. ! ! This routine duplicates the MACRO trick of JMPing into SCH$QAST ! and letting its RSB return to the ASTDEL code. This eliminates ! the problems of returning to code in memory that may have already ! been deallocated to nonpaged pool. ! ! Environment: ! ! Kernel-mode, IPL 2. Called via JSB. ! ! Formal parameters: ! ! R4 - PCB address ! R5 - ACB address ! ! Implicit inputs: ! ! CTL$AG_CLIDATA, PPD$L_PRC, PRC_G_COMMANDS, PRC_S_COMMANDS, ! PRC_L_RECALLPTR ! ! Outputs: ! ! None. ! ! Returns: ! ! R0 - Status ! ! Side effects: ! ! Re-uses the ACB to queue an AST back to the requesting process. ! !- MAP pcb : REF $BBLOCK, acb : REF $BBLOCK; REGISTER src : REF $BBLOCK, dst : REF $BBLOCK, prc : REF $BBLOCK; EXTERNAL CTL$AG_CLIDATA : G_HAT REF $BBLOCK, !Process Permanent Data PPD$L_PRC : ADDRESSING_MODE (ABSOLUTE), !Pointer to PRC PRC_L_RECALLPTR : ADDRESSING_MODE (ABSOLUTE), !Address of last cmd PRC_G_COMMANDS : ADDRESSING_MODE (ABSOLUTE), !Address of cmd buffer PRC_S_COMMANDS : ADDRESSING_MODE (ABSOLUTE); !Size of cmd buffer ! ! First, we need to get the pointer to the process's PRC region. This ! address is stored in the PPD at offset PPD$L_PRC. The global symbol ! CTL$AG_CLIDATA points to the PPD. ! prc = CTL$AG_CLIDATA; !Point to the PPD prc = .(.prc + PPD$L_PRC); !Now point to the PRC ! ! The command recall buffer begins at offset PRC_G_COMMANDS. The pointer ! to the end of the last command in the buffer is stored at offset ! PRC_L_RECALLPTR. We need to convert this PRC_L_RECALLPTR address into ! a relative offset into the buffer. ! acb[ACB_L_RECALLPTR] = .(.prc + PRC_L_RECALLPTR) - (.prc + PRC_G_COMMANDS); ! ! Now we're ready to copy the whole command recall buffer to our extended ! ACB. A loop instead of CH$MOVE to keep register usage within R0--R5. ! (When CH$MOVE is used, BLISS saves R6 and changes the JMP to SCH$QAST ! to a JSB. Hence the reason for the loop.) ! src = .prc + PRC_G_COMMANDS; !Point to the recall buffer dst = acb[ACB_T_DCLBUFFER]; !Point to the buffer in the ACB DECR i FROM PRC_S_COMMANDS TO 0 !Copy PRC_S_COMMANDS bytes DO !... from the recall buffer CH$WCHAR_A(CH$RCHAR_A(src),dst); !... to the buffer in the ACB ! ! Not used for reason given above. ! ! CH$MOVE (PRC_S_COMMANDS, .prc + PRC_G_COMMANDS, acb[ACB_T_DCLBUFFER]); ! ! Now reset the ACB for queueing back to the original process. ! acb[ACB$L_KAST] = .acb[ACB_L_2ND_KAST]; !KAST field points to 2nd AST acb[ACB$L_PID] = .acb[ACB_L_PID]; !Move requestor's PID acb[ACB$B_RMOD] = .acb[ACB$B_RMOD] OR ACB$M_KAST; !Make sure KAST is set ! ! Now we're ready to JMP into SCH$QAST to queue the AST back. BLISS is ! tricked into generating a JMP so that control does *not* return back ! to this routine. It is possible on SMP machines that the second AST ! *could* deallocate the extended ACB before SCH$QAST returned control ! to this routine, resulting in a return to memory deallacted back to ! pool. The issue is avoided by JMPing into SCH$QAST, which will then ! RSB to the ASTDEL code. ! SCH$QAST (0, .acb; pcb); !JMP into SCH$QAST and let it return !... to the caller END; %SBTTL 'AST2_CODE' ROUTINE AST2_CODE (pcb, acb) : NOVALUE ast_jsb = BEGIN !+ ! ! Routine: AST2_CODE ! ! Functional Description: ! ! This routine executes as a special Kernel-mode AST in the context ! of the requesting process. It copies the target's command buffer ! from the extended ACB back into this program's buffer. ! ! This routine duplicates the MACRO trick of JMPing into EXE$DEANONPAGED ! and letting its RSB return to the ASTDEL code. This eliminates ! the problems of returning to code in memory that may have already ! been deallocated to nonpaged pool. ! ! Environment: ! ! Kernel-mode, IPL 2. Called via JSB. ! ! Formal parameters: ! ! R4 - PCB address ! R5 - ACB address ! ! Implicit inputs: ! ! Addresses stored in the extended ACB ! ! Outputs: ! ! None. ! ! Returns: ! ! R0 - Status ! ! Side effects: ! ! JMPs into EXE$DEANONPAGED to deallocate the extended ACB back to ! nonpaged pool. ! !- MAP pcb : REF $BBLOCK, acb : REF $BBLOCK; EXTERNAL PRC_S_COMMANDS : ADDRESSING_MODE (ABSOLUTE); !Size of command buffer REGISTER src : REF $BBLOCK, dst : REF $BBLOCK; src = .pcb[PCB$L_PHD]; !Get the PHD address ! ! If the original process is no longer running the same image that queued ! the ASTs, then we don't want to do anything except deallocate the ! extended ACB (otherwise, we'd copy the DCL command buffer to an address ! that is no longer valid). To ensure the same image is running, compare ! the IMGCNT fields. ! IF (.src[PHD$L_IMGCNT] EQLU .acb[ACB_L_IMGCNT]) !If the image count is THEN !... the same, we're BEGIN !... still running .acb[ACB_L_USERPTR] = .acb[ACB_L_RECALLPTR]; !Copy the offset src = acb[ACB_T_DCLBUFFER]; !Point to ACB buffer dst = .acb[ACB_L_USERBUF]; !Point to user buffer DECR i FROM PRC_S_COMMANDS TO 0 !Copy the buffer DO !Again, a loop is used CH$WCHAR_A(CH$RCHAR_A(src),dst); !... to avoid MOVC3 ! ! The process is waiting for the event flag to be set, so post it ! to indicate that the recall buffer has been successfully copied. ! SCH$POSTEF (.acb[ACB_L_PID], 0, .acb[ACB_L_EFN]); END; EXE$DEANONPAGED (.acb); !JMP to deallocate the extended ACB END; !From LOGIN PSECT OWN = $CODE$(NOWRITE,EXECUTE); ! Cause this OWN to occur here OWN wslock_end : UNSIGNED LONG; ! Used to lock down previous code pages PSECT OWN = $OWN$(WRITE,NOEXECUTE); ! Restore normal own psect END !End of module BEGIN ELUDOM !End of module