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