1 /****^  ***********************************************************
   2         *                                                         *
   3         * Copyright, (C) Honeywell Bull Inc., 1987                *
   4         *                                                         *
   5         * Copyright, (C) Honeywell Information Systems Inc., 1984 *
   6         *                                                         *
   7         *********************************************************** */
   8 
   9 /****^  HISTORY COMMENTS:
  10   1) change(85-09-09,Farley), approve(85-09-09,MCR6979),
  11      audit(85-12-02,CLJones), install(86-03-21,MR12.0-1033):
  12      Fixed to continue
  13      adding devices if one cannot be added, if the add_all_attachment was
  14      given.
  15   2) change(87-06-12,Lippard), approve(87-06-29,MCR7729),
  16      audit(87-07-08,Farley), install(86-08-06,MR12.1-1064):
  17      Changed to allow -delete_all_attachments for IOMs when some channels
  18      have already been deleted.
  19   3) change(87-10-22,Parisek), approve(87-10-29,MCR7790),
  20      audit(88-04-28,GDixon), install(88-05-04,MR12.2-1045):
  21      Replace a call to com_err_ with a call to ioa_ for reporting an error
  22      resulting from attempting to delete a device which is already in the
  23      deleted state.
  24   4) change(88-12-07,Parisek), approve(88-12-30,MCR8040),
  25      audit(89-01-04,Farley), install(89-01-17,MR12.3-1005):
  26      Revise the rc_messages array declaration so the second dimension of the
  27      array represents the actual count of 11 messages.  The eleventh message
  28      was added for MR12.2.
  29                                                    END HISTORY COMMENTS */
  30 /* User-ring and operator interface to reconfiguration software. */
  31 /* Written May 1984 by Chris Jones. */
  32 /* Bugfixes from exposure, July 1984, Chris Jones */
  33 /* Modified to add -force, August 1984, Chris Jones */
  34 /* Modified to avoid duplicate messages on operators' console, add sc_reconfigure_request entrypoint,
  35    November 1984, Chris Jones */
  36 /* Modified to use ioa_ on non-error messages, parse fnp names correctly, January 1985, Chris Jones */
  37 /* Modified to correctly handle adding and removing disks with "*_all_attachments", May 1985, Chris Jones */
  38 /* Modified to continue adding devices if one cannot be added, if the add_all_attachment was given. Sept 1985, Paul Farley */
  39 
  40 /* format: style4,delnl,insnl,indattr,ifthen,dclind10 */
  41 
  42 reconfigure:
  43 rcf:
  44      proc options (variable);
  45 
  46 dcl       action                 fixed bin;                 /* says what kind of thing we're doing */
  47 dcl       add_all_sw             bit (1) aligned;           /* set if -add_all_attachments given */
  48 dcl       add_del_sw             bit (1) aligned;           /* indicates add or delete operation */
  49 dcl       arg_count              fixed bin;                 /* number of arguments we were invoked with */
  50 dcl       arg_idx                fixed bin;                 /* current argument number */
  51 dcl       arg_state              fixed bin;                 /* denotes which of operation, type, or name is next */
  52 dcl       backing_out            bit (1) aligned;           /* set if we're backing out what we've done */
  53 dcl       backout_list_ptr       ptr;                       /* pointer to list of things to undo if we have problems */
  54 dcl       brief_sw               bit (1) aligned;           /* set if -brief given */
  55 dcl       cdtp                   ptr;
  56 dcl       channel_idx            fixed bin;                 /* index into io_config_data.channel_table */
  57 dcl       code                   fixed bin (35);            /* system status code */
  58 dcl       control_arg            bit (1) aligned;           /* set if current arg is a control arg */
  59 dcl       deadline               fixed bin (71);            /* when Godot has to go it alone */
  60 dcl       delete_all_sw          bit (1) aligned;           /* set if -delete_all_attachments given */
  61 dcl       device_idx             fixed bin;                 /* index into device_table */
  62 dcl       error_tag              fixed bin (3);             /* tag of SCU where problem occurred */
  63 dcl       first_frame            fixed bin;                 /* first memory frame to play with */
  64 dcl       force_sw               bit (1) aligned;           /* set if -force used */
  65 dcl       i                      fixed bin;                 /* random index */
  66 dcl       iom_idx                fixed bin;                 /* index into iom_table */
  67 dcl       interlace              bit (1) aligned;           /* set if an SCU is externally interlaced */
  68 dcl       mpc_idx                fixed bin;                 /* index into controller_table */
  69 dcl       n_frames               fixed bin;                 /* number of frames to play with */
  70 dcl       name                   char (32);                 /* name of the thing we're fiddling with */
  71 dcl       reason                 char (256) varying;        /* used for error messages */
  72 dcl       sci_ptr                ptr;                       /* info pointer for ssu_ */
  73 dcl       standalone_invocation  bit (1) aligned;           /* On if called as command rather than an ssu request */
  74 dcl       switches               (4) bit (36) aligned;      /* results from rsw's on a CPU */
  75 dcl       tag                    fixed bin (3);             /* CPU, IOM, or SCU identifier */
  76 dcl       type_idx               fixed bin;                 /* denotes what kind of thing we're playing with */
  77 
  78 dcl       arg                    char (argl) based (argp);
  79 dcl       argl                   fixed bin (21);
  80 dcl       argp                   ptr;
  81 
  82 dcl       1 pi                   like rsw_1_3.port_info based (pip) unal;
  83                                                             /* port info for one port (from rsw) */
  84 dcl       pip                    ptr;
  85 
  86 dcl       area                   area based (area_ptr);
  87 dcl       area_ptr               ptr;
  88 
  89 dcl       1 backout_item         aligned based (item_ptr),  /* what we have to undo when we cleanup */
  90             2 idx                fixed bin,                 /* how we reference it */
  91             2 type               char (8),                  /* "device", "channel", or "iom" */
  92             2 next_item          ptr;                       /* forward pointer */
  93 dcl       item_ptr               ptr;
  94 dcl       ADD                    bit (1) aligned static options (constant) init ("1"b);
  95 dcl       DELETE                 bit (1) aligned static options (constant) init ("0"b);
  96 
  97 dcl       TYPES                  (15) char (16) static options (constant)
  98                                  init ("channel", "chan", "chnl", "cpu", "device", "dv", "prph", "iom", "link_adapter",
  99                                  "la", "mpc", "page", "pages", "mem", "scu");
 100 
 101 dcl       ACTIONS                (15) fixed bin static options (constant)
 102                                  init (5, 5, 5, 1, 4, 4, 4, 8, 6, 6, 7, 3, 3, 2, 2);
 103 dcl       ME                     char (16) static options (constant) init ("reconfigure");
 104 dcl       RCF_V1                 char (4) static options (constant) init ("1.00");
 105 dcl       RANGE_SEPARATOR        char (1) static options (constant) init (":");
 106 dcl       REASON_SEPARATOR       char (2) static options (constant) init ("
 107           ");
 108 
 109 dcl       TAGS_STRING            char (8) static options (constant) init ("abcdefgh");
 110 dcl       TAGS_STRING_UPPER_CASE char (8) static options (constant) init ("ABCDEFGH");
 111 dcl       TAGS                   (0:7) char (1) unal defined TAGS_STRING;
 112 
 113 dcl       TEN_SECONDS            fixed bin (71) static options (constant) init (10000000);
 114 
 115 dcl       error_table_$action_not_performed
 116                                  fixed bin (35) ext static;
 117 dcl       error_table_$bad_arg   fixed bin (35) ext static;
 118 dcl       error_table_$badopt    fixed bin (35) ext static;
 119 dcl       error_table_$chnl_already_deleted
 120                                  fixed bin (35) ext static;
 121 dcl       error_table_$inconsistent
 122                                  fixed bin (35) ext static;
 123 dcl       error_table_$io_not_configured
 124                                  fixed bin (35) ext static;
 125 dcl       error_table_$io_not_defined
 126                                  fixed bin (35) ext static;
 127 dcl       error_table_$noarg     fixed bin (35) ext static;
 128 dcl       error_table_$not_base_channel
 129                                  fixed bin (35) ext static;
 130 dcl       error_table_$too_many_args
 131                                  fixed bin (35) ext static;
 132 dcl       error_table_$unimplemented_version
 133                                  fixed bin (35) ext static;
 134 
 135 dcl       rc_messages$rc_messages
 136                                  (0:7, 11) char (64) aligned ext;
 137 
 138 dcl       com_err_               entry () options (variable);
 139 dcl       cu_$arg_list_ptr       entry (ptr);
 140 dcl       cv_integer_string_check_
 141                                  entry (char (*), fixed bin, fixed bin (35)) returns (fixed bin (35));
 142 dcl       hphcs_$add_channel     entry (char (8) aligned, fixed bin (35));
 143 dcl       hphcs_$add_cpu         entry (fixed bin (3), (4) bit (36) aligned, fixed bin (35));
 144 dcl       hphcs_$add_iom         entry (fixed bin (3), fixed bin (35));
 145 dcl       hphcs_$add_main        entry (fixed bin, fixed bin, fixed bin (35));
 146 dcl       hphcs_$add_scu         entry (fixed bin (3), bit (1) aligned, fixed bin (3), fixed bin (35));
 147 dcl       hphcs_$del_cpu         entry (fixed bin (3), fixed bin (35));
 148 dcl       hphcs_$del_scu         entry (fixed bin (3), bit (1) aligned, fixed bin (35));
 149 dcl       hphcs_$del_main        entry (fixed bin, fixed bin, fixed bin (35));
 150 dcl       hphcs_$delete_channel  entry (char (8) aligned, fixed bin (35));
 151 dcl       hphcs_$delete_iom      entry (fixed bin (3), fixed bin (35));
 152 dcl       ioa_                   entry () options (variable);
 153 dcl       ioa_$general_rs        entry (ptr, fixed bin, fixed bin, char (*), fixed bin (21), bit (1) aligned,
 154                                  bit (1) aligned);
 155 dcl       rcp_sys_$add_device    entry (char (*), fixed bin (35));
 156 dcl       rcp_sys_$delete_device entry (char (*), fixed bin (35));
 157 dcl       ssu_$abort_line        entry () options (variable);
 158 dcl       ssu_$arg_count         entry (ptr, fixed bin);
 159 dcl       ssu_$arg_ptr           entry (ptr, fixed bin, ptr, fixed bin (21));
 160 dcl       ssu_$destroy_invocation
 161                                  entry (ptr);
 162 dcl       ssu_$get_area          entry (ptr, ptr, char (*), ptr);
 163 dcl       ssu_$print_message     entry () options (variable);
 164 dcl       ssu_$release_area      entry (ptr, ptr);
 165 dcl       ssu_$standalone_invocation
 166                                  entry (ptr, char (*), char (*), ptr, entry, fixed bin (35));
 167 dcl       terminate_file_        entry (ptr, fixed bin (24), bit (*), fixed bin (35));
 168 
 169 dcl       (addr, after, bin, clock, hbound, index, lbound, mod, null, ptr, rtrim, substr, translate)
 170                                  builtin;
 171 
 172 dcl       cleanup                condition;
 173 ^L
 174           standalone_invocation = "1"b;
 175           sci_ptr, sc_subsystem_info_ptr, cdtp, backout_list_ptr,
 176                area_ptr = null ();
 177           on cleanup begin;
 178                call backout_work_so_far;
 179                call clean_up;
 180           end;
 181           call ssu_$standalone_invocation (sci_ptr, ME, RCF_V1, null (), abort_entry, code);
 182           if code ^= 0 then do;
 183                call com_err_ (code, ME, "Could not create ssu_ invocation.");
 184                return;
 185           end;
 186           goto RECONFIGURE_COMMON;
 187 
 188 sc_reconfigure_request:
 189      entry (p_sci_ptr, p_ssu_info_ptr);
 190 
 191 dcl       p_sci_ptr              ptr;                       /* invocation structure pointer */
 192 dcl       p_ssu_info_ptr         ptr;                       /* pointer to subsystem info */
 193 
 194           standalone_invocation = "0"b;
 195           cdtp = null ();
 196           backout_list_ptr, area_ptr = null ();
 197           sci_ptr = p_sci_ptr;
 198           sc_subsystem_info_ptr = p_ssu_info_ptr;
 199           on cleanup begin;
 200                call backout_work_so_far;
 201                call clean_up;
 202           end;
 203 
 204 RECONFIGURE_COMMON:
 205           backing_out = "0"b;
 206           call ssu_$arg_count (sci_ptr, arg_count);
 207           if arg_count = 0 then
 208                call quit_with_usage (0);
 209           else if arg_count < 3 then
 210                call quit_with_usage (error_table_$noarg);
 211           call ssu_$get_area (sci_ptr, null (), "undo list", area_ptr);
 212 
 213           add_all_sw, delete_all_sw = "0"b;
 214           brief_sw = "0"b;
 215           force_sw = "0"b;
 216           arg_state = 0;                                    /* have seen no non-control arguments */
 217           reason = "";
 218           do arg_idx = 1 to arg_count;
 219                call ssu_$arg_ptr (sci_ptr, arg_idx, argp, argl);
 220                if argl > 0 then
 221                     if substr (arg, 1, 1) = "-" then
 222                          control_arg = "1"b;
 223                     else control_arg = "0"b;
 224                else control_arg = "0"b;
 225 
 226                if control_arg then do;
 227                     if arg = "-add_all_attachments" then
 228                          add_all_sw = "1"b;
 229                     else if arg = "-brief" | arg = "-bf" then
 230                          brief_sw = "1"b;
 231                     else if arg = "-delete_all_attachments" then
 232                          delete_all_sw = "1"b;
 233                     else if arg = "-long" | arg = "-lg" then
 234                          brief_sw = "0"b;
 235                     else if arg = "-force" | arg = "-fc" then
 236                          force_sw = "1"b;
 237                     else if arg = "-no_force" | arg = "-nfc" then
 238                          force_sw = "0"b;
 239                     else call quit (error_table_$badopt, arg);
 240                end;
 241                else do;                                     /* non-control arg */
 242                     arg_state = arg_state + 1;              /* one more non-control argument */
 243 
 244                     if arg_state = 1 then do;
 245                          if arg = "add" then
 246                               add_del_sw = ADD;
 247                          else if arg = "delete" | arg = "dl" then
 248                               add_del_sw = DELETE;
 249                          else call quit (error_table_$bad_arg, arg);
 250                     end;
 251                     else if arg_state = 2 then do;
 252                          do type_idx = lbound (TYPES, 1) to hbound (TYPES, 1) while (TYPES (type_idx) ^= arg);
 253                          end;
 254                          if type_idx > hbound (TYPES, 1) then
 255                               call quit (error_table_$bad_arg, arg);
 256                          action = ACTIONS (type_idx);
 257                     end;
 258                     else if arg_state = 3 then do;
 259                          name = arg;                        /* we'll accept anything for the name at this point */
 260                     end;
 261                     else call quit (error_table_$too_many_args);
 262                end;
 263           end;
 264 
 265           if arg_state ^= 3 then                            /* don't have all the pieces */
 266                call quit_with_usage (error_table_$noarg);
 267           if (add_all_sw & add_del_sw = DELETE) | (delete_all_sw & add_del_sw = ADD) then
 268                call quit (error_table_$inconsistent);
 269 
 270 /**** At this point, we know what we want to do.   Now, go do it. ****/
 271 
 272           goto ACTION_LABEL (action);                       /* n-way branch on type of thing to reconfigure */
 273 
 274 ACTION_LABEL (0):
 275           call quit (error_table_$bad_arg, "The request is not yet implemented.");
 276 ^L
 277 /**** CPUs ****/
 278 
 279 ACTION_LABEL (1):                                           /* CPU */
 280           tag = get_tag_from_name (name) - 1;
 281           name = "CPU " || rtrim (name);                    /* for better error messages */
 282           if add_del_sw = ADD then do;                      /* add case */
 283                call hphcs_$add_cpu (tag, switches, code);
 284                if code = rcerr_addcpu_bad_switches then do; /* If config switches in error ... */
 285                     rswp = addr (switches (2));
 286                     if dps_rsw_2.fault_base then
 287                          call generate_switch_message ("Fault Base");
 288                     if dps_rsw_2.cpu_num ^= 0 then
 289                          call generate_switch_message ("Processor Number");
 290 
 291                     rswp = addr (switches (4));
 292                     do i = 0 to 7;
 293                          if i < 4 then
 294                               pip = addr (addr (switches (1)) -> rsw_1_3.port_info (i));
 295                          else pip = addr (addr (switches (3)) -> rsw_1_3.port_info (i - 4));
 296 
 297                          if pi.port_assignment then
 298                               call generate_switch_memory_message ("Port Assignment");
 299                          if pi.port_enable then
 300                               call generate_switch_memory_message ("Port Enable");
 301                          if pi.interlace_enable | rsw_4.four (i) then
 302                               call generate_switch_memory_message ("Interlace");
 303                          if pi.mem_size ^= 0 then
 304                               call generate_switch_memory_message ("Size");
 305                          if rsw_4.half (i) then
 306                               call generate_switch_memory_message ("Half/Full");
 307                     end;
 308                end;
 309 
 310                else if code = rcerr_addcpu_enable then do;
 311                     reason = TAGS (bin (switches (1)));     /* Get offending SCU tag. */
 312                end;
 313                if code ^= 0 then
 314                     call quit_if_rc_error (code);
 315 
 316                if message_is_called_for () then
 317                     call ioa_ ("^a is now running.", name);
 318           end;
 319           else do;                                          /* delete a CPU */
 320                call hphcs_$del_cpu (tag, code);
 321                if code ^= 0 then
 322                     call quit_if_rc_error (code);
 323                if message_is_called_for () then
 324                     call ioa_ ("Deleted ^a.", name);
 325           end;
 326           goto DONE;
 327 
 328 generate_switch_message:
 329      proc (aspect);
 330 
 331 dcl       aspect                 char (*) parameter;
 332 
 333           reason = reason || REASON_SEPARATOR;
 334           reason = reason || aspect;
 335           return;
 336 
 337 generate_switch_memory_message:
 338      entry (aspect);
 339 
 340           reason = reason || REASON_SEPARATOR;
 341           reason = reason || "MEM ";
 342           reason = reason || TAGS (i);
 343           reason = reason || " ";
 344           reason = reason || aspect;
 345 
 346      end generate_switch_message;
 347 ^L
 348 ACTION_LABEL (2):                                           /* SCU */
 349           tag = get_tag_from_name (name) - 1;
 350           name = "MEM " || rtrim (name);                    /* for better error messages */
 351           if add_del_sw = ADD then do;                      /* adding an SCU */
 352                call hphcs_$add_scu (tag, interlace, error_tag, code);
 353                if code ^= rcerr_addscu_size & code ^= rcerr_addscu_manual & code ^= 0 & code ^= rcerr_addscu_bigconfig
 354                     then
 355                     reason = TAGS (error_tag);
 356                if code ^= 0 then
 357                     call quit_if_rc_error (code);
 358                call print_scu_message;
 359           end;
 360           else do;                                          /* deleting an SCU */
 361                call hphcs_$del_scu (tag, interlace, code);
 362                if code ^= 0 then
 363                     call quit_if_rc_error (code);
 364                call print_scu_message;
 365           end;
 366           goto DONE;
 367 
 368 print_scu_message:
 369      proc;
 370 
 371           if message_is_called_for () then
 372                call ioa_ ("^[Added^;Removed^] SCU^[s ^s^a and ^a (interlaced)^; ^a^2s^] and ^[their^;its^] memory.",
 373                     add_del_sw = ADD, interlace, TAGS (tag), TAGS (tag - mod (tag, 2)), TAGS (tag + 1 - mod (tag, 2)),
 374                     interlace);
 375 
 376      end print_scu_message;
 377 ^L
 378 ACTION_LABEL (3):                                           /* pages */
 379           i = index (name, RANGE_SEPARATOR);
 380           if i = 0 then do;                                 /* only one page */
 381                first_frame = cv_integer_string_check_ (name, 8, code);
 382                if code ^= 0 then
 383                     call quit (code, name);
 384                n_frames = 1;
 385           end;
 386           else do;
 387                first_frame = cv_integer_string_check_ (substr (name, 1, i - 1), 8, code);
 388                if code ^= 0 then
 389                     call quit (code, substr (name, 1, i - 1));
 390                n_frames = cv_integer_string_check_ (substr (name, i + 1), 8, code);
 391                if code ^= 0 then
 392                     call quit (code, substr (name, i + 1));
 393                n_frames = n_frames - first_frame + 1;
 394           end;
 395           if add_del_sw = ADD then
 396                call hphcs_$add_main (first_frame, n_frames, code);
 397           else call hphcs_$del_main (first_frame, n_frames, code);
 398           if code ^= 0 then
 399                call quit_if_rc_error (code);
 400           if message_is_called_for () then
 401                call ioa_ ("^[Added^;Removed^] frame^[s ^o thru ^o^; ^o^s^].", add_del_sw = ADD, n_frames ^= 1,
 402                     first_frame, first_frame + n_frames - 1);
 403           goto DONE;
 404 ^L
 405 ACTION_LABEL (4):                                           /* device */
 406           call setup_io_config_ptrs;
 407           do device_idx = lbound (device_table.device_entry, 1) to hbound (device_table.device_entry, 1)
 408                while (device_table.device_entry (device_idx).name ^= name);
 409           end;
 410           if device_idx > hbound (device_table.device_entry, 1) then do;
 411                code = error_table_$io_not_defined;
 412                return;
 413           end;
 414           if add_del_sw = ADD then
 415                call add_device (device_idx, code);
 416           else call delete_device (device_idx, code);
 417           if code ^= 0 then
 418                call quit (code, "Device ^a.", name);
 419           if message_is_called_for () then
 420                call ioa_ ("^[Added^;Removed^] device ^a.", add_del_sw = ADD, name);
 421           goto DONE;
 422 
 423 add_device:
 424      proc (idx, code);
 425 
 426 dcl       idx                    fixed bin parameter;
 427 dcl       code                   fixed bin (35) parameter;
 428 
 429 dcl       desired_configured_setting
 430                                  bit (1) aligned;
 431 
 432           call rcp_sys_$add_device (device_table.device_entry (idx).name, code);
 433           desired_configured_setting = "1"b;
 434 add_del_device_common:
 435           if code ^= 0 then
 436                return;
 437           deadline = clock () + TEN_SECONDS;
 438           do while (clock () < deadline & device_table.device_entry (idx).configured ^= desired_configured_setting);
 439           end;
 440           if device_table.device_entry (idx).configured = desired_configured_setting then do;
 441                code = 0;
 442                call add_to_backout_list (idx, "device");
 443           end;
 444           else code = error_table_$action_not_performed;
 445 /**** RCP should do this work ****/
 446           if device_type (idx) = "fnp" & desired_configured_setting then
 447                call add_fnp (idx, code);
 448           return;
 449 
 450 delete_device:
 451      entry (idx, code);
 452 
 453           if device_type (idx) = "fnp" then do;
 454                call delete_fnp (idx, code);
 455                if code ^= 0 then
 456                     return;
 457           end;
 458           call rcp_sys_$delete_device (device_table.device_entry (idx).name, code);
 459           desired_configured_setting = "0"b;
 460           goto add_del_device_common;
 461 
 462      end add_device;
 463 
 464 /**** This work should more properly be done in RCP when it gets an FNP device type. ****/
 465 
 466 add_fnp:
 467      proc (idx, code);
 468 
 469 dcl       idx                    fixed bin parameter;
 470 dcl       code                   fixed bin (35) parameter;
 471 
 472 dcl       n_users                fixed bin;
 473 
 474 dcl       hphcs_$configure_fnp   entry (fixed bin, fixed bin (35));
 475 dcl       hphcs_$deconfigure_fnp entry (fixed bin, fixed bin (35));
 476 dcl       initiate_file_         entry (char (*), char (*), bit (*), ptr, fixed bin (24), fixed bin (35));
 477 dcl       multiplexer_mgr_$count_mpx_users
 478                                  entry (char (*), ptr, fixed bin, fixed bin (35));
 479 dcl       parse_fnp_name_        entry (char (*)) returns (fixed bin);
 480 
 481 dcl       error_table_$io_configured
 482                                  fixed bin (35) ext static;
 483 dcl       error_table_$io_not_available
 484                                  fixed bin (35) ext static;
 485 dcl       error_table_$io_not_configured
 486                                  fixed bin (35) ext static;
 487 
 488           call hphcs_$configure_fnp (parse_fnp_name_ (after (device_table.device_entry (idx).name, "fnp")), code);
 489           if code = error_table_$io_configured then
 490                code = 0;
 491           return;
 492 
 493 delete_fnp:
 494      entry (idx, code);
 495 
 496           if ^force_sw then do;
 497                call initiate_file_ (">sc1", "cdt", R_ACCESS, cdtp, (0), code);
 498                if code ^= 0 then
 499                     return;
 500                call multiplexer_mgr_$count_mpx_users (substr (device_table.device_entry (idx).name, 4), cdtp, n_users,
 501                     code);
 502                call terminate_file_ (cdtp, (0), TERM_FILE_TERM, (0));
 503                cdtp = null ();
 504                if (code = 0) & (n_users > 0) then do;
 505                     code = error_table_$io_not_available;
 506                     return;
 507                end;
 508           end;
 509           call hphcs_$deconfigure_fnp (parse_fnp_name_ (after (device_table.device_entry (idx).name, "fnp")), code);
 510           if code = error_table_$io_not_configured then
 511                code = 0;
 512           return;
 513 
 514 %include access_mode_values;
 515 
 516      end add_fnp;
 517 ^L
 518 ACTION_LABEL (5):                                           /* channels */
 519           call setup_io_config_ptrs;
 520           call canonicalize_channel_name (name);
 521           do channel_idx = lbound (channel_table.channel_entry, 1) to hbound (channel_table.channel_entry, 1)
 522                while (name ^= channel_table.channel_entry (channel_idx).name);
 523           end;
 524           if channel_idx > hbound (channel_table.channel_entry, 1) then
 525                call quit (error_table_$io_not_defined, "Channel ^a.", name);
 526           if add_del_sw = ADD then do;
 527                call add_channel (channel_idx, code);
 528                if code ^= 0 then
 529                     call quit (code, "Channel ^a.", name);
 530           end;
 531           else do;
 532                if delete_all_sw then do;
 533                     call delete_devices (channel_idx, code);
 534                     if code ^= 0 then
 535                          call quit (code, "Channel ^a.", name);
 536                end;
 537                call delete_channel (channel_idx, code);
 538                if code ^= 0 then
 539                     call quit (code, "Channel ^a.", name);
 540           end;
 541           if message_is_called_for () then
 542                call ioa_ ("^[Added^;Removed^] logical channel ^a.", add_del_sw = ADD,
 543                     channel_table.channel_entry (idx).name);
 544           goto DONE;
 545 
 546 add_channel:
 547      proc (idx, code);
 548 
 549 dcl       idx                    fixed bin parameter;
 550 dcl       code                   fixed bin (35) parameter;
 551 
 552 dcl       add_entry              bit (1) aligned;
 553 
 554           add_entry = "1"b;
 555           call hphcs_$add_channel (channel_table.channel_entry (idx).name, code);
 556           goto add_delete_channel_common;
 557 
 558 delete_channel:
 559      entry (idx, code);
 560 
 561           add_entry = "0"b;
 562           if delete_all_sw then do;
 563                call delete_devices (idx, code);
 564                if code ^= 0 then
 565                     return;
 566           end;
 567           call hphcs_$delete_channel (channel_table.channel_entry (idx).name, code);
 568 add_delete_channel_common:
 569           if code = 0 then do;
 570                call add_to_backout_list (idx, "channel");
 571                if add_all_sw then
 572                     call add_devices (idx, code);
 573           end;
 574           return;
 575 
 576      end add_channel;
 577 
 578 /**** Procedure to add all newly accessible devices after a channel is added ****/
 579 add_devices:
 580      proc (channel_idx, code);
 581 
 582 dcl       channel_idx            fixed bin parameter;
 583 dcl       code                   fixed bin (35) parameter;
 584 
 585 dcl       device_idx             fixed bin;
 586 
 587           do device_idx = lbound (device_table.device_entry, 1) to hbound (device_table.device_entry, 1);
 588                if newly_accessible (device_idx, channel_idx) then do;
 589                     call add_device (device_idx, code);
 590                     if code ^= 0 then do;
 591                          if add_all_sw then do;
 592                               call ssu_$print_message (sci_ptr, code, "Adding device ^a.",
 593                                    device_table.device_entry (device_idx).name);
 594                               code = 0;                     /* continue.. */
 595                          end;
 596                          else return;
 597                     end;
 598                     else if message_is_called_for () then
 599                          call ioa_ ("Added device ^a.", device_table.device_entry (device_idx).name);
 600                end;
 601           end;
 602 
 603 /* Funtion to determine whether a device d is being made accessible by the addition of channel c.
 604    For non-disks, this is true if no path other than channel c currently exists to device d.  For
 605    disks, we insist that two paths be available (to allow IOI and disk_control to peacefully coexist). */
 606 
 607 newly_accessible:
 608           proc (d, c) returns (bit (1) aligned);
 609 
 610 dcl       d                      fixed bin parameter;
 611 dcl       c                      fixed bin parameter;
 612 
 613 dcl       path_count             fixed bin;
 614 
 615                if device_table.device_entry (d).configured then
 616                     return ("0"b);                          /* it's previously accessible */
 617                path_count = available_paths (d);
 618                if device_type (d) = "dsk" then
 619                     return ((path_count = 2) & connected (c, d));
 620                else return ((path_count = 1) & connected (c, d));
 621 
 622           end newly_accessible;
 623 
 624      end add_devices;
 625 
 626 /**** Procedure to delete all devices which are only accessible via a given channel */
 627 delete_devices:
 628      proc (channel_idx, code);
 629 
 630 dcl       channel_idx            fixed bin parameter;
 631 dcl       code                   fixed bin (35) parameter;
 632 
 633 dcl       device_idx             fixed bin;
 634 
 635           code = 0;
 636           do device_idx = lbound (device_table.device_entry, 1) to hbound (device_table.device_entry, 1);
 637                if needs_this_channel (device_idx, channel_idx) then do;
 638                     call delete_device (device_idx, code);
 639                     if code ^= 0 then
 640                          return;
 641                     if message_is_called_for () then
 642                          call ioa_ ("Removed device ^a.", device_table.device_entry (device_idx).name);
 643                end;
 644           end;
 645 
 646 /* Function which decides if device d requires channel c.  The rules are the inverse of newly_accessible above. */
 647 
 648 needs_this_channel:
 649           proc (d, c) returns (bit (1) aligned);
 650 
 651 dcl       d                      fixed bin parameter;
 652 dcl       c                      fixed bin parameter;
 653 
 654 dcl       path_count             fixed bin;
 655 
 656                if ^device_table.device_entry (d).configured then
 657                     return ("0"b);
 658 
 659                path_count = available_paths (d);
 660                if device_type (d) = "dsk" then
 661                     return ((path_count = 2) & connected (c, d));
 662                else return ((path_count = 1) & connected (c, d));
 663 
 664           end needs_this_channel;
 665 
 666      end delete_devices;
 667 
 668 canonicalize_channel_name:
 669      proc (name);
 670 
 671 dcl       name                   char (*) parameter;
 672 
 673 canonicalize_iom_name:
 674      entry (name);
 675 
 676           name = translate (name, "ABCD", "abcd");
 677           return;
 678 
 679      end canonicalize_channel_name;
 680 
 681 base_channel:
 682      proc (idx) returns (bit (1) aligned);
 683 
 684 dcl       idx                    fixed bin parameter;
 685 
 686           return (channel_table.channel_entry (idx).base_channel_idx = idx);
 687 
 688      end base_channel;
 689 
 690 device_type:
 691      proc (d) returns (char (3));
 692 
 693 dcl       d                      fixed bin parameter;
 694 
 695           return (substr (device_table.device_entry (d).name, 1, 3));
 696 
 697      end device_type;
 698 
 699 /**** Function which counts the number of currently available channels to a given device ****/
 700 available_paths:
 701      proc (d) returns (fixed bin);
 702 
 703 dcl       d                      fixed bin parameter;       /* device index */
 704 
 705 dcl       c                      fixed bin;                 /* channel index */
 706 dcl       count                  fixed bin;
 707 
 708           count = 0;
 709           do c = lbound (channel_table.channel_entry, 1) to hbound (channel_table.channel_entry, 1);
 710                if channel_table.channel_entry (c).configured & connected (c, d) then
 711                     count = count + 1;
 712           end;
 713           return (count);
 714 
 715      end available_paths;
 716 
 717 connected:
 718      proc (c, d) returns (bit (1) aligned);
 719 
 720 dcl       c                      fixed bin parameter;
 721 dcl       d                      fixed bin parameter;
 722 
 723 dcl       i                      fixed bin;
 724 
 725           do i = lbound (null () -> device_entry_template.pchan_idx, 1)
 726                to hbound (null () -> device_entry_template.pchan_idx, 1)
 727                while (device_table.device_entry (d).pchan_idx (i) ^= 0);
 728                if channel_table.channel_entry (c).base_channel_idx = device_table.device_entry (d).pchan_idx (i) then
 729                     return ("1"b);
 730           end;
 731           return ("0"b);
 732 
 733      end connected;
 734 ^L
 735 ACTION_LABEL (6):                                           /* physical channels */
 736           call setup_io_config_ptrs;
 737           call canonicalize_channel_name (name);
 738           do channel_idx = lbound (channel_table.channel_entry, 1) to hbound (channel_table.channel_entry, 1)
 739                while (name ^= channel_table.channel_entry (channel_idx).name);
 740           end;
 741           if channel_idx > hbound (channel_table.channel_entry, 1) then
 742                call quit (error_table_$io_not_defined, "Channel ^a.", name);
 743           if ^base_channel (channel_idx) then
 744                call quit (error_table_$not_base_channel, "Channel ^a.", name);
 745 
 746           if add_del_sw = ADD then
 747                call add_physical_channel (channel_idx, code);
 748           else call delete_physical_channel (channel_idx, brief_sw, code);
 749           if code ^= 0 then
 750                call quit (code, "Physical channel ^a.", name);
 751           if ^brief_sw then
 752                call ioa_ ("^[Added^;Removed^] physical channel ^a.", add_del_sw = ADD, name);
 753           goto DONE;
 754 
 755 add_physical_channel:
 756      proc (pc_idx, code);
 757 
 758 dcl       pc_idx                 fixed bin parameter;
 759 dcl       code                   fixed bin (35) parameter;
 760 
 761 dcl       cx                     fixed bin;
 762 
 763           call add_channel (pc_idx, code);
 764           if code ^= 0 then
 765                return;
 766           do cx = lbound (channel_table.channel_entry, 1) to hbound (channel_table.channel_entry, 1);
 767                if (^base_channel (cx)) & (channel_table.channel_entry (cx).base_channel_idx = pc_idx) then
 768                     call add_channel (cx, (0));             /* we'll let errors get reported, but we'll keep going */
 769           end;
 770 
 771      end add_physical_channel;
 772 
 773 delete_physical_channel:
 774      proc (pc_idx, ignore_sw, code);
 775 
 776 dcl       pc_idx                 fixed bin parameter;
 777 dcl       ignore_sw              bit (1) aligned parameter;
 778 dcl       code                   fixed bin (35) parameter;
 779 
 780 dcl       cx                     fixed bin;
 781 
 782           do cx = lbound (channel_table.channel_entry, 1) to hbound (channel_table.channel_entry, 1);
 783                if (^base_channel (cx)) & (channel_table.channel_entry (cx).base_channel_idx = pc_idx)
 784                     & (channel_table.channel_entry (cx).configured) then
 785                     call delete_channel (cx, (0));          /* if one of these fails, so will the base */
 786           end;
 787           call delete_channel (pc_idx, code);
 788           if ignore_sw then
 789                code = 0;
 790 
 791      end delete_physical_channel;
 792 ^L
 793 ACTION_LABEL (7):                                           /* MPC */
 794           call setup_io_config_ptrs;
 795           do mpc_idx = lbound (controller_table.controller_entry, 1)
 796                to hbound (controller_table.controller_entry, 1)
 797                while (controller_table.controller_entry (mpc_idx).name ^= name);
 798           end;
 799           if mpc_idx > hbound (controller_table.controller_entry, 1) then
 800                call quit (error_table_$io_not_defined, "Controller ^a.", name);
 801 
 802           code = 0;
 803           do channel_idx = lbound (channel_table.channel_entry, 1) to hbound (channel_table.channel_entry, 1);
 804                if (base_channel (channel_idx)) & (channel_table.channel_entry (channel_idx).controller_idx = mpc_idx)
 805                then do;
 806                     if add_del_sw = ADD then
 807                          call add_physical_channel (channel_idx, code);
 808                     else if channel_table.channel_entry (channel_idx).configured then
 809                          call delete_physical_channel (channel_idx, brief_sw, code);
 810                     if code ^= 0 then
 811                          call quit (code, "Controller ^a.", name);
 812                end;
 813           end;
 814           if ^brief_sw then
 815                call ioa_ ("^[Added^;Removed^] MPC ^a.", add_del_sw = ADD, name);
 816           goto DONE;
 817 ^L
 818 ACTION_LABEL (8):                                           /* IOMs */
 819           call setup_io_config_ptrs;
 820           call canonicalize_iom_name (name);
 821           do iom_idx = lbound (iom_table.iom_entry, 1) to hbound (iom_table.iom_entry, 1)
 822                while (iom_table.iom_entry (iom_idx).name ^= name);
 823           end;
 824           if iom_idx > hbound (iom_table.iom_entry, 1) then
 825                call quit (error_table_$io_not_defined, "IOM ^a.", name);
 826 
 827           if add_del_sw = ADD then do;
 828                call add_iom (iom_idx, code);
 829                if code ^= 0 then
 830                     call quit (code, "IOM ^a.", name);
 831           end;
 832 
 833           if add_all_sw | delete_all_sw then
 834                do channel_idx = lbound (channel_table.channel_entry, 1) to hbound (channel_table.channel_entry, 1);
 835                if (base_channel (channel_idx)) & (iom_idx = channel_table.channel_entry (channel_idx).iom_idx) then do;
 836                     if add_del_sw = ADD then
 837                          call add_physical_channel (channel_idx, code);
 838                     else do;
 839                          call delete_physical_channel (channel_idx, brief_sw, code);
 840                          if code = error_table_$chnl_already_deleted then
 841                               code = 0;
 842                     end;
 843                     if code ^= 0 then
 844                          call quit (code, "Channel ^a.", channel_table.channel_entry (channel_idx).name);
 845                end;
 846           end;
 847 
 848           if add_del_sw ^= ADD then do;
 849                call delete_iom (iom_idx, code);
 850                if code ^= 0 then
 851                     call quit (code, "IOM ^a.", name);
 852           end;
 853 
 854           if message_is_called_for () then
 855                call ioa_ ("^[Added^;Removed^] IOM ^a.", add_del_sw = ADD, name);
 856           goto DONE;
 857 
 858 add_iom:
 859      proc (idx, code);
 860 
 861 dcl       idx                    fixed bin parameter;
 862 dcl       code                   fixed bin (35) parameter;
 863 
 864           call hphcs_$add_iom (get_tag_from_name ((iom_table.iom_entry (idx).name)), code);
 865           goto add_delete_iom_common;
 866 
 867 delete_iom:
 868      entry (idx, code);
 869 
 870           call hphcs_$delete_iom (get_tag_from_name ((iom_table.iom_entry (idx).name)), code);
 871 
 872 add_delete_iom_common:
 873           if code = 0 then
 874                call add_to_backout_list (idx, "iom");
 875           return;
 876 
 877      end add_iom;
 878 ^L
 879 ERROR_RETURN:
 880           call backout_work_so_far;
 881 DONE:
 882           call clean_up;
 883 
 884           return;
 885 
 886 clean_up: proc;
 887 
 888           if cdtp ^= null () then do;
 889                call terminate_file_ (cdtp, (0), TERM_FILE_TERM, (0));
 890                cdtp = null ();
 891           end;
 892           if area_ptr ^= null () then do;
 893                call ssu_$release_area (sci_ptr, area_ptr);
 894                area_ptr =null ();
 895           end;
 896           if standalone_invocation then do;
 897                call ssu_$destroy_invocation (sci_ptr);
 898                sci_ptr = null ();
 899           end;
 900      end clean_up;
 901 
 902 add_to_backout_list:
 903      proc (idx, type);
 904 
 905 dcl       idx                    fixed bin;
 906 dcl       type                   char (*) parameter;
 907 
 908           if backing_out then
 909                return;                                      /* avoid looping */
 910           allocate backout_item in (area) set (item_ptr);
 911           backout_item.idx = idx;
 912           backout_item.type = type;
 913           backout_item.next_item = backout_list_ptr;
 914           backout_list_ptr = item_ptr;
 915 
 916      end add_to_backout_list;
 917 
 918 backout_work_so_far:
 919      proc;
 920 
 921 dcl       name                   char (32);
 922 
 923           backing_out = "1"b;
 924           if add_del_sw = ADD then do;
 925                delete_all_sw = add_all_sw;
 926                add_all_sw = "0"b;
 927           end;
 928           else do;
 929                add_all_sw = delete_all_sw;
 930                delete_all_sw = "0"b;
 931           end;
 932           if backout_list_ptr = null () then
 933                return;
 934           if ^brief_sw then
 935                call ioa_ ("****    Restoring configuration.    ****");
 936           do while (backout_list_ptr ^= null ());
 937                item_ptr = backout_list_ptr;
 938                if backout_item.type = "iom" then do;
 939                     name = iom_table.iom_entry (backout_item.idx).name;
 940                     if add_del_sw = ADD then
 941                          call delete_iom (backout_item.idx, code);
 942                     else call add_iom (backout_item.idx, code);
 943                end;
 944                else if backout_item.type = "channel" then do;
 945                     name = channel_table.channel_entry (backout_item.idx).name;
 946                     if add_del_sw = ADD then
 947                          call delete_channel (backout_item.idx, code);
 948                     else call add_channel (backout_item.idx, code);
 949                end;
 950                else if backout_item.type = "device" then do;
 951                     name = device_table.device_entry (backout_item.idx).name;
 952                     if add_del_sw = ADD then
 953                          call delete_device (backout_item.idx, code);
 954                     else call add_device (backout_item.idx, code);
 955                end;
 956                if code ^= 0 then
 957                     call ssu_$print_message (sci_ptr, code, "Unable to back out ^[addition^;removal^] of ^a ^a.",
 958                          add_del_sw = ADD, backout_item.type, name);
 959                backout_list_ptr = backout_item.next_item;
 960           end;
 961 
 962      end backout_work_so_far;
 963 ^L
 964 get_tag_from_name:
 965      proc (name) returns (fixed bin (3));
 966 
 967 dcl       name                   char (*) parameter;
 968 
 969           return (index (TAGS_STRING, translate (rtrim (name), TAGS_STRING, TAGS_STRING_UPPER_CASE)));
 970 
 971      end get_tag_from_name;
 972 
 973 setup_io_config_ptrs:
 974      proc;
 975 
 976           io_config_data_ptr = addr (io_config_data$);
 977           if io_config_data.version ^= IO_CONFIG_DATA_VERSION_1 then
 978                call quit (error_table_$unimplemented_version, "io_config_data");
 979           io_config_device_table_ptr = ptr (io_config_data_ptr, io_config_data.device_table_offset);
 980           if device_table.version ^= IO_CONFIG_DEVICE_TABLE_VERSION_1 then
 981                call quit (error_table_$unimplemented_version, "io_config_data.device_table");
 982 
 983           io_config_channel_table_ptr = ptr (io_config_data_ptr, io_config_data.channel_table_offset);
 984           if channel_table.version ^= IO_CONFIG_CHANNEL_TABLE_VERSION_1 then
 985                call quit (error_table_$unimplemented_version, "io_config_data.channel_table");
 986 
 987           io_config_controller_table_ptr = ptr (io_config_data_ptr, io_config_data.controller_table_offset);
 988           if controller_table.version ^= IO_CONFIG_CONTROLLER_TABLE_VERSION_1 then
 989                call quit (error_table_$unimplemented_version, "io_config_data.controller_table");
 990 
 991           io_config_iom_table_ptr = ptr (io_config_data_ptr, io_config_data.iom_table_offset);
 992           if iom_table.version ^= IO_CONFIG_IOM_TABLE_VERSION_1 then
 993                call quit (error_table_$unimplemented_version, "io_config_data.iom_table");
 994 
 995      end setup_io_config_ptrs;
 996 ^L
 997 abort_entry:
 998      proc;
 999 
1000           goto ERROR_RETURN;
1001 
1002      end abort_entry;
1003 
1004 quit_with_usage:
1005      proc (code);
1006 
1007 dcl       code                   fixed bin (35) parameter;
1008 
1009           call quit (code, "Usage: reconfigure op type name {-control_args}");
1010 
1011      end quit_with_usage;
1012 
1013 quit:
1014      proc options (variable);
1015 
1016 dcl       arg_list_ptr           ptr;
1017 dcl       code                   fixed bin (35);
1018 dcl       message                char (128);
1019 
1020           call extract_code ("quit");
1021           if code = error_table_$io_not_configured then
1022                call ioa_ ("Attempt to delete a device which is already deleted. ^a", name);
1023           else do;
1024                call ioa_$general_rs (arg_list_ptr, 2, 3, message, (0), "1"b, "0"b);
1025                call ssu_$abort_line (sci_ptr, code, message);
1026           end;
1027           goto ERROR_RETURN;
1028 
1029 extract_code:
1030           proc (name);
1031 
1032 dcl       name                   char (*) parameter;
1033 dcl       fb35                   fixed bin (35) based;
1034 
1035                call cu_$arg_list_ptr (arg_list_ptr);
1036                if arg_list_ptr -> arg_list.arg_count < 1 then
1037                     call quit (error_table_$noarg, "Internal programming error -- ^a called with no arguments.", name);
1038                code = arg_list_ptr -> arg_list.arg_ptrs (1) -> fb35;
1039 
1040 %include arg_list;
1041 
1042           end extract_code;
1043 
1044      end quit;
1045 
1046 quit_if_rc_error:
1047      proc (code);
1048 
1049 dcl       code                   fixed bin (35) parameter;
1050 
1051 dcl       row_idx                fixed bin;
1052 
1053 /**** rc codes are strange.   If the code is less than twice hbound (rc_messages$rc_messages, 2) but greater than
1054       hbound (...), it's a generic error, and its text is gotten from row 0 of the array.  If it's in
1055       the range hbound (...), then we use the type of thing we're reconfiguring as a
1056       row index to get the text.  If it's non-zero and out of both ranges, we assume it's a standard status code. ****/
1057 
1058           if code = 0 then
1059                return;
1060 
1061           if code > 2 * hbound (rc_messages$rc_messages, 2) then
1062                call quit (code, name);
1063           else if code > hbound (rc_messages$rc_messages, 2) then
1064                call quit (0, rc_messages$rc_messages (0, code - hbound (rc_messages$rc_messages, 2)), name, reason);
1065           else do;
1066                if add_del_sw = ADD then
1067                     row_idx = 2 * action - 1;
1068                else row_idx = 2 * action;
1069                call quit (0, rc_messages$rc_messages (row_idx, code), name, reason);
1070           end;
1071 
1072      end quit_if_rc_error;
1073 
1074 /* Procedure which decides if a message should be printed.  It honors the -brief switch, and tries to avoid
1075    printing if it knows the message (or one like it) will be printed by ring 0. */
1076 
1077 message_is_called_for:
1078      proc () returns (bit (1) aligned);
1079 
1080           if brief_sw then
1081                return ("0"b);
1082           else if standalone_invocation then
1083                return ("1"b);
1084           else return (^sc_subsystem_info.the_system_console);
1085 
1086      end message_is_called_for;
1087 ^L
1088 /**** The following code should be removed and undocumented as soon as we can find and fix all instances
1089       of problems which would leave the lock unlocked.  It's really gross. ****/
1090 
1091 reconfigure$force_unlock:
1092      entry;
1093 
1094 dcl       1 auto_rci             aligned like rci;
1095 
1096 dcl       hphcs_$rc_force_unlock entry;
1097 dcl       hphcs_$reconfig_info   entry (ptr, fixed bin (35));
1098 
1099           standalone_invocation = "1"b;
1100           area_ptr, backout_list_ptr, cdtp, sci_ptr,
1101                sc_subsystem_info_ptr = null ();
1102 
1103           on cleanup begin;
1104                call backout_work_so_far;
1105                call clean_up;
1106           end;
1107 
1108           backing_out = "0"b;
1109           call ssu_$standalone_invocation (sci_ptr, ME, RCF_V1, null (), abort_entry, code);
1110           if code ^= 0 then do;
1111                call com_err_ (code, ME, "Could not create ssu_ invocation.");
1112                return;
1113           end;
1114           call ssu_$arg_count (sci_ptr, arg_count);
1115           if arg_count ^= 0 then
1116                call quit (error_table_$too_many_args, "");
1117 
1118           call hphcs_$reconfig_info (addr (auto_rci), code);
1119           if code = 0 then
1120                call ssu_$print_message (sci_ptr, 0, "Reconfiguration data not locked.");
1121           else call ssu_$print_message (sci_ptr, 0, "Reconfiguration data locked by ^a", auto_rci.locker_group_id);
1122 
1123           call hphcs_$rc_force_unlock;
1124 
1125           goto DONE;
1126 
1127 %include rci;
1128 %include scs;
1129 ^L
1130 %include io_config_data;
1131 %page;
1132 %include rcerr;
1133 %page;
1134 %include rsw;
1135 %page;
1136 %include sc_subsystem_info_;
1137 %page;
1138 %include terminate_file;
1139 
1140      end reconfigure;