示例#1
0
static void
dumpFileDescriptorInfoWithLs(AbortHandlerState &state, char *end) {
	pid_t pid;
	int status;

	pid = asyncFork();
	if (pid == 0) {
		closeAllFileDescriptors(2, true);
		// The '-v' is for natural sorting on Linux. On BSD -v means something else but it's harmless.
		execlp("ls", "ls", "-lv", state.messageBuf, (const char * const) 0);
		_exit(1);
	} else if (pid == -1) {
		safePrintErr("ERROR: Could not fork a process to dump file descriptor information!\n");
	} else if (waitpid(pid, &status, 0) != pid || status != 0) {
		safePrintErr("ERROR: Could not run 'ls' to dump file descriptor information!\n");
	}
}
示例#2
0
static void
dumpFileDescriptorInfoWithLsof(AbortHandlerState &state, void *userData) {
	char *end;

	end = state.messageBuf;
	end = appendULL(end, state.pid);
	*end = '\0';

	closeAllFileDescriptors(2, true);

	execlp("lsof", "lsof", "-p", state.messageBuf, "-nP", (const char * const) 0);

	end = state.messageBuf;
	end = appendText(end, "ERROR: cannot execute command 'lsof': errno=");
	end = appendULL(end, errno);
	end = appendText(end, "\n");
	write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
	_exit(1);
}
示例#3
0
static void
dumpWithCrashWatch(AbortHandlerState &state) {
	char *messageBuf = state.messageBuf;
	const char *pidStr = messageBuf;
	char *end = messageBuf;
	end = appendULL(end, (unsigned long long) state.pid);
	*end = '\0';
	
	pid_t child = asyncFork();
	if (child == 0) {
		closeAllFileDescriptors(2, true);
		execlp("crash-watch", "crash-watch", "--dump", pidStr, (char * const) 0);
		if (errno == ENOENT) {
			safePrintErr("Crash-watch is not installed. Please install it with 'gem install crash-watch' "
				"or download it from https://github.com/FooBarWidget/crash-watch.\n");
		} else {
			int e = errno;
			end = messageBuf;
			end = appendText(end, "crash-watch is installed, but it could not be executed! ");
			end = appendText(end, "(execlp() returned errno=");
			end = appendULL(end, e);
			end = appendText(end, ") Please check your file permissions or something.\n");
			write_nowarn(STDERR_FILENO, messageBuf, end - messageBuf);
		}
		_exit(1);

	} else if (child == -1) {
		int e = errno;
		end = messageBuf;
		end = appendText(end, "Could not execute crash-watch: fork() failed with errno=");
		end = appendULL(end, e);
		end = appendText(end, "\n");
		write_nowarn(STDERR_FILENO, messageBuf, end - messageBuf);

	} else {
		waitpid(child, NULL, 0);
	}
}
示例#4
0
	void
	threadMain() {
		while (!boost::this_thread::interruption_requested()) {
			syscalls::sleep(60 * 60);

			begin_touch:

			boost::this_thread::disable_interruption di;
			boost::this_thread::disable_syscall_interruption dsi;
			// Fork a process which touches everything in the server instance dir.
			pid_t pid = syscalls::fork();
			if (pid == 0) {
				// Child
				int prio, ret, e;

				closeAllFileDescriptors(2);

				// Make process nicer.
				do {
					prio = getpriority(PRIO_PROCESS, getpid());
				} while (prio == -1 && errno == EINTR);
				if (prio != -1) {
					prio++;
					if (prio > 20) {
						prio = 20;
					}
					do {
						ret = setpriority(PRIO_PROCESS, getpid(), prio);
					} while (ret == -1 && errno == EINTR);
				} else {
					perror("getpriority");
				}

				do {
					ret = chdir(wo->instanceDir->getPath().c_str());
				} while (ret == -1 && errno == EINTR);
				if (ret == -1) {
					e = errno;
					fprintf(stderr, "chdir(\"%s\") failed: %s (%d)\n",
						wo->instanceDir->getPath().c_str(),
						strerror(e), e);
					fflush(stderr);
					_exit(1);
				}
				restoreOomScore(agentsOptions);

				execlp("/bin/sh", "/bin/sh", "-c", "find . | xargs touch", (char *) 0);
				e = errno;
				fprintf(stderr, "Cannot execute 'find . | xargs touch': %s (%d)\n",
					strerror(e), e);
				fflush(stderr);
				_exit(1);
			} else if (pid == -1) {
				// Error
				P_WARN("Could not touch the server instance directory because "
					"fork() failed. Retrying in 2 minutes...");
				boost::this_thread::restore_interruption si(di);
				boost::this_thread::restore_syscall_interruption rsi(dsi);
				syscalls::sleep(60 * 2);
				goto begin_touch;
			} else {
				syscalls::waitpid(pid, NULL, 0);
			}
		}
	}
