This commit is contained in:
Benjamin Summers 2019-05-05 14:46:40 -07:00
parent 88f1bc5eeb
commit 4b38053509
23 changed files with 14382 additions and 3 deletions

109
pkg/hs/vere/Vere/Behn.hs Normal file
View File

@ -0,0 +1,109 @@
{-
# Behn
This provides a timer. To use this,
- Create a new timer with `init`.
- Use `doze` to start the timer.
- Call `wait` to wait until the timer fires.
Then, `wait` will return when the specified time has come.
- If the specified time was in the past, `wait` will return immediately.
- If a timer is set again, the old timer will not fire. The new time
replaces the old one.
- If a timer is unset (with `doze _ Nothing`), the timer will not fire
until a new time has been set.
## Implementation Notes
We use `tryPutMVar` when the timer fires, so that things will continue
to work correctly if the user does not call `wait`. If a timer fires
before `wait` is called, `wait` will return immediatly.
To handle race conditions, the MVar in `bState` is used as a lock. The
code for setting a timer and the thread that runs when the timer fires
(which causes `wait` to return) both take that MVar before acting.
So, if the timer fires conncurently with a call to `doze`,
then one of those threads will get the lock and the other will wait:
- If the `doze` call gets the lock first, it will kill the timer thread
before releasing it.
- If the timer gets the the lock first, it will fire (causeing `wait`
to return) first, and then `doze` action will wait until that finishes.
-}
module Vere.Behn (Behn, init, wait, doze) where
import Control.Concurrent
import Control.Concurrent.Async hiding (wait)
import Control.Concurrent.MVar
import Data.LargeWord
import Prelude hiding (init)
import Data.Time.Clock.System (SystemTime(..), getSystemTime)
import Control.Lens ((&))
import Control.Monad (void)
-- Time Stuff ------------------------------------------------------------------
type UrbitTime = Word128
urNow :: IO UrbitTime
urNow = systemTimeToUrbitTime <$> getSystemTime
{-
TODO This is wrong.
- The high word should be `(0x8000000cce9e0d80ULL + secs)`
- The low word should be `(((usecs * 65536ULL) / 1000000ULL) << 48ULL)`
-}
systemTimeToUrbitTime :: SystemTime -> UrbitTime
systemTimeToUrbitTime (MkSystemTime secs ns) =
LargeKey (fromIntegral secs) (fromIntegral ns)
-- TODO
urbitTimeToMicrosecs :: UrbitTime -> Int
urbitTimeToMicrosecs x = fromIntegral x
-- TODO Double Check this
diffTime :: UrbitTime -> UrbitTime -> UrbitTime
diffTime fst snd | fst >= snd = 0
| otherwise = snd - fst
-- Behn Stuff ------------------------------------------------------------------
data Behn = Behn
{ bState :: MVar (Maybe (UrbitTime, Async ()))
, bSignal :: MVar UrbitTime
}
init :: IO Behn
init = do
st <- newMVar Nothing
sig <- newEmptyMVar
pure (Behn st sig)
wait :: Behn -> IO UrbitTime
wait (Behn _ sig) = takeMVar sig
startTimerThread :: Behn -> UrbitTime -> IO (Async ())
startTimerThread (Behn vSt sig) time =
async $ do
now <- urNow
threadDelay (urbitTimeToMicrosecs (now `diffTime` time))
void (swapMVar vSt Nothing >> tryPutMVar sig time)
doze :: Behn -> Maybe UrbitTime -> IO ()
doze behn@(Behn vSt sig) mNewTime = do
takeMVar vSt >>= \case Nothing -> pure ()
Just (_,timer) -> cancel timer
newSt <- mNewTime & \case
Nothing -> pure (Nothing :: Maybe (UrbitTime, Async ()))
Just time -> do timer <- startTimerThread behn time
pure (Just (time, timer))
void (putMVar vSt newSt)

View File

@ -0,0 +1,184 @@
{-
TODO When is `u3_behn_io_init` called?
-}
data Pier
data Timer
data Wen = Wen Noun Noun Noun
data TimeVal = TimeVal
{ tv_sec :: time_t -- seconds
, tv_usec :: suseconds_t -- microseconds
}
data Event
= Wake
| Born
data Wire
= Blip -- Empty path
| Behn
| Sen Text -- "an instance string"
newtype Knot = Knot Text
newtype Wire = Wire [Knot]
data Duct = [Wire]
data Blip = Blip Behn (Maybe Void)
{-
alm -- is timer active?
tim -- timer
data -- associated pier
-}
data Behn = Behn
{ _alm :: TVar Bool
, _tim :: TVar Timer
, _data :: TVar Pier
}
makeLenses ''Behn
--------------------------------------------------------------------------------
newTimer :: IO Timer
newTimer = undefined
init :: Pier -> IO ()
init p =
timer <- newTimer
atomically $ do
writeTVar (p ^. teh.alm) False
writeTVar (p ^. teh.tim) timer
writeTVar (p ^. teh.data) p
exit :: Pier -> IO ()
exit _ = pure ()
doze :: Pier -> Maybe Wen -> IO ()
doze pir mWen = do
(active, timer) <- do
(,) <$> readTVarIO (pir ^. teh.alm)
<*> readTVarIO (pir ^. teh.tim)
if active
then stopTimer timer -- TODO Race condition
else pure ()
case mWen of
Nothing -> pure ()
Just (Wen x y z) -> do
timeVal <- getTimeOfDay
let now = u3_time_in_tv timeVal
let gap = u3_time_gap_ms(y, z)
writeTVar (p ^. teh.alm) True
startTimer timer gap $ do
u3_pier *pir_u = tim_u->data;
u3_behn* teh_u = pir_u->teh_u;
writeTVar (p ^. teh.alm) False;
pierWork pir [Blip Behn] Wake
bake :: Pier -> IO ()
bake = do
sen <- readTVarIO (u3A ^. sen)
pierWork pir [Blip Behn (Sen sen)] Born
/* u3_behn_ef_bake(): notify %behn that we're live
*/
void
u3_behn_ef_bake(u3_pier *pir_u)
{
u3_noun pax = u3nq(u3_blip, c3__behn, u3k(u3A->sen), u3_nul);
u3_pier_work(pir_u, pax, u3nc(c3__born, u3_nul));
}
{-
u3_time_in_tv timeVal
u3_time_gap_ms(y, z)
u3nt(u3_blip, c3__behn, u3_nul),
u3nc(c3__wake, u3_nul));
-}
--------------------------------------------------------------------------------
/* u3_behn(): initialize time timer.
*/
void
u3_behn_io_init(u3_pier *pir_u)
{
u3_behn* teh_u = pir_u->teh_u;
teh_u->alm = c3n;
uv_timer_init(u3L, &teh_u->tim_u);
teh_u->tim_u.data = pir_u;
}
/* u3_behn_io_exit(): terminate timer.
*/
void
u3_behn_io_exit(u3_pier *pir_u)
{
}
/* _behn_time_cb(): timer callback.
*/
static void
_behn_time_cb(uv_timer_t* tim_u)
{
u3_pier *pir_u = tim_u->data;
u3_behn* teh_u = pir_u->teh_u;
teh_u->alm = c3n;
{
u3_pier_work
(pir_u,
u3nt(u3_blip, c3__behn, u3_nul),
u3nc(c3__wake, u3_nul));
}
}
/* u3_behn_ef_doze(): set or cancel timer
*/
void
u3_behn_ef_doze(u3_pier *pir_u, u3_noun wen)
{
u3_behn* teh_u = pir_u->teh_u;
if ( c3y == teh_u->alm ) {
uv_timer_stop(&teh_u->tim_u);
teh_u->alm = c3n;
}
if ( (u3_nul != wen) &&
(c3y == u3du(wen)) &&
(c3y == u3ud(u3t(wen))) )
{
struct timeval tim_tv;
gettimeofday(&tim_tv, 0);
u3_noun now = u3_time_in_tv(&tim_tv);
c3_d gap_d = u3_time_gap_ms(now, u3k(u3t(wen)));
teh_u->alm = c3y;
uv_timer_start(&teh_u->tim_u, _behn_time_cb, gap_d, 0);
}
u3z(wen);
}
/* u3_behn_ef_bake(): notify %behn that we're live
*/
void
u3_behn_ef_bake(u3_pier *pir_u)
{
u3_noun pax = u3nq(u3_blip, c3__behn, u3k(u3A->sen), u3_nul);
u3_pier_work(pir_u, pax, u3nc(c3__born, u3_nul));
}

563
pkg/hs/vere/notes/c/ames.c Normal file
View File

@ -0,0 +1,563 @@
/* vere/ames.c
**
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <uv.h>
#include <errno.h>
#include <ncurses/curses.h>
#include <termios.h>
#include <ncurses/term.h>
#include "all.h"
#include "vere/vere.h"
/* _ames_alloc(): libuv buffer allocator.
*/
static void
_ames_alloc(uv_handle_t* had_u,
size_t len_i,
uv_buf_t* buf
)
{
// we allocate 2K, which gives us plenty of space
// for a single ames packet (max size 1060 bytes)
//
void* ptr_v = c3_malloc(2048);
*buf = uv_buf_init(ptr_v, 2048);
}
/* _ames_free(): contrasting free.
*/
static void
_ames_free(void* ptr_v)
{
// u3l_log("free %p\n", ptr_v);
free(ptr_v);
}
/* _ames_pact_free(): free packet struct.
*/
static void
_ames_pact_free(u3_pact* pac_u)
{
free(pac_u->hun_y);
free(pac_u->dns_c);
free(pac_u);
}
/* _ames_send_cb(): send callback.
*/
static void
_ames_send_cb(uv_udp_send_t* req_u, c3_i sas_i)
{
u3_pact* pac_u = (u3_pact*)req_u;
#if 0
if ( 0 != sas_i ) {
u3l_log("ames: send_cb: %s\n", uv_strerror(sas_i));
}
#endif
_ames_pact_free(pac_u);
}
/* _ames_send(): send buffer to address on port.
*/
static void
_ames_send(u3_pact* pac_u)
{
// XX revisit
u3_pier* pir_u = u3_pier_stub();
u3_ames* sam_u = pir_u->sam_u;
if ( !pac_u->hun_y ) {
_ames_pact_free(pac_u);
return;
}
struct sockaddr_in add_u;
memset(&add_u, 0, sizeof(add_u));
add_u.sin_family = AF_INET;
add_u.sin_addr.s_addr = htonl(pac_u->pip_w);
add_u.sin_port = htons(pac_u->por_s);
uv_buf_t buf_u = uv_buf_init((c3_c*)pac_u->hun_y, pac_u->len_w);
c3_i sas_i;
if ( 0 != (sas_i = uv_udp_send(&pac_u->snd_u,
&sam_u->wax_u,
&buf_u, 1,
(const struct sockaddr*)&add_u,
_ames_send_cb)) ) {
u3l_log("ames: send: %s\n", uv_strerror(sas_i));
}
}
/* _ames_czar_port(): udp port for galaxy.
*/
static c3_s
_ames_czar_port(c3_y imp_y)
{
if ( c3n == u3_Host.ops_u.net ) {
return 31337 + imp_y;
}
else {
return 13337 + imp_y;
}
}
/* _ames_czar_gone(): galaxy address resolution failed.
*/
static void
_ames_czar_gone(u3_pact* pac_u, time_t now)
{
// XX revisit
u3_pier* pir_u = u3_pier_stub();
u3_ames* sam_u = pir_u->sam_u;
u3l_log("ames: czar at %s: not found (b)\n", pac_u->dns_c);
if ( (0 == sam_u->imp_w[pac_u->imp_y]) ||
(0xffffffff == sam_u->imp_w[pac_u->imp_y]) ) {
sam_u->imp_w[pac_u->imp_y] = 0xffffffff;
} /* else keep existing ip for 5 more minutes */
sam_u->imp_t[pac_u->imp_y] = now;
_ames_pact_free(pac_u);
}
/* _ames_czar_cb(): galaxy address resolution callback.
*/
static void
_ames_czar_cb(uv_getaddrinfo_t* adr_u,
c3_i sas_i,
struct addrinfo* aif_u)
{
// XX revisit
u3_pier* pir_u = u3_pier_stub();
u3_ames* sam_u = pir_u->sam_u;
u3_pact* pac_u = (u3_pact*)adr_u->data;
time_t now = time(0);
struct addrinfo* rai_u = aif_u;
while ( 1 ) {
if ( !rai_u ) {
_ames_czar_gone(pac_u, now);
break;
}
if ( (AF_INET == rai_u->ai_family) ) {
struct sockaddr_in* add_u = (struct sockaddr_in *)rai_u->ai_addr;
c3_w old_w = sam_u->imp_w[pac_u->imp_y];
sam_u->imp_w[pac_u->imp_y] = ntohl(add_u->sin_addr.s_addr);
sam_u->imp_t[pac_u->imp_y] = now;
#if 1
if ( sam_u->imp_w[pac_u->imp_y] != old_w
&& sam_u->imp_w[pac_u->imp_y] != 0xffffffff ) {
u3_noun wad = u3i_words(1, &sam_u->imp_w[pac_u->imp_y]);
u3_noun nam = u3dc("scot", c3__if, wad);
c3_c* nam_c = u3r_string(nam);
u3l_log("ames: czar %s: ip %s\n", pac_u->dns_c, nam_c);
free(nam_c); u3z(nam);
}
#endif
_ames_send(pac_u);
break;
}
rai_u = rai_u->ai_next;
}
free(adr_u);
uv_freeaddrinfo(aif_u);
}
/* _ames_czar(): galaxy address resolution.
*/
static void
_ames_czar(u3_pact* pac_u, c3_c* bos_c)
{
// XX revisit
u3_pier* pir_u = u3_pier_stub();
u3_ames* sam_u = pir_u->sam_u;
pac_u->por_s = _ames_czar_port(pac_u->imp_y);
if ( c3n == u3_Host.ops_u.net ) {
pac_u->pip_w = 0x7f000001;
_ames_send(pac_u);
return;
}
// if we don't have a galaxy domain, no-op
//
if ( 0 == bos_c ) {
u3_noun nam = u3dc("scot", 'p', pac_u->imp_y);
c3_c* nam_c = u3r_string(nam);
u3l_log("ames: no galaxy domain for %s, no-op\r\n", nam_c);
free(nam_c);
u3z(nam);
return;
}
time_t now = time(0);
// backoff
if ( (0xffffffff == sam_u->imp_w[pac_u->imp_y]) &&
(now - sam_u->imp_t[pac_u->imp_y]) < 300 ) {
_ames_pact_free(pac_u);
return;
}
if ( (0 == sam_u->imp_w[pac_u->imp_y]) ||
(now - sam_u->imp_t[pac_u->imp_y]) > 300 ) { /* 5 minute TTL */
u3_noun nam = u3dc("scot", 'p', pac_u->imp_y);
c3_c* nam_c = u3r_string(nam);
// XX remove extra byte for '~'
pac_u->dns_c = c3_malloc(1 + strlen(bos_c) + 1 + strlen(nam_c));
snprintf(pac_u->dns_c, 256, "%s.%s", nam_c + 1, bos_c);
// u3l_log("czar %s, dns %s\n", nam_c, pac_u->dns_c);
free(nam_c);
u3z(nam);
{
uv_getaddrinfo_t* adr_u = c3_malloc(sizeof(*adr_u));
adr_u->data = pac_u;
c3_i sas_i;
if ( 0 != (sas_i = uv_getaddrinfo(u3L, adr_u,
_ames_czar_cb,
pac_u->dns_c, 0, 0)) ) {
u3l_log("ames: %s\n", uv_strerror(sas_i));
_ames_czar_gone(pac_u, now);
return;
}
}
}
else {
pac_u->pip_w = sam_u->imp_w[pac_u->imp_y];
_ames_send(pac_u);
return;
}
}
/* _ames_lane_ipv4(): IPv4 address/ from lane.
*/
u3_noun
_ames_lane_ip(u3_noun lan, c3_s* por_s, c3_w* pip_w)
{
switch ( u3h(lan) ) {
case c3__if: {
*por_s= (c3_s) u3h(u3t(u3t(lan)));
*pip_w = u3r_word(0, u3t(u3t(u3t(lan))));
return c3y;
} break;
case c3__is: {
u3_noun pq_lan = u3h(u3t(u3t(lan)));
if ( u3_nul == pq_lan ) return c3n;
else return _ames_lane_ip(u3t(pq_lan), por_s, pip_w);
} break;
case c3__ix: {
*por_s = (c3_s) u3h(u3t(u3t(lan)));
*pip_w = u3r_word(0, u3t(u3t(u3t(lan))));
return c3y;
} break;
}
return c3n;
}
/* u3_ames_ef_bake(): notify %ames that we're live.
*/
void
u3_ames_ef_bake(u3_pier* pir_u)
{
u3_noun pax = u3nq(u3_blip, c3__newt, u3k(u3A->sen), u3_nul);
u3_pier_plan(pax, u3nc(c3__barn, u3_nul));
}
/* u3_ames_ef_send(): send packet to network (v4).
*/
void
u3_ames_ef_send(u3_pier* pir_u, u3_noun lan, u3_noun pac)
{
u3_ames* sam_u = pir_u->sam_u;
if ( u3_Host.ops_u.fuz_w && ((rand() % 100) < u3_Host.ops_u.fuz_w) ) {
u3z(lan); u3z(pac);
return;
}
u3_pact* pac_u = c3_calloc(sizeof(*pac_u));
if ( c3y == _ames_lane_ip(lan, &pac_u->por_s, &pac_u->pip_w) ) {
pac_u->len_w = u3r_met(3, pac);
pac_u->hun_y = c3_malloc(pac_u->len_w);
u3r_bytes(0, pac_u->len_w, pac_u->hun_y, pac);
if ( 0 == pac_u->pip_w ) {
pac_u->pip_w = 0x7f000001;
pac_u->por_s = pir_u->por_s;
}
if ( (0 == (pac_u->pip_w >> 16)) && (1 == (pac_u->pip_w >> 8)) ) {
pac_u->imp_y = (pac_u->pip_w & 0xff);
_ames_czar(pac_u, sam_u->dns_c);
}
else if ( (c3y == u3_Host.ops_u.net) || (0x7f000001 == pac_u->pip_w) ) {
_ames_send(pac_u);
}
else {
// networking disabled
_ames_pact_free(pac_u);
}
}
else {
_ames_pact_free(pac_u);
}
u3z(lan); u3z(pac);
}
/* _ames_recv_cb(): receive callback.
*/
static void
_ames_recv_cb(uv_udp_t* wax_u,
ssize_t nrd_i,
const uv_buf_t * buf_u,
const struct sockaddr* adr_u,
unsigned flg_i)
{
// u3l_log("ames: rx %p\r\n", buf_u.base);
if ( 0 == nrd_i ) {
_ames_free(buf_u->base);
}
else {
{
u3_noun msg = u3i_bytes((c3_w)nrd_i, (c3_y*)buf_u->base);
// u3l_log("ames: plan\r\n");
#if 0
u3z(msg);
#else
struct sockaddr_in* add_u = (struct sockaddr_in *)adr_u;
c3_s por_s = ntohs(add_u->sin_port);
c3_w pip_w = ntohl(add_u->sin_addr.s_addr);
u3_pier_plan
(u3nt(u3_blip, c3__ames, u3_nul),
u3nt(c3__hear,
u3nq(c3__if, u3k(u3A->now), por_s, u3i_words(1, &pip_w)),
msg));
#endif
}
_ames_free(buf_u->base);
}
}
/* _ames_io_start(): initialize ames I/O.
*/
static void
_ames_io_start(u3_pier* pir_u)
{
u3_ames* sam_u = pir_u->sam_u;
c3_s por_s = pir_u->por_s;
u3_noun who = u3i_chubs(2, pir_u->who_d);
u3_noun rac = u3do("clan:title", u3k(who));
if ( c3__czar == rac ) {
u3_noun imp = u3dc("scot", 'p', u3k(who));
c3_c* imp_c = u3r_string(imp);
c3_y num_y = (c3_y)pir_u->who_d[0];
por_s = _ames_czar_port(num_y);
if ( c3y == u3_Host.ops_u.net ) {
u3l_log("ames: czar: %s on %d\n", imp_c, por_s);
}
else {
u3l_log("ames: czar: %s on %d (localhost only)\n", imp_c, por_s);
}
u3z(imp);
free(imp_c);
}
int ret;
if ( 0 != (ret = uv_udp_init(u3L, &sam_u->wax_u)) ) {
u3l_log("ames: init: %s\n", uv_strerror(ret));
c3_assert(0);
}
// Bind and stuff.
{
struct sockaddr_in add_u;
c3_i add_i = sizeof(add_u);
memset(&add_u, 0, sizeof(add_u));
add_u.sin_family = AF_INET;
add_u.sin_addr.s_addr = _(u3_Host.ops_u.net) ?
htonl(INADDR_ANY) :
htonl(INADDR_LOOPBACK);
add_u.sin_port = htons(por_s);
int ret;
if ( (ret = uv_udp_bind(&sam_u->wax_u,
(const struct sockaddr*) & add_u, 0)) != 0 ) {
u3l_log("ames: bind: %s\n",
uv_strerror(ret));
if (UV_EADDRINUSE == ret){
u3l_log(" ...perhaps you've got two copies of vere running?\n");
}
u3_pier_exit(pir_u);
}
uv_udp_getsockname(&sam_u->wax_u, (struct sockaddr *)&add_u, &add_i);
c3_assert(add_u.sin_port);
sam_u->por_s = ntohs(add_u.sin_port);
}
// u3l_log("ames: on localhost, UDP %d.\n", sam_u->por_s);
uv_udp_recv_start(&sam_u->wax_u, _ames_alloc, _ames_recv_cb);
sam_u->liv = c3y;
u3z(rac);
u3z(who);
}
/* _cttp_mcut_char(): measure/cut character.
*/
static c3_w
_cttp_mcut_char(c3_c* buf_c, c3_w len_w, c3_c chr_c)
{
if ( buf_c ) {
buf_c[len_w] = chr_c;
}
return len_w + 1;
}
/* _cttp_mcut_cord(): measure/cut cord.
*/
static c3_w
_cttp_mcut_cord(c3_c* buf_c, c3_w len_w, u3_noun san)
{
c3_w ten_w = u3r_met(3, san);
if ( buf_c ) {
u3r_bytes(0, ten_w, (c3_y *)(buf_c + len_w), san);
}
u3z(san);
return (len_w + ten_w);
}
/* _cttp_mcut_path(): measure/cut cord list.
*/
static c3_w
_cttp_mcut_path(c3_c* buf_c, c3_w len_w, c3_c sep_c, u3_noun pax)
{
u3_noun axp = pax;
while ( u3_nul != axp ) {
u3_noun h_axp = u3h(axp);
len_w = _cttp_mcut_cord(buf_c, len_w, u3k(h_axp));
axp = u3t(axp);
if ( u3_nul != axp ) {
len_w = _cttp_mcut_char(buf_c, len_w, sep_c);
}
}
u3z(pax);
return len_w;
}
/* _cttp_mcut_host(): measure/cut host.
*/
static c3_w
_cttp_mcut_host(c3_c* buf_c, c3_w len_w, u3_noun hot)
{
len_w = _cttp_mcut_path(buf_c, len_w, '.', u3kb_flop(u3k(hot)));
u3z(hot);
return len_w;
}
/* u3_ames_ef_turf(): initialize ames I/O on domain(s).
*/
void
u3_ames_ef_turf(u3_pier* pir_u, u3_noun tuf)
{
u3_ames* sam_u = pir_u->sam_u;
if ( u3_nul != tuf ) {
// XX save all for fallback, not just first
u3_noun hot = u3k(u3h(tuf));
c3_w len_w = _cttp_mcut_host(0, 0, u3k(hot));
sam_u->dns_c = c3_malloc(1 + len_w);
_cttp_mcut_host(sam_u->dns_c, 0, hot);
sam_u->dns_c[len_w] = 0;
u3z(tuf);
}
else if ( (c3n == pir_u->fak_o) && (0 == sam_u->dns_c) ) {
u3l_log("ames: turf: no domains\n");
}
if ( c3n == sam_u->liv ) {
_ames_io_start(pir_u);
}
}
/* u3_ames_io_init(): initialize ames I/O.
*/
void
u3_ames_io_init(u3_pier* pir_u)
{
u3_ames* sam_u = pir_u->sam_u;
sam_u->liv = c3n;
}
/* u3_ames_io_talk(): start receiving ames traffic.
*/
void
u3_ames_io_talk(u3_pier* pir_u)
{
}
/* u3_ames_io_exit(): terminate ames I/O.
*/
void
u3_ames_io_exit(u3_pier* pir_u)
{
u3_ames* sam_u = pir_u->sam_u;
if ( c3y == sam_u->liv ) {
// XX remove had_u/wax_u union, cast and close wax_u
uv_close(&sam_u->had_u, 0);
}
}

