forked from Bananymous/banan-os
				
			LibC: Add support for shebangs
I implemented shebangs in userspace as I don't really see the benefit of doing it in kernel space. Only benefit I can think of is executing non readable scripts but I don't really see the benefit in that.
This commit is contained in:
		
							parent
							
								
									ab4dd6a268
								
							
						
					
					
						commit
						2ff3f88b4d
					
				|  | @ -1,9 +1,11 @@ | |||
| #include <BAN/Assert.h> | ||||
| #include <BAN/Debug.h> | ||||
| #include <BAN/StringView.h> | ||||
| 
 | ||||
| #include <kernel/Memory/Types.h> | ||||
| #include <kernel/Syscall.h> | ||||
| 
 | ||||
| #include <ctype.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <pthread.h> | ||||
|  | @ -165,8 +167,16 @@ int gethostname(char* name, size_t namelen) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int exec_impl(const char* pathname, char* const* argv, char* const* envp, bool do_path_resolution) | ||||
| static int exec_impl_shebang(FILE* fp, const char* pathname, char* const* argv, char* const* envp, int shebang_depth); | ||||
| 
 | ||||
| static int exec_impl(const char* pathname, char* const* argv, char* const* envp, bool do_path_resolution, int shebang_depth = 0) | ||||
| { | ||||
| 	if (shebang_depth > 100) | ||||
| 	{ | ||||
| 		errno = ELOOP; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	char buffer[PATH_MAX]; | ||||
| 
 | ||||
| 	if (do_path_resolution && strchr(pathname, '/') == nullptr) | ||||
|  | @ -211,9 +221,101 @@ static int exec_impl(const char* pathname, char* const* argv, char* const* envp, | |||
| 		pathname = resolved; | ||||
| 	} | ||||
| 
 | ||||
| 	if (access(pathname, X_OK) == -1) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (FILE* fp = fopen(pathname, "r")) | ||||
| 	{ | ||||
| 		char shebang[2]; | ||||
| 		if (fread(shebang, 1, 2, fp) == 2 && shebang[0] == '#' && shebang[1] == '!') | ||||
| 			return exec_impl_shebang(fp, pathname, argv, envp, shebang_depth); | ||||
| 		fclose(fp); | ||||
| 	} | ||||
| 
 | ||||
| 	return syscall(SYS_EXEC, pathname, argv, envp); | ||||
| } | ||||
| 
 | ||||
| static int exec_impl_shebang(FILE* fp, const char* pathname, char* const* argv, char* const* envp, int shebang_depth) | ||||
| { | ||||
| 	constexpr size_t buffer_len = PATH_MAX + 1 + ARG_MAX + 1; | ||||
| 	char* buffer = static_cast<char*>(malloc(buffer_len)); | ||||
| 	if (buffer == nullptr) | ||||
| 	{ | ||||
| 		fclose(fp); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (fgets(buffer, buffer_len, fp) == nullptr) | ||||
| 	{ | ||||
| 		free(buffer); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto sv_trim_whitespace = | ||||
| 		[](BAN::StringView sv) -> BAN::StringView | ||||
| 		{ | ||||
| 			while (!sv.empty() && isspace(sv.front())) | ||||
| 				sv = sv.substring(1); | ||||
| 			while (!sv.empty() && isspace(sv.back())) | ||||
| 				sv = sv.substring(0, sv.size() - 1); | ||||
| 			return sv; | ||||
| 		}; | ||||
| 
 | ||||
| 	BAN::StringView buffer_sv = buffer; | ||||
| 	if (buffer_sv.back() != '\n') | ||||
| 	{ | ||||
| 		free(buffer); | ||||
| 		errno = ENOEXEC; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	buffer_sv = sv_trim_whitespace(buffer_sv); | ||||
| 
 | ||||
| 	BAN::StringView interpreter, argument; | ||||
| 	if (auto space = buffer_sv.find([](char ch) -> bool { return isspace(ch); }); !space.has_value()) | ||||
| 		interpreter = buffer_sv; | ||||
| 	else | ||||
| 	{ | ||||
| 		interpreter = sv_trim_whitespace(buffer_sv.substring(0, space.value())); | ||||
| 		argument = sv_trim_whitespace(buffer_sv.substring(space.value())); | ||||
| 	} | ||||
| 
 | ||||
| 	if (interpreter.empty()) | ||||
| 	{ | ||||
| 		free(buffer); | ||||
| 		errno = ENOEXEC; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	// null terminate interpreter and argument
 | ||||
| 	const_cast<char*>(interpreter.data())[interpreter.size()] = '\0'; | ||||
| 	if (!argument.empty()) | ||||
| 		const_cast<char*>(argument.data())[argument.size()] = '\0'; | ||||
| 
 | ||||
| 	size_t old_argc = 0; | ||||
| 	while (argv[old_argc]) | ||||
| 		old_argc++; | ||||
| 
 | ||||
| 	const size_t extra_args = 1 + !argument.empty(); | ||||
| 	char** new_argv = static_cast<char**>(malloc((extra_args + old_argc + 1) * sizeof(char*)));; | ||||
| 	if (new_argv == nullptr) | ||||
| 	{ | ||||
| 		free(buffer); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	new_argv[0] = const_cast<char*>(pathname); | ||||
| 	if (!argument.empty()) | ||||
| 		new_argv[1] = const_cast<char*>(argument.data()); | ||||
| 	for (size_t i = 0; i < old_argc; i++) | ||||
| 		new_argv[i + extra_args] = argv[i]; | ||||
| 	new_argv[old_argc + extra_args] = nullptr; | ||||
| 
 | ||||
| 	exec_impl(interpreter.data(), new_argv, envp, true, shebang_depth + 1); | ||||
| 	free(new_argv); | ||||
| 	free(buffer); | ||||
| 	return -1; | ||||
| } | ||||
| 
 | ||||
| static int execl_impl(const char* pathname, const char* arg0, va_list ap, bool has_env, bool do_path_resolution) | ||||
| { | ||||
| 	int argc = 1; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue