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