1#include "SharedMemoryServer.h"
2#include <stdlib.h>
3#include <sys/mman.h>
4#include <fcntl.h>
5#include <machine/byte_order.h>
6#include <string>
7#include <sys/stat.h>
8#include <security_utilities/crc.h>
9#include <unistd.h>
10
11static const char* kPrefix = "/private/var/db/mds/messages/se_";
12
13SharedMemoryServer::SharedMemoryServer (const char* segmentName, SegmentOffsetType segmentSize) :
14	mSegmentName (segmentName), mSegmentSize (segmentSize)
15{
16	mFileName = kPrefix;
17	mFileName += segmentName;
18
19	// make the mds directory, just in case it doesn't exist
20	mkdir("/var/db/mds", 1777);
21	mkdir("/var/db/mds/messages", 0755);
22
23	// make the file name
24	// clean any old file away
25	unlink (mFileName.c_str ());
26
27	// open the file
28	int segmentDescriptor = open (mFileName.c_str (), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
29	if (segmentDescriptor < 0)
30	{
31		return;
32	}
33
34	// set the segment size
35	ftruncate (segmentDescriptor, segmentSize);
36
37	// map it into memory
38	mSegment = (u_int8_t*) mmap (NULL, mSegmentSize, PROT_READ | PROT_WRITE, MAP_SHARED, segmentDescriptor, 0);
39	close (segmentDescriptor);
40
41	if (mSegment == (u_int8_t*) -1) // can't map the memory?
42	{
43		mSegment = NULL;
44		unlink (mFileName.c_str());
45	}
46
47	mDataPtr = mDataArea = mSegment + sizeof(SegmentOffsetType);
48	mDataMax = mSegment + segmentSize;;
49
50	SetProducerOffset (0);
51}
52
53
54
55SharedMemoryServer::~SharedMemoryServer ()
56{
57	// go away
58	if (mSegment == NULL)
59	{
60		return;
61	}
62
63	// get out of memory
64	munmap (mSegment, mSegmentSize);
65
66	// mark the segment for deletion
67	unlink (mFileName.c_str ());
68}
69
70
71
72const SegmentOffsetType
73	kSegmentLength = 0,
74	kCRCOffset = kSegmentLength + sizeof(SegmentOffsetType),
75	kDomainOffset = kCRCOffset + sizeof(SegmentOffsetType),
76	kEventTypeOffset = kDomainOffset + sizeof(SegmentOffsetType),
77	kHeaderLength = kEventTypeOffset + sizeof(SegmentOffsetType) - kCRCOffset;
78
79void SharedMemoryServer::WriteMessage (SegmentOffsetType domain, SegmentOffsetType event, const void *message, SegmentOffsetType messageLength)
80{
81	// assemble the final message
82	ssize_t messageSize = kHeaderLength + messageLength;
83	u_int8_t finalMessage[messageSize];
84	SegmentOffsetType *fm  = (SegmentOffsetType*) finalMessage;
85	fm[0] = OSSwapHostToBigInt32(domain);
86	fm[1] = OSSwapHostToBigInt32(event);
87	memcpy(&fm[2], message, messageLength);
88
89	SegmentOffsetType crc = CalculateCRC(finalMessage, messageSize);
90
91	// write the length
92	WriteOffset(messageSize);
93
94	// write the crc
95	WriteOffset(crc);
96
97	// write the data
98	WriteData (finalMessage, messageSize);
99
100	// write the data count
101	SetProducerOffset(mDataPtr - mDataArea);
102}
103
104
105
106const char* SharedMemoryServer::GetSegmentName ()
107{
108	return mSegmentName.c_str ();
109}
110
111
112
113size_t SharedMemoryServer::GetSegmentSize ()
114{
115	return mSegmentSize;
116}
117
118
119
120SegmentOffsetType SharedMemoryServer::GetProducerOffset ()
121{
122	// the data is stored in the buffer in network byte order
123	u_int32_t pCount = OSSwapBigToHostInt32 (*(u_int32_t*) mSegment);
124	return OSSwapHostToBigInt32 (pCount);
125}
126
127
128
129void SharedMemoryServer::SetProducerOffset (SegmentOffsetType producerCount)
130{
131	*((SegmentOffsetType*) mSegment) = OSSwapHostToBigInt32 (producerCount);
132}
133
134
135
136void SharedMemoryServer::WriteOffset(SegmentOffsetType offset)
137{
138	u_int8_t buffer[4];
139	*((u_int32_t*) buffer) = OSSwapHostToBigInt32(offset);
140	WriteData(buffer, 4);
141}
142
143
144
145void SharedMemoryServer::WriteData(const void* data, SegmentOffsetType length)
146{
147	// figure out where in the buffer we actually need to write the data
148	// figure out how many bytes we can write without overflowing the buffer
149	const u_int8_t* dp = (const u_int8_t*) data;
150	SegmentOffsetType bytesToEnd = mDataMax - mDataPtr;
151
152	// figure out how many bytes we can write
153	SegmentOffsetType bytesToWrite = (length <= bytesToEnd) ? length : bytesToEnd;
154
155	// move the first part of the data, making sure to skip the producer pointer
156	memcpy (mDataPtr, dp, bytesToWrite);
157	mDataPtr += bytesToWrite;
158	dp += bytesToWrite;
159
160	// deduct the bytes just written
161	length -= bytesToWrite;
162
163	if (length != 0) // did we wrap around?
164	{
165		mDataPtr = mDataArea;
166		memcpy (mDataPtr, dp, length);
167		mDataPtr += length;
168	}
169}
170