?? RIGHT := 110 ??
MODULE ifm$job_control;
?? NEWTITLE := 'NOS/VE Interactive Facility User Job Terminal Access' ??
?? TITLE := 'Purpose and Design', EJECT ??

{  PURPOSE:
{    The purpose of this module is to provide various basic interactive
{    functions such as:
{    - initialize an interactive job
{    - terminate an interactive job
{    - process most synchronous supervisory network messages.
{    - manage disconnect and reconnect
{

?? PUSH (LISTEXT := ON) ??
*copyc amp$get_next
*copyc amp$return
*copyc amp$open
*copyc amp$close
*copyc amp$put_next
*copyc clc$standard_file_names
*copyc clp$find_current_job_synch_task
*copyc clp$get_system_file_id
*copyc clp$include_command
*copyc clp$put_job_output
*copyc oss$job_paged_literal
*copyc osv$job_pageable_heap
*copyc tmc$signal_identifiers
*copyc ift$title_for_error_codes
*copyc ifp$immediate_attribute_flush
*copyc ifk$keypoints
*copyc iit$interactive_signal_type
*copyc iit$connection_description
*copyc iip$add_sender
*copyc iip$set_lock
*copyc iip$clear_lock
*copyc iip$request_default_attributes
*copyc iip$route
*copyc i#move

  PROCEDURE [XREF] iip$discard_typed_ahead_input
    (    save: boolean);

*copyc iip$allocate_queue_entry
*copyc iip$build_data_msg_skeleton
*copyc iip$build_super_msg_skeleton
*copyc iip$convert_downline_block
*copyc iip$initialize_connection
*copyc iiv$interactive_terminated
*copyc iip$register_handler
*copyc clp$put_job_output

{*copyc jmp$get_job_internal_info

*copyc iip$receive_from_pass_on
*copyc iip$report_logical_error
*copyc iip$report_status_error
*copyc iip$report_unhandled_data_msg
*copyc iip$report_unhandled_super_msg
*copyc iip$sign_off
*copyc iip$sign_on
*copyc iip$send_output_message
*copyc iip$send_to_pass_on
*copyc iiv$int_task_open_file_count
*copyc iiv$connection_desc_ptr
*copyc iiv$output
*copyc jmv$terminal_io_disabled
*copyc jme$queued_file_conditions
*copyc pmp$long_term_wait
*copyc lgp$display_log
*copyc jmp$set_job_mode
*copyc jmp$get_job_attributes
*copyc jmp$display_job_status
*copyc pmp$continue_to_cause
*copyc pmp$disable_ts_io_in_tasks
*copyc pmp$display_active_tasks
*copyc pmp$enable_ts_io_in_tasks
*copyc pmp$get_user_identification
*copyc pmp$task_state
*copyc pmp$get_job_names
*copyc pmp$signal_all_child_tasks
*copyc osp$system_error
*copyc osp$set_status_abnormal
*copyc jmp$get_job_parameters
*copyc osp$system_error
*copyc pmp$exit
*copyc pmp$get_global_task_id
*copyc pmp$log
*copyc pmp$send_signal
*copyc pmp$establish_condition_handler
*copyc osv$job_pageable_heap
*copyc osv$170_os_type
*copyc osp$set_job_signature_lock
*copyc osp$clear_job_signature_lock
*copyc pmp$wait
*copyc mlp$force_send_message
*copyc mlp$send_message
?? POP ??

?? TITLE := 'Global Internal Type, Constant and Variable Declarations', EJECT
        ??

  TYPE
    iit$mli_status = set of mlt$status;

  VAR

{ Job Global Read/Write variables.

    iiv$reject_caused_reconnect: boolean := FALSE,
    iiv$end_job_connection,
    iiv$start_new_job,
    connection_ended: boolean := FALSE,
    connection_started: boolean := FALSE,
    connection_rejected: boolean := FALSE,
    wait_change_jm: boolean := FALSE,
    wait_hold_ack: boolean := FALSE,

{ Miscellaneous Read Only variables.

    shutdown_exit_msg: [READ, oss$job_paged_literal] string (57) :=
          'Network Shutdown - Please Do LOGOUT As Soon As Possible.',
    conend_zero2_value: [READ, oss$job_paged_literal] iit$170_display_word :=
          [0, [REP 5 of 0], [REP 5 of 0]],

{ Diagnostic Message Read Only variables.

    connection_broken_msg: [READ, oss$job_paged_literal] string (27) :=
          'Connection Broken Received.',
    immediate_shutdown_msg: [READ, oss$job_paged_literal] string (28) :=
          'Immediate Shutdown Received.',
    shutdown_warning_msg: [READ, oss$job_paged_literal] string (26) :=
          'Shutdown Warning Received.';

?? TITLE := 'PROCEDURE ifp$stop_interactive', EJECT ??

  PROCEDURE [XDCL, #GATE] ifp$stop_interactive;

{  PURPOSE:
{    The purpose of this procedure is to terminate the interactive
{    environment for the task.
{  DESIGN:
{    The connection to the terminal is ended and then a Sign Off is
{    issued to the Memory Link.
{

    VAR
      status: ost$status;

    IF connection_rejected THEN
      RETURN;
    IFEND;

{ Open up the connection to IO at job end.

    pmp$enable_ts_io_in_tasks;

    end_connection (status);

    iiv$interactive_terminated := TRUE;
    iip$sign_off (iiv$jm_application_name, status);

  PROCEND ifp$stop_interactive;

?? TITLE := 'PROCEDURE ifp$job_initialize', EJECT ??

  PROCEDURE [XDCL, #GATE] ifp$job_initialize
    (VAR status: ost$status);

{  PURPOSE:
{    The purpose of this procedure is to complete the connection to the
{    NAM so the task can access the terminal, and to sign on and register
{    the job monitor as a separate mli application.
{  DESIGN:
{    The following is done to complete the connection:
{      - Signon and register a signal handler with the mli.
{      - Send a Connection Accepted (con_req_n) message to the NAM.
{      - Receive an Initialized Connection (fc_init_r) message from the NAM.
{      - Send a Connection Initialized (fc_init_n) message to the NAM.
{

    PROCEDURE handle_condition
      (    cond: pmt$condition;
           cd: ^pmt$condition_information;
           sa: ^ost$stack_frame_save_area;
       VAR ch_status: ost$status);

      ch_status.normal := TRUE;

      osp$set_status_abnormal ('IF', 0, 'condition during initialization',
            status);

      EXIT ifp$job_initialize;
    PROCEND handle_condition;

    TYPE
      align_buffer = record { This is to prevent the buffer from being
            {allocated within 200 bytes of
        buffer: ALIGNED [0 MOD 200] iit$input_data_message,
              { the end of a page.
      recend;

    VAR
      iiv$job_output: [XREF] ^SEQ ( * ),
      iiv$output: [XDCL] ^SEQ ( * ) := NIL,
      job_params: jmt$system_job_parameters,
      eh: pmt$established_handler,
      pism: ^iit$input_supervisory_message,
      upline_data_buffer_p: ^align_buffer,
      queue_entry_descriptor: iit$queue_entry_descriptor,
      posm: ^iit$output_supervisory_message;


    status.normal := TRUE;

{ set up environment from connection request

    jmp$get_job_parameters (job_params, status);
    pism := #LOC (job_params.system_job_parameter);
    IF pism^.message_type <> iic$sm_connection_request THEN
      osp$system_error ('IF - bad connection req', NIL);
    IFEND;

    ALLOCATE iiv$output: [[REP 20000 OF cell]] IN osv$job_pageable_heap^;
    RESET iiv$output;
    iiv$job_output := iiv$output;
    iiv$job_connection := pism^.conreq_connection_number;
    iiv$cdcnet_connection := pism^.conreq_cdcnet_connection;
    iiv$cdcnet_connection := pism^.conreq_cdcnet_connection;

    pmp$establish_condition_handler (iiv$condition_descriptor,
          ^handle_condition, ^eh, status);
    pmp$establish_condition_handler (iiv$condition_descriptor,
          ^handle_condition, ^eh, status);

{ signon and register a handler for the job monitor task (this task).

    init_mli_environ (status);

{ skip connection stuff if this is a suspended job being reconnected.

    IF pism^.conreq_fill6 = iic$initial_job_start THEN

{ Send Connection Accepted (con_req_n) to Pass-On.

      PUSH posm;
      iip$build_super_msg_skeleton (posm, iic$sm_connection_accepted,
            iic$l_connection_accepted);

      posm^.connection_accepted.connection_number := iiv$job_connection;
      IF osv$170_os_type = osc$ot7_dual_state_nos_be THEN
        posm^.connection_accepted.character_type := iic$8_of_12_bit_characters;
      ELSE
        posm^.connection_accepted.character_type := iic$8_bit_characters;
      IFEND;
      posm^.connection_accepted.list_number := iic$min_list_number;

      iip$send_to_pass_on (iiv$jm_application_name, posm,
            (iic$l_connection_accepted + 1) * 8,
            iic$output_supervisory_message + iic$dont_signal, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

{ Receive Initialized Connection (fc_init_r) from Pass-On.
{
{     WHILE NOT connection_started DO
{       pmp$long_term_wait (iic$user_time_delay, iic$user_time_delay);
{     WHILEND;
{
{ Send Connection Initialized (fc_init_n) to Pass-On.
{
{     iip$build_super_msg_skeleton (posm, iic$sm_connection_initialized,
{           iic$l_connection_initialized);
{
{     posm^.connection_initialized.connection_number := iiv$job_connection;
{
{     iip$send_to_pass_on (iiv$jm_application_name, posm,
{           (iic$l_connection_initialized + 1) * 8,
{           iic$output_supervisory_message, status);
{
{ connection is automatically put on a list - take it off until input
{
{     iip$build_super_msg_skeleton (posm, iic$sm_list_off, iic$l_list_off);
{
{     posm^.list_off.connection_number := iiv$job_connection;
{
{     iip$send_to_pass_on (iiv$jm_application_name, posm, (iic$l_list_off + 1)
{           * 8, iic$output_supervisory_message, status);
{
{     iip$build_super_msg_skeleton (posm, iic$sm_list_switch,
{           iic$l_list_switch);
{
{     posm^.list_switch.connection_number := iiv$job_connection;
{     posm^.list_switch.new_list_number := iic$normal_input_list_number;
{
{     iip$send_to_pass_on (iiv$jm_application_name, posm, (iic$l_list_switch +
{           1) * 8, iic$output_supervisory_message, status);
{
{     iip$build_super_msg_skeleton (posm, iic$sm_list_off, iic$l_list_off);
{
{     posm^.list_off.connection_number := iiv$job_connection;
{
{     iip$send_to_pass_on (iiv$jm_application_name, posm, (iic$l_list_off + 1)
{           * 8, iic$output_supervisory_message, status);

    ELSE
      pmp$log (' disconnect- new job begun', status);

{ switch jmtr at passon

      PUSH posm;
      iip$build_super_msg_skeleton (posm, iic$sm_change_job_monitor,
            iic$l_change_job_monitor);
      posm^.changejm_connection_number := iiv$job_connection;
      posm^.changejm_new_jm := iiv$jm_application_name;
      wait_change_jm := TRUE;
      iip$send_to_pass_on (iiv$jm_application_name, posm,
            (iic$l_change_job_monitor + 1) * 8, iic$output_supervisory_message,
            status);
      IF NOT status.normal THEN
        pmp$log (' cannot change jm - reconnect', status);
        RETURN;
      IFEND;

{ wait for change to complete

      WHILE wait_change_jm DO
        pmp$long_term_wait (iic$user_time_delay, iic$user_time_delay);
      WHILEND;
    IFEND;

{ initialize the connection

    iip$initialize_connection (pism);

{ the following call causes a read request to be issued and causes a call
{ to iip$set_default_attributes

    iip$request_default_attributes (iiv$connection_desc_ptr, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Allocate input buffer space for the job's interactive gets.

    ALLOCATE upline_data_buffer_p IN osv$job_pageable_heap^;
    iiv$upline_data_buffer_ptr := ^upline_data_buffer_p^.buffer;


  PROCEND ifp$job_initialize;
?? TITLE := 'PROCEDURE init_mli_environ', EJECT ??

  PROCEDURE init_mli_environ
    (VAR status: ost$status);

{ PURPOSE:
{   This procedure initializes the mli environment for the interactive
{   job's job monitor.


    iip$sign_on (iiv$jm_application_name, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    iip$register_handler (iiv$jm_application_name,
          ^iip$jm_passon_signal_handler, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    iip$add_sender (iiv$jm_application_name, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  PROCEND init_mli_environ;

?? TITLE := 'PROCEDURE ifp$reject_connection', EJECT ??

{ PROCEDURE [XDCL, #GATE] ifp$reject_connection;

{    The purpose of this procedure is to issue a con_req_a instead
{  of a con_req_n at job begin.  This routine is called instead of
{  ifp$job_initialize, but note that ifp$stop_interactive will be
{  called eventually.

{   VAR
{     user_supplied_name: jmt$user_supplied_name,
{     system_supplied_name: jmt$system_supplied_name,
{     status_count: jmt$job_status_count,
{     status_options_p: ^jmt$job_status_options,
{     status_results_p: ^jmt$job_status_results,
{     internal_info: jmt$job_internal_information,
{     signal: pmt$signal,
{     isig: ^iit$reconnect_job,
{     job_params: jmt$system_job_parameters,
{     pism: ^iit$input_supervisory_message,
{     appl: mlt$application_name,
{     status: ost$status,
{     posm: ^iit$output_supervisory_message;

{   connection_rejected := TRUE;
{   pmp$log ('IF - connection being rejected', status);
{   jmp$get_job_parameters (job_params, status);
{   pism := #LOC (job_params.system_job_parameter);
{   IF pism^.message_type <> iic$sm_connection_request THEN
{     osp$system_error ('IF - bad connection req', NIL);
{   IFEND;

{   IF pism^.conreq_fill6 = iic$suspended_job_start THEN

{Reconnect the suspended job that created this job by ascertaining
{  the creator job's jsn, and, subsequently, his gtid.....

{     pmp$get_job_names (user_supplied_name, system_supplied_name, status);
{     IF NOT status.normal THEN
{       iip$report_status_error (status, ' get_job_names ');
{       RETURN;
{     IFEND;

{     PUSH status_options_p: [1 .. 2];
{     status_options_p^[1].key := jmc$job_name_list;
{     PUSH status_options_p^[1].job_name_list: [1 .. 1];
{     status_options_p^[1].job_name_list^[1].kind := jmc$system_supplied_name;
{     status_options_p^[1].job_name_list^[1].system_supplied_name :=
{     system_supplied_name;
{     status_options_p^[2].key := jmc$job_state_set;
{     status_options_p^[2].job_state_set := $jmt$job_state_set
{     [jmc$initiated_job];

{     PUSH status_results_p: [1 .. 1];
{     PUSH status_results_p^[1]: [1 .. 1];
{     status_results_p^[1]^[1].key := jmc$job_originator_ssn;

{     jmp$get_job_status (status_options_p, status_results_p, status_count,
{     status);
{     IF NOT status.normal THEN
{       iip$report_status_error (status, ' get_job_status');
{       RETURN;
{     IFEND;

{     jmp$get_job_internal_info (status_results_p^[1]^[1].job_originator_ssn,
{           internal_info, status);
{     IF NOT status.normal THEN
{       iip$report_status_error (status, ' get_job_internal_info');
{       RETURN;
{     IFEND;

{.....so that a signal can be built and sent to reconnect the creator job.

{     signal.identifier := ifc$signal_id;
{     isig := #LOC (signal.contents);
{     isig^.sig := iic$reconnect_job;
{     isig^.acn := pism^.conreq_connection_number;
{     isig^.reject_caused_reconnect := TRUE;
{     pmp$send_signal (internal_info.jmtr_global_taskid, signal, status);
{     IF NOT status.normal THEN
{       iip$report_status_error (status, ' send_signal');
{       RETURN;
{     IFEND;

{   ELSE
{     iip$sign_on (appl, status);
{     IF NOT status.normal THEN
{       osp$system_error ('IF - reject signon', ^status);
{     IFEND;

{     PUSH posm;
{     iip$build_super_msg_skeleton (posm, iic$sm_connection_rejected,
{           iic$l_connection_rejected);
{     posm^.connection_rejected.connection_number := pism^.
{           conreq_connection_number;
{     posm^.connection_rejected.reason := iic$unspecified_reject;
{     iip$send_to_pass_on (appl, posm, (iic$l_connection_rejected + 1) * 8,
{           iic$output_supervisory_message, status);
{     IF NOT status.normal THEN
{       osp$system_error ('IF - send reject', ^status);
{     IFEND;

{     iip$sign_off (appl, status);
{   IFEND;

{ PROCEND ifp$reject_connection;

?? TITLE := 'PROCEDURE handle_upline_supervisory', EJECT ??

  PROCEDURE handle_upline_supervisory
    (    pism: ^iit$input_supervisory_message;
     VAR status: ost$status);

{  PURPOSE:
{    The purpose of this procedure is to handle an upline supervisory
{    message that has been received by the "user" task from the NAM.
{  DESIGN:
{    Upline Supervisory messages are handled as follows:
{    Read rejected
{      pause and continue
{
{    All Others
{      Report on the unexpected message.
{      Return an abnormal status in order to cause the task to terminate.
{


{ Pause and retry if read request was rejected by passon.

    IF pism^.message_type = iic$sm_read_rejected THEN
      pmp$long_term_wait (iic$user_time_delay, iic$user_time_delay);
    ELSE
      report_unexpected_message (pism);
      status.normal := FALSE;
    IFEND;

  PROCEND handle_upline_supervisory;

?? TITLE := 'PROCEDURE end_connection', EJECT ??

  PROCEDURE end_connection
    (VAR status: ost$status);

{  PURPOSE:
{    The purpose of this procedure is to terminate the interactive
{    job's connection to the terminal.
{  DESIGN:
{    The following is done to terminate the connection to the terminal:
{      - Send an End Connection (con_end_r) to the NAM.
{      - Receive a Connection Ended (con_end_n) from the NAM.
{

    VAR
      appl: mlt$application_name,
      i: integer,
      posm: ^iit$output_supervisory_message;


{ Send End Connection (con_end_r) to Pass-On.

    IF connection_ended THEN
      status.normal := TRUE;
      RETURN;
    IFEND;

    iip$sign_on (appl, status);
    IF NOT status.normal THEN
      log_message ('IF - CON/END/R application failed to signon');
      RETURN;
    IFEND;
    iip$add_sender (appl, status);
    IF NOT status.normal THEN
      log_message ('IF - CON/END/R application failed to add_sender');
      RETURN;
    IFEND;

    PUSH posm;
    iip$build_super_msg_skeleton (posm, iic$sm_end_connection,
          iic$l_end_connection);
    posm^.conend_zero1 := 0;
    posm^.conend_connection_number := iiv$job_connection;
    posm^.conend_zero2 := conend_zero2_value;
    posm^.conend_fill1 := 0;
    connection_ended := FALSE;
    iip$send_to_pass_on (appl, posm, (iic$l_end_connection + 1) * 8,
          iic$output_supervisory_message, status);
    IF NOT status.normal THEN
      log_message ('IF - CON/END/R failed to be sent to PASSON');
      RETURN;
    IFEND;
    iip$sign_off (appl, status);
    IF NOT status.normal THEN
      log_message ('IF - CON/END/R application failed to signoff');
      RETURN;
    IFEND;

{ Wait for a Connection Ended (con_end_n) from Pass-On.

{Due to an absurd number of problems with this code, only wait (about) 10
{seconds
{for the connection to end - then continue as if it had

    FOR i := 1 TO 10 DO
      IF NOT connection_ended THEN
        pmp$long_term_wait (iic$user_time_delay, iic$user_time_delay);
      IFEND;
    FOREND;
    IF NOT connection_ended THEN
      connection_ended := TRUE;
      log_message ('End connection response never received');
    IFEND;

  PROCEND end_connection;


?? TITLE := 'PROCEDURE report_unexpected_message', EJECT ??

  PROCEDURE report_unexpected_message
    (    p: ^cell);

{  PURPOSE:
{    The purpose of this procedure is to report unexpected upline
{    supervisory and upline data messages that are received from the
{    Pass-On.
{

    VAR
      pism: ^iit$input_supervisory_message,
      pidm: ^iit$input_data_message;

    pism := p;
    pidm := p;

    IF pism^.header.block_type = iic$supervisory_block THEN
      iip$report_unhandled_super_msg (pism^);
      IF pism^.message_type = iic$sm_logical_error THEN
        iip$report_logical_error (pism^);
      IFEND;
    ELSE
      iip$report_unhandled_data_msg (pidm^);
    IFEND;

  PROCEND report_unexpected_message;

?? TITLE := 'PROCEDURE log_message', EJECT ??

  PROCEDURE log_message
    (    message: string ( * ));

{  PURPOSE:
{    The purpose of this procedure is to put a message into the job's
{    log so that it will be printed when the interactive job terminates.
{

    VAR
      status: ost$status;

    pmp$log (message, status);

  PROCEND log_message;

?? TITLE := 'PROCEDURE iip$jm_passon_signal_handler', EJECT ??

  PROCEDURE iip$jm_passon_signal_handler
    (    signalee: mlt$application_name;
         signaler: mlt$signaler_application_info;
         signal: mlt$signal;
     VAR status: ost$status);

    PROCEDURE respond_to_user_interrupt;

{ Build and send a INTR/RSP/R to PASSON.  This signals to the network that the
{ current interrupt has been acknowledged and another interrupt can be
{ received.

      iip$build_super_msg_skeleton (#LOC (osm), iic$sm_interrupt_response,
            iic$l_interrupt_response);
      osm.interrupt_response.alpha := $CHAR (0);
      osm.interrupt_response.connection_number := iiv$job_connection;
      osm.interrupt_response.fill1 := 0;
      iip$send_to_pass_on (iiv$jm_application_name, #LOC (osm),
            (iic$l_interrupt_response + 1) * 8, iic$output_supervisory_message,
            status);
      IF NOT status.normal THEN
        iip$report_status_error (status, 'send to passon');
      IFEND;
    PROCEND respond_to_user_interrupt;

    VAR
      display_option_selection: lgt$display_option_selection,
      flush_fid: amt$file_identifier,
      job_name: clt$data_value,
      job_name_p: ^jmt$job_attribute_results,
      ml: mlt$message_length,
      long_wait: boolean,
      old_task_break_level: integer,
      osm: iit$output_supervisory_message,
      message: string (2),
      ism: iit$input_supervisory_message;

    CASE signal^.direction OF
    = mlc$receive =
    = mlc$send =

{ Get message from passon.

      iip$receive_from_pass_on (iiv$jm_application_name, #LOC (ism),
            #SIZE (ism), ml, status);
      IF NOT status.normal THEN
        iip$report_status_error (status, 'jm sh mli');
        RETURN;
      IFEND;
      IF ism.header.block_type <> iic$supervisory_block THEN
        report_unexpected_message (^ism);
        RETURN;
      IFEND;
      status.normal := TRUE;

{ If the task is terminating only process a con_end_n message.

      IF pmp$task_state () > pmc$task_active THEN
        IF ism.message_type <> iic$sm_connection_ended THEN
          IF ism.message_type = iic$sm_interrupt_user THEN

{ Send a user-interrupt response to the network but do not
{ process the interrupt.

            log_message ('User interrupt ignored');
            respond_to_user_interrupt;
          IFEND;
          EXIT iip$jm_passon_signal_handler;
        IFEND;
      IFEND;

{ Handle Connection Broken (con_cb_r).

      IF ism.message_type = iic$sm_connection_broken THEN
        IF connection_started THEN
          log_message (connection_broken_msg);
          iip$disconnect_job (iic$end_connection, iic$dont_start_new_job,
                status);
        ELSE
          pmp$log (' Connection-Broken received before job initialized',
                status);
          iiv$abort_job_initialization := TRUE;
        IFEND;

{ Handle Shutdown (shut_insd_r).

      ELSEIF ism.message_type = iic$sm_shutdown THEN
        IF ism.shutdown.immediate THEN
          log_message (immediate_shutdown_msg);
        ELSE
          log_message (shutdown_warning_msg);
        IFEND;

{ Handle Terminal Break (fc_brk_r) .

      ELSEIF ism.message_type = iic$sm_break THEN

{ unsupported break - log and continue

        report_unexpected_message (^ism);

{ Send Reset Connection (fc_rst_r) to Pass-On.

        iip$build_super_msg_skeleton (#LOC (osm), iic$sm_reset_connection,
              iic$l_reset_connection);
        osm.reset_connection.connection_number := iiv$job_connection;

        iip$send_to_pass_on (iiv$jm_application_name, #LOC (osm),
              (iic$l_reset_connection + 1) * 8, iic$output_supervisory_message,
              status);
        RETURN;

{ Handle start output (fc_strt_r)

      ELSEIF ism.message_type = iic$sm_start_output THEN

{ Send Reset Connection (fc_rst_r) to Pass-On.

        iip$build_super_msg_skeleton (#LOC (osm), iic$sm_reset_connection,
              iic$l_reset_connection);
        osm.reset_connection.connection_number := iiv$job_connection;

        iip$send_to_pass_on (iiv$jm_application_name, #LOC (osm),
              (iic$l_reset_connection + 1) * 8, iic$output_supervisory_message,
              status);

{ Handle Connection Ended (con_end_n).

      ELSEIF ism.message_type = iic$sm_connection_ended THEN
        connection_ended := TRUE;

{ Handle initialize connection (fc_init_r).

      ELSEIF ism.message_type = iic$sm_initialized_connection THEN
        connection_started := TRUE;

{ Handle inactive connection.

      ELSEIF (ism.message_type = iic$sm_inactive_connection) OR
            (ism.message_type = iic$sm_term_char_changed) THEN

      ELSEIF ism.message_type = iic$sm_interrupt_user THEN

        CASE ism.interrupt_user.alpha OF
        = $CHAR (3) =
          log_message ('Pause break received');
          iiv$break_abn := 0;
          iiv$break_reason := iic$user_break_1;
          raise_condition (iic$pause_break);
        = $CHAR (4) =
          log_message ('Terminate break received');
          iiv$break_abn := 0;
          iiv$break_reason := iic$user_break_2;
          raise_condition (iic$terminate_break);
        ELSE
          IF NOT iiv$job_suspended THEN

            message (1) := $CHAR (10);
            message (2) := $CHAR (13);
            IF NOT jmv$terminal_io_disabled THEN
              CASE ism.interrupt_user.alpha OF
              = 'L', 'l' =
                display_option_selection.display_options := lgc$count;
                display_option_selection.count := 10;
                lgp$display_log (clc$display_job_log, display_option_selection,
                      ':$LOCAL.OUTPUT.1', status);
                clp$put_job_output (message, status);
              = 'A', 'a' =
                pmp$display_active_tasks (':$LOCAL.OUTPUT.1', status);
                clp$put_job_output (message, status);
              = 'J', 'j' =
                job_name.kind := clc$keyword;
                job_name.keyword_value := 'ALL';
                jmp$display_job_status (':$LOCAL.OUTPUT.1',
                      $jmt$attribute_keys_set [jmc$cpu_time_used,
                      jmc$display_message, jmc$job_state, jmc$page_faults,
                      jmc$system_job_name], job_name, status);
                clp$put_job_output (message, status);
              = 'T', 't' =
                iip$discard_typed_ahead_input (FALSE);
              = 'D', 'd' =
                iip$disconnect_job (iic$dont_end_connection, iic$start_new_job,
                      status);
              = 'X', 'x' =
                ;
              ELSE
                job_name.kind := clc$name;
                PUSH job_name_p: [1 .. 1];
                job_name_p^ [1].key := jmc$system_job_name;
                jmp$get_job_attributes (job_name_p, status);
                job_name.name_value := job_name_p^ [1].system_job_name;
                jmp$display_job_status (':$LOCAL.OUTPUT.1',
                      $jmt$attribute_keys_set [jmc$cpu_time_used,
                      jmc$display_message, jmc$page_faults], job_name, status);
                clp$put_job_output (message, status);
              CASEND;
            IFEND;

            CASE ism.interrupt_user.alpha OF
            = 'X', 'x' =
              osp$set_status_abnormal ('JM', jme$user_requested_exit, '',
                    status);
              pmp$exit (status);
            ELSE
            CASEND;

{ send intr/resp here for all other user-interrupts

            respond_to_user_interrupt;

{ Flush output with wait

            clp$get_system_file_id (clc$job_output, flush_fid, status);
            IF NOT status.normal THEN
            RETURN;
            IFEND;
            ifp$immediate_attribute_flush (flush_fid,status);
            IF NOT status.normal THEN
            RETURN;
            IFEND;

{ restore task break level

          ELSE {job_suspended}
            respond_to_user_interrupt;
          IFEND; {job_suspended}
        CASEND;

      ELSEIF ism.message_type = iic$sm_absentee_begun THEN
      ELSEIF ism.message_type = iic$sm_job_monitor_changed THEN
        wait_change_jm := FALSE;
      ELSEIF ism.message_type = iic$sm_hold_acknowlege THEN
        wait_hold_ack := FALSE;
      ELSE

        report_unexpected_message (^ism);
        status.normal := FALSE;
      IFEND;
    ELSE
    CASEND;

  PROCEND iip$jm_passon_signal_handler;
?? TITLE := 'PROCEDURE raise_condition', EJECT ??

  PROCEDURE raise_condition
    (    condition: iit$interactive_signal);

    VAR
      psig: ^iit$interactive_signal,
      tid: pmt$task_id,
      local_signal: pmt$signal,
      status: ost$status;


{ signal task that will handle break

{ disable IO in the job.

    pmp$disable_ts_io_in_tasks;

    psig := #LOC (local_signal.contents);
    local_signal.identifier := ifc$signal_id;
    psig^ := condition;
    clp$find_current_job_synch_task (tid, status);
    IF NOT status.normal THEN
      iip$report_status_error (status, ' find current synch task');
    IFEND;
    pmp$get_global_task_id (tid, iiv$task_handling_break, status);
    IF NOT status.normal THEN
      iip$report_status_error (status, ' get global tid');
    IFEND;
    pmp$send_signal (iiv$task_handling_break, local_signal, status);
    IF NOT status.normal THEN
      iip$report_status_error (status, ' send break signal');

{ try a second time before giving up

      clp$find_current_job_synch_task (tid, status);
      IF NOT status.normal THEN
        iip$report_status_error (status, ' find current synch task');
      IFEND;
      pmp$get_global_task_id (tid, iiv$task_handling_break, status);
      IF NOT status.normal THEN
        iip$report_status_error (status, ' get global tid');
      IFEND;
      pmp$send_signal (iiv$task_handling_break, local_signal, status);
    IFEND;
    IF NOT status.normal THEN
      iip$report_status_error (status, 'send break signal');
      iiv$task_handling_break := iiv$job_monitor_task_id;
      pmp$log ('IF couldnt find break task', status);
      pmp$send_signal (iiv$task_handling_break, local_signal, status);
    IFEND;
  PROCEND raise_condition;
?? TITLE := 'PROCEDURE iip$disconnect_job', EJECT ??

  PROCEDURE [XDCL, #GATE] iip$disconnect_job
    (    end_job_connection: boolean;
         start_new_job: boolean;
     VAR status: ost$status);

    log_message ('Job being disconnected');
    iiv$end_job_connection := end_job_connection;
    iiv$start_new_job := start_new_job;
    raise_condition (iic$terminal_disconnect);
    status.normal := TRUE;

  PROCEND iip$disconnect_job;

?? TITLE := 'PROCEDURE iip$complete_disconnect', EJECT ??

  PROCEDURE [XDCL] iip$complete_disconnect;

    VAR
      job_parameters: jmt$system_job_parameters,
      pism: ^iit$input_supervisory_message,
      posm: ^iit$output_supervisory_message,
      user_id: ost$user_identification,
      user_supplied_job_name: jmt$user_supplied_name,
      system_supplied_job_name: jmt$system_supplied_name,
      i: integer,
      appl: mlt$application_name,
      status,
      lst: ost$status;

    status.normal := TRUE;
    IF iiv$job_suspended THEN
      pmp$log (' disconnect in suspended job', status);
      RETURN;
    IFEND;

    iiv$job_suspended := TRUE;

    IF (NOT iiv$end_job_connection) AND (NOT iiv$start_new_job) THEN
      jmp$set_job_mode (jmc$interactive_sys_disconnect, status);
    IFEND;

    IF iiv$end_job_connection THEN
      pmp$log (' job suspended due to terminal disconnect', status);
      end_connection (status);
      jmp$set_job_mode (jmc$interactive_line_disconnect, status);
    IFEND;

    IF iiv$start_new_job THEN
      jmp$set_job_mode (jmc$interactive_cmnd_disconnect, status);

      iip$sign_on (appl, status);
      IF NOT status.normal THEN
      IFEND;
      iip$add_sender (appl, status);
      IF NOT status.normal THEN
        iip$sign_off (appl, status);
      IFEND;

{ idle the connection at passon, then start new job (which will skip the
{ connection sequence).

      PUSH pism;
      PUSH posm;
      iip$build_super_msg_skeleton (#LOC (posm^), iic$sm_hold, iic$l_hold);
      posm^.hold.connection_number := iiv$job_connection;
      wait_hold_ack := TRUE;
      iip$send_to_pass_on (appl, posm, (iic$l_hold + 1) * 8,
            iic$output_supervisory_message, status);
      iip$sign_off (appl, status);
      WHILE wait_hold_ack DO
        pmp$long_term_wait (iic$user_time_delay, iic$user_time_delay);
      WHILEND;

      jmp$get_job_parameters (job_parameters, status);
      pism := #LOC (job_parameters.system_job_parameter);
      IF pism^.message_type <> iic$sm_connection_request THEN
        osp$system_error ('IF - bad connection req', NIL);
      IFEND;


      pism^.conreq_fill6 := iic$suspended_job_start;
      pism^.conreq_connection_number := iiv$job_connection;

      pmp$get_user_identification (user_id, status);
      pmp$get_job_names (user_supplied_job_name, system_supplied_job_name,
            status);

      iip$route (user_id, user_supplied_job_name, job_parameters, status);
      IF NOT status.normal THEN
        iip$report_status_error (status, 'route suspended');
        iip$reconnect_job (iiv$job_connection, TRUE);
        RETURN;
      IFEND;
      pmp$log (' new (suspended) job routed', status);
    IFEND;
    connection_ended := TRUE;

  PROCEND iip$complete_disconnect;
?? TITLE := 'PROCEDURE iip$end_dr_job', EJECT ??

  PROCEDURE [XDCL] iip$end_dr_job
    (    status: ost$status);

{  PURPOSE:
{    To end an interactive job without ending the connection or generating
{    any more output.


{ cause all output/input generated from now on to be thrown away

    iiv$interactive_terminated := TRUE;
    iiv$job_suspended := FALSE;
    connection_ended := TRUE;

    pmp$exit (status);

  PROCEND iip$end_dr_job;
?? TITLE := 'PROCEDURE iip$terminate_disconnected_job', EJECT ??

  PROCEDURE [XDCL, #GATE] iip$terminate_disconnected_job;

{  PURPOSE:
{    To terminate an interactive job that has been suspended.  Consequently,
{    any output to the disconnected job will be discarded.

    VAR
      status: ost$status,
      posm: ^iit$output_supervisory_message;

    iiv$interactive_terminated := TRUE;

  /loop/
    BEGIN
      IF iiv$job_suspended THEN
        iiv$job_suspended := FALSE;
      ELSEIF iiv$end_job_connection THEN
        PUSH posm;
        iip$build_super_msg_skeleton (#LOC (posm^), iic$sm_terminate,
              iic$l_terminate);
        posm^.terminate.connection_number := iiv$job_connection;
        REPEAT
          mlp$send_message (iiv$jm_application_name,
                iic$output_supervisory_message, NIL, posm,
                (iic$l_terminate + 1) * 8, iic$passon_application_name,
                status);
          IF NOT status.normal THEN
            IF (status.condition = mlc$receiver_not_signed_on) OR
                  (status.condition = mlc$sender_not_signed_on) THEN
              EXIT /loop/;
            ELSEIF status.condition = mlc$prior_msg_not_received THEN
              pmp$wait (1000, 1000);
              mlp$force_send_message (iiv$jm_application_name,
                    iic$output_supervisory_message, NIL, posm,
                    (iic$l_terminate + 1) * 8, iic$passon_application_name,
                    status);
              IF NOT status.normal THEN
                IF (status.condition = mlc$receiver_not_signed_on) OR
                      (status.condition = mlc$sender_not_signed_on) THEN
                  EXIT /loop/;
                ELSE
                  pmp$wait (1000, 1000);
                IFEND;
              IFEND;
            ELSE
              pmp$wait (1000, 1000);
            IFEND;
          IFEND;
        UNTIL status.normal;
      IFEND;
    END /loop/;

{ Open up the connection to allow all tasks to do IO.

    pmp$enable_ts_io_in_tasks;

  PROCEND iip$terminate_disconnected_job;
?? TITLE := 'PROCEDURE iip$reconnect_job', EJECT ??

  PROCEDURE [XDCL] iip$reconnect_job
    (    acn: iit$application_connection_num;
         reject_caused_reconnect: boolean);

    log_message ('Job being reconnected');
    iiv$job_connection := acn;
    connection_ended := FALSE;
    iiv$connection_desc_ptr^.connection_number := acn;
    iiv$reject_caused_reconnect := reject_caused_reconnect;
    raise_condition (iic$terminal_reconnect);

  PROCEND iip$reconnect_job;
?? TITLE := 'PROCEDURE iip$complete_reconnect', EJECT ??

  PROCEDURE [XDCL, #GATE] iip$complete_reconnect;

    VAR
      ost,
      status: ost$status,
      posm: ^iit$output_supervisory_message,
      signal: pmt$signal,
      i: integer,
      appl: mlt$application_name,
      isig: ^iit$interactive_signal;

{ switch jmtr at passon

    iip$sign_on (appl, status);
    IF NOT status.normal THEN
    IFEND;
    iip$add_sender (appl, status);
    IF NOT status.normal THEN
      iip$sign_off (appl, status);
    IFEND;
    PUSH posm;
    iip$build_super_msg_skeleton (posm, iic$sm_change_job_monitor,
          iic$l_change_job_monitor);
    posm^.changejm_connection_number := iiv$job_connection;
    posm^.changejm_new_jm := iiv$jm_application_name;
    wait_change_jm := TRUE;
    iip$send_to_pass_on (appl, posm, (iic$l_change_job_monitor + 1) * 8,
          iic$output_supervisory_message, status);
    iip$sign_off (appl, ost);
    IF NOT status.normal THEN
      pmp$log (' cannot change jm - reconnect', status);
      RETURN;
    IFEND;

{ wait for change to complete

    WHILE wait_change_jm DO
      pmp$long_term_wait (iic$user_time_delay, iic$user_time_delay);
    WHILEND;

{ this job now owns the terminal

    jmp$set_job_mode (jmc$interactive_connected, status);

    IF NOT iiv$reject_caused_reconnect THEN

{ force current terminal attributes to be reset (except for terminal class

      iip$set_lock (iiv$connection_desc_ptr^.lock, osc$wait, status);
      FOR i := iic$key_user_break_1 TO iic$key_trans_mode_delim_char DO
        IF iiv$connection_desc_ptr^.term_char_values [i] < 0ff(16) THEN
          iiv$connection_desc_ptr^.term_char_values [i] :=
                iiv$connection_desc_ptr^.term_char_values [i] + 1;
        ELSE
          iiv$connection_desc_ptr^.term_char_values [i] := 1;
        IFEND;
        IF iiv$connection_desc_ptr^.active_term_char_values [i] < 0ff(16) THEN
          iiv$connection_desc_ptr^.active_term_char_values [i] :=
                iiv$connection_desc_ptr^.active_term_char_values [i] + 1;
        ELSE
          iiv$connection_desc_ptr^.active_term_char_values [i] := 1;
        IFEND;
      FOREND;
      iip$clear_lock (iiv$connection_desc_ptr^.lock, status);
    IFEND;

    iiv$job_suspended := FALSE;
    IF iiv$reject_caused_reconnect THEN
      iiv$reject_caused_reconnect := FALSE;
      clp$put_job_output (
            ' New job connection aborted--original job reconnecting', status);
    IFEND;


  PROCEND iip$complete_reconnect;
?? TITLE := 'PROCEDURE iip$timeout_suspended_job', EJECT ??

  PROCEDURE [XDCL, #GATE] iip$timeout_suspended_job;

    VAR
      status: ost$status;

    pmp$log ('IF: disconnected job timeout and exit', status);
    osp$set_status_abnormal (ifc$interactive_facility_id,
          ife$disconnected_job_timeout, '', status);
    iip$end_dr_job (status);
  PROCEND iip$timeout_suspended_job;
?? TITLE := 'PROCEDURE iip$process_reconnect_request', EJECT ??

  PROCEDURE [XDCL] iip$process_reconnect_request
    (    gtid: ost$global_task_id);

    VAR
      prs: ^iit$reconnect_job,
      signal: pmt$signal,
      posm: ^iit$output_supervisory_message,
      status: ost$status;

    PUSH posm;
    iip$build_super_msg_skeleton (#LOC (posm^), iic$sm_hold, iic$l_hold);
    posm^.hold.connection_number := iiv$job_connection;
    wait_hold_ack := TRUE;
    iip$send_to_pass_on (iiv$jm_application_name, posm, (iic$l_hold + 1) * 8,
          iic$output_supervisory_message, status);
    WHILE wait_hold_ack DO
      pmp$long_term_wait (iic$user_time_delay, iic$user_time_delay);
    WHILEND;
    signal.identifier := ifc$signal_id;
    prs := #LOC (signal.contents);
    prs^.acn := iiv$job_connection;
    prs^.sig := iic$reconnect_job;
    prs^.reject_caused_reconnect := FALSE;
    pmp$send_signal (gtid, signal, status);
    IF NOT status.normal THEN
      iip$report_status_error (status, 'send reconnect signal');
    ELSE
      osp$set_status_abnormal (ifc$interactive_facility_id,
            ife$terminal_reconnected_other, '', status);
      iip$end_dr_job (status);
    IFEND;
  PROCEND iip$process_reconnect_request;

MODEND ifm$job_control;
