Example #1
0
long stats_handle (const char *mount)
{
    stats_source_t *src_stats;

    if (mount == NULL)
        return 0;
    avl_tree_wlock (_stats.source_tree);
    src_stats = _find_source(_stats.source_tree, mount);
    if (src_stats == NULL)
    {
        src_stats = (stats_source_t *)calloc (1, sizeof (stats_source_t));
        if (src_stats == NULL)
            abort();
        DEBUG1 ("new source stat %s", mount);
        src_stats->source = (char *)strdup (mount);
        src_stats->stats_tree = avl_tree_new (_compare_stats, NULL);
        src_stats->flags = STATS_SLAVE|STATS_GENERAL|STATS_HIDDEN;

        avl_insert (_stats.source_tree, (void *)src_stats);
    }
    avl_tree_wlock (src_stats->stats_tree);
    avl_tree_unlock (_stats.source_tree);

    return (long)src_stats;
}
Example #2
0
/* This removes any source stats from virtual mountpoints, ie mountpoints
 * where no source_t exists. This function requires the global sources lock
 * to be held before calling.
 */
void stats_clear_virtual_mounts (void)
{
    avl_node *snode;

    avl_tree_wlock (_stats.source_tree);
    snode = avl_get_first(_stats.source_tree);
    while (snode)
    {
        stats_source_t *src = (stats_source_t *)snode->key;
        source_t *source = source_find_mount_raw (src->source);

        if (source == NULL)
        {
            stats_node_t *node;
            avl_tree_wlock (src->stats_tree);
            node = _find_node (src->stats_tree, "fallback");
            if (node == NULL)
            {
                /* no source_t and no fallback file stat, so delete */
                snode = avl_get_next (snode);
                avl_delete (_stats.source_tree, src, _free_source_stats);
                continue;
            }
            avl_tree_unlock (src->stats_tree);
        }

        snode = avl_get_next (snode);
    }
    avl_tree_unlock (_stats.source_tree);
}
Example #3
0
void *my_calloc (const char *file, int line, size_t num, size_t size)
{
    alloc_node match, *result;
    snprintf (match.name, sizeof (match.name), "%s:%d", file, line);

    avl_tree_wlock (global.alloc_tree);
    if (avl_get_by_key (global.alloc_tree, &match, (void**)&result) == 0)
    {
        allocheader *block = calloc (1, (num*size)+sizeof(allocheader));
        result->count++;
        result->allocated += (num*size);
        block->info = result;
        block->len = num*size;
        avl_tree_unlock (global.alloc_tree);
        return block+1;
    }
    result = calloc (1, sizeof(alloc_node));
    if (result)
    {
        allocheader *block = calloc (1, (num*size)+sizeof(allocheader));
        snprintf (result->name, sizeof (result->name), "%s:%d", file, line);
        result->count = 1;
        result->allocated = (num * size);
        avl_insert (global.alloc_tree, result);
        block->info = result;
        block->len = num*size;
        avl_tree_unlock (global.alloc_tree);
        return block+1;
    }
    avl_tree_unlock (global.alloc_tree);
    return NULL;
}
Example #4
0
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;
}
Example #5
0
long stats_lock (long handle, const char *mount)
{
    stats_source_t *src_stats = (stats_source_t *)handle;
    if (src_stats == NULL)
        src_stats = (stats_source_t*)stats_handle (mount);
    else
        avl_tree_wlock (src_stats->stats_tree);
    return (long)src_stats;
}
Example #6
0
static void process_source_event (stats_event_t *event)
{
    stats_source_t *snode;

    avl_tree_wlock (_stats.source_tree);
    snode = _find_source(_stats.source_tree, event->source);
    if (snode == NULL)
    {
        if (event->action == STATS_EVENT_REMOVE)
        {
            avl_tree_unlock (_stats.source_tree);
            return;
        }
        snode = (stats_source_t *)calloc(1,sizeof(stats_source_t));
        if (snode == NULL)
            abort();
        DEBUG1 ("new source stat %s", event->source);
        snode->source = (char *)strdup(event->source);
        snode->stats_tree = avl_tree_new(_compare_stats, NULL);
        snode->flags = STATS_SLAVE|STATS_GENERAL|STATS_HIDDEN;

        avl_insert(_stats.source_tree, (void *)snode);
    }
    if (event->action == STATS_EVENT_REMOVE && event->name == NULL)
    {
        int fallback_stream = 0;
        avl_tree_wlock (snode->stats_tree);
        fallback_stream = _find_node (snode->stats_tree, "fallback") == NULL ? 1 : 0;
        if (fallback_stream)
            avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
        else
            avl_tree_unlock (snode->stats_tree);
        avl_tree_unlock (_stats.source_tree);
        return;
    }
    avl_tree_wlock (snode->stats_tree);
    avl_tree_unlock (_stats.source_tree);
    process_source_stat (snode, event);
    avl_tree_unlock (snode->stats_tree);
}
Example #7
0
/* if 0 is returned then the client should not be touched, however if -1
 * is returned then the caller is responsible for handling the client
 */
