?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE job management job recovery interfaces' ??
MODULE jmm$job_recovery;

{ Purpose: This module contains the job-management job-recovery
{          interfaces.  The interfaces contain routines used for idling the system,
{          resuming the system, and recovery of queues and jobs from and idled state.

{ Design: The process of queue file and job recovery during deadstart is as follows:
{         1.  Construct the Known_Job_List (KJL), Known_Output_List (KOL) and Known_Qfile_List (KQL).
{         2.  Recover Active Jobs.
{         3.  PF reconciliation and System Commit
{         4.  Recover the queues - place each job/output/queue file into the KJL/KOL/KQL.
{
{         The messages that are logged to the system log and job log are message
{         templates.  There is some code that is primarily for debugging that
{         will log non-message template messages to these logs.  This code
{         should never be executed on a customer system.

?? NEWTITLE := 'Global Declarations Referenced by this Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc amt$file_identifier
*copyc amt$local_file_name
*copyc dfc$server_mainframes_catalog
*copyc jmc$job_management_id
*copyc jmc$system_family
*copyc jme$qfile_was_not_recovered
*copyc jme$qfile_was_recovered
*copyc jme$queued_file_conditions
*copyc jmt$jl_restart_file_version
*copyc jmt$jl_restart_job_list
*copyc jmt$job_count_range
*copyc jmt$output_count_range
*copyc jmt$output_counts
*copyc jmt$swap_file_recovery_list
*copyc jmt$swap_file_user_info
*copyc jmt$system_supplied_name
*copyc ost$status
*copyc pmt$family_name_count
*copyc pmt$family_name_list
?? POP ??
*copyc amp$get_file_attributes
*copyc amp$get_segment_pointer
*copyc amp$return
*copyc amp$set_segment_eoi
*copyc clp$put_job_output
*copyc dpp$put_critical_message
*copyc fsp$build_file_ref_from_elems
*copyc fsp$close_file
*copyc fsp$open_file
*copyc i#move
*copyc jmp$all_jobs_swapped_for_idling
*copyc jmp$get_recovery_restart_file
*copyc jmp$rebuild_generic_queue
*copyc jmp$rebuild_input_queue
*copyc jmp$rebuild_output_queue
*copyc jmp$resume_activation_of_jobs
*copyc jmp$set_idle_system_event
*copyc jmp$system_job
*copyc mmp$create_scratch_segment
*copyc mmp$delete_scratch_segment
*copyc osp$append_status_parameter
*copyc osp$generate_log_message
*copyc osp$set_status_abnormal
*copyc pfp$define
*copyc pfp$find_directory_array
*copyc pfp$find_next_info_record
*copyc pfp$get_multi_item_info
*copyc pfp$purge
*copyc pmp$convert_binary_mainframe_id
*copyc pmp$get_family_names
*copyc pmp$get_legible_date_time
*copyc pmp$get_pseudo_mainframe_id
*copyc pmp$get_unique_name
*copyc pmp$log_ascii
*copyc pmp$wait
*copyc qfp$update_last_used_ssn
*copyc syp$display_deadstart_message
*copyc syp$set_system_idling
*copyc syp$system_is_idling
*copyc tmp$ready_system_task1
*copyc dmv$display_recovery_messages
*copyc dmv$ds_msg_update_interval
*copyc jmv$input_file_recovery_option
*copyc jmv$output_file_recovery_option
*copyc jmv$qfile_recovery_option

  CONST
    restarted = 'RESTARTED',
    terminated = 'TERMINATED';

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

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

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

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

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

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

  PROCEDURE sort_directory
    (    directory: pft$p_directory_array);

    VAR
      gap: integer,
      start: integer,
      current: integer,
      swap: pft$directory_array_entry;

{ Use shell sort technique.

    gap := UPPERBOUND (directory^);
    WHILE gap > 1 DO
      gap := 2 * (gap DIV 4) + 1;
      FOR start := 1 TO UPPERBOUND (directory^) - gap DO
        current := start;
        WHILE (current > 0) AND (directory^ [current].name > directory^ [current + gap].name) DO
          swap := directory^ [current];
          directory^ [current] := directory^ [current + gap];
          directory^ [current + gap] := swap;
          current := current - gap;
        WHILEND;
      FOREND;
    WHILEND;
  PROCEND sort_directory;

?? TITLE := 'read_directory', EJECT ??

{
{    The purpose of this request is to read the directory of the catalog
{  specified on the request.  A sequence (file) is used as a placeholder for
{  the directory.  The directory is returned as a pointer to an adaptable
{  array of names.
{
{        READ_DIRECTORY (CATALOG_PATH, SEQUENCE_P, DIRECTORY_ARRAY_P, STATUS);
{
{ CATALOG_PATH: (input) This is the catalog that the directory is being
{        requested for.
{
{ SEQUENCE_P: (input/output) This is a sequence (file) that is used to place
{        the requested information on.
{
{ DIRECTORY_ARRAY_P: (output) This is a pointer to an adaptable array of the
{        names in the catalog's directory.
{
{ STATUS: (output) This is the status of the request.
{

  PROCEDURE read_directory
    (    catalog_path: pft$path;
     VAR sequence_p: ^SEQ ( * );
     VAR directory_array_p: pft$p_directory_array;
     VAR status: ost$status);

    VAR
      group: pft$group,
      info_record_p: pft$p_info_record;

    RESET sequence_p;
    group.group_type := pfc$public;

{ This request places all of the desired information into the sequence

    pfp$get_multi_item_info (catalog_path, group, $pft$catalog_info_selections [],
          $pft$file_info_selections [pfc$file_description], sequence_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ This request validates what has been placed in the sequence

    RESET sequence_p;
    pfp$find_next_info_record (sequence_p, info_record_p, status);
    IF status.normal AND (info_record_p = NIL) THEN
      osp$set_status_abnormal ('JM', jme$unable_to_recover_catalog, catalog_path [1], status);
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ This request converts the information in the sequence to something useful
{ i.e., an array that the caller can recognize.

    pfp$find_directory_array (info_record_p, directory_array_p, status);
    IF directory_array_p <> NIL THEN
      sort_directory (directory_array_p);
    IFEND;
  PROCEND read_directory;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$add_to_server_restart_file', EJECT ??

{ PURPOSE:
{   The purpose of this request is to add a job to a file containing a list of
{   jobs that a server needs to restart or recover using the job's job abort
{   disposition.
{
{ DESIGN:
{   This request opens and closes the recovery restart file every time a job
{ needs to be added.  Generally, it is expected that this request will never
{ be called.  But, even if it is, the number of times it is called is limited
{ by the number of active jobs that have been leveled from the particular
{ server that have a job recovery disposition of restart.  Therefore,
{ performance is not a constraint for this procedure.

  PROCEDURE [XDCL] jmp$add_to_server_restart_file
    (    swap_file_path_p: ^pft$path;
         recover_using_abort_disposition: boolean);

    VAR
      attachment_options_p: ^fst$attachment_options,
      created_server_file: boolean,
      cycle_selector: pft$cycle_selector,
      ignore_contains_data: boolean,
      ignore_file_exists: boolean,
      ignore_previously_opened: boolean,
      ignore_status: ost$status,
      local_status: ost$status,
      recovering_mainframe_id: pmt$binary_mainframe_id,
      restart_file_version_p: ^jmt$jl_restart_file_version,
      restart_job_count_p: ^jmt$job_count_range,
      restart_job_list_p: ^jmt$jl_restart_job_list,
      restart_job_p: ^jmt$jl_restart_job,
      restart_job_sequence_p: ^SEQ ( * ),
      segment_pointer: amt$segment_pointer,
      server_file_identifier: amt$file_identifier,
      server_file_lfn: ost$name,
      server_file_path: fst$path,
      server_file_path_p: ^pft$path,
      server_mainframe_id: pmt$mainframe_id,
      swap_file_attributes_p: ^amt$get_attributes,
      swap_file_path: fst$path,
      swap_file_user_information: jmt$swap_file_user_info;

    PUSH swap_file_attributes_p: [1 .. 1];
    swap_file_attributes_p^ [1].key := amc$user_info;
    fsp$build_file_ref_from_elems (swap_file_path_p, swap_file_path, ignore_status);
    amp$get_file_attributes (swap_file_path, swap_file_attributes_p^, ignore_file_exists,
          ignore_previously_opened, ignore_contains_data, local_status);
    IF NOT local_status.normal THEN
      log_recovery_message (swap_file_path, local_status);
      RETURN;
    IFEND;

    i#move (^swap_file_attributes_p^ [1].user_info, ^swap_file_user_information,
          #SIZE (swap_file_user_information));
    IF swap_file_user_information.version <> jmc$swap_file_version_1 THEN
      local_status.normal := TRUE;
      log_recovery_message ('Invalid swap file version', local_status);
      RETURN;
    IFEND;

{ If this is the job's server mainframe the job has been lost.
{ When deadstart is changed to log sufficient job history information for
{ recovery this should be noted.

    pmp$get_pseudo_mainframe_id (recovering_mainframe_id);
    IF swap_file_user_information.server_mainframe_id = recovering_mainframe_id THEN
      RETURN;
    IFEND;

    pmp$convert_binary_mainframe_id (swap_file_user_information.server_mainframe_id, server_mainframe_id,
          local_status);
    IF NOT local_status.normal THEN
      log_recovery_message ('Swap file has invalid server mainframe id.', local_status);
      RETURN;
    IFEND;

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

{ Create the file to place the swap file restart list in.  If an error is returned
{ assume that the file already exists.

    cycle_selector.cycle_option := pfc$specific_cycle;
    cycle_selector.cycle_number := 1;
    pfp$define (server_file_lfn, server_file_path_p^, cycle_selector, { password } osc$null_name,
          { retention } pfc$maximum_retention, pfc$log, local_status);
    IF local_status.normal THEN
      created_server_file := TRUE;
      amp$return (server_file_lfn, ignore_status);
    ELSE
      created_server_file := FALSE;
      IF local_status.condition = pfe$duplicate_cycle THEN
        local_status.normal := TRUE;
      ELSE
        log_recovery_message ('Got unexpected error attempting to create the restart file.', local_status);
        RETURN;
      IFEND;
    IFEND;

{ Open the server file as a sequence.

    fsp$build_file_ref_from_elems (server_file_path_p, server_file_path, ignore_status);
    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, fsc$shorten, fsc$append, fsc$modify];
    attachment_options_p^ [1].share_modes.selector := fsc$specific_share_modes;
    attachment_options_p^ [1].share_modes.value := $fst$file_access_options [];
    attachment_options_p^ [2].selector := fsc$open_share_modes;
    attachment_options_p^ [2].open_share_modes := $fst$file_access_options [];
    attachment_options_p^ [3].selector := fsc$wait_for_attachment;
    attachment_options_p^ [3].wait_for_attachment.wait := osc$wait;
    attachment_options_p^ [3].wait_for_attachment.wait_time := 300000; { five minutes
    fsp$open_file (server_file_path, amc$segment, attachment_options_p, NIL, NIL, NIL, NIL,
          server_file_identifier, local_status);
    IF NOT local_status.normal THEN
      log_recovery_message (server_file_path, local_status);
      RETURN;
    IFEND;
    amp$get_segment_pointer (server_file_identifier, amc$sequence_pointer, segment_pointer, local_status);
    IF NOT local_status.normal THEN
      log_recovery_message (server_file_path, local_status);
      fsp$close_file (server_file_identifier, ignore_status);
      RETURN;
    IFEND;

{ Add the job to the restart list in the file.

    restart_job_sequence_p := segment_pointer.sequence_pointer;
    RESET restart_job_sequence_p;
    NEXT restart_file_version_p IN restart_job_sequence_p;
    NEXT restart_job_count_p IN restart_job_sequence_p;
    IF created_server_file THEN
      restart_file_version_p^ := jmc$jl_rfv_version_1;
      restart_job_count_p^ := 0;
    ELSE

{ If the restart file version matches, add to the file.  If the restart file
{ version does not match, discard the contents of the file and reformat the
{ file to the new contents.

      IF restart_file_version_p^ = jmc$jl_rfv_version_1 THEN
        NEXT restart_job_list_p: [1 .. restart_job_count_p^] IN restart_job_sequence_p;
      ELSE
        restart_file_version_p^ := jmc$jl_rfv_version_1;
        restart_job_count_p^ := 0;
      IFEND;
    IFEND;
    NEXT restart_job_p IN restart_job_sequence_p;
    restart_job_p^.system_job_name := swap_file_path_p^ [4];
    restart_job_p^.recover_using_abort_disposition := recover_using_abort_disposition;
    restart_job_count_p^ := restart_job_count_p^ +1;

{ Close the file with the correct EOI.

    segment_pointer.sequence_pointer := restart_job_sequence_p;
    amp$set_segment_eoi (server_file_identifier, segment_pointer, local_status);
    IF NOT local_status.normal THEN
      log_recovery_message (server_file_path, local_status);
      fsp$close_file (server_file_identifier, ignore_status);
      RETURN;
    IFEND;

    fsp$close_file (server_file_identifier, local_status);
    IF NOT local_status.normal THEN
      log_recovery_message (server_file_path, local_status);
    IFEND;
  PROCEND jmp$add_to_server_restart_file;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] jmp$idle_system', EJECT ??

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

    VAR
      wait_on_jobs: integer,
      i: integer,
      ignore_status: ost$status;

    status.normal := TRUE;
    ignore_status.normal := TRUE;

    IF NOT jmp$system_job () THEN
      osp$set_status_abnormal ('JM', jme$must_be_system_job, 'jmp$idle_system', status);
      RETURN;
    IFEND;

{ See if the system is idling - if so - return

    IF syp$system_is_idling () THEN
      RETURN;
    IFEND;

{ the following requests:
{  1.  suspend initiation of jobs
{  2.  prohibit jobs from swapping in
{  3.  swap out all active jobs

    jmp$set_idle_system_event;
    i := 0;
    wait_on_jobs := 0;

  /time_check/

    WHILE NOT jmp$all_jobs_swapped_for_idling () DO

{ Put out a message every 10 seconds showing why the system is waiting.

      IF i = 0 THEN
        dpp$put_critical_message ('Wait 10 minutes max for all jobs to swap...', ignore_status);
        wait_on_jobs := wait_on_jobs + 1;
        IF wait_on_jobs > 60 THEN

{ Wait for 10 minutes for all jobs to swap - if not complete by then,
{ somthing is wrong. Force the system to terminate and so that we at the
{ least are able to recover all other jobs.

          dpp$put_critical_message ('Unable to swap all jobs...', ignore_status);
          dpp$put_critical_message ('  some jobs will not be recovered.', ignore_status);
          EXIT /time_check/
        IFEND;
      IFEND;
      pmp$wait (1000, 1000);
      i := (i + 1) MOD 10;
    WHILEND /time_check/;

{ Set the flag to allow interested parties to determine
{ that the system is being idled

    syp$set_system_idling (TRUE);

  PROCEND jmp$idle_system;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$recover_input_queue', EJECT ??

{ PURPOSE:
{   The purpose of this request is to recover a standard input queue catalog.
{

  PROCEDURE [XDCL] jmp$recover_input_queue
    (    family_name: ost$name;
         defer_input_queue: boolean;
     VAR status: ost$status);

    VAR
      directory_array_p: pft$p_directory_array,
      ignore_status: ost$status,
      local_status: ost$status,
      name_index: integer,
      path_p: ^pft$path,
      segment_pointer: amt$segment_pointer,
      system_job_name: jmt$system_supplied_name;

    status.normal := TRUE;
    PUSH path_p: [1 .. 3];
    path_p^ [1] := family_name;
    path_p^ [2] := jmc$system_user;
    path_p^ [3] := jmc$job_input_catalog;

    mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_sequential, segment_pointer, status);
    IF NOT status.normal THEN
      log_recovery_message ('Could not create a scratch segment for queue recovery.', status);
      RETURN;
    IFEND;

{ Read the job input catalog

    read_directory (path_p^, segment_pointer.sequence_pointer, directory_array_p, status);
    IF NOT status.normal THEN
      osp$set_status_abnormal (jmc$job_management_id, jme$unable_to_recover_catalog, jmc$job_input_catalog,
            local_status);
      osp$append_status_parameter (osc$status_parameter_delimiter, family_name, local_status);
      log_recovery_message ('', local_status);
      log_recovery_message ('', status);
      RETURN;
    IFEND;

    IF directory_array_p <> NIL THEN

    /recover_all_files/
      FOR name_index := LOWERBOUND (directory_array_p^) TO UPPERBOUND (directory_array_p^) DO

        system_job_name := directory_array_p^ [name_index].name;

        jmp$rebuild_input_queue (directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size),
              path_p^ [1], path_p^ [3], {recover_using_abort_disposition} FALSE,
              {ignore_client_initiated_jobs} FALSE, defer_input_queue, local_status);

        IF local_status.normal THEN
          osp$set_status_abnormal (jmc$job_management_id, jme$input_was_recovered, system_job_name,
                ignore_status);
          log_recovery_message ('', ignore_status);
        ELSE
          osp$set_status_abnormal (jmc$job_management_id, jme$input_was_not_recovered, system_job_name,
                ignore_status);
          log_recovery_message ('', ignore_status);
          log_recovery_message ('', local_status);
        IFEND;
      FOREND /recover_all_files/;
    IFEND;

  PROCEND jmp$recover_input_queue;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] jmp$recover_queues', EJECT ??
*copyc jmh$recover_queues

  PROCEDURE [XDCL] jmp$recover_queues
    (    swap_file_recovery_list: ^jmt$swap_file_recovery_list;
         swap_file_recovery_list_count: jmt$job_count_range;
     VAR status: ost$status);

    CONST
      second = 1000000;

    VAR
      base: integer,
      clock: integer,
      date: ost$date,
      directory_array_p: pft$p_directory_array,
      display_update_interval: integer,
      family_index: pmt$family_name_count,
      family_name_count: pmt$family_name_count,
      family_name_list: ^pmt$family_name_list,
      ignore_status: ost$status,
      input_recovered_count: integer,
      input_not_recovered_count: integer,
      job_continue_count: integer,
      job_restart_count: integer,
      job_terminate_count: integer,
      length: integer,
      local_status: ost$status,
      log_msg: string (80),
      msg_header_displayed: boolean,
      msg_len: integer,
      name_index: integer,
      path_p: ^pft$path,
      recover_using_abort_disposition: boolean,
      recovery_message: string (80),
      segment_pointer: amt$segment_pointer,
      sequence_p: ^SEQ ( * ),
      swap_file_path_p: ^pft$path,
      swap_file_purge_count: integer,
      swap_index: integer,
      time: ost$time;

?? NEWTITLE := 'delete_swap_file', EJECT ??

    PROCEDURE delete_swap_file
      (    swap_file_path: pft$path;
       VAR status: ost$status);

      VAR
        i: integer,
        cycle_selector: pft$cycle_selector;

      cycle_selector.cycle_option := pfc$lowest_cycle;
      i := 0;

      REPEAT
        i := i + 1;
        pfp$purge (swap_file_path, cycle_selector, osc$null_name, status);
        swap_file_purge_count := swap_file_purge_count + $INTEGER (status.normal);
      UNTIL NOT status.normal;

      IF (status.condition = pfe$unknown_permanent_file) AND (i > 1) THEN
        status.normal := TRUE;
      IFEND;

    PROCEND delete_swap_file;
?? OLDTITLE ??
?? NEWTITLE := 'recover_generic_queue', EJECT ??

    PROCEDURE recover_generic_queue;

      VAR
        date: ost$date,
        ignore_status: ost$status,
        log_msg: string (80),
        msg_len: integer,
        time: ost$time;

      IF dmv$display_recovery_messages THEN
        pmp$get_legible_date_time (osc$mdy_date, date, osc$hms_time, time, ignore_status);

        STRINGREP (log_msg, msg_len, '   .. Recover Generic Queue.                                         ',
              time.hms);
        clp$put_job_output (log_msg (1, msg_len), ignore_status);
      IFEND;

{ Read the generic queue directory
      path_p^ [3] := jmc$generic_queue_catalog;
      read_directory (path_p^, sequence_p, directory_array_p, status);
      IF NOT status.normal THEN
        osp$set_status_abnormal (jmc$job_management_id, jme$unable_to_recover_catalog,
              jmc$generic_queue_catalog, local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, jmc$system_family, local_status);
        log_recovery_message ('', local_status);
        log_recovery_message ('', status);
        RETURN;
      IFEND;

{ Recover the generic queue

      IF directory_array_p <> NIL THEN
        FOR name_index := LOWERBOUND (directory_array_p^) TO UPPERBOUND (directory_array_p^) DO
          jmp$rebuild_generic_queue (directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size),
                local_status);
          IF local_status.normal THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$qfile_was_recovered,
                  directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size), local_status);
            log_recovery_message ('', local_status);

          ELSE
            osp$set_status_abnormal (jmc$job_management_id, jme$qfile_was_not_recovered,
                  directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size), ignore_status);
            log_recovery_message ('', ignore_status);
            log_recovery_message ('', local_status);
          IFEND;
        FOREND;
      IFEND;
    PROCEND recover_generic_queue;
?? OLDTITLE ??
?? NEWTITLE := 'recover_input_queue', EJECT ??

{ PURPOSE:
{   The purpose of this request is to recover a standard input queue catalog.
{
{ DESIGN:
{   Read the names of the files in the input queue.  Compare this list of names with the list of names in
{ swap catalog.  If there is no entry in the swap catalog, then the job is has not been initiated so attempt
{ to recover it.  If there is an entry in the swap catalog there are four possible cases that need to be
{ managed.  If the recovery disposition of the job is not known the job should be recovered under the
{ job_abort_disposition specified when the job was submitted.  If the recovery disposition is CONTINUE then
{ the job has been recovered.  If the recovery disposition is RESTART then delete the swap file and
{ requeue the job.  Otherwise (the recovery disposition is TERMINATE) delete the swap file and the input
{ file.

    PROCEDURE recover_input_queue
      (    family_name: ost$name;
           swap_file_recovery_list_p: ^jmt$swap_file_recovery_list;
           swap_file_recovery_list_count: jmt$job_count_range;
       VAR sequence_p: ^SEQ ( * );
       VAR status: ost$status);

      VAR
        cycle_selector: pft$cycle_selector,
        directory_array_p: pft$p_directory_array,
        ignore_status: ost$status,
        input_file_path_p: ^pft$path,
        length: integer,
        local_status: ost$status,
        name_index: integer,
        path_p: ^pft$path,
        recovery_message: string (80),
        recover_using_abort_disposition: boolean,
        swap_file_path_p: ^pft$path,
        swap_index: integer,
        system_job_name: jmt$system_supplied_name;

      status.normal := TRUE;
      PUSH path_p: [1 .. 3];
      path_p^ [1] := family_name;
      path_p^ [2] := jmc$system_user;
      path_p^ [3] := jmc$job_input_catalog;

      cycle_selector.cycle_option := pfc$specific_cycle;
      cycle_selector.cycle_number := 1;
      PUSH input_file_path_p: [1 .. 4];
      input_file_path_p^ [1] := path_p^ [1];
      input_file_path_p^ [2] := path_p^ [2];
      input_file_path_p^ [3] := path_p^ [3];

      PUSH swap_file_path_p: [1 .. 4];
      swap_file_path_p^ [1] := jmc$system_family;
      swap_file_path_p^ [2] := jmc$system_user;
      swap_file_path_p^ [3] := jmc$job_swap_catalog;

{ Read the job input catalog

      read_directory (path_p^, sequence_p, directory_array_p, status);
      IF NOT status.normal THEN
        osp$set_status_abnormal (jmc$job_management_id, jme$unable_to_recover_catalog, jmc$job_input_catalog,
              local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, family_name, local_status);
        log_recovery_message ('', local_status);
        log_recovery_message ('', status);
        RETURN;
      IFEND;

      IF directory_array_p <> NIL THEN

      /recover_all_files/
        FOR name_index := LOWERBOUND (directory_array_p^) TO UPPERBOUND (directory_array_p^) DO

          system_job_name := directory_array_p^ [name_index].name;
          recover_using_abort_disposition := FALSE;

          IF dmv$display_recovery_messages THEN
            clock := #FREE_RUNNING_CLOCK (0);
            IF clock > (base + display_update_interval) THEN
              IF NOT msg_header_displayed THEN
                clp$put_job_output ('      Recovery Counts --- Job Name ---  Continue Restart Terminate',
                      ignore_status);
                msg_header_displayed := TRUE;
              IFEND;
              base := clock;
              pmp$get_legible_date_time (osc$mdy_date, date, osc$hms_time, time, ignore_status);
              STRINGREP (log_msg, msg_len, '   .. Recover Job: ', system_job_name (1, 19), '  ',
                    job_continue_count: 8, ' ', job_restart_count: 7, ' ', job_terminate_count: 9, '   ',
                    time.hms);
              clp$put_job_output (log_msg (1, msg_len), ignore_status);
            IFEND;
          IFEND;

{ Check to see if the job has a swap file

        /search_swap_file_list/
          FOR swap_index := 1 TO swap_file_recovery_list_count DO
            IF system_job_name = swap_file_recovery_list^ [swap_index].system_job_name THEN

{ The job's command file exists.  This means that the queue recovery knows what to to with
{ the job's swap file.

              swap_file_recovery_list^ [swap_index].command_file_exists := TRUE;
              IF swap_file_recovery_list^ [swap_index].recovery_disposition_available THEN
                CASE swap_file_recovery_list^ [swap_index].job_recovery_disposition OF
                = jmc$continue_on_recovery =
                  osp$set_status_abnormal (jmc$job_management_id, jme$job_was_recovered, system_job_name,
                        local_status);
                  log_recovery_message ('', local_status);
                  job_continue_count := job_continue_count + 1;
                  CYCLE /recover_all_files/;

                = jmc$restart_on_recovery =
                  osp$set_status_abnormal (jmc$job_management_id, jme$job_was_not_recovered, system_job_name,
                        local_status);
                  log_recovery_message ('', local_status);
                  osp$set_status_abnormal (jmc$job_management_id, jme$job_recovery_or_abort_set, restarted,
                        local_status);
                  log_recovery_message ('', local_status);
                  swap_file_path_p^ [4] := system_job_name;
                  delete_swap_file (swap_file_path_p^, local_status);
                  log_recovery_message ('', local_status);
                  job_restart_count := job_restart_count + 1;
                  EXIT /search_swap_file_list/;

                = jmc$terminate_on_recovery =
                  osp$set_status_abnormal (jmc$job_management_id, jme$job_was_not_recovered, system_job_name,
                        local_status);
                  log_recovery_message ('', local_status);
                  osp$set_status_abnormal (jmc$job_management_id, jme$job_recovery_or_abort_set, terminated,
                        local_status);
                  log_recovery_message ('', local_status);
                  swap_file_path_p^ [4] := system_job_name;
                  delete_swap_file (swap_file_path_p^, local_status);
                  log_recovery_message ('', local_status);
                  input_file_path_p^ [4] := system_job_name;
                  pfp$purge (input_file_path_p^, cycle_selector, osc$null_name, local_status);
                  log_recovery_message ('', local_status);
                  job_terminate_count := job_terminate_count + 1;
                  CYCLE /recover_all_files/;
                ELSE
                CASEND;
              ELSE
                recover_using_abort_disposition := TRUE;
                osp$set_status_abnormal (jmc$job_management_id, jme$job_was_not_recovered, system_job_name,
                      local_status);
                log_recovery_message ('', local_status);
                swap_file_path_p^ [4] := system_job_name;
                delete_swap_file (swap_file_path_p^, local_status);
                log_recovery_message ('', local_status);
                EXIT /search_swap_file_list/;
              IFEND;
            IFEND;
          FOREND /search_swap_file_list/;

{ The job either didn't have a swap file or it wasn't recovered - try to recover the command file.

          jmp$rebuild_input_queue (directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size),
                path_p^ [1], path_p^ [3], recover_using_abort_disposition,
                {ignore_client_initiated_jobs} FALSE, {job_deferred_by_operator} FALSE, local_status);

          IF local_status.normal THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$input_was_recovered, system_job_name,
                  ignore_status);
            log_recovery_message ('', ignore_status);
            input_recovered_count := input_recovered_count + 1;
          ELSE
            osp$set_status_abnormal (jmc$job_management_id, jme$input_was_not_recovered, system_job_name,
                  ignore_status);
            log_recovery_message ('', ignore_status);
            log_recovery_message ('', local_status);
            input_not_recovered_count := input_not_recovered_count + 1;
          IFEND;
        FOREND /recover_all_files/;
      IFEND;

    PROCEND recover_input_queue;
?? OLDTITLE ??
?? NEWTITLE := 'recover_output_queue', EJECT ??

    PROCEDURE recover_output_queue;

      VAR
        date: ost$date,
        ignore_status: ost$status,
        log_msg: string (80),
        msg_len: integer,
        time: ost$time;

      IF dmv$display_recovery_messages THEN
        pmp$get_legible_date_time (osc$mdy_date, date, osc$hms_time, time, ignore_status);

        STRINGREP (log_msg, msg_len, '   .. Recover Output Queue.                                          ',
              time.hms);
        clp$put_job_output (log_msg (1, msg_len), ignore_status);
      IFEND;

{ Read the output queue directory
      path_p^ [3] := jmc$job_output_catalog;
      read_directory (path_p^, sequence_p, directory_array_p, status);
      IF NOT status.normal THEN
        osp$set_status_abnormal (jmc$job_management_id, jme$unable_to_recover_catalog, jmc$job_output_catalog,
              local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, jmc$system_family, local_status);
        log_recovery_message ('', local_status);
        log_recovery_message ('', status);
        RETURN;
      IFEND;

{ Recover the output queue

      IF directory_array_p <> NIL THEN
        FOR name_index := LOWERBOUND (directory_array_p^) TO UPPERBOUND (directory_array_p^) DO
          jmp$rebuild_output_queue (directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size),
                path_p^ [3], local_status);
          IF local_status.normal THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$output_was_recovered,
                  directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size), local_status);
            log_recovery_message ('', local_status);

          ELSE
            osp$set_status_abnormal (jmc$job_management_id, jme$output_was_not_recovered,
                  directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size), ignore_status);
            log_recovery_message ('', ignore_status);
            log_recovery_message ('', local_status);
          IFEND;
        FOREND;
      IFEND;
    PROCEND recover_output_queue;
?? OLDTITLE ??
?? NEWTITLE := 'recover_sf_input_queue', EJECT ??

    PROCEDURE recover_sf_input_queue;

{ Read the store-and-forward job input catalog

      path_p^ [1] := jmc$system_family;
      path_p^ [2] := jmc$system_user;
      path_p^ [3] := jmc$sf_job_input_catalog;

      read_directory (path_p^, sequence_p, directory_array_p, status);
      IF NOT status.normal THEN
        osp$set_status_abnormal (jmc$job_management_id, jme$unable_to_recover_catalog,
              jmc$sf_job_input_catalog, local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, jmc$system_family, local_status);
        log_recovery_message ('', local_status);
        log_recovery_message ('', status);
        status.normal := TRUE;
      IFEND;

      IF directory_array_p <> NIL THEN
        FOR name_index := LOWERBOUND (directory_array_p^) TO UPPERBOUND (directory_array_p^) DO

{ Recover the input file.

          jmp$rebuild_input_queue (directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size),
                path_p^ [1], path_p^ [3], {recover_using_abort_disposition} FALSE,
                {ignore_client_initiated_jobs} TRUE, {job_deferred_by_operator} FALSE, local_status);
          IF local_status.normal THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$input_was_recovered,
                  directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size), ignore_status);
            log_recovery_message ('', ignore_status);
            input_recovered_count := input_recovered_count + 1;
          ELSE
            osp$set_status_abnormal (jmc$job_management_id, jme$input_was_not_recovered,
                  directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size), ignore_status);
            log_recovery_message ('', ignore_status);
            log_recovery_message ('', local_status);
            input_not_recovered_count := input_not_recovered_count + 1;
          IFEND;
        FOREND;
      IFEND;
    PROCEND recover_sf_input_queue;
?? OLDTITLE ??
?? NEWTITLE := 'recover_sf_output_queue', EJECT ??

    PROCEDURE recover_sf_output_queue;

{ Read the store and forward output queue directory

      path_p^ [3] := jmc$sf_job_output_catalog;
      read_directory (path_p^, sequence_p, directory_array_p, status);
      IF NOT status.normal THEN
        osp$set_status_abnormal (jmc$job_management_id, jme$unable_to_recover_catalog,
              jmc$sf_job_output_catalog, local_status);
        osp$append_status_parameter (osc$status_parameter_delimiter, jmc$system_family, local_status);
        log_recovery_message ('', local_status);
        log_recovery_message ('', status);
        RETURN;
      IFEND;

{ Recover the store and forward output queue.

      IF directory_array_p <> NIL THEN
        FOR name_index := LOWERBOUND (directory_array_p^) TO UPPERBOUND (directory_array_p^) DO
          jmp$rebuild_output_queue (directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size),
                path_p^ [3], local_status);
          IF local_status.normal THEN
            osp$set_status_abnormal (jmc$job_management_id, jme$output_was_recovered,
                  directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size), ignore_status);
            log_recovery_message ('', ignore_status);

          ELSE
            osp$set_status_abnormal (jmc$job_management_id, jme$output_was_not_recovered,
                  directory_array_p^ [name_index].name (1, jmc$system_supplied_name_size), ignore_status);
            log_recovery_message ('', ignore_status);
            log_recovery_message ('', local_status);
          IFEND;
        FOREND;
      IFEND;
    PROCEND recover_sf_output_queue;
?? OLDTITLE ??
?? EJECT ??

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

{ Initialize Values for Operator Alive Messages.
    display_update_interval := dmv$ds_msg_update_interval * second;
    input_recovered_count := 0;
    input_not_recovered_count := 0;
    job_continue_count := 0;
    job_restart_count := 0;
    job_terminate_count := 0;
    swap_file_purge_count := 0;
    msg_header_displayed := FALSE;
    base := #FREE_RUNNING_CLOCK (0);

{ Create a scratch sequence to use for the perm file interfaces
    mmp$create_scratch_segment (amc$sequence_pointer, mmc$as_sequential, segment_pointer, status);
    IF NOT status.normal THEN
      log_recovery_message ('Could not create a scratch segment for queue recovery.', status);
      RETURN;
    IFEND;

    sequence_p := segment_pointer.sequence_pointer;

{ Do some initial setup before reading any catalogs

    PUSH path_p: [1 .. 3];
    path_p^ [1] := jmc$system_family;
    path_p^ [2] := jmc$system_user;

    IF jmv$output_file_recovery_option = jmc$ofro_recover_all_files THEN
      recover_output_queue;
      recover_sf_output_queue;
    IFEND;

    IF jmv$qfile_recovery_option = jmc$qro_recover_all_files THEN
      recover_generic_queue;
    IFEND;

{ Recover the input queues - only recover those jobs that are not executing
{ or executing job's that indicate they should be restarted.

{ Find the family names on the system.
{ Each family has its own input queue.  Its form is as follows:
{   :<family_name>.$SYSTEM.$JOB_INPUT_QUEUE.

{ Make a guess at the number of family names defined.  The pmp$get_family_names request
{ only returns abnormal status if the result array is too full.

    PUSH family_name_list: [1 .. 10];
    pmp$get_family_names (family_name_list^, family_name_count, local_status);
    IF NOT local_status.normal THEN
      PUSH family_name_list: [1 .. family_name_count];
      pmp$get_family_names (family_name_list^, family_name_count, { ignore } local_status);
    IFEND;

    IF jmv$input_file_recovery_option = jmc$ifro_recover_all_files THEN
      IF dmv$display_recovery_messages THEN
        pmp$get_legible_date_time (osc$mdy_date, date, osc$hms_time, time, ignore_status);

        STRINGREP (log_msg, msg_len, '   .. Recover Input Queue: File Count= ',
              swap_file_recovery_list_count: 6, '                        ', time.hms);
        clp$put_job_output (log_msg (1, msg_len), ignore_status);
      IFEND;

      FOR family_index := 1 TO family_name_count DO
        local_status.normal := TRUE;
        recover_input_queue (family_name_list^ [family_index], swap_file_recovery_list,
              swap_file_recovery_list_count, sequence_p, { ignore } local_status);
      FOREND;
    IFEND;

{ Delete the swap files on any non-recovered jobs that do not have command files.

    PUSH swap_file_path_p: [1 .. 4];
    swap_file_path_p^ [1] := jmc$system_family;
    swap_file_path_p^ [2] := jmc$system_user;
    swap_file_path_p^ [3] := jmc$job_swap_catalog;

    FOR swap_index := 1 TO swap_file_recovery_list_count DO

      IF dmv$display_recovery_messages THEN
        IF NOT msg_header_displayed THEN
          clp$put_job_output ('      Recovery Counts                   Continue Restart Terminate',
                ignore_status);
          msg_header_displayed := TRUE;
        IFEND;
        clock := #FREE_RUNNING_CLOCK (0);
        IF clock > (base + display_update_interval) THEN
          base := clock;
          pmp$get_legible_date_time (osc$mdy_date, date, osc$hms_time, time, ignore_status);
          STRINGREP (log_msg, msg_len, '   .. Recover Job:                      ', job_continue_count: 8, ' ',
                job_restart_count: 7, ' ', job_terminate_count: 9, '   ', time.hms);
          clp$put_job_output (log_msg (1, msg_len), ignore_status);
        IFEND;
      IFEND;

      IF (NOT swap_file_recovery_list^ [swap_index].command_file_exists) THEN
        IF (swap_file_recovery_list^ [swap_index].recovery_disposition_available AND
              (swap_file_recovery_list^ [swap_index].job_recovery_disposition <> jmc$continue_on_recovery)) OR
              (NOT swap_file_recovery_list^ [swap_index].recovery_disposition_available) THEN
          swap_file_path_p^ [4] := swap_file_recovery_list^ [swap_index].system_job_name;
          osp$set_status_abnormal (jmc$job_management_id, jme$job_was_not_recovered, swap_file_path_p^ [4],
                local_status);
          log_recovery_message ('', local_status);
          IF (NOT swap_file_recovery_list^ [swap_index].recovery_disposition_available) OR
                (swap_file_recovery_list^ [swap_index].job_recovery_disposition = jmc$restart_on_recovery)
                THEN
            jmp$add_to_server_restart_file (swap_file_path_p, { recover_using_abort_disposition } NOT
                  swap_file_recovery_list^ [swap_index].recovery_disposition_available);
            osp$set_status_abnormal (jmc$job_management_id, jme$job_recovery_or_abort_set, restarted,
                  local_status);
            log_recovery_message ('', local_status);
            job_restart_count := job_restart_count + 1;
          ELSE
            osp$set_status_abnormal (jmc$job_management_id, jme$job_recovery_or_abort_set, terminated,
                  local_status);
            log_recovery_message ('', local_status);
            job_terminate_count := job_terminate_count + 1;
          IFEND;

          delete_swap_file (swap_file_path_p^, local_status);
          log_recovery_message ('', local_status);
        ELSE
          osp$set_status_abnormal (jmc$job_management_id, jme$job_was_recovered,
                swap_file_recovery_list^ [swap_index].system_job_name, local_status);
          log_recovery_message ('', local_status);
          job_continue_count := job_continue_count + 1;
        IFEND;
      IFEND;
    FOREND;

    IF jmv$input_file_recovery_option = jmc$ifro_recover_all_files THEN
      recover_sf_input_queue;
    IFEND;

    IF dmv$display_recovery_messages THEN
      STRINGREP (log_msg, msg_len, '   Recover Job Summary: Continue: ', job_continue_count: 6, ' Restart: ',
            job_restart_count: 6, ' Terminate: ', job_terminate_count: 6);
      clp$put_job_output (log_msg (1, msg_len), ignore_status);
      STRINGREP (log_msg, msg_len, '   Input Files Recovered:', input_recovered_count: 5, ' Not Recovered:',
            input_not_recovered_count: 5, ' Swap Files Purged:', swap_file_purge_count: 5);
      clp$put_job_output (log_msg (1, msg_len), ignore_status);
    IFEND;

    mmp$delete_scratch_segment (segment_pointer, status);

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

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

    VAR
      output_counts: jmt$output_counts,
      ignore_status: ost$status;

    status.normal := TRUE;
    ignore_status.normal := TRUE;

    IF NOT jmp$system_job () THEN
      osp$set_status_abnormal ('JM', jme$must_be_system_job, 'jmp$resume_system', status);
      RETURN;
    IFEND;

{ See if the system is idling - if not - return

    IF NOT syp$system_is_idling () THEN
      RETURN;
    IFEND;

{ Set a flag to allow interested parties to determine
{ that the system is no longer idling

    syp$set_system_idling (FALSE);

{   swap the jobs back in

    jmp$resume_activation_of_jobs;

  PROCEND jmp$resume_system;
?? TITLE := '[XDCL, #GATE] jmp$update_last_used_ssn', EJECT ??
*copyc jmh$update_last_used_ssn

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

    status.normal := TRUE;
    IF NOT jmp$system_job () THEN
      osp$set_status_abnormal ('JM', jme$must_be_system_job, 'jmp$update_last_used_ssn', status);
      RETURN;
    IFEND;

    qfp$update_last_used_ssn;
  PROCEND jmp$update_last_used_ssn;
MODEND jmm$job_recovery;
