



		    countervalidate_label_.pl1      12/11/99  1838.1r w 12/11/99  1808.4       38592



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1982 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */



/****^  HISTORY COMMENTS:
  1) change(99-06-23,Haggett):
     Y2K
                                                   END HISTORY COMMENTS */

countervalidate_label_: proc (a_labelp, a_valid_reg, a_valid_copy, a_ss_pack);

/* Countervalidator of labels for mdxhdx/rcp

   Bernard Greenberg 12/1/76 */
/* Modified 84-09-04 by EJ Sharpe - rename hdx refs to volume_registration_mgr_ */
/* Modified 84-10-29 by EJ Sharpe - change date_time_ refs to date_time_$format */

dcl  a_labelp ptr;
dcl  a_valid_reg bit (1);				/* 1 => instance of valid registered volume */
dcl  a_valid_copy bit (1);				/* 1 => valid and registered, but mtd copy exists */
dcl  a_ss_pack bit (1);				/* 1 => this is a storage system pack. */

dcl  volume_registration_mgr_$pvname_info entry (char (*), bit (36) aligned, char (*), bit (36) aligned, fixed bin, fixed bin (35));
dcl  mdx$check_lv entry (char (*), fixed bin (35));
dcl  code fixed bin (35);
dcl (lvname, pvname) char (32);
dcl addr		    builtin;

	labelp = a_labelp;

	if label.Multics ^= Multics_ID_String then do;	/* Not even ss pack */
	     a_valid_reg, a_valid_copy, a_ss_pack = "0"b;
	     return;
	end;

	pvname = label.pv_name;

	a_ss_pack = "1"b;				/* Is surely a ss pack */

	call volume_registration_mgr_$pvname_info (pvname, pvid, lvname, lvid, (0), code);
	if code ^= 0 then do;			/* No such volume */
	     a_valid_reg, a_valid_copy = "0"b;
	     return;
	end;

	if pvid = label.pvid & lvid = label.lvid & lvname = label.lv_name then do; /* It's real */
	     a_valid_reg = "1"b;
	     call mdx$check_lv (lvname, code);
	     a_valid_copy = (code = 0);
	end;
	else a_valid_reg, a_valid_copy = "0"b;
	return;

/* ----------------------------------------------------------- */

query:	entry (a_labelp, a_pvname, a_progname, a_drivename) returns (bit (1));

dcl  a_pvname char (32);
dcl (a_progname, a_drivename) char (*);
dcl (valid_reg, valid_copy, ss_pack) bit (1);
dcl  command_query_ entry options (variable);
dcl  answer char (10) varying;
dcl  date_time_$format entry (char(*), fixed bin(71), char(*), char(*)) returns(char(250) var);
dcl  chtime char (24);
dcl  story char (50);


dcl 1 query_info aligned,
    2 version fixed bin,
    2 yes_or_no_sw bit (1) unal,
    2 suppress_name_sw bit (1) unal,
    2 code fixed bin (35),
    2 query_code fixed bin (35);

dcl  initializer_mdc_$countervalidate_label_ entry (ptr, bit (1), bit (1), bit (1));


	labelp = a_labelp;
	pvname = a_pvname;

	call initializer_mdc_$countervalidate_label_ (labelp, valid_reg, valid_copy, ss_pack);
						/* Recursively get dope in other ring */
	if ^ss_pack then return ("1"b);		/* No problem here! */

	if ^valid_reg then story = "an unregistered pack named";
	else if pvname = label.pv_name
	then if valid_copy then story = "a copy of pv";
	     else story = "an earlier instance of pv";
	else if valid_copy then story = "a copy of Multics Storage System Volume";
	else story = "Multics Storage System Volume";
	if valid_reg & ^(^valid_copy & ^(pvname = label.pv_name))
	then chtime = date_time_$format ("^9999yc-^my-^dm_^Hd:^MH", label.time_map_updated, "", "");
	else chtime = "";

	query_info.version = 2;
	query_info.yes_or_no_sw = "1"b;
	query_info.suppress_name_sw = "0"b;
	query_info.code = 0;
	query_info.query_code = 0;

	call command_query_ (addr (query_info), answer, a_progname,
	     "Volume on ^a is ^a ""^a""^[.^/^2s^;,^/^[which was never used^s^;last used ^a^]. ^]Do you wish to overwrite it?",
	     a_drivename, story, label.pv_name, chtime = "", label.time_map_updated = 0, chtime);
	return (answer = "yes");
	%include fs_vol_label;

     end;




		    disk_rebuild_caller.pl1         11/11/89  1102.4r w 11/11/89  0802.8       61380



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1984 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */


/****^  HISTORY COMMENTS:
  1) change(86-01-16,Fawcett), approve(86-04-11,MCR7383),
     audit(86-05-14,LJAdams), install(86-07-17,MR12.0-1097):
     Add support for subvolumes, and 512_WORD_IO, 3380 and 3390.
                                                   END HISTORY COMMENTS */


disk_rebuild_caller: proc (a_dtp, apvtx1, apvtx2, opt_bits);

/* Written by Kobziar from init_disk_pack_. */
/* Reworked for fill_vol_extents_, BSG 08/31/76 */
/* Modified for countervalidate_label_ BSG 12/1/76 */
/* Modified for to allow rebuild to larger disk, 7/23/81 M.R. Jordan */
/* Modified for new volume format, and to allow reduction in number
          of VTOCEs, March 1982, J. Bongiovanni */
/* Modified '83 KPL for fix to error message about rebuild */

dcl  opt_bits bit (36) aligned,
    (pvtx1, pvtx2) fixed bin,				/* Physical Volume Table Indexes */
     tpsp fixed bin,				/* total partition space */
     a_dtp ptr,
    (apvtx1, apvtx2) fixed bin,			/* drive args */
    (dev_type1, dev_type2) fixed bin,			/* drive types index */
     code fixed bin (35);				/* error code */



dcl  num_pages fixed bin,				/* Number of records in paging area on this volume */
     baseadd fixed bin,
     i fixed bin,
     interlace fixed bin init (2),			/* use alternate address assignment in copying */
     old_n_vtoce fixed bin,
     n_vtoce fixed bin;				/* number of VTOC entries in VTOC */

dcl  buffer (512) fixed bin (71);

dcl  (addr, clock) builtin;

dcl  com_err_ entry options (variable);
dcl  countervalidate_label_$query entry (ptr, char (*), char (*), char (*)) returns (bit (1));
dcl     fill_vol_extents_	 entry (ptr, fixed bin, fixed bin, fixed bin, fixed bin, bit (1) aligned, fixed bin (17), fixed bin (35));
dcl  initializer_gate_$disk_rebuild entry (fixed bin, fixed bin, ptr, bit (36) aligned, fixed bin, fixed bin (35));
dcl  initializer_gate_$read_disk entry (fixed bin, fixed bin, ptr, fixed bin (35));
dcl  ioa_ entry options (variable);
%page;
	labelp = addr (buffer);

	dtp = a_dtp;
	pvtx1 = apvtx1;
	pvtx2 = apvtx2;
	dev_type1 = dt.array (pvtx1).device_type;
	dev_type2 = dt.array (pvtx2).device_type;

	call ioa_ ("rebuild_disk: volume ^a from ^a onto ^a", dt.array (pvtx1).pvname,
	     dt.array (pvtx1).drive_name, dt.array (pvtx2).drive_name);

	call initializer_gate_$read_disk (pvtx2, LABEL_ADDR, labelp, code);
	if code ^= 0 then go to ERR;

	if ^countervalidate_label_$query (labelp, (dt.array (pvtx1).pvname), "disk_rebuild",
	(dt.array (pvtx2).drive_name)) then return;

	call initializer_gate_$read_disk (pvtx1, LABEL_ADDR, labelp, code);
	if code ^= 0 then go to ERR;
	if ^dt.array (pvtx1).used			/* Must be in use for rin-0 to work */
	     then do;
	     call ioa_ ("rebuild_disk: Volume ^a must be mounted before rebuilding", dt.array (pvtx1).drive_name);
	     return;
	end;
	

	label.time_map_updated, label.time_unmounted, label.time_salvaged = clock ();
	label.shutdown_state = 0;			/* If you believe this... */
	label.esd_state = 0;

	interlace = 2;				/* Default, triggers f_v_e that this is rebuild */


	tpsp = 0;

	call ioa_ ("^/ Current disk parameters:^/");

	do i = 1 to label.nparts;
	     call ioa_ ("part ^a ^d ^d", label.parts (i).part, label.parts (i).frec, label.parts (i).nrec);
	     tpsp = tpsp + label.parts (i).nrec;
	end;

	old_n_vtoce = (label.vtoc_size - VTOC_ORIGIN) * VTOCES_PER_RECORD (dev_type1);
	call ioa_ ("Vtoc size is ^d, ave. seg length = ^.3f",
	     label.vtoc_size, ((label.vol_size - tpsp - VTOC_ORIGIN) / (label.vtoc_size - VTOC_ORIGIN)) / VTOCES_PER_RECORD (dev_type1));
	call ioa_ ("Current number of VTOCEs = ^d", old_n_vtoce);
	call ioa_ ("");


	call ioa_ ("^/New starting parameters (no partitions defined):");

	if dt.array (pvtx2).is_sub_vol then do;
	      dtep = addr (dt.array (pvtx2));
	      label.vol_size = rec_per_sv (dte.device_type);
	      label.number_of_sv = dte.num_of_sv;
	      label.this_sv = dte.sv_num;
	      label.sub_vol_name = valid_sv_array (dte.sv_num);
	end;
	else do;
	      label.vol_size = rec_per_dev (dt.array (pvtx2).device_type);
	      label.number_of_sv = 0;
	      label.this_sv = 0;
          end;
comloop:	call fill_vol_extents_ (labelp, interlace, n_vtoce, baseadd, num_pages, "0"b, (dev_type2), code);
	if code ^= 0 then return;

	call initializer_gate_$disk_rebuild (pvtx1, pvtx2, labelp, opt_bits, interlace, code);
	return;

ERR:	call com_err_ (code, "rebuild_disk", "Error in rebuild of volume ^a.", dt.array (pvtx1).pvname);
	return;

%page;  %include disk_pack;
%page;  %include disk_table;
%page;  %include fs_vol_label;
%page;  %include fs_dev_types;

/* BEGIN MESSAGE DOCUMENTATION

   Message:
   rebuild_disk: volume PVNAME from DSKA_XX onto DSKA_YY
   .br
   Current disk parameters:
   .br
   part NAME FREC NREC
   .br
   Vtoc size is XX, ave. seg length = Y.ZZZ
   .br
   Current number of VTOCEs = XX

   S:	$initializer_io

   T:	$response

   M:	This is the response to the operator command "rebuild_disk" issued in ring 1.

   A:	$ignore


   Message:
   rebuild_disk: Volume PVNAME must be mounted before rebuilding

   S:	$initializer_io

   T:	$response

   M:	The rebuild_disk command was invoked on an mounted volume.
   Only volumes which are mounted may be rebuilt.
   No action was taken.

   A:	Salvage the volume first,
   and then reissue the rebuild_disk command.


   Message:
   New starting parameters (no partitions defined):

   S:	$initializer_io

   T:	$response

   M:	This message requests the input of new disk parameters.

   A:	Input the new disk parameters. Use the "end" request when done.


   Message:
   Cannot shrink VTOC from XX to YY VTOCEs. Respecify parameters.

   S:	$initializer_io

   T:	$response

   M:	An attempt has been made to shrink the VTOC, which is illegal.

   A:	Respecify the parameters.


   Message:
   rebuild_disk: Error XX in rebuild of volume PVNAME.

   S:	$initializer_io

   T:	$response

   M:	An error has occurred trying to rebuild the disk.
   The new copy should not be used.

   A:	If the problem can be repaired,
   repair it and try again.
   $inform



   END MESSAGE DOCUMENTATION */

     end disk_rebuild_caller;




		    disk_table_.pl1                 11/11/89  1102.4rew 11/11/89  0800.0      417762



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1982 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */

/****^  HISTORY COMMENTS:
  1) change(86-06-05,GJohnson), approve(86-06-05,MCR7387),
     audit(86-06-10,Martinson), install(86-07-17,MR12.0-1091):
     Correct error message documentation.
  2) change(86-06-30,Fawcett), approve(86-06-30,MCR7383),
     audit(86-06-30,LJAdams), install(86-07-17,MR12.0-1097):
     Add support for subvolumes, 3380 and 3390.
  3) change(88-04-18,Farley), approve(88-05-26,MCR7880),
     audit(88-06-07,Parisek), install(88-07-05,MR12.2-1052):
     Corrected problem in dry_run_mhv execution by removing the zeroing of the
     return error code in MHV, which was making the dry run appear to succeed.
     Also corrected several coding violations.
                                                   END HISTORY COMMENTS */

/* format: style2,^inddcls,indcomtxt */

disk_table_:
     proc;

/* DISK_TABLE_ - manage the system's Disk Table.

   This program is called by system_startup_ and sc_command_
   to manage a table parallel to the PVT
   which contains the long names of volumes
   and various other management information.

   Atually this program is just message typing and arg counting.. see mdx for the real stuff.

   This program refers to "hv" and "hvol" which mean Hierarchy Volume.
   The new term is "Logical Volume", which means the same thing.

   See MTB-213 by Bernie Greenberg for the design.

   By Tom VanVleck, and Bernie Greenberg.
   Modified April 1976 by Greenberg for demounter, parallel drive assignment, new salv interface (28-4).
   Modified September 1976 by Greenberg for auto mhvs by lv_request, init_vol -no_reg.
   Modified December 1976 by Greenberg for dte.demounted, mhv drive selector recoded.
   Modified March 1977 by Greenberg for init_vol -copy, ss_io_reconfig.
   Modified February 1977 by Greenberg for init_vol -copy.
   Modified July 1977 by T. Casey to add set_lv_pdir_bit entry.
   Modified August 1981 by J. Bongiovanni to improve synchronization of the
   various copies of the disk table
   Modified December 1981 by J. Bongiovanni to allow mounting LVEs on mhv dry run
   Modified April 1982 by J. Bongiovanni for better init_vol argument parsing
   Modified 831122 by E. A. Ranzenbach for set_vacate_pdirs_bit entry.
   Modified 84-08-29 by EJ Sharpe for auto registration of RLV PVs during cold boot
   Modified 84-10-30 by EJ Sharpe for some minor fixes
   Modified 85-01-31 by Keith Loepere for real error codes.
   Modified 1985-04-02, BIM: don't suggest drives for non-demountable
   disks. Report missing root physical volumes at accept_rlv time.
   (Note that those messages will be repeated each time that
   system_startup_ calls $accept_rlv.)
   Modified 04/85 by RAF to support subvolumes.
*/


dcl     (i, j)		 fixed bin,
        demount_problem	 bit (1),
        took		 bit (1),
        (lpv, llv, bfsw, ldrive)
			 bit (1),			/* for list entry */
        copy_sw		 bit (1) aligned,
        rlv_sw		 bit (1) aligned,
        fb71		 fixed bin (71),
        ap		 ptr,
        al		 fixed bin,
        bchr		 char (al) based (ap),
        pvap		 ptr,
        npv		 fixed bin,
        match_dtx		 fixed bin,		/* Used in dt scan */
        trial		 fixed bin,		/* 1ce for no *offs, 2 for yes */
        mhvtest_lvx		 fixed bin,		/* for referencecounting vols */
        tpvtx		 fixed bin,		/* for mhv error drive */
        last_sv		 fixed bin,
        pvname		 char (32),
        pvname1		 char (32),
        lvname		 char (32),
        drive_arg		 char (8) aligned,
        publicstr		 char (10),
        drivea		 char (2),
        drive_id		 char (8),
        arg		 char (64),
        arg_list_ptr	 ptr,
        ignore_num		 fixed bin,
        ignore_ec		 fixed bin (35),
        ec		 fixed bin (35);

dcl     (a_pvname, a_copydrive, a_drive, a_lvname)
			 char (*) parm;

dcl     new		 bit (1);			/* TRUE if made new disk_table */
dcl     sdtp		 ptr static init (null);
dcl     cu_$level_get	 entry (fixed bin);
dcl     cu_$arg_ptr		 entry (fixed bin, ptr, fixed bin, fixed bin (35));
dcl     cu_$arg_list_ptr	 entry (ptr);
dcl     level		 fixed bin;

dcl     1 pva		 (100) based (pvap) aligned,
	2 pvname		 char (32),
	2 device_type	 fixed bin,
	2 pad		 fixed bin;

dcl     1 pvinfo		 aligned like pv_registration;

dcl     (
        ARG1		 fixed bin init (1),
        ARG2		 fixed bin init (2),
        STOP_MOUNT		 fixed bin init (1),
        START_DEMOUNT	 fixed bin init (2),
        FINISH_DEMOUNT	 fixed bin init (3)
        )			 internal static options (constant);

dcl     (
        ioa_,
        ioa_$rsnnl
        )			 entry options (variable);
dcl     com_err_		 entry options (variable);
dcl     hcs_$make_seg	 entry (char (*), char (*), char (*), fixed bin (5), ptr, fixed bin (35));
dcl     disk_table_$accept_rlv entry (fixed bin (35));
dcl     lv_request_$dhv	 entry (char (*));
dcl     lv_request_$mhv_complete
			 entry (fixed bin, ptr);
dcl     disk_rebuild_caller	 entry (ptr, fixed bin, fixed bin, bit (36) aligned);
dcl     initializer_mdc_$rlvolcheck
			 entry (fixed bin, fixed bin, bit (36) aligned, fixed bin (35));
dcl     initializer_mdc_$init	 entry (ptr, bit (1), fixed bin (35));
dcl     initializer_mdc_$get_lv_pvinfo
			 entry (char (*), ptr, fixed bin, fixed bin, fixed bin (35));
dcl     initializer_mdc_$mhv	 entry (fixed bin, fixed bin, fixed bin (35));
dcl     initializer_mdc_$demount_lv
			 entry (fixed bin, fixed bin, fixed bin (35));
dcl     initializer_mdc_$demount_pv
			 entry (fixed bin, fixed bin (35));
dcl     initializer_mdc_$take	 entry (char (*), fixed bin, bit (1), fixed bin (35));
dcl     initializer_mdc_$assert
			 entry (char (*), fixed bin, fixed bin (35));
dcl     initializer_mdc_$volsalv
			 entry (char (*), fixed bin, bit (36) aligned, fixed bin (35));
dcl     initializer_mdc_$forget
			 entry (fixed bin, fixed bin (35));
dcl     initializer_mdc_$set_lv_pdir_bit
			 entry (char (*), bit (1) aligned, fixed bin (35));
dcl     initializer_mdc_$set_vacate_pdirs_bit
			 entry (char (*), bit (1) aligned, fixed bin (35));
dcl     initializer_mdc_$ss_io_reconfig
			 entry (fixed bin, bit (1) aligned, fixed bin (35));
dcl     init_disk_pack_	 entry (ptr, ptr, fixed bin, ptr, fixed bin (35));
dcl     init_disk_pack_$parse_args
			 entry (ptr, char (*), char (*), bit (1) aligned, bit (1) aligned, fixed bin (35));
dcl     initializer_mdc_$read_disk_table
			 entry (ptr, fixed bin (35));
dcl     volume_registration_mgr_$add_pvr
			 entry (char (*), ptr, fixed bin (35));
dcl     disk_table_$accept_all entry (fixed bin (35));
dcl     mdx$reregister	 entry (char (*), fixed bin, fixed bin (35));

dcl     (addr, clock, length, null, rtrim, substr, unspec, char, verify)
			 builtin;

dcl     error_table_$unregistered_volume
			 fixed bin (35) ext;
dcl     error_table_$action_not_performed
			 fixed bin (35) ext;

/* ======================================================== */

init:
     entry (rc);					/* called by system_startup_ */

dcl     rc		 fixed bin (35);		/* errcode */
dcl     rce		 bit (1);			/* sw for rc entries */

	rce = "1"b;
	call initializer_mdc_$init (sdtp, new, rc);
	if new
	then call com_err_ (rc, "disk_table_", "New disk_table created");
	else if rc ^= 0
	then call com_err_ (rc, "disk_table_", "");

	return;

/* -------------------------------------------------------- */

accept_all:
     entry (rc);

	rce = "1"b;
	rc = 0;

	call copy_disk_table ();
	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if ^dte.used
	     then /* Take only unused volumes */
		if dte.storage_system & ^dte.deleted
		then if dte.known | dte.pre_accepted
		     then do;
			     call initializer_mdc_$take ((dte.pvname), i, took, ec);
			     if ec ^= 0
			     then call print_error;
			     if ec ^= 0
			     then rc = ec;		/* Remember if any screwed up */
			     else if took & dte.lvx ^= 1
			     then call announce_mount ((dte.lvx));
			end;
	end;
	return;

/* -------------------------------------------------------- */

general_mhv:
     entry (rc);					/* Entry to induce a general mhv, and report results */

	rce = "1"b;
	call mhv ("-all");				/* start the mhvs */
	call copy_disk_table ();
	do i = 1 to dt.n_lv_entries;			/* Scan the lve array */
	     lvep = addr (dt.lv_array (i));
	     if lve.used & ^lve.hv_mounted & ^lve.demounted_only
	     then do;
		     rc = error_table_$action_not_performed;
		     call com_err_ (0, "disk_table_", "Some volumes are incomplete. Complete or demount them.");
		     return;
		end;
	end;
	rc = 0;
	return;


accept_rlv:
     entry (rc);

/**** NOTE: this procedure does not do the "find a free drive and
      request a mount" thing that the normal "alv" command does.
      Operators must always tell US where root LV pv's are to be
      found. Could/should this be changed? --BIM */

	rce = "1"b;
	rc = 0;
	call copy_disk_table ();
	call initializer_mdc_$mhv ((dt.array (dt.rpvx).lvx), tpvtx, rc);
	ec = rc;
	if rc ^= 0
	then do;
		if tpvtx > 0
		then do;
			dtep = addr (dt.array (tpvtx));
			call print_error;		/* Still return the code and prevent exit to ring 4 */
		     end;
		else call list_missing_rlv_pvs;	/* whether code is 1 (missing vol) or something else, the call to get_lv_pvinfo will detect it. */
						/* our caller just tests the code for 0 */
		rc = 1;
	     end;
	return;

list_missing_rlv_pvs:
     procedure;

	call copy_disk_table ();
	call initializer_mdc_$get_lv_pvinfo ("root", pvap, npv, ignore_num, ec);
						/* Read the registry */
	if ec ^= 0
	then do;					/* Impossible! */
		call com_err_ (ec, "disk_table_",
		     "Cannot get volume registration info for the root logical volume.^/ Shutdown and reboot with ""boot nolv""."
		     );
		return;
	     end;

	call ioa_ ("The root logical volume is incomplete.");
	call MHV ("root", "0"b, 0);			/* Find some drives, print some messages */
	return;

     end list_missing_rlv_pvs;


accept:
     entry;

	rce = "0"b;
	call cu_$arg_ptr (ARG1, ap, al, ec);
	if ec ^= 0
	then go to gruz;
	if bchr = "-all" | bchr = "-a"
	then do;
		call disk_table_$accept_all (ignore_ec);
		return;
	     end;
	pvname = bchr;
	call cu_$arg_ptr (ARG2, ap, al, ec);
	if ec ^= 0
	then do;
gruz:
		call com_err_ (ec, "disk_table_", "");
ret:
		if rce
		then rc = ec;
		return;
	     end;
	call parse_drive_id (bchr);
	call verify_drive;				/* check drive for free */

	call initializer_mdc_$take (pvname, i, took, ec);
	call copy_disk_table ();
	if ec ^= 0
	then call print_error;
	else if took
	then call announce_mount ((dte.lvx));
	else if dt.array (i).used
	then if dt.array (i).lvx ^= 1
	     then call ioa_ ("added pv ^a to mounted lv ^a.", pvname, dt.lv_array (dt.array (i).lvx).lvname);
	return;



mount_hvol:
mhv:
     entry options (variable);

	rce = "0"b;
	call cu_$arg_ptr (ARG1, ap, al, ec);
	if ec ^= 0
	then go to gruz;
	call copy_disk_table ();
	if bchr = "-all" | bchr = "-a"
	then do;
		do j = 1 to dt.n_lv_entries;
		     lvep = addr (dt.lv_array (j));
		     if lve.used & ^lve.hv_mounted & ^lve.demounted_only
		     then do;
			     call mhv ((lve.lvname));
			     call copy_disk_table ();
			end;
		end;
		return;
	     end;
	lvname = bchr;
	j = 0;					/* -1 suppresses lve creation */
	call initializer_mdc_$get_lv_pvinfo (lvname, pvap, npv, j, ec);
	if ec ^= 0
	then do;
		call com_err_ (ec, "disk_table_", "^a", lvname);
		return;
	     end;
	call copy_disk_table ();
	lvep = addr (dt.lv_array (j));
	if lve.hv_mounted
	then do;
		call com_err_ (0, "disk_table_", "lv ^a already mounted", lvname);
		return;
	     end;
	else if lve.demounting
	then do;
		call com_err_ (0, "disk_table_", "lv ^a cannot be mounted, in process of demount.", lvname);
		return;
	     end;
	call initializer_mdc_$mhv (j, tpvtx, ec);
	call copy_disk_table ();
	if ec = 0
	then do;
		call announce_mount (j);
		return;
	     end;
	else if tpvtx > 0
	then do;
		dtep = addr (dt.array (tpvtx));
		call print_error;

	     end;

	call MHV (lvname, "0"b, 0);			/* test is off */
	return;

/* --------------------------------------------------------- */

dry_run_mhv:
     entry (a_lvname, rc);

	lvname = a_lvname;
	rce = "1"b;
	j = 0;
	call initializer_mdc_$get_lv_pvinfo (lvname, pvap, npv, j, ec);
	if ec ^= 0
	then do;
		rc = ec;				/* He'll get tired after a while */
		return;
	     end;

	rc = 0;					/* show initial success */

/* do NOT copy disk table -- lv_request_ is munging current copy for test purposes */

	dtp = sdtp;

	do i = 1 to dt.n_lv_entries while (dt.lv_array (i).lvname ^= lvname);
	end;
	if i > dt.n_lv_entries
	then do;					/* Put phony one in there */
		dt.n_lv_entries = i;
		lvep = addr (dt.lv_array (i));
		unspec (lve) = "0"b;
		lve.lvname = lvname;
	     end;

	mhvtest_lvx = i;
	dt.lv_array (mhvtest_lvx).mounting = "1"b;	/* Note elegant effect upon mhvver */

	call MHV (lvname, "1"b, mhvtest_lvx);		/* this sets rc */
	return;



/**** Assume that pva and npv are set on entry to this procedure */

MHV:
     procedure (lvname, mhvtest, mhvtest_lvx);

declare mhvtest		 bit (1) aligned;
declare mhvtest_lvx		 fixed bin;
declare lvname		 char (*);

	do i = 1 to npv;
	     call MHV_ONE_PV;
	end;
	return;

MHV_ONE_PV:
     procedure;					/* i is global parm (ycch) specifying which pv */

declare (found, found_good_drive)
			 bit (1) aligned;

	pvname = pva (i).pvname;			/* for calls &c */
	found = "0"b;				/* set to one iff we find it mounted */
	found_good_drive = "0"b;			/* set to one iff we have a drive in mind to mount it on */

/**** NOTE: the following assumes that there can only be one dte
      with a given PV name in the entire disk table. */

	do j = 1 to dt.n_entries while (^found & ^found_good_drive);
	     dtep = addr (dt.array (j));
	     if dte.pvname = pvname
	     then if dte.used | dte.known
		then found = "1"b;			/* its already mounted */
		else if dte.pre_accepted | dte.demounted
		then found_good_drive = "1"b;

/**** + ASSUME: pv names are unique across the system.
      no pv can be listed twice in the disk table.

      THEN:   if this entry has our pv name, is available for
      storage system use, then it will be either already
      known (test above) or free, since no other LV
      could claim it. Thus it is the last place that we
      saw this disk pack. Since disk packs are notorious
      for their tendency to stay where one leaves them,
      we should tell the operator to spin it up HERE. */

		else if dte.storage_system & ^dte.deleted & (dte.device_type = pva (i).device_type)
		then found_good_drive = "1"b;
	end;

	if ^found
	then do;

		if ^found_good_drive & media_removable (pva (i).device_type)
						/* we can search for a good drive for 451's */
						/* for non-demountables we either know where it was */
						/* or stay mute, to avoid fatuous suggestions. */
		then do trial = 1 to 3 while (^found_good_drive);
			do j = 1 to dt.n_entries while (^found_good_drive);
			     dtep = addr (dt.array (j));
						/*  Here are usability criteria */
			     if dte.storage_system & ^dte.deleted
						/** available at all */
				& ^(dte.used | dte.known)
						/** not in use already */
				& dte.device_type = pva (i).device_type
						/** the right kind */
			     then if (^(dte.pre_accepted & dt.lv_array (dte.lvx).mounting)
						/** we can't be planning to mount something else here */
				     | (mhvtest & dt.lv_array (dte.lvx).good_candidate))
						/** unless this is a dry run and we propose to demount this lv */
				     & ^((trial = 1) & dte.demounted)
						/** dont unseat in pass 1, we might mount it again */
				     & ^((trial < 3) & dte.pre_accepted)
						/** we are trying to mount something else here */
				then found_good_drive = "1"b;
			end;
		     end;


/**** We arrive here directly if the first loop found that the
      drive that the pack used to be on is available. In that
      case found_good_drive will be on. If the first loop found nothing,
      then found_good_drive will be on if the second loop found
      a place to put the pack. For a non-demountable pack,
      the second loop does not run, and we never set found_good_drive. */

		if found_good_drive
		then do;
			j = j - 1;		/* both loops fall out of the bottom after do has bumped the index */
						/* dtep is ok */
			if mhvtest
			then do;			/* claim tbl spot */
				dte.pre_accepted = "1"b;
				dte.demounted = "0"b;
				dte.lvx = mhvtest_lvx;
				dte.pvname = pvname;
			     end;
			else do;			/* assume for real */
				if dte.pre_accepted
				then ec = 0;
				else do;
					call initializer_mdc_$assert (pvname, j, ec);
					call copy_disk_table;
					if ec ^= 0
					then call print_error;
				     end;
				if ec = 0
				then call ioa_ ("mount pack ^a on ^a", dte.pvname, dte.drive_name);
			     end;
		     end;				/* End of found_good_drive case */
		else if media_removable (pva (i).device_type)
		then do;				/* If we couldn't suggest a drive for a 451, its an error. */
			if mhvtest
			then do;			/* no drive avl */
				rc = 1;		/* experiment failed */
				return;
			     end;
			else call ioa_ ("disk_table_: no drive available for ^a of ^a", pvname, lvname);
		     end;
		else do;				/* its a fixed disk, so we report the problem and claim success. The site must have a home for it someplace */
			if mhvtest
			then /* only mhvtest has an rc */
			     rc = 0;
			call ioa_ ("mount pack ^a.", pva (i).pvname);
		     end;
	     end;
	return;
     end MHV_ONE_PV;

     end MHV;


/* ------------------------------------------------------- */

get_dtp:
     entry returns (ptr);

	rce = "0"b;
	call copy_disk_table ();
	return (dtp);

/* -------------------------------------------------------- */


set_lv_pdir_bit:
     entry (a_lvname, a_bit, rc);

dcl     a_bit		 bit (1) aligned parm;

	rce = "1"b;
	call initializer_mdc_$set_lv_pdir_bit (a_lvname, a_bit, rc);
	call copy_disk_table ();
	return;


/* -------------------------------------------------------- */


set_vacate_pdirs_bit:
     entry (a_lvname, a_bit, rc);

	rce = "1"b;
	call initializer_mdc_$set_vacate_pdirs_bit (a_lvname, a_bit, rc);
	call copy_disk_table ();
	return;


/* -------------------------------------------------------- */

initialize_disk:
     entry;

	rce = "0"b;
	call cu_$arg_list_ptr (arg_list_ptr);
	call init_disk_pack_$parse_args (arg_list_ptr, drive_id, pvname1, copy_sw, rlv_sw, ec);
	if ec ^= 0
	then goto ret;

	call parse_drive_id (drive_id);

	if copy_sw
	then pvname = "";
	else pvname = pvname1;
	call verify_drive;				/* Check drive not in use, pvname not is use, set ptrs */


	if copy_sw
	then do;
		do j = 1 to dt.n_entries
		     while (
		     ^(dt.array (j).pvname = pvname1 & dt.array (j).storage_system
		     & (dt.array (j).used | dt.array (j).known | dt.array (j).pre_accepted)));
		end;
		if j > dt.n_entries
		then do;
			call ioa_ ("disk_table_: ^a not found.", pvname1);
			return;
		     end;
		dtep = addr (dt.array (j));
	     end;
	else do;
		call initializer_mdc_$assert (pvname1, i, ec);
		if ec = error_table_$unregistered_volume & rlv_sw
		then do;				/* rlv_sw will be on if operator supplied the "-rlv"
						   control to "init_vol" AND if we're running in
						   ring 1 AND we're in the midst of a cold boot.
						   We will automatically register the PV on the
						   root LV.  This will allow the initial system
						   to have a multi pack RLV.
						*/
			fb71 = clock ();
			pvinfo.pvid = substr (unspec (fb71), 36, 36);
			pvinfo.model = dte.device_type;
						/* will be stuck with this */
			pvinfo.pvname = pvname1;
			pvinfo.location = drive_id;
			pvinfo.mfg_serial = "registered at cold boot";
			pvinfo.date_registered = fb71;
			pvinfo.password = ""b;	/* register the PV */
			call volume_registration_mgr_$add_pvr ("root", addr (pvinfo), ec);
			if ec ^= 0
			then do;
				call print_error;	/* nothing we can do, must retry cold boot */
				return;
			     end;
			call initializer_mdc_$assert (pvname1, i, ec);
						/* try it again, with registration */
		     end;
		call copy_disk_table ();
		if ec ^= 0
		then do;
			call print_error;
			return;
		     end;

	     end;
	call init_disk_pack_ (dtp, dtep, i, arg_list_ptr, ec);
	return;

/* ------------------------------------------------------- */

reregister:
     entry;

	rce = "0"b;
	call cu_$level_get (level);
	if level ^= 1
	then do;
		call ioa_ ("disk_table_: cannot reregister from ring 4");
		return;
	     end;
	call cu_$arg_ptr (ARG1, ap, al, ec);
	if ec ^= 0
	then go to gruz;
	pvname = bchr;
	call cu_$arg_ptr (ARG2, ap, al, ec);
	if ec ^= 0
	then go to gruz;
	call parse_drive_id (bchr);
	call verify_drive;				/* Check drive not in use */

	call mdx$reregister (pvname, i, ec);
	if ec ^= 0
	then if ec < 100
	     then call print_error;
	     else call com_err_ (ec, "disk_table_", "error reregistering ^a", drive_arg);
	return;

/* -------------------------------------------------------- */

demount_hvol:
     entry;
dhv:
     entry (a_lvname);

	rce = "0"b;
	call cu_$arg_ptr (ARG1, ap, al, ec);
	if ec ^= 0
	then go to gruz;

	lvname = bchr;

	call copy_disk_table ();

	do j = 1 to dt.n_lv_entries while (^dt.lv_array (j).used | (dt.lv_array (j).lvname ^= lvname));
	end;
	if j > dt.n_lv_entries
	then do;
		call com_err_ (0, "disk_table_", "lv ^a not found", lvname);
		return;
	     end;

	lvep = addr (dt.lv_array (j));

	if lve.mounting
	then do;
		call initializer_mdc_$demount_lv (j, STOP_MOUNT, ec);
		call copy_disk_table ();
		if ec ^= 0
		then do;
			call com_err_ (ec, "disk_table_", "cannot stop mount of ^a", lvname);
			return;
		     end;

		call lv_request_$dhv (lvname);

		call copy_disk_table ();

		if lve.demounting & lve.lvname = lvname /* potential interlock? */
		then call ioa_ ("disk_table_: demounting partially mounted volume ^a", lvname);
		else do;
			call ioa_ ("mount of ^a stopped", lvname);
			return;
		     end;
	     end;
	else if lve.demounting
	then call ioa_ ("continuing demount of lv ^a", lvname);
	else if ^lve.hv_mounted
	then do;
		call ioa_ ("lv ^a is not mounted", lvname);
		return;
	     end;
	else if lve.pdirs_ok
	then do;
		call ioa_ (" lv ^a may contain process directories. Will not be demounted.", lvname);
		return;
	     end;
	else do;
		call lv_request_$dhv (lvname);
		call initializer_mdc_$demount_lv (j, START_DEMOUNT, ec);
		call copy_disk_table ();
		if ec ^= 0
		then do;
			call com_err_ (ec, "disk_table_", "Cannot demount lv ^a.", lvname);
			return;
		     end;
	     end;

	demount_problem = "0"b;

	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if (dte.lvx = j) & dte.used & dte.storage_system
	     then do;
		     call initializer_mdc_$demount_pv (i, ec);
		     call copy_disk_table ();
		     if ec ^= 0
		     then do;
			     demount_problem = "1"b;
			     call com_err_ (ec, "disk_table_", "Could not demount pv ^a of lv ^a (^a)", dte.pvname,
				lve.lvname, dte.drive_name);
			end;
		end;
	end;

	if demount_problem
	then call com_err_ (0, "disk_table_", "Could not finish demount of lv ^a", lvname);
	else do;
		call initializer_mdc_$demount_lv (j, FINISH_DEMOUNT, ec);
		call copy_disk_table ();
		if ec ^= 0
		then call com_err_ (ec, "disk_table_", "Could not finish demount of lv ^a", lvname);
		else call ioa_ ("demounted lv ^a", lvname);
	     end;
	return;

