1 /* 2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001, Mark-Jan Bastian. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 11 /*! Ports for IPC */ 12 13 14 #include <port.h> 15 16 #include <ctype.h> 17 #include <iovec.h> 18 #include <stdlib.h> 19 #include <string.h> 20 21 #include <OS.h> 22 23 #include <AutoDeleter.h> 24 25 #include <arch/int.h> 26 #include <heap.h> 27 #include <kernel.h> 28 #include <Notifications.h> 29 #include <sem.h> 30 #include <syscall_restart.h> 31 #include <team.h> 32 #include <tracing.h> 33 #include <util/AutoLock.h> 34 #include <util/list.h> 35 #include <vm/vm.h> 36 #include <wait_for_objects.h> 37 38 39 //#define TRACE_PORTS 40 #ifdef TRACE_PORTS 41 # define TRACE(x) dprintf x 42 #else 43 # define TRACE(x) 44 #endif 45 46 47 // Locking: 48 // * sPortsLock: Protects the sPorts hash table, Team::port_list, and 49 // Port::owner. 50 // * Port::lock: Protects all Port members save team_link, hash_link, and lock. 51 // id is immutable. 52 // * sPortQuotaLock: Protects sTotalSpaceInUse, sAreaChangeCounter, 53 // sWaitingForSpace and the critical section of creating/adding areas for the 54 // port heap in the grow case. It also has to be held when reading 55 // sWaitingForSpace to determine whether or not to notify the 56 // sNoSpaceCondition condition variable. 57 // 58 // The locking order is sPortsLock -> Port::lock. A port must be looked up 59 // in sPorts and locked with sPortsLock held. Afterwards sPortsLock can be 60 // dropped, unless any field guarded by sPortsLock is accessed. 61 62 63 struct port_message; 64 65 66 static void put_port_message(port_message* message); 67 68 69 struct port_message : DoublyLinkedListLinkImpl<port_message> { 70 int32 code; 71 size_t size; 72 uid_t sender; 73 gid_t sender_group; 74 team_id sender_team; 75 char buffer[0]; 76 }; 77 78 typedef DoublyLinkedList<port_message> MessageList; 79 80 81 struct Port { 82 struct list_link team_link; 83 Port* hash_link; 84 port_id id; 85 team_id owner; 86 int32 capacity; 87 mutex lock; 88 uint32 read_count; 89 int32 write_count; 90 ConditionVariable read_condition; 91 ConditionVariable write_condition; 92 int32 total_count; 93 // messages read from port since creation 94 select_info* select_infos; 95 MessageList messages; 96 97 Port(team_id owner, int32 queueLength, char* name) 98 : 99 owner(owner), 100 capacity(queueLength), 101 read_count(0), 102 write_count(queueLength), 103 total_count(0), 104 select_infos(NULL) 105 { 106 // id is initialized when the caller adds the port to the hash table 107 108 mutex_init(&lock, name); 109 read_condition.Init(this, "port read"); 110 write_condition.Init(this, "port write"); 111 } 112 113 ~Port() 114 { 115 while (port_message* message = messages.RemoveHead()) 116 put_port_message(message); 117 118 free((char*)lock.name); 119 lock.name = NULL; 120 } 121 }; 122 123 124 struct PortHashDefinition { 125 typedef port_id KeyType; 126 typedef Port ValueType; 127 128 size_t HashKey(port_id key) const 129 { 130 return key; 131 } 132 133 size_t Hash(Port* value) const 134 { 135 return HashKey(value->id); 136 } 137 138 bool Compare(port_id key, Port* value) const 139 { 140 return value->id == key; 141 } 142 143 Port*& GetLink(Port* value) const 144 { 145 return value->hash_link; 146 } 147 }; 148 149 typedef BOpenHashTable<PortHashDefinition> PortHashTable; 150 151 152 class PortNotificationService : public DefaultNotificationService { 153 public: 154 PortNotificationService(); 155 156 void Notify(uint32 opcode, port_id team); 157 }; 158 159 160 #if PORT_TRACING 161 namespace PortTracing { 162 163 class Create : public AbstractTraceEntry { 164 public: 165 Create(Port* port) 166 : 167 fID(port->id), 168 fOwner(port->owner), 169 fCapacity(port->capacity) 170 { 171 fName = alloc_tracing_buffer_strcpy(port->lock.name, B_OS_NAME_LENGTH, 172 false); 173 174 Initialized(); 175 } 176 177 virtual void AddDump(TraceOutput& out) 178 { 179 out.Print("port %ld created, name \"%s\", owner %ld, capacity %ld", 180 fID, fName, fOwner, fCapacity); 181 } 182 183 private: 184 port_id fID; 185 char* fName; 186 team_id fOwner; 187 int32 fCapacity; 188 }; 189 190 191 class Delete : public AbstractTraceEntry { 192 public: 193 Delete(Port* port) 194 : 195 fID(port->id) 196 { 197 Initialized(); 198 } 199 200 virtual void AddDump(TraceOutput& out) 201 { 202 out.Print("port %ld deleted", fID); 203 } 204 205 private: 206 port_id fID; 207 }; 208 209 210 class Read : public AbstractTraceEntry { 211 public: 212 Read(port_id id, int32 readCount, int32 writeCount, int32 code, 213 ssize_t result) 214 : 215 fID(id), 216 fReadCount(readCount), 217 fWriteCount(writeCount), 218 fCode(code), 219 fResult(result) 220 { 221 Initialized(); 222 } 223 224 virtual void AddDump(TraceOutput& out) 225 { 226 out.Print("port %ld read, read %ld, write %ld, code %lx: %ld", 227 fID, fReadCount, fWriteCount, fCode, fResult); 228 } 229 230 private: 231 port_id fID; 232 int32 fReadCount; 233 int32 fWriteCount; 234 int32 fCode; 235 ssize_t fResult; 236 }; 237 238 239 class Write : public AbstractTraceEntry { 240 public: 241 Write(port_id id, int32 readCount, int32 writeCount, int32 code, 242 size_t bufferSize, ssize_t result) 243 : 244 fID(id), 245 fReadCount(readCount), 246 fWriteCount(writeCount), 247 fCode(code), 248 fBufferSize(bufferSize), 249 fResult(result) 250 { 251 Initialized(); 252 } 253 254 virtual void AddDump(TraceOutput& out) 255 { 256 out.Print("port %ld write, read %ld, write %ld, code %lx, size %ld: %ld", 257 fID, fReadCount, fWriteCount, fCode, fBufferSize, fResult); 258 } 259 260 private: 261 port_id fID; 262 int32 fReadCount; 263 int32 fWriteCount; 264 int32 fCode; 265 size_t fBufferSize; 266 ssize_t fResult; 267 }; 268 269 270 class Info : public AbstractTraceEntry { 271 public: 272 Info(port_id id, int32 readCount, int32 writeCount, int32 code, 273 ssize_t result) 274 : 275 fID(id), 276 fReadCount(readCount), 277 fWriteCount(writeCount), 278 fCode(code), 279 fResult(result) 280 { 281 Initialized(); 282 } 283 284 virtual void AddDump(TraceOutput& out) 285 { 286 out.Print("port %ld info, read %ld, write %ld, code %lx: %ld", 287 fID, fReadCount, fWriteCount, fCode, fResult); 288 } 289 290 private: 291 port_id fID; 292 int32 fReadCount; 293 int32 fWriteCount; 294 int32 fCode; 295 ssize_t fResult; 296 }; 297 298 299 class OwnerChange : public AbstractTraceEntry { 300 public: 301 OwnerChange(Port* port, team_id newOwner, status_t status) 302 : 303 fID(port->id), 304 fOldOwner(port->owner), 305 fNewOwner(newOwner), 306 fStatus(status) 307 { 308 Initialized(); 309 } 310 311 virtual void AddDump(TraceOutput& out) 312 { 313 out.Print("port %ld owner change from %ld to %ld: %s", fID, fOldOwner, 314 fNewOwner, strerror(fStatus)); 315 } 316 317 private: 318 port_id fID; 319 team_id fOldOwner; 320 team_id fNewOwner; 321 status_t fStatus; 322 }; 323 324 } // namespace PortTracing 325 326 # define T(x) new(std::nothrow) PortTracing::x; 327 #else 328 # define T(x) ; 329 #endif 330 331 332 static const size_t kInitialPortBufferSize = 4 * 1024 * 1024; 333 static const size_t kTotalSpaceLimit = 64 * 1024 * 1024; 334 static const size_t kTeamSpaceLimit = 8 * 1024 * 1024; 335 static const size_t kBufferGrowRate = kInitialPortBufferSize; 336 337 #define MAX_QUEUE_LENGTH 4096 338 #define PORT_MAX_MESSAGE_SIZE (256 * 1024) 339 340 static int32 sMaxPorts = 4096; 341 static int32 sUsedPorts = 0; 342 343 static PortHashTable sPorts; 344 static heap_allocator* sPortAllocator; 345 static ConditionVariable sNoSpaceCondition; 346 static int32 sTotalSpaceInUse; 347 static int32 sAreaChangeCounter; 348 static int32 sWaitingForSpace; 349 static port_id sNextPortID = 1; 350 static bool sPortsActive = false; 351 static mutex sPortsLock = MUTEX_INITIALIZER("ports list"); 352 static mutex sPortQuotaLock = MUTEX_INITIALIZER("port quota"); 353 354 static PortNotificationService sNotificationService; 355 356 357 // #pragma mark - TeamNotificationService 358 359 360 PortNotificationService::PortNotificationService() 361 : 362 DefaultNotificationService("ports") 363 { 364 } 365 366 367 void 368 PortNotificationService::Notify(uint32 opcode, port_id port) 369 { 370 char eventBuffer[128]; 371 KMessage event; 372 event.SetTo(eventBuffer, sizeof(eventBuffer), PORT_MONITOR); 373 event.AddInt32("event", opcode); 374 event.AddInt32("port", port); 375 376 DefaultNotificationService::Notify(event, opcode); 377 } 378 379 380 // #pragma mark - 381 382 383 static int 384 dump_port_list(int argc, char** argv) 385 { 386 const char* name = NULL; 387 team_id owner = -1; 388 389 if (argc > 2) { 390 if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner")) 391 owner = strtoul(argv[2], NULL, 0); 392 else if (!strcmp(argv[1], "name")) 393 name = argv[2]; 394 } else if (argc > 1) 395 owner = strtoul(argv[1], NULL, 0); 396 397 kprintf("port id cap read-cnt write-cnt total team " 398 "name\n"); 399 400 for (PortHashTable::Iterator it = sPorts.GetIterator(); 401 Port* port = it.Next();) { 402 if ((owner != -1 && port->owner != owner) 403 || (name != NULL && strstr(port->lock.name, name) == NULL)) 404 continue; 405 406 kprintf("%p %8" B_PRId32 " %4" B_PRId32 " %9" B_PRIu32 " %9" B_PRId32 407 " %8" B_PRId32 " %6" B_PRId32 " %s\n", port, port->id, 408 port->capacity, port->read_count, port->write_count, 409 port->total_count, port->owner, port->lock.name); 410 } 411 412 return 0; 413 } 414 415 416 static void 417 _dump_port_info(Port* port) 418 { 419 kprintf("PORT: %p\n", port); 420 kprintf(" id: %" B_PRId32 "\n", port->id); 421 kprintf(" name: \"%s\"\n", port->lock.name); 422 kprintf(" owner: %" B_PRId32 "\n", port->owner); 423 kprintf(" capacity: %" B_PRId32 "\n", port->capacity); 424 kprintf(" read_count: %" B_PRIu32 "\n", port->read_count); 425 kprintf(" write_count: %" B_PRId32 "\n", port->write_count); 426 kprintf(" total count: %" B_PRId32 "\n", port->total_count); 427 428 if (!port->messages.IsEmpty()) { 429 kprintf("messages:\n"); 430 431 MessageList::Iterator iterator = port->messages.GetIterator(); 432 while (port_message* message = iterator.Next()) { 433 kprintf(" %p %08" B_PRIx32 " %ld\n", message, message->code, message->size); 434 } 435 } 436 437 set_debug_variable("_port", (addr_t)port); 438 set_debug_variable("_portID", port->id); 439 set_debug_variable("_owner", port->owner); 440 } 441 442 443 static int 444 dump_port_info(int argc, char** argv) 445 { 446 ConditionVariable* condition = NULL; 447 const char* name = NULL; 448 449 if (argc < 2) { 450 print_debugger_command_usage(argv[0]); 451 return 0; 452 } 453 454 if (argc > 2) { 455 if (!strcmp(argv[1], "address")) { 456 _dump_port_info((Port*)parse_expression(argv[2])); 457 return 0; 458 } else if (!strcmp(argv[1], "condition")) 459 condition = (ConditionVariable*)parse_expression(argv[2]); 460 else if (!strcmp(argv[1], "name")) 461 name = argv[2]; 462 } else if (parse_expression(argv[1]) > 0) { 463 // if the argument looks like a number, treat it as such 464 int32 num = parse_expression(argv[1]); 465 Port* port = sPorts.Lookup(num); 466 if (port == NULL) { 467 kprintf("port %" B_PRId32 " (%#" B_PRIx32 ") doesn't exist!\n", 468 num, num); 469 return 0; 470 } 471 _dump_port_info(port); 472 return 0; 473 } else 474 name = argv[1]; 475 476 // walk through the ports list, trying to match name 477 for (PortHashTable::Iterator it = sPorts.GetIterator(); 478 Port* port = it.Next();) { 479 if ((name != NULL && port->lock.name != NULL 480 && !strcmp(name, port->lock.name)) 481 || (condition != NULL && (&port->read_condition == condition 482 || &port->write_condition == condition))) { 483 _dump_port_info(port); 484 return 0; 485 } 486 } 487 488 return 0; 489 } 490 491 492 /*! Notifies the port's select events. 493 The port must be locked. 494 */ 495 static void 496 notify_port_select_events(Port* port, uint16 events) 497 { 498 if (port->select_infos) 499 notify_select_events_list(port->select_infos, events); 500 } 501 502 503 static Port* 504 get_locked_port(port_id id) 505 { 506 MutexLocker portsLocker(sPortsLock); 507 508 Port* port = sPorts.Lookup(id); 509 if (port != NULL) 510 mutex_lock(&port->lock); 511 return port; 512 } 513 514 515 /*! You need to own the port's lock when calling this function */ 516 static inline bool 517 is_port_closed(Port* port) 518 { 519 return port->capacity == 0; 520 } 521 522 523 static void 524 put_port_message(port_message* message) 525 { 526 size_t size = sizeof(port_message) + message->size; 527 heap_free(sPortAllocator, message); 528 529 MutexLocker quotaLocker(sPortQuotaLock); 530 sTotalSpaceInUse -= size; 531 if (sWaitingForSpace > 0) 532 sNoSpaceCondition.NotifyAll(); 533 } 534 535 536 static status_t 537 get_port_message(int32 code, size_t bufferSize, uint32 flags, bigtime_t timeout, 538 port_message** _message, Port& port) 539 { 540 size_t size = sizeof(port_message) + bufferSize; 541 bool needToWait = false; 542 543 MutexLocker quotaLocker(sPortQuotaLock); 544 545 while (true) { 546 while (sTotalSpaceInUse + size > kTotalSpaceLimit || needToWait) { 547 // TODO: add per team limit 548 // We are not allowed to create another heap area, as our 549 // space limit has been reached - just wait until we get 550 // some free space again. 551 552 // TODO: we don't want to wait - but does that also mean we 553 // shouldn't wait for the area creation? 554 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 555 return B_WOULD_BLOCK; 556 557 ConditionVariableEntry entry; 558 sNoSpaceCondition.Add(&entry); 559 560 sWaitingForSpace++; 561 quotaLocker.Unlock(); 562 563 port_id portID = port.id; 564 mutex_unlock(&port.lock); 565 566 status_t status = entry.Wait(flags, timeout); 567 568 // re-lock the port and the quota 569 Port* newPort = get_locked_port(portID); 570 quotaLocker.Lock(); 571 sWaitingForSpace--; 572 573 if (newPort != &port || is_port_closed(&port)) { 574 // the port is no longer usable 575 return B_BAD_PORT_ID; 576 } 577 578 if (status == B_TIMED_OUT) 579 return B_TIMED_OUT; 580 581 needToWait = false; 582 continue; 583 } 584 585 int32 areaChangeCounter = sAreaChangeCounter; 586 sTotalSpaceInUse += size; 587 quotaLocker.Unlock(); 588 589 // Quota is fulfilled, try to allocate the buffer 590 591 port_message* message 592 = (port_message*)heap_memalign(sPortAllocator, 0, size); 593 if (message != NULL) { 594 message->code = code; 595 message->size = bufferSize; 596 597 *_message = message; 598 return B_OK; 599 } 600 601 quotaLocker.Lock(); 602 603 // We weren't able to allocate and we'll start over, including 604 // re-acquireing the quota, so we remove our size from the in-use 605 // counter again. 606 sTotalSpaceInUse -= size; 607 608 if (areaChangeCounter != sAreaChangeCounter) { 609 // There was already an area added since we tried allocating, 610 // start over. 611 continue; 612 } 613 614 // Create a new area for the heap to use 615 616 addr_t base; 617 area_id area = create_area("port grown buffer", (void**)&base, 618 B_ANY_KERNEL_ADDRESS, kBufferGrowRate, B_NO_LOCK, 619 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 620 if (area < 0) { 621 // We'll have to get by with what we have, so wait for someone 622 // to free a message instead. We enforce waiting so that we don't 623 // try to create a new area over and over. 624 needToWait = true; 625 continue; 626 } 627 628 heap_add_area(sPortAllocator, area, base, kBufferGrowRate); 629 630 sAreaChangeCounter++; 631 if (sWaitingForSpace > 0) 632 sNoSpaceCondition.NotifyAll(); 633 } 634 } 635 636 637 /*! Fills the port_info structure with information from the specified 638 port. 639 The port's lock must be held when called. 640 */ 641 static void 642 fill_port_info(Port* port, port_info* info, size_t size) 643 { 644 info->port = port->id; 645 info->team = port->owner; 646 info->capacity = port->capacity; 647 648 info->queue_count = port->read_count; 649 info->total_count = port->total_count; 650 651 strlcpy(info->name, port->lock.name, B_OS_NAME_LENGTH); 652 } 653 654 655 static ssize_t 656 copy_port_message(port_message* message, int32* _code, void* buffer, 657 size_t bufferSize, bool userCopy) 658 { 659 // check output buffer size 660 size_t size = min_c(bufferSize, message->size); 661 662 // copy message 663 if (_code != NULL) 664 *_code = message->code; 665 666 if (size > 0) { 667 if (userCopy) { 668 status_t status = user_memcpy(buffer, message->buffer, size); 669 if (status != B_OK) 670 return status; 671 } else 672 memcpy(buffer, message->buffer, size); 673 } 674 675 return size; 676 } 677 678 679 static void 680 uninit_port_locked(Port* port) 681 { 682 notify_port_select_events(port, B_EVENT_INVALID); 683 port->select_infos = NULL; 684 685 // Release the threads that were blocking on this port. 686 // read_port() will see the B_BAD_PORT_ID return value, and act accordingly 687 port->read_condition.NotifyAll(false, B_BAD_PORT_ID); 688 port->write_condition.NotifyAll(false, B_BAD_PORT_ID); 689 sNotificationService.Notify(PORT_REMOVED, port->id); 690 } 691 692 693 // #pragma mark - private kernel API 694 695 696 /*! This function deletes all the ports that are owned by the passed team. 697 */ 698 void 699 delete_owned_ports(Team* team) 700 { 701 TRACE(("delete_owned_ports(owner = %ld)\n", team->id)); 702 703 MutexLocker portsLocker(sPortsLock); 704 705 // move the ports from the team's port list to a local list 706 struct list queue; 707 list_move_to_list(&team->port_list, &queue); 708 709 // iterate through the list or ports, remove them from the hash table and 710 // uninitialize them 711 Port* port = (Port*)list_get_first_item(&queue); 712 while (port != NULL) { 713 MutexLocker locker(port->lock); 714 sPorts.Remove(port); 715 uninit_port_locked(port); 716 sUsedPorts--; 717 718 port = (Port*)list_get_next_item(&queue, port); 719 } 720 721 portsLocker.Unlock(); 722 723 // delete the ports 724 while (Port* port = (Port*)list_remove_head_item(&queue)) 725 delete port; 726 } 727 728 729 int32 730 port_max_ports(void) 731 { 732 return sMaxPorts; 733 } 734 735 736 int32 737 port_used_ports(void) 738 { 739 return sUsedPorts; 740 } 741 742 743 status_t 744 port_init(kernel_args *args) 745 { 746 // initialize ports table 747 new(&sPorts) PortHashTable; 748 if (sPorts.Init() != B_OK) { 749 panic("Failed to init port hash table!"); 750 return B_NO_MEMORY; 751 } 752 753 addr_t base; 754 if (create_area("port heap", (void**)&base, B_ANY_KERNEL_ADDRESS, 755 kInitialPortBufferSize, B_NO_LOCK, 756 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA) < 0) { 757 // TODO: Since port_init() is invoked before the boot partition is 758 // mounted, the underlying VMAnonymousCache cannot commit swap space 759 // upon creation and thus the pages aren't swappable after all. This 760 // makes the area essentially B_LAZY_LOCK with additional overhead. 761 panic("unable to allocate port area!\n"); 762 return B_ERROR; 763 } 764 765 static const heap_class kBufferHeapClass = { "port heap", 100, 766 PORT_MAX_MESSAGE_SIZE + sizeof(port_message), 2 * 1024, 767 sizeof(port_message), 4, 2, 24 }; 768 sPortAllocator = heap_create_allocator("port buffer", base, 769 kInitialPortBufferSize, &kBufferHeapClass, true); 770 if (sPortAllocator == NULL) { 771 panic("unable to create port heap"); 772 return B_NO_MEMORY; 773 } 774 775 sNoSpaceCondition.Init(&sPorts, "port space"); 776 777 // add debugger commands 778 add_debugger_command_etc("ports", &dump_port_list, 779 "Dump a list of all active ports (for team, with name, etc.)", 780 "[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n" 781 "Prints a list of all active ports meeting the given\n" 782 "requirement. If no argument is given, all ports are listed.\n" 783 " <team> - The team owning the ports.\n" 784 " <name> - Part of the name of the ports.\n", 0); 785 add_debugger_command_etc("port", &dump_port_info, 786 "Dump info about a particular port", 787 "(<id> | [ \"address\" ] <address>) | ([ \"name\" ] <name>) " 788 "| (\"condition\" <address>)\n" 789 "Prints info about the specified port.\n" 790 " <address> - Pointer to the port structure.\n" 791 " <name> - Name of the port.\n" 792 " <condition> - address of the port's read or write condition.\n", 0); 793 794 new(&sNotificationService) PortNotificationService(); 795 sPortsActive = true; 796 return B_OK; 797 } 798 799 800 // #pragma mark - public kernel API 801 802 803 port_id 804 create_port(int32 queueLength, const char* name) 805 { 806 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength, 807 name)); 808 809 if (!sPortsActive) { 810 panic("ports used too early!\n"); 811 return B_BAD_PORT_ID; 812 } 813 if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH) 814 return B_BAD_VALUE; 815 816 Team* team = thread_get_current_thread()->team; 817 if (team == NULL) 818 return B_BAD_TEAM_ID; 819 820 // check & dup name 821 char* nameBuffer = strdup(name != NULL ? name : "unnamed port"); 822 if (nameBuffer == NULL) 823 return B_NO_MEMORY; 824 825 // create a port 826 Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength, 827 nameBuffer); 828 if (port == NULL) { 829 free(nameBuffer); 830 return B_NO_MEMORY; 831 } 832 ObjectDeleter<Port> portDeleter(port); 833 834 MutexLocker locker(sPortsLock); 835 836 // check the ports limit 837 if (sUsedPorts >= sMaxPorts) 838 return B_NO_MORE_PORTS; 839 840 sUsedPorts++; 841 842 // allocate a port ID 843 do { 844 port->id = sNextPortID++; 845 846 // handle integer overflow 847 if (sNextPortID < 0) 848 sNextPortID = 1; 849 } while (sPorts.Lookup(port->id) != NULL); 850 851 // insert port in table and team list 852 sPorts.Insert(port); 853 list_add_item(&team->port_list, &port->team_link); 854 portDeleter.Detach(); 855 856 // tracing, notifications, etc. 857 T(Create(port)); 858 859 port_id id = port->id; 860 861 locker.Unlock(); 862 863 TRACE(("create_port() done: port created %ld\n", id)); 864 865 sNotificationService.Notify(PORT_ADDED, id); 866 return id; 867 } 868 869 870 status_t 871 close_port(port_id id) 872 { 873 TRACE(("close_port(id = %ld)\n", id)); 874 875 if (!sPortsActive || id < 0) 876 return B_BAD_PORT_ID; 877 878 // get the port 879 Port* port = get_locked_port(id); 880 if (port == NULL) { 881 TRACE(("close_port: invalid port_id %ld\n", id)); 882 return B_BAD_PORT_ID; 883 } 884 MutexLocker lock(&port->lock, true); 885 886 // mark port to disable writing - deleting the semaphores will 887 // wake up waiting read/writes 888 port->capacity = 0; 889 890 notify_port_select_events(port, B_EVENT_INVALID); 891 port->select_infos = NULL; 892 893 port->read_condition.NotifyAll(false, B_BAD_PORT_ID); 894 port->write_condition.NotifyAll(false, B_BAD_PORT_ID); 895 896 return B_OK; 897 } 898 899 900 status_t 901 delete_port(port_id id) 902 { 903 TRACE(("delete_port(id = %ld)\n", id)); 904 905 if (!sPortsActive || id < 0) 906 return B_BAD_PORT_ID; 907 908 // get the port and remove it from the hash table and the team 909 Port* port; 910 MutexLocker locker; 911 { 912 MutexLocker portsLocker(sPortsLock); 913 914 port = sPorts.Lookup(id); 915 if (port == NULL) { 916 TRACE(("delete_port: invalid port_id %ld\n", id)); 917 return B_BAD_PORT_ID; 918 } 919 920 sPorts.Remove(port); 921 list_remove_link(&port->team_link); 922 923 sUsedPorts--; 924 925 locker.SetTo(port->lock, false); 926 927 uninit_port_locked(port); 928 } 929 930 T(Delete(port)); 931 932 locker.Unlock(); 933 934 delete port; 935 936 return B_OK; 937 } 938 939 940 status_t 941 select_port(int32 id, struct select_info* info, bool kernel) 942 { 943 if (id < 0) 944 return B_BAD_PORT_ID; 945 946 // get the port 947 Port* port = get_locked_port(id); 948 if (port == NULL) 949 return B_BAD_PORT_ID; 950 MutexLocker locker(port->lock, true); 951 952 // port must not yet be closed 953 if (is_port_closed(port)) 954 return B_BAD_PORT_ID; 955 956 if (!kernel && port->owner == team_get_kernel_team_id()) { 957 // kernel port, but call from userland 958 return B_NOT_ALLOWED; 959 } 960 961 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID; 962 963 if (info->selected_events != 0) { 964 uint16 events = 0; 965 966 info->next = port->select_infos; 967 port->select_infos = info; 968 969 // check for events 970 if ((info->selected_events & B_EVENT_READ) != 0 971 && !port->messages.IsEmpty()) { 972 events |= B_EVENT_READ; 973 } 974 975 if (port->write_count > 0) 976 events |= B_EVENT_WRITE; 977 978 if (events != 0) 979 notify_select_events(info, events); 980 } 981 982 return B_OK; 983 } 984 985 986 status_t 987 deselect_port(int32 id, struct select_info* info, bool kernel) 988 { 989 if (id < 0) 990 return B_BAD_PORT_ID; 991 if (info->selected_events == 0) 992 return B_OK; 993 994 // get the port 995 Port* port = get_locked_port(id); 996 if (port == NULL) 997 return B_BAD_PORT_ID; 998 MutexLocker locker(port->lock, true); 999 1000 // find and remove the infos 1001 select_info** infoLocation = &port->select_infos; 1002 while (*infoLocation != NULL && *infoLocation != info) 1003 infoLocation = &(*infoLocation)->next; 1004 1005 if (*infoLocation == info) 1006 *infoLocation = info->next; 1007 1008 return B_OK; 1009 } 1010 1011 1012 port_id 1013 find_port(const char* name) 1014 { 1015 TRACE(("find_port(name = \"%s\")\n", name)); 1016 1017 if (!sPortsActive) { 1018 panic("ports used too early!\n"); 1019 return B_NAME_NOT_FOUND; 1020 } 1021 if (name == NULL) 1022 return B_BAD_VALUE; 1023 1024 MutexLocker portsLocker(sPortsLock); 1025 1026 for (PortHashTable::Iterator it = sPorts.GetIterator(); 1027 Port* port = it.Next();) { 1028 if (!strcmp(name, port->lock.name)) 1029 return port->id; 1030 } 1031 1032 return B_NAME_NOT_FOUND; 1033 } 1034 1035 1036 status_t 1037 _get_port_info(port_id id, port_info* info, size_t size) 1038 { 1039 TRACE(("get_port_info(id = %ld)\n", id)); 1040 1041 if (info == NULL || size != sizeof(port_info)) 1042 return B_BAD_VALUE; 1043 if (!sPortsActive || id < 0) 1044 return B_BAD_PORT_ID; 1045 1046 // get the port 1047 Port* port = get_locked_port(id); 1048 if (port == NULL) { 1049 TRACE(("get_port_info: invalid port_id %ld\n", id)); 1050 return B_BAD_PORT_ID; 1051 } 1052 MutexLocker locker(port->lock, true); 1053 1054 // fill a port_info struct with info 1055 fill_port_info(port, info, size); 1056 return B_OK; 1057 } 1058 1059 1060 status_t 1061 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info, 1062 size_t size) 1063 { 1064 TRACE(("get_next_port_info(team = %ld)\n", teamID)); 1065 1066 if (info == NULL || size != sizeof(port_info) || _cookie == NULL 1067 || teamID < 0) { 1068 return B_BAD_VALUE; 1069 } 1070 if (!sPortsActive) 1071 return B_BAD_PORT_ID; 1072 1073 Team* team = Team::Get(teamID); 1074 if (team == NULL) 1075 return B_BAD_TEAM_ID; 1076 BReference<Team> teamReference(team, true); 1077 1078 // iterate through the team's port list 1079 MutexLocker portsLocker(sPortsLock); 1080 1081 int32 stopIndex = *_cookie; 1082 int32 index = 0; 1083 1084 Port* port = (Port*)list_get_first_item(&team->port_list); 1085 while (port != NULL) { 1086 if (!is_port_closed(port)) { 1087 if (index == stopIndex) 1088 break; 1089 index++; 1090 } 1091 1092 port = (Port*)list_get_next_item(&team->port_list, port); 1093 } 1094 1095 if (port == NULL) 1096 return B_BAD_PORT_ID; 1097 1098 // fill in the port info 1099 MutexLocker locker(port->lock); 1100 portsLocker.Unlock(); 1101 fill_port_info(port, info, size); 1102 1103 *_cookie = stopIndex + 1; 1104 return B_OK; 1105 } 1106 1107 1108 ssize_t 1109 port_buffer_size(port_id id) 1110 { 1111 return port_buffer_size_etc(id, 0, 0); 1112 } 1113 1114 1115 ssize_t 1116 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout) 1117 { 1118 port_message_info info; 1119 status_t error = get_port_message_info_etc(id, &info, flags, timeout); 1120 return error != B_OK ? error : info.size; 1121 } 1122 1123 1124 status_t 1125 _get_port_message_info_etc(port_id id, port_message_info* info, 1126 size_t infoSize, uint32 flags, bigtime_t timeout) 1127 { 1128 if (info == NULL || infoSize != sizeof(port_message_info)) 1129 return B_BAD_VALUE; 1130 if (!sPortsActive || id < 0) 1131 return B_BAD_PORT_ID; 1132 1133 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1134 | B_ABSOLUTE_TIMEOUT; 1135 1136 // get the port 1137 Port* port = get_locked_port(id); 1138 if (port == NULL) 1139 return B_BAD_PORT_ID; 1140 MutexLocker locker(port->lock, true); 1141 1142 if (is_port_closed(port) && port->messages.IsEmpty()) { 1143 T(Info(port, 0, B_BAD_PORT_ID)); 1144 TRACE(("_get_port_message_info_etc(): closed port %ld\n", id)); 1145 return B_BAD_PORT_ID; 1146 } 1147 1148 while (port->read_count == 0) { 1149 // We need to wait for a message to appear 1150 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1151 return B_WOULD_BLOCK; 1152 1153 ConditionVariableEntry entry; 1154 port->read_condition.Add(&entry); 1155 1156 locker.Unlock(); 1157 1158 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1159 status_t status = entry.Wait(flags, timeout); 1160 1161 if (status != B_OK) { 1162 T(Info(port, 0, status)); 1163 return status; 1164 } 1165 1166 // re-lock 1167 Port* newPort = get_locked_port(id); 1168 if (newPort == NULL) { 1169 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1170 return B_BAD_PORT_ID; 1171 } 1172 locker.SetTo(newPort->lock, true); 1173 1174 if (newPort != port 1175 || (is_port_closed(port) && port->messages.IsEmpty())) { 1176 // the port is no longer there 1177 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1178 return B_BAD_PORT_ID; 1179 } 1180 } 1181 1182 // determine tail & get the length of the message 1183 port_message* message = port->messages.Head(); 1184 if (message == NULL) { 1185 panic("port %" B_PRId32 ": no messages found\n", port->id); 1186 return B_ERROR; 1187 } 1188 1189 info->size = message->size; 1190 info->sender = message->sender; 1191 info->sender_group = message->sender_group; 1192 info->sender_team = message->sender_team; 1193 1194 T(Info(id, id->read_count, id->write_count, message->code, B_OK)); 1195 1196 // notify next one, as we haven't read from the port 1197 port->read_condition.NotifyOne(); 1198 1199 return B_OK; 1200 } 1201 1202 1203 ssize_t 1204 port_count(port_id id) 1205 { 1206 if (!sPortsActive || id < 0) 1207 return B_BAD_PORT_ID; 1208 1209 // get the port 1210 Port* port = get_locked_port(id); 1211 if (port == NULL) { 1212 TRACE(("port_count: invalid port_id %ld\n", id)); 1213 return B_BAD_PORT_ID; 1214 } 1215 MutexLocker locker(port->lock, true); 1216 1217 // return count of messages 1218 return port->read_count; 1219 } 1220 1221 1222 ssize_t 1223 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize) 1224 { 1225 return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0); 1226 } 1227 1228 1229 ssize_t 1230 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize, 1231 uint32 flags, bigtime_t timeout) 1232 { 1233 if (!sPortsActive || id < 0) 1234 return B_BAD_PORT_ID; 1235 if ((buffer == NULL && bufferSize > 0) || timeout < 0) 1236 return B_BAD_VALUE; 1237 1238 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0; 1239 bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0; 1240 // TODO: we could allow peeking for user apps now 1241 1242 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1243 | B_ABSOLUTE_TIMEOUT; 1244 1245 // get the port 1246 Port* port = get_locked_port(id); 1247 if (port == NULL) 1248 return B_BAD_PORT_ID; 1249 MutexLocker locker(port->lock, true); 1250 1251 if (is_port_closed(port) && port->messages.IsEmpty()) { 1252 T(Read(port, 0, B_BAD_PORT_ID)); 1253 TRACE(("read_port_etc(): closed port %ld\n", id)); 1254 return B_BAD_PORT_ID; 1255 } 1256 1257 while (port->read_count == 0) { 1258 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1259 return B_WOULD_BLOCK; 1260 1261 // We need to wait for a message to appear 1262 ConditionVariableEntry entry; 1263 port->read_condition.Add(&entry); 1264 1265 locker.Unlock(); 1266 1267 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1268 status_t status = entry.Wait(flags, timeout); 1269 1270 // re-lock 1271 Port* newPort = get_locked_port(id); 1272 if (newPort == NULL) { 1273 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1274 return B_BAD_PORT_ID; 1275 } 1276 locker.SetTo(newPort->lock, true); 1277 1278 if (newPort != port 1279 || (is_port_closed(port) && port->messages.IsEmpty())) { 1280 // the port is no longer there 1281 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1282 return B_BAD_PORT_ID; 1283 } 1284 1285 if (status != B_OK) { 1286 T(Read(port, 0, status)); 1287 return status; 1288 } 1289 } 1290 1291 // determine tail & get the length of the message 1292 port_message* message = port->messages.Head(); 1293 if (message == NULL) { 1294 panic("port %" B_PRId32 ": no messages found\n", port->id); 1295 return B_ERROR; 1296 } 1297 1298 if (peekOnly) { 1299 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1300 userCopy); 1301 1302 T(Read(port, message->code, size)); 1303 1304 port->read_condition.NotifyOne(); 1305 // we only peeked, but didn't grab the message 1306 return size; 1307 } 1308 1309 port->messages.RemoveHead(); 1310 port->total_count++; 1311 port->write_count++; 1312 port->read_count--; 1313 1314 notify_port_select_events(port, B_EVENT_WRITE); 1315 port->write_condition.NotifyOne(); 1316 // make one spot in queue available again for write 1317 1318 T(Read(id, port->read_count, port->write_count, message->code, 1319 min_c(bufferSize, message->size))); 1320 1321 locker.Unlock(); 1322 1323 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1324 userCopy); 1325 1326 put_port_message(message); 1327 return size; 1328 } 1329 1330 1331 status_t 1332 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize) 1333 { 1334 iovec vec = { (void*)buffer, bufferSize }; 1335 1336 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0); 1337 } 1338 1339 1340 status_t 1341 write_port_etc(port_id id, int32 msgCode, const void* buffer, 1342 size_t bufferSize, uint32 flags, bigtime_t timeout) 1343 { 1344 iovec vec = { (void*)buffer, bufferSize }; 1345 1346 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout); 1347 } 1348 1349 1350 status_t 1351 writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs, 1352 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1353 { 1354 if (!sPortsActive || id < 0) 1355 return B_BAD_PORT_ID; 1356 if (bufferSize > PORT_MAX_MESSAGE_SIZE) 1357 return B_BAD_VALUE; 1358 1359 // mask irrelevant flags (for acquire_sem() usage) 1360 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1361 | B_ABSOLUTE_TIMEOUT; 1362 if ((flags & B_RELATIVE_TIMEOUT) != 0 1363 && timeout != B_INFINITE_TIMEOUT && timeout > 0) { 1364 // Make the timeout absolute, since we have more than one step where 1365 // we might have to wait 1366 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT; 1367 timeout += system_time(); 1368 } 1369 1370 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0; 1371 1372 status_t status; 1373 port_message* message = NULL; 1374 1375 // get the port 1376 Port* port = get_locked_port(id); 1377 if (port == NULL) { 1378 TRACE(("write_port_etc: invalid port_id %ld\n", id)); 1379 return B_BAD_PORT_ID; 1380 } 1381 MutexLocker locker(port->lock, true); 1382 1383 if (is_port_closed(port)) { 1384 TRACE(("write_port_etc: port %ld closed\n", id)); 1385 return B_BAD_PORT_ID; 1386 } 1387 1388 if (port->write_count <= 0) { 1389 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1390 return B_WOULD_BLOCK; 1391 1392 port->write_count--; 1393 1394 // We need to block in order to wait for a free message slot 1395 ConditionVariableEntry entry; 1396 port->write_condition.Add(&entry); 1397 1398 locker.Unlock(); 1399 1400 status = entry.Wait(flags, timeout); 1401 1402 // re-lock 1403 Port* newPort = get_locked_port(id); 1404 if (newPort == NULL) { 1405 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1406 return B_BAD_PORT_ID; 1407 } 1408 locker.SetTo(newPort->lock, true); 1409 1410 if (newPort != port || is_port_closed(port)) { 1411 // the port is no longer there 1412 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1413 return B_BAD_PORT_ID; 1414 } 1415 1416 if (status != B_OK) 1417 goto error; 1418 } else 1419 port->write_count--; 1420 1421 status = get_port_message(msgCode, bufferSize, flags, timeout, 1422 &message, *port); 1423 if (status != B_OK) { 1424 if (status == B_BAD_PORT_ID) { 1425 // the port had to be unlocked and is now no longer there 1426 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1427 return B_BAD_PORT_ID; 1428 } 1429 1430 goto error; 1431 } 1432 1433 // sender credentials 1434 message->sender = geteuid(); 1435 message->sender_group = getegid(); 1436 message->sender_team = team_get_current_team_id(); 1437 1438 if (bufferSize > 0) { 1439 size_t offset = 0; 1440 for (uint32 i = 0; i < vecCount; i++) { 1441 size_t bytes = msgVecs[i].iov_len; 1442 if (bytes > bufferSize) 1443 bytes = bufferSize; 1444 1445 if (userCopy) { 1446 status_t status = user_memcpy(message->buffer + offset, 1447 msgVecs[i].iov_base, bytes); 1448 if (status != B_OK) { 1449 put_port_message(message); 1450 goto error; 1451 } 1452 } else 1453 memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes); 1454 1455 bufferSize -= bytes; 1456 if (bufferSize == 0) 1457 break; 1458 1459 offset += bytes; 1460 } 1461 } 1462 1463 port->messages.Add(message); 1464 port->read_count++; 1465 1466 T(Write(id, port->read_count, port->write_count, message->code, 1467 message->size, B_OK)); 1468 1469 notify_port_select_events(port, B_EVENT_READ); 1470 port->read_condition.NotifyOne(); 1471 return B_OK; 1472 1473 error: 1474 // Give up our slot in the queue again, and let someone else 1475 // try and fail 1476 T(Write(id, port->read_count, port->write_count, 0, 0, status)); 1477 port->write_count++; 1478 notify_port_select_events(port, B_EVENT_WRITE); 1479 port->write_condition.NotifyOne(); 1480 1481 return status; 1482 } 1483 1484 1485 status_t 1486 set_port_owner(port_id id, team_id newTeamID) 1487 { 1488 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID)); 1489 1490 if (id < 0) 1491 return B_BAD_PORT_ID; 1492 1493 // get the new team 1494 Team* team = Team::Get(newTeamID); 1495 if (team == NULL) 1496 return B_BAD_TEAM_ID; 1497 BReference<Team> teamReference(team, true); 1498 1499 // get the port 1500 MutexLocker portsLocker(sPortsLock); 1501 Port* port = sPorts.Lookup(id); 1502 if (port == NULL) { 1503 TRACE(("set_port_owner: invalid port_id %ld\n", id)); 1504 return B_BAD_PORT_ID; 1505 } 1506 MutexLocker locker(port->lock); 1507 1508 // transfer ownership to other team 1509 if (team->id != port->owner) { 1510 list_remove_link(&port->team_link); 1511 list_add_item(&team->port_list, &port->team_link); 1512 port->owner = team->id; 1513 } 1514 1515 T(OwnerChange(port, team->id, B_OK)); 1516 return B_OK; 1517 } 1518 1519 1520 // #pragma mark - syscalls 1521 1522 1523 port_id 1524 _user_create_port(int32 queueLength, const char *userName) 1525 { 1526 char name[B_OS_NAME_LENGTH]; 1527 1528 if (userName == NULL) 1529 return create_port(queueLength, NULL); 1530 1531 if (!IS_USER_ADDRESS(userName) 1532 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1533 return B_BAD_ADDRESS; 1534 1535 return create_port(queueLength, name); 1536 } 1537 1538 1539 status_t 1540 _user_close_port(port_id id) 1541 { 1542 return close_port(id); 1543 } 1544 1545 1546 status_t 1547 _user_delete_port(port_id id) 1548 { 1549 return delete_port(id); 1550 } 1551 1552 1553 port_id 1554 _user_find_port(const char *userName) 1555 { 1556 char name[B_OS_NAME_LENGTH]; 1557 1558 if (userName == NULL) 1559 return B_BAD_VALUE; 1560 if (!IS_USER_ADDRESS(userName) 1561 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1562 return B_BAD_ADDRESS; 1563 1564 return find_port(name); 1565 } 1566 1567 1568 status_t 1569 _user_get_port_info(port_id id, struct port_info *userInfo) 1570 { 1571 struct port_info info; 1572 status_t status; 1573 1574 if (userInfo == NULL) 1575 return B_BAD_VALUE; 1576 if (!IS_USER_ADDRESS(userInfo)) 1577 return B_BAD_ADDRESS; 1578 1579 status = get_port_info(id, &info); 1580 1581 // copy back to user space 1582 if (status == B_OK 1583 && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK) 1584 return B_BAD_ADDRESS; 1585 1586 return status; 1587 } 1588 1589 1590 status_t 1591 _user_get_next_port_info(team_id team, int32 *userCookie, 1592 struct port_info *userInfo) 1593 { 1594 struct port_info info; 1595 status_t status; 1596 int32 cookie; 1597 1598 if (userCookie == NULL || userInfo == NULL) 1599 return B_BAD_VALUE; 1600 if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo) 1601 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 1602 return B_BAD_ADDRESS; 1603 1604 status = get_next_port_info(team, &cookie, &info); 1605 1606 // copy back to user space 1607 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK 1608 || (status == B_OK && user_memcpy(userInfo, &info, 1609 sizeof(struct port_info)) < B_OK)) 1610 return B_BAD_ADDRESS; 1611 1612 return status; 1613 } 1614 1615 1616 ssize_t 1617 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout) 1618 { 1619 syscall_restart_handle_timeout_pre(flags, timeout); 1620 1621 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT, 1622 timeout); 1623 1624 return syscall_restart_handle_timeout_post(status, timeout); 1625 } 1626 1627 1628 ssize_t 1629 _user_port_count(port_id port) 1630 { 1631 return port_count(port); 1632 } 1633 1634 1635 status_t 1636 _user_set_port_owner(port_id port, team_id team) 1637 { 1638 return set_port_owner(port, team); 1639 } 1640 1641 1642 ssize_t 1643 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer, 1644 size_t bufferSize, uint32 flags, bigtime_t timeout) 1645 { 1646 int32 messageCode; 1647 ssize_t bytesRead; 1648 1649 syscall_restart_handle_timeout_pre(flags, timeout); 1650 1651 if (userBuffer == NULL && bufferSize != 0) 1652 return B_BAD_VALUE; 1653 if ((userCode != NULL && !IS_USER_ADDRESS(userCode)) 1654 || (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))) 1655 return B_BAD_ADDRESS; 1656 1657 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize, 1658 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1659 1660 if (bytesRead >= 0 && userCode != NULL 1661 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK) 1662 return B_BAD_ADDRESS; 1663 1664 return syscall_restart_handle_timeout_post(bytesRead, timeout); 1665 } 1666 1667 1668 status_t 1669 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer, 1670 size_t bufferSize, uint32 flags, bigtime_t timeout) 1671 { 1672 iovec vec = { (void *)userBuffer, bufferSize }; 1673 1674 syscall_restart_handle_timeout_pre(flags, timeout); 1675 1676 if (userBuffer == NULL && bufferSize != 0) 1677 return B_BAD_VALUE; 1678 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)) 1679 return B_BAD_ADDRESS; 1680 1681 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize, 1682 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1683 1684 return syscall_restart_handle_timeout_post(status, timeout); 1685 } 1686 1687 1688 status_t 1689 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs, 1690 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1691 { 1692 syscall_restart_handle_timeout_pre(flags, timeout); 1693 1694 if (userVecs == NULL && bufferSize != 0) 1695 return B_BAD_VALUE; 1696 if (userVecs != NULL && !IS_USER_ADDRESS(userVecs)) 1697 return B_BAD_ADDRESS; 1698 1699 iovec *vecs = NULL; 1700 if (userVecs && vecCount != 0) { 1701 vecs = (iovec*)malloc(sizeof(iovec) * vecCount); 1702 if (vecs == NULL) 1703 return B_NO_MEMORY; 1704 1705 if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) { 1706 free(vecs); 1707 return B_BAD_ADDRESS; 1708 } 1709 } 1710 1711 status_t status = writev_port_etc(port, messageCode, vecs, vecCount, 1712 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, 1713 timeout); 1714 1715 free(vecs); 1716 return syscall_restart_handle_timeout_post(status, timeout); 1717 } 1718 1719 1720 status_t 1721 _user_get_port_message_info_etc(port_id port, port_message_info *userInfo, 1722 size_t infoSize, uint32 flags, bigtime_t timeout) 1723 { 1724 if (userInfo == NULL || infoSize != sizeof(port_message_info)) 1725 return B_BAD_VALUE; 1726 1727 syscall_restart_handle_timeout_pre(flags, timeout); 1728 1729 port_message_info info; 1730 status_t error = _get_port_message_info_etc(port, &info, sizeof(info), 1731 flags | B_CAN_INTERRUPT, timeout); 1732 1733 // copy info to userland 1734 if (error == B_OK && (!IS_USER_ADDRESS(userInfo) 1735 || user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) { 1736 error = B_BAD_ADDRESS; 1737 } 1738 1739 return syscall_restart_handle_timeout_post(error, timeout); 1740 } 1741