This graph shows which files directly or indirectly include this file:
Go to the source code of this file.
Defines | |
#define | FEATURE_APP_ARGS_LEN 256 |
#define | FEATURE_APP_LEN 64 |
#define | FEATURE_EXTEN_LEN 32 |
#define | FEATURE_MAX_LEN 11 |
#define | FEATURE_SNAME_LEN 32 |
Functions | |
int | ast_bridge_call (struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config) |
Bridge a call, optionally allowing redirection. | |
int | ast_masq_park_call (struct ast_channel *rchan, struct ast_channel *host, int timeout, int *extout) |
Park a call via a masqueraded channel. | |
int | ast_park_call (struct ast_channel *chan, struct ast_channel *host, int timeout, int *extout) |
Park a call and read back parked location. | |
char * | ast_parking_ext (void) |
Determine system parking extension Returns the call parking extension for drivers that provide special call parking help. | |
int | ast_pickup_call (struct ast_channel *chan) |
Pickup a call. | |
char * | ast_pickup_ext (void) |
Determine system call pickup extension. | |
void | ast_register_feature (struct ast_call_feature *feature) |
register new feature into feature_set | |
void | ast_unregister_feature (struct ast_call_feature *feature) |
unregister feature from feature_set |
Definition in file features.h.
|
Definition at line 29 of file features.h. Referenced by load_config(). |
|
Definition at line 28 of file features.h. Referenced by load_config(). |
|
Definition at line 31 of file features.h. Referenced by load_config(). |
|
Definition at line 27 of file features.h. Referenced by ast_bridge_call(). |
|
Definition at line 30 of file features.h. Referenced by load_config(). |
|
Bridge a call, optionally allowing redirection.
Definition at line 1261 of file res_features.c. References ast_channel::appl, ast_answer(), ast_cdr_appenduserfield(), ast_cdr_setuserfield(), ast_channel_bridge(), ast_channel_setoption(), ast_clear_flag, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_FLASH, AST_CONTROL_HANGUP, AST_CONTROL_OPTION, AST_CONTROL_RINGING, ast_dtmf_stream(), ast_feature_interpret(), AST_FEATURE_PLAY_WARNING, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree(), ast_indicate(), ast_log(), AST_OPTION_FLAG_REQUEST, ast_strlen_zero(), ast_channel::cdr, ast_channel::data, ast_frame::data, ast_option_header::data, ast_frame::datalen, ast_bridge_config::end_sound, FEATURE_MAX_LEN, FEATURE_RETURN_PASSDIGITS, FEATURE_RETURN_SUCCESS, FEATURE_SENSE_CHAN, FEATURE_SENSE_PEER, ast_bridge_config::feature_timer, featuredigittimeout, ast_bridge_config::features_callee, ast_bridge_config::features_caller, ast_bridge_config::firstpass, ast_option_header::flag, ast_frame::frametype, free, LOG_DEBUG, LOG_WARNING, monitor_ok, ast_channel::name, ast_option_header::option, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), ast_bridge_config::play_warning, set_config_flags(), ast_bridge_config::start_sound, ast_bridge_config::start_time, ast_frame::subclass, ast_cdr::userfield, ast_bridge_config::warning_freq, and ast_bridge_config::warning_sound. Referenced by ast_bridge_call_thread(), builtin_atxfer(), dial_exec_full(), park_exec(), and try_calling(). 01262 { 01263 /* Copy voice back and forth between the two channels. Give the peer 01264 the ability to transfer calls with '#<extension' syntax. */ 01265 struct ast_frame *f; 01266 struct ast_channel *who; 01267 char chan_featurecode[FEATURE_MAX_LEN + 1]=""; 01268 char peer_featurecode[FEATURE_MAX_LEN + 1]=""; 01269 int res; 01270 int diff; 01271 int hasfeatures=0; 01272 int hadfeatures=0; 01273 struct ast_option_header *aoh; 01274 struct timeval start = { 0 , 0 }; 01275 struct ast_bridge_config backup_config; 01276 char *monitor_exec; 01277 01278 memset(&backup_config, 0, sizeof(backup_config)); 01279 01280 config->start_time = ast_tvnow(); 01281 01282 if (chan && peer) { 01283 pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name); 01284 pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name); 01285 } else if (chan) 01286 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL); 01287 01288 if (monitor_ok) { 01289 if (!monitor_app) { 01290 if (!(monitor_app = pbx_findapp("Monitor"))) 01291 monitor_ok=0; 01292 } 01293 if (monitor_app) { 01294 if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 01295 pbx_exec(chan, monitor_app, monitor_exec, 1); 01296 else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR"))) 01297 pbx_exec(peer, monitor_app, monitor_exec, 1); 01298 } 01299 } 01300 01301 set_config_flags(chan, peer, config); 01302 config->firstpass = 1; 01303 01304 /* Answer if need be */ 01305 if (ast_answer(chan)) 01306 return -1; 01307 peer->appl = "Bridged Call"; 01308 peer->data = chan->name; 01309 01310 /* copy the userfield from the B-leg to A-leg if applicable */ 01311 if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) { 01312 char tmp[256]; 01313 if (!ast_strlen_zero(chan->cdr->userfield)) { 01314 snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield); 01315 ast_cdr_appenduserfield(chan, tmp); 01316 } else 01317 ast_cdr_setuserfield(chan, peer->cdr->userfield); 01318 /* free the peer's cdr without ast_cdr_free complaining */ 01319 free(peer->cdr); 01320 peer->cdr = NULL; 01321 } 01322 for (;;) { 01323 if (config->feature_timer) 01324 start = ast_tvnow(); 01325 01326 res = ast_channel_bridge(chan, peer, config, &f, &who); 01327 01328 if (config->feature_timer) { 01329 /* Update time limit for next pass */ 01330 diff = ast_tvdiff_ms(ast_tvnow(), start); 01331 config->feature_timer -= diff; 01332 if (hasfeatures) { 01333 /* Running on backup config, meaning a feature might be being 01334 activated, but that's no excuse to keep things going 01335 indefinitely! */ 01336 if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) { 01337 ast_log(LOG_DEBUG, "Timed out, realtime this time!\n"); 01338 config->feature_timer = 0; 01339 who = chan; 01340 if (f) 01341 ast_frfree(f); 01342 f = NULL; 01343 res = 0; 01344 } else if (config->feature_timer <= 0) { 01345 /* Not *really* out of time, just out of time for 01346 digits to come in for features. */ 01347 ast_log(LOG_DEBUG, "Timed out for feature!\n"); 01348 if (!ast_strlen_zero(peer_featurecode)) { 01349 ast_dtmf_stream(chan, peer, peer_featurecode, 0); 01350 memset(peer_featurecode, 0, sizeof(peer_featurecode)); 01351 } 01352 if (!ast_strlen_zero(chan_featurecode)) { 01353 ast_dtmf_stream(peer, chan, chan_featurecode, 0); 01354 memset(chan_featurecode, 0, sizeof(chan_featurecode)); 01355 } 01356 if (f) 01357 ast_frfree(f); 01358 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode); 01359 if (!hasfeatures) { 01360 /* Restore original (possibly time modified) bridge config */ 01361 memcpy(config, &backup_config, sizeof(struct ast_bridge_config)); 01362 memset(&backup_config, 0, sizeof(backup_config)); 01363 } 01364 hadfeatures = hasfeatures; 01365 /* Continue as we were */ 01366 continue; 01367 } 01368 } else { 01369 if (config->feature_timer <=0) { 01370 /* We ran out of time */ 01371 config->feature_timer = 0; 01372 who = chan; 01373 if (f) 01374 ast_frfree(f); 01375 f = NULL; 01376 res = 0; 01377 } 01378 } 01379 } 01380 if (res < 0) { 01381 ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name); 01382 return -1; 01383 } 01384 01385 if (!f || ((f->frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) || 01386 (f->subclass == AST_CONTROL_CONGESTION)))) { 01387 res = -1; 01388 break; 01389 } 01390 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) { 01391 if (who == chan) 01392 ast_indicate(peer, AST_CONTROL_RINGING); 01393 else 01394 ast_indicate(chan, AST_CONTROL_RINGING); 01395 } 01396 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == -1)) { 01397 if (who == chan) 01398 ast_indicate(peer, -1); 01399 else 01400 ast_indicate(chan, -1); 01401 } 01402 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_FLASH)) { 01403 if (who == chan) 01404 ast_indicate(peer, AST_CONTROL_FLASH); 01405 else 01406 ast_indicate(chan, AST_CONTROL_FLASH); 01407 } 01408 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_OPTION)) { 01409 aoh = f->data; 01410 /* Forward option Requests */ 01411 if (aoh && (aoh->flag == AST_OPTION_FLAG_REQUEST)) { 01412 if (who == chan) 01413 ast_channel_setoption(peer, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0); 01414 else 01415 ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0); 01416 } 01417 } 01418 /* check for '*', if we find it it's time to disconnect */ 01419 if (f && (f->frametype == AST_FRAME_DTMF)) { 01420 char *featurecode; 01421 int sense; 01422 struct ast_channel *other; 01423 01424 hadfeatures = hasfeatures; 01425 /* This cannot overrun because the longest feature is one shorter than our buffer */ 01426 if (who == chan) { 01427 other = peer; 01428 sense = FEATURE_SENSE_CHAN; 01429 featurecode = chan_featurecode; 01430 } else { 01431 other = chan; 01432 sense = FEATURE_SENSE_PEER; 01433 featurecode = peer_featurecode; 01434 } 01435 featurecode[strlen(featurecode)] = f->subclass; 01436 /* Get rid of the frame before we start doing "stuff" with the channels */ 01437 ast_frfree(f); 01438 f = NULL; 01439 config->feature_timer = backup_config.feature_timer; 01440 res = ast_feature_interpret(chan, peer, config, featurecode, sense); 01441 switch(res) { 01442 case FEATURE_RETURN_PASSDIGITS: 01443 ast_dtmf_stream(other, who, featurecode, 0); 01444 /* Fall through */ 01445 case FEATURE_RETURN_SUCCESS: 01446 memset(featurecode, 0, sizeof(chan_featurecode)); 01447 break; 01448 } 01449 if (res >= FEATURE_RETURN_PASSDIGITS) { 01450 res = 0; 01451 } else 01452 break; 01453 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode); 01454 if (hadfeatures && !hasfeatures) { 01455 /* Restore backup */ 01456 memcpy(config, &backup_config, sizeof(struct ast_bridge_config)); 01457 memset(&backup_config, 0, sizeof(struct ast_bridge_config)); 01458 } else if (hasfeatures) { 01459 if (!hadfeatures) { 01460 /* Backup configuration */ 01461 memcpy(&backup_config, config, sizeof(struct ast_bridge_config)); 01462 /* Setup temporary config options */ 01463 config->play_warning = 0; 01464 ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING); 01465 ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING); 01466 config->warning_freq = 0; 01467 config->warning_sound = NULL; 01468 config->end_sound = NULL; 01469 config->start_sound = NULL; 01470 config->firstpass = 0; 01471 } 01472 config->feature_timer = featuredigittimeout; 01473 ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->feature_timer); 01474 } 01475 } 01476 if (f) 01477 ast_frfree(f); 01478 } 01479 return res; 01480 }
|
|
Park a call via a masqueraded channel.
Definition at line 401 of file res_features.c. References ast_channel_alloc(), ast_channel_masquerade(), ast_frfree(), ast_log(), ast_park_call(), ast_read(), ast_channel::context, ast_channel::exten, LOG_WARNING, ast_channel::name, ast_channel::priority, ast_channel::readformat, and ast_channel::writeformat. Referenced by mgcp_ss(), parkandannounce_exec(), rpt_exec(), skinny_ss(), and ss_thread(). 00402 { 00403 struct ast_channel *chan; 00404 struct ast_frame *f; 00405 00406 /* Make a new, fake channel that we'll use to masquerade in the real one */ 00407 chan = ast_channel_alloc(0); 00408 if (chan) { 00409 /* Let us keep track of the channel name */ 00410 snprintf(chan->name, sizeof (chan->name), "Parked/%s",rchan->name); 00411 00412 /* Make formats okay */ 00413 chan->readformat = rchan->readformat; 00414 chan->writeformat = rchan->writeformat; 00415 ast_channel_masquerade(chan, rchan); 00416 00417 /* Setup the extensions and such */ 00418 ast_copy_string(chan->context, rchan->context, sizeof(chan->context)); 00419 ast_copy_string(chan->exten, rchan->exten, sizeof(chan->exten)); 00420 chan->priority = rchan->priority; 00421 00422 /* Make the masq execute */ 00423 f = ast_read(chan); 00424 if (f) 00425 ast_frfree(f); 00426 ast_park_call(chan, peer, timeout, extout); 00427 } else { 00428 ast_log(LOG_WARNING, "Unable to create parked channel\n"); 00429 return -1; 00430 } 00431 return 0; 00432 }
|
|
Park a call and read back parked location.
Definition at line 278 of file res_features.c. References adsi_announce_park(), adsi_available(), adsi_unload_session(), adsipark, ast_channel::appl, ast_add_extension2(), ast_context_create(), ast_context_find(), AST_CONTROL_HOLD, ast_indicate(), ast_log(), AST_MAX_EXTENSION, ast_moh_start(), ast_mutex_lock(), ast_mutex_unlock(), ast_say_digits(), ast_strlen_zero(), ast_verbose(), parkeduser::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, parkeduser::context, ast_channel::context, ast_channel::data, EVENT_FLAG_CALL, exten, parkeduser::exten, ast_channel::exten, free, FREE, LOG_ERROR, LOG_WARNING, ast_channel::macrocontext, ast_channel::macroexten, ast_channel::macropriority, malloc, manager_event(), ast_channel::name, parkeduser::next, parkeduser::notquiteyet, option_verbose, parkedcall, parkfindnext, parking_con, parking_offset, parking_start, parking_stop, parking_thread, parkeduser::parkingnum, parkeduser::parkingtime, parkingtime, parkeduser::peername, parkeduser::priority, ast_channel::priority, registrar, parkeduser::start, strdup, and VERBOSE_PREFIX_2. Referenced by ast_masq_park_call(), builtin_blindtransfer(), iax_park_thread(), park_call_exec(), and sip_park_thread(). 00279 { 00280 struct parkeduser *pu, *cur; 00281 int i,x,parking_range; 00282 char exten[AST_MAX_EXTENSION]; 00283 struct ast_context *con; 00284 00285 pu = malloc(sizeof(struct parkeduser)); 00286 if (!pu) { 00287 ast_log(LOG_WARNING, "Out of memory\n"); 00288 return -1; 00289 } 00290 memset(pu, 0, sizeof(struct parkeduser)); 00291 ast_mutex_lock(&parking_lock); 00292 parking_range = parking_stop - parking_start+1; 00293 for (i = 0; i < parking_range; i++) { 00294 x = (i + parking_offset) % parking_range + parking_start; 00295 cur = parkinglot; 00296 while(cur) { 00297 if (cur->parkingnum == x) 00298 break; 00299 cur = cur->next; 00300 } 00301 if (!cur) 00302 break; 00303 } 00304 00305 if (!(i < parking_range)) { 00306 ast_log(LOG_WARNING, "No more parking spaces\n"); 00307 free(pu); 00308 ast_mutex_unlock(&parking_lock); 00309 return -1; 00310 } 00311 if (parkfindnext) 00312 parking_offset = x - parking_start + 1; 00313 chan->appl = "Parked Call"; 00314 chan->data = NULL; 00315 00316 pu->chan = chan; 00317 /* Start music on hold */ 00318 if (chan != peer) { 00319 ast_indicate(pu->chan, AST_CONTROL_HOLD); 00320 ast_moh_start(pu->chan, NULL); 00321 } 00322 pu->start = ast_tvnow(); 00323 pu->parkingnum = x; 00324 if (timeout > 0) 00325 pu->parkingtime = timeout; 00326 else 00327 pu->parkingtime = parkingtime; 00328 if (extout) 00329 *extout = x; 00330 if (peer) 00331 ast_copy_string(pu->peername, peer->name, sizeof(pu->peername)); 00332 00333 /* Remember what had been dialed, so that if the parking 00334 expires, we try to come back to the same place */ 00335 if (!ast_strlen_zero(chan->macrocontext)) 00336 ast_copy_string(pu->context, chan->macrocontext, sizeof(pu->context)); 00337 else 00338 ast_copy_string(pu->context, chan->context, sizeof(pu->context)); 00339 if (!ast_strlen_zero(chan->macroexten)) 00340 ast_copy_string(pu->exten, chan->macroexten, sizeof(pu->exten)); 00341 else 00342 ast_copy_string(pu->exten, chan->exten, sizeof(pu->exten)); 00343 if (chan->macropriority) 00344 pu->priority = chan->macropriority; 00345 else 00346 pu->priority = chan->priority; 00347 pu->next = parkinglot; 00348 parkinglot = pu; 00349 /* If parking a channel directly, don't quiet yet get parking running on it */ 00350 if (peer == chan) 00351 pu->notquiteyet = 1; 00352 ast_mutex_unlock(&parking_lock); 00353 /* Wake up the (presumably select()ing) thread */ 00354 pthread_kill(parking_thread, SIGURG); 00355 if (option_verbose > 1) 00356 ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000)); 00357 00358 manager_event(EVENT_FLAG_CALL, "ParkedCall", 00359 "Exten: %d\r\n" 00360 "Channel: %s\r\n" 00361 "From: %s\r\n" 00362 "Timeout: %ld\r\n" 00363 "CallerID: %s\r\n" 00364 "CallerIDName: %s\r\n" 00365 ,pu->parkingnum, pu->chan->name, peer ? peer->name : "" 00366 ,(long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL) 00367 ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>") 00368 ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>") 00369 ); 00370 00371 if (peer) { 00372 if (adsipark && adsi_available(peer)) { 00373 adsi_announce_park(peer, pu->parkingnum); 00374 } 00375 if (adsipark && adsi_available(peer)) { 00376 adsi_unload_session(peer); 00377 } 00378 } 00379 con = ast_context_find(parking_con); 00380 if (!con) { 00381 con = ast_context_create(NULL, parking_con, registrar); 00382 if (!con) { 00383 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); 00384 } 00385 } 00386 if (con) { 00387 snprintf(exten, sizeof(exten), "%d", x); 00388 ast_add_extension2(con, 1, exten, 1, NULL, NULL, parkedcall, strdup(exten), FREE, registrar); 00389 } 00390 if (peer) 00391 ast_say_digits(peer, pu->parkingnum, "", peer->language); 00392 if (pu->notquiteyet) { 00393 /* Wake up parking thread if we're really done */ 00394 ast_moh_start(pu->chan, NULL); 00395 pu->notquiteyet = 0; 00396 pthread_kill(parking_thread, SIGURG); 00397 } 00398 return 0; 00399 }
|
|
Determine system parking extension Returns the call parking extension for drivers that provide special call parking help.
Definition at line 163 of file res_features.c. References parking_ext. Referenced by builtin_blindtransfer(), dp_lookup(), get_refer_info(), handle_request_refer(), load_config(), mgcp_ss(), skinny_ss(), socket_read(), and ss_thread(). 00164 { 00165 return parking_ext; 00166 }
|
|
Pickup a call.
Definition at line 1926 of file res_features.c. References ast_channel::_state, ast_answer(), ast_channel_masquerade(), ast_channel_walk_locked(), AST_CONTROL_ANSWER, ast_log(), ast_mutex_unlock(), ast_queue_control(), AST_STATE_RING, AST_STATE_RINGING, ast_channel::callgroup, ast_channel::lock, LOG_DEBUG, LOG_WARNING, ast_channel::name, option_debug, ast_channel::pbx, and ast_channel::pickupgroup. Referenced by cb_events(), handle_request_invite(), mgcp_ss(), monitor_handle_notowned(), skinny_ss(), and ss_thread(). 01927 { 01928 struct ast_channel *cur = NULL; 01929 int res = -1; 01930 01931 while ( (cur = ast_channel_walk_locked(cur)) != NULL) { 01932 if (!cur->pbx && 01933 (cur != chan) && 01934 (chan->pickupgroup & cur->callgroup) && 01935 ((cur->_state == AST_STATE_RINGING) || 01936 (cur->_state == AST_STATE_RING))) { 01937 break; 01938 } 01939 ast_mutex_unlock(&cur->lock); 01940 } 01941 if (cur) { 01942 if (option_debug) 01943 ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name); 01944 res = ast_answer(chan); 01945 if (res) 01946 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name); 01947 res = ast_queue_control(chan, AST_CONTROL_ANSWER); 01948 if (res) 01949 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name); 01950 res = ast_channel_masquerade(cur, chan); 01951 if (res) 01952 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */ 01953 ast_mutex_unlock(&cur->lock); 01954 } else { 01955 if (option_debug) 01956 ast_log(LOG_DEBUG, "No call pickup possible...\n"); 01957 } 01958 return res; 01959 }
|
|
Determine system call pickup extension.
Definition at line 168 of file res_features.c. References pickup_ext. Referenced by cb_events(), get_destination(), handle_request_invite(), handle_showfeatures(), mgcp_ss(), monitor_handle_notowned(), skinny_ss(), and ss_thread(). 00169 { 00170 return pickup_ext; 00171 }
|
|
register new feature into feature_set
Definition at line 872 of file res_features.c. References AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_verbose(), LOG_NOTICE, option_verbose, ast_call_feature::sname, and VERBOSE_PREFIX_2. Referenced by load_config(). 00873 { 00874 if (!feature) { 00875 ast_log(LOG_NOTICE,"You didn't pass a feature!\n"); 00876 return; 00877 } 00878 00879 AST_LIST_LOCK(&feature_list); 00880 AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry); 00881 AST_LIST_UNLOCK(&feature_list); 00882 00883 if (option_verbose >= 2) 00884 ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname); 00885 }
|
|
unregister feature from feature_set
Definition at line 888 of file res_features.c. References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, and free. 00889 { 00890 if (!feature) return; 00891 00892 AST_LIST_LOCK(&feature_list); 00893 AST_LIST_REMOVE(&feature_list,feature,feature_entry); 00894 AST_LIST_UNLOCK(&feature_list); 00895 free(feature); 00896 }
|