?? RIGHT := 110 ??
?? NEWTITLE := ' NOS/VE File Server: Client: job_server_manager', EJECT ??
MODULE dfm$job_server_manager;

{
{  This module manages the association of a job on the client to a server mainframe.
{  This envolves establishing the job on the server, and maintaining a list
{  (the job server table) of server mainframes that the client job is using.
{  At job termination all server mainframes are informed of the termination
{  of the job.
{

?? NEWTITLE := '    Global Declarations ', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc avp$system_administrator
*copyc avp$family_administrator
*copyc dfd$driver_queue_types
*copyc dfi$display
*copyc dfe$error_condition_codes
*copyc dfi$display
*copyc dfi$log_display
*copyc dfp$clear_inhibit_access_work
*copyc dfp$fetch_access_work
*copyc dfp$verify_all_sdtxs_recovered
*copyc dfk$keypoints
*copyc dft$family_access
*copyc dft$mainframe_set
*copyc dfp$extract_send_parameters
*copyc dfp$find_mainframe_id
*copyc dfp$get_qit_p_from_direct_index
*copyc dfp$locate_served_family
*copyc dfv$file_server_debug_enabled
*copyc dfv$defined_server_translation
*copyc dfp$send_remote_procedure_call
*copyc dfv$job_recovery_enabled
*copyc fmp$recover_server_files
*copyc fmp$terminate_server_files
*copyc i#move
*copyc jmp$system_job
*copyc jmp$get_server_job_end_info
*copyc jmp$job_is_being_leveled
*copyc ofp$display_status_message
*copyc osc$server_job_recovery_cond
*copyc osd$integer_limits
*copyc ose$job_recovery_exceptions
*copyc osp$clear_job_signature_lock
*copyc osp$enforce_exception_policies
*copyc osp$file_access_condition
*copyc osp$increment_locked_variable
*copyc osp$initialize_signature_lock
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$set_status_from_condition
*copyc osp$test_signature_lock
*copyc oss$job_pageable
*copyc oss$job_paged_literal
*copyc ost$signature_lock
*copyc osp$system_error
*copyc osp$test_sig_lock
*copyc osv$initial_exception_context
*copyc osv$job_pageable_heap
*copyc pfe$internal_error_conditions
*copyc pmp$convert_binary_mainframe_id
*copyc pmp$cause_condition_in_tasks
*copyc pmp$get_account_project
*copyc pmp$get_executing_task_gtid
*copyc pmp$get_job_mode
*copyc pmp$get_job_names
*copyc pmp$get_pseudo_mainframe_id
*copyc pmp$get_user_identification
*copyc pmp$log_ascii
*copyc syp$pop_inhibit_job_recovery
*copyc syp$push_inhibit_job_recovery
?? POP ??
*copyc dft$job_server_table
?? TITLE := '  Parameter passing protocols ', EJECT ??
*copyc dft$start_job_recovery
*copyc dft$end_job_recovery
*copyc dft$establish_client_job
?? SKIP := 5 ??
*copyc dft$delete_client_job
?? SKIP := 5 ??
*copyc dft$change_client_job_validaton
?? TITLE := '    Global Variables ', EJECT ??

  CONST
    job_end_notification_timeout = 5 * 60 * 1000; {5 minutes}

  VAR
    dfv$p_job_server_table: [oss$job_pageable, XDCL, #GATE] dft$p_job_server_table := NIL,
    dfv$job_server_count: [oss$job_pageable, XDCL, #GATE] integer := 0,
    dfv$job_server_table_lock: [oss$job_pageable, XDCL, #GATE] ost$signature_lock := [0],
    dfv$job_recovery_lock: [oss$job_pageable, XDCL, #GATE] ost$signature_lock := [0];


  VAR
    dfv$job_recovery_rpc_requests: [XDCL, #GATE, READ, oss$job_paged_literal]
          dft$procedure_address_ordinals := [dfc$initiate_job_recovery, dfc$complete_job_recovery,
          dfc$relink_server_file];

?? TITLE := ' [XDCL, #GATE] dfp$check_job_server  ', EJECT ??

*copyc dfh$check_job_server

  PROCEDURE [XDCL, #GATE] dfp$check_job_server
    (    queue_entry_location: dft$rpc_queue_entry_location;
         queue_entry_loc_int: dft$rpc_queue_entry_loc_int;
         user_parameter_size: dft$send_parameter_size;
         job_recovery_request: boolean;
         force_job_reconnection: boolean;
     VAR client_job_id: dft$client_job_id;
     VAR status: ost$status);

    VAR
      change_validation_info: boolean,
      current_lifetime: dft$lifetime,
      display_string: string (60),
      forced_reconnection: boolean,
      p_ignore_status: ^ost$status,
      family_access_kind: dft$family_access_kinds,
      job_server_table_index: dft$job_server_table_index,
      job_table_entry_exists: boolean,
      job_using_server_mainframe: boolean,
      p_queue_interface_table: dft$p_queue_interface_table;

    #KEYPOINT (osk$entry, osk$m * queue_entry_loc_int.queue_index, dfk$check_job_server);
    forced_reconnection := FALSE;
    change_validation_info := FALSE;
    dfp$get_qit_p_from_direct_index (queue_entry_loc_int.queue_directory_index, p_queue_interface_table);

{ There is probably a window here where a terminate_server and delete_server could take
{ place while this is happening - thus deleting tables out from under this
{ operation.

    current_lifetime := p_queue_interface_table^.queue_directory.
          cpu_queue_pva_directory [queue_entry_loc_int.queue_index].p_cpu_queue^.queue_header.
          server_lifetime;
    set_job_server_table_lock;

    locate_jobs_server_mainframe (queue_entry_loc_int.server_mainframe_id, job_using_server_mainframe,
          job_server_table_index);
    job_table_entry_exists := job_using_server_mainframe;
    IF force_job_reconnection OR (job_table_entry_exists AND
          dfv$p_job_server_table^ [job_server_table_index].force_server_reconnection) THEN
      dfv$p_job_server_table^ [job_server_table_index].force_server_reconnection := FALSE;
      job_using_server_mainframe := FALSE;
      forced_reconnection := TRUE;
    ELSEIF job_using_server_mainframe THEN
      IF job_recovery_request THEN
        client_job_id := dfv$p_job_server_table^ [job_server_table_index].client_job_id;
        status.normal := TRUE;
      ELSEIF dfv$p_job_server_table^ [job_server_table_index].server_lifetime = current_lifetime THEN
        client_job_id := dfv$p_job_server_table^ [job_server_table_index].client_job_id;
        IF dfv$p_job_server_table^ [job_server_table_index].change_validation_info THEN
          dfv$p_job_server_table^ [job_server_table_index].change_validation_info := FALSE;
          change_validation_info := TRUE;
        IFEND;
        status.normal := TRUE;
      ELSEIF dfv$job_recovery_enabled THEN
        IF jmp$system_job () THEN
          status.normal := TRUE;
          job_using_server_mainframe := FALSE;
          forced_reconnection := TRUE;
        ELSE

{ The job either needs to recover or the server has been terminated
{ GO back to the caller to force recovery, or re-establishment
          pmp$convert_binary_mainframe_id (queue_entry_loc_int.server_mainframe_id,
                display_string (1, pmc$mainframe_id_size), status);
          IF p_queue_interface_table^.queue_directory.
                cpu_queue_pva_directory [queue_entry_loc_int.queue_index].
                 p_cpu_queue^.queue_header.partner_status.server_state = dfc$terminated THEN
            osp$set_status_abnormal (dfc$file_server_id, dfe$server_has_terminated,
                  display_string (1, pmc$mainframe_id_size), status);
          ELSEIF p_queue_interface_table^.queue_directory.
                cpu_queue_pva_directory [queue_entry_loc_int.queue_index].
                 p_cpu_queue^.queue_header.partner_status.server_state <> dfc$active THEN
            osp$set_status_abnormal (dfc$file_server_id, dfe$server_not_active,
                  display_string (1, pmc$mainframe_id_size), status);
          ELSE
            osp$set_status_abnormal (dfc$file_server_id, dfe$job_needs_recovery,
                  display_string (1, pmc$mainframe_id_size), status);
            IF dfv$file_server_debug_enabled THEN
              display_string := ' Job needs recovery for server ';
              PUSH p_ignore_status;
              pmp$convert_binary_mainframe_id (queue_entry_loc_int.server_mainframe_id,
                    display_string (32, pmc$mainframe_id_size), p_ignore_status^);
              pmp$log_ascii (display_string, $pmt$ascii_logset [pmc$job_log], pmc$msg_origin_system,
                    p_ignore_status^);
            IFEND;
          IFEND;
        IFEND;
      ELSE

{ The job will be re-connected to the server.
{ All server files in the job will be at the wrong lifetime.
        fmp$terminate_server_files (queue_entry_loc_int.server_mainframe_id,
             status);
        forced_reconnection := TRUE;
        status.normal := TRUE;
        job_using_server_mainframe := FALSE;
      IFEND;
    ELSE { No job server table entry exists
      { Set forced reconnection to true for the case where a client job list
      { entry exists on the server.
      forced_reconnection := TRUE;
    IFEND;

    IF status.normal AND NOT job_using_server_mainframe THEN
      inform_server_of_client_job (queue_entry_location, queue_entry_loc_int, user_parameter_size,
            forced_reconnection, current_lifetime, client_job_id, family_access_kind, status);
      IF status.normal THEN
        display_string := ' Job using server ';
        pmp$convert_binary_mainframe_id (queue_entry_loc_int.server_mainframe_id,
              display_string (20, pmc$mainframe_id_size), status);
        IF forced_reconnection AND job_table_entry_exists THEN
          display_string (40, 20) := '  reconnecting';
        IFEND;
        pmp$log_ascii (display_string, $pmt$ascii_logset [pmc$job_log], pmc$msg_origin_system, status);
        create_job_server_entry (queue_entry_loc_int.server_mainframe_id, client_job_id, current_lifetime,
              job_table_entry_exists, family_access_kind, job_server_table_index);
      IFEND;
    IFEND;
    clear_job_server_table_lock;

    IF status.normal AND change_validation_info THEN

{ Change the validation info without the lock being set so that the
{ normal remote procedure call mechanism can work. (the normal mechanism
{ calls dfp$check_job_server).

      change_server_validation_info (queue_entry_location, queue_entry_loc_int, user_parameter_size,
            client_job_id, status);
    IFEND;
    IF status.normal THEN
      #KEYPOINT (osk$exit, osk$m * client_job_id.job_list_index, dfk$check_job_server);
    ELSE
      #KEYPOINT (osk$exit, 0, dfk$check_job_server);
    IFEND;
  PROCEND dfp$check_job_server;
?? TITLE := ' [XDCL, #GATE] dfp$get_job_server_state  ', EJECT ??


  PROCEDURE [XDCL, #GATE] dfp$get_job_server_state
    (    mainframe_id: pmt$binary_mainframe_id;
     VAR server_found: boolean;
     VAR server_lifetime: dft$lifetime);

    VAR
      job_server_table_index: dft$job_server_table_index;

    locate_jobs_server_mainframe (mainframe_id, server_found, job_server_table_index);
    IF server_found THEN
      server_lifetime := dfv$p_job_server_table^ [job_server_table_index].server_lifetime;
    IFEND;
  PROCEND dfp$get_job_server_state;
?? TITLE := ' dfp$process_job_end ', EJECT ??

*copyc dfh$process_job_end

  PROCEDURE [XDCL, #GATE] dfp$process_job_end;

    VAR
      job: dft$job_server_table_index,
      job_is_leveled: boolean,
      status: ost$status;

{ Dont set a lock here:
{ 1. There is no asynchronous task executing now.
{ 2. This request calls dfp$send_remote_procedure_call which for
{    permanent file requests, calls dfp$check_job_server which would
{    attempt to set this same lock.   An alternative would be to not
{    treat this request as a permanent file request, and have the server
{    procedure dfp$delete_client_job call dfp$validate_client_job_id
{    and pfp$set_task_environment.

    job_is_leveled := jmp$job_is_being_leveled ();
    IF (dfv$job_server_count = 0) AND job_is_leveled THEN

{This  assumes that the only way the job can be leveled and not have a
{  job server table is due to an abort during job_begin; that is, before
{  the job could access any other server.

      inform_server_of_client_job_end (NIL, job_is_leveled, status);
    IFEND;

  /inform_all_servers/
    FOR job := 1 TO dfv$job_server_count DO
      inform_server_of_client_job_end (^dfv$p_job_server_table^ [job], job_is_leveled, status);
    FOREND /inform_all_servers/;

    dfv$job_server_count := 0;

  PROCEND dfp$process_job_end;

?? TITLE := '  dfp$r2_check_job_recovery  ', EJECT ??
*copyc dfh$check_job_recovery

{ This procedure must handle the case of a delete_server of a server in the
{  job server table.
{ There is probably a window here where a terminate_server and delete_server could take
{  place while this is happening - thus deleting tables out from under this
{  operation.

  PROCEDURE [XDCL, #GATE] dfp$r2_check_job_recovery (VAR recovery_occurred: boolean);

    VAR
      display_string: string (60),
      inhibit_access_work: dft$mainframe_set,
      job: dft$job_server_table_index,
      lock_status: ost$signature_lock_status,
      mainframe_found: boolean,
      p_cpu_queue: ^dft$cpu_queue,
      p_q_interface_directory_entry: ^dft$q_interface_directory_entry,
      p_queue_interface_table: dft$p_queue_interface_table,
      queue_index: dft$queue_index,
      status: ost$status,
      terminate_access_work: dft$mainframe_set;

{ LOCKING NEEDS MORE WORK
{ 1. There may be asynchronous task executing now.
{ 2. This request calls dfp$send_remote_procedure_call which
{    calls dfp$check_job_server which would
{    attempt to set the job server lock.
{ 3. What should dfp$check_job_server do while this going on? I
{    think it should wait, but it would be nice if recovery for one
{    server would not prevent a different task from using a different server.

    recovery_occurred := FALSE;

    osp$test_sig_lock (dfv$job_recovery_lock, lock_status);
    IF lock_status <> osc$sls_not_locked THEN

{ Force the wait at a higher ring

      RETURN;
    IFEND;
    syp$push_inhibit_job_recovery;
    osp$set_job_signature_lock (dfv$job_recovery_lock);

  /inform_all_servers/
    FOR job := 1 TO dfv$job_server_count DO
      dfp$find_mainframe_id (dfv$p_job_server_table^ [job].server_mainframe, { Server_to_client = } FALSE,
            mainframe_found, p_queue_interface_table, p_cpu_queue, queue_index,
            p_q_interface_directory_entry);
      IF NOT mainframe_found THEN
        { The queues are not defined yet - wait.
        CYCLE /inform_all_servers/;
      IFEND;
      IF (p_cpu_queue^.queue_header.partner_status.server_state = dfc$active) AND
            (p_cpu_queue^.queue_header.server_lifetime <> dfv$p_job_server_table^ [job].server_lifetime) THEN
        dfp$recover_job (p_cpu_queue^.queue_header.destination_mainframe_id, status);
        recovery_occurred := status.normal OR recovery_occurred;
      IFEND;
    FOREND /inform_all_servers/;

    IF NOT recovery_occurred THEN
      dfp$fetch_access_work (inhibit_access_work, terminate_access_work);
      IF inhibit_access_work <> $dft$mainframe_set [] THEN
        clear_inhibit_access_now_active  (inhibit_access_work);
      IFEND;
    IFEND;
    osp$clear_job_signature_lock (dfv$job_recovery_lock);
    syp$pop_inhibit_job_recovery;

  PROCEND dfp$r2_check_job_recovery;
?? TITLE := ' dfp$recover_job  ', EJECT ??
*copyc dfh$recover_job
  PROCEDURE [XDCL, #GATE] dfp$recover_job
    (    server_mainframe_id: pmt$binary_mainframe_id;
     VAR status: ost$status);

    VAR
      file_manager_status: ost$status,
      ignore: ost$status,
      job_server_table_index: dft$job_server_table_index,
      job_using_server_mainframe: boolean,
      p_job_server_table_entry: ^dft$job_server_table_entry,
      mainframe_found: boolean,
      message: string (ofc$max_display_message),
      message_length: integer,
      p_cpu_queue: ^dft$cpu_queue,
      p_q_interface_directory_entry: ^dft$q_interface_directory_entry,
      p_queue_interface_table: dft$p_queue_interface_table,
      queue_index: dft$queue_index,
      server_mainframe: pmt$mainframe_id;

  /job_recovery_block/
    BEGIN
      dfp$clear_inhibit_access_work (server_mainframe_id, {clear_sdtx} FALSE);

      pmp$convert_binary_mainframe_id (server_mainframe_id, server_mainframe, status);
      dfp$find_mainframe_id (server_mainframe, { Server_to_client = } FALSE, mainframe_found,
            p_queue_interface_table, p_cpu_queue, queue_index, p_q_interface_directory_entry);
      IF NOT mainframe_found THEN
        fmp$terminate_server_files (server_mainframe_id, file_manager_status);
        osp$set_status_condition (dfe$server_has_terminated, status);
        EXIT /job_recovery_block/;
      IFEND;

      locate_jobs_server_mainframe (server_mainframe_id, job_using_server_mainframe, job_server_table_index);
      IF NOT job_using_server_mainframe THEN
        fmp$terminate_server_files (server_mainframe_id, file_manager_status);
        osp$set_status_condition (dfe$server_has_terminated, status);
        EXIT /job_recovery_block/;
      IFEND;
      p_job_server_table_entry := ^dfv$p_job_server_table^ [job_server_table_index];

      IF NOT dfv$job_recovery_enabled THEN
        fmp$terminate_server_files (server_mainframe_id, file_manager_status);
        p_job_server_table_entry^.force_server_reconnection := TRUE;
        STRINGREP (message, message_length, 'Server ', server_mainframe, ' job recovery disabled');
        log_display ($pmt$ascii_logset [pmc$job_log, pmc$system_log], message (1, message_length));
        osp$set_status_condition (dfe$server_has_terminated, status);
        EXIT /job_recovery_block/;
      IFEND;

      STRINGREP (message, message_length, 'Server ', server_mainframe, ' job recovery in progress');
      ofp$display_status_message (message (1, message_length), ignore);
      start_job_recovery (server_mainframe, p_job_server_table_entry,
            status);
      IF NOT status.normal THEN
        STRINGREP (message, message_length, 'Server ', server_mainframe, ' job recovery failed');
        ofp$display_status_message (message (1, message_length), ignore);
        log_display ($pmt$ascii_logset [pmc$job_log, pmc$system_log], message (1, message_length));
        log_display_status ($pmt$ascii_logset [pmc$job_log, pmc$system_log], FALSE, status);
        IF (status.condition <> dfe$server_not_active) AND (status.condition <> dfe$server_request_terminated)
              THEN
          fmp$terminate_server_files (server_mainframe_id, file_manager_status);

{ Need to force the next access to this server to be as if new.

          p_job_server_table_entry^.force_server_reconnection := TRUE;
        IFEND;
        RETURN;
      IFEND;

      IF dfv$file_server_debug_enabled THEN
        log_display ($pmt$ascii_logset [pmc$job_log], ' fmp$recover_server_files');
      IFEND;
      fmp$recover_server_files (server_mainframe_id, file_manager_status);
      IF NOT file_manager_status.normal THEN
        log_display_status ($pmt$ascii_logset [pmc$job_log, pmc$system_log], FALSE, file_manager_status);

{ Despite the fact that some files did not recover allow the job to
{ proceed.
      IFEND;

      dfp$verify_all_sdtxs_recovered (server_mainframe_id);

      end_job_recovery (server_mainframe, p_job_server_table_entry,
            p_cpu_queue^.queue_header.server_lifetime, status);
      IF NOT status.normal THEN
        log_display_status ($pmt$ascii_logset [pmc$job_log], FALSE, status);
      IFEND;
      STRINGREP (message, message_length, 'Server ', server_mainframe, ' job recovery complete');
      ofp$display_status_message (message (1, message_length), ignore);
      log_display ($pmt$ascii_logset [pmc$job_log, pmc$system_log], message (1, message_length));
    END /job_recovery_block/;

    pmp$cause_condition_in_tasks (osc$server_job_recovery_cond);
  PROCEND dfp$recover_job;
?? TITLE := ' [XDCL, #GATE] dfp$recover_jobs_servers ', EJECT ??

{
{   The purpose of this routine is to set up for server recovery in the job.
{ IF recovery is enabled THEN
{   This routine verifies that the job server table can be read.
{ ELSE - recovery is NOT enabled
{      IF the job had used the file server but no longer had any server files
{         attached the job will recover.
{      IF the job had server files attach then
{         pfp$reattach_permanent_file procedure will detect the files then,
{         and the job will not be recovered.
{

  PROCEDURE [XDCL, #GATE] dfp$recover_jobs_servers
    (VAR status: ost$status);

    VAR
      lock_status: ost$signature_lock_status;

    status.normal := TRUE;

    IF (dfv$job_server_count > 0) AND dfv$file_server_debug_enabled THEN
      pmp$log_ascii (' Recovering Job using file server', $pmt$ascii_logset [pmc$system_log, pmc$job_log],
            pmc$msg_origin_system, status);
    IFEND;

    osp$test_signature_lock (dfv$job_server_table_lock, lock_status);
    IF lock_status <> osc$sls_not_locked THEN
      log_display ($pmt$ascii_logset [pmc$job_log, pmc$system_log],
            ' dfv$job_server_table_lock  set - cant recover job ');
      osp$set_status_abnormal (dfc$file_server_id, ose$job_severely_damaged,
            ' dfv$job_server_table_lock set ', status);
      RETURN;
    IFEND;
    osp$test_signature_lock (dfv$job_recovery_lock, lock_status);
    IF lock_status <> osc$sls_not_locked THEN
      log_display ($pmt$ascii_logset [pmc$job_log, pmc$system_log],
            ' dfv$job_recovery_lock set cant recovery job ');
      osp$set_status_abnormal (dfc$file_server_id, ose$job_severely_damaged, ' dfv$job_recovery_lock set ',
            status);
      RETURN;
    IFEND;
  PROCEND dfp$recover_jobs_servers;
?? TITLE := ' [XDCL, #GATE] dfp$set_job_validation_change ', EJECT ??

{
{   The purpose of this routine is to indicate that the validation information
{ for the current job has changed.  This is marked in the job server table entry
{ for each server that the job is using.  On the next request to this server the
{ change is noted (see dfp$check_job_server) and a request is made to that
{ server to update the information.

  PROCEDURE [XDCL, #GATE] dfp$set_job_validation_change;

    VAR
      job_server_table_index: dft$job_server_table_index;

    set_job_server_table_lock;

  /change_all_servers/
    FOR job_server_table_index := 1 TO dfv$job_server_count DO
      dfv$p_job_server_table^ [job_server_table_index].change_validation_info := TRUE;
    FOREND /change_all_servers/;

    clear_job_server_table_lock;

  PROCEND dfp$set_job_validation_change;
?? TITLE := ' [INLINE] build_job_begin_send_buffer ', EJECT ??

  PROCEDURE [INLINE] build_job_begin_send_buffer
    (    server_mainframe_id: pmt$binary_mainframe_id;
         server_lifetime: dft$lifetime;
         forced_reconnection: boolean;
     VAR family_access_kind: dft$family_access_kinds;
     VAR p_send_to_server_params: dft$p_send_parameters;
     VAR parameters_length: dft$send_parameter_size);

    VAR
      family_found: boolean,
      family_mainframe_id: pmt$binary_mainframe_id,
      p_queue_interface_table: dft$p_queue_interface_table,
      p_send_parameters: ^dft$establish_client_job_inp,
      queue_index: dft$queue_index,
      served_family_table_index: dft$served_family_table_index,
      server_state: dft$server_state,
      status: ost$status;

    NEXT p_send_parameters IN p_send_to_server_params;

{ Fetch required information to send to server

    pmp$get_user_identification (p_send_parameters^.user_id, status);
    pmp$get_account_project (p_send_parameters^.account, p_send_parameters^.project, status);
    pmp$get_job_names (p_send_parameters^.user_supplied_job_name, p_send_parameters^.system_supplied_job_name,
          status);
    pmp$get_job_mode (p_send_parameters^.job_mode, status);
    dfp$locate_served_family (p_send_parameters^.user_id.family, family_found, served_family_table_index,
          family_mainframe_id, p_queue_interface_table, queue_index, server_state);
    family_access_kind := dfc$remote_file_access;
    IF family_found THEN
      IF family_mainframe_id = server_mainframe_id THEN
        IF jmp$job_is_being_leveled () THEN
          family_access_kind := dfc$job_leveling_access;
        ELSE
          family_access_kind := dfc$remote_login_access;
        IFEND;
      IFEND;
    IFEND;
    p_send_parameters^.family_access_kind := family_access_kind;
    p_send_parameters^.system_administrator := avp$system_administrator ();
    p_send_parameters^.family_administrator := avp$family_administrator ();
    p_send_parameters^.forced_reconnection := forced_reconnection;
    p_send_parameters^.job_lifetime := server_lifetime;
    parameters_length := #SIZE (p_send_parameters^);
  PROCEND build_job_begin_send_buffer;

?? TITLE := ' [INLINE] build_job_end_send_buffer ', EJECT ??

  PROCEDURE [INLINE] build_job_end_send_buffer
    (    job_server_table_exists: boolean;
         client_job_id: dft$client_job_id;
         job_is_leveled: boolean;
         job_end_info: jmt$jl_server_job_end_info;
     VAR p_send_to_server_params: dft$p_send_parameters;
     VAR parameters_length: dft$send_parameter_size);

    VAR
      p_send_parameters: ^dft$delete_client_job_inp,
      status: ost$status,
      user_supplied_name: jmt$user_supplied_name;

    NEXT p_send_parameters IN p_send_to_server_params;

    pmp$get_job_names (user_supplied_name, p_send_parameters^.system_supplied_job_name, status);
    p_send_parameters^.client_job_table_exists := job_server_table_exists;
    p_send_parameters^.client_job_id := client_job_id;
    p_send_parameters^.job_end_info := job_end_info;
    p_send_parameters^.job_is_leveled := job_is_leveled;
    parameters_length := #SIZE (p_send_parameters^);
  PROCEND build_job_end_send_buffer;
?? TITLE := ' change_server_validation_info', EJECT ??

  PROCEDURE change_server_validation_info
    (    queue_entry_location: dft$rpc_queue_entry_location;
         queue_entry_loc_int: dft$rpc_queue_entry_loc_int;
         users_parameter_size: dft$send_parameter_size;
         client_job_id: dft$client_job_id;
     VAR status: ost$status);

    VAR
      p_receive_data: dft$p_receive_data,
      p_receive_from_server_params: dft$p_receive_parameters,
      p_saved_parameters: ^SEQ ( * ),
      p_send_parameters: ^dft$change_client_job_valid_in,
      p_send_to_server_params: dft$p_send_parameters,
      user_supplied_name: jmt$user_supplied_name;

    dfp$extract_send_parameters (queue_entry_loc_int, p_send_to_server_params);
    IF users_parameter_size > 0 THEN
      PUSH p_saved_parameters: [[REP users_parameter_size OF cell]];
      i#move (p_send_to_server_params, p_saved_parameters, users_parameter_size);
    IFEND;
    NEXT p_send_parameters IN p_send_to_server_params;
    p_send_parameters^.client_job_id := client_job_id;
    pmp$get_job_names (user_supplied_name, p_send_parameters^.system_supplied_job_name, status);
    pmp$get_account_project (p_send_parameters^.account, p_send_parameters^.project, status);
    dfp$send_remote_procedure_call (queue_entry_location, dfc$change_job_validation_info,
          #SIZE (p_send_parameters^), 0, p_receive_from_server_params, p_receive_data, status);
    IF users_parameter_size > 0 THEN
      i#move (p_saved_parameters, p_send_to_server_params, users_parameter_size);
    IFEND;
  PROCEND change_server_validation_info;
?? TITLE := ' clear_inhibit_access_now_active', EJECT ??
{
{   The purpose of this procedure is to clear the inhibit access for any server
{ mainframe that was inactive but is now active.  For a server that was
{ awaiting recovery and is now active, the access state is changed by server
{ job recovery.  In the inactive state the only change that is made in the state
{ transistion is go go through the segment descriptor table extended and mark all
{ server files with an access_state of inhibit_access.  This is the process that
{ marks the files to allow access again.

  PROCEDURE clear_inhibit_access_now_active
    (    inhibit_access_work: dft$mainframe_set);

    VAR
      job_server_table_index: dft$job_server_table_index,
      server_mainframe_found: boolean,
      server_mainframe_id: pmt$binary_mainframe_id,
      server_mainframe_ordinal: 1 .. dfc$max_number_of_mainframes,
      server_mainframe_name: pmt$mainframe_id,
      p_cpu_queue: ^dft$cpu_queue,
      p_q_interface_directory_entry: ^dft$q_interface_directory_entry,
      p_queue_interface_table: ^dft$queue_interface_table,
      queue_index: dft$queue_index,
      status: ost$status;

  /check_all_inhibit_mainframes/
    FOR server_mainframe_ordinal := 1 TO dfc$max_number_of_mainframes DO
      IF server_mainframe_ordinal IN inhibit_access_work THEN
        server_mainframe_id := dfv$defined_server_translation [server_mainframe_ordinal];
        pmp$convert_binary_mainframe_id (server_mainframe_id, server_mainframe_name, status);
        dfp$find_mainframe_id (server_mainframe_name, { Server_to_client = } FALSE, server_mainframe_found,
              p_queue_interface_table, p_cpu_queue, queue_index, p_q_interface_directory_entry);
        IF server_mainframe_found THEN
          IF (p_cpu_queue^.queue_header.partner_status.server_state = dfc$active) THEN
            locate_jobs_server_mainframe (server_mainframe_id, server_mainframe_found,
                  job_server_table_index);
            IF NOT server_mainframe_found OR (dfv$p_job_server_table^ [job_server_table_index].
                  server_lifetime = p_cpu_queue^.queue_header.server_lifetime) THEN
              IF dfv$file_server_debug_enabled THEN
                log_display ($pmt$ascii_logset [pmc$job_log], ' Inactive server now active');;
              IFEND;
              dfp$clear_inhibit_access_work (server_mainframe_id, { Clear sdtx} TRUE);
            IFEND;
          IFEND;
        ELSE { treat as terminated
        IFEND;
      IFEND;
    FOREND /check_all_inhibit_mainframes/;
  PROCEND clear_inhibit_access_now_active;
?? TITLE := ' [INLINE] clear_job_server_table_lock ', EJECT ??

  PROCEDURE [INLINE] clear_job_server_table_lock;

    osp$clear_job_signature_lock (dfv$job_server_table_lock);
  PROCEND clear_job_server_table_lock;
?? TITLE := ' create_job_server_entry ', EJECT ??

  PROCEDURE create_job_server_entry
    (    server_mainframe_id: pmt$binary_mainframe_id;
         client_job_id: dft$client_job_id;
         server_lifetime: dft$server_lifetime;
         job_table_entry_exists: boolean;
         family_access_kind: dft$family_access_kinds;
     VAR job_server_table_index {input output} : dft$job_server_table_index);

    VAR
      actual: integer,
      ignore_status: ost$status;

    IF NOT job_table_entry_exists THEN
      get_free_job_server_table_entry (job_server_table_index);
    IFEND;

    pmp$convert_binary_mainframe_id (server_mainframe_id,
          dfv$p_job_server_table^ [job_server_table_index].server_mainframe, ignore_status);
    dfv$p_job_server_table^ [job_server_table_index].server_mainframe_id := server_mainframe_id;
    dfv$p_job_server_table^ [job_server_table_index].client_job_id := client_job_id;
    dfv$p_job_server_table^ [job_server_table_index].server_lifetime := server_lifetime;
    dfv$p_job_server_table^ [job_server_table_index].family_access_kind := family_access_kind;
    dfv$p_job_server_table^ [job_server_table_index].force_server_reconnection := FALSE;
    dfv$p_job_server_table^ [job_server_table_index].change_validation_info := FALSE;
    #SPOIL (dfv$p_job_server_table^ [job_server_table_index]);

    IF NOT job_table_entry_exists THEN
      osp$increment_locked_variable (dfv$job_server_count, dfv$job_server_count, actual);
    IFEND;
  PROCEND create_job_server_entry;

?? TITLE := 'end_job_recovery ', EJECT ??
  PROCEDURE end_job_recovery
    (    server_mainframe: pmt$mainframe_id;
         p_job_server_table_entry: ^dft$job_server_table_entry;
         server_lifetime: dft$lifetime;
     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 end_job_recovery;

    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
      local_status: ost$status,
      p_end_job_recovery_params: ^dft$end_job_recovery,
      p_receive_data: dft$p_receive_data,
      p_receive_parameters: dft$p_receive_parameters,
      p_send_data: dft$p_send_data,
      p_send_parameters: dft$p_send_parameters,
      queue_entry_location: dft$rpc_queue_entry_location,
      server_location: dft$server_location;

    status.normal := TRUE;
    IF dfv$file_server_debug_enabled THEN
      log_display ($pmt$ascii_logset [pmc$job_log], ' end_job_recovery ');
    IFEND;

    server_location.server_location_selector := dfc$mainframe_id;
    server_location.server_mainframe := server_mainframe;
    dfp$begin_ch_remote_proc_call (server_location, {send_if_deactivated=} TRUE, queue_entry_location,
          p_send_parameters, p_send_data, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    NEXT p_end_job_recovery_params IN p_send_parameters;
    p_end_job_recovery_params^.client_job_id := p_job_server_table_entry^.client_job_id;
    p_end_job_recovery_params^.server_lifetime := server_lifetime;
    dfp$send_remote_procedure_call (queue_entry_location, dfc$complete_job_recovery,
          #SIZE (p_end_job_recovery_params^), 0, p_receive_parameters, p_receive_data, status);
    dfp$end_ch_remote_proc_call (queue_entry_location, local_status);
    IF status.normal OR (status.condition = pfe$not_all_pfs_recovered) THEN
      IF dfv$file_server_debug_enabled THEN
        log_display ($pmt$ascii_logset [pmc$job_log], 'Completed job_recovery ');
      IFEND;
      p_job_server_table_entry^.server_lifetime := server_lifetime;
    ELSEIF (status.condition <> dfe$server_not_active) AND
          (status.condition <> dfe$server_request_terminated) THEN
      p_job_server_table_entry^.force_server_reconnection := TRUE;
    IFEND;
  PROCEND end_job_recovery;
?? TITLE := '  get_free_job_server_table_entry  ', EJECT ??

{  PURPOSE:
{    The purpose of this procedure is
{    to get a free entry in the job server table; this includes initially
{    creating the table, expanding the table if necessary, and
{    searching for an free entry.
{    The procedure contains two nested procedures,
{      get_free_job_server_table_entry and  find_free_server_table_entry
{      followed by the main body of the code.
{
  PROCEDURE get_free_job_server_table_entry
    (VAR job_server_table_index: dft$job_server_table_index);

    CONST
      dfc$expand_server_table_amount = 3,
      dfc$initial_server_table_size = 1;

    VAR
      p_new_job_server_table: dft$p_job_server_table,
      p_old_job_server_table: dft$p_job_server_table,
      space_found: boolean,
      status: ost$status;

?? NEWTITLE := '     find_free_server_table_entry  ', EJECT ??

    PROCEDURE [INLINE] find_free_server_table_entry
      (    p_job_server_table: dft$p_job_server_table;
       VAR job_server_table_index: dft$job_server_table_index;
       VAR free_found: boolean);

      free_found := (p_job_server_table <> NIL) AND (dfv$job_server_count <
            (UPPERBOUND (p_job_server_table^)));
      IF free_found THEN
        job_server_table_index := dfv$job_server_count + 1;
      IFEND;
    PROCEND find_free_server_table_entry;
?? OLDTITLE ??
?? NEWTITLE := '     transfer_old_to_new_server_tabl   ', EJECT ??

{  PURPOSE:
{    This procedure transfers an old job server table to a new one.  The
{    new table must be equal to or larger than the old table.

    PROCEDURE transfer_old_to_new_server_tabl
      (    p_old_job_server_table: dft$p_job_server_table;
           p_new_job_server_table: dft$p_job_server_table);

      VAR
        job_server_table_index: dft$job_server_table_index;

    /copy_existing_entries/
      FOR job_server_table_index := LOWERBOUND (p_old_job_server_table^)
            TO UPPERBOUND (p_old_job_server_table^) DO
        p_new_job_server_table^ [job_server_table_index] := p_old_job_server_table^ [job_server_table_index];
      FOREND /copy_existing_entries/;

    PROCEND transfer_old_to_new_server_tabl;
?? OLDTITLE ??
?? EJECT ??
{ This is the main body of the code for procedure get_free_job_server_table_entry

    space_found := FALSE;
    IF dfv$p_job_server_table = NIL THEN
      ALLOCATE dfv$p_job_server_table: [1 .. dfc$initial_server_table_size] IN osv$job_pageable_heap^;
      find_free_server_table_entry (dfv$p_job_server_table, job_server_table_index, space_found);
    ELSE
      find_free_server_table_entry (dfv$p_job_server_table, job_server_table_index, space_found);
      IF NOT space_found THEN {expand the old table }
        ALLOCATE p_new_job_server_table: [1 .. (UPPERBOUND (dfv$p_job_server_table^) +
              dfc$expand_server_table_amount)] IN osv$job_pageable_heap^;
        transfer_old_to_new_server_tabl (dfv$p_job_server_table, p_new_job_server_table);
        p_old_job_server_table := dfv$p_job_server_table;
        dfv$p_job_server_table := p_new_job_server_table;
        FREE p_old_job_server_table IN osv$job_pageable_heap^;
        find_free_server_table_entry (dfv$p_job_server_table, job_server_table_index, space_found);
      IFEND;
    IFEND;
  PROCEND get_free_job_server_table_entry;
?? TITLE := ' inform_server_of_client_job', EJECT ??

  PROCEDURE inform_server_of_client_job
    (    queue_entry_location: dft$rpc_queue_entry_location;
         queue_entry_loc_int: dft$rpc_queue_entry_loc_int;
         users_parameter_size: dft$send_parameter_size;
         forced_reconnection: boolean;
         server_lifetime: dft$lifetime;
     VAR client_job_id: dft$client_job_id;
     VAR family_access_kind: dft$family_access_kinds;
     VAR status: ost$status);

    VAR
      p_receive_data: dft$p_receive_data,
      p_receive_from_server_params: dft$p_receive_parameters,
      p_saved_parameters: ^SEQ ( * ),
      p_send_to_server_params: dft$p_send_parameters,
      parameters_length: dft$send_parameter_size;


    dfp$extract_send_parameters (queue_entry_loc_int, p_send_to_server_params);
    IF users_parameter_size > 0 THEN
      PUSH p_saved_parameters: [[REP users_parameter_size OF cell]];
      i#move (p_send_to_server_params, p_saved_parameters, users_parameter_size);
    IFEND;
    build_job_begin_send_buffer (queue_entry_loc_int.server_mainframe_id, server_lifetime,
          forced_reconnection, family_access_kind, p_send_to_server_params, parameters_length);
    dfp$send_remote_procedure_call (queue_entry_location, dfc$establish_client_job, parameters_length, 0,
          p_receive_from_server_params, p_receive_data, status);
    IF status.normal THEN
      parse_job_begin_receive_params (p_receive_from_server_params, client_job_id);
    IFEND;
    IF users_parameter_size > 0 THEN
      i#move (p_saved_parameters, p_send_to_server_params, users_parameter_size);
    IFEND;
  PROCEND inform_server_of_client_job;
?? TITLE := '  inform_server_of_client_job_end', EJECT ??

{
{  This procedure must handle the case of a delete_server of a server in the
{  job server table.
{

  PROCEDURE inform_server_of_client_job_end
    (    p_job_server_table_entry: ^dft$job_server_table_entry;
         job_is_leveled: boolean;
     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 inform_server_of_client_job_end;

    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
      client_job_id: dft$client_job_id,
      context: ^ost$ecp_exception_context,
      job_end_info: jmt$jl_server_job_end_info,
      local_status: ost$status,
      mainframe_found: boolean,
      p_cpu_queue: ^dft$cpu_queue,
      p_q_interface_directory_entry: ^dft$q_interface_directory_entry,
      p_queue_interface_table: ^dft$queue_interface_table,
      p_receive_data: dft$p_receive_data,
      p_receive_parameters: dft$p_receive_parameters,
      p_send_data: dft$p_send_data,
      p_send_parameters: dft$p_send_parameters,
      p_server_state: ^dft$server_state,
      parameters_size: dft$send_parameter_size,
      queue_entry_location: dft$rpc_queue_entry_location,
      queue_index: dft$queue_index,
      server_location: dft$server_location,
      server_mainframe_id: pmt$mainframe_id;

    status.normal := TRUE;
    context := NIL;

    IF job_is_leveled THEN
      jmp$get_server_job_end_info (job_end_info);
    ELSE

{Invalidate end job info by setting id of this CLIENT mainframe into server id

      pmp$get_pseudo_mainframe_id (job_end_info.server_mainframe_id);
    IFEND;
    IF p_job_server_table_entry <> NIL THEN
      server_mainframe_id := p_job_server_table_entry^.server_mainframe;
      client_job_id := p_job_server_table_entry^.client_job_id;
    ELSE

{    It is assumed that since there is no job server table entry then the
{    job must be being leveled otherwise this procedure would not have
{    been called.

      pmp$convert_binary_mainframe_id (job_end_info.server_mainframe_id, server_mainframe_id, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
    IFEND;

    dfp$find_mainframe_id (server_mainframe_id, {server_to_client} FALSE, mainframe_found,
          p_queue_interface_table, p_cpu_queue, queue_index, p_q_interface_directory_entry);
    IF NOT mainframe_found THEN
      osp$set_status_abnormal (dfc$file_server_id, dfe$server_has_terminated, '', status);
      RETURN;
    IFEND;
    IF p_job_server_table_entry <> NIL THEN
      IF p_job_server_table_entry^.server_lifetime <> p_cpu_queue^.queue_header.server_lifetime THEN
        osp$set_status_abnormal (dfc$file_server_id, dfe$server_has_terminated, '', status);
        RETURN;
      IFEND;
    IFEND;

    server_location.server_location_selector := dfc$mainframe_id;
    server_location.server_mainframe := server_mainframe_id;
    p_server_state := ^p_cpu_queue^.queue_header.partner_status.server_state;

  /wait_for_active/
    REPEAT
      dfp$begin_ch_remote_proc_call (server_location, {send_if_deactivated} TRUE, queue_entry_location,
            p_send_parameters, p_send_data, status);
      IF osp$file_access_condition (status) THEN
        #SPOIL (p_server_state^);
        CASE p_server_state^ OF
        = dfc$active =
          CYCLE /wait_for_active/;
        = dfc$terminated =
          osp$set_status_abnormal (dfc$file_server_id, dfe$server_has_terminated, '', status);
          RETURN;
        = dfc$deactivated, dfc$inactive =
          ;
        = dfc$awaiting_recovery =
          { Allow the activation to remove this job
          RETURN;
        ELSE
        CASEND;
        IF context = NIL THEN
          PUSH context;
          context^ := osv$initial_exception_context;
          context^.wait_time := job_end_notification_timeout;
        IFEND;
        context^.condition_status := status;
        osp$enforce_exception_policies (context^);
        status := context^.condition_status;
      IFEND;
    UNTIL status.normal OR (NOT osp$file_access_condition (status)) OR (NOT context^.wait);
    IF status.normal THEN
      build_job_end_send_buffer (p_job_server_table_entry <> NIL, client_job_id, job_is_leveled, job_end_info,
            p_send_parameters, parameters_size);

      dfp$send_remote_procedure_call (queue_entry_location, dfc$delete_client_job, parameters_size, 0,
            p_receive_parameters, p_receive_data, status);
      dfp$end_ch_remote_proc_call (queue_entry_location, local_status);
      IF (NOT status.normal) AND (status.condition = dfe$server_not_active) THEN
        status.normal := TRUE;
      IFEND;
    IFEND;
  PROCEND inform_server_of_client_job_end;

?? TITLE := ' [INLINE] locate_jobs_server_mainframe  ', EJECT ??

  PROCEDURE [INLINE] locate_jobs_server_mainframe
    (    server_mainframe_id: pmt$binary_mainframe_id;
     VAR server_mainframe_found: boolean;
     VAR job_server_table_index: dft$job_server_table_index);


  /search_for_server_mainframe/
    FOR job_server_table_index := 1 TO dfv$job_server_count DO
      IF (dfv$p_job_server_table^ [job_server_table_index].server_mainframe_id = server_mainframe_id) THEN
        server_mainframe_found := TRUE;
        RETURN;
      IFEND;
    FOREND /search_for_server_mainframe/;
    server_mainframe_found := FALSE;
  PROCEND locate_jobs_server_mainframe;
?? TITLE := '  [INLINE] parse_job_begin_receive_params ', EJECT ??

  PROCEDURE [INLINE] parse_job_begin_receive_params
    (VAR p_receive_parameters: dft$p_receive_parameters;
     VAR client_job_id: dft$client_job_id);

    VAR
      p_client_job_id: ^dft$client_job_id;

    NEXT p_client_job_id IN p_receive_parameters;
    client_job_id := p_client_job_id^;
  PROCEND parse_job_begin_receive_params;
?? TITLE := ' [INLINE] set_job_server_table_lock  ', EJECT ??

  PROCEDURE [INLINE] set_job_server_table_lock;

    osp$set_job_signature_lock (dfv$job_server_table_lock);
  PROCEND set_job_server_table_lock;
?? TITLE := ' start_job_recovery', EJECT ??
  PROCEDURE start_job_recovery
    (    server_mainframe: pmt$mainframe_id;
         p_job_server_table_entry: ^dft$job_server_table_entry;
     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 start_job_recovery;

    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
      global_task_id: ost$global_task_id,
      local_status: ost$status,
      p_start_job_recovery_out: ^dft$start_job_recovery_out,
      p_start_job_recovery_params: ^dft$start_job_recovery_in,
      p_receive_data: dft$p_receive_data,
      p_receive_parameters: dft$p_receive_parameters,
      p_send_data: dft$p_send_data,
      p_send_parameters: dft$p_send_parameters,
      user_supplied_name: jmt$user_supplied_name,
      queue_entry_location: dft$rpc_queue_entry_location,
      server_location: dft$server_location;

    status.normal := TRUE;
    IF dfv$file_server_debug_enabled THEN
      log_display ($pmt$ascii_logset [pmc$job_log], ' Start_job_recovery ');
      pmp$get_executing_task_gtid (global_task_id);
      log_display_integer ($pmt$ascii_logset [pmc$job_log],' global_task_id.index ', global_task_id.index);
      log_display_integer ($pmt$ascii_logset [pmc$job_log],' global_task_id.seqno ', global_task_id.seqno);
    IFEND;

    server_location.server_location_selector := dfc$mainframe_id;
    server_location.server_mainframe := server_mainframe;
    dfp$begin_ch_remote_proc_call (server_location, {send_if_deactivated=} TRUE, queue_entry_location,
          p_send_parameters, p_send_data, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    NEXT p_start_job_recovery_params IN p_send_parameters;
    pmp$get_job_names (user_supplied_name, p_start_job_recovery_params^.system_supplied_job_name, status);
    IF NOT status.normal THEN
      dfp$end_ch_remote_proc_call (queue_entry_location, local_status);
      RETURN;
    IFEND;

    dfp$send_remote_procedure_call (queue_entry_location, dfc$initiate_job_recovery,
          #SIZE (p_start_job_recovery_params^), 0, p_receive_parameters, p_receive_data, status);
    IF status.normal THEN
      NEXT p_start_job_recovery_out IN p_receive_parameters;
      p_job_server_table_entry^.client_job_id := p_start_job_recovery_out^.client_job_id;
    IFEND;
    dfp$end_ch_remote_proc_call (queue_entry_location, local_status);

  PROCEND start_job_recovery;

MODEND dfm$job_server_manager;
