mirror of
https://gitlab.xiph.org/xiph/ezstream.git
synced 2024-12-04 14:46:31 -05: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:
parent
f82cf2b458
commit
ed9e84f387
@ -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
|
||||
|
@ -10,17 +10,21 @@
|
||||
|
||||
<ezstream>
|
||||
|
||||
<servers>
|
||||
<server>
|
||||
<hostname>127.0.0.1</hostname>
|
||||
<port>8000</port>
|
||||
<password>hackme</password>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
<streams>
|
||||
<stream>
|
||||
<mountpoint>/stream.ogg</mountpoint>
|
||||
<format>Vorbis</format>
|
||||
<encoder>OggEnc-Q1.5</encoder>
|
||||
</stream>
|
||||
</streams>
|
||||
|
||||
<media>
|
||||
<filename>%FILENAME%</filename>
|
||||
|
@ -13,7 +13,11 @@
|
||||
<!--
|
||||
Server configuration
|
||||
-->
|
||||
<servers>
|
||||
<server>
|
||||
<!-- Identifying name; default: "default" -->
|
||||
<name>Default</name>
|
||||
|
||||
<!-- Transport protocol: HTTP, HTTPS -->
|
||||
<protocol>HTTP</protocol>
|
||||
<!-- Server address -->
|
||||
@ -26,6 +30,9 @@
|
||||
<!-- Login password (check file permissions, or everyone can see this) -->
|
||||
<password>hackme</password>
|
||||
|
||||
<!-- Number of reconnection attempts, before giving up: -->
|
||||
<reconnect_attempts>0</reconnect_attempts>
|
||||
|
||||
<!--
|
||||
Configure TLS encryption requirement: none, may (default), required
|
||||
-->
|
||||
@ -54,37 +61,43 @@
|
||||
(with public key) and private key in the same file, for authentication.
|
||||
-->
|
||||
<client_cert>/etc/ssl/ezstream.crt</client_cert>
|
||||
|
||||
<!-- Number of reconnection attempts, before giving up: -->
|
||||
<reconnect_attempts>0</reconnect_attempts>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
<!--
|
||||
Stream configuration
|
||||
-->
|
||||
<streams>
|
||||
<stream>
|
||||
<!-- Identifying name; default: "default" -->
|
||||
<name>Default</name>
|
||||
|
||||
<!-- Mount point on server -->
|
||||
<mountpoint>/stream.ogg</mountpoint>
|
||||
|
||||
<!-- 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>
|
||||
<!-- Name of the server entry to use (default: "default") -->
|
||||
<server>Default</server>
|
||||
|
||||
<!-- Setting to allow stream to be listed in public YP directory -->
|
||||
<server_public>No</server_public>
|
||||
<public>No</public>
|
||||
|
||||
<!-- 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
|
||||
|
@ -8,15 +8,19 @@
|
||||
|
||||
<ezstream>
|
||||
|
||||
<servers>
|
||||
<server>
|
||||
<hostname>127.0.0.1</hostname>
|
||||
<password>hackme</password>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
<streams>
|
||||
<stream>
|
||||
<mountpoint>/stream.ogg</mountpoint>
|
||||
<format>Vorbis</format>
|
||||
</stream>
|
||||
</streams>
|
||||
|
||||
<media>
|
||||
<filename>playlist.m3u</filename>
|
||||
|
@ -8,15 +8,19 @@
|
||||
|
||||
<ezstream>
|
||||
|
||||
<servers>
|
||||
<server>
|
||||
<hostname>127.0.0.1</hostname>
|
||||
<password>hackme</password>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
<streams>
|
||||
<stream>
|
||||
<mountpoint>/stream.ogg</mountpoint>
|
||||
<format>Vorbis</format>
|
||||
</stream>
|
||||
</streams>
|
||||
|
||||
<media>
|
||||
<type>stdin</type>
|
||||
|
@ -6,16 +6,20 @@
|
||||
|
||||
<ezstream>
|
||||
|
||||
<servers>
|
||||
<server>
|
||||
<hostname>127.0.0.1</hostname>
|
||||
<password>hackme</password>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
<streams>
|
||||
<stream>
|
||||
<mountpoint>/video.ogg</mountpoint>
|
||||
<format>Theora</format>
|
||||
<encoder>PassThrough</encoder>
|
||||
</stream>
|
||||
</streams>
|
||||
|
||||
<media>
|
||||
<filename>playlist.m3u</filename>
|
||||
|
@ -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 \
|
||||
|
588
src/cfg.c
588
src/cfg.c
@ -32,26 +32,24 @@
|
||||
#include "log.h"
|
||||
#include "xalloc.h"
|
||||
|
||||
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
110
src/cfg.h
@ -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 *
|
||||
|
@ -39,26 +39,103 @@ 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;
|
||||
|
||||
if (!dl)
|
||||
return;
|
||||
|
||||
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_list_find(struct cfg_decoder_list *dl, const char *name)
|
||||
{
|
||||
struct cfg_decoder *d;
|
||||
|
||||
while (NULL != (d = TAILQ_FIRST(&cfg_decoders))) {
|
||||
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);
|
||||
|
||||
d = xcalloc(1UL, sizeof(*d));
|
||||
d->name = xstrdup(name);
|
||||
TAILQ_INIT(&d->exts);
|
||||
|
||||
return (d);
|
||||
}
|
||||
|
||||
void
|
||||
cfg_decoder_destroy(struct cfg_decoder **d_p)
|
||||
{
|
||||
struct cfg_decoder *d = *d_p;
|
||||
struct file_ext *e;
|
||||
|
||||
TAILQ_REMOVE(&cfg_decoders, d, entry);
|
||||
xfree(d->name);
|
||||
xfree(d->program);
|
||||
while (NULL != (e = TAILQ_FIRST(&d->exts))) {
|
||||
@ -67,51 +144,39 @@ cfg_decoder_exit(void)
|
||||
xfree(e);
|
||||
}
|
||||
xfree(d);
|
||||
}
|
||||
}
|
||||
|
||||
struct cfg_decoder *
|
||||
cfg_decoder_get(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);
|
||||
*d_p = NULL;
|
||||
}
|
||||
|
||||
int
|
||||
cfg_decoder_set_name(struct cfg_decoder *d, const char *name,
|
||||
const char **errstrp)
|
||||
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;
|
||||
|
||||
TAILQ_FOREACH(d, &cfg_decoders, entry) {
|
||||
struct file_ext *e;
|
||||
|
||||
TAILQ_FOREACH(e, &d->exts, entry) {
|
||||
if (0 == strcasecmp(e->ext, ext))
|
||||
return (d);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
const char *
|
||||
|
@ -18,21 +18,33 @@
|
||||
#define __CFG_INPUT_H__
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -18,18 +18,29 @@
|
||||
#define __CFG_ENCODER_H__
|
||||
|
||||
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 **);
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
#include "cfg.h"
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
@ -31,9 +33,7 @@
|
||||
#define DEFAULT_PORT 8000
|
||||
#define DEFAULT_USER "source"
|
||||
|
||||
struct cfg {
|
||||
int _master_cfg;
|
||||
struct program {
|
||||
struct cfg_program {
|
||||
char name[PATH_MAX];
|
||||
enum cfg_config_type config_type;
|
||||
char config_file[PATH_MAX];
|
||||
@ -41,34 +41,11 @@ struct cfg {
|
||||
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;
|
||||
};
|
||||
|
||||
struct cfg {
|
||||
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,7 +83,6 @@ struct cfg {
|
||||
*(e) = "empty"; \
|
||||
return (-1); \
|
||||
} \
|
||||
if ((t)) \
|
||||
xfree((t)); \
|
||||
(t) = xstrdup((s)); \
|
||||
} while (0)
|
||||
|
409
src/cfg_server.c
Normal file
409
src/cfg_server.c
Normal 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
105
src/cfg_server.h
Normal 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
410
src/cfg_stream.c
Normal 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
97
src/cfg_stream.h
Normal 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__ */
|
@ -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_list_t dl;
|
||||
cfg_decoder_t d;
|
||||
char d_id[11];
|
||||
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_list_t el;
|
||||
cfg_encoder_t e;
|
||||
char e_id[11];
|
||||
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
|
||||
* servers
|
||||
* server
|
||||
* name
|
||||
* protocol
|
||||
* hostname
|
||||
* port
|
||||
* user
|
||||
* password
|
||||
* reconnect_attempts
|
||||
* tls
|
||||
* tls_cipher_suite
|
||||
* ca_dir
|
||||
* ca_file
|
||||
* client_cert
|
||||
* reconnect_attempts
|
||||
* ...
|
||||
* streams
|
||||
* stream
|
||||
* mountpoint
|
||||
* name
|
||||
* url
|
||||
* genre
|
||||
* description
|
||||
* quality
|
||||
* bitrate
|
||||
* samplerate
|
||||
* channels
|
||||
* server_public
|
||||
* 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;
|
||||
}
|
||||
|
@ -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() &&
|
||||
|
219
src/stream.c
219
src/stream.c
@ -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;
|
||||
};
|
||||
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) {
|
||||
log_syserr(ALERT, ENOMEM, "shout_new");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
TAILQ_INSERT_TAIL(&streams, e, entry);
|
||||
|
||||
return (e);
|
||||
}
|
||||
|
||||
int
|
||||
stream_setup(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 = xcalloc(1UL, sizeof(*s));
|
||||
s->name = xstrdup(name);
|
||||
s->shout = shout_new();
|
||||
if (NULL == s->shout) {
|
||||
log_syserr(ALERT, ENOMEM, "shout_new");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
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_configure(struct stream *s)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
16
src/stream.h
16
src/stream.h
@ -19,20 +19,28 @@
|
||||
|
||||
#include <shout/shout.h>
|
||||
|
||||
#include "cfg.h"
|
||||
#include "mdata.h"
|
||||
|
||||
#define STREAM_DEFAULT "default"
|
||||
|
||||
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);
|
||||
|
@ -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 \
|
||||
|
@ -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
205
tests/check_cfg.h
Normal 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
192
tests/check_cfg_decoder.c
Normal 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
184
tests/check_cfg_encoder.c
Normal 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
248
tests/check_cfg_server.c
Normal 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
223
tests/check_cfg_stream.c
Normal 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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -11,82 +11,85 @@ void teardown_checked(void);
|
||||
|
||||
START_TEST(test_stream)
|
||||
{
|
||||
stream_t s1;
|
||||
stream_t s2;
|
||||
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;
|
||||
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
<ezstream>
|
||||
|
||||
<servers>
|
||||
<server>
|
||||
<name></name>
|
||||
<protocol></protocol>
|
||||
<hostname></hostname>
|
||||
<port></port>
|
||||
@ -15,21 +17,28 @@
|
||||
<client_cert></client_cert>
|
||||
<reconnect_attempts></reconnect_attempts>
|
||||
</server>
|
||||
<!-- ... -->
|
||||
</servers>
|
||||
|
||||
<streams>
|
||||
<stream>
|
||||
<mountpoint></mountpoint>
|
||||
<name></name>
|
||||
<url></url>
|
||||
<genre></genre>
|
||||
<description></description>
|
||||
<quality></quality>
|
||||
<bitrate></bitrate>
|
||||
<samplerate></samplerate>
|
||||
<channels></channels>
|
||||
<server_public></server_public>
|
||||
<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
55
tests/config-bad5.xml
Normal 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>
|
Loading…
Reference in New Issue
Block a user