示例#5
0
static void
abortHandler(int signo, siginfo_t *info, void *ctx) {
	AbortHandlerState state;
	state.pid = getpid();
	state.signo = signo;
	state.info = info;
	pid_t child;
	time_t t = time(NULL);
	char crashLogFile[256];

	abortHandlerCalled++;
	if (abortHandlerCalled > 1) {
		// The abort handler itself crashed!
		char *end = state.messageBuf;
		end = appendText(end, "[ origpid=");
		end = appendULL(end, (unsigned long long) state.pid);
		end = appendText(end, ", pid=");
		end = appendULL(end, (unsigned long long) getpid());
		end = appendText(end, ", timestamp=");
		end = appendULL(end, (unsigned long long) t);
		if (abortHandlerCalled == 2) {
			// This is the first time it crashed.
			end = appendText(end, " ] Abort handler crashed! signo=");
			end = appendSignalName(end, state.signo);
			end = appendText(end, ", reason=");
			end = appendSignalReason(end, state.info);
			end = appendText(end, "\n");
			write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
			// Run default signal handler.
			raise(signo);
		} else {
			// This is the second time it crashed, meaning it failed to
			// invoke the default signal handler to abort the process!
			end = appendText(end, " ] Abort handler crashed again! Force exiting this time. signo=");
			end = appendSignalName(end, state.signo);
			end = appendText(end, ", reason=");
			end = appendSignalReason(end, state.info);
			end = appendText(end, "\n");
			write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
			_exit(1);
		}
		return;
	}

	if (emergencyPipe1[0] != -1) {
		close(emergencyPipe1[0]);
	}
	if (emergencyPipe1[1] != -1) {
		close(emergencyPipe1[1]);
	}
	if (emergencyPipe2[0] != -1) {
		close(emergencyPipe2[0]);
	}
	if (emergencyPipe2[1] != -1) {
		close(emergencyPipe2[1]);
	}
	emergencyPipe1[0] = emergencyPipe1[1] = -1;
	emergencyPipe2[0] = emergencyPipe2[1] = -1;

	/* We want to dump the entire crash log to both stderr and a log file.
	 * We use 'tee' for this.
	 */
	if (createCrashLogFile(crashLogFile, t)) {
		forkAndRedirectToTee(crashLogFile);
	}

	char *end = state.messagePrefix;
	end = appendText(end, "[ pid=");
	end = appendULL(end, (unsigned long long) state.pid);
	*end = '\0';

	end = state.messageBuf;
	end = appendText(end, state.messagePrefix);
	end = appendText(end, ", timestamp=");
	end = appendULL(end, (unsigned long long) t);
	end = appendText(end, " ] Process aborted! signo=");
	end = appendSignalName(end, state.signo);
	end = appendText(end, ", reason=");
	end = appendSignalReason(end, state.info);
	end = appendText(end, ", randomSeed=");
	end = appendULL(end, (unsigned long long) randomSeed);
	end = appendText(end, "\n");
	write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);

	end = state.messageBuf;
	if (*crashLogFile != '\0') {
		end = appendText(end, state.messagePrefix);
		end = appendText(end, " ] Crash log dumped to ");
		end = appendText(end, crashLogFile);
		end = appendText(end, "\n");
	} else {
		end = appendText(end, state.messagePrefix);
		end = appendText(end, " ] Could not create crash log file, so dumping to stderr only.\n");
	}
	write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);

	if (beepOnAbort) {
		end = state.messageBuf;
		end = appendText(end, state.messagePrefix);
		end = appendText(end, " ] PASSENGER_BEEP_ON_ABORT on, executing beep...\n");
		write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);

		child = asyncFork();
		if (child == 0) {
			closeAllFileDescriptors(2, true);
			#ifdef __APPLE__
				execlp("osascript", "osascript", "-e", "beep 2", (const char * const) 0);
				safePrintErr("Cannot execute 'osascript' command\n");
			#else
				execlp("beep", "beep", (const char * const) 0);
				safePrintErr("Cannot execute 'beep' command\n");
			#endif
			_exit(1);

		} else if (child == -1) {
			int e = errno;
			end = state.messageBuf;
			end = appendText(end, state.messagePrefix);
			end = appendText(end, " ] Could fork a child process for invoking a beep: fork() failed with errno=");
			end = appendULL(end, e);
			end = appendText(end, "\n");
			write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
		}
	}

	if (stopOnAbort) {
		end = state.messageBuf;
		end = appendText(end, state.messagePrefix);
		end = appendText(end, " ] PASSENGER_STOP_ON_ABORT on, so process stopped. Send SIGCONT when you want to continue.\n");
		write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
		raise(SIGSTOP);
	}

	// It isn't safe to call any waiting functions in this signal handler,
	// not even read() and waitpid() even though they're async signal safe.
	// So we fork a child process and let it dump as much diagnostics as possible
	// instead of doing it in this process.
	child = asyncFork();
	if (child == 0) {
		// Sleep for a short while to allow the parent process to raise SIGSTOP.
		// usleep() and nanosleep() aren't async signal safe so we use select()
		// instead.
		struct timeval tv;
		tv.tv_sec = 0;
		tv.tv_usec = 100000;
		select(0, NULL, NULL, NULL, &tv);

		resetSignalHandlersAndMask();

		child = asyncFork();
		if (child == 0) {
			// OS X: for some reason the SIGPIPE handler may be reset to default after forking.
			// Later in this program we're going to pipe backtrace_symbols_fd() into the backtrace
			// sanitizer, which may fail, and we don't want the diagnostics process to crash
			// with SIGPIPE as a result, so we ignore SIGPIPE again.
			ignoreSigpipe();
			dumpDiagnostics(state);
			// The child process may or may or may not resume the original process.
			// We do it ourselves just to be sure.
			kill(state.pid, SIGCONT);
			_exit(0);

		} else if (child == -1) {
			int e = errno;
			end = state.messageBuf;
			end = appendText(end, state.messagePrefix);
			end = appendText(end, "] Could fork a child process for dumping diagnostics: fork() failed with errno=");
			end = appendULL(end, e);
			end = appendText(end, "\n");
			write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
			_exit(1);

		} else {
			// Exit immediately so that child process is adopted by init process.
			_exit(0);
		}

	} else if (child == -1) {
		int e = errno;
		end = state.messageBuf;
		end = appendText(end, state.messagePrefix);
		end = appendText(end, " ] Could fork a child process for dumping diagnostics: fork() failed with errno=");
		end = appendULL(end, e);
		end = appendText(end, "\n");
		write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);

	} else {
		raise(SIGSTOP);
		// Will continue after the child process has done its job.
	}

	// Run default signal handler.
	raise(signo);
}
示例#6
0
// This function is performed in a child process.
static void
dumpDiagnostics(AbortHandlerState &state) {
	char *messageBuf = state.messageBuf;
	char *end;
	pid_t pid;
	int status;

	end = messageBuf;
	end = appendText(end, state.messagePrefix);
	end = appendText(end, " ] Date, uname and ulimits:\n");
	write_nowarn(STDERR_FILENO, messageBuf, end - messageBuf);

	// Dump human-readable time string and string.
	pid = asyncFork();
	if (pid == 0) {
		closeAllFileDescriptors(2, true);
		execlp("date", "date", (const char * const) 0);
		_exit(1);
	} else if (pid == -1) {
		safePrintErr("ERROR: Could not fork a process to dump the time!\n");
	} else if (waitpid(pid, &status, 0) != pid || status != 0) {
		safePrintErr("ERROR: Could not run 'date'!\n");
	}

	// Dump system uname.
	pid = asyncFork();
	if (pid == 0) {
		closeAllFileDescriptors(2, true);
		execlp("uname", "uname", "-mprsv", (const char * const) 0);
		_exit(1);
	} else if (pid == -1) {
		safePrintErr("ERROR: Could not fork a process to dump the uname!\n");
	} else if (waitpid(pid, &status, 0) != pid || status != 0) {
		safePrintErr("ERROR: Could not run 'uname -mprsv'!\n");
	}

	// Dump ulimit.
	pid = asyncFork();
	if (pid == 0) {
		closeAllFileDescriptors(2, true);
		execlp("ulimit", "ulimit", "-a", (const char * const) 0);
		// On Linux 'ulimit' is a shell builtin, not a command.
		execlp("/bin/sh", "/bin/sh", "-c", "ulimit -a", (const char * const) 0);
		_exit(1);
	} else if (pid == -1) {
		safePrintErr("ERROR: Could not fork a process to dump the ulimit!\n");
	} else if (waitpid(pid, &status, 0) != pid || status != 0) {
		safePrintErr("ERROR: Could not run 'ulimit -a'!\n");
	}

	end = messageBuf;
	end = appendText(end, state.messagePrefix);
	end = appendText(end, " ] Phusion Passenger version: " PASSENGER_VERSION "\n");
	write_nowarn(STDERR_FILENO, messageBuf, end - messageBuf);

	if (lastAssertionFailure.filename != NULL) {
		end = messageBuf;
		end = appendText(end, state.messagePrefix);
		end = appendText(end, " ] Last assertion failure: (");
		end = appendText(end, lastAssertionFailure.expression);
		end = appendText(end, "), ");
		if (lastAssertionFailure.function != NULL) {
			end = appendText(end, "function ");
			end = appendText(end, lastAssertionFailure.function);
			end = appendText(end, ", ");
		}
		end = appendText(end, "file ");
		end = appendText(end, lastAssertionFailure.filename);
		end = appendText(end, ", line ");
		end = appendULL(end, lastAssertionFailure.line);
		end = appendText(end, ".\n");
		write_nowarn(STDERR_FILENO, messageBuf, end - messageBuf);
	}

	// It is important that writing the message and the backtrace are two
	// seperate operations because it's not entirely clear whether the
	// latter is async signal safe and thus can crash.
	end = messageBuf;
	end = appendText(end, state.messagePrefix);
	#ifdef LIBC_HAS_BACKTRACE_FUNC
		end = appendText(end, " ] libc backtrace available!\n");
	#else
		end = appendText(end, " ] libc backtrace not available.\n");
	#endif
	write_nowarn(STDERR_FILENO, messageBuf, end - messageBuf);

	#ifdef LIBC_HAS_BACKTRACE_FUNC
		runInSubprocessWithTimeLimit(state, dumpBacktrace, NULL, 4000);
	#endif

	safePrintErr("--------------------------------------\n");

	if (customDiagnosticsDumper != NULL) {
		end = messageBuf;
		end = appendText(end, state.messagePrefix);
		end = appendText(end, " ] Dumping additional diagnostical information...\n");
		write_nowarn(STDERR_FILENO, messageBuf, end - messageBuf);
		safePrintErr("--------------------------------------\n");
		runInSubprocessWithTimeLimit(state, runCustomDiagnosticsDumper, NULL, 2000);
		safePrintErr("--------------------------------------\n");
	}

	dumpFileDescriptorInfo(state);
	safePrintErr("--------------------------------------\n");

	if (shouldDumpWithCrashWatch) {
		end = messageBuf;
		end = appendText(end, state.messagePrefix);
		#ifdef LIBC_HAS_BACKTRACE_FUNC
			end = appendText(end, " ] Dumping a more detailed backtrace with crash-watch...\n");
		#else
			end = appendText(end, " ] Dumping a backtrace with crash-watch...\n");
		#endif
		write_nowarn(STDERR_FILENO, messageBuf, end - messageBuf);
		dumpWithCrashWatch(state);
	} else {
		write_nowarn(STDERR_FILENO, "\n", 1);
	}
}
示例#7
0
	static void
	dumpBacktrace(AbortHandlerState &state, void *userData) {
		void *backtraceStore[512];
		int frames = backtrace(backtraceStore, sizeof(backtraceStore) / sizeof(void *));
		char *end = state.messageBuf;
		end = appendText(end, "--------------------------------------\n");
		end = appendText(end, "[ pid=");
		end = appendULL(end, (unsigned long long) state.pid);
		end = appendText(end, " ] Backtrace with ");
		end = appendULL(end, (unsigned long long) frames);
		end = appendText(end, " frames:\n");
		write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);

		if (backtraceSanitizerCommand != NULL) {
			int p[2];
			if (pipe(p) == -1) {
				int e = errno;
				end = state.messageBuf;
				end = appendText(end, "Could not dump diagnostics through backtrace sanitizer: pipe() failed with errno=");
				end = appendULL(end, e);
				end = appendText(end, "\n");
				end = appendText(end, "Falling back to writing to stderr directly...\n");
				write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
				backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO);
				return;
			}

			pid_t pid = asyncFork();
			if (pid == 0) {
				const char *pidStr = end = state.messageBuf;
				end = appendULL(end, (unsigned long long) state.pid);
				*end = '\0';
				end++;

				close(p[1]);
				dup2(p[0], STDIN_FILENO);
				closeAllFileDescriptors(2, true);
				
				char *command = end;
				end = appendText(end, "exec ");
				end = appendText(end, backtraceSanitizerCommand);
				if (backtraceSanitizerPassProgramInfo) {
					end = appendText(end, " \"");
					end = appendText(end, argv0);
					end = appendText(end, "\" ");
					end = appendText(end, pidStr);
				}
				*end = '\0';
				end++;
				execlp("/bin/sh", "/bin/sh", "-c", command, (const char * const) 0);

				end = state.messageBuf;
				end = appendText(end, "ERROR: cannot execute '");
				end = appendText(end, backtraceSanitizerCommand);
				end = appendText(end, "' for sanitizing the backtrace, trying 'cat'...\n");
				write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
				execlp("cat", "cat", (const char * const) 0);
				execlp("/bin/cat", "cat", (const char * const) 0);
				execlp("/usr/bin/cat", "cat", (const char * const) 0);
				safePrintErr("ERROR: cannot execute 'cat'\n");
				_exit(1);

			} else if (pid == -1) {
				close(p[0]);
				close(p[1]);
				int e = errno;
				end = state.messageBuf;
				end = appendText(end, "Could not dump diagnostics through backtrace sanitizer: fork() failed with errno=");
				end = appendULL(end, e);
				end = appendText(end, "\n");
				end = appendText(end, "Falling back to writing to stderr directly...\n");
				write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
				backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO);

			} else {
				int status = -1;

				close(p[0]);
				backtrace_symbols_fd(backtraceStore, frames, p[1]);
				close(p[1]);
				if (waitpid(pid, &status, 0) == -1 || status != 0) {
					end = state.messageBuf;
					end = appendText(end, "ERROR: cannot execute '");
					end = appendText(end, backtraceSanitizerCommand);
					end = appendText(end, "' for sanitizing the backtrace, writing to stderr directly...\n");
					write_nowarn(STDERR_FILENO, state.messageBuf, end - state.messageBuf);
					backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO);
				}
			}

		} else {
			backtrace_symbols_fd(backtraceStore, frames, STDERR_FILENO);
		}
	}
