1
0
mirror of https://gitlab.xiph.org/xiph/ezstream.git synced 2024-06-09 06:10:42 +00:00

Change config format to (eventually) support concurrent streams to many servers

Everything ties together in stream configurations, of which there can be many
now.

For the time being, every stream configuration but the "default" is ignored.

Every stream/server/encoder/decoder configuration now has the name "default"
by default, so that configuring names is only needed to resolve ambiguities.

Decoder configurations are now also constrained in this manner for consistency,
meaning they are no longer uniquely identified by a number of file extensions
but also a name.
This commit is contained in:
Moritz Grimm 2017-11-18 01:54:14 +01:00
parent f82cf2b458
commit ed9e84f387
33 changed files with 3245 additions and 1853 deletions

View File

@ -93,8 +93,7 @@ Increase logging verbosity.
May be used up to three times to also include debug logging output.
.El
.Ss Runtime control
On POSIX systems,
.Nm
.Nm Ezstream
offers limited runtime control via signals.
By sending a signal to the ezstream process, e.g. with the
.Xr kill 1
@ -148,20 +147,40 @@ like in the introductory example shown above.
.It Sy \&<ezstream\ /\&>
.Pq Mandatory.
The configuration file's root element.
It contains all other configuration elements:
It contains all other configuration elements.
.Pp
.El
.Ss Servers block
.Bl -tag -width -Ds
.It Sy \&<servers\ /\&>
This element contains all server blocks as child elements.
Its parent is the
.Sy \&<ezstream\ /\&>
element.
.Pp
A configuration file may contain multiple named server configurations.
The stream configuration determines what server configuration should be used.
.El
.Ss Server block
.Bl -tag -width -Ds
.It Sy \&<server\ /\&>
.Pq Mandatory.
This element contains the entire server configuration as child elements.
This element contains a complete server configuration as child elements.
Its parent is the
.Sy \&<ezstream\ /\&>
.Sy \&<servers\ /\&>
element.
.El
.Ss Server configuration
.Bl -tag -width -Ds
.It Sy \&<name\ /\&>
Set the name of the server configuration.
This may be referenced in a
.Sy \&<stream\ /\&> .
.Pp
The name is case-aware, but not case-sensitive.
.Pp
Default:
.Ar default
.It Sy \&<protocol\ /\&>
Transport protocol used to stream to the server:
.Pp
@ -187,6 +206,14 @@ Default:
.It Sy \&<password\ /\&>
.Pq Mandatory.
Password to authenticate with on the server.
.It Sy \&<reconnect_attempts\ /\&>
Number of reconnect attempts in case of connection issues with the server,
or 0
.Pq zero
for trying indefinitely.
.Pp
Default:
.Ar 0
.It Sy \&<tls\ /\&>
Configure the TLS encryption requirement for the server connection.
Possible values are:
@ -233,11 +260,21 @@ server.
.Pp
Default:
.Em no client certificate authentication
.It Sy \&<reconnect_attempts\ /\&>
Number of reconnect attempts in case of connection issues with the server,
or 0
.Pq zero
for trying indefinitely (the default).
.El
.Ss Streams block
.Bl -tag -width -Ds
.It Sy \&<streams\ /\&>
This element contains all stream blocks as child elements.
Its parent is the
.Sy \&<ezstream\ /\&>
element.
.Pp
.Em Note:
While multiple stream configurations are supported by the file format, only
the one configuration with the name
.Ar default
will be used by
.Nm .
.El
.Ss Stream block
.Bl -tag -width -Ds
@ -245,55 +282,22 @@ for trying indefinitely (the default).
.Pq Mandatory.
This element contains the entire stream configuration as child elements.
Its parent is the
.Sy \&<ezstream\ /\&>
.Sy \&<streams\ /\&>
element.
.El
.Ss Stream configuration
.Bl -tag -width -Ds
.It Sy \&<name\ /\&>
Set the name of the stream configuration.
.Pp
The name is case-aware, but not case-sensitive.
.Pp
Default:
.Ar default
.It Sy \&<mountpoint\ /\&>
.Pq Mandatory.
Stream mountpoint on the server.
.It Sy \&<name\ /\&>
Informational name of the broadcast.
.Pp
Default:
.Em none
.It Sy \&<url\ /\&>
Informational URL associated with the broadcast, e.g. the web site.
.Pp
Default:
.Em none
.It Sy \&<genre\ /\&>
Informational genre of the broadcast.
.Pp
Default:
.Em none
.It Sy \&<description\ /\&>
Informational description of the broadcast.
.Pp
Default:
.Em none
.It Sy \&<quality\ /\&>
Informational quality setting of the VBR broadcast.
.Pp
Default:
.Em none
.It Sy \&<bitrate\ /\&>
Informational bitrate setting of the CBR broadcast.
.Pp
Default:
.Em none
.It Sy \&<samplerate\ /\&>
Informational sample rate of the broadcast audio.
.Pp
Default:
.Em none
.It Sy \&<channels\ /\&>
Informational number of audio channels of the broadcast.
.Pp
Default:
.Em none
.It Sy \&<server_public\ /\&>
.It Sy \&<public\ /\&>
Boolean setting of whether the broadcast may be listed in a public YP
directory, or not.
.Pp
@ -303,6 +307,11 @@ The broadcast is private (the default).
.It Ar 1|Yes|True
The broadcast is public.
.El
.It Sy \&<server\ /\&>
Use the server configuration with the provided symbolic name for this stream.
.Pp
Default:
.Ar default
.It Sy \&<format\ /\&>
.Pq Mandatory.
The stream format.
@ -316,9 +325,9 @@ MP3 audio format
Ogg Theora video format
.El
.It Sy \&<encoder\ /\&>
Set the encoder by its configured symbolic name
Use the encoder configuration with the provided symbolic name
.Pq see below ,
which should be used to (re)encode the stream.
for (re)encoding the stream.
Not configuring an encoder makes
.Nm
stream input media files as-is.
@ -326,6 +335,46 @@ stream input media files as-is.
The configured encoder's output stream format must match what is configured
in
.Sy \&<format\ /\&> .
.It Sy \&<stream_name\ /\&>
Informational name of the broadcast.
.Pp
Default:
.Em none
.It Sy \&<stream_url\ /\&>
Informational URL associated with the broadcast, e.g. the web site.
.Pp
Default:
.Em none
.It Sy \&<stream_genre\ /\&>
Informational genre of the broadcast.
.Pp
Default:
.Em none
.It Sy \&<stream_description\ /\&>
Informational description of the broadcast.
.Pp
Default:
.Em none
.It Sy \&<stream_quality\ /\&>
Informational quality setting of the VBR broadcast.
.Pp
Default:
.Em none
.It Sy \&<stream_bitrate\ /\&>
Informational bitrate setting of the CBR broadcast.
.Pp
Default:
.Em none
.It Sy \&<stream_samplerate\ /\&>
Informational sample rate of the broadcast audio.
.Pp
Default:
.Em none
.It Sy \&<stream_channels\ /\&>
Informational number of audio channels of the broadcast.
.Pp
Default:
.Em none
.El
.Ss Media block
.Bl -tag -width -Ds
@ -477,18 +526,16 @@ This element contains all configuration of a single decoder.
Its parent is the
.Sy \&<decoders\ /\&>
element.
.Pp
Many decoders can be configured, but a filename extension can only be
associated with one decoder.
.El
.Ss Decoder configuration
.Bl -tag -width -Ds
.It Sy \&<name\ /\&>
Set the name of the decoder configuration.
Choosing a meaningful, unique name is beneficial for logging and debugging.
.Pp
The name is case-aware, but not case-sensitive.
.Pp
Default:
.Em autoincrementing index
.Ar default
.It Sy \&<program\ /\&>
.Pq Mandatory.
Set the full command line to decode a media input file, represented by the
@ -513,6 +560,8 @@ It is possible to specify more than one
.Sy \&<file_ext\ /\&>
element per decoder to associate more than one file extension to the same
decoder.
.Pp
A filename extension can only be associated with one decoder.
.El
.Ss Encoders block
.Bl -tag -width -Ds
@ -529,18 +578,20 @@ This element contains all configuration of a single encoder.
Its parent is the
.Sy \&<encoders\ /\&>
element.
.Pp
Many encoders can be configured.
.El
.Ss Encoder configuration
.Bl -tag -width -Ds
.It Sy \&<name\ /\&>
.Pq Mandatory.
Set the name of the encoder configuration, to be referenced in the
Set the name of the encoder configuration.
This may be referenced in a
.Sy \&<stream\ /\&>
block in case (re)encoding is desired.
.Pp
The name is case-aware, but not case-sensitive.
.Pp
Default:
.Ar default
.It Sy \&<format\ /\&>
.Pq Mandatory.
Stream format produced by this encoder.
@ -549,7 +600,7 @@ This must be one of the available stream formats as specified for the
block.
.It Sy \&<program\ /\&>
.Pq Mandatory.
Set the full command line to encode
Set the full command line to encode the
.Dq canonical internal format
from standard input into a supported stream format on standard output.
.Pp

View File

@ -10,17 +10,21 @@
<ezstream>
<server>
<hostname>127.0.0.1</hostname>
<port>8000</port>
<password>hackme</password>
</server>
<servers>
<server>
<hostname>127.0.0.1</hostname>
<port>8000</port>
<password>hackme</password>
</server>
</servers>
<stream>
<mountpoint>/stream.ogg</mountpoint>
<format>Vorbis</format>
<encoder>OggEnc-Q1.5</encoder>
</stream>
<streams>
<stream>
<mountpoint>/stream.ogg</mountpoint>
<format>Vorbis</format>
<encoder>OggEnc-Q1.5</encoder>
</stream>
</streams>
<media>
<filename>%FILENAME%</filename>

View File

@ -13,78 +13,91 @@
<!--
Server configuration
-->
<server>
<!-- Transport protocol: HTTP, HTTPS -->
<protocol>HTTP</protocol>
<!-- Server address -->
<hostname>127.0.0.1</hostname>
<!-- Server port -->
<port>8000</port>
<servers>
<server>
<!-- Identifying name; default: "default" -->
<name>Default</name>
<!-- Login user -->
<user>source</user>
<!-- Login password (check file permissions, or everyone can see this) -->
<password>hackme</password>
<!-- Transport protocol: HTTP, HTTPS -->
<protocol>HTTP</protocol>
<!-- Server address -->
<hostname>127.0.0.1</hostname>
<!-- Server port -->
<port>8000</port>
<!--
Configure TLS encryption requirement: none, may (default), required
-->
<tls>Required</tls>
<!-- Login user -->
<user>source</user>
<!-- Login password (check file permissions, or everyone can see this) -->
<password>hackme</password>
<!--
Configure allowed cipher suites for TLS other than the libshout default.
See openssl(1)/ciphers(1) for details.
-->
<tls_cipher_suite>HIGH:!kRSA:!kECDH:!DH:!PKS:!aNULL:!eNULL:!3DES:!MD5:!SHA:!TLSv1</tls_cipher_suite>
<!-- Number of reconnection attempts, before giving up: -->
<reconnect_attempts>0</reconnect_attempts>
<!--
Directory to use for server certificate verification in "hash format".
See openssl(1)/verify(1) for details.
-->
<ca_dir>/etc/ssl/certs</ca_dir>
<!--
Configure TLS encryption requirement: none, may (default), required
-->
<tls>Required</tls>
<!--
CA bundle for both server certificate verification and building the
client certificate chain.
-->
<ca_file>/etc/ssl/certs/ca-certificates.crt</ca_file>
<!--
Configure allowed cipher suites for TLS other than the libshout default.
See openssl(1)/ciphers(1) for details.
-->
<tls_cipher_suite>HIGH:!kRSA:!kECDH:!DH:!PKS:!aNULL:!eNULL:!3DES:!MD5:!SHA:!TLSv1</tls_cipher_suite>
<!--
X.503 client certificate, in PEM format, containing both certificate
(with public key) and private key in the same file, for authentication.
-->
<client_cert>/etc/ssl/ezstream.crt</client_cert>
<!--
Directory to use for server certificate verification in "hash format".
See openssl(1)/verify(1) for details.
-->
<ca_dir>/etc/ssl/certs</ca_dir>
<!-- Number of reconnection attempts, before giving up: -->
<reconnect_attempts>0</reconnect_attempts>
</server>
<!--
CA bundle for both server certificate verification and building the
client certificate chain.
-->
<ca_file>/etc/ssl/certs/ca-certificates.crt</ca_file>
<!--
X.503 client certificate, in PEM format, containing both certificate
(with public key) and private key in the same file, for authentication.
-->
<client_cert>/etc/ssl/ezstream.crt</client_cert>
</server>
</servers>
<!--
Stream configuration
-->
<stream>
<!-- Mount point on server -->
<mountpoint>/stream.ogg</mountpoint>
<streams>
<stream>
<!-- Identifying name; default: "default" -->
<name>Default</name>
<!-- Various informational settings -->
<name>Test Stream</name>
<url>http://localhost:8000/</url>
<genre>Beeps</genre>
<description>Test tones and noise</description>
<quality>1.5</quality>
<bitrate>16</bitrate>
<samplerate>44100</samplerate>
<channels>2</channels>
<!-- Mount point on server -->
<mountpoint>/stream.ogg</mountpoint>
<!-- Setting to allow stream to be listed in public YP directory -->
<server_public>No</server_public>
<!-- Name of the server entry to use (default: "default") -->
<server>Default</server>
<!-- Stream format: Vorbis, MP3, Theora -->
<format>Vorbis</format>
<!-- Setting to allow stream to be listed in public YP directory -->
<public>No</public>
<!-- Encoder name (defined below) to use for (re)encoding -->
<encoder>OggEnc-Q1.5</encoder>
</stream>
<!-- Stream format: Vorbis, MP3, Theora -->
<format>Vorbis</format>
<!-- Encoder name (defined below) to use for (re)encoding -->
<encoder>OggEnc-Q1.5</encoder>
<!-- Various other informational settings -->
<stream_name>Test Stream</stream_name>
<stream_url>http://localhost:8000/</stream_url>
<stream_genre>Beeps</stream_genre>
<stream_description>Test tones and noise</stream_description>
<stream_quality>1.5</stream_quality>
<stream_bitrate>16</stream_bitrate>
<stream_samplerate>44100</stream_samplerate>
<stream_channels>2</stream_channels>
</stream>
</streams>
<!--
Media configuration

View File

@ -8,15 +8,19 @@
<ezstream>
<server>
<hostname>127.0.0.1</hostname>
<password>hackme</password>
</server>
<servers>
<server>
<hostname>127.0.0.1</hostname>
<password>hackme</password>
</server>
</servers>
<stream>
<mountpoint>/stream.ogg</mountpoint>
<format>Vorbis</format>
</stream>
<streams>
<stream>
<mountpoint>/stream.ogg</mountpoint>
<format>Vorbis</format>
</stream>
</streams>
<media>
<filename>playlist.m3u</filename>

View File

@ -8,15 +8,19 @@
<ezstream>
<server>
<hostname>127.0.0.1</hostname>
<password>hackme</password>
</server>
<servers>
<server>
<hostname>127.0.0.1</hostname>
<password>hackme</password>
</server>
</servers>
<stream>
<mountpoint>/stream.ogg</mountpoint>
<format>Vorbis</format>
</stream>
<streams>
<stream>
<mountpoint>/stream.ogg</mountpoint>
<format>Vorbis</format>
</stream>
</streams>
<media>
<type>stdin</type>

View File

@ -6,16 +6,20 @@
<ezstream>
<server>
<hostname>127.0.0.1</hostname>
<password>hackme</password>
</server>
<servers>
<server>
<hostname>127.0.0.1</hostname>
<password>hackme</password>
</server>
</servers>
<stream>
<mountpoint>/video.ogg</mountpoint>
<format>Theora</format>
<encoder>PassThrough</encoder>
</stream>
<streams>
<stream>
<mountpoint>/video.ogg</mountpoint>
<format>Theora</format>
<encoder>PassThrough</encoder>
</stream>
</streams>
<media>
<filename>playlist.m3u</filename>

View File

@ -7,6 +7,8 @@ noinst_HEADERS = \
cfg_decoder.h \
cfg_encoder.h \
cfg_private.h \
cfg_server.h \
cfg_stream.h \
cfg_xmlfile.h \
cmdline.h \
ezstream.h \
@ -20,6 +22,8 @@ libezstream_la_SOURCES = \
cfg.c \
cfg_decoder.c \
cfg_encoder.c \
cfg_server.c \
cfg_stream.c \
cfg_xmlfile.c \
cmdline.c \
log.c \

592
src/cfg.c
View File

