changeset 16156:034184e9b5ea

sethostname: Port to Windows platforms. * lib/sethostname.c: Provide an alternate implementation for Windows platforms. * tests/test-sethostname2.c (geteuid): Redefine on Cygwin. (main): Skip the test if sethostname() fails with EPERM. On Windows platforms, don't check the result of gethostname(). 2011-12-04 Bruno Haible <bruno@clisp.org>
author Bruno Haible <bruno@clisp.org>
date Mon, 05 Dec 2011 03:46:40 +0100
parents 692de13f0c43
children 2f911f94bb4f
files ChangeLog lib/sethostname.c tests/test-sethostname2.c
diffstat 3 files changed, 101 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2011-12-04  Bruno Haible  <bruno@clisp.org>
+
+	sethostname: Port to Windows platforms.
+	* lib/sethostname.c: Provide an alternate implementation for Windows
+	platforms.
+	* tests/test-sethostname2.c (geteuid): Redefine on Cygwin.
+	(main): Skip the test if sethostname() fails with EPERM. On Windows
+	platforms, don't check the result of gethostname().
+
 2011-12-04  Bruno Haible  <bruno@clisp.org>
 	    Jim Meyering  <meyering@redhat.com>
 
--- a/lib/sethostname.c
+++ b/lib/sethostname.c
@@ -19,6 +19,7 @@
 
 #include <config.h>
 
+#if !((defined _WIN32 || defined __WIN32__) || defined __CYGWIN__)
 /* Unix API.  */
 
 /* Specification.  */
@@ -82,3 +83,80 @@
   return -1;
 #endif
 }
+
+#else
+/* Native Windows API.  Also used on Cygwin.  */
+
+/* Ensure that <windows.h> declares SetComputerNameEx.  */
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x500
+
+#define WIN32_LEAN_AND_MEAN
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+
+#include <windows.h>
+/* The mingw header files don't define GetComputerNameEx, SetComputerNameEx.  */
+#ifndef GetComputerNameEx
+# define GetComputerNameEx GetComputerNameExA
+#endif
+#ifndef SetComputerNameEx
+# define SetComputerNameEx SetComputerNameExA
+#endif
+
+/* Set up to LEN chars of NAME as system hostname.
+   Return 0 if ok, set errno and return -1 on error. */
+
+int
+sethostname (const char *name, size_t len)
+{
+  char name_asciz[HOST_NAME_MAX + 1];
+  char old_name[HOST_NAME_MAX + 1];
+  DWORD old_name_len;
+
+  /* Ensure the string isn't too long.  glibc does allow setting an
+     empty hostname so no point in enforcing a lower bound. */
+  if (len > HOST_NAME_MAX)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  /* Prepare a NUL-terminated copy of name.  */
+  memcpy (name_asciz, name, len);
+  name_asciz[len] = '\0';
+
+  /* Save the old NetBIOS name.  */
+  old_name_len = sizeof (old_name) - 1;
+  if (! GetComputerNameEx (ComputerNamePhysicalNetBIOS,
+                           old_name, &old_name_len))
+    old_name_len = 0;
+
+  /* Set both the NetBIOS and the first part of the IP / DNS name.  */
+  if (! SetComputerNameEx (ComputerNamePhysicalNetBIOS, name_asciz))
+    {
+      errno = (GetLastError () == ERROR_ACCESS_DENIED ? EPERM : EINVAL);
+      return -1;
+    }
+  if (! SetComputerNameEx (ComputerNamePhysicalDnsHostname, name_asciz))
+    {
+      errno = (GetLastError () == ERROR_ACCESS_DENIED ? EPERM : EINVAL);
+      /* Restore the old NetBIOS name.  */
+      if (old_name_len > 0)
+        {
+          old_name[old_name_len] = '\0';
+          SetComputerNameEx (ComputerNamePhysicalNetBIOS, old_name);
+        }
+      return -1;
+    }
+
+  /* Note that the new host name becomes effective only after a reboot!  */
+  return 0;
+}
+
+#endif
--- a/tests/test-sethostname2.c
+++ b/tests/test-sethostname2.c
@@ -31,8 +31,10 @@
 
 #define TESTHOSTNAME "gnulib-hostname"
 
-/* mingw and MSVC 9 lack geteuid, so setup a dummy value.  */
-#if !HAVE_GETEUID
+/* mingw and MSVC 9 lack geteuid, so setup a dummy value.
+   On Cygwin, geteuid() may return non-zero even for user accounts with
+   administrator privileges, so use a dummy value as well.  */
+#if !HAVE_GETEUID || defined __CYGWIN__
 # define geteuid() 0
 #endif
 
@@ -72,6 +74,11 @@
                    "Skipping test: sethostname is not really implemented.\n");
           return 77;
         }
+      else if (rcs == -1 && errno == EPERM)
+        {
+          fprintf (stderr, "Skipping test: insufficient permissions.\n");
+          return 77;
+        }
       else
         {
           fprintf (stderr, "error setting valid hostname.\n");
@@ -82,6 +89,10 @@
     {
       ASSERT (gethostname (newname, sizeof (newname)) == 0);
 
+      /* On Windows, a hostname change becomes effective only after
+         a reboot.  */
+#if !((defined _WIN32 || defined __WIN32__) || defined __CYGWIN__)
+
       /* if we don't get back what we put in, there is no need to
          restore the original name as we will assume it was not
          properly changed. */
@@ -90,6 +101,7 @@
           fprintf (stderr, "set/get comparison failed.\n");
           return 1;
         }
+#endif
     }
 
   /* glibc does allow setting a zero length name, so the lower bound