Add printf format checks to afs_com_err()
[openafs.git] / src / bucoord / ubik_db_if.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 /* Interface and supporting routines for the backup system's ubik database */
11
12 #include <afsconfig.h>
13 #include <afs/stds.h>
14
15 #ifdef IGNORE_SOME_GCC_WARNINGS
16 # pragma GCC diagnostic warning "-Wstrict-prototypes"
17 #endif
18
19 #include <sys/types.h>
20 #include <fcntl.h>
21 #ifdef AFS_NT40_ENV
22 #include <winsock2.h>
23 #elif defined(AFS_SUN5_ENV)
24 #include <netdb.h>
25 #else
26 #include <sys/param.h>          /* for hostnames etc */
27 #endif
28 #include <afs/auth.h>
29 #include <afs/cellconfig.h>
30 #include <ubik.h>
31 #include <afs/volser.h>
32 #include <afs/volser_prototypes.h>
33 #include <afs/afsutil.h>
34 #include <afs/bubasics.h>
35 #include <afs/budb_client.h>
36 #include <afs/budb.h>
37 #include <afs/com_err.h>
38 #include <errno.h>
39
40 #include "bc.h"
41 #include "error_macros.h"
42 #include "bucoord_internal.h"
43 #include "bucoord_prototypes.h"
44
45 extern char *whoami;
46
47 /* -------------------------------------
48  * Globals 
49  * -------------------------------------
50  */
51
52 struct udbHandleS udbHandle;
53
54 /* -------------------------------------
55  * interface routines (alphabetic)
56  * -------------------------------------
57  */
58
59 afs_int32 bcdb_AddVolume(register struct budb_volumeEntry *veptr)
60 {
61     afs_int32 code;
62
63     code = ubik_BUDB_AddVolume(udbHandle.uh_client, 0, veptr);
64     return (code);
65 }
66
67 afs_int32 bcdb_AddVolumes(register struct budb_volumeEntry *veptr, afs_int32 count)
68 {
69     struct budb_volumeList volumeList;
70     afs_int32 code;
71
72     volumeList.budb_volumeList_len = count;
73     volumeList.budb_volumeList_val = veptr;
74     code = ubik_BUDB_AddVolumes(udbHandle.uh_client, 0, &volumeList);
75     return (code);
76 }
77
78
79 afs_int32 bcdb_CreateDump(register struct budb_dumpEntry *deptr)
80 {
81     afs_int32 code;
82
83     code = ubik_BUDB_CreateDump(udbHandle.uh_client, 0, deptr);
84     return (code);
85 }
86
87 afs_int32 bcdb_deleteDump(afs_int32 dumpID, afs_int32 fromTime, afs_int32 toTime, 
88   budb_dumpsList *dumps)
89 {
90     afs_int32 code;
91     budb_dumpsList dumpsList, *dumpsPtr;
92
93     dumpsList.budb_dumpsList_len = 0;
94     dumpsList.budb_dumpsList_val = 0;
95     dumpsPtr = (dumps ? dumps : &dumpsList);
96
97     code =
98         ubik_BUDB_DeleteDump(udbHandle.uh_client, 0, dumpID, fromTime,
99                   toTime, dumpsPtr);
100     if (dumpsList.budb_dumpsList_val)
101         free(dumpsList.budb_dumpsList_val);
102     return (code);
103 }
104
105 afs_int32 bcdb_listDumps (afs_int32 sflags, afs_int32 groupId,
106                           afs_int32 fromTime, afs_int32 toTime,
107                           budb_dumpsList *dumps, budb_dumpsList *flags)
108 {
109     afs_int32 code;
110     budb_dumpsList dumpsList, *dumpsPtr;
111     budb_dumpsList flagsList, *flagsPtr;
112
113     dumpsList.budb_dumpsList_len = 0;
114     dumpsList.budb_dumpsList_val = 0;
115     dumpsPtr = (dumps ? dumps : &dumpsList);
116
117     flagsList.budb_dumpsList_len = 0;
118     flagsList.budb_dumpsList_val = 0;
119     flagsPtr = (flags ? flags : &flagsList);
120
121     code =
122         ubik_BUDB_ListDumps(udbHandle.uh_client, 0, sflags, "", groupId,
123                   fromTime, toTime, dumpsPtr, flagsPtr);
124
125     if (dumpsList.budb_dumpsList_val)
126         free(dumpsList.budb_dumpsList_val);
127     if (flagsList.budb_dumpsList_val)
128         free(flagsList.budb_dumpsList_val);
129     return (code);
130 }
131
132
133 afs_int32 bcdb_DeleteVDP(char *dumpSetName, char *dumpPath, afs_int32 dumpID)
134 {
135     afs_int32 code;
136
137     code =
138         ubik_BUDB_DeleteVDP(udbHandle.uh_client, 0, dumpSetName,
139                   dumpPath, dumpID);
140     return (code);
141 }
142
143 /* bcdb_FindClone
144  *      Returns the clone time of a volume by going up the parent chain.
145  *      If no clone time is found, a clone time of 0 is returned, forcing
146  *      a full dump.
147  * entry:
148  *      dumpID - of the first dump to examine.
149  *      volName - name of the volume for whom a clone time is required
150  *      clonetime - ptr to vbl for returning result
151  * exit:
152  *      0 - clonetime set appropriately
153  *      -1 - error occured in traversing chain, clone time set to 0.
154  *      -2 - no clone times found, clone time set to 0
155  */
156
157 afs_int32 bcdb_FindClone(afs_int32 dumpID, char *volName, afs_int32 *clonetime)
158 {
159     afs_int32 code;
160     code =
161         ubik_BUDB_FindClone(udbHandle.uh_client, 0, dumpID, volName,
162                   clonetime);
163     return (code);
164 }
165
166 /* bcdb_FindDump
167  *      scan entire database for latest volume dump before adate.  Optimize
168  *      further by reading only the first line of the dump and if it is older
169  *      than the oldest acceptable dump we've found so far, we don't bother
170  *      scanning the dump file we've just opened
171  *
172  *      Used by restore code when restoring a user requested volume(s)
173  * entry:
174  *      volumeName - name of volume to match on
175  *      beforeDate - look for dumps older than this date
176  * exit:
177  *      deptr - desciptor of most recent dump
178  * notes:
179  *      should be able to implement this in a single call rather than
180  *      the current multiple bcdb_ call algorithm.
181  */
182
183 int
184 bcdb_FindDump(char *volumeName, afs_int32 beforeDate, 
185               struct budb_dumpEntry *deptr)
186 {
187     afs_int32 code;
188     code =
189         ubik_BUDB_FindDump(udbHandle.uh_client, 0, volumeName,
190                   beforeDate, deptr);
191     return (code);
192 }
193
194 /* bcdb_FindDumpByID
195  *      find a dump by id. Currently insists on a single return value.
196  * entry:
197  *      dumpID - id to lookup
198  * exit:
199  */
200 int
201 bcdb_FindDumpByID(afs_int32 dumpID, struct budb_dumpEntry *deptr)
202 {
203     register afs_int32 code;
204     afs_int32 nextindex;
205     afs_int32 dbTime;
206     budb_dumpList dl;
207
208     /* initialize the dump list */
209     dl.budb_dumpList_len = 0;
210     dl.budb_dumpList_val = 0;
211
212     /* outline algorithm */
213     code = ubik_BUDB_GetDumps(udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_DUMPID, "",    /* no name */
214                      dumpID,    /* start */
215                      0,         /* end */
216                      0,         /* index */
217                      &nextindex, &dbTime, &dl);
218
219     if ((code != 0)
220         || (dl.budb_dumpList_len != 1)  /* single retn val expected */
221         ) {
222 /*      printf("bcdb_FindDumpByID: code %d, nvalues %d\n",
223                code, dl.budb_dumpList_len); */
224         if (code == 0)
225             code = 1;           /* multiple id's */
226         goto error;
227     }
228
229     memcpy(deptr, dl.budb_dumpList_val, sizeof(*deptr));
230
231   exit:
232     if (dl.budb_dumpList_val) {
233         /* free any allocated structures */
234         free(dl.budb_dumpList_val);
235     }
236     return (code);
237
238   error:
239     memset(deptr, 0, sizeof(*deptr));
240     goto exit;
241 }
242
243 /* bcdb_FindLastVolClone
244  *      Returns the clone time, from the most recent dump of volName, when
245  *      dumped in the volume set volSetName, with dump schedule dumpName.
246  *      The clone time can be used to check if the volume has been correctly
247  *      re-cloned, and also is used as the time from which to do the current
248  *      incremental dump.
249  * entry:
250  *      volSetName - name of volume set
251  *      dumpName - full path of dump node
252  *      volName - name of volume for whom a clone time is required
253  *      clonetime - ptr to vbl for result
254  * exit:
255  *      0 - clonetime set appropriately
256  * notes:
257  *      used only for warning generation. Suggest that this be omitted.
258  */
259
260 afs_int32
261 bcdb_FindLastVolClone(char *volSetName, char *dumpName, char *volName, 
262                       afs_int32 *clonetime)
263 {
264     /* server notes
265      * search by dumpName
266      * match on volumeset and dump path
267      * search for the volume name
268      */
269     return (0);
270 }
271
272 /* bcdb_FindLatestDump
273  *      find the latest dump with volume set component avname and the
274  *      specified dump pathname. Used to find a dump, relative to which an
275  *      incremental dump can be done. Defines the parent <-> child relations
276  *      for restores.
277  * entry:
278  *      avname: volume set name
279  *      dumpPath: full path of dump node
280  * exit:
281  *      0:  adentry: dump entry structure filled in.
282  *      -1: probably an internal error
283  *      2: no such entry
284  * Notes for 4.0:
285  *      Need to store volumeset name in dump in order to implement this.
286  *      Need new routine since params are two strings
287  */
288
289 int
290 bcdb_FindLatestDump(char *volSetName, char *dumpPath, 
291                     struct budb_dumpEntry *deptr)
292 {
293     afs_int32 code;
294     code =
295         ubik_BUDB_FindLatestDump(udbHandle.uh_client, 0, volSetName,
296                   dumpPath, deptr);
297     return (code);
298 }
299
300
301 /* bcdb_FindTape
302  *      find a tape
303  * entry:
304  *      dumpid: dump id to which tape beint32s
305  *      tapeName: name of tape
306  */
307
308 int
309 bcdb_FindTape(afs_int32 dumpid, char *tapeName, 
310               struct budb_tapeEntry *teptr)
311 {
312     budb_tapeList tl;
313     afs_int32 next;
314     afs_int32 dbTime;
315     afs_int32 code = 0;
316
317     memset(teptr, 0, sizeof(*teptr));
318     tl.budb_tapeList_len = 0;
319     tl.budb_tapeList_val = 0;
320
321     code =
322         ubik_BUDB_GetTapes(udbHandle.uh_client, 0, BUDB_MAJORVERSION,
323                   BUDB_OP_TAPENAME | BUDB_OP_DUMPID, tapeName, dumpid, 0, 0,
324                   &next, &dbTime, &tl);
325
326     if (code)
327         ERROR(code);
328
329     if (tl.budb_tapeList_len != 1)
330         ERROR(BC_NOTUNIQUE);    /* expecting a single descriptor */
331
332     memcpy(teptr, tl.budb_tapeList_val, sizeof(*teptr));
333
334   error_exit:
335     if (tl.budb_tapeList_val)
336         free(tl.budb_tapeList_val);
337     return (code);
338 }
339
340 int
341 bcdb_FindTapeSeq(afs_int32 dumpid, afs_int32 tapeSeq, 
342                  struct budb_tapeEntry *teptr)
343 {
344     budb_tapeList tl;
345     afs_int32 next;
346     afs_int32 dbTime;
347     afs_int32 code = 0;
348
349     memset(teptr, 0, sizeof(*teptr));
350     tl.budb_tapeList_len = 0;
351     tl.budb_tapeList_val = 0;
352
353     code =
354         ubik_BUDB_GetTapes(udbHandle.uh_client, 0, BUDB_MAJORVERSION,
355                   BUDB_OP_TAPESEQ | BUDB_OP_DUMPID, "", dumpid, tapeSeq, 0,
356                   &next, &dbTime, &tl);
357     if (code)
358         ERROR(code);
359
360     if (tl.budb_tapeList_len != 1)
361         ERROR(BC_NOTUNIQUE);    /* expecting a single descriptor */
362
363     memcpy(teptr, tl.budb_tapeList_val, sizeof(*teptr));
364
365   error_exit:
366     if (tl.budb_tapeList_val)
367         free(tl.budb_tapeList_val);
368     return (code);
369 }
370
371 /* bcdb_FindVolumes
372  * notes: 
373  *      - this is part of dblookup. The existing semantics will not work since
374  *      they do lookups based on dump id.
375  *      - in the restore code, it uses this to extract information about
376  *      the volume. Need current semantics. Could filter the output, selecting
377  *      on the dumpid.
378  *      - Suggest that the lookup be based on volume name only, with optional
379  *      match on backup, and readonly volumes.
380  *      - Further, need to check if the volume structure returns enough
381  *      information
382  */
383
384 afs_int32
385 bcdb_FindVolumes(afs_int32 dumpID, char *volumeName, 
386                  struct budb_volumeEntry *returnArray, 
387                  afs_int32 last, afs_int32 *next, afs_int32 maxa, 
388                  afs_int32 *nEntries)
389 {
390     budb_volumeList vl;
391     afs_int32 dbTime;
392     afs_int32 code;
393
394     vl.budb_volumeList_len = maxa;
395     vl.budb_volumeList_val = returnArray;
396
397     /* outline algorithm */
398     code = ubik_BUDB_GetVolumes(udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_VOLUMENAME | BUDB_OP_DUMPID, volumeName,     /* name */
399                      dumpID,    /* start */
400                      0,         /* end */
401                      last,      /* index */
402                      next,      /* nextindex */
403                      &dbTime, &vl);
404
405     *nEntries = vl.budb_volumeList_len;
406     return (code);
407 }
408
409 int
410 bcdb_FinishDump(register struct budb_dumpEntry *deptr)
411 {
412     afs_int32 code;
413     code = ubik_BUDB_FinishDump(udbHandle.uh_client, 0, deptr);
414     return (code);
415 }
416
417 int
418 bcdb_FinishTape(register struct budb_tapeEntry *teptr)
419 {
420     afs_int32 code;
421     code = ubik_BUDB_FinishTape(udbHandle.uh_client, 0, teptr);
422     return (code);
423
424 }
425
426 /* bcdb_LookupVolumes
427  */
428
429 afs_int32
430 bcdb_LookupVolume(char *volumeName, struct budb_volumeEntry *returnArray, 
431                   afs_int32 last, afs_int32 *next, afs_int32 maxa, 
432                   afs_int32 *nEntries)
433 {
434     budb_volumeList vl;
435     afs_int32 dbTime;
436     afs_int32 code;
437
438     vl.budb_volumeList_len = maxa;
439     vl.budb_volumeList_val = returnArray;
440
441     /* outline algorithm */
442     code = ubik_BUDB_GetVolumes(udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_VOLUMENAME, volumeName,      /* name */
443                      0,         /* start */
444                      0,         /* end */
445                      last,      /* index */
446                      next,      /* nextindex */
447                      &dbTime, &vl);
448     if (code) {
449         *nEntries = 0;
450         return (code);
451     }
452     *nEntries = vl.budb_volumeList_len;
453     return (0);
454 }
455
456 int
457 bcdb_UseTape(struct budb_tapeEntry *teptr, afs_int32 *newFlag)
458 {
459     afs_int32 code;
460     code = ubik_BUDB_UseTape(udbHandle.uh_client, 0, teptr, newFlag);
461     return (code);
462 }
463
464
465 /* ---- text configuration handling routines ----
466  * 
467  * notes: 
468  *      The caller should pass in/out a fid for an unlinked, open file to prevent
469  *      tampering with the files contents; 
470  */
471
472 /* bcdb_GetTextFile
473  *      extract the specified textType and put it in a temporary, local
474  *      file.
475  * entry:
476  *      ctPtr - ptr to client structure with all the required information
477  */
478
479 int
480 bcdb_GetTextFile(register udbClientTextP ctPtr)
481 {
482     afs_int32 bufferSize;
483     afs_int32 offset, nextOffset;
484     charListT charList;
485     afs_int32 code = 0;
486
487     /* Initialize charlistT_val. We try to deallocate this structure based on
488      * this */
489     memset((void *)&charList, 0, sizeof(charList));
490
491     /* check params and cleanup any previous state */
492     if (ctPtr->lockHandle == 0)
493         ERROR(BUDB_INTERNALERROR);
494
495     if (ctPtr->textStream == NULL)      /* Should have an open stream */
496         ERROR(BUDB_INTERNALERROR);
497
498     /* allocate a buffer */
499     bufferSize = 1024;
500     charList.charListT_val = (char *)malloc(bufferSize);
501     if (charList.charListT_val == 0)
502         ERROR(BUDB_INTERNALERROR);
503     charList.charListT_len = bufferSize;
504
505     offset = 0;
506     nextOffset = 0;
507     ctPtr->textSize = 0;
508     while (nextOffset != -1) {
509         offset = nextOffset;
510         charList.charListT_len = bufferSize;
511         code =
512             ubik_BUDB_GetText(udbHandle.uh_client, 0, ctPtr->lockHandle,
513                       ctPtr->textType, bufferSize, offset, &nextOffset,
514                       &charList);
515
516         if (code)
517             ERROR(code);
518
519         code =
520             fwrite(charList.charListT_val, sizeof(char),
521                    charList.charListT_len, ctPtr->textStream);
522         if (ferror(ctPtr->textStream))
523             ERROR(BUDB_INTERNALERROR);
524
525         ctPtr->textSize += charList.charListT_len;
526     }
527
528     /* get text version */
529     code =
530         ubik_BUDB_GetTextVersion(udbHandle.uh_client, 0,
531                   ctPtr->textType, &ctPtr->textVersion);
532     if (code)
533         ERROR(code);
534
535   normal_exit:
536     fflush(ctPtr->textStream);  /* debug */
537
538     /* exit, leaving the configuration text file open */
539     if (charList.charListT_val)
540         free(charList.charListT_val);
541     return (code);
542
543   error_exit:
544     if (ctPtr->textStream != NULL) {
545         fclose(ctPtr->textStream);
546         ctPtr->textStream = NULL;
547     }
548     goto normal_exit;
549 }
550
551
552 /* bcdb_SaveTextFile
553  *      save the text file in ubik database
554  * entry:
555  *      textType - identifies type of configuration file
556  *      filename - where to get the text from
557  */
558
559 int
560 bcdb_SaveTextFile(udbClientTextP ctPtr)
561 {
562     afs_int32 bufferSize;
563     afs_int32 offset, chunkSize, fileSize;
564     charListT charList;
565     afs_int32 code = 0;
566
567     /* allocate a buffer */
568     bufferSize = 1024;
569     charList.charListT_val = (char *)malloc(bufferSize);
570     if (charList.charListT_val == 0)
571         ERROR(BUDB_INTERNALERROR);
572     charList.charListT_len = bufferSize;
573
574     if (ctPtr->textStream == NULL)
575         ERROR(BUDB_INTERNALERROR);
576     rewind(ctPtr->textStream);
577
578     fileSize = (afs_int32) filesize(ctPtr->textStream);
579
580     afs_dprintf(("filesize is %d\n", fileSize));
581
582     rewind(ctPtr->textStream);
583
584     /* special case empty files */
585     if (fileSize == 0) {
586         charList.charListT_len = 0;
587         code =
588             ubik_BUDB_SaveText(udbHandle.uh_client, 0,
589                       ctPtr->lockHandle, ctPtr->textType, 0,
590                       BUDB_TEXT_COMPLETE, &charList);
591         goto error_exit;
592     }
593
594     offset = 0;
595     while (fileSize != 0) {
596         chunkSize = MIN(fileSize, bufferSize);
597         code =
598             fread(charList.charListT_val, sizeof(char), chunkSize,
599                   ctPtr->textStream);
600
601         if (code != chunkSize)
602             printf("code = %d\n", code);
603         if (ferror(ctPtr->textStream))
604             ERROR(BUDB_INTERNALERROR);
605
606         charList.charListT_len = chunkSize;
607         code =
608             ubik_BUDB_SaveText(udbHandle.uh_client, 0,
609                       ctPtr->lockHandle, ctPtr->textType, offset,
610                       (chunkSize == fileSize) ? BUDB_TEXT_COMPLETE : 0,
611                       &charList);
612         if (code)
613             ERROR(code);
614
615         fileSize -= chunkSize;
616         offset += chunkSize;
617     }
618
619   error_exit:
620     /* if ( ctPtr->textStream >= 0 )
621      * close(ctPtr->textStream); */
622     if (charList.charListT_val)
623         free(charList.charListT_val);
624     return (code);
625 }
626
627 int
628 bcdb_FindLastTape(afs_int32 dumpID, struct budb_dumpEntry *dumpEntry,
629                   struct budb_tapeEntry *tapeEntry,
630                   struct budb_volumeEntry *volEntry)
631 {
632     return (ubik_BUDB_FindLastTape(udbHandle.uh_client, 0, dumpID, dumpEntry,
633              tapeEntry, volEntry));
634 }
635
636 int
637 bcdb_MakeDumpAppended(afs_int32 appendedDumpID, afs_int32 initialDumpID,
638                       afs_int32 startTapeSeq)
639 {
640     return (ubik_BUDB_MakeDumpAppended(udbHandle.uh_client, 0, appendedDumpID,
641              initialDumpID, startTapeSeq));
642 }
643
644
645 /* -------------------------------------
646  * misc. support routines
647  * -------------------------------------
648  */
649
650 afs_int32
651 filesize(FILE *stream)
652 {
653     afs_int32 offset;
654     afs_int32 size;
655
656     offset = ftell(stream);
657     fseek(stream, (afs_int32) 0, 2);    /* end of file */
658     size = ftell(stream);
659     fseek(stream, offset, 0);
660     return (size);
661 }
662
663
664 /* ------------------------------------
665  * misc. support routines - general text management
666  * ------------------------------------
667  */
668
669
670 /* bc_LockText
671  *      locks the text described by the ctPtr
672  * entry:
673  *      ctptr - client text ptr
674  * exit:
675  *      0 - success
676  *      n - fail
677  */
678
679 int
680 bc_LockText(udbClientTextP ctPtr)
681 {
682     afs_int32 code;
683     afs_int32 timeout, j = 0;
684
685     if (ctPtr->lockHandle != 0)
686         return (1);             /* already locked */
687
688     timeout =
689         ((ctPtr->textSize == 0) ? 30 : ((ctPtr->textSize / 50000) + 10));
690
691     while (1) {
692         code =
693             ubik_BUDB_GetLock(udbHandle.uh_client, 0,
694                       udbHandle.uh_instanceId, ctPtr->textType, timeout,
695                       &ctPtr->lockHandle);
696         if ((code != BUDB_LOCKED) && (code != BUDB_SELFLOCKED)) {
697             break;
698         }
699
700         /* Mention something every 30 seconds */
701         if (++j >= 30) {
702             afs_com_err(whoami, code,
703                     "; Waiting for db configuration text unlock");
704             j = 0;
705         }
706 #ifdef AFS_PTHREAD_ENV
707         sleep(1);
708 #else
709         IOMGR_Sleep(1);
710 #endif
711     }
712
713     /* cleanup */
714     if (code)
715         ctPtr->lockHandle = 0;
716     return (code);
717 }
718
719 /* bc_UnlockText
720  *      unlocks the text described by the ctPtr
721  * entry:
722  *      ctptr - client text ptr
723  * exit:
724  *      0 - success
725  *      n - fail
726  */
727
728 int
729 bc_UnlockText(udbClientTextP ctPtr)
730 {
731     afs_int32 code = 0;
732
733     if (ctPtr->lockHandle == 0)
734         return (0);
735
736     code =
737         ubik_BUDB_FreeLock(udbHandle.uh_client, 0, ctPtr->lockHandle);
738     ctPtr->lockHandle = 0;
739
740     /* Don't try to analyse the error. Let the lock timeout */
741     return (code);
742 }
743
744 /* bc_CheckTextVersion
745  * exit:
746  *      0 - version # ok
747  *      n - out of date or error
748  */
749
750 int
751 bc_CheckTextVersion(udbClientTextP ctPtr)
752 {
753     afs_int32 code;
754     afs_uint32 tversion;
755
756     if (ctPtr->textVersion == -1)
757         return (BC_VERSIONMISMATCH);
758
759     code =
760         ubik_BUDB_GetTextVersion(udbHandle.uh_client, 0,
761                   ctPtr->textType, &tversion);
762     if (code)
763         return (code);
764     if (tversion != ctPtr->textVersion)
765         return (BC_VERSIONMISMATCH);
766     return (0);
767 }
768
769 /* -------------------------------------
770  * initialization routines
771  * -------------------------------------
772  */
773
774 /* vldbClientInit 
775  *      Initialize a client for the vl ubik database.
776  */
777 int
778 vldbClientInit(int noAuthFlag, int localauth, char *cellName, 
779                struct ubik_client **cstruct, 
780                time_t *expires)
781 {
782     afs_int32 code = 0;
783     struct afsconf_dir *acdir;
784     struct rx_securityClass *sc;
785     afs_int32 i, scIndex = 0;   /* Index of Rx security object - noauth */
786     struct afsconf_cell info;
787     struct ktc_principal sname;
788     struct ktc_token *ttoken = NULL;
789     struct rx_connection *serverconns[VLDB_MAXSERVERS];
790
791
792     /* Find out about the given cell */
793     acdir =
794         afsconf_Open((localauth ? AFSDIR_SERVER_ETC_DIRPATH :
795                       AFSDIR_CLIENT_ETC_DIRPATH));
796     if (!acdir) {
797         afs_com_err(whoami, 0, "Can't open configuration directory '%s'",
798                 (localauth ? AFSDIR_SERVER_ETC_DIRPATH :
799                  AFSDIR_CLIENT_ETC_DIRPATH));
800         ERROR(BC_NOCELLCONFIG);
801     }
802
803     if (!cellName[0]) {
804         char cname[64];
805
806         code = afsconf_GetLocalCell(acdir, cname, sizeof(cname));
807         if (code) {
808             afs_com_err(whoami, code,
809                     "; Can't get the local cell name - check %s/%s",
810                     (localauth ? AFSDIR_SERVER_ETC_DIRPATH :
811                      AFSDIR_CLIENT_ETC_DIRPATH), AFSDIR_THISCELL_FILE);
812             ERROR(code);
813         }
814         strcpy(cellName, cname);
815     }
816
817     code = afsconf_GetCellInfo(acdir, cellName, AFSCONF_VLDBSERVICE, &info);
818     if (code) {
819         afs_com_err(whoami, code, "; Can't find cell %s's hosts in %s/%s",
820                 cellName,
821                 (localauth ? AFSDIR_SERVER_ETC_DIRPATH :
822                  AFSDIR_CLIENT_ETC_DIRPATH), AFSDIR_CELLSERVDB_FILE);
823         ERROR(BC_NOCELLCONFIG);
824     }
825
826     /*
827      * Grab tickets if we care about authentication.
828      */
829     *expires = 0;
830     if (localauth) {
831         code = afsconf_GetLatestKey(acdir, 0, 0);
832         if (code) {
833             afs_com_err(whoami, code, "; Can't get key from local key file");
834             ERROR(code);
835         } else {
836             code = afsconf_ClientAuth(acdir, &sc, &scIndex);
837             if (code) {
838                 afs_com_err(whoami, code, "; Calling ClientAuth");
839                 ERROR(code);
840             }
841
842             *expires = NEVERDATE;
843         }
844     } else {
845         if (!noAuthFlag) {
846             strcpy(sname.cell, info.name);
847             sname.instance[0] = 0;
848             strcpy(sname.name, "afs");
849
850             code =
851                 ktc_GetToken(&sname, ttoken, sizeof(struct ktc_token), NULL);
852             if (code) {
853                 afs_com_err(whoami, code,
854                             "; Can't get AFS tokens - running unauthenticated");
855             } else {
856                 if ((ttoken->kvno < 0) || (ttoken->kvno > 256))
857                     afs_com_err(whoami, 0,
858                             "Funny kvno (%d) in ticket, proceeding",
859                             ttoken->kvno);
860                 *expires = ttoken->endTime;
861                 scIndex = 2;
862             }
863         }
864
865         switch (scIndex) {
866         case 0:
867             sc = rxnull_NewClientSecurityObject();
868             break;
869         case 2:
870             sc = (struct rx_securityClass *)
871                 rxkad_NewClientSecurityObject(rxkad_clear,
872                                               &ttoken->sessionKey,
873                                               ttoken->kvno, ttoken->ticketLen,
874                                               ttoken->ticket);
875             break;
876         default:
877             afs_com_err(whoami, 0, "Unsupported authentication type %d", scIndex);
878             ERROR(-1);
879             break;
880         }
881     }
882
883     if (!sc) {
884         afs_com_err(whoami, 0,
885                 "Can't create a security object with security index %d",
886                 scIndex);
887         ERROR(-1);
888     }
889
890     /* tell UV module about default authentication */
891     UV_SetSecurity(sc, scIndex);
892
893     if (info.numServers > VLDB_MAXSERVERS) {
894         afs_com_err(whoami, 0,
895                 "Warning: %d VLDB servers exist for cell '%s', can only remember the first %d",
896                 info.numServers, cellName, VLDB_MAXSERVERS);
897         info.numServers = VLDB_MAXSERVERS;
898     }
899
900     for (i = 0; i < info.numServers; i++)
901         serverconns[i] =
902             rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
903                              info.hostAddr[i].sin_port, USER_SERVICE_ID, sc,
904                              scIndex);
905     serverconns[i] = 0;
906
907     *cstruct = 0;
908     code = ubik_ClientInit(serverconns, cstruct);
909     if (code) {
910         afs_com_err(whoami, code, "; Can't initialize ubik connection to vldb");
911         ERROR(code);
912     }
913
914   error_exit:
915     if (acdir)
916         afsconf_Close(acdir);
917     return (code);
918 }
919
920 /* udbClientInit
921  *      initialize a client for the backup systems ubik database.
922  */
923
924 afs_int32
925 udbClientInit(int noAuthFlag, int localauth, char *cellName)
926 {
927     struct ktc_principal principal;
928     struct ktc_token token;
929     struct afsconf_cell info;
930     struct afsconf_dir *acdir;
931     int i;
932     afs_int32 code = 0;
933
934     acdir =
935         afsconf_Open((localauth ? AFSDIR_SERVER_ETC_DIRPATH :
936                       AFSDIR_CLIENT_ETC_DIRPATH));
937     if (!acdir) {
938         afs_com_err(whoami, 0, "Can't open configuration directory '%s'",
939                 (localauth ? AFSDIR_SERVER_ETC_DIRPATH :
940                  AFSDIR_CLIENT_ETC_DIRPATH));
941         ERROR(BC_NOCELLCONFIG);
942     }
943
944     if (!cellName[0]) {
945         char cname[64];
946
947         code = afsconf_GetLocalCell(acdir, cname, sizeof(cname));
948         if (code) {
949             afs_com_err(whoami, code,
950                     "; Can't get the local cell name - check %s/%s",
951                     (localauth ? AFSDIR_SERVER_ETC_DIRPATH :
952                      AFSDIR_CLIENT_ETC_DIRPATH), AFSDIR_THISCELL_FILE);
953             ERROR(code);
954         }
955         strcpy(cellName, cname);
956     }
957
958     code = afsconf_GetCellInfo(acdir, cellName, 0, &info);
959     if (code) {
960         afs_com_err(whoami, code, "; Can't find cell %s's hosts in %s/%s",
961                 cellName,
962                 (localauth ? AFSDIR_SERVER_ETC_DIRPATH :
963                  AFSDIR_CLIENT_ETC_DIRPATH), AFSDIR_CELLSERVDB_FILE);
964         ERROR(BC_NOCELLCONFIG);
965     }
966
967     udbHandle.uh_scIndex = RX_SCINDEX_NULL;
968
969     if (localauth) {
970         code = afsconf_GetLatestKey(acdir, 0, 0);
971         if (code) {
972             afs_com_err(whoami, code, "; Can't get key from local key file");
973             ERROR(-1);
974         } else {
975             code =
976                 afsconf_ClientAuth(acdir, &udbHandle.uh_secobj,
977                                    &udbHandle.uh_scIndex);
978             if (code) {
979                 afs_com_err(whoami, code, "; Calling ClientAuth");
980                 ERROR(-1);
981             }
982         }
983     } else {
984         if (!noAuthFlag) {
985             /* setup principal */
986             strcpy(principal.cell, info.name);
987             principal.instance[0] = 0;
988             strcpy(principal.name, "afs");
989
990             /* get token */
991             code = ktc_GetToken(&principal, &token, sizeof(token), NULL);
992             if (code) {
993                 afs_com_err(whoami, code,
994                         "; Can't get tokens - running unauthenticated");
995             } else {
996                 if ((token.kvno < 0) || (token.kvno > 256))
997                     afs_com_err(whoami, 0,
998                             "Unexpected kvno (%d) in ticket - proceeding",
999                             token.kvno);
1000                 udbHandle.uh_scIndex = RX_SCINDEX_KAD;  /* Kerberos */
1001             }
1002         }
1003
1004         switch (udbHandle.uh_scIndex) {
1005         case 0:
1006             udbHandle.uh_secobj = rxnull_NewClientSecurityObject();
1007             break;
1008
1009         case 2:
1010             udbHandle.uh_secobj = (struct rx_securityClass *)
1011                 rxkad_NewClientSecurityObject(rxkad_clear, &token.sessionKey,
1012                                               token.kvno, token.ticketLen,
1013                                               token.ticket);
1014             break;
1015
1016         default:
1017             afs_com_err(whoami, 0, "Unsupported authentication type %d",
1018                     udbHandle.uh_scIndex);
1019             ERROR(-1);
1020             break;
1021         }
1022     }
1023
1024     if (!udbHandle.uh_secobj) {
1025         afs_com_err(whoami, 0,
1026                 "Can't create a security object with security index %d",
1027                 udbHandle.uh_scIndex);
1028         ERROR(-1);
1029     }
1030
1031     if (info.numServers > MAXSERVERS) {
1032         afs_com_err(whoami, 0,
1033                 "Warning: %d BDB servers exist for cell '%s', can only remember the first %d",
1034                 info.numServers, cellName, MAXSERVERS);
1035         info.numServers = MAXSERVERS;
1036     }
1037
1038     /* establish connections to the servers. Check for failed connections? */
1039     for (i = 0; i < info.numServers; i++) {
1040         udbHandle.uh_serverConn[i] =
1041             rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
1042                              htons(AFSCONF_BUDBPORT), BUDB_SERVICE,
1043                              udbHandle.uh_secobj, udbHandle.uh_scIndex);
1044     }
1045     udbHandle.uh_serverConn[i] = 0;
1046
1047     code = ubik_ClientInit(udbHandle.uh_serverConn, &udbHandle.uh_client);
1048     if (code) {
1049         afs_com_err(whoami, code,
1050                 "; Can't initialize ubik connection to backup database");
1051         ERROR(code);
1052     }
1053
1054     /* Try to quickly find a good site by setting deadtime low */
1055     for (i = 0; i < info.numServers; i++)
1056         rx_SetConnDeadTime(udbHandle.uh_client->conns[i], 1);
1057     code =
1058         ubik_BUDB_GetInstanceId(udbHandle.uh_client, 0,
1059                   &udbHandle.uh_instanceId);
1060
1061     /* Reset dead time back up to default */
1062     for (i = 0; i < info.numServers; i++)
1063         rx_SetConnDeadTime(udbHandle.uh_client->conns[i], 60);
1064
1065     /* If did not find a site on first quick pass, try again */
1066     if (code == -1)
1067         code =
1068             ubik_BUDB_GetInstanceId(udbHandle.uh_client, 0,
1069                       &udbHandle.uh_instanceId);
1070     if (code) {
1071         afs_com_err(whoami, code, "; Can't access backup database");
1072         ERROR(code);
1073     }
1074
1075   error_exit:
1076     if (acdir)
1077         afsconf_Close(acdir);
1078     return (code);
1079 }
1080
1081 /* -------------------------------------
1082  * specialized ubik support
1083  * -------------------------------------
1084  */
1085
1086 #include <rx/xdr.h>
1087 #include <rx/rx.h>
1088 #include <lock.h>
1089
1090 /* notes
1091  *      1) first call with SINGLESERVER set, record the server to be used.
1092  *      2) subsequent calls use that server. If a failure is encountered,
1093  *         the state is cleaned up and the error returned back to the caller.
1094  *      3) upon completion, the user must make a dummy call with
1095  *         END_SINGLESERVER set, to clean up state.
1096  *      4) if the vanilla ubik_Call is ever modified, the END_SINGLESERVER
1097  *         flag can be discarded. The first call without SINGLESERVER set
1098  *         can clean up the state.
1099  */
1100
1101 struct ubikCallState {
1102     afs_int32 ucs_flags;        /* state flags */
1103     afs_int32 ucs_selectedServer;       /* which server selected */
1104 };
1105
1106 static struct ubikCallState uServer;
1107
1108 /* ubik_Call_SingleServer
1109  *      variant of ubik_Call. This is used by the backup system to initiate
1110  *      a series of calls to a single ubik server. The first call sets up
1111  *      process state (not recorded in ubik) that requires all further calls
1112  *      of that group to be made to the same server process.
1113  *
1114  *      call this instead of stub and we'll guarantee to find a host that's up.
1115  *      in the future, we should also put in a protocol to find the sync site
1116  */
1117
1118 afs_int32
1119 ubik_Call_SingleServer(int (*aproc) (), struct ubik_client *aclient, 
1120                        afs_int32 aflags, char *p1, char *p2, char *p3, 
1121                        char *p4, char *p5, char *p6, char *p7, char *p8,
1122                        char *p9, char *p10, char *p11, char *p12, char *p13, 
1123                        char *p14, char *p15, char *p16)
1124 {
1125     register afs_int32 code;
1126     afs_int32 someCode, newHost, thisHost;
1127     register afs_int32 i;
1128     register afs_int32 count;
1129     int chaseCount;
1130     int pass;
1131     struct rx_connection *tc;
1132     struct rx_peer *rxp;
1133
1134     if ((aflags & (UF_SINGLESERVER | UF_END_SINGLESERVER)) != 0) {
1135         if (((aflags & UF_SINGLESERVER) != 0)
1136             && ((uServer.ucs_flags & UF_SINGLESERVER) != 0)
1137             ) {
1138
1139             /* have a selected server */
1140             tc = aclient->conns[uServer.ucs_selectedServer];
1141
1142             code =
1143                 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
1144                           p12, p13, p14, p15, p16);
1145             if (code) {
1146                 /* error. Clean up single server state */
1147                 memset(&uServer, 0, sizeof(uServer));
1148             }
1149             return (code);
1150         } else if ((aflags & UF_END_SINGLESERVER) != 0) {
1151             memset(&uServer, 0, sizeof(uServer));
1152             return (0);
1153         }
1154     }
1155
1156     someCode = UNOSERVERS;
1157     chaseCount = 0;
1158     pass = 0;
1159     count = 0;
1160     while (1) {                 /*w */
1161
1162         /* tc is the next conn to try */
1163         tc = aclient->conns[count];
1164         if (tc == 0) {
1165             if (pass == 0) {
1166                 pass = 1;       /* in pass 1, we look at down hosts, too */
1167                 count = 0;
1168                 continue;
1169             } else
1170                 break;          /* nothing left to try */
1171         }
1172         if (pass == 0 && (aclient->states[count] & CFLastFailed)) {
1173             count++;
1174             continue;           /* this guy's down, try someone else first */
1175         }
1176
1177         code =
1178             (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12,
1179                       p13, p14, p15, p16);
1180
1181         /* note that getting a UNOTSYNC error code back does *not* guarantee
1182          * that there is a sync site yet elected.  However, if there is a
1183          * sync site out there somewhere, and you're trying an operation that
1184          * requires a sync site, ubik will return UNOTSYNC, indicating the
1185          * operation won't work until you find a sync site
1186          */
1187         if (code == UNOTSYNC) { /*ns */
1188             /* means that this requires a sync site to work */
1189             someCode = code;    /* remember an error, if this fails */
1190
1191             /* now see if we can find the sync site host */
1192             code = VOTE_GetSyncSite(tc, &newHost);
1193             if (code == 0 && newHost != 0) {
1194                 newHost = htonl(newHost);       /* convert back to network order */
1195
1196                 /* position count at the appropriate slot in the client
1197                  * structure and retry. If we can't find in slot, we'll just
1198                  * continue through the whole list
1199                  */
1200                 for (i = 0; i < MAXSERVERS; i++) {      /*f */
1201                     rxp = rx_PeerOf(aclient->conns[i]);
1202                     if (!(thisHost = rx_HostOf(rxp))) {
1203                         count++;        /* host not found, try the next dude */
1204                         break;
1205                     }
1206                     if (thisHost == newHost) {
1207                         /* avoid asking in a loop */
1208                         if (chaseCount++ > 2)
1209                             break;
1210                         count = i;      /* we were told to use this one */
1211                         break;
1212                     }
1213                 }               /*f */
1214             } else
1215                 count++;        /* not directed, keep looking for a sync site */
1216             continue;
1217         } /*ns */
1218         else if (code == UNOQUORUM) {   /* this guy is still recovering */
1219             someCode = code;
1220             count++;
1221             continue;
1222         } else if (code < 0) {  /* network errors */
1223             someCode = code;
1224             aclient->states[count] |= CFLastFailed;
1225             count++;
1226             continue;
1227         } else {
1228             /* ok, operation worked */
1229             aclient->states[count] &= ~CFLastFailed;
1230             /* either misc ubik code, or misc application code (incl success)
1231              */
1232
1233             /* if the call succeeded, setup connection state for subsequent
1234              * calls
1235              */
1236             if ((code == 0)
1237                 && ((aflags & UF_SINGLESERVER) != 0)
1238                 ) {
1239                 /* need to save state */
1240                 uServer.ucs_flags = UF_SINGLESERVER;
1241                 uServer.ucs_selectedServer = count;
1242             }
1243
1244             return code;
1245         }
1246     }                           /*w */
1247     return someCode;
1248 }
1249
1250
1251 /* -------------------------------------
1252  * debug and test routines 
1253  * -------------------------------------
1254  */
1255
1256 /* udbLocalInit
1257  *      For testing only. Open a connect to the database server running on
1258  *      the local host
1259  * exit:
1260  *      0 - ubik connection established in the global udbHandle structure
1261  *      n - error.
1262  */
1263
1264 int
1265 udbLocalInit(void)
1266 {
1267     afs_int32 serverList[MAXSERVERS];
1268     char hostname[256];
1269     char *args[3];
1270     int i;
1271     afs_int32 code;
1272
1273     /* get our host name */
1274     gethostname(hostname, sizeof(hostname));
1275     /* strcpy(hostname, "hops"); */
1276
1277     args[0] = "";
1278     args[1] = "-servers";
1279     args[2] = hostname;
1280
1281     code = ubik_ParseClientList(3, args, serverList);
1282     if (code) {
1283         afs_com_err(whoami, code, "; udbLocalInit: parsing ubik server list");
1284         return (-1);
1285     }
1286
1287     udbHandle.uh_scIndex = RX_SCINDEX_NULL;
1288     udbHandle.uh_secobj = (struct rx_securityClass *)
1289         rxnull_NewClientSecurityObject();
1290
1291     for (i = 0; serverList[i] != 0; i++) {
1292         udbHandle.uh_serverConn[i] =
1293             rx_NewConnection(serverList[i], htons(AFSCONF_BUDBPORT),
1294                              BUDB_SERVICE, udbHandle.uh_secobj,
1295                              udbHandle.uh_scIndex);
1296         if (udbHandle.uh_serverConn[i] == 0) {
1297             afs_com_err(whoami, 0, "connection %d failed", i);
1298             continue;
1299         }
1300     }
1301     udbHandle.uh_serverConn[i] = 0;
1302     code = ubik_ClientInit(udbHandle.uh_serverConn, &udbHandle.uh_client);
1303     if (code) {
1304         afs_com_err(whoami, code, "; in ubik_ClientInit");
1305         return (code);
1306     }
1307
1308     code =
1309         ubik_BUDB_GetInstanceId(udbHandle.uh_client, 0,
1310                   &udbHandle.uh_instanceId);
1311     if (code) {
1312         afs_com_err(whoami, code, "; Can't estblish instance Id");
1313         return (code);
1314     }
1315
1316    /* abort: */
1317     return (0);
1318 }
1319
1320 /* bc_openTextFile - This function opens a temp file to read in the
1321  * config text recd from the bu server. On Unix, an unlink() is done on
1322  * the file as soon as it is opened, so when the program exits, the file will
1323  * be removed automatically, while being invisible while in use.
1324  * On NT, however, the file must be explicitly deleted after use with an unlink()
1325  * Input:
1326  *  Pointer to a udhClientTextP struct. The open stream ptr is stored in
1327  *           the udbClientTextP.textStream member.
1328  * Output: The temp file name is returned in tmpFileName. This should be used
1329  *   to delete the file when done with it.
1330  * Return Values: 
1331  *     !0: error code
1332  *     0: Success.
1333  */
1334 int
1335 bc_openTextFile(udbClientTextP ctPtr, char *tmpFileName)
1336 {
1337     int code = 0;
1338
1339     if (ctPtr->textStream != NULL)
1340         fclose(ctPtr->textStream);
1341
1342     sprintf(tmpFileName, "%s/bu_XXXXXX", gettmpdir());
1343 #ifdef AFS_LINUX20_ENV
1344     mkstemp(tmpFileName);
1345 #else
1346     mktemp(tmpFileName);
1347 #endif
1348     ctPtr->textStream = fopen(tmpFileName, "w+");
1349     if (ctPtr->textStream == NULL)
1350         ERROR(BUDB_INTERNALERROR);
1351
1352 #ifndef AFS_NT40_ENV            /* This can't be done on NT */
1353     /* make the file invisible to others */
1354     code = unlink(tmpFileName);
1355     if (code)
1356         ERROR(errno);
1357 #endif
1358
1359     afs_dprintf(("file is %s\n", tmpFileName));
1360
1361   normal_exit:
1362     return code;
1363
1364   error_exit:
1365     if (ctPtr->textStream != NULL) {
1366         fclose(ctPtr->textStream);
1367         ctPtr->textStream = NULL;
1368     }
1369     goto normal_exit;
1370 }
1371
1372
1373 /* bc_closeTextFile: This function closes any actual temp files associated with
1374  * a udbClientText structure. 
1375  * Input: ctPtr->textStream - stream to close
1376  *        tmpFileName - temp file name to delete
1377  * RetVal: 
1378  *    0  - Success
1379  *    !0 - error code
1380  */
1381 int
1382 bc_closeTextFile(udbClientTextP ctPtr, char *tmpFileName)
1383 {
1384     int code = 0;
1385
1386     if (ctPtr->textStream)
1387         fclose(ctPtr->textStream);
1388
1389     if (*tmpFileName) {         /* we have a valid name */
1390         code = unlink(tmpFileName);
1391         if (code < 0)
1392             code = errno;
1393     }
1394     return code;
1395 }