/* Team functions */

/*
** Copyright 2002-2004, The OpenBeOS Team. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
**
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/

#include <OS.h>

#include <team.h>
#include <int.h>
#include <khash.h>
#include <port.h>
#include <sem.h>
#include <user_runtime.h>
#include <kimage.h>
#include <elf.h>
#include <syscalls.h>
#include <tls.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

//#define TRACE_TEAM
#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 **args;
	char **envp;
	unsigned int argc;
	unsigned int envc;
};

// team list
static void *team_hash = NULL;
static team_id next_team_id = 1;
static struct team *kernel_team = NULL;

spinlock team_spinlock = 0;

static struct team *create_team_struct(const char *name, bool kernel);
static void delete_team_struct(struct team *p);
static int team_struct_compare(void *_p, const void *_key);
static uint32 team_struct_hash(void *_p, const void *_key, uint32 range);
static void kfree_strings_array(char **strings, int strc);
static int user_copy_strings_array(char **strings, int strc, char ***kstrings);
static void _dump_team_info(struct team *p);
static int dump_team_info(int argc, char **argv);


static void
_dump_team_info(struct team *p)
{
	dprintf("TEAM: %p\n", p);
	dprintf("id:          0x%lx\n", p->id);
	dprintf("name:        '%s'\n", p->name);
	dprintf("next:        %p\n", p->next);
	dprintf("parent:      %p\n", p->parent);
	dprintf("children:    %p\n", p->children);
	dprintf("num_threads: %d\n", p->num_threads);
	dprintf("state:       %d\n", p->state);
	dprintf("pending_signals: 0x%x\n", p->pending_signals);
	dprintf("io_context:  %p\n", p->io_context);
//	dprintf("path:        '%s'\n", p->path);
	dprintf("aspace_id:   0x%lx\n", p->_aspace_id);
	dprintf("aspace:      %p\n", p->aspace);
	dprintf("kaspace:     %p\n", p->kaspace);
	dprintf("main_thread: %p\n", p->main_thread);
	dprintf("thread_list: %p\n", p->thread_list);
}


static int
dump_team_info(int argc, char **argv)
{
	struct team *p;
	int id = -1;
	unsigned long num;
	struct hash_iterator i;

	if (argc < 2) {
		dprintf("team: not enough arguments\n");
		return 0;
	}

	// if the argument looks like a hex number, treat it as such
	if (strlen(argv[1]) > 2 && argv[1][0] == '0' && argv[1][1] == 'x') {
		num = strtoul(argv[1], NULL, 16);
		if (num > vm_get_kernel_aspace()->virtual_map.base) {
			// XXX semi-hack
			_dump_team_info((struct team*)num);
			return 0;
		} else {
			id = num;
		}
	}

	// walk through the thread list, trying to match name or id
	hash_open(team_hash, &i);
	while ((p = hash_next(team_hash, &i)) != NULL) {
		if ((p->name && strcmp(argv[1], p->name) == 0) || p->id == id) {
			_dump_team_info(p);
			break;
		}
	}
	hash_close(team_hash, &i, false);
	return 0;
}


int
team_init(kernel_args *ka)
{
	// create the team hash table
	team_hash = hash_init(15, (addr)&kernel_team->next - (addr)kernel_team,
		&team_struct_compare, &team_struct_hash);

	// create the kernel team
	kernel_team = create_team_struct("kernel_team", true);
	if (kernel_team == NULL)
		panic("could not create kernel team!\n");
	kernel_team->state = TEAM_STATE_NORMAL;

	kernel_team->io_context = vfs_new_io_context(NULL);
	if (kernel_team->io_context == NULL)
		panic("could not create io_context for kernel team!\n");

	//XXX should initialize kernel_team->path here. Set it to "/"?

	// stick it in the team hash
	hash_insert(team_hash, kernel_team);

	add_debugger_command("team", &dump_team_info, "list info about a particular team");
	return 0;
}


/**	Frees an array of strings in kernel space
 *	Parameters
 *		strings		strings array
 *		strc		number of strings in array
 */

