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