#include "inc/priv.h"#include "inc/process.h"#include "inc/time.h"#include <errno.h>#include <fcntl.h>#include <locale.h>#include <signal.h>#include <sys/mman.h>#include <sys/socket.h>#include <sys/stat.h>#include <sys/time.h>#include <sys/utsname.h>#include <sys/wait.h>#include <time.h> 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;} voidsl_segfree(void *s, usize sz){ if(s == nil) return; sz = PAGEALIGNED(sz); if(munmap(s, sz) != 0) abort();} voidsl_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);} u64intsec_realtime(void){ struct timespec now; if(clock_gettime(CLOCK_REALTIME, &now) != 0) return 0; return (u64int)now.tv_sec*Nsec + (u64int)now.tv_nsec;} u64intnanosec_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;} voidtimestr(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);} voidsleep_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);} voidsys_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;} intsys_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;} intsys_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;} intsys_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);} intsys_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;} voidsys_fd_setup(int fd){ USED(fd);} intsys_rename(const char *old, const char *new){ return rename(old, new);} soffsetsys_seek(int fd, soffset offset, int whence){ return lseek(fd, offset, whence);} intsys_close(int fd){ return close(fd);} intsys_dup(int oldfd, int newfd){ while(1){ int r = dup2(oldfd, newfd); if(r >= 0) return r; if(errno != EBUSY && errno != EINTR) return -1; }} intsys_exec(const char *path, char *const argv[], char *env[]){ if(env == nil) return execv(path, argv); return execve(path, argv, env);} intsys_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;} intsys_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;} intsys_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 voidsigchld(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);} voidsys_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; intmain(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);}
#include "inc/priv.h"
#include "inc/process.h"
#include "inc/time.h"
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <time.h>
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);
}