?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Device Management' ??
MODULE dmm$volume_online;
?? RIGHT := 110 ??

{ PURPOSE:
{  The purpose of this module is to bring a volume online/offline.  A
{  volume is brought offline by removing it from the Active Volume Table.  A
{  volume is brought online by registering it in the Active Volume Table.
{  Two versions of volume online exist.  One to introduce the volume
{  to a mainframe, and allow the mainframe to log in to the volume, the
{  other is to allow the volume to be initialized.  The volumes must be
{  brought offline using corresponding requests.  This module executes
{  in ring 1.

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc rmd$volume_declarations
*copyc mme$condition_codes
*copyc dmt$active_volume_table_index
*copyc dmt$error_condition_codes
*copyc dmt$ms_labels
*copyc dmt$ms_volume_label
*copyc dmt$physical_device_attributes
*copyc ost$status
*copyc ost$wait
*copyc stt$set_ordinal
?? POP ??
*copyc dmp$attach_dat_from_label
*copyc dmp$attach_dflt_from_label
*copyc dmp$attach_directory_from_label
*copyc dmp$attach_volume_device_file
*copyc dmp$create_mat
*copyc dmp$create_mfl
*copyc dmp$delete_mat
*copyc dmp$delete_mfl
*copyc dmp$destroy_file
*copyc dmp$detach_device_file
*copyc dmp$get_unused_avt_entry
*copyc dmp$locate_volume_label
*copyc dmp$lock_avt_entry
*copyc dmp$search_avt_by_lun
*copyc dmp$search_avt_by_rvsn
*copyc dmp$unlock_avt_entry
*copyc dpp$put_critical_message
*copyc lgp$add_entry_to_system_log
*copyc osp$append_status_integer
*copyc osp$append_status_parameter
*copyc osp$fatal_system_error
*copyc osp$initialize_sig_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$unpack_status_condition
*copyc dmv$active_volume_table
*copyc dmv$debug_options
*copyc gfv$null_sfid
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared By This Module', EJECT ??

  TYPE
    t$delete_mat_mfl = (c$dmm_no_delete, c$dmm_delete_mfl, c$dmm_delete_mat_and_mfl);

  VAR
    dmv$volume_class_kludge: [XREF] boolean;

?? OLDTITLE ??
?? NEWTITLE := 'P$ATTACH_DEVICE_FILES', EJECT ??

  PROCEDURE p$attach_device_files
    (    avt_index: dmt$active_volume_table_index;
         volume_label_p: ^dmt$ms_volume_label;
     VAR status: ost$status);

    VAR
      attach_status: ost$status,
      avt_entry_p: ^dmt$ms_active_vol_table_entry,
      dat_sfid: gft$system_file_identifier,
      dflt_sfid: gft$system_file_identifier,
      directory_sfid: gft$system_file_identifier,
      file_modified: boolean,
      fmd_modified: boolean;

?? NEWTITLE := 'P$LOG_ERROR', EJECT ??

    PROCEDURE p$log_error
      (    text: string ( * );
           error_status: ost$status);

      VAR
        identifier: ost$status_identifier,
        l: integer,
        log_status: ost$status,
        logtime: ost$time,
        number: ost$status_condition_number,
        s: string (60);

      osp$unpack_status_condition (error_status.condition, identifier, number);
      STRINGREP (s, l, text, avt_index, ' ', identifier, number);
      dpp$put_critical_message (s, log_status);
      dpp$put_critical_message (error_status.text.value (1, error_status.text.size), log_status);
      lgp$add_entry_to_system_log (pmc$msg_origin_system, s, logtime, log_status);
      lgp$add_entry_to_system_log (pmc$msg_origin_system, error_status.text.value (1, error_status.text.size),
            logtime, log_status);

    PROCEND p$log_error;
?? OLDTITLE ??
?? EJECT ??

    status.normal := TRUE;
    avt_entry_p := ^dmv$active_volume_table.table_p^ [avt_index].mass_storage;
    avt_entry_p^.p_device_allocation_table := gfv$null_sfid;
    avt_entry_p^.p_directory := gfv$null_sfid;
    avt_entry_p^.p_device_file_list_table := gfv$null_sfid;

{DAT
    dmp$attach_dat_from_label (volume_label_p^, avt_index, dat_sfid, status);
    IF NOT status.normal THEN
      p$log_error (' DM: CANT ATTACH DAT AVT ', status);
      RETURN; {----->
    IFEND;

    avt_entry_p^.p_device_allocation_table := dat_sfid;

{DIRECTORY
    dmp$attach_directory_from_label (volume_label_p^, dat_sfid, directory_sfid, status);
    IF NOT status.normal THEN
      p$log_error (' DM: CANT ATTACH DIRECTORY  AVT ', status);
      p$detach_device_files (c$dmm_no_delete, avt_entry_p^);
      RETURN; {----->
    IFEND;

    avt_entry_p^.p_directory := directory_sfid;

{DFLT
    dmp$attach_dflt_from_label (volume_label_p^, dat_sfid, dflt_sfid, status);
    IF NOT status.normal THEN
      p$log_error (' DM: CANT ATTACH DFLT AVT ', status);
      p$detach_device_files (c$dmm_no_delete, avt_entry_p^);
      RETURN; {----->
    IFEND;

    avt_entry_p^.p_device_file_list_table := dflt_sfid;

  PROCEND p$attach_device_files;
?? OLDTITLE ??
?? NEWTITLE := 'P$CREATE_NEW_MS_AVT_ENTRY', EJECT ??

  PROCEDURE p$create_new_ms_avt_entry
    (    volume_label_p: ^dmt$ms_volume_label;
         logical_unit_number: iot$logical_unit;
         avt_index: dmt$active_volume_table_index;
     VAR status: ost$status);

    VAR
      avt_entry_p: ^dmt$ms_active_vol_table_entry,
      create_status: ost$status,
      dat_sfid: gft$system_file_identifier,
      dfl_sfid: gft$system_file_identifier,
      file_modified: boolean,
      fmd_modified: boolean,
      identifier: ost$status_identifier,
      l: integer,
      log_status: ost$status,
      login_table_name: ost$name,
      login_table_sfid: gft$system_file_identifier,
      logtime: ost$time,
      number: ost$status_condition_number,
      p_mat: cyt$adaptable_array_pointer,
      p_volume_label: ^dmt$ms_volume_label,
      p_volume_label_0_0: ^dmt$ms_label_0_0,
      p_volume_label_header: ^dmt$volume_label_header;

    status.normal := TRUE;
    p_volume_label := volume_label_p;
    RESET p_volume_label;
    NEXT p_volume_label_header IN p_volume_label;
    NEXT p_volume_label_0_0 IN p_volume_label;

    dmv$active_volume_table.table_p^ [avt_index].logical_unit_number := logical_unit_number;
    avt_entry_p := ^dmv$active_volume_table.table_p^ [avt_index].mass_storage;

    IF (avt_index = 1) {system device} THEN
      {Allocation is done on the system device before it is activated.  This bad
      {practice should be eliminated if we can ever figure out how.
      avt_entry_p^.allocation_allowed := TRUE;
    ELSE
      {Wait for volume activation to allow allocation on other devices.
      avt_entry_p^.allocation_allowed := FALSE;
    IFEND;

    avt_entry_p^.space_low := FALSE;
    avt_entry_p^.space_gone := FALSE;
    avt_entry_p^.disk_table_status := $dmt$ms_volume_table_status [];

    IF dmv$volume_class_kludge THEN
      avt_entry_p^.class := -$dmt$class [];
    ELSE
      avt_entry_p^.class := p_volume_label_0_0^.class;
    IFEND;

    avt_entry_p^.logged_in_for_recovery := FALSE;
    avt_entry_p^.recorded_vsn := p_volume_label_header^.recorded_vsn;
    avt_entry_p^.internal_vsn := p_volume_label_header^.internal_vsn;
    avt_entry_p^.volume_owner := p_volume_label_0_0^.owner_id;
    avt_entry_p^.current_position_offset_in_log := 0;

    osp$initialize_sig_lock (avt_entry_p^.update_lock);
    osp$initialize_sig_lock (avt_entry_p^.logging_lock);

    avt_entry_p^.set_name := osc$null_name;
    avt_entry_p^.status := $dmt$ms_volume_system_status [dmc$system_dismounted, dmc$mainframe_dismounted];
    avt_entry_p^.p_device_log := gfv$null_sfid;
    avt_entry_p^.p_login_table := gfv$null_sfid;

    p$attach_device_files (avt_index, volume_label_p, status);
    IF NOT status.normal THEN
      avt_entry_p^.recorded_vsn := '';
      dmv$active_volume_table.table_p^ [avt_index].entry_available := TRUE;
      dmv$active_volume_table.table_p^ [avt_index].logical_unit_number := 0;
      RETURN; {----->
    IFEND;

    dat_sfid := avt_entry_p^.p_device_allocation_table;
    dfl_sfid := avt_entry_p^.p_device_file_list_table;

    dmp$create_mfl (avt_entry_p^.p_mfl);
    dmp$create_mat (avt_index, dat_sfid, p_mat, status);
    IF NOT status.normal THEN
      p$detach_device_files (c$dmm_delete_mfl, avt_entry_p^);
      RETURN; {----->
    IFEND;

    avt_entry_p^.p_mat := p_mat;

    login_table_name := 'LOGIN_TABLE';
    login_table_name (12, rmc$recorded_vsn_size) := avt_entry_p^.recorded_vsn;

    dmp$attach_volume_device_file (login_table_name, avt_entry_p^.p_directory, dfl_sfid, dat_sfid, avt_index,
          login_table_sfid, status);
    IF NOT status.normal THEN
      p$detach_device_files (c$dmm_delete_mat_and_mfl, avt_entry_p^);
      RETURN; {----->
    IFEND;

    avt_entry_p^.p_login_table := login_table_sfid;
    avt_entry_p^.status := avt_entry_p^.status + $dmt$ms_volume_system_status [dmc$system_mounted];
    avt_entry_p^.status := avt_entry_p^.status - $dmt$ms_volume_system_status [dmc$system_dismounted];

  PROCEND p$create_new_ms_avt_entry;
?? OLDTITLE ??
?? NEWTITLE := 'P$DETACH_DEVICE_FILES', EJECT ??
{
{   The purpose of this request is to detach any of the volume device files
{ that are still attached.  Any bad status from the detach is ignored, unless
{ debug_device_manager is turned on.

  PROCEDURE p$detach_device_files
    (    delete_mat_mfl: t$delete_mat_mfl;
     VAR avt_entry: dmt$ms_active_vol_table_entry);

    VAR
      file_modified: boolean,
      fmd_modified: boolean,
      sfid: gft$system_file_identifier,
      status: ost$status;

{ Detach or destroy device log. }
    sfid := avt_entry.p_device_log;
    IF (sfid <> gfv$null_sfid) THEN
      IF avt_entry.logged_in_for_recovery THEN
        dmp$destroy_file (sfid, sfc$no_limit, status);
      ELSE
        dmp$detach_device_file (sfid, file_modified, fmd_modified, status);
      IFEND;
      IF status.normal THEN
        avt_entry.p_device_log := gfv$null_sfid;
      ELSEIF (dmc$debug_device_manager IN dmv$debug_options) THEN
        osp$fatal_system_error ('Detach or destroy device log failed - p$detach_device_files', ^status);
      IFEND;
    IFEND;

{ Detach login table. }
    sfid := avt_entry.p_login_table;
    IF (sfid <> gfv$null_sfid) THEN
      dmp$detach_device_file (sfid, file_modified, fmd_modified, status);
      IF status.normal THEN
        avt_entry.p_login_table := gfv$null_sfid;
      ELSEIF (dmc$debug_device_manager IN dmv$debug_options) THEN
        osp$fatal_system_error ('Detach login table failed - p$detach_device_files', ^status);
      IFEND;
    IFEND;

{ Detach DFL. }
    sfid := avt_entry.p_device_file_list_table;
    IF (sfid <> gfv$null_sfid) THEN
      dmp$detach_device_file (sfid, file_modified, fmd_modified, status);
      IF status.normal THEN
        avt_entry.p_device_file_list_table := gfv$null_sfid;
      ELSEIF (dmc$debug_device_manager IN dmv$debug_options) THEN
        osp$fatal_system_error ('Detach DFL failed - p$detach_device_files', ^status);
      IFEND;
    IFEND;

{ Detach directory. }
    sfid := avt_entry.p_directory;
    IF (sfid <> gfv$null_sfid) THEN
      dmp$detach_device_file (sfid, file_modified, fmd_modified, status);
      IF status.normal THEN
        avt_entry.p_directory := gfv$null_sfid;
      ELSEIF (dmc$debug_device_manager IN dmv$debug_options) THEN
        osp$fatal_system_error ('Detach directory failed - p$detach_device_files', ^status);
      IFEND;
    IFEND;

{ Detach DAT. }
    sfid := avt_entry.p_device_allocation_table;
    IF (sfid <> gfv$null_sfid) THEN
      dmp$detach_device_file (sfid, file_modified, fmd_modified, status);
      IF status.normal THEN
        avt_entry.p_device_allocation_table := gfv$null_sfid;
      ELSEIF (dmc$debug_device_manager IN dmv$debug_options) THEN
        osp$fatal_system_error ('Detach DAT failed - p$detach_device_files', ^status);
      IFEND;
    IFEND;

{ Delete MAT / MFL
    IF delete_mat_mfl = c$dmm_no_delete THEN
      RETURN; {----->
    IFEND;

    dmp$delete_mfl (avt_entry.p_mfl);

    IF delete_mat_mfl = c$dmm_delete_mat_and_mfl THEN
      dmp$delete_mat (avt_entry.p_mat);
    IFEND;

  PROCEND p$detach_device_files;
?? OLDTITLE ??
?? NEWTITLE := '[xdcl] DMP$BRING_VOLUME_ONLINE', EJECT ??

  PROCEDURE [XDCL] dmp$bring_volume_online
    (    logical_unit_number: iot$logical_unit;
     VAR avt_index: dmt$active_volume_table_index;
     VAR status: ost$status);

    VAR
      avt_entry_found: boolean;

    status.normal := TRUE;
    dmp$search_avt_by_lun (logical_unit_number, avt_index, avt_entry_found);
    IF avt_entry_found THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$volume_already_online,
            'volume already online - DMMVONL', status);
      RETURN; {----->
    IFEND;

    dmp$get_unused_avt_entry (avt_index, avt_entry_found);
    IF NOT avt_entry_found THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$no_free_avt_entry,
            'unable to get free avt entry - DMMVONL', status);
      RETURN; {----->
    IFEND;

    dmp$lock_avt_entry (avt_index);

    dmv$active_volume_table.table_p^ [avt_index].logical_unit_number := logical_unit_number;

    dmp$unlock_avt_entry (avt_index);

  PROCEND dmp$bring_volume_online;
?? OLDTITLE ??
?? NEWTITLE := '[xdcl] DMP$TAKE_VOLUME_OFFLINE', EJECT ??

  PROCEDURE [XDCL, #GATE] dmp$take_volume_offline
    (    logical_unit_number: iot$logical_unit;
     VAR status: ost$status);

    VAR
      avt_entry_found: boolean,
      avt_index: dmt$active_volume_table_index;

    status.normal := TRUE;
    dmp$search_avt_by_lun (logical_unit_number, avt_index, avt_entry_found);
    IF NOT avt_entry_found THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_locate_avt_entry,
            'unable to find avt entry by lun - DMMVONL', status);
      RETURN; {----->
    IFEND;

    dmp$lock_avt_entry (avt_index);

    p$detach_device_files (c$dmm_delete_mat_and_mfl, dmv$active_volume_table.table_p^ [avt_index].
          mass_storage);
    dmv$active_volume_table.table_p^ [avt_index].entry_available := TRUE;
    dmv$active_volume_table.table_p^ [avt_index].logical_unit_number := 0;

    dmp$unlock_avt_entry (avt_index);

  PROCEND dmp$take_volume_offline;
?? OLDTITLE ??
?? NEWTITLE := '[xdcl, #gate] DMP$VOLUME_IS_ONLINE', EJECT ??

  PROCEDURE [XDCL, #GATE] dmp$volume_is_online
    (    logical_unit_number: iot$logical_unit;
     VAR volume_online: boolean);

    VAR
      avt_index: dmt$active_volume_table_index;

    dmp$search_avt_by_lun (logical_unit_number, avt_index, volume_online);

  PROCEND dmp$volume_is_online;
?? OLDTITLE ??
?? NEWTITLE := '[xdcl, #gate] dmp$volume_online', EJECT ??
*copy dmh$volume_online

  PROCEDURE [XDCL, #GATE] dmp$volume_online
    (    logical_unit_number: iot$logical_unit;
         p_physical_attributes: ^dmt$physical_device_attributes;
     VAR status: ost$status);

    VAR
      avt_entry_found: boolean,
      avt_index: dmt$active_volume_table_index,
      index: integer,
      max_label_size: dmt$max_volume_label_size,
      online: boolean,
      p_volume_label: ^dmt$ms_volume_label,
      p_volume_label_header: ^dmt$volume_label_header,
      volume_active: boolean;

    status.normal := TRUE;
    dmp$volume_is_online (logical_unit_number, online);
    IF online THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$volume_already_online,
            'Volume already online - dmp$volume_online', status);
      RETURN; {----->
    IFEND;

    max_label_size := dmc$default_volume_label_size;
    IF p_physical_attributes <> NIL THEN
      FOR index := LOWERBOUND (p_physical_attributes^) TO UPPERBOUND (p_physical_attributes^) DO
        CASE p_physical_attributes^ [index].keyword OF
        = dmc$pda_max_label_size =
          max_label_size := p_physical_attributes^ [index].max_label_size;
        ELSE
        CASEND;
      FOREND;
    IFEND;

    PUSH p_volume_label: [[REP max_label_size OF cell]];
    RESET p_volume_label;

    dmp$locate_volume_label (logical_unit_number, p_physical_attributes, p_volume_label^, status);
    IF NOT status.normal THEN
      CASE status.condition OF
      = ioe$unit_disabled =
        osp$set_status_condition (mme$volume_unavailable, status);
        osp$append_status_integer (osc$status_parameter_delimiter, logical_unit_number, 10, FALSE, status);
        RETURN; {----->

      = dme$no_ms_label_type =
        osp$set_status_abnormal (dmc$device_manager_ident, dme$unable_to_locate_label,
              'Unable to find label or path - dmp$volume_online', status);
        RETURN; {----->

      = ioe$unrecovered_disk_error =
        osp$append_status_parameter (osc$status_parameter_delimiter,
              'while reading the label in taking the volume online.', status);
        RETURN; {----->

      ELSE
        RETURN; {----->
      CASEND;
    IFEND;

    RESET p_volume_label;
    NEXT p_volume_label_header IN p_volume_label;

    dmp$search_avt_by_rvsn (p_volume_label_header^.recorded_vsn, avt_index, volume_active);
    IF volume_active THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$volume_already_active,
            'Volume already in AVT - dmp$volume_online', status);
      RETURN; {----->
    IFEND;

    dmp$get_unused_avt_entry (avt_index, avt_entry_found);
    IF NOT avt_entry_found THEN
      osp$set_status_abnormal (dmc$device_manager_ident, dme$no_free_avt_entry,
            'Unable to get free AVT entry - dmp$volume_online', status);
      RETURN; {----->
    IFEND;

    dmp$lock_avt_entry (avt_index);

    p$create_new_ms_avt_entry (p_volume_label, logical_unit_number, avt_index, status);

    dmp$unlock_avt_entry (avt_index);

  PROCEND dmp$volume_online;
?? OLDTITLE ??
MODEND dmm$volume_online;
