Your LOGIN.COM
command file (or script) may be one of the most important files you'll ever use and maintain on a VMS system. It is stored in your own “home directory” (logical name SYS$LOGIN
), and your version or instance of LOGIN.COM
is yours alone, unique to you and your needs.
Your LOGIN.COM
script is also a “work-in-progress”… you will likely find reasons to modify and update it frequently over the years (especially if you're reading these PARSEC Wiki Articles out of curiosity or a desire to learn more), and as such, it's “never done!” It is:
You maintain (update, change, evolve) your LOGIN.COM
with any standard VMS text editor, such as EVE, EDT, Emacs, or whatever you like to use:
$ SHOW DEFAULT DSA2:[LRICKER] $ ! equivalent to SYS$LOGIN for this user... $ SHOW LOG SYS$LOGIN /FULL "SYS$LOGIN" [exec] = "DSA2:[LRICKER]" (LNM$JOB_859C3940) $ $ EDIT /TPU LOGIN.COM ... edit window/buffer appears, editing occurs ...
Don't forget to save your work (changes) frequently – each editing file SAVE
or WRITE
operation creates a new version of your LOGIN.COM
file, so you can “always go back” if something doesn't turn out right. PURGE
this file if and whenever necessary.
When you first got your own VMS login account (username, home directory, etc.), your VMS system administrator likely “seeded” a site-specific stock copy of a LOGIN.COM
command file in your home directory when s/he created it. That “seed file” may have been well organized and thought-out… or maybe it wasn't.
Let's look at a trivial, empty LOGIN.COM
script, a sort of “skeleton” for the various parts that a well-organized LOGIN.COM
script should look like:
$ ! LOGIN.COM -- "My" login script -- template/skeleton version 'F$VERIFY(0)' $ ! $ GOTO 'F$MODE()' ! 3 $ ! $ ! ========== ! 5 $INTERACTIVE: ! 6 $ SET TERMINAL /INQUIRE ! 7 $ ! $ ! Interactive login process definitions go here... ! 9 $ ! $ EXIT ! 'F$VERIFY(0)' ! 11 $ ! $ ! ========== ! 13 $BATCH: ! 14 $ ! $ ! Batch job definitions, if any, go here... ! 16 $ ! (Why don't we do a SET TERMINAL command here?) ! 17 $ ! $ EXIT ! 'F$VERIFY(0)' ! 19 $ ! $ ! ========== ! 21 $NETWORK: ! 22 $OTHER: ! 23 $ ! This section is rarely, if ever, used... ! 24 $ ! $ EXIT ! 'F$VERIFY(0)' ! 26 $ !
The non-empty (comment) lines are numbered above. After the first (line #1) comment, internal-documentation line, here's how these lines work or function in this skeleton script:
GOTO
command functions much like a case
statement in another, higher-level language; this is DCL's best shot at a case
-like statement/function. Its purpose is to immediately direct DCL flow-of-execution to one of three “stanzas” (sections) of code (statements), each labelled with one of the strings-of-characters “INTERACTIVE
”, “BATCH
”, “NETWORK
” or “OTHER
” – see line-#s 6, 14, 22 and 23. The GOTO
statement's “target” is determined by the DCL Lexical Function F$MODE()
, which (when called or invoked) returns one of those four character strings, depending on the execution mode of the process (your login process, for example) when it executes your LOGIN.COM
file.$ ! ==========
are simply used as visual separators between the execution-mode stanzas (sections).INTERACTIVE:
” which is the line that the $ GOTO F$MODE()
statement (line 3) jumps to when the process is executing LOGIN.COM as an interactive process.BATCH:
” which is the line that the $ GOTO F$MODE()
statement (line 3) jumps to when the process is executing LOGIN.COM as a batch process.NETWORK:
” and “OTHER:
”, the lines that the $ GOTO F$MODE()
statement (line 3) jumps to when the process is executing LOGIN.COM as either a network or a detached (“other”) process.$ SET TERMINAL /INQUIRE
command ensures that your terminal (PuTTY) window is set up as a VT-terminal compatible device, necessary so that you can do full-screen editing and other screen-oriented tasks. And if you prefer overstrike-mode line editing to insert-mode line editing (the default), change this line to read: $ SET TERMINAL /INQUIRE /OVERSTRIKE
– will take effect the next time you login, or if you execute $ @LOGIN
immediately after saving this file edit.SET TERMINAL
command in the BATCH
stanza? If you try it, you'll see why (and don't forget to take that test command out of your LOGIN.COM
when you've figured this out!).LOGIN.COM
.EXIT
commands (which also use the Lexical Function F$VERIFY(0) to ensure that command file execution-verification (tracing) is turned off (if it was on).
The above is nothing more than a skeleton, a template, for a real LOGIN.COM
file. It has the advantage of being well-structured and simple to understand, therefore simple to maintain and extend into your future. Other forms and contents of the file are possible, and you may see those other formats. Unfortunately, the contents of many LOGIN.COM
files that we've seen “in the wild” are unstructured, often a tangled mass of:
$ IF F$MODE() .EQS. "INTERACTIVE" THEN do-something ... $ IF F$MODE() .EQS. "BATCH" THEN do-something-else ... $ IF F$MODE() .EQS. "INTERACTIVE" THEN declare-something-elsse $ IF F$MODE() .NES. "BATCH" THEN GOTO some-other-lable ...
…Well, you get the idea. Spaghetti code is not much fun to extend or maintain. We advocate and teach the skeleton-template form above.
Here's a possible first pass which extends the template LOGIN.COM
file, maybe suitable for an “ordinary, normal user” of VMS:
$ ! LOGIN.COM -- "My" login script -- 1st version 'F$VERIFY(0)' $ ! $ ! Likely want this set the same way for all process types: $ SET PROTECTION=(S:RWED,O:RWED,G,W) /DEFAULT $ ! $ GOTO 'F$MODE()' $ ! $ ! ========== $INTERACTIVE: $ SET TERMINAL /INQUIRE /INSERT $ ! $ CALL CreDir "SYS$SCRATCH" ! separate sub-dir for temp-files ! *** $ CALL CreDir "LOGS" ! a sub-dir for batch log-files ! *** $ ! $ ! VMS-style command aliases (symbols) -- ! *** $ dir == "DIRECTORY /SIZE /DATE /PROTECTION" ! *** $ move == "RENAME" ! *** $ prlj == "PRINT /QUEUE=LASERJET /LOG" ! *** $ ssys*tem == "PIPE SHOW SYSTEM | SEARCH SYS$PIPE " ! *** $ SUBM*IT == "SUBMIT /NOTIFY /NOPRINT /LOG_FILE=logs:" ! *** $ count == "PIPE SHOW SYSTEM | SEARCH SYS$PIPE /STATISTICS " ! *** $ ! ... ! *** $ ! $ SET PROMPT="''F$DIRECTORY()'$ " ! *** $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $ ! ========== $BATCH: $ ! $ ! Batch job definitions, if any, go here... $ ! (Why don't we do a SET TERMINAL command here?) $ ! $ ! Replicate this interactive symbol, for consistency $ ! in self-SUBMITted batch jobs -- $ SUBM*IT == "SUBMIT /NOTIFY /NOPRINT /LOG_FILE=logs:" ! *** $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $ ! ========== $NETWORK: $OTHER: $ ! This section is rarely, if ever, used... $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $CreDir: SUBROUTINE $ ! P1 : Subdirectory to test and create $ ! P2 : Job logical name to define $ IF ( P2 .EQS. "" ) THEN P2 = P1 $ homedd = F$TRNLNM("SYS$LOGIN") - "]" $ IF ( F$SEARCH("''P1'.DIR;1") .EQS. "" ) $ THEN CREATE /DIRECTORY [.'P1'] $ ENDIF $ DEFINE /JOB /NOLOG 'P2' 'homedd'.'P1'] $ EXIT 1 $ ENDSUBROUTINE ! CreDir $ !
The comments ! ***
mark the lines where are added to replace INTERACTIVE
stanza Line 9 in the original template/skeleton, plus a line added to the BATCH
stanza to make re-submits easier.
This also includes a DCL subroutine, labelled CreDir
(for “create directory”) at the end of this script, to test for a user's home directory subdirectory, conditionally creating it plus a suitable logical name if it doesn't already exist. Here, two subdirectories are tested and created, one for SYS$SCRATCH
(redirected from the user's home directory), and a LOGS
subdirectory for batch log-files.
Note that these enhancement lines were easy to add right into the appropriate INTERACTIVE
or BATCH
stanza, and didn't take any complicated IF/THEN/ELSE
logic to make things work right. It's always best to keep things simple…
Here's a version for a VMS “power user”:
$ ! LOGIN.COM -- "My" login script -- 2nd version 'F$VERIFY(0)' $ ! $ ! Likely want this set the same way for all process types: $ SET PROTECTION=(S:RWED,O:RWED,G,W) /DEFAULT $ ! $ GOTO 'F$MODE()' $ ! $ ! ========== $INTERACTIVE: $ SET TERMINAL /INQUIRE /INSERT $ SET CONTROL=(Y,T) $ ! $ CALL CreDir "SYS$SCRATCH" $ CALL CreDir "LOGS" $ CALL CreDir "COM" $ ! $ EveInitF = "sys$login:eve$init.eve" $ IF ( F$SEARCH(EveInitF) .EQS. "" ) $ THEN CREATE 'EveInitF' ! a *nix-style "here-doc": ! EVE$INIT.EVE -- sets EDT-keypad and bound-cursor modes: SET KEYPAD EDT SET CURSOR BOUND $ ! -- end of "here-doc" data $ ENDIF $ DEFINE /PROCESS /NOLOG eve$init 'EveInitF' $ ! $ ! VMS-style command aliases (symbols) -- $ dir == "DIRECTORY /SIZE /DATE /PROTECTION" $ ed*it == "EDIT /TPU /INIT=eve$init" $ move == "RENAME" $ prlj == "PRINT /QUEUE=LASERJET /LOG" $ ssys*tem == "PIPE SHOW SYSTEM | SEARCH SYS$PIPE " $ SUBM*IT == "SUBMIT /NOTIFY /NOPRINT /LOG_FILE=logs:" $ count == "PIPE SHOW SYSTEM | SEARCH SYS$PIPE /STATISTICS " $ ! $ ! Linux/Unix-style command aliases (symbols) -- $ cd == "SET DEFAULT" $ pwd == "SHOW DEFAULT" $ cls == "TYPE /PAGE=CLEAR_SCREEN NLA0:" $ cp == "COPY" $ rm == "DELETE" $ mv == "RENAME" $ ls == "DIRECTORY /SIZE /DATE /PROTECTION" $ home == "PIPE SET DEFAULT sys$login ; SHOW DEFAULT" $ upt*ime == "SHOW SYSTEM /NOPROCESS /FULL" $ ! $ PgSize = F$INTEGER(F$GETDVI("TT","TT_PAGE")) - 2 ! allow some margin $ PgWidth = F$INTEGER(F$GETDVI("TT","DEVBUFSIZ")) - 2 $ tail == "TYPE /TAIL=''PgSize'" $ ! ... $ ! $ nodename = F$EDIT( F$GETSYI("NODENAME"), "TRIM,UPCASE" ) $ SET PROMPT="''nodename'$ " $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $ ! ========== $BATCH: $ ! $ ! Batch job definitions, if any, go here... $ ! (Why don't we do a SET TERMINAL command here?) $ ! $ ! Replicate this interactive symbol, for consistency $ ! in self-SUBMITted batch jobs -- $ SUBM*IT == "SUBMIT /NOTIFY /NOPRINT /LOG_FILE=logs:" $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $ ! ========== $NETWORK: $OTHER: $ ! This section is rarely, if ever, used... $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $CreDir: SUBROUTINE $ ! P1 : Subdirectory to test and create $ ! P2 : Job logical name to define $ IF ( P2 .EQS. "" ) THEN P2 = P1 $ homedd = F$TRNLNM("SYS$LOGIN") - "]" $ IF ( F$SEARCH("''P1'.DIR;1") .EQS. "" ) $ THEN CREATE /DIRECTORY [.'P1'] $ ENDIF $ DEFINE /JOB /NOLOG 'P2' 'homedd'.'P1'] $ EXIT 1 $ ENDSUBROUTINE ! CreDir $ !
The CreDir
subroutine is called for two more (suggested) subdirectories.
Here's a version with command-tools for a VMS system administrator:
$ ! LOGIN.COM -- "My" login script -- 3rd version 'F$VERIFY(0)' $ ! with System Administrator tools $ ! $ ! Likely want this set the same way for all process types: $ SET PROTECTION=(S:RWED,O:RWED,G,W) /DEFAULT $ ! $ GOTO 'F$MODE()' $ ! $ ! ========== $INTERACTIVE: $ SET TERMINAL /INQUIRE /INSERT $ SET CONTROL=(Y,T) $ ! $ CALL CreDir "SYS$SCRATCH" $ CALL CreDir "LOGS" $ CALL CreDir "COM" $ ! $ EveInitF = "sys$login:eve$init.eve" $ IF ( F$SEARCH(EveInitF) .EQS. "" ) $ THEN CREATE 'EveInitF' ! a *nix-style "here-doc": ! EVE$INIT.EVE -- sets EDT-keypad and bound-cursor modes: SET KEYPAD EDT SET CURSOR BOUND $ ! -- end of "here-doc" data $ ENDIF $ DEFINE /PROCESS /NOLOG eve$init 'EveInitF' $ ! $ ! VMS-style command aliases (symbols) -- $ IF F$GETSYI("ARCH_NAME") .NES. "VAX" $ THEN dir == "DIRECTORY /ACL /SIZE /DATE /PROT /WIDTH=(FILENAME=24,SIZE=10)" !Alpha, Itanium $ ELSE dir == "DIRECTORY /ACL /SIZE /DATE /PROT /WIDTH=SIZE=7" $ ENDIF $ ed*it == "EDIT /TPU /INIT=eve$init" $ move == "RENAME" $ prlj == "PRINT /QUEUE=LASERJET /LOG" $ ssys*tem == "PIPE SHOW SYSTEM | SEARCH SYS$PIPE " $ SUBM*IT == "SUBMIT /NOTIFY /NOPRINT /LOG_FILE=logs:" $ count == "PIPE SHOW SYSTEM | SEARCH SYS$PIPE /STATISTICS " $ ! $ ! Linux/Unix-style command aliases (symbols) -- $ cd == "SET DEFAULT" $ pwd == "SHOW DEFAULT" $ cls == "TYPE /PAGE=CLEAR_SCREEN NLA0:" $ ! See also [...BEGINNER]ANSISEQ.COM for a better approach to clr-screen $ cp == "COPY" $ rm == "DELETE" $ mv == "RENAME" $ ls == "DIRECTORY /SIZE /DATE /PROTECTION" $ home == "PIPE SET DEFAULT sys$login ; SHOW DEFAULT" $ upt*ime == "PIPE SHOW SYSTEM /NOPROCESS | SEARCH /HIGHLIGHT=UNDERLINE sys$pipe uptime" $ ! $ PgSize = F$INTEGER(F$GETDVI("TT","TT_PAGE")) - 2 ! allow some margin $ PgWidth = F$INTEGER(F$GETDVI("TT","DEVBUFSIZ")) - 2 $ tail == "TYPE /TAIL=''PgSize'" $ ! $ ! Pick up TCP/IP command set (if HP-stack): $ tcpipdef = "sys$manager:tcpip$define_commands.com" $ IF ( F$SEARCH(tcpipdef) .NES. "" ) THEN @'tcpipdef' $ ! (If your TCP/IP stack is MultiNet or TCPware, then the above doesn't apply, $ ! and your TCP-commands will be defined differently, see your doc-set.) $ ! $ ! System Administrator tools -- $ anim*age == "ANALYZE /IMAGE /SELECT=(ARCH,IMAGE_TYPE,IDENT=IMAGE,NAME)" $ break*in == "SHOW INTRUSION /TYPE=ALL" $ delbreak*in == "DELETE /INTRUSION_RECORD" $ chks*um == "CHECKSUM /ALGORITHM=MD5 /SHOW=ALL" $ crc == "CHECKSUM /ALGORITHM=CRC /SHOW=ALL" $ disk*s == "SHOW DEVICE /MOUNTED D" $ dheader == "DUMP /HEADER /BLOCK=COUNT=0" $ sclu*ster == "SHOW CLUSTER /CONTINUOUS" $ IF F$SEARCH("sys$login:show_cluster$init.ini") .NES. "" $ THEN DEFINE /PROCESS /SUPERVISOR /NOLOG show_cluster$init sys$login:show_cluster$init.ini $ ENDIF $ ! $ privcomf = "com:privilege.com" ! (from Lorin's repository) $ IF ( F$SEARCH( privcomf ) .NES. "" ) $ THEN priv == "@''privcomf'" $ pow*er == "@''privcomf'" - ".COM" - " ONE$SHOT" + " ONE$SHOT" $ sudo == "''power'" ! make a Linux-synonym too... $ ENDIF $ ! $ IF F$TYPE(authorize) .EQS. "" THEN auth*orize == "$SYS$SYSTEM:AUTHORIZE" $ IF F$TYPE(sysgen) .EQS. "" THEN sysgen == "$SYS$SYSTEM:SYSGEN" $ IF F$TYPE(sysman) .EQS. "" THEN sysman == "$SYS$SYSTEM:SYSMAN SET ENV/CLU" $ IF F$TYPE(lancp) .EQS. "" THEN lancp == "$SYS$SYSTEM:LANCP" $ IF F$TYPE(ncp) .EQS. "" THEN ncp == "$SYS$SYSTEM:NCP" $ IF F$TYPE(ncl) .EQS. "" THEN ncl == "$SYS$SYSTEM:NCL" $ ! $ nodename = F$EDIT( F$GETSYI("NODENAME"), "TRIM,UPCASE" ) $ SET PROMPT="''nodename'$ " $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $ ! ========== $BATCH: $ ! $ ! Batch job definitions, if any, go here... $ ! (Why don't we do a SET TERMINAL command here?) $ ! $ ! Replicate this interactive symbol, for consistency $ ! in self-SUBMITted batch jobs -- $ SUBM*IT == "SUBMIT /NOTIFY /NOPRINT /LOG_FILE=logs:" $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $ ! ========== $NETWORK: $OTHER: $ ! This section is rarely, if ever, used... $ ! $ EXIT ! 'F$VERIFY(0)' $ ! $CreDir: SUBROUTINE $ ! P1 : Subdirectory to test and create $ ! P2 : Job logical name to define $ IF ( P2 .EQS. "" ) THEN P2 = P1 $ homedd = F$TRNLNM("SYS$LOGIN") - "]" $ IF ( F$SEARCH("''P1'.DIR;1") .EQS. "" ) $ THEN CREATE /DIRECTORY [.'P1'] $ ENDIF $ DEFINE /JOB /NOLOG 'P2' 'homedd'.'P1'] $ EXIT 1 $ ENDSUBROUTINE ! CreDir $ !
This final sample just adds more command aliases suitable for a system manager's needs.
Hopefully, these samples will give you some good ideas for your own LOGIN.COM
file.