The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 1 | // |
| 2 | // Copyright 2005 The Android Open Source Project |
| 3 | // |
| 4 | // Message stream abstraction. |
| 5 | // |
| 6 | #include "MessageStream.h" |
| 7 | #include "LogBundle.h" |
| 8 | |
| 9 | #include "utils/Log.h" |
| 10 | |
| 11 | #include <string.h> |
| 12 | #include <assert.h> |
| 13 | |
| 14 | using namespace android; |
| 15 | |
| 16 | /* |
| 17 | * =========================================================================== |
| 18 | * Message |
| 19 | * =========================================================================== |
| 20 | */ |
| 21 | |
| 22 | /* |
| 23 | * Send a blob of raw data. |
| 24 | */ |
| 25 | void Message::setRaw(const unsigned char* data, int len, Cleanup cleanup) |
| 26 | { |
| 27 | reset(); |
| 28 | |
| 29 | mData = const_cast<unsigned char*>(data); |
| 30 | mLength = len; |
| 31 | mCleanup = cleanup; |
| 32 | mType = kTypeRaw; |
| 33 | } |
| 34 | |
| 35 | /* |
| 36 | * Send a "name=value" config pair. |
| 37 | */ |
| 38 | void Message::setConfig(const char* name, const char* value) |
| 39 | { |
| 40 | reset(); |
| 41 | |
| 42 | assert(name != NULL && value != NULL); |
| 43 | |
| 44 | int nlen = strlen(name) +1; |
| 45 | int vlen = strlen(value) +1; |
| 46 | mData = new unsigned char[nlen+vlen]; |
| 47 | mCleanup = kCleanupDelete; |
| 48 | mLength = nlen + vlen; |
| 49 | mType = kTypeConfig; |
| 50 | |
| 51 | memcpy(mData, name, nlen); |
| 52 | memcpy(mData + nlen, value, vlen); |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | * Try to return the contents of the message as if it were a name/value pair. |
| 57 | */ |
| 58 | bool Message::getConfig(const char** pName, const char** pValue) |
| 59 | { |
| 60 | if (mLength < 2) |
| 61 | return false; |
| 62 | assert(mData != NULL); |
| 63 | |
| 64 | *pName = (const char*) mData; |
| 65 | *pValue = (const char*) (mData + strlen((char*)mData) +1); |
| 66 | return true; |
| 67 | } |
| 68 | |
| 69 | /* |
| 70 | * Send a command/arg pair. |
| 71 | */ |
| 72 | void Message::setCommand(int cmd, int arg) |
| 73 | { |
| 74 | reset(); |
| 75 | |
| 76 | mData = new unsigned char[sizeof(int) * 2]; |
| 77 | mCleanup = kCleanupDelete; |
| 78 | mLength = sizeof(int) * 2; |
| 79 | mType = kTypeCommand; |
| 80 | |
| 81 | int* pInt = (int*) mData; |
| 82 | pInt[0] = cmd; |
| 83 | pInt[1] = arg; |
| 84 | } |
| 85 | |
| 86 | /* |
| 87 | * Send a command with 3 args instead of just one. |
| 88 | */ |
| 89 | void Message::setCommandExt(int cmd, int arg0, int arg1, int arg2) |
| 90 | { |
| 91 | reset(); |
| 92 | |
| 93 | mData = new unsigned char[sizeof(int) * 4]; |
| 94 | mCleanup = kCleanupDelete; |
| 95 | mLength = sizeof(int) * 4; |
| 96 | mType = kTypeCommandExt; |
| 97 | |
| 98 | int* pInt = (int*) mData; |
| 99 | pInt[0] = cmd; |
| 100 | pInt[1] = arg0; |
| 101 | pInt[2] = arg1; |
| 102 | pInt[3] = arg2; |
| 103 | } |
| 104 | |
| 105 | /* |
| 106 | * Try to return the contents of the message as if it were a "command". |
| 107 | */ |
| 108 | bool Message::getCommand(int* pCmd, int* pArg) |
| 109 | { |
| 110 | if (mLength != sizeof(int) * 2) { |
| 111 | LOG(LOG_WARN, "", "type is %d, len is %d\n", mType, mLength); |
| 112 | return false; |
| 113 | } |
| 114 | assert(mData != NULL); |
| 115 | |
| 116 | const int* pInt = (const int*) mData; |
| 117 | *pCmd = pInt[0]; |
| 118 | *pArg = pInt[1]; |
| 119 | |
| 120 | return true; |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | * Serialize a log message. |
| 125 | * |
| 126 | * DO NOT call LOG() from here. |
| 127 | */ |
| 128 | void Message::setLogBundle(const android_LogBundle* pBundle) |
| 129 | { |
| 130 | reset(); |
| 131 | |
| 132 | /* get string lengths; we add one here to include the '\0' */ |
| 133 | int tagLen, msgLen; |
| 134 | tagLen = strlen(pBundle->tag) + 1; |
| 135 | size_t i; |
| 136 | msgLen = 0; |
| 137 | for (i=0; i<pBundle->msgCount; i++) msgLen += pBundle->msgVec[i].iov_len; |
| 138 | msgLen += 1; |
| 139 | |
| 140 | /* set up the structure */ |
| 141 | mCleanup = kCleanupDelete; |
| 142 | mLength = sizeof(pBundle->when) + |
| 143 | sizeof(pBundle->priority) + |
| 144 | sizeof(pBundle->pid) + |
| 145 | tagLen + |
| 146 | msgLen; |
| 147 | mData = new unsigned char[mLength]; |
| 148 | mType = kTypeLogBundle; |
| 149 | |
| 150 | unsigned char* pCur = mData; |
| 151 | |
| 152 | /* copy the stuff over */ |
| 153 | *((time_t*)pCur) = pBundle->when; |
| 154 | pCur += sizeof(pBundle->when); |
| 155 | *((android_LogPriority*)pCur) = pBundle->priority; |
| 156 | pCur += sizeof(pBundle->priority); |
| 157 | *((pid_t*)pCur) = pBundle->pid; |
| 158 | pCur += sizeof(pBundle->pid); |
| 159 | memcpy(pCur, pBundle->tag, tagLen); |
| 160 | pCur += tagLen; |
| 161 | for (i=0; i<pBundle->msgCount; i++) { |
| 162 | memcpy(pCur, pBundle->msgVec[i].iov_base, pBundle->msgVec[i].iov_len); |
| 163 | pCur += pBundle->msgVec[i].iov_len; |
| 164 | } |
| 165 | *pCur++ = 0; |
| 166 | |
| 167 | assert(pCur - mData == mLength); |
| 168 | } |
| 169 | |
| 170 | /* |
| 171 | * Extract the components of a log bundle. |
| 172 | * |
| 173 | * We're just returning points inside the message buffer, so the caller |
| 174 | * will need to copy them out before the next reset(). |
| 175 | */ |
| 176 | bool Message::getLogBundle(android_LogBundle* pBundle) |
| 177 | { |
| 178 | if (mLength < (int)(sizeof(time_t) + sizeof(int)*2 + 4)) { |
| 179 | LOG(LOG_WARN, "", "type is %d, len is %d, too small\n", |
| 180 | mType, mLength); |
| 181 | return false; |
| 182 | } |
| 183 | assert(mData != NULL); |
| 184 | |
| 185 | unsigned char* pCur = mData; |
| 186 | |
| 187 | pBundle->when = *((time_t*) pCur); |
| 188 | pCur += sizeof(pBundle->when); |
| 189 | pBundle->priority = *((android_LogPriority*) pCur); |
| 190 | pCur += sizeof(pBundle->priority); |
| 191 | pBundle->pid = *((pid_t*) pCur); |
| 192 | pCur += sizeof(pBundle->pid); |
| 193 | pBundle->tag = (const char*) pCur; |
| 194 | pCur += strlen((const char*) pCur) +1; |
| 195 | mVec.iov_base = (char*) pCur; |
| 196 | mVec.iov_len = strlen((const char*) pCur); |
| 197 | pBundle->msgVec = &mVec; |
| 198 | pBundle->msgCount = 1; |
| 199 | pCur += mVec.iov_len +1; |
| 200 | |
| 201 | if (pCur - mData != mLength) { |
| 202 | LOG(LOG_WARN, "", "log bundle rcvd %d, used %d\n", mLength, |
| 203 | (int) (pCur - mData)); |
| 204 | return false; |
| 205 | } |
| 206 | |
| 207 | return true; |
| 208 | } |
| 209 | |
| 210 | /* |
| 211 | * Read the next event from the pipe. |
| 212 | * |
| 213 | * This is not expected to work well when multiple threads are reading. |
| 214 | */ |
| 215 | bool Message::read(Pipe* pPipe, bool wait) |
| 216 | { |
| 217 | if (pPipe == NULL) |
| 218 | return false; |
| 219 | assert(pPipe->isCreated()); |
| 220 | |
| 221 | if (!wait) { |
| 222 | if (!pPipe->readReady()) |
| 223 | return false; |
| 224 | } |
| 225 | |
| 226 | reset(); |
| 227 | |
| 228 | unsigned char header[4]; |
| 229 | if (pPipe->read(header, 4) != 4) |
| 230 | return false; |
| 231 | |
| 232 | mType = (MessageType) header[2]; |
| 233 | mLength = header[0] | header[1] << 8; |
| 234 | mLength -= 2; // we already read two of them in the header |
| 235 | |
| 236 | if (mLength > 0) { |
| 237 | int actual; |
| 238 | |
| 239 | mData = new unsigned char[mLength]; |
| 240 | if (mData == NULL) { |
| 241 | LOG(LOG_ERROR, "", "alloc failed\n"); |
| 242 | return false; |
| 243 | } |
| 244 | mCleanup = kCleanupDelete; |
| 245 | |
| 246 | actual = pPipe->read(mData, mLength); |
| 247 | if (actual != mLength) { |
| 248 | LOG(LOG_WARN, "", "failed reading message body (%d of %d bytes)\n", |
| 249 | actual, mLength); |
| 250 | return false; |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | return true; |
| 255 | } |
| 256 | |
| 257 | /* |
| 258 | * Write this event to a pipe. |
| 259 | * |
| 260 | * It would be easiest to write the header and message body with two |
| 261 | * separate calls, but that will occasionally fail on multithreaded |
| 262 | * systems when the writes are interleaved. We have to allocate a |
| 263 | * temporary buffer, copy the data, and write it all at once. This |
| 264 | * would be easier with writev(), but we can't rely on having that. |
| 265 | * |
| 266 | * DO NOT call LOG() from here, as we could be in the process of sending |
| 267 | * a log message. |
| 268 | */ |
| 269 | bool Message::write(Pipe* pPipe) const |
| 270 | { |
| 271 | char tmpBuf[128]; |
| 272 | char* writeBuf = tmpBuf; |
| 273 | bool result = false; |
| 274 | int kHeaderLen = 4; |
| 275 | |
| 276 | if (pPipe == NULL) |
| 277 | return false; |
| 278 | assert(pPipe->isCreated()); |
| 279 | |
| 280 | if (mData == NULL || mLength < 0) |
| 281 | return false; |
| 282 | |
| 283 | /* if it doesn't fit in stack buffer, allocate space */ |
| 284 | if (mLength + kHeaderLen > (int) sizeof(tmpBuf)) { |
| 285 | writeBuf = new char[mLength + kHeaderLen]; |
| 286 | if (writeBuf == NULL) |
| 287 | goto bail; |
| 288 | } |
| 289 | |
| 290 | /* |
| 291 | * The current value of "mLength" does not include the 4-byte header. |
| 292 | * Two of the 4 header bytes are included in the length we output |
| 293 | * (the type byte and the pad byte), so we adjust mLength. |
| 294 | */ |
| 295 | writeBuf[0] = (unsigned char) (mLength + kHeaderLen -2); |
| 296 | writeBuf[1] = (unsigned char) ((mLength + kHeaderLen -2) >> 8); |
| 297 | writeBuf[2] = (unsigned char) mType; |
| 298 | writeBuf[3] = 0; |
| 299 | if (mLength > 0) |
| 300 | memcpy(writeBuf + kHeaderLen, mData, mLength); |
| 301 | |
| 302 | int actual; |
| 303 | |
| 304 | actual = pPipe->write(writeBuf, mLength + kHeaderLen); |
| 305 | if (actual != mLength + kHeaderLen) { |
| 306 | fprintf(stderr, |
| 307 | "Message::write failed writing message body (%d of %d bytes)\n", |
| 308 | actual, mLength + kHeaderLen); |
| 309 | goto bail; |
| 310 | } |
| 311 | |
| 312 | result = true; |
| 313 | |
| 314 | bail: |
| 315 | if (writeBuf != tmpBuf) |
| 316 | delete[] writeBuf; |
| 317 | return result; |
| 318 | } |
| 319 | |
| 320 | |
| 321 | /* |
| 322 | * =========================================================================== |
| 323 | * MessageStream |
| 324 | * =========================================================================== |
| 325 | */ |
| 326 | |
| 327 | /* |
| 328 | * Get ready to go. |
| 329 | */ |
| 330 | bool MessageStream::init(Pipe* readPipe, Pipe* writePipe, bool initiateHello) |
| 331 | { |
| 332 | assert(mReadPipe == NULL && mWritePipe == NULL); // only once |
| 333 | |
| 334 | /* |
| 335 | * Swap "hello" messages. |
| 336 | * |
| 337 | * In a more robust implementation, this would include version numbers |
| 338 | * and capability flags. |
| 339 | */ |
| 340 | if (initiateHello) { |
| 341 | long data = kHelloMsg; |
| 342 | Message msg; |
| 343 | |
| 344 | /* send hello */ |
| 345 | msg.setRaw((unsigned char*) &data, sizeof(data), |
| 346 | Message::kCleanupNoDelete); |
| 347 | if (!msg.write(writePipe)) { |
| 348 | LOG(LOG_WARN, "", "hello write failed in stream init\n"); |
| 349 | return false; |
| 350 | } |
| 351 | |
| 352 | LOG(LOG_DEBUG, "", "waiting for peer to ack my hello\n"); |
| 353 | |
| 354 | /* wait for the ack */ |
| 355 | if (!msg.read(readPipe, true)) { |
| 356 | LOG(LOG_WARN, "", "hello ack read failed in stream init\n"); |
| 357 | return false; |
| 358 | } |
| 359 | |
| 360 | const long* pAck; |
| 361 | pAck = (const long*) msg.getData(); |
| 362 | if (pAck == NULL || *pAck != kHelloAckMsg) { |
| 363 | LOG(LOG_WARN, "", "hello ack was bad\n"); |
| 364 | return false; |
| 365 | } |
| 366 | } else { |
| 367 | long data = kHelloAckMsg; |
| 368 | Message msg; |
| 369 | |
| 370 | LOG(LOG_DEBUG, "", "waiting for hello from peer\n"); |
| 371 | |
| 372 | /* wait for the hello */ |
| 373 | if (!msg.read(readPipe, true)) { |
| 374 | LOG(LOG_WARN, "", "hello read failed in stream init\n"); |
| 375 | return false; |
| 376 | } |
| 377 | |
| 378 | const long* pAck; |
| 379 | pAck = (const long*) msg.getData(); |
| 380 | if (pAck == NULL || *pAck != kHelloMsg) { |
| 381 | LOG(LOG_WARN, "", "hello was bad\n"); |
| 382 | return false; |
| 383 | } |
| 384 | |
| 385 | /* send hello ack */ |
| 386 | msg.setRaw((unsigned char*) &data, sizeof(data), |
| 387 | Message::kCleanupNoDelete); |
| 388 | if (!msg.write(writePipe)) { |
| 389 | LOG(LOG_WARN, "", "hello ack write failed in stream init\n"); |
| 390 | return false; |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | /* success, set up our local stuff */ |
| 395 | mReadPipe = readPipe; |
| 396 | mWritePipe = writePipe; |
| 397 | |
| 398 | //LOG(LOG_DEBUG, "", "init success\n"); |
| 399 | |
| 400 | return true; |
| 401 | } |
| 402 | |