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_MESSAGE_CHANNEL_H_ 00026 #define _PASSENGER_MESSAGE_CHANNEL_H_ 00027 00028 #include <oxt/system_calls.hpp> 00029 #include <oxt/macros.hpp> 00030 00031 #include <algorithm> 00032 #include <string> 00033 #include <list> 00034 #include <vector> 00035 00036 #include <sys/types.h> 00037 #include <sys/socket.h> 00038 #include <sys/uio.h> 00039 #include <arpa/inet.h> 00040 #include <errno.h> 00041 #include <unistd.h> 00042 #include <cstdarg> 00043 #if !APR_HAVE_IOVEC 00044 // We don't want apr_want.h to redefine 'struct iovec'. 00045 // http://groups.google.com/group/phusion-passenger/browse_thread/thread/7e162f60df212e9c 00046 #undef APR_HAVE_IOVEC 00047 #define APR_HAVE_IOVEC 1 00048 #endif 00049 00050 #include "StaticString.h" 00051 #include "Exceptions.h" 00052 #include "Utils/Timer.h" 00053 #include "Utils/MemZeroGuard.h" 00054 00055 namespace Passenger { 00056 00057 using namespace std; 00058 using namespace oxt; 00059 00060 /** 00061 * Convenience class for I/O operations on file descriptors. 00062 * 00063 * This class provides convenience methods for: 00064 * - sending and receiving raw data over a file descriptor. 00065 * - sending and receiving messages over a file descriptor. 00066 * - file descriptor passing over a Unix socket. 00067 * - data size limit enforcement and time constraint enforcement. 00068 * All of these methods use exceptions for error reporting. 00069 * 00070 * There are two kinds of messages: 00071 * - Array messages. These are just a list of strings, and the message 00072 * itself has a specific length. The contained strings may not 00073 * contain NUL characters (<tt>'\\0'</tt>). Note that an array message 00074 * must have at least one element. 00075 * - Scalar messages. These are byte strings which may contain arbitrary 00076 * binary data. Scalar messages also have a specific length. 00077 * The protocol is designed to be low overhead, easy to implement and 00078 * easy to parse. 00079 * 00080 * MessageChannel is to be wrapped around a file descriptor. For example: 00081 * @code 00082 * int p[2]; 00083 * pipe(p); 00084 * MessageChannel channel1(p[0]); 00085 * MessageChannel channel2(p[1]); 00086 * 00087 * // Send an array message. 00088 * channel2.write("hello", "world !!", NULL); 00089 * list<string> args; 00090 * channel1.read(args); // args now contains { "hello", "world !!" } 00091 * 00092 * // Send a scalar message. 00093 * channel2.writeScalar("some long string which can contain arbitrary binary data"); 00094 * string str; 00095 * channel1.readScalar(str); 00096 * @endcode 00097 * 00098 * The life time of a MessageChannel is independent from that of the 00099 * wrapped file descriptor. If a MessageChannel object is destroyed, 00100 * the file descriptor is not automatically closed. Call close() 00101 * if you want to close the file descriptor. 00102 * 00103 * @note I/O operations are not buffered. 00104 * @note Be careful with mixing the sending/receiving of array messages, 00105 * scalar messages and file descriptors. If you send a collection of any 00106 * of these in a specific order, then the receiving side must receive them 00107 * in the exact some order. So suppose you first send a message, then a 00108 * file descriptor, then a scalar, then the receiving side must first 00109 * receive a message, then a file descriptor, then a scalar. If the 00110 * receiving side does things in the wrong order then bad things will 00111 * happen. 00112 * @note MessageChannel is not thread-safe, but is reentrant. 00113 * @note Some methods throw SecurityException and TimeoutException. When these 00114 * exceptions are thrown, the channel will be left in an inconsistent state 00115 * because only parts of the data have been read. You should close the channel 00116 * after having caught these exceptions. 00117 * 00118 * @ingroup Support 00119 */ 00120 class MessageChannel { 00121 private: 00122 const static char DELIMITER = '\0'; 00123 int fd; 00124 00125 #ifdef __OpenBSD__ 00126 typedef u_int32_t uint32_t; 00127 typedef u_int16_t uint16_t; 00128 #endif 00129 00130 public: 00131 /** 00132 * Construct a new MessageChannel with no underlying file descriptor. 00133 * Thus the resulting MessageChannel object will not be usable. 00134 * This constructor exists to allow one to declare an "empty" 00135 * MessageChannel variable which is to be initialized later. 00136 */ 00137 MessageChannel() { 00138 this->fd = -1; 00139 } 00140 00141 /** 00142 * Construct a new MessageChannel with the given file descriptor. 00143 */ 00144 MessageChannel(int fd) { 00145 this->fd = fd; 00146 } 00147 00148 /** 00149 * Returns the underlying file descriptor. -1 if it has already been closed. 00150 */ 00151 int filenum() const { 00152 return fd; 00153 } 00154 00155 /** 00156 * Returns whether close() has been called. 00157 */ 00158 bool connected() const { 00159 return fd != -1; 00160 } 00161 00162 /** 00163 * Close the underlying file descriptor. If this method is called multiple 00164 * times, the file descriptor will only be closed the first time. 00165 * 00166 * @throw SystemException 00167 * @throw boost::thread_interrupted 00168 * @post filenum() == -1 00169 * @post !connected() 00170 */ 00171 void close() { 00172 if (fd != -1) { 00173 int ret = syscalls::close(fd); 00174 fd = -1; 00175 if (ret == -1) { 00176 throw SystemException("Cannot close file descriptor", errno); 00177 } 00178 } 00179 } 00180 00181 /** 00182 * Send an array message, which consists of the given elements, over the underlying 00183 * file descriptor. 00184 * 00185 * @param args An object which contains the message elements. This object must 00186 * support STL-style iteration, and each iterator must have an 00187 * std::string as value. Use the StringArrayType and 00188 * StringArrayConstIteratorType template parameters to specify the exact type names. 00189 * @throws SystemException An error occured while writing the data to the file descriptor. 00190 * @throws boost::thread_interrupted 00191 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00192 * @see read(), write(const char *, ...) 00193 */ 00194 template<typename StringArrayType, typename StringArrayConstIteratorType> 00195 void write(const StringArrayType &args) { 00196 StringArrayConstIteratorType it; 00197 string data; 00198 uint16_t dataSize = 0; 00199 00200 for (it = args.begin(); it != args.end(); it++) { 00201 dataSize += it->size() + 1; 00202 } 00203 data.reserve(dataSize + sizeof(dataSize)); 00204 dataSize = htons(dataSize); 00205 data.append((const char *) &dataSize, sizeof(dataSize)); 00206 for (it = args.begin(); it != args.end(); it++) { 00207 data.append(*it); 00208 data.append(1, DELIMITER); 00209 } 00210 00211 writeRaw(data); 00212 } 00213 00214 /** 00215 * Send an array message, which consists of the given elements, over the underlying 00216 * file descriptor. 00217 * 00218 * @param args The message elements. 00219 * @throws SystemException An error occured while writing the data to the file descriptor. 00220 * @throws boost::thread_interrupted 00221 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00222 * @see read(), write(const char *, ...) 00223 */ 00224 void write(const list<string> &args) { 00225 write<list<string>, list<string>::const_iterator>(args); 00226 } 00227 00228 /** 00229 * Send an array message, which consists of the given elements, over the underlying 00230 * file descriptor. 00231 * 00232 * @param args The message elements. 00233 * @throws SystemException An error occured while writing the data to the file descriptor. 00234 * @throws boost::thread_interrupted 00235 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00236 * @see read(), write(const char *, ...) 00237 */ 00238 void write(const vector<string> &args) { 00239 write<vector<string>, vector<string>::const_iterator>(args); 00240 } 00241 00242 /** 00243 * Send an array message, which consists of the given strings, over the underlying 00244 * file descriptor. Like <tt>write(const char *name, ...)</tt> but takes a va_list 00245 * instead. 00246 * 00247 * @throws SystemException An error occured while writing the data to the file descriptor. 00248 * @throws boost::thread_interrupted 00249 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00250 */ 00251 void write(const char *name, va_list &ap) { 00252 list<string> args; 00253 args.push_back(name); 00254 00255 while (true) { 00256 const char *arg = va_arg(ap, const char *); 00257 if (arg == NULL) { 00258 break; 00259 } else { 00260 args.push_back(arg); 00261 } 00262 } 00263 write(args); 00264 } 00265 00266 /** 00267 * Send an array message, which consists of the given strings, over the underlying 00268 * file descriptor. 00269 * 00270 * @param name The first element of the message to send. 00271 * @param ... Other elements of the message. These *must* be strings, i.e. of type char*. 00272 * It is also required to terminate this list with a NULL. 00273 * @throws SystemException An error occured while writing the data to the file descriptor. 00274 * @throws boost::thread_interrupted 00275 * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>). 00276 * @see read(), write(const list<string> &) 00277 */ 00278 void write(const char *name, ...) { 00279 va_list ap; 00280 va_start(ap, name); 00281 try { 00282 write(name, ap); 00283 va_end(ap); 00284 } catch (...) { 00285 va_end(ap); 00286 throw; 00287 } 00288 } 00289 00290 /** 00291 * Write a 32-bit big-endian unsigned integer to the underlying file descriptor. 00292 * 00293 * @throws SystemException An error occurred while writing the data to the file descriptor. 00294 * @throws boost::thread_interrupted 00295 */ 00296 void writeUint32(unsigned int value) { 00297 uint32_t l = htonl(value); 00298 writeRaw((const char *) &l, sizeof(uint32_t)); 00299 } 00300 00301 /** 00302 * Write a scalar message to the underlying file descriptor. 00303 * 00304 * @note Security guarantee: this method will not copy the data in memory, 00305 * so it's safe to use this method to write passwords to the underlying 00306 * file descriptor. 00307 * 00308 * @param str The scalar message's content. 00309 * @throws SystemException An error occured while writing the data to the file descriptor. 00310 * @throws boost::thread_interrupted 00311 * @see readScalar(), writeScalar(const char *, unsigned int) 00312 */ 00313 void writeScalar(const string &str) { 00314 writeScalar(str.c_str(), str.size()); 00315 } 00316 00317 /** 00318 * Write a scalar message to the underlying file descriptor. 00319 * 00320 * @note Security guarantee: this method will not copy the data in memory, 00321 * so it's safe to use this method to write passwords to the underlying 00322 * file descriptor. 00323 * 00324 * @param data The scalar message's content. 00325 * @param size The number of bytes in <tt>data</tt>. 00326 * @pre <tt>data != NULL</tt> 00327 * @throws SystemException An error occured while writing the data to the file descriptor. 00328 * @throws boost::thread_interrupted 00329 * @see readScalar(), writeScalar(const string &) 00330 */ 00331 void writeScalar(const char *data, unsigned int size) { 00332 writeUint32(size); 00333 writeRaw(data, size); 00334 } 00335 00336 /** 00337 * Send a block of data over the underlying file descriptor. 00338 * This method blocks until everything is sent. 00339 * 00340 * @note Security guarantee: this method will not copy the data in memory, 00341 * so it's safe to use this method to write passwords to the underlying 00342 * file descriptor. 00343 * 00344 * @param data The data to send. 00345 * @param size The number of bytes in <tt>data</tt>. 00346 * @pre <tt>data != NULL</tt> 00347 * @throws SystemException An error occured while writing the data to the file descriptor. 00348 * @throws boost::thread_interrupted 00349 * @see readRaw() 00350 */ 00351 void writeRaw(const char *data, unsigned int size) { 00352 ssize_t ret; 00353 unsigned int written = 0; 00354 do { 00355 ret = syscalls::write(fd, data + written, size - written); 00356 if (ret == -1) { 00357 throw SystemException("write() failed", errno); 00358 } else { 00359 written += ret; 00360 } 00361 } while (written < size); 00362 } 00363 00364 /** 00365 * Send a block of data over the underlying file descriptor. 00366 * This method blocks until everything is sent. 00367 * 00368 * @note Security guarantee: this method will not copy the data in memory, 00369 * so it's safe to use this method to write passwords to the underlying 00370 * file descriptor. 00371 * 00372 * @param data The data to send. 00373 * @pre <tt>data != NULL</tt> 00374 * @throws SystemException An error occured while writing the data to the file descriptor. 00375 * @throws boost::thread_interrupted 00376 */ 00377 void writeRaw(const string &data) { 00378 writeRaw(data.c_str(), data.size()); 00379 } 00380 00381 /** 00382 * Pass a file descriptor. This only works if the underlying file 00383 * descriptor is a Unix socket. 00384 * 00385 * @param fileDescriptor The file descriptor to pass. 00386 * @param negotiate See Ruby's MessageChannel#send_io method's comments. 00387 * @throws SystemException Something went wrong during file descriptor passing. 00388 * @throws boost::thread_interrupted 00389 * @pre <tt>fileDescriptor >= 0</tt> 00390 * @see readFileDescriptor() 00391 */ 00392 void writeFileDescriptor(int fileDescriptor, bool negotiate = true) { 00393 // See message_channel.rb for more info about negotiation. 00394 if (negotiate) { 00395 vector<string> args; 00396 00397 if (!read(args)) { 00398 throw IOException("Unexpected end of stream encountered while pre-negotiating a file descriptor"); 00399 } else if (args.size() != 1 || args[0] != "pass IO") { 00400 throw IOException("FD passing pre-negotiation message expected."); 00401 } 00402 } 00403 00404 struct msghdr msg; 00405 struct iovec vec; 00406 char dummy[1]; 00407 #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) 00408 struct { 00409 struct cmsghdr header; 00410 int fd; 00411 } control_data; 00412 #else 00413 char control_data[CMSG_SPACE(sizeof(int))]; 00414 #endif 00415 struct cmsghdr *control_header; 00416 int ret; 00417 00418 msg.msg_name = NULL; 00419 msg.msg_namelen = 0; 00420 00421 /* Linux and Solaris require msg_iov to be non-NULL. */ 00422 dummy[0] = '\0'; 00423 vec.iov_base = dummy; 00424 vec.iov_len = sizeof(dummy); 00425 msg.msg_iov = &vec; 00426 msg.msg_iovlen = 1; 00427 00428 msg.msg_control = (caddr_t) &control_data; 00429 msg.msg_controllen = sizeof(control_data); 00430 msg.msg_flags = 0; 00431 00432 control_header = CMSG_FIRSTHDR(&msg); 00433 control_header->cmsg_level = SOL_SOCKET; 00434 control_header->cmsg_type = SCM_RIGHTS; 00435 #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) 00436 control_header->cmsg_len = sizeof(control_data); 00437 control_data.fd = fileDescriptor; 00438 #else 00439 control_header->cmsg_len = CMSG_LEN(sizeof(int)); 00440 memcpy(CMSG_DATA(control_header), &fileDescriptor, sizeof(int)); 00441 #endif 00442 00443 ret = syscalls::sendmsg(fd, &msg, 0); 00444 if (ret == -1) { 00445 throw SystemException("Cannot send file descriptor with sendmsg()", errno); 00446 } 00447 00448 if (negotiate) { 00449 vector<string> args; 00450 00451 if (!read(args)) { 00452 throw IOException("Unexpected end of stream encountered while post-negotiating a file descriptor"); 00453 } else if (args.size() != 1 || args[0] != "got IO") { 00454 throw IOException("FD passing post-negotiation message expected."); 00455 } 00456 } 00457 } 00458 00459 /** 00460 * Read an array message from the underlying file descriptor. 00461 * 00462 * @param args The message will be put in this variable. 00463 * @return Whether end-of-file has been reached. If so, then the contents 00464 * of <tt>args</tt> will be undefined. 00465 * @throws SystemException If an error occured while receiving the message. 00466 * @throws boost::thread_interrupted 00467 * @see write() 00468 */ 00469 bool read(vector<string> &args) { 00470 uint16_t size; 00471 int ret; 00472 unsigned int alreadyRead = 0; 00473 00474 do { 00475 ret = syscalls::read(fd, (char *) &size + alreadyRead, sizeof(size) - alreadyRead); 00476 if (ret == -1) { 00477 throw SystemException("read() failed", errno); 00478 } else if (ret == 0) { 00479 return false; 00480 } 00481 alreadyRead += ret; 00482 } while (alreadyRead < sizeof(size)); 00483 size = ntohs(size); 00484 00485 string buffer; 00486 args.clear(); 00487 buffer.reserve(size); 00488 while (buffer.size() < size) { 00489 char tmp[1024 * 8]; 00490 ret = syscalls::read(fd, tmp, min(size - buffer.size(), sizeof(tmp))); 00491 if (ret == -1) { 00492 throw SystemException("read() failed", errno); 00493 } else if (ret == 0) { 00494 return false; 00495 } 00496 buffer.append(tmp, ret); 00497 } 00498 00499 if (!buffer.empty()) { 00500 string::size_type start = 0, pos; 00501 const string &const_buffer(buffer); 00502 while ((pos = const_buffer.find('\0', start)) != string::npos) { 00503 args.push_back(const_buffer.substr(start, pos - start)); 00504 start = pos + 1; 00505 } 00506 } 00507 return true; 00508 } 00509 00510 /** 00511 * Read a 32-bit big-endian unsigned integer from the underlying file descriptor. 00512 * 00513 * @param value Upon success, the read value will be stored in here. 00514 * @param timeout A pointer to an integer, representing the maximum number of 00515 * milliseconds to spend on reading the entire integer. 00516 * A TimeoutException will be thrown if the timeout expires. 00517 * If no exception is thrown, the the amount of time spent on waiting 00518 * will be deducted from <tt>*timeout</tt>. 00519 * Pass NULL if you do not want to enforce any time limits. 00520 * @return True if a value was read, false if EOF was reached before all data can be 00521 * read. 00522 * @throws SystemException An error occurred while reading data from the file descriptor. 00523 * @throws boost::thread_interrupted 00524 */ 00525 bool readUint32(unsigned int &value, unsigned long long *timeout = NULL) { 00526 uint32_t temp; 00527 00528 if (!readRaw(&temp, sizeof(uint32_t), timeout)) { 00529 return false; 00530 } else { 00531 value = ntohl(temp); 00532 return true; 00533 } 00534 } 00535 00536 /** 00537 * Read a scalar message from the underlying file descriptor. 00538 * 00539 * @param output The message will be put in here. 00540 * @param maxSize The maximum number of bytes that may be read. If the 00541 * scalar to read is larger than this, then a SecurityException 00542 * will be thrown. Set to 0 for no size limit. 00543 * @param timeout A pointer to an integer, representing the maximum number of 00544 * milliseconds to spend on reading the entire scalar. 00545 * A TimeoutException will be thrown if unable to read the entire 00546 * scalar within this time period. 00547 * If no exception is thrown, the the amount of time spent on waiting 00548 * will be deducted from <tt>*timeout</tt>. 00549 * Pass NULL if you do not want to enforce any time limits. 00550 * @returns Whether end-of-file was reached during reading. 00551 * @throws SystemException An error occured while reading data from the file descriptor. 00552 * @throws SecurityException There is more data to read than allowed by maxSize. 00553 * @throws TimeoutException Unable to read the entire scalar within <tt>timeout</tt> 00554 * milliseconds. 00555 * @throws boost::thread_interrupted 00556 * @see writeScalar() 00557 */ 00558 bool readScalar(string &output, unsigned int maxSize = 0, unsigned long long *timeout = NULL) { 00559 unsigned int size; 00560 unsigned int remaining; 00561 00562 if (!readUint32(size, timeout)) { 00563 return false; 00564 } 00565 00566 if (maxSize != 0 && size > maxSize) { 00567 throw SecurityException("There is more data available than is allowed by the size limit."); 00568 } 00569 00570 output.clear(); 00571 output.reserve(size); 00572 remaining = size; 00573 if (OXT_LIKELY(remaining > 0)) { 00574 char buf[1024 * 32]; 00575 // Wipe the buffer when we're done; it might contain sensitive data. 00576 MemZeroGuard g(buf, sizeof(buf)); 00577 00578 while (remaining > 0) { 00579 unsigned int blockSize = min((unsigned int) sizeof(buf), remaining); 00580 00581 if (!readRaw(buf, blockSize, timeout)) { 00582 return false; 00583 } 00584 output.append(buf, blockSize); 00585 remaining -= blockSize; 00586 } 00587 } 00588 return true; 00589 } 00590 00591 /** 00592 * Read exactly <tt>size</tt> bytes of data from the underlying file descriptor, 00593 * and put the result in <tt>buf</tt>. If end-of-file has been reached, or if 00594 * end-of-file was encountered before <tt>size</tt> bytes have been read, then 00595 * <tt>false</tt> will be returned. Otherwise (i.e. if the read was successful), 00596 * <tt>true</tt> will be returned. 00597 * 00598 * @param buf The buffer to place the read data in. This buffer must be at least 00599 * <tt>size</tt> bytes long. 00600 * @param size The number of bytes to read. 00601 * @param timeout A pointer to an integer, which specifies the maximum number of 00602 * milliseconds that may be spent on reading the <tt>size</tt> bytes 00603 * of data. If the timeout expired then TimeoutException will be 00604 * thrown. 00605 * If this function returns without throwing an exception, then the 00606 * total number of milliseconds spent on reading will be deducted 00607 * from <tt>timeout</tt>. 00608 * Pass NULL if you do not want to enforce a timeout. 00609 * @return Whether reading was successful or whether EOF was reached. 00610 * @pre buf != NULL 00611 * @throws SystemException Something went wrong during reading. 00612 * @throws TimeoutException Unable to read <tt>size</tt> bytes of data within 00613 * <tt>timeout</tt> milliseconds. 00614 * @throws boost::thread_interrupted 00615 * @see writeRaw() 00616 */ 00617 bool readRaw(void *buf, unsigned int size, unsigned long long *timeout = NULL) { 00618 ssize_t ret; 00619 unsigned int alreadyRead = 0; 00620 00621 while (alreadyRead < size) { 00622 if (timeout != NULL && !waitUntilReadable(timeout)) { 00623 throw TimeoutException("Cannot read enough data within the specified timeout."); 00624 } 00625 ret = syscalls::read(fd, (char *) buf + alreadyRead, size - alreadyRead); 00626 if (ret == -1) { 00627 throw SystemException("read() failed", errno); 00628 } else if (ret == 0) { 00629 return false; 00630 } else { 00631 alreadyRead += ret; 00632 } 00633 } 00634 return true; 00635 } 00636 00637 /** 00638 * Waits at most <tt>*timeout</tt> milliseconds for the file descriptor to become readable. 00639 * Returns true if it become readable within the timeout, false if the timeout expired. 00640 * 00641 * <tt>*timeout</tt> may be 0, in which case this method will check whether the file 00642 * descriptor is readable, and immediately returns without waiting. 00643 * 00644 * If no exception is thrown, this method deducts the number of milliseconds that has 00645 * passed from <tt>*timeout</tt>. 00646 * 00647 * @throws SystemException 00648 * @throws boost::thread_interrupted 00649 */ 00650 bool waitUntilReadable(unsigned long long *timeout) { 00651 fd_set fds; 00652 struct timeval tv; 00653 int ret; 00654 00655 FD_ZERO(&fds); 00656 FD_SET(fd, &fds); 00657 tv.tv_sec = *timeout / 1000; 00658 tv.tv_usec = *timeout % 1000 * 1000; 00659 00660 Timer timer; 00661 ret = syscalls::select(fd + 1, &fds, NULL, NULL, &tv); 00662 if (ret == -1) { 00663 throw SystemException("select() failed", errno); 00664 } else { 00665 unsigned long long elapsed = timer.elapsed(); 00666 if (elapsed > *timeout) { 00667 *timeout = 0; 00668 } else { 00669 *timeout -= elapsed; 00670 } 00671 return ret != 0; 00672 } 00673 } 00674 00675 /** 00676 * Receive a file descriptor, which had been passed over the underlying 00677 * file descriptor. 00678 * 00679 * @param negotiate See Ruby's MessageChannel#send_io method's comments. 00680 * @return The passed file descriptor. 00681 * @throws SystemException If something went wrong during the 00682 * receiving of a file descriptor. Perhaps the underlying 00683 * file descriptor isn't a Unix socket. 00684 * @throws IOException Whatever was received doesn't seem to be a 00685 * file descriptor. 00686 * @throws boost::thread_interrupted 00687 */ 00688 int readFileDescriptor(bool negotiate = true) { 00689 // See message_channel.rb for more info about negotiation. 00690 if (negotiate) { 00691 write("pass IO", NULL); 00692 } 00693 00694 struct msghdr msg; 00695 struct iovec vec; 00696 char dummy[1]; 00697 #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) 00698 // File descriptor passing macros (CMSG_*) seem to be broken 00699 // on 64-bit MacOS X. This structure works around the problem. 00700 struct { 00701 struct cmsghdr header; 00702 int fd; 00703 } control_data; 00704 #define EXPECTED_CMSG_LEN sizeof(control_data) 00705 #else 00706 char control_data[CMSG_SPACE(sizeof(int))]; 00707 #define EXPECTED_CMSG_LEN CMSG_LEN(sizeof(int)) 00708 #endif 00709 struct cmsghdr *control_header; 00710 int ret; 00711 00712 msg.msg_name = NULL; 00713 msg.msg_namelen = 0; 00714 00715 dummy[0] = '\0'; 00716 vec.iov_base = dummy; 00717 vec.iov_len = sizeof(dummy); 00718 msg.msg_iov = &vec; 00719 msg.msg_iovlen = 1; 00720 00721 msg.msg_control = (caddr_t) &control_data; 00722 msg.msg_controllen = sizeof(control_data); 00723 msg.msg_flags = 0; 00724 00725 ret = syscalls::recvmsg(fd, &msg, 0); 00726 if (ret == -1) { 00727 throw SystemException("Cannot read file descriptor with recvmsg()", errno); 00728 } 00729 00730 control_header = CMSG_FIRSTHDR(&msg); 00731 if (control_header == NULL) { 00732 throw IOException("No valid file descriptor received."); 00733 } 00734 if (control_header->cmsg_len != EXPECTED_CMSG_LEN 00735 || control_header->cmsg_level != SOL_SOCKET 00736 || control_header->cmsg_type != SCM_RIGHTS) { 00737 throw IOException("No valid file descriptor received."); 00738 } 00739 00740 #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) 00741 int fd = control_data.fd; 00742 #else 00743 int fd = *((int *) CMSG_DATA(control_header)); 00744 #endif 00745 00746 if (negotiate) { 00747 try { 00748 write("got IO", NULL); 00749 } catch (...) { 00750 this_thread::disable_syscall_interruption dsi; 00751 syscalls::close(fd); 00752 throw; 00753 } 00754 } 00755 00756 return fd; 00757 } 00758 00759 /** 00760 * Set the timeout value for reading data from this channel. 00761 * If no data can be read within the timeout period, then a 00762 * SystemException will be thrown by one of the read methods, 00763 * with error code EAGAIN or EWOULDBLOCK. 00764 * 00765 * @param msec The timeout, in milliseconds. If 0 is given, 00766 * there will be no timeout. 00767 * @throws SystemException Cannot set the timeout. 00768 */ 00769 void setReadTimeout(unsigned int msec) { 00770 // See the comment for setWriteTimeout(). 00771 struct timeval tv; 00772 int ret; 00773 00774 tv.tv_sec = msec / 1000; 00775 tv.tv_usec = msec % 1000 * 1000; 00776 ret = syscalls::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, 00777 &tv, sizeof(tv)); 00778 #ifndef __SOLARIS__ 00779 // SO_RCVTIMEO is unimplemented and returns an error on Solaris 00780 // 9 and 10 SPARC. Seems to work okay without it. 00781 if (ret == -1) { 00782 throw SystemException("Cannot set read timeout for socket", errno); 00783 } 00784 #endif 00785 } 00786 00787 /** 00788 * Set the timeout value for writing data to this channel. 00789 * If no data can be written within the timeout period, then a 00790 * SystemException will be thrown, with error code EAGAIN or 00791 * EWOULDBLOCK. 00792 * 00793 * @param msec The timeout, in milliseconds. If 0 is given, 00794 * there will be no timeout. 00795 * @throws SystemException Cannot set the timeout. 00796 */ 00797 void setWriteTimeout(unsigned int msec) { 00798 // People say that SO_RCVTIMEO/SO_SNDTIMEO are unreliable and 00799 // not well-implemented on all platforms. 00800 // http://www.developerweb.net/forum/archive/index.php/t-3439.html 00801 // That's why we use APR's timeout facilities as well (see Hooks.cpp). 00802 struct timeval tv; 00803 int ret; 00804 00805 tv.tv_sec = msec / 1000; 00806 tv.tv_usec = msec % 1000 * 1000; 00807 ret = syscalls::setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, 00808 &tv, sizeof(tv)); 00809 #ifndef __SOLARIS__ 00810 // SO_SNDTIMEO is unimplemented and returns an error on Solaris 00811 // 9 and 10 SPARC. Seems to work okay without it. 00812 if (ret == -1) { 00813 throw SystemException("Cannot set read timeout for socket", errno); 00814 } 00815 #endif 00816 } 00817 }; 00818 00819 } // namespace Passenger 00820 00821 #endif /* _PASSENGER_MESSAGE_CHANNEL_H_ */