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