mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-27 00:48:30 +01:00 
			
		
		
		
	 a632a69386
			
		
	
	
	a632a69386
	
	
	
		
			
			Added a test to verify mariadb-upgrade-service functionality on Windows. The test runs mariadb-install-db.exe to create a Windows service, then executes mariadb-upgrade-service.exe while the service is online or offline. There is no real cross-version upgrade in this test(not possible with MTR), the actual goal is just to run smoke-test. Last times mariadb-upgrade-service was broken (CONC-760, MDEV-30639) there were problems with named pipe connections, which could be detected and prevented by smoke-test alone.
		
			
				
	
	
		
			628 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			628 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub
 | |
| 
 | |
|    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
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
 | |
| 
 | |
| /*
 | |
|   mysql_upgrade_service upgrades mysql service on Windows.
 | |
|   It changes service definition to point to the new mysqld.exe, restarts the 
 | |
|   server and runs mysql_upgrade
 | |
| */
 | |
| 
 | |
| #define DONT_DEFINE_VOID
 | |
| #include "mariadb.h"
 | |
| #include <process.h>
 | |
| #include <my_getopt.h>
 | |
| #include <my_sys.h>
 | |
| #include <m_string.h>
 | |
| #include <mysql_version.h>
 | |
| #include <winservice.h>
 | |
| 
 | |
| #include <windows.h>
 | |
| #include <string>
 | |
| 
 | |
| extern int upgrade_config_file(const char *myini_path);
 | |
| 
 | |
| /* We're using version APIs */
 | |
| #pragma comment(lib, "version")
 | |
| 
 | |
| #define USAGETEXT \
 | |
| "mysql_upgrade_service.exe  Ver 1.00 for Windows\n" \
 | |
| "Copyright (C) 2010-2011 Monty Program Ab & Vladislav Vaintroub" \
 | |
| "This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n" \
 | |
| "and you are welcome to modify and redistribute it under the GPL v2 license\n" \
 | |
| "Usage: mysql_upgrade_service.exe [OPTIONS]\n" \
 | |
| "OPTIONS:"
 | |
| 
 | |
| static char mysqld_path[MAX_PATH];
 | |
| static char mysqlupgrade_path[MAX_PATH];
 | |
| 
 | |
| static char defaults_file_param[MAX_PATH + 16]; /*--defaults-file=<path> */
 | |
| static char logfile_path[MAX_PATH];
 | |
| char my_ini_bck[MAX_PATH];
 | |
| mysqld_service_properties service_properties;
 | |
| static char *opt_service;
 | |
| static SC_HANDLE service;
 | |
| static SC_HANDLE scm;
 | |
| HANDLE mysqld_process; // mysqld.exe started for upgrade
 | |
| DWORD initial_service_state= UINT_MAX; // initial state of the service
 | |
| HANDLE logfile_handle;
 | |
| 
 | |
| /*
 | |
|   Startup and shutdown timeouts, in seconds. 
 | |
|   Maybe,they can be made parameters
 | |
| */
 | |
| static unsigned int startup_timeout= 60;
 | |
| static unsigned int shutdown_timeout= 60*60;
 | |
| 
 | |
| static struct my_option my_long_options[]=
 | |
