[ioquake3] 79/136: Working Windows port of the autoupdater!

Simon McVittie smcv at debian.org
Thu Jun 15 09:09:12 UTC 2017


This is an automated email from the git hooks/post-receive script.

smcv pushed a commit to branch debian/master
in repository ioquake3.

commit 82977da9c86555ea7bab06330e91b74108d88edf
Author: Ryan C. Gordon <icculus at icculus.org>
Date:   Fri Jun 2 00:49:42 2017 -0400

    Working Windows port of the autoupdater!
---
 Makefile                       |  14 +-
 code/autoupdater/autoupdater.c | 378 +++++++++++++++++++++++++++++++----------
 code/sys/sys_autoupdater.c     |   9 +-
 3 files changed, 304 insertions(+), 97 deletions(-)

diff --git a/Makefile b/Makefile
index 964d3a7..7b16040 100644
--- a/Makefile
+++ b/Makefile
@@ -592,6 +592,8 @@ ifdef MINGW
   endif
 
   LIBS= -lws2_32 -lwinmm -lpsapi
+  AUTOUPDATER_LIBS += -lwininet
+
   # clang 3.4 doesn't support this
   ifneq ("$(CC)", $(findstring "$(CC)", "clang" "clang++"))
     CLIENT_LDFLAGS += -mwindows
@@ -985,7 +987,12 @@ ifneq ($(BUILD_GAME_QVM),0)
 endif
 
 ifneq ($(BUILD_AUTOUPDATER),0)
-  AUTOUPDATER_BIN := autoupdater$(FULLBINEXT)
+  # PLEASE NOTE that if you run an exe on Windows Vista or later
+  #  with "setup", "install", "update" or other related terms, it
+  #  will unconditionally trigger a UAC prompt, and in the case of
+  #  ioq3 calling CreateProcess() on it, it'll just fail immediately.
+  #  So don't call this thing "autoupdater" here!
+  AUTOUPDATER_BIN := autosyncerator$(FULLBINEXT)
   TARGETS += $(B)/$(AUTOUPDATER_BIN)
 endif
 
@@ -1325,6 +1332,9 @@ endif
 	@echo "  CLIENT_LIBS:"
 	$(call print_wrapped, $(CLIENT_LIBS))
 	@echo ""
+	@echo "  AUTOUPDATER_LIBS:"
+	$(call print_wrapped, $(AUTOUPDATER_LIBS))
+	@echo ""
 	@echo "  Output:"
 	$(call print_list, $(NAKED_TARGETS))
 	@echo ""
@@ -1588,7 +1598,7 @@ $(B)/autoupdater/%.o: $(AUTOUPDATERSRCDIR)/%.c
 
 $(B)/$(AUTOUPDATER_BIN): $(Q3AUTOUPDATEROBJ)
 	$(echo_cmd) "AUTOUPDATER_LD $@"
-	$(Q)$(CC) $(LDFLAGS) -o $@ $(Q3AUTOUPDATEROBJ) $(LIBS)
+	$(Q)$(CC) $(LDFLAGS) -o $@ $(Q3AUTOUPDATEROBJ) $(AUTOUPDATER_LIBS)
 
 
 #############################################################################
diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c
index 2576a71..ed631e3 100644
--- a/code/autoupdater/autoupdater.c
+++ b/code/autoupdater/autoupdater.c
@@ -6,16 +6,37 @@ is licensed until the GPLv2. Do not mingle code, please!
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdint.h>
 #include <stdarg.h>
 
+#ifdef _MSC_VER
+typedef __int64 int64_t;
+#else
+#include <stdint.h>
+#endif
+
+#include <time.h>
 #include <unistd.h>
 #include <sys/stat.h>
-#include <signal.h>
 
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+#include <wininet.h>
+#include <tlhelp32.h>
+#define PIDFMT "%u"
+#define PIDFMTCAST unsigned int
+typedef DWORD PID;
+#else
+#include <signal.h>
 #include <curl/curl.h>
