This commit is contained in:
Vladislav Vaintroub 2021-11-18 17:19:52 +01:00
parent 2dcb823631
commit 220dc1fd59
7 changed files with 283 additions and 79 deletions

View file

@ -38,7 +38,7 @@ static double getopt_double(char *arg, const struct my_option *optp, int *err);
static void init_variables(const struct my_option *, init_func_p);
static void init_one_value(const struct my_option *, void *, longlong);
static void fini_one_value(const struct my_option *, void *, longlong);
static int setval(const struct my_option *, void *, char *, my_bool);
static int setval(const struct my_option *, void *, char *, my_bool, const char *);
static char *check_struct_option(char *cur_arg, char *key_name);
/*
@ -133,6 +133,48 @@ double getopt_ulonglong2double(ulonglong v)
return u.dbl;
}
#ifdef _WIN32
/**
On Windows, if program is running in UTF8 mode, but some arguments are not UTF8.
This will mostly likely be a sign of old "ANSI" my.ini, and it is likely that
something will go wrong, e.g file access error.
*/
static void validate_value(const char *key, const char *value,
const char *filename)
{
MY_STRCOPY_STATUS status;
const struct charset_info_st *cs= &my_charset_utf8mb4_bin;
size_t len;
if (GetACP() != CP_UTF8)
return;
len= strlen(value);
if (!len)
return;
cs->cset->well_formed_char_length(cs, value, value + len, len, &status);
if (!status.m_well_formed_error_pos)
return;
if (filename && *filename)
{
my_getopt_error_reporter(WARNING_LEVEL,
"%s: invalid (non-UTF8) characters found for option '%s'"
" in file '%s'",
my_progname, key, filename);
}
else
{
DBUG_ASSERT(0);
my_getopt_error_reporter(
WARNING_LEVEL, "%s: invalid (non-UTF8) characters for option %s",
my_progname, key);
}
}
#else
#define validate_value(key, value, filename) (void)filename
#endif
/**
Handle command line options.
Sort options.
@ -564,7 +606,7 @@ int handle_options(int *argc, char ***argv, const struct my_option *longopts,
}
}
if ((error= setval(optp, optp->value, argument,
set_maximum_value)))
set_maximum_value,filename)))
DBUG_RETURN(error);
if (get_one_option(optp, argument, filename))
DBUG_RETURN(EXIT_UNSPECIFIED_ERROR);
@ -610,7 +652,7 @@ int handle_options(int *argc, char ***argv, const struct my_option *longopts,
continue;
}
if ((!option_is_autoset) &&
((error= setval(optp, value, argument, set_maximum_value))) &&
((error= setval(optp, value, argument, set_maximum_value,filename))) &&
!option_is_loose)
DBUG_RETURN(error);
if (get_one_option(optp, argument, filename))
@ -711,7 +753,7 @@ static my_bool get_bool_argument(const struct my_option *opts,
*/
static int setval(const struct my_option *opts, void *value, char *argument,
my_bool set_maximum_value)
my_bool set_maximum_value, const char *option_file)
{
int err= 0, res= 0;
DBUG_ENTER("setval");
@ -858,6 +900,7 @@ static int setval(const struct my_option *opts, void *value, char *argument,
goto ret;
};
}
validate_value(opts->name, argument, option_file);
DBUG_RETURN(0);
ret:

View file

