diff options
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | lib/sethostname.c | 78 | ||||
-rw-r--r-- | tests/test-sethostname2.c | 16 |
3 files changed, 101 insertions, 2 deletions
@@ -1,4 +1,13 @@ 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> tests: Avoid spurious error message on platforms without mktemp program. diff --git a/lib/sethostname.c b/lib/sethostname.c index 3c2ec3c0c5..6555921609 100644 --- 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 @@ sethostname (const char *name, size_t len) 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 diff --git a/tests/test-sethostname2.c b/tests/test-sethostname2.c index 51f92aeeb1..92c2c03991 100644 --- 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 @@ main (int argc, char *argv[] _GL_UNUSED) "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 @@ main (int argc, char *argv[] _GL_UNUSED) { 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 @@ main (int argc, char *argv[] _GL_UNUSED) fprintf (stderr, "set/get comparison failed.\n"); return 1; } +#endif } /* glibc does allow setting a zero length name, so the lower bound |