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