/* Plugin implementation */ int janus_videocall_init(janus_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_VIDEOCALL_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { janus_config_print(config); janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events"); if(events != NULL && events->value != NULL) notify_events = janus_is_true(events->value); if(!notify_events && callback->events_is_enabled()) { JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_VIDEOCALL_NAME); } } janus_config_destroy(config); config = NULL; sessions = g_hash_table_new(g_str_hash, g_str_equal); janus_mutex_init(&sessions_mutex); messages = g_async_queue_new_full((GDestroyNotify) janus_videocall_message_free); /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; g_atomic_int_set(&initialized, 1); GError *error = NULL; /* Start the sessions watchdog */ watchdog = g_thread_try_new("videocall watchdog", &janus_videocall_watchdog, NULL, &error); if(error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall watchdog thread...\n", error->code, error->message ? error->message : "??"); return -1; } /* Launch the thread that will handle incoming messages */ handler_thread = g_thread_try_new("videocall handler", janus_videocall_handler, NULL, &error); if(error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the VideoCall handler thread...\n", error->code, error->message ? error->message : "??"); return -1; } JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOCALL_NAME); return 0; }
/* Transport implementation */ int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI JANUS_LOG(LOG_INFO, "libwebsockets >= 1.6 available, using new API\n"); #else JANUS_LOG(LOG_INFO, "libwebsockets < 1.6 available, using old API\n"); #endif /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { janus_config_print(config); /* Handle configuration */ janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json"); if(item && item->value) { /* Check how we need to format/serialize the JSON output */ if(!strcasecmp(item->value, "indented")) { /* Default: indented, we use three spaces for that */ json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "plain")) { /* Not indented and no new lines, but still readable */ json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "compact")) { /* Compact, so no spaces between separators */ json_format = JSON_COMPACT | JSON_PRESERVE_ORDER; } else { JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value); json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } } /* Check if we need to send events to handlers */ janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events"); if(events != NULL && events->value != NULL) notify_events = janus_is_true(events->value); if(!notify_events && callback->events_is_enabled()) { JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_WEBSOCKETS_NAME); } item = janus_config_get_item_drilldown(config, "general", "ws_logging"); if(item && item->value) { ws_log_level = atoi(item->value); if(ws_log_level < 0) ws_log_level = 0; } JANUS_LOG(LOG_VERB, "libwebsockets logging: %d\n", ws_log_level); lws_set_log_level(ws_log_level, NULL); old_wss = NULL; janus_mutex_init(&old_wss_mutex); /* Any ACL for either the Janus or Admin API? */ item = janus_config_get_item_drilldown(config, "general", "ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), FALSE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), TRUE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } /* Setup the Janus API WebSockets server(s) */ item = janus_config_get_item_drilldown(config, "general", "ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "WebSockets server disabled\n"); } else { int wsport = 8188; item = janus_config_get_item_drilldown(config, "general", "ws_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "general", "ws_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "general", "ws_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = wss_protocols; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI info.extensions = NULL; #else info.extensions = libwebsocket_get_internal_extensions(); #endif info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI wss = lws_create_context(&info); #else wss = libwebsocket_create_context(&info); #endif if(wss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n"); } else { JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport); } g_free(ip); } item = janus_config_get_item_drilldown(config, "general", "wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n"); } else { int wsport = 8989; item = janus_config_get_item_drilldown(config, "general", "wss_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "general", "wss_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "general", "wss_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = swss_protocols; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI info.extensions = NULL; #else info.extensions = libwebsocket_get_internal_extensions(); #endif info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.gid = -1; info.uid = -1; #if LWS_LIBRARY_VERSION_MAJOR >= 2 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #else info.options = 0; #endif /* Create the secure WebSocket context */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI swss = lws_create_context(&info); #else swss = libwebsocket_create_context(&info); #endif if(swss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n"); } else { JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport); } g_free(ip); } } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n"); } else { int wsport = 7188; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = admin_wss_protocols; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI info.extensions = NULL; #else info.extensions = libwebsocket_get_internal_extensions(); #endif info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI admin_wss = lws_create_context(&info); #else admin_wss = libwebsocket_create_context(&info); #endif if(admin_wss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n"); } else { JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport); } g_free(ip); } item = janus_config_get_item_drilldown(config, "admin", "admin_wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n"); } else { int wsport = 7989; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = admin_swss_protocols; #ifdef HAVE_LIBWEBSOCKETS_NEWAPI info.extensions = NULL; #else info.extensions = libwebsocket_get_internal_extensions(); #endif info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.gid = -1; info.uid = -1; #if LWS_LIBRARY_VERSION_MAJOR >= 2 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #else info.options = 0; #endif /* Create the secure WebSocket context */ #ifdef HAVE_LIBWEBSOCKETS_NEWAPI admin_swss = lws_create_context(&info); #else admin_swss = libwebsocket_create_context(&info); #endif if(admin_swss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsockets...\n"); } else { JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport); } g_free(ip); } } } janus_config_destroy(config); config = NULL; if(!wss && !swss && !admin_wss && !admin_swss) { JANUS_LOG(LOG_FATAL, "No WebSockets server started, giving up...\n"); return -1; /* No point in keeping the plugin loaded */ } wss_janus_api_enabled = wss || swss; wss_admin_api_enabled = admin_wss || admin_swss; GError *error = NULL; /* Start the WebSocket service threads */ if(wss != NULL) { wss_thread = g_thread_try_new("ws thread", &janus_websockets_thread, wss, &error); if(!wss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(swss != NULL) { swss_thread = g_thread_try_new("sws thread", &janus_websockets_thread, swss, &error); if(!swss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(admin_wss != NULL) { admin_wss_thread = g_thread_try_new("admin ws thread", &janus_websockets_thread, admin_wss, &error); if(!admin_wss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Admin WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(admin_swss != NULL) { admin_swss_thread = g_thread_try_new("admin sws thread", &janus_websockets_thread, admin_swss, &error); if(!admin_swss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure Admin WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } /* Done */ g_atomic_int_set(&initialized, 1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME); return 0; }
/* Transport implementation */ int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } #ifndef LWS_WITH_IPV6 JANUS_LOG(LOG_WARN, "libwebsockets has been built without IPv6 support, will bind to IPv4 only\n"); #endif /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; /* Prepare the common context */ struct lws_context_creation_info wscinfo; memset(&wscinfo, 0, sizeof wscinfo); wscinfo.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS; /* We use vhosts on the same context to address both APIs, secure or not */ struct lws_vhost *wss = NULL, *swss = NULL, *admin_wss = NULL, *admin_swss = NULL; /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { janus_config_print(config); /* Handle configuration */ janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json"); if(item && item->value) { /* Check how we need to format/serialize the JSON output */ if(!strcasecmp(item->value, "indented")) { /* Default: indented, we use three spaces for that */ json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "plain")) { /* Not indented and no new lines, but still readable */ json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "compact")) { /* Compact, so no spaces between separators */ json_format = JSON_COMPACT | JSON_PRESERVE_ORDER; } else { JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value); json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } } /* Check if we need to send events to handlers */ janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events"); if(events != NULL && events->value != NULL) notify_events = janus_is_true(events->value); if(!notify_events && callback->events_is_enabled()) { JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_WEBSOCKETS_NAME); } item = janus_config_get_item_drilldown(config, "general", "ws_logging"); if(item && item->value) { /* libwebsockets uses a mask to set log levels, as documented here: * https://libwebsockets.org/lws-api-doc-master/html/group__log.html */ if(strstr(item->value, "none")) { /* Disable libwebsockets logging completely (the default) */ } else if(strstr(item->value, "all")) { /* Enable all libwebsockets logging */ ws_log_level = LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY | LLL_USER | LLL_COUNT; } else { /* Only enable some of the properties */ if(strstr(item->value, "err")) ws_log_level |= LLL_ERR; if(strstr(item->value, "warn")) ws_log_level |= LLL_WARN; if(strstr(item->value, "notice")) ws_log_level |= LLL_NOTICE; if(strstr(item->value, "info")) ws_log_level |= LLL_INFO; if(strstr(item->value, "debug")) ws_log_level |= LLL_DEBUG; if(strstr(item->value, "parser")) ws_log_level |= LLL_PARSER; if(strstr(item->value, "header")) ws_log_level |= LLL_HEADER; if(strstr(item->value, "ext")) ws_log_level |= LLL_EXT; if(strstr(item->value, "client")) ws_log_level |= LLL_CLIENT; if(strstr(item->value, "latency")) ws_log_level |= LLL_LATENCY; if(strstr(item->value, "user")) ws_log_level |= LLL_USER; if(strstr(item->value, "count")) ws_log_level |= LLL_COUNT; } } JANUS_LOG(LOG_INFO, "libwebsockets logging: %d\n", ws_log_level); lws_set_log_level(ws_log_level, janus_websockets_log_emit_function); /* Any ACL for either the Janus or Admin API? */ item = janus_config_get_item_drilldown(config, "general", "ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), FALSE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), TRUE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } /* Check if we need to enable the transport level ping/pong mechanism */ int pingpong_trigger = 0, pingpong_timeout = 0; item = janus_config_get_item_drilldown(config, "general", "pingpong_trigger"); if(item && item->value) { #if LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 1 pingpong_trigger = atoi(item->value); if(pingpong_trigger < 0) { JANUS_LOG(LOG_WARN, "Invalid value for pingpong_trigger (%d), ignoring...\n", pingpong_trigger); pingpong_trigger = 0; } #else JANUS_LOG(LOG_WARN, "WebSockets ping/pong only supported in libwebsockets >= 2.1\n"); #endif } item = janus_config_get_item_drilldown(config, "general", "pingpong_timeout"); if(item && item->value) { #if LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 1 pingpong_timeout = atoi(item->value); if(pingpong_timeout < 0) { JANUS_LOG(LOG_WARN, "Invalid value for pingpong_timeout (%d), ignoring...\n", pingpong_timeout); pingpong_timeout = 0; } #else JANUS_LOG(LOG_WARN, "WebSockets ping/pong only supported in libwebsockets >= 2.1\n"); #endif } if((pingpong_trigger && !pingpong_timeout) || (!pingpong_trigger && pingpong_timeout)) { JANUS_LOG(LOG_WARN, "pingpong_trigger and pingpong_timeout not both set, ignoring...\n"); } #if LWS_LIBRARY_VERSION_MAJOR >= 2 && LWS_LIBRARY_VERSION_MINOR >= 1 if(pingpong_trigger > 0 && pingpong_timeout > 0) { wscinfo.ws_ping_pong_interval = pingpong_trigger; wscinfo.timeout_secs = pingpong_timeout; } #endif /* Force single-thread server */ wscinfo.count_threads = 1; /* Create the base context */ wsc = lws_create_context(&wscinfo); if(wsc == NULL) { JANUS_LOG(LOG_ERR, "Error creating libwebsockets context...\n"); janus_config_destroy(config); return -1; /* No point in keeping the plugin loaded */ } /* Setup the Janus API WebSockets server(s) */ item = janus_config_get_item_drilldown(config, "general", "ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "WebSockets server disabled\n"); } else { int wsport = 8188; item = janus_config_get_item_drilldown(config, "general", "ws_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "general", "ws_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "general", "ws_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = ws_protocols; info.extensions = NULL; info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.ssl_private_key_password = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ wss = lws_create_vhost(wsc, &info); if(wss == NULL) { JANUS_LOG(LOG_FATAL, "Error creating vhost for WebSockets server...\n"); } else { JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport); } g_free(ip); } item = janus_config_get_item_drilldown(config, "general", "wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n"); } else { int wsport = 8989; item = janus_config_get_item_drilldown(config, "general", "wss_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "general", "wss_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "general", "wss_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; char *password = NULL; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_pwd"); if(item && item->value) password = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = sws_protocols; info.extensions = NULL; info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.ssl_private_key_password = password; info.gid = -1; info.uid = -1; #if LWS_LIBRARY_VERSION_MAJOR >= 2 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #else info.options = 0; #endif /* Create the secure WebSocket context */ swss = lws_create_vhost(wsc, &info); if(swss == NULL) { JANUS_LOG(LOG_FATAL, "Error creating vhost for Secure WebSockets server...\n"); } else { JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport); } g_free(ip); } } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n"); } else { int wsport = 7188; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = admin_ws_protocols; info.extensions = NULL; info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.ssl_private_key_password = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ admin_wss = lws_create_vhost(wsc, &info); if(admin_wss == NULL) { JANUS_LOG(LOG_FATAL, "Error creating vhost for Admin WebSockets server...\n"); } else { JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport); } g_free(ip); } item = janus_config_get_item_drilldown(config, "admin", "admin_wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n"); } else { int wsport = 7989; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port"); if(item && item->value) wsport = atoi(item->value); char *interface = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_interface"); if(item && item->value) interface = (char *)item->value; char *ip = NULL; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_ip"); if(item && item->value) { ip = (char *)item->value; char *iface = janus_websockets_get_interface_name(ip); if(iface == NULL) { JANUS_LOG(LOG_WARN, "No interface associated with %s? Falling back to no interface...\n", ip); } ip = iface; } item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; char *password = NULL; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_pwd"); if(item && item->value) password = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = ip ? ip : interface; info.protocols = admin_sws_protocols; info.extensions = NULL; info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.ssl_private_key_password = password; info.gid = -1; info.uid = -1; #if LWS_LIBRARY_VERSION_MAJOR >= 2 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #else info.options = 0; #endif /* Create the secure WebSocket context */ admin_swss = lws_create_vhost(wsc, &info); if(admin_swss == NULL) { JANUS_LOG(LOG_FATAL, "Error creating vhost for Secure Admin WebSockets server...\n"); } else { JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport); } g_free(ip); } } } janus_config_destroy(config); config = NULL; if(!wss && !swss && !admin_wss && !admin_swss) { JANUS_LOG(LOG_WARN, "No WebSockets server started, giving up...\n"); lws_context_destroy(wsc); return -1; /* No point in keeping the plugin loaded */ } ws_janus_api_enabled = wss || swss; ws_admin_api_enabled = admin_wss || admin_swss; g_atomic_int_set(&initialized, 1); GError *error = NULL; /* Start the WebSocket service thread */ if(ws_janus_api_enabled || ws_admin_api_enabled) { ws_thread = g_thread_try_new("ws thread", &janus_websockets_thread, wsc, &error); if(!ws_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } /* Done */ JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME); return 0; }
/* Transport implementation */ int janus_pfunix_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_PFUNIX_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { /* Handle configuration */ janus_config_print(config); janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json"); if(item && item->value) { /* Check how we need to format/serialize the JSON output */ if(!strcasecmp(item->value, "indented")) { /* Default: indented, we use three spaces for that */ json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "plain")) { /* Not indented and no new lines, but still readable */ json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "compact")) { /* Compact, so no spaces between separators */ json_format = JSON_COMPACT | JSON_PRESERVE_ORDER; } else { JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value); json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } } /* Check if we need to send events to handlers */ janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events"); if(events != NULL && events->value != NULL) notify_events = janus_is_true(events->value); if(!notify_events && callback->events_is_enabled()) { JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_PFUNIX_NAME); } /* First of all, initialize the socketpair for writeable notifications */ if(socketpair(PF_LOCAL, SOCK_STREAM, 0, write_fd) < 0) { JANUS_LOG(LOG_FATAL, "Error creating socket pair for writeable events: %d, %s\n", errno, strerror(errno)); return -1; } /* Setup the Janus API Unix Sockets server(s) */ item = janus_config_get_item_drilldown(config, "general", "enabled"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Unix Sockets server disabled (Janus API)\n"); } else { item = janus_config_get_item_drilldown(config, "general", "path"); char *pfname = (char *)(item && item->value ? item->value : NULL); item = janus_config_get_item_drilldown(config, "general", "type"); const char *type = item && item->value ? item->value : "SOCK_SEQPACKET"; dgram = FALSE; if(!strcasecmp(type, "SOCK_SEQPACKET")) { dgram = FALSE; } else if(!strcasecmp(type, "SOCK_DGRAM")) { dgram = TRUE; } else { JANUS_LOG(LOG_WARN, "Unknown type %s, assuming SOCK_SEQPACKET\n", type); type = "SOCK_SEQPACKET"; } if(pfname == NULL) { JANUS_LOG(LOG_WARN, "No path configured, skipping Unix Sockets server (Janus API)\n"); } else { JANUS_LOG(LOG_INFO, "Configuring %s Unix Sockets server (Janus API)\n", type); pfd = janus_pfunix_create_socket(pfname, dgram); } } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_enabled"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Unix Sockets server disabled (Admin API)\n"); } else { item = janus_config_get_item_drilldown(config, "admin", "admin_path"); char *pfname = (char *)(item && item->value ? item->value : NULL); item = janus_config_get_item_drilldown(config, "admin", "admin_type"); const char *type = item && item->value ? item->value : "SOCK_SEQPACKET"; if(!strcasecmp(type, "SOCK_SEQPACKET")) { admin_dgram = FALSE; } else if(!strcasecmp(type, "SOCK_DGRAM")) { admin_dgram = TRUE; } else { JANUS_LOG(LOG_WARN, "Unknown type %s, assuming SOCK_SEQPACKET\n", type); type = "SOCK_SEQPACKET"; } if(pfname == NULL) { JANUS_LOG(LOG_WARN, "No path configured, skipping Unix Sockets server (Admin API)\n"); } else { JANUS_LOG(LOG_INFO, "Configuring %s Unix Sockets server (Admin API)\n", type); admin_pfd = janus_pfunix_create_socket(pfname, admin_dgram); } } } janus_config_destroy(config); config = NULL; if(pfd < 0 && admin_pfd < 0) { JANUS_LOG(LOG_WARN, "No Unix Sockets server started, giving up...\n"); return -1; /* No point in keeping the plugin loaded */ } /* Create a couple of hashtables for all clients */ clients = g_hash_table_new(NULL, NULL); clients_by_fd = g_hash_table_new(NULL, NULL); clients_by_path = g_hash_table_new(g_str_hash, g_str_equal); janus_mutex_init(&clients_mutex); /* Start the Unix Sockets service thread */ GError *error = NULL; pfunix_thread = g_thread_try_new("pfunix thread", &janus_pfunix_thread, NULL, &error); if(!pfunix_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Unix Sockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } /* Done */ g_atomic_int_set(&initialized, 1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_PFUNIX_NAME); return 0; }
/* Plugin implementation */ int janus_sampleevh_init(const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(config_path == NULL) { /* Invalid arguments */ return -1; } /* Read configuration */ gboolean enabled = FALSE; char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_SAMPLEEVH_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { /* Handle configuration */ janus_config_print(config); /* Setup the sample event handler, if required */ janus_config_item *item = janus_config_get_item_drilldown(config, "general", "enabled"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Sample event handler disabled (Janus API)\n"); } else { /* Backend to send events to */ item = janus_config_get_item_drilldown(config, "general", "backend"); if(!item || !item->value || strstr(item->value, "http") != item->value) { JANUS_LOG(LOG_WARN, "Missing or invalid backend\n"); } else { backend = g_strdup(item->value); /* Any credentials needed? */ item = janus_config_get_item_drilldown(config, "general", "backend_user"); backend_user = (item && item->value) ? g_strdup(item->value) : NULL; item = janus_config_get_item_drilldown(config, "general", "backend_pwd"); backend_pwd = (item && item->value) ? g_strdup(item->value) : NULL; /* Any specific setting for retransmissions? */ item = janus_config_get_item_drilldown(config, "general", "max_retransmissions"); if(item && item->value) { int mr = atoi(item->value); if(mr < 0) { JANUS_LOG(LOG_WARN, "Invalid negative value for 'max_retransmissions', using default (%d)\n", max_retransmissions); } else if(mr == 0) { JANUS_LOG(LOG_WARN, "Retransmissions disabled (max_retransmissions=0)\n"); max_retransmissions = 0; } else { max_retransmissions = mr; } } item = janus_config_get_item_drilldown(config, "general", "retransmissions_backoff"); if(item && item->value) { int rb = atoi(item->value); if(rb <= 0) { JANUS_LOG(LOG_WARN, "Invalid negative or null value for 'retransmissions_backoff', using default (%d)\n", retransmissions_backoff); } else { retransmissions_backoff = rb; } } /* Which events should we subscribe to? */ item = janus_config_get_item_drilldown(config, "general", "events"); if(item && item->value) { if(!strcasecmp(item->value, "none")) { /* Don't subscribe to anything at all */ janus_flags_reset(&janus_sampleevh.events_mask); } else if(!strcasecmp(item->value, "all")) { /* Subscribe to everything */ janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_ALL); } else { /* Check what we need to subscribe to */ gchar **subscribe = g_strsplit(item->value, ",", -1); if(subscribe != NULL) { gchar *index = subscribe[0]; if(index != NULL) { int i=0; while(index != NULL) { while(isspace(*index)) index++; if(strlen(index)) { if(!strcasecmp(index, "sessions")) { janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_SESSION); } else if(!strcasecmp(index, "handles")) { janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_HANDLE); } else if(!strcasecmp(index, "jsep")) { janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_JSEP); } else if(!strcasecmp(index, "webrtc")) { janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_WEBRTC); } else if(!strcasecmp(index, "media")) { janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_MEDIA); } else if(!strcasecmp(index, "plugins")) { janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_PLUGIN); } else if(!strcasecmp(index, "transports")) { janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_TRANSPORT); } else if(!strcasecmp(index, "core")) { janus_flags_set(&janus_sampleevh.events_mask, JANUS_EVENT_TYPE_CORE); } else { JANUS_LOG(LOG_WARN, "Unknown event type '%s'\n", index); } } i++; index = subscribe[i]; } } g_strfreev(subscribe); } } } /* Is grouping of events ok? */ item = janus_config_get_item_drilldown(config, "general", "grouping"); group_events = item && item->value && janus_is_true(item->value); /* Done */ enabled = TRUE; } } } janus_config_destroy(config); config = NULL; if(!enabled) { JANUS_LOG(LOG_FATAL, "Sample event handler not enabled/needed, giving up...\n"); return -1; /* No point in keeping the plugin loaded */ } JANUS_LOG(LOG_VERB, "Sample event handler configured: %s\n", backend); /* Initialize libcurl, needed for forwarding events via HTTP POST */ curl_global_init(CURL_GLOBAL_ALL); /* Initialize the events queue */ events = g_async_queue_new_full((GDestroyNotify) janus_sampleevh_event_free); g_atomic_int_set(&initialized, 1); /* Launch the thread that will handle incoming events */ GError *error = NULL; handler_thread = g_thread_try_new("janus sampleevh handler", janus_sampleevh_handler, NULL, &error); if(error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the SampleEventHandler handler thread...\n", error->code, error->message ? error->message : "??"); return -1; } JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_SAMPLEEVH_NAME); return 0; }
int janus_mqtt_init(janus_transport_callbacks *callback, const char *config_path) { if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* Initializing context */ janus_mqtt_context *ctx = g_malloc0(sizeof(struct janus_mqtt_context)); ctx->gateway = callback; context_ = ctx; /* Prepare the transport session (again, just one) */ mqtt_session = janus_transport_session_create(context_, NULL); /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_MQTT_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { janus_config_print(config); } /* Handle configuration */ janus_config_item *url_item = janus_config_get_item_drilldown(config, "general", "url"); const char *url = g_strdup((url_item && url_item->value) ? url_item->value : "tcp://localhost:1883"); janus_config_item *client_id_item = janus_config_get_item_drilldown(config, "general", "client_id"); const char *client_id = g_strdup((client_id_item && client_id_item->value) ? client_id_item->value : "guest"); janus_config_item *username_item = janus_config_get_item_drilldown(config, "general", "username"); ctx->connect.username = g_strdup((username_item && username_item->value) ? username_item->value : "guest"); janus_config_item *password_item = janus_config_get_item_drilldown(config, "general", "password"); ctx->connect.password = g_strdup((password_item && password_item->value) ? password_item->value : "guest"); janus_config_item *json_item = janus_config_get_item_drilldown(config, "general", "json"); if(json_item && json_item->value) { /* Check how we need to format/serialize the JSON output */ if(!strcasecmp(json_item->value, "indented")) { /* Default: indented, we use three spaces for that */ json_format_ = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(json_item->value, "plain")) { /* Not indented and no new lines, but still readable */ json_format_ = JSON_INDENT(0) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(json_item->value, "compact")) { /* Compact, so no spaces between separators */ json_format_ = JSON_COMPACT | JSON_PRESERVE_ORDER; } else { JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", json_item->value); json_format_ = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } } /* Check if we need to send events to handlers */ janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events"); if(events != NULL && events->value != NULL) notify_events = janus_is_true(events->value); if(!notify_events && callback->events_is_enabled()) { JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_MQTT_NAME); } /* Check if we need to enable SSL support */ janus_config_item *ssl = janus_config_get_item_drilldown(config, "general", "ssl_enable"); if(ssl && ssl->value && janus_is_true(ssl->value)) { if(strstr(url, "ssl://") != url) JANUS_LOG(LOG_WARN, "SSL enabled, but MQTT url doesn't start with ssl://...\n"); ctx->ssl_enable = TRUE; janus_config_item *cacertfile = janus_config_get_item_drilldown(config, "general", "cacertfile"); if(!cacertfile || !cacertfile->value) { JANUS_LOG(LOG_FATAL, "Missing CA certificate for MQTT integration...\n"); goto error; } ctx->cacert_file = g_strdup(cacertfile->value); janus_config_item *certfile = janus_config_get_item_drilldown(config, "general", "certfile"); ctx->cert_file = (certfile && certfile->value) ? g_strdup(certfile->value) : NULL; janus_config_item *keyfile = janus_config_get_item_drilldown(config, "general", "keyfile"); ctx->key_file = (keyfile && keyfile->value) ? g_strdup(keyfile->value) : NULL; if(ctx->cert_file && !ctx->key_file) { JANUS_LOG(LOG_FATAL, "Certificate is set but key isn't for MQTT integration...\n"); goto error; } if(!ctx->cert_file && ctx->key_file) { JANUS_LOG(LOG_FATAL, "Key is set but certificate isn't for MQTT integration...\n"); goto error; } janus_config_item *verify = janus_config_get_item_drilldown(config, "general", "verify_peer"); ctx->verify_peer = (verify && verify->value && janus_is_true(verify->value)) ? TRUE : FALSE; } else { JANUS_LOG(LOG_INFO, "MQTT SSL support disabled\n"); if(strstr(url, "ssl://") == url) JANUS_LOG(LOG_WARN, "SSL disabled, but MQTT url starts with ssl:// instead of tcp://...\n"); } /* Connect configuration */ janus_config_item *keep_alive_interval_item = janus_config_get_item_drilldown(config, "general", "keep_alive_interval"); ctx->connect.keep_alive_interval = (keep_alive_interval_item && keep_alive_interval_item->value) ? atoi(keep_alive_interval_item->value) : 20; janus_config_item *cleansession_item = janus_config_get_item_drilldown(config, "general", "cleansession"); ctx->connect.cleansession = (cleansession_item && cleansession_item->value) ? atoi(cleansession_item->value) : 0; /* Disconnect configuration */ janus_config_item *disconnect_timeout_item = janus_config_get_item_drilldown(config, "general", "disconnect_timeout"); ctx->disconnect.timeout = (disconnect_timeout_item && disconnect_timeout_item->value) ? atoi(disconnect_timeout_item->value) : 100; janus_config_item *enable_item = janus_config_get_item_drilldown(config, "general", "enable"); if(enable_item && enable_item->value && janus_is_true(enable_item->value)) { janus_mqtt_api_enabled_ = TRUE; /* Subscribe configuration */ { janus_config_item *topic_item = janus_config_get_item_drilldown(config, "general", "subscribe_topic"); if(!topic_item || !topic_item->value) { JANUS_LOG(LOG_FATAL, "Missing topic for incoming messages for MQTT integration...\n"); goto error; } ctx->subscribe.topic = g_strdup(topic_item->value); janus_config_item *qos_item = janus_config_get_item_drilldown(config, "general", "subscribe_qos"); ctx->subscribe.qos = (qos_item && qos_item->value) ? atoi(qos_item->value) : 1; } /* Publish configuration */ { janus_config_item *topic_item = janus_config_get_item_drilldown(config, "general", "publish_topic"); if(!topic_item || !topic_item->value) { JANUS_LOG(LOG_FATAL, "Missing topic for outgoing messages for MQTT integration...\n"); goto error; } ctx->publish.topic = g_strdup(topic_item->value); janus_config_item *qos_item = janus_config_get_item_drilldown(config, "general", "publish_qos"); ctx->publish.qos = (qos_item && qos_item->value) ? atoi(qos_item->value) : 1; } } else { janus_mqtt_api_enabled_ = FALSE; ctx->subscribe.topic = NULL; ctx->publish.topic = NULL; } /* Admin configuration */ janus_config_item *admin_enable_item = janus_config_get_item_drilldown(config, "admin", "admin_enable"); if(admin_enable_item && admin_enable_item->value && janus_is_true(admin_enable_item->value)) { janus_mqtt_admin_api_enabled_ = TRUE; /* Admin subscribe configuration */ { janus_config_item *topic_item = janus_config_get_item_drilldown(config, "admin", "subscribe_topic"); if(!topic_item || !topic_item->value) { JANUS_LOG(LOG_FATAL, "Missing topic for incoming admin messages for MQTT integration...\n"); goto error; } ctx->admin.subscribe.topic = g_strdup(topic_item->value); janus_config_item *qos_item = janus_config_get_item_drilldown(config, "admin", "subscribe_qos"); ctx->admin.subscribe.qos = (qos_item && qos_item->value) ? atoi(qos_item->value) : 1; } /* Admin publish configuration */ { janus_config_item *topic_item = janus_config_get_item_drilldown(config, "admin", "publish_topic"); if(!topic_item || !topic_item->value) { JANUS_LOG(LOG_FATAL, "Missing topic for outgoing admin messages for MQTT integration...\n"); goto error; } ctx->admin.publish.topic = g_strdup(topic_item->value); janus_config_item *qos_item = janus_config_get_item_drilldown(config, "admin", "publish_qos"); ctx->admin.publish.qos = (qos_item && qos_item->value) ? atoi(qos_item->value) : 1; } } else { janus_mqtt_admin_api_enabled_ = FALSE; ctx->admin.subscribe.topic = NULL; ctx->admin.publish.topic = NULL; } if(!janus_mqtt_api_enabled_ && !janus_mqtt_admin_api_enabled_) { JANUS_LOG(LOG_WARN, "MQTT support disabled for both Janus and Admin API, giving up\n"); goto error; } /* Creating a client */ if(MQTTAsync_create( &ctx->client, url, client_id, MQTTCLIENT_PERSISTENCE_NONE, NULL) != MQTTASYNC_SUCCESS) { JANUS_LOG(LOG_FATAL, "Can't connect to MQTT broker: error creating client...\n"); goto error; } if(MQTTAsync_setCallbacks( ctx->client, ctx, janus_mqtt_client_connection_lost, janus_mqtt_client_message_arrived, janus_mqtt_client_delivery_complete) != MQTTASYNC_SUCCESS) { JANUS_LOG(LOG_FATAL, "Can't connect to MQTT broker: error setting up callbacks...\n"); goto error; } /* Connecting to the broker */ int rc = janus_mqtt_client_connect(ctx); if(rc != MQTTASYNC_SUCCESS) { JANUS_LOG(LOG_FATAL, "Can't connect to MQTT broker, return code: %d\n", rc); goto error; } return 0; error: /* If we got here, something went wrong */ janus_transport_session_destroy(mqtt_session); janus_mqtt_client_destroy_context(&ctx); g_free((char *)url); g_free((char *)client_id); g_free(config); return -1; }
/* Transport implementation */ int janus_websockets_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_WEBSOCKETS_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { janus_config_print(config); /* Handle configuration */ janus_config_item *item = janus_config_get_item_drilldown(config, "general", "ws_logging"); if(item && item->value) { ws_log_level = atoi(item->value); if(ws_log_level < 0) ws_log_level = 0; } JANUS_LOG(LOG_VERB, "libwebsockets logging: %d\n", ws_log_level); lws_set_log_level(ws_log_level, NULL); old_wss = NULL; janus_mutex_init(&old_wss_mutex); /* Any ACL for either the Janus or Admin API? */ item = janus_config_get_item_drilldown(config, "general", "ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Janus API allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), FALSE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } item = janus_config_get_item_drilldown(config, "admin", "admin_ws_acl"); if(item && item->value) { gchar **list = g_strsplit(item->value, ",", -1); gchar *index = list[0]; if(index != NULL) { int i=0; while(index != NULL) { if(strlen(index) > 0) { JANUS_LOG(LOG_INFO, "Adding '%s' to the Admin/monitor allowed list...\n", index); janus_websockets_allow_address(g_strdup(index), TRUE); } i++; index = list[i]; } } g_strfreev(list); list = NULL; } /* Setup the Janus API WebSockets server(s) */ item = janus_config_get_item_drilldown(config, "general", "ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "WebSockets server disabled\n"); } else { int wsport = 8188; item = janus_config_get_item_drilldown(config, "general", "ws_port"); if(item && item->value) wsport = atoi(item->value); /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = NULL; info.protocols = wss_protocols; info.extensions = libwebsocket_get_internal_extensions(); info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ wss = libwebsocket_create_context(&info); if(wss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n"); } else { JANUS_LOG(LOG_INFO, "WebSockets server started (port %d)...\n", wsport); } } item = janus_config_get_item_drilldown(config, "general", "wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure WebSockets server disabled\n"); } else { int wsport = 8989; item = janus_config_get_item_drilldown(config, "general", "wss_port"); if(item && item->value) wsport = atoi(item->value); item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = NULL; info.protocols = swss_protocols; info.extensions = libwebsocket_get_internal_extensions(); info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.gid = -1; info.uid = -1; info.options = 0; /* Create the secure WebSocket context */ swss = libwebsocket_create_context(&info); if(swss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n"); } else { JANUS_LOG(LOG_INFO, "Secure WebSockets server started (port %d)...\n", wsport); } } } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_ws"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Admin WebSockets server disabled\n"); } else { int wsport = 7188; item = janus_config_get_item_drilldown(config, "admin", "admin_ws_port"); if(item && item->value) wsport = atoi(item->value); /* Prepare context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = NULL; info.protocols = admin_wss_protocols; info.extensions = libwebsocket_get_internal_extensions(); info.ssl_cert_filepath = NULL; info.ssl_private_key_filepath = NULL; info.gid = -1; info.uid = -1; info.options = 0; /* Create the WebSocket context */ admin_wss = libwebsocket_create_context(&info); if(admin_wss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n"); } else { JANUS_LOG(LOG_INFO, "Admin WebSockets server started (port %d)...\n", wsport); } } item = janus_config_get_item_drilldown(config, "admin", "admin_wss"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Secure Admin WebSockets server disabled\n"); } else { int wsport = 7989; item = janus_config_get_item_drilldown(config, "admin", "admin_wss_port"); if(item && item->value) wsport = atoi(item->value); item = janus_config_get_item_drilldown(config, "certificates", "cert_pem"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing certificate/key path\n"); } else { char *server_pem = (char *)item->value; char *server_key = (char *)item->value; item = janus_config_get_item_drilldown(config, "certificates", "cert_key"); if(item && item->value) server_key = (char *)item->value; JANUS_LOG(LOG_VERB, "Using certificates:\n\t%s\n\t%s\n", server_pem, server_key); /* Prepare secure context */ struct lws_context_creation_info info; memset(&info, 0, sizeof info); info.port = wsport; info.iface = NULL; info.protocols = admin_swss_protocols; info.extensions = libwebsocket_get_internal_extensions(); info.ssl_cert_filepath = server_pem; info.ssl_private_key_filepath = server_key; info.gid = -1; info.uid = -1; info.options = 0; /* Create the secure WebSocket context */ admin_swss = libwebsocket_create_context(&info); if(admin_swss == NULL) { JANUS_LOG(LOG_FATAL, "Error initializing libwebsock...\n"); } else { JANUS_LOG(LOG_INFO, "Secure Admin WebSockets server started (port %d)...\n", wsport); } } } } janus_config_destroy(config); config = NULL; if(!wss && !swss && !admin_wss && !admin_swss) { JANUS_LOG(LOG_FATAL, "No WebSockets server started, giving up...\n"); return -1; /* No point in keeping the plugin loaded */ } wss_janus_api_enabled = wss || swss; wss_admin_api_enabled = admin_wss || admin_swss; GError *error = NULL; /* Start the WebSocket service threads */ if(wss != NULL) { wss_thread = g_thread_try_new("websockets thread", &janus_websockets_thread, wss, &error); if(!wss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(swss != NULL) { swss_thread = g_thread_try_new("secure websockets thread", &janus_websockets_thread, swss, &error); if(!swss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(admin_wss != NULL) { admin_wss_thread = g_thread_try_new("admin websockets thread", &janus_websockets_thread, admin_wss, &error); if(!admin_wss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Admin WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } if(admin_swss != NULL) { admin_swss_thread = g_thread_try_new("secure admin websockets thread", &janus_websockets_thread, admin_swss, &error); if(!admin_swss_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Secure Admin WebSockets thread...\n", error->code, error->message ? error->message : "??"); return -1; } } /* Done */ g_atomic_int_set(&initialized, 1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_WEBSOCKETS_NAME); return 0; }
/* Transport implementation */ int janus_nanomsg_init(janus_transport_callbacks *callback, const char *config_path) { if(g_atomic_int_get(&stopping)) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_NANOMSG_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) { /* Handle configuration */ janus_config_print(config); janus_config_item *item = janus_config_get_item_drilldown(config, "general", "json"); if(item && item->value) { /* Check how we need to format/serialize the JSON output */ if(!strcasecmp(item->value, "indented")) { /* Default: indented, we use three spaces for that */ json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "plain")) { /* Not indented and no new lines, but still readable */ json_format = JSON_INDENT(0) | JSON_PRESERVE_ORDER; } else if(!strcasecmp(item->value, "compact")) { /* Compact, so no spaces between separators */ json_format = JSON_COMPACT | JSON_PRESERVE_ORDER; } else { JANUS_LOG(LOG_WARN, "Unsupported JSON format option '%s', using default (indented)\n", item->value); json_format = JSON_INDENT(3) | JSON_PRESERVE_ORDER; } } /* Check if we need to send events to handlers */ janus_config_item *events = janus_config_get_item_drilldown(config, "general", "events"); if(events != NULL && events->value != NULL) notify_events = janus_is_true(events->value); if(!notify_events && callback->events_is_enabled()) { JANUS_LOG(LOG_WARN, "Notification of events to handlers disabled for %s\n", JANUS_NANOMSG_NAME); } /* First of all, initialize the pipeline for writeable notifications */ write_nfd[0] = nn_socket(AF_SP, NN_PULL); write_nfd[1] = nn_socket(AF_SP, NN_PUSH); if(nn_bind(write_nfd[0], "inproc://janus") < 0) { JANUS_LOG(LOG_WARN, "Error configuring internal Nanomsg pipeline... %d (%s)\n", errno, nn_strerror(errno)); return -1; /* No point in keeping the plugin loaded */ } if(nn_connect(write_nfd[1], "inproc://janus") < 0) { JANUS_LOG(LOG_WARN, "Error configuring internal Nanomsg pipeline...%d (%s)\n", errno, nn_strerror(errno)); return -1; /* No point in keeping the plugin loaded */ } /* Setup the Janus API Nanomsg server(s) */ item = janus_config_get_item_drilldown(config, "general", "enabled"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Nanomsg server disabled (Janus API)\n"); } else { item = janus_config_get_item_drilldown(config, "general", "address"); const char *address = item && item->value ? item->value : NULL; item = janus_config_get_item_drilldown(config, "general", "mode"); const char *mode = item && item->value ? item->value : NULL; if(mode == NULL) mode = "bind"; nfd = nn_socket(AF_SP, NN_PAIR); if(nfd < 0) { JANUS_LOG(LOG_ERR, "Error creating Janus API Nanomsg socket: %d (%s)\n", errno, nn_strerror(errno)); } else { if(!strcasecmp(mode, "bind")) { /* Bind to this address */ nfd_addr = nn_bind(nfd, address); if(nfd_addr < 0) { JANUS_LOG(LOG_ERR, "Error binding Janus API Nanomsg socket to address '%s': %d (%s)\n", address, errno, nn_strerror(errno)); nn_close(nfd); nfd = -1; } } else if(!strcasecmp(mode, "connect")) { /* Connect to this address */ nfd_addr = nn_connect(nfd, address); if(nfd_addr < 0) { JANUS_LOG(LOG_ERR, "Error connecting Janus API Nanomsg socket to address '%s': %d (%s)\n", address, errno, nn_strerror(errno)); nn_close(nfd); nfd = -1; } } else { /* Unsupported mode */ JANUS_LOG(LOG_ERR, "Unsupported mode '%s'\n", mode); nn_close(nfd); nfd = -1; } } } /* Do the same for the Admin API, if enabled */ item = janus_config_get_item_drilldown(config, "admin", "admin_enabled"); if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "Nanomsg server disabled (Admin API)\n"); } else { item = janus_config_get_item_drilldown(config, "admin", "admin_address"); const char *address = item && item->value ? item->value : NULL; item = janus_config_get_item_drilldown(config, "general", "admin_mode"); const char *mode = item && item->value ? item->value : NULL; if(mode == NULL) mode = "bind"; admin_nfd = nn_socket(AF_SP, NN_PAIR); if(admin_nfd < 0) { JANUS_LOG(LOG_ERR, "Error creating Admin API Nanomsg socket: %d (%s)\n", errno, nn_strerror(errno)); } else { if(!strcasecmp(mode, "bind")) { /* Bind to this address */ admin_nfd_addr = nn_bind(admin_nfd, address); if(admin_nfd_addr < 0) { JANUS_LOG(LOG_ERR, "Error binding Admin API Nanomsg socket to address '%s': %d (%s)\n", address, errno, nn_strerror(errno)); nn_close(admin_nfd); admin_nfd = -1; } } else if(!strcasecmp(mode, "connect")) { /* Connect to this address */ admin_nfd_addr = nn_connect(admin_nfd, address); if(admin_nfd_addr < 0) { JANUS_LOG(LOG_ERR, "Error connecting Admin API Nanomsg socket to address '%s': %d (%s)\n", address, errno, nn_strerror(errno)); nn_close(admin_nfd); admin_nfd = -1; } } else { /* Unsupported mode */ JANUS_LOG(LOG_ERR, "Unsupported mode '%s'\n", mode); nn_close(admin_nfd); admin_nfd = -1; } } } } janus_config_destroy(config); config = NULL; if(nfd < 0 && admin_nfd < 0) { JANUS_LOG(LOG_WARN, "No Nanomsg server started, giving up...\n"); return -1; /* No point in keeping the plugin loaded */ } /* Create the clients */ memset(&client, 0, sizeof(janus_nanomsg_client)); if(nfd > -1) { client.admin = FALSE; client.messages = g_async_queue_new(); /* Create a transport instance as well */ client.ts = janus_transport_session_create(&client, NULL); /* Notify handlers about this new transport */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("created")); json_object_set_new(info, "admin_api", json_false()); json_object_set_new(info, "socket", json_integer(nfd)); gateway->notify_event(&janus_nanomsg_transport, client.ts, info); } } memset(&admin_client, 0, sizeof(janus_nanomsg_client)); if(admin_nfd > -1) { admin_client.admin = TRUE; admin_client.messages = g_async_queue_new(); /* Create a transport instance as well */ admin_client.ts = janus_transport_session_create(&admin_client, NULL); /* Notify handlers about this new transport */ if(notify_events && gateway->events_is_enabled()) { json_t *info = json_object(); json_object_set_new(info, "event", json_string("created")); json_object_set_new(info, "admin_api", json_true()); json_object_set_new(info, "socket", json_integer(admin_nfd)); gateway->notify_event(&janus_nanomsg_transport, admin_client.ts, info); } } /* Start the Nanomsg service thread */ GError *error = NULL; nanomsg_thread = g_thread_try_new("nanomsg thread", &janus_nanomsg_thread, NULL, &error); if(!nanomsg_thread) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Nanomsg thread...\n", error->code, error->message ? error->message : "??"); return -1; } /* Done */ g_atomic_int_set(&initialized, 1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_NANOMSG_NAME); return 0; }