MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ndb_daemon.cc
1 /* Copyright (c) 2009, 2010, 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 as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
15 */
16 
17 #ifdef _WIN32
18 #include <process.h>
19 #endif
20 #include <my_global.h>
21 #include <my_sys.h>
22 #include <m_string.h>
23 #include <ndb_daemon.h>
24 
25 #include <BaseString.hpp>
26 #include <portlib/NdbHost.h>
27 
28 static FILE *dlog_file;
29 
30 static int ERR1(const char* fmt, ...)
31  ATTRIBUTE_FORMAT(printf, 1, 2);
32 
33 char ndb_daemon_error[1024];
34 static int ERR1(const char* fmt, ...)
35 {
36  va_list argptr;
37  va_start(argptr, fmt);
38  my_vsnprintf(ndb_daemon_error,sizeof(ndb_daemon_error),fmt,argptr);
39  va_end(argptr);
40  return 1;
41 }
42 
43 
44 #ifdef _WIN32
45 
46 #include <nt_servc.h>
47 static NTService g_ntsvc;
48 
49 static int g_argc;
50 static char** g_argv;
51 
52 static HANDLE g_shutdown_event;
53 static ndb_daemon_stop_t g_stop_func;
54 static ndb_daemon_run_t g_run_func;
55 
56 static void stopper_thread(void*)
57 {
58  // Wait forever until the shutdown event is signaled
59  WaitForSingleObject(g_shutdown_event, INFINITE);
60 
61  // Call the installed stop callback function
62  g_stop_func();
63 }
64 
65 
66 /*
67  This function is called like:
68  <service dipatcher thread>
69  - NTService::ServiceMain
70  - NTService::StartService
71  <new service thread>
72  -service_main
73  and runs the "application" through
74  the installed callback function "g_run_func"
75 */
76 
77 static int service_main(NTService* service)
78 {
79  /* Inform SCM that service is running and can be stopped */
80  service->SetRunning();
81 
82  /* Run the application with with argc/argv */
83  return g_run_func(g_argc, g_argv);
84 }
85 
86 
87 /*
88  Check if "arg" contains "option", return the
89  options argument in opt_arg(i.e everything after =)
90 */
91 
92 static bool
93 is_option(const char* arg, const char* option, const char** opt_arg)
94 {
95  size_t option_len = strlen(option);
96  if (strncmp(arg, option, option_len))
97  return false; // No hit
98 
99  // Step forward to the end of --<option>
100  arg+= option_len;
101  if (*arg == '=')
102  {
103  /* Assign opt_arg pointer to first char after = */
104  *opt_arg= arg + 1;
105  }
106  return true;
107 }
108 
109 
110 static int
111 install_or_remove_service(int argc, char** argv,
112  const char* name, const char* display_name)
113 {
114  if (argc < 2)
115  return 0; // Nothing to do
116 
117  /* --remove as first argument on command line */
118  const char* remove_name = NULL;
119  if (is_option(argv[1], "--remove", &remove_name))
120  {
121  if (remove_name)
122  {
123  /* Use the part after = as service name _and_ display name */
124  name = display_name = remove_name;
125  }
126  printf("Removing service '%s'\n", display_name);
127  /* Remove service. Ignore return value, error is printed to stdout */
128  (void)g_ntsvc.Remove(name);
129  return 1;
130  }
131 
132  const char* install_name = NULL;
133  if (is_option(argv[1], "--install", &install_name))
134  {
135  if (install_name)
136  {
137  /* Use the part after = as service name _and_ display name */
138  name = display_name = install_name;
139  }
140 
141  BaseString cmd;
142 
143  /* Full path to this binary */
144  char exe[MAX_PATH];
145  GetModuleFileName(NULL, exe, sizeof(exe));
146  cmd.assfmt("\"%s\"", exe);
147 
148  /* The option that tells which service is starting */
149  cmd.appfmt(" \"--service=%s\"", name);
150 
151  /* All the args after --install(which must be first) */
152  for (int i = 2; i < argc; i++)
153  cmd.appfmt(" \"%s\"", argv[i]);
154 
155  printf("Installing service '%s' as '%s'\n", display_name, cmd.c_str());
156 
157  /* Install service. Ignore return value, error is printed to stdout */
158  (void)g_ntsvc.Install(1, name, display_name, cmd.c_str());
159  return 1;
160  }
161  return 0;
162 }
163 #endif
164 
165 
166 int ndb_daemon_init(int argc, char** argv,
167  ndb_daemon_run_t run, ndb_daemon_stop_t stop,
168  const char* name, const char* display_name)
169 {
170 #ifdef _WIN32
171  // Check for --install or --remove options
172  if (install_or_remove_service(argc, argv, name, display_name))
173  return 1;
174 
175  // Check if first arg is --service -> run as service
176  const char* service_name = NULL;
177  if (argc > 1 &&
178  is_option(argv[1], "--service", &service_name) &&
179  service_name)
180  {
181  // Create the shutdown event that will be signaled
182  // by g_ntsvc if the service is to be stopped
183  g_shutdown_event = CreateEvent(0, 0, 0, 0);
184 
185  // Install the shutdown event in g_ntsvc
186  g_ntsvc.SetShutdownEvent(g_shutdown_event);
187 
188  // Save the stop function so it can be called
189  // by 'stopper_thread'
190  g_stop_func = stop;
191 
192  // Create a thread whose only purpose is to wait for
193  // the shutdown event to be signaled and then call the 'stop'
194  // function
195  uintptr_t stop_thread = _beginthread(stopper_thread,0,0);
196  if(!stop_thread)
197  return ERR1("couldn't start stopper thread");
198 
199  // Save the run function so it can be called
200  // by 'service_main'
201  g_run_func = run;
202 
203  // Build argv without --service
204  BaseString cmd;
205  for (int i = 2; i < argc; i++)
206  cmd.appfmt(" %s", argv[i]);
207  g_argv = BaseString::argify(argv[0], cmd.c_str());
208  g_argc = argc - 1;
209 
210  // Start the service thread and let it run 'service_main'
211  // This call will not return until the service thread returns
212  return g_ntsvc.Init(service_name, service_main);
213  }
214 #endif
215 
216  // Default behaviour, run the "run" function which
217  // should be the "applications" real main
218  return run(argc, argv);
219 
220 }
221 
222 
223 #ifdef _WIN32
224 
225 #include <sys/locking.h>
226 
227 #define F_TLOCK _LK_NBLCK
228 #define F_ULOCK _LK_UNLCK
229 #define F_LOCK _LK_LOCK
230 
231 static inline int lockf(int fd, int cmd, off_t len)
232 {
233  return _locking(fd, cmd, len);
234 }
235 
236 static inline int ftruncate(int fd, off_t length)
237 {
238  return _chsize(fd, length);
239 }
240 
241 static inline int unlink(const char *filename)
242 {
243  return _unlink(filename);
244 }
245 #endif
246 
247 static const char *g_pidfile_name = 0;
248 static int g_pidfd = -1, g_logfd = -1;
249 
250 static int
251 check_files(const char *pidfile_name,
252  const char *logfile_name,
253  int *pidfd_ret, int *logfd_ret)
254 {
255  /* Open log file if any */
256  if (logfile_name)
257  {
258  int logfd = open(logfile_name, O_CREAT | O_WRONLY | O_APPEND, 0644);
259  if(logfd == -1)
260  return ERR1("Failed to open logfile '%s' for write, errno: %d",
261  logfile_name, errno);
262  g_logfd = logfd;
263  dlog_file = fdopen(logfd, "a");
264  *logfd_ret = logfd;
265  }
266 
267  /* Check that we have write access to lock file */
268  if (!pidfile_name)
269  return ERR1("Missing pid file name");
270  int pidfd= open(pidfile_name, O_CREAT | O_RDWR, 0644);
271  if (pidfd == -1)
272  return ERR1("Failed to open pidfile '%s' for write, errno: %d",
273  pidfile_name, errno);
274  g_pidfd = pidfd;
275 
276  /* Read any old pid from lock file */
277  char buf[32];
278  int bytes_read = read(pidfd, buf, sizeof(buf));
279  if(bytes_read < 0)
280  return ERR1("Failed to read from pidfile '%s', errno: %d",
281  pidfile_name, errno);
282  buf[bytes_read]= 0;
283  long currpid= atol(buf);
284  if(lseek(pidfd, 0, SEEK_SET) == -1)
285  return ERR1("Failed to lseek pidfile '%s', errno: %d",
286  pidfile_name, errno);
287 
288  /* Check that file can be locked */
289  if(lockf(pidfd, F_TLOCK, 0) == -1)
290  {
291  if(errno == EACCES || errno == EAGAIN)
292  return ERR1("Failed to lock pidfile '%s', already locked by "
293  "pid=%ld, errno: %d", pidfile_name, currpid, errno);
294  }
295  if(lockf(pidfd, F_ULOCK, 0) == -1)
296  return ERR1("Failed to unlock pidfile '%s', errno: %d",
297  pidfile_name, errno);
298 
299  *pidfd_ret = pidfd;
300  return 0;
301 }
302 
303 
304 static int
305 do_files(const char *pidfile_name, const char* logfile_name, int pidfd, int logfd)
306 {
307  /* Lock the lock file */
308  if (lockf(pidfd, F_LOCK, 0) == -1)
309  return ERR1("Failed to lock pidfile '%s', errno: %d",
310  pidfile_name, errno);
311 
312  /* Write pid to lock file */
313  if (ftruncate(pidfd, 0) == -1)
314  return ERR1("Failed to truncate file '%s', errno: %d",
315  pidfile_name, errno);
316 
317  char buf[32];
318  int length = my_snprintf(buf, sizeof(buf), "%ld",
319  (long)NdbHost_GetProcessId());
320  if (write(pidfd, buf, length) != length)
321  return ERR1("Failed to write pid to pidfile '%s', errno: %d",
322  pidfile_name, errno);
323 
324 #ifdef _WIN32
325  // Redirect stdout and stderr to the daemon log file
326  freopen(logfile_name, "a+", stdout);
327  freopen(logfile_name, "a+", stderr);
328  setbuf(stderr, NULL);
329 #else
330  /* Do input/output redirections (assume fd 0,1,2 not in use) */
331  close(0);
332  const char* fname = "/dev/null";
333  if (open(fname, O_RDONLY) == -1)
334  return ERR1("Failed to open '%s', errno: %d", fname, errno);
335 
336  if (logfd != -1)
337  {
338  dup2(logfd, 1);
339  dup2(logfd, 2);
340  close(logfd);
341  dlog_file= stdout;
342  }
343 #endif
344 
345  return 0;
346 }
347 
348 
349 int ndb_daemonize(const char* pidfile_name, const char *logfile_name)
350 {
351  int pidfd = -1, logfd = -1;
352 
353  if (check_files(pidfile_name, logfile_name, &pidfd, &logfd))
354  return 1;
355 
356 #ifndef _WIN32
357  pid_t child = fork();
358  if (child == -1)
359  return ERR1("fork failed, errno: %d, error: %s", errno, strerror(errno));
360 
361  /* Exit if we are the parent */
362  if (child != 0)
363  exit(0);
364 
365  /* Become process group leader */
366  if(setsid() == -1)
367  return ERR1("Failed to setsid, errno: %d", errno);
368 
369 #endif
370 
371  if (do_files(pidfile_name, logfile_name, pidfd, logfd))
372  return 1;
373 
374  g_pidfile_name = pidfile_name;
375 
376  return 0;
377 }
378 
379 void ndb_daemon_exit(int status)
380 {
381  if (g_pidfd != -1)
382  close(g_pidfd);
383 
384  if (g_logfd != -1)
385  close(g_logfd);
386 
387  if (g_pidfile_name)
388  unlink(g_pidfile_name);
389 
390 #ifdef _WIN32
391  /*
392  Stop by calling NtService::Stop if running
393  as a service(i.e g_shutdown_event created)
394  */
395  if (g_shutdown_event)
396  g_ntsvc.Stop();
397 #endif
398 
399 #ifdef HAVE_gcov
400  exit(status);
401 #else
402  _exit(status);
403 #endif
404 
405 }
406 
407 void ndb_service_print_options(const char* name)
408 {
409 #ifdef _WIN32
410  puts("");
411  puts("The following Windows specific options may be given as "
412  "the first argument:");
413  printf(" --install[=name]\tInstall %s as service with given "
414  "name(default: %s), \n"
415  "\t\t\tusing the arguments currently given on command line.\n",
416  name, name);
417  printf(" --remove[=name]\tRemove service with name(default: %s)\n",
418  name);
419  puts("");
420 #endif
421 }
422 
423 
424 void ndb_service_wait_for_debugger(int timeout_sec)
425 {
426 #ifdef _WIN32
427  if(!IsDebuggerPresent())
428  {
429  int i;
430  printf("Waiting for debugger to attach, pid=%u\n",GetCurrentProcessId());
431  fflush(stdout);
432  for(i= 0; i < timeout_sec; i++)
433  {
434  Sleep(1000);
435  if(IsDebuggerPresent())
436  {
437  /* Break into debugger */
438  __debugbreak();
439  return;
440  }
441  }
442  printf("pid=%u, debugger not attached after %d seconds, resuming\n",GetCurrentProcessId(),
443  timeout_sec);
444  fflush(stdout);
445  }
446 #endif
447 }