mirror of
https://gitlab.xiph.org/xiph/ezstream.git
synced 2024-11-03 04:17:18 -05:00
Merge branch 'develop' into feature/https_support
This commit is contained in:
commit
57749b4d73
@ -240,7 +240,6 @@ dnl #######################
|
||||
AC_CHECK_FUNCS([ \
|
||||
arc4random \
|
||||
basename \
|
||||
gettimeofday \
|
||||
nl_langinfo \
|
||||
pclose \
|
||||
popen \
|
||||
|
@ -49,6 +49,10 @@ LOGFILE="ezstream.log"
|
||||
|
||||
_myname="$(basename $0)"
|
||||
|
||||
# Ignore SIGHUP, and exit 1 on SIGINT and SIGTERM
|
||||
trap '' 1
|
||||
trap 'exit 1' 2 15
|
||||
|
||||
# Check configuration above:
|
||||
if [ -z "${STATE_DIR}" -o ! -d "${STATE_DIR}" ]; then
|
||||
echo "${_myname}: STATE_DIR is not configured, does not exist or is not a directory." >&2
|
||||
@ -67,8 +71,8 @@ if [ $? -ne 0 ]; then
|
||||
echo "${_myname}: Unable to create temporary file." >&2
|
||||
exit 1
|
||||
fi
|
||||
# Delete temporary file on exit
|
||||
trap 'rm -f ${_playlist}' 0
|
||||
trap 'rm -f ${_playlist}; exit 1' 2 15
|
||||
|
||||
# Strip comments and empty lines from PLAYLIST, to support .m3u:
|
||||
sed -e 's,#.*,,g' < ${PLAYLIST} | grep -v '^[[:space:]]*$' >> ${_playlist}
|
||||
@ -90,13 +94,6 @@ if [ -z "${_track_no}" ]; then
|
||||
_track_no=1
|
||||
fi
|
||||
|
||||
# Count number of tracks in the playlist:
|
||||
_num_tracks="$(wc -l < ${_playlist})"
|
||||
if [ ${_num_tracks} -eq 0 ]; then
|
||||
# Nothing to do, really.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Handle the end-of-playlist case:
|
||||
if [ ${_track_no} -gt ${_num_tracks} ]; then
|
||||
if [ ${REPEAT} -ne 1 ]; then
|
||||
|
85
src/cfg.c
85
src/cfg.c
@ -63,6 +63,8 @@ _cfg_reset(struct cfg *c)
|
||||
static void
|
||||
_cfg_copy(struct cfg *dst, struct cfg *src)
|
||||
{
|
||||
dst->_master_cfg = src->_master_cfg;
|
||||
|
||||
memcpy(&dst->program, &src->program, sizeof(dst->program));
|
||||
|
||||
memcpy(&dst->server, &src->server, sizeof(dst->server));
|
||||
@ -121,6 +123,7 @@ int
|
||||
cfg_init(void)
|
||||
{
|
||||
_cfg_reset(&cfg);
|
||||
cfg._master_cfg = 1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -130,18 +133,68 @@ cfg_exit(void)
|
||||
_cfg_reset(&cfg);
|
||||
}
|
||||
|
||||
void
|
||||
cfg_save(void)
|
||||
{
|
||||
_cfg_reset(&cfg_tmp);
|
||||
_cfg_copy(&cfg_tmp, &cfg);
|
||||
}
|
||||
|
||||
void
|
||||
cfg_pop(void)
|
||||
{
|
||||
if (!cfg_tmp._master_cfg)
|
||||
return;
|
||||
_cfg_reset(&cfg);
|
||||
_cfg_copy(&cfg, &cfg_tmp);
|
||||
_cfg_reset(&cfg_tmp);
|
||||
}
|
||||
|
||||
void
|
||||
cfg_clear(void)
|
||||
{
|
||||
_cfg_reset(&cfg_tmp);
|
||||
}
|
||||
|
||||
int
|
||||
cfg_reload(void)
|
||||
{
|
||||
_cfg_copy(&cfg_tmp, &cfg);
|
||||
cfg_save();
|
||||
if (0 > _cfg_load()) {
|
||||
/* roll back */
|
||||
_cfg_reset(&cfg);
|
||||
_cfg_copy(&cfg, &cfg_tmp);
|
||||
_cfg_reset(&cfg_tmp);
|
||||
cfg_pop();
|
||||
return (-1);
|
||||
}
|
||||
cfg_clear();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
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()) {
|
||||
if (NULL != errstrp)
|
||||
*errstrp = "media filename missing";
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (CFG_STREAM_INVALID == cfg_get_stream_format()) {
|
||||
if (NULL != errstrp)
|
||||
*errstrp = "stream format missing or unsupported";
|
||||
return (-1);
|
||||
}
|
||||
_cfg_reset(&cfg_tmp);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -607,7 +660,7 @@ cfg_get_server_protocol_str(void)
|
||||
const char *
|
||||
cfg_get_server_hostname(void)
|
||||
{
|
||||
return (cfg.server.hostname);
|
||||
return (cfg.server.hostname[0] ? cfg.server.hostname : NULL);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
@ -625,31 +678,31 @@ cfg_get_server_user(void)
|
||||
const char *
|
||||
cfg_get_server_password(void)
|
||||
{
|
||||
return (cfg.server.password);
|
||||
return (cfg.server.password[0] ? cfg.server.password : NULL);
|
||||
}
|
||||
|
||||
const char *
|
||||
cfg_get_server_ca_dir(void)
|
||||
{
|
||||
return (cfg.server.ca_dir);
|
||||
return (cfg.server.ca_dir[0] ? cfg.server.ca_dir : NULL);
|
||||
}
|
||||
|
||||
const char *
|
||||
cfg_get_server_ca_file(void)
|
||||
{
|
||||
return (cfg.server.ca_file);
|
||||
return (cfg.server.ca_file[0] ? cfg.server.ca_file : NULL);
|
||||
}
|
||||
|
||||
const char *
|
||||
cfg_get_server_client_cert(void)
|
||||
{
|
||||
return (cfg.server.client_cert);
|
||||
return (cfg.server.client_cert[0] ? cfg.server.client_cert : NULL);
|
||||
}
|
||||
|
||||
const char *
|
||||
cfg_get_server_client_key(void)
|
||||
{
|
||||
return (cfg.server.client_key);
|
||||
return (cfg.server.client_key[0] ? cfg.server.client_key : NULL);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
@ -724,6 +777,12 @@ 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)
|
||||
{
|
||||
@ -739,7 +798,7 @@ cfg_get_media_type(void)
|
||||
const char *
|
||||
cfg_get_media_filename(void)
|
||||
{
|
||||
return (cfg.media.filename);
|
||||
return (cfg.media.filename[0] ? cfg.media.filename : NULL);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -65,6 +65,12 @@ enum cfg_stream_format {
|
||||
int cfg_init(void);
|
||||
void cfg_exit(void);
|
||||
|
||||
void cfg_save(void);
|
||||
void cfg_pop(void);
|
||||
void cfg_clear(void);
|
||||
|
||||
int cfg_check(const char **);
|
||||
|
||||
int cfg_reload(void);
|
||||
|
||||
int cfg_stream_str2fmt(const char *, enum cfg_stream_format *);
|
||||
@ -170,6 +176,8 @@ const char *
|
||||
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);
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#define DEFAULT_USER "source"
|
||||
|
||||
struct cfg {
|
||||
int _master_cfg;
|
||||
struct program {
|
||||
char name[PATH_MAX];
|
||||
enum cfg_config_type config_type;
|
||||
|
@ -153,7 +153,7 @@ cmdline_parse(int argc, char *argv[], int *ret_p)
|
||||
cfg_set_program_verbosity(verbosity, NULL);
|
||||
|
||||
if (playlistFile) {
|
||||
playlist_t *pl;
|
||||
playlist_t pl;
|
||||
const char *entry;
|
||||
|
||||
if (0 > playlist_init()) {
|
||||
|
@ -41,26 +41,26 @@
|
||||
#define STREAM_SERVERR 3
|
||||
#define STREAM_UPDMDATA 4
|
||||
|
||||
playlist_t *playlist = NULL;
|
||||
int playlistMode = 0;
|
||||
unsigned int resource_errors = 0;
|
||||
playlist_t playlist;
|
||||
int playlistMode;
|
||||
unsigned int resource_errors;
|
||||
|
||||
#ifdef HAVE_SIGNALS
|
||||
const int ezstream_signals[] = {
|
||||
SIGTERM, SIGINT, SIGHUP, SIGUSR1, SIGUSR2
|
||||
};
|
||||
|
||||
volatile sig_atomic_t rereadPlaylist = 0;
|
||||
volatile sig_atomic_t rereadPlaylist_notify = 0;
|
||||
volatile sig_atomic_t skipTrack = 0;
|
||||
volatile sig_atomic_t queryMetadata = 0;
|
||||
volatile sig_atomic_t quit = 0;
|
||||
volatile sig_atomic_t rereadPlaylist;
|
||||
volatile sig_atomic_t rereadPlaylist_notify;
|
||||
volatile sig_atomic_t skipTrack;
|
||||
volatile sig_atomic_t queryMetadata;
|
||||
volatile sig_atomic_t quit;
|
||||
#else
|
||||
int rereadPlaylist = 0;
|
||||
int rereadPlaylist_notify = 0;
|
||||
int skipTrack = 0;
|
||||
int queryMetadata = 0;
|
||||
int quit = 0;
|
||||
int rereadPlaylist;
|
||||
int rereadPlaylist_notify;
|
||||
int skipTrack;
|
||||
int queryMetadata;
|
||||
int quit;
|
||||
#endif /* HAVE_SIGNALS */
|
||||
|
||||
typedef struct tag_ID3Tag {
|
||||
@ -85,7 +85,7 @@ FILE * openResource(shout_t *, const char *, int *, metadata_t **,
|
||||
int reconnectServer(shout_t *, int);
|
||||
const char * getTimeString(long);
|
||||
int sendStream(shout_t *, FILE *, const char *, int, const char *,
|
||||
struct timeval *);
|
||||
struct timespec *);
|
||||
int streamFile(shout_t *, const char *);
|
||||
int streamPlaylist(shout_t *);
|
||||
int ez_shutdown(int);
|
||||
@ -699,19 +699,19 @@ getTimeString(long seconds)
|
||||
|
||||
int
|
||||
sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
|
||||
int isStdin, const char *songLenStr, struct timeval *tv)
|
||||
int isStdin, const char *songLenStr, struct timespec *tv)
|
||||
{
|
||||
unsigned char buff[4096];
|
||||
size_t bytes_read, total, oldTotal;
|
||||
int ret;
|
||||
double kbps = -1.0;
|
||||
struct timeval timeStamp, *startTime = tv;
|
||||
struct timeval callTime, currentTime;
|
||||
struct timespec timeStamp, *startTime = tv;
|
||||
struct timespec callTime, currentTime;
|
||||
|
||||
ez_gettimeofday((void *)&callTime);
|
||||
clock_gettime(CLOCK_MONOTONIC, &callTime);
|
||||
|
||||
timeStamp.tv_sec = startTime->tv_sec;
|
||||
timeStamp.tv_usec = startTime->tv_usec;
|
||||
timeStamp.tv_nsec = startTime->tv_nsec;
|
||||
|
||||
total = oldTotal = 0;
|
||||
ret = STREAM_DONE;
|
||||
@ -747,7 +747,7 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
|
||||
break;
|
||||
}
|
||||
|
||||
ez_gettimeofday((void *)¤tTime);
|
||||
clock_gettime(CLOCK_MONOTONIC, ¤tTime);
|
||||
|
||||
if (queryMetadata ||
|
||||
(0 <= cfg_get_metadata_refresh_interval() &&
|
||||
@ -776,9 +776,9 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
|
||||
}
|
||||
|
||||
oldTime = (double)timeStamp.tv_sec
|
||||
+ (double)timeStamp.tv_usec / 1000000.0;
|
||||
+ (double)timeStamp.tv_nsec / 1000000000.0;
|
||||
newTime = (double)currentTime.tv_sec
|
||||
+ (double)currentTime.tv_usec / 1000000.0;
|
||||
+ (double)currentTime.tv_nsec / 1000000000.0;
|
||||
if (songLenStr == NULL)
|
||||
printf(" [ %s]",
|
||||
getTimeString(currentTime.tv_sec -
|
||||
@ -792,7 +792,7 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
|
||||
kbps = (((double)(total - oldTotal)
|
||||
/ (newTime - oldTime)) * 8.0) / 1000.0;
|
||||
timeStamp.tv_sec = currentTime.tv_sec;
|
||||
timeStamp.tv_usec = currentTime.tv_usec;
|
||||
timeStamp.tv_nsec = currentTime.tv_nsec;
|
||||
oldTotal = total;
|
||||
}
|
||||
if (kbps < 0)
|
||||
@ -828,7 +828,7 @@ streamFile(shout_t *shout, const char *fileName)
|
||||
int ret, retval = 0;
|
||||
long songLen;
|
||||
metadata_t *mdata;
|
||||
struct timeval startTime;
|
||||
struct timespec startTime;
|
||||
|
||||
if ((filepstream = openResource(shout, fileName, &popenFlag, &mdata, &isStdin, &songLen))
|
||||
== NULL) {
|
||||
@ -861,7 +861,7 @@ streamFile(shout_t *shout, const char *fileName)
|
||||
|
||||
if (songLen > 0)
|
||||
songLenStr = xstrdup(getTimeString(songLen));
|
||||
ez_gettimeofday((void *)&startTime);
|
||||
clock_gettime(CLOCK_MONOTONIC, &startTime);
|
||||
do {
|
||||
ret = sendStream(shout, filepstream, fileName, isStdin,
|
||||
songLenStr, &startTime);
|
||||
@ -935,15 +935,22 @@ streamPlaylist(shout_t *shout)
|
||||
char lastSong[PATH_MAX];
|
||||
|
||||
if (playlist == NULL) {
|
||||
if (CFG_MEDIA_PROGRAM == cfg_get_media_type()) {
|
||||
switch (cfg_get_media_type()) {
|
||||
case CFG_MEDIA_PROGRAM:
|
||||
if ((playlist = playlist_program(cfg_get_media_filename())) == NULL)
|
||||
return (0);
|
||||
} else {
|
||||
break;
|
||||
case CFG_MEDIA_STDIN:
|
||||
if ((playlist = playlist_read(NULL)) == NULL)
|
||||
return (0);
|
||||
break;
|
||||
default:
|
||||
if ((playlist = playlist_read(cfg_get_media_filename())) == NULL)
|
||||
return (0);
|
||||
if (playlist_get_num_items(playlist) == 0)
|
||||
log_notice("%s: playlist empty",
|
||||
log_warning("%s: playlist empty",
|
||||
cfg_get_media_filename());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@ -1002,6 +1009,7 @@ int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
const char *errstr;
|
||||
shout_t *shout;
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
@ -1020,30 +1028,12 @@ main(int argc, char *argv[])
|
||||
return (ez_shutdown(ret));
|
||||
shout_init();
|
||||
|
||||
if (!cfg_get_server_hostname() ||
|
||||
!cfg_get_server_port()){
|
||||
log_error("%s: missing server configuration",
|
||||
cfg_get_program_config_file());
|
||||
if (0 > cfg_check(&errstr)) {
|
||||
log_error("%s: %s", cfg_get_program_config_file(), errstr);
|
||||
return (ez_shutdown(2));
|
||||
}
|
||||
if (!cfg_get_server_password()) {
|
||||
log_error("%s: <sourcepassword> missing",
|
||||
cfg_get_program_config_file());
|
||||
return (ez_shutdown(2));
|
||||
}
|
||||
if (!cfg_get_media_filename()) {
|
||||
log_error("%s: <filename> missing",
|
||||
cfg_get_program_config_file());
|
||||
return (ez_shutdown(2));
|
||||
}
|
||||
if (CFG_STREAM_INVALID == cfg_get_stream_format()) {
|
||||
log_error("%s: <format> missing or unsupported value",
|
||||
cfg_get_program_config_file());
|
||||
}
|
||||
|
||||
shout = stream_setup(cfg_get_server_hostname(),
|
||||
cfg_get_server_port(), cfg_get_stream_mountpoint());
|
||||
if (shout == NULL)
|
||||
if (NULL == (shout = stream_setup()))
|
||||
return (ez_shutdown(1));
|
||||
|
||||
#ifdef HAVE_SIGNALS
|
||||
|
76
src/log.c
76
src/log.c
@ -22,30 +22,32 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "cfg.h"
|
||||
#include "log.h"
|
||||
|
||||
static void _log(enum log_levels, const char *, ...)
|
||||
static int _log(enum log_levels, const char *, ...)
|
||||
ATTRIBUTE_NONNULL(2)
|
||||
ATTRIBUTE_FORMAT(printf, 2, 3);
|
||||
static void _vlog(enum log_levels, const char *, va_list)
|
||||
static int _vlog(enum log_levels, const char *, va_list)
|
||||
ATTRIBUTE_NONNULL(2);
|
||||
|
||||
static void
|
||||
static int
|
||||
_log(enum log_levels lvl, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_vlog(lvl, fmt, ap);
|
||||
ret = _vlog(lvl, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
_vlog(enum log_levels lvl, const char *fmt, va_list ap)
|
||||
{
|
||||
va_list ap2;
|
||||
@ -63,18 +65,18 @@ _vlog(enum log_levels lvl, const char *fmt, va_list ap)
|
||||
break;
|
||||
case NOTICE:
|
||||
if (cfg_get_program_verbosity() < 1)
|
||||
return;
|
||||
return (0);
|
||||
p = LOG_NOTICE;
|
||||
break;
|
||||
case INFO:
|
||||
if (cfg_get_program_verbosity() < 2)
|
||||
return;
|
||||
return (0);
|
||||
p = LOG_INFO;
|
||||
break;
|
||||
case DEBUG:
|
||||
default:
|
||||
if (cfg_get_program_verbosity() < 3)
|
||||
return;
|
||||
return (0);
|
||||
p = LOG_DEBUG;
|
||||
break;
|
||||
};
|
||||
@ -82,6 +84,8 @@ _vlog(enum log_levels lvl, const char *fmt, va_list ap)
|
||||
va_copy(ap2, ap);
|
||||
vsyslog(p, fmt, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
@ -99,75 +103,97 @@ log_exit(void)
|
||||
closelog();
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
log_syserr(enum log_levels lvl, int error, const char *pfx)
|
||||
{
|
||||
char errbuf[1024];
|
||||
int ret;
|
||||
|
||||
if (0 != strerror_r(error, errbuf, sizeof(errbuf)))
|
||||
abort();
|
||||
_log(lvl, "%s%s%s",
|
||||
if (!error)
|
||||
return (0);
|
||||
(void)snprintf(errbuf, sizeof(errbuf), "%s", strerror(error));
|
||||
ret = _log(lvl, "%s%s%s",
|
||||
pfx ? pfx : "",
|
||||
pfx ? ": " : "",
|
||||
errbuf);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
log_alert(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_vlog(ALERT, fmt, ap);
|
||||
ret = _vlog(ALERT, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
log_error(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_vlog(ERROR, fmt, ap);
|
||||
ret = _vlog(ERROR, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
log_warning(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_vlog(WARNING, fmt, ap);
|
||||
ret = _vlog(WARNING, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
log_notice(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_vlog(NOTICE, fmt, ap);
|
||||
ret = _vlog(NOTICE, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
log_info(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_vlog(INFO, fmt, ap);
|
||||
ret = _vlog(INFO, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
log_debug(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, fmt);
|
||||
_vlog(DEBUG, fmt, ap);
|
||||
ret = _vlog(DEBUG, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
14
src/log.h
14
src/log.h
@ -31,24 +31,24 @@ enum log_levels {
|
||||
int log_init(void);
|
||||
void log_exit(void);
|
||||
|
||||
void log_syserr(enum log_levels, int, const char *);
|
||||
int log_syserr(enum log_levels, int, const char *);
|
||||
|
||||
void log_alert(const char *, ...)
|
||||
int log_alert(const char *, ...)
|
||||
ATTRIBUTE_NONNULL(1)
|
||||
ATTRIBUTE_FORMAT(printf, 1, 2);
|
||||
void log_error(const char *, ...)
|
||||
int log_error(const char *, ...)
|
||||
ATTRIBUTE_NONNULL(1)
|
||||
ATTRIBUTE_FORMAT(printf, 1, 2);
|
||||
void log_warning(const char *, ...)
|
||||
int log_warning(const char *, ...)
|
||||
ATTRIBUTE_NONNULL(1)
|
||||
ATTRIBUTE_FORMAT(printf, 1, 2);
|
||||
void log_notice(const char *, ...)
|
||||
int log_notice(const char *, ...)
|
||||
ATTRIBUTE_NONNULL(1)
|
||||
ATTRIBUTE_FORMAT(printf, 1, 2);
|
||||
void log_info(const char *, ...)
|
||||
int log_info(const char *, ...)
|
||||
ATTRIBUTE_NONNULL(1)
|
||||
ATTRIBUTE_FORMAT(printf, 1, 2);
|
||||
void log_debug(const char *, ...)
|
||||
int log_debug(const char *, ...)
|
||||
ATTRIBUTE_NONNULL(1)
|
||||
ATTRIBUTE_FORMAT(printf, 1, 2);
|
||||
|
||||
|
@ -18,10 +18,21 @@
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "ezstream.h"
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#if defined(HAVE_LIBGEN_H) && !defined(__linux__)
|
||||
# include <libgen.h>
|
||||
#endif /* HAVE_LIBGEN_H && !__linux__ */
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_TAGLIB
|
||||
# include <taglib/tag_c.h>
|
||||
#endif /* HAVE_TAGLIB */
|
||||
@ -30,8 +41,6 @@
|
||||
#endif /* HAVE_VORBISFILE */
|
||||
#include <shout/shout.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "metadata.h"
|
||||
#include "util.h"
|
||||
@ -353,42 +362,24 @@ metadata_t *
|
||||
metadata_program(const char *program, int normalize)
|
||||
{
|
||||
metadata_t *md;
|
||||
#ifdef HAVE_STAT
|
||||
struct stat st;
|
||||
#else
|
||||
FILE *filep;
|
||||
#endif
|
||||
|
||||
md = metadata_create(program);
|
||||
md->program = 1;
|
||||
md->string = xstrdup("");
|
||||
|
||||
#ifdef HAVE_STAT
|
||||
if (stat(program, &st) == -1) {
|
||||
log_error("%s: %s", program, strerror(errno));
|
||||
metadata_free(&md);
|
||||
return (NULL);
|
||||
}
|
||||
if (st.st_mode & (S_IWGRP | S_IWOTH)) {
|
||||
log_error("%s: group and/or world writeable",
|
||||
program);
|
||||
metadata_free(&md);
|
||||
if (st.st_mode & S_IWOTH) {
|
||||
log_error("%s: world writeable", program);
|
||||
return (NULL);
|
||||
}
|
||||
if (!(st.st_mode & (S_IEXEC | S_IXGRP | S_IXOTH))) {
|
||||
log_error("%s: not an executable program", program);
|
||||
metadata_free(&md);
|
||||
return (NULL);
|
||||
}
|
||||
#else
|
||||
if ((filep = fopen(program, "r")) == NULL) {
|
||||
log_error("%s: %s", program, strerror(errno));
|
||||
metadata_free(&md);
|
||||
return (NULL);
|
||||
}
|
||||
fclose(filep);
|
||||
#endif /* HAVE_STAT */
|
||||
|
||||
md = metadata_create(program);
|
||||
md->program = 1;
|
||||
md->string = xstrdup("");
|
||||
md->normalize = normalize;
|
||||
|
||||
return (md);
|
||||
|
144
src/playlist.c
144
src/playlist.c
@ -18,7 +18,15 @@
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "ezstream.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "playlist.h"
|
||||
@ -39,24 +47,24 @@ struct playlist {
|
||||
char *prog_track;
|
||||
};
|
||||
|
||||
static playlist_t * playlist_create(const char *);
|
||||
static int playlist_add(playlist_t *, const char *);
|
||||
static unsigned int playlist_random(void);
|
||||
static const char * playlist_run_program(playlist_t *);
|
||||
static struct playlist * _playlist_create(const char *);
|
||||
static int _playlist_add(struct playlist *, const char *);
|
||||
static unsigned int _playlist_random(void);
|
||||
static const char * _playlist_run_program(struct playlist *);
|
||||
|
||||
static playlist_t *
|
||||
playlist_create(const char *filename)
|
||||
static struct playlist *
|
||||
_playlist_create(const char *filename)
|
||||
{
|
||||
playlist_t *pl;
|
||||
struct playlist *pl;
|
||||
|
||||
pl = xcalloc(1UL, sizeof(playlist_t));
|
||||
pl = xcalloc(1UL, sizeof(*pl));
|
||||
pl->filename = xstrdup(filename);
|
||||
|
||||
return (pl);
|
||||
}
|
||||
|
||||
static int
|
||||
playlist_add(playlist_t *pl, const char *entry)
|
||||
_playlist_add(struct playlist *pl, const char *entry)
|
||||
{
|
||||
size_t num;
|
||||
|
||||
@ -84,7 +92,7 @@ playlist_add(playlist_t *pl, const char *entry)
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
playlist_random(void)
|
||||
_playlist_random(void)
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
|
||||
@ -100,13 +108,10 @@ playlist_random(void)
|
||||
}
|
||||
|
||||
static const char *
|
||||
playlist_run_program(playlist_t *pl)
|
||||
_playlist_run_program(struct playlist *pl)
|
||||
{
|
||||
FILE *filep;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if (!pl->program)
|
||||
return (NULL);
|
||||
char buf[PATH_MAX + 1];
|
||||
|
||||
fflush(NULL);
|
||||
errno = 0;
|
||||
@ -121,21 +126,13 @@ playlist_run_program(playlist_t *pl)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (fgets(buf, (int)sizeof(buf), filep) == NULL) {
|
||||
int errnum = errno;
|
||||
|
||||
fgets(buf, (int)sizeof(buf), filep);
|
||||
if (ferror(filep)) {
|
||||
log_error("%s: output read error: %s", pl->filename,
|
||||
strerror(ferror(filep)));
|
||||
pclose(filep);
|
||||
|
||||
if (ferror(filep)) {
|
||||
log_alert("%s: output read error: %s", pl->filename,
|
||||
strerror(errnum));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* No output (end of playlist.) */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pclose(filep);
|
||||
|
||||
if (strlen(buf) == sizeof(buf) - 1) {
|
||||
@ -149,8 +146,7 @@ playlist_run_program(playlist_t *pl)
|
||||
/* Empty line (end of playlist.) */
|
||||
return (NULL);
|
||||
|
||||
if (pl->prog_track != NULL)
|
||||
xfree(pl->prog_track);
|
||||
xfree(pl->prog_track);
|
||||
pl->prog_track = xstrdup(buf);
|
||||
|
||||
return ((const char *)pl->prog_track);
|
||||
@ -173,16 +169,16 @@ playlist_init(void)
|
||||
|
||||
void playlist_exit(void) {}
|
||||
|
||||
playlist_t *
|
||||
struct playlist *
|
||||
playlist_read(const char *filename)
|
||||
{
|
||||
playlist_t *pl;
|
||||
struct playlist *pl;
|
||||
unsigned long line;
|
||||
FILE *filep;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if (filename != NULL) {
|
||||
pl = playlist_create(filename);
|
||||
pl = _playlist_create(filename);
|
||||
|
||||
if ((filep = fopen(filename, "r")) == NULL) {
|
||||
log_error("%s: %s", filename, strerror(errno));
|
||||
@ -190,7 +186,7 @@ playlist_read(const char *filename)
|
||||
return (NULL);
|
||||
}
|
||||
} else {
|
||||
pl = playlist_create("stdin");
|
||||
pl = _playlist_create("stdin");
|
||||
|
||||
if ((filep = fdopen(STDIN_FILENO, "r")) == NULL) {
|
||||
log_error("stdin: %s", strerror(errno));
|
||||
@ -233,7 +229,7 @@ playlist_read(const char *filename)
|
||||
continue;
|
||||
|
||||
/* We got one. */
|
||||
if (!playlist_add(pl, buf)) {
|
||||
if (!_playlist_add(pl, buf)) {
|
||||
fclose(filep);
|
||||
playlist_free(&pl);
|
||||
return (NULL);
|
||||
@ -251,53 +247,36 @@ playlist_read(const char *filename)
|
||||
return (pl);
|
||||
}
|
||||
|
||||
playlist_t *
|
||||
struct playlist *
|
||||
playlist_program(const char *filename)
|
||||
{
|
||||
playlist_t *pl;
|
||||
#ifdef HAVE_STAT
|
||||
struct playlist *pl;
|
||||
struct stat st;
|
||||
#else
|
||||
FILE *filep;
|
||||
#endif
|
||||
|
||||
pl = playlist_create(filename);
|
||||
pl->program = 1;
|
||||
|
||||
#ifdef HAVE_STAT
|
||||
if (stat(filename, &st) == -1) {
|
||||
log_error("%s: %s", filename, strerror(errno));
|
||||
playlist_free(&pl);
|
||||
return (NULL);
|
||||
}
|
||||
if (st.st_mode & (S_IWGRP | S_IWOTH)) {
|
||||
log_error("%s: group and/or world writeable",
|
||||
filename);
|
||||
playlist_free(&pl);
|
||||
if (st.st_mode & S_IWOTH) {
|
||||
log_error("%s: world writeable", filename);
|
||||
return (NULL);
|
||||
}
|
||||
if (!(st.st_mode & (S_IEXEC | S_IXGRP | S_IXOTH))) {
|
||||
log_error("%s: not an executable program", filename);
|
||||
playlist_free(&pl);
|
||||
return (NULL);
|
||||
}
|
||||
#else
|
||||
if ((filep = fopen(filename, "r")) == NULL) {
|
||||
log_error("%s: %s", filename, strerror(errno));
|
||||
playlist_free(&pl);
|
||||
return (NULL);
|
||||
}
|
||||
fclose(filep);
|
||||
#endif /* HAVE_STAT */
|
||||
|
||||
pl = _playlist_create(filename);
|
||||
pl->program = 1;
|
||||
|
||||
return (pl);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_free(playlist_t **pl_p)
|
||||
playlist_free(struct playlist **pl_p)
|
||||
{
|
||||
size_t i;
|
||||
playlist_t *pl;
|
||||
struct playlist *pl;
|
||||
|
||||
if (pl_p == NULL || (pl = *pl_p) == NULL)
|
||||
return;
|
||||
@ -327,13 +306,14 @@ playlist_free(playlist_t **pl_p)
|
||||
}
|
||||
|
||||
xfree(*pl_p);
|
||||
*pl_p = NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
playlist_get_next(playlist_t *pl)
|
||||
playlist_get_next(struct playlist *pl)
|
||||
{
|
||||
if (pl->program)
|
||||
return (playlist_run_program(pl));
|
||||
return (_playlist_run_program(pl));
|
||||
|
||||
if (pl->num == 0)
|
||||
return (NULL);
|
||||
@ -341,17 +321,8 @@ playlist_get_next(playlist_t *pl)
|
||||
return ((const char *)pl->list[pl->index++]);
|
||||
}
|
||||
|
||||
const char *
|
||||
playlist_peek_next(playlist_t *pl)
|
||||
{
|
||||
if (pl->program || pl->num == 0)
|
||||
return (NULL);
|
||||
|
||||
return ((const char *)pl->list[pl->index]);
|
||||
}
|
||||
|
||||
void
|
||||
playlist_skip_next(playlist_t *pl)
|
||||
playlist_skip_next(struct playlist *pl)
|
||||
{
|
||||
if (pl->program || pl->num == 0)
|
||||
return;
|
||||
@ -361,7 +332,7 @@ playlist_skip_next(playlist_t *pl)
|
||||
}
|
||||
|
||||
unsigned long
|
||||
playlist_get_num_items(playlist_t *pl)
|
||||
playlist_get_num_items(struct playlist *pl)
|
||||
{
|
||||
if (pl->program)
|
||||
return (0);
|
||||
@ -370,7 +341,7 @@ playlist_get_num_items(playlist_t *pl)
|
||||
}
|
||||
|
||||
unsigned long
|
||||
playlist_get_position(playlist_t *pl)
|
||||
playlist_get_position(struct playlist *pl)
|
||||
{
|
||||
if (pl->program)
|
||||
return (0);
|
||||
@ -379,18 +350,7 @@ playlist_get_position(playlist_t *pl)
|
||||
}
|
||||
|
||||
int
|
||||
playlist_set_position(playlist_t *pl, unsigned long idx)
|
||||
{
|
||||
if (pl->program || idx > pl->num - 1)
|
||||
return (0);
|
||||
|
||||
pl->index = (size_t)idx;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
playlist_goto_entry(playlist_t *pl, const char *entry)
|
||||
playlist_goto_entry(struct playlist *pl, const char *entry)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
@ -408,7 +368,7 @@ playlist_goto_entry(playlist_t *pl, const char *entry)
|
||||
}
|
||||
|
||||
void
|
||||
playlist_rewind(playlist_t *pl)
|
||||
playlist_rewind(struct playlist *pl)
|
||||
{
|
||||
if (pl->program)
|
||||
return;
|
||||
@ -417,9 +377,9 @@ playlist_rewind(playlist_t *pl)
|
||||
}
|
||||
|
||||
int
|
||||
playlist_reread(playlist_t **plist)
|
||||
playlist_reread(struct playlist **plist)
|
||||
{
|
||||
playlist_t *new_pl, *pl;
|
||||
struct playlist *new_pl, *pl;
|
||||
|
||||
pl = *plist;
|
||||
|
||||
@ -439,7 +399,7 @@ playlist_reread(playlist_t **plist)
|
||||
* Yet another implementation of the "Knuth Shuffle":
|
||||
*/
|
||||
void
|
||||
playlist_shuffle(playlist_t *pl)
|
||||
playlist_shuffle(struct playlist *pl)
|
||||
{
|
||||
size_t d, i, range;
|
||||
char *temp;
|
||||
@ -455,7 +415,7 @@ playlist_shuffle(playlist_t *pl)
|
||||
* largest multiple of our range. This reduces PRNG bias.
|
||||
*/
|
||||
do {
|
||||
d = (unsigned long)playlist_random();
|
||||
d = (unsigned long)_playlist_random();
|
||||
} while (d > RAND_MAX - (RAND_MAX % range));
|
||||
|
||||
/*
|
||||
|
@ -17,7 +17,7 @@
|
||||
#ifndef __PLAYLIST_H__
|
||||
#define __PLAYLIST_H__
|
||||
|
||||
typedef struct playlist playlist_t;
|
||||
typedef struct playlist * playlist_t;
|
||||
|
||||
/*
|
||||
* Initialize the playlist routines. Should be called before any of the other
|
||||
@ -34,71 +34,59 @@ void playlist_exit(void);
|
||||
* Read a playlist file (in .M3U format), and return a new playlist handler
|
||||
* on success, or NULL on failure.
|
||||
*/
|
||||
playlist_t * playlist_read(const char * /* filename */);
|
||||
playlist_t playlist_read(const char * /* filename */);
|
||||
|
||||
/*
|
||||
* For each call to playlist_get_next(), the specified program is run. This
|
||||
* program is supposed to print one line to standard output, containing the
|
||||
* path and filename of a media file.
|
||||
*/
|
||||
playlist_t * playlist_program(const char * /* program name */);
|
||||
playlist_t playlist_program(const char * /* program name */);
|
||||
|
||||
/*
|
||||
* Free all memory used by a playlist handler that was created with
|
||||
* playlist_read().
|
||||
*/
|
||||
void playlist_free(playlist_t **);
|
||||
void playlist_free(playlist_t *);
|
||||
|
||||
/*
|
||||
* Get the next item in the playlist. Returns a NUL-terminated string of a
|
||||
* playlist entry, or NULL if the end of the list has been reached.
|
||||
*/
|
||||
const char * playlist_get_next(playlist_t *);
|
||||
const char * playlist_get_next(playlist_t);
|
||||
|
||||
/*
|
||||
* The functions below work on playlist handlers obtained with playlist_read()
|
||||
* and are no-ops (i.e. return failure) for playlists from playlist_program().
|
||||
*/
|
||||
|
||||
/*
|
||||
* Get the next item in the playlist without moving on to the following entry.
|
||||
* Returns a NUL-terminated string of the next playlist entry, or NULL if the
|
||||
* currently playing song is the last one in the list.
|
||||
*/
|
||||
const char * playlist_peek_next(playlist_t *);
|
||||
|
||||
/*
|
||||
* Skip the playlist item that would be played next.
|
||||
*/
|
||||
void playlist_skip_next(playlist_t *);
|
||||
void playlist_skip_next(playlist_t);
|
||||
|
||||
/*
|
||||
* Get the number of items in the playlist.
|
||||
*/
|
||||
unsigned long playlist_get_num_items(playlist_t *);
|
||||
unsigned long playlist_get_num_items(playlist_t);
|
||||
|
||||
/*
|
||||
* Get the current position in the playlist.
|
||||
*/
|
||||
unsigned long playlist_get_position(playlist_t *);
|
||||
|
||||
/*
|
||||
* Set a position in the playlist. Returns 1 on success, and 0 on failure.
|
||||
*/
|
||||
int playlist_set_position(playlist_t *, unsigned long /* index */);
|
||||
unsigned long playlist_get_position(playlist_t);
|
||||
|
||||
/*
|
||||
* Search for a given entry in the playlist and reposition to it. Returns 1 on
|
||||
* success and 0 on failure. A subsequent call to playlist_get_next() will
|
||||
* return this list item again.
|
||||
*/
|
||||
int playlist_goto_entry(playlist_t *, const char * /* name */);
|
||||
int playlist_goto_entry(playlist_t, const char * /* name */);
|
||||
|
||||
/*
|
||||
* Rewind the playlist to the beginning, so that it can be replayed. Does
|
||||
* not reread the playlist file.
|
||||
*/
|
||||
void playlist_rewind(playlist_t *);
|
||||
void playlist_rewind(playlist_t);
|
||||
|
||||
/*
|
||||
* Reread the playlist file and rewind to the beginning. Equivalent to calling
|
||||
@ -106,11 +94,11 @@ void playlist_rewind(playlist_t *);
|
||||
* after calling this function.
|
||||
* Returns 1 on success, and 0 on error.
|
||||
*/
|
||||
int playlist_reread(playlist_t **);
|
||||
int playlist_reread(playlist_t *);
|
||||
|
||||
/*
|
||||
* Shuffle the entries of the playlist randomly.
|
||||
*/
|
||||
void playlist_shuffle(playlist_t *);
|
||||
void playlist_shuffle(playlist_t);
|
||||
|
||||
#endif /* __PLAYLIST_H__ */
|
||||
|
228
src/util.c
228
src/util.c
@ -82,139 +82,149 @@ strrcasecmp(const char *s, const char *sub)
|
||||
}
|
||||
|
||||
shout_t *
|
||||
stream_setup(const char *host, unsigned short port, const char *mount)
|
||||
stream_setup(void)
|
||||
{
|
||||
shout_t *shout = NULL;
|
||||
shout_t *shout;
|
||||
|
||||
if ((shout = shout_new()) == NULL) {
|
||||
log_syserr(ERROR, ENOMEM, "shout_new");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (shout_set_host(shout, host) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_host: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
switch (cfg_get_server_protocol()) {
|
||||
case CFG_PROTO_HTTP:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_protocol(shout, SHOUT_PROTOCOL_HTTP)) {
|
||||
log_error("shout_set_protocol: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("unsupported protocol: %s",
|
||||
cfg_get_server_protocol_str());
|
||||
goto error;
|
||||
}
|
||||
if (shout_set_protocol(shout, SHOUT_PROTOCOL_HTTP) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_protocol: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_host(shout, cfg_get_server_hostname())) {
|
||||
log_error("shout_set_host: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (shout_set_port(shout, port) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_port: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_port(shout, (unsigned short)cfg_get_server_port())) {
|
||||
log_error("shout_set_port: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (shout_set_password(shout, cfg_get_server_password()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_password: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_user(shout, cfg_get_server_user())) {
|
||||
log_error("shout_set_user: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (shout_set_mount(shout, mount) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_mount: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
}
|
||||
if (shout_set_user(shout, "source") != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_user: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_password(shout, cfg_get_server_password())) {
|
||||
log_error("shout_set_password: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (CFG_STREAM_MP3 == cfg_get_stream_format() &&
|
||||
shout_set_format(shout, SHOUT_FORMAT_MP3) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_format(MP3): %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
}
|
||||
if ((CFG_STREAM_VORBIS == cfg_get_stream_format() ||
|
||||
CFG_STREAM_THEORA == cfg_get_stream_format()) &&
|
||||
shout_set_format(shout, SHOUT_FORMAT_OGG) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_format(OGG): %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_mount(shout, cfg_get_stream_mountpoint())) {
|
||||
log_error("shout_set_mount: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (shout_set_user(shout, cfg_get_server_user()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_user: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
switch (cfg_get_stream_format()) {
|
||||
case CFG_STREAM_VORBIS:
|
||||
case CFG_STREAM_THEORA:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_format(shout, SHOUT_FORMAT_OGG)) {
|
||||
log_error("shout_set_format: OGG: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case CFG_STREAM_MP3:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_format(shout, SHOUT_FORMAT_MP3)) {
|
||||
log_error("shout_set_format: MP3: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("unsupported stream format: %s",
|
||||
cfg_get_stream_format_str());
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (cfg_get_stream_name() &&
|
||||
shout_set_name(shout, cfg_get_stream_name()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_name: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_name(shout, cfg_get_stream_name())) {
|
||||
log_error("shout_set_name: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_url() &&
|
||||
shout_set_url(shout, cfg_get_stream_url()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_url: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_url(shout, cfg_get_stream_url())) {
|
||||
log_error("shout_set_url: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_genre() &&
|
||||
shout_set_genre(shout, cfg_get_stream_genre()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_genre: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_genre(shout, cfg_get_stream_genre())) {
|
||||
log_error("shout_set_genre: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_description() &&
|
||||
shout_set_description(shout, cfg_get_stream_description()) != SHOUTERR_SUCCESS) {
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_description(shout, cfg_get_stream_description())) {
|
||||
log_error("shout_set_description: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
}
|
||||
if (cfg_get_stream_bitrate() &&
|
||||
shout_set_audio_info(shout, SHOUT_AI_BITRATE, cfg_get_stream_bitrate()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_audio_info(AI_BITRATE): %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
}
|
||||
if (cfg_get_stream_channels() &&
|
||||
shout_set_audio_info(shout, SHOUT_AI_CHANNELS, cfg_get_stream_channels()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_audio_info(AI_CHANNELS): %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
}
|
||||
if (cfg_get_stream_samplerate() &&
|
||||
shout_set_audio_info(shout, SHOUT_AI_SAMPLERATE, cfg_get_stream_samplerate()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_audio_info(AI_SAMPLERATE): %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_quality() &&
|
||||
shout_set_audio_info(shout, SHOUT_AI_QUALITY, cfg_get_stream_quality()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_audio_info(AI_QUALITY): %s",
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(shout, SHOUT_AI_QUALITY,
|
||||
cfg_get_stream_quality())) {
|
||||
log_error("shout_set_audio_info: QUALITY: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_bitrate() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(shout, SHOUT_AI_BITRATE,
|
||||
cfg_get_stream_bitrate())) {
|
||||
log_error("shout_set_audio_info: BITRATE: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_samplerate() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(shout, SHOUT_AI_SAMPLERATE,
|
||||
cfg_get_stream_samplerate())) {
|
||||
log_error("shout_set_audio_info: SAMPLERATE: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_channels() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(shout, SHOUT_AI_CHANNELS,
|
||||
cfg_get_stream_channels())) {
|
||||
log_error("shout_set_audio_info: CHANNELS: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (shout_set_public(shout, (unsigned int)cfg_get_stream_server_public()) != SHOUTERR_SUCCESS) {
|
||||
log_error("shout_set_public: %s",
|
||||
shout_get_error(shout));
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_public(shout, (unsigned int)cfg_get_stream_server_public())) {
|
||||
log_error("shout_set_public: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
|
||||
return (shout);
|
||||
|
||||
error:
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
@ -348,21 +358,3 @@ iconvert(const char *in_str, const char *from, const char *to, int mode)
|
||||
return (xstrdup(in_str));
|
||||
#endif /* HAVE_ICONV */
|
||||
}
|
||||
|
||||
int
|
||||
ez_gettimeofday(void *tp_arg)
|
||||
{
|
||||
struct timeval *tp = (struct timeval *)tp_arg;
|
||||
int ret = -1;
|
||||
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
ret = gettimeofday(tp, NULL);
|
||||
#else /* HAVE_GETTIMEOFDAY */
|
||||
/* Fallback to time(): */
|
||||
tp->tv_sec = (long)time(NULL);
|
||||
tp->tv_usec = 0;
|
||||
ret = 0;
|
||||
#endif /* HAVE_GETTIMEOFDAY */
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
@ -24,9 +24,8 @@
|
||||
|
||||
int strrcmp(const char *, const char *);
|
||||
int strrcasecmp(const char *, const char *);
|
||||
shout_t * stream_setup(const char *, unsigned short, const char *);
|
||||
shout_t * stream_setup(void);
|
||||
char * CHARtoUTF8(const char *, int);
|
||||
char * UTF8toCHAR(const char *, int);
|
||||
int ez_gettimeofday(void *);
|
||||
|
||||
#endif /* __UTIL_H__ */
|
||||
|
@ -75,7 +75,7 @@ xstrdup_c(const char *str, const char *file, unsigned int line)
|
||||
char *ret = strdup(str);
|
||||
|
||||
if (NULL == ret) {
|
||||
log_error("%s[%u]: cannot allocate %zu bytes",
|
||||
log_alert("%s[%u]: cannot allocate %zu bytes",
|
||||
file, line, strlen(str) + 1);
|
||||
exit(1);
|
||||
}
|
||||
|
@ -2,7 +2,11 @@ AUTOMAKE_OPTIONS = 1.10 foreign subdir-objects
|
||||
|
||||
TESTS = \
|
||||
check_cfg \
|
||||
check_cfg_xmlfile
|
||||
check_cfg_xmlfile \
|
||||
check_cmdline \
|
||||
check_log \
|
||||
check_playlist \
|
||||
check_xalloc
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
check_cfg_SOURCES = \
|
||||
@ -15,6 +19,26 @@ check_cfg_xmlfile_SOURCES = \
|
||||
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_DEPENDENCIES = $(top_builddir)/src/libezstream.la
|
||||
check_cmdline_LDADD = $(check_cmdline_DEPENDENCIES) @CHECK_LIBS@
|
||||
|
||||
check_log_SOURCES = \
|
||||
check_log.c
|
||||
check_log_DEPENDENCIES = $(top_builddir)/src/libezstream.la
|
||||
check_log_LDADD = $(check_log_DEPENDENCIES) @CHECK_LIBS@
|
||||
|
||||
check_playlist_SOURCES = \
|
||||
check_playlist.c
|
||||
check_playlist_DEPENDENCIES = $(top_builddir)/src/libezstream.la
|
||||
check_playlist_LDADD = $(check_playlist_DEPENDENCIES) @CHECK_LIBS@
|
||||
|
||||
check_xalloc_SOURCES = \
|
||||
check_xalloc.c
|
||||
check_xalloc_DEPENDENCIES = $(top_builddir)/src/libezstream.la
|
||||
check_xalloc_LDADD = $(check_xalloc_DEPENDENCIES) @CHECK_LIBS@
|
||||
|
||||
AM_CPPFLAGS = @EZ_CPPFLAGS@ \
|
||||
-I$(top_srcdir)/compat \
|
||||
-I$(top_srcdir)/src \
|
||||
@ -25,9 +49,16 @@ AM_CFLAGS = @EZ_CFLAGS@ @CHECK_CFLAGS@ \
|
||||
AM_LDFLAGS = @EZ_LDFLAGS@
|
||||
|
||||
EXTRA_DIST = \
|
||||
bad-executable.sh \
|
||||
config-bad.xml \
|
||||
config-bad2.xml \
|
||||
config-bad3.xml \
|
||||
config-bad4.xml
|
||||
config-bad4.xml \
|
||||
play-bad.sh \
|
||||
play-bad2.sh \
|
||||
play-bad3.sh \
|
||||
playlist-bad.txt \
|
||||
playlist-bad2.txt \
|
||||
playlist.txt
|
||||
|
||||
CLEANFILES = *~ *.core core *.gcno *.gcda
|
||||
|
2
tests/bad-executable.sh
Executable file
2
tests/bad-executable.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exit 1
|
@ -100,6 +100,54 @@ 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_pop();
|
||||
ck_assert_str_eq(cfg_get_stream_name(), "test_stash");
|
||||
cfg_pop();
|
||||
ck_assert_str_eq(cfg_get_stream_name(), "test_stash");
|
||||
cfg_save();
|
||||
cfg_clear();
|
||||
cfg_pop();
|
||||
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_filename(SRCDIR "/playlist.txt", 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);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_stream_str2fmt)
|
||||
{
|
||||
enum cfg_stream_format fmt;
|
||||
@ -347,6 +395,8 @@ START_TEST(test_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
|
||||
|
||||
@ -727,6 +777,8 @@ cfg_suite(void)
|
||||
|
||||
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);
|
||||
tcase_add_test(tc_core, test_file_check);
|
||||
|
@ -43,9 +43,9 @@ cfg_xmlfile_suite(void)
|
||||
Suite *s;
|
||||
TCase *tc_xmlcfg;
|
||||
|
||||
s = suite_create("XmlConfig");
|
||||
s = suite_create("Config");
|
||||
|
||||
tc_xmlcfg = tcase_create("Reload");
|
||||
tc_xmlcfg = tcase_create("XmlConfigFile");
|
||||
tcase_add_checked_fixture(tc_xmlcfg, setup_checked, teardown_checked);
|
||||
tcase_add_test(tc_xmlcfg, test_reload);
|
||||
suite_add_tcase(s, tc_xmlcfg);
|
||||
|
182
tests/check_cmdline.c
Normal file
182
tests/check_cmdline.c
Normal file
@ -0,0 +1,182 @@
|
||||
#include <check.h>
|
||||
|
||||
#include "cfg.h"
|
||||
#include "cmdline.h"
|
||||
|
||||
Suite * cmdline_suite(void);
|
||||
void setup_checked(void);
|
||||
void teardown_checked(void);
|
||||
|
||||
extern int optind;
|
||||
|
||||
START_TEST(test_configfile)
|
||||
{
|
||||
char *argv[] =
|
||||
{
|
||||
"check_cmdline", "-c", EXAMPLESDIR "/ezstream-full.xml" , NULL
|
||||
};
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
ck_assert_int_eq(cmdline_parse(argc, argv, &ret), 0);
|
||||
ck_assert_int_eq(ret, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_help)
|
||||
{
|
||||
char *argv[] = { "check_cmdline", "-h", NULL };
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
|
||||
int ret;
|
||||
|
||||
ck_assert_int_ne(cmdline_parse(argc, argv, &ret), 0);
|
||||
ck_assert_int_eq(ret, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_quiet_stderr)
|
||||
{
|
||||
char *argv[] = { "check_cmdline", "-q", NULL };
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
|
||||
int ret;
|
||||
|
||||
ck_assert_int_eq(cfg_get_program_quiet_stderr(), 0);
|
||||
ck_assert_int_ne(cmdline_parse(argc, argv, &ret), 0);
|
||||
ck_assert_int_eq(ret, 2);
|
||||
ck_assert_int_ne(cfg_get_program_quiet_stderr(), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_rtstatus_output)
|
||||
{
|
||||
char *argv[] = { "check_cmdline", "-r", NULL };
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
|
||||
int ret;
|
||||
|
||||
ck_assert_int_eq(cfg_get_program_rtstatus_output(), 0);
|
||||
ck_assert_int_eq(cfg_get_program_quiet_stderr(), 0);
|
||||
ck_assert_int_ne(cmdline_parse(argc, argv, &ret), 0);
|
||||
ck_assert_int_eq(ret, 2);
|
||||
ck_assert_int_ne(cfg_get_program_rtstatus_output(), 0);
|
||||
ck_assert_int_ne(cfg_get_program_quiet_stderr(), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_shuffle)
|
||||
{
|
||||
char *argv[] =
|
||||
{
|
||||
"check_cmdline", "-s", SRCDIR "/playlist.txt", NULL
|
||||
};
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
|
||||
int ret;
|
||||
|
||||
ck_assert_int_ne(cmdline_parse(argc, argv, &ret), 0);
|
||||
ck_assert_int_eq(ret, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_version)
|
||||
{
|
||||
char *argv[] = { "check_cmdline", "-V", NULL };
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
|
||||
int ret;
|
||||
|
||||
ck_assert_int_ne(cmdline_parse(argc, argv, &ret), 0);
|
||||
ck_assert_int_eq(ret, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_verbose)
|
||||
{
|
||||
char *argv[] = { "check_cmdline", "-v", NULL };
|
||||
char *argv2[] = { "check_cmdline", "-v", "-v", NULL };
|
||||
char *argv3[] = { "check_cmdline", "-v", "-v", "-v", NULL };
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
|
||||
int argc2 = (int)(sizeof(argv2) / sizeof(argv2[0])) - 1;
|
||||
int argc3 = (int)(sizeof(argv3) / sizeof(argv3[0])) - 1;
|
||||
int ret;
|
||||
|
||||
ck_assert_uint_eq(cfg_get_program_verbosity(), 0);
|
||||
ck_assert_int_ne(cmdline_parse(argc, argv, &ret), 0);
|
||||
ck_assert_uint_eq(cfg_get_program_verbosity(), 1);
|
||||
|
||||
optind = 1;
|
||||
ck_assert_int_ne(cmdline_parse(argc2, argv2, &ret), 0);
|
||||
ck_assert_uint_eq(cfg_get_program_verbosity(), 2);
|
||||
|
||||
optind = 1;
|
||||
ck_assert_int_ne(cmdline_parse(argc3, argv3, &ret), 0);
|
||||
ck_assert_uint_eq(cfg_get_program_verbosity(), 3);
|
||||
|
||||
ck_assert_int_eq(ret, 2);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_invalid)
|
||||
{
|
||||
char *argv[] = { "check_cmdline", "-x", NULL };
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
|
||||
int ret;
|
||||
|
||||
ck_assert_int_ne(cmdline_parse(argc, argv, &ret), 0);
|
||||
ck_assert_int_eq(ret, 2);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *
|
||||
cmdline_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *tc_cmdline;
|
||||
|
||||
s = suite_create("CmdLine");
|
||||
|
||||
tc_cmdline = tcase_create("CmdLine");
|
||||
tcase_add_checked_fixture(tc_cmdline, setup_checked, teardown_checked);
|
||||
tcase_add_test(tc_cmdline, test_configfile);
|
||||
tcase_add_test(tc_cmdline, test_help);
|
||||
tcase_add_test(tc_cmdline, test_quiet_stderr);
|
||||
tcase_add_test(tc_cmdline, test_rtstatus_output);
|
||||
tcase_add_test(tc_cmdline, test_shuffle);
|
||||
tcase_add_test(tc_cmdline, test_version);
|
||||
tcase_add_test(tc_cmdline, test_verbose);
|
||||
tcase_add_test(tc_cmdline, test_invalid);
|
||||
suite_add_tcase(s, tc_cmdline);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
void
|
||||
setup_checked(void)
|
||||
{
|
||||
if (0 < cfg_init())
|
||||
ck_abort_msg("setup_checked failed");
|
||||
optind = 1;
|
||||
}
|
||||
|
||||
void
|
||||
teardown_checked(void)
|
||||
{
|
||||
cfg_exit();
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
unsigned int num_failed;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = cmdline_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);
|
||||
}
|
95
tests/check_log.c
Normal file
95
tests/check_log.c
Normal file
@ -0,0 +1,95 @@
|
||||
#include <check.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "cfg.h"
|
||||
#include "log.h"
|
||||
|
||||
Suite * log_suite(void);
|
||||
void setup_checked(void);
|
||||
void teardown_checked(void);
|
||||
|
||||
START_TEST(test_log)
|
||||
{
|
||||
unsigned int verbosity;
|
||||
|
||||
ck_assert_int_eq(log_syserr(ALERT, 0, "alert"), 0);
|
||||
|
||||
verbosity = 0;
|
||||
ck_assert_int_eq(cfg_set_program_verbosity(verbosity, NULL), 0);
|
||||
ck_assert_int_ne(log_alert("alert"), 0);
|
||||
ck_assert_int_ne(log_syserr(ALERT, EINVAL, "alert"), 0);
|
||||
ck_assert_int_ne(log_syserr(ALERT, EINVAL, NULL), 0);
|
||||
ck_assert_int_ne(log_error("error"), 0);
|
||||
ck_assert_int_ne(log_syserr(ERROR, EINVAL, "error"), 0);
|
||||
ck_assert_int_ne(log_warning("warning"), 0);
|
||||
ck_assert_int_ne(log_syserr(WARNING, EINVAL, "warning"), 0);
|
||||
|
||||
ck_assert_int_eq(log_notice("notice"), 0);
|
||||
ck_assert_int_eq(log_syserr(NOTICE, EINVAL, "notice"), 0);
|
||||
ck_assert_int_eq(cfg_set_program_verbosity(++verbosity, NULL), 0);
|
||||
ck_assert_int_ne(log_notice("notice"), 0);
|
||||
ck_assert_int_ne(log_syserr(NOTICE, EINVAL, "notice"), 0);
|
||||
|
||||
ck_assert_int_eq(log_info("info"), 0);
|
||||
ck_assert_int_eq(log_syserr(INFO, EINVAL, "info"), 0);
|
||||
ck_assert_int_eq(cfg_set_program_verbosity(++verbosity, NULL), 0);
|
||||
ck_assert_int_ne(log_info("info"), 0);
|
||||
ck_assert_int_ne(log_syserr(INFO, EINVAL, "info"), 0);
|
||||
|
||||
ck_assert_int_eq(log_debug("debug"), 0);
|
||||
ck_assert_int_eq(log_syserr(DEBUG, EINVAL, "debug"), 0);
|
||||
ck_assert_int_eq(cfg_set_program_verbosity(++verbosity, NULL), 0);
|
||||
ck_assert_int_ne(log_debug("debug"), 0);
|
||||
ck_assert_int_ne(log_syserr(DEBUG, EINVAL, "debug"), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *
|
||||
log_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *tc_log;
|
||||
|
||||
s = suite_create("Log");
|
||||
|
||||
tc_log = tcase_create("Log");
|
||||
tcase_add_checked_fixture(tc_log, setup_checked, teardown_checked);
|
||||
tcase_add_test(tc_log, test_log);
|
||||
suite_add_tcase(s, tc_log);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
void
|
||||
setup_checked(void)
|
||||
{
|
||||
if (0 < cfg_init() ||
|
||||
0 < log_init())
|
||||
ck_abort_msg("setup_checked failed");
|
||||
}
|
||||
|
||||
void
|
||||
teardown_checked(void)
|
||||
{
|
||||
log_exit();
|
||||
cfg_exit();
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
unsigned int num_failed;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = log_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);
|
||||
}
|
154
tests/check_playlist.c
Normal file
154
tests/check_playlist.c
Normal file
@ -0,0 +1,154 @@
|
||||
#include <check.h>
|
||||
|
||||
#include "playlist.h"
|
||||
|
||||
Suite * playlist_suite(void);
|
||||
void setup_checked(void);
|
||||
void teardown_checked(void);
|
||||
|
||||
START_TEST(test_playlist_file)
|
||||
{
|
||||
playlist_t p;
|
||||
|
||||
ck_assert_ptr_eq(playlist_read("nonexistent.txt"), NULL);
|
||||
p = playlist_read(SRCDIR "/playlist.txt");
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
ck_assert_uint_gt(playlist_get_num_items(p), 0);
|
||||
ck_assert_str_eq(playlist_get_next(p), "1.ogg");
|
||||
playlist_skip_next(p);
|
||||
ck_assert_uint_eq(playlist_get_position(p), 2);
|
||||
ck_assert_str_eq(playlist_get_next(p), "3.ogg");
|
||||
ck_assert_int_eq(playlist_goto_entry(p, "2.ogg"), 1);
|
||||
ck_assert_str_eq(playlist_get_next(p), "2.ogg");
|
||||
playlist_rewind(p);
|
||||
ck_assert_str_eq(playlist_get_next(p), "1.ogg");
|
||||
ck_assert_int_eq(playlist_goto_entry(p, "5.ogg"), 1);
|
||||
ck_assert_int_eq(playlist_goto_entry(p, "6.ogg"), 0);
|
||||
ck_assert_str_eq(playlist_get_next(p), "5.ogg");
|
||||
ck_assert_ptr_eq(playlist_get_next(p), NULL);
|
||||
ck_assert_int_eq(playlist_reread(&p), 1);
|
||||
ck_assert_str_eq(playlist_get_next(p), "1.ogg");
|
||||
ck_assert_str_eq(playlist_get_next(p), "2.ogg");
|
||||
ck_assert_str_eq(playlist_get_next(p), "3.ogg");
|
||||
ck_assert_str_eq(playlist_get_next(p), "4.ogg");
|
||||
ck_assert_str_eq(playlist_get_next(p), "5.ogg");
|
||||
ck_assert_ptr_eq(playlist_get_next(p), NULL);
|
||||
playlist_shuffle(p);
|
||||
playlist_free(&p);
|
||||
|
||||
p = playlist_read(SRCDIR "/playlist-bad.txt");
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
ck_assert_ptr_eq(playlist_get_next(p), NULL);
|
||||
playlist_free(&p);
|
||||
|
||||
p = playlist_read(SRCDIR "/playlist-bad2.txt");
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
ck_assert_str_eq(playlist_get_next(p), "1.ogg");
|
||||
playlist_free(&p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_playlist_program)
|
||||
{
|
||||
playlist_t p;
|
||||
|
||||
ck_assert_ptr_eq(playlist_program("nonexistent.sh"), NULL);
|
||||
ck_assert_ptr_eq(playlist_program(SRCDIR "/playlist.txt"), NULL);
|
||||
|
||||
// p = playlist_program(SRCDIR "/bad-executable.sh");
|
||||
// ck_assert_ptr_ne(p, NULL);
|
||||
// ck_assert_ptr_eq(playlist_get_next(p), NULL);
|
||||
// playlist_free(&p);
|
||||
|
||||
p = playlist_program(EXAMPLESDIR "/play.sh");
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
ck_assert_str_eq(playlist_get_next(p),
|
||||
"Great_Artist_-_Great_Song.ogg");
|
||||
ck_assert_uint_eq(playlist_get_num_items(p), 0);
|
||||
ck_assert_uint_eq(playlist_get_position(p), 0);
|
||||
ck_assert_int_eq(playlist_goto_entry(p,
|
||||
"Great_Artist_-_Great_Song.ogg"), 0);
|
||||
ck_assert_int_eq(playlist_reread(&p), 0);
|
||||
playlist_rewind(p);
|
||||
playlist_shuffle(p);
|
||||
playlist_free(&p);
|
||||
|
||||
p = playlist_program(SRCDIR "/play-bad.sh");
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
ck_assert_ptr_eq(playlist_get_next(p), NULL);
|
||||
playlist_free(&p);
|
||||
|
||||
p = playlist_program(SRCDIR "/play-bad2.sh");
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
ck_assert_ptr_eq(playlist_get_next(p), NULL);
|
||||
playlist_free(&p);
|
||||
|
||||
p = playlist_program(SRCDIR "/play-bad3.sh");
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
ck_assert_ptr_eq(playlist_get_next(p), NULL);
|
||||
playlist_free(&p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_playlist_free)
|
||||
{
|
||||
playlist_t p;
|
||||
|
||||
p = playlist_read(SRCDIR "/playlist.txt");
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
playlist_free(&p);
|
||||
ck_assert_ptr_eq(p, NULL);
|
||||
playlist_free(&p);
|
||||
playlist_free(NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *
|
||||
playlist_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *tc_playlist;
|
||||
|
||||
s = suite_create("Playlist");
|
||||
|
||||
tc_playlist = tcase_create("Playlist");
|
||||
tcase_add_checked_fixture(tc_playlist, setup_checked, teardown_checked);
|
||||
tcase_add_test(tc_playlist, test_playlist_file);
|
||||
tcase_add_test(tc_playlist, test_playlist_program);
|
||||
tcase_add_test(tc_playlist, test_playlist_free);
|
||||
suite_add_tcase(s, tc_playlist);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
void
|
||||
setup_checked(void)
|
||||
{
|
||||
if (0 < playlist_init())
|
||||
ck_abort_msg("setup_checked failed");
|
||||
}
|
||||
|
||||
void
|
||||
teardown_checked(void)
|
||||
{
|
||||
playlist_exit();
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
unsigned int num_failed;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = playlist_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);
|
||||
}
|
107
tests/check_xalloc.c
Normal file
107
tests/check_xalloc.c
Normal file
@ -0,0 +1,107 @@
|
||||
#include <check.h>
|
||||
|
||||
#include "xalloc.h"
|
||||
|
||||
Suite * xalloc_suite(void);
|
||||
|
||||
START_TEST(test_malloc)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = xmalloc(0UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
p = xmalloc(1UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_calloc)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = xcalloc(0UL, 0UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
p = xcalloc(1UL, 0UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
p = xcalloc(0UL, 1UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
p = xcalloc(1UL, 1UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_reallocarray)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = xreallocarray(NULL, 0UL, 0UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
p = xreallocarray(NULL, 1UL, 0UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
p = xreallocarray(NULL, 0UL, 1UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
p = xreallocarray(NULL, 1UL, 1UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
p = xreallocarray(p, 2UL, 2UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
p = xreallocarray(p, 1UL, 1UL);
|
||||
ck_assert_ptr_ne(p, NULL);
|
||||
xfree(p);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_strdup)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = xstrdup("test");
|
||||
ck_assert_str_eq(s, "test");
|
||||
xfree(s);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *
|
||||
xalloc_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *tc_xalloc;
|
||||
|
||||
s = suite_create("Xalloc");
|
||||
|
||||
tc_xalloc = tcase_create("Xalloc");
|
||||
tcase_add_test(tc_xalloc, test_malloc);
|
||||
tcase_add_test(tc_xalloc, test_calloc);
|
||||
tcase_add_test(tc_xalloc, test_reallocarray);
|
||||
tcase_add_test(tc_xalloc, test_strdup);
|
||||
suite_add_tcase(s, tc_xalloc);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
unsigned int num_failed;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = xalloc_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);
|
||||
}
|
21
tests/integration/ezcfg-test1.xml
Normal file
21
tests/integration/ezcfg-test1.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<ezstream>
|
||||
|
||||
<server>
|
||||
<hostname>127.0.0.1</hostname>
|
||||
<port>34533</port>
|
||||
<password>ezstream</password>
|
||||
</server>
|
||||
|
||||
<stream>
|
||||
<mountpoint>/test1.ogg</mountpoint>
|
||||
<format>Vorbis</format>
|
||||
</stream>
|
||||
|
||||
<media>
|
||||
<filename>tests.m3u</filename>
|
||||
<stream_once>Yes</stream_once>
|
||||
</media>
|
||||
|
||||
</ezstream>
|
46
tests/integration/icecast.xml
Normal file
46
tests/integration/icecast.xml
Normal file
@ -0,0 +1,46 @@
|
||||
<icecast>
|
||||
<limits>
|
||||
<clients>100</clients>
|
||||
<sources>2</sources>
|
||||
<threadpool>5</threadpool>
|
||||
<queue-size>524288</queue-size>
|
||||
<client-timeout>30</client-timeout>
|
||||
<header-timeout>15</header-timeout>
|
||||
<source-timeout>10</source-timeout>
|
||||
<burst-on-connect>1</burst-on-connect>
|
||||
<burst-size>65535</burst-size>
|
||||
</limits>
|
||||
|
||||
<authentication>
|
||||
<source-password>ezstream</source-password>
|
||||
<relay-password>ezstream</relay-password>
|
||||
<admin-user>admin</admin-user>
|
||||
<admin-password>ezstream</admin-password>
|
||||
</authentication>
|
||||
|
||||
<hostname>127.0.0.1</hostname>
|
||||
|
||||
<listen-socket>
|
||||
<port>34533</port>
|
||||
<bind-address>127.0.0.1</bind-address>
|
||||
</listen-socket>
|
||||
|
||||
<mount>
|
||||
<mount-name>/usertest.ogg</mount-name>
|
||||
<username>ezstream</username>
|
||||
<password>test</password>
|
||||
</mount>
|
||||
|
||||
<fileserve>1</fileserve>
|
||||
|
||||
<paths>
|
||||
<logdir>/tmp</logdir>
|
||||
<!-- <webroot>/usr/share/icecast2/web</webroot> -->
|
||||
<!-- <adminroot>/usr/share/icecast2/admin</adminroot> -->
|
||||
<!-- <pidfile>/usr/share/icecast2/icecast.pid</pidfile> -->
|
||||
</paths>
|
||||
|
||||
<security>
|
||||
<chroot>0</chroot>
|
||||
</security>
|
||||
</icecast>
|
57
tests/integration/test-ezstream.rb
Executable file
57
tests/integration/test-ezstream.rb
Executable file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env ruby
|
||||
#
|
||||
# encoding: UTF-8
|
||||
|
||||
MYNAME = File.basename($PROGRAM_NAME, '.rb')
|
||||
EZSTREAM = '../../src/ezstream'
|
||||
ICECAST = 'icecast2'
|
||||
|
||||
require 'open3'
|
||||
require 'timeout'
|
||||
|
||||
def test_help
|
||||
STDERR.puts 'test_help'
|
||||
exit_status = 0
|
||||
Open3.popen3(EZSTREAM, '-h') do |i, o, e, wait_thr|
|
||||
exit_status = wait_thr.value.exitstatus
|
||||
end
|
||||
fail "exit_status: 0 != #{exit_status}" if exit_status != 0
|
||||
1
|
||||
rescue => e
|
||||
STDERR.puts 'test_help: ' + e.message
|
||||
0
|
||||
end
|
||||
|
||||
def test_stream
|
||||
STDERR.puts 'test_stream: http://127.0.0.1:34533/test1.ogg'
|
||||
exit_status = 0
|
||||
Open3.popen3(ICECAST, '-c', 'icecast.xml') do |ii, io, ie, icecast|
|
||||
sleep(2) # icecast needs time to boot
|
||||
Open3.popen3(EZSTREAM, '-v', '-v', '-v', '-c', 'ezcfg-test1.xml') do |ei, eo, ee, ezstream|
|
||||
exit_status += ezstream.value.exitstatus
|
||||
puts ee.read.chomp
|
||||
end
|
||||
sleep(1)
|
||||
Process.kill(15, icecast.pid)
|
||||
end
|
||||
fail "exit_status: 0 != #{exit_status}" if exit_status != 0
|
||||
1
|
||||
rescue => e
|
||||
STDERR.puts 'test_stream: ' + e.message
|
||||
0
|
||||
end
|
||||
|
||||
num_tests = 0
|
||||
num_passed = 0
|
||||
|
||||
num_tests += 1
|
||||
num_passed += test_help
|
||||
|
||||
num_tests += 1
|
||||
num_passed += test_stream
|
||||
|
||||
success_rate = num_passed.to_f.fdiv(num_tests.to_f) * 100
|
||||
|
||||
STDERR.puts "#{num_passed}/#{num_tests} passed (#{(success_rate.round)}%)"
|
||||
|
||||
exit num_tests == num_passed ? 0 : 1
|
BIN
tests/integration/test1.ogg
Normal file
BIN
tests/integration/test1.ogg
Normal file
Binary file not shown.
BIN
tests/integration/test2.ogg
Normal file
BIN
tests/integration/test2.ogg
Normal file
Binary file not shown.
2
tests/integration/tests.m3u
Normal file
2
tests/integration/tests.m3u
Normal file
@ -0,0 +1,2 @@
|
||||
test1.ogg
|
||||
test2.ogg
|
3
tests/play-bad.sh
Executable file
3
tests/play-bad.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "$(cat playlist-bad.txt)"
|
3
tests/play-bad2.sh
Executable file
3
tests/play-bad2.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo ""
|
3
tests/play-bad3.sh
Executable file
3
tests/play-bad3.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exit 0
|
1
tests/playlist-bad.txt
Normal file
1
tests/playlist-bad.txt
Normal file
File diff suppressed because one or more lines are too long
2
tests/playlist-bad2.txt
Normal file
2
tests/playlist-bad2.txt
Normal file
File diff suppressed because one or more lines are too long
7
tests/playlist.txt
Normal file
7
tests/playlist.txt
Normal file
@ -0,0 +1,7 @@
|
||||
1.ogg
|
||||
|
||||
2.ogg
|
||||
# This is a comment
|
||||
3.ogg
|
||||
4.ogg
|
||||
5.ogg
|
Loading…
Reference in New Issue
Block a user