/* * * Copyright (c) 2006 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include "qpid/sys/Fork.h" #include "qpid/log/Statement.h" #include "qpid/Exception.h" #include #include #include #include #include #include #include #include namespace qpid { namespace sys { using namespace std; namespace { void writeStr(int fd, const std::string& str) { const char* WRITE_ERR = "Error writing to parent process"; int size = str.size(); if (int(sizeof(size)) > ::write(fd, &size, sizeof(size))) throw ErrnoException(WRITE_ERR); if (size > ::write(fd, str.data(), size)) throw ErrnoException(WRITE_ERR); } string readStr(int fd) { string value; const char* READ_ERR = "Error reading from forked process"; int size; if (int(sizeof(size)) > ::read(fd, &size, sizeof(size))) throw ErrnoException(READ_ERR); if (size > 0) { // Read string message value.resize(size); if (size > ::read(fd, const_cast(value.data()), size)) throw ErrnoException(READ_ERR); } return value; } } // namespace Fork::Fork() {} Fork::~Fork() {} void Fork::fork() { pid_t pid = ::fork(); if (pid < 0) throw ErrnoException("Failed to fork the process"); if (pid == 0) child(); else parent(pid); } ForkWithMessage::ForkWithMessage() { pipeFds[0] = pipeFds[1] = -1; } struct AutoCloseFd { int fd; AutoCloseFd(int d) : fd(d) {} ~AutoCloseFd() { ::close(fd); } }; void ForkWithMessage::fork() { if(::pipe(pipeFds) < 0) throw ErrnoException("Can't create pipe"); pid_t pid = ::fork(); if(pid < 0) throw ErrnoException("Fork fork failed"); if (pid == 0) { // Child AutoCloseFd ac(pipeFds[1]); // Write side. ::close(pipeFds[0]); // Read side try { child(); } catch (const std::exception& e) { QPID_LOG(error, "Error in forked child: " << e.what()); std::string msg = e.what(); if (msg.empty()) msg = " "; // Make sure we send a non-empty error string. writeStr(pipeFds[1], msg); } } else { // Parent close(pipeFds[1]); // Write side. AutoCloseFd ac(pipeFds[0]); // Read side parent(pid); } } string ForkWithMessage::wait(int timeout) { // parent waits for child. errno = 0; struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = 0; fd_set fds; FD_ZERO(&fds); FD_SET(pipeFds[0], &fds); int n=select(FD_SETSIZE, &fds, 0, 0, &tv); if(n<0) throw ErrnoException("Error waiting for fork"); if (n==0) throw Exception("Timed out waiting for fork"); string error = readStr(pipeFds[0]); if (error.empty()) return readStr(pipeFds[0]); else throw Exception("Error in forked process: " + error); } // Write empty error string followed by value string to pipe. void ForkWithMessage::ready(const string& value) { // child // Write empty string for error followed by value. writeStr(pipeFds[1], string()); // No error writeStr(pipeFds[1], value); } }} // namespace qpid::sys