static void
kfree_strings_array(char **strings, int strc)
{
	int cnt = strc;


	if (strings != NULL) {
		for (cnt = 0; cnt < strc; cnt++){
			free(strings[cnt]);
		}
	    free(strings);
	}
}


/**	Copy an array of strings from user space to kernel space
 *	Parameters
 *		strings		userspace strings array
 *		strc		number of strings in array
 *		kstrings	pointer to the kernel copy
 *	Returns < 0 on error and **kstrings = NULL
 */

static int
user_copy_strings_array(char **userStrings, int strc, char ***kstrings)
{
	char **lstrings;
	int err;
	int cnt;
	char *source;
	char buffer[SYS_THREAD_STRING_LENGTH_MAX];

	*kstrings = NULL;

	if (!IS_USER_ADDRESS(userStrings))
		return B_BAD_ADDRESS;

	lstrings = (char **)malloc((strc + 1) * sizeof(char *));
	if (lstrings == NULL)
		return B_NO_MEMORY;

	// scan all strings and copy to kernel space

	for (cnt = 0; cnt < strc; cnt++) {
		err = user_memcpy(&source, &(userStrings[cnt]), sizeof(char *));
		if (err < 0)
			goto error;

		if (!IS_USER_ADDRESS(source)) {
			err = B_BAD_ADDRESS;
			goto error;
		}

		err = user_strlcpy(buffer, source, SYS_THREAD_STRING_LENGTH_MAX);
		if (err < 0)
			goto error;

		lstrings[cnt] = strdup(buffer);
		if (lstrings[cnt] == NULL){
			err = ENOMEM;
			goto error;
		}
	}

	lstrings[strc] = NULL;

	*kstrings = lstrings;
	return B_NO_ERROR;

error:
	kfree_strings_array(lstrings, cnt);
	dprintf("user_copy_strings_array failed %d \n", err);
	return err;
}


/** Quick check to see if we have a valid team ID.
 */

bool
team_is_valid(team_id id)
{
	struct team *team;
	int state;

	if (id <= 0)
		return false;

	state = disable_interrupts();
	GRAB_TEAM_LOCK();

	team = team_get_team_struct_locked(id);

	RELEASE_TEAM_LOCK();
	restore_interrupts(state);

	return team != NULL;
}


struct team *
team_get_team_struct_locked(team_id id)
{
	struct team_key key;

	key.id = id;

	return hash_lookup(team_hash, &key);
}


static int
team_struct_compare(void *_p, const void *_key)
{
	struct team *p = _p;
	const struct team_key *key = _key;

	if (p->id == key->id)
		return 0;

	return 1;
}


static uint32
team_struct_hash(void *_p, const void *_key, uint32 range)
{
	struct team *p = _p;
	const struct team_key *key = _key;

	if (p != NULL)
		return p->id % range;

	return key->id % range;
}


static void
insert_team_into_parent(struct team *parent, struct team *team)
{
	ASSERT(parent != NULL);

	team->siblings_next = parent->children;
	parent->children = team;
	team->parent = parent;
}


/**	Note: must have TEAM lock held
 */

static void
remove_team_from_parent(struct team *parent, struct team *team)
{
	struct team *child, *last = NULL;

	for (child = parent->children; child != NULL; child = child->siblings_next) {
		if (child == team) {
			if (last == NULL)
				parent->children = child->siblings_next;
			else
				last->siblings_next = child->siblings_next;

			team->parent = NULL;
			break;
		}
		last = child;
	}
}


/**	Reparent each of our children
 *	Note: must have TEAM lock held
 */

static void
reparent_children(struct team *team)
{
	struct team *child = team->children;

	while (child != NULL) {
		// remove the child from the current proc and add to the parent
		remove_team_from_parent(team, child);
		insert_team_into_parent(team->parent, child);

		child = team->children;
	}
}


struct team *
team_get_kernel_team(void)
{
	return kernel_team;
}


team_id
team_get_kernel_team_id(void)
{
	if (!kernel_team)
		return 0;

	return kernel_team->id;
}


team_id
team_get_current_team_id(void)
{
	return thread_get_current_thread()->team->id;
}


