xref: /haiku/src/kits/app/LinkReceiver.cpp (revision 03187b607b2b5eec7ee059f1ead09bdba14991fb)
1 /*
2  * Copyright 2001-2008, 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  *		Artur Wyszynski <harakash@gmail.com>
10  */
11 
12 /** Class for low-overhead port-based messaging */
13 
14 #include <LinkReceiver.h>
15 
16 #include <stdlib.h>
17 #include <string.h>
18 #include <new>
19 
20 #include <ServerProtocol.h>
21 #include <String.h>
22 #include <Region.h>
23 #include <GradientLinear.h>
24 #include <GradientRadial.h>
25 #include <GradientRadialFocus.h>
26 #include <GradientDiamond.h>
27 #include <GradientConic.h>
28 
29 #include "link_message.h"
30 #include "syscalls.h"
31 
32 //#define DEBUG_BPORTLINK
33 #ifdef DEBUG_BPORTLINK
34 #	include <stdio.h>
35 #	define STRACE(x) printf x
36 #else
37 #	define STRACE(x) ;
38 #endif
39 
40 //#define TRACE_LINK_RECEIVER_GRADIENTS
41 #ifdef TRACE_LINK_RECEIVER_GRADIENTS
42 #	include <OS.h>
43 #	define GTRACE(x) debug_printf x
44 #else
45 #	define GTRACE(x) ;
46 #endif
47 
48 
49 namespace BPrivate {
50 
51 LinkReceiver::LinkReceiver(port_id port)
52 	:
53 	fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0),
54 	fRecvBufferSize(0), fDataSize(0),
55 	fReplySize(0), fReadError(B_OK)
56 {
57 }
58 
59 
60 LinkReceiver::~LinkReceiver()
61 {
62 	free(fRecvBuffer);
63 }
64 
65 
66 void
67 LinkReceiver::SetPort(port_id port)
68 {
69 	fReceivePort = port;
70 }
71 
72 
73 status_t
74 LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout)
75 {
76 	fReadError = B_OK;
77 
78 	int32 remaining = fDataSize - (fRecvStart + fReplySize);
79 	STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining));
80 
81 	// find the position of the next message header in the buffer
82 	message_header *header;
83 	if (remaining <= 0) {
84 		status_t err = ReadFromPort(timeout);
85 		if (err < B_OK)
86 			return err;
87 		remaining = fDataSize;
88 		header = (message_header *)fRecvBuffer;
89 	} else {
90 		fRecvStart += fReplySize;	// start of the next message
91 		fRecvPosition = fRecvStart;
92 		header = (message_header *)(fRecvBuffer + fRecvStart);
93 	}
94 
95 	// check we have a well-formed message
96 	if (remaining < (int32)sizeof(message_header)) {
97 		// we don't have enough data for a complete header
98 		STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining));
99 		ResetBuffer();
100 		return B_ERROR;
101 	}
102 
103 	fReplySize = header->size;
104 	if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) {
105 		STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize));
106 		ResetBuffer();
107 		return B_ERROR;
108 	}
109 
110 	code = header->code;
111 	fRecvPosition += sizeof(message_header);
112 
113 	STRACE(("info: LinkReceiver got header %ld [%ld %ld %ld] from port %ld.\n",
114 		header->code, fReplySize, header->code, header->flags, fReceivePort));
115 
116 	return B_OK;
117 }
118 
119 
120 bool
121 LinkReceiver::HasMessages() const
122 {
123 	return fDataSize - (fRecvStart + fReplySize) > 0
124 		|| port_count(fReceivePort) > 0;
125 }
126 
127 
128 bool
129 LinkReceiver::NeedsReply() const
130 {
131 	if (fReplySize == 0)
132 		return false;
133 
134 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
135 	return (header->flags & kNeedsReply) != 0;
136 }
137 
138 
139 int32
140 LinkReceiver::Code() const
141 {
142 	if (fReplySize == 0)
143 		return B_ERROR;
144 
145 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
146 	return header->code;
147 }
148 
149 
150 void
151 LinkReceiver::ResetBuffer()
152 {
153 	fRecvPosition = 0;
154 	fRecvStart = 0;
155 	fDataSize = 0;
156 	fReplySize = 0;
157 }
158 
159 
160 status_t
161 LinkReceiver::AdjustReplyBuffer(bigtime_t timeout)
162 {
163 	// Here we take advantage of the compiler's dead-code elimination
164 	if (kInitialBufferSize == kMaxBufferSize) {
165 		// fixed buffer size
166 
167 		if (fRecvBuffer != NULL)
168 			return B_OK;
169 
170 		fRecvBuffer = (char *)malloc(kInitialBufferSize);
171 		if (fRecvBuffer == NULL)
172 			return B_NO_MEMORY;
173 
174 		fRecvBufferSize = kInitialBufferSize;
175 	} else {
176 		STRACE(("info: LinkReceiver getting port_buffer_size().\n"));
177 
178 		ssize_t bufferSize;
179 		do {
180 			bufferSize = port_buffer_size_etc(fReceivePort,
181 				timeout == B_INFINITE_TIMEOUT ? B_RELATIVE_TIMEOUT : 0,
182 				timeout);
183 		} while (bufferSize == B_INTERRUPTED);
184 
185 		STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
186 
187 		if (bufferSize < 0)
188 			return (status_t)bufferSize;
189 
190 		// make sure our receive buffer is large enough
191 		if (bufferSize > fRecvBufferSize) {
192 			if (bufferSize <= (ssize_t)kInitialBufferSize)
193 				bufferSize = (ssize_t)kInitialBufferSize;
194 			else
195 				bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
196 
197 			if (bufferSize > (ssize_t)kMaxBufferSize)
198 				return B_ERROR;	// we can't continue
199 
200 			STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
201 			char *buffer = (char *)malloc(bufferSize);
202 			if (buffer == NULL)
203 				return B_NO_MEMORY;
204 
205 			free(fRecvBuffer);
206 			fRecvBuffer = buffer;
207 			fRecvBufferSize = bufferSize;
208 		}
209 	}
210 
211 	return B_OK;
212 }
213 
214 
215 status_t
216 LinkReceiver::ReadFromPort(bigtime_t timeout)
217 {
218 	// we are here so it means we finished reading the buffer contents
219 	ResetBuffer();
220 
221 	status_t err = AdjustReplyBuffer(timeout);
222 	if (err < B_OK)
223 		return err;
224 
225 	int32 code;
226 	ssize_t bytesRead;
227 
228 	STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
229 	while (true) {
230 		if (timeout != B_INFINITE_TIMEOUT) {
231 			do {
232 				bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer,
233 					fRecvBufferSize, B_TIMEOUT, timeout);
234 			} while (bytesRead == B_INTERRUPTED);
235 		} else {
236 			do {
237 				bytesRead = read_port(fReceivePort, &code, fRecvBuffer,
238 					fRecvBufferSize);
239 			} while (bytesRead == B_INTERRUPTED);
240 		}
241 
242 		STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead));
243 		if (bytesRead < B_OK)
244 			return bytesRead;
245 
246 		// we just ignore incorrect messages, and don't bother our caller
247 
248 		if (code != kLinkCode) {
249 			STRACE(("wrong port message %lx received.\n", code));
250 			continue;
251 		}
252 
253 		// port read seems to be valid
254 		break;
255 	}
256 
257 	fDataSize = bytesRead;
258 	return B_OK;
259 }
260 
261 
262 status_t
263 LinkReceiver::Read(void *data, ssize_t passedSize)
264 {
265 //	STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
266 	ssize_t size = passedSize;
267 
268 	if (fReadError < B_OK)
269 		return fReadError;
270 
271 	if (data == NULL || size < 1) {
272 		fReadError = B_BAD_VALUE;
273 		return B_BAD_VALUE;
274 	}
275 
276 	if (fDataSize == 0 || fReplySize == 0)
277 		return B_NO_INIT;	// need to call GetNextReply() first
278 
279 	bool useArea = false;
280 	if ((size_t)size >= kMaxBufferSize) {
281 		useArea = true;
282 		size = sizeof(area_id);
283 	}
284 
285 	if (fRecvPosition + size > fRecvStart + fReplySize) {
286 		// reading past the end of current message
287 		fReadError = B_BAD_VALUE;
288 		return B_BAD_VALUE;
289 	}
290 
291 	if (useArea) {
292 		area_id sourceArea;
293 		memcpy((void*)&sourceArea, fRecvBuffer + fRecvPosition, size);
294 
295 		area_info areaInfo;
296 		if (get_area_info(sourceArea, &areaInfo) < B_OK)
297 			fReadError = B_BAD_VALUE;
298 
299 		if (fReadError >= B_OK) {
300 			thread_info threadInfo;
301 			get_thread_info(find_thread(NULL), &threadInfo);
302 
303 			void* areaAddress = NULL;
304 			if (areaInfo.team != threadInfo.team) {
305 				sourceArea = _kern_transfer_area(sourceArea, &areaAddress,
306 					B_ANY_ADDRESS, threadInfo.team);
307 
308 				if (sourceArea < B_OK)
309 					fReadError = sourceArea;
310 			} else {
311 				areaAddress = areaInfo.address;
312 			}
313 
314 			if (areaAddress && sourceArea >= B_OK) {
315 				memcpy(data, areaAddress, passedSize);
316 				delete_area(sourceArea);
317 			}
318 		}
319 	} else {
320 		memcpy(data, fRecvBuffer + fRecvPosition, size);
321 	}
322 	fRecvPosition += size;
323 	return fReadError;
324 }
325 
326 
327 status_t
328 LinkReceiver::ReadString(char** _string, size_t* _length)
329 {
330 	int32 length = 0;
331 	status_t status = Read<int32>(&length);
332 
333 	if (status < B_OK)
334 		return status;
335 
336 	char *string;
337 	if (length < 0) {
338 		status = B_ERROR;
339 		goto err;
340 	}
341 
342 	string = (char *)malloc(length + 1);
343 	if (string == NULL) {
344 		status = B_NO_MEMORY;
345 		goto err;
346 	}
347 
348 	if (length > 0) {
349 		status = Read(string, length);
350 		if (status < B_OK) {
351 			free(string);
352 			return status;
353 		}
354 	}
355 
356 	// make sure the string is null terminated
357 	string[length] = '\0';
358 
359 	if (_length)
360 		*_length = length;
361 
362 	*_string = string;
363 
364 	return B_OK;
365 
366 err:
367 	fRecvPosition -= sizeof(int32);
368 		// rewind the transaction
369 	return status;
370 }
371 
372 
373 status_t
374 LinkReceiver::ReadString(BString &string, size_t* _length)
375 {
376 	int32 length = 0;
377 	status_t status = Read<int32>(&length);
378 
379 	if (status < B_OK)
380 		return status;
381 
382 	if (length < 0) {
383 		status = B_ERROR;
384 		goto err;
385 	}
386 
387 	if (length > 0) {
388 		char* buffer = string.LockBuffer(length + 1);
389 		if (buffer == NULL) {
390 			status = B_NO_MEMORY;
391 			goto err;
392 		}
393 
394 		status = Read(buffer, length);
395 		if (status < B_OK) {
396 			string.UnlockBuffer();
397 			goto err;
398 		}
399 
400 		// make sure the string is null terminated
401 		buffer[length] = '\0';
402 		string.UnlockBuffer(length);
403 	} else
404 		string = "";
405 
406 	if (_length)
407 		*_length = length;
408 
409 	return B_OK;
410 
411 err:
412 	fRecvPosition -= sizeof(int32);
413 		// rewind the transaction
414 	return status;
415 }
416 
417 
418 status_t
419 LinkReceiver::ReadString(char *buffer, size_t bufferLength)
420 {
421 	int32 length = 0;
422 	status_t status = Read<int32>(&length);
423 
424 	if (status < B_OK)
425 		return status;
426 
427 	if (length >= (int32)bufferLength) {
428 		status = B_BUFFER_OVERFLOW;
429 		goto err;
430 	}
431 
432 	if (length < 0) {
433 		status = B_ERROR;
434 		goto err;
435 	}
436 
437 	if (length > 0) {
438 		status = Read(buffer, length);
439 		if (status < B_OK)
440 			goto err;
441 	}
442 
443 	// make sure the string is null terminated
444 	buffer[length] = '\0';
445 	return B_OK;
446 
447 err:
448 	fRecvPosition -= sizeof(int32);
449 		// rewind the transaction
450 	return status;
451 }
452 
453 status_t
454 LinkReceiver::ReadRegion(BRegion* region)
455 {
456 	status_t status = Read(&region->fCount, sizeof(int32));
457 	if (status >= B_OK)
458 		status = Read(&region->fBounds, sizeof(clipping_rect));
459 	if (status >= B_OK) {
460 		if (!region->_SetSize(region->fCount))
461 			status = B_NO_MEMORY;
462 		else {
463 			status = Read(region->fData,
464 				region->fCount * sizeof(clipping_rect));
465 		}
466 		if (status < B_OK)
467 			region->MakeEmpty();
468 	}
469 	return status;
470 }
471 
472 
473 static BGradient*
474 gradient_for_type(BGradient::Type type)
475 {
476 	switch (type) {
477 		case BGradient::TYPE_LINEAR:
478 			return new (std::nothrow) BGradientLinear();
479 		case BGradient::TYPE_RADIAL:
480 			return new (std::nothrow) BGradientRadial();
481 		case BGradient::TYPE_RADIAL_FOCUS:
482 			return new (std::nothrow) BGradientRadialFocus();
483 		case BGradient::TYPE_DIAMOND:
484 			return new (std::nothrow) BGradientDiamond();
485 		case BGradient::TYPE_CONIC:
486 			return new (std::nothrow) BGradientConic();
487 		case BGradient::TYPE_NONE:
488 			return new (std::nothrow) BGradient();
489 	}
490 	return NULL;
491 }
492 
493 
494 status_t
495 LinkReceiver::ReadGradient(BGradient** _gradient)
496 {
497 	GTRACE(("LinkReceiver::ReadGradient\n"));
498 	BGradient::Type gradientType;
499 	int32 colorsCount;
500 	status_t ret;
501 	if ((ret = Read(&gradientType, sizeof(BGradient::Type))) != B_OK)
502 		return ret;
503 	if ((ret = Read(&colorsCount, sizeof(int32))) != B_OK)
504 		return ret;
505 	BGradient* gradient = gradient_for_type(gradientType);
506 	if (!gradient)
507 		return B_NO_MEMORY;
508 
509 	*_gradient = gradient;
510 
511 	if (colorsCount > 0) {
512 		BGradient::ColorStop stop;
513 		for (int i = 0; i < colorsCount; i++) {
514 			if ((ret = Read(&stop, sizeof(BGradient::ColorStop))) != B_OK)
515 				return ret;
516 			if (!gradient->AddColorStop(stop, i))
517 				return B_NO_MEMORY;
518 		}
519 	}
520 
521 	switch(gradientType) {
522 		case BGradient::TYPE_LINEAR: {
523 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_LINEAR\n"));
524 			BGradientLinear* linear = (BGradientLinear*)gradient;
525 			BPoint start;
526 			BPoint end;
527 			if ((ret = Read(&start, sizeof(BPoint))) != B_OK)
528 				return ret;
529 			if ((ret = Read(&end, sizeof(BPoint))) != B_OK)
530 				return ret;
531 			linear->SetStart(start);
532 			linear->SetEnd(end);
533 			return B_OK;
534 		}
535 		case BGradient::TYPE_RADIAL: {
536 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL\n"));
537 			BGradientRadial* radial = (BGradientRadial*)gradient;
538 			BPoint center;
539 			float radius;
540 			if ((ret = Read(&center, sizeof(BPoint))) != B_OK)
541 				return ret;
542 			if ((ret = Read(&radius, sizeof(float))) != B_OK)
543 				return ret;
544 			radial->SetCenter(center);
545 			radial->SetRadius(radius);
546 			return B_OK;
547 		}
548 		case BGradient::TYPE_RADIAL_FOCUS: {
549 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_RADIAL_FOCUS\n"));
550 			BGradientRadialFocus* radialFocus =
551 				(BGradientRadialFocus*)gradient;
552 			BPoint center;
553 			BPoint focal;
554 			float radius;
555 			if ((ret = Read(&center, sizeof(BPoint))) != B_OK)
556 				return ret;
557 			if ((ret = Read(&focal, sizeof(BPoint))) != B_OK)
558 				return ret;
559 			if ((ret = Read(&radius, sizeof(float))) != B_OK)
560 				return ret;
561 			radialFocus->SetCenter(center);
562 			radialFocus->SetFocal(focal);
563 			radialFocus->SetRadius(radius);
564 			return B_OK;
565 		}
566 		case BGradient::TYPE_DIAMOND: {
567 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_DIAMOND\n"));
568 			BGradientDiamond* diamond = (BGradientDiamond*)gradient;
569 			BPoint center;
570 			if ((ret = Read(&center, sizeof(BPoint))) != B_OK)
571 				return ret;
572 			diamond->SetCenter(center);
573 			return B_OK;
574 		}
575 		case BGradient::TYPE_CONIC: {
576 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_CONIC\n"));
577 			BGradientConic* conic = (BGradientConic*)gradient;
578 			BPoint center;
579 			float angle;
580 			if ((ret = Read(&center, sizeof(BPoint))) != B_OK)
581 				return ret;
582 			if ((ret = Read(&angle, sizeof(float))) != B_OK)
583 				return ret;
584 			conic->SetCenter(center);
585 			conic->SetAngle(angle);
586 			return B_OK;
587 		}
588 		case BGradient::TYPE_NONE: {
589 			GTRACE(("LinkReceiver::ReadGradient> type == TYPE_NONE\n"));
590 			break;
591 		}
592 	}
593 
594 	return B_ERROR;
595 }
596 
597 }	// namespace BPrivate
598