summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladislav Vaintroub <wlad@mariadb.com>2020-07-04 18:24:40 +0200
committerVladislav Vaintroub <wlad@mariadb.com>2020-07-04 18:24:40 +0200
commit9701759b3d9ea9fd9bee640ce27171bdd51b7e78 (patch)
tree88ad4445989ffb68b1f3af82642749ca50372ab9
parentd46576b35ac0775e6500ee15e828f29c74998262 (diff)
downloadmariadb-git-9701759b3d9ea9fd9bee640ce27171bdd51b7e78.tar.gz
MDEV-23043 Refactor Windows service handling
Removed the existing nt_service classes - they provide little abstraction, and only obscure a relatively simple service handling. This replaces by similar code inspired by MS docs samples. Service handling is now moved into winmain.cc, which contains the main() function for Windows. winmain provides reporting callbacks, which should be used by external code ,to report transitions from starting to running to shutting down to stopped. Removed a do-nothing ServiceMain thread, and the non-working service "pause/continue". Removed a lot of #ifdef __WIN__ code from mysqld.cc
-rw-r--r--extra/mariabackup/CMakeLists.txt7
-rw-r--r--include/my_pthread.h1
-rw-r--r--sql/CMakeLists.txt4
-rw-r--r--sql/handle_connections_win.cc19
-rw-r--r--sql/mysqld.cc423
-rw-r--r--sql/mysqld.h10
-rw-r--r--sql/nt_servc.cc555
-rw-r--r--sql/nt_servc.h113
-rw-r--r--sql/winmain.cc371
-rw-r--r--unittest/sql/CMakeLists.txt9
10 files changed, 496 insertions, 1016 deletions
diff --git a/extra/mariabackup/CMakeLists.txt b/extra/mariabackup/CMakeLists.txt
index 561f2ab0500..41de993317e 100644
--- a/extra/mariabackup/CMakeLists.txt
+++ b/extra/mariabackup/CMakeLists.txt
@@ -46,12 +46,6 @@ ADD_DEFINITIONS(-UMYSQL_SERVER)
# xtrabackup binary
########################################################################
-IF(WIN32)
- SET(NT_SERVICE_SOURCE ${PROJECT_SOURCE_DIR}/sql/nt_servc.cc)
-ELSE()
- SET(NT_SERVICE_SOURCE)
-ENDIF()
-
ADD_DEFINITIONS(-DPCRE_STATIC=1)
ADD_DEFINITIONS(${SSL_DEFINES})
MYSQL_ADD_EXECUTABLE(mariadb-backup
@@ -76,7 +70,6 @@ MYSQL_ADD_EXECUTABLE(mariadb-backup
encryption_plugin.cc
${PROJECT_BINARY_DIR}/sql/sql_builtin.cc
${PROJECT_SOURCE_DIR}/sql/net_serv.cc
- ${NT_SERVICE_SOURCE}
${PROJECT_SOURCE_DIR}/libmysqld/libmysql.c
COMPONENT backup
)
diff --git a/include/my_pthread.h b/include/my_pthread.h
index 4888bfcc2c8..8137bca570b 100644
--- a/include/my_pthread.h
+++ b/include/my_pthread.h
@@ -104,7 +104,6 @@ int pthread_cancel(pthread_t thread);
#define ETIMEDOUT 145 /* Win32 doesn't have this */
#endif
-#define getpid() GetCurrentThreadId()
#define HAVE_LOCALTIME_R 1
#define _REENTRANT 1
#define HAVE_PTHREAD_ATTR_SETSTACKSIZE 1
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index c2bf0c68f2f..1a53db4aa61 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -179,7 +179,7 @@ IF ((CMAKE_SYSTEM_NAME MATCHES "Linux" OR
ENDIF()
IF(WIN32)
- SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc nt_servc.cc)
+ SET(SQL_SOURCE ${SQL_SOURCE} handle_connections_win.cc)
ENDIF()
MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY
@@ -209,7 +209,7 @@ FOREACH(se aria partition perfschema sql_sequence wsrep)
ENDFOREACH()
IF(WIN32)
- SET(MYSQLD_SOURCE main.cc message.rc)
+ SET(MYSQLD_SOURCE winmain.cc message.rc)
ELSE()
SET(MYSQLD_SOURCE main.cc ${DTRACE_PROBES_ALL})
ENDIF()
diff --git a/sql/handle_connections_win.cc b/sql/handle_connections_win.cc
index 44ad8ddc865..49b24d96210 100644
--- a/sql/handle_connections_win.cc
+++ b/sql/handle_connections_win.cc
@@ -26,7 +26,6 @@
#include <handle_connections_win.h>
/* From mysqld.cc */
-extern HANDLE hEventShutdown;
extern MYSQL_SOCKET base_ip_sock, extra_ip_sock;
#ifdef HAVE_POOL_OF_THREADS
extern PTP_CALLBACK_ENVIRON get_threadpool_win_callback_environ();
@@ -458,6 +457,18 @@ struct Pipe_Listener : public Listener
}
};
+ /* The shutdown event, which is set whenever*/
+static void create_shutdown_event()
+{
+ char shutdown_event_name[40];
+ sprintf_s(shutdown_event_name, "MySQLShutdown%u", GetCurrentProcessId());
+ if (!(hEventShutdown= CreateEvent(0, FALSE, FALSE, shutdown_event_name)))
+ {
+ sql_print_error("Can't create shutdown event, Windows error %u", GetLastError());
+ unireg_abort(1);
+ }
+}
+
/**
Accept new client connections on Windows.
@@ -518,6 +529,8 @@ void handle_connections_win()
unireg_abort(1);
}
+ create_shutdown_event();
+
wait_events[SHUTDOWN_IDX]= hEventShutdown;
n_waits = 1;
@@ -532,6 +545,8 @@ void handle_connections_win()
all_listeners[i]->begin_accept();
}
+ mysqld_win_set_startup_complete();
+
for (;;)
{
DWORD idx = WaitForMultipleObjects(n_waits ,wait_events, FALSE, INFINITE);
@@ -543,6 +558,8 @@ void handle_connections_win()
all_listeners[idx - LISTENER_START_IDX]->completion_callback();
}
+ mysqld_win_initiate_shutdown();
+
/* Cleanup */
for (int i= 0; i < n_listeners; i++)
{
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index d14d2a10ecb..40c7d78e98b 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -117,6 +117,8 @@
#include "sql_reload.h" // reload_acl_and_cache
#include "sp_head.h" // init_sp_psi_keys
+#include <mysqld_default_groups.h>
+
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
@@ -370,7 +372,7 @@ my_bool locked_in_memory;
bool opt_using_transactions;
bool volatile abort_loop;
uint volatile global_disable_checkpoint;
-#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
+#if defined(_WIN32)
ulong slow_start_timeout;
#endif
/**
@@ -1401,21 +1403,10 @@ static pthread_t select_thread;
/* OS specific variables */
-#ifdef __WIN__
-#undef getpid
-#include <process.h>
-
-static bool start_mode=0, use_opt_args;
-static int opt_argc;
-static char **opt_argv;
-
-#if !defined(EMBEDDED_LIBRARY)
+#ifdef _WIN32
HANDLE hEventShutdown;
-static char shutdown_event_name[40];
-#include "nt_servc.h"
-static NTService Service; ///< Service object for WinNT
-#endif /* EMBEDDED_LIBRARY */
-#endif /* __WIN__ */
+#endif
+
#ifndef EMBEDDED_LIBRARY
bool mysqld_embedded=0;
@@ -1632,9 +1623,8 @@ static void break_connect_loop()
abort_loop= 1;
-#if defined(__WIN__)
- if (!SetEvent(hEventShutdown))
- DBUG_PRINT("error", ("Got error: %ld from SetEvent", GetLastError()));
+#if defined(_WIN32)
+ mysqld_win_initiate_shutdown();
#else
/* Avoid waiting for ourselves when thread-handling=no-threads. */
if (pthread_equal(pthread_self(), select_thread))
@@ -1909,6 +1899,12 @@ extern "C" void unireg_abort(int exit_code)
mysqld_exit(exit_code);
}
+#ifdef _WIN32
+typedef void (*report_svc_status_t)(DWORD current_state, DWORD win32_exit_code,
+ DWORD wait_hint);
+static void dummy_svc_status(DWORD, DWORD, DWORD) {}
+static report_svc_status_t my_report_svc_status= dummy_svc_status;
+#endif
static void mysqld_exit(int exit_code)
{
@@ -1939,6 +1935,9 @@ static void mysqld_exit(int exit_code)
SAFEMALLOC_REPORT_MEMORY(0);
}
DBUG_LEAVE;
+#ifdef _WIN32
+ my_report_svc_status(SERVICE_STOPPED, exit_code, 0);
+#endif
sd_notify(0, "STATUS=MariaDB server is down");
exit(exit_code); /* purecov: inspected */
}
@@ -2616,14 +2615,60 @@ void unlink_thd(THD *thd)
}
-/******************************************************************************
- Setup a signal thread with handles all signals.
- Because Linux doesn't support schemas use a mutex to check that
- the signal thread is ready before continuing
-******************************************************************************/
+#if defined(_WIN32)
+/*
+ If server is started as service, the service routine will set
+ the callback function.
+*/
+void mysqld_set_service_status_callback(void (*r)(DWORD, DWORD, DWORD))
+{
+ my_report_svc_status= r;
+}
-#if defined(__WIN__)
+static bool startup_complete()
+{
+ return hEventShutdown != NULL;
+}
+/**
+ Initiates shutdown on Windows by setting shutdown event.
+ Reports windows service status.
+
+ If startup was not finished, terminates process (no good
+ cleanup possible)
+*/
+void mysqld_win_initiate_shutdown()
+{
+ if (startup_complete())
+ {
+ my_report_svc_status(SERVICE_STOP_PENDING, 0, 0);
+ abort_loop= 1;
+ if (!SetEvent(hEventShutdown))
+ /* This should never fail.*/
+ abort();
+ }
+ else
+ {
+ my_report_svc_status(SERVICE_STOPPED, 1, 0);
+ TerminateProcess(GetCurrentProcess(), 1);
+ }
+}
+
+/*
+ Signal when server has started and can accept connections.
+*/
+void mysqld_win_set_startup_complete()
+{
+ my_report_svc_status(SERVICE_RUNNING, 0, 0);
+ DBUG_ASSERT(startup_complete());
+}
+
+
+void mysqld_win_set_service_name(const char *name)
+{
+ if (stricmp(name, "mysql"))
+ load_default_groups[array_elements(load_default_groups) - 2]= name;
+}
/*
On Windows, we use native SetConsoleCtrlHandler for handle events like Ctrl-C
@@ -2634,33 +2679,30 @@ void unlink_thd(THD *thd)
callstack.
*/
-static BOOL WINAPI console_event_handler( DWORD type )
+static BOOL WINAPI console_event_handler( DWORD type )
{
- DBUG_ENTER("console_event_handler");
-#ifndef EMBEDDED_LIBRARY
- if(type == CTRL_C_EVENT)
+ static const char *names[]= {
+ "CTRL_C_EVENT","CTRL_BREAK_EVENT", "CTRL_CLOSE_EVENT", "", "",
+ "CTRL_LOGOFF_EVENT", "CTRL_SHUTDOWN_EVENT"};
+
+ switch (type)
{
- /*
- Do not shutdown before startup is finished and shutdown
- thread is initialized. Otherwise there is a race condition
- between main thread doing initialization and CTRL-C thread doing
- cleanup, which can result into crash.
- */
-#ifndef EMBEDDED_LIBRARY
- if(hEventShutdown)
- break_connect_loop();
- else
-#endif
- sql_print_warning("CTRL-C ignored during startup");
- DBUG_RETURN(TRUE);
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ sql_print_information("console_event_handler: received %s event, shutting down",
+ names[type]);
+ mysqld_win_initiate_shutdown();
+ return TRUE;
+ case CTRL_CLOSE_EVENT:
+ sql_print_information("console_event_handler: received CTRL_CLOSE_EVENT event, terminating");
+ TerminateProcess(GetCurrentProcess(), 1);
+ return TRUE;
+ default:
+ return FALSE;
}
-#endif
- DBUG_RETURN(FALSE);
}
-
-
#ifdef DEBUG_UNHANDLED_EXCEPTION_FILTER
#define DEBUGGER_ATTACH_TIMEOUT 120
/*
@@ -2691,7 +2733,7 @@ static void wait_for_debugger(int timeout_sec)
}
#endif /* DEBUG_UNHANDLED_EXCEPTION_FILTER */
-LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
+static LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
{
static BOOL first_time= TRUE;
if(!first_time)
@@ -2738,10 +2780,9 @@ LONG WINAPI my_unhandler_exception_filter(EXCEPTION_POINTERS *ex_pointers)
void init_signals(void)
{
- if(opt_console)
- SetConsoleCtrlHandler(console_event_handler,TRUE);
+ SetConsoleCtrlHandler(console_event_handler,TRUE);
- /* Avoid MessageBox()es*/
+ /* Avoid MessageBox()es*/
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
@@ -2758,7 +2799,8 @@ void init_signals(void)
*/
SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS
| SEM_NOOPENFILEERRORBOX);
- SetUnhandledExceptionFilter(my_unhandler_exception_filter);
+ if(!opt_debugging)
+ SetUnhandledExceptionFilter(my_unhandler_exception_filter);
}
@@ -3137,12 +3179,7 @@ void *my_str_realloc_mysqld(void *ptr, size_t size)
}
#endif
-#include <mysqld_default_groups.h>
-#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
-static const int load_default_groups_sz=
-sizeof(load_default_groups)/sizeof(load_default_groups[0]);
-#endif
/**
@@ -5191,19 +5228,6 @@ static int init_server_components()
#ifndef EMBEDDED_LIBRARY
-#ifdef _WIN32
-static void create_shutdown_event()
-{
- hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name);
- // On "Stop Service" we have to do regular shutdown
- Service.SetShutdownEvent(hEventShutdown);
-}
-#else /*_WIN32*/
-#define create_shutdown_event()
-#endif
-#endif /* EMBEDDED_LIBRARY */
-
-#ifndef EMBEDDED_LIBRARY
#ifndef DBUG_OFF
/*
@@ -5243,11 +5267,7 @@ static void test_lc_time_sz()
#endif//DBUG_OFF
-#ifdef __WIN__
-int win_main(int argc, char **argv)
-#else
int mysqld_main(int argc, char **argv)
-#endif
{
#ifndef _WIN32
/* We can't close stdin just now, because it may be booststrap mode. */
@@ -5265,7 +5285,6 @@ int mysqld_main(int argc, char **argv)
if (init_early_variables())
exit(1);
-#ifndef _WIN32
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
pre_initialize_performance_schema();
#endif /*WITH_PERFSCHEMA_STORAGE_ENGINE */
@@ -5275,7 +5294,6 @@ int mysqld_main(int argc, char **argv)
fprintf(stderr, "my_init() failed.");
return 1;
}
-#endif
orig_argc= argc;
orig_argv= argv;
@@ -5482,12 +5500,12 @@ int mysqld_main(int argc, char **argv)
if (WSREP_ON && wsrep_check_opts()) unireg_abort(1);
#endif
+#ifdef _WIN32
/*
The subsequent calls may take a long time : e.g. innodb log read.
Thus set the long running service control manager timeout
*/
-#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
- Service.SetSlowStarting(slow_start_timeout);
+ my_report_svc_status(SERVICE_START_PENDING, NO_ERROR, slow_start_timeout);
#endif
if (init_server_components())
@@ -5590,7 +5608,6 @@ int mysqld_main(int argc, char **argv)
}
}
- create_shutdown_event();
start_handle_manager();
/* Copy default global rpl_filter to global_rpl_filter */
@@ -5644,9 +5661,6 @@ int mysqld_main(int argc, char **argv)
}
#endif
-#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
- Service.SetRunning();
-#endif
/* Signal threads waiting for server to be started */
mysql_mutex_lock(&LOCK_server_started);
@@ -5702,16 +5716,6 @@ int mysqld_main(int argc, char **argv)
*/
PSI_CALL_delete_current_thread();
-#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
- if (start_mode)
- Service.Stop();
- else
- {
- Service.SetShutdownEvent(0);
- if (hEventShutdown)
- CloseHandle(hEventShutdown);
- }
-#endif
#if (defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY))
ERR_remove_state(0);
#endif
@@ -5722,245 +5726,6 @@ int mysqld_main(int argc, char **argv)
#endif /* !EMBEDDED_LIBRARY */
-/****************************************************************************
- Main and thread entry function for Win32
- (all this is needed only to run mysqld as a service on WinNT)
-****************************************************************************/
-
-#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
-void mysql_service(void *p)
-{
- if (my_thread_init())
- abort();
-
- if (use_opt_args)
- win_main(opt_argc, opt_argv);
- else
- win_main(Service.my_argc, Service.my_argv);
-
- my_thread_end();
-}
-
-
-/* Quote string if it contains space, else copy */
-
-static char *add_quoted_string(char *to, const char *from, char *to_end)
-{
- uint length= (uint) (to_end-to);
-
- if (!strchr(from, ' '))
- return strmake(to, from, length-1);
- return strxnmov(to, length-1, "\"", from, "\"", NullS);
-}
-
-
-/**
- Handle basic handling of services, like installation and removal.
-
- @param argv Pointer to argument list
- @param servicename Internal name of service
- @param displayname Display name of service (in taskbar ?)
- @param file_path Path to this program
- @param startup_option Startup option to mysqld
-
- @retval 0 option handled
- @retval 1 Could not handle option
-*/
-
-static bool
-default_service_handling(char **argv,
- const char *servicename,
- const char *displayname,
- const char *file_path,
- const char *extra_opt,
- const char *account_name)
-{
- char path_and_service[FN_REFLEN+FN_REFLEN+32], *pos, *end;
- const char *opt_delim;
- end= path_and_service + sizeof(path_and_service)-3;
-
- /* We have to quote filename if it contains spaces */
- pos= add_quoted_string(path_and_service, file_path, end);
- if (extra_opt && *extra_opt)
- {
- /*
- Add option after file_path. There will be zero or one extra option. It's
- assumed to be --defaults-file=file but isn't checked. The variable (not
- the option name) should be quoted if it contains a string.
- */
- *pos++= ' ';
- if ((opt_delim= strchr(extra_opt, '=')))
- {
- size_t length= ++opt_delim - extra_opt;
- pos= strnmov(pos, extra_opt, length);
- }
- else
- opt_delim= extra_opt;
-
- pos= add_quoted_string(pos, opt_delim, end);
- }
- /* We must have servicename last */
- *pos++= ' ';
- (void) add_quoted_string(pos, servicename, end);
-
- if (Service.got_service_option(argv, "install"))
- {
- Service.Install(1, servicename, displayname, path_and_service,
- account_name);
- return 0;
- }
- if (Service.got_service_option(argv, "install-manual"))
- {
- Service.Install(0, servicename, displayname, path_and_service,
- account_name);
- return 0;
- }
- if (Service.got_service_option(argv, "remove"))
- {
- Service.Remove(servicename);
- return 0;
- }
- return 1;
-}
-
-/* Remove service name from the command line arguments, and pass
-resulting command line to the service via opt_args.*/
-#include <vector>
-static void service_init_cmdline_args(int argc, char **argv)
-{
- start_mode= 1;
- use_opt_args= 1;
-
- if(argc == 1)
- {
- opt_argc= argc;
- opt_argv= argv;
- }
- else
- {
- static std::vector<char *> argv_no_service;
- for (int i= 0; argv[i]; i++)
- argv_no_service.push_back(argv[i]);
- // Remove the last argument, service name
- argv_no_service[argv_no_service.size() - 1]= 0;
- opt_argc= (int)argv_no_service.size() - 1;
- opt_argv= &argv_no_service[0];
- }
- DBUG_ASSERT(!opt_argv[opt_argc]);
-}
-
-int mysqld_main(int argc, char **argv)
-{
- my_progname= argv[0];
-
- /*
- When several instances are running on the same machine, we
- need to have an unique named hEventShudown through the
- application PID e.g.: MySQLShutdown1890; MySQLShutdown2342
- */
- int10_to_str((int) GetCurrentProcessId(),strmov(shutdown_event_name,
- "MySQLShutdown"), 10);
-
- /* Must be initialized early for comparison of service name */
- system_charset_info= &my_charset_utf8mb3_general_ci;
-
-#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
- pre_initialize_performance_schema();
-#endif /*WITH_PERFSCHEMA_STORAGE_ENGINE */
-
- if (my_init())
- {
- fprintf(stderr, "my_init() failed.");
- return 1;
- }
-
-
- char file_path[FN_REFLEN];
- my_path(file_path, argv[0], ""); /* Find name in path */
- fn_format(file_path,argv[0],file_path,"", MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS);
-
-
- if (argc == 2)
- {
- if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME,
- file_path, "", NULL))
- return 0;
-
- if (Service.IsService(argv[1])) /* Start an optional service */
- {
- /*
- Only add the service name to the groups read from the config file
- if it's not "MySQL". (The default service name should be 'mysqld'
- but we started a bad tradition by calling it MySQL from the start
- and we are now stuck with it.
- */
- if (my_strcasecmp(system_charset_info, argv[1],"mysql"))
- load_default_groups[load_default_groups_sz-2]= argv[1];
- service_init_cmdline_args(argc, argv);
- Service.Init(argv[1], mysql_service);
- return 0;
- }
- }
- else if (argc == 3) /* install or remove any optional service */
- {
- if (!default_service_handling(argv, argv[2], argv[2], file_path, "",
- NULL))
- return 0;
- if (Service.IsService(argv[2]))
- {
- /*
- mysqld was started as
- mysqld --defaults-file=my_path\my.ini service-name
- */
- if (my_strcasecmp(system_charset_info, argv[2],"mysql"))
- load_default_groups[load_default_groups_sz-2]= argv[2];
- service_init_cmdline_args(argc, argv);
- Service.Init(argv[2], mysql_service);
- return 0;
- }
- }
- else if (argc == 4 || argc == 5)
- {
- /*
- This may seem strange, because we handle --local-service while
- preserving 4.1's behavior of allowing any one other argument that is
- passed to the service on startup. (The assumption is that this is
- --defaults-file=file, but that was not enforced in 4.1, so we don't
- enforce it here.)
- */
- const char *extra_opt= NullS;
- const char *account_name = NullS;
- int index;
- for (index = 3; index < argc; index++)
- {
- if (!strcmp(argv[index], "--local-service"))
- account_name= "NT AUTHORITY\\LocalService";
- else
- extra_opt= argv[index];
- }
-
- if (argc == 4 || account_name)
- if (!default_service_handling(argv, argv[2], argv[2], file_path,
- extra_opt, account_name))
- return 0;
- }
- else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME))
- {
- /* start the default service */
- service_init_cmdline_args(argc, argv);
- Service.Init(MYSQL_SERVICENAME, mysql_service);
- return 0;
- }
-
- /* Start as standalone server */
- Service.my_argc=argc;
- Service.my_argv=argv;
- mysql_service(NULL);
- return 0;
-}
-#endif
-
-
static bool read_init_file(char *file_name)
{
MYSQL_FILE *file;
diff --git a/sql/mysqld.h b/sql/mysqld.h
index db8d9b4b1aa..6f0db56d309 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -952,4 +952,14 @@ extern ulong opt_binlog_dbug_fsync_sleep;
extern uint volatile global_disable_checkpoint;
extern my_bool opt_help;
+extern int mysqld_main(int argc, char **argv);
+
+#ifdef _WIN32
+extern HANDLE hEventShutdown;
+extern void mysqld_win_initiate_shutdown();
+extern void mysqld_win_set_startup_complete();
+extern void mysqld_set_service_status_callback(void (*)(DWORD, DWORD, DWORD));
+extern void mysqld_win_set_service_name(const char *name);
+#endif
+
#endif /* MYSQLD_INCLUDED */
diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc
deleted file mode 100644
index 9c754763aab..00000000000
--- a/sql/nt_servc.cc
+++ /dev/null
@@ -1,555 +0,0 @@
-/**
- @file
-
- @brief
- Windows NT Service class library.
-
- Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
- This file is public domain and comes with NO WARRANTY of any kind
-*/
-#include <windows.h>
-#include <process.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "nt_servc.h"
-
-
-static NTService *pService;
-
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-NTService::NTService()
-{
-
- bOsNT = FALSE;
- //service variables
- ServiceName = NULL;
- hExitEvent = 0;
- bPause = FALSE;
- bRunning = FALSE;
- hThreadHandle = 0;
- fpServiceThread = NULL;
-
- //time-out variables
- nStartTimeOut = 15000;
- nStopTimeOut = 86400000;
- nPauseTimeOut = 5000;
- nResumeTimeOut = 5000;
-
- //install variables
- dwDesiredAccess = SERVICE_ALL_ACCESS;
- dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- dwStartType = SERVICE_AUTO_START;
- dwErrorControl = SERVICE_ERROR_NORMAL;
- szLoadOrderGroup = NULL;
- lpdwTagID = NULL;
- szDependencies = NULL;
-
- my_argc = 0;
- my_argv = NULL;
- hShutdownEvent = 0;
- nError = 0;
- dwState = 0;
-}
-
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-NTService::~NTService()
-{
- if (ServiceName != NULL) delete[] ServiceName;
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-
-
-/**
- Registers the main service thread with the service manager.
-
- @param ServiceThread pointer to the main programs entry function
- when the service is started
-*/
-
-
-long NTService::Init(LPCSTR szInternName, THREAD_FC ServiceThread)
-{
-
- pService = this;
-
- fpServiceThread = ServiceThread;
- ServiceName = new char[lstrlen(szInternName)+1];
- lstrcpy(ServiceName,szInternName);
-
- SERVICE_TABLE_ENTRY stb[] =
- {
- { (char *)szInternName, ServiceMain} ,
- { NULL, NULL }
- };
-
- return StartServiceCtrlDispatcher(stb); //register with the Service Manager
-}
-
-
-/**
- Installs the service with Service manager.
-
- nError values:
- - 0 success
- - 1 Can't open the Service manager
- - 2 Failed to create service.
-*/
-
-
-BOOL NTService::Install(int startType, LPCSTR szInternName,
- LPCSTR szDisplayName,
- LPCSTR szFullPath, LPCSTR szAccountName,
- LPCSTR szPassword)
-{
- BOOL ret_val=FALSE;
- SC_HANDLE newService, scm;
-
- if (!SeekStatus(szInternName,1))
- return FALSE;
-
- char szFilePath[_MAX_PATH];
- GetModuleFileName(NULL, szFilePath, sizeof(szFilePath));
-
- // open a connection to the SCM
- if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
- printf("Failed to install the service (Couldn't open the SCM)\n");
- else // Install the new service
- {
- if (!(newService=
- CreateService(scm,
- szInternName,
- szDisplayName,
- dwDesiredAccess,//default: SERVICE_ALL_ACCESS
- dwServiceType, //default: SERVICE_WIN32_OWN_PROCESS
- //default: SERVICE_AUTOSTART
- (startType == 1 ? SERVICE_AUTO_START :
- SERVICE_DEMAND_START),
- dwErrorControl, //default: SERVICE_ERROR_NORMAL
- szFullPath, //exec full path
- szLoadOrderGroup, //default: NULL
- lpdwTagID, //default: NULL
- szDependencies, //default: NULL
- szAccountName, //default: NULL
- szPassword))) //default: NULL
- printf("Failed to install the service (Couldn't create service)\n");
- else
- {
- printf("Service successfully installed.\n");
- CloseServiceHandle(newService);
- ret_val=TRUE; // Everything went ok
- }
- CloseServiceHandle(scm);
- }
- return ret_val;
-}
-
-
-/**
- Removes the service.
-
- nError values:
- - 0 success
- - 1 Can't open the Service manager
- - 2 Failed to locate service
- - 3 Failed to delete service.
-*/
-
-
-BOOL NTService::Remove(LPCSTR szInternName)
-{
- BOOL ret_value=FALSE;
- SC_HANDLE service, scm;
-
- if (!SeekStatus(szInternName,0))
- return FALSE;
-
- nError=0;
-
- // open a connection to the SCM
- if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
- {
- printf("Failed to remove the service (Couldn't open the SCM)\n");
- }
- else
- {
- if ((service = OpenService(scm,szInternName, DELETE)))
- {
- if (!DeleteService(service))
- printf("Failed to remove the service\n");
- else
- {
- printf("Service successfully removed.\n");
- ret_value=TRUE; // everything went ok
- }
- CloseServiceHandle(service);
- }
- else
- printf("Failed to remove the service (Couldn't open the service)\n");
- CloseServiceHandle(scm);
- }
- return ret_value;
-}
-
-/**
- this function should be called before the app. exits to stop
- the service
-*/
-void NTService::Stop(void)
-{
- SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, 60000);
- StopService();
- SetStatus(SERVICE_STOPPED, NO_ERROR, 0, 1, 1000);
-}
-
-/**
- This is the function that is called from the
- service manager to start the service.
-*/
-
-
-void NTService::ServiceMain(DWORD argc, LPTSTR *argv)
-{
-
- // registration function
- if (!(pService->hServiceStatusHandle =
- RegisterServiceCtrlHandler(pService->ServiceName,
- NTService::ServiceCtrlHandler)))
- goto error;
-
- // notify SCM of progress
- if (!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 1, 8000))
- goto error;
-
- // create the exit event
- if (!(pService->hExitEvent = CreateEvent (0, TRUE, FALSE,0)))
- goto error;
-
- if (!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 3,
- pService->nStartTimeOut))
- goto error;
-
- // save start arguments
- pService->my_argc=argc;
- pService->my_argv=argv;
-
- // start the service
- if (!pService->StartService())
- goto error;
-
- // wait for exit event
- WaitForSingleObject (pService->hExitEvent, INFINITE);
-
- // wait for thread to exit
- if (WaitForSingleObject (pService->hThreadHandle, INFINITE) == WAIT_TIMEOUT)
- CloseHandle(pService->hThreadHandle);
-
- pService->Exit(0);
- return;
-
-error:
- pService->Exit(GetLastError());
- return;
-}
-
-
-
-void NTService::SetRunning()
-{
- if (pService)
- pService->SetStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
-}
-
-void NTService::SetSlowStarting(unsigned long timeout)
-{
- if (pService)
- pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 0, timeout);
-}
-
-
-/* ------------------------------------------------------------------------
- StartService() - starts the application thread
- -------------------------------------------------------------------------- */
-
-BOOL NTService::StartService()
-{
- // Start the real service's thread (application)
- if (!(hThreadHandle = (HANDLE) _beginthread(fpServiceThread,0,
- (void *) this)))
- return FALSE;
- bRunning = TRUE;
- return TRUE;
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-void NTService::StopService()
-{
- bRunning=FALSE;
-
- // Set the event for application
- if (hShutdownEvent)
- SetEvent(hShutdownEvent);
-
- // Set the event for ServiceMain
- SetEvent(hExitEvent);
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-void NTService::PauseService()
-{
- bPause = TRUE;
- SuspendThread(hThreadHandle);
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-void NTService::ResumeService()
-{
- bPause=FALSE;
- ResumeThread(hThreadHandle);
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-BOOL NTService::SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
- DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint,
- DWORD dwWaitHint)
-{
- BOOL bRet;
- SERVICE_STATUS serviceStatus;
-
- dwState=dwCurrentState;
-
- serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- serviceStatus.dwCurrentState = dwCurrentState;
-
- if (dwCurrentState == SERVICE_START_PENDING)
- serviceStatus.dwControlsAccepted = 0; //don't accept control events
- else
- serviceStatus.dwControlsAccepted = (SERVICE_ACCEPT_STOP |
- SERVICE_ACCEPT_PAUSE_CONTINUE |
- SERVICE_ACCEPT_SHUTDOWN);
-
- // if a specific exit code is defined,set up the win32 exit code properly
- if (dwServiceSpecificExitCode == 0)
- serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
- else
- serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
-
- serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
-
- serviceStatus.dwCheckPoint = dwCheckPoint;
- serviceStatus.dwWaitHint = dwWaitHint;
-
- // Pass the status to the Service Manager
- if (!(bRet=SetServiceStatus (hServiceStatusHandle, &serviceStatus)))
- StopService();
-
- return bRet;
-}
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-void NTService::ServiceCtrlHandler(DWORD ctrlCode)
-{
- DWORD dwState;
-
- if (!pService)
- return;
-
- dwState=pService->dwState; // get current state
-
- switch(ctrlCode) {
- case SERVICE_CONTROL_SHUTDOWN:
- case SERVICE_CONTROL_STOP:
- dwState = SERVICE_STOP_PENDING;
- pService->SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1,
- pService->nStopTimeOut);
- pService->StopService();
- break;
-
- default:
- pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
- break;
- }
- //pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
-}
-
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-
-void NTService::Exit(DWORD error)
-{
- if (hExitEvent)
- CloseHandle(hExitEvent);
-
- // Send a message to the scm to tell that we stop
- if (hServiceStatusHandle)
- SetStatus(SERVICE_STOPPED, error,0, 0, 0);
-
- // If the thread has started kill it ???
- // if (hThreadHandle) CloseHandle(hThreadHandle);
-
-}
-
-/* ------------------------------------------------------------------------
-
- -------------------------------------------------------------------------- */
-
-BOOL NTService::SeekStatus(LPCSTR szInternName, int OperationType)
-{
- BOOL ret_value=FALSE;
- SC_HANDLE service, scm;
-
- // open a connection to the SCM
- if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE)))
- {
- DWORD ret_error=GetLastError();
- if (ret_error == ERROR_ACCESS_DENIED)
- {
- printf("Install/Remove of the Service Denied!\n");
- if (!is_super_user())
- printf("That operation should be made by an user with Administrator privileges!\n");
- }
- else
- printf("There is a problem for to open the Service Control Manager!\n");
- }
- else
- {
- if (OperationType == 1)
- {
- /* an install operation */
- if ((service = OpenService(scm,szInternName, SERVICE_ALL_ACCESS )))
- {
- LPQUERY_SERVICE_CONFIG ConfigBuf;
- DWORD dwSize;
-
- ConfigBuf = (LPQUERY_SERVICE_CONFIG) LocalAlloc(LPTR, 4096);
- printf("The service already exists!\n");
- if (QueryServiceConfig(service,ConfigBuf,4096,&dwSize))
- printf("The current server installed: %s\n",
- ConfigBuf->lpBinaryPathName);
- LocalFree(ConfigBuf);
- CloseServiceHandle(service);
- }
- else
- ret_value=TRUE;
- }
- else
- {
- /* a remove operation */
- if (!(service = OpenService(scm,szInternName, SERVICE_ALL_ACCESS )))
- printf("The service doesn't exist!\n");
- else
- {
- SERVICE_STATUS ss;
-
- memset(&ss, 0, sizeof(ss));
- if (QueryServiceStatus(service,&ss))
- {
- DWORD dwState = ss.dwCurrentState;
- if (dwState == SERVICE_RUNNING)
- printf("Failed to remove the service because the service is running\nStop the service and try again\n");
- else if (dwState == SERVICE_STOP_PENDING)
- printf("\
-Failed to remove the service because the service is in stop pending state!\n\
-Wait 30 seconds and try again.\n\
-If this condition persist, reboot the machine and try again\n");
- else
- ret_value= TRUE;
- }
- CloseServiceHandle(service);
- }
- }
- CloseServiceHandle(scm);
- }
-
- return ret_value;
-}
-/* ------------------------------------------------------------------------
- -------------------------------------------------------------------------- */
-BOOL NTService::IsService(LPCSTR ServiceName)
-{
- BOOL ret_value=FALSE;
- SC_HANDLE service, scm;
-
- if ((scm= OpenSCManager(0, 0,SC_MANAGER_ENUMERATE_SERVICE)))
- {
- if ((service = OpenService(scm,ServiceName, SERVICE_QUERY_STATUS)))
- {
- ret_value=TRUE;
- CloseServiceHandle(service);
- }
- CloseServiceHandle(scm);
- }
- return ret_value;
-}
-/* ------------------------------------------------------------------------
- -------------------------------------------------------------------------- */
-BOOL NTService::got_service_option(char **argv, const char *service_option)
-{
- char *option;
- for (option= argv[1]; *option; option++)
- if (!strcmp(option, service_option))
- return TRUE;
- return FALSE;
-}
-/* ------------------------------------------------------------------------
- -------------------------------------------------------------------------- */
-BOOL NTService::is_super_user()
-{
- HANDLE hAccessToken;
- UCHAR InfoBuffer[1024];
- PTOKEN_GROUPS ptgGroups=(PTOKEN_GROUPS)InfoBuffer;
- DWORD dwInfoBufferSize;
- PSID psidAdministrators;
- SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
- UINT x;
- BOOL ret_value=FALSE;
-
- if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE,&hAccessToken ))
- {
- if (GetLastError() != ERROR_NO_TOKEN)
- return FALSE;
-
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hAccessToken))
- return FALSE;
- }
-
- ret_value= GetTokenInformation(hAccessToken,TokenGroups,InfoBuffer,
- 1024, &dwInfoBufferSize);
-
- CloseHandle(hAccessToken);
-
- if (!ret_value )
- return FALSE;
-
- if (!AllocateAndInitializeSid(&siaNtAuthority, 2,
- SECURITY_BUILTIN_DOMAIN_RID,
- DOMAIN_ALIAS_RID_ADMINS,
- 0, 0, 0, 0, 0, 0,
- &psidAdministrators))
- return FALSE;
-
- ret_value = FALSE;
-
- for (x=0;x<ptgGroups->GroupCount;x++)
- {
- if ( EqualSid(psidAdministrators, ptgGroups->Groups[x].Sid) )
- {
- ret_value = TRUE;
- break;
- }
-
- }
- FreeSid(psidAdministrators);
- return ret_value;
-}
diff --git a/sql/nt_servc.h b/sql/nt_servc.h
deleted file mode 100644
index 8ba29519c8f..00000000000
--- a/sql/nt_servc.h
+++ /dev/null
@@ -1,113 +0,0 @@
-#ifndef NT_SERVC_INCLUDED
-#define NT_SERVC_INCLUDED
-
-/**
- @file
-
- @brief
- Windows NT Service class library
-
- Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
- This file is public domain and comes with NO WARRANTY of any kind
-*/
-
-// main application thread
-typedef void (*THREAD_FC)(void *);
-
-class NTService
-{
- public:
- NTService();
- ~NTService();
-
- BOOL bOsNT; ///< true if OS is NT, false for Win95
- //install optinos
- DWORD dwDesiredAccess;
- DWORD dwServiceType;
- DWORD dwStartType;
- DWORD dwErrorControl;
-
- LPSTR szLoadOrderGroup;
- LPDWORD lpdwTagID;
- LPSTR szDependencies;
- OSVERSIONINFO osVer;
-
- // time-out (in milisec)
- int nStartTimeOut;
- int nStopTimeOut;
- int nPauseTimeOut;
- int nResumeTimeOut;
-
- //
- DWORD my_argc;
- LPTSTR *my_argv;
- HANDLE hShutdownEvent;
- int nError;
- DWORD dwState;
-
- //init service entry point
- long Init(LPCSTR szInternName,THREAD_FC ServiceThread);
-
- //application shutdown event
- void SetShutdownEvent(HANDLE hEvent){ hShutdownEvent=hEvent; }
-
-
- //service install / un-install
- BOOL Install(int startType,LPCSTR szInternName,LPCSTR szDisplayName,
- LPCSTR szFullPath, LPCSTR szAccountName=NULL,
- LPCSTR szPassword=NULL);
- BOOL SeekStatus(LPCSTR szInternName, int OperationType);
- BOOL Remove(LPCSTR szInternName);
- BOOL IsService(LPCSTR ServiceName);
- BOOL got_service_option(char **argv, const char *service_option);
- BOOL is_super_user();
-
- /*
- SetRunning() is to be called by the application
- when initialization completes and it can accept
- stop request
- */
- void SetRunning(void);
-
- /**
- Sets a timeout after which SCM will abort service startup if SetRunning()
- was not called or the timeout was not extended with another call to
- SetSlowStarting(). Should be called when static initialization completes,
- and the variable initialization part begins
-
- @arg timeout the timeout to pass to the SCM (in milliseconds)
- */
- void SetSlowStarting(unsigned long timeout);
-
- /*
- Stop() is to be called by the application to stop
- the service
- */
- void Stop(void);
-
- protected:
- LPSTR ServiceName;
- HANDLE hExitEvent;
- SERVICE_STATUS_HANDLE hServiceStatusHandle;
- BOOL bPause;
- BOOL bRunning;
- HANDLE hThreadHandle;
- THREAD_FC fpServiceThread;
-
- void PauseService();
- void ResumeService();
- void StopService();
- BOOL StartService();
-
- static void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
- static void WINAPI ServiceCtrlHandler (DWORD ctrlCode);
-
- void Exit(DWORD error);
- BOOL SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
- DWORD dwServiceSpecificExitCode,
- DWORD dwCheckPoint,DWORD dwWaitHint);
-
-};
-/* ------------------------- the end -------------------------------------- */
-
-#endif /* NT_SERVC_INCLUDED */
diff --git a/sql/winmain.cc b/sql/winmain.cc
new file mode 100644
index 00000000000..fb5da40cf2f
--- /dev/null
+++ b/sql/winmain.cc
@@ -0,0 +1,371 @@
+/* Copyright (C) 2020 MariaDB Corporation
+
+ 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
+*/
+
+/*
+ main() function for the server on Windows is implemented here.
+ The core functionality is implemented elsewhere, in mysqld_main(), and running as
+ service is done here.
+
+ Main tasks of the service are
+
+ 1. Report current status back to service control manager. Here we're
+ providing callbacks so code outside of winmain.cc can call it
+ (via mysqld_set_service_status_callback())
+
+ 2. React to notification, the only one we care about is the "stop"
+ notification. we initiate shutdown, when instructed.
+
+ Note that our service might not be too Windows-friendly, as it might take
+ a while to startup (recovery), and a while to shut down(innodb cleanups).
+
+ Most of the code more of less standard service stuff, taken from Microsoft
+ docs examples.
+
+ Notable oddity in running services, is that we do not know for sure,
+ whether we should run as a service or not (there is no --service parameter that
+ would tell).Heuristics are used, and if the last command line argument is
+ valid service name, we try to run as service, but fallback to usual process
+ if this fails.
+
+ As an example, even if mysqld.exe is started with command line like "mysqld.exe --help",
+ it is entirely possible that mysqld.exe run as service "--help".
+
+ Apart from that, now deprecated and obsolete service registration/removal functionality is
+ still provided (mysqld.exe --install/--remove)
+*/
+
+#include <my_global.h>
+#include <mysqld.h>
+#include <log.h>
+
+#include <stdio.h>
+#include <windows.h>
+#include <string>
+#include <cassert>
+
+static SERVICE_STATUS svc_status{SERVICE_WIN32_OWN_PROCESS};
+static SERVICE_STATUS_HANDLE svc_status_handle;
+static char *svc_name;
+
+static char **orig_argv;
+static int orig_argc;
+
+static int install_service(int argc, char **argv, const char *name);
+static int remove_service(const char *name);
+
+/*
+ Report service status to SCM. This function is indirectly invoked
+ by the server to report state transitions.
+
+ 1. from START_PENDING to SERVICE_RUNNING, when we start accepting user connections
+ 2. from SERVICE_RUNNING to STOP_PENDING, when we start shutdown
+ 3. from STOP_PENDING to SERVICE_STOPPED, in mysqld_exit()
+ sometimes also START_PENDING to SERVICE_STOPPED, on startup errors
+*/
+static void report_svc_status(DWORD current_state, DWORD exit_code, DWORD wait_hint)
+{
+ if (!svc_status_handle)
+ return;
+
+ static DWORD check_point= 1;
+ svc_status.dwCurrentState= current_state;
+ svc_status.dwWaitHint= wait_hint;
+
+ if (exit_code)
+ {
+ svc_status.dwWin32ExitCode= ERROR_SERVICE_SPECIFIC_ERROR;
+ svc_status.dwServiceSpecificExitCode= exit_code;
+ }
+ else
+ {
+ svc_status.dwWin32ExitCode= 0;
+ }
+
+ if (current_state == SERVICE_START_PENDING)
+ svc_status.dwControlsAccepted= 0;
+ else
+ svc_status.dwControlsAccepted= SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
+
+ if ((current_state == SERVICE_RUNNING) || (current_state == SERVICE_STOPPED))
+ svc_status.dwCheckPoint= 0;
+ else
+ svc_status.dwCheckPoint= check_point++;
+
+ SetServiceStatus(svc_status_handle, &svc_status);
+}
+
+/* Report unexpected errors. */
+static void svc_report_event(const char *svc_name, const char *command)
+{
+ char buffer[80];
+ sprintf_s(buffer, "mariadb service %s, %s failed with %d",
+ svc_name, command, GetLastError());
+ OutputDebugString(buffer);
+}
+
+/*
+ Service control function.
+ Reacts to service stop, initiates shutdown.
+*/
+static void WINAPI svc_ctrl_handle(DWORD cntrl)
+{
+ switch (cntrl)
+ {
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+ sql_print_information(
+ "Windows service \"%s\": received %s",
+ svc_name,
+ cntrl == SERVICE_CONTROL_STOP? "SERVICE_CONTROL_STOP": "SERVICE_CONTROL_SHUTDOWN");
+
+ /* The below will also set the status to STOP_PENDING. */
+ mysqld_win_initiate_shutdown();
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ default:
+ break;
+ }
+}
+
+/* Service main routine, mainly runs mysqld_main() */
+static void WINAPI svc_main(DWORD svc_argc, char **svc_argv)
+{
+ /* Register the handler function for the service */
+ char *name= svc_argv[0];
+
+ svc_status_handle= RegisterServiceCtrlHandler(name, svc_ctrl_handle);
+ if (!svc_status_handle)
+ {
+ svc_report_event(name, "RegisterServiceCtrlHandler");
+ return;
+ }
+ report_svc_status(SERVICE_START_PENDING, NO_ERROR, 0);
+
+ /* Make server report service status via our callback.*/
+ mysqld_set_service_status_callback(report_svc_status);
+
+ /* This would add service name entry to load_defaults.*/
+ mysqld_win_set_service_name(name);
+
+ /*
+ Do not pass the service name parameter (last on the command line)
+ to mysqld_main(), it is unaware of it.
+ */
+ orig_argv[orig_argc - 1]= 0;
+ mysqld_main(orig_argc - 1, orig_argv);
+}
+
+/*
+ This start the service. Sometimes it will fail, because
+ currently we do not know for sure whether we run as service or not.
+ If this fails, the fallback is to run as normal process.
+*/
+static int run_as_service(char *name)
+{
+ SERVICE_TABLE_ENTRY stb[]= {{name, svc_main}, {0, 0}};
+ if (!StartServiceCtrlDispatcher(stb))
+ {
+ assert(GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ Check for valid existing service name.
+ Part of our guesswork, whether we run as service or not.
+*/
+static bool is_existing_service(const char *name)
+{
+ if (strchr(name, '\\') || strchr(name, '/'))
+ {
+ /* Invalid characters in service name */
+ return false;
+ }
+
+ SC_HANDLE sc_service= 0, scm= 0;
+ bool ret= ((scm= OpenSCManager(0, 0, SC_MANAGER_ENUMERATE_SERVICE)) != 0) &&
+ ((sc_service= OpenService(scm, name, SERVICE_QUERY_STATUS)) != 0);
+
+ if (sc_service)
+ CloseServiceHandle(sc_service);
+ if (scm)
+ CloseServiceHandle(scm);
+
+ return ret;
+}
+
+/*
+ If service name is not given to --install/--remove
+ it is assumed to be "MySQL" (traditional handling)
+*/
+static const char *get_svc_name(const char *arg)
+{
+ return arg ? arg : "MySQL";
+}
+
+/*
+ Main function on Windows.
+ Runs mysqld as normal process, or as a service.
+
+ Plus, the obsolete functionality to register/remove services.
+*/
+int main(int argc, char **argv)
+{
+ orig_argv= argv;
+ orig_argc= argc;
+
+ /*
+ If no special arguments are given, service name is nor present
+ run as normal program.
+ */
+ if (argc == 1)
+ return mysqld_main(argc, argv);
+
+ auto cmd= argv[1];
+
+ /* Handle install/remove */
+ if (!strcmp(cmd, "--install") || !strcmp(cmd, "--install-manual"))
+ return install_service(argc, argv, get_svc_name(argv[2]));
+
+ if (!strcmp(cmd, "--remove"))
+ return remove_service(get_svc_name(argv[2]));
+
+ /* Try to run as service, and fallback to mysqld_main(), if this fails */
+ svc_name= argv[argc - 1];
+ if (is_existing_service(svc_name) && !run_as_service(svc_name))
+ return 0;
+ svc_name= 0;
+
+ /* Run as normal program.*/
+ return mysqld_main(argc, argv);
+}
+
+
+/*
+ Register/remove services functionality.
+ This is kept for backward compatibility only, and is
+ superseeded by much more versatile mysql_install_db.exe
+
+ "mysqld --remove=svc" has no advantage over
+ OS own "sc delete svc"
+*/
+static void ATTRIBUTE_NORETURN die(const char *func, const char *name)
+{
+ DWORD err= GetLastError();
+ fprintf(stderr, "FATAL ERROR : %s failed (%lu)\n", func, err);
+ switch (err)
+ {
+ case ERROR_SERVICE_EXISTS:
+ fprintf(stderr, "Service %s already exists.\n", name);
+ break;
+ case ERROR_SERVICE_DOES_NOT_EXIST:
+ fprintf(stderr, "Service %s does not exist.\n", name);
+ break;
+ case ERROR_ACCESS_DENIED:
+ fprintf(stderr, "Access is denied. "
+ "Make sure to run as elevated admin user.\n");
+ break;
+ case ERROR_INVALID_NAME:
+ fprintf(stderr, "Invalid service name '%s'\n", name);
+ default:
+ break;
+ }
+ exit(1);
+}
+
+static inline std::string quoted(const char *src)
+{
+ std::string s;
+ s.append("\"").append(src).append("\"");
+ return s;
+}
+
+static int install_service(int argc, char **argv, const char *name)
+{
+ std::string cmdline;
+
+ char path[MAX_PATH];
+ auto nSize = GetModuleFileName(0, path, sizeof(path));
+
+ if (nSize == (DWORD) sizeof(path) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ die("GetModuleName", name);
+
+ cmdline.append(quoted(path));
+
+ const char *user= 0;
+ // mysqld --install[-manual] name ...[--local-service]
+ if (argc > 2)
+ {
+ for (int i= 3; argv[i]; i++)
+ {
+ if (!strcmp(argv[i], "--local-service"))
+ user= "NT AUTHORITY\\LocalService";
+ else
+ {
+ cmdline.append(" ").append(quoted(argv[i]));
+ }
+ }
+ }
+ cmdline.append(" ").append(quoted(name));
+
+ DWORD start_type;
+ if (!strcmp(argv[1], "--install-manual"))
+ start_type= SERVICE_DEMAND_START;
+ else
+ start_type= SERVICE_AUTO_START;
+
+ SC_HANDLE scm, sc_service;
+ if (!(scm= OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE)))
+ die("OpenSCManager", name);
+
+ if (!(sc_service= CreateService(
+ scm, name, name, SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS, start_type, SERVICE_ERROR_NORMAL,
+ cmdline.c_str(), 0, 0, 0, user, 0)))
+ die("CreateService", name);
+
+ char description[]= "MariaDB database server";
+ SERVICE_DESCRIPTION sd= {description};
+ ChangeServiceConfig2(sc_service, SERVICE_CONFIG_DESCRIPTION, &sd);
+
+ CloseServiceHandle(sc_service);
+ CloseServiceHandle(scm);
+
+ printf("Service '%s' successfully installed.\n", name);
+ return 0;
+}
+
+static int remove_service(const char *name)
+{
+ SC_HANDLE scm, sc_service;
+
+ if (!(scm= OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE)))
+ die("OpenSCManager", name);
+
+ if (!(sc_service= OpenService(scm, name, DELETE)))
+ die("OpenService", name);
+
+ if (!DeleteService(sc_service))
+ die("DeleteService", name);
+
+ CloseServiceHandle(sc_service);
+ CloseServiceHandle(scm);
+
+ printf("Service '%s' successfully deleted.\n", name);
+ return 0;
+}
diff --git a/unittest/sql/CMakeLists.txt b/unittest/sql/CMakeLists.txt
index 987e78433a4..b7923511b97 100644
--- a/unittest/sql/CMakeLists.txt
+++ b/unittest/sql/CMakeLists.txt
@@ -19,15 +19,8 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/unittest/mytap
${CMAKE_SOURCE_DIR}/extra/yassl/include)
+ADD_EXECUTABLE(explain_filename-t explain_filename-t.cc dummy_builtins.cc)
-IF(WIN32)
- ADD_EXECUTABLE(explain_filename-t
- explain_filename-t.cc
- dummy_builtins.cc
- ../../sql/nt_servc.cc)
-ELSE()
- ADD_EXECUTABLE(explain_filename-t explain_filename-t.cc dummy_builtins.cc)
-ENDIF()
TARGET_LINK_LIBRARIES(explain_filename-t sql mytap)
MY_ADD_TEST(explain_filename)