efl/legacy/ecore/src/lib/ecore_con/ecore_con.c

1680 lines
43 KiB
C

/*
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
*/
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <netdb.h>
#include "config.h"
#if USE_OPENSSL
#include <time.h>
#endif
#include "Ecore.h"
#include "ecore_private.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#elif WIN32
#include <winsock.h>
#endif
static void _ecore_con_cb_dns_lookup(void *data, struct hostent *he);
static void _ecore_con_server_free(Ecore_Con_Server *svr);
static void _ecore_con_client_free(Ecore_Con_Client *cl);
static int _ecore_con_svr_handler(void *data, Ecore_Fd_Handler *fd_handler);
static int _ecore_con_cl_handler(void *data, Ecore_Fd_Handler *fd_handler);
static int _ecore_con_svr_cl_handler(void *data, Ecore_Fd_Handler *fd_handler);
static void _ecore_con_server_flush(Ecore_Con_Server *svr);
static void _ecore_con_client_flush(Ecore_Con_Client *cl);
static void _ecore_con_event_client_add_free(void *data, void *ev);
static void _ecore_con_event_client_del_free(void *data, void *ev);
static void _ecore_con_event_client_data_free(void *data, void *ev);
static void _ecore_con_event_server_add_free(void *data, void *ev);
static void _ecore_con_event_server_del_free(void *data, void *ev);
static void _ecore_con_event_server_data_free(void *data, void *ev);
EAPI int ECORE_CON_EVENT_CLIENT_ADD = 0;
EAPI int ECORE_CON_EVENT_CLIENT_DEL = 0;
EAPI int ECORE_CON_EVENT_SERVER_ADD = 0;
EAPI int ECORE_CON_EVENT_SERVER_DEL = 0;
EAPI int ECORE_CON_EVENT_CLIENT_DATA = 0;
EAPI int ECORE_CON_EVENT_SERVER_DATA = 0;
static Ecore_List *servers = NULL;
static int init_count = 0;
#if USE_OPENSSL
static int ssl_init_count = 0;
#endif
#define LENGTH_OF_SOCKADDR_UN(s) (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
#define LENGTH_OF_ABSTRACT_SOCKADDR_UN(s, path) (strlen(path) + 1 + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
/**
* @defgroup Ecore_Con_Lib_Group Ecore Connection Library Functions
*
* Utility functions that set up and shut down the Ecore Connection
* library.
*/
/**
* Initialises the Ecore_Con library.
* @return Number of times the library has been initialised without being
* shut down.
* @ingroup Ecore_Con_Lib_Group
*/
EAPI int
ecore_con_init(void)
{
if (++init_count != 1) return init_count;
ecore_init();
ECORE_CON_EVENT_CLIENT_ADD = ecore_event_type_new();
ECORE_CON_EVENT_CLIENT_DEL = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_ADD = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_DEL = ecore_event_type_new();
ECORE_CON_EVENT_CLIENT_DATA = ecore_event_type_new();
ECORE_CON_EVENT_SERVER_DATA = ecore_event_type_new();
/* TODO Remember return value, if it fails, use gethostbyname() */
ecore_con_dns_init();
servers = ecore_list_new();
return init_count;
}
/**
* Shuts down the Ecore_Con library.
* @return Number of times the library has been initialised without being
* shut down.
* @ingroup Ecore_Con_Lib_Group
*/
EAPI int
ecore_con_shutdown(void)
{
if (--init_count != 0) return init_count;
while (!ecore_list_empty_is(servers))
_ecore_con_server_free(ecore_list_first_remove(servers));
ecore_list_destroy(servers);
servers = NULL;
ecore_con_dns_shutdown();
ecore_shutdown();
return init_count;
}
/**
* @defgroup Ecore_Con_Server_Group Ecore Connection Server Functions
*
* Functions that operate on Ecore server objects.
*/
/**
* Creates a server to listen for connections.
*
* The socket on which the server listens depends on the connection
* type:
* @li If @a compl_type is @c ECORE_CON_LOCAL_USER, the server will listen on
* the Unix socket "~/.ecore/[name]/[port]".
* @li If @a compl_type is @c ECORE_CON_LOCAL_SYSTEM, the server will listen
* on Unix socket "/tmp/.ecore_service|[name]|[port]".
* @li If @a compl_type is @c ECORE_CON_REMOTE_SYSTEM, the server will listen
* on TCP port @c port.
*
* @param compl_type The connection type.
* @param name Name to associate with the socket. It is used when
* generating the socket name of a Unix socket. Though
* it is not used for the TCP socket, it still needs to
* be a valid character array. @c NULL will not be
* accepted.
* @param port Number to identify socket. When a Unix socket is used,
* it becomes part of the socket name. When a TCP socket
* is used, it is used as the TCP port.
* @param data Data to associate with the created Ecore_Con_Server
* object.
* @return A new Ecore_Con_Server.
* @ingroup Ecore_Con_Server_Group
*/
EAPI Ecore_Con_Server *
ecore_con_server_add(Ecore_Con_Type compl_type,
const char *name,
int port,
const void *data)
{
Ecore_Con_Server *svr;
Ecore_Con_Type type;
struct sockaddr_in socket_addr;
struct sockaddr_un socket_unix;
struct linger lin;
char buf[4096];
if (port < 0) return NULL;
/* local user socket: FILE: ~/.ecore/[name]/[port] */
/* local system socket: FILE: /tmp/.ecore_service|[name]|[port] */
/* remote system socket: TCP/IP: [name]:[port] */
svr = calloc(1, sizeof(Ecore_Con_Server));
if (!svr) return NULL;
type = compl_type;
#if USE_OPENSSL
/* unset the SSL flag for the following checks */
type &= ~ECORE_CON_USE_SSL;
#endif
if ((type == ECORE_CON_LOCAL_USER) ||
(type == ECORE_CON_LOCAL_SYSTEM) ||
(type == ECORE_CON_LOCAL_ABSTRACT))
{
const char *homedir;
struct stat st;
mode_t pmode, mask;
int socket_unix_len;
if (!name) goto error;
mask =
S_IRGRP | S_IWGRP | S_IXGRP |
S_IROTH | S_IWOTH | S_IXOTH;
if (type == ECORE_CON_LOCAL_USER)
{
homedir = getenv("HOME");
if (!homedir) homedir = getenv("TMP");
if (!homedir) homedir = "/tmp";
mask = S_IRUSR | S_IWUSR | S_IXUSR;
snprintf(buf, sizeof(buf), "%s/.ecore", homedir);
if (stat(buf, &st) < 0) mkdir(buf, mask);
snprintf(buf, sizeof(buf), "%s/.ecore/%s", homedir, name);
if (stat(buf, &st) < 0) mkdir(buf, mask);
snprintf(buf, sizeof(buf), "%s/.ecore/%s/%i", homedir, name, port);
mask =
S_IRGRP | S_IWGRP | S_IXGRP |
S_IROTH | S_IWOTH | S_IXOTH;
}
else if (type == ECORE_CON_LOCAL_SYSTEM)
{
mask = 0;
if (name[0] == '/')
snprintf(buf, sizeof(buf), "%s|%i", name, port);
else
snprintf(buf, sizeof(buf), "/tmp/.ecore_service|%s|%i", name, port);
}
else if (type == ECORE_CON_LOCAL_ABSTRACT)
{
strncpy(buf, name, sizeof(buf));
}
pmode = umask(mask);
start:
svr->fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (svr->fd < 0)
{
umask(pmode);
goto error;
}
if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0)
{
umask(pmode);
goto error;
}
if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0)
{
umask(pmode);
goto error;
}
lin.l_onoff = 1;
lin.l_linger = 0;
if (setsockopt(svr->fd, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0)
{
umask(pmode);
goto error;
}
socket_unix.sun_family = AF_UNIX;
if (type == ECORE_CON_LOCAL_ABSTRACT)
{
#ifdef HAVE_ABSTRACT_SOCKET
/* . is a placeholder */
snprintf(socket_unix.sun_path, sizeof(socket_unix.sun_path), ".%s", name);
/* first char null indicates abstract namespace */
socket_unix.sun_path[0] = '\0';
socket_unix_len = LENGTH_OF_ABSTRACT_SOCKADDR_UN(&socket_unix, name);
#else
fprintf(stderr, "Your system does not support abstract sockets!\n");
umask(pmode);
goto error;
#endif
}
else
{
strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path));
socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
}
if (bind(svr->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
{
if (connect(svr->fd, (struct sockaddr *)&socket_unix,
socket_unix_len) < 0)
{
if ((type == ECORE_CON_LOCAL_USER) ||
(type == ECORE_CON_LOCAL_SYSTEM))
{
if (unlink(buf) < 0)
{
umask(pmode);
goto error;
}
else
goto start;
}
else
{
umask(pmode);
goto error;
}
}
else
{
umask(pmode);
goto error;
}
}
if (listen(svr->fd, 4096) < 0)
{
umask(pmode);
goto error;
}
svr->path = strdup(buf);
if (!svr->path)
{
umask(pmode);
goto error;
}
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ,
_ecore_con_svr_handler, svr,
NULL, NULL);
umask(pmode);
if (!svr->fd_handler) goto error;
}
else if (type == ECORE_CON_REMOTE_SYSTEM)
{
svr->fd = socket(AF_INET, SOCK_STREAM, 0);
if (svr->fd < 0) goto error;
if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
lin.l_onoff = 1;
lin.l_linger = 0;
if (setsockopt(svr->fd, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0) goto error;
socket_addr.sin_family = AF_INET;
socket_addr.sin_port = htons(port);
socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(svr->fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in)) < 0) goto error;
if (listen(svr->fd, 4096) < 0) goto error;
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ,
_ecore_con_svr_handler, svr,
NULL, NULL);
if (!svr->fd_handler) goto error;
}
#if USE_OPENSSL
if (compl_type & ECORE_CON_USE_SSL)
{
if (!ssl_init_count)
{
SSL_library_init();
SSL_load_error_strings();
}
ssl_init_count++;
/* SSLv3 gives *weird* results on my box, don't use it yet */
if (!(svr->ssl_ctx = SSL_CTX_new(SSLv2_client_method())))
goto error;
if (!(svr->ssl = SSL_new(svr->ssl_ctx)))
goto error;
SSL_set_fd(svr->ssl, svr->fd);
}
#endif
svr->name = strdup(name);
if (!svr->name) goto error;
svr->type = type;
svr->port = port;
svr->data = (void *)data;
svr->created = 1;
svr->reject_excess_clients = 0;
svr->client_limit = -1;
svr->clients = ecore_list_new();
svr->ppid = getpid();
ecore_list_append(servers, svr);
ECORE_MAGIC_SET(svr, ECORE_MAGIC_CON_SERVER);
return svr;
error:
if (svr->name) free(svr->name);
if (svr->path) free(svr->path);
if (svr->fd >= 0) close(svr->fd);
if (svr->fd_handler) ecore_main_fd_handler_del(svr->fd_handler);
if (svr->write_buf) free(svr->write_buf);
if (svr->ip) free(svr->ip);
#if USE_OPENSSL
if (svr->ssl) SSL_free(svr->ssl);
if (svr->ssl_ctx) SSL_CTX_free(svr->ssl_ctx);
#endif
free(svr);
return NULL;
}
/**
* Creates a server object to represent the server listening at the
* given port.
*
* The socket to which the server connects depends on the connection type:
* @li If @a compl_type is @c ECORE_CON_LOCAL_USER, the function will
* connect to the server listening on the Unix socket
* "~/.ecore/[name]/[port]".
* @li If @a compl_type is @c ECORE_CON_LOCAL_SYSTEM, the function will
* connect to the server listening on the Unix socket
* "/tmp/.ecore_service|[name]|[port]".
* @li If @a compl_type is @c ECORE_CON_REMOTE_SYSTEM, the function will
* connect to the server listening on the TCP port "[name]:[port]".
*
* @param compl_type The connection type.
* @param name Name used when determining what socket to connect to.
* It is used to generate the socket name when the socket
* is a Unix socket. It is used as the hostname when
* connecting with a TCP socket.
* @param port Number to identify the socket to connect to. Used when
* generating the socket name for a Unix socket, or as the
* TCP port when connecting to a TCP socket.
* @param data Data to associate with the created Ecore_Con_Server
* object.
* @return A new Ecore_Con_Server.
* @ingroup Ecore_Con_Server_Group
*/
EAPI Ecore_Con_Server *
ecore_con_server_connect(Ecore_Con_Type compl_type,
const char *name,
int port,
const void *data)
{
Ecore_Con_Server *svr;
Ecore_Con_Type type;
struct sockaddr_un socket_unix;
int curstate = 0;
char buf[4096];
if (!name) return NULL;
/* local user socket: FILE: ~/.ecore/[name]/[port] */
/* local system socket: FILE: /tmp/.ecore_service|[name]|[port] */
/* remote system socket: TCP/IP: [name]:[port] */
svr = calloc(1, sizeof(Ecore_Con_Server));
if (!svr) return NULL;
type = compl_type;
#if USE_OPENSSL
/* unset the SSL flag for the following checks */
type &= ~ECORE_CON_USE_SSL;
#endif
if ((type == ECORE_CON_REMOTE_SYSTEM) && (port < 0)) return NULL;
if ((type == ECORE_CON_LOCAL_USER) ||
(type == ECORE_CON_LOCAL_SYSTEM) ||
(type == ECORE_CON_LOCAL_ABSTRACT))
{
const char *homedir;
int socket_unix_len;
if (type == ECORE_CON_LOCAL_USER)
{
homedir = getenv("HOME");
if (!homedir) homedir = getenv("TMP");
if (!homedir) homedir = "/tmp";
snprintf(buf, sizeof(buf), "%s/.ecore/%s/%i", homedir, name, port);
}
else if (type == ECORE_CON_LOCAL_SYSTEM)
{
if (port < 0)
{
if (name[0] == '/')
strncpy(buf, name, sizeof(buf));
else
snprintf(buf, sizeof(buf), "/tmp/.ecore_service|%s", name);
}
else
{
if (name[0] == '/')
snprintf(buf, sizeof(buf), "%s|%i", name, port);
else
snprintf(buf, sizeof(buf), "/tmp/.ecore_service|%s|%i", name, port);
}
}
else if (type == ECORE_CON_LOCAL_ABSTRACT)
{
strncpy(buf, name, sizeof(buf));
}
svr->fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (svr->fd < 0) goto error;
if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate)) < 0) goto error;
socket_unix.sun_family = AF_UNIX;
if (type == ECORE_CON_LOCAL_ABSTRACT)
{
#ifdef HAVE_ABSTRACT_SOCKETS
/* copy name insto sun_path, prefixed by null to indicate abstract namespace */
snprintf(socket_unix.sun_path, sizeof(socket_unix.sun_path), ".%s", name);
socket_unix.sun_path[0] = '\0';
socket_unix_len = LENGTH_OF_ABSTRACT_SOCKADDR_UN(&socket_unix, name);
#else
fprintf(stderr, "Your system does not support abstract sockets!\n");
goto error;
#endif
}
else
{
strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path));
socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
}
if (connect(svr->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) goto error;
svr->path = strdup(buf);
if (!svr->path) goto error;
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ,
_ecore_con_cl_handler, svr,
NULL, NULL);
if (!svr->fd_handler) goto error;
if (!svr->delete_me)
{
/* we got our server! */
Ecore_Con_Event_Server_Add *e;
e = calloc(1, sizeof(Ecore_Con_Event_Server_Add));
if (e)
{
svr->event_count++;
e->server = svr;
ecore_event_add(ECORE_CON_EVENT_SERVER_ADD, e,
_ecore_con_event_server_add_free, NULL);
}
}
}
svr->name = strdup(name);
if (!svr->name) goto error;
svr->type = compl_type;
svr->port = port;
svr->data = (void *)data;
svr->created = 0;
svr->reject_excess_clients = 0;
svr->client_limit = -1;
svr->clients = ecore_list_new();
ecore_list_append(servers, svr);
ECORE_MAGIC_SET(svr, ECORE_MAGIC_CON_SERVER);
if (type == ECORE_CON_REMOTE_SYSTEM)
{
if (!ecore_con_dns_lookup(svr->name, _ecore_con_cb_dns_lookup, svr))
goto error;
}
return svr;
error:
if (svr->name) free(svr->name);
if (svr->path) free(svr->path);
if (svr->fd >= 0) close(svr->fd);
if (svr->fd_handler) ecore_main_fd_handler_del(svr->fd_handler);
#if USE_OPENSSL
if (svr->ssl) SSL_free(svr->ssl);
if (svr->ssl_ctx) SSL_CTX_free(svr->ssl_ctx);
#endif
free(svr);
return NULL;
}
/**
* Closes the connection and frees the given server.
* @param svr The given server.
* @return Data associated with the server when it was created.
* @ingroup Ecore_Con_Server_Group
*/
EAPI void *
ecore_con_server_del(Ecore_Con_Server *svr)
{
void *data;
if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
{
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
"ecore_con_server_del");
return NULL;
}
data = svr->data;
svr->data = NULL;
svr->delete_me = 1;
if (svr->event_count > 0)
{
if (svr->fd_handler)
{
ecore_main_fd_handler_del(svr->fd_handler);
svr->fd_handler = NULL;
}
}
else
{
_ecore_con_server_free(svr);
if (ecore_list_goto(servers, svr)) ecore_list_remove(servers);
}
return data;
}
/**
* Retrieves the data associated with the given server.
* @param svr The given server.
* @return The associated data.
* @ingroup Ecore_Con_Server_Group
*/
EAPI void *
ecore_con_server_data_get(Ecore_Con_Server *svr)
{
if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
{
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
"ecore_con_server_data_get");
return NULL;
}
return svr->data;
}
/**
* Retrieves whether the given server is currently connected.
* @todo Check that this function does what the documenter believes it does.
* @param svr The given server.
* @return @c 1 if the server is connected. @c 0 otherwise.
* @ingroup Ecore_Con_Server_Group
*/
EAPI int
ecore_con_server_connected_get(Ecore_Con_Server *svr)
{
if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
{
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
"ecore_con_server_connected_get");
return 0;
}
if (svr->connecting) return 0;
return 1;
}
/**
* Retrieves the current list of clients.
* @param svr The given server.
* @return The list of clients on this server.
* @ingroup Ecore_Con_Server_Group
*/
EAPI Ecore_List*
ecore_con_server_clients_get(Ecore_Con_Server *svr)
{
if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
{
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
"ecore_con_server_clients_get");
return NULL;
}
return svr->clients;
}
/**
* Sends the given data to the given server.
* @param svr The given server.
* @param data The given data.
* @param size Length of the data, in bytes, to send.
* @return The number of bytes sent. @c 0 will be returned if there is an
* error.
* @ingroup Ecore_Con_Server_Group
*/
EAPI int
ecore_con_server_send(Ecore_Con_Server *svr, const void *data, int size)
{
if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
{
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
"ecore_con_server_send");
return 0;
}
if (svr->dead) return 0;
if (!data) return 0;
if (size < 1) return 0;
if (svr->fd_handler)
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
if (svr->write_buf)
{
unsigned char *newbuf;
newbuf = realloc(svr->write_buf, svr->write_buf_size + size);
if (newbuf) svr->write_buf = newbuf;
else return 0;
memcpy(svr->write_buf + svr->write_buf_size, data, size);
svr->write_buf_size += size;
}
else
{
svr->write_buf = malloc(size);
if (!svr->write_buf) return 0;
svr->write_buf_size = size;
memcpy(svr->write_buf, data, size);
}
return size;
}
/**
* Sets a limit on the number of clients that can be handled concurrently
* by the given server, and a policy on what to do if excess clients try to
* connect.
* Beware that if you set this once ecore is already running, you may
* already have pending CLIENT_ADD events in your event queue. Those
* clients have already connected and will not be affected by this call.
* Only clients subsequently trying to connect will be affected.
* @param svr The given server.
* @param client_limit The maximum number of clients to handle
* concurrently. -1 means unlimited (default). 0
* effectively disables the server.
* @param reject_excess_clients Set to 1 to automatically disconnect
* excess clients as soon as they connect if you are
* already handling client_limit clients. Set to 0
* (default) to just hold off on the "accept()"
* system call until the number of active clients
* drops. This causes the kernel to queue up to 4096
* connections (or your kernel's limit, whichever is
* lower).
* @ingroup Ecore_Con_Server_Group
*/
EAPI void
ecore_con_server_client_limit_set(Ecore_Con_Server *svr, int client_limit, char reject_excess_clients)
{
if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
{
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
"ecore_con_server_client_limit_set");
return;
}
svr->client_limit = client_limit;
svr->reject_excess_clients = reject_excess_clients;
}
/**
* Gets the IP address of a server that has been connected to.
*
* @param svr The given server.
* @return A pointer to an internal string that contains the IP address of
* the connected server in the form "XXX.YYY.ZZZ.AAA" IP notation.
* This string should not be modified or trusted to stay valid after
* deletion for the @p svr object. If no IP is known NULL is returned.
* @ingroup Ecore_Con_Server_Group
*/
EAPI char *
ecore_con_server_ip_get(Ecore_Con_Server *svr)
{
if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
{
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
"ecore_con_server_ip_get");
return NULL;
}
return svr->ip;
}
/**
* Flushes all pending data to the given server. Will return when done.
*
* @param svr The given server.
* @ingroup Ecore_Con_Server_Group
*/
EAPI void
ecore_con_server_flush(Ecore_Con_Server *svr)
{
if (!ECORE_MAGIC_CHECK(svr, ECORE_MAGIC_CON_SERVER))
{
ECORE_MAGIC_FAIL(svr, ECORE_MAGIC_CON_SERVER,
"ecore_con_server_flush");
return;
}
_ecore_con_server_flush(svr);
}
/**
* @defgroup Ecore_Con_Client_Group Ecore Connection Client Functions
*
* Functions that operate on Ecore connection client objects.
*/
/**
* Sends the given data to the given client.
* @param cl The given client.
* @param data The given data.
* @param size Length of the data, in bytes, to send.
* @return The number of bytes sent. @c 0 will be returned if there is an
* error.
* @ingroup Ecore_Con_Client_Group
*/
EAPI int
ecore_con_client_send(Ecore_Con_Client *cl, void *data, int size)
{
if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
{
ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT,
"ecore_con_client_send");
return 0;
}
if (cl->dead) return 0;
if (!data) return 0;
if (size < 1) return 0;
if (cl->fd_handler)
ecore_main_fd_handler_active_set(cl->fd_handler, ECORE_FD_READ | ECORE_FD_WRITE);
if (cl->buf)
{
unsigned char *newbuf;
newbuf = realloc(cl->buf, cl->buf_size + size);
if (newbuf) cl->buf = newbuf;
else return 0;
memcpy(cl->buf + cl->buf_size, data, size);
cl->buf_size += size;
}
else
{
cl->buf = malloc(size);
if (!cl->buf) return 0;
cl->buf_size = size;
memcpy(cl->buf, data, size);
}
return size;
}
/**
* Retrieves the server representing the socket the client has
* connected to.
* @param cl The given client.
* @return The server that the client connected to.
* @ingroup Ecore_Con_Client_Group
*/
EAPI Ecore_Con_Server *
ecore_con_client_server_get(Ecore_Con_Client *cl)
{
if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
{
ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT,
"ecore_con_client_server_get");
return NULL;
}
return cl->server;
}
/**
* Closes the connection and frees memory allocated to the given client.
* @param cl The given client.
* @return Data associated with the client.
* @ingroup Ecore_Con_Client_Group
*/
EAPI void *
ecore_con_client_del(Ecore_Con_Client *cl)
{
void *data;
if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
{
ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT,
"ecore_con_client_del");
return NULL;
}
data = cl->data;
cl->data = NULL;
cl->delete_me = 1;
if (cl->event_count > 0)
{
if (cl->fd_handler)
{
ecore_main_fd_handler_del(cl->fd_handler);
cl->fd_handler = NULL;
}
}
else
{
if (ecore_list_goto(cl->server->clients, cl))
ecore_list_remove(cl->server->clients);
_ecore_con_client_free(cl);
}
return data;
}
/**
* Sets the data associated with the given client to @p data.
* @param cl The given client.
* @param data What to set the data to.
* @ingroup Ecore_Con_Client_Group
*/
EAPI void
ecore_con_client_data_set(Ecore_Con_Client *cl, const void *data)
{
if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
{
ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT,
"ecore_con_client_data_set");
return;
}
cl->data = (void *)data;
}
/**
* Retrieves the data associated with the given client.
* @param cl The given client.
* @return The data associated with @p cl.
* @ingroup Ecore_Con_Client_Group
*/
EAPI void *
ecore_con_client_data_get(Ecore_Con_Client *cl)
{
if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
{
ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT,
"ecore_con_client_data_get");
return NULL;
}
return cl->data;
}
/**
* Gets the IP address of a cleint that has connected.
*
* @param cl The given client.
* @return A pointer to an internal string that contains the IP address of
* the connected client in the form "XXX.YYY.ZZZ.AAA" IP notation.
* This string should not be modified or trusted to stay valid after
* deletion for the @p cl object. If no IP is known NULL is returned.
* @ingroup Ecore_Con_Client_Group
*/
EAPI char *
ecore_con_client_ip_get(Ecore_Con_Client *cl)
{
if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
{
ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT,
"ecore_con_client_ip_get");
return NULL;
}
return cl->ip;
}
/**
* Flushes all pending data to the given client. Will return when done.
*
* @param cl The given client.
* @ingroup Ecore_Con_Client_Group
*/
EAPI void
ecore_con_client_flush(Ecore_Con_Client *cl)
{
if (!ECORE_MAGIC_CHECK(cl, ECORE_MAGIC_CON_CLIENT))
{
ECORE_MAGIC_FAIL(cl, ECORE_MAGIC_CON_CLIENT,
"ecore_con_client_flush");
return;
}
_ecore_con_client_flush(cl);
}
/**
* Returns if SSL support is available
* @return 1 if SSL is available, 0 if it is not.
* @ingroup Ecore_Con_Client_Group
*/
EAPI int
ecore_con_ssl_available_get(void)
{
#if USE_OPENSSL
return 1;
#else
return 0;
#endif
}
static void
_ecore_con_server_free(Ecore_Con_Server *svr)
{
double t_start, t;
ECORE_MAGIC_SET(svr, ECORE_MAGIC_NONE);
t_start = ecore_time_get();
while ((svr->write_buf) && (!svr->dead))
{
_ecore_con_server_flush(svr);
t = ecore_time_get();
if ((t - t_start) > 0.5)
{
printf("ECORE_CON: EEK - stuck in _ecore_con_server_free() trying\n"
" to flush data out from the server, and have been for\n"
" %1.1f seconds. This is taking too long. Aborting flush.\n",
(t - t_start));
break;
}
}
if (svr->write_buf) free(svr->write_buf);
while (!ecore_list_empty_is(svr->clients))
_ecore_con_client_free(ecore_list_first_remove(svr->clients));
ecore_list_destroy(svr->clients);
if ((svr->created) && (svr->path) && (svr->ppid == getpid())) unlink(svr->path);
if (svr->fd >= 0) close(svr->fd);
#if USE_OPENSSL
if (svr->ssl)
{
SSL_shutdown(svr->ssl);
SSL_free(svr->ssl);
}
if (svr->ssl_ctx) SSL_CTX_free(svr->ssl_ctx);
#endif
if (svr->name) free(svr->name);
if (svr->path) free(svr->path);
if (svr->ip) free(svr->ip);
if (svr->fd_handler) ecore_main_fd_handler_del(svr->fd_handler);
free(svr);
}
static void
_ecore_con_client_free(Ecore_Con_Client *cl)
{
double t_start, t;
ECORE_MAGIC_SET(cl, ECORE_MAGIC_NONE);
t_start = ecore_time_get();
while ((cl->buf) && (!cl->dead))
{
_ecore_con_client_flush(cl);
t = ecore_time_get();
if ((t - t_start) > 0.5)
{
printf("ECORE_CON: EEK - stuck in _ecore_con_client_free() trying\n"
" to flush data out from the client, and have been for\n"
" %1.1f seconds. This is taking too long. Aborting flush.\n",
(t - t_start));
break;
}
}
if (cl->buf) free(cl->buf);
if (cl->fd >= 0) close(cl->fd);
if (cl->fd_handler) ecore_main_fd_handler_del(cl->fd_handler);
if (cl->ip) free(cl->ip);
free(cl);
}
static int
_ecore_con_svr_handler(void *data, Ecore_Fd_Handler *fd_handler __UNUSED__)
{
Ecore_Con_Server *svr;
int new_fd;
struct sockaddr_in incoming;
size_t size_in;
svr = data;
if (svr->dead) return 1;
if (svr->delete_me) return 1;
if ((svr->client_limit >= 0) && (!svr->reject_excess_clients))
{
if (ecore_list_count(svr->clients) >= svr->client_limit) return 1;
}
/* a new client */
size_in = sizeof(struct sockaddr_in);
new_fd = accept(svr->fd, (struct sockaddr *)&incoming, (socklen_t *)&size_in);
if (new_fd >= 0)
{
Ecore_Con_Client *cl;
char buf[64];
uint32_t ip;
if ((svr->client_limit >= 0) && (svr->reject_excess_clients))
{
close(new_fd);
return 1;
}
cl = calloc(1, sizeof(Ecore_Con_Client));
if (!cl)
{
close(new_fd);
return 1;
}
fcntl(new_fd, F_SETFL, O_NONBLOCK);
fcntl(new_fd, F_SETFD, FD_CLOEXEC);
cl->fd = new_fd;
cl->server = svr;
cl->fd_handler = ecore_main_fd_handler_add(cl->fd,
ECORE_FD_READ,
_ecore_con_svr_cl_handler,
cl, NULL, NULL);
ECORE_MAGIC_SET(cl, ECORE_MAGIC_CON_CLIENT);
ecore_list_append(svr->clients, cl);
if (!svr->path)
{
ip = incoming.sin_addr.s_addr;
snprintf(buf, sizeof(buf),
"%i.%i.%i.%i",
(ip ) & 0xff,
(ip >> 8 ) & 0xff,
(ip >> 16) & 0xff,
(ip >> 24) & 0xff);
cl->ip = strdup(buf);
}
if (!cl->delete_me)
{
Ecore_Con_Event_Client_Add *e;
e = calloc(1, sizeof(Ecore_Con_Event_Client_Add));
if (e)
{
cl->event_count++;
e->client = cl;
ecore_event_add(ECORE_CON_EVENT_CLIENT_ADD, e,
_ecore_con_event_client_add_free, NULL);
}
}
}
return 1;
}
#if USE_OPENSSL
/* Tries to connect an Ecore_Con_Server to an SSL host.
* Returns 1 on success, -1 on fatal errors and 0 if the caller
* should try again later.
*/
static int
svr_try_connect_ssl(Ecore_Con_Server *svr)
{
int res, ssl_err, flag = 0;
if (!ssl_init_count)
{
SSL_library_init();
SSL_load_error_strings();
}
ssl_init_count++;
res = SSL_connect(svr->ssl);
if (res == 1) return 1;
ssl_err = SSL_get_error(svr->ssl, res);
if (ssl_err == SSL_ERROR_NONE) return 1;
if (ssl_err == SSL_ERROR_WANT_READ) flag = ECORE_FD_READ;
else if (ssl_err == SSL_ERROR_WANT_WRITE) flag = ECORE_FD_WRITE;
else return -1;
if (svr->fd_handler)
{
if (flag) ecore_main_fd_handler_active_set(svr->fd_handler, flag);
}
return 0;
}
#endif
static void
kill_server(Ecore_Con_Server *svr)
{
if (!svr->delete_me)
{
Ecore_Con_Event_Server_Del *e;
e = calloc(1, sizeof(Ecore_Con_Event_Server_Del));
if (e)
{
svr->event_count++;
e->server = svr;
ecore_event_add(ECORE_CON_EVENT_SERVER_DEL, e,
_ecore_con_event_server_del_free, NULL);
}
}
svr->dead = 1;
if (svr->fd_handler) ecore_main_fd_handler_del(svr->fd_handler);
svr->fd_handler = NULL;
}
static void
_ecore_con_cb_dns_lookup(void *data, struct hostent *he)
{
Ecore_Con_Server *svr;
struct sockaddr_in socket_addr;
int curstate = 0;
char buf[64];
uint32_t ip;
svr = data;
if (!he) goto error;
svr->fd = socket(AF_INET, SOCK_STREAM, 0);
if (svr->fd < 0) goto error;
if (fcntl(svr->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
if (fcntl(svr->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
if (setsockopt(svr->fd, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate)) < 0) goto error;
socket_addr.sin_family = AF_INET;
socket_addr.sin_port = htons(svr->port);
memcpy((struct in_addr *)&socket_addr.sin_addr,
he->h_addr, sizeof(struct in_addr));
if (connect(svr->fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in)) < 0)
{
if (errno != EINPROGRESS)
goto error;
svr->connecting = 1;
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ | ECORE_FD_WRITE,
_ecore_con_cl_handler, svr,
NULL, NULL);
}
else
svr->fd_handler = ecore_main_fd_handler_add(svr->fd,
ECORE_FD_READ,
_ecore_con_cl_handler, svr,
NULL, NULL);
if (!svr->fd_handler) goto error;
ip = socket_addr.sin_addr.s_addr;
snprintf(buf, sizeof(buf),
"%i.%i.%i.%i",
(ip ) & 0xff,
(ip >> 8 ) & 0xff,
(ip >> 16) & 0xff,
(ip >> 24) & 0xff);
svr->ip = strdup(buf);
#if USE_OPENSSL
if (svr->type & ECORE_CON_USE_SSL)
{
if (!ssl_init_count)
{
SSL_library_init();
SSL_load_error_strings();
}
ssl_init_count++;
/* SSLv3 gives *weird* results on my box, don't use it yet */
if (!(svr->ssl_ctx = SSL_CTX_new(SSLv2_client_method())))
goto error;
if (!(svr->ssl = SSL_new(svr->ssl_ctx)))
goto error;
SSL_set_fd(svr->ssl, svr->fd);
}
#endif
return;
error:
kill_server(svr);
}
static int
svr_try_connect_plain(Ecore_Con_Server *svr)
{
int so_err = 0;
unsigned int size = sizeof(int);
if (getsockopt(svr->fd, SOL_SOCKET, SO_ERROR, &so_err, &size) < 0)
so_err = -1;
if (so_err != 0)
{
/* we lost our server! */
kill_server(svr);
}
else
{
if (!svr->delete_me)
{
/* we got our server! */
Ecore_Con_Event_Server_Add *e;
svr->connecting = 0;
e = calloc(1, sizeof(Ecore_Con_Event_Server_Add));
if (e)
{
svr->event_count++;
e->server = svr;
ecore_event_add(ECORE_CON_EVENT_SERVER_ADD, e,
_ecore_con_event_server_add_free, NULL);
}
}
if (svr->fd_handler)
{
if (!svr->write_buf)
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ);
}
}
return (!svr->dead);
}
/* returns 1 on success, 0 on failure */
static int svr_try_connect(Ecore_Con_Server *svr)
{
#if USE_OPENSSL
if (!svr->ssl)
{
#endif
return svr_try_connect_plain(svr);
#if USE_OPENSSL
}
else
switch (svr_try_connect_ssl(svr)) {
case 1:
return svr_try_connect_plain(svr);
case -1:
kill_server(svr);
return 0;
default:
return 0;
}
#endif
}
static int
_ecore_con_cl_handler(void *data, Ecore_Fd_Handler *fd_handler)
{
Ecore_Con_Server *svr;
#if USE_OPENSSL
int ssl_err = SSL_ERROR_NONE;
#endif
svr = data;
if (svr->dead) return 1;
if (svr->delete_me) return 1;
if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
{
unsigned char *inbuf = NULL;
int inbuf_num = 0;
if (svr->connecting && !svr_try_connect(svr))
return 1;
for (;;)
{
int num, lost_server;
char buf[READBUFSIZ];
lost_server = 0;
#if USE_OPENSSL
if (!svr->ssl)
{
#endif
if ((num = read(svr->fd, buf, READBUFSIZ)) < 1)
{
lost_server = ((errno == EIO) ||
(errno == EBADF) ||
(errno == EPIPE) ||
(errno == EINVAL) ||
(errno == ENOSPC) ||
(num == 0));
/* is num == 0 is right - when the server closes us
* off we will get this (as this is called when select
* tells us there is data to read!)
*/
}
#if USE_OPENSSL
}
else
{
num = SSL_read(svr->ssl, buf, READBUFSIZ);
if (num < 1)
{
ssl_err = SSL_get_error(svr->ssl, num);
lost_server = (ssl_err == SSL_ERROR_ZERO_RETURN);
}
else
ssl_err = SSL_ERROR_NONE;
}
#endif
if (num < 1)
{
if (inbuf)
{
if (!svr->delete_me)
{
Ecore_Con_Event_Server_Data *e;
e = calloc(1, sizeof(Ecore_Con_Event_Server_Data));
if (e)
{
svr->event_count++;
e->server = svr;
e->data = inbuf;
e->size = inbuf_num;
ecore_event_add(ECORE_CON_EVENT_SERVER_DATA, e,
_ecore_con_event_server_data_free,
NULL);
}
}
}
if (lost_server)
{
/* we lost our server! */
kill_server(svr);
return 1;
}
break;
}
else
{
inbuf = realloc(inbuf, inbuf_num + num);
memcpy(inbuf + inbuf_num, buf, num);
inbuf_num += num;
}
}
#if USE_OPENSSL
if (svr->fd_handler)
{
if (svr->ssl && ssl_err == SSL_ERROR_WANT_READ)
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ);
else if (svr->ssl && ssl_err == SSL_ERROR_WANT_WRITE)
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_WRITE);
}
#endif
}
else if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE))
{
if (svr->connecting && !svr_try_connect (svr))
return 1;
_ecore_con_server_flush(svr);
}
return 1;
}
static int
_ecore_con_svr_cl_handler(void *data, Ecore_Fd_Handler *fd_handler)
{
Ecore_Con_Client *cl;
cl = data;
if (cl->dead) return 1;
if (cl->delete_me) return 1;
if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ))
{
unsigned char *inbuf = NULL;
int inbuf_num = 0;
for (;;)
{
char buf[65536];
int num;
errno = 0;
num = read(cl->fd, buf, 65536);
if (num < 1)
{
if (inbuf)
{
if (!cl->delete_me)
{
Ecore_Con_Event_Client_Data *e;
e = calloc(1, sizeof(Ecore_Con_Event_Client_Data));
if (e)
{
cl->event_count++;
e->client = cl;
e->data = inbuf;
e->size = inbuf_num;
ecore_event_add(ECORE_CON_EVENT_CLIENT_DATA, e,
_ecore_con_event_client_data_free,
NULL);
}
}
}
if ((errno == EIO) || (errno == EBADF) ||
(errno == EPIPE) || (errno == EINVAL) ||
(errno == ENOSPC) || (num == 0)/* is num == 0 right? */)
{
if (!cl->delete_me)
{
/* we lost our client! */
Ecore_Con_Event_Client_Del *e;
e = calloc(1, sizeof(Ecore_Con_Event_Client_Del));
if (e)
{
cl->event_count++;
e->client = cl;
ecore_event_add(ECORE_CON_EVENT_CLIENT_DEL, e,
_ecore_con_event_client_del_free,
NULL);
}
}
cl->dead = 1;
if (cl->fd_handler)
ecore_main_fd_handler_del(cl->fd_handler);
cl->fd_handler = NULL;
}
break;
}
else
{
inbuf = realloc(inbuf, inbuf_num + num);
memcpy(inbuf + inbuf_num, buf, num);
inbuf_num += num;
}
}
}
else if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_WRITE))
_ecore_con_client_flush(cl);
return 1;
}
static void
_ecore_con_server_flush(Ecore_Con_Server *svr)
{
int count, num, lost_server = 0;
#if USE_OPENSSL
int ssl_err = SSL_ERROR_NONE;
#endif
if (!svr->write_buf) return;
/* check whether we need to write anything at all.
* we must not write zero bytes with SSL_write() since it
* causes undefined behaviour
*/
if (svr->write_buf_size == svr->write_buf_offset)
return;
num = svr->write_buf_size - svr->write_buf_offset;
#if USE_OPENSSL
if (!svr->ssl)
{
#endif
count = write(svr->fd, svr->write_buf + svr->write_buf_offset, num);
if (count < 1)
lost_server = (errno == EIO || errno == EBADF ||
errno == EPIPE || errno == EINVAL ||
errno == ENOSPC);
#if USE_OPENSSL
}
else
{
count = SSL_write(svr->ssl, svr->write_buf + svr->write_buf_offset, num);
if (count < 1)
{
ssl_err = SSL_get_error(svr->ssl, count);
lost_server = (ssl_err == SSL_ERROR_ZERO_RETURN);
}
}
#endif
if (lost_server)
{
/* we lost our server! */
kill_server(svr);
return;
}
if (count < 1)
{
#if USE_OPENSSL
if (svr->fd_handler)
{
if (svr->ssl && ssl_err == SSL_ERROR_WANT_READ)
ecore_main_fd_handler_active_set(svr->fd_handler,
ECORE_FD_READ);
else if (svr->ssl && ssl_err == SSL_ERROR_WANT_WRITE)
ecore_main_fd_handler_active_set(svr->fd_handler,
ECORE_FD_WRITE);
}
#endif
return;
}
svr->write_buf_offset += count;
if (svr->write_buf_offset >= svr->write_buf_size)
{
svr->write_buf_size = 0;
svr->write_buf_offset = 0;
free(svr->write_buf);
svr->write_buf = NULL;
if (svr->fd_handler)
ecore_main_fd_handler_active_set(svr->fd_handler, ECORE_FD_READ);
}
}
static void
_ecore_con_client_flush(Ecore_Con_Client *cl)
{
int count, num;
if (!cl->buf) return;
num = cl->buf_size - cl->buf_offset;
count = write(cl->fd, cl->buf + cl->buf_offset, num);
if (count < 1)
{
if ((errno == EIO) || (errno == EBADF) || (errno == EPIPE) ||
(errno == EINVAL) || (errno == ENOSPC))
{
if (!cl->delete_me)
{
/* we lost our client! */
Ecore_Con_Event_Client_Del *e;
e = calloc(1, sizeof(Ecore_Con_Event_Client_Del));
if (e)
{
cl->event_count++;
e->client = cl;
ecore_event_add(ECORE_CON_EVENT_CLIENT_DEL, e,
_ecore_con_event_client_del_free, NULL);
}
cl->dead = 1;
if (cl->fd_handler)
ecore_main_fd_handler_del(cl->fd_handler);
cl->fd_handler = NULL;
}
}
return;
}
cl->buf_offset += count;
if (cl->buf_offset >= cl->buf_size)
{
cl->buf_size = 0;
cl->buf_offset = 0;
free(cl->buf);
cl->buf = NULL;
if (cl->fd_handler)
ecore_main_fd_handler_active_set(cl->fd_handler, ECORE_FD_READ);
}
}
static void
_ecore_con_event_client_add_free(void *data __UNUSED__, void *ev)
{
Ecore_Con_Event_Client_Add *e;
e = ev;
e->client->event_count--;
if ((e->client->event_count == 0) && (e->client->delete_me))
ecore_con_client_del(e->client);
free(e);
}
static void
_ecore_con_event_client_del_free(void *data __UNUSED__, void *ev)
{
Ecore_Con_Event_Client_Del *e;
e = ev;
e->client->event_count--;
if ((e->client->event_count == 0) && (e->client->delete_me))
ecore_con_client_del(e->client);
free(e);
}
static void
_ecore_con_event_client_data_free(void *data __UNUSED__, void *ev)
{
Ecore_Con_Event_Client_Data *e;
e = ev;
e->client->event_count--;
if (e->data) free(e->data);
if ((e->client->event_count == 0) && (e->client->delete_me))
ecore_con_client_del(e->client);
free(e);
}
static void
_ecore_con_event_server_add_free(void *data __UNUSED__, void *ev)
{
Ecore_Con_Event_Server_Add *e;
e = ev;
e->server->event_count--;
if ((e->server->event_count == 0) && (e->server->delete_me))
ecore_con_server_del(e->server);
free(e);
}
static void
_ecore_con_event_server_del_free(void *data __UNUSED__, void *ev)
{
Ecore_Con_Event_Server_Del *e;
e = ev;
e->server->event_count--;
if ((e->server->event_count == 0) && (e->server->delete_me))
ecore_con_server_del(e->server);
free(e);
}
static void
_ecore_con_event_server_data_free(void *data __UNUSED__, void *ev)
{
Ecore_Con_Event_Server_Data *e;
e = ev;
e->server->event_count--;
if (e->data) free(e->data);
if ((e->server->event_count == 0) && (e->server->delete_me))
ecore_con_server_del(e->server);
free(e);
}