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