Permalink
Browse files

Move parts of MessageChannel::readFileDescriptor() and MessageChannel…

…::writeFileDescriptor() into free functions in IOUtils.
  • Loading branch information...
1 parent c885c39 commit e3885d5c9e7b5403422742f193be0aa5775a84d2 @FooBarWidget FooBarWidget committed Aug 3, 2011
Showing with 251 additions and 111 deletions.
  1. +2 −94 ext/common/MessageChannel.h
  2. +110 −0 ext/common/Utils/IOUtils.cpp
  3. +37 −0 ext/common/Utils/IOUtils.h
  4. +47 −17 test/cxx/IOUtilsTest.cpp
  5. +47 −0 test/cxx/TestSupport.cpp
  6. +8 −0 test/cxx/TestSupport.h
@@ -359,49 +359,7 @@ class MessageChannel {
}
}
- struct msghdr msg;
- struct iovec vec;
- char dummy[1];
- #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
- struct {
- struct cmsghdr header;
- int fd;
- } control_data;
- #else
- char control_data[CMSG_SPACE(sizeof(int))];
- #endif
- struct cmsghdr *control_header;
- int ret;
-
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
-
- /* Linux and Solaris require msg_iov to be non-NULL. */
- dummy[0] = '\0';
- vec.iov_base = dummy;
- vec.iov_len = sizeof(dummy);
- msg.msg_iov = &vec;
- msg.msg_iovlen = 1;
-
- msg.msg_control = (caddr_t) &control_data;
- msg.msg_controllen = sizeof(control_data);
- msg.msg_flags = 0;
-
- control_header = CMSG_FIRSTHDR(&msg);
- control_header->cmsg_level = SOL_SOCKET;
- control_header->cmsg_type = SCM_RIGHTS;
- #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
- control_header->cmsg_len = sizeof(control_data);
- control_data.fd = fileDescriptor;
- #else
- control_header->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(control_header), &fileDescriptor, sizeof(int));
- #endif
-
- ret = syscalls::sendmsg(fd, &msg, 0);
- if (ret == -1) {
- throw SystemException("Cannot send file descriptor with sendmsg()", errno);
- }
+ Passenger::writeFileDescriptor(fd, fileDescriptor);
if (negotiate) {
vector<string> args;
@@ -615,57 +573,7 @@ class MessageChannel {
write("pass IO", NULL);
}
- struct msghdr msg;
- struct iovec vec;
- char dummy[1];
- #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
- // File descriptor passing macros (CMSG_*) seem to be broken
- // on 64-bit MacOS X. This structure works around the problem.
- struct {
- struct cmsghdr header;
- int fd;
- } control_data;
- #define EXPECTED_CMSG_LEN sizeof(control_data)
- #else
- char control_data[CMSG_SPACE(sizeof(int))];
- #define EXPECTED_CMSG_LEN CMSG_LEN(sizeof(int))
- #endif
- struct cmsghdr *control_header;
- int ret;
-
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
-
- dummy[0] = '\0';
- vec.iov_base = dummy;
- vec.iov_len = sizeof(dummy);
- msg.msg_iov = &vec;
- msg.msg_iovlen = 1;
-
- msg.msg_control = (caddr_t) &control_data;
- msg.msg_controllen = sizeof(control_data);
- msg.msg_flags = 0;
-
- ret = syscalls::recvmsg(fd, &msg, 0);
- if (ret == -1) {
- throw SystemException("Cannot read file descriptor with recvmsg()", errno);
- }
-
- control_header = CMSG_FIRSTHDR(&msg);
- if (control_header == NULL) {
- throw IOException("No valid file descriptor received.");
- }
- if (control_header->cmsg_len != EXPECTED_CMSG_LEN
- || control_header->cmsg_level != SOL_SOCKET
- || control_header->cmsg_type != SCM_RIGHTS) {
- throw IOException("No valid file descriptor received.");
- }
-
- #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
- int fd = control_data.fd;
- #else
- int fd = *((int *) CMSG_DATA(control_header));
- #endif
+ int fd = Passenger::readFileDescriptor(this->fd);
if (negotiate) {
try {
@@ -895,6 +895,116 @@ setWritevFunction(WritevFunction func) {
}
}
+int
+readFileDescriptor(int fd, unsigned long long *timeout) {
+ if (timeout != NULL && !waitUntilReadable(fd, timeout)) {
+ throw TimeoutException("Cannot receive file descriptor within the specified timeout");
+ }
+
+ struct msghdr msg;
+ struct iovec vec;
+ char dummy[1];
+ #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
+ // File descriptor passing macros (CMSG_*) seem to be broken
+ // on 64-bit MacOS X. This structure works around the problem.
+ struct {
+ struct cmsghdr header;
+ int fd;
+ } control_data;
+ #define EXPECTED_CMSG_LEN sizeof(control_data)
+ #else
+ char control_data[CMSG_SPACE(sizeof(int))];
+ #define EXPECTED_CMSG_LEN CMSG_LEN(sizeof(int))
+ #endif
+ struct cmsghdr *control_header;
+ int ret;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ dummy[0] = '\0';
+ vec.iov_base = dummy;
+ vec.iov_len = sizeof(dummy);
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = (caddr_t) &control_data;
+ msg.msg_controllen = sizeof(control_data);
+ msg.msg_flags = 0;
+
+ ret = syscalls::recvmsg(fd, &msg, 0);
+ if (ret == -1) {
+ throw SystemException("Cannot read file descriptor with recvmsg()", errno);
+ }
+
+ control_header = CMSG_FIRSTHDR(&msg);
+ if (control_header == NULL) {
+ throw IOException("No valid file descriptor received.");
+ }
+ if (control_header->cmsg_len != EXPECTED_CMSG_LEN
+ || control_header->cmsg_level != SOL_SOCKET
+ || control_header->cmsg_type != SCM_RIGHTS) {
+ throw IOException("No valid file descriptor received.");
+ }
+
+ #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
+ return control_data.fd;
+ #else
+ return *((int *) CMSG_DATA(control_header));
+ #endif
+}
+
+void
+writeFileDescriptor(int fd, int fdToSend, unsigned long long *timeout) {
+ if (timeout != NULL && !waitUntilWritable(fd, timeout)) {
+ throw TimeoutException("Cannot send file descriptor within the specified timeout");
+ }
+
+ struct msghdr msg;
+ struct iovec vec;
+ char dummy[1];
+ #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
+ struct {
+ struct cmsghdr header;
+ int fd;
+ } control_data;
+ #else
+ char control_data[CMSG_SPACE(sizeof(int))];
+ #endif
+ struct cmsghdr *control_header;
+ int ret;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ /* Linux and Solaris require msg_iov to be non-NULL. */
+ dummy[0] = '\0';
+ vec.iov_base = dummy;
+ vec.iov_len = sizeof(dummy);
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+
+ msg.msg_control = (caddr_t) &control_data;
+ msg.msg_controllen = sizeof(control_data);
+ msg.msg_flags = 0;
+
+ control_header = CMSG_FIRSTHDR(&msg);
+ control_header->cmsg_level = SOL_SOCKET;
+ control_header->cmsg_type = SCM_RIGHTS;
+ #if defined(__APPLE__) || defined(__SOLARIS__) || defined(__arm__)
+ control_header->cmsg_len = sizeof(control_data);
+ control_data.fd = fdToSend;
+ #else
+ control_header->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(control_header), &fdToSend, sizeof(int));
+ #endif
+
+ ret = syscalls::sendmsg(fd, &msg, 0);
+ if (ret == -1) {
+ throw SystemException("Cannot send file descriptor with sendmsg()", errno);
+ }
+}
+
void
safelyClose(int fd) {
if (syscalls::close(fd) == -1) {
@@ -366,6 +366,43 @@ void gatheredWrite(int fd, const StaticString data[], unsigned int dataCount,
void setWritevFunction(WritevFunction func);
/**
+ * Receive a file descriptor over the given Unix domain socket.
+ *
+ * @param timeout A pointer to an integer, which specifies the maximum number of
+ * microseconds that may be spent on receiving the file descriptor.
+ * If the timeout expired then TimeoutException will be thrown.
+ * If this function returns without throwing an exception, then the
+ * total number of microseconds spent on receiving will be deducted
+ * from <tt>timeout</tt>.
+ * Pass NULL if you do not want to enforce a timeout.
+ * @return The received file descriptor.
+ * @throws SystemException Something went wrong.
+ * @throws IOException Whatever was received doesn't seem to be a
+ * file descriptor.
+ * @throws TimeoutException Unable to receive a file descriptor within
+ * <tt>timeout</tt> microseconds.
+ * @throws boost::thread_interrupted
+ */
+int readFileDescriptor(int fd, unsigned long long *timeout = NULL);
+
+/**
+ * Pass the file descriptor 'fdToSend' over the Unix socket 'fd'.
+ *
+ * @param timeout A pointer to an integer, which specifies the maximum number of
+ * microseconds that may be spent on trying to pass the file descriptor.
+ * If the timeout expired then TimeoutException will be thrown.
+ * If this function returns without throwing an exception, then the
+ * total number of microseconds spent on writing will be deducted
+ * from <tt>timeout</tt>.
+ * Pass NULL if you do not want to enforce a timeout.
+ * @throws SystemException Something went wrong.
+ * @throws TimeoutException Unable to pass the file descriptor within
+ * <tt>timeout</tt> microseconds.
+ * @throws boost::thread_interrupted
+ */
+void writeFileDescriptor(int fd, int fdToSend, unsigned long long *timeout = NULL);
+
+/**
* Closes the given file descriptor and throws an exception if anything goes wrong.
* This function also works around certain close() bugs on certain operating systems.
*
@@ -55,23 +55,6 @@ namespace tut {
return p;
}
- void writeUntilFull(int fd) {
- char buf[1024 * 4];
- memset(buf, 0, sizeof(buf));
- bool done = false;
- while (!done) {
- ssize_t ret = write(fd, buf, sizeof(buf));
- if (ret == -1) {
- if (errno == EAGAIN) {
- done = true;
- } else {
- int e = errno;
- throw SystemException("Cannot write to pipe", e);
- }
- }
- }
- }
-
static void writeDataAfterSomeTime(int fd, unsigned int sleepTimeInUsec) {
try {
syscalls::usleep(sleepTimeInUsec);
@@ -813,4 +796,51 @@ namespace tut {
// Pass.
}
}
+
+ /***** Test readFileDescriptor() and writeFileDescriptor() *****/
+
+ TEST_METHOD(80) {
+ // Test whether it works.
+ SocketPair sockets = createUnixSocketPair();
+ Pipe pipes = createPipe();
+ writeFileDescriptor(sockets[0], pipes[1]);
+ FileDescriptor fd = readFileDescriptor(sockets[1]);
+ writeExact(fd, "hello");
+ char buf[6];
+ ensure_equals(readExact(pipes[0], buf, 5), 5u);
+ buf[5] = '\0';
+ ensure_equals(StaticString(buf), "hello");
+ }
+
+ TEST_METHOD(81) {
+ // Test whether timeout works.
+ SocketPair sockets = createUnixSocketPair();
+ Pipe pipes = createPipe();
+
+ unsigned long long timeout = 30000;
+ unsigned long long startTime = SystemTime::getUsec();
+ try {
+ FileDescriptor fd = readFileDescriptor(sockets[0], &timeout);
+ fail("TimeoutException expected");
+ } catch (const TimeoutException &) {
+ unsigned long long elapsed = SystemTime::getUsec() - startTime;
+ ensure("readFileDescriptor() timed out after 30 msec",
+ elapsed >= 29000 && elapsed <= 45000);
+ ensure(timeout <= 2000);
+ }
+
+ writeUntilFull(sockets[0]);
+
+ startTime = SystemTime::getUsec();
+ timeout = 30000;
+ try {
+ writeFileDescriptor(sockets[0], pipes[0], &timeout);
+ fail("TimeoutException expected");
+ } catch (const TimeoutException &) {
+ unsigned long long elapsed = SystemTime::getUsec() - startTime;
+ ensure("writeFileDescriptor() timed out after 30 msec",
+ elapsed >= 29000 && elapsed <= 45000);
+ ensure(timeout <= 2000);
+ }
+ }
}
Oops, something went wrong.

0 comments on commit e3885d5

Please sign in to comment.