/* update the specified source with details from the config or mount. * mountinfo can be NULL in which case default settings should be taken */ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy *mountinfo) { /* skip if source is a fallback to file */ if (source->running && source->client == NULL) { stats_event_hidden (source->mount, NULL, 1); return; } /* set global settings first */ source->queue_size_limit = config->queue_size_limit; source->timeout = config->source_timeout; source->burst_size = config->burst_size; stats_event_args (source->mount, "listenurl", "http://%s:%d%s", config->hostname, config->port, source->mount); source_apply_mount (source, mountinfo); if (source->fallback_mount) DEBUG1 ("fallback %s", source->fallback_mount); if (mountinfo && mountinfo->intro_filename) DEBUG1 ("intro file is %s", mountinfo->intro_filename); if (source->dumpfilename) DEBUG1 ("Dumping stream to %s", source->dumpfilename); if (mountinfo && mountinfo->on_connect) DEBUG1 ("connect script \"%s\"", mountinfo->on_connect); if (mountinfo && mountinfo->on_disconnect) DEBUG1 ("disconnect script \"%s\"", mountinfo->on_disconnect); if (source->on_demand) { DEBUG0 ("on_demand set"); stats_event (source->mount, "on_demand", "1"); stats_event_args (source->mount, "listeners", "%ld", source->listeners); } else stats_event (source->mount, "on_demand", NULL); if (source->hidden) { stats_event_hidden (source->mount, NULL, 1); DEBUG0 ("hidden from public"); } else stats_event_hidden (source->mount, NULL, 0); if (source->max_listeners == -1) stats_event (source->mount, "max_listeners", "unlimited"); else { char buf [10]; snprintf (buf, sizeof (buf), "%ld", source->max_listeners); stats_event (source->mount, "max_listeners", buf); } DEBUG1 ("public set to %d", source->yp_public); DEBUG1 ("max listeners to %ld", source->max_listeners); DEBUG1 ("queue size to %u", source->queue_size_limit); DEBUG1 ("burst size to %u", source->burst_size); DEBUG1 ("source timeout to %u", source->timeout); DEBUG1 ("fallback_when_full to %u", source->fallback_when_full); }
/* This is called when there has been a change in the metadata. Usually * artist and title are provided separately so here we update the stats * and write log entry if required. */ static void update_comments (source_t *source) { ogg_state_t *ogg_info = source->format->_state; char *title = ogg_info->title; char *artist = ogg_info->artist; char *metadata = NULL; unsigned int len = 0; ogg_codec_t *codec; char codec_names [100] = ""; if (ogg_info->artist) { if (title) { len += strlen(artist) + strlen(title) + 3; metadata = calloc (1, len); snprintf (metadata, len, "%s - %s", artist, title); } else { len += strlen(artist); metadata = calloc (1, len); snprintf (metadata, len, "%s", artist); } } else { if (title) { len += strlen (title); metadata = calloc (1, len); snprintf (metadata, len, "%s", title); } } if (metadata) { logging_playlist (source->mount, metadata, source->listeners); free (metadata); } stats_event (source->mount, "artist", artist); stats_event (source->mount, "title", title); codec = ogg_info->codecs; while (codec) { if (codec->name) { int len = strlen (codec_names); int remaining = sizeof (codec_names) - len; char *where = codec_names + len; char *separator = "/"; if (len == 0) separator = ""; snprintf (where, remaining, "%s%s", separator, codec->name); } codec = codec->next; } stats_event (source->mount, "subtype", codec_names); yp_touch (source->mount); }
void stats_global (ice_config_t *config) { stats_event (NULL, "server_id", config->server_id); stats_event (NULL, "host", config->hostname); stats_event (NULL, "location", config->location); stats_event (NULL, "admin", config->admin); }
void source_client_callback (client_t *client, void *arg) { const char *agent; source_t *source = arg; refbuf_t *old_data = client->refbuf; if (client->con->error) { global_lock(); global.sources--; global_unlock(); source_clear_source (source); source_free_source (source); return; } client->refbuf = old_data->associated; old_data->associated = NULL; refbuf_release (old_data); stats_event (source->mount, "source_ip", source->client->con->ip); agent = httpp_getvar (source->client->parser, "user-agent"); if (agent) stats_event (source->mount, "user_agent", agent); thread_create ("Source Thread", source_client_thread, source, THREAD_DETACHED); }
/* 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(); }
/* called after each xml reload */ void stats_global (ice_config_t *config) { stats_event_flags (NULL, "server_id", config->server_id, STATS_GENERAL); stats_event_flags (NULL, "host", config->hostname, STATS_GENERAL); stats_event (NULL, "location", config->location); stats_event (NULL, "admin", config->admin); thread_spin_lock (&global.spinlock); global.max_rate = config->max_bandwidth; throttle_sends = 0; thread_spin_unlock (&global.spinlock); }
static void flac_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) { ICECAST_LOG_DEBUG("freeing FLAC codec"); stats_event (ogg_info->mount, "FLAC_version", NULL); ogg_stream_clear (&codec->os); free (codec); }
static void filter_shoutcast_metadata (source_t *source, char *metadata, unsigned int meta_len) { if (metadata) { char *end, *p; int len; do { metadata++; if (strncmp (metadata, "StreamTitle='", 13)) break; if ((end = strstr (metadata, "\';")) == NULL) break; len = (end - metadata) - 13; p = calloc (1, len+1); if (p) { memcpy (p, metadata+13, len); logging_playlist (source->mount, p, source->listeners); stats_event (source->mount, "title", p); yp_touch (source->mount); free (p); } } while (0); } }
void stats_initialize(void) { if (_stats_running) return; /* set up global struct */ _stats.global_tree = avl_tree_new(_compare_stats, NULL); _stats.source_tree = avl_tree_new(_compare_source_stats, NULL); _stats.event_listeners = NULL; thread_mutex_create (&_stats.listeners_lock); _stats_running = 1; stats_event_time (NULL, "server_start", STATS_GENERAL); /* global currently active stats */ stats_event_flags (NULL, "clients", "0", STATS_COUNTERS); stats_event_flags (NULL, "connections", "0", STATS_COUNTERS); stats_event_flags (NULL, "sources", "0", STATS_COUNTERS); stats_event_flags (NULL, "stats", "0", STATS_COUNTERS); stats_event_flags (NULL, "banned_IPs", "0", STATS_COUNTERS); stats_event (NULL, "listeners", "0"); /* global accumulating stats */ stats_event_flags (NULL, "client_connections", "0", STATS_COUNTERS); stats_event_flags (NULL, "source_client_connections", "0", STATS_COUNTERS); stats_event_flags (NULL, "source_relay_connections", "0", STATS_COUNTERS); stats_event_flags (NULL, "source_total_connections", "0", STATS_COUNTERS); stats_event_flags (NULL, "stats_connections", "0", STATS_COUNTERS); stats_event_flags (NULL, "listener_connections", "0", STATS_COUNTERS); stats_event_flags (NULL, "outgoing_kbitrate", "0", STATS_COUNTERS|STATS_REGULAR); stats_event_flags (NULL, "stream_kbytes_sent", "0", STATS_COUNTERS|STATS_REGULAR); stats_event_flags (NULL, "stream_kbytes_read", "0", STATS_COUNTERS|STATS_REGULAR); }
void stats_event_time_iso8601 (const char *mount, const char *name) { char buffer[100]; __format_time(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S"); stats_event (mount, name, buffer); }
/* wrapper for stats_event, this takes a charset to convert from */ void stats_event_conv(const char *mount, const char *name, const char *value, const char *charset) { const char *metadata = value; xmlBufferPtr conv = xmlBufferCreate (); if (charset && value) { xmlCharEncodingHandlerPtr handle = xmlFindCharEncodingHandler (charset); if (handle) { xmlBufferPtr raw = xmlBufferCreate (); xmlBufferAdd (raw, (const xmlChar *)value, strlen (value)); if (xmlCharEncInFunc (handle, conv, raw) > 0) metadata = (char *)xmlBufferContent (conv); xmlBufferFree (raw); xmlCharEncCloseFunc (handle); } else WARN1 ("No charset found for \"%s\"", charset); } stats_event (mount, name, metadata); xmlBufferFree (conv); }
void stats_event_time (const char *mount, const char *name) { char buffer[100]; __format_time(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S "); stats_event (mount, name, buffer); }
static void _parse_audio_info (source_t *source, const char *s) { const char *start = s; unsigned int len; while (start != NULL && *start != '\0') { if ((s = strchr (start, ';')) == NULL) len = strlen (start); else { len = (int)(s - start); s++; /* skip passed the ';' */ } if (len) { char name[100], value[200]; char *esc; sscanf (start, "%99[^=]=%199[^;\r\n]", name, value); esc = util_url_unescape (value); if (esc) { util_dict_set (source->audio_info, name, esc); stats_event (source->mount, name, esc); free (esc); } } start = s; } }
void stats_event_time (const char *mount, const char *name) { time_t now = time(NULL); struct tm local; char buffer[100]; localtime_r (&now, &local); strftime (buffer, sizeof (buffer), "%a, %d %b %Y %H:%M:%S %z", &local); stats_event (mount, name, buffer); }
static void vorbis_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) { vorbis_codec_t *vorbis = codec->specific; DEBUG0 ("freeing vorbis codec"); stats_event (ogg_info->mount, "audio_bitrate", NULL); stats_event (ogg_info->mount, "audio_channels", NULL); stats_event (ogg_info->mount, "audio_samplerate", NULL); vorbis_info_clear (&vorbis->vi); vorbis_comment_clear (&vorbis->vc); ogg_stream_clear (&codec->os); ogg_stream_clear (&vorbis->new_os); free_ogg_packet (vorbis->header[0]); free_ogg_packet (vorbis->header[1]); free_ogg_packet (vorbis->header[2]); free_ogg_packet (vorbis->prev_packet); free (vorbis->bos_page.header); free (vorbis); free (codec); }
void stats_event_args(char *source, char *name, char *format, ...) { char buf[1024]; va_list val; va_start(val, format); vsnprintf(buf, 1024, format, val); va_end(val); stats_event(source, name, buf); }
void *source_client_thread (void *arg) { source_t *source = arg; stats_event_inc(NULL, "source_client_connections"); stats_event (source->mount, "listeners", "0"); source_main (source); source_free_source (source); slave_update_all_mounts(); return NULL; }
void fserve_initialize(void) { ice_config_t *config = config_get_config(); mimetypes = NULL; active_list = NULL; pending_list = NULL; thread_spin_create (&pending_lock); fserve_recheck_mime_types (config); config_release_config(); stats_event (NULL, "file_connections", "0"); INFO0("file serving started"); }
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); }
void source_apply_mount (source_t *source, mount_proxy *mountinfo) { DEBUG1("Applying mount information for \"%s\"", source->mount); source->max_listeners = mountinfo->max_listeners; source->fallback_override = mountinfo->fallback_override; source->no_mount = mountinfo->no_mount; source->hidden = mountinfo->hidden; stats_event_hidden (source->mount, NULL, source->hidden); if (mountinfo->fallback_mount) { source->fallback_mount = strdup (mountinfo->fallback_mount); DEBUG1 ("fallback %s", mountinfo->fallback_mount); } if (mountinfo->auth_type != NULL) { source->authenticator = auth_get_authenticator( mountinfo->auth_type, mountinfo->auth_options); stats_event(source->mount, "authenticator", mountinfo->auth_type); } if (mountinfo->dumpfile) { DEBUG1("Dumping stream to %s", mountinfo->dumpfile); source->dumpfilename = strdup (mountinfo->dumpfile); } if (mountinfo->queue_size_limit) { source->queue_size_limit = mountinfo->queue_size_limit; DEBUG1 ("queue size to %u", source->queue_size_limit); } if (mountinfo->source_timeout) { source->timeout = mountinfo->source_timeout; DEBUG1 ("source timeout to %u", source->timeout); } if (mountinfo->no_yp) { source->yp_prevent = 1; DEBUG0 ("preventing YP listings"); } if (mountinfo->burst_size > -1) source->burst_size = mountinfo->burst_size; DEBUG1 ("amount to burst on client connect set to %u", source->burst_size); }
int format_get_plugin(format_type_t type, source_t *source) { int ret = -1; switch (type) { case FORMAT_TYPE_OGG: ret = format_ogg_get_plugin (source); break; case FORMAT_TYPE_GENERIC: ret = format_mp3_get_plugin (source); break; default: break; } if (ret < 0) stats_event (source->mount, "content-type", source->format->contenttype); return ret; }
/* printf style formatting for stat create/update */ void stats_event_args(const char *source, char *name, char *format, ...) { va_list val; int ret; char buf[1024]; if (name == NULL) return; va_start(val, format); ret = vsnprintf(buf, sizeof (buf), format, val); va_end(val); if (ret < 0 || (unsigned int)ret >= sizeof (buf)) { WARN2 ("problem with formatting %s stat %s", source==NULL ? "global" : source, name); return; } stats_event(source, name, buf); }
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); }
/* Apply the mountinfo details to the source */ static void source_apply_mount (source_t *source, mount_proxy *mountinfo) { const char *str; int val; http_parser_t *parser = NULL; DEBUG1("Applying mount information for \"%s\"", source->mount); avl_tree_rlock (source->client_tree); stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners); if (mountinfo) { source->max_listeners = mountinfo->max_listeners; source->fallback_override = mountinfo->fallback_override; source->hidden = mountinfo->hidden; } /* if a setting is available in the mount details then use it, else * check the parser details. */ if (source->client) parser = source->client->parser; /* to be done before possible non-utf8 stats */ if (source->format && source->format->apply_settings) source->format->apply_settings (source->client, source->format, mountinfo); /* public */ if (mountinfo && mountinfo->yp_public >= 0) val = mountinfo->yp_public; else { do { str = httpp_getvar (parser, "ice-public"); if (str) break; str = httpp_getvar (parser, "icy-pub"); if (str) break; str = httpp_getvar (parser, "x-audiocast-public"); if (str) break; /* handle header from icecast v2 release */ str = httpp_getvar (parser, "icy-public"); if (str) break; str = "0"; } while (0); val = atoi (str); } stats_event_args (source->mount, "public", "%d", val); if (source->yp_public != val) { DEBUG1 ("YP changed to %d", val); if (val) yp_add (source->mount); else yp_remove (source->mount); source->yp_public = val; } /* stream name */ if (mountinfo && mountinfo->stream_name) stats_event (source->mount, "server_name", mountinfo->stream_name); else { do { str = httpp_getvar (parser, "ice-name"); if (str) break; str = httpp_getvar (parser, "icy-name"); if (str) break; str = httpp_getvar (parser, "x-audiocast-name"); if (str) break; str = "Unspecified name"; } while (0); if (source->format) stats_event_conv (source->mount, "server_name", str, source->format->charset); } /* stream description */ if (mountinfo && mountinfo->stream_description) stats_event (source->mount, "server_description", mountinfo->stream_description); else { do { str = httpp_getvar (parser, "ice-description"); if (str) break; str = httpp_getvar (parser, "icy-description"); if (str) break; str = httpp_getvar (parser, "x-audiocast-description"); if (str) break; str = "Unspecified description"; } while (0); if (source->format) stats_event_conv (source->mount, "server_description", str, source->format->charset); } /* stream URL */ if (mountinfo && mountinfo->stream_url) stats_event (source->mount, "server_url", mountinfo->stream_url); else { do { str = httpp_getvar (parser, "ice-url"); if (str) break; str = httpp_getvar (parser, "icy-url"); if (str) break; str = httpp_getvar (parser, "x-audiocast-url"); if (str) break; } while (0); if (str && source->format) stats_event_conv (source->mount, "server_url", str, source->format->charset); } /* stream genre */ if (mountinfo && mountinfo->stream_genre) stats_event (source->mount, "genre", mountinfo->stream_genre); else { do { str = httpp_getvar (parser, "ice-genre"); if (str) break; str = httpp_getvar (parser, "icy-genre"); if (str) break; str = httpp_getvar (parser, "x-audiocast-genre"); if (str) break; str = "various"; } while (0); if (source->format) stats_event_conv (source->mount, "genre", str, source->format->charset); } /* stream bitrate */ if (mountinfo && mountinfo->bitrate) str = mountinfo->bitrate; else { do { str = httpp_getvar (parser, "ice-bitrate"); if (str) break; str = httpp_getvar (parser, "icy-br"); if (str) break; str = httpp_getvar (parser, "x-audiocast-bitrate"); } while (0); } stats_event (source->mount, "bitrate", str); /* handle MIME-type */ if (mountinfo && mountinfo->type) stats_event (source->mount, "server_type", mountinfo->type); else if (source->format) stats_event (source->mount, "server_type", source->format->contenttype); if (mountinfo && mountinfo->subtype) stats_event (source->mount, "subtype", mountinfo->subtype); if (mountinfo && mountinfo->auth) stats_event (source->mount, "authenticator", mountinfo->auth->type); else stats_event (source->mount, "authenticator", NULL); if (mountinfo && mountinfo->fallback_mount) { char *mount = source->fallback_mount; source->fallback_mount = strdup (mountinfo->fallback_mount); free (mount); } else source->fallback_mount = NULL; if (mountinfo && mountinfo->dumpfile) { char *filename = source->dumpfilename; source->dumpfilename = strdup (mountinfo->dumpfile); free (filename); } else source->dumpfilename = NULL; if (source->intro_file) { fclose (source->intro_file); source->intro_file = NULL; } if (mountinfo && mountinfo->intro_filename) { ice_config_t *config = config_get_config_unlocked (); unsigned int len = strlen (config->webroot_dir) + strlen (mountinfo->intro_filename) + 2; char *path = malloc (len); if (path) { FILE *f; snprintf (path, len, "%s" PATH_SEPARATOR "%s", config->webroot_dir, mountinfo->intro_filename); f = fopen (path, "rb"); if (f) source->intro_file = f; else WARN2 ("Cannot open intro file \"%s\": %s", path, strerror(errno)); free (path); } } if (mountinfo && mountinfo->queue_size_limit) source->queue_size_limit = mountinfo->queue_size_limit; if (mountinfo && mountinfo->source_timeout) source->timeout = mountinfo->source_timeout; if (mountinfo && mountinfo->burst_size >= 0) source->burst_size = (unsigned int)mountinfo->burst_size; if (mountinfo && mountinfo->fallback_when_full) source->fallback_when_full = mountinfo->fallback_when_full; avl_tree_unlock (source->client_tree); }
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; }
static void *_stats_thread(void *arg) { stats_event_t *event; stats_event_t *copy; event_listener_t *listener; stats_event_time (NULL, "server_start"); stats_event_time_iso8601 (NULL, "server_start_iso8601"); /* global currently active stats */ stats_event (NULL, "clients", "0"); stats_event (NULL, "connections", "0"); stats_event (NULL, "sources", "0"); stats_event (NULL, "stats", "0"); stats_event (NULL, "listeners", "0"); /* global accumulating stats */ stats_event (NULL, "client_connections", "0"); stats_event (NULL, "source_client_connections", "0"); stats_event (NULL, "source_relay_connections", "0"); stats_event (NULL, "source_total_connections", "0"); stats_event (NULL, "stats_connections", "0"); stats_event (NULL, "listener_connections", "0"); ICECAST_LOG_INFO("stats thread started"); while (_stats_running) { thread_mutex_lock(&_global_event_mutex); if (_global_event_queue.head != NULL) { /* grab the next event from the queue */ event = _get_event_from_queue (&_global_event_queue); thread_mutex_unlock(&_global_event_mutex); if (event == NULL) continue; event->next = NULL; thread_mutex_lock(&_stats_mutex); /* check if we are dealing with a global or source event */ if (event->source == NULL) process_global_event (event); else process_source_event (event); /* now we have an event that's been processed into the running stats */ /* this event should get copied to event listeners' queues */ listener = (event_listener_t *)_event_listeners; while (listener) { copy = _copy_event(event); thread_mutex_lock (&listener->mutex); _add_event_to_queue (copy, &listener->queue); thread_mutex_unlock (&listener->mutex); listener = listener->next; } /* now we need to destroy the event */ _free_event(event); thread_mutex_unlock(&_stats_mutex); continue; } else { thread_mutex_unlock(&_global_event_mutex); } thread_sleep(300000); } return NULL; }
/* 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; }
static int command_metadata (client_t *client, source_t *source, int response) { const char *song, *title, *artist, *artwork, *charset, *url; format_plugin_t *plugin; xmlDocPtr doc; xmlNodePtr node; int same_ip = 1; doc = xmlNewDoc(XMLSTR("1.0")); node = xmlNewDocNode(doc, NULL, XMLSTR("iceresponse"), NULL); xmlDocSetRootElement(doc, node); DEBUG0("Got metadata update request"); COMMAND_OPTIONAL(client, "song", song); COMMAND_OPTIONAL(client, "title", title); COMMAND_OPTIONAL(client, "artist", artist); COMMAND_OPTIONAL(client, "url", url); COMMAND_OPTIONAL(client, "artwork", artwork); COMMAND_OPTIONAL(client, "charset", charset); plugin = source->format; if (source_running (source)) if (strcmp (client->connection.ip, source->client->connection.ip) != 0) if (response == RAW && connection_check_admin_pass (client->parser) == 0) same_ip = 0; do { if (same_ip == 0 || plugin == NULL) break; if (artwork) stats_event (source->mount, "artwork", artwork); if (plugin->set_tag) { if (url) { plugin->set_tag (plugin, "url", url, charset); INFO2 ("Metadata url on %s set to \"%s\"", source->mount, url); } if (song) { plugin->set_tag (plugin, "artist", NULL, NULL); plugin->set_tag (plugin, "title", song, charset); INFO2("Metadata song on %s set to \"%s\"", source->mount, song); } if (artist) { plugin->set_tag (plugin, "artist", artist, charset); INFO2 ("Metadata artist on %s changed to \"%s\"", source->mount, artist); } if (title) { plugin->set_tag (plugin, "title", title, charset); INFO2 ("Metadata title on %s changed to \"%s\"", source->mount, title); } /* updates are now done, let them be pushed into the stream */ plugin->set_tag (plugin, NULL, NULL, charset); } else { break; } thread_mutex_unlock (&source->lock); xmlNewChild(node, NULL, XMLSTR("message"), XMLSTR("Metadata update successful")); xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("1")); return admin_send_response(doc, client, response, "response.xsl"); } while (0); INFO1 ("Metadata on mountpoint %s prevented", source->mount); thread_mutex_unlock (&source->lock); xmlNewChild(node, NULL, XMLSTR("message"), XMLSTR("Mountpoint will not accept this URL update")); xmlNewChild(node, NULL, XMLSTR("return"), XMLSTR("1")); return admin_send_response(doc, client, response, "response.xsl"); }
static unsigned do_yp_touch (ypdata_t *yp, char *s, unsigned len) { unsigned listeners = 0, max_listeners = 1; char *val, *artist, *title; int ret; artist = (char *)stats_get_value (yp->mount, "artist"); title = (char *)stats_get_value (yp->mount, "title"); if (artist || title) { char *song; char *separator = " - "; if (artist == NULL) { artist = strdup(""); separator = ""; } if (title == NULL) title = strdup(""); song = malloc (strlen (artist) + strlen (title) + strlen (separator) +1); if (song) { sprintf (song, "%s%s%s", artist, separator, title); add_yp_info(yp, song, YP_CURRENT_SONG); stats_event (yp->mount, "yp_currently_playing", song); free (song); } } free (artist); free (title); val = (char *)stats_get_value (yp->mount, "listeners"); if (val) { listeners = atoi (val); free (val); } val = stats_get_value (yp->mount, "max_listeners"); if (val == NULL || strcmp (val, "unlimited") == 0) { free (val); max_listeners = client_limit; } else max_listeners = atoi (val); val = stats_get_value (yp->mount, "subtype"); if (val) { add_yp_info (yp, val, YP_SUBTYPE); free (val); } ret = snprintf (s, len, "action=touch&sid=%s&st=%s" "&listeners=%u&max_listeners=%u&stype=%s\r\n", yp->sid, yp->current_song, listeners, max_listeners, yp->subtype); if (ret >= (signed)len) return ret+1; /* space required for above text and nul*/ send_to_yp ("touch", yp, s); return 0; }