@ -366,7 +366,6 @@ static void get_service_config()
*/
static void change_service_config()
{
char defaults_file[MAX_PATH];
char default_character_set[64];
char buf[MAX_PATH];
char commandline[3 * MAX_PATH + 19];
@ -376,13 +375,17 @@ static void change_service_config()
Write datadir to my.ini, after converting backslashes to
unix style slashes.
*/
strcpy_s(buf, MAX_PATH, service_properties.datadir);
for(i= 0; buf[i]; i++)
if (service_properties.datadir[0])
{
if (buf[i] == '\\')
buf[i]= '/';
strcpy_s(buf, MAX_PATH, service_properties.datadir);
for (i= 0; buf[i]; i++)
{
if (buf[i] == '\\')
buf[i]= '/';
}
WritePrivateProfileString("mysqld", "datadir", buf,
service_properties.inifile);
}
WritePrivateProfileString("mysqld", "datadir",buf, service_properties.inifile);
/*
Remove basedir from defaults file, otherwise the service wont come up in
@ -397,19 +400,19 @@ static void change_service_config()
*/
default_character_set[0]= 0;
GetPrivateProfileString("mysqld", "default-character-set", NULL,
default_character_set, sizeof(default_character_set), defaults_file);
default_character_set, sizeof(default_character_set), service_properties.inifile);
if (default_character_set[0])
{
WritePrivateProfileString("mysqld", "default-character-set", NULL,
defaults_file);
service_properties.inifile);
WritePrivateProfileString("mysqld", "character-set-server",
default_character_set, defaults_file);
default_character_set, service_properties.inifile);
}
sprintf(defaults_file_param,"--defaults-file=%s", service_properties.inifile);
sprintf_s(commandline, "\"%s\" \"%s\" \"%s\"", mysqld_path,
defaults_file_param, opt_service);
if (!ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
if (!my_ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE, commandline, NULL, NULL, NULL, NULL, NULL, NULL))
{
die("ChangeServiceConfig failed with %u", GetLastError());
@ -483,13 +486,8 @@ int main(int argc, char **argv)
}
}
old_mysqld_exe_exists = (GetFileAttributes(service_properties.mysqld_exe) != INVALID_FILE_ATTRIBUTES);
log("Phase %d/%d: Fixing server config file%s", ++phase, max_phases, my_ini_exists ? "" : "(skipped)");
snprintf(my_ini_bck, sizeof(my_ini_bck), "%s.BCK", service_properties.inifile);
CopyFile(service_properties.inifile, my_ini_bck, FALSE);
upgrade_config_file(service_properties.inifile);
old_mysqld_exe_exists= (GetFileAttributes(service_properties.mysqld_exe) !=
INVALID_FILE_ATTRIBUTES);
bool do_start_stop_server = old_mysqld_exe_exists && initial_service_state != SERVICE_RUNNING;
log("Phase %d/%d: Start and stop server in the old version, to avoid crash recovery %s", ++phase, max_phases,
@ -544,6 +542,14 @@ int main(int argc, char **argv)
start_duration_ms += 500;
}
}
log("Phase %d/%d: Fixing server config file%s", ++phase, max_phases,
my_ini_exists ? "" : "(skipped)");
snprintf(my_ini_bck, sizeof(my_ini_bck), "%s.BCK",
service_properties.inifile);
CopyFile(service_properties.inifile, my_ini_bck, FALSE);
upgrade_config_file(service_properties.inifile);
/*
Start mysqld.exe as non-service skipping privileges (so we do not
care about the password). But disable networking and enable pipe

View file

@ -127,6 +127,7 @@
#ifdef _WIN32
#include <handle_connections_win.h>
#include <sddl.h>
#include <winservice.h> /* SERVICE_STOPPED, SERVICE_RUNNING etc */
#endif
#include <my_service_manager.h>

View file

