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