1 /* 2 * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include <stdio.h> 26 27 // This file is currently used for os/solaris/agent too. At some point in time 28 // the source will be reorganized to avoid these ifdefs. 29 30 #ifdef __sun 31 #include <string.h> 32 #include <inttypes.h> 33 #include <sys/byteorder.h> 34 #endif 35 36 #include "IOBuf.hpp" 37 38 // Formats for printing pointers 39 #ifdef _LP64 40 # define INTPTR_FORMAT "0x%016lx" 41 #else /* ! _LP64 */ 42 # define INTPTR_FORMAT "0x%08lx" 43 #endif /* _LP64 */ 44 45 // Uncomment the #define below to get messages on stderr 46 // #define DEBUGGING 47 48 IOBuf::IOBuf(int inLen, int outLen) { 49 inBuf = new Buffer(inLen); 50 outBuf = new Buffer(outLen); 51 fd = INVALID_SOCKET; 52 outHandle = NULL; 53 usingSocket = true; 54 reset(); 55 } 56 57 IOBuf::~IOBuf() { 58 delete inBuf; 59 delete outBuf; 60 } 61 62 void 63 IOBuf::setSocket(SOCKET sock) { 64 fd = sock; 65 usingSocket = true; 66 } 67 68 // Reading/writing files is only needed and used on windows. 69 #ifdef WIN32 70 void 71 IOBuf::setOutputFileHandle(HANDLE handle) { 72 outHandle = handle; 73 usingSocket = false; 74 } 75 #endif 76 77 void 78 IOBuf::reset() { 79 gotDataLastTime = false; 80 state = TEXT_STATE; 81 binPos = 0; 82 binLength = 0; 83 } 84 85 IOBuf::ReadLineResult 86 IOBuf::tryReadLine() { 87 return doReadLine(false); 88 } 89 90 char* 91 IOBuf::readLine() { 92 ReadLineResult rr = doReadLine(true); 93 if (rr != RL_GOT_DATA) { 94 return NULL; 95 } 96 return getLine(); 97 } 98 99 IOBuf::ReadLineResult 100 IOBuf::doReadLine(bool shouldWait) { 101 102 if (!usingSocket) { 103 return IOBuf::RL_ERROR; 104 } 105 106 if (gotDataLastTime) { 107 curLine.clear(); 108 } 109 110 int c; 111 do { 112 c = readChar(shouldWait); 113 if (c >= 0) { 114 Action act = processChar((char) c); 115 if (act == GOT_LINE) { 116 curLine.push_back('\0'); 117 gotDataLastTime = true; 118 return IOBuf::RL_GOT_DATA; 119 } else if (act == SKIP_EOL_CHAR) { 120 // Do nothing 121 } else { 122 curLine.push_back((char) c); 123 } 124 } 125 } while (shouldWait || c >= 0); 126 127 gotDataLastTime = false; 128 return IOBuf::RL_NO_DATA; 129 } 130 131 bool 132 IOBuf::flushImpl(bool moreDataToCome) { 133 int numWritten = 0; 134 135 #ifdef WIN32 136 // When running on Windows and using IOBufs for inter-process 137 // communication, we need to write metadata into the stream 138 // indicating how many bytes are coming down. Five bytes are written 139 // per flush() call, four containing the integer number of bytes 140 // coming (not including the five-byte header) and one (a 0 or 1) 141 // indicating whether there is more data coming. 142 if (!usingSocket) { 143 int numToWrite = outBuf->drainRemaining(); 144 char moreToCome = (moreDataToCome ? 1 : 0); 145 DWORD numBytesWritten; 146 if (!WriteFile(outHandle, &numToWrite, sizeof(int), &numBytesWritten, NULL)) { 147 return false; 148 } 149 if (numBytesWritten != sizeof(int)) { 150 return false; 151 } 152 if (!WriteFile(outHandle, &moreToCome, 1, &numBytesWritten, NULL)) { 153 return false; 154 } 155 if (numBytesWritten != 1) { 156 return false; 157 } 158 } 159 #endif 160 161 while (outBuf->drainRemaining() != 0) { 162 #ifdef DEBUGGING 163 fprintf(stderr, "Flushing %d bytes\n", outBuf->drainRemaining()); 164 #endif 165 if (usingSocket) { 166 numWritten = send(fd, outBuf->drainPos(), outBuf->drainRemaining(), 0); 167 } else { 168 #ifdef WIN32 169 DWORD numBytesWritten; 170 if (!WriteFile(outHandle, outBuf->drainPos(), outBuf->drainRemaining(), &numBytesWritten, NULL)) { 171 numWritten = -1; 172 } else { 173 numWritten = numBytesWritten; 174 } 175 #endif 176 } 177 if (numWritten != -1) { 178 #ifdef DEBUGGING 179 fprintf(stderr, "Flushed %d bytes\n", numWritten); 180 #endif 181 outBuf->incrDrainPos(numWritten); 182 } else { 183 return false; 184 } 185 } 186 187 outBuf->compact(); 188 189 return true; 190 } 191 192 int 193 IOBuf::readChar(bool block) { 194 do { 195 int c = inBuf->readByte(); 196 if (c >= 0) { 197 return c; 198 } 199 // See whether we need to compact the input buffer 200 if (inBuf->remaining() < inBuf->size() / 2) { 201 inBuf->compact(); 202 } 203 // See whether socket is ready 204 fd_set fds; 205 FD_ZERO(&fds); 206 FD_SET(fd, &fds); 207 struct timeval timeout; 208 timeout.tv_sec = 0; 209 timeout.tv_usec = 0; 210 if (block || select(1 + fd, &fds, NULL, NULL, &timeout) > 0) { 211 if (block || FD_ISSET(fd, &fds)) { 212 #ifdef DEBUGGING 213 int b = (block ? 1 : 0); 214 fprintf(stderr, "calling recv: block = %d\n", b); 215 #endif 216 // Read data from socket 217 int numRead = recv(fd, inBuf->fillPos(), inBuf->remaining(), 0); 218 if (numRead < 0) { 219 #ifdef DEBUGGING 220 fprintf(stderr, "recv failed\n"); 221 #endif 222 return -1; 223 } 224 inBuf->incrFillPos(numRead); 225 } 226 } 227 } while (block); 228 229 return inBuf->readByte(); 230 } 231 232 char* 233 IOBuf::getLine() { 234 #ifdef DEBUGGING 235 fprintf(stderr, "Returning (first 10 chars) \"%.10s\"\n", curLine.begin()); 236 #endif 237 return curLine.begin(); 238 } 239 240 bool 241 IOBuf::flush() { 242 return flushImpl(false); 243 } 244 245 bool 246 IOBuf::writeString(const char* str) { 247 int len = strlen(str); 248 249 if (len > outBuf->size()) { 250 return false; 251 } 252 253 if (len > outBuf->remaining()) { 254 if (!flushImpl(true)) { 255 return false; 256 } 257 } 258 259 // NOTE we do not copy the null terminator of the string. 260 261 strncpy(outBuf->fillPos(), str, len); 262 outBuf->incrFillPos(len); 263 return true; 264 } 265 266 bool 267 IOBuf::writeInt(int val) { 268 char buf[128]; 269 sprintf(buf, "%d", val); 270 return writeString(buf); 271 } 272 273 bool 274 IOBuf::writeUnsignedInt(unsigned int val) { 275 char buf[128]; 276 sprintf(buf, "%u", val); 277 return writeString(buf); 278 } 279 280 bool 281 IOBuf::writeBoolAsInt(bool val) { 282 if (val) { 283 return writeString("1"); 284 } else { 285 return writeString("0"); 286 } 287 } 288 289 bool 290 IOBuf::writeAddress(void* val) { 291 char buf[128]; 292 sprintf(buf, INTPTR_FORMAT, val); 293 return writeString(buf); 294 } 295 296 bool 297 IOBuf::writeSpace() { 298 return writeString(" "); 299 } 300 301 bool 302 IOBuf::writeEOL() { 303 return writeString("\n\r"); 304 } 305 306 bool 307 IOBuf::writeBinChar(char c) { 308 return writeBinBuf((char*) &c, sizeof(c)); 309 } 310 311 bool 312 IOBuf::writeBinUnsignedShort(unsigned short i) { 313 i = htons(i); 314 return writeBinBuf((char*) &i, sizeof(i)); 315 } 316 317 bool 318 IOBuf::writeBinUnsignedInt(unsigned int i) { 319 i = htonl(i); 320 return writeBinBuf((char*) &i, sizeof(i)); 321 } 322 323 bool 324 IOBuf::writeBinBuf(char* buf, int size) { 325 while (size > 0) { 326 int spaceRemaining = outBuf->remaining(); 327 if (spaceRemaining == 0) { 328 if (!flushImpl(true)) { 329 return false; 330 } 331 spaceRemaining = outBuf->remaining(); 332 } 333 int toCopy = (size > spaceRemaining) ? spaceRemaining : size; 334 memcpy(outBuf->fillPos(), buf, toCopy); 335 outBuf->incrFillPos(toCopy); 336 buf += toCopy; 337 size -= toCopy; 338 if (size > 0) { 339 if (!flushImpl(true)) { 340 return false; 341 } 342 } 343 } 344 return true; 345 } 346 347 #ifdef WIN32 348 IOBuf::FillState 349 IOBuf::fillFromFileHandle(HANDLE fh, DWORD* numBytesRead) { 350 int totalToRead; 351 char moreToCome; 352 353 outBuf->compact(); 354 355 DWORD numRead; 356 if (!ReadFile(fh, &totalToRead, sizeof(int), &numRead, NULL)) { 357 return FAILED; 358 } 359 if (numRead != sizeof(int)) { 360 return FAILED; 361 } 362 if (!ReadFile(fh, &moreToCome, 1, &numRead, NULL)) { 363 return FAILED; 364 } 365 if (numRead != 1) { 366 return FAILED; 367 } 368 if (outBuf->remaining() < totalToRead) { 369 return FAILED; 370 } 371 372 int tmp = totalToRead; 373 374 while (totalToRead > 0) { 375 if (!ReadFile(fh, outBuf->fillPos(), totalToRead, &numRead, NULL)) { 376 return FAILED; 377 } 378 outBuf->incrFillPos((int) numRead); 379 totalToRead -= numRead; 380 } 381 382 *numBytesRead = tmp; 383 return ((moreToCome == 0) ? DONE : MORE_DATA_PENDING); 384 } 385 #endif 386 387 bool 388 IOBuf::isBinEscapeChar(char c) { 389 return (c == '|'); 390 } 391 392 IOBuf::Action 393 IOBuf::processChar(char c) { 394 Action action = NO_ACTION; 395 switch (state) { 396 case TEXT_STATE: { 397 // Looking for text char, bin escape char, or EOL 398 if (isBinEscapeChar(c)) { 399 #ifdef DEBUGGING 400 fprintf(stderr, "[a: '%c'] ", inBuf[0]); 401 #endif 402 binPos = 0; 403 #ifdef DEBUGGING 404 fprintf(stderr, "[b: '%c'] ", inBuf[0]); 405 #endif 406 binLength = 0; 407 #ifdef DEBUGGING 408 fprintf(stderr, "[c: '%c'] ", inBuf[0]); 409 #endif 410 state = BIN_STATE; 411 #ifdef DEBUGGING 412 fprintf(stderr, "[d: '%c'] ", inBuf[0]); 413 #endif 414 #ifdef DEBUGGING 415 fprintf(stderr, "\nSwitching to BIN_STATE\n"); 416 #endif 417 } else if (isEOL(c)) { 418 state = EOL_STATE; 419 action = GOT_LINE; 420 #ifdef DEBUGGING 421 fprintf(stderr, "\nSwitching to EOL_STATE (GOT_LINE)\n"); 422 #endif 423 } 424 #ifdef DEBUGGING 425 else { 426 fprintf(stderr, "'%c' ", c); 427 fflush(stderr); 428 } 429 #endif 430 break; 431 } 432 433 case BIN_STATE: { 434 // Seeking to finish read of input 435 if (binPos < 4) { 436 int cur = c & 0xFF; 437 binLength <<= 8; 438 binLength |= cur; 439 ++binPos; 440 } else { 441 #ifdef DEBUGGING 442 fprintf(stderr, "Reading binary byte %d of %d\n", 443 binPos - 4, binLength); 444 #endif 445 ++binPos; 446 if (binPos == 4 + binLength) { 447 state = TEXT_STATE; 448 #ifdef DEBUGGING 449 fprintf(stderr, "Switching to TEXT_STATE\n"); 450 #endif 451 } 452 } 453 break; 454 } 455 456 case EOL_STATE: { 457 // More EOL characters just cause us to re-enter this state 458 if (isEOL(c)) { 459 action = SKIP_EOL_CHAR; 460 } else if (isBinEscapeChar(c)) { 461 binPos = 0; 462 binLength = 0; 463 state = BIN_STATE; 464 } else { 465 state = TEXT_STATE; 466 #ifdef DEBUGGING 467 fprintf(stderr, "'%c' ", c); 468 fflush(stderr); 469 #endif 470 } 471 break; 472 } 473 474 } // switch 475 476 return action; 477 } 478 479 480 bool 481 IOBuf::isEOL(char c) { 482 #ifdef WIN32 483 return ((c == '\n') || (c == '\r')); 484 #elif defined(__sun) 485 return c == '\n'; 486 #else 487 #error Please port isEOL() to your platform 488 return false; 489 #endif 490 }