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