From 5875fd0313cb0aeb7e63d5ea9455f83ea18787b5 Mon Sep 17 00:00:00 2001 From: Markus Mohrhard Date: Mon, 7 Aug 2017 15:35:30 +0200 Subject: updater: move the windows process starting code to comphelper Change-Id: I1a499f57d01ee28afdb2c4f85dc976f2e6837dfd Reviewed-on: https://gerrit.libreoffice.org/40837 Tested-by: Jenkins Reviewed-by: Markus Mohrhard --- comphelper/Module_comphelper.mk | 2 + comphelper/StaticLibrary_windows_process.mk | 24 +++ comphelper/source/windows/windows_process.cxx | 270 +++++++++++++++++++++++++ include/comphelper/windowsStart.hxx | 26 +++ onlineupdate/Executable_updater.mk | 2 +- onlineupdate/Module_onlineupdate.mk | 3 +- onlineupdate/StaticLibrary_winhelper.mk | 22 -- onlineupdate/inc/winhelper/windowsStart.hxx | 26 --- onlineupdate/source/update/updater/updater.cxx | 2 +- onlineupdate/source/winhelper/windowsStart.cxx | 270 ------------------------- 10 files changed, 325 insertions(+), 322 deletions(-) create mode 100644 comphelper/StaticLibrary_windows_process.mk create mode 100644 comphelper/source/windows/windows_process.cxx create mode 100644 include/comphelper/windowsStart.hxx delete mode 100644 onlineupdate/StaticLibrary_winhelper.mk delete mode 100644 onlineupdate/inc/winhelper/windowsStart.hxx delete mode 100644 onlineupdate/source/winhelper/windowsStart.cxx diff --git a/comphelper/Module_comphelper.mk b/comphelper/Module_comphelper.mk index e04d230d2069..30ac708a927d 100644 --- a/comphelper/Module_comphelper.mk +++ b/comphelper/Module_comphelper.mk @@ -21,6 +21,8 @@ $(eval $(call gb_Module_Module,comphelper)) $(eval $(call gb_Module_add_targets,comphelper,\ Library_comphelper \ + $(if $(filter WNT,$(OS)),\ + StaticLibrary_windows_process )\ )) $(eval $(call gb_Module_add_subsequentcheck_targets,comphelper,\ diff --git a/comphelper/StaticLibrary_windows_process.mk b/comphelper/StaticLibrary_windows_process.mk new file mode 100644 index 000000000000..1b086eac952b --- /dev/null +++ b/comphelper/StaticLibrary_windows_process.mk @@ -0,0 +1,24 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_StaticLibrary_StaticLibrary,windows_process)) + +$(eval $(call gb_StaticLibrary_set_include,windows_process,\ + $$(INCLUDE) \ +)) + +$(eval $(call gb_StaticLibrary_add_defs,windows_process,\ + -DUNICODE \ +)) + +$(eval $(call gb_StaticLibrary_add_exception_objects,windows_process,\ + comphelper/source/windows/windows_process \ +)) + +# vim:set shiftwidth=4 tabstop=4 noexpandtab: */ diff --git a/comphelper/source/windows/windows_process.cxx b/comphelper/source/windows/windows_process.cxx new file mode 100644 index 000000000000..1c782d7a289f --- /dev/null +++ b/comphelper/source/windows/windows_process.cxx @@ -0,0 +1,270 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +#include +#include + +// Needed for CreateEnvironmentBlock +#include +#pragma comment(lib, "userenv.lib") + +/** + * Get the length that the string will take and takes into account the + * additional length if the string needs to be quoted and if characters need to + * be escaped. + */ +static int ArgStrLen(const wchar_t *s) +{ + int backslashes = 0; + int i = wcslen(s); + BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr; + // Only add doublequotes if the string contains a space or a tab + BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr; + + if (addDoubleQuotes) + { + i += 2; // initial and final duoblequote + } + + if (hasDoubleQuote) + { + while (*s) + { + if (*s == '\\') + { + ++backslashes; + } + else + { + if (*s == '"') + { + // Escape the doublequote and all backslashes preceding the doublequote + i += backslashes + 1; + } + + backslashes = 0; + } + + ++s; + } + } + + return i; +} + +/** + * Copy string "s" to string "d", quoting the argument as appropriate and + * escaping doublequotes along with any backslashes that immediately precede + * doublequotes. + * The CRT parses this to retrieve the original argc/argv that we meant, + * see STDARGV.C in the MSVC CRT sources. + * + * @return the end of the string + */ +static wchar_t* ArgToString(wchar_t *d, const wchar_t *s) +{ + int backslashes = 0; + BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr; + // Only add doublequotes if the string contains a space or a tab + BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr; + + if (addDoubleQuotes) + { + *d = '"'; // initial doublequote + ++d; + } + + if (hasDoubleQuote) + { + int i; + while (*s) + { + if (*s == '\\') + { + ++backslashes; + } + else + { + if (*s == '"') + { + // Escape the doublequote and all backslashes preceding the doublequote + for (i = 0; i <= backslashes; ++i) + { + *d = '\\'; + ++d; + } + } + + backslashes = 0; + } + + *d = *s; + ++d; + ++s; + } + } + else + { + wcscpy(d, s); + d += wcslen(s); + } + + if (addDoubleQuotes) + { + *d = '"'; // final doublequote + ++d; + } + + return d; +} + +/** + * Creates a command line from a list of arguments. The returned + * string is allocated with "malloc" and should be "free"d. + * + * argv is UTF8 + */ +wchar_t* +MakeCommandLine(int argc, wchar_t **argv) +{ + int i; + int len = 0; + + // The + 1 of the last argument handles the allocation for null termination + for (i = 0; i < argc; ++i) + len += ArgStrLen(argv[i]) + 1; + + // Protect against callers that pass 0 arguments + if (len == 0) + len = 1; + + wchar_t *s = (wchar_t*) malloc(len * sizeof(wchar_t)); + if (!s) + return nullptr; + + wchar_t *c = s; + for (i = 0; i < argc; ++i) + { + c = ArgToString(c, argv[i]); + if (i + 1 != argc) + { + *c = ' '; + ++c; + } + } + + *c = '\0'; + + return s; +} + +/** + * Launch a child process with the specified arguments. + * @note argv[0] is ignored + * @note The form of this function that takes char **argv expects UTF-8 + */ +BOOL +WinLaunchChild(const wchar_t *exePath, + int argc, wchar_t **argv, + HANDLE userToken = nullptr, + HANDLE *hProcess = nullptr); + +BOOL +WinLaunchChild(const wchar_t *exePath, + int argc, + wchar_t **argv, + HANDLE userToken, + HANDLE *hProcess) +{ + wchar_t *cl; + BOOL ok; + + cl = MakeCommandLine(argc, argv); + if (!cl) + { + return FALSE; + } + + STARTUPINFOW si = {0}; + si.cb = sizeof(STARTUPINFOW); + si.lpDesktop = L"winsta0\\Default"; + PROCESS_INFORMATION pi = {0}; + + if (userToken == nullptr) + { + ok = CreateProcessW(exePath, + cl, + nullptr, // no special security attributes + nullptr, // no special thread attributes + FALSE, // don't inherit filehandles + 0, // creation flags + nullptr, // inherit my environment + nullptr, // use my current directory + &si, + &pi); + } + else + { + // Create an environment block for the process we're about to start using + // the user's token. + LPVOID environmentBlock = nullptr; + if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) + { + environmentBlock = nullptr; + } + + ok = CreateProcessAsUserW(userToken, + exePath, + cl, + nullptr, // no special security attributes + nullptr, // no special thread attributes + FALSE, // don't inherit filehandles + 0, // creation flags + environmentBlock, + nullptr, // use my current directory + &si, + &pi); + + if (environmentBlock) + { + DestroyEnvironmentBlock(environmentBlock); + } + } + + if (ok) + { + if (hProcess) + { + *hProcess = pi.hProcess; // the caller now owns the HANDLE + } + else + { + CloseHandle(pi.hProcess); + } + CloseHandle(pi.hThread); + } + else + { + LPVOID lpMsgBuf = nullptr; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + nullptr); + wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)"); + if (lpMsgBuf) + LocalFree(lpMsgBuf); + } + + free(cl); + + return ok; +} diff --git a/include/comphelper/windowsStart.hxx b/include/comphelper/windowsStart.hxx new file mode 100644 index 000000000000..ce66ef933b68 --- /dev/null +++ b/include/comphelper/windowsStart.hxx @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef INCLUDED_COMPHELPER_WINSTART_HXX +#define INCLUDED_COMPHELPER_WINSTART_HXX + +#ifdef _WIN32 +#include +#endif + + +BOOL +WinLaunchChild(const wchar_t *exePath, int argc, + wchar_t **argv, HANDLE userToken = nullptr, + HANDLE *hProcess = nullptr); + +BOOL +WinLaunchChild(const wchar_t *exePath, int argc, + char **argv, HANDLE userToken = nullptr, + HANDLE *hProcess = nullptr); + +wchar_t* MakeCommandLine(int argc, WCHAR **argv); + +#endif diff --git a/onlineupdate/Executable_updater.mk b/onlineupdate/Executable_updater.mk index 6071ae54837a..754fc9e898d5 100644 --- a/onlineupdate/Executable_updater.mk +++ b/onlineupdate/Executable_updater.mk @@ -23,7 +23,7 @@ $(eval $(call gb_Executable_use_static_libraries,updater,\ libmarverify \ updatehelper \ $(if $(filter WNT,$(OS)), \ - winhelper )\ + windows_process )\ )) $(eval $(call gb_Executable_use_externals,updater,\ diff --git a/onlineupdate/Module_onlineupdate.mk b/onlineupdate/Module_onlineupdate.mk index 2f3817182e03..a900e631a877 100644 --- a/onlineupdate/Module_onlineupdate.mk +++ b/onlineupdate/Module_onlineupdate.mk @@ -17,8 +17,7 @@ $(eval $(call gb_Module_add_targets,onlineupdate,\ StaticLibrary_updatehelper \ $(if $(filter WNT,$(OS)),\ Executable_update_service \ - WinResTarget_updater \ - StaticLibrary_winhelper )\ + WinResTarget_updater )\ Executable_mar \ Executable_updater \ Executable_mbsdiff \ diff --git a/onlineupdate/StaticLibrary_winhelper.mk b/onlineupdate/StaticLibrary_winhelper.mk deleted file mode 100644 index e6058c3f51e7..000000000000 --- a/onlineupdate/StaticLibrary_winhelper.mk +++ /dev/null @@ -1,22 +0,0 @@ -# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- -# -# This file is part of the LibreOffice project. -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# - -$(eval $(call gb_StaticLibrary_StaticLibrary,winhelper)) - -$(eval $(call gb_StaticLibrary_set_include,winhelper,\ - -I$(SRCDIR)/onlineupdate/inc/ \ - -I$(SRCDIR)/onlineupdate/source/winhelper/ \ - $$(INCLUDE) \ -)) - -$(eval $(call gb_StaticLibrary_add_exception_objects,winhelper,\ - onlineupdate/source/winhelper/windowsStart \ -)) - -# vim:set shiftwidth=4 tabstop=4 noexpandtab: */ diff --git a/onlineupdate/inc/winhelper/windowsStart.hxx b/onlineupdate/inc/winhelper/windowsStart.hxx deleted file mode 100644 index 7a9d54a506f2..000000000000 --- a/onlineupdate/inc/winhelper/windowsStart.hxx +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef INCLUDED_ONLINEUPDATE_INC_WINSTART_HXX -#define INCLUDED_ONLINEUPDATE_INC_WINSTART_HXX - -#ifdef _WIN32 -#include -#endif - - -BOOL -WinLaunchChild(const wchar_t *exePath, int argc, - wchar_t **argv, HANDLE userToken = nullptr, - HANDLE *hProcess = nullptr); - -BOOL -WinLaunchChild(const wchar_t *exePath, int argc, - char **argv, HANDLE userToken = nullptr, - HANDLE *hProcess = nullptr); - -wchar_t* MakeCommandLine(int argc, WCHAR **argv); - -#endif diff --git a/onlineupdate/source/update/updater/updater.cxx b/onlineupdate/source/update/updater/updater.cxx index 8a1ef8bf5b24..1509d570ba23 100644 --- a/onlineupdate/source/update/updater/updater.cxx +++ b/onlineupdate/source/update/updater/updater.cxx @@ -63,7 +63,7 @@ #include #ifdef _WIN32 -#include +#include #include "uachelper.h" #include "pathhash.h" diff --git a/onlineupdate/source/winhelper/windowsStart.cxx b/onlineupdate/source/winhelper/windowsStart.cxx deleted file mode 100644 index 1c782d7a289f..000000000000 --- a/onlineupdate/source/winhelper/windowsStart.cxx +++ /dev/null @@ -1,270 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include - -#include -#include - -// Needed for CreateEnvironmentBlock -#include -#pragma comment(lib, "userenv.lib") - -/** - * Get the length that the string will take and takes into account the - * additional length if the string needs to be quoted and if characters need to - * be escaped. - */ -static int ArgStrLen(const wchar_t *s) -{ - int backslashes = 0; - int i = wcslen(s); - BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr; - // Only add doublequotes if the string contains a space or a tab - BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr; - - if (addDoubleQuotes) - { - i += 2; // initial and final duoblequote - } - - if (hasDoubleQuote) - { - while (*s) - { - if (*s == '\\') - { - ++backslashes; - } - else - { - if (*s == '"') - { - // Escape the doublequote and all backslashes preceding the doublequote - i += backslashes + 1; - } - - backslashes = 0; - } - - ++s; - } - } - - return i; -} - -/** - * Copy string "s" to string "d", quoting the argument as appropriate and - * escaping doublequotes along with any backslashes that immediately precede - * doublequotes. - * The CRT parses this to retrieve the original argc/argv that we meant, - * see STDARGV.C in the MSVC CRT sources. - * - * @return the end of the string - */ -static wchar_t* ArgToString(wchar_t *d, const wchar_t *s) -{ - int backslashes = 0; - BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr; - // Only add doublequotes if the string contains a space or a tab - BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr; - - if (addDoubleQuotes) - { - *d = '"'; // initial doublequote - ++d; - } - - if (hasDoubleQuote) - { - int i; - while (*s) - { - if (*s == '\\') - { - ++backslashes; - } - else - { - if (*s == '"') - { - // Escape the doublequote and all backslashes preceding the doublequote - for (i = 0; i <= backslashes; ++i) - { - *d = '\\'; - ++d; - } - } - - backslashes = 0; - } - - *d = *s; - ++d; - ++s; - } - } - else - { - wcscpy(d, s); - d += wcslen(s); - } - - if (addDoubleQuotes) - { - *d = '"'; // final doublequote - ++d; - } - - return d; -} - -/** - * Creates a command line from a list of arguments. The returned - * string is allocated with "malloc" and should be "free"d. - * - * argv is UTF8 - */ -wchar_t* -MakeCommandLine(int argc, wchar_t **argv) -{ - int i; - int len = 0; - - // The + 1 of the last argument handles the allocation for null termination - for (i = 0; i < argc; ++i) - len += ArgStrLen(argv[i]) + 1; - - // Protect against callers that pass 0 arguments - if (len == 0) - len = 1; - - wchar_t *s = (wchar_t*) malloc(len * sizeof(wchar_t)); - if (!s) - return nullptr; - - wchar_t *c = s; - for (i = 0; i < argc; ++i) - { - c = ArgToString(c, argv[i]); - if (i + 1 != argc) - { - *c = ' '; - ++c; - } - } - - *c = '\0'; - - return s; -} - -/** - * Launch a child process with the specified arguments. - * @note argv[0] is ignored - * @note The form of this function that takes char **argv expects UTF-8 - */ -BOOL -WinLaunchChild(const wchar_t *exePath, - int argc, wchar_t **argv, - HANDLE userToken = nullptr, - HANDLE *hProcess = nullptr); - -BOOL -WinLaunchChild(const wchar_t *exePath, - int argc, - wchar_t **argv, - HANDLE userToken, - HANDLE *hProcess) -{ - wchar_t *cl; - BOOL ok; - - cl = MakeCommandLine(argc, argv); - if (!cl) - { - return FALSE; - } - - STARTUPINFOW si = {0}; - si.cb = sizeof(STARTUPINFOW); - si.lpDesktop = L"winsta0\\Default"; - PROCESS_INFORMATION pi = {0}; - - if (userToken == nullptr) - { - ok = CreateProcessW(exePath, - cl, - nullptr, // no special security attributes - nullptr, // no special thread attributes - FALSE, // don't inherit filehandles - 0, // creation flags - nullptr, // inherit my environment - nullptr, // use my current directory - &si, - &pi); - } - else - { - // Create an environment block for the process we're about to start using - // the user's token. - LPVOID environmentBlock = nullptr; - if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) - { - environmentBlock = nullptr; - } - - ok = CreateProcessAsUserW(userToken, - exePath, - cl, - nullptr, // no special security attributes - nullptr, // no special thread attributes - FALSE, // don't inherit filehandles - 0, // creation flags - environmentBlock, - nullptr, // use my current directory - &si, - &pi); - - if (environmentBlock) - { - DestroyEnvironmentBlock(environmentBlock); - } - } - - if (ok) - { - if (hProcess) - { - *hProcess = pi.hProcess; // the caller now owns the HANDLE - } - else - { - CloseHandle(pi.hProcess); - } - CloseHandle(pi.hThread); - } - else - { - LPVOID lpMsgBuf = nullptr; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, - nullptr); - wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)"); - if (lpMsgBuf) - LocalFree(lpMsgBuf); - } - - free(cl); - - return ok; -} -- cgit