View File

@ -0,0 +1,90 @@
/* vere/behn.c
**
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <uv.h>
#include <ncurses/curses.h>
#include <termios.h>
#include <ncurses/term.h>
#include <errno.h>
#include "all.h"
#include "vere/vere.h"
/* u3_behn(): initialize time timer.
*/
void
u3_behn_io_init(u3_pier *pir_u)
{
u3_behn* teh_u = pir_u->teh_u;
teh_u->alm = c3n;
uv_timer_init(u3L, &teh_u->tim_u);
teh_u->tim_u.data = pir_u;
}
/* u3_behn_io_exit(): terminate timer.
*/
void
u3_behn_io_exit(u3_pier *pir_u)
{
}
/* _behn_time_cb(): timer callback.
*/
static void
_behn_time_cb(uv_timer_t* tim_u)
{
u3_pier *pir_u = tim_u->data;
u3_behn* teh_u = pir_u->teh_u;
teh_u->alm = c3n;
{
u3_pier_work
(pir_u,
u3nt(u3_blip, c3__behn, u3_nul),
u3nc(c3__wake, u3_nul));
}
}
/* u3_behn_ef_doze(): set or cancel timer
*/
void
u3_behn_ef_doze(u3_pier *pir_u, u3_noun wen)
{
u3_behn* teh_u = pir_u->teh_u;
if ( c3y == teh_u->alm ) {
uv_timer_stop(&teh_u->tim_u);
teh_u->alm = c3n;
}
if ( (u3_nul != wen) &&
(c3y == u3du(wen)) &&
(c3y == u3ud(u3t(wen))) )
{
struct timeval tim_tv;
gettimeofday(&tim_tv, 0);
u3_noun now = u3_time_in_tv(&tim_tv);
c3_d gap_d = u3_time_gap_ms(now, u3k(u3t(wen)));
teh_u->alm = c3y;
uv_timer_start(&teh_u->tim_u, _behn_time_cb, gap_d, 0);
}
u3z(wen);
}
/* u3_behn_ef_bake(): notify %behn that we're live
*/
void
u3_behn_ef_bake(u3_pier *pir_u)
{
u3_noun pax = u3nq(u3_blip, c3__behn, u3k(u3A->sen), u3_nul);
u3_pier_work(pir_u, pax, u3nc(c3__born, u3_nul));
}

989
pkg/hs/vere/notes/c/cttp.c Normal file
View File

@ -0,0 +1,989 @@
/* vere/cttp.c
**
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <uv.h>
#include <errno.h>
#include <openssl/ssl.h>
#include <h2o.h>
#include "all.h"
#include "vere/vere.h"
// XX deduplicate with _http_vec_to_atom
/* _cttp_vec_to_atom(): convert h2o_iovec_t to atom (cord)
*/
static u3_noun
_cttp_vec_to_atom(h2o_iovec_t vec_u)
{
return u3i_bytes(vec_u.len, (const c3_y*)vec_u.base);
}
/* _cttp_bods_free(): free body structure.
*/
static void
_cttp_bods_free(u3_hbod* bod_u)
{
while ( bod_u ) {
u3_hbod* nex_u = bod_u->nex_u;
free(bod_u);
bod_u = nex_u;
}
}
/* _cttp_bod_new(): create a data buffer
*/
static u3_hbod*
_cttp_bod_new(c3_w len_w, c3_c* hun_c)
{
u3_hbod* bod_u = c3_malloc(1 + len_w + sizeof(*bod_u));
bod_u->hun_y[len_w] = 0;
bod_u->len_w = len_w;
memcpy(bod_u->hun_y, (const c3_y*)hun_c, len_w);
bod_u->nex_u = 0;
return bod_u;
}
/* _cttp_bod_from_hed(): create a data buffer from a header
*/
static u3_hbod*
_cttp_bod_from_hed(u3_hhed* hed_u)
{
c3_w len_w = hed_u->nam_w + 2 + hed_u->val_w + 2;
u3_hbod* bod_u = c3_malloc(1 + len_w + sizeof(*bod_u));
bod_u->hun_y[len_w] = 0;
memcpy(bod_u->hun_y, hed_u->nam_c, hed_u->nam_w);
memcpy(bod_u->hun_y + hed_u->nam_w, ": ", 2);
memcpy(bod_u->hun_y + hed_u->nam_w + 2, hed_u->val_c, hed_u->val_w);
memcpy(bod_u->hun_y + hed_u->nam_w + 2 + hed_u->val_w, "\r\n", 2);
bod_u->len_w = len_w;
bod_u->nex_u = 0;
return bod_u;
}
/* _cttp_bods_to_octs: translate body buffer into octet-stream noun.
*/
static u3_noun
_cttp_bods_to_octs(u3_hbod* bod_u)
{
c3_w len_w;
c3_y* buf_y;
u3_noun cos;
{
u3_hbod* bid_u = bod_u;
len_w = 0;
while ( bid_u ) {
len_w += bid_u->len_w;
bid_u = bid_u->nex_u;
}
}
buf_y = c3_malloc(1 + len_w);
buf_y[len_w] = 0;
{
c3_y* ptr_y = buf_y;
while ( bod_u ) {
memcpy(ptr_y, bod_u->hun_y, bod_u->len_w);
ptr_y += bod_u->len_w;
bod_u = bod_u->nex_u;
}
}
cos = u3i_bytes(len_w, buf_y);
free(buf_y);
return u3nc(len_w, cos);
}
/* _cttp_bod_from_octs(): translate octet-stream noun into body.
*/
static u3_hbod*
_cttp_bod_from_octs(u3_noun oct)
{
c3_w len_w;
if ( !_(u3a_is_cat(u3h(oct))) ) { // 2GB max
u3m_bail(c3__fail); return 0;
}
len_w = u3h(oct);
{
u3_hbod* bod_u = c3_malloc(1 + len_w + sizeof(*bod_u));
bod_u->hun_y[len_w] = 0;
bod_u->len_w = len_w;
u3r_bytes(0, len_w, bod_u->hun_y, u3t(oct));
bod_u->nex_u = 0;
u3z(oct);
return bod_u;
}
}
/* _cttp_bods_to_vec(): translate body buffers to array of h2o_iovec_t
*/
static h2o_iovec_t*
_cttp_bods_to_vec(u3_hbod* bod_u, c3_w* tot_w)
{
h2o_iovec_t* vec_u;
c3_w len_w;
{
u3_hbod* bid_u = bod_u;
len_w = 0;
while( bid_u ) {
len_w++;
bid_u = bid_u->nex_u;
}
}
if ( 0 == len_w ) {
*tot_w = len_w;
return 0;
}
vec_u = c3_malloc(sizeof(h2o_iovec_t) * len_w);
len_w = 0;
while( bod_u ) {
vec_u[len_w] = h2o_iovec_init(bod_u->hun_y, bod_u->len_w);
len_w++;
bod_u = bod_u->nex_u;
}
*tot_w = len_w;
return vec_u;
}
// XX deduplicate with _http_heds_free
/* _cttp_heds_free(): free header linked list
*/
static void
_cttp_heds_free(u3_hhed* hed_u)
{
while ( hed_u ) {
u3_hhed* nex_u = hed_u->nex_u;
free(hed_u->nam_c);
free(hed_u->val_c);
free(hed_u);
hed_u = nex_u;
}
}
// XX deduplicate with _http_hed_new
/* _cttp_hed_new(): create u3_hhed from nam/val cords
*/
static u3_hhed*
_cttp_hed_new(u3_atom nam, u3_atom val)
{
c3_w nam_w = u3r_met(3, nam);
c3_w val_w = u3r_met(3, val);
u3_hhed* hed_u = c3_malloc(sizeof(*hed_u));
hed_u->nam_c = c3_malloc(1 + nam_w);
hed_u->val_c = c3_malloc(1 + val_w);
hed_u->nam_c[nam_w] = 0;
hed_u->val_c[val_w] = 0;
hed_u->nex_u = 0;
hed_u->nam_w = nam_w;
hed_u->val_w = val_w;
u3r_bytes(0, nam_w, (c3_y*)hed_u->nam_c, nam);
u3r_bytes(0, val_w, (c3_y*)hed_u->val_c, val);
return hed_u;
}
// XX vv similar to _http_heds_from_noun
/* _cttp_heds_math(): create headers from +math
*/
static u3_hhed*
_cttp_heds_math(u3_noun mah)
{
u3_noun hed = u3kdi_tap(mah);
u3_noun deh = hed;
u3_hhed* hed_u = 0;
while ( u3_nul != hed ) {
u3_noun nam = u3h(u3h(hed));
u3_noun lit = u3t(u3h(hed));
while ( u3_nul != lit ) {
u3_hhed* nex_u = _cttp_hed_new(nam, u3h(lit));
nex_u->nex_u = hed_u;
hed_u = nex_u;
lit = u3t(lit);
}
hed = u3t(hed);
}
u3z(deh);
return hed_u;
}
// XX deduplicate with _http_heds_to_noun
/* _cttp_heds_to_noun(): convert h2o_header_t to (list (pair @t @t))
*/
static u3_noun
_cttp_heds_to_noun(h2o_header_t* hed_u, c3_d hed_d)
{
u3_noun hed = u3_nul;
c3_d dex_d = hed_d;
h2o_header_t deh_u;
while ( 0 < dex_d ) {
deh_u = hed_u[--dex_d];
hed = u3nc(u3nc(_cttp_vec_to_atom(*deh_u.name),
_cttp_vec_to_atom(deh_u.value)), hed);
}
return hed;
}
/* _cttp_cres_free(): free a u3_cres.
*/
static void
_cttp_cres_free(u3_cres* res_u)
{
_cttp_bods_free(res_u->bod_u);
free(res_u);
}
/* _cttp_cres_new(): create a response
*/
static void
_cttp_cres_new(u3_creq* ceq_u, c3_w sas_w)
{
ceq_u->res_u = c3_calloc(sizeof(*ceq_u->res_u));
ceq_u->res_u->sas_w = sas_w;
}
/* _cttp_cres_fire_body(): attach response body buffer
*/
static void
_cttp_cres_fire_body(u3_cres* res_u, u3_hbod* bod_u)
{
c3_assert(!bod_u->nex_u);
if ( !(res_u->bod_u) ) {
res_u->bod_u = res_u->dob_u = bod_u;
}
else {
res_u->dob_u->nex_u = bod_u;
res_u->dob_u = bod_u;
}
}
/* _cttp_mcut_char(): measure/cut character.
*/
static c3_w
_cttp_mcut_char(c3_c* buf_c, c3_w len_w, c3_c chr_c)
{
if ( buf_c ) {
buf_c[len_w] = chr_c;
}
return len_w + 1;
}
/* _cttp_mcut_cord(): measure/cut cord.
*/
static c3_w
_cttp_mcut_cord(c3_c* buf_c, c3_w len_w, u3_noun san)
{
c3_w ten_w = u3r_met(3, san);
if ( buf_c ) {
u3r_bytes(0, ten_w, (c3_y *)(buf_c + len_w), san);
}
u3z(san);
return (len_w + ten_w);
}
/* _cttp_mcut_path(): measure/cut cord list.
*/
static c3_w
_cttp_mcut_path(c3_c* buf_c, c3_w len_w, c3_c sep_c, u3_noun pax)
{
u3_noun axp = pax;
while ( u3_nul != axp ) {
u3_noun h_axp = u3h(axp);
len_w = _cttp_mcut_cord(buf_c, len_w, u3k(h_axp));
axp = u3t(axp);
if ( u3_nul != axp ) {
len_w = _cttp_mcut_char(buf_c, len_w, sep_c);
}
}
u3z(pax);
return len_w;
}
/* _cttp_mcut_host(): measure/cut host.
*/
static c3_w
_cttp_mcut_host(c3_c* buf_c, c3_w len_w, u3_noun hot)
{
len_w = _cttp_mcut_path(buf_c, len_w, '.', u3kb_flop(u3k(hot)));
u3z(hot);
return len_w;
}
/* _cttp_mcut_pork(): measure/cut path/extension.
*/
static c3_w
_cttp_mcut_pork(c3_c* buf_c, c3_w len_w, u3_noun pok)
{
u3_noun h_pok = u3h(pok);
u3_noun t_pok = u3t(pok);
len_w = _cttp_mcut_path(buf_c, len_w, '/', u3k(t_pok));
if ( u3_nul != h_pok ) {
len_w = _cttp_mcut_char(buf_c, len_w, '.');
len_w = _cttp_mcut_cord(buf_c, len_w, u3k(u3t(h_pok)));
}
u3z(pok);
return len_w;
}
/* _cttp_mcut_quay(): measure/cut query.
*/
static c3_w
_cttp_mcut_quay(c3_c* buf_c, c3_w len_w, u3_noun quy)
{
if ( u3_nul == quy ) {
return len_w;
}
else {
u3_noun i_quy = u3h(quy);
u3_noun pi_quy = u3h(i_quy);
u3_noun qi_quy = u3t(i_quy);
u3_noun t_quy = u3t(quy);
len_w = _cttp_mcut_char(buf_c, len_w, '&');
len_w = _cttp_mcut_cord(buf_c, len_w, u3k(pi_quy));
len_w = _cttp_mcut_char(buf_c, len_w, '=');
len_w = _cttp_mcut_cord(buf_c, len_w, u3k(qi_quy));
len_w = _cttp_mcut_quay(buf_c, len_w, u3k(t_quy));
}
u3z(quy);
return len_w;
}
/* _cttp_mcut_url(): measure/cut purl, producing relative URL.
*/
static c3_w
_cttp_mcut_url(c3_c* buf_c, c3_w len_w, u3_noun pul)
{
u3_noun q_pul = u3h(u3t(pul));
u3_noun r_pul = u3t(u3t(pul));
len_w = _cttp_mcut_char(buf_c, len_w, '/');
len_w = _cttp_mcut_pork(buf_c, len_w, u3k(q_pul));
if ( u3_nul != r_pul ) {
len_w = _cttp_mcut_char(buf_c, len_w, '?');
len_w = _cttp_mcut_quay(buf_c, len_w, u3k(r_pul));
}
u3z(pul);
return len_w;
}
/* _cttp_creq_port(): stringify port
*/
static c3_c*
_cttp_creq_port(c3_s por_s)
{
c3_c* por_c = c3_malloc(8);
snprintf(por_c, 7, "%d", 0xffff & por_s);
return por_c;
}
/* _cttp_creq_url(): construct url from noun.
*/
static c3_c*
_cttp_creq_url(u3_noun pul)
{
c3_w len_w = _cttp_mcut_url(0, 0, u3k(pul));
c3_c* url_c = c3_malloc(1 + len_w);
_cttp_mcut_url(url_c, 0, pul);
url_c[len_w] = 0;
return url_c;
}
/* _cttp_creq_host(): construct host from noun.
*/
static c3_c*
_cttp_creq_host(u3_noun hot)
{
c3_w len_w = _cttp_mcut_host(0, 0, u3k(hot));
c3_c* hot_c = c3_malloc(1 + len_w);
_cttp_mcut_host(hot_c, 0, hot);
hot_c[len_w] = 0;
return hot_c;
}
/* _cttp_creq_ip(): stringify ip
*/
static c3_c*
_cttp_creq_ip(c3_w ipf_w)
{
c3_c* ipf_c = c3_malloc(17);
snprintf(ipf_c, 16, "%d.%d.%d.%d", (ipf_w >> 24),
((ipf_w >> 16) & 255),
((ipf_w >> 8) & 255),
(ipf_w & 255));
return ipf_c;
}
/* _cttp_creq_find(): find a request by number in the client
*/
static u3_creq*
_cttp_creq_find(c3_l num_l)
{
u3_creq* ceq_u = u3_Host.ctp_u.ceq_u;
// XX glories of linear search
//
while ( ceq_u ) {
if ( num_l == ceq_u->num_l ) {
return ceq_u;
}
ceq_u = ceq_u->nex_u;
}
return 0;
}
/* _cttp_creq_link(): link request to client
*/
static void
_cttp_creq_link(u3_creq* ceq_u)
{
ceq_u->nex_u = u3_Host.ctp_u.ceq_u;
if ( 0 != ceq_u->nex_u ) {
ceq_u->nex_u->pre_u = ceq_u;
}
u3_Host.ctp_u.ceq_u = ceq_u;
}
/* _cttp_creq_unlink(): unlink request from client
*/
static void
_cttp_creq_unlink(u3_creq* ceq_u)
{
if ( ceq_u->pre_u ) {
ceq_u->pre_u->nex_u = ceq_u->nex_u;
if ( 0 != ceq_u->nex_u ) {
ceq_u->nex_u->pre_u = ceq_u->pre_u;
}
}
else {
u3_Host.ctp_u.ceq_u = ceq_u->nex_u;
if ( 0 != ceq_u->nex_u ) {
ceq_u->nex_u->pre_u = 0;
}
}
}
/* _cttp_creq_free(): free a u3_creq.
*/
static void
_cttp_creq_free(u3_creq* ceq_u)
{
_cttp_creq_unlink(ceq_u);
_cttp_heds_free(ceq_u->hed_u);
// Note: ceq_u->bod_u is covered here
_cttp_bods_free(ceq_u->rub_u);
if ( ceq_u->res_u ) {
_cttp_cres_free(ceq_u->res_u);
}
free(ceq_u->hot_c);
free(ceq_u->por_c);
free(ceq_u->url_c);
free(ceq_u->vec_u);
free(ceq_u);
}
/* _cttp_creq_new(): create a request from a +hiss noun
*/
static u3_creq*
_cttp_creq_new(c3_l num_l, u3_noun hes)
{
u3_creq* ceq_u = c3_calloc(sizeof(*ceq_u));
u3_noun pul = u3h(hes); // +purl
u3_noun hat = u3h(pul); // +hart
u3_noun sec = u3h(hat);
u3_noun por = u3h(u3t(hat));
u3_noun hot = u3t(u3t(hat)); // +host
u3_noun moh = u3t(hes); // +moth
u3_noun met = u3h(moh); // +meth
u3_noun mah = u3h(u3t(moh)); // +math
u3_noun bod = u3t(u3t(moh));
ceq_u->sat_e = u3_csat_init;
ceq_u->num_l = num_l;
ceq_u->sec = sec;
if ( c3y == u3h(hot) ) {
ceq_u->hot_c = _cttp_creq_host(u3k(u3t(hot)));
} else {
ceq_u->ipf_w = u3r_word(0, u3t(hot));
ceq_u->ipf_c = _cttp_creq_ip(ceq_u->ipf_w);
}
if ( u3_nul != por ) {
ceq_u->por_s = u3t(por);
ceq_u->por_c = _cttp_creq_port(ceq_u->por_s);
}
ceq_u->met_m = met;
ceq_u->url_c = _cttp_creq_url(u3k(pul));
ceq_u->hed_u = _cttp_heds_math(u3k(mah));
if ( u3_nul != bod ) {
ceq_u->bod_u = _cttp_bod_from_octs(u3k(u3t(bod)));
}
_cttp_creq_link(ceq_u);
u3z(hes);
return ceq_u;
}
/* _cttp_creq_fire_body(): attach body to request buffers.
*/
static void
_cttp_creq_fire_body(u3_creq* ceq_u, u3_hbod *rub_u)
{
c3_assert(!rub_u->nex_u);
if ( !(ceq_u->rub_u) ) {
ceq_u->rub_u = ceq_u->bur_u = rub_u;
}
else {
ceq_u->bur_u->nex_u = rub_u;
ceq_u->bur_u = rub_u;
}
}
/* _cttp_creq_fire_str(): attach string to request buffers.
*/
static void
_cttp_creq_fire_str(u3_creq* ceq_u, c3_c* str_c)
{
_cttp_creq_fire_body(ceq_u, _cttp_bod_new(strlen(str_c), str_c));
}
/* _cttp_creq_fire_heds(): attach output headers.
*/
static void
_cttp_creq_fire_heds(u3_creq* ceq_u, u3_hhed* hed_u)
{
while ( hed_u ) {
_cttp_creq_fire_body(ceq_u, _cttp_bod_from_hed(hed_u));
hed_u = hed_u->nex_u;
}
}
/* _cttp_creq_fire(): load request data for into buffers.
*/
static void
_cttp_creq_fire(u3_creq* ceq_u)
{
switch ( ceq_u->met_m ) {
default: c3_assert(0);
case c3__get: _cttp_creq_fire_str(ceq_u, "GET "); break;
case c3__put: _cttp_creq_fire_str(ceq_u, "PUT "); break;
case c3__post: _cttp_creq_fire_str(ceq_u, "POST "); break;
case c3__head: _cttp_creq_fire_str(ceq_u, "HEAD "); break;
case c3__conn: _cttp_creq_fire_str(ceq_u, "CONNECT "); break;
case c3__delt: _cttp_creq_fire_str(ceq_u, "DELETE "); break;
case c3__opts: _cttp_creq_fire_str(ceq_u, "OPTIONS "); break;
case c3__trac: _cttp_creq_fire_str(ceq_u, "TRACE "); break;
}
_cttp_creq_fire_str(ceq_u, ceq_u->url_c);
_cttp_creq_fire_str(ceq_u, " HTTP/1.1\r\n");
{
c3_c* hot_c = ceq_u->hot_c ? ceq_u->hot_c : ceq_u->ipf_c;
c3_c* hos_c;
c3_w len_w;
if ( ceq_u->por_c ) {
len_w = 6 + strlen(hot_c) + 1 + strlen(ceq_u->por_c) + 3;
hos_c = c3_malloc(len_w);
len_w = snprintf(hos_c, len_w, "Host: %s:%s\r\n", hot_c, ceq_u->por_c);
}
else {
len_w = 6 + strlen(hot_c) + 3;
hos_c = c3_malloc(len_w);
len_w = snprintf(hos_c, len_w, "Host: %s\r\n", hot_c);
}
_cttp_creq_fire_body(ceq_u, _cttp_bod_new(len_w, hos_c));
free(hos_c);
}
_cttp_creq_fire_heds(ceq_u, ceq_u->hed_u);
if ( !ceq_u->bod_u ) {
_cttp_creq_fire_body(ceq_u, _cttp_bod_new(2, "\r\n"));
}
else {
c3_c len_c[41];
c3_w len_w = snprintf(len_c, 40, "Content-Length: %u\r\n\r\n",
ceq_u->bod_u->len_w);
_cttp_creq_fire_body(ceq_u, _cttp_bod_new(len_w, len_c));
_cttp_creq_fire_body(ceq_u, ceq_u->bod_u);
}
}
/* _cttp_creq_quit(): cancel a u3_creq
*/
static void
_cttp_creq_quit(u3_creq* ceq_u)
{
if ( u3_csat_addr == ceq_u->sat_e ) {
ceq_u->sat_e = u3_csat_quit;
return; // wait to be called again on address resolution
}
if ( ceq_u->cli_u ) {
h2o_http1client_cancel(ceq_u->cli_u);
}
_cttp_creq_free(ceq_u);
}
/* _cttp_httr(): dispatch http response to %eyre
*/
static void
_cttp_httr(c3_l num_l, c3_w sas_w, u3_noun mes, u3_noun uct)
{
u3_noun htr = u3nt(sas_w, mes, uct);
u3_noun pox = u3nt(u3_blip, c3__http, u3_nul);
u3_pier_plan(pox, u3nt(c3__they, num_l, htr));
}
/* _cttp_creq_quit(): dispatch error response
*/
static void
_cttp_creq_fail(u3_creq* ceq_u, const c3_c* err_c)
{
// XX anything other than a 504?
c3_w cod_w = 504;
u3l_log("http: fail (%d, %d): %s\r\n", ceq_u->num_l, cod_w, err_c);
// XX include err_c as response body?
_cttp_httr(ceq_u->num_l, cod_w, u3_nul, u3_nul);
_cttp_creq_free(ceq_u);
}
/* _cttp_creq_quit(): dispatch response
*/
static void
_cttp_creq_respond(u3_creq* ceq_u)
{
u3_cres* res_u = ceq_u->res_u;
_cttp_httr(ceq_u->num_l, res_u->sas_w, res_u->hed,
( !res_u->bod_u ) ? u3_nul :
u3nc(u3_nul, _cttp_bods_to_octs(res_u->bod_u)));
_cttp_creq_free(ceq_u);
}
// XX research: may be called with closed client?
/* _cttp_creq_on_body(): cb invoked by h2o upon receiving a response body
*/
static c3_i
_cttp_creq_on_body(h2o_http1client_t* cli_u, const c3_c* err_c)
{
u3_creq* ceq_u = (u3_creq *)cli_u->data;
if ( 0 != err_c && h2o_http1client_error_is_eos != err_c ) {
_cttp_creq_fail(ceq_u, err_c);
return -1;
}
h2o_buffer_t* buf_u = cli_u->sock->input;
if ( buf_u->size ) {
_cttp_cres_fire_body(ceq_u->res_u,
_cttp_bod_new(buf_u->size, buf_u->bytes));
h2o_buffer_consume(&cli_u->sock->input, buf_u->size);
}
if ( h2o_http1client_error_is_eos == err_c ) {
_cttp_creq_respond(ceq_u);
}
return 0;
}
/* _cttp_creq_on_head(): cb invoked by h2o upon receiving response headers
*/
static h2o_http1client_body_cb
_cttp_creq_on_head(h2o_http1client_t* cli_u, const c3_c* err_c, c3_i ver_i,
c3_i sas_i, h2o_iovec_t sas_u, h2o_header_t* hed_u,
size_t hed_t, c3_i len_i)
{
u3_creq* ceq_u = (u3_creq *)cli_u->data;
if ( 0 != err_c && h2o_http1client_error_is_eos != err_c ) {
_cttp_creq_fail(ceq_u, err_c);
return 0;
}
_cttp_cres_new(ceq_u, (c3_w)sas_i);
ceq_u->res_u->hed = _cttp_heds_to_noun(hed_u, hed_t);
if ( h2o_http1client_error_is_eos == err_c ) {
_cttp_creq_respond(ceq_u);
return 0;
}
return _cttp_creq_on_body;
}
/* _cttp_creq_on_connect(): cb invoked by h2o upon successful connection
*/
static h2o_http1client_head_cb
_cttp_creq_on_connect(h2o_http1client_t* cli_u, const c3_c* err_c,
h2o_iovec_t** vec_p, size_t* vec_t, c3_i* hed_i)
{
u3_creq* ceq_u = (u3_creq *)cli_u->data;
if ( 0 != err_c ) {
_cttp_creq_fail(ceq_u, err_c);
return 0;
}
{
c3_w len_w;
ceq_u->vec_u = _cttp_bods_to_vec(ceq_u->rub_u, &len_w);
*vec_t = len_w;
*vec_p = ceq_u->vec_u;
*hed_i = c3__head == ceq_u->met_m;
}
return _cttp_creq_on_head;
}
/* _cttp_creq_connect(): establish connection
*/
static void
_cttp_creq_connect(u3_creq* ceq_u)
{
c3_assert(u3_csat_ripe == ceq_u->sat_e);
c3_assert(ceq_u->ipf_c);
h2o_iovec_t ipf_u = h2o_iovec_init(ceq_u->ipf_c, strlen(ceq_u->ipf_c));
c3_s por_s = ceq_u->por_s ? ceq_u->por_s :
( c3y == ceq_u->sec ) ? 443 : 80;
// connect by IP
h2o_http1client_connect(&ceq_u->cli_u, ceq_u, u3_Host.ctp_u.ctx_u, ipf_u,
por_s, c3y == ceq_u->sec, _cttp_creq_on_connect);
// set hostname for TLS handshake
if ( ceq_u->hot_c && c3y == ceq_u->sec ) {
c3_w len_w = 1 + strlen(ceq_u->hot_c);
c3_c* hot_c = c3_malloc(len_w);
strncpy(hot_c, ceq_u->hot_c, len_w);
free(ceq_u->cli_u->ssl.server_name);
ceq_u->cli_u->ssl.server_name = hot_c;
}
_cttp_creq_fire(ceq_u);
}
/* _cttp_creq_resolve_cb(): cb upon IP address resolution
*/
static void
_cttp_creq_resolve_cb(uv_getaddrinfo_t* adr_u,
c3_i sas_i,
struct addrinfo* aif_u)
{
u3_creq* ceq_u = adr_u->data;
if ( u3_csat_quit == ceq_u->sat_e ) {
_cttp_creq_quit(ceq_u);;
}
else if ( 0 != sas_i ) {
_cttp_creq_fail(ceq_u, uv_strerror(sas_i));
}
else {
// XX traverse struct a la _ames_czar_cb
ceq_u->ipf_w = ntohl(((struct sockaddr_in *)aif_u->ai_addr)->sin_addr.s_addr);
ceq_u->ipf_c = _cttp_creq_ip(ceq_u->ipf_w);
ceq_u->sat_e = u3_csat_ripe;
_cttp_creq_connect(ceq_u);
}
free(adr_u);
uv_freeaddrinfo(aif_u);
}
/* _cttp_creq_resolve(): resolve hostname to IP address
*/
static void
_cttp_creq_resolve(u3_creq* ceq_u)
{
c3_assert(u3_csat_addr == ceq_u->sat_e);
c3_assert(ceq_u->hot_c);
uv_getaddrinfo_t* adr_u = c3_malloc(sizeof(*adr_u));
adr_u->data = ceq_u;
struct addrinfo hin_u;
memset(&hin_u, 0, sizeof(struct addrinfo));
hin_u.ai_family = PF_INET;
hin_u.ai_socktype = SOCK_STREAM;
hin_u.ai_protocol = IPPROTO_TCP;
// XX is this necessary?
c3_c* por_c = ceq_u->por_c ? ceq_u->por_c :
( c3y == ceq_u->sec ) ? "443" : "80";
c3_i sas_i;
if ( 0 != (sas_i = uv_getaddrinfo(u3L, adr_u, _cttp_creq_resolve_cb,
ceq_u->hot_c, por_c, &hin_u)) ) {
_cttp_creq_fail(ceq_u, uv_strerror(sas_i));
}
}
/* _cttp_creq_start(): start a request
*/
static void
_cttp_creq_start(u3_creq* ceq_u)
{
if ( ceq_u->ipf_c ) {
ceq_u->sat_e = u3_csat_ripe;
_cttp_creq_connect(ceq_u);
} else {
ceq_u->sat_e = u3_csat_addr;
_cttp_creq_resolve(ceq_u);
}
}
/* _cttp_init_tls: initialize OpenSSL context
*/
static SSL_CTX*
_cttp_init_tls()
{
// XX require 1.1.0 and use TLS_client_method()
SSL_CTX* tls_u = SSL_CTX_new(SSLv23_client_method());
// XX use SSL_CTX_set_max_proto_version() and SSL_CTX_set_min_proto_version()
SSL_CTX_set_options(tls_u, SSL_OP_NO_SSLv2 |
SSL_OP_NO_SSLv3 |
// SSL_OP_NO_TLSv1 | // XX test
SSL_OP_NO_COMPRESSION);
SSL_CTX_set_verify(tls_u, SSL_VERIFY_PEER, 0);
SSL_CTX_set_default_verify_paths(tls_u);
SSL_CTX_set_session_cache_mode(tls_u, SSL_SESS_CACHE_OFF);
SSL_CTX_set_cipher_list(tls_u,
"ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:"
"ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:"
"RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS");
return tls_u;
}
/* _cttp_init_h2o: initialize h2o client ctx and timeout
*/
static h2o_http1client_ctx_t*
_cttp_init_h2o()
{
h2o_timeout_t* tim_u = c3_malloc(sizeof(*tim_u));
h2o_timeout_init(u3L, tim_u, 300 * 1000);
h2o_http1client_ctx_t* ctx_u = c3_calloc(sizeof(*ctx_u));
ctx_u->loop = u3L;
ctx_u->io_timeout = tim_u;
return ctx_u;
};
/* u3_cttp_ef_thus(): send %thus effect (outgoing request) to cttp.
*/
void
u3_cttp_ef_thus(c3_l num_l,
u3_noun cuq)
{
u3_creq* ceq_u;
if ( u3_nul == cuq ) {
ceq_u =_cttp_creq_find(num_l);
if ( ceq_u ) {
_cttp_creq_quit(ceq_u);
}
}
else {
ceq_u = _cttp_creq_new(num_l, u3k(u3t(cuq)));
_cttp_creq_start(ceq_u);
}
u3z(cuq);
}
/* u3_cttp_io_init(): initialize http client I/O.
*/
void
u3_cttp_io_init()
{
u3_Host.ctp_u.tls_u = _cttp_init_tls();
u3_Host.ctp_u.ctx_u = _cttp_init_h2o();
u3_Host.ctp_u.ctx_u->ssl_ctx = u3_Host.ctp_u.tls_u;
u3_Host.ctp_u.ceq_u = 0;
}
/* u3_cttp_io_exit(): shut down cttp.
*/
void
u3_cttp_io_exit(void)
{
SSL_CTX_free(u3_Host.ctp_u.tls_u);
free(u3_Host.ctp_u.ctx_u->io_timeout);
free(u3_Host.ctp_u.ctx_u);
}

