#include <team.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <OS.h>
#include <AutoDeleter.h>
#include <FindDirectory.h>
#include <extended_system_info_defs.h>
#include <commpage.h>
#include <boot_device.h>
#include <elf.h>
#include <file_cache.h>
#include <find_directory_private.h>
#include <fs/KPath.h>
#include <heap.h>
#include <interrupts.h>
#include <kernel.h>
#include <kimage.h>
#include <kscheduler.h>
#include <ksignal.h>
#include <Notifications.h>
#include <port.h>
#include <posix/realtime_sem.h>
#include <posix/xsi_semaphore.h>
#include <safemode.h>
#include <sem.h>
#include <syscall_process_info.h>
#include <syscall_load_image.h>
#include <syscall_restart.h>
#include <syscalls.h>
#include <tls.h>
#include <tracing.h>
#include <user_mutex.h>
#include <user_runtime.h>
#include <user_thread.h>
#include <usergroup.h>
#include <vfs.h>
#include <vm/vm.h>
#include <vm/VMAddressSpace.h>
#include <util/AutoLock.h>
#include <util/ThreadAutoLock.h>
#include "TeamThreadTables.h"
#ifdef TRACE_TEAM
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
struct team_key {
team_id id;
};
struct team_arg {
char *path;
char **flat_args;
size_t flat_args_size;
uint32 arg_count;
uint32 env_count;
mode_t umask;
uint32 flags;
port_id error_port;
uint32 error_token;
};
#define TEAM_ARGS_FLAG_NO_ASLR 0x01
namespace {
class TeamNotificationService : public DefaultNotificationService {
public:
TeamNotificationService();
void Notify(uint32 eventCode, Team* team);
};
typedef BKernel::TeamThreadTable<Team> TeamTable;
struct ProcessGroupHashDefinition {
typedef pid_t KeyType;
typedef ProcessGroup ValueType;
size_t HashKey(pid_t key) const
{
return key;
}
size_t Hash(ProcessGroup* value) const
{
return HashKey(value->id);
}
bool Compare(pid_t key, ProcessGroup* value) const
{
return value->id == key;
}
ProcessGroup*& GetLink(ProcessGroup* value) const
{
return value->hash_next;
}
};
typedef BOpenHashTable<ProcessGroupHashDefinition> ProcessGroupHashTable;
}
static TeamTable sTeamHash;
static rw_spinlock sTeamHashLock = B_RW_SPINLOCK_INITIALIZER;
static ProcessGroupHashTable sGroupHash;
static spinlock sGroupHashLock = B_SPINLOCK_INITIALIZER;
static Team* sKernelTeam = NULL;
static bool sDisableUserAddOns = false;
static ProcessGroupList sOrphanedCheckProcessGroups;
static mutex sOrphanedCheckLock
= MUTEX_INITIALIZER("orphaned process group check");
static int32 sMaxTeams = 2048;
static int32 sUsedTeams = 1;
static TeamNotificationService sNotificationService;
static const size_t kTeamUserDataReservedSize = 128 * B_PAGE_SIZE;
static const size_t kTeamUserDataInitialSize = 4 * B_PAGE_SIZE;
TeamListIterator::TeamListIterator()
{
InterruptsWriteSpinLocker locker(sTeamHashLock);
sTeamHash.InsertIteratorEntry(&fEntry);
}
TeamListIterator::~TeamListIterator()
{
InterruptsWriteSpinLocker locker(sTeamHashLock);
sTeamHash.RemoveIteratorEntry(&fEntry);
}
Team*
TeamListIterator::Next()
{
InterruptsWriteSpinLocker locker(sTeamHashLock);
Team* team = sTeamHash.NextElement(&fEntry);
if (team != NULL)
team->AcquireReference();
return team;
}
#if TEAM_TRACING
namespace TeamTracing {
class TeamForked : public AbstractTraceEntry {
public:
TeamForked(thread_id forkedThread)
:
fForkedThread(forkedThread)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("team forked, new thread %" B_PRId32, fForkedThread);
}
private:
thread_id fForkedThread;
};
class ExecTeam : public AbstractTraceEntry {
public:
ExecTeam(const char* path, int32 argCount, const char* const* args,
int32 envCount, const char* const* env)
:
fArgCount(argCount),
fArgs(NULL)
{
fPath = alloc_tracing_buffer_strcpy(path, B_PATH_NAME_LENGTH,
false);
size_t argBufferSize = 0;
for (int32 i = 0; i < argCount; i++)
argBufferSize += strlen(args[i]) + 1;
fArgs = (char*)alloc_tracing_buffer(argBufferSize);
if (fArgs) {
char* buffer = fArgs;
for (int32 i = 0; i < argCount; i++) {
size_t argSize = strlen(args[i]) + 1;
memcpy(buffer, args[i], argSize);
buffer += argSize;
}
}
(void)envCount;
(void)env;
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("team exec, \"%p\", args:", fPath);
if (fArgs != NULL) {
char* args = fArgs;
for (int32 i = 0; !out.IsFull() && i < fArgCount; i++) {
out.Print(" \"%s\"", args);
args += strlen(args) + 1;
}
} else
out.Print(" <too long>");
}
private:
char* fPath;
int32 fArgCount;
char* fArgs;
};
static const char*
job_control_state_name(job_control_state state)
{
switch (state) {
case JOB_CONTROL_STATE_NONE:
return "none";
case JOB_CONTROL_STATE_STOPPED:
return "stopped";
case JOB_CONTROL_STATE_CONTINUED:
return "continued";
case JOB_CONTROL_STATE_DEAD:
return "dead";
default:
return "invalid";
}
}
class SetJobControlState : public AbstractTraceEntry {
public:
SetJobControlState(team_id team, job_control_state newState, Signal* signal)
:
fTeam(team),
fNewState(newState),
fSignal(signal != NULL ? signal->Number() : 0)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("team set job control state, team %" B_PRId32 ", "
"new state: %s, signal: %d",
fTeam, job_control_state_name(fNewState), fSignal);
}
private:
team_id fTeam;
job_control_state fNewState;
int fSignal;
};
class WaitForChild : public AbstractTraceEntry {
public:
WaitForChild(pid_t child, uint32 flags)
:
fChild(child),
fFlags(flags)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("team wait for child, child: %" B_PRId32 ", "
"flags: %#" B_PRIx32, fChild, fFlags);
}
private:
pid_t fChild;
uint32 fFlags;
};
class WaitForChildDone : public AbstractTraceEntry {
public:
WaitForChildDone(const job_control_entry& entry)
:
fState(entry.state),
fTeam(entry.thread),
fStatus(entry.status),
fReason(entry.reason),
fSignal(entry.signal)
{
Initialized();
}
WaitForChildDone(status_t error)
:
fTeam(error)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
if (fTeam >= 0) {
out.Print("team wait for child done, team: %" B_PRId32 ", "
"state: %s, status: %#" B_PRIx32 ", reason: %#x, signal: %d\n",
fTeam, job_control_state_name(fState), fStatus, fReason,
fSignal);
} else {
out.Print("team wait for child failed, error: "
"%#" B_PRIx32 ", ", fTeam);
}
}
private:
job_control_state fState;
team_id fTeam;
status_t fStatus;
uint16 fReason;
uint16 fSignal;
};
}
# define T(x) new(std::nothrow) TeamTracing::x;
#else
# define T(x) ;
#endif
TeamNotificationService::TeamNotificationService()
: DefaultNotificationService("teams")
{
}
void
TeamNotificationService::Notify(uint32 eventCode, Team* team)
{
char eventBuffer[128];
KMessage event;
event.SetTo(eventBuffer, sizeof(eventBuffer), TEAM_MONITOR);
event.AddInt32("event", eventCode);
event.AddInt32("team", team->id);
event.AddPointer("teamStruct", team);
DefaultNotificationService::Notify(event, eventCode);
}
Team::Team(team_id id, bool kernel)
{
this->id = id;
visible = true;
hash_next = parent = NULL;
serial_number = -1;
group_id = session_id = -1;
group = NULL;
num_threads = 0;
state = TEAM_STATE_BIRTH;
flags = 0;
io_context = NULL;
user_mutex_context = NULL;
realtime_sem_context = NULL;
xsi_sem_context = NULL;
death_entry = NULL;
dead_children.condition_variable.Init(&dead_children, "team children");
dead_children.count = 0;
dead_children.kernel_time = 0;
dead_children.user_time = 0;
job_control_entry = new(nothrow) ::job_control_entry;
if (job_control_entry != NULL) {
job_control_entry->state = JOB_CONTROL_STATE_NONE;
job_control_entry->thread = id;
job_control_entry->team = this;
}
address_space = NULL;
main_thread = NULL;
loading_info = NULL;
list_init(&watcher_list);
list_init(&sem_list);
list_init_etc(&port_list, port_team_link_offset());
user_data = 0;
user_data_area = -1;
used_user_data = 0;
user_data_size = 0;
free_user_threads = NULL;
commpage_address = NULL;
clear_team_debug_info(&debug_info, true);
dead_threads_kernel_time = 0;
dead_threads_user_time = 0;
cpu_clock_offset = 0;
B_INITIALIZE_SPINLOCK(&time_lock);
saved_set_uid = real_uid = effective_uid = -1;
saved_set_gid = real_gid = effective_gid = -1;
exit.initialized = false;
B_INITIALIZE_SPINLOCK(&signal_lock);
if (kernel) {
mutex_init(&fLock, "Team:kernel");
} else {
char lockName[16];
snprintf(lockName, sizeof(lockName), "Team:%" B_PRId32, id);
mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
}
fName[0] = '\0';
fArgs[0] = '\0';
fQueuedSignalsCounter = new(std::nothrow) BKernel::QueuedSignalsCounter(
kernel ? -1 : MAX_QUEUED_SIGNALS);
memset(fSignalActions, 0, sizeof(fSignalActions));
fUserDefinedTimerCount = 0;
fCoreDumpCondition = NULL;
}
Team::~Team()
{
PrepareForDeletion();
if (io_context != NULL)
vfs_put_io_context(io_context);
delete_owned_ports(this);
sem_delete_owned_sems(this);
DeleteUserTimers(false);
fPendingSignals.Clear();
if (fQueuedSignalsCounter != NULL)
fQueuedSignalsCounter->ReleaseReference();
while (thread_death_entry* threadDeathEntry = dead_threads.RemoveHead())
free(threadDeathEntry);
while (::job_control_entry* entry = dead_children.entries.RemoveHead())
delete entry;
while (free_user_thread* entry = free_user_threads) {
free_user_threads = entry->next;
free(entry);
}
delete job_control_entry;
mutex_destroy(&fLock);
}
Team*
Team::Create(team_id id, const char* name, bool kernel)
{
Team* team = new(std::nothrow) Team(id, kernel);
if (team == NULL)
return NULL;
ObjectDeleter<Team> teamDeleter(team);
if (name != NULL)
team->SetName(name);
if (team->job_control_entry == NULL || team->fQueuedSignalsCounter == NULL)
return NULL;
if (arch_team_init_team_struct(team, kernel) != B_OK)
return NULL;
if (!kernel) {
status_t error = user_timer_create_team_timers(team);
if (error != B_OK)
return NULL;
}
team->start_time = system_time();
return teamDeleter.Detach();
}
Team*
Team::Get(team_id id)
{
if (id == B_CURRENT_TEAM) {
Team* team = thread_get_current_thread()->team;
team->AcquireReference();
return team;
}
InterruptsReadSpinLocker locker(sTeamHashLock);
Team* team = sTeamHash.Lookup(id);
if (team != NULL)
team->AcquireReference();
return team;
}
Team*
Team::GetAndLock(team_id id)
{
Team* team = Get(id);
if (team == NULL)
return NULL;
team->Lock();
if (team->state >= TEAM_STATE_SHUTDOWN) {
team->Unlock();
team->ReleaseReference();
return NULL;
}
return team;
}
void
Team::LockTeamAndParent(bool dontLockParentIfKernel)
{
Lock();
while (true) {
Team* parent = this->parent;
if (parent == NULL || (dontLockParentIfKernel && parent == sKernelTeam)
|| parent->TryLock()) {
return;
}
BReference<Team> parentReference(parent);
Unlock();
parent->Lock();
Lock();
if (this->parent == parent)
return;
parent->Unlock();
}
}
void
Team::UnlockTeamAndParent()
{
if (parent != NULL)
parent->Unlock();
Unlock();
}
void
Team::LockTeamParentAndProcessGroup()
{
LockTeamAndProcessGroup();
if (this->parent == NULL || this->parent->TryLock())
return;
Unlock();
LockTeamAndParent(false);
}
void
Team::UnlockTeamParentAndProcessGroup()
{
group->Unlock();
if (parent != NULL)
parent->Unlock();
Unlock();
}
void
Team::LockTeamAndProcessGroup()
{
Lock();
while (true) {
ProcessGroup* group = this->group;
if (group == NULL || group->TryLock())
return;
BReference<ProcessGroup> groupReference(group);
Unlock();
group->Lock();
Lock();
if (this->group == group)
return;
group->Unlock();
}
}
void
Team::UnlockTeamAndProcessGroup()
{
group->Unlock();
Unlock();
}
void
Team::SetName(const char* name)
{
if (const char* lastSlash = strrchr(name, '/'))
name = lastSlash + 1;
strlcpy(fName, name, B_OS_NAME_LENGTH);
}
void
Team::SetArgs(const char* args)
{
strlcpy(fArgs, args, sizeof(fArgs));
}
void
Team::SetArgs(const char* path, const char* const* otherArgs, int otherArgCount)
{
fArgs[0] = '\0';
strlcpy(fArgs, path, sizeof(fArgs));
for (int i = 0; i < otherArgCount; i++) {
strlcat(fArgs, " ", sizeof(fArgs));
strlcat(fArgs, otherArgs[i], sizeof(fArgs));
}
}
void
Team::ResetSignalsOnExec()
{
for (uint32 i = 1; i <= MAX_SIGNAL_NUMBER; i++) {
struct sigaction& action = SignalActionFor(i);
if (action.sa_handler != SIG_IGN && action.sa_handler != SIG_DFL)
action.sa_handler = SIG_DFL;
action.sa_mask = 0;
action.sa_flags = 0;
action.sa_userdata = NULL;
}
}
void
Team::InheritSignalActions(Team* parent)
{
memcpy(fSignalActions, parent->fSignalActions, sizeof(fSignalActions));
}
status_t
Team::AddUserTimer(UserTimer* timer)
{
AssertLocked();
if (state >= TEAM_STATE_SHUTDOWN)
return B_BAD_TEAM_ID;
if (timer->ID() < 0 && !CheckAddUserDefinedTimer())
return EAGAIN;
fUserTimers.AddTimer(timer);
return B_OK;
}
void
Team::RemoveUserTimer(UserTimer* timer)
{
AssertLocked();
fUserTimers.RemoveTimer(timer);
if (timer->ID() >= USER_TIMER_FIRST_USER_DEFINED_ID)
UserDefinedTimersRemoved(1);
}
void
Team::DeleteUserTimers(bool userDefinedOnly)
{
int32 count = fUserTimers.DeleteTimers(userDefinedOnly);
UserDefinedTimersRemoved(count);
}
bool
Team::CheckAddUserDefinedTimer()
{
int32 oldCount = atomic_add(&fUserDefinedTimerCount, 1);
if (oldCount >= MAX_USER_TIMERS_PER_TEAM) {
atomic_add(&fUserDefinedTimerCount, -1);
return false;
}
return true;
}
void
Team::UserDefinedTimersRemoved(int32 count)
{
atomic_add(&fUserDefinedTimerCount, -count);
}
void
Team::DeactivateCPUTimeUserTimers()
{
while (TeamTimeUserTimer* timer = fCPUTimeUserTimers.Head())
timer->Deactivate();
while (TeamUserTimeUserTimer* timer = fUserTimeUserTimers.Head())
timer->Deactivate();
}
bigtime_t
Team::CPUTime(bool ignoreCurrentRun, Thread* lockedThread) const
{
bigtime_t time = cpu_clock_offset + dead_threads_kernel_time
+ dead_threads_user_time;
Thread* currentThread = thread_get_current_thread();
bigtime_t now = system_time();
for (Thread* thread = thread_list.First(); thread != NULL;
thread = thread_list.GetNext(thread)) {
bool alreadyLocked = thread == lockedThread;
SpinLocker threadTimeLocker(thread->time_lock, alreadyLocked);
time += thread->kernel_time + thread->user_time;
if (thread->last_time != 0) {
if (!ignoreCurrentRun || thread != currentThread)
time += now - thread->last_time;
}
if (alreadyLocked)
threadTimeLocker.Detach();
}
return time;
}
bigtime_t
Team::UserCPUTime() const
{
bigtime_t time = dead_threads_user_time;
bigtime_t now = system_time();
for (Thread* thread = thread_list.First(); thread != NULL;
thread = thread_list.GetNext(thread)) {
SpinLocker threadTimeLocker(thread->time_lock);
time += thread->user_time;
if (thread->last_time != 0 && !thread->in_kernel)
time += now - thread->last_time;
}
return time;
}
ProcessGroup::ProcessGroup(pid_t id)
:
id(id),
fSession(NULL),
fInOrphanedCheckList(false)
{
char lockName[32];
snprintf(lockName, sizeof(lockName), "Group:%" B_PRId32, id);
mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
}
ProcessGroup::~ProcessGroup()
{
TRACE(("ProcessGroup::~ProcessGroup(): id = %" B_PRId32 "\n", id));
MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
if (fInOrphanedCheckList)
sOrphanedCheckProcessGroups.Remove(this);
orphanedCheckLocker.Unlock();
if (fSession != NULL) {
InterruptsSpinLocker groupHashLocker(sGroupHashLock);
sGroupHash.RemoveUnchecked(this);
groupHashLocker.Unlock();
fSession->ReleaseReference();
}
mutex_destroy(&fLock);
}
ProcessGroup*
ProcessGroup::Get(pid_t id)
{
InterruptsSpinLocker groupHashLocker(sGroupHashLock);
ProcessGroup* group = sGroupHash.Lookup(id);
if (group != NULL)
group->AcquireReference();
return group;
}
void
ProcessGroup::Publish(ProcessSession* session)
{
InterruptsSpinLocker groupHashLocker(sGroupHashLock);
PublishLocked(session);
}
void
ProcessGroup::PublishLocked(ProcessSession* session)
{
ASSERT(sGroupHash.Lookup(this->id) == NULL);
fSession = session;
fSession->AcquireReference();
sGroupHash.InsertUnchecked(this);
}
bool
ProcessGroup::IsOrphaned() const
{
bool orphaned = true;
Team* team = teams.First();
while (orphaned && team != NULL) {
team->LockTeamAndParent(false);
Team* parent = team->parent;
if (parent != NULL && parent->group_id != id
&& parent->session_id == fSession->id) {
orphaned = false;
}
team->UnlockTeamAndParent();
team = teams.GetNext(team);
}
return orphaned;
}
void
ProcessGroup::ScheduleOrphanedCheck()
{
MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
if (!fInOrphanedCheckList) {
sOrphanedCheckProcessGroups.Add(this);
fInOrphanedCheckList = true;
}
}
void
ProcessGroup::UnsetOrphanedCheck()
{
fInOrphanedCheckList = false;
}
ProcessSession::ProcessSession(pid_t id)
:
id(id),
controlling_tty(NULL),
foreground_group(-1)
{
char lockName[32];
snprintf(lockName, sizeof(lockName), "Session:%" B_PRId32, id);
mutex_init_etc(&fLock, lockName, MUTEX_FLAG_CLONE_NAME);
}
ProcessSession::~ProcessSession()
{
mutex_destroy(&fLock);
}
static void
_dump_team_info(Team* team)
{
kprintf("TEAM: %p\n", team);
kprintf("id: %" B_PRId32 " (%#" B_PRIx32 ")\n", team->id,
team->id);
kprintf("serial_number: %" B_PRId64 "\n", team->serial_number);
kprintf("name: '%s'\n", team->Name());
kprintf("args: '%s'\n", team->Args());
kprintf("hash_next: %p\n", team->hash_next);
kprintf("parent: %p", team->parent);
if (team->parent != NULL) {
kprintf(" (id = %" B_PRId32 ")\n", team->parent->id);
} else
kprintf("\n");
kprintf("children: %p\n", team->children.First());
kprintf("num_threads: %d\n", team->num_threads);
kprintf("state: %d\n", team->state);
kprintf("flags: 0x%" B_PRIx32 "\n", team->flags);
kprintf("io_context: %p\n", team->io_context);
if (team->address_space)
kprintf("address_space: %p\n", team->address_space);
kprintf("user data: %p (area %" B_PRId32 ")\n",
(void*)team->user_data, team->user_data_area);
kprintf("free user thread: %p\n", team->free_user_threads);
kprintf("main_thread: %p\n", team->main_thread);
kprintf("thread_list: %p\n", team->thread_list.First());
kprintf("group_id: %" B_PRId32 "\n", team->group_id);
kprintf("session_id: %" B_PRId32 "\n", team->session_id);
}
static int
dump_team_info(int argc, char** argv)
{
ulong arg;
bool found = false;
if (argc < 2) {
Thread* thread = thread_get_current_thread();
if (thread != NULL && thread->team != NULL)
_dump_team_info(thread->team);
else
kprintf("No current team!\n");
return 0;
}
arg = strtoul(argv[1], NULL, 0);
if (IS_KERNEL_ADDRESS(arg)) {
_dump_team_info((Team*)arg);
return 0;
}
for (TeamTable::Iterator it = sTeamHash.GetIterator();
Team* team = it.Next();) {
if ((team->Name() && strcmp(argv[1], team->Name()) == 0)
|| team->id == (team_id)arg) {
_dump_team_info(team);
found = true;
break;
}
}
if (!found)
kprintf("team \"%s\" (%" B_PRId32 ") doesn't exist!\n", argv[1], (team_id)arg);
return 0;
}
static int
dump_teams(int argc, char** argv)
{
kprintf("%-*s id %-*s name\n", B_PRINTF_POINTER_WIDTH, "team",
B_PRINTF_POINTER_WIDTH, "parent");
for (TeamTable::Iterator it = sTeamHash.GetIterator();
Team* team = it.Next();) {
kprintf("%p%7" B_PRId32 " %p %s\n", team, team->id, team->parent, team->Name());
}
return 0;
}
static pid_t
_getppid(pid_t id)
{
if (id < 0) {
errno = EINVAL;
return -1;
}
if (id == 0) {
Team* team = thread_get_current_thread()->team;
TeamLocker teamLocker(team);
if (team->parent == NULL) {
errno = EINVAL;
return -1;
}
return team->parent->id;
}
Team* team = Team::GetAndLock(id);
if (team == NULL) {
errno = ESRCH;
return -1;
}
pid_t parentID;
if (team->parent == NULL) {
errno = EINVAL;
parentID = -1;
} else
parentID = team->parent->id;
team->UnlockAndReleaseReference();
return parentID;
}
static void
insert_team_into_parent(Team* parent, Team* team)
{
ASSERT(parent != NULL);
parent->children.Add(team, false);
team->parent = parent;
}
static void
remove_team_from_parent(Team* parent, Team* team)
{
parent->children.Remove(team);
team->parent = NULL;
}
static bool
is_session_leader(Team* team)
{
return team->session_id == team->id;
}
static bool
is_process_group_leader(Team* team)
{
return team->group_id == team->id;
}
static void
insert_team_into_group(ProcessGroup* group, Team* team)
{
team->group = group;
team->group_id = group->id;
team->session_id = group->Session()->id;
group->teams.Add(team, false);
group->AcquireReference();
}
static void
remove_team_from_group(Team* team)
{
ProcessGroup* group = team->group;
if (group == NULL)
return;
group->teams.Remove(team);
team->group = NULL;
team->group_id = -1;
group->ReleaseReference();
}
static status_t
create_team_user_data(Team* team, void* exactAddress = NULL)
{
void* address;
uint32 addressSpec;
if (exactAddress != NULL) {
address = exactAddress;
addressSpec = B_EXACT_ADDRESS;
} else {
address = (void*)KERNEL_USER_DATA_BASE;
addressSpec = B_RANDOMIZED_BASE_ADDRESS;
}
status_t result = vm_reserve_address_range(team->id, &address, addressSpec,
kTeamUserDataReservedSize, RESERVED_AVOID_BASE);
virtual_address_restrictions virtualRestrictions = {};
if (result == B_OK || exactAddress != NULL) {
if (exactAddress != NULL)
virtualRestrictions.address = exactAddress;
else
virtualRestrictions.address = address;
virtualRestrictions.address_specification = B_EXACT_ADDRESS;
} else {
virtualRestrictions.address = (void*)KERNEL_USER_DATA_BASE;
virtualRestrictions.address_specification = B_RANDOMIZED_BASE_ADDRESS;
}
physical_address_restrictions physicalRestrictions = {};
team->user_data_area = create_area_etc(team->id, "user area",
kTeamUserDataInitialSize, B_FULL_LOCK,
B_READ_AREA | B_WRITE_AREA | B_KERNEL_AREA, 0, 0,
&virtualRestrictions, &physicalRestrictions, &address);
if (team->user_data_area < 0)
return team->user_data_area;
team->user_data = (addr_t)address;
team->used_user_data = 0;
team->user_data_size = kTeamUserDataInitialSize;
team->free_user_threads = NULL;
return B_OK;
}
static void
delete_team_user_data(Team* team)
{
if (team->user_data_area >= 0) {
vm_delete_area(team->id, team->user_data_area, true);
vm_unreserve_address_range(team->id, (void*)team->user_data,
kTeamUserDataReservedSize);
team->user_data = 0;
team->used_user_data = 0;
team->user_data_size = 0;
team->user_data_area = -1;
while (free_user_thread* entry = team->free_user_threads) {
team->free_user_threads = entry->next;
free(entry);
}
}
}
static status_t
copy_user_process_args(const char* const* userFlatArgs, size_t flatArgsSize,
int32 argCount, int32 envCount, char**& _flatArgs)
{
if (argCount < 0 || envCount < 0)
return B_BAD_VALUE;
if (flatArgsSize > MAX_PROCESS_ARGS_SIZE)
return B_TOO_MANY_ARGS;
if ((argCount + envCount + 2) * sizeof(char*) > flatArgsSize)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userFlatArgs))
return B_BAD_ADDRESS;
char** flatArgs = (char**)malloc(_ALIGN(flatArgsSize));
if (flatArgs == NULL)
return B_NO_MEMORY;
if (user_memcpy(flatArgs, userFlatArgs, flatArgsSize) != B_OK) {
free(flatArgs);
return B_BAD_ADDRESS;
}
status_t error = B_OK;
const char* stringBase = (char*)flatArgs + argCount + envCount + 2;
const char* stringEnd = (char*)flatArgs + flatArgsSize;
for (int32 i = 0; i < argCount + envCount + 2; i++) {
if (i == argCount || i == argCount + envCount + 1) {
if (flatArgs[i] != NULL) {
error = B_BAD_VALUE;
break;
}
} else {
char* arg = (char*)flatArgs + (flatArgs[i] - (char*)userFlatArgs);
size_t maxLen = stringEnd - arg;
if (arg < stringBase || arg >= stringEnd
|| strnlen(arg, maxLen) == maxLen) {
error = B_BAD_VALUE;
break;
}
flatArgs[i] = arg;
}
}
if (error == B_OK)
_flatArgs = flatArgs;
else
free(flatArgs);
return error;
}
static void
free_team_arg(struct team_arg* teamArg)
{
if (teamArg != NULL) {
free(teamArg->flat_args);
free(teamArg->path);
free(teamArg);
}
}
static status_t
create_team_arg(struct team_arg** _teamArg, const char* path, char** flatArgs,
size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask,
port_id port, uint32 token)
{
struct team_arg* teamArg = (struct team_arg*)malloc(sizeof(team_arg));
if (teamArg == NULL)
return B_NO_MEMORY;
teamArg->path = strdup(path);
if (teamArg->path == NULL) {
free(teamArg);
return B_NO_MEMORY;
}
teamArg->flat_args = flatArgs;
teamArg->flat_args_size = flatArgsSize;
teamArg->arg_count = argCount;
teamArg->env_count = envCount;
teamArg->flags = 0;
teamArg->umask = umask;
teamArg->error_port = port;
teamArg->error_token = token;
const char* const* env = flatArgs + argCount + 1;
for (int32 i = 0; i < envCount; i++) {
if (strcmp(env[i], "DISABLE_ASLR=1") == 0) {
teamArg->flags |= TEAM_ARGS_FLAG_NO_ASLR;
break;
}
}
*_teamArg = teamArg;
return B_OK;
}
static status_t
team_create_thread_start_internal(void* args)
{
status_t err;
Thread* thread;
Team* team;
struct team_arg* teamArgs = (struct team_arg*)args;
const char* path;
addr_t entry;
char** userArgs;
char** userEnv;
struct user_space_program_args* programArgs;
uint32 argCount, envCount;
thread = thread_get_current_thread();
team = thread->team;
cache_node_launched(teamArgs->arg_count, teamArgs->flat_args);
TRACE(("team_create_thread_start: entry thread %" B_PRId32 "\n",
thread->id));
argCount = teamArgs->arg_count;
envCount = teamArgs->env_count;
programArgs = (struct user_space_program_args*)(thread->user_stack_base
+ thread->user_stack_size + TLS_SIZE);
userArgs = (char**)(programArgs + 1);
userEnv = userArgs + argCount + 1;
path = teamArgs->path;
if (user_strlcpy(programArgs->program_path, path,
sizeof(programArgs->program_path)) < B_OK
|| user_memcpy(&programArgs->arg_count, &argCount, sizeof(int32)) < B_OK
|| user_memcpy(&programArgs->args, &userArgs, sizeof(char**)) < B_OK
|| user_memcpy(&programArgs->env_count, &envCount, sizeof(int32)) < B_OK
|| user_memcpy(&programArgs->env, &userEnv, sizeof(char**)) < B_OK
|| user_memcpy(&programArgs->error_port, &teamArgs->error_port,
sizeof(port_id)) < B_OK
|| user_memcpy(&programArgs->error_token, &teamArgs->error_token,
sizeof(uint32)) < B_OK
|| user_memcpy(&programArgs->umask, &teamArgs->umask, sizeof(mode_t)) < B_OK
|| user_memcpy(&programArgs->disable_user_addons,
&sDisableUserAddOns, sizeof(bool)) < B_OK
|| user_memcpy(userArgs, teamArgs->flat_args,
teamArgs->flat_args_size) < B_OK) {
free_team_arg(teamArgs);
return B_BAD_ADDRESS;
}
free_team_arg(teamArgs);
TRACE(("team_create_thread_start: loading elf binary '%s'\n", path));
team->Lock();
team->state = TEAM_STATE_NORMAL;
team->Unlock();
area_id commPageArea = clone_commpage_area(team->id,
&team->commpage_address);
if (commPageArea < B_OK) {
TRACE(("team_create_thread_start: clone_commpage_area() failed: %s\n",
strerror(commPageArea)));
return commPageArea;
}
image_id commPageImage = get_commpage_image();
extended_image_info imageInfo;
err = get_image_info(commPageImage, &imageInfo.basic_info);
if (err != B_OK) {
TRACE(("team_create_thread_start: get_image_info() failed: %s\n",
strerror(err)));
return err;
}
imageInfo.basic_info.text = team->commpage_address;
imageInfo.text_delta = (ssize_t)(addr_t)team->commpage_address;
imageInfo.symbol_table = NULL;
imageInfo.symbol_hash = NULL;
imageInfo.string_table = NULL;
image_id image = register_image(team, &imageInfo, sizeof(imageInfo));
if (image < 0) {
TRACE(("team_create_thread_start: register_image() failed: %s\n",
strerror(image)));
return image;
}
user_debug_image_created(&imageInfo.basic_info);
{
KPath runtimeLoaderPath;
err = __find_directory(B_SYSTEM_DIRECTORY, gBootDevice, false,
runtimeLoaderPath.LockBuffer(), runtimeLoaderPath.BufferSize());
if (err < B_OK) {
TRACE(("team_create_thread_start: find_directory() failed: %s\n",
strerror(err)));
return err;
}
runtimeLoaderPath.UnlockBuffer();
err = runtimeLoaderPath.Append("runtime_loader");
if (err == B_OK) {
err = elf_load_user_image(runtimeLoaderPath.Path(), team, 0,
&entry);
}
}
if (err < B_OK) {
TRACE(("team_create_thread_start: elf_load_user_image() failed: "
"%s\n", strerror(err)));
return err;
}
TRACE(("team_create_thread_start: loaded elf. entry = %#lx\n", entry));
return thread_enter_userspace_new_team(thread, (addr_t)entry,
programArgs, team->commpage_address);
}
static status_t
team_create_thread_start(void* args)
{
team_create_thread_start_internal(args);
team_init_exit_info_on_error(thread_get_current_thread()->team);
thread_exit();
return B_OK;
}
static thread_id
load_image_internal(char**& _flatArgs, size_t flatArgsSize, int32 argCount,
int32 envCount, int32 priority, team_id parentID, uint32 flags,
port_id errorPort, uint32 errorToken)
{
char** flatArgs = _flatArgs;
thread_id thread;
status_t status;
struct team_arg* teamArgs;
struct team_loading_info loadingInfo;
ConditionVariableEntry loadingWaitEntry;
io_context* parentIOContext = NULL;
team_id teamID;
bool teamLimitReached = false;
if (flatArgs == NULL || argCount == 0)
return B_BAD_VALUE;
const char* path = flatArgs[0];
TRACE(("load_image_internal: name '%s', args = %p, argCount = %" B_PRId32
"\n", path, flatArgs, argCount));
const char* threadName = strrchr(path, '/');
if (threadName != NULL)
threadName++;
else
threadName = path;
Thread* mainThread;
status = Thread::Create(threadName, mainThread);
if (status != B_OK)
return status;
BReference<Thread> mainThreadReference(mainThread, true);
Team* team = Team::Create(mainThread->id, path, false);
if (team == NULL)
return B_NO_MEMORY;
BReference<Team> teamReference(team, true);
BReference<Team> teamLoadingReference;
if ((flags & B_WAIT_TILL_LOADED) != 0) {
loadingInfo.condition.Init(team, "image load");
loadingInfo.condition.Add(&loadingWaitEntry);
loadingInfo.result = B_ERROR;
team->loading_info = &loadingInfo;
teamLoadingReference = teamReference;
}
Team* parent = Team::Get(parentID);
if (parent == NULL)
return B_BAD_TEAM_ID;
BReference<Team> parentReference(parent, true);
parent->LockTeamAndProcessGroup();
team->Lock();
inherit_parent_user_and_group(team, parent);
parentIOContext = (parent->id == B_SYSTEM_TEAM) ? NULL : parent->io_context;
if (parentIOContext != NULL)
vfs_get_io_context(parentIOContext);
team->Unlock();
parent->UnlockTeamAndProcessGroup();
update_set_id_user_and_group(team, path);
status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize, argCount,
envCount, (mode_t)-1, errorPort, errorToken);
if (status != B_OK)
goto err1;
_flatArgs = NULL;
team->SetArgs(path, teamArgs->flat_args + 1, argCount - 1);
team->io_context = vfs_new_io_context(parentIOContext, true);
if (team->io_context == NULL) {
status = B_NO_MEMORY;
goto err2;
}
if (parentIOContext != NULL) {
vfs_put_io_context(parentIOContext);
parentIOContext = NULL;
}
status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false,
&team->address_space);
if (status != B_OK)
goto err2;
team->address_space->SetRandomizingEnabled(
(teamArgs->flags & TEAM_ARGS_FLAG_NO_ASLR) == 0);
status = create_team_user_data(team);
if (status != B_OK)
goto err4;
parent->LockTeamAndProcessGroup();
team->Lock();
{
InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
sTeamHash.Insert(team);
teamLimitReached = sUsedTeams >= sMaxTeams;
if (!teamLimitReached)
sUsedTeams++;
}
insert_team_into_parent(parent, team);
insert_team_into_group(parent->group, team);
team->Unlock();
parent->UnlockTeamAndProcessGroup();
sNotificationService.Notify(TEAM_ADDED, team);
if (teamLimitReached) {
status = B_NO_MORE_TEAMS;
goto err6;
}
teamID = team->id;
{
ThreadCreationAttributes threadAttributes(team_create_thread_start,
threadName, B_NORMAL_PRIORITY, teamArgs, teamID, mainThread);
threadAttributes.additional_stack_size = sizeof(user_space_program_args)
+ teamArgs->flat_args_size;
thread = thread_create_thread(threadAttributes, false);
if (thread < 0) {
status = thread;
goto err6;
}
}
teamReference.Detach();
user_debug_team_created(teamID);
if ((flags & B_WAIT_TILL_LOADED) != 0) {
if (mainThread != NULL) {
thread_continue(mainThread);
}
loadingWaitEntry.Wait();
team->Lock();
ASSERT(team->loading_info == NULL);
team->Unlock();
teamLoadingReference.Unset();
if (loadingInfo.result < B_OK)
return loadingInfo.result;
}
return thread;
err6:
parent->LockTeamAndProcessGroup();
team->Lock();
remove_team_from_group(team);
remove_team_from_parent(team->parent, team);
team->Unlock();
parent->UnlockTeamAndProcessGroup();
{
InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
sTeamHash.Remove(team);
if (!teamLimitReached)
sUsedTeams--;
}
sNotificationService.Notify(TEAM_REMOVED, team);
delete_team_user_data(team);
err4:
team->address_space->Put();
err2:
free_team_arg(teamArgs);
err1:
if (parentIOContext != NULL)
vfs_put_io_context(parentIOContext);
return status;
}
static void
team_kill_other_threads_locked(TeamLocker &teamLocker, thread_id threadNotBeKilled = -1)
{
Team* team = teamLocker.Get();
team_death_entry deathEntry;
deathEntry.condition.Init(team, "team death");
while (true) {
team->death_entry = &deathEntry;
deathEntry.remaining_threads = 0;
for (Thread* thread = team->thread_list.First(); thread != NULL;
thread = team->thread_list.GetNext(thread)) {
if (thread != team->main_thread && thread->id != threadNotBeKilled) {
Signal signal(SIGKILLTHR, SI_USER, B_OK, team->id);
send_signal_to_thread(thread, signal, B_DO_NOT_RESCHEDULE);
deathEntry.remaining_threads++;
}
}
if (deathEntry.remaining_threads == 0)
break;
ConditionVariableEntry entry;
deathEntry.condition.Add(&entry);
teamLocker.Unlock();
entry.Wait();
teamLocker.Lock();
}
team->death_entry = NULL;
}
static status_t
exec_team(const char* path, char**& _flatArgs, size_t flatArgsSize,
int32 argCount, int32 envCount, mode_t umask)
{
char** flatArgs = _flatArgs;
Team* team = thread_get_current_thread()->team;
struct team_arg* teamArgs;
const char* threadName;
thread_id nubThreadID = -1;
TRACE(("exec_team(path = \"%s\", argc = %" B_PRId32 ", envCount = %"
B_PRId32 "): team %" B_PRId32 "\n", path, argCount, envCount,
team->id));
T(ExecTeam(path, argCount, flatArgs, envCount, flatArgs + argCount + 1));
if (team == team_get_kernel_team())
return B_NOT_ALLOWED;
Thread* currentThread = thread_get_current_thread();
if (currentThread != team->main_thread)
return B_NOT_ALLOWED;
TeamLocker teamLocker(team);
InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED)
nubThreadID = team->debug_info.nub_thread;
debugInfoLocker.Unlock();
team_kill_other_threads_locked(teamLocker, nubThreadID);
team->DeleteUserTimers(true);
team->ResetSignalsOnExec();
teamLocker.Unlock();
status_t status = create_team_arg(&teamArgs, path, flatArgs, flatArgsSize,
argCount, envCount, umask, -1, 0);
if (status != B_OK)
return status;
_flatArgs = NULL;
team->SetArgs(path, teamArgs->flat_args + 1, argCount - 1);
thread_reset_for_exec();
user_debug_prepare_for_exec();
delete_team_user_data(team);
vm_delete_areas(team->address_space, false);
xsi_sem_undo(team);
delete_owned_ports(team);
sem_delete_owned_sems(team);
remove_images(team);
vfs_exec_io_context(team->io_context);
delete_user_mutex_context(team->user_mutex_context);
team->user_mutex_context = NULL;
delete_realtime_sem_context(team->realtime_sem_context);
team->realtime_sem_context = NULL;
team->address_space->SetRandomizingEnabled(
(teamArgs->flags & TEAM_ARGS_FLAG_NO_ASLR) == 0);
status = create_team_user_data(team);
if (status != B_OK) {
free_team_arg(teamArgs);
exit_thread(status);
return status;
}
user_debug_finish_after_exec();
team->Lock();
team->SetName(path);
team->Unlock();
threadName = strrchr(path, '/');
if (threadName != NULL)
threadName++;
else
threadName = path;
rename_thread(thread_get_current_thread_id(), threadName);
atomic_or(&team->flags, TEAM_FLAG_EXEC_DONE);
update_set_id_user_and_group(team, path);
user_debug_team_exec();
sNotificationService.Notify(TEAM_EXEC, team);
user_thread* userThread = team_allocate_user_thread(team);
ThreadLocker currentThreadLocker(currentThread);
currentThread->user_thread = userThread;
currentThreadLocker.Unlock();
status = thread_create_user_stack(currentThread->team, currentThread, NULL,
0, sizeof(user_space_program_args) + teamArgs->flat_args_size);
if (status == B_OK) {
team_create_thread_start(teamArgs);
} else
free_team_arg(teamArgs);
exit_thread(status);
return B_ERROR;
}
static thread_id
fork_team(void)
{
Thread* parentThread = thread_get_current_thread();
Team* parentTeam = parentThread->team;
Team* team;
arch_fork_arg* forkArgs;
struct area_info info;
thread_id threadID;
status_t status;
ssize_t areaCookie;
bool teamLimitReached = false;
TRACE(("fork_team(): team %" B_PRId32 "\n", parentTeam->id));
if (parentTeam == team_get_kernel_team())
return B_NOT_ALLOWED;
Thread* thread;
status = Thread::Create(parentThread->name, thread);
if (status != B_OK)
return status;
BReference<Thread> threadReference(thread, true);
team = Team::Create(thread->id, NULL, false);
if (team == NULL)
return B_NO_MEMORY;
parentTeam->LockTeamAndProcessGroup();
team->Lock();
team->SetName(parentTeam->Name());
team->SetArgs(parentTeam->Args());
team->commpage_address = parentTeam->commpage_address;
inherit_parent_user_and_group(team, parentTeam);
team->InheritSignalActions(parentTeam);
team->Unlock();
parentTeam->UnlockTeamAndProcessGroup();
team->debug_info.flags |= atomic_get(&parentTeam->debug_info.flags)
& B_TEAM_DEBUG_INHERITED_FLAGS;
forkArgs = (arch_fork_arg*)malloc(sizeof(arch_fork_arg));
if (forkArgs == NULL) {
status = B_NO_MEMORY;
goto err1;
}
team->io_context = vfs_new_io_context(parentTeam->io_context, false);
if (!team->io_context) {
status = B_NO_MEMORY;
goto err2;
}
if (parentTeam->realtime_sem_context) {
team->realtime_sem_context = clone_realtime_sem_context(
parentTeam->realtime_sem_context);
if (team->realtime_sem_context == NULL) {
status = B_NO_MEMORY;
goto err2;
}
}
status = VMAddressSpace::Create(team->id, USER_BASE, USER_SIZE, false,
&team->address_space);
if (status < B_OK)
goto err3;
areaCookie = 0;
while (get_next_area_info(B_CURRENT_TEAM, &areaCookie, &info) == B_OK) {
if (info.area == parentTeam->user_data_area) {
status = create_team_user_data(team, info.address);
if (status != B_OK)
break;
thread->user_thread = team_allocate_user_thread(team);
} else {
void* address;
area_id area = vm_copy_area(team->address_space->ID(), info.name,
&address, B_CLONE_ADDRESS, info.area);
if (area < B_OK) {
status = area;
break;
}
if (info.area == parentThread->user_stack_area)
thread->user_stack_area = area;
}
}
if (status < B_OK)
goto err4;
if (thread->user_thread == NULL) {
#if KDEBUG
panic("user data area not found, parent area is %" B_PRId32,
parentTeam->user_data_area);
#endif
status = B_ERROR;
goto err4;
}
thread->user_stack_base = parentThread->user_stack_base;
thread->user_stack_size = parentThread->user_stack_size;
thread->user_local_storage = parentThread->user_local_storage;
thread->sig_block_mask = parentThread->sig_block_mask;
thread->signal_stack_base = parentThread->signal_stack_base;
thread->signal_stack_size = parentThread->signal_stack_size;
thread->signal_stack_enabled = parentThread->signal_stack_enabled;
arch_store_fork_frame(forkArgs);
if (copy_images(parentTeam->id, team) != B_OK)
goto err5;
parentTeam->LockTeamAndProcessGroup();
team->Lock();
{
InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
sTeamHash.Insert(team);
teamLimitReached = sUsedTeams >= sMaxTeams;
if (!teamLimitReached)
sUsedTeams++;
}
insert_team_into_parent(parentTeam, team);
insert_team_into_group(parentTeam->group, team);
team->Unlock();
parentTeam->UnlockTeamAndProcessGroup();
sNotificationService.Notify(TEAM_ADDED, team);
if (teamLimitReached) {
status = B_NO_MORE_TEAMS;
goto err6;
}
{
ThreadCreationAttributes threadCreationAttributes(NULL,
parentThread->name, parentThread->priority, NULL, team->id, thread);
threadCreationAttributes.forkArgs = forkArgs;
threadCreationAttributes.flags |= THREAD_CREATION_FLAG_DEFER_SIGNALS;
threadCreationAttributes.signal_mask = thread->sig_block_mask;
threadID = thread_create_thread(threadCreationAttributes, false);
if (threadID < 0) {
status = threadID;
goto err6;
}
}
user_debug_team_created(team->id);
T(TeamForked(threadID));
resume_thread(threadID);
return threadID;
err6:
parentTeam->LockTeamAndProcessGroup();
team->Lock();
remove_team_from_group(team);
remove_team_from_parent(team->parent, team);
team->Unlock();
parentTeam->UnlockTeamAndProcessGroup();
{
InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
sTeamHash.Remove(team);
if (!teamLimitReached)
sUsedTeams--;
}
sNotificationService.Notify(TEAM_REMOVED, team);
err5:
remove_images(team);
err4:
team->address_space->RemoveAndPut();
err3:
delete_realtime_sem_context(team->realtime_sem_context);
err2:
free(forkArgs);
err1:
team->ReleaseReference();
return status;
}
static bool
has_children_in_group(Team* parent, pid_t groupID)
{
for (Team* child = parent->children.First(); child != NULL;
child = parent->children.GetNext(child)) {
TeamLocker childLocker(child);
if (child->group_id == groupID)
return true;
}
return false;
}
static job_control_entry*
get_job_control_entry(team_job_control_children& children, pid_t id)
{
for (JobControlEntryList::Iterator it = children.entries.GetIterator();
job_control_entry* entry = it.Next();) {
if (id > 0) {
if (entry->thread == id)
return entry;
} else if (id == -1) {
return entry;
} else {
pid_t processGroup
= (entry->team ? entry->team->group_id : entry->group_id);
if (processGroup == -id)
return entry;
}
}
return NULL;
}
static job_control_entry*
get_job_control_entry(Team* team, pid_t id, uint32 flags)
{
job_control_entry* entry = NULL;
if ((flags & WEXITED) != 0)
entry = get_job_control_entry(team->dead_children, id);
if (entry == NULL && (flags & WCONTINUED) != 0)
entry = get_job_control_entry(team->continued_children, id);
if (entry == NULL && (flags & (WUNTRACED | WSTOPPED)) != 0)
entry = get_job_control_entry(team->stopped_children, id);
return entry;
}
job_control_entry::job_control_entry()
:
has_group_ref(false)
{
}
job_control_entry::~job_control_entry()
{
if (has_group_ref) {
InterruptsSpinLocker groupHashLocker(sGroupHashLock);
ProcessGroup* group = sGroupHash.Lookup(group_id);
if (group == NULL) {
panic("job_control_entry::~job_control_entry(): unknown group "
"ID: %" B_PRId32, group_id);
return;
}
groupHashLocker.Unlock();
group->ReleaseReference();
}
}
void
job_control_entry::InitDeadState()
{
if (team != NULL) {
ASSERT(team->exit.initialized);
group_id = team->group_id;
team->group->AcquireReference();
has_group_ref = true;
thread = team->id;
status = team->exit.status;
reason = team->exit.reason;
signal = team->exit.signal;
signaling_user = team->exit.signaling_user;
user_time = team->dead_threads_user_time
+ team->dead_children.user_time;
kernel_time = team->dead_threads_kernel_time
+ team->dead_children.kernel_time;
team = NULL;
}
}
job_control_entry&
job_control_entry::operator=(const job_control_entry& other)
{
state = other.state;
thread = other.thread;
signal = other.signal;
has_group_ref = false;
signaling_user = other.signaling_user;
team = other.team;
group_id = other.group_id;
status = other.status;
reason = other.reason;
user_time = other.user_time;
kernel_time = other.kernel_time;
return *this;
}
static thread_id
wait_for_child(pid_t child, uint32 flags, siginfo_t& _info,
team_usage_info& _usage_info)
{
Thread* thread = thread_get_current_thread();
Team* team = thread->team;
struct job_control_entry foundEntry;
struct job_control_entry* freeDeathEntry = NULL;
status_t status = B_OK;
TRACE(("wait_for_child(child = %" B_PRId32 ", flags = %" B_PRId32 ")\n",
child, flags));
T(WaitForChild(child, flags));
if ((flags & (WEXITED | WUNTRACED | WSTOPPED | WCONTINUED)) == 0) {
T(WaitForChildDone(B_BAD_VALUE));
return B_BAD_VALUE;
}
pid_t originalChild = child;
bool ignoreFoundEntries = false;
bool ignoreFoundEntriesChecked = false;
while (true) {
TeamLocker teamLocker(team);
child = originalChild == 0 ? -team->group_id : originalChild;
job_control_entry* entry = get_job_control_entry(team, child, flags);
if (entry == NULL) {
bool childrenExist = false;
if (child == -1) {
childrenExist = !team->children.IsEmpty();
} else if (child < -1) {
childrenExist = has_children_in_group(team, -child);
} else if (child != team->id) {
if (Team* childTeam = Team::Get(child)) {
BReference<Team> childTeamReference(childTeam, true);
TeamLocker childTeamLocker(childTeam);
childrenExist = childTeam->parent == team;
}
}
if (!childrenExist) {
status = ECHILD;
} else {
status = B_WOULD_BLOCK;
}
} else {
foundEntry = *entry;
if ((flags & WNOWAIT) == 0 || ignoreFoundEntries) {
if (entry->state == JOB_CONTROL_STATE_DEAD) {
freeDeathEntry = entry;
team->dead_children.entries.Remove(entry);
team->dead_children.count--;
} else {
team_set_job_control_state(entry->team,
JOB_CONTROL_STATE_NONE, NULL);
}
}
}
ConditionVariableEntry deadWaitEntry;
if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0)
team->dead_children.condition_variable.Add(&deadWaitEntry);
teamLocker.Unlock();
if (status == B_OK) {
if (ignoreFoundEntries) {
delete freeDeathEntry;
freeDeathEntry = NULL;
continue;
}
break;
}
if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0) {
T(WaitForChildDone(status));
return status;
}
status = deadWaitEntry.Wait(B_CAN_INTERRUPT);
if (status == B_INTERRUPTED) {
T(WaitForChildDone(status));
return status;
}
if (!ignoreFoundEntriesChecked) {
teamLocker.Lock();
struct sigaction& handler = team->SignalActionFor(SIGCHLD);
if ((handler.sa_flags & SA_NOCLDWAIT) != 0
|| handler.sa_handler == SIG_IGN) {
ignoreFoundEntries = true;
}
teamLocker.Unlock();
ignoreFoundEntriesChecked = true;
}
}
delete freeDeathEntry;
memset(&_info, 0, sizeof(_info));
_info.si_signo = SIGCHLD;
_info.si_pid = foundEntry.thread;
_info.si_uid = foundEntry.signaling_user;
switch (foundEntry.state) {
case JOB_CONTROL_STATE_DEAD:
_info.si_code = foundEntry.reason;
_info.si_status = foundEntry.reason == CLD_EXITED
? foundEntry.status : foundEntry.signal;
_usage_info.user_time = foundEntry.user_time;
_usage_info.kernel_time = foundEntry.kernel_time;
break;
case JOB_CONTROL_STATE_STOPPED:
_info.si_code = CLD_STOPPED;
_info.si_status = foundEntry.signal;
break;
case JOB_CONTROL_STATE_CONTINUED:
_info.si_code = CLD_CONTINUED;
_info.si_status = 0;
break;
case JOB_CONTROL_STATE_NONE:
break;
}
TeamLocker teamLocker(team);
InterruptsSpinLocker signalLocker(team->signal_lock);
SpinLocker threadCreationLocker(gThreadCreationLock);
if (is_team_signal_blocked(team, SIGCHLD)) {
if (get_job_control_entry(team, child, flags) == NULL)
team->RemovePendingSignals(SIGNAL_TO_MASK(SIGCHLD));
}
threadCreationLocker.Unlock();
signalLocker.Unlock();
teamLocker.Unlock();
if (foundEntry.state == JOB_CONTROL_STATE_DEAD)
wait_for_thread(foundEntry.thread, NULL);
T(WaitForChildDone(foundEntry));
return foundEntry.thread;
}
static status_t
fill_team_info(Team* team, team_info* info, size_t size)
{
if (size > sizeof(team_info))
return B_BAD_VALUE;
memset(info, 0, size);
info->team = team->id;
info->image_count = count_images(team);
TeamLocker teamLocker(team);
InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
info->thread_count = team->num_threads;
info->debugger_nub_thread = team->debug_info.nub_thread;
info->debugger_nub_port = team->debug_info.nub_port;
info->uid = team->effective_uid;
info->gid = team->effective_gid;
strlcpy(info->args, team->Args(), sizeof(info->args));
info->argc = 1;
if (size > offsetof(team_info, real_uid)) {
info->real_uid = team->real_uid;
info->real_gid = team->real_gid;
info->group_id = team->group_id;
info->session_id = team->session_id;
if (team->parent != NULL)
info->parent = team->parent->id;
else
info->parent = -1;
strlcpy(info->name, team->Name(), sizeof(info->name));
info->start_time = team->start_time;
}
return B_OK;
}
static bool
process_group_has_stopped_processes(ProcessGroup* group)
{
Team* team = group->teams.First();
while (team != NULL) {
team->LockTeamAndParent(false);
if (team->job_control_entry != NULL
&& team->job_control_entry->state == JOB_CONTROL_STATE_STOPPED) {
team->UnlockTeamAndParent();
return true;
}
team->UnlockTeamAndParent();
team = group->teams.GetNext(team);
}
return false;
}
static void
orphaned_process_group_check()
{
while (true) {
MutexLocker orphanedCheckLocker(sOrphanedCheckLock);
ProcessGroup* group = sOrphanedCheckProcessGroups.RemoveHead();
if (group == NULL)
return;
group->UnsetOrphanedCheck();
BReference<ProcessGroup> groupReference(group);
orphanedCheckLocker.Unlock();
AutoLocker<ProcessGroup> groupLocker(group);
if (group->IsOrphaned() && process_group_has_stopped_processes(group)) {
Thread* currentThread = thread_get_current_thread();
Signal signal(SIGHUP, SI_USER, B_OK, currentThread->team->id);
send_signal_to_process_group_locked(group, signal, 0);
signal.SetNumber(SIGCONT);
send_signal_to_process_group_locked(group, signal, 0);
}
}
}
static status_t
common_get_team_usage_info(team_id id, int32 who, team_usage_info* info,
uint32 flags)
{
if (who != B_TEAM_USAGE_SELF && who != B_TEAM_USAGE_CHILDREN)
return B_BAD_VALUE;
Team* team = Team::GetAndLock(id);
if (team == NULL)
return B_BAD_TEAM_ID;
BReference<Team> teamReference(team, true);
TeamLocker teamLocker(team, true);
if ((flags & B_CHECK_PERMISSION) != 0) {
uid_t uid = geteuid();
if (uid != 0 && uid != team->effective_uid)
return B_NOT_ALLOWED;
}
bigtime_t kernelTime = 0;
bigtime_t userTime = 0;
switch (who) {
case B_TEAM_USAGE_SELF:
{
for (Thread* thread = team->thread_list.First(); thread != NULL;
thread = team->thread_list.GetNext(thread)) {
InterruptsSpinLocker threadTimeLocker(thread->time_lock);
kernelTime += thread->kernel_time;
userTime += thread->user_time;
}
kernelTime += team->dead_threads_kernel_time;
userTime += team->dead_threads_user_time;
break;
}
case B_TEAM_USAGE_CHILDREN:
{
Team* child = team->children.First();
for (; child != NULL; child = team->children.GetNext(child)) {
TeamLocker childLocker(child);
for (Thread* thread = child->thread_list.First(); thread != NULL;
thread = child->thread_list.GetNext(thread)) {
InterruptsSpinLocker threadTimeLocker(thread->time_lock);
kernelTime += thread->kernel_time;
userTime += thread->user_time;
}
kernelTime += child->dead_threads_kernel_time;
userTime += child->dead_threads_user_time;
}
kernelTime += team->dead_children.kernel_time;
userTime += team->dead_children.user_time;
break;
}
}
info->kernel_time = kernelTime;
info->user_time = userTime;
return B_OK;
}
status_t
team_init(kernel_args* args)
{
new(&sTeamHash) TeamTable;
if (sTeamHash.Init(64) != B_OK)
panic("Failed to init team hash table!");
new(&sGroupHash) ProcessGroupHashTable;
if (sGroupHash.Init() != B_OK)
panic("Failed to init process group hash table!");
ProcessSession* session = new(std::nothrow) ProcessSession(1);
if (session == NULL)
panic("Could not create initial session.\n");
BReference<ProcessSession> sessionReference(session, true);
ProcessGroup* group = new(std::nothrow) ProcessGroup(1);
if (group == NULL)
panic("Could not create initial process group.\n");
BReference<ProcessGroup> groupReference(group, true);
group->Publish(session);
sKernelTeam = Team::Create(1, "kernel_team", true);
if (sKernelTeam == NULL)
panic("could not create kernel team!\n");
sKernelTeam->address_space = VMAddressSpace::Kernel();
sKernelTeam->SetArgs(sKernelTeam->Name());
sKernelTeam->state = TEAM_STATE_NORMAL;
sKernelTeam->saved_set_uid = 0;
sKernelTeam->real_uid = 0;
sKernelTeam->effective_uid = 0;
sKernelTeam->saved_set_gid = 0;
sKernelTeam->real_gid = 0;
sKernelTeam->effective_gid = 0;
sKernelTeam->supplementary_groups = NULL;
insert_team_into_group(group, sKernelTeam);
sKernelTeam->io_context = vfs_new_io_context(NULL, false);
if (sKernelTeam->io_context == NULL)
panic("could not create io_context for kernel team!\n");
if (vfs_resize_fd_table(sKernelTeam->io_context, 4096) != B_OK)
dprintf("Failed to resize FD table for kernel team!\n");
sTeamHash.Insert(sKernelTeam);
sDisableUserAddOns = get_safemode_boolean(B_SAFEMODE_DISABLE_USER_ADD_ONS,
false);
add_debugger_command_etc("team", &dump_team_info,
"Dump info about a particular team",
"[ <id> | <address> | <name> ]\n"
"Prints information about the specified team. If no argument is given\n"
"the current team is selected.\n"
" <id> - The ID of the team.\n"
" <address> - The address of the team structure.\n"
" <name> - The team's name.\n", 0);
add_debugger_command_etc("teams", &dump_teams, "List all teams",
"\n"
"Prints a list of all existing teams.\n", 0);
new(&sNotificationService) TeamNotificationService();
sNotificationService.Register();
return B_OK;
}
int32
team_max_teams(void)
{
return sMaxTeams;
}
int32
team_used_teams(void)
{
InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
return sUsedTeams;
}
job_control_entry*
team_get_death_entry(Team* team, thread_id child, bool* _deleteEntry)
{
if (child <= 0)
return NULL;
job_control_entry* entry = get_job_control_entry(team->dead_children,
child);
if (entry) {
if (team_get_current_team_id() == entry->thread) {
team->dead_children.entries.Remove(entry);
team->dead_children.count--;
*_deleteEntry = true;
} else {
*_deleteEntry = false;
}
}
return entry;
}
bool
team_is_valid(team_id id)
{
if (id <= 0)
return false;
InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
return team_get_team_struct_locked(id) != NULL;
}
Team*
team_get_team_struct_locked(team_id id)
{
return sTeamHash.Lookup(id);
}
void
team_set_controlling_tty(void* tty)
{
Team* team = thread_get_current_thread()->team;
TeamLocker teamLocker(team);
ProcessSession* session = team->group->Session();
AutoLocker<ProcessSession> sessionLocker(session);
session->controlling_tty = tty;
session->foreground_group = -1;
}
void*
team_get_controlling_tty()
{
Team* team = thread_get_current_thread()->team;
TeamLocker teamLocker(team);
ProcessSession* session = team->group->Session();
AutoLocker<ProcessSession> sessionLocker(session);
return session->controlling_tty;
}
status_t
team_set_foreground_process_group(void* tty, pid_t processGroupID)
{
Thread* thread = thread_get_current_thread();
Team* team = thread->team;
TeamLocker teamLocker(team);
ProcessSession* session = team->group->Session();
AutoLocker<ProcessSession> sessionLocker(session);
if (session->controlling_tty != tty)
return ENOTTY;
{
InterruptsSpinLocker groupHashLocker(sGroupHashLock);
ProcessGroup* group = sGroupHash.Lookup(processGroupID);
if (group == NULL)
return B_BAD_VALUE;
if (group->Session() != session)
return EPERM;
}
if (session->foreground_group != -1
&& session->foreground_group != team->group_id
&& team->SignalActionFor(SIGTTOU).sa_handler != SIG_IGN
&& (thread->sig_block_mask & SIGNAL_TO_MASK(SIGTTOU)) == 0) {
InterruptsSpinLocker signalLocker(team->signal_lock);
if (!is_team_signal_blocked(team, SIGTTOU)) {
pid_t groupID = team->group_id;
signalLocker.Unlock();
sessionLocker.Unlock();
teamLocker.Unlock();
Signal signal(SIGTTOU, SI_USER, B_OK, team->id);
send_signal_to_process_group(groupID, signal, 0);
return B_INTERRUPTED;
}
}
session->foreground_group = processGroupID;
return B_OK;
}
uid_t
team_geteuid(team_id id)
{
InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
Team* team = team_get_team_struct_locked(id);
if (team == NULL)
return (uid_t)-1;
return team->effective_uid;
}
void
team_remove_team(Team* team, pid_t& _signalGroup)
{
Team* parent = team->parent;
parent->dead_children.kernel_time += team->dead_threads_kernel_time
+ team->dead_children.kernel_time;
parent->dead_children.user_time += team->dead_threads_user_time
+ team->dead_children.user_time;
InterruptsWriteSpinLocker teamsLocker(sTeamHashLock);
sTeamHash.Remove(team);
sUsedTeams--;
teamsLocker.Unlock();
team->state = TEAM_STATE_DEATH;
_signalGroup = -1;
bool isSessionLeader = false;
if (team->session_id == team->id
&& team->group->Session()->controlling_tty != NULL) {
isSessionLeader = true;
ProcessSession* session = team->group->Session();
AutoLocker<ProcessSession> sessionLocker(session);
session->controlling_tty = NULL;
_signalGroup = session->foreground_group;
}
remove_team_from_group(team);
while (Team* child = team->children.First()) {
TeamLocker childLocker(child);
remove_team_from_parent(team, child);
insert_team_into_parent(sKernelTeam, child);
sKernelTeam->stopped_children.entries.TakeFrom(
&team->stopped_children.entries);
sKernelTeam->continued_children.entries.TakeFrom(
&team->continued_children.entries);
if (isSessionLeader) {
ProcessGroup* childGroup = child->group;
if (childGroup->Session()->id == team->session_id
&& childGroup->id != team->group_id) {
childGroup->ScheduleOrphanedCheck();
}
}
}
remove_team_from_parent(parent, team);
}
port_id
team_shutdown_team(Team* team)
{
ASSERT(thread_get_current_thread() == team->main_thread);
TeamLocker teamLocker(team);
port_id debuggerPort = -1;
while (true) {
ConditionVariableEntry waitForDebuggerEntry;
bool waitForDebugger = false;
InterruptsSpinLocker debugInfoLocker(team->debug_info.lock);
if (team->debug_info.debugger_changed_condition != NULL) {
team->debug_info.debugger_changed_condition->Add(
&waitForDebuggerEntry);
waitForDebugger = true;
} else if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED) {
debuggerPort = team->debug_info.debugger_port;
}
debugInfoLocker.Unlock();
if (!waitForDebugger)
break;
teamLocker.Unlock();
waitForDebuggerEntry.Wait();
teamLocker.Lock();
}
team->state = TEAM_STATE_SHUTDOWN;
team->DeleteUserTimers(false);
InterruptsSpinLocker timeLocker(team->time_lock);
if (team->HasActiveCPUTimeUserTimers())
team->DeactivateCPUTimeUserTimers();
timeLocker.Unlock();
team_kill_other_threads_locked(teamLocker);
return debuggerPort;
}
void
team_delete_team(Team* team, port_id debuggerPort)
{
orphaned_process_group_check();
team_id teamID = team->id;
ASSERT(team->num_threads == 0);
TeamLocker teamLocker(team);
if (team->loading_info != NULL) {
team->loading_info->result = B_ERROR;
team->loading_info->condition.NotifyAll();
team->loading_info = NULL;
}
{
struct team_watcher* watcher;
while ((watcher = (struct team_watcher*)list_remove_head_item(
&team->watcher_list)) != NULL) {
watcher->hook(teamID, watcher->data);
free(watcher);
}
}
status_t exitStatus = -1;
int signal = -1;
switch (team->exit.reason) {
case CLD_EXITED:
exitStatus = team->exit.status;
break;
case CLD_KILLED:
signal = team->exit.signal;
break;
}
teamLocker.Unlock();
sNotificationService.Notify(TEAM_REMOVED, team);
InterruptsSpinLocker timeLocker(team->time_lock);
team_usage_info usageInfo;
usageInfo.kernel_time = team->dead_threads_kernel_time;
usageInfo.user_time = team->dead_threads_user_time;
timeLocker.Unlock();
delete_user_mutex_context(team->user_mutex_context);
delete_realtime_sem_context(team->realtime_sem_context);
xsi_sem_undo(team);
remove_images(team);
team->address_space->RemoveAndPut();
team->ReleaseReference();
user_debug_team_deleted(teamID, debuggerPort, exitStatus, signal, &usageInfo);
}
Team*
team_get_kernel_team(void)
{
return sKernelTeam;
}
team_id
team_get_kernel_team_id(void)
{
if (!sKernelTeam)
return 0;
return sKernelTeam->id;
}
team_id
team_get_current_team_id(void)
{
return thread_get_current_thread()->team->id;
}
status_t
team_get_address_space(team_id id, VMAddressSpace** _addressSpace)
{
if (id == sKernelTeam->id) {
*_addressSpace = VMAddressSpace::GetKernel();
return B_OK;
}
InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
Team* team = team_get_team_struct_locked(id);
if (team == NULL)
return B_BAD_VALUE;
team->address_space->Get();
*_addressSpace = team->address_space;
return B_OK;
}
void
team_set_job_control_state(Team* team, job_control_state newState,
Signal* signal)
{
if (team == NULL || team->job_control_entry == NULL)
return;
job_control_entry* entry = team->job_control_entry;
if (entry->state == newState || entry->state == JOB_CONTROL_STATE_DEAD)
return;
T(SetJobControlState(team->id, newState, signal));
switch (entry->state) {
case JOB_CONTROL_STATE_NONE:
break;
case JOB_CONTROL_STATE_DEAD:
break;
case JOB_CONTROL_STATE_STOPPED:
team->parent->stopped_children.entries.Remove(entry);
break;
case JOB_CONTROL_STATE_CONTINUED:
team->parent->continued_children.entries.Remove(entry);
break;
}
entry->state = newState;
if (signal != NULL) {
entry->signal = signal->Number();
entry->signaling_user = signal->SendingUser();
}
team_job_control_children* childList = NULL;
switch (entry->state) {
case JOB_CONTROL_STATE_NONE:
break;
case JOB_CONTROL_STATE_DEAD:
childList = &team->parent->dead_children;
team->parent->dead_children.count++;
break;
case JOB_CONTROL_STATE_STOPPED:
childList = &team->parent->stopped_children;
break;
case JOB_CONTROL_STATE_CONTINUED:
childList = &team->parent->continued_children;
break;
}
if (childList != NULL) {
childList->entries.Add(entry);
team->parent->dead_children.condition_variable.NotifyAll();
}
}
void
team_init_exit_info_on_error(Team* team)
{
TeamLocker teamLocker(team);
if (!team->exit.initialized) {
team->exit.reason = CLD_KILLED;
team->exit.signal = SIGKILL;
team->exit.signaling_user = geteuid();
team->exit.status = 0;
team->exit.initialized = true;
}
}
status_t
start_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data)
{
if (hook == NULL || teamID < B_OK)
return B_BAD_VALUE;
team_watcher* watcher = (team_watcher*)malloc(sizeof(team_watcher));
if (watcher == NULL)
return B_NO_MEMORY;
watcher->hook = hook;
watcher->data = data;
Team* team = Team::GetAndLock(teamID);
if (team == NULL) {
free(watcher);
return B_BAD_TEAM_ID;
}
list_add_item(&team->watcher_list, watcher);
team->UnlockAndReleaseReference();
return B_OK;
}
status_t
stop_watching_team(team_id teamID, void (*hook)(team_id, void*), void* data)
{
if (hook == NULL || teamID < 0)
return B_BAD_VALUE;
Team* team = Team::GetAndLock(teamID);
if (team == NULL)
return B_BAD_TEAM_ID;
team_watcher* watcher = NULL;
while ((watcher = (team_watcher*)list_get_next_item(
&team->watcher_list, watcher)) != NULL) {
if (watcher->hook == hook && watcher->data == data) {
list_remove_item(&team->watcher_list, watcher);
break;
}
}
team->UnlockAndReleaseReference();
if (watcher == NULL)
return B_ENTRY_NOT_FOUND;
free(watcher);
return B_OK;
}
struct user_thread*
team_allocate_user_thread(Team* team)
{
if (team->user_data == 0)
return NULL;
if (struct free_user_thread* entry = team->free_user_threads) {
user_thread* thread = entry->thread;
team->free_user_threads = entry->next;
free(entry);
return thread;
}
while (true) {
size_t needed = ROUNDUP(sizeof(user_thread), CACHE_LINE_SIZE);
if (team->user_data_size - team->used_user_data < needed) {
if (resize_area(team->user_data_area,
team->user_data_size + B_PAGE_SIZE) != B_OK) {
return NULL;
}
team->user_data_size += B_PAGE_SIZE;
continue;
}
user_thread* thread
= (user_thread*)(team->user_data + team->used_user_data);
team->used_user_data += needed;
return thread;
}
}
void
team_free_user_thread(Team* team, struct user_thread* userThread)
{
if (userThread == NULL)
return;
free_user_thread* entry
= (free_user_thread*)malloc(sizeof(free_user_thread));
if (entry == NULL) {
return;
}
TeamLocker teamLocker(team);
entry->thread = userThread;
entry->next = team->free_user_threads;
team->free_user_threads = entry;
}
AssociatedData::AssociatedData()
:
fOwner(NULL)
{
}
AssociatedData::~AssociatedData()
{
}
void
AssociatedData::OwnerDeleted(AssociatedDataOwner* owner)
{
}
AssociatedDataOwner::AssociatedDataOwner()
{
mutex_init(&fLock, "associated data owner");
}
AssociatedDataOwner::~AssociatedDataOwner()
{
mutex_destroy(&fLock);
}
bool
AssociatedDataOwner::AddData(AssociatedData* data)
{
MutexLocker locker(fLock);
if (data->Owner() != NULL)
return false;
data->AcquireReference();
fList.Add(data);
data->SetOwner(this);
return true;
}
bool
AssociatedDataOwner::RemoveData(AssociatedData* data)
{
MutexLocker locker(fLock);
if (data->Owner() != this)
return false;
data->SetOwner(NULL);
fList.Remove(data);
locker.Unlock();
data->ReleaseReference();
return true;
}
void
AssociatedDataOwner::PrepareForDeletion()
{
MutexLocker locker(fLock);
DataList list;
list.TakeFrom(&fList);
for (DataList::Iterator it = list.GetIterator();
AssociatedData* data = it.Next();) {
data->SetOwner(NULL);
}
locker.Unlock();
while (AssociatedData* data = list.RemoveHead()) {
data->OwnerDeleted(this);
data->ReleaseReference();
}
}
bool
team_associate_data(AssociatedData* data)
{
return thread_get_current_thread()->team->AddData(data);
}
bool
team_dissociate_data(AssociatedData* data)
{
return thread_get_current_thread()->team->RemoveData(data);
}
thread_id
load_image(int32 argCount, const char** args, const char** env)
{
return load_image_etc(argCount, args, env, B_NORMAL_PRIORITY,
B_CURRENT_TEAM, B_WAIT_TILL_LOADED);
}
thread_id
load_image_etc(int32 argCount, const char* const* args,
const char* const* env, int32 priority, team_id parentID, uint32 flags)
{
if (args == NULL)
return B_BAD_VALUE;
int32 argSize = 0;
for (int32 i = 0; i < argCount; i++)
argSize += strlen(args[i]) + 1;
int32 envCount = 0;
int32 envSize = 0;
while (env != NULL && env[envCount] != NULL)
envSize += strlen(env[envCount++]) + 1;
int32 size = (argCount + envCount + 2) * sizeof(char*) + argSize + envSize;
if (size > MAX_PROCESS_ARGS_SIZE)
return B_TOO_MANY_ARGS;
char** flatArgs = (char**)malloc(size);
if (flatArgs == NULL)
return B_NO_MEMORY;
char** slot = flatArgs;
char* stringSpace = (char*)(flatArgs + argCount + envCount + 2);
for (int32 i = 0; i < argCount; i++) {
int32 argSize = strlen(args[i]) + 1;
memcpy(stringSpace, args[i], argSize);
*slot++ = stringSpace;
stringSpace += argSize;
}
*slot++ = NULL;
for (int32 i = 0; i < envCount; i++) {
int32 envSize = strlen(env[i]) + 1;
memcpy(stringSpace, env[i], envSize);
*slot++ = stringSpace;
stringSpace += envSize;
}
*slot++ = NULL;
thread_id thread = load_image_internal(flatArgs, size, argCount, envCount,
B_NORMAL_PRIORITY, parentID, B_WAIT_TILL_LOADED, -1, 0);
free(flatArgs);
return thread;
}
status_t
wait_for_team(team_id id, status_t* _returnCode)
{
InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
Team* team = team_get_team_struct_locked(id);
if (team == NULL)
return B_BAD_TEAM_ID;
id = team->id;
teamsLocker.Unlock();
return wait_for_thread(id, _returnCode);
}
status_t
kill_team(team_id id)
{
InterruptsReadSpinLocker teamsLocker(sTeamHashLock);
Team* team = team_get_team_struct_locked(id);
if (team == NULL)
return B_BAD_TEAM_ID;
id = team->id;
teamsLocker.Unlock();
if (team == sKernelTeam)
return B_NOT_ALLOWED;
return kill_thread(id);
}
status_t
_get_team_info(team_id id, team_info* info, size_t size)
{
Team* team = Team::Get(id);
if (team == NULL)
return B_BAD_TEAM_ID;
BReference<Team> teamReference(team, true);
return fill_team_info(team, info, size);
}
status_t
_get_next_team_info(int32* cookie, team_info* info, size_t size)
{
int32 slot = *cookie;
if (slot < 1)
slot = 1;
InterruptsReadSpinLocker locker(sTeamHashLock);
team_id lastTeamID = peek_next_thread_id();
Team* team = NULL;
while (slot < lastTeamID && !(team = team_get_team_struct_locked(slot)))
slot++;
if (team == NULL)
return B_BAD_TEAM_ID;
BReference<Team> teamReference(team);
locker.Unlock();
*cookie = ++slot;
return fill_team_info(team, info, size);
}
status_t
_get_team_usage_info(team_id id, int32 who, team_usage_info* info, size_t size)
{
if (size != sizeof(team_usage_info))
return B_BAD_VALUE;
return common_get_team_usage_info(id, who, info, 0);
}
pid_t
getpid(void)
{
return thread_get_current_thread()->team->id;
}
pid_t
getppid()
{
return _getppid(0);
}
pid_t
getpgid(pid_t id)
{
if (id < 0) {
errno = EINVAL;
return -1;
}
if (id == 0) {
Team* team = thread_get_current_thread()->team;
TeamLocker teamLocker(team);
return team->group_id;
}
Team* team = Team::GetAndLock(id);
if (team == NULL) {
errno = ESRCH;
return -1;
}
pid_t groupID = team->group_id;
team->UnlockAndReleaseReference();
return groupID;
}
pid_t
getsid(pid_t id)
{
if (id < 0) {
errno = EINVAL;
return -1;
}
if (id == 0) {
Team* team = thread_get_current_thread()->team;
TeamLocker teamLocker(team);
return team->session_id;
}
Team* team = Team::GetAndLock(id);
if (team == NULL) {
errno = ESRCH;
return -1;
}
pid_t sessionID = team->session_id;
team->UnlockAndReleaseReference();
return sessionID;
}
status_t
_user_exec(const char* userPath, const char* const* userFlatArgs,
size_t flatArgsSize, int32 argCount, int32 envCount, mode_t umask)
{
char path[B_PATH_NAME_LENGTH];
if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userFlatArgs)
|| user_strlcpy(path, userPath, sizeof(path)) < B_OK)
return B_BAD_ADDRESS;
char** flatArgs;
status_t error = copy_user_process_args(userFlatArgs, flatArgsSize,
argCount, envCount, flatArgs);
if (error == B_OK) {
error = exec_team(path, flatArgs, _ALIGN(flatArgsSize), argCount,
envCount, umask);
}
free(flatArgs);
return error;
}
thread_id
_user_fork(void)
{
return fork_team();
}
pid_t
_user_wait_for_child(thread_id child, uint32 flags, siginfo_t* userInfo,
team_usage_info* usageInfo)
{
if (userInfo != NULL && !IS_USER_ADDRESS(userInfo))
return B_BAD_ADDRESS;
if (usageInfo != NULL && !IS_USER_ADDRESS(usageInfo))
return B_BAD_ADDRESS;
siginfo_t info;
team_usage_info usage_info;
pid_t foundChild = wait_for_child(child, flags, info, usage_info);
if (foundChild < 0)
return syscall_restart_handle_post(foundChild);
if (userInfo != NULL && user_memcpy(userInfo, &info, sizeof(info)) != B_OK)
return B_BAD_ADDRESS;
if (usageInfo != NULL && user_memcpy(usageInfo, &usage_info,
sizeof(usage_info)) != B_OK) {
return B_BAD_ADDRESS;
}
return foundChild;
}
pid_t
_user_process_info(pid_t process, int32 which)
{
pid_t result;
switch (which) {
case SESSION_ID:
result = getsid(process);
break;
case GROUP_ID:
result = getpgid(process);
break;
case PARENT_ID:
result = _getppid(process);
break;
default:
return B_BAD_VALUE;
}
return result >= 0 ? result : errno;
}
pid_t
_user_setpgid(pid_t processID, pid_t groupID)
{
if (groupID < 0)
return B_BAD_VALUE;
Team* currentTeam = thread_get_current_thread()->team;
if (processID == 0)
processID = currentTeam->id;
if (groupID == 0)
groupID = processID;
while (true) {
ProcessGroup* group = ProcessGroup::Get(groupID);
bool newGroup = false;
if (group == NULL) {
if (groupID != processID)
return B_NOT_ALLOWED;
group = new(std::nothrow) ProcessGroup(groupID);
if (group == NULL)
return B_NO_MEMORY;
newGroup = true;
}
BReference<ProcessGroup> groupReference(group, true);
Team* team = Team::Get(processID);
if (team == NULL)
return ESRCH;
BReference<Team> teamReference(team, true);
for (Team* parentTeam = team; parentTeam != currentTeam; parentTeam = parentTeam->parent) {
if (parentTeam == NULL || parentTeam->id == 1)
return ESRCH;
}
if (is_session_leader(team))
return B_NOT_ALLOWED;
while (true) {
team->LockProcessGroup();
ProcessGroup* oldGroup = team->group;
if (oldGroup == NULL) {
ASSERT(team->state >= TEAM_STATE_SHUTDOWN);
return ESRCH;
}
if (oldGroup == group) {
oldGroup->Unlock();
return group->id;
}
oldGroup->AcquireReference();
if (newGroup || group->id > oldGroup->id) {
group->Lock();
break;
}
if (group->TryLock())
break;
oldGroup->Unlock();
group->Lock();
oldGroup->Lock();
TeamLocker teamLocker(team);
if (team->group == oldGroup)
break;
teamLocker.Unlock();
oldGroup->Unlock();
group->Unlock();
oldGroup->ReleaseReference();
}
BReference<ProcessGroup> oldGroupReference(team->group, true);
AutoLocker<ProcessGroup> oldGroupLocker(team->group, true);
AutoLocker<ProcessGroup> groupLocker(group, true);
team->LockTeamAndParent(false);
TeamLocker parentLocker(team->parent, true);
TeamLocker teamLocker(team, true);
if (team != currentTeam) {
if (team->session_id != currentTeam->session_id)
return B_NOT_ALLOWED;
if ((team->flags & TEAM_FLAG_EXEC_DONE) != 0)
return EACCES;
}
if (newGroup) {
InterruptsSpinLocker groupHashLocker(sGroupHashLock);
if (sGroupHash.Lookup(groupID)) {
continue;
}
group->PublishLocked(team->group->Session());
} else if (group->Session()->id != team->session_id) {
return B_NOT_ALLOWED;
}
remove_team_from_group(team);
insert_team_into_group(group, team);
team->parent->dead_children.condition_variable.NotifyAll();
return group->id;
}
}
pid_t
_user_setsid(void)
{
Team* team = thread_get_current_thread()->team;
ProcessGroup* group = new(std::nothrow) ProcessGroup(team->id);
if (group == NULL)
return B_NO_MEMORY;
BReference<ProcessGroup> groupReference(group, true);
AutoLocker<ProcessGroup> groupLocker(group);
ProcessSession* session = new(std::nothrow) ProcessSession(group->id);
if (session == NULL)
return B_NO_MEMORY;
BReference<ProcessSession> sessionReference(session, true);
team->LockTeamParentAndProcessGroup();
BReference<ProcessGroup> oldGroupReference(team->group);
AutoLocker<ProcessGroup> oldGroupLocker(team->group, true);
TeamLocker parentLocker(team->parent, true);
TeamLocker teamLocker(team, true);
if (is_process_group_leader(team))
return B_NOT_ALLOWED;
remove_team_from_group(team);
group->Publish(session);
insert_team_into_group(group, team);
team->parent->dead_children.condition_variable.NotifyAll();
return group->id;
}
status_t
_user_wait_for_team(team_id id, status_t* _userReturnCode)
{
status_t returnCode;
status_t status;
if (_userReturnCode != NULL && !IS_USER_ADDRESS(_userReturnCode))
return B_BAD_ADDRESS;
status = wait_for_team(id, &returnCode);
if (status >= B_OK && _userReturnCode != NULL) {
if (user_memcpy(_userReturnCode, &returnCode, sizeof(returnCode))
!= B_OK)
return B_BAD_ADDRESS;
return B_OK;
}
return syscall_restart_handle_post(status);
}
thread_id
_user_load_image(const char* const* userFlatArgs, size_t flatArgsSize,
int32 argCount, int32 envCount, int32 priority, uint32 flags,
port_id errorPort, uint32 errorToken)
{
TRACE(("_user_load_image: argc = %" B_PRId32 "\n", argCount));
if (argCount < 1)
return B_BAD_VALUE;
char** flatArgs;
status_t error = copy_user_process_args(userFlatArgs, flatArgsSize,
argCount, envCount, flatArgs);
if (error != B_OK)
return error;
thread_id thread = load_image_internal(flatArgs, _ALIGN(flatArgsSize),
argCount, envCount, priority, B_CURRENT_TEAM, flags, errorPort,
errorToken);
free(flatArgs);
return thread;
}
void
_user_exit_team(status_t returnValue)
{
Thread* thread = thread_get_current_thread();
Team* team = thread->team;
thread->exit.status = returnValue;
TeamLocker teamLocker(team);
if (!team->exit.initialized) {
team->exit.reason = CLD_EXITED;
team->exit.signal = 0;
team->exit.signaling_user = 0;
team->exit.status = returnValue;
team->exit.initialized = true;
}
teamLocker.Unlock();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
if ((atomic_get(&team->debug_info.flags) & B_TEAM_DEBUG_PREVENT_EXIT) != 0)
user_debug_stop_thread();
#pragma GCC diagnostic pop
Signal signal(SIGKILL, SI_USER, B_OK, team->id);
send_signal_to_thread(thread, signal, 0);
}
status_t
_user_kill_team(team_id team)
{
return kill_team(team);
}
status_t
_user_get_team_info(team_id id, team_info* userInfo, size_t size)
{
status_t status;
team_info info;
if (size > sizeof(team_info))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userInfo))
return B_BAD_ADDRESS;
status = _get_team_info(id, &info, size);
if (status == B_OK) {
if (user_memcpy(userInfo, &info, size) < B_OK)
return B_BAD_ADDRESS;
}
return status;
}
status_t
_user_get_next_team_info(int32* userCookie, team_info* userInfo, size_t size)
{
status_t status;
team_info info;
int32 cookie;
if (size > sizeof(team_info))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userCookie)
|| !IS_USER_ADDRESS(userInfo)
|| user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
return B_BAD_ADDRESS;
status = _get_next_team_info(&cookie, &info, size);
if (status != B_OK)
return status;
if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
|| user_memcpy(userInfo, &info, size) < B_OK)
return B_BAD_ADDRESS;
return status;
}
team_id
_user_get_current_team(void)
{
return team_get_current_team_id();
}
status_t
_user_get_team_usage_info(team_id team, int32 who, team_usage_info* userInfo,
size_t size)
{
if (size != sizeof(team_usage_info))
return B_BAD_VALUE;
team_usage_info info;
status_t status = common_get_team_usage_info(team, who, &info,
B_CHECK_PERMISSION);
if (userInfo == NULL || !IS_USER_ADDRESS(userInfo)
|| user_memcpy(userInfo, &info, size) != B_OK) {
return B_BAD_ADDRESS;
}
return status;
}
status_t
_user_get_extended_team_info(team_id teamID, uint32 flags, void* buffer,
size_t size, size_t* _sizeNeeded)
{
if ((buffer != NULL && !IS_USER_ADDRESS(buffer))
|| (buffer == NULL && size > 0)
|| _sizeNeeded == NULL || !IS_USER_ADDRESS(_sizeNeeded)) {
return B_BAD_ADDRESS;
}
Team* team = Team::Get(teamID);
if (team == NULL)
return B_BAD_TEAM_ID;
BReference<Team> teamReference(team, true);
uid_t uid = geteuid();
if (uid != 0 && uid != team->effective_uid)
return B_NOT_ALLOWED;
KMessage info;
if ((flags & B_TEAM_INFO_BASIC) != 0) {
struct ExtendedTeamData {
team_id id;
pid_t group_id;
pid_t session_id;
uid_t real_uid;
gid_t real_gid;
uid_t effective_uid;
gid_t effective_gid;
char name[B_OS_NAME_LENGTH];
} teamClone;
io_context* ioContext;
{
TeamLocker teamLocker(team);
teamClone.id = team->id;
strlcpy(teamClone.name, team->Name(), sizeof(teamClone.name));
teamClone.group_id = team->group_id;
teamClone.session_id = team->session_id;
teamClone.real_uid = team->real_uid;
teamClone.real_gid = team->real_gid;
teamClone.effective_uid = team->effective_uid;
teamClone.effective_gid = team->effective_gid;
ioContext = team->io_context;
vfs_get_io_context(ioContext);
}
CObjectDeleter<io_context, void, vfs_put_io_context>
ioContextPutter(ioContext);
if (info.AddInt32("id", teamClone.id) != B_OK
|| info.AddString("name", teamClone.name) != B_OK
|| info.AddInt32("process group", teamClone.group_id) != B_OK
|| info.AddInt32("session", teamClone.session_id) != B_OK
|| info.AddInt32("uid", teamClone.real_uid) != B_OK
|| info.AddInt32("gid", teamClone.real_gid) != B_OK
|| info.AddInt32("euid", teamClone.effective_uid) != B_OK
|| info.AddInt32("egid", teamClone.effective_gid) != B_OK) {
return B_NO_MEMORY;
}
dev_t cwdDevice;
ino_t cwdDirectory;
{
ReadLocker ioContextLocker(ioContext->lock);
vfs_vnode_to_node_ref(ioContext->cwd, &cwdDevice, &cwdDirectory);
}
if (info.AddInt32("cwd device", cwdDevice) != B_OK
|| info.AddInt64("cwd directory", cwdDirectory) != B_OK) {
return B_NO_MEMORY;
}
}
size_t sizeNeeded = info.ContentSize();
if (user_memcpy(_sizeNeeded, &sizeNeeded, sizeof(sizeNeeded)) != B_OK)
return B_BAD_ADDRESS;
if (sizeNeeded > size)
return B_BUFFER_OVERFLOW;
if (user_memcpy(buffer, info.Buffer(), sizeNeeded) != B_OK)
return B_BAD_ADDRESS;
return B_OK;
}