afsconfig-and-rcsid-all-around-20010705
[openafs.git] / src / bucoord / restore.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * ALL RIGHTS RESERVED
12  */
13
14 #include <afs/param.h>
15 #include <afsconfig.h>
16
17 RCSID("$Header$");
18
19 #include <afs/stds.h>
20 #include <sys/types.h>
21 #include <afs/cmd.h>
22 #ifdef AFS_NT40_ENV
23 #include <winsock2.h>
24 #else
25 #include <strings.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <netdb.h>
29 #include <sys/time.h>
30 #endif
31 #include <lwp.h>
32 #include <afs/bubasics.h>
33 #include "bc.h"
34 #include <afs/com_err.h>
35 #include <afs/butc.h>
36 #include <afs/budb.h>
37 #include <afs/vlserver.h>
38 #include "error_macros.h"
39
40
41 extern struct bc_config *bc_globalConfig;
42 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
43 extern void bc_HandleMisc();
44 extern char *whoami;
45 extern struct rx_connection *bc_GetConn();
46
47 #ifdef AFS_SGI64_ENV
48 extern char *rindex();
49 #endif
50
51 #define BC_MAXLEVELS        20
52 #define MAXTAPESATONCE      10
53
54 #ifdef AFS_DEC_ENV
55 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.S_un.S_addr
56 #else
57 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
58 #endif
59
60 /* local structure to keep track of volumes and the dumps from which
61  * they should be restored 
62  */
63 struct dumpinfo 
64 {
65   struct dumpinfo *next;
66   struct volinfo  *volinfolist;
67   struct volinfo  *lastinlist;
68   afs_int32 DumpId;
69   afs_int32 initialDumpId;
70   afs_int32 parentDumpId;
71   afs_int32 level;
72 };
73
74 struct volinfo
75 {
76   struct voli *next;
77   char  *volname;
78   afs_int32 server;
79   afs_int32 partition;
80 };
81
82 /* local structure used to keep track of a tape, including a list of
83  * volumes to restore from that tape (bc_tapeItem)
84  */
85 struct bc_tapeList 
86 {
87     struct bc_tapeList *next;               /* next guy in list */
88     char  *tapeName;                        /* name of the tape */
89     afs_int32 dumpID;                       /* dump located on this tape */
90     afs_int32 initialDumpID;                /* initial dump located on this tape */
91     afs_int32 tapeNumber;                           /* which tape in the dump */
92     struct bc_tapeItem *restoreList;        /* volumes to restore from this tape */
93 };
94
95 /* each tape has a list of volumes to restore; hangs off of a struct
96    bc_tapeList.  Kept sorted so that we can read the tape once to do
97    everything we need to do.  */
98 struct bc_tapeItem 
99 {
100     struct bc_tapeItem *next;
101     char  *volumeName;              /* volume to restore */
102     afs_int32 position;             /* file slot on this tape */
103     afs_int32 oid;                          /* id of the volume */
104     afs_int32 dumplevel;                    /* level # of the containing dump (0 == top level) */
105     afs_int32 vollevel;             /* level # of the containing volume (0 == full dump) */
106     afs_uint32 server;
107     afs_int32 partition;
108     int   lastdump;                 /* The last incremental to restore */
109 };
110
111 /* strip .backup from the end of a name */
112 static StripBackup(aname)
113     register char *aname; 
114 {
115     int j;
116     
117     if (j = BackupName(aname)) {
118         aname[j] = 0;
119         return 0;
120     }
121     return -1;
122 }
123
124 int BackupName(aname)
125     char *aname; 
126 {
127     int j;
128
129     j = strlen(aname);
130     if ((j > 7) && (strcmp(".backup", aname+j-7) == 0))
131         return (j-7);
132     if ((j > 9) && (strcmp(".readonly", aname+j-9) == 0))
133         return (j-9);
134     return 0;
135 }
136
137 extractTapeSeq(tapename)
138     char *tapename;
139 {
140     char *sptr;
141
142     sptr = rindex(tapename, '.');
143     if ( !sptr ) return(-1);
144     sptr++;
145     return(atol(sptr));
146 }
147
148 viceName(value)
149   long value;
150 {
151   char *alph;
152   int r;
153
154   alph = "abcdefjhijklmnopqrstuvwxyz";
155
156   if (value > 25)
157     viceName(value/26 - 1);
158
159   r = value % 26;
160   printf("%c", alph[r]);
161 }
162
163 /* bc_DoRestore
164  * entry:
165  *      aindex - index into bc_dumpTasks that describes this dump.
166  */
167 bc_Restorer(aindex)
168      afs_int32 aindex; 
169 {
170     struct bc_dumpTask *dumpTaskPtr;
171
172     char   vname[BU_MAXNAMELEN];
173     struct budb_dumpEntry *dumpDescr, dumpDescr1, dumpDescr2;
174     struct budb_volumeEntry *volumeEntries;
175     struct bc_volumeDump *tvol;
176     afs_int32   code = 0, tcode;
177     afs_int32   tapedumpid, parent;
178
179     afs_int32  nentries = 0;
180     afs_int32  last, next, ve, vecount;
181     struct bc_tapeItem *ti, *pti, *nti;
182     struct bc_tapeList *tapeList = (struct bc_tapeList *)0;
183     struct bc_tapeList *tle, *ptle, *ntle;
184     afs_int32              tlid;
185     afs_int32              tapeid, tid;
186
187     struct tc_restoreArray rpcArray;            /* the rpc structure we use */
188     struct tc_restoreDesc  *tcarray = (struct tc_restoreDesc *)0;
189
190     afs_int32 i, startentry, todump;
191     afs_int32 port, nextport;
192     afs_int32 level, tapeseq;
193     afs_int32 foundvolume, voldumplevel;
194     struct rx_connection *aconn = (struct rx_connection *)0;
195     statusP statusPtr, newStatusPtr;
196
197     struct dumpinfo *dumpinfolist = (struct dumpinfo *)0;
198     struct dumpinfo *pdi, *ndi, *di, dlevels[BC_MAXLEVELS];
199     struct volinfo  *pvi, *nvi, *vi;
200     afs_int32           lvl, lv;
201
202     afs_int32           serverAll;         /* The server to which all volumes are to be restore to */
203     afs_int32           partitionAll;      /* Likewise for partition */
204     struct hostent  *hostPtr;
205     long            haddr;
206     u_long          did;
207     int             foundtape, c;
208
209     extern statusP createStatusNode();
210     extern statusP findStatus();
211
212     dumpTaskPtr  = &bc_dumpTasks[aindex];
213     serverAll    = HOSTADDR(&dumpTaskPtr->destServer);
214     partitionAll = dumpTaskPtr->destPartition;
215
216     volumeEntries = (struct budb_volumeEntry *)
217                       malloc(MAXTAPESATONCE * sizeof(struct budb_volumeEntry));
218     if (!volumeEntries)
219     {
220       com_err(whoami,BC_NOMEM,"");
221         ERROR(BC_NOMEM);
222     }
223     
224     /* For each volume to restore, find which dump it's most recent full or 
225      * incremental is on and thread onto our dump list (from oldest to newest
226      * dump). Also hang the volume off of the dump (no particular order).
227      */
228     for (tvol = dumpTaskPtr->volumes; tvol; tvol = tvol->next) 
229     { /*tvol*/
230         strcpy(vname, tvol->name);
231         dumpDescr = &dumpDescr1;
232         code = bcdb_FindDump(vname, dumpTaskPtr->fromDate, dumpDescr);
233
234         if ( !BackupName(vname) )    /* See if backup volume is there */
235         {
236             strcat(vname, ".backup");
237             dumpDescr = &dumpDescr2;
238             tcode = code;
239             code = bcdb_FindDump(vname, dumpTaskPtr->fromDate, dumpDescr);
240
241             if (code)                 /* Can't find backup, go with first results */
242             {
243                 strcpy(vname, tvol->name);
244                 dumpDescr = &dumpDescr1;
245                 code = tcode;
246             }
247             else if (!tcode)          /* Both found an entry, go with latest result */
248             {
249                 if (dumpDescr1.created > dumpDescr2.created)
250                 {
251                     strcpy(vname, tvol->name);
252                     dumpDescr = &dumpDescr1;
253                     code = tcode;
254                 }
255             }
256         }
257
258         if (code)                 /* If FindDump took an error */
259         {
260             com_err(whoami, code, "; Can't find any dump for volume %s", tvol->name);
261             continue;
262         }
263         
264         /* look to see if this dump has already been found */
265         for (pdi=0, di=dumpinfolist; di; pdi=di, di=di->next)
266         {
267             if      (di->DumpId <  dumpDescr->id) { di = 0; break; }
268             else if (di->DumpId == dumpDescr->id) { break; }
269         }
270
271         /* If didn't find it, create one and thread into list */
272         if (!di)
273         {
274             di = (struct dumpinfo *) malloc(sizeof(struct dumpinfo));
275             if (!di)
276             {
277                 com_err(whoami,BC_NOMEM,"");
278                 ERROR(BC_NOMEM);
279             }
280             bzero (di, sizeof(struct dumpinfo));
281
282             di->DumpId        = dumpDescr->id;
283             di->initialDumpId = dumpDescr->initialDumpID;
284             di->parentDumpId  = dumpDescr->parent;
285             di->level         = dumpDescr->level;
286
287             if (!pdi)
288             {
289                 di->next = dumpinfolist;
290                 dumpinfolist = di;
291             }
292             else
293             {
294                 di->next = pdi->next;
295                 pdi->next = di;
296             }
297         }
298         
299         /* Create one and thread into list */
300         vi = (struct volinfo *) malloc(sizeof(struct volinfo));
301         if (!vi)
302         {
303             com_err(whoami,BC_NOMEM,"");
304             ERROR(BC_NOMEM);
305         }
306         bzero (vi, sizeof(struct volinfo));
307         
308         vi->volname = (char*)malloc(strlen(vname)+1);
309         if (!vi->volname)
310         {
311             free(vi);
312             com_err(whoami,BC_NOMEM,"");
313             ERROR(BC_NOMEM);
314         }
315
316         strcpy(vi->volname, vname);
317         if (serverAll)
318         {
319             vi->server    = serverAll;
320             vi->partition = partitionAll;
321         }
322         else
323         {
324             vi->server    = HOSTADDR(&tvol->server);
325             vi->partition = tvol->partition;
326         }
327
328         /* thread onto end of list */
329         if (!di->lastinlist) di->volinfolist = vi;
330         else di->lastinlist->next = vi;
331         di->lastinlist = vi;
332     } /*tvol*/
333
334     /* For each of the above dumps we found (they could be increments), find 
335      * the dump's lineage (up to the full dump). 
336      */
337     for (di=dumpinfolist; di; di=di->next)
338     {
339         /* Find each of the parent dumps */
340         bcopy(di, &dlevels[0], sizeof(struct dumpinfo));
341         for (lvl=1, parent=dlevels[0].parentDumpId; parent; 
342              parent=dlevels[lvl].parentDumpId, lvl++)
343         {
344             code = bcdb_FindDumpByID(parent, &dumpDescr1);
345             if (code) 
346             {
347                 for (vi=di->volinfolist; vi; vi=vi->next)
348                 {
349                     com_err(whoami, code, "; Can't find parent DumpID %u for volume %s",
350                             parent, vi->volname);
351                 }
352                 break;
353             }
354
355             dlevels[lvl].DumpId        = dumpDescr1.id;
356             dlevels[lvl].initialDumpId = dumpDescr1.initialDumpID;
357             dlevels[lvl].parentDumpId  = dumpDescr1.parent;
358             dlevels[lvl].level         = dumpDescr1.level;
359         }
360
361         /* For each of the volumes that has a dump in this lineage (vi),
362          * find where it is in each dump level (lv) starting at level 0 and
363          * going to higest. Each dump level could contain one or more
364          * fragments (vecount) of the volume (volume fragments span tapes). 
365          * Each volume fragment is sorted by tapeid, tape sequence, and tape 
366          * position.
367          */
368         for (vi=di->volinfolist; vi; vi=vi->next)
369         {
370             tle  = tapeList;                  /* Use for searching the tapelist */
371             ptle = 0;                         /* The previous tape list entry */
372             tlid = 0;                         /* tapeid of first entry */
373             /* Volume's dump-level. May not be same as dump's dump-level. This
374              * value gets incremented everytime the volume is found on a dump.
375              */
376             voldumplevel = 0;
377
378             /* Do from level 0 dump to highest level dump */
379             for (lv=(lvl-1); lv>=0; lv--)
380             {
381                 foundvolume  = 0;             /* If found volume on this dump */
382                 tapeid = (dlevels[lv].initialDumpId ? dlevels[lv].initialDumpId :
383                                                       dlevels[lv].DumpId);
384
385                 /* Cycle through the volume fragments for this volume on the tape */
386                 for (last=0,c=0; last>=0; last=next,c++)
387                 {
388                     code = bcdb_FindVolumes(dlevels[lv].DumpId, vi->volname, volumeEntries, 
389                                             last, &next, MAXTAPESATONCE, &vecount);
390                     if (code)
391                     {
392                         /* Volume wasn't found on the tape - so continue.
393                          * This can happen when volume doesn't exist during level 0 dump
394                          * but exists for incremental dump.
395                          */
396                         if (code == BUDB_ENDOFLIST)
397                         {
398                             code = 0;
399                             break;
400                         }
401
402                         com_err(whoami, code, "; Can't find volume %s in DumpID %u",
403                                 vi->volname, dlevels[lv].DumpId);
404                         ERROR(code);
405                     }
406                       
407                     /* If we have made the Findvolumes call more than once for the same
408                      * volume, then begin the search at the top. This sort assumes that
409                      * we are collecting information from lowest level dump (full dump)
410                      * to the highest level dump (highest incremental)
411                      * and from first fragment to last fragment at each level. If
412                      * there is more than one call to FindVolumes, this is not true
413                      * anymore.
414                      */
415                     if (c)
416                     {
417                         tle  = tapeList;
418                         ptle = 0;
419                         tlid = 0;
420                     }
421
422                     /* For each of the volume fragments that we read, sort them into
423                      * the list of tapes they are on. Sort by tapeid, then tape sequence.
424                      * Do from first fragment (where volume begins) to last fragment.
425                      */
426                     for (ve=(vecount-1); ve>=0; ve--)
427                     {
428                         foundvolume = 1;        /* Found the volume on this dump */
429                         foundtape = 0;
430                         tapeseq   = volumeEntries[ve].tapeSeq;
431
432                         /* Determine where in the list of tapes this should go */
433                         for (; tle; ptle=tle, tle=tle->next)
434                         {
435                             tid = (tle->initialDumpID ? tle->initialDumpID : tle->dumpID);
436
437                             /* Sort by tapeids. BUT, we don't want add an entry in the middle 
438                              * of a dumpset (might split a volume fragmented across tapes). 
439                              * So make sure we step to next dumpset. tlid is the tapeid of 
440                              * the last tape we added a volume to. This can happen when an
441                              * incremental was appended to a tape created prior its parent-
442                              * dump's tape, and needs to be restored after it.
443                              */
444                             if (tapeid < tid)
445                             {
446                                 if (tlid != tid)
447                                     { tle = 0; break; }   /* Allocate and insert a tape entry */
448                             }
449
450                             /* Found the tapeid (the dumpset). Check if its the correct 
451                              * tape sequence
452                              */
453                             else if (tapeid == tid)
454                             {
455                                 if (tapeseq < tle->tapeNumber)
456                                     { tle = 0; break; }   /* Allocate and insert a tape entry */
457
458                                 if (tapeseq == tle->tapeNumber) 
459                                     { break; }            /* Don't allocate tape entry */
460
461                                 foundtape = 1;            /* Found dumpset but not the tape */
462                             }
463
464                             /* Prevously found the tapeid (the dumpset) but this tape 
465                              * sequence not included (the tapeid has changed). So add 
466                              * this tape entry to the end of its dumpset.
467                              */
468                             else if (foundtape)
469                                 { tle = 0; break; }       /* Allocate and insert a tape entry */
470                             
471                         }
472
473                         /* Allocate a new tapelist entry if one not found */
474                         if (!tle)
475                         {
476                             tle = (struct bc_tapeList *) malloc(sizeof(struct bc_tapeList));
477                             if (!tle)
478                             {
479                                 com_err(whoami,BC_NOMEM,"");
480                                 return(BC_NOMEM);
481                             }
482                             bzero(tle, sizeof(struct bc_tapeList));
483
484                             tle->tapeName = (char*)malloc(strlen(volumeEntries[ve].tape)+1);
485                             if (!tle->tapeName)
486                             {
487                                 free(tle);
488                                 com_err(whoami,BC_NOMEM,"");
489                                 return(BC_NOMEM);
490                             }
491
492                             strcpy(tle->tapeName, volumeEntries[ve].tape);
493                             tle->dumpID        = dlevels[lv].DumpId;
494                             tle->initialDumpID = dlevels[lv].initialDumpId;
495                             tle->tapeNumber    = tapeseq;
496
497                             /* Now thread the tape onto the list */
498                             if (!ptle)
499                             {
500                                 tle->next = tapeList;
501                                 tapeList = tle;
502                             }
503                             else
504                             {
505                                 tle->next = ptle->next;
506                                 ptle->next = tle;
507                             }
508                         }
509                         tlid = (tle->initialDumpID ? tle->initialDumpID : tle->dumpID);
510
511                         /* Now place the volume fragment into the correct position on 
512                          * this tapelist entry. Duplicate entries are ignored.
513                          */
514                         for (pti = 0, ti=tle->restoreList; ti; pti=ti, ti=ti->next)
515                         {
516                             if (volumeEntries[ve].position < ti->position) 
517                                 { ti = 0; break; }     /* Allocate an entry */
518
519                             else if (volumeEntries[ve].position == ti->position) 
520                                 { break; }             /* Duplicate entry */
521                         }
522
523                         /* If didn't find one, allocate and thread onto list.
524                          * Remember the server and partition.
525                          */
526                         if (!ti)
527                         {
528                             ti = (struct bc_tapeItem *) malloc(sizeof(struct bc_tapeItem));
529                             if (!ti)
530                             {
531                                 com_err(whoami,BC_NOMEM,"");
532                                 return(BC_NOMEM);
533                             }
534                             bzero(ti, sizeof(struct bc_tapeItem));
535
536                             ti->volumeName = (char*)malloc(strlen(volumeEntries[ve].name)+1);
537                             if (!ti->volumeName)
538                             {
539                                 free(ti);
540                                 com_err(whoami,BC_NOMEM,"");
541                                 return(BC_NOMEM);
542                             }
543
544                             strcpy(ti->volumeName, volumeEntries[ve].name);
545                             ti->server    = vi->server;
546                             ti->partition = vi->partition;
547                             ti->oid       = volumeEntries[ve].id;
548                             ti->position  = volumeEntries[ve].position;
549                             ti->vollevel  = voldumplevel;
550                             ti->dumplevel = dlevels[lv].level;
551                             ti->lastdump  = ((lv == 0) ? 1 : 0);
552
553                             if (!pti)
554                             {
555                                 ti->next = tle->restoreList;
556                                 tle->restoreList = ti;
557                             }
558                             else
559                             {
560                                 ti->next = pti->next;
561                                 pti->next = ti;
562                             }
563
564                             nentries++;
565                         } /* !ti */
566                     } /* ve: for each returned volume fragment by bcdb_FindVolumes() */
567                 } /* last: Multiple calls to bcdb_FindVolumes() */
568
569                 if (foundvolume) voldumplevel++;
570             } /* lv: For each dump level */
571         } /* vi: For each volume to search for in the dump hierarchy */
572     } /* di: For each dump */
573
574     if (!nentries)
575     {
576         com_err(whoami, 0, "No volumes to restore");
577         ERROR(0);
578     }
579
580     if (dumpTaskPtr->dontExecute)
581     {
582         for (tle=tapeList; tle; tle=tle->next)
583         {
584             for (ti=tle->restoreList; ti; ti=ti->next) 
585             {
586                 tapedumpid = (tle->initialDumpID ? tle->initialDumpID : tle->dumpID);
587                 strcpy(vname, ti->volumeName);
588                 StripBackup(vname);
589                 if (dumpTaskPtr->newExt) strcat(vname, dumpTaskPtr->newExt);
590
591                 /* print volume to restore and the tape and position its on */
592                 if (serverAll)
593                 {
594                     printf("Restore volume %s (ID %d) from tape %s (%u), position %d",
595                            ti->volumeName, ti->oid, tle->tapeName, tapedumpid, ti->position);
596
597                     if (strcmp(ti->volumeName, vname) != 0)
598                         printf(" as %s", vname);
599
600                     printf(".\n");
601                 }
602
603                 /* print in format recognized by volsetrestore */
604                 else
605                 {
606                     haddr = ti->server;
607                     hostPtr = gethostbyaddr((char *) &haddr, sizeof(haddr), AF_INET);
608                     if (hostPtr) printf("%s", hostPtr->h_name);
609                     else         printf("%u", ti->server);
610                     
611                     printf(" /vicep");
612                     viceName(ti->partition);
613
614                     printf(" %s # as %s; %s (%u); pos %d;", 
615                            ti->volumeName, vname, tle->tapeName, tapedumpid, ti->position);
616                     
617                     did = tle->dumpID;
618                     printf(" %s", ctime(&did));
619                 }
620             }
621         }
622
623         ERROR(0);
624     }
625
626     /* Allocate a list of volumes to restore */
627     tcarray = (struct tc_restoreDesc *) malloc(nentries*sizeof(struct tc_restoreDesc));
628     if (!tcarray)
629     {
630         com_err(whoami,BC_NOMEM,"");
631         ERROR(BC_NOMEM);
632     }
633     bzero(tcarray, nentries*sizeof(struct tc_restoreDesc));
634
635     /* Fill in the array with the list above */
636     i = 0;
637     for (tle=tapeList; tle; tle=tle->next)
638     {
639         for (ti=tle->restoreList; ti; ti=ti->next) 
640         {
641             tcarray[i].origVid       = ti->oid; /* means unknown */
642             tcarray[i].vid           = 0;
643             tcarray[i].flags         = 0;
644             if (ti->vollevel == 0)
645                tcarray[i].flags     |= RDFLAG_FIRSTDUMP;
646             if (ti->lastdump)
647                tcarray[i].flags     |= RDFLAG_LASTDUMP;
648             tcarray[i].position      = ti->position;
649             tcarray[i].dbDumpId      = tle->dumpID;
650             tcarray[i].initialDumpId = tle->initialDumpID;
651             tcarray[i].hostAddr      = ti->server;      /* just the internet address */
652             tcarray[i].partition     = ti->partition;
653
654             strcpy(tcarray[i].tapeName, tle->tapeName);
655             strcpy(tcarray[i].oldName,  ti->volumeName);
656             strcpy(tcarray[i].newName,  ti->volumeName);
657             StripBackup(tcarray[i].newName);
658             if (dumpTaskPtr->newExt) strcat(tcarray[i].newName, dumpTaskPtr->newExt);
659
660             tcarray[i].dumpLevel = ti->dumplevel;
661             i++;
662         }
663     }
664
665     printf("Starting restore\n");
666
667     /* Loop until all of the tape entries are used */
668     for (startentry=0; startentry<nentries; startentry+=todump)
669     {
670         /* Get all the next tape entries with the same port offset */
671         if (dumpTaskPtr->portCount == 0)
672         {
673             port   = 0;
674             todump = nentries - startentry;
675         }
676         else if (dumpTaskPtr->portCount == 1)
677         {
678             port   = dumpTaskPtr->portOffset[0];
679             todump = nentries - startentry;
680         }
681         else 
682         {
683             level = tcarray[startentry].dumpLevel;
684             port  = dumpTaskPtr->portOffset[(level <= dumpTaskPtr->portCount-1 ? 
685                                        level : dumpTaskPtr->portCount-1)];
686
687             for (todump=1; ((startentry+todump) < nentries); todump++)
688             {
689                 level     = tcarray[startentry+todump].dumpLevel;
690                 nextport  = dumpTaskPtr->portOffset[(level <= dumpTaskPtr->portCount-1 ? 
691                                                level : dumpTaskPtr->portCount-1)];
692                 if (port != nextport) break;          /* break if we change ports */
693             }
694         }
695
696         rpcArray.tc_restoreArray_len = todump;
697         rpcArray.tc_restoreArray_val = &tcarray[startentry];
698
699         code = ConnectButc(dumpTaskPtr->config, port, &aconn);
700         if (code) return(code);
701     
702         if (tcarray[startentry].dumpLevel == 0)
703             printf("\nFull restore being processed on port %d\n", port);
704         else
705             printf("\nIncremental restore being processed on port %d\n", port);
706
707         code = TC_PerformRestore(aconn, "DumpSetName", &rpcArray, &dumpTaskPtr->dumpID);
708         if (code)
709         {
710             com_err(whoami,code,"; Failed to start restore");
711             break;
712         }
713
714         /* setup status monitor node */
715         statusPtr = createStatusNode();
716         lock_Status();
717         statusPtr->port       = port;
718         statusPtr->taskId     = dumpTaskPtr->dumpID;
719         statusPtr->jobNumber  = bc_jobNumber();
720         statusPtr->flags     &= ~STARTING;             /* clearStatus to examine */
721         if (tcarray[startentry].dumpLevel == 0)
722             sprintf(statusPtr->taskName, "Full Restore");
723         else
724             sprintf(statusPtr->taskName, "Incremental Restore");
725         unlock_Status();
726
727         /* Wait until this restore pass is complete before starting the next
728          * pass. Query the statusWatcher for the status
729          */
730         while (1)
731         {
732             lock_Status();
733             newStatusPtr = findStatus(dumpTaskPtr->dumpID);
734             if (!newStatusPtr || (newStatusPtr->flags &
735                                   (ABORT_REQUEST|ABORT_SENT|ABORT_DONE|TASK_ERROR)) )
736             {
737                 unlock_Status();
738                 ERROR(0);
739             }
740             else if (newStatusPtr->flags & (TASK_DONE)) 
741             {
742                 unlock_Status();
743                 break;
744             }
745
746             unlock_Status();
747             IOMGR_Sleep(2);
748         }
749
750         if (aconn) rx_DestroyConnection(aconn);
751         aconn = (struct rx_connection *)0;
752     } /* while */
753
754     /* free up everything */
755   error_exit:
756     if (aconn) rx_DestroyConnection(aconn);
757
758     if (tcarray) free(tcarray);
759
760     /* Free the dumpinfo list and its volumes */
761     for (di=dumpinfolist; di; di=ndi)
762     {
763         for (vi=di->volinfolist; vi; vi=nvi)
764         {
765             if (vi->volname) free(vi->volname);
766             nvi = vi->next;
767             free(vi);
768         }
769         ndi = di->next;
770         free(di);
771     }
772
773     /* Free the tape lists and items */
774     for (tle = tapeList; tle; tle=ntle) 
775     {
776         for(ti = tle->restoreList; ti; ti = nti) 
777         {
778             nti = ti->next;
779             if (ti->volumeName) free(ti->volumeName);
780             free(ti);
781         }
782         ntle = tle->next;
783         if (tle->tapeName) free(tle->tapeName);
784         free(tle);
785     }
786
787     /* free local-like things we alloacted to save stack space */
788     if (volumeEntries) free(volumeEntries);
789
790     return code;
791 }