2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
11 static const char sccsid
[] = "@(#)db_appinit.c 10.52 (Sleepycat) 6/2/98";
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
33 #include "common_ext.h"
35 static int __db_home
__P((DB_ENV
*, const char *, u_int32_t
));
36 static int __db_parse
__P((DB_ENV
*, char *));
37 static int __db_tmp_dir
__P((DB_ENV
*, u_int32_t
));
38 static int __db_tmp_open
__P((DB_ENV
*, u_int32_t
, char *, int *));
42 * Return version information.
45 db_version(majverp
, minverp
, patchp
)
46 int *majverp
, *minverp
, *patchp
;
49 *majverp
= DB_VERSION_MAJOR
;
51 *minverp
= DB_VERSION_MINOR
;
53 *patchp
= DB_VERSION_PATCH
;
54 return ((char *)DB_VERSION_STRING
);
59 * Initialize the application environment.
62 db_appinit(db_home
, db_config
, dbenv
, flags
)
64 char * const *db_config
;
71 char *lp
, buf
[MAXPATHLEN
* 2];
73 /* Validate arguments. */
80 (DB_CREATE | DB_NOMMAP | DB_THREAD | DB_INIT_LOCK | DB_INIT_LOG | \
81 DB_INIT_MPOOL | DB_INIT_TXN | DB_MPOOL_PRIVATE | DB_RECOVER | \
82 DB_RECOVER_FATAL | DB_TXN_NOSYNC | DB_USE_ENVIRON | DB_USE_ENVIRON_ROOT)
85 (DB_CREATE | DB_NOMMAP | DB_INIT_LOCK | DB_INIT_LOG | \
86 DB_INIT_MPOOL | DB_INIT_TXN | DB_MPOOL_PRIVATE | DB_RECOVER | \
87 DB_RECOVER_FATAL | DB_TXN_NOSYNC | DB_USE_ENVIRON | DB_USE_ENVIRON_ROOT)
89 if ((ret
= __db_fchk(dbenv
, "db_appinit", flags
, OKFLAGS
)) != 0)
92 /* Transactions imply logging. */
93 if (LF_ISSET(DB_INIT_TXN
))
96 /* Convert the db_appinit(3) flags. */
97 if (LF_ISSET(DB_THREAD
))
98 F_SET(dbenv
, DB_ENV_THREAD
);
102 /* Set the database home. */
103 if ((ret
= __db_home(dbenv
, db_home
, flags
)) != 0)
106 /* Parse the config array. */
107 for (p
= db_config
; p
!= NULL
&& *p
!= NULL
; ++p
)
108 if ((ret
= __db_parse(dbenv
, *p
)) != 0)
112 * Parse the config file.
115 * Don't use sprintf(3)/snprintf(3) -- the former is dangerous, and
116 * the latter isn't standard, and we're manipulating strings handed
117 * us by the application.
119 if (dbenv
->db_home
!= NULL
) {
120 #define CONFIG_NAME "/DB_CONFIG"
121 if (strlen(dbenv
->db_home
) +
122 strlen(CONFIG_NAME
) + 1 > sizeof(buf
)) {
126 (void)strcpy(buf
, dbenv
->db_home
);
127 (void)strcat(buf
, CONFIG_NAME
);
128 if ((fp
= fopen(buf
, "r")) != NULL
) {
129 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
130 if ((lp
= strchr(buf
, '\n')) != NULL
)
132 if ((ret
= __db_parse(dbenv
, buf
)) != 0)
140 /* Set up the tmp directory path. */
141 if (dbenv
->db_tmp_dir
== NULL
&&
142 (ret
= __db_tmp_dir(dbenv
, flags
)) != 0)
145 /* Indicate that the path names have been set. */
146 F_SET(dbenv
, DB_ENV_APPINIT
);
149 * If we are doing recovery, remove all the old shared memory
152 if (LF_ISSET(DB_RECOVER
| DB_RECOVER_FATAL
)) {
153 if ((ret
= log_unlink(NULL
, 1, dbenv
)) != 0)
155 if ((ret
= memp_unlink(NULL
, 1, dbenv
)) != 0)
157 if ((ret
= lock_unlink(NULL
, 1, dbenv
)) != 0)
159 if ((ret
= txn_unlink(NULL
, 1, dbenv
)) != 0)
164 * Create the new shared regions.
166 * Default permissions are read-write for both owner and group.
168 mode
= __db_omode("rwrw--");
169 if (LF_ISSET(DB_INIT_LOCK
) && (ret
= lock_open(NULL
,
170 LF_ISSET(DB_CREATE
| DB_THREAD
),
171 mode
, dbenv
, &dbenv
->lk_info
)) != 0)
173 if (LF_ISSET(DB_INIT_LOG
) && (ret
= log_open(NULL
,
174 LF_ISSET(DB_CREATE
| DB_THREAD
),
175 mode
, dbenv
, &dbenv
->lg_info
)) != 0)
177 if (LF_ISSET(DB_INIT_MPOOL
) && (ret
= memp_open(NULL
,
178 LF_ISSET(DB_CREATE
| DB_MPOOL_PRIVATE
| DB_NOMMAP
| DB_THREAD
),
179 mode
, dbenv
, &dbenv
->mp_info
)) != 0)
181 if (LF_ISSET(DB_INIT_TXN
) && (ret
= txn_open(NULL
,
182 LF_ISSET(DB_CREATE
| DB_THREAD
| DB_TXN_NOSYNC
),
183 mode
, dbenv
, &dbenv
->tx_info
)) != 0)
187 * If the application is running with transactions, initialize the
188 * function tables. Once that's done, do recovery for any previous
191 if (LF_ISSET(DB_INIT_TXN
)) {
192 if ((ret
= __bam_init_recover(dbenv
)) != 0)
194 if ((ret
= __db_init_recover(dbenv
)) != 0)
196 if ((ret
= __ham_init_recover(dbenv
)) != 0)
198 if ((ret
= __log_init_recover(dbenv
)) != 0)
200 if ((ret
= __txn_init_recover(dbenv
)) != 0)
203 if (LF_ISSET(DB_RECOVER
| DB_RECOVER_FATAL
) &&
204 (ret
= __db_apprec(dbenv
,
205 LF_ISSET(DB_RECOVER
| DB_RECOVER_FATAL
))) != 0)
214 (void)db_appexit(dbenv
);
220 * Close down the default application environment.
231 /* Close subsystems. */
232 if (dbenv
->tx_info
&& (t_ret
= txn_close(dbenv
->tx_info
)) != 0)
235 if (dbenv
->mp_info
&& (t_ret
= memp_close(dbenv
->mp_info
)) != 0)
238 if (dbenv
->lg_info
&& (t_ret
= log_close(dbenv
->lg_info
)) != 0)
241 if (dbenv
->lk_info
&& (t_ret
= lock_close(dbenv
->lk_info
)) != 0)
245 /* Free allocated memory. */
246 if (dbenv
->db_home
!= NULL
)
247 FREES(dbenv
->db_home
);
248 if ((p
= dbenv
->db_data_dir
) != NULL
) {
249 for (; *p
!= NULL
; ++p
)
251 FREE(dbenv
->db_data_dir
, dbenv
->data_cnt
* sizeof(char **));
253 if (dbenv
->db_log_dir
!= NULL
)
254 FREES(dbenv
->db_log_dir
);
255 if (dbenv
->db_tmp_dir
!= NULL
)
256 FREES(dbenv
->db_tmp_dir
);
261 #define DB_ADDSTR(str) { \
262 if ((str) != NULL) { \
263 /* If leading slash, start over. */ \
264 if (__db_abspath(str)) { \
268 /* Append to the current string. */ \
271 *p++ = PATH_SEPARATOR[0]; \
272 memcpy(p, str, len); \
274 slash = strchr(PATH_SEPARATOR, p[-1]) == NULL; \
280 * Given an optional DB environment, directory and file name and type
281 * of call, build a path based on the db_appinit(3) rules, and return
282 * it in allocated space.
284 * PUBLIC: int __db_appname __P((DB_ENV *,
285 * PUBLIC: APPNAME, const char *, const char *, u_int32_t, int *, char **));
288 __db_appname(dbenv
, appname
, dir
, file
, tmp_oflags
, fdp
, namep
)
291 const char *dir
, *file
;
292 u_int32_t tmp_oflags
;
298 int data_entry
, ret
, slash
, tmp_create
, tmp_free
;
299 const char *a
, *b
, *c
;
304 tmp_create
= tmp_free
= 0;
307 * We don't return a name when creating temporary files, just an fd.
308 * Default to error now.
316 * Absolute path names are never modified. If the file is an absolute
317 * path, we're done. If the directory is, simply append the file and
320 if (file
!= NULL
&& __db_abspath(file
))
322 (char *)__db_strdup(file
)) == NULL
? ENOMEM
: 0);
323 if (dir
!= NULL
&& __db_abspath(dir
)) {
329 * DB_ENV DIR APPNAME RESULT
330 * -------------------------------------------
331 * null null none <tmp>/file
332 * null set none DIR/file
333 * set null none DB_HOME/file
334 * set set none DB_HOME/DIR/file
336 * DB_ENV FILE APPNAME RESULT
337 * -------------------------------------------
338 * null null DB_APP_DATA <tmp>/<create>
339 * null set DB_APP_DATA ./file
340 * set null DB_APP_DATA <tmp>/<create>
341 * set set DB_APP_DATA DB_HOME/DB_DATA_DIR/file
343 * DB_ENV DIR APPNAME RESULT
344 * -------------------------------------------
345 * null null DB_APP_LOG <tmp>/file
346 * null set DB_APP_LOG DIR/file
347 * set null DB_APP_LOG DB_HOME/DB_LOG_DIR/file
348 * set set DB_APP_LOG DB_HOME/DB_LOG_DIR/DIR/file
350 * DB_ENV APPNAME RESULT
351 * -------------------------------------------
352 * null DB_APP_TMP* <tmp>/<create>
353 * set DB_APP_TMP* DB_HOME/DB_TMP_DIR/<create>
355 retry
: switch (appname
) {
357 if (dbenv
== NULL
|| !F_ISSET(dbenv
, DB_ENV_APPINIT
)) {
369 "DB_APP_DATA: illegal directory specification");
377 if (dbenv
== NULL
|| !F_ISSET(dbenv
, DB_ENV_APPINIT
))
381 if (dbenv
->db_data_dir
!= NULL
&&
382 (b
= dbenv
->db_data_dir
[++data_entry
]) == NULL
) {
384 b
= dbenv
->db_data_dir
[0];
389 if (dbenv
== NULL
|| !F_ISSET(dbenv
, DB_ENV_APPINIT
)) {
395 b
= dbenv
->db_log_dir
;
400 if (dir
!= NULL
|| file
!= NULL
) {
402 "DB_APP_TMP: illegal directory or file specification");
407 if (dbenv
== NULL
|| !F_ISSET(dbenv
, DB_ENV_APPINIT
))
411 b
= dbenv
->db_tmp_dir
;
416 /* Reference a file from the appropriate temporary directory. */
418 tmp
: if (dbenv
== NULL
|| !F_ISSET(dbenv
, DB_ENV_APPINIT
)) {
419 memset(&etmp
, 0, sizeof(etmp
));
420 if ((ret
= __db_tmp_dir(&etmp
, DB_USE_ENVIRON
)) != 0)
425 a
= dbenv
->db_tmp_dir
;
429 (a
== NULL
? 0 : strlen(a
) + 1) +
430 (b
== NULL
? 0 : strlen(b
) + 1) +
431 (c
== NULL
? 0 : strlen(c
) + 1) +
432 (file
== NULL
? 0 : strlen(file
) + 1);
435 * Allocate space to hold the current path information, as well as any
436 * temporary space that we're going to need to create a temporary file
439 #define DB_TRAIL "XXXXXX"
441 (char *)__db_malloc(len
+ sizeof(DB_TRAIL
) + 10)) == NULL
) {
442 __db_err(dbenv
, "%s", strerror(ENOMEM
));
444 FREES(etmp
.db_tmp_dir
);
456 * If we're opening a data file, see if it exists. If it does,
457 * return it, otherwise, try and find another one to open.
459 if (data_entry
!= -1 && __db_exists(start
, NULL
) != 0) {
465 /* Discard any space allocated to find the temp directory. */
467 FREES(etmp
.db_tmp_dir
);
469 /* Create the file if so requested. */
471 (ret
= __db_tmp_open(dbenv
, tmp_oflags
, start
, fdp
)) != 0) {
483 * Find the database home.
486 __db_home(dbenv
, db_home
, flags
)
495 /* Use the environment if it's permitted and initialized. */
497 if (LF_ISSET(DB_USE_ENVIRON
) ||
498 (LF_ISSET(DB_USE_ENVIRON_ROOT
) && getuid() == 0)) {
500 if (LF_ISSET(DB_USE_ENVIRON
)) {
502 if ((p
= getenv("DB_HOME")) == NULL
)
504 else if (p
[0] == '\0') {
506 "illegal DB_HOME environment variable");
514 if ((dbenv
->db_home
= (char *)__db_strdup(p
)) == NULL
) {
515 __db_err(dbenv
, "%s", strerror(ENOMEM
));
523 * Parse a single NAME VALUE pair.
531 char *local_s
, *name
, *value
, **p
, *tp
;
536 * We need to strdup the argument in case the caller passed us
539 if ((local_s
= (char *)__db_strdup(s
)) == NULL
)
543 while ((name
= strsep(&tp
, " \t")) != NULL
&& *name
== '\0')
547 while ((value
= strsep(&tp
, " \t")) != NULL
&& *value
== '\0')
550 illegal
: ret
= EINVAL
;
551 __db_err(dbenv
, "illegal name-value pair: %s", s
);
555 #define DATA_INIT_CNT 20 /* Start with 20 data slots. */
556 if (!strcmp(name
, "DB_DATA_DIR")) {
557 if (dbenv
->db_data_dir
== NULL
) {
558 if ((dbenv
->db_data_dir
=
559 (char **)__db_calloc(DATA_INIT_CNT
,
560 sizeof(char **))) == NULL
)
562 dbenv
->data_cnt
= DATA_INIT_CNT
;
563 } else if (dbenv
->data_next
== dbenv
->data_cnt
- 1) {
564 dbenv
->data_cnt
*= 2;
565 if ((dbenv
->db_data_dir
=
566 (char **)__db_realloc(dbenv
->db_data_dir
,
567 dbenv
->data_cnt
* sizeof(char **))) == NULL
)
570 p
= &dbenv
->db_data_dir
[dbenv
->data_next
++];
571 } else if (!strcmp(name
, "DB_LOG_DIR")) {
572 if (dbenv
->db_log_dir
!= NULL
)
573 FREES(dbenv
->db_log_dir
);
574 p
= &dbenv
->db_log_dir
;
575 } else if (!strcmp(name
, "DB_TMP_DIR")) {
576 if (dbenv
->db_tmp_dir
!= NULL
)
577 FREES(dbenv
->db_tmp_dir
);
578 p
= &dbenv
->db_tmp_dir
;
582 if ((*p
= (char *)__db_strdup(value
)) == NULL
) {
584 __db_err(dbenv
, "%s", strerror(ENOMEM
));
592 #include <TFileSpec.h>
594 static char *sTempFolder
;
599 * Set the temporary directory path.
602 __db_tmp_dir(dbenv
, flags
)
606 static const char * list
[] = { /* Ordered: see db_appinit(3). */
609 "/temp", /* WIN32. */
611 "C:/temp", /* WIN32. */
612 "C:/tmp", /* WIN32. */
617 /* Use the environment if it's permitted and initialized. */
620 if (LF_ISSET(DB_USE_ENVIRON
) ||
621 (LF_ISSET(DB_USE_ENVIRON_ROOT
) && getuid() == 0)) {
623 if (LF_ISSET(DB_USE_ENVIRON
)) {
625 if ((p
= getenv("TMPDIR")) != NULL
&& p
[0] == '\0') {
626 __db_err(dbenv
, "illegal TMPDIR environment variable");
630 if (p
== NULL
&& (p
= getenv("TEMP")) != NULL
&& p
[0] == '\0') {
631 __db_err(dbenv
, "illegal TEMP environment variable");
635 if (p
== NULL
&& (p
= getenv("TMP")) != NULL
&& p
[0] == '\0') {
636 __db_err(dbenv
, "illegal TMP environment variable");
641 (p
= getenv("TempFolder")) != NULL
&& p
[0] == '\0') {
643 "illegal TempFolder environment variable");
649 /* Get the path to the temporary folder. */
653 if (!Special2FSSpec(kTemporaryFolderType
,
654 kOnSystemDisk
, 0, &spec
)) {
655 p
= FSp2FullPath(&spec
);
656 sTempFolder
= __db_malloc(strlen(p
) + 1);
657 strcpy(sTempFolder
, p
);
663 /* Step through the list looking for a possibility. */
665 for (lp
= list
; *lp
!= NULL
; ++lp
)
666 if (__db_exists(p
= *lp
, NULL
) == 0)
672 if ((dbenv
->db_tmp_dir
= (char *)__db_strdup(p
)) == NULL
) {
673 __db_err(dbenv
, "%s", strerror(ENOMEM
));
681 * Create a temporary file.
684 __db_tmp_open(dbenv
, flags
, path
, fdp
)
690 #ifdef HAVE_SIGFILLSET
694 int mode
, isdir
, ret
;
699 * Check the target directory; if you have six X's and it doesn't
700 * exist, this runs for a *very* long time.
702 if ((ret
= __db_exists(path
, &isdir
)) != 0) {
703 __db_err(dbenv
, "%s: %s", path
, strerror(ret
));
707 __db_err(dbenv
, "%s: %s", path
, strerror(EINVAL
));
711 /* Build the path. */
712 for (trv
= path
; *trv
!= '\0'; ++trv
)
714 *trv
= PATH_SEPARATOR
[0];
715 for (p
= DB_TRAIL
; (*++trv
= *p
) != '\0'; ++p
)
719 * Replace the X's with the process ID. Pid should be a pid_t,
720 * but we use unsigned long for portability.
722 for (pid
= getpid(); *--trv
== 'X'; pid
/= 10)
724 case 0: *trv
= '0'; break;
725 case 1: *trv
= '1'; break;
726 case 2: *trv
= '2'; break;
727 case 3: *trv
= '3'; break;
728 case 4: *trv
= '4'; break;
729 case 5: *trv
= '5'; break;
730 case 6: *trv
= '6'; break;
731 case 7: *trv
= '7'; break;
732 case 8: *trv
= '8'; break;
733 case 9: *trv
= '9'; break;
737 /* Set up open flags and mode. */
738 LF_SET(DB_CREATE
| DB_EXCL
);
739 mode
= __db_omode("rw----");
742 * Try to open a file. We block every signal we can get our hands
743 * on so that, if we're interrupted at the wrong time, the temporary
744 * file isn't left around -- of course, if we drop core in-between
745 * the calls we'll hang forever, but that's probably okay. ;-}
747 #ifdef HAVE_SIGFILLSET
748 if (LF_ISSET(DB_TEMPORARY
))
749 (void)sigfillset(&set
);
752 #ifdef HAVE_SIGFILLSET
753 if (LF_ISSET(DB_TEMPORARY
))
754 (void)sigprocmask(SIG_BLOCK
, &set
, &oset
);
756 ret
= __db_open(path
, flags
, flags
, mode
, fdp
);
757 #ifdef HAVE_SIGFILLSET
758 if (LF_ISSET(DB_TEMPORARY
))
759 (void)sigprocmask(SIG_SETMASK
, &oset
, NULL
);
766 * If we don't get an EEXIST error, then there's something
767 * seriously wrong. Unfortunately, if the implementation
768 * doesn't return EEXIST for O_CREAT and O_EXCL regardless
769 * of other possible errors, we've lost.
773 "tmp_open: %s: %s", path
, strerror(ret
));
778 * Tricky little algorithm for backward compatibility.
779 * Assumes the ASCII ordering of lower-case characters.