From 83bda237d9dcc3788b4a43d577898d810da75ab1 Mon Sep 17 00:00:00 2001 From: Naveen Albert Date: Thu, 11 Sep 2025 15:57:03 -0400 Subject: [PATCH] chan_dahdi: Add DAHDI_CHANNEL function. Add a dialplan function that can be used to get/set properties of DAHDI channels (as opposed to Asterisk channels). This exposes properties that were not previously available, allowing for certain operations to now be performed in the dialplan. Resolves: #1455 UserNote: The DAHDI_CHANNEL function allows for getting/setting certain properties about DAHDI channels from the dialplan. --- channels/chan_dahdi.c | 337 ++++++++++++++++++++++++++++++++---------- channels/sig_analog.c | 6 +- 2 files changed, 262 insertions(+), 81 deletions(-) diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index a468650e36..6ab7b22441 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -220,6 +220,61 @@ + + + Set or get a property of a DAHDI channel. + + + + The property to set or get. + + + R/O The name of the active channel on this DAHDI device. + + + R/O The name of the call waiting channel on this DAHDI device. + + + R/O The name of the three-way channel on this DAHDI device. + + + R/W The polarity of the channel (0 or 1, idle or reverse can also be used for setting). + Only valid on FXO-signalled channels. + + + R/W Whether Do Not Disturb is active. + Only valid on FXO-signalled channels. + + + R/W The call forwarding target number. + Only valid on FXO-signalled channels. + + + R/W The last number dialed. + Only valid on FXO-signalled channels. + + + + + The DAHDI channel number. + If not specified, the DAHDI channel number of the current channel + will be used (in which case it must be a DAHDI channel). + + + + The DAHDI_CHANNEL function can be used to set or get properties of DAHDI channels + that are not specific to Asterisk channels. + This function may also be called from non-DAHDI channels. + + same => n,Set(DAHDI_CHANNEL(polarity)=1) + same => n,NoOp(${DAHDI_CHANNEL(polarity)}) + + + same => n,Set(DAHDI_CHANNEL(callforwardnumber,1)=5552368) + same => n,NoOp(Calls now forwarding to ${DAHDI_CHANNEL(callforwardnumber,1)}) + + + @@ -2913,86 +2968,6 @@ static void my_hangup_polarityswitch(void *pvt) } } -/*! \brief Return DAHDI pivot if channel is FXO signalled */ -static struct dahdi_pvt *fxo_pvt(struct ast_channel *chan) -{ - int res; - struct dahdi_params dahdip; - struct dahdi_pvt *pvt = NULL; - - if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) { - ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", ast_channel_name(chan)); - return NULL; - } - - memset(&dahdip, 0, sizeof(dahdip)); - res = ioctl(ast_channel_fd(chan, 0), DAHDI_GET_PARAMS, &dahdip); - - if (res) { - ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", ast_channel_name(chan), strerror(errno)); - return NULL; - } - if (!(dahdip.sigtype & __DAHDI_SIG_FXO)) { - ast_log(LOG_WARNING, "%s is not FXO signalled\n", ast_channel_name(chan)); - return NULL; - } - - pvt = ast_channel_tech_pvt(chan); - if (!dahdi_analog_lib_handles(pvt->sig, 0, 0)) { - ast_log(LOG_WARNING, "Channel signalling is not analog"); - return NULL; - } - - return pvt; -} - -static int polarity_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen) -{ - struct dahdi_pvt *pvt; - - pvt = fxo_pvt(chan); - if (!pvt) { - return -1; - } - - snprintf(buffer, buflen, "%d", pvt->polarity); - - return 0; -} - -static int polarity_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) -{ - struct dahdi_pvt *pvt; - int polarity; - - pvt = fxo_pvt(chan); - if (!pvt) { - return -1; - } - - if (!strcasecmp(value, "idle")) { - polarity = POLARITY_IDLE; - } else if (!strcasecmp(value, "reverse")) { - polarity = POLARITY_REV; - } else { - polarity = atoi(value); - } - - if (polarity != POLARITY_IDLE && polarity != POLARITY_REV) { - ast_log(LOG_WARNING, "Invalid polarity: '%s'\n", value); - return -1; - } - - my_set_polarity(pvt, polarity); - return 0; -} - -static struct ast_custom_function polarity_function = { - .name = "POLARITY", - .write = polarity_write, - .read = polarity_read, -}; - static int my_start(void *pvt) { struct dahdi_pvt *p = pvt; @@ -16905,6 +16880,206 @@ static struct dahdi_pvt *find_channel_from_str(const char *channel) return find_channel(chan_num); } +static int print_subchannel(struct dahdi_pvt *p, int subchan, char *buffer, size_t len) +{ + if (!p->subs[subchan].owner) { + return -1; + } + ast_channel_lock(p->subs[subchan].owner); + snprintf(buffer, len, "%s", ast_channel_name(p->subs[subchan].owner)); + ast_channel_unlock(p->subs[subchan].owner); + return 0; +} + +#define REQUIRE_FXO_SIG() \ + if (!(dahdip->sigtype & __DAHDI_SIG_FXO)) { \ + ast_log(LOG_WARNING, "DAHDI channel %d is not FXO signalled\n", p->channel); \ + return -1; \ + } + +static int dahdichan_read_property(struct dahdi_pvt *p, struct dahdi_params *dahdip, const char *property, char *buffer, size_t len) +{ + struct analog_pvt *analog_p = p->sig_pvt; + + /* R/O properties */ + if (!strcasecmp(property, "owner")) { + return print_subchannel(p, SUB_REAL, buffer, len); + } else if (!strcasecmp(property, "callwait")) { + return print_subchannel(p, SUB_CALLWAIT, buffer, len); + } else if (!strcasecmp(property, "threeway")) { + return print_subchannel(p, SUB_THREEWAY, buffer, len); + /* R/W properties */ + } else if (!strcasecmp(property, "polarity")) { + REQUIRE_FXO_SIG(); + snprintf(buffer, len, "%d", p->polarity); + } else if (!strcasecmp(property, "dnd")) { + REQUIRE_FXO_SIG(); + snprintf(buffer, len, "%d", analog_p->dnd); + } else if (!strcasecmp(property, "callforward")) { + REQUIRE_FXO_SIG(); + snprintf(buffer, len, "%s", analog_p->call_forward); + } else if (!strcasecmp(property, "lastexten")) { + REQUIRE_FXO_SIG(); + snprintf(buffer, len, "%s", analog_p->lastexten); + } else { + ast_log(LOG_ERROR, "Unknown DAHDI_CHANNEL property '%s'\n", property); + return -1; + } + return 0; +} + +static int dahdichan_write_property(struct dahdi_pvt *p, struct dahdi_params *dahdip, const char *property, const char *value) +{ + struct analog_pvt *analog_p = p->sig_pvt; + + /* We don't need to check ast_strlen_zero(value) because it's obviously not NULL. + * It may even be okay for it to be an empty string, but that's a per-setting thing. */ + + /* R/O properties */ + if (!strcasecmp(property, "owner") || !strcasecmp(property, "callwait") || !strcasecmp(property, "threeway")) { + ast_log(LOG_ERROR, "DAHDI subchannel names are R/O\n"); + return -1; + /* R/W properties */ + } else if (!strcasecmp(property, "polarity")) { + int polarity = atoi(value); + REQUIRE_FXO_SIG(); + if (polarity != POLARITY_IDLE && polarity != POLARITY_REV) { + ast_log(LOG_ERROR, "Invalid polarity: '%s'\n", value); + return -1; + } + my_set_polarity(p, polarity); + } else if (!strcasecmp(property, "dnd")) { + int dnd = atoi(value); + REQUIRE_FXO_SIG(); + analog_dnd(analog_p, dnd ? 1 : 0); + } else if (!strcasecmp(property, "callforward")) { + REQUIRE_FXO_SIG(); + if (strlen(value) >= sizeof(analog_p->call_forward) - 1) { + ast_log(LOG_ERROR, "Provided call forwarding target '%s' is too long\n", value); + } + ast_copy_string(analog_p->call_forward, value, sizeof(analog_p->call_forward)); /* Could be empty to clear value */ + } else if (!strcasecmp(property, "lastexten")) { + REQUIRE_FXO_SIG(); + if (strlen(value) >= sizeof(analog_p->lastexten) - 1) { + ast_log(LOG_ERROR, "Provided lastexten target '%s' is too long\n", value); + } + ast_copy_string(analog_p->lastexten, value, sizeof(analog_p->lastexten)); /* Could be empty to clear value */ + } else { + ast_log(LOG_ERROR, "Unknown DAHDI_CHANNEL property '%s'\n", property); + return -1; + } + return 0; +} +#undef REQUIRE_FXO_SIG + +static int dahdichan_helper(struct ast_channel *chan, char *data, const char *value, char *buffer, size_t buflen) +{ + char *parse; + struct dahdi_pvt *pvt; + struct dahdi_params dahdip; + int res; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(property); + AST_APP_ARG(dahdichan); + ); + + parse = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parse); + + if (buflen > 0) { + *buffer = '\0'; + } + + if (!ast_strlen_zero(args.dahdichan)) { + /* DAHDI channel number explicitly provided, find it. */ + int channo = atoi(args.dahdichan); + pvt = find_channel(channo); + if (!pvt) { + ast_log(LOG_ERROR, "DAHDI channel %d does not exist\n", channo); + return -1; + } + } else { + /* No channel specified explicitly, so implicitly use the current channel, in which case it must be a DAHDI channel. */ + if (!chan || !ast_channel_tech(chan) || strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) { + ast_log(LOG_WARNING, "%s is not a DAHDI channel, and no DAHDI channel specified\n", ast_channel_name(chan)); + return -1; + } + pvt = ast_channel_tech_pvt(chan); + } + + memset(&dahdip, 0, sizeof(dahdip)); + if (ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &dahdip)) { + ast_log(LOG_WARNING, "Unable to get parameters of DAHDI channel %d: %s\n", pvt->channel, strerror(errno)); + return -1; + } + + /* We have the channel private to use. */ + ast_mutex_lock(&pvt->lock); + if (value) { + res = dahdichan_write_property(pvt, &dahdip, args.property, value); + } else { + res = dahdichan_read_property(pvt, &dahdip, args.property, buffer, buflen); + } + ast_mutex_unlock(&pvt->lock); + return res; +} + +static int dahdichan_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen) +{ + return dahdichan_helper(chan, data, NULL, buffer, buflen); +} + +static int dahdichan_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) +{ + return dahdichan_helper(chan, data, value, NULL, 0); +} + +static struct ast_custom_function dahdichan_function = { + .name = "DAHDI_CHANNEL", + .write = dahdichan_write, + .read = dahdichan_read, +}; + +/*! \todo The standalone POLARITY function can and should be deprecated/removed, since its functionality is now part of DAHDI_CHANNEL. */ + +static int polarity_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen) +{ + struct dahdi_params dahdip; + struct dahdi_pvt *pvt = ast_channel_tech_pvt(chan); + if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) { + ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", ast_channel_name(chan)); + return -1; + } + memset(&dahdip, 0, sizeof(dahdip)); + if (ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &dahdip)) { + ast_log(LOG_WARNING, "Unable to get parameters of DAHDI channel %d: %s\n", pvt->subs[SUB_REAL].dfd, strerror(errno)); + return -1; + } + return dahdichan_read_property(ast_channel_tech_pvt(chan), &dahdip, "polarity", buffer, buflen); +} + +static int polarity_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) +{ + struct dahdi_params dahdip; + struct dahdi_pvt *pvt = ast_channel_tech_pvt(chan); + if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) { + ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", ast_channel_name(chan)); + return -1; + } + memset(&dahdip, 0, sizeof(dahdip)); + if (ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &dahdip)) { + ast_log(LOG_WARNING, "Unable to get parameters of DAHDI channel %d: %s\n", pvt->subs[SUB_REAL].dfd, strerror(errno)); + return -1; + } + return dahdichan_write_property(ast_channel_tech_pvt(chan), &dahdip, "polarity", value); +} + +static struct ast_custom_function polarity_function = { + .name = "POLARITY", + .write = polarity_write, + .read = polarity_read, +}; + static int action_dahdidndon(struct mansession *s, const struct message *m) { struct dahdi_pvt *p; @@ -18258,6 +18433,7 @@ static int __unload_module(void) ast_unregister_application(dahdi_accept_r2_call_app); #endif + ast_custom_function_unregister(&dahdichan_function); ast_custom_function_unregister(&polarity_function); ast_cli_unregister_multiple(dahdi_cli, ARRAY_LEN(dahdi_cli)); @@ -20483,6 +20659,7 @@ static int load_module(void) ast_register_application_xml(dahdi_accept_r2_call_app, dahdi_accept_r2_call_exec); #endif + ast_custom_function_register(&dahdichan_function); ast_custom_function_register(&polarity_function); ast_cli_register_multiple(dahdi_cli, ARRAY_LEN(dahdi_cli)); diff --git a/channels/sig_analog.c b/channels/sig_analog.c index 7eb222dba4..580115c3db 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -2452,7 +2452,9 @@ static void *__analog_ss_thread(void *data) /* Last Number Redial */ if (!ast_strlen_zero(p->lastexten)) { ast_verb(4, "Redialing last number dialed on channel %d\n", p->channel); + analog_lock_private(p); ast_copy_string(exten, p->lastexten, sizeof(exten)); + analog_unlock_private(p); } else { ast_verb(3, "Last Number Redial not possible on channel %d (no saved number)\n", p->channel); res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION); @@ -2465,8 +2467,10 @@ static void *__analog_ss_thread(void *data) if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) { if (getforward) { /* Record this as the forwarding extension */ + analog_lock_private(p); ast_copy_string(p->call_forward, exten, sizeof(p->call_forward)); - ast_verb(3, "Setting call forward to '%s' on channel %d\n", p->call_forward, p->channel); + analog_unlock_private(p); + ast_verb(3, "Setting call forward to '%s' on channel %d\n", exten, p->channel); res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL); if (res) { break;