assorted-warning-cleanup-20071126
[openafs.git] / src / vlserver / vldb_check.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 /* Read a VLDB file and verify it for correctness */
11
12 #define VL  0x001               /* good volume entry */
13 #define FR  0x002               /* free volume entry */
14 #define MH  0x004               /* multi-homed entry */
15
16 #define RWH 0x010               /* on rw hash chain */
17 #define ROH 0x020               /* on ro hash chain */
18 #define BKH 0x040               /* on bk hash chain */
19 #define NH  0x080               /* on name hash chain */
20
21 #define MHC 0x100               /* on multihomed chain */
22 #define FRC 0x200               /* on free chain */
23
24 #define REFRW 0x1000            /* linked from something (RW) */
25 #define REFRO 0x2000            /* linked from something (RO) */
26 #define REFBK 0x4000            /* linked from something (BK) */
27 #define REFN  0x8000            /* linked from something (name) */
28
29 #define vldbread(x,y,z) vldbio(x,y,z,0)
30 #define vldbwrite(x,y,z) vldbio(x,y,z,1)
31
32 #include <afsconfig.h>
33 #include <afs/param.h>
34
35 RCSID
36     ("$Header$");
37
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <string.h>
43 #ifdef AFS_NT40_ENV
44 #include <winsock2.h>
45 #include <WINNT/afsevent.h>
46 #include <io.h>
47 #else
48 #include <sys/socket.h>
49 #include <netdb.h>
50 #include <netinet/in.h>
51 #endif
52
53 #include "vlserver.h"
54 #include "vldbint.h"
55 #include <ubik.h>
56 #include <afs/afsutil.h>
57 #include <afs/cmd.h>
58
59 #define ADDR(x) (x/sizeof(struct nvlentry))
60
61 int fd;
62 int listentries, listservers, listheader, listuheader, verbose;
63
64 int fix = 0;
65
66 struct er {
67     long addr;
68     int type;
69 } *record;
70 int serveraddrs[MAXSERVERID + 2];
71
72 #if 0
73 int
74 writeUbikHeader()
75 {
76     /* Bump the version number?? We could cheat and push a new db... */
77 }
78 #endif
79
80 #define HDRSIZE 64
81 int
82 readUbikHeader()
83 {
84     int offset, r;
85     struct ubik_hdr uheader;
86
87     offset = lseek(fd, 0, 0);
88     if (offset != 0) {
89         printf("error: lseek to 0 failed: %d %d\n", offset, errno);
90         return (-1);
91     }
92
93     /* now read the info */
94     r = read(fd, &uheader, sizeof(uheader));
95     if (r != sizeof(uheader)) {
96         printf("error: read of %d bytes failed: %d %d\n", sizeof(uheader), r,
97                errno);
98         return (-1);
99     }
100
101     uheader.magic = ntohl(uheader.magic);
102     uheader.size = ntohl(uheader.size);
103     uheader.version.epoch = ntohl(uheader.version.epoch);
104     uheader.version.counter = ntohl(uheader.version.counter);
105
106     if (listuheader) {
107         printf("Ubik Header\n");
108         printf("   Magic           = 0x%x\n", uheader.magic);
109         printf("   Size            = %u\n", uheader.size);
110         printf("   Version.epoch   = %u\n", uheader.version.epoch);
111         printf("   Version.counter = %u\n", uheader.version.counter);
112     }
113
114     if (uheader.size != HDRSIZE)
115         printf("Ubik header size is %u (should be %u)\n", uheader.size,
116                HDRSIZE);
117     if (uheader.magic != UBIK_MAGIC)
118         printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic,
119                UBIK_MAGIC);
120
121     return (0);
122 }
123
124 int
125 vldbio(int position, char *buffer, int size, int rdwr)
126 {
127     int offset, r, p;
128
129     /* seek to the correct spot. skip ubik stuff */
130     p = position + HDRSIZE;
131     offset = lseek(fd, p, 0);
132     if (offset != p) {
133         printf("error: lseek to %d failed: %d %d\n", p, offset, errno);
134         return (-1);
135     }
136
137     if (rdwr == 1) 
138         r = write(fd, buffer, size);
139     else 
140         r = read(fd, buffer, size);
141
142     if (r != size) {
143         printf("error: %s of %d bytes failed: %d %d\n", rdwr==1?"write":"read",
144                size, r, errno);
145         return (-1);
146     }
147     return (0);
148 }
149
150 char *
151 vtype(int type)
152 {
153     static char Type[3];
154
155     if (type == 0)
156         strcpy(Type, "rw");
157     else if (type == 1)
158         strcpy(Type, "ro");
159     else if (type == 2)
160         strcpy(Type, "bk");
161     else
162         strcpy(Type, "??");
163     return (Type);
164 }
165
166 afs_int32
167 NameHash(char *volname)
168 {
169     unsigned int hash;
170     char *vchar;
171
172     hash = 0;
173     for (vchar = volname + strlen(volname) - 1; vchar >= volname; vchar--)
174         hash = (hash * 63) + (*((unsigned char *)vchar) - 63);
175     return (hash % HASHSIZE);
176 }
177
178 afs_int32
179 IdHash(afs_int32 volid)
180 {
181     return ((abs(volid)) % HASHSIZE);
182 }
183
184 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
185 int
186 InvalidVolname(char *volname)
187 {
188     char *map;
189     size_t slen;
190
191     map = LEGALCHARS;
192     slen = strlen(volname);
193     if (slen >= VL_MAXNAMELEN)
194         return 1;
195     return (slen != strspn(volname, map));
196 }
197
198 void
199 readheader(struct vlheader *headerp)
200 {
201     int i, j;
202
203     vldbread(0, headerp, sizeof(*headerp));
204
205     headerp->vital_header.vldbversion =
206         ntohl(headerp->vital_header.vldbversion);
207     headerp->vital_header.headersize =
208         ntohl(headerp->vital_header.headersize);
209     headerp->vital_header.freePtr = ntohl(headerp->vital_header.freePtr);
210     headerp->vital_header.eofPtr = ntohl(headerp->vital_header.eofPtr);
211     headerp->vital_header.allocs = ntohl(headerp->vital_header.allocs);
212     headerp->vital_header.frees = ntohl(headerp->vital_header.frees);
213     headerp->vital_header.MaxVolumeId =
214         ntohl(headerp->vital_header.MaxVolumeId);
215     headerp->vital_header.totalEntries[0] =
216         ntohl(headerp->vital_header.totalEntries[0]);
217     for (i = 0; i < MAXTYPES; i++)
218         headerp->vital_header.totalEntries[i] =
219             ntohl(headerp->vital_header.totalEntries[1]);
220
221     headerp->SIT = ntohl(headerp->SIT);
222     for (i = 0; i < MAXSERVERID; i++)
223         headerp->IpMappedAddr[i] = ntohl(headerp->IpMappedAddr[i]);
224     for (i = 0; i < HASHSIZE; i++)
225         headerp->VolnameHash[i] = ntohl(headerp->VolnameHash[i]);
226     for (i = 0; i < MAXTYPES; i++)
227         for (j = 0; j < HASHSIZE; j++)
228             headerp->VolidHash[i][j] = ntohl(headerp->VolidHash[i][j]);
229
230     if (listheader) {
231         printf("vldb header\n");
232         printf("   vldbversion      = %u\n",
233                headerp->vital_header.vldbversion);
234         printf("   headersize       = %u [actual=%u]\n",
235                headerp->vital_header.headersize, sizeof(*headerp));
236         printf("   freePtr          = 0x%x\n", headerp->vital_header.freePtr);
237         printf("   eofPtr           = %u\n", headerp->vital_header.eofPtr);
238         printf("   allocblock calls = %10u\n", headerp->vital_header.allocs);
239         printf("   freeblock  calls = %10u\n", headerp->vital_header.frees);
240         printf("   MaxVolumeId      = %u\n",
241                headerp->vital_header.MaxVolumeId);
242         printf("   rw vol entries   = %u\n",
243                headerp->vital_header.totalEntries[0]);
244         printf("   ro vol entries   = %u\n",
245                headerp->vital_header.totalEntries[1]);
246         printf("   bk vol entries   = %u\n",
247                headerp->vital_header.totalEntries[2]);
248         printf("   multihome info   = 0x%x (%u)\n", headerp->SIT,
249                headerp->SIT);
250         printf("   server ip addr   table: size = %d entries\n",
251                MAXSERVERID + 1);
252         printf("   volume name hash table: size = %d buckets\n", HASHSIZE);
253         printf("   volume id   hash table: %d tables with %d buckets each\n",
254                MAXTYPES, HASHSIZE);
255     }
256
257     /* Check the header size */
258     if (headerp->vital_header.headersize != sizeof(*headerp))
259         printf("Header reports its size as %d (should be %d)\n",
260                headerp->vital_header.headersize, sizeof(*headerp));
261     return 0;
262 }
263
264 void
265 writeheader(struct vlheader *headerp)
266 {
267     int i, j;
268
269     headerp->vital_header.vldbversion =
270         htonl(headerp->vital_header.vldbversion);
271     headerp->vital_header.headersize =
272         htonl(headerp->vital_header.headersize);
273     headerp->vital_header.freePtr = htonl(headerp->vital_header.freePtr);
274     headerp->vital_header.eofPtr = htonl(headerp->vital_header.eofPtr);
275     headerp->vital_header.allocs = htonl(headerp->vital_header.allocs);
276     headerp->vital_header.frees = htonl(headerp->vital_header.frees);
277     headerp->vital_header.MaxVolumeId =
278         htonl(headerp->vital_header.MaxVolumeId);
279     headerp->vital_header.totalEntries[0] =
280         htonl(headerp->vital_header.totalEntries[0]);
281     for (i = 0; i < MAXTYPES; i++)
282         headerp->vital_header.totalEntries[i] =
283             htonl(headerp->vital_header.totalEntries[1]);
284
285     headerp->SIT = htonl(headerp->SIT);
286     for (i = 0; i < MAXSERVERID; i++)
287         headerp->IpMappedAddr[i] = htonl(headerp->IpMappedAddr[i]);
288     for (i = 0; i < HASHSIZE; i++)
289         headerp->VolnameHash[i] = htonl(headerp->VolnameHash[i]);
290     for (i = 0; i < MAXTYPES; i++)
291         for (j = 0; j < HASHSIZE; j++)
292             headerp->VolidHash[i][j] = htonl(headerp->VolidHash[i][j]);
293
294     vldbwrite(0, headerp, sizeof(*headerp));
295 }
296
297 void
298 readMH(afs_int32 addr, struct extentaddr *mhblockP)
299 {
300     int i, j;
301     struct extentaddr *e;
302
303     vldbread(addr, mhblockP, VL_ADDREXTBLK_SIZE);
304
305     mhblockP->ex_count = ntohl(mhblockP->ex_count);
306     mhblockP->ex_flags = ntohl(mhblockP->ex_flags);
307     for (i = 0; i < VL_MAX_ADDREXTBLKS; i++)
308         mhblockP->ex_contaddrs[i] = ntohl(mhblockP->ex_contaddrs[i]);
309
310     for (i = 1; i < VL_MHSRV_PERBLK; i++) {
311         e = &(mhblockP[i]);
312
313         /* won't convert hostuuid */
314         e->ex_uniquifier = ntohl(e->ex_uniquifier);
315         for (j = 0; j < VL_MAXIPADDRS_PERMH; j++)
316             e->ex_addrs[j] = ntohl(e->ex_addrs[j]);
317     }
318     return 0;
319 }
320
321 void
322 readentry(afs_int32 addr, struct nvlentry *vlentryp, afs_int32 *type)
323 {
324     int i;
325
326     vldbread(addr, vlentryp, sizeof(*vlentryp));
327
328     for (i = 0; i < MAXTYPES; i++)
329         vlentryp->volumeId[i] = ntohl(vlentryp->volumeId[i]);
330     vlentryp->flags = ntohl(vlentryp->flags);
331     vlentryp->LockAfsId = ntohl(vlentryp->LockAfsId);
332     vlentryp->LockTimestamp = ntohl(vlentryp->LockTimestamp);
333     vlentryp->cloneId = ntohl(vlentryp->cloneId);
334     for (i = 0; i < MAXTYPES; i++)
335         vlentryp->nextIdHash[i] = ntohl(vlentryp->nextIdHash[i]);
336     vlentryp->nextNameHash = ntohl(vlentryp->nextNameHash);
337     for (i = 0; i < NMAXNSERVERS; i++) {
338         vlentryp->serverNumber[i] = ntohl(vlentryp->serverNumber[i]);
339         vlentryp->serverPartition[i] = ntohl(vlentryp->serverPartition[i]);
340         vlentryp->serverFlags[i] = ntohl(vlentryp->serverFlags[i]);
341     }
342
343     if (vlentryp->flags == VLCONTBLOCK) {
344         *type = MH;
345     } else if (vlentryp->flags == VLFREE) {
346         *type = FR;
347     } else {
348         *type = VL;
349     }
350
351     if (listentries) {
352         printf("address %u: ", addr);
353         if (vlentryp->flags == VLCONTBLOCK) {
354             printf("mh extension block\n");
355         } else if (vlentryp->flags == VLFREE) {
356             printf("free vlentry\n");
357         } else {
358             printf("vlentry %s\n", vlentryp->name);
359             printf("   rw id = %u ; ro id = %u ; bk id = %u\n",
360                    vlentryp->volumeId[0], vlentryp->volumeId[1],
361                    vlentryp->volumeId[2]);
362             printf("   flags         =");
363             if (vlentryp->flags & VLF_RWEXISTS)
364                 printf(" rw");
365             if (vlentryp->flags & VLF_ROEXISTS)
366                 printf(" ro");
367             if (vlentryp->flags & VLF_BACKEXISTS)
368                 printf(" bk");
369             if (vlentryp->flags & 0xffff8fff)
370                 printf(" errorflag(0x%x)", vlentryp->flags);
371             printf("\n");
372             printf("   LockAfsId     = %d\n", vlentryp->LockAfsId);
373             printf("   LockTimestamp = %d\n", vlentryp->LockTimestamp);
374             printf("   cloneId       = %u\n", vlentryp->cloneId);
375             printf
376                 ("   next hash for rw = %u ; ro = %u ; bk = %u ; name = %u\n",
377                  vlentryp->nextIdHash[0], vlentryp->nextIdHash[1],
378                  vlentryp->nextIdHash[2], vlentryp->nextNameHash);
379             for (i = 0; i < NMAXNSERVERS; i++) {
380                 if (vlentryp->serverNumber[i] != 255) {
381                     printf("   server %d ; partition %d ; flags =",
382                            vlentryp->serverNumber[i],
383                            vlentryp->serverPartition[i]);
384                     if (vlentryp->serverFlags[i] & VLSF_RWVOL)
385                         printf(" rw");
386                     if (vlentryp->serverFlags[i] & VLSF_ROVOL)
387                         printf(" ro");
388                     if (vlentryp->serverFlags[i] & VLSF_BACKVOL)
389                         printf(" bk");
390                     if (vlentryp->serverFlags[i] & VLSF_NEWREPSITE)
391                         printf(" newro");
392                     printf("\n");
393                 }
394             }
395         }
396     }
397     return 0;
398 }
399
400 void
401 writeentry(afs_int32 addr, struct nvlentry *vlentryp)
402 {
403     int i;
404
405     if (verbose)
406         printf("Writing back entry at addr %u\n", addr);
407
408     for (i = 0; i < MAXTYPES; i++)
409         vlentryp->volumeId[i] = htonl(vlentryp->volumeId[i]);
410     vlentryp->flags = htonl(vlentryp->flags);
411     vlentryp->LockAfsId = htonl(vlentryp->LockAfsId);
412     vlentryp->LockTimestamp = htonl(vlentryp->LockTimestamp);
413     vlentryp->cloneId = htonl(vlentryp->cloneId);
414     for (i = 0; i < MAXTYPES; i++)
415         vlentryp->nextIdHash[i] = htonl(vlentryp->nextIdHash[i]);
416     vlentryp->nextNameHash = htonl(vlentryp->nextNameHash);
417     for (i = 0; i < NMAXNSERVERS; i++) {
418         vlentryp->serverNumber[i] = htonl(vlentryp->serverNumber[i]);
419         vlentryp->serverPartition[i] = htonl(vlentryp->serverPartition[i]);
420         vlentryp->serverFlags[i] = htonl(vlentryp->serverFlags[i]);
421     }
422     vldbwrite(addr, vlentryp, sizeof(*vlentryp));
423 }
424
425 void
426 readSIT(int base, int addr)
427 {
428     int i, j, a;
429     char sitbuf[VL_ADDREXTBLK_SIZE];
430     struct extentaddr *extent;
431
432     if (!addr)
433         return;
434     vldbread(addr, sitbuf, VL_ADDREXTBLK_SIZE);
435     extent = (struct extentaddr *)sitbuf;
436
437     printf("multihome info block: base %d\n", base);
438     if (base == 0) {
439         printf("   count = %u\n", ntohl(extent->ex_count));
440         printf("   flags = %u\n", ntohl(extent->ex_flags));
441         for (i = 0; i < VL_MAX_ADDREXTBLKS; i++) {
442             printf("   contaddrs[%d] = %u\n", i,
443                    ntohl(extent->ex_contaddrs[i]));
444         }
445     }
446     for (i = 1; i < VL_MHSRV_PERBLK; i++) {
447         /* should we skip this entry */
448         for (j = 0; j < VL_MAX_ADDREXTBLKS; j++) {
449             if (extent[i].ex_addrs[j])
450                 break;
451         }
452         if (j >= VL_MAX_ADDREXTBLKS)
453             continue;
454
455         printf("   base %d index %d:\n", base, i);
456
457         printf("       afsuuid    = (%x %x %x /%d/%d/ /%x/%x/%x/%x/%x/%x/)\n",
458                ntohl(extent[i].ex_hostuuid.time_low),
459                ntohl(extent[i].ex_hostuuid.time_mid),
460                ntohl(extent[i].ex_hostuuid.time_hi_and_version),
461                ntohl(extent[i].ex_hostuuid.clock_seq_hi_and_reserved),
462                ntohl(extent[i].ex_hostuuid.clock_seq_low),
463                ntohl(extent[i].ex_hostuuid.node[0]),
464                ntohl(extent[i].ex_hostuuid.node[1]),
465                ntohl(extent[i].ex_hostuuid.node[2]),
466                ntohl(extent[i].ex_hostuuid.node[3]),
467                ntohl(extent[i].ex_hostuuid.node[4]),
468                ntohl(extent[i].ex_hostuuid.node[5]));
469         printf("       uniquifier = %u\n", ntohl(extent[i].ex_uniquifier));
470         for (j = 0; j < VL_MAXIPADDRS_PERMH; j++) {
471             a = ntohl(extent[i].ex_addrs[j]);
472             if (a) {
473                 printf("       %d.%d.%d.%d\n", (a >> 24) & 0xff,
474                        (a >> 16) & 0xff, (a >> 8) & 0xff, (a) & 0xff);
475             }
476         }
477     }
478 }
479
480 /*
481  * Read each entry in the database:
482  * Record what type of entry it is and its address in the record array.
483  * Remember what the maximum volume id we found is and check against the header.
484  */
485 void
486 ReadAllEntries(struct vlheader *header)
487 {
488     afs_int32 type, rindex, i, j, e;
489     int freecount = 0, mhcount = 0, vlcount = 0;
490     int rwcount = 0, rocount = 0, bkcount = 0;
491     struct nvlentry vlentry;
492     afs_uint32 addr, entrysize, maxvolid = 0;
493
494     if (verbose)
495         printf("Read each entry in the database\n");
496     for (addr = header->vital_header.headersize;
497          addr < header->vital_header.eofPtr; addr += entrysize) {
498
499         /* Remember the highest volume id */
500         readentry(addr, &vlentry, &type);
501         if (type == VL) {
502             if (!(vlentry.flags & VLF_RWEXISTS))
503                 printf("WARNING: VLDB entry '%s' has no RW volume\n",
504                        vlentry.name);
505
506             for (i = 0; i < MAXTYPES; i++)
507                 if (maxvolid < vlentry.volumeId[i])
508                     maxvolid = vlentry.volumeId[i];
509
510             e = 1;
511             for (j = 0; j < NMAXNSERVERS; j++) {
512                 if (vlentry.serverNumber[j] == 255)
513                     continue;
514                 if (vlentry.serverFlags[j] & (VLSF_ROVOL | VLSF_NEWREPSITE)) {
515                     rocount++;
516                     continue;
517                 }
518                 if (vlentry.serverFlags[j] & VLSF_RWVOL) {
519                     rwcount++;
520                     if (vlentry.flags & VLF_BACKEXISTS)
521                         bkcount++;
522                     continue;
523                 }
524                 if (!vlentry.serverFlags[j]) {
525                     /*e = 0;*/
526                     continue;
527                 }
528                 if (e) {
529                     printf
530                         ("VLDB entry '%s' contains an unknown RW/RO index serverFlag\n",
531                          vlentry.name);
532                     e = 0;
533                 }
534                 printf
535                     ("   index %d : serverNumber %d : serverPartition %d : serverFlag %d\n",
536                      j, vlentry.serverNumber[j], vlentry.serverPartition[j],
537                      vlentry.serverFlags[j]);
538             }
539         }
540
541         rindex = addr / sizeof(vlentry);
542         if (record[rindex].type) {
543             printf("INTERNAL ERROR: record holder %d already in use\n",
544                    rindex);
545             return;
546         }
547         record[rindex].addr = addr;
548         record[rindex].type = type;
549
550         /* Determine entrysize and keep count */
551         if (type == VL) {
552             entrysize = sizeof(vlentry);
553             vlcount++;
554         } else if (type == FR) {
555             entrysize = sizeof(vlentry);
556             freecount++;
557         } else if (type == MH) {
558             entrysize = VL_ADDREXTBLK_SIZE;
559             mhcount++;
560         } else {
561             printf("Unknown entry at %u\n", addr);
562         }
563     }
564     if (verbose) {
565         printf("Found %d entries, %d free entries, %d multihomed blocks\n",
566                vlcount, freecount, mhcount);
567         printf("Found %d RW volumes, %d BK volumes, %d RO volumes\n", rwcount,
568                bkcount, rocount);
569     }
570
571     /* Check the maxmimum volume id in the header */
572     if (maxvolid != header->vital_header.MaxVolumeId - 1)
573         printf
574             ("Header's maximum volume id is %u and largest id found in VLDB is %u\n",
575              header->vital_header.MaxVolumeId, maxvolid);
576 }
577
578
579 void
580 SetHashEnd(long addr, int type, long new)
581 {
582     struct nvlentry vlentry;
583     afs_int32 i, rindex, type2, next = -1;
584
585     for (; addr; addr = next) {
586         readentry(addr, &vlentry, &type2);
587         switch(type & 0xf0) {
588         case RWH:
589             next = vlentry.nextIdHash[0];
590             break;
591         case ROH:
592             next = vlentry.nextIdHash[1];
593             break;
594         case BKH:
595             next = vlentry.nextIdHash[2];
596             break;
597         case NH:
598             next = vlentry.nextNameHash;
599             break;
600         default:
601             next = -1;
602         }
603
604         if (next < 1) {
605             switch(type & 0xf0) {
606             case RWH:
607               if (vlentry.nextIdHash[0] != 0) {printf("bwoop\n");}
608                 vlentry.nextIdHash[0] = new;
609                 break;
610             case ROH:
611               if (vlentry.nextIdHash[1] != 0) {printf("bwoop\n");}
612                 vlentry.nextIdHash[1] = new;
613                 break;
614             case BKH:
615               if (vlentry.nextIdHash[2] != 0) {printf("bwoop\n");}
616                 vlentry.nextIdHash[2] = new;
617                 break;
618             case NH:
619               if (vlentry.nextNameHash != 0) {printf("bwoop\n");}
620                 vlentry.nextNameHash = new;
621                 break;
622             }
623             writeentry(addr, &vlentry);
624             return;
625         }
626     }
627 }
628
629 /*
630  * Follow each Name hash bucket marking it as read in the record array.
631  * Record we found it in the name hash within the record array.
632  * Check that the name is hashed correctly.
633  */
634 void
635 FollowNameHash(struct vlheader *header)
636 {
637     int count = 0, longest = 0, shortest = -1, chainlength;
638     struct nvlentry vlentry;
639     afs_uint32 addr;
640     afs_int32 i, type, rindex;
641
642     /* Now follow the Name Hash Table */
643     if (verbose)
644         printf("Check Volume Name Hash\n");
645     for (i = 0; i < HASHSIZE; i++) {
646         chainlength = 0;
647         for (addr = header->VolnameHash[i]; addr; addr = vlentry.nextNameHash) {
648             readentry(addr, &vlentry, &type);
649             if (type != VL) {
650                 printf("Name Hash %d: Bad entry at %u: Not a valid vlentry\n",
651                        i, addr);
652                 continue;
653             }
654
655             rindex = addr / sizeof(vlentry);
656
657             if (record[rindex].addr != addr && record[rindex].addr) {
658                 printf
659                     ("INTERNAL ERROR: addresses %u and %u use same record slot %d\n",
660                      record[rindex].addr, addr, rindex);
661             }
662             if (record[rindex].type & NH) {
663                 printf
664                     ("Name Hash %d: Bad entry '%s': Already in the name hash\n",
665                      i, vlentry.name);
666                 break;
667             }
668             record[rindex].type |= NH;
669
670             record[rindex].type |= REFN;
671
672             chainlength++;
673             count++;
674
675             /* Hash the name and check if in correct hash table */
676             if (NameHash(vlentry.name) != i) {
677                 printf
678                     ("Name Hash %d: Bad entry '%s': Incorrect name hash chain (should be in %d)\n",
679                      i, vlentry.name, NameHash(vlentry.name));
680             }
681         }
682         if (chainlength > longest)
683             longest = chainlength;
684         if ((shortest == -1) || (chainlength < shortest))
685             shortest = chainlength;
686     }
687     if (verbose) {
688         printf
689             ("%d entries in name hash, longest is %d, shortest is %d, average length is %f\n",
690              count, longest, shortest, ((float)count / (float)HASHSIZE));
691     }
692     return 0;
693 }
694
695 /*
696  * Follow the ID hash chains for the RW, RO, and BK hash tables.
697  * Record we found it in the id hash within the record array.
698  * Check that the ID is hashed correctly.
699  */
700 void
701 FollowIdHash(struct vlheader *header)
702 {
703     int count = 0, longest = 0, shortest = -1, chainlength;
704     struct nvlentry vlentry;
705     afs_uint32 addr;
706     afs_int32 i, j, hash, type, rindex, ref;
707
708     /* Now follow the RW, RO, and BK Hash Tables */
709     if (verbose)
710         printf("Check RW, RO, and BK id Hashes\n");
711     for (i = 0; i < MAXTYPES; i++) {
712         hash = ((i == 0) ? RWH : ((i == 1) ? ROH : BKH));
713         ref = ((i == 0) ? REFRW : ((i == 1) ? REFRO : REFBK));
714         count = longest = 0;
715         shortest = -1;
716
717         for (j = 0; j < HASHSIZE; j++) {
718             chainlength = 0;
719             for (addr = header->VolidHash[i][j]; addr;
720                  addr = vlentry.nextIdHash[i]) {
721                 readentry(addr, &vlentry, &type);
722                 if (type != VL) {
723                     printf
724                         ("%s Id Hash %d: Bad entry at %u: Not a valid vlentry\n",
725                          vtype(i), j, addr);
726                     continue;
727                 }
728
729                 rindex = addr / sizeof(vlentry);
730                 if (record[rindex].addr != addr && record[rindex].addr) {
731                     printf
732                         ("INTERNAL ERROR: addresses %u and %u use same record slot %d\n",
733                          record[rindex].addr, addr, rindex);
734                 }
735                 if (record[rindex].type & hash) {
736                     printf
737                         ("%s Id Hash %d: Bad entry '%s': Already in the the hash table\n",
738                          vtype(i), j, vlentry.name);
739                     break;
740                 }
741                 record[rindex].type |= hash;
742                 record[rindex].type |= ref;
743
744                 chainlength++;
745                 count++;
746
747                 /* Hash the id and check if in correct hash table */
748                 if (IdHash(vlentry.volumeId[i]) != j) {
749                     printf
750                         ("%s Id Hash %d: Bad entry '%s': Incorrect Id hash chain (should be in %d)\n",
751                          vtype(i), j, vlentry.name,
752                          IdHash(vlentry.volumeId[i]));
753                 }
754             }
755
756             if (chainlength > longest)
757                 longest = chainlength;
758             if ((shortest == -1) || (chainlength < shortest))
759                 shortest = chainlength;
760         }
761         if (verbose) {
762             printf
763                 ("%d entries in %s hash, longest is %d, shortest is %d, average length is %f\n",
764                  count, vtype(i), longest, shortest,
765                  ((float)count / (float)HASHSIZE));
766         }
767     }
768     return 0;
769 }
770
771 /*
772  * Follow the free chain.
773  * Record we found it in the free chain within the record array.
774  */
775 void
776 FollowFreeChain(struct vlheader *header)
777 {
778     afs_int32 count = 0;
779     struct nvlentry vlentry;
780     afs_uint32 addr;
781     afs_int32 type, rindex;
782
783     /* Now follow the Free Chain */
784     if (verbose)
785         printf("Check Volume Free Chain\n");
786     for (addr = header->vital_header.freePtr; addr;
787          addr = vlentry.nextIdHash[0]) {
788         readentry(addr, &vlentry, &type);
789         if (type != FR) {
790             printf
791                 ("Free Chain %d: Bad entry at %u: Not a valid free vlentry (0x%x)\n",
792                  count, addr, type);
793             continue;
794         }
795
796         rindex = addr / sizeof(vlentry);
797         if (record[rindex].addr != addr && record[rindex].addr) {
798             printf
799                 ("INTERNAL ERROR: addresses %u and %u use same record slot %d\n",
800                  record[rindex].addr, addr, rindex);
801         }
802         if (record[rindex].type & FRC) {
803             printf("Free Chain: Bad entry at %u: Already in the free chain\n",
804                    addr);
805             break;
806         }
807         record[rindex].type |= FRC;
808
809         count++;
810     }
811     if (verbose)
812         printf("%d entries on free chain\n", count);
813     return 0;
814 }
815
816 /*
817  * Read each multihomed block and mark it as found in the record.
818  * Read each entry in each multihomed block and mark the serveraddrs
819  * array with the number of ip addresses found for this entry.
820  * 
821  * Then read the IpMappedAddr array in the header.
822  * Verify that multihomed entries base and index are valid and points to
823  * a good multhomed entry.
824  * Mark the serveraddrs array with 1 ip address for regular entries.
825  * 
826  * By the end, the severaddrs array will have a 0 if the entry has no 
827  * IP addresses in it or the count of the number of IP addresses.
828  *
829  * The code does not verify if there are duplicate IP addresses in the 
830  * list. The vlserver does this when a fileserver registeres itself.
831  */
832 void
833 CheckIpAddrs(struct vlheader *header)
834 {
835     int mhblocks = 0;
836     afs_int32 i, j, m, rindex;
837     afs_int32 mhentries, regentries;
838     afs_int32 caddrs[VL_MAX_ADDREXTBLKS];
839     char mhblock[VL_ADDREXTBLK_SIZE];
840     struct extentaddr *MHblock = (struct extentaddr *)mhblock;
841     struct extentaddr *e;
842     int ipindex, ipaddrs;
843     afsUUID nulluuid;
844
845     memset(&nulluuid, 0, sizeof(nulluuid));
846
847     if (verbose)
848         printf("Check Multihomed blocks\n");
849
850     if (header->SIT) {
851         /* Read the first MH block and from it, gather the 
852          * addresses of all the mh blocks.
853          */
854         readMH(header->SIT, MHblock);
855         if (MHblock->ex_flags != VLCONTBLOCK) {
856             printf
857                 ("Multihomed Block 0: Bad entry at %u: Not a valid multihomed block\n",
858                  header->SIT);
859         }
860
861         for (i = 0; i < VL_MAX_ADDREXTBLKS; i++) {
862             caddrs[i] = MHblock->ex_contaddrs[i];
863         }
864
865         if (header->SIT != caddrs[0]) {
866             printf
867                 ("MH block does not point to self %u in header, %u in block\n",
868                  header->SIT, caddrs[0]);
869         }
870
871         /* Now read each MH block and record it in the record array */
872         for (i = 0; i < VL_MAX_ADDREXTBLKS; i++) {
873             if (!caddrs[i])
874                 continue;
875
876             readMH(caddrs[i], MHblock);
877             if (MHblock->ex_flags != VLCONTBLOCK) {
878                 printf
879                     ("Multihomed Block 0: Bad entry at %u: Not a valid multihomed block\n",
880                      header->SIT);
881             }
882
883             rindex = caddrs[i] / sizeof(vlentry);
884             if (record[rindex].addr != caddrs[i] && record[rindex].addr) {
885                 printf
886                     ("INTERNAL ERROR: addresses %u and %u use same record slot %d\n",
887                      record[rindex].addr, caddrs[i], rindex);
888             }
889             if (record[rindex].type & FRC) {
890                 printf
891                     ("MH Blocks Chain %d: Bad entry at %u: Already a MH block\n",
892                      i, record[rindex].addr);
893                 break;
894             }
895             record[rindex].type |= MHC;
896
897             mhblocks++;
898
899             /* Read each entry in a multihomed block. 
900              * Find the pointer to the entry in the IpMappedAddr array and
901              * verify that the entry is good (has IP addresses in it).
902              */
903             mhentries = 0;
904             for (j = 1; j < VL_MHSRV_PERBLK; j++) {
905                 e = (struct extentaddr *)&(MHblock[j]);
906
907                 /* Search the IpMappedAddr array for the reference to this entry */
908                 for (ipindex = 0; ipindex < MAXSERVERID; ipindex++) {
909                     if (((header->IpMappedAddr[ipindex] & 0xff000000) ==
910                          0xff000000)
911                         &&
912                         (((header->
913                            IpMappedAddr[ipindex] & 0x00ff0000) >> 16) == i)
914                         && ((header->IpMappedAddr[ipindex] & 0x0000ffff) ==
915                             j)) {
916                         break;
917                     }
918                 }
919                 if (ipindex >= MAXSERVERID)
920                     ipindex = -1;
921                 else
922                     serveraddrs[ipindex] = -1;
923
924                 if (memcmp(&e->ex_hostuuid, &nulluuid, sizeof(afsUUID)) == 0) {
925                     if (ipindex != -1) {
926                         printf
927                             ("Server Addrs index %d references null MH block %d, index %d\n",
928                              ipindex, i, j);
929                         serveraddrs[ipindex] = 0;       /* avoids printing 2nd error below */
930                     }
931                     continue;
932                 }
933
934                 /* Step through each ip address and count the good addresses */
935                 ipaddrs = 0;
936                 for (m = 0; m < VL_MAXIPADDRS_PERMH; m++) {
937                     if (e->ex_addrs[m])
938                         ipaddrs++;
939                 }
940
941                 /* If we found any good ip addresses, mark it in the serveraddrs record */
942                 if (ipaddrs) {
943                     mhentries++;
944                     if (ipindex == -1) {
945                         printf
946                             ("MH block %d, index %d: Not referenced by server addrs\n",
947                              i, j);
948                     } else {
949                         serveraddrs[ipindex] = ipaddrs; /* It is good */
950                     }
951                 }
952
953                 if (listservers && ipaddrs) {
954                     printf("MH block %d, index %d:", i, j);
955                     for (m = 0; m < VL_MAXIPADDRS_PERMH; m++) {
956                         if (!e->ex_addrs[m])
957                             continue;
958                         printf(" %d.%d.%d.%d",
959                                (e->ex_addrs[m] & 0xff000000) >> 24,
960                                (e->ex_addrs[m] & 0x00ff0000) >> 16,
961                                (e->ex_addrs[m] & 0x0000ff00) >> 8,
962                                (e->ex_addrs[m] & 0x000000ff));
963                     }
964                     printf("\n");
965                 }
966             }
967 /*
968  *      if (mhentries != MHblock->ex_count) {
969  *         printf("MH blocks says it has %d entries (found %d)\n",
970  *                MHblock->ex_count, mhentries);
971  *      }
972  */
973         }
974     }
975     if (verbose)
976         printf("%d multihomed blocks\n", mhblocks);
977
978     /* Check the server addresses */
979     if (verbose)
980         printf("Check server addresses\n");
981     mhentries = regentries = 0;
982     for (i = 0; i <= MAXSERVERID; i++) {
983         if (header->IpMappedAddr[i]) {
984             if ((header->IpMappedAddr[i] & 0xff000000) == 0xff000000) {
985                 mhentries++;
986                 if (((header->IpMappedAddr[i] & 0x00ff0000) >> 16) >
987                     VL_MAX_ADDREXTBLKS)
988                     printf
989                         ("IP Addr for entry %d: Multihome block is bad (%d)\n",
990                          i, ((header->IpMappedAddr[i] & 0x00ff0000) >> 16));
991                 if (((header->IpMappedAddr[i] & 0x0000ffff) > VL_MHSRV_PERBLK)
992                     || ((header->IpMappedAddr[i] & 0x0000ffff) < 1))
993                     printf
994                         ("IP Addr for entry %d: Multihome index is bad (%d)\n",
995                          i, (header->IpMappedAddr[i] & 0x0000ffff));
996                 if (serveraddrs[i] == -1) {
997                     printf
998                         ("warning: IP Addr for entry %d: Multihome entry has no ip addresses\n",
999                          i);
1000                     serveraddrs[i] = 0;
1001                 }
1002                 if (listservers) {
1003                     printf("   Server ip addr %d = MH block %d, index %d\n",
1004                            i, (header->IpMappedAddr[i] & 0x00ff0000) >> 16,
1005                            (header->IpMappedAddr[i] & 0x0000ffff));
1006                 }
1007             } else {
1008                 regentries++;
1009                 serveraddrs[i] = 1;     /* It is good */
1010                 if (listservers) {
1011                     printf("   Server ip addr %d = %d.%d.%d.%d\n", i,
1012                            (header->IpMappedAddr[i] & 0xff000000) >> 24,
1013                            (header->IpMappedAddr[i] & 0x00ff0000) >> 16,
1014                            (header->IpMappedAddr[i] & 0x0000ff00) >> 8,
1015                            (header->IpMappedAddr[i] & 0x000000ff));
1016                 }
1017             }
1018         }
1019     }
1020     if (verbose) {
1021         printf("%d simple entries, %d multihomed entries, Total = %d\n",
1022                regentries, mhentries, mhentries + regentries);
1023     }
1024     return 0;
1025 }
1026
1027 void 
1028 FixBad(afs_uint32 idx, afs_uint32 addr, afs_uint32 type, afs_uint32 tmp, 
1029        struct nvlentry *vlentry) {
1030     SetHashEnd(addr, type, tmp);
1031     printf("linked unlinked chain %u (index %d) to end of chain\n", 
1032            tmp, ADDR(tmp));
1033 }
1034
1035 int
1036 WorkerBee(struct cmd_syndesc *as, void *arock)
1037 {
1038     char *dbfile;
1039     afs_int32 maxentries, type, tmp;
1040     struct vlheader header;
1041     struct nvlentry vlentry;
1042     int i, j, help = 0;
1043
1044     dbfile = as->parms[0].items->data;  /* -database */
1045     listuheader = (as->parms[1].items ? 1 : 0); /* -uheader  */
1046     listheader = (as->parms[2].items ? 1 : 0);  /* -vheader  */
1047     listservers = (as->parms[3].items ? 1 : 0); /* -servers  */
1048     listentries = (as->parms[4].items ? 1 : 0); /* -entries  */
1049     verbose = (as->parms[5].items ? 1 : 0);     /* -verbose  */
1050     fix = (as->parms[6].items ? 1 : 0); /* -fix  */
1051
1052     /* open the vldb database file */
1053     fd = open(dbfile, (fix > 0)?O_RDWR:O_RDONLY, 0);
1054     if (fd < 0) {
1055         printf("can't open file '%s'. error = %d\n", dbfile, errno);
1056         return 0;
1057     }
1058
1059     /* read the ubik header and the vldb database header */
1060     readUbikHeader();
1061     readheader(&header);
1062     if (header.vital_header.vldbversion < 3) {
1063         printf("does not support vldb with version less than 3\n");
1064         return 0;
1065     }
1066
1067     maxentries = (header.vital_header.eofPtr / sizeof(vlentry)) + 1;
1068     record = (struct er *)malloc(maxentries * sizeof(struct er));
1069     memset((char *)record, 0, (maxentries * sizeof(struct er)));
1070     memset((char *)serveraddrs, 0, sizeof(serveraddrs));
1071
1072     /* Will fill in the record array of entries it found */
1073     ReadAllEntries(&header);
1074     listentries = 0;            /* Listed all the entries */
1075
1076     /* Check the multihomed blocks for valid entries as well as
1077      * the IpMappedAddrs array in the header for valid entries.
1078      */
1079     CheckIpAddrs(&header);
1080
1081     /* Follow the hash tables */
1082     FollowNameHash(&header);
1083     FollowIdHash(&header);
1084
1085     /* Follow the chain of free entries */
1086     FollowFreeChain(&header);
1087
1088     /* Now check the record we have been keeping for inconsistancies
1089      * For valid vlentries, also check that the server we point to is 
1090      * valid (the serveraddrs array).
1091      */
1092     if (verbose)
1093         printf("Verify each volume entry\n");
1094     for (i = 0; i < maxentries; i++) {
1095         int nextp;
1096         int reft;
1097         int hash;
1098         int *nextpp;
1099         char *which;
1100
1101         if (record[i].type == 0)
1102             continue;
1103
1104         /* If a vlentry, verify that its name is valid, its name and ids are
1105          * on the hash chains, and its server numbers are good.
1106          */
1107         if (record[i].type & VL) {
1108             int foundbad = 0;
1109             char volidbuf[256];
1110
1111             readentry(record[i].addr, &vlentry, &type);
1112
1113             if (InvalidVolname(vlentry.name))
1114                 printf("Volume '%s' at addr %u has an invalid name\n",
1115                        vlentry.name, record[i].addr);
1116
1117             if (!(record[i].type & NH)) {
1118                 nextp = ADDR(vlentry.nextNameHash);
1119                 reft = REFN;
1120                 hash = NameHash(vlentry.name);
1121                 nextpp = &vlentry.nextNameHash;
1122                 which = "name";
1123                 sprintf(volidbuf, "");
1124                 foundbad = 1;
1125             }
1126
1127             if (vlentry.volumeId[0] && !(record[i].type & RWH)) {
1128                 nextp = ADDR(vlentry.nextIdHash[0]);
1129                 reft = REFRW;
1130                 hash = IdHash(vlentry.volumeId[0]);
1131                 nextpp = &(vlentry.nextIdHash[0]);
1132                 which = "RW";
1133                 sprintf(volidbuf, "id %u ", vlentry.volumeId[0]);
1134                 foundbad = 1;
1135             }
1136
1137             if (vlentry.volumeId[1] && !(record[i].type & ROH)) {
1138                 nextp = ADDR(vlentry.nextIdHash[1]);
1139                 reft = REFRO;
1140                 hash = IdHash(vlentry.volumeId[1]);
1141                 nextpp = &(vlentry.nextIdHash[1]);
1142                 which = "RO";
1143                 sprintf(volidbuf, "id %u ", vlentry.volumeId[1]);
1144                 foundbad = 1;
1145             }
1146
1147             if (vlentry.volumeId[2] && !(record[i].type & BKH)) {
1148                 nextp = ADDR(vlentry.nextIdHash[2]);
1149                 reft = REFBK;
1150                 hash = IdHash(vlentry.volumeId[2]);
1151                 nextpp = &(vlentry.nextIdHash[2]);
1152                 which = "BK";
1153                 sprintf(volidbuf, "id %u ", vlentry.volumeId[2]);
1154                 foundbad = 1;
1155             }
1156
1157             if (foundbad) {
1158                 printf("%d: Volume '%s' %snot found in %s hash %d", i, 
1159                        vlentry.name, volidbuf, which, hash);
1160                 if (nextp) {
1161                     printf(" (next %d", nextp);
1162                     if (!(record[nextp].type & reft)) {
1163                         printf(" not in chain ");
1164                         record[nextp].type |= reft;
1165                     } else if (nextp != 0) {
1166                         printf(" next in chain");
1167                         if (fix) {
1168                             printf(", unchaining");
1169                             *nextpp = 0;
1170                             writeentry(record[i].addr, &vlentry);
1171                         }
1172                     }
1173                     printf(")");
1174                 }
1175                 printf("\n");
1176             }
1177         
1178             for (j = 0; j < NMAXNSERVERS; j++) {
1179                 if ((vlentry.serverNumber[j] != 255)
1180                     && (serveraddrs[vlentry.serverNumber[j]] == 0)) {
1181                     printf
1182                         ("Volume '%s', index %d points to empty server entry %d\n",
1183                          vlentry.name, j, vlentry.serverNumber[j]);
1184                 }
1185             }
1186         
1187             if (record[i].type & 0xffff0f00)
1188                 printf
1189                     ("Volume '%s' id %u also found on other chains (0x%x)\n",
1190                      vlentry.name, vlentry.volumeId[0], record[i].type);
1191             
1192             /* A free entry */
1193         } else if (record[i].type & FR) {
1194             if (!(record[i].type & FRC))
1195                 printf("Free vlentry at %u not on free chain\n",
1196                        record[i].addr);
1197             
1198             if (record[i].type & 0xfffffdf0)
1199                 printf
1200                     ("Free vlentry at %u also found on other chains (0x%x)\n",
1201                      record[i].addr, record[i].type);
1202             
1203             /* A multihomed entry */
1204         } else if (record[i].type & MH) {
1205             if (!(record[i].type & MHC))
1206                 printf("Multihomed block at %u is orphaned\n",
1207                        record[i].addr);
1208             
1209             if (record[i].type & 0xfffffef0)
1210                 printf
1211                     ("Multihomed block at %u also found on other chains (0x%x)\n",
1212                      record[i].addr, record[i].type);
1213             
1214         } else {
1215             printf("Unknown entry type at %u (0x%x)\n", record[i].addr,
1216                    record[i].type);
1217         }
1218     }
1219
1220     /* By the time we get here, unchained entries are really unchained */
1221     printf("Scanning %u entries for possible repairs\n", maxentries);
1222     for (i = 0; i < maxentries; i++) {
1223         if (record[i].type & VL) {
1224             readentry(record[i].addr, &vlentry, &type);
1225             if (!(record[i].type & REFN) && (strlen(vlentry.name)>0)) {
1226                 printf("%d: Record %u (type 0x%x) not in a name chain\n", i, 
1227                        record[i].addr, record[i].type);
1228                 if (fix) {
1229                     if (header.VolnameHash[NameHash(vlentry.name)] == 0)
1230                         header.VolnameHash[NameHash(vlentry.name)] = record[i].addr;
1231                     else
1232                         FixBad(i, header.VolnameHash[NameHash(vlentry.name)], NH, record[i].addr, &vlentry);
1233                 }
1234             }
1235             if (vlentry.volumeId[0] && !(record[i].type & REFRW)) {
1236                 printf("%d: Record %u (type 0x%x) not in a RW chain\n", i,
1237                        record[i].addr, record[i].type);
1238                 if (fix) {
1239                     if (header.VolidHash[0][IdHash(vlentry.volumeId[0])] == 0)
1240                         header.VolidHash[0][IdHash(vlentry.volumeId[0])] = record[i].addr;
1241                     else
1242                         FixBad(i, header.VolidHash[0][IdHash(vlentry.volumeId[0])], RWH, record[i].addr, &vlentry);
1243                 }
1244             }
1245             if (vlentry.volumeId[1] && !(record[i].type & REFRO)) {
1246                 printf("%d: Record %u (type 0x%x) not in a RO chain\n", i, 
1247                        record[i].addr, record[i].type);
1248                 if (fix) {
1249                     if (header.VolidHash[1][IdHash(vlentry.volumeId[1])] == 0)
1250                         header.VolidHash[1][IdHash(vlentry.volumeId[1])] = record[i].addr;
1251                     else
1252                         FixBad(i, header.VolidHash[1][IdHash(vlentry.volumeId[1])], ROH, record[i].addr, &vlentry);
1253                 }
1254             }
1255             if (vlentry.volumeId[2] && !(record[i].type & REFBK)) {
1256                 printf("%d: Record %u (type 0x%x) not in a BK chain\n", i, 
1257                        record[i].addr, record[i].type);
1258                 if (fix) {
1259                     if (header.VolidHash[2][IdHash(vlentry.volumeId[2])] == 0)
1260                         header.VolidHash[2][IdHash(vlentry.volumeId[2])] = record[i].addr;
1261                     else
1262                         FixBad(i, header.VolidHash[2][IdHash(vlentry.volumeId[2])], BKH, record[i].addr, &vlentry);
1263                 }
1264
1265             }
1266         }
1267     }
1268     if (fix) 
1269         writeheader(&header);
1270
1271     return 0;
1272 }
1273
1274 int
1275 main(int argc, char **argv)
1276 {
1277     struct cmd_syndesc *ts;
1278
1279     setlinebuf(stdout);
1280
1281     ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "vldb check");
1282     cmd_AddParm(ts, "-database", CMD_SINGLE, CMD_REQUIRED, "vldb_file");
1283     cmd_AddParm(ts, "-uheader", CMD_FLAG, CMD_OPTIONAL,
1284                 "Display UBIK header");
1285     cmd_AddParm(ts, "-vheader", CMD_FLAG, CMD_OPTIONAL,
1286                 "Display VLDB header");
1287     cmd_AddParm(ts, "-servers", CMD_FLAG, CMD_OPTIONAL,
1288                 "Display server list");
1289     cmd_AddParm(ts, "-entries", CMD_FLAG, CMD_OPTIONAL, "Display entries");
1290     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");
1291     cmd_AddParm(ts, "-fix", CMD_FLAG, CMD_OPTIONAL, "attempt to patch the database (potentially dangerous)");
1292
1293     return cmd_Dispatch(argc, argv);
1294 }