static int wait_for_fds(void) { fserve_t *fclient; int ret; while (run_fserv) { /* add any new clients here */ if (pending_list) { thread_spin_lock (&pending_lock); fclient = (fserve_t*)pending_list; while (fclient) { fserve_t *to_move = fclient; fclient = fclient->next; to_move->next = active_list; active_list = to_move; client_tree_changed = 1; fserve_clients++; } pending_list = NULL; thread_spin_unlock(&pending_lock); } /* drop out of here if someone is ready */ ret = fserve_client_waiting(); if (ret) return ret; } return -1; }
// write filename extension for matching mime type. // lookup matching mime type and write extension into buffer space provided void fserve_write_mime_ext (const char *mimetype, char *buf, unsigned int len) { avl_node *node; int semi; if (mimetype == NULL || buf == NULL || len > 2000) return; semi = strcspn (mimetype, "; "); if (semi == 0) return; if (mimetype [semi]) { char *mt = alloca (++semi); snprintf (mt, semi, "%s", mimetype); mimetype = (const char *)mt; } thread_spin_lock (&pending_lock); node = avl_get_first (mimetypes); while (node) { mime_type *mime = (mime_type *)node->key; if (mime && strcmp (mime->type, mimetype) == 0) { snprintf (buf, len, "%s", mime->ext); break; } node = avl_get_next (node); } thread_spin_unlock (&pending_lock); }
/* add client to connection queue. At this point some header information * has been collected, so we now pass it onto the connection thread for * further processing */ static void _add_connection (client_queue_t *node) { thread_spin_lock (&_connection_lock); *_con_queue_tail = node; _con_queue_tail = (volatile client_queue_t **)&node->next; thread_spin_unlock (&_connection_lock); }
void fserve_shutdown(void) { if (!__inited) return; thread_spin_lock (&pending_lock); run_fserv = 0; while (pending_list) { fserve_t *to_go = (fserve_t *)pending_list; pending_list = to_go->next; fserve_client_destroy (to_go); } while (active_list) { fserve_t *to_go = active_list; active_list = to_go->next; fserve_client_destroy (to_go); } if (mimetypes) avl_tree_free (mimetypes, _delete_mapping); thread_spin_unlock (&pending_lock); thread_spin_destroy (&pending_lock); ICECAST_LOG_INFO("file serving stopped"); }
unsigned long global_getrate_avg (struct rate_calc *rate) { unsigned long v; thread_spin_lock (&global.spinlock); v = rate_avg (rate); thread_spin_unlock (&global.spinlock); return v; }
static unsigned long _next_connection_id(void) { unsigned long id; thread_spin_lock (&_connection_lock); id = _current_id++; thread_spin_unlock (&_connection_lock); return id; }
static uint64_t _next_connection_id(void) { uint64_t id; thread_spin_lock (&_connection_lock); id = _current_id++; thread_spin_unlock (&_connection_lock); return id; }
/* 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); }
int client_change_worker (client_t *client, worker_t *dest_worker) { if (dest_worker->running == 0) return 0; client->next_on_worker = NULL; thread_spin_lock (&dest_worker->lock); worker_add_client (dest_worker, client); thread_spin_unlock (&dest_worker->lock); worker_wakeup (dest_worker); return 1; }
void client_add_incoming (client_t *client) { worker_t *handler; thread_rwlock_rlock (&workers_lock); handler = worker_incoming; thread_spin_lock (&handler->lock); thread_rwlock_unlock (&workers_lock); worker_add_client (handler, client); thread_spin_unlock (&handler->lock); worker_wakeup (handler); }
/* Routine to actually add pre-configured client structure to pending list and * then to start off the file serving thread if it is not already running */ static void fserve_add_pending (fserve_t *fclient) { thread_spin_lock (&pending_lock); fclient->next = (fserve_t *)pending_list; pending_list = fclient; if (run_fserv == 0) { run_fserv = 1; ICECAST_LOG_DEBUG("fserve handler waking up"); thread_create("File Serving Thread", fserv_thread_function, NULL, THREAD_DETACHED); } thread_spin_unlock (&pending_lock); }
void client_add_worker (client_t *client) { worker_t *handler; thread_rwlock_rlock (&workers_lock); /* add client to the handler with the least number of clients */ handler = worker_selected(); thread_spin_lock (&handler->lock); thread_rwlock_unlock (&workers_lock); worker_add_client (handler, client); thread_spin_unlock (&handler->lock); worker_wakeup (handler); }
int fserve_client_waiting(void) { fserve_t *fclient; fd_set realfds; /* only rebuild fds if there are clients added/removed */ if (client_tree_changed) { client_tree_changed = 0; FD_ZERO(&fds); fd_max = SOCK_ERROR; fclient = active_list; while (fclient) { FD_SET(fclient->client->con->sock, &fds); if (fclient->client->con->sock > fd_max || fd_max == SOCK_ERROR) fd_max = fclient->client->con->sock; fclient = fclient->next; } } /* hack for windows, select needs at least 1 descriptor */ if (fd_max == SOCK_ERROR) { thread_spin_lock (&pending_lock); run_fserv = 0; thread_spin_unlock (&pending_lock); return -1; } else { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 200000; /* make a duplicate of the set so we do not have to rebuild it * each time around */ memcpy(&realfds, &fds, sizeof(fd_set)); if(select(fd_max+1, NULL, &realfds, NULL, &tv) > 0) { /* mark any clients that are ready */ fclient = active_list; while (fclient) { if (FD_ISSET (fclient->client->con->sock, &realfds)) fclient->ready = 1; fclient = fclient->next; } return 1; } } return 0; }
/* thread to read xsl file and add to the cache */ void *xslt_update (void *arg) { xsl_req *x = arg; client_t *client = x->client; char *fn = x->cache.filename; xsltStylesheetPtr sheet; xmlSetStructuredErrorFunc ("xsl/file", config_xml_parse_failure); xsltSetGenericErrorFunc ("", log_parse_failure); sheet = x->cache.stylesheet = xsltParseStylesheetFile (XMLSTR(fn)); if (sheet) { int i; INFO1 ("loaded stylesheet %s", x->cache.filename); if (sheet->mediaType && strcmp ((char*)sheet->mediaType, "text/html") != 0) { // avoid this lookup for html pages const char _hdr[] = "Content-Disposition: attachment; filename=\"file."; const size_t _hdrlen = sizeof (_hdr); size_t len = _hdrlen + 12; char *filename = malloc (len); // enough for name and extension strcpy (filename, _hdr); fserve_write_mime_ext ((char*)sheet->mediaType, filename + _hdrlen - 1, len - _hdrlen - 4); strcat (filename, "\"\r\n"); x->cache.disposition = filename; } // we now have a sheet, find and update. thread_rwlock_wlock (&xslt_lock); i = xslt_cached (fn, &x->cache, time(NULL)); xslt_send_sheet (client, x->doc, i); } else { WARN1 ("problem reading stylesheet \"%s\"", x->cache.filename); free (fn); xmlFreeDoc (x->doc); free (x->cache.disposition); client->shared_data = NULL; client_send_404 (client, "Could not parse XSLT file"); } thread_spin_lock (&update_lock); xsl_updating--; thread_spin_unlock (&update_lock); free (x); return NULL; }
/* string returned needs to be free'd */ char *fserve_content_type (const char *path) { char *ext = util_get_extension(path); mime_type exttype = { NULL, NULL }; void *result; char *type; if (ext == NULL) return strdup ("text/html"); exttype.ext = strdup (ext); thread_spin_lock (&pending_lock); if (mimetypes && !avl_get_by_key (mimetypes, &exttype, &result)) { mime_type *mime = result; free (exttype.ext); type = strdup (mime->type); } else { free (exttype.ext); /* Fallbacks for a few basic ones */ if(!strcmp(ext, "ogg")) type = strdup ("application/ogg"); else if(!strcmp(ext, "mp3")) type = strdup ("audio/mpeg"); else if(!strcmp(ext, "html")) type = strdup ("text/html"); else if(!strcmp(ext, "css")) type = strdup ("text/css"); else if(!strcmp(ext, "txt")) type = strdup ("text/plain"); else if(!strcmp(ext, "jpg")) type = strdup ("image/jpeg"); else if(!strcmp(ext, "png")) type = strdup ("image/png"); else if(!strcmp(ext, "m3u")) type = strdup ("audio/x-mpegurl"); else if(!strcmp(ext, "aac")) type = strdup ("audio/aac"); else type = strdup ("application/octet-stream"); } thread_spin_unlock (&pending_lock); return type; }
/* this returns queued clients for the connection thread. headers are * already provided, but need to be parsed. */ static client_queue_t *_get_connection(void) { client_queue_t *node = NULL; thread_spin_lock (&_connection_lock); if (_con_queue) { node = (client_queue_t *)_con_queue; _con_queue = node->next; if (_con_queue == NULL) _con_queue_tail = &_con_queue; node->next = NULL; } thread_spin_unlock (&_connection_lock); return node; }
int fserve_client_waiting (void) { fserve_t *fclient; unsigned int i = 0; /* only rebuild ufds if there are clients added/removed */ if (client_tree_changed) { struct pollfd *ufds_new = realloc(ufds, fserve_clients * sizeof(struct pollfd)); /* REVIEW: If we can not allocate new ufds, keep old ones for now. */ if (ufds_new || fserve_clients == 0) { ufds = ufds_new; client_tree_changed = 0; fclient = active_list; while (fclient) { ufds[i].fd = fclient->client->con->sock; ufds[i].events = POLLOUT; ufds[i].revents = 0; fclient = fclient->next; i++; } } } if (!ufds) { thread_spin_lock (&pending_lock); run_fserv = 0; thread_spin_unlock (&pending_lock); return -1; } else if (poll(ufds, fserve_clients, 200) > 0) { /* mark any clients that are ready */ fclient = active_list; for (i=0; i<fserve_clients; i++) { if (ufds[i].revents & (POLLOUT|POLLHUP|POLLERR)) fclient->ready = 1; fclient = fclient->next; } return 1; } return 0; }
void global_add_bitrates (struct rate_calc *rate, unsigned long value, uint64_t milli) { float avg; thread_spin_lock (&global.spinlock); rate_add (rate, value, milli); avg = rate_avg (rate); if (global.max_rate) { float ratio = avg / global.max_rate; if (ratio > 0.99) throttle_sends = 3; else if (ratio > 0.9) throttle_sends = 2; else if (ratio > 0.8) throttle_sends = 1; else if (throttle_sends > 0) throttle_sends--; } thread_spin_unlock (&global.spinlock); }
/* thread to read xsl file and add to the cache */ void *xslt_update (void *arg) { xsl_req *x = arg; client_t *client = x->client; worker_t *worker = client ? client->worker : NULL; char *fn = x->cache.filename; x->cache.stylesheet = xsltParseStylesheetFile (XMLSTR(fn)); if (x->cache.stylesheet) { int i = x->index; if (client) fn = strdup (fn); // need to copy the filename if another lookup is to do INFO1 ("loaded stylesheet %s", x->cache.filename); thread_rwlock_wlock (&xslt_lock); free (cache[i].filename); xsltFreeStylesheet (cache[i].stylesheet); memcpy (&cache[i], &x->cache, sizeof (stylesheet_cache_t)); thread_rwlock_unlock (&xslt_lock); memset (&x->cache, 0, sizeof (stylesheet_cache_t)); if (client) { x->cache.filename = fn; client->flags |= CLIENT_ACTIVE; } } else { WARN1 ("problem reading stylesheet \"%s\"", x->cache.filename); free (fn); if (client) client_send_404 (client, "Could not parse XSLT file"); } thread_spin_lock (&update_lock); xsl_updating--; thread_spin_unlock (&update_lock); if (worker) worker_wakeup (worker); // wakeup after the decrease or it may delay if (client == NULL) free (x); return NULL; }
static void worker_relocate_clients (worker_t *worker) { if (workers == NULL) return; while (worker->count || worker->pending_count) { client_t *client = worker->clients, **prevp = &worker->clients; worker->wakeup_ms = worker->time_ms + 150; worker->current_time.tv_sec = (time_t)(worker->time_ms/1000); while (client) { if (client->flags & CLIENT_ACTIVE) { client->worker = workers; prevp = &client->next_on_worker; } else { *prevp = client->next_on_worker; worker_add_client (worker, client); worker->count--; } client = *prevp; } if (worker->clients) { thread_spin_lock (&workers->lock); *workers->pending_clients_tail = worker->clients; workers->pending_clients_tail = prevp; workers->pending_count += worker->count; thread_spin_unlock (&workers->lock); worker_wakeup (workers); worker->clients = NULL; worker->last_p = &worker->clients; worker->count = 0; } worker_wait (worker); } }
/* string returned needs to be free'd */ char *fserve_content_type (const char *path) { char *ext = util_get_extension(path); mime_type exttype = { NULL, NULL }; void *result; char *type; if (ext == NULL) return strdup ("text/html"); exttype.ext = strdup (ext); thread_spin_lock (&pending_lock); if (mimetypes && !avl_get_by_key (mimetypes, &exttype, &result)) { mime_type *mime = result; type = strdup (mime->type); } else type = strdup ("application/octet-stream"); thread_spin_unlock (&pending_lock); free (exttype.ext); return type; }
void fserve_recheck_mime_types (ice_config_t *config) { mime_type *mapping; int i; avl_tree *old_mimetypes = NULL, *new_mimetypes = avl_tree_new(_compare_mappings, NULL); mime_type defaults[] = { { "m3u", "audio/x-mpegurl" }, { "pls", "audio/x-scpls" }, { "xspf", "application/xspf+xml" }, { "ogg", "application/ogg" }, { "mp3", "audio/mpeg" }, { "aac", "audio/aac" }, { "aacp", "audio/aacp" }, { "css", "text/css" }, { "txt", "text/plain" }, { "html", "text/html" }, { "jpg", "image/jpg" }, { "png", "image/png" }, { "gif", "image/gif" }, { NULL, NULL } }; for (i=0; defaults[i].ext; i++) { mapping = malloc (sizeof(mime_type)); mapping->ext = strdup (defaults [i].ext); mapping->type = strdup (defaults [i].type); if (avl_insert (new_mimetypes, mapping) != 0) _delete_mapping (mapping); } do { char *type, *ext, *cur; FILE *mimefile = NULL; char line[4096]; if (config->mimetypes_fn == NULL) { INFO0 ("no mime types file defined, using defaults"); break; } mimefile = fopen (config->mimetypes_fn, "r"); if (mimefile == NULL) { WARN1 ("Cannot open mime types file %s, using defaults", config->mimetypes_fn); break; } while (fgets(line, sizeof line, mimefile)) { line[4095] = 0; if(*line == 0 || *line == '#') continue; type = line; cur = line; while(*cur != ' ' && *cur != '\t' && *cur) cur++; if(*cur == 0) continue; *cur++ = 0; while(1) { while(*cur == ' ' || *cur == '\t') cur++; if(*cur == 0) break; ext = cur; while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur) cur++; *cur++ = 0; if(*ext) { void *tmp; /* Add a new extension->type mapping */ mapping = malloc(sizeof(mime_type)); mapping->ext = strdup(ext); mapping->type = strdup(type); if (!avl_get_by_key (new_mimetypes, mapping, &tmp)) avl_delete (new_mimetypes, mapping, _delete_mapping); if (avl_insert (new_mimetypes, mapping) != 0) _delete_mapping (mapping); } } } fclose(mimefile); } while (0); thread_spin_lock (&pending_lock); old_mimetypes = mimetypes; mimetypes = new_mimetypes; thread_spin_unlock (&pending_lock); if (old_mimetypes) avl_tree_free (old_mimetypes, _delete_mapping); }
static int xslt_req_sheet (client_t *client, xmlDocPtr doc, const char *fn, int i) { xsl_req *x = client->shared_data; worker_t *worker = client->worker; time_t now = worker->current_time.tv_sec; struct stat file; // DEBUG4 ("idx %d, fn %s, check %ld/%ld", i, i==CACHESIZE?"XXX":cache[i].filename, (long)cache[i].next_check, now); while (i < CACHESIZE && i >= 0 && cache[i].filename && cache[i].next_check >= now) { thread_spin_lock (&update_lock); if (now == cache[i].next_check) { cache[i].next_check = now + 20; thread_spin_unlock (&update_lock); break; // jump out of loop to do xsl load } thread_spin_unlock (&update_lock); return i; } if (stat (fn, &file)) { WARN2("Error checking for stylesheet file \"%s\": %s", fn, strerror(errno)); return -1; } if (i < CACHESIZE && i >= 0) { thread_spin_lock (&update_lock); cache[i].next_check = now + 20; if (file.st_mtime == cache[i].last_modified) { thread_spin_unlock (&update_lock); DEBUG1 ("file %s has same mtime, not modified", cache[i].filename); return i; } thread_spin_unlock (&update_lock); // DEBUG3 ("idx %d, time is %ld, %ld", i, (long)(cache[i].last_modified), (long)file.st_mtime); } if (x == NULL) { x = calloc (1, sizeof (xsl_req)); x->index = i; x->client = client; x->doc = doc; x->cache.filename = strdup (fn); x->cache.last_modified = file.st_mtime; x->cache.cache_age = now; x->cache.next_check = now + 20; client->shared_data = x; client->schedule_ms = worker->time_ms; client->ops = &xslt_ops; } thread_spin_lock (&update_lock); if (xsl_updating < 3) { xsl_updating++; thread_spin_unlock (&update_lock); client->flags &= ~CLIENT_ACTIVE; // DEBUG1 ("Starting update thread for %s", x->cache.filename); thread_create ("update xslt", xslt_update, x, THREAD_DETACHED); return CACHESIZE; } thread_spin_unlock (&update_lock); // DEBUG1 ("Delaying update thread for %s", x->cache.filename); client->schedule_ms += 10; if ((client->flags & CLIENT_ACTIVE) == 0) { client->flags |= CLIENT_ACTIVE; worker_wakeup (worker); } return CACHESIZE; }
void global_reduce_bitrate_sampling (struct rate_calc *rate) { thread_spin_lock (&global.spinlock); rate_reduce (rate, 500); thread_spin_unlock (&global.spinlock); }
static int xslt_cached (const char *fn, client_t *client) { worker_t *worker = client->worker; time_t now = worker->current_time.tv_sec, oldest = now; int evict = 0, i; struct stat file; for(i=0; i < CACHESIZE; i++) { if(cache[i].filename) { #ifdef _WIN32 if (stricmp(fn, cache[i].filename) == 0) #else if (strcmp(fn, cache[i].filename) == 0) #endif { if (now - cache[i].last_checked > 10) { cache[i].last_checked = now; if (stat (fn, &file)) { WARN2("Error checking for stylesheet file \"%s\": %s", fn, strerror(errno)); return i; } DEBUG1 ("rechecked file time on %s", fn); thread_spin_lock (&update_lock); if (file.st_mtime > cache[i].last_modified) { cache[i].last_modified = file.st_mtime; thread_spin_unlock (&update_lock); break; } } thread_spin_unlock (&update_lock); cache[i].cache_age = now; return i; } if (oldest < cache[i].cache_age) continue; } evict = i; } xsl_req *x = calloc (1, sizeof (xsl_req)); if (i < CACHESIZE) { x->index = i; } else { if (stat (fn, &file)) { WARN2("Error checking for stylesheet file \"%s\": %s", fn, strerror(errno)); free (x); return -2; } x->client = client; x->index = evict; } x->doc = client->shared_data; x->cache.filename = strdup (fn); x->cache.last_modified = file.st_mtime; x->cache.cache_age = now; x->cache.last_checked = now; client->shared_data = x; client->schedule_ms = worker->time_ms; client->ops = &xslt_ops; thread_spin_lock (&update_lock); if (xsl_updating < 3) { xsl_updating++; thread_spin_unlock (&update_lock); if (x->client) client->flags &= ~CLIENT_ACTIVE; thread_create ("update xslt", xslt_update, x, THREAD_DETACHED); if (x->client == NULL) return i; } else { thread_spin_unlock (&update_lock); x->client = client; client->schedule_ms += 10; if ((client->flags & CLIENT_ACTIVE) == 0) { client->flags |= CLIENT_ACTIVE; worker_wakeup (worker); } } return -1; }
void fserve_recheck_mime_types(ice_config_t *config) { FILE *mimefile; char line[4096]; char *type, *ext, *cur; mime_type *mapping; avl_tree *new_mimetypes; if (config->mimetypes_fn == NULL) return; mimefile = fopen (config->mimetypes_fn, "r"); if (mimefile == NULL) { ICECAST_LOG_WARN("Cannot open mime types file %s", config->mimetypes_fn); return; } new_mimetypes = avl_tree_new(_compare_mappings, NULL); while(fgets(line, 4096, mimefile)) { line[4095] = 0; if(*line == 0 || *line == '#') continue; type = line; cur = line; while(*cur != ' ' && *cur != '\t' && *cur) cur++; if(*cur == 0) continue; *cur++ = 0; while(1) { while(*cur == ' ' || *cur == '\t') cur++; if(*cur == 0) break; ext = cur; while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur) cur++; *cur++ = 0; if(*ext) { void *tmp; /* Add a new extension->type mapping */ mapping = malloc(sizeof(mime_type)); mapping->ext = strdup(ext); mapping->type = strdup(type); if (!avl_get_by_key (new_mimetypes, mapping, &tmp)) avl_delete (new_mimetypes, mapping, _delete_mapping); avl_insert (new_mimetypes, mapping); } } } fclose(mimefile); thread_spin_lock (&pending_lock); if (mimetypes) avl_tree_free (mimetypes, _delete_mapping); mimetypes = new_mimetypes; thread_spin_unlock (&pending_lock); }
static void *start_relay_stream (void *arg) { client_t *client = arg; relay_server *relay; source_t *src; int failed = 1, sources; global_lock(); sources = ++global.sources; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); /* set the start time, because we want to decrease the sources on all failures */ client->connection.con_time = time (NULL); do { ice_config_t *config = config_get_config(); mount_proxy *mountinfo; relay = client->shared_data; src = relay->source; thread_rwlock_wlock (&src->lock); src->flags |= SOURCE_PAUSE_LISTENERS; if (sources > config->source_limit) { config_release_config(); WARN1 ("starting relayed mountpoint \"%s\" requires a higher sources limit", relay->localmount); break; } config_release_config(); INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount); if (open_relay (relay) < 0) break; if (connection_complete_source (src) < 0) { WARN1 ("Failed to complete initialisation on %s", relay->localmount); break; } stats_event_inc (NULL, "source_relay_connections"); source_init (src); config = config_get_config(); mountinfo = config_find_mount (config, src->mount); source_update_settings (config, src, mountinfo); INFO1 ("source %s is ready to start", src->mount); config_release_config(); failed = 0; } while (0); client->ops = &relay_client_ops; client->schedule_ms = timing_get_time(); if (failed) { /* failed to start any connection, better clean up and reset */ if (relay->on_demand == 0) { yp_remove (relay->localmount); src->yp_public = -1; } relay->in_use = NULL; INFO2 ("listener count remaining on %s is %d", src->mount, src->listeners); src->flags &= ~(SOURCE_PAUSE_LISTENERS|SOURCE_RUNNING); } thread_rwlock_unlock (&src->lock); thread_spin_lock (&relay_start_lock); relays_connecting--; thread_spin_unlock (&relay_start_lock); client->flags |= CLIENT_ACTIVE; worker_wakeup (client->worker); return NULL; }