===== Anatomy of Your LOGIN.COM Script ===== 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: * The means by which you "tailor" or "customize" your own VMS login environment, making it specific and useful to your needs. * An expression of your on-going development with VMS tools and concepts. * A place to keep notes, ideas and experiments with VMS concepts and resources. ==== Maintaining Your LOGIN.COM ==== 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. ==== Structure of LOGIN.COM (template, skeleton) ==== 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: * Line 3 -- The **most important line** in the whole script. This ''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 Functions|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. * Lines 5, 13 and 21 -- these lines ''$ ! =========='' are simply used as visual separators between the execution-mode stanzas (sections). * Line 6 -- The label "''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__. * Line 14 -- The label "''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__. * Lines 22 and 23 -- The labels "''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. * Line 7 -- This ''$ 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. * Line 9 -- The comment on this line suggests that you'll be replacing it (the comment) with actual command alias (global symbol) and logical name definitions to tailor your VMS environment for //interactive// work and com-line use. This is "where the action is"... You'll add, change and enhance DCL statements here over the months and years of VMS use to keep your environment "up to date" and useful. * Line 9 -- A place-holder comment, suggesting that you will replace it with "real stuff," actual DCL command alias and logical name definitions (and other things) that contribute together to make up your own VMS login environment -- VMS the way //you// want it. * Line 16 -- Similar to Line 9 above, but here only a few commands (if any) are used to define or enhance your process environment for batch jobs. Minimally used (by most folks), but useful for some specific things. * Line 17 -- A comment which asks a reasonable question: Why //don't// we do a ''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!). * Line 24 -- Similar to Lines 9 and 16 above, but this comment is //rarely// replaced, as network and detached process environments are usually self-defining, and don't need much from ''LOGIN.COM''. * Lines 11, 19 and 26 -- DCL ''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. ==== First Enhancement to LOGIN Script ==== 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... ==== Enhancements for a Power User ==== 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. ==== Sys-Admin's LOGIN script ==== 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.