/* OsmoMSC - Circuit-Switched Core Network (MSC+VLR+SMSC) implementation */ /* (C) 2016-2019 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Based on OsmoNITB: * (C) 2008-2010 by Harald Welte * (C) 2009-2012 by Holger Hans Peter Freyther * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #define _GNU_SOURCE #include /* build switches from the configure script */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BUILD_IU #include #include #endif static const char * const osmomsc_copyright = "OsmoMSC - Osmocom Circuit-Switched Core Network implementation\r\n" "Copyright (C) 2016 by sysmocom s.f.m.c. GmbH \r\n" "Based on OsmoNITB:\r\n" " (C) 2008-2010 by Harald Welte \r\n" " (C) 2009-2012 by Holger Hans Peter Freyther \r\n" "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" "Dieter Spaar, Andreas Eversberg, Sylvain Munaut, Neels Hofmeyr\r\n\r\n" "License AGPLv3+: GNU AGPL version 3 or later \r\n" "This is free software: you are free to change and redistribute it.\r\n" "There is NO WARRANTY, to the extent permitted by law.\r\n"; void *tall_msc_ctx = NULL; /* satisfy deps from libbsc legacy. TODO double check these */ void *tall_map_ctx = NULL; /* end deps from libbsc legacy. */ static struct { const char *config_file; int daemonize; const char *mncc_sock_path; } msc_cmdline_config = { .config_file = "osmo-msc.cfg", }; /* timer to store statistics */ #define EXPIRE_INTERVAL 10, 0 static int quit = 0; static void print_usage() { printf("Usage: osmo-msc\n"); } static void print_help() { printf("Some useful options:\n"); printf(" -h --help This text.\n"); printf(" -d option --debug=DCC:DMM:DRR: Enable debugging.\n"); printf(" -D --daemonize Fork the process into a background daemon.\n"); printf(" -c --config-file filename The config file to use.\n"); printf(" -s --disable-color\n"); printf(" -T --timestamp Prefix every log line with a timestamp.\n"); printf(" -V --version Print the version of OsmoMSC.\n"); printf(" -e --log-level number Set a global loglevel.\n"); printf("\nVTY reference generation:\n"); printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"); printf(" --vty-ref-xml Generate the VTY reference XML output and exit.\n"); } static void handle_long_options(const char *prog_name, const int long_option) { static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT; switch (long_option) { case 1: vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg); if (vty_ref_mode < 0) { fprintf(stderr, "%s: Unknown VTY reference generation " "mode '%s'\n", prog_name, optarg); exit(2); } break; case 2: fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n", get_value_string(vty_ref_gen_mode_names, vty_ref_mode), get_value_string(vty_ref_gen_mode_desc, vty_ref_mode)); vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode); exit(0); default: fprintf(stderr, "%s: error parsing cmdline options\n", prog_name); exit(2); } } static void handle_options(int argc, char **argv) { while (1) { int option_index = 0, c; static int long_option = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"debug", 1, 0, 'd'}, {"daemonize", 0, 0, 'D'}, {"config-file", 1, 0, 'c'}, {"disable-color", 0, 0, 's'}, {"database", 1, 0, 'l'}, /* deprecated */ {"timestamp", 0, 0, 'T'}, {"version", 0, 0, 'V' }, {"log-level", 1, 0, 'e'}, {"mncc-sock-path", 1, 0, 'M'}, /* deprecated */ {"no-dbcounter", 0, 0, 'C'}, /* deprecated */ {"vty-ref-mode", 1, &long_option, 1}, {"vty-ref-xml", 0, &long_option, 2}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hd:Dsl:TVc:e:CM:", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': print_usage(); print_help(); exit(0); case 0: handle_long_options(argv[0], long_option); break; case 's': log_set_use_color(osmo_stderr_target, 0); break; case 'd': log_parse_category_mask(osmo_stderr_target, optarg); break; case 'D': msc_cmdline_config.daemonize = 1; break; case 'l': fprintf(stderr, "Command line argument '-%c' is deprecated, use VTY " "parameter 'smsc' / 'database %s' instead.\n", c, optarg); exit(2); break; case 'c': msc_cmdline_config.config_file = optarg; break; case 'T': log_set_print_timestamp(osmo_stderr_target, 1); break; case 'e': log_set_log_level(osmo_stderr_target, atoi(optarg)); break; case 'M': msc_cmdline_config.mncc_sock_path = optarg; fprintf(stderr, "Command line argument '-%c' is deprecated, use VTY " "parameter 'msc' / 'mncc external %s' instead.\n", c, optarg); break; case 'C': fprintf(stderr, "-C is deprecated and does nothing."); break; case 'V': print_version(1); exit(0); break; default: /* catch unknown options *as well as* missing arguments. */ fprintf(stderr, "Error in command line options. Exiting.\n"); exit(-1); } } if (argc > optind) { fprintf(stderr, "Unsupported positional arguments on command line\n"); exit(2); } } static struct gsm_network *msc_network_alloc(void *ctx, mncc_recv_cb_t mncc_recv) { struct gsm_network *net = gsm_network_init(ctx, mncc_recv); if (!net) return NULL; net->name_long = talloc_strdup(net, "OsmoMSC"); net->name_short = talloc_strdup(net, "OsmoMSC"); net->gsup_server_addr_str = talloc_strdup(net, MSC_HLR_REMOTE_IP_DEFAULT); net->gsup_server_port = MSC_HLR_REMOTE_PORT_DEFAULT; net->mgw.mgw_pool = mgcp_client_pool_alloc(net); net->mgw.conf = mgcp_client_conf_alloc(net); net->call_waiting = true; net->lcls_permitted = false; net->mgw.tdefs = g_mgw_tdefs; osmo_tdefs_reset(net->mgw.tdefs); net->sms_queue_cfg = sms_queue_cfg_alloc(ctx); return net; } void msc_network_shutdown(struct gsm_network *net) { /* nothing here yet */ } static struct gsm_network *msc_network = NULL; extern void *tall_vty_ctx; static void signal_handler(int signum) { fprintf(stdout, "signal %u received\n", signum); switch (signum) { case SIGINT: case SIGTERM: LOGP(DMSC, LOGL_NOTICE, "Terminating due to signal %d\n", signum); quit++; break; case SIGABRT: osmo_generate_backtrace(); /* in case of abort, we want to obtain a talloc report and * then run default SIGABRT handler, who will generate coredump * and abort the process. abort() should do this for us after we * return, but program wouldn't exit if an external SIGABRT is * received. */ talloc_report(tall_vty_ctx, stderr); talloc_report_full(tall_msc_ctx, stderr); signal(SIGABRT, SIG_DFL); raise(SIGABRT); break; case SIGUSR1: talloc_report(tall_vty_ctx, stderr); talloc_report_full(tall_msc_ctx, stderr); break; case SIGUSR2: talloc_report_full(tall_vty_ctx, stderr); break; default: break; } } static int msc_vty_go_parent(struct vty *vty) { switch (vty->node) { case GSMNET_NODE: vty->node = CONFIG_NODE; vty->index = NULL; break; case MGW_NODE: OSMO_ASSERT(msc_network != NULL); vty->node = GSMNET_NODE; vty->index = msc_network; break; case SMPP_ESME_NODE: vty->node = SMPP_NODE; vty->index = NULL; break; case SMPP_NODE: case MSC_NODE: case MNCC_INT_NODE: case ASCI_NODE: vty->node = CONFIG_NODE; vty->index = NULL; break; case GCR_NODE: vty->node = ASCI_NODE; vty->index = NULL; break; case VGC_NODE: case VBC_NODE: vty->node = GCR_NODE; vty->index = NULL; break; case SUBSCR_NODE: vty->node = ENABLE_NODE; vty->index = NULL; break; default: osmo_ss7_vty_go_parent(vty); } return vty->node; } static int msc_vty_is_config_node(struct vty *vty, int node) { /* Check if libosmo-sigtran declares the node in * question as config node */ if (osmo_ss7_is_config_node(vty, node)) return 1; switch (node) { /* add items that are not config */ case SUBSCR_NODE: case CONFIG_NODE: return 0; default: return 1; } } static struct vty_app_info msc_vty_info = { .name = "OsmoMSC", .version = PACKAGE_VERSION, .go_parent_cb = msc_vty_go_parent, .is_config_node = msc_vty_is_config_node, }; #define DEFAULT_M3UA_LOCAL_IP "localhost" #define DEFAULT_M3UA_REMOTE_IP "localhost" #define DEFAULT_PC "0.23.1" static struct osmo_sccp_instance *sccp_setup(void *ctx, uint32_t cs7_instance, const char *label, const char *default_pc_str) { int default_pc = osmo_ss7_pointcode_parse(NULL, default_pc_str); if (default_pc < 0) return NULL; return osmo_sccp_simple_client_on_ss7_id(ctx, cs7_instance, label, default_pc, OSMO_SS7_ASP_PROT_M3UA, 0, DEFAULT_M3UA_LOCAL_IP, /* local: use arbitrary port and 0.0.0.0. */ 0, /* remote: use protocol default port */ DEFAULT_M3UA_REMOTE_IP); /* Note: If a differing remote IP is to be used, it was already entered in the vty config at * 'cs7' / 'asp' / 'remote-ip', and this default remote IP has no effect. * Similarly, 'cs7' / 'listen' can specify the local IP address. */ } static int ss7_setup(void *ctx, struct osmo_sccp_instance **sccp_a, struct osmo_sccp_instance **sccp_iu) { uint32_t i_a = msc_network->a.cs7_instance; uint32_t i_iu = msc_network->iu.cs7_instance; const char *name_a = "OsmoMSC-A"; const char *name_iu = NULL; #if BUILD_IU if (i_a == i_iu) name_a = name_iu = "OsmoMSC-A-Iu"; else name_iu = "OsmoMSC-Iu"; #endif *sccp_a = sccp_setup(ctx, i_a, name_a, DEFAULT_PC); if (!*sccp_a) return -EINVAL; if (!name_iu) { *sccp_iu = NULL; return 0; } if (i_a == i_iu) { *sccp_iu = *sccp_a; return 0; } *sccp_iu = sccp_setup(ctx, i_iu, name_iu, DEFAULT_PC); if (!*sccp_iu) return -EINVAL; return 0; } static const struct log_info_cat msc_default_categories[] = { [DRLL] = { .name = "DRLL", .description = "A-bis Radio Link Layer (RLL)", .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCC] = { .name = "DCC", .description = "Layer3 Call Control (CC)", .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DBCC] = { .name = "DBCC", .description = "Layer3 Broadcast Call Control (BCC)", .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DGCC] = { .name = "DGCC", .description = "Layer3 Group Call Control (GCC)", .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMM] = { .name = "DMM", .description = "Layer3 Mobility Management (MM)", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRR] = { .name = "DRR", .description = "Layer3 Radio Resource (RR)", .color = "\033[1;34m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMNCC] = { .name = "DMNCC", .description = "MNCC API for Call Control application", .color = "\033[1;39m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DPAG] = { .name = "DPAG", .description = "Paging Subsystem", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMSC] = { .name = "DMSC", .description = "Mobile Switching Center", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMGCP] = { .name = "DMGCP", .description = "Media Gateway Control Protocol", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DHO] = { .name = "DHO", .description = "Hand-Over", .color = "\033[1;38m", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DDB] = { .name = "DDB", .description = "Database Layer", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DREF] = { .name = "DREF", .description = "Reference Counting", .enabled = 0, .loglevel = LOGL_NOTICE, }, [DCTRL] = { .name = "DCTRL", .description = "Control interface", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSMPP] = { .name = "DSMPP", .description = "SMPP interface for external SMS apps", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRANAP] = { .name = "DRANAP", .description = "Radio Access Network Application Part Protocol", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DVLR] = { .name = "DVLR", .description = "Visitor Location Register", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DIUCS] = { .name = "DIUCS", .description = "Iu-CS Protocol", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DBSSAP] = { .name = "DBSSAP", .description = "BSSAP Protocol (A Interface)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSGS] = { .name = "DSGS", .description = "SGs Interface (SGsAP)", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSS] = { .name = "DSS", .description = "Supplementary Services", .enabled = 1, .loglevel = LOGL_NOTICE, }, [DASCI] = { .name = "DASCI", .description = "Advanced Speech Call Items", .enabled = 1, .loglevel = LOGL_NOTICE, }, }; static int filter_fn(const struct log_context *ctx, struct log_target *tar) { const struct vlr_subscr *vsub = ctx->ctx[LOG_CTX_VLR_SUBSCR]; if ((tar->filter_map & (1 << LOG_FLT_VLR_SUBSCR)) != 0 && vsub && vsub == tar->filter_data[LOG_FLT_VLR_SUBSCR]) return 1; return 0; } const struct log_info log_info = { .filter_fn = filter_fn, .cat = msc_default_categories, .num_cat = ARRAY_SIZE(msc_default_categories), }; extern void *tall_gsms_ctx; extern void *tall_call_ctx; extern void *tall_trans_ctx; static int msc_mgw_setup(void) { struct mgcp_client *mgcp_client_single; unsigned int pool_members_initalized; /* Initialize MGW pool. This initalizes and connects all MGCP clients that are currently configured in * the pool. Adding additional MGCP clients to the pool is possible but the user has to configure and * (re)connect them manually from the VTY. */ if (!mgcp_client_pool_empty(msc_network->mgw.mgw_pool)) { pool_members_initalized = mgcp_client_pool_connect(msc_network->mgw.mgw_pool); if (!pool_members_initalized) { LOGP(DMSC, LOGL_ERROR, "MGW pool failed to initialize any pool members\n"); return -EINVAL; } LOGP(DMSC, LOGL_NOTICE, "MGW pool with %u pool members configured, (ignoring MGW configuration in VTY node 'msc').\n", pool_members_initalized); return 0; } /* Initialize and connect a single MGCP client. This MGCP client will appear as the one and only pool * member if there is no MGW pool configured. */ LOGP(DMSC, LOGL_NOTICE, "No MGW pool configured, using MGW configuration in VTY node 'msc'\n"); mgcp_client_single = mgcp_client_init(msc_network, msc_network->mgw.conf); if (!mgcp_client_single) { LOGP(DMSC, LOGL_ERROR, "MGW (single) client initalization failed\n"); return -EINVAL; } if (mgcp_client_connect(mgcp_client_single)) { LOGP(DMSC, LOGL_ERROR, "MGW (single) connect failed at (%s:%u)\n", msc_network->mgw.conf->remote_addr, msc_network->mgw.conf->remote_port); return -EINVAL; } mgcp_client_pool_register_single(msc_network->mgw.mgw_pool, mgcp_client_single); return 0; } int main(int argc, char **argv) { int rc; int ret = 0; struct osmo_sccp_instance *sccp_a; struct osmo_sccp_instance *sccp_iu; const struct osmo_ss7_instance *ss7; /* Track the use of talloc NULL memory contexts */ talloc_enable_null_tracking(); osmo_fsm_set_dealloc_ctx(OTC_SELECT); msc_vty_info.copyright = osmomsc_copyright; tall_msc_ctx = talloc_named_const(NULL, 1, "osmo_msc"); msc_vty_info.tall_ctx = tall_msc_ctx; msgb_talloc_ctx_init(tall_msc_ctx, 0); osmo_signal_talloc_ctx_init(tall_msc_ctx); tall_gsms_ctx = talloc_named_const(tall_msc_ctx, 0, "sms"); tall_call_ctx = talloc_named_const(tall_msc_ctx, 0, "gsm_call"); tall_trans_ctx = talloc_named_const(tall_msc_ctx, 0, "transaction"); osmo_init_logging2(tall_msc_ctx, &log_info); osmo_fsm_log_timeouts(true); osmo_fsm_log_addr(true); osmo_stats_init(tall_msc_ctx); rate_ctr_init(tall_msc_ctx); /* For --version, vty_init() must be called before handling options */ vty_init(&msc_vty_info); OSMO_ASSERT(osmo_ss7_init() == 0); osmo_ss7_vty_init_asp(tall_msc_ctx); osmo_sccp_vty_init(); ctrl_vty_init(tall_msc_ctx); logging_vty_add_cmds(); osmo_talloc_vty_add_cmds(); osmo_cpu_sched_vty_init(tall_msc_ctx); /* Allocate global gsm_network struct. * At first set the internal MNCC as default, may be changed below according to cfg or cmdline option. */ msc_network = msc_network_alloc(tall_msc_ctx, int_mncc_recv); if (!msc_network) return -ENOMEM; msc_vty_init(msc_network); /* Parse options */ handle_options(argc, argv); call_leg_init(msc_network); mncc_call_fsm_init(msc_network); if (msc_vlr_alloc(msc_network)) { fprintf(stderr, "Failed to allocate VLR\n"); exit(1); } #ifdef BUILD_SMPP if (smpp_msc_alloc_init(tall_msc_ctx) < 0) return -1; #endif sgs_iface_init(tall_msc_ctx, msc_network); rc = vty_read_config_file(msc_cmdline_config.config_file, NULL); if (rc < 0) { LOGP(DMSC, LOGL_FATAL, "Failed to parse the config file: '%s'\n", msc_cmdline_config.config_file); return 1; } /* Initialize MNCC socket if appropriate. If the cmdline option -M is present, it overrides the .cfg file * setting 'msc' / 'mncc external MNCC_SOCKET_PATH'. Note that when -M is given, it "bleeds" back into the vty * 'write' command and is reflected in the written out 'mncc external' cfg. */ if (msc_cmdline_config.mncc_sock_path) { LOGP(DMNCC, LOGL_NOTICE, "MNCC socket path is configured from commandline argument -M." " This affects a written-back config file. Instead consider using the config file directly" " ('msc' / 'mncc external MNCC_SOCKET_PATH').\n"); gsm_network_set_mncc_sock_path(msc_network, msc_cmdline_config.mncc_sock_path); } if (msc_network->mncc_sock_path) { msc_network->mncc_recv = mncc_sock_from_cc; rc = mncc_sock_init(msc_network, msc_network->mncc_sock_path); if (rc) { fprintf(stderr, "MNCC socket initialization failed. exiting.\n"); exit(1); } } else DEBUGP(DMNCC, "Using internal MNCC handler.\n"); /* start telnet after reading config for vty_get_bind_addr() */ rc = telnet_init_default(tall_msc_ctx, &msc_network, OSMO_VTY_PORT_MSC); if (rc < 0) return 2; /* BSC stuff is to be split behind an A-interface to be used with * OsmoBSC, but there is no need to remove it yet. Most of the * following code until iu_init() is legacy. */ #ifdef BUILD_SMPP smpp_msc_start(msc_network); #endif /* start control interface after reading config for * ctrl_vty_get_bind_addr() */ msc_network->ctrl = ctrl_interface_setup(msc_network, OSMO_CTRL_PORT_MSC, NULL); if (!msc_network->ctrl) { fprintf(stderr, "Failed to initialize control interface. Exiting.\n"); return -1; } #if 0 TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_install(). if (bsc_base_ctrl_cmds_install() != 0) { fprintf(stderr, "Failed to initialize the BSC control commands.\n"); return -1; } #endif if (msc_ctrl_cmds_install(msc_network) != 0) { fprintf(stderr, "Failed to initialize the MSC control commands.\n"); return -1; } /* seed the PRNG */ srand(time(NULL)); /* TODO: is this used for crypto?? Improve randomness, at least we * should try to use the nanoseconds part of the current time. */ if (msc_gsup_client_start(msc_network)) { fprintf(stderr, "Failed to start GSUP client\n"); exit(1); } msc_a_i_t_gsup_init(msc_network); if (msc_vlr_start(msc_network)) { fprintf(stderr, "Failed to start VLR\n"); exit(1); } signal(SIGINT, &signal_handler); signal(SIGTERM, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); /* start the SMS queue */ if (sms_queue_start(msc_network) != 0) { ret = -1; goto error; } if (msc_mgw_setup() != 0) { ret = 7; goto error; } if (ss7_setup(tall_msc_ctx, &sccp_a, &sccp_iu)) { fprintf(stderr, "Setting up SCCP client failed.\n"); ret = 8; goto error; } if (sgs_server_open(g_sgs)) { fprintf(stderr, "Starting SGs server failed\n"); ret = 9; goto error; } msc_network->a.sri = sccp_ran_init(msc_network, sccp_a, OSMO_SCCP_SSN_BSSAP, "OsmoMSC-A", &msc_ran_infra[OSMO_RAT_GERAN_A], msc_network); if (!msc_network->a.sri) { fprintf(stderr, "Setting up A receiver failed\n"); ret = 10; goto error; } ss7 = osmo_sccp_get_ss7(msc_network->a.sri->sccp); OSMO_ASSERT(ss7); LOGP(DMSC, LOGL_NOTICE, "A-interface: SCCP user %s, cs7-instance %u (%s)\n", osmo_sccp_user_name(msc_network->a.sri->scu), osmo_ss7_instance_get_id(ss7), osmo_ss7_instance_get_name(ss7)); #ifdef BUILD_IU talloc_asn1_ctx = talloc_named_const(tall_msc_ctx, 0, "asn1"); msc_network->iu.sri = sccp_ran_init(msc_network, sccp_iu, OSMO_SCCP_SSN_RANAP, "OsmoMSC-IuCS", &msc_ran_infra[OSMO_RAT_UTRAN_IU], msc_network); if (!msc_network->iu.sri) { fprintf(stderr, "Setting up IuCS receiver failed\n"); ret = 11; goto error; } /* Compatibility with legacy osmo-hnbgw that was unable to properly handle RESET messages. */ msc_network->iu.sri->ignore_missing_reset = true; ss7 = osmo_sccp_get_ss7(msc_network->iu.sri->sccp); LOGP(DMSC, LOGL_NOTICE, "Iu-interface: SCCP user %s, cs7-instance %u (%s)\n", osmo_sccp_user_name(msc_network->iu.sri->scu), osmo_ss7_instance_get_id(ss7), osmo_ss7_instance_get_name(ss7)); #endif /* Init RRLP handlers */ msc_rrlp_init(); if (msc_cmdline_config.daemonize) { rc = osmo_daemonize(); if (rc < 0) { perror("Error during daemonize"); ret = 6; goto error; } } do { log_reset_context(); osmo_select_main_ctx(0); /* If the user hits Ctrl-C the third time, just terminate immediately. */ if (quit >= 3) break; /* Has SIGTERM been received (and not yet been handled)? */ if (quit && !osmo_select_shutdown_requested()) { msc_network_shutdown(msc_network); osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); /* Request write-only mode in osmo_select_main_ctx() */ osmo_select_shutdown_request(); /* continue the main select loop until all write queues are serviced. */ } } while (!osmo_select_shutdown_done()); error: db_fini(); log_fini(); /** * Report the heap state of root context, then free, * so both ASAN and Valgrind are happy... */ talloc_report_full(tall_msc_ctx, stderr); talloc_free(tall_msc_ctx); /* FIXME: VTY code still uses NULL-context */ talloc_free(tall_vty_ctx); /** * Report the heap state of NULL context, then free, * so both ASAN and Valgrind are happy... */ talloc_report_full(NULL, stderr); talloc_disable_null_tracking(); return ret; }