@ -158,51 +158,196 @@ static int cmp_strings(const void* a, const void *b)
return strcmp((const char *)a, *(const char **)b);
}
/**
Convert file from a previous version, by removing
*/
int upgrade_config_file(const char *myini_path)
static bool is_utf8_str(const char *s)
{
#define MY_INI_SECTION_SIZE 32*1024 +3
static char section_data[MY_INI_SECTION_SIZE];
for (const char *section_name : { "mysqld","server","mariadb" })
const unsigned char *bytes= (const unsigned char *) s;
int num;
while (*bytes)
{
DWORD size = GetPrivateProfileSection(section_name, section_data, MY_INI_SECTION_SIZE, myini_path);
if (size == MY_INI_SECTION_SIZE - 2)
if ((*bytes & 0x80) == 0x00)
num= 1;
else if ((*bytes & 0xE0) == 0xC0)
num= 2;
else if ((*bytes & 0xF0) == 0xE0)
num= 3;
else if ((*bytes & 0xF8) == 0xF0)
num= 4;
else
return false;
bytes++;
for (int i= 1; i < num; i++)
{
return -1;
if ((*bytes & 0xC0) != 0x80)
return false;
bytes++;
}
}
return true;
}
for (char *keyval = section_data; *keyval; keyval += strlen(keyval) + 1)
static UINT get_system_acp()
{
static DWORD system_acp;
if (system_acp)
return system_acp;
char str_cp[10];
int cch= GetLocaleInfo(GetSystemDefaultLCID(), LOCALE_IDEFAULTANSICODEPAGE,
str_cp, sizeof(str_cp));
system_acp= cch > 0 ? atoi(str_cp) : 1252;
return system_acp;
}
#define MY_INI_SECTION_SIZE 32 * 1024 + 3
static char *ansi_to_utf8(const char *s)
{
#define MAX_STR_LEN MY_INI_SECTION_SIZE
static wchar_t utf16_buf[MAX_STR_LEN];
static char utf8_buf[MAX_STR_LEN];
if (MultiByteToWideChar(get_system_acp(), 0, s, -1, utf16_buf, MAX_STR_LEN))
{
if (WideCharToMultiByte(CP_UTF8, 0, utf16_buf, -1, utf8_buf, MAX_STR_LEN,
0, 0))
return utf8_buf;
}
return 0;
}
int fix_section(const char *myini_path, const char *section_name,
bool is_server)
{
if (!is_server && GetACP() != CP_UTF8)
return 0;
static char section_data[MY_INI_SECTION_SIZE];
DWORD size= GetPrivateProfileSection(section_name, section_data,
MY_INI_SECTION_SIZE, myini_path);
if (size == MY_INI_SECTION_SIZE - 2)
{
return -1;
}
for (char *keyval= section_data; *keyval; keyval += strlen(keyval)+1)
{
char varname[256];
char *value;
char *key_end= strchr(keyval, '=');
if (!key_end)
key_end= keyval + strlen(keyval);
if (key_end - keyval > sizeof(varname))
continue;
value= key_end + 1;
if (GetACP() == CP_UTF8 && !is_utf8_str(value))
{
char varname[256];
char *key_end = strchr(keyval, '=');
if (!key_end)
key_end = keyval+ strlen(keyval);
if (key_end - keyval > sizeof(varname))
continue;
// copy and normalize (convert dash to underscore) to variable names
for (char *p = keyval, *q = varname;; p++,q++)
char *new_val= ansi_to_utf8(value);
if (new_val)
{
if (p == key_end)
{
*q = 0;
break;
}
*q = (*p == '-') ? '_' : *p;
*key_end= 0;
fprintf(stdout, "Fixing variable '%s' charset, value=%s\n", keyval,
new_val);
WritePrivateProfileString(section_name, keyval, new_val, myini_path);
*key_end= '=';
}
const char *v = (const char *)bsearch(varname, removed_variables, sizeof(removed_variables) / sizeof(removed_variables[0]),
sizeof(char *), cmp_strings);
}
if (!is_server)
continue;
if (v)
// Check if variable should be removed from config.
// First, copy and normalize (convert dash to underscore) to variable
// names
for (char *p= keyval, *q= varname;; p++, q++)
{
if (p == key_end)
{
fprintf(stdout, "Removing variable '%s' from config file\n", varname);
// delete variable
*key_end = 0;
WritePrivateProfileString(section_name, keyval, 0, myini_path);
*q= 0;
break;
}
*q= (*p == '-') ? '_' : *p;
}
const char *v= (const char *) bsearch(varname, removed_variables, sizeof(removed_variables) / sizeof(removed_variables[0]),
sizeof(char *), cmp_strings);
if (v)
{
fprintf(stdout, "Removing variable '%s' from config file\n", varname);
// delete variable
*key_end= 0;
WritePrivateProfileString(section_name, keyval, 0, myini_path);
}
}
return 0;
}
static bool is_mariadb_section(const char *name, bool *is_server)
{
if (strncmp(name, "mysql", 5)
&& strncmp(name, "mariadb", 7)
&& strcmp(name, "client")
&& strcmp(name, "client-server")
&& strcmp(name, "server"))
{
return false;
}
for (const char *section_name : {"mysqld", "server", "mariadb"})
if (*is_server= !strcmp(section_name, name))
break;
return *is_server;
}
/**
Convert file from a previous version, by removing obsolete variables
Also, fix values to be UTF8, if MariaDB is running in utf8 mode
*/
int upgrade_config_file(const char *myini_path)
{
static char all_sections[MY_INI_SECTION_SIZE];
int sz= GetPrivateProfileSectionNamesA(all_sections, MY_INI_SECTION_SIZE,
myini_path);
if (!sz)
return 0;
if (sz > MY_INI_SECTION_SIZE - 2)
{
fprintf(stderr, "Too many sections in config file\n");
return -1;
}
for (char *section= all_sections; *section; section+= strlen(section) + 1)
{
bool is_server_section;
if (is_mariadb_section(section, &is_server_section))
fix_section(myini_path, section, is_server_section);
}
return 0;
}
#ifdef MAIN
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage : %s <config_file>\n", argv[0]);
return 1;
}
int rc= upgrade_config_file(argv[1]);
if (rc)
{
fprintf(stderr, "upgrade_config_file(\"%s\") returned an error\n",
argv[1]);
return 1;
}
return 0;
}
#endif

View file

