"long" may be 64 bits, leading to confusion: hello ack was bad (7f3331455221 vs 31455221) This changes it to int32_t. Change-Id: I17e8bd0902eda6aeda36d54f0530bdf02fe3ae9a
405 lines
10 KiB
C++
405 lines
10 KiB
C++
//
|
|
// Copyright 2005 The Android Open Source Project
|
|
//
|
|
// Message stream abstraction.
|
|
//
|
|
#include "MessageStream.h"
|
|
#include "LogBundle.h"
|
|
|
|
#include "utils/Log.h"
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
using namespace android;
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* Message
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Send a blob of raw data.
|
|
*/
|
|
void Message::setRaw(const unsigned char* data, int len, Cleanup cleanup)
|
|
{
|
|
reset();
|
|
|
|
mData = const_cast<unsigned char*>(data);
|
|
mLength = len;
|
|
mCleanup = cleanup;
|
|
mType = kTypeRaw;
|
|
}
|
|
|
|
/*
|
|
* Send a "name=value" config pair.
|
|
*/
|
|
void Message::setConfig(const char* name, const char* value)
|
|
{
|
|
reset();
|
|
|
|
assert(name != NULL && value != NULL);
|
|
|
|
int nlen = strlen(name) +1;
|
|
int vlen = strlen(value) +1;
|
|
mData = new unsigned char[nlen+vlen];
|
|
mCleanup = kCleanupDelete;
|
|
mLength = nlen + vlen;
|
|
mType = kTypeConfig;
|
|
|
|
memcpy(mData, name, nlen);
|
|
memcpy(mData + nlen, value, vlen);
|
|
}
|
|
|
|
/*
|
|
* Try to return the contents of the message as if it were a name/value pair.
|
|
*/
|
|
bool Message::getConfig(const char** pName, const char** pValue)
|
|
{
|
|
if (mLength < 2)
|
|
return false;
|
|
assert(mData != NULL);
|
|
|
|
*pName = (const char*) mData;
|
|
*pValue = (const char*) (mData + strlen((char*)mData) +1);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Send a command/arg pair.
|
|
*/
|
|
void Message::setCommand(int cmd, int arg)
|
|
{
|
|
reset();
|
|
|
|
mData = new unsigned char[sizeof(int) * 2];
|
|
mCleanup = kCleanupDelete;
|
|
mLength = sizeof(int) * 2;
|
|
mType = kTypeCommand;
|
|
|
|
int* pInt = (int*) mData;
|
|
pInt[0] = cmd;
|
|
pInt[1] = arg;
|
|
}
|
|
|
|
/*
|
|
* Send a command with 3 args instead of just one.
|
|
*/
|
|
void Message::setCommandExt(int cmd, int arg0, int arg1, int arg2)
|
|
{
|
|
reset();
|
|
|
|
mData = new unsigned char[sizeof(int) * 4];
|
|
mCleanup = kCleanupDelete;
|
|
mLength = sizeof(int) * 4;
|
|
mType = kTypeCommandExt;
|
|
|
|
int* pInt = (int*) mData;
|
|
pInt[0] = cmd;
|
|
pInt[1] = arg0;
|
|
pInt[2] = arg1;
|
|
pInt[3] = arg2;
|
|
}
|
|
|
|
/*
|
|
* Try to return the contents of the message as if it were a "command".
|
|
*/
|
|
bool Message::getCommand(int* pCmd, int* pArg)
|
|
{
|
|
if (mLength != sizeof(int) * 2) {
|
|
LOG(LOG_WARN, "", "type is %d, len is %d\n", mType, mLength);
|
|
return false;
|
|
}
|
|
assert(mData != NULL);
|
|
|
|
const int* pInt = (const int*) mData;
|
|
*pCmd = pInt[0];
|
|
*pArg = pInt[1];
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Serialize a log message.
|
|
*
|
|
* DO NOT call LOG() from here.
|
|
*/
|
|
void Message::setLogBundle(const android_LogBundle* pBundle)
|
|
{
|
|
reset();
|
|
|
|
/* get string lengths; we add one here to include the '\0' */
|
|
int tagLen, msgLen;
|
|
tagLen = strlen(pBundle->tag) + 1;
|
|
size_t i;
|
|
msgLen = 0;
|
|
for (i=0; i<pBundle->msgCount; i++) msgLen += pBundle->msgVec[i].iov_len;
|
|
msgLen += 1;
|
|
|
|
/* set up the structure */
|
|
mCleanup = kCleanupDelete;
|
|
mLength = sizeof(pBundle->when) +
|
|
sizeof(pBundle->priority) +
|
|
sizeof(pBundle->pid) +
|
|
tagLen +
|
|
msgLen;
|
|
mData = new unsigned char[mLength];
|
|
mType = kTypeLogBundle;
|
|
|
|
unsigned char* pCur = mData;
|
|
|
|
/* copy the stuff over */
|
|
*((time_t*)pCur) = pBundle->when;
|
|
pCur += sizeof(pBundle->when);
|
|
*((android_LogPriority*)pCur) = pBundle->priority;
|
|
pCur += sizeof(pBundle->priority);
|
|
*((pid_t*)pCur) = pBundle->pid;
|
|
pCur += sizeof(pBundle->pid);
|
|
memcpy(pCur, pBundle->tag, tagLen);
|
|
pCur += tagLen;
|
|
for (i=0; i<pBundle->msgCount; i++) {
|
|
memcpy(pCur, pBundle->msgVec[i].iov_base, pBundle->msgVec[i].iov_len);
|
|
pCur += pBundle->msgVec[i].iov_len;
|
|
}
|
|
*pCur++ = 0;
|
|
|
|
assert(pCur - mData == mLength);
|
|
}
|
|
|
|
/*
|
|
* Extract the components of a log bundle.
|
|
*
|
|
* We're just returning points inside the message buffer, so the caller
|
|
* will need to copy them out before the next reset().
|
|
*/
|
|
bool Message::getLogBundle(android_LogBundle* pBundle)
|
|
{
|
|
if (mLength < (int)(sizeof(time_t) + sizeof(int)*2 + 4)) {
|
|
LOG(LOG_WARN, "", "type is %d, len is %d, too small\n",
|
|
mType, mLength);
|
|
return false;
|
|
}
|
|
assert(mData != NULL);
|
|
|
|
unsigned char* pCur = mData;
|
|
|
|
pBundle->when = *((time_t*) pCur);
|
|
pCur += sizeof(pBundle->when);
|
|
pBundle->priority = *((android_LogPriority*) pCur);
|
|
pCur += sizeof(pBundle->priority);
|
|
pBundle->pid = *((pid_t*) pCur);
|
|
pCur += sizeof(pBundle->pid);
|
|
pBundle->tag = (const char*) pCur;
|
|
pCur += strlen((const char*) pCur) +1;
|
|
mVec.iov_base = (char*) pCur;
|
|
mVec.iov_len = strlen((const char*) pCur);
|
|
pBundle->msgVec = &mVec;
|
|
pBundle->msgCount = 1;
|
|
pCur += mVec.iov_len +1;
|
|
|
|
if (pCur - mData != mLength) {
|
|
LOG(LOG_WARN, "", "log bundle rcvd %d, used %d\n", mLength,
|
|
(int) (pCur - mData));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Read the next event from the pipe.
|
|
*
|
|
* This is not expected to work well when multiple threads are reading.
|
|
*/
|
|
bool Message::read(Pipe* pPipe, bool wait)
|
|
{
|
|
if (pPipe == NULL)
|
|
return false;
|
|
assert(pPipe->isCreated());
|
|
|
|
if (!wait) {
|
|
if (!pPipe->readReady())
|
|
return false;
|
|
}
|
|
|
|
reset();
|
|
|
|
unsigned char header[4];
|
|
if (pPipe->read(header, 4) != 4)
|
|
return false;
|
|
|
|
mType = (MessageType) header[2];
|
|
mLength = header[0] | header[1] << 8;
|
|
mLength -= 2; // we already read two of them in the header
|
|
|
|
if (mLength > 0) {
|
|
int actual;
|
|
|
|
mData = new unsigned char[mLength];
|
|
if (mData == NULL) {
|
|
LOG(LOG_ERROR, "", "alloc failed\n");
|
|
return false;
|
|
}
|
|
mCleanup = kCleanupDelete;
|
|
|
|
actual = pPipe->read(mData, mLength);
|
|
if (actual != mLength) {
|
|
LOG(LOG_WARN, "", "failed reading message body (%d of %d bytes)\n",
|
|
actual, mLength);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Write this event to a pipe.
|
|
*
|
|
* It would be easiest to write the header and message body with two
|
|
* separate calls, but that will occasionally fail on multithreaded
|
|
* systems when the writes are interleaved. We have to allocate a
|
|
* temporary buffer, copy the data, and write it all at once. This
|
|
* would be easier with writev(), but we can't rely on having that.
|
|
*
|
|
* DO NOT call LOG() from here, as we could be in the process of sending
|
|
* a log message.
|
|
*/
|
|
bool Message::write(Pipe* pPipe) const
|
|
{
|
|
char tmpBuf[128];
|
|
char* writeBuf = tmpBuf;
|
|
bool result = false;
|
|
int kHeaderLen = 4;
|
|
|
|
if (pPipe == NULL)
|
|
return false;
|
|
assert(pPipe->isCreated());
|
|
|
|
if (mData == NULL || mLength < 0)
|
|
return false;
|
|
|
|
/* if it doesn't fit in stack buffer, allocate space */
|
|
if (mLength + kHeaderLen > (int) sizeof(tmpBuf)) {
|
|
writeBuf = new char[mLength + kHeaderLen];
|
|
if (writeBuf == NULL)
|
|
goto bail;
|
|
}
|
|
|
|
/*
|
|
* The current value of "mLength" does not include the 4-byte header.
|
|
* Two of the 4 header bytes are included in the length we output
|
|
* (the type byte and the pad byte), so we adjust mLength.
|
|
*/
|
|
writeBuf[0] = (unsigned char) (mLength + kHeaderLen -2);
|
|
writeBuf[1] = (unsigned char) ((mLength + kHeaderLen -2) >> 8);
|
|
writeBuf[2] = (unsigned char) mType;
|
|
writeBuf[3] = 0;
|
|
if (mLength > 0)
|
|
memcpy(writeBuf + kHeaderLen, mData, mLength);
|
|
|
|
int actual;
|
|
|
|
actual = pPipe->write(writeBuf, mLength + kHeaderLen);
|
|
if (actual != mLength + kHeaderLen) {
|
|
fprintf(stderr,
|
|
"Message::write failed writing message body (%d of %d bytes)\n",
|
|
actual, mLength + kHeaderLen);
|
|
goto bail;
|
|
}
|
|
|
|
result = true;
|
|
|
|
bail:
|
|
if (writeBuf != tmpBuf)
|
|
delete[] writeBuf;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* MessageStream
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Get ready to go.
|
|
*/
|
|
bool MessageStream::init(Pipe* readPipe, Pipe* writePipe, bool initiateHello)
|
|
{
|
|
assert(mReadPipe == NULL && mWritePipe == NULL); // only once
|
|
|
|
/*
|
|
* Swap "hello" messages.
|
|
*
|
|
* In a more robust implementation, this would include version numbers
|
|
* and capability flags.
|
|
*/
|
|
if (initiateHello) {
|
|
int32_t data = kHelloMsg;
|
|
Message msg;
|
|
|
|
/* send hello */
|
|
msg.setRaw((unsigned char*) &data, sizeof(data),
|
|
Message::kCleanupNoDelete);
|
|
if (!msg.write(writePipe)) {
|
|
LOG(LOG_WARN, "", "hello write failed in stream init\n");
|
|
return false;
|
|
}
|
|
|
|
LOG(LOG_DEBUG, "", "waiting for peer to ack my hello\n");
|
|
|
|
/* wait for the ack */
|
|
if (!msg.read(readPipe, true)) {
|
|
LOG(LOG_WARN, "", "hello ack read failed in stream init\n");
|
|
return false;
|
|
}
|
|
|
|
const int32_t* pAck;
|
|
pAck = (const int32_t*) msg.getData();
|
|
if (pAck == NULL || *pAck != kHelloAckMsg) {
|
|
LOG(LOG_WARN, "", "hello ack was bad (%08x vs %08x)\n",
|
|
*pAck, kHelloAckMsg);
|
|
return false;
|
|
}
|
|
} else {
|
|
int32_t data = kHelloAckMsg;
|
|
Message msg;
|
|
|
|
LOG(LOG_DEBUG, "", "waiting for hello from peer\n");
|
|
|
|
/* wait for the hello */
|
|
if (!msg.read(readPipe, true)) {
|
|
LOG(LOG_WARN, "", "hello read failed in stream init\n");
|
|
return false;
|
|
}
|
|
|
|
const int32_t* pAck;
|
|
pAck = (const int32_t*) msg.getData();
|
|
if (pAck == NULL || *pAck != kHelloMsg) {
|
|
LOG(LOG_WARN, "", "hello was bad\n");
|
|
return false;
|
|
}
|
|
|
|
/* send hello ack */
|
|
msg.setRaw((unsigned char*) &data, sizeof(data),
|
|
Message::kCleanupNoDelete);
|
|
if (!msg.write(writePipe)) {
|
|
LOG(LOG_WARN, "", "hello ack write failed in stream init\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* success, set up our local stuff */
|
|
mReadPipe = readPipe;
|
|
mWritePipe = writePipe;
|
|
|
|
//LOG(LOG_DEBUG, "", "init success\n");
|
|
|
|
return true;
|
|
}
|
|
|