kernel: Avoid double lock in wait_for_child with child == team.
[haiku.git] / src / system / kernel / team.cpp
blob8c85d0bd78e8a0d06e0d29db4cbd2d4182a06f5c
1 /*
2 * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org.
3 * Copyright 2008-2016, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
5 * Distributed under the terms of the MIT License.
7 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
8 * Distributed under the terms of the NewOS License.
9 */
12 /*! Team functions */
15 #include <team.h>
17 #include <errno.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/wait.h>
23 #include <OS.h>
25 #include <AutoDeleter.h>
26 #include <FindDirectory.h>
28 #include <extended_system_info_defs.h>
30 #include <commpage.h>
31 #include <boot_device.h>
32 #include <elf.h>
33 #include <file_cache.h>
34 #include <find_directory_private.h>
35 #include <fs/KPath.h>
36 #include <heap.h>
37 #include <int.h>
38 #include <kernel.h>
39 #include <kimage.h>
40 #include <kscheduler.h>
41 #include <ksignal.h>
42 #include <Notifications.h>
43 #include <port.h>
44 #include <posix/realtime_sem.h>
45 #include <posix/xsi_semaphore.h>
46 #include <sem.h>
47 #include <syscall_process_info.h>
48 #include <syscall_restart.h>
49 #include <syscalls.h>
50 #include <tls.h>
51 #include <tracing.h>
52 #include <user_runtime.h>
53 #include <user_thread.h>
54 #include <usergroup.h>
55 #include <vfs.h>
56 #include <vm/vm.h>
57 #include <vm/VMAddressSpace.h>
58 #include <util/AutoLock.h>
60 #include "TeamThreadTables.h"
63 //#define TRACE_TEAM
64 #ifdef TRACE_TEAM
65 # define TRACE(x) dprintf x
66 #else
67 # define TRACE(x) ;
68 #endif
71 struct team_key {
72 team_id id;
75 struct team_arg {
76 char *path;
77 char **flat_args;
78 size_t flat_args_size;
79 uint32 arg_count;
80 uint32 env_count;
81 mode_t umask;
82 uint32 flags;
83 port_id error_port;
84 uint32 error_token;
87 #define TEAM_ARGS_FLAG_NO_ASLR 0x01
90 namespace {
93 class TeamNotificationService : public DefaultNotificationService {
94 public:
95 TeamNotificationService();
97 void Notify(uint32 eventCode, Team* team);
101 // #pragma mark - TeamTable
104 typedef BKernel::TeamThreadTable<Team> TeamTable;
107 // #pragma mark - ProcessGroupHashDefinition
110 struct ProcessGroupHashDefinition {
111 typedef pid_t KeyType;
112 typedef ProcessGroup ValueType;
114 size_t HashKey(pid_t key) const
116 return key;
119 size_t Hash(ProcessGroup* value) const
121 return HashKey(value->id);
124 bool Compare(pid_t key, ProcessGroup* value) const
126 return value->id == key;
129 ProcessGroup*& GetLink(ProcessGroup* value) const
131 return value->next;
135 typedef BOpenHashTable<ProcessGroupHashDefinition> ProcessGroupHashTable;
138 } // unnamed namespace
141 // #pragma mark -
144 // the team_id -> Team hash table and the lock protecting it
145 static TeamTable sTeamHash;
146 static spinlock sTeamHashLock = B_SPINLOCK_INITIALIZER;
148 // the pid_t -> ProcessGroup hash table and the lock protecting it
149 static ProcessGroupHashTable sGroupHash;
150 static spinlock sGroupHashLock = B_SPINLOCK_INITIALIZER;
152 static Team* sKernelTeam = NULL;
154 // A list of process groups of children of dying session leaders that need to
155 // be signalled, if they have become orphaned and contain stopped processes.
156 static ProcessGroupList sOrphanedCheckProcessGroups;
157 static mutex sOrphanedCheckLock
158 = MUTEX_INITIALIZER("orphaned process group check");
160 // some arbitrarily chosen limits -- should probably depend on the available
161 // memory (the limit is not yet enforced)
162 static int32 sMaxTeams = 2048;
163 static int32 sUsedTeams = 1;
165 static TeamNotificationService sNotificationService;
167 static const size_t kTeamUserDataReservedSize = 128 * B_PAGE_SIZE;
168 static const size_t kTeamUserDataInitialSize = 4 * B_PAGE_SIZE;
171 // #pragma mark - TeamListIterator
174 TeamListIterator::TeamListIterator()
176 // queue the entry
177 InterruptsSpinLocker locker(sTeamHashLock);
178 sTeamHash.InsertIteratorEntry(&fEntry);
182 TeamListIterator::~TeamListIterator()
184 // remove the entry
185 InterruptsSpinLocker locker(sTeamHashLock);
186 sTeamHash.RemoveIteratorEntry(&fEntry);
190 Team*
191 TeamListIterator::Next()
193 // get the next team -- if there is one, get reference for it
194 InterruptsSpinLocker locker(sTeamHashLock);
195 Team* team = sTeamHash.NextElement(&fEntry);
196 if (team != NULL)
197 team->AcquireReference();
199 return team;
203 // #pragma mark - Tracing
206 #if TEAM_TRACING
207 namespace TeamTracing {
209 class TeamForked : public AbstractTraceEntry {
210 public:
211 TeamForked(thread_id forkedThread)
213 fForkedThread(forkedThread)
215 Initialized();
218 virtual void AddDump(TraceOutput& out)
220 out.Print("team forked, new thread %" B_PRId32, fForkedThread);
223 private:
224 thread_id fForkedThread;
228 class ExecTeam : public AbstractTraceEntry {
229 public:
230 ExecTeam(const char* path, int32 argCount, const char* const* args,
231 int32 envCount, const char* const* env)
233 fArgCount(argCount),
234 fArgs(NULL)
236 fPath = alloc_tracing_buffer_strcpy(path, B_PATH_NAME_LENGTH,
237 false);
239 // determine the buffer size we need for the args
240 size_t argBufferSize = 0;
241 for (int32 i = 0; i < argCount; i++)
242 argBufferSize += strlen(args[i]) + 1;
244 // allocate a buffer
245 fArgs = (char*)alloc_tracing_buffer(argBufferSize);
246 if (fArgs) {
247 char* buffer = fArgs;
248 for (int32 i = 0; i < argCount; i++) {
249 size_t argSize = strlen(args[i]) + 1;
250 memcpy(buffer, args[i], argSize);
251 buffer += argSize;
255 // ignore env for the time being
256 (void)envCount;
257 (void)env;
259 Initialized();
262 virtual void AddDump(TraceOutput& out)
264 out.Print("team exec, \"%p\", args:", fPath);
266 if (fArgs != NULL) {
267 char* args = fArgs;
268 for (int32 i = 0; !out.IsFull() && i < fArgCount; i++) {
269 out.Print(" \"%s\"", args);
270 args += strlen(args) + 1;
272 } else
273 out.Print(" <too long>");
276 private:
277 char* fPath;
278 int32 fArgCount;
279 char* fArgs;
283 static const char*
284 job_control_state_name(job_control_state state)
286 switch (state) {
287 case JOB_CONTROL_STATE_NONE:
288 return "none";
289 case JOB_CONTROL_STATE_STOPPED:
290 return "stopped";
291 case JOB_CONTROL_STATE_CONTINUED:
292 return "continued";
293 case JOB_CONTROL_STATE_DEAD:
294 return "dead";
295 default:
296 return "invalid";
301 class SetJobControlState : public AbstractTraceEntry {
302 public:
303 SetJobControlState(team_id team, job_control_state newState, Signal* signal)
305 fTeam(team),
306 fNewState(newState),
307 fSignal(signal != NULL ? signal->Number() : 0)
309 Initialized();
312 virtual void AddDump(TraceOutput& out)
314 out.Print("team set job control state, team %" B_PRId32 ", "
315 "new state: %s, signal: %d",
316 fTeam, job_control_state_name(fNewState), fSignal);
319 private:
320 team_id fTeam;
321 job_control_state fNewState;
322 int fSignal;
326 class WaitForChild : public AbstractTraceEntry {
327 public:
328 WaitForChild(pid_t child, uint32 flags)
330 fChild(child),
331 fFlags(flags)
333 Initialized();
336 virtual void AddDump(TraceOutput& out)
338 out.Print("team wait for child, child: %" B_PRId32 ", "
339 "flags: %#" B_PRIx32, fChild, fFlags);
342 private:
343 pid_t fChild;
344 uint32 fFlags;
348 class WaitForChildDone : public AbstractTraceEntry {
349 public:
350 WaitForChildDone(const job_control_entry& entry)
352 fState(entry.state),
353 fTeam(entry.thread),
354 fStatus(entry.status),
355 fReason(entry.reason),
356 fSignal(entry.signal)
358 Initialized();
361 WaitForChildDone(status_t error)
363 fTeam(error)
365 Initialized();
368 virtual void AddDump(TraceOutput& out)
370 if (fTeam >= 0) {
371 out.Print("team wait for child done, team: %" B_PRId32 ", "
372 "state: %s, status: %#" B_PRIx32 ", reason: %#x, signal: %d\n",
373 fTeam, job_control_state_name(fState), fStatus, fReason,
374 fSignal);
375 } else {
376 out.Print("team wait for child failed, error: "
377 "%#" B_PRIx32 ", ", fTeam);
381 private:
382 job_control_state fState;
383 team_id fTeam;
384 status_t fStatus;
385 uint16 fReason;
386 uint16 fSignal;
389 } // namespace TeamTracing
391 # define T(x) new(std::nothrow) TeamTracing::x;
392 #else
393 # define T(x) ;
394 #endif
397 // #pragma mark - TeamNotificationService
400 TeamNotificationService::TeamNotificationService()
401 : DefaultNotificationService("teams")
406 void
407 TeamNotificationService::Notify(uint32 eventCode, Team* team)
409 char eventBuffer[128];
410 KMessage event;
411 event.SetTo(eventBuffer, sizeof(eventBuffer), TEAM_MONITOR);
412 event.AddInt32("event", eventCode);
413 event.AddInt32("team", team->id);
414 event.AddPointer("teamStruct", team);
416 DefaultNotificationService::Notify(event, eventCode);
420 // #pragma mark - Team
423 Team::Team(team_id id, bool kernel)
425 // allocate an ID
426 this->id = id;
427 visible = true;
428 serial_number = -1;
430 // init mutex
431 if (kernel) {
432 mutex_init(&fLock, "Team:kernel");
433 } else {
434 char lockName[16];
435 snprintf(lockName, sizeof(lockName), "Team:%" B_PRId32, id);
436 mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
439 hash_next = siblings_next = children = parent = NULL;
440 fName[0] = '\0';
441 fArgs[0] = '\0';
442 num_threads = 0;
443 io_context = NULL;
444 address_space = NULL;
445 realtime_sem_context = NULL;
446 xsi_sem_context = NULL;
447 thread_list = NULL;
448 main_thread = NULL;
449 loading_info = NULL;
450 state = TEAM_STATE_BIRTH;
451 flags = 0;
452 death_entry = NULL;
453 user_data_area = -1;
454 user_data = 0;
455 used_user_data = 0;
456 user_data_size = 0;
457 free_user_threads = NULL;
459 commpage_address = NULL;
461 supplementary_groups = NULL;
462 supplementary_group_count = 0;
464 dead_threads_kernel_time = 0;
465 dead_threads_user_time = 0;
466 cpu_clock_offset = 0;
468 // dead threads
469 list_init(&dead_threads);
470 dead_threads_count = 0;
472 // dead children
473 dead_children.count = 0;
474 dead_children.kernel_time = 0;
475 dead_children.user_time = 0;
477 // job control entry
478 job_control_entry = new(nothrow) ::job_control_entry;
479 if (job_control_entry != NULL) {
480 job_control_entry->state = JOB_CONTROL_STATE_NONE;
481 job_control_entry->thread = id;
482 job_control_entry->team = this;
485 // exit status -- setting initialized to false suffices
486 exit.initialized = false;
488 list_init(&sem_list);
489 list_init_etc(&port_list, port_team_link_offset());
490 list_init(&image_list);
491 list_init(&watcher_list);
493 clear_team_debug_info(&debug_info, true);
495 // init dead/stopped/continued children condition vars
496 dead_children.condition_variable.Init(&dead_children, "team children");
498 B_INITIALIZE_SPINLOCK(&time_lock);
499 B_INITIALIZE_SPINLOCK(&signal_lock);
501 fQueuedSignalsCounter = new(std::nothrow) BKernel::QueuedSignalsCounter(
502 kernel ? -1 : MAX_QUEUED_SIGNALS);
503 memset(fSignalActions, 0, sizeof(fSignalActions));
505 fUserDefinedTimerCount = 0;
507 fCoreDumpCondition = NULL;
511 Team::~Team()
513 // get rid of all associated data
514 PrepareForDeletion();
516 if (io_context != NULL)
517 vfs_put_io_context(io_context);
518 delete_owned_ports(this);
519 sem_delete_owned_sems(this);
521 DeleteUserTimers(false);
523 fPendingSignals.Clear();
525 if (fQueuedSignalsCounter != NULL)
526 fQueuedSignalsCounter->ReleaseReference();
528 while (thread_death_entry* threadDeathEntry
529 = (thread_death_entry*)list_remove_head_item(&dead_threads)) {
530 free(threadDeathEntry);
533 while (::job_control_entry* entry = dead_children.entries.RemoveHead())
534 delete entry;
536 while (free_user_thread* entry = free_user_threads) {
537 free_user_threads = entry->next;
538 free(entry);
541 malloc_referenced_release(supplementary_groups);
543 delete job_control_entry;
544 // usually already NULL and transferred to the parent
546 mutex_destroy(&fLock);
550 /*static*/ Team*
551 Team::Create(team_id id, const char* name, bool kernel)
553 // create the team object
554 Team* team = new(std::nothrow) Team(id, kernel);
555 if (team == NULL)
556 return NULL;
557 ObjectDeleter<Team> teamDeleter(team);
559 if (name != NULL)
560 team->SetName(name);
562 // check initialization
563 if (team->job_control_entry == NULL || team->fQueuedSignalsCounter == NULL)
564 return NULL;
566 // finish initialization (arch specifics)
567 if (arch_team_init_team_struct(team, kernel) != B_OK)
568 return NULL;
570 if (!kernel) {
571 status_t error = user_timer_create_team_timers(team);
572 if (error != B_OK)
573 return NULL;
576 // everything went fine
577 return teamDeleter.Detach();
581 /*! \brief Returns the team with the given ID.
582 Returns a reference to the team.
583 Team and thread spinlock must not be held.
585 /*static*/ Team*
586 Team::Get(team_id id)
588 if (id == B_CURRENT_TEAM) {
589 Team* team = thread_get_current_thread()->team;
590 team->AcquireReference();
591 return team;
594 InterruptsSpinLocker locker(sTeamHashLock);
595 Team* team = sTeamHash.Lookup(id);
596 if (team != NULL)
597 team->AcquireReference();
598 return team;
602 /*! \brief Returns the team with the given ID in a locked state.
603 Returns a reference to the team.
604 Team and thread spinlock must not be held.
606 /*static*/ Team*
607 Team::GetAndLock(team_id id)
609 // get the team
610 Team* team = Get(id);
611 if (team == NULL)
612 return NULL;
614 // lock it
615 team->Lock();
617 // only return the team, when it isn't already dying
618 if (team->state >= TEAM_STATE_SHUTDOWN) {
619 team->Unlock();
620 team->ReleaseReference();
621 return NULL;
624 return team;
628 /*! Locks the team and its parent team (if any).
629 The caller must hold a reference to the team or otherwise make sure that
630 it won't be deleted.
631 If the team doesn't have a parent, only the team itself is locked. If the
632 team's parent is the kernel team and \a dontLockParentIfKernel is \c true,
633 only the team itself is locked.
635 \param dontLockParentIfKernel If \c true, the team's parent team is only
636 locked, if it is not the kernel team.
638 void
639 Team::LockTeamAndParent(bool dontLockParentIfKernel)
641 // The locking order is parent -> child. Since the parent can change as long
642 // as we don't lock the team, we need to do a trial and error loop.
643 Lock();
645 while (true) {
646 // If the team doesn't have a parent, we're done. Otherwise try to lock
647 // the parent.This will succeed in most cases, simplifying things.
648 Team* parent = this->parent;
649 if (parent == NULL || (dontLockParentIfKernel && parent == sKernelTeam)
650 || parent->TryLock()) {
651 return;
654 // get a temporary reference to the parent, unlock this team, lock the
655 // parent, and re-lock this team
656 BReference<Team> parentReference(parent);
658 Unlock();
659 parent->Lock();
660 Lock();
662 // If the parent hasn't changed in the meantime, we're done.
663 if (this->parent == parent)
664 return;
666 // The parent has changed -- unlock and retry.
667 parent->Unlock();
672 /*! Unlocks the team and its parent team (if any).
674 void
675 Team::UnlockTeamAndParent()
677 if (parent != NULL)
678 parent->Unlock();
680 Unlock();
684 /*! Locks the team, its parent team (if any), and the team's process group.
685 The caller must hold a reference to the team or otherwise make sure that
686 it won't be deleted.
687 If the team doesn't have a parent, only the team itself is locked.
689 void
690 Team::LockTeamParentAndProcessGroup()
692 LockTeamAndProcessGroup();
694 // We hold the group's and the team's lock, but not the parent team's lock.
695 // If we have a parent, try to lock it.
696 if (this->parent == NULL || this->parent->TryLock())
697 return;
699 // No success -- unlock the team and let LockTeamAndParent() do the rest of
700 // the job.
701 Unlock();
702 LockTeamAndParent(false);
706 /*! Unlocks the team, its parent team (if any), and the team's process group.
708 void
709 Team::UnlockTeamParentAndProcessGroup()
711 group->Unlock();
713 if (parent != NULL)
714 parent->Unlock();
716 Unlock();
720 void
721 Team::LockTeamAndProcessGroup()
723 // The locking order is process group -> child. Since the process group can
724 // change as long as we don't lock the team, we need to do a trial and error
725 // loop.
726 Lock();
728 while (true) {
729 // Try to lock the group. This will succeed in most cases, simplifying
730 // things.
731 ProcessGroup* group = this->group;
732 if (group->TryLock())
733 return;
735 // get a temporary reference to the group, unlock this team, lock the
736 // group, and re-lock this team
737 BReference<ProcessGroup> groupReference(group);
739 Unlock();
740 group->Lock();
741 Lock();
743 // If the group hasn't changed in the meantime, we're done.
744 if (this->group == group)
745 return;
747 // The group has changed -- unlock and retry.
748 group->Unlock();
753 void
754 Team::UnlockTeamAndProcessGroup()
756 group->Unlock();
757 Unlock();
761 void
762 Team::SetName(const char* name)
764 if (const char* lastSlash = strrchr(name, '/'))
765 name = lastSlash + 1;
767 strlcpy(fName, name, B_OS_NAME_LENGTH);
771 void
772 Team::SetArgs(const char* args)
774 strlcpy(fArgs, args, sizeof(fArgs));
778 void
779 Team::SetArgs(const char* path, const char* const* otherArgs, int otherArgCount)
781 fArgs[0] = '\0';
782 strlcpy(fArgs, path, sizeof(fArgs));
783 for (int i = 0; i < otherArgCount; i++) {
784 strlcat(fArgs, " ", sizeof(fArgs));
785 strlcat(fArgs, otherArgs[i], sizeof(fArgs));
790 void
791 Team::ResetSignalsOnExec()
793 // We are supposed to keep pending signals. Signal actions shall be reset
794 // partially: SIG_IGN and SIG_DFL dispositions shall be kept as they are
795 // (for SIGCHLD it's implementation-defined). Others shall be reset to
796 // SIG_DFL. SA_ONSTACK shall be cleared. There's no mention of the other
797 // flags, but since there aren't any handlers, they make little sense, so
798 // we clear them.
800 for (uint32 i = 1; i <= MAX_SIGNAL_NUMBER; i++) {
801 struct sigaction& action = SignalActionFor(i);
802 if (action.sa_handler != SIG_IGN && action.sa_handler != SIG_DFL)
803 action.sa_handler = SIG_DFL;
805 action.sa_mask = 0;
806 action.sa_flags = 0;
807 action.sa_userdata = NULL;
812 void
813 Team::InheritSignalActions(Team* parent)
815 memcpy(fSignalActions, parent->fSignalActions, sizeof(fSignalActions));
819 /*! Adds the given user timer to the team and, if user-defined, assigns it an
822 The caller must hold the team's lock.
824 \param timer The timer to be added. If it doesn't have an ID yet, it is
825 considered user-defined and will be assigned an ID.
826 \return \c B_OK, if the timer was added successfully, another error code
827 otherwise.
829 status_t
830 Team::AddUserTimer(UserTimer* timer)
832 // don't allow addition of timers when already shutting the team down
833 if (state >= TEAM_STATE_SHUTDOWN)
834 return B_BAD_TEAM_ID;
836 // If the timer is user-defined, check timer limit and increment
837 // user-defined count.
838 if (timer->ID() < 0 && !CheckAddUserDefinedTimer())
839 return EAGAIN;
841 fUserTimers.AddTimer(timer);
843 return B_OK;
847 /*! Removes the given user timer from the team.
849 The caller must hold the team's lock.
851 \param timer The timer to be removed.
854 void
855 Team::RemoveUserTimer(UserTimer* timer)
857 fUserTimers.RemoveTimer(timer);
859 if (timer->ID() >= USER_TIMER_FIRST_USER_DEFINED_ID)
860 UserDefinedTimersRemoved(1);
864 /*! Deletes all (or all user-defined) user timers of the team.
866 Timer's belonging to the team's threads are not affected.
867 The caller must hold the team's lock.
869 \param userDefinedOnly If \c true, only the user-defined timers are deleted,
870 otherwise all timers are deleted.
872 void
873 Team::DeleteUserTimers(bool userDefinedOnly)
875 int32 count = fUserTimers.DeleteTimers(userDefinedOnly);
876 UserDefinedTimersRemoved(count);
880 /*! If not at the limit yet, increments the team's user-defined timer count.
881 \return \c true, if the limit wasn't reached yet, \c false otherwise.
883 bool
884 Team::CheckAddUserDefinedTimer()
886 int32 oldCount = atomic_add(&fUserDefinedTimerCount, 1);
887 if (oldCount >= MAX_USER_TIMERS_PER_TEAM) {
888 atomic_add(&fUserDefinedTimerCount, -1);
889 return false;
892 return true;
896 /*! Subtracts the given count for the team's user-defined timer count.
897 \param count The count to subtract.
899 void
900 Team::UserDefinedTimersRemoved(int32 count)
902 atomic_add(&fUserDefinedTimerCount, -count);
906 void
907 Team::DeactivateCPUTimeUserTimers()
909 while (TeamTimeUserTimer* timer = fCPUTimeUserTimers.Head())
910 timer->Deactivate();
912 while (TeamUserTimeUserTimer* timer = fUserTimeUserTimers.Head())
913 timer->Deactivate();
917 /*! Returns the team's current total CPU time (kernel + user + offset).
919 The caller must hold \c time_lock.
921 \param ignoreCurrentRun If \c true and the current thread is one team's
922 threads, don't add the time since the last time \c last_time was
923 updated. Should be used in "thread unscheduled" scheduler callbacks,
924 since although the thread is still running at that time, its time has
925 already been stopped.
926 \return The team's current total CPU time.
928 bigtime_t
929 Team::CPUTime(bool ignoreCurrentRun, Thread* lockedThread) const
931 bigtime_t time = cpu_clock_offset + dead_threads_kernel_time
932 + dead_threads_user_time;
934 Thread* currentThread = thread_get_current_thread();
935 bigtime_t now = system_time();
937 for (Thread* thread = thread_list; thread != NULL;
938 thread = thread->team_next) {
939 bool alreadyLocked = thread == lockedThread;
940 SpinLocker threadTimeLocker(thread->time_lock, alreadyLocked);
941 time += thread->kernel_time + thread->user_time;
943 if (thread->last_time != 0) {
944 if (!ignoreCurrentRun || thread != currentThread)
945 time += now - thread->last_time;
948 if (alreadyLocked)
949 threadTimeLocker.Detach();
952 return time;
956 /*! Returns the team's current user CPU time.
958 The caller must hold \c time_lock.
960 \return The team's current user CPU time.
962 bigtime_t
963 Team::UserCPUTime() const
965 bigtime_t time = dead_threads_user_time;
967 bigtime_t now = system_time();
969 for (Thread* thread = thread_list; thread != NULL;
970 thread = thread->team_next) {
971 SpinLocker threadTimeLocker(thread->time_lock);
972 time += thread->user_time;
974 if (thread->last_time != 0 && !thread->in_kernel)
975 time += now - thread->last_time;
978 return time;
982 // #pragma mark - ProcessGroup
985 ProcessGroup::ProcessGroup(pid_t id)
987 id(id),
988 teams(NULL),
989 fSession(NULL),
990 fInOrphanedCheckList(false)
992 char lockName[32];
993 snprintf(lockName, sizeof(lockName), "Group:%" B_PRId32, id);
994 mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
998 ProcessGroup::~ProcessGroup()
1000 TRACE(("ProcessGroup::~ProcessGroup(): id = %" B_PRId32 "\n", id));
1002 // If the group is in the orphaned check list, remove it.
1003 MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
1005 if (fInOrphanedCheckList)
1006 sOrphanedCheckProcessGroups.Remove(this);
1008 orphanedCheckLocker.Unlock();
1010 // remove group from the hash table and from the session
1011 if (fSession != NULL) {
1012 InterruptsSpinLocker groupHashLocker(sGroupHashLock);
1013 sGroupHash.RemoveUnchecked(this);
1014 groupHashLocker.Unlock();
1016 fSession->ReleaseReference();
1019 mutex_destroy(&fLock);
1023 /*static*/ ProcessGroup*
1024 ProcessGroup::Get(pid_t id)
1026 InterruptsSpinLocker groupHashLocker(sGroupHashLock);
1027 ProcessGroup* group = sGroupHash.Lookup(id);
1028 if (group != NULL)
1029 group->AcquireReference();
1030 return group;
1034 /*! Adds the group the given session and makes it publicly accessible.
1035 The caller must not hold the process group hash lock.
1037 void
1038 ProcessGroup::Publish(ProcessSession* session)
1040 InterruptsSpinLocker groupHashLocker(sGroupHashLock);
1041 PublishLocked(session);
1045 /*! Adds the group to the given session and makes it publicly accessible.
1046 The caller must hold the process group hash lock.
1048 void
1049 ProcessGroup::PublishLocked(ProcessSession* session)
1051 ASSERT(sGroupHash.Lookup(this->id) == NULL);
1053 fSession = session;
1054 fSession->AcquireReference();
1056 sGroupHash.InsertUnchecked(this);
1060 /*! Checks whether the process group is orphaned.
1061 The caller must hold the group's lock.
1062 \return \c true, if the group is orphaned, \c false otherwise.
1064 bool
1065 ProcessGroup::IsOrphaned() const
1067 // Orphaned Process Group: "A process group in which the parent of every
1068 // member is either itself a member of the group or is not a member of the
1069 // group's session." (Open Group Base Specs Issue 7)
1070 bool orphaned = true;
1072 Team* team = teams;
1073 while (orphaned && team != NULL) {
1074 team->LockTeamAndParent(false);
1076 Team* parent = team->parent;
1077 if (parent != NULL && parent->group_id != id
1078 && parent->session_id == fSession->id) {
1079 orphaned = false;
1082 team->UnlockTeamAndParent();
1084 team = team->group_next;
1087 return orphaned;
1091 void
1092 ProcessGroup::ScheduleOrphanedCheck()
1094 MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
1096 if (!fInOrphanedCheckList) {
1097 sOrphanedCheckProcessGroups.Add(this);
1098 fInOrphanedCheckList = true;
1103 void
1104 ProcessGroup::UnsetOrphanedCheck()
1106 fInOrphanedCheckList = false;
1110 // #pragma mark - ProcessSession
1113 ProcessSession::ProcessSession(pid_t id)
1115 id(id),
1116 controlling_tty(-1),
1117 foreground_group(-1)
1119 char lockName[32];
1120 snprintf(lockName, sizeof(lockName), "Session:%" B_PRId32, id);
1121 mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
1125 ProcessSession::~ProcessSession()
1127 mutex_destroy(&fLock);
1131 // #pragma mark - KDL functions
1134 static void
1135 _dump_team_info(Team* team)
1137 kprintf("TEAM: %p\n", team);
1138 kprintf("id: %" B_PRId32 " (%#" B_PRIx32 ")\n", team->id,
1139 team->id);
1140 kprintf("serial_number: %" B_PRId64 "\n", team->serial_number);
1141 kprintf("name: '%s'\n", team->Name());
1142 kprintf("args: '%s'\n", team->Args());
1143 kprintf("hash_next: %p\n", team->hash_next);
1144 kprintf("parent: %p", team->parent);
1145 if (team->parent != NULL) {
1146 kprintf(" (id = %" B_PRId32 ")\n", team->parent->id);
1147 } else
1148 kprintf("\n");
1150 kprintf("children: %p\n", team->children);
1151 kprintf("num_threads: %d\n", team->num_threads);
1152 kprintf("state: %d\n", team->state);
1153 kprintf("flags: 0x%" B_PRIx32 "\n", team->flags);
1154 kprintf("io_context: %p\n", team->io_context);
1155 if (team->address_space)
1156 kprintf("address_space: %p\n", team->address_space);
1157 kprintf("user data: %p (area %" B_PRId32 ")\n",
1158 (void*)team->user_data, team->user_data_area);
1159 kprintf("free user thread: %p\n", team->free_user_threads);
1160 kprintf("main_thread: %p\n", team->main_thread);
1161 kprintf("thread_list: %p\n", team->thread_list);
1162 kprintf("group_id: %" B_PRId32 "\n", team->group_id);
1163 kprintf("session_id: %" B_PRId32 "\n", team->session_id);
1167 static int
1168 dump_team_info(int argc, char** argv)
1170 ulong arg;
1171 bool found = false;
1173 if (argc < 2) {
1174 Thread* thread = thread_get_current_thread();
1175 if (thread != NULL && thread->team != NULL)
1176 _dump_team_info(thread->team);
1177 else
1178 kprintf("No current team!\n");
1179 return 0;
1182 arg = strtoul(argv[1], NULL, 0);
1183 if (IS_KERNEL_ADDRESS(arg)) {
1184 // semi-hack
1185 _dump_team_info((Team*)arg);
1186 return 0;
1189 // walk through the thread list, trying to match name or id
1190 for (TeamTable::Iterator it = sTeamHash.GetIterator();
1191 Team* team = it.Next();) {
1192 if ((team->Name() && strcmp(argv[1], team->Name()) == 0)
1193 || team->id == (team_id)arg) {
1194 _dump_team_info(team);
1195 found = true;
1196 break;
1200 if (!found)
1201 kprintf("team \"%s\" (%" B_PRId32 ") doesn't exist!\n", argv[1], (team_id)arg);
1202 return 0;
1206 static int
1207 dump_teams(int argc, char** argv)
1209 kprintf("%-*s id %-*s name\n", B_PRINTF_POINTER_WIDTH, "team",
1210 B_PRINTF_POINTER_WIDTH, "parent");
1212 for (TeamTable::Iterator it = sTeamHash.GetIterator();
1213 Team* team = it.Next();) {
1214 kprintf("%p%7" B_PRId32 " %p %s\n", team, team->id, team->parent, team->Name());
1217 return 0;
1221 // #pragma mark - Private functions
1224 /*! Inserts team \a team into the child list of team \a parent.
1226 The caller must hold the lock of both \a parent and \a team.
1228 \param parent The parent team.
1229 \param team The team to be inserted into \a parent's child list.
1231 static void
1232 insert_team_into_parent(Team* parent, Team* team)
1234 ASSERT(parent != NULL);
1236 team->siblings_next = parent->children;
1237 parent->children = team;
1238 team->parent = parent;
1242 /*! Removes team \a team from the child list of team \a parent.
1244 The caller must hold the lock of both \a parent and \a team.
1246 \param parent The parent team.
1247 \param team The team to be removed from \a parent's child list.
1249 static void
1250 remove_team_from_parent(Team* parent, Team* team)
1252 Team* child;
1253 Team* last = NULL;
1255 for (child = parent->children; child != NULL;
1256 child = child->siblings_next) {
1257 if (child == team) {
1258 if (last == NULL)
1259 parent->children = child->siblings_next;
1260 else
1261 last->siblings_next = child->siblings_next;
1263 team->parent = NULL;
1264 break;
1266 last = child;
1271 /*! Returns whether the given team is a session leader.
1272 The caller must hold the team's lock or its process group's lock.
1274 static bool
1275 is_session_leader(Team* team)
1277 return team->session_id == team->id;
1281 /*! Returns whether the given team is a process group leader.
1282 The caller must hold the team's lock or its process group's lock.
1284 static bool
1285 is_process_group_leader(Team* team)
1287 return team->group_id == team->id;
1291 /*! Inserts the given team into the given process group.
1292 The caller must hold the process group's lock, the team's lock, and the
1293 team's parent's lock.
1295 static void
1296 insert_team_into_group(ProcessGroup* group, Team* team)
1298 team->group = group;
1299 team->group_id = group->id;
1300 team->session_id = group->Session()->id;
1302 team->group_next = group->teams;
1303 group->teams = team;
1304 group->AcquireReference();
1308 /*! Removes the given team from its process group.
1310 The caller must hold the process group's lock, the team's lock, and the
1311 team's parent's lock. Interrupts must be enabled.
1313 \param team The team that'll be removed from its process group.
1315 static void
1316 remove_team_from_group(Team* team)
1318 ProcessGroup* group = team->group;
1319 Team* current;
1320 Team* last = NULL;
1322 // the team must be in a process group to let this function have any effect
1323 if (group == NULL)
1324 return;
1326 for (current = group->teams; current != NULL;
1327 current = current->group_next) {
1328 if (current == team) {
1329 if (last == NULL)
1330 group->teams = current->group_next;
1331 else
1332 last->group_next = current->group_next;
1334 team->group = NULL;
1335 break;
1337 last = current;
1340 team->group = NULL;
1341 team->group_next = NULL;
1343 group->ReleaseReference();
1347 static status_t
1348 create_team_user_data(Team* team, void* exactAddress = NULL)
1350 void* address;
1351 uint32 addressSpec;
1353 if (exactAddress != NULL) {
1354 address = exactAddress;
1355 addressSpec = B_EXACT_ADDRESS;
1356 } else {
1357 address = (void*)KERNEL_USER_DATA_BASE;
1358 addressSpec = B_RANDOMIZED_BASE_ADDRESS;
1361 status_t result = vm_reserve_address_range(team->id, &address, addressSpec,
1362 kTeamUserDataReservedSize, RESERVED_AVOID_BASE);
1364 virtual_address_restrictions virtualRestrictions = {};
1365 if (result == B_OK || exactAddress != NULL) {
1366 if (exactAddress != NULL)
1367 virtualRestrictions.address = exactAddress;
1368 else
1369 virtualRestrictions.address = address;
1370 virtualRestrictions.address_specification = B_EXACT_ADDRESS;
1371 } else {
1372 virtualRestrictions.address = (void*)KERNEL_USER_DATA_BASE;
1373 virtualRestrictions.address_specification = B_RANDOMIZED_BASE_ADDRESS;
1376 physical_address_restrictions physicalRestrictions = {};
1377 team->user_data_area = create_area_etc(team->id, "user area",
1378 kTeamUserDataInitialSize, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA, 0, 0,
1379 &virtualRestrictions, &physicalRestrictions, &address);
1380 if (team->user_data_area < 0)
1381 return team->user_data_area;
1383 team->user_data = (addr_t)address;
1384 team->used_user_data = 0;
1385 team->user_data_size = kTeamUserDataInitialSize;
1386 team->free_user_threads = NULL;
1388 return B_OK;
1392 static void
1393 delete_team_user_data(Team* team)
1395 if (team->user_data_area >= 0) {
1396 vm_delete_area(team->id, team->user_data_area, true);
1397 vm_unreserve_address_range(team->id, (void*)team->user_data,
1398 kTeamUserDataReservedSize);
1400 team->user_data = 0;
1401 team->used_user_data = 0;
1402 team->user_data_size = 0;
1403 team->user_data_area = -1;
1404 while (free_user_thread* entry = team->free_user_threads) {
1405 team->free_user_threads = entry->next;
1406 free(entry);
1412 static status_t
1413 copy_user_process_args(const char* const* userFlatArgs, size_t flatArgsSize,
1414 int32 argCount, int32 envCount, char**& _flatArgs)
1416 if (argCount < 0 || envCount < 0)
1417 return B_BAD_VALUE;
1419 if (flatArgsSize > MAX_PROCESS_ARGS_SIZE)
1420 return B_TOO_MANY_ARGS;
1421 if ((argCount + envCount + 2) * sizeof(char*) > flatArgsSize)
1422 return B_BAD_VALUE;
1424 if (!IS_USER_ADDRESS(userFlatArgs))
1425 return B_BAD_ADDRESS;
1427 // allocate kernel memory
1428 char** flatArgs = (char**)malloc(_ALIGN(flatArgsSize));
1429 if (flatArgs == NULL)
1430 return B_NO_MEMORY;
1432 if (user_memcpy(flatArgs, userFlatArgs, flatArgsSize) != B_OK) {
1433 free(flatArgs);
1434 return B_BAD_ADDRESS;
1437 // check and relocate the array
1438 status_t error = B_OK;
1439 const char* stringBase = (char*)flatArgs + argCount + envCount + 2;
1440 const char* stringEnd = (char*)flatArgs + flatArgsSize;
1441 for (int32 i = 0; i < argCount + envCount + 2; i++) {
1442 if (i == argCount || i == argCount + envCount + 1) {
1443 // check array null termination
1444 if (flatArgs[i] != NULL) {
1445 error = B_BAD_VALUE;
1446 break;
1448 } else {
1449 // check string
1450 char* arg = (char*)flatArgs + (flatArgs[i] - (char*)userFlatArgs);
1451 size_t maxLen = stringEnd - arg;
1452 if (arg < stringBase || arg >= stringEnd
1453 || strnlen(arg, maxLen) == maxLen) {
1454 error = B_BAD_VALUE;
1455 break;
1458 flatArgs[i] = arg;
1462 if (error == B_OK)
1463 _flatArgs = flatArgs;
1464 else
1465 free(flatArgs);
1467 return error;
1471 static void
1472 free_team_arg(struct team_arg* teamArg)
1474 if (teamArg != NULL) {
1475 free(teamArg->flat_args);
1476 free(teamArg->path);
1477 free(teamArg);
1482 static status_t
1483 create_team_arg(struct team_arg** _teamArg, const char* path, char** flatArgs,
1484 size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask,
1485 port_id port, uint32 token)
1487 struct team_arg* teamArg = (struct team_arg*)malloc(sizeof(team_arg));
1488 if (teamArg == NULL)
1489 return B_NO_MEMORY;
1491 teamArg->path = strdup(path);
1492 if (teamArg->path == NULL) {
1493 free(teamArg);
1494 return B_NO_MEMORY;
1497 // copy the args over
1498 teamArg->flat_args = flatArgs;
1499 teamArg->flat_args_size = flatArgsSize;
1500 teamArg->arg_count = argCount;
1501 teamArg->env_count = envCount;
1502 teamArg->flags = 0;
1503 teamArg->umask = umask;
1504 teamArg->error_port = port;
1505 teamArg->error_token = token;
1507 // determine the flags from the environment
1508 const char* const* env = flatArgs + argCount + 1;
1509 for (int32 i = 0; i < envCount; i++) {
1510 if (strcmp(env[i], "DISABLE_ASLR=1") == 0) {
1511 teamArg->flags |= TEAM_ARGS_FLAG_NO_ASLR;
1512 break;
1516 *_teamArg = teamArg;
1517 return B_OK;
1521 static status_t
1522 team_create_thread_start_internal(void* args)
1524 status_t err;
1525 Thread* thread;
1526 Team* team;
1527 struct team_arg* teamArgs = (struct team_arg*)args;
1528 const char* path;
1529 addr_t entry;
1530 char** userArgs;
1531 char** userEnv;
1532 struct user_space_program_args* programArgs;
1533 uint32 argCount, envCount;
1535 thread = thread_get_current_thread();
1536 team = thread->team;
1537 cache_node_launched(teamArgs->arg_count, teamArgs->flat_args);
1539 TRACE(("team_create_thread_start: entry thread %" B_PRId32 "\n",
1540 thread->id));
1542 // Main stack area layout is currently as follows (starting from 0):
1544 // size | usage
1545 // ---------------------------------+--------------------------------
1546 // USER_MAIN_THREAD_STACK_SIZE | actual stack
1547 // TLS_SIZE | TLS data
1548 // sizeof(user_space_program_args) | argument structure for the runtime
1549 // | loader
1550 // flat arguments size | flat process arguments and environment
1552 // TODO: ENV_SIZE is a) limited, and b) not used after libroot copied it to
1553 // the heap
1554 // TODO: we could reserve the whole USER_STACK_REGION upfront...
1556 argCount = teamArgs->arg_count;
1557 envCount = teamArgs->env_count;
1559 programArgs = (struct user_space_program_args*)(thread->user_stack_base
1560 + thread->user_stack_size + TLS_SIZE);
1562 userArgs = (char**)(programArgs + 1);
1563 userEnv = userArgs + argCount + 1;
1564 path = teamArgs->path;
1566 if (user_strlcpy(programArgs->program_path, path,
1567 sizeof(programArgs->program_path)) < B_OK
1568 || user_memcpy(&programArgs->arg_count, &argCount, sizeof(int32)) < B_OK
1569 || user_memcpy(&programArgs->args, &userArgs, sizeof(char**)) < B_OK
1570 || user_memcpy(&programArgs->env_count, &envCount, sizeof(int32)) < B_OK
1571 || user_memcpy(&programArgs->env, &userEnv, sizeof(char**)) < B_OK
1572 || user_memcpy(&programArgs->error_port, &teamArgs->error_port,
1573 sizeof(port_id)) < B_OK
1574 || user_memcpy(&programArgs->error_token, &teamArgs->error_token,
1575 sizeof(uint32)) < B_OK
1576 || user_memcpy(&programArgs->umask, &teamArgs->umask, sizeof(mode_t)) < B_OK
1577 || user_memcpy(userArgs, teamArgs->flat_args,
1578 teamArgs->flat_args_size) < B_OK) {
1579 // the team deletion process will clean this mess
1580 free_team_arg(teamArgs);
1581 return B_BAD_ADDRESS;
1584 TRACE(("team_create_thread_start: loading elf binary '%s'\n", path));
1586 // set team args and update state
1587 team->Lock();
1588 team->SetArgs(path, teamArgs->flat_args + 1, argCount - 1);
1589 team->state = TEAM_STATE_NORMAL;
1590 team->Unlock();
1592 free_team_arg(teamArgs);
1593 // the arguments are already on the user stack, we no longer need
1594 // them in this form
1596 // Clone commpage area
1597 area_id commPageArea = clone_commpage_area(team->id,
1598 &team->commpage_address);
1599 if (commPageArea < B_OK) {
1600 TRACE(("team_create_thread_start: clone_commpage_area() failed: %s\n",
1601 strerror(commPageArea)));
1602 return commPageArea;
1605 // Register commpage image
1606 image_id commPageImage = get_commpage_image();
1607 extended_image_info imageInfo;
1608 err = get_image_info(commPageImage, &imageInfo.basic_info);
1609 if (err != B_OK) {
1610 TRACE(("team_create_thread_start: get_image_info() failed: %s\n",
1611 strerror(err)));
1612 return err;
1614 imageInfo.basic_info.text = team->commpage_address;
1615 imageInfo.text_delta = (ssize_t)(addr_t)team->commpage_address;
1616 imageInfo.symbol_table = NULL;
1617 imageInfo.symbol_hash = NULL;
1618 imageInfo.string_table = NULL;
1619 image_id image = register_image(team, &imageInfo, sizeof(imageInfo));
1620 if (image < 0) {
1621 TRACE(("team_create_thread_start: register_image() failed: %s\n",
1622 strerror(image)));
1623 return image;
1626 // NOTE: Normally arch_thread_enter_userspace() never returns, that is
1627 // automatic variables with function scope will never be destroyed.
1629 // find runtime_loader path
1630 KPath runtimeLoaderPath;
1631 err = __find_directory(B_SYSTEM_DIRECTORY, gBootDevice, false,
1632 runtimeLoaderPath.LockBuffer(), runtimeLoaderPath.BufferSize());
1633 if (err < B_OK) {
1634 TRACE(("team_create_thread_start: find_directory() failed: %s\n",
1635 strerror(err)));
1636 return err;
1638 runtimeLoaderPath.UnlockBuffer();
1639 err = runtimeLoaderPath.Append("runtime_loader");
1641 if (err == B_OK) {
1642 err = elf_load_user_image(runtimeLoaderPath.Path(), team, 0,
1643 &entry);
1647 if (err < B_OK) {
1648 // Luckily, we don't have to clean up the mess we created - that's
1649 // done for us by the normal team deletion process
1650 TRACE(("team_create_thread_start: elf_load_user_image() failed: "
1651 "%s\n", strerror(err)));
1652 return err;
1655 TRACE(("team_create_thread_start: loaded elf. entry = %#lx\n", entry));
1657 // enter userspace -- returns only in case of error
1658 return thread_enter_userspace_new_team(thread, (addr_t)entry,
1659 programArgs, team->commpage_address);
1663 static status_t
1664 team_create_thread_start(void* args)
1666 team_create_thread_start_internal(args);
1667 team_init_exit_info_on_error(thread_get_current_thread()->team);
1668 thread_exit();
1669 // does not return
1670 return B_OK;
1674 static thread_id
1675 load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount,
1676 int32 envCount, int32 priority, team_id parentID, uint32 flags,
1677 port_id errorPort, uint32 errorToken)
1679 char** flatArgs = _flatArgs;
1680 thread_id thread;
1681 status_t status;
1682 struct team_arg* teamArgs;
1683 struct team_loading_info loadingInfo;
1684 io_context* parentIOContext = NULL;
1685 team_id teamID;
1686 bool teamLimitReached = false;
1688 if (flatArgs == NULL || argCount == 0)
1689 return B_BAD_VALUE;
1691 const char* path = flatArgs[0];
1693 TRACE(("load_image_internal: name '%s', args = %p, argCount = %" B_PRId32
1694 "\n", path, flatArgs, argCount));
1696 // cut the path from the main thread name
1697 const char* threadName = strrchr(path, '/');
1698 if (threadName != NULL)
1699 threadName++;
1700 else
1701 threadName = path;
1703 // create the main thread object
1704 Thread* mainThread;
1705 status = Thread::Create(threadName, mainThread);
1706 if (status != B_OK)
1707 return status;
1708 BReference<Thread> mainThreadReference(mainThread, true);
1710 // create team object
1711 Team* team = Team::Create(mainThread->id, path, false);
1712 if (team == NULL)
1713 return B_NO_MEMORY;
1714 BReference<Team> teamReference(team, true);
1716 if (flags & B_WAIT_TILL_LOADED) {
1717 loadingInfo.thread = thread_get_current_thread();
1718 loadingInfo.result = B_ERROR;
1719 loadingInfo.done = false;
1720 team->loading_info = &loadingInfo;
1723 // get the parent team
1724 Team* parent = Team::Get(parentID);
1725 if (parent == NULL)
1726 return B_BAD_TEAM_ID;
1727 BReference<Team> parentReference(parent, true);
1729 parent->LockTeamAndProcessGroup();
1730 team->Lock();
1732 // inherit the parent's user/group
1733 inherit_parent_user_and_group(team, parent);
1735 // get a reference to the parent's I/O context -- we need it to create ours
1736 parentIOContext = parent->io_context;
1737 vfs_get_io_context(parentIOContext);
1739 team->Unlock();
1740 parent->UnlockTeamAndProcessGroup();
1742 // check the executable's set-user/group-id permission
1743 update_set_id_user_and_group(team, path);
1745 status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, argCount,
1746 envCount, (mode_t)-1, errorPort, errorToken);
1747 if (status != B_OK)
1748 goto err1;
1750 _flatArgs = NULL;
1751 // args are owned by the team_arg structure now
1753 // create a new io_context for this team
1754 team->io_context = vfs_new_io_context(parentIOContext, true);
1755 if (!team->io_context) {
1756 status = B_NO_MEMORY;
1757 goto err2;
1760 // We don't need the parent's I/O context any longer.
1761 vfs_put_io_context(parentIOContext);
1762 parentIOContext = NULL;
1764 // remove any fds that have the CLOEXEC flag set (emulating BeOS behaviour)
1765 vfs_exec_io_context(team->io_context);
1767 // create an address space for this team
1768 status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false,
1769 &team->address_space);
1770 if (status != B_OK)
1771 goto err2;
1773 team->address_space->SetRandomizingEnabled(
1774 (teamArgs->flags & TEAM_ARGS_FLAG_NO_ASLR) == 0);
1776 // create the user data area
1777 status = create_team_user_data(team);
1778 if (status != B_OK)
1779 goto err4;
1781 // insert the team into its parent and the teams hash
1782 parent->LockTeamAndProcessGroup();
1783 team->Lock();
1786 InterruptsSpinLocker teamsLocker(sTeamHashLock);
1788 sTeamHash.Insert(team);
1789 teamLimitReached = sUsedTeams >= sMaxTeams;
1790 if (!teamLimitReached)
1791 sUsedTeams++;
1794 insert_team_into_parent(parent, team);
1795 insert_team_into_group(parent->group, team);
1797 team->Unlock();
1798 parent->UnlockTeamAndProcessGroup();
1800 // notify team listeners
1801 sNotificationService.Notify(TEAM_ADDED, team);
1803 if (teamLimitReached) {
1804 status = B_NO_MORE_TEAMS;
1805 goto err6;
1808 // In case we start the main thread, we shouldn't access the team object
1809 // afterwards, so cache the team's ID.
1810 teamID = team->id;
1812 // Create a kernel thread, but under the context of the new team
1813 // The new thread will take over ownership of teamArgs.
1815 ThreadCreationAttributes threadAttributes(team_create_thread_start,
1816 threadName, B_NORMAL_PRIORITY, teamArgs, teamID, mainThread);
1817 threadAttributes.additional_stack_size = sizeof(user_space_program_args)
1818 + teamArgs->flat_args_size;
1819 thread = thread_create_thread(threadAttributes, false);
1820 if (thread < 0) {
1821 status = thread;
1822 goto err6;
1826 // The team has been created successfully, so we keep the reference. Or
1827 // more precisely: It's owned by the team's main thread, now.
1828 teamReference.Detach();
1830 // wait for the loader of the new team to finish its work
1831 if ((flags & B_WAIT_TILL_LOADED) != 0) {
1832 if (mainThread != NULL) {
1833 // resume the team's main thread
1834 thread_continue(mainThread);
1837 // Now suspend ourselves until loading is finished. We will be woken
1838 // either by the thread, when it finished or aborted loading, or when
1839 // the team is going to die (e.g. is killed). In either case the one
1840 // setting `loadingInfo.done' is responsible for removing the info from
1841 // the team structure.
1842 while (!loadingInfo.done)
1843 thread_suspend();
1845 if (loadingInfo.result < B_OK)
1846 return loadingInfo.result;
1849 // notify the debugger
1850 user_debug_team_created(teamID);
1852 return thread;
1854 err6:
1855 // Remove the team structure from the process group, the parent team, and
1856 // the team hash table and delete the team structure.
1857 parent->LockTeamAndProcessGroup();
1858 team->Lock();
1860 remove_team_from_group(team);
1861 remove_team_from_parent(team->parent, team);
1863 team->Unlock();
1864 parent->UnlockTeamAndProcessGroup();
1867 InterruptsSpinLocker teamsLocker(sTeamHashLock);
1868 sTeamHash.Remove(team);
1869 if (!teamLimitReached)
1870 sUsedTeams--;
1873 sNotificationService.Notify(TEAM_REMOVED, team);
1875 delete_team_user_data(team);
1876 err4:
1877 team->address_space->Put();
1878 err2:
1879 free_team_arg(teamArgs);
1880 err1:
1881 if (parentIOContext != NULL)
1882 vfs_put_io_context(parentIOContext);
1884 return status;
1888 /*! Almost shuts down the current team and loads a new image into it.
1889 If successful, this function does not return and will takeover ownership of
1890 the arguments provided.
1891 This function may only be called in a userland team (caused by one of the
1892 exec*() syscalls).
1894 static status_t
1895 exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize,
1896 int32 argCount, int32 envCount, mode_t umask)
1898 // NOTE: Since this function normally doesn't return, don't use automatic
1899 // variables that need destruction in the function scope.
1900 char** flatArgs = _flatArgs;
1901 Team* team = thread_get_current_thread()->team;
1902 struct team_arg* teamArgs;
1903 const char* threadName;
1904 thread_id nubThreadID = -1;
1906 TRACE(("exec_team(path = \"%s\", argc = %" B_PRId32 ", envCount = %"
1907 B_PRId32 "): team %" B_PRId32 "\n", path, argCount, envCount,
1908 team->id));
1910 T(ExecTeam(path, argCount, flatArgs, envCount, flatArgs + argCount + 1));
1912 // switching the kernel at run time is probably not a good idea :)
1913 if (team == team_get_kernel_team())
1914 return B_NOT_ALLOWED;
1916 // we currently need to be single threaded here
1917 // TODO: maybe we should just kill all other threads and
1918 // make the current thread the team's main thread?
1919 Thread* currentThread = thread_get_current_thread();
1920 if (currentThread != team->main_thread)
1921 return B_NOT_ALLOWED;
1923 // The debug nub thread, a pure kernel thread, is allowed to survive.
1924 // We iterate through the thread list to make sure that there's no other
1925 // thread.
1926 TeamLocker teamLocker(team);
1927 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
1929 if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED)
1930 nubThreadID = team->debug_info.nub_thread;
1932 debugInfoLocker.Unlock();
1934 for (Thread* thread = team->thread_list; thread != NULL;
1935 thread = thread->team_next) {
1936 if (thread != team->main_thread && thread->id != nubThreadID)
1937 return B_NOT_ALLOWED;
1940 team->DeleteUserTimers(true);
1941 team->ResetSignalsOnExec();
1943 teamLocker.Unlock();
1945 status_t status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize,
1946 argCount, envCount, umask, -1, 0);
1947 if (status != B_OK)
1948 return status;
1950 _flatArgs = NULL;
1951 // args are owned by the team_arg structure now
1953 // TODO: remove team resources if there are any left
1954 // thread_atkernel_exit() might not be called at all
1956 thread_reset_for_exec();
1958 user_debug_prepare_for_exec();
1960 delete_team_user_data(team);
1961 vm_delete_areas(team->address_space, false);
1962 xsi_sem_undo(team);
1963 delete_owned_ports(team);
1964 sem_delete_owned_sems(team);
1965 remove_images(team);
1966 vfs_exec_io_context(team->io_context);
1967 delete_realtime_sem_context(team->realtime_sem_context);
1968 team->realtime_sem_context = NULL;
1970 // update ASLR
1971 team->address_space->SetRandomizingEnabled(
1972 (teamArgs->flags & TEAM_ARGS_FLAG_NO_ASLR) == 0);
1974 status = create_team_user_data(team);
1975 if (status != B_OK) {
1976 // creating the user data failed -- we're toast
1977 free_team_arg(teamArgs);
1978 exit_thread(status);
1979 return status;
1982 user_debug_finish_after_exec();
1984 // rename the team
1986 team->Lock();
1987 team->SetName(path);
1988 team->Unlock();
1990 // cut the path from the team name and rename the main thread, too
1991 threadName = strrchr(path, '/');
1992 if (threadName != NULL)
1993 threadName++;
1994 else
1995 threadName = path;
1996 rename_thread(thread_get_current_thread_id(), threadName);
1998 atomic_or(&team->flags, TEAM_FLAG_EXEC_DONE);
2000 // Update user/group according to the executable's set-user/group-id
2001 // permission.
2002 update_set_id_user_and_group(team, path);
2004 user_debug_team_exec();
2006 // notify team listeners
2007 sNotificationService.Notify(TEAM_EXEC, team);
2009 // get a user thread for the thread
2010 user_thread* userThread = team_allocate_user_thread(team);
2011 // cannot fail (the allocation for the team would have failed already)
2012 ThreadLocker currentThreadLocker(currentThread);
2013 currentThread->user_thread = userThread;
2014 currentThreadLocker.Unlock();
2016 // create the user stack for the thread
2017 status = thread_create_user_stack(currentThread->team, currentThread, NULL,
2018 0, sizeof(user_space_program_args) + teamArgs->flat_args_size);
2019 if (status == B_OK) {
2020 // prepare the stack, load the runtime loader, and enter userspace
2021 team_create_thread_start(teamArgs);
2022 // does never return
2023 } else
2024 free_team_arg(teamArgs);
2026 // Sorry, we have to kill ourselves, there is no way out anymore
2027 // (without any areas left and all that).
2028 exit_thread(status);
2030 // We return a status here since the signal that is sent by the
2031 // call above is not immediately handled.
2032 return B_ERROR;
2036 static thread_id
2037 fork_team(void)
2039 Thread* parentThread = thread_get_current_thread();
2040 Team* parentTeam = parentThread->team;
2041 Team* team;
2042 arch_fork_arg* forkArgs;
2043 struct area_info info;
2044 thread_id threadID;
2045 status_t status;
2046 ssize_t areaCookie;
2047 bool teamLimitReached = false;
2049 TRACE(("fork_team(): team %" B_PRId32 "\n", parentTeam->id));
2051 if (parentTeam == team_get_kernel_team())
2052 return B_NOT_ALLOWED;
2054 // create a new team
2055 // TODO: this is very similar to load_image_internal() - maybe we can do
2056 // something about it :)
2058 // create the main thread object
2059 Thread* thread;
2060 status = Thread::Create(parentThread->name, thread);
2061 if (status != B_OK)
2062 return status;
2063 BReference<Thread> threadReference(thread, true);
2065 // create the team object
2066 team = Team::Create(thread->id, NULL, false);
2067 if (team == NULL)
2068 return B_NO_MEMORY;
2070 parentTeam->LockTeamAndProcessGroup();
2071 team->Lock();
2073 team->SetName(parentTeam->Name());
2074 team->SetArgs(parentTeam->Args());
2076 team->commpage_address = parentTeam->commpage_address;
2078 // Inherit the parent's user/group.
2079 inherit_parent_user_and_group(team, parentTeam);
2081 // inherit signal handlers
2082 team->InheritSignalActions(parentTeam);
2084 team->Unlock();
2085 parentTeam->UnlockTeamAndProcessGroup();
2087 // inherit some team debug flags
2088 team->debug_info.flags |= atomic_get(&parentTeam->debug_info.flags)
2089 & B_TEAM_DEBUG_INHERITED_FLAGS;
2091 forkArgs = (arch_fork_arg*)malloc(sizeof(arch_fork_arg));
2092 if (forkArgs == NULL) {
2093 status = B_NO_MEMORY;
2094 goto err1;
2097 // create a new io_context for this team
2098 team->io_context = vfs_new_io_context(parentTeam->io_context, false);
2099 if (!team->io_context) {
2100 status = B_NO_MEMORY;
2101 goto err2;
2104 // duplicate the realtime sem context
2105 if (parentTeam->realtime_sem_context) {
2106 team->realtime_sem_context = clone_realtime_sem_context(
2107 parentTeam->realtime_sem_context);
2108 if (team->realtime_sem_context == NULL) {
2109 status = B_NO_MEMORY;
2110 goto err2;
2114 // create an address space for this team
2115 status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false,
2116 &team->address_space);
2117 if (status < B_OK)
2118 goto err3;
2120 // copy all areas of the team
2121 // TODO: should be able to handle stack areas differently (ie. don't have
2122 // them copy-on-write)
2124 areaCookie = 0;
2125 while (get_next_area_info(B_CURRENT_TEAM, &areaCookie, &info) == B_OK) {
2126 if (info.area == parentTeam->user_data_area) {
2127 // don't clone the user area; just create a new one
2128 status = create_team_user_data(team, info.address);
2129 if (status != B_OK)
2130 break;
2132 thread->user_thread = team_allocate_user_thread(team);
2133 } else {
2134 void* address;
2135 area_id area = vm_copy_area(team->address_space->ID(), info.name,
2136 &address, B_CLONE_ADDRESS, info.protection, info.area);
2137 if (area < B_OK) {
2138 status = area;
2139 break;
2142 if (info.area == parentThread->user_stack_area)
2143 thread->user_stack_area = area;
2147 if (status < B_OK)
2148 goto err4;
2150 if (thread->user_thread == NULL) {
2151 #if KDEBUG
2152 panic("user data area not found, parent area is %" B_PRId32,
2153 parentTeam->user_data_area);
2154 #endif
2155 status = B_ERROR;
2156 goto err4;
2159 thread->user_stack_base = parentThread->user_stack_base;
2160 thread->user_stack_size = parentThread->user_stack_size;
2161 thread->user_local_storage = parentThread->user_local_storage;
2162 thread->sig_block_mask = parentThread->sig_block_mask;
2163 thread->signal_stack_base = parentThread->signal_stack_base;
2164 thread->signal_stack_size = parentThread->signal_stack_size;
2165 thread->signal_stack_enabled = parentThread->signal_stack_enabled;
2167 arch_store_fork_frame(forkArgs);
2169 // copy image list
2170 if (copy_images(parentTeam->id, team) != B_OK)
2171 goto err5;
2173 // insert the team into its parent and the teams hash
2174 parentTeam->LockTeamAndProcessGroup();
2175 team->Lock();
2178 InterruptsSpinLocker teamsLocker(sTeamHashLock);
2180 sTeamHash.Insert(team);
2181 teamLimitReached = sUsedTeams >= sMaxTeams;
2182 if (!teamLimitReached)
2183 sUsedTeams++;
2186 insert_team_into_parent(parentTeam, team);
2187 insert_team_into_group(parentTeam->group, team);
2189 team->Unlock();
2190 parentTeam->UnlockTeamAndProcessGroup();
2192 // notify team listeners
2193 sNotificationService.Notify(TEAM_ADDED, team);
2195 if (teamLimitReached) {
2196 status = B_NO_MORE_TEAMS;
2197 goto err6;
2200 // create the main thread
2202 ThreadCreationAttributes threadCreationAttributes(NULL,
2203 parentThread->name, parentThread->priority, NULL, team->id, thread);
2204 threadCreationAttributes.forkArgs = forkArgs;
2205 threadCreationAttributes.flags |= THREAD_CREATION_FLAG_DEFER_SIGNALS;
2206 threadID = thread_create_thread(threadCreationAttributes, false);
2207 if (threadID < 0) {
2208 status = threadID;
2209 goto err6;
2213 // notify the debugger
2214 user_debug_team_created(team->id);
2216 T(TeamForked(threadID));
2218 resume_thread(threadID);
2219 return threadID;
2221 err6:
2222 // Remove the team structure from the process group, the parent team, and
2223 // the team hash table and delete the team structure.
2224 parentTeam->LockTeamAndProcessGroup();
2225 team->Lock();
2227 remove_team_from_group(team);
2228 remove_team_from_parent(team->parent, team);
2230 team->Unlock();
2231 parentTeam->UnlockTeamAndProcessGroup();
2234 InterruptsSpinLocker teamsLocker(sTeamHashLock);
2235 sTeamHash.Remove(team);
2236 if (!teamLimitReached)
2237 sUsedTeams--;
2240 sNotificationService.Notify(TEAM_REMOVED, team);
2241 err5:
2242 remove_images(team);
2243 err4:
2244 team->address_space->RemoveAndPut();
2245 err3:
2246 delete_realtime_sem_context(team->realtime_sem_context);
2247 err2:
2248 free(forkArgs);
2249 err1:
2250 team->ReleaseReference();
2252 return status;
2256 /*! Returns if the specified team \a parent has any children belonging to the
2257 process group with the specified ID \a groupID.
2258 The caller must hold \a parent's lock.
2260 static bool
2261 has_children_in_group(Team* parent, pid_t groupID)
2263 for (Team* child = parent->children; child != NULL;
2264 child = child->siblings_next) {
2265 TeamLocker childLocker(child);
2266 if (child->group_id == groupID)
2267 return true;
2270 return false;
2274 /*! Returns the first job control entry from \a children, which matches \a id.
2275 \a id can be:
2276 - \code > 0 \endcode: Matching an entry with that team ID.
2277 - \code == -1 \endcode: Matching any entry.
2278 - \code < -1 \endcode: Matching any entry with a process group ID of \c -id.
2279 \c 0 is an invalid value for \a id.
2281 The caller must hold the lock of the team that \a children belongs to.
2283 \param children The job control entry list to check.
2284 \param id The match criterion.
2285 \return The first matching entry or \c NULL, if none matches.
2287 static job_control_entry*
2288 get_job_control_entry(team_job_control_children& children, pid_t id)
2290 for (JobControlEntryList::Iterator it = children.entries.GetIterator();
2291 job_control_entry* entry = it.Next();) {
2293 if (id > 0) {
2294 if (entry->thread == id)
2295 return entry;
2296 } else if (id == -1) {
2297 return entry;
2298 } else {
2299 pid_t processGroup
2300 = (entry->team ? entry->team->group_id : entry->group_id);
2301 if (processGroup == -id)
2302 return entry;
2306 return NULL;
2310 /*! Returns the first job control entry from one of team's dead, continued, or
2311 stopped children which matches \a id.
2312 \a id can be:
2313 - \code > 0 \endcode: Matching an entry with that team ID.
2314 - \code == -1 \endcode: Matching any entry.
2315 - \code < -1 \endcode: Matching any entry with a process group ID of \c -id.
2316 \c 0 is an invalid value for \a id.
2318 The caller must hold \a team's lock.
2320 \param team The team whose dead, stopped, and continued child lists shall be
2321 checked.
2322 \param id The match criterion.
2323 \param flags Specifies which children shall be considered. Dead children
2324 always are. Stopped children are considered when \a flags is ORed
2325 bitwise with \c WUNTRACED, continued children when \a flags is ORed
2326 bitwise with \c WCONTINUED.
2327 \return The first matching entry or \c NULL, if none matches.
2329 static job_control_entry*
2330 get_job_control_entry(Team* team, pid_t id, uint32 flags)
2332 job_control_entry* entry = get_job_control_entry(team->dead_children, id);
2334 if (entry == NULL && (flags & WCONTINUED) != 0)
2335 entry = get_job_control_entry(team->continued_children, id);
2337 if (entry == NULL && (flags & WUNTRACED) != 0)
2338 entry = get_job_control_entry(team->stopped_children, id);
2340 return entry;
2344 job_control_entry::job_control_entry()
2346 has_group_ref(false)
2351 job_control_entry::~job_control_entry()
2353 if (has_group_ref) {
2354 InterruptsSpinLocker groupHashLocker(sGroupHashLock);
2356 ProcessGroup* group = sGroupHash.Lookup(group_id);
2357 if (group == NULL) {
2358 panic("job_control_entry::~job_control_entry(): unknown group "
2359 "ID: %" B_PRId32, group_id);
2360 return;
2363 groupHashLocker.Unlock();
2365 group->ReleaseReference();
2370 /*! Invoked when the owning team is dying, initializing the entry according to
2371 the dead state.
2373 The caller must hold the owning team's lock and the scheduler lock.
2375 void
2376 job_control_entry::InitDeadState()
2378 if (team != NULL) {
2379 ASSERT(team->exit.initialized);
2381 group_id = team->group_id;
2382 team->group->AcquireReference();
2383 has_group_ref = true;
2385 thread = team->id;
2386 status = team->exit.status;
2387 reason = team->exit.reason;
2388 signal = team->exit.signal;
2389 signaling_user = team->exit.signaling_user;
2390 user_time = team->dead_threads_user_time
2391 + team->dead_children.user_time;
2392 kernel_time = team->dead_threads_kernel_time
2393 + team->dead_children.kernel_time;
2395 team = NULL;
2400 job_control_entry&
2401 job_control_entry::operator=(const job_control_entry& other)
2403 state = other.state;
2404 thread = other.thread;
2405 signal = other.signal;
2406 has_group_ref = false;
2407 signaling_user = other.signaling_user;
2408 team = other.team;
2409 group_id = other.group_id;
2410 status = other.status;
2411 reason = other.reason;
2412 user_time = other.user_time;
2413 kernel_time = other.kernel_time;
2415 return *this;
2419 /*! This is the kernel backend for waitid().
2421 static thread_id
2422 wait_for_child(pid_t child, uint32 flags, siginfo_t& _info,
2423 team_usage_info& _usage_info)
2425 Thread* thread = thread_get_current_thread();
2426 Team* team = thread->team;
2427 struct job_control_entry foundEntry;
2428 struct job_control_entry* freeDeathEntry = NULL;
2429 status_t status = B_OK;
2431 TRACE(("wait_for_child(child = %" B_PRId32 ", flags = %" B_PRId32 ")\n",
2432 child, flags));
2434 T(WaitForChild(child, flags));
2436 pid_t originalChild = child;
2438 bool ignoreFoundEntries = false;
2439 bool ignoreFoundEntriesChecked = false;
2441 while (true) {
2442 // lock the team
2443 TeamLocker teamLocker(team);
2445 // A 0 child argument means to wait for all children in the process
2446 // group of the calling team.
2447 child = originalChild == 0 ? -team->group_id : originalChild;
2449 // check whether any condition holds
2450 job_control_entry* entry = get_job_control_entry(team, child, flags);
2452 // If we don't have an entry yet, check whether there are any children
2453 // complying to the process group specification at all.
2454 if (entry == NULL) {
2455 // No success yet -- check whether there are any children complying
2456 // to the process group specification at all.
2457 bool childrenExist = false;
2458 if (child == -1) {
2459 childrenExist = team->children != NULL;
2460 } else if (child < -1) {
2461 childrenExist = has_children_in_group(team, -child);
2462 } else if (child != team->id) {
2463 if (Team* childTeam = Team::Get(child)) {
2464 BReference<Team> childTeamReference(childTeam, true);
2465 TeamLocker childTeamLocker(childTeam);
2466 childrenExist = childTeam->parent == team;
2470 if (!childrenExist) {
2471 // there is no child we could wait for
2472 status = ECHILD;
2473 } else {
2474 // the children we're waiting for are still running
2475 status = B_WOULD_BLOCK;
2477 } else {
2478 // got something
2479 foundEntry = *entry;
2481 // unless WNOWAIT has been specified, "consume" the wait state
2482 if ((flags & WNOWAIT) == 0 || ignoreFoundEntries) {
2483 if (entry->state == JOB_CONTROL_STATE_DEAD) {
2484 // The child is dead. Reap its death entry.
2485 freeDeathEntry = entry;
2486 team->dead_children.entries.Remove(entry);
2487 team->dead_children.count--;
2488 } else {
2489 // The child is well. Reset its job control state.
2490 team_set_job_control_state(entry->team,
2491 JOB_CONTROL_STATE_NONE, NULL);
2496 // If we haven't got anything yet, prepare for waiting for the
2497 // condition variable.
2498 ConditionVariableEntry deadWaitEntry;
2500 if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0)
2501 team->dead_children.condition_variable.Add(&deadWaitEntry);
2503 teamLocker.Unlock();
2505 // we got our entry and can return to our caller
2506 if (status == B_OK) {
2507 if (ignoreFoundEntries) {
2508 // ... unless we shall ignore found entries
2509 delete freeDeathEntry;
2510 freeDeathEntry = NULL;
2511 continue;
2514 break;
2517 if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0) {
2518 T(WaitForChildDone(status));
2519 return status;
2522 status = deadWaitEntry.Wait(B_CAN_INTERRUPT);
2523 if (status == B_INTERRUPTED) {
2524 T(WaitForChildDone(status));
2525 return status;
2528 // If SA_NOCLDWAIT is set or SIGCHLD is ignored, we shall wait until
2529 // all our children are dead and fail with ECHILD. We check the
2530 // condition at this point.
2531 if (!ignoreFoundEntriesChecked) {
2532 teamLocker.Lock();
2534 struct sigaction& handler = team->SignalActionFor(SIGCHLD);
2535 if ((handler.sa_flags & SA_NOCLDWAIT) != 0
2536 || handler.sa_handler == SIG_IGN) {
2537 ignoreFoundEntries = true;
2540 teamLocker.Unlock();
2542 ignoreFoundEntriesChecked = true;
2546 delete freeDeathEntry;
2548 // When we got here, we have a valid death entry, and already got
2549 // unregistered from the team or group. Fill in the returned info.
2550 memset(&_info, 0, sizeof(_info));
2551 _info.si_signo = SIGCHLD;
2552 _info.si_pid = foundEntry.thread;
2553 _info.si_uid = foundEntry.signaling_user;
2554 // TODO: Fill in si_errno?
2556 switch (foundEntry.state) {
2557 case JOB_CONTROL_STATE_DEAD:
2558 _info.si_code = foundEntry.reason;
2559 _info.si_status = foundEntry.reason == CLD_EXITED
2560 ? foundEntry.status : foundEntry.signal;
2561 _usage_info.user_time = foundEntry.user_time;
2562 _usage_info.kernel_time = foundEntry.kernel_time;
2563 break;
2564 case JOB_CONTROL_STATE_STOPPED:
2565 _info.si_code = CLD_STOPPED;
2566 _info.si_status = foundEntry.signal;
2567 break;
2568 case JOB_CONTROL_STATE_CONTINUED:
2569 _info.si_code = CLD_CONTINUED;
2570 _info.si_status = 0;
2571 break;
2572 case JOB_CONTROL_STATE_NONE:
2573 // can't happen
2574 break;
2577 // If SIGCHLD is blocked, we shall clear pending SIGCHLDs, if no other child
2578 // status is available.
2579 TeamLocker teamLocker(team);
2580 InterruptsSpinLocker signalLocker(team->signal_lock);
2581 SpinLocker threadCreationLocker(gThreadCreationLock);
2583 if (is_team_signal_blocked(team, SIGCHLD)) {
2584 if (get_job_control_entry(team, child, flags) == NULL)
2585 team->RemovePendingSignals(SIGNAL_TO_MASK(SIGCHLD));
2588 threadCreationLocker.Unlock();
2589 signalLocker.Unlock();
2590 teamLocker.Unlock();
2592 // When the team is dead, the main thread continues to live in the kernel
2593 // team for a very short time. To avoid surprises for the caller we rather
2594 // wait until the thread is really gone.
2595 if (foundEntry.state == JOB_CONTROL_STATE_DEAD)
2596 wait_for_thread(foundEntry.thread, NULL);
2598 T(WaitForChildDone(foundEntry));
2600 return foundEntry.thread;
2604 /*! Fills the team_info structure with information from the specified team.
2605 Interrupts must be enabled. The team must not be locked.
2607 static status_t
2608 fill_team_info(Team* team, team_info* info, size_t size)
2610 if (size != sizeof(team_info))
2611 return B_BAD_VALUE;
2613 // TODO: Set more informations for team_info
2614 memset(info, 0, size);
2616 info->team = team->id;
2617 // immutable
2618 info->image_count = count_images(team);
2619 // protected by sImageMutex
2621 TeamLocker teamLocker(team);
2622 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
2624 info->thread_count = team->num_threads;
2625 //info->area_count =
2626 info->debugger_nub_thread = team->debug_info.nub_thread;
2627 info->debugger_nub_port = team->debug_info.nub_port;
2628 info->uid = team->effective_uid;
2629 info->gid = team->effective_gid;
2631 strlcpy(info->args, team->Args(), sizeof(info->args));
2632 info->argc = 1;
2634 return B_OK;
2638 /*! Returns whether the process group contains stopped processes.
2639 The caller must hold the process group's lock.
2641 static bool
2642 process_group_has_stopped_processes(ProcessGroup* group)
2644 Team* team = group->teams;
2645 while (team != NULL) {
2646 // the parent team's lock guards the job control entry -- acquire it
2647 team->LockTeamAndParent(false);
2649 if (team->job_control_entry != NULL
2650 && team->job_control_entry->state == JOB_CONTROL_STATE_STOPPED) {
2651 team->UnlockTeamAndParent();
2652 return true;
2655 team->UnlockTeamAndParent();
2657 team = team->group_next;
2660 return false;
2664 /*! Iterates through all process groups queued in team_remove_team() and signals
2665 those that are orphaned and have stopped processes.
2666 The caller must not hold any team or process group locks.
2668 static void
2669 orphaned_process_group_check()
2671 // process as long as there are groups in the list
2672 while (true) {
2673 // remove the head from the list
2674 MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
2676 ProcessGroup* group = sOrphanedCheckProcessGroups.RemoveHead();
2677 if (group == NULL)
2678 return;
2680 group->UnsetOrphanedCheck();
2681 BReference<ProcessGroup> groupReference(group);
2683 orphanedCheckLocker.Unlock();
2685 AutoLocker<ProcessGroup> groupLocker(group);
2687 // If the group is orphaned and contains stopped processes, we're
2688 // supposed to send SIGHUP + SIGCONT.
2689 if (group->IsOrphaned() && process_group_has_stopped_processes(group)) {
2690 Thread* currentThread = thread_get_current_thread();
2692 Signal signal(SIGHUP, SI_USER, B_OK, currentThread->team->id);
2693 send_signal_to_process_group_locked(group, signal, 0);
2695 signal.SetNumber(SIGCONT);
2696 send_signal_to_process_group_locked(group, signal, 0);
2702 static status_t
2703 common_get_team_usage_info(team_id id, int32 who, team_usage_info* info,
2704 uint32 flags)
2706 if (who != B_TEAM_USAGE_SELF && who != B_TEAM_USAGE_CHILDREN)
2707 return B_BAD_VALUE;
2709 // get the team
2710 Team* team = Team::GetAndLock(id);
2711 if (team == NULL)
2712 return B_BAD_TEAM_ID;
2713 BReference<Team> teamReference(team, true);
2714 TeamLocker teamLocker(team, true);
2716 if ((flags & B_CHECK_PERMISSION) != 0) {
2717 uid_t uid = geteuid();
2718 if (uid != 0 && uid != team->effective_uid)
2719 return B_NOT_ALLOWED;
2722 bigtime_t kernelTime = 0;
2723 bigtime_t userTime = 0;
2725 switch (who) {
2726 case B_TEAM_USAGE_SELF:
2728 Thread* thread = team->thread_list;
2730 for (; thread != NULL; thread = thread->team_next) {
2731 InterruptsSpinLocker threadTimeLocker(thread->time_lock);
2732 kernelTime += thread->kernel_time;
2733 userTime += thread->user_time;
2736 kernelTime += team->dead_threads_kernel_time;
2737 userTime += team->dead_threads_user_time;
2738 break;
2741 case B_TEAM_USAGE_CHILDREN:
2743 Team* child = team->children;
2744 for (; child != NULL; child = child->siblings_next) {
2745 TeamLocker childLocker(child);
2747 Thread* thread = team->thread_list;
2749 for (; thread != NULL; thread = thread->team_next) {
2750 InterruptsSpinLocker threadTimeLocker(thread->time_lock);
2751 kernelTime += thread->kernel_time;
2752 userTime += thread->user_time;
2755 kernelTime += child->dead_threads_kernel_time;
2756 userTime += child->dead_threads_user_time;
2759 kernelTime += team->dead_children.kernel_time;
2760 userTime += team->dead_children.user_time;
2761 break;
2765 info->kernel_time = kernelTime;
2766 info->user_time = userTime;
2768 return B_OK;
2772 // #pragma mark - Private kernel API
2775 status_t
2776 team_init(kernel_args* args)
2778 // create the team hash table
2779 new(&sTeamHash) TeamTable;
2780 if (sTeamHash.Init(64) != B_OK)
2781 panic("Failed to init team hash table!");
2783 new(&sGroupHash) ProcessGroupHashTable;
2784 if (sGroupHash.Init() != B_OK)
2785 panic("Failed to init process group hash table!");
2787 // create initial session and process groups
2789 ProcessSession* session = new(std::nothrow) ProcessSession(1);
2790 if (session == NULL)
2791 panic("Could not create initial session.\n");
2792 BReference<ProcessSession> sessionReference(session, true);
2794 ProcessGroup* group = new(std::nothrow) ProcessGroup(1);
2795 if (group == NULL)
2796 panic("Could not create initial process group.\n");
2797 BReference<ProcessGroup> groupReference(group, true);
2799 group->Publish(session);
2801 // create the kernel team
2802 sKernelTeam = Team::Create(1, "kernel_team", true);
2803 if (sKernelTeam == NULL)
2804 panic("could not create kernel team!\n");
2805 sKernelTeam->SetArgs(sKernelTeam->Name());
2806 sKernelTeam->state = TEAM_STATE_NORMAL;
2808 sKernelTeam->saved_set_uid = 0;
2809 sKernelTeam->real_uid = 0;
2810 sKernelTeam->effective_uid = 0;
2811 sKernelTeam->saved_set_gid = 0;
2812 sKernelTeam->real_gid = 0;
2813 sKernelTeam->effective_gid = 0;
2814 sKernelTeam->supplementary_groups = NULL;
2815 sKernelTeam->supplementary_group_count = 0;
2817 insert_team_into_group(group, sKernelTeam);
2819 sKernelTeam->io_context = vfs_new_io_context(NULL, false);
2820 if (sKernelTeam->io_context == NULL)
2821 panic("could not create io_context for kernel team!\n");
2823 if (vfs_resize_fd_table(sKernelTeam->io_context, 4096) != B_OK)
2824 dprintf("Failed to resize FD table for kernel team!\n");
2826 // stick it in the team hash
2827 sTeamHash.Insert(sKernelTeam);
2829 add_debugger_command_etc("team", &dump_team_info,
2830 "Dump info about a particular team",
2831 "[ <id> | <address> | <name> ]\n"
2832 "Prints information about the specified team. If no argument is given\n"
2833 "the current team is selected.\n"
2834 " <id> - The ID of the team.\n"
2835 " <address> - The address of the team structure.\n"
2836 " <name> - The team's name.\n", 0);
2837 add_debugger_command_etc("teams", &dump_teams, "List all teams",
2838 "\n"
2839 "Prints a list of all existing teams.\n", 0);
2841 new(&sNotificationService) TeamNotificationService();
2843 sNotificationService.Register();
2845 return B_OK;
2849 int32
2850 team_max_teams(void)
2852 return sMaxTeams;
2856 int32
2857 team_used_teams(void)
2859 InterruptsSpinLocker teamsLocker(sTeamHashLock);
2860 return sUsedTeams;
2864 /*! Returns a death entry of a child team specified by ID (if any).
2865 The caller must hold the team's lock.
2867 \param team The team whose dead children list to check.
2868 \param child The ID of the child for whose death entry to lock. Must be > 0.
2869 \param _deleteEntry Return variable, indicating whether the caller needs to
2870 delete the returned entry.
2871 \return The death entry of the matching team, or \c NULL, if no death entry
2872 for the team was found.
2874 job_control_entry*
2875 team_get_death_entry(Team* team, thread_id child, bool* _deleteEntry)
2877 if (child <= 0)
2878 return NULL;
2880 job_control_entry* entry = get_job_control_entry(team->dead_children,
2881 child);
2882 if (entry) {
2883 // remove the entry only, if the caller is the parent of the found team
2884 if (team_get_current_team_id() == entry->thread) {
2885 team->dead_children.entries.Remove(entry);
2886 team->dead_children.count--;
2887 *_deleteEntry = true;
2888 } else {
2889 *_deleteEntry = false;
2893 return entry;
2897 /*! Quick check to see if we have a valid team ID. */
2898 bool
2899 team_is_valid(team_id id)
2901 if (id <= 0)
2902 return false;
2904 InterruptsSpinLocker teamsLocker(sTeamHashLock);
2906 return team_get_team_struct_locked(id) != NULL;
2910 Team*
2911 team_get_team_struct_locked(team_id id)
2913 return sTeamHash.Lookup(id);
2917 void
2918 team_set_controlling_tty(int32 ttyIndex)
2920 // lock the team, so its session won't change while we're playing with it
2921 Team* team = thread_get_current_thread()->team;
2922 TeamLocker teamLocker(team);
2924 // get and lock the session
2925 ProcessSession* session = team->group->Session();
2926 AutoLocker<ProcessSession> sessionLocker(session);
2928 // set the session's fields
2929 session->controlling_tty = ttyIndex;
2930 session->foreground_group = -1;
2934 int32
2935 team_get_controlling_tty()
2937 // lock the team, so its session won't change while we're playing with it
2938 Team* team = thread_get_current_thread()->team;
2939 TeamLocker teamLocker(team);
2941 // get and lock the session
2942 ProcessSession* session = team->group->Session();
2943 AutoLocker<ProcessSession> sessionLocker(session);
2945 // get the session's field
2946 return session->controlling_tty;
2950 status_t
2951 team_set_foreground_process_group(int32 ttyIndex, pid_t processGroupID)
2953 // lock the team, so its session won't change while we're playing with it
2954 Thread* thread = thread_get_current_thread();
2955 Team* team = thread->team;
2956 TeamLocker teamLocker(team);
2958 // get and lock the session
2959 ProcessSession* session = team->group->Session();
2960 AutoLocker<ProcessSession> sessionLocker(session);
2962 // check given TTY -- must be the controlling tty of the calling process
2963 if (session->controlling_tty != ttyIndex)
2964 return ENOTTY;
2966 // check given process group -- must belong to our session
2968 InterruptsSpinLocker groupHashLocker(sGroupHashLock);
2969 ProcessGroup* group = sGroupHash.Lookup(processGroupID);
2970 if (group == NULL || group->Session() != session)
2971 return B_BAD_VALUE;
2974 // If we are a background group, we can do that unharmed only when we
2975 // ignore or block SIGTTOU. Otherwise the group gets a SIGTTOU.
2976 if (session->foreground_group != -1
2977 && session->foreground_group != team->group_id
2978 && team->SignalActionFor(SIGTTOU).sa_handler != SIG_IGN
2979 && (thread->sig_block_mask & SIGNAL_TO_MASK(SIGTTOU)) == 0) {
2980 InterruptsSpinLocker signalLocker(team->signal_lock);
2982 if (!is_team_signal_blocked(team, SIGTTOU)) {
2983 pid_t groupID = team->group_id;
2985 signalLocker.Unlock();
2986 sessionLocker.Unlock();
2987 teamLocker.Unlock();
2989 Signal signal(SIGTTOU, SI_USER, B_OK, team->id);
2990 send_signal_to_process_group(groupID, signal, 0);
2991 return B_INTERRUPTED;
2995 session->foreground_group = processGroupID;
2997 return B_OK;
3001 /*! Removes the specified team from the global team hash, from its process
3002 group, and from its parent.
3003 It also moves all of its children to the kernel team.
3005 The caller must hold the following locks:
3006 - \a team's process group's lock,
3007 - the kernel team's lock,
3008 - \a team's parent team's lock (might be the kernel team), and
3009 - \a team's lock.
3011 void
3012 team_remove_team(Team* team, pid_t& _signalGroup)
3014 Team* parent = team->parent;
3016 // remember how long this team lasted
3017 parent->dead_children.kernel_time += team->dead_threads_kernel_time
3018 + team->dead_children.kernel_time;
3019 parent->dead_children.user_time += team->dead_threads_user_time
3020 + team->dead_children.user_time;
3022 // remove the team from the hash table
3023 InterruptsSpinLocker teamsLocker(sTeamHashLock);
3024 sTeamHash.Remove(team);
3025 sUsedTeams--;
3026 teamsLocker.Unlock();
3028 // The team can no longer be accessed by ID. Navigation to it is still
3029 // possible from its process group and its parent and children, but that
3030 // will be rectified shortly.
3031 team->state = TEAM_STATE_DEATH;
3033 // If we're a controlling process (i.e. a session leader with controlling
3034 // terminal), there's a bit of signalling we have to do. We can't do any of
3035 // the signaling here due to the bunch of locks we're holding, but we need
3036 // to determine, whom to signal.
3037 _signalGroup = -1;
3038 bool isSessionLeader = false;
3039 if (team->session_id == team->id
3040 && team->group->Session()->controlling_tty >= 0) {
3041 isSessionLeader = true;
3043 ProcessSession* session = team->group->Session();
3045 AutoLocker<ProcessSession> sessionLocker(session);
3047 session->controlling_tty = -1;
3048 _signalGroup = session->foreground_group;
3051 // remove us from our process group
3052 remove_team_from_group(team);
3054 // move the team's children to the kernel team
3055 while (Team* child = team->children) {
3056 // remove the child from the current team and add it to the kernel team
3057 TeamLocker childLocker(child);
3059 remove_team_from_parent(team, child);
3060 insert_team_into_parent(sKernelTeam, child);
3062 // move job control entries too
3063 sKernelTeam->stopped_children.entries.MoveFrom(
3064 &team->stopped_children.entries);
3065 sKernelTeam->continued_children.entries.MoveFrom(
3066 &team->continued_children.entries);
3068 // If the team was a session leader with controlling terminal,
3069 // we need to send SIGHUP + SIGCONT to all newly-orphaned process
3070 // groups with stopped processes. Due to locking complications we can't
3071 // do that here, so we only check whether we were a reason for the
3072 // child's process group not being an orphan and, if so, schedule a
3073 // later check (cf. orphaned_process_group_check()).
3074 if (isSessionLeader) {
3075 ProcessGroup* childGroup = child->group;
3076 if (childGroup->Session()->id == team->session_id
3077 && childGroup->id != team->group_id) {
3078 childGroup->ScheduleOrphanedCheck();
3082 // Note, we don't move the dead children entries. Those will be deleted
3083 // when the team structure is deleted.
3086 // remove us from our parent
3087 remove_team_from_parent(parent, team);
3091 /*! Kills all threads but the main thread of the team and shuts down user
3092 debugging for it.
3093 To be called on exit of the team's main thread. No locks must be held.
3095 \param team The team in question.
3096 \return The port of the debugger for the team, -1 if none. To be passed to
3097 team_delete_team().
3099 port_id
3100 team_shutdown_team(Team* team)
3102 ASSERT(thread_get_current_thread() == team->main_thread);
3104 TeamLocker teamLocker(team);
3106 // Make sure debugging changes won't happen anymore.
3107 port_id debuggerPort = -1;
3108 while (true) {
3109 // If a debugger change is in progress for the team, we'll have to
3110 // wait until it is done.
3111 ConditionVariableEntry waitForDebuggerEntry;
3112 bool waitForDebugger = false;
3114 InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
3116 if (team->debug_info.debugger_changed_condition != NULL) {
3117 team->debug_info.debugger_changed_condition->Add(
3118 &waitForDebuggerEntry);
3119 waitForDebugger = true;
3120 } else if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
3121 // The team is being debugged. That will stop with the termination
3122 // of the nub thread. Since we set the team state to death, no one
3123 // can install a debugger anymore. We fetch the debugger's port to
3124 // send it a message at the bitter end.
3125 debuggerPort = team->debug_info.debugger_port;
3128 debugInfoLocker.Unlock();
3130 if (!waitForDebugger)
3131 break;
3133 // wait for the debugger change to be finished
3134 teamLocker.Unlock();
3136 waitForDebuggerEntry.Wait();
3138 teamLocker.Lock();
3141 // Mark the team as shutting down. That will prevent new threads from being
3142 // created and debugger changes from taking place.
3143 team->state = TEAM_STATE_SHUTDOWN;
3145 // delete all timers
3146 team->DeleteUserTimers(false);
3148 // deactivate CPU time user timers for the team
3149 InterruptsSpinLocker timeLocker(team->time_lock);
3151 if (team->HasActiveCPUTimeUserTimers())
3152 team->DeactivateCPUTimeUserTimers();
3154 timeLocker.Unlock();
3156 // kill all threads but the main thread
3157 team_death_entry deathEntry;
3158 deathEntry.condition.Init(team, "team death");
3160 while (true) {
3161 team->death_entry = &deathEntry;
3162 deathEntry.remaining_threads = 0;
3164 Thread* thread = team->thread_list;
3165 while (thread != NULL) {
3166 if (thread != team->main_thread) {
3167 Signal signal(SIGKILLTHR, SI_USER, B_OK, team->id);
3168 send_signal_to_thread(thread, signal, B_DO_NOT_RESCHEDULE);
3169 deathEntry.remaining_threads++;
3172 thread = thread->team_next;
3175 if (deathEntry.remaining_threads == 0)
3176 break;
3178 // there are threads to wait for
3179 ConditionVariableEntry entry;
3180 deathEntry.condition.Add(&entry);
3182 teamLocker.Unlock();
3184 entry.Wait();
3186 teamLocker.Lock();
3189 team->death_entry = NULL;
3191 return debuggerPort;
3195 /*! Called on team exit to notify threads waiting on the team and free most
3196 resources associated with it.
3197 The caller shouldn't hold any locks.
3199 void
3200 team_delete_team(Team* team, port_id debuggerPort)
3202 // Not quite in our job description, but work that has been left by
3203 // team_remove_team() and that can be done now that we're not holding any
3204 // locks.
3205 orphaned_process_group_check();
3207 team_id teamID = team->id;
3209 ASSERT(team->num_threads == 0);
3211 // If someone is waiting for this team to be loaded, but it dies
3212 // unexpectedly before being done, we need to notify the waiting
3213 // thread now.
3215 TeamLocker teamLocker(team);
3217 if (team->loading_info) {
3218 // there's indeed someone waiting
3219 struct team_loading_info* loadingInfo = team->loading_info;
3220 team->loading_info = NULL;
3222 loadingInfo->result = B_ERROR;
3223 loadingInfo->done = true;
3225 // wake up the waiting thread
3226 thread_continue(loadingInfo->thread);
3229 // notify team watchers
3232 // we're not reachable from anyone anymore at this point, so we
3233 // can safely access the list without any locking
3234 struct team_watcher* watcher;
3235 while ((watcher = (struct team_watcher*)list_remove_head_item(
3236 &team->watcher_list)) != NULL) {
3237 watcher->hook(teamID, watcher->data);
3238 free(watcher);
3242 teamLocker.Unlock();
3244 sNotificationService.Notify(TEAM_REMOVED, team);
3246 // free team resources
3248 delete_realtime_sem_context(team->realtime_sem_context);
3249 xsi_sem_undo(team);
3250 remove_images(team);
3251 team->address_space->RemoveAndPut();
3253 team->ReleaseReference();
3255 // notify the debugger, that the team is gone
3256 user_debug_team_deleted(teamID, debuggerPort);
3260 Team*
3261 team_get_kernel_team(void)
3263 return sKernelTeam;
3267 team_id
3268 team_get_kernel_team_id(void)
3270 if (!sKernelTeam)
3271 return 0;
3273 return sKernelTeam->id;
3277 team_id
3278 team_get_current_team_id(void)
3280 return thread_get_current_thread()->team->id;
3284 status_t
3285 team_get_address_space(team_id id, VMAddressSpace** _addressSpace)
3287 if (id == sKernelTeam->id) {
3288 // we're the kernel team, so we don't have to go through all
3289 // the hassle (locking and hash lookup)
3290 *_addressSpace = VMAddressSpace::GetKernel();
3291 return B_OK;
3294 InterruptsSpinLocker teamsLocker(sTeamHashLock);
3296 Team* team = team_get_team_struct_locked(id);
3297 if (team == NULL)
3298 return B_BAD_VALUE;
3300 team->address_space->Get();
3301 *_addressSpace = team->address_space;
3302 return B_OK;
3306 /*! Sets the team's job control state.
3307 The caller must hold the parent team's lock. Interrupts are allowed to be
3308 enabled or disabled.
3309 \a team The team whose job control state shall be set.
3310 \a newState The new state to be set.
3311 \a signal The signal the new state was caused by. Can \c NULL, if none. Then
3312 the caller is responsible for filling in the following fields of the
3313 entry before releasing the parent team's lock, unless the new state is
3314 \c JOB_CONTROL_STATE_NONE:
3315 - \c signal: The number of the signal causing the state change.
3316 - \c signaling_user: The real UID of the user sending the signal.
3318 void
3319 team_set_job_control_state(Team* team, job_control_state newState,
3320 Signal* signal)
3322 if (team == NULL || team->job_control_entry == NULL)
3323 return;
3325 // don't touch anything, if the state stays the same or the team is already
3326 // dead
3327 job_control_entry* entry = team->job_control_entry;
3328 if (entry->state == newState || entry->state == JOB_CONTROL_STATE_DEAD)
3329 return;
3331 T(SetJobControlState(team->id, newState, signal));
3333 // remove from the old list
3334 switch (entry->state) {
3335 case JOB_CONTROL_STATE_NONE:
3336 // entry is in no list ATM
3337 break;
3338 case JOB_CONTROL_STATE_DEAD:
3339 // can't get here
3340 break;
3341 case JOB_CONTROL_STATE_STOPPED:
3342 team->parent->stopped_children.entries.Remove(entry);
3343 break;
3344 case JOB_CONTROL_STATE_CONTINUED:
3345 team->parent->continued_children.entries.Remove(entry);
3346 break;
3349 entry->state = newState;
3351 if (signal != NULL) {
3352 entry->signal = signal->Number();
3353 entry->signaling_user = signal->SendingUser();
3356 // add to new list
3357 team_job_control_children* childList = NULL;
3358 switch (entry->state) {
3359 case JOB_CONTROL_STATE_NONE:
3360 // entry doesn't get into any list
3361 break;
3362 case JOB_CONTROL_STATE_DEAD:
3363 childList = &team->parent->dead_children;
3364 team->parent->dead_children.count++;
3365 break;
3366 case JOB_CONTROL_STATE_STOPPED:
3367 childList = &team->parent->stopped_children;
3368 break;
3369 case JOB_CONTROL_STATE_CONTINUED:
3370 childList = &team->parent->continued_children;
3371 break;
3374 if (childList != NULL) {
3375 childList->entries.Add(entry);
3376 team->parent->dead_children.condition_variable.NotifyAll();
3381 /*! Inits the given team's exit information, if not yet initialized, to some
3382 generic "killed" status.
3383 The caller must not hold the team's lock. Interrupts must be enabled.
3385 \param team The team whose exit info shall be initialized.
3387 void
3388 team_init_exit_info_on_error(Team* team)
3390 TeamLocker teamLocker(team);
3392 if (!team->exit.initialized) {
3393 team->exit.reason = CLD_KILLED;
3394 team->exit.signal = SIGKILL;
3395 team->exit.signaling_user = geteuid();
3396 team->exit.status = 0;
3397 team->exit.initialized = true;
3402 /*! Adds a hook to the team that is called as soon as this team goes away.
3403 This call might get public in the future.
3405 status_t
3406 start_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data)
3408 if (hook == NULL || teamID < B_OK)
3409 return B_BAD_VALUE;
3411 // create the watcher object
3412 team_watcher* watcher = (team_watcher*)malloc(sizeof(team_watcher));
3413 if (watcher == NULL)
3414 return B_NO_MEMORY;
3416 watcher->hook = hook;
3417 watcher->data = data;
3419 // add watcher, if the team isn't already dying
3420 // get the team
3421 Team* team = Team::GetAndLock(teamID);
3422 if (team == NULL) {
3423 free(watcher);
3424 return B_BAD_TEAM_ID;
3427 list_add_item(&team->watcher_list, watcher);
3429 team->UnlockAndReleaseReference();
3431 return B_OK;
3435 status_t
3436 stop_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data)
3438 if (hook == NULL || teamID < 0)
3439 return B_BAD_VALUE;
3441 // get team and remove watcher (if present)
3442 Team* team = Team::GetAndLock(teamID);
3443 if (team == NULL)
3444 return B_BAD_TEAM_ID;
3446 // search for watcher
3447 team_watcher* watcher = NULL;
3448 while ((watcher = (team_watcher*)list_get_next_item(
3449 &team->watcher_list, watcher)) != NULL) {
3450 if (watcher->hook == hook && watcher->data == data) {
3451 // got it!
3452 list_remove_item(&team->watcher_list, watcher);
3453 break;
3457 team->UnlockAndReleaseReference();
3459 if (watcher == NULL)
3460 return B_ENTRY_NOT_FOUND;
3462 free(watcher);
3463 return B_OK;
3467 /*! Allocates a user_thread structure from the team.
3468 The team lock must be held, unless the function is called for the team's
3469 main thread. Interrupts must be enabled.
3471 struct user_thread*
3472 team_allocate_user_thread(Team* team)
3474 if (team->user_data == 0)
3475 return NULL;
3477 // take an entry from the free list, if any
3478 if (struct free_user_thread* entry = team->free_user_threads) {
3479 user_thread* thread = entry->thread;
3480 team->free_user_threads = entry->next;
3481 free(entry);
3482 return thread;
3485 while (true) {
3486 // enough space left?
3487 size_t needed = ROUNDUP(sizeof(user_thread), CACHE_LINE_SIZE);
3488 if (team->user_data_size - team->used_user_data < needed) {
3489 // try to resize the area
3490 if (resize_area(team->user_data_area,
3491 team->user_data_size + B_PAGE_SIZE) != B_OK) {
3492 return NULL;
3495 // resized user area successfully -- try to allocate the user_thread
3496 // again
3497 team->user_data_size += B_PAGE_SIZE;
3498 continue;
3501 // allocate the user_thread
3502 user_thread* thread
3503 = (user_thread*)(team->user_data + team->used_user_data);
3504 team->used_user_data += needed;
3506 return thread;
3511 /*! Frees the given user_thread structure.
3512 The team's lock must not be held. Interrupts must be enabled.
3513 \param team The team the user thread was allocated from.
3514 \param userThread The user thread to free.
3516 void
3517 team_free_user_thread(Team* team, struct user_thread* userThread)
3519 if (userThread == NULL)
3520 return;
3522 // create a free list entry
3523 free_user_thread* entry
3524 = (free_user_thread*)malloc(sizeof(free_user_thread));
3525 if (entry == NULL) {
3526 // we have to leak the user thread :-/
3527 return;
3530 // add to free list
3531 TeamLocker teamLocker(team);
3533 entry->thread = userThread;
3534 entry->next = team->free_user_threads;
3535 team->free_user_threads = entry;
3539 // #pragma mark - Associated data interface
3542 AssociatedData::AssociatedData()
3544 fOwner(NULL)
3549 AssociatedData::~AssociatedData()
3554 void
3555 AssociatedData::OwnerDeleted(AssociatedDataOwner* owner)
3560 AssociatedDataOwner::AssociatedDataOwner()
3562 mutex_init(&fLock, "associated data owner");
3566 AssociatedDataOwner::~AssociatedDataOwner()
3568 mutex_destroy(&fLock);
3572 bool
3573 AssociatedDataOwner::AddData(AssociatedData* data)
3575 MutexLocker locker(fLock);
3577 if (data->Owner() != NULL)
3578 return false;
3580 data->AcquireReference();
3581 fList.Add(data);
3582 data->SetOwner(this);
3584 return true;
3588 bool
3589 AssociatedDataOwner::RemoveData(AssociatedData* data)
3591 MutexLocker locker(fLock);
3593 if (data->Owner() != this)
3594 return false;
3596 data->SetOwner(NULL);
3597 fList.Remove(data);
3599 locker.Unlock();
3601 data->ReleaseReference();
3603 return true;
3607 void
3608 AssociatedDataOwner::PrepareForDeletion()
3610 MutexLocker locker(fLock);
3612 // move all data to a temporary list and unset the owner
3613 DataList list;
3614 list.MoveFrom(&fList);
3616 for (DataList::Iterator it = list.GetIterator();
3617 AssociatedData* data = it.Next();) {
3618 data->SetOwner(NULL);
3621 locker.Unlock();
3623 // call the notification hooks and release our references
3624 while (AssociatedData* data = list.RemoveHead()) {
3625 data->OwnerDeleted(this);
3626 data->ReleaseReference();
3631 /*! Associates data with the current team.
3632 When the team is deleted, the data object is notified.
3633 The team acquires a reference to the object.
3635 \param data The data object.
3636 \return \c true on success, \c false otherwise. Fails only when the supplied
3637 data object is already associated with another owner.
3639 bool
3640 team_associate_data(AssociatedData* data)
3642 return thread_get_current_thread()->team->AddData(data);
3646 /*! Dissociates data from the current team.
3647 Balances an earlier call to team_associate_data().
3649 \param data The data object.
3650 \return \c true on success, \c false otherwise. Fails only when the data
3651 object is not associated with the current team.
3653 bool
3654 team_dissociate_data(AssociatedData* data)
3656 return thread_get_current_thread()->team->RemoveData(data);
3660 // #pragma mark - Public kernel API
3663 thread_id
3664 load_image(int32 argCount, const char** args, const char** env)
3666 return load_image_etc(argCount, args, env, B_NORMAL_PRIORITY,
3667 B_CURRENT_TEAM, B_WAIT_TILL_LOADED);
3671 thread_id
3672 load_image_etc(int32 argCount, const char* const* args,
3673 const char* const* env, int32 priority, team_id parentID, uint32 flags)
3675 // we need to flatten the args and environment
3677 if (args == NULL)
3678 return B_BAD_VALUE;
3680 // determine total needed size
3681 int32 argSize = 0;
3682 for (int32 i = 0; i < argCount; i++)
3683 argSize += strlen(args[i]) + 1;
3685 int32 envCount = 0;
3686 int32 envSize = 0;
3687 while (env != NULL && env[envCount] != NULL)
3688 envSize += strlen(env[envCount++]) + 1;
3690 int32 size = (argCount + envCount + 2) * sizeof(char*) + argSize + envSize;
3691 if (size > MAX_PROCESS_ARGS_SIZE)
3692 return B_TOO_MANY_ARGS;
3694 // allocate space
3695 char** flatArgs = (char**)malloc(size);
3696 if (flatArgs == NULL)
3697 return B_NO_MEMORY;
3699 char** slot = flatArgs;
3700 char* stringSpace = (char*)(flatArgs + argCount + envCount + 2);
3702 // copy arguments and environment
3703 for (int32 i = 0; i < argCount; i++) {
3704 int32 argSize = strlen(args[i]) + 1;
3705 memcpy(stringSpace, args[i], argSize);
3706 *slot++ = stringSpace;
3707 stringSpace += argSize;
3710 *slot++ = NULL;
3712 for (int32 i = 0; i < envCount; i++) {
3713 int32 envSize = strlen(env[i]) + 1;
3714 memcpy(stringSpace, env[i], envSize);
3715 *slot++ = stringSpace;
3716 stringSpace += envSize;
3719 *slot++ = NULL;
3721 thread_id thread = load_image_internal(flatArgs, size, argCount, envCount,
3722 B_NORMAL_PRIORITY, parentID, B_WAIT_TILL_LOADED, -1, 0);
3724 free(flatArgs);
3725 // load_image_internal() unset our variable if it took over ownership
3727 return thread;
3731 status_t
3732 wait_for_team(team_id id, status_t* _returnCode)
3734 // check whether the team exists
3735 InterruptsSpinLocker teamsLocker(sTeamHashLock);
3737 Team* team = team_get_team_struct_locked(id);
3738 if (team == NULL)
3739 return B_BAD_TEAM_ID;
3741 id = team->id;
3743 teamsLocker.Unlock();
3745 // wait for the main thread (it has the same ID as the team)
3746 return wait_for_thread(id, _returnCode);
3750 status_t
3751 kill_team(team_id id)
3753 InterruptsSpinLocker teamsLocker(sTeamHashLock);
3755 Team* team = team_get_team_struct_locked(id);
3756 if (team == NULL)
3757 return B_BAD_TEAM_ID;
3759 id = team->id;
3761 teamsLocker.Unlock();
3763 if (team == sKernelTeam)
3764 return B_NOT_ALLOWED;
3766 // Just kill the team's main thread (it has same ID as the team). The
3767 // cleanup code there will take care of the team.
3768 return kill_thread(id);
3772 status_t
3773 _get_team_info(team_id id, team_info* info, size_t size)
3775 // get the team
3776 Team* team = Team::Get(id);
3777 if (team == NULL)
3778 return B_BAD_TEAM_ID;
3779 BReference<Team> teamReference(team, true);
3781 // fill in the info
3782 return fill_team_info(team, info, size);
3786 status_t
3787 _get_next_team_info(int32* cookie, team_info* info, size_t size)
3789 int32 slot = *cookie;
3790 if (slot < 1)
3791 slot = 1;
3793 InterruptsSpinLocker locker(sTeamHashLock);
3795 team_id lastTeamID = peek_next_thread_id();
3796 // TODO: This is broken, since the id can wrap around!
3798 // get next valid team
3799 Team* team = NULL;
3800 while (slot < lastTeamID && !(team = team_get_team_struct_locked(slot)))
3801 slot++;
3803 if (team == NULL)
3804 return B_BAD_TEAM_ID;
3806 // get a reference to the team and unlock
3807 BReference<Team> teamReference(team);
3808 locker.Unlock();
3810 // fill in the info
3811 *cookie = ++slot;
3812 return fill_team_info(team, info, size);
3816 status_t
3817 _get_team_usage_info(team_id id, int32 who, team_usage_info* info, size_t size)
3819 if (size != sizeof(team_usage_info))
3820 return B_BAD_VALUE;
3822 return common_get_team_usage_info(id, who, info, 0);
3826 pid_t
3827 getpid(void)
3829 return thread_get_current_thread()->team->id;
3833 pid_t
3834 getppid(void)
3836 Team* team = thread_get_current_thread()->team;
3838 TeamLocker teamLocker(team);
3840 return team->parent->id;
3844 pid_t
3845 getpgid(pid_t id)
3847 if (id < 0) {
3848 errno = EINVAL;
3849 return -1;
3852 if (id == 0) {
3853 // get process group of the calling process
3854 Team* team = thread_get_current_thread()->team;
3855 TeamLocker teamLocker(team);
3856 return team->group_id;
3859 // get the team
3860 Team* team = Team::GetAndLock(id);
3861 if (team == NULL) {
3862 errno = ESRCH;
3863 return -1;
3866 // get the team's process group ID
3867 pid_t groupID = team->group_id;
3869 team->UnlockAndReleaseReference();
3871 return groupID;
3875 pid_t
3876 getsid(pid_t id)
3878 if (id < 0) {
3879 errno = EINVAL;
3880 return -1;
3883 if (id == 0) {
3884 // get session of the calling process
3885 Team* team = thread_get_current_thread()->team;
3886 TeamLocker teamLocker(team);
3887 return team->session_id;
3890 // get the team
3891 Team* team = Team::GetAndLock(id);
3892 if (team == NULL) {
3893 errno = ESRCH;
3894 return -1;
3897 // get the team's session ID
3898 pid_t sessionID = team->session_id;
3900 team->UnlockAndReleaseReference();
3902 return sessionID;
3906 // #pragma mark - User syscalls
3909 status_t
3910 _user_exec(const char* userPath, const char* const* userFlatArgs,
3911 size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask)
3913 // NOTE: Since this function normally doesn't return, don't use automatic
3914 // variables that need destruction in the function scope.
3915 char path[B_PATH_NAME_LENGTH];
3917 if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userFlatArgs)
3918 || user_strlcpy(path, userPath, sizeof(path)) < B_OK)
3919 return B_BAD_ADDRESS;
3921 // copy and relocate the flat arguments
3922 char** flatArgs;
3923 status_t error = copy_user_process_args(userFlatArgs, flatArgsSize,
3924 argCount, envCount, flatArgs);
3926 if (error == B_OK) {
3927 error = exec_team(path, flatArgs, _ALIGN(flatArgsSize), argCount,
3928 envCount, umask);
3929 // this one only returns in case of error
3932 free(flatArgs);
3933 return error;
3937 thread_id
3938 _user_fork(void)
3940 return fork_team();
3944 pid_t
3945 _user_wait_for_child(thread_id child, uint32 flags, siginfo_t* userInfo,
3946 team_usage_info* usageInfo)
3948 if (userInfo != NULL && !IS_USER_ADDRESS(userInfo))
3949 return B_BAD_ADDRESS;
3950 if (usageInfo != NULL && !IS_USER_ADDRESS(usageInfo))
3951 return B_BAD_ADDRESS;
3953 siginfo_t info;
3954 team_usage_info usage_info;
3955 pid_t foundChild = wait_for_child(child, flags, info, usage_info);
3956 if (foundChild < 0)
3957 return syscall_restart_handle_post(foundChild);
3959 // copy info back to userland
3960 if (userInfo != NULL && user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
3961 return B_BAD_ADDRESS;
3962 // copy usage_info back to userland
3963 if (usageInfo != NULL && user_memcpy(usageInfo, &usage_info,
3964 sizeof(usage_info)) != B_OK) {
3965 return B_BAD_ADDRESS;
3968 return foundChild;
3972 pid_t
3973 _user_process_info(pid_t process, int32 which)
3975 // we only allow to return the parent of the current process
3976 if (which == PARENT_ID
3977 && process != 0 && process != thread_get_current_thread()->team->id)
3978 return B_BAD_VALUE;
3980 pid_t result;
3981 switch (which) {
3982 case SESSION_ID:
3983 result = getsid(process);
3984 break;
3985 case GROUP_ID:
3986 result = getpgid(process);
3987 break;
3988 case PARENT_ID:
3989 result = getppid();
3990 break;
3991 default:
3992 return B_BAD_VALUE;
3995 return result >= 0 ? result : errno;
3999 pid_t
4000 _user_setpgid(pid_t processID, pid_t groupID)
4002 // setpgid() can be called either by the parent of the target process or
4003 // by the process itself to do one of two things:
4004 // * Create a new process group with the target process' ID and the target
4005 // process as group leader.
4006 // * Set the target process' process group to an already existing one in the
4007 // same session.
4009 if (groupID < 0)
4010 return B_BAD_VALUE;
4012 Team* currentTeam = thread_get_current_thread()->team;
4013 if (processID == 0)
4014 processID = currentTeam->id;
4016 // if the group ID is not specified, use the target process' ID
4017 if (groupID == 0)
4018 groupID = processID;
4020 // We loop when running into the following race condition: We create a new
4021 // process group, because there isn't one with that ID yet, but later when
4022 // trying to publish it, we find that someone else created and published
4023 // a group with that ID in the meantime. In that case we just restart the
4024 // whole action.
4025 while (true) {
4026 // Look up the process group by ID. If it doesn't exist yet and we are
4027 // allowed to create a new one, do that.
4028 ProcessGroup* group = ProcessGroup::Get(groupID);
4029 bool newGroup = false;
4030 if (group == NULL) {
4031 if (groupID != processID)
4032 return B_NOT_ALLOWED;
4034 group = new(std::nothrow) ProcessGroup(groupID);
4035 if (group == NULL)
4036 return B_NO_MEMORY;
4038 newGroup = true;
4040 BReference<ProcessGroup> groupReference(group, true);
4042 // get the target team
4043 Team* team = Team::Get(processID);
4044 if (team == NULL)
4045 return ESRCH;
4046 BReference<Team> teamReference(team, true);
4048 // lock the new process group and the team's current process group
4049 while (true) {
4050 // lock the team's current process group
4051 team->LockProcessGroup();
4053 ProcessGroup* oldGroup = team->group;
4054 if (oldGroup == group) {
4055 // it's the same as the target group, so just bail out
4056 oldGroup->Unlock();
4057 return group->id;
4060 oldGroup->AcquireReference();
4062 // lock the target process group, if locking order allows it
4063 if (newGroup || group->id > oldGroup->id) {
4064 group->Lock();
4065 break;
4068 // try to lock
4069 if (group->TryLock())
4070 break;
4072 // no dice -- unlock the team's current process group and relock in
4073 // the correct order
4074 oldGroup->Unlock();
4076 group->Lock();
4077 oldGroup->Lock();
4079 // check whether things are still the same
4080 TeamLocker teamLocker(team);
4081 if (team->group == oldGroup)
4082 break;
4084 // something changed -- unlock everything and retry
4085 teamLocker.Unlock();
4086 oldGroup->Unlock();
4087 group->Unlock();
4088 oldGroup->ReleaseReference();
4091 // we now have references and locks of both new and old process group
4092 BReference<ProcessGroup> oldGroupReference(team->group, true);
4093 AutoLocker<ProcessGroup> oldGroupLocker(team->group, true);
4094 AutoLocker<ProcessGroup> groupLocker(group, true);
4096 // also lock the target team and its parent
4097 team->LockTeamAndParent(false);
4098 TeamLocker parentLocker(team->parent, true);
4099 TeamLocker teamLocker(team, true);
4101 // perform the checks
4102 if (team == currentTeam) {
4103 // we set our own group
4105 // we must not change our process group ID if we're a session leader
4106 if (is_session_leader(currentTeam))
4107 return B_NOT_ALLOWED;
4108 } else {
4109 // Calling team != target team. The target team must be a child of
4110 // the calling team and in the same session. (If that's the case it
4111 // isn't a session leader either.)
4112 if (team->parent != currentTeam
4113 || team->session_id != currentTeam->session_id) {
4114 return B_NOT_ALLOWED;
4117 // The call is also supposed to fail on a child, when the child has
4118 // already executed exec*() [EACCES].
4119 if ((team->flags & TEAM_FLAG_EXEC_DONE) != 0)
4120 return EACCES;
4123 // If we created a new process group, publish it now.
4124 if (newGroup) {
4125 InterruptsSpinLocker groupHashLocker(sGroupHashLock);
4126 if (sGroupHash.Lookup(groupID)) {
4127 // A group with the group ID appeared since we first checked.
4128 // Back to square one.
4129 continue;
4132 group->PublishLocked(team->group->Session());
4133 } else if (group->Session()->id != team->session_id) {
4134 // The existing target process group belongs to a different session.
4135 // That's not allowed.
4136 return B_NOT_ALLOWED;
4139 // Everything is ready -- set the group.
4140 remove_team_from_group(team);
4141 insert_team_into_group(group, team);
4143 // Changing the process group might have changed the situation for a
4144 // parent waiting in wait_for_child(). Hence we notify it.
4145 team->parent->dead_children.condition_variable.NotifyAll();
4147 return group->id;
4152 pid_t
4153 _user_setsid(void)
4155 Team* team = thread_get_current_thread()->team;
4157 // create a new process group and session
4158 ProcessGroup* group = new(std::nothrow) ProcessGroup(team->id);
4159 if (group == NULL)
4160 return B_NO_MEMORY;
4161 BReference<ProcessGroup> groupReference(group, true);
4162 AutoLocker<ProcessGroup> groupLocker(group);
4164 ProcessSession* session = new(std::nothrow) ProcessSession(group->id);
4165 if (session == NULL)
4166 return B_NO_MEMORY;
4167 BReference<ProcessSession> sessionReference(session, true);
4169 // lock the team's current process group, parent, and the team itself
4170 team->LockTeamParentAndProcessGroup();
4171 BReference<ProcessGroup> oldGroupReference(team->group);
4172 AutoLocker<ProcessGroup> oldGroupLocker(team->group, true);
4173 TeamLocker parentLocker(team->parent, true);
4174 TeamLocker teamLocker(team, true);
4176 // the team must not already be a process group leader
4177 if (is_process_group_leader(team))
4178 return B_NOT_ALLOWED;
4180 // remove the team from the old and add it to the new process group
4181 remove_team_from_group(team);
4182 group->Publish(session);
4183 insert_team_into_group(group, team);
4185 // Changing the process group might have changed the situation for a
4186 // parent waiting in wait_for_child(). Hence we notify it.
4187 team->parent->dead_children.condition_variable.NotifyAll();
4189 return group->id;
4193 status_t
4194 _user_wait_for_team(team_id id, status_t* _userReturnCode)
4196 status_t returnCode;
4197 status_t status;
4199 if (_userReturnCode != NULL && !IS_USER_ADDRESS(_userReturnCode))
4200 return B_BAD_ADDRESS;
4202 status = wait_for_team(id, &returnCode);
4203 if (status >= B_OK && _userReturnCode != NULL) {
4204 if (user_memcpy(_userReturnCode, &returnCode, sizeof(returnCode))
4205 != B_OK)
4206 return B_BAD_ADDRESS;
4207 return B_OK;
4210 return syscall_restart_handle_post(status);
4214 thread_id
4215 _user_load_image(const char* const* userFlatArgs, size_t flatArgsSize,
4216 int32 argCount, int32 envCount, int32 priority, uint32 flags,
4217 port_id errorPort, uint32 errorToken)
4219 TRACE(("_user_load_image: argc = %" B_PRId32 "\n", argCount));
4221 if (argCount < 1)
4222 return B_BAD_VALUE;
4224 // copy and relocate the flat arguments
4225 char** flatArgs;
4226 status_t error = copy_user_process_args(userFlatArgs, flatArgsSize,
4227 argCount, envCount, flatArgs);
4228 if (error != B_OK)
4229 return error;
4231 thread_id thread = load_image_internal(flatArgs, _ALIGN(flatArgsSize),
4232 argCount, envCount, priority, B_CURRENT_TEAM, flags, errorPort,
4233 errorToken);
4235 free(flatArgs);
4236 // load_image_internal() unset our variable if it took over ownership
4238 return thread;
4242 void
4243 _user_exit_team(status_t returnValue)
4245 Thread* thread = thread_get_current_thread();
4246 Team* team = thread->team;
4248 // set this thread's exit status
4249 thread->exit.status = returnValue;
4251 // set the team exit status
4252 TeamLocker teamLocker(team);
4254 if (!team->exit.initialized) {
4255 team->exit.reason = CLD_EXITED;
4256 team->exit.signal = 0;
4257 team->exit.signaling_user = 0;
4258 team->exit.status = returnValue;
4259 team->exit.initialized = true;
4262 teamLocker.Unlock();
4264 // Stop the thread, if the team is being debugged and that has been
4265 // requested.
4266 if ((atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_PREVENT_EXIT) != 0)
4267 user_debug_stop_thread();
4269 // Send this thread a SIGKILL. This makes sure the thread will not return to
4270 // userland. The signal handling code forwards the signal to the main
4271 // thread (if that's not already this one), which will take the team down.
4272 Signal signal(SIGKILL, SI_USER, B_OK, team->id);
4273 send_signal_to_thread(thread, signal, 0);
4277 status_t
4278 _user_kill_team(team_id team)
4280 return kill_team(team);
4284 status_t
4285 _user_get_team_info(team_id id, team_info* userInfo)
4287 status_t status;
4288 team_info info;
4290 if (!IS_USER_ADDRESS(userInfo))
4291 return B_BAD_ADDRESS;
4293 status = _get_team_info(id, &info, sizeof(team_info));
4294 if (status == B_OK) {
4295 if (user_memcpy(userInfo, &info, sizeof(team_info)) < B_OK)
4296 return B_BAD_ADDRESS;
4299 return status;
4303 status_t
4304 _user_get_next_team_info(int32* userCookie, team_info* userInfo)
4306 status_t status;
4307 team_info info;
4308 int32 cookie;
4310 if (!IS_USER_ADDRESS(userCookie)
4311 || !IS_USER_ADDRESS(userInfo)
4312 || user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
4313 return B_BAD_ADDRESS;
4315 status = _get_next_team_info(&cookie, &info, sizeof(team_info));
4316 if (status != B_OK)
4317 return status;
4319 if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
4320 || user_memcpy(userInfo, &info, sizeof(team_info)) < B_OK)
4321 return B_BAD_ADDRESS;
4323 return status;
4327 team_id
4328 _user_get_current_team(void)
4330 return team_get_current_team_id();
4334 status_t
4335 _user_get_team_usage_info(team_id team, int32 who, team_usage_info* userInfo,
4336 size_t size)
4338 if (size != sizeof(team_usage_info))
4339 return B_BAD_VALUE;
4341 team_usage_info info;
4342 status_t status = common_get_team_usage_info(team, who, &info,
4343 B_CHECK_PERMISSION);
4345 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo)
4346 || user_memcpy(userInfo, &info, size) != B_OK) {
4347 return B_BAD_ADDRESS;
4350 return status;
4354 status_t
4355 _user_get_extended_team_info(team_id teamID, uint32 flags, void* buffer,
4356 size_t size, size_t* _sizeNeeded)
4358 // check parameters
4359 if ((buffer != NULL && !IS_USER_ADDRESS(buffer))
4360 || (buffer == NULL && size > 0)
4361 || _sizeNeeded == NULL || !IS_USER_ADDRESS(_sizeNeeded)) {
4362 return B_BAD_ADDRESS;
4365 KMessage info;
4367 if ((flags & B_TEAM_INFO_BASIC) != 0) {
4368 // allocate memory for a copy of the needed team data
4369 struct ExtendedTeamData {
4370 team_id id;
4371 pid_t group_id;
4372 pid_t session_id;
4373 uid_t real_uid;
4374 gid_t real_gid;
4375 uid_t effective_uid;
4376 gid_t effective_gid;
4377 char name[B_OS_NAME_LENGTH];
4380 ExtendedTeamData* teamClone
4381 = (ExtendedTeamData*)malloc(sizeof(ExtendedTeamData));
4382 // It would be nicer to use new, but then we'd have to use
4383 // ObjectDeleter and declare the structure outside of the function
4384 // due to template parameter restrictions.
4385 if (teamClone == NULL)
4386 return B_NO_MEMORY;
4387 MemoryDeleter teamCloneDeleter(teamClone);
4389 io_context* ioContext;
4391 // get the team structure
4392 Team* team = Team::GetAndLock(teamID);
4393 if (team == NULL)
4394 return B_BAD_TEAM_ID;
4395 BReference<Team> teamReference(team, true);
4396 TeamLocker teamLocker(team, true);
4398 // copy the data
4399 teamClone->id = team->id;
4400 strlcpy(teamClone->name, team->Name(), sizeof(teamClone->name));
4401 teamClone->group_id = team->group_id;
4402 teamClone->session_id = team->session_id;
4403 teamClone->real_uid = team->real_uid;
4404 teamClone->real_gid = team->real_gid;
4405 teamClone->effective_uid = team->effective_uid;
4406 teamClone->effective_gid = team->effective_gid;
4408 // also fetch a reference to the I/O context
4409 ioContext = team->io_context;
4410 vfs_get_io_context(ioContext);
4412 CObjectDeleter<io_context> ioContextPutter(ioContext,
4413 &vfs_put_io_context);
4415 // add the basic data to the info message
4416 if (info.AddInt32("id", teamClone->id) != B_OK
4417 || info.AddString("name", teamClone->name) != B_OK
4418 || info.AddInt32("process group", teamClone->group_id) != B_OK
4419 || info.AddInt32("session", teamClone->session_id) != B_OK
4420 || info.AddInt32("uid", teamClone->real_uid) != B_OK
4421 || info.AddInt32("gid", teamClone->real_gid) != B_OK
4422 || info.AddInt32("euid", teamClone->effective_uid) != B_OK
4423 || info.AddInt32("egid", teamClone->effective_gid) != B_OK) {
4424 return B_NO_MEMORY;
4427 // get the current working directory from the I/O context
4428 dev_t cwdDevice;
4429 ino_t cwdDirectory;
4431 MutexLocker ioContextLocker(ioContext->io_mutex);
4432 vfs_vnode_to_node_ref(ioContext->cwd, &cwdDevice, &cwdDirectory);
4435 if (info.AddInt32("cwd device", cwdDevice) != B_OK
4436 || info.AddInt64("cwd directory", cwdDirectory) != B_OK) {
4437 return B_NO_MEMORY;
4441 // TODO: Support the other flags!
4443 // copy the needed size and, if it fits, the message back to userland
4444 size_t sizeNeeded = info.ContentSize();
4445 if (user_memcpy(_sizeNeeded, &sizeNeeded, sizeof(sizeNeeded)) != B_OK)
4446 return B_BAD_ADDRESS;
4448 if (sizeNeeded > size)
4449 return B_BUFFER_OVERFLOW;
4451 if (user_memcpy(buffer, info.Buffer(), sizeNeeded) != B_OK)
4452 return B_BAD_ADDRESS;
4454 return B_OK;