diff options
| author | Paul Joseph Davis <davisp@apache.org> | 2009-08-18 01:27:03 +0000 | 
|---|---|---|
| committer | Paul Joseph Davis <davisp@apache.org> | 2009-08-18 01:27:03 +0000 | 
| commit | 39a629b0d63ca675bd24844c924203d30bd3ba9b (patch) | |
| tree | 3071af152be6485e53853551fa8c6e967b5e31ba | |
| parent | f723cb99008f2462abec46ab9b614fb8dd2f31d1 (diff) | |
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
| -rw-r--r-- | README | 141 | ||||
| -rw-r--r-- | bin/Makefile.am | 10 | ||||
| -rw-r--r-- | bin/couchdb.bat.tpl.in | 14 | ||||
| -rw-r--r-- | configure.ac | 89 | ||||
| -rw-r--r-- | etc/couchdb/Makefile.am | 24 | ||||
| -rw-r--r-- | src/couchdb/Makefile.am | 50 | ||||
| -rwxr-xr-x | src/couchdb/priv/couchspawnkillable.sh (renamed from src/couchdb/priv/couchspawnkillable) | 0 | ||||
| -rw-r--r-- | src/couchdb/priv/couchspawnkillable_win.c | 139 | 
8 files changed, 438 insertions, 29 deletions
| @@ -261,4 +261,143 @@ If you want the Apache CouchDB daemon to run at startup, copy the  Windows  ~~~~~~~ -Windows documentation is incomplete. Please submit suggestions. +The Windows build process is very similar to the erlang build process; +indeed, we re-use some of their shell-scripts.  Therefore, it is recommended +you build erlang itself from sources - this will ensure that you have all the +tools and environment necessary to build couch itself.  A binary build of +erlang should work for those in a hurry (see below), but it isn't really +supported.  See the end of these notes or information on building erlang +which is relevant to couch. + +Build Tools +----------- + +To build on Windows, you need the following tools: + +* cygwin - it isn't clear exactly which tools you need - select all +  'development' tools.  As mentioned above, if you can build erlang itself +  you have everything you need. + +* VS2008 + +* Erlang - a built source distro of erlang is preferred.  A binary +  distribution of Erlang is OK, but you will also need a source distribution +  for the cc.sh/link.sh etc wrapper shell scripts used by erlang - couch +  reuses these scripts in its build process and the license isn't compatible +  enough for us to clone them. + +Other tools: + +* Fetch the 'curl' sources and build them as per the instructions.  The cygwin +  curl binaries are built with a different compiler so are no good. + +* Download the ICU binaries built with VS2008. + +* Download and build the same version of spidermonkey the version of couch +  requires - at time of writing this is 1.8.  Different versions will not +  work (ie, at time of writing, 1.8.1 does not work with couch). + +Build Environment +----------------- + +Setup your environment: + +For the sake of everything you find sacred: +  set CYGWIN=nontsec +BEFORE starting any cygwin environments.  Set this variable globally.  Without +it you can expect all kinds of permissions-related problems. + +Execute the VC .bat files to setup your environment such that cl.exe etc are +on your path.  Start a cygwin shell. + +Check your environment: +  * which link -> should point at the MS linker. +  * which cl -> should point at the MS compiler. + +If you are building from a source version of erlang: + +  Executing  "eval `./otp_build env_win32`" as per the Erlang build +  instructions, will have set everything up, including the CC, LD etc +  variables.  Do this even if erlang is already built and you are just +  building couch.  This will have set the variables ERL_TOP, CC, LD, AD +  and a number of others. + +  Then, PATH must be adjusted such what 'which erl' lists the erl.exe +  you built rather than the 'erl' script in the erts/etc/win32/cygwin_tools +  directory - eg: + +  $ export PATH=$ERL_TOP/release/win32/erts-5.7.2/bin:$PATH + +If you are building from a binary version of erlang: + +  * set ERL_TOP to the top of the erlang directory + +  Add to the PATH such that 'which erl' points at your erl.exe - eg: + +  $ export PATH=$ERL_TOP/erts-5.6.5/bin + +  You must also ensure the cc.sh etc scripts from the Erlang source tree is +  on your PATH.  Use "which cc.sh" to ensure the path is set correctly. + +  Then set more variables: +    $ export CC=cc.sh +    $ export LD=ld.sh +    $ export AR=ar.sh +    $ export RC=rc.sh + +Set COUCHDB_TOP to the source directory. + +And we should be ready to bootstrap and build. + +Building +-------- + +We start by bootstrapping: + +  $ cd $COUCHDB_TOP +  $ ./bootstrap +  You have bootstrapped Apache CouchDB, time to relax. + +  Run `./configure' to configure the source before you install. +  $ + +Relax. + +Now we need to run a complicated configure command-line. + +  $ ./configure \ +      --with-js-include=/cygdrive/c/path_to_seamonkey_include \ +      --with-js-lib=/cygdrive/c/path_to_seamonkey_lib \ +      --with-win32-icu-binaries=/cygdrive/c/path_to_icu_binaries_root \ +      --with-erlang=$ERL_TOP/release/win32/usr/include \ +      --with-win32-curl=/cygdrive/c/path/to/curl/root/directory +      --prefix=$ERL_TOP/release/win32 + +Note that all paths must be in cygwin format.  Those starting with $ERL_TOP +can be entered literally, assuming ERL_TOP is set as described above. + +Relax on your new couch: + +  The $ERL_TOP/win32/release directory is now ready to .zip up, be packaged +  by an installer, etc.  To test it in-place, execute: +   +  $ $ERL_TOP/win32/release/bin/couchdb.bat +   +  and everything should work fine.   + +Additional Notes: + +Building erlang:  +* Follow the instructions as described.  You do need openssl, but don't need +  the GUI tools.  You may like to execute: + +    echo "skipping gs" > lib/gs/SKIP +    echo "skipping ic" > lib/ic/SKIP + +  To skip them. + +* Ensure 'which link' points at the ms linker; one in /usr/bin may be found +  instead. + +* After executing './otp_build release -a', be sure to execute Install.exe in +  the release/win32 directory to setup the release/win32/bin dir correctly. diff --git a/bin/Makefile.am b/bin/Makefile.am index cb0a827a..93a53192 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -10,7 +10,12 @@  ## License for the specific language governing permissions and limitations under  ## the License. +if WINDOWS +bin_SCRIPTS = couchdb.bat +else  bin_SCRIPTS = couchdb couchjs +endif +  noinst_SCRIPTS = couchjs_dev  if HELP2MAN @@ -69,6 +74,11 @@ couchjs_dev: couchjs.tpl  	$@ < $<  	chmod +x $@ +couchdb.bat: couchdb.bat.tpl +	sed -e "s|%ICU_CONFIG%|$(ICU_CONFIG)|g" \ +	    -e "s|%version%|@version@|g" \ +	$< > $@ +  HELP2MAN_OPTION=--no-info --help-option="-h" --version-option="-V"  # XXX: Because the scripts are made at build time for the user we need to diff --git a/bin/couchdb.bat.tpl.in b/bin/couchdb.bat.tpl.in new file mode 100644 index 00000000..99c096f1 --- /dev/null +++ b/bin/couchdb.bat.tpl.in @@ -0,0 +1,14 @@ +@echo off +setlocal +rem First change to the erlang bin directory +cd %~dp0 + +rem Allow a different erlang executable (eg, werl) to be used. +if "%ERL%x" == "x" set ERL=erl.exe + +echo CouchDB %version% - prepare to relax... +%ERL% -smp auto -sasl errlog_type error ^ +      -eval "application:load(crypto)" ^ +      -eval "application:load(couch)" ^ +      -eval "crypto:start()" ^ +      -eval "couch_server:start([""../etc/couchdb/default.ini"", ""../etc/couchdb/local.ini""]), receive done -> done end." diff --git a/configure.ac b/configure.ac index c7b766f2..a281a868 100644 --- a/configure.ac +++ b/configure.ac @@ -80,17 +80,36 @@ AC_ARG_VAR([FLAGS], [general flags to prepend to LDFLAGS and CPPFLAGS])  LIB_FLAGS="$JS_LIB_FLAGS -L/usr/local/lib -L/opt/local/lib"  LIBS="$LIB_FLAGS $LIBS" -# XP_UNIX required for jsapi.h and has been tested to work on Linux and Darwin. -FLAGS="$LIB_FLAGS $ERLANG_FLAGS $JS_FLAGS -DXP_UNIX $FLAGS" -CPPFLAGS="$FLAGS $CPPFLAGS" -# manually linking libm is requred for FreeBSD 7.0 -LDFLAGS="$FLAGS -lm $LDFLAGS" + +case "$(uname -s)" in +  CYGWIN*) +    FLAGS="$LIB_FLAGS $ERLANG_FLAGS $JS_FLAGS -DXP_WIN $FLAGS" +    CPPFLAGS="$FLAGS $CPPFLAGS" +    LDFLAGS="$FLAGS $LDFLAGS" +    IS_WINDOWS="TRUE" +    # The erlang cc.sh/ld.sh scripts will convert a -O option +    # into the same optimization flags erlang itself uses. +    CFLAGS="-O2" +    LTCFLAGS="$CFLAGS" +    ;; +  *) +    # XP_UNIX required for jsapi.h and has been tested to work on Linux and Darwin. +    FLAGS="$LIB_FLAGS $ERLANG_FLAGS $JS_FLAGS -DXP_UNIX $FLAGS" +    CPPFLAGS="$FLAGS $CPPFLAGS" +    # manually linking libm is requred for FreeBSD 7.0 +    LDFLAGS="$FLAGS -lm $LDFLAGS" +    ;; +esac + +AM_CONDITIONAL([WINDOWS], [test x$IS_WINDOWS = xTRUE])  AC_CHECK_LIB([mozjs], [JS_NewContext], [], [      AC_CHECK_LIB([js], [JS_NewContext], [], [ -        AC_MSG_ERROR([Could not find the js library. +        AC_CHECK_LIB([js3250], [JS_NewContext], [], [ +            AC_CHECK_LIB([js32], [JS_NewContext], [], [ +                AC_MSG_ERROR([Could not find the js library. -Is the Mozilla SpiderMonkey library installed?])])]) +Is the Mozilla SpiderMonkey library installed?])])])])])  AC_CHECK_HEADER([jsapi.h], [], [      AC_CHECK_HEADER([js/jsapi.h], @@ -116,18 +135,39 @@ AC_COMPILE_IFELSE(  CFLAGS="$OLD_CFLAGS"  AC_LANG_POP(C) -AC_CHECK_ICU([3]) +AC_ARG_WITH([win32-icu-binaries], [AC_HELP_STRING([--with-win32-icu-binaries=PATH], +    [set PATH to the Win32 native ICU binaries directory])], [ +    ICU_CONFIG="" # supposed to be a command to query options... +    ICU_LOCAL_CFLAGS="-I$withval/include" +    ICU_LOCAL_LDFLAGS="-L$withval/lib" +    ICU_LOCAL_BIN=$withval/bin +], [ +    AC_CHECK_ICU([3]) +    ICU_LOCAL_CFLAGS=`$ICU_CONFIG --cppflags-searchpath` +    ICU_LOCAL_LDFLAGS=`$ICU_CONFIG --ldflags-searchpath` +    ICU_LOCAL_BIN= +]) -ICU_LOCAL_CFLAGS=`$ICU_CONFIG --cppflags-searchpath` -ICU_LOCAL_LDFLAGS=`$ICU_CONFIG --ldflags-searchpath`  AC_SUBST(ICU_CONFIG)  AC_SUBST(ICU_LOCAL_CFLAGS)  AC_SUBST(ICU_LOCAL_LDFLAGS) +AC_SUBST(ICU_LOCAL_BIN) + +AC_ARG_WITH([win32-curl], [AC_HELP_STRING([--with-win32-curl=PATH], +    [set PATH to the Win32 native curl directory])], [ +    # default build on windows is a static lib, and that's what we want too +    CURL_CFLAGS="-I$withval/include -DCURL_STATICLIB" +    CURL_LIBS="$withval/lib/libcurl" +    CURL_LDFLAGS="-l$CURL_LIBS -lWs2_32 -lkernel32 -luser32 -ladvapi32 -lWldap32" +], [ +    AC_CHECK_CURL([7.15.5]) +    CURL_LDFLAGS=-lcurl +]) -AC_CHECK_CURL([7.15.5])  AC_SUBST(CURL_CFLAGS)  AC_SUBST(CURL_LIBS) +AC_SUBST(CURL_LDFLAGS)  case "$(uname -s)" in    Linux) @@ -268,8 +308,15 @@ AC_SUBST([locallibdir], [${libdir}/${package_identifier}])  AC_SUBST([localstatelibdir], [${localstatedir}/lib/${package_identifier}])  AC_SUBST([localstatelogdir], [${localstatedir}/log/${package_identifier}])  AC_SUBST([localstaterundir], [${localstatedir}/run/${package_identifier}]) -AC_SUBST([locallibbindir], [${locallibdir}/bin]) -AC_SUBST([localerlanglibdir], [${locallibdir}/erlang/lib]) + +# On Windows we install directly into our erlang distribution. +if test x${IS_WINDOWS} = xTRUE; then +    AC_SUBST([locallibbindir], [${prefix}/bin]) +    AC_SUBST([localerlanglibdir], [${libdir}]) +else +    AC_SUBST([locallibbindir], [${locallibdir}/bin]) +    AC_SUBST([localerlanglibdir], [${locallibdir}/erlang/lib]) +fi  # fix for older autotools that don't define "abs_top_YYY" by default  AC_SUBST(abs_top_srcdir) @@ -280,6 +327,7 @@ AC_REVISION([LOCAL_VERSION])  AC_CONFIG_FILES([Makefile])  AC_CONFIG_FILES([bin/couchjs.tpl])  AC_CONFIG_FILES([bin/couchdb.tpl]) +AC_CONFIG_FILES([bin/couchdb.bat.tpl])  AC_CONFIG_FILES([bin/Makefile])  AC_CONFIG_FILES([etc/couchdb/Makefile])  AC_CONFIG_FILES([etc/couchdb/default.ini.tpl]) @@ -304,6 +352,21 @@ AC_CONFIG_FILES([var/Makefile])  AC_OUTPUT +# *sob* - on Windows libtool fails as 'libname_spec' isn't correct (it +# expects GNU style lib names).  I can't work out how to configure this +# option sanely, so we pass the script through sed to modify it. +# Also, the erlang cc.sh script doesn't cope well with the '-link' command +# line option libtool provides. +# PLEASE, someone help put this out of its misery!! +# This hackery is being tracked via COUCHDB-440. +if test x${IS_WINDOWS} = xTRUE; then +    sed -e 's,libname_spec="lib\\$name",libname_spec="\\\$name",' \ +        -e 's,-link,,' \ +        < libtool > libtool.tmp +    mv libtool.tmp libtool +    # probably would chmod +x if we weren't on windows... +fi +  echo  echo "You have configured Apache CouchDB, time to relax."  echo diff --git a/etc/couchdb/Makefile.am b/etc/couchdb/Makefile.am index 70463f59..253634de 100644 --- a/etc/couchdb/Makefile.am +++ b/etc/couchdb/Makefile.am @@ -24,6 +24,17 @@ transform = @program_transform_name@  couchjs_command_name = `echo couchjs | sed '$(transform)'`  couchjs_dev_command_name = `echo couchjs_dev | sed '$(transform)'` +if WINDOWS +default.ini: default.ini.tpl +	sed -e "s|%bindir%|.|g" \ +	    -e "s|%localconfdir%|$(localconfdir)|g" \ +	    -e "s|%localdatadir%|../share/couchdb|g" \ +	    -e "s|%localstatelibdir%|../var/lib/couchdb|g" \ +	    -e "s|%localstatelogdir%|../var/log/couchdb|g" \ +	    -e "s|%couchprivlibdir%|../lib/couch-$(version)/priv/lib|g" \ +	    -e "s|%couchjs_command_name%|couchjs.exe|g" \ +	< $< > $@ +else  default.ini: default.ini.tpl  	sed -e "s|%bindir%|$(bindir)|g" \  	    -e "s|%localconfdir%|$(localconfdir)|g" \ @@ -33,6 +44,7 @@ default.ini: default.ini.tpl  	    -e "s|%couchprivlibdir%|$(couchprivlibdir)|g" \  	    -e "s|%couchjs_command_name%|$(couchjs_command_name)|g" \  	< $< > $@ +endif  default_dev.ini: default.ini.tpl  	sed -e "s|%bindir%|$(abs_top_srcdir)/bin|g" \ @@ -48,16 +60,16 @@ local_dev.ini:  	cp local.ini $@  install-data-hook: -	if test ! -f "$(DESTDIR)/$(localconfdir)/local.ini"; then \ -	    cp $(srcdir)/local.ini "$(DESTDIR)/$(localconfdir)/local.ini"; \ +	if test ! -f "$(DESTDIR)$(localconfdir)/local.ini"; then \ +	    cp $(srcdir)/local.ini "$(DESTDIR)$(localconfdir)/local.ini"; \  	fi  	if test ! "$(mkdir_p)" = ""; then \ -	    $(mkdir_p) "$(DESTDIR)/$(localconfdir)/default.d"; \ -	    $(mkdir_p) "$(DESTDIR)/$(localconfdir)/local.d"; \ +	    $(mkdir_p) "$(DESTDIR)$(localconfdir)/default.d"; \ +	    $(mkdir_p) "$(DESTDIR)$(localconfdir)/local.d"; \  	else \  	    echo "WARNING: You may have to create these directories by hand."; \ -	    mkdir -p "$(DESTDIR)/$(localconfdir)/default.d"; \ -	    mkdir -p "$(DESTDIR)/$(localconfdir)/local.d"; \ +	    mkdir -p "$(DESTDIR)$(localconfdir)/default.d"; \ +	    mkdir -p "$(DESTDIR)$(localconfdir)/local.d"; \  	fi  uninstall-local: 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.sh index f8d042e3..f8d042e3 100755 --- a/src/couchdb/priv/couchspawnkillable +++ b/src/couchdb/priv/couchspawnkillable.sh 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; +} | 
