diff --git a/runtime/include/module.h b/runtime/include/module.h index 34267ee..1ddb9b9 100644 --- a/runtime/include/module.h +++ b/runtime/include/module.h @@ -8,7 +8,7 @@ struct module { char name[MOD_NAME_MAX]; char path[MOD_PATH_MAX]; - void * dl_handle; + void * dynamic_library_handle; // Handle to the *.so of the serverless function mod_main_fn_t entry_fn; mod_glb_fn_t glb_init_fn; mod_mem_fn_t mem_init_fn; @@ -24,8 +24,10 @@ struct module { u32 reference_count; // ref count how many instances exist here. - struct sockaddr_in srvaddr; - int srvsock, srvport; + struct sockaddr_in socket_address; + int socket_descriptor; + int port; + // unfortunately, using UV for accepting connections is not great! // on_connection, to create a new accepted connection, will have to // init a tcp handle, which requires a uvloop. cannot use main as @@ -51,7 +53,7 @@ struct module *module_alloc(char *mod_name, char *mod_path, i32 argument_count, // frees only if reference_count == 0 void module_free(struct module *module); struct module *module_find_by_name(char *name); -struct module *module_find_by_sock(int sock); +struct module *module_find_by_socket_descriptor(int sock); static inline void @@ -73,14 +75,23 @@ module_http_info( strcpy(module->response_content_type, response_content_type); } +/** + * Validate module, defined as having a non-NULL dynamical library handle and entry function pointer + * @param module + * @return 1 if valid. 0 if invalid + **/ static inline int module_is_valid(struct module *module) { - if (module && module->dl_handle && module->entry_fn) return 1; + if (module && module->dynamic_library_handle && module->entry_fn) return 1; return 0; } +/** + * Invoke a module's glb_init_fn + * @param module + **/ static inline void module_globals_init(struct module *module) { @@ -88,6 +99,10 @@ module_globals_init(struct module *module) module->glb_init_fn(); } +/** + * Invoke a module's tbl_init_fn + * @param module + **/ static inline void module_table_init(struct module *module) { @@ -95,6 +110,10 @@ module_table_init(struct module *module) module->tbl_init_fn(); } +/** + * Invoke a module's libc_init_fn + * @param module + **/ static inline void module_libc_init(struct module *module, i32 env, i32 args) { @@ -102,6 +121,10 @@ module_libc_init(struct module *module, i32 env, i32 args) module->libc_init_fn(env, args); } +/** + * Invoke a module's mem_init_fn + * @param module + **/ static inline void module_memory_init(struct module *module) { @@ -109,27 +132,45 @@ module_memory_init(struct module *module) module->mem_init_fn(); } +/** + * Invoke a module's entry function, forwarding on argc and argv + * @param module + * @param argc standard UNIX count of arguments + * @param argv standard UNIX vector of arguments + **/ static inline i32 module_entry(struct module *module, i32 argc, i32 argv) { return module->entry_fn(argc, argv); } -// instantiate this module. +/** + * Increment a modules reference count + * @param module + **/ static inline void module_acquire(struct module *module) { - // FIXME: atomic. + // TODO: atomic. module->reference_count++; } +/** + * Decrement a modules reference count + * @param module + **/ static inline void module_release(struct module *module) { - // FIXME: atomic. + // TODO: atomic. module->reference_count--; } +/** + * Get a module's argument count + * @param module + * @returns the number of arguments + **/ static inline i32 module_argument_count(struct module *module) { diff --git a/runtime/include/util.h b/runtime/include/util.h index 7a82ab8..73ac71e 100644 --- a/runtime/include/util.h +++ b/runtime/include/util.h @@ -5,9 +5,6 @@ #include /* perhaps move it to module.h or sandbox.h? */ -struct sandbox *util_parse_sandbox_string_custom(struct module *m, char *str, const struct sockaddr *addr); -struct sandbox *util_parse_sandbox_string_json(struct module *m, char *str, const struct sockaddr *addr); int util_parse_modules_file_json(char *filename); -int util_parse_modules_file_custom(char *filename); #endif /* SFRT_UTIL_H */ diff --git a/runtime/src/module.c b/runtime/src/module.c index 4b6b451..cc04732 100644 --- a/runtime/src/module.c +++ b/runtime/src/module.c @@ -6,9 +6,15 @@ #include #include +// In-memory representation of all active modules static struct module *__mod_db[MOD_MAX] = { NULL }; static int __mod_free_off = 0; +/** + * Given a name, find the associated module + * @param name + * @return module or NULL if no match found + **/ struct module * module_find_by_name(char *name) { @@ -20,21 +26,31 @@ module_find_by_name(char *name) return NULL; } +/** + * Given a socket_descriptor, find the associated module + * @param socket_descriptor + * @return module or NULL if no match found + **/ struct module * -module_find_by_sock(int sock) +module_find_by_socket_descriptor(int socket_descriptor) { int f = __mod_free_off; for (int i = 0; i < f; i++) { assert(__mod_db[i]); - if (__mod_db[i]->srvsock == sock) return __mod_db[i]; + if (__mod_db[i]->socket_descriptor == socket_descriptor) return __mod_db[i]; } return NULL; } +/** + * Adds a module to the in-memory module DB + * @param module module to add + * @return 0 on success. Aborts program on failure + **/ static inline int module_add(struct module *module) { - assert(module->srvsock == -1); + assert(module->socket_descriptor == -1); int f = __sync_fetch_and_add(&__mod_free_off, 1); assert(f < MOD_MAX); @@ -43,106 +59,153 @@ module_add(struct module *module) return 0; } +/** + * Start the module as a server listening at module->port + * @param module + **/ static inline void module_server_init(struct module *module) { - int fd = socket(AF_INET, SOCK_STREAM, 0); - assert(fd > 0); - module->srvaddr.sin_family = AF_INET; - module->srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); - module->srvaddr.sin_port = htons((unsigned short)module->srvport); + // Allocate a new socket + int socket_descriptor = socket(AF_INET, SOCK_STREAM, 0); + assert(socket_descriptor > 0); + // Configure socket address as [all addresses]:[module->port] + module->socket_address.sin_family = AF_INET; + module->socket_address.sin_addr.s_addr = htonl(INADDR_ANY); + module->socket_address.sin_port = htons((unsigned short)module->port); + + // Configure the socket to allow multiple sockets to bind to the same host and port int optval = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); + setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); optval = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - if (bind(fd, (struct sockaddr *)&module->srvaddr, sizeof(module->srvaddr)) < 0) { + setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + // Bind to the interface + if (bind(socket_descriptor, (struct sockaddr *)&module->socket_address, sizeof(module->socket_address)) < 0) { perror("bind"); assert(0); } - if (listen(fd, MOD_BACKLOG) < 0) assert(0); - module->srvsock = fd; + // Listen to the interface? Check that it is live? + if (listen(socket_descriptor, MOD_BACKLOG) < 0) assert(0); + + module->socket_descriptor = socket_descriptor; + + // Register the socket descriptor with our global epoll instance to monitor for incoming HTTP requests struct epoll_event accept_evt; accept_evt.data.ptr = (void *)module; accept_evt.events = EPOLLIN; - - if (epoll_ctl(epoll_file_descriptor, EPOLL_CTL_ADD, module->srvsock, &accept_evt) < 0) assert(0); + if (epoll_ctl(epoll_file_descriptor, EPOLL_CTL_ADD, module->socket_descriptor, &accept_evt) < 0) assert(0); } +/** + * Module Mega Setup Function + * Creates a new module, invokes tbl_init_fn to initialize the indirect table, adds it to the module DB, and starts listening for HTTP Requests + * + * @param name + * @param path + * @param argument_count + * @param stack_size + * @param max_memory + * @param timeout + * @param port + * @param request_size + * @returns A new module or NULL in case of failure + **/ struct module * -module_alloc(char *module_name, char *module_path, i32 argument_count, u32 stack_size, u32 max_memory, u32 timeout, int port, int request_size, +module_alloc(char *name, char *path, i32 argument_count, u32 stack_size, u32 max_memory, u32 timeout, int port, int request_size, int response_size) { struct module *module = (struct module *)malloc(sizeof(struct module)); if (!module) return NULL; memset(module, 0, sizeof(struct module)); - module->dl_handle = dlopen(module_path, RTLD_LAZY | RTLD_DEEPBIND); - if (module->dl_handle == NULL) goto dl_open_error; - module->entry_fn = (mod_main_fn_t)dlsym(module->dl_handle, MOD_MAIN_FN); + // Load the dynamic library *.so file with lazy function call binding and deep binding + module->dynamic_library_handle = dlopen(path, RTLD_LAZY | RTLD_DEEPBIND); + if (module->dynamic_library_handle == NULL) goto dl_open_error; + + // Resolve the symbols in the dynamic library *.so file + module->entry_fn = (mod_main_fn_t)dlsym(module->dynamic_library_handle, MOD_MAIN_FN); if (module->entry_fn == NULL) goto dl_error; - module->glb_init_fn = (mod_glb_fn_t)dlsym(module->dl_handle, MOD_GLB_FN); + module->glb_init_fn = (mod_glb_fn_t)dlsym(module->dynamic_library_handle, MOD_GLB_FN); if (module->glb_init_fn == NULL) goto dl_error; - module->mem_init_fn = (mod_mem_fn_t)dlsym(module->dl_handle, MOD_MEM_FN); + module->mem_init_fn = (mod_mem_fn_t)dlsym(module->dynamic_library_handle, MOD_MEM_FN); if (module->mem_init_fn == NULL) goto dl_error; - module->tbl_init_fn = (mod_tbl_fn_t)dlsym(module->dl_handle, MOD_TBL_FN); + module->tbl_init_fn = (mod_tbl_fn_t)dlsym(module->dynamic_library_handle, MOD_TBL_FN); if (module->tbl_init_fn == NULL) goto dl_error; - module->libc_init_fn = (mod_libc_fn_t)dlsym(module->dl_handle, MOD_LIBC_FN); + module->libc_init_fn = (mod_libc_fn_t)dlsym(module->dynamic_library_handle, MOD_LIBC_FN); if (module->libc_init_fn == NULL) goto dl_error; - strncpy(module->name, module_name, MOD_NAME_MAX); - strncpy(module->path, module_path, MOD_PATH_MAX); + // Set fields in the module struct + strncpy(module->name, name, MOD_NAME_MAX); + strncpy(module->path, path, MOD_PATH_MAX); module->argument_count = argument_count; module->stack_size = round_up_to_page(stack_size == 0 ? WASM_STACK_SIZE : stack_size); module->max_memory = max_memory == 0 ? ((u64)WASM_PAGE_SIZE * WASM_MAX_PAGES) : max_memory; module->timeout = timeout; - module->srvsock = -1; - module->srvport = port; + module->socket_descriptor = -1; + module->port = port; if (request_size == 0) request_size = MOD_REQ_RESP_DEFAULT; if (response_size == 0) response_size = MOD_REQ_RESP_DEFAULT; module->max_request_size = request_size; module->max_response_size = response_size; module->max_request_or_response_size = round_up_to_page(request_size > response_size ? request_size : response_size); + // module_indirect_table is a thread-local struct struct indirect_table_entry *cache_tbl = module_indirect_table; - // assumption: modules are created before enabling preemption and before running runtime-sandboxing threads.. - // if this isn't a good assumption, just let the first invocation do table init..!! + + // assumption: All modules are created at program start before we enable preemption or enable the execution of any worker threads + // We are checking that thread-local module_indirect_table is NULL to prove that we aren't yet preempting + // If we want to be able to do this later, we can possibly defer module_table_init until the first invocation assert(cache_tbl == NULL); + + // TODO: determine why we have to set the module_indirect_table state before calling table init and then restore the existing value + // What is the relationship between these things? module_indirect_table = module->indirect_table; module_table_init(module); module_indirect_table = cache_tbl; + + // Add the module to the in-memory module DB module_add(module); + + // Start listening for requests module_server_init(module); return module; dl_error: - dlclose(module->dl_handle); + dlclose(module->dynamic_library_handle); dl_open_error: free(module); debuglog("%s\n", dlerror()); - return NULL; } +/** + * Module Mega Teardown Function + * Closes the socket and dynamic library, and then frees the module + * @param module - the module to teardown + **/ void module_free(struct module *module) { if (module == NULL) return; - if (module->dl_handle == NULL) return; + if (module->dynamic_library_handle == NULL) return; // Do not free if we still have oustanding references if (module->reference_count) return; - close(module->srvsock); - dlclose(module->dl_handle); + // TODO: What about the module database? Do we need to do any cleanup there? + + close(module->socket_descriptor); + dlclose(module->dynamic_library_handle); free(module); } diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index 7549071..00e401c 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -322,7 +322,7 @@ runtime_accept_thdfn(void *d) socklen_t client_len = sizeof(client); struct module * m = (struct module *)epoll_events[i].data.ptr; assert(m); - int es = m->srvsock; + int es = m->socket_descriptor; int s = accept(es, (struct sockaddr *)&client, &client_len); if (s < 0) { perror("accept"); diff --git a/runtime/tools/httpclient/client.c b/runtime/tools/httpclient/client.c index 2eeb5e4..230a935 100644 --- a/runtime/tools/httpclient/client.c +++ b/runtime/tools/httpclient/client.c @@ -174,16 +174,16 @@ connect_n_send(void) return -1; } - struct sockaddr_in srvaddr; - srvaddr.sin_family = AF_INET; - srvaddr.sin_port = htons(SERVER_PORT); - if (inet_aton(SERVER_ADDR, &srvaddr.sin_addr) == 0) { + struct sockaddr_in socket_address; + socket_address.sin_family = AF_INET; + socket_address.sin_port = htons(SERVER_PORT); + if (inet_aton(SERVER_ADDR, &socket_address.sin_addr) == 0) { perror("inet_addr"); close(sock); return -1; } - if (connect(sock, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0) { + if (connect(sock, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) { perror("connect"); close(sock); return -1;