mirror of
https://github.com/JamesonHuang/OpenWrt_Luci_Lua.git
synced 2025-03-30 08:09:05 +00:00
392 lines
7.9 KiB
C
392 lines
7.9 KiB
C
/* libmpdclient
|
|
(c) 2003-2015 The Music Player Daemon Project
|
|
This project's homepage is: http://www.musicpd.org
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
- Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
- Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "iasync.h"
|
|
#include "buffer.h"
|
|
#include "ierror.h"
|
|
#include "quote.h"
|
|
#include "socket.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifndef WIN32
|
|
#include <sys/socket.h>
|
|
|
|
static inline int
|
|
closesocket(int fd)
|
|
{
|
|
return close(fd);
|
|
}
|
|
#endif
|
|
|
|
#ifndef MSG_DONTWAIT
|
|
#define MSG_DONTWAIT 0
|
|
#endif
|
|
|
|
struct mpd_async {
|
|
int fd;
|
|
|
|
struct mpd_error_info error;
|
|
|
|
struct mpd_buffer input;
|
|
|
|
struct mpd_buffer output;
|
|
};
|
|
|
|
struct mpd_async *
|
|
mpd_async_new(int fd)
|
|
{
|
|
struct mpd_async *async;
|
|
|
|
assert(fd >= 0);
|
|
|
|
async = malloc(sizeof(*async));
|
|
if (async == NULL)
|
|
return NULL;
|
|
|
|
async->fd = fd;
|
|
mpd_error_init(&async->error);
|
|
|
|
mpd_buffer_init(&async->input);
|
|
mpd_buffer_init(&async->output);
|
|
|
|
return async;
|
|
}
|
|
|
|
void
|
|
mpd_async_free(struct mpd_async *async)
|
|
{
|
|
assert(async != NULL);
|
|
|
|
closesocket(async->fd);
|
|
mpd_error_deinit(&async->error);
|
|
free(async);
|
|
}
|
|
|
|
enum mpd_error
|
|
mpd_async_get_error(const struct mpd_async *async)
|
|
{
|
|
assert(async != NULL);
|
|
|
|
return async->error.code;
|
|
}
|
|
|
|
const char *
|
|
mpd_async_get_error_message(const struct mpd_async *async)
|
|
{
|
|
assert(async != NULL);
|
|
|
|
return mpd_error_get_message(&async->error);
|
|
}
|
|
|
|
int
|
|
mpd_async_get_system_error(const struct mpd_async *async)
|
|
{
|
|
assert(async != NULL);
|
|
assert(async->error.code == MPD_ERROR_SYSTEM);
|
|
|
|
return async->error.system;
|
|
}
|
|
|
|
bool
|
|
mpd_async_copy_error(const struct mpd_async *async,
|
|
struct mpd_error_info *dest)
|
|
{
|
|
assert(async != NULL);
|
|
|
|
return mpd_error_copy(dest, &async->error);
|
|
}
|
|
|
|
int
|
|
mpd_async_get_fd(const struct mpd_async *async)
|
|
{
|
|
assert(async != NULL);
|
|
assert(async->fd >= 0);
|
|
|
|
return async->fd;
|
|
}
|
|
|
|
void
|
|
mpd_async_set_keepalive(struct mpd_async *async,
|
|
bool keepalive)
|
|
{
|
|
assert(async != NULL);
|
|
assert(async->fd >= 0);
|
|
|
|
mpd_socket_keepalive(async->fd, keepalive);
|
|
}
|
|
|
|
enum mpd_async_event
|
|
mpd_async_events(const struct mpd_async *async)
|
|
{
|
|
enum mpd_async_event events;
|
|
|
|
assert(async != NULL);
|
|
|
|
if (mpd_error_is_defined(&async->error))
|
|
return 0;
|
|
|
|
/* always listen to hangups and errors */
|
|
events = MPD_ASYNC_EVENT_HUP | MPD_ASYNC_EVENT_ERROR;
|
|
|
|
if (mpd_buffer_room(&async->input) > 0)
|
|
/* there's room left in the input buffer: attempt to
|
|
read */
|
|
events |= MPD_ASYNC_EVENT_READ;
|
|
|
|
if (mpd_buffer_size(&async->output) > 0)
|
|
/* there's data in the output buffer: attempt to
|
|
write */
|
|
events |= MPD_ASYNC_EVENT_WRITE;
|
|
|
|
return events;
|
|
}
|
|
|
|
static bool
|
|
ignore_errno(int e)
|
|
{
|
|
#ifdef WIN32
|
|
return e == WSAEINTR || e == WSAEINPROGRESS;
|
|
#else
|
|
return e == EINTR || e == EAGAIN;
|
|
#endif
|
|
}
|
|
|
|
static bool
|
|
mpd_async_read(struct mpd_async *async)
|
|
{
|
|
size_t room;
|
|
ssize_t nbytes;
|
|
|
|
assert(async != NULL);
|
|
assert(async->fd >= 0);
|
|
assert(!mpd_error_is_defined(&async->error));
|
|
|
|
room = mpd_buffer_room(&async->input);
|
|
if (room == 0)
|
|
return true;
|
|
|
|
nbytes = recv(async->fd, mpd_buffer_write(&async->input), room,
|
|
MSG_DONTWAIT);
|
|
if (nbytes < 0) {
|
|
/* I/O error */
|
|
|
|
if (ignore_errno(mpd_socket_errno()))
|
|
return true;
|
|
|
|
mpd_error_errno(&async->error);
|
|
return false;
|
|
}
|
|
|
|
if (nbytes == 0) {
|
|
mpd_error_code(&async->error, MPD_ERROR_CLOSED);
|
|
mpd_error_message(&async->error,
|
|
"Connection closed by the server");
|
|
return false;
|
|
}
|
|
|
|
mpd_buffer_expand(&async->input, (size_t)nbytes);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
mpd_async_write(struct mpd_async *async)
|
|
{
|
|
size_t size;
|
|
ssize_t nbytes;
|
|
|
|
assert(async != NULL);
|
|
assert(async->fd >= 0);
|
|
assert(!mpd_error_is_defined(&async->error));
|
|
|
|
size = mpd_buffer_size(&async->output);
|
|
if (size == 0)
|
|
return true;
|
|
|
|
nbytes = send(async->fd, mpd_buffer_read(&async->output), size,
|
|
MSG_DONTWAIT);
|
|
if (nbytes < 0) {
|
|
/* I/O error */
|
|
|
|
if (ignore_errno(mpd_socket_errno()))
|
|
return true;
|
|
|
|
mpd_error_errno(&async->error);
|
|
return false;
|
|
}
|
|
|
|
mpd_buffer_consume(&async->output, (size_t)nbytes);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
mpd_async_io(struct mpd_async *async, enum mpd_async_event events)
|
|
{
|
|
bool success;
|
|
|
|
assert(async != NULL);
|
|
|
|
if (mpd_error_is_defined(&async->error))
|
|
return false;
|
|
|
|
if ((events & (MPD_ASYNC_EVENT_HUP|MPD_ASYNC_EVENT_ERROR)) != 0) {
|
|
mpd_error_code(&async->error, MPD_ERROR_CLOSED);
|
|
mpd_error_message(&async->error, "Socket connection aborted");
|
|
return false;
|
|
}
|
|
|
|
if (events & MPD_ASYNC_EVENT_READ) {
|
|
success = mpd_async_read(async);
|
|
if (!success)
|
|
return false;
|
|
}
|
|
|
|
assert(!mpd_error_is_defined(&async->error));
|
|
|
|
if (events & MPD_ASYNC_EVENT_WRITE) {
|
|
success = mpd_async_write(async);
|
|
if (!success)
|
|
return false;
|
|
}
|
|
|
|
assert(!mpd_error_is_defined(&async->error));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
mpd_async_send_command_v(struct mpd_async *async, const char *command,
|
|
va_list args)
|
|
{
|
|
size_t room, length;
|
|
char *dest, *end, *p;
|
|
const char *arg;
|
|
|
|
assert(async != NULL);
|
|
assert(command != NULL);
|
|
|
|
if (mpd_error_is_defined(&async->error))
|
|
return false;
|
|
|
|
room = mpd_buffer_room(&async->output);
|
|
length = strlen(command);
|
|
if (room <= length)
|
|
return false;
|
|
|
|
dest = mpd_buffer_write(&async->output);
|
|
/* -1 because we reserve space for the \n character */
|
|
end = dest + room - 1;
|
|
|
|
/* copy the command (no quoting, we asumme it is "clean") */
|
|
|
|
memcpy(dest, command, length);
|
|
p = dest + length;
|
|
|
|
/* now append all arguments (quoted) */
|
|
|
|
while ((arg = va_arg(args, const char *)) != NULL) {
|
|
/* append a space separator */
|
|
|
|
if (p >= end)
|
|
return false;
|
|
|
|
*p++ = ' ';
|
|
|
|
/* quote the argument into the destination buffer */
|
|
|
|
p = quote(p, end, arg);
|
|
assert(p == NULL || (p >= dest && p <= end));
|
|
if (p == NULL)
|
|
return false;
|
|
}
|
|
|
|
|
|
/* append the newline to finish this command */
|
|
|
|
*p++ = '\n';
|
|
|
|
mpd_buffer_expand(&async->output, p - dest);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
mpd_async_send_command(struct mpd_async *async, const char *command, ...)
|
|
{
|
|
va_list args;
|
|
bool success;
|
|
|
|
assert(async != NULL);
|
|
assert(command != NULL);
|
|
|
|
va_start(args, command);
|
|
success = mpd_async_send_command_v(async, command, args);
|
|
va_end(args);
|
|
|
|
return success;
|
|
}
|
|
|
|
char *
|
|
mpd_async_recv_line(struct mpd_async *async)
|
|
{
|
|
size_t size;
|
|
char *src, *newline;
|
|
|
|
assert(async != NULL);
|
|
|
|
size = mpd_buffer_size(&async->input);
|
|
if (size == 0)
|
|
return NULL;
|
|
|
|
src = mpd_buffer_read(&async->input);
|
|
assert(src != NULL);
|
|
newline = memchr(src, '\n', size);
|
|
if (newline == NULL) {
|
|
/* line is not finished yet */
|
|
if (mpd_buffer_full(&async->input)) {
|
|
/* .. but the buffer is full - line is too
|
|
long, abort connection and bail out */
|
|
mpd_error_code(&async->error, MPD_ERROR_MALFORMED);
|
|
mpd_error_message(&async->error,
|
|
"Response line too large");
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
*newline = 0;
|
|
mpd_buffer_consume(&async->input, newline + 1 - src);
|
|
|
|
return src;
|
|
}
|