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(®ion->fCount, sizeof(int32)); 442 if (status >= B_OK) 443 status = Read(®ion->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