?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE : Job Leveler Management Ring 3' ??
MODULE jmm$queue_file_leveler_manager;

{ PURPOSE:
{   This module contains the interfaces responsible for the validation and
{   processing of requests made by the job leveler task.
{
{ DESIGN:
{   The procedures in this module execute in ring 3 and are callable from ring 6.
{  They are used solely to support the job leveler task and its operations.
{
{ NOTES:
{   These ring 3 routines validate that the caller is the job leveler task.  As
{   a result, it is assumed that the parameter values passed to the request are valid
{   and need not be copied or protected further if the caller is the expected caller.
{   If the caller of an interface in this module is not the job leveler task, an
{   access violation is forced and the task is aborted.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dfc$server_mainframes_catalog
*copyc dft$rpc_parameters
*copyc jmc$job_management_id
*copyc jmc$system_family
*copyc jme$queued_file_conditions
*copyc jmt$jl_assigned_job_list
*copyc jmt$jl_job_class_data
*copyc jmt$jl_job_class_priorities
*copyc jmt$jl_leveler_server_request
*copyc jmt$jl_missing_job_list
*copyc jmt$jl_restart_file_version
*copyc jmt$jl_scheduling_data
*copyc jmt$jl_server_job_end_info
*copyc jmt$jl_server_job_list
*copyc jmt$jl_server_job_priorities
*copyc jmt$jl_unassigned_job_list
*copyc jmt$job_category_set
*copyc jmt$job_count_range
*copyc jmt$service_interval
*copyc jmt$valid_mainframe_set
*copyc oss$job_paged_literal
*copyc ost$global_task_id
*copyc ost$status
*copyc pmt$binary_mainframe_id
?? POP ??
*copyc amp$get_segment_pointer
*copyc dfp$get_partner_mainframes
*copyc dfp$send_remote_procedure_call
*copyc fsp$build_file_ref_from_elems
*copyc fsp$close_file
*copyc fsp$open_file
*copyc i#current_sequence_position
*copyc i#move
*copyc jmp$change_input_attributes
*copyc jmp$get_recovery_restart_file
*copyc jmp$rebuild_input_queue
*copyc jmp$system_job
*copyc mmp$create_scratch_segment
*copyc mmp$delete_scratch_segment
*copyc osp$disestablish_cond_handler
*copyc osp$establish_block_exit_hndlr
*copyc osp$force_access_violation
*copyc osp$generate_log_message
*copyc osp$set_status_condition
*copyc osp$set_status_from_condition
*copyc osp$system_error
*copyc osp$verify_system_privilege
*copyc pfp$begin_system_authority
*copyc pfp$end_system_authority
*copyc pfp$purge
*copyc pmp$convert_binary_mainframe_id
*copyc pmp$get_pseudo_mainframe_id
*copyc pmp$get_executing_task_gtid
*copyc pmp$log_ascii
*copyc pmp$long_term_wait
*copyc qfp$assign_jobs_to_client
*copyc qfp$assign_server_jobs
*copyc qfp$clear_server_job_classes
*copyc qfp$determine_needed_priorities
*copyc qfp$determine_need_for_jobs
*copyc qfp$discard_server_jobs
*copyc qfp$get_server_jobs
*copyc qfp$ready_job_leveler
*copyc qfp$register_job_leveler
*copyc qfp$set_leveler_ready
*copyc qfp$unassign_client_jobs
*copyc qfp$unassign_server_jobs
*copyc qfp$update_server_priorities
*copyc qfp$verify_client_assigned_jobs
*copyc qfp$verify_inactive_server
*copyc qfp$wait_for_leveler_deactivate
*copyc jmv$executing_within_system_job
*copyc jmv$jcb
*copyc jmv$job_scheduler_table
*copyc jmv$kjl_p
*copyc jmv$kjlx_p
*copyc jmv$known_job_list
*copyc jmv$leveler_profile_loading
*copyc qfv$leveler_readied
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

{ The maximum_assigned_job_list_size is the number of jobs that the assign request
{ will allow to be assigned at one time.  The physical limitation on the number is
{ defined to be the size of sequence that can be transferred back to the client
{ divided by the size of an assigned job list entry.  The number chosen here is
{ a practical limit, versus a physical limit.

  CONST
    maximum_assigned_job_list_size = 1000;

  VAR
    null_global_task_id: [STATIC, READ, oss$job_paged_literal] ost$global_task_id := [0, 0];

?? OLDTITLE ??
?? NEWTITLE := 'log_recovery_message', EJECT ??

{ The purpose of this procedure is to log recovery related information to the
{ system log and job log.

  PROCEDURE log_recovery_message
    (    error_message: string ( * );
         bad_status: ost$status);

    VAR
      logset: pmt$ascii_logset,
      log_origin: pmt$log_msg_origin,
      ignore_status: ost$status;

    ignore_status.normal := TRUE;
    log_origin := pmc$msg_origin_system;
    logset := $pmt$ascii_logset [pmc$system_log, pmc$job_log];

    IF error_message <> '' THEN
      pmp$log_ascii (error_message, logset, log_origin, ignore_status);
    IFEND;

    IF NOT bad_status.normal THEN
      osp$generate_log_message (logset, bad_status, ignore_status);
    IFEND;
  PROCEND log_recovery_message;
?? OLDTITLE ??
?? NEWTITLE := 'verify_client_assigned_jobs', EJECT ??

{ PURPOSE:
{    This request will verify that the jobs assigned to a client mainframe match the
{ list of jobs the server believes are assigned to the client.  For any job that the
{ server believes is initiated on the client, that the client does not know about, the
{ job_recovery and job_abort dispositions will be used to determine the fate of the job.
{
{ DESIGN:
{   This request is called only on the server mainframe.

  PROCEDURE verify_client_assigned_jobs
    (    client_mainframe_id: pmt$binary_mainframe_id;
         server_job_list_p: ^jmt$jl_server_job_list;
         restart_job_list_p: ^jmt$jl_restart_job_list);

?? NEWTITLE := 'handle_block_exit', EJECT ??

{ PURPOSE:
{   The purpose of this procedure is to deal with block exit conditions that
{   arise while system_authority is in effect.

    PROCEDURE handle_block_exit
      (    condition: pmt$condition;
           condition_information_p: ^pmt$condition_information;
           sfsa_p: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      pfp$end_system_authority;
    PROCEND handle_block_exit;
?? OLDTITLE, EJECT ??

    VAR
      cycle_selector: pft$cycle_selector,
      ignore_status: ost$status,
      input_attribute_changes_p: ^jmt$input_attribute_changes,
      input_file_name: jmt$name,
      local_status: ost$status,
      missing_job_count: jmt$job_count_range,
      missing_job_list_index: jmt$job_count_range,
      missing_job_list_p: ^jmt$jl_missing_job_list,
      password: pft$password,
      path_p: ^pft$path,
      restart_job_index: jmt$job_count_range;

    PUSH missing_job_list_p: [1 .. jmc$maximum_job_count];
    qfp$verify_client_assigned_jobs (client_mainframe_id, server_job_list_p, missing_job_list_p,
          missing_job_count);

    IF missing_job_count > 0 THEN
      input_file_name.kind := jmc$system_supplied_name;
      PUSH input_attribute_changes_p: [1 .. 1];
      input_attribute_changes_p^ [1].key := jmc$null_attribute;

      PUSH path_p: [1 .. 4];
      path_p^ [1] := osc$null_name;
      path_p^ [2] := jmc$system_user;
      path_p^ [3] := jmc$job_input_catalog;
      path_p^ [4] := osc$null_name;
      cycle_selector.cycle_option := pfc$specific_cycle;
      cycle_selector.cycle_number := 1;
      password := osc$null_name;
      osp$establish_block_exit_hndlr (^handle_block_exit);
      pfp$begin_system_authority;

    /search_for_missing_jobs/
      FOR missing_job_list_index := 1 TO missing_job_count DO
        local_status.normal := TRUE;

{ Search for the job in the restart job list.  If the job is in the restart list
{ it may need to be recovered.  If the job is not in the restart job list, its
{ recovery or abort disposition was set to terminate or the job had completed.

        IF restart_job_list_p <> NIL THEN

        /search_restart_job_list/
          FOR restart_job_index := 1 TO UPPERBOUND (restart_job_list_p^) DO
            IF missing_job_list_p^ [missing_job_list_index].system_job_name =
                  restart_job_list_p^ [restart_job_index].system_job_name THEN
              jmp$rebuild_input_queue (missing_job_list_p^ [missing_job_list_index].system_job_name,
                    missing_job_list_p^ [missing_job_list_index].login_family, jmc$job_input_catalog,
                    restart_job_list_p^ [restart_job_index].recover_using_abort_disposition,
                    {ignore_client_initiated_jobs} TRUE, {job_deferred_by_operator} FALSE, local_status);
              IF local_status.normal THEN
                input_file_name.system_supplied_name := missing_job_list_p^ [missing_job_list_index].
                      system_job_name;
                osp$set_status_abnormal (jmc$job_management_id, jme$input_was_recovered,
                      missing_job_list_p^ [missing_job_list_index].system_job_name, local_status);
                log_recovery_message ('', local_status);

{ Passing the null attribute versus NIL for the input attribute changes forces change input attributes
{ to recategorize and prevalidate the job again.

                jmp$change_input_attributes (input_file_name, input_attribute_changes_p, local_status);
                IF NOT local_status.normal THEN

{ Log the error.  After file server recovery completes manas can be used to
{ resubmit the jobs.  They are in the unassigned job class.

                  log_recovery_message (missing_job_list_p^ [missing_job_list_index].system_job_name,
                        local_status);
                IFEND;
                CYCLE /search_for_missing_jobs/;
              ELSE
                EXIT /search_restart_job_list/;
              IFEND;
            IFEND;
          FOREND /search_restart_job_list/;
        IFEND;

{ The input file was not recovered - purge it.

        osp$set_status_abnormal (jmc$job_management_id, jme$input_was_not_recovered,
              missing_job_list_p^ [missing_job_list_index].system_job_name, ignore_status);
        log_recovery_message ('', ignore_status);
        log_recovery_message ('', local_status);
        path_p^ [1] := missing_job_list_p^ [missing_job_list_index].login_family;
        path_p^ [4] := missing_job_list_p^ [missing_job_list_index].system_job_name;

        pfp$purge (path_p^, cycle_selector, password, ignore_status);

      FOREND /search_for_missing_jobs/;
      pfp$end_system_authority;
      osp$disestablish_cond_handler;
    IFEND;
  PROCEND verify_client_assigned_jobs;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$assign_server_jobs', EJECT ??
*copy jmh$assign_server_jobs

  PROCEDURE [XDCL, #GATE] jmp$assign_server_jobs
    (    server_mainframe_id: pmt$binary_mainframe_id;
         assigned_job_list_p: { output } ^jmt$jl_assigned_job_list;
     VAR number_of_jobs_assigned: jmt$job_count_range;
     VAR status: ost$status);

    jmp$verify_job_leveler;
    qfp$assign_server_jobs (server_mainframe_id, assigned_job_list_p, number_of_jobs_assigned, status);
  PROCEND jmp$assign_server_jobs;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$call_job_leveler_server', EJECT ??
*copy jmh$call_job_leveler_server

  PROCEDURE [XDCL, #GATE] jmp$call_job_leveler_server
    (    server_mainframe_id: pmt$binary_mainframe_id;
     VAR leveler_server_request { input, output } : jmt$jl_leveler_server_request;
     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 jmp$call_job_leveler_server;

    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_size: dft$send_data_size,
      ignore_status: ost$status,
      local_assigned_job_list_p: ^jmt$jl_assigned_job_list,
      local_assigned_job_count_p: ^jmt$job_count_range,
      local_categories_p: ^jmt$job_category_set,
      local_client_mainframe_id_p: ^pmt$binary_mainframe_id,
      local_job_class_data_p: ^jmt$jl_job_class_data,
      local_job_class_priorities_p: ^jmt$jl_job_class_priorities,
      local_job_leveling_enabled_p: ^boolean,
      local_profile_id_p: ^ost$name,
      local_profile_mismatch_p: ^boolean,
      local_request_kind_p: ^jmt$jl_request_kind,
      local_restart_job_count_p: ^jmt$job_count_range,
      local_restart_job_list_p: ^jmt$jl_restart_job_list,
      local_server_job_count_p: ^jmt$job_count_range,
      local_server_job_list_p: ^jmt$jl_server_job_list,
      local_server_job_priorities_p: ^jmt$jl_server_job_priorities,
      local_unassigned_job_list_p: ^jmt$jl_unassigned_job_list,
      local_unassigned_job_count_p: ^jmt$job_count_range,
      queue_entry_location: dft$rpc_queue_entry_location,
      parameter_size: dft$send_parameter_size,
      receive_from_server_data_p: dft$p_receive_data,
      receive_from_server_params_p: dft$p_receive_parameters,
      send_to_server_data_p: dft$p_send_data,
      send_to_server_parameters_p: dft$p_send_parameters,
      server_location: dft$server_location;

    IF (leveler_server_request.request_kind = jmc$jl_ready_levelers_request) THEN
      osp$verify_system_privilege;
    ELSEIF (leveler_server_request.request_kind = jmc$jl_signon_request) THEN
      IF NOT jmp$system_job () THEN
        osp$force_access_violation;
      IFEND;
    ELSE
      jmp$verify_job_leveler;
    IFEND;

{ Need to convert the server's mainframe id to a queue entry location.

    server_location.server_location_selector := dfc$mainframe_id;
    pmp$convert_binary_mainframe_id (server_mainframe_id, server_location.server_mainframe,
          { ignore } status);
    dfp$begin_ch_remote_proc_call (server_location, { allowed_when_server_deactivated } TRUE,
          queue_entry_location, send_to_server_parameters_p, send_to_server_data_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Build the send to server parameters and data to send to the server mainframe.
{ RPC sequences are already reset.

    NEXT local_client_mainframe_id_p IN send_to_server_parameters_p;
    pmp$get_pseudo_mainframe_id (local_client_mainframe_id_p^);
    NEXT local_request_kind_p IN send_to_server_parameters_p;
    local_request_kind_p^ := leveler_server_request.request_kind;
    CASE leveler_server_request.request_kind OF
    = jmc$jl_signon_request =
      NEXT local_server_job_count_p IN send_to_server_data_p;
      IF leveler_server_request.signon_request.server_job_list_p = NIL THEN
        local_server_job_count_p^ := 0;
      ELSE
        local_server_job_count_p^ := UPPERBOUND (leveler_server_request.signon_request.server_job_list_p^);
        NEXT local_server_job_list_p: [1 .. local_server_job_count_p^] IN send_to_server_data_p;
        local_server_job_list_p^ := leveler_server_request.signon_request.server_job_list_p^;
      IFEND;
      NEXT local_restart_job_count_p IN send_to_server_data_p;
      IF leveler_server_request.signon_request.restart_job_list_p = NIL THEN
        local_restart_job_count_p^ := 0;
      ELSE
        local_restart_job_count_p^ := UPPERBOUND (leveler_server_request.signon_request.restart_job_list_p^);
        NEXT local_restart_job_list_p: [1 .. local_restart_job_count_p^] IN send_to_server_data_p;
        local_restart_job_list_p^ := leveler_server_request.signon_request.restart_job_list_p^;
      IFEND;

    = jmc$jl_normal_request =
      NEXT local_profile_id_p IN send_to_server_parameters_p;
      local_profile_id_p^ := leveler_server_request.normal_request.active_profile_id;
      NEXT local_categories_p IN send_to_server_parameters_p;
      local_categories_p^ := leveler_server_request.normal_request.initiation_required_categories;
      NEXT local_categories_p IN send_to_server_parameters_p;
      local_categories_p^ := leveler_server_request.normal_request.initiation_excluded_categories;
      NEXT local_job_class_data_p IN send_to_server_parameters_p;
      local_job_class_data_p^ := leveler_server_request.normal_request.leveler_job_class_data;
      NEXT local_job_class_priorities_p IN send_to_server_parameters_p;
      local_job_class_priorities_p^ := leveler_server_request.normal_request.job_class_priorities;
      NEXT local_unassigned_job_count_p IN send_to_server_data_p;
      IF leveler_server_request.normal_request.unassigned_job_list_p = NIL THEN
        local_unassigned_job_count_p^ := 0;
      ELSE
        local_unassigned_job_count_p^ := UPPERBOUND (leveler_server_request.normal_request.
              unassigned_job_list_p^);
        NEXT local_unassigned_job_list_p: [1 .. local_unassigned_job_count_p^] IN send_to_server_data_p;
        local_unassigned_job_list_p^ := leveler_server_request.normal_request.unassigned_job_list_p^;
      IFEND;

    = jmc$jl_unassign_jobs_request =
      NEXT local_unassigned_job_count_p IN send_to_server_data_p;
      IF leveler_server_request.unassign_jobs_request.unassigned_job_list_p = NIL THEN
        local_unassigned_job_count_p^ := 0;
      ELSE
        local_unassigned_job_count_p^ := UPPERBOUND (leveler_server_request.unassign_jobs_request.
              unassigned_job_list_p^);
        NEXT local_unassigned_job_list_p: [1 .. local_unassigned_job_count_p^] IN send_to_server_data_p;
        local_unassigned_job_list_p^ := leveler_server_request.unassign_jobs_request.unassigned_job_list_p^;
      IFEND;

    = jmc$jl_signoff_request =
      NEXT local_unassigned_job_count_p IN send_to_server_data_p;
      IF leveler_server_request.signoff_request.unassigned_job_list_p = NIL THEN
        local_unassigned_job_count_p^ := 0;
      ELSE
        local_unassigned_job_count_p^ := UPPERBOUND (leveler_server_request.signoff_request.
              unassigned_job_list_p^);
        NEXT local_unassigned_job_list_p: [1 .. local_unassigned_job_count_p^] IN send_to_server_data_p;
        local_unassigned_job_list_p^ := leveler_server_request.signoff_request.unassigned_job_list_p^;
      IFEND;

    = jmc$jl_ready_levelers_request =
      ; { Nothing.

    ELSE
      dfp$end_ch_remote_proc_call (queue_entry_location, ignore_status);
      osp$system_error ('Unknown Job Leveler request.', NIL);
    CASEND;
    parameter_size := i#current_sequence_position (send_to_server_parameters_p);
    data_size := i#current_sequence_position (send_to_server_data_p);

    dfp$send_remote_procedure_call (queue_entry_location, dfc$rpc_jl_job_leveler_server, parameter_size,
          data_size, receive_from_server_params_p, receive_from_server_data_p, status);
    IF status.normal THEN
      IF leveler_server_request.request_kind = jmc$jl_normal_request THEN
        NEXT local_profile_mismatch_p IN receive_from_server_params_p;
        NEXT local_job_leveling_enabled_p IN receive_from_server_params_p;
        leveler_server_request.normal_request.profile_mismatch := local_profile_mismatch_p^;
        leveler_server_request.normal_request.job_leveling_enabled := local_job_leveling_enabled_p^;
        IF local_profile_mismatch_p^ OR (NOT local_job_leveling_enabled_p^) THEN

{ There is a profile mismatch error or leveling is disabled - for now, don't do anything.

        ELSE
          NEXT local_server_job_priorities_p IN receive_from_server_params_p;
          leveler_server_request.normal_request.server_job_priorities := local_server_job_priorities_p^;

          NEXT local_assigned_job_count_p IN receive_from_server_data_p;
          leveler_server_request.normal_request.assigned_job_count := local_assigned_job_count_p^;
          IF leveler_server_request.normal_request.assigned_job_count > 0 THEN
            NEXT local_assigned_job_list_p: [1 .. leveler_server_request.normal_request.assigned_job_count] IN
                  receive_from_server_data_p;
            i#move (local_assigned_job_list_p, leveler_server_request.normal_request.
                  assigned_job_list_p, #SIZE (jmt$jl_assigned_job) *
                  leveler_server_request.normal_request.assigned_job_count);
          IFEND;
        IFEND;
      IFEND;
      dfp$end_ch_remote_proc_call (queue_entry_location, status);
    ELSE { IF NOT status.normal THEN
      dfp$end_ch_remote_proc_call (queue_entry_location, ignore_status);
    IFEND;
  PROCEND jmp$call_job_leveler_server;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$called_by_job_leveler', EJECT ??
*copy jmh$called_by_job_leveler

  FUNCTION [XDCL] jmp$called_by_job_leveler: boolean;

    VAR
      global_task_id: ost$global_task_id;

    pmp$get_executing_task_gtid (global_task_id);
    jmp$called_by_job_leveler := jmv$known_job_list.application_table [jmc$ve_input_application_index].
          global_task_id = global_task_id;
  FUNCEND jmp$called_by_job_leveler;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$clear_server_job_classes', EJECT ??
*copy jmh$clear_server_job_classes

  PROCEDURE [XDCL, #GATE] jmp$clear_server_job_classes;

    jmp$verify_job_leveler;
    qfp$clear_server_job_classes;
  PROCEND jmp$clear_server_job_classes;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$deactivate_job_leveling', EJECT ??
*copy jmh$deactivate_job_leveling

  PROCEDURE [XDCL] jmp$deactivate_job_leveling
    (VAR status: ost$status);

    CONST
      deactivate_wait_time_sec = 60; { One minute

    VAR
      client_mainframe_count: dft$partner_mainframe_count,
      client_mainframe_index: dft$partner_mainframe_count,
      client_mainframe_list: array [1 .. dfc$maximum_partner_mainframes] of dft$partner_mainframe_entry,
      ignore_status: ost$status,
      ignore_task_executing: boolean,
      leveler_client_request: jmt$jl_leveler_server_request,
      leveler_deactivated: boolean;

    status.normal := TRUE;
    jmp$ready_job_leveler_task (ignore_task_executing);

{ Ready the job leveler tasks that are executing on any client mainframes.  Remember, levelers execute on
{ clients, not servers.

    dfp$get_partner_mainframes ({ partners_are_servers } FALSE, ^client_mainframe_list,
          client_mainframe_count);
    leveler_client_request.request_kind := jmc$jl_ready_levelers_request;
    FOR client_mainframe_index := 1 TO client_mainframe_count DO
      IF client_mainframe_list [client_mainframe_index].partner_state = dfc$active THEN
        jmp$call_job_leveler_server (client_mainframe_list [client_mainframe_index].mainframe_id,
              leveler_client_request, ignore_status);
      IFEND;
    FOREND;

    qfp$wait_for_leveler_deactivate (deactivate_wait_time_sec, leveler_deactivated);
    IF NOT leveler_deactivated THEN
      osp$set_status_condition (jme$leveler_not_responding, status);
    IFEND;
  PROCEND jmp$deactivate_job_leveling;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$determine_needed_priorities', EJECT ??
*copy jmh$determine_needed_priorities

  PROCEDURE [XDCL, #GATE] jmp$determine_needed_priorities
    (    leveler_job_class_data: jmt$jl_job_class_data;
     VAR job_class_priorities: jmt$jl_job_class_priorities);

    jmp$verify_job_leveler;
    qfp$determine_needed_priorities (leveler_job_class_data, job_class_priorities);
  PROCEND jmp$determine_needed_priorities;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$determine_need_for_jobs', EJECT ??
*copy jmh$determine_need_for_jobs

  PROCEDURE [XDCL, #GATE] jmp$determine_need_for_jobs
    (VAR leveler_job_class_data: jmt$jl_job_class_data);

    jmp$verify_job_leveler;
    qfp$determine_need_for_jobs (leveler_job_class_data);
  PROCEND jmp$determine_need_for_jobs;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$discard_server_jobs', EJECT ??
*copy jmh$discard_server_jobs

  PROCEDURE [XDCL, #GATE] jmp$discard_server_jobs
    (    server_mainframe_id: pmt$binary_mainframe_id);

    jmp$verify_job_leveler;
    qfp$discard_server_jobs (server_mainframe_id);
  PROCEND jmp$discard_server_jobs;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$get_client_scheduling_data', EJECT ??
*copy jmh$get_client_scheduling_data

  PROCEDURE [XDCL, #GATE] jmp$get_client_scheduling_data
    (VAR scheduling_data: jmt$jl_scheduling_data);

    jmp$verify_job_leveler;
    scheduling_data.job_leveling_interval := jmv$job_scheduler_table.job_leveling_interval;
    scheduling_data.profile_identification := jmv$job_scheduler_table.profile_identification;
    scheduling_data.initiation_required_categories := jmv$job_scheduler_table.initiation_required_categories;
    scheduling_data.initiation_excluded_categories := jmv$job_scheduler_table.initiation_excluded_categories;
    scheduling_data.job_leveling_enabled := jmv$job_scheduler_table.enable_job_leveling;
    scheduling_data.profile_loading_in_progress := jmv$leveler_profile_loading;
  PROCEND jmp$get_client_scheduling_data;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$get_server_job_end_info', EJECT ??
*copy jmh$get_server_job_end_info

  PROCEDURE [XDCL] jmp$get_server_job_end_info
    (VAR job_end_information: jmt$jl_server_job_end_info);

    VAR
      kjl_index: jmt$kjl_index;

    kjl_index := jmv$jcb.job_id;
    pmp$get_pseudo_mainframe_id (job_end_information.client_mainframe_id);
    job_end_information.server_mainframe_id := jmv$known_job_list.server_data.
          state_data [jmv$kjl_p^ [kjl_index].server_index].mainframe_id;
    job_end_information.system_job_name := jmv$jcb.system_name;
    job_end_information.server_kjl_index := jmv$kjl_p^ [kjl_index].server_kjl_index;
    job_end_information.job_requests_restart := jmv$kjlx_p^ [kjl_index].restart_job;

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

  FUNCTION [XDCL] jmp$job_is_being_leveled: boolean;

    jmp$job_is_being_leveled := (jmv$kjl_p^ [jmv$jcb.job_id].server_index <>
          jmc$kjl_server_this_mainframe) AND (NOT jmv$executing_within_system_job) AND
          (jmv$kjlx_p^ [jmv$jcb.job_id].job_mode = jmc$batch);
  FUNCEND jmp$job_is_being_leveled;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$job_leveler_server', EJECT ??
*copy jmh$job_leveler_server

  PROCEDURE [XDCL] jmp$job_leveler_server
    (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
      active_profile_id_p: ^ost$name,
      assigned_job_list_p: ^jmt$jl_assigned_job_list,
      assigned_job_count_p: ^jmt$job_count_range,
      client_mainframe_id_p: ^pmt$binary_mainframe_id,
      excluded_categories_p: ^jmt$job_category_set,
      ignore_leveler_executing: boolean,
      job_class_data_p: ^jmt$jl_job_class_data,
      job_class_priorities_p: ^jmt$jl_job_class_priorities,
      job_leveling_enabled_p: ^boolean,
      profile_mismatch_p: ^boolean,
      request_kind_p: ^jmt$jl_request_kind,
      required_categories_p: ^jmt$job_category_set,
      restart_job_count_p: ^jmt$job_count_range,
      restart_job_list_p: ^jmt$jl_restart_job_list,
      server_job_count_p: ^jmt$job_count_range,
      server_job_list_p: ^jmt$jl_server_job_list,
      server_job_priorities_p: ^jmt$jl_server_job_priorities,
      unassigned_job_count_p: ^jmt$job_count_range,
      unassigned_job_list_p: ^jmt$jl_unassigned_job_list;

    status.normal := TRUE;

{ RPC sequences are already reset.

    NEXT client_mainframe_id_p IN received_from_client_params_p;
    NEXT request_kind_p IN received_from_client_params_p;

    CASE request_kind_p^ OF
    = jmc$jl_signon_request =
      NEXT server_job_count_p IN received_from_client_data_p;
      IF server_job_count_p^ = 0 THEN
        server_job_list_p := NIL;
      ELSE
        NEXT server_job_list_p: [1 .. server_job_count_p^] IN received_from_client_data_p;
      IFEND;
      NEXT restart_job_count_p IN received_from_client_data_p;
      IF restart_job_count_p^ = 0 THEN
        restart_job_list_p := NIL;
      ELSE
        NEXT restart_job_list_p: [1 .. restart_job_count_p^] IN received_from_client_data_p;
      IFEND;

      verify_client_assigned_jobs (client_mainframe_id_p^, server_job_list_p, restart_job_list_p);

    = jmc$jl_normal_request =
      NEXT active_profile_id_p IN received_from_client_params_p;
      NEXT required_categories_p IN received_from_client_params_p;
      NEXT excluded_categories_p IN received_from_client_params_p;
      NEXT job_class_data_p IN received_from_client_params_p;
      NEXT job_class_priorities_p IN received_from_client_params_p;

{ Unassign the jobs in the unassigned job list.

      NEXT unassigned_job_count_p IN received_from_client_data_p;
      IF unassigned_job_count_p^ = 0 THEN
        unassigned_job_list_p := NIL;
      ELSE
        NEXT unassigned_job_list_p: [1 .. unassigned_job_count_p^] IN received_from_client_data_p;
      IFEND;

      qfp$unassign_client_jobs (client_mainframe_id_p^, unassigned_job_list_p);

{ Assign jobs as the client requested.

      NEXT profile_mismatch_p IN send_to_client_params_p;
      profile_mismatch_p^ := (active_profile_id_p^ <> jmv$job_scheduler_table.profile_identification) OR
            jmv$leveler_profile_loading;
      NEXT job_leveling_enabled_p IN send_to_client_params_p;
      job_leveling_enabled_p^ := jmv$job_scheduler_table.enable_job_leveling;

      NEXT server_job_priorities_p IN send_to_client_params_p;

      NEXT assigned_job_count_p IN send_to_client_data_p;
      IF profile_mismatch_p^ OR (NOT job_leveling_enabled_p^) THEN
        assigned_job_count_p^ := 0;
      ELSE

        NEXT assigned_job_list_p: [1 .. maximum_assigned_job_list_size] IN send_to_client_data_p;
        qfp$assign_jobs_to_client (client_mainframe_id_p^, job_class_data_p^, job_class_priorities_p^,
              required_categories_p^, excluded_categories_p^, assigned_job_list_p, assigned_job_count_p^,
              server_job_priorities_p^);
        RESET send_to_client_data_p TO assigned_job_list_p;
        IF assigned_job_count_p^ > 0 THEN
          NEXT assigned_job_list_p: [1 .. assigned_job_count_p^] IN send_to_client_data_p;
        IFEND;
      IFEND;

    = jmc$jl_unassign_jobs_request =
      NEXT unassigned_job_count_p IN received_from_client_data_p;
      IF unassigned_job_count_p^ = 0 THEN
        unassigned_job_list_p := NIL;
      ELSE
        NEXT unassigned_job_list_p: [1 .. unassigned_job_count_p^] IN received_from_client_data_p;
      IFEND;

      qfp$unassign_client_jobs (client_mainframe_id_p^, unassigned_job_list_p);

    = jmc$jl_signoff_request =
      NEXT unassigned_job_count_p IN received_from_client_data_p;
      IF unassigned_job_count_p^ = 0 THEN
        unassigned_job_list_p := NIL;
      ELSE
        NEXT unassigned_job_list_p: [1 .. unassigned_job_count_p^] IN received_from_client_data_p;
      IFEND;

      qfp$unassign_client_jobs (client_mainframe_id_p^, unassigned_job_list_p);

    = jmc$jl_ready_levelers_request =
      jmp$ready_job_leveler_task (ignore_leveler_executing);

    ELSE
      osp$system_error ('Unknown job leveler request from client.', NIL);
    CASEND;

    parameter_size := i#current_sequence_position (send_to_client_params_p);
    data_size := i#current_sequence_position (send_to_client_data_p);
  PROCEND jmp$job_leveler_server;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$leveler_wait', EJECT ??
*copy jmh$leveler_wait

  PROCEDURE [XDCL, #GATE] jmp$leveler_wait
    (    job_leveling_interval: jmt$service_interval);

    CONST
      number_of_us_in_a_ms = 1000,
      number_of_us_in_a_second = 1000000;

    VAR
      end_wait_time: jmt$clock_time,
      requested_wait_time: jmt$clock_time,
      start_wait_time: jmt$clock_time;

    jmp$verify_job_leveler;
    start_wait_time := #FREE_RUNNING_CLOCK (0);
    end_wait_time := start_wait_time + (job_leveling_interval * number_of_us_in_a_second);
    WHILE (NOT qfv$leveler_readied) AND (start_wait_time < end_wait_time) DO
      requested_wait_time := (end_wait_time - start_wait_time) DIV number_of_us_in_a_ms;
      pmp$long_term_wait (requested_wait_time, requested_wait_time);
      start_wait_time := #FREE_RUNNING_CLOCK (0);
    WHILEND;
    qfp$set_leveler_ready (FALSE);
  PROCEND jmp$leveler_wait;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$reconcile_leveled_jobs', EJECT ??
*copy jmh$reconcile_leveled_jobs

  PROCEDURE [XDCL] jmp$reconcile_leveled_jobs
    (    server_mainframe_id: pmt$binary_mainframe_id;
     VAR status: ost$status);

    VAR
      attachment_options_p: ^fst$attachment_options,
      cycle_selector: pft$cycle_selector,
      ignore_status: ost$status,
      leveler_server_request: jmt$jl_leveler_server_request,
      local_status: ost$status,
      restart_file_version_p: ^jmt$jl_restart_file_version,
      restart_job_count_p: ^jmt$job_count_range,
      restart_job_sequence_p: ^SEQ ( * ),
      scratch_segment_pointer: amt$segment_pointer,
      scratch_sequence_p: ^SEQ ( * ),
      segment_pointer: amt$segment_pointer,
      server_ascii_mainframe_id: pmt$mainframe_id,
      server_file_exists: boolean,
      server_file_identifier: amt$file_identifier,
      server_file_path: fst$path,
      server_file_path_p: ^pft$path,
      server_job_count: jmt$job_count_range;

    status.normal := TRUE;
    pmp$convert_binary_mainframe_id (server_mainframe_id, server_ascii_mainframe_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_random, scratch_segment_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    scratch_sequence_p := scratch_segment_pointer.sequence_pointer;
    RESET scratch_sequence_p;

    leveler_server_request.request_kind := jmc$jl_signon_request;
    NEXT leveler_server_request.signon_request.server_job_list_p: [1 .. jmc$maximum_job_count] IN
          scratch_sequence_p;
    qfp$get_server_jobs (server_mainframe_id, leveler_server_request.signon_request.server_job_list_p,
          server_job_count);
    RESET scratch_sequence_p TO leveler_server_request.signon_request.server_job_list_p;
    IF server_job_count = 0 THEN
      leveler_server_request.signon_request.server_job_list_p := NIL;
    ELSE
      NEXT leveler_server_request.signon_request.server_job_list_p: [1 .. server_job_count] IN
            scratch_sequence_p;
    IFEND;

{ Open the server file and retrieve the restart job list.
{ If the file cannot be opened for read access assume that it does not exist.

    PUSH server_file_path_p: [1 .. 4];
    server_file_path_p^ [1] := jmc$system_family;
    server_file_path_p^ [2] := jmc$system_user;
    server_file_path_p^ [3] := dfc$server_mainframes_catalog;
    jmp$get_recovery_restart_file (server_ascii_mainframe_id, server_file_path_p^ [4]);

    fsp$build_file_ref_from_elems (server_file_path_p, server_file_path, ignore_status);
    PUSH attachment_options_p: [1 .. 2];
    attachment_options_p^ [1].selector := fsc$access_and_share_modes;
    attachment_options_p^ [1].access_modes.selector := fsc$specific_access_modes;
    attachment_options_p^ [1].access_modes.value := $fst$file_access_options [fsc$read];
    attachment_options_p^ [1].share_modes.selector := fsc$specific_share_modes;
    attachment_options_p^ [1].share_modes.value := $fst$file_access_options [];
    attachment_options_p^ [2].selector := fsc$open_share_modes;
    attachment_options_p^ [2].open_share_modes := $fst$file_access_options [];
    fsp$open_file (server_file_path, amc$segment, attachment_options_p, NIL, NIL, NIL, NIL,
          server_file_identifier, local_status);
    IF local_status.normal THEN
      server_file_exists := TRUE;
      amp$get_segment_pointer (server_file_identifier, amc$sequence_pointer, segment_pointer, local_status);
      IF local_status.normal THEN
        restart_job_sequence_p := segment_pointer.sequence_pointer;
        RESET restart_job_sequence_p;
        NEXT restart_file_version_p IN restart_job_sequence_p;
        IF restart_file_version_p^ = jmc$jl_rfv_version_1 THEN
          NEXT restart_job_count_p IN restart_job_sequence_p;
          IF restart_job_count_p = NIL THEN
            leveler_server_request.signon_request.restart_job_list_p := NIL;
          ELSE
            IF restart_job_count_p^ = 0 THEN
              leveler_server_request.signon_request.restart_job_list_p := NIL;
            ELSE
              NEXT leveler_server_request.signon_request.restart_job_list_p: [1 .. restart_job_count_p^] IN
                    restart_job_sequence_p;
            IFEND;
          IFEND;
        ELSE
          leveler_server_request.signon_request.restart_job_list_p := NIL;
        IFEND;
      ELSE
        leveler_server_request.signon_request.restart_job_list_p := NIL;
      IFEND;

    ELSE { couldn't open the file
      server_file_exists := FALSE;
      leveler_server_request.signon_request.restart_job_list_p := NIL;
    IFEND;

    jmp$call_job_leveler_server (server_mainframe_id, leveler_server_request, status);

    IF server_file_exists THEN
      fsp$close_file (server_file_identifier, ignore_status);
      IF status.normal THEN
        cycle_selector.cycle_option := pfc$specific_cycle;
        cycle_selector.cycle_number := 1;
        pfp$purge (server_file_path_p^, cycle_selector, { password } osc$null_name, status);
      IFEND;
    IFEND;
    mmp$delete_scratch_segment (scratch_segment_pointer, ignore_status);
  PROCEND jmp$reconcile_leveled_jobs;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$ready_job_leveler_task', EJECT ??
*copy jmh$ready_job_leveler_task

  PROCEDURE [XDCL] jmp$ready_job_leveler_task
    (VAR task_executing: boolean);

    task_executing := jmv$known_job_list.application_table [jmc$ve_input_application_index].global_task_id <>
          null_global_task_id;
    IF task_executing THEN
      qfp$ready_job_leveler;
    IFEND;
  PROCEND jmp$ready_job_leveler_task;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$register_job_leveler', EJECT ??
*copy jmh$register_job_leveler

  PROCEDURE [XDCL, #GATE] jmp$register_job_leveler;

    IF NOT jmp$system_job () THEN
      osp$force_access_violation;
    IFEND;

    qfp$register_job_leveler;
  PROCEND jmp$register_job_leveler;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$unassign_server_jobs', EJECT ??
*copy jmh$unassign_server_jobs

  PROCEDURE [XDCL, #GATE] jmp$unassign_server_jobs
    (    server_mainframe_id: pmt$binary_mainframe_id;
         unassign_all_jobs: boolean;
         job_class_priorities: jmt$jl_job_class_priorities;
         unassigned_job_list { output } : ^jmt$jl_unassigned_job_list;
     VAR number_of_unassigned_jobs: jmt$job_count_range);

    jmp$verify_job_leveler;
    qfp$unassign_server_jobs (server_mainframe_id, unassign_all_jobs, job_class_priorities,
          unassigned_job_list, number_of_unassigned_jobs);
  PROCEND jmp$unassign_server_jobs;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$update_server_priorities', EJECT ??
*copy jmh$update_server_priorities

  PROCEDURE [XDCL, #GATE] jmp$update_server_priorities
    (    highest_server_priorities: jmt$jl_server_job_priorities);

    jmp$verify_job_leveler;
    qfp$update_server_priorities (highest_server_priorities);
  PROCEND jmp$update_server_priorities;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$verify_inactive_server', EJECT ??
*copy jmh$verify_inactive_server

  PROCEDURE [XDCL, #GATE] jmp$verify_inactive_server
    (    server_mainframe_id: pmt$binary_mainframe_id;
     VAR server_inactive: boolean);

    jmp$verify_job_leveler;
    qfp$verify_inactive_server (server_mainframe_id, server_inactive);
  PROCEND jmp$verify_inactive_server;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$verify_job_leveler', EJECT ??
*copy jmh$verify_job_leveler

  PROCEDURE [XDCL] jmp$verify_job_leveler;

    VAR
      global_task_id: ost$global_task_id;

    pmp$get_executing_task_gtid (global_task_id);
    IF jmv$known_job_list.application_table [jmc$ve_input_application_index].global_task_id <>
          global_task_id THEN
      osp$force_access_violation;
    IFEND;
  PROCEND jmp$verify_job_leveler;
?? OLDTITLE ??
MODEND jmm$queue_file_leveler_manager;
