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 { struct team_arg {
char *path; char *path;
char **args; uint32 arg_count;
char **envp; char **args;
unsigned int argc; uint32 env_count;
unsigned int envc; char **env;
}; };
// team list // 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 void delete_team_struct(struct team *p);
static int team_struct_compare(void *_p, const void *_key); static int team_struct_compare(void *_p, const void *_key);
static uint32 team_struct_hash(void *_p, const void *_key, uint32 range); static uint32 team_struct_hash(void *_p, const void *_key, uint32 range);
static void kfree_strings_array(char **strings, int strc); static void free_strings_array(char **strings, int32 count);
static int user_copy_strings_array(char **strings, int strc, char ***kstrings); static status_t user_copy_strings_array(char * const *strings, int32 count, char ***_strings);
static void _dump_team_info(struct team *p); static void _dump_team_info(struct team *p);
static int dump_team_info(int argc, char **argv); 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 /** Frees an array of strings in kernel space.
* Parameters *
* strings strings array * \param strings strings array
* strc number of strings in array * \param count number of strings in array
*/ */
static void 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 (i = 0; i < count; i++)
for (cnt = 0; cnt < strc; cnt++){ free(strings[i]);
free(strings[cnt]);
} free(strings);
free(strings);
}
} }
/** Copy an array of strings from user space to kernel space /** Copy an array of strings from user space to kernel space
* Parameters *
* strings userspace strings array * \param strings userspace strings array
* strc number of strings in array * \param count number of strings in array
* kstrings pointer to the kernel copy * \param kstrings pointer to the kernel copy
* Returns < 0 on error and **kstrings = NULL * \return \c B_OK on success, or an appropriate error code on
* failure.
*/ */
static int static status_t
user_copy_strings_array(char **userStrings, int strc, char ***kstrings) 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]; char buffer[SYS_THREAD_STRING_LENGTH_MAX];
char **strings;
*kstrings = NULL; status_t err;
int32 i = 0;
if (!IS_USER_ADDRESS(userStrings)) if (!IS_USER_ADDRESS(userStrings))
return B_BAD_ADDRESS; return B_BAD_ADDRESS;
lstrings = (char **)malloc((strc + 1) * sizeof(char *)); strings = (char **)malloc((count + 1) * sizeof(char *));
if (lstrings == NULL) if (strings == NULL)
return B_NO_MEMORY; 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 // scan all strings and copy to kernel space
for (cnt = 0; cnt < strc; cnt++) { for (; i < count; i++) {
err = user_memcpy(&source, &(userStrings[cnt]), sizeof(char *)); err = user_strlcpy(buffer, strings[i], SYS_THREAD_STRING_LENGTH_MAX);
if (err < 0) if (err < B_OK)
goto error; goto error;
if (!IS_USER_ADDRESS(source)) { strings[i] = strdup(buffer);
err = B_BAD_ADDRESS; if (strings[i] == NULL) {
goto error; err = B_NO_MEMORY;
}
err = user_strlcpy(buffer, source, SYS_THREAD_STRING_LENGTH_MAX);
if (err < 0)
goto error;
lstrings[cnt] = strdup(buffer);
if (lstrings[cnt] == NULL){
err = ENOMEM;
goto error; goto error;
} }
} }
lstrings[strc] = NULL; strings[count] = NULL;
*_strings = strings;
*kstrings = lstrings; return B_OK;
return B_NO_ERROR;
error: error:
kfree_strings_array(lstrings, cnt); free_strings_array(strings, i);
dprintf("user_copy_strings_array failed %d \n", err);
TRACE(("user_copy_strings_array failed %d \n", err));
return 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 static int32
team_create_team2(void *args) team_create_team2(void *args)
{ {
@@ -495,13 +518,12 @@ team_create_team2(void *args)
char *path; char *path;
addr entry; addr entry;
char ustack_name[128]; char ustack_name[128];
uint32 totalSize; uint32 sizeLeft;
char **uargs; char **uargs;
char **uenv; char **uenv;
char *udest; char *udest;
struct uspace_program_args *uspa; struct uspace_program_args *uspa;
unsigned int arg_cnt; uint32 argCount, envCount, i;
unsigned int env_cnt;
t = thread_get_current_thread(); t = thread_get_current_thread();
team = t->team; team = t->team;
@@ -513,15 +535,15 @@ team_create_team2(void *args)
// ToDo: make ENV_SIZE variable and put it on the heap? // ToDo: make ENV_SIZE variable and put it on the heap?
// ToDo: we could reserve the whole USER_STACK_REGION upfront... // ToDo: we could reserve the whole USER_STACK_REGION upfront...
totalSize = PAGE_ALIGN(MAIN_THREAD_STACK_SIZE + TLS_SIZE + ENV_SIZE + sizeLeft = PAGE_ALIGN(MAIN_THREAD_STACK_SIZE + TLS_SIZE + ENV_SIZE +
get_arguments_data_size(teamArgs->args, teamArgs->argc)); get_arguments_data_size(teamArgs->args, teamArgs->arg_count));
t->user_stack_base = USER_STACK_REGION + USER_STACK_REGION_SIZE - totalSize; t->user_stack_base = USER_STACK_REGION + USER_STACK_REGION_SIZE - sizeLeft;
t->user_stack_size = MAIN_THREAD_STACK_SIZE; t->user_stack_size = MAIN_THREAD_STACK_SIZE;
// the exact location at the end of the user stack region // the exact location at the end of the user stack region
sprintf(ustack_name, "%s_main_stack", team->name); sprintf(ustack_name, "%s_main_stack", team->name);
t->user_stack_region_id = create_area_etc(team, ustack_name, (void **)&t->user_stack_base, 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) { if (t->user_stack_region_id < 0) {
dprintf("team_create_team2: could not create default user stack region\n"); dprintf("team_create_team2: could not create default user stack region\n");
return t->user_stack_region_id; return t->user_stack_region_id;
@@ -530,42 +552,49 @@ team_create_team2(void *args)
// now that the TLS area is allocated, initialize TLS // now that the TLS area is allocated, initialize TLS
arch_thread_init_tls(t); 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); uspa = (struct uspace_program_args *)(t->user_stack_base + STACK_SIZE + TLS_SIZE + ENV_SIZE);
uargs = (char **)(uspa + 1); 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", TRACE(("addr: stack base = 0x%lx, uargs = %p, udest = %p, sizeLeft = %lu\n",
t->user_stack_base, uargs, udest, totalSize)); t->user_stack_base, uargs, udest, sizeLeft));
for (arg_cnt = 0; arg_cnt < teamArgs->argc; arg_cnt++) { for (i = 0; i < argCount; i++) {
uargs[arg_cnt] = udest; ssize_t length = user_strlcpy(udest, teamArgs->args[i], sizeLeft) + 1;
udest += user_strlcpy(udest, teamArgs->args[arg_cnt], totalSize) + 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; team->user_env_base = t->user_stack_base + t->user_stack_size + TLS_SIZE;
uenv = (char **)team->user_env_base; uenv = (char **)team->user_env_base;
udest = (char *)team->user_env_base + ENV_SIZE - 1; 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++) { for (i = 0; i < envCount; i++) {
size_t length = strlen(teamArgs->envp[env_cnt]) + 1; ssize_t length = user_strlcpy(udest, teamArgs->env[i], sizeLeft) + 1;
udest -= length;
uenv[env_cnt] = udest; uenv[i] = udest;
user_memcpy(udest, teamArgs->envp[env_cnt], length); 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_name, team->name, sizeof(uspa->program_name));
user_memcpy(uspa->program_path, teamArgs->path, sizeof(uspa->program_path)); user_memcpy(uspa->program_path, teamArgs->path, sizeof(uspa->program_path));
uspa->argc = arg_cnt; uspa->argc = argCount;
uspa->argv = uargs; uspa->argv = uargs;
uspa->envc = env_cnt; uspa->envc = envCount;
uspa->envp = uenv; uspa->envp = uenv;
kfree_strings_array(teamArgs->args, teamArgs->argc); free_strings_array(teamArgs->args, teamArgs->arg_count);
kfree_strings_array(teamArgs->envp, teamArgs->envc); free_strings_array(teamArgs->env, teamArgs->env_count);
path = teamArgs->path; path = teamArgs->path;
TRACE(("team_create_team2: loading elf binary '%s'\n", 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); err = elf_load_user_image("/boot/beos/system/lib/rld.so", team, 0, &entry);
// free the args // free the args
free(teamArgs->path); free_team_arg(teamArgs);
free(teamArgs);
if (err < 0) { if (err < 0) {
// Luckily, we don't have to clean up the mess we created - that's // 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_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; struct team *team, *parent;
const char *threadName; const char *threadName;
@@ -626,33 +654,24 @@ team_create_team(const char *path, const char *name, char **args, int argc, char
restore_interrupts(state); restore_interrupts(state);
// copy the args over // copy the args over
teamArgs = (struct team_arg *)malloc(sizeof(struct team_arg)); teamArgs = create_team_arg(path, argc, args, envCount, env);
if (teamArgs == NULL){ if (teamArgs == NULL) {
err = B_NO_MEMORY; err = B_NO_MEMORY;
goto err1; 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 // create a new io_context for this team
team->io_context = vfs_new_io_context(thread_get_current_thread()->team->io_context); team->io_context = vfs_new_io_context(thread_get_current_thread()->team->io_context);
if (!team->io_context) { if (!team->io_context) {
err = B_NO_MEMORY; err = B_NO_MEMORY;
goto err3; goto err2;
} }
// create an address space for this team // create an address space for this team
team->_aspace_id = vm_create_aspace(team->name, USER_BASE, USER_SIZE, false); team->_aspace_id = vm_create_aspace(team->name, USER_BASE, USER_SIZE, false);
if (team->_aspace_id < 0) { if (team->_aspace_id < 0) {
err = team->_aspace_id; err = team->_aspace_id;
goto err4; goto err3;
} }
team->aspace = vm_get_aspace_by_id(team->_aspace_id); 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); tid = spawn_kernel_thread_etc(team_create_team2, threadName, B_NORMAL_PRIORITY, teamArgs, team->id);
if (tid < 0) { if (tid < 0) {
err = tid; err = tid;
goto err5; goto err4;
} }
resume_thread(tid); resume_thread(tid);
return pid; return pid;
err5: err4:
vm_put_aspace(team->aspace); vm_put_aspace(team->aspace);
vm_delete_aspace(team->_aspace_id); vm_delete_aspace(team->_aspace_id);
err4:
vfs_free_io_context(team->io_context);
err3: err3:
free(teamArgs->path); vfs_free_io_context(team->io_context);
err2: err2:
free(teamArgs); free_team_arg(teamArgs);
err1: err1:
// remove the team structure from the team hash table and delete the team structure // remove the team structure from the team hash table and delete the team structure
state = disable_interrupts(); state = disable_interrupts();
@@ -694,11 +711,54 @@ err1:
RELEASE_TEAM_LOCK(); RELEASE_TEAM_LOCK();
restore_interrupts(state); restore_interrupts(state);
delete_team_struct(team); delete_team_struct(team);
//err:
return 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 /** 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. * 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 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; char path[B_PATH_NAME_LENGTH];
dprintf("exec(path = \"%s\", argc = %ld, envc = %ld) is not yet implemented\n", path, argc, envCount); status_t status;
for (i = 0; i < argc; i++) { char **args;
char argv[B_FILE_NAME_LENGTH]; char **env;
user_strlcpy(argv, userArgv[i], sizeof(argv));
dprintf(" [%ld] %s\n", i, argv); if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userArgs) || !IS_USER_ADDRESS(userEnvironment)
} || user_strlcpy(path, userPath, sizeof(path)) < B_OK)
for (i = 0; i < envCount; i++) { return B_BAD_ADDRESS;
char env[B_FILE_NAME_LENGTH];
user_strlcpy(env, userEnvironment[i], sizeof(env));
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); return team_create_team(path, name, args, argCount, env, envCount, priority);
error: error:
kfree_strings_array(args, argCount); free_strings_array(args, argCount);
kfree_strings_array(env, envCount); free_strings_array(env, envCount);
return rc; return rc;
} }