2003-03-27 12:10:14 -05:00
|
|
|
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
|
2002-08-05 10:48:04 -04:00
|
|
|
/* slave.c
|
|
|
|
* by Ciaran Anscomb <ciaran.anscomb@6809.org.uk>
|
|
|
|
*
|
|
|
|
* Periodically requests a list of streams from a master server
|
|
|
|
* and creates source threads for any it doesn't already have.
|
|
|
|
* */
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#else
|
|
|
|
#include <winsock2.h>
|
|
|
|
#define snprintf _snprintf
|
|
|
|
#define strcasecmp stricmp
|
|
|
|
#define strncasecmp strnicmp
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "os.h"
|
|
|
|
|
|
|
|
#include "thread.h"
|
|
|
|
#include "avl.h"
|
|
|
|
#include "sock.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "httpp.h"
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "global.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "connection.h"
|
|
|
|
#include "refbuf.h"
|
|
|
|
#include "client.h"
|
|
|
|
#include "stats.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "source.h"
|
2002-12-30 02:55:56 -05:00
|
|
|
#include "format.h"
|
2002-08-05 10:48:04 -04:00
|
|
|
|
|
|
|
#define CATMODULE "slave"
|
|
|
|
|
|
|
|
static void *_slave_thread(void *arg);
|
2002-12-29 10:46:32 -05:00
|
|
|
thread_type *_slave_thread_id;
|
2002-08-05 10:48:04 -04:00
|
|
|
static int _initialized = 0;
|
|
|
|
|
|
|
|
void slave_initialize(void) {
|
2003-03-05 08:03:35 -05:00
|
|
|
ice_config_t *config;
|
2003-03-14 21:10:19 -05:00
|
|
|
if (_initialized) return;
|
2003-03-05 08:03:35 -05:00
|
|
|
|
|
|
|
config = config_get_config();
|
2002-08-05 10:48:04 -04:00
|
|
|
/* Don't create a slave thread if it isn't configured */
|
2003-03-05 08:03:35 -05:00
|
|
|
if (config->master_server == NULL &&
|
|
|
|
config->relay == NULL)
|
|
|
|
{
|
|
|
|
config_release_config();
|
2002-08-05 10:48:04 -04:00
|
|
|
return;
|
2003-03-05 08:03:35 -05:00
|
|
|
}
|
|
|
|
config_release_config();
|
2002-08-05 10:48:04 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
_initialized = 1;
|
|
|
|
_slave_thread_id = thread_create("Slave Thread", _slave_thread, NULL, THREAD_ATTACHED);
|
2002-08-05 10:48:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void slave_shutdown(void) {
|
2003-03-14 21:10:19 -05:00
|
|
|
if (!_initialized) return;
|
|
|
|
_initialized = 0;
|
|
|
|
thread_join(_slave_thread_id);
|
2002-08-05 10:48:04 -04:00
|
|
|
}
|
|
|
|
|
2003-02-12 06:04:26 -05:00
|
|
|
static void create_relay_stream(char *server, int port,
|
2003-02-24 08:37:15 -05:00
|
|
|
char *remotemount, char *localmount, int mp3)
|
2003-02-07 05:53:38 -05:00
|
|
|
{
|
|
|
|
sock_t streamsock;
|
2003-03-14 21:10:19 -05:00
|
|
|
char header[4096];
|
|
|
|
connection_t *con;
|
|
|
|
http_parser_t *parser;
|
2002-08-16 10:26:48 -04:00
|
|
|
client_t *client;
|
2003-02-07 05:53:38 -05:00
|
|
|
|
2003-02-12 06:04:26 -05:00
|
|
|
if(!localmount)
|
|
|
|
localmount = remotemount;
|
|
|
|
|
|
|
|
DEBUG1("Adding source at mountpoint \"%s\"", localmount);
|
2003-02-07 05:53:38 -05:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
streamsock = sock_connect_wto(server, port, 0);
|
|
|
|
if (streamsock == SOCK_ERROR) {
|
2003-02-07 06:56:40 -05:00
|
|
|
WARN2("Failed to relay stream from master server, couldn't connect to http://%s:%d", server, port);
|
2003-02-07 05:53:38 -05:00
|
|
|
return;
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2003-04-23 08:44:29 -04:00
|
|
|
con = create_connection(streamsock, -1, NULL);
|
2003-02-24 08:37:15 -05:00
|
|
|
if(mp3) {
|
2003-02-25 04:40:34 -05:00
|
|
|
/* Some mp3 servers are bitchy, send a user-agent string to make them
|
|
|
|
* send the right response.
|
|
|
|
*/
|
2003-03-14 21:10:19 -05:00
|
|
|
sock_write(streamsock, "GET %s HTTP/1.0\r\n"
|
2003-02-25 04:40:34 -05:00
|
|
|
"User-Agent: " ICECAST_VERSION_STRING "\r\n"
|
|
|
|
"Icy-MetaData: 1\r\n"
|
|
|
|
"\r\n",
|
2003-02-24 08:37:15 -05:00
|
|
|
remotemount);
|
|
|
|
}
|
|
|
|
else {
|
2003-03-14 21:10:19 -05:00
|
|
|
sock_write(streamsock, "GET %s HTTP/1.0\r\n"
|
2003-03-06 09:52:09 -05:00
|
|
|
"User-Agent: " ICECAST_VERSION_STRING "\r\n"
|
|
|
|
"\r\n",
|
|
|
|
remotemount);
|
2003-02-24 08:37:15 -05:00
|
|
|
}
|
2003-03-14 21:10:19 -05:00
|
|
|
memset(header, 0, sizeof(header));
|
|
|
|
if (util_read_header(con->sock, header, 4096) == 0) {
|
2003-02-25 04:40:34 -05:00
|
|
|
WARN0("Header read failed");
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_close(con);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
parser = httpp_create_parser();
|
|
|
|
httpp_initialize(parser, NULL);
|
|
|
|
if(!httpp_parse_response(parser, header, strlen(header), localmount)) {
|
2003-02-07 05:53:38 -05:00
|
|
|
if(httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE)) {
|
|
|
|
ERROR1("Error parsing relay request: %s",
|
|
|
|
httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ERROR0("Error parsing relay request");
|
2003-03-14 21:10:19 -05:00
|
|
|
connection_close(con);
|
2003-02-07 05:53:38 -05:00
|
|
|
httpp_destroy(parser);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
client = client_create(con, parser);
|
2003-03-14 21:10:19 -05:00
|
|
|
if (!connection_create_source(client, con, parser,
|
2003-02-07 05:53:38 -05:00
|
|
|
httpp_getvar(parser, HTTPP_VAR_URI))) {
|
2003-02-25 04:40:34 -05:00
|
|
|
DEBUG0("Failed to create source");
|
2003-02-07 05:53:38 -05:00
|
|
|
client_destroy(client);
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2003-02-25 04:40:34 -05:00
|
|
|
|
2003-02-07 05:53:38 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *_slave_thread(void *arg) {
|
2003-03-14 21:10:19 -05:00
|
|
|
sock_t mastersock;
|
|
|
|
char buf[256];
|
2003-03-05 08:03:35 -05:00
|
|
|
int interval;
|
2002-08-16 10:55:56 -04:00
|
|
|
char *authheader, *data;
|
|
|
|
int len;
|
|
|
|
char *username = "relay";
|
2003-03-05 08:03:35 -05:00
|
|
|
char *password;
|
|
|
|
int max_interval;
|
2003-02-07 09:00:33 -05:00
|
|
|
relay_server *relay;
|
2003-03-05 08:03:35 -05:00
|
|
|
ice_config_t *config;
|
|
|
|
|
|
|
|
config = config_get_config();
|
|
|
|
|
|
|
|
password = config->master_password;
|
|
|
|
interval = max_interval = config->master_update_interval;
|
2002-08-17 02:25:38 -04:00
|
|
|
|
|
|
|
if(password == NULL)
|
2003-03-05 08:03:35 -05:00
|
|
|
password = config->source_password;
|
|
|
|
|
|
|
|
config_release_config();
|
2002-08-17 02:25:38 -04:00
|
|
|
|
2002-08-05 10:48:04 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
while (_initialized) {
|
2003-03-05 08:03:35 -05:00
|
|
|
if (max_interval > ++interval) {
|
2003-03-14 21:10:19 -05:00
|
|
|
thread_sleep(1000000);
|
2002-08-05 10:48:04 -04:00
|
|
|
continue;
|
|
|
|
}
|
2003-03-05 08:03:35 -05:00
|
|
|
else {
|
|
|
|
/* In case it's been reconfigured */
|
|
|
|
config = config_get_config();
|
|
|
|
max_interval = config->master_update_interval;
|
|
|
|
config_release_config();
|
|
|
|
|
2002-08-05 10:48:04 -04:00
|
|
|
interval = 0;
|
2003-03-05 08:03:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
config = config_get_config();
|
|
|
|
if(config->master_server != NULL) {
|
|
|
|
char *server = config->master_server;
|
|
|
|
int port = config->master_server_port;
|
|
|
|
config_release_config();
|
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
mastersock = sock_connect_wto(server, port, 0);
|
2002-08-05 10:48:04 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
if (mastersock == SOCK_ERROR) {
|
2003-02-07 06:46:03 -05:00
|
|
|
WARN0("Relay slave failed to contact master server to fetch stream list");
|
2003-03-14 21:10:19 -05:00
|
|
|
continue;
|
|
|
|
}
|
2003-02-07 06:46:03 -05:00
|
|
|
|
|
|
|
len = strlen(username) + strlen(password) + 1;
|
|
|
|
authheader = malloc(len+1);
|
|
|
|
strcpy(authheader, username);
|
|
|
|
strcat(authheader, ":");
|
|
|
|
strcat(authheader, password);
|
|
|
|
data = util_base64_encode(authheader);
|
2003-03-14 21:10:19 -05:00
|
|
|
sock_write(mastersock,
|
2003-02-07 06:46:03 -05:00
|
|
|
"GET /admin/streamlist HTTP/1.0\r\n"
|
|
|
|
"Authorization: Basic %s\r\n"
|
|
|
|
"\r\n", data);
|
|
|
|
free(authheader);
|
|
|
|
free(data);
|
2003-03-14 21:10:19 -05:00
|
|
|
while (sock_read_line(mastersock, buf, sizeof(buf))) {
|
2003-02-07 06:46:03 -05:00
|
|
|
if(!strlen(buf))
|
|
|
|
break;
|
|
|
|
}
|
2002-08-09 04:06:00 -04:00
|
|
|
|
2003-03-14 21:10:19 -05:00
|
|
|
while (sock_read_line(mastersock, buf, sizeof(buf))) {
|
|
|
|
avl_tree_rlock(global.source_tree);
|
|
|
|
if (!source_find_mount(buf)) {
|
|
|
|
avl_tree_unlock(global.source_tree);
|
2003-02-07 06:46:03 -05:00
|
|
|
|
2003-03-05 08:03:35 -05:00
|
|
|
create_relay_stream(server, port, buf, NULL, 0);
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
2003-02-07 06:46:03 -05:00
|
|
|
else
|
2003-03-14 21:10:19 -05:00
|
|
|
avl_tree_unlock(global.source_tree);
|
|
|
|
}
|
|
|
|
sock_close(mastersock);
|
2003-02-07 06:46:03 -05:00
|
|
|
}
|
2003-03-05 08:03:35 -05:00
|
|
|
else {
|
|
|
|
config_release_config();
|
|
|
|
}
|
2003-02-07 05:53:38 -05:00
|
|
|
|
|
|
|
/* And now, we process the individual mounts... */
|
2003-03-05 08:03:35 -05:00
|
|
|
config = config_get_config();
|
|
|
|
relay = config->relay;
|
|
|
|
thread_mutex_lock(&(config_locks()->relay_lock));
|
|
|
|
config_release_config();
|
|
|
|
|
2003-02-07 05:53:38 -05:00
|
|
|
while(relay) {
|
|
|
|
avl_tree_rlock(global.source_tree);
|
2003-02-17 06:56:12 -05:00
|
|
|
if(!source_find_mount(relay->localmount)) {
|
2003-02-07 05:53:38 -05:00
|
|
|
avl_tree_unlock(global.source_tree);
|
|
|
|
|
2003-02-12 06:04:26 -05:00
|
|
|
create_relay_stream(relay->server, relay->port, relay->mount,
|
2003-02-24 08:37:15 -05:00
|
|
|
relay->localmount, relay->mp3metadata);
|
2003-02-07 05:53:38 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
avl_tree_unlock(global.source_tree);
|
2003-02-07 06:46:03 -05:00
|
|
|
relay = relay->next;
|
2003-02-07 05:53:38 -05:00
|
|
|
}
|
2003-03-05 08:03:35 -05:00
|
|
|
|
|
|
|
thread_mutex_unlock(&(config_locks()->relay_lock));
|
2003-03-14 21:10:19 -05:00
|
|
|
}
|
|
|
|
thread_exit(0);
|
|
|
|
return NULL;
|
2002-08-05 10:48:04 -04:00
|
|
|
}
|
2003-02-07 05:53:38 -05:00
|
|
|
|