From 39a629b0d63ca675bd24844c924203d30bd3ba9b Mon Sep 17 00:00:00 2001
From: Paul Joseph Davis <davisp@apache.org>
Date: Tue, 18 Aug 2009 01:27:03 +0000
Subject: Applying markh's Windows build patch.

Fixes COUCHDB-406



git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@805243 13f79535-47bb-0310-9956-ffa450edef68
---
 src/couchdb/Makefile.am                   |  50 +++++++++--
 src/couchdb/priv/couchspawnkillable       |  20 -----
 src/couchdb/priv/couchspawnkillable.sh    |  20 +++++
 src/couchdb/priv/couchspawnkillable_win.c | 139 ++++++++++++++++++++++++++++++
 4 files changed, 200 insertions(+), 29 deletions(-)
 delete mode 100755 src/couchdb/priv/couchspawnkillable
 create mode 100755 src/couchdb/priv/couchspawnkillable.sh
 create mode 100644 src/couchdb/priv/couchspawnkillable_win.c

(limited to 'src')

diff --git a/src/couchdb/Makefile.am b/src/couchdb/Makefile.am
index 7d7b1720..89a0c7f2 100644
--- a/src/couchdb/Makefile.am
+++ b/src/couchdb/Makefile.am
@@ -13,6 +13,11 @@
 SUBDIRS = priv
 
 ICU_LOCAL_FLAGS = $(ICU_LOCAL_CFLAGS) $(ICU_LOCAL_LDFLAGS)
+if WINDOWS
+ICU_LOCAL_LIBS=-licuuc -licudt -licuin
+else
+ICU_LOCAL_LIBS=-licuuc -licudata -licui18n
+endif
 
 # devdocdir = $(localdocdir)/developer/couchdb
 couchlibdir = $(localerlanglibdir)/couch-$(version)
@@ -25,19 +30,28 @@ couchprivlib_LTLIBRARIES = couch_erl_driver.la
 couch_erl_driver_la_SOURCES = couch_erl_driver.c
 couch_erl_driver_la_LDFLAGS = -module -avoid-version $(ICU_LOCAL_FLAGS)
 couch_erl_driver_la_CFLAGS = $(ICU_LOCAL_FLAGS)
-couch_erl_driver_la_LIBADD = -licuuc -licudata -licui18n
+couch_erl_driver_la_LIBADD = $(ICU_LOCAL_LIBS)
 
 locallibbin_PROGRAMS = couchjs
 couchjs_SOURCES = couch_js.c curlhelper.c curlhelper.h
-couchjs_LDLAGS = $(CURL_LDLAGS)
+couchjs_LDFLAGS = $(CURL_LDFLAGS)
 couchjs_CFLAGS = $(CURL_CFLAGS)
-couchjs_LDADD = -lcurl
+couchjs_LDADD = $(CURL_LDFLAGS)
+
+if WINDOWS
+priv_couchspawnkillable_win_SOURCES = priv/couchspawnkillable_win.c
+couchpriv_PROGRAMS = priv/couchspawnkillable_win
+couch_erl_driver_la_LDFLAGS += -no-undefined
+
+# copy ICU dlls for the erlang driver
+dist_couch_erl_driver_la_DATA=$(ICU_LOCAL_BIN)/icuuc42.dll $(ICU_LOCAL_BIN)/icudt42.dll $(ICU_LOCAL_BIN)/icuin42.dll
+couch_erl_driver_ladir=$(bindir)
+endif
 
 couchinclude_DATA = couch_db.hrl
 
 couchebin_DATA = $(compiled_files)
 
-couchpriv_SCRIPTS = priv/couchspawnkillable
 
 # dist_devdoc_DATA = $(doc_base) $(doc_modules)
 
@@ -98,6 +112,7 @@ source_files = \
 EXTRA_DIST = $(source_files) couch_db.hrl couch_stats.hrl
 
 compiled_files = \
+	priv/couchspawnkillable \
     couch.app \
     couch_btree.beam \
     couch_batch_save.beam \