View File

@ -0,0 +1,735 @@
/* vere/main.c
**
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <limits.h>
#include <uv.h>
#include <sigsegv.h>
#include <stdlib.h>
#include <ncurses/curses.h>
#include <termios.h>
#include <ncurses/term.h>
#include <dirent.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <h2o.h>
#include <curl/curl.h>
#include <argon2.h>
#define U3_GLOBAL
#define C3_GLOBAL
#include "all.h"
#include "vere/vere.h"
/* Require unsigned char
*/
STATIC_ASSERT(( 0 == CHAR_MIN && UCHAR_MAX == CHAR_MAX ),
"unsigned char required");
/* _main_readw(): parse a word from a string.
*/
static u3_noun
_main_readw(const c3_c* str_c, c3_w max_w, c3_w* out_w)
{
c3_c* end_c;
c3_w par_w = strtoul(str_c, &end_c, 0);
if ( *str_c != '\0' && *end_c == '\0' && par_w < max_w ) {
*out_w = par_w;
return c3y;
}
else return c3n;
}
/* _main_presig(): prefix optional sig.
*/
c3_c*
_main_presig(c3_c* txt_c)
{
c3_c* new_c = malloc(2 + strlen(txt_c));
if ( '~' == *txt_c ) {
strcpy(new_c, txt_c);
} else {
new_c[0] = '~';
strcpy(new_c + 1, txt_c);
}
return new_c;
}
/* _main_getopt(): extract option map from command line.
*/
static u3_noun
_main_getopt(c3_i argc, c3_c** argv)
{
c3_i ch_i;
c3_w arg_w;
u3_Host.ops_u.abo = c3n;
u3_Host.ops_u.bat = c3n;
u3_Host.ops_u.can = c3n;
u3_Host.ops_u.dem = c3n;
u3_Host.ops_u.dry = c3n;
u3_Host.ops_u.etn = c3n;
u3_Host.ops_u.gab = c3n;
u3_Host.ops_u.git = c3n;
// always disable hashboard
// XX temporary, remove once hashes are added
//
u3_Host.ops_u.has = c3y;
u3_Host.ops_u.net = c3y;
u3_Host.ops_u.nuu = c3n;
u3_Host.ops_u.pro = c3n;
u3_Host.ops_u.qui = c3n;
u3_Host.ops_u.rep = c3n;
u3_Host.ops_u.tex = c3n;
u3_Host.ops_u.tra = c3n;
u3_Host.ops_u.veb = c3n;
u3_Host.ops_u.kno_w = DefaultKernel;
while ( -1 != (ch_i=getopt(argc, argv,
"G:J:B:K:A:H:w:u:e:E:f:F:k:m:p:LjabcCdgqstvxPDRS")) )
{
switch ( ch_i ) {
case 'J': {
u3_Host.ops_u.lit_c = strdup(optarg);
break;
}
case 'B': {
u3_Host.ops_u.pil_c = strdup(optarg);
break;
}
case 'G': {
u3_Host.ops_u.gen_c = strdup(optarg);
break;
}
case 'A': {
u3_Host.ops_u.arv_c = strdup(optarg);
break;
}
case 'H': {
u3_Host.ops_u.dns_c = strdup(optarg);
break;
}
case 'e': {
u3_Host.ops_u.eth_c = strdup(optarg);
break;
}
case 'E': {
u3_Host.ops_u.ets_c = strdup(optarg);
break;
}
case 'F': {
u3_Host.ops_u.fak_c = _main_presig(optarg);
u3_Host.ops_u.net = c3n;
break;
}
case 'w': {
u3_Host.ops_u.who_c = _main_presig(optarg);
u3_Host.ops_u.nuu = c3y;
break;
}
case 'u': {
u3_Host.ops_u.url_c = strdup(optarg);
break;
}
case 'x': {
u3_Host.ops_u.tex = c3y;
break;
}
case 'f': {
if ( c3n == _main_readw(optarg, 100, &u3_Host.ops_u.fuz_w) ) {
return c3n;
}
break;
}
case 'K': {
if ( c3n == _main_readw(optarg, 256, &u3_Host.ops_u.kno_w) ) {
return c3n;
}
break;
}
case 'k': {
u3_Host.ops_u.key_c = strdup(optarg);
break;
}
case 'm': {
u3_Host.ops_u.sap_c = strdup(optarg);
break;
}
case 'p': {
if ( c3n == _main_readw(optarg, 65536, &arg_w) ) {
return c3n;
} else u3_Host.ops_u.por_s = arg_w;
break;
}
case 'R': {
u3_Host.ops_u.rep = c3y;
return c3y;
}
case 'L': { u3_Host.ops_u.net = c3n; break; }
case 'j': { u3_Host.ops_u.tra = c3y; break; }
case 'a': { u3_Host.ops_u.abo = c3y; break; }
case 'b': { u3_Host.ops_u.bat = c3y; break; }
case 'c': { u3_Host.ops_u.nuu = c3y; break; }
case 'C': { u3_Host.ops_u.can = c3y; break; }
case 'd': { u3_Host.ops_u.dem = c3y; break; }
case 'g': { u3_Host.ops_u.gab = c3y; break; }
case 'P': { u3_Host.ops_u.pro = c3y; break; }
case 'D': { u3_Host.ops_u.dry = c3y; break; }
case 'q': { u3_Host.ops_u.qui = c3y; break; }
case 'v': { u3_Host.ops_u.veb = c3y; break; }
case 's': { u3_Host.ops_u.git = c3y; break; }
case 'S': { u3_Host.ops_u.has = c3y; break; }
case 't': { u3_Host.ops_u.etn = c3y; break; }
case '?': default: {
return c3n;
}
}
}
#if defined(U3_OS_bsd)
if (u3_Host.ops_u.pro == c3y) {
fprintf(stderr, "profiling isn't yet supported on BSD\r\n");
return c3n;
}
#endif
if ( 0 != u3_Host.ops_u.fak_c ) {
if ( 28 < strlen(u3_Host.ops_u.fak_c) ) {
fprintf(stderr, "fake comets are disallowed\r\n");
return c3n;
}
u3_Host.ops_u.who_c = strdup(u3_Host.ops_u.fak_c);
u3_Host.ops_u.has = c3y; /* no battery hashing on fake ships. */
u3_Host.ops_u.net = c3n; /* no networking on fake ships. */
u3_Host.ops_u.nuu = c3y;
}
if ( argc != (optind + 1) && u3_Host.ops_u.who_c != 0 ) {
u3_Host.dir_c = strdup(1 + u3_Host.ops_u.who_c);
}
if ( argc != (optind + 1) ) {
return u3_Host.dir_c ? c3y : c3n;
} else {
{
c3_c* ash_c;
if ( (ash_c = strrchr(argv[optind], '/')) && (ash_c[1] == 0) ) {
*ash_c = 0;
}
}
u3_Host.dir_c = strdup(argv[optind]);
}
if ( c3y == u3_Host.ops_u.bat ) {
u3_Host.ops_u.dem = c3y;
u3_Host.ops_u.nuu = c3y;
}
// make -c optional, catch invalid boot of existing pier
//
{
struct stat s;
if ( 0 != stat(u3_Host.dir_c, &s) ) {
if ( c3n == u3_Host.ops_u.nuu ) {
u3_Host.ops_u.nuu = c3y;
}
}
else if ( c3y == u3_Host.ops_u.nuu ) {
fprintf(stderr, "tried to create, but %s already exists\n", u3_Host.dir_c);
fprintf(stderr, "normal usage: %s %s\n", argv[0], u3_Host.dir_c);
exit(1);
}
}
c3_t imp_t = ((0 != u3_Host.ops_u.who_c) &&
(4 == strlen(u3_Host.ops_u.who_c)));
if ( u3_Host.ops_u.gen_c != 0 && u3_Host.ops_u.nuu == c3n ) {
fprintf(stderr, "-G only makes sense when bootstrapping a new instance\n");
return c3n;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.who_c != 0) {
fprintf(stderr, "-w only makes sense when creating a new ship\n");
return c3n;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.pil_c != 0) {
fprintf(stderr, "-B only makes sense when creating a new ship\n");
return c3n;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.dns_c != 0) {
fprintf(stderr, "-H only makes sense when bootstrapping a new instance\n");
return c3n;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.pil_c != 0) {
fprintf(stderr, "-B only makes sense when bootstrapping a new instance\n");
return c3n;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.key_c != 0) {
fprintf(stderr, "-k only makes sense when bootstrapping a new instance\n");
return c3n;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.url_c != 0 ) {
fprintf(stderr, "-u only makes sense when bootstrapping a new instance\n");
return c3n;
}
if ( u3_Host.ops_u.nuu != c3y && u3_Host.ops_u.sap_c != 0 ) {
fprintf(stderr, "-m only makes sense when bootstrapping a new instance\n");
return c3n;
}
if ( u3_Host.ops_u.fak_c != 0 && u3_Host.ops_u.sap_c != 0 ) {
fprintf(stderr, "-m and -F cannot be used together\n");
return c3n;
}
if ( u3_Host.ops_u.ets_c != 0 && u3_Host.ops_u.sap_c != 0 ) {
fprintf(stderr, "-m and -E cannot be used together\n");
return c3n;
}
if ( u3_Host.ops_u.can == c3y && u3_Host.ops_u.sap_c != 0 ) {
fprintf(stderr, "-m and -C cannot be used together\n");
return c3n;
}
if ( u3_Host.ops_u.can == c3y && u3_Host.ops_u.ets_c != 0 ) {
fprintf(stderr, "-C and -E cannot be used together\n");
return c3n;
}
if ( u3_Host.ops_u.eth_c == 0 && imp_t ) {
u3_Host.ops_u.eth_c = "http://eth-mainnet.urbit.org:8545";
}
if ( u3_Host.ops_u.sap_c == 0 && u3_Host.ops_u.can == c3n ) {
u3_Host.ops_u.sap_c =
"https://bootstrap.urbit.org/urbit-" URBIT_VERSION ".snap";
}
if ( u3_Host.ops_u.url_c != 0 && u3_Host.ops_u.pil_c != 0 ) {
fprintf(stderr, "-B and -u cannot be used together\n");
return c3n;
}
else if ( u3_Host.ops_u.nuu == c3y
&& u3_Host.ops_u.url_c == 0
&& u3_Host.ops_u.git == c3n ) {
u3_Host.ops_u.url_c =
"https://bootstrap.urbit.org/urbit-" URBIT_VERSION ".pill";
}
else if ( u3_Host.ops_u.nuu == c3y
&& u3_Host.ops_u.url_c == 0
&& u3_Host.ops_u.arv_c == 0 ) {
fprintf(stderr, "-s only makes sense with -A\n");
return c3n;
}
if ( u3_Host.ops_u.pil_c != 0 ) {
struct stat s;
if ( stat(u3_Host.ops_u.pil_c, &s) != 0 ) {
fprintf(stderr, "pill %s not found\n", u3_Host.ops_u.pil_c);
return c3n;
}
}
if ( u3_Host.ops_u.key_c != 0 ) {
struct stat s;
if ( stat(u3_Host.ops_u.key_c, &s) != 0 ) {
fprintf(stderr, "keyfile %s not found\n", u3_Host.ops_u.key_c);
return c3n;
}
}
return c3y;
}
/* u3_ve_usage(): print usage and exit.
*/
static void
u3_ve_usage(c3_i argc, c3_c** argv)
{
c3_c *use_c[] = {
"Urbit: a personal server operating function\n",
"https://urbit.org\n",
"Version " URBIT_VERSION "\n",
"\n",
"Usage: %s [options...] ship_name\n",
"where ship_name is a @p phonetic representation of an urbit address\n",
"without the leading '~', and options is some subset of the following:\n",
"\n",
// XX find a way to re-enable
// "-A dir Use dir for initial galaxy sync\n",
"-B pill Bootstrap from this pill\n",
"-b Batch create\n",
"-c pier Create a new urbit in pier/\n",
"-D Recompute from events\n",
"-d Daemon mode\n",
"-e url Ethereum gateway\n",
"-F ship Fake keys; also disables networking\n",
"-f Fuzz testing\n",
"-g Set GC flag\n",
"-j file Create json trace file\n",
"-K stage Start at Hoon kernel version stage\n",
"-k keys Private key file\n",
"-L local networking only\n",
"-P Profiling\n",
"-p ames_port Set the ames port to bind to\n",
"-q Quiet\n",
"-R Report urbit build info\n",
"-S Disable battery hashing\n",
// XX find a way to re-enable
// "-s Pill URL from arvo git hash\n",
"-u url URL from which to download pill\n",
"-v Verbose\n",
"-w name Boot as ~name\n",
"-x Exit immediately\n",
"\n",
"Development Usage:\n",
" To create a development ship, use a fakezod:\n",
" %s -F zod -A /path/to/arvo/folder -B /path/to/pill -c zod\n",
"\n",
" For more information about developing on urbit, see:\n",
" https://github.com/urbit/urbit/blob/master/CONTRIBUTING.md\n",
"\n",
"Simple Usage: \n",
" %s -c <my-comet> to create a comet (anonymous urbit)\n",
" %s -w <my-planet> -k <my-key-file> if you own a planet\n",
" %s <myplanet or mycomet> to restart an existing urbit\n",
0
};
c3_i i;
for ( i=0; use_c[i]; i++ ) {
fprintf(stderr, use_c[i], argv[0]);
}
exit(1);
}
#if 0
/* u3_ve_panic(): panic and exit.
*/
static void
u3_ve_panic(c3_i argc, c3_c** argv)
{
fprintf(stderr, "%s: gross system failure\n", argv[0]);
exit(1);
}
#endif
/* u3_ve_sysopt(): apply option map to system state.
*/
static void
u3_ve_sysopt()
{
u3_Local = strdup(u3_Host.dir_c);
}
static void
report(void)
{
printf("urbit %s\n", URBIT_VERSION);
printf("---------\nLibraries\n---------\n");
printf("gmp: %s\n", gmp_version);
printf("sigsegv: %d.%d\n",
(libsigsegv_version >> 8) & 0xff,
libsigsegv_version & 0xff);
printf("openssl: %s\n", SSLeay_version(SSLEAY_VERSION));
printf("curses: %s\n", curses_version());
printf("libuv: %s\n", uv_version_string());
printf("libh2o: %d.%d.%d\n",
H2O_LIBRARY_VERSION_MAJOR,
H2O_LIBRARY_VERSION_MINOR,
H2O_LIBRARY_VERSION_PATCH);
printf("lmdb: %d.%d.%d\n",
MDB_VERSION_MAJOR,
MDB_VERSION_MINOR,
MDB_VERSION_PATCH);
printf("curl: %d.%d.%d\n",
LIBCURL_VERSION_MAJOR,
LIBCURL_VERSION_MINOR,
LIBCURL_VERSION_PATCH);
printf("argon2: 0x%x\n", ARGON2_VERSION_NUMBER);
}
static void
_stop_exit(c3_i int_i)
{
// explicit fprintf to avoid allocation in u3l_log
//
fprintf(stderr, "\r\n[received keyboard stop signal, exiting]\r\n");
u3_daemon_bail();
}
/*
This is set to the the write-end of a pipe when Urbit is started in
daemon mode. It's meant to be used as a signal to the parent process
that the child process has finished booting.
*/
static c3_i _child_process_booted_signal_fd = -1;
/*
This should be called whenever the ship has been booted enough to
handle commands from automation code. Specifically, once the Eyre's
`chis` interface is up and running.
In daemon mode, this signals to the parent process that it can
exit. Otherwise, it does nothing.
Once we've sent a signal with `write`, we close the file descriptor
and overwrite the global to make it impossible to accidentally do
this twice.
*/
static void _on_boot_completed_cb() {
c3_c buf[2] = {0,0};
if ( -1 == _child_process_booted_signal_fd ) {
return;
}
if ( 0 == write(_child_process_booted_signal_fd, buf, 1) ) {
c3_assert(!"_on_boot_completed_cb: Can't write to parent FD");
}
close(_child_process_booted_signal_fd);
_child_process_booted_signal_fd = -1;
}
/*
In daemon mode, run the urbit as a background process, but don't
exit from the parent process until the ship is finished booting.
We use a pipe to communicate between the child and the parent. The
parent waits for the child to write something to the pipe and
then exits. If the pipe is closed with nothing written to it, get
the exit status from the child process and also exit with that status.
We want the child to write to the pipe once it's booted, so we put
`_on_boot_completed_cb` into `u3_Host.bot_f`, which is NULL in
non-daemon mode. That gets called once the `chis` service is
available.
In both processes, we are good fork() citizens, and close all unused
file descriptors. Closing `pipefd[1]` in the parent process is
especially important, since the pipe needs to be closed if the child
process dies. When the pipe is closed, the read fails, and that's
how we know that something went wrong.
There are some edge cases around `WEXITSTATUS` that are not handled
here, but I don't think it matters.
*/
static void
_fork_into_background_process()
{
c3_i pipefd[2];
if ( 0 != pipe(pipefd) ) {
c3_assert(!"Failed to create pipe");
}
pid_t childpid = fork();
if ( 0 == childpid ) {
close(pipefd[0]);
_child_process_booted_signal_fd = pipefd[1];
u3_Host.bot_f = _on_boot_completed_cb;
return;
}
close(pipefd[1]);
close(0);
close(1);
close(2);
c3_c buf[2] = {0,0};
if ( 1 == read(pipefd[0], buf, 1) ) {
exit(0);
}
c3_i status;
wait(&status);
exit(WEXITSTATUS(status));
}
c3_i
main(c3_i argc,
c3_c** argv)
{
// Parse options.
//
if ( c3n == _main_getopt(argc, argv) ) {
u3_ve_usage(argc, argv);
return 1;
}
// Set `u3_Host.wrk_c` to the worker executable path.
c3_i worker_exe_len = 1 + strlen(argv[0]) + strlen("-worker");
u3_Host.wrk_c = c3_malloc(worker_exe_len);
snprintf(u3_Host.wrk_c, worker_exe_len, "%s-worker", argv[0]);
// Set TERMINFO_DIRS environment variable
c3_i terminfo_len = 1 + strlen(argv[0]) + strlen("-terminfo");
c3_c terminfo_dir[terminfo_len];
snprintf(terminfo_dir, terminfo_len, "%s-terminfo", argv[0]);
setenv("TERMINFO_DIRS", terminfo_dir, 1);
if ( c3y == u3_Host.ops_u.dem ) {
_fork_into_background_process();
}
if ( c3y == u3_Host.ops_u.rep ) {
report();
return 0;
}
#if 0
if ( 0 == getuid() ) {
chroot(u3_Host.dir_c);
u3_Host.dir_c = "/";
}
#endif
u3_ve_sysopt();
// Block profiling signal, which should be delivered to exactly one thread.
//
// XX review, may be unnecessary due to similar in u3m_init()
//
if ( _(u3_Host.ops_u.pro) ) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGPROF);
if ( 0 != pthread_sigmask(SIG_BLOCK, &set, NULL) ) {
u3l_log("boot: thread mask SIGPROF: %s\r\n", strerror(errno));
exit(1);
}
}
// Handle SIGTSTP as if it was SIGTERM.
//
// Configured here using signal() so as to be immediately available.
//
signal(SIGTSTP, _stop_exit);
printf("~\n");
// printf("welcome.\n");
printf("urbit %s\n", URBIT_VERSION);
// prints the absolute path of the pier
//
c3_c* abs_c = realpath(u3_Host.dir_c, 0);
// if the ship is being booted, we use realpath(). Otherwise, we use getcwd()
// with a memory-allocation loop
//
if (abs_c == NULL) {
c3_i mprint_i = 1000;
abs_c = c3_malloc(mprint_i);
// allocates more memory as needed if the path is too large
//
while ( abs_c != getcwd(abs_c, mprint_i) ) {
free(abs_c);
mprint_i *= 2;
abs_c = c3_malloc(mprint_i);
}
printf("boot: home is %s/%s\n", abs_c, u3_Host.dir_c);
free(abs_c);
} else {
printf("boot: home is %s\n", abs_c);
free(abs_c);
}
// printf("vere: hostname is %s\n", u3_Host.ops_u.nam_c);
if ( c3y == u3_Host.ops_u.dem && c3n == u3_Host.ops_u.bat ) {
printf("boot: running as daemon\n");
}
// Seed prng. Don't panic -- just for fuzz testing.
//
srand(getpid());
// Instantiate process globals.
{
/* Boot the image and checkpoint. Set flags.
*/
{
/* Set pier directory.
*/
u3C.dir_c = u3_Host.dir_c;
/* Logging that doesn't interfere with console output.
*/
u3C.stderr_log_f = u3_term_io_log;
/* Set GC flag.
*/
if ( _(u3_Host.ops_u.gab) ) {
u3C.wag_w |= u3o_debug_ram;
}
/* Set profile flag.
*/
if ( _(u3_Host.ops_u.pro) ) {
u3C.wag_w |= u3o_debug_cpu;
}
/* Set verbose flag.
*/
if ( _(u3_Host.ops_u.veb) ) {
u3C.wag_w |= u3o_verbose;
}
/* Set quiet flag.
*/
if ( _(u3_Host.ops_u.qui) ) {
u3C.wag_w |= u3o_quiet;
}
/* Set dry-run flag.
*/
if ( _(u3_Host.ops_u.dry) ) {
u3C.wag_w |= u3o_dryrun;
}
/* Set hashboard flag
*/
if ( _(u3_Host.ops_u.has) ) {
u3C.wag_w |= u3o_hashless;
}
/* Set tracing flag
*/
if ( _(u3_Host.ops_u.tra) ) {
u3C.wag_w |= u3o_trace;
u3_Host.tra_u.nid_w = 0;
u3_Host.tra_u.fil_u = NULL;
u3_Host.tra_u.con_w = 0;
u3_Host.tra_u.fun_w = 0;
}
}
/* Initialize OpenSSL for client and server
*/
SSL_library_init();
SSL_load_error_strings();
u3_daemon_commence();
}
return 0;
}

