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:
SemInfo()31 SemInfo()
32 :
33 fSemaphoreID(-1)
34 {
35 }
36
~SemInfo()37 virtual ~SemInfo()
38 {
39 if (fSemaphoreID >= 0)
40 delete_sem(fSemaphoreID);
41 }
42
SemaphoreID() const43 sem_id SemaphoreID() const { return fSemaphoreID; }
44
Init(int32 semCount,const char * name)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:
NamedSem()65 NamedSem()
66 :
67 fName(NULL),
68 fRefCount(1)
69 {
70 }
71
~NamedSem()72 virtual ~NamedSem()
73 {
74 free(fName);
75 }
76
Name() const77 const char* Name() const { return fName; }
78
Init(const char * name,mode_t mode,int32 semCount)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
AcquireReference()96 void AcquireReference()
97 {
98 atomic_add(&fRefCount, 1);
99 }
100
ReleaseReference()101 void ReleaseReference()
102 {
103 if (atomic_add(&fRefCount, -1) == 1)
104 delete this;
105 }
106
HasPermissions() const107 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
ID() const123 virtual sem_id ID() const
124 {
125 return SemaphoreID();
126 }
127
Clone()128 virtual SemInfo* Clone()
129 {
130 AcquireReference();
131 return this;
132 }
133
Delete()134 virtual void Delete()
135 {
136 ReleaseReference();
137 }
138
HashLink()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
HashKey__anon7f0ec9450111::NamedSemHashDefinition159 size_t HashKey(const KeyType& key) const
160 {
161 return hash_hash_string(key);
162 }
163
Hash__anon7f0ec9450111::NamedSemHashDefinition164 size_t Hash(NamedSem* semaphore) const
165 {
166 return HashKey(semaphore->Name());
167 }
168
Compare__anon7f0ec9450111::NamedSemHashDefinition169 bool Compare(const KeyType& key, NamedSem* semaphore) const
170 {
171 return strcmp(key, semaphore->Name()) == 0;
172 }
173
GetLink__anon7f0ec9450111::NamedSemHashDefinition174 NamedSem*& GetLink(NamedSem* semaphore) const
175 {
176 return semaphore->HashLink();
177 }
178 };
179
180
181 class GlobalSemTable {
182 public:
GlobalSemTable()183 GlobalSemTable()
184 :
185 fSemaphoreCount(0)
186 {
187 mutex_init(&fLock, "global named sem table");
188 }
189
~GlobalSemTable()190 ~GlobalSemTable()
191 {
192 mutex_destroy(&fLock);
193 }
194
Init()195 status_t Init()
196 {
197 return fNamedSemaphores.Init();
198 }
199
OpenNamedSem(const char * name,int openFlags,mode_t mode,uint32 semCount,NamedSem * & _sem,bool & _created)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
UnlinkNamedSem(const char * name)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:
TeamSemInfo(SemInfo * semaphore,sem_t * userSem)285 TeamSemInfo(SemInfo* semaphore, sem_t* userSem)
286 :
287 fSemaphore(semaphore),
288 fUserSemaphore(userSem),
289 fOpenCount(1)
290 {
291 }
292
~TeamSemInfo()293 ~TeamSemInfo()
294 {
295 if (fSemaphore != NULL)
296 fSemaphore->Delete();
297 }
298
ID() const299 sem_id ID() const { return fSemaphore->ID(); }
SemaphoreID() const300 sem_id SemaphoreID() const { return fSemaphore->SemaphoreID(); }
UserSemaphore() const301 sem_t* UserSemaphore() const { return fUserSemaphore; }
302
Open()303 void Open()
304 {
305 fOpenCount++;
306 }
307
Close()308 bool Close()
309 {
310 return --fOpenCount == 0;
311 }
312
Clone() const313 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
HashLink()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
HashKey__anon7f0ec9450111::TeamSemHashDefinition348 size_t HashKey(const KeyType& key) const
349 {
350 return (size_t)key;
351 }
352
Hash__anon7f0ec9450111::TeamSemHashDefinition353 size_t Hash(TeamSemInfo* semaphore) const
354 {
355 return HashKey(semaphore->ID());
356 }
357
Compare__anon7f0ec9450111::TeamSemHashDefinition358 bool Compare(const KeyType& key, TeamSemInfo* semaphore) const
359 {
360 return key == semaphore->ID();
361 }
362
GetLink__anon7f0ec9450111::TeamSemHashDefinition363 TeamSemInfo*& GetLink(TeamSemInfo* semaphore) const
364 {
365 return semaphore->HashLink();
366 }
367 };
368
369 } // namespace
370
371
372 struct realtime_sem_context {
realtime_sem_contextrealtime_sem_context373 realtime_sem_context()
374 :
375 fSemaphoreCount(0)
376 {
377 mutex_init(&fLock, "realtime sem context");
378 }
379
~realtime_sem_contextrealtime_sem_context380 ~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
Initrealtime_sem_context396 status_t Init()
397 {
398 fNextPrivateSemID = -1;
399 return fSemaphores.Init();
400 }
401
Clonerealtime_sem_context402 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
OpenSemrealtime_sem_context432 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
CloseSemrealtime_sem_context488 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
AcquireSemrealtime_sem_context509 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
ReleaseSemrealtime_sem_context525 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
GetSemCountrealtime_sem_context541 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:
_NextPrivateSemIDrealtime_sem_context563 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*
get_current_team_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
copy_sem_name_to_kernel(const char * userName,KPath & buffer,char * & name)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
realtime_sem_init()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
delete_realtime_sem_context(realtime_sem_context * context)655 delete_realtime_sem_context(realtime_sem_context* context)
656 {
657 delete context;
658 }
659
660
661 realtime_sem_context*
clone_realtime_sem_context(realtime_sem_context * 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
_user_realtime_sem_open(const char * userName,int openFlagsOrShared,mode_t mode,uint32 semCount,sem_t * userSem,sem_t ** _usedUserSem)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
_user_realtime_sem_close(sem_id semID,sem_t ** _deleteUserSem)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
_user_realtime_sem_unlink(const char * userName)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
_user_realtime_sem_get_value(sem_id semID,int * _value)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
_user_realtime_sem_post(sem_id semID)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
_user_realtime_sem_wait(sem_id semID,uint32 flags,bigtime_t timeout)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