@@ -191,14 +206,31 @@ couch.app: couch.app.tpl
 %.beam: %.erl couch_db.hrl
 	$(ERLC) $(ERLC_FLAGS) ${TEST} $<;
 
+if WINDOWS
+priv/couchspawnkillable: priv/couchspawnkillable_win
+	cp $< $@
+else
+priv/couchspawnkillable: priv/couchspawnkillable.sh
+	cp $< $@
+endif
+
 install-data-hook:
-	if test -f "$(DESTDIR)/$(couchprivlibdir)/couch_erl_driver"; then \
-	    rm -f "$(DESTDIR)/$(couchprivlibdir)/couch_erl_driver.so"; \
-	    cd "$(DESTDIR)/$(couchprivlibdir)" && \
+	if test -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver"; then \
+	    rm -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver.so"; \
+	    cd "$(DESTDIR)$(couchprivlibdir)" && \
 	        $(LN_S) couch_erl_driver couch_erl_driver.so; \
 	fi
+if WINDOWS
+# libtool and automake have defeated markh.  For each of our executables
+# we end up with 2 copies - one directly in the 'target' folder (eg, 'priv')
+# and another - the correct one - in .libs.  The former doesn't work but is 
+# what gets installed for 'couchspawnkillable' - but the correct one for
+# couchjs.exe *does* get copied.  *shrug*  So just clobber it with the 
+# correct one here... See bug COUCHDB-439
+	$(INSTALL) priv/.libs/couchspawnkillable_win.exe "$(DESTDIR)$(couchprivdir)/couchspawnkillable.exe"
+endif
 
 uninstall-local:
-	if test -f "$(DESTDIR)/$(couchprivlibdir)/couch_erl_driver"; then \
-	    rm -f "$(DESTDIR)/$(couchprivlibdir)/couch_erl_driver.so"; \
+	if test -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver"; then \
+	    rm -f "$(DESTDIR)$(couchprivlibdir)/couch_erl_driver.so"; \
 	fi
