Commit 1b28a088 authored by Vladislav Vaintroub's avatar Vladislav Vaintroub

split long lines, use get_mysql_service_properties()

parent 366ee3c7
/*
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
/*
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
...@@ -70,8 +70,12 @@ extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall) ...@@ -70,8 +70,12 @@ extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall)
return WcaFinalize(er); 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; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
...@@ -112,7 +116,8 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall, const wchar_t ...@@ -112,7 +116,8 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall, const wchar_t
} }
if(empty) 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 else
WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName); WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName);
...@@ -225,22 +230,22 @@ wchar_t *strip_quotes(wchar_t *s) ...@@ -225,22 +230,22 @@ wchar_t *strip_quotes(wchar_t *s)
It can happen that SERVICENAME or DATADIR It can happen that SERVICENAME or DATADIR
MSI properties are in inconsistent state after somebody upgraded database MSI properties are in inconsistent state after somebody upgraded database
We catch this case during uninstall. In particular, either service is not removed We catch this case during uninstall. In particular, either service is not
even if SERVICENAME was set (but this name is reused by someone else) or data removed even if SERVICENAME was set (but this name is reused by someone else)
directory is not removed (if it is used by someone else). To find out whether or data directory is not removed (if it is used by someone else). To find out
service name and datadirectory are in use For every service, configuration is whether service name and datadirectory are in use For every service,
read and checked as follows: configuration is read and checked as follows:
- look if a service has to do something with mysql - look if a service has to do something with mysql
- If so, check its name against SERVICENAME. if match, check binary path against - If so, check its name against SERVICENAME. if match, check binary path
INSTALLDIR\bin. If binary path does not match, then service runs under different against INSTALLDIR\bin. If binary path does not match, then service runs
installation and won't be removed. under different installation and won't be removed.
- Check options file for datadir and look if this is inside this installation's - Check options file for datadir and look if this is inside this
datadir don't remove datadir if this is the case. 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 "Don't remove" in this context means that custom action is removing
or CLEANUPDATA property, which later on in course of installation mean that either datadir SERVICENAME property or CLEANUPDATA property, which later on in course of
or service is retained. installation mean, that either datadir or service is kept.
*/ */
void CheckServiceConfig( void CheckServiceConfig(
...@@ -262,7 +267,8 @@ void CheckServiceConfig( ...@@ -262,7 +267,8 @@ void CheckServiceConfig(
goto end; 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)) if (wcsstr(argv[0], bindir))
{ {
WcaLog(LOGMSG_STANDARD, "executable under bin directory"); WcaLog(LOGMSG_STANDARD, "executable under bin directory");
...@@ -281,7 +287,8 @@ void CheckServiceConfig( ...@@ -281,7 +287,8 @@ void CheckServiceConfig(
else if (!same_bindir) else if (!same_bindir)
{ {
WcaLog(LOGMSG_STANDARD, 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""); WcaSetProperty(L"SERVICENAME", L"");
} }
...@@ -299,8 +306,8 @@ void CheckServiceConfig( ...@@ -299,8 +306,8 @@ void CheckServiceConfig(
WcaLog(LOGMSG_STANDARD, "parsed defaults file is %S", defaults_file); WcaLog(LOGMSG_STANDARD, "parsed defaults file is %S", defaults_file);
if (GetPrivateProfileStringW(L"mysqld", L"datadir", NULL, current_datadir, MAX_PATH, if (GetPrivateProfileStringW(L"mysqld", L"datadir", NULL, current_datadir,
defaults_file) == 0) MAX_PATH, defaults_file) == 0)
{ {
WcaLog(LOGMSG_STANDARD, WcaLog(LOGMSG_STANDARD,
"Cannot find datadir in ini file '%S'", defaults_file); "Cannot find datadir in ini file '%S'", defaults_file);
...@@ -311,16 +318,19 @@ void CheckServiceConfig( ...@@ -311,16 +318,19 @@ void CheckServiceConfig(
strip_quotes(current_datadir); strip_quotes(current_datadir);
/* Convert to Windows path */ /* 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 */ /* Add backslash to be compatible with directory formats in MSI */
wcsncat(normalized_current_datadir, L"\\", MAX_PATH+1); 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) 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""); WcaSetProperty(L"CLEANUPDATA", L"");
} }
...@@ -335,22 +345,22 @@ void CheckServiceConfig( ...@@ -335,22 +345,22 @@ void CheckServiceConfig(
would normally mean user has done an upgrade of the database and in this case would normally mean user has done an upgrade of the database and in this case
uninstall should neither delete service nor database directory. uninstall should neither delete service nor database directory.
If this function find that service is modified by user (mysqld.exe used by service If this function find that service is modified by user (mysqld.exe used by
does not point to the installation bin directory), MSI public variable SERVICENAME is service does not point to the installation bin directory), MSI public variable
removed, if DATADIR is used by some other service, variables DATADIR and CLEANUPDATA SERVICENAME is removed, if DATADIR is used by some other service, variables
are removed. DATADIR and CLEANUPDATA are removed.
The effect of variable removal is that service does not get uninstalled and datadir The effect of variable removal is that service does not get uninstalled and
is not touched by uninstallation. datadir is not touched by uninstallation.
Note that this function is running without elevation and does not use anything that would Note that this function is running without elevation and does not use anything
require special privileges. that would require special privileges.
*/ */
extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
{ {
static BYTE buf[256*1024]; /* largest possible buffer for EnumServices */ 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; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
wchar_t *servicename= NULL; wchar_t *servicename= NULL;
...@@ -373,7 +383,8 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) ...@@ -373,7 +383,8 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S", WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S",
servicename, datadir, bindir); 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) if (scm == NULL)
{ {
ExitOnFailure(E_FAIL, "OpenSCManager failed"); ExitOnFailure(E_FAIL, "OpenSCManager failed");
...@@ -397,17 +408,21 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) ...@@ -397,17 +408,21 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
info = (LPENUM_SERVICE_STATUS_PROCESS)buf; info = (LPENUM_SERVICE_STATUS_PROCESS)buf;
for (ULONG i=0; i < num_services; i++) 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) if (!service)
continue; continue;
WcaLog(LOGMSG_VERBOSE, "Checking Service %S", info[i].lpServiceName); 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; DWORD needed;
BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), &needed); BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
&needed);
CloseServiceHandle(service); CloseServiceHandle(service);
if (ok) 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) ...@@ -454,14 +469,17 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
} }
if(CheckServiceExists(ServiceName)) 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; goto err;
} }
} }
DWORD SkipNetworkingLen= MAX_PATH; DWORD SkipNetworkingLen= MAX_PATH;
MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking, &SkipNetworkingLen); MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking,
&SkipNetworkingLen);
MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen); MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen);
if(SkipNetworking[0]==0 && Port[0] != 0) if(SkipNetworking[0]==0 && Port[0] != 0)
...@@ -488,14 +506,17 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) ...@@ -488,14 +506,17 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
} }
if (haveInvalidPort) 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; goto err;
} }
short port = (short)_wtoi(Port); short port = (short)_wtoi(Port);
if (!IsPortFree(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; goto err;
} }
} }
...@@ -550,7 +571,8 @@ extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall) ...@@ -550,7 +571,8 @@ extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall)
/* /*
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 #define MAX_VERSION_PROPERTY_SIZE 64
...@@ -589,7 +611,8 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -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) if (scm == NULL)
{ {
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
...@@ -621,31 +644,30 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -621,31 +644,30 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
QUERY_SERVICE_CONFIGW *config= QUERY_SERVICE_CONFIGW *config=
(QUERY_SERVICE_CONFIGW*)(void *)config_buffer; (QUERY_SERVICE_CONFIGW*)(void *)config_buffer;
DWORD needed; DWORD needed;
BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), &needed); BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
&needed);
CloseServiceHandle(service); CloseServiceHandle(service);
if (ok) if (ok)
{ {
bool isMySQL; mysqld_service_properties props;
int major; if (get_mysql_service_properties(config->lpBinaryPathName, &props))
int minor; continue;
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.
/* */
Only look for services that have mysqld.exe outside of the current if(strstr(props.mysqld_exe,installDir) == 0)
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))
{ {
upgradableServiceFound= true; WcaLog(LOGMSG_STANDARD, "found service %S, major=%d, minor=%d",
break; 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;
}
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment