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(®ion->fCount, sizeof(int32)); 457 if (status >= B_OK) 458 status = Read(®ion->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(¢er, 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(¢er, 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(¢er, 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(¢er, 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