summaryrefslogtreecommitdiff
path: root/win/packaging/ca/CustomAction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'win/packaging/ca/CustomAction.cpp')
-rw-r--r--win/packaging/ca/CustomAction.cpp863
1 files changed, 863 insertions, 0 deletions
diff --git a/win/packaging/ca/CustomAction.cpp b/win/packaging/ca/CustomAction.cpp
new file mode 100644
index 00000000000..81c9f7eea92
--- /dev/null
+++ b/win/packaging/ca/CustomAction.cpp
@@ -0,0 +1,863 @@
+/* Copyright 2010, Oracle and/or its affiliates. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <windows.h>
+#include <winreg.h>
+#include <msi.h>
+#include <msiquery.h>
+#include <wcautil.h>
+#include <strutil.h>
+#include <string.h>
+#include <strsafe.h>
+#include <assert.h>
+
+#include <winservice.h>
+
+#define ONE_MB 1048576
+UINT ExecRemoveDataDirectory(wchar_t *dir)
+{
+ /* Strip stray backslash */
+ DWORD len = (DWORD)wcslen(dir);
+ if(len > 0 && dir[len-1]==L'\\')
+ dir[len-1] = 0;
+
+ SHFILEOPSTRUCTW fileop;
+ fileop.hwnd= NULL; /* no status display */
+ fileop.wFunc= FO_DELETE; /* delete operation */
+ fileop.pFrom= dir; /* source file name as double null terminated string */
+ fileop.pTo= NULL; /* no destination needed */
+ fileop.fFlags= FOF_NOCONFIRMATION|FOF_SILENT; /* do not prompt the user */
+
+ fileop.fAnyOperationsAborted= FALSE;
+ fileop.lpszProgressTitle= NULL;
+ fileop.hNameMappings= NULL;
+
+ return SHFileOperationW(&fileop);
+}
+
+
+extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, __FUNCTION__);
+ ExitOnFailure(hr, "Failed to initialize");
+ WcaLog(LOGMSG_STANDARD, "Initialized.");
+
+ wchar_t dir[MAX_PATH];
+ DWORD len = MAX_PATH;
+ MsiGetPropertyW(hInstall, L"CustomActionData", dir, &len);
+
+ er= ExecRemoveDataDirectory(dir);
+ WcaLog(LOGMSG_STANDARD, "SHFileOperation returned %d", er);
+LExit:
+ return WcaFinalize(er);
+}
+
+/*
+ Check for if directory is empty during install,
+ sets "<PROPERTY>_NOT_EMPTY" otherise
+*/
+extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall,
+ const wchar_t *PropertyName)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, __FUNCTION__);
+ ExitOnFailure(hr, "Failed to initialize");
+ WcaLog(LOGMSG_STANDARD, "Initialized.");
+
+
+ wchar_t buf[MAX_PATH];
+ DWORD len = MAX_PATH;
+ MsiGetPropertyW(hInstall, PropertyName, buf, &len);
+ wcscat_s(buf, MAX_PATH, L"*.*");
+
+ WcaLog(LOGMSG_STANDARD, "Checking files in %S", buf);
+ WIN32_FIND_DATAW data;
+ HANDLE h;
+ bool empty;
+ h= FindFirstFile(buf, &data);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ empty= true;
+ for(;;)
+ {
+ if (wcscmp(data.cFileName, L".") || wcscmp(data.cFileName, L".."))
+ {
+ empty= false;
+ break;
+ }
+ if (!FindNextFile(h, &data))
+ break;
+ }
+ FindClose(h);
+ }
+ else
+ {
+ /* Non-existent directory, we handle it as empty */
+ empty = true;
+ }
+
+ if(empty)
+ WcaLog(LOGMSG_STANDARD, "Directory %S is empty or non-existent",
+ PropertyName);
+ else
+ WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName);
+
+ wcscpy_s(buf, MAX_PATH, PropertyName);
+ wcscat_s(buf, L"NOTEMPTY");
+ WcaSetProperty(buf, empty? L"":L"1");
+
+LExit:
+ return WcaFinalize(er);
+}
+
+extern "C" UINT __stdcall CheckDataDirectoryEmpty(MSIHANDLE hInstall)
+{
+ return CheckDirectoryEmpty(hInstall, L"DATADIR");
+}
+
+bool CheckServiceExists(const wchar_t *name)
+{
+ SC_HANDLE manager =0, service=0;
+ manager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT);
+ if (!manager)
+ {
+ return false;
+ }
+
+ service = OpenService(manager, name, SC_MANAGER_CONNECT);
+ if(service)
+ CloseServiceHandle(service);
+ CloseServiceHandle(manager);
+
+ return service?true:false;
+}
+
+/* User in rollback of create database custom action */
+bool ExecRemoveService(const wchar_t *name)
+{
+ SC_HANDLE manager =0, service=0;
+ manager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ bool ret;
+ if (!manager)
+ {
+ return false;
+ }
+ service = OpenService(manager, name, DELETE);
+ if(service)
+ {
+ ret= DeleteService(service);
+ }
+ else
+ {
+ ret= false;
+ }
+ CloseServiceHandle(manager);
+ return ret;
+}
+
+/*
+ Check if port is free by trying to bind to the port
+*/
+bool IsPortFree(short port)
+{
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 2);
+
+ WSAStartup(wVersionRequested, &wsaData);
+
+ struct sockaddr_in sin;
+ SOCKET sock;
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if(sock == -1)
+ {
+ return false;
+ }
+ sin.sin_port = htons(port);
+ sin.sin_addr.s_addr = 0;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_family = AF_INET;
+ if(bind(sock, (struct sockaddr *)&sin,sizeof(struct sockaddr_in) ) == -1)
+ {
+ return false;
+ }
+ closesocket(sock);
+ WSACleanup();
+ return true;
+}
+
+
+/*
+ Helper function used in filename normalization.
+ Removes leading quote and terminates string at the position of the next one
+ (if applicable, does not change string otherwise). Returns modified string
+*/
+wchar_t *strip_quotes(wchar_t *s)
+{
+ if (s && (*s == L'"'))
+ {
+ s++;
+ wchar_t *p = wcschr(s, L'"');
+ if(p)
+ *p = 0;
+ }
+ return s;
+}
+
+
+/*
+ Checks for consistency of service configuration.
+
+ It can happen that SERVICENAME or DATADIR
+ MSI properties are in inconsistent state after somebody upgraded database
+ We catch this case during uninstall. In particular, either service is not
+ removed even if SERVICENAME was set (but this name is reused by someone else)
+ or data directory is not removed (if it is used by someone else). To find out
+ whether service name and datadirectory are in use For every service,
+ configuration is read and checked as follows:
+
+ - look if a service has to do something with mysql
+ - If so, check its name against SERVICENAME. if match, check binary path
+ against INSTALLDIR\bin. If binary path does not match, then service runs
+ under different installation and won't be removed.
+ - Check options file for datadir and look if this is inside this
+ installation's datadir don't remove datadir if this is the case.
+
+ "Don't remove" in this context means that custom action is removing
+ SERVICENAME property or CLEANUPDATA property, which later on in course of
+ installation mean, that either datadir or service is kept.
+*/
+
+void CheckServiceConfig(
+ wchar_t *my_servicename, /* SERVICENAME property in this installation*/
+ wchar_t *datadir, /* DATADIR property in this installation*/
+ wchar_t *bindir, /* INSTALLDIR\bin */
+ wchar_t *other_servicename, /* Service to check against */
+ QUERY_SERVICE_CONFIGW * config /* Other service's config */
+ )
+{
+
+ bool same_bindir = false;
+ wchar_t * commandline= config->lpBinaryPathName;
+ int numargs;
+ wchar_t **argv= CommandLineToArgvW(commandline, &numargs);
+ WcaLog(LOGMSG_VERBOSE, "CommandLine= %S", commandline);
+ if(!argv || !argv[0] || ! wcsstr(argv[0], L"mysqld"))
+ {
+ goto end;
+ }
+
+ WcaLog(LOGMSG_STANDARD, "MySQL service %S found: CommandLine= %S",
+ other_servicename, commandline);
+ if (wcsstr(argv[0], bindir))
+ {
+ WcaLog(LOGMSG_STANDARD, "executable under bin directory");
+ same_bindir = true;
+ }
+
+ bool is_my_service = (_wcsicmp(my_servicename, other_servicename) == 0);
+ if(!is_my_service)
+ {
+ WcaLog(LOGMSG_STANDARD, "service does not match current service");
+ /*
+ TODO probably the best thing possible would be to add temporary
+ row to MSI ServiceConfig table with remove on uninstall
+ */
+ }
+ else if (!same_bindir)
+ {
+ WcaLog(LOGMSG_STANDARD,
+ "Service name matches, but not the executable path directory, mine is %S",
+ bindir);
+ WcaSetProperty(L"SERVICENAME", L"");
+ }
+
+ /* Check if data directory is used */
+ if(!datadir || numargs <= 1 || wcsncmp(argv[1],L"--defaults-file=",16) != 0)
+ {
+ goto end;
+ }
+
+ wchar_t current_datadir_buf[MAX_PATH]={0};
+ wchar_t normalized_current_datadir[MAX_PATH+1];
+ wchar_t *current_datadir= current_datadir_buf;
+ wchar_t *defaults_file= argv[1]+16;
+ defaults_file= strip_quotes(defaults_file);
+
+ WcaLog(LOGMSG_STANDARD, "parsed defaults file is %S", defaults_file);
+
+ if (GetPrivateProfileStringW(L"mysqld", L"datadir", NULL, current_datadir,
+ MAX_PATH, defaults_file) == 0)
+ {
+ WcaLog(LOGMSG_STANDARD,
+ "Cannot find datadir in ini file '%S'", defaults_file);
+ goto end;
+ }
+
+ WcaLog(LOGMSG_STANDARD, "datadir from defaults-file is %S", current_datadir);
+ strip_quotes(current_datadir);
+
+ /* Convert to Windows path */
+ if (GetFullPathNameW(current_datadir, MAX_PATH, normalized_current_datadir,
+ NULL))
+ {
+ /* Add backslash to be compatible with directory formats in MSI */
+ wcsncat(normalized_current_datadir, L"\\", MAX_PATH+1);
+ WcaLog(LOGMSG_STANDARD, "normalized current datadir is '%S'",
+ normalized_current_datadir);
+ }
+
+ if (_wcsicmp(datadir, normalized_current_datadir) == 0 && !same_bindir)
+ {
+ WcaLog(LOGMSG_STANDARD,
+ "database directory from current installation, but different mysqld.exe");
+ WcaSetProperty(L"CLEANUPDATA", L"");
+ }
+
+end:
+ LocalFree((HLOCAL)argv);
+}
+
+/*
+ Checks if database directory or service are modified by user
+ For example, service may point to different mysqld.exe that it was originally
+ installed, or some different service might use this database directory. This
+ would normally mean user has done an upgrade of the database and in this case
+ uninstall should neither delete service nor database directory.
+
+ If this function find that service is modified by user (mysqld.exe used by
+ service does not point to the installation bin directory), MSI public variable
+ SERVICENAME is removed, if DATADIR is used by some other service, variables
+ DATADIR and CLEANUPDATA are removed.
+
+ The effect of variable removal is that service does not get uninstalled and
+ datadir is not touched by uninstallation.
+
+ Note that this function is running without elevation and does not use anything
+ that would require special privileges.
+
+*/
+extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
+{
+ static BYTE buf[256*1024]; /* largest possible buffer for EnumServices */
+ static char config_buffer[8*1024]; /*largest buffer for QueryServiceConfig */
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ wchar_t *servicename= NULL;
+ wchar_t *datadir= NULL;
+ wchar_t *bindir=NULL;
+
+ SC_HANDLE scm = NULL;
+ ULONG bufsize = sizeof(buf);
+ ULONG bufneed = 0x00;
+ ULONG num_services = 0x00;
+ LPENUM_SERVICE_STATUS_PROCESS info = NULL;
+
+ hr = WcaInitialize(hInstall, __FUNCTION__);
+ ExitOnFailure(hr, "Failed to initialize");
+ WcaLog(LOGMSG_STANDARD, "Initialized.");
+
+ WcaGetProperty(L"SERVICENAME", &servicename);
+ WcaGetProperty(L"DATADIR", &datadir);
+ WcaGetFormattedString(L"[INSTALLDIR]bin\\", &bindir);
+ WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S",
+ servicename, datadir, bindir);
+
+ scm = OpenSCManager(NULL, NULL,
+ SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
+ if (scm == NULL)
+ {
+ ExitOnFailure(E_FAIL, "OpenSCManager failed");
+ }
+
+ BOOL ok= EnumServicesStatusExW( scm,
+ SC_ENUM_PROCESS_INFO,
+ SERVICE_WIN32,
+ SERVICE_STATE_ALL,
+ buf,
+ bufsize,
+ &bufneed,
+ &num_services,
+ NULL,
+ NULL);
+ if(!ok)
+ {
+ WcaLog(LOGMSG_STANDARD, "last error %d", GetLastError());
+ ExitOnFailure(E_FAIL, "EnumServicesStatusExW failed");
+ }
+ info = (LPENUM_SERVICE_STATUS_PROCESS)buf;
+ for (ULONG i=0; i < num_services; i++)
+ {
+ SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName,
+ SERVICE_QUERY_CONFIG);
+ if (!service)
+ continue;
+ WcaLog(LOGMSG_VERBOSE, "Checking Service %S", info[i].lpServiceName);
+ QUERY_SERVICE_CONFIGW *config=
+ (QUERY_SERVICE_CONFIGW *)(void *)config_buffer;
+ DWORD needed;
+ BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
+ &needed);
+ CloseServiceHandle(service);
+ if (ok)
+ {
+ CheckServiceConfig(servicename, datadir, bindir, info[i].lpServiceName,
+ config);
+ }
+ }
+
+LExit:
+ if(scm)
+ CloseServiceHandle(scm);
+
+ ReleaseStr(servicename);
+ ReleaseStr(datadir);
+ ReleaseStr(bindir);
+ return WcaFinalize(er);
+}
+
+/*
+ Get maximum size of the buffer process can allocate.
+ this is calculated as min(RAM,virtualmemorylimit)
+ For 32bit processes, virtual address memory is 2GB (x86 OS)
+ or 4GB(x64 OS).
+
+ Fragmentation due to loaded modules, heap and stack
+ limit maximum size of continous memory block further,
+ so that limit for 32 bit process is about 1200 on 32 bit OS
+ or 2000 MB on 64 bit OS(found experimentally).
+*/
+unsigned long long GetMaxBufferSize(unsigned long long totalPhys)
+{
+#ifdef _M_IX86
+ BOOL wow64;
+ if (IsWow64Process(GetCurrentProcess(), &wow64))
+ return min(totalPhys, 2000ULL*ONE_MB);
+ else
+ return min(totalPhys, 1200ULL*ONE_MB);
+#else
+ return totalPhys;
+#endif
+}
+/*
+ Checks SERVICENAME, PORT and BUFFERSIZE parameters
+*/
+extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
+{
+ wchar_t ServiceName[MAX_PATH]={0};
+ wchar_t SkipNetworking[MAX_PATH]={0};
+ wchar_t QuickConfig[MAX_PATH]={0};
+ wchar_t Port[6];
+ wchar_t BufferPoolSize[16];
+ DWORD PortLen=6;
+ bool haveInvalidPort=false;
+ const wchar_t *ErrorMsg=0;
+ HRESULT hr= S_OK;
+ UINT er= ERROR_SUCCESS;
+
+
+ hr = WcaInitialize(hInstall, __FUNCTION__);
+ ExitOnFailure(hr, "Failed to initialize");
+ WcaLog(LOGMSG_STANDARD, "Initialized.");
+
+ DWORD ServiceNameLen = MAX_PATH;
+ MsiGetPropertyW (hInstall, L"SERVICENAME", ServiceName, &ServiceNameLen);
+ if(ServiceName[0])
+ {
+ if(ServiceNameLen > 256)
+ {
+ ErrorMsg= L"Invalid service name. The maximum length is 256 characters.";
+ goto LExit;
+ }
+ for(DWORD i=0; i< ServiceNameLen;i++)
+ {
+ if(ServiceName[i] == L'\\' || ServiceName[i] == L'/'
+ || ServiceName[i]=='\'' || ServiceName[i] ==L'"')
+ {
+ ErrorMsg =
+ L"Invalid service name. Forward slash and back slash are forbidden."
+ L"Single and double quotes are also not permitted.";
+ goto LExit;
+ }
+ }
+ if(CheckServiceExists(ServiceName))
+ {
+ ErrorMsg=
+ L"A service with the same name already exists. "
+ L"Please use a different name.";
+ goto LExit;
+ }
+ }
+
+ DWORD SkipNetworkingLen= MAX_PATH;
+
+ MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking,
+ &SkipNetworkingLen);
+ MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen);
+
+ if(SkipNetworking[0]==0 && Port[0] != 0)
+ {
+ /* Strip spaces */
+ for(DWORD i=PortLen-1; i > 0; i--)
+ {
+ if(Port[i]== ' ')
+ Port[i] = 0;
+ }
+
+ if(PortLen > 5 || PortLen <= 3)
+ haveInvalidPort = true;
+ else
+ {
+ for (DWORD i=0; i< PortLen && Port[i] != 0;i++)
+ {
+ if(Port[i] < '0' || Port[i] >'9')
+ {
+ haveInvalidPort=true;
+ break;
+ }
+ }
+ }
+ if (haveInvalidPort)
+ {
+ ErrorMsg =
+ L"Invalid port number. Please use a number between 1025 and 65535.";
+ goto LExit;
+ }
+
+ short port = (short)_wtoi(Port);
+ if (!IsPortFree(port))
+ {
+ ErrorMsg =
+ L"The TCP Port you selected is already in use. "
+ L"Please choose a different port.";
+ goto LExit;
+ }
+ }
+
+
+ DWORD QuickConfigLen = MAX_PATH;
+ MsiGetPropertyW (hInstall, L"STDCONFIG", QuickConfig, &QuickConfigLen);
+ if(QuickConfig[0] !=0)
+ {
+ MEMORYSTATUSEX memstatus;
+ memstatus.dwLength =sizeof(memstatus);
+ wchar_t invalidValueMsg[256];
+
+ if (!GlobalMemoryStatusEx(&memstatus))
+ {
+ WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx",
+ GetLastError());
+ er= ERROR_INSTALL_FAILURE;
+ goto LExit;
+ }
+ DWORD BufferPoolSizeLen= 16;
+ MsiGetPropertyW(hInstall, L"BUFFERPOOLSIZE", BufferPoolSize, &BufferPoolSizeLen);
+ /* Strip spaces */
+ for(DWORD i=BufferPoolSizeLen-1; i > 0; i--)
+ {
+ if(BufferPoolSize[i]== ' ')
+ BufferPoolSize[i] = 0;
+ }
+ unsigned long long availableMemory=
+ GetMaxBufferSize(memstatus.ullTotalPhys)/ONE_MB;
+ swprintf_s(invalidValueMsg,
+ L"Invalid buffer pool size. Please use a number between 1 and %llu",
+ availableMemory);
+ if(BufferPoolSizeLen == 0 || BufferPoolSizeLen > 15)
+ {
+ ErrorMsg= invalidValueMsg;
+ goto LExit;
+ }
+ for (DWORD i=0; i < BufferPoolSizeLen && BufferPoolSize[BufferPoolSizeLen];
+ i++)
+ {
+ if(BufferPoolSize[i]< '0' || BufferPoolSize[i] > '9')
+ {
+ ErrorMsg= invalidValueMsg;
+ goto LExit;
+ }
+ }
+ BufferPoolSize[BufferPoolSizeLen]=0;
+ MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", BufferPoolSize);
+ long long sz = _wtoi64(BufferPoolSize);
+ if(sz <= 0 || sz > (long long)availableMemory)
+ {
+ if(sz > 0)
+ {
+ swprintf_s(invalidValueMsg,
+ L"Value for buffer pool size is too large."
+ L"Only approximately %llu MB is available for allocation."
+ L"Please use a number between 1 and %llu.",
+ availableMemory, availableMemory);
+ }
+ ErrorMsg= invalidValueMsg;
+ goto LExit;
+ }
+ }
+LExit:
+ MsiSetPropertyW (hInstall, L"WarningText", ErrorMsg);
+ return WcaFinalize(er);
+}
+
+/*
+ Sets Innodb buffer pool size (1/8 of RAM by default), if not already specified
+ via command line.
+ Calculates innodb log file size as min(50, innodb buffer pool size/8)
+*/
+extern "C" UINT __stdcall PresetDatabaseProperties(MSIHANDLE hInstall)
+{
+ unsigned long long InnodbBufferPoolSize= 256;
+ unsigned long long InnodbLogFileSize= 50;
+ wchar_t buff[MAX_PATH];
+ UINT er = ERROR_SUCCESS;
+ HRESULT hr= S_OK;
+ MEMORYSTATUSEX memstatus;
+ hr = WcaInitialize(hInstall, __FUNCTION__);
+ ExitOnFailure(hr, "Failed to initialize");
+ WcaLog(LOGMSG_STANDARD, "Initialized.");
+
+ /* Check if bufferpoolsize parameter was given on the command line*/
+ DWORD BufferPoolsizeParamLen = MAX_PATH;
+ MsiGetPropertyW(hInstall, L"BUFFERPOOLSIZE", buff, &BufferPoolsizeParamLen);
+
+ if (BufferPoolsizeParamLen && buff[0])
+ {
+ WcaLog(LOGMSG_STANDARD, "BUFFERPOOLSIZE=%s, len=%u",buff, BufferPoolsizeParamLen);
+ InnodbBufferPoolSize= _wtoi64(buff);
+ }
+ else
+ {
+ memstatus.dwLength = sizeof(memstatus);
+ if (!GlobalMemoryStatusEx(&memstatus))
+ {
+ WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx",
+ GetLastError());
+ er= ERROR_INSTALL_FAILURE;
+ goto LExit;
+ }
+ unsigned long long totalPhys= memstatus.ullTotalPhys;
+ /* Give innodb 12.5% of available physical memory. */
+ InnodbBufferPoolSize= totalPhys/ONE_MB/8;
+ #ifdef _M_IX86
+ /*
+ For 32 bit processes, take virtual address space limitation into account.
+ Do not try to use more than 3/4 of virtual address space, even if there
+ is plenty of physical memory.
+ */
+ InnodbBufferPoolSize= min(GetMaxBufferSize(totalPhys)/ONE_MB*3/4,
+ InnodbBufferPoolSize);
+ #endif
+ swprintf_s(buff, L"%llu",InnodbBufferPoolSize);
+ MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", buff);
+ }
+ InnodbLogFileSize = min(50, InnodbBufferPoolSize);
+ swprintf_s(buff, L"%llu",InnodbLogFileSize);
+ MsiSetPropertyW(hInstall, L"LOGFILESIZE", buff);
+
+LExit:
+ return WcaFinalize(er);
+}
+/* Remove service and data directory created by CreateDatabase operation */
+extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ wchar_t* service= 0;
+ wchar_t* dir= 0;
+
+ hr = WcaInitialize(hInstall, __FUNCTION__);
+ ExitOnFailure(hr, "Failed to initialize");
+ WcaLog(LOGMSG_STANDARD, "Initialized.");
+ wchar_t data[2*MAX_PATH];
+ DWORD len= MAX_PATH;
+ MsiGetPropertyW(hInstall, L"CustomActionData", data, &len);
+
+ /* Property is encoded as [SERVICENAME]\[DBLOCATION] */
+ if(data[0] == L'\\')
+ {
+ dir= data+1;
+ }
+ else
+ {
+ service= data;
+ dir= wcschr(data, '\\');
+ if (dir)
+ {
+ *dir=0;
+ dir++;
+ }
+ }
+
+ if(service)
+ {
+ ExecRemoveService(service);
+ }
+ if(dir)
+ {
+ ExecRemoveDataDirectory(dir);
+ }
+LExit:
+ return WcaFinalize(er);
+}
+
+
+/*
+ Enables/disables optional "Launch upgrade wizard" checkbox at the end of
+ installation
+*/
+#define MAX_VERSION_PROPERTY_SIZE 64
+
+extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ wchar_t* service= 0;
+ wchar_t* dir= 0;
+ wchar_t installerVersion[MAX_VERSION_PROPERTY_SIZE];
+ char installDir[MAX_PATH];
+ DWORD size =MAX_VERSION_PROPERTY_SIZE;
+ int installerMajorVersion, installerMinorVersion, installerPatchVersion;
+ bool upgradableServiceFound=false;
+
+ hr = WcaInitialize(hInstall, __FUNCTION__);
+ WcaLog(LOGMSG_STANDARD, "Initialized.");
+ if (MsiGetPropertyW(hInstall, L"ProductVersion", installerVersion, &size)
+ != ERROR_SUCCESS)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ ExitOnFailure(hr, "MsiGetPropertyW failed");
+ }
+ if (swscanf(installerVersion,L"%d.%d.%d",
+ &installerMajorVersion, &installerMinorVersion, &installerPatchVersion) !=3)
+ {
+ assert(FALSE);
+ }
+
+ size= MAX_PATH;
+ if (MsiGetPropertyA(hInstall,"INSTALLDIR", installDir, &size)
+ != ERROR_SUCCESS)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ ExitOnFailure(hr, "MsiGetPropertyW failed");
+ }
+
+
+ SC_HANDLE scm = OpenSCManager(NULL, NULL,
+ SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
+ if (scm == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ ExitOnFailure(hr,"OpenSCManager failed");
+ }
+
+ static BYTE buf[64*1024];
+ static BYTE config_buffer[8*1024];
+
+ DWORD bufsize= sizeof(buf);
+ DWORD bufneed;
+ DWORD num_services;
+ BOOL ok= EnumServicesStatusExW(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
+ SERVICE_STATE_ALL, buf, bufsize, &bufneed, &num_services, NULL, NULL);
+ if(!ok)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ ExitOnFailure(hr,"EnumServicesStatusEx failed");
+ }
+ LPENUM_SERVICE_STATUS_PROCESSW info =
+ (LPENUM_SERVICE_STATUS_PROCESSW)buf;
+ int index=-1;
+ for (ULONG i=0; i < num_services; i++)
+ {
+ SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName,
+ SERVICE_QUERY_CONFIG);
+ if (!service)
+ continue;
+ QUERY_SERVICE_CONFIGW *config=
+ (QUERY_SERVICE_CONFIGW*)(void *)config_buffer;
+ DWORD needed;
+ BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
+ &needed);
+ CloseServiceHandle(service);
+ if (ok)
+ {
+ mysqld_service_properties props;
+ if (get_mysql_service_properties(config->lpBinaryPathName, &props))
+ continue;
+ /*
+ Only look for services that have mysqld.exe outside of the current
+ installation directory.
+ */
+ if(strstr(props.mysqld_exe,installDir) == 0)
+ {
+ WcaLog(LOGMSG_STANDARD, "found service %S, major=%d, minor=%d",
+ info[i].lpServiceName, props.version_major, props.version_minor);
+ if(props.version_major < installerMajorVersion
+ || (props.version_major == installerMajorVersion &&
+ props.version_minor <= installerMinorVersion))
+ {
+ upgradableServiceFound= true;
+ break;
+ }
+ }
+ }
+ }
+
+ if(!upgradableServiceFound)
+ {
+ /* Disable optional checkbox at the end of installation */
+ MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT", L"");
+ MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOX",L"");
+ }
+ else
+ {
+ MsiSetPropertyW(hInstall, L"UpgradableServiceFound", L"1");
+ MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOX",L"1");
+ }
+LExit:
+ if(scm)
+ CloseServiceHandle(scm);
+ return WcaFinalize(er);
+}
+
+
+/* DllMain - Initialize and cleanup WiX custom action utils */
+extern "C" BOOL WINAPI DllMain(
+ __in HINSTANCE hInst,
+ __in ULONG ulReason,
+ __in LPVOID
+ )
+{
+ switch(ulReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ WcaGlobalInitialize(hInst);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ WcaGlobalFinalize();
+ break;
+ }
+
+ return TRUE;
+}