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