Move common functionality (analyze service configuration) into winservice library

This commit is contained in:
Vladislav Vaintroub 2011-01-30 22:27:59 +01:00
parent e353bc80f7
commit 366ee3c791
8 changed files with 70 additions and 176 deletions

View File

@ -186,10 +186,13 @@ ADD_CUSTOM_COMMAND(
COMPONENT Server) COMPONENT Server)
TARGET_LINK_LIBRARIES(mysql_install_db mysys strings dbug) TARGET_LINK_LIBRARIES(mysql_install_db mysys strings dbug)
ADD_LIBRARY(winservice STATIC winservice.c)
MYSQL_ADD_EXECUTABLE(mysql_upgrade_service MYSQL_ADD_EXECUTABLE(mysql_upgrade_service
mysql_upgrade_service.cc mysql_upgrade_service.cc
COMPONENT Server) COMPONENT Server)
TARGET_LINK_LIBRARIES(mysql_upgrade_service mysys strings dbug)
TARGET_LINK_LIBRARIES(mysql_upgrade_service mysys strings dbug winservice)
# mysql_install_db should be in the same directory as mysqld # mysql_install_db should be in the same directory as mysqld
# to work correctly # to work correctly

View File

@ -153,6 +153,7 @@ BUILT_SOURCES = $(BUILT_MAINT_SRC) lex_hash.h link_sources
EXTRA_DIST = udf_example.c udf_example.def $(BUILT_MAINT_SRC) \ EXTRA_DIST = udf_example.c udf_example.def $(BUILT_MAINT_SRC) \
nt_servc.cc nt_servc.h mysql_install_db.cc mysql_upgrade_service.cc \ nt_servc.cc nt_servc.h mysql_install_db.cc mysql_upgrade_service.cc \
message.mc message.h message.rc MSG00001.bin \ message.mc message.h message.rc MSG00001.bin \
winservice.c winservice.h
CMakeLists.txt CMakeLists.txt
CLEANFILES = lex_hash.h sql_yacc.output link_sources CLEANFILES = lex_hash.h sql_yacc.output link_sources

View File

