2004-02-22 17:52:59 +03:00
|
|
|
/*
|
2006-05-05 19:33:34 +04:00
|
|
|
* Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de.
|
2004-11-18 21:15:39 +03:00
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*
|
|
|
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
|
|
|
* Distributed under the terms of the NewOS License.
|
|
|
|
*/
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-07 21:17:04 +04:00
|
|
|
/* Team functions */
|
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
#include <OS.h>
|
2004-03-16 05:46:28 +03:00
|
|
|
|
2005-12-20 18:54:45 +03:00
|
|
|
#include <elf.h>
|
|
|
|
#include <file_cache.h>
|
2002-08-04 03:39:50 +04:00
|
|
|
#include <int.h>
|
2005-12-20 18:54:45 +03:00
|
|
|
#include <kernel.h>
|
2003-01-12 19:29:28 +03:00
|
|
|
#include <kimage.h>
|
2005-10-25 20:59:12 +04:00
|
|
|
#include <kscheduler.h>
|
2005-12-20 18:54:45 +03:00
|
|
|
#include <port.h>
|
|
|
|
#include <sem.h>
|
2004-10-14 18:46:12 +04:00
|
|
|
#include <syscall_process_info.h>
|
2005-12-20 18:54:45 +03:00
|
|
|
#include <syscalls.h>
|
|
|
|
#include <team.h>
|
2003-01-07 12:40:59 +03:00
|
|
|
#include <tls.h>
|
2005-12-20 18:54:45 +03:00
|
|
|
#include <user_runtime.h>
|
2004-12-14 02:02:18 +03:00
|
|
|
#include <vfs.h>
|
2005-12-20 18:54:45 +03:00
|
|
|
#include <vm.h>
|
|
|
|
#include <vm_address_space.h>
|
|
|
|
#include <util/khash.h>
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
#include <sys/wait.h>
|
2004-03-16 05:46:28 +03:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2004-03-16 06:14:48 +03:00
|
|
|
//#define TRACE_TEAM
|
|
|
|
#ifdef TRACE_TEAM
|
2003-11-12 18:37:44 +03:00
|
|
|
# define TRACE(x) dprintf x
|
|
|
|
#else
|
|
|
|
# define TRACE(x) ;
|
|
|
|
#endif
|
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
|
|
|
|
struct team_key {
|
|
|
|
team_id id;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct team_arg {
|
2004-10-07 19:34:17 +04:00
|
|
|
uint32 arg_count;
|
|
|
|
char **args;
|
|
|
|
uint32 env_count;
|
|
|
|
char **env;
|
2002-08-04 03:39:50 +04:00
|
|
|
};
|
|
|
|
|
2004-10-12 08:03:52 +04:00
|
|
|
struct fork_arg {
|
|
|
|
area_id user_stack_area;
|
|
|
|
addr_t user_stack_base;
|
|
|
|
size_t user_stack_size;
|
|
|
|
addr_t user_local_storage;
|
|
|
|
|
|
|
|
struct arch_fork_arg arch_info;
|
|
|
|
};
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
// team list
|
|
|
|
static void *team_hash = NULL;
|
|
|
|
static struct team *kernel_team = NULL;
|
|
|
|
|
2004-12-01 00:11:37 +03:00
|
|
|
// some arbitrary chosen limits - should probably depend on the available
|
|
|
|
// memory (the limit is not yet enforced)
|
|
|
|
static int32 sMaxTeams = 2048;
|
|
|
|
static int32 sUsedTeams = 1;
|
|
|
|
|
2002-10-26 20:13:36 +04:00
|
|
|
spinlock team_spinlock = 0;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
static void insert_group_into_session(struct process_session *session, struct process_group *group);
|
|
|
|
static void insert_team_into_group(struct process_group *group, struct team *team);
|
|
|
|
static struct process_session *create_process_session(pid_t id);
|
|
|
|
static struct process_group *create_process_group(pid_t id);
|
2002-08-04 03:39:50 +04:00
|
|
|
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);
|
2002-11-29 11:38:52 +03:00
|
|
|
static uint32 team_struct_hash(void *_p, const void *_key, uint32 range);
|
2004-10-07 19:34:17 +04:00
|
|
|
static void free_strings_array(char **strings, int32 count);
|
|
|
|
static status_t user_copy_strings_array(char * const *strings, int32 count, char ***_strings);
|
2002-08-04 03:39:50 +04:00
|
|
|
static void _dump_team_info(struct team *p);
|
|
|
|
static int dump_team_info(int argc, char **argv);
|
|
|
|
|
|
|
|
|
2002-09-24 03:24:12 +04:00
|
|
|
static void
|
2005-04-12 12:13:12 +04:00
|
|
|
_dump_team_info(struct team *team)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2005-11-02 13:43:30 +03:00
|
|
|
kprintf("TEAM: %p\n", team);
|
|
|
|
kprintf("id: 0x%lx\n", team->id);
|
|
|
|
kprintf("name: '%s'\n", team->name);
|
|
|
|
kprintf("next: %p\n", team->next);
|
|
|
|
kprintf("parent: %p", team->parent);
|
|
|
|
if (team->parent != NULL) {
|
|
|
|
kprintf(" (id = 0x%lx)\n", team->parent->id);
|
|
|
|
} else
|
|
|
|
kprintf("\n");
|
|
|
|
|
|
|
|
kprintf("children: %p\n", team->children);
|
|
|
|
kprintf("num_threads: %d\n", team->num_threads);
|
|
|
|
kprintf("state: %d\n", team->state);
|
|
|
|
kprintf("pending_signals: 0x%x\n", team->pending_signals);
|
|
|
|
kprintf("io_context: %p\n", team->io_context);
|
2005-12-20 16:29:11 +03:00
|
|
|
if (team->address_space)
|
|
|
|
kprintf("address_space: %p (id = 0x%lx)\n", team->address_space, team->address_space->id);
|
2005-11-02 13:43:30 +03:00
|
|
|
kprintf("main_thread: %p\n", team->main_thread);
|
|
|
|
kprintf("thread_list: %p\n", team->thread_list);
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-09-24 03:24:12 +04:00
|
|
|
|
|
|
|
static int
|
|
|
|
dump_team_info(int argc, char **argv)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2005-11-02 13:38:28 +03:00
|
|
|
struct hash_iterator iterator;
|
2005-04-12 12:13:12 +04:00
|
|
|
struct team *team;
|
|
|
|
team_id id = -1;
|
2005-10-06 12:36:37 +04:00
|
|
|
bool found = false;
|
|
|
|
|
2005-11-02 13:38:28 +03:00
|
|
|
if (argc != 2) {
|
2005-10-06 12:36:37 +04:00
|
|
|
kprintf("usage: team [id/address/name]\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2005-10-06 12:36:37 +04:00
|
|
|
id = strtoul(argv[1], NULL, 0);
|
|
|
|
if (IS_KERNEL_ADDRESS(id)) {
|
|
|
|
// semi-hack
|
|
|
|
_dump_team_info((struct team *)id);
|
|
|
|
return 0;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// walk through the thread list, trying to match name or id
|
2005-11-02 13:38:28 +03:00
|
|
|
hash_open(team_hash, &iterator);
|
|
|
|
while ((team = hash_next(team_hash, &iterator)) != NULL) {
|
2005-04-12 12:13:12 +04:00
|
|
|
if ((team->name && strcmp(argv[1], team->name) == 0) || team->id == id) {
|
|
|
|
_dump_team_info(team);
|
2005-10-06 12:36:37 +04:00
|
|
|
found = true;
|
2002-08-04 03:39:50 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-11-02 13:38:28 +03:00
|
|
|
hash_close(team_hash, &iterator, false);
|
2005-10-06 12:36:37 +04:00
|
|
|
|
|
|
|
if (!found)
|
|
|
|
kprintf("team \"%s\" (%ld) doesn't exist!\n", argv[1], id);
|
2002-08-04 03:39:50 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-02 13:38:28 +03:00
|
|
|
static int
|
|
|
|
dump_teams(int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct hash_iterator iterator;
|
|
|
|
struct team *team;
|
|
|
|
|
|
|
|
kprintf("team id parent name\n");
|
|
|
|
hash_open(team_hash, &iterator);
|
|
|
|
|
|
|
|
while ((team = hash_next(team_hash, &iterator)) != NULL) {
|
|
|
|
kprintf("%p%6lx %p %s\n", team, team->id, team->parent, team->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
hash_close(team_hash, &iterator, false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-12-01 00:11:37 +03:00
|
|
|
status_t
|
|
|
|
team_init(kernel_args *args)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2004-10-14 18:46:12 +04:00
|
|
|
struct process_session *session;
|
|
|
|
struct process_group *group;
|
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
// create the team hash table
|
2004-12-01 00:11:37 +03:00
|
|
|
team_hash = hash_init(15, offsetof(struct team, next),
|
2002-08-04 03:39:50 +04:00
|
|
|
&team_struct_compare, &team_struct_hash);
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
// create initial session and process groups
|
|
|
|
|
|
|
|
session = create_process_session(1);
|
|
|
|
if (session == NULL)
|
|
|
|
panic("Could not create initial session.\n");
|
|
|
|
|
|
|
|
group = create_process_group(1);
|
|
|
|
if (group == NULL)
|
|
|
|
panic("Could not create initial process group.\n");
|
|
|
|
|
|
|
|
insert_group_into_session(session, group);
|
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
// create the kernel team
|
|
|
|
kernel_team = create_team_struct("kernel_team", true);
|
2002-08-05 09:26:52 +04:00
|
|
|
if (kernel_team == NULL)
|
2002-08-04 03:39:50 +04:00
|
|
|
panic("could not create kernel team!\n");
|
2006-05-30 04:21:22 +04:00
|
|
|
strcpy(kernel_team->args, kernel_team->name);
|
2002-08-04 03:39:50 +04:00
|
|
|
kernel_team->state = TEAM_STATE_NORMAL;
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
insert_team_into_group(group, kernel_team);
|
|
|
|
|
2002-12-03 17:17:53 +03:00
|
|
|
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");
|
2002-08-04 03:39:50 +04:00
|
|
|
|
|
|
|
// 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");
|
2005-11-02 13:38:28 +03:00
|
|
|
add_debugger_command("teams", &dump_teams, "list all teams");
|
2002-08-05 09:26:52 +04:00
|
|
|
return 0;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-12-01 00:11:37 +03:00
|
|
|
int32
|
|
|
|
team_max_teams(void)
|
|
|
|
{
|
|
|
|
return sMaxTeams;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32
|
|
|
|
team_used_teams(void)
|
|
|
|
{
|
|
|
|
return sUsedTeams;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
/** Frees an array of strings in kernel space.
|
|
|
|
*
|
|
|
|
* \param strings strings array
|
|
|
|
* \param count number of strings in array
|
2002-08-05 09:26:52 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
2004-10-07 19:34:17 +04:00
|
|
|
free_strings_array(char **strings, int32 count)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2004-10-07 19:34:17 +04:00
|
|
|
int32 i;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
if (strings == NULL)
|
|
|
|
return;
|
2004-05-11 23:52:38 +04:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
free(strings[i]);
|
|
|
|
|
|
|
|
free(strings);
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-10-15 05:52:07 +04:00
|
|
|
/** Copy an array of strings in kernel space
|
|
|
|
*
|
|
|
|
* \param strings strings array to be copied
|
|
|
|
* \param count number of strings in array
|
|
|
|
* \param kstrings pointer to the kernel copy
|
|
|
|
* \return \c B_OK on success, or an appropriate error code on
|
|
|
|
* failure.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static status_t
|
2005-10-05 19:32:48 +04:00
|
|
|
kernel_copy_strings_array(char * const *in, int32 count, char ***_strings)
|
2004-10-15 05:52:07 +04:00
|
|
|
{
|
|
|
|
status_t status;
|
|
|
|
char **strings;
|
|
|
|
int32 i = 0;
|
|
|
|
|
|
|
|
strings = (char **)malloc((count + 1) * sizeof(char *));
|
|
|
|
if (strings == NULL)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
for (; i < count; i++) {
|
|
|
|
strings[i] = strdup(in[i]);
|
|
|
|
if (strings[i] == NULL) {
|
|
|
|
status = B_NO_MEMORY;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
strings[count] = NULL;
|
|
|
|
*_strings = strings;
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
error:
|
|
|
|
free_strings_array(strings, i);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
/** Copy an array of strings from user space to kernel space
|
2004-10-07 19:34:17 +04:00
|
|
|
*
|
|
|
|
* \param strings userspace strings array
|
|
|
|
* \param count number of strings in array
|
|
|
|
* \param kstrings pointer to the kernel copy
|
|
|
|
* \return \c B_OK on success, or an appropriate error code on
|
|
|
|
* failure.
|
2002-08-05 09:26:52 +04:00
|
|
|
*/
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
static status_t
|
|
|
|
user_copy_strings_array(char * const *userStrings, int32 count, char ***_strings)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2006-06-29 19:23:34 +04:00
|
|
|
char *buffer;
|
2004-10-07 19:34:17 +04:00
|
|
|
char **strings;
|
|
|
|
status_t err;
|
|
|
|
int32 i = 0;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-02-23 07:08:09 +03:00
|
|
|
if (!IS_USER_ADDRESS(userStrings))
|
|
|
|
return B_BAD_ADDRESS;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2006-06-29 19:23:34 +04:00
|
|
|
// buffer for safely accessing the user string
|
|
|
|
// TODO: maybe have a user_strdup() instead?
|
|
|
|
buffer = (char *)malloc(4 * B_PAGE_SIZE);
|
|
|
|
if (buffer == NULL)
|
2002-12-03 17:17:53 +03:00
|
|
|
return B_NO_MEMORY;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2006-06-29 19:23:34 +04:00
|
|
|
strings = (char **)malloc((count + 1) * sizeof(char *));
|
|
|
|
if (strings == NULL) {
|
|
|
|
err = B_NO_MEMORY;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
if ((err = user_memcpy(strings, userStrings, count * sizeof(char *))) < B_OK)
|
|
|
|
goto error;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
// scan all strings and copy to kernel space
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
for (; i < count; i++) {
|
2006-06-29 19:23:34 +04:00
|
|
|
err = user_strlcpy(buffer, strings[i], 4 * B_PAGE_SIZE);
|
2004-10-07 19:34:17 +04:00
|
|
|
if (err < B_OK)
|
2002-08-04 03:39:50 +04:00
|
|
|
goto error;
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
strings[i] = strdup(buffer);
|
|
|
|
if (strings[i] == NULL) {
|
|
|
|
err = B_NO_MEMORY;
|
2002-08-04 03:39:50 +04:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
strings[count] = NULL;
|
|
|
|
*_strings = strings;
|
2006-06-29 19:23:34 +04:00
|
|
|
free(buffer);
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
return B_OK;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
|
|
|
error:
|
2004-10-07 19:34:17 +04:00
|
|
|
free_strings_array(strings, i);
|
2006-06-29 19:23:34 +04:00
|
|
|
free(buffer);
|
2004-10-07 19:34:17 +04:00
|
|
|
|
2004-11-02 00:38:56 +03:00
|
|
|
TRACE(("user_copy_strings_array failed %ld\n", err));
|
2002-08-04 03:39:50 +04:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
static status_t
|
|
|
|
copy_strings_array(char * const *strings, int32 count, char ***_strings,
|
|
|
|
bool kernel)
|
|
|
|
{
|
|
|
|
if (kernel)
|
|
|
|
return kernel_copy_strings_array(strings, count, _strings);
|
|
|
|
|
|
|
|
return user_copy_strings_array(strings, count, _strings);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-23 07:08:09 +03:00
|
|
|
/** Quick check to see if we have a valid team ID.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool
|
|
|
|
team_is_valid(team_id id)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2004-02-23 07:08:09 +03:00
|
|
|
struct team *team;
|
2005-03-09 04:43:56 +03:00
|
|
|
cpu_status state;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-02-23 07:08:09 +03:00
|
|
|
if (id <= 0)
|
|
|
|
return false;
|
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
2004-02-23 07:08:09 +03:00
|
|
|
team = team_get_team_struct_locked(id);
|
2002-08-04 03:39:50 +04:00
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
2004-02-23 07:08:09 +03:00
|
|
|
return team != NULL;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
2002-08-04 06:04:37 +04:00
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
struct team *
|
|
|
|
team_get_team_struct_locked(team_id id)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
|
|
|
struct team_key key;
|
|
|
|
|
|
|
|
key.id = id;
|
|
|
|
|
|
|
|
return hash_lookup(team_hash, &key);
|
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
|
|
|
static int
|
|
|
|
team_struct_compare(void *_p, const void *_key)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
|
|
|
struct team *p = _p;
|
|
|
|
const struct team_key *key = _key;
|
|
|
|
|
2002-11-29 11:38:52 +03:00
|
|
|
if (p->id == key->id)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2002-11-29 11:38:52 +03:00
|
|
|
static uint32
|
|
|
|
team_struct_hash(void *_p, const void *_key, uint32 range)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
|
|
|
struct team *p = _p;
|
|
|
|
const struct team_key *key = _key;
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
if (p != NULL)
|
2002-11-29 11:38:52 +03:00
|
|
|
return p->id % range;
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2005-08-26 06:02:33 +04:00
|
|
|
return (uint32)key->id % range;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-03-16 05:46:28 +03:00
|
|
|
static void
|
|
|
|
insert_team_into_parent(struct team *parent, struct team *team)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2004-03-16 05:46:28 +03:00
|
|
|
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;
|
|
|
|
}
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
static bool
|
|
|
|
is_process_group_leader(struct team *team)
|
|
|
|
{
|
|
|
|
return team->group_id == team->main_thread->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
insert_group_into_session(struct process_session *session, struct process_group *group)
|
|
|
|
{
|
|
|
|
if (group == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
group->session = session;
|
|
|
|
list_add_link_to_tail(&session->groups, group);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
insert_team_into_group(struct process_group *group, struct team *team)
|
|
|
|
{
|
|
|
|
team->group = group;
|
|
|
|
team->group_id = group->id;
|
|
|
|
team->session_id = group->session->id;
|
2004-10-14 19:42:56 +04:00
|
|
|
atomic_add(&team->dead_children.wait_for_any, group->wait_for_any);
|
2004-10-14 18:46:12 +04:00
|
|
|
|
|
|
|
team->group_next = group->teams;
|
|
|
|
group->teams = team;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Removes a group from a session, and puts the session object
|
|
|
|
* back into the session cache, if it's not used anymore.
|
|
|
|
* You must hold the team lock when calling this function.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
remove_group_from_session(struct process_group *group)
|
|
|
|
{
|
|
|
|
struct process_session *session = group->session;
|
|
|
|
|
|
|
|
// the group must be in any session to let this function have any effect
|
|
|
|
if (session == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
list_remove_link(group);
|
|
|
|
|
|
|
|
// we cannot free the resource here, so we're keeping the group link
|
|
|
|
// around - this way it'll be freed by free_process_group()
|
|
|
|
if (!list_is_empty(&session->groups))
|
|
|
|
group->session = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
team_delete_process_group(struct process_group *group)
|
|
|
|
{
|
|
|
|
if (group == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
TRACE(("team_delete_process_group(id = %ld)\n", group->id));
|
|
|
|
|
|
|
|
delete_sem(group->dead_child_sem);
|
|
|
|
|
|
|
|
// remove_group_from_session() keeps this pointer around
|
|
|
|
// only if the session can be freed as well
|
|
|
|
if (group->session) {
|
|
|
|
TRACE(("team_delete_process_group(): frees session %ld\n", group->session->id));
|
|
|
|
free(group->session);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(group);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Removes the team from the group. If that group becomes therefore
|
|
|
|
* unused, it will set \a _freeGroup to point to the group - otherwise
|
|
|
|
* it will be \c NULL.
|
|
|
|
* It cannot be freed here because this function has to be called
|
|
|
|
* with having the team lock held.
|
|
|
|
*
|
|
|
|
* \param team the team that'll be removed from it's group
|
|
|
|
* \param _freeGroup points to the group to be freed or NULL
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
remove_team_from_group(struct team *team, struct process_group **_freeGroup)
|
|
|
|
{
|
|
|
|
struct process_group *group = team->group;
|
|
|
|
struct team *current, *last = NULL;
|
|
|
|
|
|
|
|
*_freeGroup = NULL;
|
|
|
|
|
|
|
|
// the team must be in any team to let this function have any effect
|
|
|
|
if (group == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (current = group->teams; current != NULL; current = current->group_next) {
|
|
|
|
if (current == team) {
|
|
|
|
if (last == NULL)
|
|
|
|
group->teams = current->group_next;
|
|
|
|
else
|
|
|
|
last->group_next = current->group_next;
|
|
|
|
|
|
|
|
team->group = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
last = current;
|
|
|
|
}
|
|
|
|
|
2004-10-14 19:42:56 +04:00
|
|
|
// all wait_for_child() that wait on the group don't wait for us anymore
|
|
|
|
atomic_add(&team->dead_children.wait_for_any, -group->wait_for_any);
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
team->group = NULL;
|
|
|
|
team->group_next = NULL;
|
|
|
|
|
|
|
|
if (group->teams != NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// we can remove this group as it is no longer used
|
|
|
|
|
|
|
|
remove_group_from_session(group);
|
|
|
|
*_freeGroup = group;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct process_group *
|
|
|
|
create_process_group(pid_t id)
|
|
|
|
{
|
|
|
|
struct process_group *group = (struct process_group *)malloc(sizeof(struct process_group));
|
|
|
|
if (group == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
group->dead_child_sem = create_sem(0, "dead group children");
|
|
|
|
if (group->dead_child_sem < B_OK) {
|
|
|
|
free(group);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
group->id = id;
|
|
|
|
group->session = NULL;
|
|
|
|
group->teams = NULL;
|
2004-10-14 19:42:56 +04:00
|
|
|
group->wait_for_any = 0;
|
2004-10-14 18:46:12 +04:00
|
|
|
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct process_session *
|
|
|
|
create_process_session(pid_t id)
|
|
|
|
{
|
|
|
|
struct process_session *session = (struct process_session *)malloc(sizeof(struct process_session));
|
|
|
|
if (session == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
session->id = id;
|
|
|
|
list_init(&session->groups);
|
|
|
|
|
|
|
|
return session;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
struct team *
|
|
|
|
team_get_kernel_team(void)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
|
|
|
return kernel_team;
|
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
|
|
|
team_id
|
|
|
|
team_get_kernel_team_id(void)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2002-08-05 09:26:52 +04:00
|
|
|
if (!kernel_team)
|
2002-08-04 03:39:50 +04:00
|
|
|
return 0;
|
2002-08-05 09:26:52 +04:00
|
|
|
|
|
|
|
return kernel_team->id;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
|
|
|
team_id
|
|
|
|
team_get_current_team_id(void)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
|
|
|
return thread_get_current_thread()->team->id;
|
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-10-10 21:30:42 +04:00
|
|
|
status_t
|
|
|
|
team_get_address_space(team_id id, vm_address_space **_addressSpace)
|
|
|
|
{
|
|
|
|
cpu_status state;
|
|
|
|
struct team *team;
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
// ToDo: we need to do something about B_SYSTEM_TEAM vs. its real ID (1)
|
|
|
|
if (id == 1) {
|
|
|
|
// we're the kernel team, so we don't have to go through all
|
|
|
|
// the hassle (locking and hash lookup)
|
2005-12-20 16:29:11 +03:00
|
|
|
*_addressSpace = vm_get_kernel_address_space();
|
2004-10-10 21:30:42 +04:00
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
team = team_get_team_struct_locked(id);
|
|
|
|
if (team != NULL) {
|
2005-12-20 16:29:11 +03:00
|
|
|
atomic_add(&team->address_space->ref_count, 1);
|
|
|
|
*_addressSpace = team->address_space;
|
2004-10-10 21:30:42 +04:00
|
|
|
status = B_OK;
|
|
|
|
} else
|
|
|
|
status = B_BAD_VALUE;
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
static struct team *
|
|
|
|
create_team_struct(const char *name, bool kernel)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2004-02-23 07:08:09 +03:00
|
|
|
struct team *team = (struct team *)malloc(sizeof(struct team));
|
2003-01-06 11:10:54 +03:00
|
|
|
if (team == NULL)
|
2004-02-23 07:08:09 +03:00
|
|
|
return NULL;
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-02-23 07:08:09 +03:00
|
|
|
team->next = team->siblings_next = team->children = team->parent = NULL;
|
2005-03-08 21:18:10 +03:00
|
|
|
team->id = allocate_thread_id();
|
2004-02-23 07:08:09 +03:00
|
|
|
strlcpy(team->name, name, B_OS_NAME_LENGTH);
|
2003-01-06 11:10:54 +03:00
|
|
|
team->num_threads = 0;
|
|
|
|
team->io_context = NULL;
|
2005-12-20 16:29:11 +03:00
|
|
|
team->address_space = NULL;
|
2003-01-06 11:10:54 +03:00
|
|
|
team->thread_list = NULL;
|
|
|
|
team->main_thread = NULL;
|
2005-03-12 18:13:51 +03:00
|
|
|
team->loading_info = NULL;
|
2003-01-06 11:10:54 +03:00
|
|
|
team->state = TEAM_STATE_BIRTH;
|
|
|
|
team->pending_signals = 0;
|
|
|
|
team->death_sem = -1;
|
2004-10-14 18:46:12 +04:00
|
|
|
|
2004-11-26 17:58:01 +03:00
|
|
|
team->dead_threads_kernel_time = 0;
|
|
|
|
team->dead_threads_user_time = 0;
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
list_init(&team->dead_children.list);
|
|
|
|
team->dead_children.count = 0;
|
|
|
|
team->dead_children.wait_for_any = 0;
|
2004-11-26 17:58:01 +03:00
|
|
|
team->dead_children.kernel_time = 0;
|
|
|
|
team->dead_children.user_time = 0;
|
2004-10-14 18:46:12 +04:00
|
|
|
team->dead_children.sem = create_sem(0, "dead children");
|
|
|
|
if (team->dead_children.sem < B_OK)
|
|
|
|
goto error1;
|
|
|
|
|
2003-01-27 02:31:38 +03:00
|
|
|
list_init(&team->image_list);
|
2005-08-03 16:00:42 +04:00
|
|
|
list_init(&team->watcher_list);
|
2003-01-06 11:10:54 +03:00
|
|
|
|
2005-02-28 03:37:16 +03:00
|
|
|
clear_team_debug_info(&team->debug_info, true);
|
2005-02-10 06:03:53 +03:00
|
|
|
|
2003-01-06 11:10:54 +03:00
|
|
|
if (arch_team_init_team_struct(team, kernel) < 0)
|
2004-10-14 18:46:12 +04:00
|
|
|
goto error2;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2003-01-06 11:10:54 +03:00
|
|
|
return team;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
error2:
|
|
|
|
delete_sem(team->dead_children.sem);
|
|
|
|
error1:
|
2004-02-23 07:08:09 +03:00
|
|
|
free(team);
|
2002-08-04 03:39:50 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
|
|
|
static void
|
2003-01-06 11:10:54 +03:00
|
|
|
delete_team_struct(struct team *team)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2005-08-03 16:42:01 +04:00
|
|
|
struct death_entry *death;
|
2004-10-14 18:46:12 +04:00
|
|
|
|
|
|
|
delete_sem(team->dead_children.sem);
|
|
|
|
|
2005-08-03 16:42:01 +04:00
|
|
|
while ((death = list_remove_head_item(&team->dead_children.list)) != NULL)
|
2004-10-14 18:46:12 +04:00
|
|
|
free(death);
|
|
|
|
|
2003-01-06 11:10:54 +03:00
|
|
|
free(team);
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
/** Removes the specified team from the global team hash, and from its parent.
|
|
|
|
* It also moves all of its children up to the parent.
|
|
|
|
* You must hold the team lock when you call this function.
|
|
|
|
* If \a _freeGroup is set to a value other than \c NULL, it must be freed
|
|
|
|
* from the calling function.
|
|
|
|
*/
|
|
|
|
|
2004-03-16 05:46:28 +03:00
|
|
|
void
|
2004-10-14 18:46:12 +04:00
|
|
|
team_remove_team(struct team *team, struct process_group **_freeGroup)
|
2004-03-16 05:46:28 +03:00
|
|
|
{
|
2005-10-21 04:15:17 +04:00
|
|
|
struct team *parent = team->parent;
|
|
|
|
|
|
|
|
// remember how long this team lasted
|
|
|
|
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;
|
|
|
|
|
2004-03-16 05:46:28 +03:00
|
|
|
hash_remove(team_hash, team);
|
2004-12-01 00:11:37 +03:00
|
|
|
sUsedTeams--;
|
|
|
|
|
2004-03-16 05:46:28 +03:00
|
|
|
team->state = TEAM_STATE_DEATH;
|
|
|
|
|
|
|
|
// reparent each of the team's children
|
|
|
|
reparent_children(team);
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
// remove us from our process group
|
|
|
|
remove_team_from_group(team, _freeGroup);
|
|
|
|
|
2005-10-21 04:15:17 +04:00
|
|
|
// remove us from our parent
|
|
|
|
remove_team_from_parent(parent, team);
|
2004-03-16 05:46:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
team_delete_team(struct team *team)
|
|
|
|
{
|
2005-02-10 06:03:53 +03:00
|
|
|
team_id teamID = team->id;
|
2005-02-28 03:37:16 +03:00
|
|
|
port_id debuggerPort = -1;
|
2005-08-05 02:45:04 +04:00
|
|
|
cpu_status state;
|
2005-02-10 06:03:53 +03:00
|
|
|
|
2004-03-16 05:46:28 +03:00
|
|
|
if (team->num_threads > 0) {
|
|
|
|
// there are other threads still in this team,
|
|
|
|
// cycle through and signal kill on each of the threads
|
2004-10-14 18:46:12 +04:00
|
|
|
// ToDo: this can be optimized. There's got to be a better solution.
|
2004-03-16 05:46:28 +03:00
|
|
|
struct thread *temp_thread;
|
|
|
|
char death_sem_name[B_OS_NAME_LENGTH];
|
2005-03-25 21:28:24 +03:00
|
|
|
sem_id deathSem;
|
|
|
|
int32 threadCount;
|
2004-03-16 05:46:28 +03:00
|
|
|
|
2005-08-03 16:00:42 +04:00
|
|
|
sprintf(death_sem_name, "team %ld death sem", teamID);
|
2005-03-25 21:28:24 +03:00
|
|
|
deathSem = create_sem(0, death_sem_name);
|
|
|
|
if (deathSem < 0)
|
2005-10-05 19:32:48 +04:00
|
|
|
panic("team_delete_team: cannot init death sem for team %ld\n", teamID);
|
2004-03-16 05:46:28 +03:00
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
2005-02-28 03:37:16 +03:00
|
|
|
|
2005-03-25 21:28:24 +03:00
|
|
|
team->death_sem = deathSem;
|
|
|
|
threadCount = team->num_threads;
|
|
|
|
|
2005-02-28 03:37:16 +03:00
|
|
|
// If the team was being debugged, that will stop with the termination
|
|
|
|
// of the nub thread. The team structure has already been removed from
|
|
|
|
// the team hash table at this point, so noone can install a debugger
|
|
|
|
// anymore. We fetch the debugger's port to send it a message at the
|
|
|
|
// bitter end.
|
|
|
|
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
|
|
|
|
|
|
|
if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED)
|
|
|
|
debuggerPort = team->debug_info.debugger_port;
|
|
|
|
|
|
|
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
|
|
|
|
2004-03-16 05:46:28 +03:00
|
|
|
// 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;
|
2004-03-17 18:25:43 +03:00
|
|
|
|
|
|
|
send_signal_etc(temp_thread->id, SIGKILLTHR, B_DO_NOT_RESCHEDULE);
|
2004-03-16 05:46:28 +03:00
|
|
|
temp_thread = next;
|
|
|
|
}
|
2005-03-12 18:13:51 +03:00
|
|
|
|
2004-03-16 05:46:28 +03:00
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
// wait until all threads in team are dead.
|
2005-03-25 21:28:24 +03:00
|
|
|
acquire_sem_etc(team->death_sem, threadCount, 0, 0);
|
2004-03-16 05:46:28 +03:00
|
|
|
delete_sem(team->death_sem);
|
|
|
|
}
|
|
|
|
|
2005-08-05 02:45:04 +04:00
|
|
|
// If someone is waiting for this team to be loaded, but it dies
|
|
|
|
// unexpectedly before being done, we need to notify the waiting
|
|
|
|
// thread now.
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
if (team->loading_info) {
|
|
|
|
// there's indeed someone waiting
|
|
|
|
struct team_loading_info *loadingInfo = team->loading_info;
|
|
|
|
team->loading_info = NULL;
|
|
|
|
|
|
|
|
loadingInfo->result = B_ERROR;
|
|
|
|
loadingInfo->done = true;
|
|
|
|
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
// wake up the waiting thread
|
|
|
|
if (loadingInfo->thread->state == B_THREAD_SUSPENDED) {
|
|
|
|
loadingInfo->thread->state = B_THREAD_READY;
|
|
|
|
loadingInfo->thread->next_state = B_THREAD_READY;
|
|
|
|
scheduler_enqueue_in_run_queue(loadingInfo->thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
2005-08-03 16:00:42 +04:00
|
|
|
// notify team watchers
|
|
|
|
|
|
|
|
{
|
|
|
|
// we're not reachable from anyone anymore at this point, so we
|
|
|
|
// can safely access the list without any locking
|
|
|
|
struct team_watcher *watcher;
|
|
|
|
while ((watcher = list_remove_head_item(&team->watcher_list)) != NULL) {
|
|
|
|
watcher->hook(teamID, watcher->data);
|
|
|
|
free(watcher);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-05 14:27:08 +04:00
|
|
|
// free team resources
|
|
|
|
|
2005-12-20 16:29:11 +03:00
|
|
|
vm_delete_address_space(team->address_space);
|
2005-08-05 14:27:08 +04:00
|
|
|
delete_owned_ports(teamID);
|
|
|
|
sem_delete_owned_sems(teamID);
|
|
|
|
remove_images(team);
|
|
|
|
vfs_free_io_context(team->io_context);
|
|
|
|
|
2005-08-03 16:42:01 +04:00
|
|
|
// ToDo: should our death_entries be moved one level up?
|
|
|
|
delete_team_struct(team);
|
2005-02-10 06:03:53 +03:00
|
|
|
|
|
|
|
// notify the debugger, that the team is gone
|
2005-02-24 19:11:25 +03:00
|
|
|
user_debug_team_deleted(teamID, debuggerPort);
|
2004-03-16 05:46:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
static uint32
|
|
|
|
get_arguments_data_size(char **args, int32 argc)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2003-09-09 06:36:52 +04:00
|
|
|
uint32 size = 0;
|
2004-10-14 22:07:04 +04:00
|
|
|
int32 count;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2003-01-12 19:29:28 +03:00
|
|
|
for (count = 0; count < argc; count++)
|
|
|
|
size += strlen(args[count]) + 1;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2003-01-12 19:29:28 +03:00
|
|
|
return size + (argc + 1) * sizeof(char *) + sizeof(struct uspace_program_args);
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
static void
|
|
|
|
free_team_arg(struct team_arg *teamArg)
|
|
|
|
{
|
2004-10-15 05:52:07 +04:00
|
|
|
free_strings_array(teamArg->args, teamArg->arg_count);
|
|
|
|
free_strings_array(teamArg->env, teamArg->env_count);
|
2004-10-14 22:07:04 +04:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
free(teamArg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
static status_t
|
|
|
|
create_team_arg(struct team_arg **_teamArg, int32 argCount, char * const *args,
|
|
|
|
int32 envCount, char * const *env, bool kernel)
|
2004-10-07 19:34:17 +04:00
|
|
|
{
|
2005-10-05 19:32:48 +04:00
|
|
|
status_t status;
|
|
|
|
char **argsCopy;
|
|
|
|
char **envCopy;
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
struct team_arg *teamArg = (struct team_arg *)malloc(sizeof(struct team_arg));
|
|
|
|
if (teamArg == NULL)
|
2005-10-05 19:32:48 +04:00
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
// copy the args over
|
|
|
|
|
|
|
|
status = copy_strings_array(args, argCount, &argsCopy, kernel);
|
|
|
|
if (status != B_OK)
|
|
|
|
return status;
|
2004-10-07 19:34:17 +04:00
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
status = copy_strings_array(env, envCount, &envCopy, kernel);
|
|
|
|
if (status != B_OK) {
|
|
|
|
free_strings_array(argsCopy, argCount);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
teamArg->arg_count = argCount;
|
|
|
|
teamArg->args = argsCopy;
|
2004-10-07 19:34:17 +04:00
|
|
|
teamArg->env_count = envCount;
|
2005-10-05 19:32:48 +04:00
|
|
|
teamArg->env = envCopy;
|
2004-10-07 19:34:17 +04:00
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
*_teamArg = teamArg;
|
|
|
|
return B_OK;
|
2004-10-07 19:34:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-16 16:52:59 +04:00
|
|
|
static int32
|
2004-10-12 08:03:52 +04:00
|
|
|
team_create_thread_start(void *args)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2005-09-13 19:44:30 +04:00
|
|
|
status_t err;
|
2002-08-04 03:39:50 +04:00
|
|
|
struct thread *t;
|
2003-01-07 12:40:59 +03:00
|
|
|
struct team *team;
|
2003-01-12 19:29:28 +03:00
|
|
|
struct team_arg *teamArgs = args;
|
2004-10-14 22:07:04 +04:00
|
|
|
const char *path;
|
2004-10-20 04:19:38 +04:00
|
|
|
addr_t entry;
|
2002-08-04 03:39:50 +04:00
|
|
|
char ustack_name[128];
|
2004-10-07 19:34:17 +04:00
|
|
|
uint32 sizeLeft;
|
2002-08-04 03:39:50 +04:00
|
|
|
char **uargs;
|
|
|
|
char **uenv;
|
|
|
|
char *udest;
|
2003-01-12 19:29:28 +03:00
|
|
|
struct uspace_program_args *uspa;
|
2004-10-07 19:34:17 +04:00
|
|
|
uint32 argCount, envCount, i;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
|
|
|
t = thread_get_current_thread();
|
2003-01-07 12:40:59 +03:00
|
|
|
team = t->team;
|
2005-06-14 15:15:05 +04:00
|
|
|
cache_node_launched(teamArgs->arg_count, teamArgs->args);
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
TRACE(("team_create_thread_start: entry thread %ld\n", t->id));
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-11-08 16:53:34 +03:00
|
|
|
// create an initial primary stack area
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-11-18 21:15:39 +03:00
|
|
|
// Main stack area layout is currently as follows (starting from 0):
|
|
|
|
//
|
|
|
|
// size | usage
|
|
|
|
// -----------------------------+--------------------------------
|
|
|
|
// USER_MAIN_THREAD_STACK_SIZE | actual stack
|
|
|
|
// TLS_SIZE | TLS data
|
|
|
|
// ENV_SIZE | environment variables
|
|
|
|
// arguments size | arguments passed to the team
|
|
|
|
|
2005-03-11 02:06:51 +03:00
|
|
|
// ToDo: ENV_SIZE is a) limited, and b) not used after libroot copied it to the heap
|
2004-06-10 05:43:16 +04:00
|
|
|
// ToDo: we could reserve the whole USER_STACK_REGION upfront...
|
2003-01-07 12:40:59 +03:00
|
|
|
|
2004-11-18 21:15:39 +03:00
|
|
|
sizeLeft = PAGE_ALIGN(USER_MAIN_THREAD_STACK_SIZE + TLS_SIZE + ENV_SIZE +
|
2004-10-07 19:34:17 +04:00
|
|
|
get_arguments_data_size(teamArgs->args, teamArgs->arg_count));
|
|
|
|
t->user_stack_base = USER_STACK_REGION + USER_STACK_REGION_SIZE - sizeLeft;
|
2004-11-18 21:15:39 +03:00
|
|
|
t->user_stack_size = USER_MAIN_THREAD_STACK_SIZE;
|
2004-11-08 16:53:34 +03:00
|
|
|
// the exact location at the end of the user stack area
|
2003-01-07 12:40:59 +03:00
|
|
|
|
2004-06-09 01:35:14 +04:00
|
|
|
sprintf(ustack_name, "%s_main_stack", team->name);
|
2004-11-08 16:53:34 +03:00
|
|
|
t->user_stack_area = create_area_etc(team, ustack_name, (void **)&t->user_stack_base,
|
2004-11-18 21:15:39 +03:00
|
|
|
B_EXACT_ADDRESS, sizeLeft, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA | B_STACK_AREA);
|
2004-11-08 16:53:34 +03:00
|
|
|
if (t->user_stack_area < 0) {
|
2004-10-14 22:07:04 +04:00
|
|
|
dprintf("team_create_thread_start: could not create default user stack region\n");
|
2005-10-05 19:32:48 +04:00
|
|
|
|
|
|
|
free_team_arg(teamArgs);
|
2004-11-08 16:53:34 +03:00
|
|
|
return t->user_stack_area;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2003-01-07 12:40:59 +03:00
|
|
|
// now that the TLS area is allocated, initialize TLS
|
|
|
|
arch_thread_init_tls(t);
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
argCount = teamArgs->arg_count;
|
|
|
|
envCount = teamArgs->env_count;
|
|
|
|
|
2004-11-02 00:38:56 +03:00
|
|
|
uspa = (struct uspace_program_args *)(t->user_stack_base + t->user_stack_size + TLS_SIZE + ENV_SIZE);
|
2002-08-04 03:39:50 +04:00
|
|
|
uargs = (char **)(uspa + 1);
|
2004-10-07 19:34:17 +04:00
|
|
|
udest = (char *)(uargs + argCount + 1);
|
2004-03-16 06:14:48 +03:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
TRACE(("addr: stack base = 0x%lx, uargs = %p, udest = %p, sizeLeft = %lu\n",
|
|
|
|
t->user_stack_base, uargs, udest, sizeLeft));
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-11-18 21:15:39 +03:00
|
|
|
sizeLeft = t->user_stack_base + sizeLeft - (addr_t)udest;
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
for (i = 0; i < argCount; i++) {
|
2004-11-02 00:38:56 +03:00
|
|
|
ssize_t length = user_strlcpy(udest, teamArgs->args[i], sizeLeft);
|
|
|
|
if (length < B_OK) {
|
|
|
|
argCount = 0;
|
|
|
|
break;
|
|
|
|
}
|
2004-10-07 19:34:17 +04:00
|
|
|
|
|
|
|
uargs[i] = udest;
|
2004-11-02 00:38:56 +03:00
|
|
|
udest += ++length;
|
2004-10-07 19:34:17 +04:00
|
|
|
sizeLeft -= length;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
2004-10-07 19:34:17 +04:00
|
|
|
uargs[argCount] = NULL;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2005-03-11 02:06:51 +03:00
|
|
|
uenv = (char **)(t->user_stack_base + t->user_stack_size + TLS_SIZE);
|
|
|
|
udest = (char *)uenv + ENV_SIZE - 1;
|
2004-11-02 00:38:56 +03:00
|
|
|
// the environment variables are copied from back to front
|
2004-03-16 06:14:48 +03:00
|
|
|
|
2004-11-02 00:38:56 +03:00
|
|
|
TRACE(("team_create_thread_start: envc: %ld, env: %p\n", teamArgs->env_count, (void *)teamArgs->env));
|
2004-10-07 19:34:17 +04:00
|
|
|
|
|
|
|
for (i = 0; i < envCount; i++) {
|
2004-11-02 00:38:56 +03:00
|
|
|
ssize_t length = strlen(teamArgs->env[i]) + 1;
|
|
|
|
udest -= length;
|
2004-10-07 19:34:17 +04:00
|
|
|
uenv[i] = udest;
|
2004-11-02 00:38:56 +03:00
|
|
|
|
|
|
|
if (user_memcpy(udest, teamArgs->env[i], length) < B_OK) {
|
|
|
|
envCount = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
sizeLeft -= length;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
2004-10-07 19:34:17 +04:00
|
|
|
uenv[envCount] = NULL;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-11-02 00:38:56 +03:00
|
|
|
path = teamArgs->args[0];
|
2004-10-14 22:07:04 +04:00
|
|
|
user_memcpy(uspa->program_path, path, sizeof(uspa->program_path));
|
2004-10-07 19:34:17 +04:00
|
|
|
uspa->argc = argCount;
|
2002-08-04 03:39:50 +04:00
|
|
|
uspa->argv = uargs;
|
2004-10-07 19:34:17 +04:00
|
|
|
uspa->envc = envCount;
|
2002-08-04 03:39:50 +04:00
|
|
|
uspa->envp = uenv;
|
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
TRACE(("team_create_thread_start: loading elf binary '%s'\n", path));
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2006-05-30 04:21:22 +04:00
|
|
|
// TODO: add args
|
|
|
|
strlcpy(team->args, path, sizeof(team->args));
|
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
free_team_arg(teamArgs);
|
2005-10-05 19:32:48 +04:00
|
|
|
// the arguments are already on the user stack, we no longer need them in this form
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-06-10 05:43:16 +04:00
|
|
|
// ToDo: don't use fixed paths!
|
2006-03-30 22:08:51 +04:00
|
|
|
err = elf_load_user_image("/boot/beos/system/runtime_loader", team, 0, &entry);
|
2005-03-09 04:43:56 +03:00
|
|
|
if (err < B_OK) {
|
2004-06-10 05:43:16 +04:00
|
|
|
// Luckily, we don't have to clean up the mess we created - that's
|
|
|
|
// done for us by the normal team deletion process
|
2005-09-13 19:44:30 +04:00
|
|
|
TRACE(("team_create_thread_start: error when elf_load_user_image() %s\n", strerror(err)));
|
2004-06-10 05:43:16 +04:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
TRACE(("team_create_thread_start: loaded elf. entry = 0x%lx\n", entry));
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2003-01-07 12:40:59 +03:00
|
|
|
team->state = TEAM_STATE_NORMAL;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
|
|
|
// jump to the entry point in user space
|
2003-04-18 13:38:28 +04:00
|
|
|
arch_thread_enter_uspace(t, entry, uspa, NULL);
|
2002-08-04 03:39:50 +04:00
|
|
|
|
|
|
|
// never gets here
|
2005-09-13 19:44:30 +04:00
|
|
|
return B_OK;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
/** The BeOS kernel exports a function with this name, but most probably with
|
|
|
|
* different parameters; we should not make it public.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static thread_id
|
2005-10-05 19:32:48 +04:00
|
|
|
load_image_etc(int32 argCount, char * const *args, int32 envCount, char * const *env,
|
|
|
|
int32 priority, uint32 flags, bool kernel)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2004-10-14 18:46:12 +04:00
|
|
|
struct process_group *group;
|
2004-03-16 05:46:28 +03:00
|
|
|
struct team *team, *parent;
|
2004-06-11 05:36:29 +04:00
|
|
|
const char *threadName;
|
2004-10-14 22:07:04 +04:00
|
|
|
thread_id thread;
|
2005-10-05 19:32:48 +04:00
|
|
|
status_t status;
|
2004-06-11 05:36:29 +04:00
|
|
|
cpu_status state;
|
2003-01-12 19:29:28 +03:00
|
|
|
struct team_arg *teamArgs;
|
2005-03-12 18:13:51 +03:00
|
|
|
struct team_loading_info loadingInfo;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
if (args == NULL || argCount == 0)
|
|
|
|
return B_BAD_VALUE;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
TRACE(("load_image_etc: name '%s', args = %p, argCount = %ld\n",
|
|
|
|
args[0], args, argCount));
|
|
|
|
|
|
|
|
team = create_team_struct(args[0], false);
|
2003-01-07 12:40:59 +03:00
|
|
|
if (team == NULL)
|
2004-03-16 05:46:28 +03:00
|
|
|
return B_NO_MEMORY;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-03-16 05:46:28 +03:00
|
|
|
parent = thread_get_current_thread()->team;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2005-03-12 18:13:51 +03:00
|
|
|
if (flags & B_WAIT_TILL_LOADED) {
|
|
|
|
loadingInfo.thread = thread_get_current_thread();
|
|
|
|
loadingInfo.result = B_ERROR;
|
|
|
|
loadingInfo.done = false;
|
|
|
|
team->loading_info = &loadingInfo;
|
|
|
|
}
|
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
2004-03-16 05:46:28 +03:00
|
|
|
|
2003-01-07 12:40:59 +03:00
|
|
|
hash_insert(team_hash, team);
|
2004-03-16 05:46:28 +03:00
|
|
|
insert_team_into_parent(parent, team);
|
2004-10-14 18:46:12 +04:00
|
|
|
insert_team_into_group(parent->group, team);
|
2004-12-01 00:11:37 +03:00
|
|
|
sUsedTeams++;
|
2004-03-16 05:46:28 +03:00
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
status = create_team_arg(&teamArgs, argCount, args, envCount, env, kernel);
|
|
|
|
if (status != B_OK)
|
2002-08-04 03:39:50 +04:00
|
|
|
goto err1;
|
|
|
|
|
2002-12-03 17:17:53 +03:00
|
|
|
// create a new io_context for this team
|
2004-10-10 21:30:42 +04:00
|
|
|
team->io_context = vfs_new_io_context(parent->io_context);
|
2003-01-07 12:40:59 +03:00
|
|
|
if (!team->io_context) {
|
2005-10-05 19:32:48 +04:00
|
|
|
status = B_NO_MEMORY;
|
2004-10-07 19:34:17 +04:00
|
|
|
goto err2;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// create an address space for this team
|
2005-12-20 16:29:11 +03:00
|
|
|
status = vm_create_address_space(team->id, USER_BASE, USER_SIZE, false,
|
|
|
|
&team->address_space);
|
2005-10-05 19:32:48 +04:00
|
|
|
if (status < B_OK)
|
2004-10-07 19:34:17 +04:00
|
|
|
goto err3;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-06-11 05:36:29 +04:00
|
|
|
// cut the path from the main thread name
|
2004-10-14 22:07:04 +04:00
|
|
|
threadName = strrchr(args[0], '/');
|
2004-06-11 05:36:29 +04:00
|
|
|
if (threadName != NULL)
|
|
|
|
threadName++;
|
|
|
|
else
|
2004-10-14 22:07:04 +04:00
|
|
|
threadName = args[0];
|
2004-06-11 05:36:29 +04:00
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
// Create a kernel thread, but under the context of the new team
|
|
|
|
// The new thread will take over ownership of teamArgs
|
2005-01-27 10:11:45 +03:00
|
|
|
thread = spawn_kernel_thread_etc(team_create_thread_start, threadName, B_NORMAL_PRIORITY,
|
2005-03-08 21:18:10 +03:00
|
|
|
teamArgs, team->id, team->id);
|
2004-10-14 22:07:04 +04:00
|
|
|
if (thread < 0) {
|
2005-10-05 19:32:48 +04:00
|
|
|
status = thread;
|
2004-10-07 19:34:17 +04:00
|
|
|
goto err4;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2005-03-12 18:13:51 +03:00
|
|
|
// wait for the loader of the new team to finish its work
|
|
|
|
if (flags & B_WAIT_TILL_LOADED) {
|
|
|
|
struct thread *mainThread;
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
mainThread = thread_get_thread_struct_locked(thread);
|
|
|
|
if (mainThread) {
|
|
|
|
// resume the team's main thread
|
|
|
|
if (mainThread->state == B_THREAD_SUSPENDED) {
|
|
|
|
mainThread->state = mainThread->next_state = B_THREAD_READY;
|
|
|
|
scheduler_enqueue_in_run_queue(mainThread);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now suspend ourselves until loading is finished.
|
2005-08-05 02:45:04 +04:00
|
|
|
// We will be woken either by the thread, when it finished or
|
2005-03-12 18:13:51 +03:00
|
|
|
// aborted loading, or when the team is going to die (e.g. is
|
|
|
|
// killed). In either case the one setting `loadingInfo.done' is
|
|
|
|
// responsible for removing the info from the team structure.
|
|
|
|
while (!loadingInfo.done) {
|
|
|
|
thread_get_current_thread()->next_state = B_THREAD_SUSPENDED;
|
|
|
|
scheduler_reschedule();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Impressive! Someone managed to kill the thread in this short
|
|
|
|
// time.
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
if (loadingInfo.result < B_OK)
|
|
|
|
return loadingInfo.result;
|
|
|
|
}
|
|
|
|
|
2005-02-24 19:11:25 +03:00
|
|
|
// notify the debugger
|
|
|
|
user_debug_team_created(team->id);
|
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
return thread;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
err4:
|
2005-12-20 16:29:11 +03:00
|
|
|
vm_put_address_space(team->address_space);
|
2002-08-04 03:39:50 +04:00
|
|
|
err3:
|
2004-10-07 19:34:17 +04:00
|
|
|
vfs_free_io_context(team->io_context);
|
2002-08-04 03:39:50 +04:00
|
|
|
err2:
|
2004-10-07 19:34:17 +04:00
|
|
|
free_team_arg(teamArgs);
|
2002-08-04 03:39:50 +04:00
|
|
|
err1:
|
|
|
|
// remove the team structure from the team hash table and delete the team structure
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
2004-03-16 05:46:28 +03:00
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
remove_team_from_group(team, &group);
|
2004-03-16 05:46:28 +03:00
|
|
|
remove_team_from_parent(parent, team);
|
2003-01-07 12:40:59 +03:00
|
|
|
hash_remove(team_hash, team);
|
2004-03-16 05:46:28 +03:00
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
2004-10-14 18:46:12 +04:00
|
|
|
|
|
|
|
team_delete_process_group(group);
|
2003-01-07 12:40:59 +03:00
|
|
|
delete_team_struct(team);
|
2004-10-07 19:34:17 +04:00
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
return status;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
/** Almost shuts down the current team and loads a new image into it.
|
|
|
|
* If successful, this function does not return and will takeover ownership of
|
|
|
|
* the arguments provided.
|
2005-10-05 19:32:48 +04:00
|
|
|
* This function may only be called from user space.
|
2004-10-14 22:07:04 +04:00
|
|
|
*/
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
static status_t
|
2005-10-05 19:32:48 +04:00
|
|
|
exec_team(const char *path, int32 argCount, char * const *args,
|
|
|
|
int32 envCount, char * const *env)
|
2004-10-07 19:34:17 +04:00
|
|
|
{
|
|
|
|
struct team *team = thread_get_current_thread()->team;
|
|
|
|
struct team_arg *teamArgs;
|
2004-11-21 01:05:50 +03:00
|
|
|
const char *threadName;
|
2005-03-25 21:28:24 +03:00
|
|
|
status_t status = B_OK;
|
|
|
|
cpu_status state;
|
|
|
|
struct thread *thread;
|
|
|
|
thread_id nubThreadID = -1;
|
2004-10-07 19:34:17 +04:00
|
|
|
|
2005-05-29 16:53:56 +04:00
|
|
|
TRACE(("exec_team(path = \"%s\", argc = %ld, envCount = %ld): team %lx\n", args[0], argCount, envCount, team->id));
|
2004-10-07 19:34:17 +04:00
|
|
|
|
|
|
|
// switching the kernel at run time is probably not a good idea :)
|
|
|
|
if (team == team_get_kernel_team())
|
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
|
|
|
// we currently need to be single threaded here
|
|
|
|
// ToDo: maybe we should just kill all other threads and
|
|
|
|
// make the current thread the team's main thread?
|
2005-03-25 21:28:24 +03:00
|
|
|
if (team->main_thread != thread_get_current_thread())
|
2004-10-07 19:34:17 +04:00
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
2005-03-25 21:28:24 +03:00
|
|
|
// The debug nub thread, a pure kernel thread, is allowed to survive.
|
|
|
|
// We iterate through the thread list to make sure that there's no other
|
|
|
|
// thread.
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
|
|
|
|
|
|
|
if (team->debug_info.flags & B_TEAM_DEBUG_DEBUGGER_INSTALLED)
|
|
|
|
nubThreadID = team->debug_info.nub_thread;
|
|
|
|
|
|
|
|
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
|
|
|
|
|
|
|
|
for (thread = team->thread_list; thread; thread = thread->team_next) {
|
|
|
|
if (thread != team->main_thread && thread->id != nubThreadID) {
|
|
|
|
status = B_NOT_ALLOWED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
if (status != B_OK)
|
|
|
|
return status;
|
|
|
|
|
2004-10-07 19:34:17 +04:00
|
|
|
// ToDo: maybe we should make sure upfront that the target path is an app?
|
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
status = create_team_arg(&teamArgs, argCount, args, envCount, env, false);
|
|
|
|
if (status != B_OK)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
// replace args[0] with the path argument, just to be on the safe side
|
|
|
|
free(teamArgs->args[0]);
|
|
|
|
teamArgs->args[0] = strdup(path);
|
2004-10-07 19:34:17 +04:00
|
|
|
|
|
|
|
// ToDo: remove team resources if there are any left
|
2004-10-10 21:30:42 +04:00
|
|
|
// alarm, signals
|
2004-10-12 08:03:52 +04:00
|
|
|
// thread_atkernel_exit() might not be called at all
|
2004-10-07 19:34:17 +04:00
|
|
|
|
2005-03-25 21:28:24 +03:00
|
|
|
user_debug_prepare_for_exec();
|
|
|
|
|
2005-12-20 16:29:11 +03:00
|
|
|
vm_delete_areas(team->address_space);
|
2004-10-07 19:34:17 +04:00
|
|
|
delete_owned_ports(team->id);
|
|
|
|
sem_delete_owned_sems(team->id);
|
|
|
|
remove_images(team);
|
|
|
|
vfs_exec_io_context(team->io_context);
|
|
|
|
|
2005-03-25 21:28:24 +03:00
|
|
|
user_debug_finish_after_exec();
|
|
|
|
|
2004-11-21 01:31:28 +03:00
|
|
|
// rename the team
|
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
strlcpy(team->name, path, B_OS_NAME_LENGTH);
|
2004-11-21 01:31:28 +03:00
|
|
|
|
|
|
|
// cut the path from the team name and rename the main thread, too
|
2005-10-05 19:32:48 +04:00
|
|
|
threadName = strrchr(path, '/');
|
2004-11-21 01:05:50 +03:00
|
|
|
if (threadName != NULL)
|
|
|
|
threadName++;
|
|
|
|
else
|
2005-10-05 19:32:48 +04:00
|
|
|
threadName = path;
|
2004-11-21 01:05:50 +03:00
|
|
|
rename_thread(thread_get_current_thread_id(), threadName);
|
|
|
|
|
2004-10-12 08:03:52 +04:00
|
|
|
status = team_create_thread_start(teamArgs);
|
2004-10-07 19:34:17 +04:00
|
|
|
// this one usually doesn't return...
|
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
// sorry, we have to kill us, there is no way out anymore
|
|
|
|
// (without any areas left and all that)
|
2004-10-07 19:34:17 +04:00
|
|
|
exit_thread(status);
|
2005-10-05 19:32:48 +04:00
|
|
|
|
|
|
|
// we return a status here since the signal that is sent by the
|
|
|
|
// call above is not immediately handled
|
2004-10-07 21:17:04 +04:00
|
|
|
return B_ERROR;
|
2004-10-07 19:34:17 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-12 08:03:52 +04:00
|
|
|
/** This is the first function to be called from the newly created
|
|
|
|
* main child thread.
|
|
|
|
* It will fill in everything what's left to do from fork_arg, and
|
|
|
|
* return from the parent's fork() syscall to the child.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int32
|
|
|
|
fork_team_thread_start(void *_args)
|
|
|
|
{
|
|
|
|
struct thread *thread = thread_get_current_thread();
|
|
|
|
struct fork_arg *forkArgs = (struct fork_arg *)_args;
|
|
|
|
|
|
|
|
struct arch_fork_arg archArgs = forkArgs->arch_info;
|
|
|
|
// we need a local copy of the arch dependent part
|
|
|
|
|
2004-11-08 16:53:34 +03:00
|
|
|
thread->user_stack_area = forkArgs->user_stack_area;
|
2004-10-12 08:03:52 +04:00
|
|
|
thread->user_stack_base = forkArgs->user_stack_base;
|
|
|
|
thread->user_stack_size = forkArgs->user_stack_size;
|
|
|
|
thread->user_local_storage = forkArgs->user_local_storage;
|
|
|
|
|
|
|
|
arch_thread_init_tls(thread);
|
|
|
|
|
|
|
|
free(forkArgs);
|
|
|
|
|
|
|
|
// set frame of the parent thread to this one, too
|
|
|
|
|
|
|
|
arch_restore_fork_frame(&archArgs);
|
|
|
|
// This one won't return here
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-10 21:30:42 +04:00
|
|
|
static thread_id
|
|
|
|
fork_team(void)
|
|
|
|
{
|
2004-10-12 08:03:52 +04:00
|
|
|
struct team *parentTeam = thread_get_current_thread()->team, *team;
|
|
|
|
struct thread *parentThread = thread_get_current_thread();
|
2004-10-15 20:14:51 +04:00
|
|
|
struct process_group *group = NULL;
|
2004-10-12 08:03:52 +04:00
|
|
|
struct fork_arg *forkArgs;
|
2004-10-10 21:30:42 +04:00
|
|
|
struct area_info info;
|
2004-10-12 08:03:52 +04:00
|
|
|
thread_id threadID;
|
2004-10-10 21:30:42 +04:00
|
|
|
cpu_status state;
|
|
|
|
status_t status;
|
|
|
|
int32 cookie;
|
|
|
|
|
2005-05-29 16:53:56 +04:00
|
|
|
TRACE(("fork_team(): team %lx\n", parentTeam->id));
|
2004-10-10 21:30:42 +04:00
|
|
|
|
2004-10-12 08:03:52 +04:00
|
|
|
if (parentTeam == team_get_kernel_team())
|
2004-10-10 21:30:42 +04:00
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
|
|
|
// create a new team
|
|
|
|
// ToDo: this is very similar to team_create_team() - maybe we can do something about it :)
|
|
|
|
|
2004-10-12 08:03:52 +04:00
|
|
|
team = create_team_struct(parentTeam->name, false);
|
2004-10-10 21:30:42 +04:00
|
|
|
if (team == NULL)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
hash_insert(team_hash, team);
|
2004-10-12 08:03:52 +04:00
|
|
|
insert_team_into_parent(parentTeam, team);
|
2004-10-15 20:14:51 +04:00
|
|
|
insert_team_into_group(parentTeam->group, team);
|
2004-12-01 00:11:37 +03:00
|
|
|
sUsedTeams++;
|
2004-10-10 21:30:42 +04:00
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
2004-10-12 08:03:52 +04:00
|
|
|
forkArgs = (struct fork_arg *)malloc(sizeof(struct fork_arg));
|
|
|
|
if (forkArgs == NULL) {
|
|
|
|
status = B_NO_MEMORY;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
|
2004-10-10 21:30:42 +04:00
|
|
|
// create a new io_context for this team
|
2004-10-12 08:03:52 +04:00
|
|
|
team->io_context = vfs_new_io_context(parentTeam->io_context);
|
2004-10-10 21:30:42 +04:00
|
|
|
if (!team->io_context) {
|
|
|
|
status = B_NO_MEMORY;
|
|
|
|
goto err2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// create an address space for this team
|
2005-12-20 16:29:11 +03:00
|
|
|
status = vm_create_address_space(team->id, USER_BASE, USER_SIZE, false,
|
|
|
|
&team->address_space);
|
2004-10-10 21:30:42 +04:00
|
|
|
if (status < B_OK)
|
|
|
|
goto err3;
|
|
|
|
|
|
|
|
// copy all areas of the team
|
|
|
|
// ToDo: should be able to handle stack areas differently (ie. don't have them copy-on-write)
|
|
|
|
// ToDo: all stacks of other threads than the current one could be left out
|
|
|
|
|
|
|
|
cookie = 0;
|
|
|
|
while (get_next_area_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
|
|
|
|
void *address;
|
2005-12-20 16:29:11 +03:00
|
|
|
area_id area = vm_copy_area(team->address_space->id, info.name, &address, B_CLONE_ADDRESS,
|
2004-10-12 08:03:52 +04:00
|
|
|
info.protection, info.area);
|
|
|
|
if (area < B_OK) {
|
|
|
|
status = area;
|
2004-10-10 21:30:42 +04:00
|
|
|
break;
|
2004-10-12 08:03:52 +04:00
|
|
|
}
|
|
|
|
|
2004-11-08 16:53:34 +03:00
|
|
|
if (info.area == parentThread->user_stack_area)
|
2004-10-12 08:03:52 +04:00
|
|
|
forkArgs->user_stack_area = area;
|
2004-10-10 21:30:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (status < B_OK)
|
|
|
|
goto err4;
|
|
|
|
|
2004-10-12 08:03:52 +04:00
|
|
|
forkArgs->user_stack_base = parentThread->user_stack_base;
|
|
|
|
forkArgs->user_stack_size = parentThread->user_stack_size;
|
|
|
|
forkArgs->user_local_storage = parentThread->user_local_storage;
|
|
|
|
arch_store_fork_frame(&forkArgs->arch_info);
|
|
|
|
|
|
|
|
// ToDo: copy image list
|
|
|
|
|
|
|
|
// create a kernel thread under the context of the new team
|
|
|
|
threadID = spawn_kernel_thread_etc(fork_team_thread_start, parentThread->name,
|
2005-03-08 21:18:10 +03:00
|
|
|
parentThread->priority, forkArgs, team->id, team->id);
|
2004-10-12 08:03:52 +04:00
|
|
|
if (threadID < 0) {
|
|
|
|
status = threadID;
|
2004-10-10 21:30:42 +04:00
|
|
|
goto err4;
|
|
|
|
}
|
|
|
|
|
2005-02-24 19:11:25 +03:00
|
|
|
// notify the debugger
|
|
|
|
user_debug_team_created(team->id);
|
|
|
|
|
2004-10-12 08:03:52 +04:00
|
|
|
resume_thread(threadID);
|
|
|
|
return threadID;
|
2004-10-10 21:30:42 +04:00
|
|
|
|
|
|
|
err4:
|
2005-12-20 16:29:11 +03:00
|
|
|
vm_delete_address_space(team->address_space);
|
2004-10-10 21:30:42 +04:00
|
|
|
err3:
|
|
|
|
vfs_free_io_context(team->io_context);
|
|
|
|
err2:
|
2004-10-12 08:03:52 +04:00
|
|
|
free(forkArgs);
|
|
|
|
err1:
|
2004-10-10 21:30:42 +04:00
|
|
|
// remove the team structure from the team hash table and delete the team structure
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
2004-10-15 20:14:51 +04:00
|
|
|
remove_team_from_group(team, &group);
|
2004-10-12 08:03:52 +04:00
|
|
|
remove_team_from_parent(parentTeam, team);
|
2004-10-10 21:30:42 +04:00
|
|
|
hash_remove(team_hash, team);
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
2004-10-15 20:14:51 +04:00
|
|
|
|
|
|
|
team_delete_process_group(group);
|
2004-10-10 21:30:42 +04:00
|
|
|
delete_team_struct(team);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-14 19:42:56 +04:00
|
|
|
/** This searches the session of the team for the specified group ID.
|
|
|
|
* You must hold the team lock when you call this function.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct process_group *
|
|
|
|
get_process_group_locked(struct team *team, pid_t id)
|
|
|
|
{
|
|
|
|
struct list *groups = &team->group->session->groups;
|
|
|
|
struct process_group *group = NULL;
|
2004-10-18 19:16:00 +04:00
|
|
|
|
|
|
|
// ToDo: a process group lasts as long as its last member - and
|
|
|
|
// that doesn't have to be the process leader. IOW we need
|
|
|
|
// a separate hash table for those groups without a leader.
|
2004-10-14 19:42:56 +04:00
|
|
|
|
|
|
|
// a short cut when the current team's group is asked for
|
|
|
|
if (team->group->id == id)
|
|
|
|
return team->group;
|
|
|
|
|
|
|
|
while ((group = list_get_next_item(groups, group)) != NULL) {
|
|
|
|
if (group->id == id)
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
static status_t
|
2004-10-18 19:16:00 +04:00
|
|
|
update_wait_for_any(struct team *team, thread_id child, int32 change)
|
2004-10-14 19:42:56 +04:00
|
|
|
{
|
|
|
|
struct process_group *group;
|
|
|
|
cpu_status state;
|
|
|
|
|
|
|
|
if (child > 0)
|
|
|
|
return B_OK;
|
|
|
|
|
|
|
|
if (child == -1) {
|
|
|
|
// we only wait for children of the current team
|
2004-10-18 19:16:00 +04:00
|
|
|
atomic_add(&team->dead_children.wait_for_any, change);
|
2004-10-14 19:42:56 +04:00
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
if (child < 0) {
|
|
|
|
// we wait for all children of the specified process group
|
|
|
|
group = get_process_group_locked(team, -child);
|
|
|
|
} else {
|
|
|
|
// we wait for any children of the current team's group
|
|
|
|
group = team->group;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (group != NULL) {
|
|
|
|
for (team = group->teams; team; team = team->group_next) {
|
2004-10-18 19:16:00 +04:00
|
|
|
atomic_add(&team->dead_children.wait_for_any, change);
|
2004-10-14 19:42:56 +04:00
|
|
|
}
|
|
|
|
|
2004-10-18 19:16:00 +04:00
|
|
|
atomic_add(&group->wait_for_any, change);
|
2004-10-14 19:42:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
return group != NULL ? B_OK : B_BAD_THREAD_ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-18 19:16:00 +04:00
|
|
|
static status_t
|
|
|
|
unregister_wait_for_any(struct team *team, thread_id child)
|
|
|
|
{
|
2004-11-04 19:42:16 +03:00
|
|
|
return update_wait_for_any(team, child, -1);
|
2004-10-18 19:16:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static status_t
|
|
|
|
register_wait_for_any(struct team *team, thread_id child)
|
|
|
|
{
|
2004-11-04 19:42:16 +03:00
|
|
|
return update_wait_for_any(team, child, 1);
|
2004-10-18 19:16:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-14 19:42:56 +04:00
|
|
|
static status_t
|
|
|
|
get_team_death_entry(struct team *team, thread_id child, struct death_entry *death,
|
|
|
|
struct death_entry **_freeDeath)
|
2004-10-14 18:46:12 +04:00
|
|
|
{
|
|
|
|
struct death_entry *entry = NULL;
|
|
|
|
|
|
|
|
// find matching death entry structure
|
2004-10-14 19:42:56 +04:00
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
while ((entry = list_get_next_item(&team->dead_children.list, entry)) != NULL) {
|
|
|
|
if (child != -1 && entry->thread != child)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// we found one
|
|
|
|
|
|
|
|
*death = *entry;
|
|
|
|
|
|
|
|
// only remove the death entry if there aren't any other interested parties
|
|
|
|
if ((child == -1 && atomic_add(&team->dead_children.wait_for_any, -1) == 1)
|
|
|
|
|| (child != -1 && team->dead_children.wait_for_any == 0)) {
|
|
|
|
list_remove_link(entry);
|
|
|
|
team->dead_children.count--;
|
2004-10-14 19:42:56 +04:00
|
|
|
*_freeDeath = entry;
|
2004-10-14 18:46:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return child > 0 ? B_BAD_THREAD_ID : B_WOULD_BLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-08-03 16:42:01 +04:00
|
|
|
/** Gets the next pending death entry, if any. Also fills in \a _waitSem
|
|
|
|
* to the semaphore the caller have to wait for for other death entries.
|
|
|
|
* Must be called with the team lock held.
|
|
|
|
*/
|
|
|
|
|
2004-10-14 19:42:56 +04:00
|
|
|
static status_t
|
|
|
|
get_death_entry(struct team *team, pid_t child, struct death_entry *death,
|
2004-10-18 19:16:00 +04:00
|
|
|
sem_id *_waitSem, struct death_entry **_freeDeath)
|
2004-10-14 19:42:56 +04:00
|
|
|
{
|
|
|
|
struct process_group *group;
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
if (child == -1 || child > 0) {
|
|
|
|
// wait for any children or a specific child of this team to die
|
2004-10-18 19:16:00 +04:00
|
|
|
*_waitSem = team->dead_children.sem;
|
2004-10-14 19:42:56 +04:00
|
|
|
return get_team_death_entry(team, child, death, _freeDeath);
|
|
|
|
} else if (child < 0) {
|
|
|
|
// we wait for all children of the specified process group
|
|
|
|
group = get_process_group_locked(team, -child);
|
|
|
|
if (group == NULL)
|
|
|
|
return B_BAD_THREAD_ID;
|
|
|
|
} else {
|
|
|
|
// we wait for any children of the current team's group
|
|
|
|
group = team->group;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (team = group->teams; team; team = team->group_next) {
|
|
|
|
status = get_team_death_entry(team, -1, death, _freeDeath);
|
|
|
|
if (status == B_OK) {
|
|
|
|
atomic_add(&group->wait_for_any, -1);
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-18 19:16:00 +04:00
|
|
|
*_waitSem = group->dead_child_sem;
|
2004-10-14 19:42:56 +04:00
|
|
|
return B_WOULD_BLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-02 05:41:06 +04:00
|
|
|
/** This is the kernel backend for waitpid(). It is a bit more powerful when it comes
|
|
|
|
* to the reason why a thread has died than waitpid() can be.
|
|
|
|
*/
|
|
|
|
|
2004-09-15 19:45:37 +04:00
|
|
|
static thread_id
|
|
|
|
wait_for_child(thread_id child, uint32 flags, int32 *_reason, status_t *_returnCode)
|
2004-09-02 05:41:06 +04:00
|
|
|
{
|
2004-10-14 18:46:12 +04:00
|
|
|
struct team *team = thread_get_current_thread()->team;
|
2004-10-14 19:42:56 +04:00
|
|
|
struct death_entry death, *freeDeath = NULL;
|
2004-10-14 18:46:12 +04:00
|
|
|
status_t status = B_OK;
|
2004-10-18 19:16:00 +04:00
|
|
|
sem_id waitSem;
|
2004-10-14 18:46:12 +04:00
|
|
|
cpu_status state;
|
2004-09-02 05:41:06 +04:00
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
TRACE(("wait_for_child(child = %ld, flags = %ld)\n", child, flags));
|
2004-09-15 19:45:37 +04:00
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
if (child == 0 || child < -1) {
|
|
|
|
dprintf("wait_for_child() process group ID waiting not yet implemented!\n");
|
|
|
|
return EOPNOTSUPP;
|
2004-09-02 05:41:06 +04:00
|
|
|
}
|
|
|
|
|
2004-10-14 19:42:56 +04:00
|
|
|
if (child <= 0) {
|
2004-10-14 18:46:12 +04:00
|
|
|
// we need to make sure the death entries won't get deleted too soon
|
2004-10-14 19:42:56 +04:00
|
|
|
status = register_wait_for_any(team, child);
|
|
|
|
if (status != B_OK)
|
|
|
|
return status;
|
2004-10-14 18:46:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (child > 0) {
|
|
|
|
// wait for the specified child
|
|
|
|
|
|
|
|
if (thread_get_thread_struct(child) != NULL) {
|
|
|
|
// team is still running, so we would need to block
|
|
|
|
return B_WOULD_BLOCK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// see if there is any death entry for us already
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
2004-10-18 19:16:00 +04:00
|
|
|
status = get_death_entry(team, child, &death, &waitSem, &freeDeath);
|
2004-10-14 18:46:12 +04:00
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
2004-10-14 19:42:56 +04:00
|
|
|
// we got our death entry and can return to our caller
|
|
|
|
if (status == B_OK)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// there was no matching group/child we could wait for
|
2004-10-14 18:46:12 +04:00
|
|
|
if (status == B_BAD_THREAD_ID)
|
2004-10-18 19:16:00 +04:00
|
|
|
goto err;
|
2004-10-14 18:46:12 +04:00
|
|
|
|
2004-10-18 19:16:00 +04:00
|
|
|
if ((flags & WNOHANG) != 0) {
|
|
|
|
status = B_WOULD_BLOCK;
|
|
|
|
goto err;
|
|
|
|
}
|
2004-10-14 18:46:12 +04:00
|
|
|
|
2004-10-18 19:16:00 +04:00
|
|
|
status = acquire_sem(waitSem);
|
2004-10-14 18:46:12 +04:00
|
|
|
if (status == B_INTERRUPTED)
|
2004-10-18 19:16:00 +04:00
|
|
|
goto err;
|
2004-10-14 18:46:12 +04:00
|
|
|
}
|
|
|
|
|
2004-10-14 19:42:56 +04:00
|
|
|
free(freeDeath);
|
|
|
|
|
2004-10-18 19:16:00 +04:00
|
|
|
// when we got here, we have a valid death entry, and
|
|
|
|
// already got unregistered from the team or group
|
2004-10-14 18:46:12 +04:00
|
|
|
*_returnCode = death.status;
|
2006-05-05 19:33:34 +04:00
|
|
|
*_reason = (death.signal << 16) | death.reason;
|
2004-10-14 18:46:12 +04:00
|
|
|
|
|
|
|
return death.thread;
|
2004-10-18 19:16:00 +04:00
|
|
|
|
|
|
|
err:
|
|
|
|
unregister_wait_for_any(team, child);
|
|
|
|
return status;
|
2004-09-02 05:41:06 +04:00
|
|
|
}
|
2004-09-15 19:45:37 +04:00
|
|
|
|
|
|
|
|
2005-08-03 16:00:42 +04:00
|
|
|
/** Adds a hook to the team that is called as soon as this
|
|
|
|
* team goes away.
|
|
|
|
* This call might get public in the future.
|
|
|
|
*/
|
|
|
|
|
|
|
|
status_t
|
|
|
|
start_watching_team(team_id teamID, void (*hook)(team_id, void *), void *data)
|
|
|
|
{
|
|
|
|
struct team_watcher *watcher;
|
|
|
|
struct team *team;
|
|
|
|
cpu_status state;
|
|
|
|
|
|
|
|
if (hook == NULL || teamID < B_OK)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
watcher = malloc(sizeof(struct team_watcher));
|
|
|
|
if (watcher == NULL)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
2005-08-03 20:57:40 +04:00
|
|
|
watcher->hook = hook;
|
|
|
|
watcher->data = data;
|
|
|
|
|
2005-08-03 16:00:42 +04:00
|
|
|
// find team and add watcher
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
team = team_get_team_struct_locked(teamID);
|
|
|
|
if (team != NULL)
|
|
|
|
list_add_item(&team->watcher_list, watcher);
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
if (team == NULL) {
|
|
|
|
free(watcher);
|
|
|
|
return B_BAD_TEAM_ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
stop_watching_team(team_id teamID, void (*hook)(team_id, void *), void *data)
|
|
|
|
{
|
|
|
|
struct team_watcher *watcher = NULL;
|
|
|
|
struct team *team;
|
|
|
|
cpu_status state;
|
|
|
|
|
|
|
|
if (hook == NULL || teamID < B_OK)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
// find team and remove watcher (if present)
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
team = team_get_team_struct_locked(teamID);
|
|
|
|
if (team != NULL) {
|
|
|
|
// search for watcher
|
|
|
|
while ((watcher = list_get_next_item(&team->watcher_list, watcher)) != NULL) {
|
|
|
|
if (watcher->hook == hook && watcher->data == data) {
|
|
|
|
// got it!
|
|
|
|
list_remove_item(&team->watcher_list, watcher);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
if (watcher == NULL)
|
|
|
|
return B_ENTRY_NOT_FOUND;
|
|
|
|
|
|
|
|
free(watcher);
|
|
|
|
return B_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-15 19:45:37 +04:00
|
|
|
// #pragma mark -
|
|
|
|
// public team API
|
2004-09-02 05:41:06 +04:00
|
|
|
|
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
thread_id
|
|
|
|
load_image(int32 argCount, const char **args, const char **env)
|
|
|
|
{
|
|
|
|
int32 envCount = 0;
|
2004-10-15 05:52:07 +04:00
|
|
|
|
|
|
|
// count env variables
|
2004-10-14 22:07:04 +04:00
|
|
|
while (env && env[envCount] != NULL)
|
|
|
|
envCount++;
|
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
return load_image_etc(argCount, (char * const *)args, envCount,
|
|
|
|
(char * const *)env, B_NORMAL_PRIORITY, B_WAIT_TILL_LOADED, true);
|
2004-10-14 22:07:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-03 03:57:00 +03:00
|
|
|
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)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2005-03-09 04:43:56 +03:00
|
|
|
cpu_status state;
|
2003-01-07 12:40:59 +03:00
|
|
|
struct team *team;
|
2002-08-04 03:39:50 +04:00
|
|
|
// struct thread *t;
|
|
|
|
thread_id tid = -1;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
2003-01-07 12:40:59 +03:00
|
|
|
team = team_get_team_struct_locked(id);
|
2005-07-17 03:38:07 +04:00
|
|
|
if (team != NULL) {
|
|
|
|
if (team == kernel_team)
|
|
|
|
retval = B_NOT_ALLOWED;
|
|
|
|
else
|
|
|
|
tid = team->main_thread->id;
|
|
|
|
} else
|
2004-02-23 07:12:34 +03:00
|
|
|
retval = B_BAD_THREAD_ID;
|
2002-08-04 03:39:50 +04:00
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
2003-01-07 12:40:59 +03:00
|
|
|
|
|
|
|
if (retval < 0)
|
2002-08-04 03:39:50 +04:00
|
|
|
return retval;
|
|
|
|
|
|
|
|
// just kill the main thread in the team. The cleanup code there will
|
|
|
|
// take care of the team
|
2004-03-03 03:57:00 +03:00
|
|
|
return kill_thread(tid);
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-12 19:29:28 +03:00
|
|
|
/** Fills the team_info structure with information from the specified
|
|
|
|
* team.
|
2004-02-23 07:08:09 +03:00
|
|
|
* The team lock must be held when called.
|
2003-01-12 19:29:28 +03:00
|
|
|
*/
|
|
|
|
|
2002-12-03 17:17:53 +03:00
|
|
|
static status_t
|
|
|
|
fill_team_info(struct team *team, team_info *info, size_t size)
|
2002-08-04 03:39:50 +04:00
|
|
|
{
|
2002-12-03 17:17:53 +03:00
|
|
|
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;
|
2003-01-12 19:29:28 +03:00
|
|
|
info->image_count = count_images(team);
|
2002-12-03 17:17:53 +03:00
|
|
|
//info->area_count =
|
2005-03-11 02:19:25 +03:00
|
|
|
info->debugger_nub_thread = team->debug_info.nub_thread;
|
|
|
|
info->debugger_nub_port = team->debug_info.nub_port;
|
2002-12-03 17:17:53 +03:00
|
|
|
//info->uid =
|
|
|
|
//info->gid =
|
|
|
|
|
2006-05-30 04:21:22 +04:00
|
|
|
strlcpy(info->args, team->args, sizeof(info->args));
|
2002-12-03 17:17:53 +03:00
|
|
|
info->argc = 1;
|
|
|
|
|
|
|
|
return B_OK;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
_get_team_info(team_id id, team_info *info, size_t size)
|
|
|
|
{
|
2005-03-09 04:43:56 +03:00
|
|
|
cpu_status state;
|
2002-08-04 03:39:50 +04:00
|
|
|
status_t rc = B_OK;
|
|
|
|
struct team *team;
|
2004-02-23 07:08:09 +03:00
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
2004-02-23 07:08:09 +03:00
|
|
|
|
2004-11-26 19:37:41 +03:00
|
|
|
if (id == B_CURRENT_TEAM)
|
|
|
|
team = thread_get_current_thread()->team;
|
|
|
|
else
|
|
|
|
team = team_get_team_struct_locked(id);
|
|
|
|
|
|
|
|
if (team == NULL) {
|
2002-08-04 03:39:50 +04:00
|
|
|
rc = B_BAD_TEAM_ID;
|
|
|
|
goto err;
|
|
|
|
}
|
2002-12-03 17:17:53 +03:00
|
|
|
|
|
|
|
rc = fill_team_info(team, info, size);
|
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
err:
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
_get_next_team_info(int32 *cookie, team_info *info, size_t size)
|
|
|
|
{
|
2002-12-03 17:17:53 +03:00
|
|
|
status_t status = B_BAD_TEAM_ID;
|
2002-08-04 03:39:50 +04:00
|
|
|
struct team *team = NULL;
|
2002-12-03 17:17:53 +03:00
|
|
|
int32 slot = *cookie;
|
2005-03-09 04:43:56 +03:00
|
|
|
team_id lastTeamID;
|
2006-05-30 04:21:22 +04:00
|
|
|
cpu_status state;
|
2002-12-03 17:17:53 +03:00
|
|
|
|
2006-05-30 04:21:22 +04:00
|
|
|
if (slot < 1)
|
|
|
|
slot = 1;
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
2002-08-04 03:39:50 +04:00
|
|
|
GRAB_TEAM_LOCK();
|
2002-12-03 17:17:53 +03:00
|
|
|
|
2005-03-09 04:43:56 +03:00
|
|
|
lastTeamID = peek_next_thread_id();
|
|
|
|
if (slot >= lastTeamID)
|
2002-12-03 17:17:53 +03:00
|
|
|
goto err;
|
|
|
|
|
|
|
|
// get next valid team
|
2005-03-09 04:43:56 +03:00
|
|
|
while (slot < lastTeamID && !(team = team_get_team_struct_locked(slot)))
|
2002-08-04 03:39:50 +04:00
|
|
|
slot++;
|
2002-12-03 17:17:53 +03:00
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
if (team) {
|
2002-12-03 17:17:53 +03:00
|
|
|
status = fill_team_info(team, info, size);
|
|
|
|
*cookie = ++slot;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
2002-12-03 17:17:53 +03:00
|
|
|
|
2002-08-04 03:39:50 +04:00
|
|
|
err:
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
2002-12-03 17:17:53 +03:00
|
|
|
return status;
|
2002-08-04 03:39:50 +04:00
|
|
|
}
|
|
|
|
|
2002-08-05 09:26:52 +04:00
|
|
|
|
2004-11-26 00:20:17 +03:00
|
|
|
status_t
|
2004-11-26 17:58:01 +03:00
|
|
|
_get_team_usage_info(team_id id, int32 who, team_usage_info *info, size_t size)
|
2004-11-26 00:20:17 +03:00
|
|
|
{
|
2004-11-26 17:58:01 +03:00
|
|
|
bigtime_t kernelTime = 0, userTime = 0;
|
|
|
|
status_t status = B_OK;
|
|
|
|
struct team *team;
|
|
|
|
cpu_status state;
|
|
|
|
|
|
|
|
if (size != sizeof(team_usage_info)
|
2004-11-27 15:57:36 +03:00
|
|
|
|| (who != B_TEAM_USAGE_SELF && who != B_TEAM_USAGE_CHILDREN))
|
2004-11-26 00:20:17 +03:00
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
2004-11-26 17:58:01 +03:00
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
2004-11-26 00:20:17 +03:00
|
|
|
|
2004-11-26 19:34:32 +03:00
|
|
|
if (id == B_CURRENT_TEAM)
|
|
|
|
team = thread_get_current_thread()->team;
|
|
|
|
else
|
|
|
|
team = team_get_team_struct_locked(id);
|
|
|
|
|
2004-11-26 17:58:01 +03:00
|
|
|
if (team == NULL) {
|
|
|
|
status = B_BAD_TEAM_ID;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (who) {
|
2004-11-27 15:57:36 +03:00
|
|
|
case B_TEAM_USAGE_SELF:
|
2004-11-26 17:58:01 +03:00
|
|
|
{
|
|
|
|
struct thread *thread = team->thread_list;
|
|
|
|
|
|
|
|
for (; thread != NULL; thread = thread->team_next) {
|
|
|
|
kernelTime += thread->kernel_time;
|
|
|
|
userTime += thread->user_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
kernelTime += team->dead_threads_kernel_time;
|
|
|
|
userTime += team->dead_threads_user_time;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-11-27 15:57:36 +03:00
|
|
|
case B_TEAM_USAGE_CHILDREN:
|
2004-11-26 17:58:01 +03:00
|
|
|
{
|
|
|
|
struct team *child = team->children;
|
|
|
|
for (; child != NULL; child = child->siblings_next) {
|
|
|
|
struct thread *thread = team->thread_list;
|
|
|
|
|
|
|
|
for (; thread != NULL; thread = thread->team_next) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
if (status == B_OK) {
|
|
|
|
info->kernel_time = kernelTime;
|
|
|
|
info->user_time = userTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
2004-11-26 00:20:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
pid_t
|
|
|
|
getpid(void)
|
|
|
|
{
|
|
|
|
return thread_get_current_thread()->team->main_thread->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pid_t
|
|
|
|
getppid(void)
|
|
|
|
{
|
|
|
|
struct team *team = thread_get_current_thread()->team;
|
|
|
|
cpu_status state;
|
|
|
|
pid_t parent;
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
parent = team->parent->main_thread->id;
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
return parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pid_t
|
|
|
|
getpgid(pid_t process)
|
|
|
|
{
|
|
|
|
struct thread *thread;
|
|
|
|
pid_t result = -1;
|
|
|
|
cpu_status state;
|
|
|
|
|
|
|
|
if (process == 0)
|
|
|
|
process = thread_get_current_thread()->team->main_thread->id;
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
thread = thread_get_thread_struct_locked(process);
|
|
|
|
if (thread != NULL)
|
|
|
|
result = thread->team->group_id;
|
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
return thread != NULL ? result : B_BAD_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pid_t
|
|
|
|
getsid(pid_t process)
|
|
|
|
{
|
|
|
|
struct thread *thread;
|
|
|
|
pid_t result = -1;
|
|
|
|
cpu_status state;
|
|
|
|
|
|
|
|
if (process == 0)
|
|
|
|
process = thread_get_current_thread()->team->main_thread->id;
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
thread = thread_get_thread_struct_locked(process);
|
|
|
|
if (thread != NULL)
|
|
|
|
result = thread->team->session_id;
|
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
return thread != NULL ? result : B_BAD_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-12-03 17:17:53 +03:00
|
|
|
// #pragma mark -
|
2004-03-03 03:57:00 +03:00
|
|
|
// User syscalls
|
2002-12-03 17:17:53 +03:00
|
|
|
|
|
|
|
|
2004-09-15 19:45:37 +04:00
|
|
|
status_t
|
2004-10-07 19:34:17 +04:00
|
|
|
_user_exec(const char *userPath, int32 argCount, char * const *userArgs,
|
|
|
|
int32 envCount, char * const *userEnvironment)
|
2004-09-15 19:45:37 +04:00
|
|
|
{
|
2004-10-07 19:34:17 +04:00
|
|
|
char path[B_PATH_NAME_LENGTH];
|
2004-09-15 19:45:37 +04:00
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
if (argCount < 1)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userArgs)
|
|
|
|
|| !IS_USER_ADDRESS(userEnvironment)
|
2004-10-07 19:34:17 +04:00
|
|
|
|| user_strlcpy(path, userPath, sizeof(path)) < B_OK)
|
|
|
|
return B_BAD_ADDRESS;
|
2004-09-15 19:45:37 +04:00
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
return exec_team(path, argCount, userArgs, envCount, userEnvironment);
|
2004-10-07 19:34:17 +04:00
|
|
|
// this one only returns in case of error
|
2004-09-15 19:45:37 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
thread_id
|
|
|
|
_user_fork(void)
|
|
|
|
{
|
2004-10-10 21:30:42 +04:00
|
|
|
return fork_team();
|
2004-09-15 19:45:37 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
thread_id
|
|
|
|
_user_wait_for_child(thread_id child, uint32 flags, int32 *_userReason, status_t *_userReturnCode)
|
|
|
|
{
|
|
|
|
status_t returnCode;
|
|
|
|
int32 reason;
|
|
|
|
thread_id deadChild;
|
|
|
|
|
|
|
|
if ((_userReason != NULL && !IS_USER_ADDRESS(_userReason))
|
|
|
|
|| (_userReturnCode != NULL && !IS_USER_ADDRESS(_userReturnCode)))
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
|
|
|
deadChild = wait_for_child(child, flags, &reason, &returnCode);
|
|
|
|
|
|
|
|
if (deadChild >= B_OK) {
|
|
|
|
// copy result data on successful completion
|
|
|
|
if ((_userReason != NULL && user_memcpy(_userReason, &reason, sizeof(int32)) < B_OK)
|
|
|
|
|| (_userReturnCode != NULL && user_memcpy(_userReturnCode, &returnCode, sizeof(status_t)) < B_OK))
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return deadChild;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
pid_t
|
|
|
|
_user_process_info(pid_t process, int32 which)
|
|
|
|
{
|
|
|
|
// we only allow to return the parent of the current process
|
|
|
|
if (which == PARENT_ID
|
|
|
|
&& process != 0 && process != thread_get_current_thread()->team->main_thread->id)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
|
|
|
switch (which) {
|
|
|
|
case SESSION_ID:
|
|
|
|
return getsid(process);
|
|
|
|
case GROUP_ID:
|
|
|
|
return getpgid(process);
|
|
|
|
case PARENT_ID:
|
|
|
|
return getppid();
|
|
|
|
}
|
|
|
|
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pid_t
|
|
|
|
_user_setpgid(pid_t processID, pid_t groupID)
|
|
|
|
{
|
2005-04-05 16:39:27 +04:00
|
|
|
struct thread *thread = thread_get_current_thread();
|
|
|
|
struct team *currentTeam = thread->team;
|
2004-10-14 18:46:12 +04:00
|
|
|
struct process_group *group = NULL, *freeGroup = NULL;
|
|
|
|
struct team *team;
|
|
|
|
cpu_status state;
|
|
|
|
team_id teamID = -1;
|
|
|
|
status_t status = B_OK;
|
|
|
|
|
|
|
|
if (groupID < 0)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
2005-04-05 16:39:27 +04:00
|
|
|
if (processID == 0)
|
2004-10-14 18:46:12 +04:00
|
|
|
processID = currentTeam->main_thread->id;
|
2005-04-05 16:39:27 +04:00
|
|
|
|
|
|
|
if (processID == currentTeam->main_thread->id) {
|
|
|
|
// we set our own group
|
2004-10-14 18:46:12 +04:00
|
|
|
teamID = currentTeam->id;
|
|
|
|
|
|
|
|
// we must not change our process group ID if we're a group leader
|
2005-03-25 21:38:27 +03:00
|
|
|
if (is_process_group_leader(currentTeam)) {
|
|
|
|
// if the group ID was not specified, we just return the
|
|
|
|
// process ID as we already are a process group leader
|
2005-04-05 16:39:27 +04:00
|
|
|
if (groupID == 0 || groupID == processID)
|
2005-03-25 21:38:27 +03:00
|
|
|
return processID;
|
|
|
|
|
2004-10-14 18:46:12 +04:00
|
|
|
return B_NOT_ALLOWED;
|
2005-03-25 21:38:27 +03:00
|
|
|
}
|
2004-10-14 18:46:12 +04:00
|
|
|
|
|
|
|
status = B_OK;
|
|
|
|
} else {
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_THREAD_LOCK();
|
|
|
|
|
|
|
|
thread = thread_get_thread_struct_locked(processID);
|
|
|
|
|
|
|
|
// the thread must be the team's main thread, as that
|
|
|
|
// determines its process ID
|
|
|
|
if (thread != NULL && thread == thread->team->main_thread) {
|
|
|
|
// check if the thread is in a child team of the calling team and
|
|
|
|
// if it's already a process group leader and in the same session
|
|
|
|
if (thread->team->parent != currentTeam
|
|
|
|
|| is_process_group_leader(thread->team)
|
|
|
|
|| thread->team->session_id != currentTeam->session_id)
|
|
|
|
status = B_NOT_ALLOWED;
|
|
|
|
else
|
|
|
|
teamID = thread->team->id;
|
|
|
|
} else
|
|
|
|
status = B_BAD_THREAD_ID;
|
|
|
|
|
|
|
|
RELEASE_THREAD_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status != B_OK)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
// if the group ID is not specified, a new group should be created
|
|
|
|
if (groupID == 0)
|
|
|
|
groupID = processID;
|
|
|
|
|
|
|
|
if (groupID == processID) {
|
|
|
|
// We need to create a new process group for this team
|
|
|
|
group = create_process_group(groupID);
|
|
|
|
if (group == NULL)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
team = team_get_team_struct_locked(teamID);
|
|
|
|
if (team != NULL) {
|
|
|
|
if (processID == groupID) {
|
|
|
|
// we created a new process group, let us insert it into the team's session
|
|
|
|
insert_group_into_session(team->group->session, group);
|
|
|
|
remove_team_from_group(team, &freeGroup);
|
|
|
|
insert_team_into_group(group, team);
|
|
|
|
} else {
|
|
|
|
struct process_session *session = team->group->session;
|
|
|
|
struct process_group *group = NULL;
|
|
|
|
|
|
|
|
// check if this team can have the group ID; there must be one matching
|
|
|
|
// process ID in the team's session
|
|
|
|
|
|
|
|
while ((group = list_get_next_item(&session->groups, group)) != NULL) {
|
|
|
|
if (group->id == groupID)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (group) {
|
|
|
|
// we got a group, let's move the team there
|
|
|
|
remove_team_from_group(team, &freeGroup);
|
|
|
|
insert_team_into_group(group, team);
|
|
|
|
} else
|
|
|
|
status = B_NOT_ALLOWED;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
status = B_NOT_ALLOWED;
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
if (status != B_OK && group != NULL)
|
|
|
|
team_delete_process_group(group);
|
|
|
|
|
|
|
|
team_delete_process_group(freeGroup);
|
|
|
|
|
|
|
|
return status == B_OK ? groupID : status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pid_t
|
|
|
|
_user_setsid(void)
|
|
|
|
{
|
|
|
|
struct team *team = thread_get_current_thread()->team;
|
|
|
|
struct process_session *session;
|
|
|
|
struct process_group *group, *freeGroup = NULL;
|
|
|
|
cpu_status state;
|
|
|
|
bool failed = false;
|
|
|
|
|
|
|
|
// the team must not already be a process group leader
|
|
|
|
if (is_process_group_leader(team))
|
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
|
|
|
|
group = create_process_group(team->main_thread->id);
|
|
|
|
if (group == NULL)
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
|
|
|
|
session = create_process_session(group->id);
|
|
|
|
if (session == NULL) {
|
|
|
|
team_delete_process_group(group);
|
|
|
|
return B_NO_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = disable_interrupts();
|
|
|
|
GRAB_TEAM_LOCK();
|
|
|
|
|
|
|
|
// this may have changed since the check above
|
|
|
|
if (!is_process_group_leader(team)) {
|
|
|
|
remove_team_from_group(team, &freeGroup);
|
|
|
|
|
|
|
|
insert_group_into_session(session, group);
|
|
|
|
insert_team_into_group(group, team);
|
|
|
|
} else
|
|
|
|
failed = true;
|
|
|
|
|
|
|
|
RELEASE_TEAM_LOCK();
|
|
|
|
restore_interrupts(state);
|
|
|
|
|
|
|
|
if (failed) {
|
|
|
|
team_delete_process_group(group);
|
|
|
|
free(session);
|
|
|
|
return B_NOT_ALLOWED;
|
|
|
|
} else
|
|
|
|
team_delete_process_group(freeGroup);
|
|
|
|
|
|
|
|
return team->group_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-27 06:09:33 +03:00
|
|
|
status_t
|
2004-03-03 03:57:00 +03:00
|
|
|
_user_wait_for_team(team_id id, status_t *_userReturnCode)
|
2002-12-03 17:17:53 +03:00
|
|
|
{
|
2003-01-27 06:09:33 +03:00
|
|
|
status_t returnCode;
|
|
|
|
status_t status;
|
2002-12-03 17:17:53 +03:00
|
|
|
|
2004-09-15 19:45:37 +04:00
|
|
|
if (_userReturnCode != NULL && !IS_USER_ADDRESS(_userReturnCode))
|
2002-12-03 17:17:53 +03:00
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
2003-01-27 06:09:33 +03:00
|
|
|
status = wait_for_team(id, &returnCode);
|
2004-08-13 23:08:35 +04:00
|
|
|
if (status >= B_OK && _userReturnCode != NULL) {
|
2003-01-27 06:09:33 +03:00
|
|
|
if (user_memcpy(_userReturnCode, &returnCode, sizeof(returnCode)) < B_OK)
|
2002-12-03 17:17:53 +03:00
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
team_id
|
2004-10-14 22:07:04 +04:00
|
|
|
_user_load_image(int32 argCount, const char **userArgs, int32 envCount,
|
2005-03-12 18:13:51 +03:00
|
|
|
const char **userEnv, int32 priority, uint32 flags)
|
2002-12-03 17:17:53 +03:00
|
|
|
{
|
2004-11-02 00:38:56 +03:00
|
|
|
TRACE(("_user_load_image_etc: argc = %ld\n", argCount));
|
2002-12-03 17:17:53 +03:00
|
|
|
|
2004-10-14 22:07:04 +04:00
|
|
|
if (argCount < 1 || userArgs == NULL || userEnv == NULL)
|
|
|
|
return B_BAD_VALUE;
|
2002-12-03 17:17:53 +03:00
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
if (!IS_USER_ADDRESS(userArgs) || !IS_USER_ADDRESS(userEnv))
|
2004-10-14 22:07:04 +04:00
|
|
|
return B_BAD_ADDRESS;
|
2002-12-03 17:17:53 +03:00
|
|
|
|
2005-10-05 19:32:48 +04:00
|
|
|
return load_image_etc(argCount, (char * const *)userArgs,
|
|
|
|
envCount, (char * const *)userEnv, priority, flags, false);
|
2002-12-03 17:17:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-12-01 07:26:10 +03:00
|
|
|
void
|
|
|
|
_user_exit_team(status_t returnValue)
|
|
|
|
{
|
|
|
|
struct thread *thread = thread_get_current_thread();
|
|
|
|
|
|
|
|
thread->exit.status = returnValue;
|
|
|
|
thread->exit.reason = THREAD_RETURN_EXIT;
|
|
|
|
|
|
|
|
send_signal(thread->id, SIGKILL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-12-03 17:17:53 +03:00
|
|
|
status_t
|
2004-03-03 03:57:00 +03:00
|
|
|
_user_kill_team(team_id team)
|
|
|
|
{
|
|
|
|
return kill_team(team);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
|
|
|
_user_get_team_info(team_id id, team_info *userInfo)
|
2002-12-03 17:17:53 +03:00
|
|
|
{
|
2003-11-12 18:37:44 +03:00
|
|
|
status_t status;
|
|
|
|
team_info info;
|
|
|
|
|
2004-02-22 17:52:59 +03:00
|
|
|
if (!IS_USER_ADDRESS(userInfo))
|
2003-11-12 18:37:44 +03:00
|
|
|
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;
|
2002-12-03 17:17:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
status_t
|
2004-03-03 03:57:00 +03:00
|
|
|
_user_get_next_team_info(int32 *userCookie, team_info *userInfo)
|
2002-12-03 17:17:53 +03:00
|
|
|
{
|
2003-11-12 18:37:44 +03:00
|
|
|
status_t status;
|
|
|
|
team_info info;
|
|
|
|
int32 cookie;
|
|
|
|
|
2004-02-22 17:52:59 +03:00
|
|
|
if (!IS_USER_ADDRESS(userCookie)
|
|
|
|
|| !IS_USER_ADDRESS(userInfo)
|
2003-11-12 18:37:44 +03:00
|
|
|
|| 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;
|
2002-12-03 17:17:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-03 03:57:00 +03:00
|
|
|
team_id
|
|
|
|
_user_get_current_team(void)
|
|
|
|
{
|
|
|
|
return team_get_current_team_id();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-11-26 00:20:17 +03:00
|
|
|
status_t
|
|
|
|
_user_get_team_usage_info(team_id team, int32 who, team_usage_info *userInfo, size_t size)
|
|
|
|
{
|
|
|
|
team_usage_info info;
|
|
|
|
status_t status;
|
|
|
|
|
|
|
|
if (!IS_USER_ADDRESS(userInfo))
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
|
|
|
status = _get_team_usage_info(team, who, &info, size);
|
|
|
|
if (status != B_OK)
|
|
|
|
return status;
|
|
|
|
|
|
|
|
if (user_memcpy(userInfo, &info, size) < B_OK)
|
|
|
|
return B_BAD_ADDRESS;
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|