1
0
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:
Moritz Grimm 2016-02-29 15:24:55 +01:00
commit 57749b4d73
35 changed files with 1150 additions and 372 deletions

View File

@ -240,7 +240,6 @@ dnl #######################
AC_CHECK_FUNCS([ \
arc4random \
basename \
gettimeofday \
nl_langinfo \
pclose \
popen \

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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()) {

View File

@ -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 *)&currentTime);
clock_gettime(CLOCK_MONOTONIC, &currentTime);
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

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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));
/*

View File

@ -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__ */

View File

@ -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);
}

View File

@ -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__ */

View File

@ -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);
}

View File

@ -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
View File

@ -0,0 +1,2 @@
#!/bin/sh
exit 1

View File

@ -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);

View File

@ -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
View 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
View 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
View 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
View 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);
}

View 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>

View 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>

View 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

Binary file not shown.

BIN
tests/integration/test2.ogg Normal file

Binary file not shown.

View File

@ -0,0 +1,2 @@
test1.ogg
test2.ogg

3
tests/play-bad.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "$(cat playlist-bad.txt)"

3
tests/play-bad2.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
echo ""

3
tests/play-bad3.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
exit 0

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

File diff suppressed because one or more lines are too long

7
tests/playlist.txt Normal file
View File

@ -0,0 +1,7 @@
1.ogg
2.ogg
# This is a comment
3.ogg
4.ogg
5.ogg