Date: Mon, 17 Aug 87 17:11:45 edt From: David Krowitz Subject: Another program for the Apollo library. Here is the REMOTE program for the Apollo workstation software library. REMOTE is a short file daemon program which periodically checks a directory and executes a shell command for each of the files it finds there before deleting them. It is intended to be a simple and relatively robust way for the Apollos to automatically process files on other machines. See the file REMOTE.DOC for a longer description and REMOTE.HLP for some examples of how the program can be a useful tool in an Apollo network with a DSP9000 (an Alliant FX/1 or FX/8). The packed source files begin below the dashed line. Run the packed file through /bin/sh to unpack the files. -- David Krowitz mit-erl!mit-kermit!krowitz@eddie.mit.edu mit-erl!mit-kermit!krowitz@mit-eddie.arpa krowitz@mit-mc.arpa (in order of decreasing preference) ------------------------------------------------------------------ #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # remote.pas # remote.bld # remote.doc # remote.hlp # This archive created: Mon Aug 17 17:06:44 1987 export PATH; PATH=/bin:$PATH if test -f 'remote.pas' then echo shar: will not over-write existing file "'remote.pas'" else cat << \SHAR_EOF > 'remote.pas' {***************************************************************************** ***** ***** ***** REMOTE.PAS ***** ***** ***** ***** Program to periodically check a directory on a remote ***** ***** machine via a transparent file access mechanism (ie. NFS ***** ***** or the Apollo V2 demo program) and which executes the ***** ***** specified command line for every file which is found in the ***** ***** directory on the remote machine and then deletes the files. ***** ***** ***** ***** Version 5 ***** ***** David M. Krowitz August 13, 1987. ***** ***** ***** ***** Copyright (c) 1987 ***** ***** David M. Krowitz ***** ***** Massachusetts Institute of Technology ***** ***** Department of Earth, Atmospheric, and Planetary Sciences ***** ***************************************************************************** } PROGRAM REMOTE; %NOLIST; %INSERT '/sys/ins/base.ins.pas'; %INSERT '/sys/ins/cal.ins.pas'; %INSERT '/sys/ins/error.ins.pas'; %INSERT '/sys/ins/ios.ins.pas'; %INSERT '/sys/ins/ios_dir.ins.pas'; %INSERT '/sys/ins/pfm.ins.pas'; %INSERT '/sys/ins/pgm.ins.pas'; %INSERT '/sys/ins/time.ins.pas'; %INSERT '/sys/ins/vfmt.ins.pas'; %LIST; CONST {Program version number - should be same as in file header above} version_number = 5; max_ins = 16; {Maximum number of times file name can be inserted into command line} TYPE command_line_t = array[1..1024] of char; shell_command_t = array[1..256] of char; VAR {Defintions of global variables} i,j,k: INTEGER32; {counters} count: INTEGER16; {Count of fields processed by VFMT calls} status: status_$t; {Status returned by stream I/O calls} clock_time: TIME_$CLOCK_T; {Time to sleep in system clock format} cleanup_id: PFM_$CLEANUP_REC; {Handle for processing faults} working_dir: NAME_$PNAME_T; {Current working directory} working_dir_len: INTEGER16; {Length of working directory name} dir_name: NAME_$PNAME_T; {Pathname of the directory to be checked} dir_name_len: pinteger; {Length of the directory name} dir_stream: IOS_$ID_T; {Stream ID of the directory} dir_buffer: IOS_DIR_$DIR_ENTRY_T; {Buffer for data read from directory} dir_entry_ptr: ^IOS_DIR_$DIR_ENTRY_T; {Pointer to data read from directory} dir_entry_len: linteger; {Number of bytes read from directory} dir_entry_count: linteger; {Number of entries read from directory} shell_command: shell_command_t; {Shell command to be executed for each file} shell_command_len: pinteger; {Length of command} shell_ins_file: array[1..max_ins] of pinteger; {Locations of file name inserted into shell command string} shell_num_ins: pinteger; {Number of locations where file name is to be inserted} command: command_line_t; {Shell command actually executed (with file names inserted)} command_len: pinteger; {Length of command actually executed} object_name: NAME_$PNAME_T; {Pathname of the object read from the directory} object_name_len: pinteger; {Length of the object pathname} object_stream: IOS_$ID_T; {Stream ID of object read from the directory} sleep_time: pinteger; {Number of seconds to sleep between check directory} debug_flag: BOOLEAN; {TRUE if we want debugging info printed while running} FUNCTION uppercase (IN character: CHAR):CHAR; BEGIN IF (character IN ['a'..'z']) THEN BEGIN uppercase := CHR(ORD(character)-ORD('a')+ORD('A')) END ELSE BEGIN uppercase := character; END; END; {End of function UPPERCASE.} FUNCTION compare_strings ( IN string1: NAME_$PNAME_T; IN len1: pinteger; IN string2: NAME_$PNAME_T; IN len2: pinteger ):BOOLEAN; VAR i: pinteger; BEGIN IF (len1 <> len2) THEN BEGIN compare_strings := FALSE; RETURN; END; compare_strings := TRUE; FOR i := 1 TO len1 DO BEGIN IF (uppercase(string1[i]) <> uppercase(string2[i])) THEN BEGIN compare_strings := FALSE; RETURN; END; END; END; {End of function COMPARE_STRINGS.} PROCEDURE REMOTE_EXECUTE ( IN command_line: command_line_t; IN command_len: pinteger ); VAR i,j: pinteger; {Counters} args: array[1..3] of ^PGM_$ARG; {Pointers to arguments to invoked program} arg_list: array[1..3] of PGM_$ARG; {The actual arguements} conn_vec: array[0..1] of STREAM_$ID_T; {Stream connection vector} mode: PGM_$MODE; {Program mode} reserved: array[1..8] of char; {Reserved for future use} BEGIN {Set up argument and I/O stream vectors.} conn_vec[0] := STREAM_$STDIN; conn_vec[1] := STREAM_$STDOUT; args[1] := ADDR(arg_list[1]); args[2] := ADDR(arg_list[2]); args[3] := ADDR(arg_list[3]); {Set up arguments to execute the AEGIS shell.} args[1]^.len := 2; {Length of argument} args[1]^.chars := 'sh'; {1st arg. is the program name} args[2]^.len := 2; {Length of 2nd argument} args[2]^.chars := '-c'; {2nd arg. is the arg to the program} {Copy the shell command to be executed into an argument for /com/sh.} FOR i := 1 TO command_len DO BEGIN args[3]^.chars[i] := command_line[i]; END; args[3]^.len := command_len; mode := [PGM_$WAIT]; PGM_$INVOKE ('/com/sh',7,3,args,2,conn_vec,mode,reserved,status); IF (status.all <> 0) THEN BEGIN writeln ('**** REMOTE_EXECUTE: Error - command execution failed ****'); ERROR_$PRINT (status); PGM_$EXIT; END; END; {End of procedure REMOTE_EXECUTE.} BEGIN {Type banner info.} WRITELN ('This is REMOTE Version ',version_number:-1,'.'); WRITELN; {Check for the -DEBUG and -R (or -REPEAT) switches on the command line. Use a default time of 10 seconds between each time the directory is checked and turn off the debugging messages by default.} sleep_time := 10; debug_flag := FALSE; IF (PGM_$GET_ARG(1,dir_name,status,SIZEOF(dir_name)) <> 0) THEN BEGIN i := 1; REPEAT dir_name_len := PGM_$GET_ARG(i,dir_name,status,SIZEOF(dir_name)); IF (status.all = STATUS_$OK) THEN BEGIN IF (dir_name[1] <> '-') THEN BEGIN i := i+1; END ELSE BEGIN IF (compare_strings(dir_name,dir_name_len,'-R',2)) OR (compare_strings(dir_name,dir_name_len,'-REPEAT',7)) THEN BEGIN PGM_$DEL_ARG(i); dir_name_len := PGM_$GET_ARG(i,dir_name,status,SIZEOF(dir_name)); PGM_$DEL_ARG(i); j := VFMT_$DECODE2 ('%5EUWD%$',dir_name,dir_name_len,count,status,sleep_time,k); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - bad argument ''',dir_name:dir_name_len,''' to -REPEAT switch ****'); ERROR_$PRINT (status); PGM_$EXIT; END; END ELSE IF (compare_strings(dir_name,dir_name_len,'-DEBUG',6)) THEN BEGIN debug_flag := TRUE; PGM_$DEL_ARG(i); END ELSE BEGIN WRITELN ('**** REMOTE: Error - unknown switch ''',dir_name:dir_name_len,''' ****'); PGM_$EXIT; END; END; END; UNTIL (status.all <> STATUS_$OK); END; {Get the name of the directory to check and check that the given pathname is really a directory.} IF (PGM_$GET_ARG(1,dir_name,status,SIZEOF(dir_name)) <> 0) THEN BEGIN dir_name_len := PGM_$GET_ARG(1,dir_name,status,SIZEOF(dir_name)); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - argument 1 exists, but bad status? ****'); ERROR_$PRINT (status); PGM_$EXIT; END; END ELSE BEGIN WRITE ('Enter the pathname of the directory to be checked : '); READLN (dir_name); dir_name_len := SIZEOF(dir_name); END; dir_stream := IOS_$OPEN (dir_name,dir_name_len,[IOS_$UNREGULATED_OPT],status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - bad status trying to open specified directory ****'); ERROR_$PRINT (status); PGM_$EXIT; END; IOS_DIR_$ISA (dir_stream,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - specified pathname is not a directory ****'); ERROR_$PRINT (status); IOS_$CLOSE (dir_stream,status); PGM_$EXIT; END ELSE BEGIN IF (debug_flag) THEN WRITELN ('Remote host directory: ',dir_name:dir_name_len,' is ok.'); IOS_$CLOSE (dir_stream,status); END; {Get the shell command to be executed and find the point at which the file name is to be inserted into the command. The locations are marked by the characters '^1' ... similar to the way arguments are inserted into regular shell commands.} IF (PGM_$GET_ARG(2,shell_command,status,SIZEOF(shell_command)) <> 0) THEN BEGIN shell_command_len := PGM_$GET_ARG(2,shell_command,status,SIZEOF(shell_command)); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - argument 2 exists, but bad status? ****'); ERROR_$PRINT (status); PGM_$EXIT; END; END ELSE BEGIN WRITE ('Enter the shell command to be executed : '); READLN (shell_command); shell_command_len := SIZEOF(shell_command); END; IF (debug_flag) THEN WRITELN ('shell command is: ',shell_command:shell_command_len); {Check that there is at least one place in the command line for the file name to be inserted and not more than the maximum number of places we are set up to handle.} shell_num_ins := 0; FOR i := 1 to shell_command_len DO BEGIN IF (shell_command[i] = '^') AND (shell_command[i+1] = '1') THEN BEGIN shell_num_ins := shell_num_ins+1; shell_ins_file[shell_num_ins] := i; END; END; IF (shell_num_ins = 0) THEN BEGIN WRITELN ('**** REMOTE: Error - No place to insert file name into command ****'); PGM_$EXIT; END; IF (shell_num_ins > max_ins) THEN BEGIN WRITELN ('**** REMOTE: Error - Too many places to insert file name into command ****'); PGM_$EXIT; END; {Set the process' working directory so that relative pathnames can be used in the shell command which will be executed. Save the current working directory so we can restore it when the program exits.} IOS_$GET_DIR (IOS_$WDIR,working_dir,working_dir_len,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - unable to get current working directory ****'); ERROR_$PRINT (status); PGM_$EXIT; END; IOS_$SET_DIR (dir_name,dir_name_len,IOS_$WDIR,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - unable to set working directory to ',dir_name:dir_name_len,' ****'); ERROR_$PRINT (status); PGM_$EXIT; END; {Set up a cleanup fault handler to reset the process' working directory when the server is killed (either by a SIGP or by the node being shutdown).} status := PFM_$CLEANUP (cleanup_id); IF (status.all <> PFM_$CLEANUP_SET) THEN BEGIN WRITELN ('***** REMOTE: Cleanup in progress *****'); IOS_$SET_DIR (working_dir,working_dir_len,IOS_$WDIR,status); IF (status.all <> STATUS_$OK) THEN BEGIN; WRITELN ('***** REMOTE: Cleanup handler failed to reset working directory *****'); ERROR_$PRINT (status); END; WRITELN ('***** REMOTE: Cleanup handler done *****'); PGM_$EXIT; END; {Open up the directory every SLEEP_TIME seconds, check if there are any files in the directory, construct the appropiate shell command for each file, invoke /COM/SH for that command, and then delete the file.} WHILE TRUE DO BEGIN dir_stream := IOS_$OPEN (dir_name,dir_name_len,[IOS_$UNREGULATED_OPT],status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - bad status trying to re-open directory ****'); ERROR_$PRINT (status); PGM_$EXIT; END; dir_entry_len := IOS_$GET (dir_stream,[],dir_buffer,SIZEOF(dir_buffer),status); IF (debug_flag) THEN WRITELN ('Directory entry size is: ',dir_entry_len:-1); IF (status.all <> STATUS_$OK) AND (status.all <> IOS_$END_OF_FILE) THEN BEGIN WRITELN ('**** REMOTE: Error - bad status trying to read first entry from directory ****'); ERROR_$PRINT (status); PGM_$EXIT; END; {For each entry, check that the name read from the directory is a file and not a link or another directory. Only process files. Do not fool with links and subdirectories. (Note that a Unix directory *always* contains two directories -- '.' the current directory, and '..' the parent directory -- so we *must* pay attention to these details!)} dir_entry_count := 1; WHILE (status.all <> IOS_$END_OF_FILE) DO BEGIN dir_entry_ptr := ADDR(dir_buffer); IF (debug_flag) THEN WRITELN ('Object name is: ',dir_entry_ptr^.entname:dir_entry_ptr^.entlen); IF (dir_entry_ptr^.enttype <> IOS_DIR_$DIR_ENTTYPE_LINK) THEN WITH dir_entry_ptr^ DO BEGIN {If the object is a file, then we can go ahead and process it. Otherwise, it is a subdirectory in which case we just close the stream so we won't get an error the next time we check the directory.} object_stream := IOS_$OPEN (entname,entlen,[],status); IF (debug_flag) THEN WRITELN ('Stream ID number is: ',object_stream); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - bad status opening entry #',dir_entry_count:-1,' (', entname:entlen,') for object-type checking ****'); ERROR_$PRINT (status); IOS_$CLOSE (object_stream,status); PGM_$EXIT; END; IOS_DIR_$ISA (object_stream,status); IF (status.all <> STATUS_$OK) AND (enttype = IOS_DIR_$DIR_ENTTYPE_FILE) THEN BEGIN {Re-open the file with write-access to see if the file is already in use by another process. If file is in use, ignore it for the time being. Otherwise, use the WRITE option to lock the object and begin to process the shell command.} IOS_$CLOSE (object_stream,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - could not close object ',entname:entlen,' to re-open with write-access ****'); ERROR_$PRINT (status); PGM_$EXIT; END; object_stream := IOS_$OPEN (entname,entlen,[IOS_$WRITE_OPT],status); IF (status.all = STATUS_$OK) THEN BEGIN {Construct the shell command to be invoked from the command line the user supplied, the specified directory pathname, and the name of the files found in the directory.} j := 1; command_len := 0; FOR i := 1 to shell_num_ins DO BEGIN FOR k := j TO shell_ins_file[i]-1 DO BEGIN command_len := command_len+1; command[command_len] := shell_command[k]; END; FOR k := 1 to entlen DO BEGIN command_len := command_len+1; command[command_len] := entname[k]; END; j := shell_ins_file[i]+2; END; IF (j < shell_command_len) THEN BEGIN FOR i := j TO shell_command_len DO BEGIN command_len := command_len+1; command[command_len] := shell_command[i]; END; END; {Unlock the file so that the shell command can access the file if it wants to and process the command with the file name inserted. Then re-open the file and delete it from the directory after it has been processed.} WRITELN ('invoked command is: ',command:command_len); IOS_$CLOSE (object_stream,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - could not close object ',entname:entlen,' prior to executing command ****'); ERROR_$PRINT (status); PGM_$EXIT; END; REMOTE_EXECUTE (command,command_len); object_stream := IOS_$OPEN (entname,entlen,[IOS_$WRITE_OPT],status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Warning - unable to re-open object ',entname:entlen,' for deletion ****'); ERROR_$PRINT (status); END; IOS_$DELETE (object_stream,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Warning - unable to delete object ',entname:entlen,' ****'); ERROR_$PRINT (status); END; END; {End of 'is object in use?'} END ELSE BEGIN IOS_$CLOSE (object_stream,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - bad status closing stream to sub directory ',entname:entlen,' ****'); ERROR_$PRINT (status); PGM_$EXIT; END; END; {End of 'is it a file?'} END; {End of 'is object a link?'} dir_entry_count := dir_entry_count+1; dir_entry_len := IOS_$GET (dir_stream,[],dir_buffer,SIZEOF(dir_buffer),status); IF (debug_flag) THEN WRITELN ('Directory entry size is: ',dir_entry_len:-1); IF (status.all <> STATUS_$OK) AND (status.all <> IOS_$END_OF_FILE) THEN BEGIN WRITELN ('**** REMOTE: Error - bad status trying to read entry ',dir_entry_count:-1,' from directory ****'); ERROR_$PRINT (status); PGM_$EXIT; END; END; {End of while there are still files in the directory ...} {Finished with this pass through the directory. Close the stream to the directory so we can re-use it later.} IOS_$CLOSE (dir_stream,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - bad status closing stream to directory begin checked ****'); ERROR_$PRINT (status); PGM_$EXIT; END; {Go to sleep until the next time to check the directory.} CAL_$SEC_TO_CLOCK (sleep_time,clock_time); TIME_$WAIT (TIME_$RELATIVE,clock_time,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** REMOTE: Error - bad status from TIME_$WAIT ****'); ERROR_$PRINT (status); PGM_$EXIT; END; END; {***** End of Program REMOTE.PAS *****} END. SHAR_EOF chmod +x 'remote.pas' fi # end of overwriting check if test -f 'remote.bld' then echo shar: will not over-write existing file "'remote.bld'" else cat << \SHAR_EOF > 'remote.bld' von pas remote.pas bind -b remote remote.bin dlf remote.bin voff SHAR_EOF chmod +x 'remote.bld' fi # end of overwriting check if test -f 'remote.doc' then echo shar: will not over-write existing file "'remote.doc'" else cat << \SHAR_EOF > 'remote.doc' ******************************************************************************* ***** ***** ***** REMOTE.DOC ***** ***** Version 5 ***** ***** Program Notes for the REMOTE File Daemon Program ***** ***** ***** ***** Copyright (c) 1987 ***** ***** David M. Krowitz ***** ***** Massachusetts Institute of Technology ***** ***** Department of Earth, Atmosheric, and Planetary Sciences ***** ******************************************************************************* Description of the REMOTE file daemon program --------------------------------------------- REMOTE is a short file daemon program written with the IOS calls which periodically checks a directory and executes a shell command for each of the files it finds there before deleting them. It is intended to be a simple and relatively robust way for the Apollos to automatically process files on other machines. Why not use RSH, REXEC, and the other Unix commands? Well, for starters, REMOTE does not require that the other machine(s) be Unix based machines. It can check and directory which is on the local Apollo Domain network or on any machine which the Apollos have access to via the Network File System (NFS) or the V2 demo program. If the Apollos have no transparent file access to the remote machines, then the directory can be located on the local Apollo Domain network, and the remote machines can send files to the directory via FTP or even by dialing up the Apollo and send the file over the phone lines. Using REMOTE ------------ To start up a copy of REMOTE to execute a specified shell command for every file that is put into a given directory, simply type the command: REMOTE [directory pathname] [shell command] [options] eg. The command: remote /dsp9000_gateway/print/text_files 'prf ^1 -text' -repeat 30 Will check the /print/text_files directory of DSP9000 and execute the PRF -TEXT command for each file found in the directory and then delete the file when the command is completed. The directory will be rechecked every 30 seconds. If the shell command to be executed is not included on the command line, then REMOTE will prompt the user for it. The shell command must contain at least one reference to the file (the string '^1') found in the directory. It may contain up to 16 references. The length of the shell command specified by the user is limited to 256 characters. The shell command which is executed, including the file name which is substituted for each occurrance of the '^1' string, is limited to a length of 1024 characters. If the file in the directory is to be executed as a shell script (ie. the shell command given to REMOTE is something like '^1' or '^1 arg1 arg2 arg3 ...') then the directory must be located on the local Apollo Domain net, or the shell command must first copy the file to a directory on the local Apollo net before executing the shell script (ie. the shell command 'cpf ^1 //apollo/sys/batch/^1; ^1 arg1 arg2 arg3 ...' would work). This is a restriction required by /COM/SH, not a restriction of REMOTE. The AEGIS shell checks whether the file is a UASC file (in which case it is executed as a shell script) or a OBJ file (in which case it is an executable program), and this cannot be done with a file in a remote directory since Unix (and many other operating systems) do not keep track of file types. If neither pathname of the directory which is to be checked nor the shell command to be executed is included on the command line, then REMOTE will prompt the user for the pathname of the directory prior to prompting the user for the shell command. You can not included the shell command on the command line without also giving the directory pathname -- the user must specify both the directory and the shell command, just the directory, or neither. Otherwise, REMOTE will wind up interpreting the shell command as the directory pathname, and will fail when it checks that the given pathname is actually a directory. The directory pathname may be a directory which is on the local Apollo network or a directory on a remote (possibly non-Apollo machine) which is accessed via a V2 or NFS (Network File System) gateway object. The switches which are recognized are: -R[EPEAT] seconds Wait for the specified number of seconds after checking the directory before repeating the operation. Default is to wait 10 seconds before checking the directory again. -DEBUG Print debugging info while the program is running. Once REMOTE has read its arguments it will check that the pathname specified by the user is a valid directory and then save the pathname of the current working directory. REMOTE then sets its working directory to the directory specified by the user and begins to periodically check the directory to see if there are any files in it. If it finds a file in the directory it will insert the file name into the shell command specified by the user and will execute the resulting shell command and then delete the file. REMOTE will process all of the files it finds in the directory, ignoring links and subdirectories, and then will go to sleep for the amount of time specified by the -REPEAT switch (or for the default of 10 seconds if the switch is not given). The fact that REMOTE sets its working directory to the pathname specified by the user and that it ignores subdirectories can be quite handy. It means that other files associated with the file to be processed (eg. additional input files) can be stored in a subdirectory which can easily be specified by a relative pathname. For instance, REMOTE could be running a data plotting program for each output file created on a DSP9000 compute server, and the data plotting program could require a second file of parameters describing the layout of each plot which could be stored in a subdirectory. The command to run REMOTE might look something like: remote /dsp9000_gatway/data/plot.dir 'xyplot ^1 .in'. Note that we must explicitly delete the file of plot parameters from the 'inputs' subdirectory since REMOTE only processes and deletes the files found in the main directory. This feature allows both the files to be processed and any associated input files to be queued for processing without interfering with each other. See the help file for REMOTE (REMOTE.HLP on the distribution tape) for more examples (including some fairly complicated ones) of how REMOTE could be used. Differences Between Versions of REMOTE -------------------------------------- Version 5 --------- First released version. Handles a shell command of up to 256 characters with the file name inserted in up to 16 locations. Files Needed for the REMOTE Package ---------------------------------- The files which are provided for the REMOTE package are: REMOTE.DOC - This file. REMOTE.HLP - Help file for using REMOTE. REMOTE.PAS - The source code for the REMOTE. file daemon program. REMOTE.BLD - Command file to compile and bind the REMOTE program. REMOTE - A ready to run copy of the REMOTE program (in case you don't have a Pascal compiler). You will also need the following standard Apollo-supplied files: /SYS/INS/BASE.INS.PAS - These are all standard insert files which /SYS/INS/CAL.INS.PAS are used by REMOTE. /SYS/INS/ERROR.INS.PAS /SYS/INS/IOS.INS.PAS /SYS/INS/IOS_DIR.INS.PAS /SYS/INS/PFM.INS.PAS /SYS/INS/PGM.INS.PAS /SYS/INS/TIME.INS.PAS /SYS/INS/VFMT.INS.PAS Bugs, Questions, and Improvements --------------------------------- If you find a bugs in the REMOTE package, have questions on how to install or use it, or have a good idea for improving the program please feel free to contact me at the address below. David M. Krowitz MIT dept. of Earth, Atmospheric, and Planetary Sciences Room 54-527 Cambridge, MA 02139 (617) 253-6180 network mailing address: mit-erl!mit-kermit!krowitz@eddie.mit.edu mit-erl!mit-kermit!krowitz@mit-eddie.arpa krowitz@mit-mc.arpa (in order of decreasing preference) SHAR_EOF chmod +x 'remote.doc' fi # end of overwriting check if test -f 'remote.hlp' then echo shar: will not over-write existing file "'remote.hlp'" else cat << \SHAR_EOF > 'remote.hlp' 5.0;remote (remote file daemon), revision 5.0, 87/08/13 REMOTE (REMOTE FILE DAEMON) -- File daemon to execute shell command for each file in a directory. usage: REMOTE [directory pathname] [shell command] [-DEBUG] [-R|-REPEAT] FORMAT REMOTE [directory pathname] [shell command] [options] REMOTE periodically checks the specified directory and if it finds any files in the directory it inserts the file name into the specified shell command and executes the command for each file that REMOTE finds in the directory. The files are then deleted from the directory. ARGUMENTS directory pathname Pathname of the directory to be checked periodically. The directory name be a local directory on the Apollo Domain network, or it may be a directory located on a remote machine accessed via a V2 or NFS (Network File System) gateway. If the pathname is not given on the command line the user will be prompted for it. shell command The shell command to be executed for every file found in the directory. The file name will be inserted into the shell command wherever the string '^1' occurs. Must be at least one location where the file name is to be inserted into the shell command. Commands which include spaces must be enclosed in quotes. (eg. the string 'prf -plot ^1 -magnification 1' will cause the REMOTE program to print each file that it finds as a bitmap with 1 to 1 scaling.) If the shell command is not given on the command line the user will be prompted for it. The command does not need to be enclosed in quotes when the program prompts the user for it. OPTIONS -R[EPEAT] seconds Wait for the specified number of seconds after checking the directory before repeating the operation. Default is to wait 10 seconds before checking the directory again. -DEBUG Print debugging info while running. EXAMPLES 1. $ remote /nfs_gateways/dsp9000/sys/print 'prf ^1 -pitch 10 -lpi 6' Print each file found in the /sys/print directory of a DSP9000 which is being accessed via an NFS gateway located in the /nfs_gateways directory. The file will be deleted after it is queued for printing on the Apollo network. 2. $ remote //local_node/sys/batch ^1 -repeat 60 Execute each file found in the /sys/batch directory of the local Apollo node as a shell script. Wait 60 seconds after finished checking the directory before trying again. Note: /com/sh can not execute shell scripts through a V2 or NFS gateway. In this case the directory must be located on the Apollo network. The file will be deleted when the command is completed. 3. $ remote /nfs_or_v2_gateway/sys/batch 'cpf ^1 //local_node/sys/batch; @ _ //local_node/sys/batch/^1; dlf //local_node/sys/batch/^1' Copy each file found in the /sys/batch directory of the remote machine into the /sys/batch directory of the local Apollo node and then execute the local copy as a shell script and delete it. REMOTE will delete the original copy when the command is completed. Note that the working directory of process executing the command is /sys/batch directory of the remote machine, not the local Apollo directory. 4. $ remote /nfs_or_v2_gateway/sys/batch 'cpf ^1 //local_node/sys/batch; @ _ //local_node/sys/batch/^1 <./inputs/^1.in; dlf //local_node/sys/batch/^1; @ _ dlf ./inputs/^1.in' Copy each file found in the /sys/batch directory of the remote machine to the local Apollo node's /sys/batch directory so that it can be executed as a shell script. Execute the shell script using the file '.in' from the /sys/batch/inputs directory of the remote machine as the input to the shell script. Then delete the local copy of the shell script and the remote copy of the input file. REMOTE will automatically delete the copy of the shell script in the /sys/batch directory of the remote machine when the command is completed. SHAR_EOF chmod +x 'remote.hlp' fi # end of overwriting check # End of shell archive exit 0