/* * __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's 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\""); /* Find a match for the method name. */ for (epp = conn->config_entries; (*epp)->method != NULL; ++epp) if (strcmp((*epp)->method, method) == 0) break; if ((*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_def(session, 1, &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; /* * Build a new checks entry name field. There may be a default value * in the config argument we're passed, we don't want that as part of * the checks entry name field. */ WT_ERR(__wt_strdup(session, config, &newcheck_name)); if ((p = strchr(newcheck_name, '=')) != NULL) *p = '\0'; /* * Build a new checks array. The new configuration name may replace * an existing check with new information, in that case skip the old * version. */ for (cnt = 0, cp = (*epp)->checks; cp->name != NULL; ++cp) ++cnt; WT_ERR(__wt_calloc_def(session, cnt + 2, &checks)); for (cnt = 0, 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)); if (check != NULL) WT_ERR(__wt_strdup(session, check, &newcheck->checks)); entry->checks = checks; /* Confirm the configuration string passes the new set of checks. */ WT_ERR(config_check(session, entry->checks, 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, add them to the list now. */ WT_ERR(__wt_conn_foc_add(session, entry, entry->base, checks, newcheck->name, newcheck->type, newcheck->checks, NULL)); *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); }
/* * __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); }