MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Process.cpp
1 /*
2  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17 
18 #include <ndb_global.h>
19 
20 #ifdef _WIN32
21 #include <process.h>
22 #include <io.h>
23 #endif
24 #include <BaseString.hpp>
25 #include <InputStream.hpp>
26 
27 #include "common.hpp"
28 #include "CPCD.hpp"
29 #include <errno.h>
30 
31 #ifndef _WIN32
32 #include <pwd.h>
33 #else
34 #include <direct.h>
35 #endif
36 
37 #ifdef HAVE_GETRLIMIT
38 #include <sys/resource.h>
39 #endif
40 
41 void
43  fprintf(f, "define process\n");
44  fprintf(f, "id: %d\n", m_id);
45  fprintf(f, "name: %s\n", m_name.c_str() ? m_name.c_str() : "");
46  fprintf(f, "group: %s\n", m_group.c_str() ? m_group.c_str() : "");
47  fprintf(f, "env: %s\n", m_env.c_str() ? m_env.c_str() : "");
48  fprintf(f, "path: %s\n", m_path.c_str() ? m_path.c_str() : "");
49  fprintf(f, "args: %s\n", m_args.c_str() ? m_args.c_str() : "");
50  fprintf(f, "type: %s\n", m_type.c_str() ? m_type.c_str() : "");
51  fprintf(f, "cwd: %s\n", m_cwd.c_str() ? m_cwd.c_str() : "");
52  fprintf(f, "owner: %s\n", m_owner.c_str() ? m_owner.c_str() : "");
53  fprintf(f, "runas: %s\n", m_runas.c_str() ? m_runas.c_str() : "");
54  fprintf(f, "stdin: %s\n", m_stdin.c_str() ? m_stdin.c_str() : "");
55  fprintf(f, "stdout: %s\n", m_stdout.c_str() ? m_stdout.c_str() : "");
56  fprintf(f, "stderr: %s\n", m_stderr.c_str() ? m_stderr.c_str() : "");
57  fprintf(f, "ulimit: %s\n", m_ulimit.c_str() ? m_ulimit.c_str() : "");
58  fprintf(f, "shutdown: %s\n", m_shutdown_options.c_str() ?
59  m_shutdown_options.c_str() : "");
60 }
61 
62 CPCD::Process::Process(const Properties & props, class CPCD *cpcd) {
63  m_id = -1;
64  m_pid = bad_pid;
65  props.get("id", (Uint32 *) &m_id);
66  props.get("name", m_name);
67  props.get("group", m_group);
68  props.get("env", m_env);
69  props.get("path", m_path);
70  props.get("args", m_args);
71  props.get("cwd", m_cwd);
72  props.get("owner", m_owner);
73  props.get("type", m_type);
74  props.get("runas", m_runas);
75 
76  props.get("stdin", m_stdin);
77  props.get("stdout", m_stdout);
78  props.get("stderr", m_stderr);
79  props.get("ulimit", m_ulimit);
80  props.get("shutdown", m_shutdown_options);
81  m_status = STOPPED;
82 
83  if(strcasecmp(m_type.c_str(), "temporary") == 0){
84  m_processType = TEMPORARY;
85  } else {
86 #ifdef _WIN32
87  logger.critical("Process type must be 'temporary' on windows");
88  exit(1);
89 #endif
90  m_processType = PERMANENT;
91  }
92 
93  m_cpcd = cpcd;
94 }
95 
96 void
98  switch(m_status) {
99  case STARTING:
100  break;
101  case RUNNING:
102  if(!isRunning()){
103  if(m_processType == TEMPORARY){
104  m_status = STOPPED;
105  } else {
106  start();
107  }
108  }
109  break;
110  case STOPPED:
111  assert(!isRunning());
112  break;
113  case STOPPING:
114  break;
115  }
116 }
117 
118 bool
120 
121  if (is_bad_pid(m_pid)) {
122  //logger.critical("isRunning(%d) invalid pid: %d", m_id, m_pid);
123  return false;
124  }
125  /* Check if there actually exists a process with such a pid */
126  errno = 0;
127 
128 #ifdef _WIN32
129  HANDLE proc;
130 
131  if (!(proc = OpenProcess(PROCESS_QUERY_INFORMATION, 0, m_pid)))
132  {
133  logger.debug("Cannot OpenProcess with pid: %d, error: %d",
134  m_pid, GetLastError());
135  return false;
136  }
137 
138  DWORD exitcode;
139  if (GetExitCodeProcess(proc, &exitcode) && exitcode != STILL_ACTIVE)
140  {
141  CloseHandle(proc);
142  return false;
143  }
144 
145  CloseHandle(proc);
146 
147 #else
148  int s = kill((pid_t)-m_pid, 0); /* Sending "signal" 0 to a process only
149  * checkes if the process actually exists */
150  if(s != 0) {
151  switch(errno) {
152  case EPERM:
153  logger.critical("Not enough privileges to control pid %d\n", m_pid);
154  break;
155  case ESRCH:
156  /* The pid in the file does not exist, which probably means that it
157  has died, or the file contains garbage for some other reason */
158  break;
159  default:
160  logger.critical("Cannot not control pid %d: %s\n", m_pid, strerror(errno));
161  break;
162  }
163  return false;
164  }
165 #endif
166  return true;
167 }
168 
169 int
171  if (!is_bad_pid(m_pid)) {
172  logger.critical("Reading pid while having valid process (%d)", m_pid);
173  return m_pid;
174  }
175 
176  char filename[PATH_MAX*2+1];
177  char buf[1024];
178  FILE *f;
179 
180  memset(buf, 0, sizeof(buf));
181 
182  BaseString::snprintf(filename, sizeof(filename), "%d", m_id);
183 
184  f = fopen(filename, "r");
185 
186  if(f == NULL){
187  return -1; /* File didn't exist */
188  }
189 
190  errno = 0;
191  size_t r = fread(buf, 1, sizeof(buf), f);
192  fclose(f);
193  if(r > 0)
194  m_pid = strtol(buf, (char **)NULL, 0);
195 
196  if(errno == 0){
197  return m_pid;
198  }
199 
200  return -1;
201 }
202 #ifdef _WIN32
203 inline int mkstemp(char *tmp)
204 {
205  int fd;
206 
207  if (!_mktemp(tmp))
208  return -1;
209  fd = _open(tmp, _O_CREAT|_O_RDWR|_O_TEXT|_O_TRUNC, _S_IREAD|_S_IWRITE);
210  return fd;
211 }
212 #endif
213 
214 int
216  char tmpfilename[PATH_MAX+1+4+8];
217  char filename[PATH_MAX*2+1];
218  FILE *f;
219 
220  BaseString::snprintf(tmpfilename, sizeof(tmpfilename), "tmp.XXXXXX");
221  BaseString::snprintf(filename, sizeof(filename), "%d", m_id);
222 
223  int fd = mkstemp(tmpfilename);
224  if(fd < 0) {
225  logger.error("Cannot open `%s': %s\n", tmpfilename, strerror(errno));
226  return -1; /* Couldn't open file */
227  }
228 
229  f = fdopen(fd, "w");
230 
231  if(f == NULL) {
232  logger.error("Cannot open `%s': %s\n", tmpfilename, strerror(errno));
233  return -1; /* Couldn't open file */
234  }
235 
236  fprintf(f, "%d", pid);
237  fclose(f);
238 
239 #ifdef _WIN32
240  unlink(filename);
241 #endif
242 
243  if(rename(tmpfilename, filename) == -1){
244  logger.error("Unable to rename from %s to %s", tmpfilename, filename);
245  return -1;
246  }
247  return 0;
248 }
249 
250 static void
251 setup_environment(const char *env) {
252  char **p;
253  p = BaseString::argify("", env);
254  for(int i = 0; p[i] != NULL; i++){
255  /*int res = */ putenv(p[i]);
256  }
257 }
258 
259 static
260 int
261 set_ulimit(const BaseString & pair){
262 #ifdef HAVE_GETRLIMIT
263  errno = 0;
264  Vector<BaseString> list;
265  pair.split(list, ":");
266  if(list.size() != 2){
267  logger.error("Unable to process ulimit: split >%s< list.size()=%d",
268  pair.c_str(), list.size());
269  return -1;
270  }
271 
272  int res;
273  rlim_t value = RLIM_INFINITY;
274  if(!(list[1].trim() == "unlimited")){
275  value = atoi(list[1].c_str());
276  }
277 
278  struct rlimit rlp;
279 #define _RLIMIT_FIX(x) { res = getrlimit(x,&rlp); if(!res){ rlp.rlim_cur = value; res = setrlimit(x, &rlp); }}
280 
281  if(list[0].trim() == "c"){
282  _RLIMIT_FIX(RLIMIT_CORE);
283  } else if(list[0] == "d"){
284  _RLIMIT_FIX(RLIMIT_DATA);
285  } else if(list[0] == "f"){
286  _RLIMIT_FIX(RLIMIT_FSIZE);
287  } else if(list[0] == "n"){
288  _RLIMIT_FIX(RLIMIT_NOFILE);
289  } else if(list[0] == "s"){
290  _RLIMIT_FIX(RLIMIT_STACK);
291  } else if(list[0] == "t"){
292  _RLIMIT_FIX(RLIMIT_CPU);
293  } else {
294  res= -11;
295  errno = EINVAL;
296  }
297  if(res){
298  logger.error("Unable to process ulimit: %s res=%d error=%d(%s)",
299  pair.c_str(), res, errno, strerror(errno));
300  return -1;
301  }
302 #endif
303  return 0;
304 }
305 
306 #ifdef _WIN32
307 const int S_IRUSR = _S_IREAD, S_IWUSR = _S_IWRITE;
308 
309 static void
310 save_environment(const char *env, Vector<BaseString> &saved) {
311  char **ptr;
312 
313  ptr = BaseString::argify("", env);
314  if(!ptr) {
315  logger.error("Could not argify new environment");
316  return;
317  }
318 
319  for(int i = 0; ptr[i] != NULL; i++) {
320  if(!ptr[i][0]) {
321  continue;
322  }
323  char *str1 = strdup(ptr[i]);
324  char *str2;
325  BaseString bs;
326 
327  *strchr(str1, '=') = 0;
328  str2 = getenv(str1);
329  bs.assfmt("%s=%s", str1, str2 ? str2 : "");
330  saved.push_back(bs);
331  }
332 }
333 #endif
334 
335 void
336 CPCD::Process::do_exec() {
337  size_t i;
338 
339 #ifdef _WIN32
340  Vector<BaseString> saved;
341  char *cwd = 0;
342  save_environment(m_env.c_str(), saved);
343 #endif
344 
345  setup_environment(m_env.c_str());
346 
347  char **argv = BaseString::argify(m_path.c_str(), m_args.c_str());
348 
349  if(strlen(m_cwd.c_str()) > 0) {
350 #ifdef _WIN32
351  cwd = getcwd(0, 0);
352  if(!cwd)
353  {
354  logger.critical("Couldn't getcwd before spawn");
355  }
356 #endif
357  int err = chdir(m_cwd.c_str());
358  if(err == -1) {
359  BaseString err;
360  logger.error("%s: %s\n", m_cwd.c_str(), strerror(errno));
361  _exit(1);
362  }
363  }
364 #ifndef _WIN32
365  Vector<BaseString> ulimit;
366  m_ulimit.split(ulimit);
367  for(i = 0; i<ulimit.size(); i++){
368  if(ulimit[i].trim().length() > 0 && set_ulimit(ulimit[i]) != 0){
369  _exit(1);
370  }
371  }
372 #endif
373 
374  const char *nul = IF_WIN("nul:", "/dev/null");
375  int fdnull = open(nul, O_RDWR, 0);
376  if(fdnull == -1) {
377  logger.error("Cannot open `%s': %s\n", nul, strerror(errno));
378  _exit(1);
379  }
380 
381  BaseString * redirects[] = { &m_stdin, &m_stdout, &m_stderr };
382  int fds[3];
383 #ifdef _WIN32
384  int std_dups[3];
385 #endif
386  for (i = 0; i < 3; i++) {
387 #ifdef _WIN32
388  std_dups[i] = dup(i);
389 #endif
390  if (redirects[i]->empty()) {
391 #ifndef DEBUG
392  dup2(fdnull, i);
393 #endif
394  continue;
395  }
396 
397  if((* redirects[i]) == "2>&1" && i == 2){
398  dup2(fds[1], 2);
399  continue;
400  }
401 
405  int flags = 0;
406  int mode = S_IRUSR | S_IWUSR ;
407  if(i == 0){
408  flags |= O_RDONLY;
409  } else {
410  flags |= O_WRONLY | O_CREAT | O_APPEND;
411  }
412  int f = fds[i]= open(redirects[i]->c_str(), flags, mode);
413  if(f == -1){
414  logger.error("Cannot redirect %ld to/from '%s' : %s\n", i,
415  redirects[i]->c_str(), strerror(errno));
416  _exit(1);
417  }
418  dup2(f, i);
419 #ifdef _WIN32
420  close(f);
421 #endif
422  }
423 
424 #ifndef _WIN32
425  /* Close all filedescriptors */
426  for(i = STDERR_FILENO+1; (int)i < getdtablesize(); i++)
427  close(i);
428 
429  execv(m_path.c_str(), argv);
430  /* XXX If we reach this point, an error has occurred, but it's kind of hard
431  * to report it, because we've closed all files... So we should probably
432  * create a new logger here */
433  logger.error("Exec failed: %s\n", strerror(errno));
434  /* NOTREACHED */
435 #else
436 
437  // Get full path to cygwins shell
438  FILE *fpipe = _popen("sh -c 'cygpath -w `which sh`'", "rt");
439  char buf[MAX_PATH];
440 
441  require(fgets(buf, MAX_PATH - 1, fpipe));
442  fclose(fpipe);
443 
444  BaseString sh;
445  sh.assign(buf);
446  sh.trim("\n");
447  sh.append(".exe");
448 
449  BaseString shcmd;
450  shcmd.assfmt("%s -c '%s %s'", sh.c_str(), m_path.c_str(), m_args.c_str());
451 
452  PROCESS_INFORMATION pi = {0};
453  STARTUPINFO si = {sizeof(STARTUPINFO), 0};
454 
455  si.dwFlags |= STARTF_USESTDHANDLES;
456  si.hStdInput = (HANDLE)_get_osfhandle(0);
457  si.hStdOutput = (HANDLE)_get_osfhandle(1);
458  si.hStdError = (HANDLE)_get_osfhandle(2);
459 
460  if(!CreateProcessA(sh.c_str(),
461  (LPSTR)shcmd.c_str(),
462  NULL,
463  NULL,
464  TRUE,
465  CREATE_SUSPENDED, // Resumed after assigned to Job
466  NULL,
467  NULL,
468  &si,
469  &pi))
470  {
471  char* message;
472  DWORD err = GetLastError();
473 
474  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
475  FORMAT_MESSAGE_FROM_SYSTEM |
476  FORMAT_MESSAGE_IGNORE_INSERTS,
477  NULL,
478  err,
479  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
480  (LPTSTR)&message,
481  0, NULL );
482 
483  logger.error("CreateProcess failed, error: %d, message: '%s'",
484  err, message);
485  LocalFree(message);
486 
487  }
488 
489  HANDLE proc = pi.hProcess;
490  require(proc);
491 
492  // Job control
493  require(m_job = CreateJobObject(0, 0));
494  require(AssignProcessToJobObject(m_job, proc));
495 
496  // Resum process after it has been added to Job
497  ResumeThread(pi.hThread);
498  CloseHandle(pi.hThread);
499 
500 
501  // go back up to original cwd
502  if(chdir(cwd))
503  {
504  logger.critical("Couldn't go back to saved cwd after spawn()");
505  logger.critical("errno: %d, strerror: %s", errno, strerror(errno));
506  }
507  free(cwd);
508 
509  // get back to original std i/o
510  for(i = 0; i < 3; i++) {
511  dup2(std_dups[i], i);
512  close(std_dups[i]);
513  }
514 
515  for (i = 0; i < saved.size(); i++) {
516  putenv(saved[i].c_str());
517  }
518 
519  logger.debug("'%s' has been started", shcmd.c_str());
520 
521  DWORD exitcode;
522  BOOL result = GetExitCodeProcess(proc, &exitcode);
523  //maybe a short running process
524  if (result && exitcode != 259) {
525  m_status = STOPPED;
526  logger.warning("Process terminated early");
527  }
528 
529  int pid = GetProcessId(proc);
530  if (!pid)
531  logger.critical("GetProcessId failed, error: %d!", GetLastError());
532 
533  logger.debug("new pid %d", pid);
534 
535  CloseHandle(proc);
536  m_status = RUNNING;
537  writePid(pid);
538 #endif
539 
540  close(fdnull);
541 }
542 
543 #ifdef _WIN32
544 void sched_yield() {
545  Sleep(100);
546 }
547 #endif
548 
549 int
551  /* We need to fork() twice, so that the second child (grandchild?) can
552  * become a daemon. The original child then writes the pid file,
553  * so that the monitor knows the pid of the new process, and then
554  * exit()s. That way, the monitor process can pickup the pid, and
555  * the running process is a daemon.
556  *
557  * This is a bit tricky but has the following advantages:
558  * - the cpcd can die, and "reconnect" to the monitored clients
559  * without restarting them.
560  * - the cpcd does not have to wait() for the childs. init(1) will
561  * take care of that.
562  */
563  logger.info("Starting %d: %s", m_id, m_name.c_str());
564  m_status = STARTING;
565 
566  int pid = -1;
567  switch(m_processType){
568  case TEMPORARY:{
569 #ifndef _WIN32
570 
574  switch(pid = fork()) {
575  case 0: /* Child */
576  setsid();
577  writePid(getpgrp());
578  if(runas(m_runas.c_str()) == 0){
579  signal(SIGCHLD, SIG_DFL);
580  do_exec();
581  }
582  _exit(1);
583  break;
584  case -1: /* Error */
585  logger.error("Cannot fork: %s\n", strerror(errno));
586  m_status = STOPPED;
587  return -1;
588  break;
589  default: /* Parent */
590  logger.debug("Started temporary %d : pid=%d", m_id, pid);
591  break;
592  }
593 #else //_WIN32
594  do_exec();
595 #endif
596  break;
597  }
598 #ifndef _WIN32
599  case PERMANENT:{
603  switch(fork()) {
604  case 0: /* Child */
605  signal(SIGCHLD, SIG_IGN);
606  switch(pid = fork()) {
607  case 0: /* Child */
608  setsid();
609  writePid(getpgrp());
610  if(runas(m_runas.c_str()) != 0){
611  _exit(1);
612  }
613  signal(SIGCHLD, SIG_DFL);
614  do_exec();
615  _exit(1);
616  /* NOTREACHED */
617  break;
618  case -1: /* Error */
619  logger.error("Cannot fork: %s\n", strerror(errno));
620  writePid(-1);
621  _exit(1);
622  break;
623  default: /* Parent */
624  logger.debug("Started permanent %d : pid=%d", m_id, pid);
625  _exit(0);
626  break;
627  }
628  break;
629  case -1: /* Error */
630  logger.error("Cannot fork: %s\n", strerror(errno));
631  m_status = STOPPED;
632  return -1;
633  break;
634  default: /* Parent */
635  break;
636  }
637  break;
638  }
639 #endif
640  default:
641  logger.critical("Unknown process type");
642  return -1;
643  }
644 
645  while(readPid() < 0){
646  sched_yield();
647  }
648 
649  errno = 0;
650  pid_t pgid = IF_WIN(-1, getpgid(pid));
651 
652  if(pgid != -1 && pgid != m_pid){
653  logger.error("pgid and m_pid don't match: %d %d (%d)", pgid, m_pid, pid);
654  }
655 
656  if(isRunning()){
657  m_status = RUNNING;
658  return 0;
659  }
660  m_status = STOPPED;
661 
662  return -1;
663 }
664 
665 void
667 
668  char filename[PATH_MAX*2+1];
669  BaseString::snprintf(filename, sizeof(filename), "%d", m_id);
670  unlink(filename);
671 
672  if (is_bad_pid(m_pid))
673  {
674  logger.critical("Stopping process with bogus pid: %d id: %d",
675  m_pid, m_id);
676  return;
677  }
678 
679  m_status = STOPPING;
680 
681 #ifndef _WIN32
682  errno = 0;
683  int signo= SIGTERM;
684  if(m_shutdown_options == "SIGKILL")
685  signo= SIGKILL;
686 
687  int ret = kill(-m_pid, signo);
688  switch(ret) {
689  case 0:
690  logger.debug("Sent SIGTERM to pid %d", (int)-m_pid);
691  break;
692  default:
693  logger.debug("kill pid: %d : %s", (int)-m_pid, strerror(errno));
694  break;
695  }
696 
697  if(isRunning()){
698  errno = 0;
699  ret = kill(-m_pid, SIGKILL);
700  switch(ret) {
701  case 0:
702  logger.debug("Sent SIGKILL to pid %d", (int)-m_pid);
703  break;
704  default:
705  logger.debug("kill pid: %d : %s\n", (int)-m_pid, strerror(errno));
706  break;
707  }
708  }
709 #else
710  if(isRunning())
711  {
712  BOOL truth;
713  HANDLE proc;
714  require(proc = OpenProcess(PROCESS_QUERY_INFORMATION, 0, m_pid));
715  require(IsProcessInJob(proc, m_job, &truth));
716  require(truth == TRUE);
717  require(CloseHandle(proc));
718  // Terminate process with exit code 37
719  require(TerminateJobObject(m_job, 37));
720  require(CloseHandle(m_job));
721  }
722 #endif
723 
724  m_pid = bad_pid;
725  m_status = STOPPED;
726 }