os_getcwd(): do not realloc if there was no malloc().
[exim.git] / src / src / os.c
index 810baf6b63a6daa0c9db34c96ddfe943ac22b80b..ca24e8dd27e47d42df5140e55de39cd0a072c6d8 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/os.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 #ifdef STAND_ALONE
@@ -167,8 +165,8 @@ that have nothing. It provides a basic translation for the common standard
 signal numbers. I've been extra cautious with the ifdef's here. Probably more
 than is necessary... */
 
-char *
-os_strsignal(int n)
+const char *
+os_strsignal(const int n)
 {
 switch (n)
   {
@@ -284,8 +282,8 @@ switch (n)
 exit codes into text, but this function is implemented this way so that if any
 OS does have such a thing, it could be used instead of this build-in one. */
 
-char *
-os_strexit(int n)
+const char *
+os_strexit(const int n)
 {
 switch (n)
   {
@@ -463,6 +461,75 @@ calls the common function; on Linux it calls the Linux function.
 This function finds the addresses of all the running interfaces on the machine.
 A chain of blocks containing the textual form of the addresses is returned.
 
+getifaddrs() provides a sane consistent way to query this on modern OSs,
+otherwise fall back to a maze of twisty ioctl() calls
+
+Arguments:    none
+Returns:      a chain of ip_address_items, each pointing to a textual
+              version of an IP address, with the port field set to zero
+*/
+
+
+#ifndef NO_FIND_INTERFACES
+
+#ifdef HAVE_GETIFADDRS
+
+#include <ifaddrs.h>
+
+ip_address_item *
+os_common_find_running_interfaces(void)
+{
+struct ifaddrs *ifalist = NULL;
+ip_address_item *yield = NULL;
+ip_address_item *last = NULL;
+ip_address_item  *next;
+
+if (getifaddrs(&ifalist) != 0)
+  log_write(0, LOG_PANIC_DIE, "Unable to call getifaddrs: %d %s",
+    errno, strerror(errno));
+
+struct ifaddrs *ifa;
+for (ifa = ifalist; ifa != NULL; ifa = ifa->ifa_next)
+  {
+  if (ifa->ifa_addr->sa_family != AF_INET
+#if HAVE_IPV6
+    && ifa->ifa_addr->sa_family != AF_INET6
+#endif /* HAVE_IPV6 */
+    )
+    continue;
+
+  if ( !(ifa->ifa_flags & IFF_UP) ) /* Only want 'UP' interfaces */
+    continue;
+
+  /* Create a data block for the address, fill in the data, and put it on the
+  chain. */
+
+  next = store_get(sizeof(ip_address_item));
+  next->next = NULL;
+  next->port = 0;
+  (void)host_ntoa(-1, ifa->ifa_addr, next->address, NULL);
+
+  if (yield == NULL)
+    yield = last = next;
+  else
+    {
+    last->next = next;
+    last = next;
+    }
+
+  DEBUG(D_interface) debug_printf("Actual local interface address is %s (%s)\n",
+    last->address, ifa->ifa_name);
+  }
+
+/* free the list of addresses, and return the chain of data blocks. */
+
+freeifaddrs (ifalist);
+return yield;
+}
+
+#else /* HAVE_GETIFADDRS */
+
+/*
 Problems:
 
   (1) Solaris 2 has the SIOGIFNUM call to get the number of interfaces, but
@@ -486,15 +553,8 @@ Problems:
   the former, calling the latter does no harm, but it causes grief on Linux and
   BSD systems in the case of IP aliasing, so a means of cutting it out is
   provided.
-
-Arguments:    none
-Returns:      a chain of ip_address_items, each pointing to a textual
-              version of an IP address, with the port field set to zero
 */
 
-
-#ifndef NO_FIND_INTERFACES
-
 /* If there is IPv6 support, and SIOCGLIFCONF is defined, define macros to
 use these new, longer versions of the old IPv4 interfaces. Otherwise, define
 the macros to use the historical versions. */
@@ -556,7 +616,7 @@ char *cp;
 char buf[MAX_INTERFACES*sizeof(struct V_ifreq)];
 struct sockaddr *addrp;
 size_t len = 0;
-char addrbuf[256];
+char addrbuf[512];
 
 /* We have to create a socket in order to do ioctls on it to find out
 what we want to know. */
@@ -661,8 +721,9 @@ for (cp = buf; cp < buf + ifc.V_ifc_len; cp += len)
   if ((ifreq.V_ifr_flags & IFF_UP) == 0) continue;
 
   /* On some operating systems we have to get the IP address of the interface
-  by another call. On others, it's already there, but we must reinstate the
-  data in ifreq, because SIOCGIFFLAGS may wreck it. */
+  by another call. On others, it's already there, but we must copy the full
+  length because we only copied the basic length above, and anyway,
+  GIFFLAGS may have wrecked the data. */
 
   #ifndef SIOCGIFCONF_GIVES_ADDR
   if (ioctl(vs, V_GIFADDR, (char *)&ifreq) < 0)
@@ -671,8 +732,8 @@ for (cp = buf; cp < buf + ifc.V_ifc_len; cp += len)
   addrp = &ifreq.V_ifr_addr;
 
   #else
-  memcpy((char *)&ifreq, cp, sizeof(ifreq));
-  memcpy(addrbuf, (char *)&(ifreq.V_ifr_addr), len - sizeof(ifreq.V_ifr_name));
+  memcpy(addrbuf, cp + offsetof(struct V_ifreq, V_ifr_addr),
+    len - sizeof(ifreq.V_ifr_name));
   addrp = (struct sockaddr *)addrbuf;
   #endif
 
@@ -696,10 +757,12 @@ for (cp = buf; cp < buf + ifc.V_ifc_len; cp += len)
 
 /* Close the socket, and return the chain of data blocks. */
 
-close(vs);
+(void)close(vs);
 return yield;
 }
 
+#endif /* HAVE_GETIFADDRS */
+
 #else  /* NO_FIND_INTERFACES */
 
 /* Some experimental or developing OS (e.g. GNU/Hurd) do not have the ioctls,
@@ -732,6 +795,98 @@ return yield;
 
 
 
+/* ----------------------------------------------------------------------- */
+
+/***********************************************************
+*                 DNS Resolver Base Finder                 *
+***********************************************************/
+
+/* We need to be able to set options for the system resolver(5), historically
+made available as _res.  At least one OS (NetBSD) now no longer provides this
+directly, instead making you call a function per thread to get a handle.
+Other OSs handle thread-safe resolver differently, in ways which fail if the
+programmer creates their own structs. */
+
+#if !defined(OS_GET_DNS_RESOLVER_RES) && !defined(COMPILE_UTILITY)
+
+#include <resolv.h>
+
+/* confirmed that res_state is typedef'd as a struct* on BSD and Linux, will
+find out how unportable it is on other OSes, but most resolver implementations
+should be descended from ISC's bind.
+
+Linux and BSD do:
+  define _res (*__res_state())
+identically.  We just can't rely on __foo functions.  It's surprising that use
+of _res has been as portable as it has, for so long.
+
+So, since _res works everywhere, and everything can decode the struct, I'm
+going to gamble that res_state is a typedef everywhere and use that as the
+return type.
+*/
+
+res_state
+os_get_dns_resolver_res(void)
+{
+  return &_res;
+}
+
+#endif /* OS_GET_DNS_RESOLVER_RES */
+
+/* ----------------------------------------------------------------------- */
+
+/***********************************************************
+*                 unsetenv()                               *
+***********************************************************/
+
+/* Most modern systems define int unsetenv(const char*),
+* some don't. */
+
+#if !defined(OS_UNSETENV)
+int
+os_unsetenv(const unsigned char * name)
+{
+return unsetenv((char *)name);
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+
+/***********************************************************
+*               getcwd()                                   *
+***********************************************************/
+
+/* Glibc allows getcwd(NULL, 0) to do auto-allocation. Some systems
+do auto-allocation, but need the size of the buffer, and others
+may not even do this. If the OS supports getcwd(NULL, 0) we'll use
+this, for all other systems we provide our own getcwd() */
+
+#if !defined(OS_GETCWD)
+unsigned char *
+os_getcwd(unsigned char * buffer, size_t size)
+{
+return (unsigned char *) getcwd((char *)buffer, size);
+}
+#else
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+unsigned char *
+os_getcwd(unsigned char * buffer, size_t size)
+{
+char * b = (char *)buffer;
+
+if (!size) size = PATH_MAX;
+if (!b && !(b = malloc(size))) return NULL;
+if (!(b = getcwd(b, size))) return NULL;
+return buffer ? buffer : realloc(b, strlen(b) + 1);
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+
+
+
 
 /*************************************************
 **************************************************