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