We now have a working exec_team() function (the backend of the exec() function family)!

Should be tested and verified more intensively, though.
Moved the creation, initialization, and deletion of the team_arg structure out of
team_create_team(); it's now also used by exec_team().
Renamed kfree_strings_array() to free_strings_array().
Renamed the fields of the team_arg structure to be more in sync with the names used
at other places.
Fixed the routine that copied the environment/arguments into the userland space.
Improved and fixed the user_copy_strings_array() function.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@9250 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2004-10-07 15:34:17 +00:00
parent 2895720c82
commit acfca1a3f5
+189 -117
View File
@@ -38,11 +38,11 @@ struct team_key {
};
struct team_arg {
char *path;
char **args;
char **envp;
unsigned int argc;
unsigned int envc;
char *path;
uint32 arg_count;
char **args;
uint32 env_count;
char **env;
};
// team list
@@ -56,8 +56,8 @@ static struct team *create_team_struct(const char *name, bool kernel);
static void delete_team_struct(struct team *p);
static int team_struct_compare(void *_p, const void *_key);
static uint32 team_struct_hash(void *_p, const void *_key, uint32 range);
static void kfree_strings_array(char **strings, int strc);
static int user_copy_strings_array(char **strings, int strc, char ***kstrings);
static void free_strings_array(char **strings, int32 count);
static status_t user_copy_strings_array(char * const *strings, int32 count, char ***_strings);
static void _dump_team_info(struct team *p);
static int dump_team_info(int argc, char **argv);
@@ -149,84 +149,77 @@ team_init(kernel_args *ka)
}
/** Frees an array of strings in kernel space
* Parameters
* strings strings array
* strc number of strings in array
/** Frees an array of strings in kernel space.
*
* \param strings strings array
* \param count number of strings in array
*/
static void
kfree_strings_array(char **strings, int strc)
free_strings_array(char **strings, int32 count)
{
int cnt = strc;
int32 i;
if (strings == NULL)
return;
if (strings != NULL) {
for (cnt = 0; cnt < strc; cnt++){
free(strings[cnt]);
}
free(strings);
}
for (i = 0; i < count; i++)
free(strings[i]);
free(strings);
}
/** Copy an array of strings from user space to kernel space
* Parameters
* strings userspace strings array
* strc number of strings in array
* kstrings pointer to the kernel copy
* Returns < 0 on error and **kstrings = NULL
*
* \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.
*/
static int
user_copy_strings_array(char **userStrings, int strc, char ***kstrings)
static status_t
user_copy_strings_array(char * const *userStrings, int32 count, char ***_strings)
{
char **lstrings;
int err;
int cnt;
char *source;
char buffer[SYS_THREAD_STRING_LENGTH_MAX];
*kstrings = NULL;
char **strings;
status_t err;
int32 i = 0;
if (!IS_USER_ADDRESS(userStrings))
return B_BAD_ADDRESS;
lstrings = (char **)malloc((strc + 1) * sizeof(char *));
if (lstrings == NULL)
strings = (char **)malloc((count + 1) * sizeof(char *));
if (strings == NULL)
return B_NO_MEMORY;
if ((err = user_memcpy(strings, userStrings, count * sizeof(char *))) < B_OK)
goto error;
// scan all strings and copy to kernel space
for (cnt = 0; cnt < strc; cnt++) {
err = user_memcpy(&source, &(userStrings[cnt]), sizeof(char *));
if (err < 0)
for (; i < count; i++) {
err = user_strlcpy(buffer, strings[i], SYS_THREAD_STRING_LENGTH_MAX);
if (err < B_OK)
goto error;
if (!IS_USER_ADDRESS(source)) {
err = B_BAD_ADDRESS;
goto error;
}
err = user_strlcpy(buffer, source, SYS_THREAD_STRING_LENGTH_MAX);
if (err < 0)
goto error;
lstrings[cnt] = strdup(buffer);
if (lstrings[cnt] == NULL){
err = ENOMEM;
strings[i] = strdup(buffer);
if (strings[i] == NULL) {
err = B_NO_MEMORY;
goto error;
}
}
lstrings[strc] = NULL;
strings[count] = NULL;
*_strings = strings;
*kstrings = lstrings;
return B_NO_ERROR;
return B_OK;
error:
kfree_strings_array(lstrings, cnt);
dprintf("user_copy_strings_array failed %d \n", err);
free_strings_array(strings, i);
TRACE(("user_copy_strings_array failed %d \n", err));
return err;
}
@@ -485,6 +478,36 @@ get_arguments_data_size(char **args, int argc)
}
static void
free_team_arg(struct team_arg *teamArg)
{
free(teamArg->path);
free(teamArg);
}
static struct team_arg *
create_team_arg(const char *path, int32 argc, char **args, int32 envCount, char **env)
{
struct team_arg *teamArg = (struct team_arg *)malloc(sizeof(struct team_arg));
if (teamArg == NULL)
return NULL;
teamArg->path = strdup(path);
if (teamArg->path == NULL) {
free(teamArg);
return NULL;
}
teamArg->arg_count = argc;
teamArg->args = args;
teamArg->env_count = envCount;
teamArg->env = env;
return teamArg;
}
static int32
team_create_team2(void *args)
{
@@ -495,13 +518,12 @@ team_create_team2(void *args)
char *path;
addr entry;
char ustack_name[128];
uint32 totalSize;
uint32 sizeLeft;
char **uargs;
char **uenv;
char *udest;
struct uspace_program_args *uspa;
unsigned int arg_cnt;
unsigned int env_cnt;
uint32 argCount, envCount, i;
t = thread_get_current_thread();
team = t->team;
@@ -513,15 +535,15 @@ team_create_team2(void *args)
// ToDo: make ENV_SIZE variable and put it on the heap?
// ToDo: we could reserve the whole USER_STACK_REGION upfront...
totalSize = PAGE_ALIGN(MAIN_THREAD_STACK_SIZE + TLS_SIZE + ENV_SIZE +
get_arguments_data_size(teamArgs->args, teamArgs->argc));
t->user_stack_base = USER_STACK_REGION + USER_STACK_REGION_SIZE - totalSize;
sizeLeft = PAGE_ALIGN(MAIN_THREAD_STACK_SIZE + TLS_SIZE + ENV_SIZE +
get_arguments_data_size(teamArgs->args, teamArgs->arg_count));
t->user_stack_base = USER_STACK_REGION + USER_STACK_REGION_SIZE - sizeLeft;
t->user_stack_size = MAIN_THREAD_STACK_SIZE;
// the exact location at the end of the user stack region
sprintf(ustack_name, "%s_main_stack", team->name);
t->user_stack_region_id = create_area_etc(team, ustack_name, (void **)&t->user_stack_base,
B_EXACT_ADDRESS, totalSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
B_EXACT_ADDRESS, sizeLeft, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
if (t->user_stack_region_id < 0) {
dprintf("team_create_team2: could not create default user stack region\n");
return t->user_stack_region_id;
@@ -530,42 +552,49 @@ team_create_team2(void *args)
// now that the TLS area is allocated, initialize TLS
arch_thread_init_tls(t);
argCount = teamArgs->arg_count;
envCount = teamArgs->env_count;
uspa = (struct uspace_program_args *)(t->user_stack_base + STACK_SIZE + TLS_SIZE + ENV_SIZE);
uargs = (char **)(uspa + 1);
udest = (char *)(uargs + teamArgs->argc + 1);
udest = (char *)(uargs + argCount + 1);
TRACE(("addr: stack base = 0x%lx, uargs = %p, udest = %p, totalSize = %lu\n",
t->user_stack_base, uargs, udest, totalSize));
TRACE(("addr: stack base = 0x%lx, uargs = %p, udest = %p, sizeLeft = %lu\n",
t->user_stack_base, uargs, udest, sizeLeft));
for (arg_cnt = 0; arg_cnt < teamArgs->argc; arg_cnt++) {
uargs[arg_cnt] = udest;
udest += user_strlcpy(udest, teamArgs->args[arg_cnt], totalSize) + 1;
for (i = 0; i < argCount; i++) {
ssize_t length = user_strlcpy(udest, teamArgs->args[i], sizeLeft) + 1;
uargs[i] = udest;
udest += length;
sizeLeft -= length;
}
uargs[arg_cnt] = NULL;
uargs[argCount] = NULL;
team->user_env_base = t->user_stack_base + t->user_stack_size + TLS_SIZE;
uenv = (char **)team->user_env_base;
udest = (char *)team->user_env_base + ENV_SIZE - 1;
TRACE(("team_create_team2: envc: %d, envp: 0x%p\n", teamArgs->envc, (void *)teamArgs->envp));
TRACE(("team_create_team2: envc: %d, envp: 0x%p\n", teamArgs->env_count, (void *)teamArgs->env));
for (env_cnt = 0; env_cnt < teamArgs->envc; env_cnt++) {
size_t length = strlen(teamArgs->envp[env_cnt]) + 1;
udest -= length;
uenv[env_cnt] = udest;
user_memcpy(udest, teamArgs->envp[env_cnt], length);
for (i = 0; i < envCount; i++) {
ssize_t length = user_strlcpy(udest, teamArgs->env[i], sizeLeft) + 1;
uenv[i] = udest;
udest += length;
sizeLeft -= length;
}
uenv[env_cnt] = NULL;
uenv[envCount] = NULL;
user_memcpy(uspa->program_name, team->name, sizeof(uspa->program_name));
user_memcpy(uspa->program_path, teamArgs->path, sizeof(uspa->program_path));
uspa->argc = arg_cnt;
uspa->argc = argCount;
uspa->argv = uargs;
uspa->envc = env_cnt;
uspa->envc = envCount;
uspa->envp = uenv;
kfree_strings_array(teamArgs->args, teamArgs->argc);
kfree_strings_array(teamArgs->envp, teamArgs->envc);
free_strings_array(teamArgs->args, teamArgs->arg_count);
free_strings_array(teamArgs->env, teamArgs->env_count);
path = teamArgs->path;
TRACE(("team_create_team2: loading elf binary '%s'\n", path));
@@ -574,8 +603,7 @@ team_create_team2(void *args)
err = elf_load_user_image("/boot/beos/system/lib/rld.so", team, 0, &entry);
// free the args
free(teamArgs->path);
free(teamArgs);
free_team_arg(teamArgs);
if (err < 0) {
// Luckily, we don't have to clean up the mess we created - that's
@@ -596,7 +624,7 @@ team_create_team2(void *args)
team_id
team_create_team(const char *path, const char *name, char **args, int argc, char **envp, int envc, int priority)
team_create_team(const char *path, const char *name, char **args, int argc, char **env, int envCount, int priority)
{
struct team *team, *parent;
const char *threadName;
@@ -626,33 +654,24 @@ team_create_team(const char *path, const char *name, char **args, int argc, char
restore_interrupts(state);
// copy the args over
teamArgs = (struct team_arg *)malloc(sizeof(struct team_arg));
if (teamArgs == NULL){
teamArgs = create_team_arg(path, argc, args, envCount, env);
if (teamArgs == NULL) {
err = B_NO_MEMORY;
goto err1;
}
teamArgs->path = strdup(path);
if (teamArgs->path == NULL){
err = B_NO_MEMORY;
goto err2;
}
teamArgs->argc = argc;
teamArgs->args = args;
teamArgs->envp = envp;
teamArgs->envc = envc;
// create a new io_context for this team
team->io_context = vfs_new_io_context(thread_get_current_thread()->team->io_context);
if (!team->io_context) {
err = B_NO_MEMORY;
goto err3;
goto err2;
}
// create an address space for this team
team->_aspace_id = vm_create_aspace(team->name, USER_BASE, USER_SIZE, false);
if (team->_aspace_id < 0) {
err = team->_aspace_id;
goto err4;
goto err3;
}
team->aspace = vm_get_aspace_by_id(team->_aspace_id);
@@ -667,22 +686,20 @@ team_create_team(const char *path, const char *name, char **args, int argc, char
tid = spawn_kernel_thread_etc(team_create_team2, threadName, B_NORMAL_PRIORITY, teamArgs, team->id);
if (tid < 0) {
err = tid;
goto err5;
goto err4;
}
resume_thread(tid);
return pid;
err5:
err4:
vm_put_aspace(team->aspace);
vm_delete_aspace(team->_aspace_id);
err4:
vfs_free_io_context(team->io_context);
err3:
free(teamArgs->path);
vfs_free_io_context(team->io_context);
err2:
free(teamArgs);
free_team_arg(teamArgs);
err1:
// remove the team structure from the team hash table and delete the team structure
state = disable_interrupts();
@@ -694,11 +711,54 @@ err1:
RELEASE_TEAM_LOCK();
restore_interrupts(state);
delete_team_struct(team);
//err:
return err;
}
static status_t
exec_team(const char *path, int32 argCount, char **args, int32 envCount, char **env)
{
struct team *team = thread_get_current_thread()->team;
struct team_arg *teamArgs;
status_t status;
TRACE(("exec_team(path = \"%s\", argc = %ld, envCount = %ld\n", path, argCount, envCount));
// 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?
if (team->main_thread != thread_get_current_thread()
|| team->main_thread != team->thread_list
|| team->main_thread->team_next != NULL)
return B_NOT_ALLOWED;
// ToDo: maybe we should make sure upfront that the target path is an app?
teamArgs = create_team_arg(path, argCount, args, envCount, env);
if (teamArgs == NULL)
return B_NO_MEMORY;
// ToDo: remove team resources if there are any left
vm_delete_areas(team->aspace);
delete_owned_ports(team->id);
sem_delete_owned_sems(team->id);
remove_images(team);
vfs_exec_io_context(team->io_context);
status = team_create_team2(teamArgs);
// this one usually doesn't return...
// sorry, we have to kill us, there is no way out anymore (without any areas left and all that)
exit_thread(status);
}
/** 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.
*/
@@ -999,23 +1059,35 @@ sys_getenv(const char *name, char **value)
status_t
_user_exec(const char *path, int32 argc, char * const *userArgv, int32 envCount, char * const *userEnvironment)
_user_exec(const char *userPath, int32 argCount, char * const *userArgs,
int32 envCount, char * const *userEnvironment)
{
int32 i;
dprintf("exec(path = \"%s\", argc = %ld, envc = %ld) is not yet implemented\n", path, argc, envCount);
for (i = 0; i < argc; i++) {
char argv[B_FILE_NAME_LENGTH];
user_strlcpy(argv, userArgv[i], sizeof(argv));
char path[B_PATH_NAME_LENGTH];
status_t status;
char **args;
char **env;
dprintf(" [%ld] %s\n", i, argv);
}
for (i = 0; i < envCount; i++) {
char env[B_FILE_NAME_LENGTH];
user_strlcpy(env, userEnvironment[i], sizeof(env));
if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userArgs) || !IS_USER_ADDRESS(userEnvironment)
|| user_strlcpy(path, userPath, sizeof(path)) < B_OK)
return B_BAD_ADDRESS;
dprintf(" (%ld) %s\n", i, env);
status = user_copy_strings_array(userArgs, argCount, &args);
if (status < B_OK)
return status;
status = user_copy_strings_array(userEnvironment, envCount, &env);
if (status < B_OK) {
free_strings_array(args, argCount);
return status;
}
return B_ERROR;
status = exec_team(path, argCount, args, envCount, env);
// this one only returns in case of error
free_strings_array(args, argCount);
free_strings_array(env, envCount);
return status;
}
@@ -1106,8 +1178,8 @@ _user_create_team(const char *userPath, const char *userName, char **userArgs,
return team_create_team(path, name, args, argCount, env, envCount, priority);
error:
kfree_strings_array(args, argCount);
kfree_strings_array(env, envCount);
free_strings_array(args, argCount);
free_strings_array(env, envCount);
return rc;
}