@ -26,6 +26,7 @@
#include <my_sys.h> #include <my_sys.h>
#include <m_string.h> #include <m_string.h>
#include <mysql_version.h> #include <mysql_version.h>
#include <winservice.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -38,8 +39,8 @@ static char mysqld_path[MAX_PATH];
static char mysqladmin_path[MAX_PATH]; static char mysqladmin_path[MAX_PATH];
static char mysqlupgrade_path[MAX_PATH]; static char mysqlupgrade_path[MAX_PATH];
static char defaults_file_param[FN_REFLEN]; static char defaults_file_param[MAX_PATH + 16]; /*--defaults-file=<path> */
static char logfile_path[FN_REFLEN]; static char logfile_path[MAX_PATH];
static char *opt_service; static char *opt_service;
static SC_HANDLE service; static SC_HANDLE service;
static SC_HANDLE scm; static SC_HANDLE scm;
@ -301,7 +302,7 @@ end:
/* /*
Shutdown mysql server. Not using mysqladmin, since Shutdown mysql server. Not using mysqladmin, since
our --skip-grant-tables do not work anymore after mysql_upgrade our --skip-grant-tables do not work anymore after mysql_upgrade
that does "flush privileges". Instead, the shutdown handle is set. that does "flush privileges". Instead, the shutdown event is set.
*/ */
void initiate_mysqld_shutdown() void initiate_mysqld_shutdown()
{ {
@ -327,8 +328,7 @@ void initiate_mysqld_shutdown()
*/ */
static void change_service_config() static void change_service_config()
{ {
wchar_t old_mysqld_path[MAX_PATH];
wchar_t *file_part;
char defaults_file[MAX_PATH]; char defaults_file[MAX_PATH];
char default_character_set[64]; char default_character_set[64];
@ -346,59 +346,32 @@ static void change_service_config()
if (!QueryServiceConfigW(service, config, size, &needed)) if (!QueryServiceConfigW(service, config, size, &needed))
die("QueryServiceConfig failed with %d\n", GetLastError()); die("QueryServiceConfig failed with %d\n", GetLastError());
int numargs; mysqld_service_properties props;
LPWSTR *args= CommandLineToArgvW(config->lpBinaryPathName, &numargs); if (get_mysql_service_properties(config->lpBinaryPathName, &props))
char commandline[3*FN_REFLEN +32];
/* Run some checks to ensure we're really upgrading mysql service */
if(numargs != 3)
{ {
die("Expected 3 parameters in service configuration binPath," die("Not a valid MySQL service");
"got %d parameters instead\n. binPath: %S", numargs,
config->lpBinaryPathName);
}
if(wcsncmp(args[1], L"--defaults-file=", 16) != 0)
{
die("Unexpected service configuration, second parameter must start with "
"--defaults-file. binPath= %S", config->lpBinaryPathName);
}
GetFullPathNameW(args[0], MAX_PATH, old_mysqld_path, &file_part);
if(wcsicmp(file_part, L"mysqld.exe") != 0 &&
wcsicmp(file_part, L"mysqld") != 0)
{
die("The service executable is not mysqld. binPath: %S",
config->lpBinaryPathName);
} }
if(wcsicmp(file_part, L"mysqld") == 0)
wcscat_s(old_mysqld_path, L".exe");
int old_mysqld_major, old_mysqld_minor;
get_file_version(old_mysqld_path, &old_mysqld_major, &old_mysqld_minor);
int my_major= MYSQL_VERSION_ID/10000; int my_major= MYSQL_VERSION_ID/10000;
int my_minor= (MYSQL_VERSION_ID - 10000*my_major)/100; int my_minor= (MYSQL_VERSION_ID - 10000*my_major)/100;
if(my_major < old_mysqld_major || if(my_major < props.version_major ||
(my_major == old_mysqld_major && my_minor < old_mysqld_minor)) (my_major == props.version_minor && my_minor < props.version_patch))
{ {
die("Can not downgrade, the service is currently running as version %d.%d" die("Can not downgrade, the service is currently running as version %d.%d"
", my version is %d.%d", old_mysqld_major, old_mysqld_minor, my_major, ", my version is %d.%d", props.version_major, props.version_minor,
my_minor); my_major, my_minor);
} }
wcstombs(defaults_file, args[1] + 16, MAX_PATH);
/* /*
Remove basedir from defaults file, otherwise the service wont come up in Remove basedir from defaults file, otherwise the service wont come up in
the new version, and will complain about mismatched message file. the new version, and will complain about mismatched message file.
*/ */
WritePrivateProfileString("mysqld", "basedir",NULL, defaults_file); WritePrivateProfileString("mysqld", "basedir",NULL, props.inifile);
#ifdef _WIN64 #ifdef _WIN64
/* Currently, pbxt is non-functional on x64 */ /* Currently, pbxt is non-functional on x64 */
WritePrivateProfileString("mysqld", "loose-skip-pbxt","1", defaults_file); WritePrivateProfileString("mysqld", "loose-skip-pbxt","1", props.inifile);
#endif #endif
/* /*
Replace default-character-set with character-set-server, to avoid Replace default-character-set with character-set-server, to avoid
@ -406,7 +379,7 @@ static void change_service_config()
message. message.
*/ */
default_character_set[0]=0; default_character_set[0]=0;
GetPrivateProfileStringA("mysqld", "default-character-set", NULL, GetPrivateProfileString("mysqld", "default-character-set", NULL,
default_character_set, sizeof(default_character_set), defaults_file); default_character_set, sizeof(default_character_set), defaults_file);
if(default_character_set[0]) if(default_character_set[0])
{ {
@ -416,15 +389,16 @@ static void change_service_config()
default_character_set, defaults_file); default_character_set, defaults_file);
} }
sprintf_s(commandline, "\"%s\" \"%S\" \"%S\"", mysqld_path, args[1], args[2]); sprintf(defaults_file_param,"--defaults-file=%s", props.inifile);
char commandline[3*MAX_PATH + 19];
sprintf_s(commandline, "\"%s\" \"%s\" \"%s\"", mysqld_path,
defaults_file_param, opt_service);
if (!ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, if (!ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE, commandline, NULL, NULL, NULL, NULL, NULL, NULL)) SERVICE_NO_CHANGE, commandline, NULL, NULL, NULL, NULL, NULL, NULL))
{ {
die("ChangeServiceConfigW failed with %d", GetLastError()); die("ChangeServiceConfigW failed with %d", GetLastError());
} }
sprintf_s(defaults_file_param, "%S", args[1]);
LocalFree(args);
} }
@ -483,13 +457,13 @@ int main(int argc, char **argv)
for communication, for security reasons. for communication, for security reasons.
*/ */
char socket_param[FN_REFLEN]; char socket_param[FN_REFLEN];
sprintf_s(socket_param,"--shared_memory_base_name=mysql_upgrade_service_%d", sprintf_s(socket_param,"--socket=mysql_upgrade_service_%d",
GetCurrentProcessId()); GetCurrentProcessId());
log("Phase 3/8: Starting mysqld for upgrade"); log("Phase 3/8: Starting mysqld for upgrade");
mysqld_process= (HANDLE)run_tool(P_NOWAIT, mysqld_path, mysqld_process= (HANDLE)run_tool(P_NOWAIT, mysqld_path,
defaults_file_param, "--skip-networking", "--skip-grant-tables", defaults_file_param, "--skip-networking", "--skip-grant-tables",
"--enable-shared-memory", socket_param, NULL); "--enable-named-pipe", socket_param, NULL);
if(mysqld_process == INVALID_HANDLE_VALUE) if(mysqld_process == INVALID_HANDLE_VALUE)
{ {
@ -503,7 +477,7 @@ int main(int argc, char **argv)
if (WaitForSingleObject(mysqld_process, 0) != WAIT_TIMEOUT) if (WaitForSingleObject(mysqld_process, 0) != WAIT_TIMEOUT)
die("mysqld.exe did not start"); die("mysqld.exe did not start");
if (run_tool(P_WAIT, mysqladmin_path, "--protocol=memory", if (run_tool(P_WAIT, mysqladmin_path, "--protocol=pipe",
socket_param, "ping", NULL) == 0) socket_param, "ping", NULL) == 0)
{ {
break; break;
@ -516,7 +490,7 @@ int main(int argc, char **argv)
log("Phase 5/8: Running mysql_upgrade"); log("Phase 5/8: Running mysql_upgrade");
int upgrade_err = (int) run_tool(P_WAIT, mysqlupgrade_path, int upgrade_err = (int) run_tool(P_WAIT, mysqlupgrade_path,
"--protocol=memory", "--force", socket_param, "--protocol=pipe", "--force", socket_param,
NULL); NULL);
log("Phase 6/8: Initiating server shutdown"); log("Phase 6/8: Initiating server shutdown");

View File

@ -23,6 +23,7 @@ FIND_PATH(WIX_DIR heat.exe
$ENV{ProgramFiles}/wix/bin $ENV{ProgramFiles}/wix/bin
"$ENV{ProgramFiles}/Windows Installer XML v3/bin" "$ENV{ProgramFiles}/Windows Installer XML v3/bin"
"$ENV{ProgramFiles}/Windows Installer XML v3.5/bin" "$ENV{ProgramFiles}/Windows Installer XML v3.5/bin"
"$ENV{ProgramFiles}/Windows Installer XML v3.6/bin"
) )
SET(CPACK_WIX_PACKAGE_BASE_NAME "MariaDB") SET(CPACK_WIX_PACKAGE_BASE_NAME "MariaDB")

View File

@ -18,6 +18,7 @@ LINK_DIRECTORIES(${WIX_DIR}/../SDK/lib)
SET(WIXCA_SOURCES CustomAction.cpp CustomAction.def) SET(WIXCA_SOURCES CustomAction.cpp CustomAction.def)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql)
IF(CMAKE_SIZEOF_VOID_P EQUAL 8) IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET(WIX_ARCH_SUFFIX "_x64") SET(WIX_ARCH_SUFFIX "_x64")
@ -47,4 +48,4 @@ FIND_LIBRARY(WIX_DUTIL_LIBRARY
ADD_VERSION_INFO(wixca SHARED WIXCA_SOURCES) ADD_VERSION_INFO(wixca SHARED WIXCA_SOURCES)
ADD_LIBRARY(wixca SHARED EXCLUDE_FROM_ALL ${WIXCA_SOURCES}) ADD_LIBRARY(wixca SHARED EXCLUDE_FROM_ALL ${WIXCA_SOURCES})
TARGET_LINK_LIBRARIES(wixca ${WIX_WCAUTIL_LIBRARY} ${WIX_DUTIL_LIBRARY} TARGET_LINK_LIBRARIES(wixca ${WIX_WCAUTIL_LIBRARY} ${WIX_DUTIL_LIBRARY}
msi version) msi version winservice)

View File

@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include <strsafe.h> #include <strsafe.h>
#include <assert.h> #include <assert.h>
#include <winservice.h>
UINT ExecRemoveDataDirectory(wchar_t *dir) UINT ExecRemoveDataDirectory(wchar_t *dir)
{ {
@ -547,49 +548,6 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
/*
Extract major and minor version from
mysqld.exe, using commandline in service definition
*/
static void GetMySQLVersion(
wchar_t *cmdline,
wchar_t *programBuf,
bool *isMySQL, int *major, int *minor)
{
*major= 0;
*minor= 0;
*isMySQL= false;
int argc;
wchar_t **wargv = CommandLineToArgvW(cmdline, &argc);
if(argc != 3)
return;
wchar_t path[MAX_PATH];
wchar_t *filepart;
wcscpy_s(programBuf, MAX_PATH, wargv[0]);
if(!wcsstr(programBuf, L".exe"))
wcscat_s(programBuf,MAX_PATH, L".exe");
GetFullPathNameW(programBuf,MAX_PATH, path, &filepart);
if(wcsicmp(filepart, L"mysqld.exe") == 0)
{
*isMySQL = true;
DWORD handle;
DWORD size = GetFileVersionInfoSizeW(path, &handle);
BYTE* versionInfo = new BYTE[size];
if (GetFileVersionInfo(path, handle, size, versionInfo))
{
UINT len = 0;
VS_FIXEDFILEINFO* vsfi = NULL;
VerQueryValueW(versionInfo, L"\\", (void**)&vsfi, &len);
*major= HIWORD(vsfi->dwFileVersionMS);
*minor= LOWORD(vsfi->dwFileVersionMS);
}
delete[] versionInfo;
}
}
/* /*
Enables/disables optional "Launch upgrade wizard" checkbox at the end of installation Enables/disables optional "Launch upgrade wizard" checkbox at the end of installation
@ -603,7 +561,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
wchar_t* service= 0; wchar_t* service= 0;
wchar_t* dir= 0; wchar_t* dir= 0;
wchar_t installerVersion[MAX_VERSION_PROPERTY_SIZE]; wchar_t installerVersion[MAX_VERSION_PROPERTY_SIZE];
wchar_t installDir[MAX_PATH]; char installDir[MAX_PATH];
DWORD size =MAX_VERSION_PROPERTY_SIZE; DWORD size =MAX_VERSION_PROPERTY_SIZE;
int installerMajorVersion, installerMinorVersion, installerPatchVersion; int installerMajorVersion, installerMinorVersion, installerPatchVersion;
bool upgradableServiceFound=false; bool upgradableServiceFound=false;
@ -623,7 +581,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
} }
size= MAX_PATH; size= MAX_PATH;
if (MsiGetPropertyW(hInstall,L"INSTALLDIR", installDir, &size) if (MsiGetPropertyA(hInstall,"INSTALLDIR", installDir, &size)
!= ERROR_SUCCESS) != ERROR_SUCCESS)
{ {
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());

View File

@ -18,9 +18,11 @@ SET(CMAKE_MFC_FLAG 1)
# Enable exception handling (avoids warnings) # Enable exception handling (avoids warnings)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
MYSQL_ADD_EXECUTABLE(upgrade_wizard upgrade.cpp upgradeDlg.cpp upgrade.rc INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql)
MYSQL_ADD_EXECUTABLE(upgrade_wizard
upgrade.cpp upgradeDlg.cpp upgrade.rc
COMPONENT Server) COMPONENT Server)
TARGET_LINK_LIBRARIES(upgrade_wizard winservice)
# upgrade_wizard is Windows executable, set WIN32_EXECUTABLE so it does not # upgrade_wizard is Windows executable, set WIN32_EXECUTABLE so it does not
# create a console. # create a console.
SET_TARGET_PROPERTIES(upgrade_wizard PROPERTIES WIN32_EXECUTABLE 1) SET_TARGET_PROPERTIES(upgrade_wizard PROPERTIES WIN32_EXECUTABLE 1)

View File

@ -14,6 +14,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <winservice.h>
using namespace std; using namespace std;
#ifdef _DEBUG #ifdef _DEBUG
@ -75,7 +77,7 @@ vector<ServiceProperties> services;
<unknown> , of executable does not have any version <unknown> , of executable does not have any version
info embedded (like MySQL 5.1 for example) info embedded (like MySQL 5.1 for example)
*/ */
string GetExeVersion(const string& filename, int *major, int *minor, int *patch) void GetExeVersion(const string& filename, int *major, int *minor, int *patch)
{ {
DWORD handle; DWORD handle;
*major= *minor= *patch= 0; *major= *minor= *patch= 0;
@ -85,20 +87,17 @@ string GetExeVersion(const string& filename, int *major, int *minor, int *patch)
if (!GetFileVersionInfo(filename.c_str(), handle, size, versionInfo)) if (!GetFileVersionInfo(filename.c_str(), handle, size, versionInfo))
{ {
delete[] versionInfo; delete[] versionInfo;
return "<unknown>"; return;
} }
// we have version information // we have version information
UINT len = 0; UINT len = 0;
VS_FIXEDFILEINFO* vsfi = NULL; VS_FIXEDFILEINFO* vsfi = NULL;
VerQueryValue(versionInfo, "\\", (void**)&vsfi, &len); VerQueryValue(versionInfo, "\\", (void**)&vsfi, &len);
char arr[64];
*major= (int)HIWORD(vsfi->dwFileVersionMS); *major= (int)HIWORD(vsfi->dwFileVersionMS);
*minor= (int)LOWORD(vsfi->dwFileVersionMS); *minor= (int)LOWORD(vsfi->dwFileVersionMS);
*patch= (int)HIWORD(vsfi->dwFileVersionLS); *patch= (int)HIWORD(vsfi->dwFileVersionLS);
sprintf_s(arr,"%d.%d.%d", *major, *minor, *patch);
delete[] versionInfo; delete[] versionInfo;
return string(arr);
} }
@ -122,19 +121,6 @@ void CUpgradeDlg::SelectService(int index)
} }
/* Remove quotes from string */
static char *RemoveQuotes(char *s)
{
if(s[0]=='"')
{
s++;
char *p= strchr(s, '"');
if(p)
*p= 0;
}
return s;
}
/* /*
Iterate over services, lookup for mysqld.exe ones. Iterate over services, lookup for mysqld.exe ones.
@ -157,8 +143,7 @@ void CUpgradeDlg::PopulateServicesList()
static BYTE buf[64*1024]; static BYTE buf[64*1024];
static BYTE configBuffer[8*1024]; static BYTE configBuffer[8*1024];
char datadirBuf[MAX_PATH];
char datadirNormalized[MAX_PATH];
DWORD bufsize= sizeof(buf); DWORD bufsize= sizeof(buf);
DWORD bufneed; DWORD bufneed;
DWORD num_services; DWORD num_services;
@ -184,70 +169,39 @@ void CUpgradeDlg::PopulateServicesList()
CloseServiceHandle(service); CloseServiceHandle(service);
if (ok) if (ok)
{ {
int argc; mysqld_service_properties service_props;
wchar_t **wargv = CommandLineToArgvW(config->lpBinaryPathName, &argc);
// We expect path\to\mysqld --defaults-file=<path> <servicename> if (get_mysql_service_properties(config->lpBinaryPathName,
if(argc == 3) &service_props))
continue;
/* Check if service uses mysqld in installation directory */
if (_strnicmp(service_props.mysqld_exe, m_InstallDir.c_str(),
m_InstallDir.size()) == 0)
continue;
if(m_MajorVersion > service_props.version_major ||
(m_MajorVersion == service_props.version_major && m_MinorVersion >=
service_props.version_minor))
{ {
ServiceProperties props;
// Convert wide strings to ANSI props.myini= service_props.inifile;
char *argv[3]; props.datadir= service_props.datadir;
for(int k=0; k < 3;k++) props.servicename = info[i].lpServiceName;
if (service_props.version_major)
{ {
size_t nbytes = 2*wcslen(wargv[k])+1; char ver[64];
argv[k]= new char[nbytes]; sprintf(ver, "%d.%d.%d", service_props.version_major,
wcstombs(argv[k], wargv[k], nbytes); service_props.version_minor, service_props.version_patch);
props.version= ver;
} }
else
props.version= "<unknown>";
size_t len= strlen(argv[0]); index = m_Services.AddString(info[i].lpServiceName);
char path[MAX_PATH]={0}; services.resize(index+1);
char *filepart; services[index] = props;
GetFullPathName(argv[0],MAX_PATH, path, &filepart);
if(_stricmp(filepart, "mysqld.exe") == 0 ||
_stricmp(filepart, "mysqld") == 0)
{
if(_strnicmp(argv[1],"--defaults-file=",16) == 0)
{
/* Remove quotes around defaults-file */
char *inifile= argv[1] + 16;
inifile = RemoveQuotes(inifile);
char *datadir=datadirBuf;
GetPrivateProfileString("mysqld", "datadir", NULL, datadirBuf,
MAX_PATH, inifile);
/* Remove quotes from datadir */
datadir= RemoveQuotes(datadir);
GetFullPathName(datadir, MAX_PATH, datadirNormalized, NULL);
ServiceProperties props;
props.myini = inifile;
props.servicename = info[i].lpServiceName;
string exefilename(argv[0]);
if(!strstr(argv[0], ".exe"))
exefilename += ".exe";
int major, minor, patch;
props.version= GetExeVersion(exefilename, &major, &minor, &patch);
if(m_MajorVersion > major ||
(m_MajorVersion == major && m_MinorVersion >= minor))
{
if (_strnicmp(exefilename.c_str(), m_InstallDir.c_str(),
m_InstallDir.size()) != 0)
{
props.datadir = datadirNormalized;
index = m_Services.AddString(info[i].lpServiceName);
services.resize(index+1);
services[index] = props;
}
}
}
}
for(int k=0; k< 3;k++)
delete[] argv[k];
} }
LocalFree((HLOCAL)wargv);
} }
if (index != -1) if (index != -1)
{ {