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.
6 *
7 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
8 * Distributed under the terms of the NewOS License.
9 */
10
11
12/*!	Team functions */
13
14
15#include <team.h>
16
17#include <errno.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/wait.h>
22
23#include <OS.h>
24
25#include <AutoDeleter.h>
26#include <FindDirectory.h>
27
28#include <extended_system_info_defs.h>
29
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 <safemode.h>
47#include <sem.h>
48#include <syscall_process_info.h>
49#include <syscall_load_image.h>
50#include <syscall_restart.h>
51#include <syscalls.h>
52#include <tls.h>
53#include <tracing.h>
54#include <user_mutex.h>
55#include <user_runtime.h>
56#include <user_thread.h>
57#include <usergroup.h>
58#include <vfs.h>
59#include <vm/vm.h>
60#include <vm/VMAddressSpace.h>
61#include <util/AutoLock.h>
62#include <util/ThreadAutoLock.h>
63
64#include "TeamThreadTables.h"
65
66
67//#define TRACE_TEAM
68#ifdef TRACE_TEAM
69#	define TRACE(x) dprintf x
70#else
71#	define TRACE(x) ;
72#endif
73
74
75struct team_key {
76	team_id id;
77};
78
79struct team_arg {
80	char	*path;
81	char	**flat_args;
82	size_t	flat_args_size;
83	uint32	arg_count;
84	uint32	env_count;
85	mode_t	umask;
86	uint32	flags;
87	port_id	error_port;
88	uint32	error_token;
89};
90
91#define TEAM_ARGS_FLAG_NO_ASLR	0x01
92
93
94namespace {
95
96
97class TeamNotificationService : public DefaultNotificationService {
98public:
99							TeamNotificationService();
100
101			void			Notify(uint32 eventCode, Team* team);
102};
103
104
105// #pragma mark - TeamTable
106
107
108typedef BKernel::TeamThreadTable<Team> TeamTable;
109
110
111// #pragma mark - ProcessGroupHashDefinition
112
113
114struct ProcessGroupHashDefinition {
115	typedef pid_t			KeyType;
116	typedef	ProcessGroup	ValueType;
117
118	size_t HashKey(pid_t key) const
119	{
120		return key;
121	}
122
123	size_t Hash(ProcessGroup* value) const
124	{
125		return HashKey(value->id);
126	}
127
128	bool Compare(pid_t key, ProcessGroup* value) const
129	{
130		return value->id == key;
131	}
132
133	ProcessGroup*& GetLink(ProcessGroup* value) const
134	{
135		return value->next;
136	}
137};
138
139typedef BOpenHashTable<ProcessGroupHashDefinition> ProcessGroupHashTable;
140
141
142}	// unnamed namespace
143
144
145// #pragma mark -
146
147
148// the team_id -> Team hash table and the lock protecting it
149static TeamTable sTeamHash;
150static rw_spinlock sTeamHashLock = B_RW_SPINLOCK_INITIALIZER;
151
152// the pid_t -> ProcessGroup hash table and the lock protecting it
153static ProcessGroupHashTable sGroupHash;
154static spinlock sGroupHashLock = B_SPINLOCK_INITIALIZER;
155
156static Team* sKernelTeam = NULL;
157static bool sDisableUserAddOns = false;
158
159// A list of process groups of children of dying session leaders that need to
160// be signalled, if they have become orphaned and contain stopped processes.
161static ProcessGroupList sOrphanedCheckProcessGroups;
162static mutex sOrphanedCheckLock
163	= MUTEX_INITIALIZER("orphaned process group check");
164
165// some arbitrarily chosen limits -- should probably depend on the available
166// memory (the limit is not yet enforced)
167static int32 sMaxTeams = 2048;
168static int32 sUsedTeams = 1;
169
170static TeamNotificationService sNotificationService;
171
172static const size_t kTeamUserDataReservedSize	= 128 * B_PAGE_SIZE;
173static const size_t kTeamUserDataInitialSize	= 4 * B_PAGE_SIZE;
174
175
176// #pragma mark - TeamListIterator
177
178
179TeamListIterator::TeamListIterator()
180{
181	// queue the entry
182	InterruptsWriteSpinLocker locker(sTeamHashLock);
183	sTeamHash.InsertIteratorEntry(&fEntry);
184}
185
186
187TeamListIterator::~TeamListIterator()
188{
189	// remove the entry
190	InterruptsWriteSpinLocker locker(sTeamHashLock);
191	sTeamHash.RemoveIteratorEntry(&fEntry);
192}
193
194
195Team*
196TeamListIterator::Next()
197{
198	// get the next team -- if there is one, get reference for it
199	InterruptsWriteSpinLocker locker(sTeamHashLock);
200	Team* team = sTeamHash.NextElement(&fEntry);
201	if (team != NULL)
202		team->AcquireReference();
203
204	return team;
205}
206
207
208// #pragma mark - Tracing
209
210
211#if TEAM_TRACING
212namespace TeamTracing {
213
214class TeamForked : public AbstractTraceEntry {
215public:
216	TeamForked(thread_id forkedThread)
217		:
218		fForkedThread(forkedThread)
219	{
220		Initialized();
221	}
222
223	virtual void AddDump(TraceOutput& out)
224	{
225		out.Print("team forked, new thread %" B_PRId32, fForkedThread);
226	}
227
228private:
229	thread_id			fForkedThread;
230};
231
232
233class ExecTeam : public AbstractTraceEntry {
234public:
235	ExecTeam(const char* path, int32 argCount, const char* const* args,
236			int32 envCount, const char* const* env)
237		:
238		fArgCount(argCount),
239		fArgs(NULL)
240	{
241		fPath = alloc_tracing_buffer_strcpy(path, B_PATH_NAME_LENGTH,
242			false);
243
244		// determine the buffer size we need for the args
245		size_t argBufferSize = 0;
246		for (int32 i = 0; i < argCount; i++)
247			argBufferSize += strlen(args[i]) + 1;
248
249		// allocate a buffer
250		fArgs = (char*)alloc_tracing_buffer(argBufferSize);
251		if (fArgs) {
252			char* buffer = fArgs;
253			for (int32 i = 0; i < argCount; i++) {
254				size_t argSize = strlen(args[i]) + 1;
255				memcpy(buffer, args[i], argSize);
256				buffer += argSize;
257			}
258		}
259
260		// ignore env for the time being
261		(void)envCount;
262		(void)env;
263
264		Initialized();
265	}
266
267	virtual void AddDump(TraceOutput& out)
268	{
269		out.Print("team exec, \"%p\", args:", fPath);
270
271		if (fArgs != NULL) {
272			char* args = fArgs;
273			for (int32 i = 0; !out.IsFull() && i < fArgCount; i++) {
274				out.Print(" \"%s\"", args);
275				args += strlen(args) + 1;
276			}
277		} else
278			out.Print(" <too long>");
279	}
280
281private:
282	char*	fPath;
283	int32	fArgCount;
284	char*	fArgs;
285};
286
287
288static const char*
289job_control_state_name(job_control_state state)
290{
291	switch (state) {
292		case JOB_CONTROL_STATE_NONE:
293			return "none";
294		case JOB_CONTROL_STATE_STOPPED:
295			return "stopped";
296		case JOB_CONTROL_STATE_CONTINUED:
297			return "continued";
298		case JOB_CONTROL_STATE_DEAD:
299			return "dead";
300		default:
301			return "invalid";
302	}
303}
304
305
306class SetJobControlState : public AbstractTraceEntry {
307public:
308	SetJobControlState(team_id team, job_control_state newState, Signal* signal)
309		:
310		fTeam(team),
311		fNewState(newState),
312		fSignal(signal != NULL ? signal->Number() : 0)
313	{
314		Initialized();
315	}
316
317	virtual void AddDump(TraceOutput& out)
318	{
319		out.Print("team set job control state, team %" B_PRId32 ", "
320			"new state: %s, signal: %d",
321			fTeam, job_control_state_name(fNewState), fSignal);
322	}
323
324private:
325	team_id				fTeam;
326	job_control_state	fNewState;
327	int					fSignal;
328};
329
330
331class WaitForChild : public AbstractTraceEntry {
332public:
333	WaitForChild(pid_t child, uint32 flags)
334		:
335		fChild(child),
336		fFlags(flags)
337	{
338		Initialized();
339	}
340
341	virtual void AddDump(TraceOutput& out)
342	{
343		out.Print("team wait for child, child: %" B_PRId32 ", "
344			"flags: %#" B_PRIx32, fChild, fFlags);
345	}
346
347private:
348	pid_t	fChild;
349	uint32	fFlags;
350};
351
352
353class WaitForChildDone : public AbstractTraceEntry {
354public:
355	WaitForChildDone(const job_control_entry& entry)
356		:
357		fState(entry.state),
358		fTeam(entry.thread),
359		fStatus(entry.status),
360		fReason(entry.reason),
361		fSignal(entry.signal)
362	{
363		Initialized();
364	}
365
366	WaitForChildDone(status_t error)
367		:
368		fTeam(error)
369	{
370		Initialized();
371	}
372
373	virtual void AddDump(TraceOutput& out)
374	{
375		if (fTeam >= 0) {
376			out.Print("team wait for child done, team: %" B_PRId32 ", "
377				"state: %s, status: %#" B_PRIx32 ", reason: %#x, signal: %d\n",
378				fTeam, job_control_state_name(fState), fStatus, fReason,
379				fSignal);
380		} else {
381			out.Print("team wait for child failed, error: "
382				"%#" B_PRIx32 ", ", fTeam);
383		}
384	}
385
386private:
387	job_control_state	fState;
388	team_id				fTeam;
389	status_t			fStatus;
390	uint16				fReason;
391	uint16				fSignal;
392};
393
394}	// namespace TeamTracing
395
396#	define T(x) new(std::nothrow) TeamTracing::x;
397#else
398#	define T(x) ;
399#endif
400
401
402//	#pragma mark - TeamNotificationService
403
404
405TeamNotificationService::TeamNotificationService()
406	: DefaultNotificationService("teams")
407{
408}
409
410
411void
412TeamNotificationService::Notify(uint32 eventCode, Team* team)
413{
414	char eventBuffer[128];
415	KMessage event;
416	event.SetTo(eventBuffer, sizeof(eventBuffer), TEAM_MONITOR);
417	event.AddInt32("event", eventCode);
418	event.AddInt32("team", team->id);
419	event.AddPointer("teamStruct", team);
420
421	DefaultNotificationService::Notify(event, eventCode);
422}
423
424
425//	#pragma mark - Team
426
427
428Team::Team(team_id id, bool kernel)
429{
430	// allocate an ID
431	this->id = id;
432	visible = true;
433
434	hash_next = siblings_next = parent = children = group_next = NULL;
435	serial_number = -1;
436
437	group_id = session_id = -1;
438	group = NULL;
439
440	num_threads = 0;
441	state = TEAM_STATE_BIRTH;
442	flags = 0;
443	io_context = NULL;
444	user_mutex_context = NULL;
445	realtime_sem_context = NULL;
446	xsi_sem_context = NULL;
447	death_entry = NULL;
448	list_init(&dead_threads);
449
450	dead_children.condition_variable.Init(&dead_children, "team children");
451	dead_children.count = 0;
452	dead_children.kernel_time = 0;
453	dead_children.user_time = 0;
454
455	job_control_entry = new(nothrow) ::job_control_entry;
456	if (job_control_entry != NULL) {
457		job_control_entry->state = JOB_CONTROL_STATE_NONE;
458		job_control_entry->thread = id;
459		job_control_entry->team = this;
460	}
461
462	address_space = NULL;
463	main_thread = NULL;
464	thread_list = NULL;
465	loading_info = NULL;
466
467	list_init(&image_list);
468	list_init(&watcher_list);
469	list_init(&sem_list);
470	list_init_etc(&port_list, port_team_link_offset());
471
472	user_data = 0;
473	user_data_area = -1;
474	used_user_data = 0;
475	user_data_size = 0;
476	free_user_threads = NULL;
477
478	commpage_address = NULL;
479
480	clear_team_debug_info(&debug_info, true);
481
482	dead_threads_kernel_time = 0;
483	dead_threads_user_time = 0;
484	cpu_clock_offset = 0;
485	B_INITIALIZE_SPINLOCK(&time_lock);
486
487	saved_set_uid = real_uid = effective_uid = -1;
488	saved_set_gid = real_gid = effective_gid = -1;
489
490	// exit status -- setting initialized to false suffices
491	exit.initialized = false;
492
493	B_INITIALIZE_SPINLOCK(&signal_lock);
494
495	// init mutex
496	if (kernel) {
497		mutex_init(&fLock, "Team:kernel");
498	} else {
499		char lockName[16];
500		snprintf(lockName, sizeof(lockName), "Team:%" B_PRId32, id);
501		mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
502	}
503
504	fName[0] = '\0';
505	fArgs[0] = '\0';
506
507	fQueuedSignalsCounter = new(std::nothrow) BKernel::QueuedSignalsCounter(
508		kernel ? -1 : MAX_QUEUED_SIGNALS);
509	memset(fSignalActions, 0, sizeof(fSignalActions));
510	fUserDefinedTimerCount = 0;
511
512	fCoreDumpCondition = NULL;
513}
514
515
516Team::~Team()
517{
518	// get rid of all associated data
519	PrepareForDeletion();
520
521	if (io_context != NULL)
522		vfs_put_io_context(io_context);
523	delete_owned_ports(this);
524	sem_delete_owned_sems(this);
525
526	DeleteUserTimers(false);
527
528	fPendingSignals.Clear();
529
530	if (fQueuedSignalsCounter != NULL)
531		fQueuedSignalsCounter->ReleaseReference();
532
533	while (thread_death_entry* threadDeathEntry
534			= (thread_death_entry*)list_remove_head_item(&dead_threads)) {
535		free(threadDeathEntry);
536	}
537
538	while (::job_control_entry* entry = dead_children.entries.RemoveHead())
539		delete entry;
540
541	while (free_user_thread* entry = free_user_threads) {
542		free_user_threads = entry->next;
543		free(entry);
544	}
545
546	delete job_control_entry;
547		// usually already NULL and transferred to the parent
548
549	mutex_destroy(&fLock);
550}
551
552
553/*static*/ Team*
554Team::Create(team_id id, const char* name, bool kernel)
555{
556	// create the team object
557	Team* team = new(std::nothrow) Team(id, kernel);
558	if (team == NULL)
559		return NULL;
560	ObjectDeleter<Team> teamDeleter(team);
561
562	if (name != NULL)
563		team->SetName(name);
564
565	// check initialization
566	if (team->job_control_entry == NULL || team->fQueuedSignalsCounter == NULL)
567		return NULL;
568
569	// finish initialization (arch specifics)
570	if (arch_team_init_team_struct(team, kernel) != B_OK)
571		return NULL;
572
573	if (!kernel) {
574		status_t error = user_timer_create_team_timers(team);
575		if (error != B_OK)
576			return NULL;
577	}
578
579	team->start_time = system_time();
580
581	// everything went fine
582	return teamDeleter.Detach();
583}
584
585
586/*!	\brief Returns the team with the given ID.
587	Returns a reference to the team.
588	Team and thread spinlock must not be held.
589*/
590/*static*/ Team*
591Team::Get(team_id id)
592{
593	if (id == B_CURRENT_TEAM) {
594		Team* team = thread_get_current_thread()->team;
595		team->AcquireReference();
596		return team;
597	}
598
599	InterruptsReadSpinLocker locker(sTeamHashLock);
600	Team* team = sTeamHash.Lookup(id);
601	if (team != NULL)
602		team->AcquireReference();
603	return team;
604}
605
606
607/*!	\brief Returns the team with the given ID in a locked state.
608	Returns a reference to the team.
609	Team and thread spinlock must not be held.
610*/
611/*static*/ Team*
612Team::GetAndLock(team_id id)
613{
614	// get the team
615	Team* team = Get(id);
616	if (team == NULL)
617		return NULL;
618
619	// lock it
620	team->Lock();
621
622	// only return the team, when it isn't already dying
623	if (team->state >= TEAM_STATE_SHUTDOWN) {
624		team->Unlock();
625		team->ReleaseReference();
626		return NULL;
627	}
628
629	return team;
630}
631
632
633/*!	Locks the team and its parent team (if any).
634	The caller must hold a reference to the team or otherwise make sure that
635	it won't be deleted.
636	If the team doesn't have a parent, only the team itself is locked. If the
637	team's parent is the kernel team and \a dontLockParentIfKernel is \c true,
638	only the team itself is locked.
639
640	\param dontLockParentIfKernel If \c true, the team's parent team is only
641		locked, if it is not the kernel team.
642*/
643void
644Team::LockTeamAndParent(bool dontLockParentIfKernel)
645{
646	// The locking order is parent -> child. Since the parent can change as long
647	// as we don't lock the team, we need to do a trial and error loop.
648	Lock();
649
650	while (true) {
651		// If the team doesn't have a parent, we're done. Otherwise try to lock
652		// the parent.This will succeed in most cases, simplifying things.
653		Team* parent = this->parent;
654		if (parent == NULL || (dontLockParentIfKernel && parent == sKernelTeam)
655			|| parent->TryLock()) {
656			return;
657		}
658
659		// get a temporary reference to the parent, unlock this team, lock the
660		// parent, and re-lock this team
661		BReference<Team> parentReference(parent);
662
663		Unlock();
664		parent->Lock();
665		Lock();
666
667		// If the parent hasn't changed in the meantime, we're done.
668		if (this->parent == parent)
669			return;
670
671		// The parent has changed -- unlock and retry.
672		parent->Unlock();
673	}
674}
675
676
677/*!	Unlocks the team and its parent team (if any).
678*/
679void
680Team::UnlockTeamAndParent()
681{
682	if (parent != NULL)
683		parent->Unlock();
684
685	Unlock();
686}
687
688
689/*!	Locks the team, its parent team (if any), and the team's process group.
690	The caller must hold a reference to the team or otherwise make sure that
691	it won't be deleted.
692	If the team doesn't have a parent, only the team itself is locked.
693*/
694void
695Team::LockTeamParentAndProcessGroup()
696{
697	LockTeamAndProcessGroup();
698
699	// We hold the group's and the team's lock, but not the parent team's lock.
700	// If we have a parent, try to lock it.
701	if (this->parent == NULL || this->parent->TryLock())
702		return;
703
704	// No success -- unlock the team and let LockTeamAndParent() do the rest of
705	// the job.
706	Unlock();
707	LockTeamAndParent(false);
708}
709
710
711/*!	Unlocks the team, its parent team (if any), and the team's process group.
712*/
713void
714Team::UnlockTeamParentAndProcessGroup()
715{
716	group->Unlock();
717
718	if (parent != NULL)
719		parent->Unlock();
720
721	Unlock();
722}
723
724
725void
726Team::LockTeamAndProcessGroup()
727{
728	// The locking order is process group -> child. Since the process group can
729	// change as long as we don't lock the team, we need to do a trial and error
730	// loop.
731	Lock();
732
733	while (true) {
734		// Try to lock the group. This will succeed in most cases, simplifying
735		// things.
736		ProcessGroup* group = this->group;
737		if (group == NULL)
738			return;
739
740		if (group->TryLock())
741			return;
742
743		// get a temporary reference to the group, unlock this team, lock the
744		// group, and re-lock this team
745		BReference<ProcessGroup> groupReference(group);
746
747		Unlock();
748		group->Lock();
749		Lock();
750
751		// If the group hasn't changed in the meantime, we're done.
752		if (this->group == group)
753			return;
754
755		// The group has changed -- unlock and retry.
756		group->Unlock();
757	}
758}
759
760
761void
762Team::UnlockTeamAndProcessGroup()
763{
764	group->Unlock();
765	Unlock();
766}
767
768
769void
770Team::SetName(const char* name)
771{
772	if (const char* lastSlash = strrchr(name, '/'))
773		name = lastSlash + 1;
774
775	strlcpy(fName, name, B_OS_NAME_LENGTH);
776}
777
778
779void
780Team::SetArgs(const char* args)
781{
782	strlcpy(fArgs, args, sizeof(fArgs));
783}
784
785
786void
787Team::SetArgs(const char* path, const char* const* otherArgs, int otherArgCount)
788{
789	fArgs[0] = '\0';
790	strlcpy(fArgs, path, sizeof(fArgs));
791	for (int i = 0; i < otherArgCount; i++) {
792		strlcat(fArgs, " ", sizeof(fArgs));
793		strlcat(fArgs, otherArgs[i], sizeof(fArgs));
794	}
795}
796
797
798void
799Team::ResetSignalsOnExec()
800{
801	// We are supposed to keep pending signals. Signal actions shall be reset
802	// partially: SIG_IGN and SIG_DFL dispositions shall be kept as they are
803	// (for SIGCHLD it's implementation-defined). Others shall be reset to
804	// SIG_DFL. SA_ONSTACK shall be cleared. There's no mention of the other
805	// flags, but since there aren't any handlers, they make little sense, so
806	// we clear them.
807
808	for (uint32 i = 1; i <= MAX_SIGNAL_NUMBER; i++) {
809		struct sigaction& action = SignalActionFor(i);
810		if (action.sa_handler != SIG_IGN && action.sa_handler != SIG_DFL)
811			action.sa_handler = SIG_DFL;
812
813		action.sa_mask = 0;
814		action.sa_flags = 0;
815		action.sa_userdata = NULL;
816	}
817}
818
819
820void
821Team::InheritSignalActions(Team* parent)
822{
823	memcpy(fSignalActions, parent->fSignalActions, sizeof(fSignalActions));
824}
825
826
827/*!	Adds the given user timer to the team and, if user-defined, assigns it an
828	ID.
829
830	The caller must hold the team's lock.
831
832	\param timer The timer to be added. If it doesn't have an ID yet, it is
833		considered user-defined and will be assigned an ID.
834	\return \c B_OK, if the timer was added successfully, another error code
835		otherwise.
836*/
837status_t
838Team::AddUserTimer(UserTimer* timer)
839{
840	// don't allow addition of timers when already shutting the team down
841	if (state >= TEAM_STATE_SHUTDOWN)
842		return B_BAD_TEAM_ID;
843
844	// If the timer is user-defined, check timer limit and increment
845	// user-defined count.
846	if (timer->ID() < 0 && !CheckAddUserDefinedTimer())
847		return EAGAIN;
848
849	fUserTimers.AddTimer(timer);
850
851	return B_OK;
852}
853
854
855/*!	Removes the given user timer from the team.
856
857	The caller must hold the team's lock.
858
859	\param timer The timer to be removed.
860
861*/
862void
863Team::RemoveUserTimer(UserTimer* timer)
864{
865	fUserTimers.RemoveTimer(timer);
866
867	if (timer->ID() >= USER_TIMER_FIRST_USER_DEFINED_ID)
868		UserDefinedTimersRemoved(1);
869}
870
871
872/*!	Deletes all (or all user-defined) user timers of the team.
873
874	Timer's belonging to the team's threads are not affected.
875	The caller must hold the team's lock.
876
877	\param userDefinedOnly If \c true, only the user-defined timers are deleted,
878		otherwise all timers are deleted.
879*/
880void
881Team::DeleteUserTimers(bool userDefinedOnly)
882{
883	int32 count = fUserTimers.DeleteTimers(userDefinedOnly);
884	UserDefinedTimersRemoved(count);
885}
886
887
888/*!	If not at the limit yet, increments the team's user-defined timer count.
889	\return \c true, if the limit wasn't reached yet, \c false otherwise.
890*/
891bool
892Team::CheckAddUserDefinedTimer()
893{
894	int32 oldCount = atomic_add(&fUserDefinedTimerCount, 1);
895	if (oldCount >= MAX_USER_TIMERS_PER_TEAM) {
896		atomic_add(&fUserDefinedTimerCount, -1);
897		return false;
898	}
899
900	return true;
901}
902
903
904/*!	Subtracts the given count for the team's user-defined timer count.
905	\param count The count to subtract.
906*/
907void
908Team::UserDefinedTimersRemoved(int32 count)
909{
910	atomic_add(&fUserDefinedTimerCount, -count);
911}
912
913
914void
915Team::DeactivateCPUTimeUserTimers()
916{
917	while (TeamTimeUserTimer* timer = fCPUTimeUserTimers.Head())
918		timer->Deactivate();
919
920	while (TeamUserTimeUserTimer* timer = fUserTimeUserTimers.Head())
921		timer->Deactivate();
922}
923
924
925/*!	Returns the team's current total CPU time (kernel + user + offset).
926
927	The caller must hold \c time_lock.
928
929	\param ignoreCurrentRun If \c true and the current thread is one team's
930		threads, don't add the time since the last time \c last_time was
931		updated. Should be used in "thread unscheduled" scheduler callbacks,
932		since although the thread is still running at that time, its time has
933		already been stopped.
934	\return The team's current total CPU time.
935*/
936bigtime_t
937Team::CPUTime(bool ignoreCurrentRun, Thread* lockedThread) const
938{
939	bigtime_t time = cpu_clock_offset + dead_threads_kernel_time
940		+ dead_threads_user_time;
941
942	Thread* currentThread = thread_get_current_thread();
943	bigtime_t now = system_time();
944
945	for (Thread* thread = thread_list; thread != NULL;
946			thread = thread->team_next) {
947		bool alreadyLocked = thread == lockedThread;
948		SpinLocker threadTimeLocker(thread->time_lock, alreadyLocked);
949		time += thread->kernel_time + thread->user_time;
950
951		if (thread->last_time != 0) {
952			if (!ignoreCurrentRun || thread != currentThread)
953				time += now - thread->last_time;
954		}
955
956		if (alreadyLocked)
957			threadTimeLocker.Detach();
958	}
959
960	return time;
961}
962
963
964/*!	Returns the team's current user CPU time.
965
966	The caller must hold \c time_lock.
967
968	\return The team's current user CPU time.
969*/
970bigtime_t
971Team::UserCPUTime() const
972{
973	bigtime_t time = dead_threads_user_time;
974
975	bigtime_t now = system_time();
976
977	for (Thread* thread = thread_list; thread != NULL;
978			thread = thread->team_next) {
979		SpinLocker threadTimeLocker(thread->time_lock);
980		time += thread->user_time;
981
982		if (thread->last_time != 0 && !thread->in_kernel)
983			time += now - thread->last_time;
984	}
985
986	return time;
987}
988
989
990//	#pragma mark - ProcessGroup
991
992
993ProcessGroup::ProcessGroup(pid_t id)
994	:
995	id(id),
996	teams(NULL),
997	fSession(NULL),
998	fInOrphanedCheckList(false)
999{
1000	char lockName[32];
1001	snprintf(lockName, sizeof(lockName), "Group:%" B_PRId32, id);
1002	mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
1003}
1004
1005
1006ProcessGroup::~ProcessGroup()
1007{
1008	TRACE(("ProcessGroup::~ProcessGroup(): id = %" B_PRId32 "\n", id));
1009
1010	// If the group is in the orphaned check list, remove it.
1011	MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
1012
1013	if (fInOrphanedCheckList)
1014		sOrphanedCheckProcessGroups.Remove(this);
1015
1016	orphanedCheckLocker.Unlock();
1017
1018	// remove group from the hash table and from the session
1019	if (fSession != NULL) {
1020		InterruptsSpinLocker groupHashLocker(sGroupHashLock);
1021		sGroupHash.RemoveUnchecked(this);
1022		groupHashLocker.Unlock();
1023
1024		fSession->ReleaseReference();
1025	}
1026
1027	mutex_destroy(&fLock);
1028}
1029
1030
1031/*static*/ ProcessGroup*
1032ProcessGroup::Get(pid_t id)
1033{
1034	InterruptsSpinLocker groupHashLocker(sGroupHashLock);
1035	ProcessGroup* group = sGroupHash.Lookup(id);
1036	if (group != NULL)
1037		group->AcquireReference();
1038	return group;
1039}
1040
1041
1042/*!	Adds the group the given session and makes it publicly accessible.
1043	The caller must not hold the process group hash lock.
1044*/
1045void
1046ProcessGroup::Publish(ProcessSession* session)
1047{
1048	InterruptsSpinLocker groupHashLocker(sGroupHashLock);
1049	PublishLocked(session);
1050}
1051
1052
1053/*!	Adds the group to the given session and makes it publicly accessible.
1054	The caller must hold the process group hash lock.
1055*/
1056void
1057ProcessGroup::PublishLocked(ProcessSession* session)
1058{
1059	ASSERT(sGroupHash.Lookup(this->id) == NULL);
1060
1061	fSession = session;
1062	fSession->AcquireReference();
1063
1064	sGroupHash.InsertUnchecked(this);
1065}
1066
1067
1068/*!	Checks whether the process group is orphaned.
1069	The caller must hold the group's lock.
1070	\return \c true, if the group is orphaned, \c false otherwise.
1071*/
1072bool
1073ProcessGroup::IsOrphaned() const
1074{
1075	// Orphaned Process Group: "A process group in which the parent of every
1076	// member is either itself a member of the group or is not a member of the
1077	// group's session." (Open Group Base Specs Issue 7)
1078	bool orphaned = true;
1079
1080	Team* team = teams;
1081	while (orphaned && team != NULL) {
1082		team->LockTeamAndParent(false);
1083
1084		Team* parent = team->parent;
1085		if (parent != NULL && parent->group_id != id
1086			&& parent->session_id == fSession->id) {
1087			orphaned = false;
1088		}
1089
1090		team->UnlockTeamAndParent();
1091
1092		team = team->group_next;
1093	}
1094
1095	return orphaned;
1096}
1097
1098
1099void
1100ProcessGroup::ScheduleOrphanedCheck()
1101{
1102	MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
1103
1104	if (!fInOrphanedCheckList) {
1105		sOrphanedCheckProcessGroups.Add(this);
1106		fInOrphanedCheckList = true;
1107	}
1108}
1109
1110
1111void
1112ProcessGroup::UnsetOrphanedCheck()
1113{
1114	fInOrphanedCheckList = false;
1115}
1116
1117
1118//	#pragma mark - ProcessSession
1119
1120
1121ProcessSession::ProcessSession(pid_t id)
1122	:
1123	id(id),
1124	controlling_tty(NULL),
1125	foreground_group(-1)
1126{
1127	char lockName[32];
1128	snprintf(lockName, sizeof(lockName), "Session:%" B_PRId32, id);
1129	mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
1130}
1131
1132
1133ProcessSession::~ProcessSession()
1134{
1135	mutex_destroy(&fLock);
1136}
1137
1138
1139//	#pragma mark - KDL functions
1140
1141
1142static void
1143_dump_team_info(Team* team)
1144{
1145	kprintf("TEAM: %p\n", team);
1146	kprintf("id:               %" B_PRId32 " (%#" B_PRIx32 ")\n", team->id,
1147		team->id);
1148	kprintf("serial_number:    %" B_PRId64 "\n", team->serial_number);
1149	kprintf("name:             '%s'\n", team->Name());
1150	kprintf("args:             '%s'\n", team->Args());
1151	kprintf("hash_next:        %p\n", team->hash_next);
1152	kprintf("parent:           %p", team->parent);
1153	if (team->parent != NULL) {
1154		kprintf(" (id = %" B_PRId32 ")\n", team->parent->id);
1155	} else
1156		kprintf("\n");
1157
1158	kprintf("children:         %p\n", team->children);
1159	kprintf("num_threads:      %d\n", team->num_threads);
1160	kprintf("state:            %d\n", team->state);
1161	kprintf("flags:            0x%" B_PRIx32 "\n", team->flags);
1162	kprintf("io_context:       %p\n", team->io_context);
1163	if (team->address_space)
1164		kprintf("address_space:    %p\n", team->address_space);
1165	kprintf("user data:        %p (area %" B_PRId32 ")\n",
1166		(void*)team->user_data, team->user_data_area);
1167	kprintf("free user thread: %p\n", team->free_user_threads);
1168	kprintf("main_thread:      %p\n", team->main_thread);
1169	kprintf("thread_list:      %p\n", team->thread_list);
1170	kprintf("group_id:         %" B_PRId32 "\n", team->group_id);
1171	kprintf("session_id:       %" B_PRId32 "\n", team->session_id);
1172}
1173
1174
1175static int
1176dump_team_info(int argc, char** argv)
1177{
1178	ulong arg;
1179	bool found = false;
1180
1181	if (argc < 2) {
1182		Thread* thread = thread_get_current_thread();
1183		if (thread != NULL && thread->team != NULL)
1184			_dump_team_info(thread->team);
1185		else
1186			kprintf("No current team!\n");
1187		return 0;
1188	}
1189
1190	arg = strtoul(argv[1], NULL, 0);
1191	if (IS_KERNEL_ADDRESS(arg)) {
1192		// semi-hack
1193		_dump_team_info((Team*)arg);
1194		return 0;
1195	}
1196
1197	// walk through the thread list, trying to match name or id
1198	for (TeamTable::Iterator it = sTeamHash.GetIterator();
1199		Team* team = it.Next();) {
1200		if ((team->Name() && strcmp(argv[1], team->Name()) == 0)
1201			|| team->id == (team_id)arg) {
1202			_dump_team_info(team);
1203			found = true;
1204			break;
1205		}
1206	}
1207
1208	if (!found)
1209		kprintf("team \"%s\" (%" B_PRId32 ") doesn't exist!\n", argv[1], (team_id)arg);
1210	return 0;
1211}
1212
1213
1214static int
1215dump_teams(int argc, char** argv)
1216{
1217	kprintf("%-*s       id  %-*s    name\n", B_PRINTF_POINTER_WIDTH, "team",
1218		B_PRINTF_POINTER_WIDTH, "parent");
1219
1220	for (TeamTable::Iterator it = sTeamHash.GetIterator();
1221		Team* team = it.Next();) {
1222		kprintf("%p%7" B_PRId32 "  %p  %s\n", team, team->id, team->parent, team->Name());
1223	}
1224
1225	return 0;
1226}
1227
1228
1229//	#pragma mark - Private functions
1230
1231
1232/*! Get the parent of a given process.
1233
1234	Used in the implementation of getppid (where a process can get its own
1235	parent, only) as well as in user_process_info where the information is
1236	available to anyone (allowing to display a tree of running processes)
1237*/
1238static pid_t
1239_getppid(pid_t id)
1240{
1241	if (id < 0) {
1242		errno = EINVAL;
1243		return -1;
1244	}
1245
1246	if (id == 0) {
1247		Team* team = thread_get_current_thread()->team;
1248		TeamLocker teamLocker(team);
1249		if (team->parent == NULL) {
1250			errno = EINVAL;
1251			return -1;
1252		}
1253		return team->parent->id;
1254	}
1255
1256	Team* team = Team::GetAndLock(id);
1257	if (team == NULL) {
1258		errno = ESRCH;
1259		return -1;
1260	}
1261
1262	pid_t parentID;
1263
1264	if (team->parent == NULL) {
1265		errno = EINVAL;
1266		parentID = -1;
1267	} else
1268		parentID = team->parent->id;
1269
1270	team->UnlockAndReleaseReference();
1271
1272	return parentID;
1273}
1274
1275
1276/*!	Inserts team \a team into the child list of team \a parent.
1277
1278	The caller must hold the lock of both \a parent and \a team.
1279
1280	\param parent The parent team.
1281	\param team The team to be inserted into \a parent's child list.
1282*/
1283static void
1284insert_team_into_parent(Team* parent, Team* team)
1285{
1286	ASSERT(parent != NULL);
1287
1288	team->siblings_next = parent->children;
1289	parent->children = team;
1290	team->parent = parent;
1291}
1292
1293
1294/*!	Removes team \a team from the child list of team \a parent.
1295
1296	The caller must hold the lock of both \a parent and \a team.
1297
1298	\param parent The parent team.
1299	\param team The team to be removed from \a parent's child list.
1300*/
1301static void
1302remove_team_from_parent(Team* parent, Team* team)
1303{
1304	Team* child;
1305	Team* last = NULL;
1306
1307	for (child = parent->children; child != NULL;
1308			child = child->siblings_next) {
1309		if (child == team) {
1310			if (last == NULL)
1311				parent->children = child->siblings_next;
1312			else
1313				last->siblings_next = child->siblings_next;
1314
1315			team->parent = NULL;
1316			break;
1317		}
1318		last = child;
1319	}
1320}
1321
1322
1323/*!	Returns whether the given team is a session leader.
1324	The caller must hold the team's lock or its process group's lock.
1325*/
1326static bool
1327is_session_leader(Team* team)
1328{
1329	return team->session_id == team->id;
1330}
1331
1332
1333/*!	Returns whether the given team is a process group leader.
1334	The caller must hold the team's lock or its process group's lock.
1335*/
1336static bool
1337is_process_group_leader(Team* team)
1338{
1339	return team->group_id == team->id;
1340}
1341
1342
1343/*!	Inserts the given team into the given process group.
1344	The caller must hold the process group's lock, the team's lock, and the
1345	team's parent's lock.
1346*/
1347static void
1348insert_team_into_group(ProcessGroup* group, Team* team)
1349{
1350	team->group = group;
1351	team->group_id = group->id;
1352	team->session_id = group->Session()->id;
1353
1354	team->group_next = group->teams;
1355	group->teams = team;
1356	group->AcquireReference();
1357}
1358
1359
1360/*!	Removes the given team from its process group.
1361
1362	The caller must hold the process group's lock, the team's lock, and the
1363	team's parent's lock. Interrupts must be enabled.
1364
1365	\param team The team that'll be removed from its process group.
1366*/
1367static void
1368remove_team_from_group(Team* team)
1369{
1370	ProcessGroup* group = team->group;
1371	Team* current;
1372	Team* last = NULL;
1373
1374	// the team must be in a process group to let this function have any effect
1375	if (group == NULL)
1376		return;
1377
1378	for (current = group->teams; current != NULL;
1379			current = current->group_next) {
1380		if (current == team) {
1381			if (last == NULL)
1382				group->teams = current->group_next;
1383			else
1384				last->group_next = current->group_next;
1385
1386			break;
1387		}
1388		last = current;
1389	}
1390
1391	team->group = NULL;
1392	team->group_next = NULL;
1393	team->group_id = -1;
1394
1395	group->ReleaseReference();
1396}
1397
1398
1399static status_t
1400create_team_user_data(Team* team, void* exactAddress = NULL)
1401{
1402	void* address;
1403	uint32 addressSpec;
1404
1405	if (exactAddress != NULL) {
1406		address = exactAddress;
1407		addressSpec = B_EXACT_ADDRESS;
1408	} else {
1409		address = (void*)KERNEL_USER_DATA_BASE;
1410		addressSpec = B_RANDOMIZED_BASE_ADDRESS;
1411	}
1412
1413	status_t result = vm_reserve_address_range(team->id, &address, addressSpec,
1414		kTeamUserDataReservedSize, RESERVED_AVOID_BASE);
1415
1416	virtual_address_restrictions virtualRestrictions = {};
1417	if (result == B_OK || exactAddress != NULL) {
1418		if (exactAddress != NULL)
1419			virtualRestrictions.address = exactAddress;
1420		else
1421			virtualRestrictions.address = address;
1422		virtualRestrictions.address_specification = B_EXACT_ADDRESS;
1423	} else {
1424		virtualRestrictions.address = (void*)KERNEL_USER_DATA_BASE;
1425		virtualRestrictions.address_specification = B_RANDOMIZED_BASE_ADDRESS;
1426	}
1427
1428	physical_address_restrictions physicalRestrictions = {};
1429	team->user_data_area = create_area_etc(team->id, "user area",
1430		kTeamUserDataInitialSize, B_FULL_LOCK,
1431		B_READ_AREA | B_WRITE_AREA | B_KERNEL_AREA, 0, 0,
1432		&virtualRestrictions, &physicalRestrictions, &address);
1433	if (team->user_data_area < 0)
1434		return team->user_data_area;
1435
1436	team->user_data = (addr_t)address;
1437	team->used_user_data = 0;
1438	team->user_data_size = kTeamUserDataInitialSize;
1439	team->free_user_threads = NULL;
1440
1441	return B_OK;
1442}
1443
1444
1445static void
1446delete_team_user_data(Team* team)
1447{
1448	if (team->user_data_area >= 0) {
1449		vm_delete_area(team->id, team->user_data_area, true);
1450		vm_unreserve_address_range(team->id, (void*)team->user_data,
1451			kTeamUserDataReservedSize);
1452
1453		team->user_data = 0;
1454		team->used_user_data = 0;
1455		team->user_data_size = 0;
1456		team->user_data_area = -1;
1457		while (free_user_thread* entry = team->free_user_threads) {
1458			team->free_user_threads = entry->next;
1459			free(entry);
1460		}
1461	}
1462}
1463
1464
1465static status_t
1466copy_user_process_args(const char* const* userFlatArgs, size_t flatArgsSize,
1467	int32 argCount, int32 envCount, char**& _flatArgs)
1468{
1469	if (argCount < 0 || envCount < 0)
1470		return B_BAD_VALUE;
1471
1472	if (flatArgsSize > MAX_PROCESS_ARGS_SIZE)
1473		return B_TOO_MANY_ARGS;
1474	if ((argCount + envCount + 2) * sizeof(char*) > flatArgsSize)
1475		return B_BAD_VALUE;
1476
1477	if (!IS_USER_ADDRESS(userFlatArgs))
1478		return B_BAD_ADDRESS;
1479
1480	// allocate kernel memory
1481	char** flatArgs = (char**)malloc(_ALIGN(flatArgsSize));
1482	if (flatArgs == NULL)
1483		return B_NO_MEMORY;
1484
1485	if (user_memcpy(flatArgs, userFlatArgs, flatArgsSize) != B_OK) {
1486		free(flatArgs);
1487		return B_BAD_ADDRESS;
1488	}
1489
1490	// check and relocate the array
1491	status_t error = B_OK;
1492	const char* stringBase = (char*)flatArgs + argCount + envCount + 2;
1493	const char* stringEnd = (char*)flatArgs + flatArgsSize;
1494	for (int32 i = 0; i < argCount + envCount + 2; i++) {
1495		if (i == argCount || i == argCount + envCount + 1) {
1496			// check array null termination
1497			if (flatArgs[i] != NULL) {
1498				error = B_BAD_VALUE;
1499				break;
1500			}
1501		} else {
1502			// check string
1503			char* arg = (char*)flatArgs + (flatArgs[i] - (char*)userFlatArgs);
1504			size_t maxLen = stringEnd - arg;
1505			if (arg < stringBase || arg >= stringEnd
1506					|| strnlen(arg, maxLen) == maxLen) {
1507				error = B_BAD_VALUE;
1508				break;
1509			}
1510
1511			flatArgs[i] = arg;
1512		}
1513	}
1514
1515	if (error == B_OK)
1516		_flatArgs = flatArgs;
1517	else
1518		free(flatArgs);
1519
1520	return error;
1521}
1522
1523
1524static void
1525free_team_arg(struct team_arg* teamArg)
1526{
1527	if (teamArg != NULL) {
1528		free(teamArg->flat_args);
1529		free(teamArg->path);
1530		free(teamArg);
1531	}
1532}
1533
1534
1535static status_t
1536create_team_arg(struct team_arg** _teamArg, const char* path, char** flatArgs,
1537	size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask,
1538	port_id port, uint32 token)
1539{
1540	struct team_arg* teamArg = (struct team_arg*)malloc(sizeof(team_arg));
1541	if (teamArg == NULL)
1542		return B_NO_MEMORY;
1543
1544	teamArg->path = strdup(path);
1545	if (teamArg->path == NULL) {
1546		free(teamArg);
1547		return B_NO_MEMORY;
1548	}
1549
1550	// copy the args over
1551	teamArg->flat_args = flatArgs;
1552	teamArg->flat_args_size = flatArgsSize;
1553	teamArg->arg_count = argCount;
1554	teamArg->env_count = envCount;
1555	teamArg->flags = 0;
1556	teamArg->umask = umask;
1557	teamArg->error_port = port;
1558	teamArg->error_token = token;
1559
1560	// determine the flags from the environment
1561	const char* const* env = flatArgs + argCount + 1;
1562	for (int32 i = 0; i < envCount; i++) {
1563		if (strcmp(env[i], "DISABLE_ASLR=1") == 0) {
1564			teamArg->flags |= TEAM_ARGS_FLAG_NO_ASLR;
1565			break;
1566		}
1567	}
1568
1569	*_teamArg = teamArg;
1570	return B_OK;
1571}
1572
1573
1574static status_t
1575team_create_thread_start_internal(void* args)
1576{
1577	status_t err;
1578	Thread* thread;
1579	Team* team;
1580	struct team_arg* teamArgs = (struct team_arg*)args;
1581	const char* path;
1582	addr_t entry;
1583	char** userArgs;
1584	char** userEnv;
1585	struct user_space_program_args* programArgs;
1586	uint32 argCount, envCount;
1587
1588	thread = thread_get_current_thread();
1589	team = thread->team;
1590	cache_node_launched(teamArgs->arg_count, teamArgs->flat_args);
1591
1592	TRACE(("team_create_thread_start: entry thread %" B_PRId32 "\n",
1593		thread->id));
1594
1595	// Main stack area layout is currently as follows (starting from 0):
1596	//
1597	// size								| usage
1598	// ---------------------------------+--------------------------------
1599	// USER_MAIN_THREAD_STACK_SIZE		| actual stack
1600	// TLS_SIZE							| TLS data
1601	// sizeof(user_space_program_args)	| argument structure for the runtime
1602	//									| loader
1603	// flat arguments size				| flat process arguments and environment
1604
1605	// TODO: ENV_SIZE is a) limited, and b) not used after libroot copied it to
1606	// the heap
1607	// TODO: we could reserve the whole USER_STACK_REGION upfront...
1608
1609	argCount = teamArgs->arg_count;
1610	envCount = teamArgs->env_count;
1611
1612	programArgs = (struct user_space_program_args*)(thread->user_stack_base
1613		+ thread->user_stack_size + TLS_SIZE);
1614
1615	userArgs = (char**)(programArgs + 1);
1616	userEnv = userArgs + argCount + 1;
1617	path = teamArgs->path;
1618
1619	if (user_strlcpy(programArgs->program_path, path,
1620				sizeof(programArgs->program_path)) < B_OK
1621		|| user_memcpy(&programArgs->arg_count, &argCount, sizeof(int32)) < B_OK
1622		|| user_memcpy(&programArgs->args, &userArgs, sizeof(char**)) < B_OK
1623		|| user_memcpy(&programArgs->env_count, &envCount, sizeof(int32)) < B_OK
1624		|| user_memcpy(&programArgs->env, &userEnv, sizeof(char**)) < B_OK
1625		|| user_memcpy(&programArgs->error_port, &teamArgs->error_port,
1626				sizeof(port_id)) < B_OK
1627		|| user_memcpy(&programArgs->error_token, &teamArgs->error_token,
1628				sizeof(uint32)) < B_OK
1629		|| user_memcpy(&programArgs->umask, &teamArgs->umask, sizeof(mode_t)) < B_OK
1630		|| user_memcpy(&programArgs->disable_user_addons,
1631			&sDisableUserAddOns, sizeof(bool)) < B_OK
1632		|| user_memcpy(userArgs, teamArgs->flat_args,
1633				teamArgs->flat_args_size) < B_OK) {
1634		// the team deletion process will clean this mess
1635		free_team_arg(teamArgs);
1636		return B_BAD_ADDRESS;
1637	}
1638
1639	TRACE(("team_create_thread_start: loading elf binary '%s'\n", path));
1640
1641	// set team args and update state
1642	team->Lock();
1643	team->SetArgs(path, teamArgs->flat_args + 1, argCount - 1);
1644	team->state = TEAM_STATE_NORMAL;
1645	team->Unlock();
1646
1647	free_team_arg(teamArgs);
1648		// the arguments are already on the user stack, we no longer need
1649		// them in this form
1650
1651	// Clone commpage area
1652	area_id commPageArea = clone_commpage_area(team->id,
1653		&team->commpage_address);
1654	if (commPageArea  < B_OK) {
1655		TRACE(("team_create_thread_start: clone_commpage_area() failed: %s\n",
1656			strerror(commPageArea)));
1657		return commPageArea;
1658	}
1659
1660	// Register commpage image
1661	image_id commPageImage = get_commpage_image();
1662	extended_image_info imageInfo;
1663	err = get_image_info(commPageImage, &imageInfo.basic_info);
1664	if (err != B_OK) {
1665		TRACE(("team_create_thread_start: get_image_info() failed: %s\n",
1666			strerror(err)));
1667		return err;
1668	}
1669	imageInfo.basic_info.text = team->commpage_address;
1670	imageInfo.text_delta = (ssize_t)(addr_t)team->commpage_address;
1671	imageInfo.symbol_table = NULL;
1672	imageInfo.symbol_hash = NULL;
1673	imageInfo.string_table = NULL;
1674	image_id image = register_image(team, &imageInfo, sizeof(imageInfo));
1675	if (image < 0) {
1676		TRACE(("team_create_thread_start: register_image() failed: %s\n",
1677			strerror(image)));
1678		return image;
1679	}
1680
1681	// NOTE: Normally arch_thread_enter_userspace() never returns, that is
1682	// automatic variables with function scope will never be destroyed.
1683	{
1684		// find runtime_loader path
1685		KPath runtimeLoaderPath;
1686		err = __find_directory(B_SYSTEM_DIRECTORY, gBootDevice, false,
1687			runtimeLoaderPath.LockBuffer(), runtimeLoaderPath.BufferSize());
1688		if (err < B_OK) {
1689			TRACE(("team_create_thread_start: find_directory() failed: %s\n",
1690				strerror(err)));
1691			return err;
1692		}
1693		runtimeLoaderPath.UnlockBuffer();
1694		err = runtimeLoaderPath.Append("runtime_loader");
1695
1696		if (err == B_OK) {
1697			err = elf_load_user_image(runtimeLoaderPath.Path(), team, 0,
1698				&entry);
1699		}
1700	}
1701
1702	if (err < B_OK) {
1703		// Luckily, we don't have to clean up the mess we created - that's
1704		// done for us by the normal team deletion process
1705		TRACE(("team_create_thread_start: elf_load_user_image() failed: "
1706			"%s\n", strerror(err)));
1707		return err;
1708	}
1709
1710	TRACE(("team_create_thread_start: loaded elf. entry = %#lx\n", entry));
1711
1712	// enter userspace -- returns only in case of error
1713	return thread_enter_userspace_new_team(thread, (addr_t)entry,
1714		programArgs, team->commpage_address);
1715}
1716
1717
1718static status_t
1719team_create_thread_start(void* args)
1720{
1721	team_create_thread_start_internal(args);
1722	team_init_exit_info_on_error(thread_get_current_thread()->team);
1723	thread_exit();
1724		// does not return
1725	return B_OK;
1726}
1727
1728
1729static thread_id
1730load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount,
1731	int32 envCount, int32 priority, team_id parentID, uint32 flags,
1732	port_id errorPort, uint32 errorToken)
1733{
1734	char** flatArgs = _flatArgs;
1735	thread_id thread;
1736	status_t status;
1737	struct team_arg* teamArgs;
1738	struct team_loading_info loadingInfo;
1739	ConditionVariableEntry loadingWaitEntry;
1740	io_context* parentIOContext = NULL;
1741	team_id teamID;
1742	bool teamLimitReached = false;
1743
1744	if (flatArgs == NULL || argCount == 0)
1745		return B_BAD_VALUE;
1746
1747	const char* path = flatArgs[0];
1748
1749	TRACE(("load_image_internal: name '%s', args = %p, argCount = %" B_PRId32
1750		"\n", path, flatArgs, argCount));
1751
1752	// cut the path from the main thread name
1753	const char* threadName = strrchr(path, '/');
1754	if (threadName != NULL)
1755		threadName++;
1756	else
1757		threadName = path;
1758
1759	// create the main thread object
1760	Thread* mainThread;
1761	status = Thread::Create(threadName, mainThread);
1762	if (status != B_OK)
1763		return status;
1764	BReference<Thread> mainThreadReference(mainThread, true);
1765
1766	// create team object
1767	Team* team = Team::Create(mainThread->id, path, false);
1768	if (team == NULL)
1769		return B_NO_MEMORY;
1770	BReference<Team> teamReference(team, true);
1771
1772	BReference<Team> teamLoadingReference;
1773	if ((flags & B_WAIT_TILL_LOADED) != 0) {
1774		loadingInfo.condition.Init(team, "image load");
1775		loadingInfo.condition.Add(&loadingWaitEntry);
1776		loadingInfo.result = B_ERROR;
1777		team->loading_info = &loadingInfo;
1778		teamLoadingReference = teamReference;
1779	}
1780
1781	// get the parent team
1782	Team* parent = Team::Get(parentID);
1783	if (parent == NULL)
1784		return B_BAD_TEAM_ID;
1785	BReference<Team> parentReference(parent, true);
1786
1787	parent->LockTeamAndProcessGroup();
1788	team->Lock();
1789
1790	// inherit the parent's user/group
1791	inherit_parent_user_and_group(team, parent);
1792
1793	// get a reference to the parent's I/O context -- we need it to create ours
1794	parentIOContext = parent->io_context;
1795	vfs_get_io_context(parentIOContext);
1796
1797	team->Unlock();
1798	parent->UnlockTeamAndProcessGroup();
1799
1800	// check the executable's set-user/group-id permission
1801	update_set_id_user_and_group(team, path);
1802
1803	status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, argCount,
1804		envCount, (mode_t)-1, errorPort, errorToken);
1805	if (status != B_OK)
1806		goto err1;
1807
1808	_flatArgs = NULL;
1809		// args are owned by the team_arg structure now
1810
1811	// create a new io_context for this team
1812	team->io_context = vfs_new_io_context(parentIOContext, true);
1813	if (!team->io_context) {
1814		status = B_NO_MEMORY;
1815		goto err2;
1816	}
1817
1818	// We don't need the parent's I/O context any longer.
1819	vfs_put_io_context(parentIOContext);
1820	parentIOContext = NULL;
1821
1822	// remove any fds that have the CLOEXEC flag set (emulating BeOS behaviour)
1823	vfs_exec_io_context(team->io_context);
1824
1825	// create an address space for this team
1826	status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false,
1827		&team->address_space);
1828	if (status != B_OK)
1829		goto err2;
1830
1831	team->address_space->SetRandomizingEnabled(
1832		(teamArgs->flags & TEAM_ARGS_FLAG_NO_ASLR) == 0);
1833
1834	// create the user data area
1835	status = create_team_user_data(team);
1836	if (status != B_OK)
1837		goto err4;
1838
1839	// insert the team into its parent and the teams hash
1840	parent->LockTeamAndProcessGroup();
1841	team->Lock();
1842
1843	{
1844		InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
1845
1846		sTeamHash.Insert(team);
1847		teamLimitReached = sUsedTeams >= sMaxTeams;
1848		if (!teamLimitReached)
1849			sUsedTeams++;
1850	}
1851
1852	insert_team_into_parent(parent, team);
1853	insert_team_into_group(parent->group, team);
1854
1855	team->Unlock();
1856	parent->UnlockTeamAndProcessGroup();
1857
1858	// notify team listeners
1859	sNotificationService.Notify(TEAM_ADDED, team);
1860
1861	if (teamLimitReached) {
1862		status = B_NO_MORE_TEAMS;
1863		goto err6;
1864	}
1865
1866	// In case we start the main thread, we shouldn't access the team object
1867	// afterwards, so cache the team's ID.
1868	teamID = team->id;
1869
1870	// Create a kernel thread, but under the context of the new team
1871	// The new thread will take over ownership of teamArgs.
1872	{
1873		ThreadCreationAttributes threadAttributes(team_create_thread_start,
1874			threadName, B_NORMAL_PRIORITY, teamArgs, teamID, mainThread);
1875		threadAttributes.additional_stack_size = sizeof(user_space_program_args)
1876			+ teamArgs->flat_args_size;
1877		thread = thread_create_thread(threadAttributes, false);
1878		if (thread < 0) {
1879			status = thread;
1880			goto err6;
1881		}
1882	}
1883
1884	// The team has been created successfully, so we keep the reference. Or
1885	// more precisely: It's owned by the team's main thread, now.
1886	teamReference.Detach();
1887
1888	// wait for the loader of the new team to finish its work
1889	if ((flags & B_WAIT_TILL_LOADED) != 0) {
1890		if (mainThread != NULL) {
1891			// resume the team's main thread
1892			thread_continue(mainThread);
1893		}
1894
1895		// Now wait until loading is finished. We will be woken either by the
1896		// thread, when it finished or aborted loading, or when the team is
1897		// going to die (e.g. is killed). In either case the one notifying is
1898		// responsible for unsetting `loading_info` in the team structure.
1899		loadingWaitEntry.Wait();
1900
1901		// We must synchronize with the thread that woke us up, to ensure
1902		// there are no remaining consumers of the team_loading_info.
1903		team->Lock();
1904		if (team->loading_info != NULL)
1905			panic("team loading wait complete, but loading_info != NULL");
1906		team->Unlock();
1907		teamLoadingReference.Unset();
1908
1909		if (loadingInfo.result < B_OK)
1910			return loadingInfo.result;
1911	}
1912
1913	// notify the debugger
1914	user_debug_team_created(teamID);
1915
1916	return thread;
1917
1918err6:
1919	// Remove the team structure from the process group, the parent team, and
1920	// the team hash table and delete the team structure.
1921	parent->LockTeamAndProcessGroup();
1922	team->Lock();
1923
1924	remove_team_from_group(team);
1925	remove_team_from_parent(team->parent, team);
1926
1927	team->Unlock();
1928	parent->UnlockTeamAndProcessGroup();
1929
1930	{
1931		InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
1932		sTeamHash.Remove(team);
1933		if (!teamLimitReached)
1934			sUsedTeams--;
1935	}
1936
1937	sNotificationService.Notify(TEAM_REMOVED, team);
1938
1939	delete_team_user_data(team);
1940err4:
1941	team->address_space->Put();
1942err2:
1943	free_team_arg(teamArgs);
1944err1:
1945	if (parentIOContext != NULL)
1946		vfs_put_io_context(parentIOContext);
1947
1948	return status;
1949}
1950
1951
1952/*!	Almost shuts down the current team and loads a new image into it.
1953	If successful, this function does not return and will takeover ownership of
1954	the arguments provided.
1955	This function may only be called in a userland team (caused by one of the
1956	exec*() syscalls).
1957*/
1958static status_t
1959exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize,
1960	int32 argCount, int32 envCount, mode_t umask)
1961{
1962	// NOTE: Since this function normally doesn't return, don't use automatic
1963	// variables that need destruction in the function scope.
1964	char** flatArgs = _flatArgs;
1965	Team* team = thread_get_current_thread()->team;
1966	struct team_arg* teamArgs;
1967	const char* threadName;
1968	thread_id nubThreadID = -1;
1969
1970	TRACE(("exec_team(path = \"%s\", argc = %" B_PRId32 ", envCount = %"
1971		B_PRId32 "): team %" B_PRId32 "\n", path, argCount, envCount,
1972		team->id));
1973
1974	T(ExecTeam(path, argCount, flatArgs, envCount, flatArgs + argCount + 1));
1975
1976	// switching the kernel at run time is probably not a good idea :)
1977	if (team == team_get_kernel_team())
1978		return B_NOT_ALLOWED;
1979
1980	// we currently need to be single threaded here
1981	// TODO: maybe we should just kill all other threads and
1982	//	make the current thread the team's main thread?
1983	Thread* currentThread = thread_get_current_thread();
1984	if (currentThread != team->main_thread)
1985		return B_NOT_ALLOWED;
1986
1987	// The debug nub thread, a pure kernel thread, is allowed to survive.
1988	// We iterate through the thread list to make sure that there's no other
1989	// thread.
1990	TeamLocker teamLocker(team);
1991	InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
1992
1993	if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED)
1994		nubThreadID = team->debug_info.nub_thread;
1995
1996	debugInfoLocker.Unlock();
1997
1998	for (Thread* thread = team->thread_list; thread != NULL;
1999			thread = thread->team_next) {
2000		if (thread != team->main_thread && thread->id != nubThreadID)
2001			return B_NOT_ALLOWED;
2002	}
2003
2004	team->DeleteUserTimers(true);
2005	team->ResetSignalsOnExec();
2006
2007	teamLocker.Unlock();
2008
2009	status_t status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize,
2010		argCount, envCount, umask, -1, 0);
2011	if (status != B_OK)
2012		return status;
2013
2014	_flatArgs = NULL;
2015		// args are owned by the team_arg structure now
2016
2017	// TODO: remove team resources if there are any left
2018	// thread_atkernel_exit() might not be called at all
2019
2020	thread_reset_for_exec();
2021
2022	user_debug_prepare_for_exec();
2023
2024	delete_team_user_data(team);
2025	vm_delete_areas(team->address_space, false);
2026	xsi_sem_undo(team);
2027	delete_owned_ports(team);
2028	sem_delete_owned_sems(team);
2029	remove_images(team);
2030	vfs_exec_io_context(team->io_context);
2031	delete_user_mutex_context(team->user_mutex_context);
2032	team->user_mutex_context = NULL;
2033	delete_realtime_sem_context(team->realtime_sem_context);
2034	team->realtime_sem_context = NULL;
2035
2036	// update ASLR
2037	team->address_space->SetRandomizingEnabled(
2038		(teamArgs->flags & TEAM_ARGS_FLAG_NO_ASLR) == 0);
2039
2040	status = create_team_user_data(team);
2041	if (status != B_OK) {
2042		// creating the user data failed -- we're toast
2043		free_team_arg(teamArgs);
2044		exit_thread(status);
2045		return status;
2046	}
2047
2048	user_debug_finish_after_exec();
2049
2050	// rename the team
2051
2052	team->Lock();
2053	team->SetName(path);
2054	team->Unlock();
2055
2056	// cut the path from the team name and rename the main thread, too
2057	threadName = strrchr(path, '/');
2058	if (threadName != NULL)
2059		threadName++;
2060	else
2061		threadName = path;
2062	rename_thread(thread_get_current_thread_id(), threadName);
2063
2064	atomic_or(&team->flags, TEAM_FLAG_EXEC_DONE);
2065
2066	// Update user/group according to the executable's set-user/group-id
2067	// permission.
2068	update_set_id_user_and_group(team, path);
2069
2070	user_debug_team_exec();
2071
2072	// notify team listeners
2073	sNotificationService.Notify(TEAM_EXEC, team);
2074
2075	// get a user thread for the thread
2076	user_thread* userThread = team_allocate_user_thread(team);
2077		// cannot fail (the allocation for the team would have failed already)
2078	ThreadLocker currentThreadLocker(currentThread);
2079	currentThread->user_thread = userThread;
2080	currentThreadLocker.Unlock();
2081
2082	// create the user stack for the thread
2083	status = thread_create_user_stack(currentThread->team, currentThread, NULL,
2084		0, sizeof(user_space_program_args) + teamArgs->flat_args_size);
2085	if (status == B_OK) {
2086		// prepare the stack, load the runtime loader, and enter userspace
2087		team_create_thread_start(teamArgs);
2088			// does never return
2089	} else
2090		free_team_arg(teamArgs);
2091
2092	// Sorry, we have to kill ourselves, there is no way out anymore
2093	// (without any areas left and all that).
2094	exit_thread(status);
2095
2096	// We return a status here since the signal that is sent by the
2097	// call above is not immediately handled.
2098	return B_ERROR;
2099}
2100
2101
2102static thread_id
2103fork_team(void)
2104{
2105	Thread* parentThread = thread_get_current_thread();
2106	Team* parentTeam = parentThread->team;
2107	Team* team;
2108	arch_fork_arg* forkArgs;
2109	struct area_info info;
2110	thread_id threadID;
2111	status_t status;
2112	ssize_t areaCookie;
2113	bool teamLimitReached = false;
2114
2115	TRACE(("fork_team(): team %" B_PRId32 "\n", parentTeam->id));
2116
2117	if (parentTeam == team_get_kernel_team())
2118		return B_NOT_ALLOWED;
2119
2120	// create a new team
2121	// TODO: this is very similar to load_image_internal() - maybe we can do
2122	// something about it :)
2123
2124	// create the main thread object
2125	Thread* thread;
2126	status = Thread::Create(parentThread->name, thread);
2127	if (status != B_OK)
2128		return status;
2129	BReference<Thread> threadReference(thread, true);
2130
2131	// create the team object
2132	team = Team::Create(thread->id, NULL, false);
2133	if (team == NULL)
2134		return B_NO_MEMORY;
2135
2136	parentTeam->LockTeamAndProcessGroup();
2137	team->Lock();
2138
2139	team->SetName(parentTeam->Name());
2140	team->SetArgs(parentTeam->Args());
2141
2142	team->commpage_address = parentTeam->commpage_address;
2143
2144	// Inherit the parent's user/group.
2145	inherit_parent_user_and_group(team, parentTeam);
2146
2147	// inherit signal handlers
2148	team->InheritSignalActions(parentTeam);
2149
2150	team->Unlock();
2151	parentTeam->UnlockTeamAndProcessGroup();
2152
2153	// inherit some team debug flags
2154	team->debug_info.flags |= atomic_get(&parentTeam->debug_info.flags)
2155		& B_TEAM_DEBUG_INHERITED_FLAGS;
2156
2157	forkArgs = (arch_fork_arg*)malloc(sizeof(arch_fork_arg));
2158	if (forkArgs == NULL) {
2159		status = B_NO_MEMORY;
2160		goto err1;
2161	}
2162
2163	// create a new io_context for this team
2164	team->io_context = vfs_new_io_context(parentTeam->io_context, false);
2165	if (!team->io_context) {
2166		status = B_NO_MEMORY;
2167		goto err2;
2168	}
2169
2170	// duplicate the realtime sem context
2171	if (parentTeam->realtime_sem_context) {
2172		team->realtime_sem_context = clone_realtime_sem_context(
2173			parentTeam->realtime_sem_context);
2174		if (team->realtime_sem_context == NULL) {
2175			status = B_NO_MEMORY;
2176			goto err2;
2177		}
2178	}
2179
2180	// create an address space for this team
2181	status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false,
2182		&team->address_space);
2183	if (status < B_OK)
2184		goto err3;
2185
2186	// copy all areas of the team
2187	// TODO: should be able to handle stack areas differently (ie. don't have
2188	// them copy-on-write)
2189
2190	areaCookie = 0;
2191	while (get_next_area_info(B_CURRENT_TEAM, &areaCookie, &info) == B_OK) {
2192		if (info.area == parentTeam->user_data_area) {
2193			// don't clone the user area; just create a new one
2194			status = create_team_user_data(team, info.address);
2195			if (status != B_OK)
2196				break;
2197
2198			thread->user_thread = team_allocate_user_thread(team);
2199		} else {
2200			void* address;
2201			area_id area = vm_copy_area(team->address_space->ID(), info.name,
2202				&address, B_CLONE_ADDRESS, info.area);
2203			if (area < B_OK) {
2204				status = area;
2205				break;
2206			}
2207
2208			if (info.area == parentThread->user_stack_area)
2209				thread->user_stack_area = area;
2210		}
2211	}
2212
2213	if (status < B_OK)
2214		goto err4;
2215
2216	if (thread->user_thread == NULL) {
2217#if KDEBUG
2218		panic("user data area not found, parent area is %" B_PRId32,
2219			parentTeam->user_data_area);
2220#endif
2221		status = B_ERROR;
2222		goto err4;
2223	}
2224
2225	thread->user_stack_base = parentThread->user_stack_base;
2226	thread->user_stack_size = parentThread->user_stack_size;
2227	thread->user_local_storage = parentThread->user_local_storage;
2228	thread->sig_block_mask = parentThread->sig_block_mask;
2229	thread->signal_stack_base = parentThread->signal_stack_base;
2230	thread->signal_stack_size = parentThread->signal_stack_size;
2231	thread->signal_stack_enabled = parentThread->signal_stack_enabled;
2232
2233	arch_store_fork_frame(forkArgs);
2234
2235	// copy image list
2236	if (copy_images(parentTeam->id, team) != B_OK)
2237		goto err5;
2238
2239	// insert the team into its parent and the teams hash
2240	parentTeam->LockTeamAndProcessGroup();
2241	team->Lock();
2242
2243	{
2244		InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
2245
2246		sTeamHash.Insert(team);
2247		teamLimitReached = sUsedTeams >= sMaxTeams;
2248		if (!teamLimitReached)
2249			sUsedTeams++;
2250	}
2251
2252	insert_team_into_parent(parentTeam, team);
2253	insert_team_into_group(parentTeam->group, team);
2254
2255	team->Unlock();
2256	parentTeam->UnlockTeamAndProcessGroup();
2257
2258	// notify team listeners
2259	sNotificationService.Notify(TEAM_ADDED, team);
2260
2261	if (teamLimitReached) {
2262		status = B_NO_MORE_TEAMS;
2263		goto err6;
2264	}
2265
2266	// create the main thread
2267	{
2268		ThreadCreationAttributes threadCreationAttributes(NULL,
2269			parentThread->name, parentThread->priority, NULL, team->id, thread);
2270		threadCreationAttributes.forkArgs = forkArgs;
2271		threadCreationAttributes.flags |= THREAD_CREATION_FLAG_DEFER_SIGNALS;
2272		threadID = thread_create_thread(threadCreationAttributes, false);
2273		if (threadID < 0) {
2274			status = threadID;
2275			goto err6;
2276		}
2277	}
2278
2279	// notify the debugger
2280	user_debug_team_created(team->id);
2281
2282	T(TeamForked(threadID));
2283
2284	resume_thread(threadID);
2285	return threadID;
2286
2287err6:
2288	// Remove the team structure from the process group, the parent team, and
2289	// the team hash table and delete the team structure.
2290	parentTeam->LockTeamAndProcessGroup();
2291	team->Lock();
2292
2293	remove_team_from_group(team);
2294	remove_team_from_parent(team->parent, team);
2295
2296	team->Unlock();
2297	parentTeam->UnlockTeamAndProcessGroup();
2298
2299	{
2300		InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
2301		sTeamHash.Remove(team);
2302		if (!teamLimitReached)
2303			sUsedTeams--;
2304	}
2305
2306	sNotificationService.Notify(TEAM_REMOVED, team);
2307err5:
2308	remove_images(team);
2309err4:
2310	team->address_space->RemoveAndPut();
2311err3:
2312	delete_realtime_sem_context(team->realtime_sem_context);
2313err2:
2314	free(forkArgs);
2315err1:
2316	team->ReleaseReference();
2317
2318	return status;
2319}
2320
2321
2322/*!	Returns if the specified team \a parent has any children belonging to the
2323	process group with the specified ID \a groupID.
2324	The caller must hold \a parent's lock.
2325*/
2326static bool
2327has_children_in_group(Team* parent, pid_t groupID)
2328{
2329	for (Team* child = parent->children; child != NULL;
2330			child = child->siblings_next) {
2331		TeamLocker childLocker(child);
2332		if (child->group_id == groupID)
2333			return true;
2334	}
2335
2336	return false;
2337}
2338
2339
2340/*!	Returns the first job control entry from \a children, which matches \a id.
2341	\a id can be:
2342	- \code > 0 \endcode: Matching an entry with that team ID.
2343	- \code == -1 \endcode: Matching any entry.
2344	- \code < -1 \endcode: Matching any entry with a process group ID of \c -id.
2345	\c 0 is an invalid value for \a id.
2346
2347	The caller must hold the lock of the team that \a children belongs to.
2348
2349	\param children The job control entry list to check.
2350	\param id The match criterion.
2351	\return The first matching entry or \c NULL, if none matches.
2352*/
2353static job_control_entry*
2354get_job_control_entry(team_job_control_children& children, pid_t id)
2355{
2356	for (JobControlEntryList::Iterator it = children.entries.GetIterator();
2357		 job_control_entry* entry = it.Next();) {
2358
2359		if (id > 0) {
2360			if (entry->thread == id)
2361				return entry;
2362		} else if (id == -1) {
2363			return entry;
2364		} else {
2365			pid_t processGroup
2366				= (entry->team ? entry->team->group_id : entry->group_id);
2367			if (processGroup == -id)
2368				return entry;
2369		}
2370	}
2371
2372	return NULL;
2373}
2374
2375
2376/*!	Returns the first job control entry from one of team's dead, continued, or
2377	stopped children which matches \a id.
2378	\a id can be:
2379	- \code > 0 \endcode: Matching an entry with that team ID.
2380	- \code == -1 \endcode: Matching any entry.
2381	- \code < -1 \endcode: Matching any entry with a process group ID of \c -id.
2382	\c 0 is an invalid value for \a id.
2383
2384	The caller must hold \a team's lock.
2385
2386	\param team The team whose dead, stopped, and continued child lists shall be
2387		checked.
2388	\param id The match criterion.
2389	\param flags Specifies which children shall be considered. Dead children
2390		are considered when \a flags is ORed bitwise with \c WEXITED, stopped
2391		children are considered when \a flags is ORed bitwise with \c WUNTRACED
2392		or \c WSTOPPED, continued children when \a flags is ORed bitwise with
2393		\c WCONTINUED.
2394	\return The first matching entry or \c NULL, if none matches.
2395*/
2396static job_control_entry*
2397get_job_control_entry(Team* team, pid_t id, uint32 flags)
2398{
2399	job_control_entry* entry = NULL;
2400
2401	if ((flags & WEXITED) != 0)
2402		entry = get_job_control_entry(team->dead_children, id);
2403
2404	if (entry == NULL && (flags & WCONTINUED) != 0)
2405		entry = get_job_control_entry(team->continued_children, id);
2406
2407	if (entry == NULL && (flags & (WUNTRACED | WSTOPPED)) != 0)
2408		entry = get_job_control_entry(team->stopped_children, id);
2409
2410	return entry;
2411}
2412
2413
2414job_control_entry::job_control_entry()
2415	:
2416	has_group_ref(false)
2417{
2418}
2419
2420
2421job_control_entry::~job_control_entry()
2422{
2423	if (has_group_ref) {
2424		InterruptsSpinLocker groupHashLocker(sGroupHashLock);
2425
2426		ProcessGroup* group = sGroupHash.Lookup(group_id);
2427		if (group == NULL) {
2428			panic("job_control_entry::~job_control_entry(): unknown group "
2429				"ID: %" B_PRId32, group_id);
2430			return;
2431		}
2432
2433		groupHashLocker.Unlock();
2434
2435		group->ReleaseReference();
2436	}
2437}
2438
2439
2440/*!	Invoked when the owning team is dying, initializing the entry according to
2441	the dead state.
2442
2443	The caller must hold the owning team's lock and the scheduler lock.
2444*/
2445void
2446job_control_entry::InitDeadState()
2447{
2448	if (team != NULL) {
2449		ASSERT(team->exit.initialized);
2450
2451		group_id = team->group_id;
2452		team->group->AcquireReference();
2453		has_group_ref = true;
2454
2455		thread = team->id;
2456		status = team->exit.status;
2457		reason = team->exit.reason;
2458		signal = team->exit.signal;
2459		signaling_user = team->exit.signaling_user;
2460		user_time = team->dead_threads_user_time
2461			+ team->dead_children.user_time;
2462		kernel_time = team->dead_threads_kernel_time
2463			+ team->dead_children.kernel_time;
2464
2465		team = NULL;
2466	}
2467}
2468
2469
2470job_control_entry&
2471job_control_entry::operator=(const job_control_entry& other)
2472{
2473	state = other.state;
2474	thread = other.thread;
2475	signal = other.signal;
2476	has_group_ref = false;
2477	signaling_user = other.signaling_user;
2478	team = other.team;
2479	group_id = other.group_id;
2480	status = other.status;
2481	reason = other.reason;
2482	user_time = other.user_time;
2483	kernel_time = other.kernel_time;
2484
2485	return *this;
2486}
2487
2488
2489/*! This is the kernel backend for waitid().
2490*/
2491static thread_id
2492wait_for_child(pid_t child, uint32 flags, siginfo_t& _info,
2493	team_usage_info& _usage_info)
2494{
2495	Thread* thread = thread_get_current_thread();
2496	Team* team = thread->team;
2497	struct job_control_entry foundEntry;
2498	struct job_control_entry* freeDeathEntry = NULL;
2499	status_t status = B_OK;
2500
2501	TRACE(("wait_for_child(child = %" B_PRId32 ", flags = %" B_PRId32 ")\n",
2502		child, flags));
2503
2504	T(WaitForChild(child, flags));
2505
2506	if ((flags & (WEXITED | WUNTRACED | WSTOPPED | WCONTINUED)) == 0) {
2507		T(WaitForChildDone(B_BAD_VALUE));
2508		return B_BAD_VALUE;
2509	}
2510
2511	pid_t originalChild = child;
2512
2513	bool ignoreFoundEntries = false;
2514	bool ignoreFoundEntriesChecked = false;
2515
2516	while (true) {
2517		// lock the team
2518		TeamLocker teamLocker(team);
2519
2520		// A 0 child argument means to wait for all children in the process
2521		// group of the calling team.
2522		child = originalChild == 0 ? -team->group_id : originalChild;
2523
2524		// check whether any condition holds
2525		job_control_entry* entry = get_job_control_entry(team, child, flags);
2526
2527		// If we don't have an entry yet, check whether there are any children
2528		// complying to the process group specification at all.
2529		if (entry == NULL) {
2530			// No success yet -- check whether there are any children complying
2531			// to the process group specification at all.
2532			bool childrenExist = false;
2533			if (child == -1) {
2534				childrenExist = team->children != NULL;
2535			} else if (child < -1) {
2536				childrenExist = has_children_in_group(team, -child);
2537			} else if (child != team->id) {
2538				if (Team* childTeam = Team::Get(child)) {
2539					BReference<Team> childTeamReference(childTeam, true);
2540					TeamLocker childTeamLocker(childTeam);
2541					childrenExist = childTeam->parent == team;
2542				}
2543			}
2544
2545			if (!childrenExist) {
2546				// there is no child we could wait for
2547				status = ECHILD;
2548			} else {
2549				// the children we're waiting for are still running
2550				status = B_WOULD_BLOCK;
2551			}
2552		} else {
2553			// got something
2554			foundEntry = *entry;
2555
2556			// unless WNOWAIT has been specified, "consume" the wait state
2557			if ((flags & WNOWAIT) == 0 || ignoreFoundEntries) {
2558				if (entry->state == JOB_CONTROL_STATE_DEAD) {
2559					// The child is dead. Reap its death entry.
2560					freeDeathEntry = entry;
2561					team->dead_children.entries.Remove(entry);
2562					team->dead_children.count--;
2563				} else {
2564					// The child is well. Reset its job control state.
2565					team_set_job_control_state(entry->team,
2566						JOB_CONTROL_STATE_NONE, NULL);
2567				}
2568			}
2569		}
2570
2571		// If we haven't got anything yet, prepare for waiting for the
2572		// condition variable.
2573		ConditionVariableEntry deadWaitEntry;
2574
2575		if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0)
2576			team->dead_children.condition_variable.Add(&deadWaitEntry);
2577
2578		teamLocker.Unlock();
2579
2580		// we got our entry and can return to our caller
2581		if (status == B_OK) {
2582			if (ignoreFoundEntries) {
2583				// ... unless we shall ignore found entries
2584				delete freeDeathEntry;
2585				freeDeathEntry = NULL;
2586				continue;
2587			}
2588
2589			break;
2590		}
2591
2592		if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0) {
2593			T(WaitForChildDone(status));
2594			return status;
2595		}
2596
2597		status = deadWaitEntry.Wait(B_CAN_INTERRUPT);
2598		if (status == B_INTERRUPTED) {
2599			T(WaitForChildDone(status));
2600			return status;
2601		}
2602
2603		// If SA_NOCLDWAIT is set or SIGCHLD is ignored, we shall wait until
2604		// all our children are dead and fail with ECHILD. We check the
2605		// condition at this point.
2606		if (!ignoreFoundEntriesChecked) {
2607			teamLocker.Lock();
2608
2609			struct sigaction& handler = team->SignalActionFor(SIGCHLD);
2610			if ((handler.sa_flags & SA_NOCLDWAIT) != 0
2611				|| handler.sa_handler == SIG_IGN) {
2612				ignoreFoundEntries = true;
2613			}
2614
2615			teamLocker.Unlock();
2616
2617			ignoreFoundEntriesChecked = true;
2618		}
2619	}
2620
2621	delete freeDeathEntry;
2622
2623	// When we got here, we have a valid death entry, and already got
2624	// unregistered from the team or group. Fill in the returned info.
2625	memset(&_info, 0, sizeof(_info));
2626	_info.si_signo = SIGCHLD;
2627	_info.si_pid = foundEntry.thread;
2628	_info.si_uid = foundEntry.signaling_user;
2629	// TODO: Fill in si_errno?
2630
2631	switch (foundEntry.state) {
2632		case JOB_CONTROL_STATE_DEAD:
2633			_info.si_code = foundEntry.reason;
2634			_info.si_status = foundEntry.reason == CLD_EXITED
2635				? foundEntry.status : foundEntry.signal;
2636			_usage_info.user_time = foundEntry.user_time;
2637			_usage_info.kernel_time = foundEntry.kernel_time;
2638			break;
2639		case JOB_CONTROL_STATE_STOPPED:
2640			_info.si_code = CLD_STOPPED;
2641			_info.si_status = foundEntry.signal;
2642			break;
2643		case JOB_CONTROL_STATE_CONTINUED:
2644			_info.si_code = CLD_CONTINUED;
2645			_info.si_status = 0;
2646			break;
2647		case JOB_CONTROL_STATE_NONE:
2648			// can't happen
2649			break;
2650	}
2651
2652	// If SIGCHLD is blocked, we shall clear pending SIGCHLDs, if no other child
2653	// status is available.
2654	TeamLocker teamLocker(team);
2655	InterruptsSpinLocker signalLocker(team->signal_lock);
2656	SpinLocker threadCreationLocker(gThreadCreationLock);
2657
2658	if (is_team_signal_blocked(team, SIGCHLD)) {
2659		if (get_job_control_entry(team, child, flags) == NULL)
2660			team->RemovePendingSignals(SIGNAL_TO_MASK(SIGCHLD));
2661	}
2662
2663	threadCreationLocker.Unlock();
2664	signalLocker.Unlock();
2665	teamLocker.Unlock();
2666
2667	// When the team is dead, the main thread continues to live in the kernel
2668	// team for a very short time. To avoid surprises for the caller we rather
2669	// wait until the thread is really gone.
2670	if (foundEntry.state == JOB_CONTROL_STATE_DEAD)
2671		wait_for_thread(foundEntry.thread, NULL);
2672
2673	T(WaitForChildDone(foundEntry));
2674
2675	return foundEntry.thread;
2676}
2677
2678
2679/*! Fills the team_info structure with information from the specified team.
2680	Interrupts must be enabled. The team must not be locked.
2681*/
2682static status_t
2683fill_team_info(Team* team, team_info* info, size_t size)
2684{
2685	if (size > sizeof(team_info))
2686		return B_BAD_VALUE;
2687
2688	// TODO: Set more informations for team_info
2689	memset(info, 0, size);
2690
2691	info->team = team->id;
2692		// immutable
2693	info->image_count = count_images(team);
2694		// protected by sImageMutex
2695
2696	TeamLocker teamLocker(team);
2697	InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
2698
2699	info->thread_count = team->num_threads;
2700	//info->area_count =
2701	info->debugger_nub_thread = team->debug_info.nub_thread;
2702	info->debugger_nub_port = team->debug_info.nub_port;
2703	info->uid = team->effective_uid;
2704	info->gid = team->effective_gid;
2705
2706	strlcpy(info->args, team->Args(), sizeof(info->args));
2707	info->argc = 1;
2708
2709	if (size > offsetof(team_info, real_uid)) {
2710		info->real_uid = team->real_uid;
2711		info->real_gid = team->real_gid;
2712		info->group_id = team->group_id;
2713		info->session_id = team->session_id;
2714
2715		if (team->parent != NULL)
2716			info->parent = team->parent->id;
2717		else
2718			info->parent = -1;
2719
2720		strlcpy(info->name, team->Name(), sizeof(info->name));
2721		info->start_time = team->start_time;
2722	}
2723
2724	return B_OK;
2725}
2726
2727
2728/*!	Returns whether the process group contains stopped processes.
2729	The caller must hold the process group's lock.
2730*/
2731static bool
2732process_group_has_stopped_processes(ProcessGroup* group)
2733{
2734	Team* team = group->teams;
2735	while (team != NULL) {
2736		// the parent team's lock guards the job control entry -- acquire it
2737		team->LockTeamAndParent(false);
2738
2739		if (team->job_control_entry != NULL
2740			&& team->job_control_entry->state == JOB_CONTROL_STATE_STOPPED) {
2741			team->UnlockTeamAndParent();
2742			return true;
2743		}
2744
2745		team->UnlockTeamAndParent();
2746
2747		team = team->group_next;
2748	}
2749
2750	return false;
2751}
2752
2753
2754/*!	Iterates through all process groups queued in team_remove_team() and signals
2755	those that are orphaned and have stopped processes.
2756	The caller must not hold any team or process group locks.
2757*/
2758static void
2759orphaned_process_group_check()
2760{
2761	// process as long as there are groups in the list
2762	while (true) {
2763		// remove the head from the list
2764		MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
2765
2766		ProcessGroup* group = sOrphanedCheckProcessGroups.RemoveHead();
2767		if (group == NULL)
2768			return;
2769
2770		group->UnsetOrphanedCheck();
2771		BReference<ProcessGroup> groupReference(group);
2772
2773		orphanedCheckLocker.Unlock();
2774
2775		AutoLocker<ProcessGroup> groupLocker(group);
2776
2777		// If the group is orphaned and contains stopped processes, we're
2778		// supposed to send SIGHUP + SIGCONT.
2779		if (group->IsOrphaned() && process_group_has_stopped_processes(group)) {
2780			Thread* currentThread = thread_get_current_thread();
2781
2782			Signal signal(SIGHUP, SI_USER, B_OK, currentThread->team->id);
2783			send_signal_to_process_group_locked(group, signal, 0);
2784
2785			signal.SetNumber(SIGCONT);
2786			send_signal_to_process_group_locked(group, signal, 0);
2787		}
2788	}
2789}
2790
2791
2792static status_t
2793common_get_team_usage_info(team_id id, int32 who, team_usage_info* info,
2794	uint32 flags)
2795{
2796	if (who != B_TEAM_USAGE_SELF && who != B_TEAM_USAGE_CHILDREN)
2797		return B_BAD_VALUE;
2798
2799	// get the team
2800	Team* team = Team::GetAndLock(id);
2801	if (team == NULL)
2802		return B_BAD_TEAM_ID;
2803	BReference<Team> teamReference(team, true);
2804	TeamLocker teamLocker(team, true);
2805
2806	if ((flags & B_CHECK_PERMISSION) != 0) {
2807		uid_t uid = geteuid();
2808		if (uid != 0 && uid != team->effective_uid)
2809			return B_NOT_ALLOWED;
2810	}
2811
2812	bigtime_t kernelTime = 0;
2813	bigtime_t userTime = 0;
2814
2815	switch (who) {
2816		case B_TEAM_USAGE_SELF:
2817		{
2818			Thread* thread = team->thread_list;
2819
2820			for (; thread != NULL; thread = thread->team_next) {
2821				InterruptsSpinLocker threadTimeLocker(thread->time_lock);
2822				kernelTime += thread->kernel_time;
2823				userTime += thread->user_time;
2824			}
2825
2826			kernelTime += team->dead_threads_kernel_time;
2827			userTime += team->dead_threads_user_time;
2828			break;
2829		}
2830
2831		case B_TEAM_USAGE_CHILDREN:
2832		{
2833			Team* child = team->children;
2834			for (; child != NULL; child = child->siblings_next) {
2835				TeamLocker childLocker(child);
2836
2837				Thread* thread = team->thread_list;
2838
2839				for (; thread != NULL; thread = thread->team_next) {
2840					InterruptsSpinLocker threadTimeLocker(thread->time_lock);
2841					kernelTime += thread->kernel_time;
2842					userTime += thread->user_time;
2843				}
2844
2845				kernelTime += child->dead_threads_kernel_time;
2846				userTime += child->dead_threads_user_time;
2847			}
2848
2849			kernelTime += team->dead_children.kernel_time;
2850			userTime += team->dead_children.user_time;
2851			break;
2852		}
2853	}
2854
2855	info->kernel_time = kernelTime;
2856	info->user_time = userTime;
2857
2858	return B_OK;
2859}
2860
2861
2862//	#pragma mark - Private kernel API
2863
2864
2865status_t
2866team_init(kernel_args* args)
2867{
2868	// create the team hash table
2869	new(&sTeamHash) TeamTable;
2870	if (sTeamHash.Init(64) != B_OK)
2871		panic("Failed to init team hash table!");
2872
2873	new(&sGroupHash) ProcessGroupHashTable;
2874	if (sGroupHash.Init() != B_OK)
2875		panic("Failed to init process group hash table!");
2876
2877	// create initial session and process groups
2878
2879	ProcessSession* session = new(std::nothrow) ProcessSession(1);
2880	if (session == NULL)
2881		panic("Could not create initial session.\n");
2882	BReference<ProcessSession> sessionReference(session, true);
2883
2884	ProcessGroup* group = new(std::nothrow) ProcessGroup(1);
2885	if (group == NULL)
2886		panic("Could not create initial process group.\n");
2887	BReference<ProcessGroup> groupReference(group, true);
2888
2889	group->Publish(session);
2890
2891	// create the kernel team
2892	sKernelTeam = Team::Create(1, "kernel_team", true);
2893	if (sKernelTeam == NULL)
2894		panic("could not create kernel team!\n");
2895
2896	sKernelTeam->address_space = VMAddressSpace::Kernel();
2897	sKernelTeam->SetArgs(sKernelTeam->Name());
2898	sKernelTeam->state = TEAM_STATE_NORMAL;
2899
2900	sKernelTeam->saved_set_uid = 0;
2901	sKernelTeam->real_uid = 0;
2902	sKernelTeam->effective_uid = 0;
2903	sKernelTeam->saved_set_gid = 0;
2904	sKernelTeam->real_gid = 0;
2905	sKernelTeam->effective_gid = 0;
2906	sKernelTeam->supplementary_groups = NULL;
2907
2908	insert_team_into_group(group, sKernelTeam);
2909
2910	sKernelTeam->io_context = vfs_new_io_context(NULL, false);
2911	if (sKernelTeam->io_context == NULL)
2912		panic("could not create io_context for kernel team!\n");
2913
2914	if (vfs_resize_fd_table(sKernelTeam->io_context, 4096) != B_OK)
2915		dprintf("Failed to resize FD table for kernel team!\n");
2916
2917	// stick it in the team hash
2918	sTeamHash.Insert(sKernelTeam);
2919
2920	// check safe mode settings
2921	sDisableUserAddOns = get_safemode_boolean(B_SAFEMODE_DISABLE_USER_ADD_ONS,
2922		false);
2923
2924	add_debugger_command_etc("team", &dump_team_info,
2925		"Dump info about a particular team",
2926		"[ <id> | <address> | <name> ]\n"
2927		"Prints information about the specified team. If no argument is given\n"
2928		"the current team is selected.\n"
2929		"  <id>       - The ID of the team.\n"
2930		"  <address>  - The address of the team structure.\n"
2931		"  <name>     - The team's name.\n", 0);
2932	add_debugger_command_etc("teams", &dump_teams, "List all teams",
2933		"\n"
2934		"Prints a list of all existing teams.\n", 0);
2935
2936	new(&sNotificationService) TeamNotificationService();
2937
2938	sNotificationService.Register();
2939
2940	return B_OK;
2941}
2942
2943
2944int32
2945team_max_teams(void)
2946{
2947	return sMaxTeams;
2948}
2949
2950
2951int32
2952team_used_teams(void)
2953{
2954	InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
2955	return sUsedTeams;
2956}
2957
2958
2959/*! Returns a death entry of a child team specified by ID (if any).
2960	The caller must hold the team's lock.
2961
2962	\param team The team whose dead children list to check.
2963	\param child The ID of the child for whose death entry to lock. Must be > 0.
2964	\param _deleteEntry Return variable, indicating whether the caller needs to
2965		delete the returned entry.
2966	\return The death entry of the matching team, or \c NULL, if no death entry
2967		for the team was found.
2968*/
2969job_control_entry*
2970team_get_death_entry(Team* team, thread_id child, bool* _deleteEntry)
2971{
2972	if (child <= 0)
2973		return NULL;
2974
2975	job_control_entry* entry = get_job_control_entry(team->dead_children,
2976		child);
2977	if (entry) {
2978		// remove the entry only, if the caller is the parent of the found team
2979		if (team_get_current_team_id() == entry->thread) {
2980			team->dead_children.entries.Remove(entry);
2981			team->dead_children.count--;
2982			*_deleteEntry = true;
2983		} else {
2984			*_deleteEntry = false;
2985		}
2986	}
2987
2988	return entry;
2989}
2990
2991
2992/*! Quick check to see if we have a valid team ID. */
2993bool
2994team_is_valid(team_id id)
2995{
2996	if (id <= 0)
2997		return false;
2998
2999	InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
3000	return team_get_team_struct_locked(id) != NULL;
3001}
3002
3003
3004Team*
3005team_get_team_struct_locked(team_id id)
3006{
3007	return sTeamHash.Lookup(id);
3008}
3009
3010
3011void
3012team_set_controlling_tty(void* tty)
3013{
3014	// lock the team, so its session won't change while we're playing with it
3015	Team* team = thread_get_current_thread()->team;
3016	TeamLocker teamLocker(team);
3017
3018	// get and lock the session
3019	ProcessSession* session = team->group->Session();
3020	AutoLocker<ProcessSession> sessionLocker(session);
3021
3022	// set the session's fields
3023	session->controlling_tty = tty;
3024	session->foreground_group = -1;
3025}
3026
3027
3028void*
3029team_get_controlling_tty()
3030{
3031	// lock the team, so its session won't change while we're playing with it
3032	Team* team = thread_get_current_thread()->team;
3033	TeamLocker teamLocker(team);
3034
3035	// get and lock the session
3036	ProcessSession* session = team->group->Session();
3037	AutoLocker<ProcessSession> sessionLocker(session);
3038
3039	// get the session's field
3040	return session->controlling_tty;
3041}
3042
3043
3044status_t
3045team_set_foreground_process_group(void* tty, pid_t processGroupID)
3046{
3047	// lock the team, so its session won't change while we're playing with it
3048	Thread* thread = thread_get_current_thread();
3049	Team* team = thread->team;
3050	TeamLocker teamLocker(team);
3051
3052	// get and lock the session
3053	ProcessSession* session = team->group->Session();
3054	AutoLocker<ProcessSession> sessionLocker(session);
3055
3056	// check given TTY -- must be the controlling tty of the calling process
3057	if (session->controlling_tty != tty)
3058		return ENOTTY;
3059
3060	// check given process group -- must belong to our session
3061	{
3062		InterruptsSpinLocker groupHashLocker(sGroupHashLock);
3063		ProcessGroup* group = sGroupHash.Lookup(processGroupID);
3064		if (group == NULL || group->Session() != session)
3065			return B_BAD_VALUE;
3066	}
3067
3068	// If we are a background group, we can do that unharmed only when we
3069	// ignore or block SIGTTOU. Otherwise the group gets a SIGTTOU.
3070	if (session->foreground_group != -1
3071		&& session->foreground_group != team->group_id
3072		&& team->SignalActionFor(SIGTTOU).sa_handler != SIG_IGN
3073		&& (thread->sig_block_mask & SIGNAL_TO_MASK(SIGTTOU)) == 0) {
3074		InterruptsSpinLocker signalLocker(team->signal_lock);
3075
3076		if (!is_team_signal_blocked(team, SIGTTOU)) {
3077			pid_t groupID = team->group_id;
3078
3079			signalLocker.Unlock();
3080			sessionLocker.Unlock();
3081			teamLocker.Unlock();
3082
3083			Signal signal(SIGTTOU, SI_USER, B_OK, team->id);
3084			send_signal_to_process_group(groupID, signal, 0);
3085			return B_INTERRUPTED;
3086		}
3087	}
3088
3089	session->foreground_group = processGroupID;
3090
3091	return B_OK;
3092}
3093
3094
3095uid_t
3096team_geteuid(team_id id)
3097{
3098	InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
3099	Team* team = team_get_team_struct_locked(id);
3100	if (team == NULL)
3101		return (uid_t)-1;
3102	return team->effective_uid;
3103}
3104
3105
3106/*!	Removes the specified team from the global team hash, from its process
3107	group, and from its parent.
3108	It also moves all of its children to the kernel team.
3109
3110	The caller must hold the following locks:
3111	- \a team's process group's lock,
3112	- the kernel team's lock,
3113	- \a team's parent team's lock (might be the kernel team), and
3114	- \a team's lock.
3115*/
3116void
3117team_remove_team(Team* team, pid_t& _signalGroup)
3118{
3119	Team* parent = team->parent;
3120
3121	// remember how long this team lasted
3122	parent->dead_children.kernel_time += team->dead_threads_kernel_time
3123		+ team->dead_children.kernel_time;
3124	parent->dead_children.user_time += team->dead_threads_user_time
3125		+ team->dead_children.user_time;
3126
3127	// remove the team from the hash table
3128	InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
3129	sTeamHash.Remove(team);
3130	sUsedTeams--;
3131	teamsLocker.Unlock();
3132
3133	// The team can no longer be accessed by ID. Navigation to it is still
3134	// possible from its process group and its parent and children, but that
3135	// will be rectified shortly.
3136	team->state = TEAM_STATE_DEATH;
3137
3138	// If we're a controlling process (i.e. a session leader with controlling
3139	// terminal), there's a bit of signalling we have to do. We can't do any of
3140	// the signaling here due to the bunch of locks we're holding, but we need
3141	// to determine, whom to signal.
3142	_signalGroup = -1;
3143	bool isSessionLeader = false;
3144	if (team->session_id == team->id
3145		&& team->group->Session()->controlling_tty != NULL) {
3146		isSessionLeader = true;
3147
3148		ProcessSession* session = team->group->Session();
3149
3150		AutoLocker<ProcessSession> sessionLocker(session);
3151
3152		session->controlling_tty = NULL;
3153		_signalGroup = session->foreground_group;
3154	}
3155
3156	// remove us from our process group
3157	remove_team_from_group(team);
3158
3159	// move the team's children to the kernel team
3160	while (Team* child = team->children) {
3161		// remove the child from the current team and add it to the kernel team
3162		TeamLocker childLocker(child);
3163
3164		remove_team_from_parent(team, child);
3165		insert_team_into_parent(sKernelTeam, child);
3166
3167		// move job control entries too
3168		sKernelTeam->stopped_children.entries.MoveFrom(
3169			&team->stopped_children.entries);
3170		sKernelTeam->continued_children.entries.MoveFrom(
3171			&team->continued_children.entries);
3172
3173		// If the team was a session leader with controlling terminal,
3174		// we need to send SIGHUP + SIGCONT to all newly-orphaned process
3175		// groups with stopped processes. Due to locking complications we can't
3176		// do that here, so we only check whether we were a reason for the
3177		// child's process group not being an orphan and, if so, schedule a
3178		// later check (cf. orphaned_process_group_check()).
3179		if (isSessionLeader) {
3180			ProcessGroup* childGroup = child->group;
3181			if (childGroup->Session()->id == team->session_id
3182				&& childGroup->id != team->group_id) {
3183				childGroup->ScheduleOrphanedCheck();
3184			}
3185		}
3186
3187		// Note, we don't move the dead children entries. Those will be deleted
3188		// when the team structure is deleted.
3189	}
3190
3191	// remove us from our parent
3192	remove_team_from_parent(parent, team);
3193}
3194
3195
3196/*!	Kills all threads but the main thread of the team and shuts down user
3197	debugging for it.
3198	To be called on exit of the team's main thread. No locks must be held.
3199
3200	\param team The team in question.
3201	\return The port of the debugger for the team, -1 if none. To be passed to
3202		team_delete_team().
3203*/
3204port_id
3205team_shutdown_team(Team* team)
3206{
3207	ASSERT(thread_get_current_thread() == team->main_thread);
3208
3209	TeamLocker teamLocker(team);
3210
3211	// Make sure debugging changes won't happen anymore.
3212	port_id debuggerPort = -1;
3213	while (true) {
3214		// If a debugger change is in progress for the team, we'll have to
3215		// wait until it is done.
3216		ConditionVariableEntry waitForDebuggerEntry;
3217		bool waitForDebugger = false;
3218
3219		InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
3220
3221		if (team->debug_info.debugger_changed_condition != NULL) {
3222			team->debug_info.debugger_changed_condition->Add(
3223				&waitForDebuggerEntry);
3224			waitForDebugger = true;
3225		} else if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
3226			// The team is being debugged. That will stop with the termination
3227			// of the nub thread. Since we set the team state to death, no one
3228			// can install a debugger anymore. We fetch the debugger's port to
3229			// send it a message at the bitter end.
3230			debuggerPort = team->debug_info.debugger_port;
3231		}
3232
3233		debugInfoLocker.Unlock();
3234
3235		if (!waitForDebugger)
3236			break;
3237
3238		// wait for the debugger change to be finished
3239		teamLocker.Unlock();
3240
3241		waitForDebuggerEntry.Wait();
3242
3243		teamLocker.Lock();
3244	}
3245
3246	// Mark the team as shutting down. That will prevent new threads from being
3247	// created and debugger changes from taking place.
3248	team->state = TEAM_STATE_SHUTDOWN;
3249
3250	// delete all timers
3251	team->DeleteUserTimers(false);
3252
3253	// deactivate CPU time user timers for the team
3254	InterruptsSpinLocker timeLocker(team->time_lock);
3255
3256	if (team->HasActiveCPUTimeUserTimers())
3257		team->DeactivateCPUTimeUserTimers();
3258
3259	timeLocker.Unlock();
3260
3261	// kill all threads but the main thread
3262	team_death_entry deathEntry;
3263	deathEntry.condition.Init(team, "team death");
3264
3265	while (true) {
3266		team->death_entry = &deathEntry;
3267		deathEntry.remaining_threads = 0;
3268
3269		Thread* thread = team->thread_list;
3270		while (thread != NULL) {
3271			if (thread != team->main_thread) {
3272				Signal signal(SIGKILLTHR, SI_USER, B_OK, team->id);
3273				send_signal_to_thread(thread, signal, B_DO_NOT_RESCHEDULE);
3274				deathEntry.remaining_threads++;
3275			}
3276
3277			thread = thread->team_next;
3278		}
3279
3280		if (deathEntry.remaining_threads == 0)
3281			break;
3282
3283		// there are threads to wait for
3284		ConditionVariableEntry entry;
3285		deathEntry.condition.Add(&entry);
3286
3287		teamLocker.Unlock();
3288
3289		entry.Wait();
3290
3291		teamLocker.Lock();
3292	}
3293
3294	team->death_entry = NULL;
3295
3296	return debuggerPort;
3297}
3298
3299
3300/*!	Called on team exit to notify threads waiting on the team and free most
3301	resources associated with it.
3302	The caller shouldn't hold any locks.
3303*/
3304void
3305team_delete_team(Team* team, port_id debuggerPort)
3306{
3307	// Not quite in our job description, but work that has been left by
3308	// team_remove_team() and that can be done now that we're not holding any
3309	// locks.
3310	orphaned_process_group_check();
3311
3312	team_id teamID = team->id;
3313
3314	ASSERT(team->num_threads == 0);
3315
3316	// If someone is waiting for this team to be loaded, but it dies
3317	// unexpectedly before being done, we need to notify the waiting
3318	// thread now.
3319
3320	TeamLocker teamLocker(team);
3321
3322	if (team->loading_info != NULL) {
3323		// there's indeed someone waiting
3324		team->loading_info->result = B_ERROR;
3325
3326		// wake up the waiting thread
3327		team->loading_info->condition.NotifyAll();
3328		team->loading_info = NULL;
3329	}
3330
3331	// notify team watchers
3332
3333	{
3334		// we're not reachable from anyone anymore at this point, so we
3335		// can safely access the list without any locking
3336		struct team_watcher* watcher;
3337		while ((watcher = (struct team_watcher*)list_remove_head_item(
3338				&team->watcher_list)) != NULL) {
3339			watcher->hook(teamID, watcher->data);
3340			free(watcher);
3341		}
3342	}
3343
3344	teamLocker.Unlock();
3345
3346	sNotificationService.Notify(TEAM_REMOVED, team);
3347
3348	// free team resources
3349
3350	delete_user_mutex_context(team->user_mutex_context);
3351	delete_realtime_sem_context(team->realtime_sem_context);
3352	xsi_sem_undo(team);
3353	remove_images(team);
3354	team->address_space->RemoveAndPut();
3355
3356	team->ReleaseReference();
3357
3358	// notify the debugger, that the team is gone
3359	user_debug_team_deleted(teamID, debuggerPort);
3360}
3361
3362
3363Team*
3364team_get_kernel_team(void)
3365{
3366	return sKernelTeam;
3367}
3368
3369
3370team_id
3371team_get_kernel_team_id(void)
3372{
3373	if (!sKernelTeam)
3374		return 0;
3375
3376	return sKernelTeam->id;
3377}
3378
3379
3380team_id
3381team_get_current_team_id(void)
3382{
3383	return thread_get_current_thread()->team->id;
3384}
3385
3386
3387status_t
3388team_get_address_space(team_id id, VMAddressSpace** _addressSpace)
3389{
3390	if (id == sKernelTeam->id) {
3391		// we're the kernel team, so we don't have to go through all
3392		// the hassle (locking and hash lookup)
3393		*_addressSpace = VMAddressSpace::GetKernel();
3394		return B_OK;
3395	}
3396
3397	InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
3398
3399	Team* team = team_get_team_struct_locked(id);
3400	if (team == NULL)
3401		return B_BAD_VALUE;
3402
3403	team->address_space->Get();
3404	*_addressSpace = team->address_space;
3405	return B_OK;
3406}
3407
3408
3409/*!	Sets the team's job control state.
3410	The caller must hold the parent team's lock. Interrupts are allowed to be
3411	enabled or disabled.
3412	\a team The team whose job control state shall be set.
3413	\a newState The new state to be set.
3414	\a signal The signal the new state was caused by. Can \c NULL, if none. Then
3415		the caller is responsible for filling in the following fields of the
3416		entry before releasing the parent team's lock, unless the new state is
3417		\c JOB_CONTROL_STATE_NONE:
3418		- \c signal: The number of the signal causing the state change.
3419		- \c signaling_user: The real UID of the user sending the signal.
3420*/
3421void
3422team_set_job_control_state(Team* team, job_control_state newState,
3423	Signal* signal)
3424{
3425	if (team == NULL || team->job_control_entry == NULL)
3426		return;
3427
3428	// don't touch anything, if the state stays the same or the team is already
3429	// dead
3430	job_control_entry* entry = team->job_control_entry;
3431	if (entry->state == newState || entry->state == JOB_CONTROL_STATE_DEAD)
3432		return;
3433
3434	T(SetJobControlState(team->id, newState, signal));
3435
3436	// remove from the old list
3437	switch (entry->state) {
3438		case JOB_CONTROL_STATE_NONE:
3439			// entry is in no list ATM
3440			break;
3441		case JOB_CONTROL_STATE_DEAD:
3442			// can't get here
3443			break;
3444		case JOB_CONTROL_STATE_STOPPED:
3445			team->parent->stopped_children.entries.Remove(entry);
3446			break;
3447		case JOB_CONTROL_STATE_CONTINUED:
3448			team->parent->continued_children.entries.Remove(entry);
3449			break;
3450	}
3451
3452	entry->state = newState;
3453
3454	if (signal != NULL) {
3455		entry->signal = signal->Number();
3456		entry->signaling_user = signal->SendingUser();
3457	}
3458
3459	// add to new list
3460	team_job_control_children* childList = NULL;
3461	switch (entry->state) {
3462		case JOB_CONTROL_STATE_NONE:
3463			// entry doesn't get into any list
3464			break;
3465		case JOB_CONTROL_STATE_DEAD:
3466			childList = &team->parent->dead_children;
3467			team->parent->dead_children.count++;
3468			break;
3469		case JOB_CONTROL_STATE_STOPPED:
3470			childList = &team->parent->stopped_children;
3471			break;
3472		case JOB_CONTROL_STATE_CONTINUED:
3473			childList = &team->parent->continued_children;
3474			break;
3475	}
3476
3477	if (childList != NULL) {
3478		childList->entries.Add(entry);
3479		team->parent->dead_children.condition_variable.NotifyAll();
3480	}
3481}
3482
3483
3484/*!	Inits the given team's exit information, if not yet initialized, to some
3485	generic "killed" status.
3486	The caller must not hold the team's lock. Interrupts must be enabled.
3487
3488	\param team The team whose exit info shall be initialized.
3489*/
3490void
3491team_init_exit_info_on_error(Team* team)
3492{
3493	TeamLocker teamLocker(team);
3494
3495	if (!team->exit.initialized) {
3496		team->exit.reason = CLD_KILLED;
3497		team->exit.signal = SIGKILL;
3498		team->exit.signaling_user = geteuid();
3499		team->exit.status = 0;
3500		team->exit.initialized = true;
3501	}
3502}
3503
3504
3505/*! Adds a hook to the team that is called as soon as this team goes away.
3506	This call might get public in the future.
3507*/
3508status_t
3509start_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data)
3510{
3511	if (hook == NULL || teamID < B_OK)
3512		return B_BAD_VALUE;
3513
3514	// create the watcher object
3515	team_watcher* watcher = (team_watcher*)malloc(sizeof(team_watcher));
3516	if (watcher == NULL)
3517		return B_NO_MEMORY;
3518
3519	watcher->hook = hook;
3520	watcher->data = data;
3521
3522	// add watcher, if the team isn't already dying
3523	// get the team
3524	Team* team = Team::GetAndLock(teamID);
3525	if (team == NULL) {
3526		free(watcher);
3527		return B_BAD_TEAM_ID;
3528	}
3529
3530	list_add_item(&team->watcher_list, watcher);
3531
3532	team->UnlockAndReleaseReference();
3533
3534	return B_OK;
3535}
3536
3537
3538status_t
3539stop_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data)
3540{
3541	if (hook == NULL || teamID < 0)
3542		return B_BAD_VALUE;
3543
3544	// get team and remove watcher (if present)
3545	Team* team = Team::GetAndLock(teamID);
3546	if (team == NULL)
3547		return B_BAD_TEAM_ID;
3548
3549	// search for watcher
3550	team_watcher* watcher = NULL;
3551	while ((watcher = (team_watcher*)list_get_next_item(
3552			&team->watcher_list, watcher)) != NULL) {
3553		if (watcher->hook == hook && watcher->data == data) {
3554			// got it!
3555			list_remove_item(&team->watcher_list, watcher);
3556			break;
3557		}
3558	}
3559
3560	team->UnlockAndReleaseReference();
3561
3562	if (watcher == NULL)
3563		return B_ENTRY_NOT_FOUND;
3564
3565	free(watcher);
3566	return B_OK;
3567}
3568
3569
3570/*!	Allocates a user_thread structure from the team.
3571	The team lock must be held, unless the function is called for the team's
3572	main thread. Interrupts must be enabled.
3573*/
3574struct user_thread*
3575team_allocate_user_thread(Team* team)
3576{
3577	if (team->user_data == 0)
3578		return NULL;
3579
3580	// take an entry from the free list, if any
3581	if (struct free_user_thread* entry = team->free_user_threads) {
3582		user_thread* thread = entry->thread;
3583		team->free_user_threads = entry->next;
3584		free(entry);
3585		return thread;
3586	}
3587
3588	while (true) {
3589		// enough space left?
3590		size_t needed = ROUNDUP(sizeof(user_thread), CACHE_LINE_SIZE);
3591		if (team->user_data_size - team->used_user_data < needed) {
3592			// try to resize the area
3593			if (resize_area(team->user_data_area,
3594					team->user_data_size + B_PAGE_SIZE) != B_OK) {
3595				return NULL;
3596			}
3597
3598			// resized user area successfully -- try to allocate the user_thread
3599			// again
3600			team->user_data_size += B_PAGE_SIZE;
3601			continue;
3602		}
3603
3604		// allocate the user_thread
3605		user_thread* thread
3606			= (user_thread*)(team->user_data + team->used_user_data);
3607		team->used_user_data += needed;
3608
3609		return thread;
3610	}
3611}
3612
3613
3614/*!	Frees the given user_thread structure.
3615	The team's lock must not be held. Interrupts must be enabled.
3616	\param team The team the user thread was allocated from.
3617	\param userThread The user thread to free.
3618*/
3619void
3620team_free_user_thread(Team* team, struct user_thread* userThread)
3621{
3622	if (userThread == NULL)
3623		return;
3624
3625	// create a free list entry
3626	free_user_thread* entry
3627		= (free_user_thread*)malloc(sizeof(free_user_thread));
3628	if (entry == NULL) {
3629		// we have to leak the user thread :-/
3630		return;
3631	}
3632
3633	// add to free list
3634	TeamLocker teamLocker(team);
3635
3636	entry->thread = userThread;
3637	entry->next = team->free_user_threads;
3638	team->free_user_threads = entry;
3639}
3640
3641
3642//	#pragma mark - Associated data interface
3643
3644
3645AssociatedData::AssociatedData()
3646	:
3647	fOwner(NULL)
3648{
3649}
3650
3651
3652AssociatedData::~AssociatedData()
3653{
3654}
3655
3656
3657void
3658AssociatedData::OwnerDeleted(AssociatedDataOwner* owner)
3659{
3660}
3661
3662
3663AssociatedDataOwner::AssociatedDataOwner()
3664{
3665	mutex_init(&fLock, "associated data owner");
3666}
3667
3668
3669AssociatedDataOwner::~AssociatedDataOwner()
3670{
3671	mutex_destroy(&fLock);
3672}
3673
3674
3675bool
3676AssociatedDataOwner::AddData(AssociatedData* data)
3677{
3678	MutexLocker locker(fLock);
3679
3680	if (data->Owner() != NULL)
3681		return false;
3682
3683	data->AcquireReference();
3684	fList.Add(data);
3685	data->SetOwner(this);
3686
3687	return true;
3688}
3689
3690
3691bool
3692AssociatedDataOwner::RemoveData(AssociatedData* data)
3693{
3694	MutexLocker locker(fLock);
3695
3696	if (data->Owner() != this)
3697		return false;
3698
3699	data->SetOwner(NULL);
3700	fList.Remove(data);
3701
3702	locker.Unlock();
3703
3704	data->ReleaseReference();
3705
3706	return true;
3707}
3708
3709
3710void
3711AssociatedDataOwner::PrepareForDeletion()
3712{
3713	MutexLocker locker(fLock);
3714
3715	// move all data to a temporary list and unset the owner
3716	DataList list;
3717	list.MoveFrom(&fList);
3718
3719	for (DataList::Iterator it = list.GetIterator();
3720		AssociatedData* data = it.Next();) {
3721		data->SetOwner(NULL);
3722	}
3723
3724	locker.Unlock();
3725
3726	// call the notification hooks and release our references
3727	while (AssociatedData* data = list.RemoveHead()) {
3728		data->OwnerDeleted(this);
3729		data->ReleaseReference();
3730	}
3731}
3732
3733
3734/*!	Associates data with the current team.
3735	When the team is deleted, the data object is notified.
3736	The team acquires a reference to the object.
3737
3738	\param data The data object.
3739	\return \c true on success, \c false otherwise. Fails only when the supplied
3740		data object is already associated with another owner.
3741*/
3742bool
3743team_associate_data(AssociatedData* data)
3744{
3745	return thread_get_current_thread()->team->AddData(data);
3746}
3747
3748
3749/*!	Dissociates data from the current team.
3750	Balances an earlier call to team_associate_data().
3751
3752	\param data The data object.
3753	\return \c true on success, \c false otherwise. Fails only when the data
3754		object is not associated with the current team.
3755*/
3756bool
3757team_dissociate_data(AssociatedData* data)
3758{
3759	return thread_get_current_thread()->team->RemoveData(data);
3760}
3761
3762
3763//	#pragma mark - Public kernel API
3764
3765
3766thread_id
3767load_image(int32 argCount, const char** args, const char** env)
3768{
3769	return load_image_etc(argCount, args, env, B_NORMAL_PRIORITY,
3770		B_CURRENT_TEAM, B_WAIT_TILL_LOADED);
3771}
3772
3773
3774thread_id
3775load_image_etc(int32 argCount, const char* const* args,
3776	const char* const* env, int32 priority, team_id parentID, uint32 flags)
3777{
3778	// we need to flatten the args and environment
3779
3780	if (args == NULL)
3781		return B_BAD_VALUE;
3782
3783	// determine total needed size
3784	int32 argSize = 0;
3785	for (int32 i = 0; i < argCount; i++)
3786		argSize += strlen(args[i]) + 1;
3787
3788	int32 envCount = 0;
3789	int32 envSize = 0;
3790	while (env != NULL && env[envCount] != NULL)
3791		envSize += strlen(env[envCount++]) + 1;
3792
3793	int32 size = (argCount + envCount + 2) * sizeof(char*) + argSize + envSize;
3794	if (size > MAX_PROCESS_ARGS_SIZE)
3795		return B_TOO_MANY_ARGS;
3796
3797	// allocate space
3798	char** flatArgs = (char**)malloc(size);
3799	if (flatArgs == NULL)
3800		return B_NO_MEMORY;
3801
3802	char** slot = flatArgs;
3803	char* stringSpace = (char*)(flatArgs + argCount + envCount + 2);
3804
3805	// copy arguments and environment
3806	for (int32 i = 0; i < argCount; i++) {
3807		int32 argSize = strlen(args[i]) + 1;
3808		memcpy(stringSpace, args[i], argSize);
3809		*slot++ = stringSpace;
3810		stringSpace += argSize;
3811	}
3812
3813	*slot++ = NULL;
3814
3815	for (int32 i = 0; i < envCount; i++) {
3816		int32 envSize = strlen(env[i]) + 1;
3817		memcpy(stringSpace, env[i], envSize);
3818		*slot++ = stringSpace;
3819		stringSpace += envSize;
3820	}
3821
3822	*slot++ = NULL;
3823
3824	thread_id thread = load_image_internal(flatArgs, size, argCount, envCount,
3825		B_NORMAL_PRIORITY, parentID, B_WAIT_TILL_LOADED, -1, 0);
3826
3827	free(flatArgs);
3828		// load_image_internal() unset our variable if it took over ownership
3829
3830	return thread;
3831}
3832
3833
3834status_t
3835wait_for_team(team_id id, status_t* _returnCode)
3836{
3837	// check whether the team exists
3838	InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
3839
3840	Team* team = team_get_team_struct_locked(id);
3841	if (team == NULL)
3842		return B_BAD_TEAM_ID;
3843
3844	id = team->id;
3845
3846	teamsLocker.Unlock();
3847
3848	// wait for the main thread (it has the same ID as the team)
3849	return wait_for_thread(id, _returnCode);
3850}
3851
3852
3853status_t
3854kill_team(team_id id)
3855{
3856	InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
3857
3858	Team* team = team_get_team_struct_locked(id);
3859	if (team == NULL)
3860		return B_BAD_TEAM_ID;
3861
3862	id = team->id;
3863
3864	teamsLocker.Unlock();
3865
3866	if (team == sKernelTeam)
3867		return B_NOT_ALLOWED;
3868
3869	// Just kill the team's main thread (it has same ID as the team). The
3870	// cleanup code there will take care of the team.
3871	return kill_thread(id);
3872}
3873
3874
3875status_t
3876_get_team_info(team_id id, team_info* info, size_t size)
3877{
3878	// get the team
3879	Team* team = Team::Get(id);
3880	if (team == NULL)
3881		return B_BAD_TEAM_ID;
3882	BReference<Team> teamReference(team, true);
3883
3884	// fill in the info
3885	return fill_team_info(team, info, size);
3886}
3887
3888
3889status_t
3890_get_next_team_info(int32* cookie, team_info* info, size_t size)
3891{
3892	int32 slot = *cookie;
3893	if (slot < 1)
3894		slot = 1;
3895
3896	InterruptsReadSpinLocker locker(sTeamHashLock);
3897
3898	team_id lastTeamID = peek_next_thread_id();
3899		// TODO: This is broken, since the id can wrap around!
3900
3901	// get next valid team
3902	Team* team = NULL;
3903	while (slot < lastTeamID && !(team = team_get_team_struct_locked(slot)))
3904		slot++;
3905
3906	if (team == NULL)
3907		return B_BAD_TEAM_ID;
3908
3909	// get a reference to the team and unlock
3910	BReference<Team> teamReference(team);
3911	locker.Unlock();
3912
3913	// fill in the info
3914	*cookie = ++slot;
3915	return fill_team_info(team, info, size);
3916}
3917
3918
3919status_t
3920_get_team_usage_info(team_id id, int32 who, team_usage_info* info, size_t size)
3921{
3922	if (size != sizeof(team_usage_info))
3923		return B_BAD_VALUE;
3924
3925	return common_get_team_usage_info(id, who, info, 0);
3926}
3927
3928
3929pid_t
3930getpid(void)
3931{
3932	return thread_get_current_thread()->team->id;
3933}
3934
3935
3936pid_t
3937getppid()
3938{
3939	return _getppid(0);
3940}
3941
3942
3943pid_t
3944getpgid(pid_t id)
3945{
3946	if (id < 0) {
3947		errno = EINVAL;
3948		return -1;
3949	}
3950
3951	if (id == 0) {
3952		// get process group of the calling process
3953		Team* team = thread_get_current_thread()->team;
3954		TeamLocker teamLocker(team);
3955		return team->group_id;
3956	}
3957
3958	// get the team
3959	Team* team = Team::GetAndLock(id);
3960	if (team == NULL) {
3961		errno = ESRCH;
3962		return -1;
3963	}
3964
3965	// get the team's process group ID
3966	pid_t groupID = team->group_id;
3967
3968	team->UnlockAndReleaseReference();
3969
3970	return groupID;
3971}
3972
3973
3974pid_t
3975getsid(pid_t id)
3976{
3977	if (id < 0) {
3978		errno = EINVAL;
3979		return -1;
3980	}
3981
3982	if (id == 0) {
3983		// get session of the calling process
3984		Team* team = thread_get_current_thread()->team;
3985		TeamLocker teamLocker(team);
3986		return team->session_id;
3987	}
3988
3989	// get the team
3990	Team* team = Team::GetAndLock(id);
3991	if (team == NULL) {
3992		errno = ESRCH;
3993		return -1;
3994	}
3995
3996	// get the team's session ID
3997	pid_t sessionID = team->session_id;
3998
3999	team->UnlockAndReleaseReference();
4000
4001	return sessionID;
4002}
4003
4004
4005//	#pragma mark - User syscalls
4006
4007
4008status_t
4009_user_exec(const char* userPath, const char* const* userFlatArgs,
4010	size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask)
4011{
4012	// NOTE: Since this function normally doesn't return, don't use automatic
4013	// variables that need destruction in the function scope.
4014	char path[B_PATH_NAME_LENGTH];
4015
4016	if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userFlatArgs)
4017		|| user_strlcpy(path, userPath, sizeof(path)) < B_OK)
4018		return B_BAD_ADDRESS;
4019
4020	// copy and relocate the flat arguments
4021	char** flatArgs;
4022	status_t error = copy_user_process_args(userFlatArgs, flatArgsSize,
4023		argCount, envCount, flatArgs);
4024
4025	if (error == B_OK) {
4026		error = exec_team(path, flatArgs, _ALIGN(flatArgsSize), argCount,
4027			envCount, umask);
4028			// this one only returns in case of error
4029	}
4030
4031	free(flatArgs);
4032	return error;
4033}
4034
4035
4036thread_id
4037_user_fork(void)
4038{
4039	return fork_team();
4040}
4041
4042
4043pid_t
4044_user_wait_for_child(thread_id child, uint32 flags, siginfo_t* userInfo,
4045	team_usage_info* usageInfo)
4046{
4047	if (userInfo != NULL && !IS_USER_ADDRESS(userInfo))
4048		return B_BAD_ADDRESS;
4049	if (usageInfo != NULL && !IS_USER_ADDRESS(usageInfo))
4050		return B_BAD_ADDRESS;
4051
4052	siginfo_t info;
4053	team_usage_info usage_info;
4054	pid_t foundChild = wait_for_child(child, flags, info, usage_info);
4055	if (foundChild < 0)
4056		return syscall_restart_handle_post(foundChild);
4057
4058	// copy info back to userland
4059	if (userInfo != NULL && user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
4060		return B_BAD_ADDRESS;
4061	// copy usage_info back to userland
4062	if (usageInfo != NULL && user_memcpy(usageInfo, &usage_info,
4063		sizeof(usage_info)) != B_OK) {
4064		return B_BAD_ADDRESS;
4065	}
4066
4067	return foundChild;
4068}
4069
4070
4071pid_t
4072_user_process_info(pid_t process, int32 which)
4073{
4074	pid_t result;
4075	switch (which) {
4076		case SESSION_ID:
4077			result = getsid(process);
4078			break;
4079		case GROUP_ID:
4080			result = getpgid(process);
4081			break;
4082		case PARENT_ID:
4083			result = _getppid(process);
4084			break;
4085		default:
4086			return B_BAD_VALUE;
4087	}
4088
4089	return result >= 0 ? result : errno;
4090}
4091
4092
4093pid_t
4094_user_setpgid(pid_t processID, pid_t groupID)
4095{
4096	// setpgid() can be called either by the parent of the target process or
4097	// by the process itself to do one of two things:
4098	// * Create a new process group with the target process' ID and the target
4099	//   process as group leader.
4100	// * Set the target process' process group to an already existing one in the
4101	//   same session.
4102
4103	if (groupID < 0)
4104		return B_BAD_VALUE;
4105
4106	Team* currentTeam = thread_get_current_thread()->team;
4107	if (processID == 0)
4108		processID = currentTeam->id;
4109
4110	// if the group ID is not specified, use the target process' ID
4111	if (groupID == 0)
4112		groupID = processID;
4113
4114	// We loop when running into the following race condition: We create a new
4115	// process group, because there isn't one with that ID yet, but later when
4116	// trying to publish it, we find that someone else created and published
4117	// a group with that ID in the meantime. In that case we just restart the
4118	// whole action.
4119	while (true) {
4120		// Look up the process group by ID. If it doesn't exist yet and we are
4121		// allowed to create a new one, do that.
4122		ProcessGroup* group = ProcessGroup::Get(groupID);
4123		bool newGroup = false;
4124		if (group == NULL) {
4125			if (groupID != processID)
4126				return B_NOT_ALLOWED;
4127
4128			group = new(std::nothrow) ProcessGroup(groupID);
4129			if (group == NULL)
4130				return B_NO_MEMORY;
4131
4132			newGroup = true;
4133		}
4134		BReference<ProcessGroup> groupReference(group, true);
4135
4136		// get the target team
4137		Team* team = Team::Get(processID);
4138		if (team == NULL)
4139			return ESRCH;
4140		BReference<Team> teamReference(team, true);
4141
4142		// lock the new process group and the team's current process group
4143		while (true) {
4144			// lock the team's current process group
4145			team->LockProcessGroup();
4146
4147			ProcessGroup* oldGroup = team->group;
4148			if (oldGroup == NULL) {
4149				// This can only happen if the team is exiting.
4150				ASSERT(team->state >= TEAM_STATE_SHUTDOWN);
4151				return ESRCH;
4152			}
4153
4154			if (oldGroup == group) {
4155				// it's the same as the target group, so just bail out
4156				oldGroup->Unlock();
4157				return group->id;
4158			}
4159
4160			oldGroup->AcquireReference();
4161
4162			// lock the target process group, if locking order allows it
4163			if (newGroup || group->id > oldGroup->id) {
4164				group->Lock();
4165				break;
4166			}
4167
4168			// try to lock
4169			if (group->TryLock())
4170				break;
4171
4172			// no dice -- unlock the team's current process group and relock in
4173			// the correct order
4174			oldGroup->Unlock();
4175
4176			group->Lock();
4177			oldGroup->Lock();
4178
4179			// check whether things are still the same
4180			TeamLocker teamLocker(team);
4181			if (team->group == oldGroup)
4182				break;
4183
4184			// something changed -- unlock everything and retry
4185			teamLocker.Unlock();
4186			oldGroup->Unlock();
4187			group->Unlock();
4188			oldGroup->ReleaseReference();
4189		}
4190
4191		// we now have references and locks of both new and old process group
4192		BReference<ProcessGroup> oldGroupReference(team->group, true);
4193		AutoLocker<ProcessGroup> oldGroupLocker(team->group, true);
4194		AutoLocker<ProcessGroup> groupLocker(group, true);
4195
4196		// also lock the target team and its parent
4197		team->LockTeamAndParent(false);
4198		TeamLocker parentLocker(team->parent, true);
4199		TeamLocker teamLocker(team, true);
4200
4201		// perform the checks
4202		if (team == currentTeam) {
4203			// we set our own group
4204
4205			// we must not change our process group ID if we're a session leader
4206			if (is_session_leader(currentTeam))
4207				return B_NOT_ALLOWED;
4208		} else {
4209			// Calling team != target team. The target team must be a child of
4210			// the calling team and in the same session. (If that's the case it
4211			// isn't a session leader either.)
4212			if (team->parent != currentTeam
4213				|| team->session_id != currentTeam->session_id) {
4214				return B_NOT_ALLOWED;
4215			}
4216
4217			// The call is also supposed to fail on a child, when the child has
4218			// already executed exec*() [EACCES].
4219			if ((team->flags & TEAM_FLAG_EXEC_DONE) != 0)
4220				return EACCES;
4221		}
4222
4223		// If we created a new process group, publish it now.
4224		if (newGroup) {
4225			InterruptsSpinLocker groupHashLocker(sGroupHashLock);
4226			if (sGroupHash.Lookup(groupID)) {
4227				// A group with the group ID appeared since we first checked.
4228				// Back to square one.
4229				continue;
4230			}
4231
4232			group->PublishLocked(team->group->Session());
4233		} else if (group->Session()->id != team->session_id) {
4234			// The existing target process group belongs to a different session.
4235			// That's not allowed.
4236			return B_NOT_ALLOWED;
4237		}
4238
4239		// Everything is ready -- set the group.
4240		remove_team_from_group(team);
4241		insert_team_into_group(group, team);
4242
4243		// Changing the process group might have changed the situation for a
4244		// parent waiting in wait_for_child(). Hence we notify it.
4245		team->parent->dead_children.condition_variable.NotifyAll();
4246
4247		return group->id;
4248	}
4249}
4250
4251
4252pid_t
4253_user_setsid(void)
4254{
4255	Team* team = thread_get_current_thread()->team;
4256
4257	// create a new process group and session
4258	ProcessGroup* group = new(std::nothrow) ProcessGroup(team->id);
4259	if (group == NULL)
4260		return B_NO_MEMORY;
4261	BReference<ProcessGroup> groupReference(group, true);
4262	AutoLocker<ProcessGroup> groupLocker(group);
4263
4264	ProcessSession* session = new(std::nothrow) ProcessSession(group->id);
4265	if (session == NULL)
4266		return B_NO_MEMORY;
4267	BReference<ProcessSession> sessionReference(session, true);
4268
4269	// lock the team's current process group, parent, and the team itself
4270	team->LockTeamParentAndProcessGroup();
4271	BReference<ProcessGroup> oldGroupReference(team->group);
4272	AutoLocker<ProcessGroup> oldGroupLocker(team->group, true);
4273	TeamLocker parentLocker(team->parent, true);
4274	TeamLocker teamLocker(team, true);
4275
4276	// the team must not already be a process group leader
4277	if (is_process_group_leader(team))
4278		return B_NOT_ALLOWED;
4279
4280	// remove the team from the old and add it to the new process group
4281	remove_team_from_group(team);
4282	group->Publish(session);
4283	insert_team_into_group(group, team);
4284
4285	// Changing the process group might have changed the situation for a
4286	// parent waiting in wait_for_child(). Hence we notify it.
4287	team->parent->dead_children.condition_variable.NotifyAll();
4288
4289	return group->id;
4290}
4291
4292
4293status_t
4294_user_wait_for_team(team_id id, status_t* _userReturnCode)
4295{
4296	status_t returnCode;
4297	status_t status;
4298
4299	if (_userReturnCode != NULL && !IS_USER_ADDRESS(_userReturnCode))
4300		return B_BAD_ADDRESS;
4301
4302	status = wait_for_team(id, &returnCode);
4303	if (status >= B_OK && _userReturnCode != NULL) {
4304		if (user_memcpy(_userReturnCode, &returnCode, sizeof(returnCode))
4305				!= B_OK)
4306			return B_BAD_ADDRESS;
4307		return B_OK;
4308	}
4309
4310	return syscall_restart_handle_post(status);
4311}
4312
4313
4314thread_id
4315_user_load_image(const char* const* userFlatArgs, size_t flatArgsSize,
4316	int32 argCount, int32 envCount, int32 priority, uint32 flags,
4317	port_id errorPort, uint32 errorToken)
4318{
4319	TRACE(("_user_load_image: argc = %" B_PRId32 "\n", argCount));
4320
4321	if (argCount < 1)
4322		return B_BAD_VALUE;
4323
4324	// copy and relocate the flat arguments
4325	char** flatArgs;
4326	status_t error = copy_user_process_args(userFlatArgs, flatArgsSize,
4327		argCount, envCount, flatArgs);
4328	if (error != B_OK)
4329		return error;
4330
4331	thread_id thread = load_image_internal(flatArgs, _ALIGN(flatArgsSize),
4332		argCount, envCount, priority, B_CURRENT_TEAM, flags, errorPort,
4333		errorToken);
4334
4335	free(flatArgs);
4336		// load_image_internal() unset our variable if it took over ownership
4337
4338	return thread;
4339}
4340
4341
4342void
4343_user_exit_team(status_t returnValue)
4344{
4345	Thread* thread = thread_get_current_thread();
4346	Team* team = thread->team;
4347
4348	// set this thread's exit status
4349	thread->exit.status = returnValue;
4350
4351	// set the team exit status
4352	TeamLocker teamLocker(team);
4353
4354	if (!team->exit.initialized) {
4355		team->exit.reason = CLD_EXITED;
4356		team->exit.signal = 0;
4357		team->exit.signaling_user = 0;
4358		team->exit.status = returnValue;
4359		team->exit.initialized = true;
4360	}
4361
4362	teamLocker.Unlock();
4363
4364	// Stop the thread, if the team is being debugged and that has been
4365	// requested.
4366	// Note: GCC 13 marks the following call as potentially overflowing, since it thinks team may
4367	//       be `nullptr`. This cannot be the case in reality, therefore ignore this specific
4368	//       error.
4369	#pragma GCC diagnostic push
4370	#pragma GCC diagnostic ignored "-Wstringop-overflow"
4371	if ((atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_PREVENT_EXIT) != 0)
4372		user_debug_stop_thread();
4373	#pragma GCC diagnostic pop
4374
4375	// Send this thread a SIGKILL. This makes sure the thread will not return to
4376	// userland. The signal handling code forwards the signal to the main
4377	// thread (if that's not already this one), which will take the team down.
4378	Signal signal(SIGKILL, SI_USER, B_OK, team->id);
4379	send_signal_to_thread(thread, signal, 0);
4380}
4381
4382
4383status_t
4384_user_kill_team(team_id team)
4385{
4386	return kill_team(team);
4387}
4388
4389
4390status_t
4391_user_get_team_info(team_id id, team_info* userInfo, size_t size)
4392{
4393	status_t status;
4394	team_info info;
4395
4396	if (size > sizeof(team_info))
4397		return B_BAD_VALUE;
4398
4399	if (!IS_USER_ADDRESS(userInfo))
4400		return B_BAD_ADDRESS;
4401
4402	status = _get_team_info(id, &info, size);
4403	if (status == B_OK) {
4404		if (user_memcpy(userInfo, &info, size) < B_OK)
4405			return B_BAD_ADDRESS;
4406	}
4407
4408	return status;
4409}
4410
4411
4412status_t
4413_user_get_next_team_info(int32* userCookie, team_info* userInfo, size_t size)
4414{
4415	status_t status;
4416	team_info info;
4417	int32 cookie;
4418
4419	if (size > sizeof(team_info))
4420		return B_BAD_VALUE;
4421
4422	if (!IS_USER_ADDRESS(userCookie)
4423		|| !IS_USER_ADDRESS(userInfo)
4424		|| user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
4425		return B_BAD_ADDRESS;
4426
4427	status = _get_next_team_info(&cookie, &info, size);
4428	if (status != B_OK)
4429		return status;
4430
4431	if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
4432		|| user_memcpy(userInfo, &info, size) < B_OK)
4433		return B_BAD_ADDRESS;
4434
4435	return status;
4436}
4437
4438
4439team_id
4440_user_get_current_team(void)
4441{
4442	return team_get_current_team_id();
4443}
4444
4445
4446status_t
4447_user_get_team_usage_info(team_id team, int32 who, team_usage_info* userInfo,
4448	size_t size)
4449{
4450	if (size != sizeof(team_usage_info))
4451		return B_BAD_VALUE;
4452
4453	team_usage_info info;
4454	status_t status = common_get_team_usage_info(team, who, &info,
4455		B_CHECK_PERMISSION);
4456
4457	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo)
4458		|| user_memcpy(userInfo, &info, size) != B_OK) {
4459		return B_BAD_ADDRESS;
4460	}
4461
4462	return status;
4463}
4464
4465
4466status_t
4467_user_get_extended_team_info(team_id teamID, uint32 flags, void* buffer,
4468	size_t size, size_t* _sizeNeeded)
4469{
4470	// check parameters
4471	if ((buffer != NULL && !IS_USER_ADDRESS(buffer))
4472		|| (buffer == NULL && size > 0)
4473		|| _sizeNeeded == NULL || !IS_USER_ADDRESS(_sizeNeeded)) {
4474		return B_BAD_ADDRESS;
4475	}
4476
4477	KMessage info;
4478
4479	if ((flags & B_TEAM_INFO_BASIC) != 0) {
4480		// allocate memory for a copy of the needed team data
4481		struct ExtendedTeamData {
4482			team_id	id;
4483			pid_t	group_id;
4484			pid_t	session_id;
4485			uid_t	real_uid;
4486			gid_t	real_gid;
4487			uid_t	effective_uid;
4488			gid_t	effective_gid;
4489			char	name[B_OS_NAME_LENGTH];
4490		} teamClone;
4491
4492		io_context* ioContext;
4493		{
4494			// get the team structure
4495			Team* team = Team::GetAndLock(teamID);
4496			if (team == NULL)
4497				return B_BAD_TEAM_ID;
4498			BReference<Team> teamReference(team, true);
4499			TeamLocker teamLocker(team, true);
4500
4501			// copy the data
4502			teamClone.id = team->id;
4503			strlcpy(teamClone.name, team->Name(), sizeof(teamClone.name));
4504			teamClone.group_id = team->group_id;
4505			teamClone.session_id = team->session_id;
4506			teamClone.real_uid = team->real_uid;
4507			teamClone.real_gid = team->real_gid;
4508			teamClone.effective_uid = team->effective_uid;
4509			teamClone.effective_gid = team->effective_gid;
4510
4511			// also fetch a reference to the I/O context
4512			ioContext = team->io_context;
4513			vfs_get_io_context(ioContext);
4514		}
4515		CObjectDeleter<io_context, void, vfs_put_io_context>
4516			ioContextPutter(ioContext);
4517
4518		// add the basic data to the info message
4519		if (info.AddInt32("id", teamClone.id) != B_OK
4520			|| info.AddString("name", teamClone.name) != B_OK
4521			|| info.AddInt32("process group", teamClone.group_id) != B_OK
4522			|| info.AddInt32("session", teamClone.session_id) != B_OK
4523			|| info.AddInt32("uid", teamClone.real_uid) != B_OK
4524			|| info.AddInt32("gid", teamClone.real_gid) != B_OK
4525			|| info.AddInt32("euid", teamClone.effective_uid) != B_OK
4526			|| info.AddInt32("egid", teamClone.effective_gid) != B_OK) {
4527			return B_NO_MEMORY;
4528		}
4529
4530		// get the current working directory from the I/O context
4531		dev_t cwdDevice;
4532		ino_t cwdDirectory;
4533		{
4534			MutexLocker ioContextLocker(ioContext->io_mutex);
4535			vfs_vnode_to_node_ref(ioContext->cwd, &cwdDevice, &cwdDirectory);
4536		}
4537
4538		if (info.AddInt32("cwd device", cwdDevice) != B_OK
4539			|| info.AddInt64("cwd directory", cwdDirectory) != B_OK) {
4540			return B_NO_MEMORY;
4541		}
4542	}
4543
4544	// TODO: Support the other flags!
4545
4546	// copy the needed size and, if it fits, the message back to userland
4547	size_t sizeNeeded = info.ContentSize();
4548	if (user_memcpy(_sizeNeeded, &sizeNeeded, sizeof(sizeNeeded)) != B_OK)
4549		return B_BAD_ADDRESS;
4550
4551	if (sizeNeeded > size)
4552		return B_BUFFER_OVERFLOW;
4553
4554	if (user_memcpy(buffer, info.Buffer(), sizeNeeded) != B_OK)
4555		return B_BAD_ADDRESS;
4556
4557	return B_OK;
4558}
4559