# HG changeset patch # User Bruno Haible # Date 1323053200 -3600 # Node ID 034184e9b5ea777052f55fbabffe957ce39c9e12 # Parent 692de13f0c4320db62f23ce64ea23725798acfa1 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 diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2011-12-04 Bruno Haible + + 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 Jim Meyering diff --git a/lib/sethostname.c b/lib/sethostname.c --- a/lib/sethostname.c +++ b/lib/sethostname.c @@ -19,6 +19,7 @@ #include +#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 declares SetComputerNameEx. */ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x500 + +#define WIN32_LEAN_AND_MEAN + +/* Specification. */ +#include + +#include +#include +#include + +#include +/* 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 --- 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