+typedef pid_t PID;
+#define PIDFMT "%llu"
+#define PIDFMTCAST unsigned long long
+#endif
+
 #include "sha256.h"
 
+
 #ifndef AUTOUPDATE_USER_AGENT
 #define AUTOUPDATE_USER_AGENT "ioq3autoupdater/0.1"
 #endif
@@ -35,6 +56,8 @@ is licensed until the GPLv2. Do not mingle code, please!
 #define AUTOUPDATE_PLATFORM "mac"
 #elif defined(__linux__)
 #define AUTOUPDATE_PLATFORM "linux"
+#elif defined(_WIN32)
+#define AUTOUPDATE_PLATFORM "windows"
 #else
 #error Please define your platform.
 #endif
@@ -154,7 +177,146 @@ static void restoreRollbacks(void)
 static void die(const char *why) NEVER_RETURNS;
 
 
-#ifndef _WIN32  /* hooray for Unix linker hostility! */
+
+#ifdef _WIN32
+
+#define chmod(a,b) do {} while (0)
+#define makeDir(path) mkdir(path)
+
+static void windowsWaitForProcessToDie(const DWORD pid)
+{
+    HANDLE h;
+    infof("Waiting on process ID #%u", (unsigned int) pid);
+    h = OpenProcess(SYNCHRONIZE, FALSE, pid);
+    if (!h) {
+// !!! FIXME: what does this return if process is already dead?  
+        die("OpenProcess failed");
+    }
+    if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) {
+        die("WaitForSingleObject failed");
+    }
+    CloseHandle(h);
+}
+
+static void launchProcess(const char *exe, ...)
+{
+     PROCESS_INFORMATION procinfo;
+     STARTUPINFO startinfo;
+     va_list ap;
+     char cmdline[1024];
+     char *ptr = cmdline;
+     size_t totallen = 0;
+     const char *arg = NULL;
+
+     #define APPENDCMDLINE(str) { \
+        const size_t len = strlen(str); \
+        totallen += len; \
+        if ((totallen + 1) < sizeof (cmdline)) { \
+            strcpy(ptr, str); \
+            ptr += len; \
+        } \
+     }
+
+     va_start(ap, exe);
+     APPENDCMDLINE(exe);
+     while ((arg = va_arg(ap, const char *)) != NULL) {
+         APPENDCMDLINE(arg);
+     }
+     va_end(ap);
+
+     if (totallen >= sizeof (cmdline)) {
+        die("command line too long to launch.");
+     }
+
+     cmdline[totallen] = 0;
+
+     infof("launching process '%s' with cmdline '%s'", exe, cmdline);
+
+     memset(&procinfo, '\0', sizeof (procinfo));
+     memset(&startinfo, '\0', sizeof (startinfo));
+     startinfo.cb = sizeof (startinfo);
+     if (CreateProcessA(exe, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startinfo, &procinfo))
+     {
+         CloseHandle(procinfo.hProcess);
+         CloseHandle(procinfo.hThread);
+         exit(0);  /* we're done, it's launched. */
+     }
+
+     infof("CreateProcess failed: err=%d", (int) GetLastError());
+}
+
+static HINTERNET hInternet;
+static void prepHttpLib(void)
+{
+    hInternet = InternetOpenA(AUTOUPDATE_USER_AGENT,
+                              INTERNET_OPEN_TYPE_PRECONFIG,
+                              NULL, NULL, 0);
+    if (!hInternet) {
+        die("InternetOpen failed");
+    }
+}
+
+static void shutdownHttpLib(void)
+{
+    if (hInternet) {
+        InternetCloseHandle(hInternet);
+        hInternet = NULL;
+    }
+}
+
+static int runHttpDownload(const char *from, FILE *to)
+{
+    /* !!! FIXME: some of this could benefit from GetLastError+FormatMessage. */
+    int retval = 0;
+    DWORD httpcode = 0;
+    DWORD dwordlen = sizeof (DWORD);
+    DWORD zero = 0;
+    HINTERNET hUrl = InternetOpenUrlA(hInternet, from, NULL, 0,
+                                INTERNET_FLAG_HYPERLINK |
+                                INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
+                                INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS |
+                                INTERNET_FLAG_NO_CACHE_WRITE |
+                                INTERNET_FLAG_NO_COOKIES |
+                                INTERNET_FLAG_NO_UI |
+                                INTERNET_FLAG_RESYNCHRONIZE |
+                                INTERNET_FLAG_RELOAD |
+                                INTERNET_FLAG_SECURE, 0);
+
+    if (!hUrl) {
+        infof("InternetOpenUrl failed. err=%d", (int) GetLastError());
+    } else if (!HttpQueryInfo(hUrl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &httpcode, &dwordlen, &zero)) {
+        infof("HttpQueryInfo failed. err=%d", (int) GetLastError());
+    } else if (httpcode != 200) {
+        infof("HTTP request failed with response code %d", (int) httpcode);
+    } else {
+        while (1) {
+            DWORD br = 0;
+            BYTE buf[1024 * 64];
+            if (!InternetReadFile(hUrl, buf, sizeof (buf), &br)) {
+                infof("InternetReadFile failed. err=%d", (int) GetLastError());
+                break;
+            } else if (br == 0) {
+                retval = 1;
+                break;  /* done! */
+            } else {
+                if (fwrite(buf, br, 1, to) != 1) {
+                    info("fwrite failed");
+                    break;
+                }
+            }
+        }
+    }
+
+    InternetCloseHandle(hUrl);
+    return retval;
+}
+
+#else  /* Everything that isn't Windows. */
+
+#define launchProcess execl
+#define makeDir(path) mkdir(path, 0777)
+
+/* hooray for Unix linker hostility! */
 #undef curl_easy_setopt
 #include <dlfcn.h>
 typedef void (*CURLFN_curl_easy_cleanup)(CURL *curl);
