MODULE dmm$idle_system;
?? RIGHT := 110 ??

{ PURPOSE:
{   Idle / Resume and Terminate System Interfaces.

?? PUSH (LISTEXT := ON) ??
?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
*copyc dmt$error_condition_codes
*copyc tmt$rb_update_job_task_enviro
?? POP ??
*copyc i#call_monitor
*copyc dmp$activate_volume
*copyc dmp$change_dfl_damage
*copyc dmp$deactivate_volume
*copyc dmp$evacuate_active_device_log
*copyc dmp$get_active_vol_attributes
*copyc dmp$get_avt_logging_info
*copyc dmp$get_disk_file_descriptor_p
*copyc dmp$get_fmd_by_index
*copyc dmp$process_device_log_entry
*copyc dmp$split_allocation_log
*copyc dpp$put_critical_message
*copyc gfp$count_all_fdes
*copyc gfp$get_sfid_from_fde_p
*copyc gfp$lock_fde
*copyc gfp$scan_all_fdes
*copyc gfp$unlock_fde_p
*copyc mmp$issue_ring1_segment_request
*copyc osp$set_status_abnormal
*copyc osp$system_error
*copyc pmp$delay
*copyc syp$disable_job_recovery
*copyc syp$enable_job_recovery
*copyc tmp$ready_system_task1
*copyc dmv$active_volume_table
*copyc dmv$display_recovery_messages
*copyc dmv$ds_msg_update_interval
*copyc dmv$minimum_log_count
*copyc gfv$null_sfid
*copyc jmv$system_ijl_ordinal
*copyc osv$mainframe_pageable_heap
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared By This Module' ??

  VAR
    dmv$idle_system: [XDCL] boolean := FALSE,

    v$idle_info: ^array [ * ] of record
      active: boolean,
      dat: gft$system_file_identifier,
      login: gft$system_file_identifier,
      dfl: gft$system_file_identifier,
      dlog: gft$system_file_identifier,
      dir: gft$system_file_identifier,
    recend := NIL;

?? OLDTITLE ??
?? NEWTITLE := '[xdcl, #gate] DMP$IDLE_SYSTEM' ??

{The purpose of this request is to idle the device management area of the system so that
{the next system deadstart may be a continuation rather than a recovery deadstart.

  PROCEDURE [XDCL, #GATE] dmp$idle_system;

    CONST
      second = 1000000,
      ten_milliseconds = 10;

    VAR
      able: boolean,
      avt_index: dmt$active_volume_table_index,
      base: integer,
      change_sys_tasks_request_block: tmt$rb_update_job_task_enviro,
      clock: integer,
      display_update_interval: integer,
      entry_p: ^dmt$active_volume_table_entry,
      fdes_scanned: integer,
      file_count: integer,
      ignore_status: ost$status,
      info: dmt$avt_logging_info,
      not_recovered_file_count: integer,
      p_fde: gft$file_desc_entry_p,
      server_file_count: integer,
      scan_control: gft$scan_all_fdes_state,
      status: ost$status,
      st: string (100),
      stl: integer,
      total_fde_count: integer;

    file_count := 0;
    fdes_scanned := 0;
    server_file_count := 0;
    not_recovered_file_count := 0;

    base := 0;
    display_update_interval := dmv$ds_msg_update_interval * second;

    IF dmv$display_recovery_messages THEN

      { We first count all FDEs in use to get the operator an idea of how long the idle may take.

      total_fde_count := gfp$count_all_fdes (gfc$tr_system);
      STRINGREP (st, stl, ' .. Scanning FDEs: Total Number of FDEs in use: ', total_fde_count, '   ');
      dpp$put_critical_message (st (1, stl), status);
    IFEND;

    gfp$scan_all_fdes (gfc$tr_system, scan_control, p_fde);

    WHILE p_fde <> NIL DO
      fdes_scanned := fdes_scanned + 1;

{Skip Device Files and Files attached for read-only
      IF (p_fde^.file_kind <> gfc$fk_device_file) AND (p_fde^.attached_in_write_count >= 1) THEN
        IF p_fde^.media = gfc$fm_served_file THEN
          server_file_count := server_file_count + 1;
          idle_server_file (p_fde, status);
        ELSEIF p_fde^.media = gfc$fm_mass_storage_file THEN
          file_count := file_count + 1;
          idle_file (p_fde, status);
        IFEND;
        IF NOT status.normal THEN
          not_recovered_file_count := not_recovered_file_count + 1;
          STRINGREP (st, stl, 'Job recovery disabled; cannot idle file due to: ', status.condition);
          dpp$put_critical_message (st (1, stl), status);
          syp$disable_job_recovery;
        IFEND;
      IFEND;

      gfp$scan_all_fdes (gfc$tr_null_residence, scan_control, p_fde);

      IF dmv$display_recovery_messages THEN
        clock := #FREE_RUNNING_CLOCK (0);
        IF clock > (base + display_update_interval) THEN
          base := clock;
          STRINGREP (st, stl, ' .. Scanning FDEs:', fdes_scanned, ' Idled:', file_count + server_file_count,
                ' Not Recovered:', not_recovered_file_count);
          dpp$put_critical_message (st (1, stl), status);
        IFEND;
      IFEND;

    WHILEND;

    STRINGREP (st, stl, file_count, ' File(s) idled');
    dpp$put_critical_message (st (1, stl), status);
    STRINGREP (st, stl, server_file_count, ' Server File(s) idled');
    dpp$put_critical_message (st (1, stl), status);

    {Flush device logs
    {Ensure all entries are moved to the device logs.  dmp$evacuate_active_device_log will
    {not do this for down devices.

    dmp$split_allocation_log (FALSE, status);

    FOR avt_index := LOWERBOUND (dmv$active_volume_table.table_p^) TO
          UPPERBOUND (dmv$active_volume_table.table_p^) DO
      entry_p := ^dmv$active_volume_table.table_p^ [avt_index];
      IF NOT entry_p^.entry_available THEN
        IF entry_p^.mass_storage.p_device_log <> gfv$null_sfid THEN
          STRINGREP (st, stl, 'Flushing volume: ', entry_p^.mass_storage.recorded_vsn);
          dpp$put_critical_message (st (1, stl), status);

          dmp$evacuate_active_device_log (avt_index, status);
          IF NOT status.normal THEN
            STRINGREP (st, stl, 'Cannot flush volume due to: ', status.condition);
            dpp$put_critical_message (st (1, stl), ignore_status);
          IFEND;
          dmp$get_avt_logging_info (avt_index, info, able);
          IF (NOT status.normal) OR ((info.log_entry_count > dmv$minimum_log_count) AND
                (entry_p^.mass_storage.volume_unavailable)) THEN
            STRINGREP (st, stl, 'Log entry count = ', info.log_entry_count);
            dpp$put_critical_message (st (1, stl), ignore_status);
            syp$disable_job_recovery;
            dpp$put_critical_message ('Job recovery disabled', status);
          IFEND;

        IFEND;
      IFEND;
    FOREND;

{ Call monitor to idle the Device_Management system tasks.

    REPEAT
      change_sys_tasks_request_block.reqcode := syc$rc_update_job_task_enviro;
      change_sys_tasks_request_block.status.normal := TRUE;
      change_sys_tasks_request_block.subcode := tmc$ujte_idle_dm_sys_tasks;
      i#call_monitor (#LOC (change_sys_tasks_request_block), #SIZE (change_sys_tasks_request_block));
      IF NOT change_sys_tasks_request_block.status.normal THEN
        pmp$delay (ten_milliseconds, ignore_status);
      IFEND;
    UNTIL change_sys_tasks_request_block.status.normal;

    IF v$idle_info = NIL THEN
      ALLOCATE v$idle_info: [LOWERBOUND (dmv$active_volume_table.table_p^
            ) .. UPPERBOUND (dmv$active_volume_table.table_p^)] IN osv$mainframe_pageable_heap^;
    IFEND;

    FOR avt_index := LOWERBOUND (dmv$active_volume_table.table_p^) TO
          UPPERBOUND (dmv$active_volume_table.table_p^) DO

      entry_p := ^dmv$active_volume_table.table_p^ [avt_index];
      v$idle_info^ [avt_index].active := FALSE;

      IF NOT entry_p^.entry_available THEN
        STRINGREP (st, stl, 'Idling volume: ', entry_p^.mass_storage.recorded_vsn);
        dpp$put_critical_message (st (1, stl), status);

        v$idle_info^ [avt_index].active := TRUE;
        v$idle_info^ [avt_index].dat := entry_p^.mass_storage.p_device_allocation_table;
        v$idle_info^ [avt_index].dfl := entry_p^.mass_storage.p_device_file_list_table;
        v$idle_info^ [avt_index].dir := entry_p^.mass_storage.p_directory;
        v$idle_info^ [avt_index].login := entry_p^.mass_storage.p_login_table;
        v$idle_info^ [avt_index].dlog := entry_p^.mass_storage.p_device_log;

        IF entry_p^.mass_storage.p_device_log <> gfv$null_sfid THEN
          IF NOT entry_p^.mass_storage.volume_unavailable THEN
            dmp$deactivate_volume (avt_index, status);
            IF NOT status.normal THEN
              STRINGREP (st, stl, 'Cannot idle volume due to: ', status.condition);
              dpp$put_critical_message (st (1, stl), status);
              osp$system_error ('Cannot idle', ^status);
            IFEND;
          ELSE
            dpp$put_critical_message ('Cannot idle a down volume', status);
          IFEND;
        IFEND;

        entry_p^.mass_storage.p_device_allocation_table := gfv$null_sfid;
        entry_p^.mass_storage.p_device_file_list_table := gfv$null_sfid;
        entry_p^.mass_storage.p_directory := gfv$null_sfid;
        entry_p^.mass_storage.p_login_table := gfv$null_sfid;
      IFEND;
    FOREND;

    dmv$idle_system := TRUE;

  PROCEND dmp$idle_system;
?? OLDTITLE ??
?? NEWTITLE := '[xdcl, #gate] DMP$RESUME_SYSTEM' ??

  PROCEDURE [XDCL, #GATE] dmp$resume_system;

    VAR
      able: boolean,
      avt_index: dmt$active_volume_table_index,
      change_sys_tasks_request_block: tmt$rb_update_job_task_enviro,
      entry_p: ^dmt$active_volume_table_entry,
      file_count: integer,
      p_fde: gft$file_desc_entry_p,
      scan_control: gft$scan_all_fdes_state,
      st: string (100),
      status: ost$status,
      stl: integer;

    FOR avt_index := LOWERBOUND (dmv$active_volume_table.table_p^) TO
          UPPERBOUND (dmv$active_volume_table.table_p^) DO
      entry_p := ^dmv$active_volume_table.table_p^ [avt_index];
      IF NOT entry_p^.entry_available THEN
        entry_p^.mass_storage.p_device_allocation_table := v$idle_info^ [avt_index].dat;
        entry_p^.mass_storage.p_device_file_list_table := v$idle_info^ [avt_index].dfl;
        entry_p^.mass_storage.p_directory := v$idle_info^ [avt_index].dir;
        entry_p^.mass_storage.p_login_table := v$idle_info^ [avt_index].login;
        entry_p^.mass_storage.p_device_log := v$idle_info^ [avt_index].dlog;

        IF entry_p^.mass_storage.p_device_log <> gfv$null_sfid THEN

          STRINGREP (st, stl, 'Resuming volume: ', entry_p^.mass_storage.recorded_vsn);
          dpp$put_critical_message (st (1, stl), status);

          IF NOT entry_p^.mass_storage.volume_unavailable THEN
            dmp$activate_volume (entry_p^.logical_unit_number, status);
            IF NOT status.normal THEN
              STRINGREP (st, stl, 'Cannot resume volume due to: ', status.condition);
              dpp$put_critical_message (st (1, stl), status);
              osp$system_error ('Cannot resume', ^status);
            IFEND;
          ELSE
            dpp$put_critical_message ('Job recovery enabled', status);
            syp$enable_job_recovery;
          IFEND;

        IFEND;
      IFEND;
    FOREND;

{ Call monitor to restart the Device_Management system tasks.

    change_sys_tasks_request_block.reqcode := syc$rc_update_job_task_enviro;
    change_sys_tasks_request_block.status.normal := TRUE;
    change_sys_tasks_request_block.subcode := tmc$ujte_restart_dm_systasks;
    i#call_monitor (#LOC (change_sys_tasks_request_block), #SIZE (change_sys_tasks_request_block));

    file_count := 0;

    gfp$scan_all_fdes (gfc$tr_system, scan_control, p_fde);

    WHILE p_fde <> NIL DO

      IF (p_fde^.file_kind <> gfc$fk_device_file) AND (p_fde^.attached_in_write_count >= 1) AND
            (p_fde^.media = gfc$fm_mass_storage_file) THEN
        file_count := file_count + 1;
        resume_file (p_fde, status);
        IF NOT status.normal THEN
          STRINGREP (st, stl, 'Cannot resume file due to: ', status.condition);
          dpp$put_critical_message (st (1, stl), status);
        IFEND;
      IFEND;

      gfp$scan_all_fdes (gfc$tr_null_residence, scan_control, p_fde);
    WHILEND;

    STRINGREP (st, stl, file_count, ' File(s) resumed');
    dpp$put_critical_message (st (1, stl), status);

    dmv$idle_system := FALSE;

  PROCEND dmp$resume_system;
?? OLDTITLE ??
?? NEWTITLE := 'IDLE_FILE' ??

{This procedure was adapted from dmp$detach_file.  The operations performed must allow
{the next system deadstart to be a continuation deadstart rather than a recovery deadstart.
{The operations must be *reversible* by dmp$resume_system (e.g. without a deadstart)

  PROCEDURE idle_file
    (    p_fde: gft$file_desc_entry_p;
     VAR status: ost$status);

    VAR
      avt_entry_found: boolean,
      avt_index: dmt$active_volume_table_index,
      fmd_index: dmt$fmd_index,
      entry_to_be_processed: boolean,
      length: 8 .. 120,
      lock_status: ost$status,
      log_entry: dmt$dl_entry,
      number_of_fmds: dmt$fmd_index,
      active_vol_attributes: array [1 .. 1] of dmt$assigned_ms_vol_attribute,
      p_fmd: ^dmt$file_medium_descriptor,
      p_dfd: ^dmt$disk_file_descriptor,
      p_rb_ring1_segment_request: ^mmt$rb_ring1_segment_request,
      recorded_vsn: rmt$recorded_vsn,
      sfid: gft$system_file_identifier;

    status.normal := TRUE;

    gfp$lock_fde (p_fde);


  /file_descriptor_locked/
    BEGIN
      gfp$get_sfid_from_fde_p (p_fde, sfid);
      dmp$get_disk_file_descriptor_p (p_fde, p_dfd);

      IF NOT p_dfd^.p_fmd^.volume_assigned OR p_dfd^.purged THEN
        EXIT /file_descriptor_locked/; {----->
      IFEND;

      PUSH p_rb_ring1_segment_request;

      p_rb_ring1_segment_request^.reqcode := syc$rc_ring1_segment_request;
      p_rb_ring1_segment_request^.request := mmc$sr1_detach_file;
      p_rb_ring1_segment_request^.sfid := sfid;
      p_rb_ring1_segment_request^.wait_for_io_complete := TRUE;
      p_rb_ring1_segment_request^.status.normal := TRUE;

      mmp$issue_ring1_segment_request (p_rb_ring1_segment_request^);
      IF NOT p_rb_ring1_segment_request^.status.normal THEN
        osp$set_status_abnormal (dmc$device_manager_ident, p_rb_ring1_segment_request^.status.condition, '',
              status);
        EXIT /file_descriptor_locked/; {----->
      IFEND;


      avt_index := p_dfd^.p_fmd^.avt_index;
      recorded_vsn := dmv$active_volume_table.table_p^ [avt_index].mass_storage.recorded_vsn;

      IF p_fde^.attached_in_write_count > 0 THEN
        active_vol_attributes [1].keyword := dmc$ms_mainframe_assigned;

        dmp$get_active_vol_attributes (recorded_vsn, avt_index, active_vol_attributes, avt_entry_found);
        IF NOT avt_entry_found THEN
          osp$set_status_abnormal (dmc$device_manager_ident, dme$avt_entry_not_found,
                'unable to locate avt entry - idle_system', status);
          EXIT /file_descriptor_locked/; {----->
        IFEND;

        log_entry.kind := dmc$dl_detach_file;
        log_entry.attach_file_block.global_file_name := p_fde^.global_file_name;
        log_entry.attach_file_block.dfl_index := p_dfd^.p_fmd^.dfl_index;
        log_entry.attach_file_block.mainframe_assigned := active_vol_attributes [1].mainframe_assigned;

        dmp$process_device_log_entry (avt_index, log_entry, status);
        IF NOT status.normal THEN
          EXIT /file_descriptor_locked/; {----->
        IFEND;
      IFEND;

      IF p_dfd^.damaged_detection_enabled AND NOT p_dfd^.file_damaged THEN
        IF (p_fde^.queue_status = gfc$qs_global_shared) OR (p_fde^.queue_status = gfc$qs_job_working_set) THEN
          log_entry.kind := dmc$dl_file_damaged;
          log_entry.file_damaged_block.global_file_name := p_fde^.global_file_name;
          log_entry.file_damaged_block.dfl_index := p_dfd^.p_fmd^.dfl_index;
          log_entry.file_damaged_block.add_damage := $dmt$file_damage [];
          log_entry.file_damaged_block.remove_damage := $dmt$file_damage [dmc$media_image_inconsistent];
        IFEND;
        dmp$process_device_log_entry (avt_index, log_entry, status);
        IF NOT status.normal THEN
          EXIT /file_descriptor_locked/; {----->
        IFEND;
      IFEND;

      IF (p_dfd^.dfd_modified OR p_fde^.flags.eoi_modified) THEN
        log_entry.kind := dmc$dl_update_file_length;
        log_entry.file_length_block.global_file_name := p_fde^.global_file_name;
        log_entry.file_length_block.dfl_index := p_dfd^.p_fmd^.dfl_index;

        log_entry.file_length_block.eof_specified := TRUE;
        log_entry.file_length_block.eof := (p_fde^.eoi_byte_address + p_dfd^.bytes_per_allocation - 1) DIV
              p_dfd^.bytes_per_allocation * p_dfd^.bytes_per_allocation;

        log_entry.file_length_block.eoi_specified := TRUE;
        log_entry.file_length_block.eoi := p_fde^.eoi_byte_address;

        dmp$process_device_log_entry (avt_index, log_entry, status);
        IF NOT status.normal THEN
          EXIT /file_descriptor_locked/; {----->
        IFEND;

        number_of_fmds := p_dfd^.number_of_fmds;

        log_entry.kind := dmc$dl_update_fmd_length;
        log_entry.fmd_length_block.global_file_name := p_fde^.global_file_name;
        log_entry.fmd_length_block.fmd_length_specified := TRUE;
        log_entry.fmd_length_block.logical_length_specified := TRUE;

        FOR fmd_index := 1 TO number_of_fmds DO
          dmp$get_fmd_by_index (p_dfd, fmd_index, p_fmd);
          entry_to_be_processed := (p_fmd^.in_use) AND p_fmd^.volume_assigned;

          IF entry_to_be_processed THEN
            avt_index := p_fmd^.avt_index;
            log_entry.fmd_length_block.dfl_index := p_fmd^.dfl_index;
            log_entry.fmd_length_block.fmd_length := p_fmd^.fmd_allocated_length;
            log_entry.fmd_length_block.logical_length := p_fmd^.fmd_allocated_length;

            dmp$process_device_log_entry (avt_index, log_entry, status);
            IF NOT status.normal THEN
              EXIT /file_descriptor_locked/; {----->
            IFEND;

          IFEND;
        FOREND;
      IFEND;

    END /file_descriptor_locked/;

    gfp$unlock_fde_p (p_fde);


  PROCEND idle_file;
?? OLDTITLE ??
?? NEWTITLE := 'IDLE_SERVER_FILE' ??

{ This procedure was adapted from IDLE_FILE (above).

  PROCEDURE idle_server_file
    (    p_fde: gft$file_desc_entry_p;
     VAR status: ost$status);

    VAR
      p_rb_ring1_segment_request: ^mmt$rb_ring1_segment_request,
      sfid: gft$system_file_identifier;


    status.normal := TRUE;
    gfp$get_sfid_from_fde_p (p_fde, sfid);

    PUSH p_rb_ring1_segment_request;
    p_rb_ring1_segment_request^.reqcode := syc$rc_ring1_segment_request;
    p_rb_ring1_segment_request^.sfid := sfid;
    p_rb_ring1_segment_request^.wait_for_io_complete := TRUE;
    p_rb_ring1_segment_request^.status.normal := TRUE;
    p_rb_ring1_segment_request^.request := mmc$sr1_detach_file;
    mmp$issue_ring1_segment_request (p_rb_ring1_segment_request^);
    IF NOT p_rb_ring1_segment_request^.status.normal THEN
      osp$set_status_abnormal (dmc$device_manager_ident, p_rb_ring1_segment_request^.status.condition, '',
            status);
    IFEND;

  PROCEND idle_server_file;
?? OLDTITLE ??
?? NEWTITLE := 'RESUME_FILE' ??

  PROCEDURE resume_file
    (    p_fde: gft$file_desc_entry_p;
     VAR status: ost$status);

    VAR
      avt_entry_found: boolean,
      avt_index: dmt$active_volume_table_index,
      lock_status: ost$status,
      log_entry: dmt$dl_entry,
      active_vol_attributes: array [1 .. 1] of dmt$assigned_ms_vol_attribute,
      p_dfd: ^dmt$disk_file_descriptor,
      recorded_vsn: rmt$recorded_vsn;

    status.normal := TRUE;

    gfp$lock_fde (p_fde);

  /file_descriptor_locked/
    BEGIN
      dmp$get_disk_file_descriptor_p (p_fde, p_dfd);

      IF (NOT p_dfd^.p_fmd^.volume_assigned) OR p_dfd^.purged THEN
        EXIT /file_descriptor_locked/; {----->
      IFEND;

      avt_index := p_dfd^.p_fmd^.avt_index;
      recorded_vsn := dmv$active_volume_table.table_p^ [avt_index].mass_storage.recorded_vsn;

      IF p_fde^.attached_in_write_count > 0 THEN
        active_vol_attributes [1].keyword := dmc$ms_mainframe_assigned;

        dmp$get_active_vol_attributes (recorded_vsn, avt_index, active_vol_attributes, avt_entry_found);
        IF NOT avt_entry_found THEN
          osp$set_status_abnormal (dmc$device_manager_ident, dme$avt_entry_not_found,
                'unable to locate avt entry - resume_system', status);
          EXIT /file_descriptor_locked/; {----->
        IFEND;

        log_entry.kind := dmc$dl_attach_file;
        log_entry.attach_file_block.global_file_name := p_fde^.global_file_name;
        log_entry.attach_file_block.dfl_index := p_dfd^.p_fmd^.dfl_index;
        log_entry.attach_file_block.mainframe_assigned := active_vol_attributes [1].mainframe_assigned;

        dmp$process_device_log_entry (avt_index, log_entry, status);
        IF NOT status.normal THEN
          EXIT /file_descriptor_locked/; {----->
        IFEND;

        IF p_dfd^.damaged_detection_enabled AND NOT p_dfd^.file_damaged THEN
          dmp$change_dfl_damage (avt_index, $dmt$file_damage [dmc$media_image_inconsistent],
                $dmt$file_damage [], p_dfd^.p_fmd^.dfl_index, {flush_device_log} TRUE,
                p_fde^.global_file_name, status);
        IFEND;
      IFEND;

    END /file_descriptor_locked/;

    gfp$unlock_fde_p (p_fde);

  PROCEND resume_file;
?? OLDTITLE ??
MODEND dmm$idle_system;

