#include "inc/priv.h" #include "inc/process.h" #include "inc/time.h" #include #include #include #include #include #include #include #include #include #include #include static usize pagesize = 4096; #define PAGEALIGNED(x) (((x) + pagesize-1) & ~(pagesize-1)) #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) #define MAP_ANONYMOUS MAP_ANON #endif #if !defined(O_PATH) #define O_PATH 0 #endif void * sl_segalloc(usize sz) { sz = PAGEALIGNED(sz); void *s = mmap(nil, sz, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); if(s == (void*)-1) s = nil; return s; } void sl_segfree(void *s, usize sz) { if(s == nil) return; sz = PAGEALIGNED(sz); if(munmap(s, sz) != 0) abort(); } void sl_segused(void *s, usize sz, usize used) { sz = PAGEALIGNED(sz); used = PAGEALIGNED(used); // it's fine if this fails posix_madvise((u8int*)s+used, sz-used, POSIX_MADV_DONTNEED); } u64int sec_realtime(void) { struct timespec now; if(clock_gettime(CLOCK_REALTIME, &now) != 0) return 0; return (u64int)now.tv_sec*Nsec + (u64int)now.tv_nsec; } u64int nanosec_monotonic(void) { static s64int z; struct timespec now; if(clock_gettime(CLOCK_MONOTONIC, &now) != 0) return 0; if(z == 0){ z = now.tv_sec*Nsec + now.tv_nsec; return 0; } return now.tv_sec*Nsec + now.tv_nsec - z; } void timestr(u64int ns, char *buf, int sz) { time_t tme = ns/Nsec; struct tm tm; *buf = 0; if(localtime_r(&tme, &tm) != nil) strftime(buf, sz, "%a %b %e %H:%M:%S %Y", &tm); } void sleep_ms(int ms) { if(ms != 0){ struct timeval timeout; timeout.tv_sec = ms/1000; timeout.tv_usec = (ms % 1000) * 1000; select(0, nil, nil, nil, &timeout); } } void __attribute__((noinline)) sys_setcurrpc(int dummy) { USED(dummy); sys_setcallpc(dummy); } void sys_seed(void *p, int sz) { u8int *o = p; int f; if((f = open("/dev/urandom", O_RDONLY)) >= 0){ while(sz > 0){ ssize_t n = read(f, o, sz); if(n < 0) break; o += n; sz -= n; } close(f); } // not sure what kind of OS that would be where it fails. have fun with that, anyway } static int _enonfatal(int err) { return err == 0 || err == EAGAIN || err == EINPROGRESS || err == EINTR || err == EWOULDBLOCK; } int sys_read(int fd, u8int *buf, usize n, usize *nread) { ssize r; while(1){ errno = 0; r = read(fd, buf, n); if(r > -1){ *nread = (usize)r; break; } if(!_enonfatal(errno)){ *nread = 0; return errno; } sleep_ms(1); } return 0; } int sys_write(int fd, const void *buf, usize n, usize *nwritten) { ssize r; while(1){ errno = 0; r = write(fd, buf, n); if(r > -1){ *nwritten = (usize)r; break; } if(!_enonfatal(errno)){ *nwritten = 0; return errno; } sleep_ms(1); } return 0; } int sys_open(const char *fname, ios_flags flags, usize mode) { int of = (flags & IOS_WRITE) ? ((flags & IOS_READ) ? O_RDWR : O_WRONLY) : O_RDONLY; if(flags & IOS_TRUNC) of |= O_TRUNC; if(flags & IOS_CREATE) of |= O_CREAT; if(flags & IOS_APPEND) of |= O_APPEND; if(flags & IOS_EXCL) of |= O_EXCL; if(flags & IOS_DIR) of |= O_DIRECTORY | O_PATH; of |= O_CLOEXEC; return open(fname, of, mode); } int sys_mkdir(const char *path, usize mode) { if(mkdir(path, mode) == 0) return 0; char *p = MEM_STRDUP(path); if(p == nil) return -1; int r = -1; for(char *s = p+1; (s = strchr(s, '/')) != nil;){ struct stat sb = {0}; *s = 0; if(stat(p, &sb) == 0){ if((sb.st_mode & S_IFMT) != S_IFDIR) goto done; }else if(mkdir(p, mode) != 0 && errno != EEXIST) goto done; *s++ = '/'; while(*s == '/') s++; } if((r = mkdir(path, mode)) != 0 && errno == EEXIST) r = 0; done: MEM_FREE(p); return r; } void sys_fd_setup(int fd) { USED(fd); } int sys_rename(const char *old, const char *new) { return rename(old, new); } soffset sys_seek(int fd, soffset offset, int whence) { return lseek(fd, offset, whence); } int sys_close(int fd) { return close(fd); } int sys_dup(int oldfd, int newfd) { while(1){ int r = dup2(oldfd, newfd); if(r >= 0) return r; if(errno != EBUSY && errno != EINTR) return -1; } } int sys_exec(const char *path, char *const argv[], char *env[]) { if(env == nil) return execv(path, argv); return execve(path, argv, env); } int sys_fork(fork_flags flags) { // note: nothing needs to be done for FORK_CLEAR_ENV since the only // use of sys_fork is make-process, which is using sys_exec after and // passes the new environment USED(flags); return fork(); } char * sys_getcwd(void) { char tmp[4096]; if(getcwd(tmp, sizeof(tmp)) != nil) return MEM_STRDUP(tmp); return nil; } int sys_pipe(int fd[2], bool rd, bool wr) { if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) return -1; if(!rd){ shutdown(fd[0], SHUT_RD); shutdown(fd[1], SHUT_WR); } if(!wr){ shutdown(fd[0], SHUT_WR); shutdown(fd[1], SHUT_RD); } return 0; } char * sys_find_program(const char *p) { if((p[0] == '/') || (p[0] == '.' && p[1] == '/') || (p[0] == '.' && p[1] == '.' && p[2] == '/')) return MEM_STRDUP(p); char *path = getenv("PATH"); if(path == nil) return nil; path = MEM_STRDUP(path); char *s, *e; for(s = path; s != nil;){ e = strchr(s, ':'); if(e != nil) *e = 0; int dir = open(s, O_RDONLY|O_DIRECTORY|O_PATH); if(dir >= 0){ int r = faccessat(dir, p, X_OK, AT_EACCESS); close(dir); if(r == 0) break; } s = e != nil ? e+1 : nil; } if(s != nil){ int n = strlen(s); e = MEM_ALLOC(n+1+strlen(p)+1); memcpy(e, s, n); e[n] = '/'; strcpy(e+n+1, p); s = e; } free(path); return s; } int sys_postnote(int pid, const char *note, const char *sig) { static const struct { const char *str; int sig; }sigs[] = { {"ABRT", SIGABRT}, {"ALRM", SIGALRM}, {"CONT", SIGCONT}, {"HUP", SIGHUP}, {"INT", SIGINT}, {"KILL", SIGKILL}, {"QUIT", SIGQUIT}, {"STOP", SIGSTOP}, {"TERM", SIGTERM}, {"USR1", SIGUSR1}, {"USR2", SIGUSR2}, }; USED(note); if(strncmp(sig, "SIG", 3) != 0) return -1; sig += 3; for(int i = 0; i < nelem(sigs); i++){ if(strcmp(sig, sigs[i].str) == 0) return kill(pid, sigs[i].sig); } return -1; } BUILTIN("getpid", getpid) { USED(args); noargs(); return fixnum(getpid()); } static void sigchld(int sig, siginfo_t *info, void *ucontext) { USED(sig); USED(ucontext); waitpid(info->si_pid, nil, WUNTRACED|WCONTINUED); int state; switch(info->si_code){ case CLD_EXITED: state = PSTATE_EXITED; break; case CLD_DUMPED: state = PSTATE_DUMPED; break; case CLD_STOPPED: state = PSTATE_STOPPED; break; case CLD_CONTINUED: state = PSTATE_RUNNING; break; case CLD_KILLED: state = PSTATE_SIGNALED; break; case CLD_TRAPPED: default: return; } int code = info->si_status; char msg[16]; if(code != 0) snprintf(msg, sizeof(msg), "%d", code); process_update(info->si_pid, state, code, code != 0 ? msg : nil); } void sys_init(void) { struct sigaction s = { .sa_sigaction = sigchld, .sa_flags = SA_SIGINFO, }; sigaction(SIGCHLD, &s, nil); } static const u8int boot[] = { #include "sl.boot.h" }; char *os_version; int main(int argc, char **argv) { setlocale(LC_NUMERIC, "C"); struct utsname u; os_version = strdup(uname(&u) >= 0 ? u.release : ""); long p = sysconf(_SC_PAGE_SIZE); if(p > 0) pagesize = p; slmain(boot, sizeof(boot), argc, argv); }