?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Job Management : Scheduling Utility Interfaces' ??
MODULE jmm$job_scheduler_utility_r3;

{ PURPOSE:
{   This module contains the ring 3 interfaces for the MANAGE_ACTIVE_SCHEDULING
{   utility.  These interfaces manage the active scheduler tables and provide
{   miscellaneous support functions for the scheduling utility and the
{   scheduling profile.
{ DESIGN:
{   This module contains the system interfaces which enable the MANAGE_ACTIVE_
{   SCHEDULING utility to activate and install a new scheduling profile in the
{   scheduler tables, update an existing profile in the tables, and obtain the
{   contents of these tables.  It also contains an interface for system deadstart
{   which obtains the size of the scheduler tables associated with the system
{   scheduling profile.
{ NOTES:
{   Applicable documents for the MANAGE_ACTIVE_SCHEDULING utility include:
{   NOS/VE Job Scheduling Phase II ERS, DCS# A7594
{   NOS/VE Job Scheduling Phase IIA Increment Plan DCS# ARH7808

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc clc$standard_file_names
*copyc cle$ecc_lexical
*copyc clt$string_size
*copyc fst$goi_object_information
*copyc jmc$class_names
*copyc jmc$job_management_id
*copyc jmc$status_message_text
*copyc jmc$system_scheduling_profile
*copyc jme$job_scheduler_conditions
*copyc jme$queued_file_conditions
*copyc jme$work_area_too_small
*copyc jmt$application_attributes
*copyc jmt$application_index
*copyc jmt$application_name
*copyc jmt$application_set
*copyc jmt$application_table
*copyc jmt$class_kind
*copyc jmt$defined_classes
*copyc jmt$job_category
*copyc jmt$job_category_data
*copyc jmt$job_class
*copyc jmt$job_class_attributes
*copyc jmt$job_class_set
*copyc jmt$job_class_statistics
*copyc jmt$job_class_table
*copyc jmt$job_scheduler_table
*copyc jmt$profile_header
*copyc jmt$scheduling_attr_results
*copyc jmt$scheduling_results_keys
*copyc jmt$service_class_attributes
*copyc jmt$service_class_index
*copyc jmt$service_class_set
*copyc jmt$service_class_statistics
*copyc jmt$service_class_table
*copyc jmt$system_profile_cycle_number
*copyc jmt$work_area
*copyc osd$integer_limits
*copyc osd$virtual_address
*copyc oss$job_paged_literal
*copyc ost$binary_unique_name
*copyc ost$caller_identifier
*copyc ost$status
?? POP ??
*copyc amp$return
*copyc avp$get_capability
*copyc clp$get_processing_phase
*copyc clp$get_source
*copyc clp$validate_name
*copyc fsp$close_file
*copyc fsp$expand_file_label
*copyc fsp$open_file
*copyc jmp$clear_leveler_profile_flag
*copyc jmp$clear_utility_active_flag
*copyc jmp$deactivate_job_leveling
*copyc jmp$determine_job_class
*copyc jmp$determine_service_class
*copyc jmp$install_profile_in_tables
*copyc jmp$read_application_record
*copyc jmp$read_category_data
*copyc jmp$read_defined_classes
*copyc jmp$read_job_class_record
*copyc jmp$read_scheduler_table
*copyc jmp$ready_job_leveler_task
*copyc jmp$set_profile_loading_flag
*copyc jmp$set_utility_active_flag
*copyc jmp$system_job
*copyc jmp$update_profile_in_tables
*copyc jmp$verify_utility_access_id
*copyc osp$append_status_integer
*copyc osp$append_status_parameter
*copyc osp$begin_system_activity
*copyc osp$check_for_desired_mf_class
*copyc osp$disestablish_cond_handler
*copyc osp$end_system_activity
*copyc osp$establish_block_exit_hndlr
*copyc osp$format_message
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$set_status_from_condition
*copyc osp$verify_system_privilege
*copyc pfp$begin_system_authority
*copyc pfp$change
*copyc pfp$end_system_authority
*copyc pfp$get_object_information
*copyc pfp$purge
*copyc qfp$move_input_q_to_unassigned
*copyc rmp$request_mass_storage
*copyc syp$process_deadstart_status
*copyc osv$deadstart_phase
*copyc jmv$application_table_p
*copyc jmv$default_job_class_attr
*copyc jmv$default_service_class_attr
*copyc jmv$default_application_attr
*copyc jmv$jcb
*copyc jmv$job_category_data
*copyc jmv$job_class_table_p
*copyc jmv$job_counts
*copyc jmv$job_scheduler_table
*copyc jmv$kjl_p
*copyc jmv$max_service_class_in_use
*copyc jmv$maximum_job_class_in_use
*copyc jmv$maximum_job_classes
*copyc jmv$maximum_service_classes
*copyc jmv$scheduling_utility_usage
*copyc jmv$service_classes
?? TITLE := 'check_active_jobs', EJECT ??

{ PURPOSE:
{   The purpose of this request is to determine if there are jobs active in
{   the job and service classes which are to be deleted from the active
{   scheduler tables.
{ NOTES:
{   Verification that the job and service classes are defined in the scheduler
{   tables has been made prior to calling this procedure.

  PROCEDURE check_active_jobs
    (    deleted_job_classes: jmt$job_class_set;
         deleted_service_classes: jmt$service_class_set;
     VAR status: ost$status);

    VAR
      job_class_index: jmt$job_class,
      service_class_index: jmt$service_class_index;

    status.normal := TRUE;

    IF deleted_job_classes <> $jmt$job_class_set [] THEN
      FOR job_class_index := jmc$lowest_site_job_class TO jmv$maximum_job_class_in_use DO
        IF job_class_index IN deleted_job_classes THEN
          IF jmv$job_counts.job_class_counts [job_class_index].initiated_jobs > 0 THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$delete_class_still_active, jmc$smt_job_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter,
                  jmv$job_class_table_p^ [job_class_index].name, status);
            RETURN;
          IFEND;
        IFEND;
      FOREND;
    IFEND;

    IF deleted_service_classes <> $jmt$service_class_set [] THEN
      FOR service_class_index := jmc$lowest_site_service_class TO jmv$max_service_class_in_use DO
        IF service_class_index IN deleted_service_classes THEN
          IF jmv$job_counts.service_class_counts [service_class_index].scheduler_initiated_jobs > 0 THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$delete_class_still_active,
                  jmc$smt_service_class, status);
            osp$append_status_parameter (osc$status_parameter_delimiter,
                  jmv$service_classes [service_class_index]^.attributes.name, status);
            RETURN;
          IFEND;
        IFEND;
      FOREND;
    IFEND;

  PROCEND check_active_jobs;
?? TITLE := 'delete_profile_cycle', EJECT ??

{ PURPOSE:
{   The purpose of this request is to delete the specified cycle of the
{   system scheduling profile permanent file if it exists.

  PROCEDURE delete_profile_cycle
    (    cycle_number: fst$cycle_number;
     VAR status: ost$status);

?? 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);

      VAR
        ignore_status: ost$status;

      pfp$end_system_authority;
      IF status.normal THEN
        osp$set_status_from_condition (jmc$job_management_id, condition, sfsa_p, status, ignore_status);
      IFEND;
    PROCEND handle_block_exit;
?? OLDTITLE, EJECT ??

    VAR
      file_cycle: pft$cycle_selector,
      local_status: ost$status,
      path: array [1 .. 4] of pft$name;

    status.normal := TRUE;

    path [1] := jmc$scheduling_profile_family;
    path [2] := jmc$scheduling_profile_user;
    path [3] := jmc$scheduling_profile_catalog;
    path [4] := jmc$scheduling_profile_filename;
    file_cycle.cycle_option := pfc$specific_cycle;
    file_cycle.cycle_number := cycle_number;

    osp$establish_block_exit_hndlr (^handle_block_exit);
    pfp$begin_system_authority;
    pfp$purge (path, file_cycle, jmc$scheduling_profile_password, local_status);
    pfp$end_system_authority;
    osp$disestablish_cond_handler;
    IF NOT local_status.normal THEN
      IF local_status.condition <> pfe$unknown_cycle THEN
        status := local_status;
      IFEND;
    IFEND;

  PROCEND delete_profile_cycle;
?? TITLE := '[XDCL, #GATE] jmp$abort_deadstart', EJECT ??
*copy jmh$abort_deadstart

  PROCEDURE [XDCL, #GATE] jmp$abort_deadstart
    (    display_message: string ( * );
         display_status: ost$status;
     VAR status: ost$status);

    VAR
      local_status: ost$status,
      message: ost$status_message,
      msg_line_count: ^ost$status_message_line_count,
      msg_line_size: ^ost$status_message_line_size,
      msg_line_text: ^ost$status_message_line,
      pointer: ^ost$status_message;

    status.normal := TRUE;

    IF NOT jmp$system_job () THEN
      osp$set_status_condition (jme$must_be_system_job, status);
      RETURN;
    IFEND;

    IF (osv$deadstart_phase <> osc$normal_deadstart) THEN
      RETURN;
    IFEND;

{ Format the error message since the deadstart message proceesor cannot process
{ templates.

  /format_error_message/
    BEGIN
      osp$format_message (display_status, osc$full_message_level, 256, message, local_status);
      IF NOT local_status.normal THEN
        EXIT /format_error_message/;
      IFEND;

      pointer := ^message;
      RESET pointer;
      NEXT msg_line_count IN pointer;
      IF msg_line_count = NIL THEN
        EXIT /format_error_message/;
      IFEND;
      NEXT msg_line_size IN pointer;
      IF msg_line_size = NIL THEN
        EXIT /format_error_message/;
      IFEND;
      NEXT msg_line_text: [msg_line_size^] IN pointer;
      IF msg_line_text = NIL THEN
        EXIT /format_error_message/;
      IFEND;

{ No return is expected from this call.

      syp$process_deadstart_status (msg_line_text^, {fatal_status} TRUE, status);
    END /format_error_message/;

{ Issue a general text message if the template message cannot be generated.

{ No return is expected from this call.

    syp$process_deadstart_status ('The system scheduling profile contains more Job and/or Service ' CAT
          'Classes than were declared at deadstart.', {fatal_status} TRUE, display_status);

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

  PROCEDURE [XDCL, #GATE] jmp$change_profile_cycle
    (    access_id: ost$binary_unique_name;
     VAR status: ost$status);

?? 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);

      VAR
        ignore_status: ost$status;

      pfp$end_system_authority;
      IF status.normal THEN
        osp$set_status_from_condition (jmc$job_management_id, condition, sfsa_p, status, ignore_status);
      IFEND;
    PROCEND handle_block_exit;
?? OLDTITLE, EJECT ??

    VAR
      change_list: array [1 .. 1] of pft$change_descriptor,
      file_cycle: pft$cycle_selector,
      local_access_id: ost$binary_unique_name,
      local_status: ost$status,
      path: array [1 .. 4] of pft$name;

    status.normal := TRUE;
    local_access_id := access_id;
    jmp$verify_utility_access_id (local_access_id, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    path [1] := jmc$scheduling_profile_family;
    path [2] := jmc$scheduling_profile_user;
    path [3] := jmc$scheduling_profile_catalog;
    path [4] := jmc$scheduling_profile_filename;
    file_cycle.cycle_option := pfc$specific_cycle;
    file_cycle.cycle_number := 1;

    change_list [1].change_type := pfc$cycle_number_change;
    change_list [1].cycle_number := 2;

    osp$establish_block_exit_hndlr (^handle_block_exit);
    pfp$begin_system_authority;
    pfp$change (path, file_cycle, jmc$scheduling_profile_password, change_list, status);
    pfp$end_system_authority;
    osp$disestablish_cond_handler;

    IF NOT status.normal THEN
      RETURN;
    IFEND;

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

  PROCEDURE [XDCL, #GATE] jmp$clear_utility_active
    (    access_id: ost$binary_unique_name;
     VAR status: ost$status);

    VAR
      local_access_id: ost$binary_unique_name,
      local_status: ost$status;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    local_access_id := access_id;
    jmp$clear_utility_active_flag (local_access_id, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
    IFEND;

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

  PROCEDURE [XDCL, #GATE] jmp$close_system_profile
    (    access_id: ost$binary_unique_name;
         detach_file: boolean;
         file_identifier: amt$file_identifier;
     VAR status: ost$status);

    VAR
      local_access_id: ost$binary_unique_name,
      local_status: ost$status;

    local_access_id := access_id;
    jmp$verify_utility_access_id (local_access_id, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    fsp$close_file (file_identifier, status);

    IF status.normal AND detach_file THEN
      amp$return (jmc$scheduling_profile_pathname CAT '.1', status);
    IFEND;

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

  PROCEDURE [XDCL, #GATE] jmp$delete_profile_cycle
    (    access_id: ost$binary_unique_name;
         cycle_number: jmt$system_profile_cycle_number;
     VAR status: ost$status);

    VAR
      local_access_id: ost$binary_unique_name,
      local_status: ost$status;

    status.normal := TRUE;
    local_access_id := access_id;
    jmp$verify_utility_access_id (local_access_id, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    delete_profile_cycle (cycle_number, status);

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

  PROCEDURE [XDCL] jmp$get_active_scheduling_attr
    (    job_class_name: ost$name;
         scheduling_results_keys_p: ^jmt$scheduling_results_keys;
     VAR work_area_p: {input, output} ^jmt$work_area;
     VAR scheduling_attribute_results_p: ^jmt$scheduling_attr_results;
     VAR status: ost$status);


    VAR
      caller_id: ost$caller_identifier,
      job_class_found: boolean,
      job_class_index: jmt$job_class,
      key_index: ost$non_negative_integers,
      local_status: ost$status,
      number_of_keys: ost$non_negative_integers,
      queued_jobs: jmt$job_count_range,
      queued_job_class_index: jmt$job_class,
      service_class_index: jmt$service_class_index,
      service_class_status: ost$status;


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

    #CALLER_ID (caller_id);
    IF caller_id.ring > osc$tsrv_ring THEN
      jmp$get_scheduling_admin_status (local_status);
      IF NOT local_status.normal THEN
        status := local_status;
        RETURN;
      IFEND;
    IFEND;

    scheduling_attribute_results_p := NIL;

    IF scheduling_results_keys_p <> NIL THEN
      number_of_keys := UPPERBOUND (scheduling_results_keys_p^);
      IF number_of_keys > 0 THEN
        NEXT scheduling_attribute_results_p: [1 .. number_of_keys] IN work_area_p;
        IF scheduling_attribute_results_p = NIL THEN
          osp$set_status_condition (jme$work_area_too_small, status);
          RETURN;
        IFEND;

        job_class_found := FALSE;

      /find_job_class_index/
        FOR job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
          IF jmv$job_class_table_p^ [job_class_index].defined AND
                (jmv$job_class_table_p^ [job_class_index].name = job_class_name) THEN
            job_class_found := TRUE;
            service_class_index := jmv$job_class_table_p^ [job_class_index].initial_service_class_index;
            IF (service_class_index = 0) OR (jmv$service_classes [service_class_index] = NIL) OR
                  (NOT jmv$service_classes [service_class_index]^.attributes.defined) THEN

{ This status will only be returned if a service class attribute has been requested.

              osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined,
                    jmc$smt_service_class, service_class_status);
              osp$append_status_integer (osc$status_parameter_delimiter, service_class_index, 10, FALSE,
                    service_class_status);
            IFEND;
            EXIT /find_job_class_index/;
          IFEND;
        FOREND /find_job_class_index/;

        FOR key_index := 1 TO number_of_keys DO
          scheduling_attribute_results_p^ [key_index].key := scheduling_results_keys_p^ [key_index];
          CASE scheduling_results_keys_p^ [key_index] OF
          = jmc$sak_active_jobs =
            IF job_class_found THEN
              IF service_class_status.normal THEN
                scheduling_attribute_results_p^ [key_index].active_jobs :=
                      jmv$job_counts.service_class_counts [service_class_index].scheduler_initiated_jobs -
                      jmv$job_counts.service_class_counts [service_class_index].swapped_jobs;
              ELSE
                status := service_class_status;
                RETURN;
              IFEND;
            ELSE
              osp$set_status_condition (jme$job_class_not_defined, status);
              RETURN;
            IFEND;

          = jmc$sak_enable_class_initiation =
            IF job_class_found THEN
              scheduling_attribute_results_p^ [key_index].enable_class_initiation :=
                    jmv$job_class_table_p^ [job_class_index].enable_class_initiation;
            ELSE
              osp$set_status_condition (jme$job_class_not_defined, status);
              RETURN;
            IFEND;

          = jmc$sak_enable_job_leveling =
            scheduling_attribute_results_p^ [key_index].enable_job_leveling :=
                  jmv$job_scheduler_table.enable_job_leveling;

          = jmc$sak_initiation_age_interval =
            IF job_class_found THEN
              scheduling_attribute_results_p^ [key_index].initiation_age_interval :=
                    jmv$job_class_table_p^ [job_class_index].initiation_age_interval;
            ELSE
              osp$set_status_condition (jme$job_class_not_defined, status);
              RETURN;
            IFEND;

          = jmc$sak_initiation_level =
            IF job_class_found THEN
              scheduling_attribute_results_p^ [key_index].initiation_level :=
                    jmv$job_class_table_p^ [job_class_index].initiation_level;
            ELSE
              osp$set_status_condition (jme$job_class_not_defined, status);
              RETURN;
            IFEND;

          = jmc$sak_job_leveling_prior_bias =
            scheduling_attribute_results_p^ [key_index].job_leveling_priority_bias :=
                  jmv$job_scheduler_table.job_leveling_priority_bias;

          = jmc$sak_maximum_active_jobs =
            IF job_class_found THEN
              scheduling_attribute_results_p^ [key_index].maximum_active_jobs :=
                    jmv$service_classes [service_class_index]^.attributes.maximum_active_jobs;
            ELSE
              osp$set_status_condition (jme$job_class_not_defined, status);
              RETURN;
            IFEND;

          = jmc$sak_null_attribute =
            ;

          = jmc$sak_queued_jobs =
            IF job_class_found THEN
              IF service_class_status.normal THEN
                queued_jobs := 0;

              /compute_queued_job_counts/
                FOR queued_job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
                  IF jmv$job_class_table_p^ [queued_job_class_index].defined THEN
                    IF jmv$job_class_table_p^ [queued_job_class_index].initial_service_class_index =
                          service_class_index THEN
                      queued_jobs := queued_jobs + jmv$job_counts.job_class_counts [queued_job_class_index].
                            queued_jobs;
                    IFEND;
                  IFEND;
                FOREND /compute_queued_job_counts/;
                scheduling_attribute_results_p^ [key_index].queued_jobs := queued_jobs;
              ELSE
                status := service_class_status;
                RETURN;
              IFEND;
            ELSE
              osp$set_status_condition (jme$job_class_not_defined, status);
              RETURN;
            IFEND;

          = jmc$sak_selection_priority =
            IF job_class_found THEN
              scheduling_attribute_results_p^ [key_index].selection_priority :=
                    jmv$job_class_table_p^ [job_class_index].selection_priority;
            ELSE
              osp$set_status_condition (jme$job_class_not_defined, status);
              RETURN;
            IFEND;

          = jmc$sak_swapped_jobs =
            IF job_class_found THEN
              IF service_class_status.normal THEN
                scheduling_attribute_results_p^ [key_index].swapped_jobs :=
                      jmv$job_counts.service_class_counts [service_class_index].swapped_jobs;
              ELSE
                status := service_class_status;
                RETURN;
              IFEND;
            ELSE
              osp$set_status_condition (jme$job_class_not_defined, status);
              RETURN;
            IFEND;

          ELSE
            ;
          CASEND;
        FOREND;
      IFEND;
    IFEND;

  PROCEND jmp$get_active_scheduling_attr;
?? TITLE := '[XDCL, #GATE] jmp$get_application_record', EJECT ??
*copy jmh$get_application_record

  PROCEDURE [XDCL, #GATE] jmp$get_application_record
    (    application_name: jmt$application_name;
     VAR application_record: jmt$application_attributes;
     VAR status: ost$status);

    VAR
      application_index: jmt$application_index,
      caller_id: ost$caller_identifier,
      local_application_name: jmt$application_name,
      local_application_record: jmt$application_attributes,
      local_status: ost$status,
      scl_name: ost$name,
      valid_name: boolean;

    status.normal := TRUE;

    #CALLER_ID (caller_id);
    IF caller_id.ring > osc$tsrv_ring THEN
      jmp$get_scheduling_admin_status (local_status);
      IF NOT local_status.normal THEN
        status := local_status;
        RETURN;
      IFEND;
    IFEND;

    local_application_name := application_name;
    clp$validate_name (local_application_name, scl_name, valid_name);
    IF NOT valid_name THEN
      osp$set_status_abnormal ('CL', cle$improper_name, local_application_name, status);
      RETURN;
    IFEND;

    application_index := 0;
    jmp$read_application_record (application_name, application_index, local_application_record, local_status);
    IF local_status.normal THEN
      application_record := local_application_record;
    ELSE
      status := local_status;
    IFEND;

  PROCEND jmp$get_application_record;
?? TITLE := '[XDCL, #GATE] jmp$get_category_data', EJECT ??
*copy jmh$get_category_data

  PROCEDURE [XDCL, #GATE] jmp$get_category_data
    (VAR category_data: jmt$job_category_data;
     VAR data_p: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      local_category_data: jmt$job_category_data,
      local_data_length: integer,
      local_data_p: ^SEQ ( * ),
      local_status: ost$status;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    REPEAT
      local_data_length := 1;
      IF jmv$job_category_data.item_list <> NIL THEN
        local_data_length := local_data_length + #SIZE (jmv$job_category_data.item_list^);
      IFEND;
      IF jmv$job_category_data.category_names <> NIL THEN
        local_data_length := local_data_length + #SIZE (jmv$job_category_data.category_names^);
      IFEND;
      PUSH local_data_p: [[REP local_data_length OF cell]];
      IF local_data_p = NIL THEN
        osp$set_status_condition (jme$no_space_in_runtime_stack, status);
        RETURN;
      IFEND;
      jmp$read_category_data (local_category_data, local_data_p, local_status);
      IF NOT local_status.normal AND (local_status.condition <> jme$no_element_in_sequence) THEN
        status := local_status;
        RETURN;
      IFEND;
    UNTIL local_status.normal;

    category_data := local_category_data;
    RESET data_p;
    IF local_category_data.item_list <> NIL THEN
      NEXT category_data.item_list: [[REP #SIZE (local_category_data.item_list^) OF cell]] IN data_p;
      IF category_data.item_list = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
        RETURN;
      IFEND;
      category_data.item_list^ := local_category_data.item_list^;
    IFEND;
    IF local_category_data.category_names <> NIL THEN
      NEXT category_data.category_names: [0 .. UPPERBOUND (local_category_data.category_names^)] IN data_p;
      IF category_data.category_names = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
        RETURN;
      IFEND;
      category_data.category_names^ := local_category_data.category_names^;
    IFEND;

  PROCEND jmp$get_category_data;
?? TITLE := '[XDCL, #GATE] jmp$get_default_class_values', EJECT ??
*copy jmh$get_default_class_values

  PROCEDURE [XDCL, #GATE] jmp$get_default_class_values
    (VAR job_class_defaults: jmt$job_class_attributes;
     VAR service_class_defaults: jmt$service_class_attributes;
     VAR application_defaults: jmt$application_attributes;
     VAR status: ost$status);

    VAR
      local_status: ost$status;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
    ELSE
      job_class_defaults := jmv$default_job_class_attr;
      service_class_defaults := jmv$default_service_class_attr;
      application_defaults := jmv$default_application_attr;
    IFEND;

  PROCEND jmp$get_default_class_values;
?? TITLE := '[XDCL, #GATE] jmp$get_defined_classes', EJECT ??
*copy jmh$get_defined_classes

  PROCEDURE [XDCL, #GATE] jmp$get_defined_classes
    (    class_kind: jmt$class_kind;
     VAR defined_classes: jmt$defined_classes;
     VAR number_of_classes: ost$non_negative_integers;
     VAR status: ost$status);

    VAR
      local_class_kind: jmt$class_kind,
      local_defined_classes_p: ^jmt$defined_classes,
      local_status: ost$status;

    status.normal := TRUE;
    number_of_classes := 0;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    local_class_kind := class_kind;
    IF (local_class_kind < LOWERVALUE (jmt$class_kind)) OR (local_class_kind > UPPERVALUE (jmt$class_kind))
          THEN
      osp$set_status_condition (jme$unknown_class_kind, status);
    ELSE
      PUSH local_defined_classes_p: [1 .. UPPERBOUND (defined_classes)];
      jmp$read_defined_classes (local_class_kind, local_defined_classes_p^, number_of_classes, local_status);
      IF local_status.normal THEN
        defined_classes := local_defined_classes_p^;
      ELSE
        status := local_status;
        IF local_status.condition = jme$error_in_job_class_ranking THEN
          defined_classes := local_defined_classes_p^;
        IFEND;
      IFEND;
    IFEND;

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

  PROCEDURE [XDCL] jmp$get_job_class_epilog
    (VAR job_class_epilog: fst$file_reference;
     VAR status: ost$status);

    VAR
      job_class_index: jmt$job_class;

    status.normal := TRUE;

    job_class_index := jmv$kjl_p^ [jmv$jcb.job_id].job_class;
    IF jmv$job_class_table_p^ [job_class_index].defined THEN
      IF jmv$job_class_table_p^ [job_class_index].epilog_p <> NIL THEN
        job_class_epilog := jmv$job_class_table_p^ [job_class_index].epilog_p^;
      ELSE
        job_class_epilog := '';
      IFEND;
    ELSE
      osp$set_status_condition (jme$job_class_not_defined, status);
    IFEND;

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

  PROCEDURE [XDCL, #GATE] jmp$get_job_class_prolog
    (VAR job_class_prolog: fst$file_reference;
     VAR status: ost$status);

    VAR
      job_class_index: jmt$job_class;

    osp$verify_system_privilege;
    status.normal := TRUE;

    job_class_index := jmv$kjl_p^ [jmv$jcb.job_id].job_class;
    IF jmv$job_class_table_p^ [job_class_index].defined THEN
      IF jmv$job_class_table_p^ [job_class_index].prolog_p <> NIL THEN
        job_class_prolog := jmv$job_class_table_p^ [job_class_index].prolog_p^;
      ELSE
        job_class_prolog := '';
      IFEND;
    ELSE
      osp$set_status_condition (jme$job_class_not_defined, status);
    IFEND;

  PROCEND jmp$get_job_class_prolog;
?? TITLE := '[XDCL, #GATE] jmp$get_job_class_record', EJECT ??
*copy jmh$get_job_class_record

  PROCEDURE [XDCL, #GATE] jmp$get_job_class_record
    (    job_class_index: jmt$job_class;
     VAR job_class_record: jmt$job_class_attributes;
     VAR data_p: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      local_data_length: integer,
      local_data_p: ^SEQ ( * ),
      local_job_class_index: jmt$job_class,
      local_job_class_record: jmt$job_class_attributes,
      local_status: ost$status;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    local_job_class_index := job_class_index;
    IF (local_job_class_index = 0) OR (local_job_class_index > jmv$maximum_job_class_in_use) OR
          (NOT jmv$job_class_table_p^ [local_job_class_index].defined) THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_job_class, status);
      osp$append_status_integer (osc$status_parameter_delimiter, local_job_class_index, 10, FALSE, status);
      RETURN;
    IFEND;

    REPEAT
      local_data_length := 1;
      IF jmv$job_class_table_p^ [local_job_class_index].prolog_p <> NIL THEN
        local_data_length := local_data_length + #SIZE (jmv$job_class_table_p^ [local_job_class_index].
              prolog_p^);
      IFEND;
      IF jmv$job_class_table_p^ [local_job_class_index].epilog_p <> NIL THEN
        local_data_length := local_data_length + #SIZE (jmv$job_class_table_p^ [local_job_class_index].
              epilog_p^);
      IFEND;
      PUSH local_data_p: [[REP local_data_length OF cell]];
      IF local_data_p = NIL THEN
        osp$set_status_condition (jme$no_space_in_runtime_stack, status);
        RETURN;
      IFEND;
      jmp$read_job_class_record (local_job_class_index, local_job_class_record, local_data_p, local_status);
      IF NOT local_status.normal AND (local_status.condition <> jme$no_element_in_sequence) THEN
        status := local_status;
        RETURN;
      IFEND;
    UNTIL local_status.normal;

    job_class_record := local_job_class_record;
    RESET data_p;
    IF local_job_class_record.prolog_p <> NIL THEN
      NEXT job_class_record.prolog_p: [STRLENGTH (local_job_class_record.prolog_p^)] IN data_p;
      IF job_class_record.prolog_p = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
        RETURN;
      IFEND;
      job_class_record.prolog_p^ := local_job_class_record.prolog_p^;
    IFEND;
    IF local_job_class_record.epilog_p <> NIL THEN
      NEXT job_class_record.epilog_p: [STRLENGTH (local_job_class_record.epilog_p^)] IN data_p;
      IF job_class_record.epilog_p = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
        RETURN;
      IFEND;
      job_class_record.epilog_p^ := local_job_class_record.epilog_p^;
    IFEND;

  PROCEND jmp$get_job_class_record;
?? TITLE := '[XDCL, #GATE] jmp$get_job_class_statistics', EJECT ??
*copy jmh$get_job_class_statistics

  PROCEDURE [XDCL, #GATE] jmp$get_job_class_statistics
    (    job_class_index: jmt$job_class;
     VAR job_class_statistics: jmt$job_class_statistics;
     VAR status: ost$status);

    VAR
      local_status: ost$status;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    IF (job_class_index = 0) OR (job_class_index > jmv$maximum_job_class_in_use) OR
          (NOT jmv$job_class_table_p^ [job_class_index].defined) THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_job_class, status);
      osp$append_status_integer (osc$status_parameter_delimiter, job_class_index, 10, FALSE, status);
      RETURN;
    IFEND;

    job_class_statistics.queued_jobs := jmv$job_counts.job_class_counts [job_class_index].queued_jobs;
    job_class_statistics.initiated_jobs := jmv$job_counts.job_class_counts [job_class_index].initiated_jobs;

  PROCEND jmp$get_job_class_statistics;
?? TITLE := '[XDCL, #GATE] jmp$get_length_of_sched_tables', EJECT ??
*copy jmh$get_length_of_sched_tables

  PROCEDURE [XDCL, #GATE] jmp$get_length_of_sched_tables
    (VAR maximum_job_classes: jmt$job_class;
     VAR maximum_job_class_index: jmt$job_class;
     VAR maximum_service_classes: jmt$service_class_index;
     VAR maximum_service_class_index: jmt$service_class_index;
     VAR maximum_applications: jmt$application_index;
     VAR maximum_categories: integer;
     VAR status: ost$status);

    VAR
      local_status: ost$status;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    maximum_job_classes := jmv$maximum_job_classes;
    maximum_job_class_index := jmc$maximum_job_classes;
    maximum_service_classes := jmv$maximum_service_classes;
    maximum_service_class_index := jmc$maximum_service_classes;
    maximum_applications := jmc$maximum_application_index;
    maximum_categories := jmc$number_of_job_categories;

  PROCEND jmp$get_length_of_sched_tables;
?? TITLE := '[XDCL, #GATE] jmp$get_scheduler_table', EJECT ??
*copy jmh$get_scheduler_table

  PROCEDURE [XDCL, #GATE] jmp$get_scheduler_table
    (VAR scheduler_table: jmt$job_scheduler_table;
     VAR data_p: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      local_data_length: integer,
      local_data_p: ^SEQ ( * ),
      local_scheduler_table: jmt$job_scheduler_table,
      local_status: ost$status;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    REPEAT
      local_data_length := 1;
      IF jmv$job_scheduler_table.validation_categories_p <> NIL THEN
        local_data_length := local_data_length + #SIZE (jmv$job_scheduler_table.validation_categories_p^);
      IFEND;
      PUSH local_data_p: [[REP local_data_length OF cell]];
      IF local_data_p = NIL THEN
        osp$set_status_condition (jme$no_space_in_runtime_stack, status);
        RETURN;
      IFEND;
      jmp$read_scheduler_table (local_scheduler_table, local_data_p, local_status);
      IF NOT local_status.normal AND (local_status.condition <> jme$no_element_in_sequence) THEN
        status := local_status;
        RETURN;
      IFEND;
    UNTIL local_status.normal;

    scheduler_table := local_scheduler_table;
    RESET data_p;
    IF local_scheduler_table.validation_categories_p <> NIL THEN
      NEXT scheduler_table.validation_categories_p: [1 .. UPPERBOUND (local_scheduler_table.
            validation_categories_p^)] IN data_p;
      IF scheduler_table.validation_categories_p = NIL THEN
        osp$set_status_condition (jme$no_element_in_sequence, status);
        RETURN;
      IFEND;
      scheduler_table.validation_categories_p^ := local_scheduler_table.validation_categories_p^;
    IFEND;

  PROCEND jmp$get_scheduler_table;
?? TITLE := '[XDCL] jmp$get_scheduling_admin_status', EJECT ??
*copy jmh$get_scheduling_admin_status

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

    VAR
      administrator_status: boolean,
      override_validation: boolean,
      processing_phase: clt$processing_phase,
      restricted_mainframe: boolean;


?? NEWTITLE := 'check_for_establish_job_classes', EJECT ??

{ This procedure checks to see if the command that initiated this request is on
{ $SYSTEM.OSF$BUILTIN_LIBRARY, its R1 ring attribute is 3, and that the program
{ called (MANAS) is also on $SYSTEM.OSF$BUILTIN_LIBRARY.  This will allow the
{ Soviet 962 systems to deadstart even though the $system user does not have the
{ Scheduling_Administration capability.

    PROCEDURE check_for_establish_job_classes
      (VAR override_validation: boolean;
       VAR status: ost$status);

      CONST
        expected_library_path = ':$SYSTEM.$SYSTEM.OSF$BUILTIN_LIBRARY',
        expected_library_path_size = 36;

      VAR
        file_previously_opened: boolean,
        information_request: fst$goi_information_request,
        object_info_p: ^fst$goi_object_information,
        resolved_path_size: clt$string_size,
        source: clt$source,
        static_label_attributes: bat$static_label_attributes,
        work_area_p: ^SEQ ( * ),
        work_area_size: [STATIC] ost$segment_length := #SIZE (fst$goi_object_information) +
              #SIZE (fst$goi_object) + fsc$max_path_size + #SIZE (bat$static_label_attributes);


      status.normal := TRUE;
      override_validation := FALSE;

      information_request.catalog_depth.depth_specification := fsc$specific_depth;
      information_request.catalog_depth.depth := 1;
      information_request.object_information_requests := $fst$goi_object_info_requests [fsc$goi_file_label];

      PUSH work_area_p: [[REP work_area_size OF cell]];
      RESET work_area_p;

{ Verify that the commands are coming from the expected library.

      pfp$get_object_information (clc$current_command_input, information_request, NIL, work_area_p, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      RESET work_area_p;
      NEXT object_info_p IN work_area_p;

      resolved_path_size := STRLENGTH (object_info_p^.resolved_path^);
      IF expected_library_path_size <= resolved_path_size THEN
        IF (object_info_p^.resolved_path^ (1, expected_library_path_size) = expected_library_path) THEN

          fsp$expand_file_label (object_info_p^.object^.file_label, static_label_attributes,
                file_previously_opened, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;

{ Verify the R1 ring attribute of the file.

          IF static_label_attributes.ring_attributes.r1 <> osc$tsrv_ring THEN
            RETURN;
          IFEND;

{ Verify that the program called is from the expected library.

          clp$get_source (source, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;

          IF (source.kind = clc$library_source) AND (source.path_name (1, resolved_path_size) =
                object_info_p^.resolved_path^) THEN
            override_validation := TRUE;
          IFEND;
        IFEND;
      IFEND;
    PROCEND check_for_establish_job_classes;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;

{ If this task already has the utility active there is no need to verify the
{ scheduling_administration capability.

    jmp$verify_utility_access_id (jmv$scheduling_utility_usage.access_id, status);
    IF status.normal THEN
      RETURN;
    IFEND;

    IF NOT jmp$system_job () THEN
      avp$get_capability (avc$scheduling_administration, avc$user, administrator_status, status);
      IF NOT status.normal THEN
        osp$set_status_condition (jme$must_be_scheduling_admin, status);
        RETURN;
      IFEND;
      IF administrator_status THEN
        RETURN;
      IFEND;

      clp$get_processing_phase (processing_phase, status);
      IF status.normal THEN
        IF (processing_phase = clc$class_prolog_phase) OR (processing_phase = clc$class_epilog_phase) THEN
          RETURN;
        IFEND;
      IFEND;
      osp$set_status_condition (jme$must_be_scheduling_admin, status);
    ELSE
      status.normal := TRUE;
      osp$check_for_desired_mf_class (osc$mc_china_or_soviet_class, restricted_mainframe);
      IF restricted_mainframe THEN
        avp$get_capability (avc$scheduling_administration, avc$user, administrator_status, status);
        IF NOT status.normal THEN
          osp$set_status_condition (jme$must_be_scheduling_admin, status);
        ELSEIF NOT administrator_status THEN

{ Check to see if the call was made from the rap$establish_job_classes procedure
{ on OSF$BUILTIN_LIBRARY, and that the program called is also on this library.
{ This will allow Soviet 962 systems to recover the scheduling profile in
{ deadstart even though the system job does not have the Scheduling Administration
{ capability.

          check_for_establish_job_classes (override_validation, status);
          IF (NOT status.normal) OR (NOT override_validation) THEN
            osp$set_status_condition (jme$must_be_scheduling_admin, status);
          IFEND;
        IFEND;
      IFEND;
    IFEND;

  PROCEND jmp$get_scheduling_admin_status;
?? TITLE := '[XDCL, #GATE] jmp$get_service_class_record', EJECT ??
*copy jmh$get_service_class_record

  PROCEDURE [XDCL, #GATE] jmp$get_service_class_record
    (    service_class_index: jmt$service_class_index;
     VAR service_class_record: jmt$service_class_attributes;
     VAR status: ost$status);

    VAR
      local_status: ost$status;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    IF (service_class_index = 0) OR (jmv$service_classes [service_class_index] = NIL) OR
          (NOT jmv$service_classes [service_class_index]^.attributes.defined) THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_service_class,
            status);
      osp$append_status_integer (osc$status_parameter_delimiter, service_class_index, 10, FALSE, status);
      RETURN;
    IFEND;

    service_class_record := jmv$service_classes [service_class_index]^.attributes;

  PROCEND jmp$get_service_class_record;
?? TITLE := '[XDCL, #GATE] jmp$get_service_class_stats', EJECT ??
*copy jmh$get_service_class_stats

  PROCEDURE [XDCL, #GATE] jmp$get_service_class_stats
    (    service_class_index: jmt$service_class_index;
     VAR service_class_statistics: jmt$service_class_statistics;
     VAR status: ost$status);

    VAR
      active_jobs: jmt$job_count_range,
      job_class_index: jmt$job_class,
      local_status: ost$status,
      queued_jobs: jmt$job_count_range;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    IF (service_class_index = 0) OR (jmv$service_classes [service_class_index] = NIL) OR
          (NOT jmv$service_classes [service_class_index]^.attributes.defined) THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_service_class,
            status);
      osp$append_status_integer (osc$status_parameter_delimiter, service_class_index, 10, FALSE, status);
      RETURN;
    IFEND;

    queued_jobs := 0;

  /compute_queued_job_counts/
    FOR job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
      IF jmv$job_class_table_p^ [job_class_index].defined THEN
        IF jmv$job_class_table_p^ [job_class_index].initial_service_class_index = service_class_index THEN
          queued_jobs := queued_jobs + jmv$job_counts.job_class_counts [job_class_index].queued_jobs;
        IFEND;
      IFEND;
    FOREND /compute_queued_job_counts/;

    active_jobs := jmv$job_counts.service_class_counts [service_class_index].scheduler_initiated_jobs -
          jmv$job_counts.service_class_counts [service_class_index].swapped_jobs;

    service_class_statistics.queued_jobs := queued_jobs;
    service_class_statistics.active_jobs := active_jobs;
    service_class_statistics.swapped_jobs := jmv$job_counts.service_class_counts [service_class_index].
          swapped_jobs;

  PROCEND jmp$get_service_class_stats;
?? TITLE := '[XDCL, #GATE] jmp$install_profile', EJECT ??
*copy jmh$install_profile

{ DESIGN:
{   Verify that the caller has the proper access and that the profile to be
{   installed contains scheduling definitions that are consistent with the
{   active scheduler tables.  Make the calling job unswappable.  Prevent job
{   submission, job initiation, and service class switching by executing jobs.
{   Check that no jobs are still active in any job or service classes that are
{   going to be deleted.  Move the input queues of job classes that are changing
{   structure or being deleted to the UNASSIGNED job class.  Delete the current
{   scheduling profile, if requested, and then install the new profile in the
{   scheduler tables.  Allow job submission, job initiation, and service class
{   switching to proceed and allow the calling job to be swapped.
{ NOTES:
{   All data verification is performed before the job is made unswappable.
{   While the job is unswappable, the only mass storage I/O performed is the
{   delete of the scheduling profile permanent file.

  PROCEDURE [XDCL, #GATE] jmp$install_profile
    (    access_id: ost$binary_unique_name;
         job_class_entries_p: ^jmt$job_class_table;
         service_class_entries_p: ^jmt$service_class_table;
         application_entries_p: ^jmt$application_table;
         controls_entry: jmt$job_scheduler_table;
         category_data: jmt$job_category_data;
         move_job_classes: jmt$job_class_set;
         deleted_job_classes: jmt$job_class_set;
         deleted_service_classes: jmt$service_class_set;
         deleted_applications: jmt$application_set;
         delete_profile_cycle2: boolean;
     VAR status: ost$status);

    VAR
      cycle_number: fst$cycle_number,
      ignore_status: ost$status,
      local_access_id: ost$binary_unique_name,
      local_number_of_jobs_moved: jmt$job_count_range,
      local_status: ost$status,
      profile_deleted: boolean,
      profile_installed: boolean,
      system_activity_begun: boolean;

?? NEWTITLE := 'install_profile_abort_handler', EJECT ??

{ PURPOSE:
{   The purpose of this request is to clear any restraints on system activity
{   which were set to install a scheduling profile if the install process
{   aborts.

    PROCEDURE install_profile_abort_handler
      (    condition: pmt$condition;
           condition_information: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

{ Allow job submission and initiation and service class switching to proceed.

      jmp$set_profile_loading_flag ({profile_is_loading} FALSE, controls_entry.profile_identification,
            ignore_status);

{ Clear special processing for this task.

      IF system_activity_begun THEN
        system_activity_begun := FALSE;
        #SPOIL (system_activity_begun);
        osp$end_system_activity;
      IFEND;

      IF local_status.normal THEN
        IF NOT profile_installed THEN
          IF profile_deleted THEN
            osp$set_status_condition (jme$profile_cycle2_lost, status);
          ELSE
            osp$set_status_condition (jme$profile_not_installed, status);
          IFEND;
        IFEND;
      ELSE
        status := local_status;
      IFEND;

    PROCEND install_profile_abort_handler;
?? OLDTITLE, EJECT ??
    status.normal := TRUE;
    local_status.normal := TRUE;
    profile_deleted := FALSE;
    profile_installed := FALSE;
    system_activity_begun := FALSE;
    #SPOIL (profile_deleted);
    #SPOIL (profile_installed);
    #SPOIL (system_activity_begun);

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    local_access_id := access_id;
    jmp$verify_utility_access_id (local_access_id, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    verify_profile_to_be_installed (job_class_entries_p, service_class_entries_p, application_entries_p,
          move_job_classes, deleted_job_classes, deleted_service_classes, deleted_applications, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    osp$establish_block_exit_hndlr (^install_profile_abort_handler);

{ Set special processing for this task.

    osp$begin_system_activity;
    system_activity_begun := TRUE;
    #SPOIL (system_activity_begun);

  /install_profile/
    BEGIN

{ Prevent job submission and initiation and service class switching.

      jmp$set_profile_loading_flag ({profile_is_loading} TRUE, controls_entry.profile_identification,
            local_status);
      IF NOT local_status.normal THEN
        EXIT /install_profile/;
      IFEND;

{ Ready all the job levelers that are directly connected and wait for them
{ to unassign jobs from this mainframe (a.k.a., the client).

      jmp$deactivate_job_leveling (local_status);
      IF NOT local_status.normal THEN
        EXIT /install_profile/;
      IFEND;

{ Check that no jobs are active in the job and service classes to be deleted.

      check_active_jobs (deleted_job_classes, deleted_service_classes, local_status);
      IF NOT local_status.normal THEN
        EXIT /install_profile/;
      IFEND;

{ Move the input queues of job classes that are changing structure or being
{ deleted to the UNASSIGNED job class.

      move_jobs_to_unassigned (move_job_classes, local_number_of_jobs_moved, local_status);
      IF NOT local_status.normal THEN
        EXIT /install_profile/;
      IFEND;

{ Delete the current scheduling profile.

      IF delete_profile_cycle2 THEN
        cycle_number := jmc$scheduling_profile_cycle;
        delete_profile_cycle (cycle_number, local_status);
        IF NOT local_status.normal THEN
          EXIT /install_profile/;
        IFEND;
        profile_deleted := TRUE;
        #SPOIL (profile_deleted);
      IFEND;

{ Install the new scheduling profile in the scheduling tables.

      jmp$install_profile_in_tables (local_access_id, job_class_entries_p, service_class_entries_p,
            application_entries_p, controls_entry, category_data, deleted_job_classes,
            deleted_service_classes, deleted_applications, local_status);
      IF NOT local_status.normal THEN
        IF profile_deleted THEN
          osp$set_status_condition (jme$profile_cycle2_lost, local_status);
        ELSE
          osp$set_status_condition (jme$profile_not_installed, local_status);
        IFEND;
        EXIT /install_profile/;
      IFEND;
      profile_installed := TRUE;
      #SPOIL (profile_installed);

    END /install_profile/;

{ Allow job submission and initiation and service class switching to proceed.

    jmp$set_profile_loading_flag ({profile_is_loading} FALSE, controls_entry.profile_identification,
          ignore_status);

{ Clear special processing for this task.

    osp$end_system_activity;
    system_activity_begun := FALSE;
    #SPOIL (system_activity_begun);

    IF NOT local_status.normal THEN
      status := local_status;
    IFEND;

    osp$disestablish_cond_handler;

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

  PROCEDURE [XDCL, #GATE] jmp$open_system_profile
    (    access_id: ost$binary_unique_name;
         cycle_number: jmt$system_profile_cycle_number;
         open_for_write: boolean;
         validation_attributes_p: ^fst$file_cycle_attributes;
     VAR file_identifier: amt$file_identifier;
     VAR status: ost$status);

?? 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);

      VAR
        ignore_status: ost$status;

      pfp$end_system_authority;
      IF status.normal THEN
        osp$set_status_from_condition (jmc$job_management_id, condition, sfsa_p, status, ignore_status);
      IFEND;
    PROCEND handle_block_exit;
?? OLDTITLE, EJECT ??

    CONST
      path_size = jmc$scheduling_profile_path_siz + 2;

    VAR
      access_ring_p: ^fst$file_cycle_attributes,
      attachment_options_p: ^fst$attachment_options,
      creation_ring_p: ^fst$file_cycle_attributes,
      caller_id: ost$caller_identifier,
      file_path: string (path_size),
      local_access_id: ost$binary_unique_name,
      local_status: ost$status;

    status.normal := TRUE;
    local_access_id := access_id;
    jmp$verify_utility_access_id (local_access_id, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    #CALLER_ID (caller_id);

    file_path := jmc$scheduling_profile_pathname CAT '.1';
    IF cycle_number = 2 THEN
      file_path (path_size) := '2';
    IFEND;

{ Attach the file for read access and open the file with the rings of the caller
{
{ Note:
{   For compatibility with MANAS at previous levels of NOS/VE (< 1.5.2), the
{   file is created with ring attributes of osc$user_ring and opened at the
{   larger of of osc$user_ring and the caller ring.  Eventually this should be
{   changed to create the file with rings of (3, 3, 3) and open the file with
{   the rings of the caller.  This must be done in two more phases.  1) Create
{   the file with rings of (3, 3, 3) but open it at the caller ring or
{   osc$user_ring which ever is larger.  2) Override the open ring to the
{   caller ring.

    PUSH creation_ring_p: [1 .. 1];
    creation_ring_p^ [1].selector := fsc$ring_attributes;
    creation_ring_p^ [1].ring_attributes.r1 := osc$user_ring;
    creation_ring_p^ [1].ring_attributes.r2 := osc$user_ring;
    creation_ring_p^ [1].ring_attributes.r3 := osc$user_ring;

    access_ring_p := creation_ring_p;
    IF caller_id.ring > osc$user_ring THEN
      PUSH access_ring_p: [1 .. 1];
      access_ring_p^ [1].ring_attributes.r1 := caller_id.ring;
      access_ring_p^ [1].ring_attributes.r2 := caller_id.ring;
      access_ring_p^ [1].ring_attributes.r3 := caller_id.ring;
    IFEND;

    PUSH attachment_options_p: [1 .. 3];
    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 [fsc$read];
    attachment_options_p^ [2].selector := fsc$open_position;
    attachment_options_p^ [2].open_position := amc$open_at_boi;
    attachment_options_p^ [3].selector := fsc$create_file;
    attachment_options_p^ [3].create_file := FALSE;

    osp$establish_block_exit_hndlr (^handle_block_exit);
    pfp$begin_system_authority;
    IF open_for_write THEN
      attachment_options_p^ [1].access_modes.value := $fst$file_access_options
            [fsc$read, fsc$append, fsc$modify, fsc$shorten];
      attachment_options_p^ [3].create_file := TRUE;
      rmp$request_mass_storage (file_path, rmc$unspecified_allocation_size, rmc$unspecified_file_size,
            rmc$msc_system_critical_files, rmc$unspecified_vsn, {volume_overflow_allowed =} TRUE, status);
    IFEND;
    IF status.normal THEN
      fsp$open_file (file_path, amc$segment, attachment_options_p, creation_ring_p, validation_attributes_p,
            validation_attributes_p, access_ring_p, file_identifier, status);
    IFEND;
    pfp$end_system_authority;
    osp$disestablish_cond_handler;

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

  PROCEDURE [XDCL, #GATE] jmp$reactivate_job_leveling
    (    access_id: ost$binary_unique_name;
     VAR status: ost$status);

    VAR
      ignore_leveler_exists: boolean,
      local_status: ost$status;

    status.normal := TRUE;
    jmp$clear_leveler_profile_flag (access_id, local_status);
    IF local_status.normal THEN
      jmp$ready_job_leveler_task (ignore_leveler_exists);
    ELSE
      status := local_status;
    IFEND;
  PROCEND jmp$reactivate_job_leveling;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$set_utility_active', EJECT ??
*copy jmh$set_utility_active

  PROCEDURE [XDCL, #GATE] jmp$set_utility_active
    (VAR access_id: ost$binary_unique_name;
     VAR status: ost$status);

    VAR
      local_status: ost$status,
      local_access_id: ost$binary_unique_name;

    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    jmp$set_utility_active_flag (local_access_id, local_status);
    IF local_status.normal THEN
      access_id := local_access_id;
    ELSE
      status := local_status;
    IFEND;

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

{ DESIGN:
{   Verify that the caller has the proper access and that the structure
{   of the scheduling profile is not being changed.  Verify that all of the
{   job classes, service classes, and applications to be updated are defined
{   in the scheduler tables and that the job and service classes have the
{   same indices.  Update the profile in the scheduler tables.
{ NOTES:
{   A change in structure is indicated by a change in the profile identification
{   field of the job scheduler controls, a class, or an application.
{   The following types of changes are not made by an update request.  They
{   require an install request.
{   . change in category data
{   . addition of a class or application
{   . deletion of a class or application
{   . change in the  Membership attributes of the job scheduler controls or
{           job classes
{   . change in the name, abbreviation, or index of a class
{   . change in the name of an application

  PROCEDURE [XDCL, #GATE] jmp$update_profile
    (    access_id: ost$binary_unique_name;
         changed_job_classes_p: ^jmt$job_class_table;
         changed_service_classes_p: ^jmt$service_class_table;
         changed_applications_p: ^jmt$application_table;
         controls_p: ^jmt$job_scheduler_table;
     VAR status: ost$status);

    VAR
      application_record: jmt$application_attributes,
      application_index: jmt$application_index,
      i: integer,
      job_class_index: jmt$job_class,
      job_class_name: jmt$job_class_name,
      local_access_id: ost$binary_unique_name,
      local_status: ost$status,
      service_class_index: jmt$service_class_index,
      service_class_name: jmt$service_class_name;


    status.normal := TRUE;

    jmp$get_scheduling_admin_status (local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

    local_access_id := access_id;
    jmp$verify_utility_access_id (local_access_id, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
      RETURN;
    IFEND;

{ Verify that the structure of the scheduling profile is not being changed.

    IF controls_p <> NIL THEN
      IF controls_p^.profile_identification <> jmv$job_scheduler_table.profile_identification THEN
        osp$set_status_condition (jme$profile_id_mismatch, status);
        RETURN;
      IFEND;
    IFEND;

{ Verify that the job classes to be updated are defined with the same name
{ and class index in the job class table and that their structure is not being
{ changed.

    IF changed_job_classes_p <> NIL THEN
      FOR i := 1 TO UPPERBOUND (changed_job_classes_p^) DO
        job_class_index := changed_job_classes_p^ [i].index;
        job_class_name := changed_job_classes_p^ [i].name;
        IF jmv$job_class_table_p^ [job_class_index].defined THEN
          IF jmv$job_class_table_p^ [job_class_index].name <> job_class_name THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_conflict, jmc$smt_job_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter, job_class_name, status);
            RETURN;
          ELSEIF jmv$job_class_table_p^ [job_class_index].profile_identification <>
                changed_job_classes_p^ [i].profile_identification THEN
            osp$set_status_condition (jme$profile_id_mismatch, status);
            RETURN;
          IFEND;
        ELSE
          osp$set_status_abnormal (jmc$job_management_id, jme$class_or_appl_not_defined, jmc$smt_job_class,
                status);
          osp$append_status_parameter (osc$status_parameter_delimiter, job_class_name, status);
          RETURN;
        IFEND;
      FOREND;
    IFEND;

{ Verify that the service classes to be updated are defined with the same name
{ and class index in the service class table and that their structure is not
{ being changed.

    IF changed_service_classes_p <> NIL THEN
      FOR i := 1 TO UPPERBOUND (changed_service_classes_p^) DO
        service_class_index := changed_service_classes_p^ [i].index;
        service_class_name := changed_service_classes_p^ [i].name;
        IF (jmv$service_classes [service_class_index] <> NIL) AND
              jmv$service_classes [service_class_index]^.attributes.defined THEN
          IF jmv$service_classes [service_class_index]^.attributes.name <> service_class_name THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_conflict, jmc$smt_service_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter, service_class_name, status);
            RETURN;
          ELSEIF jmv$service_classes [service_class_index]^.attributes.profile_identification <>
                changed_service_classes_p^ [i].profile_identification THEN
            osp$set_status_condition (jme$profile_id_mismatch, status);
            RETURN;
          IFEND;
        ELSE
          osp$set_status_abnormal (jmc$job_management_id, jme$class_or_appl_not_defined,
                jmc$smt_service_class, status);
          osp$append_status_parameter (osc$status_parameter_delimiter, service_class_name, status);
          RETURN;
        IFEND;
      FOREND;
    IFEND;

{ Verify that the applications to be updated are defined in the application
{ table.

    IF changed_applications_p <> NIL THEN
      FOR i := 1 TO UPPERBOUND (changed_applications_p^) DO
        application_index := 0;
        jmp$read_application_record (changed_applications_p^ [i].name, application_index, application_record,
              local_status);
        IF NOT local_status.normal THEN
          status := local_status;
          RETURN;
        IFEND;
        IF application_record.profile_identification <> changed_applications_p^ [i].
              profile_identification THEN
          osp$set_status_condition (jme$profile_id_mismatch, status);
          RETURN;
        IFEND;
      FOREND;
    IFEND;

    jmp$update_profile_in_tables (local_access_id, changed_job_classes_p, changed_service_classes_p,
          changed_applications_p, controls_p, local_status);
    IF NOT local_status.normal THEN
      status := local_status;
    IFEND;

  PROCEND jmp$update_profile;
?? OLDTITLE ??
?? NEWTITLE := 'move_jobs_to_unassigned', EJECT ??

{ PURPOSE:
{   The purpose of this request is to move the input queues for the given job
{   classes to the UNASSIGNED job class.
{ NOTES:
{   Verification that the job classes are defined in the job class table has
{   been made prior to calling this procedure.

  PROCEDURE move_jobs_to_unassigned
    (    move_job_classes: jmt$job_class_set;
     VAR number_of_jobs_moved: jmt$job_count_range;
     VAR status: ost$status);

    VAR
      job_class_index: jmt$job_class,
      jobs_moved: jmt$job_count_range;

    status.normal := TRUE;
    number_of_jobs_moved := 0;

    FOR job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
      IF job_class_index IN move_job_classes THEN
        qfp$move_input_q_to_unassigned (job_class_index, jobs_moved, status);
        number_of_jobs_moved := number_of_jobs_moved + jobs_moved;
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;
    FOREND;

  PROCEND move_jobs_to_unassigned;
?? TITLE := 'verify_applications_in_profile', EJECT ??

{ PURPOSE:
{   The purpose of this request is to verify that applications in the profile
{   to be installed contain valid application definitions.
{ DESIGN:
{   Verify that the applications to be installed do not exceed the maximum
{   number supported by the system.  Verify that they have a unique name and
{   are sorted in ascending sequence by name.

  PROCEDURE verify_applications_in_profile
    (    application_entries_p: ^jmt$application_table;
     VAR status: ost$status);

    VAR
      application_name: jmt$application_name,
      entry: ost$non_negative_integers,
      next_entry: ost$non_negative_integers,
      number_of_entries: ost$non_negative_integers;

    status.normal := TRUE;

    IF application_entries_p <> NIL THEN
      number_of_entries := UPPERBOUND (application_entries_p^);
      IF number_of_entries > jmc$maximum_application_index THEN
        osp$set_status_abnormal (jmc$job_management_id, jme$profile_too_large, jmc$smt_applications, status);
        osp$append_status_integer (osc$status_parameter_delimiter, jmc$maximum_application_index, 10, FALSE,
              status);
        RETURN;
      IFEND;
      FOR entry := 1 TO number_of_entries DO
        application_name := application_entries_p^ [entry].name;
        next_entry := entry + 1;
        IF next_entry < number_of_entries THEN
          IF application_name > application_entries_p^ [next_entry].name THEN
            osp$set_status_condition (jme$applications_not_sorted, status);
            RETURN;
          IFEND;
        IFEND;

      /verify_unique_names/
        WHILE next_entry < number_of_entries DO
          IF application_name = application_entries_p^ [next_entry].name THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_or_appl_not_unique, jmc$smt_application,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter, application_name, status);
            RETURN;
          IFEND;
          next_entry := next_entry + 1;
        WHILEND /verify_unique_names/;
      FOREND;
    IFEND;

  PROCEND verify_applications_in_profile;
?? TITLE := 'verify_classes_to_be_deleted', EJECT ??

{ PURPOSE:
{   The purpose of this request is to verify that the classes and applications
{   to be deleted are valid for deletion.
{ DESIGN:
{   Verify that the job and service classes to be deleted are defined in the
{   scheduler tables and are not any of the predefined classes, SYSTEM,
{   MAINTENANCE, or UNASSIGNED.  Verify that the applications to be deleted
{   are defined in the application table for application scheduling.

  PROCEDURE verify_classes_to_be_deleted
    (    deleted_job_classes: jmt$job_class_set;
         deleted_service_classes: jmt$service_class_set;
         deleted_applications: jmt$application_set;
     VAR status: ost$status);

    VAR
      application_index: jmt$application_index,
      job_class_index: jmt$job_class,
      service_class_index: jmt$service_class_index;

    status.normal := TRUE;

{ Verify job classes to be deleted.

    IF deleted_job_classes <> $jmt$job_class_set [] THEN
      FOR job_class_index := jmc$system_job_class TO jmc$maximum_job_classes DO
        IF job_class_index IN deleted_job_classes THEN
          IF (job_class_index > jmv$maximum_job_class_in_use) OR
                (NOT jmv$job_class_table_p^ [job_class_index].defined) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_job_class,
                  status);
            osp$append_status_integer (osc$status_parameter_delimiter, job_class_index, 10, FALSE, status);
            RETURN;
          ELSEIF job_class_index < jmc$lowest_site_job_class THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$no_delete_of_default_class, jmc$smt_job_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter,
                  jmv$job_class_table_p^ [job_class_index].name, status);
            RETURN;
          IFEND;
        IFEND;
      FOREND;
    IFEND;

{ Verify service classes to be deleted.

    IF deleted_service_classes <> $jmt$service_class_set [] THEN
      FOR service_class_index := jmc$system_service_class TO jmc$maximum_service_classes DO
        IF service_class_index IN deleted_service_classes THEN
          IF (service_class_index > jmv$max_service_class_in_use) OR
                (jmv$service_classes [service_class_index] = NIL) OR
                (NOT jmv$service_classes [service_class_index]^.attributes.defined) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined,
                  jmc$smt_service_class, status);
            osp$append_status_integer (osc$status_parameter_delimiter, service_class_index, 10, FALSE,
                  status);
            RETURN;
          ELSEIF service_class_index < jmc$lowest_site_service_class THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$no_delete_of_default_class,
                  jmc$smt_service_class, status);
            osp$append_status_parameter (osc$status_parameter_delimiter,
                  jmv$service_classes [service_class_index]^.attributes.name, status);
            RETURN;
          IFEND;
        IFEND;
      FOREND;
    IFEND;

{ Verify applications to be deleted.

    IF deleted_applications <> $jmt$application_set [] THEN
      FOR application_index := 1 TO jmc$maximum_application_index DO
        IF application_index IN deleted_applications THEN
          IF jmv$application_table_p = NIL THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_application,
                  status);
            osp$append_status_integer (osc$status_parameter_delimiter, application_index, 10, FALSE, status);
            RETURN;
          ELSEIF (application_index > UPPERBOUND (jmv$application_table_p^)) OR
                (NOT jmv$application_table_p^ [application_index].defined) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_application,
                  status);
            osp$append_status_integer (osc$status_parameter_delimiter, application_index, 10, FALSE, status);
            RETURN;
          IFEND;
        IFEND;
      FOREND;
    IFEND;

  PROCEND verify_classes_to_be_deleted;
?? TITLE := 'verify_classes_to_be_moved', EJECT ??

{ PURPOSE:
{   The purpose of this request is to verify that the job classes whose input
{   queues are to be moved to the UNASSIGNED job class are defined in the
{   job class table.

  PROCEDURE verify_classes_to_be_moved
    (    move_job_classes: jmt$job_class_set;
     VAR status: ost$status);

    VAR
      job_class_index: jmt$job_class;

    status.normal := TRUE;

    FOR job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
      IF job_class_index IN move_job_classes THEN
        IF NOT jmv$job_class_table_p^ [job_class_index].defined THEN
          osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_job_class,
                status);
          osp$append_status_integer (osc$status_parameter_delimiter, job_class_index, 10, FALSE, status);
          RETURN;
        IFEND;
      IFEND;
    FOREND;

  PROCEND verify_classes_to_be_moved;
?? TITLE := 'verify_job_classes_in_profile', EJECT ??

{ PURPOSE:
{   The purpose of this request is to verify that job classes in the profile
{   to be installed contain valid job class definitions that are consistent
{   with the active job class table.
{ DESIGN:
{   Verify that the job classes to be installed have a unique name,
{   abbreviation, and class index and that existing classes defined in
{   the job class table retain the same index.

  PROCEDURE verify_job_classes_in_profile
    (    job_class_entries_p: ^jmt$job_class_table;
         deleted_job_classes: jmt$job_class_set;
     VAR status: ost$status);

    VAR
      current_job_class_index: jmt$job_class,
      entry: ost$non_negative_integers,
      existing_job_classes: jmt$job_class_set,
      job_class_abbreviation: jmt$job_class_name,
      job_class_index: jmt$job_class,
      job_class_name: jmt$job_class_name,
      local_status: ost$status,
      next_entry: ost$non_negative_integers;

    status.normal := TRUE;
    existing_job_classes := $jmt$job_class_set [];

    IF job_class_entries_p <> NIL THEN

      FOR entry := 1 TO UPPERBOUND (job_class_entries_p^) DO
        job_class_index := job_class_entries_p^ [entry].index;
        job_class_name := job_class_entries_p^ [entry].name;
        job_class_abbreviation := job_class_entries_p^ [entry].abbreviation;
        IF job_class_index = jmc$null_job_class THEN
          osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_job_class,
                status);
          osp$append_status_integer (osc$status_parameter_delimiter, job_class_index, 10, FALSE, status);
          RETURN;
        IFEND;
        IF job_class_index < jmc$lowest_site_job_class THEN
          IF job_class_entries_p^ [entry].automatic_class_selection THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$no_ranking_of_default_class,
                  jmc$smt_job_class, status);
            osp$append_status_parameter (osc$status_parameter_delimiter, job_class_name, status);
            RETURN;
          IFEND;
        IFEND;

{ Verify that an existing job class has the same index and a new job class
{ has a valid index.

        jmp$determine_job_class (job_class_name, current_job_class_index, local_status);
        IF local_status.normal AND (job_class_name = jmv$job_class_table_p^ [current_job_class_index].name)
              THEN
          IF job_class_index = current_job_class_index THEN
            existing_job_classes := existing_job_classes + $jmt$job_class_set [job_class_index];
          ELSE
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_conflict, jmc$smt_job_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter, job_class_name, status);
            RETURN;
          IFEND;
        ELSEIF jmv$job_class_table_p^ [job_class_index].defined THEN
          IF NOT (job_class_index IN deleted_job_classes) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_already_in_use, jmc$smt_job_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter,
                  jmv$job_class_table_p^ [job_class_index].name, status);
            osp$append_status_integer (osc$status_parameter_delimiter, job_class_index, 10, FALSE, status);
            RETURN;
          IFEND;
        IFEND;

{ Verify the uniqueness of the job class name, abbreviation, and index.

        next_entry := entry + 1;

      /verify_uniqueness/
        WHILE next_entry < UPPERBOUND (job_class_entries_p^) DO
          IF (job_class_name = job_class_entries_p^ [next_entry].name) OR
                (job_class_name = job_class_entries_p^ [next_entry].abbreviation) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_or_appl_not_unique, jmc$smt_job_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter, job_class_name, status);
            RETURN;
          ELSEIF (job_class_abbreviation <> jmc$null_class_name) AND
                ((job_class_abbreviation = job_class_entries_p^ [next_entry].name) OR
                (job_class_abbreviation = job_class_entries_p^ [next_entry].abbreviation)) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_abbrev_not_unique, jmc$smt_job_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter, job_class_abbreviation, status);
            RETURN;
          ELSEIF job_class_index = job_class_entries_p^ [next_entry].index THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_already_in_use, jmc$smt_job_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter, job_class_name, status);
            osp$append_status_integer (osc$status_parameter_delimiter, job_class_index, 10, FALSE, status);
            RETURN;
          IFEND;
          next_entry := next_entry + 1;
        WHILEND /verify_uniqueness/;

      FOREND;
    IFEND;

{ Check for any classes in the job class table which are not accounted for in the
{ profile being installed.

    FOR job_class_index := jmc$system_job_class TO jmv$maximum_job_class_in_use DO
      IF jmv$job_class_table_p^ [job_class_index].defined THEN
        IF NOT ((job_class_index IN deleted_job_classes) OR (job_class_index IN existing_job_classes)) THEN
          osp$set_status_abnormal (jmc$job_management_id, jme$excess_class_in_sched_table, jmc$smt_job_class,
                status);
          osp$append_status_parameter (osc$status_parameter_delimiter,
                jmv$job_class_table_p^ [job_class_index].name, status);
          RETURN;
        IFEND;
      IFEND;
    FOREND;

  PROCEND verify_job_classes_in_profile;
?? TITLE := 'verify_profile_to_be_installed', EJECT ??

{ PURPOSE:
{   The purpose of this request is to verify that the profile to be installed
{   contains valid scheduler table definitions and is consistent with the
{   active scheduler tables.
{ DESIGN:
{   Verify that the classes and applications to be deleted are valid for
{   deletion.  Verify that job classes whose input queues are to be moved
{   are defined in the job class table.  Verify that the job and service
{   classes to be installed contain valid class definitions that are
{   consistent with the active job and service class tables.  Verify that
{   the applications to be installed contain valid application definitions.

  PROCEDURE verify_profile_to_be_installed
    (    job_class_entries_p: ^jmt$job_class_table;
         service_class_entries_p: ^jmt$service_class_table;
         application_entries_p: ^jmt$application_table;
         move_job_classes: jmt$job_class_set;
         deleted_job_classes: jmt$job_class_set;
         deleted_service_classes: jmt$service_class_set;
         deleted_applications: jmt$application_set;
     VAR status: ost$status);

    status.normal := TRUE;

    verify_classes_to_be_deleted (deleted_job_classes, deleted_service_classes, deleted_applications, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    verify_classes_to_be_moved (move_job_classes, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    verify_job_classes_in_profile (job_class_entries_p, deleted_job_classes, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    verify_serv_classes_in_profile (service_class_entries_p, deleted_service_classes, job_class_entries_p,
          application_entries_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    verify_applications_in_profile (application_entries_p, status);

  PROCEND verify_profile_to_be_installed;
?? TITLE := 'verify_serv_classes_in_profile', EJECT ??

{ PURPOSE:
{   The purpose of this request is to verify that service classes in the profile
{   to be installed contain valid service class definitions that are consistent
{   with the active service class table.
{ DESIGN:
{   Verify that the service classes to be installed will fit in the service class
{   table space allocated during deadstart.  Verify that they have a unique
{   name, abbreviation, and class index and that existing classes defined in
{   the service class table retain the same index.  Verify that all service
{   classes referenced by job classes, other service classes, and applications
{   are defined in the profile.

  PROCEDURE verify_serv_classes_in_profile
    (    service_class_entries_p: ^jmt$service_class_table;
         deleted_service_classes: jmt$service_class_set;
         job_class_entries_p: ^jmt$job_class_table;
         application_entries_p: ^jmt$application_table;
     VAR status: ost$status);

    VAR
      all_service_classes: jmt$service_class_set,
      current_service_class_index: jmt$service_class_index,
      entry: ost$non_negative_integers,
      existing_service_classes: jmt$service_class_set,
      local_status: ost$status,
      next_entry: ost$non_negative_integers,
      service_class_abbreviation: jmt$service_class_name,
      service_class_index: jmt$service_class_index,
      service_class_name: jmt$service_class_name,
      unknown_service_class_index: jmt$service_class_index;

    status.normal := TRUE;
    all_service_classes := $jmt$service_class_set [];
    existing_service_classes := $jmt$service_class_set [];

    IF service_class_entries_p <> NIL THEN
      service_class_index := 0;
      FOR entry := 1 TO UPPERBOUND (service_class_entries_p^) DO
        IF (service_class_entries_p^ [entry].index > service_class_index) THEN
          service_class_index := service_class_entries_p^ [entry].index;
        IFEND;
      FOREND;

      FOR entry := 1 TO UPPERBOUND (service_class_entries_p^) DO
        service_class_index := service_class_entries_p^ [entry].index;
        service_class_name := service_class_entries_p^ [entry].name;
        service_class_abbreviation := service_class_entries_p^ [entry].abbreviation;
        all_service_classes := all_service_classes + $jmt$service_class_set [service_class_index];
        IF service_class_index = jmc$null_service_class THEN
          osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_service_class,
                status);
          osp$append_status_integer (osc$status_parameter_delimiter, service_class_index, 10, FALSE, status);
          RETURN;
        IFEND;

{ Verify that an existing service class has the same index and a new service
{ class has a valid index.

        jmp$determine_service_class (service_class_name, current_service_class_index, local_status);
        IF (local_status.normal) AND (service_class_name = jmv$service_classes [current_service_class_index]^.
              attributes.name) THEN
          IF service_class_index = current_service_class_index THEN
            existing_service_classes := existing_service_classes + $jmt$service_class_set
                  [service_class_index];
          ELSE
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_conflict, jmc$smt_service_class,
                  status);
            osp$append_status_parameter (osc$status_parameter_delimiter, service_class_name, status);
            RETURN;
          IFEND;
        ELSEIF (jmv$service_classes [service_class_index] <> NIL) AND
              jmv$service_classes [service_class_index]^.attributes.defined THEN
          IF NOT (service_class_index IN deleted_service_classes) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_already_in_use,
                  jmc$smt_service_class, status);
            osp$append_status_parameter (osc$status_parameter_delimiter,
                  jmv$service_classes [service_class_index]^.attributes.name, status);
            osp$append_status_integer (osc$status_parameter_delimiter, service_class_index, 10, FALSE,
                  status);
            RETURN;
          IFEND;
        IFEND;

{ Verify the uniqueness of the service class name, abbreviation, and index.

        next_entry := entry + 1;

      /verify_uniqueness/
        WHILE next_entry < UPPERBOUND (service_class_entries_p^) DO
          IF (service_class_name = service_class_entries_p^ [next_entry].name) OR
                (service_class_name = service_class_entries_p^ [next_entry].abbreviation) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_or_appl_not_unique,
                  jmc$smt_service_class, status);
            osp$append_status_parameter (osc$status_parameter_delimiter, service_class_name, status);
            RETURN;
          ELSEIF (service_class_abbreviation <> jmc$null_class_name) AND
                ((service_class_abbreviation = service_class_entries_p^ [next_entry].name) OR
                (service_class_abbreviation = service_class_entries_p^ [next_entry].abbreviation)) THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_abbrev_not_unique,
                  jmc$smt_service_class, status);
            osp$append_status_parameter (osc$status_parameter_delimiter, service_class_abbreviation, status);
            RETURN;
          ELSEIF service_class_index = service_class_entries_p^ [next_entry].index THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$class_index_already_in_use,
                  jmc$smt_service_class, status);
            osp$append_status_parameter (osc$status_parameter_delimiter, service_class_name, status);
            osp$append_status_integer (osc$status_parameter_delimiter, service_class_index, 10, FALSE,
                  status);
            RETURN;
          IFEND;
          next_entry := next_entry + 1;
        WHILEND /verify_uniqueness/;

      FOREND;
    IFEND;

{ Check for any classes in the service class table which are not accounted for in the
{ profile being installed.

    FOR service_class_index := jmc$system_service_class TO jmv$max_service_class_in_use DO
      IF (jmv$service_classes [service_class_index] <> NIL) AND
            jmv$service_classes [service_class_index]^.attributes.defined THEN
        IF NOT ((service_class_index IN deleted_service_classes) OR
              (service_class_index IN existing_service_classes)) THEN
          osp$set_status_abnormal (jmc$job_management_id, jme$excess_class_in_sched_table,
                jmc$smt_service_class, status);
          osp$append_status_parameter (osc$status_parameter_delimiter,
                jmv$service_classes [service_class_index]^.attributes.name, status);
          RETURN;
        IFEND;
      IFEND;
    FOREND;

{ Check that service classes referenced by job classes, other service classes,
{ and applications are defined in the profile.

    unknown_service_class_index := jmc$null_service_class;

    IF job_class_entries_p <> NIL THEN

    /check_job_class_references/
      FOR entry := 1 TO UPPERBOUND (job_class_entries_p^) DO
        IF NOT (job_class_entries_p^ [entry].initial_service_class_index IN all_service_classes) THEN
          unknown_service_class_index := job_class_entries_p^ [entry].initial_service_class_index;
        IFEND;
      FOREND /check_job_class_references/;
    IFEND;

    IF service_class_entries_p <> NIL THEN

    /check_service_class_references/
      FOR entry := 1 TO UPPERBOUND (service_class_entries_p^) DO
        IF service_class_entries_p^ [entry].next_service_class_index <> jmc$null_service_class THEN
          IF NOT (service_class_entries_p^ [entry].next_service_class_index IN all_service_classes) THEN
            unknown_service_class_index := service_class_entries_p^ [entry].next_service_class_index;
          IFEND;
        IFEND;
      FOREND /check_service_class_references/;
    IFEND;

    IF application_entries_p <> NIL THEN

    /check_application_references/
      FOR entry := 1 TO UPPERBOUND (application_entries_p^) DO
        IF application_entries_p^ [entry].service_class_index <> jmc$null_service_class THEN
          IF NOT (application_entries_p^ [entry].service_class_index IN all_service_classes) THEN
            unknown_service_class_index := application_entries_p^ [entry].service_class_index;
          IFEND;
        IFEND;
      FOREND /check_application_references/;
    IFEND;

    IF unknown_service_class_index <> jmc$null_service_class THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$class_index_not_defined, jmc$smt_service_class,
            status);
      osp$append_status_integer (osc$status_parameter_delimiter, unknown_service_class_index, 10, FALSE,
            status);
    IFEND;

  PROCEND verify_serv_classes_in_profile;

MODEND jmm$job_scheduler_utility_r3;