@@ -171,7 +333,7 @@ static CURLFN_curl_easy_perform CURL_curl_easy_perform;
 static CURLFN_curl_global_init CURL_curl_global_init;
 static CURLFN_curl_global_cleanup CURL_curl_global_cleanup;
 
-static void load_libcurl(void)
+static void prepHttpLib(void)
 {
     #ifdef __APPLE__
     const char *libname = "libcurl.4.dylib";
@@ -195,21 +357,69 @@ static void load_libcurl(void)
     LOADCURLSYM(curl_easy_perform);
     LOADCURLSYM(curl_global_init);
     LOADCURLSYM(curl_global_cleanup);
+
+    #define curl_easy_cleanup CURL_curl_easy_cleanup
+    #define curl_easy_init CURL_curl_easy_init
+    #define curl_easy_setopt CURL_curl_easy_setopt
+    #define curl_easy_perform CURL_curl_easy_perform
+    #define curl_global_init CURL_curl_global_init
+    #define curl_global_cleanup CURL_curl_global_cleanup
+
+    if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
+        die("curl_global_init() failed!");
+    }
+}
+
+static void shutdownHttpLib(void)
+{
+    if (curl_global_cleanup) {
+        curl_global_cleanup();
+    }
 }
 
-#define curl_easy_cleanup CURL_curl_easy_cleanup
-#define curl_easy_init CURL_curl_easy_init
-#define curl_easy_setopt CURL_curl_easy_setopt
-#define curl_easy_perform CURL_curl_easy_perform
-#define curl_global_init CURL_curl_global_init
-#define curl_global_cleanup CURL_curl_global_cleanup
+static int runHttpDownload(const char *from, FILE *to)
+{
+    int retval;
+    CURL *curl = curl_easy_init();
+    if (!curl) {
+        info("curl_easy_init() failed");
+        return 0;
+    }
+
+    #if 0
+    /* !!! FIXME: enable compression? */
+    curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");  /* enable compression */
+
+    /* !!! FIXME; hook up proxy support to libcurl */
+    curl_easy_setopt(curl, CURLOPT_PROXY, proxyURL);
+    #endif
+
+    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
+    curl_easy_setopt(curl, CURLOPT_STDERR, logfile);
+
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, to);
+
+    curl_easy_setopt(curl, CURLOPT_URL, from);
+
+    curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
+    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);  /* allow redirects. */
+    curl_easy_setopt(curl, CURLOPT_USERAGENT, AUTOUPDATE_USER_AGENT);
+
+    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);  /* require valid SSL cert. */
+    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);  /* require SSL cert with same hostname as we connected to. */
+
+    retval = (curl_easy_perform(curl) == CURLE_OK);
+    curl_easy_cleanup(curl);
+    return retval;
+}
 #endif
 
 
 static void die(const char *why)
 {
     infof("FAILURE: %s", why);
-    curl_global_cleanup();
     restoreRollbacks();
     freeManifest();
     infof("Updater ending (in failure), %s", timestamp());
@@ -222,12 +432,6 @@ static void outOfMemory(void)
     die("Out of memory");
 }
 