static int add_listener_to_source (source_t *source, client_t *client)
{
    int loop = 10;
    do
    {
        ICECAST_LOG_DEBUG("max on %s is %ld (cur %lu)", source->mount,
                source->max_listeners, source->listeners);
        if (source->max_listeners == -1)
            break;
        if (source->listeners < (unsigned long)source->max_listeners)
            break;

        if (loop && source->fallback_when_full && source->fallback_mount)
        {
            source_t *next = source_find_mount (source->fallback_mount);
            if (!next) {
                ICECAST_LOG_ERROR("Fallback '%s' for full source '%s' not found", 
                        source->mount, source->fallback_mount);
                return -1;
            }

            ICECAST_LOG_INFO("stream full trying %s", next->mount);
            source = next;
            loop--;
            continue;
        }
        /* now we fail the client */
        return -1;

    } while (1);

    client->write_to_client = format_generic_write_to_client;
    client->check_buffer = format_check_http_buffer;
    client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
    memset (client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);

    /* lets add the client to the active list */
    avl_tree_wlock (source->pending_tree);
    avl_insert (source->pending_tree, client);
    avl_tree_unlock (source->pending_tree);

    if (source->running == 0 && source->on_demand)
    {
        /* enable on-demand relay to start, wake up the slave thread */
        ICECAST_LOG_DEBUG("kicking off on-demand relay");
        source->on_demand_req = 1;
    }
    ICECAST_LOG_DEBUG("Added client to %s", source->mount);
    return 0;
}
Example #8
0
void my_free (void *freeblock)
{
    allocheader *block;
    alloc_node *info;
    if (freeblock == NULL)
        return;
    block = (allocheader*)freeblock -1;
    info = block->info;
    avl_tree_wlock (global.alloc_tree);
    info->count--;
    info->allocated -= block->len;
    avl_tree_unlock (global.alloc_tree);
    free (block);
}
Example #9
0
/* Remove the provided source from the global tree and free it */
void source_free_source (source_t *source)
{
    DEBUG1 ("freeing source \"%s\"", source->mount);
    avl_tree_wlock (global.source_tree);
    avl_delete (global.source_tree, source, NULL);
    avl_tree_unlock (global.source_tree);

    avl_tree_free(source->pending_tree, _free_client);
    avl_tree_free(source->client_tree, _free_client);

    free (source->mount);
    free (source);

    return;
}
Example #10
0
int fserve_set_override (const char *mount, const char *dest, format_type_t type)
{
    fh_node fh, *result;

    fh.finfo.flags = FS_FALLBACK;
    fh.finfo.mount = (char *)mount;
    fh.finfo.fallback = NULL;
    fh.finfo.type = type;

    avl_tree_wlock (fh_cache);
    result = find_fh (&fh.finfo);
    if (result)
    {
        thread_mutex_lock (&result->lock);

        if (result->refcount > 0)
        {
            fh_node *copy = calloc (1, sizeof (*copy));
            avl_delete (fh_cache, result, NULL);
            copy->finfo = result->finfo;
            copy->finfo.mount = strdup (copy->finfo.mount);
            copy->prev_count = -1; // trigger stats update
            copy->expire = (time_t)-1;
            copy->stats = result->stats;
            copy->format = result->format;
            copy->f = result->f;
            thread_mutex_create (&copy->lock);
            copy->out_bitrate = rate_setup (10000, 1000);
            copy->clients = avl_tree_new (client_compare, NULL);
            avl_insert (fh_cache, copy);

            result->finfo.flags |= FS_DELETE;
            result->finfo.flags &= ~FS_FALLBACK;
            result->format = NULL;
            result->stats = 0;
            result->f = SOCK_ERROR;
            result->finfo.fallback = strdup (dest);
            result->finfo.type = type;
        }
        avl_tree_unlock (fh_cache);
        thread_mutex_unlock (&result->lock);
        INFO2 ("move clients from %s to %s", mount, dest);
        return 1;
    }
    avl_tree_unlock (fh_cache);
    return 0;
}
Example #11
0
/* Remove the provided source from the global tree and free it */
void source_free_source (source_t *source)
{
    DEBUG1 ("freeing source \"%s\"", source->mount);
    avl_tree_wlock (global.source_tree);
    avl_delete (global.source_tree, source, NULL);
    avl_tree_unlock (global.source_tree);

    avl_tree_free(source->pending_tree, _free_client);
    avl_tree_free(source->client_tree, _free_client);

    /* make sure all YP entries have gone */
    yp_remove (source->mount);

    free (source->mount);
    free (source);

    return;
}
Example #12
0
void *my_realloc (const char *file, int line, void *ptr, size_t size)
{
    allocheader *block, *newblock;
    alloc_node *info;

    if (ptr == NULL)
        return my_calloc (file, line, 1, size);
    if (size == 0)
    {
        my_free (ptr);
        return NULL;
    }
    block = (allocheader*)ptr -1;
    avl_tree_wlock (global.alloc_tree);
    newblock = realloc (block, sizeof (allocheader)+size);
    info = newblock->info;
    info->allocated -= newblock->len;
    info->allocated += size;
    newblock->len = size;
    avl_tree_unlock (global.alloc_tree);
    return newblock+1;
}
Example #13
0
static void process_global_event (stats_event_t *event)
{
    stats_node_t *node = NULL;

    avl_tree_wlock (_stats.global_tree);
    /* DEBUG3("global event %s %s %d", event->name, event->value, event->action); */
    if (event->action == STATS_EVENT_REMOVE)
    {
        /* we're deleting */
        node = _find_node(_stats.global_tree, event->name);
        if (node != NULL)
        {
            stats_listener_send (node->flags, "DELETE global %s\n", event->name);
            avl_delete(_stats.global_tree, (void *)node, _free_stats);
        }
        avl_tree_unlock (_stats.global_tree);
        return;
    }
    node = _find_node(_stats.global_tree, event->name);
    if (node)
    {
        modify_node_event (node, event);
        if ((node->flags & STATS_REGULAR) == 0)
            stats_listener_send (node->flags, "EVENT global %s %s\n", node->name, node->value);
    }
    else
    {
        /* add node */
        node = (stats_node_t *)calloc(1, sizeof(stats_node_t));
        node->name = (char *)strdup(event->name);
        node->value = (char *)strdup(event->value);
        node->flags = event->flags;

        avl_insert(_stats.global_tree, (void *)node);
        stats_listener_send (node->flags, "EVENT global %s %s\n", event->name, event->value);
    }
    avl_tree_unlock (_stats.global_tree);
}
Example #14
0
/* Allocate a new source with the stated mountpoint, if one already
 * exists with that mountpoint in the global source tree then return
 * NULL.
 */
