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 if (negotiate) { 00397 vector<string> args; 00398 00399 if (!read(args)) { 00400 throw IOException("Unexpected end of stream encountered"); 00401 } else if (args.size() != 1 || args[0] != "pass IO") { 00402 throw IOException("FD passing negotiation message expected."); 00403 } 00404 } 00405 00406 struct msghdr msg; 00407 struct iovec vec; 00408 char dummy[1]; 00409 #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) 00410 struct { 00411 struct cmsghdr header; 00412 int fd; 00413 } control_data; 00414 #else 00415 char control_data[CMSG_SPACE(sizeof(int))]; 00416 #endif 00417 struct cmsghdr *control_header; 00418 int ret; 00419 00420 msg.msg_name = NULL; 00421 msg.msg_namelen = 0; 00422 00423 /* Linux and Solaris require msg_iov to be non-NULL. */ 00424 dummy[0] = '\0'; 00425 vec.iov_base = dummy; 00426 vec.iov_len = sizeof(dummy); 00427 msg.msg_iov = &vec; 00428 msg.msg_iovlen = 1; 00429 00430 msg.msg_control = (caddr_t) &control_data; 00431 msg.msg_controllen = sizeof(control_data); 00432 msg.msg_flags = 0; 00433 00434 control_header = CMSG_FIRSTHDR(&msg); 00435 control_header->cmsg_level = SOL_SOCKET; 00436 control_header->cmsg_type = SCM_RIGHTS; 00437 #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) 00438 control_header->cmsg_len = sizeof(control_data); 00439 control_data.fd = fileDescriptor; 00440 #else 00441 control_header->cmsg_len = CMSG_LEN(sizeof(int)); 00442 memcpy(CMSG_DATA(control_header), &fileDescriptor, sizeof(int)); 00443 #endif 00444 00445 ret = syscalls::sendmsg(fd, &msg, 0); 00446 if (ret == -1) { 00447 throw SystemException("Cannot send file descriptor with sendmsg()", errno); 00448 } 00449 } 00450 00451 /** 00452 * Read an array message from the underlying file descriptor. 00453 * 00454 * @param args The message will be put in this variable. 00455 * @return Whether end-of-file has been reached. If so, then the contents 00456 * of <tt>args</tt> will be undefined. 00457 * @throws SystemException If an error occured while receiving the message. 00458 * @throws boost::thread_interrupted 00459 * @see write() 00460 */ 00461 bool read(vector<string> &args) { 00462 uint16_t size; 00463 int ret; 00464 unsigned int alreadyRead = 0; 00465 00466 do { 00467 ret = syscalls::read(fd, (char *) &size + alreadyRead, sizeof(size) - alreadyRead); 00468 if (ret == -1) { 00469 throw SystemException("read() failed", errno); 00470 } else if (ret == 0) { 00471 return false; 00472 } 00473 alreadyRead += ret; 00474 } while (alreadyRead < sizeof(size)); 00475 size = ntohs(size); 00476 00477 string buffer; 00478 args.clear(); 00479 buffer.reserve(size); 00480 while (buffer.size() < size) { 00481 char tmp[1024 * 8]; 00482 ret = syscalls::read(fd, tmp, min(size - buffer.size(), sizeof(tmp))); 00483 if (ret == -1) { 00484 throw SystemException("read() failed", errno); 00485 } else if (ret == 0) { 00486 return false; 00487 } 00488 buffer.append(tmp, ret); 00489 } 00490 00491 if (!buffer.empty()) { 00492 string::size_type start = 0, pos; 00493 const string &const_buffer(buffer); 00494 while ((pos = const_buffer.find('\0', start)) != string::npos) { 00495 args.push_back(const_buffer.substr(start, pos - start)); 00496 start = pos + 1; 00497 } 00498 } 00499 return true; 00500 } 00501 00502 /** 00503 * Read a 32-bit big-endian unsigned integer from the underlying file descriptor. 00504 * 00505 * @param value Upon success, the read value will be stored in here. 00506 * @param timeout A pointer to an integer, representing the maximum number of 00507 * milliseconds to spend on reading the entire integer. 00508 * A TimeoutException will be thrown if the timeout expires. 00509 * If no exception is thrown, the the amount of time spent on waiting 00510 * will be deducted from <tt>*timeout</tt>. 00511 * Pass NULL if you do not want to enforce any time limits. 00512 * @return True if a value was read, false if EOF was reached before all data can be 00513 * read. 00514 * @throws SystemException An error occurred while reading data from the file descriptor. 00515 * @throws boost::thread_interrupted 00516 */ 00517 bool readUint32(unsigned int &value, unsigned long long *timeout = NULL) { 00518 uint32_t temp; 00519 00520 if (!readRaw(&temp, sizeof(uint32_t), timeout)) { 00521 return false; 00522 } else { 00523 value = ntohl(temp); 00524 return true; 00525 } 00526 } 00527 00528 /** 00529 * Read a scalar message from the underlying file descriptor. 00530 * 00531 * @param output The message will be put in here. 00532 * @param maxSize The maximum number of bytes that may be read. If the 00533 * scalar to read is larger than this, then a SecurityException 00534 * will be thrown. Set to 0 for no size limit. 00535 * @param timeout A pointer to an integer, representing the maximum number of 00536 * milliseconds to spend on reading the entire scalar. 00537 * A TimeoutException will be thrown if unable to read the entire 00538 * scalar within this time period. 00539 * If no exception is thrown, the the amount of time spent on waiting 00540 * will be deducted from <tt>*timeout</tt>. 00541 * Pass NULL if you do not want to enforce any time limits. 00542 * @returns Whether end-of-file was reached during reading. 00543 * @throws SystemException An error occured while reading data from the file descriptor. 00544 * @throws SecurityException There is more data to read than allowed by maxSize. 00545 * @throws TimeoutException Unable to read the entire scalar within <tt>timeout</tt> 00546 * milliseconds. 00547 * @throws boost::thread_interrupted 00548 * @see writeScalar() 00549 */ 00550 bool readScalar(string &output, unsigned int maxSize = 0, unsigned long long *timeout = NULL) { 00551 unsigned int size; 00552 unsigned int remaining; 00553 00554 if (!readUint32(size, timeout)) { 00555 return false; 00556 } 00557 00558 if (maxSize != 0 && size > maxSize) { 00559 throw SecurityException("There is more data available than is allowed by the size limit."); 00560 } 00561 00562 output.clear(); 00563 output.reserve(size); 00564 remaining = size; 00565 if (OXT_LIKELY(remaining > 0)) { 00566 char buf[1024 * 32]; 00567 // Wipe the buffer when we're done; it might contain sensitive data. 00568 MemZeroGuard g(buf, sizeof(buf)); 00569 00570 while (remaining > 0) { 00571 unsigned int blockSize = min((unsigned int) sizeof(buf), remaining); 00572 00573 if (!readRaw(buf, blockSize, timeout)) { 00574 return false; 00575 } 00576 output.append(buf, blockSize); 00577 remaining -= blockSize; 00578 } 00579 } 00580 return true; 00581 } 00582 00583 /** 00584 * Read exactly <tt>size</tt> bytes of data from the underlying file descriptor, 00585 * and put the result in <tt>buf</tt>. If end-of-file has been reached, or if 00586 * end-of-file was encountered before <tt>size</tt> bytes have been read, then 00587 * <tt>false</tt> will be returned. Otherwise (i.e. if the read was successful), 00588 * <tt>true</tt> will be returned. 00589 * 00590 * @param buf The buffer to place the read data in. This buffer must be at least 00591 * <tt>size</tt> bytes long. 00592 * @param size The number of bytes to read. 00593 * @param timeout A pointer to an integer, which specifies the maximum number of 00594 * milliseconds that may be spent on reading the <tt>size</tt> bytes 00595 * of data. If the timeout expired then TimeoutException will be 00596 * thrown. 00597 * If this function returns without throwing an exception, then the 00598 * total number of milliseconds spent on reading will be deducted 00599 * from <tt>timeout</tt>. 00600 * Pass NULL if you do not want to enforce a timeout. 00601 * @return Whether reading was successful or whether EOF was reached. 00602 * @pre buf != NULL 00603 * @throws SystemException Something went wrong during reading. 00604 * @throws TimeoutException Unable to read <tt>size</tt> bytes of data within 00605 * <tt>timeout</tt> milliseconds. 00606 * @throws boost::thread_interrupted 00607 * @see writeRaw() 00608 */ 00609 bool readRaw(void *buf, unsigned int size, unsigned long long *timeout = NULL) { 00610 ssize_t ret; 00611 unsigned int alreadyRead = 0; 00612 00613 while (alreadyRead < size) { 00614 if (timeout != NULL && !waitUntilReadable(timeout)) { 00615 throw TimeoutException("Cannot read enough data within the specified timeout."); 00616 } 00617 ret = syscalls::read(fd, (char *) buf + alreadyRead, size - alreadyRead); 00618 if (ret == -1) { 00619 throw SystemException("read() failed", errno); 00620 } else if (ret == 0) { 00621 return false; 00622 } else { 00623 alreadyRead += ret; 00624 } 00625 } 00626 return true; 00627 } 00628 00629 /** 00630 * Waits at most <tt>*timeout</tt> milliseconds for the file descriptor to become readable. 00631 * Returns true if it become readable within the timeout, false if the timeout expired. 00632 * 00633 * <tt>*timeout</tt> may be 0, in which case this method will check whether the file 00634 * descriptor is readable, and immediately returns without waiting. 00635 * 00636 * If no exception is thrown, this method deducts the number of milliseconds that has 00637 * passed from <tt>*timeout</tt>. 00638 * 00639 * @throws SystemException 00640 * @throws boost::thread_interrupted 00641 */ 00642 bool waitUntilReadable(unsigned long long *timeout) { 00643 fd_set fds; 00644 struct timeval tv; 00645 int ret; 00646 00647 FD_ZERO(&fds); 00648 FD_SET(fd, &fds); 00649 tv.tv_sec = *timeout / 1000; 00650 tv.tv_usec = *timeout % 1000 * 1000; 00651 00652 Timer timer; 00653 ret = syscalls::select(fd + 1, &fds, NULL, NULL, &tv); 00654 if (ret == -1) { 00655 throw SystemException("select() failed", errno); 00656 } else { 00657 unsigned long long elapsed = timer.elapsed(); 00658 if (elapsed > *timeout) { 00659 *timeout = 0; 00660 } else { 00661 *timeout -= elapsed; 00662 } 00663 return ret != 0; 00664 } 00665 } 00666 00667 /** 00668 * Receive a file descriptor, which had been passed over the underlying 00669 * file descriptor. 00670 * 00671 * @param negotiate See Ruby's MessageChannel#send_io method's comments. 00672 * @return The passed file descriptor. 00673 * @throws SystemException If something went wrong during the 00674 * receiving of a file descriptor. Perhaps the underlying 00675 * file descriptor isn't a Unix socket. 00676 * @throws IOException Whatever was received doesn't seem to be a 00677 * file descriptor. 00678 * @throws boost::thread_interrupted 00679 */ 00680 int readFileDescriptor(bool negotiate = true) { 00681 if (negotiate) { 00682 write("pass IO", NULL); 00683 } 00684 00685 struct msghdr msg; 00686 struct iovec vec; 00687 char dummy[1]; 00688 #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) 00689 // File descriptor passing macros (CMSG_*) seem to be broken 00690 // on 64-bit MacOS X. This structure works around the problem. 00691 struct { 00692 struct cmsghdr header; 00693 int fd; 00694 } control_data; 00695 #define EXPECTED_CMSG_LEN sizeof(control_data) 00696 #else 00697 char control_data[CMSG_SPACE(sizeof(int))]; 00698 #define EXPECTED_CMSG_LEN CMSG_LEN(sizeof(int)) 00699 #endif 00700 struct cmsghdr *control_header; 00701 int ret; 00702 00703 msg.msg_name = NULL; 00704 msg.msg_namelen = 0; 00705 00706 dummy[0] = '\0'; 00707 vec.iov_base = dummy; 00708 vec.iov_len = sizeof(dummy); 00709 msg.msg_iov = &vec; 00710 msg.msg_iovlen = 1; 00711 00712 msg.msg_control = (caddr_t) &control_data; 00713 msg.msg_controllen = sizeof(control_data); 00714 msg.msg_flags = 0; 00715 00716 ret = syscalls::recvmsg(fd, &msg, 0); 00717 if (ret == -1) { 00718 throw SystemException("Cannot read file descriptor with recvmsg()", errno); 00719 } 00720 00721 control_header = CMSG_FIRSTHDR(&msg); 00722 if (control_header == NULL) { 00723 throw IOException("No valid file descriptor received."); 00724 } 00725 if (control_header->cmsg_len != EXPECTED_CMSG_LEN 00726 || control_header->cmsg_level != SOL_SOCKET 00727 || control_header->cmsg_type != SCM_RIGHTS) { 00728 throw IOException("No valid file descriptor received."); 00729 } 00730 #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__) 00731 return control_data.fd; 00732 #else 00733 return *((int *) CMSG_DATA(control_header)); 00734 #endif 00735 } 00736 00737 /** 00738 * Set the timeout value for reading data from this channel. 00739 * If no data can be read within the timeout period, then a 00740 * SystemException will be thrown by one of the read methods, 00741 * with error code EAGAIN or EWOULDBLOCK. 00742 * 00743 * @param msec The timeout, in milliseconds. If 0 is given, 00744 * there will be no timeout. 00745 * @throws SystemException Cannot set the timeout. 00746 */ 00747 void setReadTimeout(unsigned int msec) { 00748 // See the comment for setWriteTimeout(). 00749 struct timeval tv; 00750 int ret; 00751 00752 tv.tv_sec = msec / 1000; 00753 tv.tv_usec = msec % 1000 * 1000; 00754 ret = syscalls::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, 00755 &tv, sizeof(tv)); 00756 #ifndef __SOLARIS__ 00757 // SO_RCVTIMEO is unimplemented and returns an error on Solaris 00758 // 9 and 10 SPARC. Seems to work okay without it. 00759 if (ret == -1) { 00760 throw SystemException("Cannot set read timeout for socket", errno); 00761 } 00762 #endif 00763 } 00764 00765 /** 00766 * Set the timeout value for writing data to this channel. 00767 * If no data can be written within the timeout period, then a 00768 * SystemException will be thrown, with error code EAGAIN or 00769 * EWOULDBLOCK. 00770 * 00771 * @param msec The timeout, in milliseconds. If 0 is given, 00772 * there will be no timeout. 00773 * @throws SystemException Cannot set the timeout. 00774 */ 00775 void setWriteTimeout(unsigned int msec) { 00776 // People say that SO_RCVTIMEO/SO_SNDTIMEO are unreliable and 00777 // not well-implemented on all platforms. 00778 // http://www.developerweb.net/forum/archive/index.php/t-3439.html 00779 // That's why we use APR's timeout facilities as well (see Hooks.cpp). 00780 struct timeval tv; 00781 int ret; 00782 00783 tv.tv_sec = msec / 1000; 00784 tv.tv_usec = msec % 1000 * 1000; 00785 ret = syscalls::setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, 00786 &tv, sizeof(tv)); 00787 #ifndef __SOLARIS__ 00788 // SO_SNDTIMEO is unimplemented and returns an error on Solaris 00789 // 9 and 10 SPARC. Seems to work okay without it. 00790 if (ret == -1) { 00791 throw SystemException("Cannot set read timeout for socket", errno); 00792 } 00793 #endif 00794 } 00795 }; 00796 00797 } // namespace Passenger 00798 00799 #endif /* _PASSENGER_MESSAGE_CHANNEL_H_ */