diff --git a/src/examples/ecore/efl_net_server_example.c b/src/examples/ecore/efl_net_server_example.c index 6f760f0933..c8fc088499 100644 --- a/src/examples/ecore/efl_net_server_example.c +++ b/src/examples/ecore/efl_net_server_example.c @@ -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(); diff --git a/src/lib/ecore_con/ecore_con.c b/src/lib/ecore_con/ecore_con.c index be0c1a8d4a..56a5f899fd 100644 --- a/src/lib/ecore_con/ecore_con.c +++ b/src/lib/ecore_con/ecore_con.c @@ -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) { diff --git a/src/lib/ecore_con/ecore_con_private.h b/src/lib/ecore_con/ecore_con_private.h index ec2fc21310..f222656ee2 100644 --- a/src/lib/ecore_con/ecore_con_private.h +++ b/src/lib/ecore_con/ecore_con_private.h @@ -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. diff --git a/src/lib/ecore_con/efl_net_server_fd.c b/src/lib/ecore_con/efl_net_server_fd.c index 704ccd8cef..bb3c7cc8b5 100644 --- a/src/lib/ecore_con/efl_net_server_fd.c +++ b/src/lib/ecore_con/efl_net_server_fd.c @@ -17,6 +17,10 @@ # include #endif +#ifdef HAVE_SYSTEMD +# include +#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) { diff --git a/src/lib/ecore_con/efl_net_server_fd.eo b/src/lib/ecore_con/efl_net_server_fd.eo index df86c54e7f..0e806b9cd8 100644 --- a/src/lib/ecore_con/efl_net_server_fd.eo +++ b/src/lib/ecore_con/efl_net_server_fd.eo @@ -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. diff --git a/src/lib/ecore_con/efl_net_server_ssl.c b/src/lib/ecore_con/efl_net_server_ssl.c index e07ea5918b..4fa44eb736 100644 --- a/src/lib/ecore_con/efl_net_server_ssl.c +++ b/src/lib/ecore_con/efl_net_server_ssl.c @@ -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) { diff --git a/src/lib/ecore_con/efl_net_server_ssl.eo b/src/lib/ecore_con/efl_net_server_ssl.eo index 000a86ec53..9d25a302a7 100644 --- a/src/lib/ecore_con/efl_net_server_ssl.eo +++ b/src/lib/ecore_con/efl_net_server_ssl.eo @@ -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 { diff --git a/src/lib/ecore_con/efl_net_server_tcp.c b/src/lib/ecore_con/efl_net_server_tcp.c index c979c2f5e2..4d215d5e0e 100644 --- a/src/lib/ecore_con/efl_net_server_tcp.c +++ b/src/lib/ecore_con/efl_net_server_tcp.c @@ -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) { diff --git a/src/lib/ecore_con/efl_net_server_tcp.eo b/src/lib/ecore_con/efl_net_server_tcp.eo index 5b60a911dd..83948353d0 100644 --- a/src/lib/ecore_con/efl_net_server_tcp.eo +++ b/src/lib/ecore_con/efl_net_server_tcp.eo @@ -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; } } diff --git a/src/lib/ecore_con/efl_net_server_udp.c b/src/lib/ecore_con/efl_net_server_udp.c index 3e0e951c95..051db7fab9 100644 --- a/src/lib/ecore_con/efl_net_server_udp.c +++ b/src/lib/ecore_con/efl_net_server_udp.c @@ -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) { diff --git a/src/lib/ecore_con/efl_net_server_udp.eo b/src/lib/ecore_con/efl_net_server_udp.eo index e3b0df28ff..660f1d2156 100644 --- a/src/lib/ecore_con/efl_net_server_udp.eo +++ b/src/lib/ecore_con/efl_net_server_udp.eo @@ -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; } } diff --git a/src/lib/ecore_con/efl_net_server_unix.c b/src/lib/ecore_con/efl_net_server_unix.c index 845df44259..29d83168d7 100644 --- a/src/lib/ecore_con/efl_net_server_unix.c +++ b/src/lib/ecore_con/efl_net_server_unix.c @@ -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) { diff --git a/src/lib/ecore_con/efl_net_server_unix.eo b/src/lib/ecore_con/efl_net_server_unix.eo index c068a1d8ac..9f69758c13 100644 --- a/src/lib/ecore_con/efl_net_server_unix.eo +++ b/src/lib/ecore_con/efl_net_server_unix.eo @@ -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; } }