/* * ************************************************************************** ** * ** © Copyright 2003 Hewlett-Packard Development Company, L.P. * ** * ** Confidential computer software. Valid license from HP and/or * ** its subsidiaries required for possession, use, or copying. * ** * ** Consistent with FAR 12.211 and 12.212, Commercial Computer Software, * ** Computer Software Documentation, and Technical Data for Commercial * ** Items are licensed to the U.S. Government under vendor's standard * ** commercial license. * ** * ** Neither HP nor any of its subsidiaries shall be liable for technical * ** or editorial errors or omissions contained herein. The information * ** in this document is provided "as is" without warranty of any kind and * ** is subject to change without notice. The warranties for HP products * ** are set forth in the express limited warranty statements accompanying * ** such products. Nothing herein should be construed as constituting an * ** additional warranty. * ** * ************************************************************************** * * ++ * FACILITY: * * PTD * * ABSTRACT: * Simple program to demonstrate using a Pseudo Terminal. This * program is based loosely on the popular PHOTO program that is available * on the ARPA net. The program is discussed in Appendix B of the pseudo * terminal functional sepcification. * * * AUTHOR: Forrest A. Kenney 01-Nov-1989 * * Revision history: * * X-03 FAK002 Forrest A. Kenney 10-July-2003 * Fix a bug that would always case a spurious wake up of the * the code. Add two includes and a typedef to clean up the * compilation. Should no longer need to add standard=vaxc * when compiling on an Alpha system. * * X-02 FAK001 Forrest A. Kenney 28-Apr-1992 * Make single common source code for EVMS and VMS. Remove * reference IOC$GW_MAXBUF and use $GETSYI instead. The removes * the need to link against SYS.STB and removes tie to system * version numbers. * * -- * * Compile and link instructions: * * For VAX/VMS: * * $ CC LOGGER * $ ! * $ ! Command file to link logger program * $ ! * $ link LOGGER/MAP=LOGGER, sys$input:/opt * sys$share:vaxcrtl/share * $ exit * * For Alpha/VMS * $ CC/STANDARD=VAXC LOGGER * $ ! * $ ! Command file to link logger program * $ ! * $ link LOGGER/MAP=LOGGER * * -- */ /* Define constants */ #define BELL 0x7 #define BUFFIO_OVERHEAD 78 /* Overhead of TERMINAL buffered I/O */ #define CR 0x0D #define CHAR_BUF_SIZE 492 /* Space in I/O buffer for data */ #define FALSE 0 #define IO_BUFFERS 6 /* Number of one page I/O buffers */ #define LF 0x0A #define ASCII_NULL '\0' /* ASCII null character */ #define NOWAIT 1 /* No wait flag for LIB$SPAWN */ #define REQUEST_LENGTH 0X1FF /* For traditional VAX/VMS this is */ /* the last byte in a page */ #define BUFFER_SIZE 0X200 /* Size of a I/O BUFFER in bytes. For */ /* traditional VAX/VMS this is 1 page.*/ #define TRUE 1 #define XOFF 0x13 #define XON 0x11 #define PTD$C_SEND_XON 0 /* Pseudo Terminal Driver event */ #define PTD$C_SEND_BELL 1 /* types. When these are in */ #define PTD$C_SEND_XOFF 2 /* SYS$LIBRARY:VAXCDEF.TLB they */ #define PTD$C_STOP_OUTPUT 3 /* should be removed from here. */ #define PTD$C_RESUME_OUTPUT 4 #define PTD$C_CHAR_CHANGED 5 #define PTD$C_ABORT_OUTPUT 6 #define PTD$C_START_READ 7 #define PTD$C_MIDDLE_READ 8 #define PTD$C_END_READ 9 #define PTD$C_ENABLE_READ 10 #define PTD$C_DISABLE_READ 11 #define PTD$C_MAX_EVENTS 12 /* Include various necessary description files */ #include #include #include #include #include #pragma nomember_alignment #include #pragma member_alignment #include #include #include #include #include #include /* Define several gloabl structures */ struct dev_char /* Device characteristics block */ { unsigned char class; unsigned char type; short int buffer_size; unsigned int basic_chars; unsigned int extended_chars; }; struct iosb /* Standard I/O status block */ { short int status; short int byte_cnt; int unused; }; struct sense_iosb /* IOSB for set and sense requests */ { short int status; unsigned char xmit_speed; unsigned char rcv_speed; unsigned char cr_fill; unsigned char lf_fill; unsigned char parity_flags; unsigned char unused; }; struct item /* VMS item list item */ { short int buf_len; short int item_code; int *buff_addr; int *ret_addr; }; struct io_buff /* I/O block used by logger code */ { int *flink; /* forward and backard queue links */ int *blink; short int status; /* IOSB used to terminal requests */ short int byte_cnt; int unused; short int io_status; /* Status longword used by pseudo */ short int io_byte_cnt; /* terminal control requests */ char data[CHAR_BUF_SIZE]; /* Data buffer */ }; struct q_head /* Queue head structure */ { int *flink; int *blink; }; /* Forward routine references */ int initialization(); int create_log_file(); int create_pseudo_terminal(); int setup_tty(); struct io_buff *allocate_io_buffer(); typedef struct _mem_region { struct io_buff *start; struct io_buff *end; } MEM_REGION; void bell_ast(); void free_io_buffer(); void ft_echo_ast(); void ft_read_ast(); void kbd_read_ast(); void set_line_ast(); void subprocess_exit(); void terminal_output_ast(); void xoff_ast(); void xon_ast(); /* Global Variables*/ char *rec_buffer; char *char_pos; short int char_count; short int cr_seen = FALSE; short int exiting = FALSE; short int ft_chan; short int have_subprocess = TRUE; short int read_stopped = FALSE; short int tty_chan; int exit_status; int pid; int term_mask[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int maxbuf; struct FAB logger_fab; struct RAB logger_rab; struct dev_char starting_chars; struct sense_iosb starting_iosb; struct io_buff *tty_r_buff; struct q_head _align(QUADWORD) buffer_queue = {0,0}; struct q_head _align(QUADWORD) log_queue = {0,0}; struct term_descrip { short int size; short int unused; int *ptr; } term_block = {32, 0, &term_mask[0]}; /* **+ ** main - Main routine ** ** Functional Description: ** ** The program intitializes the environment and then hibernates waiting to ** be awakened. When awakened, it checks to see if exiting or if more log data ** is available. If more data to log, the data is appended to the current log ** record and checked to see if a log record should be written. A log record ** will be written when either maxbuf characters are in the log buffer, or a ** character pair are seen. The algorithm allows an unlimited ** number of fill characters to occur between the and the ** . If exiting the program closes the log file, deletes the pseudo ** terminal, resets terminal, and exits. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** char_pos - Point to next available character position in ** rec_buffer ** char_count - Number of characters presently in rec_buffer ** cr_seen - Flag indication if last significant character ** seen was a ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** ft_chan - VMS channel pointing to control side of pseudo ** terminal ** have_subprocess - Flag indicating if subprocess is still running ** maxbuf - Cell containing value of largest legal ** buffered I/O ** log_queue - Queue of I/O buffers to be written to log file ** logger_fab - File Access Block of log file ** logger_rab - Record Access Block of log file ** pid - Process ID of subprocess ** rec_buffer - Pointer to buffer holding characters to be ** written to the log file ** starting_chars - Terminal characteristics when logger started up ** tty_chan - VMS channel pointing to terminal ** ** Local Variables: ** ** buffer_pos - Character position in current log buffer ** got_buf_status - Return status from call to remove log buffer ** from log queue ** log_buff - Pointer to current log buffer being processed ** set_iosb - I/O status block used when resetting terminal ** to startup characteristics ** status - Return status from various routine calls ** ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ main() { int buffer_pos; int got_buf_status; int status; struct io_buff *log_buff; struct sense_iosb set_iosb; status= initialization(); if (status & SS$_NORMAL) { do { got_buf_status = LIB$REMQHI(&log_queue, &log_buff); while (got_buf_status & SS$_NORMAL) { for (buffer_pos = 0; buffer_pos < log_buff->io_byte_cnt; buffer_pos++) { if (cr_seen) { if (log_buff->data[buffer_pos] == LF) { logger_rab.rab$w_rsz = char_count; status = SYS$PUT(&logger_rab); if (!(status & SS$_NORMAL)) { SYS$FORCEX(&pid, 0, 0); LIB$SIGNAL(status); } cr_seen = FALSE; char_count = 0; char_pos = rec_buffer; } else if (log_buff->data[buffer_pos] != ASCII_NULL) { *char_pos++ = CR; *char_pos++ = log_buff->data[buffer_pos]; char_count += 2; cr_seen = FALSE; } } else if (log_buff->data[buffer_pos] != CR) { *char_pos++ = log_buff->data[buffer_pos]; char_count += 1; } else { cr_seen = TRUE; } if (char_count >= (maxbuf-BUFFIO_OVERHEAD)) { logger_rab.rab$w_rsz = char_count; status = SYS$PUT(&logger_rab); if (!(status & SS$_NORMAL)) { SYS$FORCEX(&pid, 0, 0); LIB$SIGNAL(status); } cr_seen = FALSE; char_count = 0; char_pos = rec_buffer; } } free_io_buffer(log_buff); got_buf_status = LIB$REMQHI(&log_queue, &log_buff); } if (!exiting) SYS$HIBER(); } while ((!exiting) || (log_queue.flink != 0)); if (char_count != 0) { logger_rab.rab$w_rsz = char_count; status = SYS$PUT(&logger_rab); if (!(status & SS$_NORMAL) && (exit_status & SS$_NORMAL)) exit_status = status; } status = SYS$CLOSE(&logger_fab); if (!(status & SS$_NORMAL) && (exit_status & SS$_NORMAL)) exit_status = status; if (have_subprocess) SYS$FORCEX (&pid, 0, 0); PTD$CANCEL(ft_chan); SYS$CANCEL(tty_chan); status = PTD$DELETE(ft_chan); if (!(status & SS$_NORMAL) && (exit_status & SS$_NORMAL)) exit_status = status; status = SYS$QIOW(0, tty_chan, IO$_SETMODE, &set_iosb, 0, 0, &starting_chars, sizeof(starting_chars), 0, 0, 0, 0); if (!(status & SS$_NORMAL) && (exit_status & SS$_NORMAL)) exit_status = status; if (!(set_iosb.status & SS$_NORMAL) && (exit_status & SS$_NORMAL)) exit_status = set_iosb.status; } else { exit_status = status; } LIB$SIGNAL(exit_status); } /* **+ ** initialization - Set up environment ** ** Functional Description: ** ** This routine sets the terminal characteristics, creates the pseudo ** terminal, starts up the subprocess, and opens the log file. If any of these ** steps fails it backs out all steps done up to this points and returns to the ** mainline. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** buffer_queue - Queue of free I/O buffer ** char_pos - Point to next available character position in ** rec_buffer ** ft_chan - VMS channel pointing to control side of pseudo ** terminal ** maxbuf - Cell containing value of largest legal ** buffered I/O ** logger_fab - File Access Block of log file ** rec_buffer - Pointer to buffer holding characters to be ** written to the log file ** starting_chars - Terminal characteristics when logger started up ** starting_iosb - IOSB used to hold starting characteristics like ** terminal speed and parity ** tty_chan - VMS channel pointing to terminal ** tty_r_buff - Pointer to I/O buffer used to read characters ** from terminal ** ** Local Variables: ** ** loop - Temporary loop counter used will inserting I/O ** buffers onto queue of free I/O buffers ** rec_size - Local varible used to pass record buffer size ** to LIB$GET_VM ** ret_addr - Pointer to start and end of pages allocated for ** I/O buffers ** status - Return status from various routine calls ** tty_name - String descriptor contianing terminal name ** ** syi_item1 - $GETSYI item list to read system MAXBUF ** parameter ** ** ** Outputs: ** ** R0 - SS$_NORMAL ** Various error status values ** ** NOTES: ** ** None ** **- */ initialization(void) { $DESCRIPTOR(tty_name,"TT:"); int loop; int rec_size; int status; MEM_REGION ret_addr; struct { struct item item1; int end; } syi_item1 = {4, SYI$_MAXBUF, &maxbuf, 0, 0}; struct iosb iosb; status = SYS$GETSYIW(0, 0, 0, &syi_item1, &iosb, 0, 0); rec_size = maxbuf; status = LIB$GET_VM (&rec_size, &rec_buffer); if (status & SS$_NORMAL) { char_pos = rec_buffer; status = SYS$EXPREG (IO_BUFFERS, &ret_addr, 0, 0); if (status & SS$_NORMAL) { tty_r_buff = (struct io_buff *)((char *)ret_addr.end - REQUEST_LENGTH); for (loop = 0; loop < IO_BUFFERS-1; loop++) { free_io_buffer((char *)ret_addr.start + loop*BUFFER_SIZE); } status = SYS$ASSIGN(&tty_name, &tty_chan, 0, 0); if (status &SS$_NORMAL) { status = SYS$QIOW (0, tty_chan, IO$_SENSEMODE, &starting_iosb, 0, 0, &starting_chars, sizeof(starting_chars), 0, 0, 0, 0); if ((status & SS$_NORMAL) && (starting_iosb.status & SS$_NORMAL)) { status = create_pseudo_terminal(&ret_addr); if (status & SS$_NORMAL) { status = create_log_file(); if (status & SS$_NORMAL) { status = setup_tty(); if (!(status & SS$_NORMAL)) { PTD$DELETE(ft_chan); logger_fab.fab$l_fop = logger_fab.fab$l_fop | FAB$M_DLT; SYS$CLOSE(&logger_fab); } } else { PTD$DELETE(ft_chan); } } } else if (status & SS$_NORMAL) { status = starting_iosb.status; } } } } return(status); } /* **+ ** setup_tty - Start subprocess and setup terminal ** ** Functional Description: ** ** ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** ft_chan - VMS channel pointing to control side of pseudo ** terminal ** pid - Process ID of subprocess ** starting_chars - Terminal characteristics when logger started up ** term_block - Long form to terminal driver read termination ** mask ** tty_chan - VMS channel pointing to terminal ** ** Local Variables: ** ** devnam - String descriptor containing name of pseudo ** terminal ** flags - Flags passed to LIB$SPAWN set to NOWAIT ** item_list - VMS item list used to request device name using ** SYS$GETDVIW ** modified_chars - Device characteristics used to setup terminal ** to have attributes we want ** set_iosb - IOSB used to synchronize various requests ** status - Return status from various routine calls ** tmp_status - Return status from various routine calls used ** on error path so that original error status is ** preserved ** ** Outputs: ** ** R0 - SS$_NORMAL ** Various error status values ** ** NOTES: ** ** None ** **- */ int setup_tty(void) { char devnam_string[16]; $DESCRIPTOR(devnam,devnam_string); int flags = NOWAIT; int status; int tmp_status; struct { short int buf_len; short int item; char *buf_addr; unsigned short int *ret_len; int end; } item_list = {devnam.dsc$w_length, DVI$_DEVNAM, devnam.dsc$a_pointer, &devnam.dsc$w_length, 0}; struct dev_char modified_chars; struct sense_iosb set_iosb; status = SYS$GETDVIW (0, ft_chan, 0, &item_list, &set_iosb, 0, 0, 0); if (status & SS$_NORMAL) { status = LIB$SPAWN(0, &devnam, &devnam, &flags, 0, &pid, 0, 0, subprocess_exit, 0); if (status & SS$_NORMAL) { modified_chars = starting_chars; modified_chars.basic_chars = modified_chars.basic_chars | TT$M_NOECHO; modified_chars.extended_chars = modified_chars.extended_chars | TT2$M_PASTHRU; status = SYS$QIOW (0, tty_chan, IO$_SETMODE, &set_iosb, 0, 0, &modified_chars, sizeof(modified_chars), 0, 0, 0, 0); if ((status & SS$_NORMAL) && (set_iosb.status & SS$_NORMAL)) { status = SYS$QIO (0, tty_chan, IO$_READVBLK, &tty_r_buff->status, kbd_read_ast, 0, &tty_r_buff->data[0], 1, 0, &term_block, 0, 0); if (!(status & SS$_NORMAL)) { tmp_status = SYS$QIOW (0,tty_chan, IO$_SETMODE, &set_iosb, 0, 0, &starting_chars, sizeof(starting_chars), 0, 0, 0, 0); tmp_status = SYS$FORCEX (&pid, 0, 0); } } else if (status & SS$_NORMAL) { status = set_iosb.status; tmp_status = SYS$FORCEX (&pid, 0, 0); } } } return(status); } /* **+ ** create_pseudo_terminal - Create and setup pseudo terminal ** ** Functional Description: ** ** This routine creates the pseudo terminal enables the AST's and starts ** the first read on the pseudo terminal. ** ** Explicit Inputs: ** ** ret_addr - Pointer to start and end of pages allocated for ** I/O buffers ** ** Implicit Inputs: ** ** ft_chan - VMS channel pointing to control side of pseudo ** terminal ** starting_chars - Terminal characteristics when logger started up ** ** Local Variables: ** ** read_buffer - Pointer to I/O buffer used to hold data read ** from the pseudo terminal ** status - Return status from various routine calls ** ** Outputs: ** ** R0 - SS$_NORMAL ** Various return status codes ** ** NOTES: ** ** None ** **- */ create_pseudo_terminal (MEM_REGION *ret_addr) { int status; struct io_buff *read_buffer; status = PTD$CREATE(&ft_chan, 0, &starting_chars, sizeof(starting_chars), 0, 0, 0, ret_addr); if (status & SS$_NORMAL) { status = PTD$SET_EVENT_NOTIFICATION (ft_chan, bell_ast, 0, 0, PTD$C_SEND_BELL); if (status & SS$_NORMAL) { status = PTD$SET_EVENT_NOTIFICATION (ft_chan, xoff_ast, 0, 0, PTD$C_SEND_XOFF); if (status & SS$_NORMAL) { status = PTD$SET_EVENT_NOTIFICATION (ft_chan, xon_ast, 0, 0, PTD$C_SEND_XON); if (status &SS$_NORMAL) { status = PTD$SET_EVENT_NOTIFICATION (ft_chan, set_line_ast, 0, 0, PTD$C_CHAR_CHANGED); if (status & SS$_NORMAL) { read_buffer = allocate_io_buffer(); if (((int)read_buffer != LIB$_QUEWASEMP) && ((int)read_buffer != SS$_FORCEDEXIT)) { status = PTD$READ (0, ft_chan, ft_read_ast, read_buffer, &read_buffer->io_status, CHAR_BUF_SIZE); } } } } } if (!(status & SS$_NORMAL)) PTD$DELETE (ft_chan); } return(status); } /* **+ ** create_log_file - Initialize FAB, RAB and open log file ** ** Functional Description: ** ** This routine initialize the File Access Block (FAB) and the Record ** Access Block (RAB). It opens the file and connects the RAB to the FAB so ** that the file is ready to have records written to it. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** logger_fab - File Access Block for log file ** logger_rab - Record Access Block for log file ** ** Local Variables: ** ** status - Return status from various routine calls ** ** Outputs: ** ** R0 - RMS$_NORMAL successful completion ** Various RMS errors ** ** NOTES: ** ** None ** **- */ int create_log_file (void) { int status; logger_fab = cc$rms_fab; logger_fab.fab$l_fop = FAB$M_SUP | FAB$M_SQO; logger_fab.fab$b_fac = FAB$M_PUT; logger_fab.fab$b_shr = FAB$M_SHRGET; logger_fab.fab$b_org = FAB$C_SEQ; logger_fab.fab$b_rat = FAB$M_CR; logger_fab.fab$b_rfm = FAB$C_RFM_DFLT; logger_fab.fab$l_fna = "SESSION"; logger_fab.fab$l_dna = ".LOG"; logger_fab.fab$b_fns = 7; logger_fab.fab$b_dns = 4; logger_fab.fab$w_mrs = maxbuf; status = SYS$CREATE(&logger_fab); if (status & SS$_NORMAL) { logger_rab = cc$rms_rab; logger_rab.rab$l_rbf = rec_buffer; logger_rab.rab$l_fab = &logger_fab; status = SYS$CONNECT (&logger_rab); } return (status); } /* **+ ** kbd_read_ast - Process characters typed at keyboard ** ** Functional Description: ** ** This routine is called every time data is read from the users terminal. ** If the program is exiting then the routine will exit without restarting the ** read. The character read is checked to see if the terminate processing ** character CTRL-\ was entered. If the terminate processing character was ** entered the exiting state will be set and a SYS$WAKE will be issued to start ** the mainline code. Now an attempt will be made to get an I/O buffer to ** store echoed output in. If cannot get an I/O buffer a simple PTD$WRITE will ** be used otherwise a PTD$WRITE with echo will be issued. If the write ** completed successfully another read read will be issued to the keyboard. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** ft_chan - VMS channel pointing to control side of pseudo ** terminal ** term_block - Long form to terminal driver read termination ** mask ** tty_chan - VMS channel pointing to terminal ** tty_r_buff - Pointer to I/O buffer used to read characters ** from terminal ** ** Local Variables: ** ** echo_buff - Pointe to I/O buffer used to receive any ** characters when data was written to the pseudo ** terminal ** status - Return status from various routine calls ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void kbd_read_ast(void) { int status; struct io_buff *echo_buff; if (!exiting) { if (tty_r_buff->status & SS$_NORMAL) { if (tty_r_buff->data[0] != '\034') { echo_buff = allocate_io_buffer(); if (((int)echo_buff != LIB$_QUEWASEMP) && ((int)echo_buff != SS$_FORCEDEXIT)) { status = PTD$WRITE (ft_chan, ft_echo_ast, echo_buff, &tty_r_buff->io_status, tty_r_buff->byte_cnt, &echo_buff->io_status, CHAR_BUF_SIZE); } else { status = PTD$WRITE (ft_chan, 0, echo_buff, &tty_r_buff->io_status, tty_r_buff->byte_cnt, 0, 0); } if (status & SS$_NORMAL) { if ((tty_r_buff->io_status & SS$_NORMAL) || (tty_r_buff->io_status == SS$_DATAOVERUN)) { status = SYS$QIO (0, tty_chan, IO$_READVBLK, &tty_r_buff->status, kbd_read_ast, 0, &tty_r_buff->data[0], 1, 0, &term_block, 0, 0); if ((status & SS$_NORMAL) != SS$_NORMAL) { exiting = TRUE; exit_status = status; } } else { exiting = TRUE; exit_status = tty_r_buff->io_status; } } else { exiting = TRUE; exit_status = status; } } else { exiting = TRUE; exit_status = SS$_NORMAL; } } else { exiting = TRUE; exit_status = tty_r_buff->status; } if (exiting) SYS$WAKE(0, 0); } } /* **+ ** terminal_output_ast - Queue output to logging queue ** ** Functional Description: ** ** This routine is called every time an a I/O buffer has been written to ** the terminal. If the terminal write request completed successfully, it will ** queue the I/O buffer into the queue of I/O buffers to be logged. If the I/O ** buffer is the only entry on the queue then it will issue a SYS$WAKE to start ** the mainline. If multiple entries are already on the queue then it will not ** issue a SYS$WAKE. This should prevent spurious wake requests from ** occurring. If a terminal write error occurred it will set the exit flag and ** wake the mainline. ** ** Explicit Inputs: ** ** buff_addr - Pointer to I/O buffer to be placed on log queue ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** log_queue - Queue of I/O buffers to be written to log file ** ** Local Variables: ** ** status - Return status from LIB$INSQTI call ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void terminal_output_ast(buff_addr) struct io_buff *buff_addr; { int status; if (buff_addr->status & SS$_NORMAL) { status = LIB$INSQTI(buff_addr, &log_queue); if (status == LIB$_ONEENTQUE) { SYS$WAKE (0, 0); } } else { exiting = TRUE; exit_status = buff_addr->status; SYS$WAKE (0, 0); } } /* **+ ** ft_read_ast - Output characters read from pseudo terminal ** ** Functional Description: ** ** This routine is called when a pseudo terminal read request completes. ** It will write the buffer to the terminal and attempt to start another read ** from the pseudo terminal. If the program is not exiting it will write the ** buffer to the terminal, and it will attempt to start another pseudo terminal ** read. ** ** Explicit Inputs: ** ** buff_addr - Pointer to I/O buffer containing characters ** read from pseudo terminal ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** ft_chan - VMS channel pointing to control side of pseudo ** terminal ** exit_status - Contains the reason while logging is stopping ** tty_chan - VMS channel pointing to terminal ** ** Local Variables: ** ** status - Return status from various routine calls ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void ft_read_ast(buff_addr) struct io_buff *buff_addr; { int status; if (!exiting) { if (buff_addr->io_status & SS$_NORMAL) { status = SYS$QIO (0, tty_chan, IO$_WRITEVBLK, &buff_addr->status, terminal_output_ast, buff_addr, &buff_addr->data[0], buff_addr->io_byte_cnt, 0, 0, 0, 0); if (status & SS$_NORMAL) { buff_addr = allocate_io_buffer(); if (((int)buff_addr != LIB$_QUEWASEMP) && ((int)buff_addr != SS$_FORCEDEXIT)) { status = PTD$READ (0, ft_chan, ft_read_ast, buff_addr, &buff_addr->io_status, CHAR_BUF_SIZE); if ((status & SS$_NORMAL) != SS$_NORMAL) { exiting = TRUE; exit_status = status; SYS$WAKE (0, 0); } } else { read_stopped = TRUE; } } else { exiting = TRUE; exit_status = status; SYS$WAKE (0, 0); } } else { exiting = TRUE; exit_status = buff_addr->io_status; SYS$WAKE (0, 0); } } } /* **+ ** ft_echo_ast - Write any immediately echoed data ** ** Functional Description: ** ** This routine is called if a write to the pseudo terminal used an ECHO ** buffer. If any data was echoed it needs to write the output to the ** terminal. If no data was echoed then the I/O buffer needs to be freed so it ** can be used later. If the program is exiting this routine will just exit and ** not worry about cleaning up. ** ** Explicit Inputs: ** ** buff_addr - Pointer to I/O buffer contianing any characters ** that resulted from a write to the pseudo ** terminal ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** tty_chan - VMS channel pointing to terminal ** ** Local Variables: ** ** status - Return status from various routine calls ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void ft_echo_ast(buff_addr) struct io_buff *buff_addr; { int status; if (!exiting) { if (buff_addr->io_byte_cnt != 0) { status = SYS$QIO (0, tty_chan, IO$_WRITEVBLK, &buff_addr->status, terminal_output_ast, buff_addr, &buff_addr->data[0], buff_addr->io_byte_cnt, 0, 0, 0, 0); if ((status & SS$_NORMAL) != SS$_NORMAL) { exiting = TRUE; exit_status = status; status = SYS$WAKE (0, 0); } } else { free_io_buffer(buff_addr); } } } /* **+ ** free_io_buffer - Put I/O buffer on queue of available buffers ** ** Functional Description: ** ** This routine will place a free I/O buffer on the queue of available I/O ** buffers. It has the additional responsibility for restarting reads from the ** pseudo terminal if they were stopped. This routine disables AST delivery ** while running to synchronize reading and resetting the read stopped flag. ** This is a big hammer approach but is the easiest way to do it. ** ** Explicit Inputs: ** ** buff_addr - Pointer to I/O buffer to be placed on queue of ** free I/O buffers ** ** Implicit Inputs: ** ** buffer_queue - Queue of free I/O buffer ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** ** Local Variables: ** ** ast_stat - Return status from SYS$SETAST used to determine ** if AST delivery should turned back on ** status - Return status from PTD$READ ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void free_io_buffer (buff_addr) struct io_buff *buff_addr; { int ast_stat; int status; if (!exiting) { ast_stat = SYS$SETAST(0); if (!read_stopped) { LIB$INSQHI(buff_addr, &buffer_queue); } else { status = PTD$READ (0, ft_chan, ft_read_ast, buff_addr, &buff_addr->io_status, CHAR_BUF_SIZE); if (status & SS$_NORMAL) { read_stopped = FALSE; } else { exiting = TRUE; exit_status = status; status = SYS$WAKE (0, 0); } } if (ast_stat == SS$_WASSET) ast_stat = SYS$SETAST(1); } } /* **+ ** allocate_io_buffer - Get a free I/O buffer from queue ** ** Functional Description: ** ** This routine is used to get a free I/O buffer from the queue of ** available I/O buffers. If the program is exiting then this routine will ** return with an error. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** buffer_queue - Queue of free I/O buffer ** exiting - Flag indicating if program is finishing up ** processing ** ** Local Variables: ** ** buff_addr - Pointer to free I/O buffer if one was available ** status - Return status from LIB$REMQHI ** ** Outputs: ** ** return_status - Address of buffer or ** LIB$_QUEWASEMP no I/O buffer ** SS$_FORCEDEXIT program exiting ** ** NOTES: ** ** None ** **- */ struct io_buff *allocate_io_buffer(void) { int status; struct io_buff *buff_addr; if (!exiting) { status = LIB$REMQHI(&buffer_queue, &buff_addr); if (status & SS$_NORMAL) { return(buff_addr); } else { return((struct io_buff *)status); } } else { return((struct io_buff *)SS$_FORCEDEXIT); } } /* **+ ** subprocess_exit - Set up exit status when subprocess exists ** ** Functional Description: ** ** This routine will be called when the subprocess has completed and ** exited. This routine will see if program is already exiting. If not then it ** will indicate that the program is exiting and wake up the main program. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** exit_status - Contains the reason while logging is stopping ** have_subprocess - Flag indicating if subprocess is still running ** ** Local Variables: ** ** None ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void subprocess_exit(void) { if (!exiting) { have_subprocess = FALSE; exit_status = SS$_NORMAL; exiting = TRUE; SYS$WAKE (0, 0); } } /* **+ ** xon_ast - Send XON character to terminal ** ** Functional Description: ** ** This routine is called when the pseudo terminal driver wants to signal ** that it is ready to accept keyboard input. The routine will attempt to send ** an XON character to the terminal. This is done on a best effort basis - if ** it fails it will not be retried. This is done by sending XON DC1 to the ** terminal using SYS$QIO. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** tty_chan - VMS channel pointing to terminal ** ** Local Variables: ** ** dc1 - ASCII XON character to be sent to terminal ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void xon_ast(void) { char dc1 = XON; if (!exiting) { SYS$QIO (0, tty_chan, IO$_WRITEVBLK, 0, 0, 0, &dc1, 1, 0, 0, 0, 0); } } /* **+ ** bell_ast - Send BELL character to terminal ** ** Functional Description: ** ** This routine is called when the pseudo terminal driver wants to warn the ** user to stop sending keyboard data. The routine will attempt to ring the ** terminals bell. This is done on a best effort basis - if it fails it will ** not be retried. This is done by send the BELL character to the terminal ** using SYS$QIO. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** tty_chan - VMS channel pointing to terminal ** ** Local Variables: ** ** bell - ASCII bell character to be sent to terminal ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void bell_ast(void) { char bell = BELL; if (!exiting) { SYS$QIO (0, tty_chan, IO$_WRITEVBLK, 0, 0, 0, &bell, 1, 0, 0, 0, 0); } } /* **+ ** xoff_ast - Send XOFF character to terminal ** ** Functional Description: ** ** This routine is called when the pseudo terminal driver wants to signal ** that it wants to stop accepting keyboard input. The routine will attempt to ** send an XOFF character to the terminal. This is done on a best effort ** basis - if it fails it will not be retried. This is done by sending XOFF ** to the terminal using SYS$QIO. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** tty_chan - VMS channel pointing to terminal ** ** Local Variables: ** ** dc3 - ASCII XOFF character to be sent to terminal ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void xoff_ast(void) { char dc3 = XOFF; if (!exiting) { SYS$QIO (0, tty_chan, IO$_WRITEVBLK, 0, 0, 0, &dc3, 1, 0, 0, 0, 0); } } /* **+ ** set_line_ast - Mirror changes to FT changes onto TT ** ** Functional Description: ** ** This routine is called when the pseudo terminal's device characteristics ** changed. It will read the current pseudo terminal characteristics. These ** characteristics will be changed to have PASTHRU and NOECHO set. They will ** then be applied to the user's input terminal. Altering the terminal ** characteristics will be done on a best effort basis - if it fails it will ** not be retried. ** ** Explicit Inputs: ** ** None ** ** Implicit Inputs: ** ** exiting - Flag indicating if program is finishing up ** processing ** ft_chan - VMS channel pointing to control side of pseudo ** terminal ** tty_chan - VMS channel pointing to terminal ** ** Local Variables: ** ** device_chars - Structure used to hold to new pseudo temrinal ** charateristics that need to be applied to the ** actual terminal. These characteristics will be ** changed to have TT$M_NOECHO and TT2$M_PASTHRU ** set. ** fill - Word containing the and fill values ** sense_iosb - I/O status block filled in upon completion of ** sense mode $QIO ** set_iosb - I/O status block filled in upon completions of ** set mode $QIO ** speed - Terminal transmit and receive speeds ** status - Return status of set and sense mode $QIO ** operations ** ** Outputs: ** ** None ** ** NOTES: ** ** None ** **- */ void set_line_ast(void) { short int fill; short int speed; int status; struct dev_char device_chars; struct iosb set_iosb; struct sense_iosb sense_iosb; if (!exiting) { status = SYS$QIOW (0, ft_chan, IO$_SENSEMODE, &sense_iosb, 0, 0, &device_chars, sizeof(device_chars), 0, 0, 0, 0); if (status & SS$_NORMAL) { device_chars.basic_chars = device_chars.basic_chars | TT$M_NOECHO; device_chars.extended_chars = device_chars.extended_chars | TT2$M_PASTHRU; speed = sense_iosb.xmit_speed | (sense_iosb.rcv_speed << 8); fill = sense_iosb.cr_fill | (sense_iosb.lf_fill << 8); status = SYS$QIO (0, tty_chan, IO$_SETMODE, &set_iosb, 0, 0, &device_chars, sizeof(device_chars), speed, fill, sense_iosb.parity_flags,0); } } }