1 /* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "restart_monitor_win.h"
24
25 #include <windows.h>
26 #include <memory>
27 #include <vector>
28
29 #include <shellapi.h> // windows.h needs to be included before this header
30
31 #include "my_dbug.h"
32 #include "my_sys.h"
33 #include "mysqld.h"
34 #include "sql/log.h" // sql_print_*
35 #include "sql/message.h"
36 #include "sql/sql_const.h" // MYSQLD_*
37
38 // PID of the monitor process.
39 static DWORD my_monitor_pid;
40
41 // True if the process is mysqld monitor else false.
42 static bool mysqld_monitor_process;
43
44 /*
45 True if the server option is early option.
46 Under early option the server exits.
47 */
48 static bool mysqld_monitor_early_option;
49
50 static HANDLE shutdown_event;
51 static HANDLE service_status_cmd_event;
52 static HANDLE service_status_pipe;
53 static HANDLE client_service_status_pipe = nullptr;
54 static HANDLE service_status_cmd_processed_handle = nullptr;
55 static HANDLE event_log_handle = nullptr;
56
57 static const int NUM_WAIT_HANDLES = 2;
58 static const int MYSQLD_HANDLE = 0;
59 static const int SERVICE_STATUS_CMD_HANDLE = 1;
60 static const int EVENT_NAME_LEN = 32;
61
62 static char service_status_cmd_processed_event_name[EVENT_NAME_LEN];
63 static char shutdown_event_name[EVENT_NAME_LEN];
64 static char service_status_pipe_name[EVENT_NAME_LEN];
65 static char service_status_cmd_event_name[EVENT_NAME_LEN];
66
67 /**
68 Spawn mysqld process. Wait on spawned process handle for mysqld exit. On
69 exit, retrieve exit code to check for restart and shutdown or abort exit.
70 If we exit using MYSQLD_RESTART_EXIT code then we respawn this child process.
71 In addition, if the mysqld process is instantiated as a windows service,
72 then we create a named pipe. The pipe allows for communication
73 from mysqld to send service status code. This service status code can then
74 be relayed to the Service Control Manager using the service status handle.
75 This is used to relay the service running status and set slow status timeout
76 value.
77 */
78
79 static int monitor_mysqld(LPSTR cmdline);
80
81 /**
82 Initialize event handles in monitor process.
83 */
84
85 static bool initialize_events(void);
86
87 /**
88 This method closes the event handles.
89 */
90
91 static void deinitialize_events(void);
92
93 /**
94 Initialize the monitor log. In case mysqld is spawned from
95 windows service, we use event log for logging error messages.
96 (stderr/stdout is not available when started as as a service.)
97 */
98
initialize_monitor_log()99 bool initialize_monitor_log() {
100 HKEY handle_reg_key = nullptr;
101
102 DWORD res = RegCreateKey(HKEY_LOCAL_MACHINE,
103 "SYSTEM\\CurrentControlSet\\services\\eventlog\\"
104 "Application\\MySQLD Service",
105 &handle_reg_key);
106 if (res != ERROR_SUCCESS) return true;
107
108 TCHAR reg_value[MAX_PATH];
109 GetModuleFileName(nullptr, reg_value, MAX_PATH);
110 res = RegSetValueEx(handle_reg_key, "EventMessageFile", 0, REG_EXPAND_SZ,
111 (PBYTE)reg_value, (DWORD)(strlen(reg_value) + 1));
112 if (res != ERROR_SUCCESS) {
113 RegCloseKey(handle_reg_key);
114 return true;
115 }
116
117 DWORD dw_types =
118 (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE);
119 res = RegSetValueEx(handle_reg_key, "TypesSupported", 0, REG_DWORD,
120 (LPBYTE)&dw_types, sizeof(dw_types));
121
122 if (res != ERROR_SUCCESS) {
123 RegCloseKey(handle_reg_key);
124 return true;
125 }
126
127 RegCloseKey(handle_reg_key);
128 // Set up registry entries for event logging by monitor.
129 event_log_handle = RegisterEventSource(nullptr, "MySQLD Monitor");
130 if (event_log_handle == nullptr) return true;
131
132 return false;
133 }
134
135 /**
136 Deinitialize the monitor log.
137 This method deregisters the event log.
138 */
139
deinitialize_monitor_log()140 void deinitialize_monitor_log() {
141 if (event_log_handle) DeregisterEventSource(event_log_handle);
142 }
143
144 /**
145 Map monitor msg type to corresponding event log type.
146
147 @param type Type of the message.
148
149 @return a DWORD indicating event log type.
150 */
151
get_event_type(Monitor_log_msg_type type)152 static WORD get_event_type(Monitor_log_msg_type type) {
153 switch (type) {
154 case Monitor_log_msg_type::MONITOR_LOG_ERROR:
155 return EVENTLOG_ERROR_TYPE;
156 case Monitor_log_msg_type::MONITOR_LOG_WARN:
157 return EVENTLOG_WARNING_TYPE;
158 default:
159 return EVENTLOG_INFORMATION_TYPE;
160 }
161 }
162
163 /**
164 Log a message to the event log.
165
166 @param type Event log type.
167
168 @param msg Pointer to the message string.
169 */
170
log_in_eventlog(WORD type,LPSTR msg)171 inline static void log_in_eventlog(WORD type, LPSTR msg) {
172 LPSTR strings[2];
173 strings[0] = msg;
174 ReportEvent(event_log_handle, type, 0, MSG_DEFAULT, nullptr, 1, 0,
175 (LPCSTR *)strings, nullptr);
176 }
177
178 /**
179 Log an msg. If the monitor is started as a windows service, then
180 log it to the event log using apporirate type.
181
182 @param type Type of message indicating whether it is information,
183 warning, error.
184 @param format format string.
185 */
186
monitor_log_msg(Monitor_log_msg_type type,LPCTSTR format,...)187 void monitor_log_msg(Monitor_log_msg_type type, LPCTSTR format, ...) {
188 va_list args;
189 char buf[2048];
190
191 va_start(args, format);
192 int len = vsnprintf(buf, sizeof(buf), format, args);
193 va_end(args);
194
195 if (is_windows_service())
196 log_in_eventlog(get_event_type(type), buf);
197 else
198 fprintf(stderr, "%s.\n", buf);
199 }
200
201 /**
202 Signal an named event by the event name.
203
204 @param event_name pointer to char string representing the event.
205 */
206
signal_thr_open_event(const char * event_name)207 static void signal_thr_open_event(const char *event_name) {
208 HANDLE event = OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name);
209 if (event == nullptr) {
210 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
211 "Open on %s event failed(%d).\n", event_name,
212 GetLastError());
213 return;
214 }
215
216 if (SetEvent(event) == 0) {
217 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
218 "Set on %s event failed(%d).\n", event_name,
219 GetLastError());
220 }
221
222 CloseHandle(event);
223 }
224
get_monitor_pid()225 const char *get_monitor_pid() { return getenv("MYSQLD_PARENT_PID"); }
226
signal_event(Signal_type signal_type)227 void signal_event(Signal_type signal_type) {
228 if (my_monitor_pid == GetCurrentProcessId()) {
229 switch (signal_type) {
230 case Signal_type::SIGNAL_SHUTDOWN:
231 signal_thr_open_event(shutdown_event_name);
232 break;
233 case Signal_type::SIGNAL_SERVICE_STATUS_CMD:
234 SetEvent(service_status_cmd_event);
235 break;
236 case Signal_type::SIGNAL_SERVICE_STATUS_CMD_PROCESSED:
237 signal_thr_open_event(service_status_cmd_processed_event_name);
238 break;
239 }
240 return;
241 }
242
243 if (signal_type == Signal_type::SIGNAL_SERVICE_STATUS_CMD)
244 signal_thr_open_event(service_status_cmd_event_name);
245
246 return;
247 }
248
249 /**
250 Get handle to service status pipe. This call will be in the context
251 of mysqld process.
252
253 @return handle to the service status pipe.
254 */
255
get_service_status_pipe_in_mysqld()256 static HANDLE get_service_status_pipe_in_mysqld() {
257 DBUG_ASSERT(!is_mysqld_monitor());
258
259 if (client_service_status_pipe != nullptr) return client_service_status_pipe;
260 while (1) {
261 client_service_status_pipe =
262 CreateFile(service_status_pipe_name, GENERIC_WRITE, 0, nullptr,
263 OPEN_EXISTING, FILE_READ_ATTRIBUTES, nullptr);
264
265 if (client_service_status_pipe != INVALID_HANDLE_VALUE)
266 return client_service_status_pipe;
267
268 if (GetLastError() != ERROR_PIPE_BUSY) return nullptr;
269
270 if (!WaitNamedPipe(service_status_pipe_name, 20000)) return nullptr;
271 }
272 }
273
274 /**
275 Close mysqld service status pipe.
276 This call shall made in context of the mysqld process.
277
278 */
279
close_service_status_pipe_in_mysqld()280 void close_service_status_pipe_in_mysqld() {
281 DBUG_ASSERT(!is_mysqld_monitor());
282
283 if (client_service_status_pipe != nullptr)
284 CloseHandle(client_service_status_pipe);
285 }
286
287 /**
288 Setup service status pipe.
289
290 @return true if service status pipe is setup successfully else false.
291 */
292
setup_service_status_pipe_in_monitor()293 static bool setup_service_status_pipe_in_monitor() {
294 DBUG_ASSERT(is_mysqld_monitor());
295
296 initialize_events();
297
298 SECURITY_ATTRIBUTES sa;
299 memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
300 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
301 sa.lpSecurityDescriptor = nullptr;
302 sa.bInheritHandle = FALSE;
303
304 service_status_pipe =
305 CreateNamedPipe(service_status_pipe_name,
306 PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
307 PIPE_TYPE_MESSAGE | PIPE_WAIT | PIPE_READMODE_MESSAGE,
308 PIPE_UNLIMITED_INSTANCES, 256, 256, 0, &sa);
309
310 if (service_status_pipe == INVALID_HANDLE_VALUE) {
311 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
312 "Pipe creation failed (%d)", GetLastError());
313 return true;
314 }
315 return false;
316 }
317
318 /**
319 Close service status pipe. This handle is closed in
320 context of the monitor process.
321 */
322
close_service_status_pipe_in_monitor()323 static void close_service_status_pipe_in_monitor() {
324 DBUG_ASSERT(is_mysqld_monitor());
325
326 DisconnectNamedPipe(service_status_pipe);
327 CloseHandle(service_status_pipe);
328 if (service_status_cmd_event) CloseHandle(service_status_cmd_event);
329 }
330
send_service_status(const Service_status_msg & msg)331 bool send_service_status(const Service_status_msg &msg) {
332 if (get_service_status_pipe_in_mysqld() == nullptr) return true;
333
334 DWORD bytes_written = 0;
335 WriteFile(get_service_status_pipe_in_mysqld(), &msg, sizeof(msg),
336 &bytes_written, 0);
337 signal_event(Signal_type::SIGNAL_SERVICE_STATUS_CMD);
338 WaitForSingleObject(service_status_cmd_processed_handle, 1000);
339 return false;
340 }
341
342 /**
343 Get the service status command from the mysqld process and
344 process it and set the status in the SCM.
345
346 @return bool true if the call is successful else false.
347 */
348
get_and_set_service_status()349 static bool get_and_set_service_status() {
350 Service_status_msg service_msg;
351 DWORD bytes_read = 0;
352
353 ReadFile(service_status_pipe, &service_msg, sizeof(service_msg), &bytes_read,
354 nullptr);
355
356 if (bytes_read != 0) {
357 switch (service_msg.service_msg()[0]) {
358 case 'R':
359 get_win_service_ptr()->SetRunning();
360 break;
361 case 'T':
362 // Read further to read the slow start timeout value.
363 ulong slow_start_timeout =
364 strtoul(&service_msg.service_msg()[3], nullptr, 0);
365 get_win_service_ptr()->SetSlowStarting(slow_start_timeout);
366 break;
367 }
368 }
369 signal_event(Signal_type::SIGNAL_SERVICE_STATUS_CMD_PROCESSED);
370 return false;
371 }
372
373 /**
374 Add an argument to a command line adding quotes and backlashes for paths as
375 necessary.
376
377 @param arg string representing the argument.
378 @param[in, out] cmd_line command line to which the string should be appended
379 with necessary quoting.
380 */
381
quote_arg(const std::string & arg,std::string * cmd_line)382 static void quote_arg(const std::string &arg, std::string *cmd_line) {
383 if (!arg.empty() && arg.find_first_of(" \t\n\v\"") == arg.npos) {
384 cmd_line->append(arg);
385 } else {
386 cmd_line->push_back('"');
387 for (std::string::const_iterator it = arg.begin();; ++it) {
388 int num_backslashes = 0;
389 while (it != arg.end() && *it == '\\') {
390 ++it;
391 ++num_backslashes;
392 }
393
394 if (it == arg.end()) {
395 cmd_line->append(num_backslashes * 2, '\\');
396 break;
397 } else if (*it == '"') {
398 cmd_line->append(num_backslashes * 2 + 1, '\\');
399 cmd_line->push_back(*it);
400 } else {
401 cmd_line->append(num_backslashes, '\\');
402 cmd_line->push_back(*it);
403 }
404 }
405 cmd_line->push_back('"');
406 }
407 }
408
409 /**
410 Construct the command line that is to be passed to the spawned
411 mysqld process.
412
413 @param args reference to vector of string arguments.
414
415 @return string representing the cmd line.
416 */
417
construct_cmd_line(const std::vector<std::string> & args)418 static std::string construct_cmd_line(const std::vector<std::string> &args) {
419 std::string cmd_line;
420 for (int i = 0; i < args.size(); ++i) {
421 quote_arg(args[i], &cmd_line);
422 if (i < args.size() - 1) cmd_line += " ";
423 }
424 cmd_line += '\0';
425 return cmd_line;
426 }
427
monitor_mysqld(LPSTR cmd_line)428 static int monitor_mysqld(LPSTR cmd_line) {
429 int envc;
430 char pidbuf[32];
431 char service_buf[32];
432 BOOL res = FALSE;
433 TCHAR path_name[MAX_PATH], cwd[MAX_PATH];
434 DWORD creation_flags = 0, exit_code;
435 PROCESS_INFORMATION pi = {0};
436 STARTUPINFO si = {sizeof(STARTUPINFO), 0};
437 si.cb = sizeof(si);
438
439 if (!GetModuleFileName(nullptr, path_name, MAX_PATH)) {
440 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
441 "GetModuleFileName failed (%d)", GetLastError());
442 return MYSQLD_ABORT_EXIT;
443 }
444
445 if (!GetCurrentDirectory(sizeof(cwd), cwd)) {
446 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
447 "GetCurrentDirectory failed (%d)", GetLastError());
448 return MYSQLD_ABORT_EXIT;
449 }
450
451 for (envc = 0; _environ[envc]; ++envc) {
452 // Get count of environment variables
453 ;
454 }
455
456 std::vector<std::string> env;
457 for (int i = 0; i < envc; i++) env.push_back(_environ[i]);
458 snprintf(pidbuf, sizeof(pidbuf), "MYSQLD_PARENT_PID=%lu", my_monitor_pid);
459 env.push_back(pidbuf);
460 snprintf(service_buf, sizeof(service_buf), "MYSQLD_WINDOWS_SERVICE=%d",
461 is_windows_service());
462 env.push_back(service_buf);
463
464 /*
465 We need to pass environment as a null terminated block of null
466 terminated strings.
467 */
468 size_t env_len = 1;
469 int i = 0;
470 for (const auto &e : env) {
471 env_len += e.size() + 1;
472 i++;
473 }
474 if (i != 0) ++env_len;
475
476 std::unique_ptr<char> env_block(new (std::nothrow) char[env_len]);
477 char *next = env_block.get();
478 i = 0;
479 for (const auto &e : env) {
480 strcpy(next, e.c_str());
481 next = strchr(next, '\0') + 1;
482 i++;
483 }
484 if (i != 0) *next = '\0';
485
486 if (is_windows_service()) setup_service_status_pipe_in_monitor();
487
488 res = CreateProcess(path_name, cmd_line, nullptr, nullptr, FALSE, 0,
489 env_block.get(), cwd, &si, &pi);
490
491 if (!res) {
492 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
493 "CreateProcess Failed (%d)", GetLastError());
494 return MYSQLD_ABORT_EXIT;
495 }
496
497 HANDLE event_handles[NUM_WAIT_HANDLES];
498 event_handles[MYSQLD_HANDLE] = pi.hProcess;
499 event_handles[SERVICE_STATUS_CMD_HANDLE] = service_status_cmd_event;
500
501 if (is_windows_service()) {
502 while (true) {
503 DWORD rv = WaitForMultipleObjects(
504 NUM_WAIT_HANDLES, (HANDLE *)event_handles, FALSE, INFINITE);
505 if (rv == WAIT_FAILED) {
506 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
507 "WaitForMultipleObjects failed(%s).", GetLastError());
508 exit_code = MYSQLD_ABORT_EXIT;
509 break;
510 } else if (rv == WAIT_OBJECT_0 + SERVICE_STATUS_CMD_HANDLE) {
511 get_and_set_service_status();
512 ResetEvent(service_status_cmd_event);
513 } else if (rv == WAIT_OBJECT_0 + MYSQLD_HANDLE) {
514 if (!GetExitCodeProcess(pi.hProcess, &exit_code)) {
515 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
516 "GetExitCodeProcess Failed (%d)", GetLastError());
517 exit_code = MYSQLD_ABORT_EXIT;
518 }
519 break;
520 }
521 }
522 } else {
523 WaitForSingleObject(pi.hProcess, INFINITE);
524 if (!GetExitCodeProcess(pi.hProcess, &exit_code)) {
525 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
526 "GetExitCodeProcess Failed (%d)", GetLastError());
527 exit_code = MYSQLD_ABORT_EXIT;
528 }
529 }
530
531 CloseHandle(pi.hProcess);
532 CloseHandle(pi.hThread);
533
534 if (is_windows_service()) close_service_status_pipe_in_monitor();
535
536 return exit_code;
537 }
538
539 /**
540 Check if the char representing is an early option.
541
542 @param ch charactering representing an short option.
543
544 @return true if the option char is an early short option else false.
545 */
546
is_early_short_option(const char ch)547 static inline bool is_early_short_option(const char ch) {
548 return ch == 'I' || ch == '?' || ch == 'V' || ch == 'v';
549 }
550
551 /**
552 Check if the option specified is an early option.
553
554 @param cur_arg string representing an early option.
555
556 @return true if string is an early option else false.
557 */
558
is_early_option(const char * cur_arg)559 static inline bool is_early_option(const char *cur_arg) {
560 return strncmp(cur_arg, "initialize-insecure",
561 strlen("initialize-insecure")) == 0 ||
562 strncmp(cur_arg, "initialize", strlen("initialize")) == 0 ||
563 strncmp(cur_arg, "verbose", strlen("verbose")) == 0 ||
564 strncmp(cur_arg, "version", strlen("version")) == 0 ||
565 strncmp(cur_arg, "help", strlen("help")) == 0 ||
566 strncmp(cur_arg, "gdb", strlen("gdb")) == 0 ||
567 strncmp(cur_arg, "no-monitor", strlen("no-monitor")) == 0 ||
568 strncmp(cur_arg, "validate-config", strlen("validate-config")) == 0;
569 }
570
is_early_option(int argc,char ** argv)571 bool is_early_option(int argc, char **argv) {
572 for (int index = 1; index < argc; index++) {
573 char *cur_arg = argv[index];
574 if (cur_arg[0] == '-' && cur_arg[1] != '\0') {
575 if (is_early_short_option(cur_arg[1])) return true;
576 cur_arg += 2; // Skip --
577 if (*cur_arg != '\0' && is_early_option(cur_arg)) return true;
578 }
579 }
580 return false;
581 }
582
initialize_mysqld_monitor()583 bool initialize_mysqld_monitor() {
584 mysqld_monitor_process = (getenv("MYSQLD_PARENT_PID") == nullptr);
585 if (mysqld_monitor_process) return initialize_monitor_log();
586 return false;
587 }
588
is_monitor_win_service()589 bool is_monitor_win_service() {
590 if (!mysqld_monitor_process) {
591 char *pid = getenv("MYSQLD_WINDOWS_SERVICE");
592 if (pid != nullptr) return atoi(pid) == 1;
593 }
594 return false;
595 }
596
deinitialize_mysqld_monitor()597 void deinitialize_mysqld_monitor() { deinitialize_monitor_log(); }
598
is_mysqld_monitor()599 bool is_mysqld_monitor() { return mysqld_monitor_process; }
600
601 /**
602 This function assigns names to the shutdown event, service status command
603 event, service status command processed event and the name of service
604 status pipe.
605 */
606
set_event_names()607 static void set_event_names() {
608 snprintf(shutdown_event_name, sizeof(shutdown_event_name),
609 "mysqld%d_shutdown", my_monitor_pid);
610 snprintf(service_status_cmd_event_name, sizeof(service_status_cmd_event_name),
611 "mysqld%d_srv", my_monitor_pid);
612 snprintf(service_status_cmd_processed_event_name,
613 sizeof(service_status_cmd_processed_event_name), "mysqld%d_srvp",
614 my_monitor_pid);
615
616 snprintf(service_status_pipe_name, sizeof(service_status_pipe_name),
617 "\\\\.\\pipe\\mysqld%d_pipe", my_monitor_pid);
618 }
619
620 /**
621 Create an event handle to indicate service status command has been
622 processed from the mysqld monitor parent to the mysqld child.
623
624 @return true if service status command processed handle could not be
625 setup else false.
626 */
627
setup_service_status_cmd_processed_handle()628 bool setup_service_status_cmd_processed_handle() {
629 SECURITY_ATTRIBUTES *service_status_cmd_processed_sec_attr;
630 const char *errmsg = nullptr;
631
632 if (my_security_attr_create(&service_status_cmd_processed_sec_attr, &errmsg,
633 GENERIC_ALL, SYNCHRONIZE | EVENT_MODIFY_STATE)) {
634 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
635 "my_security_attr_create failed");
636 return true;
637 }
638 service_status_cmd_processed_handle =
639 CreateEvent(service_status_cmd_processed_sec_attr, FALSE, FALSE,
640 service_status_cmd_processed_event_name);
641 if (service_status_cmd_processed_handle == INVALID_HANDLE_VALUE) {
642 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
643 "CreateEvent failed %d.", GetLastError());
644 return true;
645 }
646 my_security_attr_free(service_status_cmd_processed_sec_attr);
647 return false;
648 }
649
close_service_status_cmd_processed_handle()650 void close_service_status_cmd_processed_handle() {
651 if (service_status_cmd_processed_handle)
652 CloseHandle(service_status_cmd_processed_handle);
653 }
654
initialize_events()655 static bool initialize_events() {
656 SECURITY_ATTRIBUTES *service_status_cmd_sec_attr;
657 const char *errmsg = nullptr;
658
659 if (my_security_attr_create(&service_status_cmd_sec_attr, &errmsg,
660 GENERIC_ALL, SYNCHRONIZE | EVENT_MODIFY_STATE)) {
661 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
662 "my_security_attr_create failed(%s).", errmsg);
663 return true;
664 }
665 service_status_cmd_event = CreateEvent(service_status_cmd_sec_attr, FALSE,
666 FALSE, service_status_cmd_event_name);
667 if (service_status_cmd_event == INVALID_HANDLE_VALUE) {
668 monitor_log_msg(Monitor_log_msg_type::MONITOR_LOG_ERROR,
669 "CreateEvent failed %d", GetLastError());
670 return true;
671 }
672
673 my_security_attr_free(service_status_cmd_sec_attr);
674 return false;
675 }
676
start_monitor()677 int start_monitor() {
678 char *pid = getenv("MYSQLD_PARENT_PID");
679 if (pid != nullptr) // We should not monitor here, allow for mysqld bootup.
680 {
681 my_monitor_pid = static_cast<DWORD>(atol(pid));
682 set_event_names();
683 return -1; // This is not error scenario.
684 }
685
686 my_monitor_pid = GetCurrentProcessId();
687 set_event_names();
688
689 int exit_code;
690 // Construct the command line
691 int argc_tmp;
692 LPWSTR *argv_tmp = CommandLineToArgvW(GetCommandLineW(), &argc_tmp);
693 std::vector<std::string> argv_vec;
694
695 for (int i = 0; i < argc_tmp; i++) {
696 std::string arg;
697 arg.resize(wcslen(argv_tmp[i])); // Do not copy null
698 wcstombs(&arg[0], argv_tmp[i], arg.size());
699 argv_vec.push_back(arg);
700 }
701 LocalFree(argv_tmp);
702
703 std::string cmd_line = construct_cmd_line(argv_vec);
704 LPSTR cmd_line_str =
705 reinterpret_cast<LPSTR>(const_cast<char *>(cmd_line.c_str()));
706
707 do {
708 exit_code = monitor_mysqld(cmd_line_str);
709 } while (exit_code == MYSQLD_RESTART_EXIT);
710
711 if (is_windows_service()) get_win_service_ptr()->SetExitEvent();
712
713 return exit_code;
714 }
715