static struct team *
create_team_struct(const char *name, bool kernel)
{
	struct team *team = (struct team *)malloc(sizeof(struct team));
	if (team == NULL)
		return NULL;

	team->next = team->siblings_next = team->children = team->parent = NULL;
	team->id = atomic_add(&next_team_id, 1);
	strlcpy(team->name, name, B_OS_NAME_LENGTH);
	team->num_threads = 0;
	team->io_context = NULL;
	team->_aspace_id = -1;
	team->aspace = NULL;
	team->kaspace = vm_get_kernel_aspace();
	vm_put_aspace(team->kaspace);
	team->thread_list = NULL;
	team->main_thread = NULL;
	team->state = TEAM_STATE_BIRTH;
	team->pending_signals = 0;
	team->death_sem = -1;
	team->user_env_base = 0;
	list_init(&team->image_list);

	if (arch_team_init_team_struct(team, kernel) < 0)
		goto error;

	return team;

error:
	free(team);
	return NULL;
}


static void
delete_team_struct(struct team *team)
{
	free(team);
}


void
team_remove_team(struct team *team)
{
	hash_remove(team_hash, team);
	team->state = TEAM_STATE_DEATH;

	// reparent each of the team's children
	reparent_children(team);

	// remove us from our parent 
	remove_team_from_parent(team->parent, team);
}


void
team_delete_team(struct team *team)
{
	if (team->num_threads > 0) {
		// there are other threads still in this team,
		// cycle through and signal kill on each of the threads
		// XXX this can be optimized. There's got to be a better solution.
		cpu_status state;
		struct thread *temp_thread;
		char death_sem_name[B_OS_NAME_LENGTH];

		sprintf(death_sem_name, "team %ld death sem", team->id);
		team->death_sem = create_sem(0, death_sem_name);
		if (team->death_sem < 0)
			panic("thread_exit: cannot init death sem for team %ld\n", team->id);

		state = disable_interrupts();
		GRAB_TEAM_LOCK();
		// we can safely walk the list because of the lock. no new threads can be created
		// because of the TEAM_STATE_DEATH flag on the team
		temp_thread = team->thread_list;
		while (temp_thread) {
			struct thread *next = temp_thread->team_next;

			send_signal_etc(temp_thread->id, SIGKILLTHR, B_DO_NOT_RESCHEDULE);
			temp_thread = next;
		}
		RELEASE_TEAM_LOCK();
		restore_interrupts(state);

		// wait until all threads in team are dead.
		acquire_sem_etc(team->death_sem, team->num_threads, 0, 0);
		delete_sem(team->death_sem);
	}

	// free team resources
	vm_put_aspace(team->aspace);
	vm_delete_aspace(team->_aspace_id);
	delete_owned_ports(team->id);
	sem_delete_owned_sems(team->id);
	remove_images(team);
	vfs_free_io_context(team->io_context);

	free(team);
}


static int
get_arguments_data_size(char **args, int argc)
{
	uint32 size = 0;
	int count;

	for (count = 0; count < argc; count++)
		size += strlen(args[count]) + 1;

	return size + (argc + 1) * sizeof(char *) + sizeof(struct uspace_program_args);
}


