split long lines, use get_mysql_service_properties()
This commit is contained in:
parent
366ee3c791
commit
1b28a0883d
167
sql/winservice.c
Normal file
167
sql/winservice.c
Normal file
@ -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;
|
||||
}
|
24
sql/winservice.h
Normal file
24
sql/winservice.h
Normal file
@ -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
|
0
win/cmake/dummy.in
Normal file
0
win/cmake/dummy.in
Normal file
@ -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.
|
||||
- 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.
|
||||
"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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user