>From 66dd9a0d5185f93914f1cc7a347dfe225ee4cde8 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Wed, 7 Oct 2009 09:45:39 -0400
Subject: [PATCH] ELAPI Compatibility code for getifaddr()

Addreses ticket #94
Actually works pretty well.
To try use --enable-compat when build ELAPI.
It will use compatibility code instead of
getifaddr().
The trick in the elapi_ioctl.h with memory
allocation is taken from Stevens book.
---
 common/elapi/Makefile.am            |    3 +
 common/elapi/conf_macros.m4         |   18 +++++
 common/elapi/configure.ac           |    3 +
 common/elapi/elapi_event.c          |   35 +++++----
 common/elapi/elapi_ioctl.c          |  142 +++++++++++++++++++++++++++++++++++
 common/elapi/elapi_ioctl.h          |   42 ++++++++++
 common/elapi/elapi_net.h            |  103 +++++++++++++++++++++++++
 common/elapi/elapi_test/Makefile.am |    3 +
 8 files changed, 333 insertions(+), 16 deletions(-)
 create mode 100644 common/elapi/elapi_ioctl.c
 create mode 100644 common/elapi/elapi_ioctl.h
 create mode 100644 common/elapi/elapi_net.h

diff --git a/common/elapi/Makefile.am b/common/elapi/Makefile.am
index 53f2c3e..fa27985 100644
--- a/common/elapi/Makefile.am
+++ b/common/elapi/Makefile.am
@@ -55,11 +55,14 @@ libelapi_la_SOURCES = \
     elapi_resolve.c \
     elapi_async.c \
     elapi_subst.c \
+    elapi_ioctl.c \
     elapi_event.h \
     elapi_priv.h \
     elapi_sink.h \
     elapi_log.h \
     elapi_async.h \
+    elapi_net.h \
+    elapi_ioctl.h \
     elapi_fd.h \
     elapi_tm.h \
     elapi.h
diff --git a/common/elapi/conf_macros.m4 b/common/elapi/conf_macros.m4
index e20dfdb..2cefb1d 100644
--- a/common/elapi/conf_macros.m4
+++ b/common/elapi/conf_macros.m4
@@ -54,3 +54,21 @@ AC_DEFUN([WITH_APP_NAME_SIZE],
     fi
     AC_SUBST(appnamesize)
   ])
+
+AC_DEFUN([WITH_COMPAT],
+  [ AC_ARG_ENABLE([compat],
+                  [AC_HELP_STRING([--enable-compat],
+                                  [use platform compatibility code])
+                  ],
+                  [AC_DEFINE([HAVE_GETIFADDRS], [0],
+                             [Force ELAPI to use compatibility code for getifaddrs()]
+                            )
+                  ],
+                  [AC_CHECK_FUNC([getifaddrs],
+                                 AC_DEFINE([HAVE_GETIFADDRS], [1],
+                                           [Define if platform has getifaddrs()])
+                                 )
+                  ]
+                 )
+
+  ])
diff --git a/common/elapi/configure.ac b/common/elapi/configure.ac
index ad2ffce..82be136 100644
--- a/common/elapi/configure.ac
+++ b/common/elapi/configure.ac
@@ -14,6 +14,7 @@ AM_SILENT_RULES
 
 AC_CONFIG_HEADERS([config.h])
 
+
 # Enable trace build
 AC_ARG_ENABLE([trace],
               [AS_HELP_STRING([--enable-trace[=LEVEL]],[build with low level tracing enabled])],
@@ -21,12 +22,14 @@ AC_ARG_ENABLE([trace],
               [trace_level="0"])
 AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] ],[AC_SUBST([TRACE_VAR],["-DTRACE_LEVEL=$trace_level"])])
 
+
 m4_include(conf_macros.m4)
 
 WITH_CONFIG_DIR
 WITH_CONFIG_APP_DIR
 WITH_APP_NAME
 WITH_APP_NAME_SIZE
+WITH_COMPAT
 
 m4_include(def_macros.m4)
 
diff --git a/common/elapi/elapi_event.c b/common/elapi/elapi_event.c
index e7a5d77..ff73939 100644
--- a/common/elapi/elapi_event.c
+++ b/common/elapi/elapi_event.c
@@ -31,10 +31,10 @@
 #include <ctype.h>      /* for isspace() */
 #include <stdarg.h>     /* for va_arg() */
 #include <string.h>     /* for strndup() */
-#include <ifaddrs.h>    /* for getifaddrs() */
 
 #include "elapi_priv.h"
 #include "elapi_event.h"
+#include "elapi_net.h"
 #include "trace.h"
 #include "config.h"
 