source_t *source_reserve (const char *mount)
{
    source_t *src = NULL;

    if(mount[0] != '/')
        WARN1("Source at \"%s\" does not start with '/', clients will be "
                "unable to connect", mount);

    do
    {
        avl_tree_wlock (global.source_tree);
        src = source_find_mount_raw (mount);
        if (src)
        {
            src = NULL;
            break;
        }

        src = calloc (1, sizeof(source_t));
        if (src == NULL)
            break;

        src->client_tree = avl_tree_new(_compare_clients, NULL);
        src->pending_tree = avl_tree_new(_compare_clients, NULL);

        /* make duplicates for strings or similar */
        src->mount = strdup (mount);
        src->max_listeners = -1;

        avl_insert (global.source_tree, src);

    } while (0);

    avl_tree_unlock (global.source_tree);
    return src;
}
Example #15
0
/* Move clients from source to dest provided dest is running
 * and that the stream format is the same.
 * The only lock that should be held when this is called is the
 * source tree lock
 */
void source_move_clients (source_t *source, source_t *dest)
{
    /* we don't want the two write locks to deadlock in here */
    thread_mutex_lock (&move_clients_mutex);

    /* if the destination is not running then we can't move clients */

    if (dest->running == 0)
    {
        WARN1 ("destination mount %s not running, unable to move clients ", dest->mount);
        thread_mutex_unlock (&move_clients_mutex);
        return;
    }

    avl_tree_wlock (dest->pending_tree);
    do
    {
        client_t *client;

        /* we need to move the client and pending trees */
        avl_tree_wlock (source->pending_tree);

        if (source->format == NULL)
        {
            INFO1 ("source mount %s is not available", source->mount);
            break;
        }
        if (source->format->type != dest->format->type)
        {
            WARN2 ("stream %s and %s are of different types, ignored", source->mount, dest->mount);
            break;
        }

        while (1)
        {
            avl_node *node = avl_get_first (source->pending_tree);
            if (node == NULL)
                break;
            client = (client_t *)(node->key);
            avl_delete (source->pending_tree, client, NULL);

            /* switch client to different queue */
            client_set_queue (client, dest->stream_data_tail);
            avl_insert (dest->pending_tree, (void *)client);
        }

        avl_tree_wlock (source->client_tree);
        while (1)
        {
            avl_node *node = avl_get_first (source->client_tree);
            if (node == NULL)
                break;

            client = (client_t *)(node->key);
            avl_delete (source->client_tree, client, NULL);

            /* switch client to different queue */
            client_set_queue (client, dest->stream_data_tail);
            avl_insert (dest->pending_tree, (void *)client);
        }
        source->listeners = 0;
        stats_event (source->mount, "listeners", "0");
        avl_tree_unlock (source->client_tree);

    } while (0);

    avl_tree_unlock (source->pending_tree);
    avl_tree_unlock (dest->pending_tree);
    thread_mutex_unlock (&move_clients_mutex);
}
Example #16
0
int fserve_setup_client_fb (client_t *client, fbinfo *finfo)
{
    fh_node *fh = &no_file;
    int ret = 0;

    if (finfo)
    {
        mount_proxy *minfo;
        if (finfo->flags & FS_FALLBACK && finfo->limit == 0)
            return -1;
        avl_tree_wlock (fh_cache);
        fh = find_fh (finfo);
        minfo = config_find_mount (config_get_config(), finfo->mount);
        if (fh)
        {
            thread_mutex_lock (&fh->lock);
            avl_tree_unlock (fh_cache);
            client->shared_data = NULL;
            if (minfo)
            {
                if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners)
                {
                    thread_mutex_unlock (&fh->lock);
                    config_release_config();
                    return client_send_403redirect (client, finfo->mount, "max listeners reached");
                }
                if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0)
                {
                    thread_mutex_unlock (&fh->lock);
                    config_release_config();
                    return client_send_403 (client, "Account already in use");
                }
            }
            config_release_config();
        }
        else
        {
            if (minfo && minfo->max_listeners == 0)
            {
                avl_tree_unlock (fh_cache);
                config_release_config();
                client->shared_data = NULL;
                return client_send_403redirect (client, finfo->mount, "max listeners reached");
            }
            config_release_config();
            fh = open_fh (finfo);
            if (fh == NULL)
                return client_send_404 (client, NULL);
        }
        if (fh->finfo.limit)
        {
            client->timer_start = client->worker->current_time.tv_sec;
            if (client->connection.sent_bytes == 0)
                client->timer_start -= 2;
            client->counter = 0;
            client->intro_offset = 0;
            global_reduce_bitrate_sampling (global.out_bitrate);
        }
    }
    else
        thread_mutex_lock (&fh->lock);
    client->mount = fh->finfo.mount;
    if (fh->finfo.type == FORMAT_TYPE_UNDEFINED)
    {
        if (client->respcode == 0)
        {
            client->refbuf->len = 0;
            ret = format_general_headers (fh->format, client);
        }
    }
    else
    {
        if (fh->format->create_client_data && client->format_data == NULL)
            ret = fh->format->create_client_data (fh->format, client);
        if (fh->format->write_buf_to_client)
            client->check_buffer = fh->format->write_buf_to_client;
    }
    if (ret < 0)
    {
        thread_mutex_unlock (&fh->lock);
        return client_send_416 (client);
    }
    fh_add_client (fh, client);
    thread_mutex_unlock (&fh->lock);
    client->shared_data = fh;

    if (client->check_buffer == NULL)
        client->check_buffer = format_generic_write_to_client;

    client->ops = &buffer_content_ops;
    client->flags &= ~CLIENT_HAS_INTRO_CONTENT;
    client->flags |= CLIENT_IN_FSERVE;
    if (client->flags & CLIENT_ACTIVE)
    {
        client->schedule_ms = client->worker->time_ms;
        if (finfo && finfo->flags & FS_FALLBACK)
            return 0; // prevent a recursive loop 
        return client->ops->process (client);
    }
    else
    {
        worker_t *worker = client->worker;
        client->flags |= CLIENT_ACTIVE;
        worker_wakeup (worker); /* worker may of already processed client but make sure */
    }
    return 0;
}
Example #17
0
void source_clear_source (source_t *source)
{
    int c;

    DEBUG1 ("clearing source \"%s\"", source->mount);

    avl_tree_wlock (source->pending_tree);
    client_destroy(source->client);
    source->client = NULL;
    source->parser = NULL;
    source->con = NULL;

    /* log bytes read in access log */
    if (source->client && source->format)
        source->client->con->sent_bytes = source->format->read_bytes;

    if (source->dumpfile)
    {
        INFO1 ("Closing dumpfile for %s", source->mount);
        fclose (source->dumpfile);
        source->dumpfile = NULL;
    }

    /* lets kick off any clients that are left on here */
    avl_tree_wlock (source->client_tree);
    c=0;
    while (1)
    {
        avl_node *node = avl_get_first (source->client_tree);
        if (node)
        {
            client_t *client = node->key;
            if (client->respcode == 200)
                c++; /* only count clients that have had some processing */
            avl_delete (source->client_tree, client, _free_client);
            continue;
        }
        break;
    }
    if (c)
    {
        stats_event_sub (NULL, "listeners", source->listeners);
        INFO2 ("%d active listeners on %s released", c, source->mount);
    }
    avl_tree_unlock (source->client_tree);

    while (avl_get_first (source->pending_tree))
    {
        avl_delete (source->pending_tree,
                avl_get_first(source->pending_tree)->key, _free_client);
    }

    if (source->format && source->format->free_plugin)
        source->format->free_plugin (source->format);
    source->format = NULL;

    /* Lets clear out the source queue too */
    while (source->stream_data)
    {
        refbuf_t *p = source->stream_data;
        source->stream_data = p->next;
        p->next = NULL;
        /* can be referenced by burst handler as well */
        while (p->_count > 1)
            refbuf_release (p);
        refbuf_release (p);
    }
    source->stream_data_tail = NULL;

    source->burst_point = NULL;
    source->burst_size = 0;
    source->burst_offset = 0;
    source->queue_size = 0;
    source->queue_size_limit = 0;
    source->listeners = 0;
    source->max_listeners = -1;
    source->prev_listeners = 0;
    source->hidden = 0;
    source->shoutcast_compat = 0;
    source->client_stats_update = 0;
    util_dict_free (source->audio_info);
    source->audio_info = NULL;

    free(source->fallback_mount);
    source->fallback_mount = NULL;

    free(source->dumpfilename);
    source->dumpfilename = NULL;

    if (source->intro_file)
    {
        fclose (source->intro_file);
        source->intro_file = NULL;
    }

    source->on_demand_req = 0;
    avl_tree_unlock (source->pending_tree);
}
Example #18
0
/* Move clients from source to dest provided dest is running
 * and that the stream format is the same.
 * The only lock that should be held when this is called is the
 * source tree lock
 */