static int32
team_create_team2(void *args)
{
	int err;
	struct thread *t;
	struct team *team;
	struct team_arg *teamArgs = args;
	char *path;
	addr entry;
	char ustack_name[128];
	uint32 totalSize;
	char **uargs;
	char **uenv;
	char *udest;
	struct uspace_program_args *uspa;
	unsigned int arg_cnt;
	unsigned int env_cnt;

	t = thread_get_current_thread();
	team = t->team;

	TRACE(("team_create_team2: entry thread %ld\n", t->id));

	// create an initial primary stack region

	// ToDo: make ENV_SIZE variable?
	// ToDo: when B_BASE_ADDRESS is implemented, we could just allocate the stack from
	//		the bottom of the USER_STACK_REGION.

	totalSize = PAGE_ALIGN(MAIN_THREAD_STACK_SIZE + TLS_SIZE + ENV_SIZE +
		get_arguments_data_size(teamArgs->args, teamArgs->argc));
	t->user_stack_base = USER_STACK_REGION + USER_STACK_REGION_SIZE - totalSize;
		// the exact location at the end of the user stack region

	sprintf(ustack_name, "%s_primary_stack", team->name);
	t->user_stack_region_id = create_area_etc(team, ustack_name, (void **)&t->user_stack_base,
		B_EXACT_ADDRESS, totalSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
	if (t->user_stack_region_id < 0) {
		panic("team_create_team2: could not create default user stack region\n");
		return t->user_stack_region_id;
	}

	// now that the TLS area is allocated, initialize TLS
	arch_thread_init_tls(t);

	uspa = (struct uspace_program_args *)(t->user_stack_base + STACK_SIZE + TLS_SIZE + ENV_SIZE);
	uargs = (char **)(uspa + 1);
	udest = (char  *)(uargs + teamArgs->argc + 1);

	TRACE(("addr: stack base = 0x%lx, uargs = %p, udest = %p, totalSize = %lu\n",
		t->user_stack_base, uargs, udest, totalSize));

	for (arg_cnt = 0; arg_cnt < teamArgs->argc; arg_cnt++) {
		uargs[arg_cnt] = udest;
		udest += user_strlcpy(udest, teamArgs->args[arg_cnt], totalSize) + 1;
	}
	uargs[arg_cnt] = NULL;

	team->user_env_base = t->user_stack_base + STACK_SIZE + TLS_SIZE;
	uenv = (char **)team->user_env_base;
	udest = (char *)team->user_env_base + ENV_SIZE - 1;

	TRACE(("team_create_team2: envc: %d, envp: 0x%p\n", teamArgs->envc, (void *)teamArgs->envp));

	for (env_cnt = 0; env_cnt < teamArgs->envc; env_cnt++) {
		size_t length = strlen(teamArgs->envp[env_cnt]) + 1;
		udest -= length;
		uenv[env_cnt] = udest;
		user_memcpy(udest, teamArgs->envp[env_cnt], length);
	}
	uenv[env_cnt] = NULL;

	user_memcpy(uspa->program_name, team->name, sizeof(uspa->program_name));
	user_memcpy(uspa->program_path, teamArgs->path, sizeof(uspa->program_path));
	uspa->argc = arg_cnt;
	uspa->argv = uargs;
	uspa->envc = env_cnt;
	uspa->envp = uenv;

	kfree_strings_array(teamArgs->args, teamArgs->argc);
	kfree_strings_array(teamArgs->envp, teamArgs->envc);

	path = teamArgs->path;
	TRACE(("team_create_team2: loading elf binary '%s'\n", path));

	err = elf_load_user_image("/boot/libexec/rld.so", team, 0, &entry);
	if (err < 0) {
		// XXX clean up team
		return err;
	}

	// free the args
	free(teamArgs->path);
	free(teamArgs);

	TRACE(("team_create_team2: loaded elf. entry = 0x%lx\n", entry));

	team->state = TEAM_STATE_NORMAL;

	// jump to the entry point in user space
	arch_thread_enter_uspace(t, entry, uspa, NULL);

	// never gets here
	return 0;
}


team_id
team_create_team(const char *path, const char *name, char **args, int argc, char **envp, int envc, int priority)
{
	struct team *team, *parent;
	thread_id tid;
	team_id pid;
	int err;
	unsigned int state;
//	int sem_retcode;
	struct team_arg *teamArgs;

	TRACE(("team_create_team: entry '%s', name '%s' args = %p argc = %d\n",
		path, name, args, argc));

	team = create_team_struct(name, false);
	if (team == NULL)
		return B_NO_MEMORY;

	pid = team->id;
	parent = thread_get_current_thread()->team;

	state = disable_interrupts();
	GRAB_TEAM_LOCK();

	hash_insert(team_hash, team);
	insert_team_into_parent(parent, team);

	RELEASE_TEAM_LOCK();
	restore_interrupts(state);

	// copy the args over
	teamArgs = (struct team_arg *)malloc(sizeof(struct team_arg));
	if (teamArgs == NULL){
		err = B_NO_MEMORY;
		goto err1;
	}
	teamArgs->path = strdup(path);
	if (teamArgs->path == NULL){
		err = B_NO_MEMORY;
		goto err2;
	}
	teamArgs->argc = argc;
	teamArgs->args = args;
	teamArgs->envp = envp;
	teamArgs->envc = envc;

	// create a new io_context for this team
	team->io_context = vfs_new_io_context(thread_get_current_thread()->team->io_context);
	if (!team->io_context) {
		err = ENOMEM;
		goto err3;
	}

	// create an address space for this team
	team->_aspace_id = vm_create_aspace(team->name, USER_BASE, USER_SIZE, false);
	if (team->_aspace_id < 0) {
		err = team->_aspace_id;
		goto err4;
	}
	team->aspace = vm_get_aspace_by_id(team->_aspace_id);

	// create a kernel thread, but under the context of the new team
	tid = spawn_kernel_thread_etc(team_create_team2, name, B_NORMAL_PRIORITY, teamArgs, team->id);
	if (tid < 0) {
		err = tid;
		goto err5;
	}

	resume_thread(tid);

	return pid;

err5:
	vm_put_aspace(team->aspace);
	vm_delete_aspace(team->_aspace_id);
err4:
	vfs_free_io_context(team->io_context);
err3:
	free(teamArgs->path);
err2:
	free(teamArgs);
err1:
	// remove the team structure from the team hash table and delete the team structure
	state = disable_interrupts();
	GRAB_TEAM_LOCK();

	remove_team_from_parent(parent, team);
	hash_remove(team_hash, team);

	RELEASE_TEAM_LOCK();
	restore_interrupts(state);
	delete_team_struct(team);
//err:
	return err;
}


//	#pragma mark -
// public team API


status_t
wait_for_team(team_id id, status_t *_returnCode)
{
	struct team *team;
	thread_id thread;
	cpu_status state;

	// find main thread and wait for that

	state = disable_interrupts();
	GRAB_TEAM_LOCK();

	team = team_get_team_struct_locked(id);
	if (team && team->main_thread)
		thread = team->main_thread->id;
	else
		thread = B_BAD_THREAD_ID;

	RELEASE_TEAM_LOCK();
	restore_interrupts(state);

	if (thread < 0)
		return thread;

	return wait_for_thread(thread, _returnCode);
}


status_t
kill_team(team_id id)
{
	int state;
	struct team *team;
//	struct thread *t;
	thread_id tid = -1;
	int retval = 0;

	state = disable_interrupts();
	GRAB_TEAM_LOCK();

	team = team_get_team_struct_locked(id);
	if (team != NULL)
		tid = team->main_thread->id;
	else
		retval = B_BAD_THREAD_ID;

	RELEASE_TEAM_LOCK();
	restore_interrupts(state);

	if (retval < 0)
		return retval;

	// just kill the main thread in the team. The cleanup code there will
	// take care of the team
	return kill_thread(tid);
}


/** Fills the team_info structure with information from the specified
 *	team.
 *	The team lock must be held when called.
 */

static status_t
fill_team_info(struct team *team, team_info *info, size_t size)
{
	if (size != sizeof(team_info))
		return B_BAD_VALUE;

	// ToDo: Set more informations for team_info
	memset(info, 0, size);

	info->team = team->id;
	info->thread_count = team->num_threads;
	info->image_count = count_images(team);
	//info->area_count = 
	//info->debugger_nub_thread = 
	//info->debugger_nub_port = 
	//info->argc = 
	//info->args[64] = 
	//info->uid = 
	//info->gid = 

	// ToDo: make this to return real argc/argv
	strlcpy(info->args, team->name, sizeof(info->args));
	info->argc = 1;

	return B_OK;
}


status_t
_get_team_info(team_id id, team_info *info, size_t size)
{
	int state;
	status_t rc = B_OK;
	struct team *team;

	state = disable_interrupts();
	GRAB_TEAM_LOCK();

	team = team_get_team_struct_locked(id);
	if (!team) {
		rc = B_BAD_TEAM_ID;
		goto err;
	}

	rc = fill_team_info(team, info, size);

err:
	RELEASE_TEAM_LOCK();
	restore_interrupts(state);
	
	return rc;
}


status_t
_get_next_team_info(int32 *cookie, team_info *info, size_t size)
{
	status_t status = B_BAD_TEAM_ID;
	struct team *team = NULL;
	int32 slot = *cookie;

	int state = disable_interrupts();
	GRAB_TEAM_LOCK();

	if (slot >= next_team_id)
		goto err;

	// get next valid team
	while ((slot < next_team_id) && !(team = team_get_team_struct_locked(slot)))
		slot++;

	if (team) {
		status = fill_team_info(team, info, size);
		*cookie = ++slot;
	}

err:
	RELEASE_TEAM_LOCK();
	restore_interrupts(state);

	return status;
}


int
sys_setenv(const char *name, const char *value, int overwrite)
{
	char var[SYS_THREAD_STRING_LENGTH_MAX];
	int state;
	addr env_space;
	char **envp;
	int envc;
	bool var_exists = false;
	int var_pos = 0;
	int name_size;
	int rc = 0;
	int i;
	char *p;

	// ToDo: please put me out of the kernel into libroot.so!

	TRACE(("sys_setenv: entry (name=%s, value=%s)\n", name, value));

	if (strlen(name) + strlen(value) + 1 >= SYS_THREAD_STRING_LENGTH_MAX)
		return -1;

	state = disable_interrupts();
	GRAB_TEAM_LOCK();

	strcpy(var, name);
	strncat(var, "=", SYS_THREAD_STRING_LENGTH_MAX-1);
	name_size = strlen(var);
	strncat(var, value, SYS_THREAD_STRING_LENGTH_MAX-1);

	env_space = (addr)thread_get_current_thread()->team->user_env_base;
	envp = (char **)env_space;
	for (envc = 0; envp[envc]; envc++) {
		if (!strncmp(envp[envc], var, name_size)) {
			var_exists = true;
			var_pos = envc;
		}
	}
	if (!var_exists)
		var_pos = envc;

	TRACE(("sys_setenv: variable does%s exist\n", var_exists ? "" : " not"));

	if ((!var_exists) || (var_exists && overwrite)) {
		// XXX- make a better allocator
		if (var_exists) {
			if (strlen(var) <= strlen(envp[var_pos])) {
				strcpy(envp[var_pos], var);
			} else {
				for (p = (char *)env_space + ENV_SIZE - 1, i = 0; envp[i]; i++)
					if (envp[i] < p)
						p = envp[i];
				p -= (strlen(var) + 1);
				if (p < (char *)env_space + (envc * sizeof(char *))) {
					rc = -1;
				} else {
					envp[var_pos] = p;
					strcpy(envp[var_pos], var);
				}
			}
		}
		else {
			for (p = (char *)env_space + ENV_SIZE - 1, i=0; envp[i]; i++)
				if (envp[i] < p)
					p = envp[i];
			p -= (strlen(var) + 1);
			if (p < (char *)env_space + ((envc + 1) * sizeof(char *))) {
				rc = -1;
			} else {
				envp[envc] = p;
				strcpy(envp[envc], var);
				envp[envc + 1] = NULL;
			}
		}
	}
	TRACE(("sys_setenv: variable set.\n"));

	RELEASE_TEAM_LOCK();
	restore_interrupts(state);
	
	return rc;
}


int
sys_getenv(const char *name, char **value)
{
	char **envp;
	char *p;
	int state;
	int i;
	int len = strlen(name);
	int rc = -1;

	// ToDo: please put me out of the kernel into libroot.so!
	
	state = disable_interrupts();
	GRAB_TEAM_LOCK();

	envp = (char **)thread_get_current_thread()->team->user_env_base;
	for (i = 0; envp[i]; i++) {
		if (!strncmp(envp[i], name, len)) {
			p = envp[i] + len;
			if (*p == '=') {
				*value = (p + 1);
				rc = 0;
				break;
			}
		}
	}
	
	RELEASE_TEAM_LOCK();
	restore_interrupts(state);
	
	return rc;
}


//	#pragma mark -
//	User syscalls


status_t
_user_wait_for_team(team_id id, status_t *_userReturnCode)
{
	status_t returnCode;
	status_t status;

	if (!IS_USER_ADDRESS(_userReturnCode))
		return B_BAD_ADDRESS;

	status = wait_for_team(id, &returnCode);
	if (status >= B_OK) {
		if (user_memcpy(_userReturnCode, &returnCode, sizeof(returnCode)) < B_OK)
			return B_BAD_ADDRESS;
	}

	return status;
}


team_id
_user_create_team(const char *userPath, const char *userName, char **userArgs,
	int argCount, char **userEnv, int envCount, int priority)
{
	char path[SYS_MAX_PATH_LEN];
	char name[B_OS_NAME_LENGTH];
	char **args = NULL;
	char **env = NULL;
	int rc;

	TRACE(("user_team_create_team: argc = %d\n", argCount));

	if (!IS_USER_ADDRESS(userPath)
		|| !IS_USER_ADDRESS(userName))
		return B_BAD_ADDRESS;

	rc = user_copy_strings_array(userArgs, argCount, &args);
	if (rc < 0)
		goto error;

	if (userEnv == NULL) {
		// ToDo: this doesn't look particularly safe to me - where
		//	is user_env_base?
		userEnv = (char **)thread_get_current_thread()->team->user_env_base;
		for (envCount = 0; userEnv && (userEnv[envCount]); envCount++);
	}
	if (user_copy_strings_array(userEnv, envCount, &env) < B_OK
		|| user_strlcpy(path, userPath, SYS_MAX_PATH_LEN) < B_OK
		|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK) {
		rc = B_BAD_ADDRESS;
		goto error;
	}

	return team_create_team(path, name, args, argCount, env, envCount, priority);

error:
	kfree_strings_array(args, argCount);
	kfree_strings_array(env, envCount);
	return rc;
}


status_t
_user_kill_team(team_id team)
{
	return kill_team(team);
}


status_t
_user_get_team_info(team_id id, team_info *userInfo)
{
	status_t status;
	team_info info;

	if (!IS_USER_ADDRESS(userInfo))
		return B_BAD_ADDRESS;	

	status = _get_team_info(id, &info, sizeof(team_info));
	if (status == B_OK) {
		if (user_memcpy(userInfo, &info, sizeof(team_info)) < B_OK)
			return B_BAD_ADDRESS;
	}

	return status;
}


status_t
_user_get_next_team_info(int32 *userCookie, team_info *userInfo)
{
	status_t status;
	team_info info;
	int32 cookie;

	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, sizeof(team_info));
	if (status != B_OK)
		return status;

	if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
		|| user_memcpy(userInfo, &info, sizeof(team_info)) < B_OK)
		return B_BAD_ADDRESS;

	return status;
}


