Skip to content

Commit 2dcb823

Browse files
committed
MDEV-26713 Windows- UTF8 encoding in the installer
Workaround Windows' bug in services "ANSI" when process ANSI codepage is UTF8. They turn out to work unlike any other API . Expected behavior : strings be converted from GetACP() to Unicode, and "wide" function would be then called. Actual current behavior : it seems to handle strings as-if they would be encoded in system-default ACP, rather than process-specific GetACP() Fix: redefine the OpenService,CreateService and ChangeServiceConfig and do ANSI-Wide conversion outselves. Tell compiler to deprecate some ANSI service functions. xxx
1 parent 68b16d8 commit 2dcb823

File tree

6 files changed

+194
-25
lines changed

6 files changed

+194
-25
lines changed

cmake/os/Windows.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ ENDIF()
6060

6161
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
6262
ADD_DEFINITIONS(-D_WIN32_WINNT=0x0A00)
63-
# We do not want the windows.h macros min/max
64-
ADD_DEFINITIONS(-DNOMINMAX)
63+
# We do not want the windows.h , or winsvc.h macros min/max
64+
ADD_DEFINITIONS(-DNOMINMAX -DNOSERVICE)
6565
# Speed up build process excluding unused header files
6666
ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN)
6767

include/my_global.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929
#pragma GCC poison __WIN__
3030
#endif
3131

32+
#if defined(_MSC_VER)
33+
/*
34+
Following functions have bugs, when used with UTF-8 active codepage.
35+
#include <winservice.h> will use the non-buggy wrappers
36+
*/
37+
#pragma deprecated("CreateServiceA", "OpenServiceA", "ChangeServiceConfigA")
38+
#endif
39+
3240
/*
3341
InnoDB depends on some MySQL internals which other plugins should not
3442
need. This is because of InnoDB's foreign key support, "safe" binlog

sql/mysql_install_db.cc

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <sddl.h>
3232
struct IUnknown;
3333
#include <shlwapi.h>
34+
#include <winservice.h>
3435

3536
#include <string>
3637

@@ -531,11 +532,9 @@ static int handle_user_privileges(const char *account_name, const wchar_t *privi
531532

532533
/* Register service. Assume my.ini is in datadir */
533534

534-
static int register_service(const char *datadir, const char *user)
535+
static int register_service(const char *datadir, const char *user, const char *passwd)
535536
{
536-
#define MAX_SERVICE_STRING_LEN 3 * MAX_PATH + 32
537-
char buf[MAX_SERVICE_STRING_LEN]; /* path to mysqld.exe, to my.ini, service name */
538-
537+
char buf[3*MAX_PATH +32]; /* path to mysqld.exe, to my.ini, service name */
539538
SC_HANDLE sc_manager, sc_service;
540539

541540
size_t datadir_len= strlen(datadir);
@@ -556,17 +555,10 @@ static int register_service(const char *datadir, const char *user)
556555
die("OpenSCManager failed (%u)\n", GetLastError());
557556
}
558557

559-
/* Windows bug with utf8 ANSI codepage - CreateServiceA is not really UTF8. */
560-
wchar_t wbuf[MAX_SERVICE_STRING_LEN];
561-
MultiByteToWideChar(CP_ACP, 0, buf, -1, wbuf, MAX_SERVICE_STRING_LEN);
562-
wchar_t wservice[2 * MAX_PATH];
563-
MultiByteToWideChar(CP_ACP, 0, opt_service, -1, wservice, MAX_PATH);
564-
wchar_t wuser[MAX_PATH];
565-
MultiByteToWideChar(CP_ACP, 0, user, -1, wuser, MAX_PATH);
566558
/* Create the service. */
567-
sc_service= CreateServiceW(sc_manager, wservice, wservice,
559+
sc_service= CreateService(sc_manager, opt_service, opt_service,
568560
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
569-
SERVICE_ERROR_NORMAL, wbuf, NULL, NULL, NULL, wuser, NULL);
561+
SERVICE_ERROR_NORMAL, buf, NULL, NULL, NULL, user, passwd);
570562