void source_move_clients (source_t *source, source_t *dest)
{
    unsigned long count = 0;
    if (strcmp (source->mount, dest->mount) == 0)
    {
        WARN1 ("src and dst are the same \"%s\", skipping", source->mount);
        return;
    }
    /* we don't want the two write locks to deadlock in here */
    thread_mutex_lock (&move_clients_mutex);

    /* if the destination is not running then we can't move clients */

    avl_tree_wlock (dest->pending_tree);
    if (dest->running == 0 && dest->on_demand == 0)
    {
        WARN1 ("destination mount %s not running, unable to move clients ", dest->mount);
        avl_tree_unlock (dest->pending_tree);
        thread_mutex_unlock (&move_clients_mutex);
        return;
    }

    do
    {
        client_t *client;

        /* we need to move the client and pending trees - we must take the
         * locks in this order to avoid deadlocks */
        avl_tree_wlock (source->pending_tree);
        avl_tree_wlock (source->client_tree);

        if (source->on_demand == 0 && source->format == NULL)
        {
            INFO1 ("source mount %s is not available", source->mount);
            break;
        }
        if (source->format && dest->format)
        {
            if (source->format->type != dest->format->type)
            {
                WARN2 ("stream %s and %s are of different types, ignored", source->mount, dest->mount);
                break;
            }
        }

        while (1)
        {
            avl_node *node = avl_get_first (source->pending_tree);
            if (node == NULL)
                break;
            client = (client_t *)(node->key);
            avl_delete (source->pending_tree, client, NULL);

            /* when switching a client to a different queue, be wary of the 
             * refbuf it's referring to, if it's http headers then we need
             * to write them so don't release it.
             */
            if (client->check_buffer != format_check_http_buffer)
            {
                client_set_queue (client, NULL);
                client->check_buffer = format_check_file_buffer;
                if (source->con == NULL)
                    client->intro_offset = -1;
            }

            avl_insert (dest->pending_tree, (void *)client);
            count++;
        }

        while (1)
        {
            avl_node *node = avl_get_first (source->client_tree);
            if (node == NULL)
                break;

            client = (client_t *)(node->key);
            avl_delete (source->client_tree, client, NULL);

            /* when switching a client to a different queue, be wary of the 
             * refbuf it's referring to, if it's http headers then we need
             * to write them so don't release it.
             */
            if (client->check_buffer != format_check_http_buffer)
            {
                client_set_queue (client, NULL);
                client->check_buffer = format_check_file_buffer;
                if (source->con == NULL)
                    client->intro_offset = -1;
            }
            avl_insert (dest->pending_tree, (void *)client);
            count++;
        }
        INFO2 ("passing %lu listeners to \"%s\"", count, dest->mount);

        source->listeners = 0;
        stats_event (source->mount, "listeners", "0");

    } while (0);

    avl_tree_unlock (source->pending_tree);
    avl_tree_unlock (source->client_tree);

    /* see if we need to wake up an on-demand relay */
    if (dest->running == 0 && dest->on_demand && count)
        dest->on_demand_req = 1;

    avl_tree_unlock (dest->pending_tree);
    thread_mutex_unlock (&move_clients_mutex);
}
Example #19
0
static void process_source_stat (stats_source_t *src_stats, stats_event_t *event)
{
    if (event->name)
    {
        stats_node_t *node = _find_node (src_stats->stats_tree, event->name);
        if (node == NULL)
        {
            /* adding node */
            if (event->action != STATS_EVENT_REMOVE && event->value)
            {
                DEBUG3 ("new node on %s \"%s\" (%s)", src_stats->source, event->name, event->value);
                node = (stats_node_t *)calloc (1,sizeof(stats_node_t));
                node->name = (char *)strdup (event->name);
                node->value = (char *)strdup (event->value);
                node->flags = event->flags;
                if (src_stats->flags & STATS_HIDDEN)
                    node->flags |= STATS_HIDDEN;
                stats_listener_send (node->flags, "EVENT %s %s %s\n", src_stats->source, event->name, event->value);
                avl_insert (src_stats->stats_tree, (void *)node);
            }
            return;
        }
        if (event->action == STATS_EVENT_REMOVE)
        {
            DEBUG2 ("delete node %s from %s", event->name, src_stats->source);
            stats_listener_send (node->flags, "DELETE %s %s\n", src_stats->source, event->name);
            avl_delete (src_stats->stats_tree, (void *)node, _free_stats);
            return;
        }
        modify_node_event (node, event);
        stats_listener_send (node->flags, "EVENT %s %s %s\n", src_stats->source, node->name, node->value);
        return;
    }
    if (event->action == STATS_EVENT_REMOVE && event->name == NULL)
    {
        avl_tree_unlock (src_stats->stats_tree);
        avl_tree_wlock (_stats.source_tree);
        avl_tree_wlock (src_stats->stats_tree);
        avl_delete (_stats.source_tree, (void *)src_stats, _free_source_stats);
        avl_tree_unlock (_stats.source_tree);
        return;
    }
    /* change source flags status */
    if (event->action & STATS_EVENT_HIDDEN)
    {
        avl_node *node = avl_get_first (src_stats->stats_tree);
        int visible = 0;

        if ((event->flags&STATS_HIDDEN) == (src_stats->flags&STATS_HIDDEN))
            return;
        if (src_stats->flags & STATS_HIDDEN)
        {
            stats_node_t *ct = _find_node (src_stats->stats_tree, "server_type");
            const char *type = "audio/mpeg";
            if (ct)
                type = ct->value;
            src_stats->flags &= ~STATS_HIDDEN;
            stats_listener_send (src_stats->flags, "NEW %s %s\n", type, src_stats->source);
            visible = 1;
        }
        else
        {
            stats_listener_send (src_stats->flags, "DELETE %s\n", src_stats->source);
            src_stats->flags |= STATS_HIDDEN;
        }
        while (node)
        {
            stats_node_t *stats = (stats_node_t*)node->key;
            if (visible)
            {
                stats->flags &= ~STATS_HIDDEN;
                stats_listener_send (stats->flags, "EVENT %s %s %s\n", src_stats->source, stats->name, stats->value);
            }
            else
                stats->flags |= STATS_HIDDEN;
            node = avl_get_next (node);
        }
    }
}
Example #20
0
void source_main (source_t *source)
{
    refbuf_t *refbuf;
    client_t *client;
    avl_node *client_node;

    source_init (source);

    while (global.running == ICE_RUNNING && source->running) {
        int remove_from_q;

        refbuf = get_next_buffer (source);

        remove_from_q = 0;
        source->short_delay = 0;

        if (refbuf)
        {
            /* append buffer to the in-flight data queue,  */
            if (source->stream_data == NULL)
            {
                source->stream_data = refbuf;
                source->burst_point = refbuf;
            }
            if (source->stream_data_tail)
                source->stream_data_tail->next = refbuf;
            source->stream_data_tail = refbuf;
            source->queue_size += refbuf->len;
            /* new buffer is referenced for burst */
            refbuf_addref (refbuf);

            /* new data on queue, so check the burst point */
            source->burst_offset += refbuf->len;
            while (source->burst_offset > source->burst_size)
            {
                refbuf_t *to_release = source->burst_point;

                if (to_release->next)
                {
                    source->burst_point = to_release->next;
                    source->burst_offset -= to_release->len;
                    refbuf_release (to_release);
                    continue;
                }
                break;
            }

            /* save stream to file */
            if (source->dumpfile && source->format->write_buf_to_file)
                source->format->write_buf_to_file (source, refbuf);
        }
        /* lets see if we have too much data in the queue, but don't remove it until later */
        if (source->queue_size > source->queue_size_limit)
            remove_from_q = 1;

        /* acquire write lock on pending_tree */
        avl_tree_wlock(source->pending_tree);

        /* acquire write lock on client_tree */
        avl_tree_wlock(source->client_tree);

        client_node = avl_get_first(source->client_tree);
        while (client_node) {
            client = (client_t *)client_node->key;

            send_to_listener (source, client, remove_from_q);

            if (client->con->error) {
                client_node = avl_get_next(client_node);
                if (client->respcode == 200)
                    stats_event_dec (NULL, "listeners");
                avl_delete(source->client_tree, (void *)client, _free_client);
                source->listeners--;
                DEBUG0("Client removed");
                continue;
            }
            client_node = avl_get_next(client_node);
        }

        /** add pending clients **/
        client_node = avl_get_first(source->pending_tree);
        while (client_node) {

            if(source->max_listeners != -1 && 
                    source->listeners >= (unsigned long)source->max_listeners) 
            {
                /* The common case is caught in the main connection handler,
                 * this deals with rarer cases (mostly concerning fallbacks)
                 * and doesn't give the listening client any information about
                 * why they were disconnected
                 */
                client = (client_t *)client_node->key;
                client_node = avl_get_next(client_node);
                avl_delete(source->pending_tree, (void *)client, _free_client);

                INFO0("Client deleted, exceeding maximum listeners for this "
                        "mountpoint.");
                continue;
            }
            
            /* Otherwise, the client is accepted, add it */
            avl_insert(source->client_tree, client_node->key);

            source->listeners++;
            DEBUG0("Client added");
            stats_event_inc(source->mount, "connections");

            client_node = avl_get_next(client_node);
        }

        /** clear pending tree **/
        while (avl_get_first(source->pending_tree)) {
            avl_delete(source->pending_tree, 
                    avl_get_first(source->pending_tree)->key, 
                    source_remove_client);
        }

        /* release write lock on pending_tree */
        avl_tree_unlock(source->pending_tree);

        /* update the stats if need be */
        if (source->listeners != source->prev_listeners)
        {
            source->prev_listeners = source->listeners;
            INFO2("listener count on %s now %lu", source->mount, source->listeners);
            if (source->listeners > source->peak_listeners)
            {
                source->peak_listeners = source->listeners;
                stats_event_args (source->mount, "listener_peak", "%lu", source->peak_listeners);
            }
            stats_event_args (source->mount, "listeners", "%lu", source->listeners);
            if (source->listeners == 0 && source->on_demand)
                source->running = 0;
        }

        /* lets reduce the queue, any lagging clients should of been
         * terminated by now
         */
        if (source->stream_data)
        {
            /* normal unreferenced queue data will have a refcount 1, but
             * burst queue data will be at least 2, active clients will also
             * increase refcount */
            while (source->stream_data->_count == 1)
            {
                refbuf_t *to_go = source->stream_data;

                if (to_go->next == NULL || source->burst_point == to_go)
                {
                    /* this should not happen */
                    ERROR0 ("queue state is unexpected");
                    source->running = 0;
                    break;
                }
                source->stream_data = to_go->next;
                source->queue_size -= to_go->len;
                to_go->next = NULL;
                refbuf_release (to_go);
            }
        }

        /* release write lock on client_tree */
        avl_tree_unlock(source->client_tree);
    }
    source_shutdown (source);
}
Example #21
0
void fserve_scan (time_t now)
{
    avl_node *node;
    avl_tree_wlock (fh_cache);
    node = avl_get_first (fh_cache);
    while (node)
    {
        fh_node *fh = node->key;
        node = avl_get_next (node);

        thread_mutex_lock (&fh->lock);
        if (global.running != ICE_RUNNING)
            fh->expire = 0;
        if (now == (time_t)0)
        {
            fh->expire = 0;
            thread_mutex_unlock (&fh->lock);
            continue;
        }

        if (fh->finfo.limit)
        {
            fbinfo *finfo = &fh->finfo;
            if (fh->stats == 0)
            {
                int len = strlen (finfo->mount) + 10;
                char *str = alloca (len);
                char buf[20];
                snprintf (str, len, "%s-%s", (finfo->flags & FS_FALLBACK) ? "fallback" : "file", finfo->mount);
                fh->stats = stats_handle (str);
                stats_set_flags (fh->stats, "fallback", "file", STATS_COUNTERS|STATS_HIDDEN);
                stats_set_flags (fh->stats, "outgoing_kbitrate", "0", STATS_COUNTERS|STATS_HIDDEN);
                snprintf (buf, sizeof (buf), "%d", fh->refcount);
                stats_set_flags (fh->stats, "listeners", buf, STATS_GENERAL|STATS_HIDDEN);
                snprintf (buf, sizeof (buf), "%d", fh->peak);
                stats_set_flags (fh->stats, "listener_peak", buf, STATS_GENERAL|STATS_HIDDEN);
                fh->prev_count = fh->refcount;
            }
            else
            {
                stats_lock (fh->stats, NULL);
                if (fh->prev_count != fh->refcount)
                {
                    fh->prev_count = fh->refcount;
                    stats_set_args (fh->stats, "listeners", "%ld", fh->refcount);
                    stats_set_args (fh->stats, "listener_peak", "%ld", fh->peak);
                }
            }
            if (fh->stats_update <= now)
            {
                fh->stats_update = now + 5;
                stats_set_args (fh->stats, "outgoing_kbitrate", "%ld",
                        (long)((8 * rate_avg (fh->out_bitrate))/1024));
            }
            stats_release (fh->stats);
        }

        if (fh->refcount == 0 && fh->expire >= 0 && now >= fh->expire)
        {
            DEBUG1 ("timeout of %s", fh->finfo.mount);
            if (fh->stats)
            {
                stats_lock (fh->stats, NULL);
                stats_set (fh->stats, NULL, NULL);
            }
            remove_fh_from_cache (fh);
            thread_mutex_unlock (&fh->lock);
            _delete_fh (fh);
            continue;
        }
        thread_mutex_unlock (&fh->lock);
    }
    avl_tree_unlock (fh_cache);
}
Example #22
0
int fserve_setup_client_fb (client_t *client, fbinfo *finfo)
{
    fh_node *fh = &no_file;
    int ret = 0;
    refbuf_t *refbuf;
    ssize_t bytes;

    if (finfo)
    {
        mount_proxy *minfo;
        if (finfo->flags & FS_FALLBACK && finfo->limit == 0)
            return -1;
        avl_tree_wlock (fh_cache);
        fh = find_fh (finfo);
        minfo = config_find_mount (config_get_config(), finfo->mount);
        if (fh)
        {
            thread_mutex_lock (&fh->lock);
            avl_tree_unlock (fh_cache);
            client->shared_data = NULL;
            if (minfo)
            {
                if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners)
                {
                    thread_mutex_unlock (&fh->lock);
                    config_release_config();
                    return client_send_403redirect (client, finfo->mount, "max listeners reached");
                }
                if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0)
                {
                    thread_mutex_unlock (&fh->lock);
                    config_release_config();
                    return client_send_403 (client, "Account already in use");
                }
            }
            config_release_config();
        }
        else
        {
            if (minfo && minfo->max_listeners == 0)
            {
                avl_tree_unlock (fh_cache);
                config_release_config();
                client->shared_data = NULL;
                return client_send_403redirect (client, finfo->mount, "max listeners reached");
            }
            config_release_config();
            fh = open_fh (finfo);
            if (fh == NULL)
                return client_send_404 (client, NULL);
            if (fh->finfo.limit)
                DEBUG2 ("request for throttled file %s (bitrate %d)", fh->finfo.mount, fh->finfo.limit*8);
        }
        if (fh->finfo.limit)
        {
            client->timer_start = client->worker->current_time.tv_sec;
            if (client->connection.sent_bytes == 0)
                client->timer_start -= 2;
            client->counter = 0;
            client->intro_offset = 0;
            global_reduce_bitrate_sampling (global.out_bitrate);
        }
    }
    else
    {
        if (client->mount && (client->flags & CLIENT_AUTHENTICATED) && (client->respcode >= 300 || client->respcode < 200))
        {
            fh = calloc (1, sizeof (no_file));
            fh->finfo.mount = strdup (client->mount);
            fh->finfo.flags |= FS_DELETE;
            fh->refcount = 1;
            fh->f = SOCK_ERROR;
            thread_mutex_create (&fh->lock);
        }
        thread_mutex_lock (&fh->lock);
    }
    client->mount = fh->finfo.mount;
    if (fh->finfo.type == FORMAT_TYPE_UNDEFINED)
    {
        if (client->respcode == 0)
        {
            client->refbuf->len = 0;
            ret = format_general_headers (fh->format, client);
        }
    }
    else
    {
        if (fh->format->create_client_data && client->format_data == NULL)
            ret = fh->format->create_client_data (fh->format, client);
        if (fh->format->write_buf_to_client)
            client->check_buffer = fh->format->write_buf_to_client;
    }
    if (ret < 0)
    {
        thread_mutex_unlock (&fh->lock);
        return client_send_416 (client);
    }
    fh_add_client (fh, client);
    thread_mutex_unlock (&fh->lock);
    client->shared_data = fh;

    if (client->check_buffer == NULL)
        client->check_buffer = format_generic_write_to_client;

    // workaround for #134: fill the preallocated, but empty, chained buffer in case a range request was made
    if (client->flags & CLIENT_RANGE_END)
    {
        if (client->refbuf && client->refbuf->next)
        {
            refbuf = client->refbuf->next;
            bytes = pread (fh->f, refbuf->data, refbuf->len, client->intro_offset);
            if (bytes < 0)
                return -1;
        }
    }
    
    client->ops = &buffer_content_ops;
    client->flags &= ~CLIENT_HAS_INTRO_CONTENT;
    client->flags |= CLIENT_IN_FSERVE;
    if (client->flags & CLIENT_ACTIVE)
    {
        client->schedule_ms = client->worker->time_ms;
        if (finfo && finfo->flags & FS_FALLBACK)
            return 0; // prevent a recursive loop 
        return client->ops->process (client);
    }
    else
    {
        worker_t *worker = client->worker;
        ret = (fh->finfo.limit) ? 0 : -1;
        client->flags |= CLIENT_ACTIVE;
        worker_wakeup (worker); /* worker may of already processed client but make sure */
    }
    return ret;
}
Example #23
0
static void _handle_get_request(connection_t *con,
        http_parser_t *parser, char *passed_uri)
{
    char *fullpath;
    client_t *client;
    int bytes;
    struct stat statbuf;
    source_t *source;
    int fileserve;
    char *host;
    int port;
    int i;
    char *serverhost = NULL;
    int serverport = 0;
    aliases *alias;
    ice_config_t *config;
    int client_limit;
    int ret;
    char *uri = passed_uri;

    config = config_get_config();
    fileserve = config->fileserve;
    host = config->hostname;
    port = config->port;
    for(i = 0; i < MAX_LISTEN_SOCKETS; i++) {
        if(global.serversock[i] == con->serversock) {
            serverhost = config->listeners[i].bind_address;
            serverport = config->listeners[i].port;
            break;
        }
    }
    alias = config->aliases;
    client_limit = config->client_limit;

    /* there are several types of HTTP GET clients
    ** media clients, which are looking for a source (eg, URI = /stream.ogg)
    ** stats clients, which are looking for /admin/stats.xml
    ** and directory server authorizers, which are looking for /GUID-xxxxxxxx 
    ** (where xxxxxx is the GUID in question) - this isn't implemented yet.
    ** we need to handle the latter two before the former, as the latter two
    ** aren't subject to the limits.
    */
    /* TODO: add GUID-xxxxxx */

    /* Handle aliases */
    while(alias) {
        if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) {
            uri = strdup (alias->destination);
            DEBUG2 ("alias has made %s into %s", passed_uri, uri);
            break;
        }
        alias = alias->next;
    }
    config_release_config();

    /* make a client */
    client = client_create(con, parser);
    stats_event_inc(NULL, "client_connections");

    /* Dispatch all admin requests */
    if (strncmp(uri, "/admin/", 7) == 0) {
        admin_handle_request(client, uri);
        if (uri != passed_uri) free (uri);
        return;
    }

    /* Here we are parsing the URI request to see
    ** if the extension is .xsl, if so, then process
    ** this request as an XSLT request
    */
    fullpath = util_get_path_from_normalised_uri(uri);
    if (util_check_valid_extension(fullpath) == XSLT_CONTENT) {
        /* If the file exists, then transform it, otherwise, write a 404 */
        if (stat(fullpath, &statbuf) == 0) {
            DEBUG0("Stats request, sending XSL transformed stats");
            client->respcode = 200;
            bytes = sock_write(client->con->sock, 
                    "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
            if(bytes > 0) client->con->sent_bytes = bytes;
            stats_transform_xslt(client, fullpath);
            client_destroy(client);
        }
        else {
            client_send_404(client, "The file you requested could not be found");
        }
        free(fullpath);
        if (uri != passed_uri) free (uri);
        return;
    }
    else if(fileserve && stat(fullpath, &statbuf) == 0 && 
#ifdef _WIN32
            ((statbuf.st_mode) & _S_IFREG))
#else
            S_ISREG(statbuf.st_mode)) 
