1// Bend Bank: the host's FFI2// =========================3//4// The few OS calls host.cml needs beyond CakeML's basis_ffi.c, in CakeML's5// FFI convention: c/clen is a string from CakeML, a/alen a byte array both6// ways. Numbers cross as 4 (fd, count) or 8 (time) bytes, big endian.7//8// bank_listen a = port: listens on 127.0.0.1:port, backlog 4096. a[0] = 0 ok.9// bank_wait blocks until the next event on any connection, accepting new10// ones on the way: a = fd, n, bytes. n > 0: the n bytes read;11// n = 0: the fd closed (by the peer, an error, or idle 30 s).12// bank_write c = bytes, a = fd: writes all of c. a[0] = 0 ok.13// bank_close a = fd: closes it.14// bank_open c = path: opens the log for append. a[0] = 0 ok.15// bank_append c = line: appends it to the log and fsyncs. a[0] = 0 ok.16// bank_random a = 16 random bytes.17// bank_time a = the unix time in seconds.1819#include <arpa/inet.h>20#include <fcntl.h>21#include <poll.h>22#include <signal.h>23#include <stdlib.h>24#include <string.h>25#include <sys/random.h>26#include <sys/socket.h>27#include <sys/time.h>28#include <time.h>29#include <unistd.h>3031#define MAXFD 6553632#define IDLE 303334static struct pollfd fds[MAXFD]; // indexed by fd; fd -1 is a free slot35static long last[MAXFD]; // the time of each connection's last activity36static int lfd = -1, logfd = -1, hi = 0, cur = 0;3738static long get(unsigned char *a, int k) { long n = 0; for (int i = 0; i < k; i++) n = n << 8 | a[i]; return n; }39static void put(unsigned char *a, int k, long n) { for (int i = k - 1; i >= 0; i--, n >>= 8) a[i] = n & 255; }4041static int all(int fd, unsigned char *c, long n) {42 for (long w; n > 0; c += w, n -= w) if ((w = write(fd, c, n)) <= 0) return 1;43 return 0;44}4546static void watch(int fd) {47 fds[fd].fd = fd; fds[fd].events = POLLIN; last[fd] = time(0);48 if (fd >= hi) hi = fd + 1;49}5051void ffibank_listen(unsigned char *c, long clen, unsigned char *a, long alen) {52 struct sockaddr_in in = {.sin_family = AF_INET, .sin_port = htons(get(a, 4))};53 int one = 1;54 signal(SIGPIPE, SIG_IGN);55 for (int i = 0; i < MAXFD; i++) fds[i].fd = -1;56 in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);57 lfd = socket(AF_INET, SOCK_STREAM, 0);58 setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);59 a[0] = bind(lfd, (struct sockaddr *)&in, sizeof in) || listen(lfd, 4096);60 fcntl(lfd, F_SETFL, O_NONBLOCK);61 watch(lfd);62}6364void ffibank_wait(unsigned char *c, long clen, unsigned char *a, long alen) {65 for (;;) {66 poll(fds, hi, 1000);67 for (int k = 0; k < hi; k++) {68 int fd = cur = (cur + 1) % hi;69 if (fds[fd].fd < 0) continue;70 if (fd == lfd && fds[fd].revents) {71 int s = accept(lfd, 0, 0);72 struct timeval t = {IDLE, 0};73 if (s >= MAXFD) close(s);74 else if (s >= 0) { setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof t); watch(s); }75 }76 if (fd == lfd || !fds[fd].revents && time(0) - last[fd] < IDLE) continue;77 long n = fds[fd].revents ? read(fd, a + 8, alen - 8) : 0;78 if (n <= 0) { close(fd); fds[fd].fd = -1; n = 0; }79 last[fd] = time(0); put(a, 4, fd); put(a + 4, 4, n);80 return;81 }82 }83}8485void ffibank_write(unsigned char *c, long clen, unsigned char *a, long alen) {86 int fd = get(a, 4);87 last[fd] = time(0);88 a[0] = all(fd, c, clen);89}9091void ffibank_close(unsigned char *c, long clen, unsigned char *a, long alen) {92 int fd = get(a, 4);93 close(fd); fds[fd].fd = -1;94}9596void ffibank_open(unsigned char *c, long clen, unsigned char *a, long alen) {97 char p[4096] = {0};98 memcpy(p, c, clen < 4095 ? clen : 4095);99 logfd = open(p, O_WRONLY | O_APPEND | O_CREAT, 0644);100 a[0] = logfd < 0;101}102103void ffibank_append(unsigned char *c, long clen, unsigned char *a, long alen) {104 a[0] = all(logfd, c, clen) || fsync(logfd);105}106107void ffibank_random(unsigned char *c, long clen, unsigned char *a, long alen) {108 if (getrandom(a, 16, 0) != 16) abort();109}110111void ffibank_time(unsigned char *c, long clen, unsigned char *a, long alen) {112 put(a, 8, time(0));113}