diff options
25 files changed, 1236 insertions, 220 deletions
diff --git a/server-tools/instance-manager/IMService.cpp b/server-tools/instance-manager/IMService.cpp new file mode 100755 index 00000000000..920a0f3db0c --- /dev/null +++ b/server-tools/instance-manager/IMService.cpp @@ -0,0 +1,73 @@ +#include <windows.h> +#include "log.h" +#include "options.h" +#include "IMService.h" + +IMService::IMService(void) +{ + serviceName = "MySqlManager"; + displayName = "MySQL Manager"; +} + +IMService::~IMService(void) +{ +} + +void IMService::Stop() +{ + ReportStatus(SERVICE_STOP_PENDING); + // stop the IM work +} + +void IMService::Run() +{ + // report to the SCM that we're about to start + ReportStatus((DWORD)SERVICE_START_PENDING); + + // init goes here + + ReportStatus((DWORD)SERVICE_RUNNING); + + // wait for main loop to terminate +} + +void IMService::Log(const char *msg) +{ + log_info(msg); +} + +int HandleServiceOptions(Options options) +{ + int ret_val = 0; + + IMService winService; + + if (options.install_as_service) + { + if (winService.IsInstalled()) + log_info("Service is already installed\n"); + else if (winService.Install()) + log_info("Service installed successfully\n"); + else + { + log_info("Service failed to install\n"); + ret_val = -1; + } + } + else if (options.remove_service) + { + if (! winService.IsInstalled()) + log_info("Service is not installed\n"); + else if (winService.Remove()) + log_info("Service removed successfully\n"); + else + { + log_info("Service failed to remove\n"); + ret_val = -1; + } + } + else + return (int)winService.Init(); + return ret_val; +} + diff --git a/server-tools/instance-manager/IMService.h b/server-tools/instance-manager/IMService.h new file mode 100755 index 00000000000..60c202fc561 --- /dev/null +++ b/server-tools/instance-manager/IMService.h @@ -0,0 +1,14 @@ +#pragma once +#include "windowsservice.h" + +class IMService : public WindowsService +{ +public: + IMService(void); + ~IMService(void); + +protected: + void Log(const char *msg); + void Stop(); + void Run(); +}; diff --git a/server-tools/instance-manager/WindowsService.cpp b/server-tools/instance-manager/WindowsService.cpp new file mode 100755 index 00000000000..851ac9beac8 --- /dev/null +++ b/server-tools/instance-manager/WindowsService.cpp @@ -0,0 +1,200 @@ +#include <windows.h> +#include <assert.h> +#include ".\windowsservice.h" + +static WindowsService *gService; + +WindowsService::WindowsService(void) +: statusCheckpoint(0), serviceName(NULL), inited(false), + dwAcceptedControls(SERVICE_ACCEPT_STOP) +{ + gService = this; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwServiceSpecificExitCode = 0; +} + +WindowsService::~WindowsService(void) +{ +} + +BOOL WindowsService::Install() +{ + bool ret_val=false; + SC_HANDLE newService; + SC_HANDLE scm; + + if (IsInstalled()) return true; + + // determine the name of the currently executing file + char szFilePath[_MAX_PATH]; + GetModuleFileName(NULL, szFilePath, sizeof(szFilePath)); + + // open a connection to the SCM + if (!(scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE))) + return false; + + newService = CreateService(scm, serviceName, displayName, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, szFilePath, + NULL, NULL, NULL, username, password); + + if (newService) + { + CloseServiceHandle(newService); + ret_val = true; + } + + CloseServiceHandle(scm); + return ret_val; +} + +BOOL WindowsService::Init() +{ + assert(serviceName != NULL); + + if (inited) return true; + + SERVICE_TABLE_ENTRY stb[] = + { + { (LPSTR)serviceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain}, + { NULL, NULL } + }; + inited = true; + return StartServiceCtrlDispatcher(stb); //register with the Service Manager +} + +BOOL WindowsService::Remove() +{ + bool ret_val = false; + + if (! IsInstalled()) + return true; + + // open a connection to the SCM + SC_HANDLE scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE); + if (! scm) + return false; + + SC_HANDLE service = OpenService(scm, serviceName, DELETE); + if (service) + { + if (DeleteService(service)) + ret_val = true; + DWORD dw = ::GetLastError(); + CloseServiceHandle(service); + } + + CloseServiceHandle(scm); + return ret_val; +} + +BOOL WindowsService::IsInstalled() +{ + BOOL ret_val = FALSE; + + SC_HANDLE scm = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + SC_HANDLE serv_handle = ::OpenService(scm, serviceName, SERVICE_QUERY_STATUS); + + ret_val = serv_handle != NULL; + + ::CloseServiceHandle(serv_handle); + ::CloseServiceHandle(scm); + + return ret_val; +} + +void WindowsService::SetAcceptedControls(DWORD acceptedControls) +{ + dwAcceptedControls = acceptedControls; +} + + +BOOL WindowsService::ReportStatus(DWORD currentState, DWORD waitHint, DWORD dwError) +{ + if(debugging) return TRUE; + + if(currentState == SERVICE_START_PENDING) + status.dwControlsAccepted = 0; + else + status.dwControlsAccepted = dwAcceptedControls; + + status.dwCurrentState = currentState; + status.dwWin32ExitCode = dwError != 0 ? ERROR_SERVICE_SPECIFIC_ERROR : NO_ERROR; + status.dwWaitHint = waitHint; + status.dwServiceSpecificExitCode = dwError; + + if(currentState == SERVICE_RUNNING || currentState == SERVICE_STOPPED) + { + status.dwCheckPoint = 0; + statusCheckpoint = 0; + } + else + status.dwCheckPoint = ++statusCheckpoint; + + // Report the status of the service to the service control manager. + BOOL result = SetServiceStatus(statusHandle, &status); + if (!result) + Log("ReportStatus failed"); + + return result; +} + +void WindowsService::RegisterAndRun(DWORD argc, LPTSTR *argv) +{ + statusHandle = ::RegisterServiceCtrlHandler(serviceName, ControlHandler); + if (statusHandle && ReportStatus(SERVICE_START_PENDING)) + Run(); + ReportStatus(SERVICE_STOPPED); +} + +void WindowsService::HandleControlCode(DWORD opcode) +{ + // Handle the requested control code. + switch(opcode) + { + case SERVICE_CONTROL_STOP: + // Stop the service. + status.dwCurrentState = SERVICE_STOP_PENDING; + Stop(); + break; + + case SERVICE_CONTROL_PAUSE: + status.dwCurrentState = SERVICE_PAUSE_PENDING; + Pause(); + break; + + case SERVICE_CONTROL_CONTINUE: + status.dwCurrentState = SERVICE_CONTINUE_PENDING; + Continue(); + break; + + case SERVICE_CONTROL_SHUTDOWN: + Shutdown(); + break; + + case SERVICE_CONTROL_INTERROGATE: + ReportStatus(status.dwCurrentState); + break; + + default: + // invalid control code + break; + } +} + +void WINAPI WindowsService::ServiceMain(DWORD argc, LPTSTR *argv) +{ + assert(gService != NULL); + + // register our service control handler: + gService->RegisterAndRun(argc, argv); +} + +void WINAPI WindowsService::ControlHandler(DWORD opcode) +{ + assert(gService != NULL); + + return gService->HandleControlCode(opcode); +} + + diff --git a/server-tools/instance-manager/WindowsService.h b/server-tools/instance-manager/WindowsService.h new file mode 100755 index 00000000000..b266bbca533 --- /dev/null +++ b/server-tools/instance-manager/WindowsService.h @@ -0,0 +1,44 @@ +#pragma once + +class WindowsService +{ +protected: + bool inited; + const char *serviceName; + const char *displayName; + const char *username; + const char *password; + SERVICE_STATUS_HANDLE statusHandle; + DWORD statusCheckpoint; + SERVICE_STATUS status; + DWORD dwAcceptedControls; + bool debugging; + +public: + WindowsService(void); + ~WindowsService(void); + + BOOL Install(); + BOOL Remove(); + BOOL Init(); + BOOL IsInstalled(); + void SetAcceptedControls(DWORD acceptedControls); + void Debug(bool debugFlag) { debugging = debugFlag; } + +public: + static void WINAPI ServiceMain(DWORD argc, LPTSTR * argv); + static void WINAPI ControlHandler(DWORD CtrlType); + +protected: + virtual void Run() = 0; + virtual void Stop() {} + virtual void Shutdown() {} + virtual void Pause() {} + virtual void Continue() {} + virtual void Log(const char *msg) {} + + BOOL ReportStatus(DWORD currentStatus, DWORD waitHint=3000, DWORD dwError=0); + void HandleControlCode(DWORD opcode); + void RegisterAndRun(DWORD argc, LPTSTR *argv); +}; + diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 2357f97fd93..3cc5039a59c 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -22,6 +22,7 @@ #include "mysql_manager_error.h" #include "protocol.h" #include "buffer.h" +#include "options.h" #include <m_string.h> #include <mysql.h> @@ -469,7 +470,7 @@ int Show_instance_log::execute(struct st_net *net, ulong connection_id) size_t buff_size; int read_len; /* calculate buffer size */ - struct stat file_stat; + MY_STAT file_stat; /* my_fstat doesn't use the flag parameter */ if (my_fstat(fd, &file_stat, MYF(0))) @@ -481,7 +482,7 @@ int Show_instance_log::execute(struct st_net *net, ulong connection_id) read_len= my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0)); char *bf= (char*) malloc(sizeof(char)*buff_size); - if ((read_len= my_read(fd, bf, buff_size, MYF(0))) < 0) + if ((read_len= my_read(fd, (byte*)bf, buff_size, MYF(0))) < 0) return ER_READ_FILE; store_to_protocol_packet(&send_buff, (char*) bf, &position, read_len); close(fd); @@ -604,7 +605,7 @@ int Show_instance_log_files::execute(struct st_net *net, ulong connection_id) store_to_protocol_packet(&send_buff, "", &position); store_to_protocol_packet(&send_buff, (char*) "0", &position); } - else if (S_ISREG(file_stat.st_mode)) + else if (MY_S_ISREG(file_stat.st_mode)) { store_to_protocol_packet(&send_buff, (char*) log_files->value, @@ -689,7 +690,7 @@ int Set_option::correct_file(int skip) { int error; - error= modify_defaults_file("/etc/my.cnf", option, + error= modify_defaults_file(Options::config_file, option, option_value, instance_name, skip); if (error > 0) return ER_OUT_OF_RESOURCES; diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 404e9f34ab5..0d6ebfa8d79 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -25,6 +25,7 @@ #include "instance.h" #include "mysql_manager_error.h" #include "log.h" +#include "port.h" #include <string.h> #include <sys/types.h> @@ -32,7 +33,6 @@ - C_MODE_START pthread_handler_decl(guardian, arg) @@ -426,11 +426,21 @@ int Guardian_thread::stop_instances(bool stop_instances_arg) int Guardian_thread::lock() { +#ifdef __WIN__ + pthread_mutex_lock(&LOCK_guardian); + return 0; +#else return pthread_mutex_lock(&LOCK_guardian); +#endif } int Guardian_thread::unlock() { +#ifdef __WIN__ + pthread_mutex_unlock(&LOCK_guardian); + return 0; +#else return pthread_mutex_unlock(&LOCK_guardian); +#endif } diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 2b25c74439e..5e3c07b9b31 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -18,14 +18,19 @@ #pragma implementation #endif +#ifdef __WIN__ +#include <process.h> +#endif #include "instance.h" #include "mysql_manager_error.h" #include "log.h" #include "instance_map.h" #include "priv.h" - +#include "port.h" +#ifndef __WIN__ #include <sys/wait.h> +#endif #include <my_sys.h> #include <signal.h> #include <m_string.h> @@ -50,6 +55,16 @@ pthread_handler_decl(proxy, arg) C_MODE_END +void Instance::remove_pid() +{ + int pid; + if ((pid= options.get_pid()) != 0) /* check the pidfile */ + if (options.unlink_pidfile()) /* remove stalled pidfile */ + log_error("cannot remove pidfile for instance %i, this might be \ + since IM lacks permmissions or hasn't found the pidifle", + options.instance_name); +} + /* The method starts an instance. @@ -65,8 +80,6 @@ C_MODE_END int Instance::start() { - pid_t pid; - /* clear crash flag */ pthread_mutex_lock(&LOCK_instance); crashed= 0; @@ -75,11 +88,7 @@ int Instance::start() if (!is_running()) { - if ((pid= options.get_pid()) != 0) /* check the pidfile */ - if (options.unlink_pidfile()) /* remove stalled pidfile */ - log_error("cannot remove pidfile for instance %i, this might be \ - since IM lacks permmissions or hasn't found the pidifle", - options.instance_name); + remove_pid(); /* No need to monitor this thread in the Thread_registry, as all @@ -107,20 +116,21 @@ int Instance::start() return ER_INSTANCE_ALREADY_STARTED; } - -void Instance::fork_and_monitor() +#ifndef __WIN__ +int Instance::launch_and_wait() { - pid_t pid; - log_info("starting instance %s", options.instance_name); - switch (pid= fork()) { - case 0: - execv(options.mysqld_path, options.argv); - /* exec never returns */ - exit(1); - case -1: - log_info("cannot fork() to start instance %s", options.instance_name); - return; - default: + pid_t pid = fork(); + + switch (pid) + { + case 0: + execv(options.mysqld_path, options.argv); + /* exec never returns */ + exit(1); + case -1: + log_info("cannot fork() to start instance %s", options.instance_name); + return -1; + default: /* Here we wait for the child created. This process differs for systems running LinuxThreads and POSIX Threads compliant systems. This is because @@ -141,22 +151,89 @@ void Instance::fork_and_monitor() wait(NULL); /* LinuxThreads were detected */ else waitpid(pid, NULL, 0); - /* set instance state to crashed */ - pthread_mutex_lock(&LOCK_instance); - crashed= 1; - pthread_mutex_unlock(&LOCK_instance); - - /* - Wake connection threads waiting for an instance to stop. This - is needed if a user issued command to stop an instance via - mysql connection. This is not the case if Guardian stop the thread. - */ - pthread_cond_signal(&COND_instance_stopped); - /* wake guardian */ - pthread_cond_signal(&instance_map->guardian->COND_guardian); - /* thread exits */ - return; } + return 0; +} +#else +int Instance::launch_and_wait() +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + + int cmdlen = 0; + for (int i=1; options.argv[i] != 0; i++) + cmdlen += strlen(options.argv[i]) + 1; + cmdlen++; // we have to add a single space for CreateProcess (read the docs) + + char *cmdline = NULL; + if (cmdlen > 0) + { + cmdline = new char[cmdlen]; + cmdline[0] = 0; + for (int i=1; options.argv[i] != 0; i++) + { + strcat(cmdline, " "); + strcat(cmdline, options.argv[i]); + } + } + + // Start the child process. + BOOL result = CreateProcess(options.mysqld_path, // file to execute + cmdline, // Command line. + NULL, // Process handle not inheritable. + NULL, // Thread handle not inheritable. + FALSE, // Set handle inheritance to FALSE. + 0, // No creation flags. + NULL, // Use parent's environment block. + NULL, // Use parent's starting directory. + &si, // Pointer to STARTUPINFO structure. + &pi ); // Pointer to PROCESS_INFORMATION structure. + delete cmdline; + if (! result) + return -1; + + // Wait until child process exits. + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exitcode; + ::GetExitCodeProcess(pi.hProcess, &exitcode); + + // Close process and thread handles. + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + + return exitcode; +} +#endif + + +void Instance::fork_and_monitor() +{ + log_info("starting instance %s", options.instance_name); + + int result = launch_and_wait(); + if (result == -1) return; + + /* set instance state to crashed */ + pthread_mutex_lock(&LOCK_instance); + crashed= 1; + pthread_mutex_unlock(&LOCK_instance); + + /* + Wake connection threads waiting for an instance to stop. This + is needed if a user issued command to stop an instance via + mysql connection. This is not the case if Guardian stop the thread. + */ + pthread_cond_signal(&COND_instance_stopped); + /* wake guardian */ + pthread_cond_signal(&instance_map->guardian->COND_guardian); + /* thread exits */ + return; + /* we should never end up here */ DBUG_ASSERT(0); } @@ -253,7 +330,6 @@ bool Instance::is_running() int Instance::stop() { - pid_t pid; struct timespec timeout; uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY; @@ -290,6 +366,68 @@ err: return ER_STOP_INSTANCE; } +#ifdef __WIN__ + +BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode) +{ + DWORD dwTID, dwCode, dwErr = 0; + HANDLE hProcessDup = INVALID_HANDLE_VALUE; + HANDLE hRT = NULL; + HINSTANCE hKernel = GetModuleHandle("Kernel32"); + BOOL bSuccess = FALSE; + + BOOL bDup = DuplicateHandle(GetCurrentProcess(), + hProcess, GetCurrentProcess(), &hProcessDup, PROCESS_ALL_ACCESS, FALSE, 0); + + // Detect the special case where the process is + // already dead... + if ( GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) && + (dwCode == STILL_ACTIVE) ) + { + FARPROC pfnExitProc; + + pfnExitProc = GetProcAddress(hKernel, "ExitProcess"); + + hRT = CreateRemoteThread((bDup) ? hProcessDup : hProcess, NULL, 0, + (LPTHREAD_START_ROUTINE)pfnExitProc, (PVOID)uExitCode, 0, &dwTID); + + if ( hRT == NULL ) + dwErr = GetLastError(); + } + else + { + dwErr = ERROR_PROCESS_ABORTED; + } + + if ( hRT ) + { + // Must wait process to terminate to + // guarantee that it has exited... + WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE); + + CloseHandle(hRT); + bSuccess = TRUE; + } + + if ( bDup ) + CloseHandle(hProcessDup); + + if ( !bSuccess ) + SetLastError(dwErr); + + return bSuccess; +} + +int kill(pid_t pid, int signum) +{ + HANDLE processhandle = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (signum == SIGTERM) + ::SafeTerminateProcess(processhandle, 0); + else + ::TerminateProcess(processhandle, -1); + return 0; +} +#endif void Instance::kill_instance(int signum) { diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 39a2b8ee846..0ff5ecc179e 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -61,6 +61,9 @@ private: */ pthread_cond_t COND_instance_stopped; Instance_map *instance_map; + + void remove_pid(); + int launch_and_wait(); }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */ diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index fa8a5d58114..2c7166c59f5 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -22,6 +22,7 @@ #include "buffer.h" #include "instance.h" +#include "options.h" #include <m_ctype.h> #include <mysql_com.h> @@ -138,13 +139,23 @@ Instance_map::~Instance_map() int Instance_map::lock() { +#ifdef __WIN__ + pthread_mutex_lock(&LOCK_instance_map); + return 0; +#else return pthread_mutex_lock(&LOCK_instance_map); +#endif } int Instance_map::unlock() { +#ifdef __WIN__ + pthread_mutex_unlock(&LOCK_instance_map); + return 0; +#else return pthread_mutex_unlock(&LOCK_instance_map); +#endif } @@ -245,7 +256,7 @@ int Instance_map::load() else argv_options[1]= '\0'; - if (my_search_option_files("my", &argc, (char ***) &argv, &args_used, + if (my_search_option_files(Options::config_file, &argc, (char ***) &argv, &args_used, process_option, (void*) this) || complete_initialization()) return 1; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 9dac8b39bdc..a5a38ff1221 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -19,6 +19,7 @@ #include <my_global.h> #include <my_sys.h> #include "parse.h" +#include "port.h" #ifdef __GNUC__ #pragma interface diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 4aed18c753d..a161fc9730b 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -19,11 +19,13 @@ #endif #include "listener.h" - +#include "priv.h" #include <m_string.h> #include <mysql.h> #include <violite.h> +#ifndef __WIN__ #include <sys/un.h> +#endif #include <sys/stat.h> #include "thread_registry.h" @@ -31,7 +33,7 @@ #include "instance_map.h" #include "log.h" #include "mysql_connection.h" -#include "priv.h" +#include "port.h" /* @@ -47,10 +49,17 @@ public: private: ulong total_connection_count; Thread_info thread_info; + + int sockets[2]; + int num_sockets; + fd_set read_fds; private: void handle_new_mysql_connection(Vio *vio); + int create_tcp_socket(); + int create_unix_socket(struct sockaddr_un &unix_socket_address); }; +const int LISTEN_BACK_LOG_SIZE = 5; // standard backlog size Listener_thread::Listener_thread(const Listener_thread_args &args) : Listener_thread_args(args.thread_registry, args.options, args.user_map, @@ -58,6 +67,7 @@ Listener_thread::Listener_thread(const Listener_thread_args &args) : ,total_connection_count(0) ,thread_info(pthread_self()) { + num_sockets= 0; } @@ -78,30 +88,134 @@ Listener_thread::~Listener_thread() void Listener_thread::run() { - enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size - int flags; - int arg= 1; /* value to be set by setsockopt */ - int unix_socket; - uint im_port; /* we use this var to check whether we are running on LinuxThreads */ pid_t thread_pid; + int n; thread_pid= getpid(); + +#ifndef __WIN__ + struct sockaddr_un unix_socket_address; /* set global variable */ linuxthreads= (thread_pid != manager_pid); +#endif thread_registry.register_thread(&thread_info); my_thread_init(); + FD_ZERO(&read_fds); + /* I. prepare 'listen' sockets */ + if (create_tcp_socket()) + goto err; + +#ifndef __WIN__ + if (create_unix_socket(unix_socket_address)) + goto err; +#endif + + /* II. Listen sockets and spawn childs */ + for (int i=0; i < num_sockets; i++) + n = max(n, sockets[i]); + n++; + + while (thread_registry.is_shutdown() == false) + { + fd_set read_fds_arg= read_fds; + + /* + When using valgrind 2.0 this syscall doesn't get kicked off by a + signal during shutdown. This results in failing assert + (Thread_registry::~Thread_registry). Valgrind 2.2 works fine. + */ + int rc= select(n, &read_fds_arg, 0, 0, 0); + + + if (rc == -1 && errno != EINTR) + { + log_error("Listener_thread::run(): select() failed, %s", + strerror(errno)); + continue; + } + + + for (int socket_index=0; socket_index < num_sockets; socket_index++) + { + /* Assuming that rc > 0 as we asked to wait forever */ + if (FD_ISSET(sockets[socket_index], &read_fds_arg)) + { + int client_fd= accept(sockets[socket_index], 0, 0); + /* accept may return -1 (failure or spurious wakeup) */ + if (client_fd >= 0) // connection established + { + Vio *vio = vio_new(client_fd, socket_index==0?VIO_TYPE_SOCKET:VIO_TYPE_TCPIP, + socket_index==0?1:0); + if (vio != 0) + handle_new_mysql_connection(vio); + else + { + shutdown(client_fd, SHUT_RDWR); + close(client_fd); + } + } + } + } + } + + /* III. Release all resources and exit */ + + log_info("Listener_thread::run(): shutdown requested, exiting..."); + + for (int i=0; i < num_sockets; i++) + close(sockets[i]); + +#ifndef __WIN__ + unlink(unix_socket_address.sun_path); +#endif + + thread_registry.unregister_thread(&thread_info); + my_thread_end(); + return; + +err: + thread_registry.unregister_thread(&thread_info); + thread_registry.request_shutdown(); + my_thread_end(); + return; +} + +void set_non_blocking(int socket) +{ +#ifndef __WIN__ + int flags= fcntl(socket, F_GETFL, 0); + fcntl(socket, F_SETFL, flags | O_NONBLOCK); +#else + u_long arg = 1; + ioctlsocket(socket, FIONBIO, &arg); +#endif +} + +void set_no_inherit(int socket) +{ +#ifndef __WIN__ + int flags= fcntl(socket, F_GETFD, 0); + fcntl(socket, F_SETFD, flags | FD_CLOEXEC); +#else +#endif +} + +int Listener_thread::create_tcp_socket() +{ + /* value to be set by setsockopt */ + int arg= 1; int ip_socket= socket(AF_INET, SOCK_STREAM, 0); if (ip_socket == INVALID_SOCKET) { log_error("Listener_thead::run(): socket(AF_INET) failed, %s", strerror(errno)); - goto err; + return -1; } struct sockaddr_in ip_socket_address; @@ -115,7 +229,7 @@ void Listener_thread::run() } else im_bind_addr= htonl(INADDR_ANY); - im_port= options.port_number; + uint im_port= options.port_number; ip_socket_address.sin_family= AF_INET; ip_socket_address.sin_addr.s_addr = im_bind_addr; @@ -130,154 +244,86 @@ void Listener_thread::run() { log_error("Listener_thread::run(): bind(ip socket) failed, '%s'", strerror(errno)); - goto err; + close(ip_socket); + return -1; } if (listen(ip_socket, LISTEN_BACK_LOG_SIZE)) { log_error("Listener_thread::run(): listen(ip socket) failed, %s", strerror(errno)); - goto err; + close(ip_socket); + return -1; } - /* set the socket nonblocking */ - flags= fcntl(ip_socket, F_GETFL, 0); - fcntl(ip_socket, F_SETFL, flags | O_NONBLOCK); - /* make sure that instances won't be listening our sockets */ - flags= fcntl(ip_socket, F_GETFD, 0); - fcntl(ip_socket, F_SETFD, flags | FD_CLOEXEC); + /* set the socket nonblocking */ + set_non_blocking(ip_socket); + + /* make sure that instances won't be listening our sockets */ + set_no_inherit(ip_socket); + + FD_SET(ip_socket, &read_fds); + sockets[num_sockets++] = ip_socket; log_info("accepting connections on ip socket"); + return 0; +} - /*--------------------------------------------------------------*/ - unix_socket= socket(AF_UNIX, SOCK_STREAM, 0); +#ifndef __WIN__ +int Listener_thread::create_unix_socket( + struct sockaddr_un &unix_socket_address) +{ + int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0); if (unix_socket == INVALID_SOCKET) { log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s", strerror(errno)); - goto err; + return -1; } - struct sockaddr_un unix_socket_address; bzero(&unix_socket_address, sizeof(unix_socket_address)); unix_socket_address.sun_family= AF_UNIX; strmake(unix_socket_address.sun_path, options.socket_file_name, sizeof(unix_socket_address.sun_path)); - unlink(unix_socket_address.sun_path); /* in case we have stale socket file */ + unlink(unix_socket_address.sun_path); // in case we have stale socket file + /* + POSIX specifies default permissions for a pathname created by bind + to be 0777. We need everybody to have access to the socket. + */ + mode_t old_mask= umask(0); + if (bind(unix_socket, (struct sockaddr *) &unix_socket_address, + sizeof(unix_socket_address))) { - /* - POSIX specifies default permissions for a pathname created by bind - to be 0777. We need everybody to have access to the socket. - */ - mode_t old_mask= umask(0); - if (bind(unix_socket, (struct sockaddr *) &unix_socket_address, - sizeof(unix_socket_address))) - { - log_error("Listener_thread::run(): bind(unix socket) failed, " + log_error("Listener_thread::run(): bind(unix socket) failed, " "socket file name is '%s', error '%s'", unix_socket_address.sun_path, strerror(errno)); - goto err; - } - umask(old_mask); - - if (listen(unix_socket, LISTEN_BACK_LOG_SIZE)) - { - log_error("Listener_thread::run(): listen(unix socket) failed, %s", - strerror(errno)); - goto err; - } - - /* set the socket nonblocking */ - flags= fcntl(unix_socket, F_GETFL, 0); - fcntl(unix_socket, F_SETFL, flags | O_NONBLOCK); - /* make sure that instances won't be listening our sockets */ - flags= fcntl(unix_socket, F_GETFD, 0); - fcntl(unix_socket, F_SETFD, flags | FD_CLOEXEC); + close(unix_socket); + return -1; } - log_info("accepting connections on unix socket %s", - unix_socket_address.sun_path); - - /* II. Listen sockets and spawn childs */ - + + umask(old_mask); + + if (listen(unix_socket, LISTEN_BACK_LOG_SIZE)) { - int n= max(unix_socket, ip_socket) + 1; - fd_set read_fds; - - FD_ZERO(&read_fds); - FD_SET(unix_socket, &read_fds); - FD_SET(ip_socket, &read_fds); - - while (thread_registry.is_shutdown() == false) - { - fd_set read_fds_arg= read_fds; - - /* - When using valgrind 2.0 this syscall doesn't get kicked off by a - signal during shutdown. This results in failing assert - (Thread_registry::~Thread_registry). Valgrind 2.2 works fine. - */ - int rc= select(n, &read_fds_arg, 0, 0, 0); - - - if (rc == -1 && errno != EINTR) - log_error("Listener_thread::run(): select() failed, %s", - strerror(errno)); - else - { - /* Assuming that rc > 0 as we asked to wait forever */ - if (FD_ISSET(unix_socket, &read_fds_arg)) - { - int client_fd= accept(unix_socket, 0, 0); - /* accept may return -1 (failure or spurious wakeup) */ - if (client_fd >= 0) // connection established - { - if (Vio *vio= vio_new(client_fd, VIO_TYPE_SOCKET, 1)) - handle_new_mysql_connection(vio); - else - { - shutdown(client_fd, SHUT_RDWR); - close(client_fd); - } - } - } - else if (FD_ISSET(ip_socket, &read_fds_arg)) - { - int client_fd= accept(ip_socket, 0, 0); - /* accept may return -1 (failure or spurious wakeup) */ - if (client_fd >= 0) // connection established - { - if (Vio *vio= vio_new(client_fd, VIO_TYPE_TCPIP, 0)) - handle_new_mysql_connection(vio); - else - { - shutdown(client_fd, SHUT_RDWR); - close(client_fd); - } - } - } - } - } + log_error("Listener_thread::run(): listen(unix socket) failed, %s", + strerror(errno)); + close(unix_socket); + return -1; } - /* III. Release all resources and exit */ - - log_info("Listener_thread::run(): shutdown requested, exiting..."); - - close(unix_socket); - close(ip_socket); - unlink(unix_socket_address.sun_path); + /* set the socket nonblocking */ + set_non_blocking(unix_socket); - thread_registry.unregister_thread(&thread_info); - my_thread_end(); - return; + /* make sure that instances won't be listening our sockets */ + set_no_inherit(unix_socket); -err: - thread_registry.unregister_thread(&thread_info); - thread_registry.request_shutdown(); - my_thread_end(); - return; + log_info("accepting connections on unix socket %s", unix_socket_address.sun_path); + sockets[num_sockets++] = unix_socket; + FD_SET(unix_socket, &read_fds); + return 0; } +#endif /* diff --git a/server-tools/instance-manager/listener.h b/server-tools/instance-manager/listener.h index 7a8af49e2b3..67a090c3aa2 100644 --- a/server-tools/instance-manager/listener.h +++ b/server-tools/instance-manager/listener.h @@ -31,7 +31,7 @@ pthread_handler_decl(listener, arg); C_MODE_END class Thread_registry; -class Options; +struct Options; class User_map; class Instance_map; diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc index 3c18d2816bf..2a363312fed 100644 --- a/server-tools/instance-manager/log.cc +++ b/server-tools/instance-manager/log.cc @@ -17,7 +17,7 @@ #include <my_global.h> #include "log.h" - +#include "port.h" #include <stdarg.h> #include <m_string.h> #include <my_sys.h> diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index a4c81739b17..5f2d9d0b493 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -30,7 +30,9 @@ #include <m_string.h> #include <signal.h> #include <thr_alarm.h> +#ifndef __WIN__ #include <sys/wait.h> +#endif static int create_pid_file(const char *pid_file_name) @@ -50,6 +52,61 @@ static int create_pid_file(const char *pid_file_name) return 0; } +#ifndef __WIN__ +void set_signals(sigset_t *mask) +{ + /* block signals */ + sigemptyset(mask); + sigaddset(mask, SIGINT); + sigaddset(mask, SIGTERM); + sigaddset(mask, SIGPIPE); + sigaddset(mask, SIGHUP); + signal(SIGPIPE, SIG_IGN); + + /* + We want this signal to be blocked in all theads but the signal + one. It is needed for the thr_alarm subsystem to work. + */ + sigaddset(mask,THR_SERVER_ALARM); + + /* all new threads will inherite this signal mask */ + pthread_sigmask(SIG_BLOCK, mask, NULL); + + /* + In our case the signal thread also implements functions of alarm thread. + Here we init alarm thread functionality. We suppose that we won't have + more then 10 alarms at the same time. + */ + init_thr_alarm(10); +} +#else + +bool have_signal; + +void onsignal(int signo) +{ + have_signal = true; +} + +void set_signals(sigset_t *set) +{ + signal(SIGINT, onsignal); + signal(SIGTERM, onsignal); + have_signal = false; +} + +int my_sigwait(const sigset_t *set, int *sig) +{ +// MSG msg; + while (!have_signal) + { + Sleep(100); + } + return 0; +} + +#endif + /* manager - entry point to the main instance manager process: start @@ -98,21 +155,8 @@ void manager(const Options &options) if (create_pid_file(options.pid_file_name)) return; - /* block signals */ sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGPIPE); - sigaddset(&mask, SIGHUP); - /* - We want this signal to be blocked in all theads but the signal - one. It is needed for the thr_alarm subsystem to work. - */ - sigaddset(&mask,THR_SERVER_ALARM); - - /* all new threads will inherite this signal mask */ - pthread_sigmask(SIG_BLOCK, &mask, NULL); + set_signals(&mask); /* create the listener */ { @@ -166,12 +210,7 @@ void manager(const Options &options) bool shutdown_complete; shutdown_complete= FALSE; - /* - In our case the signal thread also implements functions of alarm thread. - Here we init alarm thread functionality. We suppose that we won't have - more then 10 alarms at the same time. - */ - init_thr_alarm(10); + /* init list of guarded instances */ guardian_thread.lock(); @@ -185,8 +224,6 @@ void manager(const Options &options) */ pthread_cond_signal(&guardian_thread.COND_guardian); - signal(SIGPIPE, SIG_IGN); - while (!shutdown_complete) { int status= 0; @@ -197,11 +234,11 @@ void manager(const Options &options) goto err; } - switch (signo) { - case THR_SERVER_ALARM: - process_alarm(signo); - break; - default: +#ifndef __WIN__ + if (THR_SERVER_ALARM == signo) + process_alarm(signo); + else +#endif { if (!guardian_thread.is_stopped()) { @@ -215,16 +252,16 @@ void manager(const Options &options) shutdown_complete= TRUE; } } - break; } - } err: /* delete the pid file */ my_delete(options.pid_file_name, MYF(0)); +#ifndef __WIN__ /* free alarm structures */ end_thr_alarm(1); /* don't pthread_exit to kill all threads who did not shut down in time */ +#endif } diff --git a/server-tools/instance-manager/manager.h b/server-tools/instance-manager/manager.h index d73f4b35f18..12ed6b3b1ff 100644 --- a/server-tools/instance-manager/manager.h +++ b/server-tools/instance-manager/manager.h @@ -16,7 +16,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -class Options; +struct Options; void manager(const Options &options); diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index 5a6c398614b..1533580de31 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -23,12 +23,16 @@ #include <my_sys.h> #include <string.h> #include <signal.h> +#ifndef __WIN__ #include <pwd.h> #include <grp.h> #include <sys/wait.h> +#endif #include <sys/types.h> #include <sys/stat.h> - +#ifdef __WIN__ +#include "windowsservice.h" +#endif /* Few notes about Instance Manager architecture: @@ -55,10 +59,14 @@ */ static void init_environment(char *progname); +#ifndef __WIN__ static void daemonize(const char *log_file_name); static void angel(const Options &options); static struct passwd *check_user(const char *user); static int set_user(const char *user, struct passwd *user_info); +#else +int HandleServiceOptions(Options options); +#endif /* @@ -78,6 +86,7 @@ int main(int argc, char *argv[]) if (options.load(argc, argv)) goto err; +#ifndef __WIN__ if ((user_info= check_user(options.user))) { if (set_user(options.user, user_info)) @@ -94,6 +103,12 @@ int main(int argc, char *argv[]) /* forks again, and returns only in child: parent becomes angel */ angel(options); } +#else +#ifdef NDEBUG + return HandleServiceOptions(options); +#endif +#endif + manager(options); options.cleanup(); my_end(0); @@ -105,11 +120,11 @@ err: /******************* Auxilary functions implementation **********************/ +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) /* Change to run as another user if started with --user */ static struct passwd *check_user(const char *user) { -#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) struct passwd *user_info; uid_t user_id= geteuid(); @@ -150,7 +165,6 @@ static struct passwd *check_user(const char *user) err: log_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n", user); -#endif return NULL; } @@ -172,7 +186,7 @@ static int set_user(const char *user, struct passwd *user_info) } return 0; } - +#endif /* @@ -188,6 +202,7 @@ static void init_environment(char *progname) } +#ifndef __WIN__ /* Become a UNIX service SYNOPSYS @@ -342,3 +357,4 @@ spawn: } } +#endif diff --git a/server-tools/instance-manager/mysqlmanager.vcproj b/server-tools/instance-manager/mysqlmanager.vcproj new file mode 100755 index 00000000000..5263deae6a9 --- /dev/null +++ b/server-tools/instance-manager/mysqlmanager.vcproj @@ -0,0 +1,295 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="mysqlmanager" + ProjectGUID="{6D524B3E-210A-4FCD-8D41-FEC0D21E83AC}" + Keyword="Win32Proj"> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="Debug" + IntermediateDirectory="Debug" + ConfigurationType="1" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\include" + PreprocessorDefinitions="MYSQL_INSTANCE_MANAGER;MYSQL_SERVER;_DEBUG;SAFEMALLOC;SAFE_MUTEX;_WINDOWS;CONSOLE" + MinimalRebuild="TRUE" + ExceptionHandling="FALSE" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="TRUE" + DebugInformationFormat="4"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wsock32.lib" + OutputFile="$(OutDir)/mysqlmanager.exe" + LinkIncremental="2" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/mysqlmanager.pdb" + SubSystem="1" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="Release" + IntermediateDirectory="Release" + ConfigurationType="1" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="..\include" + PreprocessorDefinitions="MYSQL_INSTANCE_MANAGER;MYSQL_SERVER;_WINDOWS;CONSOLE" + ExceptionHandling="FALSE" + RuntimeLibrary="0" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="TRUE" + DebugInformationFormat="3"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wsock32.lib" + OutputFile="$(OutDir)/mysqlmanager.exe" + LinkIncremental="1" + GenerateDebugInformation="TRUE" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> + <File + RelativePath=".\buffer.cc"> + </File> + <File + RelativePath="..\sql\client.c"> + </File> + <File + RelativePath=".\command.cc"> + </File> + <File + RelativePath=".\commands.cc"> + </File> + <File + RelativePath=".\factory.cc"> + </File> + <File + RelativePath="..\libmysql\get_password.c"> + </File> + <File + RelativePath=".\guardian.cc"> + </File> + <File + RelativePath=".\IMService.cpp"> + </File> + <File + RelativePath=".\instance.cc"> + </File> + <File + RelativePath=".\instance_map.cc"> + </File> + <File + RelativePath=".\instance_options.cc"> + </File> + <File + RelativePath=".\listener.cc"> + </File> + <File + RelativePath=".\log.cc"> + </File> + <File + RelativePath=".\manager.cc"> + </File> + <File + RelativePath=".\messages.cc"> + </File> + <File + RelativePath="..\sql\mini_client_errors.c"> + </File> + <File + RelativePath=".\mysql_connection.cc"> + </File> + <File + RelativePath=".\mysqlmanager.cc"> + </File> + <File + RelativePath="..\sql\net_serv.cpp"> + </File> + <File + RelativePath=".\options.cc"> + </File> + <File + RelativePath="..\sql\pack.c"> + </File> + <File + RelativePath=".\parse.cc"> + </File> + <File + RelativePath=".\parse_output.cc"> + </File> + <File + RelativePath="..\sql\password.c"> + </File> + <File + RelativePath=".\priv.cc"> + </File> + <File + RelativePath=".\protocol.cc"> + </File> + <File + RelativePath=".\service_funcs.cpp"> + </File> + <File + RelativePath="..\sql\sql_state.c"> + </File> + <File + RelativePath=".\thread_registry.cc"> + </File> + <File + RelativePath=".\user_map.cc"> + </File> + <File + RelativePath=".\WindowsService.cpp"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> + <File + RelativePath=".\buffer.h"> + </File> + <File + RelativePath=".\command.h"> + </File> + <File + RelativePath=".\commands.h"> + </File> + <File + RelativePath=".\factory.h"> + </File> + <File + RelativePath=".\guardian.h"> + </File> + <File + RelativePath=".\IMService.h"> + </File> + <File + RelativePath=".\instance.h"> + </File> + <File + RelativePath=".\instance_map.h"> + </File> + <File + RelativePath=".\instance_options.h"> + </File> + <File + RelativePath=".\listener.h"> + </File> + <File + RelativePath=".\log.h"> + </File> + <File + RelativePath=".\manager.h"> + </File> + <File + RelativePath=".\messages.h"> + </File> + <File + RelativePath=".\mysql_connection.h"> + </File> + <File + RelativePath=".\mysql_manager_error.h"> + </File> + <File + RelativePath=".\options.h"> + </File> + <File + RelativePath=".\parse.h"> + </File> + <File + RelativePath=".\parse_output.h"> + </File> + <File + RelativePath=".\port.h"> + </File> + <File + RelativePath=".\priv.h"> + </File> + <File + RelativePath=".\protocol.h"> + </File> + <File + RelativePath=".\thread_registry.h"> + </File> + <File + RelativePath=".\user_map.h"> + </File> + <File + RelativePath=".\WindowsService.h"> + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index e44e4c6ff34..f563fa783d2 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -21,7 +21,7 @@ #include "options.h" #include "priv.h" - +#include "port.h" #include <my_sys.h> #include <my_getopt.h> #include <m_string.h> @@ -30,19 +30,29 @@ #define QUOTE2(x) #x #define QUOTE(x) QUOTE2(x) +const char *default_password_file_name = QUOTE(DEFAULT_PASSWORD_FILE_NAME); +const char *default_log_file_name = QUOTE(DEFAULT_LOG_FILE_NAME); +char default_config_file[FN_REFLEN] = "/etc/my.cnf"; + +#ifndef __WIN__ char Options::run_as_service; -const char *Options::log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME); +const char *Options::user= 0; /* No default value */ +#else +char Options::install_as_service; +char Options::remove_service; +#endif +const char *Options::log_file_name= default_log_file_name; const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME); -const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); +const char *Options::password_file_name= default_password_file_name; const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); const char *Options::first_option= 0; /* No default value */ const char *Options::bind_address= 0; /* No default value */ -const char *Options::user= 0; /* No default value */ uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; uint Options::port_number= DEFAULT_PORT; /* just to declare */ char **Options::saved_argv; +const char *Options::config_file = NULL; /* List of options, accepted by the instance manager. @@ -55,8 +65,13 @@ enum options { OPT_SOCKET, OPT_PASSWORD_FILE, OPT_MYSQLD_PATH, +#ifndef __WIN__ OPT_RUN_AS_SERVICE, OPT_USER, +#else + OPT_INSTALL_SERVICE, + OPT_REMOVE_SERVICE, +#endif OPT_MONITORING_INTERVAL, OPT_PORT, OPT_BIND_ADDRESS @@ -107,7 +122,14 @@ static struct my_option my_long_options[] = (gptr *) &Options::monitoring_interval, 0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL, 0, 0, 0, 0, 0 }, - +#ifdef __WIN__ + { "install", OPT_INSTALL_SERVICE, "Install as system service.", + (gptr *) &Options::install_as_service, (gptr*) &Options::install_as_service, + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, + { "remove", OPT_REMOVE_SERVICE, "Remove system service.", + (gptr *)&Options::remove_service, (gptr*) &Options::remove_service, + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0}, +#else { "run-as-service", OPT_RUN_AS_SERVICE, "Daemonize and start angel process.", (gptr *) &Options::run_as_service, 0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, @@ -116,7 +138,7 @@ static struct my_option my_long_options[] = (gptr *) &Options::user, (gptr *) &Options::user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - +#endif { "version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, @@ -214,20 +236,44 @@ C_MODE_END int Options::load(int argc, char **argv) { int rc; + char** argv_ptr = argv; + +#ifdef __WIN__ + setup_windows_defaults(*argv); +#endif + config_file=NULL; if (argc >= 2) { + if (is_prefix(argv[1], "--defaults-file=")) + config_file=argv[1]; if (is_prefix(argv[1],"--defaults-file=") || is_prefix(argv[1],"--defaults-extra-file=")) Options::first_option= argv[1]; } + // we were not given a config file on the command line so we + // set have to construct a new argv array + if (config_file == NULL) + { +#ifdef __WIN__ + ::GetModuleFileName(NULL, default_config_file, sizeof(default_config_file)); + char *filename = strstr(default_config_file, "mysqlmanager.exe"); + strcpy(filename, "my.ini"); +#endif + config_file = default_config_file; + } + /* config-file options are prepended to command-line ones */ - load_defaults("my", default_groups, &argc, &argv); - Options::saved_argv= argv; + load_defaults(config_file, default_groups, &argc, &argv); + + rc= handle_options(&argc, &argv, my_long_options, get_one_option); - if ((rc= handle_options(&argc, &argv, my_long_options, get_one_option)) != 0) + if (rc != 0) return rc; + + Options::saved_argv= argv; + return 0; } @@ -235,4 +281,31 @@ void Options::cleanup() { /* free_defaults returns nothing */ free_defaults(Options::saved_argv); + +#ifdef __WIN__ + free((char*)default_password_file_name); +#endif +} + +#ifdef __WIN__ + +char* change_extension(const char *src, const char *newext) +{ + char *dot = (char*)strrchr(src, '.'); + if (!dot) return (char*)src; + + int newlen = dot-src+strlen(newext)+1; + char *temp = (char*)malloc(newlen); + bzero(temp, newlen); + strncpy(temp, src, dot-src+1); + strcat(temp, newext); + return temp; } + +void Options::setup_windows_defaults(const char *progname) +{ + Options::password_file_name = default_password_file_name = change_extension(progname, "passwd"); + Options::log_file_name = default_log_file_name = change_extension(progname, "log"); +} + +#endif diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index 3df259864be..3ef054b6339 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -28,23 +28,32 @@ struct Options { +#ifdef __WIN__ + static char install_as_service; + static char remove_service; +#else static char run_as_service; /* handle_options doesn't support bool */ + static const char *user; +#endif static const char *log_file_name; static const char *pid_file_name; static const char *socket_file_name; static const char *password_file_name; static const char *default_mysqld_path; - static const char *user; /* the option which should be passed to process_default_option_files */ static const char *first_option; static uint monitoring_interval; static uint port_number; static const char *bind_address; + static const char *config_file; static char **saved_argv; - static int load(int argc, char **argv); + int load(int argc, char **argv); void cleanup(); +#ifdef __WIN__ + void setup_windows_defaults(const char *progname); +#endif }; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index e83a75c6778..4b3a8b75ca1 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -21,6 +21,7 @@ #include <stdio.h> #include <my_sys.h> #include <m_string.h> +#include "port.h" /* diff --git a/server-tools/instance-manager/port.h b/server-tools/instance-manager/port.h new file mode 100755 index 00000000000..3b1c3e2ad93 --- /dev/null +++ b/server-tools/instance-manager/port.h @@ -0,0 +1,25 @@ +#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_PORT_H +#define INCLUDES_MYSQL_INSTANCE_MANAGER_PORT_H + +#ifdef __WIN__ + +#define vsnprintf _vsnprintf + +#define SIGKILL 9 +#define SHUT_RDWR 0x2 + +//TODO: fix this +#define DEFAULT_MONITORING_INTERVAL 20 +#define DEFAULT_PORT 2273 +#define PROTOCOL_VERSION 10 + +typedef int pid_t; + +#undef popen +#define popen(A,B) _popen(A,B) + +#endif /* __WIN__ */ + +#endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PORT_H */ + + diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc index dd192370aaf..496992aa43b 100644 --- a/server-tools/instance-manager/priv.cc +++ b/server-tools/instance-manager/priv.cc @@ -14,7 +14,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <my_global.h> #include "priv.h" +#include "port.h" /* the pid of the manager process (of the signal thread on the LinuxThreads) */ pid_t manager_pid; diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index decc3605dff..0f4c9be9a11 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -17,17 +17,23 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <sys/types.h> +#ifdef __WIN__ +#include "port.h" +#else #include <unistd.h> +#endif /* the pid of the manager process (of the signal thread on the LinuxThreads) */ extern pid_t manager_pid; +#ifndef __WIN__ /* This flag is set if mysqlmanager has detected that it is running on the system using LinuxThreads */ extern bool linuxthreads; +#endif extern const char mysqlmanager_version[]; extern const int mysqlmanager_version_length; diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc index 16821df4146..0d47664a89a 100644 --- a/server-tools/instance-manager/thread_registry.cc +++ b/server-tools/instance-manager/thread_registry.cc @@ -27,6 +27,7 @@ #include <thr_alarm.h> +#ifndef __WIN__ /* Kick-off signal handler */ enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 }; @@ -34,7 +35,7 @@ enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 }; static void handle_signal(int __attribute__((unused)) sig_no) { } - +#endif /* TODO: think about moving signal information (now it's shutdown_in_progress) @@ -76,12 +77,13 @@ Thread_registry::~Thread_registry() void Thread_registry::register_thread(Thread_info *info) { +#ifndef __WIN__ struct sigaction sa; sa.sa_handler= handle_signal; sa.sa_flags= 0; sigemptyset(&sa.sa_mask); sigaction(THREAD_KICK_OFF_SIGNAL, &sa, 0); - +#endif info->current_cond= 0; pthread_mutex_lock(&LOCK_thread_registry); @@ -156,6 +158,7 @@ void Thread_registry::deliver_shutdown() pthread_mutex_lock(&LOCK_thread_registry); shutdown_in_progress= true; +#ifndef __WIN__ /* to stop reading from the network we need to flush alarm queue */ end_thr_alarm(0); /* @@ -163,6 +166,8 @@ void Thread_registry::deliver_shutdown() stopped alarm processing. */ process_alarm(THR_SERVER_ALARM); +#endif + for (info= head.next; info != &head; info= info->next) { pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index 7cb2cd67d7f..a80cb7767d1 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -36,7 +36,8 @@ struct User int User::init(const char *line) { - const char *name_begin, *name_end, *password; + const char *name_begin, *name_end; + char *password; if (line[0] == '\'' || line[0] == '"') { @@ -44,7 +45,7 @@ int User::init(const char *line) name_end= strchr(name_begin, line[0]); if (name_end == 0 || name_end[1] != ':') goto err; - password= name_end + 2; + password= (char*)(name_end + 2); } else { @@ -52,13 +53,18 @@ int User::init(const char *line) name_end= strchr(name_begin, ':'); if (name_end == 0) goto err; - password= name_end + 1; + password= (char*)(name_end + 1); } user_length= name_end - name_begin; if (user_length > USERNAME_LENGTH) goto err; /* assume that newline characater is present */ + if (password[strlen(password)-2] == '\r') + { + password[strlen(password)-2] = '\n'; + password[strlen(password)-1] = 0; + } if (strlen(password) != SCRAMBLED_PASSWORD_CHAR_LENGTH + 1) goto err; |