#endif
    {
        fserve_client_create(client, fullpath);
        free(fullpath);
        if (uri != passed_uri) free (uri);
        return;
    }
    free(fullpath);

    if(strcmp(util_get_extension(uri), "m3u") == 0) {
        char *sourceuri = strdup(uri);
        char *dot = strrchr(sourceuri, '.');
        *dot = 0;
        client->respcode = 200;
        bytes = sock_write(client->con->sock,
                    "HTTP/1.0 200 OK\r\n"
                    "Content-Type: audio/x-mpegurl\r\n\r\n"
                    "http://%s:%d%s\r\n", 
                    host, 
                    port,
                    sourceuri
                    );
        if(bytes > 0) client->con->sent_bytes = bytes;
        client_destroy(client);
        free(sourceuri);
        if (uri != passed_uri) free (uri);
        return;
    }

    global_lock();
    if (global.clients >= client_limit) {
        global_unlock();
        client_send_404(client,
                "The server is already full. Try again later.");
        if (uri != passed_uri) free (uri);
        return;
    }
    global_unlock();
                    
    avl_tree_rlock(global.source_tree);
    source = source_find_mount(uri);
    if (source) {
        DEBUG0("Source found for client");

        /* The source may not be the requested source - it might have gone
         * via one or more fallbacks. We only reject it for no-mount if it's
         * the originally requested source
         */
        if(strcmp(uri, source->mount) == 0 && source->no_mount) {
            avl_tree_unlock(global.source_tree);
            client_send_404(client, "This mount is unavailable.");
            if (uri != passed_uri) free (uri);
            return;
        }
        if (source->running == 0)
        {
            avl_tree_unlock(global.source_tree);
            DEBUG0("inactive source, client dropped");
            client_send_404(client, "This mount is unavailable.");
            if (uri != passed_uri) free (uri);
            return;
        }

        /* Check for any required authentication first */
        if(source->authenticator != NULL) {
            ret = auth_check_client(source, client);
            if(ret != AUTH_OK) {
                avl_tree_unlock(global.source_tree);
                if (ret == AUTH_FORBIDDEN) {
                    INFO1("Client attempted to log multiple times to source "
                        "(\"%s\")", uri);
                    client_send_403(client);
                }
                else {
                /* If not FORBIDDEN, default to 401 */
                    INFO1("Client attempted to log in to source (\"%s\")with "
                        "incorrect or missing password", uri);
                    client_send_401(client);
                }
                if (uri != passed_uri) free (uri);
                return;
            }
        }

        /* And then check that there's actually room in the server... */
        global_lock();
        if (global.clients >= client_limit) {
            global_unlock();
            avl_tree_unlock(global.source_tree);
            client_send_404(client, 
                    "The server is already full. Try again later.");
            if (uri != passed_uri) free (uri);
            return;
        }
        /* Early-out for per-source max listeners. This gets checked again
         * by the source itself, later. This route gives a useful message to
         * the client, also.
         */
        else if(source->max_listeners != -1 && 
                source->listeners >= source->max_listeners) 
        {
            global_unlock();
            avl_tree_unlock(global.source_tree);
            client_send_404(client, 
                    "Too many clients on this mountpoint. Try again later.");
            if (uri != passed_uri) free (uri);
            return;
        }
        global.clients++;
        global_unlock();
                        
        source->format->create_client_data (source, client);

        source->format->client_send_headers(source->format, source, client);
                        
        bytes = sock_write(client->con->sock, "\r\n");
        if(bytes > 0) client->con->sent_bytes += bytes;
                            
        sock_set_blocking(client->con->sock, SOCK_NONBLOCK);
        sock_set_nodelay(client->con->sock);
                        
        avl_tree_wlock(source->pending_tree);
        avl_insert(source->pending_tree, (void *)client);
        avl_tree_unlock(source->pending_tree);
    }
                    
    avl_tree_unlock(global.source_tree);
                    
    if (!source) {
        DEBUG0("Source not found for client");
        client_send_404(client, "The source you requested could not be found.");
    }
    if (uri != passed_uri) free (uri);
}