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, uint32 flags, 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 = acquire_sem_etc(id, 1, flags | B_CAN_INTERRUPT, timeout); 522 return error == B_BAD_SEM_ID ? B_BAD_VALUE : error; 523 } 524 525 status_t ReleaseSem(sem_id id) 526 { 527 MutexLocker locker(fLock); 528 529 TeamSemInfo* sem = fSemaphores.Lookup(id); 530 if (sem == NULL) 531 return B_BAD_VALUE; 532 else 533 id = sem->SemaphoreID(); 534 535 locker.Unlock(); 536 537 status_t error = release_sem(id); 538 return error == B_BAD_SEM_ID ? B_BAD_VALUE : error; 539 } 540 541 status_t GetSemCount(sem_id id, int& _count) 542 { 543 MutexLocker locker(fLock); 544 545 TeamSemInfo* sem = fSemaphores.Lookup(id); 546 if (sem == NULL) 547 return B_BAD_VALUE; 548 else 549 id = sem->SemaphoreID(); 550 551 locker.Unlock(); 552 553 int32 count; 554 status_t error = get_sem_count(id, &count); 555 if (error != B_OK) 556 return error; 557 558 _count = count; 559 return B_OK; 560 } 561 562 private: 563 sem_id _NextPrivateSemID() 564 { 565 while (true) { 566 if (fNextPrivateSemID >= 0) 567 fNextPrivateSemID = -1; 568 569 sem_id id = fNextPrivateSemID--; 570 if (fSemaphores.Lookup(id) == NULL) 571 return id; 572 } 573 } 574 575 private: 576 typedef BOpenHashTable<TeamSemHashDefinition, true> SemTable; 577 578 mutex fLock; 579 SemTable fSemaphores; 580 int32 fSemaphoreCount; 581 sem_id fNextPrivateSemID; 582 }; 583 584 585 // #pragma mark - implementation private 586 587 588 static realtime_sem_context* 589 get_current_team_context() 590 { 591 Team* team = thread_get_current_thread()->team; 592 593 // get context 594 realtime_sem_context* context = atomic_pointer_get( 595 &team->realtime_sem_context); 596 if (context != NULL) 597 return context; 598 599 // no context yet -- create a new one 600 context = new(std::nothrow) realtime_sem_context; 601 if (context == NULL || context->Init() != B_OK) { 602 delete context; 603 return NULL; 604 } 605 606 // set the allocated context 607 realtime_sem_context* oldContext = atomic_pointer_test_and_set( 608 &team->realtime_sem_context, context, (realtime_sem_context*)NULL); 609 if (oldContext == NULL) 610 return context; 611 612 // someone else was quicker 613 delete context; 614 return oldContext; 615 } 616 617 618 static status_t 619 copy_sem_name_to_kernel(const char* userName, KPath& buffer, char*& name) 620 { 621 if (userName == NULL) 622 return B_BAD_VALUE; 623 if (!IS_USER_ADDRESS(userName)) 624 return B_BAD_ADDRESS; 625 626 if (buffer.InitCheck() != B_OK) 627 return B_NO_MEMORY; 628 629 // copy userland path to kernel 630 name = buffer.LockBuffer(); 631 ssize_t actualLength = user_strlcpy(name, userName, buffer.BufferSize()); 632 633 if (actualLength < 0) 634 return B_BAD_ADDRESS; 635 if ((size_t)actualLength >= buffer.BufferSize()) 636 return ENAMETOOLONG; 637 638 return B_OK; 639 } 640 641 642 // #pragma mark - kernel internal 643 644 645 void 646 realtime_sem_init() 647 { 648 new(&sSemTable) GlobalSemTable; 649 if (sSemTable.Init() != B_OK) 650 panic("realtime_sem_init() failed to init global sem table"); 651 } 652 653 654 void 655 delete_realtime_sem_context(realtime_sem_context* context) 656 { 657 delete context; 658 } 659 660 661 realtime_sem_context* 662 clone_realtime_sem_context(realtime_sem_context* context) 663 { 664 if (context == NULL) 665 return NULL; 666 667 return context->Clone(); 668 } 669 670 671 // #pragma mark - syscalls 672 673 674 status_t 675 _user_realtime_sem_open(const char* userName, int openFlagsOrShared, 676 mode_t mode, uint32 semCount, sem_t* userSem, sem_t** _usedUserSem) 677 { 678 realtime_sem_context* context = get_current_team_context(); 679 if (context == NULL) 680 return B_NO_MEMORY; 681 682 if (semCount > MAX_POSIX_SEM_VALUE) 683 return B_BAD_VALUE; 684 685 // userSem must always be given 686 if (userSem == NULL) 687 return B_BAD_VALUE; 688 if (!IS_USER_ADDRESS(userSem)) 689 return B_BAD_ADDRESS; 690 691 // check user pointers 692 if (_usedUserSem == NULL) 693 return B_BAD_VALUE; 694 if (!IS_USER_ADDRESS(_usedUserSem) || !IS_USER_ADDRESS(userName)) 695 return B_BAD_ADDRESS; 696 697 // copy name to kernel 698 KPath nameBuffer(B_PATH_NAME_LENGTH); 699 char* name; 700 status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name); 701 if (error != B_OK) 702 return error; 703 704 // open the semaphore 705 sem_t* usedUserSem; 706 bool created = false; 707 int32_t id; 708 error = context->OpenSem(name, openFlagsOrShared, mode, semCount, userSem, 709 usedUserSem, id, created); 710 if (error != B_OK) 711 return error; 712 713 // copy results back to userland 714 if (user_memcpy(&userSem->u.named_sem_id, &id, sizeof(int32_t)) != B_OK 715 || user_memcpy(_usedUserSem, &usedUserSem, sizeof(sem_t*)) != B_OK) { 716 if (created) 717 sSemTable.UnlinkNamedSem(name); 718 sem_t* dummy; 719 context->CloseSem(id, dummy); 720 return B_BAD_ADDRESS; 721 } 722 723 return B_OK; 724 } 725 726 727 status_t 728 _user_realtime_sem_close(sem_id semID, sem_t** _deleteUserSem) 729 { 730 if (_deleteUserSem != NULL && !IS_USER_ADDRESS(_deleteUserSem)) 731 return B_BAD_ADDRESS; 732 733 realtime_sem_context* context = get_current_team_context(); 734 if (context == NULL) 735 return B_BAD_VALUE; 736 737 // close sem 738 sem_t* deleteUserSem; 739 status_t error = context->CloseSem(semID, deleteUserSem); 740 if (error != B_OK) 741 return error; 742 743 // copy back result to userland 744 if (_deleteUserSem != NULL 745 && user_memcpy(_deleteUserSem, &deleteUserSem, sizeof(sem_t*)) 746 != B_OK) { 747 return B_BAD_ADDRESS; 748 } 749 750 return B_OK; 751 } 752 753 754 status_t 755 _user_realtime_sem_unlink(const char* userName) 756 { 757 // copy name to kernel 758 KPath nameBuffer(B_PATH_NAME_LENGTH); 759 char* name; 760 status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name); 761 if (error != B_OK) 762 return error; 763 764 return sSemTable.UnlinkNamedSem(name); 765 } 766 767 768 status_t 769 _user_realtime_sem_get_value(sem_id semID, int* _value) 770 { 771 if (_value == NULL) 772 return B_BAD_VALUE; 773 if (!IS_USER_ADDRESS(_value)) 774 return B_BAD_ADDRESS; 775 776 realtime_sem_context* context = get_current_team_context(); 777 if (context == NULL) 778 return B_BAD_VALUE; 779 780 // get sem count 781 int count; 782 status_t error = context->GetSemCount(semID, count); 783 if (error != B_OK) 784 return error; 785 786 // copy back result to userland 787 if (user_memcpy(_value, &count, sizeof(int)) != B_OK) 788 return B_BAD_ADDRESS; 789 790 return B_OK; 791 } 792 793 794 status_t 795 _user_realtime_sem_post(sem_id semID) 796 { 797 realtime_sem_context* context = get_current_team_context(); 798 if (context == NULL) 799 return B_BAD_VALUE; 800 801 return context->ReleaseSem(semID); 802 } 803 804 805 status_t 806 _user_realtime_sem_wait(sem_id semID, uint32 flags, bigtime_t timeout) 807 { 808 realtime_sem_context* context = get_current_team_context(); 809 if (context == NULL) 810 return B_BAD_VALUE; 811 812 return syscall_restart_handle_post(context->AcquireSem(semID, flags, timeout)); 813 } 814