efl_net_server support systemd socket activation.
It includes extensive verifications to avoid mistakes and usage of incorrect sockets.
This commit is contained in:
parent
3ac1812a1f
commit
c2630c829f
|
@ -504,6 +504,8 @@ static const Ecore_Getopt options = {
|
|||
{
|
||||
ECORE_GETOPT_STORE_TRUE('e', "echo",
|
||||
"Behave as 'echo' server, send back to client all the data receive"),
|
||||
ECORE_GETOPT_STORE_TRUE(0, "socket-activated",
|
||||
"Try to use $LISTEN_FDS from systemd, if not do a regular serve()"),
|
||||
ECORE_GETOPT_STORE_UINT('l', "clients-limit",
|
||||
"If set will limit number of clients to accept"),
|
||||
ECORE_GETOPT_STORE_TRUE('r', "clients-reject-excess",
|
||||
|
@ -564,9 +566,11 @@ main(int argc, char **argv)
|
|||
Eina_List *crls = NULL;
|
||||
Eina_List *cas = NULL;
|
||||
char *cipher_choice = NULL;
|
||||
Eina_Bool socket_activated = EINA_FALSE;
|
||||
Eina_Bool quit_option = EINA_FALSE;
|
||||
Ecore_Getopt_Value values[] = {
|
||||
ECORE_GETOPT_VALUE_BOOL(echo),
|
||||
ECORE_GETOPT_VALUE_BOOL(socket_activated),
|
||||
ECORE_GETOPT_VALUE_UINT(clients_limit),
|
||||
ECORE_GETOPT_VALUE_BOOL(clients_reject_excess),
|
||||
ECORE_GETOPT_VALUE_BOOL(ipv6_only),
|
||||
|
@ -659,6 +663,8 @@ main(int argc, char **argv)
|
|||
efl_net_server_fd_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
||||
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
||||
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
||||
|
||||
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
|
||||
}
|
||||
else if (cls == EFL_NET_SERVER_UDP_CLASS)
|
||||
{
|
||||
|
@ -677,6 +683,7 @@ main(int argc, char **argv)
|
|||
efl_net_server_fd_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
||||
efl_net_server_fd_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
||||
efl_net_server_fd_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
||||
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
|
||||
}
|
||||
else if (cls == EFL_NET_SERVER_SSL_CLASS)
|
||||
{
|
||||
|
@ -708,11 +715,13 @@ main(int argc, char **argv)
|
|||
efl_net_server_ssl_close_on_exec_set(server, EINA_TRUE); /* recommended */
|
||||
efl_net_server_ssl_reuse_address_set(server, EINA_TRUE); /* optional, but nice for testing */
|
||||
efl_net_server_ssl_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
|
||||
if (socket_activated) efl_net_server_ssl_socket_activate(server, address);
|
||||
}
|
||||
#ifndef _WIN32
|
||||
else if (cls == EFL_NET_SERVER_UNIX_CLASS)
|
||||
{
|
||||
efl_net_server_unix_unlink_before_bind_set(server, EINA_TRUE); /* makes testing easier */
|
||||
if (socket_activated) efl_net_server_fd_socket_activate(server, address);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -721,12 +730,18 @@ main(int argc, char **argv)
|
|||
* with the object to add more properties that couldn't be done
|
||||
* during efl_add().
|
||||
*/
|
||||
err = efl_net_server_serve(server, address);
|
||||
if (err)
|
||||
if (!efl_net_server_serving_get(server))
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not serve(%s): %s\n",
|
||||
address, eina_error_msg_get(err));
|
||||
goto end_server;
|
||||
if (socket_activated)
|
||||
fprintf(stderr, "WARNING: --socket-activated, but not able to use $LISTEN_FDS descriptors. Try to start the server...\n");
|
||||
|
||||
err = efl_net_server_serve(server, address);
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not serve(%s): %s\n",
|
||||
address, eina_error_msg_get(err));
|
||||
goto end_server;
|
||||
}
|
||||
}
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
|
|
@ -3175,6 +3175,220 @@ efl_net_ip_port_split(char *buf, const char **p_host, const char **p_port)
|
|||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
Eina_Error
|
||||
efl_net_ip_socket_activate_check(const char *address, int family, int type, Eina_Bool *listening)
|
||||
{
|
||||
SOCKET fd = SD_LISTEN_FDS_START + sd_fd_index;
|
||||
int r;
|
||||
|
||||
if (sd_fd_index >= sd_fd_max) return ENOENT;
|
||||
|
||||
if (family == AF_UNIX)
|
||||
{
|
||||
char buf[sizeof(struct sockaddr_un)] = "";
|
||||
const char *sun_path;
|
||||
size_t len;
|
||||
|
||||
if (strncmp(address, "abstract:", strlen("abstract:")) == 0)
|
||||
{
|
||||
const char *path = address + strlen("abstract:");
|
||||
if (strlen(path) + 2 > sizeof(buf))
|
||||
{
|
||||
ERR("abstract path is too long: %s", path);
|
||||
return EINVAL;
|
||||
}
|
||||
buf[0] = '\0';
|
||||
memcpy(buf + 1, path, strlen(path) + 1);
|
||||
sun_path = buf;
|
||||
len = strlen(path) + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strlen(address) + 1 > sizeof(buf))
|
||||
{
|
||||
ERR("path is too long: %s", address);
|
||||
return EINVAL;
|
||||
}
|
||||
sun_path = address;
|
||||
len = strlen(address) + 1;
|
||||
}
|
||||
|
||||
r = sd_is_socket_unix(fd, type, 0, sun_path, len);
|
||||
if (r < 0)
|
||||
{
|
||||
ERR("socket %d is not of family=%d, type=%d", fd, family, type);
|
||||
return EINVAL;
|
||||
}
|
||||
if (listening) *listening = (r == 1);
|
||||
return 0;
|
||||
}
|
||||
else if ((family == AF_UNSPEC) || (family == AF_INET) || (family == AF_INET6))
|
||||
{
|
||||
char *str;
|
||||
const char *host, *port;
|
||||
struct sockaddr_storage sock_addr;
|
||||
struct sockaddr_storage want_addr = { .ss_family = family };
|
||||
socklen_t addrlen;
|
||||
Eina_Error err;
|
||||
int x;
|
||||
|
||||
r = sd_is_socket(fd, family, type, (type == SOCK_DGRAM) ? -1 : 0);
|
||||
if (r < 0)
|
||||
{
|
||||
ERR("socket %d is not of family=%d, type=%d", fd, family, type);
|
||||
return EINVAL;
|
||||
}
|
||||
if ((type == SOCK_DGRAM) && (listening)) *listening = EINA_FALSE;
|
||||
else if (listening) *listening = (r == 1);
|
||||
|
||||
addrlen = sizeof(sock_addr);
|
||||
if (getsockname(fd, (struct sockaddr *)&sock_addr, &addrlen) != 0)
|
||||
{
|
||||
err = efl_net_socket_error_get();
|
||||
ERR("could not query socket=%d name: %s", fd, eina_error_msg_get(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
str = strdup(address);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(str, ENOMEM);
|
||||
if (!efl_net_ip_port_split(str, &host, &port))
|
||||
{
|
||||
ERR("invalid IP:PORT address: %s", address);
|
||||
free(str);
|
||||
return EINVAL;
|
||||
}
|
||||
if (!port) port = "0";
|
||||
|
||||
if ((family == AF_UNSPEC) && (strchr(host, ':'))) family = AF_INET6;
|
||||
|
||||
if (family == AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 *a = (struct sockaddr_in6 *)&want_addr;
|
||||
x = inet_pton(AF_INET6, host, &a->sin6_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct sockaddr_in *a = (struct sockaddr_in *)&want_addr;
|
||||
x = inet_pton(AF_INET, host, &a->sin_addr);
|
||||
}
|
||||
|
||||
/* FAST PATH: numbers were provided */
|
||||
if (x == 1)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long p;
|
||||
Eina_Bool matches;
|
||||
|
||||
want_addr.ss_family = family;
|
||||
if (want_addr.ss_family != sock_addr.ss_family)
|
||||
{
|
||||
ERR("socket %d family=%d differs from wanted %d", fd, sock_addr.ss_family, want_addr.ss_family);
|
||||
free(str);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
p = strtoul(port, &endptr, 10);
|
||||
if ((errno) || (endptr == port) || (*endptr != '\0'))
|
||||
{
|
||||
ERR("invalid port number '%s'", port);
|
||||
free(str);
|
||||
return EINVAL;
|
||||
}
|
||||
else if (p > UINT16_MAX)
|
||||
{
|
||||
ERR("invalid port number %lu (out of range)", p);
|
||||
free(str);
|
||||
return ERANGE;
|
||||
}
|
||||
|
||||
if (family == AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 *a = (struct sockaddr_in6 *)&want_addr;
|
||||
a->sin6_port = htons(p);
|
||||
matches = memcmp(a, &sock_addr, sizeof(*a)) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct sockaddr_in *a = (struct sockaddr_in *)&want_addr;
|
||||
x = inet_pton(AF_INET, host, &a->sin_addr);
|
||||
a->sin_port = htons(p);
|
||||
matches = memcmp(a, &sock_addr, sizeof(*a)) == 0;
|
||||
}
|
||||
|
||||
if (!matches)
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")] = "";
|
||||
|
||||
efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&sock_addr);
|
||||
ERR("socket %d address %s differs from wanted %s", fd, buf, address);
|
||||
free(str);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
free(str);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* NOTE: this may block, but users should be using the IP:PORT
|
||||
* as numbers, getting into the fast path above.
|
||||
*
|
||||
* This is best-try to help API to be usable, but may
|
||||
* impact the main loop execution for a while. However
|
||||
* people doing bind are expected to do so on a local
|
||||
* address, usually resolves faster without too many DNS
|
||||
* lookups.
|
||||
*/
|
||||
struct addrinfo hints = {
|
||||
.ai_socktype = type,
|
||||
.ai_family = family,
|
||||
.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED,
|
||||
};
|
||||
struct addrinfo *results, *itr;
|
||||
|
||||
DBG("resolving '%s', it may block main loop! Consider using IP:PORT", address);
|
||||
do
|
||||
{
|
||||
x = getaddrinfo(host, port, &hints, &results);
|
||||
}
|
||||
while ((r == EAI_AGAIN) || ((r == EAI_SYSTEM) && (errno == EINTR)));
|
||||
|
||||
if (x != 0)
|
||||
{
|
||||
ERR("couldn't resolve host='%s', port='%s': %s",
|
||||
host, port, gai_strerror(x));
|
||||
free(str);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
err = EINVAL;
|
||||
for (itr = results; itr != NULL; itr = itr->ai_next)
|
||||
{
|
||||
if (sock_addr.ss_family != itr->ai_family) continue;
|
||||
if (memcmp(itr->ai_addr, &sock_addr, itr->ai_addrlen) == 0)
|
||||
{
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(results);
|
||||
free(str);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (listening) *listening = EINA_FALSE;
|
||||
ERR("unsupported family=%d", family);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
_cleanup_close(void *data)
|
||||
{
|
||||
|
|
|
@ -411,6 +411,30 @@ Eina_Bool efl_net_unix_fmt(char *buf, size_t buflen, SOCKET fd, const struct soc
|
|||
#endif
|
||||
Eina_Bool efl_net_ip_port_fmt(char *buf, size_t buflen, const struct sockaddr *addr);
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/**
|
||||
* Checks if the next FD in the sd_fd_index:sd_fd_max is of the
|
||||
* expected family, protocol and if it's listening.
|
||||
*
|
||||
* This is similar to sd_is_socket()/sd_is_socket_inet(), but will
|
||||
* also parse address in our standard format "IP:PORT", including IPv6
|
||||
* within braces, and then will validate the address with
|
||||
* getsockaddr() for INET.
|
||||
*
|
||||
* @param address the address to validate
|
||||
* @param family AF_UNIX or AF_UNSPEC for INET, in that case AF_INET
|
||||
* or AF_INET6 will be inferred from @a address.
|
||||
* @param type SOCK_STREAM or SOCK_DGRAM
|
||||
* @param[out] listening where to return listening state, should be
|
||||
* NULL for @a type SOCK_DGRAM
|
||||
*
|
||||
* @return 0 on success, error otherwise.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
Eina_Error efl_net_ip_socket_activate_check(const char *address, int family, int type, Eina_Bool *listening);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief splits an address in the format "host:port" in two
|
||||
* null-terminated strings.
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
# include <Evil.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
# include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
#define MY_CLASS EFL_NET_SERVER_FD_CLASS
|
||||
|
||||
typedef struct _Efl_Net_Server_Fd_Data
|
||||
|
@ -196,6 +200,58 @@ _efl_net_server_fd_efl_net_server_serving_get(Eo *o EINA_UNUSED, Efl_Net_Server_
|
|||
return pd->serving;
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_fd_socket_activate(Eo *o, Efl_Net_Server_Fd_Data *pd EINA_UNUSED, const char *address)
|
||||
{
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
||||
|
||||
#ifndef HAVE_SYSTEMD
|
||||
DBG("systemd support is disabled");
|
||||
return ENOENT;
|
||||
#else
|
||||
if (!sd_fd_max)
|
||||
{
|
||||
DBG("This service was not socket-activated, no $LISTEN_FDS");
|
||||
return ENOENT;
|
||||
}
|
||||
else if (sd_fd_index >= sd_fd_max)
|
||||
{
|
||||
WRN("No more systemd sockets available. Configuration mismatch?");
|
||||
return ENOENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
SOCKET fd = SD_LISTEN_FDS_START + sd_fd_index;
|
||||
int family;
|
||||
socklen_t len = sizeof(family);
|
||||
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &family, &len) != 0)
|
||||
{
|
||||
WRN("socket %d failed to return family: %s", fd, eina_error_msg_get(efl_net_socket_error_get()));
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
sd_fd_index++;
|
||||
efl_net_server_fd_family_set(o, family);
|
||||
efl_loop_fd_set(o, fd);
|
||||
if (efl_loop_fd_get(o) == INVALID_SOCKET)
|
||||
{
|
||||
sd_fd_index--;
|
||||
WRN("socket %d could not be used by %p (%s)",
|
||||
fd, o, efl_class_name_get(efl_class_get(o)));
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* by default they all come with close_on_exec set
|
||||
* and we must apply our local conf.
|
||||
*/
|
||||
efl_net_server_fd_close_on_exec_set(o, pd->close_on_exec);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Bool
|
||||
_efl_net_server_fd_close_on_exec_set(Eo *o, Efl_Net_Server_Fd_Data *pd, Eina_Bool close_on_exec)
|
||||
{
|
||||
|
|
|
@ -5,6 +5,53 @@ class Efl.Net.Server.Fd (Efl.Loop.Fd, Efl.Net.Server) {
|
|||
]]
|
||||
|
||||
methods {
|
||||
socket_activate {
|
||||
[[If this method is called use an already activated socket.
|
||||
|
||||
This method allows a server to use an existing socket
|
||||
received from systemd or similar system.
|
||||
|
||||
It will replace @Efl.Net.Server.serve, thus if this is
|
||||
used, that method will return EALREADY.
|
||||
|
||||
\@note The parameter 'address' given to this function is
|
||||
only used to validate the next socket available, it
|
||||
doesn't search for a socket with the given address. Thus
|
||||
the socket to be used is the next unused and orders
|
||||
matter is using multiple servers!
|
||||
|
||||
\@note subclasses must validate the socket and return
|
||||
EINVAL prior to call the base class with
|
||||
Efl.Object.super. They must also emit "serving" when
|
||||
ready, for instance stream protocols may need to check
|
||||
for listening and if not try to listen. Usually they
|
||||
will also query getsockname() and set
|
||||
@Efl.Net.Server.address.
|
||||
|
||||
Errors:
|
||||
|
||||
- EALREADY: already have a socket, either from
|
||||
previous @.socket_activate or
|
||||
@Efl.Net.Server.serve. Usually represents a
|
||||
programming error.
|
||||
|
||||
- ENOENT: no sockets received from process manager
|
||||
(ie: systemd). Usually this is not a fatal error,
|
||||
just proceed by calling @Efl.Net.Server.serve
|
||||
|
||||
- EINVAL: the socket received is not of the correct
|
||||
family, type or protocol. Usually this means a
|
||||
configuration mismatch with the order of server
|
||||
creation and calls to socket_activate. The
|
||||
systemd.socket entries must match the order in your
|
||||
application.
|
||||
]]
|
||||
params {
|
||||
address: string; [[The address to validate the next available socket. It doesn't serve as search, only as validation!]]
|
||||
}
|
||||
return: Eina.Error; [[0 on success, ENOENT if no socket is available or EALREADY if already have a socket]]
|
||||
}
|
||||
|
||||
@property family {
|
||||
[[The address family (AF_*) family of this socket.
|
||||
|
||||
|
|
|
@ -117,6 +117,14 @@ _efl_net_server_ssl_ssl_context_get(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *
|
|||
return pd->ssl_ctx;
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_ssl_socket_activate(Eo *o, Efl_Net_Server_Ssl_Data *pd, const char *address)
|
||||
{
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_server_serving_get(o), EALREADY);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
||||
return efl_net_server_fd_socket_activate(pd->server, address);
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_ssl_efl_net_server_serve(Eo *o EINA_UNUSED, Efl_Net_Server_Ssl_Data *pd, const char *address)
|
||||
{
|
||||
|
|
|
@ -18,14 +18,49 @@ class Efl.Net.Server.Ssl (Efl.Loop_User, Efl.Net.Server) {
|
|||
}
|
||||
}
|
||||
|
||||
socket_activate {
|
||||
[[If this method is called use an already activated socket.
|
||||
|
||||
This method allows a server to use an existing socket
|
||||
received from systemd or similar system.
|
||||
|
||||
It will replace @Efl.Net.Server.serve, thus if this is
|
||||
used, that method will return EALREADY.
|
||||
|
||||
\@note The parameter 'address' given to this function is
|
||||
only used to validate the next socket available, it
|
||||
doesn't search for a socket with the given address. Thus
|
||||
the socket to be used is the next unused and orders
|
||||
matter is using multiple servers!
|
||||
|
||||
Errors:
|
||||
|
||||
- EALREADY: already have a socket, either from
|
||||
previous @.socket_activate or
|
||||
@Efl.Net.Server.serve. Usually represents a
|
||||
programming error.
|
||||
|
||||
- ENOENT: no sockets received from process manager
|
||||
(ie: systemd). Usually this is not a fatal error,
|
||||
just proceed by calling @Efl.Net.Server.serve
|
||||
|
||||
- EINVAL: the socket received is not of the correct
|
||||
family, type or protocol. Usually this means a
|
||||
configuration mismatch with the order of server
|
||||
creation and calls to socket_activate. The
|
||||
systemd.socket entries must match the order in your
|
||||
application.
|
||||
]]
|
||||
params {
|
||||
address: string; [[The address to validate the next available socket. It doesn't serve as search, only as validation!]]
|
||||
}
|
||||
return: Eina.Error; [[0 on success, ENOENT if no socket is available or EALREADY if already have a socket]]
|
||||
}
|
||||
|
||||
@property family {
|
||||
[[The address family (AF_*) family of this socket.
|
||||
|
||||
It will be one of AF_INET (IPv4), AF_INET6 (IPv6),
|
||||
AF_UNIX...
|
||||
|
||||
It must be set before the @Efl.Loop.Fd.fd.set is called
|
||||
with a valid file descriptor.
|
||||
It will be one of AF_INET (IPv4) or AF_INET6 (IPv6).
|
||||
]]
|
||||
get { }
|
||||
values {
|
||||
|
|
|
@ -154,6 +154,64 @@ _efl_net_server_tcp_resolved(void *data, const char *host EINA_UNUSED, const cha
|
|||
efl_unref(o);
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_tcp_efl_net_server_fd_socket_activate(Eo *o, Efl_Net_Server_Tcp_Data *pd EINA_UNUSED, const char *address)
|
||||
{
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
||||
|
||||
#ifndef HAVE_SYSTEMD
|
||||
return efl_net_server_fd_socket_activate(efl_super(o, MY_CLASS), address);
|
||||
#else
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
|
||||
Eina_Bool listening;
|
||||
Eina_Error err;
|
||||
struct sockaddr_storage *addr;
|
||||
socklen_t addrlen;
|
||||
int fd;
|
||||
|
||||
err = efl_net_ip_socket_activate_check(address, AF_UNSPEC, SOCK_STREAM, &listening);
|
||||
if (err) return err;
|
||||
|
||||
err = efl_net_server_fd_socket_activate(efl_super(o, MY_CLASS), address);
|
||||
if (err) return err;
|
||||
|
||||
fd = efl_loop_fd_get(o);
|
||||
|
||||
if (!listening)
|
||||
{
|
||||
if (listen(fd, 0) != 0)
|
||||
{
|
||||
err = efl_net_socket_error_get();
|
||||
DBG("listen(%d): %s", fd, eina_error_msg_get(err));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
||||
{
|
||||
err = efl_net_socket_error_get();
|
||||
ERR("getsockname(%d): %s", fd, eina_error_msg_get(err));
|
||||
goto error;
|
||||
}
|
||||
else if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
|
||||
efl_net_server_address_set(o, buf);
|
||||
|
||||
DBG("fd=%d serving at %s", fd, address);
|
||||
efl_net_server_serving_set(o, EINA_TRUE);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
efl_net_server_fd_family_set(o, AF_UNSPEC);
|
||||
efl_loop_fd_set(o, INVALID_SOCKET);
|
||||
closesocket(fd);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_tcp_efl_net_server_serve(Eo *o, Efl_Net_Server_Tcp_Data *pd, const char *address)
|
||||
{
|
||||
|
|
|
@ -39,5 +39,6 @@ class Efl.Net.Server.Tcp (Efl.Net.Server.Fd) {
|
|||
Efl.Net.Server.serve;
|
||||
Efl.Net.Server.Fd.client_add;
|
||||
Efl.Net.Server.Fd.client_reject;
|
||||
Efl.Net.Server.Fd.socket_activate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,6 +201,53 @@ _efl_net_server_udp_resolved(void *data, const char *host EINA_UNUSED, const cha
|
|||
efl_unref(o);
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_udp_efl_net_server_fd_socket_activate(Eo *o, Efl_Net_Server_Udp_Data *pd EINA_UNUSED, const char *address)
|
||||
{
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
||||
|
||||
#ifndef HAVE_SYSTEMD
|
||||
return efl_net_server_fd_socket_activate(efl_super(o, MY_CLASS), address);
|
||||
#else
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
|
||||
Eina_Error err;
|
||||
struct sockaddr_storage *addr;
|
||||
socklen_t addrlen;
|
||||
int fd;
|
||||
|
||||
err = efl_net_ip_socket_activate_check(address, AF_UNSPEC, SOCK_DGRAM, NULL);
|
||||
if (err) return err;
|
||||
|
||||
err = efl_net_server_fd_socket_activate(efl_super(o, MY_CLASS), address);
|
||||
if (err) return err;
|
||||
|
||||
fd = efl_loop_fd_get(o);
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
||||
{
|
||||
err = efl_net_socket_error_get();
|
||||
ERR("getsockname(%d): %s", fd, eina_error_msg_get(err));
|
||||
goto error;
|
||||
}
|
||||
else if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
|
||||
efl_net_server_address_set(o, buf);
|
||||
|
||||
DBG("fd=%d serving at %s", fd, address);
|
||||
efl_net_server_serving_set(o, EINA_TRUE);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
efl_net_server_fd_family_set(o, AF_UNSPEC);
|
||||
efl_loop_fd_set(o, INVALID_SOCKET);
|
||||
closesocket(fd);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_udp_efl_net_server_serve(Eo *o, Efl_Net_Server_Udp_Data *pd, const char *address)
|
||||
{
|
||||
|
|
|
@ -125,5 +125,6 @@ class Efl.Net.Server.Udp (Efl.Net.Server.Fd) {
|
|||
Efl.Object.destructor;
|
||||
Efl.Net.Server.serve;
|
||||
Efl.Net.Server.Fd.process_incoming_data;
|
||||
Efl.Net.Server.Fd.socket_activate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,6 +162,64 @@ _efl_net_server_unix_bind_job(void *data, const Efl_Event *event EINA_UNUSED)
|
|||
efl_unref(o);
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_unix_efl_net_server_fd_socket_activate(Eo *o, Efl_Net_Server_Unix_Data *pd EINA_UNUSED, const char *address)
|
||||
{
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) != INVALID_SOCKET, EALREADY);
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
|
||||
|
||||
#ifndef HAVE_SYSTEMD
|
||||
return efl_net_server_fd_socket_activate(efl_super(o, MY_CLASS), address);
|
||||
#else
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
|
||||
Eina_Bool listening;
|
||||
Eina_Error err;
|
||||
struct sockaddr_storage *addr;
|
||||
socklen_t addrlen;
|
||||
int fd;
|
||||
|
||||
err = efl_net_ip_socket_activate_check(address, AF_UNIX, SOCK_STREAM, &listening);
|
||||
if (err) return err;
|
||||
|
||||
err = efl_net_server_fd_socket_activate(efl_super(o, MY_CLASS), address);
|
||||
if (err) return err;
|
||||
|
||||
fd = efl_loop_fd_get(o);
|
||||
|
||||
if (!listening)
|
||||
{
|
||||
if (listen(fd, 0) != 0)
|
||||
{
|
||||
err = efl_net_socket_error_get();
|
||||
DBG("listen(%d): %s", fd, eina_error_msg_get(err));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0)
|
||||
{
|
||||
err = efl_net_socket_error_get();
|
||||
ERR("getsockname(%d): %s", fd, eina_error_msg_get(err));
|
||||
goto error;
|
||||
}
|
||||
else if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
|
||||
efl_net_server_address_set(o, buf);
|
||||
|
||||
DBG("fd=%d serving at %s", fd, address);
|
||||
efl_net_server_serving_set(o, EINA_TRUE);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
efl_net_server_fd_family_set(o, AF_UNSPEC);
|
||||
efl_loop_fd_set(o, INVALID_SOCKET);
|
||||
closesocket(fd);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
EOLIAN static Eina_Error
|
||||
_efl_net_server_unix_efl_net_server_serve(Eo *o, Efl_Net_Server_Unix_Data *pd, const char *address)
|
||||
{
|
||||
|
|
|
@ -24,5 +24,6 @@ class Efl.Net.Server.Unix (Efl.Net.Server.Fd) {
|
|||
Efl.Net.Server.serve;
|
||||
Efl.Net.Server.Fd.client_add;
|
||||
Efl.Net.Server.Fd.client_reject;
|
||||
Efl.Net.Server.Fd.socket_activate;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue