static char *_get_stats(const char *source, const char *name) { stats_node_t *stats = NULL; stats_source_t *src = NULL; char *value = NULL; if (source == NULL) { avl_tree_rlock (_stats.global_tree); stats = _find_node(_stats.global_tree, name); if (stats) value = (char *)strdup(stats->value); avl_tree_unlock (_stats.global_tree); } else { avl_tree_rlock (_stats.source_tree); src = _find_source(_stats.source_tree, source); if (src) { avl_tree_rlock (src->stats_tree); avl_tree_unlock (_stats.source_tree); stats = _find_node(src->stats_tree, name); if (stats) value = (char *)strdup(stats->value); avl_tree_unlock (src->stats_tree); } else avl_tree_unlock (_stats.source_tree); } return value; }
int fserve_kill_client (client_t *client, const char *mount, int response) { int loop = 2, id; fbinfo finfo; xmlDocPtr doc; xmlNodePtr node; const char *idtext, *v = "0"; char buf[50]; finfo.flags = 0; finfo.mount = (char*)mount; finfo.limit = 0; finfo.fallback = NULL; idtext = httpp_get_query_param (client->parser, "id"); if (idtext == NULL) return client_send_400 (client, "missing parameter id"); id = atoi(idtext); doc = xmlNewDoc(XMLSTR("1.0")); node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL); xmlDocSetRootElement(doc, node); snprintf (buf, sizeof(buf), "Client %d not found", id); avl_tree_rlock (fh_cache); while (1) { avl_node *node; fh_node *fh = find_fh (&finfo); if (fh) { thread_mutex_lock (&fh->lock); avl_tree_unlock (fh_cache); node = avl_get_first (fh->clients); while (node) { client_t *listener = (client_t *)node->key; if (listener->connection.id == id) { listener->connection.error = 1; snprintf (buf, sizeof(buf), "Client %d removed", id); v = "1"; loop = 0; break; } node = avl_get_next (node); } thread_mutex_unlock (&fh->lock); avl_tree_rlock (fh_cache); } if (loop == 0) break; loop--; if (loop == 1) finfo.flags = FS_FALLBACK; } avl_tree_unlock (fh_cache); xmlNewChild (node, NULL, XMLSTR("message"), XMLSTR(buf)); xmlNewChild (node, NULL, XMLSTR("return"), XMLSTR(v)); return admin_send_response (doc, client, response, "response.xsl"); }
static int command_alloc(client_t *client) { xmlDocPtr doc = xmlNewDoc (XMLSTR("1.0")); xmlNodePtr rootnode = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL); avl_node *node; xmlDocSetRootElement(doc, rootnode); avl_tree_rlock (global.alloc_tree); node = avl_get_first (global.alloc_tree); while (node) { alloc_node *an = node->key; char value[25]; xmlNodePtr bnode = xmlNewChild (rootnode, NULL, XMLSTR("block"), NULL); xmlSetProp (bnode, XMLSTR("name"), XMLSTR(an->name)); snprintf (value, sizeof value, "%d", an->count); xmlNewChild (bnode, NULL, XMLSTR("count"), XMLSTR(value)); snprintf (value, sizeof value, "%d", an->allocated); xmlNewChild (bnode, NULL, XMLSTR("allocated"), XMLSTR(value)); node = avl_get_next (node); } avl_tree_unlock (global.alloc_tree); return admin_send_response (doc, client, RAW, "stats.xsl"); }
int command_list_mounts(client_t *client, int response) { DEBUG0("List mounts request"); client_set_queue (client, NULL); client->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE); if (response == TEXT) { redirector_update (client); snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"); client->refbuf->len = strlen (client->refbuf->data); client->respcode = 200; if (strcmp (httpp_getvar (client->parser, HTTPP_VAR_URI), "/admin/streams") == 0) client->refbuf->next = stats_get_streams (1); else client->refbuf->next = stats_get_streams (0); return fserve_setup_client (client); } else { xmlDocPtr doc; avl_tree_rlock (global.source_tree); doc = admin_build_sourcelist(NULL); avl_tree_unlock (global.source_tree); return admin_send_response (doc, client, response, "listmounts.xsl"); } }
void stats_global_calc (void) { stats_event_t event; avl_node *anode; char buffer [VAL_BUFSIZE]; connection_stats (); avl_tree_rlock (_stats.global_tree); anode = avl_get_first(_stats.global_tree); while (anode) { stats_node_t *node = (stats_node_t *)anode->key; if (node->flags & STATS_REGULAR) stats_listener_send (node->flags, "EVENT global %s %s\n", node->name, node->value); anode = avl_get_next (anode); } avl_tree_unlock (_stats.global_tree); build_event (&event, NULL, "outgoing_kbitrate", buffer); event.flags = STATS_COUNTERS|STATS_HIDDEN; snprintf (buffer, sizeof(buffer), "%" PRIu64, (int64_t)global_getrate_avg (global.out_bitrate) * 8 / 1024); process_event (&event); }
int fserve_list_clients_xml (xmlNodePtr parent, fbinfo *finfo) { int ret = 0; fh_node *fh; avl_node *anode; avl_tree_rlock (fh_cache); fh = find_fh (finfo); if (fh == NULL) { avl_tree_unlock (fh_cache); return 0; } thread_mutex_lock (&fh->lock); avl_tree_unlock (fh_cache); anode = avl_get_first (fh->clients); while (anode) { client_t *listener = (client_t *)anode->key; stats_listener_to_xml (listener, parent); ret++; anode = avl_get_next (anode); } thread_mutex_unlock (&fh->lock); return ret; }
int fserve_query_count (fbinfo *finfo) { int ret = -1; fh_node *fh; if (finfo->flags & FS_FALLBACK && finfo->limit) { avl_tree_wlock (fh_cache); fh = open_fh (finfo); if (fh) { ret = fh->refcount; thread_mutex_unlock (&fh->lock); } } else { avl_tree_rlock (fh_cache); fh = find_fh (finfo); if (fh) { thread_mutex_lock (&fh->lock); ret = fh->refcount; thread_mutex_unlock (&fh->lock); } avl_tree_unlock (fh_cache); } return ret; }
static void command_list_mounts(client_t *client, int response) { DEBUG0("List mounts request"); if (response == PLAINTEXT) { snprintf (client->refbuf->data, PER_CLIENT_REFBUF_SIZE, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"); client->refbuf->len = strlen (client->refbuf->data); client->respcode = 200; client->refbuf->next = stats_get_streams (); fserve_add_client (client, NULL); } else { xmlDocPtr doc; avl_tree_rlock (global.source_tree); doc = admin_build_sourcelist(NULL); avl_tree_unlock (global.source_tree); admin_send_response(doc, client, response, LISTMOUNTS_TRANSFORMED_REQUEST); xmlFreeDoc(doc); } }
static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int flags) { avl_node *avlnode; xmlNodePtr ret = NULL; /* general stats first */ avl_tree_rlock (_stats.global_tree); avlnode = avl_get_first(_stats.global_tree); while (avlnode) { stats_node_t *stat = avlnode->key; if (stat->flags & flags) xmlNewTextChild (root, NULL, XMLSTR(stat->name), XMLSTR(stat->value)); avlnode = avl_get_next (avlnode); } avl_tree_unlock (_stats.global_tree); /* now per mount stats */ avl_tree_rlock (_stats.source_tree); avlnode = avl_get_first(_stats.source_tree); while (avlnode) { stats_source_t *source = (stats_source_t *)avlnode->key; if (((flags&STATS_HIDDEN) || (source->flags&STATS_HIDDEN) == (flags&STATS_HIDDEN)) && (show_mount == NULL || strcmp (show_mount, source->source) == 0)) { avl_node *avlnode2; xmlNodePtr xmlnode = xmlNewTextChild (root, NULL, XMLSTR("source"), NULL); avl_tree_rlock (source->stats_tree); avlnode2 = avl_get_first (source->stats_tree); xmlSetProp (xmlnode, XMLSTR("mount"), XMLSTR(source->source)); if (ret == NULL) ret = xmlnode; while (avlnode2) { stats_node_t *stat = avlnode2->key; if ((flags&STATS_HIDDEN) || (stat->flags&STATS_HIDDEN) == (flags&STATS_HIDDEN)) xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value)); avlnode2 = avl_get_next (avlnode2); } avl_tree_unlock (source->stats_tree); } avlnode = avl_get_next (avlnode); } avl_tree_unlock (_stats.source_tree); return ret; }
/* 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(); }
/* Check whether this client is currently on this mount, the client may be * on either the active or pending lists. * return 1 if ok to add or 0 to prevent */ static int check_duplicate_logins (source_t *source, client_t *client, auth_t *auth) { /* allow multiple authenticated relays */ if (client->username == NULL) return 1; if (auth && auth->allow_duplicate_users == 0) { avl_node *node; avl_tree_rlock (source->client_tree); node = avl_get_first (source->client_tree); while (node) { client_t *existing_client = (client_t *)node->key; if (existing_client->username && strcmp (existing_client->username, client->username) == 0) { avl_tree_unlock (source->client_tree); return 0; } node = avl_get_next (node); } avl_tree_unlock (source->client_tree); avl_tree_rlock (source->pending_tree); node = avl_get_first (source->pending_tree); while (node) { client_t *existing_client = (client_t *)node->key; if (existing_client->username && strcmp (existing_client->username, client->username) == 0) { avl_tree_unlock (source->pending_tree); return 0; } node = avl_get_next (node); } avl_tree_unlock (source->pending_tree); } return 1; }
/* Check for changes in the YP servers configured */ static void check_servers (void) { struct yp_server *server = (struct yp_server *)active_yps, **server_p = (struct yp_server **)&active_yps; while (server) { if (server->remove) { struct yp_server *to_go = server; DEBUG1 ("YP server \"%s\"removed", server->url); *server_p = server->next; server = server->next; destroy_yp_server (to_go); continue; } server_p = &server->next; server = server->next; } /* add new server entries */ while (pending_yps) { avl_node *node; server = (struct yp_server *)pending_yps; pending_yps = server->next; DEBUG1("Add pending yps %s", server->url); server->next = (struct yp_server *)active_yps; active_yps = server; /* new YP server configured, need to populate with existing sources */ avl_tree_rlock (global.source_tree); node = avl_get_first (global.source_tree); while (node) { ypdata_t *yp; source_t *source = node->key; thread_rwlock_rlock (&source->lock); if (source->yp_public && (yp = create_yp_entry (source->mount)) != NULL) { DEBUG1 ("Adding existing mount %s", source->mount); yp->server = server; yp->touch_interval = server->touch_interval; yp->next = server->mounts; server->mounts = yp; } thread_rwlock_unlock (&source->lock); node = avl_get_next (node); } avl_tree_unlock (global.source_tree); } }
static void command_show_listeners(client_t *client, source_t *source, int response) { xmlDocPtr doc; xmlNodePtr node, srcnode, listenernode; avl_node *client_node; client_t *current; char buf[22]; char *userAgent = NULL; time_t now = time(NULL); doc = xmlNewDoc("1.0"); node = xmlNewDocNode(doc, NULL, "icestats", NULL); srcnode = xmlNewChild(node, NULL, "source", NULL); xmlSetProp(srcnode, "mount", source->mount); xmlDocSetRootElement(doc, node); memset(buf, '\000', sizeof(buf)); snprintf(buf, sizeof(buf)-1, "%ld", source->listeners); xmlNewChild(srcnode, NULL, "Listeners", buf); avl_tree_rlock(source->client_tree); client_node = avl_get_first(source->client_tree); while(client_node) { current = (client_t *)client_node->key; listenernode = xmlNewChild(srcnode, NULL, "listener", NULL); xmlNewChild(listenernode, NULL, "IP", current->con->ip); userAgent = httpp_getvar(current->parser, "user-agent"); if (userAgent) { xmlNewChild(listenernode, NULL, "UserAgent", userAgent); } else { xmlNewChild(listenernode, NULL, "UserAgent", "Unknown"); } memset(buf, '\000', sizeof(buf)); snprintf(buf, sizeof(buf)-1, "%ld", now - current->con->con_time); xmlNewChild(listenernode, NULL, "Connected", buf); memset(buf, '\000', sizeof(buf)); snprintf(buf, sizeof(buf)-1, "%lu", current->con->id); xmlNewChild(listenernode, NULL, "ID", buf); if (current->username) { xmlNewChild(listenernode, NULL, "username", current->username); } client_node = avl_get_next(client_node); } avl_tree_unlock(source->client_tree); admin_send_response(doc, client, response, LISTCLIENTS_TRANSFORMED_REQUEST); xmlFreeDoc(doc); client_destroy(client); }
/* Add listener to the pending lists of either the source or fserve thread. * This can be run from the connection or auth thread context */ static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo, client_t *client) { int ret = 0; source_t *source = NULL; client->authenticated = 1; /* Here we are parsing the URI request to see if the extension is .xsl, if * so, then process this request as an XSLT request */ if (util_check_valid_extension (mount) == XSLT_CONTENT) { /* If the file exists, then transform it, otherwise, write a 404 */ ICECAST_LOG_DEBUG("Stats request, sending XSL transformed stats"); stats_transform_xslt (client, mount); return 0; } avl_tree_rlock (global.source_tree); source = source_find_mount (mount); if (source) { if (mountinfo) { if (check_duplicate_logins (source, client, mountinfo->auth) == 0) { avl_tree_unlock (global.source_tree); return -1; } /* set a per-mount disconnect time if auth hasn't set one already */ if (mountinfo->max_listener_duration && client->con->discon_time == 0) client->con->discon_time = time(NULL) + mountinfo->max_listener_duration; } ret = add_listener_to_source (source, client); avl_tree_unlock (global.source_tree); if (ret == 0) ICECAST_LOG_DEBUG("client authenticated, passed to source"); } else { avl_tree_unlock (global.source_tree); fserve_client_create (client, mount); } return ret; }
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; } }
static void source_shutdown (source_t *source) { mount_proxy *mountinfo; source->running = 0; INFO1("Source \"%s\" exiting", source->mount); mountinfo = config_find_mount (config_get_config(), source->mount); if (mountinfo) { if (mountinfo->on_disconnect) source_run_script (mountinfo->on_disconnect, source->mount); auth_stream_end (mountinfo, source->mount); } config_release_config(); /* we have de-activated the source now, so no more clients will be * added, now move the listeners we have to the fallback (if any) */ if (source->fallback_mount) { source_t *fallback_source; avl_tree_rlock(global.source_tree); fallback_source = source_find_mount (source->fallback_mount); if (fallback_source != NULL) source_move_clients (source, fallback_source); avl_tree_unlock (global.source_tree); } /* delete this sources stats */ stats_event(source->mount, NULL, NULL); /* we don't remove the source from the tree here, it may be a relay and therefore reserved */ source_clear_source (source); global_lock(); global.sources--; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); /* release our hold on the lock so the main thread can continue cleaning up */ thread_rwlock_unlock(source->shutdown_rwlock); }
static void command_list_mounts(client_t *client, int response) { DEBUG0("List mounts request"); avl_tree_rlock (global.source_tree); if (response == PLAINTEXT) { char buffer [4096], *buf = buffer; unsigned int remaining = sizeof (buffer); int ret = snprintf (buffer, remaining, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"); avl_node *node = avl_get_first(global.source_tree); while (node && ret > 0 && (unsigned)ret < remaining) { source_t *source = (source_t *)node->key; node = avl_get_next(node); if (source->hidden) continue; remaining -= ret; buf += ret; ret = snprintf (buf, remaining, "%s\n", source->mount); } avl_tree_unlock (global.source_tree); /* handle last line */ if (ret > 0 && (unsigned)ret < remaining) { remaining -= ret; buf += ret; } sock_write_bytes (client->con->sock, buffer, sizeof (buffer)-remaining); } else { xmlDocPtr doc = admin_build_sourcelist(NULL); avl_tree_unlock (global.source_tree); admin_send_response(doc, client, response, LISTMOUNTS_TRANSFORMED_REQUEST); xmlFreeDoc(doc); } client_destroy(client); return; }
/* return a list of blocks which contain lines of text. Each line is a mountpoint * reference that a slave will use for relaying. The prepend setting is to indicate * if some something else needs to be added to each line. */ refbuf_t *stats_get_streams (int prepend) { #define STREAMLIST_BLKSIZE 4096 avl_node *node; unsigned int remaining = STREAMLIST_BLKSIZE, prelen; refbuf_t *start = refbuf_new (remaining), *cur = start; const char *pre = ""; char *buffer = cur->data; if (prepend) pre = "/admin/streams?mount="; prelen = strlen (pre); /* now the stats for each source */ avl_tree_rlock (_stats.source_tree); node = avl_get_first(_stats.source_tree); while (node) { int ret; stats_source_t *source = (stats_source_t *)node->key; if ((source->flags & STATS_HIDDEN) == 0) { if (remaining <= strlen (source->source) + prelen + 3) { cur->len = STREAMLIST_BLKSIZE - remaining; cur->next = refbuf_new (STREAMLIST_BLKSIZE); remaining = STREAMLIST_BLKSIZE; cur = cur->next; buffer = cur->data; } ret = snprintf (buffer, remaining, "%s%s\r\n", pre, source->source); if (ret > 0) { buffer += ret; remaining -= ret; } } node = avl_get_next(node); } avl_tree_unlock (_stats.source_tree); cur->len = STREAMLIST_BLKSIZE - remaining; return start; }
client_t *source_find_client(source_t *source, int id) { client_t fakeclient; void *result; connection_t fakecon; fakeclient.con = &fakecon; fakeclient.con->id = id; avl_tree_rlock(source->client_tree); if(avl_get_by_key(source->client_tree, &fakeclient, &result) == 0) { avl_tree_unlock(source->client_tree); return result; } avl_tree_unlock(source->client_tree); return NULL; }
int fserve_contains (const char *name) { int ret = -1; fbinfo finfo; memset (&finfo, 0, sizeof (finfo)); if (strncmp (name, "fallback-/", 10) == 0) { finfo.mount = (char*)name+9; finfo.flags = FS_FALLBACK; } else if (strncmp (name, "file-/", 6) == 0) finfo.mount = (char*)name; DEBUG1 ("looking for %s", name); avl_tree_rlock (fh_cache); if (find_fh (&finfo)) ret = 0; avl_tree_unlock (fh_cache); return ret; }
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; }
int auth_is_listener_connected(source_t *source, char *username) { client_t *client; avl_node *client_node; avl_tree_rlock(source->client_tree); client_node = avl_get_first(source->client_tree); while(client_node) { client = (client_t *)client_node->key; if (client->username) { if (!strcmp(client->username, username)) { avl_tree_unlock(source->client_tree); return 1; } } client_node = avl_get_next(client_node); } avl_tree_unlock(source->client_tree); return 0; }
static void source_shutdown (source_t *source) { source->running = 0; INFO1("Source \"%s\" exiting", source->mount); /* we have de-activated the source now, so no more clients will be * added, now move the listeners we have to the fallback (if any) */ if (source->fallback_mount) { source_t *fallback_source; avl_tree_rlock(global.source_tree); fallback_source = source_find_mount (source->fallback_mount); if (fallback_source != NULL) source_move_clients (source, fallback_source); avl_tree_unlock (global.source_tree); } /* delete this sources stats */ stats_event_dec(NULL, "sources"); stats_event(source->mount, NULL, NULL); /* we don't remove the source from the tree here, it may be a relay and therefore reserved */ source_clear_source (source); global_lock(); global.sources--; global_unlock(); /* release our hold on the lock so the main thread can continue cleaning up */ thread_rwlock_unlock(source->shutdown_rwlock); }
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; }
int format_vorbis_get_buffer(format_plugin_t *self, char *data, unsigned long len, refbuf_t **buffer) { char *buf; int i, result; ogg_packet op; char *tag; refbuf_t *refbuf; vstate_t *state = (vstate_t *)self->_state; #ifdef USE_YP source_t *source; time_t current_time; #endif if (data) { /* write the data to the buffer */ buf = ogg_sync_buffer(&state->oy, len); memcpy(buf, data, len); ogg_sync_wrote(&state->oy, len); } refbuf = NULL; if (ogg_sync_pageout(&state->oy, &state->og) == 1) { refbuf = refbuf_new(state->og.header_len + state->og.body_len); memcpy(refbuf->data, state->og.header, state->og.header_len); memcpy(&refbuf->data[state->og.header_len], state->og.body, state->og.body_len); if (state->serialno != ogg_page_serialno(&state->og)) { /* this is a new logical bitstream */ state->header = 0; state->packets = 0; /* release old headers, stream state, vorbis data */ for (i = 0; i < MAX_HEADER_PAGES; i++) { if (state->headbuf[i]) { refbuf_release(state->headbuf[i]); state->headbuf[i] = NULL; } } /* Clear old stuff. Rarely but occasionally needed. */ ogg_stream_clear(&state->os); vorbis_comment_clear(&state->vc); vorbis_info_clear(&state->vi); state->serialno = ogg_page_serialno(&state->og); ogg_stream_init(&state->os, state->serialno); vorbis_info_init(&state->vi); vorbis_comment_init(&state->vc); } if (state->header >= 0) { /* FIXME: In some streams (non-vorbis ogg streams), this could get * extras pages beyond the header. We need to collect the pages * here anyway, but they may have to be discarded later. */ if (ogg_page_granulepos(&state->og) <= 0) { state->header++; } else { /* we're done caching headers */ state->header = -1; /* put known comments in the stats */ tag = vorbis_comment_query(&state->vc, "TITLE", 0); if (tag) stats_event(self->mount, "title", tag); else stats_event(self->mount, "title", "unknown"); tag = vorbis_comment_query(&state->vc, "ARTIST", 0); if (tag) stats_event(self->mount, "artist", tag); else stats_event(self->mount, "artist", "unknown"); /* don't need these now */ ogg_stream_clear(&state->os); vorbis_comment_clear(&state->vc); vorbis_info_clear(&state->vi); #ifdef USE_YP /* If we get an update on the mountpoint, force a yp touch */ avl_tree_rlock(global.source_tree); source = source_find_mount(self->mount); avl_tree_unlock(global.source_tree); if (source) { /* If we get an update on the mountpoint, force a yp touch */ current_time = time(NULL); for (i=0; i<source->num_yp_directories; i++) { source->ypdata[i]->yp_last_touch = current_time - source->ypdata[i]->yp_touch_interval + 2; } } #endif } } /* cache header pages */ if (state->header > 0 && state->packets < 3) { if(state->header > MAX_HEADER_PAGES) { refbuf_release(refbuf); ERROR1("Bad vorbis input: header is more than %d pages long", MAX_HEADER_PAGES); return -1; } refbuf_addref(refbuf); state->headbuf[state->header - 1] = refbuf; if (state->packets >= 0 && state->packets < 3) { ogg_stream_pagein(&state->os, &state->og); while (state->packets < 3) { result = ogg_stream_packetout(&state->os, &op); if (result == 0) break; /* need more data */ if (result < 0) { state->packets = -1; break; } state->packets++; if (vorbis_synthesis_headerin(&state->vi, &state->vc, &op) < 0) { state->packets = -1; break; } } } } } *buffer = refbuf; return 0; }
/* wrapper for starting the provided relay stream */ static void check_relay_stream (relay_server *relay) { if (relay->source == NULL) { if (relay->localmount[0] != '/') { ICECAST_LOG_WARN("relay mountpoint \"%s\" does not start with /, skipping", relay->localmount); return; } /* new relay, reserve the name */ relay->source = source_reserve (relay->localmount); if (relay->source) { ICECAST_LOG_DEBUG("Adding relay source at mountpoint \"%s\"", relay->localmount); if (relay->on_demand) { ice_config_t *config = config_get_config (); mount_proxy *mountinfo = config_find_mount (config, relay->localmount, MOUNT_TYPE_NORMAL); relay->source->on_demand = relay->on_demand; if (mountinfo == NULL) source_update_settings (config, relay->source, mountinfo); config_release_config (); stats_event (relay->localmount, "listeners", "0"); slave_update_all_mounts(); } } else { if (relay->start == 0) { ICECAST_LOG_WARN("new relay but source \"%s\" already exists", relay->localmount); relay->start = 1; } return; } } do { source_t *source = relay->source; /* skip relay if active, not configured or just not time yet */ if (relay->source == NULL || relay->running || relay->start > time(NULL)) break; /* check if an inactive on-demand relay has a fallback that has listeners */ if (relay->on_demand && source->on_demand_req == 0) { relay->source->on_demand = relay->on_demand; if (source->fallback_mount && source->fallback_override) { source_t *fallback; avl_tree_rlock (global.source_tree); fallback = source_find_mount (source->fallback_mount); if (fallback && fallback->running && fallback->listeners) { ICECAST_LOG_DEBUG("fallback running %d with %lu listeners", fallback->running, fallback->listeners); source->on_demand_req = 1; } avl_tree_unlock (global.source_tree); } if (source->on_demand_req == 0) break; } relay->start = time(NULL) + 5; relay->running = 1; relay->thread = thread_create ("Relay Thread", start_relay_stream, relay, THREAD_ATTACHED); return; } while (0); /* the relay thread may of shut down itself */ if (relay->cleanup) { if (relay->thread) { ICECAST_LOG_DEBUG("waiting for relay thread for \"%s\"", relay->localmount); thread_join (relay->thread); relay->thread = NULL; } relay->cleanup = 0; relay->running = 0; if (relay->on_demand && relay->source) { ice_config_t *config = config_get_config (); mount_proxy *mountinfo = config_find_mount (config, relay->localmount, MOUNT_TYPE_NORMAL); source_update_settings (config, relay->source, mountinfo); config_release_config (); stats_event (relay->localmount, "listeners", "0"); } } }
/* This does the actual connection for a relay. A thread is * started off if a connection can be acquired */ static void *start_relay_stream (void *arg) { relay_server *relay = arg; source_t *src = relay->source; client_t *client; ICECAST_LOG_INFO("Starting relayed source at mountpoint \"%s\"", relay->localmount); do { client = open_relay_connection (relay); if (client == NULL) continue; src->client = client; src->parser = client->parser; src->con = client->con; if (connection_complete_source (src, 0) < 0) { ICECAST_LOG_INFO("Failed to complete source initialisation"); client_destroy (client); src->client = NULL; continue; } stats_event_inc(NULL, "source_relay_connections"); stats_event (relay->localmount, "source_ip", client->con->ip); source_main (relay->source); if (relay->on_demand == 0) { /* only keep refreshing YP entries for inactive on-demand relays */ yp_remove (relay->localmount); relay->source->yp_public = -1; relay->start = time(NULL) + 10; /* prevent busy looping if failing */ slave_update_all_mounts(); } /* we've finished, now get cleaned up */ relay->cleanup = 1; slave_rebuild_mounts(); return NULL; } while (0); /* TODO allow looping through multiple servers */ if (relay->source->fallback_mount) { source_t *fallback_source; ICECAST_LOG_DEBUG("failed relay, fallback to %s", relay->source->fallback_mount); avl_tree_rlock(global.source_tree); fallback_source = source_find_mount(relay->source->fallback_mount); if (fallback_source != NULL) source_move_clients(relay->source, fallback_source); avl_tree_unlock(global.source_tree); } source_clear_source(relay->source); /* cleanup relay, but prevent this relay from starting up again too soon */ thread_mutex_lock(&_slave_mutex); thread_mutex_lock(&(config_locks()->relay_lock)); relay->source->on_demand = 0; relay->start = time(NULL) + max_interval; relay->cleanup = 1; thread_mutex_unlock(&(config_locks()->relay_lock)); thread_mutex_unlock(&_slave_mutex); return NULL; }
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); } }