/** * Send an info request to the entire cluster. * * ~~~~~~~~~~{.c} * if ( aerospike_info_foreach(&as, &err, NULL, "info", callback, NULL) != AEROSPIKE_OK ) { * // handle error * } * ~~~~~~~~~~ * * The callback takes a response string. The caller should not free this string. * * ~~~~~~~~~~{.c} * bool callback(const as_error * err, const char * node, char * res, void * udata) { * // handle response * } * ~~~~~~~~~~ * * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param req The info request to send. * @param callback The function to call when a response is received. * @param udata User-data to send to the callback. * * @return AEROSPIKE_OK on success. Otherwise an error. * * @ingroup info_operations */ as_status aerospike_info_foreach( aerospike * as, as_error * err, const as_policy_info * policy, const char * req, aerospike_info_foreach_callback callback, void * udata) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_info p; as_policy_info_resolve(&p, &as->config.policies, policy); if ( !as ) { return AEROSPIKE_ERR; } citrusleaf_info_cluster_foreach_data data = { .callback = callback, .udata = udata }; char* error = 0; int rc = citrusleaf_info_cluster_foreach( as->cluster, req, p.send_as_is, p.check_bounds, p.timeout, (void *) &data, &error, citrusleaf_info_cluster_foreach_callback); if (rc) { as_strncpy(err->message, error, sizeof(err->message)); free(error); return as_error_fromrc(err, rc); } return AEROSPIKE_OK; }
/** * Send an info request to a specific host. The response must be freed by the caller on success. * * ~~~~~~~~~~{.c} * char * res = NULL; * if ( aerospike_info_host(&as, &err, NULL, "127.0.0.1", 3000, "info", &res) != AEROSPIKE_OK ) { * // handle error * } * else { * // handle response * free(res); * res = NULL; * } * ~~~~~~~~~~ * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param addr The IP address or hostname to send the request to. * @param port The port to send the request to. * @param req The info request to send. * @param res The response from the node. The response will be a NULL terminated string, allocated by the function, and must be freed by the caller. * * @return AEROSPIKE_OK on success. Otherwise an error. * * @ingroup info_operations */ as_status aerospike_info_host( aerospike * as, as_error * err, const as_policy_info * policy, const char * addr, uint16_t port, const char * req, char ** res) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_info p; as_policy_info_resolve(&p, &as->config.policies, policy); if (! as) { return AEROSPIKE_ERR; } cl_rv rc = citrusleaf_info_auth(as->cluster, (char *) addr, port, (char *) req, res, p.timeout); if (rc) { as_strncpy(err->message, *res, sizeof(err->message)); free(*res); return as_error_fromrc(err, rc); } return AEROSPIKE_OK; }
static bool scan_udf_info_callback(const as_error * err, const as_node * node, const char * req, char * res, void * udata) { if (!err) { goto done; } if ( err->code != AEROSPIKE_OK ) { debug("UDF_CALLBACK Error: (%d) %s - node=%s response=%s\n", err->code, err->message, node ? node->name : "NULL", res); } else { if ( res == NULL || strlen(res) == 0 ) { goto done; } char * start_resp = strchr(res, '\t'); if ( start_resp == NULL || strlen(start_resp) == 0 ) { goto done; } char print_resp[128]; as_strncpy(print_resp, start_resp, sizeof(print_resp)); debug("%s", print_resp); } done: return true; }
bool as_config_set_user(as_config* config, const char* user, const char* password) { if (user && *user) { if (as_strncpy(config->user, user, sizeof(config->user))) { return false; } return as_password_get_constant_hash(password, config->password); } else { return false; } }
bool as_password_prompt_hash(const char* password, char* hash) { char pass[AS_PASSWORD_HASH_SIZE]; if (password && *password) { as_strncpy(pass, password, sizeof(pass)); } else { as_password_prompt(pass, sizeof(pass)); } return as_password_get_constant_hash(pass, hash); }
/** * Create a new secondary index. * * @param as - the aerospike cluster to connect to. * @param err - the error is populated if the return value is not AEROSPIKE_OK. * @param policy - the policy to use for this operation. If NULL, then the default policy will be used. * @param ns - the namespace to be indexed * @param set - the set to be indexed * @param bin - the bin to be indexed * @param type - the type of the bin to be indexed * @param name - the name of the index * * @return AEROSPIKE_OK if successful or index already exists. Otherwise an error. */ static as_status aerospike_index_create( aerospike * as, as_error * err, const as_policy_info * policy, const char * ns, const char * set, const char * bin, const char * type, const char * name) { as_error_reset(err); char * response = NULL; int rc = citrusleaf_secondary_index_create(as->cluster, ns, set, name, bin, type, &response); switch ( rc ) { case CITRUSLEAF_OK: case CITRUSLEAF_FAIL_INDEX_FOUND: break; default: as_strncpy(err->message, response, sizeof(err->message)); as_error_fromrc(err, rc); break; } free(response); return err->code; }
/** * Create secondary index. * * This asynchronous server call will return before the command is complete. * The user can optionally wait for command completion by using a task instance. * * ~~~~~~~~~~{.c} * as_index_task task; * if ( aerospike_index_create(&as, &err, &task, NULL, "test", "demo", "bin1", "idx_test_demo_bin1") == AEROSPIKE_OK ) { * aerospike_index_create_wait(&err, &task, 0); * } * ~~~~~~~~~~ * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param task The optional task data used to poll for completion. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param ns The namespace to be indexed. * @param set The set to be indexed. * @param bin The bin to be indexed. * @param name The name of the index. * * @return AEROSPIKE_OK if successful or index already exists. Otherwise an error. * * @ingroup index_operations */ as_status aerospike_index_create_complex( aerospike * as, as_error * err, as_index_task * task, const as_policy_info * policy, const as_namespace ns, const as_set set, const as_index_position position, const char * name, as_index_type itype, as_index_datatype dtype) { as_error_reset(err); const char* dtype_string; switch (dtype) { case AS_INDEX_NUMERIC: dtype_string = "NUMERIC"; break; case AS_INDEX_GEO2DSPHERE: dtype_string = "GEO2DSPHERE"; break; default: case AS_INDEX_STRING: dtype_string = "STRING"; break; } const char* itype_string; switch (itype) { default: case AS_INDEX_TYPE_DEFAULT: { itype_string = "DEFAULT"; break; } case AS_INDEX_TYPE_LIST: { itype_string = "LIST"; break; } case AS_INDEX_TYPE_MAPKEYS: { itype_string = "MAPKEYS"; break; } case AS_INDEX_TYPE_MAPVALUES: { itype_string = "MAPVALUES"; break; } } char ddl[1024]; if (itype == AS_INDEX_TYPE_DEFAULT) { // Use old format, so command can work with older servers. sprintf(ddl, "sindex-create:ns=%s%s%s;indexname=%s;" "numbins=1;indexdata=%s,%s;priority=normal\n", ns, set ? ";set=" : "", set ? set : "", name, position, dtype_string ); } else { // Use new format. sprintf(ddl, "sindex-create:ns=%s%s%s;indexname=%s;" "numbins=1;indextype=%s;indexdata=%s,%s;priority=normal\n", ns, set ? ";set=" : "", set ? set : "", name, itype_string, position, dtype_string ); } char* response = NULL; as_status status = aerospike_info_any(as, err, policy, ddl, &response); switch (status) { case AEROSPIKE_OK: // Return task that could optionally be polled for completion. if (task) { task->as = as; as_strncpy(task->ns, ns, sizeof(task->ns)); as_strncpy(task->name, name, sizeof(task->name)); task->done = false; } cf_free(response); break; case AEROSPIKE_ERR_INDEX_FOUND: // Index has already been created. Do not need to poll for completion. if (task) { task->done = true; } status = AEROSPIKE_OK; as_error_reset(err); break; default: break; } return status; }
static as_status as_cluster_seed_nodes(as_cluster* cluster, as_error* err, bool enable_warnings) { // Add all nodes at once to avoid copying entire array multiple times. as_vector nodes_to_add; as_vector_inita(&nodes_to_add, sizeof(as_node*), 64); as_vector addresses; as_vector_inita(&addresses, sizeof(struct sockaddr_in), 5); as_node_info node_info; as_error error_local; as_error_init(&error_local); // AEROSPIKE_ERR_TIMEOUT doesn't come with a message; make sure it's initialized. as_status status = AEROSPIKE_OK; as_seeds* seeds = as_seeds_reserve(cluster); for (uint32_t i = 0; i < seeds->size; i++) { as_seed* seed = &seeds->array[i]; as_vector_clear(&addresses); status = as_lookup(&error_local, seed->name, seed->port, &addresses); if (status != AEROSPIKE_OK) { if (enable_warnings) { as_log_warn("Failed to lookup %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message); } continue; } for (uint32_t i = 0; i < addresses.size; i++) { struct sockaddr_in* addr = as_vector_get(&addresses, i); status = as_lookup_node(cluster, &error_local, addr, &node_info); if (status == AEROSPIKE_OK) { as_host host; if (as_strncpy(host.name, seed->name, sizeof(host.name))) { as_log_warn("Hostname has been truncated: %s", host.name); } host.port = seed->port; as_node* node = as_cluster_find_node_in_vector(&nodes_to_add, node_info.name); if (node) { as_close(node_info.fd); as_node_add_address(node, &host, addr); } else { node = as_node_create(cluster, &host, addr, &node_info); as_address* a = as_node_get_address_full(node); as_log_info("Add node %s %s:%d", node->name, a->name, (int)cf_swap_from_be16(a->addr.sin_port)); as_vector_append(&nodes_to_add, &node); } } else { if (enable_warnings) { as_log_warn("Failed to connect to seed %s:%d. %s %s", seed->name, seed->port, as_error_string(status), error_local.message); } } } } as_seeds_release(seeds); if (nodes_to_add.size > 0) { as_cluster_add_nodes(cluster, &nodes_to_add); status = AEROSPIKE_OK; } else { status = as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to seed cluster"); } as_vector_destroy(&nodes_to_add); as_vector_destroy(&addresses); return status; }
static as_status as_lookup_node(as_cluster* cluster, as_error* err, struct sockaddr_in* addr, as_node_info* node_info) { uint64_t deadline = as_socket_deadline(cluster->conn_timeout_ms); int fd; as_status status = as_info_create_socket(cluster, err, addr, deadline, &fd); if (status) { return status; } char* response = 0; status = as_info_command(err, fd, "node\nfeatures\n", true, deadline, 0, &response); if (status) { as_close(fd); return status; } as_vector values; as_vector_inita(&values, sizeof(as_name_value), 2); as_info_parse_multi_response(response, &values); if (values.size != 2) { goto Error; } as_name_value* nv = as_vector_get(&values, 0); char* node_name = nv->value; if (node_name == 0 || *node_name == 0) { goto Error; } as_strncpy(node_info->name, node_name, AS_NODE_NAME_SIZE); node_info->fd = fd; nv = as_vector_get(&values, 1); char* features = nv->value; if (features == 0) { goto Error; } char* begin = features; char* end = begin; uint8_t has_batch_index = 0; uint8_t has_replicas_all = 0; uint8_t has_double = 0; uint8_t has_geo = 0; while (*begin && ! (has_batch_index && has_replicas_all && has_double && has_geo)) { while (*end) { if (*end == ';') { *end++ = 0; break; } end++; } if (strcmp(begin, "batch-index") == 0) { has_batch_index = 1; } if (strcmp(begin, "replicas-all") == 0) { has_replicas_all = 1; } if (strcmp(begin, "float") == 0) { has_double = 1; } if (strcmp(begin, "geo") == 0) { has_geo = 1; } begin = end; } node_info->has_batch_index = has_batch_index; node_info->has_replicas_all = has_replicas_all; node_info->has_double = has_double; node_info->has_geo = has_geo; cf_free(response); return AEROSPIKE_OK; Error: { char addr_name[INET_ADDRSTRLEN]; as_socket_address_name(addr, addr_name); as_error_update(err, status, "Invalid node info response from %s: %s", addr_name, response); cf_free(response); as_close(fd); return AEROSPIKE_ERR_CLIENT; } }
/** * @return AEROSPIKE_OK if successful. Otherwise an error occurred. */ as_status aerospike_udf_list( aerospike * as, as_error * err, const as_policy_info * policy, as_udf_files * files) { as_error_reset(err); if (! policy) { policy = &as->config.policies.info; } char* response = 0; as_status status = aerospike_info_any(as, err, policy, "udf-list", &response); if (status) { return status; } // response := udf-list\tfilename=<name>,hash=<hash>,type=<type>;[filename=<name>...] char* p = strchr(response, '\t'); if (!p) { as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid udf-list response: %s", response); free(response); return AEROSPIKE_ERR_PARAM; } p++; uint32_t capacity = (files->capacity <= 0) ? 500 : files->capacity; as_vector ptrs; as_vector_inita(&ptrs, sizeof(as_udf_file_ptr), capacity); as_udf_file_ptr ptr = {0,0,0}; char* token = p; while (*p) { switch (*p) { case '=': *p++ = 0; as_udf_parse_file(token, p, &ptr); break; case ',': *p++ = 0; token = p; break; case ';': *p++ = 0; token = p; as_vector_append(&ptrs, &ptr); ptr.name = 0; ptr.hash = 0; ptr.type = 0; break; default: p++; break; } } if (files->capacity == 0 && files->entries == NULL) { as_udf_files_init(files, ptrs.size); } uint32_t limit = ptrs.size < files->capacity ? ptrs.size : files->capacity; files->size = limit; for (uint32_t i = 0; i < limit; i++) { as_udf_file_ptr* ptr = as_vector_get(&ptrs, i); as_udf_file* file = &files->entries[i]; if (ptr->name) { as_strncpy(file->name, ptr->name, AS_UDF_FILE_NAME_SIZE); } else { file->name[0] = 0; } if (ptr->hash) { // The hash is not null terminated, so normal strncpy is appropriate here. // strncpy will also pad zeroes if hash size is incorrect. strncpy((char*)file->hash, ptr->hash, AS_UDF_FILE_HASH_SIZE); } else { file->hash[0] = 0; } file->type = AS_UDF_TYPE_LUA; file->content._free = false; file->content.size = 0; file->content.capacity = 0; file->content.bytes = NULL; } as_vector_destroy(&ptrs); free(response); return AEROSPIKE_OK; }
/** * @return AEROSPIKE_OK if successful. Otherwise an error occurred. */ as_status aerospike_udf_get( aerospike * as, as_error * err, const as_policy_info * policy, const char * filename, as_udf_type type, as_udf_file * file) { as_error_reset(err); if (! policy) { policy = &as->config.policies.info; } char command[512]; snprintf(command, sizeof(command), "udf-get:filename=%s;", filename); char* response = 0; as_status status = aerospike_info_any(as, err, policy, command, &response); if (status) { return status; } // response := <command>\tgen=<string>;type=<string>;content=<string>; char* p = strchr(response, '\t'); if (!p) { as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid udf-get response: %s", response); free(response); return AEROSPIKE_ERR_PARAM; } p++; p = strstr(p, "content="); if (!p) { as_error_update(err, AEROSPIKE_ERR_PARAM, "Invalid udf-get response: %s", response); free(response); return AEROSPIKE_ERR_PARAM; } p += 8; as_strncpy(file->name, filename, AS_UDF_FILE_NAME_SIZE); file->type = AS_UDF_TYPE_LUA; char* content = p; while (*p) { if (*p == ';') { *p = 0; break; } p++; } uint32_t len = (uint32_t)(p - content); uint32_t size; cf_b64_validate_and_decode_in_place((uint8_t*)content, len, &size); // Update file hash unsigned char hash[SHA_DIGEST_LENGTH]; #ifdef __APPLE__ // Openssl is deprecated on mac, but the library is still included. // Save old settings and disable deprecated warnings. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif SHA1((uint8_t*)content, size, hash); #ifdef __APPLE__ // Restore old settings. #pragma GCC diagnostic pop #endif cf_convert_sha1_to_hex(hash, file->hash); file->content._free = true; file->content.size = size; file->content.capacity = size; file->content.bytes = malloc(size); memcpy(file->content.bytes, content, size); free(response); return AEROSPIKE_OK; }