571563
if (!sc_service)
572564
{
@@ -779,7 +771,7 @@ static int create_db_instance(const char *datadir)
779771
{
780772
/* Run service under virtual account NT SERVICE\service_name.*/
781773
service_user.append("NT SERVICE\\").append(opt_service);
782-
ret = register_service(datadir, service_user.c_str());
774+
ret = register_service(datadir, service_user.c_str(), NULL);
783775
if (ret)
784776
goto end;
785777
service_created = true;

sql/winmain.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include <windows.h>
5656
#include <string>
5757
#include <cassert>
58+
#include <winservice.h>
5859

5960
static SERVICE_STATUS svc_status{SERVICE_WIN32_OWN_PROCESS};
6061
static SERVICE_STATUS_HANDLE svc_status_handle;

sql/winservice.h

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
extern "C" {
2222
#endif
2323

24-
#include <windows.h>
24+
#include <windows.h>
25+
#pragma warning(suppress : 4995)
26+
#include <winsvc.h>
2527
typedef struct mysqld_service_properties_st
2628
{
2729
char mysqld_exe[MAX_PATH];
@@ -32,9 +34,171 @@ typedef struct mysqld_service_properties_st
3234
int version_patch;
3335
} mysqld_service_properties;
3436

35-
extern int get_mysql_service_properties(const wchar_t *bin_path,
37+
extern int get_mysql_service_properties(const wchar_t *bin_path,
3638
mysqld_service_properties *props);
3739

40+
#if !defined(UNICODE)
41+
#include <malloc.h>
42+
/*
43+
The following wrappers workaround Windows bugs
44+
with CreateService/OpenService with ANSI codepage UTF8.
45+
46+
Apparently, these function in ANSI mode, for this codepage only
47+
do *not* behave as expected (as-if string parameters were
48+
converted to UTF16 and "wide" function were called)
49+
*/
50+
static inline wchar_t* awstrdup(const char *str)
51+
{
52+
if (!str)
53+
return NULL;
54+
size_t len= strlen(str) + 1;
55+
wchar_t *wstr= (wchar_t *) malloc(sizeof(wchar_t)*len);
56+
if (MultiByteToWideChar(CP_ACP, 0, str, (int)len, wstr, (int)len) == 0)
57+
{
58+
free(wstr);
59+
return NULL;
60+
}
61+
return wstr;
62+
}
63+
64+
#define AWSTRDUP(dest, src) \
65+
dest= awstrdup(src); \
66+
if (src && !dest) \
67+
{ \
68+
ok= FALSE; \
69+
last_error = ERROR_OUTOFMEMORY; \
70+
goto end; \
71+
}
72+
73+
static inline SC_HANDLE my_OpenService(SC_HANDLE hSCManager, LPCSTR lpServiceName, DWORD dwDesiredAccess)
74+
{
75+
wchar_t *w_ServiceName= NULL;
76+
BOOL ok=TRUE;
77+
DWORD last_error;
78+
SC_HANDLE sch;
79+
80+
AWSTRDUP(w_ServiceName, lpServiceName);
81+
sch= OpenServiceW(hSCManager, w_servicename, dwDesiredAccess);
82+
if (!sch)
83+
{
84+
ok= FALSE;
85+
last_error= GetLastError();
86+
}
87+
88+
end:
89+
free(w_ServiceName);
90+
if (!ret)
91+
SetLastError(last_error);
92+
return ret;
93+
}
94+
95+
static inline SC_HANDLE my_CreateService(SC_HANDLE hSCManager, LPCSTR lpServiceName, LPCSTR lpDisplayName,
96+
DWORD dwDesiredAccess, DWORD dwServiceType,
97+
DWORD dwStartType, DWORD dwErrorControl,
98+
LPCSTR lpBinaryPathName,
99+
LPCSTR lpLoadOrderGroup,
100+
LPDWORD lpdwTagId,
101+
LPCSTR lpDependencies,
102+
LPCSTR lpServiceStartName,
103+
LPCSTR lpPassword)
104+
{
105+
wchar_t *w_ServiceName= NULL;
106+
wchar_t *w_DisplayName= NULL;
107+
wchar_t *w_BinaryPathName= NULL;
108+
wchar_t *w_LoadOrderGroup= NULL;
109+
wchar_t *w_Depedencies= NULL;
110+
wchar_t *w_ServiceStartName= NULL;
111+
wchar_t *w_Password= NULL;
112+
SC_HANDLE sch;
113+
DWORD last_error;
114+
BOOL ok= TRUE;
115+
116+
AWSTRDUP(w_ServiceName,lpServiceName);
117+
AWSTRDUP(w_DisplayName,lpDisplayName);
118+
AWSTRDUP(w_BinaryPathName, lpBinaryPathName);
119+
AWSTRDUP(w_LoadOrderGroup, lpLoadOrderGroup);
120+
AWSTRDUP(w_Dependencies, lpDependencies);
121+
AWSTRDUP(w_ServiceStartName, lpServiceStartName);
122+
AWSTRDUP(w_Password, lpPassword);
123+
124+
sch= CreateServiceW(
125+
hSCManager, w_ServiceName, w_DisplayName, dwDesiredAccess, dwServiceType,
126+
dwStartType, dwErrorControl, w_BinaryPathName, w_LoadOrderGroup,
127+
lpdwTagId, w_Depedencies, w_ServiceStartName, w_Password);
128+
if(!sch)
129+
{
130+
ok= FALSE;
131+
last_error= GetLastError();
132+
}
133+
134+
end:
135+
free(w_ServiceName);
136+
free(w_DisplayName);
137+
free(w_BinaryPathName);
138+
free(w_LoadOrderGroup);
139+
free(w_Depedencies);
140+
free(w_ServiceStartName);
141+
free(w_Password);
142+
143+
if (!ok)
144+
SetLastError(last_error);
145+
return sch;
146+
}
147+
148+
static inline BOOL my_ChangeServiceConfig(SC_HANDLE hService, DWORD dwServiceType,
149+
DWORD dwStartType, DWORD dwErrorControl,
150+
LPCSTR lpBinaryPathName, LPCSTR lpLoadOrderGroup,
151+
LPDWORD lpdwTagId, LPCSTR lpDependencies,
152+
LPCSTR lpServiceStartName, LPCSTR lpPassword,
153+
LPCSTR lpDisplayName)
154+
{
155+
wchar_t *w_DisplayName= NULL;
156+
wchar_t *w_BinaryPathName= NULL;
157+
wchar_t *w_LoadOrderGroup= NULL;
158+
wchar_t *w_Depedencies= NULL;
159+
wchar_t *w_ServiceStartName= NULL;
160+
wchar_t *w_Password= NULL;
161+
SC_HANDLE sch;
162+
DWORD last_error;
163+
BOOL ok= TRUE;
164+
165+
AWSTRDUP(w_DisplayName, lpDisplayName);
166+
AWSTRDUP(w_BinaryPathName, lpBinaryPathName);
167+
AWSTRDUP(w_LoadOrderGroup, lpLoadOrderGroup);
168+
AWSTRDUP(w_Dependencies, lpDependencies);
169+
AWSTRDUP(w_ServiceStartName, lpServiceStartName);
170+
AWSTRDUP(w_Password, lpPassword);
171+
172+
ok= ChangeServiceConfigW(
173+
hService, dwServiceType, dwStartType, dwErrorControl, w_BinaryPathName,
174+
w_LoadOrderGroup, lpdwTagId, w_Depedencies, w_ServiceStartName,
175+
w_Password, w_DisplayName);
176+
if (!ok)
177+
{
178+
last_error= GetLastError();
179+
}
180+
181+
end:
182+
free(w_DisplayName);
183+
free(w_BinaryPathName);
184+
free(w_LoadOrderGroup);
185+
free(w_Depedencies);
186+
free(w_ServiceStartName);
187+
free(w_Password);
188+
189+
if (last_error)
190+
SetLastError(last_error);
191+
return ret;
192+
}
193+
194+
#undef OpenService
195+
#define OpenService my_OpenService
196+
#undef ChangeServiceConfig
197+
#define ChangeServiceConfig my_ChangeServiceConfig
198+
#undef CreateService
199+
#define CreateService my_CreateService
200+
#endif
201+
38202
#ifdef __cplusplus
39203
}
40204
#endif

win/upgrade_wizard/upgradeDlg.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,24 +141,24 @@ void CUpgradeDlg::PopulateServicesList()
141141
ErrorExit("OpenSCManager failed");
142142
}
143143

144-
static BYTE buf[64*1024];
144+
static BYTE buf[2*64*1024];
145145
static BYTE configBuffer[8*1024];
146146

147147
DWORD bufsize= sizeof(buf);
148148
DWORD bufneed;
149149
DWORD num_services;
150-
BOOL ok= EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
150+
BOOL ok= EnumServicesStatusExW(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
151151
SERVICE_STATE_ALL, buf, bufsize, &bufneed, &num_services, NULL, NULL);
152152
if(!ok)
153153
ErrorExit("EnumServicesStatusEx failed");
154154

155155

156-
LPENUM_SERVICE_STATUS_PROCESS info =
157-
(LPENUM_SERVICE_STATUS_PROCESS)buf;
156+
LPENUM_SERVICE_STATUS_PROCESSW info =
157+
(LPENUM_SERVICE_STATUS_PROCESSW)buf;
158158
int index=-1;
159159
for (ULONG i=0; i < num_services; i++)
160160
{
161-
SC_HANDLE service= OpenService(scm, info[i].lpServiceName,
161+
SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName,
162162
SERVICE_QUERY_CONFIG);
163163
if (!service)
164164
continue;
@@ -187,7 +187,11 @@ void CUpgradeDlg::PopulateServicesList()
187187
ServiceProperties props;
188188
props.myini= service_props.inifile;
189189
props.datadir= service_props.datadir;
190-
props.servicename = info[i].lpServiceName;
190+
char service_name_buf[1024];
191+
WideCharToMultiByte(CP_ACP, 0, info[i].lpServiceName, -1,
192+
service_name_buf, sizeof(service_name_buf),
193+
0, 0);
194+
props.servicename= service_name_buf;
191195
if (service_props.version_major)
192196
{
193197
char ver[64];
@@ -198,7 +202,7 @@ void CUpgradeDlg::PopulateServicesList()
198202
else
199203
props.version= "<unknown>";
200204

201-
index = m_Services.AddString(info[i].lpServiceName);
205+
index = m_Services.AddString(service_name_buf);
202206
services.resize(index+1);
203207
services[index] = props;
204208
}

0 commit comments

Comments
 (0)