525
pkg/hs/vere/notes/c/dawn.c Normal file
View File

@ -0,0 +1,525 @@
/* vere/dawn.c
**
** ethereum-integrated pre-boot validation
*/
#include <curl/curl.h>
#include <uv.h>
#include "all.h"
#include "vere/vere.h"
/* _dawn_oct_to_buf(): +octs to uv_buf_t
*/
static uv_buf_t
_dawn_oct_to_buf(u3_noun oct)
{
if ( c3n == u3a_is_cat(u3h(oct)) ) {
exit(1);
}
c3_w len_w = u3h(oct);
c3_y* buf_y = c3_malloc(1 + len_w);
buf_y[len_w] = 0;
u3r_bytes(0, len_w, buf_y, u3t(oct));
u3z(oct);
return uv_buf_init((void*)buf_y, len_w);
}
/* _dawn_buf_to_oct(): uv_buf_t to +octs
*/
static u3_noun
_dawn_buf_to_oct(uv_buf_t buf_u)
{
u3_noun len = u3i_words(1, (c3_w*)&buf_u.len);
if ( c3n == u3a_is_cat(len) ) {
exit(1);
}
return u3nc(len, u3i_bytes(buf_u.len, (const c3_y*)buf_u.base));
}
/* _dawn_curl_alloc(): allocate a response buffer for curl
*/
static size_t
_dawn_curl_alloc(void* dat_v, size_t uni_t, size_t mem_t, uv_buf_t* buf_u)
{
size_t siz_t = uni_t * mem_t;
buf_u->base = c3_realloc(buf_u->base, 1 + siz_t + buf_u->len);
memcpy(buf_u->base + buf_u->len, dat_v, siz_t);
buf_u->len += siz_t;
buf_u->base[buf_u->len] = 0;
return siz_t;
}
/* _dawn_post_json(): POST JSON to url_c
*/
static uv_buf_t
_dawn_post_json(c3_c* url_c, uv_buf_t lod_u)
{
CURL *curl;
CURLcode result;
long cod_l;
struct curl_slist* hed_u = 0;
uv_buf_t buf_u = uv_buf_init(c3_malloc(1), 0);
if ( !(curl = curl_easy_init()) ) {
u3l_log("failed to initialize libcurl\n");
exit(1);
}
hed_u = curl_slist_append(hed_u, "Accept: application/json");
hed_u = curl_slist_append(hed_u, "Content-Type: application/json");
hed_u = curl_slist_append(hed_u, "charsets: utf-8");
// XX require TLS, pin default cert?
curl_easy_setopt(curl, CURLOPT_URL, url_c);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _dawn_curl_alloc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&buf_u);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hed_u);
// note: must be terminated!
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, lod_u.base);
result = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &cod_l);
// XX retry?
if ( CURLE_OK != result ) {
u3l_log("failed to fetch %s: %s\n",
url_c, curl_easy_strerror(result));
exit(1);
}
if ( 300 <= cod_l ) {
u3l_log("error fetching %s: HTTP %ld\n", url_c, cod_l);
exit(1);
}
curl_easy_cleanup(curl);
curl_slist_free_all(hed_u);
return buf_u;
}
/* _dawn_get_jam(): GET a jammed noun from url_c
*/
static u3_noun
_dawn_get_jam(c3_c* url_c)
{
CURL *curl;
CURLcode result;
long cod_l;
uv_buf_t buf_u = uv_buf_init(c3_malloc(1), 0);
if ( !(curl = curl_easy_init()) ) {
u3l_log("failed to initialize libcurl\n");
exit(1);
}
// XX require TLS, pin default cert?
curl_easy_setopt(curl, CURLOPT_URL, url_c);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _dawn_curl_alloc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&buf_u);
result = curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &cod_l);
// XX retry?
if ( CURLE_OK != result ) {
u3l_log("failed to fetch %s: %s\n",
url_c, curl_easy_strerror(result));
exit(1);
}
if ( 300 <= cod_l ) {
u3l_log("error fetching %s: HTTP %ld\n", url_c, cod_l);
exit(1);
}
curl_easy_cleanup(curl);
// throw away the length from the octs
//
u3_noun octs = _dawn_buf_to_oct(buf_u);
u3_noun jammed = u3k(u3t(octs));
u3z(octs);
return u3ke_cue(jammed);
}
/* _dawn_eth_rpc(): ethereum JSON RPC with request/response as +octs
*/
static u3_noun
_dawn_eth_rpc(c3_c* url_c, u3_noun oct)
{
return _dawn_buf_to_oct(_dawn_post_json(url_c, _dawn_oct_to_buf(oct)));
}
/* _dawn_fail(): pre-boot validation failed
*/
static void
_dawn_fail(u3_noun who, u3_noun rac, u3_noun sas)
{
u3_noun how = u3dc("scot", 'p', u3k(who));
c3_c* how_c = u3r_string(u3k(how));
c3_c* rac_c;
switch (rac) {
default: c3_assert(0);
case c3__czar: {
rac_c = "galaxy";
break;
}
case c3__king: {
rac_c = "star";
break;
}
case c3__duke: {
rac_c = "planet";
break;
}
case c3__earl: {
rac_c = "moon";
break;
}
case c3__pawn: {
rac_c = "comet";
break;
}
}
u3l_log("boot: invalid keys for %s '%s'\r\n", rac_c, how_c);
// XX deconstruct sas, print helpful error messages
u3m_p("pre-boot error", u3t(sas));
u3z(how);
free(how_c);
exit(1);
}
/* _dawn_need_unit(): produce a value or print error and exit
*/
static u3_noun
_dawn_need_unit(u3_noun nit, c3_c* msg_c)
{
if ( u3_nul == nit ) {
u3l_log("%s\r\n", msg_c);
exit(1);
}
else {
u3_noun pro = u3k(u3t(nit));
u3z(nit);
return pro;
}
}
/* _dawn_purl(): ethereum gateway url as (unit purl)
*/
static u3_noun
_dawn_purl(u3_noun rac)
{
u3_noun url;
if ( 0 == u3_Host.ops_u.eth_c ) {
if ( c3__czar == rac ) {
u3l_log("boot: galaxy requires ethereum gateway via -e\r\n");
exit(1);
}
url = u3_nul;
}
else {
// XX call de-purl directly
//
u3_noun par = u3v_wish("auru:de-purl:html");
u3_noun lur = u3i_string(u3_Host.ops_u.eth_c);
u3_noun rul = u3dc("rush", u3k(lur), u3k(par));
if ( u3_nul == rul ) {
if ( c3__czar == rac ) {
u3l_log("boot: galaxy requires ethereum gateway via -e\r\n");
exit(1);
}
url = u3_nul;
}
else {
// XX revise for de-purl
// auru:de-purl:html parses to (pair user purl)
// we need (unit purl)
//
url = u3nc(u3_nul, u3k(u3t(u3t(rul))));
}
u3z(par); u3z(lur); u3z(rul);
}
return url;
}
/* _dawn_turf(): override contract domains with -H
*/
static u3_noun
_dawn_turf(c3_c* dns_c)
{
u3_noun tuf;
u3_noun par = u3v_wish("thos:de-purl:html");
u3_noun dns = u3i_string(dns_c);
u3_noun rul = u3dc("rush", u3k(dns), u3k(par));
if ( (u3_nul == rul) || (c3n == u3h(u3t(rul))) ) {
u3l_log("boot: invalid domain specified with -H %s\r\n", dns_c);
exit(1);
}
else {
u3l_log("boot: overriding network domains with %s\r\n", dns_c);
u3_noun dom = u3t(u3t(rul));
tuf = u3nc(u3k(dom), u3_nul);
}
u3z(par); u3z(dns); u3z(rul);
return tuf;
}
/* u3_dawn_vent(): validated boot event
*/
u3_noun
u3_dawn_vent(u3_noun seed)
{
u3_noun url, bok, pon, zar, tuf, sap;
u3_noun ship = u3h(seed);
u3_noun rank = u3do("clan:title", u3k(ship));
// load snapshot from file
//
if ( 0 != u3_Host.ops_u.ets_c ) {
u3l_log("boot: loading azimuth snapshot\r\n");
u3_noun raw_snap = u3ke_cue(u3m_file(u3_Host.ops_u.ets_c));
sap = u3nc(u3_nul, raw_snap);
}
// load snapshot from HTTP URL
//
else if ( 0 != u3_Host.ops_u.sap_c ) {
u3l_log("boot: downloading azimuth snapshot from %s\r\n",
u3_Host.ops_u.sap_c);
u3_noun raw_snap = _dawn_get_jam(u3_Host.ops_u.sap_c);
sap = u3nc(u3_nul, raw_snap);
}
// no snapshot
//
else {
u3l_log("boot: no azimuth snapshot specified\n");
sap = u3_nul;
}
url = _dawn_purl(rank);
// XX require https?
//
c3_c* url_c = ( 0 != u3_Host.ops_u.eth_c ) ?
u3_Host.ops_u.eth_c :
"https://mainnet.infura.io/v3/196a7f37c7d54211b4a07904ec73ad87";
// pin block number
//
if ( c3y == u3_Host.ops_u.etn ) {
u3l_log("boot: extracting block from snapshot\r\n");
bok = _dawn_need_unit(u3do("bloq:snap:dawn", u3k(u3t(sap))),
"boot: failed to extract "
"block from snapshot");
}
else {
u3l_log("boot: retrieving latest block\r\n");
u3_noun oct = u3v_wish("bloq:give:dawn");
u3_noun kob = _dawn_eth_rpc(url_c, u3k(oct));
bok = _dawn_need_unit(u3do("bloq:take:dawn", u3k(kob)),
"boot: block retrieval failed");
u3z(oct); u3z(kob);
}
{
// +point:azimuth: on-chain state
//
u3_noun pot;
if ( c3y == u3_Host.ops_u.etn ) {
u3l_log("boot: extracting public keys from snapshot\r\n");
pot = _dawn_need_unit(u3dc("point:snap:dawn", u3k(ship), u3k(u3t(sap))),
"boot: failed to extract "
"public keys from snapshot");
}
else if ( c3__pawn == rank ) {
// irrelevant, just bunt +point
//
pot = u3v_wish("*point:azimuth");
}
else {
u3_noun who;
if ( c3__earl == rank ) {
who = u3do("^sein:title", u3k(ship));
{
u3_noun seg = u3dc("scot", 'p', u3k(who));
c3_c* seg_c = u3r_string(seg);
u3l_log("boot: retrieving %s's public keys (for %s)\r\n",
seg_c, u3_Host.ops_u.who_c);
free(seg_c);
u3z(seg);
}
}
else {
who = u3k(ship);
u3l_log("boot: retrieving %s's public keys\r\n",
u3_Host.ops_u.who_c);
}
{
u3_noun oct = u3dc("point:give:dawn", u3k(bok), u3k(who));
u3_noun luh = _dawn_eth_rpc(url_c, u3k(oct));
pot = _dawn_need_unit(u3dc("point:take:dawn", u3k(ship), u3k(luh)),
"boot: failed to retrieve public keys");
u3z(oct); u3z(luh);
}
u3z(who);
}
// +live:dawn: network state
// XX actually make request
//
u3_noun liv = u3_nul;
// u3_noun liv = _dawn_get_json(parent, /some/url)
u3l_log("boot: verifying keys\r\n");
// (each sponsor=ship error=@tas)
//
u3_noun sas = u3dt("veri:dawn", u3k(seed), u3k(pot), u3k(liv));
if ( c3n == u3h(sas) ) {
// bails, won't return
_dawn_fail(ship, rank, sas);
return u3_none;
}
// ship: sponsor
// produced by +veri:dawn to avoid coupling to +point structure
// XX reconsider
//
pon = u3k(u3t(sas));
u3z(pot); u3z(liv); u3z(sas);
}
// (map ship [=life =pass]): galaxy table
//
if ( c3y == u3_Host.ops_u.etn ) {
u3l_log("boot: extracting galaxy table from snapshot\r\n");
zar = _dawn_need_unit(u3do("czar:snap:dawn", u3k(u3t(sap))),
"boot: failed to extract "
"galaxy table from snapshot");
}
else {
u3l_log("boot: retrieving galaxy table\r\n");
u3_noun oct = u3do("czar:give:dawn", u3k(bok));
u3_noun raz = _dawn_eth_rpc(url_c, u3k(oct));
zar = _dawn_need_unit(u3do("czar:take:dawn", u3k(raz)),
"boot: failed to retrieve galaxy table");
u3z(oct); u3z(raz);
}
// (list turf): ames domains
//
if ( 0 != u3_Host.ops_u.dns_c ) {
tuf = _dawn_turf(u3_Host.ops_u.dns_c);
}
else if ( c3y == u3_Host.ops_u.etn ) {
u3l_log("boot: extracting network domains from snapshot\r\n");
tuf = _dawn_need_unit(u3do("turf:snap:dawn", u3k(u3t(sap))),
"boot: failed to extract "
"network domains from snapshot");
}
else {
u3l_log("boot: retrieving network domains\r\n");
u3_noun oct = u3do("turf:give:dawn", u3k(bok));
u3_noun fut = _dawn_eth_rpc(url_c, u3k(oct));
tuf = _dawn_need_unit(u3do("turf:take:dawn", u3k(fut)),
"boot: failed to retrieve network domains");
u3z(oct); u3z(fut);
}
u3z(rank);
// [%dawn seed sponsor galaxies domains block eth-url snap]
//
return u3nc(c3__dawn, u3nq(seed, pon, zar, u3nq(tuf, bok, url, sap)));
}
/* _dawn_come(): mine a comet under a list of stars
*/
static u3_noun
_dawn_come(u3_noun stars)
{
u3_noun seed;
{
c3_w eny_w[16];
u3_noun eny;
c3_rand(eny_w);
eny = u3i_words(16, eny_w);
u3l_log("boot: mining a comet. May take up to an hour.\r\n");
u3l_log("If you want to boot faster, get an Azimuth point.\r\n");
seed = u3dc("come:dawn", u3k(stars), u3k(eny));
u3z(eny);
}
{
u3_noun who = u3dc("scot", 'p', u3k(u3h(seed)));
c3_c* who_c = u3r_string(who);
u3l_log("boot: found comet %s\r\n", who_c);
free(who_c);
u3z(who);
}
u3z(stars);
return seed;
}
/* u3_dawn_come(): mine a comet under a list of stars we download
*/
u3_noun
u3_dawn_come()
{
return _dawn_come(
_dawn_get_jam("https://bootstrap.urbit.org/comet-stars.jam"));
}

170
pkg/hs/vere/notes/c/foil.c Normal file
View File

