//////////////////////////////////////////////////////////////////////////////// // // Task: CHK_MODS.TSK // (Collect & compare Loaded Module info from one or more Servers) // Author: Avanti Technology, Inc. // http://www.avanti-tech.com // Version: 1.0 - Initial Release // // Description: // ============ // Collect and compare information about Loaded Modules on one or more Servers // based upon the Loaded Module information of the host (base) Server running // the task script (i.e., compare one or more Remote Servers to Local Server). // // The task collects Loaded Module information from the Local Server which is // then used as a comparison against one or more Server(s) which can be // specified on the command line or in a file. // // If a file is used for the specified Server list, it is must be created as // an ASCII text file and placed in the same directory and named the same as // the task but with an extension .TXT. This file should contain only the // names of the Servers to be compared, one name per line with no trailing // spaces or comments // // The Loaded Module information for each Server is stored in a file located // in the same directory as where the task resides with a base name that is // the same as the task name but with the appropriate Server name appended in // parens () and an extension of .LST : // Example: SYS:\SYSTEM\TASKMSTR\CHK_MODS(SERVER_NAME).LST // // The results of the comparison for the two Server is written to a file // located in in the same directory as where the task resides with a base // name that is the same as the task name but with the appropriate Server // name appended in parens () and an extension of .RPT : // Example: SYS:\SYSTEM\TASKMSTR\CHK_MODS(SERVER_NAME).RPT // // Objective: // ========== // Keep Servers in sync with respect to Loaded Modules. // // Usage: // ====== // Script can be manually executed using the TaskMaster TMRUN command // at the TMConsole (Shell) Screen: // // Example: TMRUN [vol:path\]CHK_MODS.TSK [server [server] [server] ...] // // Notes: [vol:path\] is not required if the task resides either // in SYS:SYSTEM or the TaskMaster NLM load directory. // // Up to ten (10) individual Server names can be specified // on the command line to be compared to the Local Server. // // Script can be scheduled for automatic execution using Client interface or // using the TaskMaster TMSCHEDULE command at the TMConsole (Shell) Screen: // // Examples: TMSCHEDULE ADD CHK_MODS.TSK 01 02:00 // (Executes the 1st of each month at 2:00am) // // TMSCHEDULE ADD CHK_MODS.TSK YNNNNNN 02:00 // (Executes every Sunday [SMTWTFS] at 2:00am) // // Notes: Scheduled tasks must reside either in SYS:SYSTEM or the // TaskMaster NLM load directory for security reasons. // // If used as a scheduled task, it is must be created as // an ASCII text file and placed in the same directory // and named the same as the task but with an extension // .TXT. This file should contain only the names of the // Servers to be compared, one name per line with no // trailing spaces or comments. // // Compatibility: // ============== // This task has been tested on the following platforms without demonstrating // any compatibility issues or any other reported/confirmed conflicts: // TaskMaster v4.05 (or later) // NetWare v5.1 / v6.0 / v6.5 (OES NetWare Kernel) // // Warning: // ======== // AS WITH ANY NEW SOFTWARE PROGRAM, BATCH SCRIPT, OR AUTOMATED PROCESSING // PROCEDURE, CAUTION SHOULD BE EXERCISED AND DUE DILIGENCE OBSERVED DURING // INITIAL IMPLEMENTATION. WHERE POSSIBLE, TESTING SHOULD BE PERFORMED ON // NON-PRODUCTION SYSTEMS PRIOR TO FULL IMPLEMENTATION. // //////////////////////////////////////////////////////////////////////////////// // Check for compatible version of TaskMaster NLM IF "%TM_VERSION%.%TM_SUBVERSION%%TM_REVISION%"<"4.05" // Report the status ECHO. ECHO Error: Incompatible TaskMaster release (requires v4.05 or later)! ECHO. ABORT ENDIF // Declare the task variables to be used // Rename some dynamic variables to simpler variable // names for use in the task script VARALIAS %VAR10% %DATA% VARALIAS %VAR11% %NAME% VARALIAS %VAR12% %VERSION% VARALIAS %VAR13% %COMPILED% VARALIAS %VAR14% %RETURN_LABEL% VARALIAS %VAR15% %RSERVER% VARALIAS %VAR16% %RDATA% VARALIAS %VAR17% %RNAME% VARALIAS %VAR18% %RVERSION% VARALIAS %VAR19% %RCOMPILED% VARALIAS %VAR99% %TEMP% // Redirect a list of the loaded modules on the Server // to a file in the SYS:\ root directory with the same // name as the active Server but with an extension .TMP LIST MODULES >%TASK_PATH%\%SERVER%.TMP // Open the redirected output file for reading OPEN READ #0 %TASK_PATH%\%SERVER%.TMP // Create/truncate a file in the current task directory // with the same name as the active Server but with an // extension .DAT OPEN WRITE #0 %TASK_PATH%\%SERVER%.DAT TRUNCATE // Set return point for GOTO (GOSUB) to next line after GOTO (GOSUB) DEFINE %RETURN_LABEL% LOCAL_REPORT // Call (GOTO / GOSUB) routine to parse Loaded Module data GOTO PARSE_LM_DATA // Finished parsing data so sort final report and cleanup temp files :LOCAL_REPORT // Close redirected input (OPEN READ) file CLOSE READ #0 // Close parsed output (OPEN WRITE) file CLOSE WRITE #0 // Check for existing output file and delete DELETE %TASK_PATH%\%TASK_FILE%(%SERVER%).LST // Sort the output in alphabetical order by Loaded Module SORT %TASK_PATH%\%SERVER%.DAT %TASK_PATH%\%TASK_FILE%(%SERVER%).LST 1-12,15-24 // Cleanup temporary work files DELETE %TASK_PATH%\%SERVER%.DAT DELETE %TASK_PATH%\%SERVER%.TMP // The following logic first checks for and uses any Server specifications // included on the command line then for a file in the same directory as the // task script with the same name but with an extension of .TXT which can // contain a list of Remote Server names to ebe used. // // Note: If %TASK_PATH%\%TASK_FILE%.TXT is used, it must be an ANSI text file // which contains only the names of Servers to be processed, one per line // with with no trailing spaces or comments. :GET_REMOTE // Check for any (more) Server specifications on command line IF NOT "%0"=="" // Set %RSERVER% to first/next command line Server specification DEFINE %RSERVER% %0 // Shift any remaining command line specification left 1 position (%0 <- %1) // Note: Do not use SHIFT due to bug in pre-4.19 releases which will shift // all variables (%VAR00%-%VAR99%), not just %0-%9 (%VAR00%-%VAR09%) DEFINE %0 %1 DEFINE %1 %2 DEFINE %2 %3 DEFINE %3 %4 DEFINE %4 %5 DEFINE %5 %6 DEFINE %6 %7 DEFINE %7 %8 DEFINE %8 %9 DEFINE %9 ENDIF // Check if Server specification being processed from command line IF "%RSERVER%"=="" // If no (more) Server command line specifications // then check for a file containing a Server list IF EXIST %TASK_PATH%\%TASK_FILE%.TXT // Server list exists, check if already in use IF NOT FILE_IN_USE %TASK_PATH%\%TASK_FILE%.TXT // Open Server List for reading OPEN READ #2 %TASK_PATH%\%TASK_FILE%.TXT ENDIF // Check if Server list file is open for processing IF FILE_IN_USE %TASK_PATH%\%TASK_FILE%.TXT // If open then read first/next Server entry READ #2 %RSERVER% ENDIF ENDIF ENDIF // If %RSERVER% is null, must be no (more) Servers to process IF "%RSERVER%"=="" THEN GOTO EOJ // Check if valid Server specification provided else get next IF NOT EXIST %RSERVER%/SYS:\SYSTEM THEN GOTO GET_REMOTE // Issue command to collect Loaded Module list on Remote Server TMSCMD %RSERVER% LIST MODULES >SYS:\SYSTEM\%RSERVER%.TMP // Check once per second for command completion (file not in use when done) WHILE FILE_IN_USE %RSERVER%/SYS:\SYSTEM\%RSERVER%.TMP SLEEP 1 LOOP // Retrieve redirected output by LIST MODULES from Remote Server SCOPY %RSERVER%/SYS:\SYSTEM\%RSERVER%.TMP %TASK_PATH% // Check for errors and remove local temp files if problem IF ERRORLEVEL THEN DELETE %TASK_PATH%\%RSERVER%.TMP // Delete remote temp file TMSCMD %RSERVER% DELETE SYS:\SYSTEM\%RSERVER%.TMP // Check for Remote Server Loaded Modules list IF NOT EXIST %TASK_PATH%\%RSERVER%.TMP THEN GOTO GET_REMOTE // Open the redirected output file for reading OPEN READ #0 %TASK_PATH%\%RSERVER%.TMP // Create/truncate a file in the current task directory // with the same name as the active Server but with an // extension .DAT OPEN WRITE #0 %TASK_PATH%\%RSERVER%.DAT TRUNCATE // Set return point for GOTO (GOSUB) to next line after GOTO (GOSUB) DEFINE %RETURN_LABEL% REMOTE_REPORT // Call (GOTO / GOSUB) routine to parse Loaded Module data GOTO PARSE_LM_DATA // Finished parsing data so sort final report and cleanup temp files :REMOTE_REPORT // Close redirected input (OPEN READ) file CLOSE READ #0 // Close parsed output (OPEN WRITE) file CLOSE WRITE #0 // Check for existing output file and delete DELETE %TASK_PATH%\%TASK_FILE%(%RSERVER%).LST // Sort the output in alphabetical order by Loaded Module SORT %TASK_PATH%\%RSERVER%.DAT %TASK_PATH%\%TASK_FILE%(%RSERVER%).LST 1-24,15-24 // Cleanup temporary work files DELETE %TASK_PATH%\%RSERVER%.DAT DELETE %TASK_PATH%\%RSERVER%.TMP // Open the Local Server Loaded Modules list (skip processing on error) OPEN READ #0 %TASK_PATH%\%TASK_FILE%(%SERVER%).LST IF ERRORLEVEL THEN GOTO NEXT_REPORT // Open the Remote Server Loaded Modules list (skip processing on error) OPEN READ #1 %TASK_PATH%\%TASK_FILE%(%RSERVER%).LST IF ERRORLEVEL THEN GOTO NEXT_REPORT // Create/overwrite the Remote Server Loaded Modules report (skip on error) OPEN WRITE #0 %TASK_PATH%\%TASK_FILE%(%RSERVER%).RPT TRUNCATE IF ERRORLEVEL THEN GOTO NEXT_REPORT // Write Header Record to explain comparison and denotation WRITE #0 %TASK_FILE%(%RSERVER%).RPT : Compare Loaded Modules & versions between WRITE #0 %SERVER% (base) WRITE #0 %RSERVER% WRITE #0 WRITE #0 Legend: ++ = Add'l Loaded Module on %RSERVER% WRITE #0 -- = Not a Loaded Module on %RSERVER% WRITE #0 <> = Loaded on both but version mismatch WRITE #0 // WHILE/LOOP for reading, comparing, and reporting discrepancies WHILE // Read first/next Local Server data READ #0 %DATA% // Read first/next Local Server data READ #1 %RDATA% // Check for no more data from either file IF "%DATA%"=="" AND "%RDATA%"=="" THEN BREAK // Compare matched Loaded Modules and report discrepancies IF "%DATA%"=="%RDATA%" THEN CONTINUE // Parse out the individual Local Server Loaded Module data items PARSE %NAME% %DATA% 1-24 PARSE %VERSION% %DATA% 27-36 PARSE %COMPILED% %DATA% 39-58 // Parse out the individual Remote Server Loaded Module data items PARSE %RNAME% %RDATA% 1-24 PARSE %RVERSION% %RDATA% 27-36 PARSE %RCOMPILED% %RDATA% 39-58 // WHILE/LOOP for recording Loaded Module discrepancies and re-sync lists WHILE NOT "%NAME%"=="%RNAME%" // Compare Loaded Module names and process accordingly IF "%NAME%">"%RNAME%" WRITE #0 ++ %RNAME% %RVERSION% %RCOMPILED% WRITE #0 READ #1 %RDATA% // Parse out the individual Remote Server Loaded Module data items PARSE %RNAME% %RDATA% 1-24 PARSE %RVERSION% %RDATA% 27-36 PARSE %RCOMPILED% %RDATA% 39-58 ELSEIF "%NAME%"<"%RNAME%" WRITE #0 -- %NAME% %VERSION% %COMPILED% WRITE #0 READ #0 %DATA% // Parse out the individual Local Server Loaded Module data items PARSE %NAME% %DATA% 1-24 PARSE %VERSION% %DATA% 27-36 PARSE %COMPILED% %DATA% 39-58 ENDIF // If either read returns null data then terminate loop IF "%DATA%"=="" OR "%RDATA%"=="" THEN CONTINUE LOOP // Compare matched Loaded Modules and report discrepancies IF "%NAME%"=="%RNAME%" // Record discrepancy IF NOT "%VERSION%"=="%RVERSION%" OR NOT "%COMPILED%"=="%RCOMPILED%" WRITE #0 <> %RNAME% %RVERSION% %RCOMPILED% (%RSERVER%) WRITE #0 %NAME% %VERSION% %COMPILED% (%SERVER%) WRITE #0 ENDIF CONTINUE ENDIF LOOP :NEXT_REPORT // Check if file open before closing it (in case of errors) IF FILE_IN_USE %TASK_PATH%\%TASK_FILE%(%SERVER%).LST THEN CLOSE READ #0 // Check if file open before closing it (in case of errors) IF FILE_IN_USE %TASK_PATH%\%TASK_FILE%(%RSERVER%).LST THEN CLOSE READ #1 // Check if file open before closing it (in case of errors) IF FILE_IN_USE %TASK_PATH%\%TASK_FILE%(%RSERVER%).RPT THEN CLOSE WRITE #0 // Clear %RSERVER% for next Server name retrieval test DEFINE %RSERVER% GOTO GET_REMOTE :EOJ // Check if Server list file is open (in use) and close before exiting IF FILE_IN_USE %TASK_PATH%\%TASK_FILE%.TXT THEN CLOSE READ #2 EXIT // GOTO subroutine that parses Module data from Loaded Modules list // and writes formatted Module name, version, and compiled date data :PARSE_LM_DATA // WHILE/LOOP to parse the Loaded Module information from the // LIST MODULES redirected output WHILE // Read the next line in the list of modules READ #0 %DATA% // ERRORLEVEL is set for errors and End Of File (EOF) IF ERRORLEVEL THEN BREAK // Check for null string, reset %NAME% and read next line if found IF "%DATA%"=="" THEN DEFINE %NAME% IF "%DATA%"=="" THEN CONTINUE // Replace any " which may be in string with ' to avoid errors REPLACE %DATA% ""'" // Check if first/next Loaded Module name has been retrieved // IF %NAME% == NULL and no spaces in %DATA%, probably Loaded Module name IF "%NAME%"=="" AND NOT SCAN_STRING " " "%DATA%" // PARSE into fixed length Loaded Module name field (24 bytes) PARSE %NAME% %DATA% 1-24 ENDIF // Continue until Module name retrieved IF "%NAME%"=="" THEN CONTINUE // Continue if not Version data IF NOT SCAN_STRING "Version" "%DATA%" THEN CONTINUE // String contains word 'Version' so check if at correct offset PARSE %TEMP% %DATA% 3 // If 3rd character is not a V, it is not the Version/Compile string IF NOT "%TEMP%"=="V" THEN CONTINUE // Parse Version data PARSE %VERSION% %DATA% 11-20 // Parse Compile date PARSE %COMPILED% %DATA% 24-43 // Write the information to a pre-sort file WRITE #0 %NAME% %VERSION% %COMPILED% // Clear %NAME% for next READ loop check DEFINE %NAME% LOOP GOTO %RETURN_LABEL%