51d9262859b4cf8118377c47e36485219b506d71
[kopensolaris-gnu/glibc.git] / db2 / common / db_appinit.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7
8 #include "config.h"
9
10 #ifndef lint
11 static const char sccsid[] = "@(#)db_appinit.c  10.33 (Sleepycat) 8/28/97";
12 #endif /* not lint */
13
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/param.h>
16 #include <sys/stat.h>
17
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #endif
26
27 #include "db_int.h"
28 #include "shqueue.h"
29 #include "db_page.h"
30 #include "btree.h"
31 #include "hash.h"
32 #include "log.h"
33 #include "txn.h"
34 #include "clib_ext.h"
35 #include "common_ext.h"
36
37 static int __db_home __P((DB_ENV *, const char *, int));
38 static int __db_parse __P((DB_ENV *, char *));
39 static int __db_tmp_dir __P((DB_ENV *, int));
40 static int __db_tmp_open __P((DB_ENV *, char *, int *));
41
42 /*
43  * db_version --
44  *      Return verision information.
45  */
46 char *
47 db_version(majverp, minverp, patchp)
48         int *majverp, *minverp, *patchp;
49 {
50         if (majverp != NULL)
51                 *majverp = DB_VERSION_MAJOR;
52         if (minverp != NULL)
53                 *minverp = DB_VERSION_MINOR;
54         if (patchp != NULL)
55                 *patchp = DB_VERSION_PATCH;
56         return ((char *)DB_VERSION_STRING);
57 }
58
59 /*
60  * db_appinit --
61  *      Initialize the application environment.
62  */
63 int
64 db_appinit(db_home, db_config, dbenv, flags)
65         const char *db_home;
66         char * const *db_config;
67         DB_ENV *dbenv;
68         int flags;
69 {
70         FILE *fp;
71         int ret;
72         char *lp, **p, buf[MAXPATHLEN * 2];
73
74         /* Validate arguments. */
75         if (dbenv == NULL)
76                 return (EINVAL);
77 #ifdef HAVE_SPINLOCKS
78 #define OKFLAGS                                                         \
79    (DB_CREATE | DB_NOMMAP | DB_THREAD | DB_INIT_LOCK | DB_INIT_LOG |    \
80     DB_INIT_MPOOL | DB_INIT_TXN | DB_MPOOL_PRIVATE | DB_RECOVER |       \
81     DB_RECOVER_FATAL | DB_TXN_NOSYNC | DB_USE_ENVIRON | DB_USE_ENVIRON_ROOT)
82 #else
83 #define OKFLAGS                                                         \
84    (DB_CREATE | DB_NOMMAP | DB_INIT_LOCK | DB_INIT_LOG |                \
85     DB_INIT_MPOOL | DB_INIT_TXN | DB_MPOOL_PRIVATE | DB_RECOVER |       \
86     DB_RECOVER_FATAL | DB_TXN_NOSYNC | DB_USE_ENVIRON | DB_USE_ENVIRON_ROOT)
87 #endif
88         if ((ret = __db_fchk(dbenv, "db_appinit", flags, OKFLAGS)) != 0)
89                 return (ret);
90
91 #define RECOVERY_FLAGS (DB_CREATE | DB_INIT_TXN | DB_INIT_LOG)
92         if (LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL) &&
93             LF_ISSET(RECOVERY_FLAGS) != RECOVERY_FLAGS)
94                 return (__db_ferr(dbenv, "db_appinit", 1));
95
96         fp = NULL;
97
98         /* Set the database home. */
99         if ((ret = __db_home(dbenv, db_home, flags)) != 0)
100                 goto err;
101
102         /* Parse the config array. */
103         for (p = (char **)db_config; p != NULL && *p != NULL; ++p)
104                 if ((ret = __db_parse(dbenv, *p)) != 0)
105                         goto err;
106
107         /* Parse the config file. */
108         if (dbenv->db_home != NULL) {
109                 (void)snprintf(buf,
110                     sizeof(buf), "%s/DB_CONFIG", dbenv->db_home);
111                 if ((fp = fopen(buf, "r")) != NULL) {
112                         while (fgets(buf, sizeof(buf), fp) != NULL) {
113                                 if ((lp = strchr(buf, '\n')) != NULL)
114                                         *lp = '\0';
115                                 if ((ret = __db_parse(dbenv, buf)) != 0)
116                                         goto err;
117                         }
118                         (void)fclose(fp);
119                         fp = NULL;
120                 }
121         }
122
123         /* Set up the tmp directory path. */
124         if (dbenv->db_tmp_dir == NULL &&
125             (ret = __db_tmp_dir(dbenv, flags)) != 0)
126                 goto err;
127
128         /* Indicate that the path names have been set. */
129         F_SET(dbenv, DB_APP_INIT);
130
131         /*
132          * If we are doing recovery, remove all the regions.
133          */
134         if (LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL)) {
135                 /* Remove all the old shared memory regions.  */
136                 if ((ret = log_unlink(NULL, 1 /* force */, dbenv)) != 0)
137                         goto err;
138                 if ((ret = memp_unlink(NULL, 1 /* force */, dbenv)) != 0)
139                         goto err;
140                 if ((ret = lock_unlink(NULL, 1 /* force */, dbenv)) != 0)
141                         goto err;
142                 if ((ret = txn_unlink(NULL, 1 /* force */, dbenv)) != 0)
143                         goto err;
144         }
145
146         /* Transactions imply logging. */
147         if (LF_ISSET(DB_INIT_TXN))
148                 LF_SET(DB_INIT_LOG);
149
150         /* Default permissions are 0660. */
151 #undef  DB_DEFPERM
152 #define DB_DEFPERM      (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
153
154         /* Initialize the subsystems. */
155         if (LF_ISSET(DB_INIT_LOCK) && (ret = lock_open(NULL,
156             LF_ISSET(DB_CREATE | DB_THREAD),
157             DB_DEFPERM, dbenv, &dbenv->lk_info)) != 0)
158                 goto err;
159         if (LF_ISSET(DB_INIT_LOG) && (ret = log_open(NULL,
160             LF_ISSET(DB_CREATE | DB_THREAD),
161             DB_DEFPERM, dbenv, &dbenv->lg_info)) != 0)
162                 goto err;
163         if (LF_ISSET(DB_INIT_MPOOL) && (ret = memp_open(NULL,
164             LF_ISSET(DB_CREATE | DB_MPOOL_PRIVATE | DB_NOMMAP | DB_THREAD),
165             DB_DEFPERM, dbenv, &dbenv->mp_info)) != 0)
166                 goto err;
167         if (LF_ISSET(DB_INIT_TXN) && (ret = txn_open(NULL,
168             LF_ISSET(DB_CREATE | DB_THREAD | DB_TXN_NOSYNC),
169             DB_DEFPERM, dbenv, &dbenv->tx_info)) != 0)
170                 goto err;
171
172         /* Initialize recovery. */
173         if (LF_ISSET(DB_INIT_TXN)) {
174                 if ((ret = __bam_init_recover(dbenv)) != 0)
175                         goto err;
176                 if ((ret = __db_init_recover(dbenv)) != 0)
177                         goto err;
178                 if ((ret = __ham_init_recover(dbenv)) != 0)
179                         goto err;
180                 if ((ret = __log_init_recover(dbenv)) != 0)
181                         goto err;
182                 if ((ret = __txn_init_recover(dbenv)) != 0)
183                         goto err;
184         }
185
186         /* Run recovery if necessary. */
187         if (LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL) && (ret =
188             __db_apprec(dbenv, LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL))) != 0)
189                 goto err;
190
191         return (ret);
192
193 err:    if (fp != NULL)
194                 (void)fclose(fp);
195
196         (void)db_appexit(dbenv);
197         return (ret);
198 }
199
200 /*
201  * db_appexit --
202  *      Close down the default application environment.
203  */
204 int
205 db_appexit(dbenv)
206         DB_ENV *dbenv;
207 {
208         int ret, t_ret;
209         char **p;
210
211         ret = 0;
212
213         /* Close subsystems. */
214         if (dbenv->tx_info && (t_ret = txn_close(dbenv->tx_info)) != 0)
215                 if (ret == 0)
216                         ret = t_ret;
217         if (dbenv->mp_info && (t_ret = memp_close(dbenv->mp_info)) != 0)
218                 if (ret == 0)
219                         ret = t_ret;
220         if (dbenv->lg_info && (t_ret = log_close(dbenv->lg_info)) != 0)
221                 if (ret == 0)
222                         ret = t_ret;
223         if (dbenv->lk_info && (t_ret = lock_close(dbenv->lk_info)) != 0)
224                 if (ret == 0)
225                         ret = t_ret;
226
227         /* Free allocated memory. */
228         if (dbenv->db_home != NULL)
229                 FREES(dbenv->db_home);
230         if ((p = dbenv->db_data_dir) != NULL) {
231                 for (; *p != NULL; ++p)
232                         FREES(*p);
233                 FREE(dbenv->db_data_dir, dbenv->data_cnt * sizeof(char **));
234         }
235         if (dbenv->db_log_dir != NULL)
236                 FREES(dbenv->db_log_dir);
237         if (dbenv->db_tmp_dir != NULL)
238                 FREES(dbenv->db_tmp_dir);
239
240         return (ret);
241 }
242
243 #define DB_ADDSTR(str) {                                                \
244         if ((str) != NULL) {                                            \
245                 /* If leading slash, start over. */                     \
246                 if (__db_abspath(str)) {                                \
247                         p = start;                                      \
248                         slash = 0;                                      \
249                 }                                                       \
250                 /* Append to the current string. */                     \
251                 len = strlen(str);                                      \
252                 if (slash)                                              \
253                         *p++ = PATH_SEPARATOR[0];                       \
254                 memcpy(p, str, len);                                    \
255                 p += len;                                               \
256                 slash = strchr(PATH_SEPARATOR, p[-1]) == NULL;          \
257         }                                                               \
258 }
259
260 /*
261  * __db_appname --
262  *      Given an optional DB environment, directory and file name and type
263  *      of call, build a path based on the db_appinit(3) rules, and return
264  *      it in allocated space.
265  *
266  * PUBLIC: int __db_appname __P((DB_ENV *,
267  * PUBLIC:    APPNAME, const char *, const char *, int *, char **));
268  */
269 int
270 __db_appname(dbenv, appname, dir, file, fdp, namep)
271         DB_ENV *dbenv;
272         APPNAME appname;
273         const char *dir, *file;
274         int *fdp;
275         char **namep;
276 {
277         DB_ENV etmp;
278         size_t len;
279         int ret, slash, tmp_create, tmp_free;
280         const char *a, *b, *c;
281         int data_entry;
282         char *p, *start;
283
284         a = b = c = NULL;
285         data_entry = -1;
286         tmp_create = tmp_free = 0;
287
288         /*
289          * We don't return a name when creating temporary files, just an fd.
290          * Default to error now.
291          */
292         if (fdp != NULL)
293                 *fdp = -1;
294         if (namep != NULL)
295                 *namep = NULL;
296
297         /*
298          * Absolute path names are never modified.  If the file is an absolute
299          * path, we're done.  If the directory is, simply append the file and
300          * return.
301          */
302         if (file != NULL && __db_abspath(file))
303                 return ((*namep = (char *)strdup(file)) == NULL ? ENOMEM : 0);
304         if (dir != NULL && __db_abspath(dir)) {
305                 a = dir;
306                 goto done;
307         }
308
309         /*
310          * DB_ENV  DIR     APPNAME         RESULT
311          * -------------------------------------------
312          * null    null    none            <tmp>/file
313          * null    set     none            DIR/file
314          * set     null    none            DB_HOME/file
315          * set     set     none            DB_HOME/DIR/file
316          *
317          * DB_ENV  FILE    APPNAME         RESULT
318          * -------------------------------------------
319          * null    null    DB_APP_DATA     <tmp>/<create>
320          * null    set     DB_APP_DATA     ./file
321          * set     null    DB_APP_DATA     <tmp>/<create>
322          * set     set     DB_APP_DATA     DB_HOME/DB_DATA_DIR/file
323          *
324          * DB_ENV  DIR     APPNAME         RESULT
325          * -------------------------------------------
326          * null    null    DB_APP_LOG      <tmp>/file
327          * null    set     DB_APP_LOG      DIR/file
328          * set     null    DB_APP_LOG      DB_HOME/DB_LOG_DIR/file
329          * set     set     DB_APP_LOG      DB_HOME/DB_LOG_DIR/DIR/file
330          *
331          * DB_ENV          APPNAME         RESULT
332          * -------------------------------------------
333          * null            DB_APP_TMP      <tmp>/<create>
334          * set             DB_APP_TMP      DB_HOME/DB_TMP_DIR/<create>
335          */
336 retry:  switch (appname) {
337         case DB_APP_NONE:
338                 if (dbenv == NULL || !F_ISSET(dbenv, DB_APP_INIT)) {
339                         if (dir == NULL)
340                                 goto tmp;
341                         a = dir;
342                 } else {
343                         a = dbenv->db_home;
344                         b = dir;
345                 }
346                 break;
347         case DB_APP_DATA:
348                 if (dir != NULL) {
349                         __db_err(dbenv,
350                             "DB_APP_DATA: illegal directory specification");
351                         return (EINVAL);
352                 }
353
354                 if (file == NULL) {
355                         tmp_create = 1;
356                         goto tmp;
357                 }
358                 if (dbenv == NULL || !F_ISSET(dbenv, DB_APP_INIT))
359                         a = PATH_DOT;
360                 else {
361                         a = dbenv->db_home;
362                         if (dbenv->db_data_dir != NULL &&
363                             (b = dbenv->db_data_dir[++data_entry]) == NULL) {
364                                 data_entry = -1;
365                                 b = dbenv->db_data_dir[0];
366                         }
367                 }
368                 break;
369         case DB_APP_LOG:
370                 if (dbenv == NULL || !F_ISSET(dbenv, DB_APP_INIT)) {
371                         if (dir == NULL)
372                                 goto tmp;
373                         a = dir;
374                 } else {
375                         a = dbenv->db_home;
376                         b = dbenv->db_log_dir;
377                         c = dir;
378                 }
379                 break;
380         case DB_APP_TMP:
381                 if (dir != NULL || file != NULL) {
382                         __db_err(dbenv,
383                     "DB_APP_TMP: illegal directory or file specification");
384                         return (EINVAL);
385                 }
386
387                 tmp_create = 1;
388                 if (dbenv == NULL || !F_ISSET(dbenv, DB_APP_INIT))
389                         goto tmp;
390                 else {
391                         a = dbenv->db_home;
392                         b = dbenv->db_tmp_dir;
393                 }
394                 break;
395         }
396
397         /* Reference a file from the appropriate temporary directory. */
398         if (0) {
399 tmp:            if (dbenv == NULL || !F_ISSET(dbenv, DB_APP_INIT)) {
400                         memset(&etmp, 0, sizeof(etmp));
401                         if ((ret = __db_tmp_dir(&etmp, DB_USE_ENVIRON)) != 0)
402                                 return (ret);
403                         tmp_free = 1;
404                         a = etmp.db_tmp_dir;
405                 } else
406                         a = dbenv->db_tmp_dir;
407         }
408
409 done:   len =
410             (a == NULL ? 0 : strlen(a) + 1) +
411             (b == NULL ? 0 : strlen(b) + 1) +
412             (c == NULL ? 0 : strlen(c) + 1) +
413             (file == NULL ? 0 : strlen(file) + 1);
414
415         if ((start = (char *)malloc(len)) == NULL) {
416                 __db_err(dbenv, "%s", strerror(ENOMEM));
417                 if (tmp_free)
418                         FREES(etmp.db_tmp_dir);
419                 return (ENOMEM);
420         }
421
422         slash = 0;
423         p = start;
424         DB_ADDSTR(a);
425         DB_ADDSTR(b);
426         DB_ADDSTR(file);
427         *p = '\0';
428
429         /*
430          * If we're opening a data file, see if it exists.  If it does,
431          * return it, otherwise, try and find another one to open.
432          */
433         if (data_entry != -1 && __db_exists(start, NULL) != 0) {
434                 FREES(start);
435                 a = b = c = NULL;
436                 goto retry;
437         }
438
439         /* Discard any space allocated to find the temp directory. */
440         if (tmp_free)
441                 FREES(etmp.db_tmp_dir);
442
443         /* Create the file if so requested. */
444         if (tmp_create) {
445                 ret = __db_tmp_open(dbenv, start, fdp);
446                 FREES(start);
447         } else {
448                 *namep = start;
449                 ret = 0;
450         }
451         return (ret);
452 }
453
454 /*
455  * __db_home --
456  *      Find the database home.
457  */
458 static int
459 __db_home(dbenv, db_home, flags)
460         DB_ENV *dbenv;
461         const char *db_home;
462         int flags;
463 {
464         const char *p;
465
466         p = db_home;
467
468         /* Use the environment if it's permitted and initialized. */
469 #ifdef HAVE_GETUID
470         if (LF_ISSET(DB_USE_ENVIRON) ||
471             (LF_ISSET(DB_USE_ENVIRON_ROOT) && getuid() == 0)) {
472 #else
473         if (LF_ISSET(DB_USE_ENVIRON)) {
474 #endif
475                 if ((p = getenv("DB_HOME")) == NULL)
476                         p = db_home;
477                 else if (p[0] == '\0') {
478                         __db_err(dbenv,
479                             "illegal DB_HOME environment variable");
480                         return (EINVAL);
481                 }
482         }
483
484         if (p == NULL)
485                 return (0);
486
487         if ((dbenv->db_home = (char *)strdup(p)) == NULL) {
488                 __db_err(dbenv, "%s", strerror(ENOMEM));
489                 return (ENOMEM);
490         }
491         return (0);
492 }
493
494 /*
495  * __db_parse --
496  *      Parse a single NAME VALUE pair.
497  */
498 static int
499 __db_parse(dbenv, s)
500         DB_ENV *dbenv;
501         char *s;
502 {
503         int ret;
504         char *local_s, *name, *value, **p, *tp;
505
506         ret = 0;
507
508         /*
509          * We need to strdup the argument in case the caller passed us
510          * static data.
511          */
512         if ((local_s = (char *)strdup(s)) == NULL)
513                 return (ENOMEM);
514
515         tp = local_s;
516         while ((name = strsep(&tp, " \t")) != NULL && *name == '\0');
517         if (name == NULL)
518                 goto illegal;
519         while ((value = strsep(&tp, " \t")) != NULL && *value == '\0');
520         if (value == NULL) {
521 illegal:        ret = EINVAL;
522                 __db_err(dbenv, "illegal name-value pair: %s", s);
523                 goto err;
524         }
525
526 #define DATA_INIT_CNT   20                      /* Start with 20 data slots. */
527         if (!strcmp(name, "DB_DATA_DIR")) {
528                 if (dbenv->db_data_dir == NULL) {
529                         if ((dbenv->db_data_dir = (char **)calloc(DATA_INIT_CNT,
530                             sizeof(char **))) == NULL)
531                                 goto nomem;
532                         dbenv->data_cnt = DATA_INIT_CNT;
533                 } else if (dbenv->data_next == dbenv->data_cnt - 1) {
534                         dbenv->data_cnt *= 2;
535                         if ((dbenv->db_data_dir =
536                             (char **)realloc(dbenv->db_data_dir,
537                             dbenv->data_cnt * sizeof(char **))) == NULL)
538                                 goto nomem;
539                 }
540                 p = &dbenv->db_data_dir[dbenv->data_next++];
541         } else if (!strcmp(name, "DB_LOG_DIR")) {
542                 if (dbenv->db_log_dir != NULL)
543                         FREES(dbenv->db_log_dir);
544                 p = &dbenv->db_log_dir;
545         } else if (!strcmp(name, "DB_TMP_DIR")) {
546                 if (dbenv->db_tmp_dir != NULL)
547                         FREES(dbenv->db_tmp_dir);
548                 p = &dbenv->db_tmp_dir;
549         } else
550                 goto err;
551
552         if ((*p = (char *)strdup(value)) == NULL) {
553 nomem:          ret = ENOMEM;
554                 __db_err(dbenv, "%s", strerror(ENOMEM));
555         }
556
557 err:    FREES(local_s);
558         return (ret);
559 }
560
561 #ifdef macintosh
562 #include <TFileSpec.h>
563
564 static char *sTempFolder;
565 #endif
566
567 /*
568  * tmp --
569  *      Set the temporary directory path.
570  */
571 static int
572 __db_tmp_dir(dbenv, flags)
573         DB_ENV *dbenv;
574         int flags;
575 {
576         static const char * list[] = {  /* Ordered: see db_appinit(3). */
577                 "/var/tmp",
578                 "/usr/tmp",
579                 "/temp",                /* WIN32. */
580                 "/tmp",
581                 "C:/temp",              /* WIN32. */
582                 "C:/tmp",               /* WIN32. */
583                 NULL
584         };
585         const char **lp, *p;
586
587         /* Use the environment if it's permitted and initialized. */
588         p = NULL;
589 #ifdef HAVE_GETEUID
590         if (LF_ISSET(DB_USE_ENVIRON) ||
591             (LF_ISSET(DB_USE_ENVIRON_ROOT) && getuid() == 0)) {
592 #else
593         if (LF_ISSET(DB_USE_ENVIRON)) {
594 #endif
595                 if ((p = getenv("TMPDIR")) != NULL && p[0] == '\0') {
596                         __db_err(dbenv, "illegal TMPDIR environment variable");
597                         return (EINVAL);
598                 }
599                 /* WIN32 */
600                 if (p == NULL && (p = getenv("TEMP")) != NULL && p[0] == '\0') {
601                         __db_err(dbenv, "illegal TEMP environment variable");
602                         return (EINVAL);
603                 }
604                 /* WIN32 */
605                 if (p == NULL && (p = getenv("TMP")) != NULL && p[0] == '\0') {
606                         __db_err(dbenv, "illegal TMP environment variable");
607                         return (EINVAL);
608                 }
609                 /* Macintosh */
610                 if (p == NULL &&
611                     (p = getenv("TempFolder")) != NULL && p[0] == '\0') {
612                         __db_err(dbenv,
613                             "illegal TempFolder environment variable");
614                         return (EINVAL);
615                 }
616         }
617
618 #ifdef macintosh
619         /* Get the path to the temporary folder. */
620         if (p == NULL) {
621                 FSSpec spec;
622
623                 if (!Special2FSSpec(kTemporaryFolderType,
624                     kOnSystemDisk, 0, &spec)) {
625                         p = FSp2FullPath(&spec);
626                         sTempFolder = malloc(strlen(p) + 1);
627                         strcpy(sTempFolder, p);
628                         p = sTempFolder;
629                 }
630         }
631 #endif
632
633         /* Step through the list looking for a possibility. */
634         if (p == NULL)
635                 for (lp = list; *lp != NULL; ++lp)
636                         if (__db_exists(p = *lp, NULL) == 0)
637                                 break;
638
639         if (p == NULL)
640                 return (0);
641
642         if ((dbenv->db_tmp_dir = (char *)strdup(p)) == NULL) {
643                 __db_err(dbenv, "%s", strerror(ENOMEM));
644                 return (ENOMEM);
645         }
646         return (0);
647 }
648
649 /*
650  * __db_tmp_open --
651  *      Create a temporary file.
652  */
653 static int
654 __db_tmp_open(dbenv, dir, fdp)
655         DB_ENV *dbenv;
656         char *dir;
657         int *fdp;
658 {
659 #ifdef HAVE_SIGFILLSET
660         sigset_t set, oset;
661 #endif
662         u_long pid;
663         size_t len;
664         int isdir, ret;
665         char *trv, buf[MAXPATHLEN];
666
667         /*
668          * Check the target directory; if you have six X's and it doesn't
669          * exist, this runs for a *very* long time.
670          */
671         if ((ret = __db_exists(dir, &isdir)) != 0) {
672                 __db_err(dbenv, "%s: %s", dir, strerror(ret));
673                 return (ret);
674         }
675         if (!isdir) {
676                 __db_err(dbenv, "%s: %s", dir, strerror(EINVAL));
677                 return (EINVAL);
678         }
679
680         /* Build the path. */
681 #define DB_TRAIL        "/XXXXXX"
682         if ((len = strlen(dir)) + sizeof(DB_TRAIL) > sizeof(buf)) {
683                 __db_err(dbenv,
684                     "tmp_open: %s: %s", buf, strerror(ENAMETOOLONG));
685                 return (ENAMETOOLONG);
686         }
687         (void)strcpy(buf, dir);
688         (void)strcpy(buf + len, DB_TRAIL);
689         buf[len] = PATH_SEPARATOR[0];                   /* WIN32 */
690
691         /*
692          * Replace the X's with the process ID.  Pid should be a pid_t,
693          * but we use unsigned long for portability.
694          */
695         for (pid = getpid(),
696             trv = buf + len + sizeof(DB_TRAIL) - 1; *--trv == 'X'; pid /= 10)
697                 switch (pid % 10) {
698                 case 0: *trv = '0'; break;
699                 case 1: *trv = '1'; break;
700                 case 2: *trv = '2'; break;
701                 case 3: *trv = '3'; break;
702                 case 4: *trv = '4'; break;
703                 case 5: *trv = '5'; break;
704                 case 6: *trv = '6'; break;
705                 case 7: *trv = '7'; break;
706                 case 8: *trv = '8'; break;
707                 case 9: *trv = '9'; break;
708                 }
709         ++trv;
710
711         /*
712          * Try and open a file.  We block every signal we can get our hands
713          * on so that, if we're interrupted at the wrong time, the temporary
714          * file isn't left around -- of course, if we drop core in-between
715          * the calls we'll hang forever, but that's probably okay.  ;-}
716          */
717 #ifdef HAVE_SIGFILLSET
718         (void)sigfillset(&set);
719 #endif
720         for (;;) {
721 #ifdef HAVE_SIGFILLSET
722                 (void)sigprocmask(SIG_BLOCK, &set, &oset);
723 #endif
724 #define DB_TEMPOPEN     DB_CREATE | DB_EXCL | DB_TEMPORARY
725                 if ((ret = __db_fdopen(buf,
726                     DB_TEMPOPEN, DB_TEMPOPEN, S_IRUSR | S_IWUSR, fdp)) == 0) {
727 #ifdef HAVE_SIGFILLSET
728                         (void)sigprocmask(SIG_SETMASK, &oset, NULL);
729 #endif
730                         return (0);
731                 }
732 #ifdef HAVE_SIGFILLSET
733                 (void)sigprocmask(SIG_SETMASK, &oset, NULL);
734 #endif
735                 /*
736                  * XXX:
737                  * If we don't get an EEXIST error, then there's something
738                  * seriously wrong.  Unfortunately, if the implementation
739                  * doesn't return EEXIST for O_CREAT and O_EXCL regardless
740                  * of other possible errors, we've lost.
741                  */
742                 if (ret != EEXIST) {
743                         __db_err(dbenv,
744                             "tmp_open: %s: %s", buf, strerror(ret));
745                         return (ret);
746                 }
747
748                 /*
749                  * Tricky little algorithm for backward compatibility.
750                  * Assumes the ASCII ordering of lower-case characters.
751                  */
752                 for (;;) {
753                         if (*trv == '\0')
754                                 return (EINVAL);
755                         if (*trv == 'z')
756                                 *trv++ = 'a';
757                         else {
758                                 if (isdigit(*trv))
759                                         *trv = 'a';
760                                 else
761                                         ++*trv;
762                                 break;
763                         }
764                 }
765         }
766         /* NOTREACHED */
767 }