@ -0,0 +1,170 @@
/* vere/foil.c
**
** This file is in the public domain.
*/
#include "all.h"
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <setjmp.h>
#include <gmp.h>
#include <dirent.h>
#include <stdint.h>
#include <uv.h>
#include <termios.h>
#include <ncurses/term.h>
#include <errno.h>
#include <libgen.h>
#include <ftw.h>
#include "vere/vere.h"
/* assumptions:
** all measurements are in chubs (double-words, c3_d, uint64_t).
** little-endian addressing is ASSUMED.
**
** framing:
** the last two chubs of a frame:
**
** {
** 64-bit frame length
** {
** (high 32 bits) mug of frame
** (low 32 bits) mug of current address
** }
** }
**
** we can scan for one of these frames with very low probability
** of a false positive. we always write to and read from the end
** of a file. a frame position points to its end.
**
** protocol:
** once the callback is called, all results are fully fsynced.
** all callbacks are optional and can be passed 0.
*/
/* _foil_fail(): fail with error.
*/
static void
_foil_fail(const c3_c* why_c, c3_i err_i)
{
if ( err_i ) {
u3l_log("%s: error: %s\r\n", why_c, uv_strerror(err_i));
c3_assert(0);
} else {
u3l_log("%s: file error\r\n", why_c);
}
exit(1);
}
/* _foil_close(): close file, blockingly.
*/
static void
_foil_close(uv_file fil_f)
{
c3_i err_i;
uv_fs_t ruq_u;
if ( 0 != (err_i = uv_fs_close(u3L, &ruq_u, fil_f, 0)) ) {
_foil_fail("uv_fs_close", err_i);
}
}
/* _foil_path(): allocate path.
*/
static c3_c*
_foil_path(u3_dire* dir_u,
const c3_c* nam_c)
{
c3_w len_w = strlen(dir_u->pax_c);
c3_c* pax_c;
pax_c = c3_malloc(1 + len_w + 1 + strlen(nam_c));
strcpy(pax_c, dir_u->pax_c);
pax_c[len_w] = '/';
strcpy(pax_c + len_w + 1, nam_c);
return pax_c;
}
/* u3_foil_folder(): load directory, blockingly. null if nonexistent.
*/
u3_dire*
u3_foil_folder(const c3_c* pax_c)
{
u3_dire* dir_u;
uv_fs_t ruq_u;
uv_dirent_t den_u;
c3_i err_i;
/* open directory, synchronously
*/
{
err_i = uv_fs_scandir(u3L, &ruq_u, pax_c, 0, 0);
if ( err_i < 0 ) {
if ( UV_ENOENT != err_i ) {
_foil_fail(pax_c, err_i);
return 0;
}
else {
if ( 0 != (err_i = uv_fs_mkdir(u3L, &ruq_u, pax_c, 0700, 0)) ) {
_foil_fail(pax_c, err_i);
return 0;
}
else {
uv_fs_req_cleanup(&ruq_u);
return u3_foil_folder(pax_c);
}
}
}
dir_u = c3_malloc(sizeof *dir_u);
dir_u->all_u = 0;
dir_u->pax_c = c3_malloc(1 + strlen(pax_c));
strcpy(dir_u->pax_c, pax_c);
}
/* create entries for all files
*/
while ( UV_EOF != uv_fs_scandir_next(&ruq_u, &den_u) ) {
if ( UV_DIRENT_FILE == den_u.type ) {
u3_dent* det_u = c3_malloc(sizeof(*det_u));
det_u->nam_c = c3_malloc(1 + strlen(den_u.name));
strcpy(det_u->nam_c, den_u.name);
det_u->nex_u = dir_u->all_u;
dir_u->all_u = det_u;
}
}
/* clean up request
*/
{
uv_fs_req_cleanup(&ruq_u);
}
/* open directory file for reading, to fsync
*/
{
if ( 0 > (err_i = uv_fs_open(u3L,
&ruq_u,
pax_c,
O_RDONLY,
0600,
0)) )
{
_foil_fail("open directory", err_i);
return 0;
}
dir_u->fil_u = ruq_u.result;
uv_fs_req_cleanup(&ruq_u);
}
return dir_u;
}

View File

@ -0,0 +1,105 @@
#include "all.h"
/* _setup(): prepare for tests.
*/
static void
_setup(void)
{
u3m_init();
u3m_pave(c3y, c3n);
}
/* _test_mug(): spot check u3r_mug hashes.
*/
static void
_test_mug(void)
{
if ( 0x4d441035 != u3r_mug_string("Hello, world!") ) {
fprintf(stderr, "fail (a)\r\n");
exit(1);
}
if ( 0x4d441035 != u3r_mug(u3i_string("Hello, world!")) ) {
fprintf(stderr, "fail (b)\r\n");
exit(1);
}
if ( 0x79ff04e8 != u3r_mug_bytes(0, 0) ) {
fprintf(stderr, "fail (c)\r\n");
exit(1);
}
if ( 0x64dfda5c != u3r_mug(u3i_string("xxxxxxxxxxxxxxxxxxxxxxxxxxxx")) ) {
fprintf(stderr, "fail (d)\r\n");
exit(1);
}
if ( 0x389ca03a != u3r_mug_cell(0, 0) ) {
fprintf(stderr, "fail (e)\r\n");
exit(1);
}
if ( 0x389ca03a != u3r_mug_cell(1, 1) ) {
fprintf(stderr, "fail (f)\r\n");
exit(1);
}
if ( 0x5258a6c0 != u3r_mug_cell(0, u3qc_bex(32)) ) {
fprintf(stderr, "fail (g)\r\n");
exit(1);
}
if ( 0x2ad39968 != u3r_mug_cell(u3qa_dec(u3qc_bex(128)), 1) ) {
fprintf(stderr, "fail (h)\r\n");
exit(1);
}
{
// stick some zero bytes in a string
//
u3_noun str = u3kc_lsh(3, 1,
u3kc_mix(u3qc_bex(212),
u3i_string("abcdefjhijklmnopqrstuvwxyz")));
c3_w byt_w = u3r_met(3, str);
c3_w wor_w = u3r_met(5, str);
c3_y* str_y = c3_malloc(byt_w);
c3_w* str_w = c3_malloc(4 * wor_w);
c3_d str_d = 0;
u3r_bytes(0, byt_w, str_y, str);
u3r_words(0, wor_w, str_w, str);
str_d |= str_w[0];
str_d |= ((c3_d)str_w[1] << 32ULL);
if ( 0x34d08717 != u3r_mug(str) ) {
fprintf(stderr, "fail (i) (1) \r\n");
exit(1);
}
if ( 0x34d08717 != u3r_mug_bytes(str_y, byt_w) ) {
fprintf(stderr, "fail (i) (2)\r\n");
exit(1);
}
if ( 0x34d08717 != u3r_mug_words(str_w, wor_w) ) {
fprintf(stderr, "fail (i) (3)\r\n");
exit(1);
}
if ( u3r_mug_words(str_w, 2) != u3r_mug_chub(str_d) ) {
fprintf(stderr, "fail (i) (4)\r\n");
exit(1);
}
}
}
/* main(): run all test cases.
*/
int
main(int argc, char* argv[])
{
_setup();
_test_mug();
return 0;
}

View File

@ -0,0 +1,144 @@
#include "all.h"
static void _setup(void);
static void _test_cache_replace_value(void);
static void _test_cache_trimming(void);
static void _test_no_cache(void);
static void _test_skip_slot(void);
// defined in noun/hashtable.c
c3_w _ch_skip_slot(c3_w mug_w, c3_w lef_w);
/* main(): run all test cases.
*/
int
main(int argc, char* argv[])
{
_setup();
_test_no_cache();
_test_skip_slot();
_test_cache_trimming();
_test_cache_replace_value();
return 0;
}
/* _setup(): prepare for tests.
*/
static void
_setup(void)
{
u3m_init();
u3m_pave(c3y, c3n);
}
/* _test_no_cache(): test a hashtable without caching.
*/
static void
_test_no_cache(void)
{
c3_w i_w;
c3_w max_w = 1000;
u3p(u3h_root) har_p = u3h_new();
for ( i_w = 0; i_w < max_w; i_w++ ) {
u3h_put(har_p, i_w, i_w + max_w);
}
for ( i_w = 0; i_w < max_w; i_w++ ) {
c3_assert(i_w + max_w == u3h_get(har_p, i_w));
}
printf("test_no_cache: ok\n");
}
/* _test_skip_slot():
*/
static void
_test_skip_slot(void)
{
// root table
{
c3_w mug_w = 0x17 << 25;
c3_w res_w = _ch_skip_slot(mug_w, 25);
c3_assert((0x18 << 25) == res_w);
}
{
c3_w mug_w = 63 << 25; // 6 bits, all ones
c3_w res_w = _ch_skip_slot(mug_w, 25);
c3_assert(0 == res_w);
}
// child nodes
{
c3_w mug_w = 17 << 20;
c3_w res_w = _ch_skip_slot(mug_w, 20);
c3_assert((18 << 20) == res_w);
}
{
c3_w mug_w = 31 << 20; // 5 bits, all ones
c3_w res_w = _ch_skip_slot(mug_w, 20);
c3_assert((1 << 25) == res_w);
}
fprintf(stderr, "test_skip_slot: ok\n");
}
/* _test_cache_trimming(): ensure a caching hashtable removes stale items.
*/
static void
_test_cache_trimming(void)
{
c3_w max_w = 620;
c3_w i_w;
//u3p(u3h_root) har_p = u3h_new_cache(max_w / 2);
u3p(u3h_root) har_p = u3h_new_cache(max_w / 10 );
u3h_root* har_u = u3to(u3h_root, har_p);
for ( i_w = 0; i_w < max_w; i_w++ ) {
u3h_put(har_p, i_w, i_w + max_w);
}
if ( ( max_w + max_w - 1) != u3h_get(har_p, max_w - 1) ) {
fprintf(stderr, "fail\r\n");
exit(1);
}
if ( ( max_w / 10 ) != har_u->use_w ) {
fprintf(stderr, "fail\r\n");
exit(1);
}
fprintf(stderr, "test_cache_trimming: ok\n");
}
static void
_test_cache_replace_value(void)
{
c3_w max_w = 100;
c3_w i_w;
u3p(u3h_root) har_p = u3h_new_cache(max_w);
u3h_root* har_u = u3to(u3h_root, har_p);
for ( i_w = 0; i_w < max_w; i_w++ ) {
u3h_put(har_p, i_w, i_w + max_w);
}
for ( i_w = 0; i_w < max_w; i_w++ ) {
u3h_put(har_p, i_w, i_w + max_w + 1);
}
if ( (2 * max_w) != u3h_get(har_p, max_w - 1) ) {
fprintf(stderr, "fail\r\n");
exit(1);
}
if ( max_w != har_u->use_w ) {
fprintf(stderr, "fail\r\n");
exit(1);
}
fprintf(stderr, "test_cache_replace_value: ok\r\n");
}

2908
pkg/hs/vere/notes/c/http.c Normal file

File diff suppressed because it is too large Load Diff

670
pkg/hs/vere/notes/c/lmdb.c Normal file
View File

@ -0,0 +1,670 @@
/* vere/lmdb.c
*/
#include "all.h"
#include <uv.h>
#include <lmdb.h>
#include "vere/vere.h"
// Event log persistence for Urbit
//
// Persistence works by having an lmdb environment opened on the main
// thread. This environment is used to create read-only transactions
// synchronously when needed.
//
// But the majority of lmdb writes operate asynchronously in the uv worker
// pool. Since individual transactions are bound to threads, we perform all
// blocking writing on worker threads.
//
// We perform the very first metadata writes on the main thread because we
// can't do anything until they persist.
/* u3_lmdb_init(): Opens up a log environment
**
** Precondition: log_path points to an already created directory
*/
MDB_env* u3_lmdb_init(const char* log_path)
{
MDB_env* env = 0;
c3_w ret_w = mdb_env_create(&env);
if (ret_w != 0) {
u3l_log("lmdb: init fail: %s\n", mdb_strerror(ret_w));
return 0;
}
// Our databases have up to three tables: META, EVENTS, and GRAINS.
ret_w = mdb_env_set_maxdbs(env, 3);
if (ret_w != 0) {
u3l_log("lmdb: failed to set number of databases: %s\n", mdb_strerror(ret_w));
return 0;
}
// TODO: Start with forty gigabytes for the maximum event log size. We'll
// need to do something more sophisticated for real in the long term, though.
//
const size_t forty_gigabytes = 42949672960;
ret_w = mdb_env_set_mapsize(env, forty_gigabytes);
if (ret_w != 0) {
u3l_log("lmdb: failed to set database size: %s\n", mdb_strerror(ret_w));
return 0;
}
ret_w = mdb_env_open(env, log_path, 0, 0664);
if (ret_w != 0) {
u3l_log("lmdb: failed to open event log: %s\n", mdb_strerror(ret_w));
return 0;
}
return env;
}
/* u3_lmdb_shutdown(): Shuts down lmdb
*/
void u3_lmdb_shutdown(MDB_env* env)
{
mdb_env_close(env);
}
/* _perform_put_on_database_raw(): Writes a key/value pair to a specific
** database as part of a transaction.
**
** The raw version doesn't take ownership of either key/value and performs no
** nock calculations, so it is safe to call from any thread.
*/
static
c3_o _perform_put_on_database_raw(MDB_txn* transaction_u,
MDB_dbi database_u,
c3_w flags,
void* key,
size_t key_len,
void* value,
size_t value_len) {
MDB_val key_val, value_val;
key_val.mv_size = key_len;
key_val.mv_data = key;
value_val.mv_size = value_len;
value_val.mv_data = value;
c3_w ret_w = mdb_put(transaction_u, database_u, &key_val, &value_val, flags);
if (ret_w != 0) {
u3l_log("lmdb: write failed: %s\n", mdb_strerror(ret_w));
return c3n;
}
return c3y;
}
/* _perform_get_on_database_raw(): Reads a key/value pair to a specific
** database as part of a transaction.
*/
static
c3_o _perform_get_on_database_raw(MDB_txn* transaction_u,
MDB_dbi database_u,
void* key,
size_t key_len,
MDB_val* value) {
MDB_val key_val;
key_val.mv_size = key_len;
key_val.mv_data = key;
c3_w ret_w = mdb_get(transaction_u, database_u, &key_val, value);
if (ret_w != 0) {
u3l_log("lmdb: read failed: %s\n", mdb_strerror(ret_w));
return c3n;
}
return c3y;
}
/* _perform_put_on_database_noun(): Writes a noun to the database.
**
** This requires access to the loom so it must only be run from the libuv
** thread.
*/
static
c3_o _perform_put_on_database_noun(MDB_txn* transaction_u,
MDB_dbi database_u,
c3_c* key,
u3_noun noun) {
// jam noun into an atom representation
u3_atom mat = u3ke_jam(noun);
// copy the jammed noun into a byte buffer we can hand to lmdb
c3_w len_w = u3r_met(3, mat);
c3_y* bytes_y = (c3_y*) malloc(len_w);
u3r_bytes(0, len_w, bytes_y, mat);
c3_o ret = _perform_put_on_database_raw(
transaction_u,
database_u,
0,
key, strlen(key),
bytes_y, len_w);
free(bytes_y);
u3z(mat);
return ret;
}
/* _perform_get_on_database_noun(): Reads a noun from the database.
**
** This requires access to the loom so it must only be run from the libuv
** thread.
*/
static
c3_o _perform_get_on_database_noun(MDB_txn* transaction_u,
MDB_dbi database_u,
c3_c* key,
u3_noun* noun) {
MDB_val value_val;
c3_o ret = _perform_get_on_database_raw(transaction_u,
database_u,
key, strlen(key),
&value_val);
if (ret == c3n) {
return c3y;
}
// Take the bytes and cue them.
u3_atom raw_atom = u3i_bytes(value_val.mv_size, value_val.mv_data);
*noun = u3qe_cue(raw_atom);
return c3y;
}
/* u3_lmdb_write_request: Events to be written together
*/
struct u3_lmdb_write_request {
// The event number of the first event.
c3_d first_event;
// The number of events in this write request. Nonzero.
c3_d event_count;
// An array of serialized event datas. The array size is |event_count|. We
// perform the event serialization on the main thread so we can read the loom
// and write into a malloced structure for the worker thread.
void** malloced_event_data;
// An array of sizes of serialized event datas. We keep track of this for the
// database write.
size_t* malloced_event_data_size;
};
/* u3_lmdb_build_write_request(): Allocates and builds a write request
*/
struct u3_lmdb_write_request*
u3_lmdb_build_write_request(u3_writ* event_u, c3_d count)
{
struct u3_lmdb_write_request* request =
c3_malloc(sizeof(struct u3_lmdb_write_request));
request->first_event = event_u->evt_d;
request->event_count = count;
request->malloced_event_data = c3_malloc(sizeof(void*) * count);
request->malloced_event_data_size = c3_malloc(sizeof(size_t) * count);
for (c3_d i = 0; i < count; ++i) {
// Sanity check that the events in u3_writ are in order.
c3_assert(event_u->evt_d == (request->first_event + i));
// Serialize the jammed event log entry into a malloced buffer we can send
// to the other thread.
c3_w siz_w = u3r_met(3, event_u->mat);
c3_y* data_u = c3_calloc(siz_w);
u3r_bytes(0, siz_w, data_u, event_u->mat);
request->malloced_event_data[i] = data_u;
request->malloced_event_data_size[i] = siz_w;
event_u = event_u->nex_u;
}
return request;
}
/* u3_lmdb_free_write_request(): Frees a write request
*/
void u3_lmdb_free_write_request(struct u3_lmdb_write_request* request) {
for (c3_d i = 0; i < request->event_count; ++i)
free(request->malloced_event_data[i]);
free(request->malloced_event_data);
free(request->malloced_event_data_size);
free(request);
}
/* _write_request_data: callback struct for u3_lmdb_write_event()
*/
struct _write_request_data {
// The database environment to write to. This object is thread-safe, though
// the transactions and handles opened from it are explicitly not.
MDB_env* environment;
// The pier that we're writing for.
u3_pier* pir_u;
// The encapsulated request. This may contain multiple event writes.
struct u3_lmdb_write_request* request;
// Whether the write completed successfully.
c3_o success;
// Called on main loop thread on completion.
void (*on_complete)(c3_o, u3_pier*, c3_d, c3_d);
};
/* _u3_lmdb_write_event_cb(): Implementation of u3_lmdb_write_event()
**
** This is always run on a libuv background worker thread; actual nouns cannot
** be touched here.
*/
static void _u3_lmdb_write_event_cb(uv_work_t* req) {
struct _write_request_data* data = req->data;
// Creates the write transaction.
MDB_txn* transaction_u;
c3_w ret_w = mdb_txn_begin(data->environment,
(MDB_txn *) NULL,
0, /* flags */
&transaction_u);
if (0 != ret_w) {
u3l_log("lmdb: txn_begin fail: %s\n", mdb_strerror(ret_w));
return;
}
// Opens the database as part of the transaction.
c3_w flags_w = MDB_CREATE | MDB_INTEGERKEY;
MDB_dbi database_u;
ret_w = mdb_dbi_open(transaction_u,
"EVENTS",
flags_w,
&database_u);
if (0 != ret_w) {
u3l_log("lmdb: dbi_open fail: %s\n", mdb_strerror(ret_w));
return;
}
struct u3_lmdb_write_request* request = data->request;
for (c3_d i = 0; i < request->event_count; ++i) {
c3_d event_number = request->first_event + i;
c3_o success = _perform_put_on_database_raw(
transaction_u,
database_u,
MDB_NOOVERWRITE,
&event_number,
sizeof(c3_d),
request->malloced_event_data[i],
request->malloced_event_data_size[i]);
if (success == c3n) {
u3l_log("lmdb: failed to write event %" PRIu64 "\n", event_number);
mdb_txn_abort(transaction_u);
data->success = c3n;
return;
}
}
ret_w = mdb_txn_commit(transaction_u);
if (0 != ret_w) {
if ( request->event_count == 1 ) {
u3l_log("lmdb: failed to commit event %" PRIu64 ": %s\n",
request->first_event,
mdb_strerror(ret_w));
} else {
c3_d through = request->first_event + request->event_count - 1ULL;
u3l_log("lmdb: failed to commit events %" PRIu64 " through %" PRIu64
": %s\n",
request->first_event,
through,
mdb_strerror(ret_w));
}
data->success = c3n;
return;
}
data->success = c3y;
}
/* _u3_lmdb_write_event_after_cb(): Implementation of u3_lmdb_write_event()
**
** This is always run on the main loop thread after the worker thread event
** completes.
*/
static void _u3_lmdb_write_event_after_cb(uv_work_t* req, int status) {
struct _write_request_data* data = req->data;
data->on_complete(data->success,
data->pir_u,
data->request->first_event,
data->request->event_count);
u3_lmdb_free_write_request(data->request);
free(data);
free(req);
}
/* u3_lmdb_write_event(): Asynchronously writes events to the database.
**
** This writes all the passed in events along with log metadata updates to the
** database as a single transaction on a worker thread. Once the transaction
** is completed, it calls the passed in callback on the main loop thread.
*/
void u3_lmdb_write_event(MDB_env* environment,
u3_pier* pir_u,
struct u3_lmdb_write_request* request_u,
void (*on_complete)(c3_o, u3_pier*, c3_d, c3_d))
{
// Structure to pass to the worker thread.
struct _write_request_data* data = c3_malloc(sizeof(struct _write_request_data));
data->environment = environment;
data->pir_u = pir_u;
data->request = request_u;
data->on_complete = on_complete;
data->success = c3n;
// Queue asynchronous work to happen on the other thread.
uv_work_t* req = c3_malloc(sizeof(uv_work_t));
req->data = data;
uv_queue_work(uv_default_loop(),
req,
_u3_lmdb_write_event_cb,
_u3_lmdb_write_event_after_cb);
}
/* u3_lmdb_read_events(): Synchronously reads events from the database.
**
** Reads back up to |len_d| events starting with |first_event_d|. For
** each event, the event will be passed to |on_event_read| and further
** reading will be aborted if the callback returns c3n.
**
** Returns c3y on complete success; c3n on any error.
*/
c3_o u3_lmdb_read_events(u3_pier* pir_u,
c3_d first_event_d,
c3_d len_d,
c3_o(*on_event_read)(u3_pier* pir_u, c3_d id,
u3_noun mat))
{
// Creates the read transaction.
MDB_txn* transaction_u;
c3_w ret_w = mdb_txn_begin(pir_u->log_u->db_u,
//environment,
(MDB_txn *) NULL,
MDB_RDONLY, /* flags */
&transaction_u);
if (0 != ret_w) {
u3l_log("lmdb: txn_begin fail: %s\n", mdb_strerror(ret_w));
return c3n;
}
// Opens the database as part of the transaction.
c3_w flags_w = MDB_CREATE | MDB_INTEGERKEY;
MDB_dbi database_u;
ret_w = mdb_dbi_open(transaction_u,
"EVENTS",
flags_w,
&database_u);
if (0 != ret_w) {
u3l_log("lmdb: dbi_open fail: %s\n", mdb_strerror(ret_w));
return c3n;
}
// Creates a cursor to iterate over keys starting at first_event_d.
MDB_cursor* cursor_u;
ret_w = mdb_cursor_open(transaction_u, database_u, &cursor_u);
if (0 != ret_w) {
u3l_log("lmdb: cursor_open fail: %s\n", mdb_strerror(ret_w));
return c3n;
}
// Sets the cursor to the position of first_event_d.
MDB_val key;
MDB_val val;
key.mv_size = sizeof(c3_d);
key.mv_data = &first_event_d;
ret_w = mdb_cursor_get(cursor_u, &key, &val, MDB_SET_KEY);
if (0 != ret_w) {
u3l_log("lmdb: could not find initial event %" PRIu64 ": %s\r\n",
first_event_d, mdb_strerror(ret_w));
mdb_cursor_close(cursor_u);
return c3n;
}
// Load up to len_d events, iterating forward across the cursor.
for (c3_d loaded = 0; (ret_w != MDB_NOTFOUND) && (loaded < len_d); ++loaded) {
// As a sanity check, we make sure that there aren't any discontinuities in
// the sequence of loaded events.
c3_d current_id = first_event_d + loaded;
if (key.mv_size != sizeof(c3_d)) {
u3l_log("lmdb: invalid cursor key\r\n");
return c3n;
}
if (*(c3_d*)key.mv_data != current_id) {
u3l_log("lmdb: missing event in database. Expected %" PRIu64 ", received %"
PRIu64 "\r\n",
current_id,
*(c3_d*)key.mv_data);
return c3n;
}
// Now build the atom version and then the cued version from the raw data
u3_noun mat = u3i_bytes(val.mv_size, val.mv_data);
if (on_event_read(pir_u, current_id, mat) == c3n) {
u3z(mat);
u3l_log("lmdb: aborting replay due to error.\r\n");
return c3n;
}
u3z(mat);
ret_w = mdb_cursor_get(cursor_u, &key, &val, MDB_NEXT);
if (ret_w != 0 && ret_w != MDB_NOTFOUND) {
u3l_log("lmdb: error while loading events: %s\r\n",
mdb_strerror(ret_w));
return c3n;
}
}
mdb_cursor_close(cursor_u);
// Read-only transactions are aborted since we don't need to record the fact
// that we performed a read.
mdb_txn_abort(transaction_u);
return c3y;
}
/* u3_lmdb_get_latest_event_number(): Gets last event id persisted
**
** Reads the last key in order from the EVENTS table as the latest event
** number. On table empty, returns c3y but doesn't modify event_number.
*/
c3_o u3_lmdb_get_latest_event_number(MDB_env* environment, c3_d* event_number)
{
// Creates the read transaction.
MDB_txn* transaction_u;
c3_w ret_w = mdb_txn_begin(environment,
(MDB_txn *) NULL,
0, /* flags */
&transaction_u);
if (0 != ret_w) {
u3l_log("lmdb: txn_begin fail: %s\n", mdb_strerror(ret_w));
return c3n;
}
// Opens the database as part of the transaction.
c3_w flags_w = MDB_CREATE | MDB_INTEGERKEY;
MDB_dbi database_u;
ret_w = mdb_dbi_open(transaction_u,
"EVENTS",
flags_w,
&database_u);
if (0 != ret_w) {
u3l_log("lmdb: dbi_open fail: %s\n", mdb_strerror(ret_w));
return c3n;
}
// Creates a cursor to point to the last event
MDB_cursor* cursor_u;
ret_w = mdb_cursor_open(transaction_u, database_u, &cursor_u);
if (0 != ret_w) {
u3l_log("lmdb: cursor_open fail: %s\n", mdb_strerror(ret_w));
return c3n;
}
// Set the cursor at the end of the line.
MDB_val key;
MDB_val val;
ret_w = mdb_cursor_get(cursor_u, &key, &val, MDB_LAST);
if (MDB_NOTFOUND == ret_w) {
// Clean up, but don't error out.
mdb_cursor_close(cursor_u);
mdb_txn_abort(transaction_u);
return c3y;
}
if (0 != ret_w) {
u3l_log("lmdb: could not find last event: %s\r\n", mdb_strerror(ret_w));
mdb_cursor_close(cursor_u);
mdb_txn_abort(transaction_u);
return c3n;
}
*event_number = *(c3_d*)key.mv_data;
mdb_cursor_close(cursor_u);
// Read-only transactions are aborted since we don't need to record the fact
// that we performed a read.
mdb_txn_abort(transaction_u);
return c3y;
}
/* u3_lmdb_write_identity(): Writes the event log identity information
**
** We have a secondary database (table) in this environment named META where we
** read/write identity information from/to.
*/
c3_o u3_lmdb_write_identity(MDB_env* environment,
u3_noun who,
u3_noun is_fake,
u3_noun life)
{
// Creates the write transaction.
MDB_txn* transaction_u;
c3_w ret_w = mdb_txn_begin(environment,
(MDB_txn *) NULL,
0, /* flags */
&transaction_u);
if (0 != ret_w) {
u3l_log("lmdb: txn_begin fail: %s\n", mdb_strerror(ret_w));
return c3n;
}
// Opens the database as part of the transaction.
c3_w flags_w = MDB_CREATE;
MDB_dbi database_u;
ret_w = mdb_dbi_open(transaction_u,
"META",
flags_w,
&database_u);
if (0 != ret_w) {
u3l_log("lmdb: dbi_open fail: %s\n", mdb_strerror(ret_w));
mdb_txn_abort(transaction_u);
return c3n;
}
c3_o ret;
ret = _perform_put_on_database_noun(transaction_u, database_u, "who", who);
if (ret == c3n) {
mdb_txn_abort(transaction_u);
return c3n;
}
ret = _perform_put_on_database_noun(transaction_u, database_u, "is-fake",
is_fake);
if (ret == c3n) {
mdb_txn_abort(transaction_u);
return c3n;
}
ret = _perform_put_on_database_noun(transaction_u, database_u, "life", life);
if (ret == c3n) {
mdb_txn_abort(transaction_u);
return c3n;
}
ret_w = mdb_txn_commit(transaction_u);
if (0 != ret_w) {
u3l_log("lmdb: failed to commit transaction: %s\n", mdb_strerror(ret_w));
return c3n;
}
return c3y;
}
/* u3_lmdb_read_identity(): Reads the event log identity information.
*/
c3_o u3_lmdb_read_identity(MDB_env* environment,
u3_noun* who,
u3_noun* is_fake,
u3_noun* life) {
// Creates the write transaction.
MDB_txn* transaction_u;
c3_w ret_w = mdb_txn_begin(environment,
(MDB_txn *) NULL,
MDB_RDONLY, /* flags */
&transaction_u);
if (0 != ret_w) {
u3l_log("lmdb: txn_begin fail: %s\n", mdb_strerror(ret_w));
return c3n;
}
// Opens the database as part of the transaction.
MDB_dbi database_u;
ret_w = mdb_dbi_open(transaction_u,
"META",
0,
&database_u);
if (0 != ret_w) {
u3l_log("lmdb: dbi_open fail: %s\n", mdb_strerror(ret_w));
mdb_txn_abort(transaction_u);
return c3n;
}
c3_o ret;
ret = _perform_get_on_database_noun(transaction_u, database_u, "who", who);
if (ret == c3n) {
mdb_txn_abort(transaction_u);
return c3n;
}
ret = _perform_get_on_database_noun(transaction_u, database_u, "is-fake",
is_fake);
if (ret == c3n) {
mdb_txn_abort(transaction_u);
return c3n;
}
ret = _perform_get_on_database_noun(transaction_u, database_u, "life", life);
if (ret == c3n) {
mdb_txn_abort(transaction_u);
return c3n;
}
// Read-only transactions are aborted since we don't need to record the fact
// that we performed a read.
mdb_txn_abort(transaction_u);
return c3y;
}

