xref: /haiku/src/kits/app/LinkReceiver.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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::NeedsReply() const
106 {
107 	if (fReplySize == 0)
108 		return false;
109 
110 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
111 	return (header->flags & kNeedsReply) != 0;
112 }
113 
114 
115 int32
116 LinkReceiver::Code() const
117 {
118 	if (fReplySize == 0)
119 		return B_ERROR;
120 
121 	message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
122 	return header->code;
123 }
124 
125 
126 void
127 LinkReceiver::ResetBuffer()
128 {
129 	fRecvPosition = 0;
130 	fRecvStart = 0;
131 	fDataSize = 0;
132 	fReplySize = 0;
133 }
134 
135 
136 status_t
137 LinkReceiver::AdjustReplyBuffer(bigtime_t timeout)
138 {
139 	// Here we take advantage of the compiler's dead-code elimination
140 	if (kInitialBufferSize == kMaxBufferSize) {
141 		// fixed buffer size
142 
143 		if (fRecvBuffer != NULL)
144 			return B_OK;
145 
146 		fRecvBuffer = (char *)malloc(kInitialBufferSize);
147 		if (fRecvBuffer == NULL)
148 			return B_NO_MEMORY;
149 
150 		fRecvBufferSize = kInitialBufferSize;
151 	} else {
152 		STRACE(("info: LinkReceiver getting port_buffer_size().\n"));
153 		ssize_t bufferSize;
154 		if (timeout == B_INFINITE_TIMEOUT)
155 			bufferSize = port_buffer_size(fReceivePort);
156 		else
157 			bufferSize = port_buffer_size_etc(fReceivePort, B_TIMEOUT, timeout);
158 		STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
159 
160 		if (bufferSize < 0)
161 			return (status_t)bufferSize;
162 
163 		// make sure our receive buffer is large enough
164 		if (bufferSize > fRecvBufferSize) {
165 			if (bufferSize <= (ssize_t)kInitialBufferSize)
166 				bufferSize = (ssize_t)kInitialBufferSize;
167 			else
168 				bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
169 
170 			if (bufferSize > (ssize_t)kMaxBufferSize)
171 				return B_ERROR;	// we can't continue
172 
173 			STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
174 			char *buffer = (char *)malloc(bufferSize);
175 			if (buffer == NULL)
176 				return B_NO_MEMORY;
177 
178 			free(fRecvBuffer);
179 			fRecvBuffer = buffer;
180 			fRecvBufferSize = bufferSize;
181 		}
182 	}
183 
184 	return B_OK;
185 }
186 
187 
188 status_t
189 LinkReceiver::ReadFromPort(bigtime_t timeout)
190 {
191 	// we are here so it means we finished reading the buffer contents
192 	ResetBuffer();
193 
194 	status_t err = AdjustReplyBuffer(timeout);
195 	if (err < B_OK)
196 		return err;
197 
198 	int32 code;
199 	ssize_t bytesRead;
200 
201 	STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
202 	while (true) {
203 		if (timeout != B_INFINITE_TIMEOUT) {
204 			do {
205 				bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer,
206 					fRecvBufferSize, B_TIMEOUT, timeout);
207 			} while (bytesRead == B_INTERRUPTED);
208 		} else {
209 			do {
210 				bytesRead = read_port(fReceivePort, &code, fRecvBuffer,
211 					fRecvBufferSize);
212 			} while (bytesRead == B_INTERRUPTED);
213 		}
214 
215 		STRACE(("info: LinkReceiver read %ld bytes.\n", bytesRead));
216 		if (bytesRead < B_OK)
217 			return bytesRead;
218 
219 		// we just ignore incorrect messages, and don't bother our caller
220 
221 		if (code != kLinkCode) {
222 			STRACE(("wrong port message %lx received.\n", code));
223 			continue;
224 		}
225 
226 		// port read seems to be valid
227 		break;
228 	}
229 
230 	fDataSize = bytesRead;
231 	return B_OK;
232 }
233 
234 
235 status_t
236 LinkReceiver::Read(void *data, ssize_t size)
237 {
238 //	STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
239 	if (fReadError < B_OK)
240 		return fReadError;
241 
242 	if (data == NULL || size < 1) {
243 		fReadError = B_BAD_VALUE;
244 		return B_BAD_VALUE;
245 	}
246 
247 	if (fDataSize == 0 || fReplySize == 0)
248 		return B_NO_INIT;	// need to call GetNextReply() first
249 
250 	if (fRecvPosition + size > fRecvStart + fReplySize) {
251 		// reading past the end of current message
252 		fReadError = B_BAD_VALUE;
253 		return B_BAD_VALUE;
254 	}
255 
256 	memcpy(data, fRecvBuffer + fRecvPosition, size);
257 	fRecvPosition += size;
258 	return fReadError;
259 }
260 
261 
262 status_t
263 LinkReceiver::ReadString(char **_string)
264 {
265 	int32 length = 0;
266 	status_t status;
267 
268 	status = Read<int32>(&length);
269 	if (status < B_OK)
270 		return status;
271 
272 	if (length >= 0) {
273 		char *string = (char *)malloc(length + 1);
274 		if (string == NULL) {
275 			fRecvPosition -= sizeof(int32);	// rewind the transaction
276 			return B_NO_MEMORY;
277 		}
278 
279 		if (length > 0) {
280 			status = Read(string, length);
281 			if (status < B_OK) {
282 				free(string);
283 				fRecvPosition -= sizeof(int32);	// rewind the transaction
284 				return status;
285 			}
286 		}
287 
288 		// make sure the string is null terminated
289 		string[length] = '\0';
290 
291 		*_string = string;
292 		return B_OK;
293 	} else {
294 		fRecvPosition -= sizeof(int32);	// rewind the transaction
295 		return B_ERROR;
296 	}
297 }
298 
299 
300 status_t
301 LinkReceiver::ReadString(BString &string)
302 {
303 	int32 length = 0;
304 	status_t status;
305 
306 	status = Read<int32>(&length);
307 	if (status < B_OK)
308 		return status;
309 
310 	if (length < 0) {
311 		status = B_ERROR;
312 		goto err;
313 	}
314 
315 	if (length > 0) {
316 		char* buffer = string.LockBuffer(length + 1);
317 		if (buffer == NULL) {
318 			status = B_NO_MEMORY;
319 			goto err;
320 		}
321 
322 		status = Read(buffer, length);
323 		if (status < B_OK) {
324 			string.UnlockBuffer();
325 			goto err;
326 		}
327 
328 		// make sure the string is null terminated
329 		buffer[length] = '\0';
330 		string.UnlockBuffer(length);
331 	} else
332 		string = "";
333 
334 	return B_OK;
335 
336 err:
337 	fRecvPosition -= sizeof(int32);
338 		// rewind the transaction
339 	return status;
340 }
341 
342 
343 status_t
344 LinkReceiver::ReadString(char *buffer, size_t bufferLength)
345 {
346 	int32 length = 0;
347 	status_t status;
348 
349 	status = Read<int32>(&length);
350 	if (status < B_OK)
351 		return status;
352 
353 	if (length >= (int32)bufferLength) {
354 		status = B_BUFFER_OVERFLOW;
355 		goto err;
356 	}
357 
358 	if (length < 0) {
359 		status = B_ERROR;
360 		goto err;
361 	}
362 
363 	if (length > 0) {
364 		status = Read(buffer, length);
365 		if (status < B_OK)
366 			goto err;
367 	}
368 
369 	// make sure the string is null terminated
370 	buffer[length] = '\0';
371 	return B_OK;
372 
373 err:
374 	fRecvPosition -= sizeof(int32);
375 		// rewind the transaction
376 	return status;
377 }
378 
379 }	// namespace BPrivate
380