@@ -69,7 +69,9 @@ static int add_host_identity(struct collection_item *tpl, unsigned base)
     char host[NI_MAXHOST];
     char address[NI_MAXHOST];
     char *hnm, *haddr;
-    struct ifaddrs *ifaddr, *ifa;
+    ELAPI_ADDRLIST ifaddr;
+    ELAPI_ADDRITEM ifa;
+    struct sockaddr *addr;
     int family;
     int set_hostname = 0;
     int set_ip = 0;
@@ -88,26 +90,27 @@ static int add_host_identity(struct collection_item *tpl, unsigned base)
     /* If we are not asked for ip then say we already have it */
     if (!(base & E_HAVE_HOSTIP)) set_ip = 1;
 
-    if (getifaddrs(&ifaddr) == EOK) {
+    if (ELAPI_GET_ADDRLIST(&ifaddr) == EOK) {
 
         TRACE_FLOW_STRING("getifaddrs", "Ok");
 
         /* Walk through linked list, maintaining head pointer so we
             can free list later */
+        ELAPI_GET_FIRST_ADDR_ITEM(ifaddr, ifa);
 
-        ifa = ifaddr;
         while (ifa != NULL) {
 
             TRACE_FLOW_STRING("Top of the loop", "");
 
             used_this_ip = 0;
 
-            if (!ifa->ifa_addr) {
-                ifa = ifa->ifa_next;
+            ELAPI_GET_ADDR(ifa, addr);
+            if (!addr) {
+                ELAPI_GET_NEXT_ADDR_ITEM(ifaddr, ifa);
                 continue;
             }
 
-            family = ifa->ifa_addr->sa_family;
+            family = addr->sa_family;
 
             TRACE_FLOW_NUMBER("Family", family);
 
@@ -121,7 +124,7 @@ static int add_host_identity(struct collection_item *tpl, unsigned base)
                  * We will trust it here and not clear memory using memset.
                  */
 
-                gai_ret_host = getnameinfo(ifa->ifa_addr,
+                gai_ret_host = getnameinfo(addr,
                                            (family == AF_INET) ? sizeof(struct sockaddr_in) :
                                                                  sizeof(struct sockaddr_in6),
                                            host,
@@ -130,7 +133,7 @@ static int add_host_identity(struct collection_item *tpl, unsigned base)
                                            0,
                                            0 /* Gets host name */);
 
-                gai_ret_addr = getnameinfo(ifa->ifa_addr,
+                gai_ret_addr = getnameinfo(addr,
                                            (family == AF_INET) ? sizeof(struct sockaddr_in) :
                                                                  sizeof(struct sockaddr_in6),
                                            address,
@@ -173,7 +176,7 @@ static int add_host_identity(struct collection_item *tpl, unsigned base)
                         error = col_add_str_property(tpl, NULL, E_HOSTNAME, hnm, 0);
                         if (error) {
                             TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
-                            freeifaddrs(ifaddr);
+                            ELAPI_ADDR_LIST_CLEANUP(ifaddr);
                             return error;
                         }
                         /* Done with the name */
@@ -201,7 +204,7 @@ static int add_host_identity(struct collection_item *tpl, unsigned base)
                         error = col_add_str_property(tpl, NULL, E_HOSTIP, haddr, 0);
                         if (error) {
                             TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
-                            freeifaddrs(ifaddr);
+                            ELAPI_ADDR_LIST_CLEANUP(ifaddr);
                             return error;
                         }
                         set_ip = 1;
@@ -230,7 +233,7 @@ static int add_host_identity(struct collection_item *tpl, unsigned base)
                         error = col_add_str_property(tpl, NULL, E_HOSTALIAS, hnm, 0);
                         if (error) {
                             TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
-                            freeifaddrs(ifaddr);
+                            ELAPI_ADDR_LIST_CLEANUP(ifaddr);
                             return error;
                         }
                     }
@@ -253,18 +256,18 @@ static int add_host_identity(struct collection_item *tpl, unsigned base)
                         error = col_add_str_property(tpl, NULL, E_HOSTIPS, haddr, 0);
                         if (error) {
                             TRACE_ERROR_NUMBER("Failed to add host name. Error", error);
-                            freeifaddrs(ifaddr);
+                            ELAPI_ADDR_LIST_CLEANUP(ifaddr);
                             return error;
                         }
                     }
                 }
             }
-            TRACE_INFO_NUMBER("Moving to next", ifa->ifa_next);
-            ifa = ifa->ifa_next;
+            TRACE_INFO_STRING("Moved to next", "");
+            ELAPI_GET_NEXT_ADDR_ITEM(ifaddr, ifa);
             TRACE_INFO_NUMBER("Moved to", ifa);
         }
 
-        freeifaddrs(ifaddr);
+        ELAPI_ADDR_LIST_CLEANUP(ifaddr);
     }
 
     /* Make sure that we really have the name after all */
diff --git a/common/elapi/elapi_ioctl.c b/common/elapi/elapi_ioctl.c
new file mode 100644
index 0000000..c2a42a1
--- /dev/null
+++ b/common/elapi/elapi_ioctl.c
@@ -0,0 +1,142 @@
+/*
+    ELAPI
+
+    Special platform specific functions related to networking
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "elapi_ioctl.h"
+#include "trace.h"
+#include "config.h"
+
+#ifndef HAVE_GETIFADDRS
+
+/* These functions are taken form Stevens's
+ * UNIX Network Programming Volume 1
+ */
+
+int elapi_get_addrlist(struct ifconf *ifc)
+{
+    int sockfd;
+    int length, lastlen;
+    int error;
+    char *buffer;
+
+    TRACE_FLOW_STRING("elapi_get_addrlist", "Entry");
+
+    /* Open socket */
+    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+
+    lastlen = 0;
+    length = INTERFACE_NUM_GUESS * sizeof(struct ifreq);
+
+    /* Allocate right amount of memory */
+    /* This is a trick from Stevens book */
+    /* Search web for "get_ifi_info" to get original code */
+    for ( ; ; ) {
+
+        buffer = malloc(length);
+        ifc->ifc_len = length;
+        ifc->ifc_buf = buffer;
+
+        /* Read list */
+        if (ioctl(sockfd, SIOCGIFCONF, ifc) < 0) {
+            error = errno;
+            TRACE_INFO_NUMBER("Ioctl call failed", error);
+            if (error != EINVAL || lastlen != 0) {
+                free(buffer);
+                TRACE_ERROR_NUMBER("ioctl failed", error);
+                return error;
+            }
+        } else {
+            TRACE_INFO_NUMBER("Length returned", ifc->ifc_len);
+            TRACE_INFO_NUMBER("Previous length", lastlen);
+            /* Break if length is same */
+            if (ifc->ifc_len == lastlen) break;
+            lastlen = ifc->ifc_len;
+        }
+
+        /* Grow length */
+        length += INTERFACE_NUM_INC * sizeof(struct ifreq);
+        free(buffer);
+    }
+
+    TRACE_FLOW_STRING("elapi_get_addrlist", "Exit");
+    return EOK;
+}
+
+/* Get the variable part of the size of the address */
+static int elapi_get_addrlen(struct ifreq *ifr)
+{
+    int len;
+
+    TRACE_FLOW_STRING("elapi_get_addrlen", "Entry");
+
+#ifdef HAVE_SOCKADDR_SA_LEN
+    len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
+#else
+    switch (ifr->ifr_addr.sa_family) {
+#ifdef IPV6
+    case AF_INET6:
+        len = sizeof(struct sockaddr_in6);
+        break;
+#endif
+    case AF_INET:
+        default:
+        len = sizeof(struct sockaddr);
+        break;
+    }
+#endif    /* HAVE_SOCKADDR_SA_LEN */
+
+    TRACE_FLOW_NUMBER("elapi_get_addrlen Returning", len);
+    return len;
+}
+
+/* Get next address */
+struct ifreq *elapi_get_next_addr(struct ifconf *ifc, struct ifreq *current)
+{
+    char *ifr;
+
+    TRACE_FLOW_STRING("elapi_get_next_addr", "Entry");
+
+    TRACE_INFO_NUMBER("Current ifi", current);
+    TRACE_INFO_NUMBER("Address", &current->ifr_addr);
+
+    /* Move to the next item */
+    ifr = (char *)current + sizeof(current->ifr_name) + elapi_get_addrlen(current);
+
+    TRACE_INFO_NUMBER("New ifi", ifr);
+
+    /* Check if we are beyond the end of the allocated area */
+    /* Have to cast otherwise get warnings */
+    if (ifr >= ((char *)ifc->ifc_buf + ifc->ifc_len)) ifr = NULL;
+
+    TRACE_INFO_NUMBER("New ifi adjusted", ifr);
+
+    TRACE_FLOW_STRING("elapi_get_next_addr", "Exit");
+
+    return (struct ifreq *)ifr;
+}
+
+
+#endif /* HAVE_GETIFADDRS */
diff --git a/common/elapi/elapi_ioctl.h b/common/elapi/elapi_ioctl.h
new file mode 100644
index 0000000..6baa4bf
--- /dev/null
+++ b/common/elapi/elapi_ioctl.h
@@ -0,0 +1,42 @@
+/*
+    ELAPI
+
+    Header file for the ELAPI handling of network interfaces.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ELAPI_IOCTL_H
+#define ELAPI_IOCTL_H
+
+#include "config.h"
+
+#ifndef HAVE_GETIFADDRS
+
+#ifndef EOK
+#define EOK 0
+#endif
+
+#include <net/if.h>
+
+/* Function prototypes */
+int elapi_get_addrlist(struct ifconf *ifc);
+struct ifreq *elapi_get_next_addr(struct ifconf *ifc, struct ifreq *current);
+
+#define INTERFACE_NUM_GUESS 3
+#define INTERFACE_NUM_INC 1
+
+#endif /* HAVE_GETIFADDRS */
+
+#endif
diff --git a/common/elapi/elapi_net.h b/common/elapi/elapi_net.h
new file mode 100644
index 0000000..c9f8af7
--- /dev/null
+++ b/common/elapi/elapi_net.h
@@ -0,0 +1,103 @@
+/*
+    ELAPI
+
+    Header file for the ELAPI handling of netwok interfaces.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ELAPI_NET_H
+#define ELAPI_NET_H
+
+#include "config.h"
+
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>    /* for getifaddrs() */
+
+/* Macros then just wrap the getifaddrs() interface */
+
+/* Type of the variable that stores the list */
+#define ELAPI_ADDRLIST struct ifaddrs *
+
+/* Type of the variable that stores the item */
+#define ELAPI_ADDRITEM struct ifaddrs *
+
+/* Function to build list of the interfaces */
+#define  ELAPI_GET_ADDRLIST getifaddrs
+
+/* Macro to get first item from the list */
+#define ELAPI_GET_FIRST_ADDR_ITEM(list, item) \
+    do { \
+        item = list; \
+    } while(0)
+
+/* Macro to get next item from the list */
+#define ELAPI_GET_NEXT_ADDR_ITEM(list, item) \
+    do { \
+        item = item->ifa_next; \
+    } while(0)
+
+
+/* Macro to get address */
+#define ELAPI_GET_ADDR(item, addr) \
+    do { \
+        addr = item->ifa_addr; \
+    } while(0)
+
+/* Function to free the list */
+#define ELAPI_ADDR_LIST_CLEANUP freeifaddrs
+
+#else
+/* Do everything using ioctl yourself... */
+
+#include "elapi_ioctl.h"
+
+/* Type of the variable that stores the list */
+#define ELAPI_ADDRLIST struct ifconf
+
+/* Type of valiable that is used as a pointer */
+#define ELAPI_ADDRITEM struct ifreq *
+
+/* Function to build list of the interfaces */
+#define  ELAPI_GET_ADDRLIST elapi_get_addrlist
+
+/* Macro to get first item from the list */
+#define ELAPI_GET_FIRST_ADDR_ITEM(list, item) \
+    do { \
+        item = (struct ifreq *)list.ifc_buf; \
+    } while(0)
+
+/* Macro to get next item from the list */
+#define ELAPI_GET_NEXT_ADDR_ITEM(list, item) \
+    do { \
+        item = elapi_get_next_addr(&list, item); \
+    } while(0)
+
+
+/* Macro to get address */
+#define ELAPI_GET_ADDR(item, addr) \
+    do { \
+        addr = &(item->ifr_addr); \
+    } while(0)
+
+/* Function to free the list */
+#define ELAPI_ADDR_LIST_CLEANUP(list) \
+    do { \
+        free(list.ifc_buf); \
+    } while(0)
+
+
+#endif /* HAVE_GETIFADDRS */
+
+#endif
diff --git a/common/elapi/elapi_test/Makefile.am b/common/elapi/elapi_test/Makefile.am
index c0eac8a..9ecd556 100644
--- a/common/elapi/elapi_test/Makefile.am
+++ b/common/elapi/elapi_test/Makefile.am
@@ -33,11 +33,14 @@ libelapi_test_la_SOURCES = \
     ../elapi_resolve.c \
     ../elapi_async.c \
     ../elapi_subst.c \
+    ../elapi_ioctl.c \
     ../elapi_event.h \
     ../elapi_priv.h \
     ../elapi_sink.h \
     ../elapi_log.h \
     ../elapi_async.h \
+    ../elapi_ioctl.h \
+    ../elapi_net.h \
     ../elapi_fd.h \
     ../elapi_tm.h \
     ../elapi.h \
-- 
1.5.5.6

