diff options
author | Vladislav Vaintroub <wlad@montyprogram.com> | 2011-01-30 22:42:02 +0100 |
---|---|---|
committer | Vladislav Vaintroub <wlad@montyprogram.com> | 2011-01-30 22:42:02 +0100 |
commit | 1b28a0883dc02946384843af281e968913b4ed1e (patch) | |
tree | 034edc27b519882eed8638b4a665b6b70a88b80c | |
parent | 366ee3c791ca6b5e7688a5138dfe347860772d2a (diff) | |
download | mariadb-git-1b28a0883dc02946384843af281e968913b4ed1e.tar.gz |
split long lines, use get_mysql_service_properties()
-rw-r--r-- | sql/winservice.c | 167 | ||||
-rw-r--r-- | sql/winservice.h | 24 | ||||
-rw-r--r-- | win/cmake/dummy.in | 0 | ||||
-rw-r--r-- | win/packaging/ca/CustomAction.cpp | 152 |
4 files changed, 278 insertions, 65 deletions
diff --git a/sql/winservice.c b/sql/winservice.c new file mode 100644 index 00000000000..17d678e4f1a --- /dev/null +++ b/sql/winservice.c @@ -0,0 +1,167 @@ +/* + Get Properties of an existing mysqld Windows service +*/ + +#include <windows.h> +#include <winsvc.h> +#include "winservice.h" +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + + +/* + Get version from an executable file +*/ +void get_file_version(const char *path, int *major, int *minor, int *patch) +{ + DWORD version_handle; + char *ver= 0; + VS_FIXEDFILEINFO info; + UINT len; + DWORD size; + void *p; + *major= *minor= *patch= 0; + + size= GetFileVersionInfoSize(path, &version_handle); + if (size == 0) + return; + ver= (char *)malloc(size); + if(!GetFileVersionInfo(path, version_handle, size, ver)) + goto end; + + if(!VerQueryValue(ver,"\\",&p,&len)) + goto end; + memcpy(&info,p ,sizeof(VS_FIXEDFILEINFO)); + + *major= (info.dwFileVersionMS & 0xFFFF0000) >> 16; + *minor= (info.dwFileVersionMS & 0x0000FFFF); + *patch= (info.dwFileVersionLS & 0xFFFF0000) >> 16; +end: + free(ver); +} + +void normalize_path(char *path, size_t size) +{ + char buf[MAX_PATH]; + if (*path== '"') + { + char *p; + strcpy_s(buf, MAX_PATH, path+1); + p= strchr(buf, '"'); + if (p) + *p=0; + } + else + strcpy_s(buf, MAX_PATH, path); + GetFullPathName(buf, MAX_PATH, buf, NULL); + strcpy_s(path, size, buf); +} + +/* + Retrieve some properties from windows mysqld service binary path. + We're interested in ini file location and datadir, and also in version of + the data. We tolerate missing mysqld.exe. + + Note that this function carefully avoids using mysql libraries (e.g dbug), + since it is used in unusual environments (windows installer, MFC), where we + do not have much control over how threads are created and destroyed, so we + cannot assume MySQL thread initilization here. +*/ +int get_mysql_service_properties(const wchar_t *bin_path, + mysqld_service_properties *props) +{ + int numargs; + wchar_t mysqld_path[MAX_PATH + 4]; + wchar_t *file_part; + wchar_t **args= NULL; + int retval= 1; + + props->datadir[0]= 0; + props->inifile[0]= 0; + props->mysqld_exe[0]= 0; + props->version_major= 0; + props->version_minor= 0; + props->version_patch= 0; + + args= CommandLineToArgvW(bin_path, &numargs); + + if(numargs != 3) + goto end; + + if(wcsncmp(args[1], L"--defaults-file=", 16) != 0) + goto end; + + GetFullPathNameW(args[0], MAX_PATH, mysqld_path, &file_part); + + if(wcsstr(mysqld_path, L".exe") == NULL) + wcscat(mysqld_path, L".exe"); + + if(wcsicmp(file_part, L"mysqld.exe") != 0 && + wcsicmp(file_part, L"mysqld.exe") != 0 && + wcsicmp(file_part, L"mysqld-nt.exe") != 0) + { + /* The service executable is not mysqld. */ + goto end; + } + + wcstombs(props->mysqld_exe, args[0], MAX_PATH); + wcstombs(props->inifile, args[1]+16, MAX_PATH); + normalize_path(props->inifile, MAX_PATH); + + if (GetFileAttributes(props->inifile) == INVALID_FILE_ATTRIBUTES) + goto end; + + /* If mysqld.exe exists, try to get its version from executable */ + if (GetFileAttributes(props->mysqld_exe) != INVALID_FILE_ATTRIBUTES) + { + get_file_version(props->mysqld_exe, &props->version_major, + &props->version_minor, &props->version_patch); + } + + GetPrivateProfileString("mysqld", "datadir", NULL, props->datadir, MAX_PATH, + props->inifile); + + if (props->datadir[0]) + { + normalize_path(props->datadir, MAX_PATH); + /* Check if datadir really exists */ + if (GetFileAttributes(props->datadir) == INVALID_FILE_ATTRIBUTES) + goto end; + } + else + { + /* There is no datadir in ini file, bail out.*/ + goto end; + } + + /* + If version could not be determined so far, try mysql_upgrade_info in + database directory. + */ + if(props->version_major == 0) + { + char buf[MAX_PATH]; + FILE *mysql_upgrade_info; + + sprintf_s(buf, MAX_PATH, "%s\\mysql_upgrade_info", props->datadir); + mysql_upgrade_info= fopen(buf, "r"); + if(mysql_upgrade_info) + { + if (fgets(buf, MAX_PATH, mysql_upgrade_info)) + { + int major,minor,patch; + if (sscanf(buf, "%d.%d.%d", &major, &minor, &patch) == 3) + { + props->version_major= major; + props->version_minor= minor; + props->version_patch= patch; + } + } + } + } + retval = 0; +end: + LocalFree((HLOCAL)args); + return retval; +}
\ No newline at end of file diff --git a/sql/winservice.h b/sql/winservice.h new file mode 100644 index 00000000000..8957413783f --- /dev/null +++ b/sql/winservice.h @@ -0,0 +1,24 @@ +/*
+ Extract properties of a windows service binary path
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <windows.h>
+typedef struct mysqld_service_properties_st
+{
+ char mysqld_exe[MAX_PATH];
+ char inifile[MAX_PATH];
+ char datadir[MAX_PATH];
+ int version_major;
+ int version_minor;
+ int version_patch;
+} mysqld_service_properties;
+
+extern int get_mysql_service_properties(const wchar_t *bin_path,
+ mysqld_service_properties *props);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/win/cmake/dummy.in b/win/cmake/dummy.in new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/win/cmake/dummy.in diff --git a/win/packaging/ca/CustomAction.cpp b/win/packaging/ca/CustomAction.cpp index af2602348cd..ddc0ffd0614 100644 --- a/win/packaging/ca/CustomAction.cpp +++ b/win/packaging/ca/CustomAction.cpp @@ -70,8 +70,12 @@ 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) +/* + 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; @@ -112,7 +116,8 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall, const wchar_t } if(empty) - WcaLog(LOGMSG_STANDARD, "Directory %S is empty or non-existent", PropertyName); + WcaLog(LOGMSG_STANDARD, "Directory %S is empty or non-existent", + PropertyName); else WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName); @@ -225,22 +230,22 @@ wchar_t *strip_quotes(wchar_t *s) 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: + 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 retained. + - 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( @@ -262,7 +267,8 @@ void CheckServiceConfig( goto end; } - WcaLog(LOGMSG_STANDARD, "MySQL service %S found: CommandLine= %S", other_servicename, commandline); + WcaLog(LOGMSG_STANDARD, "MySQL service %S found: CommandLine= %S", + other_servicename, commandline); if (wcsstr(argv[0], bindir)) { WcaLog(LOGMSG_STANDARD, "executable under bin directory"); @@ -281,7 +287,8 @@ void CheckServiceConfig( else if (!same_bindir) { WcaLog(LOGMSG_STANDARD, - "Service name matches, but not the executable path directory, mine is %S", bindir); + "Service name matches, but not the executable path directory, mine is %S", + bindir); WcaSetProperty(L"SERVICENAME", L""); } @@ -299,8 +306,8 @@ void CheckServiceConfig( WcaLog(LOGMSG_STANDARD, "parsed defaults file is %S", defaults_file); - if (GetPrivateProfileStringW(L"mysqld", L"datadir", NULL, current_datadir, MAX_PATH, - defaults_file) == 0) + 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); @@ -311,16 +318,19 @@ void CheckServiceConfig( strip_quotes(current_datadir); /* Convert to Windows path */ - if (GetFullPathNameW(current_datadir, MAX_PATH, normalized_current_datadir, NULL)) + 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); + 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"); + WcaLog(LOGMSG_STANDARD, + "database directory from current installation, but different mysqld.exe"); WcaSetProperty(L"CLEANUPDATA", L""); } @@ -335,22 +345,22 @@ end: 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. + 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. + 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. + 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 possible buffer for QueryServiceConfig */ + static char config_buffer[8*1024]; /*largest buffer for QueryServiceConfig */ HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; wchar_t *servicename= NULL; @@ -373,7 +383,8 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S", servicename, datadir, bindir); - scm = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); + scm = OpenSCManager(NULL, NULL, + SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); if (scm == NULL) { ExitOnFailure(E_FAIL, "OpenSCManager failed"); @@ -397,17 +408,21 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) 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); + 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; + QUERY_SERVICE_CONFIGW *config= + (QUERY_SERVICE_CONFIGW *)(void *)config_buffer; DWORD needed; - BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), &needed); + BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), + &needed); CloseServiceHandle(service); if (ok) { - CheckServiceConfig(servicename, datadir, bindir, info[i].lpServiceName, config); + CheckServiceConfig(servicename, datadir, bindir, info[i].lpServiceName, + config); } } @@ -454,14 +469,17 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) } if(CheckServiceExists(ServiceName)) { - ErrorMsg= L"A service with the same name already exists. Please use a different name."; + ErrorMsg= + L"A service with the same name already exists. " + L"Please use a different name."; goto err; } } DWORD SkipNetworkingLen= MAX_PATH; - MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking, &SkipNetworkingLen); + MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking, + &SkipNetworkingLen); MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen); if(SkipNetworking[0]==0 && Port[0] != 0) @@ -488,14 +506,17 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) } if (haveInvalidPort) { - ErrorMsg = L"Invalid port number. Please use a number between 1025 and 65535."; + ErrorMsg = + L"Invalid port number. Please use a number between 1025 and 65535."; goto err; } short port = (short)_wtoi(Port); if (!IsPortFree(port)) { - ErrorMsg = L"The TCP Port you selected is already in use. Please choose a different port."; + ErrorMsg = + L"The TCP Port you selected is already in use. " + L"Please choose a different port."; goto err; } } @@ -550,7 +571,8 @@ LExit: /* - Enables/disables optional "Launch upgrade wizard" checkbox at the end of installation + Enables/disables optional "Launch upgrade wizard" checkbox at the end of + installation */ #define MAX_VERSION_PROPERTY_SIZE 64 @@ -589,7 +611,8 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) } - SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); + SC_HANDLE scm = OpenSCManager(NULL, NULL, + SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); if (scm == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); @@ -621,31 +644,30 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) QUERY_SERVICE_CONFIGW *config= (QUERY_SERVICE_CONFIGW*)(void *)config_buffer; DWORD needed; - BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), &needed); + BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), + &needed); CloseServiceHandle(service); if (ok) { - bool isMySQL; - int major; - int minor; - wchar_t program[MAX_PATH]={0}; - GetMySQLVersion(config->lpBinaryPathName, program, &isMySQL, &major, &minor); - - /* - Only look for services that have mysqld.exe outside of the current - installation directory. - */ - if(isMySQL && (wcsstr(program,installDir) == 0)) - { - WcaLog(LOGMSG_STANDARD, "found service %S, major=%d, minor=%d", - info[i].lpServiceName, major, minor); - if(major < installerMajorVersion - || (major == installerMajorVersion && minor <= installerMinorVersion)) + 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) { - upgradableServiceFound= true; - break; - } - } + 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; + } + } } } |