/* 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; }
/* Plugin implementation */ int janus_echotest_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_ECHOTEST_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) janus_config_print(config); /* This plugin actually has nothing to configure... */ janus_config_destroy(config); config = NULL; sessions = g_hash_table_new(NULL, NULL); janus_mutex_init(&sessions_mutex); messages = g_async_queue_new_full((GDestroyNotify) janus_echotest_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("etest watchdog", &janus_echotest_watchdog, NULL, &error); if(error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the EchoTest 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("janus echotest handler", janus_echotest_handler, NULL, &error); if(error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the EchoTest handler thread...\n", error->code, error->message ? error->message : "??"); return -1; } JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_ECHOTEST_NAME); return 0; }
/* Plugin implementation */ int janus_videocall_init(janus_callbacks *callback, const char *config_path) { if(stopping) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* Read configuration */ char filename[255]; sprintf(filename, "%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); /* This plugin actually has nothing to configure... */ 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; initialized = 1; /* Launch the thread that will handle incoming messages */ GError *error = NULL; handler_thread = g_thread_try_new("janus videocall handler", janus_videocall_handler, NULL, &error); if(error != NULL) { initialized = 0; /* Something went wrong... */ JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch thread...\n", error->code, error->message ? error->message : "??"); return -1; } JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_VIDEOCALL_NAME); return 0; }
/* Plugin implementation */ int janus_serial_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_SERIAL_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) janus_config_print(config); /* This plugin actually has nothing to configure... */ janus_config_destroy(config); config = NULL; sessions = g_hash_table_new(NULL, NULL); janus_mutex_init(&sessions_mutex); messages = g_async_queue_new_full((GDestroyNotify) janus_serial_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("serial watchdog", &janus_serial_watchdog, NULL, &error); if(error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Serial 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("janus serial handler", janus_serial_handler, NULL, &error); if(error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the serial handler thread...\n", error->code, error->message ? error->message : "??"); return -1; } // init part /* Open the file descriptor in non-blocking mode */ if(fd = open(portname,O_RDWR | O_NOCTTY | O_NONBLOCK)){ //printf("stream aperto\n"); JANUS_LOG(LOG_INFO, "stream aperto - Janus Serial\n"); }else{ //printf("errora nell'apertura dello stream\n"); JANUS_LOG(LOG_INFO, "errore nell'apertura dello stream\n"); return 0; } /* Set up the control structure */ struct termios toptions; /* Get currently set options for the tty */ tcgetattr(fd, &toptions); /* Set custom options */ /* 9600 baud */ cfsetispeed(&toptions, B9600); cfsetospeed(&toptions, B9600); /* 8 bits, no parity, no stop bits */ toptions.c_cflag &= ~PARENB; toptions.c_cflag &= ~CSTOPB; toptions.c_cflag &= ~CSIZE; toptions.c_cflag |= CS8; /* no hardware flow control */ toptions.c_cflag &= ~CRTSCTS; /* enable receiver, ignore status lines */ toptions.c_cflag |= CREAD | CLOCAL; /* disable input/output flow control, disable restart chars */ toptions.c_iflag &= ~(IXON | IXOFF | IXANY); /* disable canonical input, disable echo, disable visually erase chars, disable terminal-generated signals */ toptions.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* disable output processing */ toptions.c_oflag &= ~OPOST; /* wait for 24 characters to come in before read returns */ toptions.c_cc[VMIN] = 12; /* no minimum time to wait before read returns */ toptions.c_cc[VTIME] = 0; /* commit the options */ tcsetattr(fd, TCSANOW, &toptions); /* Wait for the Arduino to reset */ usleep(1000*1000); /* Flush anything already in the serial buffer */ tcflush(fd, TCIFLUSH); write(fd,"k",1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_SERIAL_NAME); return 0; }
/* Plugin implementation */ int janus_streaming_init(janus_callbacks *callback, const char *config_path) { if(stopping) { /* Still stopping from before */ return -1; } if(callback == NULL || config_path == NULL) { /* Invalid arguments */ return -1; } /* Read configuration */ char filename[255]; sprintf(filename, "%s/%s.cfg", config_path, JANUS_STREAMING_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config != NULL) janus_config_print(config); mountpoints = g_hash_table_new(NULL, NULL); /* Parse configuration to populate the mountpoints */ if(config != NULL) { janus_config_category *cat = janus_config_get_categories(config); while(cat != NULL) { if(cat->name == NULL) { cat = cat->next; continue; } JANUS_LOG(LOG_VERB, "Adding stream '%s'\n", cat->name); janus_config_item *type = janus_config_get_item(cat, "type"); if(type == NULL || type->value == NULL) { JANUS_LOG(LOG_VERB, " -- Invalid type, skipping stream...\n"); cat = cat->next; continue; } if(!strcasecmp(type->value, "rtp")) { /* RTP live source (e.g., from gstreamer/ffmpeg/vlc/etc.) */ janus_config_item *id = janus_config_get_item(cat, "id"); janus_config_item *desc = janus_config_get_item(cat, "description"); janus_config_item *audio = janus_config_get_item(cat, "audio"); janus_config_item *video = janus_config_get_item(cat, "video"); janus_config_item *aport = janus_config_get_item(cat, "audioport"); janus_config_item *acodec = janus_config_get_item(cat, "audiopt"); janus_config_item *artpmap = janus_config_get_item(cat, "audiortpmap"); janus_config_item *vport = janus_config_get_item(cat, "videoport"); janus_config_item *vcodec = janus_config_get_item(cat, "videopt"); janus_config_item *vrtpmap = janus_config_get_item(cat, "videortpmap"); janus_streaming_mountpoint *live_rtp = calloc(1, sizeof(janus_streaming_mountpoint)); if(live_rtp == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); continue; } if(id == NULL || id->value == NULL) { JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, missing mandatory information...\n"); cat = cat->next; continue; } gboolean doaudio = audio && audio->value && !strcasecmp(audio->value, "yes"); gboolean dovideo = video && video->value && !strcasecmp(video->value, "yes"); if(!doaudio && !dovideo) { JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, no audio or video have to be streamed...\n"); g_free(live_rtp); cat = cat->next; continue; } if(doaudio && (aport == NULL || aport->value == NULL || acodec == NULL || acodec->value == NULL || artpmap == NULL || artpmap->value == NULL)) { JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, missing mandatory information for audio...\n"); cat = cat->next; continue; } if(dovideo && (vport == NULL || vport->value == NULL || vcodec == NULL || vcodec->value == NULL || vrtpmap == NULL || vrtpmap->value == NULL)) { JANUS_LOG(LOG_ERR, "Can't add 'rtp' stream, missing mandatory information for video...\n"); cat = cat->next; continue; } JANUS_LOG(LOG_VERB, "Audio %s, Video %s\n", doaudio ? "enabled" : "NOT enabled", dovideo ? "enabled" : "NOT enabled"); live_rtp->name = g_strdup(cat->name); live_rtp->id = atoi(id->value); char *description = NULL; if(desc != NULL && desc->value != NULL) description = g_strdup(desc->value); else description = g_strdup(cat->name); live_rtp->description = description; live_rtp->active = FALSE; live_rtp->streaming_type = janus_streaming_type_live; live_rtp->streaming_source = janus_streaming_source_rtp; janus_streaming_rtp_source *live_rtp_source = calloc(1, sizeof(janus_streaming_rtp_source)); if(live_rtp->name == NULL || description == NULL || live_rtp_source == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); if(live_rtp->name) g_free(live_rtp->name); if(description) g_free(description); if(live_rtp_source); g_free(live_rtp_source); g_free(live_rtp); continue; } live_rtp_source->audio_port = doaudio ? atoi(aport->value) : -1; live_rtp_source->video_port = dovideo ? atoi(vport->value) : -1; live_rtp->source = live_rtp_source; live_rtp->codecs.audio_pt = doaudio ? atoi(acodec->value) : -1; live_rtp->codecs.audio_rtpmap = doaudio ? g_strdup(artpmap->value) : NULL; live_rtp->codecs.video_pt = dovideo ? atoi(vcodec->value) : -1; live_rtp->codecs.video_rtpmap = dovideo ? g_strdup(vrtpmap->value) : NULL; live_rtp->listeners = NULL; g_hash_table_insert(mountpoints, GINT_TO_POINTER(live_rtp->id), live_rtp); g_thread_new(live_rtp->name, &janus_streaming_relay_thread, live_rtp); } else if(!strcasecmp(type->value, "live")) { /* a-Law file live source */ janus_config_item *id = janus_config_get_item(cat, "id"); janus_config_item *desc = janus_config_get_item(cat, "description"); janus_config_item *file = janus_config_get_item(cat, "filename"); janus_config_item *audio = janus_config_get_item(cat, "audio"); janus_config_item *video = janus_config_get_item(cat, "video"); if(id == NULL || id->value == NULL || file == NULL || file->value == NULL) { JANUS_LOG(LOG_ERR, "Can't add 'live' stream, missing mandatory information...\n"); cat = cat->next; continue; } gboolean doaudio = audio && audio->value && !strcasecmp(audio->value, "yes"); gboolean dovideo = video && video->value && !strcasecmp(video->value, "yes"); /* TODO We should support something more than raw a-Law and mu-Law streams... */ if(!doaudio || dovideo) { JANUS_LOG(LOG_ERR, "Can't add 'live' stream, we only support audio file streaming right now...\n"); cat = cat->next; continue; } if(!strstr(file->value, ".alaw") && !strstr(file->value, ".mulaw")) { JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, unsupported format (we only support raw mu-Law and a-Law files right now)\n"); cat = cat->next; continue; } janus_streaming_mountpoint *live_file = calloc(1, sizeof(janus_streaming_mountpoint)); if(live_file == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); continue; } live_file->name = g_strdup(cat->name); live_file->id = atoi(id->value); char *description = NULL; if(desc != NULL && desc->value != NULL) description = g_strdup(desc->value); else description = g_strdup(cat->name); live_file->description = description; live_file->active = FALSE; live_file->streaming_type = janus_streaming_type_live; live_file->streaming_source = janus_streaming_source_file; janus_streaming_file_source *live_file_source = calloc(1, sizeof(janus_streaming_file_source)); if(live_file->name == NULL || description == NULL || live_file_source == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); if(live_file->name) g_free(live_file->name); if(description) g_free(description); if(live_file_source); g_free(live_file_source); g_free(live_file); continue; } live_file_source->filename = g_strdup(file->value); live_file->source = live_file_source; live_file->codecs.audio_pt = strstr(file->value, ".alaw") ? 8 : 0; live_file->codecs.audio_rtpmap = strstr(file->value, ".alaw") ? "PCMA/8000" : "PCMU/8000"; live_file->codecs.video_pt = -1; /* FIXME We don't support video for this type yet */ live_file->codecs.video_rtpmap = NULL; live_file->listeners = NULL; g_hash_table_insert(mountpoints, GINT_TO_POINTER(live_file->id), live_file); g_thread_new(live_file->name, &janus_streaming_filesource_thread, live_file); } else if(!strcasecmp(type->value, "ondemand")) { /* mu-Law file on demand source */ janus_config_item *id = janus_config_get_item(cat, "id"); janus_config_item *desc = janus_config_get_item(cat, "description"); janus_config_item *file = janus_config_get_item(cat, "filename"); janus_config_item *audio = janus_config_get_item(cat, "audio"); janus_config_item *video = janus_config_get_item(cat, "video"); if(id == NULL || id->value == NULL || file == NULL || file->value == NULL) { JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, missing mandatory information...\n"); cat = cat->next; continue; } gboolean doaudio = audio && audio->value && !strcasecmp(audio->value, "yes"); gboolean dovideo = video && video->value && !strcasecmp(video->value, "yes"); /* TODO We should support something more than raw a-Law and mu-Law streams... */ if(!doaudio || dovideo) { JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, we only support audio file streaming right now...\n"); cat = cat->next; continue; } if(!strstr(file->value, ".alaw") && !strstr(file->value, ".mulaw")) { JANUS_LOG(LOG_ERR, "Can't add 'ondemand' stream, unsupported format (we only support raw mu-Law and a-Law files right now)\n"); cat = cat->next; continue; } janus_streaming_mountpoint *ondemand_file = calloc(1, sizeof(janus_streaming_mountpoint)); if(ondemand_file == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); continue; } ondemand_file->name = g_strdup(cat->name); ondemand_file->id = atoi(id->value); char *description = NULL; if(desc != NULL && desc->value != NULL) description = g_strdup(desc->value); else description = g_strdup(cat->name); ondemand_file->description = description; ondemand_file->active = FALSE; ondemand_file->streaming_type = janus_streaming_type_on_demand; ondemand_file->streaming_source = janus_streaming_source_file; janus_streaming_file_source *ondemand_file_source = calloc(1, sizeof(janus_streaming_file_source)); if(ondemand_file->name == NULL || description == NULL || ondemand_file_source == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); if(ondemand_file->name) g_free(ondemand_file->name); if(description) g_free(description); if(ondemand_file_source); g_free(ondemand_file_source); g_free(ondemand_file); continue; } ondemand_file_source->filename = g_strdup(file->value); ondemand_file->source = ondemand_file_source; ondemand_file->codecs.audio_pt = strstr(file->value, ".alaw") ? 8 : 0; ondemand_file->codecs.audio_rtpmap = strstr(file->value, ".alaw") ? "PCMA/8000" : "PCMU/8000"; ondemand_file->codecs.video_pt = -1; /* FIXME We don't support video for this type yet */ ondemand_file->codecs.video_rtpmap = NULL; ondemand_file->listeners = NULL; g_hash_table_insert(mountpoints, GINT_TO_POINTER(ondemand_file->id), ondemand_file); } cat = cat->next; } /* Done */ janus_config_destroy(config); config = NULL; } /* Show available mountpoints */ GList *mountpoints_list = g_hash_table_get_values(mountpoints); GList *m = mountpoints_list; while(m) { janus_streaming_mountpoint *mp = (janus_streaming_mountpoint *)m->data; JANUS_LOG(LOG_VERB, " ::: [%"SCNu64"][%s] %s (%s, %s)\n", mp->id, mp->name, mp->description, mp->streaming_type == janus_streaming_type_live ? "live" : "on demand", mp->streaming_source == janus_streaming_source_rtp ? "RTP source" : "file source"); m = m->next; } g_list_free(mountpoints_list); sessions = g_hash_table_new(NULL, NULL); janus_mutex_init(&sessions_mutex); messages = g_queue_new(); /* This is the callback we'll need to invoke to contact the gateway */ gateway = callback; initialized = 1; /* Launch the thread that will handle incoming messages */ GError *error = NULL; handler_thread = g_thread_try_new("janus streaming handler", janus_streaming_handler, NULL, &error); if(error != NULL) { initialized = 0; /* Something went wrong... */ JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch thread...\n", error->code, error->message ? error->message : "??"); return -1; } JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_STREAMING_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; }
/* Plugin implementation */ int janus_source_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_SOURCE_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if (config != NULL) janus_config_print(config); /* Parse configuration */ if (config != NULL) { GList *cl = janus_config_get_categories(config); while (cl != NULL) { janus_config_category *cat = (janus_config_category *)cl->data; if (cat->name == NULL) { cl = cl->next; continue; } JANUS_LOG(LOG_VERB, "Parsing category '%s'\n", cat->name); janus_source_parse_ports_range(janus_config_get_item(cat, "udp_port_range"), &udp_min_port, &udp_max_port); janus_source_parse_keepalive_interval(janus_config_get_item(cat, "keepalive_interval"), &keepalive_interval); janus_source_parse_status_service_url(janus_config_get_item(cat, "keepalive_service_url"), &keepalive_service_url); janus_source_parse_status_service_url(janus_config_get_item(cat,"status_service_url"),&status_service_url); janus_source_parse_video_codec_priority(janus_config_get_item(cat, "video_codec_priority")); janus_source_parse_rtsp_interface_ip(janus_config_get_item(cat, "interface"),&rtsp_interface_ip); cl = cl->next; } janus_config_destroy(config); config = NULL; } if (udp_min_port <= 0 || udp_max_port <= 0) { udp_min_port = 4000; udp_max_port = 5000; JANUS_LOG(LOG_WARN, "Using default port range: %d-%d\n", udp_min_port, udp_max_port); } sessions = g_hash_table_new(NULL, NULL); janus_mutex_init(&sessions_mutex); messages = g_async_queue_new_full((GDestroyNotify)janus_source_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("source watchdog", &janus_source_watchdog, NULL, &error); if (error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the SourcePlugin watchdog thread...\n", error->code, error->message ? error->message : "??"); return -1; } gst_init(NULL, NULL); gst_debug_set_threshold_from_string(gst_debug_str, FALSE); curl_handle = curl_init(); socket_utils_init(udp_min_port, udp_max_port); /* Launch the thread that will handle incoming messages */ handler_thread = g_thread_try_new("janus source handler", janus_source_handler, NULL, &error); if (error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Source handler thread...\n", error->code, error->message ? error->message : "??"); return -1; } /* Launch the thread that will handle rtsp clients */ handler_rtsp_thread = g_thread_try_new("rtsp server", janus_source_rtsp_server_thread, NULL, &error); if (error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the Source rtsp server thread...\n", error->code, error->message ? error->message : "??"); return -1; } /* Set PID */ memset(&PID, 0, JANUS_PID_SIZE); if (0 > janus_set_pid()) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got an error while plugin id initialize."); return -1; } /*Start the keepalive thread */ keepalive = g_thread_try_new("source keepalive", &janus_source_keepalive, NULL, &error); if (error != NULL) { g_atomic_int_set(&initialized, 0); JANUS_LOG(LOG_ERR, "Got error %d (%s) trying to launch the SourcePlugin keepalive thread...\n", error->code, error->message ? error->message : "??"); return -1; } JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_SOURCE_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; /* 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_rabbitmq_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 Janus core */ gateway = callback; /* Read configuration */ char filename[255]; g_snprintf(filename, 255, "%s/%s.jcfg", config_path, JANUS_RABBITMQ_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); janus_config *config = janus_config_parse(filename); if(config == NULL) { JANUS_LOG(LOG_WARN, "Couldn't find .jcfg configuration file (%s), trying .cfg\n", JANUS_RABBITMQ_PACKAGE); g_snprintf(filename, 255, "%s/%s.cfg", config_path, JANUS_RABBITMQ_PACKAGE); JANUS_LOG(LOG_VERB, "Configuration file: %s\n", filename); config = janus_config_parse(filename); } if(config != NULL) janus_config_print(config); janus_config_category *config_general = janus_config_get_create(config, NULL, janus_config_type_category, "general"); janus_config_category *config_admin = janus_config_get_create(config, NULL, janus_config_type_category, "admin"); janus_config_item *item = janus_config_get(config, config_general, janus_config_type_item, "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(config, config_general, janus_config_type_item, "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_RABBITMQ_NAME); } /* Handle configuration, starting from the server details */ item = janus_config_get(config, config_general, janus_config_type_item, "host"); if(item && item->value) rmqhost = g_strdup(item->value); else rmqhost = g_strdup("localhost"); int rmqport = AMQP_PROTOCOL_PORT; item = janus_config_get(config, config_general, janus_config_type_item, "port"); if(item && item->value) rmqport = atoi(item->value); /* Credentials and Virtual Host */ item = janus_config_get(config, config_general, janus_config_type_item, "vhost"); if(item && item->value) vhost = g_strdup(item->value); else vhost = g_strdup("/"); item = janus_config_get(config, config_general, janus_config_type_item, "username"); if(item && item->value) username = g_strdup(item->value); else username = g_strdup("guest"); item = janus_config_get(config, config_general, janus_config_type_item, "password"); if(item && item->value) password = g_strdup(item->value); else password = g_strdup("guest"); /* SSL config*/ gboolean ssl_enabled = FALSE; gboolean ssl_verify_peer = FALSE; gboolean ssl_verify_hostname = FALSE; item = janus_config_get(config, config_general, janus_config_type_item, "ssl_enabled"); if(item == NULL) { /* Try legacy property */ item = janus_config_get(config, config_general, janus_config_type_item, "ssl_enable"); if (item && item->value) { JANUS_LOG(LOG_WARN, "Found deprecated 'ssl_enable' property, please update it to 'ssl_enabled' instead\n"); } } if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_INFO, "RabbitMQ SSL support disabled\n"); } else { ssl_enabled = TRUE; item = janus_config_get(config, config_general, janus_config_type_item, "ssl_cacert"); if(item && item->value) ssl_cacert_file = g_strdup(item->value); item = janus_config_get(config, config_general, janus_config_type_item, "ssl_cert"); if(item && item->value) ssl_cert_file = g_strdup(item->value); item = janus_config_get(config, config_general, janus_config_type_item, "ssl_key"); if(item && item->value) ssl_key_file = g_strdup(item->value); item = janus_config_get(config, config_general, janus_config_type_item, "ssl_verify_peer"); if(item && item->value && janus_is_true(item->value)) ssl_verify_peer = TRUE; item = janus_config_get(config, config_general, janus_config_type_item, "ssl_verify_hostname"); if(item && item->value && janus_is_true(item->value)) ssl_verify_hostname = TRUE; } /* Now check if the Janus API must be supported */ item = janus_config_get(config, config_general, janus_config_type_item, "enabled"); if(item == NULL) { /* Try legacy property */ item = janus_config_get(config, config_general, janus_config_type_item, "enable"); if (item && item->value) { JANUS_LOG(LOG_WARN, "Found deprecated 'enable' property, please update it to 'enabled' instead\n"); } } if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "RabbitMQ support disabled (Janus API)\n"); } else { /* Parse configuration */ item = janus_config_get(config, config_general, janus_config_type_item, "to_janus"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing name of incoming queue for RabbitMQ integration...\n"); goto error; } to_janus = g_strdup(item->value); item = janus_config_get(config, config_general, janus_config_type_item, "from_janus"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing name of outgoing queue for RabbitMQ integration...\n"); goto error; } from_janus = g_strdup(item->value); item = janus_config_get(config, config_general, janus_config_type_item, "janus_exchange"); if(!item || !item->value) { JANUS_LOG(LOG_INFO, "Missing name of outgoing exchange for RabbitMQ integration, using default\n"); } else { janus_exchange = g_strdup(item->value); } if (janus_exchange == NULL) { JANUS_LOG(LOG_INFO, "RabbitMQ support for Janus API enabled, %s:%d (%s/%s)\n", rmqhost, rmqport, to_janus, from_janus); } else { JANUS_LOG(LOG_INFO, "RabbitMQ support for Janus API enabled, %s:%d (%s/%s) exch: (%s)\n", rmqhost, rmqport, to_janus, from_janus, janus_exchange); } rmq_janus_api_enabled = TRUE; } /* Do the same for the admin API */ item = janus_config_get(config, config_admin, janus_config_type_item, "admin_enabled"); if(item == NULL) { /* Try legacy property */ item = janus_config_get(config, config_general, janus_config_type_item, "admin_enable"); if (item && item->value) { JANUS_LOG(LOG_WARN, "Found deprecated 'admin_enable' property, please update it to 'admin_enabled' instead\n"); } } if(!item || !item->value || !janus_is_true(item->value)) { JANUS_LOG(LOG_WARN, "RabbitMQ support disabled (Admin API)\n"); } else { /* Parse configuration */ item = janus_config_get(config, config_admin, janus_config_type_item, "to_janus_admin"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing name of incoming queue for RabbitMQ integration...\n"); goto error; } to_janus_admin = g_strdup(item->value); item = janus_config_get(config, config_admin, janus_config_type_item, "from_janus_admin"); if(!item || !item->value) { JANUS_LOG(LOG_FATAL, "Missing name of outgoing queue for RabbitMQ integration...\n"); goto error; } from_janus_admin = g_strdup(item->value); JANUS_LOG(LOG_INFO, "RabbitMQ support for Admin API enabled, %s:%d (%s/%s)\n", rmqhost, rmqport, to_janus_admin, from_janus_admin); rmq_admin_api_enabled = TRUE; } if(!rmq_janus_api_enabled && !rmq_admin_api_enabled) { JANUS_LOG(LOG_WARN, "RabbitMQ support disabled for both Janus and Admin API, giving up\n"); goto error; } else { /* FIXME We currently support a single application, create a new janus_rabbitmq_client instance */ rmq_client = g_malloc0(sizeof(janus_rabbitmq_client)); /* Connect */ rmq_client->rmq_conn = amqp_new_connection(); amqp_socket_t *socket = NULL; int status; JANUS_LOG(LOG_VERB, "Creating RabbitMQ socket...\n"); if (ssl_enabled) { socket = amqp_ssl_socket_new(rmq_client->rmq_conn); if(socket == NULL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error creating socket...\n"); goto error; } if(ssl_verify_peer) { amqp_ssl_socket_set_verify_peer(socket, 1); } else { amqp_ssl_socket_set_verify_peer(socket, 0); } if(ssl_verify_hostname) { amqp_ssl_socket_set_verify_hostname(socket, 1); } else { amqp_ssl_socket_set_verify_hostname(socket, 0); } if(ssl_cacert_file) { status = amqp_ssl_socket_set_cacert(socket, ssl_cacert_file); if(status != AMQP_STATUS_OK) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error setting CA certificate... (%s)\n", amqp_error_string2(status)); goto error; } } if(ssl_cert_file && ssl_key_file) { status = amqp_ssl_socket_set_key(socket, ssl_cert_file, ssl_key_file); if(status != AMQP_STATUS_OK) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error setting key... (%s)\n", amqp_error_string2(status)); goto error; } } } else { socket = amqp_tcp_socket_new(rmq_client->rmq_conn); if(socket == NULL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error creating socket...\n"); goto error; } } JANUS_LOG(LOG_VERB, "Connecting to RabbitMQ server...\n"); status = amqp_socket_open(socket, rmqhost, rmqport); if(status != AMQP_STATUS_OK) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error opening socket... (%s)\n", amqp_error_string2(status)); goto error; } JANUS_LOG(LOG_VERB, "Logging in...\n"); amqp_rpc_reply_t result = amqp_login(rmq_client->rmq_conn, vhost, 0, 131072, 0, AMQP_SASL_METHOD_PLAIN, username, password); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error logging in... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } rmq_client->rmq_channel = 1; JANUS_LOG(LOG_VERB, "Opening channel...\n"); amqp_channel_open(rmq_client->rmq_conn, rmq_client->rmq_channel); result = amqp_get_rpc_reply(rmq_client->rmq_conn); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error opening channel... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } rmq_client->janus_exchange = amqp_empty_bytes; if(janus_exchange != NULL) { JANUS_LOG(LOG_VERB, "Declaring exchange...\n"); rmq_client->janus_exchange = amqp_cstring_bytes(janus_exchange); amqp_exchange_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->janus_exchange, amqp_cstring_bytes(JANUS_RABBITMQ_EXCHANGE_TYPE), 0, 0, 0, 0, amqp_empty_table); result = amqp_get_rpc_reply(rmq_client->rmq_conn); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error diclaring exchange... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } } rmq_client->janus_api_enabled = FALSE; if(rmq_janus_api_enabled) { rmq_client->janus_api_enabled = TRUE; JANUS_LOG(LOG_VERB, "Declaring incoming queue... (%s)\n", to_janus); rmq_client->to_janus_queue = amqp_cstring_bytes(to_janus); amqp_queue_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->to_janus_queue, 0, 0, 0, 0, amqp_empty_table); result = amqp_get_rpc_reply(rmq_client->rmq_conn); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error declaring queue... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } JANUS_LOG(LOG_VERB, "Declaring outgoing queue... (%s)\n", from_janus); rmq_client->from_janus_queue = amqp_cstring_bytes(from_janus); amqp_queue_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->from_janus_queue, 0, 0, 0, 0, amqp_empty_table); result = amqp_get_rpc_reply(rmq_client->rmq_conn); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error declaring queue... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } amqp_basic_consume(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->to_janus_queue, amqp_empty_bytes, 0, 1, 0, amqp_empty_table); result = amqp_get_rpc_reply(rmq_client->rmq_conn); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error consuming... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } } rmq_client->admin_api_enabled = FALSE; if(rmq_admin_api_enabled) { rmq_client->admin_api_enabled = TRUE; JANUS_LOG(LOG_VERB, "Declaring incoming queue... (%s)\n", to_janus_admin); rmq_client->to_janus_admin_queue = amqp_cstring_bytes(to_janus_admin); amqp_queue_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->to_janus_admin_queue, 0, 0, 0, 0, amqp_empty_table); result = amqp_get_rpc_reply(rmq_client->rmq_conn); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error declaring queue... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } JANUS_LOG(LOG_VERB, "Declaring outgoing queue... (%s)\n", from_janus_admin); rmq_client->from_janus_admin_queue = amqp_cstring_bytes(from_janus_admin); amqp_queue_declare(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->from_janus_admin_queue, 0, 0, 0, 0, amqp_empty_table); result = amqp_get_rpc_reply(rmq_client->rmq_conn); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error declaring queue... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } amqp_basic_consume(rmq_client->rmq_conn, rmq_client->rmq_channel, rmq_client->to_janus_admin_queue, amqp_empty_bytes, 0, 1, 0, amqp_empty_table); result = amqp_get_rpc_reply(rmq_client->rmq_conn); if(result.reply_type != AMQP_RESPONSE_NORMAL) { JANUS_LOG(LOG_FATAL, "Can't connect to RabbitMQ server: error consuming... %s, %s\n", amqp_error_string2(result.library_error), amqp_method_name(result.reply.id)); goto error; } } rmq_client->messages = g_async_queue_new(); rmq_client->destroy = 0; /* Prepare the transport session (again, just one) */ rmq_session = janus_transport_session_create(rmq_client, NULL); /* Start the threads */ GError *error = NULL; rmq_client->in_thread = g_thread_try_new("rmq_in_thread", &janus_rmq_in_thread, rmq_client, &error); if(error != NULL) { /* Something went wrong... */ JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the RabbitMQ incoming thread...\n", error->code, error->message ? error->message : "??"); janus_transport_session_destroy(rmq_session); g_free(rmq_client); janus_config_destroy(config); return -1; } rmq_client->out_thread = g_thread_try_new("rmq_out_thread", &janus_rmq_out_thread, rmq_client, &error); if(error != NULL) { /* Something went wrong... */ JANUS_LOG(LOG_FATAL, "Got error %d (%s) trying to launch the RabbitMQ outgoing thread...\n", error->code, error->message ? error->message : "??"); janus_transport_session_destroy(rmq_session); g_free(rmq_client); janus_config_destroy(config); return -1; } janus_mutex_init(&rmq_client->mutex); /* Done */ JANUS_LOG(LOG_INFO, "Setup of RabbitMQ integration completed\n"); /* 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("connected")); gateway->notify_event(&janus_rabbitmq_transport, rmq_session, info); } } janus_config_destroy(config); config = NULL; /* Done */ g_atomic_int_set(&initialized, 1); JANUS_LOG(LOG_INFO, "%s initialized!\n", JANUS_RABBITMQ_NAME); return 0; error: /* If we got here, something went wrong */ g_free(rmq_client); g_free(rmqhost); g_free(vhost); g_free(username); g_free(password); g_free(janus_exchange); g_free(to_janus); g_free(from_janus); g_free(to_janus_admin); g_free(from_janus_admin); g_free(ssl_cacert_file); g_free(ssl_cert_file); g_free(ssl_key_file); if(config) janus_config_destroy(config); return -1; }
/* 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; }