00001
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <config.h>
00022
00023 #include <xapian/error.h>
00024
00025 #include "safeerrno.h"
00026 #include "safefcntl.h"
00027 #include "safeunistd.h"
00028
00029 #include <string>
00030
00031 #include "omassert.h"
00032 #include "omdebug.h"
00033 #include "omtime.h"
00034 #include "remoteconnection.h"
00035 #include "serialise.h"
00036
00037 #ifndef __WIN32__
00038 # include "safesysselect.h"
00039 #endif
00040
00041 using namespace std;
00042
00043 #ifdef __WIN32__
00044
00045
00046
00047 # if defined _MSC_VER && _MSC_VER >= 1400 && defined __STDC_SECURE_LIB__
00048 # include <stdlib.h>
00049 # include <crtdbg.h>
00050
00052 static void dummy_handler(const wchar_t*,
00053 const wchar_t*,
00054 const wchar_t*,
00055 unsigned int,
00056 uintptr_t)
00057 {
00058 }
00059
00060
00061
00062
00063
00064
00065 class MSVCIgnoreInvalidParameter {
00066 _invalid_parameter_handler old_handler;
00067 int old_report_mode;
00068
00069 public:
00070 MSVCIgnoreInvalidParameter() {
00071
00072 old_handler = _set_invalid_parameter_handler(dummy_handler);
00073
00074 old_report_mode = _CrtSetReportMode(_CRT_ASSERT, 0);
00075 }
00076
00077 ~MSVCIgnoreInvalidParameter() {
00078
00079 _set_invalid_parameter_handler(old_handler);
00080 _CrtSetReportMode(_CRT_ASSERT, old_report_mode);
00081 }
00082 };
00083 # else
00084
00085
00086
00087 struct MSVCIgnoreInvalidParameter {
00088
00089
00090
00091 MSVCIgnoreInvalidParameter() { }
00092 };
00093 # endif
00094
00096 static HANDLE fd_to_handle(int fd) {
00097 MSVCIgnoreInvalidParameter invalid_handle_value_is_ok;
00098 HANDLE handle = (HANDLE)_get_osfhandle(fd);
00099
00100
00101 return (handle != INVALID_HANDLE_VALUE ? handle : (HANDLE)fd);
00102 }
00103
00105 static void close_fd_or_socket(int fd) {
00106 MSVCIgnoreInvalidParameter invalid_fd_value_is_ok;
00107 if (close(fd) == -1 && errno == EBADF) {
00108
00109
00110 closesocket(fd);
00111 }
00112 }
00113 #else
00114
00115 inline void close_fd_or_socket(int fd) { close(fd); }
00116 #endif
00117
00118 RemoteConnection::RemoteConnection(int fdin_, int fdout_,
00119 const string & context_)
00120 : fdin(fdin_), fdout(fdout_), context(context_)
00121 {
00122 #ifdef __WIN32__
00123 memset(&overlapped, 0, sizeof(overlapped));
00124 overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
00125 if (!overlapped.hEvent)
00126 throw Xapian::NetworkError("Failed to setup OVERLAPPED",
00127 context, -(int)GetLastError());
00128
00129 #endif
00130 }
00131
00132 RemoteConnection::~RemoteConnection()
00133 {
00134 #ifdef __WIN32__
00135 if (overlapped.hEvent)
00136 CloseHandle(overlapped.hEvent);
00137 #endif
00138 }
00139
00140 void
00141 RemoteConnection::read_at_least(size_t min_len, const OmTime & end_time)
00142 {
00143 DEBUGCALL(REMOTE, string, "RemoteConnection::read_at_least",
00144 min_len << ", " << end_time);
00145
00146 if (buffer.length() >= min_len) return;
00147
00148 #ifdef __WIN32__
00149 HANDLE hin = fd_to_handle(fdin);
00150 do {
00151 char buf[4096];
00152 DWORD received;
00153 BOOL ok = ReadFile(hin, buf, sizeof(buf), &received, &overlapped);
00154 if (!ok) {
00155 int errcode = GetLastError();
00156 if (errcode != ERROR_IO_PENDING)
00157 throw Xapian::NetworkError("read failed", context, -errcode);
00158
00159 DWORD waitrc;
00160 waitrc = WaitForSingleObject(overlapped.hEvent, calc_read_wait_msecs(end_time));
00161 if (waitrc != WAIT_OBJECT_0) {
00162 DEBUGLINE(REMOTE, "read: timeout has expired");
00163 throw Xapian::NetworkTimeoutError("Timeout expired while trying to read", context);
00164 }
00165
00166 if (!GetOverlappedResult(hin, &overlapped, &received, FALSE))
00167 throw Xapian::NetworkError("Failed to get overlapped result",
00168 context, -(int)GetLastError());
00169 }
00170
00171 if (received == 0)
00172 throw Xapian::NetworkError("Received EOF", context);
00173
00174 buffer.append(buf, received);
00175 } while (buffer.length() < min_len);
00176 #else
00177
00178 if (fcntl(fdin, F_SETFL, end_time.is_set() ? O_NONBLOCK : 0) < 0) {
00179 throw Xapian::NetworkError("Failed to set fdin non-blocking-ness",
00180 context, errno);
00181 }
00182
00183 while (true) {
00184 char buf[4096];
00185 ssize_t received = read(fdin, buf, sizeof(buf));
00186
00187 if (received > 0) {
00188 buffer.append(buf, received);
00189 if (buffer.length() >= min_len) return;
00190 continue;
00191 }
00192
00193 if (received == 0)
00194 throw Xapian::NetworkError("Received EOF", context);
00195
00196 DEBUGLINE(REMOTE, "read gave errno = " << strerror(errno));
00197 if (errno == EINTR) continue;
00198
00199 if (errno != EAGAIN)
00200 throw Xapian::NetworkError("read failed", context, errno);
00201
00202 Assert(end_time.is_set());
00203 while (true) {
00204
00205 OmTime time_diff = end_time - OmTime::now();
00206
00207 if (time_diff.sec < 0) {
00208 DEBUGLINE(REMOTE, "read: timeout has expired");
00209 throw Xapian::NetworkTimeoutError("Timeout expired while trying to read", context);
00210 }
00211
00212 struct timeval tv;
00213 tv.tv_sec = time_diff.sec;
00214 tv.tv_usec = time_diff.usec;
00215
00216
00217 fd_set fdset;
00218 FD_ZERO(&fdset);
00219 FD_SET(fdin, &fdset);
00220
00221 int select_result = select(fdin + 1, &fdset, 0, &fdset, &tv);
00222 if (select_result > 0) break;
00223
00224 if (select_result == 0)
00225 throw Xapian::NetworkTimeoutError("Timeout expired while trying to read", context);
00226
00227
00228 if (errno != EINTR)
00229 throw Xapian::NetworkError("select failed during read", context, errno);
00230 }
00231 }
00232 #endif
00233 }
00234
00235 bool
00236 RemoteConnection::ready_to_read() const
00237 {
00238 DEBUGCALL(REMOTE, bool, "RemoteConnection::ready_to_read", "");
00239
00240 if (!buffer.empty()) RETURN(true);
00241
00242
00243 fd_set fdset;
00244 FD_ZERO(&fdset);
00245 FD_SET(fdin, &fdset);
00246
00247
00248
00249
00250 struct timeval tv;
00251 tv.tv_sec = 0;
00252 tv.tv_usec = 100000;
00253 RETURN(select(fdin + 1, &fdset, 0, &fdset, &tv) > 0);
00254 }
00255
00256 void
00257 RemoteConnection::send_message(char type, const string &message, const OmTime & end_time)
00258 {
00259 DEBUGCALL(REMOTE, void, "RemoteConnection::send_message",
00260 type << ", " << message << ", " << end_time);
00261
00262 string header;
00263 header += type;
00264 header += encode_length(message.size());
00265
00266 #ifdef __WIN32__
00267 HANDLE hout = fd_to_handle(fdout);
00268 const string * str = &header;
00269
00270 size_t count = 0;
00271 while (true) {
00272 DWORD n;
00273 BOOL ok = WriteFile(hout, str->data() + count, str->size() - count, &n, &overlapped);
00274 if (!ok) {
00275 int errcode = GetLastError();
00276 if (errcode != ERROR_IO_PENDING)
00277 throw Xapian::NetworkError("write failed", context, -errcode);
00278
00279 DWORD waitrc;
00280 waitrc = WaitForSingleObject(overlapped.hEvent, calc_read_wait_msecs(end_time));
00281 if (waitrc != WAIT_OBJECT_0) {
00282 DEBUGLINE(REMOTE, "write: timeout has expired");
00283 throw Xapian::NetworkTimeoutError("Timeout expired while trying to write", context);
00284 }
00285
00286 if (!GetOverlappedResult(hout, &overlapped, &n, FALSE))
00287 throw Xapian::NetworkError("Failed to get overlapped result",
00288 context, -(int)GetLastError());
00289 }
00290
00291 count += n;
00292 if (count == str->size()) {
00293 if (str == &message || message.empty()) return;
00294 str = &message;
00295 count = 0;
00296 }
00297 }
00298 #else
00299
00300 if (fcntl(fdout, F_SETFL, end_time.is_set() ? O_NONBLOCK : 0) < 0) {
00301 throw Xapian::NetworkError("Failed to set fdout non-blocking-ness",
00302 context, errno);
00303 }
00304
00305 const string * str = &header;
00306
00307 fd_set fdset;
00308 size_t count = 0;
00309 while (true) {
00310
00311
00312 ssize_t n = write(fdout, str->data() + count, str->size() - count);
00313
00314 if (n >= 0) {
00315 count += n;
00316 if (count == str->size()) {
00317 if (str == &message || message.empty()) return;
00318 str = &message;
00319 count = 0;
00320 }
00321 continue;
00322 }
00323
00324 DEBUGLINE(REMOTE, "write gave errno = " << strerror(errno));
00325 if (errno == EINTR) continue;
00326
00327 if (errno != EAGAIN)
00328 throw Xapian::NetworkError("write failed", context, errno);
00329
00330
00331 FD_ZERO(&fdset);
00332 FD_SET(fdout, &fdset);
00333
00334 OmTime time_diff(end_time - OmTime::now());
00335 if (time_diff.sec < 0) {
00336 DEBUGLINE(REMOTE, "write: timeout has expired");
00337 throw Xapian::NetworkTimeoutError("Timeout expired while trying to write", context);
00338 }
00339
00340 struct timeval tv;
00341 tv.tv_sec = time_diff.sec;
00342 tv.tv_usec = time_diff.usec;
00343
00344 int select_result = select(fdout + 1, 0, &fdset, &fdset, &tv);
00345
00346 if (select_result < 0) {
00347 if (errno == EINTR) {
00348
00349
00350
00351 continue;
00352 }
00353 throw Xapian::NetworkError("select failed during write", context, errno);
00354 }
00355
00356 if (select_result == 0)
00357 throw Xapian::NetworkTimeoutError("Timeout expired while trying to write", context);
00358 }
00359 #endif
00360 }
00361
00362 char
00363 RemoteConnection::get_message(string &result, const OmTime & end_time)
00364 {
00365 DEBUGCALL(REMOTE, char, "RemoteConnection::get_message",
00366 "[result], " << end_time);
00367
00368 read_at_least(2, end_time);
00369 size_t len = static_cast<unsigned char>(buffer[1]);
00370 read_at_least(len + 2, end_time);
00371 if (len != 0xff) {
00372 result.assign(buffer.data() + 2, len);
00373 char type = buffer[0];
00374 buffer.erase(0, len + 2);
00375 RETURN(type);
00376 }
00377 len = 0;
00378 string::const_iterator i = buffer.begin() + 2;
00379 unsigned char ch;
00380 int shift = 0;
00381 do {
00382 if (i == buffer.end() || shift > 28) {
00383
00384 throw Xapian::NetworkError("Insane message length specified!");
00385 }
00386 ch = *i++;
00387 len |= size_t(ch & 0x7f) << shift;
00388 shift += 7;
00389 } while ((ch & 0x80) == 0);
00390 len += 255;
00391 size_t header_len = (i - buffer.begin());
00392 read_at_least(header_len + len, end_time);
00393 result.assign(buffer.data() + header_len, len);
00394 char type = buffer[0];
00395 buffer.erase(0, header_len + len);
00396 RETURN(type);
00397 }
00398
00399 void
00400 RemoteConnection::do_close(bool wait)
00401 {
00402 DEBUGCALL(REMOTE, void, "RemoteConnection::do_close", wait);
00403
00404 if (fdout == -1) return;
00405
00406 if (wait) {
00407 try {
00408 send_message(MSG_SHUTDOWN, string(), OmTime());
00409 } catch (...) {
00410 }
00411 #ifdef __WIN32__
00412 HANDLE hin = fd_to_handle(fdin);
00413 char dummy;
00414 DWORD received;
00415 BOOL ok = ReadFile(hin, &dummy, 1, &received, &overlapped);
00416 if (!ok && GetLastError() == ERROR_IO_PENDING) {
00417
00418 (void)WaitForSingleObject(overlapped.hEvent, INFINITE);
00419 }
00420 #else
00421
00422
00423 fd_set fdset;
00424 FD_ZERO(&fdset);
00425 FD_SET(fdin, &fdset);
00426 int res;
00427 do {
00428 res = select(fdin + 1, &fdset, 0, &fdset, NULL);
00429 } while (res < 0 && errno == EINTR);
00430 #endif
00431 }
00432 close_fd_or_socket(fdin);
00433 if (fdin != fdout) close_fd_or_socket(fdout);
00434 fdout = -1;
00435 }
00436
00437 #ifdef __WIN32__
00438 DWORD
00439 RemoteConnection::calc_read_wait_msecs(const OmTime & end_time)
00440 {
00441 if (!end_time.is_set())
00442 return INFINITE;
00443
00444
00445 OmTime now(OmTime::now());
00446
00447 DWORD msecs;
00448
00449
00450 if (now > end_time) {
00451 throw Xapian::NetworkTimeoutError("Timeout expired before starting read", context);
00452 }
00453 OmTime time_diff = end_time - now;
00454 msecs = time_diff.sec * 1000 + time_diff.usec / 1000;
00455 return msecs;
00456 }
00457 #endif