/* Search for mount, if the mount is there but not currently running then * check the fallback, and so on. Must have a global source lock to call * this function. */ source_t *source_find_mount (const char *mount) { source_t *source = NULL; ice_config_t *config; mount_proxy *mountinfo; int depth = 0; config = config_get_config(); while (mount && depth < MAX_FALLBACK_DEPTH) { source = source_find_mount_raw(mount); if (source) { if (source->running || source->on_demand) break; } /* we either have a source which is not active (relay) or no source * at all. Check the mounts list for fallback settings */ mountinfo = config_find_mount (config, mount); source = NULL; if (mountinfo == NULL) break; mount = mountinfo->fallback_mount; depth++; } config_release_config(); return source; }
/* This removes any source stats from virtual mountpoints, ie mountpoints * where no source_t exists. This function requires the global sources lock * to be held before calling. */ void stats_clear_virtual_mounts (void) { avl_node *snode; avl_tree_wlock (_stats.source_tree); snode = avl_get_first(_stats.source_tree); while (snode) { stats_source_t *src = (stats_source_t *)snode->key; source_t *source = source_find_mount_raw (src->source); if (source == NULL) { stats_node_t *node; avl_tree_wlock (src->stats_tree); node = _find_node (src->stats_tree, "fallback"); if (node == NULL) { /* no source_t and no fallback file stat, so delete */ snode = avl_get_next (snode); avl_delete (_stats.source_tree, src, _free_source_stats); continue; } avl_tree_unlock (src->stats_tree); } snode = avl_get_next (snode); } avl_tree_unlock (_stats.source_tree); }
/* rescan the mount list, so that xsl files are updated to show * unconnected but active fallback mountpoints */ void source_recheck_mounts (int update_all) { ice_config_t *config; mount_proxy *mount; avl_tree_rlock (global.source_tree); config = config_get_config(); mount = config->mounts; if (update_all) stats_clear_virtual_mounts (); while (mount) { source_t *source = source_find_mount (mount->mountname); if (source) { source = source_find_mount_raw (mount->mountname); if (source) { mount_proxy *mountinfo = config_find_mount (config, source->mount); source_update_settings (config, source, mountinfo); } else if (update_all) { stats_event_hidden (mount->mountname, NULL, mount->hidden); stats_event_args (mount->mountname, "listenurl", "http://%s:%d%s", config->hostname, config->port, mount->mountname); stats_event (mount->mountname, "listeners", "0"); if (mount->max_listeners < 0) stats_event (mount->mountname, "max_listeners", "unlimited"); else stats_event_args (mount->mountname, "max_listeners", "%d", mount->max_listeners); } } else stats_event (mount->mountname, NULL, NULL); /* check for fallback to file */ if (global.running == ICE_RUNNING && mount->fallback_mount) { source_t *fallback = source_find_mount (mount->fallback_mount); if (fallback == NULL) { thread_create ("Fallback file thread", source_fallback_file, strdup (mount->fallback_mount), THREAD_DETACHED); } } mount = mount->next; } avl_tree_unlock (global.source_tree); config_release_config(); }
int admin_mount_request (client_t *client, const char *uri) { source_t *source; const char *mount = httpp_get_query_param (client->parser, "mount"); struct admin_command *cmd = find_admin_command (admin_mount, uri); if (cmd == NULL) return command_stats (client, uri); if (cmd == NULL || cmd->handle.source == NULL) { INFO0("mount request not recognised"); return client_send_400 (client, "unknown request"); } avl_tree_rlock(global.source_tree); source = source_find_mount_raw(mount); if (source == NULL) { avl_tree_unlock(global.source_tree); if (strncmp (cmd->request, "stats", 5) == 0) return command_stats (client, uri); if (strncmp (cmd->request, "listclients", 11) == 0) return fserve_list_clients (client, mount, cmd->response, 1); if (strncmp (cmd->request, "killclient", 10) == 0) return fserve_kill_client (client, mount, cmd->response); WARN1("Admin command on non-existent source %s", mount); return client_send_400 (client, "Source does not exist"); } else { int ret = 0; thread_mutex_lock (&source->lock); if (source_available (source) == 0) { thread_mutex_unlock (&source->lock); avl_tree_unlock (global.source_tree); INFO1("Received admin command on unavailable mount \"%s\"", mount); return client_send_400 (client, "Source is not available"); } ret = cmd->handle.source (client, source, cmd->response); avl_tree_unlock(global.source_tree); return ret; } }
/* Search for mount, if the mount is there but not currently running then * check the fallback, and so on. Must have a global source lock to call * this function. */ source_t *source_find_mount (const char *mount) { source_t *source = NULL; ice_config_t *config; mount_proxy *mountinfo; int depth = 0; config = config_get_config(); while (mount != NULL) { /* limit the number of times through, maybe infinite */ if (depth > MAX_FALLBACK_DEPTH) { source = NULL; break; } source = source_find_mount_raw(mount); if (source != NULL && source->running) break; /* source is not running, meaning that the fallback is not configured within the source, we need to check the mount list */ mountinfo = config->mounts; source = NULL; while (mountinfo) { if (strcmp (mountinfo->mountname, mount) == 0) break; mountinfo = mountinfo->next; } if (mountinfo) mount = mountinfo->fallback_mount; else mount = NULL; depth++; } config_release_config(); return source; }
static int command_show_image (client_t *client, const char *mount) { source_t *source; avl_tree_rlock (global.source_tree); source = source_find_mount_raw (mount); if (source && source->format && source->format->get_image) { thread_mutex_lock (&source->lock); avl_tree_unlock (global.source_tree); if (source->format->get_image (client, source->format) == 0) { thread_mutex_unlock (&source->lock); return fserve_setup_client (client); } thread_mutex_unlock (&source->lock); } else avl_tree_unlock (global.source_tree); return client_send_404 (client, "No image available"); }
xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount, operation_mode mode) { xmlDocPtr doc; xmlNodePtr node; source_t * source; doc = xmlNewDoc (XMLSTR("1.0")); node = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL); xmlDocSetRootElement(doc, node); node = _dump_stats_to_doc (node, show_mount, show_hidden); if (show_mount && node) { avl_tree_rlock(global.source_tree); source = source_find_mount_raw(show_mount); admin_add_listeners_to_mount(source, node, mode); avl_tree_unlock(global.source_tree); } return doc; }
xmlDocPtr stats_get_xml (int flags, const char *show_mount) { xmlDocPtr doc; xmlNodePtr node; doc = xmlNewDoc (XMLSTR("1.0")); node = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL); xmlDocSetRootElement(doc, node); node = _dump_stats_to_doc (node, show_mount, flags); if (show_mount && node) { source_t *source; /* show each listener */ avl_tree_rlock (global.source_tree); source = source_find_mount_raw (show_mount); if (source) { thread_mutex_lock (&source->lock); admin_source_listeners (source, node); thread_mutex_unlock (&source->lock); avl_tree_unlock (global.source_tree); } else { fbinfo finfo; avl_tree_unlock (global.source_tree); finfo.flags = FS_FALLBACK; finfo.mount = (char*)show_mount; finfo.limit = 0; finfo.fallback = NULL; fserve_list_clients_xml (node, &finfo); } } return doc; }
/* This removes any source stats from virtual mountpoints, ie mountpoints * where no source_t exists. This function requires the global sources lock * to be held before calling. */ void stats_clear_virtual_mounts (void) { avl_node *snode; thread_mutex_lock (&_stats_mutex); snode = avl_get_first(_stats.source_tree); while (snode) { stats_source_t *src = (stats_source_t *)snode->key; source_t *source = source_find_mount_raw (src->source); if (source == NULL) { /* no source_t is reserved so remove them now */ snode = avl_get_next (snode); ICECAST_LOG_DEBUG("releasing %s stats", src->source); avl_delete (_stats.source_tree, src, _free_source_stats); continue; } snode = avl_get_next (snode); } thread_mutex_unlock (&_stats_mutex); }
/* Allocate a new source with the stated mountpoint, if one already * exists with that mountpoint in the global source tree then return * NULL. */ source_t *source_reserve (const char *mount) { source_t *src = NULL; if(mount[0] != '/') WARN1("Source at \"%s\" does not start with '/', clients will be " "unable to connect", mount); do { avl_tree_wlock (global.source_tree); src = source_find_mount_raw (mount); if (src) { src = NULL; break; } src = calloc (1, sizeof(source_t)); if (src == NULL) break; src->client_tree = avl_tree_new(_compare_clients, NULL); src->pending_tree = avl_tree_new(_compare_clients, NULL); /* make duplicates for strings or similar */ src->mount = strdup (mount); src->max_listeners = -1; avl_insert (global.source_tree, src); } while (0); avl_tree_unlock (global.source_tree); return src; }
int move_listener (client_t *client, struct _fbinfo *finfo) { source_t *source; mount_proxy *minfo; int rate = finfo->limit, loop = 20, ret = -1; ice_config_t *config = config_get_config(); struct _fbinfo where; unsigned int len = 4096; char buffer [len]; memcpy (&where, finfo, sizeof (where)); if (finfo->fallback) where.fallback = strdup (finfo->fallback); avl_tree_rlock (global.source_tree); do { len = sizeof buffer; util_expand_pattern (where.fallback, where.mount, buffer, &len); where.mount = buffer; minfo = config_find_mount (config, where.mount); if (rate == 0 && minfo && minfo->limit_rate) rate = minfo->limit_rate; source = source_find_mount_raw (where.mount); if (source == NULL && minfo == NULL) break; if (source) { thread_rwlock_wlock (&source->lock); if (source_available (source)) { // an unused on-demand relay will still have an unitialised type if (source->format->type == finfo->type || source->format->type == FORMAT_TYPE_UNDEFINED) { config_release_config(); avl_tree_unlock (global.source_tree); source_setup_listener (source, client); source->listeners++; client->flags |= CLIENT_HAS_MOVED; thread_rwlock_unlock (&source->lock); free (where.fallback); return 0; } } thread_rwlock_unlock (&source->lock); } if (minfo && minfo->fallback_mount) { free (where.fallback); where.fallback = strdup (where.mount); where.mount = minfo->fallback_mount; } else break; } while (loop--); avl_tree_unlock (global.source_tree); config_release_config(); if (where.mount && ((client->flags & CLIENT_IS_SLAVE) == 0)) { if (where.limit == 0) { if (rate == 0) if (sscanf (where.mount, "%*[^[][%d]", &rate) == 1) rate = rate * 1000/8; where.limit = rate; } client->intro_offset = 0; ret = fserve_setup_client_fb (client, &where); } free (where.fallback); return ret; }
void admin_handle_request(client_t *client, const char *uri) { const char *mount, *command_string; int command; DEBUG1("Admin request (%s)", uri); if (!((strcmp(uri, "/admin.cgi") == 0) || (strncmp("/admin/", uri, 7) == 0))) { ERROR0("Internal error: admin request isn't"); client_send_401(client); return; } if (strcmp(uri, "/admin.cgi") == 0) { command_string = uri + 1; } else { command_string = uri + 7; } DEBUG1("Got command (%s)", command_string); command = admin_get_command(command_string); if(command < 0) { ERROR1("Error parsing command string or unrecognised command: %s", command_string); client_send_400(client, "Unrecognised command"); return; } if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) { ice_config_t *config; const char *sc_mount; const char *pass = httpp_get_query_param (client->parser, "pass"); listener_t *listener; if (pass == NULL) { client_send_400 (client, "missing pass parameter"); return; } config = config_get_config (); sc_mount = config->shoutcast_mount; listener = config_get_listen_sock (config, client->con); if (listener && listener->shoutcast_mount) sc_mount = listener->shoutcast_mount; httpp_set_query_param (client->parser, "mount", sc_mount); httpp_setvar (client->parser, HTTPP_VAR_PROTOCOL, "ICY"); httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, pass); config_release_config (); } mount = httpp_get_query_param(client->parser, "mount"); if(mount != NULL) { source_t *source; /* this request does not require auth but can apply to files on webroot */ if (command == COMMAND_BUILDM3U) { command_buildm3u (client, mount); return; } /* This is a mount request, handle it as such */ if (!connection_check_admin_pass(client->parser)) { if (!connection_check_source_pass(client->parser, mount)) { INFO1("Bad or missing password on mount modification admin " "request (command: %s)", command_string); client_send_401(client); return; } } avl_tree_rlock(global.source_tree); source = source_find_mount_raw(mount); if (source == NULL) { WARN2("Admin command %s on non-existent source %s", command_string, mount); avl_tree_unlock(global.source_tree); client_send_400(client, "Source does not exist"); } else { if (source->running == 0 && source->on_demand == 0) { avl_tree_unlock (global.source_tree); INFO2("Received admin command %s on unavailable mount \"%s\"", command_string, mount); client_send_400 (client, "Source is not available"); return; } if (command == COMMAND_SHOUTCAST_METADATA_UPDATE && source->shoutcast_compat == 0) { avl_tree_unlock (global.source_tree); ERROR0 ("illegal change of metadata on non-shoutcast " "compatible stream"); client_send_400 (client, "illegal metadata call"); return; } INFO2("Received admin command %s on mount \"%s\"", command_string, mount); admin_handle_mount_request(client, source, command); avl_tree_unlock(global.source_tree); } } else { if (command == COMMAND_PLAINTEXT_LISTSTREAM) { /* this request is used by a slave relay to retrieve mounts from the master, so handle this request validating against the relay password */ if(!connection_check_relay_pass(client->parser)) { INFO1("Bad or missing password on admin command " "request (command: %s)", command_string); client_send_401(client); return; } } else { if(!connection_check_admin_pass(client->parser)) { INFO1("Bad or missing password on admin command " "request (command: %s)", command_string); client_send_401(client); return; } } admin_handle_general_request(client, command); } }
static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int hidden) { avl_node *avlnode; xmlNodePtr ret = NULL; ice_config_t *config; thread_mutex_lock(&_stats_mutex); /* general stats first */ avlnode = avl_get_first(_stats.global_tree); while (avlnode) { stats_node_t *stat = avlnode->key; if (stat->hidden <= hidden) xmlNewTextChild (root, NULL, XMLSTR(stat->name), XMLSTR(stat->value)); avlnode = avl_get_next (avlnode); } /* now per mount stats */ avlnode = avl_get_first(_stats.source_tree); config = config_get_config(); __add_authstack(config->authstack, root); config_release_config(); while (avlnode) { stats_source_t *source = (stats_source_t *)avlnode->key; if (source->hidden <= hidden && (show_mount == NULL || strcmp (show_mount, source->source) == 0)) { xmlNodePtr metadata, history; source_t *source_real; mount_proxy *mountproxy; int i; avl_node *avlnode2 = avl_get_first (source->stats_tree); xmlNodePtr xmlnode = xmlNewTextChild (root, NULL, XMLSTR("source"), NULL); xmlSetProp (xmlnode, XMLSTR("mount"), XMLSTR(source->source)); if (ret == NULL) ret = xmlnode; while (avlnode2) { stats_node_t *stat = avlnode2->key; xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value)); avlnode2 = avl_get_next (avlnode2); } avl_tree_rlock(global.source_tree); source_real = source_find_mount_raw(source->source); history = playlist_render_xspf(source_real->history); if (history) xmlAddChild(xmlnode, history); metadata = xmlNewTextChild(xmlnode, NULL, XMLSTR("metadata"), NULL); if (source_real->format) { for (i = 0; i < source_real->format->vc.comments; i++) __add_metadata(metadata, source_real->format->vc.user_comments[i]); } avl_tree_unlock(global.source_tree); config = config_get_config(); mountproxy = config_find_mount(config, source->source, MOUNT_TYPE_NORMAL); __add_authstack(mountproxy->authstack, xmlnode); config_release_config(); } avlnode = avl_get_next (avlnode); } thread_mutex_unlock(&_stats_mutex); return ret; }