mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-26 23:25:56 -05:00
Merge pull request #334 from sshambar/fixthreads
[threads] Added missing initializers, check errors on mutex/cond calls
This commit is contained in:
commit
8ffdd6dd86
@ -20,14 +20,13 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
struct command
|
struct command
|
||||||
{
|
{
|
||||||
@ -79,7 +78,7 @@ command_cb_sync(struct commands_base *cmdbase, struct command *cmd)
|
|||||||
{
|
{
|
||||||
enum command_state cmdstate;
|
enum command_state cmdstate;
|
||||||
|
|
||||||
pthread_mutex_lock(&cmd->lck);
|
CHECK_ERR(L_MAIN, pthread_mutex_lock(&cmd->lck));
|
||||||
|
|
||||||
cmdstate = cmd->func(cmd->arg, &cmd->ret);
|
cmdstate = cmd->func(cmd->arg, &cmd->ret);
|
||||||
if (cmdstate == COMMAND_PENDING)
|
if (cmdstate == COMMAND_PENDING)
|
||||||
@ -95,8 +94,8 @@ command_cb_sync(struct commands_base *cmdbase, struct command *cmd)
|
|||||||
cmd->func_bh(cmd->arg, &cmd->ret);
|
cmd->func_bh(cmd->arg, &cmd->ret);
|
||||||
|
|
||||||
// Signal the calling thread that the command execution finished
|
// Signal the calling thread that the command execution finished
|
||||||
pthread_cond_signal(&cmd->cond);
|
CHECK_ERR(L_MAIN, pthread_cond_signal(&cmd->cond));
|
||||||
pthread_mutex_unlock(&cmd->lck);
|
CHECK_ERR(L_MAIN, pthread_mutex_unlock(&cmd->lck));
|
||||||
|
|
||||||
event_add(cmdbase->command_event, NULL);
|
event_add(cmdbase->command_event, NULL);
|
||||||
}
|
}
|
||||||
@ -284,8 +283,8 @@ commands_exec_end(struct commands_base *cmdbase, int retvalue)
|
|||||||
{
|
{
|
||||||
cmdbase->current_cmd->func_bh(cmdbase->current_cmd->arg, &cmdbase->current_cmd->ret);
|
cmdbase->current_cmd->func_bh(cmdbase->current_cmd->arg, &cmdbase->current_cmd->ret);
|
||||||
}
|
}
|
||||||
pthread_cond_signal(&cmdbase->current_cmd->cond);
|
CHECK_ERR(L_MAIN, pthread_cond_signal(&cmdbase->current_cmd->cond));
|
||||||
pthread_mutex_unlock(&cmdbase->current_cmd->lck);
|
CHECK_ERR(L_MAIN, pthread_mutex_unlock(&cmdbase->current_cmd->lck));
|
||||||
|
|
||||||
cmdbase->current_cmd = NULL;
|
cmdbase->current_cmd = NULL;
|
||||||
|
|
||||||
@ -318,18 +317,25 @@ commands_exec_sync(struct commands_base *cmdbase, command_function func, command
|
|||||||
cmd.arg = arg;
|
cmd.arg = arg;
|
||||||
cmd.nonblock = 0;
|
cmd.nonblock = 0;
|
||||||
|
|
||||||
pthread_mutex_lock(&cmd.lck);
|
CHECK_ERR(L_MAIN, mutex_init(&cmd.lck));
|
||||||
|
CHECK_ERR(L_MAIN, pthread_cond_init(&cmd.cond, NULL));
|
||||||
|
|
||||||
|
CHECK_ERR(L_MAIN, pthread_mutex_lock(&cmd.lck));
|
||||||
|
|
||||||
ret = send_command(cmdbase, &cmd);
|
ret = send_command(cmdbase, &cmd);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_MAIN, "Error sending command\n");
|
DPRINTF(E_LOG, L_MAIN, "Error sending command\n");
|
||||||
pthread_mutex_unlock(&cmd.lck);
|
cmd.ret = -1;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CHECK_ERR(L_MAIN, pthread_cond_wait(&cmd.cond, &cmd.lck));
|
||||||
|
}
|
||||||
|
CHECK_ERR(L_MAIN, pthread_mutex_unlock(&cmd.lck));
|
||||||
|
|
||||||
pthread_cond_wait(&cmd.cond, &cmd.lck);
|
CHECK_ERR(L_MAIN, pthread_cond_destroy(&cmd.cond));
|
||||||
pthread_mutex_unlock(&cmd.lck);
|
CHECK_ERR(L_MAIN, pthread_mutex_destroy(&cmd.lck));
|
||||||
|
|
||||||
return cmd.ret;
|
return cmd.ret;
|
||||||
}
|
}
|
||||||
|
22
src/db.c
22
src/db.c
@ -35,8 +35,6 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
#include "conffile.h"
|
#include "conffile.h"
|
||||||
@ -532,12 +530,12 @@ unlock_notify_cb(void **args, int nargs)
|
|||||||
{
|
{
|
||||||
u = (struct db_unlock *)args[i];
|
u = (struct db_unlock *)args[i];
|
||||||
|
|
||||||
pthread_mutex_lock(&u->lck);
|
CHECK_ERR(L_DB, pthread_mutex_lock(&u->lck));
|
||||||
|
|
||||||
u->proceed = 1;
|
u->proceed = 1;
|
||||||
pthread_cond_signal(&u->cond);
|
CHECK_ERR(L_DB, pthread_cond_signal(&u->cond));
|
||||||
|
|
||||||
pthread_mutex_unlock(&u->lck);
|
CHECK_ERR(L_DB, pthread_mutex_unlock(&u->lck));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,25 +546,25 @@ db_wait_unlock(void)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
u.proceed = 0;
|
u.proceed = 0;
|
||||||
pthread_mutex_init(&u.lck, NULL);
|
CHECK_ERR(L_DB, mutex_init(&u.lck));
|
||||||
pthread_cond_init(&u.cond, NULL);
|
CHECK_ERR(L_DB, pthread_cond_init(&u.cond, NULL));
|
||||||
|
|
||||||
ret = sqlite3_unlock_notify(hdl, unlock_notify_cb, &u);
|
ret = sqlite3_unlock_notify(hdl, unlock_notify_cb, &u);
|
||||||
if (ret == SQLITE_OK)
|
if (ret == SQLITE_OK)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&u.lck);
|
CHECK_ERR(L_DB, pthread_mutex_lock(&u.lck));
|
||||||
|
|
||||||
if (!u.proceed)
|
if (!u.proceed)
|
||||||
{
|
{
|
||||||
DPRINTF(E_INFO, L_DB, "Waiting for database unlock\n");
|
DPRINTF(E_INFO, L_DB, "Waiting for database unlock\n");
|
||||||
pthread_cond_wait(&u.cond, &u.lck);
|
CHECK_ERR(L_DB, pthread_cond_wait(&u.cond, &u.lck));
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&u.lck);
|
CHECK_ERR(L_DB, pthread_mutex_unlock(&u.lck));
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_cond_destroy(&u.cond);
|
CHECK_ERR(L_DB, pthread_cond_destroy(&u.cond));
|
||||||
pthread_mutex_destroy(&u.lck);
|
CHECK_ERR(L_DB, pthread_mutex_destroy(&u.lck));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
90
src/logger.c
90
src/logger.c
@ -20,6 +20,8 @@
|
|||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@ -27,25 +29,31 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
|
|
||||||
#include <libavutil/log.h>
|
#include <libavutil/log.h>
|
||||||
|
|
||||||
#include "conffile.h"
|
#include "conffile.h"
|
||||||
#include "logger.h"
|
#include "misc.h"
|
||||||
|
|
||||||
|
static pthread_mutex_t logger_lck;
|
||||||
static pthread_mutex_t logger_lck = PTHREAD_MUTEX_INITIALIZER;
|
static int logger_initialized;
|
||||||
static int logdomains;
|
static int logdomains;
|
||||||
static int threshold;
|
static int threshold;
|
||||||
static int console;
|
static int console = 1;
|
||||||
static char *logfilename;
|
static char *logfilename;
|
||||||
static FILE *logfile;
|
static FILE *logfile;
|
||||||
static char *labels[] = { "config", "daap", "db", "httpd", "http", "main", "mdns", "misc", "rsp", "scan", "xcode", "event", "remote", "dacp", "ffmpeg", "artwork", "player", "raop", "laudio", "dmap", "dbperf", "spotify", "lastfm", "cache", "mpd", "stream", "cast", "fifo", "lib" };
|
static char *labels[] = { "config", "daap", "db", "httpd", "http", "main", "mdns", "misc", "rsp", "scan", "xcode", "event", "remote", "dacp", "ffmpeg", "artwork", "player", "raop", "laudio", "dmap", "dbperf", "spotify", "lastfm", "cache", "mpd", "stream", "cast", "fifo", "lib" };
|
||||||
static char *severities[] = { "FATAL", "LOG", "WARN", "INFO", "DEBUG", "SPAM" };
|
static char *severities[] = { "FATAL", "LOG", "WARN", "INFO", "DEBUG", "SPAM" };
|
||||||
|
|
||||||
|
/* We need our own check to avoid nested locking or recursive calls */
|
||||||
|
#define LOGGER_CHECK_ERR(f) \
|
||||||
|
do { int lerr; lerr = f; if (lerr != 0) { \
|
||||||
|
vlogger_fatal("%s failed at line %d, err %d (%s)\n", #f, __LINE__, \
|
||||||
|
lerr, strerror(lerr)); \
|
||||||
|
abort(); \
|
||||||
|
} } while(0)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
set_logdomains(char *domains)
|
set_logdomains(char *domains)
|
||||||
@ -80,24 +88,13 @@ set_logdomains(char *domains)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vlogger(int severity, int domain, const char *fmt, va_list args)
|
vlogger_writer(int severity, int domain, const char *fmt, va_list args)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char stamp[32];
|
char stamp[32];
|
||||||
time_t t;
|
time_t t;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!((1 << domain) & logdomains) || (severity > threshold))
|
|
||||||
return;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&logger_lck);
|
|
||||||
|
|
||||||
if (!logfile && !console)
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&logger_lck);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logfile)
|
if (logfile)
|
||||||
{
|
{
|
||||||
t = time(NULL);
|
t = time(NULL);
|
||||||
@ -122,8 +119,43 @@ vlogger(int severity, int domain, const char *fmt, va_list args)
|
|||||||
vfprintf(stderr, fmt, ap);
|
vfprintf(stderr, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&logger_lck);
|
static void
|
||||||
|
vlogger_fatal(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vlogger_writer(E_FATAL, L_MISC, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vlogger(int severity, int domain, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(! logger_initialized)
|
||||||
|
{
|
||||||
|
/* lock not initialized, use stderr */
|
||||||
|
vlogger_writer(severity, domain, fmt, args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!((1 << domain) & logdomains) || (severity > threshold))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOGGER_CHECK_ERR(pthread_mutex_lock(&logger_lck));
|
||||||
|
|
||||||
|
if (!logfile && !console)
|
||||||
|
{
|
||||||
|
LOGGER_CHECK_ERR(pthread_mutex_unlock(&logger_lck));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vlogger_writer(severity, domain, fmt, args);
|
||||||
|
|
||||||
|
LOGGER_CHECK_ERR(pthread_mutex_unlock(&logger_lck));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -204,7 +236,7 @@ logger_reinit(void)
|
|||||||
if (!logfile)
|
if (!logfile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pthread_mutex_lock(&logger_lck);
|
LOGGER_CHECK_ERR(pthread_mutex_lock(&logger_lck));
|
||||||
|
|
||||||
fp = fopen(logfilename, "a");
|
fp = fopen(logfilename, "a");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
@ -218,7 +250,7 @@ logger_reinit(void)
|
|||||||
logfile = fp;
|
logfile = fp;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
pthread_mutex_unlock(&logger_lck);
|
LOGGER_CHECK_ERR(pthread_mutex_unlock(&logger_lck));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -287,6 +319,11 @@ logger_init(char *file, char *domains, int severity)
|
|||||||
|
|
||||||
logfilename = file;
|
logfilename = file;
|
||||||
|
|
||||||
|
/* logging w/o locks before initialized complete */
|
||||||
|
CHECK_ERR(L_MISC, mutex_init(&logger_lck));
|
||||||
|
|
||||||
|
logger_initialized = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,5 +331,16 @@ void
|
|||||||
logger_deinit(void)
|
logger_deinit(void)
|
||||||
{
|
{
|
||||||
if (logfile)
|
if (logfile)
|
||||||
fclose(logfile);
|
{
|
||||||
|
fclose(logfile);
|
||||||
|
logfile = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(logger_initialized)
|
||||||
|
{
|
||||||
|
/* logging w/o locks to stderr now */
|
||||||
|
logger_initialized = 0;
|
||||||
|
console = 1;
|
||||||
|
CHECK_ERR(L_MISC, pthread_mutex_destroy(&logger_lck));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
24
src/main.c
24
src/main.c
@ -44,8 +44,6 @@
|
|||||||
# include <sys/event.h>
|
# include <sys/event.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <event2/event.h>
|
#include <event2/event.h>
|
||||||
#include <libavutil/log.h>
|
#include <libavutil/log.h>
|
||||||
@ -419,26 +417,29 @@ signal_kqueue_cb(int fd, short event, void *arg)
|
|||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ffmpeg_lockmgr(void **mutex, enum AVLockOp op)
|
ffmpeg_lockmgr(void **pmutex, enum AVLockOp op)
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case AV_LOCK_CREATE:
|
case AV_LOCK_CREATE:
|
||||||
*mutex = malloc(sizeof(pthread_mutex_t));
|
*pmutex = malloc(sizeof(pthread_mutex_t));
|
||||||
if (!*mutex)
|
if (!*pmutex)
|
||||||
return 1;
|
return 1;
|
||||||
|
CHECK_ERR(L_MAIN, mutex_init(*pmutex));
|
||||||
return !!pthread_mutex_init(*mutex, NULL);
|
return 0;
|
||||||
|
|
||||||
case AV_LOCK_OBTAIN:
|
case AV_LOCK_OBTAIN:
|
||||||
return !!pthread_mutex_lock(*mutex);
|
CHECK_ERR(L_MAIN, pthread_mutex_lock(*pmutex));
|
||||||
|
return 0;
|
||||||
|
|
||||||
case AV_LOCK_RELEASE:
|
case AV_LOCK_RELEASE:
|
||||||
return !!pthread_mutex_unlock(*mutex);
|
CHECK_ERR(L_MAIN, pthread_mutex_unlock(*pmutex));
|
||||||
|
return 0;
|
||||||
|
|
||||||
case AV_LOCK_DESTROY:
|
case AV_LOCK_DESTROY:
|
||||||
pthread_mutex_destroy(*mutex);
|
CHECK_ERR(L_MAIN, pthread_mutex_destroy(*pmutex));
|
||||||
free(*mutex);
|
free(*pmutex);
|
||||||
|
*pmutex = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -446,7 +447,6 @@ ffmpeg_lockmgr(void **mutex, enum AVLockOp op)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
32
src/misc.c
32
src/misc.c
@ -927,3 +927,35 @@ timespec_cmp(struct timespec time1, struct timespec time2)
|
|||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
mutex_init(pthread_mutex_t *mutex)
|
||||||
|
{
|
||||||
|
pthread_mutexattr_t mattr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
CHECK_ERR(L_MISC, pthread_mutexattr_init(&mattr));
|
||||||
|
CHECK_ERR(L_MISC, pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ERRORCHECK));
|
||||||
|
err = pthread_mutex_init(mutex, &mattr);
|
||||||
|
CHECK_ERR(L_MISC, pthread_mutexattr_destroy(&mattr));
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_fatal_err(int domain, const char *func, int line, int err) {
|
||||||
|
DPRINTF(E_FATAL, domain, "%s failed at line %d, error %d (%s)\n", func,
|
||||||
|
line, err, strerror(err));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_fatal_errno(int domain, const char *func, int line) {
|
||||||
|
DPRINTF(E_FATAL, domain, "%s failed at line %d, error %d (%s)\n", func,
|
||||||
|
line, errno, strerror(errno));
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_fatal_null(int domain, const char *func, int line) {
|
||||||
|
DPRINTF(E_FATAL, domain, "%s returned NULL at line %d\n", func, line);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
46
src/misc.h
46
src/misc.h
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
struct onekeyval {
|
struct onekeyval {
|
||||||
char *name;
|
char *name;
|
||||||
@ -98,4 +99,49 @@ timespec_add(struct timespec time1, struct timespec time2);
|
|||||||
int
|
int
|
||||||
timespec_cmp(struct timespec time1, struct timespec time2);
|
timespec_cmp(struct timespec time1, struct timespec time2);
|
||||||
|
|
||||||
|
/* initialize mutex with error checking (not default on all platforms) */
|
||||||
|
int
|
||||||
|
mutex_init(pthread_mutex_t *mutex);
|
||||||
|
|
||||||
|
/* Check that the function returns 0, logging a fatal error referencing
|
||||||
|
returned error (type errno) if it fails, and aborts the process.
|
||||||
|
Example: CHECK_ERR(L_MAIN, my_function()); */
|
||||||
|
#define CHECK_ERR(d, f) \
|
||||||
|
do { int chk_err; \
|
||||||
|
if ( (chk_err = (f)) != 0) \
|
||||||
|
log_fatal_err(d, #f, __LINE__, chk_err); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* Check that the function returns 0 or okval, logging a fatal
|
||||||
|
error referencing returned erro (type errno) if not, and aborts the process.
|
||||||
|
Example: int err; CHECK_ERR_EXCEPT(L_MAIN, my_wait(), err, ETIMEDOUT); */
|
||||||
|
#define CHECK_ERR_EXCEPT(d, f, var, okval) \
|
||||||
|
do { (var) = (f); \
|
||||||
|
if (! (((var) == (okval)) || ((var) == 0))) \
|
||||||
|
log_fatal_err(d, #f, __LINE__, (var)); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* Check that the function returns value >= 0, logging a fatal error
|
||||||
|
referencing errno if it not, and aborts the process.
|
||||||
|
Example: int ret; CHECK_ERRNO(L_MAIN, ret = my_function()); */
|
||||||
|
#define CHECK_ERRNO(d, f) \
|
||||||
|
do { \
|
||||||
|
if ( (f) < 0 ) \
|
||||||
|
log_fatal_errno(d, #f, __LINE__); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* Check that the function returns non-NULL, logging a fatal error if not,
|
||||||
|
and aborts the process.
|
||||||
|
Example: void *ptr; CHECK_NULL(L_MAIN, ptr = my_create()); */
|
||||||
|
#define CHECK_NULL(d, f) \
|
||||||
|
do { \
|
||||||
|
if ( (f) == NULL ) \
|
||||||
|
log_fatal_null(d, #f, __LINE__); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/* Used by CHECK_*() macros */
|
||||||
|
void log_fatal_err(int domain, const char *func, int line, int err);
|
||||||
|
void log_fatal_errno(int domain, const char *func, int line);
|
||||||
|
void log_fatal_null(int domain, const char *func, int line);
|
||||||
|
|
||||||
#endif /* !__MISC_H__ */
|
#endif /* !__MISC_H__ */
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_EVENTFD
|
#ifdef HAVE_EVENTFD
|
||||||
# include <sys/eventfd.h>
|
# include <sys/eventfd.h>
|
||||||
@ -85,7 +84,7 @@ static int pairing_efd;
|
|||||||
static int pairing_pipe[2];
|
static int pairing_pipe[2];
|
||||||
#endif
|
#endif
|
||||||
static struct event *pairingev;
|
static struct event *pairingev;
|
||||||
static pthread_mutex_t remote_lck = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t remote_lck;
|
||||||
static struct remote_info *remote_list;
|
static struct remote_info *remote_list;
|
||||||
|
|
||||||
|
|
||||||
@ -661,7 +660,7 @@ pairing_cb(int fd, short event, void *arg)
|
|||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&remote_lck);
|
CHECK_ERR(L_REMOTE, pthread_mutex_lock(&remote_lck));
|
||||||
|
|
||||||
for (ri = remote_list; ri; ri = ri->next)
|
for (ri = remote_list; ri; ri = ri->next)
|
||||||
{
|
{
|
||||||
@ -673,7 +672,7 @@ pairing_cb(int fd, short event, void *arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&remote_lck);
|
CHECK_ERR(L_REMOTE, pthread_mutex_unlock(&remote_lck));
|
||||||
|
|
||||||
if (!ri)
|
if (!ri)
|
||||||
break;
|
break;
|
||||||
@ -700,11 +699,11 @@ touch_remote_cb(const char *name, const char *type, const char *domain, const ch
|
|||||||
* failed; any subsequent attempt will need a new pairing pin, so
|
* failed; any subsequent attempt will need a new pairing pin, so
|
||||||
* we can just forget everything we know about the remote.
|
* we can just forget everything we know about the remote.
|
||||||
*/
|
*/
|
||||||
pthread_mutex_lock(&remote_lck);
|
CHECK_ERR(L_REMOTE, pthread_mutex_lock(&remote_lck));
|
||||||
|
|
||||||
remove_remote_address_byid(name, family);
|
remove_remote_address_byid(name, family);
|
||||||
|
|
||||||
pthread_mutex_unlock(&remote_lck);
|
CHECK_ERR(L_REMOTE, pthread_mutex_unlock(&remote_lck));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -762,7 +761,7 @@ touch_remote_cb(const char *name, const char *type, const char *domain, const ch
|
|||||||
DPRINTF(E_LOG, L_REMOTE, "Discovered remote '%s' (id %s) at %s:%d, paircode %s\n", devname, name, address, port, paircode);
|
DPRINTF(E_LOG, L_REMOTE, "Discovered remote '%s' (id %s) at %s:%d, paircode %s\n", devname, name, address, port, paircode);
|
||||||
|
|
||||||
/* Add the data to the list, adding the remote to the list if needed */
|
/* Add the data to the list, adding the remote to the list if needed */
|
||||||
pthread_mutex_lock(&remote_lck);
|
CHECK_ERR(L_REMOTE, pthread_mutex_lock(&remote_lck));
|
||||||
|
|
||||||
ret = add_remote_mdns_data(name, family, address, port, devname, paircode);
|
ret = add_remote_mdns_data(name, family, address, port, devname, paircode);
|
||||||
|
|
||||||
@ -776,7 +775,7 @@ touch_remote_cb(const char *name, const char *type, const char *domain, const ch
|
|||||||
else if (ret == 1)
|
else if (ret == 1)
|
||||||
kickoff_pairing();
|
kickoff_pairing();
|
||||||
|
|
||||||
pthread_mutex_unlock(&remote_lck);
|
CHECK_ERR(L_REMOTE, pthread_mutex_unlock(&remote_lck));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -886,7 +885,7 @@ remote_pairing_read_pin(char *path)
|
|||||||
|
|
||||||
DPRINTF(E_LOG, L_REMOTE, "Read Remote pairing data (name '%s', pin '%s') from %s\n", devname, pin, path);
|
DPRINTF(E_LOG, L_REMOTE, "Read Remote pairing data (name '%s', pin '%s') from %s\n", devname, pin, path);
|
||||||
|
|
||||||
pthread_mutex_lock(&remote_lck);
|
CHECK_ERR(L_REMOTE, pthread_mutex_lock(&remote_lck));
|
||||||
|
|
||||||
ret = add_remote_pin_data(devname, pin);
|
ret = add_remote_pin_data(devname, pin);
|
||||||
free(devname);
|
free(devname);
|
||||||
@ -895,7 +894,7 @@ remote_pairing_read_pin(char *path)
|
|||||||
else
|
else
|
||||||
kickoff_pairing();
|
kickoff_pairing();
|
||||||
|
|
||||||
pthread_mutex_unlock(&remote_lck);
|
CHECK_ERR(L_REMOTE, pthread_mutex_unlock(&remote_lck));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -907,6 +906,8 @@ remote_pairing_init(void)
|
|||||||
|
|
||||||
remote_list = NULL;
|
remote_list = NULL;
|
||||||
|
|
||||||
|
CHECK_ERR(L_REMOTE, mutex_init(&remote_lck));
|
||||||
|
|
||||||
#ifdef HAVE_EVENTFD
|
#ifdef HAVE_EVENTFD
|
||||||
pairing_efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
pairing_efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||||
if (pairing_efd < 0)
|
if (pairing_efd < 0)
|
||||||
@ -990,4 +991,6 @@ remote_pairing_deinit(void)
|
|||||||
close(pairing_pipe[0]);
|
close(pairing_pipe[0]);
|
||||||
close(pairing_pipe[1]);
|
close(pairing_pipe[1]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
CHECK_ERR(L_REMOTE, pthread_mutex_destroy(&remote_lck));
|
||||||
}
|
}
|
||||||
|
@ -1187,7 +1187,7 @@ audio_fifo_flush(void)
|
|||||||
|
|
||||||
DPRINTF(E_DBG, L_SPOTIFY, "Flushing audio fifo\n");
|
DPRINTF(E_DBG, L_SPOTIFY, "Flushing audio fifo\n");
|
||||||
|
|
||||||
pthread_mutex_lock(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&g_audio_fifo->mutex));
|
||||||
|
|
||||||
while((afd = TAILQ_FIRST(&g_audio_fifo->q))) {
|
while((afd = TAILQ_FIRST(&g_audio_fifo->q))) {
|
||||||
TAILQ_REMOVE(&g_audio_fifo->q, afd, link);
|
TAILQ_REMOVE(&g_audio_fifo->q, afd, link);
|
||||||
@ -1196,7 +1196,7 @@ audio_fifo_flush(void)
|
|||||||
|
|
||||||
g_audio_fifo->qlen = 0;
|
g_audio_fifo->qlen = 0;
|
||||||
g_audio_fifo->fullcount = 0;
|
g_audio_fifo->fullcount = 0;
|
||||||
pthread_mutex_unlock(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&g_audio_fifo->mutex));
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum command_state
|
static enum command_state
|
||||||
@ -1366,6 +1366,7 @@ audio_get(void *arg, int *retval)
|
|||||||
int processed;
|
int processed;
|
||||||
int timeout;
|
int timeout;
|
||||||
int ret;
|
int ret;
|
||||||
|
int err;
|
||||||
int s;
|
int s;
|
||||||
|
|
||||||
audio = (struct audio_get_param *) arg;
|
audio = (struct audio_get_param *) arg;
|
||||||
@ -1376,7 +1377,7 @@ audio_get(void *arg, int *retval)
|
|||||||
if (g_state == SPOTIFY_STATE_PAUSED)
|
if (g_state == SPOTIFY_STATE_PAUSED)
|
||||||
playback_play(NULL, retval);
|
playback_play(NULL, retval);
|
||||||
|
|
||||||
pthread_mutex_lock(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&g_audio_fifo->mutex));
|
||||||
|
|
||||||
while ((processed < audio->wanted) && (g_state != SPOTIFY_STATE_STOPPED))
|
while ((processed < audio->wanted) && (g_state != SPOTIFY_STATE_STOPPED))
|
||||||
{
|
{
|
||||||
@ -1399,7 +1400,7 @@ audio_get(void *arg, int *retval)
|
|||||||
DPRINTF(E_DBG, L_SPOTIFY, "Waiting for audio\n");
|
DPRINTF(E_DBG, L_SPOTIFY, "Waiting for audio\n");
|
||||||
timeout += 5;
|
timeout += 5;
|
||||||
mk_reltime(&ts, 5);
|
mk_reltime(&ts, 5);
|
||||||
pthread_cond_timedwait(&g_audio_fifo->cond, &g_audio_fifo->mutex, &ts);
|
CHECK_ERR_EXCEPT(L_SPOTIFY, pthread_cond_timedwait(&g_audio_fifo->cond, &g_audio_fifo->mutex, &ts), err, ETIMEDOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!afd) && (timeout >= SPOTIFY_TIMEOUT))
|
if ((!afd) && (timeout >= SPOTIFY_TIMEOUT))
|
||||||
@ -1423,7 +1424,7 @@ audio_get(void *arg, int *retval)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Out of memory for evbuffer (tried to add %d bytes)\n", s);
|
DPRINTF(E_LOG, L_SPOTIFY, "Out of memory for evbuffer (tried to add %d bytes)\n", s);
|
||||||
pthread_mutex_unlock(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&g_audio_fifo->mutex));
|
||||||
*retval = -1;
|
*retval = -1;
|
||||||
return COMMAND_END;
|
return COMMAND_END;
|
||||||
}
|
}
|
||||||
@ -1431,7 +1432,7 @@ audio_get(void *arg, int *retval)
|
|||||||
processed += s;
|
processed += s;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&g_audio_fifo->mutex));
|
||||||
|
|
||||||
|
|
||||||
*retval = processed;
|
*retval = processed;
|
||||||
@ -1442,15 +1443,15 @@ static void
|
|||||||
artwork_loaded_cb(sp_image *image, void *userdata)
|
artwork_loaded_cb(sp_image *image, void *userdata)
|
||||||
{
|
{
|
||||||
struct artwork_get_param *artwork;
|
struct artwork_get_param *artwork;
|
||||||
|
|
||||||
artwork = userdata;
|
artwork = userdata;
|
||||||
|
|
||||||
pthread_mutex_lock(&artwork->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&artwork->mutex));
|
||||||
|
|
||||||
artwork->is_loaded = 1;
|
artwork->is_loaded = 1;
|
||||||
|
|
||||||
pthread_cond_signal(&artwork->cond);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_signal(&artwork->cond));
|
||||||
pthread_mutex_unlock(&artwork->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&artwork->mutex));
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum command_state
|
static enum command_state
|
||||||
@ -1666,10 +1667,10 @@ logged_out(sp_session *sess)
|
|||||||
{
|
{
|
||||||
DPRINTF(E_INFO, L_SPOTIFY, "Logout complete\n");
|
DPRINTF(E_INFO, L_SPOTIFY, "Logout complete\n");
|
||||||
|
|
||||||
pthread_mutex_lock(&login_lck);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&login_lck));
|
||||||
|
|
||||||
pthread_cond_signal(&login_cond);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_signal(&login_cond));
|
||||||
pthread_mutex_unlock(&login_lck);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1694,7 +1695,7 @@ static int music_delivery(sp_session *sess, const sp_audioformat *format,
|
|||||||
if (num_frames == 0)
|
if (num_frames == 0)
|
||||||
return 0; // Audio discontinuity, do nothing
|
return 0; // Audio discontinuity, do nothing
|
||||||
|
|
||||||
pthread_mutex_lock(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&g_audio_fifo->mutex));
|
||||||
|
|
||||||
/* Buffer three seconds of audio */
|
/* Buffer three seconds of audio */
|
||||||
if (g_audio_fifo->qlen > (3 * format->sample_rate))
|
if (g_audio_fifo->qlen > (3 * format->sample_rate))
|
||||||
@ -1710,7 +1711,7 @@ static int music_delivery(sp_session *sess, const sp_audioformat *format,
|
|||||||
g_audio_fifo->fullcount = 0;
|
g_audio_fifo->fullcount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&g_audio_fifo->mutex));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1727,8 +1728,8 @@ static int music_delivery(sp_session *sess, const sp_audioformat *format,
|
|||||||
TAILQ_INSERT_TAIL(&g_audio_fifo->q, afd, link);
|
TAILQ_INSERT_TAIL(&g_audio_fifo->q, afd, link);
|
||||||
g_audio_fifo->qlen += num_frames;
|
g_audio_fifo->qlen += num_frames;
|
||||||
|
|
||||||
pthread_cond_signal(&g_audio_fifo->cond);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_signal(&g_audio_fifo->cond));
|
||||||
pthread_mutex_unlock(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&g_audio_fifo->mutex));
|
||||||
|
|
||||||
return num_frames;
|
return num_frames;
|
||||||
}
|
}
|
||||||
@ -1993,29 +1994,30 @@ spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
|
|||||||
struct artwork_get_param artwork;
|
struct artwork_get_param artwork;
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
int ret;
|
int ret;
|
||||||
|
int err;
|
||||||
|
|
||||||
artwork.evbuf = evbuf;
|
artwork.evbuf = evbuf;
|
||||||
artwork.path = path;
|
artwork.path = path;
|
||||||
artwork.max_w = max_w;
|
artwork.max_w = max_w;
|
||||||
artwork.max_h = max_h;
|
artwork.max_h = max_h;
|
||||||
|
|
||||||
pthread_mutex_init(&artwork.mutex, NULL);
|
CHECK_ERR(L_SPOTIFY, mutex_init(&artwork.mutex));
|
||||||
pthread_cond_init(&artwork.cond, NULL);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_init(&artwork.cond, NULL));
|
||||||
|
|
||||||
ret = commands_exec_sync(cmdbase, artwork_get, NULL, &artwork);
|
ret = commands_exec_sync(cmdbase, artwork_get, NULL, &artwork);
|
||||||
|
|
||||||
// Artwork was not ready, wait for callback from libspotify
|
// Artwork was not ready, wait for callback from libspotify
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&artwork.mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&artwork.mutex));
|
||||||
mk_reltime(&ts, SPOTIFY_ARTWORK_TIMEOUT);
|
mk_reltime(&ts, SPOTIFY_ARTWORK_TIMEOUT);
|
||||||
if (!artwork.is_loaded)
|
if (!artwork.is_loaded)
|
||||||
pthread_cond_timedwait(&artwork.cond, &artwork.mutex, &ts);
|
CHECK_ERR_EXCEPT(L_SPOTIFY, pthread_cond_timedwait(&artwork.cond, &artwork.mutex, &ts), err, ETIMEDOUT);
|
||||||
pthread_mutex_unlock(&artwork.mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&artwork.mutex));
|
||||||
|
|
||||||
ret = commands_exec_sync(cmdbase, artwork_get_bh, NULL, &artwork);
|
ret = commands_exec_sync(cmdbase, artwork_get_bh, NULL, &artwork);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2104,7 +2106,7 @@ spotify_login(char *path)
|
|||||||
|
|
||||||
if (SP_CONNECTION_STATE_LOGGED_IN == fptr_sp_session_connectionstate(g_sess))
|
if (SP_CONNECTION_STATE_LOGGED_IN == fptr_sp_session_connectionstate(g_sess))
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&login_lck);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&login_lck));
|
||||||
|
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Logging out of Spotify (current state is %d)\n", g_state);
|
DPRINTF(E_LOG, L_SPOTIFY, "Logging out of Spotify (current state is %d)\n", g_state);
|
||||||
|
|
||||||
@ -2114,12 +2116,12 @@ spotify_login(char *path)
|
|||||||
if (SP_ERROR_OK != err)
|
if (SP_ERROR_OK != err)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_SPOTIFY, "Could not logout of Spotify: %s\n", fptr_sp_error_message(err));
|
DPRINTF(E_LOG, L_SPOTIFY, "Could not logout of Spotify: %s\n", fptr_sp_error_message(err));
|
||||||
pthread_mutex_unlock(&login_lck);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_cond_wait(&login_cond, &login_lck);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_wait(&login_cond, &login_lck));
|
||||||
pthread_mutex_unlock(&login_lck);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(E_INFO, L_SPOTIFY, "Logging into Spotify\n");
|
DPRINTF(E_INFO, L_SPOTIFY, "Logging into Spotify\n");
|
||||||
@ -2658,11 +2660,11 @@ spotify_init(void)
|
|||||||
}
|
}
|
||||||
TAILQ_INIT(&g_audio_fifo->q);
|
TAILQ_INIT(&g_audio_fifo->q);
|
||||||
g_audio_fifo->qlen = 0;
|
g_audio_fifo->qlen = 0;
|
||||||
pthread_mutex_init(&g_audio_fifo->mutex, NULL);
|
CHECK_ERR(L_SPOTIFY, mutex_init(&g_audio_fifo->mutex));
|
||||||
pthread_cond_init(&g_audio_fifo->cond, NULL);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_init(&g_audio_fifo->cond, NULL));
|
||||||
|
|
||||||
pthread_mutex_init(&login_lck, NULL);
|
CHECK_ERR(L_SPOTIFY, mutex_init(&login_lck));
|
||||||
pthread_cond_init(&login_cond, NULL);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_init(&login_cond, NULL));
|
||||||
|
|
||||||
/* Spawn thread */
|
/* Spawn thread */
|
||||||
ret = pthread_create(&tid_spotify, NULL, spotify, NULL);
|
ret = pthread_create(&tid_spotify, NULL, spotify, NULL);
|
||||||
@ -2682,11 +2684,11 @@ spotify_init(void)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
thread_fail:
|
thread_fail:
|
||||||
pthread_cond_destroy(&login_cond);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond));
|
||||||
pthread_mutex_destroy(&login_lck);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck));
|
||||||
|
|
||||||
pthread_cond_destroy(&g_audio_fifo->cond);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&g_audio_fifo->cond));
|
||||||
pthread_mutex_destroy(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&g_audio_fifo->mutex));
|
||||||
free(g_audio_fifo);
|
free(g_audio_fifo);
|
||||||
|
|
||||||
audio_fifo_fail:
|
audio_fifo_fail:
|
||||||
@ -2746,12 +2748,12 @@ spotify_deinit(void)
|
|||||||
close(g_notify_pipe[1]);
|
close(g_notify_pipe[1]);
|
||||||
|
|
||||||
/* Destroy locks */
|
/* Destroy locks */
|
||||||
pthread_cond_destroy(&login_cond);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond));
|
||||||
pthread_mutex_destroy(&login_lck);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck));
|
||||||
|
|
||||||
/* Clear audio fifo */
|
/* Clear audio fifo */
|
||||||
pthread_cond_destroy(&g_audio_fifo->cond);
|
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&g_audio_fifo->cond));
|
||||||
pthread_mutex_destroy(&g_audio_fifo->mutex);
|
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&g_audio_fifo->mutex));
|
||||||
free(g_audio_fifo);
|
free(g_audio_fifo);
|
||||||
|
|
||||||
/* Release libspotify handle */
|
/* Release libspotify handle */
|
||||||
|
@ -294,6 +294,7 @@ spotifywebapi_token_get(const char *code, const char *redirect_uri, const char *
|
|||||||
struct keyval kv;
|
struct keyval kv;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
*err = "";
|
||||||
memset(&kv, 0, sizeof(struct keyval));
|
memset(&kv, 0, sizeof(struct keyval));
|
||||||
ret = ( (keyval_add(&kv, "grant_type", "authorization_code") == 0) &&
|
ret = ( (keyval_add(&kv, "grant_type", "authorization_code") == 0) &&
|
||||||
(keyval_add(&kv, "code", code) == 0) &&
|
(keyval_add(&kv, "code", code) == 0) &&
|
||||||
|
Loading…
Reference in New Issue
Block a user