示例#8
0
    /**
     * Starts the agent process. May throw arbitrary exceptions.
     */
    virtual pid_t start() {
        this_thread::disable_interruption di;
        this_thread::disable_syscall_interruption dsi;
        string exeFilename = getExeFilename();
        SocketPair fds;
        int e, ret;
        pid_t pid;

        /* Create feedback fd for this agent process. We'll send some startup
         * arguments to this agent process through this fd, and we'll receive
         * startup information through it as well.
         */
        fds = createUnixSocketPair();

        pid = syscalls::fork();
        if (pid == 0) {
            // Child

            /* Make sure file descriptor FEEDBACK_FD refers to the newly created
             * feedback fd (fds[1]) and close all other file descriptors.
             * In this child process we don't care about the original FEEDBACK_FD
             * (which is the Watchdog's communication channel to the agents starter.)
             *
             * fds[1] is guaranteed to be != FEEDBACK_FD because the watchdog
             * is started with FEEDBACK_FD already assigned.
             */
            syscalls::close(fds[0]);

            if (syscalls::dup2(fds[1], FEEDBACK_FD) == -1) {
                /* Something went wrong, report error through feedback fd. */
                e = errno;
                try {
                    writeArrayMessage(fds[1],
                                      "system error before exec",
                                      "dup2() failed",
                                      toString(e).c_str(),
                                      NULL);
                    _exit(1);
                } catch (...) {
                    fprintf(stderr, "Passenger Watchdog: dup2() failed: %s (%d)\n",
                            strerror(e), e);
                    fflush(stderr);
                    _exit(1);
                }
            }

            closeAllFileDescriptors(FEEDBACK_FD);

            /* Become the process group leader so that the watchdog can kill the
             * agent as well as all its descendant processes. */
            setpgid(getpid(), getpid());

            setOomScore(oldOomScore);

            try {
                execProgram();
            } catch (...) {
                fprintf(stderr, "PassengerWatchdog: execProgram() threw an exception\n");
                fflush(stderr);
                _exit(1);
            }
            e = errno;
            try {
                writeArrayMessage(FEEDBACK_FD,
                                  "exec error",
                                  toString(e).c_str(),
                                  NULL);
            } catch (...) {
                fprintf(stderr, "Passenger Watchdog: could not execute %s: %s (%d)\n",
                        exeFilename.c_str(), strerror(e), e);
                fflush(stderr);
            }
            _exit(1);
        } else if (pid == -1) {
            // Error
            e = errno;
            throw SystemException("Cannot fork a new process", e);
        } else {
            // Parent
            FileDescriptor feedbackFd = fds[0];
            vector<string> args;

            fds[1].close();
            this_thread::restore_interruption ri(di);
            this_thread::restore_syscall_interruption rsi(dsi);
            ScopeGuard failGuard(boost::bind(killAndWait, pid));

            /* Send startup arguments. Ignore EPIPE and ECONNRESET here
             * because the child process might have sent an feedback message
             * without reading startup arguments.
             */
            try {
                sendStartupArguments(pid, feedbackFd);
            } catch (const SystemException &ex) {
                if (ex.code() != EPIPE && ex.code() != ECONNRESET) {
                    throw SystemException(string("Unable to start the ") + name() +
                                          ": an error occurred while sending startup arguments",
                                          ex.code());
                }
            }

            // Now read its feedback.
            try {
                ret = readArrayMessage(feedbackFd, args);
            } catch (const SystemException &e) {
                if (e.code() == ECONNRESET) {
                    ret = false;
                } else {
                    throw SystemException(string("Unable to start the ") + name() +
                                          ": unable to read its startup information",
                                          e.code());
                }
            }
            if (!ret) {
                this_thread::disable_interruption di2;
                this_thread::disable_syscall_interruption dsi2;
                int status;

                /* The feedback fd was prematurely closed for an unknown reason.
                 * Did the agent process crash?
                 *
                 * We use timedWaitPid() here because if the process crashed
                 * because of an uncaught exception, the file descriptor
                 * might be closed before the process has printed an error
                 * message, so we give it some time to print the error
                 * before we kill it.
                 */
                ret = timedWaitPid(pid, &status, 5000);
                if (ret == 0) {
                    /* Doesn't look like it; it seems it's still running.
                     * We can't do anything without proper feedback so kill
                     * the agent process and throw an exception.
                     */
                    failGuard.runNow();
                    throw RuntimeException(string("Unable to start the ") + name() +
                                           ": it froze and reported an unknown error during its startup");
                } else if (ret != -1 && WIFSIGNALED(status)) {
                    /* Looks like a crash which caused a signal. */
                    throw RuntimeException(string("Unable to start the ") + name() +
                                           ": it seems to have been killed with signal " +
                                           getSignalName(WTERMSIG(status)) + " during startup");
                } else if (ret == -1) {
                    /* Looks like it exited after detecting an error. */
                    throw RuntimeException(string("Unable to start the ") + name() +
                                           ": it seems to have crashed during startup for an unknown reason");
                } else {
                    /* Looks like it exited after detecting an error, but has an exit code. */
                    throw RuntimeException(string("Unable to start the ") + name() +
                                           ": it seems to have crashed during startup for an unknown reason, "
                                           "with exit code " + toString(WEXITSTATUS(status)));
                }
            }

            if (args[0] == "system error before exec") {
                throw SystemException(string("Unable to start the ") + name() +
                                      ": " + args[1], atoi(args[2]));
            } else if (args[0] == "exec error") {
                e = atoi(args[1]);
                if (e == ENOENT) {
                    throw RuntimeException(string("Unable to start the ") + name() +
                                           " because its executable (" + getExeFilename() + ") "
                                           "doesn't exist. This probably means that your "
                                           "Phusion Passenger installation is broken or "
                                           "incomplete. Please reinstall Phusion Passenger");
                } else {
                    throw SystemException(string("Unable to start the ") + name() +
                                          " because exec(\"" + getExeFilename() + "\") failed",
                                          atoi(args[1]));
                }
            } else if (!processStartupInfo(pid, feedbackFd, args)) {
                throw RuntimeException(string("The ") + name() +
                                       " sent an unknown startup info message '" +
                                       args[0] + "'");
            }

            lock_guard<boost::mutex> l(lock);
            this->feedbackFd = feedbackFd;
            this->pid = pid;
            failGuard.clear();
            return pid;
        }
    }