team_id
_user_get_current_team(void)
{
	return team_get_current_team_id();
}


int
user_getenv(const char *userName, char **_userValue)
{
	char name[SYS_THREAD_STRING_LENGTH_MAX];
	char *value;
	int rc;

	if (!IS_USER_ADDRESS(userName)
		|| !IS_USER_ADDRESS(_userValue)
		|| user_strlcpy(name, userName, SYS_THREAD_STRING_LENGTH_MAX) < B_OK)
		return B_BAD_ADDRESS;

	rc = sys_getenv(name, &value);
	if (rc < 0)
		return rc;

	if (user_memcpy(_userValue, &value, sizeof(char *)) < B_OK)
		return B_BAD_ADDRESS;

	return rc;
}


int
user_setenv(const char *userName, const char *userValue, int overwrite)
{
	char name[SYS_THREAD_STRING_LENGTH_MAX];
	char value[SYS_THREAD_STRING_LENGTH_MAX];

	if (!IS_USER_ADDRESS(userName)
		|| !IS_USER_ADDRESS(userValue)
		|| user_strlcpy(name, userName, SYS_THREAD_STRING_LENGTH_MAX) < B_OK
		|| user_strlcpy(value, userValue, SYS_THREAD_STRING_LENGTH_MAX) < B_OK)
		return B_BAD_ADDRESS;

	return sys_setenv(name, value, overwrite);
}