00001 /* 00002 * Phusion Passenger - http://www.modrails.com/ 00003 * Copyright (c) 2010 Phusion 00004 * 00005 * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. 00006 * 00007 * Permission is hereby granted, free of charge, to any person obtaining a copy 00008 * of this software and associated documentation files (the "Software"), to deal 00009 * in the Software without restriction, including without limitation the rights 00010 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 00011 * copies of the Software, and to permit persons to whom the Software is 00012 * furnished to do so, subject to the following conditions: 00013 * 00014 * The above copyright notice and this permission notice shall be included in 00015 * all copies or substantial portions of the Software. 00016 * 00017 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00020 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00022 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 00023 * THE SOFTWARE. 00024 */ 00025 #ifndef _PASSENGER_PROCESS_H_ 00026 #define _PASSENGER_PROCESS_H_ 00027 00028 #include <boost/shared_ptr.hpp> 00029 #include <boost/function.hpp> 00030 #include <oxt/system_calls.hpp> 00031 #include <oxt/backtrace.hpp> 00032 #include <string> 00033 #include <map> 00034 00035 #include <sys/types.h> 00036 #include <unistd.h> 00037 #include <errno.h> 00038 00039 #include "Session.h" 00040 #include "MessageChannel.h" 00041 #include "Exceptions.h" 00042 #include "Logging.h" 00043 #include "Utils.h" 00044 00045 namespace Passenger { 00046 00047 using namespace std; 00048 using namespace boost; 00049 00050 /** 00051 * Represents a single application process, as spawned by SpawnManager 00052 * or by ApplicationPool::Interface::get(). 00053 * 00054 * @ingroup Support 00055 */ 00056 class Process { 00057 public: 00058 struct SocketInfo { 00059 string address; 00060 string type; 00061 00062 SocketInfo() {} 00063 00064 SocketInfo(const string &address, const string &type) { 00065 this->address = address; 00066 this->type = type; 00067 } 00068 }; 00069 00070 typedef map<string, SocketInfo> SocketInfoMap; 00071 00072 private: 00073 string appRoot; 00074 pid_t pid; 00075 int ownerPipe; 00076 string detachKey; 00077 string connectPassword; 00078 SocketInfoMap serverSockets; 00079 SocketInfo *mainServerSocket; 00080 function<void ()> destructionCallback; 00081 00082 public: 00083 /** 00084 * Construct a new Process object. 00085 * 00086 * @param appRoot The application root of an application. 00087 * This must be a valid directory, but the path does not have to be absolute. 00088 * @param pid The process ID of this application process. 00089 * @param ownerPipe The owner pipe of this application process. 00090 * @param serverSockets All the server sockets that this process listens on. 00091 * There must a server socket with the name 'main'. 00092 * @param detachKey A detach key. Used by the ApplicationPool algorithm. 00093 * @param connectPassword The password to use when connecting to this process. 00094 * Must be valid ASCII. 00095 * @param destructionCallback A callback to be called when this Process is destroyed. 00096 * @throws ArgumentException If serverSockets has no socket named 'main'. 00097 */ 00098 Process(const string &appRoot, pid_t pid, int ownerPipe, const SocketInfoMap &serverSockets, 00099 const string &detachKey, const string &connectPassword, 00100 const function<void ()> &destructionCallback = function<void ()>()) 00101 { 00102 this->appRoot = appRoot; 00103 this->pid = pid; 00104 this->ownerPipe = ownerPipe; 00105 this->serverSockets = serverSockets; 00106 this->detachKey = detachKey; 00107 this->connectPassword = connectPassword; 00108 this->destructionCallback = destructionCallback; 00109 if (serverSockets.find("main") == serverSockets.end()) { 00110 TRACE_POINT(); 00111 throw ArgumentException("There must be a server socket named 'main'."); 00112 } 00113 mainServerSocket = &this->serverSockets["main"]; 00114 P_TRACE(3, "Application process " << this << ": created."); 00115 } 00116 00117 virtual ~Process() { 00118 TRACE_POINT(); 00119 SocketInfoMap::const_iterator it; 00120 int ret; 00121 00122 if (ownerPipe != -1) { 00123 do { 00124 ret = close(ownerPipe); 00125 } while (ret == -1 && errno == EINTR); 00126 } 00127 for (it = serverSockets.begin(); it != serverSockets.end(); it++) { 00128 const SocketInfo &info = it->second; 00129 if (info.type == "unix") { 00130 do { 00131 ret = unlink(info.address.c_str()); 00132 } while (ret == -1 && errno == EINTR); 00133 } 00134 } 00135 P_TRACE(3, "Application process " << this << ": destroyed."); 00136 00137 if (destructionCallback) { 00138 destructionCallback(); 00139 } 00140 } 00141 00142 /** 00143 * Returns the application root for this application process. See 00144 * the constructor for information about the application root. 00145 */ 00146 string getAppRoot() const { 00147 return appRoot; 00148 } 00149 00150 /** 00151 * Returns the process ID of this application process. 00152 */ 00153 pid_t getPid() const { 00154 return pid; 00155 } 00156 00157 /** 00158 * Returns this process's detach key. 00159 */ 00160 string getDetachKey() const { 00161 return detachKey; 00162 } 00163 00164 /** 00165 * Returns this process's connect password. This password is 00166 * guaranteed to be valid ASCII. 00167 */ 00168 string getConnectPassword() const { 00169 return connectPassword; 00170 } 00171 00172 /** 00173 * Returns a map containing all server sockets that this process 00174 * listens on. 00175 */ 00176 const SocketInfoMap *getServerSockets() const { 00177 return &serverSockets; 00178 } 00179 00180 /** 00181 * Request a new session from this application process by connecting to its 00182 * main server socket. This session represents the life time of a single 00183 * request/response pair, and can be used to send the request data to the 00184 * application process, as well as receiving the response data. 00185 * 00186 * The use of connect() is demonstrated in the following example. 00187 * @code 00188 * // Request a new session from the process. 00189 * SessionPtr session = process->newSession(...); 00190 * 00191 * // Send the request headers and request body data. 00192 * session->sendHeaders(...); 00193 * session->sendBodyBlock(...); 00194 * // Done sending data, so we close the writer channel. 00195 * session->shutdownWriter(); 00196 * 00197 * // Now read the HTTP response. 00198 * string responseData = readAllDataFromSocket(session->getReader()); 00199 * // Done reading data, so we close the reader channel. 00200 * session->shutdownReader(); 00201 * 00202 * // This session has now finished, so we close the session by resetting 00203 * // the smart pointer to NULL (thereby destroying the Session object). 00204 * session.reset(); 00205 * 00206 * // We can connect to a Process multiple times. Just make sure 00207 * // the previous session is closed. 00208 * session = process->newSession(...); 00209 * @endcode 00210 * 00211 * You <b>must</b> close a session when you no longer need it. If you 00212 * call connect() without having properly closed a previous session, 00213 * you might cause a deadlock because the application process may be 00214 * waiting for you to close the previous session. 00215 * 00216 * @param closeCallback A function which will be called when the session has been closed. 00217 * @param initiateNow Whether the session should be initiated immediately. 00218 * If set to false then you must call <tt>initiate()</tt> on 00219 * the session before it's usable. 00220 * @return A smart pointer to a Session object, which represents the created session. 00221 * @post result->initiated() == initiateNow 00222 * @throws SystemException Something went wrong during session initiation. 00223 * @throws IOException Something went wrong during session initiation. 00224 * @throws boost::thread_interrupted 00225 */ 00226 SessionPtr newSession(const function<void()> &closeCallback = function<void()>(), 00227 bool initiateNow = true) 00228 { 00229 SessionPtr session(new StandardSession(pid, closeCallback, 00230 mainServerSocket->type, mainServerSocket->address, 00231 detachKey, connectPassword)); 00232 if (initiateNow) { 00233 session->initiate(); 00234 } 00235 return session; 00236 } 00237 }; 00238 00239 /** Convenient alias for Process smart pointer. */ 00240 typedef shared_ptr<Process> ProcessPtr; 00241 00242 } // namespace Passenger 00243 00244 #endif /* _PASSENGER_PROCESS_H_ */