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