diff options
Diffstat (limited to 'init')
-rw-r--r-- | init/init.c | 210 | ||||
-rw-r--r-- | init/log.c | 209 | ||||
-rw-r--r-- | init/subsys.c | 180 |
3 files changed, 0 insertions, 599 deletions
diff --git a/init/init.c b/init/init.c deleted file mode 100644 index 126442e..0000000 --- a/init/init.c +++ /dev/null @@ -1,210 +0,0 @@ -#include <fcntl.h> -#include <signal.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/param.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <sys/wait.h> - -#include <curl/curl.h> - -#include <config.h> -#include <init.h> -#include <log.h> -#include <util.h> - -extern int subsystem_handle_term(int pid); -extern int subsystem_count; -int mainpid = 0; -long stack_size = 8192 * 512; -char *token; - -/* For some reason, I get SIGSEGV'd when running because a random-ass - byte was inserted where it isnt supposed to be. Added a safety byte - because I cannot be asked to try to figure out how to do this cleanly. */ -static unsigned long __1bsafebuf - __attribute__((used)) __attribute__((section(".1bsafebuf.init"))) = 0; - -/* We start initcall levels at [1] instead of [0], so we must adjust - in code for this minor design choice. Math is done on the level passed - through i.e. do_initcall_level so that you can call it with (1) and have - the expected initcall (l1_initcall) run. */ -extern initcall_entry_t __initcall1_start[]; -extern initcall_entry_t __initcall2_start[]; -extern initcall_entry_t __initcall3_start[]; -extern initcall_entry_t __initcall4_start[]; -extern initcall_entry_t __initcall5_start[]; -extern initcall_entry_t __initcall_end[]; - -static initcall_entry_t *initcall_levels[] = { - __initcall1_start, - __initcall2_start, - __initcall3_start, - __initcall4_start, - __initcall5_start, - __initcall_end, -}; - -static void do_initcall_level(int level) -{ - initcall_entry_t *fn; - - for (fn = initcall_levels[level - 1]; - fn < initcall_levels[level]; - fn++) - initcall_from_entry(fn)(); -} - -static void do_initcalls(void) -{ - unsigned long level; - for (level = 1; level < ARRAY_SIZE(initcall_levels); level++) { - do_initcall_level(level); - } -} - -static void doenv(char *path) -{ - int fd = open(path, O_RDONLY); - if(fd < 0) - return; - - struct stat statbuf; - if(fstat(fd, &statbuf) < 0) - return; - - char *file_mmap = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if(file_mmap == NULL) - return; - - char *file = malloc(statbuf.st_size + 1); - file[statbuf.st_size + 1] = 0; - memcpy(file, file_mmap, statbuf.st_size); - munmap(file_mmap, statbuf.st_size); - - int offset = 0; - while(1) { - char *line = &(file[offset]); - if(*line == '\0') - break; - - char *eol = strchrnul(line, '\n'); - *eol = '\0'; - if(*line == '#') - goto nextline; - - char *divider = strchr(line, '='); - if(divider == NULL) - goto nextline; - - *divider = '\0'; - setenv(line, divider + 1, 0); - -nextline: - offset += (eol - line) + 1; - continue; - } - - free(file); -} - -int main(void) -{ - /* Hello, World! */ - - /* set mainpid for the subsystem service so it is fully accessible - during l1 */ - mainpid = getpid(); - - /* set stack_size for subsystem service */ - struct rlimit *stack_rlimit = malloc(sizeof(struct rlimit)); - getrlimit(RLIMIT_STACK, stack_rlimit); - if(stack_rlimit->rlim_cur != RLIM_INFINITY) { - stack_size = MIN(stack_rlimit->rlim_cur, stack_size); - } - free(stack_rlimit); - - /* configure signal handlers early to prevent race condition where subsystems - can terminate main process on accident, and disable Terminated output during - early-mode panic */ - static sigset_t set; - sigaddset(&set, SIGCHLD); - sigaddset(&set, SIGINT); - sigaddset(&set, SIGTERM); - sigprocmask(SIG_BLOCK, &set, NULL); - - /* use .env files if present */ - doenv(".env"); - - /* find directory of self and use env from there if it exists */ - char *buf = calloc(PATH_MAX, sizeof(char)); - ssize_t self_size = readlink("/proc/self/exe", buf, PATH_MAX); - if(self_size + strlen(".env") + 1 > PATH_MAX) - goto skip_self; - - char *lastslash = strrchr(buf, '/'); - *lastslash = '\0'; - - char *cwd = get_current_dir_name(); - int cwd_is_exec_dir = strcmp(buf, cwd) == 0; - free(cwd); - if(cwd_is_exec_dir) - goto skip_self; - - strcat(buf, "/.env"); - doenv(buf); -skip_self: - free(buf); - - /* fetch token */ - char *token_base = getenv("TOKEN"); - if(!token_base) - panic("init: cannot find TOKEN in env"); - - token = calloc(strlen(token_base) + strlen("Authorization: Bot ") + 1, - sizeof(char)); - strcpy(token, "Authorization: Bot "); - strcat(token, token_base); - - /* init curl */ - if(curl_global_init(CURL_GLOBAL_DEFAULT)) - panic("init: curl init failed"); - - /* init random seed */ - srand(time(NULL)); - - /* Rest of the program.. */ - do_initcalls(); - - /* Reaper. Much like init. */ - - siginfo_t siginfo; - while(subsystem_count > 0) { - sigwaitinfo(&set, &siginfo); - int sig = siginfo.si_signo; - switch(sig) { - case SIGCHLD: ; - int process = 0; - while((process = waitpid(-1, NULL, WNOHANG)) > 0) - if(subsystem_handle_term(process) > 0) - print(LOG_WARNING "init: failed to reap process %d", - process); - if(siginfo.si_status != 0) { - panic("init: process %d exited with non-zero status (%d)", siginfo.si_pid, siginfo.si_status); - } - break; - case SIGINT: - panic("init: keyboard interrupt"); - break; - case SIGTERM: - exit(0); - break; - default: - break; - } - } - - panic("init: no more subsystems"); -} diff --git a/init/log.c b/init/log.c deleted file mode 100644 index f39467c..0000000 --- a/init/log.c +++ /dev/null @@ -1,209 +0,0 @@ -#include <execinfo.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/file.h> -#include <sys/syscall.h> -#include <sys/time.h> -#include <unistd.h> -#include <log.h> -#include <util.h> - -extern int subsystem_change_mode(int pid, char mode); -extern char *subsystem_get_name(int pid); -extern int mainpid; - -static const char *colors[] = { - [EMERG_LOGLEVEL] = ANSI_BLINK ANSI_REVERSE ANSI_BOLD ANSI_RED, - [ALERT_LOGLEVEL] = ANSI_REVERSE ANSI_BOLD ANSI_RED, - [CRIT_LOGLEVEL] = ANSI_BOLD ANSI_RED, - [ERR_LOGLEVEL] = ANSI_RED, - [WARNING_LOGLEVEL] = ANSI_BOLD, - [NOTICE_LOGLEVEL] = ANSI_BRIGHT_WHITE, - [INFO_LOGLEVEL] = ANSI_RESET, - [DEBUG_LOGLEVEL] = ANSI_ITALIC ANSI_BRIGHT_BLUE, -}; - -static const char *mode_to_string[] = { - [PANICMODE_DEBUGONLY] = "subsystem OOPS", - [PANICMODE_RESPAWN] = "subsystem failure", - [PANICMODE_DIE] = "catastrophic failure", -}; - -static int console_lock = 0; - -#define MAX_TRY_COUNT 1 << 17 -static void obtain_console_lock(void) -{ - int try_count = 0; - register int rax asm("rax"); -retry: - while(console_lock && try_count <= MAX_TRY_COUNT) - try_count += 1; - - asm("mov %0, 1 \n" - "xchg %0, %1" : "=r" (rax), "=m" (console_lock)); - - if(rax > 0 && try_count <= MAX_TRY_COUNT) - goto retry; - - if(try_count > MAX_TRY_COUNT) { - print(LOG_SOH "\3" "4" "log: broken console lock"); - } - - return; -} - -static int vaprint(const char *fmt, va_list ap) -{ - int loglevel = DEFAULT_LOGLEVEL; - int dolocks = 1; - int parsecolon = 1; - if(fmt[0] == LOG_SOH_ASCII) { - loglevel = (fmt[2] - 0x30) % 10; - char flags = fmt[1]; - if(flags & 1 << 1) - dolocks = 0; - if(flags & 1 << 2) - parsecolon = 0; - fmt += 3; - } - - /* not going to be printed? dont bother! */ - if(loglevel > CONSOLE_LOGLEVEL) - return 0; - - /* we essentially print the user's raw input to its own buffer, - later we will parse it and print out ANSI colors and what not */ - char buf[512]; - - vsnprintf(buf, 512, fmt, ap); - buf[512 - 1] = '\0'; - - size_t colon = 0; - if(parsecolon) { - for(; colon < strlen(buf); ++colon) { - if(buf[colon] == ':') - break; - } - } - - char tsbuf[64] = "\0"; - struct timeval time; - gettimeofday(&time, NULL); - snprintf(tsbuf, sizeof(tsbuf), "[%5ld.%06ld] ", - (long)time.tv_sec % 100000, (long)time.tv_usec); - - /* spin lock, at the cost of architecture portability - concurrency is something that we need to adjust for, and the - console will be scrambled and unreadable if we allow writing all - at the same time. I considered simply writing all at once, but - ended up just not caring enough to the point where spinlocks - prevail. */ - if(dolocks) - obtain_console_lock(); - - - /* we want to support stuff without colons, but frankly I havent - tested this at time of writing. will find out later */ - writeputs(ANSI_RESET ANSI_GREEN); - writeputs(tsbuf); - writeputs(ANSI_RESET); - if(parsecolon && buf[colon] == ':') { - writeputs(colors[loglevel]); - writeputs(ANSI_YELLOW); - write(STDOUT_FILENO, buf, colon); - writeputs(ANSI_RESET); - } - writeputs(colors[loglevel]); - if(colon && *(buf + colon)) { - writeputs(buf + colon); - } else { - writeputs(buf); - } - writeputs(ANSI_RESET); - write(STDOUT_FILENO, "\n", 1); - if(dolocks) - console_lock = 0; - return 0; -} - -void _panic(const char *fileorigin, - const int lineorigin, - const char *fmt, ...) -{ - char mode = PANICMODE_DIE; - int pid = getpid(); - if(fmt[0] == LOG_SOH_ASCII) { - mode = fmt[1]; - /* cannot respawn main thread */ - if(pid == mainpid && mode == PANICMODE_RESPAWN) - mode = PANICMODE_DIE; - fmt += 2; - } - -#define NOLOCK(loglevel) LOG_SOH "\3" loglevel - va_list ap; - va_start(ap, fmt); - char *_fmt = malloc(strlen(fmt) + 4 * sizeof(char)); - sprintf(_fmt, NOLOCK("1") "%s", fmt); - - void **backtrace_addresses = malloc(sizeof(void*) * 32); - int backtrace_count = backtrace(backtrace_addresses, 32); - char **backtrace_symbolnames = - backtrace_symbols(backtrace_addresses, backtrace_count); - - obtain_console_lock(); - - print(NOLOCK("5") "------------[ cut here ]------------"); - print(LOG_SOH "\7""0" "%s at %s:%d", mode_to_string[(int)mode], - fileorigin, lineorigin); - vaprint(_fmt, ap); - print(LOG_SOH "\7""7" "Call Trace:"); - for(int i = 0; i < backtrace_count; ++i) { - print(NOLOCK("7") " [0x%016x] %s", backtrace_addresses[i], - backtrace_symbolnames[i]); - } - if(mainpid == pid){ - print(NOLOCK("7") " <start of main thread>"); - } else { - print(NOLOCK("7") " <start of %s[%d]>", - subsystem_get_name(pid), pid); - } - - /* if we are going to die, we dont really need to clean up */ - if(mode == PANICMODE_DIE) { - kill(0, SIGTERM); - raise(SIGTERM); - exit(0); - } - - print(NOLOCK("5") "------------[ cut here ]------------"); - - console_lock = 0; - free(_fmt); - free(backtrace_symbolnames); - free(backtrace_addresses); - va_end(ap); - - if(mode == PANICMODE_DEBUGONLY) - return; - - if(pid != mainpid && mode == PANICMODE_RESPAWN) { - /* we want to let the main process handle the rest */ - subsystem_change_mode(pid, mode); - syscall(SYS_exit_group, 0); - } -} - -int print(const char *fmt, ...) -{ - int ret = 0; - va_list ap; - va_start(ap, fmt); - ret = vaprint(fmt, ap); - va_end(ap); - return ret; -} diff --git a/init/subsys.c b/init/subsys.c deleted file mode 100644 index 704678f..0000000 --- a/init/subsys.c +++ /dev/null @@ -1,180 +0,0 @@ -#include <errno.h> -#include <sched.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/prctl.h> -#include <sys/syscall.h> -#include <sys/mman.h> -#include <unistd.h> - -#include <config.h> -#include <log.h> -#include <util.h> - -#define MAX_SUBSYSTEMS 32 -#define MAX_RESPAWN 3 - -struct subsystem_info { - char *fn_name; - int (*fn)(void); - int pid; - void *stack; - char mode; - int respawn_count; -}; - -extern long stack_size; -extern int mainpid; -static struct subsystem_info *subsystems[MAX_SUBSYSTEMS + 1]; -int subsystem_count = 0; - -static int __subsystem_entry(struct subsystem_info *info) -{ - /* entry point from clone(). we setup the process name so we know - what we are looking at from a glance in a ps view or htop or - whatever. */ - char *name = malloc(16 * sizeof(char)); - snprintf(name, 16, NAME_SHORTHAND ": %s", info->fn_name); - name[15] = '\0'; - prctl(PR_SET_NAME, name); - free(name); - - /* clear signal handlers so SIGTERM is no longer caught */ - static sigset_t set; - sigprocmask(SIG_SETMASK, &set, NULL); - - int ret = info->fn(); - - return ret; -} - -char *subsystem_get_name(int pid) -{ - for(int i = 0; i < MAX_SUBSYSTEMS; ++i) { - struct subsystem_info *subsystem = subsystems[i]; - if(!subsystem || subsystem->pid != pid) - continue; - - return subsystem->fn_name; - } - return 0; -} - -int subsystem_change_mode(int pid, char mode) -{ - for(int i = 0; i < MAX_SUBSYSTEMS; ++i) { - struct subsystem_info *subsystem = subsystems[i]; - if(!subsystem || subsystem->pid != pid) - continue; - - subsystem->mode = mode; - return 0; - } - - return 1; -} - -int subsystem_handle_term(int pid) -{ - for(int i = 0; i < MAX_SUBSYSTEMS; ++i) { - struct subsystem_info *subsystem = subsystems[i]; - if(!subsystem || subsystem->pid != pid) - continue; - - if(subsystem->mode == PANICMODE_RESPAWN - && subsystem->respawn_count < MAX_RESPAWN) { - ++(subsystem->respawn_count); - - int pid = clone((int (*)(void *))__subsystem_entry, - (void *)((long)(subsystem->stack) + stack_size), - CLONE_FILES | CLONE_VM | SIGCHLD, subsystem); - subsystem->pid = pid; - if(pid < 0) { - print(LOG_CRIT "subsys: cannot re-start subsystem %s: " - "clone failed (errno %d)", subsystem->fn_name, errno); - if(munmap(subsystem->stack, stack_size) < 0) - print(LOG_CRIT "subsys: failed to deallocate " - "stack for subsystem %s (%d) (errno %d)", - subsystem->fn_name, pid, errno); - free(subsystem); - return 0; - } - - subsystem->mode = 'o'; - return 0; - } else if(subsystem->mode == PANICMODE_RESPAWN) { - panic("subsys: exceeded maximum respawn count for subsystem " - "%s (%d)", subsystem->fn_name, subsystem->pid); - } - - if(munmap(subsystem->stack, stack_size) < 0) - print(LOG_CRIT "subsys: failed to deallocate stack " - "for subsystem %s (%d) (errno %d)", - subsystem->fn_name, pid, errno); - subsystems[i] = 0; - --subsystem_count; - free(subsystem); - - return 0; - } - - return 1; -} - -int __impl_start_subsystem(char *fn_name, int (*fn)(void)) -{ - if(getpid() != mainpid) { - print(LOG_CRIT "subsys: cannot perform subsystem inception " - "(attempted from %d)", getpid()); - return 1; - } - if(subsystem_count >= MAX_SUBSYSTEMS) { - print(LOG_CRIT "subsys: cannot start subsystem %s: " - "reached maximum number of subsystems", fn_name); - return 1; - } - - /* because CLONE_VM is being set, our stack is not duplicated and - therefore we need to map a stack */ - void *stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_STACK | MAP_PRIVATE, -1, 0); - if((long)stack <= 0) { - print(LOG_CRIT "subsys: cannot start subsystem %s: " - "failed to allocate stack (errno %d)", fn_name, errno); - return 1; - } - - /* the libc gods have graced us with the ability to pass one (1) arg - to the function. struct required. the absence of a free is not a - memory leak because we free it above. */ - struct subsystem_info *info = malloc(sizeof(struct subsystem_info)); - info->fn_name = fn_name; - info->fn = fn; - info->stack = stack; - info->mode = 'o'; - info->respawn_count = 0; - - int pid = clone((int (*)(void *))__subsystem_entry, - (void *)((long)stack + stack_size), - CLONE_FILES | CLONE_VM | SIGCHLD, info); - info->pid = pid; - if(pid < 0) { - print(LOG_CRIT "subsys: cannot start subsystem %s: " - "clone failed (errno %d)", fn_name, errno); - munmap(stack, stack_size); - free(info); - return 1; - } - - for(int i = 0; i < MAX_SUBSYSTEMS; ++i) { - if(!subsystems[i]) { - subsystems[i] = info; - ++subsystem_count; - break; - } - } - - return 0; -} |