359
pkg/hs/vere/notes/c/newt.c Normal file
View File

@ -0,0 +1,359 @@
/* vere/newt.c
**
** implements noun blob messages with trivial framing.
**
** a message is a 64-bit little-endian byte count, followed
** by the indicated number of bytes. the bytes are the
** the ++cue of of a noun.
**
** the implementation is relatively inefficient and could
** lose a few copies, mallocs, etc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <setjmp.h>
#include <gmp.h>
#include <sigsegv.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <uv.h>
#include <errno.h>
#include <ncurses/curses.h>
#include <termios.h>
#include <ncurses/term.h>
#include "all.h"
#include "vere/vere.h"
#undef NEWT_VERBOSE
/* _newt_consume(): advance buffer processing.
*/
static void
_newt_consume(u3_moat* mot_u)
{
/* process stray bytes, trying to create a new message
** or add a block to an existing one.
*/
while ( 1 ) {
if ( mot_u->rag_y ) {
/* if there is a live message, add a block to the queue.
*/
if ( mot_u->mes_u ) {
u3_meat* met_u;
/* create block
*/
met_u = c3_malloc(mot_u->len_d + (c3_d) sizeof(u3_meat));
met_u->nex_u = 0;
met_u->len_d = mot_u->len_d;
memcpy(met_u->hun_y, mot_u->rag_y, mot_u->len_d);
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: create: msg %p, new block %p, len %"
PRIu64 ", has %" PRIu64 ", needs %" PRIu64 "\r\n",
getpid(),
mot_u->mes_u,
met_u,
met_u->len_d,
mot_u->mes_u->has_d,
mot_u->mes_u->len_d);
#endif
/* enqueue block
*/
if ( !mot_u->mes_u->meq_u ) {
mot_u->mes_u->meq_u = mot_u->mes_u->qem_u = met_u;
}
else {
mot_u->mes_u->qem_u->nex_u = met_u;
mot_u->mes_u->qem_u = met_u;
}
mot_u->mes_u->has_d += met_u->len_d;
/* free consumed stray bytes
*/
c3_free(mot_u->rag_y);
mot_u->len_d = 0;
mot_u->rag_y = 0;
}
else {
/* no message, but enough stray bytes to fill in
** a length; collect them and create a message.
*/
if ( mot_u->len_d >= 8ULL ) {
c3_d nel_d = 0;
nel_d |= ((c3_d) mot_u->rag_y[0]) << 0ULL;
nel_d |= ((c3_d) mot_u->rag_y[1]) << 8ULL;
nel_d |= ((c3_d) mot_u->rag_y[2]) << 16ULL;
nel_d |= ((c3_d) mot_u->rag_y[3]) << 24ULL;
nel_d |= ((c3_d) mot_u->rag_y[4]) << 32ULL;
nel_d |= ((c3_d) mot_u->rag_y[5]) << 40ULL;
nel_d |= ((c3_d) mot_u->rag_y[6]) << 48ULL;
nel_d |= ((c3_d) mot_u->rag_y[7]) << 56ULL;
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: parsed length %" PRIu64 "\r\n",
getpid(),
nel_d);
#endif
mot_u->len_d -= 8ULL;
mot_u->mes_u = c3_malloc(sizeof(u3_mess));
mot_u->mes_u->len_d = nel_d;
mot_u->mes_u->has_d = 0;
mot_u->mes_u->meq_u = mot_u->mes_u->qem_u = 0;
if ( !mot_u->len_d ) {
c3_free(mot_u->rag_y);
mot_u->rag_y = 0;
}
else {
/* remove consumed length from stray bytes
*/
c3_y* buf_y = c3_malloc(mot_u->len_d);
memcpy(buf_y, mot_u->rag_y + 8, mot_u->len_d);
c3_free(mot_u->rag_y);
mot_u->rag_y = buf_y;
/* remaining bytes will be installed as message meat
*/
continue;
}
}
}
}
/* check for message completions
*/
if ( mot_u->mes_u && (mot_u->mes_u->has_d >= mot_u->mes_u->len_d) ) {
c3_d len_d = mot_u->mes_u->len_d;
c3_y* buf_y = c3_malloc(len_d);
c3_d pat_d = 0;
u3_meat* met_u;
/* we should have just cleared this
*/
c3_assert(!mot_u->rag_y);
c3_assert(!mot_u->len_d);
/* collect queue blocks, cleaning them up; return any spare meat
** to the rag.
*/
{
met_u = mot_u->mes_u->meq_u;
while ( met_u && (pat_d < len_d) ) {
u3_meat* nex_u = met_u->nex_u;
c3_d end_d = (pat_d + met_u->len_d);
c3_d eat_d;
c3_d rem_d;
eat_d = c3_min(len_d, end_d) - pat_d;
memcpy(buf_y + pat_d, met_u->hun_y, eat_d);
pat_d += eat_d;
rem_d = (met_u->len_d - eat_d);
if ( rem_d ) {
mot_u->rag_y = c3_malloc(rem_d);
memcpy(mot_u->rag_y, met_u->hun_y + eat_d, rem_d);
mot_u->len_d = rem_d;
/* one: unless we got a bad length, this has to be the last
** block in the message.
**
** two: bad data on a newt channel can cause us to assert.
** that's actually the right thing for a private channel.
*/
c3_assert(0 == nex_u);
}
c3_free(met_u);
met_u = nex_u;
}
c3_assert(pat_d == len_d);
/* clear the message
*/
c3_free(mot_u->mes_u);
mot_u->mes_u = 0;
}
/* build and send the object
*/
{
u3_noun mat = u3i_bytes((c3_w) len_d, buf_y);
mot_u->pok_f(mot_u->vod_p, mat);
}
/* continue; spare meat may need processing
*/
continue;
}
/* nothing happening, await next event
*/
break;
}
}
/* _raft_alloc(): libuv-style allocator for raft.
*/
static void
_newt_alloc(uv_handle_t* had_u,
size_t len_i,
uv_buf_t* buf_u)
{
void* ptr_v = c3_malloc(len_i);
*buf_u = uv_buf_init(ptr_v, len_i);
}
/* _newt_read_cb(): stream input callback.
*/
void
_newt_read_cb(uv_stream_t* str_u,
ssize_t len_i,
const uv_buf_t* buf_u)
{
c3_d len_d = (c3_d) len_i;
u3_moat* mot_u = (void *)str_u;
if ( UV_EOF == len_i ) {
// u3l_log("newt: %d: stream closed\r\n", getpid());
uv_read_stop(str_u);
mot_u->bal_f(mot_u->vod_p, "stream closed");
}
else {
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: read %ld\r\n", getpid(), len_i);
#endif
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: <bytes>", getpid());
for ( int i = 0; i < len_i; i++) {
if (0 == (i % 16)) u3l_log("\r\n");
u3l_log(" %02x", (unsigned) buf_u->base[i]);
}
u3l_log("\r\nnewt: %d: </bytes>\r\n", getpid());
#endif
// grow read buffer by `len_d` bytes
//
if ( mot_u->rag_y ) {
mot_u->rag_y = c3_realloc(mot_u->rag_y, mot_u->len_d + len_d);
memcpy(mot_u->rag_y + mot_u->len_d, buf_u->base, len_d);
c3_free(buf_u->base);
}
else {
mot_u->rag_y = (c3_y *)buf_u->base;
mot_u->len_d = len_d;
}
_newt_consume(mot_u);
}
}
/* u3_newt_read(): start stream reading.
*/
void
u3_newt_read(u3_moat* mot_u)
{
c3_i err_i;
mot_u->mes_u = 0;
mot_u->len_d = 0;
mot_u->rag_y = 0;
err_i = uv_read_start((uv_stream_t*) &mot_u->pyp_u,
_newt_alloc,
_newt_read_cb);
if ( err_i != 0 ) {
mot_u->bal_f(mot_u, uv_strerror(err_i));
}
}
/* write request for newt
*/
struct _u3_write_t {
uv_write_t wri_u;
u3_mojo* moj_u;
void* vod_p;
c3_y* buf_y;
};
/* _newt_write_cb(): generic write callback.
*/
static void
_newt_write_cb(uv_write_t* wri_u, c3_i sas_i)
{
struct _u3_write_t* req_u = (struct _u3_write_t*)wri_u;
void* vod_p = req_u->vod_p;
u3_mojo* moj_u = req_u->moj_u;
free(req_u->buf_y);
free(req_u);
if ( 0 != sas_i ) {
u3l_log("newt: bad write %d\r\n", sas_i);
moj_u->bal_f(vod_p, uv_strerror(sas_i));
}
}
/* u3_newt_write(): write atom to stream; free atom.
*/
void
u3_newt_write(u3_mojo* moj_u,
u3_atom mat,
void* vod_p)
{
c3_w len_w = u3r_met(3, mat);
c3_y* buf_y = c3_malloc(len_w + 8);
struct _u3_write_t* req_u = c3_malloc(sizeof(*req_u));
uv_buf_t buf_u;
c3_i err_i;
/* write header; c3_d is futureproofing
*/
buf_y[0] = ((len_w >> 0) & 0xff);
buf_y[1] = ((len_w >> 8) & 0xff);
buf_y[2] = ((len_w >> 16) & 0xff);
buf_y[3] = ((len_w >> 24) & 0xff);
buf_y[4] = buf_y[5] = buf_y[6] = buf_y[7] = 0;
u3r_bytes(0, len_w, buf_y + 8, mat);
u3z(mat);
req_u->moj_u = moj_u;
req_u->buf_y = buf_y;
buf_u.base = (c3_c*) buf_y;
buf_u.len = len_w + 8;
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: write %d\n", getpid(), len_w + 8);
#endif
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: <bytes>", getpid());
for ( int i = 0; i < len_w+8; i++) {
if (0 == (i % 16)) u3l_log("\r\n");
u3l_log(" %02x", (unsigned) buf_u.base[i]);
}
u3l_log("\r\nnewt: %d: </bytes>\r\n", getpid());
#endif
if ( 0 != (err_i = uv_write((uv_write_t*)req_u,
(uv_stream_t*)&moj_u->pyp_u,
&buf_u,
1,
_newt_write_cb)) )
{
moj_u->bal_f(moj_u, uv_strerror(err_i));
}
}

2143
pkg/hs/vere/notes/c/pier.c Normal file

File diff suppressed because it is too large Load Diff

482
pkg/hs/vere/notes/c/reck.c Normal file
View File

@ -0,0 +1,482 @@
/* vere/reck.c
**
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <uv.h>
#include <ncurses/curses.h>
#include <termios.h>
#include <ncurses/term.h>
#include "all.h"
#include "vere/vere.h"
/* _reck_mole(): parse simple atomic mole.
*/
static u3_noun
_reck_mole(u3_noun fot,
u3_noun san,
c3_d* ato_d)
{
u3_noun uco = u3dc("slaw", fot, san);
u3_noun p_uco, q_uco;
if ( (c3n == u3r_cell(uco, &p_uco, &q_uco)) ||
(u3_nul != p_uco) )
{
u3l_log("strange mole %s\n", u3r_string(san));
u3z(fot); u3z(uco); return c3n;
}
else {
*ato_d = u3r_chub(0, q_uco);
u3z(fot); u3z(uco); return c3y;
}
}
/* _reck_lily(): parse little atom.
*/
static u3_noun
_reck_lily(u3_noun fot, u3_noun txt, c3_l* tid_l)
{
c3_d ato_d;
if ( c3n == _reck_mole(fot, txt, &ato_d) ) {
return c3n;
} else {
if ( ato_d >= 0x80000000ULL ) {
return c3n;
} else {
*tid_l = (c3_l) ato_d;
return c3y;
}
}
}
/* _reck_orchid(): parses only a number as text
*
* Parses a text string which contains a decimal number. In practice, this
* number is always '1'.
*/
static u3_noun
_reck_orchid(u3_noun fot, u3_noun txt, c3_l* tid_l)
{
c3_c* str = u3r_string(txt);
c3_d ato_d = strtol(str, NULL, 10);
free(str);
if ( ato_d >= 0x80000000ULL ) {
return c3n;
} else {
*tid_l = (c3_l) ato_d;
return c3y;
}
}
/* _reck_kick_term(): apply terminal outputs.
*/
static u3_noun
_reck_kick_term(u3_pier* pir_u, u3_noun pox, c3_l tid_l, u3_noun fav)
{
u3_noun p_fav;
if ( c3n == u3du(fav) ) {
u3z(pox); u3z(fav); return c3n;
}
else switch ( u3h(fav) ) {
default: u3z(pox); u3z(fav); return c3n;
case c3__bbye:
{
u3z(pox); u3z(fav); return c3y;
} break;
case c3__blit: p_fav = u3t(fav);
{
u3_term_ef_blit(tid_l, u3k(p_fav));
u3z(pox); u3z(fav); return c3y;
} break;
// this can return through dill due to our fscked up boot sequence
//
case c3__send: {
u3_noun lan = u3k(u3h(u3t(fav)));
u3_noun pac = u3k(u3t(u3t(fav)));
u3l_log("kick: strange send\r\n");
u3_ames_ef_send(pir_u, lan, pac);
u3z(pox); u3z(fav); return c3y;
} break;
case c3__logo:
{
u3_pier_exit(pir_u);
u3_Host.xit_i = u3t(fav);
u3z(pox); u3z(fav); return c3y;
} break;
case c3__init: p_fav = u3t(fav);
{
// daemon ignores %init
// u3A->own = u3nc(u3k(p_fav), u3A->own);
// u3l_log("kick: init: %d\n", p_fav);
u3z(pox); u3z(fav); return c3y;
} break;
case c3__mass: p_fav = u3t(fav);
{
u3z(pox); u3z(fav);
// gc the daemon area
//
uv_timer_start(&u3K.tim_u, (uv_timer_cb)u3_daemon_grab, 0, 0);
return c3y;
} break;
}
c3_assert(!"not reached"); return 0;
}
/* _reck_kick_http(): apply http effects.
*/
static u3_noun
_reck_kick_http(u3_pier* pir_u,
u3_noun pox,
c3_l sev_l,
c3_l coq_l,
c3_l seq_l,
u3_noun fav)
{
u3_noun p_fav, q_fav;
if ( c3n == u3du(fav) ) {
u3z(pox); u3z(fav); return c3n;
}
else switch ( u3h(fav) ) {
default: u3z(pox); u3z(fav); return c3n;
case c3__form: p_fav = u3t(fav);
{
u3_http_ef_form(u3k(p_fav));
// The control server has now started.
//
// If we're in daemon mode, we need to inform the parent process
// that we've finished booting.
if (u3_Host.bot_f) {
u3_Host.bot_f();
}
u3z(pox); u3z(fav);
return c3y;
}
case c3__that: p_fav = u3t(fav);
{
u3_http_ef_that(u3k(p_fav));
u3z(pox); u3z(fav);
return c3y;
}
case c3__thus: p_fav = u3h(u3t(fav)); q_fav = u3t(u3t(fav));
{
u3_cttp_ef_thus(u3r_word(0, p_fav), u3k(q_fav));
u3z(pox); u3z(fav);
return c3y;
}
case c3__thou: p_fav = u3t(fav);
{
u3_http_ef_thou(sev_l, coq_l, seq_l, u3k(p_fav));
u3z(pox); u3z(fav);
return c3y;
} break;
}
c3_assert(!"not reached"); return c3n;
}
/* _reck_kick_behn(): apply packet network outputs.
*/
static u3_noun
_reck_kick_behn(u3_pier* pir_u, u3_noun pox, u3_noun fav)
{
switch ( u3h(fav) ) {
default: break;
case c3__doze: {
u3_behn_ef_doze(pir_u, u3k(u3t(fav)));
u3z(pox); u3z(fav); return c3y;
} break;
}
u3z(pox); u3z(fav); return c3n;
}
/* _reck_kick_sync(): apply sync outputs.
*/
static u3_noun
_reck_kick_sync(u3_pier* pir_u, u3_noun pox, u3_noun fav)
{
switch ( u3h(fav) ) {
default: break;
case c3__dirk: {
u3_unix_ef_dirk(pir_u, u3k(u3t(fav)));
u3z(pox); u3z(fav); return c3y;
}
case c3__ergo: {
u3_noun mon = u3k(u3h(u3t(fav)));
u3_noun can = u3k(u3t(u3t(fav)));
u3_unix_ef_ergo(pir_u, mon, can);
u3z(pox); u3z(fav); return c3y;
} break;
case c3__ogre: {
u3_unix_ef_ogre(pir_u, u3k(u3t(fav)));
u3z(pox); u3z(fav); return c3y;
}
case c3__hill: {
u3_unix_ef_hill(pir_u, u3k(u3t(fav)));
u3z(pox); u3z(fav); return c3y;
}
}
// XX obviously not right!
// ? looks fine to me
u3z(pox); u3z(fav); return c3n;
}
/* _reck_kick_newt(): apply packet network outputs.
*/
static u3_noun
_reck_kick_newt(u3_pier* pir_u, u3_noun pox, u3_noun fav)
{
switch ( u3h(fav) ) {
default: break;
case c3__send: {
u3_noun lan = u3k(u3h(u3t(fav)));
u3_noun pac = u3k(u3t(u3t(fav)));
u3_ames_ef_send(pir_u, lan, pac);
u3z(pox); u3z(fav); return c3y;
} break;
case c3__turf: {
u3_ames_ef_turf(pir_u, u3k(u3t(fav)));
u3z(pox); u3z(fav); return c3y;
} break;
}
u3z(pox); u3z(fav); return c3n;
}
/* _reck_kick_ames(): apply packet network outputs.
*/
static u3_noun
_reck_kick_ames(u3_pier* pir_u, u3_noun pox, u3_noun fav)
{
u3_noun p_fav;
switch ( u3h(fav) ) {
default: break;
case c3__init: p_fav = u3t(fav);
{
// daemon ignores %init
// u3A->own = u3nc(u3k(p_fav), u3A->own);
// u3l_log("kick: init: %d\n", p_fav);
u3z(pox); u3z(fav); return c3y;
} break;
}
u3z(pox); u3z(fav); return c3n;
}
/* _reck_kick_spec(): apply an effect, by path.
*/
static u3_noun
_reck_kick_spec(u3_pier* pir_u, u3_noun pox, u3_noun fav)
{
u3_noun i_pox, t_pox;
if ( (c3n == u3r_cell(pox, &i_pox, &t_pox)) ||
((i_pox != u3_blip) &&
(i_pox != c3__gold) &&
(i_pox != c3__iron) &&
(i_pox != c3__lead)) )
{
u3z(pox); u3z(fav); return c3n;
} else {
u3_noun it_pox, tt_pox;
if ( (c3n == u3r_cell(t_pox, &it_pox, &tt_pox)) ) {
u3z(pox); u3z(fav); return c3n;
}
else switch ( it_pox ) {
default: u3z(pox); u3z(fav); return c3n;
case c3__http: {
u3_noun pud = tt_pox;
u3_noun p_pud, t_pud, tt_pud, q_pud, r_pud, s_pud;
c3_l sev_l, coq_l, seq_l;
if ( (c3n == u3r_cell(pud, &p_pud, &t_pud)) ||
(c3n == _reck_lily(c3__uv, u3k(p_pud), &sev_l)) )
{
u3z(pox); u3z(fav); return c3n;
}
if ( u3_nul == t_pud ) {
coq_l = seq_l = 0;
}
else {
if ( (c3n == u3r_cell(t_pud, &q_pud, &tt_pud)) ||
(c3n == _reck_lily(c3__ud, u3k(q_pud), &coq_l)) )
{
u3z(pox); u3z(fav); return c3n;
}
if ( u3_nul == tt_pud ) {
seq_l = 0;
} else {
if ( (c3n == u3r_cell(tt_pud, &r_pud, &s_pud)) ||
(u3_nul != s_pud) ||
(c3n == _reck_lily(c3__ud, u3k(r_pud), &seq_l)) )
{
u3z(pox); u3z(fav); return c3n;
}
}
}
return _reck_kick_http(pir_u, pox, sev_l, coq_l, seq_l, fav);
} break;
case c3__behn: {
return _reck_kick_behn(pir_u, pox, fav);
} break;
case c3__clay:
case c3__boat:
case c3__sync: {
return _reck_kick_sync(pir_u, pox, fav);
} break;
case c3__newt: {
return _reck_kick_newt(pir_u, pox, fav);
} break;
case c3__ames: {
if ( (u3_nul != tt_pox) ) {
u3z(pox); u3z(fav); return c3n;
}
else {
return _reck_kick_ames(pir_u, pox, fav);
}
} break;
case c3__init: {
// daemon ignores %init
// p_fav = u3t(fav);
// u3A->own = u3nc(u3k(p_fav), u3A->own);
// u3l_log("kick: init: %d\n", p_fav);
u3z(pox); u3z(fav); return c3y;
} break;
case c3__term: {
u3_noun pud = tt_pox;
u3_noun p_pud, q_pud;
c3_l tid_l;
if ( (c3n == u3r_cell(pud, &p_pud, &q_pud)) ||
(u3_nul != q_pud) ||
(c3n == _reck_orchid(c3__ud, u3k(p_pud), &tid_l)) )
{
u3l_log("term: bad tire\n");
u3z(pox); u3z(fav); return c3n;
} else {
return _reck_kick_term(pir_u, pox, tid_l, fav);
}
} break;
}
}
c3_assert(!"not reached");
return c3n;
}
/* _reck_kick_norm(): non path-specific effect handling.
*/
static u3_noun
_reck_kick_norm(u3_pier* pir_u, u3_noun pox, u3_noun fav)
{
if ( c3n == u3du(fav) ) {
u3z(pox); u3z(fav); return c3n;
}
else switch ( u3h(fav) ) {
default: u3z(pox); u3z(fav); return c3n;
case c3__vega:
{
u3l_log("<<<reset>>>\n");
u3z(pox); u3z(fav);
// reclaim memory from persistent caches
//
u3m_reclaim();
return c3y;
}
case c3__exit:
{
u3l_log("<<<goodbye>>>\n");
u3_pier_exit(pir_u);
u3z(pox); u3z(fav); return c3y;
} break;
}
c3_assert(!"not reached"); return c3n;
u3z(pox); u3z(fav); return c3n;
}
/* u3_reck_kick(): handle effect.
*/
void
u3_reck_kick(u3_pier* pir_u, u3_noun ovo)
{
if ( (c3n == _reck_kick_spec(pir_u, u3k(u3h(ovo)), u3k(u3t(ovo)))) &&
(c3n == _reck_kick_norm(pir_u, u3k(u3h(ovo)), u3k(u3t(ovo)))) )
{
#if 0
if ( (c3__warn != u3h(u3t(ovo))) &&
(c3__text != u3h(u3t(ovo))) &&
(c3__note != u3h(u3t(ovo))) )
#endif
#if 1
if ( (c3__crud == u3h(u3t(ovo))) )
#if 0
(c3__talk == u3h(u3t(ovo))) ||
(c3__helo == u3h(u3t(ovo))) ||
(c3__init == u3h(u3t(ovo))) )
#endif
{
u3_pier_work(pir_u,
u3nt(u3_blip, c3__term, u3_nul),
u3nc(c3__flog, u3k(u3t(ovo))));
}
else {
u3_noun tox = u3do("spat", u3k(u3h(ovo)));
u3l_log("kick: lost %%%s on %s\n",
u3r_string(u3h(u3t(ovo))),
u3r_string(tox));
u3z(tox);
#if 0
if ( c3__hear == u3h(u3t(ovo)) ) {
c3_assert(0);
}
#endif
}
#endif
}
u3z(ovo);
}

