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 sNotificationService.Register(); 796 sPortsActive = true; 797 return B_OK; 798 } 799 800 801 // #pragma mark - public kernel API 802 803 804 port_id 805 create_port(int32 queueLength, const char* name) 806 { 807 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength, 808 name)); 809 810 if (!sPortsActive) { 811 panic("ports used too early!\n"); 812 return B_BAD_PORT_ID; 813 } 814 if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH) 815 return B_BAD_VALUE; 816 817 Team* team = thread_get_current_thread()->team; 818 if (team == NULL) 819 return B_BAD_TEAM_ID; 820 821 // check & dup name 822 char* nameBuffer = strdup(name != NULL ? name : "unnamed port"); 823 if (nameBuffer == NULL) 824 return B_NO_MEMORY; 825 826 // create a port 827 Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength, 828 nameBuffer); 829 if (port == NULL) { 830 free(nameBuffer); 831 return B_NO_MEMORY; 832 } 833 ObjectDeleter<Port> portDeleter(port); 834 835 MutexLocker locker(sPortsLock); 836 837 // check the ports limit 838 if (sUsedPorts >= sMaxPorts) 839 return B_NO_MORE_PORTS; 840 841 sUsedPorts++; 842 843 // allocate a port ID 844 do { 845 port->id = sNextPortID++; 846 847 // handle integer overflow 848 if (sNextPortID < 0) 849 sNextPortID = 1; 850 } while (sPorts.Lookup(port->id) != NULL); 851 852 // insert port in table and team list 853 sPorts.Insert(port); 854 list_add_item(&team->port_list, &port->team_link); 855 portDeleter.Detach(); 856 857 // tracing, notifications, etc. 858 T(Create(port)); 859 860 port_id id = port->id; 861 862 locker.Unlock(); 863 864 TRACE(("create_port() done: port created %ld\n", id)); 865 866 sNotificationService.Notify(PORT_ADDED, id); 867 return id; 868 } 869 870 871 status_t 872 close_port(port_id id) 873 { 874 TRACE(("close_port(id = %ld)\n", id)); 875 876 if (!sPortsActive || id < 0) 877 return B_BAD_PORT_ID; 878 879 // get the port 880 Port* port = get_locked_port(id); 881 if (port == NULL) { 882 TRACE(("close_port: invalid port_id %ld\n", id)); 883 return B_BAD_PORT_ID; 884 } 885 MutexLocker lock(&port->lock, true); 886 887 // mark port to disable writing - deleting the semaphores will 888 // wake up waiting read/writes 889 port->capacity = 0; 890 891 notify_port_select_events(port, B_EVENT_INVALID); 892 port->select_infos = NULL; 893 894 port->read_condition.NotifyAll(false, B_BAD_PORT_ID); 895 port->write_condition.NotifyAll(false, B_BAD_PORT_ID); 896 897 return B_OK; 898 } 899 900 901 status_t 902 delete_port(port_id id) 903 { 904 TRACE(("delete_port(id = %ld)\n", id)); 905 906 if (!sPortsActive || id < 0) 907 return B_BAD_PORT_ID; 908 909 // get the port and remove it from the hash table and the team 910 Port* port; 911 MutexLocker locker; 912 { 913 MutexLocker portsLocker(sPortsLock); 914 915 port = sPorts.Lookup(id); 916 if (port == NULL) { 917 TRACE(("delete_port: invalid port_id %ld\n", id)); 918 return B_BAD_PORT_ID; 919 } 920 921 sPorts.Remove(port); 922 list_remove_link(&port->team_link); 923 924 sUsedPorts--; 925 926 locker.SetTo(port->lock, false); 927 928 uninit_port_locked(port); 929 } 930 931 T(Delete(port)); 932 933 locker.Unlock(); 934 935 delete port; 936 937 return B_OK; 938 } 939 940 941 status_t 942 select_port(int32 id, struct select_info* info, bool kernel) 943 { 944 if (id < 0) 945 return B_BAD_PORT_ID; 946 947 // get the port 948 Port* port = get_locked_port(id); 949 if (port == NULL) 950 return B_BAD_PORT_ID; 951 MutexLocker locker(port->lock, true); 952 953 // port must not yet be closed 954 if (is_port_closed(port)) 955 return B_BAD_PORT_ID; 956 957 if (!kernel && port->owner == team_get_kernel_team_id()) { 958 // kernel port, but call from userland 959 return B_NOT_ALLOWED; 960 } 961 962 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID; 963 964 if (info->selected_events != 0) { 965 uint16 events = 0; 966 967 info->next = port->select_infos; 968 port->select_infos = info; 969 970 // check for events 971 if ((info->selected_events & B_EVENT_READ) != 0 972 && !port->messages.IsEmpty()) { 973 events |= B_EVENT_READ; 974 } 975 976 if (port->write_count > 0) 977 events |= B_EVENT_WRITE; 978 979 if (events != 0) 980 notify_select_events(info, events); 981 } 982 983 return B_OK; 984 } 985 986 987 status_t 988 deselect_port(int32 id, struct select_info* info, bool kernel) 989 { 990 if (id < 0) 991 return B_BAD_PORT_ID; 992 if (info->selected_events == 0) 993 return B_OK; 994 995 // get the port 996 Port* port = get_locked_port(id); 997 if (port == NULL) 998 return B_BAD_PORT_ID; 999 MutexLocker locker(port->lock, true); 1000 1001 // find and remove the infos 1002 select_info** infoLocation = &port->select_infos; 1003 while (*infoLocation != NULL && *infoLocation != info) 1004 infoLocation = &(*infoLocation)->next; 1005 1006 if (*infoLocation == info) 1007 *infoLocation = info->next; 1008 1009 return B_OK; 1010 } 1011 1012 1013 port_id 1014 find_port(const char* name) 1015 { 1016 TRACE(("find_port(name = \"%s\")\n", name)); 1017 1018 if (!sPortsActive) { 1019 panic("ports used too early!\n"); 1020 return B_NAME_NOT_FOUND; 1021 } 1022 if (name == NULL) 1023 return B_BAD_VALUE; 1024 1025 MutexLocker portsLocker(sPortsLock); 1026 1027 for (PortHashTable::Iterator it = sPorts.GetIterator(); 1028 Port* port = it.Next();) { 1029 if (!strcmp(name, port->lock.name)) 1030 return port->id; 1031 } 1032 1033 return B_NAME_NOT_FOUND; 1034 } 1035 1036 1037 status_t 1038 _get_port_info(port_id id, port_info* info, size_t size) 1039 { 1040 TRACE(("get_port_info(id = %ld)\n", id)); 1041 1042 if (info == NULL || size != sizeof(port_info)) 1043 return B_BAD_VALUE; 1044 if (!sPortsActive || id < 0) 1045 return B_BAD_PORT_ID; 1046 1047 // get the port 1048 Port* port = get_locked_port(id); 1049 if (port == NULL) { 1050 TRACE(("get_port_info: invalid port_id %ld\n", id)); 1051 return B_BAD_PORT_ID; 1052 } 1053 MutexLocker locker(port->lock, true); 1054 1055 // fill a port_info struct with info 1056 fill_port_info(port, info, size); 1057 return B_OK; 1058 } 1059 1060 1061 status_t 1062 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info, 1063 size_t size) 1064 { 1065 TRACE(("get_next_port_info(team = %ld)\n", teamID)); 1066 1067 if (info == NULL || size != sizeof(port_info) || _cookie == NULL 1068 || teamID < 0) { 1069 return B_BAD_VALUE; 1070 } 1071 if (!sPortsActive) 1072 return B_BAD_PORT_ID; 1073 1074 Team* team = Team::Get(teamID); 1075 if (team == NULL) 1076 return B_BAD_TEAM_ID; 1077 BReference<Team> teamReference(team, true); 1078 1079 // iterate through the team's port list 1080 MutexLocker portsLocker(sPortsLock); 1081 1082 int32 stopIndex = *_cookie; 1083 int32 index = 0; 1084 1085 Port* port = (Port*)list_get_first_item(&team->port_list); 1086 while (port != NULL) { 1087 if (!is_port_closed(port)) { 1088 if (index == stopIndex) 1089 break; 1090 index++; 1091 } 1092 1093 port = (Port*)list_get_next_item(&team->port_list, port); 1094 } 1095 1096 if (port == NULL) 1097 return B_BAD_PORT_ID; 1098 1099 // fill in the port info 1100 MutexLocker locker(port->lock); 1101 portsLocker.Unlock(); 1102 fill_port_info(port, info, size); 1103 1104 *_cookie = stopIndex + 1; 1105 return B_OK; 1106 } 1107 1108 1109 ssize_t 1110 port_buffer_size(port_id id) 1111 { 1112 return port_buffer_size_etc(id, 0, 0); 1113 } 1114 1115 1116 ssize_t 1117 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout) 1118 { 1119 port_message_info info; 1120 status_t error = get_port_message_info_etc(id, &info, flags, timeout); 1121 return error != B_OK ? error : info.size; 1122 } 1123 1124 1125 status_t 1126 _get_port_message_info_etc(port_id id, port_message_info* info, 1127 size_t infoSize, uint32 flags, bigtime_t timeout) 1128 { 1129 if (info == NULL || infoSize != sizeof(port_message_info)) 1130 return B_BAD_VALUE; 1131 if (!sPortsActive || id < 0) 1132 return B_BAD_PORT_ID; 1133 1134 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1135 | B_ABSOLUTE_TIMEOUT; 1136 1137 // get the port 1138 Port* port = get_locked_port(id); 1139 if (port == NULL) 1140 return B_BAD_PORT_ID; 1141 MutexLocker locker(port->lock, true); 1142 1143 if (is_port_closed(port) && port->messages.IsEmpty()) { 1144 T(Info(port, 0, B_BAD_PORT_ID)); 1145 TRACE(("_get_port_message_info_etc(): closed port %ld\n", id)); 1146 return B_BAD_PORT_ID; 1147 } 1148 1149 while (port->read_count == 0) { 1150 // We need to wait for a message to appear 1151 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1152 return B_WOULD_BLOCK; 1153 1154 ConditionVariableEntry entry; 1155 port->read_condition.Add(&entry); 1156 1157 locker.Unlock(); 1158 1159 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1160 status_t status = entry.Wait(flags, timeout); 1161 1162 if (status != B_OK) { 1163 T(Info(port, 0, status)); 1164 return status; 1165 } 1166 1167 // re-lock 1168 Port* newPort = get_locked_port(id); 1169 if (newPort == NULL) { 1170 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1171 return B_BAD_PORT_ID; 1172 } 1173 locker.SetTo(newPort->lock, true); 1174 1175 if (newPort != port 1176 || (is_port_closed(port) && port->messages.IsEmpty())) { 1177 // the port is no longer there 1178 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1179 return B_BAD_PORT_ID; 1180 } 1181 } 1182 1183 // determine tail & get the length of the message 1184 port_message* message = port->messages.Head(); 1185 if (message == NULL) { 1186 panic("port %" B_PRId32 ": no messages found\n", port->id); 1187 return B_ERROR; 1188 } 1189 1190 info->size = message->size; 1191 info->sender = message->sender; 1192 info->sender_group = message->sender_group; 1193 info->sender_team = message->sender_team; 1194 1195 T(Info(id, id->read_count, id->write_count, message->code, B_OK)); 1196 1197 // notify next one, as we haven't read from the port 1198 port->read_condition.NotifyOne(); 1199 1200 return B_OK; 1201 } 1202 1203 1204 ssize_t 1205 port_count(port_id id) 1206 { 1207 if (!sPortsActive || id < 0) 1208 return B_BAD_PORT_ID; 1209 1210 // get the port 1211 Port* port = get_locked_port(id); 1212 if (port == NULL) { 1213 TRACE(("port_count: invalid port_id %ld\n", id)); 1214 return B_BAD_PORT_ID; 1215 } 1216 MutexLocker locker(port->lock, true); 1217 1218 // return count of messages 1219 return port->read_count; 1220 } 1221 1222 1223 ssize_t 1224 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize) 1225 { 1226 return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0); 1227 } 1228 1229 1230 ssize_t 1231 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize, 1232 uint32 flags, bigtime_t timeout) 1233 { 1234 if (!sPortsActive || id < 0) 1235 return B_BAD_PORT_ID; 1236 if ((buffer == NULL && bufferSize > 0) || timeout < 0) 1237 return B_BAD_VALUE; 1238 1239 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0; 1240 bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0; 1241 // TODO: we could allow peeking for user apps now 1242 1243 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1244 | B_ABSOLUTE_TIMEOUT; 1245 1246 // get the port 1247 Port* port = get_locked_port(id); 1248 if (port == NULL) 1249 return B_BAD_PORT_ID; 1250 MutexLocker locker(port->lock, true); 1251 1252 if (is_port_closed(port) && port->messages.IsEmpty()) { 1253 T(Read(port, 0, B_BAD_PORT_ID)); 1254 TRACE(("read_port_etc(): closed port %ld\n", id)); 1255 return B_BAD_PORT_ID; 1256 } 1257 1258 while (port->read_count == 0) { 1259 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1260 return B_WOULD_BLOCK; 1261 1262 // We need to wait for a message to appear 1263 ConditionVariableEntry entry; 1264 port->read_condition.Add(&entry); 1265 1266 locker.Unlock(); 1267 1268 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1269 status_t status = entry.Wait(flags, timeout); 1270 1271 // re-lock 1272 Port* newPort = get_locked_port(id); 1273 if (newPort == NULL) { 1274 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1275 return B_BAD_PORT_ID; 1276 } 1277 locker.SetTo(newPort->lock, true); 1278 1279 if (newPort != port 1280 || (is_port_closed(port) && port->messages.IsEmpty())) { 1281 // the port is no longer there 1282 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1283 return B_BAD_PORT_ID; 1284 } 1285 1286 if (status != B_OK) { 1287 T(Read(port, 0, status)); 1288 return status; 1289 } 1290 } 1291 1292 // determine tail & get the length of the message 1293 port_message* message = port->messages.Head(); 1294 if (message == NULL) { 1295 panic("port %" B_PRId32 ": no messages found\n", port->id); 1296 return B_ERROR; 1297 } 1298 1299 if (peekOnly) { 1300 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1301 userCopy); 1302 1303 T(Read(port, message->code, size)); 1304 1305 port->read_condition.NotifyOne(); 1306 // we only peeked, but didn't grab the message 1307 return size; 1308 } 1309 1310 port->messages.RemoveHead(); 1311 port->total_count++; 1312 port->write_count++; 1313 port->read_count--; 1314 1315 notify_port_select_events(port, B_EVENT_WRITE); 1316 port->write_condition.NotifyOne(); 1317 // make one spot in queue available again for write 1318 1319 T(Read(id, port->read_count, port->write_count, message->code, 1320 min_c(bufferSize, message->size))); 1321 1322 locker.Unlock(); 1323 1324 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1325 userCopy); 1326 1327 put_port_message(message); 1328 return size; 1329 } 1330 1331 1332 status_t 1333 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize) 1334 { 1335 iovec vec = { (void*)buffer, bufferSize }; 1336 1337 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0); 1338 } 1339 1340 1341 status_t 1342 write_port_etc(port_id id, int32 msgCode, const void* buffer, 1343 size_t bufferSize, uint32 flags, bigtime_t timeout) 1344 { 1345 iovec vec = { (void*)buffer, bufferSize }; 1346 1347 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout); 1348 } 1349 1350 1351 status_t 1352 writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs, 1353 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1354 { 1355 if (!sPortsActive || id < 0) 1356 return B_BAD_PORT_ID; 1357 if (bufferSize > PORT_MAX_MESSAGE_SIZE) 1358 return B_BAD_VALUE; 1359 1360 // mask irrelevant flags (for acquire_sem() usage) 1361 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1362 | B_ABSOLUTE_TIMEOUT; 1363 if ((flags & B_RELATIVE_TIMEOUT) != 0 1364 && timeout != B_INFINITE_TIMEOUT && timeout > 0) { 1365 // Make the timeout absolute, since we have more than one step where 1366 // we might have to wait 1367 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT; 1368 timeout += system_time(); 1369 } 1370 1371 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0; 1372 1373 status_t status; 1374 port_message* message = NULL; 1375 1376 // get the port 1377 Port* port = get_locked_port(id); 1378 if (port == NULL) { 1379 TRACE(("write_port_etc: invalid port_id %ld\n", id)); 1380 return B_BAD_PORT_ID; 1381 } 1382 MutexLocker locker(port->lock, true); 1383 1384 if (is_port_closed(port)) { 1385 TRACE(("write_port_etc: port %ld closed\n", id)); 1386 return B_BAD_PORT_ID; 1387 } 1388 1389 if (port->write_count <= 0) { 1390 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1391 return B_WOULD_BLOCK; 1392 1393 port->write_count--; 1394 1395 // We need to block in order to wait for a free message slot 1396 ConditionVariableEntry entry; 1397 port->write_condition.Add(&entry); 1398 1399 locker.Unlock(); 1400 1401 status = entry.Wait(flags, timeout); 1402 1403 // re-lock 1404 Port* newPort = get_locked_port(id); 1405 if (newPort == NULL) { 1406 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1407 return B_BAD_PORT_ID; 1408 } 1409 locker.SetTo(newPort->lock, true); 1410 1411 if (newPort != port || is_port_closed(port)) { 1412 // the port is no longer there 1413 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1414 return B_BAD_PORT_ID; 1415 } 1416 1417 if (status != B_OK) 1418 goto error; 1419 } else 1420 port->write_count--; 1421 1422 status = get_port_message(msgCode, bufferSize, flags, timeout, 1423 &message, *port); 1424 if (status != B_OK) { 1425 if (status == B_BAD_PORT_ID) { 1426 // the port had to be unlocked and is now no longer there 1427 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1428 return B_BAD_PORT_ID; 1429 } 1430 1431 goto error; 1432 } 1433 1434 // sender credentials 1435 message->sender = geteuid(); 1436 message->sender_group = getegid(); 1437 message->sender_team = team_get_current_team_id(); 1438 1439 if (bufferSize > 0) { 1440 size_t offset = 0; 1441 for (uint32 i = 0; i < vecCount; i++) { 1442 size_t bytes = msgVecs[i].iov_len; 1443 if (bytes > bufferSize) 1444 bytes = bufferSize; 1445 1446 if (userCopy) { 1447 status_t status = user_memcpy(message->buffer + offset, 1448 msgVecs[i].iov_base, bytes); 1449 if (status != B_OK) { 1450 put_port_message(message); 1451 goto error; 1452 } 1453 } else 1454 memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes); 1455 1456 bufferSize -= bytes; 1457 if (bufferSize == 0) 1458 break; 1459 1460 offset += bytes; 1461 } 1462 } 1463 1464 port->messages.Add(message); 1465 port->read_count++; 1466 1467 T(Write(id, port->read_count, port->write_count, message->code, 1468 message->size, B_OK)); 1469 1470 notify_port_select_events(port, B_EVENT_READ); 1471 port->read_condition.NotifyOne(); 1472 return B_OK; 1473 1474 error: 1475 // Give up our slot in the queue again, and let someone else 1476 // try and fail 1477 T(Write(id, port->read_count, port->write_count, 0, 0, status)); 1478 port->write_count++; 1479 notify_port_select_events(port, B_EVENT_WRITE); 1480 port->write_condition.NotifyOne(); 1481 1482 return status; 1483 } 1484 1485 1486 status_t 1487 set_port_owner(port_id id, team_id newTeamID) 1488 { 1489 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID)); 1490 1491 if (id < 0) 1492 return B_BAD_PORT_ID; 1493 1494 // get the new team 1495 Team* team = Team::Get(newTeamID); 1496 if (team == NULL) 1497 return B_BAD_TEAM_ID; 1498 BReference<Team> teamReference(team, true); 1499 1500 // get the port 1501 MutexLocker portsLocker(sPortsLock); 1502 Port* port = sPorts.Lookup(id); 1503 if (port == NULL) { 1504 TRACE(("set_port_owner: invalid port_id %ld\n", id)); 1505 return B_BAD_PORT_ID; 1506 } 1507 MutexLocker locker(port->lock); 1508 1509 // transfer ownership to other team 1510 if (team->id != port->owner) { 1511 list_remove_link(&port->team_link); 1512 list_add_item(&team->port_list, &port->team_link); 1513 port->owner = team->id; 1514 } 1515 1516 T(OwnerChange(port, team->id, B_OK)); 1517 return B_OK; 1518 } 1519 1520 1521 // #pragma mark - syscalls 1522 1523 1524 port_id 1525 _user_create_port(int32 queueLength, const char *userName) 1526 { 1527 char name[B_OS_NAME_LENGTH]; 1528 1529 if (userName == NULL) 1530 return create_port(queueLength, NULL); 1531 1532 if (!IS_USER_ADDRESS(userName) 1533 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1534 return B_BAD_ADDRESS; 1535 1536 return create_port(queueLength, name); 1537 } 1538 1539 1540 status_t 1541 _user_close_port(port_id id) 1542 { 1543 return close_port(id); 1544 } 1545 1546 1547 status_t 1548 _user_delete_port(port_id id) 1549 { 1550 return delete_port(id); 1551 } 1552 1553 1554 port_id 1555 _user_find_port(const char *userName) 1556 { 1557 char name[B_OS_NAME_LENGTH]; 1558 1559 if (userName == NULL) 1560 return B_BAD_VALUE; 1561 if (!IS_USER_ADDRESS(userName) 1562 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1563 return B_BAD_ADDRESS; 1564 1565 return find_port(name); 1566 } 1567 1568 1569 status_t 1570 _user_get_port_info(port_id id, struct port_info *userInfo) 1571 { 1572 struct port_info info; 1573 status_t status; 1574 1575 if (userInfo == NULL) 1576 return B_BAD_VALUE; 1577 if (!IS_USER_ADDRESS(userInfo)) 1578 return B_BAD_ADDRESS; 1579 1580 status = get_port_info(id, &info); 1581 1582 // copy back to user space 1583 if (status == B_OK 1584 && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK) 1585 return B_BAD_ADDRESS; 1586 1587 return status; 1588 } 1589 1590 1591 status_t 1592 _user_get_next_port_info(team_id team, int32 *userCookie, 1593 struct port_info *userInfo) 1594 { 1595 struct port_info info; 1596 status_t status; 1597 int32 cookie; 1598 1599 if (userCookie == NULL || userInfo == NULL) 1600 return B_BAD_VALUE; 1601 if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo) 1602 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 1603 return B_BAD_ADDRESS; 1604 1605 status = get_next_port_info(team, &cookie, &info); 1606 1607 // copy back to user space 1608 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK 1609 || (status == B_OK && user_memcpy(userInfo, &info, 1610 sizeof(struct port_info)) < B_OK)) 1611 return B_BAD_ADDRESS; 1612 1613 return status; 1614 } 1615 1616 1617 ssize_t 1618 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout) 1619 { 1620 syscall_restart_handle_timeout_pre(flags, timeout); 1621 1622 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT, 1623 timeout); 1624 1625 return syscall_restart_handle_timeout_post(status, timeout); 1626 } 1627 1628 1629 ssize_t 1630 _user_port_count(port_id port) 1631 { 1632 return port_count(port); 1633 } 1634 1635 1636 status_t 1637 _user_set_port_owner(port_id port, team_id team) 1638 { 1639 return set_port_owner(port, team); 1640 } 1641 1642 1643 ssize_t 1644 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer, 1645 size_t bufferSize, uint32 flags, bigtime_t timeout) 1646 { 1647 int32 messageCode; 1648 ssize_t bytesRead; 1649 1650 syscall_restart_handle_timeout_pre(flags, timeout); 1651 1652 if (userBuffer == NULL && bufferSize != 0) 1653 return B_BAD_VALUE; 1654 if ((userCode != NULL && !IS_USER_ADDRESS(userCode)) 1655 || (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))) 1656 return B_BAD_ADDRESS; 1657 1658 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize, 1659 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1660 1661 if (bytesRead >= 0 && userCode != NULL 1662 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK) 1663 return B_BAD_ADDRESS; 1664 1665 return syscall_restart_handle_timeout_post(bytesRead, timeout); 1666 } 1667 1668 1669 status_t 1670 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer, 1671 size_t bufferSize, uint32 flags, bigtime_t timeout) 1672 { 1673 iovec vec = { (void *)userBuffer, bufferSize }; 1674 1675 syscall_restart_handle_timeout_pre(flags, timeout); 1676 1677 if (userBuffer == NULL && bufferSize != 0) 1678 return B_BAD_VALUE; 1679 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)) 1680 return B_BAD_ADDRESS; 1681 1682 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize, 1683 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1684 1685 return syscall_restart_handle_timeout_post(status, timeout); 1686 } 1687 1688 1689 status_t 1690 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs, 1691 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1692 { 1693 syscall_restart_handle_timeout_pre(flags, timeout); 1694 1695 if (userVecs == NULL && bufferSize != 0) 1696 return B_BAD_VALUE; 1697 if (userVecs != NULL && !IS_USER_ADDRESS(userVecs)) 1698 return B_BAD_ADDRESS; 1699 1700 iovec *vecs = NULL; 1701 if (userVecs && vecCount != 0) { 1702 vecs = (iovec*)malloc(sizeof(iovec) * vecCount); 1703 if (vecs == NULL) 1704 return B_NO_MEMORY; 1705 1706 if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) { 1707 free(vecs); 1708 return B_BAD_ADDRESS; 1709 } 1710 } 1711 1712 status_t status = writev_port_etc(port, messageCode, vecs, vecCount, 1713 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, 1714 timeout); 1715 1716 free(vecs); 1717 return syscall_restart_handle_timeout_post(status, timeout); 1718 } 1719 1720 1721 status_t 1722 _user_get_port_message_info_etc(port_id port, port_message_info *userInfo, 1723 size_t infoSize, uint32 flags, bigtime_t timeout) 1724 { 1725 if (userInfo == NULL || infoSize != sizeof(port_message_info)) 1726 return B_BAD_VALUE; 1727 1728 syscall_restart_handle_timeout_pre(flags, timeout); 1729 1730 port_message_info info; 1731 status_t error = _get_port_message_info_etc(port, &info, sizeof(info), 1732 flags | B_CAN_INTERRUPT, timeout); 1733 1734 // copy info to userland 1735 if (error == B_OK && (!IS_USER_ADDRESS(userInfo) 1736 || user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) { 1737 error = B_BAD_ADDRESS; 1738 } 1739 1740 return syscall_restart_handle_timeout_post(error, timeout); 1741 } 1742