barbieri pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=c2630c829f45df5973582a4259d2a0b127cc7cae

commit c2630c829f45df5973582a4259d2a0b127cc7cae
Author: Gustavo Sverzut Barbieri <[email protected]>
Date:   Tue Nov 1 16:01:57 2016 -0200

    efl_net_server support systemd socket activation.
    
    It includes extensive verifications to avoid mistakes and usage of
    incorrect sockets.
---
 src/examples/ecore/efl_net_server_example.c |  25 +++-
 src/lib/ecore_con/ecore_con.c               | 214 ++++++++++++++++++++++++++++
 src/lib/ecore_con/ecore_con_private.h       |  24 ++++
 src/lib/ecore_con/efl_net_server_fd.c       |  56 ++++++++
 src/lib/ecore_con/efl_net_server_fd.eo      |  47 ++++++
 src/lib/ecore_con/efl_net_server_ssl.c      |   8 ++
 src/lib/ecore_con/efl_net_server_ssl.eo     |  45 +++++-
 src/lib/ecore_con/efl_net_server_tcp.c      |  58 ++++++++
 src/lib/ecore_con/efl_net_server_tcp.eo     |   1 +
 src/lib/ecore_con/efl_net_server_udp.c      |  47 ++++++
 src/lib/ecore_con/efl_net_server_udp.eo     |   1 +
 src/lib/ecore_con/efl_net_server_unix.c     |  58 ++++++++
 src/lib/ecore_con/efl_net_server_unix.eo    |   1 +
 13 files changed, 575 insertions(+), 10 deletions(-)

diff --git a/src/examples/ecore/efl_net_server_example.c 
b/src/examples/ecore/efl_net_server_example.c
index 6f760f0..c8fc088 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 be0c1a8..56a5f89 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 ec2fc21..f222656 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 704ccd8..bb3c7cc 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 <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)
 {
diff --git a/src/lib/ecore_con/efl_net_server_fd.eo 
b/src/lib/ecore_con/efl_net_server_fd.eo
index df86c54..0e806b9 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 e07ea59..4fa44eb 100644
--- a/src/lib/ecore_con/efl_net_server_ssl.c
+++ b/src/lib/ecore_con/efl_net_server_ssl.c
@@ -118,6 +118,14 @@ _efl_net_server_ssl_ssl_context_get(Eo *o EINA_UNUSED, 
Efl_Net_Server_Ssl_Data *
 }
 
 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)
 {
    return efl_net_server_serve(pd->server, address);
diff --git a/src/lib/ecore_con/efl_net_server_ssl.eo 
b/src/lib/ecore_con/efl_net_server_ssl.eo
index 000a86e..9d25a30 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 c979c2f..4d215d5 100644
--- a/src/lib/ecore_con/efl_net_server_tcp.c
+++ b/src/lib/ecore_con/efl_net_server_tcp.c
@@ -155,6 +155,64 @@ _efl_net_server_tcp_resolved(void *data, const char *host 
EINA_UNUSED, const cha
 }
 
 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)
 {
    char *str;
diff --git a/src/lib/ecore_con/efl_net_server_tcp.eo 
b/src/lib/ecore_con/efl_net_server_tcp.eo
index 5b60a91..8394835 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 3e0e951..051db7f 100644
--- a/src/lib/ecore_con/efl_net_server_udp.c
+++ b/src/lib/ecore_con/efl_net_server_udp.c
@@ -202,6 +202,53 @@ _efl_net_server_udp_resolved(void *data, const char *host 
EINA_UNUSED, const cha
 }
 
 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)
 {
    char *str;
diff --git a/src/lib/ecore_con/efl_net_server_udp.eo 
b/src/lib/ecore_con/efl_net_server_udp.eo
index e3b0df2..660f1d2 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 845df44..29d8316 100644
--- a/src/lib/ecore_con/efl_net_server_unix.c
+++ b/src/lib/ecore_con/efl_net_server_unix.c
@@ -163,6 +163,64 @@ _efl_net_server_unix_bind_job(void *data, const Efl_Event 
*event EINA_UNUSED)
 }
 
 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)
 {
    EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
diff --git a/src/lib/ecore_con/efl_net_server_unix.eo 
b/src/lib/ecore_con/efl_net_server_unix.eo
index c068a1d..9f69758 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;
     }
 }

-- 


Reply via email to