View File

@ -0,0 +1,66 @@
/* vere/save.c
**
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <termios.h>
#include <uv.h>
#include "all.h"
#include "vere/vere.h"
/* _save_time_cb(): timer callback.
*/
static void
_save_time_cb(uv_timer_t* tim_u)
{
u3_pier *pir_u = tim_u->data;
u3_pier_snap(pir_u);
}
/* u3_save_ef_chld(): report save termination.
*/
void
u3_save_ef_chld(u3_pier *pir_u)
{
u3_save* sav_u = pir_u->sav_u;
c3_i loc_i;
c3_w pid_w;
/* modified for cases with no pid_w
*/
u3l_log("checkpoint: complete %d\n", sav_u->pid_w);
pid_w = wait(&loc_i);
if (0 != sav_u->pid_w) {
c3_assert(pid_w == sav_u->pid_w);
}
else {
c3_assert(pid_w > 0);
}
sav_u->pid_w = 0;
}
/* u3_save_io_init(): initialize autosave.
*/
void
u3_save_io_init(u3_pier *pir_u)
{
u3_save* sav_u = pir_u->sav_u;
sav_u->req_d = 0;
sav_u->dun_d = 0;
sav_u->pid_w = 0;
sav_u->tim_u.data = pir_u;
uv_timer_init(u3L, &sav_u->tim_u);
uv_timer_start(&sav_u->tim_u, _save_time_cb, 120000, 120000);
}
/* u3_save_io_exit(): terminate save I/O.
*/
void
u3_save_io_exit(u3_pier *pir_u)
{
}

1342
pkg/hs/vere/notes/c/term.c Normal file

File diff suppressed because it is too large Load Diff

179
pkg/hs/vere/notes/c/time.c Normal file
View File

@ -0,0 +1,179 @@
/* vere/time.c
**
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <uv.h>
#include <ncurses/curses.h>
#include <termios.h>
#include <ncurses/term.h>
#include "all.h"
#include "vere/vere.h"
/* u3_time_sec_in(): urbit seconds from unix time.
**
** Adjust for future leap secs!
*/
c3_d
u3_time_sec_in(c3_w unx_w)
{
return 0x8000000cce9e0d80ULL + (c3_d)unx_w;
}
/* u3_time_sec_out(): unix time from urbit seconds.
**
** Adjust for future leap secs!
*/
c3_w
u3_time_sec_out(c3_d urs_d)
{
c3_d adj_d = (urs_d - 0x8000000cce9e0d80ULL);
if ( adj_d > 0xffffffffULL ) {
fprintf(stderr, "Agh! It's 2106! And no one's fixed this shite!\n");
exit(1);
}
return (c3_w)adj_d;
}
/* u3_time_fsc_in(): urbit fracto-seconds from unix microseconds.
*/
c3_d
u3_time_fsc_in(c3_w usc_w)
{
c3_d usc_d = usc_w;
return ((usc_d * 65536ULL) / 1000000ULL) << 48ULL;
}
/* u3_time_fsc_out: unix microseconds from urbit fracto-seconds.
*/
c3_w
u3_time_fsc_out(c3_d ufc_d)
{
return (c3_w) (((ufc_d >> 48ULL) * 1000000ULL) / 65536ULL);
}
/* u3_time_msc_out: unix microseconds from urbit fracto-seconds.
*/
c3_w
u3_time_msc_out(c3_d ufc_d)
{
return (c3_w) (((ufc_d >> 48ULL) * 1000ULL) / 65536ULL);
}
/* u3_time_in_tv(): urbit time from struct timeval.
*/
u3_atom
u3_time_in_tv(struct timeval* tim_tv)
{
c3_w unx_w = tim_tv->tv_sec;
c3_w usc_w = tim_tv->tv_usec;
c3_d cub_d[2];
cub_d[0] = u3_time_fsc_in(usc_w);
cub_d[1] = u3_time_sec_in(unx_w);
return u3i_chubs(2, cub_d);
}
/* u3_time_out_tv(): struct timeval from urbit time.
*/
void
u3_time_out_tv(struct timeval* tim_tv, u3_noun now)
{
c3_d ufc_d = u3r_chub(0, now);
c3_d urs_d = u3r_chub(1, now);
tim_tv->tv_sec = u3_time_sec_out(urs_d);
tim_tv->tv_usec = u3_time_fsc_out(ufc_d);
u3z(now);
}
/* u3_time_in_ts(): urbit time from struct timespec.
*/
u3_atom
u3_time_in_ts(struct timespec* tim_ts)
{
struct timeval tim_tv;
tim_tv.tv_sec = tim_ts->tv_sec;
tim_tv.tv_usec = (tim_ts->tv_nsec / 1000);
return u3_time_in_tv(&tim_tv);
}
#if defined(U3_OS_linux)
/* u3_time_t_in_ts(): urbit time from time_t.
*/
u3_atom
u3_time_t_in_ts(time_t tim)
{
struct timeval tim_tv;
tim_tv.tv_sec = tim;
tim_tv.tv_usec = 0;
return u3_time_in_tv(&tim_tv);
}
#endif // defined(U3_OS_linux)
/* u3_time_out_ts(): struct timespec from urbit time.
*/
void
u3_time_out_ts(struct timespec* tim_ts, u3_noun now)
{
struct timeval tim_tv;
u3_time_out_tv(&tim_tv, now);
tim_ts->tv_sec = tim_tv.tv_sec;
tim_ts->tv_nsec = (tim_tv.tv_usec * 1000);
}
/* u3_time_gap_ms(): (wen - now) in ms.
*/
c3_d
u3_time_gap_ms(u3_noun now, u3_noun wen)
{
if ( c3n == u3ka_gth(u3k(wen), u3k(now)) ) {
u3z(wen); u3z(now);
return 0ULL;
}
else {
u3_noun dif = u3ka_sub(wen, now);
c3_d fsc_d = u3r_chub(0, dif);
c3_d sec_d = u3r_chub(1, dif);
u3z(dif);
return (sec_d * 1000ULL) + u3_time_msc_out(fsc_d);
}
}
/* u3_time_gap_double(): (wen - now) in libev resolution.
*/
double
u3_time_gap_double(u3_noun now, u3_noun wen)
{
mpz_t now_mp, wen_mp, dif_mp;
double sec_g = (((double)(1ULL << 32ULL)) * ((double)(1ULL << 32ULL)));
double gap_g, dif_g;
u3r_mp(now_mp, now);
u3r_mp(wen_mp, wen);
mpz_init(dif_mp);
mpz_sub(dif_mp, wen_mp, now_mp);
u3z(now);
u3z(wen);
dif_g = mpz_get_d(dif_mp) / sec_g;
gap_g = (dif_g > 0.0) ? dif_g : 0.0;
mpz_clear(dif_mp); mpz_clear(wen_mp); mpz_clear(now_mp);
return gap_g;
}

1333
pkg/hs/vere/notes/c/unix.c Normal file

File diff suppressed because it is too large Load Diff

334
pkg/hs/vere/notes/c/walk.c Normal file
View File

@ -0,0 +1,334 @@
/* vere/walk.c
**
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <uv.h>
#include <ncurses/curses.h>
#include <termios.h>
#include <ncurses/term.h>
#include <errno.h>
#include "all.h"
#include "vere/vere.h"
/* |%
** ++ arch :: fs node
** $% [& p=@uvI q=*] :: file, hash/data
** [| p=(map ,@ta arch)] :: directory
** == ::
** --
*/
#if 0
static u3_noun
_walk_ok(u3_noun nod)
{
u3_noun don = u3n_mung(u3k(u2A->toy.arch), u3k(nod));
if ( c3n == u3_sing(nod, don) ) {
c3_assert(0);
}
u3z(don);
return nod;
}
#endif
/* u3_walk_safe(): load file or 0.
*/
u3_noun
u3_walk_safe(c3_c* pas_c)
{
struct stat buf_b;
c3_i fid_i = open(pas_c, O_RDONLY, 0644);
c3_w fln_w, red_w;
c3_y* pad_y;
if ( (fid_i < 0) || (fstat(fid_i, &buf_b) < 0) ) {
// u3l_log("%s: %s\n", pas_c, strerror(errno));
return 0;
}
fln_w = buf_b.st_size;
pad_y = c3_malloc(buf_b.st_size);
red_w = read(fid_i, pad_y, fln_w);
close(fid_i);
if ( fln_w != red_w ) {
free(pad_y);
return 0;
}
else {
u3_noun pad = u3i_bytes(fln_w, (c3_y *)pad_y);
free(pad_y);
return pad;
}
}
/* u3_walk_load(): load file or bail.
*/
u3_noun
u3_walk_load(c3_c* pas_c)
{
struct stat buf_b;
c3_i fid_i = open(pas_c, O_RDONLY, 0644);
c3_w fln_w, red_w;
c3_y* pad_y;
if ( (fid_i < 0) || (fstat(fid_i, &buf_b) < 0) ) {
u3l_log("%s: %s\n", pas_c, strerror(errno));
return u3m_bail(c3__fail);
}
fln_w = buf_b.st_size;
pad_y = c3_malloc(buf_b.st_size);
red_w = read(fid_i, pad_y, fln_w);
close(fid_i);
if ( fln_w != red_w ) {
free(pad_y);
return u3m_bail(c3__fail);
}
else {
u3_noun pad = u3i_bytes(fln_w, (c3_y *)pad_y);
free(pad_y);
return pad;
}
}
/* _walk_mkdirp(): recursively make directories in pax at bas_c (RETAIN)
*/
static void
_walk_mkdirp(c3_c* bas_c, u3_noun pax)
{
c3_c* pax_c;
c3_y* waq_y;
c3_w pax_w, fas_w, len_w;
if ( u3_nul == pax ) {
return;
}
pax_w = u3r_met(3, u3h(pax));
fas_w = strlen(bas_c);
len_w = 1 + fas_w + pax_w;
pax_c = c3_malloc(1 + len_w);
strncpy(pax_c, bas_c, len_w);
pax_c[fas_w] = '/';
waq_y = (void*)(1 + pax_c + fas_w);
u3r_bytes(0, pax_w, waq_y, u3h(pax));
pax_c[len_w] = '\0';
if ( 0 != mkdir(pax_c, 0755) && EEXIST != errno ) {
u3l_log("error mkdiring %s: %s\n", pax_c, strerror(errno));
u3m_bail(c3__fail);
}
_walk_mkdirp(pax_c, u3t(pax));
free(pax_c);
}
/* u3_walk_save(): save file or bail.
*/
void
u3_walk_save(c3_c* pas_c, u3_noun tim, u3_atom pad, c3_c* bas_c, u3_noun pax)
{
c3_i fid_i = open(pas_c, O_WRONLY | O_CREAT | O_TRUNC, 0666);
c3_w fln_w, rit_w;
c3_y* pad_y;
if ( fid_i < 0 ) {
if ( ENOENT == errno && u3_nul != pax ) {
_walk_mkdirp(bas_c, pax);
return u3_walk_save(pas_c, tim, pad, 0, u3_nul);
}
u3l_log("%s: %s\n", pas_c, strerror(errno));
u3m_bail(c3__fail);
}
fln_w = u3r_met(3, pad);
pad_y = c3_malloc(fln_w);
u3r_bytes(0, fln_w, pad_y, pad);
u3z(pad);
u3z(pax);
rit_w = write(fid_i, pad_y, fln_w);
close(fid_i);
free(pad_y);
if ( rit_w != fln_w ) {
u3l_log("%s: %s\n", pas_c, strerror(errno));
u3m_bail(c3__fail);
}
if ( 0 != tim ) {
struct timeval tim_tv[2];
u3_time_out_tv(&tim_tv[0], u3k(tim));
u3_time_out_tv(&tim_tv[1], tim);
utimes(pas_c, tim_tv);
}
}
/* _walk_in(): inner loop of _walk(), producing map.
*/
static u3_noun
_walk_in(const c3_c* dir_c, c3_w len_w)
{
DIR* dir_d = opendir(dir_c);
u3_noun map = u3_nul;
if ( !dir_d ) {
return u3_nul;
}
else while ( 1 ) {
struct dirent ent_n;
struct dirent* out_n;
if ( u3_readdir_r(dir_d, &ent_n, &out_n) != 0 ) {
u3l_log("%s: %s\n", dir_c, strerror(errno));
break;
}
else if ( !out_n ) {
break;
}
else if ( !strcmp(out_n->d_name, ".") ||
!strcmp(out_n->d_name, "..") ||
('~' == out_n->d_name[0]) ||
('.' == out_n->d_name[0]) ) // XX restricts some spans
{
continue;
}
else {
c3_c* fil_c = out_n->d_name;
c3_w lef_w = len_w + 1 + strlen(fil_c);
c3_c* pat_c = c3_malloc(lef_w + 1);
struct stat buf_b;
strncpy(pat_c, dir_c, lef_w);
pat_c[len_w] = '/';
strncpy(pat_c + len_w + 1, fil_c, lef_w);
pat_c[lef_w] = '\0';
if ( 0 != stat(pat_c, &buf_b) ) {
free(pat_c);
} else {
u3_noun tim = c3_stat_mtime(&buf_b);
if ( !S_ISDIR(buf_b.st_mode) ) {
c3_c* dot_c = strrchr(fil_c, '.');
c3_c* nam_c = strdup(fil_c);
c3_c* ext_c = strdup(dot_c + 1);
nam_c[dot_c - fil_c] = 0;
{
u3_noun nam = u3i_string(nam_c);
u3_noun ext = u3i_string(ext_c);
u3_noun get = u3kdb_get(u3k(map), u3k(nam));
u3_noun dat = u3_walk_load(pat_c);
u3_noun hax;
if ( !strcmp("noun", ext_c) ) {
dat = u3ke_cue(dat);
}
hax = u3do("sham", u3k(dat));
if ( u3_none == get ) { get = u3_nul; }
get = u3kdb_put(get, ext, u3nt(c3y, hax, dat));
map = u3kdb_put(map, nam, u3nc(c3n, get));
}
free(nam_c);
free(ext_c);
}
else {
u3_noun dir = _walk_in(pat_c, lef_w);
if ( u3_nul != dir ) {
map = u3kdb_put
(map, u3i_string(fil_c), u3nc(c3n, dir));
}
else u3z(tim);
}
free(pat_c);
}
}
}
closedir(dir_d);
return map;
}
/* u3_walk(): traverse `dir_c` to produce an arch, updating `old`.
*/
u3_noun
u3_walk(const c3_c* dir_c, u3_noun old)
{
// XX - obviously, cheaper to update old data.
u3z(old);
{
struct stat buf_b;
if ( 0 != stat(dir_c, &buf_b) ) {
u3l_log("can't stat %s\n", dir_c);
// return u3m_bail(c3__fail);
c3_assert(0);
}
else {
return u3nc(c3n,
_walk_in(dir_c, strlen(dir_c)));
}
}
}
/* u3_path(): C unix path in computer for file or directory.
*/
c3_c*
u3_path(c3_o fyl, u3_noun pax)
{
c3_w len_w;
c3_c *pas_c;
// measure
//
len_w = strlen(u3_Local);
{
u3_noun wiz = pax;
while ( u3_nul != wiz ) {
len_w += (1 + u3r_met(3, u3h(wiz)));
wiz = u3t(wiz);
}
}
// cut
//
pas_c = c3_malloc(len_w + 1);
strncpy(pas_c, u3_Local, len_w);
pas_c[len_w] = '\0';
{
u3_noun wiz = pax;
c3_c* waq_c = (pas_c + strlen(pas_c));
while ( u3_nul != wiz ) {
c3_w tis_w = u3r_met(3, u3h(wiz));
if ( (c3y == fyl) && (u3_nul == u3t(wiz)) ) {
*waq_c++ = '.';
} else *waq_c++ = '/';
u3r_bytes(0, tis_w, (c3_y*)waq_c, u3h(wiz));
waq_c += tis_w;
wiz = u3t(wiz);
}
*waq_c = 0;
}
u3z(pax);
return pas_c;
}

View File

