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