?? RIGHT := 110 ??
?? NEWTITLE := ' Configuration Management Signal Handler' ??
MODULE cmm$signal_handler;
?? RIGHT := 110 ??

{
{  PURPOSE:
{    This module contains the code to interpet and take the approriate action
{    when a configuration management signal is encountered.


?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc oss$task_shared
*copyc cmc$action_messages
*copyc cme$logical_configuration_mgr
*copyc cmt$signal_contents
*copyc iot$disk_statistics
*copyc ost$global_task_id
*copyc pmt$signal
?? POP ??
*copyc clp$convert_integer_to_string
*copyc clp$trimmed_string_size
*copyc cmp$activate_signal_handler
*copyc cmp$convert_channel_number
*copyc cmp$convert_iou_number
*copyc cmp$determine_tape_element
*copyc cmp$free_deadstart_signals
*copyc cmp$get_element_entry_via_adr
*copyc cmp$get_element_entry_via_lun
*copyc cmp$get_element_entry_via_name
*copyc cmp$get_element_information
*copyc cmp$get_element_r3
*copyc cmp$process_state_change
*copyc cmp$queue_deadstart_signal
*copyc cmp$return_logical_pp_number
*copyc cmp$search_active_volume_table
*copyc ofp$clear_operator_message
*copyc ofp$format_operator_message
*copyc ofp$receive_operator_response
*copyc ofp$send_formatted_operator_msg
*copyc osp$get_parameter_prompt
*copyc osp$system_error
*copyc pmp$execute
*copyc pmp$get_mainframe_id
*copyc pmp$log
*copyc pmp$long_term_wait
*copyc sfp$emit_statistic
*copyc cmv$controller_address
*copyc cmv$data_channel_address
*copyc cmv$deadstart_signals
*copyc cmv$hydra_mass_storage_address
*copyc cmv$logical_pp_table_p
*copyc cmv$mass_storage_address
*copyc cmv$peripheral_element_table
*copyc cmv$signal_handler_active
*copyc osv$task_shared_heap
*copyc tmv$system_job_monitor_gtid
*copyc cml$connection_disabled
*copyc cml$element_disabled
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  TYPE
    cmt$signal_handler_parameters = record
      originator: ost$global_task_id,
      signal: pmt$signal,
    recend;

  VAR
    default_program_attributes: [STATIC, READ] pmt$program_attributes := [[pmc$starting_proc_specified],
          'CMP$PROCESS_SIGNAL', 0, 0, 1, osc$null_name, [pmc$no_load_map], pmc$error_load_errors,
          pmc$initialize_to_zero, osc$max_segment_length, osc$null_name, osc$null_name, osc$null_name, FALSE];

  VAR
    cmv$handler_task_status_p: [oss$task_shared] ^pmt$task_status := NIL;

?? TITLE := '  build_connection_label', EJECT ??
  {
  { PURPOSE:
  {   This procedure extracts information from the upline and
  {   downline peripheral_element_entry to build a string describing
  {   the connection between the two elements.  The connection label
  {   consists of the element_names of the two elements separated by
  {   a period (i.e. ".").  When the upline element is a channel the
  {   iou name is included as part of the channel element name.

  PROCEDURE build_connection_label
    (    upline_element_entry_p: ^cmt$peripheral_element_entry;
         downline_element_entry_p: ^cmt$peripheral_element_entry;
         iou_name: ost$name;
     VAR connection_label: ost$string);

    VAR
      i: integer,
      str_size: integer;

    i := 1;
    IF upline_element_entry_p^.physical_descriptor.element_type = cmc$data_channel_element THEN
      connection_label.value (i, 4) := iou_name (1, 4);
      i := i + 4;
      connection_label.value (i) := '/';
      i := i + 1;
    IFEND;

    str_size := clp$trimmed_string_size (upline_element_entry_p^.element_name);
    connection_label.value (i, str_size) := upline_element_entry_p^.element_name (1, str_size);
    i := i + str_size;

    connection_label.value (i) := '.';
    i := i + 1;

    str_size := clp$trimmed_string_size (downline_element_entry_p^.element_name);
    connection_label.value (i, str_size) := downline_element_entry_p^.element_name (1, str_size);
    i := i + str_size;

    connection_label.size := i - 1;

  PROCEND build_connection_label;

?? TITLE := '  build_path', EJECT ??
  {
  { PURPOSE:
  {   This procedure builds a string that represents the path
  {   of the specified physical_address. The path consists of the element names
  {   of all elements in the path seperated by periods (i.e. "."). The iou/channel
  {   element always begins the path and controller and/or mass_storage elements
  {   will be appended if the are contained in the physical_address.
  {

  PROCEDURE build_path
    (    physical_address: cmt$physical_address;
     VAR path: ost$string);

    VAR
      address: cmt$physical_address,
      element_entry_p: ^cmt$peripheral_element_entry,
      i: integer,
      iou_name: ost$name,
      local_status: ost$status,
      str_size: integer;

    i := 1;

    address := physical_address;
    address.address_specifier := cmv$data_channel_address;
    address.channel_address := 0;
    address.unit_address := 0;

    {
    { Initialize path with correct IOU name.
    {
    CASE address.iou OF
    = 0 =
      path.value (i, 5) := 'IOU0/';
      i := i + 5;
    = 1 =
      path.value (i, 5) := 'IOU1/';
      i := i + 5;
    ELSE
    CASEND;
    {
    { Get channel element name and append to path.
    {
    cmp$get_element_entry_via_adr (address, element_entry_p);
    IF element_entry_p = NIL THEN
      local_status.normal := FALSE;
      osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
    IFEND;

    str_size := clp$trimmed_string_size (element_entry_p^.element_name);
    path.value (i, str_size) := element_entry_p^.element_name (1, str_size);
    i := i + str_size;
    {
    { If the address contains a channel address, get element name and append to path.
    {
    IF cmc$channel_address IN physical_address.address_specifier THEN
      address.address_specifier := cmv$controller_address;
      address.channel_address := physical_address.channel_address;

      cmp$get_element_entry_via_adr (address, element_entry_p);
      IF element_entry_p = NIL THEN
        local_status.normal := FALSE;
        osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
      IFEND;

      path.value (i) := '.';
      i := i + 1;

      str_size := clp$trimmed_string_size (element_entry_p^.element_name);
      path.value (i, str_size) := element_entry_p^.element_name (1, str_size);
      i := i + str_size;

    IFEND;
    {
    { If the address contains a unit address, get element name and append to path.
    {
    IF cmc$unit_address IN physical_address.address_specifier THEN
      address.address_specifier := cmv$mass_storage_address;
      address.unit_address := physical_address.unit_address;

      cmp$get_element_entry_via_adr (address, element_entry_p);
      IF element_entry_p = NIL THEN
        local_status.normal := FALSE;
        osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
      IFEND;

      path.value (i) := '.';
      i := i + 1;

      str_size := clp$trimmed_string_size (element_entry_p^.element_name);
      path.value (i, str_size) := element_entry_p^.element_name (1, str_size);
      i := i + str_size;
    IFEND;

    path.size := i - 1;

  PROCEND build_path;

?? TITLE := '  cmp$process_deadstart_signals', EJECT ??
  {
  { PURPOSE:
  {   This procedure will execute the CM signal handler for every signal
  {   queued during deadstart.  The normal signal handling procedure is
  {   activated and the queued signals are freed.
  {

  PROCEDURE [XDCL, #GATE] cmp$process_deadstart_signals;

    VAR
      p_next_deadstart_signal: ^cmt$deadstart_signal,
      p_deadstart_signal: ^cmt$deadstart_signal;

    cmp$activate_signal_handler;

    IF cmv$deadstart_signals = NIL THEN
      RETURN; {----->
    IFEND;

    p_deadstart_signal := cmv$deadstart_signals;
    WHILE p_deadstart_signal <> NIL DO
      p_next_deadstart_signal := p_deadstart_signal^.next_signal;
      cmp$signal_handler (p_deadstart_signal^.originator, p_deadstart_signal^.signal);
      p_deadstart_signal := p_next_deadstart_signal;
    WHILEND;

    cmp$free_deadstart_signals;

  PROCEND cmp$process_deadstart_signals;

?? TITLE := '  cmp$process_signal', EJECT ??
{
{ PURPOSE:
{   This procedure is called by the configuration management signal handler
{   via a call to pmp$execute. An asynchronous task is started in the system
{   job to perform the action required to process the signal.
{

  PROCEDURE [XDCL, #GATE] cmp$process_signal
    (    program_parameters: pmt$program_parameters;
     VAR status: ost$status);

    VAR
      p_parameters: ^SEQ ( * ),
      p_signal_handler_parameters: ^cmt$signal_handler_parameters,
      signal_contents: cmt$signal_contents;

    p_parameters := ^program_parameters;
    RESET p_parameters;
    NEXT p_signal_handler_parameters IN p_parameters;

    #UNCHECKED_CONVERSION (p_signal_handler_parameters^.signal.contents, signal_contents);

    CASE signal_contents.signal_type OF
    = cmc$reconfiguration_signal =
      process_reconfig_signal (signal_contents, status);
    = cmc$das_head_shift_signal =
      process_das_head_shift_signal (signal_contents, status);
    = cmc$ssd_battery_alert_signal =
      process_ssd_battery_signal (signal_contents, status);
    = cmc$down_element_signal =
      process_down_element_signal (signal_contents, status);
    = cmc$disable_element_signal =
      process_disable_signal (signal_contents, status);
    = cmc$controller_overtemp_signal =
      process_overtemp_signal (signal_contents, status);
    = cmc$parity_disabled_signal =
      process_parity_disabled_signal (signal_contents, status);
    ELSE
    CASEND;

  PROCEND cmp$process_signal;

?? TITLE := '  cmp$signal_handler', EJECT ??
{
{ PURPOSE:
{   This procedure is the configuration management signal handler.
{   In stuations where the system is ready to support tasks it will
{   spin off an asynchronous task to service the signal.  This is to
{   prevent the system to wait for the signal to be processed.
{   If a configurtion management signal is encountered before deadstart is
{   complete the signal handler will place the signal on a linked list
{   and process the signals when the system can support asynchronous tasks
{   and operator messages.
{

  PROCEDURE [XDCL, #GATE] cmp$signal_handler
    (    originator: ost$global_task_id;
         signal: pmt$signal);

    VAR
      local_status: ost$status,
      p_parameters: ^SEQ ( * ),
      p_program_attributes: ^pmt$program_attributes,
      p_program_description: ^pmt$program_description,
      p_signal_handler_parameters: ^cmt$signal_handler_parameters,
      signal_contents: cmt$signal_contents,
      task_id: pmt$task_id;

    IF cmv$signal_handler_active THEN
      {
      { Initiate asynchronous task to execute the procedure cmp$process_signal.
      {
      IF cmv$handler_task_status_p = NIL THEN
        ALLOCATE cmv$handler_task_status_p IN osv$task_shared_heap^;
      IFEND;

      PUSH p_program_description: [[REP #SIZE (pmt$program_attributes) OF cell]];
      RESET p_program_description;
      NEXT p_program_attributes IN p_program_description;
      p_program_attributes^ := default_program_attributes;

      PUSH p_parameters: [[REP #SIZE (cmt$signal_handler_parameters) OF cell]];
      RESET p_parameters;
      NEXT p_signal_handler_parameters IN p_parameters;
      p_signal_handler_parameters^.originator := originator;
      p_signal_handler_parameters^.signal := signal;

      pmp$execute (p_program_description^, p_parameters^, osc$nowait, task_id, cmv$handler_task_status_p^,
            local_status);
    ELSE
      {
      { Execute the process signal interface synchronously.
      {
      cmp$queue_deadstart_signal (originator, signal);
    IFEND;
  PROCEND cmp$signal_handler;

?? TITLE := 'display_disable_menu', EJECT ??
{
{ PURPOSE:
{   This procedure will build the parameter list for the disable_element
{   message module and pass the parameter list to the ]mit_operator_message
{   procedure.
{

  PROCEDURE display_disable_menu
    (    element_definition: cmt$element_definition;
         element_entry_p: ^cmt$peripheral_element_entry;
     VAR status: ost$status);

    VAR
      not_found: boolean,
      parameters: ^ost$message_parameters,
      product_id_str: ost$name,
      recorded_vsn: rmt$recorded_vsn,
      search_key: dmt$avt_search_key,
      size: ost$string_size,
      temp_str: ost$string;

    temp_str.value := ' ';

    CASE element_definition.element_type OF
    = cmc$data_channel_element =
      CASE element_entry_p^.physical_descriptor.channel_path.iou OF
      = 0 =
        temp_str.value (1, 5) := 'IOU0/';
      = 1 =
        temp_str.value (1, 5) := 'IOU1/';
      ELSE
      CASEND;

      temp_str.value (6, * ) := element_entry_p^.element_name;
      set_string_length (temp_str);

      PUSH parameters: [1 .. 1];
      parameters^ [1] := ^temp_str.value (1, temp_str.size);

      emit_operator_message (cmc$action_messages, 'CHANNEL_DISABLED               ', parameters, status);

    = cmc$controller_element =
      size := STRLENGTH (element_entry_p^.product_id.product_number);
      temp_str.value (1, size) := element_entry_p^.product_id.product_number;
      temp_str.size := size;

      size := STRLENGTH (element_entry_p^.product_id.underscore);
      temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.underscore;
      temp_str.size := temp_str.size + size;

      size := STRLENGTH (element_entry_p^.product_id.model_number);
      temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.model_number;
      temp_str.size := temp_str.size + size;

      set_string_length (temp_str);

      PUSH parameters: [1 .. 3];
      parameters^ [1] := ^element_definition.element_name;
      parameters^ [2] := ^temp_str.value (1, temp_str.size);
      parameters^ [3] := ^element_definition.serial_number;

      emit_operator_message (cmc$action_messages, 'CONTROLLER_DISABLED            ', parameters, status);

    = cmc$storage_device_element =
      search_key.value := dmc$search_avt_by_lun;
      search_key.logical_unit_number := element_entry_p^.logical_unit_number;
      cmp$search_active_volume_table (search_key, recorded_vsn, not_found);
      IF not_found THEN
        recorded_vsn := '      ';
      IFEND;

      size := STRLENGTH (element_entry_p^.product_id.product_number);
      temp_str.value (1, size) := element_entry_p^.product_id.product_number;
      temp_str.size := size;

      size := STRLENGTH (element_entry_p^.product_id.underscore);
      temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.underscore;
      temp_str.size := temp_str.size + size;

      size := STRLENGTH (element_entry_p^.product_id.model_number);
      temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.model_number;
      temp_str.size := temp_str.size + size;

      set_string_length (temp_str);

      PUSH parameters: [1 .. 4];
      parameters^ [1] := ^element_definition.element_name;
      parameters^ [2] := ^temp_str.value (1, temp_str.size);
      parameters^ [3] := ^element_definition.serial_number;
      parameters^ [4] := ^recorded_vsn;

      emit_operator_message (cmc$action_messages, 'UNIT_DISABLED                  ', parameters, status);
    ELSE
    CASEND;

  PROCEND display_disable_menu;

?? TITLE := 'emit_disable_connection_stat', EJECT ??

  {
  { PURPOSE:
  {   This procedure will construct the descriptive data portion for  a
  {   cml$connection_disabled statistic and send the statistic to the
  {   engineering log.
  {

  PROCEDURE emit_disable_connection_stat
    (    upline_element_entry_p: ^cmt$peripheral_element_entry;
         downline_element_entry_p: ^cmt$peripheral_element_entry;
         connection_label: ost$string;
         failing_element_name: cmt$element_name;
     VAR status: ost$status);

    VAR
      channel_name: cmt$element_name,
      channel_ordinal: cmt$channel_ordinal,
      index: 0 .. 256,
      iou_name: cmt$element_name,
      logical_pp: iot$pp_number,
      mainframe_id: pmt$mainframe_id,
      pp_string: string (10),
      size: integer,
      str: ost$string,
      temp_str: ost$string,
      unit_address: cmt$physical_address,
      valid_channel_name: boolean;

    status.normal := TRUE;
    index := 1;

    temp_str := connection_label;
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index) := ',';
    index := index + 1;

    temp_str.value := ' ';
    temp_str.value := failing_element_name;
    set_string_length (temp_str);
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index) := ',';
    index := index + 1;

    pmp$get_mainframe_id (mainframe_id, status);
    temp_str.value := mainframe_id;
    set_string_length (temp_str);
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index) := '.';
    index := index + 1;

    unit_address := downline_element_entry_p^.physical_descriptor.unit_path^ [1];
    cmp$convert_iou_number (unit_address.iou, iou_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    cmp$convert_channel_number (unit_address.channel.number, unit_address.channel.concurrent,
          unit_address.channel.port, channel_ordinal, channel_name, valid_channel_name);
    cmp$return_logical_pp_number (channel_name, iou_name, logical_pp, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    temp_str.value := iou_name;
    set_string_length (temp_str);
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index) := '.';
    index := index + 1;

    STRINGREP (pp_string, size, logical_pp);
    IF unit_address.channel.concurrent THEN
      temp_str.value := 'CPP ';
      temp_str.value (4, * ) := pp_string (2, * );
    ELSE
      temp_str.value := 'PP ';
      temp_str.value (3, * ) := pp_string (2, * );
    IFEND;
    set_string_length (temp_str);
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index) := '.';
    index := index + 1;

    temp_str.value := channel_name;
    set_string_length (temp_str);
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index) := '.';
    index := index + 1;

    IF unit_address.address_specifier <> cmv$hydra_mass_storage_address THEN
      temp_str.value := upline_element_entry_p^.element_name;
      set_string_length (temp_str);
      str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
      index := index + temp_str.size;

      str.value (index) := '.';
      index := index + 1;
    IFEND;

    temp_str.value := downline_element_entry_p^.element_name;
    set_string_length (temp_str);
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index, 20) := '*CONNECTION DISABLED';
    index := index + 20;

    str.size := index - 1;

    {pmp$log ('Emit connection disabled statistic.', status);
    {pmp$log (str.value (1, str.size), status);

    sfp$emit_statistic (cml$connection_disabled, str.value (1, str.size), NIL, status);

  PROCEND emit_disable_connection_stat;

?? TITLE := 'emit_disable_element_statistic', EJECT ??

  {
  { PURPOSE:
  {   This procedure will construct the descriptive data portion for a
  {   cml$element_disabled statistic and send the statistic to the
  {   engineering log.
  {

  PROCEDURE emit_disable_element_statistic
    (    element_entry_p: ^cmt$peripheral_element_entry;
     VAR status: ost$status);

    VAR
      temp_str: ost$string,
      index: 0 .. 256,
      mainframe_id: pmt$mainframe_id,
      size: 0 .. 256,
      str: ost$string;

    status.normal := TRUE;
    index := 1;

    pmp$get_mainframe_id (mainframe_id, status);
    str.value := mainframe_id;
    temp_str.value := mainframe_id;

    set_string_length (temp_str);
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index) := '.';
    index := index + 1;

    IF element_entry_p^.physical_descriptor.element_type = cmc$data_channel_element THEN
      CASE element_entry_p^.physical_descriptor.channel_path.iou OF
      = 0 =
        temp_str.value (1, 5) := 'IOU0/';
      = 1 =
        temp_str.value (1, 5) := 'IOU1/';
      ELSE
      CASEND;
      temp_str.value (6, * ) := element_entry_p^.element_name;
    ELSE
      temp_str.value := element_entry_p^.element_name;
    IFEND;
    set_string_length (temp_str);
    str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
    index := index + temp_str.size;

    str.value (index) := '*';
    index := index + 1;

    IF element_entry_p^.physical_descriptor.element_type <> cmc$data_channel_element THEN
      temp_str.value := ' ';
      size := STRLENGTH (element_entry_p^.product_id.product_number);
      temp_str.value (1, size) := element_entry_p^.product_id.product_number;
      temp_str.size := size;

      size := STRLENGTH (element_entry_p^.product_id.underscore);
      temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.underscore;
      temp_str.size := temp_str.size + size;

      size := STRLENGTH (element_entry_p^.product_id.model_number);
      temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.model_number;
      temp_str.size := temp_str.size + size;

      set_string_length (temp_str);

      str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
      index := index + temp_str.size;
    IFEND;

    str.value (index) := '*';
    index := index + 1;

    IF element_entry_p^.physical_descriptor.element_type <> cmc$data_channel_element THEN
      temp_str.value := element_entry_p^.serial_number;
      set_string_length (temp_str);
      str.value (index, temp_str.size) := temp_str.value (1, temp_str.size);
      index := index + temp_str.size;
    IFEND;

    str.value (index, 17) := '*ELEMENT DISABLED';
    index := index + 17;

    str.size := index - 1;

    {pmp$log ('Emit element disabled statistic.', status);
    {pmp$log (str.value (1, str.size), status);

    sfp$emit_statistic (cml$element_disabled, str.value (1, str.size), NIL, status);

  PROCEND emit_disable_element_statistic;

?? TITLE := 'emit_operator_message', EJECT ??

  {
  { PURPOSE:
  {   This procedure will format and send an operator message defined
  {   in the cmm$action_messages help module.  If the acknowledgment_allowed
  {   parameter is set to TRUE it will wait in ofp$receive_operator_response
  {   until the operator_action_message is acknowledged via the
  {   ACKNOWLEDGE_OPERATOR_MESSAGE command. If the interface is called prior
  {   to system commit the formatted message will be written directly to the
  {   system console.
  {

  PROCEDURE emit_operator_message
    (    seed_name: pmt$program_name;
         message_name: clt$parameter_name;
         message_parameters: ^ost$message_parameters;
     VAR status: ost$status);

    VAR
      formatted_message: oft$formatted_operator_message,
      line_count: oft$number_of_displayable_lines,
      message: ost$status_message,
      operator_response: ost$string;

    status.normal := TRUE;

    osp$get_parameter_prompt (seed_name, message_name, message_parameters, {max_message_line} 80, message,
          status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    ofp$format_operator_message (message, 1, formatted_message, line_count);
    IF line_count = 0 THEN
      RETURN; {----->
    IFEND;

    ofp$send_formatted_operator_msg (formatted_message, ofc$system_operator, TRUE, status);
    WHILE NOT status.normal AND (status.condition = ofe$max_job_operator_messages) DO
      pmp$long_term_wait (1000, 1000);
      ofp$send_formatted_operator_msg (formatted_message, ofc$system_operator, TRUE, status);
    WHILEND;

    IF status.normal THEN
      ofp$receive_operator_response (ofc$system_operator, osc$wait, operator_response, status);
    IFEND;

  PROCEND emit_operator_message;
?? TITLE := 'find_active_path', EJECT ??
  {
  { PURPOSE:
  {   This procedure will search the unit descriptors in the logical_pp_table
  {   to locate an active entry for logical_unit specified.  If an active entry is
  {   found the physical_address of the active path is returned.
  {

  PROCEDURE find_active_path
    (    logical_unit_number: iot$logical_unit;
     VAR active_path: cmt$physical_address;
     VAR found: boolean);

    VAR
      logical_unit: iot$logical_unit,
      pp: iot$pp_number,
      ppit_p: ^iot$pp_interface_table,
      unit_desc: iot$unit_descriptor_entry;

    found := FALSE;

  /search_logical_pp_table/
    FOR pp := LOWERBOUND (cmv$logical_pp_table_p^) TO UPPERBOUND (cmv$logical_pp_table_p^) DO
      IF NOT cmv$logical_pp_table_p^ [pp].flags.configured THEN
        CYCLE /search_logical_pp_table/; {----->
      IFEND;

      ppit_p := cmv$logical_pp_table_p^ [pp].pp_info.pp_interface_table_p;

      IF (logical_unit_number < LOWERBOUND (ppit_p^.unit_descriptors)) OR
            (logical_unit_number > UPPERBOUND (ppit_p^.unit_descriptors)) THEN
        CYCLE /search_logical_pp_table/; {----->
      IFEND;

      unit_desc := ppit_p^.unit_descriptors [logical_unit_number];
      IF unit_desc.unit_interface_table = NIL THEN
        CYCLE /search_logical_pp_table/; {----->
      IFEND;

      IF unit_desc.unit_interface_table_rma = 0 THEN
        CYCLE /search_logical_pp_table/; {----->
      IFEND;

      IF unit_desc.unit_interface_table^.unit_status.disabled THEN
        CYCLE /search_logical_pp_table/; {----->
      IFEND;

      IF unit_desc.logical_unit = logical_unit_number THEN
        found := TRUE;
        active_path.address_specifier := cmv$mass_storage_address;
        active_path.iou := cmv$logical_pp_table_p^ [pp].pp_info.channel.iou_number;
        active_path.channel.number := unit_desc.physical_path.channel_number;
        active_path.channel.concurrent := cmv$logical_pp_table_p^ [pp].pp_info.channel_interlock_p^.
              channel_characteristics [active_path.channel.number].concurrent_channel;
        active_path.channel.port := cmc$unspecified_port;
        IF active_path.channel.concurrent THEN
          IF (cmv$logical_pp_table_p^ [pp].controller_info.controller_type = cmc$ms5831_x) OR
                (cmv$logical_pp_table_p^ [pp].controller_info.controller_type = cmc$msntdc_1) OR
                (cmv$logical_pp_table_p^ [pp].controller_info.controller_type = cmc$msntdc_2) OR
                (cmv$logical_pp_table_p^ [pp].controller_info.controller_type = cmc$mshydra_ct) OR
                (cmv$logical_pp_table_p^ [pp].controller_info.controller_type = cmc$mscm3_ct) THEN
            CASE unit_desc.physical_path.port OF
            = 0 =
              active_path.channel.port := cmc$port_a;
            = 1 =
              active_path.channel.port := cmc$port_b;
            ELSE
            CASEND;
          IFEND;
        IFEND;
        active_path.channel_address := unit_desc.physical_path.controller_number;
        active_path.unit_address := unit_desc.physical_path.physical_unit_number;
        RETURN; {----->
      IFEND;
    FOREND /search_logical_pp_table/;

  PROCEND find_active_path;

?? TITLE := 'find_enabled_paths', EJECT ??
{
{ PURPOSE:
{   This procedure will determine which units have been reconfigured
{   away from and return a list of the active path(s) that now service those
{   units.  The physical_address input to this procedure can contain either a
{   channel address or a controller address.  All units serviced by the
{   specified element are checked to determine the current active path.  For a
{   channel, all downline connections from the channel are disabled and therefore
{   all disabled downline controllers are searched for units that have been
{   assigned a new active path. For a controller only the controller specified
{   will be searched. If the disabled path is found prior to the active path in
{   the list of unit addresses in the unit's peripheral_element_table_entry we
{   will assume this element has just been reconfigured.  The controller portion
{   of the new active path for each unit is saved and returned to the caller, with
{   duplicate entries removed.
{

  PROCEDURE find_enabled_paths
    (    downline_address: cmt$physical_address;
         channel_element_entry_p: ^cmt$peripheral_element_entry;
     VAR enabled_path_p: ^array [ * ] of cmt$physical_address;
     VAR enabled_path_count: integer);

    VAR
      controller_address: cmt$physical_address,
      controller_element_entry_p: ^cmt$peripheral_element_entry,
      disabled_address: cmt$physical_address,
      disabled_address_found: boolean,
      enabled_address: cmt$physical_address,
      enabled_path_index: integer,
      found: boolean,
      i: integer,
      j: integer,
      k: integer,
      unit_address: cmt$physical_address,
      unit_element_entry_p: ^cmt$peripheral_element_entry;

    enabled_path_count := 0;
    disabled_address := downline_address;
    disabled_address.address_specifier := cmv$controller_address;

  /controller_loop/
    FOR i := LOWERBOUND (channel_element_entry_p^.physical_descriptor.channel_connection^)
          TO UPPERBOUND (channel_element_entry_p^.physical_descriptor.channel_connection^) DO
      IF downline_address.address_specifier = cmv$data_channel_address THEN
        IF channel_element_entry_p^.physical_descriptor.channel_connection^ [i].status <> cmc$disabled THEN
          CYCLE /controller_loop/; {----->
        IFEND;
      IFEND;

      cmp$get_element_entry_via_name (channel_element_entry_p^.physical_descriptor.channel_connection^ [i].
            downline_element, {iou_number} 0, controller_element_entry_p);
      IF controller_element_entry_p = NIL THEN
        CYCLE /controller_loop/; {----->
      IFEND;

      disabled_address.channel_address := controller_element_entry_p^.physical_descriptor.equipment_path^ [1].
            channel_address;

      {
      { If the downline element specified was a controller element we will only
      { look at units connected to that controller.  If the downline element
      { was a channel all disabled controllers will be searched.
      {
      IF (downline_address.address_specifier = cmv$controller_address) AND
            (disabled_address <> downline_address) THEN
        CYCLE /controller_loop/; {----->
      IFEND;

    /unit_loop/
      FOR j := LOWERBOUND (controller_element_entry_p^.physical_descriptor.equipment_connection^)
            TO UPPERBOUND (controller_element_entry_p^.physical_descriptor.equipment_connection^) DO
        IF downline_address.address_specifier = cmv$data_channel_address THEN
          {
          { Only look at active connections of controllers when the input element is a channel.
          {
          IF controller_element_entry_p^.physical_descriptor.equipment_connection^ [j].status <>
                cmc$active THEN
            CYCLE /unit_loop/; {----->
          IFEND;
        ELSEIF downline_address.address_specifier = cmv$controller_address THEN
          {
          { Only look at disabled connections of controllers when the input element is a controller.
          {
          IF controller_element_entry_p^.physical_descriptor.equipment_connection^ [j].status <>
                cmc$disabled THEN
            CYCLE /unit_loop/; {----->
          IFEND;
        ELSE
          RETURN; {----->
        IFEND;

        cmp$get_element_entry_via_name (controller_element_entry_p^.physical_descriptor.
              equipment_connection^ [j].downline_element, {iou_number} 0, unit_element_entry_p);
        IF unit_element_entry_p = NIL THEN
          RETURN; {----->
        IFEND;

        find_active_path (unit_element_entry_p^.logical_unit_number, enabled_address, found);
        IF NOT found THEN
          CYCLE /unit_loop/; {----->
        IFEND;

        disabled_address_found := FALSE;

      /path_loop/
        FOR k := LOWERBOUND (unit_element_entry_p^.physical_descriptor.unit_path^)
              TO UPPERBOUND (unit_element_entry_p^.physical_descriptor.unit_path^) DO
          unit_address := unit_element_entry_p^.physical_descriptor.unit_path^ [k];
          controller_address := unit_address;
          controller_address.address_specifier := cmv$controller_address;
          controller_address.unit_address := 0;

          IF controller_address = disabled_address THEN
            disabled_address_found := TRUE;
          IFEND;

          IF unit_address = enabled_address THEN
            IF disabled_address_found THEN
              FOR enabled_path_index := LOWERBOUND (enabled_path_p^) TO enabled_path_count DO
                IF enabled_path_p^ [enabled_path_index] = controller_address THEN
                  CYCLE /unit_loop/; {----->
                IFEND;
              FOREND;
              enabled_path_count := enabled_path_count + 1;
              enabled_path_p^ [enabled_path_count] := controller_address;
            IFEND;
          IFEND;
        FOREND /path_loop/;
      FOREND /unit_loop/;
    FOREND /controller_loop/;
  PROCEND find_enabled_paths;

?? TITLE := 'process_das_head_shift_signal', EJECT ??
  {
  { PURPOSE:
  {   This procedure extracts the information from a signal and
  {   calls several CM interfaces to obtain the element_definition for
  {   the disabled element. The routine to format and display the
  {   information is then called.  A engineering log statistic is
  {   constructed and sent to the engineering log.
  {

  PROCEDURE process_das_head_shift_signal
    (    signal_contents: cmt$signal_contents;
     VAR status: ost$status);

    VAR
      element_definition_p: ^cmt$element_definition,
      element_entry_p: ^cmt$peripheral_element_entry,
      element_name: cmt$element_name,
      iou_name: cmt$element_name,
      local_status: ost$status;

    VAR
      not_found: boolean,
      parameters: ^ost$message_parameters,
      product_id_str: ost$name,
      recorded_vsn: rmt$recorded_vsn,
      search_key: dmt$avt_search_key,
      size: ost$string_size,
      temp_str: ost$string;

    cmp$get_element_entry_via_lun (signal_contents.hd_shift_logical_unit, element_entry_p);
    IF element_entry_p = NIL THEN
      local_status.normal := FALSE;
      osp$system_error ('cmp$get_element_entry_via_lun unable to locate element.', ^local_status);
    IFEND;

    element_name := element_entry_p^.element_name;

    temp_str.value := ' ';

    size := STRLENGTH (element_entry_p^.product_id.product_number);
    temp_str.value (1, size) := element_entry_p^.product_id.product_number;
    temp_str.size := size;

    size := STRLENGTH (element_entry_p^.product_id.underscore);
    temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.underscore;
    temp_str.size := temp_str.size + size;

    size := STRLENGTH (element_entry_p^.product_id.model_number);
    temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.model_number;
    temp_str.size := temp_str.size + size;

    set_string_length (temp_str);
    product_id_str := temp_str.value (1, temp_str.size);

    temp_str.value := ' ';

    search_key.value := dmc$search_avt_by_lun;
    search_key.logical_unit_number := element_entry_p^.logical_unit_number;
    cmp$search_active_volume_table (search_key, recorded_vsn, not_found);
    IF not_found THEN
      recorded_vsn := '      ';
    IFEND;

    clp$convert_integer_to_string (signal_contents.hd_shift_physical_unit, {radix} 16, TRUE, temp_str,
          status);

    PUSH parameters: [1 .. 4];
    parameters^ [1] := ^element_name;
    parameters^ [2] := ^product_id_str;
    parameters^ [3] := ^recorded_vsn;
    parameters^ [4] := ^temp_str.value (1, temp_str.size);

    emit_operator_message (cmc$action_messages, 'DAS_DRIVE_HEAD_SHIFT_WARNING   ', parameters, status);

  PROCEND process_das_head_shift_signal;

?? TITLE := 'process_disable_signal', EJECT ??
  {
  { PURPOSE:
  {   This procedure extracts the information from a signal and
  {   calls several CM interfaces to obtain the element_definition for
  {   the disabled element. The routine to format and display the
  {   information is then called.  A engineering log statistic is
  {   constructed and sent to the engineering log.
  {

  PROCEDURE process_disable_signal
    (    signal_contents: cmt$signal_contents;
     VAR status: ost$status);

    VAR
      element_definition_p: ^cmt$element_definition,
      element_entry_p: ^cmt$peripheral_element_entry,
      iou_name: cmt$element_name,
      local_status: ost$status;


    cmp$get_element_entry_via_adr (signal_contents.disable_element_address, element_entry_p);
    IF element_entry_p = NIL THEN
      local_status.normal := FALSE;
      osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
    IFEND;

    cmp$convert_iou_number (signal_contents.disable_element_address.iou, iou_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    PUSH element_definition_p;
    cmp$get_element_r3 (element_entry_p^.element_name, iou_name, element_definition_p, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    display_disable_menu (element_definition_p^, element_entry_p, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    emit_disable_element_statistic (element_entry_p, status);

  PROCEND process_disable_signal;

?? TITLE := 'process_down_element_signal', EJECT ??
  {
  { PURPOSE:
  {   This procedure extracts the information from a signal and
  {   calls several CM interfaces to obtain the element_definition for
  {   the downed element. The interface to perform the actual state change
  {   is called.
  {

  PROCEDURE process_down_element_signal
    (    signal_contents: cmt$signal_contents;
     VAR status: ost$status);

    VAR
      element_definition_p: ^cmt$element_definition,
      element_descriptor: cmt$element_descriptor,
      element_entry_p: ^cmt$peripheral_element_entry,
      element_info: array [1 .. 2] of cmt$element_info_item,
      iou_name: cmt$element_name,
      local_status: ost$status,
      tape_element: boolean;

    cmp$get_element_entry_via_adr (signal_contents.down_element_address, element_entry_p);
    IF element_entry_p = NIL THEN
      local_status.normal := FALSE;
      osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
    IFEND;

    cmp$convert_iou_number (signal_contents.down_element_address.iou, iou_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    PUSH element_definition_p;
    cmp$get_element_r3 (element_entry_p^.element_name, iou_name, element_definition_p, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    element_descriptor.element_type := element_definition_p^.element_type;

    CASE element_descriptor.element_type OF
    = cmc$data_channel_element =
      element_descriptor.channel_descriptor.iou := iou_name;
      element_descriptor.channel_descriptor.use_logical_identification := TRUE;
      element_descriptor.channel_descriptor.name := element_entry_p^.element_name;

    = cmc$channel_adapter_element, cmc$communications_element, cmc$controller_element,
          cmc$external_processor_element, cmc$storage_device_element =
      element_descriptor.peripheral_descriptor.use_logical_identification := TRUE;
      element_descriptor.peripheral_descriptor.element_name := element_entry_p^.element_name;

    = cmc$central_processor_element =
      element_descriptor.name := element_entry_p^.element_name;
    ELSE
      osp$set_status_abnormal (cmc$configuration_management_id, cme$lcm_not_available,
            'Invalid state change request for given element type', status);
      RETURN; {----->
    CASEND;

    cmp$determine_tape_element (element_descriptor, tape_element);

    element_info [1].selector := cmc$system_critical_element;
    element_info [2].selector := cmc$element_status;
    cmp$get_element_information (element_descriptor, element_info, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

{Note: this signal is currently not used!
{The way it was designed, it would not have processed outstanding changed, but therefore kept a lock behind.
{Now, we would clear the lock, but also process outstanding changes.

{It might make sense to use this signals to change the state in several cases. But hen, we have to rethink
{the process!

    cmp$process_state_change (tape_element, {system_call} TRUE, element_descriptor, element_info [1].
          system_critical_element, element_info [2].element_status.state, cmc$down, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

  PROCEND process_down_element_signal;

?? TITLE := 'process_overtemp_signal', EJECT ??
  {
  { PURPOSE:
  {   This procedure extracts the information from a signal and
  {   calls several CM interfaces to obtain the element_definition for
  {   the disabled element. The routine to format and display the
  {   information is then called.  A engineering log statistic is
  {   constructed and sent to the engineering log.
  {

  PROCEDURE process_overtemp_signal
    (    signal_contents: cmt$signal_contents;
     VAR status: ost$status);

    VAR
      element_definition_p: ^cmt$element_definition,
      element_entry_p: ^cmt$peripheral_element_entry,
      element_name: cmt$element_name,
      iou_name: cmt$element_name,
      local_status: ost$status;

    VAR
      not_found: boolean,
      parameters: ^ost$message_parameters,
      product_id_str: ost$name,
      recorded_vsn: rmt$recorded_vsn,
      search_key: dmt$avt_search_key,
      size: ost$string_size,
      temp_str: ost$string;


    cmp$get_element_entry_via_adr (signal_contents.overtemp_element_address, element_entry_p);
    IF element_entry_p = NIL THEN
      local_status.normal := FALSE;
      osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
    IFEND;

    cmp$convert_iou_number (signal_contents.disable_element_address.iou, iou_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    PUSH element_definition_p;
    cmp$get_element_r3 (element_entry_p^.element_name, iou_name, element_definition_p, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;
    element_name := element_entry_p^.element_name;
    PUSH parameters: [1 .. 1];
    parameters^ [1] := ^element_name;

    emit_operator_message (cmc$action_messages, 'CONTROLLER_OVERTEMP            ', parameters, status);

  PROCEND process_overtemp_signal;

?? TITLE := 'process_parity_disabled_signal', EJECT ??
  {
  { PURPOSE:
  {   This procedure extracts the information from a signal and
  {   calls several CM interfaces to obtain the element_definition for
  {   the disabled element. The routine to format and display the
  {   information is then called.  A engineering log statistic is
  {   constructed and sent to the engineering log.
  {

  PROCEDURE process_parity_disabled_signal
    (    signal_contents: cmt$signal_contents;
     VAR status: ost$status);

    VAR
      element_definition_p: ^cmt$element_definition,
      element_entry_p: ^cmt$peripheral_element_entry,
      element_name: cmt$element_name,
      iou_name: cmt$element_name,
      local_status: ost$status;

    VAR
      not_found: boolean,
      parameters: ^ost$message_parameters,
      product_id_str: ost$name,
      recorded_vsn: rmt$recorded_vsn,
      search_key: dmt$avt_search_key,
      size: ost$string_size,
      temp_str: ost$string;

    cmp$get_element_entry_via_lun (signal_contents.parity_logical_unit, element_entry_p);
    IF element_entry_p = NIL THEN
      local_status.normal := FALSE;
      osp$system_error ('cmp$get_element_entry_via_lun unable to locate element.', ^local_status);
    IFEND;

    element_name := element_entry_p^.element_name;

    temp_str.value := ' ';

    size := STRLENGTH (element_entry_p^.product_id.product_number);
    temp_str.value (1, size) := element_entry_p^.product_id.product_number;
    temp_str.size := size;

    size := STRLENGTH (element_entry_p^.product_id.underscore);
    temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.underscore;
    temp_str.size := temp_str.size + size;

    size := STRLENGTH (element_entry_p^.product_id.model_number);
    temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.model_number;
    temp_str.size := temp_str.size + size;

    set_string_length (temp_str);
    product_id_str := temp_str.value (1, temp_str.size);

    temp_str.value := ' ';

    search_key.value := dmc$search_avt_by_lun;
    search_key.logical_unit_number := element_entry_p^.logical_unit_number;
    cmp$search_active_volume_table (search_key, recorded_vsn, not_found);
    IF not_found THEN
      recorded_vsn := '      ';
    IFEND;

    clp$convert_integer_to_string (signal_contents.parity_physical_unit, {radix} 16, TRUE, temp_str, status);

    PUSH parameters: [1 .. 4];
    parameters^ [1] := ^element_name;
    parameters^ [2] := ^product_id_str;
    parameters^ [3] := ^recorded_vsn;
    parameters^ [4] := ^temp_str.value (1, temp_str.size);

    emit_operator_message (cmc$action_messages, 'PARITY_PROTECTION_DISABLED     ', parameters, status);

  PROCEND process_parity_disabled_signal;

?? TITLE := 'process_reconfig_signal', EJECT ??
  {
  { PURPOSE:
  {   This procedure extracts the information from a signal and
  {   calls several CM interfaces to obtain the element_definition for
  {   the reconfigured element. Information describing which connection
  {   and/or path has been disabled and which paths have been enabled
  {   is generated and placed in a parameter list.  The routine to format
  {   and display the information is then called.  A engineering statistic
  {   is generated and sent to the engineering log.
  {

  PROCEDURE process_reconfig_signal
    (    signal_contents: cmt$signal_contents;
     VAR status: ost$status);

    VAR
      channel_element_entry_p: ^cmt$peripheral_element_entry,
      connection_label: ost$string,
      controller_element_entry_p: ^cmt$peripheral_element_entry,
      disabled_path: ost$string,
      downline_element_address: cmt$physical_address,
      downline_element_entry_p: ^cmt$peripheral_element_entry,
      enabled_address: cmt$physical_address,
      enabled_path: ost$string,
      enabled_path_adr_list_p: ^array [ * ] of cmt$physical_address,
      enabled_path_count: integer,
      enabled_path_str_list_p: ^array [ * ] of ost$string,
      element_definition_p: ^cmt$element_definition,
      element_descriptor: cmt$element_descriptor,
      element_entry: cmt$peripheral_element_entry,
      found: boolean,
      i: integer,
      iou_name: cmt$element_name,
      local_status: ost$status,
      lun_count: integer,
      parameters: ^ost$message_parameters,
      performance_impact: string (50),
      upline_element_address: cmt$physical_address,
      upline_element_entry_p: ^cmt$peripheral_element_entry;

    performance_impact := ' ';
    downline_element_address := signal_contents.reconfig_element_address;
    cmp$get_element_entry_via_adr (downline_element_address, downline_element_entry_p);
    IF downline_element_entry_p = NIL THEN
      local_status.normal := FALSE;
      osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
    IFEND;

    cmp$convert_iou_number (signal_contents.reconfig_element_address.iou, iou_name, status);
    IF NOT status.normal THEN
      RETURN; {----->
    IFEND;

    CASE downline_element_entry_p^.physical_descriptor.element_type OF
    = cmc$data_channel_element =
      build_path (downline_element_address, disabled_path);

      enabled_path_count := 4;
      REPEAT
        PUSH enabled_path_adr_list_p: [1 .. enabled_path_count];
        find_enabled_paths (downline_element_address, downline_element_entry_p, enabled_path_adr_list_p,
              enabled_path_count);
      UNTIL enabled_path_count <= UPPERBOUND (enabled_path_adr_list_p^);

      PUSH parameters: [1 .. 6];
      parameters^ [1] := ^disabled_path.value (1, disabled_path.size);

      PUSH enabled_path_str_list_p: [1 .. enabled_path_count];

      FOR i := 1 TO 4 DO
        IF i <= enabled_path_count THEN
          build_path (enabled_path_adr_list_p^ [i], enabled_path_str_list_p^ [i]);
          parameters^ [i + 1] := ^enabled_path_str_list_p^ [i].value (1, enabled_path_str_list_p^ [i].size);
        ELSE
          parameters^ [i + 1] := NIL;
        IFEND;
      FOREND;

      parameters^ [6] := NIL; {performance imapct }

      emit_operator_message (cmc$action_messages, 'RECONFIGURE_CHANNEL            ', parameters, status);

      emit_disable_element_statistic (downline_element_entry_p, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

    = cmc$controller_element =
      upline_element_address := downline_element_address;
      upline_element_address.address_specifier := cmv$data_channel_address;
      upline_element_address.channel_address := 0;

      cmp$get_element_entry_via_adr (upline_element_address, upline_element_entry_p);
      IF upline_element_entry_p = NIL THEN
        local_status.normal := FALSE;
        osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
      IFEND;

      build_path (downline_element_address, disabled_path);

      enabled_path_count := 4;
      REPEAT
        PUSH enabled_path_adr_list_p: [1 .. enabled_path_count];
        find_enabled_paths (downline_element_address, upline_element_entry_p, enabled_path_adr_list_p,
              enabled_path_count);
      UNTIL enabled_path_count <= UPPERBOUND (enabled_path_adr_list_p^);

      PUSH parameters: [1 .. 6];
      parameters^ [1] := ^disabled_path.value (1, disabled_path.size);

      PUSH enabled_path_str_list_p: [1 .. enabled_path_count];

      FOR i := 1 TO 4 DO
        IF i <= enabled_path_count THEN
          build_path (enabled_path_adr_list_p^ [i], enabled_path_str_list_p^ [i]);
          parameters^ [i + 1] := ^enabled_path_str_list_p^ [i].value (1, enabled_path_str_list_p^ [i].size);
        ELSE
          parameters^ [i + 1] := NIL;
        IFEND;
      FOREND;

      parameters^ [6] := NIL; {performance imapct }

      emit_operator_message (cmc$action_messages, 'RECONFIGURE_CONTROLLER         ', parameters, status);

      emit_disable_element_statistic (downline_element_entry_p, status);
      IF NOT status.normal THEN
        RETURN; {----->
      IFEND;

    = cmc$storage_device_element =
      upline_element_address := downline_element_address;
      upline_element_address.address_specifier := cmv$controller_address;
      upline_element_address.unit_address := 0;

      cmp$get_element_entry_via_adr (upline_element_address, upline_element_entry_p);
      IF upline_element_entry_p = NIL THEN
        local_status.normal := FALSE;
        osp$system_error ('cmp$get_element_entry_via_adr unable to locate element.', ^local_status);
      IFEND;

      build_connection_label (upline_element_entry_p, downline_element_entry_p, iou_name, connection_label);
      build_path (downline_element_address, disabled_path);

      find_active_path (downline_element_entry_p^.logical_unit_number, enabled_address, found);
      IF found THEN
        build_path (enabled_address, enabled_path);
      ELSE
        enabled_path.value := ' ';
        enabled_path.size := 1;
      IFEND;

      PUSH parameters: [1 .. 4];
      parameters^ [1] := ^connection_label.value (1, connection_label.size);
      parameters^ [2] := ^disabled_path.value (1, disabled_path.size);
      parameters^ [3] := ^enabled_path.value (1, enabled_path.size);
      parameters^ [4] := NIL; {performance imapct }

      emit_operator_message (cmc$action_messages, 'RECONFIGURE_UNIT               ', parameters, status);

      IF signal_contents.failing_element_address = downline_element_address THEN
        emit_disable_connection_stat (upline_element_entry_p, downline_element_entry_p, connection_label,
              downline_element_entry_p^.element_name, status);
      ELSE
        emit_disable_connection_stat (upline_element_entry_p, downline_element_entry_p, connection_label,
              upline_element_entry_p^.element_name, status);
      IFEND;

    ELSE
    CASEND;

  PROCEND process_reconfig_signal;

?? TITLE := 'process_ssd_battery_signal', EJECT ??
  {
  { PURPOSE:
  {   This procedure extracts the information from a signal and
  {   calls several CM interfaces to obtain the element_definition for
  {   the disabled element. The routine to format and display the
  {   information is then called.  A engineering log statistic is
  {   constructed and sent to the engineering log.
  {

  PROCEDURE process_ssd_battery_signal
    (    signal_contents: cmt$signal_contents;
     VAR status: ost$status);

    VAR
      element_definition_p: ^cmt$element_definition,
      element_entry_p: ^cmt$peripheral_element_entry,
      element_name: cmt$element_name,
      iou_name: cmt$element_name,
      local_status: ost$status;

    VAR
      battery_condition: 0 .. 0ffff(16),
      battery_condition_str: string (31),
      not_found: boolean,
      parameters: ^ost$message_parameters,
      product_id_str: ost$name,
      recorded_vsn: rmt$recorded_vsn,
      search_key: dmt$avt_search_key,
      size: ost$string_size,
      temp_str: ost$string;

    cmp$get_element_entry_via_lun (signal_contents.hd_shift_logical_unit, element_entry_p);
    IF element_entry_p = NIL THEN
      local_status.normal := FALSE;
      osp$system_error ('cmp$get_element_entry_via_lun unable to locate element.', ^local_status);
    IFEND;

    element_name := element_entry_p^.element_name;

    temp_str.value := ' ';

    size := STRLENGTH (element_entry_p^.product_id.product_number);
    temp_str.value (1, size) := element_entry_p^.product_id.product_number;
    temp_str.size := size;

    size := STRLENGTH (element_entry_p^.product_id.underscore);
    temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.underscore;
    temp_str.size := temp_str.size + size;

    size := STRLENGTH (element_entry_p^.product_id.model_number);
    temp_str.value (temp_str.size + 1, size) := element_entry_p^.product_id.model_number;
    temp_str.size := temp_str.size + size;

    set_string_length (temp_str);
    product_id_str := temp_str.value (1, temp_str.size);

    temp_str.value := ' ';

    search_key.value := dmc$search_avt_by_lun;
    search_key.logical_unit_number := element_entry_p^.logical_unit_number;
    cmp$search_active_volume_table (search_key, recorded_vsn, not_found);
    IF not_found THEN
      recorded_vsn := '      ';
    IFEND;

    clp$convert_integer_to_string (signal_contents.hd_shift_physical_unit, {radix} 16, TRUE, temp_str,
          status);

    battery_condition := signal_contents.battery_alert_condition;
    IF battery_condition = ioc$9836_1_ssd_battery_to_low THEN
      battery_condition_str := 'SSD BATTERY TOO LOW FOR BACKUP ';
    ELSEIF battery_condition = ioc$9836_1_ssd_battery_test THEN
      battery_condition_str := 'SSD BATTERY TEST FAILED        ';
    ELSEIF battery_condition = ioc$9836_1_ssd_battery_old THEN
      battery_condition_str := 'SSD BATTERY OLD - REPLACE      ';
    IFEND;

    PUSH parameters: [1 .. 5];
    parameters^ [1] := ^element_name;
    parameters^ [2] := ^product_id_str;
    parameters^ [3] := ^recorded_vsn;
    parameters^ [4] := ^temp_str.value (1, temp_str.size);
    parameters^ [5] := ^battery_condition_str;

    emit_operator_message (cmc$action_messages, 'SSD_BATTERY_ALERT              ', parameters, status);

  PROCEND process_ssd_battery_signal;
?? TITLE := 'set_string_length', EJECT ??

{ PURPOSE:
{   This procedure finds the length of a string that is passed in as a parameter.  It also removes
{   any spaces that are at the beginning and at the end of the string.

  PROCEDURE set_string_length
    (VAR string_data: ost$string);

    VAR
      begin_index: ost$string_size,
      end_index: ost$string_size,
      temp_string: string (osc$max_string_size);

    { If the string is all blank set the string length to one and return.

    IF string_data.value = ' ' THEN
      string_data.size := 1;
      RETURN; {----->
    IFEND;

    { Find the first non-blank character in the string.

    begin_index := 1;
    WHILE (begin_index <= osc$max_string_size) AND (string_data.value (begin_index) = ' ') DO
      begin_index := begin_index + 1;
    WHILEND;

    { Find the last non-blank character in the string.

    end_index := osc$max_string_size;
    WHILE (end_index > begin_index) AND (string_data.value (end_index) = ' ') DO
      end_index := end_index - 1;
    WHILEND;

    { Move the data in the string so the first non-blank character is the first character in the string and
    { determine the size of the string from the first non-blank character to the last non-blank character.

    temp_string := string_data.value;
    string_data.value := temp_string (begin_index, (end_index - begin_index) + 1);
    string_data.size := (end_index - begin_index) + 1;

  PROCEND set_string_length;

MODEND cmm$signal_handler;