diff --git a/src/couchdb/priv/couchspawnkillable b/src/couchdb/priv/couchspawnkillable
deleted file mode 100755
index f8d042e3..00000000
--- a/src/couchdb/priv/couchspawnkillable
+++ /dev/null
@@ -1,20 +0,0 @@
-#! /bin/sh -e
-
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-# The purpose of this script is to echo an OS specific command before launching
-# the actual process. This provides a way for Erlang to hard-kill its external
-# processes.
-
-echo "kill -9 $$"
-exec $*
diff --git a/src/couchdb/priv/couchspawnkillable.sh b/src/couchdb/priv/couchspawnkillable.sh
new file mode 100755
index 00000000..f8d042e3
--- /dev/null
+++ b/src/couchdb/priv/couchspawnkillable.sh
@@ -0,0 +1,20 @@
+#! /bin/sh -e
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# The purpose of this script is to echo an OS specific command before launching
+# the actual process. This provides a way for Erlang to hard-kill its external
+# processes.
+
+echo "kill -9 $$"
+exec $*
diff --git a/src/couchdb/priv/couchspawnkillable_win.c b/src/couchdb/priv/couchspawnkillable_win.c
new file mode 100644
index 00000000..f812711e
--- /dev/null
+++ b/src/couchdb/priv/couchspawnkillable_win.c
@@ -0,0 +1,139 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License.  You may obtain a copy of
+// the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Do what 2 lines of shell script in couchspawnkillable does...
+// * Create a new suspended process with the same (duplicated) standard 
+//   handles as us.
+// * Write a line to stdout, consisting of the path to ourselves, plus
+//   '--kill {pid}' where {pid} is the PID of the newly created process.
+// * Un-suspend the new process.
+// * Terminate
+
+// Later, couch will call us with --kill and the PID, so we dutifully
+// terminate the specified PID.
+
+#include <stdlib.h>
+#include "windows.h"
+
+char *get_child_cmdline(int argc, char **argv)
+{
+    // make a new command-line, but skipping me.
+    // XXX - todo - spaces etc in args???
+    int i;
+    char *p, *cmdline;
+    int nchars = 0;
+    int nthis = 1;
+    for (i=1;i<argc;i++)
+        nchars += strlen(argv[i])+1;
+    cmdline = p = malloc(nchars+1);
+    if (!cmdline)
+        return NULL;
+    for (i=1;i<argc;i++) {
+        nthis = strlen(argv[i]);
+        strncpy(p, argv[i], nthis);
+        p[nthis] = ' ';
+        p += nthis+1;
+    }
+    // Replace the last space we added above with a '\0'
+    cmdline[nchars-1] = '\0';
+    return cmdline;
+}
+
+// create the child process, returning 0, or the exit-code we will
+// terminate with.
+int create_child(int argc, char **argv, PROCESS_INFORMATION *pi)
+{
+    char buf[1024];
+    DWORD dwcreate;
+    STARTUPINFO si;
+    char *cmdline;
+    if (argc < 2)
+        return 1;
+    cmdline = get_child_cmdline(argc, argv);
+    if (!cmdline)
+        return 2;
+
+    memset(&si, 0, sizeof(si));
+    si.cb = sizeof(si);
+    // depending on how *our* parent is started, we may or may not have
+    // a valid stderr stream - so although we try and duplicate it, only
+    // failing to duplicate stdin and stdout are considered fatal.
+    if (!DuplicateHandle(GetCurrentProcess(),
+                       GetStdHandle(STD_INPUT_HANDLE),
+                       GetCurrentProcess(),
+                       &si.hStdInput,
+                       0,
+                       TRUE, // inheritable
+                       DUPLICATE_SAME_ACCESS) ||
+       !DuplicateHandle(GetCurrentProcess(),
+                       GetStdHandle(STD_OUTPUT_HANDLE),
+                       GetCurrentProcess(),
+                       &si.hStdOutput,
+                       0,
+                       TRUE, // inheritable
+                       DUPLICATE_SAME_ACCESS)) {
+        return 3;
+    }
+    DuplicateHandle(GetCurrentProcess(),
+                   GetStdHandle(STD_ERROR_HANDLE),
+                   GetCurrentProcess(),
+                   &si.hStdError,
+                   0,
+                   TRUE, // inheritable
+                   DUPLICATE_SAME_ACCESS);
+
+    si.dwFlags = STARTF_USESTDHANDLES;
+    dwcreate = CREATE_SUSPENDED;
+    if (!CreateProcess( NULL, cmdline,
+                        NULL,
+                        NULL,
+                        TRUE, // inherit handles
+                        dwcreate,
+                        NULL, // environ
+                        NULL, // cwd
+                        &si,
+                        pi))
+        return 4;
+    return 0;
+}
+
+// and here we go...
+int main(int argc, char **argv)
+{
+    char out_buf[1024];
+    int rc;
+    DWORD cbwritten;
+    PROCESS_INFORMATION pi;
+    if (argc==3 && strcmp(argv[1], "--kill")==0) {
+        HANDLE h = OpenProcess(PROCESS_TERMINATE, 0, atoi(argv[2]));
+        if (!h)
+            return 1;
+        if (!TerminateProcess(h, 0))
+            return 2;
+        CloseHandle(h);
+        return 0;
+    }
+    // spawn the new suspended process
+    rc = create_child(argc, argv, &pi);
+    if (rc)
+        return rc;
+    // Write the 'terminate' command, which includes this PID, back to couch.
+    // *sob* - what about spaces etc?
+    sprintf_s(out_buf, sizeof(out_buf), "%s --kill %d\n", 
+              argv[0], pi.dwProcessId);
+    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), out_buf, strlen(out_buf), 
+              &cbwritten, NULL);
+    // Let the child process go...
+    ResumeThread(pi.hThread);
+    // and that is all - we can die...
+    return 0;
+}
-- 
cgit v1.2.3