示例#9
0
/**
 * Call another command and get stdout as a result.
 */
inline std::string call(const std::string& cmd, const std::vector<std::string> &args)
{
    cybozu::FileStat stat = cybozu::FilePath(cmd).stat();
    if (!stat.exists()) {
        throw std::runtime_error("command not found:" + cmd);
    }
    if (!stat.isExecutable()) {
        throw std::runtime_error("command not executable:" + cmd);
    }

    Pipe pipe0, pipe1;
    pid_t cpid;
    cpid = ::fork();
    if (cpid < 0) {
        throw std::runtime_error("fork() failed.");
    }
    if (cpid == 0) { /* child process */
        /* bind pipe and stdout. */
        pipe0.closeR();
        pipe0.dupW(1);
        pipe1.closeR();
        pipe1.dupW(2);
        try {
            redirectToNullDev(0);
        } catch (...) {
            ::exit(1);
        }
        closeAllFileDescriptors();
        std::vector<char *> argv = prepareArgv(cmd, args);
        ::execv(cmd.c_str(), &argv[0]);
    }
    /* parent process. */

    /* Read the stdout/stderr of the child process. */
    pipe0.closeW();
    pipe1.closeW();
    std::string stdOutStr, stdErrStr;
    std::exception_ptr epOut, epErr;
    std::thread th0(streamToStr, pipe0.fdR(), std::ref(stdOutStr), std::ref(epOut));
    std::thread th1(streamToStr, pipe1.fdR(), std::ref(stdErrStr), std::ref(epErr));
    th0.join();
    th1.join();

    /* wait for done */
    int status;
    ::waitpid(cpid, &status, 0);

    /* handle errors */
    if (status != 0) {
        std::string msg("child process has returned non-zero:");
        msg += cybozu::util::formatString("%d\n", status);
        msg += "cmd:" + cmd + "\n";
        msg += "args:";
        for (const std::string &arg : args) msg += arg + " ";
        msg += "\nstderr:" + stdErrStr;
        throw std::runtime_error(msg);
    }
    if (epOut) std::rethrow_exception(epOut);
    if (epErr) std::rethrow_exception(epErr);

    return stdOutStr;
}