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