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