-static void makeDir(const char *dirname)
-{
-    /* !!! FIXME: we don't care if this fails right now. */
-    mkdir(dirname, 0777);
-}
-
 static void buildParentDirs(const char *_path)
 {
     char *ptr;
@@ -267,7 +471,7 @@ static void parseArgv(int argc, char **argv)
     for (i = 1; i < argc; i += 2) {
         if (strcmp(argv[i], "--waitpid") == 0) {
             options.waitforprocess = atoll(argv[i + 1]);
-            infof("We will wait for process %lld if necessary", (long long) options.waitforprocess);
+            infof("We will wait for process " PIDFMT " if necessary", (PIDFMTCAST) options.waitforprocess);
         } else if (strcmp(argv[i], "--updateself") == 0) {
             options.updateself = argv[i + 1];
             infof("We are updating ourself ('%s')", options.updateself);
@@ -275,57 +479,17 @@ static void parseArgv(int argc, char **argv)
     }
 }
 
-static CURL *prepCurl(const char *url, FILE *outfile)
+static void downloadURL(const char *from, const char *to)
 {
-    char *fullurl;
-    const size_t len = strlen(AUTOUPDATE_URL) + strlen(url) + 1;
-    CURL *curl = curl_easy_init();
-    if (!curl) {
-        die("curl_easy_init() failed");
-    }
-
-    fullurl = (char *) alloca(len);
+    FILE *io = NULL;
+    const size_t len = strlen(AUTOUPDATE_URL) + strlen(from) + 1;
+    char *fullurl = (char *) alloca(len);
     if (!fullurl) {
         outOfMemory();
     }
+    snprintf(fullurl, len, "%s%s", AUTOUPDATE_URL, from);
 
-    snprintf(fullurl, len, "%s%s", AUTOUPDATE_URL, url);
-
-    infof("Downloading from '%s'", fullurl);
-
-    #if 0
-    /* !!! FIXME: enable compression? */
-    curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");  /* enable compression */
-
-    /* !!! FIXME; hook up proxy support to libcurl */
-    curl_easy_setopt(curl, CURLOPT_PROXY, proxyURL);
-    #endif
-
-    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
-    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
-    curl_easy_setopt(curl, CURLOPT_STDERR, logfile);
-
-    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
-    curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
-
-    curl_easy_setopt(curl, CURLOPT_URL, fullurl);
-
-    curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
-    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);  /* allow redirects. */
-    curl_easy_setopt(curl, CURLOPT_USERAGENT, AUTOUPDATE_USER_AGENT);
-
-    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);  /* require valid SSL cert. */
-    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);  /* require SSL cert with same hostname as we connected to. */
-
-    return curl;
-}
-
-static void downloadURL(const char *from, const char *to)
-{
-    FILE *io;
-    CURL *curl;
-
-    infof("Preparing to download to '%s'", to);
+    infof("Downloading from '%s' to '%s'", fullurl, to);
 
     buildParentDirs(to);
     io = fopen(to, "wb");
@@ -333,12 +497,11 @@ static void downloadURL(const char *from, const char *to)
         die("Failed to open output file");
     }
 
