mirror of
https://github.com/urbit/shrub.git
synced 2024-12-23 19:05:48 +03:00
406 lines
9.2 KiB
C
406 lines
9.2 KiB
C
/* vere/serf.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 <curses.h>
|
|
#include <termios.h>
|
|
#include <term.h>
|
|
|
|
#include "all.h"
|
|
#include <vere/vere.h>
|
|
|
|
typedef struct _u3_serf {
|
|
c3_d evt_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
|
|
} u3_serf;
|
|
static u3_serf u3V;
|
|
|
|
/* serf-lord protocol:
|
|
**
|
|
** ++ plea :: from serf to lord
|
|
** $% $: $play :: send events
|
|
** p/@ :: first number expected
|
|
** q/@ :: mug of state (or 0 to boot)
|
|
** == ::
|
|
** $: $done :: event executed unchanged
|
|
** p/@ :: number of this event
|
|
** q/@ :: mug of state (or 0)
|
|
** r/(list ovum) :: actions
|
|
** == ::
|
|
** $: $work :: replace and retry
|
|
** p/@ :: event number
|
|
** q/@ :: mug of state (or 0)
|
|
** r/(pair date ovum) :: event
|
|
** == == ::
|
|
**
|
|
** ++ writ :: from lord to serf
|
|
** $% $: $exit :: snapshot, then exit
|
|
** p/@ :: exit code
|
|
** == ::
|
|
** $: $save :: save snapshot to disk
|
|
** p/@ :: number of old snaps to save
|
|
** == ::
|
|
** $: $work :: execute event
|
|
** p/@ :: event number
|
|
** q/@ :: mug of state (or 0)
|
|
** r/(pair date ovum) :: event
|
|
** == == ::
|
|
*/
|
|
|
|
/* _serf_fail(): failure stub.
|
|
*/
|
|
static void
|
|
_serf_fail(void* vod_p, const c3_c* wut_c)
|
|
{
|
|
// fprintf(stderr, "serf: fail: %s\r\n", wut_c);
|
|
exit(1);
|
|
}
|
|
|
|
/* _serf_send(): send result back to lord.
|
|
*/
|
|
static void
|
|
_serf_send(u3_noun job)
|
|
{
|
|
u3_newt_write(&u3V.out_u, u3ke_jam(job), 0);
|
|
}
|
|
|
|
/* _serf_send_replace(): send replacement job back to lord.
|
|
*/
|
|
static void
|
|
_serf_send_replace(u3_noun ovo)
|
|
{
|
|
_serf_send(u3nq(c3__work,
|
|
u3i_chubs(1, &u3V.evt_d),
|
|
u3V.mug_l,
|
|
u3nc(u3k(u3A->now), ovo)));
|
|
}
|
|
|
|
/* _serf_send_complete(): report completion.
|
|
*/
|
|
static void
|
|
_serf_send_complete(u3_noun vir)
|
|
{
|
|
_serf_send(u3nq(c3__done,
|
|
u3i_chubs(1, &u3V.evt_d),
|
|
u3r_mug(u3A->roc),
|
|
vir));
|
|
}
|
|
|
|
/* _serf_lame(): event failed, replace with error event.
|
|
*/
|
|
static void
|
|
_serf_lame(u3_noun ovo, u3_noun why, u3_noun tan)
|
|
{
|
|
/* XX: the next crud will contain the original event.
|
|
*/
|
|
u3z(ovo);
|
|
_serf_send_replace(u3nt(c3__crud, why, tan));
|
|
}
|
|
|
|
/* _serf_sure(): event succeeded, report completion.
|
|
*/
|
|
static void
|
|
_serf_sure(u3_noun ovo, u3_noun vir, u3_noun cor)
|
|
{
|
|
u3z(ovo);
|
|
|
|
u3z(u3A->roc);
|
|
u3A->roc = cor;
|
|
|
|
_serf_send_complete(vir);
|
|
}
|
|
|
|
/* _serf_poke_live(): apply event.
|
|
*/
|
|
static void
|
|
_serf_poke_live(c3_d evt_d, // event number
|
|
c3_l mug_l, // mug of state
|
|
u3_noun job) // event date
|
|
{
|
|
u3_noun now = u3k(u3h(job));
|
|
u3_noun ovo = u3k(u3t(job));
|
|
|
|
// fprintf(stderr, "serf: (%lld)| live\r\n", evt_d);
|
|
|
|
c3_assert(evt_d == u3V.evt_d + 1ULL);
|
|
u3V.evt_d = evt_d;
|
|
|
|
u3z(job);
|
|
{
|
|
u3_noun gon;
|
|
|
|
if ( mug_l ) {
|
|
c3_assert(u3r_mug(u3A->roc) == mug_l);
|
|
}
|
|
|
|
u3z(u3A->now);
|
|
u3A->now = now;
|
|
|
|
u3A->ent_d = evt_d;
|
|
|
|
#ifdef GHETTO
|
|
struct timeval b4, f2, d0;
|
|
gettimeofday(&b4, 0);
|
|
#endif
|
|
|
|
gon = u3m_soft(0, u3v_poke, u3k(ovo));
|
|
|
|
#ifdef GHETTO
|
|
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) {
|
|
uL(fprintf(uH, "\x1b[3%dm%%%s %4d.%02dms\x1b[0m\n",
|
|
clr_w, txt_c, ms_w, (int) (d0.tv_usec % 1000) / 10));
|
|
}
|
|
free(txt_c);
|
|
#endif
|
|
|
|
if ( u3_blip != u3h(gon) ) {
|
|
u3_noun why = u3k(u3h(gon));
|
|
u3_noun tan = u3k(u3t(gon));
|
|
|
|
u3z(gon);
|
|
_serf_lame(ovo, why, tan);
|
|
}
|
|
else {
|
|
u3_noun vir = u3k(u3h(u3t(gon)));
|
|
u3_noun cor = u3k(u3t(u3t(gon)));
|
|
|
|
_serf_sure(ovo, vir, cor);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* _serf_boot_fire(): execute boot sequence.
|
|
*/
|
|
static u3_noun
|
|
_serf_boot_fire(u3_noun eve)
|
|
{
|
|
u3_noun cor = u3n_nock_on(eve, u3nt(2, u3nc(0, 3), u3nc(0, 2)));
|
|
u3_noun pro;
|
|
|
|
pro = u3k(u3r_at(7, cor));
|
|
|
|
u3z(cor);
|
|
return pro;
|
|
}
|
|
|
|
/* _serf_poke_boot(): apply initial-stage event.
|
|
*/
|
|
static void
|
|
_serf_poke_boot(c3_d evt_d,
|
|
c3_l mug_l,
|
|
u3_noun job)
|
|
{
|
|
u3A->roe = u3nc(job, u3A->roe);
|
|
|
|
c3_assert(evt_d == u3V.evt_d + 1ULL);
|
|
u3V.evt_d = evt_d;
|
|
fprintf(stderr, "serf: (%lld)| boot\r\n", evt_d);
|
|
|
|
if ( evt_d == 5 ) {
|
|
u3_noun eve = u3kb_flop(u3A->roe);
|
|
u3_noun pru;
|
|
|
|
u3A->roe = 0;
|
|
fprintf(stderr, "serf: (5)| pill: %x\r\n", u3r_mug(eve));
|
|
|
|
pru = u3m_soft(0, _serf_boot_fire, eve);
|
|
if ( u3h(pru) != 0 ) {
|
|
fprintf(stderr, "boot failed\r\n");
|
|
exit(1);
|
|
}
|
|
|
|
fprintf(stderr, "serf: (5)| core: %x\r\n", u3r_mug(u3t(pru)));
|
|
|
|
u3A->ken = 0;
|
|
u3A->roc = u3k(u3t(pru));
|
|
|
|
u3z(pru);
|
|
}
|
|
_serf_send(u3nq(c3__done,
|
|
u3i_chubs(1, &evt_d),
|
|
0,
|
|
u3_nul));
|
|
}
|
|
|
|
/* _serf_poke_work(): apply event.
|
|
*/
|
|
static void
|
|
_serf_poke_work(c3_d evt_d, // event number
|
|
c3_l mug_l, // mug of state
|
|
u3_noun job) // full event
|
|
{
|
|
if ( evt_d < 6 ) {
|
|
_serf_poke_boot(evt_d, mug_l, job);
|
|
}
|
|
else {
|
|
_serf_poke_live(evt_d, mug_l, job);
|
|
}
|
|
}
|
|
|
|
/* _serf_poke_exit(): exit on command.
|
|
*/
|
|
static void
|
|
_serf_poke_exit(c3_w cod_w) // exit code
|
|
{
|
|
exit(cod_w);
|
|
}
|
|
|
|
/* _serf_poke():
|
|
*/
|
|
void
|
|
_serf_poke(void* vod_p, u3_noun mat)
|
|
{
|
|
u3_noun jar = u3ke_cue(mat);
|
|
|
|
if ( c3y != u3du(jar) ) {
|
|
goto error;
|
|
}
|
|
else {
|
|
u3_noun p_jar, q_jar, r_jar;
|
|
|
|
switch ( u3h(jar) ) {
|
|
case c3__work: {
|
|
if ( (c3n == u3r_qual(jar, 0, &p_jar, &q_jar, &r_jar)) ||
|
|
(c3n == u3ud(p_jar)) ||
|
|
(u3r_met(6, p_jar) != 1) ||
|
|
(c3n == u3ud(q_jar)) ||
|
|
(u3r_met(5, q_jar) > 1) )
|
|
{
|
|
goto error;
|
|
}
|
|
_serf_poke_work(u3r_chub(0, p_jar),
|
|
u3r_word(0, q_jar),
|
|
u3k(r_jar));
|
|
break;
|
|
}
|
|
case c3__exit: {
|
|
if ( (c3n == u3r_cell(jar, 0, &p_jar)) ||
|
|
(c3n == u3ud(p_jar)) ||
|
|
(u3r_met(3, p_jar) > 1) )
|
|
{
|
|
goto error;
|
|
}
|
|
_serf_poke_exit(u3k(p_jar));
|
|
break;
|
|
}
|
|
case c3__save: {
|
|
if ( (c3n == u3r_cell(jar, 0, &p_jar)) ||
|
|
(c3n == u3ud(p_jar)) ) {
|
|
goto error;
|
|
}
|
|
fprintf(stderr, "serf: save\r\n");
|
|
u3e_save();
|
|
break;
|
|
}
|
|
default: {
|
|
goto error;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
error: {
|
|
_serf_fail(0, "bad jar");
|
|
}
|
|
u3z(jar);
|
|
}
|
|
|
|
/* u3_serf_boot(): send startup message to manager.
|
|
*/
|
|
void
|
|
u3_serf_boot(void)
|
|
{
|
|
c3_d nex_d = u3A->ent_d + 1ULL;
|
|
|
|
fprintf(stderr, "serf: play %lld\r\n", nex_d);
|
|
|
|
_serf_send(u3nt(c3__play, u3i_chubs(1, &nex_d), 0));
|
|
}
|
|
|
|
/* u3_serf_main(): main() when run as urbit-client
|
|
*/
|
|
c3_i
|
|
u3_serf_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_assert(3 == argc);
|
|
|
|
/* load passkey
|
|
*/
|
|
{
|
|
sscanf(key_c, "%llx:%llx:%llx:%llx", &u3V.key_d[0],
|
|
&u3V.key_d[1],
|
|
&u3V.key_d[2],
|
|
&u3V.key_d[3]);
|
|
}
|
|
|
|
/* boot image
|
|
*/
|
|
{
|
|
u3V.evt_d = u3m_boot_new(dir_c);
|
|
}
|
|
|
|
/* configure pipe to lord 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 = _serf_fail;
|
|
|
|
/* start reading
|
|
*/
|
|
u3V.inn_u.vod_p = &u3V;
|
|
u3V.inn_u.pok_f = _serf_poke;
|
|
u3V.inn_u.bal_f = _serf_fail;
|
|
|
|
u3_newt_read(&u3V.inn_u);
|
|
|
|
/* send start request
|
|
*/
|
|
u3_serf_boot();
|
|
|
|
/* enter loop
|
|
*/
|
|
uv_run(lup_u, UV_RUN_DEFAULT);
|
|
return 0;
|
|
}
|