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