tcp_socket, ...: remove obsolet RAOP sources
This commit is contained in:
parent
1dedb96478
commit
60ac702038
14
Makefile.am
14
Makefile.am
@ -290,9 +290,6 @@ src_mpd_SOURCES = \
|
||||
src/client_subscribe.h \
|
||||
src/client_subscribe.c \
|
||||
src/client_file.c src/client_file.h \
|
||||
src/tcp_connect.c src/tcp_connect.h \
|
||||
src/tcp_socket.c src/tcp_socket.h \
|
||||
src/udp_server.c src/udp_server.h \
|
||||
src/server_socket.c \
|
||||
src/listen.c \
|
||||
src/log.c \
|
||||
@ -1000,7 +997,6 @@ noinst_PROGRAMS = \
|
||||
$(C_TESTS) \
|
||||
test/read_conf \
|
||||
test/run_resolver \
|
||||
test/run_tcp_connect \
|
||||
test/run_input \
|
||||
test/dump_text_file \
|
||||
test/dump_playlist \
|
||||
@ -1031,14 +1027,6 @@ test_run_resolver_LDADD = \
|
||||
test_run_resolver_SOURCES = test/run_resolver.c \
|
||||
src/resolver.c
|
||||
|
||||
test_run_tcp_connect_LDADD = \
|
||||
$(GLIB_LIBS)
|
||||
test_run_tcp_connect_SOURCES = test/run_tcp_connect.c \
|
||||
src/io_thread.c src/io_thread.h \
|
||||
src/fd_util.c \
|
||||
src/resolver.c \
|
||||
src/tcp_connect.c
|
||||
|
||||
test_run_input_LDADD = \
|
||||
$(INPUT_LIBS) \
|
||||
$(ARCHIVE_LIBS) \
|
||||
@ -1249,8 +1237,6 @@ test_run_output_SOURCES = test/run_output.c \
|
||||
test/stdbin.h \
|
||||
src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
|
||||
src/io_thread.c src/io_thread.h \
|
||||
src/udp_server.c src/udp_server.h \
|
||||
src/tcp_socket.c src/tcp_socket.h \
|
||||
src/audio_check.c \
|
||||
src/audio_format.c \
|
||||
src/audio_parser.c \
|
||||
|
@ -1,250 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "tcp_connect.h"
|
||||
#include "fd_util.h"
|
||||
#include "io_thread.h"
|
||||
#include "glib_socket.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
struct tcp_connect {
|
||||
const struct tcp_connect_handler *handler;
|
||||
void *handler_ctx;
|
||||
|
||||
int fd;
|
||||
GSource *source;
|
||||
|
||||
unsigned timeout_ms;
|
||||
GSource *timeout_source;
|
||||
};
|
||||
|
||||
static bool
|
||||
is_in_progress_errno(int e)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return e == WSAEINPROGRESS || e == WSAEWOULDBLOCK;
|
||||
#else
|
||||
return e == EINPROGRESS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tcp_connect_event(G_GNUC_UNUSED GIOChannel *source,
|
||||
G_GNUC_UNUSED GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
struct tcp_connect *c = data;
|
||||
|
||||
assert(c->source != NULL);
|
||||
assert(c->timeout_source != NULL);
|
||||
|
||||
/* clear the socket source */
|
||||
g_source_unref(c->source);
|
||||
c->source = NULL;
|
||||
|
||||
/* delete the timeout source */
|
||||
g_source_destroy(c->timeout_source);
|
||||
g_source_unref(c->timeout_source);
|
||||
c->timeout_source = NULL;
|
||||
|
||||
/* obtain the connect result */
|
||||
int s_err = 0;
|
||||
socklen_t s_err_size = sizeof(s_err);
|
||||
if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR,
|
||||
(char*)&s_err, &s_err_size) < 0)
|
||||
s_err = errno;
|
||||
|
||||
if (s_err == 0) {
|
||||
/* connection established successfully */
|
||||
|
||||
c->handler->success(c->fd, c->handler_ctx);
|
||||
} else {
|
||||
/* there was an I/O error; close the socket and pass
|
||||
the error to the handler */
|
||||
|
||||
close_socket(c->fd);
|
||||
|
||||
GError *error =
|
||||
g_error_new_literal(g_file_error_quark(), s_err,
|
||||
g_strerror(s_err));
|
||||
c->handler->error(error, c->handler_ctx);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tcp_connect_timeout(gpointer data)
|
||||
{
|
||||
struct tcp_connect *c = data;
|
||||
|
||||
assert(c->source != NULL);
|
||||
assert(c->timeout_source != NULL);
|
||||
|
||||
/* clear the timeout source */
|
||||
g_source_unref(c->timeout_source);
|
||||
c->timeout_source = NULL;
|
||||
|
||||
/* delete the socket source */
|
||||
g_source_destroy(c->source);
|
||||
g_source_unref(c->source);
|
||||
c->source = NULL;
|
||||
|
||||
/* report timeout to handler */
|
||||
c->handler->timeout(c->handler_ctx);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
tcp_connect_init(gpointer data)
|
||||
{
|
||||
struct tcp_connect *c = data;
|
||||
|
||||
/* create a connect source */
|
||||
GIOChannel *channel = g_io_channel_new_socket(c->fd);
|
||||
c->source = g_io_create_watch(channel, G_IO_OUT);
|
||||
g_io_channel_unref(channel);
|
||||
|
||||
g_source_set_callback(c->source, (GSourceFunc)tcp_connect_event, c,
|
||||
NULL);
|
||||
g_source_attach(c->source, io_thread_context());
|
||||
|
||||
/* create a timeout source */
|
||||
if (c->timeout_ms > 0)
|
||||
c->timeout_source =
|
||||
io_thread_timeout_add(c->timeout_ms,
|
||||
tcp_connect_timeout, c);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tcp_connect_address(const struct sockaddr *address, size_t address_length,
|
||||
unsigned timeout_ms,
|
||||
const struct tcp_connect_handler *handler, void *ctx,
|
||||
struct tcp_connect **handle_r)
|
||||
{
|
||||
assert(address != NULL);
|
||||
assert(address_length > 0);
|
||||
assert(handler != NULL);
|
||||
assert(handler->success != NULL);
|
||||
assert(handler->error != NULL);
|
||||
assert(handler->canceled != NULL);
|
||||
assert(handler->timeout != NULL || timeout_ms == 0);
|
||||
assert(handle_r != NULL);
|
||||
assert(*handle_r == NULL);
|
||||
|
||||
int fd = socket_cloexec_nonblock(address->sa_family, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
GError *error =
|
||||
g_error_new_literal(g_file_error_quark(), errno,
|
||||
g_strerror(errno));
|
||||
handler->error(error, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = connect(fd, address, address_length);
|
||||
if (ret >= 0) {
|
||||
/* quick connect, no I/O thread */
|
||||
handler->success(fd, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_in_progress_errno(errno)) {
|
||||
GError *error =
|
||||
g_error_new_literal(g_file_error_quark(), errno,
|
||||
g_strerror(errno));
|
||||
close_socket(fd);
|
||||
handler->error(error, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* got EINPROGRESS, use the I/O thread to wait for the
|
||||
operation to finish */
|
||||
|
||||
struct tcp_connect *c = g_new(struct tcp_connect, 1);
|
||||
c->handler = handler;
|
||||
c->handler_ctx = ctx;
|
||||
c->fd = fd;
|
||||
c->source = NULL;
|
||||
c->timeout_ms = timeout_ms;
|
||||
c->timeout_source = NULL;
|
||||
|
||||
*handle_r = c;
|
||||
|
||||
io_thread_call(tcp_connect_init, c);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
tcp_connect_cancel_callback(gpointer data)
|
||||
{
|
||||
struct tcp_connect *c = data;
|
||||
|
||||
assert((c->source == NULL) == (c->timeout_source == NULL));
|
||||
|
||||
if (c->source == NULL)
|
||||
return NULL;
|
||||
|
||||
/* delete the socket source */
|
||||
g_source_destroy(c->source);
|
||||
g_source_unref(c->source);
|
||||
c->source = NULL;
|
||||
|
||||
/* delete the timeout source */
|
||||
g_source_destroy(c->timeout_source);
|
||||
g_source_unref(c->timeout_source);
|
||||
c->timeout_source = NULL;
|
||||
|
||||
/* close the socket */
|
||||
close_socket(c->fd);
|
||||
|
||||
/* notify the handler */
|
||||
c->handler->canceled(c->handler_ctx);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
tcp_connect_cancel(struct tcp_connect *c)
|
||||
{
|
||||
if (c->source == NULL)
|
||||
return;
|
||||
|
||||
io_thread_call(tcp_connect_cancel_callback, c);
|
||||
}
|
||||
|
||||
void
|
||||
tcp_connect_free(struct tcp_connect *c)
|
||||
{
|
||||
assert(c->source == NULL);
|
||||
|
||||
g_free(c);
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_TCP_CONNECT_H
|
||||
#define MPD_TCP_CONNECT_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
struct sockaddr;
|
||||
|
||||
struct tcp_connect_handler {
|
||||
/**
|
||||
* The connection was established successfully.
|
||||
*
|
||||
* @param fd a file descriptor that must be closed with
|
||||
* close_socket() when finished
|
||||
*/
|
||||
void (*success)(int fd, void *ctx);
|
||||
|
||||
/**
|
||||
* An error has occurred. The method is responsible for
|
||||
* freeing the GError.
|
||||
*/
|
||||
void (*error)(GError *error, void *ctx);
|
||||
|
||||
/**
|
||||
* The connection could not be established in the specified
|
||||
* time span.
|
||||
*/
|
||||
void (*timeout)(void *ctx);
|
||||
|
||||
/**
|
||||
* The operation was canceled before a result was available.
|
||||
*/
|
||||
void (*canceled)(void *ctx);
|
||||
};
|
||||
|
||||
struct tcp_connect;
|
||||
|
||||
/**
|
||||
* Establish a TCP connection to the specified address.
|
||||
*
|
||||
* Note that the result may be available before this function returns.
|
||||
*
|
||||
* The caller must free this object with tcp_connect_free().
|
||||
*
|
||||
* @param timeout_ms time out after this number of milliseconds; 0
|
||||
* means no timeout
|
||||
* @param handle_r a handle that can be used to cancel the operation;
|
||||
* the caller must initialize it to NULL
|
||||
*/
|
||||
void
|
||||
tcp_connect_address(const struct sockaddr *address, size_t address_length,
|
||||
unsigned timeout_ms,
|
||||
const struct tcp_connect_handler *handler, void *ctx,
|
||||
struct tcp_connect **handle_r);
|
||||
|
||||
/**
|
||||
* Cancel the operation. It is possible that the result is delivered
|
||||
* before the operation has been canceled; in that case, the
|
||||
* canceled() handler method will not be invoked.
|
||||
*
|
||||
* Even after calling this function, tcp_connect_free() must still be
|
||||
* called to free memory.
|
||||
*/
|
||||
void
|
||||
tcp_connect_cancel(struct tcp_connect *handle);
|
||||
|
||||
/**
|
||||
* Free memory used by this object.
|
||||
*
|
||||
* This function is not thread safe. It must not be called while
|
||||
* other threads are still working with it. If no callback has been
|
||||
* invoked so far, then you must call tcp_connect_cancel() to release
|
||||
* I/O thread resources, before calling this function.
|
||||
*/
|
||||
void
|
||||
tcp_connect_free(struct tcp_connect *handle);
|
||||
|
||||
#endif
|
377
src/tcp_socket.c
377
src/tcp_socket.c
@ -1,377 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "tcp_socket.h"
|
||||
#include "fifo_buffer.h"
|
||||
#include "io_thread.h"
|
||||
#include "glib_socket.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
struct tcp_socket {
|
||||
const struct tcp_socket_handler *handler;
|
||||
void *handler_ctx;
|
||||
|
||||
GMutex *mutex;
|
||||
|
||||
GIOChannel *channel;
|
||||
GSource *in_source, *out_source;
|
||||
|
||||
struct fifo_buffer *input, *output;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
tcp_event(GIOChannel *source, GIOCondition condition, gpointer data);
|
||||
|
||||
static void
|
||||
tcp_socket_schedule_read(struct tcp_socket *s)
|
||||
{
|
||||
assert(s->input != NULL);
|
||||
assert(!fifo_buffer_is_full(s->input));
|
||||
|
||||
if (s->in_source != NULL)
|
||||
return;
|
||||
|
||||
s->in_source = g_io_create_watch(s->channel,
|
||||
G_IO_IN|G_IO_ERR|G_IO_HUP);
|
||||
g_source_set_callback(s->in_source, (GSourceFunc)tcp_event, s, NULL);
|
||||
g_source_attach(s->in_source, io_thread_context());
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_socket_unschedule_read(struct tcp_socket *s)
|
||||
{
|
||||
if (s->in_source == NULL)
|
||||
return;
|
||||
|
||||
g_source_destroy(s->in_source);
|
||||
g_source_unref(s->in_source);
|
||||
s->in_source = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_socket_schedule_write(struct tcp_socket *s)
|
||||
{
|
||||
assert(s->output != NULL);
|
||||
assert(!fifo_buffer_is_empty(s->output));
|
||||
|
||||
if (s->out_source != NULL)
|
||||
return;
|
||||
|
||||
s->out_source = g_io_create_watch(s->channel, G_IO_OUT);
|
||||
g_source_set_callback(s->out_source, (GSourceFunc)tcp_event, s, NULL);
|
||||
g_source_attach(s->out_source, io_thread_context());
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_socket_unschedule_write(struct tcp_socket *s)
|
||||
{
|
||||
if (s->out_source == NULL)
|
||||
return;
|
||||
|
||||
g_source_destroy(s->out_source);
|
||||
g_source_unref(s->out_source);
|
||||
s->out_source = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the socket. Caller must lock the mutex.
|
||||
*/
|
||||
static void
|
||||
tcp_socket_close(struct tcp_socket *s)
|
||||
{
|
||||
tcp_socket_unschedule_read(s);
|
||||
tcp_socket_unschedule_write(s);
|
||||
|
||||
if (s->channel != NULL) {
|
||||
g_io_channel_unref(s->channel);
|
||||
s->channel = NULL;
|
||||
}
|
||||
|
||||
if (s->input != NULL) {
|
||||
fifo_buffer_free(s->input);
|
||||
s->input = NULL;
|
||||
}
|
||||
|
||||
if (s->output != NULL) {
|
||||
fifo_buffer_free(s->output);
|
||||
s->output = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
tcp_socket_close_callback(gpointer data)
|
||||
{
|
||||
struct tcp_socket *s = data;
|
||||
|
||||
g_mutex_lock(s->mutex);
|
||||
tcp_socket_close(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_socket_close_indirect(struct tcp_socket *s)
|
||||
{
|
||||
io_thread_call(tcp_socket_close_callback, s);
|
||||
|
||||
assert(s->channel == NULL);
|
||||
assert(s->in_source == NULL);
|
||||
assert(s->out_source == NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_handle_input(struct tcp_socket *s)
|
||||
{
|
||||
size_t length;
|
||||
const void *p = fifo_buffer_read(s->input, &length);
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
g_mutex_unlock(s->mutex);
|
||||
size_t consumed = s->handler->data(p, length, s->handler_ctx);
|
||||
g_mutex_lock(s->mutex);
|
||||
if (consumed > 0 && s->input != NULL)
|
||||
fifo_buffer_consume(s->input, consumed);
|
||||
}
|
||||
|
||||
static bool
|
||||
tcp_in_event(struct tcp_socket *s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s->channel != NULL);
|
||||
|
||||
g_mutex_lock(s->mutex);
|
||||
|
||||
size_t max_length;
|
||||
void *p = fifo_buffer_write(s->input, &max_length);
|
||||
if (p == NULL) {
|
||||
GError *error = g_error_new_literal(tcp_socket_quark(), 0,
|
||||
"buffer overflow");
|
||||
tcp_socket_close(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
s->handler->error(error, s->handler_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
gsize bytes_read;
|
||||
GError *error = NULL;
|
||||
GIOStatus status = g_io_channel_read_chars(s->channel,
|
||||
p, max_length,
|
||||
&bytes_read, &error);
|
||||
switch (status) {
|
||||
case G_IO_STATUS_NORMAL:
|
||||
fifo_buffer_append(s->input, bytes_read);
|
||||
tcp_handle_input(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
return true;
|
||||
|
||||
case G_IO_STATUS_AGAIN:
|
||||
/* try again later */
|
||||
g_mutex_unlock(s->mutex);
|
||||
return true;
|
||||
|
||||
case G_IO_STATUS_EOF:
|
||||
/* peer disconnected */
|
||||
tcp_socket_close(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
s->handler->disconnected(s->handler_ctx);
|
||||
return false;
|
||||
|
||||
case G_IO_STATUS_ERROR:
|
||||
/* I/O error */
|
||||
tcp_socket_close(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
s->handler->error(error, s->handler_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* unreachable */
|
||||
assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
tcp_out_event(struct tcp_socket *s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s->channel != NULL);
|
||||
|
||||
g_mutex_lock(s->mutex);
|
||||
|
||||
size_t length;
|
||||
const void *p = fifo_buffer_read(s->output, &length);
|
||||
if (p == NULL) {
|
||||
/* no more data in the output buffer, remove the
|
||||
output event */
|
||||
tcp_socket_unschedule_write(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
gsize bytes_written;
|
||||
GError *error = NULL;
|
||||
GIOStatus status = g_io_channel_write_chars(s->channel, p, length,
|
||||
&bytes_written, &error);
|
||||
switch (status) {
|
||||
case G_IO_STATUS_NORMAL:
|
||||
fifo_buffer_consume(s->output, bytes_written);
|
||||
g_mutex_unlock(s->mutex);
|
||||
return true;
|
||||
|
||||
case G_IO_STATUS_AGAIN:
|
||||
tcp_socket_schedule_write(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
return true;
|
||||
|
||||
case G_IO_STATUS_EOF:
|
||||
/* peer disconnected */
|
||||
tcp_socket_close(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
s->handler->disconnected(s->handler_ctx);
|
||||
return false;
|
||||
|
||||
case G_IO_STATUS_ERROR:
|
||||
/* I/O error */
|
||||
tcp_socket_close(s);
|
||||
g_mutex_unlock(s->mutex);
|
||||
s->handler->error(error, s->handler_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* unreachable */
|
||||
g_mutex_unlock(s->mutex);
|
||||
assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tcp_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
struct tcp_socket *s = data;
|
||||
|
||||
assert(source == s->channel);
|
||||
|
||||
switch (condition) {
|
||||
case G_IO_IN:
|
||||
case G_IO_PRI:
|
||||
return tcp_in_event(s);
|
||||
|
||||
case G_IO_OUT:
|
||||
return tcp_out_event(s);
|
||||
|
||||
case G_IO_ERR:
|
||||
case G_IO_HUP:
|
||||
case G_IO_NVAL:
|
||||
tcp_socket_close(s);
|
||||
s->handler->disconnected(s->handler_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* unreachable */
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct tcp_socket *
|
||||
tcp_socket_new(int fd,
|
||||
const struct tcp_socket_handler *handler, void *ctx)
|
||||
{
|
||||
assert(fd >= 0);
|
||||
assert(handler != NULL);
|
||||
assert(handler->data != NULL);
|
||||
assert(handler->error != NULL);
|
||||
assert(handler->disconnected != NULL);
|
||||
|
||||
struct tcp_socket *s = g_new(struct tcp_socket, 1);
|
||||
s->handler = handler;
|
||||
s->handler_ctx = ctx;
|
||||
s->mutex = g_mutex_new();
|
||||
|
||||
g_mutex_lock(s->mutex);
|
||||
|
||||
s->channel = g_io_channel_new_socket(fd);
|
||||
/* GLib is responsible for closing the file descriptor */
|
||||
g_io_channel_set_close_on_unref(s->channel, true);
|
||||
/* NULL encoding means the stream is binary safe */
|
||||
g_io_channel_set_encoding(s->channel, NULL, NULL);
|
||||
/* no buffering */
|
||||
g_io_channel_set_buffered(s->channel, false);
|
||||
|
||||
s->input = fifo_buffer_new(4096);
|
||||
s->output = fifo_buffer_new(4096);
|
||||
|
||||
s->in_source = NULL;
|
||||
s->out_source = NULL;
|
||||
|
||||
tcp_socket_schedule_read(s);
|
||||
|
||||
g_mutex_unlock(s->mutex);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
tcp_socket_free(struct tcp_socket *s)
|
||||
{
|
||||
tcp_socket_close_indirect(s);
|
||||
g_mutex_free(s->mutex);
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
bool
|
||||
tcp_socket_send(struct tcp_socket *s, const void *data, size_t length)
|
||||
{
|
||||
assert(s != NULL);
|
||||
|
||||
g_mutex_lock(s->mutex);
|
||||
|
||||
if (s->output == NULL || s->channel == NULL) {
|
||||
/* already disconnected */
|
||||
g_mutex_unlock(s->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t max_length;
|
||||
void *p = fifo_buffer_write(s->output, &max_length);
|
||||
if (p == NULL || max_length < length) {
|
||||
/* buffer is full */
|
||||
g_mutex_unlock(s->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(p, data, length);
|
||||
fifo_buffer_append(s->output, length);
|
||||
tcp_socket_schedule_write(s);
|
||||
|
||||
g_mutex_unlock(s->mutex);
|
||||
return true;
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_TCP_SOCKET_H
|
||||
#define MPD_TCP_SOCKET_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct sockaddr;
|
||||
|
||||
struct tcp_socket_handler {
|
||||
/**
|
||||
* New data has arrived.
|
||||
*
|
||||
* @return the number of bytes consumed; 0 if more data is
|
||||
* needed
|
||||
*/
|
||||
size_t (*data)(const void *data, size_t length, void *ctx);
|
||||
|
||||
void (*error)(GError *error, void *ctx);
|
||||
|
||||
void (*disconnected)(void *ctx);
|
||||
};
|
||||
|
||||
static inline GQuark
|
||||
tcp_socket_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("tcp_socket");
|
||||
}
|
||||
|
||||
G_GNUC_MALLOC
|
||||
struct tcp_socket *
|
||||
tcp_socket_new(int fd,
|
||||
const struct tcp_socket_handler *handler, void *ctx);
|
||||
|
||||
void
|
||||
tcp_socket_free(struct tcp_socket *s);
|
||||
|
||||
bool
|
||||
tcp_socket_send(struct tcp_socket *s, const void *data, size_t length);
|
||||
|
||||
#endif
|
140
src/udp_server.c
140
src/udp_server.c
@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "udp_server.h"
|
||||
#include "io_thread.h"
|
||||
#include "glib_socket.h"
|
||||
#include "gcc.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#if GCC_CHECK_VERSION(4, 2)
|
||||
/* allow C99 initialisers on struct sockaddr_in, even if the
|
||||
(non-portable) attribute "sin_zero" is missing */
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
|
||||
struct udp_server {
|
||||
const struct udp_server_handler *handler;
|
||||
void *handler_ctx;
|
||||
|
||||
int fd;
|
||||
GIOChannel *channel;
|
||||
GSource *source;
|
||||
|
||||
char buffer[8192];
|
||||
};
|
||||
|
||||
static gboolean
|
||||
udp_in_event(G_GNUC_UNUSED GIOChannel *source,
|
||||
G_GNUC_UNUSED GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
struct udp_server *udp = data;
|
||||
|
||||
struct sockaddr_storage address_storage;
|
||||
struct sockaddr *address = (struct sockaddr *)&address_storage;
|
||||
socklen_t address_length = sizeof(address_storage);
|
||||
|
||||
ssize_t nbytes = recvfrom(udp->fd, udp->buffer, sizeof(udp->buffer),
|
||||
#ifdef WIN32
|
||||
0,
|
||||
#else
|
||||
MSG_DONTWAIT,
|
||||
#endif
|
||||
address, &address_length);
|
||||
if (nbytes <= 0)
|
||||
return true;
|
||||
|
||||
udp->handler->datagram(udp->fd, udp->buffer, nbytes,
|
||||
address, address_length, udp->handler_ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct udp_server *
|
||||
udp_server_new(unsigned port,
|
||||
const struct udp_server_handler *handler, void *ctx,
|
||||
GError **error_r)
|
||||
{
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
g_set_error(error_r, udp_server_quark(), errno,
|
||||
"failed to create UDP socket: %s",
|
||||
g_strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct sockaddr_in address = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr = {
|
||||
.s_addr = htonl(INADDR_ANY),
|
||||
},
|
||||
.sin_port = htons(port),
|
||||
#if defined(__linux__) && !GCC_CHECK_VERSION(4, 2)
|
||||
.sin_zero = { 0 },
|
||||
#endif
|
||||
};
|
||||
|
||||
if (bind(fd, (const struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
g_set_error(error_r, udp_server_quark(), errno,
|
||||
"failed to bind UDP port %u: %s",
|
||||
port, g_strerror(errno));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct udp_server *udp = g_new(struct udp_server, 1);
|
||||
udp->handler = handler;
|
||||
udp->handler_ctx = ctx;
|
||||
|
||||
udp->fd = fd;
|
||||
udp->channel = g_io_channel_new_socket(fd);
|
||||
/* NULL encoding means the stream is binary safe */
|
||||
g_io_channel_set_encoding(udp->channel, NULL, NULL);
|
||||
/* no buffering */
|
||||
g_io_channel_set_buffered(udp->channel, false);
|
||||
|
||||
udp->source = g_io_create_watch(udp->channel, G_IO_IN);
|
||||
g_source_set_callback(udp->source, (GSourceFunc)udp_in_event, udp,
|
||||
NULL);
|
||||
g_source_attach(udp->source, io_thread_context());
|
||||
|
||||
return udp;
|
||||
}
|
||||
|
||||
void
|
||||
udp_server_free(struct udp_server *udp)
|
||||
{
|
||||
g_source_destroy(udp->source);
|
||||
g_source_unref(udp->source);
|
||||
g_io_channel_unref(udp->channel);
|
||||
close(udp->fd);
|
||||
g_free(udp);
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef MPD_UDP_SERVER_H
|
||||
#define MPD_UDP_SERVER_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct sockaddr;
|
||||
|
||||
struct udp_server_handler {
|
||||
/**
|
||||
* A datagram was received.
|
||||
*/
|
||||
void (*datagram)(int fd, const void *data, size_t length,
|
||||
const struct sockaddr *source_address,
|
||||
size_t source_address_length, void *ctx);
|
||||
};
|
||||
|
||||
static inline GQuark
|
||||
udp_server_quark(void)
|
||||
{
|
||||
return g_quark_from_static_string("udp_server");
|
||||
}
|
||||
|
||||
struct udp_server *
|
||||
udp_server_new(unsigned port,
|
||||
const struct udp_server_handler *handler, void *ctx,
|
||||
GError **error_r);
|
||||
|
||||
void
|
||||
udp_server_free(struct udp_server *udp);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user