0
|
1 /*
|
|
2 * Copyright 2000-2003 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or
|
|
21 * have any 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 }
|