/****************************************************************************** * * File: ASCII.H * * Contents: Definitions for standard ASCII values * ******************************************************************************/ #define NUL 0x00 #define SOH 0x01 #define STX 0x02 #define ETX 0x03 #define EOT 0x04 #define ENQ 0x05 #define ACK 0x06 #define BEL 0x07 #define BS 0x08 #define HT 0x09 #define LF 0x0A #define VT 0x0B #define FF 0x0C #define CR 0x0D #define SO 0x0E #define SI 0x0F #define DLE 0x10 #define DC1 0x11 #define DC2 0x12 #define DC3 0x13 #define DC4 0x14 #define NAK 0x15 #define SYN 0x16 #define ETB 0x17 #define CAN 0x18 #define EM 0x19 #define SUB 0x1A #define ESC 0x1B #define FS 0x1C #define GS 0x1D #define RS 0x1E #define US 0x1F #define SP 0x20 #define DEL 0x7F /****************************************************************************** * * File: USEFUL.H * * Contents: Declarations for USEFUL.LIB functions. * * NOTE: USEFUL.LIB contains general purpose functions that are missing * from STDIO.LIB or that are useful to keep for future use. This * is only a partial header file. * ******************************************************************************/ char *bld_ptr(); /* converts a segment token to a pointer */ unsigned bld_sel(); /* converts a pointer to a token (selector) */ char *c_to_plm(); /* C string to PL/M (byte count) string */ int fdelete(); /* delete a file */ int finfo(); /* get info about file (struct finfo_str) */ int faccess(); /* change file access rights */ /*1**************************************************************************** * * File: SPOOLER.C * * Program: Print Spooler Utility (for iRMX 286 OS) * * Summary: This print spooler will monitor the spooler directory for * files. As files are copied to the spooler directory, they * will be copied to the system printer. After a file has * been copied to the printer, it will be deleted from the * spooler directory. * * Usage: 1. Run the spooler as a background job under iRMX-286 R2.0: * * CREATEDIR :SD:SPOOLER (if not already present) * * BACKGROUND(100,100) SPOOLER * * Use "SPOOLER.LOG" as the name of the log file for the job. * * * 2. Next, create a logical name for the spooler directory: * * ATTACHFILE :SD:SPOOLER AS :SPOOLER: * (or) * ATTACHFILE :MASTER:SD/SPOOLER AS :SPOOLER: (RMX-NET) * * * 3. Finally, use the copy command to send files to the spooler: * * COPY SRC/ * TO :SPOOLER: * * Caveats: The spooler job sets itself up as a SUPER user so that it * can have access to any file that is copied to the spooler * directory. Other than sending a form feed control to the * printer before the start of each new file, the spooler * does nothing but copy the files directly to the printer. * All TAB conversions and paging should be performed before * a file is copied to the spooler directory. * * Author: Richard Carver Date: 03/03/88 * * Revisions: * ******************************************************************************/ #include /* RMX system call declarations */ #include /* ASCII control codes */ #include /* Standard C Library */ #include /* Useful Functions Library */ /* RMX Condition Codes */ #define E$OK ((unsigned int)0x0000) #define E$TIME ((unsigned int)0x0001) #define E$MEM ((unsigned int)0x0002) /* Wait Times For RMX System Calls */ #define NO_WAIT ((unsigned int)0) #define WAIT_ONE_SEC ((unsigned int)100) #define WAIT_TWO_SEC ((unsigned int)200) #define WAIT_FOREVER ((unsigned int)0xFFFF) /* Defines for various RMX system calls */ #define THIS_JOB ((unsigned int)1) #define ROOT_JOB ((unsigned int)3) #define FIFO_MBX ((unsigned int)0) #define NO_RESP ((unsigned int)0) #define READ_ONLY ((unsigned char)1) #define BUFFERS_TWO ((unsigned char)2) #define USER_SUPER ((unsigned int)0) #define ACCESS_DRAU ((unsigned int)15) /* Miscellaneous Defines */ #define DISABLE ((unsigned char)0x00) #define NOT_FOUND ((int)-1) #define PRINTER_NAME ":LP:" #define SPOOLER_MBX_NAME "\013SPOOLER_MBX" /* Spooler Command Codes */ #define CMND_CONTINUE ((int)0) #define CMND_SUSPEND ((int)1) #define CMND_SHUTDOWN ((int)2) #define CMND_NEXTFILE ((int)3) /* Spooler Commands */ char *spooler_cmnds[] = { "continue", "suspend", "shutdown", "nextfile", NULL }; /* Structure Declarations */ struct dir_entry_str /* RMX Directory Entry Structure */ { unsigned int fnode; char fname[14]; }; struct finfo_str /* RMX File Information Structure */ { unsigned int device_share; unsigned int number_connections; unsigned int number_reader; unsigned int number_writer; unsigned char share; unsigned char named_file; char device_name[14]; unsigned int file_drivers; unsigned char functions; unsigned char flags; unsigned int device_granularity; unsigned long int device_size; unsigned int device_connections; unsigned int file_id; unsigned char file_type; unsigned char file_granularity; unsigned int owner_id; unsigned long int create_time; unsigned long int access_time; unsigned long int modify_time; unsigned long int file_size; unsigned long int file_blocks; char volume_name[6]; unsigned int volume_granularity; unsigned long int volume_size; unsigned int accessor_count; unsigned char owner_access; }; struct user_ids_str /* RMX User ID Structure */ { unsigned int length; unsigned int count; unsigned int user_id; }user_ids = {1, 1, USER_SUPER}; struct xhndlr_str /* RMX Exception Handler Structure */ { void (*xhndlr_addr)(); unsigned char xhndlr_mode; }; struct spooler_msg_str /* Spooler Message Structure */ { int cmnd_code; }; /* Constant & Global Data */ char spooler_dir[] = "\013:sd:spooler"; int spooler_state = CMND_CONTINUE; main(argc, argv) int argc; char *argv[]; { int command; unsigned int dummy_mbx; unsigned int root_tkn; unsigned int spooler_mbx; unsigned int rsp_mbx; unsigned int stat; unsigned int status; struct xhndlr_str xhndlr_info; struct spooler_msg_str *spooler_msg_ptr; /*9**************************************************************************** * Disable the RMX exception handler. ******************************************************************************/ rq$get$exception$handler(&xhndlr_info, &status); if (status == E$OK) { xhndlr_info.xhndlr_mode = DISABLE; rq$set$exception$handler(&xhndlr_info, &status); } /*9**************************************************************************** * Get the object token for the system's root job. The root job is the * highest level in the RMX job tree. ******************************************************************************/ if (status == E$OK) { root_tkn = rq$get$task$tokens(ROOT_JOB, &status); } if (status == E$OK) { /*9**************************************************************************** * Lookup the name of the spooler mailbox in the root job's object * directory. This will be present only if the spooler is already installed. * NOTE: RMX uses byte count strings (first byte in string is length). ******************************************************************************/ spooler_mbx = rq$lookup$object(root_tkn, SPOOLER_MBX_NAME, NO_WAIT, &status); if (status == E$OK) { /*9**************************************************************************** * Since the spooler is installed, check to see if there are two command * line arguments (program name and spooler command). If there is only one * argument (program name), inform user that the spooler is already running. * If more than two arguments exist, the program was incorrectly invoked. ******************************************************************************/ if (argc != 2) { if (argc == 1) { printf("ERROR: Spooler already installed.\n"); } else { printf("ERROR: Invalid invocation.\n"); } } else { /*9**************************************************************************** * If the command invocation was correct, verify that the command is a * valid command. If an invalid command, indicate error. If a valid * command, create a message to send to the spooler. Create a mailbox to * receive the response from the spooler. The response will use the same * segment as the message. Send the message to the spooler. If the message * could not be sent, the spooler is no longer running (it may have been * deleted by the "kill" utility). Remove the spooler mailbox name from * the root directory so that the spooler may be re-installed in the future. * If the message was sent, wait for the response from the spooler. When * the response has been received, delete the response mailbox and the * message segment. ******************************************************************************/ command = command_code(argv[1]); if (command == NOT_FOUND) { printf("ERROR: Invalid spooler command.\n"); } else { spooler_msg_ptr = calloc(1, sizeof(struct spooler_msg_str)); if (spooler_msg_ptr != NULL) { spooler_msg_ptr->cmnd_code = command; rsp_mbx = rq$create$mailbox(FIFO_MBX, &status); if (status == E$OK) { rq$send$message(spooler_mbx, bld_sel(spooler_msg_ptr), rsp_mbx, &status); if (status != E$OK) { rq$uncatalog$object( root_tkn, SPOOLER_MBX_NAME, &stat); } else { rq$receive$message(rsp_mbx, WAIT_FOREVER, &dummy_mbx, &status); } rq$delete$mailbox(rsp_mbx, &stat); } free(spooler_msg_ptr); } else { status = E$MEM; } } } } else if (status == E$TIME) { /*9**************************************************************************** * If the spooler mailbox name was not found (lookup timed out), then * the spooler is not installed. If only the program name was entered (one * command line argument) then this invocation of the program will become * the installed spooler. Otherwise, nothing can be done because the * spooler is not installed. ******************************************************************************/ if (argc == 1) { spooler(root_tkn); } else { printf("ERROR: Spooler not installed.\n"); status = E$OK; } } } /*9**************************************************************************** * If any RMX errors occurred during execution, display an error * message. Finally, exit the program. ******************************************************************************/ if (status != E$OK) { printf("ERROR (%04xh): Could not execute spooler command.\n", status); } exit(0); } /*1**************************************************************************** * * Function: command_code * * Summary: Searches the array of valid commands for a match on the * given command. * * Invocation: code = command_code(cmnd_ptr) * * Inputs: cmnd_ptr (char *) - address of command to search for * * Outputs: code (int) - returns NOT_FOUND (-1), if the command * was not found in the array of valid commands; * Otherwise, the value is the code for the command. * ******************************************************************************/ command_code(cmnd_ptr) char *cmnd_ptr; { int code; int indx; code = NOT_FOUND; for (indx = 0; (spooler_cmnds[indx] != NULL) && (code == NOT_FOUND); indx++) { if (strcmp(cmnd_ptr, spooler_cmnds[indx]) == 0) { code = indx; } } return (code); } /*1**************************************************************************** * * Function: spooler_commands * * Summary: Check for any spooler commands * * Invocation: spooler_state = spooler_commands(spooler_mbx) * * Inputs: spooler_mbx (unsigned int) - the token for the mailbox * where spooler commands will be received. * * Outputs: spooler_state (int) - the current command state of the * spooler (ie. shutdown, continue) * ******************************************************************************/ spooler_commands(spooler_mbx) unsigned int spooler_mbx; { int command; unsigned int rsp_mbx; /* command response mailbox */ unsigned int msg_tkn; /* command message token */ unsigned int status; struct spooler_msg_str *spooler_msg_ptr; /*9**************************************************************************** * If a spooler shutdown is pending, then don't process any more * commands. Otherwise, check if a spooler command message is waiting * to be processed. ******************************************************************************/ if (spooler_state != CMND_SHUTDOWN) { command = CMND_CONTINUE; msg_tkn = rq$receive$message(spooler_mbx, NO_WAIT, &rsp_mbx, &status); if (status == E$OK) { /*9**************************************************************************** * If a command message was received, get the command code from the * message and return to message to acknowledge the command was received. * Then, process the command ******************************************************************************/ spooler_msg_ptr = bld_ptr(msg_tkn); command = spooler_msg_ptr->cmnd_code; rq$send$message(rsp_mbx, msg_tkn, NO_RESP, &status); switch (command) { /*9**************************************************************************** * If the command is SUSPEND, then wait until a non-SUSPEND command * is received. If a problem occurs, shutdown the spooler. ******************************************************************************/ case CMND_SUSPEND: { while (command == CMND_SUSPEND) { msg_tkn = rq$receive$message( spooler_mbx, WAIT_FOREVER, &rsp_mbx, &status); if (status == E$OK) { spooler_msg_ptr = bld_ptr(msg_tkn); command = spooler_msg_ptr->cmnd_code; rq$send$message(rsp_mbx, msg_tkn, NO_RESP, &status); } else { command = CMND_SHUTDOWN; } } break; } /*9**************************************************************************** * Any other valid commands require no additional processing in this * routine. They will be passed back to the main spooler code. ******************************************************************************/ case CMND_SHUTDOWN: case CMND_CONTINUE: case CMND_NEXTFILE: { break; } /*9**************************************************************************** * If an invalid command should get through, it will be handled like * a CONTINUE command. Which is basically a "do nothing" command. ******************************************************************************/ default: { command = CMND_CONTINUE; break; } } } /*9**************************************************************************** * Set the new state to the value of the command. ******************************************************************************/ spooler_state = command; } return (spooler_state); } /*1**************************************************************************** * * Function: spooler * * Summary: The main code for the installed spooler job * * Invocation: spooler(root_tkn) * * Inputs: root_tkn (unsigned int) - the token for the root job. * This is needed so that the spooler mailbox can be * catalogued in the root job's object directory. * * Outputs: none * ******************************************************************************/ spooler(root_tkn) unsigned int root_tkn; { char buffer[128]; /* file I/O buffer */ char fname[15]; /* file name ("C" format) */ int command; /* spooler command code */ unsigned int job_tkn; /* spooler job token */ unsigned int user_tkn; /* SUPER user object token */ unsigned int dir_con; /* spooler directory connection */ unsigned int status; /* RMX condition code */ unsigned int byte_cnt; /* bytes read from file */ unsigned int spooler_mbx; /* spooler mailbox */ struct dir_entry_str dir_entry; /* RMX directory entry */ struct finfo_str file_info; /* file information data */ FILE *lp; /* FILE pointer for printer */ FILE *fp; /* FILE pointer for file */ /*9**************************************************************************** * Create and catalog the print spooler mailbox. * NOTE: RMX uses byte count strings (first byte in string is length). ******************************************************************************/ spooler_mbx = rq$create$mailbox(FIFO_MBX, &status); if (status == E$OK) { rq$catalog$object(root_tkn, spooler_mbx, SPOOLER_MBX_NAME, &status); } if (status != E$OK) { printf("ERROR (%04xh): Could not create spooler mailbox.\n", status); exit(1); } /*9**************************************************************************** * Setup the print spooler as a SUPER user. The SUPER user can get * access to files created by any system user. ******************************************************************************/ job_tkn = rq$get$task$tokens(THIS_JOB, &status); if (status == E$OK) { user_tkn = rq$create$user(&user_ids, &status); if (status == E$OK) { rq$set$default$user(job_tkn, user_tkn, &status); } } if (status != E$OK) { printf("ERROR (%04xh): Could not setup spooler as a SUPER user.\n", status); exit(1); } /*9**************************************************************************** * Attach to the spooler directory and setup this directory as the * default directory for future file accessing. * NOTE: RMX uses byte count strings (first byte in string is length). ******************************************************************************/ dir_con = rq$s$attach$file(spooler_dir, &status); if (status == E$OK) { rq$set$default$prefix(job_tkn, dir_con, &status); } if (status != E$OK) { printf("ERROR (%04xh): Could not setup %s as spooler directory.\n", status, &spooler_dir[1]); exit(1); } /*9**************************************************************************** * Attach the system printer. ******************************************************************************/ lp = fopen(PRINTER_NAME, "w"); if (lp == NULL) { printf("ERROR: Could not attach the system printer\n"); exit(1); } /*9**************************************************************************** * Begin processing files that appear in the spooler directory until * a shutdown command is received. Each execution of this loop represents * one complete pass through the spooler directory's "directory file". * The directory file contains an entry for each file in the directory. * It also may contain empty entries from delete files. At the end of * each pass, the directory file is closed. Spooler commands are check * for at various points. ******************************************************************************/ command = spooler_commands(spooler_mbx); while ((status == E$OK) && (command != CMND_SHUTDOWN)) { rq$s$open(dir_con, READ_ONLY, BUFFERS_TWO, &status); if (status == E$OK) { /*9**************************************************************************** * Process directory entries until no more entries are found. ******************************************************************************/ byte_cnt = rq$s$read$move(dir_con, &dir_entry, sizeof(dir_entry), &status); command = spooler_commands(spooler_mbx); while ((byte_cnt == sizeof(dir_entry)) && (command == CMND_CONTINUE)) { /*9**************************************************************************** * Copy the file name into a local buffer and convert it to a C string. * If the entry is not empty (file node value is non-zero), then permit * complete access to the file (Delete, Read, Append, Update). It is * possible that the file could still be in the process of being copied to * the spooler directory, so wait until there a no users connected with the * file. ******************************************************************************/ strncpy(fname, dir_entry.fname, (sizeof(fname) - 1)); fname[sizeof(fname) - 1] = NUL; if (dir_entry.fnode != 0) { faccess(fname, USER_SUPER, ACCESS_DRAU); status = finfo(fname, &file_info); if (status == E$OK) { while (file_info.number_connections > 0) { rq$sleep(WAIT_ONE_SEC, &status); finfo(fname, &file_info); } /*9**************************************************************************** * Open the file, send a formfeed to the printer and begin copying the * file to the printer. Spooler commands will be checked for as each buffer * of data is transfered to the printer. A command other than CONTINUE, will * terminate the printing of the file. When printing if finish, possibly * due to a command, the file is close, the printer buffer flushed, and the * file deleted from the spooler directory. ******************************************************************************/ fp = fopen(fname, "r"); if (fp != NULL) { fputc(FF, lp); byte_cnt = fread(buffer, 1, sizeof(buffer), fp); command = spooler_commands(spooler_mbx); while ((byte_cnt > 0) && (command == CMND_CONTINUE)) { fwrite(buffer, 1, byte_cnt, lp); byte_cnt = fread(buffer, 1, sizeof(buffer), fp); command = spooler_commands(spooler_mbx); } fclose(fp); fflush(lp); fdelete(fname); } } } byte_cnt = rq$s$read$move(dir_con, &dir_entry, sizeof(dir_entry), &status); command = spooler_commands(spooler_mbx); } rq$s$close(dir_con, &status); } rq$sleep(WAIT_TWO_SEC, &status); command = spooler_commands(spooler_mbx); } /*9**************************************************************************** * Spooler is being shutdown so cleanup before exiting. ******************************************************************************/ rq$s$delete$connection(dir_con, &status); rq$uncatalog$object(root_tkn, SPOOLER_MBX_NAME, &status); rq$delete$mailbox(spooler_mbx, &status); fclose(lp); exit(0); }