SYSDUMPs AND HP's "TurboSTORE" FACILITY
by Eugene Volokh, VESOFT
Published by INTERACT Magazine, Sep 1988.

At a recent meeting of the GLUG (Greater Los Angeles Users' Group), one of our users approached me with a technical problem. He had just bought HP's new TurboSTORE product, which (among other things) lets you mount several tapes on several tape drives and then specify all those tape drives on one :STORE command. This way, if your nightly backup takes 3 reels and you happen to have 3 tape drives, you could mount tapes on each of the tape drives and say:

:FILE T7;DEV=7
:FILE T8;DEV=8
:FILE T9;DEV=9
:STORE @.@.@;DATE>=xxx;STORESET=(*T7),(*T8),(*T9)

Instead of just specifying the tape file to store to (after the fileset), you can specify the ;STORESET= keyword; :STORE will write to the tape on ldev 7, then to the one on ldev 8, and then to the one on ldev 9. You mount the tapes, do the :STORE, and then go home -- no need to stay around to mount additional tapes. (This works especially well if you have HP's new 7980XC compressing tape drives, which could let you cut your system backup to 2 or 3 tapes rather than 7 or 8 -- the user who prompted this whole paper is getting a 4 to 1 compression ratio.) It's much more convenient, and can save you $$$ on your nightly operator costs.

The problem with TurboSTORE is that, although it works perfectly on the :STORE command, it can't be used with :SYSDUMP -- the one place you most want to have it (for backup purposes). If you try saying

   :FILE T7;DEV=7
   :FILE T8;DEV=8
   :FILE T9;DEV=9
   :SYSDUMP (*T7),(*T8),(*T9)

instead of

:SYSDUMP *T

you'll get a syntax error; if you say

   :SYSDUMP *T
   ...
   ENTER DUMP FILE SUBSET (S)
   @.@.@;STORESET=(*T7),(*T8),(*T9)

then you'll get an error "'STORESET' NOT ALLOWED WHEN POSITIONAL TAPE NAME IS SPECIFIED (S/R 6293)".

   Thus,  you either have to abandon TurboSTORE for system backups (if
you  do,  why  use  TurboSTORE  at  all? -- how  many other multi-reel
:STOREs  do  you  do?) or do all your  system backups with :STORE. The
latter alternative -- using :STORE for backups -- seems palatable, but
has two substantial problems:

1. Backups made with :STORE do not contain the SYSTEM DIRECTORY. If you need to restore from a :STORE backup, the best you can do is put on a ;CREATE, but that still might not create all the users and groups, and certainly won't preserve capabilities, security masks, etc.

      Your  normal coldload tape (which you had to make with :SYSDUMP)
      would  have this information, but unless  you cut a new coldload
      tape  every  night,  the  directory  information  on it  will be
      substantially out of date.

2. While you can :RESTORE equally well from a :STORE backup as from a :SYSDUMP backup, you can't do a RELOAD COMPACT from a :STORE tape. If you like to use this option for better disc space utilization, switching to :STORE backups may be unpleasant.

Well, you say, these might be non-trivial problems, but what can you do? If that's the way TurboSTORE works, we have to live with it and wait for a new version that might fix the problem (which may be some time down the road).

Fortunately, our user was an imaginative type -- he though that something could be done directly, without having to wait. Even when I assured him that the problem was probably quite deep and intractable, he still goaded me to go on; and, as it ultimately turned out, we managed to come up with a very simple solution.

   The  :SYSDUMP command does not write the files-to-be-stored to tape
itself;  it  uses  :STORE  to  do  this. After writing  all the system
information  to the backup tape,  it creates STORE.PUB.SYS (the :STORE
program)  as a son process and tells  it (via the ;INFO= string) which
files  are to be stored. Actually,  it passes to STORE.PUB.SYS all the
text  that  you typed in response to  "ENTER DUMP FILE SUBSET (S)", so
any       :STORE       keywords       specified       there      (e.g.
"@.@.@;STORESET=(*T7),(*T8),(*T9)")  are  seen  by  STORE.PUB.SYS.  In
fact, if you say
   :SYSDUMP *T
   ...
   ENTER DUMP FILE SUBSET (S)
   @.@.@;STORESET=(*T7),(*T8),(*T9)

then SYSDUMP will essentially do (using the CREATEPROCESS intrinsic) the equivalent of

:RUN STORE.PUB.SYS;& INFO="STORE @.@.@;*DUMPTAPE;STORESET=(*T7),(*T8),(*T9)"

The only reason why this fails seems to be that STORE.PUB.SYS doesn't let you specify both the "storefile" (*DUMPTAPE) parameter and ;STORESET=. It's purely a syntax check on STORE's part; if only SYSDUMP had done a

:RUN STORE.PUB.SYS; INFO="STORE @.@.@;STORESET=(*T7),(*T8),(*T9)"

-- without specifying *DUMPTAPE -- then everything would work! SYSDUMP would write all the system stuff to the store file you specified (in our example, *T, which SYSDUMP later refers to as *DUMPTAPE), and then use STORE.PUB.SYS to write all the files to the given ;STORESET=. Of course, *T (the SYSDUMP tape) would have to be on the same device as *T7 (the first tape in the ;STORESET= parameter).

(Note that SYSDUMP always refers to the tape parameter as *DUMPTAPE, even though you specified it as *T -- the :SYSDUMP command automatically sets up a ":FILE DUMPTAPE=*T" file equation.)

So now we know what we wish SYSDUMP would do -- not pass the *DUMPTAPE parameter to STORE.PUB.SYS when the user specifies a ;STORESET= parameter. Now, the question is: how do we make SYSDUMP do it? How do we force an HP program (the source of which we don't, of course, have) to do something it wasn't designed to do?

The solution is surprisingly simple. In fact, it does NOT involve patching any SYSDUMP code (which would be highly uncertain, which would constrain the nature of our patch, and which would require us to re-deduce the patch for every new version of SYSDUMP). What it does involve is doing the following:

* :HELLO MANAGER.SYS

* :NEWGROUP VESTORE.SYS;CAP=IA,BA,PH,PM,MR,DS

* :FCOPY FROM=SYSDUMP.PUB.SYS;TO=SYSDUMP.VESTORE.SYS;NEW

* Compile the following SPL program:

       $CONTROL NOSOURCE, USLINIT, SEGMENT=VESTORE, SUBPROGRAM
       BEGIN
       PROCEDURE CREATEPROCESS (ERR, PIN, PROGRAM, NUMBERS, ITEMS);
       INTEGER ERR;
       INTEGER PIN;
       BYTE ARRAY PROGRAM;
       INTEGER ARRAY NUMBERS;
       INTEGER ARRAY ITEMS;
       OPTION VARIABLE;
       BEGIN
       INTRINSIC LOADPROC, UNLOADPROC;
       INTEGER VAR'MASK = Q-4;
       INTEGER STATUS = Q-1;
       BYTE POINTER INFO;
       INTEGER INFO'LEN, I, J, PLABEL, IDENT;
       BYTE ARRAY PROC'NAME(0:15);
       IF VAR'MASK.(14:1)=1 AND VAR'MASK.(15:1)=1 THEN
         BEGIN   << NUMBERS and ITEMS specified, look at them >>
         INFO'LEN:=0;
         I:=0;
         WHILE NUMBERS(I)<>0 DO
           BEGIN   << find INFO and INFO LENGTH values >>
           IF NUMBERS(I)=11 <> THEN
             @INFO:=ITEMS(I)
           ELSE IF NUMBERS(I)=12 <> THEN
             INFO'LEN:=ITEMS(I);
           I:=I+1;
           END;
         IF INFO'LEN<>0 THEN
           FOR I:=0 UNTIL INFO'LEN-11 DO
             IF INFO(I)=";STORESET=" THEN   << store-set specified? >>
               FOR J:=0 UNTIL INFO'LEN-12 DO
                 IF INFO(J)=";*DUMPTAPE;" THEN
                   MOVE INFO(J):="          ;";   << delete *DUMPTAPE >>
         END;
       << Can't call CREATEPROCESS directly (this would just call >>
       << this procedure recursively) -- LOADPROC it and then call. >>
       MOVE PROC'NAME:="CREATEPROCESS ";
       IDENT:=LOADPROC (PROC'NAME, 0, PLABEL);
       TOS:=@ERR;
       TOS:=@PIN;
       TOS:=@PROGRAM;
       TOS:=@NUMBERS;
       TOS:=@ITEMS;
       TOS:=VAR'MASK;
       TOS:=PLABEL;
       ASSEMBLE (PCAL 0);
       IF > THEN STATUS.(6:2):=0    << set condition code >>
       ELSE IF < THEN STATUS.(6:2):=1
       ELSE STATUS.(6:2):=2;
       UNLOADPROC (IDENT);
       END;
       END.

* Add it to a group SL by saying

       :SEGMENTER
       -BUILDSL SL.VESTORE.SYS,20,1
       -USL $OLDPASS  (or whatever USL you compiled the above
                       SPL procedure into)
       -ADDSL VESTORE
       -EXIT

* Now, whenever you want to do a system backup, instead of saying ":SYSDUMP *T,*LISTFILE", say

       :FILE DUMPTAPE=*T
       :FILE SYSDLIST=*LISTFILE  (or $STDLIST, if you like)
       :RUN SYSDUMP.VESTORE.SYS;LIB=G

* When SYSDUMP.VESTORE.SYS prompts you with "ENTER DUMP FILE SUBSET (S)", say

fileset,fileset,...,fileset;STORESET=(*tape,*tape,...)

     In  other  words, specify everything  that you'd normally specify
     when  asked  for  the  dump  file  subsets,  PLUS  the ;STORESET=
     parameter that you'd specify on an ordinary :STORE command.

Now, SYSDUMP.VESTORE.SYS -- an exact copy of SYSDUMP.PUB.SYS -- will do your system backup USING TurboSTORE'S ;STORESET= KEYWORD.

How does this work? Well, when SYSDUMP.VESTORE.SYS (which was run with ;LIB=G) calls the CREATEPROCESS intrinsic to create STORE.PUB.SYS, it will actually call our own CREATEPROCESS procedure that we've put into SL.VESTORE.SYS.

This CREATEPROCESS procedure looks exactly like the CREATEPROCESS intrinsic; however, it also examines the ;INFO= parameter that's being passed to the program-to-be-created. (We find the ;INFO= string and its length in the NUMBERS and ITEMS arrays passed to CREATEPROCESS.) If this ;INFO= string contains the string ";STORESET=", then our CREATEPROCESS procedure REMOVES the string ";*DUMPTAPE" from the ;INFO= parameter.

After this is done, our own CREATEPROCESS calls the REAL CREATEPROCESS. Unfortunately, it can't just simply call CREATEPROCESS, since that would call our own CREATEPROCESS recursively (instead of calling the real CREATEPROCESS in the system SL). Instead, we use LOADPROC to load the real CREATEPROCESS explicitly from the system SL and then call it using PCAL 0.

   :SYSDUMP  and  TurboSTORE  can now work  together. The customer for
whom  we originally did this is  using this quite successfully for his
nightly  backups. There may be some problems with our approach that we
haven't  encountered  (use  it at your own risk!),  but it seems to be
working  quite  well.  If  you're a TurboSTORE customer,  try it -- it
might help you out of an unpleasant spot!

Go to Adager's index of technical papers ⮂ View original 1980s typography