| {
 | |
|   {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
 | |
|    NO_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {"service", 'S', "Name of the existing Windows service",
 | |
|   &opt_service, &opt_service, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 | |
|   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| static my_bool
 | |
| get_one_option(const struct my_option *opt, const char *, const char *)
 | |
| {
 | |
|   DBUG_ENTER("get_one_option");
 | |
|   switch (opt->id) {
 | |
|   case '?':
 | |
|     printf("%s\n", USAGETEXT);
 | |
|     my_print_help(my_long_options);
 | |
|     exit(0);
 | |
|     break;
 | |
|   }
 | |
|   DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| static void log(const char *fmt, ...)
 | |
| {
 | |
|   va_list args;
 | |
|   /* Print the error message */
 | |
|   va_start(args, fmt);
 | |
|   vfprintf(stdout,fmt, args);
 | |
|   va_end(args);
 | |
|   fputc('\n', stdout);
 | |
|   fflush(stdout);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void die(const char *fmt, ...)
 | |
| {
 | |
|   va_list args;
 | |
|   DBUG_ENTER("die");
 | |
| 
 | |
|   /* Print the error message */
 | |
|   va_start(args, fmt);
 | |
| 
 | |
|   fprintf(stderr, "FATAL ERROR: ");
 | |
|   vfprintf(stderr, fmt, args);
 | |
|   fputc('\n', stderr);
 | |
|   if (logfile_path[0])
 | |
|   {
 | |
|     fprintf(stderr, "Additional information can be found in the log file %s",
 | |
|       logfile_path);
 | |
|   }
 | |
|   va_end(args);
 | |
|   fputc('\n', stderr);
 | |
|   fflush(stdout);
 | |
|   /* Cleanup */
 | |
| 
 | |
|   if (my_ini_bck[0])
 | |
|   {
 | |
|     MoveFileEx(my_ini_bck, service_properties.inifile,MOVEFILE_REPLACE_EXISTING);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     Stop service that we started, if it was not initially running at
 | |
|     program start.
 | |
|   */
 | |
|   if (initial_service_state != UINT_MAX && initial_service_state != SERVICE_RUNNING)
 | |
|   {
 | |
|     SERVICE_STATUS service_status;
 | |
|     ControlService(service, SERVICE_CONTROL_STOP, &service_status);
 | |
|   }
 | |
| 
 | |
|   if (scm)
 | |
|     CloseServiceHandle(scm);
 | |
|   if (service)
 | |
|     CloseServiceHandle(service);
 | |
|   /* Stop mysqld.exe, if it was started for upgrade */
 | |
|   if (mysqld_process)
 | |
|     TerminateProcess(mysqld_process, 3);
 | |
|   if (logfile_handle)
 | |
|     CloseHandle(logfile_handle);
 | |
|   my_end(0);
 | |
| 
 | |
|   exit(1);
 | |
| }
 | |
| 
 | |
| #define WRITE_LOG(fmt,...) {\
 | |
|   char log_buf[1024]; \
 | |
|   DWORD nbytes; \
 | |
|   snprintf(log_buf,sizeof(log_buf), fmt, __VA_ARGS__);\
 | |
|   WriteFile(logfile_handle,log_buf, (DWORD)strlen(log_buf), &nbytes , 0);\
 | |
| }
 | |
| 
 | |
| /*
 | |
|   spawn-like function to run subprocesses. 
 | |
|   We also redirect the full output to the log file.
 | |
| 
 | |
|   Typical usage could be something like
 | |
|   run_tool(P_NOWAIT, "cmd.exe", "/c" , "echo", "foo", NULL)
 | |
|   
 | |
|   @param    wait_flag (P_WAIT or P_NOWAIT)
 | |
|   @program  program to run
 | |
| 
 | |
|   Rest of the parameters is NULL terminated strings building command line.
 | |
| 
 | |
|   @return intptr containing either process handle, if P_NOWAIT is used
 | |
|   or return code of the process (if P_WAIT is used)
 | |
| */
 | |
| 
 | |
| static intptr_t run_tool(int wait_flag, const char *program,...)
 | |
| {
 | |
|   static char cmdline[32*1024];
 | |
|   char *end;
 | |
|   va_list args;
 | |
|   va_start(args, program);
 | |
|   if (!program)
 | |
|     die("Invalid call to run_tool");
 | |
|   end= strxmov(cmdline, "\"", program, "\"", NullS);
 | |
| 
 | |
|   for(;;) 
 | |
|   {
 | |
|     char *param= va_arg(args,char *);
 | |
|     if(!param)
 | |
|       break;
 | |
|     end= strxmov(end, " \"", param, "\"", NullS);
 | |
|   }
 | |
|   va_end(args);
 | |
|   
 | |
|   /* Create output file if not alredy done */
 | |
|   if (!logfile_handle)
 | |
|   {
 | |
|     char tmpdir[FN_REFLEN];
 | |
|     GetTempPath(FN_REFLEN, tmpdir);
 | |
|     sprintf_s(logfile_path, "%smysql_upgrade_service.%s.log", tmpdir,
 | |
|       opt_service);
 | |
|     SECURITY_ATTRIBUTES attr= {0};
 | |
|     attr.nLength= sizeof(SECURITY_ATTRIBUTES);
 | |
|     attr.bInheritHandle=  TRUE;
 | |
|     logfile_handle= CreateFile(logfile_path, FILE_APPEND_DATA,
 | |
|       FILE_SHARE_READ|FILE_SHARE_WRITE, &attr, CREATE_ALWAYS, 0, NULL);
 | |
|     if (logfile_handle == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       die("Cannot open log file %s, windows error %u", 
 | |
|         logfile_path, GetLastError());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   WRITE_LOG("Executing %s\r\n", cmdline);
 | |
| 
 | |
|   /* Start child process */
 | |
|   STARTUPINFO si= {0};
 | |
|   si.cb= sizeof(si);
 | |
|   si.hStdInput= GetStdHandle(STD_INPUT_HANDLE);
 | |
|   si.hStdError= logfile_handle;
 | |
|   si.hStdOutput= logfile_handle;
 | |
|   si.dwFlags= STARTF_USESTDHANDLES;
 | |
|   PROCESS_INFORMATION pi;
 | |
|   if (!CreateProcess(NULL, cmdline, NULL, 
 | |
|        NULL, TRUE, NULL, NULL, NULL, &si, &pi))
 | |
|   {
 | |
|     die("CreateProcess failed (commandline %s)", cmdline);
 | |
|   }
 | |
|   CloseHandle(pi.hThread);
 | |
| 
 | |
|   if (wait_flag == P_NOWAIT)
 | |
|   {
 | |
|     /* Do not wait for process to complete, return handle. */
 | |
|     return (intptr_t)pi.hProcess;
 | |
|   }
 | |
| 
 | |
|   /* Wait for process to complete. */
 | |
|   if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0)
 | |
|   {
 | |
|     die("WaitForSingleObject() failed");
 | |
|   }
 | |
|   DWORD exit_code;
 | |
|   if (!GetExitCodeProcess(pi.hProcess, &exit_code))
 | |
|   {
 | |
|     die("GetExitCodeProcess() failed");
 | |
|   }
 | |
|   return (intptr_t)exit_code;
 | |
| }
 | |
| 
 | |
| 
 | |
| void stop_mysqld_service()
 | |
| {
 | |
|   DWORD needed;
 | |
|   SERVICE_STATUS_PROCESS ssp;
 | |
|   int timeout= shutdown_timeout*1000; 
 | |
|   for(;;)
 | |
|   {
 | |
|     if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO,
 | |
|           (LPBYTE)&ssp, 
 | |
|           sizeof(SERVICE_STATUS_PROCESS),
 | |
|           &needed))
 | |
|     {
 | |
|       die("QueryServiceStatusEx failed (%u)\n", GetLastError()); 
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       Remember initial state of the service, we will restore it on
 | |
|       exit.
 | |
|     */
 | |
|     if(initial_service_state == UINT_MAX)
 | |
|       initial_service_state= ssp.dwCurrentState;
 | |
| 
 | |
|     switch(ssp.dwCurrentState)
 | |
|     {
 | |
|       case SERVICE_STOPPED:
 | |
|         return;
 | |
|       case SERVICE_RUNNING:
 | |
|         if(!ControlService(service, SERVICE_CONTROL_STOP, 
 | |
|              (SERVICE_STATUS *)&ssp))
 | |
|             die("ControlService failed, error %u\n", GetLastError());
 | |
|       case SERVICE_START_PENDING:
 | |
|       case SERVICE_STOP_PENDING:
 | |
|         if(timeout < 0)
 | |
|           die("Service does not stop after %d seconds timeout",shutdown_timeout);
 | |
|         Sleep(100);
 | |
|         timeout -= 100;
 | |
|         break;
 | |
|       default:
 | |
|         die("Unexpected service state %d",ssp.dwCurrentState);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 
 | |
|   Shutdown mysql server. Not using mysqladmin, since 
 | |
|   our --skip-grant-tables do not work anymore after mysql_upgrade
 | |
|   that does "flush privileges". Instead, the shutdown event  is set.
 | |
| */
 | |
| #define OPEN_EVENT_RETRY_SLEEP_MS 100
 | |
| #define OPEN_EVENT_MAX_RETRIES 50
 | |
| 
 | |
| void initiate_mysqld_shutdown()
 | |
| {
 | |
|   char event_name[32];
 | |
|   DWORD pid= GetProcessId(mysqld_process);
 | |
|   sprintf_s(event_name, "MySQLShutdown%d", pid);
 | |
| 
 | |
|   HANDLE shutdown_handle;
 | |
|   for (int i= 0;; i++)
 | |
|   {
 | |
|     shutdown_handle= OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name);
 | |
|     if(shutdown_handle != nullptr || i == OPEN_EVENT_MAX_RETRIES)
 | |
|       break;
 | |
|     if (WaitForSingleObject(mysqld_process, OPEN_EVENT_RETRY_SLEEP_MS) !=
 | |
|         WAIT_TIMEOUT)
 | |
|     {
 | |
|       die("server process exited before shutdown event was created");
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   if (!shutdown_handle)
 | |
|   {
 | |
|     die("OpenEvent() failed for shutdown event");
 | |
|   }
 | |
| 
 | |
|   if(!SetEvent(shutdown_handle))
 | |
|   {
 | |
|     die("SetEvent() failed");
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void get_service_config()
 | |
| {
 | |
|   scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 | |
|   if (!scm)
 | |
|     die("OpenSCManager failed with %u", GetLastError());
 | |
|   service = OpenService(scm, opt_service, SERVICE_ALL_ACCESS);
 | |
|   if (!service)
 | |
|     die("OpenService failed with %u", GetLastError());
 | |
| 
 | |
|   BYTE config_buffer[8 * 1024];
 | |
|   LPQUERY_SERVICE_CONFIGW config = (LPQUERY_SERVICE_CONFIGW)config_buffer;
 | |
|   DWORD size = sizeof(config_buffer);
 | |
|   DWORD needed;
 | |
|   if (!QueryServiceConfigW(service, config, size, &needed))
 | |
|     die("QueryServiceConfig failed with %u", GetLastError());
 | |
| 
 | |
|   if (get_mysql_service_properties(config->lpBinaryPathName, &service_properties))
 | |
|   {
 | |
|     die("Not a valid MySQL service");
 | |
|   }
 | |
| 
 | |
|   int my_major = MYSQL_VERSION_ID / 10000;
 | |
|   int my_minor = (MYSQL_VERSION_ID % 10000) / 100;
 | |
|   int my_patch = MYSQL_VERSION_ID % 100;
 | |
| 
 | |
|   if (my_major < service_properties.version_major ||
 | |
|     (my_major == service_properties.version_major && my_minor < service_properties.version_minor))
 | |
|   {
 | |
|     die("Can not downgrade, the service is currently running as version %d.%d.%d"
 | |
|       ", my version is %d.%d.%d", service_properties.version_major, service_properties.version_minor,
 | |
|       service_properties.version_patch, my_major, my_minor, my_patch);
 | |
|   }
 | |
|   if (service_properties.inifile[0] == 0)
 | |
|   {
 | |
|     /*
 | |
|       Weird case, no --defaults-file in service definition, need to create one.
 | |
|     */
 | |
|     sprintf_s(service_properties.inifile, MAX_PATH, "%s\\my.ini", service_properties.datadir);
 | |
|   }
 | |
|   sprintf(defaults_file_param, "--defaults-file=%s", service_properties.inifile);
 | |
| }
 | |
| /*
 | |
|   Change service configuration (binPath) to point to mysqld from 
 | |
|   this installation.
 | |
| */
 | |
| static void change_service_config()
 | |
| {
 | |
|   char buf[MAX_PATH];
 | |
|   char commandline[3 * MAX_PATH + 19];
 | |
|   int i;
 | |
| 
 | |
|   /*
 | |
|     Write datadir to my.ini, after converting  backslashes to 
 | |
|     unix style slashes.
 | |
|   */
 | |
|   if (service_properties.datadir[0])
 | |
|   {
 | |
|     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);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     Remove basedir from defaults file, otherwise the service wont come up in 
 | |
|     the new version, and will complain about mismatched message file.
 | |
|   */
 | |
|   WritePrivateProfileString("mysqld", "basedir",NULL, 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, 
 | |
|          SERVICE_NO_CHANGE, commandline, NULL, NULL, NULL, NULL, NULL, NULL))
 | |
|   {
 | |
|     die("ChangeServiceConfig failed with %u", GetLastError());
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Waits until starting server can be connected to, via given named pipe, with timeout
 | |
|   Dies if either server process exited meanwhile, or when timeout was exceeded.
 | |
| */
 | |
| static void wait_for_server_startup(HANDLE process, const char *named_pipe, DWORD timeout_sec)
 | |
| {
 | |
|   unsigned long long end_time= GetTickCount64() + 1000ULL*timeout_sec;
 | |
|   for (;;)
 | |
|   {
 | |
|     if (WaitNamedPipe(named_pipe, 0))
 | |
|       return;
 | |
| 
 | |
|     if (GetTickCount64() >= end_time)
 | |
|       die("Server did not startup after %lu seconds", timeout_sec);
 | |
| 
 | |
|     if (WaitForSingleObject(process, 100) != WAIT_TIMEOUT)
 | |
|       die("Server did not start");
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|   int error;
 | |
|   MY_INIT(argv[0]);
 | |
|   char bindir[FN_REFLEN];
 | |
|   char *p;
 | |
| 
 | |
|   /* Parse options */
 | |
|   if ((error= handle_options(&argc, &argv, my_long_options, get_one_option)))
 | |
|     die("");
 | |
|   if (!opt_service)
 | |
|     die("--service=# parameter is mandatory");
 | |
|  
 | |
|  /*
 | |
|     Get full path to mysqld, we need it when changing service configuration.
 | |
|     Assume mysqld.exe in the same directory as this program.
 | |
|     mysql_upgrade.exe is either in the same directory, or pointed to by
 | |
|     MARIADB_UPGRADE_EXE environment variable (in case of MTR running it)
 | |
|   */
 | |
|   GetModuleFileName(NULL, bindir, FN_REFLEN);
 | |
|   p= strrchr(bindir, FN_LIBCHAR);
 | |
|   if(p)
 | |
|   {
 | |
|     *p= 0;
 | |
|   }
 | |
|   sprintf_s(mysqld_path, "%s\\mysqld.exe", bindir);
 | |
|   sprintf_s(mysqlupgrade_path, "%s\\mysql_upgrade.exe", bindir);
 | |
| 
 | |
|   if (access(mysqld_path, 0))
 | |
|     die("File %s does not exist", mysqld_path);
 | |
|   if (access(mysqlupgrade_path, 0))
 | |
|   {
 | |
|     /* Try to get path from environment variable, set by MTR */
 | |
|     char *alt_mysqlupgrade_path= getenv("MARIADB_UPGRADE_EXE");
 | |
|     if (alt_mysqlupgrade_path)
 | |
|       sprintf_s(mysqlupgrade_path, "%s", alt_mysqlupgrade_path);
 | |
|   }
 | |
|   if (access(mysqlupgrade_path, 0))
 | |
|     die("File %s does not exist", mysqld_path);
 | |
| 
 | |
|   /*
 | |
|     Messages written on stdout should not be buffered,  GUI upgrade program 
 | |
|     reads them from pipe and uses as progress indicator.
 | |
|   */
 | |
|   setvbuf(stdout, NULL, _IONBF, 0);
 | |
|   int phase = 0;
 | |
|   int max_phases=10;
 | |
|   get_service_config();
 | |
| 
 | |
|   bool my_ini_exists;
 | |
|   bool old_mysqld_exe_exists;
 | |
| 
 | |
|   log("Phase %d/%d: Stopping service", ++phase,max_phases);
 | |
|   stop_mysqld_service();
 | |
| 
 | |
|   my_ini_exists = (GetFileAttributes(service_properties.inifile) != INVALID_FILE_ATTRIBUTES);
 | |
|   if (!my_ini_exists)
 | |
|   {
 | |
|     HANDLE h = CreateFile(service_properties.inifile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
 | |
|       0, CREATE_NEW, 0 ,0);
 | |
|     if (h != INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       CloseHandle(h);
 | |
|     }
 | |
|     else if (GetLastError() != ERROR_FILE_EXISTS)
 | |
|     {
 | |
|       die("Can't create ini file %s, last error %u", service_properties.inifile, GetLastError());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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,
 | |
|     do_start_stop_server?",this can take some time":"(skipped)");
 | |
| 
 | |
|   char socket_param[FN_REFLEN];
 | |
|   sprintf_s(socket_param, "--socket=mysql_upgrade_service_%u",
 | |
|     GetCurrentProcessId());
 | |
| 
 | |
|   DWORD start_duration_ms = 0;
 | |
| 
 | |
|   char pipe_name[64];
 | |
|   snprintf(pipe_name, sizeof(pipe_name),
 | |
|            "\\\\.\\pipe\\mysql_upgrade_service_%lu", GetCurrentProcessId());
 | |
| 
 | |
|   if (do_start_stop_server)
 | |
|   {
 | |
|     /* Start/stop server with  --loose-innodb-fast-shutdown=1 */
 | |
|     mysqld_process = (HANDLE)run_tool(P_NOWAIT, service_properties.mysqld_exe,
 | |
|       defaults_file_param, "--loose-innodb-fast-shutdown=1", "--skip-networking",
 | |
|       "--enable-named-pipe", socket_param, "--skip-slave-start", NULL);
 | |
| 
 | |
|     if (mysqld_process == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       die("Cannot start mysqld.exe process, last error =%u", GetLastError());
 | |
|     }
 | |
|     wait_for_server_startup(mysqld_process, pipe_name, startup_timeout);
 | |
|     // Server started, shut it down.
 | |
|     initiate_mysqld_shutdown();
 | |
|     if (WaitForSingleObject((HANDLE)mysqld_process, shutdown_timeout * 1000) != WAIT_OBJECT_0)
 | |
|     {
 | |
|        die("Could not shutdown server");
 | |
|     }
 | |
|     DWORD exit_code;
 | |
|     if (!GetExitCodeProcess((HANDLE)mysqld_process, &exit_code))
 | |
|     {
 | |
|       die("Could not get server's exit code");
 | |
|     }
 | |
|     if (exit_code)
 | |
|     {
 | |
|       die("Could not get successfully shutdown server (exit code %u)",exit_code);
 | |
|     }
 | |
|     CloseHandle(mysqld_process);
 | |
|   }
 | |
| 
 | |
|   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 
 | |
|     for communication, for security reasons.
 | |
|   */
 | |
| 
 | |
|   log("Phase %d/%d: Starting mysqld for upgrade",++phase,max_phases);
 | |
|   mysqld_process= (HANDLE)run_tool(P_NOWAIT, mysqld_path,
 | |
|     defaults_file_param, "--skip-networking",  "--skip-grant-tables", 
 | |
|     "--enable-named-pipe",  socket_param,"--skip-slave-start", NULL);
 | |
| 
 | |
|   if (mysqld_process == INVALID_HANDLE_VALUE)
 | |
|   {
 | |
|     die("Cannot start mysqld.exe process, errno=%d", errno);
 | |
|   }
 | |
| 
 | |
|   log("Phase %d/%d: Waiting for startup to complete",++phase,max_phases);
 | |
|   wait_for_server_startup(mysqld_process, pipe_name, startup_timeout);
 | |
| 
 | |
|   log("Phase %d/%d: Running mysql_upgrade",++phase,max_phases);
 | |
|   int upgrade_err= (int) run_tool(P_WAIT,  mysqlupgrade_path, 
 | |
|     "--protocol=pipe", "--force",  socket_param,
 | |
|     NULL);
 | |
| 
 | |
|   if (upgrade_err)
 | |
|     die("mysql_upgrade failed with error code %d\n", upgrade_err);
 | |
| 
 | |
|   log("Phase %d/%d: Changing service configuration", ++phase, max_phases);
 | |
|   change_service_config();
 | |
| 
 | |
|   log("Phase %d/%d: Initiating server shutdown",++phase, max_phases);
 | |
|   initiate_mysqld_shutdown();
 | |
| 
 | |
|   log("Phase %d/%d: Waiting for shutdown to complete",++phase, max_phases);
 | |
|   if (WaitForSingleObject(mysqld_process, shutdown_timeout*1000)
 | |
|       != WAIT_OBJECT_0)
 | |
|   {
 | |
|     /* Shutdown takes too long */
 | |
|     die("mysqld does not shutdown.");
 | |
|   }
 | |
|   CloseHandle(mysqld_process);
 | |
|   mysqld_process= NULL;
 | |
| 
 | |
|   log("Phase %d/%d: Starting service%s",++phase,max_phases,
 | |
|     (initial_service_state == SERVICE_RUNNING)?"":" (skipped)");
 | |
|   if (initial_service_state == SERVICE_RUNNING)
 | |
|   {
 | |
|     StartService(service, NULL, NULL);
 | |
|   }
 | |
| 
 | |
|   log("Service '%s' successfully upgraded.\nLog file is written to %s",
 | |
|     opt_service, logfile_path);
 | |
|   CloseServiceHandle(service);
 | |
|   CloseServiceHandle(scm);
 | |
|   if (logfile_handle)
 | |
|     CloseHandle(logfile_handle);
 | |
|   if(my_ini_bck[0])
 | |
|   {
 | |
|     DeleteFile(my_ini_bck);
 | |
|   }
 | |
|   my_end(0);
 | |
|   exit(0);
 | |
| }
 |