Figure Two - WINDOW_CLIENT.COM $! $! WINDOW_CLIENT.COM $! $! Author: $! $! John McMahon Phone: (408) 427-4366 $! TGV, Incorporated FAX: (408) 427-4365 $! 603 Mission Street E-Mail: MCMAHON@TGV.COM $! Santa Cruz, California 95060 $! $! This program is based on code developed by John McMahon while under contract $! to the Advanced Data Flow Technology Office - NASA Goddard Space Flight $! Center in Greenbelt, Maryland during October 1989. $! $! Usage: $! $! $ WINDOW :== @WINDOW_DIRECTORY:WINDOW_CLIENT $! $ WINDOW nodename $! $! "nodename" refers to the remote node that the user is $! requesting a DECterm from. if the user does not specify $! a nodename, they will be prompted for it. $! $! Function: $! $! This program takes a remote DECnet nodename as input. A $! connection is opened to the DECterm server program (DECnet $! object "DECterm"). A VMS status code is read back from $! the remote server and interpreted. $! $! Implicit Inputs: $! $! None. $! $! Implicit Outputs: $! $! When the DECterm server program on the remote node is triggered $! a DECterm window is created on this node. $! $! Side Effects: $! $! None. $! $!----------------------------------------------------------------------------- $ SET NoON $! $! Default_Node is the nodename used if the user hits return at the $! nodename prompt. $! $ Default_Node == "REMOTE::" $! $! Check to see if the user entered a nodename on the command line. $! if not, prompt for a nodename. $! $ If P1 .Eqs. "" Then Inquire/Nopunc P1 "Remote Node [''Default_Node'] ? " $! $! If the user hit return at the prompt, assume the default node. $! $ If P1 .Eqs. "" Then P1 = Default_Node $! $! Strip off the double colons in the nodename if they have been specified. $! $ P1 = P1 - "::" $! $! Try to open the remote server using the "DECTERM" object name. $! If successful, jump to the good connection routine. $! If not, jump to the "try numeric object" routine. $! $! The filename we use to open the remote DECnet object takes the form of: $! $! DECNETNODENAME""::"OBJECT=" $! $! The first set of double quotes tells DECnet to NOT send any proxy $! information to the remote node. Since the remote object runs under a $! special privileged account, proxy would not work properly in most $! cases. $! $! We try two object names. First "DECTERM", and then "225". If the DECTERM $! object is defined in the local NCP database, then the DECTERM name will work $! fine. If not, DECnet returns a NOSUCHOBJ error... We then fall back $! on the object number, which is not checked against the local NCP database. $! $ Open/Read/Write/Error=Try_Number Network_File 'p1'""::"DECTERM=" $ Goto Good_Connection $ Try_Number: $! $! If the connection to "DECTERM" fails, we try accessing the object by it's $! number "225". If we succeed, we jump to the good connection routine. $! If we fail, we assume that the remote node is unreachable and give up. $! $ Open/Read/Write/Error=Unable Network_File 'p1'""::"225=" $ Goto Good_Connection $ Unable: $! $! This routine is called after both attempts at network connections have $! failed $! $ Write Sys$Output "Unable to establish connection to node ''p1'." $ Exit $ Good_Connection: $ Write Sys$Output "Connection opened to node ''p1'." $! $! When a connection is successfully established this routine is called. $! First we read the remote VMS status from the server, and then close off the $! network connection. $! $ Read Network_File Remote_Status $ If (.not. $Status) $ Then $ Remote_Status = $Status $ Write Sys$Output "Error reading data from server..." $ Endif $ Close Network_File $ Write Sys$Output " " $! $! Interpret the status from the server $! $ If (.not. Remote_Status) $ Then $ Write Sys$Output "Status from remote server was: ",remote_status $ Write Sys$Output "(",F$Message(remote_status),")" $ Write Sys$Output "Command did not complete properly." $ Else $ Write Sys$Output "Remote server acknowledged startup of window." $ Endif $ exit == [Figure 3] $! $! WINDOW_SERVER.COM $! $! Author: $! $! John McMahon Phone: (408) 427-4366 $! TGV, Incorporated FAX: (408) 427-4365 $! 603 Mission Street E-Mail: MCMAHON@TGV.COM $! Santa Cruz, California 95060 $! $! This program is based on code developed by John McMahon while under contract $! to the Advanced Data Flow Technology Office - NASA Goddard Space Flight $! Center in Greenbelt, Maryland during October 1989. $! $! Usage: $! $! Executed by DECnet. This program is started when a client $! asks for a DECnet connection to object "DECTERM" or "225". $! $! Function: $! $! This program opens SYS$NET to complete a DECnet task-to-task $! connection. SYS$NET is then parsed to find out who connected $! to this program. It then determines if a terminal controller $! process is needed. Once the terminal controller is ready $! the WINDOW_CREATE program is called to tell the terminal $! controller to create a new DECterm window. Finally, a $! VMS status is written back to SYS$NET and the connection $! is closed. $! $! Output: $! $! Implicit Inputs: $! $! SYS$NET is used as the source of client nodename. $! $! Implicit Outputs: $! $! The server creates a DECterm window on the client node. $! The NETSERVER.LOG file contains output from the command $! procedure. $! $! $! Side Effects: $! $! None. $! $!----------------------------------------------------------------------------- $! Turn Verify Off $ Set Noverify $! Set the output rate for the log file to a reasonable value $ SET OUTPUT_RATE=00:00:05.00 $! Establish An Error Handler $ On Error Then Goto Error_Exit $! Complete The Network Connection By Opening SYS$NET $! This sends a signal back to the client indicating that the channel is open $! If we didn't do this, the client would time out. $ Open/Read/Write Network_Channel SYS$NET $! $! All log files are written to the SYS$LOGIN directory for the special $! network object account (X11_TERMINAL). We purge files older than a day. $! $ Purge/Log/Before=Yesterday SYS$LOGIN:*.* $! $! Write (to NETSERVER.LOG) the current version of this file, and last revision $! date. $! $ Procedure_Filename = F$Environment("Procedure") $ Write Sys$Output Procedure_Filename,- " Was Last Revised On ",- F$File_Attribute(Procedure_Filename,"RDT") $! $! Write (to NETSERVER.LOG) what node we are running on and process information. $! This is helpful if you are running in a VAXcluster and use a common account $! for the DECterm object. $! $ This_Node = F$GETSYI("NODENAME") $ Write Sys$Output "This is node ",This_Node $! $! Determine where the connection came from. $! $! First... write the value of SYS$NET to NETSERVER.LOG for debugging $! information $ Write Sys$Output "SYS$NET is currently defined as:" $ SHOW LOGICAL SYS$NET $! $! Parsing the Network Control Block (SYS$NET) to get the client information. $! $! NOTE: DECnet experts may question why we use this technique instead of $! using the logical names SYS$REM_NODE and SYS$REM_ID. Experiments with $! these logical names indicate that they are only updated when a new $! server process is created. Since DECnet will reuse processes when they $! are available, this makes the logical names unreliable as a source of $! information. $! $! The Network Control Block (SYS$NET) has the following partial format: $! $! Field: Length: Comment: $! Nodename Maximum 6 (Assumed Minimum 1) Character string $! ::"0= Constant 5 Constant $! Username Maximum 12 (Assumed Minimum 1) Character String $! (May be padded with spaces) $! / Constant 1 Character String $! (The rest of the fields are not needed) $! $! Load the symbol NCB_STRING with the contents of the NCB $ NCB_STRING = F$Trnlnm("Sys$Net") $! Locate the first colon in the string. This points to the END of $! the nodename field in the NCB $ Colon_Location = F$Locate(":",NCB_STRING) $! Extract the nodename from the network control block. $ Client_NODE = F$Extract(0,Colon_Location,NCB_STRING) $! Chop off the nodename and constant strings off the NCB. This leaves $! the username, the slash and the unused trailer fields. This simplifies $! the parsing of the username. $ NCB_STRING = F$Extract(- Colon_Location+5,- F$Length(NCB_STRING),- NCB_STRING) $! Locate the first slash in the string. This points to the END of $! the username field in the truncated NCB $ Slash_Location = F$Locate("/",NCB_STRING) $! Extract the username from the truncated network control block. $ Client_User = F$Extract(0,Slash_Location,NCB_STRING) $! Write to NETSERVER.LOG who sent the request $ Write Sys$Output "Request from ",Client_User," at ",Client_Node $! $! We now know who requested the window. $! $! Now we test for the existance of a terminal emulator for CLIENT_NODE. $! $! Note: Most of the DECterm creation code is based on sample routines $! provided by Digital in Section 4.8 of the VMS 5.2 Release Notes. $! Abbreviated samples can be found in the VMS 5.3 supplement to the $! DECwindows programming documentation. $! $! First check to see if there is a logical name pointing to a controller $! mailbox. The logical name format is: $! $! DECW$DECTERM_MAILBOX_nodename::server.display $! $! In this program server and display are assumed to be zero (the default), $! and nodename is equated to CLIENT_NODE $! $ Write Sys$Output "Testing for controller existance." $ TERMINAL_EMULATOR_MAILBOX = - F$Trnlnm("decw$decterm_mailbox_''client_node'::0.0") $! $! If the logical name exists, verify that the mailbox specified exists. $! If it does, we assume the controller is healthy and we do not start a new $! one. $! $ if TERMINAL_EMULATOR_MAILBOX .nes. "" $ then $ if f$getdvi(terminal_emulator_mailbox,"exists") then - goto controller_exists $ endif $! $! $! $! $ Write Sys$Output "Starting a new controller." $! $! Create a unique file name for the controller command procedure $! We use the process ID and the current time as the unique elements. $! The subtraction of the spaces, colons, dashes, etc is to make the time $! a valid subpart of a VMS file name. $! $! the local node and client node are included to ease debugging... $! $ Controller_Command_Procedure_Name = - "Sys$Login:COM_L_" + THIS_NODE + "_R_" + CLIENT_NODE +- "_" + F$Getjpi(0,"pid") + "_" +- (F$Cvtime(,,"TIME") - ":" - ":" - ".") + ".TMP" $! $! Translate any logical names in the command procedure name $! The RUN LOGINOUT call (later on in the procedure) usually fails if you $! use logical names. $! $ Controller_Command_Procedure_Name = - F$Parse(Controller_Command_Procedure_Name,,,,"NO_CONCEAL") $! $! Do the same type of thing for the SYS$ERROR file for the controller $! $ CONTROLLER_ERROR_NAME = - "Sys$Login:ERR_L_" + THIS_NODE + "_R_" + CLIENT_NODE +- "_" + F$Getjpi(0,"pid") + "_" +- (F$Cvtime(,,"TIME") - ":" - ":" - ".") + ".TMP" $ Controller_Error_Name = - F$Parse(Controller_Error_Name,,,,"NO_CONCEAL") $! $! And finally the SYS$OUTPUT file for the controller $! $ CONTROLLER_OUTPUT_NAME = - "Sys$Login:OUT_L_" + THIS_NODE + "_R_" + CLIENT_NODE +- "_" + F$Getjpi(0,"pid") + "_" +- (F$Cvtime(,,"TIME") - ":" - ":" - ".") + ".TMP" $ Controller_Output_Name = - F$Parse(Controller_Output_Name,,,,"NO_CONCEAL") $! $ Write Sys$Output "Input file for Server LOGINOUT is ",- Controller_Command_Procedure_Name $ Write Sys$Output "Error file for Server LOGINOUT is ",- CONTROLLER_ERROR_NAME $ Write Sys$Output "Output file for Server LOGINOUT is ",- CONTROLLER_OUTPUT_NAME $! $! We now build a command procedure to start up the controller. $! this will be fed to LOGINOUT to create a detached process running $! the DECW$TERMINAL image with SET DISPLAY pointing to CLIENT_NODE. $! $ open/write COMMAND_PROC_FILE 'controller_command_procedure_name' $! $! The detached process command procedure consists of a SET DISPLAY command, $! and a RUN DECW$TERMINAL command encased in a loop. If DECW$TERMINAL should $! crash, the program will attempt to restart it 10 times before giving up. $! Any change of status (Creation/Termination) of the process is reported to $! the NETWORK operator via the REQUEST command. $! $ Write command_proc_file "$ SET NOON" $ Write command_proc_file "$ SHOW PROC/PRIV" $ Write command_proc_file "$ SET OUTPUT_RATE=00:00:05.00" $ Write Command_proc_file "$ Restart_Count = 0" $ write command_proc_file - "$ Restart: request/to=network ""decw$terminal executable startup.""" $ write command_proc_file "$ show symbol restart_count" $! $! The SET MESSAGE command includes the error messages that may occur when $! DECW$TERMINAL has problems. For some reason DEC has not included these $! messages in the standard DEC messages library. $! $ Write command_proc_file "$ SET MESSAGE SYS$MESSAGE:decw$xlibmsg.exe" $ write command_proc_file "$ set display/create/node=" + client_node + "::0.0" $ write command_proc_file "$ run sys$system:decw$terminal" $ write command_proc_file - "$ request/to=network ""decw$terminal executable termination.""" $ Write Command_proc_file "$ Restart_Count = Restart_Count + 1" $ Write command_proc_file "$ If Restart_Count .Lt. 11 Then Goto Restart" $ write command_proc_file - "$ request/to=network ""giving up on trying to restart decw$terminal!""" $ close command_proc_file $! $! Enable SYSPRV (in case it was turned off by DECnet or a SYLOGIN proccedure) $! SYSPRV is needed by the controller to access a psuedoterminal after it $! is logged into. If SYSPRV was not available, the terminal controller will $! crash if the user uses a pull-down menu on the DECterm. $! $ Set proc/priv=sysprv $! $! Start the controller. $! - Error and Output files are in SYS$LOGIN for the X11_TERMINAL account $! - The controller process name is "DECterm_" with the client nodename $! appended. $! - The high AST count is specified to insure the terminal controller $! has enough ASTs to support 10+ windows on this controller. $! If the controller goes into a RWAST (Resource Wait-AST), raise the $! /AST value here... and bump it up in the X11_TERMINAL SYSUAF entry. $! $ run /detach/input='controller_command_procedure_name'- /output='controller_output_name'- /error='controller_error_name'- /process="DECterm_''client_node'"- /AST=100- sys$system:loginout.exe $ Set proc/priv=nosysprv $! $! Wait for the controller to start up... $! $ Wait 00:00:15 $ controller_exists: $! $! If the controller is running, we start here. $! $! Now call the Window Create program. This program tells the window $! generator to create a new window. $! $ Write Sys$Output "Starting Window Generator." $ Window_Create := $WINDOW_DIRECTORY:WINDOW_CREATE $ Window_Create 'client_Node'::0.0 $! $! The Error_Exit label is jumped to if an error is detected inside the $! command procedure. $! $ Error_Exit: $ Save_Status = $Status $! Send the status back to the client and close the connection... $ Write Network_Channel Save_Status $ Close Network_Channel $! Tell DECnet to maintain one permanent process to service future $! requests quickly $ Define Netserver$Servers_X11_TERMINAL "1" $ Exit == [Figure 4] Program Window_Create ! ! WINDOW_CREATE.FOR ! ! Author: ! ! John McMahon Phone: (408) 427-4366 ! TGV, Incorporated FAX: (408) 427-4365 ! 603 Mission Street E-Mail: MCMAHON@TGV.COM ! Santa Cruz, California 95060 ! ! This program is based on code developed by John McMahon while under contract ! to the Advanced Data Flow Technology Office - NASA Goddard Space Flight ! Center in Greenbelt, Maryland during October 1989. ! ! Usage: ! ! Executed by the DECnet object command procedure ! WINDOW_SERVER.COM. The command procedure passes one ! parameter of the format: ! ! NODENAME::SERVER.DISPLAY ! ! Function: ! ! This program calls the DECW$TERM_PORT routine to tell ! the DECwindows Terminal Controller to create a new terminal ! window. ! ! Output: ! ! A terminal window is created. ! !----------------------------------------------------------------------------- Implicit None Character*128 Input_Line ! Command Line Text Integer*2 IL_Length ! Length Of Input_Line Character*50 Terminal_Created ! Device Name Of The ! New Terminal Integer*2 TC_Length ! Length Of Terminal_Created Integer*4 Status ! Generic Status Variable Integer*4 DECW$TERM_PORT ! Create New Terminal Routine ! ! Fetch the command line ! Type *,'Window Create Start' Call Lib$Get_Foreign(Input_Line,,%ref(IL_Length),) Type *,'Command Line: ',Input_Line(1:IL_Length) ! ! Send a message to the controller to create a new terminal ! ! The name of the created terminal is returned by the call ! Status = DECW$TERM_PORT( $ Input_Line(1:IL_Length),,, $ Terminal_Created,%REF(TC_Length)) If (status .ne. 1) then print *,'DECW$TERM_PORT: status = ',status Call Lib$Signal(%val(Status)) end if If (TC_Length .gt. 0) then Type *,'Terminal Created: ',Terminal_Created(1:TC_Length) end if Type *,'Window Create End' end == BUILD_WINDOW_CREATE.COM $! $! BUILD_WINDOW_CREATE.COM $! $! This routine builds the WINDOW_CREATE.EXE from the FORTRAN source $! $! $ FORTRAN/EXTEND/CHECK=ALL WINDOW_CREATE $ LINK WINDOW_CREATE,SYS$INPUT:/OPT SYS$SHARE:DECW$TERMINALSHR/SHARE $ EXIT