MessageChannel.h

00001 /*
00002  *  Phusion Passenger - http://www.modrails.com/
00003  *  Copyright (C) 2008  Phusion
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; version 2 of the License.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00017  */
00018 #ifndef _PASSENGER_MESSAGE_CHANNEL_H_
00019 #define _PASSENGER_MESSAGE_CHANNEL_H_
00020 
00021 #include <algorithm>
00022 #include <string>
00023 #include <list>
00024 #include <vector>
00025 
00026 #include <sys/types.h>
00027 #include <sys/socket.h>
00028 #include <arpa/inet.h>
00029 #include <errno.h>
00030 #include <unistd.h>
00031 #include <cstdarg>
00032 
00033 #include "Exceptions.h"
00034 #include "Utils.h"
00035 
00036 namespace Passenger {
00037 
00038 using namespace std;
00039 
00040 /**
00041  * Convenience class for I/O operations on file descriptors.
00042  *
00043  * This class provides convenience methods for:
00044  *  - sending and receiving raw data over a file descriptor.
00045  *  - sending and receiving messages over a file descriptor.
00046  *  - file descriptor passing over a Unix socket.
00047  * All of these methods use exceptions for error reporting.
00048  *
00049  * There are two kinds of messages:
00050  *  - Array messages. These are just a list of strings, and the message
00051  *    itself has a specific length. The contained strings may not
00052  *    contain NUL characters (<tt>'\\0'</tt>). Note that an array message
00053  *    must have at least one element.
00054  *  - Scalar messages. These are byte strings which may contain arbitrary
00055  *    binary data. Scalar messages also have a specific length.
00056  * The protocol is designed to be low overhead, easy to implement and
00057  * easy to parse.
00058  *
00059  * MessageChannel is to be wrapped around a file descriptor. For example:
00060  * @code
00061  *    int p[2];
00062  *    pipe(p);
00063  *    MessageChannel channel1(p[0]);
00064  *    MessageChannel channel2(p[1]);
00065  *    
00066  *    // Send an array message.
00067  *    channel2.write("hello", "world !!", NULL);
00068  *    list<string> args;
00069  *    channel1.read(args);    // args now contains { "hello", "world !!" }
00070  *
00071  *    // Send a scalar message.
00072  *    channel2.writeScalar("some long string which can contain arbitrary binary data");
00073  *    string str;
00074  *    channel1.readScalar(str);
00075  * @endcode
00076  *
00077  * The life time of a MessageChannel is independent from that of the
00078  * wrapped file descriptor. If a MessageChannel object is destroyed,
00079  * the file descriptor is not automatically closed. Call close()
00080  * if you want to close the file descriptor.
00081  *
00082  * @note I/O operations are not buffered.
00083  * @note Be careful with mixing the sending/receiving of array messages,
00084  *    scalar messages and file descriptors. If you send a collection of any
00085  *    of these in a specific order, then the receiving side must receive them
00086  *    in the exact some order. So suppose you first send a message, then a
00087  *    file descriptor, then a scalar, then the receiving side must first
00088  *    receive a message, then a file descriptor, then a scalar. If the
00089  *    receiving side does things in the wrong order then bad things will
00090  *    happen.
00091  * @note MessageChannel is thread-safe.
00092  *
00093  * @ingroup Support
00094  */
00095 class MessageChannel {
00096 private:
00097         const static char DELIMITER = '\0';
00098         int fd;
00099 
00100 public:
00101         /**
00102          * Construct a new MessageChannel with no underlying file descriptor.
00103          * Thus the resulting MessageChannel object will not be usable.
00104          * This constructor exists to allow one to declare an "empty"
00105          * MessageChannel variable which is to be initialized later.
00106          */
00107         MessageChannel() {
00108                 this->fd = -1;
00109         }
00110 
00111         /**
00112          * Construct a new MessageChannel with the given file descriptor.
00113          */
00114         MessageChannel(int fd) {
00115                 this->fd = fd;
00116         }
00117         
00118         /**
00119          * Close the underlying file descriptor. If this method is called multiple
00120          * times, the file descriptor will only be closed the first time.
00121          */
00122         void close() {
00123                 if (fd != -1) {
00124 			::close(fd);
00125                         fd = -1;
00126                 }
00127         }
00128 
00129         /**
00130          * Send an array message, which consists of the given elements, over the underlying
00131          * file descriptor.
00132          *
00133          * @param args The message elements.
00134          * @throws SystemException An error occured while writing the data to the file descriptor.
00135          * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>).
00136          * @see read(), write(const char *, ...)
00137          */
00138         void write(const list<string> &args) {
00139                 list<string>::const_iterator it;
00140                 string data;
00141                 uint16_t dataSize = 0;
00142 
00143                 for (it = args.begin(); it != args.end(); it++) {
00144                         dataSize += it->size() + 1;
00145                 }
00146                 data.reserve(dataSize + sizeof(dataSize));
00147                 dataSize = htons(dataSize);
00148                 data.append((const char *) &dataSize, sizeof(dataSize));
00149                 for (it = args.begin(); it != args.end(); it++) {
00150                         data.append(*it);
00151                         data.append(1, DELIMITER);
00152                 }
00153                 
00154                 writeRaw(data);
00155         }
00156         
00157         /**
00158          * Send an array message, which consists of the given strings, over the underlying
00159          * file descriptor.
00160          *
00161          * @param name The first element of the message to send.
00162          * @param ... Other elements of the message. These *must* be strings, i.e. of type char*.
00163          *            It is also required to terminate this list with a NULL.
00164          * @throws SystemException An error occured while writing the data to the file descriptor.
00165          * @pre None of the message elements may contain a NUL character (<tt>'\\0'</tt>).
00166          * @see read(), write(const list<string> &)
00167          */
00168         void write(const char *name, ...) {
00169                 list<string> args;
00170                 args.push_back(name);
00171                 
00172                 va_list ap;
00173                 va_start(ap, name);
00174                 while (true) {
00175                         const char *arg = va_arg(ap, const char *);
00176                         if (arg == NULL) {
00177                                 break;
00178                         } else {
00179                                 args.push_back(arg);
00180                         }
00181                 }
00182                 va_end(ap);
00183                 write(args);
00184         }
00185         
00186         /**
00187          * Send a scalar message over the underlying file descriptor.
00188          *
00189          * @param str The scalar message's content.
00190          * @throws SystemException An error occured while writing the data to the file descriptor.
00191          * @see readScalar(), writeScalar(const char *, unsigned int)
00192          */
00193         void writeScalar(const string &str) {
00194                 writeScalar(str.c_str(), str.size());
00195         }
00196         
00197         /**
00198          * Send a scalar message over the underlying file descriptor.
00199          *
00200          * @param data The scalar message's content.
00201          * @param size The number of bytes in <tt>data</tt>.
00202          * @pre <tt>data != NULL</tt>
00203          * @throws SystemException An error occured while writing the data to the file descriptor.
00204          * @see readScalar(), writeScalar(const string &)
00205          */
00206         void writeScalar(const char *data, unsigned int size) {
00207                 uint32_t l = htonl(size);
00208                 writeRaw((const char *) &l, sizeof(uint32_t));
00209                 writeRaw(data, size);
00210         }
00211         
00212         /**
00213          * Send a block of data over the underlying file descriptor.
00214          * This method blocks until everything is sent.
00215          *
00216          * @param data The data to send.
00217          * @param size The number of bytes in <tt>data</tt>.
00218          * @pre <tt>data != NULL</tt>
00219          * @throws SystemException An error occured while writing the data to the file descriptor.
00220          * @see readRaw()
00221          */
00222         void writeRaw(const char *data, unsigned int size) {
00223                 ssize_t ret;
00224                 unsigned int written = 0;
00225                 do {
00226                         do {
00227                                 ret = ::write(fd, data + written, size - written);
00228                         } while (ret == -1 && errno == EINTR);
00229                         if (ret == -1) {
00230                                 throw SystemException("write() failed", errno);
00231                         } else {
00232                                 written += ret;
00233                         }
00234                 } while (written < size);
00235         }
00236         
00237         /**
00238          * Send a block of data over the underlying file descriptor.
00239          * This method blocks until everything is sent.
00240          *
00241          * @param data The data to send.
00242          * @pre <tt>data != NULL</tt>
00243          * @throws SystemException An error occured while writing the data to the file descriptor.
00244          */
00245         void writeRaw(const string &data) {
00246                 writeRaw(data.c_str(), data.size());
00247         }
00248         
00249         /**
00250          * Pass a file descriptor. This only works if the underlying file
00251          * descriptor is a Unix socket.
00252          *
00253          * @param fileDescriptor The file descriptor to pass.
00254          * @throws SystemException Something went wrong during file descriptor passing.
00255          * @pre <tt>fileDescriptor >= 0</tt>
00256          * @see readFileDescriptor()
00257          */
00258         void writeFileDescriptor(int fileDescriptor) {
00259                 struct {
00260                         struct cmsghdr header;
00261                         int fd;
00262                 } control;
00263 
00264                 control.header.cmsg_len   = sizeof(control);
00265                 control.header.cmsg_level = SOL_SOCKET;
00266                 control.header.cmsg_type  = SCM_RIGHTS;
00267                 control.fd = fileDescriptor;
00268 
00269                 struct msghdr msg;
00270                 struct iovec vec;
00271                 char dummy[1];
00272                 
00273                 msg.msg_name = NULL;
00274                 msg.msg_namelen = 0;
00275                 
00276                 /* Linux and Solaris require msg_iov to be non-NULL.. */
00277                 dummy[0]       = '\0';
00278                 vec.iov_base   = dummy;
00279                 vec.iov_len    = sizeof(dummy);
00280                 msg.msg_iov    = &vec;
00281                 msg.msg_iovlen = 1;
00282                 
00283                 msg.msg_control    = (caddr_t) &control;
00284                 msg.msg_controllen = sizeof(control);
00285                 msg.msg_flags      = 0;
00286                 
00287                 if (sendmsg(fd, &msg, 0) == -1) {
00288                         throw SystemException("Cannot send file descriptor with sendmsg()", errno);
00289                 }
00290         }
00291         
00292         /**
00293          * Read an array message from the underlying file descriptor.
00294          *
00295          * @param args The message will be put in this variable.
00296          * @return Whether end-of-file has been reached. If so, then the contents
00297          *         of <tt>args</tt> will be undefined.
00298          * @throws SystemException If an error occured while receiving the message.
00299          * @see write()
00300          */
00301         bool read(vector<string> &args) {
00302                 uint16_t size;
00303                 int ret;
00304                 unsigned int alreadyRead = 0;
00305                 
00306                 do {
00307                         do {
00308                                 ret = ::read(fd, (char *) &size + alreadyRead, sizeof(size) - alreadyRead);
00309                         } while (ret == -1 && errno == EINTR);
00310                         if (ret == -1) {
00311                                 throw SystemException("read() failed", errno);
00312                         } else if (ret == 0) {
00313                                 return false;
00314                         }
00315                         alreadyRead += ret;
00316                 } while (alreadyRead < sizeof(size));
00317                 size = ntohs(size);
00318                 
00319                 string buffer;
00320                 args.clear();
00321                 buffer.reserve(size);
00322                 while (buffer.size() < size) {
00323                         char tmp[1024 * 8];
00324                         do {
00325                                 ret = ::read(fd, tmp, min(size - buffer.size(), sizeof(tmp)));
00326                         } while (ret == -1 && errno == EINTR);
00327                         if (ret == -1) {
00328                                 throw SystemException("read() failed", errno);
00329                         } else if (ret == 0) {
00330                                 return false;
00331                         }
00332                         buffer.append(tmp, ret);
00333                 }
00334                 
00335                 if (!buffer.empty()) {
00336                         string::size_type start = 0, pos;
00337                         const string &const_buffer(buffer);
00338                         while ((pos = const_buffer.find('\0', start)) != string::npos) {
00339                                 args.push_back(const_buffer.substr(start, pos - start));
00340                                 start = pos + 1;
00341                         }
00342                 }
00343                 return true;
00344         }
00345         
00346         /**
00347          * Read a scalar message from the underlying file descriptor.
00348          *
00349          * @param output The message will be put in here.
00350          * @returns Whether end-of-file was reached during reading.
00351          * @throws SystemException An error occured while writing the data to the file descriptor.
00352          * @see writeScalar()
00353          */
00354         bool readScalar(string &output) {
00355                 uint32_t size;
00356                 unsigned int remaining;
00357                 
00358                 if (!readRaw(&size, sizeof(uint32_t))) {
00359                         return false;
00360                 }
00361                 size = ntohl(size);
00362                 
00363                 output.clear();
00364                 output.reserve(size);
00365                 remaining = size;
00366                 while (remaining > 0) {
00367                         char buf[1024 * 32];
00368                         unsigned int blockSize = min((unsigned int) sizeof(buf), remaining);
00369                         
00370                         if (!readRaw(buf, blockSize)) {
00371                                 return false;
00372                         }
00373                         output.append(buf, blockSize);
00374                         remaining -= blockSize;
00375                 }
00376                 return true;
00377         }
00378         
00379         /**
00380          * Read exactly <tt>size</tt> bytes of data from the underlying file descriptor,
00381          * and put the result in <tt>buf</tt>. If end-of-file has been reached, or if
00382          * end-of-file was encountered before <tt>size</tt> bytes have been read, then
00383          * <tt>false</tt> will be returned. Otherwise (i.e. if the read was successful),
00384          * <tt>true</tt> will be returned.
00385          *
00386          * @param buf The buffer to place the read data in. This buffer must be at least
00387          *            <tt>size</tt> bytes long.
00388          * @param size The number of bytes to read.
00389          * @return Whether reading was successful or whether EOF was reached.
00390          * @pre buf != NULL
00391          * @throws SystemException Something went wrong during reading.
00392          * @see writeRaw()
00393          */
00394         bool readRaw(void *buf, unsigned int size) {
00395                 ssize_t ret;
00396                 unsigned int alreadyRead = 0;
00397                 
00398                 while (alreadyRead < size) {
00399                         do {
00400                                 ret = ::read(fd, (char *) buf + alreadyRead, size - alreadyRead);
00401                         } while (ret == -1 && errno == EINTR);
00402                         if (ret == -1) {
00403                                 throw SystemException("read() failed", errno);
00404                         } else if (ret == 0) {
00405                                 return false;
00406                         } else {
00407                                 alreadyRead += ret;
00408                         }
00409                 }
00410                 return true;
00411         }
00412         
00413         /**
00414          * Receive a file descriptor, which had been passed over the underlying
00415          * file descriptor.
00416          *
00417          * @return The passed file descriptor.
00418          * @throws SystemException If something went wrong during the
00419          *            receiving of a file descriptor. Perhaps the underlying
00420          *            file descriptor isn't a Unix socket.
00421          * @throws IOException Whatever was received doesn't seem to be a
00422          *            file descriptor.
00423          */
00424         int readFileDescriptor() {
00425                 struct {
00426                         struct cmsghdr header;
00427                         int fd;
00428                 } control;
00429 
00430                 control.header.cmsg_len   = sizeof(control);
00431                 control.header.cmsg_level = SOL_SOCKET;
00432                 control.header.cmsg_type  = SCM_RIGHTS;
00433                 control.fd = -1;
00434 
00435                 struct msghdr msg;
00436                 struct iovec vec;
00437                 char dummy[1];
00438 
00439                 msg.msg_name    = NULL;
00440                 msg.msg_namelen = 0;
00441                 
00442                 dummy[0]       = '\0';
00443                 vec.iov_base   = dummy;
00444                 vec.iov_len    = sizeof(dummy);
00445                 msg.msg_iov    = &vec;
00446                 msg.msg_iovlen = 1;
00447 
00448                 msg.msg_control    = (caddr_t) &control;
00449                 msg.msg_controllen = sizeof(control);
00450                 msg.msg_flags      = 0;
00451 
00452                 if (recvmsg(fd, &msg, 0) == -1) {
00453                         throw SystemException("Cannot read file descriptor with recvmsg()", errno);
00454                 }
00455                 
00456                 if (msg.msg_controllen        != sizeof(control)
00457                  || control.header.cmsg_len   != sizeof(control)
00458                  || control.header.cmsg_level != SOL_SOCKET
00459                  || control.header.cmsg_type  != SCM_RIGHTS) {
00460                         throw IOException("No valid file descriptor received.");
00461                 }
00462                 return control.fd;
00463         }
00464 };
00465 
00466 } // namespace Passenger
00467 
00468 #endif /* _PASSENGER_MESSAGE_CHANNEL_H_ */

Generated on Wed Apr 30 00:07:01 2008 for Passenger by  doxygen 1.5.3