xref: /haiku/src/kits/app/LinkReceiver.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2  * Copyright 2001-2007, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Pahtz <pahtz@yahoo.com.au>
7  *		Axel Dörfler
8  *		Stephan Aßmus <superstippi@gmx.de>
9  */
10 
11 /** Class for low-overhead port-based messaging */
12 
13 #include <LinkReceiver.h>
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include <new>
18 
19 #include <ServerProtocol.h>
20 #include <String.h>
21 #include <Region.h>
22 
23 #include "link_message.h"
24 #include "syscalls.h"
25 
26 //#define DEBUG_BPORTLINK
27 #ifdef DEBUG_BPORTLINK
28 #	include <stdio.h>
29 #	define STRACE(x) printf x
30 #else
31 #	define STRACE(x) ;
32 #endif
33 
34 namespace BPrivate {
35 
36 LinkReceiver::LinkReceiver(port_id port)
37 	:
38 	fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0),
39 	fRecvBufferSize(0), fDataSize(0),
40 	fReplySize(0), fReadError(B_OK)
41 {
42 }
43 
44 
45 LinkReceiver::~LinkReceiver()
46 {
47 	free(fRecvBuffer);
48 }
49 
50 
51 void
52 LinkReceiver::SetPort(port_id port)
53 {
54 	fReceivePort = port;
55 }
56 
57 
58 status_t
59 LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout)
60 {
61 	fReadError = B_OK;
62 
63 	int32 remaining = fDataSize - (fRecvStart + fReplySize);
64 	STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining));
65 
66 	// find the position of the next message header in the buffer
67 	message_header *header;
68 	if (remaining <= 0) {
69 		status_t err = ReadFromPort(timeout);
70 		if (err < B_OK)
71 			return err;
72 		remaining = fDataSize;
73 		header = (message_header *)fRecvBuffer;
74 	} else {
75 		fRecvStart += fReplySize;	// start of the next message
76 		fRecvPosition = fRecvStart;
77 		header = (message_header *)(fRecvBuffer + fRecvStart);
78 	}
79 
80 	// check we have a well-formed message
81 	if (remaining < (int32)sizeof(message_header)) {
82 		// we don't have enough data for a complete header
83 		STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining));
84 		ResetBuffer();
85 		return B_ERROR;
86 	}
87 
88 	fReplySize = header->size;
89 	if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) {
90 		STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize));
91 		ResetBuffer();
92 		return B_ERROR;
93 	}
94 
95 	code = header->code;
96 	fRecvPosition += sizeof(message_header);
97 
98 	STRACE(("info: LinkReceiver got header %ld [%ld %ld %ld] from port %ld.\n",
99 		header->code, fReplySize, header->code, header->flags, fReceivePort));
100 
101 	return B_OK;
102 }
103 
104 
105 bool
106 LinkReceiver::HasMessages() const
107 {
108 	return fDataSize - (fRecvStart + fReplySize) > 0
109 		|| port_count(fReceivePort) > 0;
110 }
111 
112 
113 bool
114 LinkReceiver::NeedsReply() const
115 {
116 	if (fReplySize == 0)
117 		return false;
118 
119 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
120 	return (header->flags & kNeedsReply) != 0;
121 }
122 
123 
124 int32
125 LinkReceiver::Code() const
126 {
127 	if (fReplySize == 0)
128 		return B_ERROR;
129 
130 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
131 	return header->code;
132 }
133 
134 
135 void
136 LinkReceiver::ResetBuffer()
137 {
138 	fRecvPosition = 0;
139 	fRecvStart = 0;
140 	fDataSize = 0;
141 	fReplySize = 0;
142 }
143 
144 
145 status_t
146 LinkReceiver::AdjustReplyBuffer(bigtime_t timeout)
147 {
148 	// Here we take advantage of the compiler's dead-code elimination
149 	if (kInitialBufferSize == kMaxBufferSize) {
150 		// fixed buffer size
151 
152 		if (fRecvBuffer != NULL)
153 			return B_OK;
154 
155 		fRecvBuffer = (char *)malloc(kInitialBufferSize);
156 		if (fRecvBuffer == NULL)
157 			return B_NO_MEMORY;
158 
159 		fRecvBufferSize = kInitialBufferSize;
160 	} else {
161 		STRACE(("info: LinkReceiver getting port_buffer_size().\n"));
162 
163 		ssize_t bufferSize;
164 		do {
165 			bufferSize = port_buffer_size_etc(fReceivePort,
166 				timeout == B_INFINITE_TIMEOUT ? B_RELATIVE_TIMEOUT : 0,
167 				timeout);
168 		} while (bufferSize == B_INTERRUPTED);
169 
170 		STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
171 
172 		if (bufferSize < 0)
173 			return (status_t)bufferSize;
174 
175 		// make sure our receive buffer is large enough
176 		if (bufferSize > fRecvBufferSize) {
177 			if (bufferSize <= (ssize_t)kInitialBufferSize)
178 				bufferSize = (ssize_t)kInitialBufferSize;
179 			else
180 				bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
181 
182 			if (bufferSize > (ssize_t)kMaxBufferSize)
183 				return B_ERROR;	// we can't continue
184 
185 			STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
186 			char *buffer = (char *)malloc(bufferSize);
187 			if (buffer == NULL)
188 				return B_NO_MEMORY;
189 
190 			free(fRecvBuffer);
191 			fRecvBuffer = buffer;
192 			fRecvBufferSize = bufferSize;
193 		}
194 	}
195 
196 	return B_OK;
197 }
198 
199 
200 status_t
201 LinkReceiver::ReadFromPort(bigtime_t timeout)
202 {
203 	// we are here so it means we finished reading the buffer contents
204 	ResetBuffer();
205 
206 	status_t err = AdjustReplyBuffer(timeout);
207 	if (err < B_OK)
208 		return err;
209 
210 	int32 code;
211 	ssize_t bytesRead;
212 
213 	STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
214 	while (true) {
215 		if (timeout != B_INFINITE_TIMEOUT) {
216 			do {
217 				bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer,
218 					fRecvBufferSize, B_TIMEOUT, timeout);
219 			} while (bytesRead == B_INTERRUPTED);
220 		} else {
221 			do {
222 				bytesRead = read_port(fReceivePort, &code, fRecvBuffer,
223 					fRecvBufferSize);
224 			} while (bytesRead == B_INTERRUPTED);
225 		}
226 
227 		STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead));
228 		if (bytesRead < B_OK)
229 			return bytesRead;
230 
231 		// we just ignore incorrect messages, and don't bother our caller
232 
233 		if (code != kLinkCode) {
234 			STRACE(("wrong port message %lx received.\n", code));
235 			continue;
236 		}
237 
238 		// port read seems to be valid
239 		break;
240 	}
241 
242 	fDataSize = bytesRead;
243 	return B_OK;
244 }
245 
246 
247 status_t
248 LinkReceiver::Read(void *data, ssize_t passedSize)
249 {
250 //	STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
251 	ssize_t size = passedSize;
252 
253 	if (fReadError < B_OK)
254 		return fReadError;
255 
256 	if (data == NULL || size < 1) {
257 		fReadError = B_BAD_VALUE;
258 		return B_BAD_VALUE;
259 	}
260 
261 	if (fDataSize == 0 || fReplySize == 0)
262 		return B_NO_INIT;	// need to call GetNextReply() first
263 
264 	bool useArea = false;
265 	if ((size_t)size >= kMaxBufferSize) {
266 		useArea = true;
267 		size = sizeof(area_id);
268 	}
269 
270 	if (fRecvPosition + size > fRecvStart + fReplySize) {
271 		// reading past the end of current message
272 		fReadError = B_BAD_VALUE;
273 		return B_BAD_VALUE;
274 	}
275 
276 	if (useArea) {
277 		area_id sourceArea;
278 		memcpy((void*)&sourceArea, fRecvBuffer + fRecvPosition, size);
279 
280 		area_info areaInfo;
281 		if (get_area_info(sourceArea, &areaInfo) < B_OK)
282 			fReadError = B_BAD_VALUE;
283 
284 		if (fReadError >= B_OK) {
285 			thread_info threadInfo;
286 			get_thread_info(find_thread(NULL), &threadInfo);
287 
288 			void* areaAddress = NULL;
289 			if (areaInfo.team != threadInfo.team) {
290 				sourceArea = _kern_transfer_area(sourceArea, &areaAddress,
291 					B_ANY_ADDRESS, threadInfo.team);
292 
293 				if (sourceArea < B_OK)
294 					fReadError = sourceArea;
295 			} else {
296 				areaAddress = areaInfo.address;
297 			}
298 
299 			if (areaAddress && sourceArea >= B_OK) {
300 				memcpy(data, areaAddress, passedSize);
301 				delete_area(sourceArea);
302 			}
303 		}
304 	} else {
305 		memcpy(data, fRecvBuffer + fRecvPosition, size);
306 	}
307 	fRecvPosition += size;
308 	return fReadError;
309 }
310 
311 
312 status_t
313 LinkReceiver::ReadString(char** _string, size_t* _length)
314 {
315 	int32 length = 0;
316 	status_t status = Read<int32>(&length);
317 
318 	if (status < B_OK)
319 		return status;
320 
321 	char *string;
322 	if (length < 0) {
323 		status = B_ERROR;
324 		goto err;
325 	}
326 
327 	string = (char *)malloc(length + 1);
328 	if (string == NULL) {
329 		status = B_NO_MEMORY;
330 		goto err;
331 	}
332 
333 	if (length > 0) {
334 		status = Read(string, length);
335 		if (status < B_OK) {
336 			free(string);
337 			return status;
338 		}
339 	}
340 
341 	// make sure the string is null terminated
342 	string[length] = '\0';
343 
344 	if (_length)
345 		*_length = length;
346 
347 	*_string = string;
348 
349 	return B_OK;
350 
351 err:
352 	fRecvPosition -= sizeof(int32);
353 		// rewind the transaction
354 	return status;
355 }
356 
357 
358 status_t
359 LinkReceiver::ReadString(BString &string, size_t* _length)
360 {
361 	int32 length = 0;
362 	status_t status = Read<int32>(&length);
363 
364 	if (status < B_OK)
365 		return status;
366 
367 	if (length < 0) {
368 		status = B_ERROR;
369 		goto err;
370 	}
371 
372 	if (length > 0) {
373 		char* buffer = string.LockBuffer(length + 1);
374 		if (buffer == NULL) {
375 			status = B_NO_MEMORY;
376 			goto err;
377 		}
378 
379 		status = Read(buffer, length);
380 		if (status < B_OK) {
381 			string.UnlockBuffer();
382 			goto err;
383 		}
384 
385 		// make sure the string is null terminated
386 		buffer[length] = '\0';
387 		string.UnlockBuffer(length);
388 	} else
389 		string = "";
390 
391 	if (_length)
392 		*_length = length;
393 
394 	return B_OK;
395 
396 err:
397 	fRecvPosition -= sizeof(int32);
398 		// rewind the transaction
399 	return status;
400 }
401 
402 
403 status_t
404 LinkReceiver::ReadString(char *buffer, size_t bufferLength)
405 {
406 	int32 length = 0;
407 	status_t status = Read<int32>(&length);
408 
409 	if (status < B_OK)
410 		return status;
411 
412 	if (length >= (int32)bufferLength) {
413 		status = B_BUFFER_OVERFLOW;
414 		goto err;
415 	}
416 
417 	if (length < 0) {
418 		status = B_ERROR;
419 		goto err;
420 	}
421 
422 	if (length > 0) {
423 		status = Read(buffer, length);
424 		if (status < B_OK)
425 			goto err;
426 	}
427 
428 	// make sure the string is null terminated
429 	buffer[length] = '\0';
430 	return B_OK;
431 
432 err:
433 	fRecvPosition -= sizeof(int32);
434 		// rewind the transaction
435 	return status;
436 }
437 
438 status_t
439 LinkReceiver::ReadRegion(BRegion* region)
440 {
441 	status_t status = Read(&region->fCount, sizeof(int32));
442 	if (status >= B_OK)
443 		status = Read(&region->fBounds, sizeof(clipping_rect));
444 	if (status >= B_OK) {
445 		if (!region->_SetSize(region->fCount))
446 			status = B_NO_MEMORY;
447 		else {
448 			status = Read(region->fData,
449 				region->fCount * sizeof(clipping_rect));
450 		}
451 		if (status < B_OK)
452 			region->MakeEmpty();
453 	}
454 	return status;
455 }
456 
457 
458 }	// namespace BPrivate
459 
460 
461