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 }