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