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 string gupid; 00079 SocketInfoMap serverSockets; 00080 SocketInfo *mainServerSocket; 00081 function<void ()> destructionCallback; 00082 00083 public: 00084 /** 00085 * Construct a new Process object. 00086 * 00087 * @param appRoot The application root of an application. 00088 * This must be a valid directory, but the path does not have to be absolute. 00089 * @param pid The process ID of this application process. 00090 * @param ownerPipe The owner pipe of this application process. 00091 * @param serverSockets All the server sockets that this process listens on. 00092 * There must a server socket with the name 'main'. 00093 * @param detachKey A detach key. Used by the ApplicationPool algorithm. 00094 * @param connectPassword The password to use when connecting to this process. 00095 * Must be valid ASCII. 00096 * @param gupid A string which uniquely identifies this process. 00097 * @param destructionCallback A callback to be called when this Process is destroyed. 00098 * @throws ArgumentException If serverSockets has no socket named 'main'. 00099 */ 00100 Process(const string &appRoot, pid_t pid, int ownerPipe, const SocketInfoMap &serverSockets, 00101 const string &detachKey, const string &connectPassword, const string &gupid, 00102 const function<void ()> &destructionCallback = function<void ()>()) 00103 { 00104 this->appRoot = appRoot; 00105 this->pid = pid; 00106 this->ownerPipe = ownerPipe; 00107 this->serverSockets = serverSockets; 00108 this->detachKey = detachKey; 00109 this->connectPassword = connectPassword; 00110 this->gupid = gupid; 00111 this->destructionCallback = destructionCallback; 00112 if (serverSockets.find("main") == serverSockets.end()) { 00113 TRACE_POINT(); 00114 throw ArgumentException("There must be a server socket named 'main'."); 00115 } 00116 mainServerSocket = &this->serverSockets["main"]; 00117 P_TRACE(3, "Application process " << pid << " (" << this << "): created."); 00118 } 00119 00120 virtual ~Process() { 00121 TRACE_POINT(); 00122 SocketInfoMap::const_iterator it; 00123 int ret; 00124 00125 if (ownerPipe != -1) { 00126 do { 00127 ret = close(ownerPipe); 00128 } while (ret == -1 && errno == EINTR); 00129 } 00130 for (it = serverSockets.begin(); it != serverSockets.end(); it++) { 00131 const SocketInfo &info = it->second; 00132 if (info.type == "unix") { 00133 do { 00134 ret = unlink(info.address.c_str()); 00135 } while (ret == -1 && errno == EINTR); 00136 } 00137 } 00138 P_TRACE(3, "Application process " << pid << " (" << this << "): destroyed."); 00139 00140 if (destructionCallback) { 00141 destructionCallback(); 00142 } 00143 } 00144 00145 /** 00146 * Returns the application root for this application process. See 00147 * the constructor for information about the application root. 00148 */ 00149 string getAppRoot() const { 00150 return appRoot; 00151 } 00152 00153 /** 00154 * Returns the process ID of this application process. 00155 */ 00156 pid_t getPid() const { 00157 return pid; 00158 } 00159 00160 /** 00161 * Returns this process's detach key. 00162 */ 00163 string getDetachKey() const { 00164 return detachKey; 00165 } 00166 00167 /** 00168 * Returns this process's connect password. This password is 00169 * guaranteed to be valid ASCII. 00170 */ 00171 string getConnectPassword() const { 00172 return connectPassword; 00173 } 00174 00175 /** 00176 * Returns this process's gupid. This is like a PID, but does not rotate 00177 * and is even unique over multiple servers. 00178 */ 00179 string getGupid() const { 00180 return gupid; 00181 } 00182 00183 /** 00184 * Returns a map containing all server sockets that this process 00185 * listens on. 00186 */ 00187 const SocketInfoMap *getServerSockets() const { 00188 return &serverSockets; 00189 } 00190 00191 /** 00192 * Request a new session from this application process by connecting to its 00193 * main server socket. This session represents the life time of a single 00194 * request/response pair, and can be used to send the request data to the 00195 * application process, as well as receiving the response data. 00196 * 00197 * The use of connect() is demonstrated in the following example. 00198 * @code 00199 * // Request a new session from the process. 00200 * SessionPtr session = process->newSession(...); 00201 * 00202 * // Send the request headers and request body data. 00203 * session->sendHeaders(...); 00204 * session->sendBodyBlock(...); 00205 * // Done sending data, so we close the writer channel. 00206 * session->shutdownWriter(); 00207 * 00208 * // Now read the HTTP response. 00209 * string responseData = readAllDataFromSocket(session->getReader()); 00210 * // Done reading data, so we close the reader channel. 00211 * session->shutdownReader(); 00212 * 00213 * // This session has now finished, so we close the session by resetting 00214 * // the smart pointer to NULL (thereby destroying the Session object). 00215 * session.reset(); 00216 * 00217 * // We can connect to a Process multiple times. Just make sure 00218 * // the previous session is closed. 00219 * session = process->newSession(...); 00220 * @endcode 00221 * 00222 * You <b>must</b> close a session when you no longer need it. If you 00223 * call connect() without having properly closed a previous session, 00224 * you might cause a deadlock because the application process may be 00225 * waiting for you to close the previous session. 00226 * 00227 * @param closeCallback A function which will be called when the session has been closed. 00228 * @param initiateNow Whether the session should be initiated immediately. 00229 * If set to false then you must call <tt>initiate()</tt> on 00230 * the session before it's usable. 00231 * @return A smart pointer to a Session object, which represents the created session. 00232 * @post result->initiated() == initiateNow 00233 * @throws SystemException Something went wrong during session initiation. 00234 * @throws IOException Something went wrong during session initiation. 00235 * @throws boost::thread_interrupted 00236 */ 00237 SessionPtr newSession(const StandardSession::CloseCallback &closeCallback = StandardSession::CloseCallback(), 00238 bool initiateNow = true) 00239 { 00240 SessionPtr session(new StandardSession(pid, closeCallback, 00241 mainServerSocket->type, mainServerSocket->address, 00242 detachKey, connectPassword, gupid)); 00243 if (initiateNow) { 00244 session->initiate(); 00245 } 00246 return session; 00247 } 00248 }; 00249 00250 /** Convenient alias for Process smart pointer. */ 00251 typedef shared_ptr<Process> ProcessPtr; 00252 00253 } // namespace Passenger 00254 00255 #endif /* _PASSENGER_PROCESS_H_ */