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