Passenger: MessageChannel.h Source File

- Main Page
- Modules
- Namespaces
- Classes
- Files

- File List

# MessageChannel.h

    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_ */