@ -134,6 +134,20 @@ static void get_datadir_from_ini(const char *ini, char *service_name, char *data
}
static int fix_and_check_datadir(mysqld_service_properties *props)
{
normalize_path(props->datadir, MAX_PATH);
/* Check if datadir really exists */
if (GetFileAttributes(props->datadir) != INVALID_FILE_ATTRIBUTES)
return 0;
/*
It is possible, that datadir contains some unconvertable character.
We just pretend not to know what's the data directory
*/
props->datadir[0]= 0;
return 0;
}
/*
Retrieve some properties from windows mysqld service binary path.
We're interested in ini file location and datadir, and also in version of
@ -183,7 +197,7 @@ int get_mysql_service_properties(const wchar_t *bin_path,
}
/* Last parameter is the service name*/
wcstombs(service_name, args[numargs-1], MAX_PATH);
WideCharToMultiByte(CP_ACP, 0, args[numargs - 1], -1, service_name, MAX_PATH, NULL, NULL);
if(have_inifile && wcsncmp(args[1], L"--defaults-file=", 16) != 0)
goto end;
@ -202,7 +216,7 @@ int get_mysql_service_properties(const wchar_t *bin_path,
goto end;
}
wcstombs(props->mysqld_exe, mysqld_path, MAX_PATH);
WideCharToMultiByte(CP_ACP, 0, mysqld_path, -1, props->mysqld_exe, MAX_PATH, NULL, NULL);
/* If mysqld.exe exists, try to get its version from executable */
if (GetFileAttributes(props->mysqld_exe) != INVALID_FILE_ATTRIBUTES)
{
@ -213,7 +227,8 @@ int get_mysql_service_properties(const wchar_t *bin_path,
if (have_inifile)
{
/* We have --defaults-file in service definition. */
wcstombs(props->inifile, args[1]+16, MAX_PATH);
WideCharToMultiByte(CP_ACP, 0, args[1] + 16, -1, props->inifile,
MAX_PATH, NULL, NULL);
normalize_path(props->inifile, MAX_PATH);
if (GetFileAttributes(props->inifile) != INVALID_FILE_ATTRIBUTES)
{
@ -284,16 +299,9 @@ int get_mysql_service_properties(const wchar_t *bin_path,
}
}
if (props->datadir[0])
if (props->datadir[0] == 0 || fix_and_check_datadir(props))
{
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.*/
/* There is no datadir in ini file, or non-existing dir, bail out.*/
goto end;
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2011, 2012, Monty Program Ab
Copyright (c) 2011, 2021 Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -37,6 +37,7 @@ typedef struct mysqld_service_properties_st
extern int get_mysql_service_properties(const wchar_t *bin_path,
mysqld_service_properties *props);
#if !defined(UNICODE)
#include <malloc.h>
/*
@ -92,15 +93,13 @@ end:
return ret;
}
static inline SC_HANDLE my_CreateService(SC_HANDLE hSCManager, LPCSTR lpServiceName, LPCSTR lpDisplayName,
static inline SC_HANDLE my_CreateService(SC_HANDLE hSCManager,
LPCSTR lpServiceName, LPCSTR lpDisplayName,
DWORD dwDesiredAccess, DWORD dwServiceType,
DWORD dwStartType, DWORD dwErrorControl,
LPCSTR lpBinaryPathName,
LPCSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPCSTR lpDependencies,
LPCSTR lpServiceStartName,
LPCSTR lpPassword)
LPCSTR lpBinaryPathName, LPCSTR lpLoadOrderGroup,
LPDWORD lpdwTagId, LPCSTR lpDependencies,
LPCSTR lpServiceStartName, LPCSTR lpPassword)
{
wchar_t *w_ServiceName= NULL;
wchar_t *w_DisplayName= NULL;
@ -146,11 +145,11 @@ end:
}
static inline BOOL my_ChangeServiceConfig(SC_HANDLE hService, DWORD dwServiceType,
DWORD dwStartType, DWORD dwErrorControl,
LPCSTR lpBinaryPathName, LPCSTR lpLoadOrderGroup,
LPDWORD lpdwTagId, LPCSTR lpDependencies,
LPCSTR lpServiceStartName, LPCSTR lpPassword,
LPCSTR lpDisplayName)
DWORD dwStartType, DWORD dwErrorControl,
LPCSTR lpBinaryPathName, LPCSTR lpLoadOrderGroup,
LPDWORD lpdwTagId, LPCSTR lpDependencies,
LPCSTR lpServiceStartName, LPCSTR lpPassword,
LPCSTR lpDisplayName)
{
wchar_t *w_DisplayName= NULL;
wchar_t *w_BinaryPathName= NULL;
@ -190,6 +189,7 @@ end:
SetLastError(last_error);
return ret;
}
#undef AWSTRDUP
#undef OpenService
#define OpenService my_OpenService

View file

@ -5,6 +5,7 @@ ENDIF()
# We need MFC
# /permissive- flag does not play well with MFC, disable it.
STRING(REPLACE "/permissive-" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
REMOVE_DEFINITIONS(-DNOSERVICE) # fixes "already defined" warning in an AFX header
FIND_PACKAGE(MFC)
IF(NOT MFC_FOUND)