static bool test_free_parent_reparent_child_in_pool(void) { void *top = talloc_new(NULL); char *level1; char *alternate_level1; char *level2; void *pool; struct new_parent *level3; printf("test: free_parent_reparent_child_in_pool\n# " "TALLOC FREE PARENT REPARENT CHILD IN POOL\n"); pool = talloc_pool(top, 1024); level1 = talloc_strdup(pool, "level1"); alternate_level1 = talloc_strdup(top, "alternate_level1"); level2 = talloc_strdup(level1, "level2"); level3 = talloc(level2, struct new_parent); level3->new_parent = alternate_level1; memset(level3->val, 'x', sizeof(level3->val)); talloc_set_destructor(level3, reparenting_destructor); talloc_free(level1); talloc_set_destructor(level3, NULL); CHECK_PARENT("free_parent_reparent_child_in_pool", level3, alternate_level1); /* Even freeing alternate_level1 should leave pool alone. */ talloc_free(alternate_level1); talloc_free(top); printf("success: free_parent_reparent_child_in_pool\n"); return true; }
/** Create thread-specific connections and buffers * * @param[in] conf section containing the configuration of this module instance. * @param[in] instance of rlm_logtee_t. * @param[in] el The event list serviced by this thread. * @param[in] thread specific data. * @return * - 0 on success. * - -1 on failure. */ static int mod_thread_instantiate(UNUSED CONF_SECTION const *conf, void *instance, fr_event_list_t *el, void *thread) { rlm_logtee_t *inst = talloc_get_type_abort(instance, rlm_logtee_t); rlm_logtee_thread_t *t = talloc_get_type_abort(thread, rlm_logtee_thread_t); MEM(t->fring = fr_fring_alloc(t, inst->buffer_depth, false)); t->inst = inst; t->el = el; /* * Pre-allocate temporary attributes */ MEM(t->msg_pool = talloc_pool(t, 1024)); MEM(t->msg = fr_pair_afrom_da(t->msg_pool, attr_log_message)); MEM(t->type = fr_pair_afrom_da(t, attr_log_type)); MEM(t->lvl = fr_pair_afrom_da(t, attr_log_level)); /* * This opens the outbound connection */ t->conn = fr_connection_alloc(t, el, &inst->connection_timeout, &inst->reconnection_delay, _logtee_conn_init, _logtee_conn_open, _logtee_conn_close, inst->name, t); if (t->conn == NULL) return -1; fr_connection_signal_init(t->conn); return 0; }
/** Setup an LDAP sync request * * Allocates a request, with request/reply packets. * * Sets various fields in the request->packet with information from the file descriptor * we received from libldap. * * @param[in] listen The common listener encapsulating the libldap fd. * @param[in] inst of the proto_ldap_sync module. * @param[in] sync_id the unique identifier of the sync. * @return * - A new request on success. * - NULL on error. */ static REQUEST *proto_ldap_request_setup(rad_listen_t *listen, proto_ldap_inst_t *inst, int sync_id) { TALLOC_CTX *ctx; RADIUS_PACKET *packet; REQUEST *request; ctx = talloc_pool(NULL, main_config->talloc_pool_size); if (!ctx) return NULL; talloc_set_name_const(ctx, "ldap_inst_pool"); packet = fr_radius_alloc(ctx, false); packet->sockfd = listen->fd; packet->id = sync_id; packet->src_ipaddr = inst->dst_ipaddr; packet->src_port = inst->dst_port; packet->dst_ipaddr = inst->src_ipaddr; packet->dst_port = inst->src_port; gettimeofday(&packet->timestamp, NULL); request = request_setup(ctx, listen, packet, inst->client, NULL); if (!request) return NULL; request->process = request_queued; return request; }
/** Insert a new entry into the data store * * @param inst main rlm_cache instance. * @param request The current request. * @param handle Pointer to memcached handle. * @param c entry to insert. * @return CACHE_OK on success else CACHE_ERROR on error. */ static cache_status_t cache_entry_insert(UNUSED rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, rlm_cache_entry_t *c) { rlm_cache_memcached_handle_t *mandle = *handle; memcached_return_t ret; TALLOC_CTX *pool; char *to_store; pool = talloc_pool(NULL, 1024); if (!pool) return CACHE_ERROR; if (cache_serialize(pool, &to_store, c) < 0) { talloc_free(pool); return CACHE_ERROR; } ret = memcached_set(mandle->handle, c->key, talloc_array_length(c->key) - 1, to_store ? to_store : "", to_store ? talloc_array_length(to_store) - 1 : 0, c->expires, 0); talloc_free(pool); if (ret != MEMCACHED_SUCCESS) { RERROR("Failed storing entry with key \"%s\": %s: %s", c->key, memcached_strerror(mandle->handle, ret), memcached_last_error_message(mandle->handle)); return CACHE_ERROR; } return CACHE_OK; }
void *mod_conn_create(TALLOC_CTX *ctx, void *instance) { int rcode; rlm_sql_t *inst = instance; rlm_sql_handle_t *handle; /* * Connections cannot be alloced from the inst or * pool contexts due to threading issues. */ handle = talloc_zero(ctx, rlm_sql_handle_t); if (!handle) return NULL; handle->log_ctx = talloc_pool(handle, 2048); if (!handle->log_ctx) { talloc_free(handle); return NULL; } /* * Handle requires a pointer to the SQL inst so the * destructor has access to the module configuration. */ handle->inst = inst; /* * When something frees this handle the destructor set by * the driver will be called first, closing any open sockets. * Then we call our destructor to trigger an modules.sql.close * event, then all the memory is freed. */ talloc_set_destructor(handle, _mod_conn_free); rcode = (inst->module->sql_socket_init)(handle, inst->config); if (rcode != 0) { fail: exec_trigger(NULL, inst->cs, "modules.sql.fail", true); /* * Destroy any half opened connections. */ talloc_free(handle); return NULL; } if (inst->config->connect_query) { if (rlm_sql_select_query(inst, NULL, &handle, inst->config->connect_query) != RLM_SQL_OK) goto fail; (inst->module->sql_finish_select_query)(handle, inst->config); } exec_trigger(NULL, inst->cs, "modules.sql.open", false); return handle; }
static bool test_pool_nest(void) { void *p1, *p2, *p3; void *e = talloc_new(NULL); p1 = talloc_pool(NULL, 1024); torture_assert("talloc_pool", p1 != NULL, "failed"); p2 = talloc_pool(p1, 500); torture_assert("talloc_pool", p2 != NULL, "failed"); p3 = talloc_size(p2, 10); talloc_steal(e, p3); talloc_free(p2); talloc_free(p3); talloc_free(p1); return true; }
static bool test_pool(void) { void *pool; void *p1, *p2, *p3, *p4; pool = talloc_pool(NULL, 1024); p1 = talloc_size(pool, 80); p2 = talloc_size(pool, 20); p3 = talloc_size(p1, 50); p4 = talloc_size(p3, 1000); talloc_free(pool); return true; }
void *mod_conn_create(TALLOC_CTX *ctx, void *instance) { int rcode; rlm_sql_t *inst = instance; rlm_sql_handle_t *handle; /* * Connections cannot be alloced from the inst or * pool contexts due to threading issues. */ handle = talloc_zero(ctx, rlm_sql_handle_t); if (!handle) return NULL; handle->log_ctx = talloc_pool(handle, 2048); if (!handle->log_ctx) { talloc_free(handle); return NULL; } /* * Handle requires a pointer to the SQL inst so the * destructor has access to the module configuration. */ handle->inst = inst; rcode = (inst->module->sql_socket_init)(handle, inst->config); if (rcode != 0) { fail: /* * Destroy any half opened connections. */ talloc_free(handle); return NULL; } if (inst->config->connect_query) { if (rlm_sql_select_query(inst, NULL, &handle, inst->config->connect_query) != RLM_SQL_OK) goto fail; (inst->module->sql_finish_select_query)(handle, inst->config); } return handle; }
/** Insert a new entry into the data store * * @copydetails cache_entry_insert_t */ static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config, void *driver_inst, REQUEST *request, UNUSED void *handle, const rlm_cache_entry_t *c) { rlm_cache_redis_t *driver = driver_inst; TALLOC_CTX *pool; vp_map_t *map; fr_redis_conn_t *conn; fr_redis_cluster_state_t state; fr_redis_rcode_t status; redisReply *reply = NULL; int s_ret; static char const command[] = "RPUSH"; char const **argv; size_t *argv_len; char const **argv_p; size_t *argv_len_p; int pipelined = 0; /* How many commands pending in the pipeline */ redisReply *replies[5]; /* Should have the same number of elements as pipelined commands */ size_t reply_num = 0, i; char *p; int cnt; vp_tmpl_t expires_value; vp_map_t expires = { .op = T_OP_SET, .lhs = &driver->expires_attr, .rhs = &expires_value, }; vp_tmpl_t created_value; vp_map_t created = { .op = T_OP_SET, .lhs = &driver->created_attr, .rhs = &created_value, .next = &expires }; /* * Encode the entry created date */ tmpl_init(&created_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD); created_value.tmpl_data_type = PW_TYPE_DATE; created_value.tmpl_data_length = sizeof(created_value.tmpl_data_value.date); created_value.tmpl_data_value.date = c->created; /* * Encode the entry expiry time * * Although Redis objects expire on their own, we still need this * to ignore entries that were created before the last epoch. */ tmpl_init(&expires_value, TMPL_TYPE_DATA, "<TEMP>", 6, T_BARE_WORD); expires_value.tmpl_data_type = PW_TYPE_DATE; expires_value.tmpl_data_length = sizeof(expires_value.tmpl_data_value.date); expires_value.tmpl_data_value.date = c->expires; expires.next = c->maps; /* Head of the list */ for (cnt = 0, map = &created; map; cnt++, map = map->next); /* * The majority of serialized entries should be under 1k. * * @todo We should really calculate this using some sort of moving average. */ pool = talloc_pool(request, 1024); if (!pool) return CACHE_ERROR; argv_p = argv = talloc_array(pool, char const *, (cnt * 3) + 2); /* pair = 3 + cmd + key */ argv_len_p = argv_len = talloc_array(pool, size_t, (cnt * 3) + 2); /* pair = 3 + cmd + key */ *argv_p++ = command; *argv_len_p++ = sizeof(command) - 1; *argv_p++ = (char const *)c->key; *argv_len_p++ = c->key_len; /* * Add the maps to the command string in reverse order */ for (map = &created; map; map = map->next) { if (fr_redis_tuple_from_map(pool, argv_p, argv_len_p, map) < 0) { REDEBUG("Failed encoding map as Redis K/V pair"); talloc_free(pool); return CACHE_ERROR; } argv_p += 3; argv_len_p += 3; } RDEBUG3("Pipelining commands"); RINDENT(); for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, c->key, c->key_len, false); s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */ s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) { /* * Start the transaction, as we need to set an expiry time too. */ if (c->expires > 0) { RDEBUG3("MULTI"); if (redisAppendCommand(conn->handle, "MULTI") != REDIS_OK) { append_error: REXDENT(); RERROR("Failed appending Redis command to output buffer: %s", conn->handle->errstr); talloc_free(pool); return CACHE_ERROR; } pipelined++; } if (RDEBUG_ENABLED3) { p = fr_asprint(request, (char const *)c->key, c->key_len, '\0'); RDEBUG3("DEL \"%s\"", p); talloc_free(p); } if (redisAppendCommand(conn->handle, "DEL %b", c->key, c->key_len) != REDIS_OK) goto append_error; pipelined++; if (RDEBUG_ENABLED3) { RDEBUG3("argv command"); RINDENT(); for (i = 0; i < talloc_array_length(argv); i++) { p = fr_asprint(request, argv[i], argv_len[i], '\0'); RDEBUG3("%s", p); talloc_free(p); } REXDENT(); } redisAppendCommandArgv(conn->handle, talloc_array_length(argv), argv, argv_len); pipelined++; /* * Set the expiry time and close out the transaction. */ if (c->expires > 0) { if (RDEBUG_ENABLED3) { p = fr_asprint(request, (char const *)c->key, c->key_len, '\"'); RDEBUG3("EXPIREAT \"%s\" %li", p, (long)c->expires); talloc_free(p); } if (redisAppendCommand(conn->handle, "EXPIREAT %b %i", c->key, c->key_len, c->expires) != REDIS_OK) goto append_error; pipelined++; RDEBUG3("EXEC"); if (redisAppendCommand(conn->handle, "EXEC") != REDIS_OK) goto append_error; pipelined++; } REXDENT(); reply_num = fr_redis_pipeline_result(&status, replies, sizeof(replies) / sizeof(*replies), conn, pipelined); reply = replies[0]; } talloc_free(pool); if (s_ret != REDIS_RCODE_SUCCESS) { RERROR("Failed inserting entry"); return CACHE_ERROR; } RDEBUG3("Command results"); RINDENT(); for (i = 0; i < reply_num; i++) { fr_redis_reply_print(L_DBG_LVL_3, replies[i], request, i); fr_redis_reply_free(replies[i]); } REXDENT(); return CACHE_OK; } /** Call delete the cache entry from redis * * @copydetails cache_entry_expire_t */ static cache_status_t cache_entry_expire(UNUSED rlm_cache_config_t const *config, void *driver_inst, REQUEST *request, UNUSED void *handle, uint8_t const *key, size_t key_len) { rlm_cache_redis_t *driver = driver_inst; fr_redis_cluster_state_t state; fr_redis_conn_t *conn; fr_redis_rcode_t status; redisReply *reply = NULL; int s_ret; for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, key, key_len, false); s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */ s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) { reply = redisCommand(conn->handle, "DEL %b", key, key_len); status = fr_redis_command_status(conn, reply); } if (s_ret != REDIS_RCODE_SUCCESS) { RERROR("Failed expiring entry"); fr_redis_reply_free(reply); return CACHE_ERROR; } rad_assert(reply); /* clang scan */ if (reply->type == REDIS_REPLY_INTEGER) { fr_redis_reply_free(reply); if (reply->integer) return CACHE_OK; /* Affected */ return CACHE_MISS; } REDEBUG("Bad result type, expected integer, got %s", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); fr_redis_reply_free(reply); return CACHE_ERROR; } extern cache_driver_t rlm_cache_redis; cache_driver_t rlm_cache_redis = { .name = "rlm_cache_redis", .instantiate = mod_instantiate, .inst_size = sizeof(rlm_cache_redis_t), .free = cache_entry_free, .find = cache_entry_find, .insert = cache_entry_insert, .expire = cache_entry_expire, };
/** Handle authorization requests using Couchbase document data * * Attempt to fetch the document assocaited with the requested user by * using the deterministic key defined in the configuration. When a valid * document is found it will be parsed and the containing value pairs will be * injected into the request. * * @param instance The module instance. * @param thread specific data. * @param request The authorization request. * @return Operation status (#rlm_rcode_t). */ static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *request) { rlm_couchbase_t const *inst = instance; /* our module instance */ rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */ char buffer[MAX_KEY_SIZE]; char const *dockey; /* our document key */ lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error holder */ rlm_rcode_t rcode = RLM_MODULE_OK; /* return code */ ssize_t slen; /* assert packet as not null */ rad_assert(request->packet != NULL); /* attempt to build document key */ slen = tmpl_expand(&dockey, buffer, sizeof(buffer), request, inst->user_key, NULL, NULL); if (slen < 0) return RLM_MODULE_FAIL; if ((dockey == buffer) && is_truncated((size_t)slen, sizeof(buffer))) { REDEBUG("Key too long, expected < " STRINGIFY(sizeof(buffer)) " bytes, got %zi bytes", slen); return RLM_MODULE_FAIL; } /* get handle */ handle = fr_pool_connection_get(inst->pool, request); /* check handle */ if (!handle) return RLM_MODULE_FAIL; /* set couchbase instance */ lcb_t cb_inst = handle->handle; /* set cookie */ cookie_t *cookie = handle->cookie; /* fetch document */ cb_error = couchbase_get_key(cb_inst, cookie, dockey); /* check error */ if (cb_error != LCB_SUCCESS || !cookie->jobj) { /* log error */ RERROR("failed to fetch document or parse return"); /* set return */ rcode = RLM_MODULE_FAIL; /* return */ goto finish; } /* debugging */ RDEBUG3("parsed user document == %s", json_object_to_json_string(cookie->jobj)); { TALLOC_CTX *pool = talloc_pool(request, 1024); /* We need to do lots of allocs */ fr_cursor_t maps, vlms; vp_map_t *map_head = NULL, *map; vp_list_mod_t *vlm_head = NULL, *vlm; fr_cursor_init(&maps, &map_head); /* * Convert JSON data into maps */ if ((mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_CONTROL) < 0) || (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_REPLY) < 0) || (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_REQUEST) < 0) || (mod_json_object_to_map(pool, &maps, request, cookie->jobj, PAIR_LIST_STATE) < 0)) { invalid: talloc_free(pool); rcode = RLM_MODULE_INVALID; goto finish; } fr_cursor_init(&vlms, &vlm_head); /* * Convert all the maps into list modifications, * which are guaranteed to succeed. */ for (map = fr_cursor_head(&maps); map; map = fr_cursor_next(&maps)) { if (map_to_list_mod(pool, &vlm, request, map, NULL, NULL) < 0) goto invalid; fr_cursor_insert(&vlms, vlm); } if (!vlm_head) { RDEBUG2("Nothing to update"); talloc_free(pool); rcode = RLM_MODULE_NOOP; goto finish; } /* * Apply the list of modifications */ for (vlm = fr_cursor_head(&vlms); vlm; vlm = fr_cursor_next(&vlms)) { int ret; ret = map_list_mod_apply(request, vlm); /* SHOULD NOT FAIL */ if (!fr_cond_assert(ret == 0)) { talloc_free(pool); rcode = RLM_MODULE_FAIL; goto finish; } } talloc_free(pool); } finish: /* free json object */ if (cookie->jobj) { json_object_put(cookie->jobj); cookie->jobj = NULL; } /* release handle */ if (handle) fr_pool_connection_release(inst->pool, request, handle); /* return */ return rcode; }
/** Create and insert a cache entry * * @return * - #RLM_MODULE_OK on success. * - #RLM_MODULE_UPDATED if we merged the cache entry. * - #RLM_MODULE_FAIL on failure. */ static rlm_rcode_t cache_insert(rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle, uint8_t const *key, size_t key_len, int ttl) { vp_map_t const *map; vp_map_t **last, *c_map; VALUE_PAIR *vp; bool merge = false; rlm_cache_entry_t *c; size_t len; TALLOC_CTX *pool; if ((inst->config.max_entries > 0) && inst->driver->count && (inst->driver->count(&inst->config, inst->driver_inst->data, request, handle) > inst->config.max_entries)) { RWDEBUG("Cache is full: %d entries", inst->config.max_entries); return RLM_MODULE_FAIL; } c = cache_alloc(inst, request); if (!c) return RLM_MODULE_FAIL; c->key = talloc_memdup(c, key, key_len); c->key_len = key_len; c->created = c->expires = request->packet->timestamp.tv_sec; c->expires += ttl; last = &c->maps; RDEBUG2("Creating new cache entry"); /* * Alloc a pool so we don't have excessive allocs when * gathering VALUE_PAIRs to cache. */ pool = talloc_pool(NULL, 2048); for (map = inst->maps; map != NULL; map = map->next) { VALUE_PAIR *to_cache = NULL; fr_cursor_t cursor; rad_assert(map->lhs && map->rhs); /* * Calling map_to_vp gives us exactly the same result, * as if this were an update section. */ if (map_to_vp(pool, &to_cache, request, map, NULL) < 0) { RDEBUG2("Skipping %s", map->rhs->name); continue; } for (vp = fr_cursor_init(&cursor, &to_cache); vp; vp = fr_cursor_next(&cursor)) { /* * Prevent people from accidentally caching * cache control attributes. */ if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) { case FR_CACHE_TTL: case FR_CACHE_STATUS_ONLY: case FR_CACHE_MERGE_NEW: case FR_CACHE_ENTRY_HITS: RDEBUG2("Skipping %s", vp->da->name); continue; default: break; } RINDENT(); if (RDEBUG_ENABLED2) map_debug_log(request, map, vp); REXDENT(); MEM(c_map = talloc_zero(c, vp_map_t)); c_map->op = map->op; /* * Now we turn the VALUE_PAIRs into maps. */ switch (map->lhs->type) { /* * Attributes are easy, reuse the LHS, and create a new * RHS with the fr_value_box_t from the VALUE_PAIR. */ case TMPL_TYPE_ATTR: c_map->lhs = map->lhs; /* lhs shouldn't be touched, so this is ok */ do_rhs: MEM(c_map->rhs = tmpl_init(talloc(c_map, vp_tmpl_t), TMPL_TYPE_DATA, map->rhs->name, map->rhs->len, T_BARE_WORD)); if (fr_value_box_copy(c_map->rhs, &c_map->rhs->tmpl_value, &vp->data) < 0) { REDEBUG("Failed copying attribute value"); error: talloc_free(pool); talloc_free(c); return RLM_MODULE_FAIL; } c_map->rhs->tmpl_value_type = vp->vp_type; if (vp->vp_type == FR_TYPE_STRING) { c_map->rhs->quote = is_printable(vp->vp_strvalue, vp->vp_length) ? T_SINGLE_QUOTED_STRING : T_DOUBLE_QUOTED_STRING; } break; /* * Lists are weird... We need to fudge a new LHS template, * which is a combination of the LHS list and the attribute. */ case TMPL_TYPE_LIST: { char attr[256]; MEM(c_map->lhs = tmpl_init(talloc(c_map, vp_tmpl_t), TMPL_TYPE_ATTR, map->lhs->name, map->lhs->len, T_BARE_WORD)); c_map->lhs->tmpl_da = vp->da; if (vp->da->flags.is_unknown) { /* for tmpl_verify() */ c_map->lhs->tmpl_unknown = fr_dict_unknown_acopy(c_map->lhs, vp->da); c_map->lhs->tmpl_da = c_map->lhs->tmpl_unknown; } c_map->lhs->tmpl_tag = vp->tag; c_map->lhs->tmpl_list = map->lhs->tmpl_list; c_map->lhs->tmpl_num = map->lhs->tmpl_num; c_map->lhs->tmpl_request = map->lhs->tmpl_request; /* * We need to rebuild the attribute name, to be the * one we copied from the source list. */ len = tmpl_snprint(attr, sizeof(attr), c_map->lhs); if (is_truncated(len, sizeof(attr))) { REDEBUG("Serialized attribute too long. Must be < " STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", len); goto error; } c_map->lhs->len = len; c_map->lhs->name = talloc_typed_strdup(c_map->lhs, attr); } goto do_rhs; default: rad_assert(0); } *last = c_map; last = &(*last)->next; } talloc_free_children(pool); /* reset pool state */ } talloc_free(pool); /* * Check to see if we need to merge the entry into the request */ vp = fr_pair_find_by_da(request->control, attr_cache_merge_new, TAG_ANY); if (vp && vp->vp_bool) merge = true; if (merge) cache_merge(inst, request, c); for (;;) { cache_status_t ret; ret = inst->driver->insert(&inst->config, inst->driver_inst->data, request, *handle, c); switch (ret) { case CACHE_RECONNECT: if (cache_reconnect(handle, inst, request) == 0) continue; return RLM_MODULE_FAIL; case CACHE_OK: RDEBUG2("Committed entry, TTL %d seconds", ttl); cache_free(inst, &c); return merge ? RLM_MODULE_UPDATED : RLM_MODULE_OK; default: talloc_free(c); /* Failed insertion - use talloc_free not the driver free */ return RLM_MODULE_FAIL; } } }
int bts_main(int argc, char **argv) { struct gsm_bts_role_bts *btsb; struct gsm_bts_trx *trx; struct e1inp_line *line; void *tall_msgb_ctx; int rc, i; printf("((*))\n |\n / \\ OsmoBTS\n"); tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context"); tall_msgb_ctx = talloc_pool(tall_bts_ctx, 100*1024); msgb_set_talloc_ctx(tall_msgb_ctx); bts_log_init(NULL); handle_options(argc, argv); bts = gsm_bts_alloc(tall_bts_ctx); if (!bts) { fprintf(stderr, "Failed to create BTS structure\n"); exit(1); } for (i = 1; i < trx_num; i++) { trx = gsm_bts_trx_alloc(bts); if (!trx) { fprintf(stderr, "Failed to create TRX structure\n"); exit(1); } } vty_init(&bts_vty_info); e1inp_vty_init(); bts_vty_init(bts, &bts_log_info); /* enable realtime priority for us */ if (rt_prio != -1) { struct sched_param param; memset(¶m, 0, sizeof(param)); param.sched_priority = rt_prio; rc = sched_setscheduler(getpid(), SCHED_RR, ¶m); if (rc != 0) { fprintf(stderr, "Setting SCHED_RR priority(%d) failed: %s\n", param.sched_priority, strerror(errno)); exit(1); } } if (gsmtap_ip) { gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1); if (!gsmtap) { fprintf(stderr, "Failed during gsmtap_init()\n"); exit(1); } gsmtap_source_add_sink(gsmtap); } if (bts_init(bts) < 0) { fprintf(stderr, "unable to open bts\n"); exit(1); } abis_init(bts); rc = vty_read_config_file(config_file, NULL); if (rc < 0) { fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); exit(1); } write_pid_file("osmo-bts"); bts_controlif_setup(bts); rc = telnet_init(tall_bts_ctx, NULL, OSMO_VTY_PORT_BTS); if (rc < 0) { fprintf(stderr, "Error initializing telnet\n"); exit(1); } if (pcu_sock_init()) { fprintf(stderr, "PCU L1 socket failed\n"); exit(1); } signal(SIGINT, &signal_handler); //signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); btsb = bts_role_bts(bts); if (!btsb->bsc_oml_host) { fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n"); exit(1); } line = abis_open(bts, btsb->bsc_oml_host, "sysmoBTS"); if (!line) { fprintf(stderr, "unable to connect to BSC\n"); exit(2); } if (daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); exit(1); } } while (quit < 2) { log_reset_context(); osmo_select_main(0); } return EXIT_SUCCESS; }
/** Convert group membership information into attributes * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search. * @param[in] attr membership attribute to look for in the entry. * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, LDAPMessage *entry, char const *attr) { rlm_rcode_t rcode = RLM_MODULE_OK; struct berval **values; size_t value_len = 0; TALLOC_CTX *value_pool; char *group_name[LDAP_MAX_CACHEABLE + 1]; char **name_p = group_name; char *group_dn[LDAP_MAX_CACHEABLE + 1]; char **dn_p; char *name; VALUE_PAIR *vp, **vps; TALLOC_CTX *ctx; vp_cursor_t cursor; int is_dn, i, count; rad_assert(entry); rad_assert(attr); /* * Parse the membership information we got in the initial user query. */ values = ldap_get_values_len((*pconn)->handle, entry, attr); if (!values) { RDEBUG2("No cacheable group memberships found in user object"); return RLM_MODULE_OK; } count = ldap_count_values_len(values); vps = radius_list(request, PAIR_LIST_CONTROL); ctx = radius_list_ctx(request, PAIR_LIST_CONTROL); fr_cursor_init(&cursor, vps); /* * Avoid allocing buffers for each value. * * The old code used ldap_get_values, which was likely doing * a very similar thing internally to produce \0 terminated * buffers from bervalues. */ for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) value_len += values[i]->bv_len + 1; value_pool = talloc_pool(request, value_len); for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) { is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len); if (inst->cacheable_group_dn) { /* * The easy case, we're caching DNs and we got a DN. */ if (is_dn) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len); fr_cursor_insert(&cursor, vp); RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name, vp->vp_strvalue); /* * We were told to cache DNs but we got a name, we now need to resolve * this to a DN. Store all the group names in an array so we can do one query. */ } else { *name_p++ = rlm_ldap_berval_to_string(value_pool, values[i]); } } if (inst->cacheable_group_name) { /* * The easy case, we're caching names and we got a name. */ if (!is_dn) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, values[i]->bv_val, values[i]->bv_len); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, vp->vp_strvalue); /* * We were told to cache names but we got a DN, we now need to resolve * this to a name. * Only Active Directory supports filtering on DN, so we have to search * for each individual group. */ } else { char *dn; dn = rlm_ldap_berval_to_string(value_pool, values[i]); rcode = rlm_ldap_group_dn2name(inst, request, pconn, dn, &name); talloc_free(dn); if (rcode != RLM_MODULE_OK) { ldap_value_free_len(values); talloc_free(value_pool); return rcode; } MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrncpy(vp, name, talloc_array_length(name) - 1); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, name); talloc_free(name); } } } *name_p = NULL; rcode = rlm_ldap_group_name2dn(inst, request, pconn, group_name, group_dn, sizeof(group_dn)); ldap_value_free_len(values); talloc_free(value_pool); if (rcode != RLM_MODULE_OK) return rcode; dn_p = group_dn; while (*dn_p) { MEM(vp = pairalloc(ctx, inst->cache_da)); pairstrcpy(vp, *dn_p); fr_cursor_insert(&cursor, vp); RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, *dn_p); ldap_memfree(*dn_p); dn_p++; } return rcode; }
void do_db( struct configuration_data *config, char *dbstring) { int rc; int try; const char *error; dbi_result result; /** * a NULL dbstring? May happen when a R/W VFS function * hat 0 bytes to transfer. This data is not relevant * for statistics. */ if (dbstring == NULL) return; /** * Check if the connection is alive. We try ten times * to restore the connection if not */ for (try = 0; try < 10; try++) { rc = dbi_conn_ping( config->DBIconn ); if (rc == 1) { result = dbi_conn_query(config->DBIconn, dbstring); if (result == NULL) { dbi_conn_error(config->DBIconn,&error); syslog(LOG_DEBUG,"DBI ERROR: %s",error); rc = 0;} else break; } } if (rc == 0 ) { syslog(LOG_DEBUG, "ERROR: Database handling error!"); return; } dbi_result_free(result); } void cleanup_cache( TALLOC_CTX *ctx,struct configuration_data *config, struct cache_entry *entry) { char *dbstring; struct cache_entry *go = entry; struct cache_entry *backup; struct cache_entry *backup2 = NULL; struct cache_entry *down =NULL; // left go = go->left; while (go != NULL) { backup = go->left; dbstring = cache_create_database_string(ctx,go,config); do_db(config,dbstring); talloc_free(dbstring); // go down down = go->down; while (down != NULL) { backup2 = down->down; dbstring = cache_create_database_string(ctx,down,config); do_db(config,dbstring); talloc_free(dbstring); talloc_free(down); down = backup2; } talloc_free(go); go = backup; } // right go = entry->right; while (go != NULL) { backup = go->right; dbstring = cache_create_database_string(ctx,go,config); do_db(config,dbstring); talloc_free(dbstring); // go down down = go->down; while (down != NULL) { backup2 = down->down; dbstring = cache_create_database_string(ctx,down,config); do_db(config,dbstring); talloc_free(dbstring); talloc_free(down); down = backup2; } talloc_free(go); go = backup; } // other_ops go = entry->other_ops; while (go != NULL) { backup = go->other_ops; dbstring = cache_create_database_string(ctx,go,config); do_db(config,dbstring); talloc_free(dbstring); talloc_free(go); go = backup; } // delete tree begin dbstring = cache_create_database_string(ctx,entry,config); do_db(config,dbstring); if (dbstring != NULL) talloc_free(dbstring); talloc_free(entry); } /* * cache_manager runs as a detached thread. Every half second, * it's checking if there's data available to store to the DB. * in case of yes, it walks through the list of data, enables * a new cache, and stores the data in the DB */ void cache_manager(struct configuration_data *config ) { int maintenance_c_val; if (config->precision>0) maintenance_c_val = config->maintenance_seconds / config->precision; else maintenance_c_val = config->maintenance_seconds; int maintenance_count = 0; pthread_detach(pthread_self()); /* backup the start and make it possible to * allocate a new cache beginning */ while (1 == 1) { if (config->precision != 0) sleep(config->precision); else sleep(5); maintenance_count++; pthread_mutex_lock(&cache_mutex); struct cache_entry *backup = cache_start; TALLOC_CTX *dbpool = talloc_pool(NULL,2048); cache_start = NULL; cache_end = NULL; pthread_mutex_unlock(&cache_mutex); if (backup != NULL) { /* store all existing entries into the database */ do_db(config,"BEGIN TRANSACTION;"); cleanup_cache( dbpool,config,backup); do_db(config,"COMMIT;"); } talloc_free(dbpool); if (maintenance_count == maintenance_c_val) { char String[400]; char dbstring[300]; struct tm *tm; do_db(config,"BEGIN TRANSACTION;"); time_t today=time(NULL); time_t delete_date=today - config->maint_run_time; tm = localtime ( &delete_date ); sprintf(String,"%04d-%02d-%02d %02d:%02d:%02d", \ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, \ tm->tm_hour, tm->tm_min, tm->tm_sec); strcpy(dbstring,"delete from data where timestamp < '"); strcat(dbstring,String); strcat(dbstring,"';"); do_db(config,dbstring); do_db(config,"COMMIT;"); maintenance_count = 0; } } }
static bool test_pool(void) { void *pool; void *p1, *p2, *p3, *p4; void *p2_2; pool = talloc_pool(NULL, 1024); p1 = talloc_size(pool, 80); memset(p1, 0x11, talloc_get_size(p1)); p2 = talloc_size(pool, 20); memset(p2, 0x11, talloc_get_size(p2)); p3 = talloc_size(p1, 50); memset(p3, 0x11, talloc_get_size(p3)); p4 = talloc_size(p3, 1000); memset(p4, 0x11, talloc_get_size(p4)); #if 1 /* this relies on ALWAYS_REALLOC == 0 in talloc.c */ p2_2 = talloc_realloc_size(pool, p2, 20+1); torture_assert("pool realloc 20+1", p2_2 == p2, "failed: pointer changed"); memset(p2, 0x11, talloc_get_size(p2)); p2_2 = talloc_realloc_size(pool, p2, 20-1); torture_assert("pool realloc 20-1", p2_2 == p2, "failed: pointer changed"); memset(p2, 0x11, talloc_get_size(p2)); p2_2 = talloc_realloc_size(pool, p2, 20-1); torture_assert("pool realloc 20-1", p2_2 == p2, "failed: pointer changed"); memset(p2, 0x11, talloc_get_size(p2)); talloc_free(p3); /* this should reclaim the memory of p4 and p3 */ p2_2 = talloc_realloc_size(pool, p2, 400); torture_assert("pool realloc 400", p2_2 == p2, "failed: pointer changed"); memset(p2, 0x11, talloc_get_size(p2)); talloc_free(p1); /* this should reclaim the memory of p1 */ p2_2 = talloc_realloc_size(pool, p2, 800); torture_assert("pool realloc 800", p2_2 == p1, "failed: pointer not changed"); p2 = p2_2; memset(p2, 0x11, talloc_get_size(p2)); /* this should do a malloc */ p2_2 = talloc_realloc_size(pool, p2, 1800); torture_assert("pool realloc 1800", p2_2 != p2, "failed: pointer not changed"); p2 = p2_2; memset(p2, 0x11, talloc_get_size(p2)); /* this should reclaim the memory from the pool */ p3 = talloc_size(pool, 80); torture_assert("pool alloc 80", p3 == p1, "failed: pointer changed"); memset(p3, 0x11, talloc_get_size(p3)); talloc_free(p2); talloc_free(p3); p1 = talloc_size(pool, 80); memset(p1, 0x11, talloc_get_size(p1)); p2 = talloc_size(pool, 20); memset(p2, 0x11, talloc_get_size(p2)); talloc_free(p1); p2_2 = talloc_realloc_size(pool, p2, 20-1); torture_assert("pool realloc 20-1", p2_2 == p2, "failed: pointer changed"); memset(p2, 0x11, talloc_get_size(p2)); p2_2 = talloc_realloc_size(pool, p2, 20-1); torture_assert("pool realloc 20-1", p2_2 == p2, "failed: pointer changed"); memset(p2, 0x11, talloc_get_size(p2)); /* this should do a malloc */ p2_2 = talloc_realloc_size(pool, p2, 1800); torture_assert("pool realloc 1800", p2_2 != p2, "failed: pointer not changed"); p2 = p2_2; memset(p2, 0x11, talloc_get_size(p2)); /* this should reclaim the memory from the pool */ p3 = talloc_size(pool, 800); torture_assert("pool alloc 800", p3 == p1, "failed: pointer changed"); memset(p3, 0x11, talloc_get_size(p3)); #endif /* this relies on ALWAYS_REALLOC == 0 in talloc.c */ talloc_free(pool); return true; }
/** Converts a serialized cache entry back into a structure * * @param c Cache entry to populate (should already be allocated) * @param in String representation of cache entry. * @param inlen Length of string. May be < 0 in which case strlen will be * used to calculate the length of the string. * @return 0 on success, -1 on error. */ int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen) { vp_cursor_t packet, control, reply; TALLOC_CTX *store = NULL; char *p, *q; store = talloc_pool(c, 1024); if (!store) return -1; if (inlen < 0) inlen = strlen(in); fr_cursor_init(&packet, &c->packet); fr_cursor_init(&control, &c->control); fr_cursor_init(&reply, &c->reply); p = in; while (((size_t)(p - in)) < (size_t)inlen) { value_pair_map_t *map = NULL; VALUE_PAIR *vp = NULL; ssize_t len; q = strchr(p, '\n'); if (!q) break; /* List should also be terminated with a \n */ *q = '\0'; if (map_afrom_attr_str(store, &map, p, REQUEST_CURRENT, PAIR_LIST_REQUEST, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { fr_strerror_printf("Failed parsing pair: %s", p); goto error; } if (map->lhs->type != TMPL_TYPE_ATTR) { fr_strerror_printf("Pair left hand side \"%s\" parsed as %s, needed attribute. " "Check local dictionaries", map->lhs->name, fr_int2str(tmpl_names, map->lhs->type, "<INVALID>")); goto error; } if (map->rhs->type != TMPL_TYPE_LITERAL) { fr_strerror_printf("Pair right hand side \"%s\" parsed as %s, needed literal. " "Check serialized data quoting", map->rhs->name, fr_int2str(tmpl_names, map->rhs->type, "<INVALID>")); goto error; } /* * Convert literal to a type appropriate for * the VP. */ if (tmpl_cast_in_place(map->rhs, map->lhs->tmpl_da->type, map->lhs->tmpl_da) < 0) goto error; vp = pairalloc(c, map->lhs->tmpl_da); len = value_data_copy(vp, &vp->data, map->rhs->tmpl_data_type, &map->rhs->tmpl_data_value, map->rhs->tmpl_data_length); if (len < 0) goto error; /* * Pull out the special attributes, and set the * relevant cache entry fields. */ if (vp->da->vendor == 0) switch (vp->da->attr) { case PW_CACHE_CREATED: c->created = vp->vp_date; talloc_free(vp); goto next; case PW_CACHE_EXPIRES: c->expires = vp->vp_date; talloc_free(vp); goto next; default: break; } switch (map->lhs->tmpl_list) { case PAIR_LIST_REQUEST: fr_cursor_insert(&packet, vp); break; case PAIR_LIST_CONTROL: fr_cursor_insert(&control, vp); break; case PAIR_LIST_REPLY: fr_cursor_insert(&reply, vp); break; default: fr_strerror_printf("Invalid cache list for pair: %s", p); error: talloc_free(vp); talloc_free(map); return -1; } next: p = q + 1; talloc_free(map); } return 0; }
static bool test_pool_steal(void) { void *root; void *pool; void *p1, *p2; void *p1_2, *p2_2; size_t hdr; size_t ofs1, ofs2; root = talloc_new(NULL); pool = talloc_pool(root, 1024); p1 = talloc_size(pool, 4 * 16); torture_assert("pool allocate 4 * 16", p1 != NULL, "failed "); memset(p1, 0x11, talloc_get_size(p1)); p2 = talloc_size(pool, 4 * 16); torture_assert("pool allocate 4 * 16", p2 > p1, "failed: !(p2 > p1) "); memset(p2, 0x11, talloc_get_size(p2)); ofs1 = PTR_DIFF(p2, p1); hdr = ofs1 - talloc_get_size(p1); talloc_steal(root, p1); talloc_steal(root, p2); talloc_free(pool); p1_2 = p1; #if 1 /* this relies on ALWAYS_REALLOC == 0 in talloc.c */ p1_2 = talloc_realloc_size(root, p1, 5 * 16); torture_assert("pool realloc 5 * 16", p1_2 > p2, "failed: pointer not changed"); memset(p1_2, 0x11, talloc_get_size(p1_2)); ofs1 = PTR_DIFF(p1_2, p2); ofs2 = talloc_get_size(p2) + hdr; torture_assert("pool realloc ", ofs1 == ofs2, "failed: pointer offset unexpected"); p2_2 = talloc_realloc_size(root, p2, 3 * 16); torture_assert("pool realloc 5 * 16", p2_2 == p2, "failed: pointer changed"); memset(p2_2, 0x11, talloc_get_size(p2_2)); #endif /* this relies on ALWAYS_REALLOC == 0 in talloc.c */ talloc_free(p1_2); p2_2 = p2; #if 1 /* this relies on ALWAYS_REALLOC == 0 in talloc.c */ /* now we should reclaim the full pool */ p2_2 = talloc_realloc_size(root, p2, 8 * 16); torture_assert("pool realloc 8 * 16", p2_2 == p1, "failed: pointer not expected"); p2 = p2_2; memset(p2_2, 0x11, talloc_get_size(p2_2)); /* now we malloc and free the full pool space */ p2_2 = talloc_realloc_size(root, p2, 2 * 1024); torture_assert("pool realloc 2 * 1024", p2_2 != p1, "failed: pointer not expected"); memset(p2_2, 0x11, talloc_get_size(p2_2)); #endif /* this relies on ALWAYS_REALLOC == 0 in talloc.c */ talloc_free(p2_2); talloc_free(root); return true; }
/** Serialize a cache entry as a humanly readable string * * @param ctx to alloc new string in. Should be a talloc pool a little bigger * than the maximum serialized size of the entry. * @param out Where to write pointer to serialized cache entry. * @param c Cache entry to serialize. * @return 0 on success else -1. */ int cache_serialize(TALLOC_CTX *ctx, char **out, rlm_cache_entry_t *c) { TALLOC_CTX *pairs = NULL; vp_cursor_t cursor; VALUE_PAIR *vp; char *to_store = NULL, *pair; to_store = talloc_asprintf(ctx, "&Cache-Expires = %" PRIu64 "\n&Cache-Created = %" PRIu64 "\n", (uint64_t)c->expires, (uint64_t)c->created); if (!to_store) goto error; /* * It's valid to have an empty cache entry (save allocing the * pairs pool) */ if (!c->control && !c->packet && !c->reply) goto finish; /* * In the majority of cases using these pools reduces the number of mallocs * to two, except in the case where the total serialized pairs length is * greater than the pairs pool, or the total serialized string is greater * than the store pool. */ pairs = talloc_pool(ctx, 512); if (!pairs) { error: talloc_free(pairs); return -1; } if (c->control) { for (vp = fr_cursor_init(&cursor, &c->control); vp; vp = fr_cursor_next(&cursor)) { pair = vp_aprints(pairs, vp, '\''); if (!pair) goto error; to_store = talloc_asprintf_append_buffer(to_store, "&control:%s\n", pair); if (!to_store) goto error; } } if (c->packet) { for (vp = fr_cursor_init(&cursor, &c->packet); vp; vp = fr_cursor_next(&cursor)) { pair = vp_aprints(pairs, vp, '\''); if (!pair) goto error; to_store = talloc_asprintf_append_buffer(to_store, "&%s\n", pair); if (!to_store) goto error; } } if (c->reply) { for (vp = fr_cursor_init(&cursor, &c->reply); vp; vp = fr_cursor_next(&cursor)) { pair = vp_aprints(pairs, vp, '\''); if (!pair) goto error; to_store = talloc_asprintf_append_buffer(to_store, "&reply:%s\n", pair); if (!to_store) goto error; } } finish: talloc_free(pairs); *out = to_store; return 0; }
/* measure the speed of talloc versus malloc */ static bool test_speed(void) { void *ctx = talloc_new(NULL); unsigned count; const int loop = 1000; int i; struct timeval tv; printf("test: speed\n# TALLOC VS MALLOC SPEED\n"); tv = private_timeval_current(); count = 0; do { void *p1, *p2, *p3; for (i=0;i<loop;i++) { p1 = talloc_size(ctx, loop % 100); p2 = talloc_strdup(p1, "foo bar"); p3 = talloc_size(p1, 300); (void)p2; (void)p3; talloc_free(p1); } count += 3 * loop; } while (private_timeval_elapsed(&tv) < 5.0); fprintf(stderr, "talloc: %.0f ops/sec\n", count/private_timeval_elapsed(&tv)); talloc_free(ctx); ctx = talloc_pool(NULL, 1024); tv = private_timeval_current(); count = 0; do { void *p1, *p2, *p3; for (i=0;i<loop;i++) { p1 = talloc_size(ctx, loop % 100); p2 = talloc_strdup(p1, "foo bar"); p3 = talloc_size(p1, 300); (void)p2; (void)p3; talloc_free(p1); } count += 3 * loop; } while (private_timeval_elapsed(&tv) < 5.0); talloc_free(ctx); fprintf(stderr, "talloc_pool: %.0f ops/sec\n", count/private_timeval_elapsed(&tv)); tv = private_timeval_current(); count = 0; do { void *p1, *p2, *p3; for (i=0;i<loop;i++) { p1 = malloc(loop % 100); p2 = strdup("foo bar"); p3 = malloc(300); free(p1); free(p2); free(p3); } count += 3 * loop; } while (private_timeval_elapsed(&tv) < 5.0); fprintf(stderr, "malloc: %.0f ops/sec\n", count/private_timeval_elapsed(&tv)); printf("success: speed\n"); return true; }
/** Create and insert a cache entry. * * @return * - #RLM_MODULE_OK on success. * - #RLM_MODULE_UPDATED if we merged the cache entry. * - #RLM_MODULE_FAIL on failure. */ static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, char const *key, int ttl) { vp_map_t const *map; vp_map_t **last, *c_map; VALUE_PAIR *vp; bool merge = false; rlm_cache_entry_t *c; TALLOC_CTX *pool; if ((inst->max_entries > 0) && inst->module->count && (inst->module->count(inst, request, handle) > inst->max_entries)) { RWDEBUG("Cache is full: %d entries", inst->max_entries); return RLM_MODULE_FAIL; } c = cache_alloc(inst, request); if (!c) return RLM_MODULE_FAIL; c->key = talloc_typed_strdup(c, key); c->created = c->expires = request->timestamp; c->expires += ttl; last = &c->maps; RDEBUG("Creating new cache entry"); /* * Alloc a pool so we don't have excessive mallocs when * gathering VALUE_PAIRs to cache. */ pool = talloc_pool(NULL, 1024); for (map = inst->maps; map != NULL; map = map->next) { VALUE_PAIR *to_cache = NULL; vp_cursor_t cursor; rad_assert(map->lhs && map->rhs); /* * Calling map_to_vp gives us exactly the same result, * as if this were an update section. */ if (map_to_vp(pool, &to_cache, request, map, NULL) < 0) { RDEBUG("Skipping %s", map->rhs->name); continue; } for (vp = fr_cursor_init(&cursor, &to_cache); vp; vp = fr_cursor_next(&cursor)) { /* * Prevent people from accidentally caching * cache control attributes. */ if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) { case PW_CACHE_TTL: case PW_CACHE_STATUS_ONLY: case PW_CACHE_READ_ONLY: case PW_CACHE_MERGE: case PW_CACHE_ENTRY_HITS: RDEBUG2("Skipping %s", vp->da->name); continue; default: break; } RINDENT(); if (RDEBUG_ENABLED2) map_debug_log(request, map, vp); REXDENT(); MEM(c_map = talloc_zero(c, vp_map_t)); c_map->op = map->op; /* * Now we turn the VALUE_PAIRs into maps. */ switch (map->lhs->type) { /* * Attributes are easy, reuse the LHS, and create a new * RHS with the value_data_t from the VALUE_PAIR. */ case TMPL_TYPE_ATTR: c_map->lhs = map->lhs; /* lhs shouldn't be touched, so this is ok */ do_rhs: MEM(c_map->rhs = tmpl_init(talloc(c_map, vp_tmpl_t), TMPL_TYPE_DATA, map->rhs->name, map->rhs->len)); if (value_data_copy(c_map->rhs, &c_map->rhs->tmpl_data_value, vp->da->type, &vp->data) < 0) { REDEBUG("Failed copying attribute value"); talloc_free(pool); talloc_free(c); return RLM_MODULE_FAIL; } c_map->rhs->tmpl_data_type = vp->da->type; break; /* * Lists are weird... We need to fudge a new LHS template, * which is a combination of the LHS list and the attribute. */ case TMPL_TYPE_LIST: { char attr[256]; MEM(c_map->lhs = tmpl_init(talloc(c_map, vp_tmpl_t), TMPL_TYPE_ATTR, map->lhs->name, map->lhs->len)); c_map->lhs->tmpl_da = vp->da; c_map->lhs->tmpl_tag = vp->tag; c_map->lhs->tmpl_list = map->lhs->tmpl_list; c_map->lhs->tmpl_num = map->lhs->tmpl_num; c_map->lhs->tmpl_request = map->lhs->tmpl_request; /* * We need to rebuild the attribute name, to be the * one we copied from the source list. */ c_map->lhs->len = tmpl_prints(attr, sizeof(attr), c_map->lhs, NULL); c_map->lhs->name = talloc_strdup(map->lhs, attr); } goto do_rhs; default: rad_assert(0); } *last = c_map; last = &(*last)->next; } talloc_free_children(pool); /* reset pool state */ } talloc_free(pool); /* * Check to see if we need to merge the entry into the request */ vp = pairfind(request->config, PW_CACHE_MERGE, 0, TAG_ANY); if (vp && (vp->vp_integer > 0)) merge = true; if (merge) cache_merge(inst, request, c); for (;;) { cache_status_t ret; ret = inst->module->insert(inst, request, handle, c); switch (ret) { case CACHE_RECONNECT: if (cache_reconnect(inst, request, handle) == 0) continue; return RLM_MODULE_FAIL; case CACHE_OK: RDEBUG("Commited entry, TTL %d seconds", ttl); cache_free(inst, &c); return merge ? RLM_MODULE_UPDATED : RLM_MODULE_OK; default: talloc_free(c); /* Failed insertion - use talloc_free not the driver free */ return RLM_MODULE_FAIL; } } }