1 /* 2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <posix/realtime_sem.h> 7 8 #include <string.h> 9 10 #include <new> 11 12 #include <OS.h> 13 14 #include <AutoDeleter.h> 15 #include <fs/KPath.h> 16 #include <kernel.h> 17 #include <lock.h> 18 #include <syscall_restart.h> 19 #include <team.h> 20 #include <thread.h> 21 #include <util/atomic.h> 22 #include <util/AutoLock.h> 23 #include <util/OpenHashTable.h> 24 #include <util/StringHash.h> 25 26 27 namespace { 28 29 class SemInfo { 30 public: 31 SemInfo() 32 : 33 fSemaphoreID(-1) 34 { 35 } 36 37 virtual ~SemInfo() 38 { 39 if (fSemaphoreID >= 0) 40 delete_sem(fSemaphoreID); 41 } 42 43 sem_id SemaphoreID() const { return fSemaphoreID; } 44 45 status_t Init(int32 semCount, const char* name) 46 { 47 fSemaphoreID = create_sem(semCount, name); 48 if (fSemaphoreID < 0) 49 return fSemaphoreID; 50 51 return B_OK; 52 } 53 54 virtual sem_id ID() const = 0; 55 virtual SemInfo* Clone() = 0; 56 virtual void Delete() = 0; 57 58 private: 59 sem_id fSemaphoreID; 60 }; 61 62 63 class NamedSem : public SemInfo { 64 public: 65 NamedSem() 66 : 67 fName(NULL), 68 fRefCount(1) 69 { 70 } 71 72 virtual ~NamedSem() 73 { 74 free(fName); 75 } 76 77 const char* Name() const { return fName; } 78 79 status_t Init(const char* name, mode_t mode, int32 semCount) 80 { 81 status_t error = SemInfo::Init(semCount, name); 82 if (error != B_OK) 83 return error; 84 85 fName = strdup(name); 86 if (fName == NULL) 87 return B_NO_MEMORY; 88 89 fUID = geteuid(); 90 fGID = getegid(); 91 fPermissions = mode; 92 93 return B_OK; 94 } 95 96 void AcquireReference() 97 { 98 atomic_add(&fRefCount, 1); 99 } 100 101 void ReleaseReference() 102 { 103 if (atomic_add(&fRefCount, -1) == 1) 104 delete this; 105 } 106 107 bool HasPermissions() const 108 { 109 if ((fPermissions & S_IWOTH) != 0) 110 return true; 111 112 uid_t uid = geteuid(); 113 if (uid == 0 || (uid == fUID && (fPermissions & S_IWUSR) != 0)) 114 return true; 115 116 gid_t gid = getegid(); 117 if (gid == fGID && (fPermissions & S_IWGRP) != 0) 118 return true; 119 120 return false; 121 } 122 123 virtual sem_id ID() const 124 { 125 return SemaphoreID(); 126 } 127 128 virtual SemInfo* Clone() 129 { 130 AcquireReference(); 131 return this; 132 } 133 134 virtual void Delete() 135 { 136 ReleaseReference(); 137 } 138 139 NamedSem*& HashLink() 140 { 141 return fHashLink; 142 } 143 144 private: 145 char* fName; 146 int32 fRefCount; 147 uid_t fUID; 148 gid_t fGID; 149 mode_t fPermissions; 150 151 NamedSem* fHashLink; 152 }; 153 154 155 struct NamedSemHashDefinition { 156 typedef const char* KeyType; 157 typedef NamedSem ValueType; 158 159 size_t HashKey(const KeyType& key) const 160 { 161 return hash_hash_string(key); 162 } 163 164 size_t Hash(NamedSem* semaphore) const 165 { 166 return HashKey(semaphore->Name()); 167 } 168 169 bool Compare(const KeyType& key, NamedSem* semaphore) const 170 { 171 return strcmp(key, semaphore->Name()) == 0; 172 } 173 174 NamedSem*& GetLink(NamedSem* semaphore) const 175 { 176 return semaphore->HashLink(); 177 } 178 }; 179 180 181 class GlobalSemTable { 182 public: 183 GlobalSemTable() 184 : 185 fSemaphoreCount(0) 186 { 187 mutex_init(&fLock, "global named sem table"); 188 } 189 190 ~GlobalSemTable() 191 { 192 mutex_destroy(&fLock); 193 } 194 195 status_t Init() 196 { 197 return fNamedSemaphores.Init(); 198 } 199 200 status_t OpenNamedSem(const char* name, int openFlags, mode_t mode, 201 uint32 semCount, NamedSem*& _sem, bool& _created) 202 { 203 MutexLocker _(fLock); 204 205 NamedSem* sem = fNamedSemaphores.Lookup(name); 206 if (sem != NULL) { 207 if ((openFlags & O_EXCL) != 0) 208 return EEXIST; 209 210 if (!sem->HasPermissions()) 211 return EACCES; 212 213 sem->AcquireReference(); 214 _sem = sem; 215 _created = false; 216 return B_OK; 217 } 218 219 if ((openFlags & O_CREAT) == 0) 220 return ENOENT; 221 222 // does not exist yet -- create 223 if (fSemaphoreCount >= MAX_POSIX_SEMS) 224 return ENOSPC; 225 226 sem = new(std::nothrow) NamedSem; 227 if (sem == NULL) 228 return B_NO_MEMORY; 229 230 status_t error = sem->Init(name, mode, semCount); 231 if (error != B_OK) { 232 delete sem; 233 return error; 234 } 235 236 error = fNamedSemaphores.Insert(sem); 237 if (error != B_OK) { 238 delete sem; 239 return error; 240 } 241 242 // add one reference for the table 243 sem->AcquireReference(); 244 245 fSemaphoreCount++; 246 247 _sem = sem; 248 _created = true; 249 return B_OK; 250 } 251 252 status_t UnlinkNamedSem(const char* name) 253 { 254 MutexLocker _(fLock); 255 256 NamedSem* sem = fNamedSemaphores.Lookup(name); 257 if (sem == NULL) 258 return ENOENT; 259 260 if (!sem->HasPermissions()) 261 return EACCES; 262 263 fNamedSemaphores.Remove(sem); 264 sem->ReleaseReference(); 265 // release the table reference 266 fSemaphoreCount--; 267 268 return B_OK; 269 } 270 271 private: 272 typedef BOpenHashTable<NamedSemHashDefinition, true> NamedSemTable; 273 274 mutex fLock; 275 NamedSemTable fNamedSemaphores; 276 int32 fSemaphoreCount; 277 }; 278 279 280 static GlobalSemTable sSemTable; 281 282 283 class TeamSemInfo { 284 public: 285 TeamSemInfo(SemInfo* semaphore, sem_t* userSem) 286 : 287 fSemaphore(semaphore), 288 fUserSemaphore(userSem), 289 fOpenCount(1) 290 { 291 } 292 293 ~TeamSemInfo() 294 { 295 if (fSemaphore != NULL) 296 fSemaphore->Delete(); 297 } 298 299 sem_id ID() const { return fSemaphore->ID(); } 300 sem_id SemaphoreID() const { return fSemaphore->SemaphoreID(); } 301 sem_t* UserSemaphore() const { return fUserSemaphore; } 302 303 void Open() 304 { 305 fOpenCount++; 306 } 307 308 bool Close() 309 { 310 return --fOpenCount == 0; 311 } 312 313 TeamSemInfo* Clone() const 314 { 315 SemInfo* sem = fSemaphore->Clone(); 316 if (sem == NULL) 317 return NULL; 318 319 TeamSemInfo* clone = new(std::nothrow) TeamSemInfo(sem, fUserSemaphore); 320 if (clone == NULL) { 321 sem->Delete(); 322 return NULL; 323 } 324 325 clone->fOpenCount = fOpenCount; 326 327 return clone; 328 } 329 330 TeamSemInfo*& HashLink() 331 { 332 return fHashLink; 333 } 334 335 private: 336 SemInfo* fSemaphore; 337 sem_t* fUserSemaphore; 338 int32 fOpenCount; 339 340 TeamSemInfo* fHashLink; 341 }; 342 343 344 struct TeamSemHashDefinition { 345 typedef sem_id KeyType; 346 typedef TeamSemInfo ValueType; 347 348 size_t HashKey(const KeyType& key) const 349 { 350 return (size_t)key; 351 } 352 353 size_t Hash(TeamSemInfo* semaphore) const 354 { 355 return HashKey(semaphore->ID()); 356 } 357 358 bool Compare(const KeyType& key, TeamSemInfo* semaphore) const 359 { 360 return key == semaphore->ID(); 361 } 362 363 TeamSemInfo*& GetLink(TeamSemInfo* semaphore) const 364 { 365 return semaphore->HashLink(); 366 } 367 }; 368 369 } // namespace 370 371 372 struct realtime_sem_context { 373 realtime_sem_context() 374 : 375 fSemaphoreCount(0) 376 { 377 mutex_init(&fLock, "realtime sem context"); 378 } 379 380 ~realtime_sem_context() 381 { 382 mutex_lock(&fLock); 383 384 // delete all semaphores. 385 SemTable::Iterator it = fSemaphores.GetIterator(); 386 while (TeamSemInfo* sem = it.Next()) { 387 // Note, this uses internal knowledge about how the iterator works. 388 // Ugly, but there's no good alternative. 389 fSemaphores.RemoveUnchecked(sem); 390 delete sem; 391 } 392 393 mutex_destroy(&fLock); 394 } 395 396 status_t Init() 397 { 398 fNextPrivateSemID = -1; 399 return fSemaphores.Init(); 400 } 401 402 realtime_sem_context* Clone() 403 { 404 // create new context 405 realtime_sem_context* context = new(std::nothrow) realtime_sem_context; 406 if (context == NULL) 407 return NULL; 408 ObjectDeleter<realtime_sem_context> contextDeleter(context); 409 410 MutexLocker _(fLock); 411 412 context->fNextPrivateSemID = fNextPrivateSemID; 413 414 // clone all semaphores 415 SemTable::Iterator it = fSemaphores.GetIterator(); 416 while (TeamSemInfo* sem = it.Next()) { 417 TeamSemInfo* clonedSem = sem->Clone(); 418 if (clonedSem == NULL) 419 return NULL; 420 421 if (context->fSemaphores.Insert(clonedSem) != B_OK) { 422 delete clonedSem; 423 return NULL; 424 } 425 context->fSemaphoreCount++; 426 } 427 428 contextDeleter.Detach(); 429 return context; 430 } 431 432 status_t OpenSem(const char* name, int openFlags, mode_t mode, 433 uint32 semCount, sem_t* userSem, sem_t*& _usedUserSem, int32_t& _id, 434 bool& _created) 435 { 436 NamedSem* sem = NULL; 437 status_t error = sSemTable.OpenNamedSem(name, openFlags, mode, semCount, 438 sem, _created); 439 if (error != B_OK) 440 return error; 441 442 MutexLocker _(fLock); 443 444 TeamSemInfo* teamSem = fSemaphores.Lookup(sem->ID()); 445 if (teamSem != NULL) { 446 // already open -- just increment the open count 447 teamSem->Open(); 448 sem->ReleaseReference(); 449 _usedUserSem = teamSem->UserSemaphore(); 450 _id = teamSem->ID(); 451 return B_OK; 452 } 453 454 // not open yet -- create a new team sem 455 456 // first check the semaphore limit, though 457 if (fSemaphoreCount >= MAX_POSIX_SEMS_PER_TEAM) { 458 sem->ReleaseReference(); 459 if (_created) 460 sSemTable.UnlinkNamedSem(name); 461 return ENOSPC; 462 } 463 464 teamSem = new(std::nothrow) TeamSemInfo(sem, userSem); 465 if (teamSem == NULL) { 466 sem->ReleaseReference(); 467 if (_created) 468 sSemTable.UnlinkNamedSem(name); 469 return B_NO_MEMORY; 470 } 471 472 error = fSemaphores.Insert(teamSem); 473 if (error != B_OK) { 474 delete teamSem; 475 if (_created) 476 sSemTable.UnlinkNamedSem(name); 477 return error; 478 } 479 480 fSemaphoreCount++; 481 482 _usedUserSem = teamSem->UserSemaphore(); 483 _id = teamSem->ID(); 484 485 return B_OK; 486 } 487 488 status_t CloseSem(sem_id id, sem_t*& deleteUserSem) 489 { 490 deleteUserSem = NULL; 491 492 MutexLocker _(fLock); 493 494 TeamSemInfo* sem = fSemaphores.Lookup(id); 495 if (sem == NULL) 496 return B_BAD_VALUE; 497 498 if (sem->Close()) { 499 // last reference closed 500 fSemaphores.Remove(sem); 501 fSemaphoreCount--; 502 deleteUserSem = sem->UserSemaphore(); 503 delete sem; 504 } 505 506 return B_OK; 507 } 508 509 status_t AcquireSem(sem_id id, bigtime_t timeout) 510 { 511 MutexLocker locker(fLock); 512 513 TeamSemInfo* sem = fSemaphores.Lookup(id); 514 if (sem == NULL) 515 return B_BAD_VALUE; 516 else 517 id = sem->SemaphoreID(); 518 519 locker.Unlock(); 520 521 status_t error; 522 if (timeout == 0) { 523 error = acquire_sem_etc(id, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT, 524 0); 525 } else if (timeout == B_INFINITE_TIMEOUT) { 526 error = acquire_sem_etc(id, 1, B_CAN_INTERRUPT, 0); 527 } else { 528 error = acquire_sem_etc(id, 1, 529 B_CAN_INTERRUPT | B_ABSOLUTE_REAL_TIME_TIMEOUT, timeout); 530 } 531 532 return error == B_BAD_SEM_ID ? B_BAD_VALUE : error; 533 } 534 535 status_t ReleaseSem(sem_id id) 536 { 537 MutexLocker locker(fLock); 538 539 TeamSemInfo* sem = fSemaphores.Lookup(id); 540 if (sem == NULL) 541 return B_BAD_VALUE; 542 else 543 id = sem->SemaphoreID(); 544 545 locker.Unlock(); 546 547 status_t error = release_sem(id); 548 return error == B_BAD_SEM_ID ? B_BAD_VALUE : error; 549 } 550 551 status_t GetSemCount(sem_id id, int& _count) 552 { 553 MutexLocker locker(fLock); 554 555 TeamSemInfo* sem = fSemaphores.Lookup(id); 556 if (sem == NULL) 557 return B_BAD_VALUE; 558 else 559 id = sem->SemaphoreID(); 560 561 locker.Unlock(); 562 563 int32 count; 564 status_t error = get_sem_count(id, &count); 565 if (error != B_OK) 566 return error; 567 568 _count = count; 569 return B_OK; 570 } 571 572 private: 573 sem_id _NextPrivateSemID() 574 { 575 while (true) { 576 if (fNextPrivateSemID >= 0) 577 fNextPrivateSemID = -1; 578 579 sem_id id = fNextPrivateSemID--; 580 if (fSemaphores.Lookup(id) == NULL) 581 return id; 582 } 583 } 584 585 private: 586 typedef BOpenHashTable<TeamSemHashDefinition, true> SemTable; 587 588 mutex fLock; 589 SemTable fSemaphores; 590 int32 fSemaphoreCount; 591 sem_id fNextPrivateSemID; 592 }; 593 594 595 // #pragma mark - implementation private 596 597 598 static realtime_sem_context* 599 get_current_team_context() 600 { 601 Team* team = thread_get_current_thread()->team; 602 603 // get context 604 realtime_sem_context* context = atomic_pointer_get( 605 &team->realtime_sem_context); 606 if (context != NULL) 607 return context; 608 609 // no context yet -- create a new one 610 context = new(std::nothrow) realtime_sem_context; 611 if (context == NULL || context->Init() != B_OK) { 612 delete context; 613 return NULL; 614 } 615 616 // set the allocated context 617 realtime_sem_context* oldContext = atomic_pointer_test_and_set( 618 &team->realtime_sem_context, context, (realtime_sem_context*)NULL); 619 if (oldContext == NULL) 620 return context; 621 622 // someone else was quicker 623 delete context; 624 return oldContext; 625 } 626 627 628 static status_t 629 copy_sem_name_to_kernel(const char* userName, KPath& buffer, char*& name) 630 { 631 if (userName == NULL) 632 return B_BAD_VALUE; 633 if (!IS_USER_ADDRESS(userName)) 634 return B_BAD_ADDRESS; 635 636 if (buffer.InitCheck() != B_OK) 637 return B_NO_MEMORY; 638 639 // copy userland path to kernel 640 name = buffer.LockBuffer(); 641 ssize_t actualLength = user_strlcpy(name, userName, buffer.BufferSize()); 642 643 if (actualLength < 0) 644 return B_BAD_ADDRESS; 645 if ((size_t)actualLength >= buffer.BufferSize()) 646 return ENAMETOOLONG; 647 648 return B_OK; 649 } 650 651 652 // #pragma mark - kernel internal 653 654 655 void 656 realtime_sem_init() 657 { 658 new(&sSemTable) GlobalSemTable; 659 if (sSemTable.Init() != B_OK) 660 panic("realtime_sem_init() failed to init global sem table"); 661 } 662 663 664 void 665 delete_realtime_sem_context(realtime_sem_context* context) 666 { 667 delete context; 668 } 669 670 671 realtime_sem_context* 672 clone_realtime_sem_context(realtime_sem_context* context) 673 { 674 if (context == NULL) 675 return NULL; 676 677 return context->Clone(); 678 } 679 680 681 // #pragma mark - syscalls 682 683 684 status_t 685 _user_realtime_sem_open(const char* userName, int openFlagsOrShared, 686 mode_t mode, uint32 semCount, sem_t* userSem, sem_t** _usedUserSem) 687 { 688 realtime_sem_context* context = get_current_team_context(); 689 if (context == NULL) 690 return B_NO_MEMORY; 691 692 if (semCount > MAX_POSIX_SEM_VALUE) 693 return B_BAD_VALUE; 694 695 // userSem must always be given 696 if (userSem == NULL) 697 return B_BAD_VALUE; 698 if (!IS_USER_ADDRESS(userSem)) 699 return B_BAD_ADDRESS; 700 701 // check user pointers 702 if (_usedUserSem == NULL) 703 return B_BAD_VALUE; 704 if (!IS_USER_ADDRESS(_usedUserSem) || !IS_USER_ADDRESS(userName)) 705 return B_BAD_ADDRESS; 706 707 // copy name to kernel 708 KPath nameBuffer(B_PATH_NAME_LENGTH); 709 char* name; 710 status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name); 711 if (error != B_OK) 712 return error; 713 714 // open the semaphore 715 sem_t* usedUserSem; 716 bool created = false; 717 int32_t id; 718 error = context->OpenSem(name, openFlagsOrShared, mode, semCount, userSem, 719 usedUserSem, id, created); 720 if (error != B_OK) 721 return error; 722 723 // copy results back to userland 724 if (user_memcpy(&userSem->u.named_sem_id, &id, sizeof(int32_t)) != B_OK 725 || user_memcpy(_usedUserSem, &usedUserSem, sizeof(sem_t*)) != B_OK) { 726 if (created) 727 sSemTable.UnlinkNamedSem(name); 728 sem_t* dummy; 729 context->CloseSem(id, dummy); 730 return B_BAD_ADDRESS; 731 } 732 733 return B_OK; 734 } 735 736 737 status_t 738 _user_realtime_sem_close(sem_id semID, sem_t** _deleteUserSem) 739 { 740 if (_deleteUserSem != NULL && !IS_USER_ADDRESS(_deleteUserSem)) 741 return B_BAD_ADDRESS; 742 743 realtime_sem_context* context = get_current_team_context(); 744 if (context == NULL) 745 return B_BAD_VALUE; 746 747 // close sem 748 sem_t* deleteUserSem; 749 status_t error = context->CloseSem(semID, deleteUserSem); 750 if (error != B_OK) 751 return error; 752 753 // copy back result to userland 754 if (_deleteUserSem != NULL 755 && user_memcpy(_deleteUserSem, &deleteUserSem, sizeof(sem_t*)) 756 != B_OK) { 757 return B_BAD_ADDRESS; 758 } 759 760 return B_OK; 761 } 762 763 764 status_t 765 _user_realtime_sem_unlink(const char* userName) 766 { 767 // copy name to kernel 768 KPath nameBuffer(B_PATH_NAME_LENGTH); 769 char* name; 770 status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name); 771 if (error != B_OK) 772 return error; 773 774 return sSemTable.UnlinkNamedSem(name); 775 } 776 777 778 status_t 779 _user_realtime_sem_get_value(sem_id semID, int* _value) 780 { 781 if (_value == NULL) 782 return B_BAD_VALUE; 783 if (!IS_USER_ADDRESS(_value)) 784 return B_BAD_ADDRESS; 785 786 realtime_sem_context* context = get_current_team_context(); 787 if (context == NULL) 788 return B_BAD_VALUE; 789 790 // get sem count 791 int count; 792 status_t error = context->GetSemCount(semID, count); 793 if (error != B_OK) 794 return error; 795 796 // copy back result to userland 797 if (user_memcpy(_value, &count, sizeof(int)) != B_OK) 798 return B_BAD_ADDRESS; 799 800 return B_OK; 801 } 802 803 804 status_t 805 _user_realtime_sem_post(sem_id semID) 806 { 807 realtime_sem_context* context = get_current_team_context(); 808 if (context == NULL) 809 return B_BAD_VALUE; 810 811 return context->ReleaseSem(semID); 812 } 813 814 815 status_t 816 _user_realtime_sem_wait(sem_id semID, bigtime_t timeout) 817 { 818 realtime_sem_context* context = get_current_team_context(); 819 if (context == NULL) 820 return B_BAD_VALUE; 821 822 return syscall_restart_handle_post(context->AcquireSem(semID, timeout)); 823 } 824