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