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[64]; 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 %8ld %4ld %9ld %9ld %8ld %6ld %s\n", port, 407 port->id, port->capacity, port->read_count, port->write_count, 408 port->total_count, port->owner, port->lock.name); 409 } 410 411 return 0; 412 } 413 414 415 static void 416 _dump_port_info(Port* port) 417 { 418 kprintf("PORT: %p\n", port); 419 kprintf(" id: %ld\n", port->id); 420 kprintf(" name: \"%s\"\n", port->lock.name); 421 kprintf(" owner: %ld\n", port->owner); 422 kprintf(" capacity: %ld\n", port->capacity); 423 kprintf(" read_count: %ld\n", port->read_count); 424 kprintf(" write_count: %ld\n", port->write_count); 425 kprintf(" total count: %ld\n", port->total_count); 426 427 if (!port->messages.IsEmpty()) { 428 kprintf("messages:\n"); 429 430 MessageList::Iterator iterator = port->messages.GetIterator(); 431 while (port_message* message = iterator.Next()) { 432 kprintf(" %p %08lx %ld\n", message, message->code, message->size); 433 } 434 } 435 436 set_debug_variable("_port", (addr_t)port); 437 set_debug_variable("_portID", port->id); 438 set_debug_variable("_owner", port->owner); 439 } 440 441 442 static int 443 dump_port_info(int argc, char** argv) 444 { 445 ConditionVariable* condition = NULL; 446 const char* name = NULL; 447 448 if (argc < 2) { 449 print_debugger_command_usage(argv[0]); 450 return 0; 451 } 452 453 if (argc > 2) { 454 if (!strcmp(argv[1], "address")) { 455 _dump_port_info((Port*)parse_expression(argv[2])); 456 return 0; 457 } else if (!strcmp(argv[1], "condition")) 458 condition = (ConditionVariable*)parse_expression(argv[2]); 459 else if (!strcmp(argv[1], "name")) 460 name = argv[2]; 461 } else if (parse_expression(argv[1]) > 0) { 462 // if the argument looks like a number, treat it as such 463 int32 num = parse_expression(argv[1]); 464 Port* port = sPorts.Lookup(num); 465 if (port == NULL) { 466 kprintf("port %ld (%#lx) doesn't exist!\n", num, num); 467 return 0; 468 } 469 _dump_port_info(port); 470 return 0; 471 } else 472 name = argv[1]; 473 474 // walk through the ports list, trying to match name 475 for (PortHashTable::Iterator it = sPorts.GetIterator(); 476 Port* port = it.Next();) { 477 if ((name != NULL && port->lock.name != NULL 478 && !strcmp(name, port->lock.name)) 479 || (condition != NULL && (&port->read_condition == condition 480 || &port->write_condition == condition))) { 481 _dump_port_info(port); 482 return 0; 483 } 484 } 485 486 return 0; 487 } 488 489 490 /*! Notifies the port's select events. 491 The port must be locked. 492 */ 493 static void 494 notify_port_select_events(Port* port, uint16 events) 495 { 496 if (port->select_infos) 497 notify_select_events_list(port->select_infos, events); 498 } 499 500 501 static Port* 502 get_locked_port(port_id id) 503 { 504 MutexLocker portsLocker(sPortsLock); 505 506 Port* port = sPorts.Lookup(id); 507 if (port != NULL) 508 mutex_lock(&port->lock); 509 return port; 510 } 511 512 513 /*! You need to own the port's lock when calling this function */ 514 static inline bool 515 is_port_closed(Port* port) 516 { 517 return port->capacity == 0; 518 } 519 520 521 static void 522 put_port_message(port_message* message) 523 { 524 size_t size = sizeof(port_message) + message->size; 525 heap_free(sPortAllocator, message); 526 527 MutexLocker quotaLocker(sPortQuotaLock); 528 sTotalSpaceInUse -= size; 529 if (sWaitingForSpace > 0) 530 sNoSpaceCondition.NotifyAll(); 531 } 532 533 534 static status_t 535 get_port_message(int32 code, size_t bufferSize, uint32 flags, bigtime_t timeout, 536 port_message** _message, Port& port) 537 { 538 size_t size = sizeof(port_message) + bufferSize; 539 bool needToWait = false; 540 541 MutexLocker quotaLocker(sPortQuotaLock); 542 543 while (true) { 544 while (sTotalSpaceInUse + size > kTotalSpaceLimit || needToWait) { 545 // TODO: add per team limit 546 // We are not allowed to create another heap area, as our 547 // space limit has been reached - just wait until we get 548 // some free space again. 549 550 // TODO: we don't want to wait - but does that also mean we 551 // shouldn't wait for the area creation? 552 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 553 return B_WOULD_BLOCK; 554 555 ConditionVariableEntry entry; 556 sNoSpaceCondition.Add(&entry); 557 558 sWaitingForSpace++; 559 quotaLocker.Unlock(); 560 561 port_id portID = port.id; 562 mutex_unlock(&port.lock); 563 564 status_t status = entry.Wait(flags, timeout); 565 566 // re-lock the port and the quota 567 Port* newPort = get_locked_port(portID); 568 quotaLocker.Lock(); 569 sWaitingForSpace--; 570 571 if (newPort != &port || is_port_closed(&port)) { 572 // the port is no longer usable 573 return B_BAD_PORT_ID; 574 } 575 576 if (status == B_TIMED_OUT) 577 return B_TIMED_OUT; 578 579 needToWait = false; 580 continue; 581 } 582 583 int32 areaChangeCounter = sAreaChangeCounter; 584 sTotalSpaceInUse += size; 585 quotaLocker.Unlock(); 586 587 // Quota is fulfilled, try to allocate the buffer 588 589 port_message* message 590 = (port_message*)heap_memalign(sPortAllocator, 0, size); 591 if (message != NULL) { 592 message->code = code; 593 message->size = bufferSize; 594 595 *_message = message; 596 return B_OK; 597 } 598 599 quotaLocker.Lock(); 600 601 // We weren't able to allocate and we'll start over, including 602 // re-acquireing the quota, so we remove our size from the in-use 603 // counter again. 604 sTotalSpaceInUse -= size; 605 606 if (areaChangeCounter != sAreaChangeCounter) { 607 // There was already an area added since we tried allocating, 608 // start over. 609 continue; 610 } 611 612 // Create a new area for the heap to use 613 614 addr_t base; 615 area_id area = create_area("port grown buffer", (void**)&base, 616 B_ANY_KERNEL_ADDRESS, kBufferGrowRate, B_NO_LOCK, 617 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 618 if (area < 0) { 619 // We'll have to get by with what we have, so wait for someone 620 // to free a message instead. We enforce waiting so that we don't 621 // try to create a new area over and over. 622 needToWait = true; 623 continue; 624 } 625 626 heap_add_area(sPortAllocator, area, base, kBufferGrowRate); 627 628 sAreaChangeCounter++; 629 if (sWaitingForSpace > 0) 630 sNoSpaceCondition.NotifyAll(); 631 } 632 } 633 634 635 /*! Fills the port_info structure with information from the specified 636 port. 637 The port's lock must be held when called. 638 */ 639 static void 640 fill_port_info(Port* port, port_info* info, size_t size) 641 { 642 info->port = port->id; 643 info->team = port->owner; 644 info->capacity = port->capacity; 645 646 info->queue_count = port->read_count; 647 info->total_count = port->total_count; 648 649 strlcpy(info->name, port->lock.name, B_OS_NAME_LENGTH); 650 } 651 652 653 static ssize_t 654 copy_port_message(port_message* message, int32* _code, void* buffer, 655 size_t bufferSize, bool userCopy) 656 { 657 // check output buffer size 658 size_t size = min_c(bufferSize, message->size); 659 660 // copy message 661 if (_code != NULL) 662 *_code = message->code; 663 664 if (size > 0) { 665 if (userCopy) { 666 status_t status = user_memcpy(buffer, message->buffer, size); 667 if (status != B_OK) 668 return status; 669 } else 670 memcpy(buffer, message->buffer, size); 671 } 672 673 return size; 674 } 675 676 677 static void 678 uninit_port_locked(Port* port) 679 { 680 notify_port_select_events(port, B_EVENT_INVALID); 681 port->select_infos = NULL; 682 683 // Release the threads that were blocking on this port. 684 // read_port() will see the B_BAD_PORT_ID return value, and act accordingly 685 port->read_condition.NotifyAll(false, B_BAD_PORT_ID); 686 port->write_condition.NotifyAll(false, B_BAD_PORT_ID); 687 sNotificationService.Notify(PORT_REMOVED, port->id); 688 } 689 690 691 // #pragma mark - private kernel API 692 693 694 /*! This function deletes all the ports that are owned by the passed team. 695 */ 696 void 697 delete_owned_ports(Team* team) 698 { 699 TRACE(("delete_owned_ports(owner = %ld)\n", team->id)); 700 701 MutexLocker portsLocker(sPortsLock); 702 703 // move the ports from the team's port list to a local list 704 struct list queue; 705 list_move_to_list(&team->port_list, &queue); 706 707 // iterate through the list or ports, remove them from the hash table and 708 // uninitialize them 709 Port* port = (Port*)list_get_first_item(&queue); 710 while (port != NULL) { 711 MutexLocker locker(port->lock); 712 sPorts.Remove(port); 713 uninit_port_locked(port); 714 sUsedPorts--; 715 716 port = (Port*)list_get_next_item(&queue, port); 717 } 718 719 portsLocker.Unlock(); 720 721 // delete the ports 722 while (Port* port = (Port*)list_remove_head_item(&queue)) 723 delete port; 724 } 725 726 727 int32 728 port_max_ports(void) 729 { 730 return sMaxPorts; 731 } 732 733 734 int32 735 port_used_ports(void) 736 { 737 return sUsedPorts; 738 } 739 740 741 status_t 742 port_init(kernel_args *args) 743 { 744 // initialize ports table 745 new(&sPorts) PortHashTable; 746 if (sPorts.Init() != B_OK) { 747 panic("Failed to init port hash table!"); 748 return B_NO_MEMORY; 749 } 750 751 addr_t base; 752 if (create_area("port heap", (void**)&base, B_ANY_KERNEL_ADDRESS, 753 kInitialPortBufferSize, B_NO_LOCK, 754 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA) < 0) { 755 // TODO: Since port_init() is invoked before the boot partition is 756 // mounted, the underlying VMAnonymousCache cannot commit swap space 757 // upon creation and thus the pages aren't swappable after all. This 758 // makes the area essentially B_LAZY_LOCK with additional overhead. 759 panic("unable to allocate port area!\n"); 760 return B_ERROR; 761 } 762 763 static const heap_class kBufferHeapClass = { "port heap", 100, 764 PORT_MAX_MESSAGE_SIZE + sizeof(port_message), 2 * 1024, 765 sizeof(port_message), 4, 2, 24 }; 766 sPortAllocator = heap_create_allocator("port buffer", base, 767 kInitialPortBufferSize, &kBufferHeapClass, true); 768 if (sPortAllocator == NULL) { 769 panic("unable to create port heap"); 770 return B_NO_MEMORY; 771 } 772 773 sNoSpaceCondition.Init(&sPorts, "port space"); 774 775 // add debugger commands 776 add_debugger_command_etc("ports", &dump_port_list, 777 "Dump a list of all active ports (for team, with name, etc.)", 778 "[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n" 779 "Prints a list of all active ports meeting the given\n" 780 "requirement. If no argument is given, all ports are listed.\n" 781 " <team> - The team owning the ports.\n" 782 " <name> - Part of the name of the ports.\n", 0); 783 add_debugger_command_etc("port", &dump_port_info, 784 "Dump info about a particular port", 785 "(<id> | [ \"address\" ] <address>) | ([ \"name\" ] <name>) " 786 "| (\"condition\" <address>)\n" 787 "Prints info about the specified port.\n" 788 " <address> - Pointer to the port structure.\n" 789 " <name> - Name of the port.\n" 790 " <condition> - address of the port's read or write condition.\n", 0); 791 792 new(&sNotificationService) PortNotificationService(); 793 sPortsActive = true; 794 return B_OK; 795 } 796 797 798 // #pragma mark - public kernel API 799 800 801 port_id 802 create_port(int32 queueLength, const char* name) 803 { 804 TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength, 805 name)); 806 807 if (!sPortsActive) { 808 panic("ports used too early!\n"); 809 return B_BAD_PORT_ID; 810 } 811 if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH) 812 return B_BAD_VALUE; 813 814 Team* team = thread_get_current_thread()->team; 815 if (team == NULL) 816 return B_BAD_TEAM_ID; 817 818 // check & dup name 819 char* nameBuffer = strdup(name != NULL ? name : "unnamed port"); 820 if (nameBuffer == NULL) 821 return B_NO_MEMORY; 822 823 // create a port 824 Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength, 825 nameBuffer); 826 if (port == NULL) { 827 free(nameBuffer); 828 return B_NO_MEMORY; 829 } 830 ObjectDeleter<Port> portDeleter(port); 831 832 MutexLocker locker(sPortsLock); 833 834 // check the ports limit 835 if (sUsedPorts >= sMaxPorts) 836 return B_NO_MORE_PORTS; 837 838 sUsedPorts++; 839 840 // allocate a port ID 841 do { 842 port->id = sNextPortID++; 843 844 // handle integer overflow 845 if (sNextPortID < 0) 846 sNextPortID = 1; 847 } while (sPorts.Lookup(port->id) != NULL); 848 849 // insert port in table and team list 850 sPorts.Insert(port); 851 list_add_item(&team->port_list, &port->team_link); 852 portDeleter.Detach(); 853 854 // tracing, notifications, etc. 855 T(Create(port)); 856 857 port_id id = port->id; 858 859 locker.Unlock(); 860 861 TRACE(("create_port() done: port created %ld\n", id)); 862 863 sNotificationService.Notify(PORT_ADDED, id); 864 return id; 865 } 866 867 868 status_t 869 close_port(port_id id) 870 { 871 TRACE(("close_port(id = %ld)\n", id)); 872 873 if (!sPortsActive || id < 0) 874 return B_BAD_PORT_ID; 875 876 // get the port 877 Port* port = get_locked_port(id); 878 if (port == NULL) { 879 TRACE(("close_port: invalid port_id %ld\n", id)); 880 return B_BAD_PORT_ID; 881 } 882 MutexLocker lock(&port->lock, true); 883 884 // mark port to disable writing - deleting the semaphores will 885 // wake up waiting read/writes 886 port->capacity = 0; 887 888 notify_port_select_events(port, B_EVENT_INVALID); 889 port->select_infos = NULL; 890 891 port->read_condition.NotifyAll(false, B_BAD_PORT_ID); 892 port->write_condition.NotifyAll(false, B_BAD_PORT_ID); 893 894 return B_OK; 895 } 896 897 898 status_t 899 delete_port(port_id id) 900 { 901 TRACE(("delete_port(id = %ld)\n", id)); 902 903 if (!sPortsActive || id < 0) 904 return B_BAD_PORT_ID; 905 906 // get the port and remove it from the hash table and the team 907 Port* port; 908 MutexLocker locker; 909 { 910 MutexLocker portsLocker(sPortsLock); 911 912 port = sPorts.Lookup(id); 913 if (port == NULL) { 914 TRACE(("delete_port: invalid port_id %ld\n", id)); 915 return B_BAD_PORT_ID; 916 } 917 918 sPorts.Remove(port); 919 list_remove_link(&port->team_link); 920 921 sUsedPorts--; 922 923 locker.SetTo(port->lock, false); 924 925 uninit_port_locked(port); 926 } 927 928 T(Delete(port)); 929 930 locker.Unlock(); 931 932 delete port; 933 934 return B_OK; 935 } 936 937 938 status_t 939 select_port(int32 id, struct select_info* info, bool kernel) 940 { 941 if (id < 0) 942 return B_BAD_PORT_ID; 943 944 // get the port 945 Port* port = get_locked_port(id); 946 if (port == NULL) 947 return B_BAD_PORT_ID; 948 MutexLocker locker(port->lock, true); 949 950 // port must not yet be closed 951 if (is_port_closed(port)) 952 return B_BAD_PORT_ID; 953 954 if (!kernel && port->owner == team_get_kernel_team_id()) { 955 // kernel port, but call from userland 956 return B_NOT_ALLOWED; 957 } 958 959 info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID; 960 961 if (info->selected_events != 0) { 962 uint16 events = 0; 963 964 info->next = port->select_infos; 965 port->select_infos = info; 966 967 // check for events 968 if ((info->selected_events & B_EVENT_READ) != 0 969 && !port->messages.IsEmpty()) { 970 events |= B_EVENT_READ; 971 } 972 973 if (port->write_count > 0) 974 events |= B_EVENT_WRITE; 975 976 if (events != 0) 977 notify_select_events(info, events); 978 } 979 980 return B_OK; 981 } 982 983 984 status_t 985 deselect_port(int32 id, struct select_info* info, bool kernel) 986 { 987 if (id < 0) 988 return B_BAD_PORT_ID; 989 if (info->selected_events == 0) 990 return B_OK; 991 992 // get the port 993 Port* port = get_locked_port(id); 994 if (port == NULL) 995 return B_BAD_PORT_ID; 996 MutexLocker locker(port->lock, true); 997 998 // find and remove the infos 999 select_info** infoLocation = &port->select_infos; 1000 while (*infoLocation != NULL && *infoLocation != info) 1001 infoLocation = &(*infoLocation)->next; 1002 1003 if (*infoLocation == info) 1004 *infoLocation = info->next; 1005 1006 return B_OK; 1007 } 1008 1009 1010 port_id 1011 find_port(const char* name) 1012 { 1013 TRACE(("find_port(name = \"%s\")\n", name)); 1014 1015 if (!sPortsActive) { 1016 panic("ports used too early!\n"); 1017 return B_NAME_NOT_FOUND; 1018 } 1019 if (name == NULL) 1020 return B_BAD_VALUE; 1021 1022 MutexLocker portsLocker(sPortsLock); 1023 1024 for (PortHashTable::Iterator it = sPorts.GetIterator(); 1025 Port* port = it.Next();) { 1026 if (!strcmp(name, port->lock.name)) 1027 return port->id; 1028 } 1029 1030 return B_NAME_NOT_FOUND; 1031 } 1032 1033 1034 status_t 1035 _get_port_info(port_id id, port_info* info, size_t size) 1036 { 1037 TRACE(("get_port_info(id = %ld)\n", id)); 1038 1039 if (info == NULL || size != sizeof(port_info)) 1040 return B_BAD_VALUE; 1041 if (!sPortsActive || id < 0) 1042 return B_BAD_PORT_ID; 1043 1044 // get the port 1045 Port* port = get_locked_port(id); 1046 if (port == NULL) { 1047 TRACE(("get_port_info: invalid port_id %ld\n", id)); 1048 return B_BAD_PORT_ID; 1049 } 1050 MutexLocker locker(port->lock, true); 1051 1052 // fill a port_info struct with info 1053 fill_port_info(port, info, size); 1054 return B_OK; 1055 } 1056 1057 1058 status_t 1059 _get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info, 1060 size_t size) 1061 { 1062 TRACE(("get_next_port_info(team = %ld)\n", teamID)); 1063 1064 if (info == NULL || size != sizeof(port_info) || _cookie == NULL 1065 || teamID < 0) { 1066 return B_BAD_VALUE; 1067 } 1068 if (!sPortsActive) 1069 return B_BAD_PORT_ID; 1070 1071 Team* team = Team::Get(teamID); 1072 if (team == NULL) 1073 return B_BAD_TEAM_ID; 1074 BReference<Team> teamReference(team, true); 1075 1076 // iterate through the team's port list 1077 MutexLocker portsLocker(sPortsLock); 1078 1079 int32 stopIndex = *_cookie; 1080 int32 index = 0; 1081 1082 Port* port = (Port*)list_get_first_item(&team->port_list); 1083 while (port != NULL) { 1084 if (!is_port_closed(port)) { 1085 if (index == stopIndex) 1086 break; 1087 index++; 1088 } 1089 1090 port = (Port*)list_get_next_item(&team->port_list, port); 1091 } 1092 1093 if (port == NULL) 1094 return B_BAD_PORT_ID; 1095 1096 // fill in the port info 1097 MutexLocker locker(port->lock); 1098 portsLocker.Unlock(); 1099 fill_port_info(port, info, size); 1100 1101 *_cookie = stopIndex + 1; 1102 return B_OK; 1103 } 1104 1105 1106 ssize_t 1107 port_buffer_size(port_id id) 1108 { 1109 return port_buffer_size_etc(id, 0, 0); 1110 } 1111 1112 1113 ssize_t 1114 port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout) 1115 { 1116 port_message_info info; 1117 status_t error = get_port_message_info_etc(id, &info, flags, timeout); 1118 return error != B_OK ? error : info.size; 1119 } 1120 1121 1122 status_t 1123 _get_port_message_info_etc(port_id id, port_message_info* info, 1124 size_t infoSize, uint32 flags, bigtime_t timeout) 1125 { 1126 if (info == NULL || infoSize != sizeof(port_message_info)) 1127 return B_BAD_VALUE; 1128 if (!sPortsActive || id < 0) 1129 return B_BAD_PORT_ID; 1130 1131 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1132 | B_ABSOLUTE_TIMEOUT; 1133 1134 // get the port 1135 Port* port = get_locked_port(id); 1136 if (port == NULL) 1137 return B_BAD_PORT_ID; 1138 MutexLocker locker(port->lock, true); 1139 1140 if (is_port_closed(port) && port->messages.IsEmpty()) { 1141 T(Info(port, 0, B_BAD_PORT_ID)); 1142 TRACE(("_get_port_message_info_etc(): closed port %ld\n", id)); 1143 return B_BAD_PORT_ID; 1144 } 1145 1146 while (port->read_count == 0) { 1147 // We need to wait for a message to appear 1148 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1149 return B_WOULD_BLOCK; 1150 1151 ConditionVariableEntry entry; 1152 port->read_condition.Add(&entry); 1153 1154 locker.Unlock(); 1155 1156 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1157 status_t status = entry.Wait(flags, timeout); 1158 1159 if (status != B_OK) { 1160 T(Info(port, 0, status)); 1161 return status; 1162 } 1163 1164 // re-lock 1165 Port* newPort = get_locked_port(id); 1166 if (newPort == NULL) { 1167 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1168 return B_BAD_PORT_ID; 1169 } 1170 locker.SetTo(newPort->lock, true); 1171 1172 if (newPort != port 1173 || (is_port_closed(port) && port->messages.IsEmpty())) { 1174 // the port is no longer there 1175 T(Info(id, 0, 0, 0, B_BAD_PORT_ID)); 1176 return B_BAD_PORT_ID; 1177 } 1178 } 1179 1180 // determine tail & get the length of the message 1181 port_message* message = port->messages.Head(); 1182 if (message == NULL) { 1183 panic("port %ld: no messages found\n", port->id); 1184 return B_ERROR; 1185 } 1186 1187 info->size = message->size; 1188 info->sender = message->sender; 1189 info->sender_group = message->sender_group; 1190 info->sender_team = message->sender_team; 1191 1192 T(Info(id, id->read_count, id->write_count, message->code, B_OK)); 1193 1194 // notify next one, as we haven't read from the port 1195 port->read_condition.NotifyOne(); 1196 1197 return B_OK; 1198 } 1199 1200 1201 ssize_t 1202 port_count(port_id id) 1203 { 1204 if (!sPortsActive || id < 0) 1205 return B_BAD_PORT_ID; 1206 1207 // get the port 1208 Port* port = get_locked_port(id); 1209 if (port == NULL) { 1210 TRACE(("port_count: invalid port_id %ld\n", id)); 1211 return B_BAD_PORT_ID; 1212 } 1213 MutexLocker locker(port->lock, true); 1214 1215 // return count of messages 1216 return port->read_count; 1217 } 1218 1219 1220 ssize_t 1221 read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize) 1222 { 1223 return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0); 1224 } 1225 1226 1227 ssize_t 1228 read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize, 1229 uint32 flags, bigtime_t timeout) 1230 { 1231 if (!sPortsActive || id < 0) 1232 return B_BAD_PORT_ID; 1233 if ((buffer == NULL && bufferSize > 0) || timeout < 0) 1234 return B_BAD_VALUE; 1235 1236 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0; 1237 bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0; 1238 // TODO: we could allow peeking for user apps now 1239 1240 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1241 | B_ABSOLUTE_TIMEOUT; 1242 1243 // get the port 1244 Port* port = get_locked_port(id); 1245 if (port == NULL) 1246 return B_BAD_PORT_ID; 1247 MutexLocker locker(port->lock, true); 1248 1249 if (is_port_closed(port) && port->messages.IsEmpty()) { 1250 T(Read(port, 0, B_BAD_PORT_ID)); 1251 TRACE(("read_port_etc(): closed port %ld\n", id)); 1252 return B_BAD_PORT_ID; 1253 } 1254 1255 while (port->read_count == 0) { 1256 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1257 return B_WOULD_BLOCK; 1258 1259 // We need to wait for a message to appear 1260 ConditionVariableEntry entry; 1261 port->read_condition.Add(&entry); 1262 1263 locker.Unlock(); 1264 1265 // block if no message, or, if B_TIMEOUT flag set, block with timeout 1266 status_t status = entry.Wait(flags, timeout); 1267 1268 // re-lock 1269 Port* newPort = get_locked_port(id); 1270 if (newPort == NULL) { 1271 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1272 return B_BAD_PORT_ID; 1273 } 1274 locker.SetTo(newPort->lock, true); 1275 1276 if (newPort != port 1277 || (is_port_closed(port) && port->messages.IsEmpty())) { 1278 // the port is no longer there 1279 T(Read(id, 0, 0, 0, B_BAD_PORT_ID)); 1280 return B_BAD_PORT_ID; 1281 } 1282 1283 if (status != B_OK) { 1284 T(Read(port, 0, status)); 1285 return status; 1286 } 1287 } 1288 1289 // determine tail & get the length of the message 1290 port_message* message = port->messages.Head(); 1291 if (message == NULL) { 1292 panic("port %ld: no messages found\n", port->id); 1293 return B_ERROR; 1294 } 1295 1296 if (peekOnly) { 1297 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1298 userCopy); 1299 1300 T(Read(port, message->code, size)); 1301 1302 port->read_condition.NotifyOne(); 1303 // we only peeked, but didn't grab the message 1304 return size; 1305 } 1306 1307 port->messages.RemoveHead(); 1308 port->total_count++; 1309 port->write_count++; 1310 port->read_count--; 1311 1312 notify_port_select_events(port, B_EVENT_WRITE); 1313 port->write_condition.NotifyOne(); 1314 // make one spot in queue available again for write 1315 1316 T(Read(id, port->read_count, port->write_count, message->code, 1317 min_c(bufferSize, message->size))); 1318 1319 locker.Unlock(); 1320 1321 size_t size = copy_port_message(message, _code, buffer, bufferSize, 1322 userCopy); 1323 1324 put_port_message(message); 1325 return size; 1326 } 1327 1328 1329 status_t 1330 write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize) 1331 { 1332 iovec vec = { (void*)buffer, bufferSize }; 1333 1334 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0); 1335 } 1336 1337 1338 status_t 1339 write_port_etc(port_id id, int32 msgCode, const void* buffer, 1340 size_t bufferSize, uint32 flags, bigtime_t timeout) 1341 { 1342 iovec vec = { (void*)buffer, bufferSize }; 1343 1344 return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout); 1345 } 1346 1347 1348 status_t 1349 writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs, 1350 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1351 { 1352 if (!sPortsActive || id < 0) 1353 return B_BAD_PORT_ID; 1354 if (bufferSize > PORT_MAX_MESSAGE_SIZE) 1355 return B_BAD_VALUE; 1356 1357 // mask irrelevant flags (for acquire_sem() usage) 1358 flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT 1359 | B_ABSOLUTE_TIMEOUT; 1360 if ((flags & B_RELATIVE_TIMEOUT) != 0 1361 && timeout != B_INFINITE_TIMEOUT && timeout > 0) { 1362 // Make the timeout absolute, since we have more than one step where 1363 // we might have to wait 1364 flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT; 1365 timeout += system_time(); 1366 } 1367 1368 bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) > 0; 1369 1370 status_t status; 1371 port_message* message = NULL; 1372 1373 // get the port 1374 Port* port = get_locked_port(id); 1375 if (port == NULL) { 1376 TRACE(("write_port_etc: invalid port_id %ld\n", id)); 1377 return B_BAD_PORT_ID; 1378 } 1379 MutexLocker locker(port->lock, true); 1380 1381 if (is_port_closed(port)) { 1382 TRACE(("write_port_etc: port %ld closed\n", id)); 1383 return B_BAD_PORT_ID; 1384 } 1385 1386 if (port->write_count <= 0) { 1387 if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) 1388 return B_WOULD_BLOCK; 1389 1390 port->write_count--; 1391 1392 // We need to block in order to wait for a free message slot 1393 ConditionVariableEntry entry; 1394 port->write_condition.Add(&entry); 1395 1396 locker.Unlock(); 1397 1398 status = entry.Wait(flags, timeout); 1399 1400 // re-lock 1401 Port* newPort = get_locked_port(id); 1402 if (newPort == NULL) { 1403 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1404 return B_BAD_PORT_ID; 1405 } 1406 locker.SetTo(newPort->lock, true); 1407 1408 if (newPort != port || is_port_closed(port)) { 1409 // the port is no longer there 1410 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1411 return B_BAD_PORT_ID; 1412 } 1413 1414 if (status != B_OK) 1415 goto error; 1416 } else 1417 port->write_count--; 1418 1419 status = get_port_message(msgCode, bufferSize, flags, timeout, 1420 &message, *port); 1421 if (status != B_OK) { 1422 if (status == B_BAD_PORT_ID) { 1423 // the port had to be unlocked and is now no longer there 1424 T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID)); 1425 return B_BAD_PORT_ID; 1426 } 1427 1428 goto error; 1429 } 1430 1431 // sender credentials 1432 message->sender = geteuid(); 1433 message->sender_group = getegid(); 1434 message->sender_team = team_get_current_team_id(); 1435 1436 if (bufferSize > 0) { 1437 size_t offset = 0; 1438 for (uint32 i = 0; i < vecCount; i++) { 1439 size_t bytes = msgVecs[i].iov_len; 1440 if (bytes > bufferSize) 1441 bytes = bufferSize; 1442 1443 if (userCopy) { 1444 status_t status = user_memcpy(message->buffer + offset, 1445 msgVecs[i].iov_base, bytes); 1446 if (status != B_OK) { 1447 put_port_message(message); 1448 goto error; 1449 } 1450 } else 1451 memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes); 1452 1453 bufferSize -= bytes; 1454 if (bufferSize == 0) 1455 break; 1456 1457 offset += bytes; 1458 } 1459 } 1460 1461 port->messages.Add(message); 1462 port->read_count++; 1463 1464 T(Write(id, port->read_count, port->write_count, message->code, 1465 message->size, B_OK)); 1466 1467 notify_port_select_events(port, B_EVENT_READ); 1468 port->read_condition.NotifyOne(); 1469 return B_OK; 1470 1471 error: 1472 // Give up our slot in the queue again, and let someone else 1473 // try and fail 1474 T(Write(id, port->read_count, port->write_count, 0, 0, status)); 1475 port->write_count++; 1476 notify_port_select_events(port, B_EVENT_WRITE); 1477 port->write_condition.NotifyOne(); 1478 1479 return status; 1480 } 1481 1482 1483 status_t 1484 set_port_owner(port_id id, team_id newTeamID) 1485 { 1486 TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID)); 1487 1488 if (id < 0) 1489 return B_BAD_PORT_ID; 1490 1491 // get the new team 1492 Team* team = Team::Get(newTeamID); 1493 if (team == NULL) 1494 return B_BAD_TEAM_ID; 1495 BReference<Team> teamReference(team, true); 1496 1497 // get the port 1498 MutexLocker portsLocker(sPortsLock); 1499 Port* port = sPorts.Lookup(id); 1500 if (port == NULL) { 1501 TRACE(("set_port_owner: invalid port_id %ld\n", id)); 1502 return B_BAD_PORT_ID; 1503 } 1504 MutexLocker locker(port->lock); 1505 1506 // transfer ownership to other team 1507 if (team->id != port->owner) { 1508 list_remove_link(&port->team_link); 1509 list_add_item(&team->port_list, &port->team_link); 1510 port->owner = team->id; 1511 } 1512 1513 T(OwnerChange(port, team->id, B_OK)); 1514 return B_OK; 1515 } 1516 1517 1518 // #pragma mark - syscalls 1519 1520 1521 port_id 1522 _user_create_port(int32 queueLength, const char *userName) 1523 { 1524 char name[B_OS_NAME_LENGTH]; 1525 1526 if (userName == NULL) 1527 return create_port(queueLength, NULL); 1528 1529 if (!IS_USER_ADDRESS(userName) 1530 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1531 return B_BAD_ADDRESS; 1532 1533 return create_port(queueLength, name); 1534 } 1535 1536 1537 status_t 1538 _user_close_port(port_id id) 1539 { 1540 return close_port(id); 1541 } 1542 1543 1544 status_t 1545 _user_delete_port(port_id id) 1546 { 1547 return delete_port(id); 1548 } 1549 1550 1551 port_id 1552 _user_find_port(const char *userName) 1553 { 1554 char name[B_OS_NAME_LENGTH]; 1555 1556 if (userName == NULL) 1557 return B_BAD_VALUE; 1558 if (!IS_USER_ADDRESS(userName) 1559 || user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) 1560 return B_BAD_ADDRESS; 1561 1562 return find_port(name); 1563 } 1564 1565 1566 status_t 1567 _user_get_port_info(port_id id, struct port_info *userInfo) 1568 { 1569 struct port_info info; 1570 status_t status; 1571 1572 if (userInfo == NULL) 1573 return B_BAD_VALUE; 1574 if (!IS_USER_ADDRESS(userInfo)) 1575 return B_BAD_ADDRESS; 1576 1577 status = get_port_info(id, &info); 1578 1579 // copy back to user space 1580 if (status == B_OK 1581 && user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK) 1582 return B_BAD_ADDRESS; 1583 1584 return status; 1585 } 1586 1587 1588 status_t 1589 _user_get_next_port_info(team_id team, int32 *userCookie, 1590 struct port_info *userInfo) 1591 { 1592 struct port_info info; 1593 status_t status; 1594 int32 cookie; 1595 1596 if (userCookie == NULL || userInfo == NULL) 1597 return B_BAD_VALUE; 1598 if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo) 1599 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK) 1600 return B_BAD_ADDRESS; 1601 1602 status = get_next_port_info(team, &cookie, &info); 1603 1604 // copy back to user space 1605 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK 1606 || (status == B_OK && user_memcpy(userInfo, &info, 1607 sizeof(struct port_info)) < B_OK)) 1608 return B_BAD_ADDRESS; 1609 1610 return status; 1611 } 1612 1613 1614 ssize_t 1615 _user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout) 1616 { 1617 syscall_restart_handle_timeout_pre(flags, timeout); 1618 1619 status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT, 1620 timeout); 1621 1622 return syscall_restart_handle_timeout_post(status, timeout); 1623 } 1624 1625 1626 ssize_t 1627 _user_port_count(port_id port) 1628 { 1629 return port_count(port); 1630 } 1631 1632 1633 status_t 1634 _user_set_port_owner(port_id port, team_id team) 1635 { 1636 return set_port_owner(port, team); 1637 } 1638 1639 1640 ssize_t 1641 _user_read_port_etc(port_id port, int32 *userCode, void *userBuffer, 1642 size_t bufferSize, uint32 flags, bigtime_t timeout) 1643 { 1644 int32 messageCode; 1645 ssize_t bytesRead; 1646 1647 syscall_restart_handle_timeout_pre(flags, timeout); 1648 1649 if (userBuffer == NULL && bufferSize != 0) 1650 return B_BAD_VALUE; 1651 if ((userCode != NULL && !IS_USER_ADDRESS(userCode)) 1652 || (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))) 1653 return B_BAD_ADDRESS; 1654 1655 bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize, 1656 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1657 1658 if (bytesRead >= 0 && userCode != NULL 1659 && user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK) 1660 return B_BAD_ADDRESS; 1661 1662 return syscall_restart_handle_timeout_post(bytesRead, timeout); 1663 } 1664 1665 1666 status_t 1667 _user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer, 1668 size_t bufferSize, uint32 flags, bigtime_t timeout) 1669 { 1670 iovec vec = { (void *)userBuffer, bufferSize }; 1671 1672 syscall_restart_handle_timeout_pre(flags, timeout); 1673 1674 if (userBuffer == NULL && bufferSize != 0) 1675 return B_BAD_VALUE; 1676 if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)) 1677 return B_BAD_ADDRESS; 1678 1679 status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize, 1680 flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout); 1681 1682 return syscall_restart_handle_timeout_post(status, timeout); 1683 } 1684 1685 1686 status_t 1687 _user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs, 1688 size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout) 1689 { 1690 syscall_restart_handle_timeout_pre(flags, timeout); 1691 1692 if (userVecs == NULL && bufferSize != 0) 1693 return B_BAD_VALUE; 1694 if (userVecs != NULL && !IS_USER_ADDRESS(userVecs)) 1695 return B_BAD_ADDRESS; 1696 1697 iovec *vecs = NULL; 1698 if (userVecs && vecCount != 0) { 1699 vecs = (iovec*)malloc(sizeof(iovec) * vecCount); 1700 if (vecs == NULL) 1701 return B_NO_MEMORY; 1702 1703 if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) { 1704 free(vecs); 1705 return B_BAD_ADDRESS; 1706 } 1707 } 1708 1709 status_t status = writev_port_etc(port, messageCode, vecs, vecCount, 1710 bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, 1711 timeout); 1712 1713 free(vecs); 1714 return syscall_restart_handle_timeout_post(status, timeout); 1715 } 1716 1717 1718 status_t 1719 _user_get_port_message_info_etc(port_id port, port_message_info *userInfo, 1720 size_t infoSize, uint32 flags, bigtime_t timeout) 1721 { 1722 if (userInfo == NULL || infoSize != sizeof(port_message_info)) 1723 return B_BAD_VALUE; 1724 1725 syscall_restart_handle_timeout_pre(flags, timeout); 1726 1727 port_message_info info; 1728 status_t error = _get_port_message_info_etc(port, &info, sizeof(info), 1729 flags | B_CAN_INTERRUPT, timeout); 1730 1731 // copy info to userland 1732 if (error == B_OK && (!IS_USER_ADDRESS(userInfo) 1733 || user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) { 1734 error = B_BAD_ADDRESS; 1735 } 1736 1737 return syscall_restart_handle_timeout_post(error, timeout); 1738 } 1739