?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Logging: Internal Logging Interfaces' ??
MODULE lgm$internal_logging_interfaces;

{ PURPOSE:
{   This module contains the non-gated interfaces used to access the local and global logs.

?? NEWTITLE := 'Global Declarations Referenced by This Module.', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc jmc$system_family
*copyc lgt$log_version
*copyc ost$heap
?? POP ??
*copyc amp$get_file_attributes
*copyc amp$get_next
*copyc amp$put_next
*copyc clp$evaluate_file_reference
*copyc clp$put_job_output
*copyc clp$trimmed_string_size
*copyc dmp$close_file
*copyc dmp$detach_device_file
*copyc fsp$build_file_ref_from_elems
*copyc fsp$change_segment_number
*copyc fsp$close_file
*copyc fsp$open_file
*copyc i#current_sequence_position
*copyc lgp$add_entry_to_system_log
*copyc lgp$get_entry_from_global_log
*copyc lgp$get_entry_from_local_log
*copyc lgp$get_local_log_read_info
*copyc lgp$initialize_critical_log_lcd
*copyc lgp$initialize_global_log_lcd
*copyc lgp$release_critical_log_space
*copyc lgp$release_global_log_space
*copyc lgp$terminate_critical_log
*copyc lgp$terminate_log
*copyc mmp$invalidate_segment
*copyc mmp$set_access_selections
*copyc osp$generate_log_message
*copyc osp$generate_output_message
*copyc osp$system_error
*copyc pfp$purge
*copyc pmp$get_date
*copyc pmp$zero_out_table
*copyc clv$standard_files
*copyc lgv$critical_log_ctl
*copyc lgv$global_log_ctl
*copyc lgv$critical_log_name
*copyc lgv$log_names
*copyc lgv$recovery_log_sfid
*copyc osv$lower_to_upper
?? OLDTITLE ??
?? NEWTITLE := 'create_critical_window_log', EJECT ??

{ PURPOSE:
{   This procedure creates a new critical window log file.

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

    VAR
      attachment_options: ^fst$attachment_options,
      cycle_selector: pft$cycle_selector,
      file_identifier: amt$file_identifier,
      file_reference: fst$path,
      ignore_status: ost$status,
      log_entry_header_p: ^lgt$log_entry_header,
      mandated_creation_attributes: ^fst$file_cycle_attributes,
      path_p: ^pft$path,
      segment_pointer: amt$segment_pointer;

    status.normal := TRUE;

{ Purge the high cycle of the file (just to make sure nothing is in the way).

    PUSH path_p: [1 .. 3];
    path_p^ [1] := jmc$system_family;
    path_p^ [2] := jmc$system_user;
    path_p^ [3] := lgv$critical_log_name;
    cycle_selector.cycle_option := pfc$highest_cycle;
    pfp$purge (path_p^, cycle_selector, osc$null_name, ignore_status);

{ Create the permanent file that will contain the log.

    fsp$build_file_ref_from_elems (path_p, file_reference, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    PUSH attachment_options: [1 .. 1];
    attachment_options^ [1].selector := fsc$access_and_share_modes;
    attachment_options^ [1].access_modes.selector := fsc$specific_access_modes;
    attachment_options^ [1].access_modes.value := $fst$file_access_options
          [fsc$append, fsc$modify, fsc$shorten, fsc$read];
    attachment_options^ [1].share_modes.selector := fsc$specific_share_modes;
    attachment_options^ [1].share_modes.value := $fst$file_access_options [];

    PUSH mandated_creation_attributes: [1 .. 3];
    mandated_creation_attributes^ [1].selector := fsc$ring_attributes;
    mandated_creation_attributes^ [1].ring_attributes.r1 := osc$tsrv_ring;
    mandated_creation_attributes^ [1].ring_attributes.r2 := osc$tsrv_ring;
    mandated_creation_attributes^ [1].ring_attributes.r3 := osc$tsrv_ring;
    mandated_creation_attributes^ [2].selector := fsc$file_contents_and_processor;
    mandated_creation_attributes^ [2].file_processor := fsc$unknown_processor;
    mandated_creation_attributes^ [2].file_contents := fsc$ascii_log;
    mandated_creation_attributes^ [3].selector := fsc$user_information;
    mandated_creation_attributes^ [3].user_information := lgc$log_version;

    fsp$open_file (file_reference, amc$segment, attachment_options, NIL, mandated_creation_attributes, NIL,
          NIL, file_identifier, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Assign the log to the appropriate segment number.

    fsp$change_segment_number (file_identifier, (osc$segnum_first_global_log - 7), osc$tmtr_ring,
          amc$sequence_pointer, segment_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Inform the system that the segment will be accessed sequentially.

    mmp$set_access_selections (segment_pointer.sequence_pointer, mmc$as_sequential, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Place the first log entry header in the log.

    NEXT log_entry_header_p IN segment_pointer.sequence_pointer;
    IF log_entry_header_p = NIL THEN
      osp$set_status_abnormal ('LG', lge$log_full, lgv$critical_log_name, status);
      RETURN;
    IFEND;
    log_entry_header_p^.previous_size := 0;
    log_entry_header_p^.current_size := 0;
    RESET segment_pointer.sequence_pointer;

{ Initialize the critical window log control descriptor.

    lgp$initialize_critical_log_lcd (segment_pointer.sequence_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  PROCEND create_critical_window_log;
?? OLDTITLE ??
?? NEWTITLE := 'create_global_log', EJECT ??

{ PURPOSE:
{   This procedure creates a new global log file.

  PROCEDURE [XDCL] create_global_log
    (    global_log: pmt$global_logs;
     VAR status: ost$status);

    VAR
      attachment_options: ^fst$attachment_options,
      cycle_selector: pft$cycle_selector,
      file_identifier: amt$file_identifier,
      file_reference: fst$path,
      ignore_status: ost$status,
      log_entry_header_p: ^lgt$log_entry_header,
      mandated_creation_attributes: ^fst$file_cycle_attributes,
      path_p: ^pft$path,
      segment_pointer: amt$segment_pointer;

    status.normal := TRUE;

{ Purge the high cycle of the file (just to make sure nothing is in the way).

    PUSH path_p: [1 .. 3];
    path_p^ [1] := jmc$system_family;
    path_p^ [2] := jmc$system_user;
    path_p^ [3] := lgv$log_names [global_log];
    cycle_selector.cycle_option := pfc$highest_cycle;
    pfp$purge (path_p^, cycle_selector, osc$null_name, ignore_status);

{ Create the permanent file that will contain the log.

    fsp$build_file_ref_from_elems (path_p, file_reference, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    PUSH attachment_options: [1 .. 1];
    attachment_options^ [1].selector := fsc$access_and_share_modes;
    attachment_options^ [1].access_modes.selector := fsc$specific_access_modes;
    attachment_options^ [1].access_modes.value := $fst$file_access_options
          [fsc$append, fsc$modify, fsc$shorten, fsc$read];
    attachment_options^ [1].share_modes.selector := fsc$specific_share_modes;
    attachment_options^ [1].share_modes.value := $fst$file_access_options [];

    PUSH mandated_creation_attributes: [1 .. 3];
    mandated_creation_attributes^ [1].selector := fsc$ring_attributes;
    mandated_creation_attributes^ [1].ring_attributes.r1 := osc$tsrv_ring;
    mandated_creation_attributes^ [1].ring_attributes.r2 := osc$tsrv_ring;
    mandated_creation_attributes^ [1].ring_attributes.r3 := osc$tsrv_ring;
    mandated_creation_attributes^ [2].selector := fsc$file_contents_and_processor;
    mandated_creation_attributes^ [2].file_processor := fsc$unknown_processor;
    IF global_log = pmc$system_log THEN
      mandated_creation_attributes^ [2].file_contents := fsc$ascii_log;
    ELSE
      mandated_creation_attributes^ [2].file_contents := fsc$binary_log;
    IFEND;
    mandated_creation_attributes^ [3].selector := fsc$user_information;
    mandated_creation_attributes^ [3].user_information := lgc$log_version;

    fsp$open_file (file_reference, amc$segment, attachment_options, NIL, mandated_creation_attributes, NIL,
          NIL, file_identifier, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Assign the log to the appropriate segment number.

    fsp$change_segment_number (file_identifier, lgp$log_segment_number (global_log), osc$tmtr_ring,
          amc$sequence_pointer, segment_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Inform that system that the segment will be accessed sequentially.

    mmp$set_access_selections (segment_pointer.sequence_pointer, mmc$as_sequential, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Place the first log entry header in the log.

    NEXT log_entry_header_p IN segment_pointer.sequence_pointer;
    IF log_entry_header_p = NIL THEN
      osp$set_status_abnormal ('LG', lge$log_full, lgv$log_names [global_log], status);
      RETURN;
    IFEND;
    log_entry_header_p^.previous_size := 0;
    log_entry_header_p^.current_size := 0;
    RESET segment_pointer.sequence_pointer;

{ Initialize the corresponding log control descriptor.

    lgp$initialize_global_log_lcd (global_log, segment_pointer.sequence_pointer, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  PROCEND create_global_log;
?? OLDTITLE ??
?? NEWTITLE := 'prompt_for_file_path', EJECT ??

{ PURPOSE:
{  Prompts the operator for the path of a file.

    PROCEDURE prompt_for_file_path
      (VAR file_reference: fst$file_reference;
       VAR file_reference_size: 0 .. fsc$max_path_size;
       VAR status: ost$status);

      VAR
        byte_address: amt$file_byte_address,
        evaluated_file_reference: fst$evaluated_file_reference,
        file_identifier: amt$file_identifier,
        file_position: amt$file_position,
        ignore_status: ost$status,
        input_file_path: fst$path,
        transfer_count: amt$transfer_count;

      status.normal := TRUE;

      fsp$open_file (':$LOCAL.INPUT.1', amc$record, NIL, NIL, NIL, NIL, NIL, file_identifier, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      REPEAT
        status.normal := TRUE;

{ Get the file path from the operator.

        clp$put_job_output (' Please enter a temporary file path, permanent file path or $NULL.', status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

        input_file_path := ' ';

        amp$get_next (file_identifier, ^input_file_path, #SIZE (input_file_path), transfer_count,
              byte_address, file_position, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

{ Convert it to upper case and verify that it is a valid file path.

        #TRANSLATE (osv$lower_to_upper, input_file_path, file_reference);
        clp$evaluate_file_reference (file_reference, $clt$file_ref_parsing_options [], TRUE,
              evaluated_file_reference, status);
        IF NOT status.normal THEN
          clp$put_job_output (' --ERROR--  You must enter a valid file path.', ignore_status);
        IFEND;
      UNTIL status.normal;
      file_reference_size := transfer_count;

      fsp$close_file (file_identifier, status);

    PROCEND prompt_for_file_path;
?? OLDTITLE, EJECT ??
?? NEWTITLE := 'force_critical_log_termination', EJECT ??

{ PURPOSE:
{   This procedure asks the operator to supply the name of file to which the critical
{   window log can be terminated.

  PROCEDURE force_critical_log_termination
    (VAR status: ost$status);

    VAR
      file_path: fst$path,
      file_path_size: 0 .. fsc$max_path_size,
      ignore_status: ost$status,
      message: string (osc$max_string_size);

    status.normal := TRUE;

    clp$put_job_output (' ', status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    message := ' ';
    message (2, * ) := lgv$critical_log_name;
    message (clp$trimmed_string_size (message) + 1, * ) := ' is too large and must be terminated.';
    clp$put_job_output (message (1, clp$trimmed_string_size (message)), status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    REPEAT
      prompt_for_file_path (file_path, file_path_size, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      clp$put_job_output (' ', status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

{ Terminate the log to the specified file.  If $NULL was specified, simply discard the log.

      IF file_path <> '$NULL' THEN
        message := ' It will take a few moments to terminate ';
        message (clp$trimmed_string_size (message) + 2, * ) := lgv$critical_log_name;
        message (clp$trimmed_string_size (message) + 1, * ) := '.';
        clp$put_job_output (message (1, clp$trimmed_string_size (message)), status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
        lgp$terminate_critical_log (file_path (1, file_path_size), status);
        IF NOT status.normal THEN
          clp$put_job_output (' ', status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
          clp$put_job_output (' The following error occured while attempting to terminate the log.',
                ignore_status);
          osp$generate_output_message (status, ignore_status);
          clp$put_job_output (' Please try again. ($NULL may be used to discard the log.)', ignore_status);
        IFEND;
      ELSE
        lgp$release_critical_log_space (0, NIL, status);
        IF status.normal THEN
          message := ' ';
          message (2, * ) := lgv$critical_log_name;
          message (clp$trimmed_string_size (message) + 1, * ) := ' has been discarded.';
          clp$put_job_output (message (1, clp$trimmed_string_size (message)), status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
        IFEND;
      IFEND;
    UNTIL status.normal;

  PROCEND force_critical_log_termination;
?? OLDTITLE ??
?? NEWTITLE := 'force_log_termination', EJECT ??
{ PURPOSE:
{   This procedure asks the operator to supply the name of file to which the specified log can be terminated.

  PROCEDURE force_log_termination
    (    global_log: pmt$global_logs;
     VAR status: ost$status);

    VAR
      file_path: fst$path,
      file_path_size: 0 .. fsc$max_path_size,
      ignore_status: ost$status,
      message: string (osc$max_string_size);

    status.normal := TRUE;

    clp$put_job_output (' ', status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    message := ' ';
    message (2, * ) := lgv$log_names [global_log];
    message (clp$trimmed_string_size (message) + 1, * ) := ' is too large and must be terminated.';
    clp$put_job_output (message (1, clp$trimmed_string_size (message)), status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    REPEAT
      prompt_for_file_path (file_path, file_path_size, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      clp$put_job_output (' ', status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

{ Terminate the log to the specified file.  If $NULL was specified, simply discard the log.

      IF file_path <> '$NULL' THEN
        message := ' It will take a few moments to terminate ';
        message (clp$trimmed_string_size (message) + 2, * ) := lgv$log_names [global_log];
        message (clp$trimmed_string_size (message) + 1, * ) := '.';
        clp$put_job_output (message (1, clp$trimmed_string_size (message)), status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;
        lgp$terminate_log (global_log, file_path (1, file_path_size), status);
        IF NOT status.normal THEN
          clp$put_job_output (' ', status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
          clp$put_job_output (' The following error occured while attempting to terminate the log.',
                ignore_status);
          osp$generate_output_message (status, ignore_status);
          clp$put_job_output (' Please try again. ($NULL may be used to discard the log.)', ignore_status);
        IFEND;
      ELSE
        lgp$release_global_log_space (global_log, 0, NIL, status);
        IF status.normal THEN
          message := ' ';
          message (2, * ) := lgv$log_names [global_log];
          message (clp$trimmed_string_size (message) + 1, * ) := ' has been discarded.';
          clp$put_job_output (message (1, clp$trimmed_string_size (message)), status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
        IFEND;
      IFEND;
    UNTIL status.normal;

  PROCEND force_log_termination;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] lgp$append_job_log_to_output', EJECT ??

{ PURPOSE:
{   This procedure appends a copy of the job log to the output file.

  PROCEDURE [XDCL] lgp$append_job_log_to_output
    (VAR status: ost$status);

    VAR
      byte_address: amt$file_byte_address,
      contains_data: boolean,
      display_line: ^string ( * ),
      display_line_size: lgt$log_entry_size,
      ending_offset: amt$file_byte_address,
      existing_file: boolean,
      file_attachment: ^fst$attachment_options,
      file_attributes: array [1 .. 1] of amt$get_item,
      ignore_status: ost$status,
      indentation_size: ost$string_size,
      local_file: boolean,
      log_cycle: lgt$log_cycle,
      log_data: ^SEQ ( * ),
      log_entry_index: 1 .. lgc$maximum_log_entry_size + 1,
      log_entry_size: lgt$log_entry_size,
      log_entry_p: ^string ( * ),
      output_file_id: amt$file_identifier;

    status.normal := TRUE;

{ Determine the page width for the output file.

    file_attributes [1].key := amc$page_width;
    amp$get_file_attributes (clv$standard_files [clc$sf_job_output_file].path_handle_name, file_attributes,
          local_file, existing_file, contains_data, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Open the output file.

    PUSH file_attachment: [1 .. 2];
    file_attachment^ [1].selector := fsc$access_and_share_modes;
    file_attachment^ [1].access_modes.selector := fsc$specific_access_modes;
    file_attachment^ [1].access_modes.value := $fst$file_access_options [fsc$append];
    file_attachment^ [1].share_modes.selector := fsc$specific_share_modes;
    file_attachment^ [1].share_modes.value := $fst$file_access_options
          [fsc$append, fsc$modify, fsc$shorten, fsc$read];
    file_attachment^ [2].selector := fsc$open_position;
    file_attachment^ [2].open_position := amc$open_at_eoi;

    fsp$open_file (clv$standard_files [clc$sf_job_output_file].path_handle_name, amc$record,
          file_attachment, {default_creation_attributes} NIL, { mandated_creation_attributes } NIL,
          {attribute_validation} NIL, {attribute_override} NIL, output_file_id, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Allocate space for the log entries read from the job log and the formatted output line.  The output line is
{ initialized to force a page eject at the start of the log.

    PUSH log_entry_p: [lgc$maximum_log_entry_size];
    PUSH display_line: [file_attributes [1].page_width + 1];
    display_line^ := '1';

{ Get the log cycle and log data sequence pointers for the job log.

    lgp$get_local_log_read_info (pmc$job_log, 0, log_cycle, log_data, ending_offset, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    RESET log_data;

{ Copy the log to the output file.

    lgp$get_entry_from_local_log (pmc$job_log, log_cycle, log_data, log_entry_size, #SEQ (log_entry_p^) ^,
          status);

  /copy_log/
    WHILE status.normal DO

{ Write the log entry to the output file.  If the log entry is longer than the page width for the output file,
{ the log entry is wrapped to additional lines and each wrapped line is indented.

      log_entry_index := 1;
      indentation_size := 0;
      WHILE log_entry_index <= log_entry_size DO
        IF (log_entry_size - log_entry_index + 1) <= (file_attributes [1].page_width - indentation_size) THEN
          display_line_size := log_entry_size - log_entry_index + 1;
        ELSE
          display_line_size := file_attributes [1].page_width - indentation_size;
        IFEND;
        display_line^ (indentation_size + 2, display_line_size) :=
              log_entry_p^ (log_entry_index, display_line_size);
        amp$put_next (output_file_id, display_line, display_line_size + indentation_size + 1, byte_address,
              status);
        IF NOT status.normal THEN
          EXIT /copy_log/;
        IFEND;
        log_entry_index := log_entry_index + display_line_size;
        indentation_size := 2;
        display_line^ (1, indentation_size + 1) := '  ';
      WHILEND;

{ Get the next log entry.

      lgp$get_entry_from_local_log (pmc$job_log, log_cycle, log_data, log_entry_size, #SEQ (log_entry_p^) ^,
            status);
    WHILEND /copy_log/;
    IF status.condition = lge$end_of_log THEN
      status.normal := TRUE;
    IFEND;

    fsp$close_file (output_file_id, ignore_status);

  PROCEND lgp$append_job_log_to_output;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] lgp$install_global_logs', EJECT ??

{ PURPOSE:
{   This procedure creates the global log files during an installation deadstart.

  PROCEDURE [XDCL] lgp$install_global_logs
    (VAR status: ost$status);

    VAR
      date: ost$date,
      global_log: pmt$global_logs,
      ignore_status: ost$status,
      message: string (osc$max_string_size),
      time: ost$time;

    status.normal := TRUE;

{ Create each of the global logs and the critical window log.  If an error prevents
{ the creation of a log, stop the deadstart.

    create_critical_window_log (status);
    IF NOT status.normal THEN
      message := 'Unable to create ';
      message (clp$trimmed_string_size (message) + 2, * ) := lgv$critical_log_name;
      osp$system_error (message (1, clp$trimmed_string_size (message)), ^status);
    IFEND;
    FOR global_log := LOWERBOUND (lgv$global_log_ctl) TO UPPERBOUND (lgv$global_log_ctl) DO
      create_global_log (global_log, status);
      IF NOT status.normal THEN
        message := 'Unable to create ';
        message (clp$trimmed_string_size (message) + 2, * ) := lgv$log_names [global_log];
        osp$system_error (message (1, clp$trimmed_string_size (message)), ^status);
      IFEND;
    FOREND;

{ Place an installation deadstart message in the system log.

    pmp$get_date (osc$month_date, date, {ignore} status);
    status.normal := TRUE;
    message := '**********  INSTALLATION DEADSTART ON  ';
    message (clp$trimmed_string_size (message) + 3, * ) := date.month;
    lgp$add_entry_to_system_log (pmc$msg_origin_system, message (1, clp$trimmed_string_size (message)), time,
          ignore_status);

  PROCEND lgp$install_global_logs;
?? OLDTITLE ??
?? NEWTITLE := 'lgp$log_segment_number', EJECT ??

{ PURPOSE:
{   This function returns the segment number that should be used for the specified global log.

  FUNCTION lgp$log_segment_number
    (    global_log: pmt$global_logs): ost$segment;

    CASE global_log OF
    = pmc$account_log =
      lgp$log_segment_number := osc$segnum_first_global_log;
    = pmc$engineering_log =
      lgp$log_segment_number := osc$segnum_first_global_log - 6;
{ special case the engineering log so we can eliminate references to seg 20(16)
    = pmc$history_log =
      lgp$log_segment_number := osc$segnum_first_global_log + 2;
    = pmc$security_log =
      lgp$log_segment_number := osc$segnum_first_global_log + 3;
    = pmc$statistic_log =
      lgp$log_segment_number := osc$segnum_first_global_log + 4;
    = pmc$system_log =
      lgp$log_segment_number := osc$segnum_system_dayfile;

{   = critical_window_log =
{     lgp$log_segment_number := osc$segnum_first_global_log - 7;
{
{   The critical window log is NOT a global log but is assigned a
{   segment.  This comment is here to let people know that segment 18(16)
{   belongs to the critical window log.

    ELSE
      lgp$log_segment_number := 0;
    CASEND;

  FUNCEND lgp$log_segment_number;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] lgp$recover_global_logs', EJECT ??

{ PURPOSE:
{   This procedure recovers the global logs during a continuation deadstart.

  PROCEDURE [XDCL] lgp$recover_global_logs
    (VAR status: ost$status);

    VAR
      critical_recovery_status: ost$status,
      date: ost$date,
      global_log: pmt$global_logs,
      ignore_status: ost$status,
      message: string (osc$max_string_size),
      path: ^pft$path,
      recovery_log_data: ^SEQ ( * ),
      recovery_status: array [pmt$global_logs] of ost$status,
      time: ost$time;

?? NEWTITLE := 'copy_recovery_log', EJECT ??

{ PURPOSE:
{   This procedure is used to copy the contents of the recovery log to the system log after the system log has
{   been recovered and is available for use.

    PROCEDURE copy_recovery_log
      (VAR recovery_log_data: ^SEQ ( * );
       VAR status: ost$status);

      VAR
        file_modified: boolean,
        fmd_modified: boolean,
        log_entry_p: ^lgt$log_entry,
        log_entry_size: lgt$log_entry_size,
        text: ^string ( * ),
        time: ost$time;

      status.normal := TRUE;

{ Allocate space to hold the log entries as they are read.

      PUSH log_entry_p: [[REP lgc$maximum_log_entry_size OF cell]];

{ Copy the contents of the recovery log to the real system log.

      RESET recovery_log_data;
      lgp$get_entry_from_global_log (pmc$system_log, lgv$global_log_ctl [pmc$system_log].log_cycle,
            recovery_log_data, log_entry_size, log_entry_p^, status);
      WHILE status.normal DO
        RESET log_entry_p;
        NEXT text: [log_entry_size] IN log_entry_p;
        IF text <> NIL THEN
          lgp$add_entry_to_system_log (pmc$msg_origin_system, text^, time, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
        IFEND;
        lgp$get_entry_from_global_log (pmc$system_log, lgv$global_log_ctl [pmc$system_log].log_cycle,
              recovery_log_data, log_entry_size, log_entry_p^, status);
      WHILEND;
      IF status.condition = lge$end_of_log THEN
        status.normal := TRUE;
      ELSE
        RETURN;
      IFEND;

{ Erase the contents of the recovery log.

      pmp$zero_out_table (recovery_log_data, i#current_sequence_position (recovery_log_data));

{ Close the recovery log device file.

      dmp$close_file (recovery_log_data, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

{## Illegal to detach file - it is in the address space of other tasks in this job.
{##   dmp$detach_device_file (lgv$recovery_log_sfid, file_modified, fmd_modified, status);

      IF NOT status.normal THEN
        RETURN;
      IFEND;

    PROCEND copy_recovery_log;
?? OLDTITLE, EJECT ??
    status.normal := TRUE;

{ Save the log data sequence pointer for the recovery log.

    recovery_log_data := lgv$global_log_ctl [pmc$system_log].log_data;

{ Recover each of the global logs and the critical window log.
{ If a log cannot be recovered, it will be recreated.

    recover_critical_window_log (critical_recovery_status);
    IF NOT critical_recovery_status.normal THEN
      create_critical_window_log (status);
      IF NOT status.normal THEN
        message := 'Unable to recover or recreate ';
        message (clp$trimmed_string_size (message) + 2, * ) := lgv$critical_log_name;
        osp$system_error (message (1, clp$trimmed_string_size (message)), ^status);
      IFEND;
    IFEND;
    FOR global_log := LOWERBOUND (lgv$global_log_ctl) TO UPPERBOUND (lgv$global_log_ctl) DO
      recover_global_log (global_log, recovery_status [global_log]);
      IF (NOT recovery_status [global_log].normal) THEN
        create_global_log (global_log, status);
        IF NOT status.normal THEN
          message := 'Unable to recover or recreate ';
          message (clp$trimmed_string_size (message) + 2, * ) := lgv$log_names [global_log];
          osp$system_error (message (1, clp$trimmed_string_size (message)), ^status);
        IFEND;
      IFEND;
    FOREND;

{ Report any errors that occured during recovery in the system log.

    FOR global_log := LOWERBOUND (lgv$global_log_ctl) TO UPPERBOUND (lgv$global_log_ctl) DO
      IF NOT recovery_status [global_log].normal THEN
        message := '**********  THE FOLLOWING ERROR OCCURRED WHILE RECOVERING ';
        message (clp$trimmed_string_size (message) + 2, * ) := lgv$log_names [global_log];
        lgp$add_entry_to_system_log (pmc$msg_origin_system, message (1, clp$trimmed_string_size (message)),
              time, ignore_status);
        osp$generate_log_message ($pmt$ascii_logset [pmc$system_log], recovery_status [global_log],
              ignore_status);
        lgp$add_entry_to_system_log (pmc$msg_origin_system, '********** THE LOG WAS RECREATED', time,
              ignore_status);
      IFEND;
    FOREND;
    IF NOT critical_recovery_status.normal THEN
      message := '**********  THE FOLLOWING ERROR OCCURRED WHILE RECOVERING ';
      message (clp$trimmed_string_size (message) + 2, * ) := lgv$critical_log_name;
      lgp$add_entry_to_system_log (pmc$msg_origin_system, message (1, clp$trimmed_string_size (message)),
            time, ignore_status);
      osp$generate_log_message ($pmt$ascii_logset [pmc$system_log], critical_recovery_status,
            ignore_status);
      lgp$add_entry_to_system_log (pmc$msg_origin_system, '********** THE LOG WAS RECREATED', time,
            ignore_status);
    IFEND;

{ Record a recovery deadstart message in the system log.

    pmp$get_date (osc$month_date, date, ignore_status);
    message := '**********  RECOVERY DEADSTART ON  ';
    message (clp$trimmed_string_size (message) + 3, * ) := date.month;
    lgp$add_entry_to_system_log (pmc$msg_origin_system, message (1, clp$trimmed_string_size (message)), time,
          ignore_status);

{ Copy the entries from the recovery log to the recovered log.

    copy_recovery_log (recovery_log_data, recovery_status [global_log]);
    IF NOT recovery_status [global_log].normal THEN
      lgp$add_entry_to_system_log (pmc$msg_origin_system,
            '**********  THE FOLLOWING ERROR OCCURED WHILE COPYING THE RECOVERY LOG', time, ignore_status);
      osp$generate_log_message ($pmt$ascii_logset [pmc$system_log], recovery_status [global_log],
            ignore_status);
    IFEND;

  PROCEND lgp$recover_global_logs;
?? OLDTITLE ??
?? NEWTITLE := 'recover_critical_window_log', EJECT ??

{ PURPOSE:
{   This procedure is used to recover the critical window log.

  PROCEDURE recover_critical_window_log
    (VAR status: ost$status);

    VAR
      attachment_options: ^fst$attachment_options,
      file_identifier: amt$file_identifier,
      file_reference: fst$path,
      ignore_status: ost$status,
      path_p: ^pft$path,
      required_attributes: ^fst$file_cycle_attributes,
      segment_pointer: amt$segment_pointer;

    status.normal := TRUE;

{ Open the file that contains the log (the file must already exist and have the correct attributes).

    PUSH path_p: [1 .. 2];
    path_p^ [1] := '$user';
    path_p^ [2] := lgv$critical_log_name;
    fsp$build_file_ref_from_elems (path_p, file_reference, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    PUSH attachment_options: [1 .. 2];
    attachment_options^ [1].selector := fsc$access_and_share_modes;
    attachment_options^ [1].access_modes.selector := fsc$specific_access_modes;
    attachment_options^ [1].access_modes.value := $fst$file_access_options
          [fsc$append, fsc$modify, fsc$shorten, fsc$read];
    attachment_options^ [1].share_modes.selector := fsc$specific_share_modes;
    attachment_options^ [1].share_modes.value := $fst$file_access_options [];
    attachment_options^ [2].selector := fsc$create_file;
    attachment_options^ [2].create_file := FALSE;

    PUSH required_attributes: [1 .. 3];
    required_attributes^ [1].selector := fsc$ring_attributes;
    required_attributes^ [1].ring_attributes.r1 := osc$tsrv_ring;
    required_attributes^ [1].ring_attributes.r2 := osc$tsrv_ring;
    required_attributes^ [1].ring_attributes.r3 := osc$tsrv_ring;
    required_attributes^ [2].selector := fsc$file_contents_and_processor;
    required_attributes^ [2].file_processor := fsc$unknown_processor;
    required_attributes^ [2].file_contents := fsc$ascii_log;
    required_attributes^ [3].selector := fsc$user_information;
    required_attributes^ [3].user_information := lgc$log_version;

    fsp$open_file (file_reference, amc$segment, attachment_options, NIL, NIL, required_attributes, NIL,
          file_identifier, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /log_open/
    BEGIN

{ Assign the log to the appropriate segment number.

      fsp$change_segment_number (file_identifier, (osc$segnum_first_global_log - 7), osc$tmtr_ring,
            amc$sequence_pointer, segment_pointer, status);
      IF NOT status.normal THEN
        EXIT /log_open/;
      IFEND;

{ Inform that system that the segment will be accessed sequentially.

      mmp$set_access_selections (segment_pointer.sequence_pointer, mmc$as_sequential, status);
      IF NOT status.normal THEN
        EXIT /log_open/;
      IFEND;

{ Initialize the critical_window log control descriptor.

      lgp$initialize_critical_log_lcd (segment_pointer.sequence_pointer, status);
      IF NOT status.normal THEN
        EXIT /log_open/;
      IFEND;

{ If the log is more than 75% full, terminate the log to a file specified by the operator.

      IF i#current_sequence_position (lgv$critical_log_ctl.log_data) >
            ((lgv$critical_log_ctl.maximum_size * 3) DIV 4) THEN
        force_critical_log_termination (status);
      IFEND;
    END /log_open/;

{ If an error occurs, close the log file and invalidate the log segment.

    IF NOT status.normal THEN
      fsp$close_file (file_identifier, ignore_status);
      mmp$invalidate_segment ((osc$segnum_first_global_log - 7), 1, NIL, ignore_status);
    IFEND;

  PROCEND recover_critical_window_log;
?? OLDTITLE ??
?? NEWTITLE := 'recover_global_log', EJECT ??

{ PURPOSE:
{   This procedure is used recover the specified global log.

  PROCEDURE recover_global_log
    (    global_log: pmt$global_logs;
     VAR status: ost$status);

    VAR
      attachment_options: ^fst$attachment_options,
      file_identifier: amt$file_identifier,
      file_reference: fst$path,
      ignore_status: ost$status,
      path_p: ^pft$path,
      required_attributes: ^fst$file_cycle_attributes,
      segment_pointer: amt$segment_pointer;

    status.normal := TRUE;

{ Open the file that contains the log (the file must already exist and have the correct attributes).

    PUSH path_p: [1 .. 2];
    path_p^ [1] := '$user';
    path_p^ [2] := lgv$log_names [global_log];
    fsp$build_file_ref_from_elems (path_p, file_reference, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    PUSH attachment_options: [1 .. 2];
    attachment_options^ [1].selector := fsc$access_and_share_modes;
    attachment_options^ [1].access_modes.selector := fsc$specific_access_modes;
    attachment_options^ [1].access_modes.value := $fst$file_access_options
          [fsc$append, fsc$modify, fsc$shorten, fsc$read];
    attachment_options^ [1].share_modes.selector := fsc$specific_share_modes;
    attachment_options^ [1].share_modes.value := $fst$file_access_options [];
    attachment_options^ [2].selector := fsc$create_file;
    attachment_options^ [2].create_file := FALSE;

    PUSH required_attributes: [1 .. 3];
    required_attributes^ [1].selector := fsc$ring_attributes;
    required_attributes^ [1].ring_attributes.r1 := osc$tsrv_ring;
    required_attributes^ [1].ring_attributes.r2 := osc$tsrv_ring;
    required_attributes^ [1].ring_attributes.r3 := osc$tsrv_ring;
    required_attributes^ [2].selector := fsc$file_contents_and_processor;
    required_attributes^ [2].file_processor := fsc$unknown_processor;
    IF global_log = pmc$system_log THEN
      required_attributes^ [2].file_contents := fsc$ascii_log;
    ELSE
      required_attributes^ [2].file_contents := fsc$binary_log;
    IFEND;
    required_attributes^ [3].selector := fsc$user_information;
    required_attributes^ [3].user_information := lgc$log_version;

    fsp$open_file (file_reference, amc$segment, attachment_options, NIL, NIL, required_attributes, NIL,
          file_identifier, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /log_open/
    BEGIN

{ Assign the log to the appropriate segment number.

      fsp$change_segment_number (file_identifier, lgp$log_segment_number (global_log), osc$tmtr_ring,
            amc$sequence_pointer, segment_pointer, status);
      IF NOT status.normal THEN
        EXIT /log_open/;
      IFEND;

{ Inform that system that the segment will be accessed sequentially.

      mmp$set_access_selections (segment_pointer.sequence_pointer, mmc$as_sequential, status);
      IF NOT status.normal THEN
        EXIT /log_open/;
      IFEND;

{ Initialize the corresponding log control descriptor.

      lgp$initialize_global_log_lcd (global_log, segment_pointer.sequence_pointer, status);
      IF NOT status.normal THEN
        EXIT /log_open/;
      IFEND;

{ If the log is more than 75% full, terminate the log to a file specified by the operator.

      IF i#current_sequence_position (lgv$global_log_ctl [global_log].log_data) >
            ((lgv$global_log_ctl [global_log].maximum_size * 3) DIV 4) THEN
        force_log_termination (global_log, status);
      IFEND;
    END /log_open/;

{ If an error occurs, close the log file and invalidate the log segment.

    IF NOT status.normal THEN
      fsp$close_file (file_identifier, ignore_status);
      mmp$invalidate_segment (lgp$log_segment_number (global_log), 1, NIL, ignore_status);
    IFEND;

  PROCEND recover_global_log;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] lgp$install_eng_log', EJECT ??

{ PURPOSE:
{   This procedure deletes the old engineering log segment during  deadstart.

  PROCEDURE [XDCL] lgp$install_eng_log
    (VAR status: ost$status);

    CONST
      old_engineering_log_number = 20(16);

    VAR
      date: ost$date,
      global_log: pmt$global_logs,
      message: string (osc$max_string_size),
      ignore_status: ost$status,
      time: ost$time;

    status.normal := TRUE;

      mmp$invalidate_segment (old_engineering_log_number, 1, NIL, ignore_status);

{ Place an recovery deadstart message in the system log.

    pmp$get_date (osc$month_date, date, ignore_status);
    message := '**********  MODIFIED ENGINEERING LOG ON  ';
    message (clp$trimmed_string_size (message) + 3, * ) := date.month;
    lgp$add_entry_to_system_log (pmc$msg_origin_system, message (1, clp$trimmed_string_size (message)), time,
          ignore_status);

  PROCEND lgp$install_eng_log;

MODEND lgm$internal_logging_interfaces;
