From 67d66e73d233e156aa7dddab2d56c0bb2211c02e Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 25 Aug 2021 15:44:03 +0200 Subject: [PATCH] Structured the commands and environment variables --- src/router/call.c | 787 +++++++++++++++++++++++++------------------ src/router/call.h | 2 + src/router/main.c | 19 +- src/router/routing.c | 325 ++++++++++++------ src/router/routing.h | 4 +- 5 files changed, 712 insertions(+), 425 deletions(-) diff --git a/src/router/call.c b/src/router/call.c index 945c2ac..e74ae60 100644 --- a/src/router/call.c +++ b/src/router/call.c @@ -487,6 +487,7 @@ static void orig_info(call_t *call, osmo_cc_msg_t *old_msg) uint8_t type, plan; char number[256]; char keypad[256]; + int complete = 0; uint8_t duration_ms, pause_ms, dtmf_mode; char dtmf[256]; char command[512]; @@ -515,6 +516,11 @@ static void orig_info(call_t *call, osmo_cc_msg_t *old_msg) strcat(call->dialing_keypad, keypad); } + /* indicate complete number to environment */ + rc = osmo_cc_get_ie_complete(old_msg, 0); + if (rc) + complete = 1; + /* dtmf digits */ rc = osmo_cc_get_ie_dtmf(old_msg, 0, &duration_ms, &pause_ms, &dtmf_mode, dtmf, sizeof(dtmf)); if (rc < 0 || (dtmf_mode != OSMO_CC_DTMF_MODE_ON && dtmf_mode != OSMO_CC_DTMF_MODE_DIGITS)) @@ -535,7 +541,7 @@ static void orig_info(call_t *call, osmo_cc_msg_t *old_msg) if (call->state == CALL_STATE_OVERLAP && !call->relation_list->next) { if (!call->routing.routing) { /* restart routing with new dial string */ - routing_env_dialing(&call->routing, call->dialing_number, call->dialing_keypad); + routing_env_dialing(&call->routing, call->dialing_number, call->dialing_keypad, complete); routing_start(&call->routing, routing_script, routing_shell); } else { /* send digits to routing */ @@ -1130,26 +1136,57 @@ void cc_message(osmo_cc_endpoint_t *cc_ep, uint32_t callref, osmo_cc_msg_t *msg) * process message from routing */ -static const char *value_of_param(char *arg, char *param, char **value_p) -{ - if (!!strncmp(arg, param, strlen(param))) - return NULL; - arg += strlen(param); +static struct param { + /* rtp-proxy */ + struct osmo_cc_helper_audio_codecs orig_codecs[MAX_CODECS + 1]; + struct osmo_cc_helper_audio_codecs term_codecs[MAX_CODECS + 1]; + int orig_given, term_given; - if (*arg == '\0') { - if (value_p) - *value_p = ""; - return ""; - } + /* play */ + char *filename, *volume, *loop; - if (*arg != '=') - return NULL; - arg++; + /* gain */ + char *gain; - if (value_p) - *value_p = arg; - return arg; -} + /* call */ + char *interface; + int bearer_coding, bearer_capability, bearer_mode; + char *calling; + int calling_type, calling_plan, calling_present, calling_screen, no_calling; + char *calling2; + int calling2_type, calling2_plan, calling2_present, calling2_screen, no_calling2; + char *redirecting; + int redirecting_type, redirecting_plan, redirecting_present, redirecting_screen, redirecting_reason, no_redirecting; + char *dialing; + int dialing_type, dialing_plan; + char *keypad; + + /* disc/rel */ + int isdn_cause, sip_cause; + + /* error */ + char *error; +} param; + +struct param_def { + const char *n; + char **s; + int *i; + struct osmo_cc_helper_audio_codecs *o, *t; + const char *d; + int mandatory; + int no_value; + const char *(*value2name)(int value); + int (*name2value)(const char *name); + int num; +}; + +struct command_def { + const char *name; + void (*func)(call_t *call, int argc, char *argv[], struct command_def *command_def); + const char *description; + struct param_def *param_list; +} command_def[]; static void add_codecs_by_names(struct osmo_cc_helper_audio_codecs *codecs, char *names) { @@ -1176,13 +1213,88 @@ static void add_codecs_by_names(struct osmo_cc_helper_audio_codecs *codecs, char memset(&codecs[c], 0, sizeof(*codecs)); } +static int parse_params(int argc, char *argv[], struct command_def *command_def) +{ + struct param_def *param_def = command_def->param_list; + int i, j; + char *arg; + + /* clear parameter set */ + memset(¶m, 0, sizeof(param)); + + /* no parameter list given */ + if (param_def == NULL) + return -EINVAL; + + /* loop through all arguments and stop if there is a ':' */ + for (i = 0; i < argc && argv[i][0] != ':'; i++) { + arg = argv[i]; + /* loop through all possible parameter definitions */ + for (j = 0; param_def[j].n; j++) { + /* stop if parameter has been found */ + if (!strncasecmp(arg, param_def[j].n, strlen(param_def[j].n))) { + arg += strlen(param_def[j].n); + /* no value */ + if (*arg == '\0') { + arg = ""; + break; + } + /* has value */ + if (*arg == '=') { + arg++; + break; + } + /* continue, if more digits given */ + } + } + if (!param_def[j].n) { + PDEBUG(DROUTER, DEBUG_ERROR, "Unknown '%s' parameter '%s' from routing.\n", command_def->name, argv[i]); + continue; + } + + if (arg[0] && param_def[j].no_value) { + PDEBUG(DROUTER, DEBUG_ERROR, "'%s' parameter '%s' has no value, ignoring.\n", command_def->name, argv[i]); + continue; + } + + if (param_def[j].no_value) + arg = "1"; + + if (param_def[j].i) { + if (param_def[j].name2value) { + *param_def[j].i = param_def[j].name2value(arg); + if (*param_def[j].i < 0) + *param_def[j].i = atoi(arg); + } else + *param_def[j].i = atoi(arg); + } + if (param_def[j].s) + *param_def[j].s = arg; + if (param_def[j].o) { + PDEBUG(DROUTER, DEBUG_INFO, "Originating codecs given: '%s'.\n", arg); + add_codecs_by_names(param_def[j].o, arg); + param.orig_given = 1; + } + if (param_def[j].t) { + PDEBUG(DROUTER, DEBUG_INFO, "Terminating codecs given: '%s'.\n", arg); + add_codecs_by_names(param_def[j].t, arg); + param.term_given = 1; + } + } + + return i; +} + /* routing orders us to activate rtp proxy */ -static void routing_rtp_proxy(call_t *call, int argc, char *argv[]) +struct param_def param_rtp_proxy[] = { + { .n = "orig-codecs", .o = param.orig_codecs, .d = "Define allowed codecs to be accepted by originating endpoint." }, + { .n = "term-codecs", .t = param.term_codecs, .d = "Define allowed codecs on terminating endpoint in order of preference." }, + { .n = NULL } +}; + +static void routing_rtp_proxy(call_t *call, int argc, char *argv[], struct command_def *command_def) { call_relation_t *relation = call->relation_list; - int orig_given = 0, term_given = 0; - int i; - char *value; /* ignore, if already enabled */ if (relation->rtp_proxy) { @@ -1190,36 +1302,24 @@ static void routing_rtp_proxy(call_t *call, int argc, char *argv[]) return; } - /* loop through all arguments and stop if there is a ':' */ - for (i = 0; i < argc ; i++) { - if (value_of_param(argv[i], "orig-codecs", &value)) { - PDEBUG(DROUTER, DEBUG_INFO, "Originating codecs given: '%s'.\n", value); - add_codecs_by_names(relation->orig_codecs, value); - if (relation->orig_codecs[0].payload_name) - orig_given = 1; - } else if (value_of_param(argv[i], "term-codecs", &value)) { - PDEBUG(DROUTER, DEBUG_INFO, "Terminating codecs given: '%s'.\n", value); - add_codecs_by_names(relation->term_codecs, value); - if (relation->term_codecs[0].payload_name) - term_given = 1; - } else - PDEBUG(DROUTER, DEBUG_ERROR, "Unknown 'rtp-proxy' parameter '%s' from routing.\n", argv[i]); - } + parse_params(argc, argv, command_def); - if (!orig_given) { + if (!param.orig_given) { memcpy(&relation->orig_codecs[0], &codecs_def[0], sizeof(relation->orig_codecs[0])); memcpy(&relation->orig_codecs[1], &codecs_def[1], sizeof(relation->orig_codecs[1])); memcpy(&relation->orig_codecs[2], &codecs_def[2], sizeof(relation->orig_codecs[2])); memset(&relation->orig_codecs[3], 0, sizeof(relation->orig_codecs[3])); PDEBUG(DROUTER, DEBUG_INFO, "No originating codeds given, using default '%s' and '%s' and '%s'.\n", relation->orig_codecs[0].payload_name, relation->orig_codecs[1].payload_name, relation->orig_codecs[2].payload_name); - } + } else + memcpy(relation->orig_codecs, param.orig_codecs, sizeof(param.orig_codecs)); - if (!term_given) { + if (!param.term_given) { memcpy(&relation->term_codecs[0], &codecs_def[0], sizeof(relation->term_codecs[0])); memcpy(&relation->term_codecs[1], &codecs_def[1], sizeof(relation->term_codecs[1])); memset(&relation->term_codecs[2], 0, sizeof(relation->term_codecs[2])); PDEBUG(DROUTER, DEBUG_INFO, "No terminating codeds given, using default '%s' and '%s'.\n", relation->term_codecs[0].payload_name, relation->term_codecs[1].payload_name); - } + } else + memcpy(relation->term_codecs, param.term_codecs, sizeof(param.term_codecs)); /* error, if we already negotiated */ if (call->sdp_forwarded) { @@ -1237,11 +1337,16 @@ static void routing_rtp_proxy(call_t *call, int argc, char *argv[]) } /* routing orders us to play a wave file */ -static void routing_play(call_t *call, int argc, char *argv[]) +struct param_def param_play[] = { + { .n = "filename", .s = ¶m.filename, .d = "file name of audio file", .mandatory = 1 }, + { .n = "volume", .s = ¶m.volume, .d = "speech level, which is '1.0'" }, + { .n = "loop", .s = ¶m.loop, .d = "play in an endless loop", .no_value = 1 }, + { .n = NULL } +}; + +static void routing_play(call_t *call, int argc, char *argv[], struct command_def *command_def) { call_relation_t *relation = call->relation_list; - char *filename = NULL, *volume = "1.0", *loop = NULL; - int i; int samplerate = 8000, channels = 0; double deviation; int rc; @@ -1253,29 +1358,21 @@ static void routing_play(call_t *call, int argc, char *argv[]) return; } - /* loop through all arguments and stop if there is a ':' */ - for (i = 0; i < argc ; i++) { - if (value_of_param(argv[i], "volume", &volume)); - else if (value_of_param(argv[i], "loop", &loop)); - else { - if (filename) { - PDEBUG(DROUTER, DEBUG_ERROR, "'play' command reqires only one file name, you specified '%s' and '%s'.\n", filename, argv[i]); - return; - } - filename = argv[i]; - } - } + parse_params(argc, argv, command_def); - if (!filename) { + if (!param.filename) { PDEBUG(DROUTER, DEBUG_ERROR, "'play' command reqires a file name as parameter.\n"); return; } - deviation = 1.0 / SPEECH_LEVEL * atof(volume); - rc = wave_create_playback(&relation->play, filename, &samplerate, &channels, deviation); + if (!param.volume) + param.volume = "1.0"; + + deviation = 1.0 / SPEECH_LEVEL * atof(param.volume); + rc = wave_create_playback(&relation->play, param.filename, &samplerate, &channels, deviation); if (rc < 0) return; - strncpy(relation->play_filename, filename, sizeof(relation->play_filename) - 1); + strncpy(relation->play_filename, param.filename, sizeof(relation->play_filename) - 1); relation->play_deviation = deviation; @@ -1285,12 +1382,12 @@ static void routing_play(call_t *call, int argc, char *argv[]) return; } - if (loop) + if (param.loop) relation->play_loop = 1; } /* routing orders us stop playing a wave file */ -static void routing_play_stop(call_t *call) +static void routing_play_stop(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation = call->relation_list; @@ -1298,11 +1395,15 @@ static void routing_play_stop(call_t *call) } /* routing orders us to record a wave file */ -static void routing_record(call_t *call, int argc, char *argv[]) +struct param_def param_record[] = { + { .n = "filename", .s = ¶m.filename, .d = "file name of audio file", .mandatory = 1 }, + { .n = "volume", .s = ¶m.volume, .d = "speech level, which is '1.0'" }, + { .n = NULL } +}; + +static void routing_record(call_t *call, int argc, char *argv[], struct command_def *command_def) { call_relation_t *relation = call->relation_list; - char *filename = NULL, *volume = "1.0"; - int i; int samplerate = 8000, channels = 2; int rc; @@ -1313,30 +1414,23 @@ static void routing_record(call_t *call, int argc, char *argv[]) return; } - /* loop through all arguments and stop if there is a ':' */ - for (i = 0; i < argc ; i++) { - if (value_of_param(argv[i], "volume", &volume)); - else { - if (filename) { - PDEBUG(DROUTER, DEBUG_ERROR, "'record' command reqires only one file name, you specified '%s' and '%s'.\n", filename, argv[i]); - return; - } - filename = argv[i]; - } - } + parse_params(argc, argv, command_def); - if (!filename) { + if (!param.filename) { PDEBUG(DROUTER, DEBUG_ERROR, "'record' command reqires a file name as parameter.\n"); return; } - rc = wave_create_record(&relation->rec, filename, samplerate, channels, 1.0 / SPEECH_LEVEL / atof(volume)); + if (!param.volume) + param.volume = "1.0"; + + rc = wave_create_record(&relation->rec, param.filename, samplerate, channels, 1.0 / SPEECH_LEVEL / atof(param.volume)); if (rc < 0) return; } /* routing orders us stop recording a wave file */ -static void routing_record_stop(call_t *call) +static void routing_record_stop(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation = call->relation_list; @@ -1344,51 +1438,85 @@ static void routing_record_stop(call_t *call) } /* routing orders us to set local gain */ -static void routing_gain(call_t *call, int argc, char *argv[], int tx) -{ - int i; - char *gain = NULL; +struct param_def param_gain[] = { + { .n = "gain", .s = ¶m.gain, .d = "gain in dB" }, + { .n = NULL } +}; +static void routing_tx_gain(call_t *call, int argc, char *argv[], struct command_def *command_def) +{ if (!call->relation_list->rtp_proxy) { PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to record a file!\n"); return; } - /* loop through all arguments and stop if there is a ':' */ - for (i = 0; i < argc ; i++) { - if (gain) { - PDEBUG(DROUTER, DEBUG_ERROR, "'%s-gain' command reqires only parameter, you specified '%s' and '%s'.\n", (tx) ? "tx" : "rx", gain, argv[i]); - return; - } - gain = argv[i]; - } + parse_params(argc, argv, command_def); - if (!gain) { - PDEBUG(DROUTER, DEBUG_ERROR, "'%s-gain' command reqires a gain value as parameter.\n", (tx) ? "tx" : "rx"); + if (!param.gain) { + PDEBUG(DROUTER, DEBUG_ERROR, "'tx-gain' command reqires a gain value as parameter.\n"); return; } - if (tx) - call->tx_gain = atof(gain); - else - call->rx_gain = atof(gain); + call->tx_gain = atof(param.gain); +} + +static void routing_rx_gain(call_t *call, int argc, char *argv[], struct command_def *command_def) +{ + if (!call->relation_list->rtp_proxy) { + PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to record a file!\n"); + return; + } + + parse_params(argc, argv, command_def); + + if (!param.gain) { + PDEBUG(DROUTER, DEBUG_ERROR, "'rx-gain' command reqires a gain value as parameter.\n"); + return; + } + + call->rx_gain = atof(param.gain); } /* routing orders us to call remote end */ -static void routing_call(call_t *call, int argc, char *argv[]) +struct param_def param_call[] = { + { .n = "interface", .s = ¶m.interface, .d = "name of interface to route the call to", .mandatory = 1}, + { .n = "bearer-coding", .i = ¶m.bearer_coding, .d = "coding of bearer capability", .name2value = osmo_cc_coding_name2value, .value2name = osmo_cc_coding_value2name, .num = OSMO_CC_CODING_NUM }, + { .n = "bearer-capability", .i = ¶m.bearer_capability, .d = "bearer capability value", .name2value = osmo_cc_capability_name2value, .value2name = osmo_cc_capability_value2name, .num = OSMO_CC_CAPABILITY_NUM }, + { .n = "bearer-mode", .i = ¶m.bearer_mode, .d = "bearer mode", .name2value = osmo_cc_mode_name2value, .value2name = osmo_cc_mode_value2name, .num = OSMO_CC_MODE_NUM }, + { .n = "calling", .s = ¶m.calling, .d = "calling party number" }, + { .n = "calling-type", .i = ¶m.calling_type, .d = "type of calling party number", .name2value = osmo_cc_type_name2value, .value2name = osmo_cc_type_value2name, .num = OSMO_CC_TYPE_NUM}, + { .n = "calling-plan", .i = ¶m.calling_plan, .d = "numbering plan of calling party number", .name2value = osmo_cc_plan_name2value, .value2name = osmo_cc_plan_value2name, .num = OSMO_CC_PLAN_NUM }, + { .n = "calling-present", .i = ¶m.calling_present, .d = "presentation of calling party number", .name2value = osmo_cc_present_name2value, .value2name = osmo_cc_present_value2name, .num = OSMO_CC_PRESENT_NUM }, + { .n = "calling-screen", .i = ¶m.calling_screen, .d = "screening indicator of calling party number", .name2value = osmo_cc_screen_name2value, .value2name = osmo_cc_screen_value2name, .num = OSMO_CC_SCREEN_NUM }, + { .n = "no-calling", .i = ¶m.no_calling, .d = "disable calling party number", .no_value = 1}, + { .n = "calling2", .s = ¶m.calling2, .d = "second calling party number" }, + { .n = "calling2-type", .i = ¶m.calling2_type, .d = "type of second calling party number", .name2value = osmo_cc_type_name2value, .value2name = osmo_cc_type_value2name, .num = OSMO_CC_TYPE_NUM}, + { .n = "calling2-plan", .i = ¶m.calling2_plan, .d = "numbering plan of second calling party number", .name2value = osmo_cc_plan_name2value, .value2name = osmo_cc_plan_value2name, .num = OSMO_CC_PLAN_NUM }, + { .n = "calling2-present", .i = ¶m.calling2_present, .d = "presentation of second calling party number", .name2value = osmo_cc_present_name2value, .value2name = osmo_cc_present_value2name, .num = OSMO_CC_PRESENT_NUM }, + { .n = "calling2-screen", .i = ¶m.calling2_screen, .d = "screening indicator of second calling party number", .name2value = osmo_cc_screen_name2value, .value2name = osmo_cc_screen_value2name, .num = OSMO_CC_SCREEN_NUM }, + { .n = "no-calling2", .i = ¶m.no_calling2, .d = "disable seconds calling party number", .no_value = 1 }, + { .n = "redirecting", .s = ¶m.redirecting, .d = "redirecting number" }, + { .n = "redirecting-type", .i = ¶m.redirecting_type, .d = "type of redirecting number", .name2value = osmo_cc_type_name2value, .value2name = osmo_cc_type_value2name, .num = OSMO_CC_TYPE_NUM }, + { .n = "redirecting-plan", .i = ¶m.redirecting_plan, .d = "numbering plan of redirecting number", .name2value = osmo_cc_plan_name2value, .value2name = osmo_cc_plan_value2name, .num = OSMO_CC_PLAN_NUM }, + { .n = "redirecting-present", .i = ¶m.redirecting_present, .d = "presentation of redirecting number", .name2value = osmo_cc_present_name2value, .value2name = osmo_cc_present_value2name, .num = OSMO_CC_PRESENT_NUM }, + { .n = "redirecting-screen", .i = ¶m.redirecting_screen, .d = "screening indicator of redirecting number", .name2value = osmo_cc_screen_name2value, .value2name = osmo_cc_screen_value2name, .num = OSMO_CC_SCREEN_NUM }, + { .n = "redirecting-reason", .i = ¶m.redirecting_reason, .d = "reason for redirecting the call", .name2value = osmo_cc_redir_reason_name2value, .value2name = osmo_cc_redir_reason_value2name, .num = OSMO_CC_REDIR_REASON_NUM }, + { .n = "no-redirecting", .i = ¶m.no_redirecting, .d = "disable redirecting number", .no_value = 1 }, + { .n = "dialing", .s = ¶m.dialing, .d = "alter dialed number" }, + { .n = "dialing-type", .i = ¶m.dialing_type, .d = "type of dialed number", .name2value = osmo_cc_type_name2value, .value2name = osmo_cc_type_value2name, .num = OSMO_CC_TYPE_NUM }, + { .n = "dialing-plan", .i = ¶m.dialing_plan, .d = "numbering plan of dialed number", .name2value = osmo_cc_plan_name2value, .value2name = osmo_cc_plan_value2name, .num = OSMO_CC_PLAN_NUM }, + { .n = "keypad", .s = ¶m.keypad, .d = "keypulse digit or digits (might be required by some ISDN applications)" }, + { .n = NULL } +}; + +static void routing_call(call_t *call, int argc, char *argv[], struct command_def *command_def) { - char *interface; - char *bearer_coding, *bearer_capability, *bearer_mode; - char *calling, *calling_type, *calling_plan, *calling_present, *calling_screen, *no_calling; - char *calling2, *calling2_type, *calling2_plan, *calling2_present, *calling2_screen, *no_calling2; - char *redirecting, *redirecting_type, *redirecting_plan, *redirecting_present, *redirecting_screen, *redirecting_reason, *no_redirecting; - char *dialing, *dialing_type, *dialing_plan; - char *keypad; + osmo_cc_call_t *att = NULL; uint8_t coding, capability, mode; uint8_t type, plan, present, screen, reason; char number[256]; - int i, rc, calls = 0; + int e, i, rc, calls = 0; osmo_cc_msg_t *new_msg, *setup_msg; /* if we have a call, we don't add more terminators */ @@ -1400,56 +1528,28 @@ static void routing_call(call_t *call, int argc, char *argv[]) setup_msg = call->setup_msg; next_call: - interface = NULL; - bearer_coding = bearer_capability = bearer_mode = NULL; - calling = calling_type = calling_plan = calling_present = calling_screen = no_calling = NULL; - calling2 = calling2_type = calling2_plan = calling2_present = calling2_screen = no_calling2 = NULL; - redirecting = redirecting_type = redirecting_plan = redirecting_present = redirecting_screen = redirecting_reason = no_redirecting = NULL; - dialing = dialing_type = dialing_plan = NULL; - keypad = NULL; - /* loop through all arguments and stop if there is a ':' */ - for (i = 0; i < argc && argv[i][0] != ':'; i++) { - if (value_of_param(argv[i], "interface", &interface)); - else if (value_of_param(argv[i], "bearer-coding", &bearer_coding)); - else if (value_of_param(argv[i], "bearer-capability", &bearer_capability)); - else if (value_of_param(argv[i], "bearer-mode", &bearer_mode)); - else if (value_of_param(argv[i], "calling", &calling)); - else if (value_of_param(argv[i], "calling-type", &calling_type)); - else if (value_of_param(argv[i], "calling-plan", &calling_plan)); - else if (value_of_param(argv[i], "calling-present", &calling_present)); - else if (value_of_param(argv[i], "calling-screen", &calling_screen)); - else if (value_of_param(argv[i], "no-calling", &no_calling)); - else if (value_of_param(argv[i], "calling2", &calling2)); - else if (value_of_param(argv[i], "calling2-type", &calling2_type)); - else if (value_of_param(argv[i], "calling2-plan", &calling2_plan)); - else if (value_of_param(argv[i], "calling2-present", &calling2_present)); - else if (value_of_param(argv[i], "calling2-screen", &calling2_screen)); - else if (value_of_param(argv[i], "no-calling2", &no_calling2)); - else if (value_of_param(argv[i], "redirecting", &redirecting)); - else if (value_of_param(argv[i], "redirecting-type", &redirecting_type)); - else if (value_of_param(argv[i], "redirecting-plan", &redirecting_plan)); - else if (value_of_param(argv[i], "redirecting-present", &redirecting_present)); - else if (value_of_param(argv[i], "redirecting-screen", &redirecting_screen)); - else if (value_of_param(argv[i], "redirecting-reason", &redirecting_reason)); - else if (value_of_param(argv[i], "no-redirecting", &no_redirecting)); - else if (value_of_param(argv[i], "dialing", &dialing)); - else if (value_of_param(argv[i], "dialing-type", &dialing_type)); - else if (value_of_param(argv[i], "dialing-plan", &dialing_plan)); - else if (value_of_param(argv[i], "keypad", &keypad)); - else - PDEBUG(DROUTER, DEBUG_ERROR, "Unknown 'call' parameter '%s' from routing.\n", argv[i]); - } + i = parse_params(argc, argv, command_def); + /* if more calls, then forward arguments behind colon, otherwise set argc to 0 */ - if (i < argc) { + if (i >= 0 && i < argc) { argv += i + 1; argc -= i + 1; } else argc = 0; - if (!interface) { + if (!param.interface) { PDEBUG(DROUTER, DEBUG_ERROR, "'call' command without 'interface' parameter.\n"); goto try_next; } + for (e = 0; cc_ep_list[e]; e++) { + att = osmo_cc_get_attached_interface(cc_ep_list[e], param.interface); + if (att) + break; + } + if (!att) { + PDEBUG(DROUTER, DEBUG_ERROR, "'call' command with 'interface' parameter '%s' which is not attached.\n", param.interface); + goto try_next; + } calls++; @@ -1472,11 +1572,11 @@ next_call: new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_IND); /* interface name */ - osmo_cc_add_ie_called_interface(new_msg, interface); + osmo_cc_add_ie_called_interface(new_msg, param.interface); /* bearer capability */ rc = osmo_cc_get_ie_bearer(setup_msg, 0, &coding, &capability, &mode); - if (rc >= 0 || bearer_coding || bearer_capability || bearer_mode) { + if (rc >= 0 || param.bearer_coding || param.bearer_capability || param.bearer_mode) { /* if not preset, use default values */ if (rc < 0) { coding = OSMO_CC_CODING_ITU_T; @@ -1484,19 +1584,19 @@ next_call: mode = OSMO_CC_MODE_CIRCUIT; } /* alter values */ - if (bearer_coding) - coding = atoi(bearer_coding); - if (bearer_capability) - capability = atoi(bearer_capability); - if (bearer_mode) - mode = atoi(bearer_coding); + if (param.bearer_coding) + coding = param.bearer_coding; + if (param.bearer_capability) + capability = param.bearer_capability; + if (param.bearer_mode) + mode = param.bearer_coding; osmo_cc_add_ie_bearer(new_msg, coding, capability, mode); } /* calling */ - if (!no_calling) { + if (!param.no_calling) { rc = osmo_cc_get_ie_calling(setup_msg, 0, &type, &plan, &present, &screen, number, sizeof(number)); - if (rc >= 0 || calling_type || calling_plan || calling_present || calling_screen || calling) { + if (rc >= 0 || param.calling_type || param.calling_plan || param.calling_present || param.calling_screen || param.calling) { /* if not preset, use default values */ if (rc < 0) { type = OSMO_CC_TYPE_UNKNOWN; @@ -1506,22 +1606,22 @@ next_call: number[0] = '\0'; } /* alter values */ - if (calling_type) - type = atoi(calling_type); - if (calling_plan) - plan = atoi(calling_plan); - if (calling_present) - present = atoi(calling_present); - if (calling_screen) - screen = atoi(calling_screen); - if (!calling) - calling = number; - osmo_cc_add_ie_calling(new_msg, type, plan, present, screen, calling); + if (param.calling_type) + type = param.calling_type; + if (param.calling_plan) + plan = param.calling_plan; + if (param.calling_present) + present = param.calling_present; + if (param.calling_screen) + screen = param.calling_screen; + if (!param.calling) + param.calling = number; + osmo_cc_add_ie_calling(new_msg, type, plan, present, screen, param.calling); } } - if (!no_calling && !no_calling2) { + if (!param.no_calling && !param.no_calling2) { rc = osmo_cc_get_ie_calling(setup_msg, 1, &type, &plan, &present, &screen, number, sizeof(number)); - if (rc >= 0 || calling2_type || calling2_plan || calling2_present || calling2_screen || calling2) { + if (rc >= 0 || param.calling2_type || param.calling2_plan || param.calling2_present || param.calling2_screen || param.calling2) { /* if not preset, use default values */ if (rc < 0) { type = OSMO_CC_TYPE_UNKNOWN; @@ -1531,24 +1631,24 @@ next_call: number[0] = '\0'; } /* alter values */ - if (calling2_type) - type = atoi(calling2_type); - if (calling2_plan) - plan = atoi(calling2_plan); - if (calling2_present) - present = atoi(calling2_present); - if (calling2_screen) - screen = atoi(calling2_screen); - if (!calling2) - calling2 = number; - osmo_cc_add_ie_calling(new_msg, type, plan, present, screen, calling2); + if (param.calling2_type) + type = param.calling2_type; + if (param.calling2_plan) + plan = param.calling2_plan; + if (param.calling2_present) + present = param.calling2_present; + if (param.calling2_screen) + screen = param.calling2_screen; + if (!param.calling2) + param.calling2 = number; + osmo_cc_add_ie_calling(new_msg, type, plan, present, screen, param.calling2); } } /* redirecting */ - if (!no_redirecting) { + if (!param.no_redirecting) { rc = osmo_cc_get_ie_redir(setup_msg, 0, &type, &plan, &present, &screen, &reason, number, sizeof(number)); - if (rc >= 0 || redirecting_type || redirecting_plan || redirecting_present || redirecting_screen || redirecting) { + if (rc >= 0 || param.redirecting_type || param.redirecting_plan || param.redirecting_present || param.redirecting_screen || param.redirecting) { /* if not preset, use default values */ if (rc < 0) { type = OSMO_CC_TYPE_UNKNOWN; @@ -1559,62 +1659,62 @@ next_call: number[0] = '\0'; } /* alter values */ - if (redirecting_type) - type = atoi(redirecting_type); - if (redirecting_plan) - plan = atoi(redirecting_plan); - if (redirecting_present) - present = atoi(redirecting_present); - if (redirecting_screen) - screen = atoi(redirecting_screen); - if (!redirecting) - redirecting = number; - osmo_cc_add_ie_redir(new_msg, type, plan, present, screen, reason, redirecting); + if (param.redirecting_type) + type = param.redirecting_type; + if (param.redirecting_plan) + plan = param.redirecting_plan; + if (param.redirecting_present) + present = param.redirecting_present; + if (param.redirecting_screen) + screen = param.redirecting_screen; + if (!param.redirecting) + param.redirecting = number; + osmo_cc_add_ie_redir(new_msg, type, plan, present, screen, reason, param.redirecting); } } /* dialing */ rc = osmo_cc_get_ie_called(setup_msg, 0, &type, &plan, number, sizeof(number)); - if (rc >= 0 || dialing_type || dialing_plan || dialing) { + if (rc >= 0 || param.dialing_type || param.dialing_plan || param.dialing) { /* if not preset, use default values */ if (rc < 0) { type = OSMO_CC_TYPE_UNKNOWN; plan = OSMO_CC_PLAN_TELEPHONY; } /* alter values */ - if (dialing_type) - type = atoi(dialing_type); - if (dialing_plan) - plan = atoi(dialing_plan); - if (!dialing) - dialing = ""; - osmo_cc_add_ie_called(new_msg, type, plan, dialing); + if (param.dialing_type) + type = param.dialing_type; + if (param.dialing_plan) + plan = param.dialing_plan; + if (!param.dialing) + param.dialing = ""; + osmo_cc_add_ie_called(new_msg, type, plan, param.dialing); } /* keypad */ - if (keypad) { - if (keypad[0]) - osmo_cc_add_ie_keypad(new_msg, keypad); + if (param.keypad) { + if (param.keypad[0]) + osmo_cc_add_ie_keypad(new_msg, param.keypad); } /* only if RTP-Proxy is used */ if (call->relation_list->rtp_proxy) { PDEBUG(DROUTER, DEBUG_DEBUG, "Sending our codecs to the terminator.\n"); - relation->cc_session = osmo_cc_helper_audio_offer(relation, call->relation_list->term_codecs, receive_terminator, new_msg, 1); + relation->cc_session = osmo_cc_helper_audio_offer(&relation->cc_ep->session_config, relation, call->relation_list->term_codecs, receive_terminator, new_msg, 1); } else /* sdp from originator's setup message */ if (call->relation_list->sdp) osmo_cc_add_ie_sdp(new_msg, call->relation_list->sdp); /* send message to osmo-cc */ - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); /* remember this, since we cannot do RTP-Proxy after */ call->sdp_forwarded = 1; /* store peer info for status */ - strncpy(relation->interface, interface, sizeof(relation->interface) - 1); - strncpy(relation->id, dialing, sizeof(relation->id) - 1); + strncpy(relation->interface, (param.interface) ? : "", sizeof(relation->interface) - 1); + strncpy(relation->id, (param.dialing) ? : "", sizeof(relation->id) - 1); /* update call state for status display */ relation->state = CALL_STATE_SETUP; @@ -1627,7 +1727,7 @@ try_next: } /* routing orders us to hangup all terminating calls */ -static void routing_call_stop(call_t *call) +static void routing_call_stop(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation; osmo_cc_msg_t *new_msg; @@ -1637,14 +1737,14 @@ static void routing_call_stop(call_t *call) relation = call->relation_list->next; PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation)); new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND); - osmo_cc_add_ie_cause(new_msg, cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR, 0, 0); - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR, 0, 0); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); relation_destroy(relation); } } /* routing orders us to set call into overlap state */ -static void routing_overlap(call_t *call) +static void routing_overlap(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation = call->relation_list; osmo_cc_msg_t *new_msg; @@ -1662,11 +1762,11 @@ static void routing_overlap(call_t *call) /* send SDP answer */ proxy_send_sdp_answer(relation, new_msg); - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); } /* routing orders us to set call into proceeding state */ -static void routing_proceeding(call_t *call) +static void routing_proceeding(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation = call->relation_list; osmo_cc_msg_t *new_msg; @@ -1685,11 +1785,11 @@ static void routing_proceeding(call_t *call) /* send SDP answer */ proxy_send_sdp_answer(relation, new_msg); - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); } /* routing orders us to set call into alerting state */ -static void routing_alerting(call_t *call) +static void routing_alerting(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation = call->relation_list; osmo_cc_msg_t *new_msg; @@ -1709,11 +1809,11 @@ static void routing_alerting(call_t *call) /* send SDP answer */ proxy_send_sdp_answer(relation, new_msg); - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); } /* routing orders us to set call into answer state */ -static void routing_answer(call_t *call) +static void routing_answer(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation = call->relation_list; osmo_cc_msg_t *new_msg; @@ -1734,51 +1834,69 @@ static void routing_answer(call_t *call) /* send SDP answer */ proxy_send_sdp_answer(relation, new_msg); - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); } /* routing orders us to dsiconnect/release the call */ -static void routing_disc_rel(call_t *call, int argc, char *argv[], int disconnect) +struct param_def param_disc_rel[] = { + { .n = "isdn-cause", .i = ¶m.isdn_cause, .d = "ISDN cause number (Will be generated, if omitted.)" }, + { .n = "sip-cause", .i = ¶m.sip_cause, .d = "SIP cause number (Will be generated, if omitted.)" }, + { .n = NULL } +}; + +static void routing_disconnect(call_t *call, int argc, char *argv[], struct command_def *command_def) { call_relation_t *relation = call->relation_list; - char *cause_str; - uint8_t cause = 16; osmo_cc_msg_t *new_msg; - int i; - /* get cause, if any */ - for (i = 0; i < argc; i++) { - if (value_of_param(argv[i], "cause", &cause_str)) - cause = atoi(cause_str); - else - PDEBUG(DROUTER, DEBUG_ERROR, "Unknown 'disconnect' / 'release' parameter '%s' from routing.\n", argv[i]); - } + parse_params(argc, argv, command_def); - PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-%s-IND to originator.\n", relation_name(relation), (disconnect) ? "DISC" : "REL"); + PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-DISC-IND to originator.\n", relation_name(relation)); /* send message to originator */ - new_msg = osmo_cc_new_msg((disconnect) ? OSMO_CC_MSG_DISC_IND : OSMO_CC_MSG_REL_IND); - if (disconnect) { - /* send SDP answer */ - proxy_send_sdp_answer(relation, new_msg); - } + new_msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND); + /* send SDP answer */ + proxy_send_sdp_answer(relation, new_msg); /* add cause */ - osmo_cc_add_ie_cause(new_msg, cc_ep->serving_location, cause, 0, 0); - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, param.isdn_cause, param.sip_cause, 0); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); /* send message to all terminators, if any */ for (relation = relation->next; relation; relation = relation->next) { PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation)); new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND); /* add cause */ - osmo_cc_add_ie_cause(new_msg, cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR, 0, 0); - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR, 0, 0); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); + } +} + +static void routing_release(call_t *call, int argc, char *argv[], struct command_def *command_def) +{ + call_relation_t *relation = call->relation_list; + osmo_cc_msg_t *new_msg; + + parse_params(argc, argv, command_def); + + PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-IND to originator.\n", relation_name(relation)); + + /* send message to originator */ + new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND); + /* add cause */ + osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, param.isdn_cause, param.sip_cause, 0); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); + + /* send message to all terminators, if any */ + for (relation = relation->next; relation; relation = relation->next) { + PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation)); + new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND); + /* add cause */ + osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR, 0, 0); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); } - if (!disconnect) { - /* destroy call */ - call_destroy(call); - } + /* destroy call */ + call_destroy(call); } #define db2level(db) pow(10, (double)(db) / 20.0) @@ -1796,7 +1914,7 @@ void recv_dtmf(void *priv, char digit, dtmf_meas_t __attribute__((unused)) *meas } /* routing orders us to enable DTMF decoding */ -static void routing_dtmf(call_t *call) +static void routing_dtmf(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation = call->relation_list; @@ -1809,7 +1927,7 @@ static void routing_dtmf(call_t *call) } /* routing orders us to disable DTMF decoding */ -static void routing_dtmf_stop(call_t *call) +static void routing_dtmf_stop(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { call_relation_t *relation = call->relation_list; @@ -1819,43 +1937,66 @@ static void routing_dtmf_stop(call_t *call) } /* routing failed, release the call */ -static void routing_error(call_t *call, char *error) +struct param_def param_error[] = { + { .n = "string", .s = ¶m.error, .d = "error string to be displayed", .mandatory = 1 }, + { .n = NULL } +}; + +static void routing_error(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def) { osmo_cc_msg_t *new_msg; call_relation_t *relation; - PDEBUG(DROUTER, DEBUG_ERROR, "Routing script error: '%s'\n", error); + parse_params(argc, argv, command_def); + + if (!param.error) { + PDEBUG(DROUTER, DEBUG_ERROR, "'error' command reqires an error string as parameter.\n"); + return; + } + + PDEBUG(DROUTER, DEBUG_ERROR, "Routing script error: '%s'\n", param.error); /* send message to originator */ new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND); - osmo_cc_add_ie_cause(new_msg, cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NETWORK_OOO, 0, 0); - osmo_cc_ll_msg(cc_ep, call->relation_list->cc_callref, new_msg); + osmo_cc_add_ie_cause(new_msg, call->relation_list->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NETWORK_OOO, 0, 0); + osmo_cc_ll_msg(call->relation_list->cc_ep, call->relation_list->cc_callref, new_msg); /* send message to all terminators, if any */ for (relation = call->relation_list->next; relation; relation = relation->next) { new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND); - osmo_cc_add_ie_cause(new_msg, cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NETWORK_OOO, 0, 0); - osmo_cc_ll_msg(cc_ep, relation->cc_callref, new_msg); + osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NETWORK_OOO, 0, 0); + osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg); } } -#if 0 -/* routing script says something */ -static void routing_say(call_t *call, char *error) -{ - char text[1024] = ""; - fuer alle argv - fuege text hinzu - PDEBUG(DROUTER, DEBUG_NOTICE, "Routing script says: %s\n", text); - das script anpassen -} -#endif +struct command_def command_def[] = { + { "rtp-proxy", routing_rtp_proxy, "Turn on RTP proxy, so that audio processing is possible.", param_rtp_proxy }, + { "play", routing_play, "Play given audio file.", param_play }, + { "play-stop", routing_play_stop, "Stop playing audio file.", NULL }, + { "record", routing_record, "Record to given audio file.", param_record }, + { "record-stop", routing_record_stop, "Stop recording audio file.", NULL }, + { "tx-gain", routing_tx_gain, "Set gain of audio sent interface.", param_gain }, + { "rx-gain", routing_rx_gain, "Set gain of audio received from interface.", param_gain }, + { "call", routing_call, "Make one or mulriple calls to given interface.", param_call }, + { "call-stop", routing_call_stop, "Stop outgoing call(s).", NULL }, + { "overlap", routing_overlap, "Set caller into overlap state, digit may be dialed.", NULL }, + { "proceeding", routing_proceeding, "Set caller into proceeding state, no digits may be dialed.", NULL }, + { "alerting", routing_alerting, "Set caller into alerting state.", NULL }, + { "answer", routing_answer, "Answer call from caller.", NULL }, + { "disconnect", routing_disconnect, "Disconnect call towards caller and release callee, if any.", param_disc_rel }, + { "release", routing_release, "Release call towards caller and release callee, if any.", param_disc_rel }, + { "dtmf", routing_dtmf, "Turn on DTMF detection.", NULL }, + { "dtmf-stop", routing_dtmf_stop, "Turn off DTMF detection.", NULL }, + { "error", routing_error, "Display error message.", param_error }, + { NULL, NULL, NULL, NULL } +}; void routing_receive_stdout(routing_t *routing, const char *string) { call_t *call = routing->call; int argc = 0; char *argv[256], *token; + int i; /* convert string into tokens */ while ((token = osmo_cc_strtok_quotes(&string))) @@ -1863,65 +2004,13 @@ void routing_receive_stdout(routing_t *routing, const char *string) if (!argc) return; - if (!strcasecmp(argv[0], "rtp-proxy")) - routing_rtp_proxy(call, argc - 1, argv + 1); - else - if (!strcasecmp(argv[0], "play")) - routing_play(call, argc - 1, argv + 1); - else - if (!strcasecmp(argv[0], "play-stop")) - routing_play_stop(call); - else - if (!strcasecmp(argv[0], "record")) - routing_record(call, argc - 1, argv + 1); - else - if (!strcasecmp(argv[0], "record-stop")) - routing_record_stop(call); - else - if (!strcasecmp(argv[0], "tx-gain")) - routing_gain(call, argc - 1, argv + 1, 1); - else - if (!strcasecmp(argv[0], "rx-gain")) - routing_gain(call, argc - 1, argv + 1, 0); - else - if (!strcasecmp(argv[0], "call")) - routing_call(call, argc - 1, argv + 1); - else - if (!strcasecmp(argv[0], "call-stop")) - routing_call_stop(call); - else - if (!strcasecmp(argv[0], "overlap")) - routing_overlap(call); - else - if (!strcasecmp(argv[0], "proceeding")) - routing_proceeding(call); - else - if (!strcasecmp(argv[0], "alerting")) - routing_alerting(call); - else - if (!strcasecmp(argv[0], "answer")) - routing_answer(call); - else - if (!strcasecmp(argv[0], "disconnect")) - routing_disc_rel(call, argc - 1, argv + 1, 1); - else - if (!strcasecmp(argv[0], "release")) - routing_disc_rel(call, argc - 1, argv + 1, 0); - else - if (!strcasecmp(argv[0], "dtmf")) - routing_dtmf(call); - else - if (!strcasecmp(argv[0], "dtmf-stop")) - routing_dtmf_stop(call); - else - if (!strcasecmp(argv[0], "error")) - routing_error(call, (argc > 1) ? argv[1] : ""); - else -#if 0 - if (!strcasecmp(argv[0], "say")) - routing_say(call, argc - 1, argv + 1, 0); - else -#endif + for (i = 0; command_def[i].name; i++) { + if (!strcasecmp(argv[0], command_def[i].name)) { + command_def[i].func(call, argc - 1, argv + 1, &command_def[i]); + break; + } + } + if (!command_def[i].name) PDEBUG(DROUTER, DEBUG_ERROR, "Unknown command '%s' from routing.\n", argv[0]); while (argc) @@ -1996,6 +2085,58 @@ void telephone_event(call_relation_t *relation, struct telephone_event *te) routing_send(&relation->call->routing, digit_string); } +void routing_help(const char *command) +{ + int i, j, k; + + if (!command) { + printf("Available routing commands:\n\n"); + for (i = 0; command_def[i].name; i++) { + printf("Command: %s\n", command_def[i].name); + printf(" Description: %s\n", command_def[i].description); + } + printf("\nUse '-h ' for detailled description of a command.\n"); + return; + } + + for (i = 0; command_def[i].name; i++) { + if (!strcasecmp(command, command_def[i].name)) + break; + } + if (!command_def[i].name) { + printf("Given Command '%s' unknown!\n", command); + return; + } + + printf("Command: %s\n", command_def[i].name); + printf("Description: %s\n", command_def[i].description); + + struct param_def *param_def = command_def[i].param_list; + + if (!param_def) { + printf("This command does not have any parameters.\n"); + return; + } + + for (j = 0; param_def[j].n; j++) { + printf("Parameter: %s%s\n", param_def[j].n, (param_def[j].mandatory) ? " (manatory)" : ""); + printf(" Description: %s\n", param_def[j].d); + if (param_def[j].o || param_def[j].t) { + for (k = 0; codecs_def[k].payload_name; k++) { + printf(" Value: '%s'\n", codecs_def[k].payload_name); + } + } else if (param_def[j].no_value) { + printf(" No value\n"); + } else if (param_def[j].i && param_def[j].value2name) { + for (k = 0; k < param_def[j].num; k++) { + const char *name = param_def[j].value2name(k); + if (name && name[0] != '<') + printf(" Value: '%d' or '%s'\n", k, name); + } + } + } +} + #warning add progress, if terminating call sends sdp but call state already reached #warning beim disc muss progress geprueft werden und damit entschieden ob wir audio mitsenden sollen diff --git a/src/router/call.h b/src/router/call.h index 255ee20..ac672ac 100644 --- a/src/router/call.h +++ b/src/router/call.h @@ -96,3 +96,5 @@ int call_handle(void); void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg); void telephone_event(call_relation_t *relation, struct telephone_event *te); +void routing_help(const char *command); + diff --git a/src/router/main.c b/src/router/main.c index 82eeee3..eaf2d95 100644 --- a/src/router/main.c +++ b/src/router/main.c @@ -32,6 +32,7 @@ #include "audio.h" #include "display.h" +int show_help = 0; int num_kanal = 1; static osmo_cc_endpoint_t *cc_ep1 = NULL, *cc_ep2 = NULL; @@ -85,9 +86,8 @@ static int handle_options(int short_option, int argi, char **argv) switch (short_option) { case 'h': - print_usage(argv[0]); - print_help(); - return 0; + show_help = 1; + break; case 'v': if (!strcasecmp(argv[argi], "list")) { debug_list_cat(); @@ -182,6 +182,19 @@ int main(int argc, char *argv[]) argi = options_command_line(argc, argv, handle_options); if (argi <= 0) return argi; + if (show_help) { + if (argi < argc) { + routing_help(argv[argi]); + return 0; + } + print_usage(argv[0]); + print_help(); + printf("\n"); + env_help(); + printf("\n"); + routing_help(NULL); + return 0; + } /* init osmo-cc endpoint */ cc_ep1 = calloc(1, sizeof(*cc_ep1)); diff --git a/src/router/routing.c b/src/router/routing.c index b49f6d4..8d5485c 100644 --- a/src/router/routing.c +++ b/src/router/routing.c @@ -34,22 +34,241 @@ extern char **environ; -static const char *env_int(const char *name, int value) +static struct env { + int coding, capability, mode; + + int type, plan, present, screen, reason; + char *number; + + int network_type; + char *network_id; + + int complete; +} env; + +struct env_def { + const char *n; + char **s; + int *i; + const char *d; + const char *(*value2name)(int value); + int num; +} env_def[] = { + { .n = "BEARER_CODING", .i = &env.coding, .d = "coding of bearer capability", .value2name = osmo_cc_coding_value2name, .num = OSMO_CC_CODING_NUM }, + { .n = "BEARER_CAPABILITY", .i = &env.capability, .d = "bearer capability", .value2name = osmo_cc_capability_value2name, .num = OSMO_CC_CAPABILITY_NUM }, + { .n = "BEARER_MODE", .i = &env.mode, .d = "mode of bearer", .value2name = osmo_cc_mode_value2name, .num = OSMO_CC_MODE_NUM }, + + { .n = "CALLING_TYPE", .i = &env.type, .d = "type of calling number", .value2name = osmo_cc_type_value2name, .num = OSMO_CC_TYPE_NUM }, + { .n = "CALLING_PLAN", .i = &env.plan, .d = "numbering plan of calling number", .value2name = osmo_cc_plan_value2name, .num = OSMO_CC_PLAN_NUM }, + { .n = "CALLING_PRESENT", .i = &env.present, .d = "presentation of calling number", .value2name = osmo_cc_present_value2name, .num = OSMO_CC_PRESENT_NUM }, + { .n = "CALLING_SCREEN", .i = &env.screen, .d = "screen of calling number", .value2name = osmo_cc_screen_value2name, .num = OSMO_CC_SCREEN_NUM }, + { .n = "CALLING", .s = &env.number, .d = "calling number" }, + { .n = "CALLING_INTERFACE", .s = &env.number, .d = "calling interface name" }, + + { .n = "CALLING2_TYPE", .i = &env.type, .d = "type of second calling number", .value2name = osmo_cc_type_value2name, .num = OSMO_CC_TYPE_NUM }, + { .n = "CALLING2_PLAN", .i = &env.plan, .d = "numbering plan of awxons calling number", .value2name = osmo_cc_plan_value2name, .num = OSMO_CC_PLAN_NUM}, + { .n = "CALLING2_PRESENT", .i = &env.present, .d = "presentation of second calling number", .value2name = osmo_cc_present_value2name, .num = OSMO_CC_PRESENT_NUM }, + { .n = "CALLING2_SCREEN", .i = &env.screen, .d = "screen of second calling number", .value2name = osmo_cc_screen_value2name, .num = OSMO_CC_SCREEN_NUM }, + { .n = "CALLING2", .s = &env.number, .d = "second calling number" }, + + { .n = "NETWORK_TYPE", .i = &env.network_type, .d = "type of calling network" }, + { .n = "NETWORK_ID", .s = &env.network_id, .d = "id of subscriber at calling network" }, + + { .n = "REDIRECTING_TYPE", .i = &env.type, .d = "type of redirecting number", .value2name = osmo_cc_type_value2name, .num = OSMO_CC_TYPE_NUM }, + { .n = "REDIRECTING_PLAN", .i = &env.plan, .d = "numbering plan of redirecting number", .value2name = osmo_cc_plan_value2name, .num = OSMO_CC_PLAN_NUM }, + { .n = "REDIRECTING_PRESENT", .i = &env.present, .d = "presentation of redirecting number", .value2name = osmo_cc_present_value2name, .num = OSMO_CC_PRESENT_NUM }, + { .n = "REDIRECTING_SCREEN", .i = &env.screen, .d = "screen of redirecting number", .value2name = osmo_cc_screen_value2name, .num = OSMO_CC_SCREEN_NUM }, + { .n = "REDIRECTING_REASON", .i = &env.reason, .d = "reason for redirecting", .value2name = osmo_cc_redir_reason_value2name, .num = OSMO_CC_REDIR_REASON_NUM }, + { .n = "REDIRECTING", .s = &env.number, .d = "redirecting number" }, + + { .n = "DIALING_TYPE", .i = &env.type, .d = "type of dialing number", .value2name = osmo_cc_type_value2name, .num = OSMO_CC_TYPE_NUM }, + { .n = "DIALING_PLAN", .i = &env.plan, .d = "numbering plan of dialing number", .value2name = osmo_cc_plan_value2name, .num = OSMO_CC_PLAN_NUM }, + { .n = "DIALING", .s = &env.number, .d = "dialing number" }, + { .n = "KEYPAD", .s = &env.number, .d = "keypad dialing (May be uses for special ISDN applications.)" }, + + { .n = "COMPLETE", .i = &env.complete, .d = "set to '1' if dialing is a complete number" }, + + { .n = NULL } +}; + +static int env_set(routing_t *routing, const char *name, int index) { - char *string = malloc(strlen(name) + 20); + int i; - sprintf(string, "CC_%s=%d", name, value); + for (i = 0; env_def[i].n; i++) { + if (!strcmp(env_def[i].n, name)) + break; + } + if (!env_def[i].n) { + PDEBUG(DROUTER, DEBUG_ERROR, "Software error: Environment variable '%s' does not exist in definition, please fix!\n", name); + return index; + } - return string; + if (env_def[i].s) { + char *string = malloc(strlen(name) + strlen(*env_def[i].s) + 8); + sprintf(string, "CC_%s=%s", name, *env_def[i].s); + routing->envp[index++] = string; + } + + if (env_def[i].i) { + char *string = malloc(strlen(name) + 30); + sprintf(string, "CC_%s=%d", name, *env_def[i].i); + routing->envp[index++] = string; + if (env_def[i].value2name && env_def[i].value2name(*env_def[i].i)[0] != '<') { + char *string = malloc(strlen(name) + strlen(env_def[i].value2name(*env_def[i].i)) + 16); + sprintf(string, "CC_%s_NAME=%s", name, env_def[i].value2name(*env_def[i].i)); + routing->envp[index++] = string; + } + } + + return index; } -static const char *env_string(const char *name, const char *value) +static void env_add(routing_t *routing, const char *name) { - char *string = malloc(strlen(name) + strlen(value) + 8); + routing->envc = env_set(routing, name, routing->envc); +} - sprintf(string, "CC_%s=%s", name, value); +void env_help(void) +{ + int i; - return string; + printf("Available environment variables:\n\n"); + for (i = 0; env_def[i].n; i++) { + printf("Variable: CC_%s\n", env_def[i].n); + printf(" Description: %s\n", env_def[i].d); + if (env_def[i].value2name) { + printf(" Additional variable: CC_%s_NAME\n", env_def[i].n); + } + } +} + +/* prepare environment with setup info */ +void routing_env_msg(routing_t *routing, osmo_cc_msg_t *msg) +{ + int rc, i; + uint8_t coding, capability, mode; + uint8_t type, plan, present, screen, reason; + char number[256]; + uint8_t network_type; + char network_id[256]; + + for (i = 0; environ[i]; i++) { + routing->envp[routing->envc++] = strdup(environ[i]); + } + + memset(&env, 0, sizeof(env)); + + rc = osmo_cc_get_ie_bearer(msg, 0, &coding, &capability, &mode); + if (rc >= 0) { + env.coding = coding; + env.capability = capability; + env.mode = mode; + env_add(routing, "BEARER_CODING"); + env_add(routing, "BEARER_CAPABILITY"); + env_add(routing, "BEARER_MODE"); + } + + rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, number, sizeof(number)); + if (rc >= 0) { + env.type = type; + env.plan = plan; + env.present = present; + env.screen = screen; + env.number = number; + env_add(routing, "CALLING_TYPE"); + env_add(routing, "CALLING_PLAN"); + env_add(routing, "CALLING_PRESENT"); + env_add(routing, "CALLING_SCREEN"); + env_add(routing, "CALLING"); + } + rc = osmo_cc_get_ie_calling_interface(msg, 0, number, sizeof(number)); + if (rc >= 0) { + env.number = number; + env_add(routing, "CALLING_INTERFACE"); + } + + rc = osmo_cc_get_ie_calling(msg, 1, &type, &plan, &present, &screen, number, sizeof(number)); + if (rc >= 0) { + env.type = type; + env.plan = plan; + env.present = present; + env.screen = screen; + env.number = number; + env_add(routing, "CALLING2_TYPE"); + env_add(routing, "CALLING2_PLAN"); + env_add(routing, "CALLING2_PRESENT"); + env_add(routing, "CALLING2_SCREEN"); + env_add(routing, "CALLING2"); + } + rc = osmo_cc_get_ie_calling_network(msg, 0, &network_type, network_id, sizeof(network_id)); + if (rc >= 0) { + env.network_type = network_type; + env.network_id = network_id; + env_add(routing, "NETWORK_TYPE"); + env_add(routing, "NETWORK_ID"); + } + rc = osmo_cc_get_ie_redir(msg, 0, &type, &plan, &present, &screen, &reason, number, sizeof(number)); + if (rc >= 0) { + env.type = type; + env.plan = plan; + env.present = present; + env.screen = screen; + env.reason = reason; + env.number = number; + env_add(routing, "REDIRECTING_TYPE"); + env_add(routing, "REDIRECTING_PLAN"); + env_add(routing, "REDIRECTING_PRESENT"); + env_add(routing, "REDIRECTING_SCREEN"); + env_add(routing, "REDIRECTING_REASON"); + env_add(routing, "REDIRECTING"); + } + + rc = osmo_cc_get_ie_called(msg, 0, &type, &plan, number, sizeof(number)); + if (rc >= 0) { + env.type = type; + env.plan = plan; + env_add(routing, "DIALING_TYPE"); + env_add(routing, "DIALING_PLAN"); + } else + number[0] = 0; + /* variable must always be present, so it can be updated when overlap dialing */ + routing->envc_dialing = routing->envc; + env.number = number; + env_add(routing, "DIALING"); + + rc = osmo_cc_get_ie_keypad(msg, 0, number, sizeof(number)); + if (rc < 0) + number[0] = 0; + /* variable must always be present, so it can be updated when overlap dialing */ + routing->envc_keypad = routing->envc; + env.number = number; + env_add(routing, "KEYPAD"); + + rc = osmo_cc_get_ie_complete(msg, 0); + if (rc >= 0) + env.complete = 1; + /* variable must always be present, so it can be updated when overlap dialing */ + routing->envc_complete = routing->envc; + env_add(routing, "COMPLETE"); + + routing->envp[routing->envc++] = NULL; +} + +/* update environment with info message */ +void routing_env_dialing(routing_t *routing, char *number, char *keypad, int complete) +{ + free((char *)routing->envp[routing->envc_dialing]); + env.number = number; + env_set(routing, "DIALING", routing->envc_dialing); + + free((char *)routing->envp[routing->envc_keypad]); + env.number = keypad; + env_set(routing, "KEYPAD", routing->envc_keypad); + + free((char *)routing->envp[routing->envc_complete]); + env.complete = complete; + env_set(routing, "COMPLETE", routing->envc_complete); } static void enqueue_string(struct string_queue **queue_p, const char *string, const char *suffix) @@ -81,96 +300,6 @@ static char *dequeue_string(struct string_queue **queue_p) return string; } -/* prepare environment with setup info */ -void routing_env_msg(routing_t *routing, osmo_cc_msg_t *msg) -{ - uint8_t coding, capability, mode; - uint8_t type, plan, present, screen, reason; - uint8_t network_type; - char number[256], network_id[256]; - int rc, i; - - for (i = 0; environ[i]; i++) { - routing->envp[routing->envc++] = strdup(environ[i]); - } - - rc = osmo_cc_get_ie_bearer(msg, 0, &coding, &capability, &mode); - if (rc >= 0) { - routing->envp[routing->envc++] = env_int("BEARER_CODING", coding); - routing->envp[routing->envc++] = env_int("BEARER_CAPABILITY", capability); - routing->envp[routing->envc++] = env_int("BEARER_MODE", mode); - } - - rc = osmo_cc_get_ie_calling(msg, 0, &type, &plan, &present, &screen, number, sizeof(number)); - if (rc >= 0) { - routing->envp[routing->envc++] = env_int("CALLING_TYPE", type); - routing->envp[routing->envc++] = env_int("CALLING_PLAN", plan); - routing->envp[routing->envc++] = env_int("CALLING_PRESENT", present); - routing->envp[routing->envc++] = env_int("CALLING_SCREEN", screen); - routing->envp[routing->envc++] = env_string("CALLING", number); - } - rc = osmo_cc_get_ie_calling_interface(msg, 0, number, sizeof(number)); - if (rc >= 0) { - routing->envp[routing->envc++] = env_string("CALLING_INTERFACE", number); - } - - rc = osmo_cc_get_ie_calling(msg, 1, &type, &plan, &present, &screen, number, sizeof(number)); - if (rc >= 0) { - routing->envp[routing->envc++] = env_int("CALLING2_TYPE", type); - routing->envp[routing->envc++] = env_int("CALLING2_PLAN", plan); - routing->envp[routing->envc++] = env_int("CALLING2_PRESENT", present); - routing->envp[routing->envc++] = env_int("CALLING2_SCREEN", screen); - routing->envp[routing->envc++] = env_string("CALLING2", number); - } - rc = osmo_cc_get_ie_calling_network(msg, 0, &network_type, network_id, sizeof(network_id)); - if (rc >= 0) { - routing->envp[routing->envc++] = env_string("NETWORK_TYPE", osmo_cc_network_type_name(network_type)); - routing->envp[routing->envc++] = env_string("NETWORK_ID", network_id); - } - rc = osmo_cc_get_ie_redir(msg, 0, &type, &plan, &present, &screen, &reason, number, sizeof(number)); - if (rc >= 0) { - routing->envp[routing->envc++] = env_int("REDIRECTING_TYPE", type); - routing->envp[routing->envc++] = env_int("REDIRECTING_PLAN", plan); - routing->envp[routing->envc++] = env_int("REDIRECTING_PRESENT", present); - routing->envp[routing->envc++] = env_int("REDIRECTING_SCREEN", screen); - routing->envp[routing->envc++] = env_int("REDIRECTING_REASON", reason); - routing->envp[routing->envc++] = env_string("REDIRECTING", number); - } - - rc = osmo_cc_get_ie_called(msg, 0, &type, &plan, number, sizeof(number)); - if (rc >= 0) { - routing->envp[routing->envc++] = env_int("DIALING_TYPE", type); - routing->envp[routing->envc++] = env_int("DIALING_PLAN", plan); - } else - number[0] = 0; - /* variable must always be present, so it can be updated when overlap dialing */ - routing->envc_dialing = routing->envc; - routing->envp[routing->envc++] = env_string("DIALING", number); - - rc = osmo_cc_get_ie_keypad(msg, 0, number, sizeof(number)); - if (rc < 0) - number[0] = 0; - /* variable must always be present, so it can be updated when overlap dialing */ - routing->envc_keypad = routing->envc; - routing->envp[routing->envc++] = env_string("KEYPAD", number); - - rc = osmo_cc_get_ie_complete(msg, 0); - if (rc >= 0) - routing->envp[routing->envc++] = env_int("COMPLETE", 1); - - routing->envp[routing->envc++] = NULL; -} - -/* update environment with info message */ -void routing_env_dialing(routing_t *routing, const char *number, const char *keypad) -{ - free((char *)routing->envp[routing->envc_dialing]); - routing->envp[routing->envc_dialing] = env_string("DIALING", number); - - free((char *)routing->envp[routing->envc_keypad]); - routing->envp[routing->envc_keypad] = env_string("KEYPAD", keypad); -} - void routing_env_free(routing_t *routing) { /* remove env */ diff --git a/src/router/routing.h b/src/router/routing.h index bab5502..d5e43ca 100644 --- a/src/router/routing.h +++ b/src/router/routing.h @@ -15,6 +15,7 @@ typedef struct routing { const char *envp[256]; /* environment variables */ int envc_dialing; /* envc index for dialing number */ int envc_keypad; /* envc index for keypad */ + int envc_complete; /* envc index for complete */ pid_t script_pid; /* pid of routing script */ int script_stdin; /* pipe to stdin */ @@ -32,8 +33,9 @@ typedef struct routing { struct string_queue *stderr_queue; /* strings read from script */ } routing_t; +void env_help(void); void routing_env_msg(routing_t *routing, osmo_cc_msg_t *msg); -void routing_env_dialing(routing_t *routing, const char *number, const char *keypad); +void routing_env_dialing(routing_t *routing, char *number, char *keypad, int complete); void routing_env_free(routing_t *routing); void routing_start(routing_t *routing, const char *script, const char *shell); void routing_stop(routing_t *routing);