?? NEWTITLE := 'NOS/VE Job Scheduling : load system profile' ??
MODULE jmm$load_system_profile;

{ PURPOSE:
{   This module contains the logic to load a new scheduling profile
{   into the system tables (activate) and to reload a previously activated
{   profile at system startup.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmc$class_names
*copyc jmc$job_management_id
*copyc jmc$sched_profile_deadstart_id
*copyc jmc$status_message_text
*copyc jme$activate_profile_errors
*copyc jme$job_scheduler_conditions
*copyc jme$queued_file_conditions
*copyc jmt$application_index
*copyc jmt$job_class
*copyc jmt$profile_changes
?? POP ??
*copyc amp$return
*copyc jmp$abort_deadstart
*copyc jmp$build_profile_from_system
*copyc jmp$build_tables_from_profile
*copyc jmp$clear_utility_active
*copyc jmp$change_profile_cycle
*copyc jmp$delete_profile_cycle
*copyc jmp$get_defined_classes
*copyc jmp$get_input_q_from_unassigned
*copyc jmp$get_length_of_sched_tables
*copyc jmp$get_object
*copyc jmp$get_scheduler_table
*copyc jmp$install_profile
*copyc jmp$internal_error
*copyc jmp$reactivate_job_leveling
*copyc jmp$read_profile
*copyc jmp$read_system_profile
*copyc jmp$resubmit_queued_input_job
*copyc jmp$set_profile
*copyc jmp$set_utility_active
*copyc jmp$update_profile
*copyc jmp$write_system_profile
*copyc osp$generate_message
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$append_status_file
*copyc osp$append_status_integer
*copyc pmp$get_mainframe_id

*copyc jmv$new_profile
*copyc jmv$object_definition
*copyc jmv$object_heap
*copyc jmv$the_profile
*copyc jmv$working_storage
?? TITLE := 'Global Declarations Declared by This Module', EJECT ??

  CONST
    cycle_1 = 1,
    cycle_2 = 2;

  VAR
    utility_active: boolean := FALSE,
    profile_access_id: ost$binary_unique_name;

  TYPE
    object_set = jmt$job_class_set,
    comparison_summary = record
      any_changes: boolean,
      maximum_index: integer,
      deleted_objects: object_set,
      modified_objects: object_set,
      all_old_objects: object_set,
      all_new_objects: object_set,
    recend;

  VAR
    recovery_case: (system_failed_while_moving_jobs, everything_was_ok,
          system_failed_during_update, no_valid_profile);

  VAR
    the_changes: jmt$profile_changes,
    categories_modified: boolean;

?? TITLE := 'compare_objects', EJECT ??

{ PURPOSE:
{   This routine compares the objects (classes, categories) from the new
{   profile of the specified type with the objects from the installed
{   profile.  From this comparison it
{     o determines which objects have been deleted - an object with that
{       name cannot be found.
{     o determines which objects have been modified - an object with that
{       name was found but the definition_id is different.
{     o Determines which objects are new.
{     o assigns indicies to the new objects.

  PROCEDURE compare_objects
    (    kind: jmt$profile_object_kinds;
         assign_new_indicies: boolean;
     VAR summary: comparison_summary);

    VAR
      j: integer,
      index: integer,
      this_object: object_set,
      maximum_index: integer,
      display_name: ^ost$name,
      display_sequence: ^SEQ ( * ),
      new_object: jmt$profile_object_reference,
      old_object: jmt$profile_object_reference;

    summary.any_changes := FALSE;
    summary.modified_objects := $object_set [];
    summary.deleted_objects := $object_set [];
    summary.all_old_objects := $object_set [];
    summary.all_new_objects := $object_set [];

    display_sequence := jmv$working_storage;
    the_changes.objects_changed [kind].new_objects := NIL;
    the_changes.objects_changed [kind].deleted_objects := NIL;
    the_changes.objects_changed [kind].changed_objects := NIL;
    j := 0;

    new_object := jmv$new_profile.objects [kind];
    WHILE new_object <> NIL DO
      old_object := jmv$the_profile.objects [kind];
      new_object^.index := 0;

    /find_matching_old_object/
      WHILE (old_object <> NIL) DO
        IF old_object^.name = new_object^.name THEN
          IF NOT (old_object^.index IN summary.all_new_objects) THEN
            new_object^.index := old_object^.index;
            this_object := $object_set [new_object^.index];
            summary.all_new_objects := summary.all_new_objects + this_object;
            IF old_object^.definition_id <> new_object^.definition_id THEN
              summary.any_changes := TRUE;
              summary.modified_objects := summary.modified_objects +
                    this_object;
              NEXT display_name IN display_sequence;
              IF display_name = NIL THEN
                jmp$internal_error (120);
              IFEND;
              display_name^ := old_object^.name;
              j := j + 1;
            IFEND;
            EXIT /find_matching_old_object/;
          IFEND;
        IFEND;
        old_object := old_object^.next_object;
      WHILEND /find_matching_old_object/;
      new_object := new_object^.next_object;
    WHILEND;

    IF j > 0 THEN
      NEXT the_changes.objects_changed [kind].changed_objects: [1 .. j] IN
            jmv$working_storage;
      j := 0;
    IFEND;

    old_object := jmv$the_profile.objects [kind];
    WHILE old_object <> NIL DO
      this_object := $object_set [old_object^.index];
      summary.all_old_objects := summary.all_old_objects + this_object;
      IF NOT (old_object^.index IN summary.all_new_objects) THEN
        summary.deleted_objects := summary.deleted_objects + this_object;
        summary.any_changes := TRUE;
        NEXT display_name IN display_sequence;
        IF display_name = NIL THEN
          jmp$internal_error (121);
        IFEND;
        display_name^ := old_object^.name;
        j := j + 1;
      IFEND;
      old_object := old_object^.next_object;
    WHILEND;

    IF j > 0 THEN
      NEXT the_changes.objects_changed [kind].deleted_objects: [1 .. j] IN
            jmv$working_storage;
      j := 0;
    IFEND;

    IF assign_new_indicies THEN
      summary.all_new_objects := $object_set [];
    IFEND;

    index := 1;
    maximum_index := 1;
    new_object := jmv$new_profile.objects [kind];
    WHILE new_object <> NIL DO
      new_object^.changed := TRUE;
      IF new_object^.index = 0 THEN
        summary.any_changes := TRUE;
        WHILE index IN summary.all_new_objects DO
          index := index + 1;
        WHILEND;
        new_object^.index := index;
        index := index + 1;

        NEXT display_name IN display_sequence;
        IF display_name = NIL THEN
          jmp$internal_error (122);
        IFEND;
        display_name^ := new_object^.name;
        j := j + 1;
      ELSEIF assign_new_indicies THEN
        summary.any_changes := summary.any_changes OR
              (new_object^.index <> index);
        new_object^.index := index;
        index := index + 1;
      ELSEIF new_object^.index > maximum_index THEN
        maximum_index := new_object^.index;
      IFEND;
      summary.all_new_objects := summary.all_new_objects +
            $object_set [new_object^.index];
      new_object := new_object^.next_object;
    WHILEND;

    IF j > 0 THEN
      NEXT the_changes.objects_changed [kind].new_objects: [1 .. j] IN
            jmv$working_storage;
    IFEND;

    IF index > maximum_index THEN
      maximum_index := index - 1;
    IFEND;
    summary.maximum_index := maximum_index;

  PROCEND compare_objects;
?? TITLE := 'determine_extent_of_change', EJECT ??

{ PURPOSE:
{   This routine determines exactly how much the new profile is different from
{   the installed profile. This routine also assigns the indicies for the
{   job and service classes, applications, and categories.
{
{ DESIGN:
{   The routine compares the job classes, service classes, and categories
{   between the installed and new profile to determine the changes and
{   to assign the indicies.  The applications on the new profile are
{   sorted and assigned sequential indicies.  All applications on the old
{   profile are assumed to be deleted since it is easier to delete all
{   applications and install the new list fresh.
{
{ NOTES:
{   The assignment of indicies for applications is done after the compare
{   since they are always kept in alphabetical order in the system tables.

  PROCEDURE determine_extent_of_change
    (VAR job_class_summary: comparison_summary;
     VAR deleted_service_classes: jmt$service_class_set;
     VAR deleted_applications: jmt$application_set;
     VAR category_changes: boolean;
     VAR status: ost$status);

    VAR
      assign_new_indicies: boolean,
      new_object: jmt$profile_object_reference,
      object_kind: jmt$profile_object_kinds,
      object_summary: comparison_summary,
      old_object: jmt$profile_object_reference;

    VAR
      maximum_job_classes: jmt$job_class,
      maximum_job_class_index: jmt$job_class,
      maximum_service_classes: jmt$service_class_index,
      maximum_service_class_index: jmt$service_class_index,
      maximum_applications: jmt$application_index,
      maximum_categories: integer;

    jmp$get_length_of_sched_tables (maximum_job_classes,
          maximum_job_class_index, maximum_service_classes,
          maximum_service_class_index, maximum_applications,
          maximum_categories, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF jmv$new_profile.count [jmc$profile_job_class] > maximum_job_classes THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$profile_is_too_large,
            jmc$smt_job_classes, status);
      osp$append_status_integer (osc$status_parameter_delimiter,
            jmv$new_profile.count [jmc$profile_job_class], 10, FALSE, status);
      osp$append_status_integer (osc$status_parameter_delimiter,
            maximum_job_classes, 10, FALSE, status);
      RETURN;
    ELSEIF jmv$new_profile.count [jmc$profile_service_class] >
          maximum_service_classes THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$profile_is_too_large,
            jmc$smt_service_classes, status);
      osp$append_status_integer (osc$status_parameter_delimiter,
            jmv$new_profile.count [jmc$profile_service_class], 10, FALSE,
            status);
      osp$append_status_integer (osc$status_parameter_delimiter,
            maximum_service_classes, 10, FALSE, status);
      RETURN;
    ELSEIF jmv$new_profile.count [jmc$profile_application] >
          maximum_applications THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$profile_is_too_large,
            jmc$smt_applications, status);
      osp$append_status_integer (osc$status_parameter_delimiter,
            jmv$new_profile.count [jmc$profile_application], 10, FALSE,
            status);
      osp$append_status_integer (osc$status_parameter_delimiter,
            maximum_applications, 10, FALSE, status);
      RETURN;
    IFEND;

    sort_objects (jmv$new_profile.objects [jmc$profile_application]);

    FOR object_kind := LOWERVALUE (object_kind) TO UPPERVALUE (object_kind) DO
      assign_new_indicies := (object_kind = jmc$profile_category) OR
            (object_kind = jmc$profile_application);
      compare_objects (object_kind, assign_new_indicies, object_summary);
      CASE object_kind OF
      = jmc$profile_service_class =
        deleted_service_classes := object_summary.deleted_objects;

      = jmc$profile_job_class =
        job_class_summary := object_summary;

      = jmc$profile_category =
        category_changes := object_summary.any_changes;

      = jmc$profile_application =
        deleted_applications := object_summary.all_old_objects;

      ELSE
      CASEND;
    FOREND
  PROCEND determine_extent_of_change;
?? TITLE := 'disable_unassigned_job_class', EJECT ??

  PROCEDURE disable_unassigned_job_class;

    VAR
      job_class: jmt$profile_object_reference;

    job_class := jmv$new_profile.objects [jmc$profile_job_class];
    WHILE job_class^.name <> jmc$unassigned_class_name DO
      job_class := job_class^.next_object;
    WHILEND;

    IF job_class^.attributes.kind = jmc$type THEN
      job_class^.attributes.attribute_list^ [jmc$jc_enable_class_initiation].
            kind := jmc$boolean;
      job_class^.attributes.attribute_list^ [jmc$jc_enable_class_initiation].
            bool := FALSE;
    IFEND;
  PROCEND disable_unassigned_job_class;
?? TITLE := 'find_classes_to_move_jobs_from', EJECT ??

{ PURPOSE:
{   Determine which classes contain jobs which potentially will end up in
{   a different class after the profile is installed.
{
{ DESIGN:
{   A job may end up in a different class if the job class it is queued in
{     o no longer exists on the new profile.
{     o has a new definition id signifying that the membership attributes
{       have been modified.
{     o is an automatic membership candidate and the class is further down
{       in the list of classes - a class with automatic membership has been
{       inserted in front of this class so this job may end up in the
{       inserted class when resubmitted.

  PROCEDURE find_classes_to_move_jobs_from
    (    category_changes: boolean;
     VAR classes_to_move_jobs: jmt$job_class_set;
     VAR job_class_summary: comparison_summary);

    VAR
      j: integer,
      display_name: ^ost$name,
      display_sequence: ^SEQ ( * ),
      index: jmt$job_class,
      auto_class_selection: jmt$object_attribute,
      target: object_set,
      order: array [jmt$job_class] of jmt$job_class,
      new_object: jmt$profile_object_reference,
      the_object: jmt$profile_object_reference;

    IF category_changes THEN
      classes_to_move_jobs := job_class_summary.all_old_objects;
    ELSE

      new_object := jmv$new_profile.objects [jmc$profile_job_class];
      WHILE new_object <> NIL DO
        new_object^.profile_index := UPPERVALUE (jmt$job_class);
        new_object := new_object^.next_object;
      WHILEND;

{ Select all classes whos definition has been modified or deleted

      classes_to_move_jobs := job_class_summary.modified_objects +
            job_class_summary.deleted_objects;

      FOR index := 1 TO job_class_summary.maximum_index DO
        order [index] := UPPERVALUE (jmt$job_class);
      FOREND;

      index := 1;
      target := job_class_summary.all_old_objects - classes_to_move_jobs;
      the_object := jmv$the_profile.objects [jmc$profile_job_class];
      WHILE the_object <> NIL DO
        IF the_object^.index IN target THEN
          order [the_object^.index] := index;
          index := index + 1;
        IFEND;
        the_object := the_object^.next_object;
      WHILEND;

{ Add in the classes which are automatically selectable and have moved
{ down in the in the list of selectable classes.

      index := 1;
      new_object := jmv$new_profile.objects [jmc$profile_job_class];
      WHILE new_object <> NIL DO
        IF new_object^.attributes.kind = jmc$type THEN
          auto_class_selection := new_object^.attributes.
                attribute_list^ [jmc$jc_auto_class_selection];
          IF (auto_class_selection.kind = jmc$boolean) AND
                auto_class_selection.bool THEN
            IF order [new_object^.index] < index THEN
              classes_to_move_jobs := classes_to_move_jobs +
                    $jmt$job_class_set [new_object^.index];
            ELSE
              index := order [new_object^.index];
            IFEND;
          IFEND;
        IFEND;
        new_object := new_object^.next_object;
      WHILEND;
    IFEND;

    the_changes.move_classes := NIL;

    IF classes_to_move_jobs <> $jmt$job_class_set [] THEN
      j := 0;
      display_sequence := jmv$working_storage;
      the_object := jmv$the_profile.objects [jmc$profile_job_class];
      WHILE the_object <> NIL DO
        IF the_object^.index IN classes_to_move_jobs THEN
          NEXT display_name IN display_sequence;
          IF display_name = NIL THEN
            jmp$internal_error (123);
          IFEND;
          display_name^ := the_object^.name;
          j := j + 1;
        IFEND;
        the_object := the_object^.next_object;
      WHILEND;
      NEXT the_changes.move_classes: [1 .. j] IN jmv$working_storage;
    IFEND;
  PROCEND find_classes_to_move_jobs_from;
?? TITLE := 'install_system_profile', EJECT ??

{ PURPOSE:
{   This request installs the specified profile into the system.
{
{ DESIGN:
{   The routine builds the scheduler tables from the specified profile
{   and calls the ring 3 interface to install the tables.

  PROCEDURE install_system_profile
    (    the_profile: jmt$profile_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
      job_category_data: jmt$job_category_data,
      job_class_table: ^jmt$job_class_table,
      service_class_table: ^jmt$service_class_table,
      application_table: ^jmt$application_table,
      controls_table: jmt$job_scheduler_table;

    jmp$build_tables_from_profile (the_profile, FALSE, job_class_table,
          service_class_table, application_table, controls_table,
          job_category_data, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    jmp$install_profile (profile_access_id, job_class_table,
          service_class_table, application_table, controls_table,
          job_category_data, move_job_classes, deleted_job_classes,
          deleted_service_classes, deleted_applications, delete_profile_cycle2,
          status);

  PROCEND install_system_profile;
?? TITLE := 'find_mainframe_controls', EJECT ??

{ PURPOSE:
{   This interface finds the controls for this mainframe.
{
{ DESIGN:
{   The request makes a sequencial search for the name in the list of objects
{   of the specified type.

  PROCEDURE find_mainframe_controls
    (VAR the_profile: jmt$profile_data;
     VAR status: ost$status);

    VAR
      mainframe_id: pmt$mainframe_id,
      previous_object: jmt$profile_object_reference,
      current_object: jmt$profile_object_reference;

    status.normal := TRUE;

    pmp$get_mainframe_id (mainframe_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Look for a controls object whose name matches the current mainframe name.

    previous_object := NIL;
    current_object := the_profile.objects [jmc$profile_controls];
    WHILE (current_object <> NIL) AND (current_object^.name <> mainframe_id) DO
      previous_object := current_object;
      current_object := current_object^.next_object;
    WHILEND;

    IF current_object = NIL THEN
      osp$set_status_abnormal (jmc$job_management_id,
            jme$no_controls_for_mainframe, mainframe_id, status);
      RETURN;
    IFEND;

    IF previous_object <> NIL THEN
      previous_object^.next_object := current_object^.next_object;
      current_object^.next_object := the_profile.
            objects [jmc$profile_controls];
      the_profile.objects [jmc$profile_controls] := current_object;
    IFEND;

  PROCEND find_mainframe_controls;
?? TITLE := 'resubmit_jobs_in_unassigned', EJECT ??

{ PURPOSE
{   This request resubmits all jobs in the UNASSIGNED job class.
{
{ DESIGN:
{   Fetch list of jobs in UNASSIGNED and resubmit them one by one.

  PROCEDURE resubmit_jobs_in_unassigned
    (VAR status: ost$status);

    VAR
      job_names: ^array [1 .. * ] of jmt$system_supplied_name,
      job_count: integer,
      job_data: ^jmt$resubmitted_job_data,
      display_sequence: ^SEQ ( * ),
      i: integer;

    status.normal := TRUE;

    the_changes.resubmitted_jobs := NIL;

    job_count := 1;
    REPEAT
      PUSH job_names: [1 .. job_count];
      jmp$get_input_q_from_unassigned (job_names^, job_count, status);
      IF NOT status.normal AND (status.condition <> jme$result_array_too_small)
            THEN
        RETURN;
      IFEND;
    UNTIL status.normal;

    display_sequence := jmv$working_storage;

    FOR i := 1 TO job_count DO
      NEXT job_data IN display_sequence;
      IF job_data = NIL THEN
        jmp$internal_error (123);
      IFEND;
      job_data^.job_name := job_names^ [i];
      jmp$resubmit_queued_input_job (job_names^ [i], job_data^.status);
    FOREND;

    IF job_count > 0 THEN
      NEXT the_changes.resubmitted_jobs: [1 .. job_count] IN
            jmv$working_storage;
    IFEND;

  PROCEND resubmit_jobs_in_unassigned;

?? TITLE := 'sort_objects', EJECT ??

{ PURPOSE:
{   This request sorts the objects of the specified type by name.
{
{ DESIGN:
{   Uses merge sort with linked lists.

  PROCEDURE sort_objects
    (VAR the_list: jmt$profile_object_reference);

    VAR
      i: integer,
      merge_list_size: integer,
      old_list: jmt$profile_object_reference,
      sublist_1: jmt$profile_object_reference,
      sublist_2: jmt$profile_object_reference,
      newlist_tail: jmt$profile_object_reference,
      sublist_tail: jmt$profile_object_reference,
      top: jmt$profile_object_reference;

    IF the_list = NIL THEN
      RETURN;
    IFEND;

    PUSH top;
    top^.next_object := the_list;
    merge_list_size := 1;
    WHILE TRUE DO
      old_list := top^.next_object;
      newlist_tail := top;

      WHILE old_list <> NIL DO

        sublist_1 := old_list;

      /peel1/
        FOR i := 1 TO merge_list_size DO
          IF old_list = NIL THEN
            IF newlist_tail = top THEN
              the_list := top^.next_object;
              RETURN;
            IFEND;
            EXIT /peel1/;
          IFEND;
          sublist_tail := old_list;
          old_list := old_list^.next_object;
        FOREND /peel1/;
        sublist_tail^.next_object := NIL;

        sublist_2 := old_list;

      /peel2/
        FOR i := 1 TO merge_list_size DO
          IF old_list = NIL THEN
            EXIT /peel2/;
          IFEND;
          sublist_tail := old_list;
          old_list := old_list^.next_object;
        FOREND /peel2/;
        sublist_tail^.next_object := NIL;

      /merge/
        WHILE (sublist_1 <> NIL) AND (sublist_2 <> NIL) DO
          IF sublist_1^.name < sublist_2^.name THEN
            newlist_tail^.next_object := sublist_1;
            newlist_tail := sublist_1;
            sublist_1 := newlist_tail^.next_object;
          ELSE
            newlist_tail^.next_object := sublist_2;
            newlist_tail := sublist_2;
            sublist_2 := newlist_tail^.next_object;
          IFEND;
        WHILEND /merge/;

        newlist_tail^.next_object := sublist_1;
        WHILE sublist_1 <> NIL DO
          newlist_tail := sublist_1;
          sublist_1 := newlist_tail^.next_object;
        WHILEND;

        newlist_tail^.next_object := sublist_2;
        WHILE sublist_2 <> NIL DO
          newlist_tail := sublist_2;
          sublist_2 := newlist_tail^.next_object;
        WHILEND;

      WHILEND;
      merge_list_size := merge_list_size * 2;
    WHILEND;
  PROCEND sort_objects;
?? TITLE := 'validate_profile', EJECT ??

{ PURPOSE:
{   This request verifies that the internal profile matches the scheduler
{   tables.
{
{ DESIGN:
{   Compare the profile identification, the job class names, and the service
{   class names from the system tables with same data from the profile.

  PROCEDURE validate_profile
    (    profile_identification: ost$name;
     VAR status: ost$status);

    VAR
      i: integer,
      number_of_classes: ost$non_negative_integers,
      call_status: ost$status,
      object_p: jmt$profile_object_reference,
      defined_classes: array [1 .. jmc$maximum_job_classes] of
            jmt$defined_class;

    osp$set_status_abnormal (jmc$job_management_id,
          jme$system_profile_mismatch, ' ', status);

    IF profile_identification <> jmv$the_profile.definition_id THEN
      RETURN;
    IFEND;

    jmp$get_defined_classes (jmc$job_class, defined_classes, number_of_classes,
          call_status);
    IF NOT call_status.normal THEN
      status := call_status;
      RETURN;
    IFEND;

  /job_classes/
    FOR i := 1 TO number_of_classes DO
      object_p := jmv$the_profile.objects [jmc$profile_job_class];
      WHILE object_p <> NIL DO
        IF defined_classes [i].name = object_p^.name THEN
          IF defined_classes [i].index = object_p^.index THEN
            CYCLE /job_classes/;
          IFEND;
          RETURN;
        IFEND;
        object_p := object_p^.next_object;
      WHILEND;
      RETURN;
    FOREND /job_classes/;

    jmp$get_defined_classes (jmc$service_class, defined_classes,
          number_of_classes, call_status);
    IF NOT call_status.normal THEN
      status := call_status;
      RETURN;
    IFEND;

  /service_classes/
    FOR i := 1 TO number_of_classes DO
      object_p := jmv$the_profile.objects [jmc$profile_service_class];
      WHILE object_p <> NIL DO
        IF defined_classes [i].name = object_p^.name THEN
          IF defined_classes [i].index = object_p^.index THEN
            CYCLE /service_classes/;
          IFEND;
          RETURN;
        IFEND;
        object_p := object_p^.next_object;
      WHILEND;
      RETURN;
    FOREND /service_classes/;

    status.normal := TRUE;
  PROCEND validate_profile;
?? TITLE := '[XDCL, #GATE] jmp$activate_system_profile', EJECT ??

{ PURPOSE:
{   This interface attempts to activate the profile on the specified file.
{
{ NOTES:
{   If activating the profile could potentially cause jobs to be resubmitted
{   then the ALLOW_JOB_RECLASSIFICATION flag must be set to allow the profile
{   activation to complete.

  PROCEDURE [XDCL, #GATE] jmp$activate_system_profile
    (    access_id: ost$binary_unique_name;
         new_profile: fst$file_reference;
         allow_job_reclassification: boolean;
     VAR profile_changes: jmt$profile_changes;
     VAR status: ost$status);

    VAR
      deleted_service_classes: jmt$service_class_set,
      deleted_applications: jmt$application_set,
      category_changes: boolean,
      job_class_summary: comparison_summary,
      classes_to_move_jobs: jmt$job_class_set,
      old_object: jmt$profile_object_reference,
      j: integer,
      display_name: ^ost$name,
      local_status: ost$status;

    RESET jmv$working_storage;

    IF NOT utility_active THEN
      osp$set_status_condition (jme$no_utility_is_active, status);
      RETURN;
    IFEND;

    IF access_id <> profile_access_id THEN
      osp$set_status_condition (jme$access_id_mismatch, status);
      RETURN;
    IFEND;

    jmp$read_profile (new_profile, jmv$new_profile, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    find_mainframe_controls (jmv$new_profile, status);
    IF NOT status.normal THEN
      osp$append_status_file (osc$status_parameter_delimiter, new_profile,
            status);
      RETURN;
    IFEND;

    determine_extent_of_change (job_class_summary, deleted_service_classes,
          deleted_applications, category_changes, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    find_classes_to_move_jobs_from (category_changes, classes_to_move_jobs,
          job_class_summary);

    IF classes_to_move_jobs <> $jmt$job_class_set [] THEN
      IF NOT allow_job_reclassification THEN
        osp$set_status_condition (jme$major_profile_change, status);
        RETURN;
      IFEND;
    IFEND;

    profile_changes := the_changes;

    disable_unassigned_job_class;

    jmp$write_system_profile (access_id, jmv$new_profile, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    install_system_profile (jmv$new_profile, classes_to_move_jobs,
          job_class_summary.deleted_objects, deleted_service_classes,
          deleted_applications, TRUE, status);
    IF NOT status.normal THEN
      jmp$delete_profile_cycle (access_id, cycle_1, local_status);
      RETURN;
    IFEND;

    jmp$set_profile (jmv$new_profile);

    jmp$change_profile_cycle (access_id, local_status);
    IF NOT local_status.normal THEN
      osp$generate_message (local_status, status);
    IFEND;

    resubmit_jobs_in_unassigned ({ ignore } local_status);
    profile_changes := the_changes;

    jmp$reactivate_job_leveling (access_id, status);
  PROCEND jmp$activate_system_profile;
?? TITLE := '[XDCL, #GATE] jmp$recover_profile', EJECT ??

{ PURPOSE:
{   This interface attempts to recover a valid profile from the system
{   scheduling profile file.  If the system tables still have the deadstart
{   profile then it installs the profile it recovered otherwise it makes
{   sure that the profile it recovered is the same as the system tables.
{   If there is a mismatch the system tables are used to form a temporary
{   profile and the flag PREVENT_UPDATE_OF_PROFILE_FILE is set to TRUE.

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

    VAR
      profile1_is_valid: boolean,
      profile2_is_valid: boolean,
      scheduler_table: jmt$job_scheduler_table,
      local_status: ost$status,
      ignore: ost$status;

    RESET jmv$working_storage;

    prevent_update_of_profile_file := FALSE;

    jmp$set_utility_active (profile_access_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    access_id := profile_access_id;
    utility_active := TRUE;

    profile2_is_valid := FALSE;
    profile1_is_valid := FALSE;
    jmp$read_system_profile (access_id, cycle_2, jmv$new_profile,
          local_status);
    IF local_status.normal THEN
      find_mainframe_controls (jmv$new_profile, local_status);
      IF local_status.normal THEN
        jmp$set_profile (jmv$new_profile);
        profile2_is_valid := TRUE;
      IFEND;
    IFEND;

    jmp$read_system_profile (access_id, cycle_1, jmv$new_profile,
          local_status);
    IF local_status.normal THEN
      find_mainframe_controls (jmv$new_profile, local_status);
      profile1_is_valid := local_status.normal;
    IFEND;

    IF profile2_is_valid THEN
      IF profile1_is_valid THEN
        recovery_case := system_failed_while_moving_jobs;
        jmp$delete_profile_cycle (access_id, cycle_1, local_status);
      ELSE
        recovery_case := everything_was_ok;
      IFEND;
    ELSE
      IF profile1_is_valid THEN
        recovery_case := system_failed_during_update;
        jmp$delete_profile_cycle (access_id, cycle_2, ignore);
        jmp$change_profile_cycle (access_id, local_status);
        jmp$set_profile (jmv$new_profile);
      ELSE
        recovery_case := no_valid_profile;
      IFEND;
    IFEND;

    jmp$get_scheduler_table (scheduler_table, jmv$working_storage, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF scheduler_table.profile_identification =
          jmc$sched_profile_deadstart_id THEN

      IF recovery_case = no_valid_profile THEN
        jmp$write_system_profile (access_id, jmv$the_profile, status);
        IF NOT status.normal THEN
          jmp$clear_utility_active (profile_access_id, ignore);
          RETURN;
        IFEND;

        install_system_profile (jmv$the_profile, $jmt$job_class_set [],
              $jmt$job_class_set [], $jmt$service_class_set [],
              $jmt$application_set [], FALSE, status);
        IF NOT status.normal THEN
          jmp$clear_utility_active (profile_access_id, ignore);
          IF status.condition = jme$profile_too_large THEN
            jmp$abort_deadstart ('', status, local_status);
          IFEND;
          RETURN;
        IFEND;

        jmp$delete_profile_cycle (access_id, cycle_2, ignore);
        jmp$change_profile_cycle (access_id, status);
        IF NOT status.normal THEN
          jmp$clear_utility_active (profile_access_id, ignore);
          RETURN;
        IFEND;

        osp$set_status_condition (jme$unable_to_recover_profile, local_status);
        osp$generate_message (local_status, ignore);

      ELSE
        install_system_profile (jmv$the_profile, $jmt$job_class_set [],
              $jmt$job_class_set [], $jmt$service_class_set [],
              $jmt$application_set [], FALSE, status);
        IF NOT status.normal THEN
          jmp$clear_utility_active (profile_access_id, ignore);
          IF status.condition = jme$profile_too_large THEN
            jmp$abort_deadstart ('', status, local_status);
          IFEND;
          RETURN;
        IFEND;
      IFEND;
      resubmit_jobs_in_unassigned (status);

    ELSE
      validate_profile (scheduler_table.profile_identification, local_status);
      IF NOT local_status.normal THEN
        IF local_status.condition <> jme$system_profile_mismatch THEN
          jmp$clear_utility_active (profile_access_id, ignore);
          status := local_status;
          RETURN;
        IFEND;

        osp$generate_message (local_status, ignore);
        jmp$build_profile_from_system (jmv$new_profile, status);
        IF NOT status.normal THEN
          jmp$clear_utility_active (profile_access_id, ignore);
          RETURN;
        IFEND;

        jmp$set_profile (jmv$new_profile);
        prevent_update_of_profile_file := TRUE;
      IFEND;

    IFEND;

  PROCEND jmp$recover_profile;
?? TITLE := '[XDCL, #GATE] jmp$update_system_profile', EJECT ??

{ PURPOSE:
{   This request is used to make changes to the system tables that
{   do not involve structural changes.
{
{ NOTE
{   If the flag PREVENT_UPDATE_OF_PROFILE_FILE is TRUE then the tables
{   are updated but no new profile file cycle is written.

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

    VAR
      local_status: ost$status,
      job_category_data: jmt$job_category_data,
      job_class_table: ^jmt$job_class_table,
      service_class_table: ^jmt$service_class_table,
      application_table: ^jmt$application_table,
      controls_table: jmt$job_scheduler_table;

    IF NOT utility_active THEN
      osp$set_status_condition (jme$no_utility_is_active, status);
      RETURN;
    IFEND;

    IF access_id <> profile_access_id THEN
      osp$set_status_condition (jme$access_id_mismatch, status);
      RETURN;
    IFEND;

    RESET jmv$working_storage;

    jmp$build_tables_from_profile (jmv$the_profile, TRUE, job_class_table,
          service_class_table, application_table, controls_table,
          job_category_data, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF prevent_update_of_profile_file THEN
      jmp$update_profile (access_id, job_class_table, service_class_table,
            application_table, ^controls_table, status);
      IF status.normal THEN
        osp$set_status_condition (jme$updated_only_tables, local_status);
        osp$generate_message (local_status, status);
      IFEND;
    ELSE
      jmp$write_system_profile (access_id, jmv$the_profile, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      jmp$update_profile (access_id, job_class_table, service_class_table,
            application_table, ^controls_table, status);
      IF status.normal THEN
        jmp$delete_profile_cycle (access_id, cycle_2, local_status);
        jmp$change_profile_cycle (access_id, status);
      ELSE
        jmp$delete_profile_cycle (access_id, cycle_1, local_status);
      IFEND;
    IFEND;

  PROCEND jmp$update_system_profile;

MODEND jmm$load_system_profile;
