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