a78fc2ebaac7d017e8603b19de06e3dbc452ef71
[openafs.git] / src / bucoord / commands.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 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #include <sys/types.h>
18 #include <afs/cmd.h>
19 #ifdef AFS_NT40_ENV
20 #include <winsock2.h>
21 #else
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <netdb.h>
25 #include <strings.h>
26 #endif
27 #include <errno.h>
28 #include <afs/com_err.h>
29 #include <afs/budb.h>
30 #include <afs/butc.h>
31 #include <afs/bubasics.h>       /* PA */
32 #include <afs/volser.h>
33 #include <afs/voldefs.h>        /* PA */
34 #include <afs/vldbint.h>        /* PA */
35 #include <afs/ktime.h>          /* PA */
36 #include <time.h>
37
38 #include <string.h>
39 #include <lock.h>
40 #include <afs/butc.h>
41 #include <afs/tcdata.h>
42 #include <afs/butx.h>
43 #include "bc.h"
44 #include "error_macros.h"
45
46
47 extern struct bc_config *bc_globalConfig;
48 extern struct bc_dumpSchedule *bc_FindDumpSchedule();
49 extern struct bc_volumeSet *bc_FindVolumeSet(struct bc_config *cfg,
50                                              char *name);
51 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
52 extern struct ubik_client *cstruct;
53 extern int bc_Dumper();         /* function to do dumps */
54 extern int bc_Restorer();       /* function to do restores */
55 extern char *whoami;
56 extern struct ktc_token ttoken;
57 extern char *tailCompPtr();
58 extern statusP createStatusNode();
59
60 char *loadFile;
61 extern afs_int32 lastTaskCode;
62
63 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
64
65 int
66 bc_EvalVolumeSet(aconfig, avs, avols, uclient)
67      struct bc_config *aconfig;
68      register struct bc_volumeSet *avs;
69      struct bc_volumeDump **avols;
70      struct ubik_client *uclient;
71 {                               /*bc_EvalVolumeSet */
72     int code;
73     static afs_int32 use = 2;
74
75     if (use == 2) {             /* Use EvalVolumeSet2() */
76         code = EvalVolumeSet2(aconfig, avs, avols, uclient);
77         if (code == RXGEN_OPCODE)
78             use = 1;
79     }
80     if (use == 1) {             /* Use EvalVolumeSet1() */
81         code = EvalVolumeSet1(aconfig, avs, avols, uclient);
82     }
83     return code;
84 }                               /*bc_EvalVolumeSet */
85
86 struct partitionsort {
87     afs_int32 part;
88     struct bc_volumeDump *vdlist;
89     struct bc_volumeDump *lastvdlist;
90     struct bc_volumeDump *dupvdlist;
91     struct bc_volumeEntry *vole;
92     struct partitionsort *next;
93 };
94 struct serversort {
95     afs_uint32 ipaddr;
96     struct partitionsort *partitions;
97     struct serversort *next;
98 };
99
100 afs_int32
101 getSPEntries(server, partition, serverlist, ss, ps)
102      afs_uint32 server;
103      afs_int32 partition;
104      struct serversort **serverlist, **ss;
105      struct partitionsort **ps;
106 {
107     if (!(*ss) || ((*ss)->ipaddr != server)) {
108         *ps = 0;
109         for ((*ss) = *serverlist; (*ss); *ss = (*ss)->next) {
110             if ((*ss)->ipaddr == server)
111                 break;
112         }
113     }
114     /* No server entry added. Add one */
115     if (!(*ss)) {
116         *ss = (struct serversort *)malloc(sizeof(struct serversort));
117         if (!(*ss)) {
118             com_err(whoami, BC_NOMEM, "");
119             *ss = 0;
120             return (BC_NOMEM);
121         }
122         memset(*ss, 0, sizeof(struct serversort));
123         (*ss)->ipaddr = server;
124         (*ss)->next = *serverlist;
125         *serverlist = *ss;
126     }
127
128
129     if (!(*ps) || ((*ps)->part != partition)) {
130         for (*ps = (*ss)->partitions; *ps; *ps = (*ps)->next) {
131             if ((*ps)->part == partition)
132                 break;
133         }
134     }
135     /* No partition entry added. Add one */
136     if (!(*ps)) {
137         *ps = (struct partitionsort *)malloc(sizeof(struct partitionsort));
138         if (!(*ps)) {
139             com_err(whoami, BC_NOMEM, "");
140             free(*ss);
141             *ps = 0;
142             *ss = 0;
143             return (BC_NOMEM);
144         }
145         memset(*ps, 0, sizeof(struct partitionsort));
146         (*ps)->part = partition;
147         (*ps)->next = (*ss)->partitions;
148         (*ss)->partitions = *ps;
149     }
150     return 0;
151 }
152
153 afs_int32
154 randSPEntries(serverlist, avols)
155      struct serversort *serverlist;
156      struct bc_volumeDump **avols;
157 {
158     struct serversort *ss, **pss;
159     struct partitionsort *ps, **pps;
160     afs_int32 r;
161     afs_int32 scount, pcount;
162
163     *avols = 0;
164
165     /* Seed random number generator */
166     r = time(0) + getpid();
167     srand(r);
168
169     /* Count number of servers, remove one at a time */
170     for (scount = 0, ss = serverlist; ss; ss = ss->next, scount++);
171     for (; scount; scount--) {
172         /* Pick a random server in list and remove it */
173         r = (rand() >> 4) % scount;
174         for (pss = &serverlist, ss = serverlist; r;
175              pss = &ss->next, ss = ss->next, r--);
176         *pss = ss->next;
177
178         /* Count number of partitions, remove one at a time */
179         for (pcount = 0, ps = ss->partitions; ps; ps = ps->next, pcount++);
180         for (; pcount; pcount--) {
181             /* Pick a random parition in list and remove it */
182             r = (rand() >> 4) % pcount;
183             for (pps = &ss->partitions, ps = ss->partitions; r;
184                  pps = &ps->next, ps = ps->next, r--);
185             *pps = ps->next;
186
187             ps->lastvdlist->next = *avols;
188             *avols = ps->vdlist;
189             free(ps);
190         }
191         free(ss);
192     }
193     return 0;
194 }
195
196 int
197 EvalVolumeSet2(aconfig, avs, avols, uclient)
198      struct bc_config *aconfig;
199      struct bc_volumeSet *avs;
200      struct bc_volumeDump **avols;
201      struct ubik_client *uclient;
202 {                               /*EvalVolumeSet2 */
203     struct bc_volumeEntry *tve;
204     struct bc_volumeDump *tavols;
205     struct VldbListByAttributes attributes;
206     afs_int32 si, nsi;          /* startIndex and nextStartIndex */
207     afs_int32 nentries, e, ei, et, add, l;
208     nbulkentries bulkentries;
209     struct nvldbentry *entries = 0;
210     struct bc_volumeDump *tvd;
211     afs_int32 code = 0, tcode;
212     afs_int32 count = 0;
213     struct serversort *servers = 0, *lastserver = 0, *ss = 0;
214     struct partitionsort *ps = 0;
215
216     *avols = (struct bc_volumeDump *)0;
217     bulkentries.nbulkentries_len = 0;
218     bulkentries.nbulkentries_val = 0;
219
220     /* For each of the volume set entries - collect the volumes that match it */
221     for (tve = avs->ventries; tve; tve = tve->next) {
222         /* Put together a call to the vlserver for this vlentry. The 
223          * performance gain is from letting the vlserver expand the
224          * volumeset and not this routine.
225          */
226         attributes.Mask = 0;
227         if (tve->server.sin_addr.s_addr) {      /* The server */
228             attributes.Mask |= VLLIST_SERVER;
229             attributes.server = tve->server.sin_addr.s_addr;
230         }
231         if (tve->partition != -1) {     /* The partition */
232             attributes.Mask |= VLLIST_PARTITION;
233             attributes.partition = tve->partition;
234         }
235
236         /* Now make the call to the vlserver */
237         for (si = 0; si != -1; si = nsi) {
238             nentries = 0;
239             bulkentries.nbulkentries_len = 0;
240             bulkentries.nbulkentries_val = 0;
241             nsi = -1;
242             tcode =
243                 ubik_Call(VL_ListAttributesN2, uclient, 0, &attributes,
244                           tve->name, si, &nentries, &bulkentries, &nsi);
245             if (tcode)
246                 ERROR(tcode);
247
248             /* The 3.4 vlserver has a VL_ListAttributesN2() RPC call, but
249              * it is not complete. The only way to tell if it is not complete
250              * is if et == 0 (which we check for below). Also, if the call didn't
251              * match any entries, then we don't know what version the vlserver
252              * is. In both cases, we return RXGEN_OPCODE and the calling routine
253              * will switch to the EvalVolumeSet1() call.
254              */
255             if (nentries == 0)
256                 ERROR(RXGEN_OPCODE);    /* Use EvalVolumeSet1 */
257
258             /* Step through each entry and add it to the list of volumes */
259             entries = bulkentries.nbulkentries_val;
260             for (e = 0; e < nentries; e++) {
261                 ei = entries[e].matchindex & 0xffff;
262                 et = (entries[e].matchindex >> 16) & 0xffff;
263                 switch (et) {
264                 case ITSRWVOL:{
265                         et = RWVOL;
266                         break;
267                     }
268                 case ITSBACKVOL:{
269                         et = BACKVOL;
270                         break;
271                     }
272                 case ITSROVOL:{
273                         et = ROVOL;
274                         break;
275                     }
276                 default:
277                     ERROR(RXGEN_OPCODE);        /* Use EvalVolumeSet1 */
278                 }
279
280                 /* Find server and partiton structure to hang the entry off of */
281                 tcode =
282                     getSPEntries(entries[e].serverNumber[ei],
283                                  entries[e].serverPartition[ei], &servers,
284                                  &ss, &ps);
285                 if (tcode) {
286                     com_err(whoami, tcode, "");
287                     ERROR(tcode);
288                 }
289
290                 /* Detect if this entry should be added (not a duplicate).
291                  * Use ps->dupvdlist and ps->vole to only search volumes from
292                  * previous volume set entries.
293                  */
294                 add = 1;
295                 if (tve != avs->ventries) {
296                     l = strlen(entries[e].name);
297                     if (ps->vole != tve) {
298                         ps->vole = tve;
299                         ps->dupvdlist = ps->vdlist;
300                     }
301                     for (tavols = ps->dupvdlist; add && tavols;
302                          tavols = tavols->next) {
303                         if (strncmp(tavols->name, entries[e].name, l) == 0) {
304                             if ((strcmp(&entries[e].name[l], ".backup") == 0)
305                                 || (strcmp(&entries[e].name[l], ".readonly")
306                                     == 0)
307                                 || (strcmp(&entries[e].name[l], "") == 0))
308                                 add = 0;
309                         }
310                     }
311                 }
312
313                 if (add) {
314                     /* Allocate a volume dump structure and its name */
315                     tvd = (struct bc_volumeDump *)
316                         malloc(sizeof(struct bc_volumeDump));
317                     if (!tvd) {
318                         com_err(whoami, BC_NOMEM, "");
319                         ERROR(BC_NOMEM);
320                     }
321                     memset(tvd, 0, sizeof(*tvd));
322
323                     tvd->name = (char *)malloc(strlen(entries[e].name) + 10);
324                     if (!(tvd->name)) {
325                         com_err(whoami, BC_NOMEM, "");
326                         free(tvd);
327                         ERROR(BC_NOMEM);
328                     }
329
330                     /* Fill it in and thread onto avols list */
331                     strcpy(tvd->name, entries[e].name);
332                     if (et == BACKVOL)
333                         strcat(tvd->name, ".backup");
334                     else if (et == ROVOL)
335                         strcat(tvd->name, ".readonly");
336                     tvd->vid = entries[e].volumeId[et];
337                     tvd->entry = tve;
338                     tvd->volType = et;
339                     tvd->partition = entries[e].serverPartition[ei];
340                     tvd->server.sin_addr.s_addr = entries[e].serverNumber[ei];
341                     tvd->server.sin_port = 0;   /* default FS port */
342                     tvd->server.sin_family = AF_INET;
343 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
344                     tvd->server.sin_len = sizeof(struct sockaddr_in);
345 #endif
346
347                     /* String tvd off of partition struct */
348                     tvd->next = ps->vdlist;
349                     ps->vdlist = tvd;
350                     if (!tvd->next)
351                         ps->lastvdlist = tvd;
352
353                     count++;
354                 }
355             }
356
357             /* Free memory allocated during VL call */
358             if (bulkentries.nbulkentries_val) {
359                 free((char *)bulkentries.nbulkentries_val);
360                 bulkentries.nbulkentries_val = 0;
361                 entries = 0;
362             }
363         }
364     }
365
366     /* Randomly link the volumedump entries together */
367     randSPEntries(servers, avols);
368     fprintf(stderr, "Total number of volumes : %u\n", count);
369
370   error_exit:
371     if (bulkentries.nbulkentries_val) {
372         free((char *)bulkentries.nbulkentries_val);
373     }
374     return (code);
375 }                               /*EvalVolumeSet2 */
376
377 /*-----------------------------------------------------------------------------
378  * EvalVolumeSetOld
379  *
380  * Description:
381  *      Takes the entries in a volumeset and expands them into a list of 
382  *      volumes. Every VLDB volume entry is looked at and compared to the
383  *      volumeset entries.
384  *
385  *      When matching a VLDB volume entry to a volumeset entry, 
386  *       1. If the RW volume entry matches, that RW volume is used.
387  *       2. Otherwise, if the BK volume entry matches, the BK volume is used.
388  *       3. Finally, if the RO volume entry matches, the RO volume is used.
389  *      For instance: A volumeset entry of ".* .* user.t.*" will match volume 
390  *                    "user.troy" and "user.troy.backup". The rules will use 
391  *                    the RW volume "user.troy".
392  *
393  *      When a VLDB volume entry matches a volumeset entry (be it RW, BK or RO),
394  *      that volume is used and matches against any remaining volumeset entries 
395  *      are not even done.
396  *      For instance: A 1st volumeset entry ".* .* .*.backup" will match with
397  *                    "user.troy.backup". Its 2nd volumeset entry ".* .* .*" 
398  *                    would have matched its RW volume "user.troy", but the first 
399  *                    match is used and the second match isn't even done.
400  *
401  * Arguments:
402  *      aconfig : Global configuration info.
403  *      avs     : 
404  *      avols   : Ptr to linked list of entries describing volumes to dump.
405  *      uclient : Ptr to Ubik client structure.
406  *
407  * Returns:
408  *      0 on successful volume set evaluation,
409  *      Lower-level codes otherwise.
410  *
411  * Environment:
412  *      Expand only the single volume set provided (avs); don't go down its chain.
413  *
414  * Side Effects:
415  *      None.
416  *-----------------------------------------------------------------------------
417  */
418 int
419 EvalVolumeSet1(aconfig, avs, avols, uclient)
420      struct bc_config *aconfig;
421      struct bc_volumeSet *avs;
422      struct bc_volumeDump **avols;
423      struct ubik_client *uclient;
424 {                               /*EvalVolumeSet1 */
425     afs_int32 code;             /*Result of various calls */
426     char *errm;
427     struct bc_volumeDump *tvd;  /*Ptr to new dump instance */
428     struct bc_volumeEntry *tve, *ctve;  /*Ptr to new volume entry instance */
429     char patt[256];             /*Composite regex; also, target string */
430     int volType;                /*Type of volume that worked */
431     afs_int32 index;            /*Current VLDB entry index */
432     afs_int32 count;            /*Needed by VL_ListEntry() */
433     afs_int32 next_index;       /*Next index to list */
434     struct vldbentry entry;     /*VLDB entry */
435     int srvpartpair;            /*Loop counter: server/partition pair */
436     afs_int32 total = 0;
437     int found, foundentry;
438     struct serversort *servers = 0, *ss = 0;
439     struct partitionsort *ps = 0;
440
441     *avols = (struct bc_volumeDump *)0;
442     ctve = (struct bc_volumeEntry *)0;  /* no compiled entry */
443
444     /* For each vldb entry.
445      * Variable next_index is set to the index of the next VLDB entry
446      * in the enumeration.
447      */
448     for (index = 0; 1; index = next_index) {    /*w */
449         memset(&entry, 0, sizeof(entry));
450         code = ubik_Call(VL_ListEntry,  /*Routine to invoke */
451                          uclient,       /*Ubik client structure */
452                          0,     /*Ubik flags */
453                          index, /*Current index */
454                          &count,        /*Ptr to working variable */
455                          &next_index,   /*Ptr to next index value to list */
456                          &entry);       /*Ptr to entry to fill */
457         if (code)
458             return code;
459         if (!next_index)
460             break;              /* If the next index is invalid, bail out now. */
461
462         /* For each entry in the volume set */
463         found = 0;              /* No match in volume set yet */
464         for (tve = avs->ventries; tve; tve = tve->next) {       /*ve */
465             /* for each server in the vldb entry */
466             for (srvpartpair = 0; srvpartpair < entry.nServers; srvpartpair++) {        /*s */
467                 /* On the same server */
468                 if (tve->server.sin_addr.s_addr
469                     && !VLDB_IsSameAddrs(tve->server.sin_addr.s_addr,
470                                          entry.serverNumber[srvpartpair],
471                                          &code)) {
472                     if (code)
473                         return (code);
474                     continue;
475                 }
476
477
478                 /* On the same partition */
479                 if ((tve->partition != -1)
480                     && (tve->partition != entry.serverPartition[srvpartpair]))
481                     continue;
482
483                 /* If the volume entry is not compiled, then compile it */
484                 if (ctve != tve) {
485                     sprintf(patt, "^%s$", tve->name);
486                     errm = (char *)re_comp(patt);
487                     if (errm) {
488                         com_err(whoami, 0,
489                                 "Can't compile regular expression '%s': %s",
490                                 patt, errm);
491                         return (-1);
492                     }
493                     ctve = tve;
494                 }
495
496                 /* If the RW name matches the volume set entry, take
497                  * it and exit. First choice is to use the RW volume.
498                  */
499                 if (entry.serverFlags[srvpartpair] & ITSRWVOL) {
500                     if (entry.flags & RW_EXISTS) {
501                         sprintf(patt, "%s", entry.name);
502                         code = re_exec(patt);
503                         if (code == 1) {
504                             found = 1;
505                             foundentry = srvpartpair;
506                             volType = RWVOL;
507                             break;
508                         }
509                     }
510
511                     /* If the BK name matches the volume set entry, take 
512                      * it and exit. Second choice is to use the BK volume.
513                      */
514                     if (entry.flags & BACK_EXISTS) {
515                         sprintf(patt, "%s.backup", entry.name);
516                         code = re_exec(patt);
517                         if (code == 1) {
518                             found = 1;
519                             foundentry = srvpartpair;
520                             volType = BACKVOL;
521                             break;
522                         }
523                     }
524                 }
525
526                 /* If the RO name matches the volume set entry, remember
527                  * it, but continue searching. Further entries may be
528                  * RW or backup entries that will match.
529                  */
530                 else if (!found && (entry.serverFlags[srvpartpair] & ITSROVOL)
531                          && (entry.flags & RO_EXISTS)) {
532                     sprintf(patt, "%s.readonly", entry.name);
533                     code = re_exec(patt);
534                     if (code == 1) {
535                         found = 1;
536                         foundentry = srvpartpair;
537                         volType = ROVOL;
538                     }
539                 }
540
541                 if (code < 0)
542                     com_err(whoami, 0, "Internal error in regex package");
543             }                   /*s */
544
545             /* If found a match, then create a new volume dump entry */
546             if (found) {        /*f */
547                 /* Find server and partition structure to hang the entry off of */
548                 code =
549                     getSPEntries(entry.serverNumber[foundentry],
550                                  entry.serverPartition[foundentry], &servers,
551                                  &ss, &ps);
552                 if (code) {
553                     com_err(whoami, code, "");
554                     return (code);
555                 }
556
557                 total++;
558                 tvd = (struct bc_volumeDump *)
559                     malloc(sizeof(struct bc_volumeDump));
560                 if (!tvd) {
561                     com_err(whoami, BC_NOMEM, "");
562                     return (BC_NOMEM);
563                 }
564                 memset(tvd, 0, sizeof(*tvd));
565
566                 tvd->name = (char *)malloc(strlen(entry.name) + 10);
567                 if (!(tvd->name)) {
568                     com_err(whoami, BC_NOMEM, "");
569                     free(tvd);
570                     return (BC_NOMEM);
571                 }
572
573                 strcpy(tvd->name, entry.name);
574                 if (volType == BACKVOL)
575                     strcat(tvd->name, ".backup");
576                 else if (volType == ROVOL)
577                     strcat(tvd->name, ".readonly");
578                 tvd->vid = entry.volumeId[volType];
579                 tvd->entry = tve;
580                 tvd->volType = volType;
581                 tvd->partition = entry.serverPartition[foundentry];
582                 tvd->server.sin_addr.s_addr = entry.serverNumber[foundentry];
583                 tvd->server.sin_port = 0;       /* default FS port */
584                 tvd->server.sin_family = AF_INET;
585
586                 /* String tvd off of partition struct */
587                 tvd->next = ps->vdlist;
588                 ps->vdlist = tvd;
589                 if (!tvd->next)
590                     ps->lastvdlist = tvd;
591
592                 break;
593             }                   /*f */
594         }                       /*ve */
595     }                           /*w */
596
597     /* Randomly link the volumedump entries together */
598     randSPEntries(servers, avols);
599
600     fprintf(stderr, "Total number of volumes : %u\n", total);
601     return (0);
602 }                               /*EvalVolumeSet1 */
603
604 /* compactDateString
605  *      print out a date in compact format, 16 chars, format is
606  *      mm/dd/yyyy hh:mm
607  * entry:
608  *      date_long - ptr to a long containing the time
609  * exit:
610  *      ptr to a string containing a representation of the date
611  */
612 char *
613 compactDateString(date_long, string, size)
614      afs_int32 *date_long, size;
615      char *string;
616 {
617     struct tm *ltime;
618
619     if (!string)
620         return 0;
621
622     if (*date_long == NEVERDATE) {
623         sprintf(string, "NEVER");
624     } else {
625         time_t t = *date_long;
626         ltime = localtime(&t);
627         /* prints date in U.S. format of mm/dd/yyyy */
628         strftime(string, size, "%m/%d/%Y %H:%M", ltime);
629     }
630     return (string);
631 }
632
633 afs_int32
634 bc_SafeATOI(anum)
635      char *anum;
636 {
637     afs_int32 total = 0;
638
639     for (; *anum; anum++) {
640         if ((*anum < '0') || (*anum > '9'))
641             return -1;
642         total = (10 * total) + (afs_int32) (*anum - '0');
643     }
644     return total;
645 }
646
647 /* bc_FloatATOI:
648  *    Take a string and parse it for a number (could be float) followed
649  *    by a character representing the units (K,M,G,T). Default is 'K'.
650  *    Return the size in KBytes.
651  */
652 afs_int32
653 bc_FloatATOI(anum)
654      char *anum;
655 {
656     float total = 0;
657     afs_int32 rtotal;
658     afs_int32 fraction = 0;     /* > 0 if past the decimal */
659
660     for (; *anum; anum++) {
661         if ((*anum == 't') || (*anum == 'T')) {
662             total *= 1024 * 1024 * 1024;
663             break;
664         }
665         if ((*anum == 'g') || (*anum == 'G')) {
666             total *= 1024 * 1024;
667             break;
668         }
669         if ((*anum == 'm') || (*anum == 'M')) {
670             total *= 1024;
671             break;
672         }
673         if ((*anum == 'k') || (*anum == 'K')) {
674             break;
675         }
676         if (*anum == '.') {
677             fraction = 10;
678             continue;
679         }
680         if ((*anum < '0') || (*anum > '9'))
681             return -1;
682
683         if (!fraction) {
684             total = (10. * total) + (float)(*anum - '0');
685         } else {
686             total += ((float)(*anum - '0')) / (float)fraction;
687             fraction *= 10;
688         }
689     }
690
691     total += 0.5;               /* Round up */
692     if (total > 0x7fffffff)     /* Don't go over 2G */
693         total = 0x7fffffff;
694     rtotal = (afs_int32) total;
695     return (rtotal);
696 }
697
698 /* make a copy of a string so that it can be freed later */
699 char *
700 bc_CopyString(astring)
701      char *astring;
702 {
703     afs_int32 tlen;
704     char *tp;
705
706     if (!astring)
707         return (NULL);          /* propagate null strings easily */
708     tlen = strlen(astring);
709     tp = (char *)malloc(tlen + 1);      /* don't forget the terminating null */
710     if (!tp) {
711         com_err(whoami, BC_NOMEM, "");
712         return (tp);
713     }
714     strcpy(tp, astring);
715     return tp;
716 }
717
718 /* concatParams
719  * 
720  *    Concatenates the parameters of an option and returns the string.
721  *
722  */
723
724 char *
725 concatParams(itemPtr)
726      struct cmd_item *itemPtr;
727 {
728     struct cmd_item *tempPtr;
729     afs_int32 length = 0;
730     char *string;
731
732     /* compute the length of string required */
733     for (tempPtr = itemPtr; tempPtr; tempPtr = tempPtr->next) {
734         length += strlen(tempPtr->data);
735         length++;               /* space or null terminator */
736     }
737
738     if (length == 0) {          /* no string (0 length) */
739         com_err(whoami, 0, "Can't have zero length date and time string");
740         return (NULL);
741     }
742
743     string = (char *)malloc(length);    /* allocate the string */
744     if (!string) {
745         com_err(whoami, BC_NOMEM, "");
746         return (NULL);
747     }
748     string[0] = 0;
749
750     tempPtr = itemPtr;          /* now assemble the string */
751     while (tempPtr) {
752         strcat(string, tempPtr->data);
753         tempPtr = tempPtr->next;
754         if (tempPtr)
755             strcat(string, " ");
756     }
757
758     return (string);            /* return the string */
759 }
760
761 /* printIfStatus
762  *      print out an interface status node as received from butc
763  */
764
765 printIfStatus(statusPtr)
766      struct tciStatusS *statusPtr;
767 {
768     printf("Task %d: %s: ", statusPtr->taskId, statusPtr->taskName);
769     if (statusPtr->nKBytes)
770         printf("%ld Kbytes transferred", statusPtr->nKBytes);
771     if (strlen(statusPtr->volumeName) != 0) {
772         if (statusPtr->nKBytes)
773             printf(", ");
774         printf("volume %s", statusPtr->volumeName);
775     }
776
777     /* orphan */
778
779     if (statusPtr->flags & ABORT_REQUEST)
780         printf(" [abort request rcvd]");
781
782     if (statusPtr->flags & ABORT_DONE)
783         printf(" [abort complete]");
784
785     if (statusPtr->flags & OPR_WAIT)
786         printf(" [operator wait]");
787
788     if (statusPtr->flags & CALL_WAIT)
789         printf(" [callout in progress]");
790
791     if (statusPtr->flags & DRIVE_WAIT)
792         printf(" [drive wait]");
793
794     if (statusPtr->flags & TASK_DONE)
795         printf(" [done]");
796     printf("\n");
797 }
798
799 afs_int32
800 getPortOffset(port)
801      char *port;
802 {
803     afs_int32 portOffset;
804
805     portOffset = bc_SafeATOI(port);
806
807     if (portOffset < 0) {
808         com_err(whoami, 0, "Can't decode port offset '%s'", port);
809         return (-1);
810     } else if (portOffset > BC_MAXPORTOFFSET) {
811         com_err(whoami, 0, "%u exceeds max port offset %u", portOffset,
812                 BC_MAXPORTOFFSET);
813         return (-1);
814     }
815     return (portOffset);
816 }
817
818 /* bc_GetTapeStatusCmd
819  *      display status of all tasks on a particular tape coordinator
820  */
821
822 bc_GetTapeStatusCmd(as, arock)
823      struct cmd_syndesc *as;
824      char *arock;
825 {
826     afs_int32 code;
827     struct rx_connection *tconn;
828     afs_int32 portOffset = 0;
829
830     int ntasks;
831     afs_uint32 flags;
832     afs_uint32 taskId;
833     struct tciStatusS status;
834
835     code = bc_UpdateHosts();
836     if (code) {
837         com_err(whoami, code, "; Can't retrieve tape hosts");
838         return (code);
839     }
840
841     if (as->parms[0].items) {
842         portOffset = getPortOffset(as->parms[0].items->data);
843         if (portOffset < 0)
844             return (BC_BADARG);
845     }
846
847     code = ConnectButc(bc_globalConfig, portOffset, &tconn);
848     if (code)
849         return (code);
850
851     flags = TSK_STAT_FIRST;
852     taskId = 0;
853     ntasks = 0;
854
855     while ((flags & TSK_STAT_END) == 0) {
856         code = TC_ScanStatus(tconn, &taskId, &status, &flags);
857         if (code) {
858             if (code == TC_NOTASKS)
859                 break;
860             com_err(whoami, code, "; Can't get status from butc");
861             return (-1);
862         }
863         if ((flags & TSK_STAT_NOTFOUND))
864             break;              /* Can't find the task id */
865         flags &= ~TSK_STAT_FIRST;       /* turn off flag */
866
867         printIfStatus(&status);
868         ntasks++;
869     }
870
871     if (ntasks == 0)
872         printf("Tape coordinator is idle\n");
873
874     if (flags & TSK_STAT_ADSM)
875         printf("TSM Tape coordinator\n");
876     else if (flags & TSK_STAT_XBSA)
877         printf("XBSA Tape coordinator\n");
878
879     return (0);
880 }
881
882 extern struct Lock dispatchLock;
883
884 /* bc_WaitForNoJobs
885  *      wait for all jobs to terminate
886  */
887 bc_WaitForNoJobs()
888 {
889     afs_int32 wcode = 0;
890     int i;
891     int waitmsg = 1;
892     int usefulJobRunning = 1;
893
894     extern dlqlinkT statusHead;
895
896     com_err(whoami, 0, "waiting for job termination");
897
898     while (usefulJobRunning) {
899         usefulJobRunning = (dlqEmpty(&statusHead) ? 0 : 1);
900         if (dispatchLock.excl_locked)
901             usefulJobRunning = 1;
902         for (i = 0; (!usefulJobRunning && (i < BC_MAXSIMDUMPS)); i++) {
903             if (bc_dumpTasks[i].flags & BC_DI_INUSE)
904                 usefulJobRunning = 1;
905         }
906
907         /* Wait 5 seconds and check again */
908         if (usefulJobRunning)
909             IOMGR_Sleep(5);
910     }
911     return (lastTaskCode);
912 }
913
914 /* bc_JobsCmd
915  *      print status on running jobs
916  * parameters
917  *      ignored - a null "as" prints only jobs.
918  */
919
920 bc_JobsCmd(as, arock)
921      struct cmd_syndesc *as;
922      char *arock;
923 {
924     afs_int32 prevTime;
925     dlqlinkP ptr;
926     statusP statusPtr;
927     char ds[50];
928
929     statusP youngest;
930     dlqlinkT atJobsHead;
931     extern dlqlinkT statusHead;
932
933     dlqInit(&atJobsHead);
934
935     lock_Status();
936     ptr = (&statusHead)->dlq_next;
937     while (ptr != &statusHead) {
938         statusPtr = (statusP) ptr;
939         ptr = ptr->dlq_next;
940
941         if (statusPtr->scheduledDump) {
942             dlqUnlink((dlqlinkP) statusPtr);
943             dlqLinkb(&atJobsHead, (dlqlinkP) statusPtr);
944         } else {
945             printf("Job %d:", statusPtr->jobNumber);
946
947             printf(" %s", statusPtr->taskName);
948
949             if (statusPtr->dbDumpId)
950                 printf(": DumpID %u", statusPtr->dbDumpId);
951             if (statusPtr->nKBytes)
952                 printf(", %ld Kbytes", statusPtr->nKBytes);
953             if (strlen(statusPtr->volumeName) != 0)
954                 printf(", volume %s", statusPtr->volumeName);
955
956             if (statusPtr->flags & CONTACT_LOST)
957                 printf(" [butc contact lost]");
958
959             if (statusPtr->flags & ABORT_REQUEST)
960                 printf(" [abort request]");
961
962             if (statusPtr->flags & ABORT_SENT)
963                 printf(" [abort sent]");
964
965             if (statusPtr->flags & OPR_WAIT)
966                 printf(" [operator wait]");
967
968             if (statusPtr->flags & CALL_WAIT)
969                 printf(" [callout in progress]");
970
971             if (statusPtr->flags & DRIVE_WAIT)
972                 printf(" [drive wait]");
973             printf("\n");
974         }
975     }
976
977     /* 
978      * Now print the scheduled dumps.
979      */
980     if (!dlqEmpty(&statusHead) && as)
981         printf("\n");           /* blank line between running and scheduled dumps */
982
983     prevTime = 0;
984     while (!dlqEmpty(&atJobsHead)) {
985         ptr = (&atJobsHead)->dlq_next;
986         youngest = (statusP) ptr;
987
988         ptr = ptr->dlq_next;
989         while (ptr != &atJobsHead) {    /* Find the dump that starts the earliest */
990             statusPtr = (statusP) ptr;
991             if (statusPtr->scheduledDump < youngest->scheduledDump)
992                 youngest = statusPtr;
993             ptr = ptr->dlq_next;
994         }
995
996         /* Print token expiration time */
997         if ((ttoken.endTime > prevTime)
998             && (ttoken.endTime <= youngest->scheduledDump) && as
999             && (ttoken.endTime != NEVERDATE)) {
1000             if (ttoken.endTime > time(0)) {
1001                 compactDateString(&ttoken.endTime, ds, 50);
1002                 printf("       %16s: TOKEN EXPIRATION\n", ds);
1003             } else {
1004                 printf("       TOKEN HAS EXPIRED\n");
1005             }
1006         }
1007         prevTime = youngest->scheduledDump;
1008
1009         /* Print the info */
1010         compactDateString(&youngest->scheduledDump, ds, 50);
1011         printf("Job %d:", youngest->jobNumber);
1012         printf(" %16s: %s", ds, youngest->cmdLine);
1013         printf("\n");
1014
1015         /* return to original list */
1016         dlqUnlink((dlqlinkP) youngest);
1017         dlqLinkb(&statusHead, (dlqlinkP) youngest);
1018     }
1019
1020     /* Print token expiration time if havn't already */
1021     if ((ttoken.endTime == NEVERDATE) && as)
1022         printf("     : TOKEN NEVER EXPIRES\n");
1023     else if ((ttoken.endTime > prevTime) && as) {
1024         if (ttoken.endTime > time(0)) {
1025             compactDateString(&ttoken.endTime, ds, 50);
1026             printf("       %16s: TOKEN EXPIRATION\n", ds);
1027         } else {
1028             printf("     : TOKEN HAS EXPIRED\n");
1029         }
1030     }
1031
1032     unlock_Status();
1033     return 0;
1034 }
1035
1036 bc_KillCmd(as, arock)
1037      struct cmd_syndesc *as;
1038      char *arock;
1039 {
1040     afs_int32 i;
1041     afs_int32 slot;
1042     struct bc_dumpTask *td;
1043     char *tp;
1044     char tbuffer[256];
1045
1046     dlqlinkP ptr;
1047     statusP statusPtr;
1048
1049     extern dlqlinkT statusHead;
1050     extern statusP findStatus();
1051
1052
1053     tp = as->parms[0].items->data;
1054     if (strchr(tp, '.') == 0) {
1055         slot = bc_SafeATOI(tp);
1056         if (slot == -1) {
1057             com_err(whoami, 0, "Bad syntax for number '%s'", tp);
1058             return -1;
1059         }
1060
1061         lock_Status();
1062         ptr = (&statusHead)->dlq_next;
1063         while (ptr != &statusHead) {
1064             statusPtr = (statusP) ptr;
1065             if (statusPtr->jobNumber == slot) {
1066                 statusPtr->flags |= ABORT_REQUEST;
1067                 unlock_Status();
1068                 return (0);
1069             }
1070             ptr = ptr->dlq_next;
1071         }
1072         unlock_Status();
1073
1074         fprintf(stderr, "Job %d not found\n", slot);
1075         return (-1);
1076     } else {
1077         /* vol.dump */
1078         td = bc_dumpTasks;
1079         for (i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
1080             if (td->flags & BC_DI_INUSE) {
1081                 /* compute name */
1082                 strcpy(tbuffer, td->volSetName);
1083                 strcat(tbuffer, ".");
1084                 strcat(tbuffer, tailCompPtr(td->dumpName));
1085                 if (strcmp(tbuffer, tp) == 0)
1086                     break;
1087             }
1088         }
1089         if (i >= BC_MAXSIMDUMPS) {
1090             com_err(whoami, 0, "Can't find job %s", tp);
1091             return -1;
1092         }
1093
1094         lock_Status();
1095         statusPtr = findStatus(td->dumpID);
1096
1097         if (statusPtr == 0) {
1098             com_err(whoami, 0, "Can't locate status - internal error");
1099             unlock_Status();
1100             return (-1);
1101         }
1102         statusPtr->flags |= ABORT_REQUEST;
1103         unlock_Status();
1104         return (0);
1105     }
1106     return 0;
1107 }
1108
1109 /* restore a volume or volumes */
1110 bc_VolRestoreCmd(as, arock)
1111      struct cmd_syndesc *as;
1112      char *arock;
1113 {
1114     /*
1115      * parm 0 is the new server to restore to
1116      * parm 1 is the new partition to restore to
1117      * parm 2 is volume(s) to restore
1118      * parm 3 is the new extension, if any, for the volume name.
1119      * parm 4 gives the new volume # to restore this volume as (removed).
1120      * parm 4 date is a string representing the date
1121      *
1122      * We handle four types of restores.  If old is set, then we restore the
1123      * volume with the same name and ID.  If old is not set, we allocate
1124      * a new volume ID for the restored volume.  If a new extension is specified,
1125      * we add that extension to the volume name of the restored volume.
1126      */
1127     struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1128     struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1129     struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1130     struct bc_volumeDump *tvol; /* temp for same */
1131     struct sockaddr_in destServ;        /* machine to which to restore volumes */
1132     afs_int32 destPartition;    /* partition to which to restore volumes */
1133     char *tp;
1134     struct cmd_item *ti;
1135     afs_int32 code;
1136     int oldFlag;
1137     afs_int32 fromDate;
1138     char *newExt, *timeString;
1139     afs_int32 i;
1140     afs_int32 *ports = NULL;
1141     afs_int32 portCount = 0;
1142     int dontExecute;
1143
1144     code = bc_UpdateHosts();
1145     if (code) {
1146         com_err(whoami, code, "; Can't retrieve tape hosts");
1147         return (code);
1148     }
1149
1150     /* specified other destination host */
1151     if (as->parms[0].items) {
1152         tp = as->parms[0].items->data;
1153         if (bc_ParseHost(tp, &destServ)) {
1154             com_err(whoami, 0, "Failed to locate destination host '%s'", tp);
1155             return -1;
1156         }
1157     }
1158
1159     /* specified other destination partition */
1160     if (as->parms[1].items) {
1161         tp = as->parms[1].items->data;
1162         if (bc_GetPartitionID(tp, &destPartition)) {
1163             com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1164             return -1;
1165         }
1166     }
1167
1168     for (ti = as->parms[2].items; ti; ti = ti->next) {
1169         /* build list of volume items */
1170         tvol = (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1171         if (!tvol) {
1172             com_err(whoami, BC_NOMEM, "");
1173             return BC_NOMEM;
1174         }
1175         memset(tvol, 0, sizeof(struct bc_volumeDump));
1176
1177         tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1178         if (!tvol->name) {
1179             com_err(whoami, BC_NOMEM, "");
1180             return BC_NOMEM;
1181         }
1182         strncpy(tvol->name, ti->data, VOLSER_OLDMAXVOLNAME);
1183         tvol->entry = &tvolumeEntry;
1184
1185         if (lastVol)
1186             lastVol->next = tvol;       /* thread onto end of list */
1187         else
1188             volsToRestore = tvol;
1189         lastVol = tvol;
1190     }
1191
1192     if (as->parms[4].items) {
1193         timeString = concatParams(as->parms[4].items);
1194         if (!timeString)
1195             return (-1);
1196
1197         code = ktime_DateToLong(timeString, &fromDate);
1198         free(timeString);
1199         if (code) {
1200             com_err(whoami, 0, "Can't parse restore date and time");
1201             com_err(whoami, 0, "%s", ktime_GetDateUsage());
1202             return code;
1203         }
1204     } else {
1205         fromDate = 0x7fffffff;  /* latest one */
1206     }
1207
1208     newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1209     oldFlag = 0;
1210
1211     /* Read all the port offsets into the ports array. The first element in the
1212      * array is for full restore and the rest are for incremental restores 
1213      */
1214     if (as->parms[5].items) {
1215         for (ti = as->parms[5].items; ti; ti = ti->next)
1216             portCount++;
1217         ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1218         if (!ports) {
1219             com_err(whoami, BC_NOMEM, "");
1220             return BC_NOMEM;
1221         }
1222
1223         for (ti = as->parms[5].items, i = 0; ti; ti = ti->next, i++) {
1224             ports[i] = getPortOffset(ti->data);
1225             if (ports[i] < 0)
1226                 return (BC_BADARG);
1227         }
1228     }
1229
1230     dontExecute = (as->parms[6].items ? 1 : 0); /* -n */
1231
1232     /*
1233      * Perform the call to start the restore.
1234      */
1235     code =
1236         bc_StartDmpRst(bc_globalConfig, "volume", "restore", volsToRestore,
1237                        &destServ, destPartition, fromDate, newExt, oldFlag,
1238                        /*parentDump */ 0, /*dumpLevel */ 0,
1239                        bc_Restorer, ports, portCount,
1240                        /*dumpSched */ 0, /*append */ 0, dontExecute);
1241     if (code)
1242         com_err(whoami, code, "; Failed to queue restore");
1243
1244     return (code);
1245 }
1246
1247 /* restore a whole partition or server */
1248
1249 /* bc_DiskRestoreCmd
1250  *      restore a whole partition
1251  * params:
1252  *      first, reqd - machine (server) to restore
1253  *      second, reqd - partition to restore
1254  *      various optional
1255  */
1256
1257 bc_DiskRestoreCmd(as, arock)
1258      struct cmd_syndesc *as;
1259      char *arock;
1260 {
1261     struct bc_volumeSet tvolumeSet;     /* temporary volume set for EvalVolumeSet call */
1262     struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1263     struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1264     struct sockaddr_in destServ;        /* machine to which to restore volumes */
1265     afs_int32 destPartition;    /* partition to which to restore volumes */
1266     char *tp;
1267     afs_int32 code;
1268     int oldFlag;
1269     afs_int32 fromDate;
1270     char *newExt;
1271     afs_int32 *ports = NULL;
1272     afs_int32 portCount = 0;
1273     int dontExecute;
1274     struct bc_volumeDump *prev, *tvol, *nextvol;
1275     struct cmd_item *ti;
1276     afs_int32 i;
1277
1278     /* parm 0 is the server to restore
1279      * parm 1 is the partition to restore
1280      
1281      * parm 8 and above as in VolRestoreCmd:
1282      * parm 8 is the new server to restore to
1283      * parm 9 is the new partition to restore to
1284      */
1285
1286     code = bc_UpdateVolumeSet();
1287     if (code) {
1288         com_err(whoami, code, "; Can't retrieve volume sets");
1289         return (code);
1290     }
1291     code = bc_UpdateHosts();
1292     if (code) {
1293         com_err(whoami, code, "; Can't retrieve tape hosts");
1294         return (code);
1295     }
1296
1297     /* create a volume set corresponding to the volume pattern we've been given */
1298     memset(&tvolumeSet, 0, sizeof(tvolumeSet));
1299     memset(&tvolumeEntry, 0, sizeof(tvolumeEntry));
1300     tvolumeSet.name = "TempVolumeSet";
1301     tvolumeSet.ventries = &tvolumeEntry;
1302     tvolumeEntry.serverName = as->parms[0].items->data;
1303     tvolumeEntry.partname = as->parms[1].items->data;
1304
1305     if (bc_GetPartitionID(tvolumeEntry.partname, &tvolumeEntry.partition)) {
1306         com_err(whoami, 0, "Can't parse partition '%s'",
1307                 tvolumeEntry.partname);
1308         return -1;
1309     }
1310
1311     if (bc_ParseHost(tvolumeEntry.serverName, &tvolumeEntry.server)) {
1312         com_err(whoami, 0, "Can't locate host '%s'", tvolumeEntry.serverName);
1313         return -1;
1314     }
1315
1316     /* specified other destination host */
1317     if (as->parms[8].items) {
1318         tp = as->parms[8].items->data;
1319         if (bc_ParseHost(tp, &destServ)) {
1320             com_err(whoami, 0, "Can't locate destination host '%s'", tp);
1321             return -1;
1322         }
1323     } else                      /* use destination host == original host */
1324         memcpy(&destServ, &tvolumeEntry.server, sizeof(destServ));
1325
1326     /* specified other destination partition */
1327     if (as->parms[9].items) {
1328         tp = as->parms[9].items->data;
1329         if (bc_GetPartitionID(tp, &destPartition)) {
1330             com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1331             return -1;
1332         }
1333     } else                      /* use original partition */
1334         destPartition = tvolumeEntry.partition;
1335
1336     tvolumeEntry.name = ".*";   /* match all volumes (this should be a parameter) */
1337
1338     if (as->parms[2].items) {
1339         for (ti = as->parms[2].items; ti; ti = ti->next)
1340             portCount++;
1341         ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1342         if (!ports) {
1343             com_err(whoami, BC_NOMEM, "");
1344             return BC_NOMEM;
1345         }
1346
1347         for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1348             ports[i] = getPortOffset(ti->data);
1349             if (ports[i] < 0)
1350                 return (BC_BADARG);
1351         }
1352     }
1353
1354     newExt = (as->parms[10].items ? as->parms[10].items->data : NULL);
1355     dontExecute = (as->parms[11].items ? 1 : 0);        /* -n */
1356
1357     /*
1358      * Expand out the volume set into its component list of volumes, by calling VLDB server.
1359      */
1360     code =
1361         bc_EvalVolumeSet(bc_globalConfig, &tvolumeSet, &volsToRestore,
1362                          cstruct);
1363     if (code) {
1364         com_err(whoami, code, "; Failed to evaluate volume set");
1365         return (-1);
1366     }
1367
1368     /* Since we want only RW volumes, remove any 
1369      * BK or RO volumes from the list.
1370      */
1371     for (prev = 0, tvol = volsToRestore; tvol; tvol = nextvol) {
1372         nextvol = tvol->next;
1373         if (BackupName(tvol->name)) {
1374             if (tvol->name)
1375                 fprintf(stderr,
1376                         "Will not restore volume %s (its RW does not reside on the partition)\n",
1377                         tvol->name);
1378
1379             if (tvol == volsToRestore) {
1380                 volsToRestore = nextvol;
1381             } else {
1382                 prev->next = nextvol;
1383             }
1384             if (tvol->name)
1385                 free(tvol->name);
1386             free(tvol);
1387         } else {
1388             prev = tvol;
1389         }
1390     }
1391
1392     fromDate = 0x7fffffff;      /* last one before this date */
1393     oldFlag = 1;                /* do restore same volid (and name) */
1394
1395     /*
1396      * Perform the call to start the dump.
1397      */
1398     code =
1399         bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1400                        &destServ, destPartition, fromDate, newExt, oldFlag,
1401                        /*parentDump */ 0, /*dumpLevel */ 0,
1402                        bc_Restorer, ports, portCount,
1403                        /*dumpSched */ 0, /*append */ 0, dontExecute);
1404     if (code)
1405         com_err(whoami, code, "; Failed to queue restore");
1406
1407     return (code);
1408 }
1409
1410 /* bc_VolsetRestoreCmd
1411  *      restore a volumeset or list of volumes.
1412  */
1413
1414 bc_VolsetRestoreCmd(as, arock)
1415      struct cmd_syndesc *as;
1416      char *arock;
1417 {
1418     int oldFlag;
1419     long fromDate;
1420     char *newExt;
1421
1422     int dontExecute;
1423     afs_int32 *ports = NULL;
1424     afs_int32 portCount = 0;
1425     afs_int32 code = 0;
1426     afs_int32 portoffset = 0;
1427     char *volsetName;
1428     struct bc_volumeSet *volsetPtr;     /* Ptr to list of generated volume info */
1429     struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1430     struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1431     struct sockaddr_in destServer;      /* machine to which to restore volume */
1432     afs_int32 destPartition;    /* partition to which to restore volumes */
1433     struct cmd_item *ti;
1434     afs_int32 i;
1435
1436     code = bc_UpdateVolumeSet();
1437     if (code) {
1438         com_err(whoami, code, "; Can't retrieve volume sets");
1439         return (code);
1440     }
1441     code = bc_UpdateHosts();
1442     if (code) {
1443         com_err(whoami, code, "; Can't retrieve tape hosts");
1444         return (code);
1445     }
1446
1447     if (as->parms[0].items) {
1448         if (as->parms[1].items) {
1449             com_err(whoami, 0, "Can't have both -name and -file options");
1450             return (-1);
1451         }
1452
1453         volsetName = as->parms[0].items->data;
1454         volsetPtr = bc_FindVolumeSet(bc_globalConfig, volsetName);
1455         if (!volsetPtr) {
1456             com_err(whoami, 0,
1457                     "Can't find volume set '%s' in backup database",
1458                     volsetName);
1459             return (-1);
1460         }
1461
1462         /* Expand out the volume set into its component list of volumes. */
1463         code =
1464             bc_EvalVolumeSet(bc_globalConfig, volsetPtr, &volsToRestore,
1465                              cstruct);
1466         if (code) {
1467             com_err(whoami, code, "; Failed to evaluate volume set");
1468             return (-1);
1469         }
1470     } else if (as->parms[1].items) {
1471         FILE *fd;
1472         char line[256];
1473         char server[50], partition[50], volume[50], rest[256];
1474         long count;
1475         struct bc_volumeDump *tvol;
1476
1477         fd = fopen(as->parms[1].items->data, "r");
1478         if (!fd) {
1479             com_err(whoami, errno, "; Cannot open file '%s'",
1480                     as->parms[1].items->data);
1481             return (-1);
1482         }
1483
1484         while (fgets(line, 255, fd)) {
1485             count =
1486                 sscanf(line, "%s %s %s %s", server, partition, volume, rest);
1487
1488             if (count <= 0)
1489                 continue;
1490             if (count < 3) {
1491                 fprintf(stderr,
1492                         "Invalid volumeset file format: Wrong number of arguments: Ignoring\n");
1493                 fprintf(stderr, "     %s", line);
1494                 continue;
1495             }
1496
1497             if (bc_ParseHost(server, &destServer)) {
1498                 com_err(whoami, 0, "Failed to locate host '%s'", server);
1499                 continue;
1500             }
1501
1502             if (bc_GetPartitionID(partition, &destPartition)) {
1503                 com_err(whoami, 0,
1504                         "Failed to parse destination partition '%s'",
1505                         partition);
1506                 continue;
1507             }
1508
1509             /* Allocate a volumeDump structure and link it in */
1510             tvol =
1511                 (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1512             memset(tvol, 0, sizeof(struct bc_volumeDump));
1513
1514             tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1515             if (!tvol->name) {
1516                 com_err(whoami, BC_NOMEM, "");
1517                 return BC_NOMEM;
1518             }
1519             strncpy(tvol->name, volume, VOLSER_OLDMAXVOLNAME);
1520             memcpy(&tvol->server, &destServer, sizeof(destServer));
1521             tvol->partition = destPartition;
1522
1523             if (lastVol)
1524                 lastVol->next = tvol;   /* thread onto end of list */
1525             else
1526                 volsToRestore = tvol;
1527             lastVol = tvol;
1528         }
1529         fclose(fd);
1530     } else {
1531         com_err(whoami, 0, "-name or -file option required");
1532         return (-1);
1533     }
1534
1535
1536     /* Get the port offset for the restore */
1537     if (as->parms[2].items) {
1538         for (ti = as->parms[2].items; ti; ti = ti->next)
1539             portCount++;
1540         ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1541         if (!ports) {
1542             com_err(whoami, BC_NOMEM, "");
1543             return BC_NOMEM;
1544         }
1545
1546         for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1547             ports[i] = getPortOffset(ti->data);
1548             if (ports[i] < 0)
1549                 return (BC_BADARG);
1550         }
1551     }
1552
1553     newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1554     dontExecute = (as->parms[4].items ? 1 : 0);
1555
1556     fromDate = 0x7fffffff;      /* last one before this date */
1557     oldFlag = 1;                /* do restore same volid (and name) */
1558
1559     /* Perform the call to start the restore */
1560     code = bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1561                           /*destserver */ 0, /*destpartition */ 0, fromDate,
1562                           newExt, oldFlag,
1563                           /*parentDump */ 0, /*dumpLevel */ 0,
1564                           bc_Restorer, ports, portCount,
1565                           /*dumpSched */ 0, /*append */ 0, dontExecute);
1566     if (code)
1567         com_err(whoami, code, "; Failed to queue restore");
1568
1569     return code;
1570 }
1571
1572 /*-----------------------------------------------------------------------------
1573  * bc_DumpCmd
1574  *
1575  * Description:
1576  *      Perform a dump of the set of volumes provided.
1577  *      user specifies: -volumeset .. -dump .. [-portoffset ..] [-n]
1578  *
1579  * Arguments:
1580  *      as      : Parsed command line information.
1581  *      arock   : Ptr to misc stuff; not used.
1582  *
1583  * Returns:
1584  *      -1 on errors,
1585  *      The result of bc_StartDump() otherwise
1586  *
1587  * Environment:
1588  *      Nothing special.
1589  *
1590  * Side Effects:
1591  *      As advertised.
1592  *---------------------------------------------------------------------------
1593  */
1594 int dontExecute;
1595
1596 int
1597 bc_DumpCmd(as, arock)
1598      struct cmd_syndesc *as;
1599      char *arock;
1600 {                               /*bc_DumpCmd */
1601     static char rn[] = "bc_DumpCmd";    /*Routine name */
1602     char *dumpPath, *vsName;    /*Ptrs to various names */
1603     struct bc_volumeSet *tvs;   /*Ptr to list of generated volume info */
1604     struct bc_dumpSchedule *tds, *baseds;       /*Ptr to dump schedule node */
1605     struct bc_volumeDump *tve, *volsToDump;     /*Ptr to individual vols to be dumped */
1606     struct budb_dumpEntry dumpEntry, de, fde;   /* dump entry */
1607     afs_uint32 d;
1608
1609     afs_int32 parent;           /* parent dump */
1610     afs_int32 level;            /* this dump's level # */
1611     afs_int32 problemFindingDump;       /* can't find parent(s) */
1612
1613     afs_int32 *portp = NULL;
1614     afs_int32 portCount = 0;
1615     afs_int32 doAt, atTime;     /* Time a timed-dump is to start at */
1616     afs_int32 length;
1617     char *timeString;
1618     int doAppend;               /* Append the dump to dump set */
1619     afs_int32 code;             /* Return code */
1620     int loadfile;               /* whether to load a file or not */
1621
1622     statusP statusPtr;
1623
1624     extern struct bc_dumpTask bc_dumpTasks[];
1625     extern afs_int32 bcdb_FindLastVolClone();
1626     extern afs_int32 volImageTime();
1627
1628     code = bc_UpdateDumpSchedule();
1629     if (code) {
1630         com_err(whoami, code, "; Can't retrieve dump schedule");
1631         return (code);
1632     }
1633     code = bc_UpdateVolumeSet();
1634     if (code) {
1635         com_err(whoami, code, "; Can't retrieve volume sets");
1636         return (code);
1637     }
1638     code = bc_UpdateHosts();
1639     if (code) {
1640         com_err(whoami, code, "; Can't retrieve tape hosts");
1641         return (code);
1642     }
1643
1644     /* 
1645      * Some parameters cannot be specified together
1646      * The "-file" option cannot exist with the "-volume", "-dump", 
1647      * "-portoffset", or "-append" option 
1648      */
1649     if (as->parms[6].items) {
1650         loadfile = 1;
1651         if (as->parms[0].items || as->parms[1].items || as->parms[2].items
1652             || as->parms[4].items) {
1653             com_err(whoami, 0, "Invalid option specified with -file option");
1654             return -1;
1655         }
1656     } else {
1657         loadfile = 0;
1658         if (!as->parms[0].items || !as->parms[1].items) {
1659             com_err(whoami, 0,
1660                     "Must specify volume set name and dump level name");
1661             return -1;
1662         }
1663     }
1664
1665     /* 
1666      * Get the time we are to perform this dump
1667      */
1668     if (as->parms[3].items) {
1669         doAt = 1;
1670
1671         timeString = concatParams(as->parms[3].items);
1672         if (!timeString)
1673             return (-1);
1674
1675         /*
1676          * Now parse this string for the time to start.
1677          */
1678         code = ktime_DateToLong(timeString, &atTime);
1679         free(timeString);
1680         if (code) {
1681             com_err(whoami, 0, "Can't parse dump start date and time");
1682             com_err(whoami, 0, "%s", ktime_GetDateUsage());
1683             return (1);
1684         }
1685     } else
1686         doAt = 0;
1687
1688     dontExecute = (as->parms[5].items ? 1 : 0); /* -n */
1689
1690     /* 
1691      * If this dump is not a load file, then check the parameters.
1692      */
1693     if (!loadfile) {            /*6 */
1694         vsName = as->parms[0].items->data;      /* get volume set name */
1695         dumpPath = as->parms[1].items->data;    /* get dump path */
1696
1697         /* get the port number, if one was specified */
1698         if (as->parms[2].items) {
1699             portCount = 1;
1700             portp = (afs_int32 *) malloc(sizeof(afs_int32));
1701             if (!portp) {
1702                 com_err(whoami, BC_NOMEM, "");
1703                 return BC_NOMEM;
1704             }
1705
1706             *portp = getPortOffset(as->parms[2].items->data);
1707             if (*portp < 0)
1708                 return (BC_BADARG);
1709         }
1710
1711         doAppend = (as->parms[4].items ? 1 : 0);        /* -append */
1712
1713         /*
1714          * Get a hold of the given volume set and dump set.
1715          */
1716         tvs = bc_FindVolumeSet(bc_globalConfig, vsName);
1717         if (!tvs) {
1718             com_err(whoami, 0,
1719                     "Can't find volume set '%s' in backup database", vsName);
1720             return (-1);
1721         }
1722         baseds = bc_FindDumpSchedule(bc_globalConfig, dumpPath);
1723         if (!baseds) {
1724             com_err(whoami, 0,
1725                     "Can't find dump schedule '%s' in backup database",
1726                     dumpPath);
1727             return (-1);
1728         }
1729
1730     }
1731
1732     /*6 */
1733     /*
1734      * If given the "-at" option, then add this to the jobs list and return 
1735      * with no error.
1736      *
1737      * Create a status node for this timed dump.
1738      * Fill in the time to dump and the cmd line for the dump leaving off
1739      * the -at option.  If the -n option is there, it is scheduled with 
1740      * the Timed dump as opposed to not scheduling the time dump at all.
1741      */
1742     if (doAt) {
1743         if (atTime < time(0)) {
1744             com_err(whoami, 0,
1745                     "Time of dump is earlier then current time - not added");
1746         } else {
1747             statusPtr = createStatusNode();
1748             lock_Status();
1749             statusPtr->scheduledDump = atTime;
1750
1751             /* Determine length of the dump command */
1752             length = 0;
1753             length += 4;        /* "dump" */
1754             if (loadfile) {
1755                 length += 6;    /* " -file" */
1756                 length += 1 + strlen(as->parms[6].items->data); /* " <file>" */
1757             } else {
1758                 /* length += 11; *//* " -volumeset" */
1759                 length += 1 + strlen(as->parms[0].items->data); /* " <volset> */
1760
1761                 /* length += 6; *//* " -dump" */
1762                 length += 1 + strlen(as->parms[1].items->data); /* " <dumpset> */
1763
1764                 if (as->parms[2].items) {
1765                     /* length += 12; *//* " -portoffset" */
1766                     length += 1 + strlen(as->parms[2].items->data);     /* " <port>" */
1767                 }
1768
1769                 if (as->parms[4].items)
1770                     length += 8;        /* " -append" */
1771             }
1772
1773             if (dontExecute)
1774                 length += 3;    /* " -n" */
1775             length++;           /* end-of-line */
1776
1777             /* Allocate status block for this timed dump */
1778             sprintf(statusPtr->taskName, "Scheduled Dump");
1779             statusPtr->jobNumber = bc_jobNumber();
1780             statusPtr->scheduledDump = atTime;
1781             statusPtr->cmdLine = (char *)malloc(length);
1782             if (!statusPtr->cmdLine) {
1783                 com_err(whoami, BC_NOMEM, "");
1784                 return BC_NOMEM;
1785             }
1786
1787             /* Now reconstruct the dump command */
1788             statusPtr->cmdLine[0] = 0;
1789             strcat(statusPtr->cmdLine, "dump");
1790             if (loadfile) {
1791                 strcat(statusPtr->cmdLine, " -file");
1792                 strcat(statusPtr->cmdLine, " ");
1793                 strcat(statusPtr->cmdLine, as->parms[6].items->data);
1794             } else {
1795                 /* strcat(statusPtr->cmdLine, " -volumeset"); */
1796                 strcat(statusPtr->cmdLine, " ");
1797                 strcat(statusPtr->cmdLine, as->parms[0].items->data);
1798
1799                 /* strcat(statusPtr->cmdLine, " -dump"); */
1800                 strcat(statusPtr->cmdLine, " ");
1801                 strcat(statusPtr->cmdLine, as->parms[1].items->data);
1802
1803                 if (as->parms[2].items) {
1804                     /* strcat(statusPtr->cmdLine, " -portoffset"); */
1805                     strcat(statusPtr->cmdLine, " ");
1806                     strcat(statusPtr->cmdLine, as->parms[2].items->data);
1807                 }
1808
1809                 if (as->parms[4].items)
1810                     strcat(statusPtr->cmdLine, " -append");
1811             }
1812             if (dontExecute)
1813                 strcat(statusPtr->cmdLine, " -n");
1814
1815             printf("Add scheduled dump as job %d\n", statusPtr->jobNumber);
1816             if ((atTime > ttoken.endTime) && (ttoken.endTime != NEVERDATE))
1817                 com_err(whoami, 0,
1818                         "Warning: job %d starts after expiration of AFS token",
1819                         statusPtr->jobNumber);
1820
1821             unlock_Status();
1822         }
1823
1824         return (0);
1825     }
1826
1827     /* 
1828      * Read and execute the load file if specified.  The work of reading is done
1829      * in the main routine prior the dispatch call. loadFile and dontExecute are
1830      * global variables so this can take place in main.
1831      */
1832     if (loadfile) {
1833         loadFile = (char *)malloc(strlen(as->parms[6].items->data) + 1);
1834         if (!loadFile) {
1835             com_err(whoami, BC_NOMEM, "");
1836             return BC_NOMEM;
1837         }
1838         strcpy(loadFile, as->parms[6].items->data);
1839         return 0;
1840     }
1841
1842     /* 
1843      * We are doing a real dump (no load file or timed dump).
1844      */
1845     printf("Starting dump of volume set '%s' (dump level '%s')\n", vsName,
1846            dumpPath);
1847
1848     /* For each dump-level above this one, see if the volumeset was dumped
1849      * at the level. We search all dump-levels since a higher dump-level
1850      * may have actually been done AFTER a lower dump level.
1851      */
1852     parent = level = problemFindingDump = 0;
1853     for (tds = baseds->parent; tds; tds = tds->parent) {
1854         /* Find the most recent dump of the volume-set at this dump-level.
1855          * Raise problem flag if didn't find a dump and a parent not yet found.
1856          */
1857         code = bcdb_FindLatestDump(vsName, tds->name, &dumpEntry);
1858         if (code) {
1859             if (!parent)
1860                 problemFindingDump = 1; /* skipping a dump level */
1861             continue;
1862         }
1863
1864         /* We found the most recent dump at this level. Now check
1865          * if we should use it by seeing if its full dump hierarchy 
1866          * exists. If it doesn't, we don't want to base our incremental
1867          * off of this dump. 
1868          */
1869         if (!parent || (dumpEntry.id > parent)) {
1870             /* Follow the parent dumps to see if they are all there */
1871             for (d = dumpEntry.parent; d; d = de.parent) {
1872                 code = bcdb_FindDumpByID(d, &de);
1873                 if (code)
1874                     break;
1875             }
1876
1877             /* If we found the entire level, remember it. Otherwise raise flag.
1878              * If we had already found a dump, raise the problem flag.
1879              */
1880             if (!d && !code) {
1881                 if (parent)
1882                     problemFindingDump = 1;
1883                 parent = dumpEntry.id;
1884                 level = dumpEntry.level + 1;
1885                 memcpy(&fde, &dumpEntry, sizeof(dumpEntry));
1886             } else {
1887                 /* Dump hierarchy not complete so can't base off the latest */
1888                 problemFindingDump = 1;
1889             }
1890         }
1891     }
1892
1893     /* If the problemflag was raise, it means we are not doing the 
1894      * dump at the level we requested it be done at.
1895      */
1896     if (problemFindingDump) {
1897         com_err(whoami, 0,
1898                 "Warning: Doing level %d dump due to missing higher-level dumps",
1899                 level);
1900         if (parent) {
1901             printf("Parent dump: dump %s (DumpID %u)\n", fde.name, parent);
1902         }
1903     } else if (parent) {
1904         printf("Found parent: dump %s (DumpID %u)\n", fde.name, parent);
1905     }
1906
1907     /* Expand out the volume set into its component list of volumes. */
1908     code = bc_EvalVolumeSet(bc_globalConfig, tvs, &volsToDump, cstruct);
1909     if (code) {
1910         com_err(whoami, code, "; Failed to evaluate volume set");
1911         return (-1);
1912     }
1913     if (!volsToDump) {
1914         printf("No volumes to dump\n");
1915         return (0);
1916     }
1917
1918     /* Determine what the clone time of the volume was when it was
1919      * last dumped (tve->date). This is the time from when an
1920      * incremental should be done (remains zero if a full dump).
1921      */
1922     if (parent) {
1923         for (tve = volsToDump; tve; tve = tve->next) {
1924             code = bcdb_FindClone(parent, tve->name, &tve->date);
1925             if (code)
1926                 tve->date = 0;
1927
1928             /* Get the time the volume was last cloned and see if the volume has
1929              * changed since then. Only do this when the "-n" flag is specified
1930              * because butc will get the cloneDate at time of dump.
1931              */
1932             if (dontExecute) {
1933                 code =
1934                     volImageTime(tve->server.sin_addr.s_addr, tve->partition,
1935                                  tve->vid, tve->volType, &tve->cloneDate);
1936                 if (code)
1937                     tve->cloneDate = 0;
1938
1939                 if (tve->cloneDate && (tve->cloneDate == tve->date)) {
1940                     com_err(whoami, 0,
1941                             "Warning: Timestamp on volume %s unchanged from previous dump",
1942                             tve->name);
1943                 }
1944             }
1945         }
1946     }
1947
1948     if (dontExecute)
1949         printf("Would have dumped the following volumes:\n");
1950     else
1951         printf("Preparing to dump the following volumes:\n");
1952     for (tve = volsToDump; tve; tve = tve->next) {
1953         printf("\t%s (%u)\n", tve->name, tve->vid);
1954     }
1955     if (dontExecute)
1956         return (0);
1957
1958     code = bc_StartDmpRst(bc_globalConfig, dumpPath, vsName, volsToDump,
1959                           /*destServer */ 0, /*destPartition */ 0,
1960                           /*fromDate */ 0,
1961                           /*newExt */ 0, /*oldFlag */ 0,
1962                           parent, level, bc_Dumper, portp, /*portCount */ 1,
1963                           baseds, doAppend, dontExecute);
1964     if (code)
1965         com_err(whoami, code, "; Failed to queue dump");
1966
1967     return (code);
1968 }                               /*bc_DumpCmd */
1969
1970
1971 /* bc_QuitCmd
1972  *      terminate the backup process. Insists that that all running backup
1973  *      jobs be terminated before it will quit
1974  * parameters:
1975  *      ignored
1976  */
1977
1978 bc_QuitCmd(as, arock)
1979      struct cmd_syndesc *as;
1980      char *arock;
1981 {
1982     int i;
1983     struct bc_dumpTask *td;
1984     extern dlqlinkT statusHead;
1985     dlqlinkP ptr;
1986     statusP statusPtr;
1987
1988     /* Check the status list for outstanding jobs */
1989     lock_Status();
1990     for (ptr = (&statusHead)->dlq_next; ptr != &statusHead;
1991          ptr = ptr->dlq_next) {
1992         statusPtr = (statusP) ptr;
1993         if (!(statusPtr->flags & ABORT_REQUEST)) {
1994             unlock_Status();
1995             com_err(whoami, 0, "Job %d still running (and not aborted)",
1996                     statusPtr->jobNumber);
1997             com_err(whoami, 0,
1998                     "You must at least 'kill' all running jobs before quitting");
1999             return -1;
2000         }
2001     }
2002     unlock_Status();
2003
2004     /* A job still being initialized (but no status structure or job number since it
2005      * has not been handed to a butc process yet)
2006      */
2007     for (td = bc_dumpTasks, i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
2008         if (td->flags & BC_DI_INUSE) {
2009             com_err(whoami, 0, "A job is still running");
2010             com_err(whoami, 0,
2011                     "You must at least 'kill' all running jobs before quitting");
2012             return -1;
2013         }
2014     }
2015
2016 #ifdef AFS_NT40_ENV
2017     /* close the all temp text files before quitting */
2018     for (i = 0; i < TB_NUM; i++)
2019         bc_closeTextFile(&bc_globalConfig->configText[i],
2020                          &bc_globalConfig->tmpTextFileNames[i][0]);
2021 #endif
2022     exit(lastTaskCode);
2023 }
2024
2025 /* bc_LabelTapeCmd
2026  *      Labels a tape i.e. request the tape coordinator to perform this
2027  *      operation
2028  */
2029
2030 bc_LabelTapeCmd(as, arock)
2031      struct cmd_syndesc *as;
2032      char *arock;
2033 {
2034     char *tapename = 0, *pname = 0;
2035     afs_int32 size;
2036     afs_int32 code;
2037     afs_int32 port = 0;
2038
2039     code = bc_UpdateHosts();
2040     if (code) {
2041         com_err(whoami, code, "; Can't retrieve tape hosts");
2042         return (code);
2043     }
2044
2045     if (as->parms[0].items) {   /* -name */
2046         tapename = as->parms[0].items->data;
2047         if (strlen(tapename) >= TC_MAXTAPELEN) {
2048             com_err(whoami, 0, "AFS tape name '%s' is too long", tapename);
2049             return -1;
2050         }
2051     }
2052
2053     if (as->parms[3].items) {   /* -pname */
2054         if (tapename) {
2055             com_err(whoami, 0, "Can only specify -name or -pname");
2056             return -1;
2057         }
2058         pname = as->parms[3].items->data;
2059         if (strlen(pname) >= TC_MAXTAPELEN) {
2060             com_err(whoami, 0, "Permanent tape name '%s' is too long", pname);
2061             return -1;
2062         }
2063     }
2064
2065     if (as->parms[1].items) {
2066         size = bc_FloatATOI(as->parms[1].items->data);
2067         if (size == -1) {
2068             com_err(whoami, 0, "Bad syntax for tape size '%s'",
2069                     as->parms[1].items->data);
2070             return -1;
2071         }
2072     } else
2073         size = 0;
2074
2075     if (as->parms[2].items) {
2076         port = getPortOffset(as->parms[2].items->data);
2077         if (port < 0)
2078             return (BC_BADARG);
2079     }
2080
2081     code = bc_LabelTape(tapename, pname, size, bc_globalConfig, port);
2082     if (code)
2083         return code;
2084     return 0;
2085 }
2086
2087 /* bc_ReadLabelCmd
2088  *      read the label on a tape
2089  * params:
2090  *      optional port number
2091  */
2092
2093 bc_ReadLabelCmd(as, arock)
2094      struct cmd_syndesc *as;
2095      char *arock;
2096 {
2097     afs_int32 code;
2098     afs_int32 port = 0;
2099
2100     code = bc_UpdateHosts();
2101     if (code) {
2102         com_err(whoami, code, "; Can't retrieve tape hosts");
2103         return (code);
2104     }
2105
2106     if (as->parms[0].items) {
2107         port = getPortOffset(as->parms[0].items->data);
2108         if (port < 0)
2109             return (BC_BADARG);
2110     }
2111
2112     code = bc_ReadLabel(bc_globalConfig, port);
2113     if (code)
2114         return code;
2115     return 0;
2116 }
2117
2118 /* bc_ScanDumpsCmd
2119  *      read content information from dump tapes, and if user desires,
2120  *      add it to the database
2121  */
2122
2123 bc_ScanDumpsCmd(as, arock)
2124      struct cmd_syndesc *as;
2125      char *arock;
2126 {
2127     afs_int32 port = 0;
2128     afs_int32 dbAddFlag = 0;
2129     afs_int32 code;
2130
2131     code = bc_UpdateHosts();
2132     if (code) {
2133         com_err(whoami, code, "; Can't retrieve tape hosts");
2134         return (code);
2135     }
2136
2137     /* check for flag */
2138     if (as->parms[0].items != 0) {      /* add scan to database */
2139         dbAddFlag++;
2140     }
2141
2142     /* check for port */
2143     if (as->parms[1].items) {
2144         port = getPortOffset(as->parms[1].items->data);
2145         if (port < 0)
2146             return (BC_BADARG);
2147     }
2148
2149     code = bc_ScanDumps(bc_globalConfig, dbAddFlag, port);
2150     return (code);
2151 }
2152
2153 /* bc_ParseExpiration
2154  *
2155  * Notes:
2156  *      dates are specified as absolute or relative, the syntax is:
2157  *      absolute:       at %d/%d/%d [%d:%d]     where [..] is optional
2158  *      relative:       in [%dy][%dm][%dd]      where at least one component
2159  *                                              must be specified
2160  */
2161
2162 afs_int32
2163 bc_ParseExpiration(paramPtr, expType, expDate)
2164      struct cmd_parmdesc *paramPtr;
2165      afs_int32 *expType;
2166      afs_int32 *expDate;
2167 {
2168     struct cmd_item *itemPtr;
2169     struct ktime_date kt;
2170     char *dateString = 0;
2171     afs_int32 code = 0;
2172
2173     *expType = BC_NO_EXPDATE;
2174     *expDate = 0;
2175
2176     if (!paramPtr->items)
2177         ERROR(0);               /* no expiration specified */
2178
2179     /* some form of expiration date specified. First validate the prefix */
2180     itemPtr = paramPtr->items;
2181
2182     if (strcmp(itemPtr->data, "at") == 0) {
2183         *expType = BC_ABS_EXPDATE;
2184
2185         dateString = concatParams(itemPtr->next);
2186         if (!dateString)
2187             ERROR(1);
2188
2189         code = ktime_DateToLong(dateString, expDate);
2190         if (code)
2191             ERROR(1);
2192     } else if (strcmp(itemPtr->data, "in") == 0) {
2193         *expType = BC_REL_EXPDATE;
2194
2195         dateString = concatParams(itemPtr->next);
2196         if (!dateString)
2197             ERROR(1);
2198
2199         code = ParseRelDate(dateString, &kt);
2200         if (code)
2201             ERROR(1);
2202         *expDate = ktimeRelDate_ToLong(&kt);
2203     } else {
2204         dateString = concatParams(itemPtr);
2205         if (!dateString)
2206             ERROR(1);
2207
2208         if (ktime_DateToLong(dateString, expDate) == 0) {
2209             *expType = BC_ABS_EXPDATE;
2210             code = ktime_DateToLong(dateString, expDate);
2211             if (code)
2212                 ERROR(1);
2213         } else if (ParseRelDate(dateString, &kt) == 0) {
2214             *expType = BC_REL_EXPDATE;
2215             *expDate = ktimeRelDate_ToLong(&kt);
2216         } else {
2217             ERROR(1);
2218         }
2219     }
2220
2221   error_exit:
2222     if (dateString)
2223         free(dateString);
2224     return (code);
2225 }
2226
2227 /* database lookup command and routines */
2228
2229 /* bc_dblookupCmd
2230  *      Currently a single option, volumename to search for. Reports
2231  *      all dumps containing the specified volume
2232  */
2233
2234 bc_dblookupCmd(as, arock)
2235      struct cmd_syndesc *as;
2236      char *arock;
2237 {
2238     struct cmd_item *ciptr;
2239     afs_int32 code;
2240
2241     ciptr = as->parms[0].items;
2242     if (ciptr == 0)             /* no argument specified */
2243         return (-1);
2244
2245     code = DBLookupByVolume(ciptr->data);
2246     return (code);
2247 }
2248
2249
2250
2251 /* for ubik version */
2252
2253 bc_dbVerifyCmd(as, arock)
2254      struct cmd_syndesc *as;
2255      char *arock;
2256 {
2257     afs_int32 status;
2258     afs_int32 orphans;
2259     afs_int32 host;
2260
2261     struct hostent *hostPtr;
2262     int detail;
2263     afs_int32 code = 0;
2264
2265     extern struct udbHandleS udbHandle;
2266
2267     detail = (as->parms[0].items ? 1 : 0);      /* print more details */
2268
2269     code =
2270         ubik_Call(BUDB_DbVerify, udbHandle.uh_client, 0, &status, &orphans,
2271                   &host);
2272
2273     if (code) {
2274         com_err(whoami, code, "; Unable to verify database");
2275         return (-1);
2276     }
2277
2278     /* verification call succeeded */
2279
2280     if (status == 0)
2281         printf("Database OK\n");
2282     else
2283         com_err(whoami, status, "; Database is NOT_OK");
2284
2285     if (detail) {
2286         printf("Orphan blocks %d\n", orphans);
2287
2288         if (!host)
2289             printf("Unable to lookup host id\n");
2290         else {
2291             hostPtr = gethostbyaddr((char *)&host, sizeof(host), AF_INET);
2292             if (hostPtr == 0)
2293                 printf("Database checker was %d.%d.%d.%d\n",
2294                        ((host & 0xFF000000) >> 24), ((host & 0xFF0000) >> 16),
2295                        ((host & 0xFF00) >> 8), (host & 0xFF));
2296             else
2297                 printf("Database checker was %s\n", hostPtr->h_name);
2298         }
2299     }
2300     return ((status ? -1 : 0));
2301 }
2302
2303 /* deleteDump:
2304  * Delete a dump. If port is >= 0, it means try to delete from XBSA server
2305  */
2306 deleteDump(dumpid, port, force)
2307      afs_uint32 dumpid;         /* The dumpid to delete */
2308      afs_int32 port;            /* port==-1 means don't go to butc */
2309      afs_int32 force;
2310 {
2311     afs_int32 code = 0, tcode;
2312     struct budb_dumpEntry dumpEntry;
2313     struct rx_connection *tconn = 0;
2314     afs_int32 i, taskflag, xbsadump;
2315     statusP statusPtr = 0;
2316     budb_dumpsList dumps;
2317     afs_uint32 taskId;
2318
2319     /* If the port is set, we will try to send a delete request to the butc */
2320     if (port >= 0) {
2321         tcode = bc_UpdateHosts();
2322         if (tcode) {
2323             com_err(whoami, tcode, "; Can't retrieve tape hosts");
2324             ERROR(tcode);
2325         }
2326
2327         /* Find the dump in the backup database */
2328         tcode = bcdb_FindDumpByID(dumpid, &dumpEntry);
2329         if (tcode) {
2330             com_err(whoami, tcode, "; Unable to locate dumpID %u in database",
2331                     dumpid);
2332             ERROR(tcode);
2333         }
2334         xbsadump = (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA));
2335
2336         /* If dump is to an XBSA server, connect to butc and send it
2337          * the dump to delete. Butc will contact the XBSA server.
2338          * The dump will not be an appended dump because XBSA butc 
2339          * does not support the append option.
2340          */
2341         if (xbsadump && dumpEntry.nVolumes) {
2342             tcode = ConnectButc(bc_globalConfig, port, &tconn);
2343             if (tcode)
2344                 ERROR(tcode);
2345
2346             tcode = TC_DeleteDump(tconn, dumpid, &taskId);
2347             if (tcode) {
2348                 if (tcode == RXGEN_OPCODE)
2349                     tcode = BC_VERSIONFAIL;
2350                 com_err(whoami, tcode,
2351                         "; Unable to delete dumpID %u via butc", dumpid);
2352                 ERROR(tcode);
2353             }
2354
2355             statusPtr = createStatusNode();
2356             lock_Status();
2357             statusPtr->taskId = taskId;
2358             statusPtr->port = port;
2359             statusPtr->jobNumber = bc_jobNumber();
2360             statusPtr->flags |= (SILENT | NOREMOVE);    /* No msg & keep statusPtr */
2361             statusPtr->flags &= ~STARTING;      /* clearstatus to examine */
2362             sprintf(statusPtr->taskName, "DeleteDump");
2363             unlock_Status();
2364
2365             /* Wait for task to finish */
2366             taskflag = waitForTask(taskId);
2367             if (taskflag & (TASK_ERROR | ABORT_DONE)) {
2368                 com_err(whoami, BUTX_DELETEOBJFAIL,
2369                         "; Unable to delete dumpID %u via butc", dumpid);
2370                 ERROR(BUTX_DELETEOBJFAIL);      /* the task failed */
2371             }
2372         }
2373     }
2374
2375   error_exit:
2376     if (statusPtr)
2377         deleteStatusNode(statusPtr);    /* Clean up statusPtr - because NOREMOVE */
2378     if (tconn)
2379         rx_DestroyConnection(tconn);    /* Destroy the connection */
2380
2381     /* Remove the dump from the backup database */
2382     if (!code || force) {
2383         dumps.budb_dumpsList_len = 0;
2384         dumps.budb_dumpsList_val = 0;
2385
2386         tcode = bcdb_deleteDump(dumpid, 0, 0, &dumps);
2387         if (tcode) {
2388             com_err(whoami, tcode,
2389                     "; Unable to delete dumpID %u from database", dumpid);
2390             dumps.budb_dumpsList_len = 0;
2391             if (!code)
2392                 code = tcode;
2393         }
2394
2395         /* Display the dumps that were deleted - includes appended dumps */
2396         for (i = 0; i < dumps.budb_dumpsList_len; i++)
2397             printf("     %u%s\n", dumps.budb_dumpsList_val[i],
2398                    (i > 0) ? " Appended Dump" : "");
2399         if (dumps.budb_dumpsList_val)
2400             free(dumps.budb_dumpsList_val);
2401     }
2402
2403     return code;
2404 }
2405
2406 /* bc_deleteDumpCmd
2407  *      Delete a specified dump from the database
2408  * entry:
2409  *      dump id - single required arg as param 0.
2410  */
2411
2412 bc_deleteDumpCmd(as, arock)
2413      struct cmd_syndesc *as;
2414      char *arock;
2415 {
2416     afs_uint32 dumpid;
2417     afs_int32 code = 0;
2418     afs_int32 rcode = 0;
2419     afs_int32 groupId = 0, havegroupid, sflags, noexecute;
2420     struct cmd_item *ti;
2421     afs_uint32 fromTime = 0, toTime = 0, havetime = 0;
2422     char *timeString;
2423     budb_dumpsList dumps, flags;
2424     int i;
2425     afs_int32 port = -1, dbonly = 0, force;
2426
2427     /* Must specify at least one of -dumpid, -from, or -to */
2428     if (!as->parms[0].items && !as->parms[1].items && !as->parms[2].items
2429         && !as->parms[4].items) {
2430         com_err(whoami, 0, "Must specify at least one field");
2431         return (-1);
2432     }
2433
2434     /* Must have -to option with -from option */
2435     if (as->parms[1].items && !as->parms[2].items) {
2436         com_err(whoami, 0, "Must specify '-to' field with '-from' field");
2437         return (-1);
2438     }
2439
2440     /* Get the time to delete from */
2441     if (as->parms[1].items) {   /* -from */
2442         timeString = concatParams(as->parms[1].items);
2443         if (!timeString)
2444             return (-1);
2445
2446         /*
2447          * Now parse this string for the time to start.
2448          */
2449         code = ktime_DateToLong(timeString, &fromTime);
2450         free(timeString);
2451         if (code) {
2452             com_err(whoami, 0, "Can't parse 'from' date and time");
2453             com_err(whoami, 0, "%s", ktime_GetDateUsage());
2454             return (-1);
2455         }
2456         havetime = 1;
2457     }
2458
2459     port = (as->parms[3].items ? getPortOffset(as->parms[3].items->data) : 0);  /* -port */
2460     if (as->parms[5].items)     /* -dbonly */
2461         port = -1;
2462
2463     force = (as->parms[6].items ? 1 : 0);
2464
2465     havegroupid = (as->parms[4].items ? 1 : 0);
2466     if (havegroupid)
2467         groupId = atoi(as->parms[4].items->data);
2468
2469     noexecute = (as->parms[7].items ? 1 : 0);
2470
2471     /* Get the time to delete to */
2472     if (as->parms[2].items) {   /* -to */
2473         timeString = concatParams(as->parms[2].items);
2474         if (!timeString)
2475             return (-1);
2476
2477         /*
2478          * Now parse this string for the time to start. Simce
2479          * times are at minute granularity, add 59 seconds.
2480          */
2481         code = ktime_DateToLong(timeString, &toTime);
2482         free(timeString);
2483         if (code) {
2484             com_err(whoami, 0, "Can't parse 'to' date and time");
2485             com_err(whoami, 0, "%s", ktime_GetDateUsage());
2486             return (-1);
2487         }
2488         toTime += 59;
2489         havetime = 1;
2490     }
2491
2492     if (fromTime > toTime) {
2493         com_err(whoami, 0,
2494                 "'-from' date/time cannot be later than '-to' date/time");
2495         return (-1);
2496     }
2497
2498     /* Remove speicific dump ids - if any */
2499     printf("The following dumps %s deleted:\n",
2500            (noexecute ? "would have been" : "were"));
2501     for (ti = as->parms[0].items; ti != 0; ti = ti->next) {     /* -dumpid */
2502         dumpid = atoi(ti->data);
2503         if (!noexecute) {
2504             code = deleteDump(dumpid, port, force);
2505         } else {
2506             printf("     %u\n", dumpid);
2507         }
2508     }
2509
2510     /*
2511      * Now remove dumps between to and from dates.
2512      */
2513     if (havegroupid || havetime) {
2514         dumps.budb_dumpsList_len = 0;
2515         dumps.budb_dumpsList_val = 0;
2516         flags.budb_dumpsList_len = 0;
2517         flags.budb_dumpsList_val = 0;
2518         sflags = 0;
2519         if (havegroupid)
2520             sflags |= BUDB_OP_GROUPID;
2521         if (havetime)
2522             sflags |= BUDB_OP_DATES;
2523
2524         code =
2525             bcdb_listDumps(sflags, groupId, fromTime, toTime, &dumps, &flags);
2526         if (code) {
2527             com_err(whoami, code,
2528                     "; Error while deleting dumps from %u to %u", fromTime,
2529                     toTime);
2530             rcode = -1;
2531         }
2532
2533         for (i = 0; i < dumps.budb_dumpsList_len; i++) {
2534             if (flags.budb_dumpsList_val[i] & BUDB_OP_DBDUMP)
2535                 continue;
2536
2537             if (!noexecute) {
2538                 if (flags.budb_dumpsList_val[i] & BUDB_OP_APPDUMP)
2539                     continue;
2540                 code = deleteDump(dumps.budb_dumpsList_val[i], port, force);
2541                 /* Ignore code and continue */
2542             } else {
2543                 printf("     %u%s%s\n", dumps.budb_dumpsList_val[i],
2544                        (flags.
2545                         budb_dumpsList_val[i] & BUDB_OP_APPDUMP) ?
2546                        " Appended Dump" : "",
2547                        (flags.
2548                         budb_dumpsList_val[i] & BUDB_OP_DBDUMP) ?
2549                        " Database Dump" : "");
2550
2551             }
2552         }
2553
2554         if (dumps.budb_dumpsList_val)
2555             free(dumps.budb_dumpsList_val);
2556         dumps.budb_dumpsList_len = 0;
2557         dumps.budb_dumpsList_val = 0;
2558         if (flags.budb_dumpsList_val)
2559             free(flags.budb_dumpsList_val);
2560         flags.budb_dumpsList_len = 0;
2561         flags.budb_dumpsList_val = 0;
2562     }
2563
2564     return (rcode);
2565 }
2566
2567 bc_saveDbCmd(as, arock)
2568      struct cmd_syndesc *as;
2569      char *arock;
2570 {
2571     struct rx_connection *tconn;
2572     afs_int32 portOffset = 0;
2573     statusP statusPtr;
2574     afs_uint32 taskId;
2575     afs_int32 code;
2576     afs_uint32 toTime;
2577     char *timeString;
2578
2579     code = bc_UpdateHosts();
2580     if (code) {
2581         com_err(whoami, code, "; Can't retrieve tape hosts");
2582         return (code);
2583     }
2584
2585     if (as->parms[0].items) {
2586         portOffset = getPortOffset(as->parms[0].items->data);
2587         if (portOffset < 0)
2588             return (BC_BADARG);
2589     }
2590
2591     /* Get the time to delete to */
2592     if (as->parms[1].items) {
2593         timeString = concatParams(as->parms[1].items);
2594         if (!timeString)
2595             return (-1);
2596
2597         /*
2598          * Now parse this string for the time. Since
2599          * times are at minute granularity, add 59 seconds.
2600          */
2601         code = ktime_DateToLong(timeString, &toTime);
2602         free(timeString);
2603         if (code) {
2604             com_err(whoami, 0, "Can't parse '-archive' date and time");
2605             com_err(whoami, 0, "%s", ktime_GetDateUsage());
2606             return (-1);
2607         }
2608         toTime += 59;
2609     } else
2610         toTime = 0;
2611
2612     code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2613     if (code)
2614         return (code);
2615
2616     code = TC_SaveDb(tconn, toTime, &taskId);
2617     if (code) {
2618         com_err(whoami, code, "; Failed to save database");
2619         goto exit;
2620     }
2621
2622     /* create status monitor block */
2623     statusPtr = createStatusNode();
2624     lock_Status();
2625     statusPtr->taskId = taskId;
2626     statusPtr->port = portOffset;
2627     statusPtr->jobNumber = bc_jobNumber();
2628     statusPtr->flags &= ~STARTING;      /* clearstatus to examine */
2629     sprintf(statusPtr->taskName, "SaveDb");
2630     unlock_Status();
2631
2632   exit:
2633     rx_DestroyConnection(tconn);
2634     return (code);
2635 }
2636
2637 bc_restoreDbCmd(as, arock)
2638      struct cmd_syndesc *as;
2639      char *arock;
2640 {
2641     struct rx_connection *tconn;
2642     afs_int32 portOffset = 0;
2643     statusP statusPtr;
2644     afs_uint32 taskId;
2645     afs_int32 code;
2646
2647     code = bc_UpdateHosts();
2648     if (code) {
2649         com_err(whoami, code, "; Can't retrieve tape hosts");
2650         return (code);
2651     }
2652
2653     if (as->parms[0].items) {
2654         portOffset = getPortOffset(as->parms[0].items->data);
2655         if (portOffset < 0)
2656             return (BC_BADARG);
2657     }
2658
2659     code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2660     if (code)
2661         return (code);
2662
2663     code = TC_RestoreDb(tconn, &taskId);
2664     if (code) {
2665         com_err(whoami, code, "; Failed to restore database");
2666         goto exit;
2667     }
2668
2669     /* create status monitor block */
2670     statusPtr = createStatusNode();
2671     lock_Status();
2672     statusPtr->taskId = taskId;
2673     statusPtr->port = portOffset;
2674     statusPtr->jobNumber = bc_jobNumber();
2675     statusPtr->flags &= ~STARTING;      /* clearstatus to examine */
2676     sprintf(statusPtr->taskName, "RestoreDb");
2677     unlock_Status();
2678
2679   exit:
2680     rx_DestroyConnection(tconn);
2681     return (code);
2682 }
2683
2684 /* ----------------------------------
2685  * supporting routines for database examination 
2686  * ----------------------------------
2687  */
2688
2689 /* structures and defines for DBLookupByVolume */
2690
2691 #define DBL_MAX_VOLUMES 20      /* max. for each dump */
2692
2693 /* dumpedVol - saves interesting information so that we can print it out
2694  *      later
2695  */
2696
2697 struct dumpedVol {
2698     struct dumpedVol *next;
2699     afs_int32 dumpID;
2700     afs_int32 initialDumpID;
2701     char tapeName[BU_MAXTAPELEN];
2702     afs_int32 level;
2703     afs_int32 parent;
2704     afs_int32 createTime;
2705     afs_int32 incTime;          /* actually the clone time */
2706 };
2707
2708 /* -----------------------------------------
2709  * routines for examining the database
2710  * -----------------------------------------
2711  */
2712
2713 /* DBLookupByVolume
2714  *      Lookup the volumename in the backup database and print the results
2715  * entry:
2716  *      volumeName - volume to lookup
2717  */
2718
2719 DBLookupByVolume(volumeName)
2720      char *volumeName;
2721 {
2722     struct budb_dumpEntry dumpEntry;
2723     struct budb_volumeEntry volumeEntry[DBL_MAX_VOLUMES];
2724     afs_int32 numEntries;
2725     afs_int32 tapedumpid;
2726     afs_int32 last, next;
2727
2728     struct dumpedVol *dvptr = 0;
2729     struct dumpedVol *tempPtr = 0;
2730     afs_int32 code = 0;
2731     int i, pass;
2732     char vname[BU_MAXNAMELEN];
2733     char ds[50];
2734
2735     for (pass = 0; pass < 2; pass++) {
2736         /*p */
2737         /* On second pass, search for backup volume */
2738         if (pass == 1) {
2739             if (!BackupName(volumeName)) {
2740                 strcpy(vname, volumeName);
2741                 strcat(vname, ".backup");
2742                 volumeName = vname;
2743             } else {
2744                 continue;
2745             }
2746         }
2747
2748         last = next = 0;
2749         while (next != -1) {    /*w */
2750             code =
2751                 bcdb_LookupVolume(volumeName, &volumeEntry[0], last, &next,
2752                                   DBL_MAX_VOLUMES, &numEntries);
2753             if (code)
2754                 break;
2755
2756             /* add the volumes to the list */
2757             for (i = 0; i < numEntries; i++) {  /*f */
2758                 struct dumpedVol *insPtr, **prevPtr;
2759
2760                 tempPtr =
2761                     (struct dumpedVol *)malloc(sizeof(struct dumpedVol));
2762                 if (!tempPtr)
2763                     ERROR(BC_NOMEM);
2764
2765                 memset(tempPtr, 0, sizeof(*tempPtr));
2766                 tempPtr->incTime = volumeEntry[i].clone;
2767                 tempPtr->dumpID = volumeEntry[i].dump;
2768                 strncpy(tempPtr->tapeName, volumeEntry[i].tape,
2769                         BU_MAXTAPELEN);
2770
2771                 /* check if we need to null terminate it - just for safety */
2772                 if (strlen(volumeEntry[i].tape) >= BU_MAXTAPELEN)
2773                     tempPtr->tapeName[BU_MAXTAPELEN - 1] = 0;
2774
2775                 code = bcdb_FindDumpByID(tempPtr->dumpID, &dumpEntry);
2776                 if (code) {
2777                     free(tempPtr);
2778                     ERROR(code);
2779                 }
2780
2781                 tempPtr->initialDumpID = dumpEntry.initialDumpID;
2782                 tempPtr->parent = dumpEntry.parent;
2783                 tempPtr->level = dumpEntry.level;
2784                 tempPtr->createTime = dumpEntry.created;
2785
2786                 /* add volume to list in reverse chronological order */
2787                 prevPtr = &dvptr;
2788                 insPtr = dvptr;
2789
2790                 while ((insPtr != 0)
2791                        && (insPtr->createTime > tempPtr->createTime)
2792                     ) {
2793                     prevPtr = &insPtr->next;
2794                     insPtr = insPtr->next;
2795                 }
2796
2797                 /* now at the right place - insert the block */
2798                 tempPtr->next = *prevPtr;
2799                 *prevPtr = tempPtr;
2800             }                   /*f */
2801
2802             last = next;
2803         }                       /*w */
2804     }                           /*p */
2805
2806     if (dvptr) {
2807         printf
2808             ("DumpID    lvl parentID creation date     clone date       tape name\n");
2809         for (tempPtr = dvptr; tempPtr; tempPtr = tempPtr->next) {
2810             /* For the user, the tape name is its name and initial dump id */
2811             tapedumpid =
2812                 (tempPtr->initialDumpID ? tempPtr->initialDumpID : tempPtr->
2813                  dumpID);
2814
2815             /* beware the static items in compactDateString */
2816             compactDateString(&tempPtr->createTime, ds, 50);
2817             printf("%-9d %-2d %-9d %16s", tempPtr->dumpID, tempPtr->level,
2818                    tempPtr->parent, ds);
2819             compactDateString(&tempPtr->incTime, ds, 50);
2820             printf("  %16s %s (%u)\n", ds, tempPtr->tapeName, tapedumpid);
2821         }
2822         code = 0;
2823     }
2824
2825   error_exit:
2826     for (tempPtr = dvptr; tempPtr; tempPtr = dvptr) {
2827         dvptr = dvptr->next;
2828         free(tempPtr);
2829     }
2830
2831     if (code)
2832         com_err(whoami, code, "");
2833     return (code);
2834 }
2835
2836 /* structures for dumpInfo */
2837
2838 struct volumeLink {
2839     struct volumeLink *nextVolume;
2840     struct budb_volumeEntry volumeEntry;
2841 };
2842
2843 struct tapeLink {
2844     struct tapeLink *nextTape;
2845     struct budb_tapeEntry tapeEntry;
2846     struct volumeLink *firstVolume;
2847 };
2848
2849
2850 /* dumpInfo
2851  *      print information about a dump and all its tapes and volumes.
2852  */
2853
2854 afs_int32
2855 dumpInfo(dumpid, detailFlag)
2856      afs_int32 dumpid;
2857      afs_int32 detailFlag;
2858 {
2859     struct budb_dumpEntry dumpEntry;
2860     struct tapeLink *head = 0;
2861     struct tapeLink *tapeLinkPtr, *lastTapeLinkPtr;
2862     struct volumeLink **link, *volumeLinkPtr, *lastVolumeLinkPtr;
2863
2864     budb_volumeList vl;
2865     afs_int32 last, next, dbTime;
2866     afs_int32 tapedumpid;
2867
2868     int tapeNumber;
2869     int i;
2870     int dbDump;
2871     afs_int32 code = 0;
2872     char ds[50];
2873
2874     extern struct udbHandleS udbHandle;
2875
2876     tapeLinkPtr = 0;
2877     lastTapeLinkPtr = 0;
2878     volumeLinkPtr = 0;
2879     lastVolumeLinkPtr = 0;
2880
2881     /* first get information about the dump */
2882
2883     code = bcdb_FindDumpByID(dumpid, &dumpEntry);
2884     if (code)
2885         ERROR(code);
2886
2887     /* For the user, the tape name is its name and initial dump id */
2888     tapedumpid = (dumpEntry.initialDumpID ? dumpEntry.initialDumpID : dumpid);
2889
2890     /* Is this a database dump id or not */
2891     if (strcmp(dumpEntry.name, DUMP_TAPE_NAME) == 0)
2892         dbDump = 1;
2893     else
2894         dbDump = 0;
2895
2896     /* print out the information about the dump */
2897     if (detailFlag) {
2898         printf("\nDump\n");
2899         printf("----\n");
2900         printDumpEntry(&dumpEntry);
2901     } else {
2902         time_t t = dumpEntry.created;
2903         if (dbDump)
2904             printf("Dump: id %u, created: %s\n", dumpEntry.id,
2905                    ctime(&t));
2906         else
2907             printf("Dump: id %u, level %d, volumes %d, created: %s\n",
2908                    dumpEntry.id, dumpEntry.level, dumpEntry.nVolumes,
2909                    ctime(&t));
2910     }
2911
2912     if (!detailFlag && (strlen(dumpEntry.tapes.tapeServer) > 0)
2913         && (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) {
2914         printf("Backup Service: TSM: Server: %s\n",
2915                dumpEntry.tapes.tapeServer);
2916     }
2917
2918     /* now get the list of tapes */
2919     for (tapeNumber = dumpEntry.tapes.b; tapeNumber <= dumpEntry.tapes.maxTapes; tapeNumber++) {        /*f */
2920         tapeLinkPtr = (struct tapeLink *)malloc(sizeof(struct tapeLink));
2921         if (!tapeLinkPtr) {
2922             com_err(whoami, BC_NOMEM, "");
2923             ERROR(BC_NOMEM);
2924         }
2925
2926         memset(tapeLinkPtr, 0, sizeof(*tapeLinkPtr));
2927         code = bcdb_FindTapeSeq(dumpid, tapeNumber, &tapeLinkPtr->tapeEntry);
2928         if (code) {
2929             code = 0;
2930             free(tapeLinkPtr);
2931             continue;
2932         }
2933
2934         /* add this tape to  previous chain */
2935         if (lastTapeLinkPtr) {
2936             lastTapeLinkPtr->nextTape = tapeLinkPtr;
2937             lastTapeLinkPtr = tapeLinkPtr;
2938         }
2939
2940         if (head == 0) {
2941             head = tapeLinkPtr;
2942             lastTapeLinkPtr = head;
2943         }
2944
2945         next = 0;
2946         while (next != -1) {    /*wn */
2947             vl.budb_volumeList_len = 0;
2948             vl.budb_volumeList_val = 0;
2949             last = next;
2950
2951             /* now get all the volumes in this dump. */
2952             code = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client, UF_SINGLESERVER, BUDB_MAJORVERSION, BUDB_OP_DUMPID | BUDB_OP_TAPENAME, tapeLinkPtr->tapeEntry.name,     /* tape name */
2953                                           dumpid,       /* dumpid (not initial dumpid) */
2954                                           0,    /* end */
2955                                           last, /* last */
2956                                           &next,        /* nextindex */
2957                                           &dbTime,      /* update time */
2958                                           &vl);
2959
2960             if (code) {
2961                 if (code == BUDB_ENDOFLIST) {   /* 0 volumes on tape */
2962                     code = 0;
2963                     break;
2964                 }
2965                 ERROR(code);
2966             }
2967
2968             for (i = 0; i < vl.budb_volumeList_len; i++) {
2969                 link = &tapeLinkPtr->firstVolume;
2970
2971                 volumeLinkPtr =
2972                     (struct volumeLink *)malloc(sizeof(struct volumeLink));
2973                 if (!volumeLinkPtr) {
2974                     com_err(whoami, BC_NOMEM, "");
2975                     ERROR(BC_NOMEM);
2976                 }
2977                 memset(volumeLinkPtr, 0, sizeof(*volumeLinkPtr));
2978
2979                 memcpy(&volumeLinkPtr->volumeEntry,
2980                        &vl.budb_volumeList_val[i],
2981                        sizeof(struct budb_volumeEntry));
2982
2983                 /* now insert it onto the right place */
2984                 while ((*link != 0)
2985                        && (volumeLinkPtr->volumeEntry.position >
2986                            (*link)->volumeEntry.position)) {
2987                     link = &((*link)->nextVolume);
2988                 }
2989
2990                 /* now link it in */
2991                 volumeLinkPtr->nextVolume = *link;
2992                 *link = volumeLinkPtr;
2993             }
2994
2995             if (vl.budb_volumeList_val)
2996                 free(vl.budb_volumeList_val);
2997         }                       /*wn */
2998     }                           /*f */
2999
3000     for (tapeLinkPtr = head; tapeLinkPtr; tapeLinkPtr = tapeLinkPtr->nextTape) {
3001         if (detailFlag) {
3002             printf("\nTape\n");
3003             printf("----\n");
3004             printTapeEntry(&tapeLinkPtr->tapeEntry);
3005         } else {
3006             printf("Tape: name %s (%u)\n", tapeLinkPtr->tapeEntry.name,
3007                    tapedumpid);
3008             printf("nVolumes %d, ", tapeLinkPtr->tapeEntry.nVolumes);
3009             compactDateString(&tapeLinkPtr->tapeEntry.written, ds, 50);
3010             printf("created %16s", ds);
3011             if (tapeLinkPtr->tapeEntry.expires != 0) {
3012                 compactDateString(&tapeLinkPtr->tapeEntry.expires, ds, 50);
3013                 printf(", expires %16s", ds);
3014             }
3015             printf("\n\n");
3016         }
3017
3018         /* print out all the volumes */
3019
3020         /* print header for volume listing - db dumps have no volumes */
3021         if ((detailFlag == 0) && !dbDump)
3022             printf("%4s %16s %9s %-s\n", "Pos", "Clone time", "Nbytes",
3023                    "Volume");
3024
3025         for (volumeLinkPtr = tapeLinkPtr->firstVolume; volumeLinkPtr;
3026              volumeLinkPtr = volumeLinkPtr->nextVolume) {
3027             if (detailFlag) {
3028                 printf("\nVolume\n");
3029                 printf("------\n");
3030                 printVolumeEntry(&volumeLinkPtr->volumeEntry);
3031             } else {
3032                 compactDateString(&volumeLinkPtr->volumeEntry.clone, ds, 50),
3033                     printf("%4d %s %10u %16s\n",
3034                            volumeLinkPtr->volumeEntry.position, ds,
3035                            volumeLinkPtr->volumeEntry.nBytes,
3036                            volumeLinkPtr->volumeEntry.name);
3037             }
3038         }
3039     }
3040
3041   error_exit:
3042     if (code)
3043         com_err("dumpInfo", code, "; Can't get dump information");
3044
3045     /* free all allocated structures */
3046     tapeLinkPtr = head;
3047     while (tapeLinkPtr) {
3048         volumeLinkPtr = tapeLinkPtr->firstVolume;
3049         while (volumeLinkPtr) {
3050             lastVolumeLinkPtr = volumeLinkPtr;
3051             volumeLinkPtr = volumeLinkPtr->nextVolume;
3052             free(lastVolumeLinkPtr);
3053         }
3054
3055         lastTapeLinkPtr = tapeLinkPtr;
3056         tapeLinkPtr = tapeLinkPtr->nextTape;
3057         free(lastTapeLinkPtr);
3058     }
3059     return (code);
3060 }
3061
3062 int
3063 compareDump(ptr1, ptr2)
3064      struct budb_dumpEntry *ptr1, *ptr2;
3065 {
3066     if (ptr1->created < ptr2->created)
3067         return (-1);
3068     else if (ptr1->created > ptr2->created)
3069         return (1);
3070     return (0);
3071 }
3072
3073 afs_int32
3074 printRecentDumps(ndumps)
3075      int ndumps;
3076 {
3077     afs_int32 code = 0;
3078     afs_int32 nextindex, index = 0;
3079     afs_int32 dbTime;
3080     budb_dumpList dl;
3081     struct budb_dumpEntry *dumpPtr;
3082     int i;
3083     char ds[50];
3084
3085     extern struct udbHandleS udbHandle;
3086     extern compareDump();
3087
3088     do {                        /* while (nextindex != -1) */
3089         /* initialize the dump list */
3090         dl.budb_dumpList_len = 0;
3091         dl.budb_dumpList_val = 0;
3092
3093         /* outline algorithm */
3094         code = ubik_Call(BUDB_GetDumps, udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_NPREVIOUS, "",       /* no name */
3095                          0,     /* start */
3096                          ndumps,        /* end */
3097                          index, /* index */
3098                          &nextindex, &dbTime, &dl);
3099         if (code) {
3100             if (code == BUDB_ENDOFLIST)
3101                 return 0;
3102             com_err("dumpInfo", code, "; Can't get dump information");
3103             return (code);
3104         }
3105
3106         /* No need to sort, it's already sorted */
3107
3108         if (dl.budb_dumpList_len && (index == 0))
3109             printf("%10s %10s %2s %-16s %2s %5s dump name\n", "dumpid",
3110                    "parentid", "lv", "created", "nt", "nvols");
3111
3112         dumpPtr = dl.budb_dumpList_val;
3113         for (i = 1; i <= dl.budb_dumpList_len; i++) {
3114             compactDateString(&dumpPtr->created, ds, 50),
3115                 printf("%10u %10u %-2d %16s %2d %5d %s", dumpPtr->id,
3116                        dumpPtr->parent, dumpPtr->level, ds,
3117                        dumpPtr->tapes.maxTapes - dumpPtr->tapes.b + 1,
3118                        dumpPtr->nVolumes, dumpPtr->name);
3119             if (dumpPtr->initialDumpID) /* an appended dump */
3120                 printf(" (%u)", dumpPtr->initialDumpID);
3121             else if (dumpPtr->appendedDumpID)   /* has appended dumps */
3122                 printf(" (%u)", dumpPtr->id);
3123             printf("\n");
3124
3125             dumpPtr++;
3126         }
3127
3128         if (dl.budb_dumpList_val)
3129             free(dl.budb_dumpList_val);
3130         index = nextindex;
3131     } while (nextindex != -1);
3132
3133     return (code);
3134 }
3135
3136 /* bc_dumpInfoCmd 
3137  *      list the dumps and contens of the dumps.
3138  * params:
3139  *      as - name of tape
3140  *      arock -
3141  */
3142 bc_dumpInfoCmd(as, arock)
3143      struct cmd_syndesc *as;
3144      char *arock;
3145 {
3146     afs_int32 dumpid;
3147     afs_int32 detailFlag;
3148     afs_int32 ndumps;
3149     afs_int32 code = 0;
3150
3151     afs_int32 dumpInfo();
3152
3153     if (as->parms[0].items) {
3154         if (as->parms[1].items) {
3155             com_err(whoami, 0,
3156                     "These options are exclusive - select only one");
3157             return (BC_BADARG);
3158         }
3159         ndumps = atoi(as->parms[0].items->data);
3160         if (ndumps <= 0) {
3161             com_err(whoami, 0, "Must provide a positive number");
3162             return -1;
3163         }
3164
3165         code = printRecentDumps(ndumps);
3166     } else if (as->parms[1].items) {
3167         detailFlag = (as->parms[2].items ? 1 : 0);      /* 1 = detailed listing */
3168         dumpid = atoi(as->parms[1].items->data);
3169         code = dumpInfo(dumpid, detailFlag);
3170     } else {
3171         code = printRecentDumps(10);
3172     }
3173
3174     return (code);
3175 }