This allows nbdkit to be cross-compiled for Windows (from Linux). On
Fedora[1] you would use the commands below:
sudo dnf install mingw64-gcc mingw64-dlfcn \
mingw64-gnutls mingw64-xz mingw64-zlib
mingw64-configure --disable-plugins
make
This results in a Windows binary which can be run on Windows or on
Linux under Wine.
It also probably allows nbdkit to be compiled on Windows natively
using the mingw-w64 port of GCC, but I did not test that.
There are several limitations compared to running nbdkit on a real
server, see ‘TODO’ for the complete list.
[1] Note that mingw64-configure is a Fedora-specific script which runs
./configure with extra parameters to make cross-compilation for
Windows work. You can add other configure parameters to the command
or if you're not using Fedora then use ‘./configure --host=...’
instead.
---
configure.ac | 21 ++-
common/include/byte-swapping.h | 7 +
include/nbdkit-common.h | 24 +--
include/nbdkit-plugin.h | 2 +-
server/internal.h | 47 ++++++
server/crypto.c | 7 +-
server/log-syslog.c | 5 +
server/main.c | 259 ++++++++++++++++++++++++---------
server/quit.c | 56 +++++++
server/sockets.c | 80 +++++++++-
server/utils.c | 80 ++++++++--
.gitignore | 4 +
TODO | 21 +++
server/Makefile.am | 17 ++-
server/nbdkit.def | 47 ++++++
server/nbdkit.syms | 2 +
tests/Makefile.am | 48 +++++-
17 files changed, 626 insertions(+), 101 deletions(-)
diff --git a/configure.ac b/configure.ac
index 8c2d9c0..f618324 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,6 +105,14 @@ AS_IF([test "x$is_windows" = "xyes"],[
AC_CHECK_TOOLS([MC],[windmc mc],[no])
AS_IF([test "x$MC" = "xno"],
[AC_MSG_ERROR([mc/windmc utility must be available when compiling for
Windows])])
+
+ dnl On Windows look for dlltool.
+ AC_CHECK_TOOLS([DLLTOOL],[dlltool],[no])
+ AS_IF([test "x$DLLTOOL" = "xno"],
+ [AC_MSG_ERROR([dlltool utility must be available when compiling for
Windows])])
+
+ dnl On Windows we require winsock2.
+ AC_CHECK_LIB([ws2_32], [socket])
])
dnl On Haiku we must use BSD-compatibility headers to get the endian
@@ -199,10 +207,21 @@ AC_CHECK_HEADERS([\
alloca.h \
byteswap.h \
endian.h \
+ grp.h \
+ netdb.h \
+ poll.h \
+ pwd.h \
selinux/selinux.h \
+ syslog.h \
sys/endian.h \
sys/prctl.h \
- sys/procctl.h])
+ sys/procctl.h \
+ sys/socket.h \
+ sys/un.h \
+ sys/wait.h \
+ termios.h \
+ windows.h \
+ ws2tcpip.h])
dnl Check for functions in libc, all optional.
AC_CHECK_FUNCS([\
diff --git a/common/include/byte-swapping.h b/common/include/byte-swapping.h
index c0831ec..a7ec78b 100644
--- a/common/include/byte-swapping.h
+++ b/common/include/byte-swapping.h
@@ -59,6 +59,13 @@
#include <sys/endian.h>
#endif
+#ifdef WIN32
+#include <stdlib.h>
+#define __bswap_16 _byteswap_ushort
+#define __bswap_32 _byteswap_ulong
+#define __bswap_64 _byteswap_uint64
+#endif
+
#ifndef htobe32
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define htobe16(x) __bswap_16 (x)
diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index cb9954e..7d68cd4 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -53,6 +53,12 @@ extern "C" {
#define ATTRIBUTE_FORMAT_PRINTF(fmtpos, argpos)
#endif
+#ifdef WIN32
+#define NBDKIT_DLLEXPORT __declspec(dllexport)
+#else
+#define NBDKIT_DLLEXPORT
+#endif
+
#define NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS 0
#define NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS 1
#define NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS 2
@@ -65,16 +71,16 @@ extern "C" {
#define NBDKIT_FUA_EMULATE 1
#define NBDKIT_FUA_NATIVE 2
-extern void nbdkit_error (const char *msg, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
-extern void nbdkit_verror (const char *msg, va_list args);
-extern void nbdkit_debug (const char *msg, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
-extern void nbdkit_vdebug (const char *msg, va_list args);
+extern NBDKIT_DLLEXPORT void nbdkit_error (const char *msg, ...) ATTRIBUTE_FORMAT_PRINTF
(1, 2);
+extern NBDKIT_DLLEXPORT void nbdkit_verror (const char *msg, va_list args);
+extern NBDKIT_DLLEXPORT void nbdkit_debug (const char *msg, ...) ATTRIBUTE_FORMAT_PRINTF
(1, 2);
+extern NBDKIT_DLLEXPORT void nbdkit_vdebug (const char *msg, va_list args);
-extern char *nbdkit_absolute_path (const char *path);
-extern int64_t nbdkit_parse_size (const char *str);
-extern int nbdkit_parse_bool (const char *str);
-extern int nbdkit_read_password (const char *value, char **password);
-extern char *nbdkit_realpath (const char *path);
+extern NBDKIT_DLLEXPORT char *nbdkit_absolute_path (const char *path);
+extern NBDKIT_DLLEXPORT int64_t nbdkit_parse_size (const char *str);
+extern NBDKIT_DLLEXPORT int nbdkit_parse_bool (const char *str);
+extern NBDKIT_DLLEXPORT int nbdkit_read_password (const char *value, char **password);
+extern NBDKIT_DLLEXPORT char *nbdkit_realpath (const char *path);
/* A static non-NULL pointer which can be used when you don't need a
* per-connection handle.
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index d43b2f5..55ccc23 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -127,7 +127,7 @@ struct nbdkit_plugin {
int (*can_multi_conn) (void *handle);
};
-extern void nbdkit_set_error (int err);
+extern NBDKIT_DLLEXPORT void nbdkit_set_error (int err);
#define NBDKIT_REGISTER_PLUGIN(plugin) \
NBDKIT_CXX_LANG_C \
diff --git a/server/internal.h b/server/internal.h
index 21bb4fa..24464b9 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -37,7 +37,20 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
#include <pthread.h>
#define NBDKIT_API_VERSION 2
@@ -58,6 +71,36 @@
#define SOCK_CLOEXEC 0
#endif
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+#ifdef WIN32
+
+/* Windows <errno.h> lacks certain errnos, so replace them here as
+ * best we can.
+ */
+#ifndef EBADMSG
+#define EBADMSG EPROTO
+#endif
+#ifndef ESHUTDOWN
+#define ESHUTDOWN ECONNABORTED
+#endif
+
+/* Windows doesn't have flockfile/funlockfile so messages may get
+ * split over threads.
+ */
+#define flockfile(fp)
+#define funlockfile(fp)
+
+#endif /* WIN32 */
+
+#ifndef WIN32
+#define HAVE_UNIX_SOCKETS 1
+#else
+#undef HAVE_UNIX_SOCKETS
+#endif
+
#if HAVE_VALGRIND
# include <valgrind.h>
/*
http://valgrind.org/docs/manual/faq.html#faq.unhelpful */
@@ -113,7 +156,11 @@ extern struct backend *backend;
/* quit.c */
extern volatile int quit;
+#ifndef WIN32
extern int quit_fd;
+#else
+extern HANDLE quit_fd;
+#endif
extern void set_up_quit_pipe (void);
extern void handle_quit (int sig);
extern void set_quit (void);
diff --git a/server/crypto.c b/server/crypto.c
index 711e929..3e73ff1 100644
--- a/server/crypto.c
+++ b/server/crypto.c
@@ -162,7 +162,12 @@ start_certificates (void)
const char *home;
CLEANUP_FREE char *path = NULL;
- if (geteuid () != 0) {
+#ifndef WIN32
+#define RUNNING_AS_NON_ROOT_FOR_CERTIFICATES_DIR (geteuid () != 0)
+#else
+#define RUNNING_AS_NON_ROOT_FOR_CERTIFICATES_DIR 0
+#endif
+ if (RUNNING_AS_NON_ROOT_FOR_CERTIFICATES_DIR) {
home = getenv ("HOME");
if (home) {
if (asprintf (&path, "%s/.pki/%s", home, PACKAGE_NAME) == -1) {
diff --git a/server/log-syslog.c b/server/log-syslog.c
index 0c58b75..fbaa802 100644
--- a/server/log-syslog.c
+++ b/server/log-syslog.c
@@ -42,6 +42,11 @@
#include "internal.h"
#include "syslog.h"
+#ifdef WIN32
+/* The Windows API doesn't have open_memstream, so hack this. */
+#define open_memstream(m,n) NULL
+#endif
+
/* Tempted to use LOG_FTP instead of LOG_DAEMON! */
static const int PRIORITY = LOG_DAEMON|LOG_ERR;
diff --git a/server/main.c b/server/main.c
index 5ba4d0d..475ba68 100644
--- a/server/main.c
+++ b/server/main.c
@@ -42,14 +42,22 @@
#include <signal.h>
#include <getopt.h>
#include <limits.h>
-#include <pwd.h>
-#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/wait.h>
#include <errno.h>
#include <assert.h>
-#include <syslog.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
#include <pthread.h>
@@ -63,7 +71,6 @@
#define FIRST_SOCKET_ACTIVATION_FD 3 /* defined by systemd ABI */
-static char *make_random_fifo (void);
static struct backend *open_plugin_so (size_t i, const char *filename, int short_name);
static struct backend *open_filter_so (struct backend *next, size_t i, const char
*filename, int short_name);
static void start_serving (void);
@@ -72,8 +79,6 @@ static void run_command (void);
static void change_user (void);
static void write_pidfile (void);
static void fork_into_background (void);
-static uid_t parseuser (const char *);
-static gid_t parsegroup (const char *);
static unsigned int get_socket_activation (void);
static int is_config_key (const char *key, size_t len);
@@ -106,8 +111,12 @@ bool forked_into_background;
/* The currently loaded plugin. */
struct backend *backend;
+#ifdef HAVE_UNIX_SOCKETS
+static char *make_random_fifo (void);
static char *random_fifo_dir = NULL;
static char *random_fifo = NULL;
+#endif
+static void free_random_fifo (void);
static void
usage (void)
@@ -167,6 +176,17 @@ main (int argc, char *argv[])
size_t i;
const char *magic_config_key;
+#ifdef WIN32
+ /* Initialize winsock. 2.2 is the current & latest version. */
+ WSADATA wsaData;
+ int wsaresult;
+ wsaresult = WSAStartup (MAKEWORD (2, 2), &wsaData);
+ if (wsaresult != 0) {
+ fprintf (stderr, "WSAStartup: failed with error %d\n", wsaresult);
+ exit (EXIT_FAILURE);
+ }
+#endif
+
threadlocal_init ();
/* The default setting for TLS depends on whether we were
@@ -407,6 +427,7 @@ main (int argc, char *argv[])
}
break;
+#ifdef HAVE_UNIX_SOCKETS
case 'U':
if (socket_activation) {
fprintf (stderr, "%s: cannot use socket activation with -U flag\n",
@@ -421,6 +442,14 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
break;
+#else /* !HAVE_UNIX_SOCKETS */
+ case 'U':
+ fprintf (stderr, "%s: -U option: Unix domain sockets "
+ "are not supported on this platform\n",
+ program_name);
+ exit (EXIT_FAILURE);
+#endif
+
case 'u':
user = optarg;
break;
@@ -670,21 +699,15 @@ main (int argc, char *argv[])
free (unixsocket);
free (pidfile);
- if (random_fifo) {
- unlink (random_fifo);
- free (random_fifo);
- }
-
- if (random_fifo_dir) {
- rmdir (random_fifo_dir);
- free (random_fifo_dir);
- }
+ free_random_fifo ();
crypto_free ();
exit (EXIT_SUCCESS);
}
+#ifdef HAVE_UNIX_SOCKETS
+
/* Implementation of '-U -' */
static char *
make_random_fifo (void)
@@ -717,6 +740,29 @@ make_random_fifo (void)
return unixsocket;
}
+static void
+free_random_fifo (void)
+{
+ if (random_fifo) {
+ unlink (random_fifo);
+ free (random_fifo);
+ }
+
+ if (random_fifo_dir) {
+ rmdir (random_fifo_dir);
+ free (random_fifo_dir);
+ }
+}
+
+#else /* !HAVE_UNIX_SOCKETS */
+
+static void
+free_random_fifo (void)
+{
+}
+
+#endif /* !HAVE_UNIX_SOCKETS */
+
static struct backend *
open_plugin_so (size_t i, const char *name, int short_name)
{
@@ -886,6 +932,8 @@ start_serving (void)
free_listening_sockets (socks, nr_socks);
}
+#ifndef WIN32
+
static void
set_up_signals (void)
{
@@ -905,6 +953,23 @@ set_up_signals (void)
sigaction (SIGPIPE, &sa, NULL);
}
+#else /* WIN32 */
+
+static void
+set_up_signals (void)
+{
+
+ signal (SIGINT, handle_quit);
+ signal (SIGTERM, handle_quit);
+}
+
+#endif /* WIN32 */
+
+#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
+
+static uid_t parseuser (const char *);
+static gid_t parsegroup (const char *);
+
static void
change_user (void)
{
@@ -937,6 +1002,77 @@ change_user (void)
}
}
+static uid_t
+parseuser (const char *id)
+{
+ struct passwd *pwd;
+ int saved_errno;
+
+ errno = 0;
+ pwd = getpwnam (id);
+
+ if (NULL == pwd) {
+ int val;
+
+ saved_errno = errno;
+
+ if (sscanf (id, "%d", &val) == 1)
+ return val;
+
+ fprintf (stderr, "%s: -u option: %s is not a valid user name or uid",
+ program_name, id);
+ if (saved_errno != 0)
+ fprintf (stderr, " (getpwnam error: %s)", strerror (saved_errno));
+ fprintf (stderr, "\n");
+ exit (EXIT_FAILURE);
+ }
+
+ return pwd->pw_uid;
+}
+
+static gid_t
+parsegroup (const char *id)
+{
+ struct group *grp;
+ int saved_errno;
+
+ errno = 0;
+ grp = getgrnam (id);
+
+ if (NULL == grp) {
+ int val;
+
+ saved_errno = errno;
+
+ if (sscanf (id, "%d", &val) == 1)
+ return val;
+
+ fprintf (stderr, "%s: -g option: %s is not a valid group name or gid",
+ program_name, id);
+ if (saved_errno != 0)
+ fprintf (stderr, " (getgrnam error: %s)", strerror (saved_errno));
+ fprintf (stderr, "\n");
+ exit (EXIT_FAILURE);
+ }
+
+ return grp->gr_gid;
+}
+
+#else /* a platform like Windows which lacks pwd/grp functions */
+
+static void
+change_user (void)
+{
+ if (user || group) {
+ fprintf (stderr, "%s: "
+ "-u and -g options are not available on this plaform\n",
+ program_name);
+ exit (EXIT_FAILURE);
+ }
+}
+
+#endif
+
static void
write_pidfile (void)
{
@@ -967,6 +1103,8 @@ write_pidfile (void)
debug ("written pidfile %s", pidfile);
}
+#ifndef WIN32
+
static void
fork_into_background (void)
{
@@ -1003,6 +1141,24 @@ fork_into_background (void)
debug ("forked into background (new pid = %d)", getpid ());
}
+#else /* WIN32 */
+
+static void
+fork_into_background (void)
+{
+ if (foreground)
+ return;
+
+ fprintf (stderr, "%s: cannot fork into background on Windows.\n"
+ "You must use -f or equivalent option.\n",
+ program_name);
+ exit (EXIT_FAILURE);
+}
+
+#endif /* WIN32 */
+
+#ifndef WIN32
+
static void
run_command (void)
{
@@ -1086,61 +1242,22 @@ run_command (void)
debug ("forked into background (new pid = %d)", getpid ());
}
-static uid_t
-parseuser (const char *id)
-{
- struct passwd *pwd;
- int saved_errno;
-
- errno = 0;
- pwd = getpwnam (id);
-
- if (NULL == pwd) {
- int val;
-
- saved_errno = errno;
+#else /* !WIN32 */
- if (sscanf (id, "%d", &val) == 1)
- return val;
-
- fprintf (stderr, "%s: -u option: %s is not a valid user name or uid",
- program_name, id);
- if (saved_errno != 0)
- fprintf (stderr, " (getpwnam error: %s)", strerror (saved_errno));
- fprintf (stderr, "\n");
- exit (EXIT_FAILURE);
- }
-
- return pwd->pw_uid;
-}
-
-static gid_t
-parsegroup (const char *id)
+static void
+run_command (void)
{
- struct group *grp;
- int saved_errno;
-
- errno = 0;
- grp = getgrnam (id);
-
- if (NULL == grp) {
- int val;
+ if (!run)
+ return;
- saved_errno = errno;
-
- if (sscanf (id, "%d", &val) == 1)
- return val;
+ fprintf ("%s: cannot use --run on Windows.\n",
+ program_name);
+ exit (EXIT_FAILURE);
+}
- fprintf (stderr, "%s: -g option: %s is not a valid group name or gid",
- program_name, id);
- if (saved_errno != 0)
- fprintf (stderr, " (getgrnam error: %s)", strerror (saved_errno));
- fprintf (stderr, "\n");
- exit (EXIT_FAILURE);
- }
+#endif /* WIN32 */
- return grp->gr_gid;
-}
+#ifndef WIN32
/* Returns 0 if no socket activation, or the number of FDs.
* See also virGetListenFDs in libvirt.org:src/util/virutil.c
@@ -1199,6 +1316,16 @@ get_socket_activation (void)
return nr_fds;
}
+#else /* WIN32 */
+
+static unsigned int
+get_socket_activation (void)
+{
+ return 0;
+}
+
+#endif /* WIN32 */
+
/* When parsing plugin and filter config key=value from the command
* line, check that the key is a simple alphanumeric with period,
* underscore or dash.
diff --git a/server/quit.c b/server/quit.c
index c6f829e..edfc3ea 100644
--- a/server/quit.c
+++ b/server/quit.c
@@ -47,8 +47,16 @@
* a race.
*/
volatile int quit;
+
+#ifndef WIN32
int quit_fd;
static int write_quit_fd;
+#else
+HANDLE quit_fd;
+static HANDLE write_quit_fd;
+#endif
+
+#ifndef WIN32
void
set_up_quit_pipe (void)
@@ -63,12 +71,47 @@ set_up_quit_pipe (void)
write_quit_fd = fds[1];
}
+#else /* WIN32 */
+
+/* Non-blocking I/O is not possible on anonymous pipes in Windows, so
+ * we have to create a random named pipe. See:
+ *
https://stackoverflow.com/questions/60645/overlapped-i-o-on-anonymous-pipe
+ */
+void
+set_up_quit_pipe (void)
+{
+ char name[64];
+
+ snprintf (name, sizeof name, "\\\\.\\Pipe\\nbdkit.%lu",
+ GetCurrentProcessId ());
+ quit_fd = CreateNamedPipeA (name, PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE|PIPE_WAIT,
+ 1, 4096, 4096, 0, NULL);
+ if (quit_fd == INVALID_HANDLE_VALUE) {
+ fprintf (stderr, "%s: CreateNamedPipeA for read: %s: error %lu\n",
+ program_name, name, GetLastError ());
+ exit (EXIT_FAILURE);
+ }
+
+ write_quit_fd = CreateFileA (name, GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (write_quit_fd == INVALID_HANDLE_VALUE) {
+ fprintf (stderr, "%s: CreateNamedPipeA for write: %s: error %lu\n",
+ program_name, name, GetLastError ());
+ exit (EXIT_FAILURE);
+ }
+}
+
+#endif /* WIN32 */
+
void
handle_quit (int sig)
{
set_quit ();
}
+#ifndef WIN32
+
void
set_quit (void)
{
@@ -80,3 +123,16 @@ set_quit (void)
write (write_quit_fd, &c, 1);
#pragma GCC diagnostic pop
}
+
+#else /* WIN32 */
+
+void
+set_quit (void)
+{
+ char c = 0;
+
+ quit = 1;
+ WriteFile (write_quit_fd, &c, 1, NULL, NULL);
+}
+
+#endif /* WIN32 */
diff --git a/server/sockets.c b/server/sockets.c
index cc15c5e..eb19a15 100644
--- a/server/sockets.c
+++ b/server/sockets.c
@@ -39,12 +39,32 @@
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+
+#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
+#endif
+
+#ifdef HAVE_NETDB_H
#include <netdb.h>
+#endif
+
+#ifdef HAVE_POLL_H
#include <poll.h>
-#include <errno.h>
-#include <assert.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
#ifdef HAVE_SELINUX_SELINUX_H
#include <selinux/selinux.h>
@@ -86,6 +106,8 @@ clear_selinux_label (void)
#endif
}
+#ifdef HAVE_UNIX_SOCKETS
+
int *
bind_unix_socket (size_t *nr_socks)
{
@@ -140,6 +162,23 @@ bind_unix_socket (size_t *nr_socks)
return ret;
}
+#else /* !HAVE_UNIX_SOCKETS */
+
+int *
+bind_unix_socket (size_t *nr_socks)
+{
+ fprintf (stderr, "%s: -U option: Unix domain sockets "
+ "are not supported on this platform\n",
+ program_name);
+ exit (EXIT_FAILURE);
+}
+
+#endif /* !HAVE_UNIX_SOCKETS */
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
int *
bind_tcpip_socket (size_t *nr_socks)
{
@@ -314,6 +353,8 @@ accept_connection (int listen_sock)
*/
}
+#ifndef WIN32
+
/* Check the list of sockets plus quit_fd until a POLLIN event occurs
* on any of them.
*
@@ -360,6 +401,41 @@ check_sockets_and_quit_fd (int *socks, size_t nr_socks)
}
}
+#else /* WIN32 */
+
+static void
+check_sockets_and_quit_fd (int *socks, size_t nr_socks)
+{
+ size_t i;
+ HANDLE handles[nr_socks+1];
+ DWORD r;
+
+ for (i = 0; i < nr_socks; ++i)
+ handles[i] = socks[i];
+ handles[nr_socks] = quit_fd;
+
+ r = WaitForMultipleObjectsEx ((DWORD) (nr_socks+1), handles,
+ FALSE, INFINITE, TRUE);
+ if (r == WAIT_FAILED) {
+ fprintf (stderr, "%s: WaitForMultipleObjectsEx: error %lu\n",
+ program_name, GetLastError ());
+ exit (EXIT_FAILURE);
+ }
+
+ if (r == WAIT_OBJECT_0 + nr_socks) /* quit_fd signalled. */
+ return;
+
+ if (r >= WAIT_OBJECT_0 && r <= WAIT_OBJECT_0 + nr_socks - 1) {
+ i = r - WAIT_OBJECT_0;
+ accept_connection (socks[i]);
+ return;
+ }
+
+ debug ("WaitForMultipleObjectsEx: unexpected return value: %lu\n", r);
+}
+
+#endif /* WIN32 */
+
void
accept_incoming_connections (int *socks, size_t nr_socks)
{
diff --git a/server/utils.c b/server/utils.c
index d67ec43..ad2726c 100644
--- a/server/utils.c
+++ b/server/utils.c
@@ -41,9 +41,12 @@
#include <string.h>
#include <unistd.h>
#include <limits.h>
-#include <termios.h>
#include <errno.h>
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
#include "internal.h"
#include "realpath.h"
#include "getline.h"
@@ -184,12 +187,72 @@ nbdkit_parse_bool (const char *str)
return -1;
}
+#ifndef WIN32
+
+typedef struct termios echo_mode;
+
+static void
+echo_off (echo_mode *old_mode)
+{
+ struct termios temp;
+ int tty;
+
+ tty = isatty (0);
+ if (tty) {
+ tcgetattr (0, old_mode);
+ temp = *old_mode;
+ temp.c_lflag &= ~ECHO;
+ tcsetattr (0, TCSAFLUSH, &temp);
+ }
+}
+
+static void
+echo_restore (const echo_mode *old_mode)
+{
+ int tty;
+
+ tty = isatty (0);
+ if (tty)
+ tcsetattr (0, TCSAFLUSH, old_mode);
+}
+
+#else /* WIN32 */
+
+/* Windows implementation of tty echo off based on this:
+ *
https://stackoverflow.com/a/1455007
+ */
+typedef DWORD echo_mode;
+
+static void
+echo_off (echo_mode *old_mode)
+{
+ HANDLE h_stdin;
+ DWORD mode;
+
+ h_stdin = GetStdHandle (STD_INPUT_HANDLE);
+ GetConsoleMode (h_stdin, old_mode);
+ mode = *old_mode;
+ mode &= ~ENABLE_ECHO_INPUT;
+ SetConsoleMode (h_stdin, mode);
+}
+
+static void
+echo_restore (const echo_mode *old_mode)
+{
+ HANDLE h_stdin;
+
+ h_stdin = GetStdHandle (STD_INPUT_HANDLE);
+ SetConsoleMode (h_stdin, *old_mode);
+}
+
+#endif /* WIN32 */
+
/* Read a password from configuration value. */
int
nbdkit_read_password (const char *value, char **password)
{
- int tty, err;;
- struct termios orig, temp;
+ int err;
+ echo_mode orig;
ssize_t r;
size_t n;
FILE *fp;
@@ -199,20 +262,13 @@ nbdkit_read_password (const char *value, char **password)
printf ("password: ");
/* Set no echo. */
- tty = isatty (0);
- if (tty) {
- tcgetattr (0, &orig);
- temp = orig;
- temp.c_lflag &= ~ECHO;
- tcsetattr (0, TCSAFLUSH, &temp);
- }
+ echo_off (&orig);
r = getline (password, &n, stdin);
err = errno;
/* Restore echo. */
- if (tty)
- tcsetattr (0, TCSAFLUSH, &orig);
+ echo_restore (&orig);
/* Complete the printf above. */
printf ("\n");
diff --git a/.gitignore b/.gitignore
index 35561e9..ba29b70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@
*.trs
.deps
+.dirstamp
.gdb_history
.libs
Makefile
@@ -55,10 +56,13 @@ Makefile.in
/ltmain.sh
/missing
/nbdkit
+/nbdkit.exe
/plugins/example4/nbdkit-example4-plugin
/plugins/tar/nbdkit-tar-plugin
/podwrapper.pl
+/server/libnbdkit.a
/server/nbdkit
+/server/nbdkit.exe
/server/nbdkit.pc
/server/protostrings.c
/server/synopsis.c
diff --git a/TODO b/TODO
index 2ef8e1a..5a4e598 100644
--- a/TODO
+++ b/TODO
@@ -131,3 +131,24 @@ nbdkit processes.
The nbd plugin (plugins/nbd) already contains an NBD client, so we
could factor this client out and make it available to other plugins to
use.
+
+Windows support
+---------------
+
+The following features are not yet implemented on Windows:
+
+Because there is no fork(2) we cannot fork into background (ie. -f or
+equivalent must always be used). --run does not work for a similar
+reason.
+
+-u/-g flags do not work.
+
+No socket activation. Unclear if this makes sense on Windows, since
+there may not be a way to pass opened file descriptors to a newly
+created process.
+
+Unix domain sockets (ie. -U option) do not work, the clue being in the
+name. However is there an equivalent filesystem sockets feature in
+Windows that we should be using instead?
+
+Many plugins and filters have not yet been ported to Windows.
diff --git a/server/Makefile.am b/server/Makefile.am
index 80efef5..c6c6e15 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -32,7 +32,9 @@
include $(top_srcdir)/common-rules.mk
-EXTRA_DIST = nbdkit.syms
+EXTRA_DIST = \
+ nbdkit.def \
+ nbdkit.syms
sbin_PROGRAMS = nbdkit
@@ -86,6 +88,19 @@ if USE_LINKER_SCRIPT_FOR_SERVER
nbdkit_LDFLAGS += -Wl,--version-script=$(srcdir)/nbdkit.syms
endif
+if IS_WINDOWS
+
+# On Windows, generate an import library so that plugins can link
+# against the executable.
+#
https://stackoverflow.com/questions/15454968/dll-plugin-that-uses-functio...
+
+lib_LIBRARIES = libnbdkit.a
+
+libnbdkit.a: .libs/nbdkit.exe
+ $(DLLTOOL) -v .libs/nbdkit.exe -D nbdkit.exe -d nbdkit.def -l $@
+
+endif
+
# protostrings.c is generated from the protocol.h header file where it
# is used to map NBD protocol flags to strings.
diff --git a/server/nbdkit.def b/server/nbdkit.def
new file mode 100644
index 0000000..df6f9a6
--- /dev/null
+++ b/server/nbdkit.def
@@ -0,0 +1,47 @@
+; nbdkit
+; Copyright (C) 2018 Red Hat Inc.
+; All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions are
+; met:
+;
+; * Redistributions of source code must retain the above copyright
+; notice, this list of conditions and the following disclaimer.
+;
+; * Redistributions in binary form must reproduce the above copyright
+; notice, this list of conditions and the following disclaimer in the
+; documentation and/or other materials provided with the distribution.
+;
+; * Neither the name of Red Hat nor the names of its contributors may be
+; used to endorse or promote products derived from this software without
+; specific prior written permission.
+;
+; THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+; THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+; PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+; USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+; OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+; SUCH DAMAGE.
+;
+; Controls the visibility of symbols on Windows.
+;
+; NOTE: If you edit this file you must also edit nbdkit.syms.
+;
+EXPORTS
+ nbdkit_absolute_path
+ nbdkit_debug
+ nbdkit_error
+ nbdkit_parse_bool
+ nbdkit_parse_size
+ nbdkit_read_password
+ nbdkit_realpath
+ nbdkit_set_error
+ nbdkit_vdebug
+ nbdkit_verror
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index 672abd2..f889311 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -36,6 +36,8 @@
# functions from nbdkit, so this script lists only the symbols we want
# to export.
+# NOTE: If you edit this file you must also edit nbdkit.def.
+
{
# The functions we want plugins and filters to call.
global:
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e96dac9..f13d091 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -183,7 +183,11 @@ test_ansi_c_plugin_la_CFLAGS = \
# For use of the -rpath option, see:
#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
test_ansi_c_plugin_la_LDFLAGS = \
- -module -avoid-version -shared -rpath /nowhere
+ -module -avoid-version -shared -rpath /nowhere -no-undefined
+if IS_WINDOWS
+test_ansi_c_plugin_la_LIBADD = \
+ -L$(top_builddir)/server -lnbdkit
+endif
if HAVE_CXX
# This builds a plugin and a filter using the C++ compiler. They
@@ -205,7 +209,11 @@ test_cxx_plugin_la_CXXFLAGS = \
# For use of the -rpath option, see:
#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
test_cxx_plugin_la_LDFLAGS = \
- -module -avoid-version -shared -rpath /nowhere
+ -module -avoid-version -shared -rpath /nowhere -no-undefined
+if IS_WINDOWS
+test_cxx_plugin_la_LIBADD = \
+ -L$(top_builddir)/server -lnbdkit
+endif
test_cxx_filter_la_SOURCES = \
test-cxx-filter.cpp \
@@ -217,7 +225,11 @@ test_cxx_filter_la_CXXFLAGS = \
# For use of the -rpath option, see:
#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
test_cxx_filter_la_LDFLAGS = \
- -module -avoid-version -shared -rpath /nowhere
+ -module -avoid-version -shared -rpath /nowhere -no-undefined
+if IS_WINDOWS
+test_cxx_filter_la_LIBADD = \
+ -L$(top_builddir)/server -lnbdkit
+endif
endif HAVE_CXX
# Exit with parent test.
@@ -533,7 +545,11 @@ libvixDiskLib_la_CXXFLAGS = \
# For use of the -rpath option, see:
#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
libvixDiskLib_la_LDFLAGS = \
- -shared -version-number 6:0:0 -rpath /nowhere
+ -shared -version-number 6:0:0 -rpath /nowhere -no-undefined
+if IS_WINDOWS
+libvixDiskLib_la_LIBADD = \
+ -L$(top_builddir)/server -lnbdkit
+endif
# zero plugin test.
TESTS += test-zero.sh
@@ -695,7 +711,11 @@ test_layers_plugin_la_CFLAGS = $(WARNINGS_CFLAGS)
# For use of the -rpath option, see:
#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
test_layers_plugin_la_LDFLAGS = \
- -module -avoid-version -shared -rpath /nowhere
+ -module -avoid-version -shared -rpath /nowhere -no-undefined
+if IS_WINDOWS
+test_layers_plugin_la_LIBADD = \
+ -L$(top_builddir)/server -lnbdkit
+endif
test_layers_filter1_la_SOURCES = \
test-layers-filter.c \
@@ -705,7 +725,11 @@ test_layers_filter1_la_CFLAGS = $(WARNINGS_CFLAGS)
-Dlayer='"filter1"'
# For use of the -rpath option, see:
#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
test_layers_filter1_la_LDFLAGS = \
- -module -avoid-version -shared -rpath /nowhere
+ -module -avoid-version -shared -rpath /nowhere -no-undefined
+if IS_WINDOWS
+test_layers_filter1_la_LIBADD = \
+ -L$(top_builddir)/server -lnbdkit
+endif
test_layers_filter2_la_SOURCES = \
test-layers-filter.c \
@@ -715,7 +739,11 @@ test_layers_filter2_la_CFLAGS = $(WARNINGS_CFLAGS)
-Dlayer='"filter2"'
# For use of the -rpath option, see:
#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
test_layers_filter2_la_LDFLAGS = \
- -module -avoid-version -shared -rpath /nowhere
+ -module -avoid-version -shared -rpath /nowhere -no-undefined
+if IS_WINDOWS
+test_layers_filter2_la_LIBADD = \
+ -L$(top_builddir)/server -lnbdkit
+endif
test_layers_filter3_la_SOURCES = \
test-layers-filter.c \
@@ -725,7 +753,11 @@ test_layers_filter3_la_CFLAGS = $(WARNINGS_CFLAGS)
-Dlayer='"filter3"'
# For use of the -rpath option, see:
#
https://lists.gnu.org/archive/html/libtool/2007-07/msg00067.html
test_layers_filter3_la_LDFLAGS = \
- -module -avoid-version -shared -rpath /nowhere
+ -module -avoid-version -shared -rpath /nowhere -no-undefined
+if IS_WINDOWS
+test_layers_filter3_la_LIBADD = \
+ -L$(top_builddir)/server -lnbdkit
+endif
# blocksize filter test.
TESTS += test-blocksize.sh
--
2.20.1