/* * __conn_config_env -- * Read configuration from an environment variable, if set. */ static int __conn_config_env(WT_SESSION_IMPL *session, const char **cfg) { WT_CONFIG_ITEM cval; const char *env_config; if ((env_config = getenv("WIREDTIGER_CONFIG")) == NULL || strlen(env_config) == 0) return (0); /* * Security stuff: * * If the "use_environment_priv" configuration string is set, use the * environment variable if the process has appropriate privileges. */ WT_RET(__wt_config_gets(session, cfg, "use_environment_priv", &cval)); if (cval.val == 0 && __wt_has_priv()) WT_RET_MSG(session, WT_ERROR, "%s", "WIREDTIGER_CONFIG environment variable set but process " "lacks privileges to use that environment variable"); /* Check the configuration string. */ WT_RET(__wt_config_check( session, __wt_confchk_wiredtiger_open, env_config, 0)); /* * The environment setting comes second-to-last: it overrides the * WiredTiger.config file (if any), but not the config passed by the * application. */ while (cfg[1] != NULL) ++cfg; cfg[1] = cfg[0]; cfg[0] = env_config; return (0); }
/* * wiredtiger_open -- * Main library entry point: open a new connection to a WiredTiger * database. */ int wiredtiger_open(const char *home, WT_EVENT_HANDLER *event_handler, const char *config, WT_CONNECTION **wt_connp) { static WT_CONNECTION stdc = { __conn_close, __conn_reconfigure, __conn_get_home, __conn_is_new, __conn_open_session, __conn_load_extension, __conn_add_data_source, __conn_add_collator, __conn_add_compressor, __conn_add_extractor }; static struct { const char *name; uint32_t flag; } *ft, directio_types[] = { { "data", WT_DIRECTIO_DATA }, { "log", WT_DIRECTIO_LOG }, { NULL, 0 } }; WT_CONFIG subconfig; WT_CONFIG_ITEM cval, skey, sval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; WT_ITEM *cbuf, expath, exconfig; WT_SESSION_IMPL *session; const char *cfg[] = { __wt_confdfl_wiredtiger_open, config, NULL, NULL, NULL }; int exist; *wt_connp = NULL; session = NULL; cbuf = NULL; WT_CLEAR(expath); WT_CLEAR(exconfig); WT_RET(__wt_library_init()); WT_RET(__wt_calloc_def(NULL, 1, &conn)); conn->iface = stdc; /* * Immediately link the structure into the connection structure list: * the only thing ever looked at on that list is the database name, * and a NULL value is fine. */ __wt_spin_lock(NULL, &__wt_process.spinlock); TAILQ_INSERT_TAIL(&__wt_process.connqh, conn, q); __wt_spin_unlock(NULL, &__wt_process.spinlock); session = conn->default_session = &conn->dummy_session; session->iface.connection = &conn->iface; session->name = "wiredtiger_open"; __wt_event_handler_set(session, event_handler); /* Remaining basic initialization of the connection structure. */ WT_ERR(__wt_connection_init(conn)); /* Check the configuration strings. */ WT_ERR(__wt_config_check( session, __wt_confchk_wiredtiger_open, config, 0)); /* Get the database home. */ WT_ERR(__conn_home(session, home, cfg)); /* Make sure no other thread of control already owns this database. */ WT_ERR(__conn_single(session, cfg)); /* Read the database-home configuration file. */ WT_ERR(__conn_config_file(session, cfg, &cbuf)); /* Read the environment variable configuration. */ WT_ERR(__conn_config_env(session, cfg)); WT_ERR(__wt_config_gets(session, cfg, "hazard_max", &cval)); conn->hazard_max = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "session_max", &cval)); conn->session_size = (uint32_t)cval.val + WT_NUM_INTERNAL_SESSIONS; WT_ERR(__wt_config_gets(session, cfg, "lsm_merge", &cval)); if (cval.val) F_SET(conn, WT_CONN_LSM_MERGE); WT_ERR(__wt_config_gets(session, cfg, "sync", &cval)); if (cval.val) F_SET(conn, WT_CONN_SYNC); WT_ERR(__wt_config_gets(session, cfg, "transactional", &cval)); if (cval.val) F_SET(conn, WT_CONN_TRANSACTIONAL); /* Configure verbose flags. */ WT_ERR(__conn_verbose_config(session, cfg)); WT_ERR(__wt_conn_cache_pool_config(session, cfg)); WT_ERR(__wt_config_gets(session, cfg, "logging", &cval)); if (cval.val != 0) WT_ERR(__wt_open( session, WT_LOG_FILENAME, 1, 0, 0, &conn->log_fh)); /* Configure direct I/O and buffer alignment. */ WT_ERR(__wt_config_gets(session, cfg, "buffer_alignment", &cval)); if (cval.val == -1) conn->buffer_alignment = WT_BUFFER_ALIGNMENT_DEFAULT; else conn->buffer_alignment = (size_t)cval.val; #ifndef HAVE_POSIX_MEMALIGN if (conn->buffer_alignment != 0) WT_ERR_MSG(session, EINVAL, "buffer_alignment requires posix_memalign"); #endif /* * Configuration: direct_io, mmap, statistics. */ WT_ERR(__wt_config_gets(session, cfg, "direct_io", &cval)); for (ft = directio_types; ft->name != NULL; ft++) { ret = __wt_config_subgets(session, &cval, ft->name, &sval); if (ret == 0) { if (sval.val) FLD_SET(conn->direct_io, ft->flag); } else if (ret != WT_NOTFOUND) goto err; } WT_ERR(__wt_config_gets(session, cfg, "mmap", &cval)); conn->mmap = cval.val == 0 ? 0 : 1; WT_ERR(__wt_config_gets(session, cfg, "statistics", &cval)); conn->statistics = cval.val == 0 ? 0 : 1; /* Load any extensions referenced in the config. */ WT_ERR(__wt_config_gets(session, cfg, "extensions", &cval)); WT_ERR(__wt_config_subinit(session, &subconfig, &cval)); while ((ret = __wt_config_next(&subconfig, &skey, &sval)) == 0) { WT_ERR(__wt_buf_fmt( session, &expath, "%.*s", (int)skey.len, skey.str)); if (sval.len > 0) WT_ERR(__wt_buf_fmt(session, &exconfig, "entry=%.*s\n", (int)sval.len, sval.str)); WT_ERR(conn->iface.load_extension(&conn->iface, expath.data, (sval.len > 0) ? exconfig.data : NULL)); } WT_ERR_NOTFOUND_OK(ret); /* * Open the connection; if that fails, the connection handle has been * destroyed by the time the open function returns. */ if ((ret = __wt_connection_open(conn, cfg)) != 0) { conn = NULL; WT_ERR(ret); } /* Open the default session. */ WT_ERR(__wt_open_session(conn, 1, NULL, NULL, &conn->default_session)); session = conn->default_session; /* * Check on the turtle and metadata files, creating them if necessary * (which avoids application threads racing to create the metadata file * later). */ WT_ERR(__wt_meta_turtle_init(session, &exist)); if (!exist) { /* * We're single-threaded, but acquire the schema lock * regardless: the lower level code checks that it is * appropriately synchronized. */ WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_create(session, WT_METADATA_URI, NULL)); WT_ERR(ret); } WT_ERR(__wt_metadata_open(session)); /* If there's a hot-backup file, load it. */ WT_ERR(__wt_metadata_load_backup(session)); /* * XXX LSM initialization. * This is structured so that it could be moved to an extension. */ WT_ERR(__wt_lsm_init(&conn->iface, NULL)); STATIC_ASSERT(offsetof(WT_CONNECTION_IMPL, iface) == 0); *wt_connp = &conn->iface; /* * Destroying the connection on error will destroy our session handle, * cleanup using the session handle first, then discard the connection. */ err: if (cbuf != NULL) __wt_buf_free(session, cbuf); __wt_buf_free(session, &expath); __wt_buf_free(session, &exconfig); if (ret != 0 && conn != NULL) WT_TRET(__wt_connection_destroy(conn)); /* Let the server threads proceed. */ if (ret == 0) conn->connection_initialized = 1; return (ret); }
/* * wiredtiger_config_validate -- * Validate a configuration string. */ int wiredtiger_config_validate(WT_SESSION *wt_session, WT_EVENT_HANDLER *handler, const char *name, const char *config) { WT_CONNECTION_IMPL *conn, dummy_conn; WT_SESSION_IMPL *session; const WT_CONFIG_ENTRY *ep, **epp; session = (WT_SESSION_IMPL *)wt_session; /* * It's a logic error to specify both a session and an event handler. */ if (session != NULL && handler != NULL) WT_RET_MSG(session, EINVAL, "wiredtiger_config_validate error handler ignored when " "a session also specified"); /* * If we're not given a session, but we do have an event handler, build * a fake session/connection pair and configure the event handler. */ conn = NULL; if (session == NULL && handler != NULL) { WT_CLEAR(dummy_conn); conn = &dummy_conn; session = conn->default_session = &conn->dummy_session; session->iface.connection = &conn->iface; session->name = "wiredtiger_config_validate"; __wt_event_handler_set(session, handler); } if (session != NULL) conn = S2C(session); if (name == NULL) WT_RET_MSG(session, EINVAL, "no name specified"); if (config == NULL) WT_RET_MSG(session, EINVAL, "no configuration specified"); /* * If we don't have a real connection, look for a matching name in the * static list, otherwise look in the configuration list (which has any * configuration information the application has added). */ if (session == NULL || conn == NULL || conn->config_entries == NULL) ep = __wt_conn_config_match(name); else { ep = NULL; for (epp = conn->config_entries; *epp != NULL && (*epp)->method != NULL; ++epp) if (strcmp((*epp)->method, name) == 0) { ep = *epp; break; } } if (ep == NULL) WT_RET_MSG(session, EINVAL, "unknown or unsupported configuration API: %s", name); return (__wt_config_check(session, ep, config, 0)); }
/* * __conn_config_file -- * Read in any WiredTiger_config file in the home directory. */ static int __conn_config_file(WT_SESSION_IMPL *session, const char **cfg, WT_ITEM **cbufp) { WT_DECL_ITEM(cbuf); WT_DECL_RET; WT_FH *fh; off_t size; uint32_t len; int exist, quoted; uint8_t *p, *t; *cbufp = NULL; /* Returned buffer */ fh = NULL; /* Check for an optional configuration file. */ #define WT_CONFIGFILE "WiredTiger.config" WT_RET(__wt_exist(session, WT_CONFIGFILE, &exist)); if (!exist) return (0); /* Open the configuration file. */ WT_RET(__wt_open(session, WT_CONFIGFILE, 0, 0, 0, &fh)); WT_ERR(__wt_filesize(session, fh, &size)); if (size == 0) goto err; /* * Sanity test: a 100KB configuration file would be insane. (There's * no practical reason to limit the file size, but I can either limit * the file size to something rational, or I can add code to test if * the off_t size is larger than a uint32_t, which is more complicated * and a waste of time.) */ if (size > 100 * 1024) WT_ERR_MSG(session, EFBIG, WT_CONFIGFILE); len = (uint32_t)size; /* * Copy the configuration file into memory, with a little slop, I'm not * interested in debugging off-by-ones. * * The beginning of a file is the same as if we run into an unquoted * newline character, simplify the parsing loop by pretending that's * what we're doing. */ WT_ERR(__wt_scr_alloc(session, len + 10, &cbuf)); WT_ERR( __wt_read(session, fh, (off_t)0, len, ((uint8_t *)cbuf->mem) + 1)); ((uint8_t *)cbuf->mem)[0] = '\n'; cbuf->size = len + 1; /* * Collapse the file's lines into a single string: newline characters * are replaced with commas unless the newline is quoted or backslash * escaped. Comment lines (an unescaped newline where the next non- * white-space character is a hash), are discarded. */ for (quoted = 0, p = t = cbuf->mem; len > 0;) { /* * Backslash pairs pass through untouched, unless immediately * preceding a newline, in which case both the backslash and * the newline are discarded. Backslash characters escape * quoted characters, too, that is, a backslash followed by a * quote doesn't start or end a quoted string. */ if (*p == '\\' && len > 1) { if (p[1] != '\n') { *t++ = p[0]; *t++ = p[1]; } p += 2; len -= 2; continue; } /* * If we're in a quoted string, or starting a quoted string, * take all characters, including white-space and newlines. */ if (quoted || *p == '"') { if (*p == '"') quoted = !quoted; *t++ = *p++; --len; continue; } /* Everything else gets taken, except for newline characters. */ if (*p != '\n') { *t++ = *p++; --len; continue; } /* * Replace any newline characters with commas (and strings of * commas are safe). * * After any newline, skip to a non-white-space character; if * the next character is a hash mark, skip to the next newline. */ for (;;) { for (*t++ = ','; --len > 0 && isspace(*++p);) ; if (len == 0) break; if (*p != '#') break; while (--len > 0 && *++p != '\n') ; if (len == 0) break; } } *t = '\0'; #if 0 fprintf(stderr, "file config: {%s}\n", (const char *)cbuf->data); #endif /* Check the configuration string. */ WT_ERR(__wt_config_check( session, __wt_confchk_wiredtiger_open, cbuf->data, 0)); /* * The configuration file falls between the default configuration and * the wiredtiger_open() configuration, overriding the defaults but not * overriding the wiredtiger_open() configuration. */ while (cfg[1] != NULL) ++cfg; cfg[1] = cfg[0]; cfg[0] = cbuf->data; *cbufp = cbuf; if (0) { err: if (cbuf != NULL) __wt_buf_free(session, cbuf); } if (fh != NULL) WT_TRET(__wt_close(session, fh)); return (ret); }
/* * __wt_configure_method -- * WT_CONNECTION.configure_method. */ int __wt_configure_method(WT_SESSION_IMPL *session, const char *method, const char *uri, const char *config, const char *type, const char *check) { const WT_CONFIG_CHECK *cp; WT_CONFIG_CHECK *checks, *newcheck; const WT_CONFIG_ENTRY **epp; WT_CONFIG_ENTRY *entry; WT_CONNECTION_IMPL *conn; WT_DECL_RET; size_t cnt; char *newcheck_name, *p; /* * !!! * We ignore the specified uri, that is, all new configuration options * will be valid for all data sources. That shouldn't be too bad as * the worst that can happen is an application might specify some * configuration option and not get an error -- the option should be * ignored by the underlying implementation since it's unexpected, so * there shouldn't be any real problems. Eventually I expect we will * get the whole data-source thing sorted, at which time there may be * configuration arrays for each data source, and that's when the uri * will matter. */ WT_UNUSED(uri); conn = S2C(session); checks = newcheck = NULL; entry = NULL; newcheck_name = NULL; /* Argument checking; we only support a limited number of types. */ if (config == NULL) WT_RET_MSG(session, EINVAL, "no configuration specified"); if (type == NULL) WT_RET_MSG(session, EINVAL, "no configuration type specified"); if (strcmp(type, "boolean") != 0 && strcmp(type, "int") != 0 && strcmp(type, "list") != 0 && strcmp(type, "string") != 0) WT_RET_MSG(session, EINVAL, "type must be one of \"boolean\", \"int\", \"list\" or " "\"string\""); /* * Translate the method name to our configuration names, then find a * match. */ for (epp = conn->config_entries; *epp != NULL && (*epp)->method != NULL; ++epp) if (strcmp((*epp)->method, method) == 0) break; if (*epp == NULL || (*epp)->method == NULL) WT_RET_MSG(session, WT_NOTFOUND, "no method matching %s found", method); /* * Technically possible for threads to race, lock the connection while * adding the new configuration information. We're holding the lock * for an extended period of time, but configuration changes should be * rare and only happen during startup. */ __wt_spin_lock(session, &conn->api_lock); /* * Allocate new configuration entry and fill it in. * * The new base value is the previous base value, a separator and the * new configuration string. */ WT_ERR(__wt_calloc_one(session, &entry)); entry->method = (*epp)->method; WT_ERR(__wt_calloc_def(session, strlen((*epp)->base) + strlen(",") + strlen(config) + 1, &p)); (void)strcpy(p, (*epp)->base); (void)strcat(p, ","); (void)strcat(p, config); entry->base = p; /* * There may be a default value in the config argument passed in (for * example, (kvs_parallelism=64"). The default value isn't part of the * name, build a new one. */ WT_ERR(__wt_strdup(session, config, &newcheck_name)); if ((p = strchr(newcheck_name, '=')) != NULL) *p = '\0'; /* * The new configuration name may replace an existing check with new * information, in that case skip the old version. */ cnt = 0; if ((*epp)->checks != NULL) for (cp = (*epp)->checks; cp->name != NULL; ++cp) ++cnt; WT_ERR(__wt_calloc_def(session, cnt + 2, &checks)); cnt = 0; if ((*epp)->checks != NULL) for (cp = (*epp)->checks; cp->name != NULL; ++cp) if (strcmp(newcheck_name, cp->name) != 0) checks[cnt++] = *cp; newcheck = &checks[cnt]; newcheck->name = newcheck_name; WT_ERR(__wt_strdup(session, type, &newcheck->type)); WT_ERR(__wt_strdup(session, check, &newcheck->checks)); entry->checks = checks; entry->checks_entries = 0; /* * Confirm the configuration string passes the new set of * checks. */ WT_ERR(__wt_config_check(session, entry, config, 0)); /* * The next time this configuration is updated, we don't want to figure * out which of these pieces of memory were allocated and will need to * be free'd on close (this isn't a heavily used API and it's too much * work); add them all to the free-on-close list now. We don't check * for errors deliberately, we'd have to figure out which elements have * already been added to the free-on-close array and which have not in * order to avoid freeing chunks of memory twice. Again, this isn't a * commonly used API and it shouldn't ever happen, just leak it. */ __wt_conn_foc_add(session, entry->base); __wt_conn_foc_add(session, entry); __wt_conn_foc_add(session, checks); __wt_conn_foc_add(session, newcheck->type); __wt_conn_foc_add(session, newcheck->checks); __wt_conn_foc_add(session, newcheck_name); /* * Instead of using locks to protect configuration information, assume * we can atomically update a pointer to a chunk of memory, and because * a pointer is never partially written, readers will correctly see the * original or new versions of the memory. Readers might be using the * old version as it's being updated, though, which means we cannot free * the old chunk of memory until all possible readers have finished. * Currently, that's on connection close: in other words, we can use * this because it's small amounts of memory, and we really, really do * not want to acquire locks every time we access configuration strings, * since that's done on every API call. */ WT_PUBLISH(*epp, entry); if (0) { err: if (entry != NULL) { __wt_free(session, entry->base); __wt_free(session, entry); } __wt_free(session, checks); if (newcheck != NULL) { __wt_free(session, newcheck->type); __wt_free(session, newcheck->checks); } __wt_free(session, newcheck_name); } __wt_spin_unlock(session, &conn->api_lock); return (ret); }