?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Logging: Common Processors Ring 2' ??
MODULE lgm$common_processors_r2;

{ PURPOSE:
{   This module contains common code used for both local and global logs.
{!!!!NOTE!!!! Any modifications made to this module must be reflected in lgm$common_processors_r1.

?? NEWTITLE := 'Global Declarations Referenced by This Module.', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc amt$file_byte_address
*copyc lge$corrupted_log
*copyc lge$end_of_log
*copyc lge$log_cycles_do_not_match
*copyc lge$log_full
*copyc lge$log_not_available
*copyc lgt$log_control_descriptor
*copyc lgt$log_cycle
*copyc lgt$log_entry
*copyc lgt$log_entry_header
?? POP ??
*copyc clp$trimmed_string_size
*copyc dpp$put_critical_message
*copyc i#build_adaptable_seq_pointer
*copyc i#current_sequence_position
*copyc i#move
*copyc mmp$os_preallocate_file_space
*copyc osp$clear_job_signature_lock
*copyc osp$clear_mainframe_sig_lock
*copyc osp$establish_condition_handler
*copyc osp$disestablish_cond_handler
*copyc osp$monitor_fault_to_status
*copyc osp$set_job_signature_lock
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc osp$test_sig_lock
*copyc pmp$continue_to_cause
*copyc lgv$log_names
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] lgp$add_log_entry', EJECT ??
{ PURPOSE:
{   Adds an entry to a log.  The caller of this procedure must interlock the log before making this request.

  PROCEDURE [XDCL, #GATE] lgp$add_log_entry
    (    entry_p: ^lgt$log_entry;
         log_control_descriptor_p: ^lgt$log_control_descriptor;
     VAR status: ost$status);

    VAR
      log_entry_p: ^lgt$log_entry,
      local_log_data: ^SEQ ( * ),
      trailing_log_entry_header_p: ^lgt$log_entry_header;

?? NEWTITLE := 'condition_handler', EJECT ??

{ PURPOSE:
{   Make sure that an error is returned in the event of an error.

    PROCEDURE condition_handler
      (    condition {input} : pmt$condition;
           condition_descriptor {input} : ^pmt$condition_information;
           save_area {input, output} : ^ost$stack_frame_save_area;
       VAR ch_status {output} : ost$status);

       CASE condition.selector OF
       = pmc$system_conditions, mmc$segment_access_condition =
         pmp$continue_to_cause (pmc$execute_standard_procedure, ch_status);
         EXIT lgp$add_log_entry;

       ELSE
         pmp$continue_to_cause (pmc$execute_standard_procedure, ch_status);
         RETURN;
       CASEND;

    PROCEND condition_handler;
?? OLDTITLE ??
?? NEWTITLE := 'preallocate_log_space', EJECT ??
{ PURPOSE:
{   Preallocates disk space for a log (if the log has not reached its maximum size).

    PROCEDURE preallocate_log_space
      (    log_control_descriptor_p: ^lgt$log_control_descriptor;
       VAR local_log_data: ^SEQ ( * );
       VAR status: ost$status);

      TYPE
        variant_sequence_pointer = record
          case boolean of
          = TRUE =
            cybil_definition: cyt$sequence_pointer,
          = FALSE =
            normal_definition: ^SEQ ( * ),
          casend,
        recend;

      VAR
        message: string (osc$max_string_size),
        message_length: integer,
        preallocated_size: ost$segment_length,
        sequence_pointer: variant_sequence_pointer;

      status.normal := TRUE;

      sequence_pointer.normal_definition := local_log_data;

{ If the log has not reached its maximum size, compute the new size.  Otherwise, return log full error.
{ The calculated size will always be an even multiple of the preallocation size.

      IF sequence_pointer.cybil_definition.length < log_control_descriptor_p^.maximum_size THEN
        preallocated_size := (((sequence_pointer.cybil_definition.length + lgc$maximum_log_entry_size) DIV
              log_control_descriptor_p^.preallocation_size) + 1) *
              log_control_descriptor_p^.preallocation_size;
        IF preallocated_size > log_control_descriptor_p^.maximum_size THEN
          preallocated_size := log_control_descriptor_p^.maximum_size;
        IFEND;

{ Preallocate the disk space.

        mmp$os_preallocate_file_space (local_log_data, preallocated_size, 0, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

{ Adjust the length of the log data sequence.

        sequence_pointer.cybil_definition.length := preallocated_size;
        local_log_data := sequence_pointer.normal_definition;

{ Inform the operator if a global log is getting too large (greater than 75% of maximum size).

        IF (i#current_sequence_position (local_log_data) > ((log_control_descriptor_p^.maximum_size * 3) DIV
              4)) AND (log_control_descriptor_p^.log IN -$pmt$global_logset []) THEN
          IF log_control_descriptor_p^.critical_log THEN
            STRINGREP (message, message_length, lgv$log_names [log_control_descriptor_p^.log] (1,
                  clp$trimmed_string_size (lgv$log_names [log_control_descriptor_p^.log])), ' is',
                  ($REAL (i#current_sequence_position (local_log_data)) / 1000000.0): 7: 2,
                  'M bytes long, system will terminate at', (log_control_descriptor_p^.maximum_size DIV
                  1000000), 'M bytes.');
          ELSE
            STRINGREP (message, message_length, lgv$log_names [log_control_descriptor_p^.log] (1,
                  clp$trimmed_string_size (lgv$log_names [log_control_descriptor_p^.log])), ' is',
                  ($REAL (i#current_sequence_position (local_log_data)) / 1000000.0): 7: 2,
                  'M bytes long, logging will stop at', (log_control_descriptor_p^.maximum_size DIV 1000000),
                  'M bytes.');
          IFEND;
          dpp$put_critical_message (message (1, message_length), {ignore} status);
          status.normal := TRUE;
        IFEND;
      ELSE
        log_control_descriptor_p^.log_full := TRUE;
        osp$set_status_abnormal ('LG', lge$log_full, lgv$log_names [log_control_descriptor_p^.log], status);
      IFEND;

    PROCEND preallocate_log_space;
?? OLDTITLE, EJECT ??

    status.normal := TRUE;

{ Check if the log is available.

    IF log_control_descriptor_p^.log_data = NIL THEN
      osp$set_status_abnormal ('LG', lge$log_not_available, lgv$log_names [log_control_descriptor_p^.log],
            status);
      RETURN;
    IFEND;

{ Check if the log is full.

    IF log_control_descriptor_p^.log_full THEN
      osp$set_status_abnormal ('LG', lge$log_full, lgv$log_names [log_control_descriptor_p^.log], status);
      RETURN;
    IFEND;

{ Establish a condition handler.

    osp$establish_condition_handler (^condition_handler, FALSE);

{ Make a copy of the log data sequence pointer and use it until the log entry has been successfully placed in
{ the log.

    local_log_data := log_control_descriptor_p^.log_data;

{ Allocate space in the log for the log entry.  If the entry will not fit, try to preallocate more space.

    NEXT log_entry_p: [[REP #SIZE (entry_p^) OF cell]] IN local_log_data;
    IF log_entry_p = NIL THEN
      preallocate_log_space (log_control_descriptor_p, local_log_data, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
      NEXT log_entry_p: [[REP #SIZE (entry_p^) OF cell]] IN local_log_data;
      IF log_entry_p = NIL THEN
        osp$set_status_abnormal ('LG', lge$log_full, lgv$log_names [log_control_descriptor_p^.log], status);
        RETURN;
      IFEND;
    IFEND;

{ Allocate space in the log for the trailing log entry header.

    NEXT trailing_log_entry_header_p IN local_log_data;
    IF trailing_log_entry_header_p = NIL THEN
      preallocate_log_space (log_control_descriptor_p, local_log_data, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;
      NEXT trailing_log_entry_header_p IN local_log_data;
      IF trailing_log_entry_header_p = NIL THEN
        osp$set_status_abnormal ('LG', lge$log_full, lgv$log_names [log_control_descriptor_p^.log], status);
        RETURN;
      IFEND;
    IFEND;

{ Move the log entry into the log.

    log_entry_p^ := entry_p^;

{ Initialize the trailing log entry header.

    trailing_log_entry_header_p^.previous_size := #SIZE (entry_p^);
    trailing_log_entry_header_p^.current_size := 0;

{ Update current size in log entry header that preceeds the log entry.

    log_control_descriptor_p^.trailing_log_entry_header_p^.current_size := #SIZE (entry_p^);

{ Save pointer to the new trailing log entry header in the log control descriptor.

    log_control_descriptor_p^.trailing_log_entry_header_p := trailing_log_entry_header_p;

{ Update the actual log data sequence pointer.

    log_control_descriptor_p^.log_data := local_log_data;

    osp$disestablish_cond_handler;

  PROCEND lgp$add_log_entry;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] lgp$get_log_read_information', EJECT ??
{ PURPOSE:
{   This procedure returns the log cycle and a log data sequence pointer that is positioned the specified
{   number of log entries from the end of the log.  If the log does not contain the specified number of
{   entries, the log data sequence pointer is positioned to the beginning of the log.  If zero is specified
{   for the number of entries, the log_data sequence will be positioned at the current end of the log.

  PROCEDURE [XDCL, #GATE] lgp$get_log_read_information
    (    log_control_descriptor_p: ^lgt$log_control_descriptor;
         entry_count_from_end_of_log: ost$segment_length;
     VAR log_cycle: lgt$log_cycle;
     VAR log_data: ^SEQ ( * );
     VAR ending_offset: amt$file_byte_address;
     VAR status: ost$status);

    VAR
      count: ost$segment_length,
      log_entry_header: lgt$log_entry_header,
      log_entry_header_p: ^lgt$log_entry_header;

?? NEWTITLE := 'condition_handler', EJECT ??
{ PURPOSE:
{   Make sure that the log interlock is cleared in the event of an error.

    PROCEDURE condition_handler
      (    condition {input} : pmt$condition;
           condition_descriptor {input} : ^pmt$condition_information;
           save_area {input, output} : ^ost$stack_frame_save_area;
       VAR ch_status {output} : ost$status);

      VAR
        lock_status: ost$signature_lock_status;


       CASE condition.selector OF
       = pmc$block_exit_processing =
         osp$test_sig_lock (log_control_descriptor_p^.lock, lock_status);
         IF lock_status = osc$sls_locked_by_current_task THEN
           lgp$unlock_log (log_control_descriptor_p);
         IFEND;
         RETURN;

       = pmc$system_conditions, mmc$segment_access_condition =
         pmp$continue_to_cause (pmc$execute_standard_procedure, ch_status);
         EXIT lgp$get_log_read_information;

       ELSE
         pmp$continue_to_cause (pmc$execute_standard_procedure, ch_status);
         RETURN;
       CASEND;

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

{ Lock the log before establishing the condition handler so that if the log was already locked
{ by another procedure in this task, this procedure doesn't unlock it.

    lgp$lock_log (log_control_descriptor_p);
    osp$establish_condition_handler (^condition_handler, TRUE);

{ Get the log cycle and log data sequence information.

    log_cycle := log_control_descriptor_p^.log_cycle;
    IF log_control_descriptor_p^.log_data <> NIL THEN

{ Return a log data sequence pointer that reflects a sequence that is the larger of the actual log data
{ sequence or the declared maximum size.  This is necessary because the maximum size specified for the
{ log may changed (up or down) from one deadstart to the next and the size of a recovered log could exceed
{ the specified maximum.

      IF #SIZE (log_control_descriptor_p^.log_data^) > log_control_descriptor_p^.maximum_size THEN
        log_data := log_control_descriptor_p^.log_data;
      ELSE
        i#build_adaptable_seq_pointer (#RING (log_control_descriptor_p^.log_data),
              #SEGMENT (log_control_descriptor_p^.log_data), #OFFSET (log_control_descriptor_p^.log_data),
              log_control_descriptor_p^.maximum_size, i#current_sequence_position
              (log_control_descriptor_p^.log_data), log_data);
      IFEND;
      ending_offset := #OFFSET (log_control_descriptor_p^.trailing_log_entry_header_p);
    ELSE
      log_data := NIL;
      ending_offset := 0;
    IFEND;

{ If a non zero entry count was specified, back up the specified number of entries.  Exit if the beginning of
{ the log is found.

    IF (entry_count_from_end_of_log <> 0) AND (log_control_descriptor_p^.log_data <> NIL) THEN
      log_entry_header_p := log_control_descriptor_p^.trailing_log_entry_header_p;
      RESET log_data TO log_entry_header_p;

    /find_starting_location_in_log/
      FOR count := 1 TO entry_count_from_end_of_log DO
        IF log_entry_header_p^.previous_size = 0 THEN
          EXIT /find_starting_location_in_log/;
        ELSE
          log_entry_header_p := #ADDRESS (#RING (log_data), #SEGMENT (log_data),
                (i#current_sequence_position (log_data) - (#SIZE (lgt$log_entry_header) +
                log_entry_header_p^.previous_size)));
          RESET log_data TO log_entry_header_p;
        IFEND;
      FOREND /find_starting_location_in_log/;
    IFEND;

{ Release the log interlock.

    lgp$unlock_log (log_control_descriptor_p);
    osp$disestablish_cond_handler;

  PROCEND lgp$get_log_read_information;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] lgp$get_log_entry', EJECT ??
{ PURPOSE:
{   Retrieve an entry from a log.

  PROCEDURE [XDCL, #GATE] lgp$get_log_entry
    (    log_cycle: lgt$log_cycle;
         log_control_descriptor_p: ^lgt$log_control_descriptor;
     VAR log_data: ^SEQ ( * );
     VAR log_entry_size: lgt$log_entry_size;
     VAR log_entry: lgt$log_entry;
     VAR status: ost$status);

    VAR
      log_entry_header_p: ^lgt$log_entry_header,
      log_entry_p: ^lgt$log_entry,
      local_log_data: ^SEQ ( * );

?? NEWTITLE := 'condition_handler', EJECT ??

{ PURPOSE:
{   Make sure that the log interlock is cleared in the event of an error.

    PROCEDURE condition_handler
      (    condition {input} : pmt$condition;
           condition_descriptor {input} : ^pmt$condition_information;
           save_area {input, output} : ^ost$stack_frame_save_area;
       VAR ch_status {output} : ost$status);

      VAR
        lock_status: ost$signature_lock_status;

       CASE condition.selector OF
       = pmc$block_exit_processing =
         osp$test_sig_lock (log_control_descriptor_p^.lock, lock_status);
         IF lock_status = osc$sls_locked_by_current_task THEN
           lgp$unlock_log (log_control_descriptor_p);
         IFEND;
         RETURN;

       = pmc$system_conditions, mmc$segment_access_condition =
         pmp$continue_to_cause (pmc$execute_standard_procedure, ch_status);
         EXIT lgp$get_log_entry;

       ELSE
         pmp$continue_to_cause (pmc$execute_standard_procedure, ch_status);
         RETURN;
       CASEND;

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

{ Check if the log is available.

    IF log_control_descriptor_p^.log_data = NIL THEN
      osp$set_status_abnormal ('LG', lge$log_not_available, lgv$log_names [log_control_descriptor_p^.log],
            status);
      RETURN;
    IFEND;

{ Establish a condition handler and interlock the log.

    osp$establish_condition_handler (^condition_handler, TRUE);
    lgp$lock_log (log_control_descriptor_p);

  /log_locked/
    BEGIN

{ Verify that the specified log cycle matches the log cycle in the log control descriptor.

      IF log_cycle <> log_control_descriptor_p^.log_cycle THEN
        osp$set_status_abnormal ('LG', lge$log_cycles_do_not_match,
              lgv$log_names [log_control_descriptor_p^.log], status);
        EXIT /log_locked/;
      IFEND;

{ Make a copy of log_data to be used until a log entry is successfully retrieved.

      local_log_data := log_data;

{ Get the log entry header.

      NEXT log_entry_header_p IN local_log_data;
      IF log_entry_header_p = NIL THEN
        osp$set_status_abnormal ('LG', lge$corrupted_log, lgv$log_names [log_control_descriptor_p^.log],
              status);
        EXIT /log_locked/;
      ELSEIF log_entry_header_p^.current_size = 0 THEN
        osp$set_status_abnormal ('LG', lge$end_of_log, lgv$log_names [log_control_descriptor_p^.log], status);
        EXIT /log_locked/;
      IFEND;

{ Get the log entry.

      NEXT log_entry_p: [[REP log_entry_header_p^.current_size OF cell]] IN local_log_data;
      IF log_entry_p = NIL THEN
        osp$set_status_abnormal ('LG', lge$corrupted_log, lgv$log_names [log_control_descriptor_p^.log],
              status);
        EXIT /log_locked/;
      IFEND;

{ Return the actual size of the log entry and as much of the log entry as will fit.

      log_entry_size := log_entry_header_p^.current_size;
      IF log_entry_size <= #SIZE (log_entry) THEN
        i#move (log_entry_p, ^log_entry, log_entry_size);
      ELSE
        i#move (log_entry_p, ^log_entry, #SIZE (log_entry));
      IFEND;

{ Update log_data.

      log_data := local_log_data;

    END /log_locked/;

{ Release the log interlock.

    lgp$unlock_log (log_control_descriptor_p);
    osp$disestablish_cond_handler;

  PROCEND lgp$get_log_entry;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] lgp$get_previous_log_entry_size', EJECT ??
{ PURPOSE:
{   Retrieve the size of the previous log entry.

  PROCEDURE [XDCL, #GATE] lgp$get_previous_log_entry_size
    (    log_cycle: lgt$log_cycle;
         log_control_descriptor_p: ^lgt$log_control_descriptor;
     VAR log_data: ^SEQ ( * );
     VAR previous_size: lgt$log_entry_size;
     VAR status: ost$status);

    VAR
      log_entry_header_p: ^lgt$log_entry_header;

?? NEWTITLE := 'condition_handler', EJECT ??

{ PURPOSE:
{   Make sure that the log interlock is cleared in the event of an error.

    PROCEDURE condition_handler
      (    condition {input} : pmt$condition;
           condition_descriptor {input} : ^pmt$condition_information;
           save_area {input, output} : ^ost$stack_frame_save_area;
       VAR ch_status {output} : ost$status);

      VAR
        lock_status: ost$signature_lock_status;

       CASE condition.selector OF
       = pmc$block_exit_processing =
         osp$test_sig_lock (log_control_descriptor_p^.lock, lock_status);
         IF lock_status = osc$sls_locked_by_current_task THEN
           lgp$unlock_log (log_control_descriptor_p);
         IFEND;
         RETURN;

       = pmc$system_conditions, mmc$segment_access_condition =
         pmp$continue_to_cause (pmc$execute_standard_procedure, ch_status);
         EXIT lgp$get_previous_log_entry_size;

       ELSE
         pmp$continue_to_cause (pmc$execute_standard_procedure, ch_status);
         RETURN;
       CASEND;

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

{ Check if the log is available.

    IF log_control_descriptor_p^.log_data = NIL THEN
      osp$set_status_abnormal ('LG', lge$log_not_available, lgv$log_names [log_control_descriptor_p^.log],
            status);
      RETURN;
    IFEND;

{ Lock the log before establishing the condition handler so that if the log was already locked
{ by another procedure in this task, this procedure doesn't unlock it.

    lgp$lock_log (log_control_descriptor_p);
    osp$establish_condition_handler (^condition_handler, TRUE);

  /log_locked/
    BEGIN

{ Verify that the specified log cycle matches the log cycle in the log control descriptor.

      IF log_cycle <> log_control_descriptor_p^.log_cycle THEN
        osp$set_status_abnormal ('LG', lge$log_cycles_do_not_match,
              lgv$log_names [log_control_descriptor_p^.log], status);
        EXIT /log_locked/;
      IFEND;

{ Get the log entry header.

      NEXT log_entry_header_p IN log_data;
      IF log_entry_header_p = NIL THEN
        osp$set_status_abnormal ('LG', lge$corrupted_log, lgv$log_names [log_control_descriptor_p^.log],
              status);
        EXIT /log_locked/;
      ELSE
        previous_size := log_entry_header_p^.previous_size;
      IFEND;

    END /log_locked/;

{ Release the log interlock.

    lgp$unlock_log (log_control_descriptor_p);
    osp$disestablish_cond_handler;

  PROCEND lgp$get_previous_log_entry_size;
?? OLDTITLE ??
?? NEWTITLE := '[INLINE] lgp$lock_log', EJECT ??
{ PURPOSE:
{   This procedure is used to set the appropriate type of lock on a log.

  PROCEDURE [INLINE] lgp$lock_log
    (    log_control_descriptor_p: ^lgt$log_control_descriptor);

    IF log_control_descriptor_p^.log IN (-$pmt$global_logset []) THEN
      osp$set_mainframe_sig_lock (log_control_descriptor_p^.lock);
    ELSE
      osp$set_job_signature_lock (log_control_descriptor_p^.lock);
    IFEND;

  PROCEND lgp$lock_log;
?? OLDTITLE ??
?? NEWTITLE := '[INLINE] lgp$unlock_log', EJECT ??
{ PURPOSE:
{   This procedure is used to clear the lock on a log.

  PROCEDURE [INLINE] lgp$unlock_log
    (    log_control_descriptor_p: ^lgt$log_control_descriptor);

    IF log_control_descriptor_p^.log IN (-$pmt$global_logset []) THEN
      osp$clear_mainframe_sig_lock (log_control_descriptor_p^.lock);
    ELSE
      osp$clear_job_signature_lock (log_control_descriptor_p^.lock);
    IFEND;

  PROCEND lgp$unlock_log;
?? OLDTITLE ??
MODEND lgm$common_processors_r2;