/* -------------------------------------------------------- */

remove:
     entry;

	rce = "0"b;
	call cu_$arg_ptr (1, ap, al, ec);
	if ec ^= 0
	then go to gruz;
	if bchr = "-all" | bchr = "-a"
	then do;
		call copy_disk_table ();
		do i = 1 to dt.n_entries;
		     dtep = addr (dt.array (i));
		     if dte.storage_system & ^dte.deleted & ^dte.used & (dte.known | dte.pre_accepted)
		     then do;
			     call initializer_mdc_$forget (i, ec);
			     call copy_disk_table ();
			end;
		end;
		return;
	     end;
	call parse_drive_id (bchr);

	call copy_disk_table ();
	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if dte.drive_name = drive_arg
	     then do;
		     if ^dte.storage_system
		     then call ioa_ ("disk_table_: ^a is an IO drive", drive_arg);
		     else if dte.deleted
		     then call ioa_ ("disk_table_: ^a is deleted", drive_arg);
		     else if dte.used
		     then call ioa_ ("disk_table_: ^a in use with ^a", drive_arg, dte.pvname);
		     else if dte.pre_accepted | dte.known
		     then do;
			     call initializer_mdc_$forget (i, ec);
			     call copy_disk_table ();
			end;
		     else call ioa_ ("disk_table_: ^a not in use.", dte.drive_name);
		     return;
		end;
	end;
	call ioa_ ("disk_table_: cannot locate ^a", drive_arg);
	return;

/* ------------------------------------------------------- */

io_ss_reconfig:
     entry;

	rce = "0"b;
	call cu_$arg_ptr (ARG1, ap, al, ec);
	if ec ^= 0
	then go to gruz;

	call parse_drive_id (bchr);

	call cu_$arg_ptr (ARG2, ap, al, ec);
	if ec ^= 0
	then go to gruz;

	if ^(bchr = "io" | bchr = "ss" | bchr = "storage_system")
	then do;
		call ioa_ ("disk_table_: ""^a"" is not ""io"", ""storage_system"", or ""ss"".", bchr);
		return;
	     end;

	call copy_disk_table;
	do i = 1 to dt.n_entries while (substr (dt.array (i).drive_name, 1, 7) ^= drive_arg);
	end;
	if i > dt.n_entries
	then do;
		call ioa_ ("disk_table_: ^a not found.", drive_arg);
		return;
	     end;
	if dt.array (i).is_sub_vol
	then do;
		last_sv = (i + dt.array (i).num_of_sv) - 1;
	     end;
	else do;
		last_sv = i;
	     end;

	do i = i to last_sv;
	     call initializer_mdc_$ss_io_reconfig (i, (substr (bchr, 1, 1) = "i"), ec);
	     call copy_disk_table ();
	     if ec ^= 0
	     then call com_err_ (ec, "disk_table_");
	end;
	return;

/* --------------------------------------------------------- */

list:
     entry;

	rce = "0"b;
	lpv, llv, bfsw, ldrive = "0"b;
	call cu_$arg_ptr (ARG1, ap, al, ec);
	if ec = 0
	then do;
		if bchr = "-pv"
		then do;
			lpv = "1"b;
			call cu_$arg_ptr (ARG2, ap, al, ec);
			if ec ^= 0
			then go to gruz;
			arg = bchr;
		     end;
		else if bchr = "-bf" | bchr = "-brief"
		then bfsw = "1"b;
		else if bchr = "-mounts" | bchr = "-mt"
		then do;
			call copy_disk_table;
			go to list_hvols;
		     end;
		else if bchr = "-lv" | bchr = "-hv"
		then do;
			llv = "1"b;
			call cu_$arg_ptr (ARG2, ap, al, ec);
			if ec ^= 0
			then go to gruz;
			arg = bchr;
		     end;
		else do;
			ldrive = "1"b;
			call parse_drive_id (bchr);
		     end;
	     end;

	call copy_disk_table ();
	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if ldrive & (dte.drive_name ^= drive_arg)
	     then go to skip;
	     lvep = addr (dt.lv_array (dte.lvx));
	     if lve.hv_mounted
	     then lvname = lve.lvname;
	     else call ioa_$rsnnl ("(^a)", lvname, ignore_num, lve.lvname);
	     if ^dte.storage_system
	     then if dte.deleted
		then if ldrive
		     then call ioa_ ("^a (deleted io drive)", dte.drive_name);
		     else ;
		else call ioa_ ("^a (io drive)", dte.drive_name);
	     else if dte.deleted
	     then if ldrive
		then call ioa_ ("^a deleted", drive_arg);
		else ;
	     else do;
		     if lpv & (^(dte.used | (dte.known | dte.pre_accepted)) | (dte.pvname ^= arg))
		     then go to skip;
		     if llv & ((^lve.used) | (lve.lvname ^= arg))
		     then go to skip;
		     if dte.used
		     then call ioa_ ("^a ^8a ^8a", dte.drive_name, dte.pvname, lvname);
		     else if dte.known
		     then call ioa_ ("^a ^8a ^8a ***", dte.drive_name, dte.pvname, lvname);
		     else if dte.pre_accepted
		     then call ioa_ ("^a ^8a ^8a *", dte.drive_name, dte.pvname, lvname);
		     else if llv | bfsw
		     then go to skip;
		     else call ioa_ ("^a", dte.drive_name);
		end;
skip:
	end;
	if llv | lpv | ldrive
	then return;				/* List out mounts and demounts in progresss */

list_hvols:
	do i = 1 to dt.n_lv_entries;
	     lvep = addr (dt.lv_array (i));
	     if lve.public
	     then publicstr = "public";
	     else publicstr = "private";
	     if lve.mounting
	     then call ioa_ ("^7a lv ^a mount in progress", publicstr, lve.lvname);
	     if lve.demounting
	     then call ioa_ ("^7a lv ^a demount in progress", publicstr, lve.lvname);
	end;
	return;

/* --------------------------------------------------------------- */

volsalv:
     entry (a_pvname, a_drive, salv_opt_bits, rc);

	rce = "1"b;
	pvname = a_pvname;
	ap = addr (a_drive);
	al = length (a_drive);
	rc = 1;
	call parse_drive_id (bchr);
	if salv_opts.check
	then do;
		call disk_table_$accept_rlv (ec);
		if ec ^= 0
		then do;
			call copy_disk_table ();
			go to ret;
		     end;
	     end;

	call verify_drive;				/* change args to dtx */

	call initializer_mdc_$volsalv (pvname, i, salv_opt_bits, ec);
	call copy_disk_table ();
	if ec > 0 & ec < 100
	then do;
		call print_error;
		ec = error_table_$action_not_performed;
	     end;
	rc = ec;
	return;

/* ------------------------------------------------------- */

volsalvall:
     entry (salv_opt_bits, rc);

	rce = "1"b;
	if salv_opts.check
	then do;
		call disk_table_$accept_rlv (ec);
		if ec ^= 0
		then do;
			call copy_disk_table ();
			go to ret;
		     end;
	     end;

	call copy_disk_table ();

	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if ^dte.used
	     then if dte.storage_system & ^dte.deleted & ^dte.used & (dte.known | dte.pre_accepted)
		then do;
			pvname = dte.pvname;
			call initializer_mdc_$volsalv (pvname, i, salv_opt_bits, ec);
			if ec ^= 0
			then if ec < 100
			     then call print_error;
			     else call com_err_ (ec, "disk_table_", "salvaging ^a on ^a", pvname, dte.drive_name);
		     end;
	end;

	call copy_disk_table ();

	return;

/* -------------------------------------------------------------------- */

volrlvcheck:
     entry (a_pvname, a_drive, a_copydrive, salv_opt_bits, rc);

dcl     rbldsw		 bit (1) aligned;

	rbldsw = "0"b;
	goto copy_vol;

rbld_disk:
     entry (a_pvname, a_drive, a_copydrive, salv_opt_bits, rc);

	rbldsw = "1"b;

copy_vol:
	rce = "1"b;
	call copy_disk_table ();
	ap = addr (a_copydrive);
	al = length (a_copydrive);
	rc = 1;
	call parse_drive_id (bchr);
	pvname = "";				/* verify object drive */
	call verify_drive;
	j = i;
	if dte.pre_accepted
	then call initializer_mdc_$forget (j, ignore_ec);

	pvname = a_pvname;
	ap = addr (a_drive);
	al = length (a_drive);
	call parse_drive_id (bchr);
	do i = 1 to dt.n_entries;
	     if dt.array (i).drive_name = drive_arg
	     then go to vlrlvc1;
	end;

	call com_err_ (0, "disk_table_", "cannot locate ^a", drive_arg);
	rc = 4;
	return;
vlrlvc1:
	dtep = addr (dt.array (i));
	if dte.used & (dte.pvname = pvname)
	then ;
	else do;
		call verify_drive;

		call initializer_mdc_$take (pvname, i, took, rc);
		if rc ^= 0
		then do;
			call copy_disk_table ();
			return;
		     end;
	     end;

	call copy_disk_table ();
	dtep = addr (dt.array (i));

	if ^rbldsw
	then if dte.lvx ^= 1
	     then do;
		     call com_err_ (0, "disk_table_", "^a is not part of root volume", pvname);
		     return;
		end;

	rc = 0;
	if salv_opts.check
	then do;
		call disk_table_$accept_rlv (ec);
		if ec ^= 0
		then do;
			call copy_disk_table ();
			go to ret;
		     end;
	     end;
	call copy_disk_table ();

	if rbldsw
	then call disk_rebuild_caller (dtp, i, j, salv_opt_bits);
	else call initializer_mdc_$rlvolcheck (i, j, salv_opt_bits, rc);
	return;

/* --------------------------------------------------------------- */
salv_flag:
     entry () returns (bit (1));

	rce = "0"b;
	call copy_disk_table ();
	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if dte.storage_system & ^dte.deleted & dte.used & (dte.lvx = 1)
	     then if dte.need_salvage
		then return ("1"b);
	end;
	return ("0"b);				/* All clean */

parse_drive_id:
     proc (drive_id);

dcl     drive_id		 char (*);
dcl     id_len		 fixed bin;

	id_len = length (rtrim (drive_id));
	if char (drive_id, 3) ^= "dsk" | id_len < 7
	then do;
faugh:
		call ioa_ ("disk_table_: invalid drive id ""^a""", drive_id);
		ec = 10;
		go to ret;
	     end;
	if id_len >= 9
	then goto faugh;
	drive_arg = drive_id;
	if substr (drive_id, 5, 1) ^= "_"
	then go to faugh;
	drivea = substr (drive_id, 6, 2);
	if drivea = ""
	then go to faugh;
	if verify (drivea, "0123456789") ^= 0
	then go to faugh;
	if id_len = 8
	then do;
		if verify (substr (drive_id, 8, 1), valid_sv_string) ^= 0
		then goto faugh;
	     end;

     end parse_drive_id;

verify_drive:
     procedure;					/* Int proc to check not-in-use drive */

	call copy_disk_table ();
	match_dtx = -1;
	do i = 1 to dt.n_entries;			/* Search for named volume */
	     dtep = addr (dt.array (i));
	     if ^dte.storage_system & (drive_arg = dte.drive_name)
	     then do;
		     call ioa_ ("disk_table_: ^a is an IO drive", drive_arg);
		     ec = 5;
		     go to ret;
		end;
	     else if dte.deleted & (drive_arg = dte.drive_name)
	     then do;
		     call ioa_ ("disk_table_: ^a is deleted", drive_arg);
		     ec = 4;
		     go to ret;
		end;
	     else if dte.used
	     then do;
		     if dte.pvname = pvname
		     then do;			/* If already in table, error */
			     call ioa_ ("disk_table_: ^a already in use on ^a.", pvname, dte.drive_name);
			     ec = 1;
			     go to ret;
			end;
		     else if dte.drive_name = drive_arg
		     then do;			/* if somebody else on there */
			     call ioa_ ("disk_table_: ^a in use: contains ^a", drive_arg, dte.pvname);
			     ec = 2;
			     go to ret;
			end;
		     else ;
		end;
	     else if dte.known & (dte.pvname = pvname) & (dte.drive_name ^= drive_arg)
	     then do;
		     call ioa_ ("disk_table_: ^a currently known to be on  ^a.", pvname, dte.drive_name);
		     ec = 3;
		     go to ret;
		end;
	     else if dte.drive_name = drive_arg
	     then match_dtx = i;			/* Unused */
	end;

	if match_dtx > 0
	then do;					/* If we found it */
		dtep = addr (dt.array (match_dtx));
		i = match_dtx;
		return;
	     end;
	call ioa_ ("disk_table_: cannot locate ^a", drive_arg);
	ec = 4;
	go to ret;
     end verify_drive;

print_error:
     proc;
	call com_err_ (ec, "disk_table_", "Cannot verify label of ^a", dte.drive_name);

     end print_error;

copy_disk_table:
     proc ();

/* This internal procedure is called whenever a fresh copy of the disk_table
   is needed in the user ring... */

	call cu_$level_get (level);
	if level = 1
	then dtp = sdtp;
	else do;
		if sdtp = null
		then call hcs_$make_seg ("", "", "", 1011b, sdtp, ignore_ec);
		dtp = sdtp;
		call initializer_mdc_$read_disk_table (dtp, ignore_ec);
	     end;
	pvap = addr (dt.lv_array (dt.max_n_entries + 1));

     end copy_disk_table;

announce_mount:
     proc (lvxa);

dcl     lvxa		 fixed bin;

	call ioa_ ("lv ^a mounted", dt.lv_array (lvxa).lvname);
	if lvxa ^= 1
	then /* Not for the root */
	     call lv_request_$mhv_complete (lvxa, dtp);
     end;

%include fs_dev_types;
%include disk_table;

%include salv_options;

%include volume_registration;

/* BEGIN MESSAGE DOCUMENTATION

   Message:
   disk_table_: no drive available for PVNAME LVNAME

   S:	$initializer_io

   T:	$run

   M:	An alv LVNAME command was unable to allocate a drive for PVNAME.

   A:	Demount some un-needed logical volume and try again.


   Message:
   disk_table_: PVNAME not found.

   S:	$initializer_io

   T:	$run

   M:	An initialize_disk -copy command could not find the source volume.

   A:	$tryagn


   Message:
   disk_table_: cannot reregister from ring 4

   S:	$initializer_io

   T:	$run

   M:	A reregister command was issued from ring 4.

   A:	$tryagn


   Message:
   disk_table_: demounting partially mounted volume LVNAME

   S:	$initializer_io

   T:	$run

   M:	The operator issued a dlv LVNAME command
   for a logical volume for which a previous dlv command did not succeed.

   A:	$ignore


   Message:
   mount of LVNAME stopped

   S:	$initializer_io

   T:	$run

   M:	The operator issued a dlv LVNAME command for a volume which was being mounted.

   A:	$ignore


   Message:
   lv LVNAME is not mounted

   S:	$initializer_io

   T:	$run

   M:	The operator issued a dlv LVNAME command but LVNAME was not mounted.

   A:	$tryagn


   Message:
   continuing demount of lv LVNAME

   S:	$initializer_io

   T:	$run

   The operator issued a dlv command for a logical volume for which a previous dlv command failed.

   A:	$ignore


   Message:
   demounted lv LVNAME

   S:	$initializer_io

   T:	$run

   M:	The demount of the logical volume LVNAME is complete.

   A:	$ignore


   Message:
   disk_table_: DRIVENAME is an IO drive

   S:	$initializer_io

   T:	$run

   M:	The operator issued a del_vol command but the
   drive is an IO drive.

   A:	$tryagn


   Message:
   disk_table_: DRIVENAME is deleted

   S:	$initializer_io

   T:	$run

   M:	A del_vol command was issued
   but the drive has been deleted.

   A:	$tryagn


   Message:
   disk_table_: DRIVE in use with PVNAME

   S:	$initializer_io

   T:	$run

   M:	A del_vol DRIVE command was issued
   but the drive is currently in use.

   A:	$tryagn
   To demount the pack,
   use the dlv command.


   Message:
   disk_table_: DRIVE not in use.

   S:	$initializer_io

   T:	$run

   M:	A del_vol command was issued
   but nothing is on the drive.

   A:	$tryagn


   Message:
   disk_table_: cannot locate DRIVE

   S:	$initializer_io

   T:	$run

   M:	A del_vol DRIVE command was issued
   but there is no such drive in the configuration.

   A:	$tryagn


   Message:
   disk_table_: "KEY" is not "io", "storage_system", or "ss".

   S:	$initializer_io

   T:	$run

   M:	The operator issued an set_drive_usage DRIVE KEY command.
   KEY is invalid.

   A:	$tryagn


   Message:
   disk_table_: DRIVE not found.

   S:	$initializer_io

   T:	$run

   M:	The operator issued an set_drive_usage DRIVE KEY command,
   but DRIVE is not in the current configuration.

   A:	$tryagn


   Message:
   disk_table_: invalid drive id "ARG"

   S:	$initializer_io

   T:	$run

   M:	The drive id ARG does not have the form
   dskX_NN.

   A:	$tryagn


   Message:
   disk_table_: DRIVE is an IO drive

   S:	$initializer_io

   T:	$run

   M:	A command has been issued to affect DRIVE,
   but it is currently in use as an IO drive.

   A:	$tryagn
   Use the set_drive_usage command to change it if necessary.


   Message:
   disk_table_: DRIVE is deleted

   S:	$initializer_io

   T:	$run

   M:	The drive named is deleted.

   A:	$tryagn


   Message:
   disk_table_: PVNAME already in use on DRIVE.

   S:	$initializer_io

   T:	$run

   M:	An attempt to add a physical volume
   finds the volume already in use on another drive.

   A:	$tryagn


   Message:
   disk_table_: DRIVE in use: contains PVNAME

   S:	$initializer_io

   T:	$run

   M:	An attempt to
   use DRIVE finds it already in use.

   A:	$tryagn


   Message:
   disk_table_: PVNAME currently known to be on DRIVE

   S:	$initializer_io

   T:	$run

   M:	An attempt to specify that PVNAME
   is on a certain drive
   finds it on another drive.

   A:	$tryagn
   Use the del_vol command if necessary to cause the system to
   forget DRIVE's old contents, if a pack has been moved.


   Message:
   disk_table_: cannot locate DRIVE

   S:	$initializer_io

   T:	$run

   M:	A command specifying DRIVE
   was issued but no such drive can be found.

   A:	$tryagn


   Message:
   lv LVNAME mounted

   S:	$initializer_io

   T:	$run

   M:	The mounting of LVNAME is complete.

   A:	$tryagn


   Message:
   disk_table_: ERROR_MESSAGE. New disk_table created

   S:	$initializer_io

   T:	$init

   M:	The system has created a new copy of the
   segment >disk_table at initialization time.
   The copy from the previous bootload could not be found.
   The position of all previously mounted volumes has been forgotten.

   A:	Use the av command to specify the
   position of all volumes,
   and the alv command to mount them.


   Message:
   disk_table_: ERROR_MESSAGE.

   S:	$initializer_io

   T:	$init

   M:	An error has been returned from
   initializer_mdc_$init.
   $err
   Subsequent attempts to mount volumes may malfunction.

   A:	$contact


   Message:
   disk_table_: Some volumes are incomplete. Complete or demount them.

   S:	$initializer_io

   T:	$init

   M:	Initialization has failed to
   remount all the volumes which were previously known,
   in response to a startup command.

   A:	Use the list_disks command to
   find out which volumes are incomplete.
   Use the av or dlv commands to
   rectify the situation,
   and try startup again.


   Message:
   disk_table_: ERROR_MESSAGE. Cannot get volume registration for the root
   logical volume.
   .brf
   Shutdown and reboot with "boot nolv".

   S:	$initializer_io

   T:	$init

   M:	The system is unable to read the volume registration for the root
   logical volume, and cannot therefore determine if the root is complete.

   A:	Shutdown. If you can mount the entire root, do so, describe all
   volumes on the root config card, "boot stan nosc", and fix the registration. If
   this fails, shutdown, and "boot nolv".


   Message:
   disk_table_: The root logical volume is incomplete.

   S:	$initializer_io

   T:	$init

   M:	Some registered  physical volume(s) of the root logical volume are
   not mounted, but the operator has attempted to execute a command that requires
   the root to be completely mounted. This message is followed by mount messages
   for the missing volumes.


   A:	Mount the missing volumes, and notify the system of their location
   with the add_vol command.


   Message:
   disk_table_: ERROR_MESSAGE.

   S:	$initializer_io

   T:	$run

   M:	An error has occurred in a storage system disk manipulation command.

   A:	$tryagn


   Message:
   disk_table_: ERROR_MESSAGE. LVNAME

   S:	$initializer_io

   T:	$run

   M:	An error occurred trying to get
   the list of physical volumes which comprise LVNAME.

   A:	$tryagn


   Message:
   disk_table_: lv LVNAME already mounted

   S:	$initializer_io

   T:	$run

   M:	An alv LVNAME command
   finds LVNAME already mounted.

   A:	$tryagn


   Message:
   disk_table_: lv LVNAME  cannot be mounted, in process of demount.

   S:	$initializer_io

   T:	$run

   M:	An alv LVNAME command was issued
   but the logical volume is in the process of demounting.

   A:	$tryagn


   Message:
   disk_table_: ERROR_MESSAGE. error reregistering DRIVE

   S:	$initializer_io

   T:	$run

   M:	The reregister DRIVE command did not succeed.

   A:	$tryagn


   Message:
   disk_table_: lv LVNAME not found

   S:	$initializer_io

   T:	$run

   M:	A dlv LVNAME command was issued
   but no such logical volume is mounted.

   A:	$tryagn


   Message:
   disk_table_: ERROR_MESSAGE. cannot stop mount of LVNAME

   S:	$initializer_io

   T:	$run

   M:	A dlv LVNAME command was issued
   but an error prevents the dlv from working.

   A:	$tryagn


   Message:
   disk_table_: ERROR_MESSAGE. Cannot demount lv LVNAME

   S:	$initializer_io

   T:	$run

   M:	A dlv LVNAME command was issued
   but an error prevents the dlv from working.

   A:	$tryagn


   Message:
   disk_table_: ERROR_MESSAGE. Could not demount pv PVNAME of lv LVNAME (DRIVE)

   S:	$initializer_io

   T:	$run

   M:	An error occurred when trying to demount
   a physical volume.
   This may be due to process directory or wired segments being on this physical volume.
   The del_lv command will be partially successful when this happens.

   A: Determine which processes, if any, have
   process directories on this logical volume and bump or terminate them.
   Use list_disks to determine which volumes are still mounted, and try dlv again.
   $inform


   Message:
   disk_table_: Could not finish demount of lv LVNAME

   S:	$initializer_io

   T:	$run

   M:	The system encountered a problem
   trying to delete the
   logical volume LVNAME.

   A:	$inform


   Message:
   disk_table_: ERROR_MESSAGE. salvaging PVNAME on DRIVE

   S:	$initializer_io

   T:	$run

   M:	An error occurred while doing
   a salvage_vol -all.

   A:	$tryagn


   Message:
   disk_table_: cannot locate DRIVE

   S:	$initializer_io

   T:	$run

   M:	A disk_rebuild command cannot locate
   the output drive.

   A:	$tryagn


   Message:
   disk_table_: Cannot verify label of DRIVE: REASON

   S:	$initializer_io

   T:	$run

   M:	An error occurred checking the label of DRIVE.
   Usually either the drive is not ready, or the pack label does not match the required pack label.

   A:	$tryagn


   Message:
   disk_table_: ERROR_MESSAGE. Cannot verify label of DRIVE

   S:	$initializer_io

   T:	$run

   M:	An error occurred checking the label of the pack on DRIVE.
   Usually either the drive is not ready, or the pack label does not match the required pack label.

   A:	$tryagn


   Message:
   added pv PVNAME to mounted lv LVNAME.

   S:	$initializer_io

   T:	$run

   M:	An av command was used to increase the size of a mounted volume.

   A:	$ignore


   Message:
   mount pack PVNAME {on DRIVE}

   S:	$initializer_io

   T:	$run

   M:	This message occurs when the system
   is attempting to mount a logical volume
   and finds one or more physical volumes
   have not been made known via the add_vol command.
   If the pack is a non-demountable volume, a DRIVE
   will only be printed if the system knows the drive on which the pack
   was previously mounted. If the pack is a demountable volume, then
   a DRIVE will be printed always. If the system does not know
   the drive on which the pack was last mounted, then
   it chooses a drive where the pack should be mounted
   and prints this message.

   A:	Mount the pack specified.
   If it is not convenient to use the specified DRIVE,
   use another.
   Then use the add_vol command
   to tell the system that the physical volume has been mounted.
   When all packs have been mounted and either accepted via add_vol
   or are on the drives where the system called for them, issue a "alv LVNAME" command to accept them
   all at once. The logical volume will be put in use.


   END MESSAGE DOCUMENTATION */

     end disk_table_;
  



		    init_disk_pack_.pl1             11/11/89  1102.4r w 11/11/89  0803.1      119448



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1984 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */


/****^  HISTORY COMMENTS:
  1) change(86-01-16,Fawcett), approve(86-04-11,MCR7383),
     audit(86-05-29,LJAdams), install(86-07-17,MR12.0-1097):
     Add support for subvolumes, and 512_WORD_IO, 3380 and 3390.
                                                   END HISTORY COMMENTS */


/* format: style3 */
init_disk_pack_$parse_args:
     proc (Arg_list_ptr, Drive_id, Pvname, Copy_flag, RLV_flag, Code);

/*  This procedure initializes a disk from Ring 1.  Two cases are covered,
   either the disk is mounted, but not accepted, or a copy is being made of an
   accepted disk.  It is passed the PVT index and a pointer to the disk_table entry.
   This procedure prepares the label from the disk table entry and data derived
   from interrogation of the operator, and calls init_vol_header_ to
   write the disk based on this info. 

   Revised for init_vol_header_, fixing several bugs, BSG 06/25/76 
   Revised for fill_vol_extents_, forcing the latter to do all calculation, BSG 08/31/76 
   Revised for countervalidate_label_ BSG 12/1/76 
   Fixed not to assume pvtx and dtep correspond for Vol Backup, BSG 3/9/77 
   Modified by Mike Grady 9/79 to improve above fix 
   Modified by J. Bongiovanni, April 1982, to add parse_args entry, general cleanup 
   Modified '82 for english error codes
   Modified 84-09-24 by EJ Sharpe for "-rlv" arg and RLV_flag
   Modified 84-10-30 by EJ Sharpe to user convert_status_code_ and some minor fixes
   Modified 85-04-29 by EJ Sharpe to fix uninitialized copy_sw
*/

/*  Parameter  */

dcl	Arg_list_ptr	ptr parameter;		/* Command arg list pointer */
dcl	Drive_id		char (*) parameter;		/* Drive identifier (dskX_NN) */
dcl	Pvname		char (*) parameter;		/* Physical volume name */
dcl	Copy_flag		bit (1) aligned parameter;	/* ON => -copy specified */
dcl       RLV_flag            bit (1) aligned parameter;
dcl	Dtp		ptr;			/* -> disk table */
dcl	Dtep		ptr;			/* -> disk table entry */
dcl	Pvtx		fixed bin;		/* PVT index */
dcl	Code		fixed bin (35);

/*  Automatic  */

dcl	argl		fixed bin (21);
dcl	argp		ptr;
dcl	baseadd		fixed bin;
dcl	conversation	bit (1) aligned;
dcl	copy_sw		bit (1) aligned;
dcl	dev_type		fixed bin;
dcl	longerr		char (100) aligned;
dcl	n_vtoce		fixed bin;
dcl	no_reg_sw		bit (1) aligned;
dcl	num_pages		fixed bin;
dcl	pagep		ptr;
dcl	pv_name		char (32);
dcl       rlv_sw              bit (1) aligned;
dcl	shorterr		char (8) aligned;
dcl	thing		char (20);

/*  Static  */

dcl	myname		char (20) init ("init_disk_pack_") static options (constant);

/*  Based  */

dcl	arg		char (argl) based (argp);
dcl	1 buffer		aligned like label;
dcl	page		bit (36 * 1024) based (pagep);

/*  External  */

dcl	error_table_$badopt fixed bin (35) external;
dcl       error_table_$bad_label fixed bin (35) external;
dcl	sys_info$access_class_ceiling
			bit (72) aligned external;

/*  Builtin  */

dcl	addr		builtin;
dcl	clock		builtin;
dcl	null		builtin;

/*  Entry  */

dcl	com_err_		entry options (variable);
dcl	config_$find	entry (char(4) aligned, ptr);
dcl	convert_status_code_ entry (fixed bin(35), char(8) aligned, char(100) aligned);
dcl	countervalidate_label_$query
			entry (ptr, char (*), char (*), char (*)) returns (bit (1));
dcl	cu_$arg_ptr_rel	entry (fixed bin, ptr, fixed bin (21), fixed bin (35), ptr);
dcl	fill_vol_extents_	entry (ptr, fixed bin, fixed bin, fixed bin, fixed bin, bit (1) aligned, fixed bin (17),
			fixed bin (35));
dcl       get_ring_           entry() returns(fixed bin(3));
dcl	init_vol_header_	entry (fixed bin, fixed bin, ptr, entry, char (*), fixed bin, fixed bin, fixed bin, fixed bin (35));
dcl	initializer_gate_$write_disk
			entry (fixed bin, fixed bin, ptr, fixed bin (35));
dcl	initializer_gate_$read_disk
			entry (fixed bin, fixed bin, ptr, fixed bin (35));
dcl	ioa_		entry options (variable);






%page;
/*  Parse command arguments and return information to disk_table_  */

	Code = 0;

	call cu_$arg_ptr_rel (1, argp, argl, Code, Arg_list_ptr);
	if Code ^= 0
	then do;
		call com_err_ (Code, myname, "pvname");
		return;
	     end;

	Pvname = arg;

	call cu_$arg_ptr_rel (2, argp, argl, Code, Arg_list_ptr);
	if Code ^= 0
	then do;
		call com_err_ (Code, myname, "drive_id");
		return;
	     end;

	Drive_id = arg;

	call PARSE_ARGS;

	Copy_flag = copy_sw;
	RLV_flag = rlv_sw;
	return;

%page;
/*  Entry to do the work  */
init_disk_pack_:
     entry (Dtp, Dtep, Pvtx, Arg_list_ptr, Code);


	dtp = Dtp;
	dtep = Dtep;
	labelp = addr (buffer);

	call PARSE_ARGS;

	pv_name = "read";
	thing = "label";
	call initializer_gate_$read_disk (Pvtx, LABEL_ADDR, labelp, Code);
	if Code ^= 0
	then do;
		call com_err_ (Code, myname, "Cannot read current label of ^a.", (dt.array (Pvtx).drive_name));
		return;
	     end;
	if ^countervalidate_label_$query (labelp, (dte.pvname), myname, (dt.array (Pvtx).drive_name))
	then do;
	          Code = error_table_$bad_label; 
		return;
	     end;


	pagep = addr (buffer);
	page = "0"b;

	dev_type = dte.device_type;

	label.version = 1;
	label.mfg_serial = dte.pvname;		/* Good enough for a start */
	pv_name, label.pv_name = dte.pvname;
	label.time_registered = clock ();
	if dte.is_sub_vol
	then do;
		label.vol_size = rec_per_sv (dte.device_type);
		label.number_of_sv = dte.num_of_sv;
		label.this_sv = dte.sv_num;
		label.sub_vol_name = valid_sv_array (dte.sv_num);
	     end;
	else do;
		label.vol_size = rec_per_dev (dte.device_type);
						/* use whole pack */
		label.number_of_sv = 0;
		label.this_sv = 0;
	     end;

	if ^no_reg_sw
	then do;
		label.pvid = dte.pvid;		/* disk_table_ made this up */
		lvep = addr (dt.lv_array (dte.lvx));
		label.lvid = lve.lvid;		/* Get from disk_table_. who searched. */
		label.lv_name = lve.lvname;
		label.min_access_class = lve.min_access_class;
		label.max_access_class = lve.max_access_class;
		label.private = ^lve.public;
	     end;
	else do;
		label.pvid, label.lvid = (36)"1"b;
		label.lv_name = "";
		label.max_access_class = sys_info$access_class_ceiling;
		label.min_access_class = ""b;
	     end;

	label.Multics = Multics_ID_String;
	label.password = ""b;
	label.root.disk_table_vtocx = -1;
	label.root.disk_table_uid = ""b;
	label.root_vtocx = 0;
	label.shutdown_state = 0;
	label.esd_state = 0;



/* Fill volume defaults. Hold a dialogue if requested. */

	call fill_vol_extents_ (labelp, -1, n_vtoce, baseadd, num_pages, ^conversation, dev_type, Code);
						/* -1 = no "lace" */
	if Code ^= 0
	then return;

/* Write out the actual disk */

	call init_vol_header_ (Pvtx, dev_type, pagep, initializer_gate_$write_disk, thing, n_vtoce, baseadd, num_pages,
	     Code);

	if Code ^= 0
	then do;
	     call convert_status_code_ (Code, shorterr, longerr);
	     call ioa_ ("^a: ^a ^a disk err ""^a"" on ^a", myname, dte.drive_name, pv_name, longerr, thing);
	end;

	else call ioa_ ("volume ^a ^d records", pv_name, num_pages);
	return;


%page;

/*  Internal Procedure to do argument parsing  */

PARSE_ARGS:
     proc;

