?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE:  Job Management - message handling interfaces' ??
MODULE jmm$job_message_management;

{ PURPOSE:
{   This module contains routines that are used to handle job messages.

{ NOTES:
{   To add an additional job message do the following:
{   o  Change the types jmt$job_message_kind and jmt$job_message to reflect the
{      new message.
{   o  Add a procedure (or a call) to the procedure send_job_message to process
{      the message once the target mainframe is reached.
{   o  Change the procedure pack_job_message to move the contents of the message
{      into the parameters and data to send to the server.  The total size of
{      of the parameters should be less than 3000 bytes.  If the amount of data
{      to be sent, exceeds this, use the data sequence to send information to
{      the server.
{   o  Change the unpack_job_message procedure to remove the data from the
{      sequences from the client.
{   o  Test the request using two mainframes (client & server).  There should
{      be no need to test the indirect case since any new message that is added
{      will be passed the same way using a previously validated path.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dft$rpc_parameters
*copyc jmc$job_management_id
*copyc jme$job_message_error
*copyc jmt$job_message
*copyc jmt$mainframes_searched_list
*copyc jmt$maximum_mainframes
*copyc ost$status
?? POP ??
*copyc dfp$get_partner_mainframes
*copyc dfp$send_remote_procedure_call
*copyc i#current_sequence_position
*copyc jmp$get_job_internal_info
*copyc jmp$get_job_status
*copyc jmp$get_result_size
*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
*copyc pmp$set_system_flag
?? OLDTITLE ??
?? NEWTITLE := 'send_indirect_job_message', EJECT ??

{ PURPOSE:
{   The purpose of this request is to send a job message via the file server's
{   remote procedure call mechanism to another mainframe.  If the target mainframe
{   is directly connected, send the message to the target immediately.  If the
{   target is not directly connected, or all mainframes are the target, then
{   send the message to all connected mainframes.

  PROCEDURE send_indirect_job_message
    (    target_mainframe_id: pmt$mainframe_id;
         job_message: jmt$job_message;
     VAR mainframes_searched_count {input, output} : jmt$maximum_mainframes;
     VAR mainframes_searched_list {input, output} : jmt$mainframes_searched_list;
     VAR target_mainframe_reached: boolean;
     VAR status: ost$status);

    VAR
      binary_target_mainframe_id: pmt$binary_mainframe_id,
      data_size: dft$send_data_size,
      local_status: ost$status,
      mainframe_id: pmt$mainframe_id,
      mainframes_searched_count_p: ^jmt$maximum_mainframes,
      mainframes_searched_index: jmt$maximum_mainframes,
      mainframes_searched_list_p: ^jmt$mainframes_searched_list,
      queue_entry_location: dft$rpc_queue_entry_location,
      parameter_size: dft$send_parameter_size,
      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_directly_connected: boolean,
      server_location: dft$server_location,
      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,
      target_mainframe_id_p: ^pmt$mainframe_id,
      target_mainframe_reached_p: ^boolean;

?? NEWTITLE := 'pack_job_message', EJECT ??

{ PURPOSE:
{   The purpose of this request is to pack the parameters and data passed to
{   the server and initialize pointers within the job_message to point to the
{   passed data.
{
{ NOTE:
{   For now, no packing is necessary since all data is currently passed in
{   the job message header.  When that changes, delete this note.

    PROCEDURE pack_job_message
      (    job_message: jmt$job_message;
       VAR send_to_server_params_p: dft$p_send_parameters;
       VAR send_to_server_data_p: dft$p_send_data;
       VAR status: ost$status);

      VAR
        local_job_message_p: ^jmt$job_message;

      status.normal := TRUE;
      NEXT local_job_message_p IN send_to_server_params_p;
      local_job_message_p^ := job_message;

      CASE job_message.message_kind OF
      = jmc$jmk_null_message =
        ;

      = jmc$jmk_unseen_mail_message =
        ;

      ELSE
        osp$set_status_condition (jme$job_message_error, status);
      CASEND;
    PROCEND pack_job_message;
?? OLDTITLE ??
?? 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 send_indirect_job_message;

    PROCEND dfp$remote_procedure_call_ch;
*block
*copyc dfp$begin_ch_remote_proc_call
*copyc dfp$end_ch_remote_proc_call
*blockend
?? OLDTITLE ??
?? EJECT ??
    status.normal := TRUE;
    target_mainframe_reached := FALSE;

    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

            server_location.server_location_selector := dfc$mainframe_id;
            server_location.server_mainframe := target_mainframe_id;

            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
              EXIT /search_for_direct_mainframe/;
            IFEND;

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

            NEXT target_mainframe_id_p IN send_to_server_params_p;
            target_mainframe_id_p^ := target_mainframe_id;
            NEXT mainframes_searched_count_p IN send_to_server_params_p;
            mainframes_searched_count_p^ := mainframes_searched_count;
            NEXT mainframes_searched_list_p IN send_to_server_params_p;
            mainframes_searched_list_p^ := mainframes_searched_list;
            pack_job_message (job_message, send_to_server_params_p, send_to_server_data_p, status);

            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_send_job_message, 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^;
              IF NOT target_mainframe_reached THEN
                NEXT mainframes_searched_count_p IN received_from_server_params_p;
                NEXT mainframes_searched_list_p IN received_from_server_params_p;
                mainframes_searched_count := mainframes_searched_count_p^;
                mainframes_searched_list := mainframes_searched_list_p^;
              IFEND;
            IFEND;
            dfp$end_ch_remote_proc_call (queue_entry_location, {ignore} local_status);

            IF target_mainframe_reached 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_searched_count < jmc$maximum_mainframes THEN
        FOR mainframes_searched_index := 1 TO mainframes_searched_count DO
          IF mainframes_searched_list [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);
          server_location.server_location_selector := dfc$mainframe_id;
          server_location.server_mainframe := mainframe_id;

          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
            CYCLE /call_each_server_mainframe/;
          IFEND;

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

          NEXT target_mainframe_id_p IN send_to_server_params_p;
          target_mainframe_id_p^ := target_mainframe_id;
          NEXT mainframes_searched_count_p IN send_to_server_params_p;
          mainframes_searched_count_p^ := mainframes_searched_count;
          NEXT mainframes_searched_list_p IN send_to_server_params_p;
          mainframes_searched_list_p^ := mainframes_searched_list;
          pack_job_message (job_message, send_to_server_params_p, send_to_server_data_p, status);

          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_send_job_message, 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^;
            IF NOT target_mainframe_reached THEN
              NEXT mainframes_searched_count_p IN received_from_server_params_p;
              NEXT mainframes_searched_list_p IN received_from_server_params_p;
              mainframes_searched_count := mainframes_searched_count_p^;
              mainframes_searched_list := mainframes_searched_list_p^;
            IFEND;
          IFEND;
          dfp$end_ch_remote_proc_call (queue_entry_location, {ignore} local_status);

          IF target_mainframe_reached THEN
            EXIT /call_each_server_mainframe/;
          IFEND;
        IFEND;
      IFEND;
    FOREND /call_each_server_mainframe/;
  PROCEND send_indirect_job_message;
?? OLDTITLE ??
?? NEWTITLE := 'send_job_message', EJECT ??

{ PURPOSE:
{   The purpose of this request is to send a job message to the message target
{   once the target mainframe is reached.
{
{ NOTE:
{   This request is only called on the target mainframe.

  PROCEDURE send_job_message
    (    job_message: jmt$job_message;
     VAR status: ost$status);

?? NEWTITLE := 'broadcast_unseen_mail', EJECT ??

{ PURPOSE:
{   The purpose of this request is to deliver the unseen mail message to the
{   appropriate jobs on the mainframe.

    PROCEDURE broadcast_unseen_mail
      (    job_message: jmt$job_message;
       VAR status: ost$status);

      VAR
        ignore_status: ost$status,
        index: jmt$job_status_count,
        job_count: ost$non_negative_integers,
        job_info: jmt$job_internal_information,
        number_of_jobs_found: jmt$job_status_count,
        size_of_sequence: ost$segment_length,
        status_options: array [1 .. 4] of jmt$job_status_option,
        status_results_keys_p: ^jmt$results_keys,
        status_results_seq_p: ^jmt$work_area,
        status_results_p: ^jmt$job_status_results;

      status.normal := TRUE;

      status_options [1].key := jmc$job_state_set;
      status_options [1].job_state_set := $jmt$job_state_set [jmc$initiated_job];
      status_options [2].key := jmc$login_family;
      status_options [2].login_family := job_message.unseen_mail_message.user_id.family;
      status_options [3].key := jmc$login_user;
      status_options [3].login_user := job_message.unseen_mail_message.user_id.user;
      status_options [4].key := jmc$privilege;
      status_options [4].privilege := jmc$privileged;

      number_of_jobs_found := 0;

      PUSH status_results_keys_p: [1 .. 1];
      status_results_keys_p^ [1] := jmc$system_job_name;

      REPEAT
        job_count := number_of_jobs_found + 1;
        jmp$get_result_size (job_count, #SEQ (status_results_keys_p^), size_of_sequence);
        PUSH status_results_seq_p: [[REP size_of_sequence OF cell]];

        jmp$get_job_status (^status_options, status_results_keys_p, status_results_seq_p, status_results_p,
              number_of_jobs_found, ignore_status);
      UNTIL number_of_jobs_found <= job_count;
      IF number_of_jobs_found <> 0 THEN
        FOR index := 1 TO number_of_jobs_found DO
          jmp$get_job_internal_info (status_results_p^ [index]^ [1].system_job_name, job_info, status);
          IF status.normal THEN
            pmp$set_system_flag (jmc$message_waiting_flag_id, job_info.jmtr_global_taskid, ignore_status);
          IFEND;
        FOREND;
        status.normal := TRUE;
      IFEND;
    PROCEND broadcast_unseen_mail;
?? OLDTITLE ??
?? EJECT ??


    status.normal := TRUE;

    CASE job_message.message_kind OF
    = jmc$jmk_null_message =
      ;

    = jmc$jmk_unseen_mail_message =
      broadcast_unseen_mail (job_message, status);

    ELSE
      osp$set_status_condition (jme$job_message_error, status);
    CASEND;

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

  PROCEDURE [XDCL] jmp$send_job_message
    (    target_mainframe_id: pmt$mainframe_id;
         job_message: jmt$job_message;
     VAR status: ost$status);

    VAR
      current_mainframe_id: pmt$mainframe_id,
      mainframes_searched_count: jmt$maximum_mainframes,
      mainframes_searched_list: jmt$mainframes_searched_list,
      target_mainframe_reached: boolean;

    status.normal := TRUE;

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

{ If we are at the target mainframe, then process the message....

    IF current_mainframe_id = target_mainframe_id THEN
      send_job_message (job_message, status);
    ELSE
      IF target_mainframe_id = pmc$null_mainframe_id THEN
        send_job_message (job_message, status);
      IFEND;

      mainframes_searched_count := 1;
      pmp$get_pseudo_mainframe_id (mainframes_searched_list [1]);
      send_indirect_job_message (target_mainframe_id, job_message, mainframes_searched_count,
            mainframes_searched_list, target_mainframe_reached, status);

      IF NOT target_mainframe_reached THEN
        osp$set_status_condition (jme$job_message_error, status);
      IFEND;
    IFEND;

  PROCEND jmp$send_job_message;

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

  PROCEDURE [XDCL] jmp$server_send_job_message
    (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
      current_mainframe_id: pmt$mainframe_id,
      job_message: jmt$job_message,
      local_job_message_p: ^jmt$job_message,
      mainframes_searched_count: jmt$maximum_mainframes,
      mainframes_searched_count_p: ^jmt$maximum_mainframes,
      mainframes_searched_list: jmt$mainframes_searched_list,
      mainframes_searched_list_p: ^jmt$mainframes_searched_list,
      target_mainframe_id_p: ^pmt$mainframe_id,
      target_mainframe_reached: boolean,
      target_mainframe_reached_p: ^boolean;

?? NEWTITLE := 'unpack_job_message', EJECT ??

{ PURPOSE:
{   The purpose of this request is to unpack the parameters and data passed from
{ the client and initialize pointers within the job_message to point to the
{ passed data.
{
{ NOTE:
{   For now, no unpacking is necessary since all data is currently passed in
{ the job message header.  When that changes, delete this note.

    PROCEDURE unpack_job_message
      (VAR received_from_client_params_p {input} : dft$p_receive_parameters;
       VAR received_from_client_data_p {input} : dft$p_receive_data;
       VAR job_message {input,output} : jmt$job_message;
       VAR status: ost$status);

      VAR
        local_job_message_p: ^jmt$job_message;

      status.normal := TRUE;
      NEXT local_job_message_p IN received_from_client_params_p;
      job_message := local_job_message_p^;
      CASE job_message.message_kind OF
      = jmc$jmk_null_message =
        ;

      = jmc$jmk_unseen_mail_message =
        ;

      ELSE
        osp$set_status_condition (jme$job_message_error, status);
      CASEND;
    PROCEND unpack_job_message;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;

  /process_remote_procedure_call/
    BEGIN
      NEXT target_mainframe_id_p IN received_from_client_params_p;
      NEXT mainframes_searched_count_p IN received_from_client_params_p;
      mainframes_searched_count := mainframes_searched_count_p^;
      NEXT mainframes_searched_list_p IN received_from_client_params_p;
      mainframes_searched_list := mainframes_searched_list_p^;

      unpack_job_message (received_from_client_params_p, received_from_client_data_p, job_message, status);

      IF status.normal THEN
        pmp$get_mainframe_id (current_mainframe_id, {ignore} status);
        IF current_mainframe_id = target_mainframe_id_p^ THEN
          send_job_message (job_message, status);
          NEXT target_mainframe_reached_p IN send_to_client_params_p;
          target_mainframe_reached_p^ := TRUE;
        ELSE
          IF target_mainframe_id_p^ = pmc$null_mainframe_id THEN
            send_job_message (job_message, status);
          IFEND;
          mainframes_searched_count := mainframes_searched_count + 1;
          pmp$get_pseudo_mainframe_id (mainframes_searched_list [mainframes_searched_count]);
          send_indirect_job_message (target_mainframe_id_p^, job_message, mainframes_searched_count,
                mainframes_searched_list, target_mainframe_reached, status);

          NEXT target_mainframe_reached_p IN send_to_client_params_p;
          target_mainframe_reached_p^ := target_mainframe_reached;
          IF NOT target_mainframe_reached THEN
            NEXT mainframes_searched_count_p IN send_to_client_params_p;
            NEXT mainframes_searched_list_p IN send_to_client_params_p;
            mainframes_searched_count_p^ := mainframes_searched_count;
            mainframes_searched_list_p^ := mainframes_searched_list;
          IFEND;
        IFEND;
      IFEND; { unpack - status.normal
    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_send_job_message;
?? OLDTITLE ??
MODEND jmm$job_message_management;