@ -0,0 +1,947 @@
/* worker/main.c
**
** the main loop of a worker process.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <setjmp.h>
#include <gmp.h>
#include <sigsegv.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <uv.h>
#include <errno.h>
#include <ncurses/curses.h>
#include <termios.h>
#include <ncurses/term.h>
#include "all.h"
#include <vere/vere.h>
typedef struct _u3_worker {
c3_w len_w; // boot sequence length
u3_noun roe; // lifecycle formulas
c3_d sen_d; // last event requested
c3_d dun_d; // last event processed
c3_l mug_l; // hash of state
c3_d key_d[4]; // disk key
u3_moat inn_u; // message input
u3_mojo out_u; // message output
c3_c* dir_c; // execution directory (pier)
} u3_worker;
static u3_worker u3V;
/*
:: worker to daemon protocol
::
|%
:: +plea: from worker to daemon
::
+$ plea
$% :: status on startup
::
$: %play
$= p
:: ~ if no snapshot
::
%- unit
:: p: event number expected
:: q: mug of kernel
:: r: identity, fake flag
::
[p=@ q=@ r=[our=@p fak=?]]
==
:: event executed unchanged (in response to %work)
::
$: %done
:: p: event number
:: q: mug of kernel
:: r: effects
::
[p=@ q=@ r=(list ovum)]
==
:: replace event and retry (in response to %work)
::
$: %work
:: p: event number
:: q: mug of kernel
:: r: replacement event (at date)
::
[p=@ q=@ r=(pair date ovum)]
==
:: sends a line to stderr while computing event
::
$: %stdr
:: p: event number
:: q: output cord
::
[p=@ q=cord]
==
:: send slog hint while computing event
::
$: %slog
:: p: event number
:: q: priority
:: r: output tank
::
[p=@ q=@ r=tank]
== ==
:: +writ: from daemon to worker
::
+$ writ
$% :: prepare to boot
::
:: p: identity
:: q: fake?
:: r: number of boot formulas
::
[%boot p=@p q=? r=@]
:: exit immediately
::
:: p: exit code
::
[%exit p=@]
:: save snapshot to disk
::
:: p: event number
::
[%save p=@]
:: execute event
::
$: %work
:: p: event number
:: q: a jammed noun [mug [date ovum]]
::
[p=@ q=@]
== ==
--
*/
/* _worker_space(): print n spaces.
*/
void _worker_space(FILE* fil_u, c3_w n)
{
for (; n > 0; n--)
(fprintf(fil_u," "));
}
/* _worker_print_memory(): print memory amount.
**
** Helper for _worker_prof(), just an un-captioned u3a_print_memory().
*/
void
_worker_print_memory(FILE* fil_u, c3_w wor_w)
{
c3_w byt_w = (wor_w * 4);
c3_w gib_w = (byt_w / 1000000000);
c3_w mib_w = (byt_w % 1000000000) / 1000000;
c3_w kib_w = (byt_w % 1000000) / 1000;
c3_w bib_w = (byt_w % 1000);
if ( gib_w ) {
(fprintf(fil_u, "GB/%d.%03d.%03d.%03d\r\n",
gib_w, mib_w, kib_w, bib_w));
}
else if ( mib_w ) {
(fprintf(fil_u, "MB/%d.%03d.%03d\r\n", mib_w, kib_w, bib_w));
}
else if ( kib_w ) {
(fprintf(fil_u, "KB/%d.%03d\r\n", kib_w, bib_w));
}
else {
(fprintf(fil_u, "B/%d\r\n", bib_w));
}
}
/* _worker_prof(): print memory profile. RETAIN.
*/
c3_w
_worker_prof(FILE* fil_u, c3_w den, u3_noun mas)
{
c3_w tot_w = 0;
u3_noun h_mas, t_mas;
if ( c3n == u3r_cell(mas, &h_mas, &t_mas) ) {
_worker_space(fil_u, den);
fprintf(fil_u, "mistyped mass\r\n");
return tot_w;
}
else if ( _(u3du(h_mas)) ) {
_worker_space(fil_u, den);
fprintf(fil_u, "mistyped mass head\r\n");
{
c3_c* lab_c = u3m_pretty(h_mas);
fprintf(fil_u, "h_mas: %s", lab_c);
free(lab_c);
}
return tot_w;
}
else {
_worker_space(fil_u, den);
{
c3_c* lab_c = u3m_pretty(h_mas);
fprintf(fil_u, "%s: ", lab_c);
free(lab_c);
}
u3_noun it_mas, tt_mas;
if ( c3n == u3r_cell(t_mas, &it_mas, &tt_mas) ) {
fprintf(fil_u, "mistyped mass tail\r\n");
return tot_w;
}
else if ( c3y == it_mas ) {
tot_w += u3a_mark_noun(tt_mas);
_worker_print_memory(fil_u, tot_w);
#if 1
/* The basic issue here is that tt_mas is included in .sac
* (the whole profile), so they can't both be roots in the
* normal sense. When we mark .sac later on, we want tt_mas
* to appear unmarked, but its children should be already
* marked.
*/
if ( _(u3a_is_dog(tt_mas)) ) {
u3a_box* box_u = u3a_botox(u3a_to_ptr(tt_mas));
#ifdef U3_MEMORY_DEBUG
if ( 1 == box_u->eus_w ) {
box_u->eus_w = 0xffffffff;
}
else {
box_u->eus_w -= 1;
}
#else
if ( -1 == (c3_w)box_u->use_w ) {
box_u->use_w = 0x80000000;
}
else {
box_u->use_w += 1;
}
#endif
}
#endif
return tot_w;
}
else if ( c3n == it_mas ) {
fprintf(fil_u, "\r\n");
while ( _(u3du(tt_mas)) ) {
tot_w += _worker_prof(fil_u, den+2, u3h(tt_mas));
tt_mas = u3t(tt_mas);
}
_worker_space(fil_u, den);
fprintf(fil_u, "--");
_worker_print_memory(fil_u, tot_w);
return tot_w;
}
else {
_worker_space(fil_u, den);
fprintf(fil_u, "mistyped (strange) mass tail\r\n");
return tot_w;
}
}
}
/* _worker_grab(): garbage collect, checking for profiling. RETAIN.
*/
static void
_worker_grab(u3_noun sac, u3_noun ovo, u3_noun vir)
{
if ( u3_nul == sac) {
if ( u3C.wag_w & (u3o_debug_ram | u3o_check_corrupt) ) {
u3m_grab(sac, ovo, vir, u3_none);
}
}
else {
c3_w usr_w = 0, man_w = 0, sac_w = 0, ova_w = 0, roe_w = 0, vir_w = 0;
FILE* fil_u;
#ifdef U3_MEMORY_LOG
{
u3_noun wen = u3dc("scot", c3__da, u3k(u3A->now));
c3_c* wen_c = u3r_string(wen);
c3_c nam_c[2048];
snprintf(nam_c, 2048, "%s/.urb/put/mass", u3P.dir_c);
struct stat st;
if ( -1 == stat(nam_c, &st) ) {
mkdir(nam_c, 0700);
}
c3_c man_c[2048];
snprintf(man_c, 2048, "%s/%s.txt", nam_c, wen_c);
fil_u = fopen(man_c, "w");
fprintf(fil_u, "%s\r\n", wen_c);
free(wen_c);
u3z(wen);
}
#else
{
fil_u = stderr;
}
#endif
c3_assert( u3R == &(u3H->rod_u) );
fprintf(fil_u, "\r\n");
usr_w = _worker_prof(fil_u, 0, sac);
u3a_print_memory(fil_u, "total userspace", usr_w);
man_w = u3m_mark(fil_u);
sac_w = u3a_mark_noun(sac);
u3a_print_memory(fil_u, "space profile", sac_w);
ova_w = u3a_mark_noun(ovo);
u3a_print_memory(fil_u, "event", ova_w);
roe_w = u3a_mark_noun(u3V.roe);
u3a_print_memory(fil_u, "lifecycle events", roe_w);
vir_w = u3a_mark_noun(vir);
u3a_print_memory(fil_u, "effects", vir_w);
u3a_print_memory(fil_u, "total marked", usr_w + man_w + sac_w + ova_w + vir_w);
u3a_print_memory(fil_u, "sweep", u3a_sweep());
#ifdef U3_MEMORY_LOG
{
fclose(fil_u);
}
#endif
}
}
/* _worker_fail(): failure stub.
*/
static void
_worker_fail(void* vod_p, const c3_c* wut_c)
{
u3l_log("work: fail: %s\r\n", wut_c);
exit(1);
}
/* _worker_send(): send result back to daemon.
*/
static void
_worker_send(u3_noun job)
{
u3_newt_write(&u3V.out_u, u3ke_jam(job), 0);
}
/* _worker_send_replace(): send replacement job back to daemon.
*/
static void
_worker_send_replace(c3_d evt_d, u3_noun job)
{
u3l_log("worker_send_replace %" PRIu64 " %s\r\n",
evt_d,
u3r_string(u3h(u3t(u3t(job)))));
_worker_send(u3nt(c3__work,
u3i_chubs(1, &evt_d),
u3ke_jam(u3nc(u3V.mug_l, job))));
}
/* _worker_send_complete(): report completion.
*/
static void
_worker_send_complete(u3_noun vir)
{
_worker_send(u3nq(c3__done,
u3i_chubs(1, &u3V.dun_d),
u3V.mug_l,
vir));
}
/* _worker_send_stdr(): send stderr output
*/
static void
_worker_send_stdr(c3_c* str_c)
{
_worker_send(u3nt(c3__stdr, u3i_chubs(1, &u3V.sen_d), u3i_string(str_c)));
}
/* _worker_send_slog(): send hint output (hod is [priority tank]).
*/
static void
_worker_send_slog(u3_noun hod)
{
_worker_send(u3nt(c3__slog, u3i_chubs(1, &u3V.sen_d), hod));
}
/* _worker_lame(): event failed, replace with error event.
*/
static void
_worker_lame(c3_d evt_d, u3_noun now, u3_noun ovo, u3_noun why, u3_noun tan)
{
u3_noun rep;
u3_noun wir, tag, cad;
u3x_trel(ovo, &wir, &tag, &cad);
// a deterministic error (%exit) in a network packet (%hear)
// generates a negative-acknowlegement attempt (%hole).
//
// A comment from the old implementation:
// There should be a separate path for crypto failures,
// to prevent timing attacks, but isn't right now. To deal
// with a crypto failure, just drop the packet.
//
if ( (c3__hear == tag) && (c3__exit == why) ) {
rep = u3nt(u3k(wir), c3__hole, u3k(cad));
}
// failed event notifications (%crud) are replaced with
// an even more generic notifications, on a generic arvo wire.
// N.B this must not be allowed to fail!
//
// [%warn original-event-tag=@tas combined-trace=(list tank)]
//
else if ( c3__crud == tag ) {
u3_noun lef = u3nc(c3__leaf, u3i_tape("crude crashed!"));
u3_noun nat = u3kb_weld(u3k(u3t(cad)), u3nc(lef, u3k(tan)));
rep = u3nc(u3nt(u3_blip, c3__arvo, u3_nul),
u3nt(c3__warn, u3k(u3h(cad)), nat));
}
// failed failure failing fails
//
else if ( c3__warn == tag ) {
_worker_fail(0, "%warn replacement event failed");
c3_assert(0);
}
// failure notifications are sent on the same wire
//
// [%crud event-tag=@tas event-trace=(list tank)]
//
else {
// prepend failure mote to tank
//
u3_noun lef = u3nc(c3__leaf, u3kb_weld(u3i_tape("bail: "),
u3qc_rip(3, why)));
u3_noun nat = u3kb_weld(u3k(tan), u3nc(lef, u3_nul));
rep = u3nc(u3k(wir), u3nt(c3__crud, u3k(tag), nat));
}
_worker_send_replace(evt_d, u3nc(now, rep));
u3z(ovo); u3z(why); u3z(tan);
}
/* _worker_sure(): event succeeded, report completion.
*/
static void
_worker_sure(u3_noun ovo, u3_noun vir, u3_noun cor)
{
u3z(u3A->roc);
u3A->roc = cor;
u3A->ent_d = u3V.dun_d;
u3V.mug_l = u3r_mug(u3A->roc);
u3_noun sac = u3_nul;
// intercept |mass, observe |reset
//
{
u3_noun riv = vir;
c3_w i_w = 0;
while ( u3_nul != riv ) {
u3_noun fec = u3t(u3h(riv));
// assumes a max of one %mass effect per event
//
if ( c3__mass == u3h(fec) ) {
// save a copy of the %mass data
//
sac = u3k(u3t(fec));
// replace the %mass data with ~
//
// For efficient transmission to daemon.
//
riv = u3kb_weld(u3qb_scag(i_w, vir),
u3nc(u3nt(u3k(u3h(u3h(riv))), c3__mass, u3_nul),
u3qb_slag(1 + i_w, vir)));
u3z(vir);
vir = riv;
break;
}
// reclaim memory from persistent caches on |reset
//
if ( c3__vega == u3h(fec) ) {
u3m_reclaim();
}
riv = u3t(riv);
i_w++;
}
}
// XX this runs on replay too
//
_worker_grab(sac, ovo, vir);
_worker_send_complete(vir);
u3z(sac); u3z(ovo);
}
/* _worker_work_live(): apply event.
*/
static void
_worker_work_live(c3_d evt_d, u3_noun job)
{
u3_noun now, ovo, gon;
u3_noun last_date;
c3_assert(evt_d == u3V.dun_d + 1ULL);
u3V.sen_d = evt_d;
u3x_cell(job, &now, &ovo);
last_date = u3A->now;
u3A->now = u3k(now);
#ifdef U3_EVENT_TIME_DEBUG
{
struct timeval b4, f2, d0;
gettimeofday(&b4, 0);
if ( c3__belt != u3h(u3t(ovo)) ) {
c3_c* txt_c = u3r_string(u3h(u3t(ovo)));
u3l_log("work: %s (%" PRIu64 ") live\r\n", txt_c, evt_d);
}
}
#endif
gon = u3m_soft(0, u3v_poke, u3k(ovo));
#ifdef U3_EVENT_TIME_DEBUG
{
c3_c* txt_c = u3r_string(u3h(u3t(ovo)));
c3_w ms_w;
c3_w clr_w;
gettimeofday(&f2, 0);
timersub(&f2, &b4, &d0);
ms_w = (d0.tv_sec * 1000) + (d0.tv_usec / 1000);
clr_w = ms_w > 1000 ? 1 : ms_w < 100 ? 2 : 3; // red, green, yellow
if (c3__belt != u3h(u3t(ovo)) || clr_w != 2) {
u3l_log("\x1b[3%dm%%%s (%" PRIu64 ") %4d.%02dms\x1b[0m\n",
clr_w, txt_c, evt_d, ms_w,
(int) (d0.tv_usec % 1000) / 10);
}
free(txt_c);
}
#endif
if ( u3_blip != u3h(gon) ) {
// event rejected
//
u3V.sen_d = u3V.dun_d;
// restore previous time
//
u3_noun nex = u3A->now;
u3A->now = last_date;
u3_noun why, tan;
u3x_cell(gon, &why, &tan);
u3k(ovo); u3k(why); u3k(tan);
u3z(gon); u3z(job);
_worker_lame(evt_d, nex, ovo, why, tan);
}
else {
// event accepted
//
u3V.dun_d = u3V.sen_d;
u3z(last_date);
// vir/(list ovum) list of effects
// cor/arvo arvo core
//
u3_noun vir, cor;
u3x_trel(gon, 0, &vir, &cor);
u3k(ovo); u3k(vir); u3k(cor);
u3z(gon); u3z(job);
_worker_sure(ovo, vir, cor);
// reclaim memory from persistent caches periodically
//
// XX this is a hack to work around the fact that
// the bytecode caches grow rapidly and are not
// able to be simply capped (due to internal posts).
//
if ( 0 == (evt_d % 1000ULL) ) {
u3m_reclaim();
}
}
}
/* _worker_work_boot(): apply initial-stage event.
*/
static void
_worker_work_boot(c3_d evt_d, u3_noun job)
{
// here we asset on u3V.sen_d, because u3V.dun_d isn't set until
// after u3V.sen_d == u3V.len_w (ie, after the lifecycle evaluation)
//
c3_assert(evt_d == u3V.sen_d + 1ULL);
u3V.sen_d = evt_d;
u3V.roe = u3nc(job, u3V.roe);
u3l_log("work: (%" PRIu64 ")| boot\r\n", evt_d);
if ( u3V.len_w == evt_d ) {
u3_noun eve = u3kb_flop(u3V.roe);
u3V.roe = u3_nul;
u3l_log("work: (%" PRIu64 ")| pill: %x\r\n", evt_d, u3r_mug(eve));
if ( c3n == u3v_boot(eve) ) {
u3l_log("work: boot failed: invalid sequence (from pill)\r\n");
exit(1);
}
u3V.dun_d = evt_d;
u3V.mug_l = u3r_mug(u3A->roc);
u3A->ent_d = u3V.dun_d;
u3l_log("work: (%" PRIu64 ")| core: %x\r\n", evt_d, u3V.mug_l);
}
else {
// prior to the evaluation of the entire lifecycle sequence,
// we simply use the mug of the formula as the kernel mug
//
u3V.mug_l = u3r_mug(job);
}
_worker_send(u3nq(c3__done,
u3i_chubs(1, &evt_d),
u3V.mug_l,
u3_nul));
}
/* _worker_poke_work(): apply event.
*/
static void
_worker_poke_work(c3_d evt_d, // event number
c3_l mug_l, // mug of state
u3_noun job) // full event
{
if ( u3C.wag_w & u3o_trace ) {
if ( u3_Host.tra_u.con_w == 0 && u3_Host.tra_u.fun_w == 0 ) {
u3t_trace_open(u3V.dir_c);
}
else if ( u3_Host.tra_u.con_w >= 100000 ) {
u3t_trace_close();
u3t_trace_open(u3V.dir_c);
}
}
// Require mugs to match
//
// We use mugs to enforce that %work is always performed against
// the exact kernel we expect it to be. If it isn't, we have either
// event-log corruption or non-determism on replay, or programmer error
// in normal operation. In either case, we immediately exit.
//
if ( u3V.mug_l != mug_l ) {
u3l_log("work: invalid %%work for event %" PRIu64 ".\r\n", evt_d);
u3l_log("work: computed mug is %x but event %" PRIu64 " expected %x.\r\n",
u3V.mug_l,
evt_d,
mug_l);
_worker_fail(0, "bad jar");
return;
}
if ( evt_d <= u3V.len_w ) {
c3_c lab_c[8];
snprintf(lab_c, 8, "boot: %" PRIu64 "", evt_d);
u3t_event_trace(lab_c, 'B');
_worker_work_boot(evt_d, job);
u3t_event_trace(lab_c, 'E');
}
else {
u3_noun wir = u3h(u3t(job));
u3_noun cad = u3h(u3t(u3t(job)));
c3_c lab_c[2048];
snprintf(lab_c, 2048, "event %" PRIu64 ": [%s %s]", evt_d,
u3m_pretty_path(wir), u3m_pretty(cad));
u3t_event_trace(lab_c, 'B');
_worker_work_live(evt_d, job);
u3t_event_trace(lab_c, 'E');
}
}
/* _worker_poke_exit(): exit on command.
*/
static void
_worker_poke_exit(c3_w cod_w) // exit code
{
if ( u3C.wag_w & u3o_debug_cpu ) {
u3t_damp();
}
exit(cod_w);
}
/* _worker_poke_boot(): prepare to boot.
*/
static void
_worker_poke_boot(u3_noun who, u3_noun fak, c3_w len_w)
{
c3_assert( u3_none == u3A->our );
c3_assert( 0 != len_w );
u3A->our = who;
u3A->fak = fak;
u3V.len_w = len_w;
}
/* _worker_poke():
*/
void
_worker_poke(void* vod_p, u3_noun mat)
{
u3_noun jar = u3ke_cue(mat);
if ( c3y != u3du(jar) ) {
goto error;
}
else {
switch ( u3h(jar) ) {
default: {
goto error;
}
case c3__boot: {
u3_noun who, fak, len;
c3_w len_w;
if ( (c3n == u3r_qual(jar, 0, &who, &fak, &len)) ||
(c3n == u3ud(who)) ||
(1 < u3r_met(7, who)) ||
(c3n == u3ud(fak)) ||
(1 < u3r_met(0, fak)) ||
(c3n == u3ud(len)) ||
(1 < u3r_met(3, len)) )
{
goto error;
}
len_w = u3r_word(0, len);
u3k(who);
u3k(fak);
u3z(jar);
return _worker_poke_boot(who, fak, len_w);
}
case c3__work: {
u3_noun evt, jammed_entry, mug, job;
c3_d evt_d;
c3_l mug_l;
if ( (c3n == u3r_trel(jar, 0, &evt, &jammed_entry)) ||
(c3n == u3ud(evt)) ||
(1 != u3r_met(6, evt)) )
{
goto error;
}
u3_noun entry = u3qe_cue(jammed_entry);
if ( (c3y != u3du(entry)) ||
(c3n == u3r_cell(entry, &mug, &job)) ||
(c3n == u3ud(mug)) ||
(1 < u3r_met(5, mug)) ) {
goto error;
}
evt_d = u3r_chub(0, evt);
mug_l = u3r_word(0, mug);
u3k(job);
u3z(entry);
u3z(jar);
return _worker_poke_work(evt_d, mug_l, job);
}
case c3__exit: {
u3_noun cod;
c3_w cod_w;
if ( (c3n == u3r_cell(jar, 0, &cod)) ||
(c3n == u3ud(cod)) ||
(1 < u3r_met(3, cod)) )
{
goto error;
}
cod_w = u3r_word(0, cod);
u3z(jar);
return _worker_poke_exit(cod_w);
}
case c3__save: {
u3_noun evt;
c3_d evt_d;
if ( (c3n == u3r_cell(jar, 0, &evt)) ||
(c3n == u3ud(evt)) )
{
goto error;
}
evt_d = u3r_chub(0, evt);
u3z(jar);
c3_assert( evt_d == u3V.dun_d );
return u3e_save();
}
}
}
error: {
u3z(jar);
_worker_fail(0, "bad jar");
}
}
/* u3_worker_boot(): send startup message to manager.
*/
void
u3_worker_boot(void)
{
c3_d nex_d = 1ULL;
u3_noun dat = u3_nul;
if ( u3_none != u3A->our ) {
u3V.mug_l = u3r_mug(u3A->roc);
nex_d = u3V.dun_d + 1ULL;
dat = u3nc(u3_nul, u3nt(u3i_chubs(1, &nex_d),
u3V.mug_l,
u3nc(u3k(u3A->our), u3k(u3A->fak))));
// disable hashboard for fake ships
//
if ( c3y == u3A->fak ) {
u3C.wag_w |= u3o_hashless;
}
// no boot sequence expected
//
u3V.len_w = 0;
}
u3l_log("work: play %" PRIu64 "\r\n", nex_d);
_worker_send(u3nc(c3__play, dat));
}
/* main(): main() when run as urbit-worker
*/
c3_i
main(c3_i argc, c3_c* argv[])
{
uv_loop_t* lup_u = uv_default_loop();
c3_c* dir_c = argv[1];
c3_c* key_c = argv[2];
c3_c* wag_c = argv[3];
c3_assert(4 == argc);
memset(&u3V, 0, sizeof(u3V));
memset(&u3_Host.tra_u, 0, sizeof(u3_Host.tra_u));
/* load passkey
*/
{
sscanf(key_c, "%" PRIx64 ":%" PRIx64 ":%" PRIx64 ":%" PRIx64 "",
&u3V.key_d[0],
&u3V.key_d[1],
&u3V.key_d[2],
&u3V.key_d[3]);
}
/* load runtime config
*/
{
sscanf(wag_c, "%" SCNu32, &u3C.wag_w);
}
/* load pier directory
*/
{
u3V.dir_c = strdup(dir_c);
}
/* boot image
*/
{
u3V.sen_d = u3V.dun_d = u3m_boot(dir_c);
u3C.stderr_log_f = _worker_send_stdr;
u3C.slog_f = _worker_send_slog;
}
/* configure pipe to daemon process
*/
{
c3_i err_i;
err_i = uv_pipe_init(lup_u, &u3V.inn_u.pyp_u, 0);
c3_assert(!err_i);
uv_pipe_open(&u3V.inn_u.pyp_u, 0);
err_i = uv_pipe_init(lup_u, &u3V.out_u.pyp_u, 0);
c3_assert(!err_i);
uv_pipe_open(&u3V.out_u.pyp_u, 1);
}
/* set up writing
*/
u3V.out_u.bal_f = _worker_fail;
/* start reading
*/
u3V.inn_u.vod_p = &u3V;
u3V.inn_u.pok_f = _worker_poke;
u3V.inn_u.bal_f = _worker_fail;
u3_newt_read(&u3V.inn_u);
/* send start request
*/
u3_worker_boot();
/* enter loop
*/
uv_run(lup_u, UV_RUN_DEFAULT);
return 0;
}

View File

@ -20,8 +20,10 @@ dependencies:
- classy-prelude
- stm
- stm-chans
- async
- lens
- largeword
- time
executables:
vere:
main: Main.hs
library:
source-dirs: .