mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-09-22 04:15:54 -04:00
Added <event>: Unified handling of events.
<event> has been added and can be used within <kartoffelsalat> both in <icecast> and <mount>. <event> takes backend depending <option> child tags. Currently supported backends: - log: send message to error log. - exec: executes a program or script. - url: delivers the event via HTTP. within <mount> <on-connect> and <on-disconnect> has been replaced by <event>. Config parser can on-the-fly convert old tags. Also <authentication type="url"> within <mount> has been fixed for those cases with <option name="mount_add" .../> and <option name="mount_remove" .../> which are now on-the-fly converted by the parser to corresponding <event> tags. Please also see TAGs added as per #2098. Some include hints for documentation updates needed after this change. Those updates should take place before 2.4.2.
This commit is contained in:
parent
8f706a4c1e
commit
bdcf008b7c
@ -111,7 +111,7 @@ XIPH_PATH_CURL([
|
|||||||
AC_CHECK_DECL([CURLOPT_NOSIGNAL],
|
AC_CHECK_DECL([CURLOPT_NOSIGNAL],
|
||||||
[ AC_DEFINE([HAVE_AUTH_URL], 1, [Define to compile in auth URL support code])
|
[ AC_DEFINE([HAVE_AUTH_URL], 1, [Define to compile in auth URL support code])
|
||||||
AC_CHECK_FUNCS([curl_global_init])
|
AC_CHECK_FUNCS([curl_global_init])
|
||||||
ICECAST_OPTIONAL="$ICECAST_OPTIONAL auth_url.o"
|
ICECAST_OPTIONAL="$ICECAST_OPTIONAL auth_url.o event_url.o"
|
||||||
enable_curl="yes"
|
enable_curl="yes"
|
||||||
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$CURL_CFLAGS])
|
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$CURL_CFLAGS])
|
||||||
XIPH_VAR_PREPEND([XIPH_LIBS],[$CURL_LIBS])
|
XIPH_VAR_PREPEND([XIPH_LIBS],[$CURL_LIBS])
|
||||||
|
@ -11,6 +11,7 @@ INCLUDES = -I./common/
|
|||||||
noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
|
noinst_HEADERS = admin.h cfgfile.h logging.h sighandler.h connection.h \
|
||||||
global.h util.h slave.h source.h stats.h refbuf.h client.h \
|
global.h util.h slave.h source.h stats.h refbuf.h client.h \
|
||||||
compat.h fserve.h xslt.h yp.h md5.h \
|
compat.h fserve.h xslt.h yp.h md5.h \
|
||||||
|
event.h event_log.h event_exec.h event_url.h \
|
||||||
acl.h auth.h auth_htpasswd.h auth_url.h auth_anonymous.h auth_static.h \
|
acl.h auth.h auth_htpasswd.h auth_url.h auth_anonymous.h auth_static.h \
|
||||||
format.h format_ogg.h format_mp3.h format_ebml.h \
|
format.h format_ogg.h format_mp3.h format_ebml.h \
|
||||||
format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h \
|
format_vorbis.h format_theora.h format_flac.h format_speex.h format_midi.h \
|
||||||
@ -19,9 +20,11 @@ icecast_SOURCES = cfgfile.c main.c logging.c sighandler.c connection.c global.c
|
|||||||
util.c slave.c source.c stats.c refbuf.c client.c \
|
util.c slave.c source.c stats.c refbuf.c client.c \
|
||||||
xslt.c fserve.c admin.c md5.c \
|
xslt.c fserve.c admin.c md5.c \
|
||||||
format.c format_ogg.c format_mp3.c format_midi.c format_flac.c format_ebml.c \
|
format.c format_ogg.c format_mp3.c format_midi.c format_flac.c format_ebml.c \
|
||||||
acl.c auth.c auth_htpasswd.c auth_anonymous.c auth_static.c format_kate.c format_skeleton.c format_opus.c
|
format_kate.c format_skeleton.c format_opus.c \
|
||||||
|
event.c event_log.c event_exec.c \
|
||||||
|
acl.c auth.c auth_htpasswd.c auth_anonymous.c auth_static.c
|
||||||
EXTRA_icecast_SOURCES = yp.c \
|
EXTRA_icecast_SOURCES = yp.c \
|
||||||
auth_url.c \
|
auth_url.c event_url.c \
|
||||||
format_vorbis.c format_theora.c format_speex.c
|
format_vorbis.c format_theora.c format_speex.c
|
||||||
|
|
||||||
icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ common/net/libicenet.la common/thread/libicethread.la \
|
icecast_DEPENDENCIES = @ICECAST_OPTIONAL@ common/net/libicenet.la common/thread/libicethread.la \
|
||||||
|
@ -470,6 +470,7 @@ auth_t *auth_get_authenticator(xmlNodePtr node)
|
|||||||
auth->method[i] = 1;
|
auth->method[i] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* BEFORE RELEASE 2.4.2 TODO: Migrate this to config_parse_options(). */
|
||||||
option = node->xmlChildrenNode;
|
option = node->xmlChildrenNode;
|
||||||
while (option)
|
while (option)
|
||||||
{
|
{
|
||||||
|
153
src/cfgfile.c
153
src/cfgfile.c
@ -33,6 +33,7 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
#include "event.h"
|
||||||
|
|
||||||
/* for config_reread_config() */
|
/* for config_reread_config() */
|
||||||
#include "yp.h"
|
#include "yp.h"
|
||||||
@ -58,7 +59,7 @@
|
|||||||
#define CONFIG_DEFAULT_PLAYLIST_LOG NULL
|
#define CONFIG_DEFAULT_PLAYLIST_LOG NULL
|
||||||
#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
|
#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
|
||||||
#define CONFIG_DEFAULT_ERROR_LOG "error.log"
|
#define CONFIG_DEFAULT_ERROR_LOG "error.log"
|
||||||
#define CONFIG_DEFAULT_LOG_LEVEL 3
|
#define CONFIG_DEFAULT_LOG_LEVEL ICECAST_LOGLEVEL_INFO
|
||||||
#define CONFIG_DEFAULT_CHROOT 0
|
#define CONFIG_DEFAULT_CHROOT 0
|
||||||
#define CONFIG_DEFAULT_CHUID 0
|
#define CONFIG_DEFAULT_CHUID 0
|
||||||
#define CONFIG_DEFAULT_USER NULL
|
#define CONFIG_DEFAULT_USER NULL
|
||||||
@ -102,6 +103,7 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
|
|||||||
static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node,
|
static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node,
|
||||||
ice_config_t *c);
|
ice_config_t *c);
|
||||||
static void _add_server(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
|
static void _add_server(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
|
||||||
|
static void _parse_events(event_registration_t **events, xmlNodePtr node);
|
||||||
|
|
||||||
static void merge_mounts(mount_proxy * dst, mount_proxy * src);
|
static void merge_mounts(mount_proxy * dst, mount_proxy * src);
|
||||||
static inline void _merge_mounts_all(ice_config_t *c);
|
static inline void _merge_mounts_all(ice_config_t *c);
|
||||||
@ -146,20 +148,6 @@ static inline int __parse_public(const char *str) {
|
|||||||
return util_str_to_bool(str);
|
return util_str_to_bool(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int __parse_loglevel(const char *str) {
|
|
||||||
if (strcasecmp(str, "debug") == 0 || strcasecmp(str, "DBUG") == 0)
|
|
||||||
return 4;
|
|
||||||
if (strcasecmp(str, "information") == 0 || strcasecmp(str, "INFO") == 0)
|
|
||||||
return 3;
|
|
||||||
if (strcasecmp(str, "warning") == 0 || strcasecmp(str, "WARN") == 0)
|
|
||||||
return 2;
|
|
||||||
if (strcasecmp(str, "error") == 0 || strcasecmp(str, "EROR") == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
/* gussing it is old-style numerical setting */
|
|
||||||
return atoi(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __append_old_style_auth(auth_stack_t **stack, const char *name, const char *type, const char *username, const char *password, const char *method, const char *allow_method, int allow_web, const char *allow_admin) {
|
static void __append_old_style_auth(auth_stack_t **stack, const char *name, const char *type, const char *username, const char *password, const char *method, const char *allow_method, int allow_web, const char *allow_admin) {
|
||||||
xmlNodePtr role, user, pass;
|
xmlNodePtr role, user, pass;
|
||||||
auth_t *auth;
|
auth_t *auth;
|
||||||
@ -266,6 +254,45 @@ static void __append_old_style_urlauth(auth_stack_t **stack, const char *client_
|
|||||||
xmlFreeNode(role);
|
xmlFreeNode(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __append_old_style_exec_event(event_registration_t **list, const char *trigger, const char *executable) {
|
||||||
|
xmlNodePtr exec;
|
||||||
|
event_registration_t *er;
|
||||||
|
|
||||||
|
exec = xmlNewNode(NULL, XMLSTR("event"));
|
||||||
|
|
||||||
|
xmlSetProp(exec, XMLSTR("type"), XMLSTR("exec"));
|
||||||
|
xmlSetProp(exec, XMLSTR("trigger"), XMLSTR(trigger));
|
||||||
|
|
||||||
|
__append_option_tag(exec, "executable", executable);
|
||||||
|
|
||||||
|
er = event_new_from_xml_node(exec);
|
||||||
|
event_registration_push(list, er);
|
||||||
|
event_registration_release(er);
|
||||||
|
|
||||||
|
xmlFreeNode(exec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __append_old_style_url_event(event_registration_t **list, const char *trigger, const char *url, const char *action, const char *username, const char *password) {
|
||||||
|
xmlNodePtr exec;
|
||||||
|
event_registration_t *er;
|
||||||
|
|
||||||
|
exec = xmlNewNode(NULL, XMLSTR("event"));
|
||||||
|
|
||||||
|
xmlSetProp(exec, XMLSTR("type"), XMLSTR("url"));
|
||||||
|
xmlSetProp(exec, XMLSTR("trigger"), XMLSTR(trigger));
|
||||||
|
|
||||||
|
__append_option_tag(exec, "url", url);
|
||||||
|
__append_option_tag(exec, "action", action);
|
||||||
|
__append_option_tag(exec, "username", username);
|
||||||
|
__append_option_tag(exec, "password", password);
|
||||||
|
|
||||||
|
er = event_new_from_xml_node(exec);
|
||||||
|
event_registration_push(list, er);
|
||||||
|
event_registration_release(er);
|
||||||
|
|
||||||
|
xmlFreeNode(exec);
|
||||||
|
}
|
||||||
|
|
||||||
static void config_clear_http_header(ice_config_http_header_t *header) {
|
static void config_clear_http_header(ice_config_http_header_t *header) {
|
||||||
ice_config_http_header_t *old;
|
ice_config_http_header_t *old;
|
||||||
|
|
||||||
@ -323,8 +350,6 @@ static void config_clear_mount(mount_proxy *mount)
|
|||||||
if (mount->mountname) xmlFree (mount->mountname);
|
if (mount->mountname) xmlFree (mount->mountname);
|
||||||
if (mount->dumpfile) xmlFree (mount->dumpfile);
|
if (mount->dumpfile) xmlFree (mount->dumpfile);
|
||||||
if (mount->intro_filename) xmlFree (mount->intro_filename);
|
if (mount->intro_filename) xmlFree (mount->intro_filename);
|
||||||
if (mount->on_connect) xmlFree (mount->on_connect);
|
|
||||||
if (mount->on_disconnect) xmlFree (mount->on_disconnect);
|
|
||||||
if (mount->fallback_mount) xmlFree (mount->fallback_mount);
|
if (mount->fallback_mount) xmlFree (mount->fallback_mount);
|
||||||
if (mount->stream_name) xmlFree (mount->stream_name);
|
if (mount->stream_name) xmlFree (mount->stream_name);
|
||||||
if (mount->stream_description) xmlFree (mount->stream_description);
|
if (mount->stream_description) xmlFree (mount->stream_description);
|
||||||
@ -336,6 +361,8 @@ static void config_clear_mount(mount_proxy *mount)
|
|||||||
if (mount->cluster_password) xmlFree (mount->cluster_password);
|
if (mount->cluster_password) xmlFree (mount->cluster_password);
|
||||||
if (mount->authstack) auth_stack_release(mount->authstack);
|
if (mount->authstack) auth_stack_release(mount->authstack);
|
||||||
|
|
||||||
|
event_registration_release(mount->event);
|
||||||
|
|
||||||
config_clear_http_header(mount->http_headers);
|
config_clear_http_header(mount->http_headers);
|
||||||
free (mount);
|
free (mount);
|
||||||
}
|
}
|
||||||
@ -391,6 +418,8 @@ void config_clear(ice_config_t *c)
|
|||||||
if (c->group) xmlFree(c->group);
|
if (c->group) xmlFree(c->group);
|
||||||
if (c->mimetypes_fn) xmlFree (c->mimetypes_fn);
|
if (c->mimetypes_fn) xmlFree (c->mimetypes_fn);
|
||||||
|
|
||||||
|
event_registration_release(c->event);
|
||||||
|
|
||||||
while ((c->listen_sock = config_clear_listener (c->listen_sock)))
|
while ((c->listen_sock = config_clear_listener (c->listen_sock)))
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -744,6 +773,9 @@ static void _parse_root(xmlDocPtr doc, xmlNodePtr node,
|
|||||||
_parse_logging(doc, node->xmlChildrenNode, configuration);
|
_parse_logging(doc, node->xmlChildrenNode, configuration);
|
||||||
} else if (xmlStrcmp (node->name, XMLSTR("security")) == 0) {
|
} else if (xmlStrcmp (node->name, XMLSTR("security")) == 0) {
|
||||||
_parse_security(doc, node->xmlChildrenNode, configuration);
|
_parse_security(doc, node->xmlChildrenNode, configuration);
|
||||||
|
} else if (xmlStrcmp (node->name, XMLSTR("kartoffelsalat")) == 0) {
|
||||||
|
/* BEFORE RELEASE NEXT REVIEW: Should this tag really be <kartoffelsalat>? */
|
||||||
|
_parse_events(&configuration->event, node->xmlChildrenNode);
|
||||||
}
|
}
|
||||||
} while ((node = node->next));
|
} while ((node = node->next));
|
||||||
|
|
||||||
@ -962,7 +994,11 @@ static void _parse_mount_oldstyle_authentication(mount_proxy *mount, xmlNodePtr
|
|||||||
child = child->next;
|
child = child->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: FIXME: XXX: implement support for mount_add and mount_remove (using only username and password) here. */
|
if (mount_add)
|
||||||
|
__append_old_style_url_event(&mount->event, "source-connect", mount_add, "mount_add", username, password);
|
||||||
|
if (mount_remove)
|
||||||
|
__append_old_style_url_event(&mount->event, "source-disconnect", mount_add, "mount_remove", username, password);
|
||||||
|
|
||||||
__append_old_style_urlauth(authstack, listener_add, listener_remove, "listener_add", "listener_remove", username, password, 0, auth_header, timelimit_header, headers, header_prefix);
|
__append_old_style_urlauth(authstack, listener_add, listener_remove, "listener_add", "listener_remove", username, password, 0, auth_header, timelimit_header, headers, header_prefix);
|
||||||
__append_old_style_urlauth(authstack, stream_auth, NULL, "stream_auth", NULL, username, password, 1, auth_header, timelimit_header, headers, header_prefix);
|
__append_old_style_urlauth(authstack, stream_auth, NULL, "stream_auth", NULL, username, password, 1, auth_header, timelimit_header, headers, header_prefix);
|
||||||
if (listener_add)
|
if (listener_add)
|
||||||
@ -1123,12 +1159,18 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (xmlStrcmp (node->name, XMLSTR("on-connect")) == 0) {
|
else if (xmlStrcmp (node->name, XMLSTR("on-connect")) == 0) {
|
||||||
mount->on_connect = (char *)xmlNodeListGetString(
|
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||||
doc, node->xmlChildrenNode, 1);
|
if (tmp) {
|
||||||
|
__append_old_style_exec_event(&mount->event, "source-connect", tmp);
|
||||||
|
xmlFree(tmp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (xmlStrcmp (node->name, XMLSTR("on-disconnect")) == 0) {
|
else if (xmlStrcmp (node->name, XMLSTR("on-disconnect")) == 0) {
|
||||||
mount->on_disconnect = (char *)xmlNodeListGetString(
|
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||||
doc, node->xmlChildrenNode, 1);
|
if (tmp) {
|
||||||
|
__append_old_style_exec_event(&mount->event, "source-disconnect", tmp);
|
||||||
|
xmlFree(tmp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (xmlStrcmp (node->name, XMLSTR("max-listener-duration")) == 0) {
|
else if (xmlStrcmp (node->name, XMLSTR("max-listener-duration")) == 0) {
|
||||||
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||||
@ -1181,6 +1223,9 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
|
|||||||
doc, node->xmlChildrenNode, 1);
|
doc, node->xmlChildrenNode, 1);
|
||||||
} else if (xmlStrcmp (node->name, XMLSTR("http-headers")) == 0) {
|
} else if (xmlStrcmp (node->name, XMLSTR("http-headers")) == 0) {
|
||||||
_parse_http_headers(doc, node->xmlChildrenNode, &(mount->http_headers));
|
_parse_http_headers(doc, node->xmlChildrenNode, &(mount->http_headers));
|
||||||
|
} else if (xmlStrcmp (node->name, XMLSTR("kartoffelsalat")) == 0) {
|
||||||
|
/* BEFORE RELEASE NEXT REVIEW: Should this tag really be <kartoffelsalat>? */
|
||||||
|
_parse_events(&mount->event, node->xmlChildrenNode);
|
||||||
}
|
}
|
||||||
} while ((node = node->next));
|
} while ((node = node->next));
|
||||||
|
|
||||||
@ -1689,7 +1734,7 @@ static void _parse_logging(xmlDocPtr doc, xmlNodePtr node,
|
|||||||
if (tmp) xmlFree(tmp);
|
if (tmp) xmlFree(tmp);
|
||||||
} else if (xmlStrcmp (node->name, XMLSTR("loglevel")) == 0) {
|
} else if (xmlStrcmp (node->name, XMLSTR("loglevel")) == 0) {
|
||||||
char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||||
configuration->loglevel = __parse_loglevel(tmp);
|
configuration->loglevel = util_str_to_loglevel(tmp);
|
||||||
if (tmp) xmlFree(tmp);
|
if (tmp) xmlFree(tmp);
|
||||||
} else if (xmlStrcmp (node->name, XMLSTR("logarchive")) == 0) {
|
} else if (xmlStrcmp (node->name, XMLSTR("logarchive")) == 0) {
|
||||||
char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
char *tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
|
||||||
@ -1779,6 +1824,64 @@ static void _add_server(xmlDocPtr doc, xmlNodePtr node,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _parse_events(event_registration_t **events, xmlNodePtr node) {
|
||||||
|
while (node) {
|
||||||
|
if (xmlStrcmp(node->name, XMLSTR("event")) == 0) {
|
||||||
|
event_registration_t *reg = event_new_from_xml_node(node);
|
||||||
|
event_registration_push(events, reg);
|
||||||
|
event_registration_release(reg);
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config_options_t *config_parse_options(xmlNodePtr node) {
|
||||||
|
config_options_t *ret = NULL;
|
||||||
|
config_options_t *cur = NULL;
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
node = node->xmlChildrenNode;
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (xmlStrcmp(node->name, XMLSTR("option")) != 0)
|
||||||
|
continue;
|
||||||
|
if (cur) {
|
||||||
|
cur->next = calloc(1, sizeof(config_options_t));
|
||||||
|
cur = cur->next;
|
||||||
|
} else {
|
||||||
|
cur = ret = calloc(1, sizeof(config_options_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
cur->type = (char *)xmlGetProp(node, XMLSTR("type"));
|
||||||
|
cur->name = (char *)xmlGetProp(node, XMLSTR("name"));
|
||||||
|
cur->value = (char *)xmlGetProp(node, XMLSTR("value"));
|
||||||
|
|
||||||
|
/* map type="default" to NULL. This is to avoid many extra xmlCharStrdup()s. */
|
||||||
|
if (cur->type && strcmp(cur->type, "default") == 0) {
|
||||||
|
xmlFree(cur->type);
|
||||||
|
cur->type = NULL;
|
||||||
|
}
|
||||||
|
} while ((node = node->next));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_clear_options(config_options_t *options) {
|
||||||
|
while (options) {
|
||||||
|
config_options_t *opt = options;
|
||||||
|
options = opt->next;
|
||||||
|
if (opt->type) xmlFree(opt->type);
|
||||||
|
if (opt->name) xmlFree(opt->name);
|
||||||
|
if (opt->value) xmlFree(opt->value);
|
||||||
|
free(opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void merge_mounts(mount_proxy * dst, mount_proxy * src) {
|
static void merge_mounts(mount_proxy * dst, mount_proxy * src) {
|
||||||
ice_config_http_header_t *http_header_next;
|
ice_config_http_header_t *http_header_next;
|
||||||
ice_config_http_header_t **http_header_tail;
|
ice_config_http_header_t **http_header_tail;
|
||||||
@ -1814,10 +1917,6 @@ static void merge_mounts(mount_proxy * dst, mount_proxy * src) {
|
|||||||
dst->mp3_meta_interval = src->mp3_meta_interval;
|
dst->mp3_meta_interval = src->mp3_meta_interval;
|
||||||
if (!dst->cluster_password)
|
if (!dst->cluster_password)
|
||||||
dst->cluster_password = (char*)xmlStrdup((xmlChar*)src->cluster_password);
|
dst->cluster_password = (char*)xmlStrdup((xmlChar*)src->cluster_password);
|
||||||
if (!dst->on_connect)
|
|
||||||
dst->on_connect = (char*)xmlStrdup((xmlChar*)src->on_connect);
|
|
||||||
if (!dst->on_disconnect)
|
|
||||||
dst->on_disconnect = (char*)xmlStrdup((xmlChar*)src->on_disconnect);
|
|
||||||
if (!dst->max_listener_duration)
|
if (!dst->max_listener_duration)
|
||||||
dst->max_listener_duration = src->max_listener_duration;
|
dst->max_listener_duration = src->max_listener_duration;
|
||||||
if (!dst->stream_name)
|
if (!dst->stream_name)
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
struct _mount_proxy;
|
struct _mount_proxy;
|
||||||
|
|
||||||
|
#include <libxml/tree.h>
|
||||||
#include "common/thread/thread.h"
|
#include "common/thread/thread.h"
|
||||||
#include "common/avl/avl.h"
|
#include "common/avl/avl.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
@ -58,6 +59,7 @@ typedef struct ice_config_dir_tag {
|
|||||||
} ice_config_dir_t;
|
} ice_config_dir_t;
|
||||||
|
|
||||||
typedef struct _config_options {
|
typedef struct _config_options {
|
||||||
|
char *type;
|
||||||
char *name;
|
char *name;
|
||||||
char *value;
|
char *value;
|
||||||
struct _config_options *next;
|
struct _config_options *next;
|
||||||
@ -99,10 +101,10 @@ typedef struct _mount_proxy {
|
|||||||
|
|
||||||
ice_config_http_header_t *http_headers; /* additional HTTP headers */
|
ice_config_http_header_t *http_headers; /* additional HTTP headers */
|
||||||
|
|
||||||
|
struct event_registration_tag *event;
|
||||||
|
|
||||||
char *cluster_password;
|
char *cluster_password;
|
||||||
struct auth_stack_tag *authstack;
|
struct auth_stack_tag *authstack;
|
||||||
char *on_connect;
|
|
||||||
char *on_disconnect;
|
|
||||||
unsigned int max_listener_duration;
|
unsigned int max_listener_duration;
|
||||||
|
|
||||||
char *stream_name;
|
char *stream_name;
|
||||||
@ -155,6 +157,8 @@ typedef struct ice_config_tag {
|
|||||||
char *shoutcast_mount;
|
char *shoutcast_mount;
|
||||||
struct auth_stack_tag *authstack;
|
struct auth_stack_tag *authstack;
|
||||||
|
|
||||||
|
struct event_registration_tag *event;
|
||||||
|
|
||||||
int touch_interval;
|
int touch_interval;
|
||||||
ice_config_dir_t *dir_list;
|
ice_config_dir_t *dir_list;
|
||||||
|
|
||||||
@ -226,6 +230,9 @@ void config_clear(ice_config_t *config);
|
|||||||
mount_proxy *config_find_mount (ice_config_t *config, const char *mount, mount_type type);
|
mount_proxy *config_find_mount (ice_config_t *config, const char *mount, mount_type type);
|
||||||
listener_t *config_get_listen_sock (ice_config_t *config, connection_t *con);
|
listener_t *config_get_listen_sock (ice_config_t *config, connection_t *con);
|
||||||
|
|
||||||
|
config_options_t *config_parse_options(xmlNodePtr node);
|
||||||
|
void config_clear_options(config_options_t *options);
|
||||||
|
|
||||||
int config_rehash(void);
|
int config_rehash(void);
|
||||||
|
|
||||||
ice_config_locks *config_locks(void);
|
ice_config_locks *config_locks(void);
|
||||||
|
376
src/event.c
Normal file
376
src/event.c
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2014, Philipp Schafft <lion@lion.leolix.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "event.h"
|
||||||
|
#include "event_log.h"
|
||||||
|
#include "event_exec.h"
|
||||||
|
#include "event_url.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "admin.h"
|
||||||
|
|
||||||
|
#define CATMODULE "event"
|
||||||
|
|
||||||
|
static mutex_t event_lock;
|
||||||
|
static event_t *event_queue = NULL;
|
||||||
|
static int event_running = 0;
|
||||||
|
static thread_type *event_thread = NULL;
|
||||||
|
|
||||||
|
/* work with event_t* */
|
||||||
|
static void event_addref(event_t *event) {
|
||||||
|
if (!event)
|
||||||
|
return;
|
||||||
|
event->refcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_release(event_t *event) {
|
||||||
|
if (!event)
|
||||||
|
return;
|
||||||
|
event->refcount--;
|
||||||
|
if (event->refcount)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(event->trigger);
|
||||||
|
free(event->uri);
|
||||||
|
free(event->connection_ip);
|
||||||
|
free(event->client_role);
|
||||||
|
free(event->client_username);
|
||||||
|
free(event->client_useragent);
|
||||||
|
event_release(event->next);
|
||||||
|
|
||||||
|
free(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_push(event_t **event, event_t *next) {
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
if (!event || !next)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (*event && i < 128) {
|
||||||
|
event = &(*event)->next;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 128) {
|
||||||
|
ICECAST_LOG_ERROR("Can not push event %p into queue. Queue is full.", event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_addref(*event = next);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_push_reglist(event_t *event, event_registration_t *reglist) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!event || !reglist)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < (sizeof(event->reglist)/sizeof(*event->reglist)); i++) {
|
||||||
|
if (!event->reglist[i]) {
|
||||||
|
event_registration_addref(event->reglist[i] = reglist);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ICECAST_LOG_ERROR("Can not push reglist %p into event %p. No space left on event.", reglist, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static event_t *event_new(const char *trigger) {
|
||||||
|
event_t *ret;
|
||||||
|
|
||||||
|
if (!trigger)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret = calloc(1, sizeof(event_t));
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret->refcount = 1;
|
||||||
|
ret->trigger = strdup(trigger);
|
||||||
|
ret->client_admin_command = ADMIN_COMMAND_ERROR;
|
||||||
|
|
||||||
|
if (!ret->trigger) {
|
||||||
|
event_release(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* subsystem functions */
|
||||||
|
static inline void _try_event(event_registration_t *er, event_t *event) {
|
||||||
|
/* er is already locked */
|
||||||
|
if (strcmp(er->trigger, event->trigger) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (er->emit)
|
||||||
|
er->emit(er->state, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _try_registrations(event_registration_t *er, event_t *event) {
|
||||||
|
if (!er)
|
||||||
|
return;
|
||||||
|
|
||||||
|
thread_mutex_lock(&er->lock);
|
||||||
|
while (1) {
|
||||||
|
/* try registration */
|
||||||
|
_try_event(er, event);
|
||||||
|
|
||||||
|
/* go to next registration */
|
||||||
|
if (er->next) {
|
||||||
|
event_registration_t *next = er->next;
|
||||||
|
|
||||||
|
thread_mutex_lock(&next->lock);
|
||||||
|
thread_mutex_unlock(&er->lock);
|
||||||
|
er = next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thread_mutex_unlock(&er->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *event_run_thread (void *arg) {
|
||||||
|
int running = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
event_t *event;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
thread_mutex_lock(&event_lock);
|
||||||
|
running = event_running;
|
||||||
|
if (event_queue) {
|
||||||
|
event = event_queue;
|
||||||
|
event_queue = event_queue->next;
|
||||||
|
} else {
|
||||||
|
event = NULL;
|
||||||
|
}
|
||||||
|
thread_mutex_unlock(&event_lock);
|
||||||
|
|
||||||
|
/* sleep if nothing todo and then try again */
|
||||||
|
if (!event) {
|
||||||
|
thread_sleep(150000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < (sizeof(event->reglist)/sizeof(*event->reglist)); i++)
|
||||||
|
_try_registrations(event->reglist[i], event);
|
||||||
|
|
||||||
|
event_release(event);
|
||||||
|
} while (running);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_initialise(void) {
|
||||||
|
/* create mutex */
|
||||||
|
thread_mutex_create(&event_lock);
|
||||||
|
|
||||||
|
/* initialise everything */
|
||||||
|
thread_mutex_lock(&event_lock);
|
||||||
|
event_running = 1;
|
||||||
|
thread_mutex_unlock(&event_lock);
|
||||||
|
|
||||||
|
/* start thread */
|
||||||
|
event_thread = thread_create("events thread", event_run_thread, NULL, THREAD_ATTACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_shutdown(void) {
|
||||||
|
/* stop thread */
|
||||||
|
if (!event_running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
thread_mutex_lock(&event_lock);
|
||||||
|
event_running = 0;
|
||||||
|
thread_mutex_unlock(&event_lock);
|
||||||
|
|
||||||
|
/* join thread as soon as it stopped */
|
||||||
|
thread_join(event_thread);
|
||||||
|
|
||||||
|
/* shutdown everything */
|
||||||
|
thread_mutex_lock(&event_lock);
|
||||||
|
event_thread = NULL;
|
||||||
|
event_release(event_queue);
|
||||||
|
event_queue = NULL;
|
||||||
|
thread_mutex_unlock(&event_lock);
|
||||||
|
|
||||||
|
/* destry mutex */
|
||||||
|
thread_mutex_destroy(&event_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* basic functions to work with event registrations */
|
||||||
|
event_registration_t * event_new_from_xml_node(xmlNodePtr node) {
|
||||||
|
event_registration_t *ret = calloc(1, sizeof(event_registration_t));
|
||||||
|
config_options_t *options;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if(!ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret->refcount = 1;
|
||||||
|
|
||||||
|
/* BEFORE RELEASE 2.4.2 DOCUMENT: Document <event type="..." trigger="..."> */
|
||||||
|
ret->type = (char*)xmlGetProp(node, XMLSTR("type"));
|
||||||
|
ret->trigger = (char*)xmlGetProp(node, XMLSTR("trigger"));
|
||||||
|
|
||||||
|
if (!ret->type || !ret->trigger) {
|
||||||
|
ICECAST_LOG_ERROR("Event node isn't complet. Type or Trigger missing.");
|
||||||
|
event_registration_release(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
options = config_parse_options(node);
|
||||||
|
if (strcmp(ret->type, EVENT_TYPE_LOG) == 0) {
|
||||||
|
rv = event_get_log(ret, options);
|
||||||
|
} else if (strcmp(ret->type, EVENT_TYPE_EXEC) == 0) {
|
||||||
|
rv = event_get_exec(ret, options);
|
||||||
|
#ifdef HAVE_AUTH_URL
|
||||||
|
} else if (strcmp(ret->type, EVENT_TYPE_URL) == 0) {
|
||||||
|
rv = event_get_url(ret, options);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
ICECAST_LOG_ERROR("Event backend %s is unknown.", ret->type);
|
||||||
|
rv = -1;
|
||||||
|
}
|
||||||
|
config_clear_options(options);
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
|
ICECAST_LOG_ERROR("Can not set up event backend %s for trigger %s", ret->type, ret->trigger);
|
||||||
|
event_registration_release(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_registration_addref(event_registration_t * er) {
|
||||||
|
if(!er)
|
||||||
|
return;
|
||||||
|
thread_mutex_lock(&er->lock);
|
||||||
|
er->refcount++;
|
||||||
|
thread_mutex_unlock(&er->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_registration_release(event_registration_t *er) {
|
||||||
|
if(!er)
|
||||||
|
return;
|
||||||
|
thread_mutex_lock(&er->lock);
|
||||||
|
er->refcount--;
|
||||||
|
|
||||||
|
if (er->refcount) {
|
||||||
|
thread_mutex_unlock(&er->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (er->next)
|
||||||
|
event_registration_release(er->next);
|
||||||
|
|
||||||
|
xmlFree(er->type);
|
||||||
|
xmlFree(er->trigger);
|
||||||
|
|
||||||
|
if (er->free)
|
||||||
|
er->free(er->state);
|
||||||
|
|
||||||
|
thread_mutex_unlock(&er->lock);
|
||||||
|
thread_mutex_destroy(&er->lock);
|
||||||
|
free(er);
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_registration_push(event_registration_t **er, event_registration_t *tail) {
|
||||||
|
event_registration_t *next, *cur;
|
||||||
|
|
||||||
|
if (!er || !tail)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!*er) {
|
||||||
|
event_registration_addref(*er = tail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_registration_addref(cur = *er);
|
||||||
|
thread_mutex_lock(&cur->lock);
|
||||||
|
while (1) {
|
||||||
|
next = cur->next;
|
||||||
|
if (!cur->next)
|
||||||
|
break;
|
||||||
|
|
||||||
|
event_registration_addref(next);
|
||||||
|
thread_mutex_unlock(&cur->lock);
|
||||||
|
event_registration_release(cur);
|
||||||
|
cur = next;
|
||||||
|
thread_mutex_lock(&cur->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
event_registration_addref(cur->next = tail);
|
||||||
|
thread_mutex_unlock(&cur->lock);
|
||||||
|
event_registration_release(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* event signaling */
|
||||||
|
void event_emit(event_t *event) {
|
||||||
|
thread_mutex_lock(&event_lock);
|
||||||
|
event_push(&event_queue, event);
|
||||||
|
thread_mutex_unlock(&event_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this function needs to extract all the infos from the client, source and mount object
|
||||||
|
* as after return the pointers become invalid.
|
||||||
|
*/
|
||||||
|
void event_emit_clientevent(const char *trigger, client_t *client, const char *uri) {
|
||||||
|
event_t *event = event_new(trigger);
|
||||||
|
ice_config_t *config;
|
||||||
|
mount_proxy *mount;
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
ICECAST_LOG_ERROR("Can not create event.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client) {
|
||||||
|
const char *tmp;
|
||||||
|
event->connection_id = client->con->id;
|
||||||
|
event->connection_time = client->con->con_time;
|
||||||
|
event->client_admin_command = client->admin_command;
|
||||||
|
event->connection_ip = strdup(client->con->ip);
|
||||||
|
if (client->role)
|
||||||
|
event->client_role = strdup(client->role);
|
||||||
|
if (client->username)
|
||||||
|
event->client_username = strdup(client->username);
|
||||||
|
tmp = httpp_getvar(client->parser, "user-agent");
|
||||||
|
if (tmp)
|
||||||
|
event->client_useragent = strdup(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri)
|
||||||
|
event->uri = strdup(uri);
|
||||||
|
|
||||||
|
config = config_get_config();
|
||||||
|
event_push_reglist(event, config->event);
|
||||||
|
|
||||||
|
mount = config_find_mount(config, uri, MOUNT_TYPE_NORMAL);
|
||||||
|
if (mount && mount->mounttype == MOUNT_TYPE_NORMAL)
|
||||||
|
event_push_reglist(event, mount->event);
|
||||||
|
|
||||||
|
mount = config_find_mount(config, uri, MOUNT_TYPE_DEFAULT);
|
||||||
|
if (mount && mount->mounttype == MOUNT_TYPE_DEFAULT)
|
||||||
|
event_push_reglist(event, mount->event);
|
||||||
|
config_release_config();
|
||||||
|
|
||||||
|
event_emit(event);
|
||||||
|
event_release(event);
|
||||||
|
}
|
108
src/event.h
Normal file
108
src/event.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2014, Philipp Schafft <lion@lion.leolix.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EVENT_H__
|
||||||
|
#define __EVENT_H__
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <libxml/xmlmemory.h>
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/tree.h>
|
||||||
|
#include "common/httpp/httpp.h"
|
||||||
|
|
||||||
|
#include "cfgfile.h"
|
||||||
|
#include "client.h"
|
||||||
|
#include "source.h"
|
||||||
|
|
||||||
|
/* implemented */
|
||||||
|
#define EVENT_TYPE_LOG "log"
|
||||||
|
#define EVENT_TYPE_EXEC "exec"
|
||||||
|
/* not implemented */
|
||||||
|
#define EVENT_TYPE_URL "url"
|
||||||
|
|
||||||
|
#define MAX_REGLISTS_PER_EVENT 8
|
||||||
|
|
||||||
|
struct event_registration_tag;
|
||||||
|
typedef struct event_registration_tag event_registration_t;
|
||||||
|
|
||||||
|
struct event_tag;
|
||||||
|
typedef struct event_tag event_t;
|
||||||
|
/* this has no lock member to protect multiple accesses as every non-readonly access is within event.c
|
||||||
|
* and is protected by global lock or on the same thread anyway.
|
||||||
|
*/
|
||||||
|
struct event_tag {
|
||||||
|
/* refernece counter */
|
||||||
|
size_t refcount;
|
||||||
|
/* reference to next element in chain */
|
||||||
|
event_t *next;
|
||||||
|
|
||||||
|
/* event_registration lists.
|
||||||
|
* They are referenced here to avoid the need to access
|
||||||
|
* config and global client list each time an event is emitted.
|
||||||
|
*/
|
||||||
|
event_registration_t *reglist[MAX_REGLISTS_PER_EVENT];
|
||||||
|
|
||||||
|
/* trigger name */
|
||||||
|
char *trigger;
|
||||||
|
|
||||||
|
/* from client */
|
||||||
|
char *uri; /* from context */
|
||||||
|
unsigned long connection_id; /* from client->con->id */
|
||||||
|
char *connection_ip; /* from client->con->ip */
|
||||||
|
time_t connection_time; /* from client->con->con_time */
|
||||||
|
char *client_role; /* from client->role */
|
||||||
|
char *client_username; /* from client->username */
|
||||||
|
char *client_useragent; /* from httpp_getvar(client->parser, "user-agent") */
|
||||||
|
int client_admin_command; /* from client->admin_command */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct event_registration_tag {
|
||||||
|
/* refernece counter */
|
||||||
|
size_t refcount;
|
||||||
|
/* reference to next element in chain */
|
||||||
|
event_registration_t *next;
|
||||||
|
|
||||||
|
/* protection */
|
||||||
|
mutex_t lock;
|
||||||
|
|
||||||
|
/* type of event */
|
||||||
|
char *type;
|
||||||
|
|
||||||
|
/* trigger name */
|
||||||
|
char *trigger;
|
||||||
|
|
||||||
|
/* backend state */
|
||||||
|
void *state;
|
||||||
|
|
||||||
|
/* emit events */
|
||||||
|
int (*emit)(void *state, event_t *event);
|
||||||
|
|
||||||
|
/* free backend state */
|
||||||
|
void (*free)(void *state);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* subsystem functions */
|
||||||
|
void event_initialise(void);
|
||||||
|
void event_shutdown(void);
|
||||||
|
|
||||||
|
|
||||||
|
/* basic functions to work with event registrations */
|
||||||
|
event_registration_t * event_new_from_xml_node(xmlNodePtr node);
|
||||||
|
|
||||||
|
void event_registration_addref(event_registration_t *er);
|
||||||
|
void event_registration_release(event_registration_t *er);
|
||||||
|
void event_registration_push(event_registration_t **er, event_registration_t *tail);
|
||||||
|
|
||||||
|
/* event signaling */
|
||||||
|
void event_emit_clientevent(const char *trigger, client_t *client, const char *uri);
|
||||||
|
#define event_emit_global(x) event_emit_clientevent((x), NULL, NULL)
|
||||||
|
|
||||||
|
#endif
|
209
src/event_exec.c
Normal file
209
src/event_exec.c
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/wait.h>
|
||||||
|
/* for __setup_empty_script_environment() */
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "event.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#define CATMODULE "event_exec"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct event_exec {
|
||||||
|
/* REVIEW: Some ideas for future work:
|
||||||
|
<option type="environ" name="TEST" value="Blubb" />
|
||||||
|
<option name="argument" value="--test" />
|
||||||
|
<option type="argument" value="--test" />
|
||||||
|
<option name="default_arguments" value="false" />
|
||||||
|
*/
|
||||||
|
/* name and path of executable */
|
||||||
|
char *executable;
|
||||||
|
} event_exec_t;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* TODO #2101: Implement script executing on win* */
|
||||||
|
#else
|
||||||
|
/* this sets up the new environment for script execution.
|
||||||
|
* We ignore most failtures as we can not handle them anyway.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_SETENV
|
||||||
|
static inline void __update_environ(const char *name, const char *value) {
|
||||||
|
if (!name || !value) return;
|
||||||
|
setenv(name, value, 1);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define __update_environ(x,y)
|
||||||
|
#endif
|
||||||
|
static inline void __setup_empty_script_environment(event_exec_t *self, event_t *event) {
|
||||||
|
ice_config_t * config = config_get_config();
|
||||||
|
mount_proxy *mountinfo;
|
||||||
|
source_t *source;
|
||||||
|
char buf[80];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* close at least the first 1024 handles */
|
||||||
|
for (i = 0; i < 1024; i++)
|
||||||
|
close(i);
|
||||||
|
|
||||||
|
/* open null device */
|
||||||
|
i = open(config->null_device, O_RDWR);
|
||||||
|
if (i != -1) {
|
||||||
|
/* attach null device to stdin, stdout and stderr */
|
||||||
|
if (i != 0)
|
||||||
|
dup2(i, 0);
|
||||||
|
if (i != 1)
|
||||||
|
dup2(i, 1);
|
||||||
|
if (i != 2)
|
||||||
|
dup2(i, 2);
|
||||||
|
|
||||||
|
/* close null device */
|
||||||
|
if (i > 2)
|
||||||
|
close(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
__update_environ("ICECAST_VERSION", ICECAST_VERSION_STRING);
|
||||||
|
__update_environ("ICECAST_HOSTNAME", config->hostname);
|
||||||
|
__update_environ("ICECAST_ADMIN", config->admin);
|
||||||
|
__update_environ("ICECAST_LOGDIR", config->log_dir);
|
||||||
|
__update_environ("EVENT_URI", event->uri);
|
||||||
|
__update_environ("EVENT_TRIGGER", event->trigger); /* new name */
|
||||||
|
__update_environ("SOURCE_ACTION", event->trigger); /* old name */
|
||||||
|
__update_environ("CLIENT_IP", event->connection_ip);
|
||||||
|
__update_environ("CLIENT_ROLE", event->client_role);
|
||||||
|
__update_environ("CLIENT_USERNAME", event->client_username);
|
||||||
|
__update_environ("CLIENT_USERAGENT", event->client_useragent);
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "%lu", event->connection_id);
|
||||||
|
__update_environ("CLIENT_ID", buf);
|
||||||
|
snprintf(buf, sizeof(buf), "%lli", (long long int)event->connection_time);
|
||||||
|
__update_environ("CLIENT_CONNECTION_TIME", buf);
|
||||||
|
snprintf(buf, sizeof(buf), "%i", event->client_admin_command);
|
||||||
|
__update_environ("CLIENT_ADMIN_COMMAND", buf);
|
||||||
|
|
||||||
|
mountinfo = config_find_mount(config, event->uri, MOUNT_TYPE_NORMAL);
|
||||||
|
if (mountinfo) {
|
||||||
|
__update_environ("MOUNT_NAME", mountinfo->stream_name);
|
||||||
|
__update_environ("MOUNT_DESCRIPTION", mountinfo->stream_description);
|
||||||
|
__update_environ("MOUNT_URL", mountinfo->stream_url);
|
||||||
|
__update_environ("MOUNT_GENRE", mountinfo->stream_genre);
|
||||||
|
}
|
||||||
|
|
||||||
|
avl_tree_rlock(global.source_tree);
|
||||||
|
source = source_find_mount(event->uri);
|
||||||
|
if (source) {
|
||||||
|
__update_environ("SOURCE_MOUNTPOINT", source->mount);
|
||||||
|
__update_environ("SOURCE_PUBLIC", source->yp_public ? "true" : "false");
|
||||||
|
__update_environ("SROUCE_HIDDEN", source->hidden ? "true" : "false");
|
||||||
|
}
|
||||||
|
avl_tree_unlock(global.source_tree);
|
||||||
|
|
||||||
|
config_release_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _run_script (event_exec_t *self, event_t *event) {
|
||||||
|
pid_t pid, external_pid;
|
||||||
|
|
||||||
|
/* do a fork twice so that the command has init as parent */
|
||||||
|
external_pid = fork();
|
||||||
|
switch (external_pid)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
switch (pid = fork ())
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
ICECAST_LOG_ERROR("Unable to fork %s (%s)", self->executable, strerror (errno));
|
||||||
|
break;
|
||||||
|
case 0: /* child */
|
||||||
|
if (access(self->executable, R_OK|X_OK) != 0) {
|
||||||
|
ICECAST_LOG_ERROR("Unable to run command %s (%s)", self->executable, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
ICECAST_LOG_DEBUG("Starting command %s", self->executable);
|
||||||
|
__setup_empty_script_environment(self, event);
|
||||||
|
/* consider to add action here as well */
|
||||||
|
if (event->uri) {
|
||||||
|
execl(self->executable, self->executable, event->uri, (char *)NULL);
|
||||||
|
} else {
|
||||||
|
execl(self->executable, self->executable, (char *)NULL);
|
||||||
|
}
|
||||||
|
exit(1);
|
||||||
|
default: /* parent */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
exit (0);
|
||||||
|
case -1:
|
||||||
|
ICECAST_LOG_ERROR("Unable to fork %s", strerror (errno));
|
||||||
|
break;
|
||||||
|
default: /* parent */
|
||||||
|
waitpid (external_pid, NULL, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int event_exec_emit(void *state, event_t *event) {
|
||||||
|
event_exec_t *self = state;
|
||||||
|
#ifdef _WIN32
|
||||||
|
ICECAST_LOG_ERROR("<event type=\"exec\" ...> not supported on win*");
|
||||||
|
#else
|
||||||
|
_run_script(self, event);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_exec_free(void *state) {
|
||||||
|
event_exec_t *self = state;
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_get_exec(event_registration_t *er, config_options_t *options) {
|
||||||
|
event_exec_t *self = calloc(1, sizeof(event_exec_t));
|
||||||
|
|
||||||
|
if (!self)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
do {
|
||||||
|
if (options->type)
|
||||||
|
continue;
|
||||||
|
if (!options->name)
|
||||||
|
continue;
|
||||||
|
/* BEFORE RELEASE 2.4.2 DOCUMENT: Document supported options:
|
||||||
|
* <option name="executable" value="..." />
|
||||||
|
*/
|
||||||
|
if (strcmp(options->name, "executable") == 0) {
|
||||||
|
free(self->executable);
|
||||||
|
self->executable = NULL;
|
||||||
|
if (options->value)
|
||||||
|
self->executable = strdup(options->value);
|
||||||
|
} else {
|
||||||
|
ICECAST_LOG_ERROR("Unknown <option> tag with name %s.", options->name);
|
||||||
|
}
|
||||||
|
} while ((options = options->next));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->executable) {
|
||||||
|
ICECAST_LOG_ERROR("No executable given.");
|
||||||
|
event_exec_free(self);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
er->state = self;
|
||||||
|
er->emit = event_exec_emit;
|
||||||
|
er->free = event_exec_free;
|
||||||
|
return 0;
|
||||||
|
}
|
14
src/event_exec.h
Normal file
14
src/event_exec.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EVENT_EXEC_H__
|
||||||
|
#define __EVENT_EXEC_H__
|
||||||
|
|
||||||
|
int event_get_exec(event_registration_t *er, config_options_t *options);
|
||||||
|
|
||||||
|
#endif
|
81
src/event_log.c
Normal file
81
src/event_log.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "event.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#define CATMODULE "event_log"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct event_log {
|
||||||
|
char *prefix;
|
||||||
|
int level;
|
||||||
|
} event_log_t;
|
||||||
|
|
||||||
|
static int event_log_emit(void *state, event_t *event) {
|
||||||
|
event_log_t *self = state;
|
||||||
|
|
||||||
|
ICECAST_LOG(self->level, "%s%strigger=\"%s\" uri=\"%s\" "
|
||||||
|
"connection_id=%lu connection_ip=\"%s\" connection_time=%lli "
|
||||||
|
"client_role=\"%s\" client_username=\"%s\" client_useragent=\"%s\" client_admin_command=%i",
|
||||||
|
self->prefix ? self->prefix : "", self->prefix ? ": " : "",
|
||||||
|
event->trigger,
|
||||||
|
event->uri,
|
||||||
|
event->connection_id, event->connection_ip, (long long int)event->connection_time,
|
||||||
|
event->client_role, event->client_username, event->client_useragent, event->client_admin_command);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_log_free(void *state) {
|
||||||
|
event_log_t *self = state;
|
||||||
|
free(self->prefix);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_get_log(event_registration_t *er, config_options_t *options) {
|
||||||
|
event_log_t *self = calloc(1, sizeof(event_log_t));
|
||||||
|
|
||||||
|
if (!self)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
self->prefix = strdup("Event");
|
||||||
|
self->level = ICECAST_LOGLEVEL_INFO;
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
do {
|
||||||
|
if (options->type)
|
||||||
|
continue;
|
||||||
|
if (!options->name)
|
||||||
|
continue;
|
||||||
|
/* BEFORE RELEASE 2.4.2 DOCUMENT: Document supported options:
|
||||||
|
* <option name="prefix" value="..." />
|
||||||
|
* <option name="level" value="..." />
|
||||||
|
*/
|
||||||
|
if (strcmp(options->name, "prefix") == 0) {
|
||||||
|
free(self->prefix);
|
||||||
|
self->prefix = NULL;
|
||||||
|
if (options->value)
|
||||||
|
self->prefix = strdup(options->value);
|
||||||
|
} else if (strcmp(options->name, "level") == 0) {
|
||||||
|
self->level = ICECAST_LOGLEVEL_INFO;
|
||||||
|
if (options->value)
|
||||||
|
self->level = util_str_to_loglevel(options->value);
|
||||||
|
} else {
|
||||||
|
ICECAST_LOG_ERROR("Unknown <option> tag with name %s.", options->name);
|
||||||
|
}
|
||||||
|
} while ((options = options->next));
|
||||||
|
}
|
||||||
|
|
||||||
|
er->state = self;
|
||||||
|
er->emit = event_log_emit;
|
||||||
|
er->free = event_log_free;
|
||||||
|
return 0;
|
||||||
|
}
|
14
src/event_log.h
Normal file
14
src/event_log.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EVENT_LOG_H__
|
||||||
|
#define __EVENT_LOG_H__
|
||||||
|
|
||||||
|
int event_get_log(event_registration_t *er, config_options_t *options);
|
||||||
|
|
||||||
|
#endif
|
192
src/event_url.c
Normal file
192
src/event_url.c
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "event.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#define CATMODULE "event_url"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct event_url {
|
||||||
|
char *url;
|
||||||
|
char *action;
|
||||||
|
char *userpwd;
|
||||||
|
CURL *handle;
|
||||||
|
char errormsg[CURL_ERROR_SIZE];
|
||||||
|
} event_url_t;
|
||||||
|
|
||||||
|
#ifdef CURLOPT_PASSWDFUNCTION
|
||||||
|
/* make sure that prompting at the console does not occur */
|
||||||
|
static int my_getpass(void *client, char *prompt, char *buffer, int buflen) {
|
||||||
|
buffer[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
static size_t handle_returned (void *ptr, size_t size, size_t nmemb, void *stream) {
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *__escape(const char *src, const char *default_value) {
|
||||||
|
if (src)
|
||||||
|
return util_url_escape(src);
|
||||||
|
|
||||||
|
return strdup(default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int event_url_emit(void *state, event_t *event) {
|
||||||
|
event_url_t *self = state;
|
||||||
|
ice_config_t *config;
|
||||||
|
char *url;
|
||||||
|
char *action, *mount, *server, *role, *username, *ip, *agent;
|
||||||
|
time_t duration;
|
||||||
|
char post[4096];
|
||||||
|
|
||||||
|
action = util_url_escape(self->action ? self->action : event->trigger);
|
||||||
|
mount = __escape(event->uri, "");
|
||||||
|
role = __escape(event->client_role, "");
|
||||||
|
username = __escape(event->client_username, "");
|
||||||
|
ip = __escape(event->connection_ip, "");
|
||||||
|
agent = __escape(event->client_useragent, "-");
|
||||||
|
|
||||||
|
if (event->connection_time) {
|
||||||
|
duration = time(NULL) - event->connection_time;
|
||||||
|
} else {
|
||||||
|
duration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
config = config_get_config();
|
||||||
|
server = __escape(config->hostname, "");
|
||||||
|
|
||||||
|
snprintf (post, sizeof (post),
|
||||||
|
"action=%s&mount=%s&server=%s&port=%d&client=%lu&role=%s&username=%s&ip=%s&agent=%s&duration=%lli&admin=%i",
|
||||||
|
action, mount, server, config->port,
|
||||||
|
event->connection_id, role, username, ip, agent, (long long int)duration, event->client_admin_command);
|
||||||
|
config_release_config();
|
||||||
|
|
||||||
|
free(action);
|
||||||
|
free(mount);
|
||||||
|
free(server);
|
||||||
|
free(role);
|
||||||
|
free(username);
|
||||||
|
free(ip);
|
||||||
|
free(agent);
|
||||||
|
|
||||||
|
if (strchr(self->url, '@') == NULL && self->userpwd) {
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_USERPWD, self->userpwd);
|
||||||
|
} else {
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_USERPWD, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REVIEW: Why do we dup the url here?
|
||||||
|
* auth_url.c does it this way. I just copied the code.
|
||||||
|
* Maybe there is a reason for it, maybe not?
|
||||||
|
* -- Philipp Schafft, 2014-12-07.
|
||||||
|
*/
|
||||||
|
url = strdup(self->url);
|
||||||
|
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_URL, url);
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_POSTFIELDS, post);
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_WRITEHEADER, NULL);
|
||||||
|
|
||||||
|
if (curl_easy_perform(self->handle))
|
||||||
|
ICECAST_LOG_WARN("auth to server %s failed with %s", url, self->errormsg);
|
||||||
|
|
||||||
|
free(url);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event_url_free(void *state) {
|
||||||
|
event_url_t *self = state;
|
||||||
|
if (self->handle)
|
||||||
|
curl_easy_cleanup(self->handle);
|
||||||
|
free(self->url);
|
||||||
|
free(self->action);
|
||||||
|
free(self->userpwd);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
int event_get_url(event_registration_t *er, config_options_t *options) {
|
||||||
|
event_url_t *self = calloc(1, sizeof(event_url_t));
|
||||||
|
const char *username = NULL;
|
||||||
|
const char *password = NULL;
|
||||||
|
|
||||||
|
if (!self)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
do {
|
||||||
|
if (options->type)
|
||||||
|
continue;
|
||||||
|
if (!options->name)
|
||||||
|
continue;
|
||||||
|
/* BEFORE RELEASE 2.4.2 DOCUMENT: Document supported options:
|
||||||
|
* <option name="url" value="..." />
|
||||||
|
* <option name="username" value="..." />
|
||||||
|
* <option name="password" value="..." />
|
||||||
|
* <option name="action" value="..." />
|
||||||
|
*/
|
||||||
|
if (strcmp(options->name, "url") == 0) {
|
||||||
|
free(self->url);
|
||||||
|
self->url = NULL;
|
||||||
|
if (options->value)
|
||||||
|
self->url = strdup(options->value);
|
||||||
|
} else if (strcmp(options->name, "username") == 0) {
|
||||||
|
username = options->value;
|
||||||
|
} else if (strcmp(options->name, "password") == 0) {
|
||||||
|
password = options->value;
|
||||||
|
} else if (strcmp(options->name, "action") == 0) {
|
||||||
|
free(self->url);
|
||||||
|
self->action = NULL;
|
||||||
|
if (options->value)
|
||||||
|
self->action = strdup(options->value);
|
||||||
|
} else {
|
||||||
|
ICECAST_LOG_ERROR("Unknown <option> tag with name %s.", options->name);
|
||||||
|
}
|
||||||
|
} while ((options = options->next));
|
||||||
|
}
|
||||||
|
|
||||||
|
self->handle = curl_easy_init();
|
||||||
|
|
||||||
|
/* check if we are in sane state */
|
||||||
|
if (!self->url || !self->handle) {
|
||||||
|
event_url_free(self);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_USERAGENT, ICECAST_VERSION_STRING);
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_HEADERFUNCTION, handle_returned);
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_WRITEFUNCTION, handle_returned);
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_WRITEDATA, self->handle);
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_NOSIGNAL, 1L);
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_TIMEOUT, 15L);
|
||||||
|
#ifdef CURLOPT_PASSWDFUNCTION
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_PASSWDFUNCTION, my_getpass);
|
||||||
|
#endif
|
||||||
|
curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->errormsg);
|
||||||
|
|
||||||
|
if (username && password) {
|
||||||
|
size_t len = strlen(username) + strlen(password) + 2;
|
||||||
|
self->userpwd = malloc(len);
|
||||||
|
if (!self->userpwd) {
|
||||||
|
event_url_free(self);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
snprintf(self->userpwd, len, "%s:%s", username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
er->state = self;
|
||||||
|
er->emit = event_url_emit;
|
||||||
|
er->free = event_url_free;
|
||||||
|
return 0;
|
||||||
|
}
|
14
src/event_url.h
Normal file
14
src/event_url.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* Icecast
|
||||||
|
*
|
||||||
|
* This program is distributed under the GNU General Public License, version 2.
|
||||||
|
* A copy of this license is included with this source.
|
||||||
|
*
|
||||||
|
* Copyright 2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EVENT_URL_H__
|
||||||
|
#define __EVENT_URL_H__
|
||||||
|
|
||||||
|
int event_get_url(event_registration_t *er, config_options_t *options);
|
||||||
|
|
||||||
|
#endif
|
@ -29,14 +29,21 @@ extern int playlistlog;
|
|||||||
#define __func__ strrchr (__FILE__, '\\') ? strrchr (__FILE__, '\\') + 1 : __FILE__
|
#define __func__ strrchr (__FILE__, '\\') ? strrchr (__FILE__, '\\') + 1 : __FILE__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Log levels */
|
||||||
|
#define ICECAST_LOGLEVEL_ERROR 1
|
||||||
|
#define ICECAST_LOGLEVEL_WARN 2
|
||||||
|
#define ICECAST_LOGLEVEL_INFO 3
|
||||||
|
#define ICECAST_LOGLEVEL_DEBUG 4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Variadic macros for logging
|
** Variadic macros for logging
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define ICECAST_LOG_ERROR(...) log_write(errorlog, 1, CATMODULE "/", __func__, __VA_ARGS__)
|
#define ICECAST_LOG(level,...) log_write(errorlog, (level), CATMODULE "/", __func__, __VA_ARGS__)
|
||||||
#define ICECAST_LOG_WARN(...) log_write(errorlog, 2, CATMODULE "/", __func__, __VA_ARGS__)
|
#define ICECAST_LOG_ERROR(...) ICECAST_LOG(ICECAST_LOGLEVEL_ERROR, __VA_ARGS__)
|
||||||
#define ICECAST_LOG_INFO(...) log_write(errorlog, 3, CATMODULE "/", __func__, __VA_ARGS__)
|
#define ICECAST_LOG_WARN(...) ICECAST_LOG(ICECAST_LOGLEVEL_WARN, __VA_ARGS__)
|
||||||
#define ICECAST_LOG_DEBUG(...) log_write(errorlog, 4, CATMODULE "/", __func__, __VA_ARGS__)
|
#define ICECAST_LOG_INFO(...) ICECAST_LOG(ICECAST_LOGLEVEL_INFO, __VA_ARGS__)
|
||||||
|
#define ICECAST_LOG_DEBUG(...) ICECAST_LOG(ICECAST_LOGLEVEL_DEBUG,__VA_ARGS__)
|
||||||
|
|
||||||
/* CATMODULE is the category or module that logging messages come from.
|
/* CATMODULE is the category or module that logging messages come from.
|
||||||
** we set one here in cause someone forgets in the .c file.
|
** we set one here in cause someone forgets in the .c file.
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
#include "fserve.h"
|
#include "fserve.h"
|
||||||
#include "yp.h"
|
#include "yp.h"
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
#include "event.h"
|
||||||
|
|
||||||
#include <libxml/xmlmemory.h>
|
#include <libxml/xmlmemory.h>
|
||||||
|
|
||||||
@ -131,6 +132,7 @@ void initialize_subsystems(void)
|
|||||||
|
|
||||||
void shutdown_subsystems(void)
|
void shutdown_subsystems(void)
|
||||||
{
|
{
|
||||||
|
event_shutdown();
|
||||||
fserve_shutdown();
|
fserve_shutdown();
|
||||||
refbuf_shutdown();
|
refbuf_shutdown();
|
||||||
slave_shutdown();
|
slave_shutdown();
|
||||||
@ -210,7 +212,7 @@ static int _start_logging_stdout(void) {
|
|||||||
if ( errorlog < 0 )
|
if ( errorlog < 0 )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
log_set_level(errorlog, 2 /* WARN */);
|
log_set_level(errorlog, ICECAST_LOGLEVEL_WARN);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -588,8 +590,11 @@ int main(int argc, char **argv)
|
|||||||
/* Do this after logging init */
|
/* Do this after logging init */
|
||||||
slave_initialize();
|
slave_initialize();
|
||||||
auth_initialise ();
|
auth_initialise ();
|
||||||
|
event_initialise();
|
||||||
|
|
||||||
|
event_emit_global("icecast-start");
|
||||||
_server_proc();
|
_server_proc();
|
||||||
|
event_emit_global("icecast-stop");
|
||||||
|
|
||||||
ICECAST_LOG_INFO("Shutting down");
|
ICECAST_LOG_INFO("Shutting down");
|
||||||
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
|
#if !defined(_WIN32) || defined(_CONSOLE) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||||
|
132
src/source.c
132
src/source.c
@ -23,6 +23,7 @@
|
|||||||
#include <ogg/ogg.h>
|
#include <ogg/ogg.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* REVIEW: Are all those includes needed? */
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@ -38,12 +39,6 @@
|
|||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
/* for __setup_empty_script_environment() */
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "common/thread/thread.h"
|
#include "common/thread/thread.h"
|
||||||
#include "common/avl/avl.h"
|
#include "common/avl/avl.h"
|
||||||
#include "common/httpp/httpp.h"
|
#include "common/httpp/httpp.h"
|
||||||
@ -61,6 +56,7 @@
|
|||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "fserve.h"
|
#include "fserve.h"
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
#include "event.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
|
|
||||||
#undef CATMODULE
|
#undef CATMODULE
|
||||||
@ -75,11 +71,6 @@ static int _compare_clients(void *compare_arg, void *a, void *b);
|
|||||||
static int _free_client(void *key);
|
static int _free_client(void *key);
|
||||||
static void _parse_audio_info (source_t *source, const char *s);
|
static void _parse_audio_info (source_t *source, const char *s);
|
||||||
static void source_shutdown (source_t *source);
|
static void source_shutdown (source_t *source);
|
||||||
#ifdef _WIN32
|
|
||||||
#define source_run_script(w,x,y,z) ICECAST_LOG_WARN("on [dis]connect scripts disabled");
|
|
||||||
#else
|
|
||||||
static void source_run_script (const char *command, source_t *source, mount_proxy *mountinfo, const char *action);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Allocate a new source with the stated mountpoint, if one already
|
/* Allocate a new source with the stated mountpoint, if one already
|
||||||
* exists with that mountpoint in the global source tree then return
|
* exists with that mountpoint in the global source tree then return
|
||||||
@ -622,7 +613,6 @@ static void source_init (source_t *source)
|
|||||||
char *listenurl;
|
char *listenurl;
|
||||||
const char *str;
|
const char *str;
|
||||||
int listen_url_size;
|
int listen_url_size;
|
||||||
mount_proxy *mountinfo;
|
|
||||||
|
|
||||||
/* 6 for max size of port */
|
/* 6 for max size of port */
|
||||||
listen_url_size = strlen("http://") + strlen(config->hostname) +
|
listen_url_size = strlen("http://") + strlen(config->hostname) +
|
||||||
@ -673,15 +663,7 @@ static void source_init (source_t *source)
|
|||||||
source->prev_listeners = -1;
|
source->prev_listeners = -1;
|
||||||
source->running = 1;
|
source->running = 1;
|
||||||
|
|
||||||
mountinfo = config_find_mount (config_get_config(), source->mount, MOUNT_TYPE_NORMAL);
|
event_emit_clientevent("source-connect", source->client, source->mount);
|
||||||
if (mountinfo)
|
|
||||||
{
|
|
||||||
if (mountinfo->on_connect)
|
|
||||||
source_run_script (mountinfo->on_connect, source, mountinfo, "source-connect");
|
|
||||||
/* TODO: replace with <event> */
|
|
||||||
/* auth_stream_start (mountinfo, source->mount); */
|
|
||||||
}
|
|
||||||
config_release_config();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Now, if we have a fallback source and override is on, we want
|
** Now, if we have a fallback source and override is on, we want
|
||||||
@ -877,20 +859,10 @@ void source_main (source_t *source)
|
|||||||
|
|
||||||
static void source_shutdown (source_t *source)
|
static void source_shutdown (source_t *source)
|
||||||
{
|
{
|
||||||
mount_proxy *mountinfo;
|
|
||||||
|
|
||||||
source->running = 0;
|
source->running = 0;
|
||||||
ICECAST_LOG_INFO("Source from %s at \"%s\" exiting", source->con->ip, source->mount);
|
ICECAST_LOG_INFO("Source from %s at \"%s\" exiting", source->con->ip, source->mount);
|
||||||
|
|
||||||
mountinfo = config_find_mount (config_get_config(), source->mount, MOUNT_TYPE_NORMAL);
|
event_emit_clientevent("source-disconnect", source->client, source->mount);
|
||||||
if (mountinfo)
|
|
||||||
{
|
|
||||||
if (mountinfo->on_disconnect)
|
|
||||||
source_run_script (mountinfo->on_disconnect, source, mountinfo, "source-disconnect");
|
|
||||||
/* TODO: replace with <event> */
|
|
||||||
/* auth_stream_end (mountinfo, source->mount); */
|
|
||||||
}
|
|
||||||
config_release_config();
|
|
||||||
|
|
||||||
/* we have de-activated the source now, so no more clients will be
|
/* 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)
|
* added, now move the listeners we have to the fallback (if any)
|
||||||
@ -1245,10 +1217,6 @@ void source_update_settings (ice_config_t *config, source_t *source, mount_proxy
|
|||||||
ICECAST_LOG_DEBUG("intro file is %s", mountinfo->intro_filename);
|
ICECAST_LOG_DEBUG("intro file is %s", mountinfo->intro_filename);
|
||||||
if (source->dumpfilename)
|
if (source->dumpfilename)
|
||||||
ICECAST_LOG_DEBUG("Dumping stream to %s", source->dumpfilename);
|
ICECAST_LOG_DEBUG("Dumping stream to %s", source->dumpfilename);
|
||||||
if (mountinfo && mountinfo->on_connect)
|
|
||||||
ICECAST_LOG_DEBUG("connect script \"%s\"", mountinfo->on_connect);
|
|
||||||
if (mountinfo && mountinfo->on_disconnect)
|
|
||||||
ICECAST_LOG_DEBUG("disconnect script \"%s\"", mountinfo->on_disconnect);
|
|
||||||
if (source->on_demand)
|
if (source->on_demand)
|
||||||
{
|
{
|
||||||
ICECAST_LOG_DEBUG("on_demand set");
|
ICECAST_LOG_DEBUG("on_demand set");
|
||||||
@ -1327,98 +1295,6 @@ void source_client_callback (client_t *client, void *arg)
|
|||||||
source, THREAD_DETACHED);
|
source, THREAD_DETACHED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
/* this sets up the new environment for script execution.
|
|
||||||
* We ignore most failtures as we can not handle them anyway.
|
|
||||||
*/
|
|
||||||
#ifdef HAVE_SETENV
|
|
||||||
static inline void __update_environ(const char *name, const char *value) {
|
|
||||||
if (!name || !value) return;
|
|
||||||
setenv(name, value, 1);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define __update_environ(x,y)
|
|
||||||
#endif
|
|
||||||
static inline void __setup_empty_script_environment(ice_config_t * config, source_t *source, mount_proxy *mountinfo, const char *action) {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* close at least the first 1024 handles */
|
|
||||||
for (i = 0; i < 1024; i++)
|
|
||||||
close(i);
|
|
||||||
|
|
||||||
/* open null device */
|
|
||||||
i = open(config->null_device, O_RDWR);
|
|
||||||
if (i != -1) {
|
|
||||||
/* attach null device to stdin, stdout and stderr */
|
|
||||||
if (i != 0)
|
|
||||||
dup2(i, 0);
|
|
||||||
if (i != 1)
|
|
||||||
dup2(i, 1);
|
|
||||||
if (i != 2)
|
|
||||||
dup2(i, 2);
|
|
||||||
|
|
||||||
/* close null device */
|
|
||||||
if (i > 2)
|
|
||||||
close(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
__update_environ("ICECAST_VERSION", ICECAST_VERSION_STRING);
|
|
||||||
__update_environ("ICECAST_HOSTNAME", config->hostname);
|
|
||||||
__update_environ("ICECAST_ADMIN", config->admin);
|
|
||||||
__update_environ("ICECAST_LOGDIR", config->log_dir);
|
|
||||||
__update_environ("SOURCE_ACTION", action);
|
|
||||||
__update_environ("SOURCE_MOUNTPOINT", source->mount);
|
|
||||||
__update_environ("SOURCE_PUBLIC", source->yp_public ? "true" : "false");
|
|
||||||
__update_environ("SROUCE_HIDDEN", source->hidden ? "true" : "false");
|
|
||||||
__update_environ("MOUNT_NAME", mountinfo->stream_name);
|
|
||||||
__update_environ("MOUNT_DESCRIPTION", mountinfo->stream_description);
|
|
||||||
__update_environ("MOUNT_URL", mountinfo->stream_url);
|
|
||||||
__update_environ("MOUNT_GENRE", mountinfo->stream_genre);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void source_run_script (const char *command, source_t *source, mount_proxy *mountinfo, const char *action) {
|
|
||||||
const char *mountpoint = source->mount;
|
|
||||||
pid_t pid, external_pid;
|
|
||||||
ice_config_t * config;
|
|
||||||
|
|
||||||
/* do a fork twice so that the command has init as parent */
|
|
||||||
external_pid = fork();
|
|
||||||
switch (external_pid)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
switch (pid = fork ())
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
ICECAST_LOG_ERROR("Unable to fork %s (%s)", command, strerror (errno));
|
|
||||||
break;
|
|
||||||
case 0: /* child */
|
|
||||||
if (access(command, R_OK|X_OK) != 0) {
|
|
||||||
ICECAST_LOG_ERROR("Unable to run command %s (%s)", command, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
ICECAST_LOG_DEBUG("Starting command %s", command);
|
|
||||||
config = config_get_config();
|
|
||||||
__setup_empty_script_environment(config, source, mountinfo, action);
|
|
||||||
config_release_config();
|
|
||||||
/* consider to add action here as well */
|
|
||||||
execl(command, command, mountpoint, (char *)NULL);
|
|
||||||
exit(1);
|
|
||||||
default: /* parent */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
exit (0);
|
|
||||||
case -1:
|
|
||||||
ICECAST_LOG_ERROR("Unable to fork %s", strerror (errno));
|
|
||||||
break;
|
|
||||||
default: /* parent */
|
|
||||||
waitpid (external_pid, NULL, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static void *source_fallback_file (void *arg)
|
static void *source_fallback_file (void *arg)
|
||||||
{
|
{
|
||||||
char *mount = arg;
|
char *mount = arg;
|
||||||
|
14
src/util.c
14
src/util.c
@ -546,6 +546,20 @@ int util_str_to_bool(const char *str) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int util_str_to_loglevel(const char *str) {
|
||||||
|
if (strcasecmp(str, "debug") == 0 || strcasecmp(str, "DBUG") == 0)
|
||||||
|
return ICECAST_LOGLEVEL_DEBUG;
|
||||||
|
if (strcasecmp(str, "information") == 0 || strcasecmp(str, "INFO") == 0)
|
||||||
|
return ICECAST_LOGLEVEL_INFO;
|
||||||
|
if (strcasecmp(str, "warning") == 0 || strcasecmp(str, "WARN") == 0)
|
||||||
|
return ICECAST_LOGLEVEL_WARN;
|
||||||
|
if (strcasecmp(str, "error") == 0 || strcasecmp(str, "EROR") == 0)
|
||||||
|
return ICECAST_LOGLEVEL_ERROR;
|
||||||
|
|
||||||
|
/* gussing it is old-style numerical setting */
|
||||||
|
return atoi(str);
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO, FIXME: handle memory allocation errors better. */
|
/* TODO, FIXME: handle memory allocation errors better. */
|
||||||
static inline void _build_headers_loop(char **ret, size_t *len, ice_config_http_header_t *header, int status) {
|
static inline void _build_headers_loop(char **ret, size_t *len, ice_config_http_header_t *header, int status) {
|
||||||
size_t headerlen;
|
size_t headerlen;
|
||||||
|
@ -49,6 +49,7 @@ typedef enum _util_hostcheck_tag {
|
|||||||
util_hostcheck_type util_hostcheck(const char *hostname);
|
util_hostcheck_type util_hostcheck(const char *hostname);
|
||||||
|
|
||||||
int util_str_to_bool(const char *str);
|
int util_str_to_bool(const char *str);
|
||||||
|
int util_str_to_loglevel(const char *str);
|
||||||
|
|
||||||
char *util_url_unescape(const char *src);
|
char *util_url_unescape(const char *src);
|
||||||
char *util_url_escape(const char *src);
|
char *util_url_escape(const char *src);
|
||||||
|
Loading…
Reference in New Issue
Block a user