dcl	code		fixed bin (35);
dcl	arg_no		fixed bin;



	conversation, no_reg_sw, rlv_sw, copy_sw = "0"b;

	code = 0;
	do arg_no = 3 repeat arg_no + 1 while (code = 0);
	     call cu_$arg_ptr_rel (arg_no, argp, argl, code, Arg_list_ptr);
	     if code = 0
	     then do;
		     if arg = "-special"
		     then conversation = "1"b;
		     else if arg = "-copy"
		     then conversation, copy_sw = "1"b;
		     else if arg = "-no_reg"
		     then conversation, no_reg_sw = "1"b;
		     else if arg = "-rlv"
			then if ^( (get_ring_() = 1)  &  BOOTED_COLD () ) then do;
				Code = error_table_$badopt;
				call com_err_ (Code, myname, """-rlv"" allowed only at ring 1 during cold boot.");
				return;
			     end;
			     else rlv_sw = "1"b;
		     else do;
			     Code = error_table_$badopt;
			     call com_err_ (Code, myname, arg);
			     return;
			end;
		end;
	end;

     end PARSE_ARGS;
%page;

/* Internal procedure to determine if we're in middle of cold boot */

BOOTED_COLD:
     proc () returns (bit (1) aligned);

	intk_cardp = null ();
	call config_$find ("intk", intk_cardp);
	if intk_cardp = null () then do;
	     call com_err_ (0, myname, "INTK card missing from config deck.");
	     return ("0"b);
	end;
	else return (intk_card.warm_or_cold = "cold");

     end BOOTED_COLD;
%page;
%include config_intk_card;
%page;
%include disk_pack;
%page;
%include disk_table;
%page;
%include fs_dev_types;
%page;
%include fs_vol_label;
%page;
/* BEGIN MESSAGE DOCUMENTATION

   Message:
   init_disk_pack_: Cannot read current label of DRIVE: ERROR_MESSAGE

   S:	$initializer_io

   T:	$response

   M:	A disk error prevents checking the label of the pack on DRIVE.
   The pack may need to be formatted before it can be initialized.

   A:	Take appropriate action.


   Message:
   init_disk_pack_: INTK card missing from config deck.

   S:	$initializer_io

   T:	$response

   M:	When the operator specifies the "-rlv" control argument to
   "init_vol" this program attempts to verify that the system is in cold
   boot by inspecting the INTK card in the config deck.  When this message
   appears, there is some difficulty in locating that card.

   A:	If really trying to boot cold, try again.


   Message:
   init_disk_pack_:  Specified control argument not accepted.  "-rlv"
   allowed only at ring-1 during a cold boot.

   S:	$initializer_io

   T:	$response

   M:	Automatic registration of root PVs is allowed only during
   a cold boot of Multics.

   A:	Use the "add_volume_registration" administrative command to
   register the PV before attempting to initialize it.


   Message:
   init_disk_pack_: DRIVE PVNAME disk err WWWW on ITEM

   S:	$initializer_io

   T:	$response

   M:	A disk error prevents writing the ITEM section of the disk volume PVNAME.

   A:	Fix the drive, or use a new pack.


   Message:
   init_disk_pack_: Volume on DRIVE is Multics Storage System Volume "PVNAME",
   .br
   last used DATE_TIME. Do you wish to overwrite it?

   S:	$initializer_io

   T:	$response

   M:	The init_disk command specifed a disk pack
   which has a valid Multics label.
   Initializing this pack will destroy all data contained on it.

   A:	Do not answer yes unless you are sure that the pack contents should be destroyed.
   Check the pack serial number.


   Message:
   init_disk_pack_: Volume on DRIVE is a copy of Multics Storage System Volume "PVNAME",
   .br
   last used DATE_TIME. Do you wish to overwrite it?

   S:	$initializer_io

   T:	$response

   M:	The init_disk command specifed a disk pack
   which has a valid Multics label.
   Another copy of PVNAME is currently in use.
   Initializing this pack will destroy all data contained on it.

   A:	Do not answer yes unless you are sure that the pack contents should be destroyed.
   Check the pack serial number.


   Message:
   init_disk_pack_: Volume on DRIVE is a copy of pv "PVNAME",
   .br
   last used DATE_TIME. Do you wish to overwrite it?

   S:	$initializer_io

   T:	$response

   M:	The init_disk command specifed a disk pack
   which has a valid Multics label.
   It appears to be a copy of a mounted pack.
   Initializing this pack will destroy all data contained on it.

   A:	Do not answer yes unless you are sure that the pack contents should be destroyed.
   Check the pack serial number.


   Message:
   init_disk_pack_: Volume on DRIVE is an earlier instance of pv "PVNAME",
   .br
   last used DATE_TIME. Do you wish to overwrite it?

   S:	$initializer_io

   T:	$response

   M:	The init_disk command specifed a disk pack
   which has a valid Multics label.
   Initializing this pack will destroy all data contained on it.

   A:	Do not answer yes unless you are sure that the pack contents should be destroyed.
   Check the pack serial number.


   Message:
   init_disk_pack_: Volume on DRIVE is an unregistered pack named "PVNAME",
   .br
   last used DATE_TIME. Do you wish to overwrite it?

   S:	$initializer_io

   T:	$response

   M:	The init_disk command specifed a disk pack
   which has a valid Multics label.
   Initializing this pack will destroy all data contained on it.

   A:	Do not answer yes unless you are sure that the pack contents should be destroyed.
   Check the pack serial number.


   Message:
   init_disk_pack_: unrecognized argument: BLAH

   S:	$initializer_io

   T:	$response

   M:	Illegal input was typed.

   A:	$tryagn


   Message:
   volume PVNAME XX records

   S:	$initializer_io

   T:	$response

   M:	The physical volume PVNAME has been successfully initialized.
   There are XX (decimal) records in the paging area.

   A:	$ignore


   END MESSAGE DOCUMENTATION */

     end init_disk_pack_$parse_args;




		    lv_request_communicator_.pl1    11/11/89  1102.4rew 11/11/89  0803.1       51255



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1982 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */



lv_request_communicator_$alloc_lvate: proc (a_ec) returns (ptr);

/* Bernard Greenberg 9/8/76 */
/* Called by user-process RCP in ring 1, this procedure intermediates conversation with the
   lv_attach_table, talking to wdx in the Initializer process */


dcl 1 message based (addr (fixedmessage)),
    2 text char (4) unal,
    2 lvax fixed bin (17) unal,
    2 other fixed bin (17) unal;

dcl  SYSDIR char (168) static init (">lv");
dcl  LVAT_NAME char (32) static init ("lv_attach_table") options (constant);

dcl (error_table_$action_not_performed, error_table_$noalloc) fixed bin (35) ext;
dcl  error_table_$resource_unassigned fixed bin (35) ext;

dcl  fixedmessage fixed bin (71) aligned;
dcl  vhighest_used fixed bin (35);

dcl  hcs_$wakeup ext entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35));
dcl  hcs_$initiate entry (char (*), char (*), char (*), fixed bin (1), fixed bin (1), ptr, fixed bin (35));
dcl  get_process_id_ entry (bit (36) aligned);
dcl  admin_gate_$syserr entry options (variable);
dcl  stac builtin;
dcl  stacq entry (ptr, bit (36) aligned, bit (36) aligned) returns (bit (1) aligned);
dcl  lvname char (32);
dcl  code fixed bin (35);
dcl  s_lvatp ptr static init (null);
dcl  processid bit (36) aligned static;
dcl  ec fixed bin (35);
dcl  lvax fixed bin;

dcl  a_lvatep ptr;
dcl  a_evch fixed bin (71) aligned;
dcl  a_lvname char (*);
dcl  a_ec fixed bin (35);
dcl  a_state fixed bin;

dcl (null, rel, addr) builtin;



/*  */
	if s_lvatp = null then do;
	     call get_process_id_ (processid);
	     call hcs_$initiate (SYSDIR, LVAT_NAME, "", 0, 0, lvatp, ec);
	     if lvatp = null then do;
		a_ec = ec;
		return (null);
	     end;
	     s_lvatp = lvatp;
	end;

	lvatp = s_lvatp;
	do lvax = 1 to lvat.max_n_entries;
	     lvatep = addr (lvat.array (lvax));
	     if stac (addr (lvate.pid), processid) then do;

lll:		vhighest_used = lvat.highest_used;
		if lvax > vhighest_used then if ^stacq (addr (lvat.highest_used), unspec (vhighest_used), unspec (lvax))
		     then go to lll;
		string (lvate.flags) = "0"b;
		a_ec = 0;
		return (lvatep);
	     end;
	end;
	a_ec = error_table_$noalloc;
	return (null);

intent_to_mount: entry (a_lvatep, a_lvname, a_evch, a_ec);

	lvatep = a_lvatep;
	lvatp = s_lvatp;
	lvname = a_lvname;

	if lvate.pid ^= processid then go to unas;

	string (lvate.flags) = "0"b;

	lvate.lvname = lvname;			/* copy stuff first, */
	lvate.evchn = a_evch;
	lvate.state = 0;
	lvate.code = 0;
	lvate.pending_mount = "1"b;			/* flag is last */
	call transmit ("moun");			/* give guy a zetz */

	a_ec = 0;
	return;


check:	entry (a_lvatep, a_state, a_ec);

	lvatep = a_lvatep;
	lvatp = s_lvatp;

	if lvate.pid ^= processid then do;		/* got snarfed */
unasa:	     a_state = 3;
unas:	     a_ec = error_table_$resource_unassigned;
	     return;
	end;

	if lvate.invalidated then go to unasa;
	if lvate.mount_req_answered then do;
	     if lvate.waiting then a_state = 1;
	     else if lvate.code ^= 0 then a_state = 3;
	     else a_state = 0;
	     a_ec = lvate.code;
	end;
	else do;					/* No answer thru yet */
	     if lvate.pending_mount then a_state = 1;
	     else a_state = 3;
	     a_ec = 0;
	end;

	return;


intent_to_detach: entry (a_lvatep, a_ec);

	lvatep = a_lvatep;
	lvatp = s_lvatp;

	if lvate.pid ^= processid | lvate.invalidated then go to unas;
	lvate.detach_requested = "1"b;
	call transmit ("demo");
	a_ec = 0;
	return;

abandon:	entry (a_lvatep);

	lvatep = a_lvatep;
	lvatp = s_lvatp;

	string (lvate.flags) = "0"b;
	lvate.lvname = "";
	if ^stacq (addr (lvate.pid), processid, "0"b) then
	     call admin_gate_$syserr (0, "lv_request_communicator_: lock ^= processid lvate ^p", lvatep);

	return;

transmit:	procedure (m);

dcl  m char (*);

	     message.text = m;
	     message.lvax = 1 + divide (fixed (rel (lvatep), 18) - fixed (rel (addr (lvat.array)), 18),
		size (lvate), 17, 0);
	     message.other = 0;

	     call hcs_$wakeup (lvat.master_pid, lvat.master_evchn, fixedmessage, ec);
	     if ec ^= 0 then do;
		call admin_gate_$syserr (0, "lv_request_communicator_: code ^o from wakeup", code);
		a_ec = error_table_$action_not_performed;
		go to nlx;
	     end;
	end transmit;
nlx:	return;

test:	entry (testdir);

dcl  testdir char (*);
	SYSDIR = testdir;
	return;
						/*  */
%include lv_atttbl;


/* BEGIN MESSAGE DOCUMENTATION

   Message:
   lv_request_communicator_: lock ^= processid lvate PPP

   S: $info

   T: In response to a user's detach_lv command

   M: A logical volume attachment table entry became disassociated
   from a user to whom it had been assigned.
   $err

   A: $notify
   Be prepared to demount logical volumes manually which might otherwise
   have been demounted automatically.

   Message:
   lv_request_communicator_: code CCC from wakeup

   S: $info

   T: $run

   M: A wakeup could not be sent to the initializer process in order to
   perform communication about attachment/detachment of logical volumes.

   A: $notify

   END MESSAGE DOCUMENTATION */

     end;
 



		    mdx.pl1                         11/11/89  1102.4r w 11/11/89  0803.2      323658



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1984 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */


/****^  HISTORY COMMENTS:
  1) change(86-06-05,GJohnson), approve(86-06-05,MCR7387),
     audit(86-06-10,Martinson), install(86-07-17,MR12.0-1091):
     Correct error message documentation.
  2) change(86-06-30,Fawcett), approve(86-06-30,MCR7383),
     audit(86-06-30,LJAdams), install(86-07-17,MR12.0-1097):
     Add support for subvolumes, 3380 and 3390.
                                                   END HISTORY COMMENTS */

/* format: indattr,inddcls,dclind5,idind30,struclvlind5,ifthenstmt,ifthendo,^inditerdo,^indnoniterend,case,^tree,^indproc,^indend,^delnl,^insnl,comcol81,indcom,linecom,^indcomtxt */
mdx:
     proc;

	/* Guts of volume management.

   This program is the brains behind disk_table_.
   We run in ring 1 here and can mess with the disk table - but we don't print any messages.

   system_startup_ and sc_command_ call disk_table_ which can run in any ring and print.
   disk_table_ calls us through initializer_mdc_.

   THVV
   4/2/76 by Greenberg for demount and auto lv/pv vanishing
   12/4/76 by Greenberg for new lv vanish including demounted bits
   03/8/77 by Greenberg for static ac/dc disks and random refcount bugs
   July 1977 by T. Casey to add set_lv_pdir_bit entry
   Jan 1978 by B. Greenberg for prev_bootload bit, making lvs "mounting" at startup.
   July 1978 by B. Greenberg for hardcore RLV acceptance.
   July 1981 by J. Bongiovanni to validate lv indices
   March 1982 by J. Bongiovanni for new PVTE
   '82 for english disk error codes 
   831122 by E. A. Ranzenbach for set_vacate_pdirs_bit entry.
   August 1984 by Chris Jones to tell IOI when we add or delete a drive.
   84-09-04 by EJ Sharpe to change hdx refs to volume_registration_mgr_
	also changed check_hv entry to check_lv
   85-01-31 by EJ Sharpe to force-init DT entries for: 1) root PVs not on the
	ROOT config card (i.e. not in PVT at init time); and 2) root PVs
	have moved to a different drive (these used to be done incorrectly
	by "forgetter").  Also, clear all lve.pdirs_ok bits at initialization.
   1985-04-02, BIM: Indicate to caller of "take" when this call has completed
	     the rlv, so that the last add_vol of an root PV's crows
	     with success.
   05/07/85 by Chris Jones to let IOI decide when a drive can or cannot be reconfigured.
   08/28/85 by Rich Fawcett to support sub-volumes.
*/

	dcl  found		     bit (1);
	dcl  must_term		     bit (1) init ("0"b);
	dcl  (sdtp, stat_pvtp)	     ptr static init (null);
	dcl  ROOT			     char (168) static init (">");
	dcl  SL1			     char (168) static init (">system_library_1");
	dcl  i			     fixed bin;
	dcl  dvn			     char (8);
	dcl  (tpvtx, a_tpvtx)	     fixed bin;
	dcl  lvx			     fixed bin;
	dcl  prev_state		     bit (1);
	dcl  oldlev		     fixed bin;
	dcl  num_pic		     pic "99";
	dcl  pvt_n_entries		     fixed bin;
	dcl  xec			     fixed bin (35);
	dcl  reasonable_time	     fixed bin (71) int static init (2276881905645328);

	dcl  (addr, fixed, null, max, rel, rtrim, substr, unspec) builtin;

	dcl  admin_gate_$ioi_add_device    entry (char (*), fixed bin (35));
	dcl  admin_gate_$ioi_delete_device entry (char (*), fixed bin (35));
	dcl  (cu_$level_set, cu_$level_get) entry (fixed bin);
	dcl  initializer_gate_$add_pv_to_lv entry (bit (36) aligned, bit (36) aligned, fixed bin (35));
	dcl  initializer_gate_$read_disk   entry (fixed bin, fixed bin, ptr, fixed bin (35));
	dcl  initializer_gate_$define_lv   entry (ptr, fixed bin (35));
	dcl  initializer_gate_$delete_lv   entry (bit (36) aligned, fixed bin (35));
	dcl  initializer_gate_$set_disk_table_loc entry (ptr, fixed bin (35));
	dcl  initializer_gate_$accept_fs_disk entry (fixed bin, fixed bin (35));
	dcl  initializer_gate_$demount_pv  entry (fixed bin, fixed bin (35));
	dcl  initializer_gate_$vol_salv    entry (fixed bin, bit (36) aligned, fixed bin (35));
	dcl  initializer_gate_$ss_io_reconfigure entry (fixed bin, bit (1) aligned, fixed bin (35));
	dcl  verify_label_		     entry (ptr, ptr, fixed bin, fixed bin (35));
	dcl  volume_registration_mgr_$init entry (ptr);
	dcl  volume_registration_mgr_$test entry (char (*));
	dcl  volume_registration_mgr_$check_volume_registration entry (ptr, fixed bin (35));
	dcl  volume_registration_mgr_$get_lv_pvinfo entry (char (*), ptr, fixed bin, fixed bin, fixed bin (35));
	dcl  volume_registration_mgr_$find entry (ptr, fixed bin (35));
	dcl  hcs_$initiate		     entry (char (*), char (*), char (*), fixed bin (1), fixed bin (2), ptr, fixed bin (35));
	dcl  hcs_$terminate_noname	     entry (ptr, fixed bin (35));
	dcl  hcs_$make_seg		     entry (char (*), char (*), char (*), fixed bin (5), ptr, fixed bin (35));
	dcl  hcs_$truncate_seg	     entry (ptr, fixed bin, fixed bin (35));
	dcl  hcs_$add_acl_entries	     entry (char (*), char (*), ptr, fixed bin, fixed bin (35));
	dcl  rcp_control_$ss_io_interchange entry (char (*), bit (1) aligned, bit (1) aligned, fixed bin (35));
	dcl  admin_gate_$syserr	     ext entry options (variable);

	dcl  1 label_buffer		     like label;

	dcl  1 adte		     like dte aligned based (aux_dtep);
	dcl  1 alve		     like lve aligned based (aux_lvep);
	dcl  (aux_dtep, aux_lvep)	     ptr;

	dcl  1 aa			     aligned int static,
		2 name		     char (32) init ("*.*.*"),
		2 mode		     bit (36) init ("111"b),
		2 mbz		     bit (36) init ((36)"0"b),
		2 code		     fixed bin (35);

	dcl  sys_info$access_class_ceiling bit (72) ext;
	dcl  error_table_$fsdisk_not_salv  fixed bin (35) ext;
	dcl  error_table_$no_label	     fixed bin (35) ext;
	dcl  error_table_$invalid_state    fixed bin (35) ext;
	dcl  error_table_$bad_label	     fixed bin (35) ext;
	dcl  error_table_$argerr	     fixed bin (35) ext;
	dcl  error_table_$action_not_performed fixed bin (35) ext;
	dcl  error_table_$io_still_assnd   fixed bin (35) ext;
	dcl  error_table_$logical_volume_not_defined fixed bin (35) ext;
	dcl  error_table_$mount_not_ready  fixed bin (35) ext;
	dcl  error_table_$private_volume   fixed bin (35) ext;

	/* -------------------------------------------------------- */

	/* This entry is the guts of disk_table_$init. Actually we could call ioa_ here but don't. */

init: entry (a_dtp, new, ec);

	dcl  a_dtp		     ptr, new bit (1), ec fixed bin (35);

	ec = 0;
	new = "0"b;
	call hcs_$make_seg (ROOT, "disk_table", "", 1010b, dtp, xec);
	if dtp = null then do;					/* Must have one */
		ec = xec;						/* quelle horreur */
		return;
	     end;
	call hcs_$add_acl_entries (ROOT, "disk_table", addr (aa), 1, xec);

	call initializer_gate_$set_disk_table_loc (dtp, xec);		/* Tell BOS where it is (fsout too) */

	call hcs_$initiate (SL1, "pvt", "", 0, 1, pvtp, xec);
	if pvtp = null then do;
		ec = xec;						/* system is broken */
		return;
	     end;
	stat_pvtp = pvtp;						/* save value for use in copying pvt.salv bit */
	sdtp = dtp;						/* Save static copy for other ep's */
	call volume_registration_mgr_$init (dtp);
	a_dtp = dtp;

	/* Does the PVT match the Disk Table? */

	pvt_n_entries = pvt.n_entries;
	pvt_arrayp = addr (pvt.array);

	if dt.version ^= 1 then ec = 3;
	else if pvt_n_entries = dt.n_entries then do;			/* Must be same size */

		do i = 1 to dt.n_lv_entries;				/* Cause lv table to be garbage-collected */
		     lvep = addr (dt.lv_array (i));
		     lve.mounting = "0"b;				/* As to not induce mhv's */
		     lve.demounted_only = "0"b;			/* develop this later */
		     lve.demounting = "0"b;				/* As to not induce dhv's */
		     lve.used = "0"b;				/* So that gc will work */
		     lve.hv_mounted = "0"b;				/* not until we know better */
		     lve.pdirs_ok = "0"b;				/* site should set these each bootload */
		end;

		do i = 1 to pvt_n_entries;				/* Find hardcore-accepted volumes, scratch old DT entries. */
		     pvtep = addr (pvt_array (i));
		     dtep = addr (dt.array (i));
		     dte.rpv = "0"b;				/* Will be set by make_root_pve if true */
		     if pvte.used then call make_root_pve;
		     else dte.hc_accepted = "0"b;
		end;

		do i = 1 to pvt_n_entries;				/* check all entries xcept PD */
		     pvtep = addr (pvt_array (i));
		     dtep = addr (dt.array (i));
		     dte.need_salvage = "0"b;
		     if pvte.storage_system ^= dte.storage_system then do;
			     dte.known, dte.used, dte.demounted, dte.pre_accepted = "0"b;
			     dte.storage_system = pvte.storage_system;
			end;
		     if dte.deleted | ^dte.storage_system
		     then dte.used, dte.known, dte.pre_accepted, dte.demounted = "0"b;
		     num_pic = pvte.logical_area_number;
		     dvn = pvte.devname || "_" || num_pic;
		     if pvte.is_sv then dvn = rtrim (dvn) || rtrim (pvte.sv_name);
		     lvx = dte.lvx;					/* Can't be TOO bad. */
		     lvep = addr (dt.lv_array (lvx));
		     if dvn ^= dte.drive_name then ec = 4;
		     else if pvte.device_type ^= dte.device_type then ec = 5;
		     else if dte.deleted then dte.deleted = "0"b;		/* Cancel previous "deld" */
		     else if lvx <= 0 then goto skip_lv;		/* invalid index -- don't diddle lve		*/
		     else if dte.used then do;			/* good thing last time? */
			     if dte.hc_accepted
			     then call volume_registration_mgr_$check_volume_registration (dtep, xec); /* re-register root pack */
			     else if dte.lvx = 1 then do;
								/* root PV that is not on ROOT card */
				     call admin_gate_$syserr (BEEP, "disk_table_: Deleted root PV ""^a"" on ^a.", dte.pvname, dte.drive_name);
				     call force_init_dte (i);
				end;
			     else do;				/* Other volumes are "pre-accepted" (ie assumed) */
estab_lv:				     call make_assumed;
				     lve.demounted_only = "0"b;
				     if dte.lvx ^= 1 then lve.mounting = "1"b;
				     lve.prev_bootload = "1"b;
suggest_lv:			     lve.used = "1"b;
				     dt.n_lv_entries = max (dt.n_lv_entries, dte.lvx);
				end;
			end;
		     else if dte.known | dte.pre_accepted then go to estab_lv;
		     else if dte.demounted then do;
			     if ^lve.used then lve.demounted_only = "1"b;
			     go to suggest_lv;			/* good vol will turn off, */
								/* bad vols wont turn on */
			end;
		     else ;					/* Idle last time */
skip_lv:
		end;
		if ec = 0 then return;				/* If disk config didn't change we exit normally */
	     end;

	else if dt.n_entries = 0 then do;				/* Did we create a new disk table */
		ec = 1;						/*  Yes */
	     end;

	else do;							/* Some other mismatch */
		ec = 2;						/* disk config changed. rebuild whole thing */
	     end;

	call hcs_$truncate_seg (dtp, 0, xec);				/* Throw away all old stuff */
	dt.version = 1;						/* .. and make new */
	new = "1"b;
	dt.n_entries = pvt_n_entries;					/*  Discount the paging device */
	dt.max_n_entries = pvt.max_n_entries;
	dt.n_lv_entries = 0;
	do i = 1 to dt.n_entries;					/* Make new table */
	     pvtep = addr (pvt_array (i));
	     dtep = addr (dt.array (i));
	     call force_init_dte (i);
	     if pvte.used then do;
		     call make_root_pve;				/* Kosher by definition */
		     call volume_registration_mgr_$check_volume_registration (dtep, xec);
								/* Make sure the rpv is registered */
		end;
	end;

	return;

	/* --------------------------------------------------------- */

take: entry (a_pvname, a_indx, took, ec);

	dcl  a_pvname		     char (*);
	dcl  a_indx		     fixed bin;
	dcl  took			     bit (1);

	ec = 0;
	dtp = sdtp;
	i = a_indx;
	dtep = addr (dt.array (i));
	took = "0"b;
	if ^dte.storage_system | dte.deleted | dte.used then do;
fail:		ec = error_table_$argerr;
		return;
	     end;

	call verify_drive_vanish_pve (dtep, a_pvname, ec);
	if ec ^= 0 then return;					/* Consistentize disk table */

	dte.pvname = a_pvname;
	if ^dte.known then do;
		call volume_registration_mgr_$find (dtep, ec);
		if ec ^= 0 then do;
			call make_demounted;
			return;
		     end;
		if ^get_it_known (i, ec) then do;
			call make_demounted;
			return;
		     end;
	     end;
	lvep = addr (dt.lv_array (dte.lvx));
	if lve.hv_mounted then do;					/* Adding vol to live lv (eg root) */
		call initializer_gate_$accept_fs_disk (i, ec);
		if ec = error_table_$fsdisk_not_salv then do;		/* salvage if needed */
			call initializer_gate_$vol_salv (i, ""b, ec);
			call initializer_gate_$accept_fs_disk (i, ec);
		     end;
		if ec = 0 then do;
			call make_used;
			call initializer_gate_$add_pv_to_lv (lve.lvid, dte.pvid, ec);
		     end;
		call check_lv_complete (xec);				/* Always try to announce success in completing the RLV */
		if xec = 0 then do;
			call verify_whole_lv (1, xec);
			took = (xec = 0);
		     end;
	     end;
	else do;
		i = dte.lvx;
		call check_lv_complete (xec);
		if xec = 0 & lve.mounting then do;			/* If vol complete AND mhv was called */
			call verify_whole_lv (i, ec);
			if ec = 0 then took = "1"b;
		     end;
	     end;
	return;

	/* ------------------------------------------------------ */

assert: entry (a_pvname, a_indx, ec);

	ec = 0;
	dtp = sdtp;
	i = a_indx;
	dtep = addr (dt.array (i));
	if ^dte.storage_system | dte.deleted | dte.used then go to fail;
	call verify_drive_vanish_pve (dtep, a_pvname, ec);
	if ec ^= 0 then return;

	dte.pvname = a_pvname;
	call volume_registration_mgr_$find (dtep, ec);
	if ec = 0 then call make_assumed;
	else call make_blank;
	return;

	/* --------------------------------------------------------- */

forget: entry (a_indx, ec);

	ec = 0;
	dtp = sdtp;
	i = a_indx;
	dtep = addr (dt.array (i));
	if ^dte.storage_system | dte.deleted | dte.used then go to fail;
	call make_blank;
	return;

	/* -------------------------------------------------------- */

volsalv: entry (a_pvname, a_indx, opt, ec);

	dcl  opt			     bit (36) aligned;

	ec = 0;
	dtp = sdtp;
	i = a_indx;
	dtep = addr (dt.array (i));
	if ^dte.storage_system | dte.deleted | dte.used then go to fail;

	call verify_drive_vanish_pve (dtep, a_pvname, ec);
	if ec ^= 0 then return;

	dte.pvname = a_pvname;
	call volume_registration_mgr_$find (dtep, ec);
	if ec ^= 0 then return;
	call verify_label_ (dtp, dtep, i, ec);
	if ec ^= 0 then return;
	call make_known;

	call initializer_gate_$vol_salv (i, opt, ec);
	return;

	/* ---------------------------------------------------------- */

rlvolcheck: entry (a_indx, a_pvtx, opt, ec);				/* allow connection volsalv of copies of rlv members */
								/* a_pvtx is copy, a_indx is real one */

	dcl  a_pvtx		     fixed bin,
	     rdtep		     ptr;

	i = a_pvtx;
	dtp = sdtp;
	dtep = addr (dt.array (i));					/* dtep -> copy being salved */
	rdtep = addr (dt.array (a_indx));				/* rdtep is place where live one lives */

	if dte.deleted | ^dte.storage_system | dte.used then go to fail;
	if ^(rdtep -> dte.used | rdtep -> dte.known) then go to fail;


	call verify_label_ (dtp, rdtep, i, ec);				/* LIE to verify_label_: info for live, read phony */
	if ec ^= 0 then return;

	call make_blank;						/* Bash away stuff, can't be right */


	call initializer_gate_$vol_salv (i, opt, ec);

	return;

	/* --------------------------------------------------------- */

mhv: entry (a_indx, a_tpvtx, ec);

	ec = 0;
	tpvtx, a_tpvtx = 0;
	dtp = sdtp;
	lvep = addr (dt.lv_array (a_indx));

	if (lve.hv_mounted & ^(a_indx = 1))				/* ok doublemount rlv */
	     | (lve.mounting & ^lve.prev_bootload)			/* autograb last-time's */
	     | lve.demounting | ^lve.used then go to fail;

	if a_indx ^= 1 then lve.mounting = "1"b;
	call check_lv_complete (ec);
	if ec = 0 | ec = -1 then do;					/* Complete or completeable? */
		call verify_whole_lv (a_indx, ec);
	     end;
	a_tpvtx = tpvtx;
	return;

	/* --------------------------------------------------------- */

reregister: entry (a_pvname, a_indx, ec);				/* Called when opr asserts disk is ok */

	ec = 0;
	dtp = sdtp;
	dtep = addr (dt.array (a_indx));

	if ^dte.storage_system | dte.used | dte.deleted then go to fail;

	call verify_drive_vanish_pve (dtep, a_pvname, ec);
	if ec ^= 0 then return;

	labelp = addr (label_buffer);
	call initializer_gate_$read_disk (a_indx, LABEL_ADDR, labelp, ec);
	if ec ^= 0 then return;

	ec = error_table_$no_label;
	if label.Multics ^= Multics_ID_String then return;
	if label.version ^= 1 then return;
	if label.time_registered < reasonable_time then return;
	ec = error_table_$bad_label;
	if label.pv_name ^= a_pvname then return;
	dte.pvname = label.pv_name;
	dte.pvid = label.pvid;
	do i = 1 to dt.n_lv_entries while (^dt.lv_array (i).used | dt.lv_array (i).lvname ^= label.lv_name); end;
	dte.lvx = i;
	lvep = addr (dt.lv_array (dte.lvx));
	if dte.lvx > dt.n_lv_entries then do;
		dt.n_lv_entries = dt.n_lv_entries + 1;
		unspec (lve) = ""b;
		lve.lvname = label.lv_name;
		lve.lvid = label.lvid;
		lve.max_access_class = label.max_access_class;
		lve.min_access_class = label.min_access_class;
		lve.public = ^label.private;
		lve.used = "1"b;
	     end;
	call volume_registration_mgr_$check_volume_registration (dtep, ec);
	if ec ^= 0 then return;
	call make_known;
	return;


	/* --------------------------------------------------------- */

read_disk_table: entry (spacep, ec);

	dcl  spacep		     ptr;
	dcl  move_len		     fixed bin (18);
	dcl  move_table		     (move_len) fixed bin (35) aligned based;

	if ^get_local_dtp () then return;
	ec = 0;
	move_len = fixed (rel (addr (dt.lv_array (dt.n_lv_entries + 1))), 18);
	spacep -> move_table = dtp -> move_table;
	go to term_exit;


	/* --------------------------------------------------------- */

check_pv: entry (a_pvname, ec);

	if ^get_local_dtp () then return;

	found = "0"b;
	do i = 1 to dt.n_entries while (^found);
	     dtep = addr (dt.array (i));
	     if a_pvname = dte.pvname & (dte.used | dte.known) then found = "1"b;
								/* oughtta take out of tbl here */
	end;
	go to found_exit;

	/* ------------------------------------------------------- */


check_lv: entry (a_hvname, ec);

	dcl  a_hvname		     char (*);

	if ^get_local_dtp () then return;

	found = "0"b;
	do i = 1 to dt.n_lv_entries while (^found);
	     if dt.lv_array (i).lvname = a_hvname & dt.lv_array (i).used & dt.lv_array (i).hv_mounted then found = "1"b;
	end;
found_exit:
	if ^found then ec = error_table_$mount_not_ready;
	else ec = 0;
term_exit:
	if must_term then do;
		call cu_$level_set (oldlev);
		call hcs_$terminate_noname (dtp, (0));
	     end;
	return;

	/* --------------------------------------------------------- */


set_lv_pdir_bit: entry (a_hvname, a_bit, ec);

	dcl  a_bit		     bit (1) aligned;

	ec = 0;							/* until something goes wrong */
	if ^get_local_dtp () then return;
	found = ""b;
	do i = 1 to dt.n_lv_entries while (^found);
	     if dt.lv_array (i).lvname = a_hvname & dt.lv_array (i).used then do;
		     found = "1"b;
		     if dt.lv_array (i).public then
			dt.lv_array (i).pdirs_ok = a_bit;
		     else ec = error_table_$private_volume;
		end;
	end;
	if ^found then
	     ec = error_table_$mount_not_ready;
	return;


	/* --------------------------------------------------------- */


set_vacate_pdirs_bit: entry (a_hvname, a_bit, ec);

	ec = 0;							/* until something goes wrong */
	if ^get_local_dtp () then return;
	found = ""b;
	do i = 1 to dt.n_lv_entries while (^found);
	     if dt.lv_array (i).lvname = a_hvname & dt.lv_array (i).used then do;
		     found = "1"b;
		     if dt.lv_array (i).public then do;
			     dt.lv_array (i).vacate_pdirs = a_bit;
			     if dt.lv_array (i).pdirs_ok & a_bit then dt.lv_array (i).pdirs_ok = "0"b;
			end;
		     else ec = error_table_$private_volume;
		end;
	end;
	if ^found then
	     ec = error_table_$mount_not_ready;
	return;


	/* --------------------------------------------------------- */

add_del: entry (a_device_name, a_adding_drive, true_if_found, ec);

	dcl  a_device_name		     char (*),
	     a_adding_drive		     bit (1) aligned,
	     true_if_found		     bit (1) aligned;
	dcl  adding_drive		     bit (1);			/* "0"b if deleting device "1"b if adding device */

	adding_drive = a_adding_drive;
	dtp = sdtp;
	ec = 0;
	true_if_found = "0"b;
	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if dte.drive_name = a_device_name then do;
		     true_if_found = dte.storage_system;
		     if dte.used | dte.known then ec = error_table_$io_still_assnd;
		     else if dte.deleted ^= adding_drive then ec = error_table_$action_not_performed;
		     else do;
			     if adding_drive then
				call admin_gate_$ioi_add_device ((dte.drive_name), ec);
			     else call admin_gate_$ioi_delete_device ((dte.drive_name), ec);
			     if ec = 0 then do;
				     dte.deleted = ^adding_drive;
				     if dte.storage_system then do;
					     call make_blank;
					     call admin_gate_$syserr
						(ANNOUNCE, "disk_table_: ^[Added^;Deleted^] drive ^a.", (adding_drive), (a_device_name));
					end;
			        end;
			end;
		end;
	end;
	return;

	/* -------------------------------------------------------- */

ss_io_reconfig: entry (a_pvtx, a_from_ss, ec);

	dcl  a_from_ss		     bit (1);
	dcl  from_ss		     bit (1) aligned;

	from_ss = a_from_ss;
	i = a_pvtx;
	dtp = sdtp;
	dtep = addr (dt.array (i));
	xec = 0;
	if from_ss ^= dte.storage_system then do;
		ec = error_table_$invalid_state;
		return;
	     end;
	if from_ss then if dte.known | dte.used then do;
		     ec = error_table_$io_still_assnd;
		     return;
		end;
	call make_blank;
	call cu_$level_get (oldlev);
	call cu_$level_set (1);
	if dte.is_sub_vol then do;
								/* if this is a drive that contains subvolumes only tell rcp about the last one */
		if dte.sv_num = (dte.num_of_sv - 1) then
								/* This should be the last one */
		     call rcp_control_$ss_io_interchange ((substr (dte.drive_name, 1, 7)), from_ss, (dte.deleted), xec);
	     end;
	else call rcp_control_$ss_io_interchange ((dte.drive_name), from_ss, (dte.deleted), xec);
	call cu_$level_set (oldlev);
	if xec = 0 then call initializer_gate_$ss_io_reconfigure (i, from_ss, xec);
	if xec = 0 then dte.storage_system = ^from_ss;
	ec = xec;
	return;

	/* --------------------------------------------------------- */

demount_pv: entry (a_pvtx, ec);

	dtp = sdtp;
	dtep = addr (dt.array (a_pvtx));

	if ^dte.storage_system | ^dte.used then do;
		ec = error_table_$action_not_performed;
		return;
	     end;

	lvx = dte.lvx;
	if lvx < 0 | lvx > dt.n_lv_entries then do;
		ec = error_table_$action_not_performed;
		return;
	     end;
	lvep = addr (dt.lv_array (lvx));

	if ^lve.demounting then do;
		ec = error_table_$io_still_assnd;
		return;
	     end;

	call initializer_gate_$demount_pv (a_pvtx, ec);

	if ec = 0 then call make_demounted;

	return;

	/* --------------------------------------------------------- */

demount_lv: entry (a_lvx, a_dhv_act, ec);

	dcl  a_lvx		     fixed bin, a_dhv_act fixed bin;

	dtp = sdtp;
	lvx = a_lvx;
	lvep = addr (dt.lv_array (lvx));
	if ^lve.used then do;
		lve.hv_mounted, lve.demounting, lve.mounting = "0"b;	/* for robustness */
		ec = error_table_$action_not_performed;
		return;
	     end;

	if lvx = dt.array (dt.rpvx).lvx
	     | (lve.pdirs_ok & lve.hv_mounted) then do;
		ec = error_table_$action_not_performed;
		return;
	     end;

	ec = 0;							/* Unless otherwise */

	if a_dhv_act = 1 then do;					/* Stop a mhv */
		if ^lve.mounting then go to fail;			/* argerr */
		lve.mounting = "0"b;
		do i = 1 to dt.n_entries while (^(dt.array (i).used & dt.array (i).lvx = lvx)); end;
		if i <= dt.n_entries then lve.demounting = "1"b;
	     end;
	else if a_dhv_act = 2 then do;				/* Demount start, take out of r0 */
		if lve.mounting then go to fail;
		if ^lve.hv_mounted then go to fail;
		call initializer_gate_$delete_lv (lve.lvid, ec);
		if ec = error_table_$logical_volume_not_defined then ec = 0;/* Ignore not-there */
		lve.demounting = "1"b;
		lve.hv_mounted = "0"b;
	     end;
	else if a_dhv_act = 3 then do;				/* Turn off demounting */
		if ^lve.demounting then go to fail;
		lve.demounting = "0"b;
	     end;

	if ec ^= 0 then return;

	do i = 1 to dt.n_entries;					/* Make Ops happy, erase the thing. */
	     dtep = addr (dt.array (i));
	     if dte.pre_accepted & (dte.lvx = lvx) then call make_demounted;
	end;
	if ^lve.used then return;

	lve.mounting = "0"b;					/* General principles, minimize screwups */
	call develop_lve_status_anon;					/* Check for weird case */

	return;
%page;
	/*	SUBROUTINES AND OTHER UTILE CONSTRUCTIONS	*/


get_it_known: proc (b_dtx, b_ec) returns (bit (1));

	/* Call verify_label, leave drive hungry or known */

	dcl  b_dtx		     fixed bin;			/* passed for convenience */
	dcl  b_ec			     fixed bin (35);

	call verify_label_ (dtp, dtep, b_dtx, b_ec);
	if b_ec = 0 then do;
		call make_known;
		return ("1"b);
	     end;
	return ("0"b);

     end get_it_known;


verify_drive_vanish_pve: proc (b_dtep, b_pvname, ec);
	dcl  ec			     fixed bin (35);
	dcl  b_dtep		     ptr;
	dcl  b_pvname		     char (*);
	dcl  bpk			     fixed bin;
	dcl  save_dtep		     ptr;
	dcl  k			     fixed bin;

	do k = 1 to dt.n_entries;
	     aux_dtep = addr (dt.array (k));
	     if aux_dtep = b_dtep then bpk = k;
	     else do;
		     if (adte.used | adte.known) & adte.pvname = b_pvname
		     then do;
			     ec = error_table_$io_still_assnd;
			     return;
			end;
		     if (adte.pre_accepted | adte.demounted) & adte.pvname = b_pvname then call forgetter (k);
		end;
	end;
	aux_dtep = b_dtep;
	if adte.used | adte.known then if adte.pvname = b_pvname then ec = 0;
	     else ec = error_table_$io_still_assnd;
	else if adte.pre_accepted | adte.demounted then call forgetter (bpk);
	if ec = 0 then do;
		save_dtep = dtep;
		dtep = b_dtep;					/* This is what lambda was invented for */
		call make_blank;
		dtep = save_dtep;
	     end;
     end;


forgetter: entry (a_indx);						/* recursive only */
	dtp = sdtp;
	dtep = addr (dt.array (a_indx));
	call make_blank;
	return;
%page;

make_root_pve:
     proc;

	dcl  mrpve_pvtx		     fixed bin;

	/* This procedure is called to construct DT PV entries for volumes
   accepted by the hardcore during initialization.  PVT and DT pointers
   are valid. */

	dte.storage_system = "1"b;					/* Get good params going */
	dte.deleted = "0"b;
	dte.rpv = pvte.rpv;						/* Get rpv bit */
	dte.pvid = pvte.pvid;
	if dte.rpv then dt.rpvx = i;
	dte.lvx = 1;
	dte.permanent = "1"b;
	call make_root_lve;
	call verify_label_ (dtp, dtep, i, ec);

	/* Special kludge in verify_label_ will cause name fillin into DT instead of check.  lvname = root triggers this. */

	dte.hc_accepted = "1"b;					/* Cause comparator to ignore. */
	call make_used;

	do mrpve_pvtx = 1 to dt.n_entries;

	     /* Since this is the first pass of mdx init, flush all other claims to this volume, regardless of
   how strong they are. */

	     aux_dtep = addr (dt.array (mrpve_pvtx));
	     if aux_dtep ^= dtep then do;				/* Don't consider self */
		     if adte.pvname = dte.pvname | adte.pvid = dte.pvid
		     then do;
			     call admin_gate_$syserr (ANNOUNCE, "disk_table_: Root PV ""^a"" moved from ^a to ^a.",
				dte.pvname, adte.drive_name, dte.drive_name);
			     call force_init_dte (mrpve_pvtx);
			end;
		end;
	end;
     end;


make_root_lve: proc;

	lvep = addr (dt.lv_array (1));
	if lve.lvname ^= "root" | ^lve.used then do;
		dt.n_lv_entries = 1;
		unspec (lve) = ""b;
		lve.public = "1"b;
		lve.lvid = pvte.lvid;
		lve.lvname = "root";
		lve.min_access_class = ""b;
		lve.max_access_class = sys_info$access_class_ceiling;
	     end;
	lve.used = "1"b;						/* gc turned off */
	lve.hv_mounted = "1"b;

     end make_root_lve;


force_init_dte:							/* procedure used during disk_table initialzation to clear a particular entry */
     proc (dte_index);

	dcl  dte_index		     fixed bin parameter;
	dcl  l_dtep		     ptr;
	dcl  1 l_dte		     aligned like dte based (l_dtep);
	dcl  l_pvtep		     ptr;
	dcl  1 l_pvte		     aligned like pvte based (l_pvtep);

	l_dtep = addr (dt.array (dte_index));
	l_pvtep = addr (pvt_array (dte_index));
	unspec (l_dte) = ""b;
	num_pic = l_pvte.logical_area_number;
	l_dte.is_sub_vol = l_pvte.is_sv;
	l_dte.sv_num = l_pvte.sv_num;
	l_dte.drive_name = l_pvte.devname || "_" || num_pic;
	if l_dte.is_sub_vol then l_dte.drive_name = rtrim (l_dte.drive_name) || rtrim (l_pvte.sv_name);
	l_dte.device_type = l_pvte.device_type;
	l_dte.storage_system = l_pvte.storage_system;
	l_dte.permanent = l_pvte.permanent;
	l_dte.num_of_sv = l_pvte.num_of_svs;
	l_dte.sv_name = l_pvte.sv_name;
	return;

     end force_init_dte;
%page;

verify_whole_lv: proc (desired_lvx, ec);

	/* This procedure is called when it is desired to mount an entire hierarchy volume,
   and all constituent pv's are either pre_accepted, known, or
   in use (from a previous failing attempt).  It tries to accept them all, promoting
   all drives up through the states.  As is the convention in mdx, any time
   a label is read which contradicts a pre_accepted, the latter is turned off. No volumes
   are promoted to used (ring 0 called) unless all labels check. */


	dcl  desired_lvx		     fixed bin, ec fixed bin (35);

	dcl  i			     fixed bin;
	dcl  1 local_lvte		     aligned like lvte;

	lvep = addr (dt.lv_array (desired_lvx));
	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if dte.lvx = desired_lvx & dte.pre_accepted then do;
		     if ^get_it_known (i, ec) then do;
			     tpvtx = i;
			     call make_assumed;
			     return;
			end;
		end;
	end;
	do i = 1 to dt.n_entries;
	     dtep = addr (dt.array (i));
	     if dte.lvx = desired_lvx & dte.known then do;
		     call initializer_gate_$accept_fs_disk (i, ec);
		     if ec = error_table_$fsdisk_not_salv then do;	/* salvage if needed */
			     call initializer_gate_$vol_salv (i, ""b, ec);
			     call initializer_gate_$accept_fs_disk (i, ec);
			end;

		     if ec ^= 0 then do;
			     tpvtx = i;
			     return;
			end;
		     call make_used;
		     if lve.hv_mounted then call initializer_gate_$add_pv_to_lv (lve.lvid, dte.pvid, ec);
		end;
	end;
	if ^lve.hv_mounted then do;					/* Want to mount new vol? */
		unspec (local_lvte) = "0"b;
		local_lvte.lvid = lve.lvid;
		local_lvte.access_class.min = lve.min_access_class;
		local_lvte.access_class.max = lve.max_access_class;
		local_lvte.public = lve.public;
		call initializer_gate_$define_lv (addr (local_lvte), ec);
		if ec = 0 then do;
			lve.hv_mounted = "1"b;
			lve.mounting = "0"b;
		     end;
	     end;

     end verify_whole_lv;
%page;

check_lv_complete: proc (ec);

	/* This procedure checks the registration for a given LV, and determines if they are all in the
   disk table. */

	dcl  ec			     fixed bin (35);

	dcl  pvap			     ptr;
	dcl  npv			     fixed bin;
	dcl  (i, j)		     fixed bin;
	dcl  (found, ready)		     bit (1);
	dcl  1 pva		     (100) based (pvap) aligned,
		2 pvname		     char (32),
		2 device_type	     fixed bin,
		2 pad		     fixed bin;

	pvap = addr (dt.lv_array (dt.max_n_entries + 1));
	call volume_registration_mgr_$get_lv_pvinfo ((lve.lvname), pvap, npv, (0), ec);
	if ec ^= 0 then return;

	ec = 0;
	ready = "1"b;
	do i = 1 to npv while (ec = 0);				/* Look for any unmounted vol */
	     found = "0"b;
	     do j = 1 to dt.n_entries while (^found);
		dtep = addr (dt.array (j));
		if dte.pvname = pva (i).pvname
		then if dte.used | dte.known then found = "1"b;
		     else if dte.pre_accepted then do;
			     found = "1"b;
			     ready = "0"b;
			end;
	     end;
	     if ^found then ec = 1;
	end;
	if ec = 0 & ^ready then ec = -1;

     end check_lv_complete;
%page;

	/*	SUBROUTINES TO PERFORM STATE TRANSIT		*/

	/* All assume dtep is set */

make_blank: proc;

	call hold_status;
	dte.pre_accepted, dte.used, dte.known, dte.demounted = "0"b;
	call develop_lve_status;
     end;

make_assumed: proc;

	dte.used, dte.known, dte.demounted, dte.used = "0"b;
	dte.pre_accepted = "1"b;
	if dte.lvx > 0 & dte.lvx <= dt.n_lv_entries then
	     dt.lv_array (dte.lvx).demounted_only = "0"b;
     end;

make_demounted: proc;

	prev_state = "1"b;						/* lvx must be good */
	dte.pre_accepted, dte.used, dte.known = "0"b;
	dte.demounted = "1"b;
	call develop_lve_status;
     end;

make_known: proc;

	dte.pre_accepted, dte.used, dte.demounted = "0"b;
	dte.known = "1"b;
	if dte.lvx > 0 & dte.lvx <= dt.n_lv_entries then
	     dt.lv_array (dte.lvx).demounted_only = "0"b;
     end;

make_used: proc;

	dte.pre_accepted, dte.demounted, dte.known = "0"b;
	if ^dte.used then call admin_gate_$syserr
		(LOG, "disk_table_: accepted PV ^a on ^a.", dte.pvname, dte.drive_name);
	dte.used = "1"b;
	if dte.lvx > 0 & dte.lvx <= dt.n_lv_entries then
	     dt.lv_array (dte.lvx).demounted_only = "0"b;
     end;

hold_status: proc;
	prev_state = dte.used | dte.pre_accepted | dte.known | dte.demounted;
     end;

develop_lve_status: proc;

	dcl  ii			     fixed bin;
	dcl  l_lvx		     fixed bin;
	dcl  hold			     bit (1) init ("0"b);

	if ^prev_state then do;
		dte.lvx = -2;					/* sure asdmp */
		return;						/* dont check garbage */
	     end;
	l_lvx = dte.lvx;
	go to join;

develop_lve_status_anon: entry;
	l_lvx = lvx;						/* global */
join:	if l_lvx < 0 | l_lvx > dt.n_lv_entries then return;
	aux_lvep = addr (dt.lv_array (l_lvx));
	do ii = 1 to dt.n_entries;
	     aux_dtep = addr (dt.array (ii));
	     if adte.lvx = l_lvx then do;
		     if adte.demounted then hold = "1"b;
		     else if adte.used | adte.known | adte.pre_accepted then do;
			     alve.demounted_only = "0"b;
			     return;
			end;
		end;
	end;

	if hold then do;						/* He thinks its vanished */
		alve.demounted_only = "1"b;
		alve.prev_bootload = "0"b;				/* No more special privileges */
	     end;
	if alve.mounting then return;
	if ^hold then do;
		alve.used = "0"b;
		alve.lvid = "0"b;
	     end;

     end develop_lve_status;
%page;

get_local_dtp: proc returns (bit (1));

	dtp = sdtp;
	must_term = "0"b;
	if dtp = null () then do;					/* we're not Inz */
		call cu_$level_get (oldlev);
		call cu_$level_set (1);
		call hcs_$initiate (ROOT, "disk_table", "", 0, 1, dtp, ec);
		if dtp = null () then do;
			call cu_$level_set (oldlev);
			return ("0"b);
		     end;
		must_term = "1"b;
	     end;
	return ("1"b);

     end;
test: entry (aroot);

	dcl  aroot		     char (*);

	SL1, ROOT = aroot;
	call volume_registration_mgr_$test (aroot);
	return;
								/* format: off */
%page; %include disk_table;
%page; %include lvt;
%page; %include pvt;
%page; %include pvte;
%page; %include fs_vol_label;
%page; %include disk_pack;
%page; %include syserr_constants;
%page;
	/* BEGIN MESSAGE DOCUMENTATION

   Message:
   disk_table_: Added drive DSKX_NN

   S:	$info

   T:	$response

   M:	The operator has added a storage system disk drive with adddev.

   A:	$ignore

   Message:
   disk_table_: Deleted drive DSKX_NN

   S:	$info

   T:	$response

   M:	The operator has deleted a storage system disk drive with deldev.

   A:	$ignore

   Message:
   disk_table_: accepted PV PVNAME on DSKX_NN

   S:	$log

   T:	$response
   $init

   M:	A storage system physical volume has been placed in use.
   The volume and drive name are identified.

   A:	$ignore
   This message is provided for the benefit of
   automatic error analysis and logging.


   Message:
   disk_table_: Deleted root PV "PV_NAME" on DSKX_NN.

   S:	$beep

   T:	$init

   M:	The specified drive did not appear in the ROOT config card.  Thus,
   it was removed from the disk_table_.  This will also occur when the
   volume is intentionally deleted.

   A:	$notify


   Message:
   disk_table_: Root PV "PV_NAME" moved from DSKX_NN to DSKX_NN.

   S:	$info

   T:	$init

   M:	The specified root volume has been moved to a different drive
   since the last shutdown.

   A:	$ignore


   END MESSAGE DOCUMENTATION */

     end mdx;
  



		    salv_caller.pl1                 11/11/89  1102.4rew 11/11/89  0803.2       97128



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1982 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */


salv_caller: procedure;
	return;

/* salv_caller	-- Module to implement administrative salvager commands */
/* Bernard Greenberg, April 6 1976 */
/* Modified 7/77 by S.E. Barr to delete branch checking. */

dcl  cu_$arg_count entry returns (fixed bin);
dcl  cu_$arg_ptr entry (fixed bin, ptr, fixed bin, fixed bin (35));
dcl  code fixed bin (35);
dcl  arg char (argl) based (argp);
dcl  argp ptr, argl fixed bin;
dcl  SALV_RING fixed bin static init (1);
dcl  cv_dec_check_ entry (char (*), fixed bin (35), fixed bin);
dcl  i fixed bin;
dcl  n fixed bin;


dcl  disk_table_$general_mhv entry (fixed bin (35));
dcl  disk_table_$accept_rlv entry (fixed bin (35));
dcl  disk_table_$rbld_disk entry (char (*), char (*), char (*), bit (36) aligned, fixed bin (35));
dcl  disk_table_$volsalv entry (char (*), char (*), bit (36) aligned, fixed bin (35));
dcl  disk_table_$volsalvall entry (bit (36) aligned, fixed bin (35));
dcl  disk_table_$volrlvcheck entry (char (*), char (*), char (*), bit (36) aligned, fixed bin (35));
dcl  initializer_gate_$salvager entry (bit (36) aligned, fixed bin, fixed bin (35));

dcl  cu_$level_get entry returns (fixed bin);
dcl (ioa_, com_err_) entry options (variable);
dcl  isvol bit (1);
dcl (copysw, allsw, drivesw, packsw, rbldsw) bit (1) init ("0"b);
dcl (drive, copyarg, pvname) char (32);

dcl (error_table_$action_not_performed, error_table_$badopt, error_table_$noarg) fixed bin (35) ext;
dcl  active_all_rings_data$max_tree_depth fixed bin ext;


	

%include salv_options;

dirsalv:	entry;
	isvol = "0"b;				/* directory salvage */
	go to join;
packsalv:	entry;
	isvol = "1"b;				/* pack salvage */
	go to join;
rbld_disk: entry;
	isvol = "1"b;
	rbldsw = "1"b;
join:	salv_opt_bits = "0"b;			/* clear all flags */
	if ^isvol then do;
	     if cu_$level_get () > SALV_RING then do;
		call com_err_ (0, "salvage_dirs", "must be in ring ^d for dir salv", SALV_RING);
		return;
	     end;
	     n = active_all_rings_data$max_tree_depth;
	end;

	do i = 1 to cu_$arg_count ();;
	     call cu_$arg_ptr (i, argp, argl, code);
	     if arg = "-console" then salv_opts.console = "1"b;
	     else if arg = "-debug" | arg = "-db" then salv_opts.debug = "1"b;
	     else if arg = "-dump" then salv_opts.dump = "1"b;
	     else if arg = "-nodump" then salv_opts.ndump = "1"b;
	     else if arg = "-nodebug" then salv_opts.ndebug = "1"b;
	     else if arg = "-noconsole" then salv_opts.nconsole = "1"b;
	     else if isvol then do;
		if arg = "-all" then do;
		     if ^rbldsw then allsw = "1"b;
		     else do;
			call com_err_ (error_table_$badopt, "rebuild disk", arg);
			return;
		     end;
		end;
		else if arg = "-copy" then do;
		     i = i + 1;
		     call cu_$arg_ptr (i, argp, argl, code);
		     if code ^= 0 then do;
nocopyarg:
			call com_err_ (code, "salvage_vol", "need copy drive name");
			return;
		     end;
		     if substr (arg, 1, 1) = "-" then do;
			code = error_table_$noarg;
			go to nocopyarg;
		     end;
		     copyarg = arg;
		     copysw = "1"b;
		end;
		else if i = 1 & substr (arg, 1, 1) ^= "-" then do; /* pack name */
		     pvname = arg;
		     packsw = "1"b;
		end;
		else if i = 2 & packsw & substr (arg, 1, 1) ^= "-" then do; /* drive name */
		     drive = arg;
		     drivesw = "1"b;
		end;
		else do;
		     call com_err_ (error_table_$badopt, "salvage_vol", arg);
		     return;
		end;
	     end;
	     else do;				/* dir salv */
		if arg = "-rebuild" then salv_opts.rbld = "1"b;
		else if arg = "-pathname" then salv_opts.pnames = "1"b;
		else if arg = "-long" then salv_opts.rbld, salv_opts.check, salv_opts.pnames = "1"b;
		else if arg = "-check_vtoce" then salv_opts.check = "1"b;
		else if arg = "-delete_connection_failure" | arg = "-dcf" then salv_opts.dcf = "1"b;
		else if arg = "-norebuild" then salv_opts.nrbld = "1"b;
		else if arg = "-nopathname" then salv_opts.npnames = "1"b;
		else if arg = "-nocheck_vtoce" then salv_opts.ncheck = "1"b;
		else if arg = "-nodelete_connection_failure" | arg = "-nodcf" then salv_opts.ndcf = "1"b;
		else if arg = "-level" then do;
		     i = i + 1;
		     call cu_$arg_ptr (i, argp, argl, code);
		     if code ^= 0 then do;
levnoarg:			call com_err_ (code, "salvage_dirs", "level number required");
			return;
		     end;
		     if substr (arg, 1, 1) = "-" then do;
			code = error_table_$noarg;
			go to levnoarg;
		     end;
		     call cv_dec_check_ (arg, code, n);
		     if code ^= 0 then do;
			call com_err_ (0, "salvage_dirs", "decimal number required for level, not ^a", arg);
			return;
		     end;
		     if n < 0 | n > active_all_rings_data$max_tree_depth then do;
			call com_err_ (0, "salvage_dirs", "depth must be between ^d and ^d", 0,
			     active_all_rings_data$max_tree_depth);
			return;
		     end;
		end;
		else do;
		     call com_err_ (error_table_$badopt, "salvage_dirs", arg);
		     return;
		end;
	     end;
	end;


	if substr (salv_opt_bits, 1, 18) & substr (salv_opt_bits, 19, 18) then do;
	     if isvol then call com_err_ (0, "salvage_vol", "Inconsistent arguments have been specified");
	     else call com_err_ (0, "salvage_dirs", "Inconsistent arguments have been specified");
	     return;
	end;

	if salv_opts.ncheck & salv_opts.dcf then do;

	     call com_err_ (0, "salvage_dirs", "VTOC checking is required for connection failure deletion.");
	     return;
	end;
	salv_opts.check = salv_opts.check | salv_opts.dcf;


	if ^isvol then do;
	     call disk_table_$accept_rlv (code);
	     if code ^= 0 then do;
		call com_err_ (0, "salvage_dirs", "will not salvage until root complete");
		return;
	     end;
	     if salv_opts.check then do;
		call disk_table_$general_mhv (code);
		if code ^= 0 then do;
		     call com_err_ (0, "salvage_dirs", "Will not salvage");
		     return;
		end;
	     end;
	end;

	if copysw & allsw then do;
	     call com_err_ (0, "salvage_vol", "-all and -copy arguments cannot be used together");
	     return;
	end;

	if allsw & drivesw then do;
	     call com_err_ (0, "salvage_vol", "no drive may be specified for -all");
	     return;
	end;

	if isvol & ^allsw & ^(packsw & drivesw) then do;
	     call com_err_ (error_table_$noarg, "salvage_vol", "first two args must be phys vol name and drive name");
	     return;
	end;


	if rbldsw & ^(drivesw & copysw) then do;
	     call com_err_ (error_table_$noarg, "rebuild_disk",
		"args are: pack name dirve name -copy drive name");
	     return;
	end;

	if rbldsw then call disk_table_$rbld_disk (pvname, drive, copyarg, salv_opt_bits, code);
	else if copysw then call disk_table_$volrlvcheck (pvname, drive, copyarg, salv_opt_bits, code);
	else if allsw then call disk_table_$volsalvall (salv_opt_bits, code);
	else if isvol then call disk_table_$volsalv (pvname, drive, salv_opt_bits, code);
	else call initializer_gate_$salvager (salv_opt_bits, n, code);

	if code ^= 0 then do;
	     if code < 100 then code = error_table_$action_not_performed;
	     if isvol then call com_err_ (code, "salvage_vol");
	     else call com_err_ (code, "salvage_dirs");
	end;
	return;

test:	entry;
	SALV_RING = cu_$level_get ();
	return;


/* BEGIN MESSAGE DOCUMENTATION

Message:
rebuild_disk: Expected argument missing.

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
rebuild_disk: Specified control argument is not implemented by this command. BLAH

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: ERROR_MESSAGE

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: ERROR_MESSAGE. level number required

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: Inconsistent arguments have been specified

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: Specified control argument is not implemented by this command. BLAH

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: Will not salvage

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: decimal number required for level, not BLAH

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: depth must be between 0 and 15

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: must be in ring 1 for dir salv

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_dirs: will not salvage until root complete

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_vol: -all and -copy arguments cannot be used together

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_vol: ERROR_MESSAGE

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_vol: ERROR_MESSAGE. need copy drive name

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_vol: Expected argument missing. first two args must be phys vol name and drive name

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_vol: Inconsistent arguments have been specified

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_vol: Specified control argument is not implemented by this command. BLAH

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn


Message:
salvage_vol: no drive may be specified for -all

S:	$initializer_io

T:	$response

M:	Incorrect arguments were specified.

A:	$tryagn



Message:
salvage_dirs: VTOC checking is required for connection failure deletion.

S:	$initializer_io

T:	$response

M:	An attempt was made to specify directory salvage with connection failure
	deletion, but no VTOC checking.

A:	$tryagn

END MESSAGE DOCUMENTATION */
     end;




		    verify_label_.pl1               11/11/89  1102.4r w 11/11/89  0803.3       21888



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1982 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */


verify_label_: proc (a_dtp, a_dtep, a_pvtx, ec);

/* VERIFY_LABEL_: Ring-1 program to check the label of a file system disk.

   This interim version calls the hardcore to read the label.
   Later versions will call IOI to read the label in.

   */

dcl (a_dtp, a_dtep) ptr parameter,
     a_pvtx fixed bin,
     ec fixed bin (35) parameter;

dcl  initializer_gate_$read_disk entry (fixed bin, fixed bin, ptr, fixed bin (35));

dcl  error_table_$bad_label fixed bin (35) ext;
dcl  error_table_$no_label fixed bin (35) ext;
dcl  error_table_$ai_restricted fixed bin (35) ext;
dcl  reasonable_time fixed bin (71) int static init (2276881905645328);

dcl 1 label_buffer like label;

/* -------------------------------------------------------- */

	labelp = addr (label_buffer);
	ec = 0;
	call initializer_gate_$read_disk (a_pvtx, LABEL_ADDR, labelp, ec);
	if ec ^= 0 then return;

	dtp = a_dtp;
	dtep = a_dtep;
	ec = error_table_$no_label;
	if label.Multics ^= Multics_ID_String then return;
	if label.version ^= 1 then return;
	if label.time_registered < reasonable_time then return;
	ec = error_table_$bad_label;
	if label.pvid ^= dte.pvid then return;
	if label.root.here ^= dte.rpv then return;
	lvep = addr (dt.lv_array (dte.lvx));
	if label.lv_name ^= lve.lvname then return;
	if label.lvid ^= lve.lvid then return;
	if label.password ^= "0"b then return;
	if lve.lvname = "root" then dte.pvname = label.pv_name; /* Gran kludge */
	else if label.pv_name ^= dte.pvname then return;

	ec = error_table_$ai_restricted;
	if label.max_access_class ^= lve.max_access_class then return;
	if label.min_access_class ^= lve.min_access_class then return;
	ec = 0;


%include disk_pack;
%include fs_vol_label;
%include disk_table;
     end verify_label_;




		    volume_registration_cmds_.pl1   11/11/89  1102.4r w 11/11/89  0803.3      298125



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1984 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */

/* format: ^inddcls,indattr,^indnoniterdo */

volume_registration_cmds_: vrc_: proc;

	comname = "volume_registration_cmds_"; ec = 0;
	call FAIL ("This entry not used.");
	return;

/* Logical Volume Registration Functions.

   add_volume_registration -pv name -lv name2 -model device_model ...
   change_volume_registration -pv|-lv name ...
   delete_volume_registration -pv|-lv name
   list_volume_registration -pv|-lv name {-bf}

   THVV
   Modified 4/79 by Michael R. Jordan to require -model in avr and print correct model number in lvr.
   Modified 84-08-21 by EJ Sharpe to move more of the database manipulation
		into ring-1 to make these system administrator rather
		than operator commands.
		Change name to volume_registration_cmds_ from hvr_.
                    Also added internal documentation.
   Modified 84-10-30 by EJ Sharpe for some minor upgrades
*/

/* AUTOMATIC */

dcl  ap		ptr;			/* arg pointer */
dcl  al   	fixed bin (21);		/* arg length */
dcl  bchr		char (al) based (ap);	/* args */

dcl  pvname 	char (32) init ("");	/* Physical volume name */
dcl  location	char (32) init ("uninitialized");
dcl  dt_str	char (24);
dcl  pvid           bit (36) aligned init ("0"b);
dcl  lvname 	char (32) init ("");	/* Logical volume name */
dcl  lvid           bit (36) aligned init ("0"b);
dcl  tcs 		char (32) init ("");	/* Control arg */
dcl  tcs1 	char (256) init ("");	/* Value */
dcl  temp_ptr       ptr init (null);
dcl  comname	char (32) init ("add_vol_registration");
dcl  aa_string	char (32) aligned;
dcl  auth_mnemonics char (172);
dcl  b36		bit (36) aligned init ("0"b);
dcl  fb35           fixed bin (35);
dcl  fb71           fixed bin (71);
dcl  old_lvid	bit (36) init ("0"b);
dcl  old_lvname	char (32) init ("");
dcl  access_range   (2) bit (72) aligned init ("0"b, sys_info$access_class_ceiling);
dcl  gave_access_low bit (1) init ("0"b);	/* TRUE if user specified */
dcl  gave_access_high bit (1) init ("0"b);	/* TRUE if user specified */
dcl  gave_public	bit (1) init ("0"b);	/* TRUE if user specified */
dcl  lv_already_registered	bit (1) init ("0"b);
dcl  new_lv	bit (1) init ("0"b);	/* TRUE if new seg */
dcl  acs_dir        char (168) init ("");
dcl  acs_ent        char (32) init ("");
dcl  ans	          bit (1);
dcl (i, j)	fixed bin;
dcl  more_args	bit (1) init ("1"b);
dcl  argno	fixed bin init (1);
dcl  serial	char (32) init ("unspecified"); /* Medium manufacturer's serial */
dcl  pubbit	bit (1) init ("1"b);	/* TRUE if public volume */
dcl  owner	char (32) init ("");	/* Person.Project of vol owner */
dcl  owneri	fixed bin;		/* .. index of dot in owner */
dcl  model	fixed bin init (0);		/* internal model number */
dcl  change_model   bit (1);
dcl  change_name    bit (1);
dcl  change_uid     bit (1);
dcl  change_auth    bit (1);
dcl  change_range   bit (1);
dcl  ec		fixed bin (35) init (0);
dcl  ec2		fixed bin (35) init (0);
dcl  temp_str	char (256);		/* for building error msgfs, etc */
dcl  str_len	fixed bin (21);		/* length of string passed to ioa_$rsnnl */


/* CONSTANTS */

dcl  YESNO (0:1) char (3) static init ("no", "yes");
dcl  PVNAME_LEGAL char (37) static options (constant)
		init ("0123456789abcdefghijklmnopqrstuvwxyz_");
dcl  LVNAME_LEGAL char (76) static options (constant)
		init (" `'_^~+-:{}!.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");

/* MISC */

dcl (addr,
     clock,
     null,
     rtrim,
     fixed,
     hbound,
     index,
     length,
     substr,
     unspec,
     verify)	builtin;

dcl  cleanup	condition;

dcl  error_table_$ai_invalid_range fixed bin (35) ext;
dcl  error_table_$badopt	fixed bin (35) ext;
dcl  error_table_$bad_arg	fixed bin (35) ext;
dcl  error_table_$noarg	fixed bin (35) ext;

dcl  sys_info$access_class_ceiling	bit (72) aligned ext;

/* ENTRIES */

dcl  aim_check_$greater_or_equal entry (bit(72) aligned, bit(72) aligned) returns(bit(1) aligned);
dcl  com_err_		entry() options(variable);
dcl  convert_access_class_$from_string	entry (bit(72) aligned, char(*), fixed bin(35));
dcl  convert_access_class_$from_string_range entry ((2) bit(72) aligned, char(*), fixed bin(35));
dcl  convert_access_class_$to_string_short entry (bit(72) aligned, char(*), fixed bin(35));
dcl  convert_aim_attributes_	entry (bit(72) aligned) returns(char(32) aligned);
dcl  convert_date_to_binary_  entry (char(*), fixed bin(71), fixed bin(35));
dcl  cv_dec_check_		entry (char(*), fixed bin(35)) returns(fixed bin(35));
dcl  cv_oct_check_		entry (char(*), fixed bin(35)) returns(fixed bin(35));
dcl  cu_$arg_ptr		entry (fixed bin, ptr, fixed bin(21), fixed bin(35));
dcl  date_time_$format	entry (char(*), fixed bin(71), char(*), char(*)) returns(char(250) var);
dcl  expand_pathname_$add_suffix entry (char(*), char(*), char(*), char(*), fixed bin(35));
dcl  get_temp_segment_	entry (char(*), ptr, fixed bin(35));
dcl  release_temp_segment_	entry (char(*), ptr, fixed bin(35));
dcl  hcs_$truncate_seg	entry (ptr, fixed bin(19), fixed bin(35));
dcl  command_query_$yes_no	entry() options(variable);
dcl  mdc_priv_$change_lvr	entry (char(*), ptr, fixed bin(35));
dcl  mdc_priv_$change_pvr	entry (char(*), ptr, fixed bin(35));
dcl  mdc_priv_$delete_lvr	entry (char(*), fixed bin(35));
dcl  mdc_priv_$delete_pvr	entry (char(*), fixed bin(35));
dcl  mdc_priv_$read_lvr	entry (char(*), ptr, fixed bin(35));
dcl  mdc_priv_$read_pvr	entry (char(*), ptr, fixed bin(35));
dcl  mdc_priv_$add_lvr	entry (char(*), ptr, fixed bin(35));
dcl  mdc_priv_$add_pvr	entry (char(*), ptr, fixed bin(35));
dcl  mdc_$find_volname	entry (bit(36), char(*), char(*), fixed bin(35));
dcl  pathname_		entry (char(*), char(*)) returns(char(168));
dcl  unique_chars_		entry (bit(*)) returns(char(15));
dcl  get_group_id_            entry() returns(char(32));
dcl  ioa_			entry() options(variable);
dcl  ioa_$rsnnl		entry() options(variable);
%page;

add_volume_registration:
add_vol_registration:	/* operator command name - pre MR11 */
avr:	 entry;

/******************************************************************************

This is an administrative command used to register logical or physical volumes.
It accepts arguments which specify attributes of the volume being registered.
Its syntax is:

     avr -pv NAME {PV ATTRIBUTE ARGS}...
	... -lv NAME {LV ATTRIBUTE ARGS}

The "PV ATTRIBUTE ARGS" must include at least the device model.
The "LV ATTRIBUTE ARGS" are specified only when registering a new LV.
All arguments are non-positional except for the first two (i.e. "pv NAME").

The code is written like:

	entry
	setup
	process args
	check arg consistancy
	if LV not already registered then
	     fill in registration structure for new LV and new PV
	     call inner ring
	else
	     fill in registration structure for new PV
	     call inner ring
	cleanup
	exit

******************************************************************************/

	call set_up("add_volume_registration");
	on cleanup call clean_up;

	call getarg (tcs);				/* First arg must be "pv" */

	ec = 0;

	if ^(tcs = "pv"  /* retained for compatability */
	     | tcs = "-physical_volume"  | tcs = "-pv")  then do;
	     ec = error_table_$badopt;
	     call FAIL ("First arg must be ""-pv"".");
	end;

	call getarg (pvname);			/* Get physical volume name */
	if pvname = "" then call FAIL ("PV name not specified.");
	if verify (rtrim (pvname), PVNAME_LEGAL) ^= 0 then call FAIL ("Invalid characters in name: " || pvname);

	do while (more_args);
	     call getarg (tcs);			/* Get control arg */
	     call getarg (tcs1);			/* .. and value */

	/* The control args -lv, -access_(high low), -owner, -public,
	   -acs, and -lvid describe the Logical Volume */

	     if tcs = "-logical_volume"  |  tcs = "-lv" then do;
		lvname = tcs1;
		if verify (rtrim(lvname), LVNAME_LEGAL) ^= 0 then call FAIL("Illegal characters in LV name.");
	     end;
	     else if tcs = "-access_class"  |  tcs = "-acc" then do;
		call convert_access_class_$from_string_range (access_range, tcs1, ec);
		if ec ^= 0 then call FAIL (tcs1);
	     end;
	     /* -access_low and -access_high retained for compatability
		with the old operator avr command */
	     else if tcs = "-access_low"  |  tcs = "-ac_low" then do;
		call convert_access_class_$from_string (access_range(1), tcs1, ec);
		if ec ^= 0 then call FAIL ("Illegal auth: " || tcs1);
		gave_access_low = "1"b;
	     end;
	     else if tcs = "-access_high"  |  tcs = "-ac_high" then do;
		call convert_access_class_$from_string (access_range(2), tcs1, ec);
		if ec ^= 0 then call FAIL ("Illegal auth: " || tcs1);
		gave_access_high = "1"b;
	     end;
	     else if tcs = "-owner"  |  tcs = "-ow" then do;
		call check_owner;
		if owneri = 0 then call FAIL ("Invalid owner: " || tcs1);
	     end;
	     else if tcs = "-public"  |  tcs = "-pub" then do;
		if tcs1 ^= "yes"  &  tcs1 ^= "y"  &
		   tcs1 ^= "no"   &  tcs1 ^= "n"  then do;
		     ec = error_table_$bad_arg;
		     call FAIL("Public argument must be ""yes"" or ""no"".");
		end;
		pubbit = ( (tcs1 = "yes") | (tcs1 = "y") );
		gave_public = "1"b;
	     end;
	     else if tcs = "-acs_path"  |  tcs = "-acs" then do;
		if tcs1 ^= "" then do;
		     call expand_pathname_$add_suffix (tcs1, "acs", acs_dir, acs_ent, ec);
		     if ec ^= 0 then call FAIL ("Bad ACS path: " || tcs1);
		end;
		else do;
		     acs_dir = "";
		     acs_ent = "";
		end;
	     end;
	     else if tcs = "-logical_volume_uid"  |  tcs = "-lvid" then do;
		fb35 = cv_oct_check_ (tcs1, ec2);
		lvid = unspec(fb35);
		if ec2 ^= 0 then call FAIL ("Invalid LVID: " || tcs1);
	     end;

	/* The rest of the control arguments (-pvid, -model, -location,
	   and -serial) are PV specific	 */

	     else if tcs = "-physical_volume_uid"  |  tcs = "-pvid" then do;
		fb35 = cv_oct_check_ (tcs1, ec2);
		pvid = unspec(fb35);
		if ec2 ^= 0 then call FAIL ("Invalid PVID: " || tcs1);
	     end;
	     else if tcs = "-device_model"  |  tcs = "-model" then do;
		i = cv_dec_check_ (tcs1, ec2);
		if ec2 ^= 0 then call FAIL ("Invalid model: " || tcs1);
		do model = 1 to hbound (MODEL, 1) while (i ^= MODEL (model)); end;
		if model > hbound (MODEL, 1) then call FAIL ("Unknown model: " || tcs1);
		model = MODELX (model);
	     end;
	     else if tcs = "-manufacturer_serial"  |  tcs = "-serial" then
		     serial = tcs1;
	     else if tcs = "-location"  |  tcs = "-loc" then
		     location = tcs1;
	     /* no control arg for "password" field */
	     else do;
		ec = error_table_$badopt;
		call FAIL (tcs);
	     end;
	end;

	if model = 0				/* Missing -model control arg */
	then do;
	     ec = error_table_$noarg;
	     call FAIL ("-model must be specified");
	end;

	if lvname = "" 
	then call FAIL ("""-lv"" must be specified."); /* Check given args */

	call mdc_priv_$read_lvr (lvname, vrp, ec);
	lv_already_registered = ( ec = 0 );

	if lv_already_registered & (gave_access_low | gave_access_high |
		gave_public | owner^="" | acs_dir^="" | lvid)
	then call FAIL ("LV already registered, use change_volume_registration to modify its attributes.");

	call mdc_priv_$read_pvr (pvname, vrp, ec);
	if ec = 0 then call FAIL ("PV already registered: " || pvname);
	ec = 0;

	if gave_access_low | gave_access_high then
	     if ^aim_check_$greater_or_equal (access_range(2), access_range(1))
	     then do;
		ec = error_table_$ai_invalid_range;
		call FAIL ("");
	     end;

	call hcs_$truncate_seg (vrp, 0, ec); /* initialize registration */
	if ec ^= 0 then call FAIL ("Trouble initializing structure.");

	if ^lv_already_registered then do;  /* code to register new LV */

	     if ^pubbit | acs_dir ^= "" then do;	/* Is ACS requested or needed? */
		if acs_dir = "" then do;
		     str_len = length (acs_dir);
		     call ioa_$rsnnl (">udd>^a>^a", acs_dir, str_len, substr(owner, owneri+1), substr (owner, 1, owneri-1));
		     acs_ent = rtrim(lvname)||".acs";
		end;
		else if acs_ent ^= rtrim(lvname)||".acs"
		     then call FAIL ("Invalid ACS path.");
	     end;

	     /* first let's check with user */
	     call command_query_$yes_no (ans, 0, comname, "",
		"LV ""^a"" does not exist.  Do you wish to create it? ", lvname);
	     if ^ans then do;  /* must've mis-typed LV name */
		call clean_up;
		return;
	     end;

	     if owner = "" then do;		/* if owner not specified, we'll make it the caller */
		owner = get_group_id_ ();
		i = length(rtrim(owner))-2;
		owner = substr(owner, 1, i);  /* strip off tag */
		owneri = index (owner, ".");
	     end;

	     /* fill in the registration structure... */
	     volume_registration.version = Volume_Registration_Version_2;
	     if lvid then volume_registration.lvid = lvid;
	     else do;
		fb71 = clock();
		volume_registration.lvid = substr (unspec (fb71), 36, 36);
	     end;
	     volume_registration.lvname = lvname;
	     volume_registration.volume_owner = owner;
	     volume_registration.public = pubbit;
	     volume_registration.access_class_range = access_range;
	     if acs_dir = ""  &  acs_ent = ""
	     then volume_registration.acs_path = "";
	     else volume_registration.acs_path = pathname_ (acs_dir, acs_ent);
	     volume_registration.npv = 1;  /* registering first right now */

	     /* now, let's get the first PV into the registration */
	     volume_registration.npv = 1;

	     pvrp = addr (volume_registration.pv(1)); /* use first entry */
	     if pvid then pv_registration.pvid = pvid;
	     else do;
		fb71 = clock();
		pv_registration.pvid = substr (unspec (fb71), 36, 36);
	     end;
	     pv_registration.model = model;
	     pv_registration.pvname = pvname;
	     pv_registration.location = location;
	     pv_registration.mfg_serial = serial;
	     pv_registration.password = "0"b;
	     pv_registration.date_registered = clock ();

	     call mdc_priv_$add_lvr (lvname, vrp, ec);
	     if ec ^= 0 then call FAIL ("Cannot register: " || lvname);

	     call ioa_ ("^a: Registered PV ""^a"" (pvid ^w) on new LV ""^a"" lvid ^w).", comname,
		pvname, volume_registration.pv(1).pvid, lvname, volume_registration.lvid);

	end;
	else do;   /* we'll just add a new PV to an existing LV */

	     if pvid then pv_registration.pvid = pvid;
	     else do;
		fb71 = clock();
		pv_registration.pvid = substr (unspec (fb71), 36, 36);
	     end;

	     pv_registration.model = model;	/* Fill in per-pak data */
	     pv_registration.pvname = pvname;
	     pv_registration.location = location;
	     pv_registration.mfg_serial = serial;
	     pv_registration.password = "0"b;
	     pv_registration.date_registered = clock ();

	     call mdc_priv_$add_pvr (lvname, pvrp, ec);
	     if ec ^= 0 then call FAIL ("Cannot register: " || pvname);

	     call ioa_ ("^a: Registered PV ""^a"" (pvid ^w) on LV ""^a"".",
		comname, pvname, pvrp->pv_registration.pvid, lvname);
	end;

	call clean_up;

	return;
%page;

change_volume_registration:
change_vol_registration:	/* operator command name - pre MR11 */
cvr:	entry;

/******************************************************************************

This is an administrative command which facilitates the adjustment of
volume attributes.  Its syntax is:

     cvr [-pv | -lv] NAME {PV or LV ATTRIBUTE ARGS}

Some volume attributes should not be changed when the physical disk medium
has already been initialized.  This is because some attributes (e.g. name
and unique identifier) are written onto the medium label at initialization
and cannot be altered without re-initialization of the medium.

The code is written like:

	entry
	setup
	if first arg is "pv" then
	     process args for PV
	     check arg consistancy
	     fill in PV registration structure with changes
	     call inner ring
	else if first arg is "lv" then
	     process args for LV
	     check arg consistancy
	     fill in LV registration structure with changes
	     call inner ring
	else first arg is in error
	cleanup
	exit

******************************************************************************/

	call set_up("change_volume_registration");
	on cleanup call clean_up;

	call getarg (tcs);

	ec = 0;

	change_model, change_name, change_uid, change_auth, change_range = "0"b;

	if tcs = "pv" /* retained for compatability */
	     |  tcs = "-physical_volume"  |  tcs = "-pv" then do;
	     pvrp = vrp;   /* use pv_registration instead of volume_registration */
	     call getarg (pvname);			/* yes */
	     call mdc_priv_$read_pvr (pvname, pvrp, ec);
	     if ec ^= 0 then call FAIL ("PV not found: " || pvname);

	     do while (more_args);
		call getarg (tcs);			/* Get control arg */
		call getarg (tcs1);			/* .. and value */
		if tcs = "-device_model"  |  tcs = "-model" then do;
		     j = cv_dec_check_ (tcs1, ec2);
		     if ec2 ^= 0 then call FAIL("Invalid model: "||tcs1);
		     else do;
			do model = 1 to hbound (MODEL, 1) while (j ^= MODEL (model)); end;
			if model > hbound (MODEL, 1) then call FAIL("Unknown model: "||tcs1);
			else do;
			     model = MODELX (model);
			     pv_registration.model = model;
			end;
		     end;
		     change_model = "1"b;
		end;
		else if tcs = "-manufacturer_serial"  |  tcs = "-serial"
		     then pv_registration.mfg_serial = tcs1;
		else if tcs = "-location"  |  tcs = "-loc"
		     then pv_registration.location = tcs1;
		else if tcs = "-date_registered"  |  tcs = "-dtr" then do;
		     call convert_date_to_binary_ (tcs1, pv_registration.date_registered, ec);
		     if ec ^= 0 then call FAIL("Bad date: "||tcs1);
		end;
		else if tcs = "-name"  |  tcs = "-nm" then do;
		     if verify (rtrim (tcs1), PVNAME_LEGAL) ^= 0 then call FAIL ("Invalid characters in name: " || tcs1);
		     else pv_registration.pvname = tcs1;
		     change_name = "1"b;
		end;
		else if tcs = "-physical_volume_uid"  |  tcs = "-pvid" then do;
		     fb35 = cv_oct_check_ (tcs1, ec2);
		     b36 = unspec(fb35);
		     if ec2 ^= 0 then call FAIL("Invalid PVID: "||tcs1);
		     else pv_registration.pvid = b36;
		     change_uid = "1"b;
		end;
		/* there is no control arg for changing "password" field */
		else do;
		     ec = error_table_$bad_arg;
		     call FAIL("Bad control arg: "||tcs||" "||tcs1);
		end;
	     end;

	     if change_model | change_name | change_uid then do;
		call command_query_$yes_no (ans, 0, comname, "",
		     "PV model, name, or uid should not be changed for initialized volumes.  Do you wish to continue? ");
		if ^ans then do;
		     call clean_up;
		     return;
		end;
	     end;

	     /* now tell ring-1 to do it! */
	     call mdc_priv_$change_pvr (pvname, pvrp, ec);
	     if ec ^= 0 then call FAIL ("Could not make changes.");

	     call ioa_ ("^a: Changed attributes for PV ""^a"".", comname, pvname);
	end;
	else if tcs = "lv" /* retained for compatability */
	     |  tcs = "-logical_volume"  |  tcs = "-lv"  then do;
	     call getarg (lvname);
	     call mdc_priv_$read_lvr (lvname, vrp, ec);
	     if ec ^= 0 then call FAIL ("LV not registered: " || lvname);

	     volume_registration.npv = 0;   /* don't care about the PV entries... */

	     do while (more_args);
		call getarg (tcs);
		call getarg (tcs1);

		if tcs = "-access_class"  |  tcs = "-acc" then do;
		     call convert_access_class_$from_string_range (access_range, tcs1, ec);
		     if ec ^= 0 then call FAIL (tcs1);
		     change_range = "1"b;
		     volume_registration.access_class_range = access_range;
		end;
		/* -access_low and -access_high retained for compatability
		     with the old operator cvr command */
		else if tcs = "-access_low"  |  tcs = "-ac_low" then do;
		     call convert_access_class_$from_string (access_range(1), tcs1, ec);
		     if ec ^= 0 then call FAIL("Illegal auth: "||tcs1);
		     else volume_registration.access_class_range(1) = access_range(1);
		     change_auth = "1"b;
		end;
		else if tcs = "-access_high"  |  tcs = "-ac_high" then do;
		     call convert_access_class_$from_string (access_range(2), tcs1, ec);
		     if ec ^= 0 then call FAIL("Illegal auth: "||tcs1);
		     else volume_registration.access_class_range(2) = access_range(2);
		     change_auth = "1"b;
		end;
		else if tcs = "-public"  |  tcs = "-pub" then do;
			if tcs1 ^= "yes"  &  tcs1 ^= "y"  &
			   tcs1 ^= "no"   &  tcs1 ^= "n"  then call FAIL("Public argument must be ""yes"" or ""no"".");
			volume_registration.public = ( (tcs1 = "yes") | (tcs1 = "y") );
		     end;
		else if tcs = "-acs_path"  |  tcs = "-acs" then do;
		     if tcs1 ^= "" then do;
			call expand_pathname_$add_suffix (tcs1, "acs", acs_dir, acs_ent, ec);
			if ec ^= 0 then call FAIL("Bad acs path: "||tcs1);
			if acs_ent ^= rtrim(lvname)||".acs" then call FAIL ("Bad acs path: "||tcs1);
			volume_registration.acs_path = pathname_ (acs_dir, acs_ent);
		     end;
		     else do;
			acs_dir = "";
			acs_ent = "";
			volume_registration.acs_path = "";
		     end;
		end;
		else if tcs = "-owner"  |  tcs = "-ow" then do;
		     call check_owner;
		     if owneri = 0 then do;
			ec = error_table_$bad_arg;
			call FAIL("Invalid owner: "||tcs1);
		     end;
		     else volume_registration.volume_owner = owner;
		end;
		else if tcs = "-name"  |  tcs = "-nm" then do;
			if verify (rtrim(tcs1), LVNAME_LEGAL) ^= 0 then call FAIL("Illegal characters in LV name.");
			volume_registration.lvname = tcs1;
			change_name = "1"b;
		     end;
		else if tcs = "-logical_volume_uid"  |  tcs = "-lvid" then do;
		     fb35 = cv_oct_check_ (tcs1, ec2);
		     b36 = unspec(fb35);
		     if ec2 ^= 0 then call FAIL("Invalid LVID: "||tcs1);
		     else volume_registration.lvid = b36;
		     change_uid = "1"b;
		end;
		else do;
		     ec = error_table_$badopt;
		     call FAIL(tcs);
		end;
	     end;

	     if change_auth then
		if ^aim_check_$greater_or_equal (volume_registration.access_class_range(2),
						volume_registration.access_class_range(1))
		then do;
		     ec = error_table_$ai_invalid_range;
		     call FAIL ("");
		end;

	     if change_range | change_auth | change_name | change_uid then do;
		call command_query_$yes_no (ans, 0, comname, "",
		     "LV access class, name, or uid should not be changed for initialized volumes.  Do you wish to continue? ");
		if ^ans then do;
		     call clean_up;
		     return;
		end;
	     end;

	     /* now get ring-1 to make the changes */
	     call mdc_priv_$change_lvr (lvname, vrp, ec);
	     if ec ^= 0 then call FAIL ("Unable to make changes.");

	     call ioa_ ("^a: Changed attributes of LV ""^a"".", comname, lvname);
	end;

	else do;
	     ec = error_table_$badopt;
	     call FAIL ("First arg must be ""-pv"" or ""-lv"".");
	end;

	call clean_up;

	return;
%page;

delete_volume_registration:
del_vol_registration:	/* operator command name - pre MR11 */
dvr:      entry;
 
/******************************************************************************

This is an administrative command used to remove the registration of a logical
or physical volume.  Its syntax is:

     dvr [-lv | -pv] NAME

Physical volumes should only be deregistered when their contents have been
moved onto other physical volumes (within the same logical volume).
Logical volumes should only be deregistered when their corresponding directory
hierearchies have been removed from the system (thus leaving the whole LV
empty).  Since a logical volume must always have at least one
physical volume, the last PV in an LV may not be deleted from the LV, the
LV itself must be deleted.

The code is written like:

	entry
	setup
	if first arg is "pv" then
	     check existance of PV
	     get name of PV's LV
	     check LV for more than one PV
	     call inner ring to delete PV
	else if first arg is "lv" then
	     check existance of LV
	     list names of PVs being deleted with the LV
	     call inner ring to delete LV
	else first arg is invalid
	cleanup
	exit

******************************************************************************/

	call set_up("delete_volume_registration");
	on cleanup call clean_up;

	call getarg (tcs);

	ec = 0;

	if tcs = "pv"  /* retained for compatability */
	     |  tcs = "-physical_volume"  |  tcs = "-pv"  then do;
	     call getarg (pvname);
	     if verify (rtrim(pvname), PVNAME_LEGAL) ^= 0 then call FAIL("Illegal characters in PV name.");

	     call mdc_priv_$read_pvr (pvname, pvrp, ec);
	     if ec ^= 0 then call FAIL ("PV not found: " || pvname);

	     call mdc_$find_volname ((pv_registration.pvid), pvname, lvname, ec);
	     if ec ^= 0 then call FAIL ("Unable to get LV name.");
	     call mdc_priv_$read_lvr (lvname, vrp, ec);
	     if ec ^= 0 then call FAIL ("Unable to get LV data.");

	     if volume_registration.npv = 1 then do;
		str_len = length (temp_str);
		call ioa_$rsnnl ("PV ""^a"" is last in LV ""^a"".  User ""dvr lv ^a"".", temp_str, str_len, pvname, lvname, lvname);
		call FAIL (temp_str);
	     end;

	     call mdc_priv_$delete_pvr (pvname, ec);
	     if ec ^= 0 then call FAIL ("Cannot delete: " || pvname);

	     call ioa_ ("^a: Deleted PV ""^a"" from LV ""^a"".", comname, pvname, lvname);
	end;
	else if tcs = "lv"  /* retained for compatability */
	     |  tcs = "-logical_volume"  |  tcs = "-lv"  then do;
	     call getarg (lvname);
	     if verify(rtrim(lvname), LVNAME_LEGAL) ^= 0 then call FAIL("Illegal characters in LV name.");

	     call mdc_priv_$read_lvr (lvname, vrp, ec);
	     if ec ^= 0 then call FAIL ("LV not registered: " || lvname);

	     do i = 1 to volume_registration.npv;
		call ioa_ ("^a: Deleting PV ""^a"" from LV ""^a"".", comname, volume_registration.pv (i).pvname, lvname);
	     end;

	     call mdc_priv_$delete_lvr (lvname, ec);
	     if ec ^= 0 then call FAIL ("Cannot modify: " || lvname);

	     call ioa_ ("^a: Deleted LV ""^a"".", comname, lvname);
	end;
	else do;
	     ec = error_table_$badopt;
	     call FAIL ("First arg must be ""-pv"" or ""-lv"".");
	end;

	call clean_up;

	return;
%page;

list_volume_registration:
list_vol_registration:	/* operator command name - pre MR11 */
lvr:	entry;

/******************************************************************************

This is an administrative command which lists the attributes of the specified
logical or physical volume.  Its syntax is:

     lvr [-pv | -lv] NAME {-brief}

When listing a logical volume, all physical volumes in that logical volume
will be listed unless "-brief" is given.

The code is written like:

	entry
	setup
	if first arg is "pv" then
	     call inner ring for PV registration structure
	     format report of PV and print
	else if first arg is "lv" then
	     call inner ring for LV registration structure
		(including all PVs in that LV)
	     if brief arg not given then
		format report of LV and print
	     if brief arg given then
		print only the PV names
	     else format report of each PV and print
	else first arg is invalid
	cleanup
	exit

******************************************************************************/

	call set_up("list_volume_registration");
	on cleanup call clean_up;

	call getarg (tcs);

	if tcs = "pv"  /* retained for compatability */
	     |  tcs = "-physical_volume"  |  tcs = "-pv"  then do;
	     call getarg (pvname);
	     if verify(rtrim(pvname), PVNAME_LEGAL) ^= 0 then call FAIL("Illegal characters in PV name.");

	     call mdc_priv_$read_pvr (pvname, vrp, ec);
	     if ec ^= 0 then call FAIL ("PV not found: " || pvname);

	     call mdc_$find_volname ((pv_registration.pvid), pvname, lvname, ec);
	     if ec ^= 0 then call FAIL ("Unable to get LV name.");

	     call ioa_ ("^/pvname:^-^a", pv_registration.pvname);
	     call ioa_ ("pvid:^-^-^w  (^a)", pv_registration.pvid, unique_chars_((pv_registration.pvid)));
	     call ioa_ ("serial:^-^-^a", pv_registration.mfg_serial);
	     call ioa_ ("model:^-^-^d", MODELN (pv_registration.model));
	     call ioa_ ("location:^-^-^a", pv_registration.location);
	     dt_str = date_time_$format ("date_time", pv_registration.date_registered, "", "");
	     call ioa_ ("date registered:^-^a", dt_str);
	     /* password field not printed */
	     call ioa_ ("lvname:^-^-^a", lvname);
	end;
	else if tcs = "lv"  /* retained for compatability */
	     |  tcs = "-logical_volume"  |  tcs = "-lv"  then do;
	     call getarg (lvname);
	     if verify(rtrim(lvname), LVNAME_LEGAL) ^= 0 then call FAIL("Illegal characters in LV name.");

	     call mdc_priv_$read_lvr (lvname, vrp, ec);
	     if ec ^= 0 then call FAIL ("LV not registered: " || lvname);
	     call getarg (tcs);
	     if tcs ^= "-bf" & tcs ^= "-brief" then do;
		call ioa_ ("^/lvname:^-^a", lvname);
		call ioa_ ("lvid:^-^-^w  (^a)", volume_registration.lvid, unique_chars_((volume_registration.lvid)));
		call ioa_ ("public:^-^-^a", YESNO (fixed (volume_registration.public, 1)));
		call ioa_ ("owner:^-^-^a", volume_registration.volume_owner);
		aa_string = convert_aim_attributes_ (volume_registration.access_class_range(1));
		call convert_access_class_$to_string_short (volume_registration.access_class_range(1), auth_mnemonics, ec);
		if ec ^= 0 then do;
		     call com_err_ (ec, comname, "Unable to convert min auth to name string.");
		     auth_mnemonics = "system_low?";
		end;
		call ioa_ ("min_access_class:^-^a (^a)", aa_string, auth_mnemonics);
		aa_string = convert_aim_attributes_ (volume_registration.access_class_range(2));
		call convert_access_class_$to_string_short (volume_registration.access_class_range(2), auth_mnemonics, ec);
		if ec ^= 0 then do;
		     call com_err_ (ec, comname, "Unable to convert max auth to name string.");
		     auth_mnemonics = "system_high?";
		end;
		call ioa_ ("max_access_class:^-^a (^a)", aa_string, auth_mnemonics);
		call ioa_ ("acs_path:^-^-^a", volume_registration.acs_path);
		call ioa_ ("^/npv:^-^-^d", volume_registration.npv);
	     end;
	     do i = 1 to volume_registration.npv;
		pvrp = addr(volume_registration.pv(i));

		if tcs = "-bf" | tcs = "-brief"
		then call ioa_ ("^a", pv_registration.pvname);
		else do;
		     call ioa_ ("^/ pvname:^-^a", pv_registration.pvname);
		     call ioa_ (" pvid:^-^-^w  (^a)", pv_registration.pvid, unique_chars_((pv_registration.pvid)));
		     call ioa_ (" serial:^-^-^a", pv_registration.mfg_serial);
		     call ioa_ (" model:^-^-^d", MODELN (pv_registration.model));
		     call ioa_ (" location:^-^a", pv_registration.location);
		     dt_str = date_time_$format ("date_time", pv_registration.date_registered, "", "");
		     call ioa_ (" date registered:^-^a", dt_str);
		     /* password field not printed */
		end;
	     end;
	end;
	else do;
	     ec = error_table_$badopt;
	     call FAIL ("First arg must be ""-pv"" or ""-lv"".");
	end;

	call ioa_ ("");

	call clean_up;

	return;
%page;

/* -------------- UTILITY ROUTINES ---------------------- */
/* ------------------------------------------------------ */

set_up:   proc (name);	/* initializations for all entrypoints */

dcl  name		char (*);

	comname = name;
	ec = 0;
	temp_ptr, vrp, pvrp = null();
	call get_temp_segment_ (comname, temp_ptr, ec);
	if ec ^= 0 then call FAIL ("Trouble getting temporary segment.");
	vrp, pvrp = temp_ptr;  /* we'll use temp for either structure */

	return;

end set_up;

/* ------------------------------------------------------ */

clean_up:	proc ();		/* clean up our external mess... */

dcl  tptr     ptr;

	if temp_ptr ^= null ()
	then do;
	     tptr = temp_ptr;
	     temp_ptr = null ();   /* so we don't try this again */
	     call release_temp_segment_ (comname, temp_ptr, ec);
	end;

	return;

end clean_up;

/* ------------------------------------------------------ */

getarg:	proc (x);		/* get next argument on command line */

dcl  x char (*);
dcl  errc fixed bin (35);

	call cu_$arg_ptr (argno, ap, al, errc);
	if errc = 0 then do;
	     if al > length(x) then call FAIL ("Argument too long");
	     x = bchr;
	end;
	else x = "";
	argno = argno + 1;
	call cu_$arg_ptr (argno, ap, al, errc);
	if errc ^= 0 | bchr = "" then more_args = "0"b;

	return;

end getarg;

/* ------------------------------------------------------ */

FAIL:     proc (x);		/* come here when something bad happens */

dcl  x char (*);

	call com_err_ (ec, comname, "^a", x);
	call clean_up;
	go to BUST;

end FAIL;

BUST:	return;		/* return from this module */

/* ------------------------------------------------------ */

check_owner: proc;		/* validates Person_ID.Project_ID */

	owneri = index (tcs1, ".");
	if owneri = 0 then return;
	if index (substr (tcs1, owneri+1), ".") ^= 0 then owneri = 0;
	else owner = tcs1;

	return;

end check_owner;
%page;
%include volume_registration;
%page;
%include fs_dev_types;


/* ------------------------------------------------------ */
/* ------------------------------------------------------ */
end volume_registration_cmds_;
   



		    volume_registration_mgr_.pl1    11/11/89  1102.4r w 11/11/89  0803.3      975069



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1984 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */



/****^  HISTORY COMMENTS:
  1) change(85-07-10,EJSharpe), approve(86-02-20,MCR7304),
     audit(86-03-27,CLJones), install(86-04-23,MR12.0-1044):
     added get_access_for_attach entrypoint in order for
     rcp_attach_lv_ to perform security auditing
     The two "get_access" entries should be merged into
     an access kernel for MDC and LV mgmt.
                                                   END HISTORY COMMENTS */


/* format: style1,insnl,linecom,indcomtxt,^inddcls,^indnoniterdo */

volume_registration_mgr_:
vrm_:
     proc;

/* Logical Volume Registration Guts.

   This program knows where the volume registration data for
   logical volumes is kept.

   It contain entries called by the volume_registration_cmds_
   and service entries called by many other programs.

   The whole program runs in ring 1.

   Written by T. H. VanVleck
   Modified July 1981 by J. Bongiovanni to validate lvx.
   Modified 83-12-06 BIM to use RCP privilege to circumvent AIM.
   Modified 83-12-07 BIM to update disk table from vol registration.
   Modified 84-08-21 by EJ Sharpe
   This module used to be named "hdx".
   removed entries: read_hv, read_pv, write_hv, delete_hv, make_link,
   and chname_hv.  Replaced with add_lvr, add_pvr, read_lvr, read_pvr,
   change_lvr, change_pvr, delete_lvr, and delete_pvr.  These entries
   do their own work (don't rely on ring 4 caller) and now set a lock
   while changing the database.  Also, added auditing of database
   changes, removed ACS link hack, changed module name to
   volume_registration_mgr_, and generally improved the code.  (This
   was all in order to move the registration commands from the operator
   interface to the system administrator's - for B2.)
   One more thing - fixed hardcore error #643 - LV access now not
   dependant on AIM and ring brackets of ACS.
   Modified 84-10-31 by EJ Sharpe for some misc upgrades and change get_access
   entry to return bit (36) modes instead of fixed bin (5).
   Modified 84-12-17 by EJ Sharpe for fixes:
   1) use currentsize instead of size in reference to volume_registration
   2) attempt delete of MDCS before deleting registration data file
   3) use hcs_$get_user_raw_mode for LV access computation
   Modified 84-12-27 by Keith Loepere for version 2 create_branch_info.
   Modified 85-01-18 by EJ Sharpe for handling unexpected conditions properly
   in a ring 1 process (e.g. Dumper)
*/
%page;
/* PARAMETERS */

dcl     a_lvname		 char (*);		/* Logical Volume name */
dcl     a_lv_access_class_range
			 (2) bit (72) aligned;	/* returned from get_access_for_attach */
dcl     a_dtp		 ptr;			/* disk_table ptr */
dcl     a_ptr		 ptr;			/* ptr to volume_registration or pv_registration */
dcl     a_ec		 fixed bin (35);		/* error code */
dcl     a_pvname		 char (*);		/* Physical Volume name */
dcl     a_device_type	 fixed bin;		/* model of disk drive */
dcl     a_lvid		 bit (36);		/* Logical volume uid */
dcl     a_pvid		 bit (36);		/* Physical volume ID (input) */
dcl     a_level		 fixed bin (3);		/* validation level (input) */
dcl     a_mode		 bit (36) aligned;		/* effective mode (output) */
dcl     a_pub_bit		 bit (1) aligned;		/* volume is public (output) */
dcl     a_pvap		 ptr;			/* ptr to pva (input) */
dcl     a_npv		 fixed bin;		/* number returned (output) */
dcl     a_lvx		 fixed bin;		/* logical volume index (in disk_table) */
dcl     a_dtep		 ptr;			/* disk_table entry ptr */
dcl     a_dn		 char (*);		/* dir name */
dcl     a_dn2		 char (*);		/* dir name */


/* AUTOMATIC */

dcl     MYNAME		 char (65) init ("");	/* first thing set by each entry */
dcl     lvid_changed	 bit (1);			/* flag used by change-lvr */
dcl     pvid_changed	 bit (1);			/* flag used by change_pvr */
dcl     lvid		 bit (36) aligned;		/* temp */
dcl     old_lvid		 bit (36) aligned;		/* used by change_lvr */
dcl     new_lvid		 bit (36) aligned;		/* used by change_lvr */
dcl     pvid		 bit (36) aligned;		/* temp */
dcl     old_pvid		 bit (36) aligned;		/* used by change_pvr */
dcl     new_pvid		 bit (36) aligned;		/* used by change_pvr */
dcl     uname		 char (32);		/* unique name temp */
dcl     uname2		 char (32);		/* unique name temp */
dcl     old_uname		 char (32);		/* used by change_(lvr pvr) */
dcl     new_uname		 char (32);		/* used by change_(lvr pvr) */
dcl     pvname		 char (32);		/* temp */
dcl     old_pvname		 char (32);		/* used by change_pvr */
dcl     new_pvname		 char (32);		/* used by change_pvr */
dcl     lvname		 char (32);		/* temp */
dcl     old_lvname		 char (32);		/* used by change_lvr */
dcl     new_lvname		 char (32);		/* used by change_lvr */
dcl     old_aa_str		 char (168);		/* access auth string used by change_lvr */
dcl     new_aa_str		 char (168);		/* access auth string used by change_lvr */
dcl     old_dt_reg		 char (24);		/* date-time string used by change_pvr */
dcl     new_dt_reg		 char (24);		/* date-time string used by change_pvr */
dcl     level		 fixed bin;
dcl     npvrp		 ptr;			/* pointer to new pv_reg struct */
dcl     nvrp		 ptr;			/* pointer to new vol_reg struct */
dcl     upvrp		 ptr;			/* pointer to user's pv_reg struct */
dcl     uvrp		 ptr;			/* pointer to user's vol_reg struct */
dcl     tsegp		 ptr init (null);		/* pointer to temporary seg */
dcl     databasep		 ptr init (null);		/* pointer to volume registration database */
dcl     dn		 char (168);		/* Volume registration DB dir */
dcl     en		 char (32);		/* Volume registration DB entry */
dcl     mode		 bit (36) aligned;		/* access mode */
dcl     type		 fixed bin (2);		/* entry type */
dcl     bc		 fixed bin (24);		/* bit count */
dcl     (i, j)		 fixed bin;		/* index vars */
dcl     owner		 char (32);		/* Person.Project of vol owner */
dcl     (proj, oproj)	 char (9);		/* temps for access check */
dcl     (pers, opers)	 char (22);		/* temps for access check */
dcl     old_level		 fixed bin init (-1);
dcl     ec		 fixed bin (35);		/* error code */
dcl     ec2		 fixed bin (35);		/* error code */
dcl     make_lve		 bit (1);			/* flag for lvname_info/ get_lv_pvinfo */
dcl     auth_mnemonics	 char (172);		/* string to hold human readable form of AIM stuff */
dcl     temp_str		 char (512);		/* temp area for formatting strings */
dcl     tstr_len		 fixed bin (21);		/* length of formatted strings */
dcl     pub_bit		 bit (1) aligned;		/* used in get_access */
dcl     set_access_range	 bit (1) aligned;		/* indicator for get_access/get_access_for_attach entries */

dcl     access_class_range	 (2) bit (72) aligned init ("0"b, sys_info$access_class_ceiling);

dcl     1 aa		 aligned,			/* access for lv.** segs */
	2 name		 char (32) init ("*.*.*"),
	2 mode		 bit (36) init ("101"b),
	2 mbz		 bit (36) init ((36)"0"b),
	2 code		 fixed bin (35);

dcl     1 dacl		 aligned,			/* access for >lv */
	2 name		 char (32) init ("*.SysAdmin.*"),
	2 mode		 bit (36) init ("111"b),
	2 code		 fixed bin (35);

dcl     1 CBI		 aligned like create_branch_info;


/* STATIC */

dcl     testing		 bit (1) static init ("0"b);

dcl     sdtp		 ptr static init (null);
dcl     first		 bit (1) static init ("1"b);

dcl     PRIV		 (0:1) char (8) static options (constant) init ("private", "public");
dcl     LVNAME_LEGAL	 char (76) int static options (constant) init
						/* Valid characters for LV names. */
			 (" '_`~^+-.{}:!0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
dcl     PVNAME_LEGAL	 char (37) int static options (constant) init
			 ("0123456789abcdefghijklmnopqrstuvwxyz_");
dcl     PV		 bit (1) static init ("0"b) options (constant);
dcl     LV		 bit (1) static init ("1"b) options (constant);
dcl     INIT		 bit (1) static init ("0"b) options (constant);
dcl     DONT_INIT		 bit (1) static init ("1"b) options (constant);
dcl     SMALL_DT		 fixed bin (71) static options (constant) init (2019686401000000);
						/* 01/01/65 00:00:01 */

dcl     ROOT		 char (168) static init (">lv");
						/* may be changed by "test" entrypoint */
dcl     LV_DIR		 char (168) static init (">lv");
						/* may be changed by "test" entrypoint */

dcl     (REAL_LV_DIR)	 char (168) static init (">lv") options (constant);


/* MISC */

dcl     sys_info$access_class_ceiling
			 bit (72) aligned external;

dcl     (cleanup, any_other)	 condition;

dcl     (addr, baseno, clock, currentsize, fixed, hbound, index, lbound, length, null, rtrim, string, substr, unspec,
        verify)		 builtin;


/* ERROR CODES */

dcl     error_table_$improper_data_format
			 fixed bin (35) ext;
dcl     error_table_$unimplemented_version
			 fixed bin (35) ext;
dcl     error_table_$action_not_performed
			 fixed bin (35) ext;
dcl     error_table_$argerr	 fixed bin (35) ext;
dcl     error_table_$bad_segment
			 fixed bin (35) ext;
dcl     error_table_$unregistered_volume
			 fixed bin (35) ext;
dcl     error_table_$fatal_error
			 fixed bin (35) ext;
dcl     error_table_$bad_volid fixed bin (35) ext;
dcl     error_table_$incorrect_device_type
			 fixed bin (35) ext;
dcl     error_table_$segknown	 fixed bin (35) ext;
dcl     error_table_$segnamedup
			 fixed bin (35) ext;
dcl     error_table_$namedup	 fixed bin (35) ext;
dcl     error_table_$bad_arg	 fixed bin (35) ext;
dcl     error_table_$unexpected_condition
			 fixed bin (35) ext;


/* ENTRIES */

dcl     aim_check_$in_range	 entry (bit (72) aligned, (2) bit (72) aligned) returns (bit (1));
dcl     aim_check_$greater_or_equal
			 entry (bit (72) aligned, bit (72) aligned) returns (bit (1) aligned);
dcl     ioa_$general_rs	 entry (ptr, fixed bin, fixed bin, char (*), fixed bin (21), bit (1) aligned,
			 bit (1) aligned);
dcl     admin_gate_$syserr	 entry () options (variable);
dcl     admin_gate_$syserr_error_code
			 entry () options (variable);
dcl     admin_gate_$reclassify_sys_seg
			 entry (char (*), char (*), bit (72) aligned, fixed bin (35));
dcl     display_access_class_	 entry (bit (72) aligned) returns (char (32) aligned);
dcl     date_time_$format	 entry (char (*), fixed bin (71), char (*), char (*)) returns (char (250) var);
dcl     get_temp_segment_	 entry (char (*), ptr, fixed bin (35));
dcl     get_ring_		 entry () returns (fixed bin (3));
dcl     get_initial_ring_	 entry () returns (fixed bin);
dcl     release_temp_segment_	 entry (char (*), ptr, fixed bin (35));
dcl     vrm_lock_$vrm_data_init
			 entry ();
dcl     vrm_lock_$lock	 entry (fixed bin (35));
dcl     vrm_lock_$cleanup	 entry ();
dcl     cu_$arg_list_ptr	 entry (ptr);
dcl     cu_$arg_ptr		 entry (fixed bin, ptr, fixed bin (21), fixed bin (35));
dcl     cu_$level_get	 entry (fixed bin);
dcl     cu_$level_set	 entry (fixed bin);
dcl     com_err_		 entry () options (variable);
dcl     continue_to_signal_	 entry (fixed bin (35));
dcl     expand_path_	 entry (ptr, fixed bin (21), ptr, ptr, fixed bin (35));
dcl     expand_pathname_	 entry (char (*), char (*), char (*), fixed bin (35));
dcl     pathname_		 entry (char (*), char (*)) returns (char (168));
dcl     mdc_repair_$make_mdcs	 entry (char (*), bit (36) aligned, fixed bin (35));
dcl     mdc_repair_$update_lvid
			 entry (char (*), bit (36) aligned, bit (36) aligned, fixed bin (35));
dcl     mdc_repair_$rename_mdcs
			 entry (char (*), bit (36) aligned, char (*), fixed bin (35));
dcl     mdc_repair_$delete_mdcs
			 entry (char (*), bit (36) aligned, fixed bin (35));
dcl     mdc_repair_$recreate_mdcs
			 entry (char (*), bit (36) aligned, fixed bin (35));
dcl     mdc_init_		 entry ();
dcl     get_authorization_	 entry () returns (bit (72) aligned);
dcl     get_privileges_	 entry () returns (bit (36) aligned);
dcl     get_group_id_	 entry () returns (char (32));
dcl     hcs_$replace_acl	 entry (char (*), char (*), ptr, fixed bin, bit (1), fixed bin (35));
dcl     hcs_$append_link	 entry (char (*), char (*), char (*), fixed bin (35));
dcl     hcs_$chname_seg	 entry (ptr, char (*), char (*), fixed bin (35));
dcl     hcs_$create_branch_	 entry (char (*), char (*), ptr, fixed bin (35));
dcl     hcs_$get_user_raw_mode entry (char (*), char (*), char (*), bit (36) aligned, fixed bin (35));
dcl     delete_$path	 entry (char (*), char (*), bit (36) aligned, char (*), fixed bin (35));
dcl     delete_$ptr		 entry (ptr, bit (36) aligned, char (*), fixed bin (35));
dcl     hcs_$add_dir_acl_entries
			 entry (char (*), char (*), ptr, fixed bin, fixed bin (35));
dcl     hcs_$initiate	 entry (char (*), char (*), char (*), fixed bin (1), fixed bin (2), ptr, fixed bin (35));
dcl     hcs_$make_seg	 entry (char (*), char (*), char (*), fixed bin (5), ptr, fixed bin (35));
dcl     hcs_$set_bc_seg	 entry (ptr, fixed bin (24), fixed bin (35));
dcl     hcs_$set_damaged_sw	 entry (char (*), char (*), bit (1), fixed bin (35));
dcl     hcs_$set_damaged_sw_seg
			 entry (ptr, bit (1), fixed bin (35));
dcl     hphcs_$set_rpv	 entry (char (*), char (*), fixed bin (35));
dcl     hcs_$status_minf	 entry (char (*), char (*), fixed bin (1), fixed bin (2), fixed bin (24), fixed bin (35));
dcl     hcs_$terminate_noname	 entry (ptr, fixed bin (35));
dcl     hcs_$truncate_seg	 entry (ptr, fixed bin (19), fixed bin (35));
dcl     read_allowed_	 entry (bit (72) aligned, bit (72) aligned) returns (bit (1) aligned);
dcl     write_allowed_	 entry (bit (72) aligned, bit (72) aligned) returns (bit (1) aligned);
dcl     unique_chars_	 entry (bit (*)) returns (char (15));
dcl     convert_access_class_$to_string_short
			 entry (bit (72) aligned, char (*), fixed bin (35));
%page;
/* ===================================================== */

/* INIT - Entry called during system initialization to get set up. */
/*          Salvaging, if necessary, is done later by mdx$init. */

init:
     entry (a_dtp);					/* Called from mdx */

/**** **************************************************************************

      This entry is called at system initialization time by mdx$init (which is
      invoked by disk_table_$init just prior to ring-1 initializer command level).
      This "init" entry simply saves a copy of the disk_table points for later
      use, sets up its lock segment >sl1>vrm_data, and calls mdc_init_ (which
      simply sets up the lock segment for Master Directory Control).

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$init";

	sdtp = a_dtp;				/* Save ptr to disk_table */
	call hcs_$set_damaged_sw_seg (sdtp, "0"b, (0));	/* We will salvage */
	call vrm_lock_$vrm_data_init ();		/* set up lock seg */
	call mdc_init_;				/* get master directory control going */

	return;
%page;

/* ===================================================== */
/* Primitive entry points to read and write volume registration segments.

   read_lvr
   read_pvr
   add_lvr
   add_pvr
   delete_lvr
   delete_pvr
   change_lvr
   change_pvr

   These entries are called through mdc_priv_ from volume_registration_cmds_ */
/* ===================================================== */



/* READ_LVR - Entry to return registration database for given LV */

read_lvr:
     entry (a_lvname, a_ptr, a_ec);

/**** **************************************************************************

      Reads the LV registration data, including the data for all
      PVs belonging to that LV, into a place provided by the caller.
      The caller supplies the PV name.

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$read_lvr";

	on any_other call emergency_exit;
	on cleanup call clean_up;

/* COPY ARGS */
	lvname = a_lvname;
	uvrp = a_ptr;

	call locate_ (lvname, LV, INIT);		/* Parse name */

	uvrp -> volume_registration = volume_registration;/* Copy whole seg to user ring */

	ec = 0;
	goto EXIT;
%page;


/* READ_PVR - Entry to return data structure for given PV */

read_pvr:
     entry (a_pvname, a_ptr, a_ec);			/* Same thing given pvname */

/**** **************************************************************************

      Reads the PV registration data for a single PV into a place provided by the
      caller.  The caller supplies the PV name.

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$read_pvr";

	on any_other call emergency_exit;
	on cleanup call clean_up;

/* COPY ARGS */
	pvname = a_pvname;
	upvrp = a_ptr;

	call locate_ (pvname, PV, INIT);		/* Parse name */

	do i = 1 to volume_registration.npv;
	     if volume_registration.pv (i).pvname = pvname
	     then do;
		ec = 0;
						/* copy PV entry into user ring structure */
		upvrp -> pv_registration = volume_registration.pv (i);
		goto EXIT;
	     end;
	end;

	ec = error_table_$bad_segment;
	call announce_syserr ("PV entry missing from database ""pv.^a"".", pvname);

	goto EXIT;
%page;


/* ADD_LVR - Entry to create a registration database for a new LV */

add_lvr:
     entry (a_lvname, a_ptr, a_ec);

/**** **************************************************************************

      Adds a new registration data segment to the volume registration database.  The
      caller provides a volume_registration structure containing the LV attributes
      and the attributes for a single PV.  The structure is the same as that used
      for the ring-1 data segment.  If all validation passes, the caller's structure
      is copied into a new segment names "lv.LVNAME".  Three addnames are added to
      the segment corresponding to the LVID, PVNAME, and PVID.  This entry is not
      used for creating the RLV registration.  RLV registration is done during
      system initialization by the entry "check_volume_registration".  The Master
      Directory Control entry "create_mdcs" is called to create its data segment
      which corresponds to the LV registratoin data segment.

      The code is implemented like:

      entry
      setup
      copy args
      lock database
      check caller's LV and PV registration data
      check existance of LV and PV already in database
      make a new segment in database for this LV
      {if anything goes wrong after this, delete the new segment}
      copy caller's data into database
      add proper names to new segment
      call MDC to create its database for the new LV
      create proper database links if necessary
      cleanup
      exit

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$add_lvr";

	on any_other call emergency_exit;
	on cleanup call clean_up;

/* COPY ARGS */
	lvname = a_lvname;
	uvrp = a_ptr;
	call get_tseg;				/* place to copy user structure */
	vrp = tsegp;
						/* we'll check to make sure number of entries is correct
						   before attempting to copy the beast into our ring.
						   It'll be checked again after copying the structure
						   by "check_new_lv" */
	if uvrp -> volume_registration.npv ^= 1
	then do;
	     ec = error_table_$bad_arg;
	     goto EXIT;
	end;
	volume_registration = uvrp -> volume_registration;

/* Verify consistancy of arguments */
	if verify (lvname, LVNAME_LEGAL) ^= 0 | volume_registration.lvname ^= lvname
	then do;
	     ec = error_table_$argerr;
	     goto EXIT;
	end;

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call check_new_lv (vrp);
	call check_new_pv (addr (volume_registration.pv (1)));

	if name_exists (LV_DIR, "lv." || lvname) |
	     name_exists (LV_DIR, "lvid." || unique_chars_ ((volume_registration.lvid))) |
	     name_exists (LV_DIR, "pv." || volume_registration.pv (1).pvname) |
	     name_exists (LV_DIR, "pvid." || unique_chars_ ((volume_registration.pv (1).pvid)))
	then do;
	     ec = error_table_$namedup;
	     goto EXIT;
	end;

	call locate_ (lvname, LV, DONT_INIT);		/* Just parse th name */
	call hcs_$make_seg (dn, en, "", 1010b, databasep, ec);
						/* Create new lv seg */
	if ec ^= 0
	then goto EXIT;
	nvrp = databasep;

	call hcs_$replace_acl (dn, en, addr (aa), 1, "0"b /* SysDaemon OK */, ec);
	if ec ^= 0
	then goto REMOVE_NEW_LV;

	call admin_gate_$reclassify_sys_seg (dn, en, sys_info$access_class_ceiling, ec);
	if ec ^= 0
	then goto REMOVE_NEW_LV;

	nvrp -> volume_registration = volume_registration;/* Copy whole structure */
	vrp = nvrp;				/* Now refer to the new copy */
	pvrp = addr (volume_registration.pv (1));	/* ... and to the only PV */

/* If any of the following operations fail we'll back out
   the new LV simply by deleting its database */

	uname = "lvid." || unique_chars_ ((volume_registration.lvid));
	call hcs_$chname_seg (vrp, "", uname, ec);
	if ec ^= 0
	then goto REMOVE_NEW_LV;
	call hcs_$chname_seg (vrp, "", "pv." || pv_registration.pvname, ec);
	if ec ^= 0
	then goto REMOVE_NEW_LV;
	uname2 = "pvid." || unique_chars_ ((pv_registration.pvid));
	call hcs_$chname_seg (vrp, "", uname2, ec);
	if ec ^= 0
	then goto REMOVE_NEW_LV;

	call mdc_repair_$make_mdcs (lvname, volume_registration.lvid, ec);
	if ec ^= 0
	then goto REMOVE_NEW_LV;

	call log_it ("Added new LV ""^a"".", lvname);
	call log_it ("Added new PV ""^a"" to LV ""^a"".", volume_registration.pv (1).pvname, lvname);

	if dn ^= LV_DIR
	then do;
						/* add links */
	     call hcs_$append_link (LV_DIR, en, pathname_ (dn, en), ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not append link for LV name.");
	     call hcs_$append_link (LV_DIR, uname, pathname_ (dn, uname), ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not append link for LVID.");
	     call hcs_$append_link (LV_DIR, "pv." || pv_registration.pvname,
		pathname_ (dn, "pv." || pv_registration.pvname), ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not append link for PV name.");
	     call hcs_$append_link (LV_DIR, uname2, pathname_ (dn, uname2), ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not append link for PVID.");
	end;

	ec = 0;					/* success! */

	goto EXIT;


REMOVE_NEW_LV:					/* come to this point if something went wrong
						   in the middle of new database creation.  */

	string (delete_options) = ""b;
	delete_options.force = "1"b;
	delete_options.segment = "1"b;
	delete_options.raw = "1"b;
	call delete_$ptr (nvrp, string (delete_options), "", ec2);
	if ec2 ^= 0
	then
	     call announce_syserr_code (ec2, "Unable to back out database ""lv.^a"".", lvname);
	else databasep = null ();			/* be nice to our clean_up handler */
	goto EXIT;
%page;


/* ADD_PVR - Entry to add a new PV entry to an existing LV */

add_pvr:
     entry (a_lvname, a_ptr, a_ec);

/**** **************************************************************************

      This entry adds the registration data for a single PV to an existing LV
      registration data segment.  If all attributes in the given pv_registration
      structure are valid, it is added to the end of the "pv" array in the LV data
      segment.  Two addnames are appended to the LV registration data segment entry
      corresponding to the PV name and PVID.  If the LV registration data segment is
      in ">", two links are created in ">lv" corresponding to these two new
      addnames.

      The code is implemented like:

      entry
      setup
      copy args
      check caller's registration data for new PV
      lock database
      check existance of PV already in database
      find proper LV data segment for this new PV
      search LV data for PV already exists (DB consistancy check)
      copy caller's PV data into database
      add proper names to LV data segment
      bump LV's number of PV's (to include new PV data)
      add proper database links if necessary
      cleanup
      exit

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$add_pvr";

	on any_other call emergency_exit;
	on cleanup call clean_up;

/* COPY ARGS */
	lvname = a_lvname;
	upvrp = a_ptr;
	call get_tseg;				/* place to copy user's structure */
	pvrp = tsegp;
	pv_registration = upvrp -> pv_registration;

	call check_new_pv (pvrp);

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

/* Check to see if there's already a database with this name */
	uname = "pvid." || unique_chars_ ((pv_registration.pvid));
	if name_exists (LV_DIR, "pv." || pv_registration.pvname) | name_exists (LV_DIR, uname)
	then do;
	     ec = error_table_$namedup;
	     goto EXIT;
	end;

	call locate_ (lvname, LV, INIT);

/* we've already checked for its existance above, here we'll
   double check to make sure there's no record with the pvname
   already in the file.	*/
	do i = 1 to volume_registration.npv while (volume_registration.pv (i).pvname ^= pv_registration.pvname);
	end;
	if i <= volume_registration.npv
	then do;
	     call announce_syserr ("Extra PV entry ""^a"" in database ""lv.^a"".", pvname, lvname);
	     ec = error_table_$bad_segment;
	     goto EXIT;
	end;

	volume_registration.pv (i) = pv_registration;	/* OOB ref */
	pvrp = addr (volume_registration.pv (i));	/* start using DB */

	pvname = pv_registration.pvname;
	call hcs_$chname_seg (vrp, "", "pv." || pvname, ec);
	if ec ^= 0
	then goto EXIT;
	call hcs_$chname_seg (vrp, "", uname, ec);
	if ec ^= 0
	then do;
						/* must remove other name we just put on */
	     call hcs_$chname_seg (vrp, "pv." || pvname, "", ec2);
	     if ec2 ^= 0
	     then call announce_syserr_code (ec2, "Unable to back out database addname ""pv.^a"".", pvname);
	     goto EXIT;
	end;

	volume_registration.npv = i;			/* now, let it be legally in the array */

	call log_it ("Added new PV ""^a"" to LV ""^a"".", pvname, lvname);

	if dn ^= LV_DIR
	then do;
						/* add links */
	     call hcs_$append_link (LV_DIR, "pv." || pvname, pathname_ (dn, "pv." || pvname), ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not append link for PV name.");
	     call hcs_$append_link (LV_DIR, uname, pathname_ (dn, uname), ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not append link for PVID.");
	end;

	ec = 0;

	goto EXIT;
%page;


/* DELETE_LVR - Entry to remove a volume registration database. */

delete_lvr:
     entry (a_lvname, a_ec);

/**** **************************************************************************

      This entry deletes the registration data segment for the given LV.  If the
      segment was in ">", the links corresponding to the LV and all the PVs are
      removed from ">lv".  The Master Directory Control entry "delete_mdcs" is
      called to delete its data segment which corresponds to the deleted LV
      registration data segment.

      The code is implemented like:

      entry
      setup
      copy args
      lock database
      locate LV registration data segment
      copy data to temporary
      delete the segment
      log deletion of each PV
      delete associated database links if necessary
      call MDC to delete its database for the LV
      cleanup
      exit

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$delete_lvr";

	on any_other call emergency_exit;
	on cleanup call clean_up;

/* COPY ARGS */
	lvname = a_lvname;

	call get_tseg;

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (lvname, LV, INIT);
	lvid = volume_registration.lvid;		/* save this for later... */

	call mdc_repair_$delete_mdcs (lvname, lvid, ec);
	if ec ^= 0
	then goto EXIT;

	tsegp -> volume_registration = volume_registration;
						/* make copy so we can log individual PV deletions later. */

	string (delete_options) = ""b;
	delete_options.force = "1"b;
	delete_options.segment = "1"b;
	delete_options.raw = "1"b;
	call delete_$ptr (vrp, string (delete_options), "", ec);
						/* this removes the LV (and all its PVs) */
	if ec ^= 0
	then goto EXIT;
	databasep = null ();			/* be nice to our clean_up handler */

	vrp = null;				/* so we dont ref deleted seg */

	do i = 1 to tsegp -> volume_registration.npv;
	     pvname = tsegp -> volume_registration.pv (i).pvname;
	     pvid = tsegp -> volume_registration.pv (i).pvid;
	     call log_it ("Deleted PV ""^a"" from LV ""^a"".", pvname, lvname);
	     if dn ^= LV_DIR
	     then do;
						/* remove links */
		string (delete_options) = ""b;
		delete_options.force = "1"b;
		delete_options.link = "1"b;
		delete_options.raw = "1"b;
		call delete_$path (LV_DIR, "pv." || pvname, string (delete_options), "", ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Could not remove PV name database link.");
		call delete_$path (LV_DIR, "pvid." || unique_chars_ ((pvid)), string (delete_options), "", ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Could not remove PVID database link.");
	     end;
	end;
	call log_it ("Deleted LV ""^a"".", lvname);

	if dn ^= LV_DIR
	then do;
						/* remove links for LV */
	     string (delete_options) = ""b;
	     delete_options.force = "1"b;
	     delete_options.link = "1"b;
	     delete_options.raw = "1"b;
	     call delete_$path (LV_DIR, "lv." || lvname, string (delete_options), "", ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not remove LV name database link.");
	     call delete_$path (LV_DIR, "lvid." || unique_chars_ ((lvid)), string (delete_options), "", ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not remove LVID database link.");
	end;

	go to EXIT;
%page;


/* DELETE_PVR - Entry to remove a single PV entry from an LV database. */

delete_pvr:
     entry (a_pvname, a_ec);

/**** **************************************************************************

      This entry deletes the registration data for a single PV.  The two addnames
      corresponding to the PV are removed from the LV registration data segment.
      The "pv" array is compressed so that the remaining active entries are
      contiguous.  If the registration data segment is in ">", the two links
      corresponding to the PV are removed from ">lv".

      The code is implemented like:

      entry
      setup
      copy args
      lock database
      locate database segment containing the PV
      find the PV entry
      remove the entry
      adjust "number of PVs" in the header
      delete associated database links if necessary
      cleanup
      logout

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$delete_pvr";

	on any_other call emergency_exit;
	on cleanup call clean_up;

/* COPY ARGS */
	pvname = a_pvname;

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (pvname, PV, INIT);		/* get volume registration database containing the PV */

	if volume_registration.npv = 1
	then do;					/* we won't delete the last PV */
	     ec = error_table_$action_not_performed;
	     goto EXIT;
	end;

	do i = 1 to volume_registration.npv while (volume_registration.pv (i).pvname ^= pvname);
	end;

	if i > volume_registration.npv
	then do;
	     ec = error_table_$bad_segment;
	     call announce_syserr ("PV entry missing from database ""pv.^a"".", pvname);
	     goto EXIT;
	end;

/* get rid of the names */
	call hcs_$chname_seg (vrp, "pv." || pvname, "", ec);
	if ec ^= 0
	then goto EXIT;
	uname = "pvid." || unique_chars_ ((volume_registration.pv (i).pvid));
	call hcs_$chname_seg (vrp, uname, "", ec);
	if ec ^= 0
	then do;
						/* put other name back on to retain consistancy */
	     call hcs_$chname_seg (vrp, "", "pv." || pvname, ec2);
	     if ec2 ^= 0
	     then call announce_syserr_code (ec2, "Unable to replace database name ""pv.^a"".", pvname);
	     goto EXIT;
	end;

/* squash the database */
	do j = i + 1 to volume_registration.npv;
	     volume_registration.pv (j - 1) = volume_registration.pv (j);
	end;
	volume_registration.npv = volume_registration.npv - 1;

	call hcs_$truncate_seg (vrp, currentsize (volume_registration), ec);
	if ec ^= 0
	then call announce_syserr_code (ec, "Trimming registration.");

	call log_it ("Deleted PV ""^a"" from LV ""^a"".", pvname, volume_registration.lvname);

	call locate_ ((volume_registration.lvname), LV, DONT_INIT);
						/* find out real location of segment */
	if dn ^= LV_DIR
	then do;
						/* remove links */
	     string (delete_options) = ""b;
	     delete_options.force = "1"b;
	     delete_options.link = "1"b;
	     delete_options.raw = "1"b;
	     call delete_$path (LV_DIR, "pv." || pvname, string (delete_options), "", ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not remove PV name database link.");
	     call delete_$path (LV_DIR, uname, string (delete_options), "", ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, "Could not remove PVID database link.");
	end;

	ec = 0;

	goto EXIT;				/* all done */
%page;


/* CHANGE_LVR - Entry to change logical volume data. */

change_lvr:
     entry (a_lvname, a_ptr, a_ec);

/**** **************************************************************************

      This entry changes selected attributes of an LV.  The caller supplies a
      volume_registration structure with no "pv" array entries.  All attributes are
      validated before any changes are made.  Changes to the LV name or LVID are
      propagated to Master Directory Control through a call to its entry
      "update_lvid" and "rename_mdcs".  Also, changes to the LV name or LVID make it
      necessary to change the LV registration data segment names (and possibly the
      names of links in ">lv").

      The code is implemented like:

      entry
      setup
      copy args
      check caller's registration data structure
      lock the database
      locate LV data segment
      make LVID change if requested
      make NAME change if requested (back out LVID change if unable to make NAME change)
      make other changes as requested
      cleanup
      exit

      ************************************************************************** ****/

/* a_lvname is the current name of the volume, it may change
   depending upon the incoming value of volume_registration.lvname */

	MYNAME = "volume_registration_mgr_$change_lvr";

	on any_other call emergency_exit;
	on cleanup call clean_up;

/* COPY ARGS */
	lvname = a_lvname;
	uvrp = a_ptr;
	call get_tseg;				/* space for copy of user's structure */
	nvrp = tsegp;
						/* let's make sure it isn't just garbage */
						/* we'll check again after the copy */
	if uvrp -> volume_registration.npv ^= 0
	then do;
	     ec = error_table_$bad_arg;
	     goto EXIT;
	end;
	nvrp -> volume_registration = uvrp -> volume_registration;
	if nvrp -> volume_registration.npv ^= 0
	then do;
	     ec = error_table_$bad_arg;
	     goto EXIT;
	end;

	call check_lv (nvrp);

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (lvname, LV, INIT);


/* The next bit of code is a touch hairy.  We need to add (potentially)
   2 names in two places each.  Should anything go wrong, we need to undo
   names already in place.  Thus, there's a slightly complicated mechanism
   for backing out partial changes.		*/

	lvid_changed = "0"b;
	if volume_registration.lvid ^= nvrp -> volume_registration.lvid
	then do;
	     new_lvid = nvrp -> volume_registration.lvid;
	     old_lvid = volume_registration.lvid;

	     old_uname = "lvid." || unique_chars_ ((old_lvid));
	     new_uname = "lvid." || unique_chars_ ((new_lvid));

	     call hcs_$chname_seg (vrp, old_uname, new_uname, ec);
	     if ec ^= 0
	     then do;
		if ec ^= error_table_$namedup &
		     ec ^= error_table_$segnamedup	/* if not normal error... */
		then call announce_syserr_code (ec, "Unexpected trouble changing name ""^a"".", old_uname);
		goto EXIT;
	     end;
	     volume_registration.lvid = new_lvid;	/* whew */
	     lvid_changed = "1"b;

	     call mdc_repair_$update_lvid (lvname, old_lvid, new_lvid, ec);
	     if ec ^= 0
	     then do;
						/* must back out the change... */
		call hcs_$chname_seg (vrp, new_uname, old_uname, ec2);
		if ec2 ^= 0
		then call announce_syserr_code (ec2, "Unable to change name ""^a"" back to ""^a"".",
			new_uname, old_uname);
		else volume_registration.lvid = old_lvid;
		goto EXIT;
	     end;

/* we won't log change yet - first see if name change goes OK */
	end;

	if volume_registration.lvname ^= nvrp -> volume_registration.lvname
	then do;
	     new_lvname = nvrp -> volume_registration.lvname;
	     old_lvname = volume_registration.lvname;

	     if old_lvname = "root"
	     then do;
						/* can't let this happen */
		ec = error_table_$bad_arg;
		go to back_out_lvid_change;		/* and exit */
	     end;

	     call hcs_$chname_seg (vrp, "lv." || old_lvname, "lv." || new_lvname, ec);
	     if ec ^= 0
	     then do;
						/* ugh! now we have to back out preceeding lvid changes. */
		if ec ^= error_table_$namedup &
		     ec ^= error_table_$segnamedup	/* if not normal error... */
		then call announce_syserr_code (ec, "Unexpected error changing name on ""lv.^a"".", old_lvname);
back_out_lvid_change:
		if lvid_changed
		then do;
		     call hcs_$chname_seg (vrp, new_uname, old_uname, ec2);
		     if ec2 ^= 0
		     then call announce_syserr_code (ec2, "Unable to change name ""^a"" back to ""^a"".",
			     new_uname, old_uname);
		     else do;
			volume_registration.lvid = old_lvid;
			call mdc_repair_$update_lvid (lvname, new_lvid, old_lvid, ec2);
			if ec2 ^= 0
			then call announce_syserr_code (ec2, "Unable to back out LVID change to ""^a.mdcs"".",
				lvname);
		     end;
		end /* back_out_lvid_change */;

		goto EXIT;
	     end;

/* update the internal name */
	     volume_registration.lvname = new_lvname;

/* now make sure MDC keeps up */
	     call mdc_repair_$rename_mdcs (old_lvname, volume_registration.lvid, new_lvname, ec);
	     if ec ^= 0
	     then do;
						/* now we need to back out lvname change AND lvid change */
		call hcs_$chname_seg (vrp, "lv." || new_lvname, "lv." || old_lvname, ec2);
		if ec2 ^= 0
		then call announce_syserr_code (ec2, "Unable to change name ""^a"" back to ""^a"".",
			new_lvname, old_lvname);
		else do;
		     volume_registration.lvname = old_lvname;
		     goto back_out_lvid_change;
		end;

		goto EXIT;
	     end;

	     call log_it ("Changed name of LV ""^a"" to ""^a"".", lvname, new_lvname);

	     if dn ^= LV_DIR
	     then do;
						/* adjust link */
		string (delete_options) = ""b;
		delete_options.force = "1"b;
		delete_options.link = "1"b;
		delete_options.raw = "1"b;
		call delete_$path (LV_DIR, "lv." || old_lvname, string (delete_options), "", ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Couldn't delete link for old LV name.");
		call hcs_$append_link (LV_DIR, "lv." || new_lvname, pathname_ (dn, "lv." || new_lvname), ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Couldn't create link for new LV name.");
	     end;
	end;

	if lvid_changed
	then do;					/* now we can log the LVID change - we wont be backing it out */
	     call log_it ("Changed LVID of LV ""^a"" from ""^w"" to ""^w"".", lvname, old_lvid, new_lvid);

	     if dn ^= LV_DIR
	     then do;
						/* adjust link */
		string (delete_options) = ""b;
		delete_options.force = "1"b;
		delete_options.link = "1"b;
		delete_options.raw = "1"b;
		call delete_$path (LV_DIR, old_uname, string (delete_options), "", ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Couldn't delete link for old LVID.");
		call hcs_$append_link (LV_DIR, new_uname, pathname_ (dn, new_uname), ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Couldn't create link for new LVID.");
	     end;
	end;

/* the rest of the changes are easy... */

	if volume_registration.access_class_range (2) ^= nvrp -> volume_registration.access_class_range (2)
	then do;
	     old_aa_str = display_access_class_ (volume_registration.access_class_range (2));
	     new_aa_str = display_access_class_ (nvrp -> volume_registration.access_class_range (2));
	     call convert_access_class_$to_string_short (nvrp -> volume_registration.access_class_range (2),
		auth_mnemonics, ec);
	     if ec ^= 0
	     then do;
		call announce_syserr_code (ec, "Unable to convert max auth to name_string.");
		auth_mnemonics = "can't convert";
	     end;
	     volume_registration.access_class_range (2) = nvrp -> volume_registration.access_class_range (2);
	     call log_it ("Changed max access class of LV ""^a"" from ""^a"" to ""^a"". (^a)",
		lvname, old_aa_str, new_aa_str, auth_mnemonics);
	end;

	if volume_registration.access_class_range (1) ^= nvrp -> volume_registration.access_class_range (1)
	then do;
	     old_aa_str = display_access_class_ (volume_registration.access_class_range (1));
	     new_aa_str = display_access_class_ (nvrp -> volume_registration.access_class_range (1));
	     call convert_access_class_$to_string_short (nvrp -> volume_registration.access_class_range (1),
		auth_mnemonics, ec);
	     if ec ^= 0
	     then do;
		call announce_syserr_code (ec, "Unable to convert min auth to name_string.");
		auth_mnemonics = "can't convert";
	     end;
	     volume_registration.access_class_range (1) = nvrp -> volume_registration.access_class_range (1);
	     call log_it ("Changed min access class of LV ""^a"" from ""^a"" to ""^a"" (^a).",
		lvname, old_aa_str, new_aa_str, auth_mnemonics);
	end;

	if volume_registration.volume_owner ^= nvrp -> volume_registration.volume_owner
	then do;
	     call log_it ("Changed owner of LV ""^a"" from ""^a"" to ""^a"".", lvname,
		volume_registration.volume_owner, nvrp -> volume_registration.volume_owner);
	     volume_registration.volume_owner = nvrp -> volume_registration.volume_owner;
	end;

	if volume_registration.public ^= nvrp -> volume_registration.public
	then do;
	     call log_it ("Changed LV ""^a"" to ^[PUBLIC^;PRIVATE^].", lvname, volume_registration.public);
	     volume_registration.public = nvrp -> volume_registration.public;
	end;

	if volume_registration.acs_path ^= nvrp -> volume_registration.acs_path
	then do;
	     volume_registration.acs_path = nvrp -> volume_registration.acs_path;
	     call log_it ("Changed ACS pathname for LV ""^a"" to ""^a"".", lvname, volume_registration.acs_path);
	end;

	ec = 0;					/* to be sure */

	goto EXIT;
%page;


/* CHANGE_PVR - Entry to change information about a particular PV */

change_pvr:
     entry (a_pvname, a_ptr, a_ec);

/**** **************************************************************************

      This entry changes selected attributes of a PV.  The caller supplies a
      pv_registration structure with all members initialized.  All attributes are
      validated before any changes are made.  If the LV name or LVID changes it is
      necessary to also change the corresponding names on the LV registration data
      segment (and possibly the names of links in ">lv").

      The code is implemented like:

      entry
      setup
      copy_args
      check caller's registration data structure
      lock database
      locate registration data segment
      find PV entry in data segment
      make PVID change if requested
      make NAME change if requested (back out PVID change if error is encountered)
      make other changes as requested
      cleanup
      exit

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$change_pvr";

	on any_other call emergency_exit;
	on cleanup call clean_up;

/* COPY ARGS */
	pvname = a_pvname;
	upvrp = a_ptr;
	call get_tseg;				/* place to make copy of user's structure */
	npvrp = tsegp;
	npvrp -> pv_registration = upvrp -> pv_registration;
						/* copy the structure */

	call check_pv (npvrp);

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (pvname, PV, INIT);

	do i = 1 to volume_registration.npv while (volume_registration.pv (i).pvname ^= pvname);
	end;

	if i > volume_registration.npv
	then do;
	     call announce_syserr ("PV entry missing from database ""pv.^a"".", pvname);
	     ec = error_table_$bad_segment;
	     goto EXIT;
	end;

	pvrp = addr (volume_registration.pv (i));

/* now - make the changes */

/* as in "change_lvr" we have to make potentially two name changes
   but only to the volume registration database (i.e. not any MDCS) */

	pvid_changed = "0"b;
	if pv_registration.pvid ^= npvrp -> pv_registration.pvid
	then do;
	     old_pvid = pv_registration.pvid;
	     new_pvid = npvrp -> pv_registration.pvid;
	     old_uname = "pvid." || unique_chars_ ((old_pvid));
	     new_uname = "pvid." || unique_chars_ ((new_pvid));

	     call hcs_$chname_seg (vrp, old_uname, new_uname, ec);
	     if ec ^= 0
	     then do;
		if ec ^= error_table_$namedup &
		     ec ^= error_table_$segnamedup	/* check for unusual errors... */
		then call announce_syserr_code (ec, "Unexpected trouble changing name ""^a"".", old_uname);
		goto EXIT;
	     end;

	     pv_registration.pvid = new_pvid;
	     pvid_changed = "1"b;
	end;

	if pv_registration.pvname ^= npvrp -> pv_registration.pvname
	then do;
	     old_pvname = pv_registration.pvname;
	     new_pvname = npvrp -> pv_registration.pvname;

	     if old_pvname = "rpv"
	     then do;
		ec = error_table_$bad_arg;		/* can't let 'em do this */
		goto back_out_pvid_change;		/* and exit */
	     end;

	     call hcs_$chname_seg (vrp, "pv." || old_pvname, "pv." || new_pvname, ec);
	     if ec ^= 0
	     then do;
		if ec ^= error_table_$namedup &
		     ec ^= error_table_$segnamedup	/* check for unusual errors... */
		then call announce_syserr_code (ec, "Unexpected trouble changing name ""^a"".", old_pvname);

back_out_pvid_change:
		if pvid_changed
		then do;				/* must back out other name change */
		     call hcs_$chname_seg (vrp, new_uname, old_uname, ec2);
		     if ec2 ^= 0
		     then call announce_syserr_code (ec2, "Unable to back out PVID change to ""^a.mdcs"".", pvname);
		     else pv_registration.pvid = old_pvid;
		end;
		goto EXIT;
	     end;
	     pv_registration.pvname = new_pvname;
	     call log_it ("Changed name of PV ""^a"" to ""^a"".", old_pvname, new_pvname);
	     call locate_ ((volume_registration.lvname), LV, DONT_INIT);
						/* find out real location of segment */
	     if dn ^= LV_DIR
	     then do;
						/* adjust link */
		string (delete_options) = ""b;
		delete_options.force = "1"b;
		delete_options.link = "1"b;
		delete_options.raw = "1"b;
		call delete_$path (LV_DIR, "pv." || old_pvname, string (delete_options), "", ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Couldn't delete link for old PV name.");
		call hcs_$append_link (LV_DIR, "pv." || new_pvname, pathname_ (dn, "pv." || new_pvname), ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Couldn't create link for new PV name.");
	     end;
	end;

	if pvid_changed
	then do;
						/* now we can log that fact - since it ain't gonna be backed out */
	     call log_it ("Changed PVID of PV ""^a"" from ""^w"" to ""^w"".", pvname, old_pvid, new_pvid);
	     call locate_ ((volume_registration.lvname), LV, DONT_INIT);
						/* find out real location of segment */
	     if dn ^= LV_DIR
	     then do;
						/* adjust link */
		string (delete_options) = ""b;
		delete_options.force = "1"b;
		delete_options.link = "1"b;
		delete_options.raw = "1"b;
		call delete_$path (LV_DIR, old_uname, string (delete_options), "", ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Couldn't delete link for old PVID.");
		call hcs_$append_link (LV_DIR, new_uname, pathname_ (dn, new_uname), ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Couldn't create link for new PVID.");
	     end;
	end;

	if pv_registration.model ^= npvrp -> pv_registration.model
	then do;
	     call log_it ("Changed model of PV ""^a"" from ""^d"" to ""^d"".", pvname,
		MODELN (pv_registration.model), MODELN (npvrp -> pv_registration.model));
	     pv_registration.model = npvrp -> pv_registration.model;
	end;

	if pv_registration.location ^= npvrp -> pv_registration.location
	then do;
	     call log_it ("Changed location of PV ""^a"" from ""^a"" to ""^a"".", pvname, pv_registration.location,
		npvrp -> pv_registration.location);
	     pv_registration.location = npvrp -> pv_registration.location;
	end;

	if pv_registration.mfg_serial ^= npvrp -> pv_registration.mfg_serial
	then do;
	     call log_it ("Changed mfg_serial of PV ""^a"" from ""^a"" to ""^a"".", pvname, pv_registration.mfg_serial,
		npvrp -> pv_registration.mfg_serial);
	     pv_registration.mfg_serial = npvrp -> pv_registration.mfg_serial;
	end;

	if pv_registration.date_registered ^= npvrp -> pv_registration.date_registered
	then do;
	     old_dt_reg = date_time_$format ("date_time", pv_registration.date_registered, "", "");
	     new_dt_reg = date_time_$format ("date_time", npvrp -> pv_registration.date_registered, "", "");
	     call log_it ("Changed date_registered of PV ""^a"" from ""^a"" to ""^a"".",
		pvname, old_dt_reg, new_dt_reg);
	     pv_registration.date_registered = npvrp -> pv_registration.date_registered;
	end;

	if pv_registration.password ^= npvrp -> pv_registration.password
	then do;
	     call log_it ("Changed password of PV ""^a"".", pvname);
						/* don't tell what it was or now is... */
	     pv_registration.password = npvrp -> pv_registration.password;
	end;

	goto EXIT;
%page;

/* ------------------------------------------------------- */

/* Service entries called by various other programs which need to know about
   volume registration information. Status, dumper, master dir control, mount */

/* ------------------------------------------------------- */



/* PVNAME_INFO - Entry to return information about a given PV */

pvname_info:
     entry (a_pvname, a_pvid, a_lvname, a_lvid, a_device_type, a_ec);

/**** **************************************************************************

      This entry returns the PVID, LV name, LVID, and device type (model index)
      for a given PV name.

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$pvname_info";

	on any_other call emergency_exit;
	on cleanup call clean_up;

	pvname = a_pvname;

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (pvname, PV, INIT);
	do i = 1 to volume_registration.npv while (pvname ^= volume_registration.pv (i).pvname);
	end;
	if i > volume_registration.npv
	then do;
	     ec = error_table_$unregistered_volume;
	     call announce_syserr ("PV entry not found in database ""pv.^a"".", pvname);
	     goto EXIT;
	end;

	ec = 0;
	a_pvid = volume_registration.pv (i).pvid;
	a_device_type = volume_registration.pv (i).model;
	a_lvname = volume_registration.lvname;
	a_lvid = volume_registration.lvid;

	goto EXIT;
%page;

/* FIND_LVID - Entry to return LVID given LV name */

find_lvid:
     entry (a_lvname, a_lvid, a_ec);			/* Translate name to uid */

/**** **************************************************************************

      This entry returns the LVID for a given LV name.

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$find_lvid";

	on any_other call emergency_exit;
	on cleanup call clean_up;

	lvname = a_lvname;

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (lvname, LV, INIT);

	a_lvid = volume_registration.lvid;		/* Return the value */
	ec = 0;

	go to EXIT;
%page;

/* FIND_VOLNAME - Entry to return LV and PV names given PVID */

find_volname:
     entry (a_pvid, a_pvname, a_lvname, a_ec);		/* pvid -> pvname, lvname (dumper) */

/**** **************************************************************************

      This entry returns the PV name and LV name for a given PVID.

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$find_volname";

	on any_other call emergency_exit;
	on cleanup call clean_up;

	pvid = a_pvid;				/* copy input arg */
						/* init return values */
	a_pvname, a_lvname = "";

	uname = "pvid." || unique_chars_ ((pvid));	/* Use name on segment */

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_uid_ (pvid, PV, INIT);

	do i = 1 to volume_registration.npv while (volume_registration.pv (i).pvid ^= pvid);
	end;
	if i > volume_registration.npv
	then do;
	     call announce_syserr ("PV entry not found in database ""^a"".", uname);
	     ec = error_table_$bad_volid;
	     goto EXIT;
	end;

	a_pvname = volume_registration.pv (i).pvname;	/* Rah. Divulge volume name */
	a_lvname = volume_registration.lvname;		/* Hand back volume name */
	ec = 0;

	go to EXIT;
%page;

/* FIND_LVNAME - Entry to return LV name given the LVID */

find_lvname:
     entry (a_lvid, a_lvname, a_ec);			/* lvid -> lvname */

/**** **************************************************************************

      This entry returns the LV name for a given LVID.

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$find_lvname";

	on any_other call emergency_exit;
	on cleanup call clean_up;

	lvid = a_lvid;
	a_lvname = "";				/* init return value */

	uname = "lvid." || unique_chars_ ((lvid));	/* Use the trick names */

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_uid_ (lvid, LV, INIT);

	a_lvname = volume_registration.lvname;
	ec = 0;

	go to EXIT;
%page;

/* GET_ACCESS - Entry to return callers' access to given LV */

get_access:
     entry (a_lvname, a_level, a_mode, a_pub_bit, a_ec);	/* Effective mode to lv (mount, mdc) */

	set_access_range = "0"b;
	goto GET_ACCESS_JOIN;

get_access_for_attach:
     entry (a_lvname, a_level, a_mode, a_pub_bit, a_lv_access_class_range, a_ec);
						/* special entry for rcp_attach_lv_ */

	a_lv_access_class_range = sys_info$access_class_ceiling;
						/* initial value */
	set_access_range = "1"b;

GET_ACCESS_JOIN:


/**** **************************************************************************

      This entry computes the callers access mode to a given LV.  The code first
      checks the AIM access range and RCP_PRIVILEGE.  If neither of these are
      acceptable, the mode returned is "null".  Otherwise, the access will be
      computed from the ACS.  If no acs_path is registered, or if the ACS itself is
      missing, the access returned is "REW" for the owner, "null" for all others.

      NOTE: NOTE: this code should really be removed and made into "lv_access_kernel_"

      The code is implemented like:

      entry
      setup
      copy args
      lock database
      locate registration data segment for LV
      if process access class out of range or process not privileged
      then return NULL mode
      else if ACS exists for LV
      then return process's raw mode to ACS
      else if process is LV owner
      then return REW mode
      else return NULL mode
      cleanup
      exit

      ************************************************************************** ****/


	MYNAME = "volume_registration_mgr_$get_access";

	on any_other call emergency_exit;
	on cleanup call clean_up;

	lvname = a_lvname;				/* copy input args */
	level = a_level;

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (lvname, LV, INIT);

	if ^((read_allowed_ (get_authorization_ (), volume_registration.access_class_range (1))
	     & write_allowed_ (get_authorization_ (), volume_registration.access_class_range (2)))
	     | ((get_privileges_ () & RCP_PRIVILEGE) ^= ""b))
	then do;
	     mode = N_ACCESS;			/* if vol off aim limits, n.g. */
	     pub_bit = "0"b;
	end;
	else do;
	     if volume_registration.acs_path ^= ""
	     then do;
		call expand_pathname_ ((volume_registration.acs_path), dn, en, ec);
		if ec ^= 0
		then do;
		     call announce_syserr_code (ec, "Bad ACS path in database ""lv.^a"".", lvname);
		     goto NO_ACS;			/* check for owner mode */
		end;

		call hcs_$get_user_raw_mode (dn, en, "", mode, ec);
						/* ignores ring brackets and AIM */
		if ec ^= 0
		then goto NO_ACS;			/* check for owner mode */
		pub_bit = volume_registration.public;
	     end;
	     else do;
NO_ACS:						/* ACS need not exist */
		uname = get_group_id_ ();
		j = length (rtrim (uname));
		substr (uname, j - 1) = "";		/* Blank instance tag */
		j = index (uname, ".");
		pers = substr (uname, 1, j - 1);
		proj = substr (uname, j + 1);
		j = index (volume_registration.volume_owner, ".");
		opers = substr (volume_registration.volume_owner, 1, j - 1);
		oproj = substr (volume_registration.volume_owner, j + 1);

		if (pers = opers | opers = "*") & (proj = oproj | oproj = "*")
		then mode = REW_ACCESS;		/* Volume owner always REW */
		else if volume_registration.public
		then mode = RW_ACCESS;		/* Everybody has RW to public vols */
		else mode = N_ACCESS;		/* Private vol, no ACS */
		pub_bit = volume_registration.public;
	     end;

	end;

	a_mode = mode;				/* Tell user */
	a_pub_bit = pub_bit;
	if set_access_range
	then a_lv_access_class_range = volume_registration.access_class_range;

	ec = 0;
	go to EXIT;
%page;

/* LVNAME_INFO - Entry to return information about PVs given an LV name */

lvname_info:
     entry (a_lvname, a_pvap, a_npv, a_ec);

/**** **************************************************************************

      This entry returns a specialized structure of information on
      all PVs belonging to a given LV name.

      The code is implemented like:

      entry
      setup
      copy args
      lock database
      locate registration data segment for LV
      fill in caller's structure with info on all PVs in the LV
      cleanup
      exit

      ************************************************************************** ****/

dcl     pvap		 ptr;

dcl     1 pva		 (100) based (pvap) aligned,	/* This entrypoint fills in a struc */
	2 pvname		 char (32),		/* .. listing all volumes */
	2 device_type	 fixed bin,		/* .. and their model numbers */
	2 pvid		 bit (36);		/* and pvid */

	MYNAME = "volume_registration_mgr_$lvname_info";

	make_lve = "0"b;				/* dont fiddle with disk table */
	goto lv_pvinfo_common;
%page;

/* GET_LV_PVINFO - Entry to do same as LVNAME_INFO plus make LVE in disk table */
/*	This entry is called by disk_table_ (via initializer_mdc_) and by mdx */

get_lv_pvinfo:
     entry (a_lvname, a_pvap, a_npv, a_lvx, a_ec);	/* Get lv pv list, make lve */

/**** **************************************************************************

      This entry has the same function as "lvname_info".  In addition
      it will return the index of the corresponding LV disk_table entry.
      If there is no corresponding disk table LV entry, it will be created.

      The code is implemented like:

      entry
      setup
      copy args
      lock database
      locate registration data segment for LV
      locate disk table LV entry or make one if not already existing
      fill in caller's structure with info on all PVs in the LV
      cleanup
      exit

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$get_lv_pvinfo";

	make_lve = "1"b;				/* update disk table */


lv_pvinfo_common:

	on any_other call emergency_exit;
	on cleanup call clean_up;

	dtp = sdtp;
	dtep = null;				/* not needed at this entry */
	lvname = a_lvname;
	pvap = a_pvap;

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (lvname, LV, INIT);
	if make_lve
	then a_lvx = get_lve ();

	do i = 1 to volume_registration.npv;
	     pva (i).pvname = volume_registration.pv (i).pvname;
	     pva (i).device_type = volume_registration.pv (i).model;
	     pva (i).pvid = volume_registration.pv (i).pvid;
	end;
	a_npv = volume_registration.npv;
	ec = 0;
	go to EXIT;
%page;

/* FIND - Entry to fill in disk table from volume registration data */
/* 	This entry is called by mdx 		*/

find:
     entry (a_dtep, a_ec);

/**** **************************************************************************

      This entry will create an LV disk table entry given an index
      to a disk table PV entry.  The LV entry will correspond to the
      LV of the given PV.

      The code is implemented like:

      entry
      setup
      copy args
      lock database
      locate registration data segment containing PV
      locate disk table LV entry or create one if it doesnt exist
      locate PV entry in the data segment
      if model in registration data and disk table are the same
      then copy to registered PVID into the disk table
      else there's an error
      cleanup
      exit

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$find";

	on any_other call emergency_exit;
	on cleanup call clean_up;

	dtp = sdtp;
	dtep = a_dtep;
	pvname = dte.pvname;			/* dte.pvname is input */

	call vrm_lock_$lock (ec);
	if ec ^= 0
	then goto EXIT;

	call locate_ (pvname, PV, INIT);
	dte.lvx = get_lve ();

	do i = 1 to volume_registration.npv while (volume_registration.pv (i).pvname ^= dte.pvname);
	end;
	if i <= volume_registration.npv
	then do;
	     if dte.device_type = volume_registration.pv (i).model
	     then do;
		dte.pvid = volume_registration.pv (i).pvid;
						/* Found stuff. Get pvid */
		ec = 0;
	     end;
	     else ec = error_table_$incorrect_device_type;
	end;
	else do;
	     call announce_syserr ("PV entry missing from ""pv.^a"".", pvname);
	     ec = error_table_$bad_volid;
	end;

	go to EXIT;
%page;

/* CHECK_VOLUME_REGISTRATION -  Salvage the registration database. */

check_volume_registration:
     entry (a_dtep, a_ec);				/* salvager module */

/**** **************************************************************************

      This entry is called during system initialization to validate/re-create the
      volume registration database.  "mdx$init" (which had previously called our
      "init" entry) scans the old disk table and calls here for each entry that had
      a valid drive mounted in the previous session.  This is evidence that the PV
      should have a valid registration.

      On the first call this entry will create ">lv" if necessary (i.e.  if its a
      new system or was booted with NOLV).

      The disk table PV entry is checked to determine that it points to a valid disk
      table LV entry.  If not, the disk table LV entry is recreated from the PV's LV
      registration data.  If the LV registration can't be determined, nothing more
      can be done but issue a console message.

      If the disk table does contain a valid LV entry for the PV in question it is
      compared to the LV registration data.  If the LV registration disagrees with
      the disk table entry, or can't be found, it is recreated.  (Thus, the disk
      table is the more trusted database.)

      If the PV registration data disagrees with the disk table PV entry, the
      registration data is "fixed" to be the same.  If the PV registration doesn't
      exist in the LV registration data segment, it is recreated there.

      This entry also ensures that all proper addnames and links are in place.

      The code is implemented like:

      entry
      setup
      (no need to copy args or lock database)
      if first call then create ">lv" if it doesn't exist
      validate PV name in disk table entry

      if disk table LV entry invalid
      then if PV is registered
      then recreate disk table LV entry
      else return to caller as failure

      validate LV name in disk table entry
      if LV registration data segment non existant
      then create new one and fill in header
      else validate existing header information - recreate if anything in error

      create proper database link for LV name if necessary
      create proper data seg addname for LVID
      create proper database link for LVID if necessary

      create proper database link for MDC data seg if necessary
      call MDC to recreate its data seg for the LV as it sees fit

      create proper data seg addname for PV name
      create proper database link for PV name if necessary

      if PV entry does not exist in data seg
      then create it
      else validate PVID and device type against disk table entry
      - update registration entry with disk table info if in error

      create proper data seg addname for PVID
      create proper database link for PVID if necessary

      cleanup
      exit

      ************************************************************************** ****/

	MYNAME = "volume_registration_mgr_$check_volume_registration";

	on any_other
	     begin;
		a_ec = error_table_$unexpected_condition;
		goto CK_VREG_EXIT;
	     end;
	on cleanup ;

/* No locking is needed at this entrypoint since
   only the Initializer exists */

	a_ec = 0;
	if first
	then do;					/* We are always called at least once (for the root) */
	     first = "0"b;
	     i = length (rtrim (LV_DIR));
	     call expand_path_ (addr (LV_DIR), (i), addr (dn), addr (en), ec);
	     call hcs_$status_minf (dn, en, 0, type, bc, ec);
	     if ec ^= 0 | type ^= 2
	     then do;
		unspec (CBI) = ""b;
		CBI.version = create_branch_version_2;
		CBI.dir_sw = "1"b;
		CBI.parent_ac_sw = "1"b;
		CBI.mode = "111"b;
		CBI.rings (1) = 7;
		CBI.rings (2) = 7;
		CBI.rings (3) = 7;
		CBI.userid = get_group_id_ ();
		call hcs_$create_branch_ (dn, en, addr (CBI), ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Cannot re-create ^a.", LV_DIR);
		else do;
		     call hcs_$add_dir_acl_entries (dn, en, addr (dacl), 1, ec);
		     if ec ^= 0
		     then call announce_syserr_code (ec, "Cannot add SysAdmin access to ^a.", LV_DIR);
		end;
	     end;
	     if LV_DIR = REAL_LV_DIR
	     then do;				/* Make LV dir be on RPV */
		call hphcs_$set_rpv (dn, en, ec);
		if ec ^= 0
		then call announce_syserr_code (ec, "Putting LV dir on RPV");
	     end;
	end;

	dtp = sdtp;
	dtep = a_dtep;
	if verify (rtrim (dte.pvname), PVNAME_LEGAL) ^= 0
	then return;				/* Dont reregister crap */
	if dte.lvx <= 0 | dte.lvx > dt.n_lv_entries
	then go to lvfail;
	lvep = addr (dt.lv_array (dte.lvx));
	if ^lve.used
	then do;
lvfail:
	     pvname = dte.pvname;			/* Invalid lve. Try to recover */
	     call locate_ (pvname, PV, DONT_INIT);
	     call hcs_$initiate (dn, en, "", 0, 0, vrp, ec);
						/* look in pv seg for lvname */
	     if vrp = null
	     then do;				/* Cannot find pv */
		call announce_syserr_code (ec, "Cannot reregister PV ""^a"".", pvname);
		a_ec = ec;			/* unable to proceed */
		return;				/* .. because i dont know what lv */
	     end;
	     dte.lvx = get_lve ();
	     call hcs_$terminate_noname (vrp, (0));
	end;
	lvname = lve.lvname;
	if verify (lvname, LVNAME_LEGAL) ^= 0
	then return;
	call locate_ (lvname, LV, DONT_INIT);
	call hcs_$initiate (dn, en, "", 0, 0, vrp, ec);	/* LV should be registered */
	if vrp = null
	then do;
	     call hcs_$make_seg (dn, en, "", 1010b, vrp, ec);
						/* Wasn't. Make new */
	     if vrp = null
	     then do;
		call announce_syserr_code (ec, "Cannot reregister LV ""^a"".", lvname);
		a_ec = ec;
		return;
	     end;
	     call admin_gate_$reclassify_sys_seg (dn, en, sys_info$access_class_ceiling, ec);
	     if ec ^= 0
	     then call announce_syserr_code (ec, pathname_ (dn, en));
remake_lv:
	     volume_registration.version = Volume_Registration_Version_2;
	     volume_registration.lvid = lve.lvid;
	     volume_registration.lvname = lvname;
	     owner = get_group_id_ ();
	     i = length (rtrim (owner));
	     volume_registration.volume_owner = substr (owner, 1, i - 2);
	     volume_registration.public = lve.public;
	     volume_registration.access_class_range (1) = lve.min_access_class;
	     volume_registration.access_class_range (2) = lve.max_access_class;
	     volume_registration.acs_path = "";
	     call announce_syserr ("Reregistered ^a LV ^a LVID ^w", PRIV (fixed (lve.public, 1)), lvname, lve.lvid);
	     call hcs_$replace_acl (dn, en, addr (aa), 1, "0"b /* SysDaemon OK */, ec);
	end;
	else do;
	     call hcs_$set_damaged_sw_seg (vrp, "0"b, ec);/* Salvaging ourself */
	     ec = 0;
	     if volume_registration.lvname ^= lvname
	     then ec = 2;
	     if volume_registration.lvid ^= lve.lvid
	     then ec = 3;
	     if volume_registration.public ^= lve.public
	     then ec = 4;
	     if volume_registration.access_class_range (1) ^= lve.min_access_class
	     then ec = 5;
	     if volume_registration.access_class_range (2) ^= lve.max_access_class
	     then ec = 6;
	     if volume_registration.version ^= Volume_Registration_Version_2
	     then call convert_database (ec);		/* to new version */
	     if ec ^= 0
	     then do;
		call announce_syserr_code (ec, "lv.^a disagreed with disk_table. It was rebuilt.", lvname);
		go to remake_lv;
	     end;
	end;
	if dn ^= LV_DIR				/* The ROOT lv must be linked to special */
	then call hcs_$append_link (LV_DIR, en, pathname_ (dn, en), ec);
	uname = "lvid." || unique_chars_ ((volume_registration.lvid));
						/* Force unique name back on */
	call hcs_$chname_seg (vrp, "", uname, ec);
	if ec ^= 0
	then if ec ^= error_table_$segnamedup
	     then call announce_syserr_code (ec, "Cant add name ""^a"" to ""^a"".", uname, en);
	if dn ^= LV_DIR
	then call hcs_$append_link (LV_DIR, uname, pathname_ (dn, uname), ec);
						/* ACS linking code was removed from this point - ejs */
	uname = rtrim (lvname) || ".mdcs";
	if dn ^= LV_DIR
	then call hcs_$append_link (LV_DIR, uname, pathname_ (dn, uname), (0));
	call hcs_$set_damaged_sw (LV_DIR, uname, "0"b, (0));
						/* Don't fault */
	call mdc_repair_$recreate_mdcs (lvname, (volume_registration.lvid), ec);
	if ec ^= 0
	then if ec ^= error_table_$namedup
	     then call announce_syserr_code (ec, "Cannot make mdcs for LV ^a", lvname);

	pvname = dte.pvname;
	en = "pv." || pvname;
	call hcs_$chname_seg (vrp, "", en, ec);		/* Put pv name back on */
	if ec ^= 0
	then if ec ^= error_table_$segnamedup
	     then call announce_syserr_code (ec, "Cant add name ""^a"" to ""^a"".", en, lvname);
	if dn ^= LV_DIR
	then call hcs_$append_link (LV_DIR, en, pathname_ (dn, en), ec);

	do i = 1 to volume_registration.npv while (volume_registration.pvname (i) ^= pvname);
	end;
	if i > volume_registration.npv
	then do;
	     volume_registration.pv (i).pvid = dte.pvid;	/* Vol was not in lv */
	     volume_registration.pv (i).pvname = pvname;
	     volume_registration.pv (i).model = dte.device_type;
	     volume_registration.pv (i).location = "online";
	     volume_registration.pv (i).mfg_serial = "registered by crash recovery";
	     volume_registration.pv (i).password = "0"b;
	     volume_registration.pv (i).date_registered = clock ();
	     volume_registration.npv = volume_registration.npv + 1;
	     call announce_syserr ("Reregistered PV ^a PVID ^w in LV ^a",
		volume_registration.pv (i).pvname, volume_registration.pv (i).pvid, lvname);
	     call hcs_$set_bc_seg (vrp, 36 * currentsize (volume_registration), ec);
	end;
	else do;
	     if volume_registration.pv (i).pvid ^= dte.pvid
		| volume_registration.pv (i).model ^= dte.device_type
	     then call announce_syserr ("Registration for PV ^a was different from disk_table.", pvname);
	     volume_registration.pv (i).pvid = dte.pvid;
	     volume_registration.pv (i).model = dte.device_type;
	end;
	uname = "pvid." || unique_chars_ ((volume_registration.pv (i).pvid));
	call hcs_$chname_seg (vrp, "", uname, ec);	/* Put name back on */
	if ec ^= 0
	then if ec ^= error_table_$segnamedup
	     then call announce_syserr_code (ec, "Cant add name ""^a"" to ""^a"".", uname, lvname);
	if dn ^= LV_DIR				/* Hard case */
	then call hcs_$append_link (LV_DIR, uname, pathname_ (dn, uname), ec);

	call hcs_$terminate_noname (vrp, ec);

CK_VREG_EXIT:
	return;
%page;
/* -------------------------------------------------- */
/* INTERNAL SUPPORT ROUTINES                          */
/* -------------------------------------------------- */


log_it:						/* log changes to databases */
     proc options (variable);

dcl     severity		 fixed bin;
dcl     based_msg		 char (tstr_len) based (addr (temp_str));
dcl     alp		 pointer;

	severity = LOG;
	goto log_it_join;

announce_syserr:
     entry options (variable);

	severity = ANNOUNCE;

log_it_join:
	on any_other ;

	call cu_$arg_list_ptr (alp);
	call ioa_$general_rs (alp, 1, 2, temp_str, tstr_len, "0"b /* no pad */, "0"b /* nnl */);

	if testing
	then call com_err_ (0, MYNAME, "(^[SYSERR^;SYSLOG^]) ^a (^a)", (severity = ANNOUNCE), based_msg,
		get_group_id_ ());
	else call admin_gate_$syserr (severity, "^a: ^a (^a)", MYNAME, based_msg, get_group_id_ ());

	return;

     end log_it;

/* -------------------------------------------------- */

announce_syserr_code:				/* console msg about something awful */
     proc options (variable);

dcl     ecode		 fixed bin (35) based (ecode_p);
dcl     ecode_p		 pointer;
dcl     ecode_len		 fixed bin (21);		/* ignored */
dcl     based_msg		 char (tstr_len) based (addr (temp_str));
dcl     severity		 fixed bin;
dcl     based_packed_ptr	 ptr unal based;
dcl     alp		 pointer;


	severity = ANNOUNCE;

	on any_other ;

	call cu_$arg_list_ptr (alp);
	call ioa_$general_rs (alp, 2, 3, temp_str, tstr_len, "0"b /* no pad */, "0"b /* nnl */);
	call cu_$arg_ptr (1, ecode_p, ecode_len, ec2);

	if testing
	then call com_err_ (ecode, MYNAME, "(^[SYSERR^;SYSLOG^]) ^a (^a)", (severity = ANNOUNCE), based_msg,
		get_group_id_ ());
	else call admin_gate_$syserr_error_code
		(ANNOUNCE, ecode, "^a:^[ Code=^d.^;^s^] ^a (^a)", MYNAME,
		(baseno (addr (ecode) -> based_packed_ptr) = "0"b), ecode, based_msg, get_group_id_ ());

	return;

     end announce_syserr_code;

/* -------------------------------------------------- */

clean_up:						/* tidy the place before we leave */
     proc;

dcl     tptr		 ptr;

	if tsegp ^= null
	then do;
	     tptr = tsegp;
	     tsegp = null;				/* so we don't try to do it again... */
	     call release_temp_segment_ ((MYNAME), tptr, ec2);
	     if ec2 ^= 0
	     then call announce_syserr_code (ec2, "Unable to release temp segment.");
	end;

	if databasep ^= null
	then do;
	     tptr = databasep;
	     databasep = null;
	     call hcs_$terminate_noname (tptr, ec2);
	     if ec2 ^= 0
	     then call announce_syserr_code (ec2, "Unable to terminate database reference.");
	end;

	call vrm_lock_$cleanup ();

	if old_level ^= -1
	then call cu_$level_set (old_level);

	a_ec = ec;				/* tell caller our final status */

     end clean_up;

/* -------------------------------------------------- */

emergency_exit:					/* just in case ... */
     proc;

	if get_ring_ () = get_initial_ring_ ()		/* inner ring process? */
	then do;
	     call continue_to_signal_ (ec);		/* behave like user ring subroutine */
	     if ec ^= 0
	     then goto EXIT;
	end;
	else do;					/* throw fit if inner ring */
	     call announce_syserr ("Unexpected error occurred.  Database may be in an inconsistant state.");
	     ec = error_table_$unexpected_condition;
	     goto EXIT;				/* "clean_up" called from there... */
	end;


     end emergency_exit;

/* -------------------------------------------------- */

get_tseg:						/* get temporary for caller */
     proc;

	if tsegp ^= null ()
	then do;
						/* should only need one temporary per invocation */
	     ec = error_table_$fatal_error;
	     call announce_syserr (ec, "Attempting to get second temp segment.");
	     goto EXIT;
	end;
	else do;
	     call get_temp_segment_ ((MYNAME), tsegp, ec);
	     if ec ^= 0
	     then goto EXIT;
	end;

     end get_tseg;

/* -------------------------------------------------- */

check_lv:						/* procedure to validate data for LV registration */
     proc (p);

dcl     p			 ptr;

	ec = error_table_$bad_arg;			/* guilty 'til proven innocent */

	if p -> volume_registration.version ^= Volume_Registration_Version_2
	then do;
	     ec = error_table_$unimplemented_version;	/* we'll be a little more specific here */
	     goto EXIT;
	end;
	if p -> volume_registration.lvid = "0"b
	then goto EXIT;
	if p -> volume_registration.lvname = ""
	then goto EXIT;
	if verify (rtrim (p -> volume_registration.lvname), LVNAME_LEGAL) ^= 0
	then goto EXIT;
	if ^aim_check_$in_range (p -> volume_registration.access_class_range (2), access_class_range)
	then goto EXIT;
	if ^aim_check_$in_range (p -> volume_registration.access_class_range (1), access_class_range)
	then goto EXIT;
	if ^aim_check_$greater_or_equal (p -> volume_registration.access_class_range (2),
	     p -> volume_registration.access_class_range (1))
	then goto EXIT;
	if p -> volume_registration.volume_owner = ""
	then goto EXIT;
	if p -> volume_registration.acs_path ^= ""
	then do;
	     call expand_pathname_ ((p -> volume_registration.acs_path), dn, en, ec2);
	     if ec2 ^= 0
	     then do;
		ec = ec2;
		goto EXIT;
	     end;
	     j = length (rtrim (en));
	     if substr (en, j - 3, 4) ^= ".acs"
	     then goto EXIT;
	end;

/* we'll leave validation of "npv" up to our caller */

/* passed all tests */
	ec = 0;
	return;

     end check_lv;

/* -------------------------------------------------- */

check_new_lv:					/* validate NEW LV registration data */
     proc (p);

dcl     p			 ptr;

	call check_lv (p);				/* will return if all OK */

	if p -> volume_registration.npv ^= 1
	then do;
	     ec = error_table_$bad_arg;
	     goto EXIT;
	end;

	ec = 0;
	return;

     end check_new_lv;

/* -------------------------------------------------- */

check_pv:						/* validate PV registration data */
     proc (p);

dcl     p			 ptr;			/* points to pv_registration structure */

	ec = error_table_$bad_arg;
	if p -> pv_registration.pvid = "0"b
	then goto EXIT;
	if p -> pv_registration.model > hbound (MODELN, 1) |
	     p -> pv_registration.model < lbound (MODELN, 1)
	then goto EXIT;
	if verify (rtrim (p -> pv_registration.pvname), PVNAME_LEGAL) ^= 0
	then goto EXIT;
	if p -> pv_registration.location = ""
	then p -> pv_registration.location = "uninitialized";
	if p -> pv_registration.mfg_serial = ""
	then p -> pv_registration.mfg_serial = "uninitialized";
	if p -> pv_registration.date_registered > clock () | p -> pv_registration.date_registered < SMALL_DT
	then goto EXIT;
						/* the two bits of "password" may be anything */

	ec = 0;
	return;

     end check_pv;

/* -------------------------------------------------- */

check_new_pv:					/* validate registration data for NEW PV */
     proc (p);

dcl     p			 ptr;

	call check_pv (p);
						/* no special data checks for new PVs at this time */
	return;

     end check_new_pv;

/* -------------------------------------------------- */

name_exists:					/* see if name exists in given directory */
     proc (dname, ename) returns (bit (1));

dcl     dname		 char (*);
dcl     ename		 char (*);

	call hcs_$status_minf (dname, ename, 0, type, bc, ec2);
	if ec2 ^= 0
	then return ("0"b);
	else return ("1"b);

     end name_exists;

/* -------------------------------------------------- */

locate_:						/* find and set pointer to registration database */
     proc (name, lvflag, dont_init_it);

dcl     name		 char (*);
dcl     (lvflag, dont_init_it) bit (1);


	if lvflag
	then do;
	     if name = "root"
	     then
		dn = ROOT;
	     else dn = LV_DIR;
	     en = "lv." || name;
	end;
	else do;
	     if name = "rpv"
	     then
		dn = ROOT;
	     else dn = LV_DIR;
	     en = "pv." || name;
	end;

	call cu_$level_get (old_level);
	call cu_$level_set (get_ring_ ());		/* SET LEVEL FOR OUR CALLER */

	if ^dont_init_it
	then do;
	     call hcs_$initiate (dn, en, "", 0, 0, databasep, ec);
						/* Locate volume registration for p|lv */
	     if ec = error_table_$segknown
	     then do;
						/* program error */
		call announce_syserr_code (ec, "Database - ""^a"".", en);
		ec = 0;
	     end;
	     else if ec ^= 0
	     then do;
		ec = error_table_$unregistered_volume;
		goto EXIT;
	     end;

	     vrp = databasep;			/* this database is "volume_registration" */
	     if volume_registration.version ^= Volume_Registration_Version_2
	     then do;
		call convert_database (ec);
		if ec ^= 0
		then goto EXIT;
	     end;
	end;

     end locate_;

/* -------------------------------------------------- */

locate_uid_:					/* find registration and set pointer based on LVID or PVID */
     proc (uid, lvflag, dont_init_it);

dcl     uid		 bit (36) aligned;
dcl     (lvflag, dont_init_it) bit (1);

	dn = LV_DIR;				/* we don't know whether it's a root volume
						   or not.  However, the LV_DIR contains links
						   for all the unique names ... */

	if lvflag
	then en = "lvid." || unique_chars_ ((uid));
	else en = "pvid." || unique_chars_ ((uid));

	call cu_$level_get (old_level);
	call cu_$level_set (get_ring_ ());		/* SET LEVEL FOR OUR CALLER */

	if ^dont_init_it
	then do;
	     call hcs_$initiate (dn, en, "", 0, 0, databasep, ec);
						/* Locate volume registration for p|lv */
	     if ec = error_table_$segknown
	     then do;
						/* program error */
		call announce_syserr_code (ec, "Database - ""^a"".", en);
		ec = 0;
	     end;
	     else if ec ^= 0
	     then do;
		ec = error_table_$unregistered_volume;
		goto EXIT;
	     end;

	     vrp = databasep;			/* this database is "volume_registration" */
	     if volume_registration.version ^= Volume_Registration_Version_2
	     then do;
		call convert_database (ec);
		if ec ^= 0
		then goto EXIT;
	     end;
	end;

     end locate_uid_;

/* -------------------------------------------------- */

get_lve:						/* routine to update Logical Volume entry in disk_table */
     proc () returns (fixed bin);

/* This entry modifies "disk_table" on behalf of the following entries:

   check_volume_registration
   find
   get_lv_pvinfo
   lvname_info

*/

dcl     (freex, j)		 fixed bin init (0);
dcl     found		 bit (1) init ("0"b);
dcl     lvx		 fixed bin;

	do j = 1 to dt.n_lv_entries while (^found);
	     lvep = addr (dt.lv_array (j));
	     if ^lve.used
	     then if freex = 0
		then freex = j;
		else ;
	     else if lve.lvname = volume_registration.lvname
	     then do;
		lvx = j;
		found = "1"b;
	     end;
	end;
	if ^found
	then do;
	     if freex = 0
	     then freex, dt.n_lv_entries = dt.n_lv_entries + 1;
	     lvx = freex;
	     lvep = addr (dt.lv_array (freex));
	     unspec (lve) = ""b;
	     lve.used = "1"b;
	end;
	else lvep = addr (dt.lv_array (lvx));

/* Always make sure disk table is up to date with our manipulations */

	lve.public = volume_registration.public;
	lve.lvid = volume_registration.lvid;
	lve.lvname = volume_registration.lvname;
	lve.min_access_class = volume_registration.access_class_range (1);
	lve.max_access_class = volume_registration.access_class_range (2);

	return (lvx);

     end get_lve;

/* -------------------------------------------------- */

convert_database:					/* routine to convert from version 1 to version 2 database */
     procedure (a_ec);

dcl     a_ec		 parameter fixed bin (35);
dcl     temp_class		 bit (72) aligned;

	if volume_registration.version ^= 1
	then a_ec = error_table_$improper_data_format;
	else do;
						/* switch access class values */
	     temp_class = volume_registration.access_class_range (1);
	     volume_registration.access_class_range (1) = volume_registration.access_class_range (2);
	     volume_registration.access_class_range (2) = temp_class;
	     volume_registration.version = Volume_Registration_Version_2;
	     call announce_syserr ("Converted "">lv>lv.^a"" to version 2.", volume_registration.lvname);
	     a_ec = 0;
	end;

	return;

     end convert_database;

/* -------------------------------------------------- */


/* ************************************************** */
/* This is the main exit for all entries              */
/* ************************************************** */

EXIT:
	call clean_up;
	return;

/* -------------------------------------------------- */

test:
     entry (a_dn);

/* PUT US IN TEST MODE - i.e. don't use the real databases */

	ROOT, LV_DIR = a_dn;
	testing = "1"b;

	return;

/* ------------------------------ */

test2:
     entry (a_dn, a_dn2);

/* same as above entry, except ROOT and LV_DIR may be specified differently */

	ROOT = a_dn;
	LV_DIR = a_dn2;
	testing = "1"b;

	return;
%page;
%include aim_privileges;
%page;
%include volume_registration;
%page;
%include disk_table;
%page;
%include create_branch_info;
%page;
%include fs_dev_types;
%page;
%include syserr_constants;
%page;
%include access_mode_values;
%page;
%include delete_options;
%page;
/* BEGIN MESSAGE DOCUMENTATION

   Message:
   volume_registration_mgr_$ENTRY: Attempting to get second temp segment. (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   All entrypoints to volume_registration_mgr_ require at most one
   temporary segment.

   A:	$contact


   Message:
   volume_registration_mgr_$ENTRY: Database - {lvid.UNIQUE | lv.LVNAME | pvid.UNIQUE | pv.PVNAME}. (GROUP_ID) Segment already known to process.

   S:	$info

   T:	$run

   M:	$err
   The inner ring module always trerminates references to the database segments
   upon return to its caller.  It found the specified segment already initiated
   upon entry to the specified routine.

   A:	$contact


   Message:
   volume_registration_mgr_$ENTRY: Unable to release temp segment. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   Some unexpected state is preventing the module from releasing its temporary
   segment.

   A:	$contact


   Message:
   volume_registration_mgr_$ENTRY: Unable to terminate database reference. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   Some unexpected state is preventing the module from terminating reference
   to a database segment.

   A:	$contact


   Message:
   volume_registration_mgr_$ENTRY: Unexpected error occurred.  Database may be in an inconsistant state. (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   Some unexpected condition was signalled while the module was executing.

   A:	$contact


   Message:
   volume_registration_mgr_$add_lvr: Added new LV "LVNAME". (GROUP_ID)

   S:	$log

   T:	$run

   M:	A new storage system logical volume has been registered on the system.

   A:	$ignore


   Message:
   volume_registration_mgr_$add_lvr: Added new PV "PVNAME" to LV "LVNAME". (GROUP_ID)

   S:	$log

   T:	$run

   M:	A new storage system physical volume has been registered on the system.

   A:	$ignore


   Message:
   volume_registration_mgr_$add_lvr: Could not append link for {LV name | LVID | PV name | PVID}. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	A link could not be added into >lv whose target is a database segment
   located under the root directory.  Future database references may not be
   possible.

   A:	$contact


   Message:
   volume_registration_mgr_$add_lvr: Unable to back out database "lv.LVNAME". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	When attempting to register a new logical volume, some error occurred.
   The entry then attempted to remove the database segment just created.  This
   removal failed.

   A:	$contact


   Message:
   volume_registration_mgr_$add_pvr: Added new PV "PVNAME" to LV "LVNAME". (GROUP_ID) -log-

   S:	$log

   T:	$run

   M:	A new storage system physical volume has been registered on the system.

   A:	$ignore


   Message:
   volume_registration_mgr_$add_pvr: Could not append link for {PV name| PVID}. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	A link could not be added into >lv whose target is a database segment
   located under the root directory.  Future database references may not be
   possible.

   A:	$contact


   Message:
   volume_registration_mgr_$add_pvr: Extra PV entry "PVNAME" in database "lv.LVNAME". (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   An entry for the specified PV was found in the database segment for
   the specified LV where no segment addname indicated its existance.

   A:	$contact


   Message:
   volume_registration_mgr_$add_pvr: Unable to back out database addname "pv.PVNAME". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	An error occurred while registering a new PV.  The entry attmpts to
   undo all work up to the point of the error.  In this case, it was unable to
   undo its manipulations and left a spurious addname "pv.PVNAME" on the
   database segment.

   A:	$contact


   Message:
   volume_registration_mgr_$change_lvr: Changed ACS pathname for LV "LVNAME" to "PATH". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered ACS pathname of a storage system logical volume has been
   changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_lvr: Changed LV "LVNAME" to {PRIVATE | PUBLIC}. (GROUP_ID)

   S:	$log

   T:	$run

   M:	The access type of the specified logical volume has been changed.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_lvr: Changed LVID of LV "LVNAME" from "LVID1" to "LVID2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered logical volume unique ID has been changed for the
   specified LV.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_lvr: Changed name of LV "LVNAME1" to "LVNAME2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered name of a storage system logical volume has been
   changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_lvr: Changed owner of LV "LVNAME" from "Person1.Project1" to "Person2.Project2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The owner of a storage system logical volume has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_lvr: Changed {max | min} access class of LV "LVNAME" from "AUTH_STR1" to "AUTH_STR2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The access class of a storage system logical volume has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_lvr: Couldn't {delete | create} link for {old | new} {LV name | LVID}. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	A link for the specified database segment residing under the root
   directory could not be created in >lv.

   A:	$contact


   Message:
   volume_registration_mgr_$change_lvr: Unable to back out LVID change to "LVNAME.mdcs". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   While changing attributes of the logical volume LVNAME, an error was
   encountered.  The entry then attempted to remove some changes already
   made to the registration database.  In this case, it could not remove
   the LVID change to the master directory control database LVNAME.mdcs.

   A:	$contact


   Message:
   volume_registration_mgr_$change_lvr: Unable to change name "lv.LVNAME1" back to "lv.LVNAME2" (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   While changing attributes ot the logical volume LVNAME, an error was
   encountered.  The entry then attempted to remove some changes already
   made to the registration database.  In this case, it could not remove
   theLV name change.

   A:	$contact


   Message:
   volume_registration_mgr_$change_lvr: Unable to change name "lvid.UNIQUE1" back to "lvid.UNIQUE2". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   While changing attributes ot the logical volume LVNAME, an error was
   encountered.  The entry then attempted to remove some changes already
   made to the registration database.  In this case, it could not remove
   LVID name change.

   A:	$contact


   Message:
   volume_registration_mgr_$change_lvr: Unable to convert {max | min} auth to name_string. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   A binary authorization string could not be converted to its readable
   representation.

   A:	$contact


   Message:
   volume_registration_mgr_$change_lvr: Unexpected error changing name on "lv.LVNAME". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   An unexpected error occured while changing the name on an LV registration
   data segment.  The message explains the trouble.

   A:	$contact


   Message:
   volume_registration_mgr_$change_lvr: Unexpected trouble changing name "lvid.UNIQUE". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   An unexpected error occured while changing the name on an LV registration
   data segment.  The message explains the trouble.

   A:	$contact


   Message:
   volume_registration_mgr_$change_pvr: Changed PVID of PV "PVNAME" from "PVID1" to "PVID2" (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered physical volume unique IFD has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_pvr: Changed date_registered of PV "PVNAME" from "DT1" to "DT2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered date for the specified PV has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_pvr: Changed location of PV "PVNAME" from "LOC1" to "LOC2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered location of the specified PV has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_pvr: Changed mfg_serial of PV "PVNAME" from "STR1" to "STR2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered serial of the specified PV has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_pvr: Changed model of PV "PVNAME" from "TYPE1" to "TYPE2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered model number of the specified PV has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_pvr: Changed name of PV "PVNAME1" to "PVNAME2". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered name of the specified PV has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_pvr: Changed password of PV "PVNAME". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registered password of the specified PV has been changed as indicated.

   A:	$ignore


   Message:
   volume_registration_mgr_$change_pvr: Couldn't {delete | create} link for {old | new} {PV name | PVID}. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	A link for a database segment residing in the root directory could
   not be created in >lv.  This may make some registration data inaccessible.

   A:	$contact


   Message:
   volume_registration_mgr_$change_pvr: PV entry missing from database "pv.PVNAME". (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   An entry for the specified PV could not be found in the data segment although
   an addname for that PV exists on the segment entry.

   A:	$contact


   Message:
   volume_registration_mgr_$change_pvr: Unable to back out PVID change. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	While making changes to the registered PV attributes an error was encountered.
   The module attempts to remove any work already done on other attributes.
   For reasons given in ERROR_MESSAGE the PVID change could not be removed.

   A:	$contact


   Message:
   volume_registration_mgr_$change_pvr: Unexpected trouble changing name "pv.PVNAME". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	While making changes to the registered PV attributes an error was encountered.
   The module attempts to remove any work already done on other attributes.
   For reasons given in ERROR_MESSAGE the PV name change could not be removed.

   A:	$contact


   Message:
   volume_registration_mgr_$change_pvr: Unexpected trouble changing name "pvid.UNIQUE". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	An unexpected state made the program unable to change the database
   name associated with the PV unique ID.

   A:	$contact


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE. cannot re-create >lv

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   If the registration does not exist, the registration files are reconstructed.
   These registration files reside in the directory >lv,
   which is also remade if it was lost or did not exist.
   .sp
   This message indicates that there is a problem in re-creating >lv.
   Other error messages will follow and reregistration will fail.

   A:	$inform


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE. Putting LV dir on RPV

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   While re-creating >lv, the system attempted to
   set the directory flag which means "RPV only"
   and failed to do so.

   A:	$inform


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE.  Cannot add SysAdmin access to >lv.

   S:	$info

   T:	$init

   M:	After creating a new >lv directory, an attempt is made to add
   "sma" access for *.SysAdmin.*.  This failed.

   A:	$inform


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE. Cannot reregister pv PVNAME

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   .sp
   The system was unable to re-register the physical volume PVNAME
   because its logical volume entry could not be located in the old disk_table
   and its physical volume registration file could not be found.
   The volume will have to be re-registered manually.

   A:	If PVNAME is garbage, ignore this message.
   Otherwise, use add_vol_registration to re-register the volume.


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE. Cannot reregister lv LVNAME

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   .sp
   The system was unable to append a branch for
   >lv>lv.LVNAME.
   The logical volume will have to be re-registered manually.

   A:	Use the add_vol_registration command
   to re-register the volume manually.


   Message:
   volume_registration_mgr_$check_volume_registration:  ERROR_MESSAGE.  PATH

   S:	$info

   T:	$init

   M:	An error occurred when trying to reclassify a new lv.** seg
   as a multi-class system segment.

   A:	$inform


   Message:
   volume_registration_mgr_$check_volume_registration: Reregistered TYPE lv LVNAME lvid WWWW

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   The registration file for LVNAME
   did not exist and was reconstructed.
   .sp
   This message is always produced for the root logical volume during a cold boot of the Multics hierarchy.

   A:	Use the list_vol_registration command
   to list the registration and check it against the
   re-created copy.
   Correct it with change_vol_registration if necessary.


   Message:
   volume_registration_mgr_$check_volume_registration: Code N. lv.LVNAME disagreed with disk_table. It was rebuilt.

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   The registration file lv.LVNAME was found
   but disagreed with the disk_table_, and so it was corrected.
   The meaning of the codes can be determined from the
   listing of volume_registration_mgr_; it is rarely important.

   A:	Use list_vol_registration to examine the remade
   registration, and correct it with change_vol_registration if necessary.


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE. Cant add name lvid.UNIQUE to lv.LVNAME

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   The registration file for LVNAME did not have the additional name
   constructed from the volume unique ID,
   and an error was discovered trying to add it.
   If this name is on another segment, confusion will result.

   A:	$inform
   Enter admin mode and
   do a "list >lv>**"
   command.


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE. Cannot make mdcs for LVNAME

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   An error occurred while reconstructing
   LVNAME.mdcs.
   Operation on master directories for LVNAME may encounter problems.

   A:	$inform_sa


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE. Cant add name pv.PVNAME to LVNAME

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   .sp
   An error occurred adding the name pv.PVNAME to >lv>lv.LVNAME.
   If this name is missing or on another segment, confusion will result.

   A:	$inform
   Enter admin mode and do a
   "list >lv>**"
   command.


   Message:
   volume_registration_mgr_$check_volume_registration: Reregistered pv PVNAME pvid WWWW in lv LVNAME

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   Registration for PVNAME was added to
   the registration file for LVNAME.
   .sp
   This message is always produced for the root physical volume, rpv, during a cold boot of the Multics hierarchy.

   A:	Use the list_vol_registration command
   to check the registration.
   If necessary, correct it with change_vol_registration.


   Message:
   volume_registration_mgr_$check_volume_registration: Registration for PVNAME was different from disk_table

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   The physical volume ID or model number of PVNAME
   was wrong in the registration files and was corrected.

   A:	$inform_sa


   Message:
   volume_registration_mgr_$check_volume_registration: ERROR_MESSAGE. Cant add name pvid.UNIQUE to LVNAME

   S:	$info

   T:	$init

   M:	When the system is bootloaded, it checks
   the list of packs which were mounted during the last bootload
   to make sure that each one has a valid logical and physical volume registration.
   An error occurred adding the name pvid.UNIQUE to >lv>lv.LVNAME.
   If this name is missing or is on another segment, confusion will result.

   A:	$inform
   Enter admin mode and do a
   "list >lv>**"
   command.


   Message:
   volume_registration_mgr_$delete_lvr: Could not remove {PV name | PVID | LV name | LVID} database link. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	An unexpected state prevented the program from removing the database
   links associated with an LV just deleted.  The links (in >lv) should be
   removed by hand using Ring_1_Repair.

   A:	$contact


   Message:
   volume_registration_mgr_$delete_lvr: Deleted LV "LVNAME". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registration for the spcified logical volume has been removed.

   A:	$ignore


   Message:
   volume_registration_mgr_$delete_lvr: Deleted PV "PVNAME" from LV "LVNAME". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registration for the specified physical volume was removed while
   removing the entire logical volume registration.

   A:	$ignore


   Message:
   volume_registration_mgr_$delete_lvr: Unable to delete MDCS "LVNAME.mdcs". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	After deleting the registration for a logical volume the module was
   unable to delete the corresponding master directory control database indicated
   in the message.

   A:	$contact


   Message:
   volume_registration_mgr_$delete_pvr: Could not remove {PV name | PVID} database link. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	An unexpected state prevented the removal of database links associated
   with a PV registration that was just deleted.  The links should be deleted
   by hand using Ring_1_Repair.

   A:	$contact


   Message:
   volume_registration_mgr_$delete_pvr: Deleted PV "PVNAME" from LV "LVNAME". (GROUP_ID)

   S:	$log

   T:	$run

   M:	The registration fo the specified PV has been deleted.

   A:	$ignore


   Message:
   volume_registration_mgr_$delete_pvr: PV entry missing from database "pv.PVNAME". (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   The entry for the specified PV could not be located in the database
   segment although its name appears on the segment entry.

   A:	$contact


   Message:
   volume_registration_mgr_$delete_pvr: Trimming registration. (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	An unexpected state prevented the trimming of a registration database
   segment.  This will not hinder subsystem performance.

   A:	$contact


   Message:
   volume_registration_mgr_$delete_pvr: Unable to replace database name "pv.PVNAME". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   While deleting the registration for the specified PV an error occurred.
   The program attempts to undo any changes to "leave things as they were."
   The attempt to put back the database segment name (as indicated) failed.

   A:	$contact


   Message:
   volume_registration_mgr_$find: PV entry missing from "pv.PVNAME". (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   The entry for the specified PV could not be found in the registration database
   segment although the name appears on the segment's entry.

   A:	$contact


   Message:
   volume_registration_mgr_$find_volname: PV entry not found in database "pvid.UNIQUE". (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   The entry for the specified PV could not be found in the registration database
   segment although the name appears on the segment's entry.

   A:	$contact


   Message:
   volume_registration_mgr_$get_access: ACS not segment - "PATH". (GROUP_ID)

   S:	$info

   T:	$run

   M:	The ACS indicated in the logical volume registration database is
   not a segment.  The database entry should be corrected using "change_volume_registration".

   A:	$contact_sa


   Message:
   volume_registration_mgr_$get_access: Bad ACS path in database "lv.LVNAME". (GROUP_ID) ERROR_MESSAGE

   S:	$info

   T:	$run

   M:	$err
   The ACS pathname entry in the LVNAME database is illegal.  This should not
   happen normally because the ring-1 primitives check all paths before
   entering them into the database.

   A:	$contact


   Message:
   volume_registration_mgr_$pvname_info: PV entry not found in database "pv.PVNAME". (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   The entry for the specified PV could not be found in the registration database
   segment although the name appears on the segment's entry.

   A:	$contact


   Message:
   volume_registration_mgr_$read_pvr: PV entry missing from database "pv.PVNAME". (GROUP_ID)

   S:	$info

   T:	$run

   M:	$err
   The entry for the specified PV could not be found in the registration database
   segment although the name appears on the segment's entry.

   A:	$contact


   END MESSAGE DOCUMENTATION */

     end volume_registration_mgr_;
   



		    vrm_lock_.pl1                   11/11/89  1102.4rew 11/11/89  0803.3       72729



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1984 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */




/****^  HISTORY COMMENTS:
  1) change(86-06-05,GJohnson), approve(86-06-05,MCR7387),
     audit(86-06-10,Martinson), install(86-07-11,MR12.0-1091):
     Correct error message documentation.
                                                   END HISTORY COMMENTS */


/* VRM_LOCK_: Entries which handle logical volume registration lock */

/* Created 07/31/84 by EJ Sharpe - actually pirated mdc_lock_ */
/* Modified 84-11-01 by EJ Sharpe for some minor fixes */

vrm_lock_: proc;

/* Parameters */

dcl  arg_code fixed bin (35);

/* Automatic */

dcl  code fixed bin (35);
dcl  vl fixed bin;					/* Validation level */
dcl  vrm_data_rings (3) fixed bin (3);

/* Static */

dcl  saved_data_ptr ptr int static init (null);
dcl  test_mode bit (1) int static init ("0"b);		/* For debugging */

/* Constants */

dcl  vrm_data_dir char (32) int static options (constant) init (">system_library_1");
dcl  vrm_data_ename char (32) int static options (constant) init ("vrm_data");


%include vrm_data;


/* External stuff */

dcl  admin_gate_$syserr_error_code entry () options (variable);
dcl  clock_ entry returns (fixed bin (52));
dcl  cu_$level_get entry (fixed bin);
dcl  cu_$level_set entry (fixed bin);
dcl  get_group_id_ entry returns (char (32));
dcl  get_lock_id_ entry returns (bit (36));
dcl  get_ring_ entry returns (fixed bin);
dcl  hcs_$initiate entry (char (*), char (*), char (*), fixed bin (1), fixed bin (2), ptr, fixed bin (35));
dcl  hcs_$truncate_file entry (char (*), char (*), fixed bin (19), fixed bin (35));
dcl  hcs_$append_branchx entry (char (*), char (*), fixed bin (5), dim (3) fixed bin (3), char (*), fixed bin (1),
     fixed bin (1), fixed bin (24), fixed bin (35));
dcl  admin_gate_$reclassify_sys_seg entry (char (*), char (*), bit (72) aligned, fixed bin (35));
dcl  set_lock_$lock entry (bit (36) aligned, fixed bin, fixed bin (35));
dcl  set_lock_$unlock entry (bit (36) aligned, fixed bin (35));

dcl  error_table_$invalid_lock_reset ext fixed bin (35);
dcl  error_table_$namedup ext fixed bin (35);

dcl  sys_info$access_class_ceiling bit (72) aligned external;

dcl (null) builtin;

/* Entries to handle the lock */

lock:	entry (arg_code);

	if test_mode then do;
	     arg_code = 0;
	     return;
	end;
	call find_vrm_data;				/* Get pointer */
	call set_lock_$lock (vrm_data.lock, 30, code);
	if code = 0 then go to locked;		/* Locked ok */
	if code = error_table_$invalid_lock_reset then do; /* Recoverable error */
	     call admin_gate_$syserr_error_code (4, code, "vrm_lock_$lock:");
	     code = 0;
	     go to locked;
	end;

	arg_code = code;

	return;					/* Lock failed */

locked:	vrm_data.lock_cnt = vrm_data.lock_cnt + 1;	/* Do some meters */
	vrm_data.time_locked = clock_ ();
	vrm_data.locker_name = get_group_id_ ();
	arg_code = code;
	return;


unlock:	entry;

	if test_mode then return;
	call find_vrm_data;
unlock_common:
	vrm_data.unlock_cnt = vrm_data.unlock_cnt + 1;
	vrm_data.time_spent_locked = vrm_data.time_spent_locked + clock_ () - vrm_data.time_locked;
	call set_lock_$unlock (vrm_data.lock, code);
	if code ^= 0 then call admin_gate_$syserr_error_code (4, code, "vrm_lock_$unlock:");
	return;


/* Entry called by cleanup handlers on crawl-out. It is not certain that the lock is locked */

cleanup:	entry;

	if test_mode then return;
	if saved_data_ptr = null then return;		/* Never called lock */
	vrm_datap = saved_data_ptr;

	if vrm_data.lock ^= get_lock_id_ () then return;	/* Not locked by me */
	go to unlock_common;			/* Join std path */

/* Entry to set test mode */

set_test_mode: entry;

	test_mode = "1"b;
	return;

/* Internal procedure to get a pointer to the vrm_data segment */

find_vrm_data: proc;

dcl  vrm_data_err condition;

	     if saved_data_ptr ^= null then do;		/* Pointer already knwon */
		vrm_datap = saved_data_ptr;
		return;
	     end;

	     call cu_$level_get (vl);
	     call cu_$level_set (get_ring_ ());
	     call hcs_$initiate (vrm_data_dir, vrm_data_ename, "", 0, 0, saved_data_ptr, code);
	     call cu_$level_set (vl);

	     if saved_data_ptr ^= null then do;		/* It worked */
		vrm_datap = saved_data_ptr;
		return;
	     end;

	     call admin_gate_$syserr_error_code (4, code,
		"vrm_lock_: Unable to initiate ^a>^a.", vrm_data_dir, vrm_data_ename);

	     signal vrm_data_err;			/* I don't know what else to do now */

	     return;

	end find_vrm_data;

/* Initialization entry to create the vrm_data segment which contains the lock */

vrm_data_init: entry;

	if test_mode then return;

	vrm_data_rings = get_ring_ ();
	call hcs_$append_branchx (vrm_data_dir, vrm_data_ename, 01010b, vrm_data_rings, "*.*.*", 0, 0, 0, code);
	if (code ^= 0) & (code ^= error_table_$namedup) then do;
	     call admin_gate_$syserr_error_code (0, code, "vrm_lock_$vrm_data_init: Unable to create ^a>^a.",
		vrm_data_dir, vrm_data_ename);
	     return;
	end;

	if code = error_table_$namedup then do;
	     call hcs_$truncate_file (vrm_data_dir, vrm_data_ename, 0, code);
	     if code ^= 0 then do;
		call admin_gate_$syserr_error_code (0, code, "vrm_lock_$vrm_data_init: Unable to truncate ^a>^a.",
		     vrm_data_dir, vrm_data_ename);
		return;
	     end;
	end;

	call admin_gate_$reclassify_sys_seg (vrm_data_dir, vrm_data_ename, sys_info$access_class_ceiling, code);
	if code ^= 0 then
	     call admin_gate_$syserr_error_code (0, code, "vrm_lock_$vrm_data_init: Unable to reclassify ^a>^a.",
	     vrm_data_dir, vrm_data_ename);
	return;

/* BEGIN MESSAGE DOCUMENTATION

   Message:
   vrm_lock_$lock: LOCK ERROR MESSAGE.

   S: $log

   T: $run

   M: A logical volume registration operation failed because of a problem
   with the vrm control lock.

   A: $ignore

   Message:
   vrm_lock_$unlock: LOCK ERROR MESSAGE.

   S: $log

   T: $run

   M: At the completion ofa logical volume registratin operation, some
   error occured unlocking the vrm control lock.

   A: $ignore

   Message:
   vrm_lock_: Unable to initiate PATHNAME. REASON.

   S: $log

   T: $run

   M: Logical volume registration control (vrm) was unable to initiate
   PATHNAME, which contains the vrm control lock, because of REASON.
   The logical volume registration control operation requested was
   not performed.

   A: $ignore

   Message:
   vrm_lock_$vrm_data_init: Unable to create PATHNAME. REASON.

   S: $info

   T: $init

   M: Logical volume registration control (vrm) was unable to create
   the segment PATHNAME, to be used for the vrm control lock, for the
   REASON given.  Subsequent logical volume registration control 
   operations may fail.

   A: $ignore

   Message:
   vrm_lock_$vrm_data_init: Unable to truncate PATHNAME. REASON.

   S: $info

   T: $init

   M: Logical volume registration control was unable to truncate
   the segment PATHNAME for the REASON given. This segment is to
   be used for the logical volume registration control lock.
   Subsequent logical volume registration control operations may fail.

   A: $ignore

   Message:
   vrm_lock_$vrm_data_init: Unable to reclassify PATHNAME. REASON.

   S: $info

   T: $init

   M: Logical volume registration control was unable to reclassify
   the segment PATHNAME to its proper access class for the REASON given.
   This segment is to be used for the logical volume registration control
   lock.  Subsequent logical volume registration control operations may fail.

   END MESSAGE DOCUMENTATION */


end vrm_lock_;
   



		    wdx.pl1                         11/11/89  1102.4r w 11/11/89  0803.3       96003



/****^  ***********************************************************
        *                                                         *
        * Copyright, (C) Honeywell Bull Inc., 1987                *
        *                                                         *
        * Copyright, (C) Honeywell Information Systems Inc., 1982 *
        *                                                         *
        * Copyright (c) 1972 by Massachusetts Institute of        *
        * Technology and Honeywell Information Systems, Inc.      *
        *                                                         *
        *********************************************************** */


wdx$init: proc (a_evchn, a_ec);
	

/* wdx - ring 1 lv_attach_table manager 

   Runs in Initializer process, communicates lv_request_'s desires to table and user process. 
   Entries are accessible through initializer_mdc_


   Entry Points:

   check_mount		issue syserr message for overdue mount 
   free_lvate		clear one out 
   init			set up for bootload 
   invalidate_lvate		cause process to give up lv 
   mhvmessage		issue syserr message for Operator  
   poll_mounts		check for overdue mounts 
   respond_mount_lv		set return state, code, and bit 
   retrieve_lvate		dredge entry out of lvat 
   scan_lv		find all lv occurences 
   scan_process		find all process occurrences 
   set_lvinfo		put lvid and stuff in lvate 

   Bernard Greenberg 09/08/76   
   Modified December 1981 by J. Bongiovanni for poll_mounts, check_mount 
   Modified April 82 BIM for legal acls.
*/

/*  Parameter  */

dcl  a_array (*) fixed bin;
dcl  a_code fixed bin (35);
dcl  a_ec fixed bin (35);
dcl  a_evchn fixed bin (71);
dcl  a_lvatep ptr;
dcl  a_lvax fixed bin (17);
dcl  a_lvid bit (36) aligned;
dcl  a_lvname char (*);
dcl  a_lvx fixed bin;
dcl  a_n fixed bin;
dcl  a_next_time fixed bin (71);
dcl  a_pid bit (36) aligned;
dcl  a_state fixed bin;
dcl  a_unit_string char (*) varying;
dcl  a_username char (*);

/*  Automatic  */

dcl  1 seg_acl (1) aligned like segment_acl_entry;

dcl  ec fixed bin (35);
dcl  fixedipcmessage fixed bin (71);
dcl  lvax fixed bin;
dcl  lvname char (32);
dcl  n fixed bin;
dcl  next_time fixed bin (71);
dcl  pid bit (36) aligned;
dcl  save_lev fixed bin;
dcl  username char (32);

/*  Static  */

dcl  END_OF_TIME fixed bin (71) int static options (constant) init (1111111111111111111111111111111111111111111111111111b);
dcl  LVAT_NAME char (32) static init ("lv_attach_table") options (constant);
dcl  s_lvatp ptr static init (null);
dcl  SYSDIR char (168) static init (">lv");
dcl  TIMEOUT fixed bin (71) int static options (constant) init (240000000);	/* 4 minutes */
dcl  WDX_RING fixed bin init (1) static;
/*  Based  */

dcl  ipcmessage char (8) based (addr (fixedipcmessage));

/*  Entry  */

dcl  admin_gate_$reclassify_sys_seg entry (char (*), char (*), bit (72) aligned, fixed bin (35));
dcl  admin_gate_$syserr entry options (variable);
dcl  admin_gate_$syserr_error_code entry options (variable);
dcl  cu_$level_get entry (fixed bin);
dcl  cu_$level_set entry (fixed bin);
dcl  get_max_authorization_ returns (bit (72) aligned);
dcl  get_process_id_ entry returns (bit (36) aligned);
dcl  get_ring_ entry returns (fixed bin);
dcl  hcs_$add_acl_entries entry (char (*), char (*), ptr, fixed bin, fixed bin (35));
dcl  hcs_$make_seg entry (char (*), char (*), char (*), fixed bin (5), ptr, fixed bin (35));
dcl  hcs_$truncate_seg entry (ptr, fixed bin, fixed bin (35));
dcl  hcs_$wakeup entry (bit (36) aligned, fixed bin (71), fixed bin (71), fixed bin (35));

/*  Builtin  */

dcl  clock builtin;
dcl  min builtin;
dcl  null builtin;
dcl  stacq builtin;

/*  Condition  */

dcl  cleanup condition;
/*  */

	a_ec = 0;

	call cu_$level_get (save_lev);
	on cleanup call cu_$level_set (save_lev);
	call cu_$level_set (WDX_RING);
	call hcs_$make_seg (SYSDIR, LVAT_NAME, "", 1011b, s_lvatp, ec);
	if s_lvatp = null then do;
init_lose:     call cu_$level_set (save_lev);
	     a_ec = ec;
	     return;
	end;

	call hcs_$truncate_seg (s_lvatp, 0, ec);
	if ec ^= 0 then go to init_lose;

	call admin_gate_$reclassify_sys_seg (SYSDIR, LVAT_NAME, get_max_authorization_ (), ec);
	if ec ^= 0 then go to init_lose;

	seg_acl (1).access_name = "*.*.*";
	seg_acl (1).mode = RW_ACCESS;
	seg_acl (1).extended_mode = ""b;
	seg_acl (1).status_code = 0;

	call hcs_$add_acl_entries (SYSDIR, LVAT_NAME, addr (seg_acl), 1, ec);
	if ec = 0 then if seg_acl (1).status_code ^= 0 then ec = seg_acl (1).status_code;
	if ec ^= 0 then go to init_lose;

	call cu_$level_set (save_lev);
	lvatp = s_lvatp;

	lvat.master_pid = get_process_id_ ();
	lvat.master_evchn = a_evchn;

	lvat.max_n_entries = 10000;
	lvat.highest_used = 0;
	lvat.initialized = "1"b;
	return;

/* --------------------------------------------------------- */

retrieve_lvate: entry (a_lvax, a_lvatep, a_ec);

	lvax = a_lvax;

	lvatp = s_lvatp;
	if lvax > lvat.highest_used | lvax <= 0 then do;
	     a_ec = 5;
	     return;
	end;

	lvatep = addr (lvat.array (lvax));
	a_lvatep -> lvate = lvate;
	a_ec = 0;
	return;

/* --------------------------------------------------------- */

free_lvate: entry (a_lvax);

	lvatp = s_lvatp;
	lvatep = addr (lvat.array (a_lvax));

	if stacq (lvate.pid, "0"b, (lvate.pid)) then;
	return;

/* --------------------------------------------------------- */

respond_mount_lv: entry (a_lvax, a_state, a_code, a_ec);


	lvatp = s_lvatp;
	lvatep = addr (lvat.array (a_lvax));

	lvate.code = a_code;
	lvate.state = a_state;
	if lvate.state = 1 then do;
	     lvate.waiting = "0"b;
	     lvate.mounted = "1"b;
	end;
	else if lvate.state = 4 then do;
	     lvate.waiting = "1"b;
	     lvate.mount_request_timeout = clock () + TIMEOUT;
	     ipcmessage = "poll    ";
	     call hcs_$wakeup (lvat.master_pid, lvat.master_evchn, fixedipcmessage, ec);
	     if ec ^= 0
		then call admin_gate_$syserr_error_code (0, ec, "wdx: Unable to send wakeup on master channel");
	end;
	else lvate.waiting = "0"b;
	if lvate.state ^= 4 then lvate.pending_mount = "0"b;
	lvate.mount_req_answered = "1"b;

	ipcmessage = "lv_mount";

	call hcs_$wakeup (lvate.pid, lvate.evchn, fixedipcmessage, ec);

	a_ec = ec;
	return;

/* --------------------------------------------------------- */

scan_process: entry (a_pid, a_array, a_n);

	pid = a_pid;
	lvatp = s_lvatp;

	n = 0;

	do lvax = 1 to lvat.highest_used;
	     if lvat.array (lvax).pid = pid then do;
		n = n + 1;
		a_array (n) = lvax;
	     end;
	end;
	a_n = n;

	return;

/* --------------------------------------------------------- */

scan_lv:	entry (a_lvname, a_array, a_n);

	lvname = a_lvname;
	lvatp = s_lvatp;

	n = 0;

	do lvax = 1 to lvat.highest_used;
	     lvatep = addr (lvat.array (lvax));
	     if lvate.pid ^= "0"b then if lvate.pending_mount | lvate.mount_req_answered
		then if lvate.lvname = lvname & ^lvate.invalidated then do;
			n = n + 1;
			a_array (n) = lvax;
		     end;
	end;
	a_n = n;
	return;

/* --------------------------------------------------------- */

invalidate_lvate: entry (a_lvax);

	lvatp = s_lvatp;
	lvatep = addr (lvat.array (a_lvax));

	lvate.invalidated = "1"b;
	return;

/* --------------------------------------------------------- */

set_lvinfo: entry (a_lvax, a_lvid, a_lvx);

	lvatp = s_lvatp;
	lvatep = addr (lvat.array (a_lvax));

	lvate.lvid = a_lvid;
	lvate.lvx = a_lvx;
	return;

/* --------------------------------------------------------- */

mhvmessage: entry (a_lvax, a_username);

	lvatp = s_lvatp;
	lvatep = addr (lvat.array (a_lvax));

	username = a_username;
	call admin_gate_$syserr (3, "RCP: Mount logical volume ^a for ^a", lvate.lvname, username);
	return;

/* --------------------------------------------------------- */

poll_mounts:
	entry (a_array, a_n, a_next_time);
	
	lvatp = s_lvatp;
	next_time = END_OF_TIME;
	n = 0;
	do lvax = 1 to lvat.highest_used;
	     lvatep = addr (lvat.array (lvax));
	     if (lvate.pid ^= "0"b) & lvate.waiting & lvate.mount_req_answered & ^lvate.invalidated
		then do;
		if lvate.mount_request_timeout < clock () then do;
		     n = n + 1;
		     a_array (n) = lvax;
		     lvate.mount_request_timeout = clock () + TIMEOUT;
		end;
		next_time = min (next_time, lvate.mount_request_timeout);
	     end;
	end;
	a_n = n;
	if next_time = END_OF_TIME then a_next_time = -1;
	else a_next_time = next_time;
	return;
	

/* --------------------------------------------------------- */

check_mount:
	entry (a_lvax, a_username, a_unit_string);
	
	lvatp = s_lvatp;
	lvatep = addr (lvat.array (a_lvax));
	if lvate.pid ^= "0"b & lvate.waiting & lvate.mount_req_answered & ^lvate.invalidated
	     then call admin_gate_$syserr (3, "RCP: Check mount of logical volume ^a for ^a^/^15x^a",
	     lvate.lvname, (a_username), (a_unit_string));
	return;
	

/* --------------------------------------------------------- */
test:	entry (testdir);

dcl  testdir char (*);
	SYSDIR = testdir;
	WDX_RING = get_ring_ ();
	return;
						/*  */
%include acl_structures;
%include access_mode_values;
%include lv_atttbl;


/*  BEGIN MESSAGE DOCUMENTATION

   Message:
   RCP: Mount logical volume LVNAME for PROCESSNAME

   S: $beep

   T: $run

   M: A user (PROCESSNAME) has requested the attachment
   of logical volume LVNAME. It is not now mounted.  The
   initializer will issue mount messages for all physical volumes needed.

   A: Mount the required physical volumes, using the add_vol command
   to indicate when each has been made ready.  The user process will
   continue when the last volume has been mounted.  If the volume
   cannot be mounted, use the del_lv command to indicate this fact,
   and the user process will receive an error indication.


   Message:
   RCP: Check mount of logical volume LVNAME for PROCESSNAME
        PVNAME1(DRIVE1) ...

   S: $beep

   T: $run

   M: A user (PROCESSNAME) previously requested the attachment of logical volume
   LVNAME.  The mount has not been accomplished in a 4 minutes.
   All required physical volumes are specified (PVNAMEi, DRIVEi).

   A: Mount the required physical volumes, using the add_vol command
   to indicate when each has been made ready.  The user process will
   continue when the last volume has been mounted.  If the volume
   cannot be mounted, use the del_lv command to indicate this fact,
   and the user process will receive an error indication.

  
   Message:
   wdx: ERRORCODE Unable to send wakeup on master channel.

   S: $info

   T: $run

   M: The ring-1 logical volume mount software was unable to send a wakeup
   to the ring-4 software for the reason indicated.  Some mount requests
   may have been lost.

   A: $contact_sa

   END MESSAGE DOCUMENTATION */

     end;




		    bull_copyright_notice.txt       08/30/05  1008.4r   08/30/05  1007.3    00020025

                                          -----------------------------------------------------------


Historical Background

This edition of the Multics software materials and documentation is provided and donated
to Massachusetts Institute of Technology by Group Bull including Bull HN Information Systems Inc. 
as a contribution to computer science knowledge.  
This donation is made also to give evidence of the common contributions of Massachusetts Institute of Technology,
Bell Laboratories, General Electric, Honeywell Information Systems Inc., Honeywell Bull Inc., Groupe Bull
and Bull HN Information Systems Inc. to the development of this operating system. 
Multics development was initiated by Massachusetts Institute of Technology Project MAC (1963-1970),
renamed the MIT Laboratory for Computer Science and Artificial Intelligence in the mid 1970s, under the leadership
of Professor Fernando Jose Corbato.Users consider that Multics provided the best software architecture for 
managing computer hardware properly and for executing programs. Many subsequent operating systems
incorporated Multics principles.
Multics was distributed in 1975 to 2000 by Group Bull in Europe , and in the U.S. by Bull HN Information Systems Inc., 
as successor in interest by change in name only to Honeywell Bull Inc. and Honeywell Information Systems Inc. .

                                          -----------------------------------------------------------

Permission to use, copy, modify, and distribute these programs and their documentation for any purpose and without
fee is hereby granted,provided that the below copyright notice and historical background appear in all copies
and that both the copyright notice and historical background and this permission notice appear in supporting
documentation, and that the names of MIT, HIS, Bull or Bull HN not be used in advertising or publicity pertaining
to distribution of the programs without specific prior written permission.
    Copyright 1972 by Massachusetts Institute of Technology and Honeywell Information Systems Inc.
    Copyright 2006 by Bull HN Information Systems Inc.
    Copyright 2006 by Bull SAS
    All Rights Reserved

