?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE:  Job Management - general purpose cluster interfaces' ??
MODULE jmm$general_purpose_cluster_rpc;

{ PURPOSE:
{   This module contains routines that are used for processing general purpose
{ remote procedure calls.

{ NOTES:
{   To add an additional target procedure for this request do the following:
{   o  Change the type jmt$general_purpose_rpc_ordinal to define an ordinal for
{      the new procedure.
{   o  Create an XREF deck for the new procedure as follows:
{         PROCEDURE [XREF] jmp$new_procedure
{           (    target_options_p: ^SEQ ( * );
{            VAR data_area_p: (input, output) ^SEQ ( * );
{            VAR number_of_data_packets: ost$non_negative_integers;
{            VAR status: ost$status)
{   o  Place a *copyc of the above mentioned XREF deck in this module.
{   o  Change the variable v$gpcr_procedures to include a pointer to the new procedure
{      at the newly defined ordinal location.
{   o  Make the controlling function call the procedure jmp$general_purpose_cluster_rpc and
{      complete the code in the new procedure according to the purpose of the function.
{   o  Test the request first using one mainframe.  Once verified, test the request using
{      two mainframes in a cluster.  It should work without any changes.  There is no
{      need to test with more than two mainframes (i.e., indirectly coupled mainframes).

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dft$rpc_parameters
*copyc jmc$job_management_id
*copyc jme$work_area_too_small
*copyc jmt$general_purpose_rpc_ordinal
*copyc jmt$rpc_mainframes_processed
*copyc osd$integer_limits
*copyc osd$virtual_address
*copyc oss$job_paged_literal
*copyc ost$status
?? POP ??
*copyc dfp$get_partner_mainframes
*copyc dfp$receive_server_rpc_segment
*copyc dfp$reserve_server_rpc_segment
*copyc dfp$send_remote_procedure_call
*copyc i#current_sequence_position
*copyc jmp$get_leveling_data
*copyc jmp$mainframe_change_input_attr
*copyc jmp$mainframe_change_output_att
*copyc jmp$mainframe_change_qfile_attr
*copyc jmp$mainframe_get_input_attribu
*copyc jmp$mainframe_get_job_status
*copyc jmp$mainframe_get_output_attrib
*copyc jmp$mainframe_get_output_status
*copyc jmp$mainframe_get_qfile_attrib
*copyc jmp$mainframe_get_qfile_status
*copyc jmp$mainframe_set_sense_switch
*copyc jmp$mainframe_terminate_output
*copyc jmp$mainframe_terminate_qfile
*copyc osp$generate_log_message
*copyc osp$set_status_condition
*copyc osp$set_status_from_condition
*copyc pmp$convert_binary_mainframe_id
*copyc pmp$convert_mainframe_to_binary
*copyc pmp$get_mainframe_id
*copyc pmp$get_pseudo_mainframe_id
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by this Module', EJECT ??

  TYPE
    t$gpcr_procedure_p = ^procedure (    target_options_p: ^SEQ ( * );
                                     VAR data_area_p: ^SEQ ( * );
                                     VAR number_of_data_packets: ost$non_negative_integers;
                                     VAR status: ost$status);

  VAR
    v$gpcr_procedures: [STATIC, READ, oss$job_paged_literal] array [jmt$general_purpose_rpc_ordinal] of
          t$gpcr_procedure_p := [
?? FMT (FORMAT := OFF) ??
{ jmc$gpro_get_job_status         } ^jmp$mainframe_get_job_status,
{ jmc$gpro_get_output_status      } ^jmp$mainframe_get_output_status,
{ jmc$gpro_get_output_attributes  } ^jmp$mainframe_get_output_attrib,
{ jmc$gpro_get_input_attributes   } ^jmp$mainframe_get_input_attribu,
{ jmc$gpro_change_output_attribut } ^jmp$mainframe_change_output_att,
{ jmc$gpro_change_input_attribute } ^jmp$mainframe_change_input_attr,
{ jmc$gpro_terminate_output       } ^jmp$mainframe_terminate_output,
{ jmc$gpro_set_sense_switches     } ^jmp$mainframe_set_sense_switch,
{ jmc$gpro_get_leveling_data      } ^jmp$get_leveling_data,
{ jmc$gpro_get_qfile_status       } ^jmp$mainframe_get_qfile_status,
{ jmc$gpro_get_qfile_attributes   } ^jmp$mainframe_get_qfile_attrib,
{ jmc$gpro_change_qfile_attribut  } ^jmp$mainframe_change_qfile_attr,
{ jmc$gpro_terminate_qfile        } ^jmp$mainframe_terminate_qfile,
{ jmc$gpro_unused_ordinal         } NIL
?? FMT (FORMAT := ON) ??
    ];

?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$general_purpose_cluster_rpc', EJECT ??
*copy jmh$general_purpose_cluster_rpc

  PROCEDURE [XDCL] jmp$general_purpose_cluster_rpc
    (    target_mainframe_id: pmt$mainframe_id;
         procedure_ordinal: jmt$general_purpose_rpc_ordinal;
         data_packet_size: ost$segment_length;
         mainframes_processed_so_far: jmt$rpc_mainframes_processed;
         target_options_p: ^SEQ ( * );
     VAR data_area_p: {input, output} ^SEQ ( * );
     VAR target_mainframe_reached: boolean;
     VAR mainframes_processed: jmt$rpc_mainframes_processed;
     VAR number_of_data_packets: ost$non_negative_integers;
     VAR status: ost$status);

    VAR
      binary_target_mainframe_id: pmt$binary_mainframe_id,
      current_mainframe_id: pmt$mainframe_id,
      local_status: ost$status,
      mainframe_id: pmt$mainframe_id,
      mainframes_searched_index: jmt$maximum_mainframes,
      server_mainframe_count: dft$partner_mainframe_count,
      server_mainframe_index: dft$partner_mainframe_count,
      server_mainframe_list: array [1 .. dfc$maximum_partner_mainframes] of dft$partner_mainframe_entry;

?? NEWTITLE := 'call_server_general_purpose_rpc', EJECT ??

{ NOTES:
{   This request will not return abnormal status if there is a remote procedure call failure.
{ If a problem occurs during communication with the server, this request treats it as though
{ the server could not be reached (from the current client).
{
{   This request will return the abnormal status JME$WORK_AREA_TOO_SMALL if the data area passed
{ in is not large enough to hold the data.  The number of data-packets returned by this request
{ still indicates the number of data elements that were available.
{
{ ASSUMPTION:
{   It is assumed that no more than a segment's worth of data can be returned by this request,
{ collectively by all servers in the cluster.  In short, it is not expected that the error
{ jme$work_area_too_small will be returned by this request except on the originating mainframe.

    PROCEDURE call_server_general_purpose_rpc
      (    mainframe_to_call: pmt$mainframe_id;
           target_mainframe_id: pmt$mainframe_id;
           procedure_ordinal: jmt$general_purpose_rpc_ordinal;
           data_packet_size: ost$segment_length;
           mainframes_processed_so_far: jmt$rpc_mainframes_processed;
           target_options_p: ^SEQ ( * );
       VAR number_of_data_packets {input, output} : ost$non_negative_integers;
       VAR data_area_p {input, output} : ^SEQ ( * );
       VAR target_mainframe_reached: boolean;
       VAR mainframes_processed: jmt$rpc_mainframes_processed;
       VAR status: ost$status);

?? NEWTITLE := 'dfp$remote_procedure_call_ch', EJECT ??

{ PURPOSE:
{   This procedure is a condition handler established to call a routine to clear the assignment of a task
{   services queue_entry if a task aborts with a queue_entry assigned to it.  The queue_entry must be clear
{   before the task can safely exit.

      PROCEDURE dfp$remote_procedure_call_ch
        (    condition: pmt$condition;
             cond_desc: ^pmt$condition_information;
             save: ^ost$stack_frame_save_area;
         VAR handler_status: ost$status);

        dfp$ch_cleanup;
        osp$set_status_from_condition (dfc$file_server_id, condition, save, status, handler_status);
        EXIT call_server_general_purpose_rpc;

      PROCEND dfp$remote_procedure_call_ch;
*block
*copyc dfp$begin_ch_remote_proc_call
*copyc dfp$end_ch_remote_proc_call
*blockend
?? OLDTITLE, EJECT ??
      VAR
        data_p: ^SEQ ( * ),
        data_packet_size_p: ^ost$segment_length,
        data_size: dft$send_data_size,
        ignore_status: ost$status,
        local_status: ost$status,
        mainframes_processed_p: ^jmt$rpc_mainframes_processed,
        number_of_data_packets_p: ^ost$non_negative_integers,
        number_of_packets: ost$non_negative_integers,
        options_p: ^SEQ ( * ),
        options_size_p: ^ost$non_negative_integers,
        queue_entry_location: dft$rpc_queue_entry_location,
        parameter_size: dft$send_parameter_size,
        procedure_ordinal_p: ^jmt$general_purpose_rpc_ordinal,
        received_from_server_data_p: dft$p_receive_data,
        received_from_server_params_p: dft$p_receive_parameters,
        send_to_server_data_p: dft$p_send_data,
        send_to_server_params_p: dft$p_send_parameters,
        server_location: dft$server_location,
        target_mainframe_id_p: ^pmt$mainframe_id,
        target_mainframe_reached_p: ^boolean;


      status.normal := TRUE;
      local_status.normal := TRUE;

      target_mainframe_reached := FALSE;
      mainframes_processed := mainframes_processed_so_far;
      server_location.server_location_selector := dfc$mainframe_id;
      server_location.server_mainframe := mainframe_to_call;

      dfp$begin_ch_remote_proc_call (server_location, {allowed_when_server_deactivated} FALSE,
            queue_entry_location, send_to_server_params_p, send_to_server_data_p, local_status);
      IF NOT local_status.normal THEN
        RETURN;
      IFEND;

{ The sequences for the remote procedure call have already been reset.

      NEXT procedure_ordinal_p IN send_to_server_params_p;
      procedure_ordinal_p^ := procedure_ordinal;
      NEXT target_mainframe_id_p IN send_to_server_params_p;
      target_mainframe_id_p^ := target_mainframe_id;
      NEXT data_packet_size_p IN send_to_server_params_p;
      data_packet_size_p^ := data_packet_size;
      NEXT mainframes_processed_p IN send_to_server_params_p;
      mainframes_processed_p^ := mainframes_processed_so_far;
      NEXT options_size_p IN send_to_server_params_p;
      options_size_p^ := #SIZE (target_options_p^);

      NEXT options_p: [[REP options_size_p^ OF cell]] IN send_to_server_data_p;
      options_p^ := target_options_p^;

      parameter_size := i#current_sequence_position (send_to_server_params_p);
      data_size := i#current_sequence_position (send_to_server_data_p);

      dfp$send_remote_procedure_call (queue_entry_location, dfc$rpc_jl_general_purpose, parameter_size,
            data_size, received_from_server_params_p, received_from_server_data_p, local_status);
      IF local_status.normal THEN
        NEXT target_mainframe_reached_p IN received_from_server_params_p;
        target_mainframe_reached := target_mainframe_reached_p^;
        NEXT mainframes_processed_p IN received_from_server_params_p;
        mainframes_processed := mainframes_processed_p^;
        NEXT number_of_data_packets_p IN received_from_server_params_p;
        IF number_of_data_packets_p^ > 0 THEN

{ Need to copy this value as it gets overwritten by the dfp$receive_server_rpc_segment
{ The spoil is used to keep the number_of_packets variable from being optimized out of
{ existence.  This will force the compiler to distinguish between the two values.

          number_of_packets := number_of_data_packets_p^;
          #SPOIL (number_of_packets);

          IF (number_of_packets * data_packet_size) > (#SIZE (data_area_p^) -
                i#current_sequence_position (data_area_p)) THEN
            number_of_data_packets := number_of_data_packets + number_of_packets;
            osp$set_status_condition (jme$work_area_too_small, status);
          ELSE

{ The data area sequence is positioned to the point where data should be added.

            NEXT data_p: [[REP (number_of_packets * data_packet_size) OF cell]] IN data_area_p;

            dfp$receive_server_rpc_segment (queue_entry_location, {server_segment_offset} 0,
                  number_of_packets * data_packet_size, data_p, local_status);
            IF local_status.normal THEN
              number_of_data_packets := number_of_data_packets + number_of_packets;
            ELSE
              RESET data_area_p TO data_p;
            IFEND;
          IFEND;
        IFEND;
      IFEND;
      IF NOT local_status.normal THEN
        osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], local_status, ignore_status);
      IFEND;
      dfp$end_ch_remote_proc_call (queue_entry_location, ignore_status);
    PROCEND call_server_general_purpose_rpc;
?? OLDTITLE ??
?? EJECT ??

{ BEGIN: jmp$general_purpose_cluster_rpc

    status.normal := TRUE;

    mainframes_processed := mainframes_processed_so_far;
    target_mainframe_reached := FALSE;
    number_of_data_packets := 0;

    IF mainframes_processed.count < jmc$maximum_mainframes THEN

{ Add the current mainframe to the mainframes processed list.

      mainframes_processed.count := mainframes_processed.count + 1;
      pmp$get_pseudo_mainframe_id (mainframes_processed.mainframes [mainframes_processed.count]);

      pmp$get_mainframe_id (current_mainframe_id, {ignore} status);

{ If we are at the target mainframe, then call the procedure requested....

      IF current_mainframe_id = target_mainframe_id THEN
        target_mainframe_reached := TRUE;
        v$gpcr_procedures [procedure_ordinal]^ (target_options_p, data_area_p, number_of_data_packets,
              status);
      ELSE
        IF target_mainframe_id = pmc$null_mainframe_id THEN
          v$gpcr_procedures [procedure_ordinal]^ (target_options_p, data_area_p, number_of_data_packets,
                status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
        IFEND;
        dfp$get_partner_mainframes ({partners_are_servers} TRUE, ^server_mainframe_list,
              server_mainframe_count);

        IF target_mainframe_id <> pmc$null_mainframe_id THEN
          pmp$convert_mainframe_to_binary (target_mainframe_id, binary_target_mainframe_id, local_status);
          IF local_status.normal THEN

{ Determine if the target mainframe is directly connected and available.

          /search_for_direct_mainframe/
            FOR server_mainframe_index := 1 TO server_mainframe_count DO
              IF (server_mainframe_list [server_mainframe_index].mainframe_id =
                    binary_target_mainframe_id) AND (server_mainframe_list [server_mainframe_index].
                    partner_state = dfc$active) THEN
                call_server_general_purpose_rpc ({mainframe_to_call} target_mainframe_id, target_mainframe_id,
                      procedure_ordinal, data_packet_size, mainframes_processed, target_options_p,
                      number_of_data_packets, data_area_p, target_mainframe_reached, mainframes_processed,
                      status);
                IF target_mainframe_reached OR (NOT status.normal) THEN
                  RETURN;
                ELSE
                  EXIT /search_for_direct_mainframe/;
                IFEND;
              IFEND;
            FOREND /search_for_direct_mainframe/;
          IFEND;
        IFEND;

{ The target is not directly connected or all mainframes are targeted.

      /call_each_server_mainframe/
        FOR server_mainframe_index := 1 TO server_mainframe_count DO
          IF mainframes_processed.count < jmc$maximum_mainframes THEN
            FOR mainframes_searched_index := 1 TO mainframes_processed.count DO
              IF mainframes_processed.mainframes [mainframes_searched_index] =
                    server_mainframe_list [server_mainframe_index].mainframe_id THEN
                CYCLE /call_each_server_mainframe/;
              IFEND;
            FOREND;

            IF server_mainframe_list [server_mainframe_index].partner_state = dfc$active THEN
              pmp$convert_binary_mainframe_id (server_mainframe_list [server_mainframe_index].mainframe_id,
                    mainframe_id, {ignore} status);
              call_server_general_purpose_rpc ({mainframe_to_call} mainframe_id, target_mainframe_id,
                    procedure_ordinal, data_packet_size, mainframes_processed, target_options_p,
                    number_of_data_packets, data_area_p, target_mainframe_reached, mainframes_processed,
                    status);
              IF target_mainframe_reached OR (NOT status.normal) THEN
                EXIT /call_each_server_mainframe/;
              IFEND;
            IFEND;
          IFEND;
        FOREND /call_each_server_mainframe/;
      IFEND
    IFEND;
  PROCEND jmp$general_purpose_cluster_rpc;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$server_general_purpose_rpc', EJECT ??
*copy jmh$server_general_purpose_rpc

  PROCEDURE [XDCL] jmp$server_general_purpose_rpc
    (VAR received_from_client_params_p { Input } : dft$p_receive_parameters;
     VAR received_from_client_data_p { Input } : dft$p_receive_data;
     VAR send_to_client_params_p { Output } : dft$p_send_parameters;
     VAR send_to_client_data_p { Output } : dft$p_send_data;
     VAR parameter_size: dft$send_parameter_size;
     VAR data_size: dft$send_data_size;
     VAR status: ost$status);

    VAR
      data_packet_size_p: ^ost$segment_length,
      local_status: ost$status,
      mainframes_processed_p: ^jmt$rpc_mainframes_processed,
      mainframes_processed_so_far_p: ^jmt$rpc_mainframes_processed,
      number_of_data_packets_p: ^ost$non_negative_integers,
      options_p: ^SEQ ( * ),
      options_size_p: ^ost$non_negative_integers,
      procedure_ordinal_p: ^jmt$general_purpose_rpc_ordinal,
      send_to_client_segment_p: ^SEQ ( * ),
      target_mainframe_id_p: ^pmt$mainframe_id,
      target_mainframe_reached_p: ^boolean;

    status.normal := TRUE;

  /process_remote_procedure_call/
    BEGIN

{ The sequences for the remote procedure call have already been reset.

      NEXT procedure_ordinal_p IN received_from_client_params_p;
      NEXT target_mainframe_id_p IN received_from_client_params_p;
      NEXT data_packet_size_p IN received_from_client_params_p;
      NEXT mainframes_processed_so_far_p IN received_from_client_params_p;
      NEXT options_size_p IN received_from_client_params_p;

      NEXT options_p: [[REP options_size_p^ OF cell]] IN received_from_client_data_p;

      NEXT target_mainframe_reached_p IN send_to_client_params_p;
      NEXT mainframes_processed_p IN send_to_client_params_p;
      NEXT number_of_data_packets_p IN send_to_client_params_p;

      dfp$reserve_server_rpc_segment (send_to_client_segment_p, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
      RESET send_to_client_segment_p;
      jmp$general_purpose_cluster_rpc (target_mainframe_id_p^, procedure_ordinal_p^, data_packet_size_p^,
            mainframes_processed_so_far_p^, options_p, send_to_client_segment_p, target_mainframe_reached_p^,
            mainframes_processed_p^, number_of_data_packets_p^, status);
    END /process_remote_procedure_call/;
    parameter_size := i#current_sequence_position (send_to_client_params_p);
    data_size := i#current_sequence_position (send_to_client_data_p);

  PROCEND jmp$server_general_purpose_rpc;
?? OLDTITLE ??
MODEND jmm$general_purpose_cluster_rpc;

