mirror of
https://github.com/urbit/shrub.git
synced 2024-12-23 02:41:35 +03:00
f0524c23f0
* release-candidate: (138 commits) adds meson option (-Dprof=true) to set U3_CPU_DEBUG adds meson option (-Dgc=true) to set U3_MEMORY_DEBUG refactors http card validation fixes leak in proxy when networking is disabled fixes leak in _http_serv_start_all bypassed profiling bug Eliminate cons optimization. Remove broken assert. Clean up some printfs. increases tcp proxy listener timeout to 2 minutes fixes dumb bug in tcp reverse proxy remote address resolution Fix issue with trimming >4 character jet names upgrades libh2o to v0.13.5 (h2o v2.2.5) sets the http client request timeout to 2 minutes adds an http request timer to ensure connections are always closed adds an http request timer to ensure connections are always closed adds (disabled) h2o access log don't leak ship-name in _proxy_ward_connect() refactors .http.ports write/release (avoid the loom, etc.) updates SSL_CTX init to support any PEM private key, simplifies error handling ...
1721 lines
35 KiB
C
1721 lines
35 KiB
C
/* n/m.c
|
|
**
|
|
*/
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <ctype.h>
|
|
#include <sigsegv.h>
|
|
#include <curl/curl.h>
|
|
|
|
#include "all.h"
|
|
|
|
#undef NO_OVERFLOW
|
|
|
|
/* (u3_noun)setjmp(u3R->esc.buf): setjmp within road.
|
|
*/
|
|
#if 0
|
|
c3_o
|
|
u3m_trap(void);
|
|
#else
|
|
# define u3m_trap() (u3_noun)(_setjmp(u3R->esc.buf))
|
|
#endif
|
|
|
|
/* u3m_signal(): treat a nock-level exception as a signal interrupt.
|
|
*/
|
|
void
|
|
u3m_signal(u3_noun sig_l);
|
|
|
|
/* u3m_dump(): dump the current road to stderr.
|
|
*/
|
|
void
|
|
u3m_dump(void);
|
|
|
|
/* u3m_fall(): return to parent road.
|
|
*/
|
|
void
|
|
u3m_fall(void);
|
|
|
|
/* u3m_leap(): in u3R, create a new road within the existing one.
|
|
*/
|
|
void
|
|
u3m_leap(c3_w pad_w);
|
|
|
|
/* u3m_golf(): record cap length for u3_flog().
|
|
*/
|
|
c3_w
|
|
u3m_golf(void);
|
|
|
|
/* u3m_flog(): pop the cap.
|
|
**
|
|
** A common sequence for inner allocation is:
|
|
**
|
|
** c3_w gof_w = u3m_golf();
|
|
** u3m_leap();
|
|
** // allocate some inner stuff...
|
|
** u3m_fall();
|
|
** // inner stuff is still valid, but on cap
|
|
** u3m_flog(gof_w);
|
|
**
|
|
** u3m_flog(0) simply clears the cap.
|
|
*/
|
|
void
|
|
u3m_flog(c3_w gof_w);
|
|
|
|
/* u3m_soft_top(): top-level safety wrapper.
|
|
*/
|
|
u3_noun
|
|
u3m_soft_top(c3_w sec_w, // timer seconds
|
|
c3_w pad_w, // base memory pad
|
|
u3_funk fun_f,
|
|
u3_noun arg);
|
|
|
|
|
|
static sigjmp_buf u3_Signal;
|
|
|
|
#ifndef SIGSTKSZ
|
|
# define SIGSTKSZ 16384
|
|
#endif
|
|
#ifndef NO_OVERFLOW
|
|
static uint8_t Sigstk[SIGSTKSZ];
|
|
#endif
|
|
|
|
void u3_unix_ef_hold(void); // suspend system signal regime
|
|
void u3_unix_ef_move(void); // restore system signal regime
|
|
|
|
extern void
|
|
u3_lo_sway(c3_l tab_l, u3_noun tax);
|
|
|
|
#if 0
|
|
/* _cm_punt(): crudely print trace.
|
|
*/
|
|
static void
|
|
_cm_punt(u3_noun tax)
|
|
{
|
|
u3_noun xat;
|
|
|
|
for ( xat = tax; xat; xat = u3t(xat) ) {
|
|
u3m_p("&", u3h(xat));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* _cm_emergency(): write emergency text to stderr, never failing.
|
|
*/
|
|
static void
|
|
_cm_emergency(c3_c* cap_c, c3_l sig_l)
|
|
{
|
|
write(2, "\r\n", 2);
|
|
write(2, cap_c, strlen(cap_c));
|
|
|
|
if ( sig_l ) {
|
|
write(2, ": ", 2);
|
|
write(2, &sig_l, 4);
|
|
}
|
|
write(2, "\r\n", 2);
|
|
}
|
|
|
|
static void _cm_overflow(void *arg1, void *arg2, void *arg3)
|
|
{
|
|
(void)(arg1);
|
|
(void)(arg2);
|
|
(void)(arg3);
|
|
siglongjmp(u3_Signal, c3__over);
|
|
}
|
|
|
|
/* _cm_signal_handle(): handle a signal in general.
|
|
*/
|
|
static void
|
|
_cm_signal_handle(c3_l sig_l)
|
|
{
|
|
if ( c3__over == sig_l ) {
|
|
sigsegv_leave_handler(_cm_overflow, NULL, NULL, NULL);
|
|
}
|
|
else {
|
|
siglongjmp(u3_Signal, sig_l);
|
|
}
|
|
}
|
|
|
|
#ifndef NO_OVERFLOW
|
|
static void
|
|
_cm_signal_handle_over(int emergency, stackoverflow_context_t scp)
|
|
{
|
|
_cm_signal_handle(c3__over);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
_cm_signal_handle_term(int x)
|
|
{
|
|
// Ignore if we are using base memory from work memory, very rare.
|
|
//
|
|
if ( (0 != u3H->rod_u.kid_p) && (&(u3H->rod_u) == u3R) ) {
|
|
_cm_emergency("ignored", c3__term);
|
|
}
|
|
else {
|
|
_cm_signal_handle(c3__term);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cm_signal_handle_intr(int x)
|
|
{
|
|
// Interrupt: stop work. Ignore if not working, or (rarely) using base.
|
|
//
|
|
if ( &(u3H->rod_u) == u3R ) {
|
|
_cm_emergency("ignored", c3__intr);
|
|
}
|
|
else {
|
|
_cm_signal_handle(c3__intr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cm_signal_handle_alrm(int x)
|
|
{
|
|
_cm_signal_handle(c3__alrm);
|
|
}
|
|
|
|
/* _cm_signal_reset(): reset top road after signal longjmp.
|
|
*/
|
|
static void
|
|
_cm_signal_reset(void)
|
|
{
|
|
u3R = &u3H->rod_u;
|
|
u3R->cap_p = u3R->mat_p;
|
|
u3R->ear_p = 0;
|
|
u3R->kid_p = 0;
|
|
}
|
|
|
|
#if 0
|
|
/* _cm_stack_recover(): recover stack trace, with lacunae.
|
|
*/
|
|
static u3_noun
|
|
_cm_stack_recover(u3a_road* rod_u)
|
|
{
|
|
c3_w len_w;
|
|
|
|
len_w = 0;
|
|
{
|
|
u3_noun tax = rod_u->bug.tax;
|
|
|
|
while ( tax ) {
|
|
len_w++;
|
|
tax = u3t(tax);
|
|
}
|
|
|
|
if ( len_w < 4096 ) {
|
|
return u3a_take(rod_u->bug.tax);
|
|
}
|
|
else {
|
|
u3_noun beg, fin;
|
|
c3_w i_w;
|
|
|
|
tax = rod_u->bug.tax;
|
|
beg = u3_nul;
|
|
for ( i_w = 0; i_w < 2048; i_w++ ) {
|
|
beg = u3nc(u3a_take(u3h(tax)), beg);
|
|
tax = u3t(tax);
|
|
}
|
|
beg = u3kb_flop(beg);
|
|
|
|
for ( i_w = 0; i_w < (len_w - 4096); i_w++ ) {
|
|
tax = u3t(tax);
|
|
}
|
|
fin = u3nc(u3nc(c3__lose, c3__over), u3a_take(tax));
|
|
|
|
return u3kb_weld(beg, fin);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* _cm_stack_unwind(): unwind to the top level, preserving all frames.
|
|
*/
|
|
static u3_noun
|
|
_cm_stack_unwind(void)
|
|
{
|
|
u3_noun tax;
|
|
|
|
while ( u3R != &(u3H->rod_u) ) {
|
|
u3_noun yat = u3m_love(u3R->bug.tax);
|
|
|
|
u3R->bug.tax = u3kb_weld(yat, u3R->bug.tax);
|
|
}
|
|
tax = u3R->bug.tax;
|
|
|
|
u3R->bug.tax = 0;
|
|
return tax;
|
|
}
|
|
|
|
/* _cm_signal_recover(): recover from a deep signal, after longjmp. Free arg.
|
|
*/
|
|
static u3_noun
|
|
_cm_signal_recover(c3_l sig_l, u3_noun arg)
|
|
{
|
|
u3_noun tax;
|
|
|
|
// Unlikely to be set, but it can be made to happen.
|
|
//
|
|
tax = u3H->rod_u.bug.tax;
|
|
u3H->rod_u.bug.tax = 0;
|
|
|
|
if ( &(u3H->rod_u) == u3R ) {
|
|
// A top-level crash - rather odd. We should GC.
|
|
//
|
|
_cm_emergency("recover: top", sig_l);
|
|
u3C.wag_w |= u3o_check_corrupt;
|
|
|
|
// Reset the top road - the problem could be a fat cap.
|
|
//
|
|
_cm_signal_reset();
|
|
|
|
if ( (c3__meme == sig_l) && (u3a_open(u3R) <= 256) ) {
|
|
// Out of memory at the top level. Error becomes c3__full,
|
|
// and we release the emergency buffer. To continue work,
|
|
// we need to readjust the image, eg, migrate to 64 bit.
|
|
//
|
|
u3z(u3R->bug.mer);
|
|
u3R->bug.mer = 0;
|
|
sig_l = c3__full;
|
|
}
|
|
return u3nt(3, sig_l, tax);
|
|
}
|
|
else {
|
|
u3_noun pro;
|
|
|
|
// A signal was generated while we were within Nock.
|
|
//
|
|
_cm_emergency("recover: dig", sig_l);
|
|
|
|
#if 0
|
|
// Descend to the innermost trace, collecting stack.
|
|
//
|
|
{
|
|
u3a_road* rod_u;
|
|
|
|
u3R = &(u3H->rod_u);
|
|
rod_u = u3R;
|
|
|
|
while ( rod_u->kid_p ) {
|
|
#if 0
|
|
fprintf(stderr, "collecting %d frames\r\n",
|
|
u3kb_lent((u3to(u3_road, rod_u->kid_p)->bug.tax));
|
|
#endif
|
|
tax = u3kb_weld(_cm_stack_recover(u3to(u3_road, rod_u->kid_p)), tax);
|
|
rod_u = u3to(u3_road, rod_u->kid_p);
|
|
}
|
|
}
|
|
#else
|
|
tax = _cm_stack_unwind();
|
|
#endif
|
|
pro = u3nt(3, sig_l, tax);
|
|
_cm_signal_reset();
|
|
|
|
u3z(arg);
|
|
return pro;
|
|
}
|
|
}
|
|
|
|
/* _cm_signal_deep(): start deep processing; set timer for sec_w or 0.
|
|
*/
|
|
static void
|
|
_cm_signal_deep(c3_w sec_w)
|
|
{
|
|
u3_unix_ef_hold();
|
|
|
|
#ifndef NO_OVERFLOW
|
|
stackoverflow_install_handler(_cm_signal_handle_over, Sigstk, SIGSTKSZ);
|
|
#endif
|
|
signal(SIGINT, _cm_signal_handle_intr);
|
|
signal(SIGTERM, _cm_signal_handle_term);
|
|
|
|
// Provide a little emergency memory, for use in case things
|
|
// go utterly haywire.
|
|
//
|
|
if ( 0 == u3H->rod_u.bug.mer ) {
|
|
u3H->rod_u.bug.mer = u3i_string("emergency buffer");
|
|
}
|
|
|
|
if ( sec_w ) {
|
|
struct itimerval itm_u;
|
|
|
|
timerclear(&itm_u.it_interval);
|
|
itm_u.it_value.tv_sec = sec_w;
|
|
itm_u.it_value.tv_usec = 0;
|
|
|
|
setitimer(ITIMER_VIRTUAL, &itm_u, 0);
|
|
signal(SIGVTALRM, _cm_signal_handle_alrm);
|
|
}
|
|
|
|
u3t_boot();
|
|
}
|
|
|
|
/* _cm_signal_done():
|
|
*/
|
|
static void
|
|
_cm_signal_done()
|
|
{
|
|
// signal(SIGINT, SIG_IGN);
|
|
signal(SIGTERM, SIG_IGN);
|
|
signal(SIGVTALRM, SIG_IGN);
|
|
|
|
stackoverflow_deinstall_handler();
|
|
{
|
|
struct itimerval itm_u;
|
|
|
|
timerclear(&itm_u.it_interval);
|
|
timerclear(&itm_u.it_value);
|
|
|
|
setitimer(ITIMER_VIRTUAL, &itm_u, 0);
|
|
}
|
|
u3_unix_ef_move();
|
|
u3t_boff();
|
|
}
|
|
|
|
/* u3m_signal(): treat a nock-level exception as a signal interrupt.
|
|
*/
|
|
void
|
|
u3m_signal(u3_noun sig_l)
|
|
{
|
|
siglongjmp(u3_Signal, sig_l);
|
|
}
|
|
|
|
/* u3m_file(): load file, as atom, or bail.
|
|
*/
|
|
u3_noun
|
|
u3m_file(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) ) {
|
|
fprintf(stderr, "%s: %s\r\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;
|
|
}
|
|
}
|
|
|
|
/* _find_north(): in restored image, point to a north home.
|
|
*/
|
|
static u3_road*
|
|
_find_north(c3_w* mem_w, c3_w siz_w, c3_w len_w)
|
|
{
|
|
return (void *) ((mem_w + len_w) - siz_w);
|
|
}
|
|
|
|
#if 0
|
|
/* _find_south(): in restored image, point to a south home.
|
|
*/
|
|
static u3_road*
|
|
_find_south(c3_w* mem_w, c3_w siz_w, c3_w len_w)
|
|
{
|
|
return (void *)mem_w;
|
|
}
|
|
#endif
|
|
|
|
static u3_road*
|
|
_pave_north(c3_w* mem_w, c3_w siz_w, c3_w len_w)
|
|
{
|
|
c3_w* rut_w = mem_w;
|
|
c3_w* hat_w = rut_w;
|
|
c3_w* mat_w = ((mem_w + len_w) - siz_w);
|
|
c3_w* cap_w = mat_w;
|
|
u3_road* rod_u = (void*) mat_w;
|
|
|
|
// memset(mem_w, 0, 4 * len_w); // enable in case of corruption
|
|
memset(rod_u, 0, 4 * siz_w);
|
|
|
|
rod_u->rut_p = u3of(c3_w, rut_w);
|
|
rod_u->hat_p = u3of(c3_w, hat_w);
|
|
|
|
rod_u->mat_p = u3of(c3_w, mat_w);
|
|
rod_u->cap_p = u3of(c3_w, cap_w);
|
|
|
|
return rod_u;
|
|
}
|
|
|
|
/* _pave_south(): install a south road.
|
|
*/
|
|
static u3_road*
|
|
_pave_south(c3_w* mem_w, c3_w siz_w, c3_w len_w)
|
|
{
|
|
c3_w* rut_w = (mem_w + len_w);
|
|
c3_w* hat_w = rut_w;
|
|
c3_w* mat_w = mem_w;
|
|
c3_w* cap_w = mat_w + siz_w;
|
|
u3_road* rod_u = (void*) mat_w;
|
|
|
|
// memset(mem_w, 0, 4 * len_w); // enable in case of corruption
|
|
memset(rod_u, 0, 4 * siz_w);
|
|
|
|
rod_u->rut_p = u3of(c3_w, rut_w);
|
|
rod_u->hat_p = u3of(c3_w, hat_w);
|
|
|
|
rod_u->mat_p = u3of(c3_w, mat_w);
|
|
rod_u->cap_p = u3of(c3_w, cap_w);
|
|
|
|
return rod_u;
|
|
}
|
|
|
|
/* _pave_parts(): build internal tables.
|
|
*/
|
|
static void
|
|
_pave_parts(void)
|
|
{
|
|
u3R->cax.har_p = u3h_new();
|
|
u3R->jed.war_p = u3h_new();
|
|
u3R->jed.cod_p = u3h_new();
|
|
u3R->jed.han_p = u3h_new();
|
|
u3R->jed.bas_p = u3h_new();
|
|
u3R->byc.har_p = u3h_new();
|
|
}
|
|
|
|
/* u3m_mark(): mark all nouns in the road.
|
|
*/
|
|
c3_w
|
|
u3m_mark(void)
|
|
{
|
|
c3_w tot_w = 0;
|
|
tot_w += u3j_mark();
|
|
tot_w += u3n_mark();
|
|
tot_w += u3a_mark_noun(u3R->ski.gul);
|
|
tot_w += u3a_mark_noun(u3R->bug.tax);
|
|
tot_w += u3a_mark_noun(u3R->bug.mer);
|
|
tot_w += u3a_mark_noun(u3R->pro.don);
|
|
tot_w += u3a_mark_noun(u3R->pro.day);
|
|
tot_w += u3h_mark(u3R->cax.har_p);
|
|
return tot_w;
|
|
}
|
|
|
|
/* u3m_pave(): instantiate or activate image.
|
|
*/
|
|
void
|
|
u3m_pave(c3_o nuu_o, c3_o bug_o)
|
|
{
|
|
if ( c3y == nuu_o ) {
|
|
u3H = (void *)_pave_north(u3_Loom + 1,
|
|
c3_wiseof(u3v_home),
|
|
u3a_words - 1);
|
|
u3R = &u3H->rod_u;
|
|
|
|
_pave_parts();
|
|
}
|
|
else {
|
|
u3H = (void *)_find_north(u3_Loom + 1,
|
|
c3_wiseof(u3v_home),
|
|
u3a_words - 1);
|
|
u3R = &u3H->rod_u;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* u3m_clear(): clear all allocated data in road.
|
|
*/
|
|
void
|
|
u3m_clear(void)
|
|
{
|
|
u3h_free(u3R->cax.har_p);
|
|
u3j_free();
|
|
u3n_free();
|
|
}
|
|
|
|
void
|
|
u3m_dump(void)
|
|
{
|
|
c3_w hat_w;
|
|
c3_w fre_w = 0;
|
|
c3_w i_w;
|
|
|
|
hat_w = _(u3a_is_north(u3R)) ? u3R->hat_w - u3R->rut_w
|
|
: u3R->rut_w - u3R->hat_w;
|
|
|
|
for ( i_w = 0; i_w < u3_cc_fbox_no; i_w++ ) {
|
|
u3a_fbox* fre_u = u3R->all.fre_u[i_w];
|
|
|
|
while ( fre_u ) {
|
|
fre_w += fre_u->box_u.siz_w;
|
|
fre_u = fre_u->nex_u;
|
|
}
|
|
}
|
|
fprintf(stderr, "dump: hat_w %x, fre_w %x, allocated %x\n",
|
|
hat_w, fre_w, (hat_w - fre_w));
|
|
|
|
if ( 0 != (hat_w - fre_w) ) {
|
|
c3_w* box_w = _(u3a_is_north(u3R)) ? u3R->rut_w : u3R->hat_w;
|
|
c3_w mem_w = 0;
|
|
|
|
while ( box_w < (_(u3a_is_north(u3R)) ? u3R->hat_w : u3R->rut_w) ) {
|
|
u3a_box* box_u = (void *)box_w;
|
|
|
|
if ( 0 != box_u->use_w ) {
|
|
#ifdef U3_MEMORY_DEBUG
|
|
// printf("live %d words, code %x\n", box_u->siz_w, box_u->cod_w);
|
|
#endif
|
|
mem_w += box_u->siz_w;
|
|
}
|
|
box_w += box_u->siz_w;
|
|
}
|
|
|
|
fprintf(stderr, "second count: %x\n", mem_w);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
c3_w Exit;
|
|
|
|
/* u3m_bail(): bail out. Does not return.
|
|
**
|
|
** Bail motes:
|
|
**
|
|
** %evil :: erroneous cryptography
|
|
** %exit :: semantic failure
|
|
** %oops :: assertion failure
|
|
** %intr :: interrupt
|
|
** %fail :: computability failure
|
|
** %over :: stack overflow (a kind of %fail)
|
|
** %need :: namespace block
|
|
** %meme :: out of memory
|
|
**
|
|
** These are equivalents of the full exception noun, the error ball:
|
|
**
|
|
** $% [%0 success]
|
|
** [%1 paths]
|
|
** [%2 trace]
|
|
** [%3 code trace]
|
|
** ==
|
|
*/
|
|
c3_i
|
|
u3m_bail(u3_noun how)
|
|
{
|
|
if ( (c3__exit == how) && (u3R == &u3H->rod_u) ) {
|
|
abort();
|
|
}
|
|
|
|
/* Printf some metadata.
|
|
*/
|
|
if ( c3__exit != how && (_(u3ud(how)) || 1 != u3h(how)) ) {
|
|
if ( _(u3ud(how)) ) {
|
|
c3_c str_c[5];
|
|
|
|
str_c[0] = ((how >> 0) & 0xff);
|
|
str_c[1] = ((how >> 8) & 0xff);
|
|
str_c[2] = ((how >> 16) & 0xff);
|
|
str_c[3] = ((how >> 24) & 0xff);
|
|
str_c[4] = 0;
|
|
fprintf(stderr, "\r\nbail: %s\r\n", str_c);
|
|
}
|
|
else {
|
|
c3_assert(_(u3ud(u3h(how))));
|
|
|
|
fprintf(stderr, "\r\nbail: %d\r\n", u3h(how));
|
|
u3m_p("bail", u3t(how));
|
|
}
|
|
}
|
|
|
|
switch ( how ) {
|
|
case c3__fail:
|
|
case c3__meme: {
|
|
fprintf(stderr, "bailing out\r\n");
|
|
abort();
|
|
}
|
|
case c3__exit: {
|
|
|
|
static c3_w xuc_w = 0;
|
|
|
|
{
|
|
// fprintf(stderr, "exit %d\r\n", xuc_w);
|
|
// if ( 49 == xuc_w ) { abort(); }
|
|
xuc_w++;
|
|
break;
|
|
}
|
|
}
|
|
case c3__foul:
|
|
case c3__oops:
|
|
fprintf(stderr, "bailing out\r\n");
|
|
assert(0);
|
|
}
|
|
|
|
if ( &(u3H->rod_u) == u3R ) {
|
|
// For top-level errors, which shouldn't happen often, we have no
|
|
// choice but to use the signal process; and we require the flat
|
|
// form of how.
|
|
//
|
|
c3_assert(_(u3a_is_cat(how)));
|
|
u3m_signal(how);
|
|
}
|
|
|
|
/* Reconstruct a correct error ball.
|
|
*/
|
|
{
|
|
if ( _(u3ud(how)) ) {
|
|
switch ( how ) {
|
|
case c3__exit: {
|
|
how = u3nc(2, u3R->bug.tax);
|
|
break;
|
|
}
|
|
case c3__need: {
|
|
c3_assert(0);
|
|
}
|
|
default: {
|
|
how = u3nt(3, how, u3R->bug.tax);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Longjmp, with an underscore.
|
|
*/
|
|
_longjmp(u3R->esc.buf, how);
|
|
}
|
|
|
|
int c3_cooked() { return u3m_bail(c3__oops); }
|
|
|
|
/* u3m_error(): bail out with %exit, ct_pushing error.
|
|
*/
|
|
c3_i
|
|
u3m_error(c3_c* str_c)
|
|
{
|
|
u3t_mean(u3i_string(str_c));
|
|
return u3m_bail(c3__exit);
|
|
}
|
|
|
|
/* u3m_leap(): in u3R, create a new road within the existing one.
|
|
*/
|
|
void
|
|
u3m_leap(c3_w pad_w)
|
|
{
|
|
c3_w len_w;
|
|
u3_road* rod_u;
|
|
|
|
/* Measure the pad - we'll need it.
|
|
*/
|
|
{
|
|
#if 0
|
|
if ( pad_w < u3R->all.fre_w ) {
|
|
pad_w = 0;
|
|
}
|
|
else {
|
|
pad_w -= u3R->all.fre_w;
|
|
}
|
|
#endif
|
|
if ( (pad_w + c3_wiseof(u3a_road)) >= u3a_open(u3R) ) {
|
|
u3m_bail(c3__meme);
|
|
}
|
|
len_w = u3a_open(u3R) - (pad_w + c3_wiseof(u3a_road));
|
|
}
|
|
|
|
/* Allocate a region on the cap.
|
|
*/
|
|
{
|
|
u3p(c3_w) bot_p;
|
|
|
|
if ( c3y == u3a_is_north(u3R) ) {
|
|
bot_p = (u3R->cap_p - len_w);
|
|
u3R->cap_p -= len_w;
|
|
|
|
rod_u = _pave_south(u3a_into(bot_p), c3_wiseof(u3a_road), len_w);
|
|
#if 0
|
|
fprintf(stderr, "leap: from north %p (cap %x), to south %p\r\n",
|
|
u3R,
|
|
u3R->cap_p + len_p,
|
|
rod_u);
|
|
#endif
|
|
}
|
|
else {
|
|
bot_p = u3R->cap_p;
|
|
u3R->cap_p += len_w;
|
|
|
|
rod_u = _pave_north(u3a_into(bot_p), c3_wiseof(u3a_road), len_w);
|
|
#if 0
|
|
fprintf(stderr, "leap: from north %p (cap %p), to south %p\r\n",
|
|
u3R,
|
|
u3R->cap_p - len_p,
|
|
rod_u);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* Attach the new road to its parents.
|
|
*/
|
|
{
|
|
c3_assert(0 == u3R->kid_p);
|
|
rod_u->par_p = u3of(u3_road, u3R);
|
|
u3R->kid_p = u3of(u3_road, rod_u);
|
|
}
|
|
|
|
/* Set up the new road.
|
|
*/
|
|
{
|
|
u3R = rod_u;
|
|
_pave_parts();
|
|
}
|
|
#ifdef U3_MEMORY_DEBUG
|
|
rod_u->all.fre_w = 0;
|
|
#endif
|
|
}
|
|
|
|
/* u3m_fall(): in u3R, return an inner road to its parent.
|
|
*/
|
|
void
|
|
u3m_fall()
|
|
{
|
|
c3_assert(0 != u3R->par_p);
|
|
|
|
#if 0
|
|
fprintf(stderr, "fall: from %s %p, to %s %p (cap %p, was %p)\r\n",
|
|
_(u3a_is_north(u3R)) ? "north" : "south",
|
|
u3R,
|
|
_(u3a_is_north(u3R)) ? "north" : "south",
|
|
u3to(u3_road, u3R->par_p),
|
|
u3R->hat_w,
|
|
u3R->rut_w);
|
|
#endif
|
|
|
|
u3to(u3_road, u3R->par_p)->pro.nox_d += u3R->pro.nox_d;
|
|
u3to(u3_road, u3R->par_p)->pro.cel_d += u3R->pro.cel_d;
|
|
|
|
/* The new cap is the old hat - it's as simple as that.
|
|
*/
|
|
u3to(u3_road, u3R->par_p)->cap_p = u3R->hat_p;
|
|
|
|
/* And, we're back home.
|
|
*/
|
|
u3R = u3to(u3_road, u3R->par_p);
|
|
u3R->kid_p = 0;
|
|
}
|
|
|
|
/* u3m_hate(): new, integrated leap mechanism (enter).
|
|
*/
|
|
void
|
|
u3m_hate(c3_w pad_w)
|
|
{
|
|
c3_assert(0 == u3R->ear_p);
|
|
|
|
u3R->ear_p = u3R->cap_p;
|
|
u3m_leap(pad_w);
|
|
}
|
|
|
|
/* u3m_love(): return product from leap.
|
|
*/
|
|
u3_noun
|
|
u3m_love(u3_noun pro)
|
|
{
|
|
{
|
|
u3p(u3h_root) cod_p = u3R->jed.cod_p;
|
|
u3p(u3h_root) war_p = u3R->jed.war_p;
|
|
u3p(u3h_root) han_p = u3R->jed.han_p;
|
|
u3p(u3h_root) bas_p = u3R->jed.bas_p;
|
|
u3p(u3h_root) byc_p = u3R->byc.har_p;
|
|
|
|
u3m_fall();
|
|
|
|
pro = u3a_take(pro);
|
|
|
|
u3j_reap(cod_p, war_p, han_p, bas_p);
|
|
u3n_reap(byc_p);
|
|
|
|
u3R->cap_p = u3R->ear_p;
|
|
u3R->ear_p = 0;
|
|
}
|
|
return pro;
|
|
}
|
|
|
|
/* u3m_golf(): record cap_p length for u3_flog().
|
|
*/
|
|
c3_w
|
|
u3m_golf(void)
|
|
{
|
|
if ( c3y == u3a_is_north(u3R) ) {
|
|
return u3R->mat_p - u3R->cap_p;
|
|
}
|
|
else {
|
|
return u3R->cap_p - u3R->mat_p;
|
|
}
|
|
}
|
|
|
|
/* u3m_flog(): reset cap_p.
|
|
*/
|
|
void
|
|
u3m_flog(c3_w gof_w)
|
|
{
|
|
// Enable memsets in case of memory corruption.
|
|
//
|
|
if ( c3y == u3a_is_north(u3R) ) {
|
|
u3_post bot_p = (u3R->mat_p - gof_w);
|
|
// c3_w len_w = (bot_w - u3R->cap_w);
|
|
|
|
// memset(u3R->cap_w, 0, 4 * len_w);
|
|
u3R->cap_p = bot_p;
|
|
}
|
|
else {
|
|
u3_post bot_p = u3R->mat_p + gof_w;
|
|
// c3_w len_w = (u3R->cap_w - bot_w);
|
|
|
|
// memset(bot_w, 0, 4 * len_w); //
|
|
u3R->cap_p = bot_p;
|
|
}
|
|
}
|
|
|
|
/* u3m_water(): produce watermarks.
|
|
*/
|
|
void
|
|
u3m_water(c3_w* low_w, c3_w* hig_w)
|
|
{
|
|
c3_assert(u3R == &u3H->rod_u);
|
|
|
|
*low_w = (u3H->rod_u.hat_p - u3H->rod_u.rut_p);
|
|
*hig_w = (u3H->rod_u.mat_p - u3H->rod_u.cap_p) + c3_wiseof(u3v_home);
|
|
}
|
|
|
|
/* u3m_soft_top(): top-level safety wrapper.
|
|
*/
|
|
u3_noun
|
|
u3m_soft_top(c3_w sec_w, // timer seconds
|
|
c3_w pad_w, // base memory pad
|
|
u3_funk fun_f,
|
|
u3_noun arg)
|
|
{
|
|
u3_noun why, pro;
|
|
c3_l sig_l;
|
|
|
|
/* Enter internal signal regime.
|
|
*/
|
|
_cm_signal_deep(0);
|
|
|
|
if ( 0 != (sig_l = sigsetjmp(u3_Signal, 1)) ) {
|
|
// reinitialize trace state
|
|
//
|
|
u3t_init();
|
|
|
|
// return to blank state
|
|
//
|
|
_cm_signal_done();
|
|
|
|
// recover memory state from the top down
|
|
//
|
|
return _cm_signal_recover(sig_l, arg);
|
|
}
|
|
|
|
/* Record the cap, and leap.
|
|
*/
|
|
u3m_hate(pad_w);
|
|
|
|
/* Trap for ordinary nock exceptions.
|
|
*/
|
|
if ( 0 == (why = (u3_noun)_setjmp(u3R->esc.buf)) ) {
|
|
pro = fun_f(arg);
|
|
|
|
/* Make sure the inner routine did not create garbage.
|
|
*/
|
|
if ( u3C.wag_w & u3o_debug_ram ) {
|
|
#ifdef U3_CPU_DEBUG
|
|
if ( u3R->all.max_w > 1000000 ) {
|
|
u3a_print_memory("execute: top", u3R->all.max_w);
|
|
}
|
|
#endif
|
|
u3m_grab(pro, u3_none);
|
|
}
|
|
|
|
/* Revert to external signal regime.
|
|
*/
|
|
_cm_signal_done();
|
|
|
|
/* Produce success, on the old road.
|
|
*/
|
|
pro = u3nc(0, u3m_love(pro));
|
|
}
|
|
else {
|
|
/* Overload the error result.
|
|
*/
|
|
pro = u3m_love(why);
|
|
}
|
|
|
|
/* Revert to external signal regime.
|
|
*/
|
|
_cm_signal_done();
|
|
|
|
/* Free the argument.
|
|
*/
|
|
u3z(arg);
|
|
|
|
/* Return the product.
|
|
*/
|
|
return pro;
|
|
}
|
|
|
|
/* u3m_soft_sure(): top-level call assumed correct.
|
|
*/
|
|
u3_noun
|
|
u3m_soft_sure(u3_funk fun_f, u3_noun arg)
|
|
{
|
|
u3_noun pro, pru = u3m_soft_top(0, (1 << 18), fun_f, arg);
|
|
|
|
c3_assert(_(u3du(pru)));
|
|
pro = u3k(u3t(pru));
|
|
u3z(pru);
|
|
|
|
return pro;
|
|
}
|
|
|
|
/* u3m_soft_slam: top-level call.
|
|
*/
|
|
u3_noun _cm_slam(u3_noun arg) { return u3n_slam_on(u3h(arg), u3t(arg)); }
|
|
u3_noun
|
|
u3m_soft_slam(u3_noun gat, u3_noun sam)
|
|
{
|
|
return u3m_soft_sure(_cm_slam, u3nc(gat, sam));
|
|
}
|
|
|
|
/* u3m_soft_nock: top-level nock.
|
|
*/
|
|
u3_noun _cm_nock(u3_noun arg) { return u3n_nock_on(u3h(arg), u3t(arg)); }
|
|
u3_noun
|
|
u3m_soft_nock(u3_noun bus, u3_noun fol)
|
|
{
|
|
return u3m_soft_sure(_cm_nock, u3nc(bus, fol));
|
|
}
|
|
|
|
/* u3m_soft_run(): descend into virtualization context.
|
|
*/
|
|
u3_noun
|
|
u3m_soft_run(u3_noun gul,
|
|
u3_funq fun_f,
|
|
u3_noun aga,
|
|
u3_noun agb)
|
|
{
|
|
u3_noun why = 0, pro;
|
|
|
|
/* Record the cap, and leap.
|
|
*/
|
|
u3m_hate(1 << 18);
|
|
|
|
/* Configure the new road.
|
|
*/
|
|
{
|
|
u3R->ski.gul = u3nc(gul, u3to(u3_road, u3R->par_p)->ski.gul);
|
|
u3R->pro.don = u3to(u3_road, u3R->par_p)->pro.don;
|
|
u3R->bug.tax = 0;
|
|
}
|
|
u3t_on(coy_o);
|
|
|
|
/* Trap for exceptions.
|
|
*/
|
|
if ( 0 == (why = (u3_noun)_setjmp(u3R->esc.buf)) ) {
|
|
u3t_off(coy_o);
|
|
pro = fun_f(aga, agb);
|
|
|
|
#ifdef U3_CPU_DEBUG
|
|
if ( u3R->all.max_w > 1000000 ) {
|
|
u3a_print_memory("execute: run", u3R->all.max_w);
|
|
}
|
|
#endif
|
|
/* Produce success, on the old road.
|
|
*/
|
|
pro = u3nc(0, u3m_love(pro));
|
|
}
|
|
else {
|
|
u3t_init();
|
|
|
|
/* Produce - or fall again.
|
|
*/
|
|
{
|
|
c3_assert(_(u3du(why)));
|
|
switch ( u3h(why) ) {
|
|
default: c3_assert(0); return 0;
|
|
|
|
case 0: { // unusual: bail with success.
|
|
pro = u3m_love(why);
|
|
} break;
|
|
|
|
case 1: { // blocking request
|
|
pro = u3m_love(why);
|
|
} break;
|
|
|
|
case 2: { // true exit
|
|
pro = u3m_love(why);
|
|
} break;
|
|
|
|
case 3: { // failure; rebail w/trace
|
|
u3_noun yod = u3m_love(u3t(why));
|
|
|
|
u3m_bail
|
|
(u3nt(3,
|
|
u3a_take(u3h(yod)),
|
|
u3kb_weld(u3t(yod), u3k(u3R->bug.tax))));
|
|
} break;
|
|
|
|
case 4: { // meta-bail
|
|
u3m_bail(u3m_love(u3t(why)));
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Release the arguments.
|
|
*/
|
|
{
|
|
u3z(gul);
|
|
u3z(aga);
|
|
u3z(agb);
|
|
}
|
|
|
|
/* Return the product.
|
|
*/
|
|
return pro;
|
|
}
|
|
|
|
/* u3m_soft_esc(): namespace lookup. Produces direct result.
|
|
*/
|
|
u3_noun
|
|
u3m_soft_esc(u3_noun ref, u3_noun sam)
|
|
{
|
|
u3_noun why, gul, pro;
|
|
|
|
/* Assert preconditions.
|
|
*/
|
|
{
|
|
c3_assert(0 != u3R->ski.gul);
|
|
gul = u3h(u3R->ski.gul);
|
|
}
|
|
|
|
/* Record the cap, and leap.
|
|
*/
|
|
u3m_hate(1 << 18);
|
|
|
|
/* Configure the new road.
|
|
*/
|
|
{
|
|
u3R->ski.gul = u3t(u3to(u3_road, u3R->par_p)->ski.gul);
|
|
u3R->pro.don = u3to(u3_road, u3R->par_p)->pro.don;
|
|
u3R->bug.tax = 0;
|
|
}
|
|
|
|
/* Trap for exceptions.
|
|
*/
|
|
if ( 0 == (why = (u3_noun)_setjmp(u3R->esc.buf)) ) {
|
|
pro = u3n_slam_on(gul, u3nc(ref, sam));
|
|
|
|
/* Fall back to the old road, leaving temporary memory intact.
|
|
*/
|
|
pro = u3m_love(pro);
|
|
}
|
|
else {
|
|
u3t_init();
|
|
|
|
/* Push the error back up to the calling context - not the run we
|
|
** are in, but the caller of the run, matching pure nock semantics.
|
|
*/
|
|
u3m_bail(u3nc(4, u3m_love(why)));
|
|
}
|
|
|
|
/* Release the sample. Note that we used it above, but in a junior
|
|
** road, so its refcount is intact.
|
|
*/
|
|
u3z(ref);
|
|
u3z(sam);
|
|
|
|
/* Return the product.
|
|
*/
|
|
return pro;
|
|
}
|
|
|
|
/* u3m_grab(): garbage-collect the world, plus extra roots.
|
|
*/
|
|
void
|
|
u3m_grab(u3_noun som, ...) // terminate with u3_none
|
|
{
|
|
// u3h_free(u3R->cax.har_p);
|
|
// u3R->cax.har_p = u3h_new();
|
|
|
|
u3v_mark();
|
|
u3m_mark();
|
|
{
|
|
va_list vap;
|
|
u3_noun tur;
|
|
|
|
va_start(vap, som);
|
|
|
|
if ( som != u3_none ) {
|
|
u3a_mark_noun(som);
|
|
|
|
while ( u3_none != (tur = va_arg(vap, u3_noun)) ) {
|
|
u3a_mark_noun(tur);
|
|
}
|
|
}
|
|
va_end(vap);
|
|
}
|
|
u3a_sweep();
|
|
}
|
|
|
|
/* u3m_soft(): top-level wrapper.
|
|
**
|
|
** Produces [0 product] or [%error (list tank)], top last.
|
|
*/
|
|
u3_noun
|
|
u3m_soft(c3_w sec_w,
|
|
u3_funk fun_f,
|
|
u3_noun arg)
|
|
{
|
|
u3_noun why;
|
|
|
|
why = u3m_soft_top(sec_w, (1 << 20), fun_f, arg); // 2MB pad
|
|
|
|
if ( 0 == u3h(why) ) {
|
|
return why;
|
|
} else {
|
|
u3_noun tax, cod, pro, mok;
|
|
|
|
c3_assert(1 != u3h(why)); // don't use .^ at the top level!
|
|
|
|
if ( 2 == u3h(why) ) {
|
|
cod = c3__exit;
|
|
tax = u3k(u3t(why));
|
|
}
|
|
else {
|
|
c3_assert(3 == u3h(why));
|
|
|
|
cod = u3k(u3h(u3t(why)));
|
|
tax = u3k(u3t(u3t(why)));
|
|
}
|
|
mok = u3dc("mook", 2, tax);
|
|
pro = u3nc(cod, u3k(u3t(mok)));
|
|
|
|
u3z(mok);
|
|
u3z(why);
|
|
|
|
return pro;
|
|
}
|
|
}
|
|
|
|
/* _cm_is_tas(): yes iff som (RETAIN) is @tas.
|
|
*/
|
|
static c3_o
|
|
_cm_is_tas(u3_atom som, c3_w len_w)
|
|
{
|
|
c3_w i_w;
|
|
|
|
for ( i_w = 0; i_w < len_w; i_w++ ) {
|
|
c3_c c_c = u3r_byte(i_w, som);
|
|
|
|
if ( islower(c_c) ||
|
|
(isdigit(c_c) && (0 != i_w) && ((len_w - 1) != i_w))
|
|
|| '-' == c_c )
|
|
{
|
|
continue;
|
|
}
|
|
return c3n;
|
|
}
|
|
return c3y;
|
|
}
|
|
|
|
/* _cm_is_ta(): yes iff som (RETAIN) is @ta.
|
|
*/
|
|
static c3_o
|
|
_cm_is_ta(u3_noun som, c3_w len_w)
|
|
{
|
|
c3_w i_w;
|
|
|
|
for ( i_w = 0; i_w < len_w; i_w++ ) {
|
|
c3_c c_c = u3r_byte(i_w, som);
|
|
|
|
if ( (c_c < 32) || (c_c > 127) ) {
|
|
return c3n;
|
|
}
|
|
}
|
|
return c3y;
|
|
}
|
|
|
|
/* _cm_hex(): hex byte.
|
|
*/
|
|
c3_y _cm_hex(c3_y c_y)
|
|
{
|
|
if ( c_y < 10 )
|
|
return '0' + c_y;
|
|
else return 'a' + (c_y - 10);
|
|
}
|
|
|
|
/* _cm_in_pretty: measure/cut prettyprint.
|
|
*/
|
|
static c3_w
|
|
_cm_in_pretty(u3_noun som, c3_o sel_o, c3_c* str_c)
|
|
{
|
|
if ( _(u3du(som)) ) {
|
|
c3_w sel_w, one_w, two_w;
|
|
|
|
sel_w = 0;
|
|
if ( _(sel_o) ) {
|
|
if ( str_c ) { *(str_c++) = '['; }
|
|
sel_w += 1;
|
|
}
|
|
|
|
one_w = _cm_in_pretty(u3h(som), c3y, str_c);
|
|
if ( str_c ) {
|
|
str_c += one_w;
|
|
*(str_c++) = ' ';
|
|
}
|
|
two_w = _cm_in_pretty(u3t(som), c3n, str_c);
|
|
if ( str_c ) { str_c += two_w; }
|
|
|
|
if ( _(sel_o) ) {
|
|
if ( str_c ) { *(str_c++) = ']'; }
|
|
sel_w += 1;
|
|
}
|
|
return one_w + two_w + 1 + sel_w;
|
|
}
|
|
else {
|
|
if ( som < 65536 ) {
|
|
c3_c buf_c[6];
|
|
c3_w len_w;
|
|
|
|
snprintf(buf_c, 6, "%d", som);
|
|
len_w = strlen(buf_c);
|
|
|
|
if ( str_c ) { strcpy(str_c, buf_c); str_c += len_w; }
|
|
return len_w;
|
|
}
|
|
else {
|
|
c3_w len_w = u3r_met(3, som);
|
|
|
|
if ( _(_cm_is_tas(som, len_w)) ) {
|
|
c3_w len_w = u3r_met(3, som);
|
|
|
|
if ( str_c ) {
|
|
*(str_c++) = '%';
|
|
u3r_bytes(0, len_w, (c3_y *)str_c, som);
|
|
str_c += len_w;
|
|
}
|
|
return len_w + 1;
|
|
}
|
|
else if ( _(_cm_is_ta(som, len_w)) ) {
|
|
if ( str_c ) {
|
|
*(str_c++) = '\'';
|
|
u3r_bytes(0, len_w, (c3_y *)str_c, som);
|
|
str_c += len_w;
|
|
*(str_c++) = '\'';
|
|
}
|
|
return len_w + 2;
|
|
}
|
|
else {
|
|
c3_w len_w = u3r_met(3, som);
|
|
c3_c *buf_c = malloc(2 + (2 * len_w) + 1);
|
|
c3_w i_w = 0;
|
|
c3_w a_w = 0;
|
|
|
|
buf_c[a_w++] = '0';
|
|
buf_c[a_w++] = 'x';
|
|
|
|
for ( i_w = 0; i_w < len_w; i_w++ ) {
|
|
c3_y c_y = u3r_byte(len_w - (i_w + 1), som);
|
|
|
|
if ( (i_w == 0) && (c_y <= 0xf) ) {
|
|
buf_c[a_w++] = _cm_hex(c_y);
|
|
} else {
|
|
buf_c[a_w++] = _cm_hex(c_y >> 4);
|
|
buf_c[a_w++] = _cm_hex(c_y & 0xf);
|
|
}
|
|
}
|
|
buf_c[a_w] = 0;
|
|
len_w = a_w;
|
|
|
|
if ( str_c ) { strcpy(str_c, buf_c); str_c += len_w; }
|
|
|
|
free(buf_c);
|
|
return len_w;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* u3m_pretty(): dumb prettyprint to string.
|
|
*/
|
|
c3_c*
|
|
u3m_pretty(u3_noun som)
|
|
{
|
|
c3_w len_w = _cm_in_pretty(som, c3y, 0);
|
|
c3_c* pre_c = malloc(len_w + 1);
|
|
|
|
_cm_in_pretty(som, c3y, pre_c);
|
|
pre_c[len_w] = 0;
|
|
return pre_c;
|
|
}
|
|
|
|
/* u3m_p(): dumb print with caption.
|
|
*/
|
|
void
|
|
u3m_p(const c3_c* cap_c, u3_noun som)
|
|
{
|
|
c3_c* pre_c = u3m_pretty(som);
|
|
|
|
fprintf(stderr, "%s: %s\r\n", cap_c, pre_c);
|
|
free(pre_c);
|
|
}
|
|
|
|
/* u3m_tape(): dump a tape to stdout.
|
|
*/
|
|
void
|
|
u3m_tape(u3_noun tep)
|
|
{
|
|
u3_noun tap = tep;
|
|
|
|
while ( u3_nul != tap ) {
|
|
c3_c car_c;
|
|
|
|
if ( u3h(tap) >= 127 ) {
|
|
car_c = '?';
|
|
} else car_c = u3h(tap);
|
|
|
|
putc(car_c, stdout);
|
|
tap = u3t(tap);
|
|
}
|
|
u3z(tep);
|
|
}
|
|
|
|
/* u3m_wall(): dump a wall to stdout.
|
|
*/
|
|
void
|
|
u3m_wall(u3_noun wol)
|
|
{
|
|
u3_noun wal = wol;
|
|
|
|
while ( u3_nul != wal ) {
|
|
u3m_tape(u3k(u3h(wal)));
|
|
|
|
putc(13, stdout);
|
|
putc(10, stdout);
|
|
|
|
wal = u3t(wal);
|
|
}
|
|
u3z(wol);
|
|
}
|
|
|
|
/* _cm_limits(): set up global modes and limits.
|
|
*/
|
|
static void
|
|
_cm_limits(void)
|
|
{
|
|
struct rlimit rlm;
|
|
c3_i ret_i;
|
|
|
|
/* Moar stack.
|
|
*/
|
|
{
|
|
ret_i = getrlimit(RLIMIT_STACK, &rlm);
|
|
c3_assert(0 == ret_i);
|
|
rlm.rlim_cur = (rlm.rlim_max > (65536 << 10))
|
|
? (65536 << 10)
|
|
: rlm.rlim_max;
|
|
if ( 0 != setrlimit(RLIMIT_STACK, &rlm) ) {
|
|
perror("stack");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* Moar filez.
|
|
*/
|
|
{
|
|
ret_i = getrlimit(RLIMIT_NOFILE, &rlm);
|
|
c3_assert(0 == ret_i);
|
|
rlm.rlim_cur = 10240; // default OSX max, not in rlim_max irritatingly
|
|
if ( 0 != setrlimit(RLIMIT_NOFILE, &rlm) ) {
|
|
// perror("file limit");
|
|
// no exit, not a critical limit
|
|
}
|
|
}
|
|
|
|
/* Moar core.
|
|
*/
|
|
{
|
|
getrlimit(RLIMIT_CORE, &rlm);
|
|
rlm.rlim_cur = RLIM_INFINITY;
|
|
if ( 0 != setrlimit(RLIMIT_CORE, &rlm) ) {
|
|
perror("core limit");
|
|
// no exit, not a critical limit
|
|
}
|
|
}
|
|
}
|
|
|
|
/* _cm_signals(): set up interrupts, etc.
|
|
*/
|
|
static void
|
|
_cm_signals(void)
|
|
{
|
|
if ( 0 != sigsegv_install_handler(u3e_fault) ) {
|
|
fprintf(stderr, "sigsegv install failed\n");
|
|
exit(1);
|
|
}
|
|
// signal(SIGINT, _loom_stop);
|
|
|
|
|
|
// Block SIGPROF, so that if/when we reactivate it on the
|
|
// main thread for profiling, we won't get hits in parallel
|
|
// on other threads.
|
|
if ( u3C.wag_w & u3o_debug_cpu ) {
|
|
sigset_t set;
|
|
|
|
sigemptyset(&set);
|
|
sigaddset(&set, SIGPROF);
|
|
|
|
if ( 0 != pthread_sigmask(SIG_BLOCK, &set, NULL) ) {
|
|
perror("pthread_sigmask");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* u3m_init(): start the environment, with/without checkpointing.
|
|
*/
|
|
void
|
|
u3m_init(c3_o chk_o)
|
|
{
|
|
_cm_limits();
|
|
_cm_signals();
|
|
|
|
/* Make sure GMP uses our malloc.
|
|
*/
|
|
mp_set_memory_functions(u3a_malloc, u3a_realloc2, u3a_free2);
|
|
|
|
/* Map at fixed address.
|
|
*/
|
|
{
|
|
c3_w len_w = u3a_bytes;
|
|
void* map_v;
|
|
|
|
map_v = mmap((void *)u3_Loom,
|
|
len_w,
|
|
_(chk_o) ? PROT_READ : (PROT_READ | PROT_WRITE),
|
|
(MAP_ANON | MAP_FIXED | MAP_PRIVATE),
|
|
-1, 0);
|
|
|
|
if ( -1 == (c3_ps)map_v ) {
|
|
void* dyn_v = mmap((void *)0,
|
|
len_w,
|
|
PROT_READ,
|
|
MAP_ANON | MAP_PRIVATE,
|
|
-1, 0);
|
|
|
|
fprintf(stderr, "boot: mapping %dMB failed\r\n", (len_w / (1024 * 1024)));
|
|
fprintf(stderr, "see urbit.org/docs/using/install for adding swap space\r\n");
|
|
if ( -1 != (c3_ps)map_v ) {
|
|
fprintf(stderr,
|
|
"if porting to a new platform, try U3_OS_LoomBase %p\r\n",
|
|
dyn_v);
|
|
}
|
|
exit(1);
|
|
}
|
|
printf("loom: mapped %dMB\r\n", len_w >> 20);
|
|
}
|
|
}
|
|
|
|
/* _get_cmd_output(): Run a shell command and capture its output.
|
|
Exits with an error if the command fails or produces no output.
|
|
The 'out_c' parameter should be an array of sufficient length to hold
|
|
the command's output, up to a max of len_c characters.
|
|
*/
|
|
static void
|
|
_get_cmd_output(c3_c *cmd_c, c3_c *out_c, c3_w len_c)
|
|
{
|
|
FILE *fp = popen(cmd_c, "r");
|
|
if ( NULL == fp ) {
|
|
fprintf(stderr, "'%s' failed\n", cmd_c);
|
|
exit(1);
|
|
}
|
|
|
|
if ( NULL == fgets(out_c, len_c, fp) ) {
|
|
fprintf(stderr, "'%s' produced no output\n", cmd_c);
|
|
exit(1);
|
|
}
|
|
|
|
pclose(fp);
|
|
}
|
|
|
|
/* _arvo_hash(): get a shortened hash of the last git commit
|
|
that modified the sys/ directory in arvo.
|
|
hax_c must be an array with length >= 11.
|
|
*/
|
|
static void
|
|
_arvo_hash(c3_c *out_c, c3_c *arv_c)
|
|
{
|
|
c3_c cmd_c[2048];
|
|
|
|
sprintf(cmd_c, "git -C %s log -1 HEAD --format=%%H -- sys/", arv_c);
|
|
_get_cmd_output(cmd_c, out_c, 11);
|
|
|
|
out_c[10] = 0; // end with null-byte
|
|
}
|
|
|
|
/* _git_pill_url(): produce a URL from which to download a pill
|
|
based on the location of an arvo git repository.
|
|
*/
|
|
static void
|
|
_git_pill_url(c3_c *out_c, c3_c *arv_c)
|
|
{
|
|
c3_c hax_c[11];
|
|
|
|
assert(NULL != arv_c);
|
|
|
|
if ( 0 != system("which git >> /dev/null") ) {
|
|
fprintf(stderr, "Could not find git executable\n");
|
|
exit(1);
|
|
}
|
|
|
|
_arvo_hash(hax_c, arv_c);
|
|
sprintf(out_c, "https://bootstrap.urbit.org/git-%s.pill", hax_c);
|
|
}
|
|
|
|
/* _boot_home(): create ship directory. */
|
|
static void
|
|
_boot_home(c3_c *dir_c, c3_c *pil_c, c3_c *url_c, c3_c *arv_c)
|
|
{
|
|
c3_c ful_c[2048];
|
|
|
|
/* Create subdirectories. */
|
|
{
|
|
mkdir(dir_c, 0700);
|
|
|
|
snprintf(ful_c, 2048, "%s/.urb", dir_c);
|
|
mkdir(ful_c, 0700);
|
|
|
|
snprintf(ful_c, 2048, "%s/.urb/get", dir_c);
|
|
mkdir(ful_c, 0700);
|
|
|
|
snprintf(ful_c, 2048, "%s/.urb/put", dir_c);
|
|
mkdir(ful_c, 0700);
|
|
|
|
snprintf(ful_c, 2048, "%s/.urb/sis", dir_c);
|
|
mkdir(ful_c, 0700);
|
|
}
|
|
/* Copy urbit.pill. */
|
|
{
|
|
{
|
|
struct stat s;
|
|
snprintf(ful_c, 2048, "%s/.urb/urbit.pill", dir_c);
|
|
if ( stat(ful_c, &s) == 0 ) {
|
|
/* we're in a "logical boot". awful hack, but bail here */
|
|
printf("%s confirmed to exist\r\n", ful_c);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Copy local pill file. */
|
|
if ( pil_c != 0 ) {
|
|
snprintf(ful_c, 2048, "cp %s %s/.urb/urbit.pill",
|
|
pil_c, dir_c);
|
|
printf("%s\r\n", ful_c);
|
|
if ( 0 != system(ful_c) ) {
|
|
fprintf(stderr, "could not %s\n", ful_c);
|
|
exit(1);
|
|
}
|
|
}
|
|
/* Fetch remote pill over HTTP. */
|
|
else {
|
|
CURL *curl;
|
|
CURLcode result;
|
|
FILE *file;
|
|
c3_c pil_c[2048];
|
|
long cod_l;
|
|
|
|
/* use arvo git hash and branch for pill url unless overridden */
|
|
if ( NULL == url_c ) {
|
|
url_c = pil_c;
|
|
_git_pill_url(url_c, arv_c);
|
|
}
|
|
|
|
snprintf(ful_c, 2048, "%s/.urb/urbit.pill", dir_c);
|
|
printf("fetching %s to %s\r\n", url_c, ful_c);
|
|
if ( !(curl = curl_easy_init()) ) {
|
|
fprintf(stderr, "failed to initialize libcurl\n");
|
|
exit(1);
|
|
}
|
|
if ( !(file = fopen(ful_c, "w")) ) {
|
|
fprintf(stderr, "failed to open %s\n", ful_c);
|
|
exit(1);
|
|
}
|
|
curl_easy_setopt(curl, CURLOPT_URL, url_c);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
|
|
result = curl_easy_perform(curl);
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &cod_l);
|
|
fclose(file);
|
|
if ( CURLE_OK != result ) {
|
|
fprintf(stderr, "failed to fetch %s: %s\n",
|
|
url_c, curl_easy_strerror(result));
|
|
fprintf(stderr,
|
|
"please fetch it manually and specify the location with -B\n");
|
|
exit(1);
|
|
}
|
|
if ( 300 <= cod_l ) {
|
|
fprintf(stderr, "error fetching %s: HTTP %ld\n", url_c, cod_l);
|
|
fprintf(stderr,
|
|
"please fetch it manually and specify the location with -B\n");
|
|
exit(1);
|
|
}
|
|
curl_easy_cleanup(curl);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* u3m_boot(): start the u3 system.
|
|
*/
|
|
void
|
|
u3m_boot(c3_o nuu_o, c3_o bug_o, c3_c* dir_c,
|
|
c3_c *pil_c, c3_c *url_c, c3_c *arv_c)
|
|
{
|
|
/* Activate the loom.
|
|
*/
|
|
u3m_init(nuu_o);
|
|
|
|
/* Activate the storage system.
|
|
*/
|
|
nuu_o = u3e_live(nuu_o, dir_c);
|
|
|
|
/* Activate tracing.
|
|
*/
|
|
u3t_init();
|
|
|
|
/* Construct or activate the allocator.
|
|
*/
|
|
u3m_pave(nuu_o, bug_o);
|
|
|
|
/* Initialize the jet system.
|
|
*/
|
|
u3j_boot(nuu_o);
|
|
|
|
/* Install or reactivate the kernel.
|
|
*/
|
|
if ( _(nuu_o) ) {
|
|
c3_c ful_c[2048];
|
|
|
|
_boot_home(dir_c, pil_c, url_c, arv_c);
|
|
|
|
snprintf(ful_c, 2048, "%s/.urb/urbit.pill", dir_c);
|
|
|
|
printf("boot: loading %s\r\n", ful_c);
|
|
u3v_make(ful_c);
|
|
|
|
u3v_jack();
|
|
}
|
|
else {
|
|
u3v_hose();
|
|
u3j_ream();
|
|
u3n_ream();
|
|
}
|
|
}
|