?? RIGHT := 110 ??
?? NEWTITLE := 'INSTALL_SOFTWARE Utility: RAP$STAGE_PRODUCTS Interface.' ??
MODULE ram$stage_products;

{ PURPOSE:
{   This module contains the interface and procedures that take a
{   subproduct or subproducts from the loading cycle to the staging cycle.
{
{ DESIGN:
{   The compiled module resides in RAF$LIBRARY.
{
{ NOTES:
{

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc rac$installation_cycles
*copyc rae$install_software_cc
*copyc rat$installation_control_record
?? POP ??
*copyc amp$change_file_attributes
*copyc amp$return
*copyc avp$ring_min
*copyc fsp$copy_file
*copyc osp$append_status_file
*copyc osp$generate_log_message
*copyc osp$set_status_abnormal
*copyc pfp$change
*copyc pfp$find_directory_array
*copyc pfp$find_direct_info_record
*copyc pfp$find_next_info_record
*copyc pfp$find_permit_array
*copyc pfp$get_item_info
*copyc pfp$permit
*copyc pfp$permit_catalog
*copyc pfp$purge
*copyc rap$clear_installation
*copyc rap$convert_path_to_str
*copyc rap$get_cycle_data
*copyc rap$get_majority_file_class
*copyc rap$record_step_status
*copyc rap$record_subproduct_status
*copyc rmp$request_mass_storage
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] rap$stage_products', EJECT ??

{ PURPOSE:
{   This interface controls the step to stage products.
{
{ DESIGN:
{   The stage products step moves the subproducts from the loading cycle to
{   the staging cycle.  The staging cycle is where the subproducts will
{   reside until they are activated for production.  While moving to the
{   staging cycle the files belonging to the subproducts are placed in
{   their proper storage class.  Once in the staging cycle, catalog and file
{   permits are created (where defined) and the file ring attributes are
{   set.
{
{   By placing the file on the correct storage class during this step
{   rather than the activation step, we prevent the possibility of causing
{   an error (attempting to move a file to a storage class that is
{   unavailable) during a deadstart to activate deferred files.
{
{   The inability to stage any file belonging to a subproduct will cause
{   the entire subproduct to have failed the staging step.  The installation
{   processing record for that subproduct is set as such and the subproduct
{   will be cleared from the system at the conclusion of this step.
{
{   The failure of one subproduct will not jeopardize the remaining
{   subproducts.  Each subproduct will be processed independently.
{
{ NOTES:
{   The SUBPRODUCTS_FAILED_PROCESSING boolean has been initialized outside of
{   this interface and should never be initialized here.
{

  PROCEDURE [XDCL] rap$stage_products
    (VAR installation_control_record {input} : rat$installation_control_record;
     VAR subproducts_failed_processing: boolean;
     VAR status: ost$status);


    VAR
      ignore_keyword: ost$name,
      ignore_status: ost$status,
      local_status: ost$status,
      majority_file_class: rmt$mass_storage_class,
      processing_record: rat$subp_processing_record,
      subproduct_index: rat$subproduct_count,
      task_status: ost$status;


    status.normal := TRUE;

    IF NOT (rac$stage_subproducts_step IN installation_control_record.processing_header_p^.step_set) THEN
      RETURN;
    IFEND;

    rap$record_step_status (rac$stage_subproducts_step, rac$step_started, installation_control_record,
          status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  /main/
    FOR subproduct_index := 1 TO UPPERBOUND (installation_control_record.subproduct_processing_records_p^)
          DO
      processing_record := installation_control_record.subproduct_processing_records_p^ [subproduct_index];

      IF (installation_control_record.job_identifier = processing_record.job_identifier) AND
            (rac$stage_files_task IN processing_record.task_set) AND
            (processing_record.task_status <> rac$task_failed) THEN

        rap$record_subproduct_status (rac$stage_files_task, rac$task_started, subproduct_index,
              installation_control_record, ignore_status);

        rap$get_majority_file_class (subproduct_index, installation_control_record, ignore_keyword,
              majority_file_class, status);
        IF NOT status.normal THEN
          EXIT /main/;
        IFEND;

        task_status.normal := TRUE;

        IF processing_record.subproduct_info_pointers.attributes_p^.catalog_permit.defined THEN
          create_catalog_permit (processing_record.subproduct_info_pointers.attributes_p^.catalog_permit,
                processing_record.installation_catalog_p^, installation_control_record.scratch_seq_p,
                task_status);
        IFEND;

        IF task_status.normal THEN
          stage_subproduct (processing_record.installation_catalog_p^, majority_file_class,
                installation_control_record.processing_header_p^.installation_defaults.ignore_storage_class,
                installation_control_record.processing_header_p^.installation_defaults.relax_ring_settings,
                processing_record.subproduct_info_pointers.element_list_p,
                processing_record.subproduct_info_pointers.subproduct_info_seq_p,
                installation_control_record.scratch_seq_p, task_status);
        IFEND;

        IF task_status.normal THEN
          rap$record_subproduct_status (rac$stage_files_task, rac$task_completed, subproduct_index,
                installation_control_record, ignore_status);
        ELSE
          osp$generate_log_message ($pmt$ascii_logset [pmc$job_log], task_status, ignore_status);
          rap$record_subproduct_status (rac$stage_files_task, rac$task_failed, subproduct_index,
                installation_control_record, ignore_status);
          subproducts_failed_processing := TRUE;
        IFEND;

      IFEND;
    FOREND /main/;

    rap$clear_installation (installation_control_record, ignore_status);

    rap$record_step_status (rac$stage_subproducts_step, rac$step_completed, installation_control_record,
          local_status);
    IF status.normal AND (NOT local_status.normal) THEN
      status := local_status;
    IFEND;

  PROCEND rap$stage_products;

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

{ PURPOSE:
{   This procedure creates a permit for a catalog belonging to the
{   subproduct.
{
{ DESIGN:
{   The permit to be created is defined by a permit record that was defined
{   in the SIF for the subproduct.
{
{   If a permit already exists for the catalog the permit is not defined.
{   The standard PF interfaces are used to retrieve permit data for the
{   catalog.
{
{ NOTES:
{   The scratch sequence is used by the PF interfaces as temporary storage
{   for catalog information.
{

  PROCEDURE create_catalog_permit
    (    permit: rat$permit;
         catalog_path: pft$path;
     VAR scratch_seq_p {input} : ^SEQ ( * );
     VAR status: ost$status);


    VAR
      cycles_p: pft$p_cycle_array,
      directory_p: pft$p_directory_array,
      group: pft$group,
      info_record_p: pft$p_info_record,
      permits_p: pft$p_permit_array;


    status.normal := TRUE;

    group.group_type := pfc$member;
    group.member_description.family := osc$null_name;
    group.member_description.account := osc$null_name;
    group.member_description.project := osc$null_name;
    group.member_description.user := osc$null_name;

    RESET scratch_seq_p;

    pfp$get_item_info (catalog_path, group, $pft$catalog_info_selections
          [pfc$catalog_directory, pfc$catalog_description, pfc$catalog_permits], $pft$file_info_selections [],
          scratch_seq_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    RESET scratch_seq_p;

    pfp$find_next_info_record (scratch_seq_p, info_record_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pfp$find_directory_array (info_record_p, directory_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pfp$find_direct_info_record (^info_record_p^.body, directory_p^ [1].info_offset, info_record_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    pfp$find_permit_array (info_record_p, permits_p, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { When no permits are found the catalog permit will be created.

    IF permits_p = NIL THEN

      group.group_type := pfc$public;

      pfp$permit_catalog (catalog_path, group, permit.permit_selections, permit.share_requirements,
            permit.application_info, status);
    IFEND;

  PROCEND create_catalog_permit;

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

{ PURPOSE:
{   This procedure stages a file for a subproduct.
{
{ DESIGN:
{   The file is moved to the staging cycle.  If the file is not in the
{   proper storage class it must be moved to the correct one.  This is
{   accomplished by a request for mass storage followed by a copy file.
{   The majority of the files were loaded to the proper storage class, so
{   this situation will occur in less that half of the files (determined by
{   size).  If the file is on the correct storage class a cycle change is
{   performed.
{
{   Once in the staging cycle the ring attributes and permits are set.
{
{   The RELAX_RING_SETTINGS flag is an install_software checkout hook.  The normal
{   values are compressed up to the user's minimum ring when the ring
{   values are below the user's minimum ring.
{
{   When a public permit has been declared for the file and the file
{   contains only 1 cycle, the permit is defined.  Otherwise, it is assumed
{   that the file has previously been installed and all desired permits
{   have already been created (and any unwanted public permit has been
{   deleted).  This protects the permits as the site has defined them.
{
{ NOTES:
{   The ring attributes record in the element record is converted
{   to a format that is compatible for PFP$CHANGE_FILE_ATTRIBUTES.
{
{   The scratch sequence is used by GET_CYCLE_DATA as temporary storage
{   for file information.
{

  PROCEDURE stage_file
    (    element_p: ^rat$element;
         file_path: pft$path;
         majority_file_class: rmt$mass_storage_class;
         ignore_storage_class: boolean;
         relax_ring_settings: boolean;
     VAR scratch_seq_p {input} : ^SEQ ( * );
     VAR status: ost$status);


    VAR
      cycles_p: pft$p_cycle_array,
      file_attributes: array [1 .. 1] of amt$file_item,
      file_loading_path: rat$path,
      file_staging_path: rat$path,
      file_fs: rat$path,
      group: pft$group,
      ignore_status: ost$status,
      loading_cycle: pft$cycle_selector,
      local_status: ost$status,
      password: pft$password,
      public_permit: pft$permit_array_entry,
      public_permit_found: boolean,
      ring_attributes: amt$ring_attributes,
      staging_cycle: array [1 .. 1] of pft$change_descriptor,
      user_minimum_ring: ost$ring;


    status.normal := TRUE;
    loading_cycle.cycle_option := pfc$specific_cycle;
    loading_cycle.cycle_number := rac$loading_cycle;
    password := '';

    rap$convert_path_to_str (file_path, file_fs);
    STRINGREP (file_staging_path.path, file_staging_path.size, file_fs.path (1, file_fs.size), '.',
          rac$staging_cycle_str);

    { move the file from the loading cycle to the staging cycle with the correct storage class.

    IF element_p^.storage_class = majority_file_class THEN
      staging_cycle [1].change_type := pfc$cycle_number_change;
      staging_cycle [1].cycle_number := rac$staging_cycle;

      pfp$change (file_path, loading_cycle, password, staging_cycle, status);

    ELSE
      STRINGREP (file_loading_path.path, file_loading_path.size, file_fs.path (1, file_fs.size), '.',
            rac$loading_cycle_str);

      rmp$request_mass_storage (file_staging_path.path (1, file_staging_path.size),
            rmc$unspecified_allocation_size, rmc$unspecified_file_size, element_p^.storage_class,
            rmc$unspecified_vsn, TRUE, status);
      IF (NOT status.normal) AND (NOT ignore_storage_class) THEN
        RETURN;
      IFEND;

      fsp$copy_file (file_loading_path.path (1, file_loading_path.size),
            file_staging_path.path (1, file_staging_path.size), NIL, NIL, NIL, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      { The interface rmp$request_mass_storage attaches the file and therefore must be returned.

      amp$return (file_staging_path.path (1, file_staging_path.size), status);
      IF (NOT status.normal) AND (NOT ignore_storage_class) THEN
        RETURN;
      IFEND;

      pfp$purge (file_path, loading_cycle, password, status);

    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Set rings.

    ring_attributes.r1 := element_p^.ring_attributes.r1;
    ring_attributes.r2 := element_p^.ring_attributes.r2;
    ring_attributes.r3 := element_p^.ring_attributes.r3;
    user_minimum_ring := avp$ring_min ();

    IF NOT relax_ring_settings THEN
      IF (ring_attributes.r1 < user_minimum_ring) OR (ring_attributes.r2 < user_minimum_ring) OR
            (ring_attributes.r3 < user_minimum_ring) THEN
        osp$set_status_abnormal ('RA', rae$insufficient_min_ring, '', status);
        osp$append_status_file (osc$status_parameter_delimiter, file_fs.path (1, file_fs.size), status);
        RETURN;
      IFEND;
    ELSE {compress rings to minimum of user}
      IF ring_attributes.r1 < user_minimum_ring THEN
        ring_attributes.r1 := user_minimum_ring;
      IFEND;
      IF ring_attributes.r2 < user_minimum_ring THEN
        ring_attributes.r2 := user_minimum_ring;
      IFEND;
      IF ring_attributes.r3 < user_minimum_ring THEN
        ring_attributes.r3 := user_minimum_ring;
      IFEND;
    IFEND;

    file_attributes [1].key := amc$ring_attributes;
    file_attributes [1].ring_attributes := ring_attributes;

    amp$change_file_attributes (file_staging_path.path (1, file_staging_path.size), ^file_attributes, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    { Create a public file permit if required.

    IF element_p^.permit.defined THEN

      rap$get_cycle_data (file_path, scratch_seq_p, cycles_p, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

      { When there is only one cycle for the file the permit is defined.
      { When more than one cycle exists it is assumed the permit has previously been defined.

      IF UPPERBOUND (cycles_p^) = 1 THEN

        group.group_type := pfc$public;

        pfp$permit (file_path, group, element_p^.permit.permit_selections,
              element_p^.permit.share_requirements, element_p^.permit.application_info, status);

      IFEND;
    IFEND;

  PROCEND stage_file;

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

{ PURPOSE:
{   This procedure stages the files belonging to a subproduct.
{
{ DESIGN:
{   Determining what files belong to the subproduct is accomplished by
{   traversing the element list for the subproduct.  The traverse is
{   performed using recursion and each call to STAGE_SUBPRODUCT moves
{   processing down to the next catalog level.
{
{   Only active elements are processed.  An inactive catalog element means
{   that all elements associated with that catalog are also inactive.
{
{   Each file is staged and catalog permits are created as defined.
{
{ NOTES:
{   The scratch sequence is used by a subsequent procedure as temporary
{   storage for file cycle information.  The contents are not preserved.
{

  PROCEDURE stage_subproduct
    (    element_path: pft$path;
         majority_file_class: rmt$mass_storage_class;
         ignore_storage_class: boolean;
         relax_ring_settings: boolean;
     VAR element_p {input} : ^rat$element;
     VAR subproduct_info_seq_p {input} : ^rat$subproduct_info_sequence;
     VAR scratch_seq_p {input} : ^SEQ ( * );
     VAR status: ost$status);


    VAR
      current_element_path_p: ^pft$path,
      first_element_down_p: ^rat$element,
      i: integer;


    status.normal := TRUE;

    { The element_path parameter is the path for the current catalog.  Create
    { a PF format path array that is 1 larger than the size of the element
    { path.  This array will be used to construct the PF paths for the files
    { and subcatalogs that reside in the current catalog.

    PUSH current_element_path_p: [1 .. UPPERBOUND (element_path) + 1];
    FOR i := 1 TO UPPERBOUND (element_path) DO
      current_element_path_p^ [i] := element_path [i];
    FOREND;

    { Process the files and subcatalogs at the current catalog level.

    WHILE element_p <> NIL DO

      current_element_path_p^ [UPPERBOUND (current_element_path_p^)] := element_p^.name;

      IF element_p^.active_element THEN

        IF element_p^.element_type = rac$file THEN

          stage_file (element_p, current_element_path_p^, majority_file_class, ignore_storage_class,
                relax_ring_settings, scratch_seq_p, status);

        ELSEIF (element_p^.element_type = rac$catalog) THEN

          IF element_p^.permit.defined THEN
            create_catalog_permit (element_p^.permit, current_element_path_p^, scratch_seq_p, status);
            IF NOT status.normal THEN
              RETURN;
            IFEND;
          IFEND;

          IF element_p^.element_count <> 0 THEN
            first_element_down_p := #PTR (element_p^.first_element_down_p, subproduct_info_seq_p^);

            stage_subproduct (current_element_path_p^, majority_file_class, ignore_storage_class,
                  relax_ring_settings, first_element_down_p, subproduct_info_seq_p, scratch_seq_p, status);
          IFEND;

        IFEND;
        IF NOT status.normal THEN
          RETURN;
        IFEND;
      IFEND;

      element_p := #PTR (element_p^.next_element_across_p, subproduct_info_seq_p^);
    WHILEND;

  PROCEND stage_subproduct;

MODEND ram$stage_products;