@ -32,26 +32,24 @@
#include "log.h"
#include "xalloc.h"
static struct cfg cfg;
static struct cfg cfg_tmp;
static struct cfg_program cfg_program;
static struct cfg cfg;
static struct cfg cfg_tmp;
static void _cfg_reset(struct cfg *);
static void _cfg_copy(struct cfg *, struct cfg *);
static void _cfg_switch(struct cfg *, struct cfg *);
static int _cfg_load(void);
static void _cfg_save(void);
static void _cfg_restore(void);
static void _cfg_commit(void);
static void
_cfg_reset(struct cfg *c)
{
xfree(c->stream.mountpoint);
xfree(c->stream.name);
xfree(c->stream.url);
xfree(c->stream.genre);
xfree(c->stream.description);
xfree(c->stream.quality);
xfree(c->stream.bitrate);
xfree(c->stream.samplerate);
xfree(c->stream.channels);
xfree(c->stream.encoder);
cfg_server_list_destroy(&c->servers);
cfg_stream_list_destroy(&c->streams);
cfg_decoder_list_destroy(&c->decoders);
cfg_encoder_list_destroy(&c->encoders);
xfree(c->metadata.format_str);
@ -61,110 +59,76 @@ _cfg_reset(struct cfg *c)
}
static void
_cfg_copy(struct cfg *dst, struct cfg *src)
_cfg_switch(struct cfg *a, struct cfg *b)
{
dst->_master_cfg = src->_master_cfg;
struct cfg tmp;
memcpy(&dst->program, &src->program, sizeof(dst->program));
memcpy(&dst->server, &src->server, sizeof(dst->server));
if (src->stream.mountpoint)
dst->stream.mountpoint = xstrdup(src->stream.mountpoint);
if (src->stream.name)
dst->stream.name = xstrdup(src->stream.name);
if (src->stream.url)
dst->stream.url = xstrdup(src->stream.url);
if (src->stream.genre)
dst->stream.genre = xstrdup(src->stream.genre);
if (src->stream.description)
dst->stream.description = xstrdup(src->stream.description);
if (src->stream.quality)
dst->stream.quality = xstrdup(src->stream.quality);
if (src->stream.bitrate)
dst->stream.bitrate = xstrdup(src->stream.bitrate);
if (src->stream.samplerate)
dst->stream.samplerate = xstrdup(src->stream.samplerate);
if (src->stream.channels)
dst->stream.channels = xstrdup(src->stream.channels);
dst->stream.server_public = src->stream.server_public;
dst->stream.format = src->stream.format;
if (src->stream.encoder)
dst->stream.encoder = xstrdup(src->stream.encoder);
memcpy(&dst->media, &src->media, sizeof(dst->media));
strlcpy(dst->metadata.program, src->metadata.program,
sizeof(dst->metadata.program));
if (src->metadata.format_str)
dst->metadata.format_str = xstrdup(src->metadata.format_str);
dst->metadata.refresh_interval = src->metadata.refresh_interval;
dst->metadata.normalize_strings = src->metadata.normalize_strings;
dst->metadata.no_updates = src->metadata.no_updates;
memcpy(&tmp, a, sizeof(tmp));
memcpy(a, b, sizeof(*a));
memcpy(b, &tmp, sizeof(*b));
}
static int
_cfg_load(void)
{
switch (cfg.program.config_type) {
switch (cfg_program.config_type) {
case CFG_TYPE_XMLFILE:
if (0 > cfg_xmlfile_parse(cfg.program.config_file))
if (0 > cfg_xmlfile_parse(cfg_program.config_file))
return (-1);
break;
default:
log_alert("unsupported config type %u",
cfg.program.config_type);
cfg_program.config_type);
abort();
}
return (0);
}
static void
_cfg_save(void)
{
_cfg_reset(&cfg_tmp);
_cfg_switch(&cfg_tmp, &cfg);
_cfg_reset(&cfg);
}
static void
_cfg_restore(void)
{
_cfg_reset(&cfg);
_cfg_switch(&cfg, &cfg_tmp);
_cfg_reset(&cfg_tmp);
}
static void
_cfg_commit(void)
{
_cfg_reset(&cfg_tmp);
}
int
cfg_init(void)
{
_cfg_reset(&cfg);
cfg._master_cfg = 1;
_cfg_commit();
return (0);
}
void
cfg_exit(void)
{
_cfg_reset(&cfg);
}
void
cfg_save(void)
{
cfg_clear();
_cfg_copy(&cfg_tmp, &cfg);
}
void
cfg_restore(void)
{
if (!cfg_tmp._master_cfg)
return;
_cfg_reset(&cfg);
_cfg_copy(&cfg, &cfg_tmp);
cfg_clear();
}
void
cfg_clear(void)
{
_cfg_reset(&cfg_tmp);
(void)cfg_init();
}
int
cfg_file_reload(void)
{
cfg_save();
_cfg_save();
if (0 > _cfg_load()) {
cfg_restore();
_cfg_restore();
return (-1);
}
cfg_clear();
_cfg_commit();
return (0);
}
@ -172,18 +136,6 @@ cfg_file_reload(void)
int
cfg_check(const char **errstrp)
{
if (!cfg_get_server_hostname()) {
if (NULL != errstrp)
*errstrp = "server hostname missing";
return (-1);
}
if (!cfg_get_server_password()) {
if (NULL != errstrp)
*errstrp = "server password missing";
return (-1);
}
if (!cfg_get_media_filename() &&
CFG_MEDIA_STDIN != cfg_get_media_type()) {
if (NULL != errstrp)
@ -191,12 +143,6 @@ cfg_check(const char **errstrp)
return (-1);
}
if (CFG_STREAM_INVALID == cfg_get_stream_format()) {
if (NULL != errstrp)
*errstrp = "stream format missing or unsupported";
return (-1);
}
return (0);
}
@ -252,10 +198,46 @@ cfg_file_check(const char *file)
return (0);
}
cfg_decoder_list_t
cfg_get_decoders(void)
{
if (!cfg.decoders)
cfg.decoders = cfg_decoder_list_create();
return (cfg.decoders);
}
cfg_encoder_list_t
cfg_get_encoders(void)
{
if (!cfg.encoders)
cfg.encoders = cfg_encoder_list_create();
return (cfg.encoders);
}
cfg_server_list_t
cfg_get_servers(void)
{
if (!cfg.servers)
cfg.servers = cfg_server_list_create();
return (cfg.servers);
}
cfg_stream_list_t
cfg_get_streams(void)
{
if (!cfg.streams)
cfg.streams = cfg_stream_list_create();
return (cfg.streams);
}
int
cfg_set_program_name(const char *progname, const char **errstrp)
{
SET_STRLCPY(cfg.program.name, progname, errstrp);
SET_STRLCPY(cfg_program.name, progname, errstrp);
return (0);
}
@ -267,21 +249,21 @@ cfg_set_program_config_type(enum cfg_config_type type, const char **errstrp)
*errstrp = "invalid";
return (-1);
}
cfg.program.config_type = type;
cfg_program.config_type = type;
return (0);
}
int
cfg_set_program_config_file(const char *file, const char **errstrp)
{
SET_STRLCPY(cfg.program.config_file, file, errstrp);
SET_STRLCPY(cfg_program.config_file, file, errstrp);
return (0);
}
int
cfg_set_program_pid_file(const char *file, const char **errstrp)
{
SET_STRLCPY(cfg.program.pid_file, file, errstrp);
SET_STRLCPY(cfg_program.pid_file, file, errstrp);
return (0);
}
@ -289,7 +271,7 @@ int
cfg_set_program_quiet_stderr(int quiet_stderr, const char **not_used)
{
(void)not_used;
cfg.program.quiet_stderr = quiet_stderr ? 1 : 0;
cfg_program.quiet_stderr = quiet_stderr ? 1 : 0;
return (0);
}
@ -297,7 +279,7 @@ int
cfg_set_program_rtstatus_output(int rtstatus_output, const char **not_used)
{
(void)not_used;
cfg.program.rtstatus_output = rtstatus_output ? 1 : 0;
cfg_program.rtstatus_output = rtstatus_output ? 1 : 0;
return (0);
}
@ -305,229 +287,7 @@ int
cfg_set_program_verbosity(unsigned int verbosity, const char **not_used)
{
(void)not_used;
cfg.program.verbosity = verbosity;
return (0);
}
int
cfg_set_server_protocol(const char *protocol, const char **errstrp)
{
if (!protocol || !protocol[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
if (0 == strcasecmp("http", protocol))
cfg.server.protocol = CFG_PROTO_HTTP;
else if (0 == strcasecmp("https", protocol))
cfg.server.protocol = CFG_PROTO_HTTPS;
else {
if (NULL != errstrp)
*errstrp = "unsupported";
return (-1);
}
return (0);
}
int
cfg_set_server_hostname(const char *hostname, const char **errstrp)
{
SET_STRLCPY(cfg.server.hostname, hostname, errstrp);
return (0);
}
int
cfg_set_server_port(const char *port_str, const char **errstrp)
{
const char *errstr;
unsigned int port;
if (!port_str || !port_str[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
port = (unsigned int)strtonum(port_str, 1, UINT16_MAX, &errstr);
if (errstr) {
if (errstrp)
*errstrp = errstr;
return (-1);
}
cfg.server.port = port;
return (0);
}
int
cfg_set_server_user(const char *user, const char **errstrp)
{
SET_STRLCPY(cfg.server.user, user, errstrp);
return (0);
}
int
cfg_set_server_password(const char *password, const char **errstrp)
{
SET_STRLCPY(cfg.server.password, password, errstrp);
return (0);
}
int
cfg_set_server_tls(const char *tls, const char **errstrp)
{
if (!tls || !tls[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
if (0 == strcasecmp("may", tls))
cfg.server.tls = CFG_TLS_MAY;
else if (0 == strcasecmp("none", tls))
cfg.server.tls = CFG_TLS_NONE;
else if (0 == strcasecmp("required", tls))
cfg.server.tls = CFG_TLS_REQUIRED;
else {
if (NULL != errstrp)
*errstrp = "invalid";
return (-1);
}
return (0);
}
int
cfg_set_server_tls_cipher_suite(const char *suite, const char **errstrp)
{
SET_STRLCPY(cfg.server.tls_cipher_suite, suite, errstrp);
return (0);
}
int
cfg_set_server_ca_dir(const char *ca_dir, const char **errstrp)
{
SET_STRLCPY(cfg.server.ca_dir, ca_dir, errstrp);
return (0);
}
int
cfg_set_server_ca_file(const char *ca_file, const char **errstrp)
{
SET_STRLCPY(cfg.server.ca_file, ca_file, errstrp);
return (0);
}
int
cfg_set_server_client_cert(const char *client_cert, const char **errstrp)
{
SET_STRLCPY(cfg.server.client_cert, client_cert, errstrp);
return (0);
}
int
cfg_set_server_reconnect_attempts(const char *num_str, const char **errstrp)
{
SET_UINTNUM(cfg.server.reconnect_attempts, num_str, errstrp);
return (0);
}
int
cfg_set_stream_mountpoint(const char *mountpoint, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.mountpoint, mountpoint, errstrp);
return (0);
}
int
cfg_set_stream_name(const char *name, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.name, name, errstrp);
return (0);
}
int
cfg_set_stream_url(const char *url, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.url, url, errstrp);
return (0);
}
int
cfg_set_stream_genre(const char *genre, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.genre, genre, errstrp);
return (0);
}
int
cfg_set_stream_description(const char *description, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.description, description, errstrp);
return (0);
}
int
cfg_set_stream_quality(const char *quality, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.quality, quality, errstrp);
return (0);
}
int
cfg_set_stream_bitrate(const char *bitrate, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.bitrate, bitrate, errstrp);
return (0);
}
int
cfg_set_stream_samplerate(const char *samplerate, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.samplerate, samplerate, errstrp);
return (0);
}
int
cfg_set_stream_channels(const char *channels, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.channels, channels, errstrp);
return (0);
}
int
cfg_set_stream_server_public(const char *server_public, const char **errstrp)
{
SET_BOOLEAN(cfg.stream.server_public, server_public, errstrp);
return (0);
}
int
cfg_set_stream_format(const char *fmt_str, const char **errstrp)
{
enum cfg_stream_format fmt;
if (!fmt_str || !fmt_str[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
if (0 > cfg_stream_str2fmt(fmt_str, &fmt)) {
if (errstrp)
*errstrp = "unsupported stream format";
return (-1);
}
cfg.stream.format = fmt;
return (0);
}
int
cfg_set_stream_encoder(const char *encoder, const char **errstrp)
{
SET_XSTRDUP(cfg.stream.encoder, encoder, errstrp);
cfg_program.verbosity = verbosity;
return (0);
}
@ -635,203 +395,43 @@ cfg_set_metadata_no_updates(const char *no_updates, const char **errstrp)
const char *
cfg_get_program_name(void)
{
return (cfg.program.name);
return (cfg_program.name);
}
enum cfg_config_type
cfg_get_program_config_type(void)
{
return (cfg.program.config_type);
return (cfg_program.config_type);
}
const char *
cfg_get_program_config_file(void)
{
return (cfg.program.config_file[0] ? cfg.program.config_file : NULL);
return (cfg_program.config_file[0] ? cfg_program.config_file : NULL);
}
const char *
cfg_get_program_pid_file(void)
{
return (cfg.program.pid_file[0] ? cfg.program.pid_file : NULL);
return (cfg_program.pid_file[0] ? cfg_program.pid_file : NULL);
}
int
cfg_get_program_quiet_stderr(void)
{
return (cfg.program.quiet_stderr);
return (cfg_program.quiet_stderr);
}
int
cfg_get_program_rtstatus_output(void)
{
return (cfg.program.rtstatus_output);
return (cfg_program.rtstatus_output);
}
unsigned int
cfg_get_program_verbosity(void)
{
return (cfg.program.verbosity);
}
enum cfg_server_protocol
cfg_get_server_protocol(void)
{
return (cfg.server.protocol);
}
const char *
cfg_get_server_protocol_str(void)
{
switch (cfg.server.protocol) {
case CFG_PROTO_HTTP:
return ("http");
case CFG_PROTO_HTTPS:
return ("https");
default:
log_alert("unsupported protocol %u", cfg.server.protocol);
abort();
}
}
const char *
cfg_get_server_hostname(void)
{
return (cfg.server.hostname[0] ? cfg.server.hostname : NULL);
}
unsigned int
cfg_get_server_port(void)
{
return (cfg.server.port ? cfg.server.port : DEFAULT_PORT);
}
const char *
cfg_get_server_user(void)
{
return (cfg.server.user[0] ? cfg.server.user : DEFAULT_USER);
}
const char *
cfg_get_server_password(void)
{
return (cfg.server.password[0] ? cfg.server.password : NULL);
}
enum cfg_server_tls
cfg_get_server_tls(void)
{
return (cfg.server.tls);
}
const char *
cfg_get_server_tls_cipher_suite(void)
{
return (cfg.server.tls_cipher_suite[0]
? cfg.server.tls_cipher_suite
: NULL);
}
const char *
cfg_get_server_ca_dir(void)
{
return (cfg.server.ca_dir[0] ? cfg.server.ca_dir : NULL);
}
const char *
cfg_get_server_ca_file(void)
{
return (cfg.server.ca_file[0] ? cfg.server.ca_file : NULL);
}
const char *
cfg_get_server_client_cert(void)
{
return (cfg.server.client_cert[0] ? cfg.server.client_cert : NULL);
}
unsigned int
cfg_get_server_reconnect_attempts(void)
{
return (cfg.server.reconnect_attempts);
}
const char *
cfg_get_stream_mountpoint(void)
{
return (cfg.stream.mountpoint);
}
const char *
cfg_get_stream_name(void)
{
return (cfg.stream.name);
}
const char *
cfg_get_stream_url(void)
{
return (cfg.stream.url);
}
const char *
cfg_get_stream_genre(void)
{
return (cfg.stream.genre);
}
const char *
cfg_get_stream_description(void)
{
return (cfg.stream.description);
}
const char *
cfg_get_stream_quality(void)
{
return (cfg.stream.quality);
}
const char *
cfg_get_stream_bitrate(void)
{
return (cfg.stream.bitrate);
}
const char *
cfg_get_stream_samplerate(void)
{
return (cfg.stream.samplerate);
}
const char *
cfg_get_stream_channels(void)
{
return (cfg.stream.channels);
}
int
cfg_get_stream_server_public(void)
{
return (cfg.stream.server_public);
}
enum cfg_stream_format
cfg_get_stream_format(void)
{
return (cfg.stream.format);
}
const char *
cfg_get_stream_format_str(void)
{
return (cfg_stream_fmt2str(cfg.stream.format));
}
const char *
cfg_get_stream_encoder(void)
{
return (cfg.stream.encoder);
return (cfg_program.verbosity);
}
enum cfg_media_type

110
src/cfg.h
View File

@ -28,27 +28,14 @@
#define PLACEHOLDER_TRACK "@T@"
#define PLACEHOLDER_STRING "@s@"
#define CFG_DEFAULT "default"
enum cfg_config_type {
CFG_TYPE_XMLFILE = 0,
CFG_TYPE_MIN = CFG_TYPE_XMLFILE,
CFG_TYPE_MAX = CFG_TYPE_XMLFILE,
};
enum cfg_server_protocol {
CFG_PROTO_HTTP = 0,
CFG_PROTO_HTTPS,
CFG_PROTO_MIN = CFG_PROTO_HTTP,
CFG_PROTO_MAX = CFG_PROTO_HTTPS,
};
enum cfg_server_tls {
CFG_TLS_MAY = 0,
CFG_TLS_NONE,
CFG_TLS_REQUIRED,
CFG_TLS_MIN = CFG_TLS_MAY,
CFG_TLS_MAX = CFG_TLS_REQUIRED,
};
enum cfg_media_type {
CFG_MEDIA_AUTODETECT = 0,
CFG_MEDIA_FILE,
@ -70,24 +57,31 @@ enum cfg_stream_format {
#include "cfg_decoder.h"
#include "cfg_encoder.h"
#include "cfg_server.h"
#include "cfg_stream.h"
int cfg_init(void);
void cfg_exit(void);
void cfg_save(void);
void cfg_restore(void);
void cfg_clear(void);
int cfg_file_reload(void);
int cfg_check(const char **);
int cfg_file_reload(void);
int cfg_stream_str2fmt(const char *, enum cfg_stream_format *);
const char *
cfg_stream_fmt2str(enum cfg_stream_format);
int cfg_file_check(const char *);
cfg_decoder_list_t
cfg_get_decoders(void);
cfg_encoder_list_t
cfg_get_encoders(void);
cfg_server_list_t
cfg_get_servers(void);
cfg_stream_list_t
cfg_get_streams(void);
int cfg_set_program_name(const char *, const char **);
int cfg_set_program_config_type(enum cfg_config_type, const char **);
int cfg_set_program_config_file(const char *, const char **);
@ -96,31 +90,6 @@ int cfg_set_program_quiet_stderr(int, const char **);
int cfg_set_program_rtstatus_output(int, const char **);
int cfg_set_program_verbosity(unsigned int, const char **);
int cfg_set_server_protocol(const char *, const char **);
int cfg_set_server_hostname(const char *, const char **);
int cfg_set_server_port(const char *, const char **);
int cfg_set_server_user(const char *, const char **);
int cfg_set_server_password(const char *, const char **);
int cfg_set_server_tls(const char *, const char **);
int cfg_set_server_tls_cipher_suite(const char *, const char **);
int cfg_set_server_ca_dir(const char *, const char **);
int cfg_set_server_ca_file(const char *, const char **);
int cfg_set_server_client_cert(const char *, const char **);
int cfg_set_server_reconnect_attempts(const char *, const char **);
int cfg_set_stream_mountpoint(const char *, const char **);
int cfg_set_stream_name(const char *, const char **);
int cfg_set_stream_url(const char *, const char **);
int cfg_set_stream_genre(const char *, const char **);
int cfg_set_stream_description(const char *, const char **);
int cfg_set_stream_quality(const char *, const char **);
int cfg_set_stream_bitrate(const char *, const char **);
int cfg_set_stream_samplerate(const char *, const char **);
int cfg_set_stream_channels(const char *, const char **);
int cfg_set_stream_server_public(const char *, const char **);
int cfg_set_stream_format(const char *, const char **);
int cfg_set_stream_encoder(const char *, const char **);
int cfg_set_media_type(const char *, const char **);
int cfg_set_media_filename(const char *, const char **);
int cfg_set_media_shuffle(const char *, const char **);
@ -145,57 +114,6 @@ int cfg_get_program_rtstatus_output(void);
unsigned int
cfg_get_program_verbosity(void);
enum cfg_server_protocol
cfg_get_server_protocol(void);
const char *
cfg_get_server_protocol_str(void);
const char *
cfg_get_server_hostname(void);
unsigned int
cfg_get_server_port(void);
const char *
cfg_get_server_user(void);
const char *
cfg_get_server_password(void);
enum cfg_server_tls
cfg_get_server_tls(void);
const char *
cfg_get_server_tls_cipher_suite(void);
const char *
cfg_get_server_ca_dir(void);
const char *
cfg_get_server_ca_file(void);
const char *
cfg_get_server_client_cert(void);
unsigned int
cfg_get_server_reconnect_attempts(void);
const char *
cfg_get_stream_mountpoint(void);
const char *
cfg_get_stream_name(void);
const char *
cfg_get_stream_url(void);
const char *
cfg_get_stream_genre(void);
const char *
cfg_get_stream_description(void);
const char *
cfg_get_stream_quality(void);
const char *
cfg_get_stream_bitrate(void);
const char *
cfg_get_stream_samplerate(void);
const char *
cfg_get_stream_channels(void);
int cfg_get_stream_server_public(void);
enum cfg_stream_format
cfg_get_stream_format(void);
const char *
cfg_get_stream_format_str(void);
const char *
cfg_get_stream_encoder(void);
enum cfg_media_type
cfg_get_media_type(void);
const char *

View File

@ -39,79 +39,144 @@ struct cfg_decoder {
char *program;
struct file_ext_list exts;
};
TAILQ_HEAD(cfg_decoder_list, cfg_decoder);
static struct cfg_decoder_list cfg_decoders;
int
cfg_decoder_init(void)
struct cfg_decoder_list *
cfg_decoder_list_create(void)
{
TAILQ_INIT(&cfg_decoders);
return (0);
struct cfg_decoder_list *dl;
dl = xcalloc(1UL, sizeof(*dl));
TAILQ_INIT(dl);
return (dl);
}
void
cfg_decoder_exit(void)
cfg_decoder_list_destroy(cfg_decoder_list_t *dl_p)
{
struct cfg_decoder_list *dl = *dl_p;
struct cfg_decoder *d;
while (NULL != (d = TAILQ_FIRST(&cfg_decoders))) {
struct file_ext *e;
if (!dl)
return;
TAILQ_REMOVE(&cfg_decoders, d, entry);
xfree(d->name);
xfree(d->program);
while (NULL != (e = TAILQ_FIRST(&d->exts))) {
TAILQ_REMOVE(&d->exts, e, entry);
xfree(e->ext);
xfree(e);
}
xfree(d);
while (NULL != (d = TAILQ_FIRST(dl))) {
TAILQ_REMOVE(dl, d, entry);
cfg_decoder_destroy(&d);
}
xfree(dl);
*dl_p = NULL;
}
struct cfg_decoder *
cfg_decoder_get(const char *name)
cfg_decoder_list_find(struct cfg_decoder_list *dl, const char *name)
{
struct cfg_decoder *d;
TAILQ_FOREACH(d, dl, entry) {
if (0 == strcasecmp(d->name, name))
return (d);
}
return (NULL);
}
struct cfg_decoder *
cfg_decoder_list_findext(struct cfg_decoder_list *dl, const char *ext)
{
struct cfg_decoder *d;
TAILQ_FOREACH(d, dl, entry) {
if (cfg_decoder_extsupport(d, ext))
return (d);
}
return (NULL);
}
struct cfg_decoder *
cfg_decoder_list_get(struct cfg_decoder_list *dl, const char *name)
{
struct cfg_decoder *d;
d = cfg_decoder_list_find(dl, name);
if (d)
return (d);
d = cfg_decoder_create(name);
if (!d)
return (NULL);
TAILQ_INSERT_TAIL(dl, d, entry);
return (d);
}
struct cfg_decoder *
cfg_decoder_create(const char *name)
{
struct cfg_decoder *d;
if (!name || !name[0])
return (NULL);
TAILQ_FOREACH(d, &cfg_decoders, entry) {
if (0 == strcasecmp(d->name, name))
return (d);
}
d = xcalloc(1UL, sizeof(*d));
d->name = xstrdup(name);
TAILQ_INIT(&d->exts);
TAILQ_INSERT_TAIL(&cfg_decoders, d, entry);
return (d);
}
int
cfg_decoder_set_name(struct cfg_decoder *d, const char *name,
const char **errstrp)
void
cfg_decoder_destroy(struct cfg_decoder **d_p)
{
struct cfg_decoder *d = *d_p;
struct file_ext *e;
xfree(d->name);
xfree(d->program);
while (NULL != (e = TAILQ_FIRST(&d->exts))) {
TAILQ_REMOVE(&d->exts, e, entry);
xfree(e->ext);
xfree(e);
}
xfree(d);
*d_p = NULL;
}
int
cfg_decoder_set_name(struct cfg_decoder *d, struct cfg_decoder_list *dl,
const char *name, const char **errstrp)
{
struct cfg_decoder *d2;
if (!name || !name[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
xfree(d->name);
d->name = xstrdup(name);
d2 = cfg_decoder_list_find(dl, name);
if (d2 && d2 != d) {
if (errstrp)
*errstrp = "already exists";
return (-1);
}
SET_XSTRDUP(d->name, name, errstrp);
return (0);
}
int
cfg_decoder_set_program(struct cfg_decoder *d, const char *program,
cfg_decoder_set_program(struct cfg_decoder *d,
struct cfg_decoder_list *not_used, const char *program,
const char **errstrp)
{
(void)not_used;
if (!program || !program[0]) {
if (errstrp)
*errstrp = "empty";
@ -125,8 +190,8 @@ cfg_decoder_set_program(struct cfg_decoder *d, const char *program,
}
int
cfg_decoder_add_match(struct cfg_decoder *d, const char *ext,
const char **errstrp)
cfg_decoder_add_match(struct cfg_decoder *d, struct cfg_decoder_list *dl,
const char *ext, const char **errstrp)
{
struct cfg_decoder *d2;
struct file_ext *e, *e2;
@ -137,7 +202,7 @@ cfg_decoder_add_match(struct cfg_decoder *d, const char *ext,
return (-1);
}
d2 = cfg_decoder_find(ext);
d2 = cfg_decoder_list_findext(dl, ext);
e = NULL;
if (d2) {
e2 = TAILQ_FIRST(&d2->exts);
@ -193,21 +258,17 @@ cfg_decoder_validate(struct cfg_decoder *d, const char **errstrp)
return (0);
}
struct cfg_decoder *
cfg_decoder_find(const char *ext)
int
cfg_decoder_extsupport(struct cfg_decoder *d, const char *ext)
{
struct cfg_decoder *d;
struct file_ext *e;
TAILQ_FOREACH(d, &cfg_decoders, entry) {
struct file_ext *e;
TAILQ_FOREACH(e, &d->exts, entry) {
if (0 == strcasecmp(e->ext, ext))
return (d);
}
TAILQ_FOREACH(e, &d->exts, entry) {
if (0 == strcasecmp(e->ext, ext))
return (1);
}
return (NULL);
return (0);
}
const char *

View File

@ -17,22 +17,34 @@
#ifndef __CFG_INPUT_H__
#define __CFG_INPUT_H__
typedef struct cfg_decoder * cfg_decoder_t;
typedef struct cfg_decoder * cfg_decoder_t;
typedef struct cfg_decoder_list * cfg_decoder_list_t;
int cfg_decoder_init(void);
void cfg_decoder_exit(void);
cfg_decoder_list_t
cfg_decoder_list_create(void);
void cfg_decoder_list_destroy(cfg_decoder_list_t *);
cfg_decoder_t
cfg_decoder_get(const char *);
cfg_decoder_list_find(cfg_decoder_list_t, const char *);
cfg_decoder_t
cfg_decoder_list_findext(cfg_decoder_list_t, const char *);
cfg_decoder_t
cfg_decoder_list_get(cfg_decoder_list_t, const char *);
int cfg_decoder_set_name(cfg_decoder_t, const char *, const char **);
int cfg_decoder_set_program(cfg_decoder_t, const char *, const char **);
int cfg_decoder_add_match(cfg_decoder_t, const char *, const char **);
cfg_decoder_t
cfg_decoder_create(const char *);
void cfg_decoder_destroy(cfg_decoder_t *);
int cfg_decoder_set_name(cfg_decoder_t, cfg_decoder_list_t, const char *,
const char **);
int cfg_decoder_set_program(cfg_decoder_t, cfg_decoder_list_t,
const char *, const char **);
int cfg_decoder_add_match(cfg_decoder_t, cfg_decoder_list_t, const char *,
const char **);
int cfg_decoder_validate(cfg_decoder_t, const char **);
cfg_decoder_t
cfg_decoder_find(const char *);
int cfg_decoder_extsupport(cfg_decoder_t, const char *);
const char *
cfg_decoder_get_name(cfg_decoder_t);

View File

@ -33,83 +33,134 @@ struct cfg_encoder {
enum cfg_stream_format format;
char *program;
};
TAILQ_HEAD(cfg_encoder_list, cfg_encoder);
static struct cfg_encoder_list cfg_encoders;
int
cfg_encoder_init(void)
struct cfg_encoder_list *
cfg_encoder_list_create(void)
{
TAILQ_INIT(&cfg_encoders);
return (0);
struct cfg_encoder_list *el;
el = xcalloc(1UL, sizeof(*el));
TAILQ_INIT(el);
return (el);
}
void
cfg_encoder_exit(void)
cfg_encoder_list_destroy(cfg_encoder_list_t *el_p)
{
struct cfg_encoder_list *el = *el_p;
struct cfg_encoder *e;
while (NULL != (e = TAILQ_FIRST(&cfg_encoders))) {
TAILQ_REMOVE(&cfg_encoders, e, entry);
xfree(e->name);
xfree(e->program);
xfree(e);
if (!el)
return;
while (NULL != (e = TAILQ_FIRST(el))) {
TAILQ_REMOVE(el, e, entry);
cfg_encoder_destroy(&e);
}
xfree(el);
*el_p = NULL;
}
struct cfg_encoder *
cfg_encoder_get(const char *name)
cfg_encoder_list_find(struct cfg_encoder_list *el, const char *name)
{
struct cfg_encoder *e;
TAILQ_FOREACH(e, el, entry) {
if (0 == strcasecmp(e->name, name))
return (e);
}
return (NULL);
}
struct cfg_encoder *
cfg_encoder_list_get(struct cfg_encoder_list *el, const char *name)
{
struct cfg_encoder *e;
e = cfg_encoder_list_find(el, name);
if (e)
return (e);
e = cfg_encoder_create(name);
if (!e)
return (NULL);
TAILQ_INSERT_TAIL(el, e, entry);
return (e);
}
struct cfg_encoder *
cfg_encoder_create(const char *name)
{
struct cfg_encoder *e;
if (!name || !name[0])
return (NULL);
TAILQ_FOREACH(e, &cfg_encoders, entry) {
if (0 == strcasecmp(e->name, name))
return (e);
}
e = xcalloc(1UL, sizeof(*e));
e->name = xstrdup(name);
TAILQ_INSERT_TAIL(&cfg_encoders, e, entry);
return (e);
}
int
cfg_encoder_set_name(struct cfg_encoder *e, const char *name,
const char **errstrp)
void
cfg_encoder_destroy(struct cfg_encoder **e_p)
{
if (!name || !name[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
struct cfg_encoder *e = *e_p;
xfree(e->name);
e->name = xstrdup(name);
return (0);
xfree(e->program);
xfree(e);
*e_p = NULL;
}
int
cfg_encoder_set_format(struct cfg_encoder *e, enum cfg_stream_format fmt,
const char **not_used)
cfg_encoder_set_format(struct cfg_encoder *e, enum cfg_stream_format fmt)
{
(void)not_used;
assert(CFG_STREAM_MIN <= fmt && CFG_STREAM_MAX >= fmt);
e->format = fmt;
return (0);
}
int
cfg_encoder_set_format_str(struct cfg_encoder *e, const char *fmt_str,
cfg_encoder_set_name(struct cfg_encoder *e, struct cfg_encoder_list *el,
const char *name, const char **errstrp)
{
struct cfg_encoder *e2;
if (!name || !name[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
e2 = cfg_encoder_list_find(el, name);
if (e2 && e2 != e) {
if (errstrp)
*errstrp = "already exists";
return (-1);
}
SET_XSTRDUP(e->name, name, errstrp);
return (0);
}
int
cfg_encoder_set_format_str(struct cfg_encoder *e,
struct cfg_encoder_list *not_used, const char *fmt_str,
const char **errstrp)
{
enum cfg_stream_format fmt;
(void)not_used;
if (!fmt_str || !fmt_str[0]) {
if (errstrp)
*errstrp = "empty";
@ -122,16 +173,18 @@ cfg_encoder_set_format_str(struct cfg_encoder *e, const char *fmt_str,
return (-1);
}
cfg_encoder_set_format(e, fmt, errstrp);
cfg_encoder_set_format(e, fmt);
return (0);
}
int
cfg_encoder_set_program(struct cfg_encoder *e, const char *program,
const char **not_used)
cfg_encoder_set_program(struct cfg_encoder *e,
struct cfg_encoder_list *not_used, const char *program,
const char **not_used2)
{
(void)not_used;
(void)not_used2;
xfree(e->program);

View File

@ -17,19 +17,30 @@
#ifndef __CFG_ENCODER_H__
#define __CFG_ENCODER_H__
typedef struct cfg_encoder * cfg_encoder_t;
typedef struct cfg_encoder * cfg_encoder_t;
typedef struct cfg_encoder_list * cfg_encoder_list_t;
int cfg_encoder_init(void);
void cfg_encoder_exit(void);
cfg_encoder_list_t
cfg_encoder_list_create(void);
void cfg_encoder_list_destroy(cfg_encoder_list_t *);
cfg_encoder_t
cfg_encoder_get(const char *);
cfg_encoder_list_find(cfg_encoder_list_t, const char *);
cfg_encoder_t
cfg_encoder_list_get(cfg_encoder_list_t, const char *);
int cfg_encoder_set_name(cfg_encoder_t, const char *, const char **);
int cfg_encoder_set_format(cfg_encoder_t, enum cfg_stream_format,
cfg_encoder_t
cfg_encoder_create(const char *);
void cfg_encoder_destroy(cfg_encoder_t *);
int cfg_encoder_set_format(cfg_encoder_t, enum cfg_stream_format);
int cfg_encoder_set_name(cfg_encoder_t, cfg_encoder_list_t, const char *,
const char **);
int cfg_encoder_set_format_str(cfg_encoder_t, const char *, const char **);
int cfg_encoder_set_program(cfg_encoder_t, const char *, const char **);
int cfg_encoder_set_format_str(cfg_encoder_t, cfg_encoder_list_t,
const char *, const char **);
int cfg_encoder_set_program(cfg_encoder_t, cfg_encoder_list_t,
const char *, const char **);
int cfg_encoder_validate(cfg_encoder_t, const char **);

View File

@ -19,56 +19,33 @@
#include "cfg.h"
#include <sys/queue.h>
#include <limits.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#define EXTENSIONS_MAX 16
#define UCREDS_SIZE 256
#define CSUITE_SIZE 2048
#define EXTENSIONS_MAX 16
#define UCREDS_SIZE 256
#define CSUITE_SIZE 2048
#define DEFAULT_PORT 8000
#define DEFAULT_USER "source"
#define DEFAULT_PORT 8000
#define DEFAULT_USER "source"
struct cfg_program {
char name[PATH_MAX];
enum cfg_config_type config_type;
char config_file[PATH_MAX];
char pid_file[PATH_MAX];
int quiet_stderr;
int rtstatus_output;
unsigned int verbosity;
};
struct cfg {
int _master_cfg;
struct program {
char name[PATH_MAX];
enum cfg_config_type config_type;
char config_file[PATH_MAX];
char pid_file[PATH_MAX];
int quiet_stderr;
int rtstatus_output;
unsigned int verbosity;
} program;
struct server {
enum cfg_server_protocol protocol;
char hostname[NI_MAXHOST];
unsigned int port;
char user[UCREDS_SIZE];
char password[UCREDS_SIZE];
enum cfg_server_tls tls;
char tls_cipher_suite[CSUITE_SIZE];
char ca_dir[PATH_MAX];
char ca_file[PATH_MAX];
char client_cert[PATH_MAX];
unsigned int reconnect_attempts;
} server;
struct stream {
char *mountpoint;
char *name;
char *url;
char *genre;
char *description;
char *quality;
char *bitrate;
char *samplerate;
char *channels;
int server_public;
enum cfg_stream_format format;
char *encoder;
} stream;
cfg_server_list_t servers;
cfg_stream_list_t streams;
struct media {
enum cfg_media_type type;
char filename[PATH_MAX];
@ -82,6 +59,8 @@ struct cfg {
int normalize_strings;
int no_updates;
} metadata;
cfg_decoder_list_t decoders;
cfg_encoder_list_t encoders;
};
#define SET_STRLCPY(t, s, e) do { \
@ -104,8 +83,7 @@ struct cfg {
*(e) = "empty"; \
return (-1); \
} \
if ((t)) \
xfree((t)); \
xfree((t)); \
(t) = xstrdup((s)); \
} while (0)

409
src/cfg_server.c Normal file
View File

@ -0,0 +1,409 @@
/*
* Copyright (c) 2017 Moritz Grimm <mgrimm@mrsserver.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */
#include "compat.h"
#include <sys/queue.h>
#include <assert.h>
#include <string.h>
#include "cfg_private.h"
#include "cfg_server.h"
#include "xalloc.h"
struct cfg_server {
TAILQ_ENTRY(cfg_server) entry;
char *name;
enum cfg_server_protocol protocol;
char hostname[NI_MAXHOST];
unsigned int port;
char user[UCREDS_SIZE];
char password[UCREDS_SIZE];
enum cfg_server_tls tls;
char tls_cipher_suite[CSUITE_SIZE];
char ca_dir[PATH_MAX];
char ca_file[PATH_MAX];
char client_cert[PATH_MAX];
unsigned int reconnect_attempts;
};
TAILQ_HEAD(cfg_server_list, cfg_server);
struct cfg_server_list *
cfg_server_list_create(void)
{
struct cfg_server_list *sl;
sl = xcalloc(1UL, sizeof(*sl));
TAILQ_INIT(sl);
return (sl);
}
void
cfg_server_list_destroy(cfg_server_list_t *sl_p)
{
struct cfg_server_list *sl = *sl_p;
struct cfg_server *s;
if (!sl)
return;
while (NULL != (s = TAILQ_FIRST(sl))) {
TAILQ_REMOVE(sl, s, entry);
cfg_server_destroy(&s);
}
xfree(sl);
*sl_p = NULL;
}
struct cfg_server *
cfg_server_list_find(struct cfg_server_list *sl, const char *name)
{
struct cfg_server *s;
TAILQ_FOREACH(s, sl, entry) {
if (0 == strcasecmp(s->name, name))
return (s);
}
return (NULL);
}
struct cfg_server *
cfg_server_list_get(struct cfg_server_list *sl, const char *name)
{
struct cfg_server *s;
s = cfg_server_list_find(sl, name);
if (s)
return (s);
s = cfg_server_create(name);
if (!s)
return (NULL);
TAILQ_INSERT_TAIL(sl, s, entry);
return (s);
}
struct cfg_server *
cfg_server_create(const char *name)
{
struct cfg_server *s;
if (!name || !name[0])
return (NULL);
s = xcalloc(1UL, sizeof(*s));
s->name = xstrdup(name);
return (s);
}
void
cfg_server_destroy(struct cfg_server **s_p)
{
struct cfg_server *s = *s_p;
xfree(s->name);
xfree(s);
*s_p = NULL;
}
int
cfg_server_set_name(struct cfg_server *s, struct cfg_server_list *sl,
const char *name, const char **errstrp)
{
struct cfg_server *s2;
if (!name || !name[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
s2 = cfg_server_list_find(sl, name);
if (s2 && s2 != s) {
if (errstrp)
*errstrp = "already exists";
return (-1);
}
SET_XSTRDUP(s->name, name, errstrp);
return (0);
}
int
cfg_server_set_protocol(struct cfg_server *s, struct cfg_server_list *not_used,
const char *protocol, const char **errstrp)
{
(void)not_used;
if (!protocol || !protocol[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
if (0 == strcasecmp("http", protocol))
s->protocol = CFG_PROTO_HTTP;
else if (0 == strcasecmp("https", protocol))
s->protocol = CFG_PROTO_HTTPS;
else {
if (NULL != errstrp)
*errstrp = "unsupported";
return (-1);
}
return (0);
}
int
cfg_server_set_hostname(struct cfg_server *s, struct cfg_server_list *not_used,
const char *hostname, const char **errstrp)
{
(void)not_used;
SET_STRLCPY(s->hostname, hostname, errstrp);
return (0);
}
int
cfg_server_set_port(struct cfg_server *s, struct cfg_server_list *not_used,
const char *port_str, const char **errstrp)
{
const char *errstr;
unsigned int port;
(void)not_used;
if (!port_str || !port_str[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
port = (unsigned int)strtonum(port_str, 1, UINT16_MAX, &errstr);
if (errstr) {
if (errstrp)
*errstrp = errstr;
return (-1);
}
s->port = port;
return (0);
}
int
cfg_server_set_user(struct cfg_server *s, struct cfg_server_list *not_used,
const char *user, const char **errstrp)
{
(void)not_used;
SET_STRLCPY(s->user, user, errstrp);
return (0);
}
int
cfg_server_set_password(struct cfg_server *s, struct cfg_server_list *not_used,
const char *password, const char **errstrp)
{
(void)not_used;
SET_STRLCPY(s->password, password, errstrp);
return (0);
}
int
cfg_server_set_tls(struct cfg_server *s, struct cfg_server_list *not_used,
const char *tls, const char **errstrp)
{
(void)not_used;
if (!tls || !tls[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
if (0 == strcasecmp("may", tls))
s->tls = CFG_TLS_MAY;
else if (0 == strcasecmp("none", tls))
s->tls = CFG_TLS_NONE;
else if (0 == strcasecmp("required", tls))
s->tls = CFG_TLS_REQUIRED;
else {
if (NULL != errstrp)
*errstrp = "invalid";
return (-1);
}
return (0);
}
int
cfg_server_set_tls_cipher_suite(struct cfg_server *s,
struct cfg_server_list *not_used,const char *suite, const char **errstrp)
{
(void)not_used;
SET_STRLCPY(s->tls_cipher_suite, suite, errstrp);
return (0);
}
int
cfg_server_set_ca_dir(struct cfg_server *s, struct cfg_server_list *not_used,
const char *ca_dir, const char **errstrp)
{
(void)not_used;
SET_STRLCPY(s->ca_dir, ca_dir, errstrp);
return (0);
}
int
cfg_server_set_ca_file(struct cfg_server *s, struct cfg_server_list *not_used,
const char *ca_file, const char **errstrp)
{
(void)not_used;
SET_STRLCPY(s->ca_file, ca_file, errstrp);
return (0);
}
int
cfg_server_set_client_cert(struct cfg_server *s,
struct cfg_server_list *not_used,const char *client_cert,
const char **errstrp)
{
(void)not_used;
SET_STRLCPY(s->client_cert, client_cert, errstrp);
return (0);
}
int
cfg_server_set_reconnect_attempts(struct cfg_server *s,
struct cfg_server_list *not_used,const char *num_str,
const char **errstrp)
{
(void)not_used;
SET_UINTNUM(s->reconnect_attempts, num_str, errstrp);
return (0);
}
int
cfg_server_validate(struct cfg_server *s, const char **errstrp)
{
if (!cfg_server_get_hostname(s)) {
if (NULL != errstrp)
*errstrp = "hostname missing";
return (-1);
}
if (!cfg_server_get_password(s)) {
if (NULL != errstrp)
*errstrp = "password missing";
return (-1);
}
return (0);
}
const char *
cfg_server_get_name(struct cfg_server *s)
{
return (s->name);
}
enum cfg_server_protocol
cfg_server_get_protocol(struct cfg_server *s)
{
return (s->protocol);
}
const char *
cfg_server_get_protocol_str(struct cfg_server *s)
{
switch (s->protocol) {
case CFG_PROTO_HTTPS:
return ("https");
case CFG_PROTO_HTTP:
default:
return ("http");
}
}
const char *
cfg_server_get_hostname(struct cfg_server *s)
{
return (s->hostname[0] ? s->hostname : NULL);
}
unsigned int
cfg_server_get_port(struct cfg_server *s)
{
return (s->port ? s->port : DEFAULT_PORT);
}
const char *
cfg_server_get_user(struct cfg_server *s)
{
return (s->user[0] ? s->user : DEFAULT_USER);
}
const char *
cfg_server_get_password(struct cfg_server *s)
{
return (s->password[0] ? s->password : NULL);
}
enum cfg_server_tls
cfg_server_get_tls(struct cfg_server *s)
{
return (s->tls);
}
const char *
cfg_server_get_tls_cipher_suite(struct cfg_server *s)
{
return (s->tls_cipher_suite[0]
? s->tls_cipher_suite
: NULL);
}
const char *
cfg_server_get_ca_dir(struct cfg_server *s)
{
return (s->ca_dir[0] ? s->ca_dir : NULL);
}
const char *
cfg_server_get_ca_file(struct cfg_server *s)
{
return (s->ca_file[0] ? s->ca_file : NULL);
}
const char *
cfg_server_get_client_cert(struct cfg_server *s)
{
return (s->client_cert[0] ? s->client_cert : NULL);
}
unsigned int
cfg_server_get_reconnect_attempts(struct cfg_server *s)
{
return (s->reconnect_attempts);
}

105
src/cfg_server.h Normal file
View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2017 Moritz Grimm <mgrimm@mrsserver.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __CFG_SERVER_H__
#define __CFG_SERVER_H__
enum cfg_server_protocol {
CFG_PROTO_HTTP = 0,
CFG_PROTO_HTTPS,
CFG_PROTO_MIN = CFG_PROTO_HTTP,
CFG_PROTO_MAX = CFG_PROTO_HTTPS,
};
enum cfg_server_tls {
CFG_TLS_MAY = 0,
CFG_TLS_NONE,
CFG_TLS_REQUIRED,
CFG_TLS_MIN = CFG_TLS_MAY,
CFG_TLS_MAX = CFG_TLS_REQUIRED,
};
typedef struct cfg_server * cfg_server_t;
typedef struct cfg_server_list * cfg_server_list_t;
cfg_server_list_t
cfg_server_list_create(void);
void cfg_server_list_destroy(cfg_server_list_t *);
cfg_server_t
cfg_server_list_find(cfg_server_list_t, const char *);
cfg_server_t
cfg_server_list_get(cfg_server_list_t, const char *);
cfg_server_t
cfg_server_create(const char *);
void cfg_server_destroy(cfg_server_t *);
int cfg_server_set_name(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_protocol(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_hostname(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_port(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_user(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_password(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_tls(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_tls_cipher_suite(cfg_server_t, cfg_server_list_t,
const char *, const char **);
int cfg_server_set_ca_dir(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_ca_file(cfg_server_t, cfg_server_list_t, const char *,
const char **);
int cfg_server_set_client_cert(cfg_server_t, cfg_server_list_t,
const char *, const char **);
int cfg_server_set_reconnect_attempts(cfg_server_t, cfg_server_list_t,
const char *, const char **);
int cfg_server_validate(cfg_server_t, const char **);
const char *
cfg_server_get_name(cfg_server_t);
enum cfg_server_protocol
cfg_server_get_protocol(cfg_server_t);
const char *
cfg_server_get_protocol_str(cfg_server_t);
const char *
cfg_server_get_hostname(cfg_server_t);
unsigned int
cfg_server_get_port(cfg_server_t);
const char *
cfg_server_get_user(cfg_server_t);
const char *
cfg_server_get_password(cfg_server_t);
enum cfg_server_tls
cfg_server_get_tls(cfg_server_t);
const char *
cfg_server_get_tls_cipher_suite(cfg_server_t);
const char *
cfg_server_get_ca_dir(cfg_server_t);
const char *
cfg_server_get_ca_file(cfg_server_t);
const char *
cfg_server_get_client_cert(cfg_server_t);
unsigned int
cfg_server_get_reconnect_attempts(cfg_server_t);
#endif /* __CFG_SERVER_H__ */

410
src/cfg_stream.c Normal file
View File

@ -0,0 +1,410 @@
/*
* Copyright (c) 2017 Moritz Grimm <mgrimm@mrsserver.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */
#include <sys/queue.h>
#include <assert.h>
#include <string.h>
#include "cfg_private.h"
#include "cfg_stream.h"
#include "xalloc.h"
struct cfg_stream {
TAILQ_ENTRY(cfg_stream) entry;
char *name;
char *mountpoint;
char *server;
int public;
enum cfg_stream_format format;
char *encoder;
char *stream_name;
char *stream_url;
char *stream_genre;
char *stream_description;
char *stream_quality;
char *stream_bitrate;
char *stream_samplerate;
char *stream_channels;
};
TAILQ_HEAD(cfg_stream_list, cfg_stream);
struct cfg_stream_list *
cfg_stream_list_create(void)
{
struct cfg_stream_list *sl;
sl = xcalloc(1UL, sizeof(*sl));
TAILQ_INIT(sl);
return (sl);
}
void
cfg_stream_list_destroy(cfg_stream_list_t *sl_p)
{
struct cfg_stream_list *sl = *sl_p;
struct cfg_stream *s;
if (!sl)
return;
while (NULL != (s = TAILQ_FIRST(sl))) {
TAILQ_REMOVE(sl, s, entry);
cfg_stream_destroy(&s);
}
xfree(sl);
*sl_p = NULL;
}
struct cfg_stream *
cfg_stream_list_find(struct cfg_stream_list *sl, const char *name)
{
struct cfg_stream *s;
TAILQ_FOREACH(s, sl, entry) {
if (0 == strcasecmp(s->name, name))
return (s);
}
return (NULL);
}
struct cfg_stream *
cfg_stream_list_get(struct cfg_stream_list *sl, const char *name)
{
struct cfg_stream *s;
s = cfg_stream_list_find(sl, name);
if (s)
return (s);
s = cfg_stream_create(name);
if (!s)
return (NULL);
TAILQ_INSERT_TAIL(sl, s, entry);
return (s);
}
struct cfg_stream *
cfg_stream_create(const char *name)
{
struct cfg_stream *s;
if (!name || !name[0])
return (NULL);
s = xcalloc(1UL, sizeof(*s));
s->name = xstrdup(name);
return (s);
}
void
cfg_stream_destroy(struct cfg_stream **s_p)
{
struct cfg_stream *s = *s_p;
xfree(s->name);
xfree(s->mountpoint);
xfree(s->server);
xfree(s->encoder);
xfree(s->stream_name);
xfree(s->stream_url);
xfree(s->stream_genre);
xfree(s->stream_description);
xfree(s->stream_quality);
xfree(s->stream_bitrate);
xfree(s->stream_samplerate);
xfree(s->stream_channels);
xfree(s);
*s_p = NULL;
}
int
cfg_stream_set_name(struct cfg_stream *s, struct cfg_stream_list *sl,
const char *name, const char **errstrp)
{
struct cfg_stream *s2;
if (!name || !name[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
s2 = cfg_stream_list_find(sl, name);
if (s2 && s2 != s) {
if (errstrp)
*errstrp = "already exists";
return (-1);
}
SET_XSTRDUP(s->name, name, errstrp);
return (0);
}
int
cfg_stream_set_mountpoint(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *mountpoint,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->mountpoint, mountpoint, errstrp);
return (0);
}
int
cfg_stream_set_server(struct cfg_stream *s, struct cfg_stream_list *not_used,
const char *server, const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->server, server, errstrp);
return (0);
}
int
cfg_stream_set_public(struct cfg_stream *s, struct cfg_stream_list *not_used,
const char *public, const char **errstrp)
{
(void)not_used;
SET_BOOLEAN(s->public, public, errstrp);
return (0);
}
int
cfg_stream_set_format(struct cfg_stream *s, struct cfg_stream_list *not_used,
const char *fmt_str, const char **errstrp)
{
enum cfg_stream_format fmt;
(void)not_used;
if (!fmt_str || !fmt_str[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
if (0 > cfg_stream_str2fmt(fmt_str, &fmt)) {
if (errstrp)
*errstrp = "unsupported stream format";
return (-1);
}
s->format = fmt;
return (0);
}
int
cfg_stream_set_encoder(struct cfg_stream *s, struct cfg_stream_list *not_used,
const char *encoder, const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->encoder, encoder, errstrp);
return (0);
}
int
cfg_stream_set_stream_name(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *stream_name,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->stream_name, stream_name, errstrp);
return (0);
}
int
cfg_stream_set_stream_url(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *stream_url,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->stream_url, stream_url, errstrp);
return (0);
}
int
cfg_stream_set_stream_genre(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *stream_genre,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->stream_genre, stream_genre, errstrp);
return (0);
}
int
cfg_stream_set_stream_description(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *stream_description,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->stream_description, stream_description, errstrp);
return (0);
}
int
cfg_stream_set_stream_quality(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *stream_quality,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->stream_quality, stream_quality, errstrp);
return (0);
}
int
cfg_stream_set_stream_bitrate(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *stream_bitrate,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->stream_bitrate, stream_bitrate, errstrp);
return (0);
}
int
cfg_stream_set_stream_samplerate(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *stream_samplerate,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->stream_samplerate, stream_samplerate, errstrp);
return (0);
}
int
cfg_stream_set_stream_channels(struct cfg_stream *s,
struct cfg_stream_list *not_used, const char *stream_channels,
const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->stream_channels, stream_channels, errstrp);
return (0);
}
int
cfg_stream_validate(struct cfg_stream *s, const char **errstrp)
{
if (CFG_STREAM_INVALID == cfg_stream_get_format(s)) {
if (NULL != errstrp)
*errstrp = "format missing or unsupported";
return (-1);
}
return (0);
}
const char *
cfg_stream_get_name(struct cfg_stream *s)
{
return (s->name);
}
const char *
cfg_stream_get_mountpoint(struct cfg_stream *s)
{
return (s->mountpoint);
}
const char *
cfg_stream_get_server(struct cfg_stream *s)
{
return (s->server);
}
int
cfg_stream_get_public(struct cfg_stream *s)
{
return (s->public);
}
enum cfg_stream_format
cfg_stream_get_format(struct cfg_stream *s)
{
return (s->format);
}
const char *
cfg_stream_get_format_str(struct cfg_stream *s)
{
return (cfg_stream_fmt2str(s->format));
}
const char *
cfg_stream_get_encoder(struct cfg_stream *s)
{
return (s->encoder);
}
const char *
cfg_stream_get_stream_name(struct cfg_stream *s)
{
return (s->stream_name);
}
const char *
cfg_stream_get_stream_url(struct cfg_stream *s)
{
return (s->stream_url);
}
const char *
cfg_stream_get_stream_genre(struct cfg_stream *s)
{
return (s->stream_genre);
}
const char *
cfg_stream_get_stream_description(struct cfg_stream *s)
{
return (s->stream_description);
}
const char *
cfg_stream_get_stream_quality(struct cfg_stream *s)
{
return (s->stream_quality);
}
const char *
cfg_stream_get_stream_bitrate(struct cfg_stream *s)
{
return (s->stream_bitrate);
}
const char *
cfg_stream_get_stream_samplerate(struct cfg_stream *s)
{
return (s->stream_samplerate);
}
const char *
cfg_stream_get_stream_channels(struct cfg_stream *s)
{
return (s->stream_channels);
}

97
src/cfg_stream.h Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2017 Moritz Grimm <mgrimm@mrsserver.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __CFG_STREAM_H__
#define __CFG_STREAM_H__
typedef struct cfg_stream * cfg_stream_t;
typedef struct cfg_stream_list * cfg_stream_list_t;
cfg_stream_list_t
cfg_stream_list_create(void);
void cfg_stream_list_destroy(cfg_stream_list_t *);
cfg_stream_t
cfg_stream_list_find(cfg_stream_list_t, const char *);
cfg_stream_t
cfg_stream_list_get(cfg_stream_list_t, const char *);
cfg_stream_t
cfg_stream_create(const char *);
void cfg_stream_destroy(cfg_stream_t *);
int cfg_stream_set_name(cfg_stream_t, cfg_stream_list_t, const char *,
const char **);
int cfg_stream_set_mountpoint(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_set_server(cfg_stream_t, cfg_stream_list_t, const char *,
const char **);
int cfg_stream_set_public(cfg_stream_t, cfg_stream_list_t, const char *,
const char **);
int cfg_stream_set_format(cfg_stream_t, cfg_stream_list_t, const char *,
const char **);
int cfg_stream_set_encoder(cfg_stream_t, cfg_stream_list_t, const char *,
const char **);
int cfg_stream_set_stream_name(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_set_stream_url(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_set_stream_genre(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_set_stream_description(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_set_stream_quality(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_set_stream_bitrate(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_set_stream_samplerate(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_set_stream_channels(cfg_stream_t, cfg_stream_list_t,
const char *, const char **);
int cfg_stream_validate(cfg_stream_t, const char **);
const char *
cfg_stream_get_name(cfg_stream_t);
const char *
cfg_stream_get_mountpoint(cfg_stream_t);
const char *
cfg_stream_get_server(cfg_stream_t);
int cfg_stream_get_public(cfg_stream_t);
enum cfg_stream_format
cfg_stream_get_format(cfg_stream_t);
const char *
cfg_stream_get_format_str(cfg_stream_t);
const char *
cfg_stream_get_encoder(cfg_stream_t);
const char *
cfg_stream_get_stream_name(cfg_stream_t);
const char *
cfg_stream_get_stream_url(cfg_stream_t);
const char *
cfg_stream_get_stream_genre(cfg_stream_t);
const char *
cfg_stream_get_stream_description(cfg_stream_t);
const char *
cfg_stream_get_stream_quality(cfg_stream_t);
const char *
cfg_stream_get_stream_bitrate(cfg_stream_t);
const char *
cfg_stream_get_stream_samplerate(cfg_stream_t);
const char *
cfg_stream_get_stream_channels(cfg_stream_t);
#endif /* __CFG_STREAM_H__ */

View File

@ -25,10 +25,10 @@
#include <libxml/parser.h>
static unsigned int decoder_id, encoder_id;
static int _cfg_xmlfile_parse_server(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_servers(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_stream(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_streams(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_media(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_metadata(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_decoder(xmlDocPtr, xmlNodePtr);
@ -39,6 +39,139 @@ static int _cfg_xmlfile_parse_encoders(xmlDocPtr, xmlNodePtr);
#define XML_CHAR(s) (const xmlChar *)(s)
#define STD_CHAR(s) (const char *)(s)
#define XML_SERVER_SET(c, l, f, e) do { \
if (0 == xmlStrcasecmp(cur->name, XML_CHAR((e)))) { \
xmlChar *val; \
const char *err_str; \
int error = 0; \
\
val = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); \
if (0 > (f)((c), (l), STD_CHAR(val), &err_str)) { \
log_error("%s[%ld]: server (%s): %s: %s", \
doc->name, xmlGetLineNo(cur), \
cfg_server_get_name((c)), (e), err_str); \
error = 1; \
} \
xmlFree(val); \
if (error) \
return (-1); \
} \
} while (0)
static int
_cfg_xmlfile_parse_server(xmlDocPtr doc, xmlNodePtr cur)
{
cfg_server_list_t sl;
cfg_server_t s;
const char *errstr;
long int line_no = xmlGetLineNo(cur);
sl = cfg_get_servers();
s = cfg_server_list_get(sl, CFG_DEFAULT);
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_SERVER_SET(s, sl, cfg_server_set_name, "name");
XML_SERVER_SET(s, sl, cfg_server_set_protocol, "protocol");
XML_SERVER_SET(s, sl, cfg_server_set_hostname, "hostname");
XML_SERVER_SET(s, sl, cfg_server_set_port, "port");
XML_SERVER_SET(s, sl, cfg_server_set_user, "user");
XML_SERVER_SET(s, sl, cfg_server_set_password, "password");
XML_SERVER_SET(s, sl, cfg_server_set_reconnect_attempts, "reconnect_attempts");
XML_SERVER_SET(s, sl, cfg_server_set_tls, "tls");
XML_SERVER_SET(s, sl, cfg_server_set_tls_cipher_suite, "tls_cipher_suite");
XML_SERVER_SET(s, sl, cfg_server_set_ca_dir, "ca_dir");
XML_SERVER_SET(s, sl, cfg_server_set_ca_file, "ca_file");
XML_SERVER_SET(s, sl, cfg_server_set_client_cert, "client_cert");
}
if (0 > cfg_server_validate(s, &errstr)) {
log_error("%s[%ld]: server (%s): %s", doc->name, line_no,
cfg_server_get_name(s), errstr);
return (-1);
}
return (0);
}
static int
_cfg_xmlfile_parse_servers(xmlDocPtr doc, xmlNodePtr cur)
{
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
if (0 == xmlStrcasecmp(cur->name, XML_CHAR("server")) &&
0 > _cfg_xmlfile_parse_server(doc, cur))
return (-1);
}
return (0);
}
#define XML_STREAM_SET(c, l, f, e) do { \
if (0 == xmlStrcasecmp(cur->name, XML_CHAR((e)))) { \
xmlChar *val; \
const char *err_str; \
int error = 0; \
\
val = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); \
if (0 > (f)((c), (l), STD_CHAR(val), &err_str)) { \
log_error("%s[%ld]: stream (%s): %s: %s", \
doc->name, xmlGetLineNo(cur), \
cfg_stream_get_name((c)), (e), err_str); \
error = 1; \
} \
xmlFree(val); \
if (error) \
return (-1); \
} \
} while (0)
static int
_cfg_xmlfile_parse_stream(xmlDocPtr doc, xmlNodePtr cur)
{
cfg_stream_list_t sl;
cfg_stream_t s;
const char *errstr;
long int line_no = xmlGetLineNo(cur);
sl = cfg_get_streams();
s = cfg_stream_list_get(sl, CFG_DEFAULT);
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_STREAM_SET(s, sl, cfg_stream_set_name, "name");
XML_STREAM_SET(s, sl, cfg_stream_set_mountpoint, "mountpoint");
XML_STREAM_SET(s, sl, cfg_stream_set_server, "server");
XML_STREAM_SET(s, sl, cfg_stream_set_public, "public");
XML_STREAM_SET(s, sl, cfg_stream_set_format, "format");
XML_STREAM_SET(s, sl, cfg_stream_set_encoder, "encoder");
XML_STREAM_SET(s, sl, cfg_stream_set_stream_url, "stream_url");
XML_STREAM_SET(s, sl, cfg_stream_set_stream_genre, "stream_genre");
XML_STREAM_SET(s, sl, cfg_stream_set_stream_description, "stream_description");
XML_STREAM_SET(s, sl, cfg_stream_set_stream_quality, "stream_quality");
XML_STREAM_SET(s, sl, cfg_stream_set_stream_bitrate, "stream_bitrate");
XML_STREAM_SET(s, sl, cfg_stream_set_stream_samplerate, "stream_samplerate");
XML_STREAM_SET(s, sl, cfg_stream_set_stream_channels, "stream_channels");
}
if (0 > cfg_stream_validate(s, &errstr)) {
log_error("%s[%ld]: stream (%s): %s", doc->name, line_no,
cfg_stream_get_name(s), errstr);
return (-1);
}
return (0);
}
static int
_cfg_xmlfile_parse_streams(xmlDocPtr doc, xmlNodePtr cur)
{
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
if (0 == xmlStrcasecmp(cur->name, XML_CHAR("stream")) &&
0 > _cfg_xmlfile_parse_stream(doc, cur))
return (-1);
}
return (0);
}
#define XML_STRCONFIG(s, f, e) do { \
if (0 == xmlStrcasecmp(cur->name, XML_CHAR((e)))) { \
xmlChar *val; \
@ -54,59 +187,6 @@ static int _cfg_xmlfile_parse_encoders(xmlDocPtr, xmlNodePtr);
} \
} while (0)
static int
_cfg_xmlfile_parse_server(xmlDocPtr doc, xmlNodePtr cur)
{
int error = 0;
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_STRCONFIG("server", cfg_set_server_protocol, "protocol");
XML_STRCONFIG("server", cfg_set_server_hostname, "hostname");
XML_STRCONFIG("server", cfg_set_server_port, "port");
XML_STRCONFIG("server", cfg_set_server_user, "user");
XML_STRCONFIG("server", cfg_set_server_password, "password");
XML_STRCONFIG("server", cfg_set_server_tls, "tls");
XML_STRCONFIG("server", cfg_set_server_tls_cipher_suite,
"tls_cipher_suite");
XML_STRCONFIG("server", cfg_set_server_ca_dir, "ca_dir");
XML_STRCONFIG("server", cfg_set_server_ca_file, "ca_file");
XML_STRCONFIG("server", cfg_set_server_client_cert, "client_cert");
XML_STRCONFIG("server", cfg_set_server_reconnect_attempts,
"reconnect_attempts");
}
if (error)
return (-1);
return (0);
}
static int
_cfg_xmlfile_parse_stream(xmlDocPtr doc, xmlNodePtr cur)
{
int error = 0;
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_STRCONFIG("stream", cfg_set_stream_mountpoint, "mountpoint");
XML_STRCONFIG("stream", cfg_set_stream_name, "name");
XML_STRCONFIG("stream", cfg_set_stream_url, "url");
XML_STRCONFIG("stream", cfg_set_stream_genre, "genre");
XML_STRCONFIG("stream", cfg_set_stream_description, "description");
XML_STRCONFIG("stream", cfg_set_stream_quality, "quality");
XML_STRCONFIG("stream", cfg_set_stream_bitrate, "bitrate");
XML_STRCONFIG("stream", cfg_set_stream_samplerate, "samplerate");
XML_STRCONFIG("stream", cfg_set_stream_channels, "channels");
XML_STRCONFIG("stream", cfg_set_stream_server_public, "server_public");
XML_STRCONFIG("stream", cfg_set_stream_format, "format");
XML_STRCONFIG("stream", cfg_set_stream_encoder, "encoder");
}
if (error)
return (-1);
return (0);
}
static int
_cfg_xmlfile_parse_media(xmlDocPtr doc, xmlNodePtr cur)
{
@ -146,15 +226,15 @@ _cfg_xmlfile_parse_metadata(xmlDocPtr doc, xmlNodePtr cur)
return (0);
}
#define XML_DECODER_SET(c, f, e) do { \
#define XML_DECODER_SET(c, l, f, e) do { \
if (0 == xmlStrcasecmp(cur->name, XML_CHAR((e)))) { \
xmlChar *val; \
const char *err_str; \
int error = 0; \
\
val = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); \
if (0 > (f)((c), STD_CHAR(val), &err_str)) { \
log_error("%s[%ld]: decoder: %s: %s: %s", \
if (0 > (f)((c), (l), STD_CHAR(val), &err_str)) { \
log_error("%s[%ld]: decoder (%s): %s: %s", \
doc->name, xmlGetLineNo(cur), \
cfg_decoder_get_name((c)), (e), err_str); \
error = 1; \
@ -168,22 +248,23 @@ _cfg_xmlfile_parse_metadata(xmlDocPtr doc, xmlNodePtr cur)
static int
_cfg_xmlfile_parse_decoder(xmlDocPtr doc, xmlNodePtr cur)
{
cfg_decoder_t d;
char d_id[11];
const char *errstr;
long int line_no = xmlGetLineNo(cur);
cfg_decoder_list_t dl;
cfg_decoder_t d;
const char *errstr;
long int line_no = xmlGetLineNo(cur);
(void)snprintf(d_id, sizeof(d_id), "%u", ++decoder_id);
d = cfg_decoder_get(d_id);
dl = cfg_get_decoders();
d = cfg_decoder_list_get(dl, CFG_DEFAULT);
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_DECODER_SET(d, cfg_decoder_set_name, "name");
XML_DECODER_SET(d, cfg_decoder_set_program, "program");
XML_DECODER_SET(d, cfg_decoder_add_match, "file_ext");
XML_DECODER_SET(d, dl, cfg_decoder_set_name, "name");
XML_DECODER_SET(d, dl, cfg_decoder_set_program, "program");
XML_DECODER_SET(d, dl, cfg_decoder_add_match, "file_ext");
}
if (0 > cfg_decoder_validate(d, &errstr)) {
log_error("%s[%ld]: decoder: %s", doc->name, line_no, errstr);
log_error("%s[%ld]: decoder (%s): %s", doc->name, line_no,
cfg_decoder_get_name(d), errstr);
return (-1);
}
@ -202,15 +283,15 @@ _cfg_xmlfile_parse_decoders(xmlDocPtr doc, xmlNodePtr cur)
return (0);
}
#define XML_ENCODER_SET(c, f, e) do { \
#define XML_ENCODER_SET(c, l, f, e) do { \
if (0 == xmlStrcasecmp(cur->name, XML_CHAR((e)))) { \
xmlChar *val; \
const char *err_str; \
int error = 0; \
\
val = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); \
if (0 > (f)((c), STD_CHAR(val), &err_str)) { \
log_error("%s[%ld]: encoder: %s: %s: %s", \
if (0 > (f)((c), (l), STD_CHAR(val), &err_str)) { \
log_error("%s[%ld]: encoder (%s): %s: %s", \
doc->name, xmlGetLineNo(cur), \
cfg_encoder_get_name((c)), (e), err_str); \
error = 1; \
@ -224,22 +305,23 @@ _cfg_xmlfile_parse_decoders(xmlDocPtr doc, xmlNodePtr cur)
static int
_cfg_xmlfile_parse_encoder(xmlDocPtr doc, xmlNodePtr cur)
{
cfg_encoder_t e;
char e_id[11];
const char *errstr;
long int line_no = xmlGetLineNo(cur);
cfg_encoder_list_t el;
cfg_encoder_t e;
const char *errstr;
long int line_no = xmlGetLineNo(cur);
(void)snprintf(e_id, sizeof(e_id), "%u", ++encoder_id);
e = cfg_encoder_get(e_id);
el = cfg_get_encoders();
e = cfg_encoder_list_get(el, CFG_DEFAULT);
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_ENCODER_SET(e, cfg_encoder_set_name, "name");
XML_ENCODER_SET(e, cfg_encoder_set_format_str, "format");
XML_ENCODER_SET(e, cfg_encoder_set_program, "program");
XML_ENCODER_SET(e, el, cfg_encoder_set_name, "name");
XML_ENCODER_SET(e, el, cfg_encoder_set_format_str, "format");
XML_ENCODER_SET(e, el, cfg_encoder_set_program, "program");
}
if (0 > cfg_encoder_validate(e, &errstr)) {
log_error("%s[%ld]: encoder: %s", doc->name, line_no, errstr);
log_error("%s[%ld]: encoder (%s): %s", doc->name, line_no,
cfg_encoder_get_name(e), errstr);
return (-1);
}
@ -262,31 +344,38 @@ _cfg_xmlfile_parse_encoders(xmlDocPtr doc, xmlNodePtr cur)
* XML configuration file structure:
*
* ezstream
* server
* protocol
* hostname
* port
* user
* password
* tls
* tls_cipher_suite
* ca_dir
* ca_file
* client_cert
* reconnect_attempts
* stream
* mountpoint
* name
* url
* genre
* description
* quality
* bitrate
* samplerate
* channels
* server_public
* format
* encoder
* servers
* server
* name
* protocol
* hostname
* port
* user
* password
* reconnect_attempts
* tls
* tls_cipher_suite
* ca_dir
* ca_file
* client_cert
* ...
* streams
* stream
* name
* mountpoint
* public
* server
* format
* encoder
* stream_name
* stream_url
* stream_genre
* stream_description
* stream_quality
* stream_bitrate
* stream_samplerate
* stream_channels
* ...
* media
* type
* filename
@ -342,13 +431,13 @@ cfg_xmlfile_parse(const char *config_file)
}
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
if (0 == xmlStrcasecmp(cur->name, XML_CHAR("server"))) {
if (0 > _cfg_xmlfile_parse_server(doc, cur))
if (0 == xmlStrcasecmp(cur->name, XML_CHAR("servers"))) {
if (0 > _cfg_xmlfile_parse_servers(doc, cur))
error = 1;
continue;
}
if (0 == xmlStrcasecmp(cur->name, XML_CHAR("stream"))) {
if (0 > _cfg_xmlfile_parse_stream(doc, cur))
if (0 == xmlStrcasecmp(cur->name, XML_CHAR("streams"))) {
if (0 > _cfg_xmlfile_parse_streams(doc, cur))
error = 1;
continue;
}

View File

@ -38,6 +38,7 @@
#define STREAM_SERVERR 3
#define STREAM_UPDMDATA 4
stream_t main_stream;
playlist_t playlist;
int playlistMode;
unsigned int resource_errors;
@ -53,8 +54,10 @@ volatile sig_atomic_t queryMetadata;
volatile sig_atomic_t quit;
void sig_handler(int);
char * _build_reencode_cmd(const char *, const char *, mdata_t);
FILE * openResource(stream_t, const char *, int *, mdata_t *,
static char * _build_reencode_cmd(const char *, const char *, cfg_stream_t,
mdata_t);
static FILE * openResource(stream_t, const char *, int *, mdata_t *,
int *, long *);
int reconnect(stream_t);
const char * getTimeString(long);
@ -87,9 +90,9 @@ sig_handler(int sig)
}
}
char *
static char *
_build_reencode_cmd(const char *extension, const char *filename,
mdata_t md)
cfg_stream_t cfg_stream, mdata_t md)
{
cfg_decoder_t decoder;
cfg_encoder_t encoder;
@ -102,16 +105,17 @@ _build_reencode_cmd(const char *extension, const char *filename,
char *cmd_str;
size_t cmd_str_size;
decoder = cfg_decoder_find(extension);
decoder = cfg_decoder_list_findext(cfg_get_decoders(), extension);
if (!decoder) {
log_error("cannot decode: %s: unsupported file extension %s",
filename, extension);
return (NULL);
}
encoder = cfg_encoder_get(cfg_get_stream_encoder());
encoder = cfg_encoder_list_get(cfg_get_encoders(),
cfg_stream_get_encoder(cfg_stream));
if (!encoder) {
log_error("cannot encode: %s: unknown encoder",
cfg_get_stream_encoder());
cfg_stream_get_encoder(cfg_stream));
return (NULL);
}
@ -201,7 +205,7 @@ _build_reencode_cmd(const char *extension, const char *filename,
return (cmd_str);
}
FILE *
static FILE *
openResource(stream_t stream, const char *filename, int *popenFlag,
mdata_t *md_p, int *isStdin, long *songLen)
{
@ -210,6 +214,7 @@ openResource(stream_t stream, const char *filename, int *popenFlag,
char *p = NULL;
char *pCommandString = NULL;
mdata_t md;
cfg_stream_t cfg_stream = stream_get_cfg_stream(stream);
if (md_p != NULL)
*md_p = NULL;
@ -268,11 +273,11 @@ openResource(stream_t stream, const char *filename, int *popenFlag,
*songLen = mdata_get_length(md);
*popenFlag = 0;
if (cfg_get_stream_encoder()) {
if (cfg_stream_get_encoder(cfg_stream)) {
int stderr_fd = -1;
pCommandString = _build_reencode_cmd(extension, filename,
md);
cfg_stream, md);
if (md_p != NULL)
*md_p = md;
else
@ -338,26 +343,27 @@ int
reconnect(stream_t stream)
{
unsigned int i;
cfg_server_t cfg_server = stream_get_cfg_server(stream);
i = 0;
while (++i) {
if (cfg_get_server_reconnect_attempts() > 0)
if (cfg_server_get_reconnect_attempts(cfg_server) > 0)
log_notice("reconnect: %s: attempt #%u/%u ...",
cfg_get_server_hostname(), i,
cfg_get_server_reconnect_attempts());
cfg_server_get_hostname(cfg_server), i,
cfg_server_get_reconnect_attempts(cfg_server));
else
log_notice("reconnect: %s: attempt #%u ...",
cfg_get_server_hostname(), i);
cfg_server_get_hostname(cfg_server), i);
stream_disconnect(stream);
if (0 == stream_connect(stream)) {
log_notice("reconnect: %s: success",
cfg_get_server_hostname());
cfg_server_get_hostname(cfg_server));
return (0);
}
if (cfg_get_server_reconnect_attempts() > 0 &&
i >= cfg_get_server_reconnect_attempts())
if (cfg_server_get_reconnect_attempts(cfg_server) > 0 &&
i >= cfg_server_get_reconnect_attempts(cfg_server))
break;
if (quit)
@ -400,6 +406,7 @@ sendStream(stream_t stream, FILE *filepstream, const char *fileName,
double kbps = -1.0;
struct timespec timeStamp, *startTime = tv;
struct timespec callTime, currentTime;
cfg_server_t cfg_server = stream_get_cfg_server(stream);
clock_gettime(CLOCK_MONOTONIC, &callTime);
@ -411,7 +418,7 @@ sendStream(stream_t stream, FILE *filepstream, const char *fileName,
while ((bytes_read = fread(buff, 1, sizeof(buff), filepstream)) > 0) {
if (!stream_get_connected(stream)) {
log_warning("%s: connection lost",
cfg_get_server_hostname());
cfg_server_get_hostname(cfg_server));
if (0 > reconnect(stream)) {
ret = STREAM_SERVERR;
break;
@ -521,6 +528,7 @@ streamFile(stream_t stream, const char *fileName)
long songLen;
mdata_t md = NULL;
struct timespec startTime;
cfg_stream_t cfg_stream = stream_get_cfg_stream(stream);
if ((filepstream = openResource(stream, fileName, &popenFlag, &md, &isStdin, &songLen))
== NULL) {
@ -546,7 +554,7 @@ streamFile(stream_t stream, const char *fileName)
xfree(metaData);
/* MP3 streams are special, so set the metadata explicitly: */
if (CFG_STREAM_MP3 == cfg_get_stream_format())
if (CFG_STREAM_MP3 == cfg_stream_get_format(cfg_stream))
stream_set_metadata(stream, md, NULL);
mdata_destroy(&md);
@ -686,10 +694,11 @@ streamPlaylist(stream_t stream)
int
ez_shutdown(int exitval)
{
if (main_stream)
stream_destroy(&main_stream);
stream_exit();
playlist_exit();
cfg_encoder_exit();
cfg_decoder_exit();
log_exit();
cfg_exit();
@ -701,18 +710,17 @@ main(int argc, char *argv[])
{
int ret, cont;
const char *errstr;
stream_t stream;
extern char *optarg;
extern int optind;
struct sigaction act;
unsigned int i;
cfg_server_t cfg_server;
cfg_stream_t cfg_stream;
ret = 1;
if (0 > cfg_init() ||
0 > cmdline_parse(argc, argv, &ret) ||
0 > log_init() ||
0 > cfg_decoder_init() ||
0 > cfg_encoder_init() ||
0 > playlist_init() ||
0 > cfg_file_reload() ||
0 > stream_init())
@ -723,9 +731,11 @@ main(int argc, char *argv[])
return (ez_shutdown(2));
}
stream = stream_get(STREAM_DEFAULT);
if (0 > stream_setup(stream))
main_stream = stream_create(CFG_DEFAULT);
if (0 > stream_configure(main_stream))
return (ez_shutdown(1));
cfg_server = stream_get_cfg_server(main_stream);
cfg_stream = stream_get_cfg_stream(main_stream);
memset(&act, 0, sizeof(act));
act.sa_handler = sig_handler;
@ -754,13 +764,15 @@ main(int argc, char *argv[])
if (0 > util_write_pid_file(cfg_get_program_pid_file()))
log_syserr(WARNING, errno, cfg_get_program_pid_file());
if (0 > stream_connect(stream)) {
if (0 > stream_connect(main_stream)) {
log_error("initial server connection failed");
return (ez_shutdown(1));
}
log_notice("connected: %s://%s:%u%s",
cfg_get_server_protocol_str(), cfg_get_server_hostname(),
cfg_get_server_port(), cfg_get_stream_mountpoint());
cfg_server_get_protocol_str(cfg_server),
cfg_server_get_hostname(cfg_server),
cfg_server_get_port(cfg_server),
cfg_stream_get_mountpoint(cfg_stream));
if (CFG_MEDIA_PROGRAM == cfg_get_media_type() ||
CFG_MEDIA_PLAYLIST == cfg_get_media_type() ||
@ -773,9 +785,9 @@ main(int argc, char *argv[])
do {
if (playlistMode) {
cont = streamPlaylist(stream);
cont = streamPlaylist(main_stream);
} else {
cont = streamFile(stream,
cont = streamFile(main_stream,
cfg_get_media_filename());
}
if (quit)
@ -784,7 +796,7 @@ main(int argc, char *argv[])
break;
} while (cont);
stream_disconnect(stream);
stream_disconnect(main_stream);
if (quit) {
if (cfg_get_program_quiet_stderr() &&

View File

@ -18,8 +18,6 @@
# include "config.h"
#endif /* HAVE_CONFIG_H */
#include <sys/queue.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@ -36,22 +34,19 @@
#include "xalloc.h"
struct stream {
TAILQ_ENTRY(stream) entry;
char *name;
shout_t *shout;
char *name;
shout_t *shout;
};
TAILQ_HEAD(stream_list, stream);
static struct stream_list streams;
static int _stream_cfg_server(struct stream *);
static int _stream_cfg_tls(struct stream *);
static int _stream_cfg_stream(struct stream *);
static int _stream_cfg_server(struct stream *, cfg_server_t);
static int _stream_cfg_tls(struct stream *, cfg_server_t);
static int _stream_cfg_stream(struct stream *, cfg_stream_t);
static void _stream_reset(struct stream *);
static int
_stream_cfg_server(struct stream *s)
_stream_cfg_server(struct stream *s, cfg_server_t cfg_server)
{
switch (cfg_get_server_protocol()) {
switch (cfg_server_get_protocol(cfg_server)) {
case CFG_PROTO_HTTP:
if (SHOUTERR_SUCCESS !=
shout_set_protocol(s->shout, SHOUT_PROTOCOL_HTTP)) {
@ -62,29 +57,29 @@ _stream_cfg_server(struct stream *s)
break;
default:
log_error("%s: protocol: unsupported: %s",
s->name, cfg_get_server_protocol_str());
s->name, cfg_server_get_protocol_str(cfg_server));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_host(s->shout, cfg_get_server_hostname())) {
shout_set_host(s->shout, cfg_server_get_hostname(cfg_server))) {
log_error("%s: hostname: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_port(s->shout, (unsigned short)cfg_get_server_port())) {
shout_set_port(s->shout, (unsigned short)cfg_server_get_port(cfg_server))) {
log_error("%s: port: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_user(s->shout, cfg_get_server_user())) {
shout_set_user(s->shout, cfg_server_get_user(cfg_server))) {
log_error("%s: user: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_password(s->shout, cfg_get_server_password())) {
shout_set_password(s->shout, cfg_server_get_password(cfg_server))) {
log_error("%s: password: %s",
s->name, shout_get_error(s->shout));
return (-1);
@ -94,12 +89,12 @@ _stream_cfg_server(struct stream *s)
}
static int
_stream_cfg_tls(struct stream *s)
_stream_cfg_tls(struct stream *s, cfg_server_t cfg_server)
{
#ifdef SHOUT_TLS_AUTO
int tls_req;
switch (cfg_get_server_tls()) {
switch (cfg_server_get_tls(cfg_server)) {
case CFG_TLS_NONE:
tls_req = SHOUT_TLS_DISABLED;
break;
@ -117,37 +112,37 @@ _stream_cfg_tls(struct stream *s)
log_error("%s: tls: %s", s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_server_ca_dir() &&
if (cfg_server_get_ca_dir(cfg_server) &&
SHOUTERR_SUCCESS !=
shout_set_ca_directory(s->shout, cfg_get_server_ca_dir())) {
shout_set_ca_directory(s->shout, cfg_server_get_ca_dir(cfg_server))) {
log_error("%s: ca_dir: %s", s->name,
shout_get_error(s->shout));
return (-1);
}
if (cfg_get_server_ca_file() &&
if (cfg_server_get_ca_file(cfg_server) &&
SHOUTERR_SUCCESS !=
shout_set_ca_file(s->shout, cfg_get_server_ca_file())) {
shout_set_ca_file(s->shout, cfg_server_get_ca_file(cfg_server))) {
log_error("%s: ca_file: %s", s->name,
shout_get_error(s->shout));
return (-1);
}
if (cfg_get_server_tls_cipher_suite() &&
if (cfg_server_get_tls_cipher_suite(cfg_server) &&
SHOUTERR_SUCCESS !=
shout_set_allowed_ciphers(s->shout, cfg_get_server_tls_cipher_suite())) {
shout_set_allowed_ciphers(s->shout, cfg_server_get_tls_cipher_suite(cfg_server))) {
log_error("%s: tls_cipher_suite: %s", s->name,
shout_get_error(s->shout));
return (-1);
}
if (cfg_get_server_client_cert() &&
if (cfg_server_get_client_cert(cfg_server) &&
SHOUTERR_SUCCESS !=
shout_set_client_certificate(s->shout, cfg_get_server_client_cert())) {
shout_set_client_certificate(s->shout, cfg_server_get_client_cert(cfg_server))) {
log_error("%s: client_cert: %s", s->name,
shout_get_error(s->shout));
return (-1);
}
#else /* SHOUT_TLS_AUTO */
# warning "libshout library does not support TLS"
switch (cfg_get_server_tls()) {
switch (cfg_server_get_tls(cfg_server)) {
case CFG_TLS_MAY:
log_warning("%s: TLS optional but not supported by libshout",
s->name);
@ -165,15 +160,15 @@ _stream_cfg_tls(struct stream *s)
}
static int
_stream_cfg_stream(struct stream *s)
_stream_cfg_stream(struct stream *s, cfg_stream_t cfg_stream)
{
if (SHOUTERR_SUCCESS !=
shout_set_mount(s->shout, cfg_get_stream_mountpoint())) {
shout_set_mount(s->shout, cfg_stream_get_mountpoint(cfg_stream))) {
log_error("%s: mountpoint: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
switch (cfg_get_stream_format()) {
switch (cfg_stream_get_format(cfg_stream)) {
case CFG_STREAM_VORBIS:
case CFG_STREAM_THEORA:
if (SHOUTERR_SUCCESS !=
@ -193,79 +188,91 @@ _stream_cfg_stream(struct stream *s)
break;
default:
log_error("%s: format: unsupported: %s",
s->name, cfg_get_stream_format_str());
s->name, cfg_stream_get_format_str(cfg_stream));
return (-1);
}
if (cfg_get_stream_name() &&
if (SHOUTERR_SUCCESS !=
shout_set_public(s->shout, (unsigned int)cfg_stream_get_public(cfg_stream))) {
log_error("%s: public: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_stream_get_stream_name(cfg_stream) &&
SHOUTERR_SUCCESS !=
shout_set_name(s->shout, cfg_get_stream_name())) {
shout_set_name(s->shout, cfg_stream_get_stream_name(cfg_stream))) {
log_error("%s: name: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_url() &&
if (cfg_stream_get_stream_url(cfg_stream) &&
SHOUTERR_SUCCESS !=
shout_set_url(s->shout, cfg_get_stream_url())) {
shout_set_url(s->shout, cfg_stream_get_stream_url(cfg_stream))) {
log_error("%s: url: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_genre() &&
if (cfg_stream_get_stream_genre(cfg_stream) &&
SHOUTERR_SUCCESS !=
shout_set_genre(s->shout, cfg_get_stream_genre())) {
shout_set_genre(s->shout, cfg_stream_get_stream_genre(cfg_stream))) {
log_error("%s: genre: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_description() &&
if (cfg_stream_get_stream_description(cfg_stream) &&
SHOUTERR_SUCCESS !=
shout_set_description(s->shout, cfg_get_stream_description())) {
shout_set_description(s->shout, cfg_stream_get_stream_description(cfg_stream))) {
log_error("%s: description: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_quality() &&
if (cfg_stream_get_stream_quality(cfg_stream) &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(s->shout, SHOUT_AI_QUALITY, cfg_get_stream_quality())) {
shout_set_audio_info(s->shout, SHOUT_AI_QUALITY, cfg_stream_get_stream_quality(cfg_stream))) {
log_error("%s: ai_quality: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_bitrate() &&
if (cfg_stream_get_stream_bitrate(cfg_stream) &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(s->shout, SHOUT_AI_BITRATE, cfg_get_stream_bitrate())) {
shout_set_audio_info(s->shout, SHOUT_AI_BITRATE, cfg_stream_get_stream_bitrate(cfg_stream))) {
log_error("%s: ai_bitrate: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_samplerate() &&
if (cfg_stream_get_stream_samplerate(cfg_stream) &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(s->shout, SHOUT_AI_SAMPLERATE, cfg_get_stream_samplerate())) {
shout_set_audio_info(s->shout, SHOUT_AI_SAMPLERATE, cfg_stream_get_stream_samplerate(cfg_stream))) {
log_error("%s: ai_samplerate: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (cfg_get_stream_channels() &&
if (cfg_stream_get_stream_channels(cfg_stream) &&
SHOUTERR_SUCCESS !=
shout_set_audio_info(s->shout, SHOUT_AI_CHANNELS, cfg_get_stream_channels())) {
shout_set_audio_info(s->shout, SHOUT_AI_CHANNELS, cfg_stream_get_stream_channels(cfg_stream))) {
log_error("%s: ai_channels: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
if (SHOUTERR_SUCCESS !=
shout_set_public(s->shout, (unsigned int)cfg_get_stream_server_public())) {
log_error("%s: public: %s",
s->name, shout_get_error(s->shout));
return (-1);
}
return (0);
}
void
_stream_reset(struct stream *s)
{
if (!s->shout)
return;
shout_free(s->shout);
s->shout = shout_new();
if (NULL == s->shout) {
log_syserr(ALERT, ENOMEM, "shout_new");
exit(1);
}
}
int
stream_init(void)
{
TAILQ_INIT(&streams);
shout_init();
return (0);
}
@ -273,53 +280,66 @@ stream_init(void)
void
stream_exit(void)
{
struct stream *e;
while (NULL != (e = TAILQ_FIRST(&streams))) {
TAILQ_REMOVE(&streams, e, entry);
xfree(e->name);
shout_free(e->shout);
xfree(e);
}
shout_shutdown();
}
struct stream *
stream_get(const char *name)
stream_create(const char *name)
{
struct stream *e;
struct stream *s;
TAILQ_FOREACH(e, &streams, entry) {
if (0 == strcasecmp(e->name, name))
return (e);
}
e = xcalloc(1UL, sizeof(*e));
e->name = xstrdup(name);
e->shout = shout_new();
if (NULL == e->shout) {
s = xcalloc(1UL, sizeof(*s));
s->name = xstrdup(name);
s->shout = shout_new();
if (NULL == s->shout) {
log_syserr(ALERT, ENOMEM, "shout_new");
exit(1);
}
TAILQ_INSERT_TAIL(&streams, e, entry);
return (s);
}
return (e);
void
stream_destroy(struct stream **s_p)
{
struct stream *s = *s_p;
shout_free(s->shout);
xfree(s->name);
xfree(s);
*s_p = NULL;
}
int
stream_setup(struct stream *s)
stream_configure(struct stream *s)
{
if (0 != _stream_cfg_server(s) ||
0 != _stream_cfg_tls(s) ||
0 != _stream_cfg_stream(s)) {
/* Reset handle on error */
shout_free(s->shout);
s->shout = shout_new();
if (NULL == s->shout) {
log_syserr(ALERT, ENOMEM, "shout_new");
exit(1);
}
cfg_stream_list_t streams;
cfg_server_list_t servers;
cfg_stream_t cfg_stream;
cfg_server_t cfg_server;
const char *server;
streams = cfg_get_streams();
cfg_stream = cfg_stream_list_find(streams, s->name);
if (!cfg_stream) {
log_error("%s: stream has no configuration", s->name);
return (-1);
}
servers = cfg_get_servers();
server = cfg_stream_get_server(cfg_stream);
if (!server)
server = CFG_DEFAULT;
cfg_server = cfg_server_list_find(servers, server);
if (!cfg_server) {
log_error("%s: stream server has no configuration: %s",
s->name, cfg_stream_get_server(cfg_stream));
return (-1);
}
if (0 != _stream_cfg_server(s, cfg_server) ||
0 != _stream_cfg_tls(s, cfg_server) ||
0 != _stream_cfg_stream(s, cfg_stream)) {
_stream_reset(s);
return (-1);
}
@ -421,6 +441,25 @@ stream_get_connected(struct stream *s)
return (shout_get_connected(s->shout) == SHOUTERR_CONNECTED ? 1 : 0);
}
cfg_stream_t
stream_get_cfg_stream(struct stream *s)
{
return (cfg_stream_list_find(cfg_get_streams(), s->name));
}
cfg_server_t
stream_get_cfg_server(struct stream *s)
{
cfg_stream_t cfg_stream;
const char *server;
cfg_stream = cfg_stream_list_get(cfg_get_streams(), s->name);
server = cfg_stream_get_server(cfg_stream);
if (!server)
server = CFG_DEFAULT;
return (cfg_server_list_get(cfg_get_servers(), server));
}
int
stream_connect(struct stream *s)
{

View File

@ -19,20 +19,28 @@
#include <shout/shout.h>
#include "cfg.h"
#include "mdata.h"
#define STREAM_DEFAULT "default"
typedef struct stream * stream_t;
typedef struct stream * stream_t;
int stream_init(void);
void stream_exit(void);
stream_t
stream_get(const char *);
int stream_setup(stream_t);
stream_create(const char *);
void stream_destroy(stream_t *);
int stream_configure(stream_t);
int stream_set_metadata(stream_t, mdata_t, char **);
const char *
stream_get_name(stream_t);
int stream_get_connected(stream_t);
cfg_stream_t
stream_get_cfg_stream(stream_t);
cfg_server_t
stream_get_cfg_server(stream_t);
int stream_connect(stream_t);
void stream_disconnect(stream_t);

View File

@ -2,6 +2,10 @@ AUTOMAKE_OPTIONS = 1.10 foreign subdir-objects
TESTS = \
check_cfg \
check_cfg_decoder \
check_cfg_encoder \
check_cfg_server \
check_cfg_stream \
check_cfg_xmlfile \
check_cmdline \
check_log \
@ -12,48 +16,57 @@ TESTS = \
check_xalloc
check_PROGRAMS = $(TESTS)
check_cfg_SOURCES = \
check_cfg.c
noinst_HEADERS = check_cfg.h
check_cfg_SOURCES = check_cfg.c
check_cfg_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_LDADD = $(check_cfg_DEPENDENCIES) @CHECK_LIBS@
check_cfg_xmlfile_SOURCES = \
check_cfg_xmlfile.c
check_cfg_decoder_SOURCES = check_cfg_decoder.c
check_cfg_decoder_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_decoder_LDADD = $(check_cfg_decoder_DEPENDENCIES) @CHECK_LIBS@
check_cfg_encoder_SOURCES = check_cfg_encoder.c
check_cfg_encoder_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_encoder_LDADD = $(check_cfg_encoder_DEPENDENCIES) @CHECK_LIBS@
check_cfg_server_SOURCES = check_cfg_server.c
check_cfg_server_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_server_LDADD = $(check_cfg_server_DEPENDENCIES) @CHECK_LIBS@
check_cfg_stream_SOURCES = check_cfg_stream.c
check_cfg_stream_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_stream_LDADD = $(check_cfg_stream_DEPENDENCIES) @CHECK_LIBS@
check_cfg_xmlfile_SOURCES = check_cfg_xmlfile.c
check_cfg_xmlfile_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_xmlfile_LDADD = $(check_cfg_xmlfile_DEPENDENCIES) @CHECK_LIBS@
check_cmdline_SOURCES = \
check_cmdline.c
check_cmdline_SOURCES = check_cmdline.c
check_cmdline_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cmdline_LDADD = $(check_cmdline_DEPENDENCIES) @CHECK_LIBS@
check_log_SOURCES = \
check_log.c
check_log_SOURCES = check_log.c
check_log_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_log_LDADD = $(check_log_DEPENDENCIES) @CHECK_LIBS@
check_mdata_SOURCES = \
check_mdata.c
check_mdata_SOURCES = check_mdata.c
check_mdata_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_mdata_LDADD = $(check_mdata_DEPENDENCIES) @CHECK_LIBS@
check_playlist_SOURCES = \
check_playlist.c
check_playlist_SOURCES = check_playlist.c
check_playlist_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_playlist_LDADD = $(check_playlist_DEPENDENCIES) @CHECK_LIBS@
check_stream_SOURCES = \
check_stream.c
check_stream_SOURCES = check_stream.c
check_stream_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_stream_LDADD = $(check_stream_DEPENDENCIES) @CHECK_LIBS@
check_util_SOURCES = \
check_util.c
check_util_SOURCES = check_util.c
check_util_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_util_LDADD = $(check_util_DEPENDENCIES) @CHECK_LIBS@
check_xalloc_SOURCES = \
check_xalloc.c
check_xalloc_SOURCES = check_xalloc.c
check_xalloc_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_xalloc_LDADD = $(check_xalloc_DEPENDENCIES) @CHECK_LIBS@
@ -73,6 +86,7 @@ EXTRA_DIST = \
config-bad2.xml \
config-bad3.xml \
config-bad4.xml \
config-bad5.xml \
play-bad.sh \
play-bad2.sh \
play-bad3.sh \

View File

@ -5,144 +5,21 @@
#include "cfg_private.h"
#include "log.h"
#define TEST_EMPTYSTR(s, g) do { \
const char *errstr; \
\
errstr = NULL; \
ck_assert_int_eq(s(NULL, &errstr), -1); \
ck_assert_str_eq(errstr, "empty"); \
\
errstr = NULL; \
ck_assert_int_eq(s("", &errstr), -1); \
ck_assert_str_eq(errstr, "empty"); \
\
} while (0)
#define TEST_XSTRDUP(s, g) do { \
TEST_EMPTYSTR(s, g); \
\
ck_assert_int_eq(s("check_cfg", NULL), 0); \
ck_assert_str_eq(g(), "check_cfg"); \
} while (0)
#define TEST_STRLCPY(s, g, l) do { \
char buf[l + 1]; \
const char *errstr2; \
\
TEST_XSTRDUP(s, g); \
\
errstr2 = NULL; \
memset(buf, 'A', sizeof(buf) - 1); \
buf[sizeof(buf) - 1] = '\0'; \
ck_assert_int_eq(s(buf, &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
} while (0)
#define TEST_BOOLEAN(s, g) do { \
const char *errstr2; \
\
TEST_EMPTYSTR(s, g); \
\
errstr2 = NULL; \
ck_assert_int_eq(s("BOGUS", &errstr2), -1); \
ck_assert_str_eq(errstr2, "invalid"); \
\
ck_assert_int_eq(s("tRuE", NULL), 0); \
ck_assert_int_eq(g(), 1); \
ck_assert_int_eq(s("YeS", NULL), 0); \
ck_assert_int_eq(g(), 1); \
ck_assert_int_eq(s("1", NULL), 0); \
ck_assert_int_eq(g(), 1); \
\
ck_assert_int_eq(s("FaLsE", NULL), 0); \
ck_assert_int_eq(g(), 0); \
ck_assert_int_eq(s("nO", NULL), 0); \
ck_assert_int_eq(g(), 0); \
ck_assert_int_eq(s("0", NULL), 0); \
ck_assert_int_eq(g(), 0); \
} while (0)
#define TEST_UINTNUM(s, g) do { \
const char *errstr2; \
\
TEST_EMPTYSTR(s, g); \
\
errstr2 = NULL; \
ck_assert_int_eq(s("-1", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
errstr2 = NULL; \
ck_assert_int_eq(s("4294967296", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
ck_assert_int_eq(s("20", NULL), 0); \
ck_assert_uint_eq(g(), 20); \
} while (0)
#define TEST_INTNUM(s, g) do { \
const char *errstr2; \
\
TEST_EMPTYSTR(s, g); \
\
errstr2 = NULL; \
ck_assert_int_eq(s("-2147483649", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
errstr2 = NULL; \
ck_assert_int_eq(s("2147483648", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
ck_assert_int_eq(s("20", NULL), 0); \
ck_assert_int_eq(g(), 20); \
} while (0)
#include "check_cfg.h"
Suite * cfg_suite(void);
void setup_checked(void);
void teardown_checked(void);
START_TEST(test_stash)
{
ck_assert_ptr_eq(cfg_get_stream_name(), NULL);
ck_assert_int_eq(cfg_set_stream_name("test_stash", NULL), 0);
ck_assert_str_eq(cfg_get_stream_name(), "test_stash");
cfg_save();
ck_assert_int_eq(cfg_set_stream_name("test_stash2", NULL), 0);
ck_assert_str_eq(cfg_get_stream_name(), "test_stash2");
cfg_restore();
ck_assert_str_eq(cfg_get_stream_name(), "test_stash");
cfg_restore();
ck_assert_str_eq(cfg_get_stream_name(), "test_stash");
cfg_save();
cfg_clear();
cfg_restore();
ck_assert_str_eq(cfg_get_stream_name(), "test_stash");
}
END_TEST
START_TEST(test_check)
{
const char *errstr = NULL;
ck_assert_int_eq(cfg_check(NULL), -1);
ck_assert_int_eq(cfg_check(&errstr), -1);
ck_assert_str_eq(errstr, "server hostname missing");
ck_assert_int_eq(cfg_set_server_hostname("localhost", NULL), 0);
ck_assert_int_eq(cfg_check(NULL), -1);
ck_assert_int_eq(cfg_check(&errstr), -1);
ck_assert_str_eq(errstr, "server password missing");
ck_assert_int_eq(cfg_set_server_password("secret", NULL), 0);
ck_assert_int_eq(cfg_check(NULL), -1);
ck_assert_int_eq(cfg_check(&errstr), -1);
ck_assert_str_eq(errstr, "media filename missing");
ck_assert_int_eq(cfg_set_media_type("stdin", NULL), 0);
ck_assert_int_eq(cfg_check(NULL), -1);
ck_assert_int_eq(cfg_check(&errstr), -1);
ck_assert_str_eq(errstr, "stream format missing or unsupported");
ck_assert_int_eq(cfg_set_stream_format(CFG_SFMT_VORBIS, NULL), 0);
ck_assert_int_eq(cfg_check(NULL), 0);
ck_assert_int_eq(cfg_set_media_type("autodetect", NULL), 0);
@ -244,208 +121,11 @@ START_TEST(test_program_verbosity)
}
END_TEST
START_TEST(test_server_protocol)
{
const char *errstr2;
TEST_EMPTYSTR(cfg_set_server_protocol, cfg_get_server_protocol);
errstr2 = NULL;
ck_assert_int_eq(cfg_set_server_protocol("invalid", &errstr2), -1);
ck_assert_ptr_ne(errstr2, NULL);
ck_assert_str_eq(errstr2, "unsupported");
ck_assert_int_eq(cfg_set_server_protocol("hTtP", NULL), 0);
ck_assert_int_eq(cfg_get_server_protocol(), CFG_PROTO_HTTP);
ck_assert_str_eq(cfg_get_server_protocol_str(), "http");
ck_assert_int_eq(cfg_set_server_protocol("HtTpS", NULL), 0);
ck_assert_int_eq(cfg_get_server_protocol(), CFG_PROTO_HTTPS);
ck_assert_str_eq(cfg_get_server_protocol_str(), "https");
}
END_TEST
START_TEST(test_server_hostname)
{
TEST_STRLCPY(cfg_set_server_hostname, cfg_get_server_hostname,
NI_MAXHOST);
}
END_TEST
START_TEST(test_server_port)
{
const char *errstr2;
ck_assert_uint_eq(cfg_get_server_port(), DEFAULT_PORT);
TEST_EMPTYSTR(cfg_set_server_port, cfg_get_server_port);
errstr2 = NULL;
ck_assert_int_eq(cfg_set_server_port("0", &errstr2), -1);
ck_assert_ptr_ne(errstr2, NULL);
errstr2 = NULL;
ck_assert_int_eq(cfg_set_server_port("65536", &errstr2), -1);
ck_assert_ptr_ne(errstr2, NULL);
ck_assert_int_eq(cfg_set_server_port("8008", NULL), 0);
ck_assert_uint_eq(cfg_get_server_port(), 8008);
}
END_TEST
START_TEST(test_server_user)
{
ck_assert_str_eq(cfg_get_server_user(), DEFAULT_USER);
TEST_STRLCPY(cfg_set_server_user, cfg_get_server_user, UCREDS_SIZE);
}
END_TEST
START_TEST(test_server_password)
{
TEST_STRLCPY(cfg_set_server_password, cfg_get_server_password,
UCREDS_SIZE);
}
END_TEST
START_TEST(test_server_ca_dir)
{
TEST_STRLCPY(cfg_set_server_ca_dir, cfg_get_server_ca_dir, PATH_MAX);
}
END_TEST
START_TEST(test_server_tls)
{
const char *errstr = NULL;
ck_assert_int_eq(cfg_set_server_tls("", &errstr), -1);
ck_assert_str_eq(errstr, "empty");
ck_assert_int_eq(cfg_set_server_tls("test", &errstr), -1);
ck_assert_str_eq(errstr, "invalid");
ck_assert_int_eq(cfg_get_server_tls(), CFG_TLS_MAY);
ck_assert_int_eq(cfg_set_server_tls("None", NULL), 0);
ck_assert_int_eq(cfg_get_server_tls(), CFG_TLS_NONE);
ck_assert_int_eq(cfg_set_server_tls("Required", NULL), 0);
ck_assert_int_eq(cfg_get_server_tls(), CFG_TLS_REQUIRED);
ck_assert_int_eq(cfg_set_server_tls("May", NULL), 0);
ck_assert_int_eq(cfg_get_server_tls(), CFG_TLS_MAY);
}
END_TEST
START_TEST(test_server_tls_cipher_suite)
{
TEST_STRLCPY(cfg_set_server_tls_cipher_suite,
cfg_get_server_tls_cipher_suite, CSUITE_SIZE);
}
END_TEST
START_TEST(test_server_ca_file)
{
TEST_STRLCPY(cfg_set_server_ca_file, cfg_get_server_ca_file, PATH_MAX);
}
END_TEST
START_TEST(test_server_client_cert)
{
TEST_STRLCPY(cfg_set_server_client_cert, cfg_get_server_client_cert,
PATH_MAX);
}
END_TEST
START_TEST(test_server_reconnect_attempts)
{
TEST_UINTNUM(cfg_set_server_reconnect_attempts,
cfg_get_server_reconnect_attempts);
}
END_TEST
START_TEST(test_stream_mountpoint)
{
TEST_XSTRDUP(cfg_set_stream_mountpoint, cfg_get_stream_mountpoint);
}
END_TEST
START_TEST(test_stream_name)
{
TEST_XSTRDUP(cfg_set_stream_name, cfg_get_stream_name);
}
END_TEST
START_TEST(test_stream_url)
{
TEST_XSTRDUP(cfg_set_stream_url, cfg_get_stream_url);
}
END_TEST
START_TEST(test_stream_genre)
{
TEST_XSTRDUP(cfg_set_stream_genre, cfg_get_stream_genre);
}
END_TEST
START_TEST(test_stream_description)
{
TEST_XSTRDUP(cfg_set_stream_description, cfg_get_stream_description);
}
END_TEST
START_TEST(test_stream_quality)
{
TEST_XSTRDUP(cfg_set_stream_quality, cfg_get_stream_quality);
}
END_TEST
START_TEST(test_stream_bitrate)
{
TEST_XSTRDUP(cfg_set_stream_bitrate, cfg_get_stream_bitrate);
}
END_TEST
START_TEST(test_stream_samplerate)
{
TEST_XSTRDUP(cfg_set_stream_samplerate, cfg_get_stream_samplerate);
}
END_TEST
START_TEST(test_stream_channels)
{
TEST_XSTRDUP(cfg_set_stream_channels, cfg_get_stream_channels);
}
END_TEST
START_TEST(test_stream_server_public)
{
TEST_BOOLEAN(cfg_set_stream_server_public,
cfg_get_stream_server_public);
}
END_TEST
START_TEST(test_stream_format)
{
const char *errstr2;
TEST_EMPTYSTR(cfg_set_stream_format, cfg_get_stream_format);
ck_assert_int_eq(cfg_set_stream_format("<something else>", &errstr2),
-1);
ck_assert_str_eq(errstr2, "unsupported stream format");
ck_assert_int_eq(cfg_set_stream_format(CFG_SFMT_VORBIS, NULL), 0);
ck_assert_int_eq(cfg_get_stream_format(), CFG_STREAM_VORBIS);
ck_assert_str_eq(cfg_get_stream_format_str(),
cfg_stream_fmt2str(CFG_STREAM_VORBIS));
}
END_TEST
START_TEST(test_stream_encoder)
{
TEST_XSTRDUP(cfg_set_stream_encoder, cfg_get_stream_encoder);
}
END_TEST
START_TEST(test_media_type)
{
const char *errstr2;
TEST_EMPTYSTR(cfg_set_media_type, cfg_get_media_type);
TEST_EMPTYSTR(cfg_set_media_type);
ck_assert_int_eq(cfg_set_media_type("<something else>", &errstr2), -1);
ck_assert_str_eq(errstr2, "unsupported");
@ -554,270 +234,19 @@ START_TEST(test_metadata_no_updates)
}
END_TEST
START_TEST(test_decoder_get)
{
cfg_decoder_t dec, dec2;
ck_assert_ptr_eq(cfg_decoder_get(NULL), NULL);
ck_assert_ptr_eq(cfg_decoder_get(""), NULL);
dec = cfg_decoder_get("TeSt");
dec2 = cfg_decoder_get("test");
ck_assert_ptr_eq(dec, dec2);
}
END_TEST
#define TEST_DEC_XSTRDUP(s, g) do { \
cfg_decoder_t dec = cfg_decoder_get(#s); \
const char *errstr; \
\
errstr = NULL; \
ck_assert_int_ne(s(dec, NULL, &errstr), 0); \
ck_assert_str_eq(errstr, "empty"); \
ck_assert_int_ne(s(dec, "", NULL), 0); \
\
ck_assert_int_eq(s(dec, "test", NULL), 0); \
ck_assert_str_eq(g(dec), "test"); \
\
ck_assert_int_eq(s(dec, #s, NULL), 0); \
ck_assert_str_eq(g(dec), #s); \
} while (0)
START_TEST(test_decoder_set_name)
{
TEST_DEC_XSTRDUP(cfg_decoder_set_name, cfg_decoder_get_name);
}
END_TEST
START_TEST(test_decoder_set_program)
{
TEST_DEC_XSTRDUP(cfg_decoder_set_program, cfg_decoder_get_program);
}
END_TEST
START_TEST(test_decoder_add_match)
{
cfg_decoder_t dec = cfg_decoder_get("test_decoder_add_match");
cfg_decoder_t dec2 = cfg_decoder_get("test_decoder_add_match_2");
const char *errstr;
errstr = NULL;
ck_assert_int_ne(cfg_decoder_add_match(dec, NULL, &errstr), 0);
ck_assert_str_eq(errstr, "empty");
ck_assert_int_ne(cfg_decoder_add_match(dec, "", NULL), 0);
ck_assert_int_eq(cfg_decoder_add_match(dec, ".test", NULL), 0);
ck_assert_int_eq(cfg_decoder_add_match(dec, ".test2", NULL), 0);
ck_assert_ptr_eq(cfg_decoder_find(".test"), dec);
ck_assert_ptr_eq(cfg_decoder_find(".test2"), dec);
ck_assert_int_eq(cfg_decoder_add_match(dec2, ".test2", NULL), 0);
ck_assert_ptr_eq(cfg_decoder_find(".test"), dec);
ck_assert_ptr_eq(cfg_decoder_find(".test2"), dec2);
}
END_TEST
START_TEST(test_decoder_validate)
{
cfg_decoder_t dec = cfg_decoder_get("test_decoder_validate");
const char *errstr;
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr, "program not set");
ck_assert_int_eq(cfg_decoder_set_program(dec, "test", NULL), 0);
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr, "no file extensions registered");
ck_assert_int_eq(cfg_decoder_add_match(dec, ".test", NULL), 0);
ck_assert_int_eq(cfg_decoder_set_program(dec, PLACEHOLDER_STRING,
NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"prohibited placeholder " PLACEHOLDER_STRING);
ck_assert_int_eq(cfg_decoder_set_program(dec,
PLACEHOLDER_TRACK PLACEHOLDER_TRACK, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_TRACK);
ck_assert_int_eq(cfg_decoder_set_program(dec,
PLACEHOLDER_METADATA PLACEHOLDER_METADATA, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_METADATA);
ck_assert_int_eq(cfg_decoder_set_program(dec,
PLACEHOLDER_ARTIST PLACEHOLDER_ARTIST, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_ARTIST);
ck_assert_int_eq(cfg_decoder_set_program(dec,
PLACEHOLDER_TITLE PLACEHOLDER_TITLE, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_TITLE);
ck_assert_int_eq(cfg_decoder_set_program(dec, "test", NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"missing placeholder " PLACEHOLDER_TRACK);
ck_assert_int_eq(cfg_decoder_set_program(dec, PLACEHOLDER_TRACK, NULL),
0);
ck_assert_int_eq(cfg_decoder_validate(dec, &errstr), 0);
}
END_TEST
START_TEST(test_encoder_get)
{
cfg_encoder_t enc, enc2;
ck_assert_ptr_eq(cfg_encoder_get(NULL), NULL);
ck_assert_ptr_eq(cfg_encoder_get(""), NULL);
enc = cfg_encoder_get("TeSt");
enc2 = cfg_encoder_get("test");
ck_assert_ptr_eq(enc, enc2);
}
END_TEST
#define TEST_ENC_XSTRDUP(s, g) do { \
cfg_encoder_t enc = cfg_encoder_get(#s); \
const char *errstr; \
\
errstr = NULL; \
ck_assert_int_ne(s(enc, NULL, &errstr), 0); \
ck_assert_str_eq(errstr, "empty"); \
ck_assert_int_ne(s(enc, "", NULL), 0); \
\
ck_assert_int_eq(s(enc, "test", NULL), 0); \
ck_assert_str_eq(g(enc), "test"); \
\
ck_assert_int_eq(s(enc, #s, NULL), 0); \
ck_assert_str_eq(g(enc), #s); \
} while (0)
START_TEST(test_encoder_set_name)
{
TEST_ENC_XSTRDUP(cfg_encoder_set_name, cfg_encoder_get_name);
}
END_TEST
START_TEST(test_encoder_set_program)
{
cfg_encoder_t enc = cfg_encoder_get("test_encoder_set_program");
ck_assert_int_eq(cfg_encoder_set_program(enc, NULL, NULL), 0);
ck_assert_ptr_eq(cfg_encoder_get_program(enc), NULL);
ck_assert_int_eq(cfg_encoder_set_program(enc, "test", NULL), 0);
ck_assert_str_eq(cfg_encoder_get_program(enc), "test");
}
END_TEST
START_TEST(test_encoder_set_format_str)
{
cfg_encoder_t enc = cfg_encoder_get("test_encoder_set_format_str");
const char *errstr;
errstr = NULL;
ck_assert_int_ne(cfg_encoder_set_format_str(enc, NULL, &errstr), 0);
ck_assert_str_eq(errstr, "empty");
ck_assert_int_ne(cfg_encoder_set_format_str(enc, "", NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_set_format_str(enc, "test", &errstr), 0);
ck_assert_str_eq(errstr, "unsupported stream format");
ck_assert_int_eq(cfg_encoder_set_format_str(enc, CFG_SFMT_VORBIS,
NULL), 0);
ck_assert_int_eq(cfg_encoder_set_format_str(enc, CFG_SFMT_MP3,
NULL), 0);
ck_assert_int_eq(cfg_encoder_set_format_str(enc, CFG_SFMT_THEORA,
NULL), 0);
ck_assert_uint_eq(cfg_encoder_get_format(enc), CFG_STREAM_THEORA);
}
END_TEST
START_TEST(test_encoder_validate)
{
cfg_encoder_t enc = cfg_encoder_get("test_encoder_validate");
const char *errstr;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr, "format not set");
ck_assert_int_eq(cfg_encoder_set_format(enc, CFG_STREAM_VORBIS, NULL),
0);
ck_assert_int_eq(cfg_encoder_validate(enc, NULL), 0);
ck_assert_int_eq(cfg_encoder_set_program(enc, PLACEHOLDER_TRACK,
NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"prohibited placeholder " PLACEHOLDER_TRACK);
ck_assert_int_eq(cfg_encoder_set_program(enc, PLACEHOLDER_STRING,
NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"prohibited placeholder " PLACEHOLDER_STRING);
ck_assert_int_eq(cfg_encoder_set_program(enc,
PLACEHOLDER_METADATA PLACEHOLDER_METADATA, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_METADATA);
ck_assert_int_eq(cfg_encoder_set_program(enc,
PLACEHOLDER_ARTIST PLACEHOLDER_ARTIST, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_ARTIST);
ck_assert_int_eq(cfg_encoder_set_program(enc,
PLACEHOLDER_TITLE PLACEHOLDER_TITLE, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_TITLE);
}
END_TEST
Suite *
cfg_suite(void)
{
Suite *s;
TCase *tc_core;
TCase *tc_program;
TCase *tc_server;
TCase *tc_stream;
TCase *tc_media;
TCase *tc_metadata;
TCase *tc_decoder;
TCase *tc_encoder;
s = suite_create("Config");
tc_core = tcase_create("Core");
tcase_add_checked_fixture(tc_core, setup_checked, teardown_checked);
tcase_add_test(tc_core, test_stash);
tcase_add_test(tc_core, test_check);
tcase_add_test(tc_core, test_stream_str2fmt);
tcase_add_test(tc_core, test_stream_fmt2str);
@ -836,37 +265,6 @@ cfg_suite(void)
tcase_add_test(tc_program, test_program_verbosity);
suite_add_tcase(s, tc_program);
tc_server = tcase_create("Server");
tcase_add_checked_fixture(tc_server, setup_checked, teardown_checked);
tcase_add_test(tc_server, test_server_protocol);
tcase_add_test(tc_server, test_server_hostname);
tcase_add_test(tc_server, test_server_port);
tcase_add_test(tc_server, test_server_user);
tcase_add_test(tc_server, test_server_password);
tcase_add_test(tc_server, test_server_tls);
tcase_add_test(tc_server, test_server_tls_cipher_suite);
tcase_add_test(tc_server, test_server_ca_dir);
tcase_add_test(tc_server, test_server_ca_file);
tcase_add_test(tc_server, test_server_client_cert);
tcase_add_test(tc_server, test_server_reconnect_attempts);
suite_add_tcase(s, tc_server);
tc_stream = tcase_create("Stream");
tcase_add_checked_fixture(tc_stream, setup_checked, teardown_checked);
tcase_add_test(tc_stream, test_stream_mountpoint);
tcase_add_test(tc_stream, test_stream_name);
tcase_add_test(tc_stream, test_stream_url);
tcase_add_test(tc_stream, test_stream_genre);
tcase_add_test(tc_stream, test_stream_description);
tcase_add_test(tc_stream, test_stream_quality);
tcase_add_test(tc_stream, test_stream_bitrate);
tcase_add_test(tc_stream, test_stream_samplerate);
tcase_add_test(tc_stream, test_stream_channels);
tcase_add_test(tc_stream, test_stream_server_public);
tcase_add_test(tc_stream, test_stream_format);
tcase_add_test(tc_stream, test_stream_encoder);
suite_add_tcase(s, tc_stream);
tc_media = tcase_create("Media");
tcase_add_checked_fixture(tc_media, setup_checked, teardown_checked);
tcase_add_test(tc_media, test_media_type);
@ -885,26 +283,6 @@ cfg_suite(void)
tcase_add_test(tc_metadata, test_metadata_no_updates);
suite_add_tcase(s, tc_metadata);
tc_decoder = tcase_create("Decoder");
tcase_add_checked_fixture(tc_decoder, setup_checked,
teardown_checked);
tcase_add_test(tc_decoder, test_decoder_get);
tcase_add_test(tc_decoder, test_decoder_set_name);
tcase_add_test(tc_decoder, test_decoder_set_program);
tcase_add_test(tc_decoder, test_decoder_add_match);
tcase_add_test(tc_decoder, test_decoder_validate);
suite_add_tcase(s, tc_decoder);
tc_encoder = tcase_create("Encoder");
tcase_add_checked_fixture(tc_encoder, setup_checked,
teardown_checked);
tcase_add_test(tc_encoder, test_encoder_get);
tcase_add_test(tc_decoder, test_encoder_set_name);
tcase_add_test(tc_decoder, test_encoder_set_program);
tcase_add_test(tc_decoder, test_encoder_set_format_str);
tcase_add_test(tc_decoder, test_encoder_validate);
suite_add_tcase(s, tc_encoder);
return (s);
}
@ -912,8 +290,6 @@ void
setup_checked(void)
{
if (0 < cfg_init() ||
0 < cfg_decoder_init() ||
0 < cfg_encoder_init() ||
0 < log_init())
ck_abort_msg("setup_checked failed");
}
@ -922,8 +298,6 @@ void
teardown_checked(void)
{
log_exit();
cfg_encoder_exit();
cfg_decoder_exit();
cfg_exit();
}

205
tests/check_cfg.h Normal file
View File

@ -0,0 +1,205 @@
#ifndef __CHECK_CFG_H__
#define __CHECK_CFG_H__
#define TEST_EMPTYSTR(setter) do { \
const char *errstr; \
\
errstr = NULL; \
ck_assert_int_eq(setter(NULL, NULL), -1); \
ck_assert_int_eq(setter(NULL, &errstr), -1); \
ck_assert_str_eq(errstr, "empty"); \
\
errstr = NULL; \
ck_assert_int_eq(setter("", &errstr), -1); \
ck_assert_str_eq(errstr, "empty"); \
} while (0)
#define TEST_EMPTYSTR_T(type, objget, list, setter) do { \
type obj3 = objget(list, #setter); \
const char *errstr; \
\
errstr = NULL; \
ck_assert_int_eq(setter(obj3, list, NULL, NULL), -1); \
ck_assert_int_eq(setter(obj3, list, NULL, &errstr), -1); \
ck_assert_str_eq(errstr, "empty"); \
\
errstr = NULL; \
ck_assert_int_eq(setter(obj3, list, "", &errstr), -1); \
ck_assert_str_eq(errstr, "empty"); \
} while (0)
#define TEST_XSTRDUP(setter, getter) do { \
TEST_EMPTYSTR(setter); \
\
ck_assert_int_eq(setter("check_cfg", NULL), 0); \
ck_assert_str_eq(getter(), "check_cfg"); \
} while (0)
#define TEST_XSTRDUP_T(type, objget, list, setter, getter) do { \
type obj2 = objget(list, #setter); \
\
TEST_EMPTYSTR_T(type, objget, list, setter); \
\
ck_assert_int_eq(setter(obj2, list, "check_cfg", NULL), 0); \
ck_assert_str_eq(getter(obj2), "check_cfg"); \
} while (0)
#define TEST_STRLCPY(setter, getter, length) do { \
char buf[length + 1]; \
const char *errstr2; \
\
TEST_XSTRDUP(setter, getter); \
\
errstr2 = NULL; \
memset(buf, 'A', sizeof(buf) - 1); \
buf[sizeof(buf) - 1] = '\0'; \
ck_assert_int_eq(setter(buf, NULL), -1); \
ck_assert_int_eq(setter(buf, &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
} while (0)
#define TEST_STRLCPY_T(type, objget, list, setter, getter, length) do { \
type obj = objget(list, #setter); \
char buf[length + 1]; \
const char *errstr2; \
\
TEST_XSTRDUP_T(type, objget, list, setter, getter); \
\
errstr2 = NULL; \
memset(buf, 'A', sizeof(buf) - 1); \
buf[sizeof(buf) - 1] = '\0'; \
ck_assert_int_eq(setter(obj, list, buf, NULL), -1); \
ck_assert_int_eq(setter(obj, list, buf, &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
} while (0)
#define TEST_BOOLEAN(setter, getter) do { \
const char *errstr2; \
\
TEST_EMPTYSTR(setter); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter("BOGUS", NULL), -1); \
ck_assert_int_eq(setter("BOGUS", &errstr2), -1); \
ck_assert_str_eq(errstr2, "invalid"); \
\
ck_assert_int_eq(setter("tRuE", NULL), 0); \
ck_assert_int_eq(getter(), 1); \
ck_assert_int_eq(setter("YeS", NULL), 0); \
ck_assert_int_eq(getter(), 1); \
ck_assert_int_eq(setter("1", NULL), 0); \
ck_assert_int_eq(getter(), 1); \
\
ck_assert_int_eq(setter("FaLsE", NULL), 0); \
ck_assert_int_eq(getter(), 0); \
ck_assert_int_eq(setter("nO", NULL), 0); \
ck_assert_int_eq(getter(), 0); \
ck_assert_int_eq(setter("0", NULL), 0); \
ck_assert_int_eq(getter(), 0); \
} while (0)
#define TEST_BOOLEAN_T(type, objget, list, setter, getter) do { \
type obj = objget(list, #setter); \
const char *errstr2; \
\
TEST_EMPTYSTR_T(type, objget, list, setter); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter(obj, list, "BOGUS", NULL), -1); \
ck_assert_int_eq(setter(obj, list, "BOGUS", &errstr2), -1); \
ck_assert_str_eq(errstr2, "invalid"); \
\
ck_assert_int_eq(setter(obj, list, "tRuE", NULL), 0); \
ck_assert_int_eq(getter(obj), 1); \
ck_assert_int_eq(setter(obj, list, "YeS", NULL), 0); \
ck_assert_int_eq(getter(obj), 1); \
ck_assert_int_eq(setter(obj, list, "1", NULL), 0); \
ck_assert_int_eq(getter(obj), 1); \
\
ck_assert_int_eq(setter(obj, list, "FaLsE", NULL), 0); \
ck_assert_int_eq(getter(obj), 0); \
ck_assert_int_eq(setter(obj, list, "nO", NULL), 0); \
ck_assert_int_eq(getter(obj), 0); \
ck_assert_int_eq(setter(obj, list, "0", NULL), 0); \
ck_assert_int_eq(getter(obj), 0); \
} while (0)
#define TEST_UINTNUM(setter, getter) do { \
const char *errstr2; \
\
TEST_EMPTYSTR(setter); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter("-1", NULL), -1); \
ck_assert_int_eq(setter("-1", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter("4294967296", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
ck_assert_int_eq(setter("20", NULL), 0); \
ck_assert_uint_eq(getter(), 20); \
} while (0)
#define TEST_UINTNUM_T(type, objget, list, setter, getter) do { \
type obj = objget(list, #setter); \
const char *errstr2; \
\
TEST_EMPTYSTR_T(type, objget, list, setter); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter(obj, list, "-1", NULL), -1); \
ck_assert_int_eq(setter(obj, list, "-1", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter(obj, list, "4294967296", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
ck_assert_int_eq(setter(obj, list, "20", NULL), 0); \
ck_assert_uint_eq(getter(obj), 20); \
} while (0)
#define TEST_INTNUM(setter, getter) do { \
const char *errstr2; \
\
TEST_EMPTYSTR(setter); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter("-2147483649", NULL), -1); \
ck_assert_int_eq(setter("-2147483649", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter("2147483648", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
ck_assert_int_eq(setter("20", NULL), 0); \
ck_assert_int_eq(getter(), 20); \
ck_assert_int_eq(setter("-20", NULL), 0); \
ck_assert_int_eq(getter(), -20); \
} while (0)
#define TEST_INTNUM_T(type, objget, list, setter, getter) do { \
type obj = objget(list, #setter); \
const char *errstr2; \
\
TEST_EMPTYSTR(type, objget, list, setter, getter); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter(obj, list, "-2147483649", NULL), -1); \
ck_assert_int_eq(setter(obj, list, "-2147483649", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
errstr2 = NULL; \
ck_assert_int_eq(setter(obj, list, "2147483648", &errstr2), -1); \
ck_assert_ptr_ne(errstr2, NULL); \
\
ck_assert_int_eq(setter(obj, list, "20", NULL), 0); \
ck_assert_int_eq(getter(obj), 20); \
ck_assert_int_eq(setter(obj, list, "-20", NULL), 0); \
ck_assert_int_eq(getter(obj), -20); \
} while (0)
#endif /* __CHECK_CFG_H__ */

192
tests/check_cfg_decoder.c Normal file
View File

@ -0,0 +1,192 @@
#include <check.h>
#include <limits.h>
#include <netdb.h>
#include "cfg_private.h"
#include "log.h"
#include "check_cfg.h"
Suite * cfg_suite(void);
void setup_checked(void);
void teardown_checked(void);
cfg_decoder_list_t decoders;
START_TEST(test_decoder_list_get)
{
cfg_decoder_t dec, dec2;
ck_assert_ptr_eq(cfg_decoder_list_get(decoders, NULL), NULL);
ck_assert_ptr_eq(cfg_decoder_list_get(decoders, ""), NULL);
dec = cfg_decoder_list_get(decoders, "TeSt");
dec2 = cfg_decoder_list_get(decoders, "test");
ck_assert_ptr_eq(dec, dec2);
}
END_TEST
START_TEST(test_decoder_set_name)
{
TEST_XSTRDUP_T(cfg_decoder_t, cfg_decoder_list_get, decoders,
cfg_decoder_set_name, cfg_decoder_get_name);
}
END_TEST
START_TEST(test_decoder_set_program)
{
TEST_XSTRDUP_T(cfg_decoder_t, cfg_decoder_list_get, decoders,
cfg_decoder_set_program, cfg_decoder_get_program);
}
END_TEST
START_TEST(test_decoder_add_match)
{
cfg_decoder_t dec = cfg_decoder_list_get(decoders, "test_decoder_add_match");
cfg_decoder_t dec2 = cfg_decoder_list_get(decoders, "test_decoder_add_match_2");
const char *errstr;
errstr = NULL;
ck_assert_int_ne(cfg_decoder_add_match(dec, decoders, NULL, NULL), 0);
ck_assert_int_ne(cfg_decoder_add_match(dec, decoders, NULL, &errstr),
0);
ck_assert_str_eq(errstr, "empty");
ck_assert_int_ne(cfg_decoder_add_match(dec, decoders, "", NULL), 0);
ck_assert_int_eq(cfg_decoder_add_match(dec, decoders, ".test", NULL),
0);
ck_assert_int_eq(cfg_decoder_add_match(dec, decoders, ".test2", NULL), 0);
ck_assert_ptr_eq(cfg_decoder_list_findext(decoders, ".test"), dec);
ck_assert_ptr_eq(cfg_decoder_list_findext(decoders, ".test2"), dec);
ck_assert_int_eq(cfg_decoder_add_match(dec2, decoders, ".test2", NULL),
0);
ck_assert_ptr_eq(cfg_decoder_list_findext(decoders, ".test"), dec);
ck_assert_ptr_eq(cfg_decoder_list_findext(decoders, ".test2"), dec2);
}
END_TEST
START_TEST(test_decoder_validate)
{
cfg_decoder_t dec = cfg_decoder_list_get(decoders, "test_decoder_validate");
const char *errstr;
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, NULL), 0);
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr, "program not set");
ck_assert_int_eq(cfg_decoder_set_program(dec, decoders, "test", NULL),
0);
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr, "no file extensions registered");
ck_assert_int_eq(cfg_decoder_add_match(dec, decoders, ".test", NULL),
0);
ck_assert_int_eq(cfg_decoder_set_program(dec, decoders,
PLACEHOLDER_STRING, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"prohibited placeholder " PLACEHOLDER_STRING);
ck_assert_int_eq(cfg_decoder_set_program(dec, decoders,
PLACEHOLDER_TRACK PLACEHOLDER_TRACK, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_TRACK);
ck_assert_int_eq(cfg_decoder_set_program(dec, decoders,
PLACEHOLDER_METADATA PLACEHOLDER_METADATA, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_METADATA);
ck_assert_int_eq(cfg_decoder_set_program(dec, decoders,
PLACEHOLDER_ARTIST PLACEHOLDER_ARTIST, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_ARTIST);
ck_assert_int_eq(cfg_decoder_set_program(dec, decoders,
PLACEHOLDER_TITLE PLACEHOLDER_TITLE, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_TITLE);
ck_assert_int_eq(cfg_decoder_set_program(dec, decoders,
"test", NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_decoder_validate(dec, &errstr), 0);
ck_assert_str_eq(errstr,
"missing placeholder " PLACEHOLDER_TRACK);
ck_assert_int_eq(cfg_decoder_set_program(dec, decoders,
PLACEHOLDER_TRACK, NULL), 0);
ck_assert_int_eq(cfg_decoder_validate(dec, &errstr), 0);
}
END_TEST
Suite *
cfg_suite(void)
{
Suite *s;
TCase *tc_decoder;
s = suite_create("Config");
tc_decoder = tcase_create("Decoder");
tcase_add_checked_fixture(tc_decoder, setup_checked,
teardown_checked);
tcase_add_test(tc_decoder, test_decoder_list_get);
tcase_add_test(tc_decoder, test_decoder_set_name);
tcase_add_test(tc_decoder, test_decoder_set_program);
tcase_add_test(tc_decoder, test_decoder_add_match);
tcase_add_test(tc_decoder, test_decoder_validate);
suite_add_tcase(s, tc_decoder);
return (s);
}
void
setup_checked(void)
{
if (0 < cfg_init() ||
0 < log_init())
ck_abort_msg("setup_checked failed");
decoders = cfg_decoder_list_create();
}
void
teardown_checked(void)
{
cfg_decoder_list_destroy(&decoders);
log_exit();
cfg_exit();
}
int
main(void)
{
int num_failed;
Suite *s;
SRunner *sr;
s = cfg_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
num_failed = srunner_ntests_failed(sr);
srunner_free(sr);
if (num_failed)
return (1);
return (0);
}

184
tests/check_cfg_encoder.c Normal file
View File

@ -0,0 +1,184 @@
#include <check.h>
#include <limits.h>
#include <netdb.h>
#include "cfg_private.h"
#include "log.h"
#include "check_cfg.h"
Suite * cfg_suite(void);
void setup_checked(void);
void teardown_checked(void);
cfg_encoder_list_t encoders;
START_TEST(test_encoder_list_get)
{
cfg_encoder_t enc, enc2;
ck_assert_ptr_eq(cfg_encoder_list_get(encoders, NULL), NULL);
ck_assert_ptr_eq(cfg_encoder_list_get(encoders, ""), NULL);
enc = cfg_encoder_list_get(encoders, "TeSt");
enc2 = cfg_encoder_list_get(encoders, "test");
ck_assert_ptr_eq(enc, enc2);
}
END_TEST
START_TEST(test_encoder_set_name)
{
TEST_XSTRDUP_T(cfg_encoder_t, cfg_encoder_list_get, encoders,
cfg_encoder_set_name, cfg_encoder_get_name);
}
END_TEST
START_TEST(test_encoder_set_program)
{
cfg_encoder_t enc = cfg_encoder_list_get(encoders, "test_encoder_set_program");
ck_assert_int_eq(cfg_encoder_set_program(enc, encoders, NULL, NULL), 0);
ck_assert_ptr_eq(cfg_encoder_get_program(enc), NULL);
ck_assert_int_eq(cfg_encoder_set_program(enc, encoders, "test", NULL), 0);
ck_assert_str_eq(cfg_encoder_get_program(enc), "test");
}
END_TEST
START_TEST(test_encoder_set_format_str)
{
cfg_encoder_t enc = cfg_encoder_list_get(encoders, "test_encoder_set_format_str");
const char *errstr;
errstr = NULL;
ck_assert_int_ne(cfg_encoder_set_format_str(enc, encoders, NULL,
NULL), 0);
ck_assert_int_ne(cfg_encoder_set_format_str(enc, encoders, NULL,
&errstr), 0);
ck_assert_str_eq(errstr, "empty");
ck_assert_int_ne(cfg_encoder_set_format_str(enc, encoders, "", NULL),
0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_set_format_str(enc, encoders, "test",
&errstr), 0);
ck_assert_str_eq(errstr, "unsupported stream format");
ck_assert_int_eq(cfg_encoder_set_format_str(enc, encoders,
CFG_SFMT_VORBIS, NULL), 0);
ck_assert_int_eq(cfg_encoder_set_format_str(enc, encoders,
CFG_SFMT_MP3, NULL), 0);
ck_assert_int_eq(cfg_encoder_set_format_str(enc, encoders,
CFG_SFMT_THEORA, NULL), 0);
ck_assert_uint_eq(cfg_encoder_get_format(enc), CFG_STREAM_THEORA);
}
END_TEST
START_TEST(test_encoder_validate)
{
cfg_encoder_t enc = cfg_encoder_list_get(encoders, "test_encoder_validate");
const char *errstr;
ck_assert_int_ne(cfg_encoder_validate(enc, NULL), 0);
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr, "format not set");
ck_assert_int_eq(cfg_encoder_set_format(enc, CFG_STREAM_VORBIS), 0);
ck_assert_int_eq(cfg_encoder_validate(enc, NULL), 0);
ck_assert_int_eq(cfg_encoder_set_program(enc, encoders,
PLACEHOLDER_TRACK, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"prohibited placeholder " PLACEHOLDER_TRACK);
ck_assert_int_eq(cfg_encoder_set_program(enc, encoders,
PLACEHOLDER_STRING, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"prohibited placeholder " PLACEHOLDER_STRING);
ck_assert_int_eq(cfg_encoder_set_program(enc, encoders,
PLACEHOLDER_METADATA PLACEHOLDER_METADATA, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_METADATA);
ck_assert_int_eq(cfg_encoder_set_program(enc, encoders,
PLACEHOLDER_ARTIST PLACEHOLDER_ARTIST, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_ARTIST);
ck_assert_int_eq(cfg_encoder_set_program(enc, encoders,
PLACEHOLDER_TITLE PLACEHOLDER_TITLE, NULL), 0);
errstr = NULL;
ck_assert_int_ne(cfg_encoder_validate(enc, &errstr), 0);
ck_assert_str_eq(errstr,
"duplicate placeholder " PLACEHOLDER_TITLE);
}
END_TEST
Suite *
cfg_suite(void)
{
Suite *s;
TCase *tc_encoder;
s = suite_create("Config");
tc_encoder = tcase_create("Encoder");
tcase_add_checked_fixture(tc_encoder, setup_checked,
teardown_checked);
tcase_add_test(tc_encoder, test_encoder_list_get);
tcase_add_test(tc_encoder, test_encoder_set_name);
tcase_add_test(tc_encoder, test_encoder_set_program);
tcase_add_test(tc_encoder, test_encoder_set_format_str);
tcase_add_test(tc_encoder, test_encoder_validate);
suite_add_tcase(s, tc_encoder);
return (s);
}
void
setup_checked(void)
{
if (0 < cfg_init() ||
0 < log_init())
ck_abort_msg("setup_checked failed");
encoders = cfg_encoder_list_create();
}
void
teardown_checked(void)
{
cfg_encoder_list_destroy(&encoders);
log_exit();
cfg_exit();
}
int
main(void)
{
int num_failed;
Suite *s;
SRunner *sr;
s = cfg_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
num_failed = srunner_ntests_failed(sr);
srunner_free(sr);
if (num_failed)
return (1);
return (0);
}

248
tests/check_cfg_server.c Normal file
View File

@ -0,0 +1,248 @@
#include <check.h>
#include <limits.h>
#include <netdb.h>
#include "cfg_private.h"
#include "log.h"
#include "check_cfg.h"
Suite * cfg_suite(void);
void setup_checked(void);
void teardown_checked(void);
cfg_server_list_t servers;
START_TEST(test_server_list_get)
{
cfg_server_t srv, srv2;
ck_assert_ptr_eq(cfg_server_list_get(servers, NULL), NULL);
ck_assert_ptr_eq(cfg_server_list_get(servers, ""), NULL);
srv = cfg_server_list_get(servers, "TeSt");
srv2 = cfg_server_list_get(servers, "test");
ck_assert_ptr_eq(srv, srv2);
}
END_TEST
START_TEST(test_server_protocol)
{
cfg_server_t srv = cfg_server_list_get(servers, "test_server_protocol");
const char *errstr2;
TEST_EMPTYSTR_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_protocol);
errstr2 = NULL;
ck_assert_int_eq(cfg_server_set_protocol(srv, servers, "invalid",
NULL), -1);
ck_assert_int_eq(cfg_server_set_protocol(srv, servers, "invalid",
&errstr2), -1);
ck_assert_ptr_ne(errstr2, NULL);
ck_assert_str_eq(errstr2, "unsupported");
ck_assert_int_eq(cfg_server_set_protocol(srv, servers, "hTtP", NULL),
0);
ck_assert_int_eq(cfg_server_get_protocol(srv), CFG_PROTO_HTTP);
ck_assert_str_eq(cfg_server_get_protocol_str(srv), "http");
ck_assert_int_eq(cfg_server_set_protocol(srv, servers, "HtTpS", NULL),
0);
ck_assert_int_eq(cfg_server_get_protocol(srv), CFG_PROTO_HTTPS);
ck_assert_str_eq(cfg_server_get_protocol_str(srv), "https");
}
END_TEST
START_TEST(test_server_hostname)
{
TEST_STRLCPY_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_hostname, cfg_server_get_hostname, NI_MAXHOST);
}
END_TEST
START_TEST(test_server_port)
{
cfg_server_t srv = cfg_server_list_get(servers, "test_server_port");
const char *errstr2;
ck_assert_uint_eq(cfg_server_get_port(srv), DEFAULT_PORT);
TEST_EMPTYSTR_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_port);
errstr2 = NULL;
ck_assert_int_eq(cfg_server_set_port(srv, servers, "0", NULL), -1);
ck_assert_int_eq(cfg_server_set_port(srv, servers, "0", &errstr2), -1);
ck_assert_ptr_ne(errstr2, NULL);
errstr2 = NULL;
ck_assert_int_eq(cfg_server_set_port(srv, servers, "65536", &errstr2),
-1);
ck_assert_ptr_ne(errstr2, NULL);
ck_assert_int_eq(cfg_server_set_port(srv, servers, "8008", NULL), 0);
ck_assert_uint_eq(cfg_server_get_port(srv), 8008);
}
END_TEST
START_TEST(test_server_user)
{
cfg_server_t srv = cfg_server_list_get(servers, "test_server_user");
ck_assert_str_eq(cfg_server_get_user(srv), DEFAULT_USER);
TEST_STRLCPY_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_user, cfg_server_get_user, UCREDS_SIZE);
}
END_TEST
START_TEST(test_server_password)
{
TEST_STRLCPY_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_password, cfg_server_get_password, UCREDS_SIZE);
}
END_TEST
START_TEST(test_server_ca_dir)
{
TEST_STRLCPY_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_ca_dir, cfg_server_get_ca_dir, PATH_MAX);
}
END_TEST
START_TEST(test_server_tls)
{
cfg_server_t srv = cfg_server_list_get(servers, "test_server_tls");
const char *errstr = NULL;
ck_assert_int_eq(cfg_server_set_tls(srv, servers, "", NULL), -1);
ck_assert_int_eq(cfg_server_set_tls(srv, servers, "", &errstr), -1);
ck_assert_str_eq(errstr, "empty");
ck_assert_int_eq(cfg_server_set_tls(srv, servers, "test", &errstr),
-1);
ck_assert_str_eq(errstr, "invalid");
ck_assert_int_eq(cfg_server_get_tls(srv), CFG_TLS_MAY);
ck_assert_int_eq(cfg_server_set_tls(srv, servers, "None", NULL), 0);
ck_assert_int_eq(cfg_server_get_tls(srv), CFG_TLS_NONE);
ck_assert_int_eq(cfg_server_set_tls(srv, servers, "Required", NULL),
0);
ck_assert_int_eq(cfg_server_get_tls(srv), CFG_TLS_REQUIRED);
ck_assert_int_eq(cfg_server_set_tls(srv, servers, "May", NULL), 0);
ck_assert_int_eq(cfg_server_get_tls(srv), CFG_TLS_MAY);
}
END_TEST
START_TEST(test_server_tls_cipher_suite)
{
TEST_STRLCPY_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_tls_cipher_suite, cfg_server_get_tls_cipher_suite,
CSUITE_SIZE);
}
END_TEST
START_TEST(test_server_ca_file)
{
TEST_STRLCPY_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_ca_file, cfg_server_get_ca_file, PATH_MAX);
}
END_TEST
START_TEST(test_server_client_cert)
{
TEST_STRLCPY_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_client_cert, cfg_server_get_client_cert, PATH_MAX);
}
END_TEST
START_TEST(test_server_reconnect_attempts)
{
TEST_UINTNUM_T(cfg_server_t, cfg_server_list_get, servers,
cfg_server_set_reconnect_attempts,
cfg_server_get_reconnect_attempts);
}
END_TEST
START_TEST(test_server_validate)
{
cfg_server_t srv = cfg_server_list_get(servers, "test_server_validate");
const char *errstr = NULL;
ck_assert_int_ne(cfg_server_validate(srv, NULL), 0);
ck_assert_int_ne(cfg_server_validate(srv, &errstr), 0);
ck_assert_str_eq(errstr, "hostname missing");
ck_assert_int_eq(cfg_server_set_hostname(srv, servers, "localhost",
NULL), 0);
ck_assert_int_ne(cfg_server_validate(srv, &errstr), 0);
ck_assert_str_eq(errstr, "password missing");
ck_assert_int_eq(cfg_server_set_password(srv, servers, "secret", NULL),
0);
ck_assert_int_eq(cfg_server_validate(srv, NULL), 0);
}
END_TEST
Suite *
cfg_suite(void)
{
Suite *s;
TCase *tc_server;
s = suite_create("Config");
tc_server = tcase_create("Server");
tcase_add_checked_fixture(tc_server, setup_checked, teardown_checked);
tcase_add_test(tc_server, test_server_list_get);
tcase_add_test(tc_server, test_server_protocol);
tcase_add_test(tc_server, test_server_hostname);
tcase_add_test(tc_server, test_server_port);
tcase_add_test(tc_server, test_server_user);
tcase_add_test(tc_server, test_server_password);
tcase_add_test(tc_server, test_server_tls);
tcase_add_test(tc_server, test_server_tls_cipher_suite);
tcase_add_test(tc_server, test_server_ca_dir);
tcase_add_test(tc_server, test_server_ca_file);
tcase_add_test(tc_server, test_server_client_cert);
tcase_add_test(tc_server, test_server_reconnect_attempts);
tcase_add_test(tc_server, test_server_validate);
suite_add_tcase(s, tc_server);
return (s);
}
void
setup_checked(void)
{
if (0 < cfg_init() ||
0 < log_init())
ck_abort_msg("setup_checked failed");
servers = cfg_server_list_create();
}
void
teardown_checked(void)
{
cfg_server_list_destroy(&servers);
log_exit();
cfg_exit();
}
int
main(void)
{
int num_failed;
Suite *s;
SRunner *sr;
s = cfg_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
num_failed = srunner_ntests_failed(sr);
srunner_free(sr);
if (num_failed)
return (1);
return (0);
}

223
tests/check_cfg_stream.c Normal file
View File

@ -0,0 +1,223 @@
#include <check.h>
#include <limits.h>
#include <netdb.h>
#include "cfg_private.h"
#include "log.h"
#include "check_cfg.h"
Suite * cfg_suite(void);
void setup_checked(void);
void teardown_checked(void);
cfg_stream_list_t streams;
START_TEST(test_stream_list_get)
{
cfg_stream_t str, str2;
ck_assert_ptr_eq(cfg_stream_list_get(streams, NULL), NULL);
ck_assert_ptr_eq(cfg_stream_list_get(streams, ""), NULL);
str = cfg_stream_list_get(streams, "TeSt");
str2 = cfg_stream_list_get(streams, "test");
ck_assert_ptr_eq(str, str2);
}
END_TEST
START_TEST(test_stream_name)
{
}
END_TEST
START_TEST(test_stream_mountpoint)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_mountpoint, cfg_stream_get_mountpoint);
}
END_TEST
START_TEST(test_stream_server)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_server, cfg_stream_get_server);
}
END_TEST
START_TEST(test_stream_public)
{
TEST_BOOLEAN_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_public, cfg_stream_get_public);
}
END_TEST
START_TEST(test_stream_format)
{
cfg_stream_t str = cfg_stream_list_get(streams, "test_stream_format");
const char *errstr2;
TEST_EMPTYSTR_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_format);
ck_assert_int_eq(cfg_stream_set_format(str, streams,
"<something else>", NULL), -1);
ck_assert_int_eq(cfg_stream_set_format(str, streams,
"<something else>", &errstr2), -1);
ck_assert_str_eq(errstr2, "unsupported stream format");
ck_assert_int_eq(cfg_stream_set_format(str, streams, CFG_SFMT_VORBIS,
NULL), 0);
ck_assert_int_eq(cfg_stream_get_format(str), CFG_STREAM_VORBIS);
ck_assert_str_eq(cfg_stream_get_format_str(str),
cfg_stream_fmt2str(CFG_STREAM_VORBIS));
}
END_TEST
START_TEST(test_stream_encoder)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_encoder, cfg_stream_get_encoder);
}
END_TEST
START_TEST(test_stream_stream_name)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_stream_name, cfg_stream_get_stream_name);
}
END_TEST
START_TEST(test_stream_stream_url)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_stream_url, cfg_stream_get_stream_url);
}
END_TEST
START_TEST(test_stream_stream_genre)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_stream_genre, cfg_stream_get_stream_genre);
}
END_TEST
START_TEST(test_stream_stream_description)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_stream_description,
cfg_stream_get_stream_description);
}
END_TEST
START_TEST(test_stream_stream_quality)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_stream_quality, cfg_stream_get_stream_quality);
}
END_TEST
START_TEST(test_stream_stream_bitrate)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_stream_bitrate, cfg_stream_get_stream_bitrate);
}
END_TEST
START_TEST(test_stream_stream_samplerate)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_stream_samplerate, cfg_stream_get_stream_samplerate);
}
END_TEST
START_TEST(test_stream_stream_channels)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_stream_channels, cfg_stream_get_stream_channels);
}
END_TEST
START_TEST(test_stream_validate)
{
cfg_stream_t str = cfg_stream_list_get(streams, "test_stream_validate");
const char *errstr = NULL;
ck_assert_int_ne(cfg_stream_validate(str, NULL), 0);
ck_assert_int_ne(cfg_stream_validate(str, &errstr), 0);
ck_assert_str_eq(errstr, "format missing or unsupported");
ck_assert_int_eq(cfg_stream_set_format(str, streams, CFG_SFMT_VORBIS,
NULL), 0);
ck_assert_int_eq(cfg_stream_validate(str, NULL), 0);
}
END_TEST
Suite *
cfg_suite(void)
{
Suite *s;
TCase *tc_stream;
s = suite_create("Config");
tc_stream = tcase_create("Stream");
tcase_add_checked_fixture(tc_stream, setup_checked, teardown_checked);
tcase_add_test(tc_stream, test_stream_list_get);
tcase_add_test(tc_stream, test_stream_name);
tcase_add_test(tc_stream, test_stream_mountpoint);
tcase_add_test(tc_stream, test_stream_server);
tcase_add_test(tc_stream, test_stream_public);
tcase_add_test(tc_stream, test_stream_format);
tcase_add_test(tc_stream, test_stream_encoder);
tcase_add_test(tc_stream, test_stream_stream_name);
tcase_add_test(tc_stream, test_stream_stream_url);
tcase_add_test(tc_stream, test_stream_stream_genre);
tcase_add_test(tc_stream, test_stream_stream_description);
tcase_add_test(tc_stream, test_stream_stream_quality);
tcase_add_test(tc_stream, test_stream_stream_bitrate);
tcase_add_test(tc_stream, test_stream_stream_samplerate);
tcase_add_test(tc_stream, test_stream_stream_channels);
tcase_add_test(tc_stream, test_stream_validate);
suite_add_tcase(s, tc_stream);
return (s);
}
void
setup_checked(void)
{
if (0 < cfg_init() ||
0 < log_init())
ck_abort_msg("setup_checked failed");
streams = cfg_stream_list_create();
}
void
teardown_checked(void)
{
cfg_stream_list_destroy(&streams);
log_exit();
cfg_exit();
}
int
main(void)
{
int num_failed;
Suite *s;
SRunner *sr;
s = cfg_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
num_failed = srunner_ntests_failed(sr);
srunner_free(sr);
if (num_failed)
return (1);
return (0);
}

View File

@ -37,6 +37,9 @@ START_TEST(test_reload)
ck_assert_int_eq(cfg_set_program_config_file(SRCDIR "/config-bad4.xml",
NULL), 0);
ck_assert_int_eq(cfg_file_reload(), -1);
ck_assert_int_eq(cfg_set_program_config_file(SRCDIR "/config-bad5.xml",
NULL), 0);
ck_assert_int_eq(cfg_file_reload(), -1);
}
END_TEST
@ -60,8 +63,6 @@ void
setup_checked(void)
{
if (0 < cfg_init() ||
0 < cfg_decoder_init() ||
0 < cfg_encoder_init() ||
0 < log_init())
ck_abort_msg("setup_checked failed");
}
@ -70,8 +71,6 @@ void
teardown_checked(void)
{
log_exit();
cfg_encoder_exit();
cfg_decoder_exit();
cfg_exit();
}

View File

@ -11,82 +11,85 @@ void teardown_checked(void);
START_TEST(test_stream)
{
stream_t s1;
stream_t s2;
mdata_t m;
char *m_str;
stream_t s;
mdata_t m;
char *m_str;
cfg_server_t srv_cfg;
cfg_stream_t str_cfg;
cfg_server_list_t servers = cfg_get_servers();
cfg_stream_list_t streams = cfg_get_streams();
s1 = stream_get("test-stream");
ck_assert_ptr_ne(s1, NULL);
s2 = stream_get("test-stream");
ck_assert_ptr_eq(s1, s2);
s2 = stream_get("test2-stream");
ck_assert_ptr_ne(s2, NULL);
ck_assert_ptr_ne(s1, s2);
s = stream_create("test-stream");
ck_assert_ptr_ne(s, NULL);
ck_assert_int_ne(stream_connect(s1), 0);
stream_disconnect(s1);
stream_sync(s1);
ck_assert_int_ne(stream_send(s1, NULL, 0), 0);
ck_assert_int_ne(stream_connect(s), 0);
stream_disconnect(s);
stream_sync(s);
ck_assert_int_ne(stream_send(s, NULL, 0), 0);
ck_assert_int_ne(stream_setup(s1), 0);
cfg_set_server_hostname("localhost", NULL);
ck_assert_int_ne(stream_setup(s1), 0);
cfg_set_server_password("test", NULL);
ck_assert_int_ne(stream_setup(s1), 0);
cfg_set_server_tls("required", NULL);
ck_assert_int_ne(stream_setup(s1), 0);
cfg_set_server_tls("may", NULL);
ck_assert_int_ne(stream_setup(s1), 0);
cfg_set_server_tls("none", NULL);
ck_assert_int_ne(stream_setup(s1), 0);
cfg_set_stream_mountpoint("/test.ogg", NULL);
ck_assert_int_ne(stream_setup(s1), 0);
cfg_set_stream_format("mp3", NULL);
ck_assert_int_eq(stream_setup(s1), 0);
cfg_set_stream_format("vorbis", NULL);
ck_assert_int_eq(stream_setup(s1), 0);
cfg_set_stream_name("test", NULL);
cfg_set_stream_url("test", NULL);
cfg_set_stream_genre("test", NULL);
cfg_set_stream_description("test", NULL);
cfg_set_stream_quality("test", NULL);
cfg_set_stream_bitrate("test", NULL);
cfg_set_stream_samplerate("test", NULL);
cfg_set_stream_channels("test", NULL);
cfg_set_stream_server_public("test", NULL);
ck_assert_int_eq(stream_setup(s1), 0);
srv_cfg = stream_get_cfg_server(s);
ck_assert_ptr_ne(srv_cfg, NULL);
str_cfg = stream_get_cfg_stream(s);
ck_assert_ptr_ne(str_cfg, NULL);
ck_assert_int_eq(stream_get_connected(s1), 0);
ck_assert_int_ne(stream_configure(s), 0);
cfg_server_set_hostname(srv_cfg, servers, "localhost", NULL);
ck_assert_int_ne(stream_configure(s), 0);
cfg_server_set_password(srv_cfg, servers, "test", NULL);
ck_assert_int_ne(stream_configure(s), 0);
cfg_server_set_tls(srv_cfg, servers, "required", NULL);
ck_assert_int_ne(stream_configure(s), 0);
cfg_server_set_tls(srv_cfg, servers, "may", NULL);
ck_assert_int_ne(stream_configure(s), 0);
cfg_server_set_tls(srv_cfg, servers, "none", NULL);
ck_assert_int_ne(stream_configure(s), 0);
cfg_stream_set_mountpoint(str_cfg, streams, "/test.ogg", NULL);
ck_assert_int_ne(stream_configure(s), 0);
cfg_stream_set_format(str_cfg, streams, "mp3", NULL);
ck_assert_int_eq(stream_configure(s), 0);
cfg_stream_set_format(str_cfg, streams, "vorbis", NULL);
ck_assert_int_eq(stream_configure(s), 0);
cfg_stream_set_stream_name(str_cfg, streams, "test", NULL);
cfg_stream_set_stream_url(str_cfg, streams, "test", NULL);
cfg_stream_set_stream_genre(str_cfg, streams, "test", NULL);
cfg_stream_set_stream_description(str_cfg, streams, "test", NULL);
cfg_stream_set_stream_quality(str_cfg, streams, "test", NULL);
cfg_stream_set_stream_bitrate(str_cfg, streams, "test", NULL);
cfg_stream_set_stream_samplerate(str_cfg, streams, "test", NULL);
cfg_stream_set_stream_channels(str_cfg, streams, "test", NULL);
cfg_stream_set_public(str_cfg, streams, "test", NULL);
ck_assert_int_eq(stream_configure(s), 0);
ck_assert_int_eq(stream_get_connected(s), 0);
m = mdata_create();
ck_assert_int_eq(mdata_parse_file(m, SRCDIR "/test01-artist+album+title.ogg"), 0);
cfg_set_metadata_no_updates("yes", NULL);
ck_assert_int_eq(stream_set_metadata(s1, NULL, NULL), 0);
ck_assert_int_eq(stream_set_metadata(s, NULL, NULL), 0);
cfg_set_metadata_no_updates("no", NULL);
ck_assert_int_ne(stream_set_metadata(s1, NULL, NULL), 0);
ck_assert_int_ne(stream_set_metadata(s, NULL, NULL), 0);
/*
* Not running stream_set_metadata checked as libshout behaves
* different on different systems, making these unreliable ...
*/
m_str = NULL;
stream_set_metadata(s1, m, &m_str);
stream_set_metadata(s, m, &m_str);
xfree(m_str);
m_str = NULL;
ck_assert_int_eq(mdata_parse_file(m, SRCDIR "/test15-title.ogg"), 0);
stream_set_metadata(s1, m, &m_str);
stream_set_metadata(s, m, &m_str);
xfree(m_str);
m_str = NULL;
ck_assert_int_eq(mdata_parse_file(m, SRCDIR "/test16-nometa.ogg"), 0);
stream_set_metadata(s1, m, &m_str);
stream_set_metadata(s, m, &m_str);
xfree(m_str);
m_str = NULL;
cfg_set_metadata_format_str("test", NULL);
ck_assert_int_eq(mdata_parse_file(m, SRCDIR "/test01-artist+album+title.ogg"), 0);
stream_set_metadata(s1, m, &m_str);
stream_set_metadata(s, m, &m_str);
xfree(m_str);
m_str = NULL;

View File

@ -2,34 +2,43 @@
<ezstream>
<server>
<protocol></protocol>
<hostname></hostname>
<port></port>
<user></user>
<password></password>
<tls></tls>
<tls_cipher_suite></tls_cipher_suite>
<ca_dir></ca_dir>
<ca_file></ca_file>
<client_cert></client_cert>
<reconnect_attempts></reconnect_attempts>
</server>
<servers>
<server>
<name></name>
<protocol></protocol>
<hostname></hostname>
<port></port>
<user></user>
<password></password>
<tls></tls>
<tls_cipher_suite></tls_cipher_suite>
<ca_dir></ca_dir>
<ca_file></ca_file>
<client_cert></client_cert>
<reconnect_attempts></reconnect_attempts>
</server>
<!-- ... -->
</servers>
<stream>
<mountpoint></mountpoint>
<name></name>
<url></url>
<genre></genre>
<description></description>
<quality></quality>
<bitrate></bitrate>
<samplerate></samplerate>
<channels></channels>
<server_public></server_public>
<format></format>
<encoder></encoder>
</stream>
<streams>
<stream>
<name></name>
<mountpoint></mountpoint>
<public></public>
<server></server>
<format></format>
<encoder></encoder>
<stream_name></stream_name>
<stream_url></stream_url>
<stream_genre></stream_genre>
<stream_description></stream_description>
<stream_quality></stream_quality>
<stream_bitrate></stream_bitrate>
<stream_samplerate></stream_samplerate>
<stream_channels></stream_channels>
</stream>
<!-- ... -->
</streams>
<media>
<type></type>

55
tests/config-bad5.xml Normal file
View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<ezstream>
<servers>
<server>
<hostname>localhost</hostname>
<password>secret</password>
<name>duplicate</name>
</server>
<server>
<hostname>localhost</hostname>
<password>secret</password>
<name>duplicate</name>
</server>
</servers>
<streams>
<stream>
<format>vorbis</format>
<name>duplicate</name>
</stream>
<stream>
<format>vorbis</format>
<name>duplicate</name>
</stream>
</streams>
<decoders>
<decoder>
<program>true @T@</program>
<name>duplicate</name>
<file_ext>.ogg</file_ext>
</decoder>
<decoder>
<program>true @T@</program>
<name>duplicate</name>
<file_ext>.ogg</file_ext>
</decoder>
<!-- ... -->
</decoders>
<encoders>
<encoder>
<format>vorbis</format>
<name>duplicate</name>
</encoder>
<encoder>
<format>vorbis</format>
<name>duplicate</name>
</encoder>
<!-- ... -->
</encoders>
</ezstream>