-    curl = prepCurl(from, io);
-    if (curl_easy_perform(curl) != CURLE_OK) {
+    if (!runHttpDownload(fullurl, io)) {
+        fclose(io);
         remove(to);
         die("Download failed");
     }
-    curl_easy_cleanup(curl);
 
     if (fclose(io) == EOF) {
         die("Can't flush file on close. i/o error? Disk full?");
@@ -361,7 +524,7 @@ static int hexcvt(const int ch)
     return 0;
 }
 
-static void convertSha256(char *str, BYTE *sha256)
+static void convertSha256(char *str, uint8 *sha256)
 {
     int i;
     for (i = 0; i < 32; i++) {
@@ -446,6 +609,32 @@ static void upgradeSelfAndRestart(const char *argv0)
     FILE *in = NULL;
     FILE *out = NULL;
 
+    /* unix replaces the process with execl(), but Windows needs to wait for the parent to terminate. */
+    #ifdef _WIN32
+    DWORD ppid = 0;
+    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+    if (h) {
+        const DWORD myPid = GetCurrentProcessId();
+        PROCESSENTRY32 pe;
+        memset(&pe, '\0', sizeof (pe));
+        pe.dwSize = sizeof(PROCESSENTRY32);
+
+        if (Process32First(h, &pe)) {
+            do {
+                if (pe.th32ProcessID == myPid) {
+                    ppid = pe.th32ParentProcessID;
+                    break;
+                }
+            } while (Process32Next(h, &pe));
+        }
+        CloseHandle(h);
+    }
+    if (!ppid) {
+        die("Can't determine parent process id");
+    }
+    windowsWaitForProcessToDie(ppid);
+    #endif
+
     in = fopen(argv0, "rb");
     if (!in) {
         die("Can't open self for input while upgrading updater");
@@ -491,10 +680,10 @@ static void upgradeSelfAndRestart(const char *argv0)
 
     if (options.waitforprocess) {
         char pidstr[64];
-        snprintf(pidstr, sizeof (pidstr), "%lld", (long long) options.waitforprocess);
-        execl(options.updateself, options.updateself, "--waitpid", pidstr, NULL);
+        snprintf(pidstr, sizeof (pidstr), PIDFMT, (PIDFMTCAST) options.waitforprocess);
+        launchProcess(options.updateself, options.updateself, "--waitpid", pidstr, NULL);
     } else {
-        execl(options.updateself, options.updateself, NULL);
+        launchProcess(options.updateself, options.updateself, NULL);
     }
     die("Failed to relaunch upgraded updater");
 }
@@ -508,7 +697,7 @@ static const char *justFilename(const char *path)
 static void hashFile(const char *fname, unsigned char *sha256)
 {
     SHA256_CTX sha256ctx;
-    BYTE buf[512];
+    uint8 buf[512];
     FILE *io;
 
     io = fopen(fname, "rb");
@@ -611,10 +800,10 @@ static void maybeUpdateSelf(const char *argv0)
 
                 if (options.waitforprocess) {
                     char pidstr[64];
-                    snprintf(pidstr, sizeof (pidstr), "%lld", (long long) options.waitforprocess);
-                    execl(to, to, "--updateself", argv0, "--waitpid", pidstr, NULL);
+                    snprintf(pidstr, sizeof (pidstr), PIDFMT, (PIDFMTCAST) options.waitforprocess);
+                    launchProcess(to, to, "--updateself", argv0, "--waitpid", pidstr, NULL);
                 } else {
-                    execl(to, to, "--updateself", argv0, NULL);
+                    launchProcess(to, to, "--updateself", argv0, NULL);
                 }
                 die("Failed to initially launch upgraded updater");
             }
@@ -668,19 +857,26 @@ static void applyUpdates(void)
     }
 }
 
+
 static void waitToApplyUpdates(void)
 {
     if (options.waitforprocess) {
-        /* ioquake3 opens a pipe on fd 3, and then forgets about it. We block
-           on a read to that pipe here. When the game process quits (and the
-           OS forcibly closes the pipe), we will unblock. Then we can loop on
-           kill() until the process is truly gone. */
-        int x = 0;
-        infof("Waiting for pid %lld to die...", (long long) options.waitforprocess);
-        read(3, &x, sizeof (x));
-        info("Pipe has closed, waiting for process to fully go away now.");
-        while (kill(options.waitforprocess, 0) == 0) {
-            usleep(100000);
+        infof("Waiting for pid " PIDFMT " to die...", (PIDFMTCAST) options.waitforprocess);
+        {
+            #ifdef _WIN32
+            windowsWaitForProcessToDie(options.waitforprocess);
+            #else
+            /* The parent opens a pipe on fd 3, and then forgets about it. We block
+               on a read to that pipe here. When the game process quits (and the
+               OS forcibly closes the pipe), we will unblock. Then we can loop on
+               kill() until the process is truly gone. */
+            int x = 0;
+            read(3, &x, sizeof (x));
+            info("Pipe has closed, waiting for process to fully go away now.");
+            while (kill(options.waitforprocess, 0) == 0) {
+                usleep(100000);
+            }
+            #endif
         }
         info("pid is gone, continuing");
     }
@@ -725,7 +921,9 @@ static void chdirToBasePath(const char *argv0)
 
 int main(int argc, char **argv)
 {
+    #ifndef _WIN32
     signal(SIGPIPE, SIG_IGN);  /* don't trigger signal when fd3 closes */
+    #endif
 
     logfile = stdout;
     chdirToBasePath(argv[0]);
@@ -749,13 +947,7 @@ int main(int argc, char **argv)
         upgradeSelfAndRestart(argv[0]);
     }
 
-    #ifndef _WIN32
-    load_libcurl();
-    #endif
-
-    if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
-        die("curl_global_init() failed!");
-    }
+    prepHttpLib();
 
     downloadManifest();  /* see if we need an update at all. */
 
@@ -771,7 +963,7 @@ int main(int argc, char **argv)
     }
 
     freeManifest();
-    curl_global_cleanup();
+    shutdownHttpLib();
 
     infof("Updater ending, %s", timestamp());
 
diff --git a/code/sys/sys_autoupdater.c b/code/sys/sys_autoupdater.c
index 56873d4..2982f85 100644
--- a/code/sys/sys_autoupdater.c
+++ b/code/sys/sys_autoupdater.c
@@ -26,9 +26,14 @@ void Sys_LaunchAutoupdater(int argc, char **argv)
 	{
 		/* We don't need the Unix pipe() tapdance here because Windows lets children wait on parent processes. */
 		PROCESS_INFORMATION procinfo;
+		STARTUPINFO startinfo;
 		char cmdline[128];
-		sprintf(cmdline, AUTOUPDATER_BIN " --waitpid %u", (unsigned int) GetCurrentProcessId());
-		if (CreateProcessA(AUTOUPDATER_BIN, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &procinfo))
+		memset(&procinfo, '\0', sizeof (procinfo));
+		memset(&startinfo, '\0', sizeof (startinfo));
+		startinfo.cb = sizeof (startinfo);
+		sprintf(cmdline, "" AUTOUPDATER_BIN " --waitpid %u", (unsigned int) GetCurrentProcessId());
+
+		if (CreateProcessA(AUTOUPDATER_BIN, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startinfo, &procinfo))
 		{
 			/* close handles now so child cleans up immediately if nothing to do */
 			CloseHandle(procinfo.hProcess);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/ioquake3.git



More information about the Pkg-games-commits mailing list