00001 /* 00002 * Phusion Passenger - http://www.modrails.com/ 00003 * Copyright (C) 2008 Phusion 00004 * 00005 * Phusion Passenger is a trademark of Hongli Lai & Ninh Bui. 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; version 2 of the License. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License along 00017 * with this program; if not, write to the Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00019 */ 00020 #ifndef _PASSENGER_MESSAGE_CHANNEL_H_ 00021 #define _PASSENGER_MESSAGE_CHANNEL_H_ 00022 00023 #include <oxt/system_calls.hpp> 00024 00025 #include <algorithm> 00026 #include <string> 00027 #include <list> 00028 #include <vector> 00029 00030 #include <sys/types.h> 00031 #include <sys/socket.h> 00032 #include <arpa/inet.h> 00033 #include <errno.h> 00034 #include <unistd.h> 00035 #include <cstdarg> 00036 #ifdef __OpenBSD__ 00037 // OpenBSD needs this for 'struct iovec'. Apparently it isn't 00038 // always included by unistd.h and sys/types.h. 00039 #include <sys/uio.h> 00040 #endif 00041 #if !APR_HAVE_IOVEC 00042 // We don't want apr_want.h to redefine 'struct iovec'. 00043 // http://tinyurl.com/b6aatw 00044 #undef APR_HAVE_IOVEC 00045 #define APR_HAVE_IOVEC 1 00046 #endif 00047 00048 #include "Exceptions.h" 00049 #include "Utils.h" 00050 00051 namespace Passenger { 00052 00053 using namespace std; 00054 using namespace oxt; 00055 00056 /** 00057 * Convenience class for I/O operations on file descriptors. 00058 * 00059 * This class provides convenience methods for: 00060 * - sending and receiving raw data over a file descriptor. 00061 * - sending and receiving messages over a file descriptor. 00062 * - file descriptor passing over a Unix socket. 00063 * All of these methods use exceptions for error reporting. 00064 * 00065 * There are two kinds of messages: 00066 * - Array messages. These are just a list of strings, and the message 00067 * itself has a specific length. The contained strings may not 00068 * contain NUL characters (<tt>'\\0'</tt>). Note that an array message 00069 * must have at least one element. 00070 * - Scalar messages. These are byte strings which may contain arbitrary 00071 * binary data. Scalar messages also have a specific length. 00072 * The protocol is designed to be low overhead, easy to implement and 00073 * easy to parse. 00074 * 00075 * MessageChannel is to be wrapped around a file descriptor. For example: 00076 * @code 00077 * int p[2]; 00078 * pipe(p); 00079 * MessageChannel channel1(p[0]); 00080 * MessageChannel channel2(p[1]); 00081 * 00082 * // Send an array message. 00083 * channel2.write("hello", "world !!", NULL); 00084 * list<string> args; 00085 * channel1.read(args); // args now contains { "hello", "world !!" } 00086 * 00087 * // Send a scalar message. 00088 * channel2.writeScalar("some long string which can contain arbitrary binary data"); 00089 * string str; 00090 * channel1.readScalar(str); 00091 * @endcode 00092 * 00093 * The life time of a MessageChannel is independent from that of the 00094 * wrapped file descriptor. If a MessageChannel object is destroyed, 00095 * the file descriptor is not automatically closed. Call close() 00096 * if you want to close the file descriptor. 00097 * 00098 * @note I/O operations are not buffered. 00099 * @note Be careful with mixing the sending/receiving of array messages, 00100 * scalar messages and file descriptors. If you send a collection of any 00101 * of these in a specific order, then the receiving side must receive them 00102 * in the exact some order. So suppose you first send a message, then a 00103 * file descriptor, then a scalar, then the receiving side must first 00104 * receive a message, then a file descriptor, then a scalar. If the 00105 * receiving side does things in the wrong order then bad things will 00106 * happen. 00107 * @note MessageChannel is not thread-safe, but is reentrant. 00108 * 00109 * @ingroup Support 00110 */ 00111 class MessageChannel { 00112 private: 00113 const static char DELIMITER = '\0'; 00114 int fd; 00115 00116 #ifdef __OpenBSD__ 00117 typedef u_int32_t uint32_t; 00118 typedef u_int16_t uint16_t; 00119 #endif 00120 00121 public: 00122 /** 00123 * Construct a new MessageChannel with no underlying file descriptor. 00124 * Thus the resulting MessageChannel object will not be usable. 00125 * This constructor exists to allow one to declare an "empty" 00126 * MessageChannel variable which is to be initialized later. 00127 */ 00128 MessageChannel() { 00129 this->fd = -1; 00130 } 00131 00132 /** 00133 * Construct a new MessageChannel with the given file descriptor. 00134 */ 00135 MessageChannel(int fd) { 00136 this->fd = fd; 00137 } 00138 00139 /** 00140 * Close the underlying file descriptor. If this method is called multiple 00141 * times, the file descriptor will only be closed the first time. 00142 * 00143 * @throw SystemException 00144 * @throw boost::thread_interrupted 00145 */ 00146 void close() { 00147 if (fd != -1) { 00148 int ret = syscalls::close(fd); 00149 if (ret == -1) { 00150 throw SystemException("Cannot close file descriptor", errno); 00151 } 00152 fd = -1; 00153 } 00154 } 00155 00156 /** 00157 * Send an array message, which consists of the given elements, over the underlying 00158 * file descriptor. 00159 * 00160 * @param args An object which contains the message elements. This object must 00161 * support STL-style iteration, and each iterator must have an 00162 * std::string as value. Use the StringArrayType and 00163 * StringArrayConstIteratorType template parameters to specify the exact type names. 00164 * @throws SystemException An error occured while writing the data to the file descriptor. 00165 * @throws boost::thread_interrupted 00166 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00167 * @see read(), write(const char *, ...) 00168 */ 00169 template<typename StringArrayType, typename StringArrayConstIteratorType> 00170 void write(const StringArrayType &args) { 00171 StringArrayConstIteratorType it; 00172 string data; 00173 uint16_t dataSize = 0; 00174 00175 for (it = args.begin(); it != args.end(); it++) { 00176 dataSize += it->size() + 1; 00177 } 00178 data.reserve(dataSize + sizeof(dataSize)); 00179 dataSize = htons(dataSize); 00180 data.append((const char *) &dataSize, sizeof(dataSize)); 00181 for (it = args.begin(); it != args.end(); it++) { 00182 data.append(*it); 00183 data.append(1, DELIMITER); 00184 } 00185 00186 writeRaw(data); 00187 } 00188 00189 /** 00190 * Send an array message, which consists of the given elements, over the underlying 00191 * file descriptor. 00192 * 00193 * @param args The message elements. 00194 * @throws SystemException An error occured while writing the data to the file descriptor. 00195 * @throws boost::thread_interrupted 00196 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00197 * @see read(), write(const char *, ...) 00198 */ 00199 void write(const list<string> &args) { 00200 write<list<string>, list<string>::const_iterator>(args); 00201 } 00202 00203 /** 00204 * Send an array message, which consists of the given elements, over the underlying 00205 * file descriptor. 00206 * 00207 * @param args The message elements. 00208 * @throws SystemException An error occured while writing the data to the file descriptor. 00209 * @throws boost::thread_interrupted 00210 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00211 * @see read(), write(const char *, ...) 00212 */ 00213 void write(const vector<string> &args) { 00214 write<vector<string>, vector<string>::const_iterator>(args); 00215 } 00216 00217 /** 00218 * Send an array message, which consists of the given strings, over the underlying 00219 * file descriptor. 00220 * 00221 * @param name The first element of the message to send. 00222 * @param ... Other elements of the message. These *must* be strings, i.e. of type char*. 00223 * It is also required to terminate this list with a NULL. 00224 * @throws SystemException An error occured while writing the data to the file descriptor. 00225 * @throws boost::thread_interrupted 00226 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00227 * @see read(), write(const list<string> &) 00228 */ 00229 void write(const char *name, ...) { 00230 list<string> args; 00231 args.push_back(name); 00232 00233 va_list ap; 00234 va_start(ap, name); 00235 while (true) { 00236 const char *arg = va_arg(ap, const char *); 00237 if (arg == NULL) { 00238 break; 00239 } else { 00240 args.push_back(arg); 00241 } 00242 } 00243 va_end(ap); 00244 write(args); 00245 } 00246 00247 /** 00248 * Send a scalar message over the underlying file descriptor. 00249 * 00250 * @param str The scalar message's content. 00251 * @throws SystemException An error occured while writing the data to the file descriptor. 00252 * @throws boost::thread_interrupted 00253 * @see readScalar(), writeScalar(const char *, unsigned int) 00254 */ 00255 void writeScalar(const string &str) { 00256 writeScalar(str.c_str(), str.size()); 00257 } 00258 00259 /** 00260 * Send a scalar message over the underlying file descriptor. 00261 * 00262 * @param data The scalar message's content. 00263 * @param size The number of bytes in <tt>data</tt>. 00264 * @pre <tt>data != NULL</tt> 00265 * @throws SystemException An error occured while writing the data to the file descriptor. 00266 * @throws boost::thread_interrupted 00267 * @see readScalar(), writeScalar(const string &) 00268 */ 00269 void writeScalar(const char *data, unsigned int size) { 00270 uint32_t l = htonl(size); 00271 writeRaw((const char *) &l, sizeof(uint32_t)); 00272 writeRaw(data, size); 00273 } 00274 00275 /** 00276 * Send a block of data over the underlying file descriptor. 00277 * This method blocks until everything is sent. 00278 * 00279 * @param data The data to send. 00280 * @param size The number of bytes in <tt>data</tt>. 00281 * @pre <tt>data != NULL</tt> 00282 * @throws SystemException An error occured while writing the data to the file descriptor. 00283 * @throws boost::thread_interrupted 00284 * @see readRaw() 00285 */ 00286 void writeRaw(const char *data, unsigned int size) { 00287 ssize_t ret; 00288 unsigned int written = 0; 00289 do { 00290 ret = syscalls::write(fd, data + written, size - written); 00291 if (ret == -1) { 00292 throw SystemException("write() failed", errno); 00293 } else { 00294 written += ret; 00295 } 00296 } while (written < size); 00297 } 00298 00299 /** 00300 * Send a block of data over the underlying file descriptor. 00301 * This method blocks until everything is sent. 00302 * 00303 * @param data The data to send. 00304 * @pre <tt>data != NULL</tt> 00305 * @throws SystemException An error occured while writing the data to the file descriptor. 00306 * @throws boost::thread_interrupted 00307 */ 00308 void writeRaw(const string &data) { 00309 writeRaw(data.c_str(), data.size()); 00310 } 00311 00312 /** 00313 * Pass a file descriptor. This only works if the underlying file 00314 * descriptor is a Unix socket. 00315 * 00316 * @param fileDescriptor The file descriptor to pass. 00317 * @throws SystemException Something went wrong during file descriptor passing. 00318 * @throws boost::thread_interrupted 00319 * @pre <tt>fileDescriptor >= 0</tt> 00320 * @see readFileDescriptor() 00321 */ 00322 void writeFileDescriptor(int fileDescriptor) { 00323 struct msghdr msg; 00324 struct iovec vec; 00325 char dummy[1]; 00326 #if defined(__APPLE__) || defined(__SOLARIS__) 00327 struct { 00328 struct cmsghdr header; 00329 int fd; 00330 } control_data; 00331 #else 00332 char control_data[CMSG_SPACE(sizeof(int))]; 00333 #endif 00334 struct cmsghdr *control_header; 00335 int ret; 00336 00337 msg.msg_name = NULL; 00338 msg.msg_namelen = 0; 00339 00340 /* Linux and Solaris require msg_iov to be non-NULL. */ 00341 dummy[0] = '\0'; 00342 vec.iov_base = dummy; 00343 vec.iov_len = sizeof(dummy); 00344 msg.msg_iov = &vec; 00345 msg.msg_iovlen = 1; 00346 00347 msg.msg_control = (caddr_t) &control_data; 00348 msg.msg_controllen = sizeof(control_data); 00349 msg.msg_flags = 0; 00350 00351 control_header = CMSG_FIRSTHDR(&msg); 00352 control_header->cmsg_level = SOL_SOCKET; 00353 control_header->cmsg_type = SCM_RIGHTS; 00354 #if defined(__APPLE__) || defined(__SOLARIS__) 00355 control_header->cmsg_len = sizeof(control_data); 00356 control_data.fd = fileDescriptor; 00357 #else 00358 control_header->cmsg_len = CMSG_LEN(sizeof(int)); 00359 memcpy(CMSG_DATA(control_header), &fileDescriptor, sizeof(int)); 00360 #endif 00361 00362 ret = syscalls::sendmsg(fd, &msg, 0); 00363 if (ret == -1) { 00364 throw SystemException("Cannot send file descriptor with sendmsg()", errno); 00365 } 00366 } 00367 00368 /** 00369 * Read an array message from the underlying file descriptor. 00370 * 00371 * @param args The message will be put in this variable. 00372 * @return Whether end-of-file has been reached. If so, then the contents 00373 * of <tt>args</tt> will be undefined. 00374 * @throws SystemException If an error occured while receiving the message. 00375 * @throws boost::thread_interrupted 00376 * @see write() 00377 */ 00378 bool read(vector<string> &args) { 00379 uint16_t size; 00380 int ret; 00381 unsigned int alreadyRead = 0; 00382 00383 do { 00384 ret = syscalls::read(fd, (char *) &size + alreadyRead, sizeof(size) - alreadyRead); 00385 if (ret == -1) { 00386 throw SystemException("read() failed", errno); 00387 } else if (ret == 0) { 00388 return false; 00389 } 00390 alreadyRead += ret; 00391 } while (alreadyRead < sizeof(size)); 00392 size = ntohs(size); 00393 00394 string buffer; 00395 args.clear(); 00396 buffer.reserve(size); 00397 while (buffer.size() < size) { 00398 char tmp[1024 * 8]; 00399 ret = syscalls::read(fd, tmp, min(size - buffer.size(), sizeof(tmp))); 00400 if (ret == -1) { 00401 throw SystemException("read() failed", errno); 00402 } else if (ret == 0) { 00403 return false; 00404 } 00405 buffer.append(tmp, ret); 00406 } 00407 00408 if (!buffer.empty()) { 00409 string::size_type start = 0, pos; 00410 const string &const_buffer(buffer); 00411 while ((pos = const_buffer.find('\0', start)) != string::npos) { 00412 args.push_back(const_buffer.substr(start, pos - start)); 00413 start = pos + 1; 00414 } 00415 } 00416 return true; 00417 } 00418 00419 /** 00420 * Read a scalar message from the underlying file descriptor. 00421 * 00422 * @param output The message will be put in here. 00423 * @returns Whether end-of-file was reached during reading. 00424 * @throws SystemException An error occured while writing the data to the file descriptor. 00425 * @throws boost::thread_interrupted 00426 * @see writeScalar() 00427 */ 00428 bool readScalar(string &output) { 00429 uint32_t size; 00430 unsigned int remaining; 00431 00432 if (!readRaw(&size, sizeof(uint32_t))) { 00433 return false; 00434 } 00435 size = ntohl(size); 00436 00437 output.clear(); 00438 output.reserve(size); 00439 remaining = size; 00440 while (remaining > 0) { 00441 char buf[1024 * 32]; 00442 unsigned int blockSize = min((unsigned int) sizeof(buf), remaining); 00443 00444 if (!readRaw(buf, blockSize)) { 00445 return false; 00446 } 00447 output.append(buf, blockSize); 00448 remaining -= blockSize; 00449 } 00450 return true; 00451 } 00452 00453 /** 00454 * Read exactly <tt>size</tt> bytes of data from the underlying file descriptor, 00455 * and put the result in <tt>buf</tt>. If end-of-file has been reached, or if 00456 * end-of-file was encountered before <tt>size</tt> bytes have been read, then 00457 * <tt>false</tt> will be returned. Otherwise (i.e. if the read was successful), 00458 * <tt>true</tt> will be returned. 00459 * 00460 * @param buf The buffer to place the read data in. This buffer must be at least 00461 * <tt>size</tt> bytes long. 00462 * @param size The number of bytes to read. 00463 * @return Whether reading was successful or whether EOF was reached. 00464 * @pre buf != NULL 00465 * @throws SystemException Something went wrong during reading. 00466 * @throws boost::thread_interrupted 00467 * @see writeRaw() 00468 */ 00469 bool readRaw(void *buf, unsigned int size) { 00470 ssize_t ret; 00471 unsigned int alreadyRead = 0; 00472 00473 while (alreadyRead < size) { 00474 ret = syscalls::read(fd, (char *) buf + alreadyRead, size - alreadyRead); 00475 if (ret == -1) { 00476 throw SystemException("read() failed", errno); 00477 } else if (ret == 0) { 00478 return false; 00479 } else { 00480 alreadyRead += ret; 00481 } 00482 } 00483 return true; 00484 } 00485 00486 /** 00487 * Receive a file descriptor, which had been passed over the underlying 00488 * file descriptor. 00489 * 00490 * @return The passed file descriptor. 00491 * @throws SystemException If something went wrong during the 00492 * receiving of a file descriptor. Perhaps the underlying 00493 * file descriptor isn't a Unix socket. 00494 * @throws IOException Whatever was received doesn't seem to be a 00495 * file descriptor. 00496 * @throws boost::thread_interrupted 00497 */ 00498 int readFileDescriptor() { 00499 struct msghdr msg; 00500 struct iovec vec; 00501 char dummy[1]; 00502 #if defined(__APPLE__) || defined(__SOLARIS__) 00503 // File descriptor passing macros (CMSG_*) seem to be broken 00504 // on 64-bit MacOS X. This structure works around the problem. 00505 struct { 00506 struct cmsghdr header; 00507 int fd; 00508 } control_data; 00509 #define EXPECTED_CMSG_LEN sizeof(control_data) 00510 #else 00511 char control_data[CMSG_SPACE(sizeof(int))]; 00512 #define EXPECTED_CMSG_LEN CMSG_LEN(sizeof(int)) 00513 #endif 00514 struct cmsghdr *control_header; 00515 int ret; 00516 00517 msg.msg_name = NULL; 00518 msg.msg_namelen = 0; 00519 00520 dummy[0] = '\0'; 00521 vec.iov_base = dummy; 00522 vec.iov_len = sizeof(dummy); 00523 msg.msg_iov = &vec; 00524 msg.msg_iovlen = 1; 00525 00526 msg.msg_control = (caddr_t) &control_data; 00527 msg.msg_controllen = sizeof(control_data); 00528 msg.msg_flags = 0; 00529 00530 ret = syscalls::recvmsg(fd, &msg, 0); 00531 if (ret == -1) { 00532 throw SystemException("Cannot read file descriptor with recvmsg()", errno); 00533 } 00534 00535 control_header = CMSG_FIRSTHDR(&msg); 00536 if (control_header->cmsg_len != EXPECTED_CMSG_LEN 00537 || control_header->cmsg_level != SOL_SOCKET 00538 || control_header->cmsg_type != SCM_RIGHTS) { 00539 throw IOException("No valid file descriptor received."); 00540 } 00541 #if defined(__APPLE__) || defined(__SOLARIS__) 00542 return control_data.fd; 00543 #else 00544 return *((int *) CMSG_DATA(control_header)); 00545 #endif 00546 } 00547 00548 /** 00549 * Set the timeout value for reading data from this channel. 00550 * If no data can be read within the timeout period, then a 00551 * SystemException will be thrown by one of the read methods, 00552 * with error code EAGAIN or EWOULDBLOCK. 00553 * 00554 * @param msec The timeout, in milliseconds. If 0 is given, 00555 * there will be no timeout. 00556 * @throws SystemException Cannot set the timeout. 00557 */ 00558 void setReadTimeout(unsigned int msec) { 00559 // See the comment for setWriteTimeout(). 00560 struct timeval tv; 00561 int ret; 00562 00563 tv.tv_sec = msec / 1000; 00564 tv.tv_usec = msec % 1000 * 1000; 00565 ret = syscalls::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, 00566 &tv, sizeof(tv)); 00567 #ifndef __SOLARIS__ 00568 // SO_RCVTIMEO is unimplemented and retuns an error on Solaris 00569 // 9 and 10 SPARC. Seems to work okay without it. 00570 if (ret == -1) { 00571 throw SystemException("Cannot set read timeout for socket", errno); 00572 } 00573 #endif 00574 } 00575 00576 /** 00577 * Set the timeout value for writing data to this channel. 00578 * If no data can be written within the timeout period, then a 00579 * SystemException will be thrown, with error code EAGAIN or 00580 * EWOULDBLOCK. 00581 * 00582 * @param msec The timeout, in milliseconds. If 0 is given, 00583 * there will be no timeout. 00584 * @throws SystemException Cannot set the timeout. 00585 */ 00586 void setWriteTimeout(unsigned int msec) { 00587 // People say that SO_RCVTIMEO/SO_SNDTIMEO are unreliable and 00588 // not well-implemented on all platforms. 00589 // http://www.developerweb.net/forum/archive/index.php/t-3439.html 00590 // That's why we use APR's timeout facilities as well (see Hooks.cpp). 00591 struct timeval tv; 00592 int ret; 00593 00594 tv.tv_sec = msec / 1000; 00595 tv.tv_usec = msec % 1000 * 1000; 00596 ret = syscalls::setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, 00597 &tv, sizeof(tv)); 00598 #ifndef __SOLARIS__ 00599 // SO_SNDTIMEO is unimplemented and returns an error on Solaris 00600 // 9 and 10 SPARC. Seems to work okay without it. 00601 if (ret == -1) { 00602 throw SystemException("Cannot set read timeout for socket", errno); 00603 } 00604 #endif 00605 } 00606 }; 00607 00608 } // namespace Passenger 00609 00610 #endif /* _PASSENGER_MESSAGE_CHANNEL_H_ */